race_api/
event.rs

1use crate::{
2    error::HandleError,
3    types::{Ciphertext, DecisionId, PlayerJoin, RandomId, SecretDigest, SecretShare, ServerJoin},
4};
5use borsh::{BorshDeserialize, BorshSerialize};
6#[cfg(feature = "serde")]
7use serde::{Deserialize, Serialize};
8
9/// A message sent by player
10/// Used to express unimportant game events that
11/// can be sent at any time without the server checking
12/// their content.
13#[derive(Debug, Clone, BorshSerialize, BorshDeserialize, PartialEq, Eq)]
14#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
15#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
16pub struct Message {
17    pub sender: String,
18    pub content: String,
19}
20
21/// Game event structure
22#[derive(Debug, Clone, BorshDeserialize, BorshSerialize, PartialEq, Eq)]
23#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
24#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
25pub enum Event {
26    /// Sent by player clients.  Represent game specific events, the `raw`
27    /// parts is the serialized data from a custom game event which
28    /// satisfies [`CustomEvent`].
29    Custom {
30        sender: String,
31        raw: Vec<u8>,
32    },
33
34    /// A event sent by system, the first event sent by transactor
35    /// when game is loaded.
36    Ready,
37
38    /// Transactor shares its secert to specific player.
39    /// The `secret_data` is encrypted with the receiver's public key.
40    ShareSecrets {
41        sender: String,
42        shares: Vec<SecretShare>,
43    },
44
45    OperationTimeout {
46        addrs: Vec<String>,
47    },
48
49    /// Randomize items.
50    /// This event is sent by transactors.
51    Mask {
52        sender: String,
53        random_id: RandomId,
54        ciphertexts: Vec<Ciphertext>,
55    },
56
57    /// Lock items.
58    /// This event is sent by transactors.
59    Lock {
60        sender: String,
61        random_id: RandomId,
62        ciphertexts_and_digests: Vec<(Ciphertext, SecretDigest)>,
63    },
64
65    /// All randomness is prepared.
66    /// This event is sent by transactor.
67    RandomnessReady {
68        random_id: RandomId,
69    },
70
71    /// Sync with on-chain account.  New players/servers will be added frist to
72    /// game context and then to game handler (WASM).
73    /// This event is sent by transactor based on the diff of the account states.
74    Sync {
75        new_players: Vec<PlayerJoin>,
76        new_servers: Vec<ServerJoin>,
77        transactor_addr: String,
78        access_version: u64,
79    },
80
81    /// A server left the game.
82    /// `transactor_addr` is the new current transactor address.
83    ///
84    /// NOTE: This event must be handled idempotently.
85    ServerLeave {
86        server_addr: String,
87        transactor_addr: String,
88    },
89
90    /// Client left game
91    /// This event is sent by transactor based on client's connection status.
92    Leave {
93        player_addr: String,
94    },
95
96    /// Transactor uses this event as the start for each game.
97    /// The `access_version` can be used to filter out which players are included.
98    GameStart {
99        access_version: u64,
100    },
101
102    /// Timeout when waiting for start
103    WaitingTimeout,
104
105    /// Random drawer takes random items by indexes.
106    DrawRandomItems {
107        sender: String,
108        random_id: usize,
109        indexes: Vec<usize>,
110    },
111
112    /// Timeout for drawing random items
113    DrawTimeout,
114
115    /// Timeout when waiting for player's action
116    /// Sent by transactor.
117    ActionTimeout {
118        player_addr: String,
119    },
120
121    /// Answer the decision question with encrypted ciphertext
122    AnswerDecision {
123        sender: String,
124        decision_id: DecisionId,
125        ciphertext: Ciphertext,
126        digest: SecretDigest,
127    },
128
129    /// All required secrets are shared
130    SecretsReady {
131        random_ids: Vec<usize>,
132    },
133
134    /// Shutdown
135    Shutdown,
136}
137
138impl std::fmt::Display for Event {
139    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
140        match self {
141            Event::Custom { sender, raw } => write!(f, "Custom from {}, inner: {:?}", sender, raw),
142            Event::Ready => write!(f, "Ready"),
143            Event::ShareSecrets { sender, shares } => {
144                let repr = shares
145                    .iter()
146                    .map(|s| format!("{}", s))
147                    .collect::<Vec<String>>()
148                    .join("|");
149                write!(f, "ShareSecrets from {}, secrets: {}", sender, repr)
150            }
151            Event::Mask {
152                sender, random_id, ..
153            } => write!(f, "Mask from {} for random: {}", sender, random_id),
154            Event::Lock {
155                sender, random_id, ..
156            } => write!(f, "Lock from {} for random: {}", sender, random_id),
157            Event::RandomnessReady { random_id } => {
158                write!(f, "RandomnessReady, random_id: {}", random_id)
159            }
160            Event::Sync {
161                new_players,
162                new_servers,
163                access_version,
164                ..
165            } => {
166                let players = new_players
167                    .iter()
168                    .map(|p| p.addr.as_str())
169                    .collect::<Vec<&str>>()
170                    .join(",");
171                let servers = new_servers
172                    .iter()
173                    .map(|s| s.addr.as_str())
174                    .collect::<Vec<&str>>()
175                    .join(", ");
176
177                write!(
178                    f,
179                    "Sync, new_players: [{}], new_servers: [{}], access_version = {}",
180                    players, servers, access_version
181                )
182            }
183            Event::Leave { player_addr } => write!(f, "Leave from {}", player_addr),
184            Event::GameStart { access_version } => {
185                write!(f, "GameStart, access_version = {}", access_version)
186            }
187            Event::WaitingTimeout => write!(f, "WaitTimeout"),
188            Event::DrawRandomItems {
189                sender,
190                random_id,
191                indexes,
192            } => write!(
193                f,
194                "DrawRandomItems from {} for random {} with indexes {:?}",
195                sender, random_id, indexes
196            ),
197            Event::DrawTimeout => write!(f, "DrawTimeout"),
198            Event::ActionTimeout { player_addr } => write!(f, "ActionTimeout for {}", player_addr),
199            Event::SecretsReady { random_ids } => {
200                write!(f, "SecretsReady for {}", format!("{:?}", random_ids))
201            }
202            Event::ServerLeave {
203                server_addr,
204                transactor_addr,
205            } => write!(
206                f,
207                "ServerLeave {}, current transactor: {}",
208                server_addr, transactor_addr
209            ),
210            Event::AnswerDecision { decision_id, .. } => {
211                write!(f, "AnswerDecision for {}", decision_id)
212            }
213            Event::OperationTimeout { addrs } => {
214                write!(f, "OperationTimeout for {:?}", addrs)
215            }
216            Event::Shutdown => {
217                write!(f, "Shutdown")
218            }
219        }
220    }
221}
222
223impl Event {
224    pub fn custom<S: Into<String>, E: CustomEvent>(sender: S, e: &E) -> Self {
225        Self::Custom {
226            sender: sender.into(),
227            raw: e.try_to_vec().unwrap(),
228        }
229    }
230}
231
232pub trait CustomEvent: Sized + BorshSerialize + BorshDeserialize {
233    fn try_parse(slice: &[u8]) -> Result<Self, HandleError> {
234        Self::try_from_slice(slice).or(Err(HandleError::MalformedCustomEvent))
235    }
236}