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/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, diff --git a/transactor/src/component/wrapped_transport.rs b/transactor/src/component/wrapped_transport.rs index cc2d7313..77b7f27c 100644 --- a/transactor/src/component/wrapped_transport.rs +++ b/transactor/src/component/wrapped_transport.rs @@ -106,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 92008446..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,20 +1278,27 @@ 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) => x, Err(e) => { @@ -1302,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, 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