Integrate unfinished server-rs refactor worklists
This commit is contained in:
@@ -37,7 +37,10 @@ pub(crate) fn emit_big_fish_publish_readiness_event(
|
||||
publish_ready,
|
||||
blockers,
|
||||
occurred_at_micros,
|
||||
} = event;
|
||||
} = event
|
||||
else {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
let blockers_json = serde_json::to_string(&blockers)
|
||||
.map_err(|error| format!("big_fish.publish_readiness.blockers 序列化失败: {error}"))?;
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
mod assets;
|
||||
mod events;
|
||||
mod runtime;
|
||||
mod session;
|
||||
mod tables;
|
||||
|
||||
pub use assets::*;
|
||||
pub(crate) use events::*;
|
||||
pub use runtime::*;
|
||||
pub use session::*;
|
||||
pub use tables::*;
|
||||
|
||||
231
server-rs/crates/spacetime-module/src/big_fish/runtime.rs
Normal file
231
server-rs/crates/spacetime-module/src/big_fish/runtime.rs
Normal file
@@ -0,0 +1,231 @@
|
||||
use crate::big_fish::tables::{
|
||||
BigFishCreationSession, BigFishRuntimeRun, big_fish_creation_session, big_fish_runtime_run,
|
||||
};
|
||||
use crate::*;
|
||||
use module_big_fish::{
|
||||
StartBigFishRunCommand, SubmitBigFishInputCommand, deserialize_runtime_snapshot,
|
||||
serialize_runtime_snapshot, start_big_fish_run as start_big_fish_run_domain,
|
||||
submit_big_fish_input as submit_big_fish_input_domain,
|
||||
};
|
||||
|
||||
#[spacetimedb::procedure]
|
||||
pub fn start_big_fish_run(
|
||||
ctx: &mut ProcedureContext,
|
||||
input: BigFishRunStartInput,
|
||||
) -> BigFishRunProcedureResult {
|
||||
match ctx.try_with_tx(|tx| start_big_fish_run_tx(tx, input.clone())) {
|
||||
Ok(run) => BigFishRunProcedureResult {
|
||||
ok: true,
|
||||
run_json: Some(serialize_big_fish_run_json(&run)),
|
||||
error_message: None,
|
||||
},
|
||||
Err(message) => BigFishRunProcedureResult {
|
||||
ok: false,
|
||||
run_json: None,
|
||||
error_message: Some(message),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[spacetimedb::procedure]
|
||||
pub fn get_big_fish_run(
|
||||
ctx: &mut ProcedureContext,
|
||||
input: BigFishRunGetInput,
|
||||
) -> BigFishRunProcedureResult {
|
||||
match ctx.try_with_tx(|tx| get_big_fish_run_tx(tx, input.clone())) {
|
||||
Ok(run) => BigFishRunProcedureResult {
|
||||
ok: true,
|
||||
run_json: Some(serialize_big_fish_run_json(&run)),
|
||||
error_message: None,
|
||||
},
|
||||
Err(message) => BigFishRunProcedureResult {
|
||||
ok: false,
|
||||
run_json: None,
|
||||
error_message: Some(message),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[spacetimedb::procedure]
|
||||
pub fn submit_big_fish_input(
|
||||
ctx: &mut ProcedureContext,
|
||||
input: BigFishInputSubmitInput,
|
||||
) -> BigFishRunProcedureResult {
|
||||
match ctx.try_with_tx(|tx| submit_big_fish_input_tx(tx, input.clone())) {
|
||||
Ok(run) => BigFishRunProcedureResult {
|
||||
ok: true,
|
||||
run_json: Some(serialize_big_fish_run_json(&run)),
|
||||
error_message: None,
|
||||
},
|
||||
Err(message) => BigFishRunProcedureResult {
|
||||
ok: false,
|
||||
run_json: None,
|
||||
error_message: Some(message),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn start_big_fish_run_tx(
|
||||
ctx: &ReducerContext,
|
||||
input: BigFishRunStartInput,
|
||||
) -> Result<BigFishRuntimeSnapshot, String> {
|
||||
validate_run_start_input(&input).map_err(|error| error.to_string())?;
|
||||
if ctx
|
||||
.db
|
||||
.big_fish_runtime_run()
|
||||
.run_id()
|
||||
.find(&input.run_id)
|
||||
.is_some()
|
||||
{
|
||||
return Err("big_fish_runtime_run.run_id 已存在".to_string());
|
||||
}
|
||||
|
||||
let session = ctx
|
||||
.db
|
||||
.big_fish_creation_session()
|
||||
.session_id()
|
||||
.find(&input.session_id)
|
||||
.ok_or_else(|| "big_fish_creation_session 不存在".to_string())?;
|
||||
ensure_big_fish_session_playable(&session, &input.owner_user_id)?;
|
||||
let draft = session
|
||||
.draft_json
|
||||
.as_deref()
|
||||
.map(deserialize_draft)
|
||||
.transpose()
|
||||
.map_err(|error| format!("big_fish.draft_json 非法: {error}"))?;
|
||||
let work_level_count = draft
|
||||
.as_ref()
|
||||
.map(|value| value.runtime_params.level_count)
|
||||
.or_else(|| Some(BIG_FISH_DEFAULT_LEVEL_COUNT));
|
||||
let result = start_big_fish_run_domain(StartBigFishRunCommand {
|
||||
run_id: input.run_id,
|
||||
session_id: input.session_id,
|
||||
owner_user_id: input.owner_user_id.clone(),
|
||||
draft,
|
||||
work_level_count,
|
||||
started_at_micros: input.started_at_micros,
|
||||
})
|
||||
.map_err(|error| error.to_string())?;
|
||||
|
||||
insert_big_fish_runtime_run(
|
||||
ctx,
|
||||
&result.snapshot,
|
||||
&input.owner_user_id,
|
||||
input.started_at_micros,
|
||||
)?;
|
||||
Ok(result.snapshot)
|
||||
}
|
||||
|
||||
pub(crate) fn get_big_fish_run_tx(
|
||||
ctx: &ReducerContext,
|
||||
input: BigFishRunGetInput,
|
||||
) -> Result<BigFishRuntimeSnapshot, String> {
|
||||
validate_run_get_input(&input).map_err(|error| error.to_string())?;
|
||||
let row = get_owned_big_fish_run_row(ctx, &input.run_id, &input.owner_user_id)?;
|
||||
deserialize_runtime_snapshot(&row.snapshot_json)
|
||||
.map_err(|error| format!("big_fish.runtime_snapshot_json 非法: {error}"))
|
||||
}
|
||||
|
||||
pub(crate) fn submit_big_fish_input_tx(
|
||||
ctx: &ReducerContext,
|
||||
input: BigFishInputSubmitInput,
|
||||
) -> Result<BigFishRuntimeSnapshot, String> {
|
||||
validate_input_submit_input(&input).map_err(|error| error.to_string())?;
|
||||
let row = get_owned_big_fish_run_row(ctx, &input.run_id, &input.owner_user_id)?;
|
||||
let current_snapshot = deserialize_runtime_snapshot(&row.snapshot_json)
|
||||
.map_err(|error| format!("big_fish.runtime_snapshot_json 非法: {error}"))?;
|
||||
let result = submit_big_fish_input_domain(SubmitBigFishInputCommand {
|
||||
owner_user_id: input.owner_user_id,
|
||||
x: input.x,
|
||||
y: input.y,
|
||||
submitted_at_micros: input.submitted_at_micros,
|
||||
current_snapshot,
|
||||
})
|
||||
.map_err(|error| error.to_string())?;
|
||||
|
||||
replace_big_fish_runtime_run(ctx, &row, &result.snapshot, input.submitted_at_micros)?;
|
||||
Ok(result.snapshot)
|
||||
}
|
||||
|
||||
fn ensure_big_fish_session_playable(
|
||||
session: &BigFishCreationSession,
|
||||
player_user_id: &str,
|
||||
) -> Result<(), String> {
|
||||
if session.owner_user_id == player_user_id {
|
||||
return Ok(());
|
||||
}
|
||||
if session.stage == BigFishCreationStage::Published {
|
||||
return Ok(());
|
||||
}
|
||||
Err("未发布的大鱼吃小鱼作品不允许非作者启动运行态".to_string())
|
||||
}
|
||||
|
||||
fn get_owned_big_fish_run_row(
|
||||
ctx: &ReducerContext,
|
||||
run_id: &str,
|
||||
owner_user_id: &str,
|
||||
) -> Result<BigFishRuntimeRun, String> {
|
||||
let row = ctx
|
||||
.db
|
||||
.big_fish_runtime_run()
|
||||
.run_id()
|
||||
.find(&run_id.to_string())
|
||||
.ok_or_else(|| "big_fish_runtime_run 不存在".to_string())?;
|
||||
if row.owner_user_id != owner_user_id {
|
||||
return Err("无权访问该 big_fish_runtime_run".to_string());
|
||||
}
|
||||
Ok(row)
|
||||
}
|
||||
|
||||
fn insert_big_fish_runtime_run(
|
||||
ctx: &ReducerContext,
|
||||
run: &BigFishRuntimeSnapshot,
|
||||
owner_user_id: &str,
|
||||
created_at_micros: i64,
|
||||
) -> Result<(), String> {
|
||||
let timestamp = Timestamp::from_micros_since_unix_epoch(created_at_micros);
|
||||
ctx.db.big_fish_runtime_run().insert(BigFishRuntimeRun {
|
||||
run_id: run.run_id.clone(),
|
||||
session_id: run.session_id.clone(),
|
||||
owner_user_id: owner_user_id.to_string(),
|
||||
status: run.status,
|
||||
snapshot_json: serialize_runtime_snapshot(run)
|
||||
.map_err(|error| format!("big_fish.runtime_snapshot 序列化失败: {error}"))?,
|
||||
last_input_x: run.last_input.x,
|
||||
last_input_y: run.last_input.y,
|
||||
tick: run.tick,
|
||||
created_at: timestamp,
|
||||
updated_at: timestamp,
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn replace_big_fish_runtime_run(
|
||||
ctx: &ReducerContext,
|
||||
current: &BigFishRuntimeRun,
|
||||
run: &BigFishRuntimeSnapshot,
|
||||
updated_at_micros: i64,
|
||||
) -> Result<(), String> {
|
||||
ctx.db
|
||||
.big_fish_runtime_run()
|
||||
.run_id()
|
||||
.delete(¤t.run_id);
|
||||
ctx.db.big_fish_runtime_run().insert(BigFishRuntimeRun {
|
||||
run_id: run.run_id.clone(),
|
||||
session_id: run.session_id.clone(),
|
||||
owner_user_id: current.owner_user_id.clone(),
|
||||
status: run.status,
|
||||
snapshot_json: serialize_runtime_snapshot(run)
|
||||
.map_err(|error| format!("big_fish.runtime_snapshot 序列化失败: {error}"))?,
|
||||
last_input_x: run.last_input.x,
|
||||
last_input_y: run.last_input.y,
|
||||
tick: run.tick,
|
||||
created_at: current.created_at,
|
||||
updated_at: Timestamp::from_micros_since_unix_epoch(updated_at_micros),
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn serialize_big_fish_run_json(run: &BigFishRuntimeSnapshot) -> String {
|
||||
serialize_runtime_snapshot(run).unwrap_or_else(|_| "{}".to_string())
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
use crate::big_fish::tables::{big_fish_agent_message, big_fish_creation_session};
|
||||
use crate::big_fish::tables::{
|
||||
big_fish_agent_message, big_fish_creation_session, big_fish_runtime_run,
|
||||
};
|
||||
use crate::runtime::{
|
||||
ProfilePlayedWorkUpsertInput, add_profile_observed_play_time, upsert_profile_played_work,
|
||||
};
|
||||
@@ -326,7 +328,7 @@ pub(crate) fn delete_big_fish_work_tx(
|
||||
.filter(|row| row.owner_user_id == input.owner_user_id)
|
||||
.ok_or_else(|| "big_fish_creation_session 不存在".to_string())?;
|
||||
|
||||
// 删除作品时同步清理 Agent 消息与素材槽;最终游玩模拟已经迁到前端,不再写后端运行快照。
|
||||
// 中文注释:删除作品时同步清理 Agent 消息、素材槽和后端运行态快照,避免失去来源会话的 run 残留。
|
||||
ctx.db
|
||||
.big_fish_creation_session()
|
||||
.session_id()
|
||||
@@ -352,6 +354,15 @@ pub(crate) fn delete_big_fish_work_tx(
|
||||
{
|
||||
ctx.db.big_fish_asset_slot().slot_id().delete(&slot.slot_id);
|
||||
}
|
||||
for run in ctx
|
||||
.db
|
||||
.big_fish_runtime_run()
|
||||
.iter()
|
||||
.filter(|row| row.session_id == input.session_id)
|
||||
.collect::<Vec<_>>()
|
||||
{
|
||||
ctx.db.big_fish_runtime_run().run_id().delete(&run.run_id);
|
||||
}
|
||||
list_big_fish_works_tx(
|
||||
ctx,
|
||||
BigFishWorksListInput {
|
||||
|
||||
@@ -52,3 +52,22 @@ pub struct BigFishAssetSlot {
|
||||
pub(crate) prompt_snapshot: String,
|
||||
pub(crate) updated_at: Timestamp,
|
||||
}
|
||||
|
||||
#[spacetimedb::table(
|
||||
accessor = big_fish_runtime_run,
|
||||
index(accessor = by_big_fish_run_owner_user_id, btree(columns = [owner_user_id])),
|
||||
index(accessor = by_big_fish_run_session_id, btree(columns = [session_id]))
|
||||
)]
|
||||
pub struct BigFishRuntimeRun {
|
||||
#[primary_key]
|
||||
pub(crate) run_id: String,
|
||||
pub(crate) session_id: String,
|
||||
pub(crate) owner_user_id: String,
|
||||
pub(crate) status: BigFishRunStatus,
|
||||
pub(crate) snapshot_json: String,
|
||||
pub(crate) last_input_x: f32,
|
||||
pub(crate) last_input_y: f32,
|
||||
pub(crate) tick: u64,
|
||||
pub(crate) created_at: Timestamp,
|
||||
pub(crate) updated_at: Timestamp,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user