1#![allow(non_upper_case_globals)]
6#![allow(non_camel_case_types)]
7
8use binrw::BinRead;
9use binrw::BinWrite;
10use binrw::binrw;
11use binrw::helpers::until_eof;
12use modular_bitfield::bitfield;
13use modular_bitfield::specifiers::B3;
14use modular_bitfield::specifiers::B28;
15use num_enum::TryFromPrimitive;
16use ygopro_derive::GameMessage;
17use ygopro_derive::Message;
18
19use crate::constants::*;
20use crate::data::CardPosition;
21use crate::data::InfoLocation;
22use crate::data::UpdateCardInfo;
23
24include!(concat!(env!("OUT_DIR"), "/game_message.rs"));
25every_game_message_flat_message!(crate::generate_enum);
26
27macro_rules! impl_into_stoc_message {
28 ($($message_name:ident=$message_flag:literal),*) => {
29 $(
30 impl From<$message_name> for crate::message::server_to_client::Message {
31 fn from(value: $message_name) -> Self {
32 crate::message::server_to_client::Message::GameMessage(
33 crate::message::server_to_client::GameMessage { message: value.into() }
34 )
35 }
36 }
37 )*
38 };
39}
40every_game_message_flat_message!(impl_into_stoc_message);
41
42impl From<crate::message::game_message::Message> for crate::message::server_to_client::Message {
43 fn from(value: crate::message::game_message::Message) -> Self {
44 crate::message::server_to_client::Message::GameMessage(
45 crate::message::server_to_client::GameMessage { message: value }
46 )
47 }
48}
49
50
51pub trait GameMessage {
52 fn mask(&mut self);
53 fn should_mask(&self, _player: CorePlayer) -> bool {
54 true
55 }
56 fn waiting_for(&self) -> Option<CorePlayer> {
57 None
58 }
59}
60
61pub trait MaskedClone: GameMessage + Clone {
62 fn clone_masked(&self) -> Self {
63 let mut mirror = self.clone();
64 mirror.mask();
65 mirror
66 }
67}
68
69impl<T: GameMessage> GameMessage for Vec<T> {
70 fn mask(&mut self) {
71 for item in self { item.mask(); }
72 }
73 fn should_mask(&self, player: CorePlayer) -> bool {
74 self.iter().any(|item| item.should_mask(player))
75 }
76}
77
78impl GameMessage for Vec<u32> {
79 fn mask(&mut self) {
80 self.fill(0);
81 }
82}
83
84impl<T> MaskedClone for T where T: GameMessage + Clone {}
85
86macro_rules! impl_mask_for_message {
87 ($($message_name:ident=$message_flag:literal),*) => {
88 impl GameMessage for Message {
89 fn mask(&mut self) {
90 match self {
91 $(Message::$message_name(inner) => inner.mask()),*
92 }
93 }
94 fn should_mask(&self, player: CorePlayer) -> bool {
95 match self {
96 $(Message::$message_name(inner) => inner.should_mask(player)),*
97 }
98 }
99 fn waiting_for(&self) -> Option<CorePlayer> {
100 match self {
101 $(Message::$message_name(inner) => inner.waiting_for()),*
102 }
103 }
104 }
105 };
106}
107every_game_message_flat_message!(impl_mask_for_message);
108
109#[binrw]
110#[derive(Debug, Clone, Message, GameMessage)]
111#[message(gm, flag = 1)]
112#[repr(C)]
113pub struct Retry;
114
115#[derive(Debug, Message, Clone, GameMessage)]
116#[message(gm, flag = 2)]
117#[binrw]
118#[repr(C)]
119pub struct Hint {
120 pub _type: crate::constants::Hint,
121 pub player: CorePlayer,
122 pub data: i32
123}
124
125#[binrw]
126#[derive(Debug, Clone, Message, GameMessage)]
127#[message(gm, flag = 3)]
128#[repr(C)]
129pub struct Waiting;
130
131#[binrw]
132#[derive(Debug, Clone, Message, GameMessage)]
133#[message(gm, flag = 4)]
134#[repr(C)]
135pub struct Start {
136 pub player_type: u8,
137 pub rule: MasterRule,
138 pub player1_lp: i32,
139 pub player2_lp: i32,
140 pub player1_deck_count: u16,
141 pub player1_extra_count: u16,
142 pub player2_deck_count: u16,
143 pub player2_extra_count: u16
144}
145
146#[binrw]
147#[derive(Debug, Clone, Message, GameMessage)]
148#[message(gm, flag = 5)]
149#[repr(C)]
150pub struct Win {
151 pub winner: CorePlayer,
152 pub reason: WinReason
153}
154
155#[binrw]
156#[derive(Debug, Clone, Message, GameMessage)]
157#[message(gm, flag = 6)]
158#[repr(C)]
159pub struct UpdateData {
160 pub player: CorePlayer,
161 pub location: Location,
162 #[br(parse_with=until_eof)]
163 #[mask]
164 #[mask_if(self.player != player)]
165 pub data: Vec<UpdateCardInfo>
166}
167
168#[binrw]
169#[derive(Debug, Clone, Message, GameMessage)]
170#[message(gm, flag = 7)]
171#[repr(C)]
172pub struct UpdateCard {
173 pub position: CardPosition<false, false, false>,
174 #[mask]
175 #[mask_if(self.position.controller != player && self.data.should_mask(player))]
176 pub data: UpdateCardInfo
177}
178
179#[binrw]
180#[derive(Debug, Clone, Message, GameMessage)]
181#[message(gm, flag = 8)]
182#[repr(C)]
183pub struct RequestDeck;
184
185#[binrw]
186#[derive(Debug, Clone, Message, GameMessage)]
187#[message(gm, flag = 10)]
188#[repr(C)]
189pub struct SelectBattleCommand {
190 #[wait_for]
191 pub selecting_player: CorePlayer,
192 #[bw(calc(activatable_cards.len() as u8))]
193 activatable_cards_size: u8,
194 #[br(count = activatable_cards_size)]
195 pub activatable_cards: Vec<CardPosition<true, false, true>>,
196 #[bw(calc(attackable_cards.len() as u8))]
197 attackable_cards_size: u8,
198 #[br(count = attackable_cards_size)]
199 pub attackable_cards: Vec<(CardPosition<true, false, false>, i8)>, #[br(map=|v:u8| v>0)]
201 #[bw(map=|v| if *v {1u8} else {0u8})]
202 pub can_enter_m2: bool, #[br(map=|v:u8| v>0)]
204 #[bw(map=|v| if *v {1u8} else {0u8})]
205 pub can_enter_ep: bool }
207
208#[binrw]
209#[derive(Debug, Clone, Message, GameMessage)]
210#[message(gm, flag = 11)]
211#[repr(C)]
212pub struct SelectIdleCommand {
213 #[wait_for]
214 pub selecting_player: CorePlayer,
215 #[bw(calc(summonable_cards.len() as u8))]
216 summonable_cards_size: u8,
217 #[br(count = summonable_cards_size)]
218 pub summonable_cards: Vec<CardPosition<true, false, false>>,
219 #[bw(calc(special_summonable_cards.len() as u8))]
220 special_summonable_cards_size: u8,
221 #[br(count = special_summonable_cards_size)]
222 pub special_summonable_cards: Vec<CardPosition<true, false, false>>,
223 #[bw(calc(repositionable_cards.len() as u8))]
224 repositionable_cards_size: u8,
225 #[br(count = repositionable_cards_size)]
226 pub repositionable_cards: Vec<CardPosition<true, false, false>>,
227 #[bw(calc(m_setable_cards.len() as u8))]
228 m_setable_cards_size: u8,
229 #[br(count = m_setable_cards_size)]
230 pub m_setable_cards: Vec<CardPosition<true, false, false>>,
231 #[bw(calc(s_setable_cards.len() as u8))]
232 s_setable_cards_size: u8,
233 #[br(count = s_setable_cards_size)]
234 pub s_setable_cards: Vec<CardPosition<true, false, false>>,
235 #[bw(calc(activatable_cards.len() as u8))]
236 activatable_cards_size: u8,
237 #[br(count = activatable_cards_size)]
238 pub activatable_cards: Vec<CardPosition<true, false, true>>,
239 #[br(map=|v:u8| v>0)]
240 #[bw(map=|v| if *v {1u8} else {0u8})]
241 pub can_enter_bp: bool, #[br(map=|v:u8| v>0)]
243 #[bw(map=|v| if *v {1u8} else {0u8})]
244 pub can_enter_ep: bool, #[br(map=|v:u8| v>0)]
246 #[bw(map=|v| if *v {1u8} else {0u8})]
247 pub can_shuffle_hand: bool }
249
250#[binrw]
251#[derive(Debug, Clone, Message, GameMessage)]
252#[message(gm, flag = 12)]
253#[repr(C)]
254pub struct SelectEffectYesNo {
255 #[wait_for]
256 pub selecting_player: CorePlayer,
257 pub code: CardCode,
258 pub card_position: InfoLocation,
259 pub description: i32,
260}
261
262#[binrw]
263#[derive(Debug, Clone, Message, GameMessage)]
264#[message(gm, flag = 13)]
265#[repr(C)]
266pub struct SelectYesNo {
267 #[wait_for]
268 pub selecting_player: CorePlayer,
269 pub description: i32
270}
271
272#[binrw]
273#[derive(Debug, Clone, Message, GameMessage)]
274#[message(gm, flag = 14)]
275#[repr(C)]
276pub struct SelectOption {
277 #[wait_for]
278 pub selecting_player: CorePlayer,
279 #[bw(calc(options.len() as u8))]
280 options_size: u8,
281 #[br(count = options_size)]
282 pub options: Vec<i32>
283}
284
285#[binrw]
286#[derive(Debug, Clone, Message)]
287#[message(gm, flag = 15)]
288#[repr(C)]
289pub struct SelectCard {
290 pub selecting_player: CorePlayer,
291 #[br(map=|v:u8| v>0)]
292 #[bw(map=|v| if *v {1u8} else {0u8})]
293 pub select_cancelable: bool,
294 pub select_min: u8,
295 pub select_max: u8,
296 #[bw(calc(positions.len() as u8))]
297 positions_size: u8,
298 #[br(count = positions_size)]
299 pub positions: Vec<(CardCode, InfoLocation)>
300}
301
302impl GameMessage for SelectCard {
303 fn mask(&mut self) {
304 for (code, position) in &mut self.positions {
305 if position.controller != self.selecting_player {
306 code.mask();
307 }
308 }
309 }
310 fn should_mask(&self, _player: CorePlayer) -> bool {
311 true
312 }
313 fn waiting_for(&self) -> Option<CorePlayer> {
314 Some(self.selecting_player)
315 }
316}
317
318#[binrw]
319#[derive(Debug, Clone)]
320pub struct Chain {
321 pub flag: u8,
322 #[br(map = |v: u8| v != 0)]
323 #[bw(map = |v: &bool| *v as u8)]
324 pub forced: bool,
325 pub code: CardCode,
326 pub location: InfoLocation,
327 pub description: i32,
328}
329
330#[binrw]
331#[derive(Debug, Clone, Message, GameMessage)]
332#[message(gm, flag = 16)]
333#[repr(C)]
334pub struct SelectChain {
335 #[wait_for]
336 pub selecting_player: CorePlayer,
337 #[bw(calc(activatable_cards.len() as u8))]
338 pub activatable_cards_count: u8,
339 pub special_count: u8,
340 pub hint0: i32,
341 pub hint1: i32,
342 #[br(count = activatable_cards_count)]
343 pub activatable_cards: Vec<Chain>,
344}
345
346#[binrw]
347#[derive(Debug, Clone, Message, GameMessage)]
348#[message(gm, flag = 18)]
349#[repr(C)]
350pub struct SelectPlace {
351 #[wait_for]
352 pub selecting_player: CorePlayer,
353 pub count: u8,
354 pub unselectable_field: i32,
355}
356
357#[binrw]
358#[derive(Debug, Clone, Message, GameMessage)]
359#[message(gm, flag = 19)]
360#[repr(C)]
361pub struct SelectPosition {
362 #[wait_for]
363 pub selecting_player: CorePlayer,
364 pub code: u32,
365 pub positions: Position
366}
367
368#[binrw]
369#[derive(Debug, Clone, Message)]
370#[message(gm, flag = 20)]
371#[repr(C)]
372pub struct SelectTribute {
373 pub selecting_player: CorePlayer,
374 #[br(map=|v:u8| v>0)]
375 #[bw(map=|v| if *v {1u8} else {0u8})]
376 pub cancelable: bool,
377 pub select_min: u8,
378 pub select_max: u8,
379 #[bw(calc(tributes.len() as u8))]
380 tributes_size: u8,
381 #[br(count = tributes_size)]
382 pub tributes: Vec<(CardPosition<true, false, false>, i8)>
383}
384
385impl GameMessage for SelectTribute {
386 fn mask(&mut self) {
387 for (card_position, _) in &mut self.tributes {
388 if card_position.controller != self.selecting_player {
389 card_position.code.mask();
390 }
391 }
392 }
393 fn should_mask(&self, _player: CorePlayer) -> bool {
394 true
395 }
396 fn waiting_for(&self) -> Option<CorePlayer> {
397 Some(self.selecting_player)
398 }
399}
400
401#[binrw]
402#[derive(Debug, Clone, Message, GameMessage)]
403#[message(gm, flag = 21)]
404#[repr(C)]
405pub struct SortChain;
406
407#[binrw]
408#[derive(Debug, Clone, Message, GameMessage)]
409#[message(gm, flag = 22)]
410#[repr(C)]
411pub struct SelectCounter {
412 #[wait_for]
413 pub selecting_player: CorePlayer,
414 pub select_counter_type: u16,
415 pub select_counter_count: u16,
416 #[bw(calc(selectable_cards.len() as u8))]
417 selectable_cards_size: u8,
418 #[br(count = selectable_cards_size)]
419 pub selectable_cards: Vec<(CardPosition<true, false, false>, i16)>
420}
421
422#[binrw]
423#[derive(Debug, Clone, Message, GameMessage)]
424#[message(gm, flag = 23)]
425#[repr(C)]
426pub struct SelectSum {
427 pub select_mode: SelectSumMode,
428 #[wait_for]
429 pub selecting_player: CorePlayer,
430 pub select_sum_value: i32,
431 pub select_min: u8,
432 pub select_max: u8,
433 #[bw(calc(must_select_cards.len() as u8))]
434 must_select_cards_size: u8,
435 #[br(count = must_select_cards_size)]
436 pub must_select_cards: Vec<(CardPosition<true, false, false>, i32)>, #[bw(calc(select_cards.len() as u8))]
438 select_cards_size: u8,
439 #[br(count = select_cards_size)]
440 pub select_cards: Vec<(CardPosition<true, false, false>, i32)> }
442
443#[binrw]
444#[derive(Debug, Clone, Message, GameMessage)]
445#[message(gm, flag = 24)]
446#[repr(C)]
447pub struct SelectDisableField {
448 #[wait_for]
449 pub selecting_player: CorePlayer,
450 pub count: u8,
451 pub unselectable_field: i32,
452}
453
454#[binrw]
455#[derive(Debug, Clone, Message, GameMessage)]
456#[message(gm, flag = 25)]
457#[repr(C)]
458pub struct SortCard {
459 #[wait_for]
460 pub player: CorePlayer,
461 #[bw(calc(cards.len() as u8))]
462 cards_size: u8,
463 #[br(count = cards_size)]
464 pub cards: Vec<CardPosition<true, false, false>>
465}
466
467#[binrw]
468#[derive(Debug, Clone, Message)]
469#[message(gm, flag = 26)]
470#[repr(C)]
471pub struct SelectUnselectCard {
472 pub selecting_player: CorePlayer,
473 #[br(map=|v:u8| v>0)]
474 #[bw(map=|v| if *v {1u8} else {0u8})]
475 pub able: bool,
476 #[br(map=|v:u8| v>0)]
477 #[bw(map=|v| if *v {1u8} else {0u8})]
478 pub cancelable: bool,
479 pub select_min: u8,
480 pub select_max: u8,
481 #[bw(calc(positions1.len() as u8))]
482 positions1_size: u8,
483 #[br(count = positions1_size)]
484 pub positions1: Vec<(CardCode, InfoLocation)>,
485 #[bw(calc(positions2.len() as u8))]
486 positions2_size: u8,
487 #[br(count = positions2_size)]
488 pub positions2: Vec<(CardCode, InfoLocation)>
489}
490
491impl GameMessage for SelectUnselectCard {
492 fn mask(&mut self) {
493 for (code, position) in &mut self.positions1 {
494 if position.controller != self.selecting_player {
495 code.mask();
496 }
497 }
498 for (code, position) in &mut self.positions2 {
499 if position.controller != self.selecting_player {
500 code.mask();
501 }
502 }
503 }
504 fn should_mask(&self, _player: CorePlayer) -> bool {
505 true
506 }
507 fn waiting_for(&self) -> Option<CorePlayer> {
508 Some(self.selecting_player)
509 }
510}
511
512#[binrw]
513#[derive(Clone, Debug, Message, GameMessage)]
514#[message(gm, flag = 30)]
515pub struct ConfirmDecktop {
516 pub controller: CorePlayer,
517 #[bw(calc(codes.len() as u8))]
518 codes_size: u8,
519 #[br(count = codes_size)]
520 pub codes: Vec<CardPosition<true, false, false>>
521}
522
523#[binrw]
524#[derive(Debug, Clone, Message, GameMessage)]
525#[message(gm, flag = 31)]
526#[repr(C)]
527pub struct ConfirmCards {
528 pub player: CorePlayer,
529 #[br(map=|v:u8| v>0)]
530 #[bw(map=|v| if *v {1u8} else {0u8})]
531 pub skip_panel: bool, #[bw(calc(cards.len() as u8))]
533 cards_size: u8,
534 #[br(count = cards_size)]
535 pub cards: Vec<CardPosition<true, false, false>>
536}
537
538#[binrw]
539#[derive(Debug, Clone, Message, GameMessage)]
540#[message(gm, flag = 32)]
541#[repr(C)]
542pub struct ShuffleDeck {
543 pub player: CorePlayer
544}
545
546#[binrw]
547#[derive(Debug, Clone, Message, GameMessage)]
548#[message(gm, flag = 33)]
549#[repr(C)]
550pub struct ShuffleHand {
551 pub player: CorePlayer,
552 #[bw(calc(codes.len() as u8))]
553 pub count: u8,
554 #[br(count = count)]
555 #[mask]
556 #[mask_if(self.player != player)]
557 pub codes: Vec<u32>
558}
559
560#[binrw]
561#[derive(Debug, Clone, Message, GameMessage)]
562#[message(gm, flag = 34)]
563#[repr(C)]
564pub struct RefreshDeck {
565 pub player: CorePlayer
566}
567
568#[binrw]
569#[derive(Debug, Clone, Message, GameMessage)]
570#[message(gm, flag = 35)]
571#[repr(C)]
572pub struct SwapGraveDeck {
573 pub player: CorePlayer
574}
575
576#[binrw]
577#[derive(Debug, Clone, Message, GameMessage)]
578#[message(gm, flag = 36)]
579pub struct ShuffleSetCard {
580 pub location: Location,
581 #[bw(calc(old_locations.len() as u8), assert(old_locations.len() == new_locations.len(), "ShuffleSetCard: old/new location count mismatch"))]
582 count: u8,
583 #[br(count = count)]
584 pub old_locations: Vec<InfoLocation>,
585 #[br(count = count)]
586 pub new_locations: Vec<InfoLocation>,
587}
588
589#[binrw]
590#[derive(Debug, Clone, Message, GameMessage)]
591#[message(gm, flag = 37)]
592#[repr(C)]
593pub struct ReverseDeck;
594
595#[binrw]
596#[derive(Debug, Clone, Message, GameMessage)]
597#[message(gm, flag = 38)]
598#[repr(C)]
599pub struct DeckTop {
600 pub player: CorePlayer,
601 pub sequence: u8,
602 pub code: CardCode
603}
604
605#[binrw]
606#[derive(Debug, Clone, Message, GameMessage)]
607#[message(gm, flag = 39)]
608#[repr(C)]
609pub struct ShuffleExtra {
610 pub player: CorePlayer,
611 #[bw(calc(codes.len() as u8))]
612 pub count: u8,
613 #[br(count = count)]
614 #[mask]
615 #[mask_if(self.player != player)]
616 pub codes: Vec<u32>
617}
618
619#[binrw]
620#[derive(Debug, Clone, Message, GameMessage)]
621#[message(gm, flag = 40)]
622#[repr(C)]
623pub struct NewTurn {
624 pub player: CorePlayer
625}
626
627#[binrw]
628#[derive(Debug, Clone, Message, GameMessage)]
629#[message(gm, flag = 41)]
630#[repr(C)]
631pub struct NewPhase {
632 pub phase: crate::constants::Phase,
633}
634
635#[binrw]
636#[derive(Debug, Clone, Message, GameMessage)]
637#[message(gm, flag = 42)]
638#[repr(C)]
639pub struct ConfirmExtraTop {
640 pub player: CorePlayer,
641 #[bw(calc(selectable_cards.len() as u8))]
642 selectable_cards_size: u8,
643 #[br(count = selectable_cards_size)]
644 pub selectable_cards: Vec<CardPosition<true,false,false>>
645}
646
647#[binrw]
648#[derive(Debug, Clone, Message, GameMessage)]
649#[message(gm, flag = 50)]
650#[repr(C)]
651pub struct Move {
652 #[mask(if self.should_mask(CorePlayer::None) { 0 } else { self.code })]
653 #[mask_if(self.current.controller != player && !self.current.location.intersects(Location::Grave | Location::Overlay) && (self.current.location.intersects(Location::Deck | Location::Hand) || self.current.position.is_face_down()))]
654 pub code: i32,
655 pub previous: InfoLocation,
656 pub current: InfoLocation,
657 pub reason: crate::constants::Reason
658}
659
660#[binrw]
661#[derive(Debug, Clone, Message, GameMessage)]
662#[message(gm, flag = 53)]
663#[repr(C)]
664pub struct PositionChange {
665 pub card: u32,
666 pub controller: CorePlayer,
667 pub location: Location,
668 pub sequence: u8,
669 pub previous_position: Position,
670 pub current_position: Position
671}
672
673#[binrw]
674#[derive(Debug, Clone, Message, GameMessage)]
675#[message(gm, flag = 54)]
676#[repr(C)]
677pub struct Set {
678 #[mask]
679 pub code: i32,
680 pub position: InfoLocation
681}
682
683#[binrw]
684#[derive(Debug, Clone, Message, GameMessage)]
685#[message(gm, flag = 55)]
686#[repr(C)]
687pub struct Swap {
688 pub code1: CardCode,
689 pub position1: InfoLocation,
690 pub code2: CardCode,
691 pub position2: InfoLocation,
692}
693
694#[binrw]
695#[derive(Debug, Clone, Message, GameMessage)]
696#[message(gm, flag = 56)]
697#[repr(C)]
698pub struct FieldDisabled {
699 pub disabled: i32
700}
701
702#[binrw]
703#[derive(Debug, Clone, Message, GameMessage)]
704#[message(gm, flag = 60)]
705#[repr(C)]
706pub struct Summoning {
707 pub code: u32,
708 pub position: InfoLocation,
709}
710
711#[binrw]
712#[derive(Debug, Clone, Message, GameMessage)]
713#[message(gm, flag = 61)]
714#[repr(C)]
715pub struct Summoned;
716
717#[binrw]
718#[derive(Debug, Clone, Message, GameMessage)]
719#[message(gm, flag = 62)]
720#[repr(C)]
721pub struct SpecialSummoning {
722 #[mask]
723 #[mask_if(self.position.position.is_face_down() && self.position.controller != player)]
724 pub code: u32,
725 pub position: InfoLocation,
726}
727
728#[binrw]
729#[derive(Debug, Clone, Message, GameMessage)]
730#[message(gm, flag = 63)]
731#[repr(C)]
732pub struct SpecialSummoned;
733
734#[binrw]
735#[derive(Debug, Clone, Message, GameMessage)]
736#[message(gm, flag = 64)]
737#[repr(C)]
738pub struct FlipSummoning {
739 pub code: u32,
740 pub position: InfoLocation,
741}
742
743#[binrw]
744#[derive(Debug, Clone, Message, GameMessage)]
745#[message(gm, flag = 65)]
746#[repr(C)]
747pub struct FlipSummoned;
748
749#[binrw]
750#[derive(Debug, Clone, Message, GameMessage)]
751#[message(gm, flag = 70)]
752#[repr(C)]
753pub struct Chaining {
754 pub card: u32,
755 pub previous: CardPosition<false, true, false>,
756 pub current: CardPosition<false, false, true>,
757 pub chain_count: u8
758}
759
760#[binrw]
761#[derive(Debug, Clone, Message, GameMessage)]
762#[message(gm, flag = 71)]
763#[repr(C)]
764pub struct Chained {
765 pub chain_index: u8
766}
767
768#[binrw]
769#[derive(Debug, Clone, Message, GameMessage)]
770#[message(gm, flag = 72)]
771#[repr(C)]
772pub struct ChainSolving {
773 pub chain_index: u8
774}
775
776#[binrw]
777#[derive(Debug, Clone, Message, GameMessage)]
778#[message(gm, flag = 73)]
779#[repr(C)]
780pub struct ChainSolved {
781 pub chain_index: u8
782}
783
784#[binrw]
785#[derive(Debug, Clone, Message, GameMessage)]
786#[message(gm, flag = 74)]
787#[repr(C)]
788pub struct ChainEnd;
789
790#[binrw]
791#[derive(Debug, Clone, Message, GameMessage)]
792#[message(gm, flag = 75)]
793#[repr(C)]
794pub struct ChainNegated {
795 pub chain_index: u8
796}
797
798#[binrw]
799#[derive(Debug, Clone, Message, GameMessage)]
800#[message(gm, flag = 76)]
801#[repr(C)]
802pub struct ChainDisabled {
803 pub chain_index: u8
804}
805
806#[binrw]
807#[derive(Debug, Clone, Message, GameMessage)]
808#[message(gm, flag = 80)]
809#[repr(C)]
810pub struct CardSelected {
811 pub player: CorePlayer,
812 #[bw(calc(pcards.len() as u8))]
813 pcards_size: u8,
814 #[br(count = pcards_size)]
815 pub pcards: Vec<InfoLocation>
816}
817
818#[binrw]
819#[derive(Debug, Clone, Message, GameMessage)]
820#[message(gm, flag = 81)]
821#[repr(C)]
822pub struct RandomSelected {
823 pub player: CorePlayer,
824 #[bw(calc(pcards.len() as u8))]
825 pcards_size: u8,
826 #[br(count = pcards_size)]
827 pub pcards: Vec<InfoLocation>
828}
829
830#[binrw]
831#[derive(Debug, Clone, Message, GameMessage)]
832#[message(gm, flag = 83)]
833#[repr(C)]
834pub struct BecomeTarget {
835 #[bw(calc(pcards.len() as u8))]
836 pcards_size: u8,
837 #[br(count = pcards_size)]
838 pub pcards: Vec<InfoLocation>
839}
840
841#[binrw]
842#[derive(Debug, Clone, Message, GameMessage)]
843#[message(gm, flag = 90)]
844#[repr(C)]
845pub struct Draw {
846 pub player: CorePlayer,
847 #[bw(calc(codes.len() as u8))]
848 codes_size: u8,
849 #[br(count = codes_size)]
850 #[mask]
851 #[mask_if(self.player != player)]
852 pub codes: Vec<CardCode>,
853}
854
855#[bitfield]
856#[derive(BinRead, BinWrite, Debug, Copy, Clone, PartialEq, Eq, Default)]
857#[br(map = Self::from_bytes)]
858#[bw(map = |&x| Self::into_bytes(x))]
859#[repr(u32)]
860pub struct CardCode {
861 pub id: B28,
862 #[skip] __: B3,
863 pub is_public: bool,
864}
865
866impl GameMessage for CardCode {
867 fn mask(&mut self) {
868 if !self.is_public() {
869 self.set_id(0);
870 }
871 }
872 fn should_mask(&self, _player: CorePlayer) -> bool {
873 !self.is_public()
874 }
875}
876
877impl<T: GameMessage> GameMessage for (T,) {
878 fn mask(&mut self) {
879 self.0.mask();
880 }
881 fn should_mask(&self, player: CorePlayer) -> bool {
882 self.0.should_mask(player)
883 }
884}
885
886#[binrw]
887#[derive(Debug, Clone, Message, GameMessage)]
888#[message(gm, flag = 91)]
889#[repr(C)]
890pub struct Damage {
891 pub player: CorePlayer,
892 pub value: i32
893}
894
895#[binrw]
896#[derive(Debug, Clone, Message, GameMessage)]
897#[message(gm, flag = 92)]
898#[repr(C)]
899pub struct Recover {
900 pub player: CorePlayer,
901 pub value: i32
902}
903
904#[binrw]
905#[derive(Debug, Clone, Message, GameMessage)]
906#[message(gm, flag = 93)]
907#[repr(C)]
908pub struct Equip {
909 pub position1: InfoLocation,
910 pub position2: InfoLocation,
911}
912
913#[binrw]
914#[derive(Debug, Clone, Message, GameMessage)]
915#[message(gm, flag = 94)]
916#[repr(C)]
917pub struct LPUpdate {
918 pub player: CorePlayer,
919 pub lp: i32
920}
921
922#[binrw]
923#[derive(Debug, Clone, Message, GameMessage)]
924#[message(gm, flag = 95)]
925#[repr(C)]
926pub struct Unequip {
927 pub position: InfoLocation
928}
929
930#[binrw]
931#[derive(Debug, Clone, Message, GameMessage)]
932#[message(gm, flag = 96)]
933#[repr(C)]
934pub struct CardTarget {
935 pub position1: InfoLocation,
936 pub position2: InfoLocation,
937}
938
939#[binrw]
940#[derive(Debug, Clone, Message, GameMessage)]
941#[message(gm, flag = 97)]
942#[repr(C)]
943pub struct CancelTarget {
944 pub position1: InfoLocation,
945 pub position2: InfoLocation,
946}
947
948#[binrw]
949#[derive(Debug, Clone, Message, GameMessage)]
950#[message(gm, flag = 100)]
951#[repr(C)]
952pub struct PayLPCost {
953 pub player: CorePlayer,
954 pub cost: i32
955}
956
957#[binrw]
958#[derive(Debug, Clone, Message, GameMessage)]
959#[message(gm, flag = 101)]
960#[repr(C)]
961pub struct AddCounter {
962 pub counter_type: u16,
963 pub position: CardPosition<false, false, false>,
964 pub count: u16
965}
966
967#[binrw]
968#[derive(Debug, Clone, Message, GameMessage)]
969#[message(gm, flag = 102)]
970#[repr(C)]
971pub struct RemoveCounter {
972 pub counter_type: u16,
973 pub position: CardPosition<false, false, false>,
974 pub count: u16
975}
976
977#[binrw]
978#[derive(Debug, Clone, Message, GameMessage)]
979#[message(gm, flag = 110)]
980#[repr(C)]
981pub struct Attack {
982 pub attacker: InfoLocation,
983 pub defenser: InfoLocation,
984}
985
986#[binrw]
987#[derive(Debug, Clone, Message, GameMessage)]
988#[message(gm, flag = 111)]
989#[repr(C)]
990pub struct Battle {
991 pub attacker: InfoLocation,
992 pub attacker_attack: i32,
993 pub attacker_defense: i32,
994 #[br(map = |v: u8| v != 0)]
995 #[bw(map = |v: &bool| *v as u8)]
996 pub attacker_destroyed: bool,
997 pub defenser: InfoLocation,
998 pub defenser_attack: i32,
999 pub defenser_defense: i32,
1000 #[br(map = |v: u8| v != 0)]
1001 #[bw(map = |v: &bool| *v as u8)]
1002 pub defender_destroyed: bool,
1003}
1004
1005#[binrw]
1006#[derive(Debug, Clone, Message, GameMessage)]
1007#[message(gm, flag = 112)]
1008#[repr(C)]
1009pub struct AttackDisabled;
1010
1011#[binrw]
1012#[derive(Debug, Clone, Message, GameMessage)]
1013#[message(gm, flag = 113)]
1014#[repr(C)]
1015pub struct DamageStepStart;
1016
1017#[binrw]
1018#[derive(Debug, Clone, Message, GameMessage)]
1019#[message(gm, flag = 114)]
1020#[repr(C)]
1021pub struct DamageStepEnd;
1022
1023#[binrw]
1024#[derive(Debug, Clone, Message, GameMessage)]
1025#[message(gm, flag = 120)]
1026#[repr(C)]
1027pub struct MissedEffect {
1028 pub location: InfoLocation,
1029 pub code: i32
1030}
1031
1032#[binrw]
1033#[derive(Debug, Clone, Message, GameMessage)]
1034#[message(gm, flag = 121)]
1035#[repr(C)]
1036pub struct BeChainTarget;
1037
1038#[binrw]
1039#[derive(Debug, Clone, Message, GameMessage)]
1040#[message(gm, flag = 122)]
1041#[repr(C)]
1042pub struct CreateRelation;
1043
1044#[binrw]
1045#[derive(Debug, Clone, Message, GameMessage)]
1046#[message(gm, flag = 123)]
1047#[repr(C)]
1048pub struct ReleaseRelation;
1049
1050#[binrw]
1051#[derive(Debug, Clone, Message, GameMessage)]
1052#[message(gm, flag = 130)]
1053#[repr(C)]
1054pub struct TossCoin {
1055 pub player: CorePlayer,
1056 #[bw(calc(result.len() as u8))]
1057 result_size: u8,
1058 #[br(count = result_size)]
1059 pub result: Vec<i8>
1060}
1061
1062#[binrw]
1063#[derive(Debug, Clone, Message, GameMessage)]
1064#[message(gm, flag = 131)]
1065#[repr(C)]
1066pub struct TossDice {
1067 pub player: CorePlayer,
1068 #[bw(calc(result.len() as u8))]
1069 result_size: u8,
1070 #[br(count = result_size)]
1071 pub result: Vec<i8>
1072}
1073
1074#[binrw]
1075#[derive(Debug, Clone, Message, GameMessage)]
1076#[message(gm, flag = 132)]
1077#[repr(C)]
1078pub struct RockPaperScissors {
1079 #[wait_for]
1080 pub player: CorePlayer
1081}
1082
1083#[binrw]
1084#[derive(Debug, Clone, Message, GameMessage)]
1085#[message(gm, flag = 133)]
1086#[repr(C)]
1087pub struct HandResult {
1088 #[br(temp)]
1089 #[bw(calc = u8::from(*hand0) | (u8::from(*hand1) << 2))]
1090 _packed: u8,
1091 #[br(calc = Hand::try_from_primitive(_packed & 0x03).unwrap())]
1092 #[bw(ignore)]
1093 pub hand0: Hand,
1094 #[br(calc = Hand::try_from_primitive((_packed >> 2) & 0x03).unwrap())]
1095 #[bw(ignore)]
1096 pub hand1: Hand,
1097}
1098
1099#[binrw]
1100#[derive(Debug, Clone, Message, GameMessage)]
1101#[message(gm, flag = 140)]
1102#[repr(C)]
1103pub struct AnnounceRace {
1104 #[wait_for]
1105 pub player: CorePlayer,
1106 pub announce_count: u8,
1107 pub available: Race
1108}
1109
1110#[binrw]
1111#[derive(Debug, Clone, Message, GameMessage)]
1112#[message(gm, flag = 141)]
1113#[repr(C)]
1114pub struct AnnounceAttribute {
1115 #[wait_for]
1116 pub player: CorePlayer,
1117 pub announce_count: u8,
1118 pub available: Attribute
1119}
1120
1121#[binrw]
1122#[derive(Debug, Clone, Message, GameMessage)]
1123#[message(gm, flag = 142)]
1124#[repr(C)]
1125pub struct AnnounceCard {
1126 #[wait_for]
1127 pub player: CorePlayer,
1128 #[bw(calc(value.len() as u8))]
1129 value_size: u8,
1130 #[br(count = value_size)]
1131 pub value: Vec<Operation>
1132}
1133
1134#[binrw]
1135#[derive(Debug, Clone, Message, GameMessage)]
1136#[message(gm, flag = 143)]
1137#[repr(C)]
1138pub struct AnnounceNumber {
1139 #[wait_for]
1140 pub player: CorePlayer,
1141 #[bw(calc(value.len() as u8))]
1142 value_size: u8,
1143 #[br(count = value_size)]
1144 pub value: Vec<i32>
1145}
1146
1147#[binrw]
1148#[derive(Debug, Clone, Message, GameMessage)]
1149#[message(gm, flag = 160)]
1150#[repr(C)]
1151pub struct CardHint {
1152 pub position: InfoLocation,
1153 pub card_hint_type: crate::constants::CardHint,
1154 pub value: i32,
1155}
1156
1157#[binrw]
1158#[derive(Debug, Clone, Message, GameMessage)]
1159#[message(gm, flag = 161)]
1160#[repr(C)]
1161pub struct TagSwap {
1162 pub player: CorePlayer,
1163 pub main_count: u8,
1164 #[bw(calc(extra_codes.len() as u8))]
1165 pub extra_count: u8,
1166 pub extra_p_count: u8,
1167 #[bw(calc(hand_codes.len() as u8))]
1168 pub hand_count: u8,
1169 pub top_code: i32,
1170 #[br(count = hand_count)]
1171 #[mask]
1172 #[mask_if(self.player != player)]
1173 pub hand_codes: Vec<CardCode>,
1174 #[br(count = extra_count)]
1175 #[mask]
1176 #[mask_if(self.player != player)]
1177 pub extra_codes: Vec<CardCode>,
1178}
1179
1180#[binrw]
1181#[derive(Debug, Clone, Default)]
1182pub struct MzoneSlot {
1183 pub occupied: u8,
1184 #[br(if(occupied != 0))]
1185 #[bw(if(*occupied != 0))]
1186 pub position: u8,
1187 #[br(if(occupied != 0))]
1188 #[bw(if(*occupied != 0))]
1189 pub xyz_count: u8,
1190}
1191
1192#[binrw]
1193#[derive(Debug, Clone)]
1194pub struct SzonaSlot {
1195 pub occupied: u8,
1196 #[br(if(occupied != 0, Position::Any))]
1197 #[bw(if(*occupied != 0))]
1198 pub position: Position,
1199}
1200
1201#[binrw]
1202#[derive(Debug, Clone)]
1203pub struct PlayerField {
1204 pub lp: i32,
1205 pub mzone: [MzoneSlot; 7],
1206 pub szone: [SzonaSlot; 8],
1207 pub main_count: u8,
1208 pub hand_count: u8,
1209 pub grave_count: u8,
1210 pub remove_count: u8,
1211 pub extra_count: u8,
1212 pub extra_p_count: u8,
1213}
1214
1215#[binrw]
1216#[derive(Debug, Clone)]
1217pub struct ChainLink {
1218 pub code: u32,
1219 pub card_position: CardPosition<false, true, false>,
1220 pub controller: CorePlayer,
1221 pub location: Location,
1222 pub sequence: i8,
1223 pub description: u32,
1224}
1225
1226#[binrw]
1227#[derive(Debug, Clone, Message, GameMessage)]
1228#[message(gm, flag = 162)]
1229#[repr(C)]
1230pub struct ReloadField {
1231 pub duel_rule: MasterRule,
1232 pub player1: PlayerField,
1233 pub player2: PlayerField,
1234 #[bw(calc(chains.len() as u8))]
1235 pub chain_count: u8,
1236 #[br(count = chain_count)]
1237 pub chains: Vec<ChainLink>,
1238}
1239
1240#[binrw]
1241#[derive(Debug, Clone, Message, GameMessage)]
1242#[message(gm, flag = 163)]
1243pub struct AIName {
1244 #[bw(calc = name.as_bytes().len() as u16)]
1245 len: u16,
1246 #[br(count = len, map = |bytes: Vec<u8>| String::from_utf8(bytes).unwrap())]
1247 #[bw(map = |s: &String| s.as_bytes().to_vec())]
1248 #[brw(pad_after = 1)]
1249 pub name: String,
1250}
1251
1252#[binrw]
1253#[derive(Debug, Clone, Message, GameMessage)]
1254#[message(gm, flag = 164)]
1255pub struct ShowHint {
1256 #[bw(calc = name.as_bytes().len() as u16)]
1257 len: u16,
1258 #[br(count = len, map = |bytes: Vec<u8>| String::from_utf8(bytes).unwrap())]
1259 #[bw(map = |s: &String| s.as_bytes().to_vec())]
1260 #[brw(pad_after = 1)]
1261 pub name: String,
1262}
1263
1264#[binrw]
1265#[derive(Debug, Clone, Message, GameMessage)]
1266#[message(gm, flag = 165)]
1267#[repr(C)]
1268pub struct PlayerHint {
1269 pub player: CorePlayer,
1270 pub player_hint_type: crate::constants::PlayerHint,
1271 pub value: i32
1272}
1273
1274#[binrw]
1275#[derive(Debug, Clone, Message, GameMessage)]
1276#[message(gm, flag = 170)]
1277#[repr(C)]
1278pub struct MatchKill {
1279 pub card_code: u32
1280}
1281
1282#[binrw]
1283#[derive(Debug, Clone, Message, GameMessage)]
1284#[message(gm, flag = 180)]
1285#[repr(C)]
1286pub struct CustomMsg {
1287 #[br(parse_with=until_eof)]
1288 pub data: Vec<u8>
1289}
1290
1291#[cfg(test)]
1292mod test {
1293 use binrw::BinRead;
1294 use binrw::BinWrite;
1295 use std::io::Cursor;
1296
1297 #[test]
1298 fn print_sizes() {
1299 macro_rules! print_size {
1300 ($($msg:ident = $flag:literal),* $(,)?) => {
1301 println!("=== GM ===");
1302 $(
1303 println!(" {:30}: {:>4} bytes", stringify!($msg), std::mem::size_of::<super::$msg>());
1304 )*
1305 println!(" {:30}: {:>4} bytes", "MessageType", std::mem::size_of::<super::MessageType>());
1306 println!(" {:30}: {:>4} bytes", "Message", std::mem::size_of::<super::Message>());
1307 };
1308 }
1309 every_game_message_flat_message!(print_size);
1310 }
1311
1312 #[test]
1313 fn test_ai_name_roundtrip() {
1314 let original = super::AIName { name: "TestAI".into() };
1315 let mut writer = Cursor::new(Vec::new());
1316 original.write_le(&mut writer).unwrap();
1317 let bytes = writer.into_inner();
1318 assert_eq!(bytes[0..2], [6, 0], "len should be 6");
1319 assert_eq!(&bytes[2..8], b"TestAI", "name bytes");
1320 assert_eq!(bytes[8], 0, "null terminator");
1321 let roundtripped = super::AIName::read_le(&mut Cursor::new(&bytes)).unwrap();
1322 assert_eq!(roundtripped.name, "TestAI");
1323 }
1324}