From f58aa848a556117866bb75cbf8c02872cc385608 Mon Sep 17 00:00:00 2001 From: miles Date: Thu, 7 Aug 2025 12:09:57 +0800 Subject: [PATCH 1/3] fix: example test --- examples/minimal/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/minimal/src/lib.rs b/examples/minimal/src/lib.rs index 76a0f41d..c614e48d 100644 --- a/examples/minimal/src/lib.rs +++ b/examples/minimal/src/lib.rs @@ -30,7 +30,7 @@ impl Minimal { impl GameHandler for Minimal { - fn init_state(init_account: InitAccount) -> Result { + fn init_state(_effect: &mut Effect, init_account: InitAccount) -> Result { let account_data: MinimalAccountData = init_account.data()?; Ok(Self { n: account_data.init_n, From d5ff303f45c2d7a90775b21bce09ad432168b3a8 Mon Sep 17 00:00:00 2001 From: miles Date: Thu, 7 Aug 2025 12:11:36 +0800 Subject: [PATCH 2/3] chore: change GameState.is_initialized to GameStatus, quit sub loop when game closed. --- core/src/error.rs | 3 +++ transactor/src/component/wrapped_transport.rs | 5 +++++ transport/src/solana.rs | 6 ++++++ transport/src/solana/types/state/game.rs | 10 +++++++++- 4 files changed, 23 insertions(+), 1 deletion(-) diff --git a/core/src/error.rs b/core/src/error.rs index 1d93d6f8..288331ba 100644 --- a/core/src/error.rs +++ b/core/src/error.rs @@ -286,6 +286,9 @@ pub enum Error { #[error("Game uninitialized")] GameUninitialized, + #[error("Game closed")] + GameClosed, + #[error("Invalid bridge event")] InvalidBridgeEvent, diff --git a/transactor/src/component/wrapped_transport.rs b/transactor/src/component/wrapped_transport.rs index cc2d7313..ef330ec5 100644 --- a/transactor/src/component/wrapped_transport.rs +++ b/transactor/src/component/wrapped_transport.rs @@ -97,6 +97,11 @@ impl TransportT for WrappedTransport { loop { let item = sub.next().await; match item { + Some(Ok(item)) if item.data_len == 1 => { + info!("Game closed, quit sub loop"); + yield Err(Error::GameClosed); + return; + } Some(Ok(item)) => { yield Ok(item); }, diff --git a/transport/src/solana.rs b/transport/src/solana.rs index 92008446..05656e17 100755 --- a/transport/src/solana.rs +++ b/transport/src/solana.rs @@ -1293,6 +1293,12 @@ impl TransportT for SolanaTransport { return; }; let state = match GameState::deserialize(&mut data.as_slice()) { + Ok(x) if x.game_status == GameStatus::Closed => { + info!("Game closed, quit sub loop"); + yield Err(Error::GameClosed); + unsub().await; + return; + }, Ok(x) => x, Err(e) => { error!("Game state deserialization error: {}", e.to_string()); diff --git a/transport/src/solana/types/state/game.rs b/transport/src/solana/types/state/game.rs index 010f8a59..0ebba73b 100644 --- a/transport/src/solana/types/state/game.rs +++ b/transport/src/solana/types/state/game.rs @@ -93,11 +93,19 @@ impl From for race_core::types::PlayerBalance { } } +#[derive(Default, BorshDeserialize, BorshSerialize, Debug, PartialEq, Eq, Clone)] +pub enum GameStatus { + #[default] + Initializing, + Initialized, + Closed, +} + // State of on-chain GameAccount #[cfg_attr(test, derive(PartialEq, Clone))] #[derive(Default, BorshDeserialize, BorshSerialize, Debug)] pub struct GameState { - pub is_initialized: bool, + pub game_status: GameStatus, // the contract version, used for upgrade pub version: String, // game name displayed on chain From 1c1bcebc5ab66551e601483872403910689318bd Mon Sep 17 00:00:00 2001 From: miles Date: Thu, 7 Aug 2025 14:52:25 +0800 Subject: [PATCH 3/3] fix: subscribe game account with retry --- transactor/src/component/wrapped_transport.rs | 23 ++++++------- transport/src/solana.rs | 33 ++++++++++++------- 2 files changed, 31 insertions(+), 25 deletions(-) diff --git a/transactor/src/component/wrapped_transport.rs b/transactor/src/component/wrapped_transport.rs index ef330ec5..77b7f27c 100644 --- a/transactor/src/component/wrapped_transport.rs +++ b/transactor/src/component/wrapped_transport.rs @@ -97,11 +97,6 @@ impl TransportT for WrappedTransport { loop { let item = sub.next().await; match item { - Some(Ok(item)) if item.data_len == 1 => { - info!("Game closed, quit sub loop"); - yield Err(Error::GameClosed); - return; - } Some(Ok(item)) => { yield Ok(item); }, @@ -111,14 +106,16 @@ impl TransportT for WrappedTransport { return; } None => { - info!("Restart subscription after {} seconds", interval); - tokio::time::sleep(std::time::Duration::from_secs(interval)).await; - let new_sub = self.inner.subscribe_game_account(addr).await; - sub = match new_sub { - Ok(new_sub) => new_sub, - Err(e) => { - yield Err(e); - return; + sub = loop { + info!("Restart subscription after {} seconds", interval); + tokio::time::sleep(std::time::Duration::from_secs(interval)).await; + let new_sub = self.inner.subscribe_game_account(addr).await; + match new_sub { + Ok(new_sub) => break new_sub, + Err(e) => { + error!("Subscribe game account err: {}", e.to_string()); + continue; + } } }; } diff --git a/transport/src/solana.rs b/transport/src/solana.rs index 05656e17..5b53c7c6 100755 --- a/transport/src/solana.rs +++ b/transport/src/solana.rs @@ -30,7 +30,7 @@ use race_core::{ use std::str::FromStr; use std::{path::PathBuf, pin::Pin}; -use solana_account_decoder::UiAccountEncoding; +use solana_account_decoder::{UiAccount, UiAccountEncoding}; use solana_pubsub_client::nonblocking::pubsub_client::PubsubClient; use solana_rpc_client::rpc_client::RpcClient; use solana_rpc_client_api::config::{RpcAccountInfoConfig, RpcSendTransactionConfig}; @@ -1278,27 +1278,28 @@ impl TransportT for SolanaTransport { .await { Ok((stream, unsub)) => (stream, unsub), - Err(e) => { + Err(_) => { error!("Failed on calling account_subscribe"); - yield Err(Error::TransportError(e.to_string())); return; } }; while let Some(rpc_response) = stream.next().await { - let ui_account = rpc_response.value; + let ui_account: UiAccount = rpc_response.value; + let Some(data) = ui_account.data.decode() else { error!("Found an empty account data"); unsub().await; return; }; + if data.as_slice().len() == 1 { + info!("Game closed, quit sub loop"); + unsub().await; + yield Err(Error::GameClosed); + return; + } + let state = match GameState::deserialize(&mut data.as_slice()) { - Ok(x) if x.game_status == GameStatus::Closed => { - info!("Game closed, quit sub loop"); - yield Err(Error::GameClosed); - unsub().await; - return; - }, Ok(x) => x, Err(e) => { error!("Game state deserialization error: {}", e.to_string()); @@ -1308,11 +1309,19 @@ impl TransportT for SolanaTransport { } }; - let players = self.internal_get_players_reg_state( + let players = match self.internal_get_players_reg_state( &state.players_reg_account, state.access_version, state.settle_version, - ).await?.players; + ).await { + Ok(players_reg) => players_reg.players, + Err(e) => { + error!("Get players reg error: {}", e.to_string()); + unsub().await; + yield Err(Error::TransportError(e.to_string())); + return; + } + }; let acc = match state.into_account(addr.clone(), players) { Ok(x) => x,