This commit is contained in:
@@ -23,10 +23,8 @@ use shared_contracts::big_fish::{
|
||||
BigFishActionResponse, BigFishAgentMessageResponse, BigFishAnchorItemResponse,
|
||||
BigFishAnchorPackResponse, BigFishAssetCoverageResponse, BigFishAssetSlotResponse,
|
||||
BigFishBackgroundBlueprintResponse, BigFishGameDraftResponse, BigFishLevelBlueprintResponse,
|
||||
BigFishRunResponse, BigFishRuntimeEntityResponse, BigFishRuntimeParamsResponse,
|
||||
BigFishRuntimeSnapshotResponse, BigFishSessionResponse, BigFishSessionSnapshotResponse,
|
||||
BigFishVector2Response, CreateBigFishSessionRequest, ExecuteBigFishActionRequest,
|
||||
SendBigFishMessageRequest, SubmitBigFishInputRequest,
|
||||
BigFishRuntimeParamsResponse, BigFishSessionResponse, BigFishSessionSnapshotResponse,
|
||||
CreateBigFishSessionRequest, ExecuteBigFishActionRequest, SendBigFishMessageRequest,
|
||||
};
|
||||
use shared_contracts::big_fish_works::{BigFishWorkSummaryResponse, BigFishWorksResponse};
|
||||
use shared_kernel::{build_prefixed_uuid_id, format_timestamp_micros};
|
||||
@@ -34,10 +32,8 @@ use spacetime_client::{
|
||||
BigFishAgentMessageRecord, BigFishAnchorItemRecord, BigFishAnchorPackRecord,
|
||||
BigFishAssetCoverageRecord, BigFishAssetGenerateRecordInput, BigFishAssetSlotRecord,
|
||||
BigFishBackgroundBlueprintRecord, BigFishGameDraftRecord, BigFishLevelBlueprintRecord,
|
||||
BigFishMessageSubmitRecordInput, BigFishRunInputSubmitRecordInput, BigFishRunStartRecordInput,
|
||||
BigFishRuntimeEntityRecord, BigFishRuntimeParamsRecord, BigFishRuntimeRecord,
|
||||
BigFishSessionCreateRecordInput, BigFishSessionRecord, BigFishVector2Record,
|
||||
BigFishWorkSummaryRecord, SpacetimeClientError,
|
||||
BigFishMessageSubmitRecordInput, BigFishRuntimeParamsRecord, BigFishSessionCreateRecordInput,
|
||||
BigFishSessionRecord, BigFishWorkSummaryRecord, SpacetimeClientError,
|
||||
};
|
||||
use tokio::time::sleep;
|
||||
|
||||
@@ -577,99 +573,6 @@ pub async fn execute_big_fish_action(
|
||||
))
|
||||
}
|
||||
|
||||
pub async fn start_big_fish_run(
|
||||
State(state): State<AppState>,
|
||||
Path(session_id): Path<String>,
|
||||
Extension(request_context): Extension<RequestContext>,
|
||||
Extension(authenticated): Extension<AuthenticatedAccessToken>,
|
||||
) -> Result<Json<Value>, Response> {
|
||||
ensure_non_empty(&request_context, &session_id, "sessionId")?;
|
||||
|
||||
let run = state
|
||||
.spacetime_client()
|
||||
.start_big_fish_run(BigFishRunStartRecordInput {
|
||||
run_id: build_prefixed_uuid_id("big-fish-run-"),
|
||||
session_id,
|
||||
owner_user_id: authenticated.claims().user_id().to_string(),
|
||||
started_at_micros: current_utc_micros(),
|
||||
})
|
||||
.await
|
||||
.map_err(|error| {
|
||||
big_fish_error_response(&request_context, map_big_fish_client_error(error))
|
||||
})?;
|
||||
|
||||
Ok(json_success_body(
|
||||
Some(&request_context),
|
||||
BigFishRunResponse {
|
||||
run: map_big_fish_runtime_response(run),
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
pub async fn get_big_fish_run(
|
||||
State(state): State<AppState>,
|
||||
Path(run_id): Path<String>,
|
||||
Extension(request_context): Extension<RequestContext>,
|
||||
Extension(authenticated): Extension<AuthenticatedAccessToken>,
|
||||
) -> Result<Json<Value>, Response> {
|
||||
ensure_non_empty(&request_context, &run_id, "runId")?;
|
||||
|
||||
let run = state
|
||||
.spacetime_client()
|
||||
.get_big_fish_run(run_id, authenticated.claims().user_id().to_string())
|
||||
.await
|
||||
.map_err(|error| {
|
||||
big_fish_error_response(&request_context, map_big_fish_client_error(error))
|
||||
})?;
|
||||
|
||||
Ok(json_success_body(
|
||||
Some(&request_context),
|
||||
BigFishRunResponse {
|
||||
run: map_big_fish_runtime_response(run),
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
pub async fn submit_big_fish_input(
|
||||
State(state): State<AppState>,
|
||||
Path(run_id): Path<String>,
|
||||
Extension(request_context): Extension<RequestContext>,
|
||||
Extension(authenticated): Extension<AuthenticatedAccessToken>,
|
||||
payload: Result<Json<SubmitBigFishInputRequest>, JsonRejection>,
|
||||
) -> Result<Json<Value>, Response> {
|
||||
let Json(payload) = payload.map_err(|error| {
|
||||
big_fish_error_response(
|
||||
&request_context,
|
||||
AppError::from_status(StatusCode::BAD_REQUEST).with_details(json!({
|
||||
"provider": "big-fish",
|
||||
"message": error.body_text(),
|
||||
})),
|
||||
)
|
||||
})?;
|
||||
ensure_non_empty(&request_context, &run_id, "runId")?;
|
||||
|
||||
let run = state
|
||||
.spacetime_client()
|
||||
.submit_big_fish_input(BigFishRunInputSubmitRecordInput {
|
||||
run_id,
|
||||
owner_user_id: authenticated.claims().user_id().to_string(),
|
||||
input_x: payload.x,
|
||||
input_y: payload.y,
|
||||
submitted_at_micros: current_utc_micros(),
|
||||
})
|
||||
.await
|
||||
.map_err(|error| {
|
||||
big_fish_error_response(&request_context, map_big_fish_client_error(error))
|
||||
})?;
|
||||
|
||||
Ok(json_success_body(
|
||||
Some(&request_context),
|
||||
BigFishRunResponse {
|
||||
run: map_big_fish_runtime_response(run),
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
fn map_big_fish_session_response(session: BigFishSessionRecord) -> BigFishSessionSnapshotResponse {
|
||||
BigFishSessionSnapshotResponse {
|
||||
session_id: session.session_id,
|
||||
@@ -910,32 +813,6 @@ fn map_big_fish_agent_message_response(
|
||||
}
|
||||
}
|
||||
|
||||
fn map_big_fish_runtime_response(run: BigFishRuntimeRecord) -> BigFishRuntimeSnapshotResponse {
|
||||
BigFishRuntimeSnapshotResponse {
|
||||
run_id: run.run_id,
|
||||
session_id: run.session_id,
|
||||
status: run.status,
|
||||
tick: run.tick,
|
||||
player_level: run.player_level,
|
||||
win_level: run.win_level,
|
||||
leader_entity_id: run.leader_entity_id,
|
||||
owned_entities: run
|
||||
.owned_entities
|
||||
.into_iter()
|
||||
.map(map_big_fish_entity_response)
|
||||
.collect(),
|
||||
wild_entities: run
|
||||
.wild_entities
|
||||
.into_iter()
|
||||
.map(map_big_fish_entity_response)
|
||||
.collect(),
|
||||
camera_center: map_big_fish_vector_response(run.camera_center),
|
||||
last_input: map_big_fish_vector_response(run.last_input),
|
||||
event_log: run.event_log,
|
||||
updated_at: run.updated_at,
|
||||
}
|
||||
}
|
||||
|
||||
fn map_big_fish_work_summary_response(
|
||||
item: BigFishWorkSummaryRecord,
|
||||
) -> BigFishWorkSummaryResponse {
|
||||
@@ -957,25 +834,6 @@ fn map_big_fish_work_summary_response(
|
||||
}
|
||||
}
|
||||
|
||||
fn map_big_fish_entity_response(
|
||||
entity: BigFishRuntimeEntityRecord,
|
||||
) -> BigFishRuntimeEntityResponse {
|
||||
BigFishRuntimeEntityResponse {
|
||||
entity_id: entity.entity_id,
|
||||
level: entity.level,
|
||||
position: map_big_fish_vector_response(entity.position),
|
||||
radius: entity.radius,
|
||||
offscreen_seconds: entity.offscreen_seconds,
|
||||
}
|
||||
}
|
||||
|
||||
fn map_big_fish_vector_response(vector: BigFishVector2Record) -> BigFishVector2Response {
|
||||
BigFishVector2Response {
|
||||
x: vector.x,
|
||||
y: vector.y,
|
||||
}
|
||||
}
|
||||
|
||||
fn build_big_fish_welcome_text(seed_text: &str) -> String {
|
||||
if seed_text.trim().is_empty() {
|
||||
return "我会先帮你确定大鱼吃小鱼的核心锚点。可以从主题生态、成长阶梯或风险节奏开始。"
|
||||
@@ -1013,7 +871,8 @@ struct BigFishFormalAssetContext {
|
||||
|
||||
const BIG_FISH_TEXT_TO_IMAGE_MODEL: &str = "wan2.2-t2i-flash";
|
||||
const BIG_FISH_ENTITY_KIND: &str = "big_fish_session";
|
||||
const BIG_FISH_DEFAULT_NEGATIVE_PROMPT: &str = "文字,水印,logo,UI界面,对话框,边框,多余肢体,畸形鱼体,低清晰度,模糊,压缩噪点,现代摄影棚,写实照片背景";
|
||||
const BIG_FISH_DEFAULT_NEGATIVE_PROMPT: &str = "文字,水印,logo,UI界面,对话框,边框,多余肢体,畸形鱼体,低清晰度,模糊,压缩噪点,现代摄影棚,写实照片背景,复杂背景";
|
||||
const BIG_FISH_TRANSPARENT_ASSET_NEGATIVE_PROMPT: &str = "文字,水印,logo,UI界面,对话框,边框,多余肢体,畸形鱼体,低清晰度,模糊,压缩噪点,现代摄影棚,写实照片背景,场景背景,水草背景,气泡背景,多只主体,阴影地面";
|
||||
|
||||
async fn generate_big_fish_formal_asset(
|
||||
state: &AppState,
|
||||
@@ -1087,7 +946,7 @@ fn build_big_fish_formal_asset_context(
|
||||
Ok(BigFishFormalAssetContext {
|
||||
entity_id: session.session_id.clone(),
|
||||
prompt: build_big_fish_level_main_image_prompt(draft, level),
|
||||
negative_prompt: BIG_FISH_DEFAULT_NEGATIVE_PROMPT.to_string(),
|
||||
negative_prompt: BIG_FISH_TRANSPARENT_ASSET_NEGATIVE_PROMPT.to_string(),
|
||||
size: "1024*1024".to_string(),
|
||||
asset_object_kind: "big_fish_level_main_image".to_string(),
|
||||
binding_slot: format!("level_main_image:{level_part}"),
|
||||
@@ -1114,7 +973,7 @@ fn build_big_fish_formal_asset_context(
|
||||
Ok(BigFishFormalAssetContext {
|
||||
entity_id: session.session_id.clone(),
|
||||
prompt: build_big_fish_level_motion_prompt(draft, level, motion_key),
|
||||
negative_prompt: BIG_FISH_DEFAULT_NEGATIVE_PROMPT.to_string(),
|
||||
negative_prompt: BIG_FISH_TRANSPARENT_ASSET_NEGATIVE_PROMPT.to_string(),
|
||||
size: "1024*1024".to_string(),
|
||||
asset_object_kind: "big_fish_level_motion".to_string(),
|
||||
binding_slot: format!("level_motion:{level_part}:{motion_key}"),
|
||||
@@ -1190,8 +1049,8 @@ fn build_big_fish_level_main_image_prompt(
|
||||
),
|
||||
format!("轮廓方向:{}。", level.silhouette_direction),
|
||||
format!("视觉提示词种子:{}。", level.visual_prompt_seed),
|
||||
"画面要求:单体游戏生物完整入镜,轮廓清晰,适合作为大鱼吃小鱼等级角色主图,2D 高完成度游戏插画,深海发光质感,中央构图。".to_string(),
|
||||
"不要出现 UI、文字、logo、水印、对话框或边框;背景保持干净的深海渐变或透明感,不要出现多只主体。".to_string(),
|
||||
"画面要求:按 RPG 角色资产口径生成,单体鱼形游戏生物完整入镜,轮廓清晰,中心构图,2D 高完成度游戏插画,深海发光质感。".to_string(),
|
||||
"背景要求:透明背景 PNG 风格,不出现任何场景、水草、气泡、阴影地面、UI、文字、logo、水印、对话框或边框;不要出现多只主体。".to_string(),
|
||||
]
|
||||
.join("")
|
||||
}
|
||||
@@ -1217,8 +1076,8 @@ fn build_big_fish_level_motion_prompt(
|
||||
),
|
||||
format!("动作提示词种子:{}。", level.motion_prompt_seed),
|
||||
format!("动作要求:{motion_text}"),
|
||||
"画面要求:单体生物完整入镜,轮廓清晰,动作方向明确,2D 高完成度游戏插画,适合作为 Big Fish 动作槽位的静态 keyframe。".to_string(),
|
||||
"不要出现 UI、文字、logo、水印、对话框或边框;不要生成序列帧拼图,不要出现多只主体。".to_string(),
|
||||
"画面要求:按 RPG 角色动画资产口径生成,单体鱼形生物完整入镜,轮廓清晰,动作方向明确,2D 高完成度游戏插画,适合作为 Big Fish 动作槽位的静态 keyframe。".to_string(),
|
||||
"背景要求:透明背景 PNG 风格,不出现任何场景、水草、气泡、阴影地面、UI、文字、logo、水印、对话框或边框;不要生成序列帧拼图,不要出现多只主体。".to_string(),
|
||||
]
|
||||
.join("")
|
||||
}
|
||||
@@ -1238,8 +1097,8 @@ fn build_big_fish_stage_background_prompt(draft: &BigFishGameDraftRecord) -> Str
|
||||
format!("安全操作区:{}。", background.safe_play_area_hint),
|
||||
format!("出生边缘:{}。", background.spawn_edge_hint),
|
||||
format!("背景提示词种子:{}。", background.background_prompt_seed),
|
||||
"画面要求:竖屏 9:16,中央 70% 保持清爽可读,边缘有深海生态层次和微弱生物光,适合作为大鱼吃小鱼运行态背景。".to_string(),
|
||||
"不要出现 UI、文字、logo、水印、对话框、边框或巨大主体遮挡;不要把中央操作区画得过暗或过复杂。".to_string(),
|
||||
"画面要求:竖屏 9:16,大场地,全屏运行态背景,中央 80% 保持开阔清爽,边缘只保留少量出生区环境提示。".to_string(),
|
||||
"元素要求:整体元素少,不出现大型主体、密集装饰、鱼群主角、UI、文字、logo、水印、对话框或边框;不要把中央操作区画得过暗或过复杂。".to_string(),
|
||||
]
|
||||
.join("")
|
||||
}
|
||||
@@ -1769,8 +1628,7 @@ fn big_fish_sse_error_event_message(message: String) -> Event {
|
||||
fn map_big_fish_client_error(error: SpacetimeClientError) -> AppError {
|
||||
let status = match &error {
|
||||
SpacetimeClientError::Procedure(message)
|
||||
if message.contains("big_fish_creation_session 不存在")
|
||||
|| message.contains("big_fish_runtime_run 不存在") =>
|
||||
if message.contains("big_fish_creation_session 不存在") =>
|
||||
{
|
||||
StatusCode::NOT_FOUND
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user