Integrate unfinished server-rs refactor worklists

This commit is contained in:
2026-04-30 13:39:06 +08:00
parent 62934b0809
commit 7ab0933f6d
676 changed files with 24487 additions and 21531 deletions

View File

@@ -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}"))?;

View File

@@ -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::*;

View 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(&current.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())
}

View File

@@ -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 {

View File

@@ -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,
}