Skip to main content

ygopro_data/message/
server_to_client.rs

1//! Server-to-client messages (`stoc`).
2//!
3//! Messages sent from the server to the client.
4
5#![allow(non_camel_case_types)]
6#![allow(non_upper_case_globals)]
7
8use binrw::BinRead;
9use binrw::BinWrite;
10use num_enum::FromPrimitive;
11use num_enum::TryFromPrimitive;
12use ygopro_derive::Message;
13
14use crate::constants;
15use crate::constants::Color;
16use crate::constants::CorePlayer;
17use crate::constants::Netplayer;
18use crate::constants::PlayerChange;
19use crate::generate_enum;
20use crate::message::game_message;
21use crate::utils::string::FixedLengthString;
22use crate::utils::string::U16String;
23
24use super::HostInfo;
25
26include!(concat!(env!("OUT_DIR"), "/server_to_client.rs"));
27every_server_to_client_flat_message!(generate_enum);
28
29#[derive(BinRead, BinWrite, Debug, Clone, Message)]
30#[message(stoc, flag = 1)]
31pub struct GameMessage {
32    pub message: game_message::Message
33}
34
35#[derive(BinRead, BinWrite, Debug, Clone, Message)]
36#[message(stoc, flag = 2)]
37#[repr(C)]
38pub struct ErrorMessage {
39    pub err: crate::constants::ErrorMessage
40}
41
42impl From<crate::constants::ErrorMessage> for ErrorMessage {
43    fn from(value: crate::constants::ErrorMessage) -> Self {
44        Self { err: value }
45    }
46}
47
48#[derive(BinRead, BinWrite, Debug, Clone, Message)]
49#[message(stoc, flag = 3)]
50#[repr(C)]
51pub struct SelectHand;
52
53#[derive(BinRead, BinWrite, Debug, Clone, Message)]
54#[message(stoc, flag = 4)]
55#[repr(C)]
56pub struct SelectTp;
57
58#[derive(BinRead, BinWrite, Debug, Clone, Message)]
59#[message(stoc, flag = 5)]
60#[repr(C)]
61pub struct HandResult {
62    pub hand1: crate::constants::Hand,
63    pub hand2: crate::constants::Hand
64}
65
66impl HandResult {
67    pub fn swap(&mut self) {
68        let r = self.hand1;
69        self.hand1 = self.hand2;
70        self.hand2 = r;
71    }
72
73    pub fn swap_clone(&self) -> Self {
74        let mut cloned = self.clone();
75        cloned.swap();
76        cloned
77    } 
78
79    pub fn judge(&self) -> crate::constants::HandResult {
80        self.hand1.judge(&self.hand2)
81    }
82}
83
84/// reserved, never sent by server
85#[derive(BinRead, BinWrite, Debug, Clone, Message)]
86#[message(stoc, flag = 6)]
87#[repr(C)]
88pub struct TpResult {
89    pub result: u8
90}
91
92#[derive(BinRead, BinWrite, Debug, Clone, Message)]
93#[message(stoc, flag = 7)]
94#[repr(C)]
95pub struct ChangeSide;
96
97#[derive(BinRead, BinWrite, Debug, Clone, Message)]
98#[message(stoc, flag = 8)]
99#[repr(C)]
100pub struct WaitingSide;
101
102#[derive(BinRead, BinWrite, Debug, Clone, Message)]
103#[message(stoc, flag = 9)]
104#[repr(C)]
105pub struct DeckCount {
106    pub mainc_s: u16,
107    pub extrac_s: u16,
108    pub sidec_s: u16,
109    pub mainc_o: u16,
110    pub extrac_o: u16,
111    pub sidec_o: u16
112}
113
114#[derive(BinRead, BinWrite, Debug, Clone, Message)]
115#[message(stoc, flag = 17)]
116#[repr(C)]
117pub struct CreateGame {
118    pub gameid: u32
119}
120
121#[derive(BinRead, BinWrite, Debug, Clone, Message)]
122#[message(stoc, flag = 18)]
123#[repr(C)]
124pub struct JoinGame {
125    pub info: HostInfo
126}
127
128// In rust ygopro, due to actor model, we must rememeber the observer index
129// which dont exist in C++ version. Netplayer is a enum which contains necessary
130// message, but will lost observer index when into bytes.
131// So we set it different with other here. stoc::TypeChange keep all message.
132// but constants::TypeChange will discard the observer index.
133#[derive(BinRead, BinWrite, Debug, Clone, Message)]
134#[br(map = |v: constants::TypeChange| v.into() )]
135#[bw(map = |v| constants::TypeChange::from(v))]
136#[message(stoc, flag = 19)]
137#[repr(C)]
138pub struct TypeChange {
139    pub player: Netplayer,
140    pub host: bool,
141}
142
143impl From<&TypeChange> for constants::TypeChange {
144    fn from(value: &TypeChange) -> Self {
145        Self::new().with_host(value.host).with_player(value.player)
146    }
147}
148
149impl From<constants::TypeChange> for TypeChange {
150    fn from(value: constants::TypeChange) -> Self {
151        Self {
152            player: value.player(),
153            host: value.host()
154        }
155    }
156}
157 
158#[derive(BinRead, BinWrite, Debug, Clone, Message)]
159#[message(stoc, flag = 20)]
160#[repr(C)]
161pub struct LeaveGame {
162    pub pos: Netplayer
163}
164
165#[derive(BinRead, BinWrite, Debug, Clone, Message)]
166#[message(stoc, flag = 21)]
167#[repr(C)]
168pub struct DuelStart;
169
170#[derive(BinRead, BinWrite, Debug, Clone, Message)]
171#[message(stoc, flag = 22)]
172#[repr(C)]
173pub struct DuelEnd;
174
175// Rust enum use its variant's max size as its size.
176// As we decide to use clone for message dispatching,
177// The replay size is too large to be put in the enum, so we box it.
178#[derive(BinRead, BinWrite, Debug, Clone, Message)]
179#[message(stoc, flag = 23)]
180pub struct Replay {
181    pub replay: Box<crate::data::Replay>
182}
183
184#[derive(BinRead, BinWrite, Debug, Clone, Message)]
185#[message(stoc, flag = 24)]
186#[repr(C)]
187pub struct TimeLimit {
188    #[brw(pad_after = 1)]
189    pub player: CorePlayer,
190    pub left_time: u16
191}
192
193#[derive(BinRead, BinWrite, Debug, Clone, Message)]
194#[message(stoc, flag = 25)]
195#[repr(C)]
196pub struct Chat {
197    #[brw(pad_after = 1)]
198    pub player: ChatSource,
199    pub msg: U16String
200}
201
202#[derive(BinRead, BinWrite, Copy, Clone, Eq, PartialEq, Debug)]
203#[br(try_map = |raw: u8| ChatSource::try_from(raw))]
204#[bw(map = |value: &ChatSource| u8::from(*value))]
205pub enum ChatSource {
206    Player(Netplayer),
207    System(Color)
208}
209
210impl From<Color> for ChatSource {
211    fn from(color: Color) -> Self {
212        ChatSource::System(color)
213    }
214}
215
216impl From<Netplayer> for ChatSource {
217    fn from(player: Netplayer) -> Self {
218        ChatSource::Player(player)
219    }
220}
221
222impl TryFrom<ChatSource> for Netplayer {
223    type Error = ();
224
225    fn try_from(value: ChatSource) -> Result<Self, Self::Error> {
226        match value {
227            ChatSource::Player(player) => Ok(player),
228            _ => Err(())
229        }
230    }
231}
232
233impl TryFrom<u8> for ChatSource {
234    type Error = binrw::Error;
235    fn try_from(value: u8) -> Result<Self, Self::Error> {
236        if value <= 7 {
237            Ok(ChatSource::Player(Netplayer::from_primitive(value)))
238        } else {
239            Color::try_from_primitive(value)
240                .map(ChatSource::System)
241                .map_err(|_| binrw::Error::NoVariantMatch { pos: 0 })
242        }
243    }
244}
245
246impl From<ChatSource> for u8 {
247    fn from(value: ChatSource) -> u8 {
248        match value {
249            ChatSource::Player(player) => player.into(),
250            ChatSource::System(color) => color.into(),
251        }
252    }
253}
254
255#[derive(BinRead, BinWrite, Debug, Clone, Message)]
256#[message(stoc, flag = 32)]
257#[repr(C)]
258pub struct HsPlayerEnter {
259    pub name: FixedLengthString<20>,
260    pub pos: Netplayer
261}
262
263#[derive(BinRead, BinWrite, Debug, Clone, Message)]
264#[message(stoc, flag = 33)]
265#[repr(C)]
266pub struct HsPlayerChange {
267    pub status: PlayerChange
268}
269
270impl From<PlayerChange> for HsPlayerChange {
271    fn from(value: PlayerChange) -> Self {
272        HsPlayerChange { status: value }
273    }
274}
275
276#[derive(BinRead, BinWrite, Debug, Clone, Message)]
277#[message(stoc, flag = 34)]
278#[repr(C)]
279pub struct HsWatchChange {
280    pub watch_count: u16
281}
282
283#[derive(BinRead, BinWrite, Debug, Clone, Message)]
284#[message(stoc, flag = 35)]
285#[repr(C)]
286pub struct TeammateSurrender;
287
288#[derive(BinRead, BinWrite, Debug, Clone, Message)]
289#[message(stoc, flag = 48)]
290#[repr(C)]
291pub struct FieldFinish;
292
293#[cfg(test)]
294mod test {
295    #[test]
296    fn print_sizes() {
297        macro_rules! print_size {
298            ($($msg:ident = $flag:literal),* $(,)?) => {
299                println!("=== STOC ===");
300                $(
301                    println!("  {:30}: {:>4} bytes", stringify!($msg), std::mem::size_of::<super::$msg>());
302                )*
303                println!("  {:30}: {:>4} bytes", "MessageType", std::mem::size_of::<super::MessageType>());
304                println!("  {:30}: {:>4} bytes", "Message", std::mem::size_of::<super::Message>());
305            };
306        }
307        every_server_to_client_flat_message!(print_size);
308    }
309}