合并 master 并保留拼消消体验路径

合入 master 的平台 UI 与后端更新。

解决拼消消创作页、结果页和运行态冲突。

保留拼消消发布前检查、背景图命名、拖拽边界与补牌动画修复。
This commit is contained in:
2026-06-12 16:38:23 +08:00
396 changed files with 43653 additions and 13291 deletions

View File

@@ -51,7 +51,8 @@ pub use mapper::{
PublicWorkGalleryEntryRecord, PuzzleAgentMessageFinalizeRecordInput, PuzzleAgentMessageRecord,
PuzzleAgentMessageSubmitRecordInput, PuzzleAgentSessionCreateRecordInput,
PuzzleAgentSessionRecord, PuzzleAgentSuggestedActionRecord, PuzzleAnchorItemRecord,
PuzzleAnchorPackRecord, PuzzleAudioAssetRecord, PuzzleBoardRecord, PuzzleCellPositionRecord,
PuzzleAnchorPackRecord, PuzzleAudioAssetRecord, PuzzleBackgroundCompileTaskClaimRecordInput,
PuzzleBackgroundCompileTaskReleaseRecordInput, PuzzleBoardRecord, PuzzleCellPositionRecord,
PuzzleClearActionRequest, PuzzleClearActionResponse, PuzzleClearActionType,
PuzzleClearBoardCell, PuzzleClearBoardSnapshot, PuzzleClearCardAsset, PuzzleClearDraftResponse,
PuzzleClearGenerationStatus, PuzzleClearImageAsset, PuzzleClearNextLevelRequest,
@@ -348,7 +349,7 @@ type ProcedureResultSender<T> =
type ReducerResultSender = Arc<Mutex<Option<oneshot::Sender<Result<(), SpacetimeClientError>>>>>;
struct SpacetimeConnectionPool {
slots: Vec<tokio::sync::Mutex<PooledConnectionSlot>>,
slots: Vec<PooledConnectionSlot>,
permits: Arc<Semaphore>,
}
@@ -371,8 +372,10 @@ impl SpacetimeStageError {
}
struct PooledConnectionSlot {
connection: Option<PooledConnection>,
in_use: bool,
// 槽位占用标记独立成原子量:抢占/复位不依赖锁,租约 Drop 兜底可以同步完成。
in_use: AtomicBool,
// in_use=true 的持有者独占本槽连接,正常情况下锁上不会有竞争。
connection: tokio::sync::Mutex<Option<PooledConnection>>,
}
struct PooledConnection {
@@ -385,9 +388,28 @@ struct PooledConnection {
struct PooledConnectionLease {
slot_index: usize,
connection: Option<PooledConnection>,
pool: Arc<SpacetimeConnectionPool>,
_permit: OwnedSemaphorePermit,
}
impl Drop for PooledConnectionLease {
// 租约 Drop 兜底:请求 future 被取消(如客户端断开导致 handler 被丢弃)时,
// 也必须归还连接并复位槽位,否则槽位会永久停留在 in_use 状态、连接池逐渐耗尽。
fn drop(&mut self) {
let slot = &self.pool.slots[self.slot_index];
if let Some(connection) = self.connection.take() {
if !connection.is_broken() {
if let Ok(mut slot_connection) = slot.connection.try_lock() {
*slot_connection = Some(connection);
}
// try_lock 理论上不会失败in_use 持有者独占);万一失败只丢弃连接,不丢槽位。
}
}
slot.in_use.store(false, Ordering::Release);
// _permit 随 Drop 自动归还信号量。
}
}
impl SpacetimeClient {
pub fn new(config: SpacetimeClientConfig) -> Self {
let pool_size = config.pool_size.max(1) as usize;
@@ -400,11 +422,9 @@ impl SpacetimeClient {
..config
};
let slots = (0..pool_size)
.map(|_| {
tokio::sync::Mutex::new(PooledConnectionSlot {
connection: None,
in_use: false,
})
.map(|_| PooledConnectionSlot {
in_use: AtomicBool::new(false),
connection: tokio::sync::Mutex::new(None),
})
.collect::<Vec<_>>();
let pool = Arc::new(SpacetimeConnectionPool {
@@ -678,42 +698,49 @@ impl SpacetimeClient {
)
})?;
loop {
for (slot_index, slot) in self.pool.slots.iter().enumerate() {
if let Ok(mut slot_guard) = slot.try_lock() {
if slot_guard.in_use {
continue;
}
let reusable_connection = slot_guard
.connection
.take()
.filter(|connection| !connection.is_broken());
slot_guard.in_use = true;
drop(slot_guard);
// 持有 permit 即保证最多 pool_size 个并发持有者,必然能抢到一个空闲槽位;
// CAS 抢占后立即构造租约,后续任何失败/取消都由租约 Drop 兜底复位槽位。
let slot_index = self
.pool
.slots
.iter()
.position(|slot| {
slot.in_use
.compare_exchange(false, true, Ordering::AcqRel, Ordering::Acquire)
.is_ok()
})
.ok_or_else(|| {
SpacetimeStageError::new(
SpacetimeClientStage::PoolAcquire,
SpacetimeClientError::Runtime(
"SpacetimeDB 连接池 permit 与槽位状态不一致".to_string(),
),
)
})?;
let connection = if let Some(connection) = reusable_connection {
connection
} else {
match self.build_pooled_connection(operation_timeout).await {
Ok(connection) => connection,
Err(error) => {
let mut slot_guard = self.pool.slots[slot_index].lock().await;
slot_guard.in_use = false;
return Err(error);
}
}
};
let mut lease = PooledConnectionLease {
slot_index,
connection: None,
pool: self.pool.clone(),
_permit: permit,
};
return Ok(PooledConnectionLease {
slot_index,
connection: Some(connection),
_permit: permit,
});
}
}
let reusable_connection = self.pool.slots[slot_index]
.connection
.lock()
.await
.take()
.filter(|connection| !connection.is_broken());
tokio::task::yield_now().await;
}
let connection = if let Some(connection) = reusable_connection {
connection
} else {
// 建连失败时直接返回错误,槽位与 permit 由 lease Drop 自动归还。
self.build_pooled_connection(operation_timeout).await?
};
lease.connection = Some(connection);
Ok(lease)
}
async fn build_pooled_connection(
@@ -911,18 +938,10 @@ impl SpacetimeClient {
Ok(subscription)
}
async fn release_connection(&self, mut lease: PooledConnectionLease) {
let mut slot_guard = self.pool.slots[lease.slot_index].lock().await;
slot_guard.in_use = false;
let Some(connection) = lease.connection.take() else {
slot_guard.connection = None;
return;
};
if connection.is_broken() {
slot_guard.connection = None;
} else {
slot_guard.connection = Some(connection);
}
async fn release_connection(&self, lease: PooledConnectionLease) {
// 显式归还与“请求被取消”的隐式归还共用同一套租约 Drop 兜底逻辑,
// 保证任何路径下槽位与 permit 都会复位,连接池不会被慢慢泄漏占满。
drop(lease);
}
// 超时后必须统一归还租约;若连接已先一步断开则回传断线,否则标记坏连接并回传超时。
@@ -1127,4 +1146,78 @@ mod tests {
SpacetimeClientError::Runtime(_)
));
}
fn test_client(pool_size: u32, procedure_timeout: Duration) -> SpacetimeClient {
SpacetimeClient::new(SpacetimeClientConfig {
// 指向本机不可达端口:测试只验证连接池行为,不需要真实 SpacetimeDB。
server_url: "http://127.0.0.1:9".to_string(),
database: "pool-test".to_string(),
token: None,
pool_size,
procedure_timeout,
})
}
/// 复现线上故障机制:修复前请求 future 被取消时租约不会归还,槽位永久停留在 in_use
/// 后续 acquire 拿着 permit 空转挂死。修复后租约 Drop 必须同时复位槽位与 permit。
#[tokio::test]
async fn dropped_lease_releases_slot_and_permit() {
let client = test_client(1, Duration::from_millis(200));
let permit = client
.pool
.permits
.clone()
.acquire_owned()
.await
.expect("permit should acquire");
client.pool.slots[0].in_use.store(true, Ordering::SeqCst);
assert_eq!(client.pool.permits.available_permits(), 0);
// 模拟请求被取消:租约未经过 release_connection 直接被 Drop。
let lease = PooledConnectionLease {
slot_index: 0,
connection: None,
pool: client.pool.clone(),
_permit: permit,
};
drop(lease);
assert!(
!client.pool.slots[0].in_use.load(Ordering::SeqCst),
"租约 Drop 后槽位必须复位,否则连接池会被泄漏占满"
);
assert_eq!(
client.pool.permits.available_permits(),
1,
"租约 Drop 后 permit 必须归还"
);
}
/// 池内 permit 全部被占用持续在途请求acquire 必须在超时窗口内返回
/// pool_acquire 超时,而不是无限等待。
#[tokio::test]
async fn acquire_times_out_at_pool_acquire_when_pool_is_busy() {
let client = test_client(1, Duration::from_millis(200));
let _held_permit = client
.pool
.permits
.clone()
.acquire_owned()
.await
.expect("permit should acquire");
let result = tokio::time::timeout(
Duration::from_secs(5),
client.acquire_connection_with_timeout(Duration::from_millis(200)),
)
.await
.expect("acquire 必须在超时窗口内返回,而不是空转挂死");
let error = match result {
Ok(_) => panic!("池占满时应返回 pool_acquire 超时"),
Err(error) => error,
};
assert_eq!(error.stage, SpacetimeClientStage::PoolAcquire);
assert!(matches!(error.error, SpacetimeClientError::Timeout));
}
}

View File

@@ -101,7 +101,8 @@ pub use self::puzzle::{
PuzzleAgentMessageFinalizeRecordInput, PuzzleAgentMessageRecord,
PuzzleAgentMessageSubmitRecordInput, PuzzleAgentSessionCreateRecordInput,
PuzzleAgentSessionRecord, PuzzleAgentSuggestedActionRecord, PuzzleAnchorItemRecord,
PuzzleAnchorPackRecord, PuzzleAudioAssetRecord, PuzzleBoardRecord, PuzzleCellPositionRecord,
PuzzleAnchorPackRecord, PuzzleAudioAssetRecord, PuzzleBackgroundCompileTaskClaimRecordInput,
PuzzleBackgroundCompileTaskReleaseRecordInput, PuzzleBoardRecord, PuzzleCellPositionRecord,
PuzzleCreatorIntentRecord, PuzzleDraftCompileFailureRecordInput, PuzzleDraftLevelRecord,
PuzzleFormDraftRecord, PuzzleFormDraftSaveRecordInput, PuzzleGalleryCardRecord,
PuzzleGeneratedImageCandidateRecord, PuzzleGeneratedImagesSaveRecordInput,
@@ -199,10 +200,10 @@ pub(crate) use self::public_work::{
map_public_work_gallery_entry, map_public_work_gallery_entry_to_detail_entry,
};
pub(crate) use self::puzzle::{
map_puzzle_agent_session_procedure_result, map_puzzle_gallery_card_view_row,
map_puzzle_run_procedure_result, map_puzzle_work_procedure_result,
map_puzzle_works_procedure_result, map_runtime_profile_wallet_ledger_source_type_back,
parse_puzzle_agent_stage_record,
map_puzzle_agent_session_procedure_result, map_puzzle_background_compile_task_procedure_result,
map_puzzle_gallery_card_view_row, map_puzzle_run_procedure_result,
map_puzzle_work_procedure_result, map_puzzle_works_procedure_result,
map_runtime_profile_wallet_ledger_source_type_back, parse_puzzle_agent_stage_record,
};
pub(crate) use self::puzzle_clear::{
map_puzzle_clear_agent_session_procedure_result, map_puzzle_clear_gallery_card_view_row,

View File

@@ -13,6 +13,16 @@ pub(crate) fn map_puzzle_agent_session_procedure_result(
Ok(map_puzzle_agent_session_snapshot(session))
}
pub(crate) fn map_puzzle_background_compile_task_procedure_result(
result: PuzzleBackgroundCompileTaskProcedureResult,
) -> Result<bool, SpacetimeClientError> {
if !result.ok {
return Err(SpacetimeClientError::procedure_failed(result.error_message));
}
Ok(result.claimed)
}
pub(crate) fn map_puzzle_work_procedure_result(
result: PuzzleWorkProcedureResult,
) -> Result<PuzzleWorkProfileRecord, SpacetimeClientError> {
@@ -614,6 +624,23 @@ pub struct PuzzleFormDraftSaveRecordInput {
pub saved_at_micros: i64,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct PuzzleBackgroundCompileTaskClaimRecordInput {
pub task_id: String,
pub claim_id: String,
pub session_id: String,
pub owner_user_id: String,
pub claimed_at_micros: i64,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct PuzzleBackgroundCompileTaskReleaseRecordInput {
pub task_id: String,
pub claim_id: String,
pub session_id: String,
pub owner_user_id: String,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct PuzzleAgentMessageSubmitRecordInput {
pub session_id: String,

View File

@@ -204,6 +204,7 @@ pub mod chapter_progression_table;
pub mod chapter_progression_type;
pub mod checkpoint_wooden_fish_run_procedure;
pub mod claim_profile_task_reward_and_return_procedure;
pub mod claim_puzzle_background_compile_task_procedure;
pub mod claim_puzzle_work_point_incentive_procedure;
pub mod clear_database_migration_import_chunks_procedure;
pub mod clear_platform_browse_history_and_return_procedure;
@@ -628,6 +629,11 @@ pub mod puzzle_anchor_item_type;
pub mod puzzle_anchor_pack_type;
pub mod puzzle_anchor_status_type;
pub mod puzzle_audio_asset_type;
pub mod puzzle_background_compile_task_claim_input_type;
pub mod puzzle_background_compile_task_procedure_result_type;
pub mod puzzle_background_compile_task_release_input_type;
pub mod puzzle_background_compile_task_row_type;
pub mod puzzle_background_compile_task_table;
pub mod puzzle_board_snapshot_type;
pub mod puzzle_cell_position_type;
pub mod puzzle_clear_agent_session_create_input_type;
@@ -766,6 +772,7 @@ pub mod redeem_profile_reward_code_procedure;
pub mod refresh_session_table;
pub mod refresh_session_type;
pub mod refund_profile_wallet_points_and_return_procedure;
pub mod release_puzzle_background_compile_task_procedure;
pub mod remix_big_fish_work_procedure;
pub mod remix_custom_world_profile_procedure;
pub mod remix_puzzle_work_procedure;
@@ -1312,6 +1319,7 @@ pub use chapter_progression_table::*;
pub use chapter_progression_type::ChapterProgression;
pub use checkpoint_wooden_fish_run_procedure::checkpoint_wooden_fish_run;
pub use claim_profile_task_reward_and_return_procedure::claim_profile_task_reward_and_return;
pub use claim_puzzle_background_compile_task_procedure::claim_puzzle_background_compile_task;
pub use claim_puzzle_work_point_incentive_procedure::claim_puzzle_work_point_incentive;
pub use clear_database_migration_import_chunks_procedure::clear_database_migration_import_chunks;
pub use clear_platform_browse_history_and_return_procedure::clear_platform_browse_history_and_return;
@@ -1736,6 +1744,11 @@ pub use puzzle_anchor_item_type::PuzzleAnchorItem;
pub use puzzle_anchor_pack_type::PuzzleAnchorPack;
pub use puzzle_anchor_status_type::PuzzleAnchorStatus;
pub use puzzle_audio_asset_type::PuzzleAudioAsset;
pub use puzzle_background_compile_task_claim_input_type::PuzzleBackgroundCompileTaskClaimInput;
pub use puzzle_background_compile_task_procedure_result_type::PuzzleBackgroundCompileTaskProcedureResult;
pub use puzzle_background_compile_task_release_input_type::PuzzleBackgroundCompileTaskReleaseInput;
pub use puzzle_background_compile_task_row_type::PuzzleBackgroundCompileTaskRow;
pub use puzzle_background_compile_task_table::*;
pub use puzzle_board_snapshot_type::PuzzleBoardSnapshot;
pub use puzzle_cell_position_type::PuzzleCellPosition;
pub use puzzle_clear_agent_session_create_input_type::PuzzleClearAgentSessionCreateInput;
@@ -1874,6 +1887,7 @@ pub use redeem_profile_reward_code_procedure::redeem_profile_reward_code;
pub use refresh_session_table::*;
pub use refresh_session_type::RefreshSession;
pub use refund_profile_wallet_points_and_return_procedure::refund_profile_wallet_points_and_return;
pub use release_puzzle_background_compile_task_procedure::release_puzzle_background_compile_task;
pub use remix_big_fish_work_procedure::remix_big_fish_work;
pub use remix_custom_world_profile_procedure::remix_custom_world_profile;
pub use remix_puzzle_work_procedure::remix_puzzle_work;
@@ -2569,6 +2583,7 @@ pub struct DbUpdate {
public_work_play_daily_stat: __sdk::TableUpdate<PublicWorkPlayDailyStat>,
puzzle_agent_message: __sdk::TableUpdate<PuzzleAgentMessageRow>,
puzzle_agent_session: __sdk::TableUpdate<PuzzleAgentSessionRow>,
puzzle_background_compile_task: __sdk::TableUpdate<PuzzleBackgroundCompileTaskRow>,
puzzle_clear_agent_session: __sdk::TableUpdate<PuzzleClearAgentSessionRow>,
puzzle_clear_event: __sdk::TableUpdate<PuzzleClearEventRow>,
puzzle_clear_gallery_card_view: __sdk::TableUpdate<PuzzleClearGalleryCardViewRow>,
@@ -2854,6 +2869,11 @@ impl TryFrom<__ws::v2::TransactionUpdate> for DbUpdate {
"puzzle_agent_session" => db_update.puzzle_agent_session.append(
puzzle_agent_session_table::parse_table_update(table_update)?,
),
"puzzle_background_compile_task" => {
db_update.puzzle_background_compile_task.append(
puzzle_background_compile_task_table::parse_table_update(table_update)?,
)
}
"puzzle_clear_agent_session" => db_update.puzzle_clear_agent_session.append(
puzzle_clear_agent_session_table::parse_table_update(table_update)?,
),
@@ -3373,6 +3393,12 @@ impl __sdk::DbUpdate for DbUpdate {
&self.puzzle_agent_session,
)
.with_updates_by_pk(|row| &row.session_id);
diff.puzzle_background_compile_task = cache
.apply_diff_to_table::<PuzzleBackgroundCompileTaskRow>(
"puzzle_background_compile_task",
&self.puzzle_background_compile_task,
)
.with_updates_by_pk(|row| &row.task_id);
diff.puzzle_clear_agent_session = cache
.apply_diff_to_table::<PuzzleClearAgentSessionRow>(
"puzzle_clear_agent_session",
@@ -3828,6 +3854,9 @@ impl __sdk::DbUpdate for DbUpdate {
"puzzle_agent_session" => db_update
.puzzle_agent_session
.append(__sdk::parse_row_list_as_inserts(table_rows.rows)?),
"puzzle_background_compile_task" => db_update
.puzzle_background_compile_task
.append(__sdk::parse_row_list_as_inserts(table_rows.rows)?),
"puzzle_clear_agent_session" => db_update
.puzzle_clear_agent_session
.append(__sdk::parse_row_list_as_inserts(table_rows.rows)?),
@@ -4192,6 +4221,9 @@ impl __sdk::DbUpdate for DbUpdate {
"puzzle_agent_session" => db_update
.puzzle_agent_session
.append(__sdk::parse_row_list_as_deletes(table_rows.rows)?),
"puzzle_background_compile_task" => db_update
.puzzle_background_compile_task
.append(__sdk::parse_row_list_as_deletes(table_rows.rows)?),
"puzzle_clear_agent_session" => db_update
.puzzle_clear_agent_session
.append(__sdk::parse_row_list_as_deletes(table_rows.rows)?),
@@ -4410,6 +4442,7 @@ pub struct AppliedDiff<'r> {
public_work_play_daily_stat: __sdk::TableAppliedDiff<'r, PublicWorkPlayDailyStat>,
puzzle_agent_message: __sdk::TableAppliedDiff<'r, PuzzleAgentMessageRow>,
puzzle_agent_session: __sdk::TableAppliedDiff<'r, PuzzleAgentSessionRow>,
puzzle_background_compile_task: __sdk::TableAppliedDiff<'r, PuzzleBackgroundCompileTaskRow>,
puzzle_clear_agent_session: __sdk::TableAppliedDiff<'r, PuzzleClearAgentSessionRow>,
puzzle_clear_event: __sdk::TableAppliedDiff<'r, PuzzleClearEventRow>,
puzzle_clear_gallery_card_view: __sdk::TableAppliedDiff<'r, PuzzleClearGalleryCardViewRow>,
@@ -4829,6 +4862,11 @@ impl<'r> __sdk::AppliedDiff<'r> for AppliedDiff<'r> {
&self.puzzle_agent_session,
event,
);
callbacks.invoke_table_row_callbacks::<PuzzleBackgroundCompileTaskRow>(
"puzzle_background_compile_task",
&self.puzzle_background_compile_task,
event,
);
callbacks.invoke_table_row_callbacks::<PuzzleClearAgentSessionRow>(
"puzzle_clear_agent_session",
&self.puzzle_clear_agent_session,
@@ -5766,6 +5804,7 @@ impl __sdk::SpacetimeModule for RemoteModule {
public_work_play_daily_stat_table::register_table(client_cache);
puzzle_agent_message_table::register_table(client_cache);
puzzle_agent_session_table::register_table(client_cache);
puzzle_background_compile_task_table::register_table(client_cache);
puzzle_clear_agent_session_table::register_table(client_cache);
puzzle_clear_event_table::register_table(client_cache);
puzzle_clear_gallery_card_view_table::register_table(client_cache);
@@ -5885,6 +5924,7 @@ impl __sdk::SpacetimeModule for RemoteModule {
"public_work_play_daily_stat",
"puzzle_agent_message",
"puzzle_agent_session",
"puzzle_background_compile_task",
"puzzle_clear_agent_session",
"puzzle_clear_event",
"puzzle_clear_gallery_card_view",

View File

@@ -0,0 +1,59 @@
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
use super::puzzle_background_compile_task_claim_input_type::PuzzleBackgroundCompileTaskClaimInput;
use super::puzzle_background_compile_task_procedure_result_type::PuzzleBackgroundCompileTaskProcedureResult;
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
#[sats(crate = __lib)]
struct ClaimPuzzleBackgroundCompileTaskArgs {
pub input: PuzzleBackgroundCompileTaskClaimInput,
}
impl __sdk::InModule for ClaimPuzzleBackgroundCompileTaskArgs {
type Module = super::RemoteModule;
}
#[allow(non_camel_case_types)]
/// Extension trait for access to the procedure `claim_puzzle_background_compile_task`.
///
/// Implemented for [`super::RemoteProcedures`].
pub trait claim_puzzle_background_compile_task {
fn claim_puzzle_background_compile_task(&self, input: PuzzleBackgroundCompileTaskClaimInput) {
self.claim_puzzle_background_compile_task_then(input, |_, _| {});
}
fn claim_puzzle_background_compile_task_then(
&self,
input: PuzzleBackgroundCompileTaskClaimInput,
__callback: impl FnOnce(
&super::ProcedureEventContext,
Result<PuzzleBackgroundCompileTaskProcedureResult, __sdk::InternalError>,
) + Send
+ 'static,
);
}
impl claim_puzzle_background_compile_task for super::RemoteProcedures {
fn claim_puzzle_background_compile_task_then(
&self,
input: PuzzleBackgroundCompileTaskClaimInput,
__callback: impl FnOnce(
&super::ProcedureEventContext,
Result<PuzzleBackgroundCompileTaskProcedureResult, __sdk::InternalError>,
) + Send
+ 'static,
) {
self.imp
.invoke_procedure_with_callback::<_, PuzzleBackgroundCompileTaskProcedureResult>(
"claim_puzzle_background_compile_task",
ClaimPuzzleBackgroundCompileTaskArgs { input },
__callback,
);
}
}

View File

@@ -0,0 +1,19 @@
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
#[sats(crate = __lib)]
pub struct PuzzleBackgroundCompileTaskClaimInput {
pub task_id: String,
pub claim_id: String,
pub session_id: String,
pub owner_user_id: String,
pub claimed_at_micros: i64,
}
impl __sdk::InModule for PuzzleBackgroundCompileTaskClaimInput {
type Module = super::RemoteModule;
}

View File

@@ -0,0 +1,17 @@
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
#[sats(crate = __lib)]
pub struct PuzzleBackgroundCompileTaskProcedureResult {
pub ok: bool,
pub claimed: bool,
pub error_message: Option<String>,
}
impl __sdk::InModule for PuzzleBackgroundCompileTaskProcedureResult {
type Module = super::RemoteModule;
}

View File

@@ -0,0 +1,18 @@
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
#[sats(crate = __lib)]
pub struct PuzzleBackgroundCompileTaskReleaseInput {
pub task_id: String,
pub claim_id: String,
pub session_id: String,
pub owner_user_id: String,
}
impl __sdk::InModule for PuzzleBackgroundCompileTaskReleaseInput {
type Module = super::RemoteModule;
}

View File

@@ -0,0 +1,66 @@
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
#[sats(crate = __lib)]
pub struct PuzzleBackgroundCompileTaskRow {
pub task_id: String,
pub claim_id: String,
pub session_id: String,
pub owner_user_id: String,
pub created_at: __sdk::Timestamp,
pub updated_at: __sdk::Timestamp,
}
impl __sdk::InModule for PuzzleBackgroundCompileTaskRow {
type Module = super::RemoteModule;
}
/// Column accessor struct for the table `PuzzleBackgroundCompileTaskRow`.
///
/// Provides typed access to columns for query building.
pub struct PuzzleBackgroundCompileTaskRowCols {
pub task_id: __sdk::__query_builder::Col<PuzzleBackgroundCompileTaskRow, String>,
pub claim_id: __sdk::__query_builder::Col<PuzzleBackgroundCompileTaskRow, String>,
pub session_id: __sdk::__query_builder::Col<PuzzleBackgroundCompileTaskRow, String>,
pub owner_user_id: __sdk::__query_builder::Col<PuzzleBackgroundCompileTaskRow, String>,
pub created_at: __sdk::__query_builder::Col<PuzzleBackgroundCompileTaskRow, __sdk::Timestamp>,
pub updated_at: __sdk::__query_builder::Col<PuzzleBackgroundCompileTaskRow, __sdk::Timestamp>,
}
impl __sdk::__query_builder::HasCols for PuzzleBackgroundCompileTaskRow {
type Cols = PuzzleBackgroundCompileTaskRowCols;
fn cols(table_name: &'static str) -> Self::Cols {
PuzzleBackgroundCompileTaskRowCols {
task_id: __sdk::__query_builder::Col::new(table_name, "task_id"),
claim_id: __sdk::__query_builder::Col::new(table_name, "claim_id"),
session_id: __sdk::__query_builder::Col::new(table_name, "session_id"),
owner_user_id: __sdk::__query_builder::Col::new(table_name, "owner_user_id"),
created_at: __sdk::__query_builder::Col::new(table_name, "created_at"),
updated_at: __sdk::__query_builder::Col::new(table_name, "updated_at"),
}
}
}
/// Indexed column accessor struct for the table `PuzzleBackgroundCompileTaskRow`.
///
/// Provides typed access to indexed columns for query building.
pub struct PuzzleBackgroundCompileTaskRowIxCols {
pub session_id: __sdk::__query_builder::IxCol<PuzzleBackgroundCompileTaskRow, String>,
pub task_id: __sdk::__query_builder::IxCol<PuzzleBackgroundCompileTaskRow, String>,
}
impl __sdk::__query_builder::HasIxCols for PuzzleBackgroundCompileTaskRow {
type IxCols = PuzzleBackgroundCompileTaskRowIxCols;
fn ix_cols(table_name: &'static str) -> Self::IxCols {
PuzzleBackgroundCompileTaskRowIxCols {
session_id: __sdk::__query_builder::IxCol::new(table_name, "session_id"),
task_id: __sdk::__query_builder::IxCol::new(table_name, "task_id"),
}
}
}
impl __sdk::__query_builder::CanBeLookupTable for PuzzleBackgroundCompileTaskRow {}

View File

@@ -0,0 +1,169 @@
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use super::puzzle_background_compile_task_row_type::PuzzleBackgroundCompileTaskRow;
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
/// Table handle for the table `puzzle_background_compile_task`.
///
/// Obtain a handle from the [`PuzzleBackgroundCompileTaskTableAccess::puzzle_background_compile_task`] method on [`super::RemoteTables`],
/// like `ctx.db.puzzle_background_compile_task()`.
///
/// Users are encouraged not to explicitly reference this type,
/// but to directly chain method calls,
/// like `ctx.db.puzzle_background_compile_task().on_insert(...)`.
pub struct PuzzleBackgroundCompileTaskTableHandle<'ctx> {
imp: __sdk::TableHandle<PuzzleBackgroundCompileTaskRow>,
ctx: std::marker::PhantomData<&'ctx super::RemoteTables>,
}
#[allow(non_camel_case_types)]
/// Extension trait for access to the table `puzzle_background_compile_task`.
///
/// Implemented for [`super::RemoteTables`].
pub trait PuzzleBackgroundCompileTaskTableAccess {
#[allow(non_snake_case)]
/// Obtain a [`PuzzleBackgroundCompileTaskTableHandle`], which mediates access to the table `puzzle_background_compile_task`.
fn puzzle_background_compile_task(&self) -> PuzzleBackgroundCompileTaskTableHandle<'_>;
}
impl PuzzleBackgroundCompileTaskTableAccess for super::RemoteTables {
fn puzzle_background_compile_task(&self) -> PuzzleBackgroundCompileTaskTableHandle<'_> {
PuzzleBackgroundCompileTaskTableHandle {
imp: self
.imp
.get_table::<PuzzleBackgroundCompileTaskRow>("puzzle_background_compile_task"),
ctx: std::marker::PhantomData,
}
}
}
pub struct PuzzleBackgroundCompileTaskInsertCallbackId(__sdk::CallbackId);
pub struct PuzzleBackgroundCompileTaskDeleteCallbackId(__sdk::CallbackId);
impl<'ctx> __sdk::Table for PuzzleBackgroundCompileTaskTableHandle<'ctx> {
type Row = PuzzleBackgroundCompileTaskRow;
type EventContext = super::EventContext;
fn count(&self) -> u64 {
self.imp.count()
}
fn iter(&self) -> impl Iterator<Item = PuzzleBackgroundCompileTaskRow> + '_ {
self.imp.iter()
}
type InsertCallbackId = PuzzleBackgroundCompileTaskInsertCallbackId;
fn on_insert(
&self,
callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static,
) -> PuzzleBackgroundCompileTaskInsertCallbackId {
PuzzleBackgroundCompileTaskInsertCallbackId(self.imp.on_insert(Box::new(callback)))
}
fn remove_on_insert(&self, callback: PuzzleBackgroundCompileTaskInsertCallbackId) {
self.imp.remove_on_insert(callback.0)
}
type DeleteCallbackId = PuzzleBackgroundCompileTaskDeleteCallbackId;
fn on_delete(
&self,
callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static,
) -> PuzzleBackgroundCompileTaskDeleteCallbackId {
PuzzleBackgroundCompileTaskDeleteCallbackId(self.imp.on_delete(Box::new(callback)))
}
fn remove_on_delete(&self, callback: PuzzleBackgroundCompileTaskDeleteCallbackId) {
self.imp.remove_on_delete(callback.0)
}
}
pub struct PuzzleBackgroundCompileTaskUpdateCallbackId(__sdk::CallbackId);
impl<'ctx> __sdk::TableWithPrimaryKey for PuzzleBackgroundCompileTaskTableHandle<'ctx> {
type UpdateCallbackId = PuzzleBackgroundCompileTaskUpdateCallbackId;
fn on_update(
&self,
callback: impl FnMut(&Self::EventContext, &Self::Row, &Self::Row) + Send + 'static,
) -> PuzzleBackgroundCompileTaskUpdateCallbackId {
PuzzleBackgroundCompileTaskUpdateCallbackId(self.imp.on_update(Box::new(callback)))
}
fn remove_on_update(&self, callback: PuzzleBackgroundCompileTaskUpdateCallbackId) {
self.imp.remove_on_update(callback.0)
}
}
/// Access to the `task_id` unique index on the table `puzzle_background_compile_task`,
/// which allows point queries on the field of the same name
/// via the [`PuzzleBackgroundCompileTaskTaskIdUnique::find`] method.
///
/// Users are encouraged not to explicitly reference this type,
/// but to directly chain method calls,
/// like `ctx.db.puzzle_background_compile_task().task_id().find(...)`.
pub struct PuzzleBackgroundCompileTaskTaskIdUnique<'ctx> {
imp: __sdk::UniqueConstraintHandle<PuzzleBackgroundCompileTaskRow, String>,
phantom: std::marker::PhantomData<&'ctx super::RemoteTables>,
}
impl<'ctx> PuzzleBackgroundCompileTaskTableHandle<'ctx> {
/// Get a handle on the `task_id` unique index on the table `puzzle_background_compile_task`.
pub fn task_id(&self) -> PuzzleBackgroundCompileTaskTaskIdUnique<'ctx> {
PuzzleBackgroundCompileTaskTaskIdUnique {
imp: self.imp.get_unique_constraint::<String>("task_id"),
phantom: std::marker::PhantomData,
}
}
}
impl<'ctx> PuzzleBackgroundCompileTaskTaskIdUnique<'ctx> {
/// Find the subscribed row whose `task_id` column value is equal to `col_val`,
/// if such a row is present in the client cache.
pub fn find(&self, col_val: &String) -> Option<PuzzleBackgroundCompileTaskRow> {
self.imp.find(col_val)
}
}
#[doc(hidden)]
pub(super) fn register_table(client_cache: &mut __sdk::ClientCache<super::RemoteModule>) {
let _table = client_cache
.get_or_make_table::<PuzzleBackgroundCompileTaskRow>("puzzle_background_compile_task");
_table.add_unique_constraint::<String>("task_id", |row| &row.task_id);
}
#[doc(hidden)]
pub(super) fn parse_table_update(
raw_updates: __ws::v2::TableUpdate,
) -> __sdk::Result<__sdk::TableUpdate<PuzzleBackgroundCompileTaskRow>> {
__sdk::TableUpdate::parse_table_update(raw_updates).map_err(|e| {
__sdk::InternalError::failed_parse(
"TableUpdate<PuzzleBackgroundCompileTaskRow>",
"TableUpdate",
)
.with_cause(e)
.into()
})
}
#[allow(non_camel_case_types)]
/// Extension trait for query builder access to the table `PuzzleBackgroundCompileTaskRow`.
///
/// Implemented for [`__sdk::QueryTableAccessor`].
pub trait puzzle_background_compile_taskQueryTableAccess {
#[allow(non_snake_case)]
/// Get a query builder for the table `PuzzleBackgroundCompileTaskRow`.
fn puzzle_background_compile_task(
&self,
) -> __sdk::__query_builder::Table<PuzzleBackgroundCompileTaskRow>;
}
impl puzzle_background_compile_taskQueryTableAccess for __sdk::QueryTableAccessor {
fn puzzle_background_compile_task(
&self,
) -> __sdk::__query_builder::Table<PuzzleBackgroundCompileTaskRow> {
__sdk::__query_builder::Table::new("puzzle_background_compile_task")
}
}

View File

@@ -0,0 +1,62 @@
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
use super::puzzle_background_compile_task_procedure_result_type::PuzzleBackgroundCompileTaskProcedureResult;
use super::puzzle_background_compile_task_release_input_type::PuzzleBackgroundCompileTaskReleaseInput;
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
#[sats(crate = __lib)]
struct ReleasePuzzleBackgroundCompileTaskArgs {
pub input: PuzzleBackgroundCompileTaskReleaseInput,
}
impl __sdk::InModule for ReleasePuzzleBackgroundCompileTaskArgs {
type Module = super::RemoteModule;
}
#[allow(non_camel_case_types)]
/// Extension trait for access to the procedure `release_puzzle_background_compile_task`.
///
/// Implemented for [`super::RemoteProcedures`].
pub trait release_puzzle_background_compile_task {
fn release_puzzle_background_compile_task(
&self,
input: PuzzleBackgroundCompileTaskReleaseInput,
) {
self.release_puzzle_background_compile_task_then(input, |_, _| {});
}
fn release_puzzle_background_compile_task_then(
&self,
input: PuzzleBackgroundCompileTaskReleaseInput,
__callback: impl FnOnce(
&super::ProcedureEventContext,
Result<PuzzleBackgroundCompileTaskProcedureResult, __sdk::InternalError>,
) + Send
+ 'static,
);
}
impl release_puzzle_background_compile_task for super::RemoteProcedures {
fn release_puzzle_background_compile_task_then(
&self,
input: PuzzleBackgroundCompileTaskReleaseInput,
__callback: impl FnOnce(
&super::ProcedureEventContext,
Result<PuzzleBackgroundCompileTaskProcedureResult, __sdk::InternalError>,
) + Send
+ 'static,
) {
self.imp
.invoke_procedure_with_callback::<_, PuzzleBackgroundCompileTaskProcedureResult>(
"release_puzzle_background_compile_task",
ReleasePuzzleBackgroundCompileTaskArgs { input },
__callback,
);
}
}

View File

@@ -1,8 +1,10 @@
use super::*;
use crate::mapper::*;
use crate::module_bindings::claim_puzzle_background_compile_task_procedure::claim_puzzle_background_compile_task;
use crate::module_bindings::claim_puzzle_work_point_incentive_procedure::claim_puzzle_work_point_incentive;
use crate::module_bindings::delete_puzzle_work_procedure::delete_puzzle_work;
use crate::module_bindings::record_puzzle_work_like_procedure::record_puzzle_work_like;
use crate::module_bindings::release_puzzle_background_compile_task_procedure::release_puzzle_background_compile_task;
use crate::module_bindings::remix_puzzle_work_procedure::remix_puzzle_work;
use crate::module_bindings::save_puzzle_ui_background_procedure::save_puzzle_ui_background;
@@ -194,6 +196,67 @@ impl SpacetimeClient {
.await
}
pub async fn claim_puzzle_background_compile_task(
&self,
input: PuzzleBackgroundCompileTaskClaimRecordInput,
) -> Result<bool, SpacetimeClientError> {
let procedure_input = PuzzleBackgroundCompileTaskClaimInput {
task_id: input.task_id,
claim_id: input.claim_id,
session_id: input.session_id,
owner_user_id: input.owner_user_id,
claimed_at_micros: input.claimed_at_micros,
};
self.call_after_connect(
"claim_puzzle_background_compile_task",
move |connection, sender| {
connection
.procedures()
.claim_puzzle_background_compile_task_then(
procedure_input,
move |_, result| {
let mapped = result
.map_err(SpacetimeClientError::from_sdk_error)
.and_then(map_puzzle_background_compile_task_procedure_result);
send_once(&sender, mapped);
},
);
},
)
.await
}
pub async fn release_puzzle_background_compile_task(
&self,
input: PuzzleBackgroundCompileTaskReleaseRecordInput,
) -> Result<bool, SpacetimeClientError> {
let procedure_input = PuzzleBackgroundCompileTaskReleaseInput {
task_id: input.task_id,
claim_id: input.claim_id,
session_id: input.session_id,
owner_user_id: input.owner_user_id,
};
self.call_after_connect(
"release_puzzle_background_compile_task",
move |connection, sender| {
connection
.procedures()
.release_puzzle_background_compile_task_then(
procedure_input,
move |_, result| {
let mapped = result
.map_err(SpacetimeClientError::from_sdk_error)
.and_then(map_puzzle_background_compile_task_procedure_result);
send_once(&sender, mapped);
},
);
},
)
.await
}
pub async fn save_puzzle_generated_images(
&self,
input: PuzzleGeneratedImagesSaveRecordInput,