1use crate::database::Finishes;
2use crate::Game;
3use crate::Snapper;
4use teehistorian::Chunk;
5use twsnap::{
6 compat::ddnet::{DemoWriter, WriteError},
7 time::Instant,
8 Snap,
9};
10use vek::Vec2;
11
12pub type DemoChatPtr<'a> = Option<&'a mut (dyn DemoChatWrite + 'static)>;
13pub type DemoPtr<'a, T> = Option<&'a mut (dyn DemoWrite<T> + 'static)>;
14
15#[derive(Debug, Clone, PartialEq)]
16pub struct ReplayerTeeInfo {
17 pub team: i32,
18 pub practice: bool,
19 pub pos: Vec2<i32>,
20}
21
22pub trait ReplayerChecker {
23 fn on_teehistorian_header(&mut self, header: &[u8]);
24 fn on_teehistorian_chunk(&mut self, now: Instant, chunk: &Chunk);
26
27 fn on_finish(&mut self, now: Instant, finish: &Finishes);
28
29 fn check_tees(
30 &mut self,
31 cur_time: Instant,
32 tees: &[Option<ReplayerTeeInfo>],
33 demo: DemoChatPtr,
34 );
35
36 fn finalize(&mut self);
38}
39
40pub trait ReplayerCheckerHelper {
41 fn check_tees(
42 &mut self,
43 cur_time: Instant,
44 tees: &[Option<ReplayerTeeInfo>],
45 demo: DemoChatPtr,
46 ) -> bool;
47}
48
49pub trait GameValidator {
51 fn tee_pos(&self, id: u32) -> Option<Vec2<i32>>;
52 fn max_tee_id(&self) -> u32;
54
55 fn set_tee_pos(&mut self, id: u32, pos: Option<Vec2<i32>>);
57
58 fn player_team(&self, id: u32) -> i32;
60 fn set_player_team(&mut self, id: u32, team: i32);
62}
63
64#[macro_export]
65macro_rules! info_demo {
66 ($write_chat:expr, $cur_time:expr, $($format_arg:tt)*) => {
67 if ::log::log_enabled!(log::Level::Info) {
68 let write_chat = $write_chat;
69 let cur_time: Instant = $cur_time;
70 let msg = format!($($format_arg)*);
71 ::log::info!("{cur_time}: {msg}");
72 if let Some(demo) = write_chat {
73 demo.write_chat(&msg).unwrap()
74 }
75 }
76 };
77}
78
79impl<T> ReplayerCheckerHelper for T
80where
81 T: GameValidator,
82{
83 fn check_tees(
84 &mut self,
85 cur_time: Instant,
86 tees: &[Option<ReplayerTeeInfo>],
87 demo: DemoChatPtr,
88 ) -> bool {
89 let mut demo = demo;
90 let mut correct = true;
91 for (player_id, player) in tees.iter().enumerate() {
92 let player_id = player_id as u32;
93 let world_pos = self.tee_pos(player_id);
94
95 let in_practice = player.as_ref().is_some_and(|p| p.practice);
96 let teehistorian_pos = player.as_ref().map(|p| p.pos);
97
98 if teehistorian_pos != world_pos {
99 if !in_practice {
100 correct = false;
101 info_demo!(
102 demo.as_deref_mut(),
103 cur_time,
104 "incorrect tee_pos player_id={}, world={:?}, teehistorian={:?} ({:?}), diff={:?}",
105 player_id,
106 world_pos,
107 teehistorian_pos,
108 teehistorian_pos.map(|p| p/32),
109 teehistorian_pos.zip(world_pos).map(|(a,b)| a - b)
110 );
111 }
112 self.set_tee_pos(player_id, teehistorian_pos);
113 }
114
115 if teehistorian_pos.is_some() {
117 let world_team = self.player_team(player_id);
118 let teehistorian_team = player.as_ref().map(|p| p.team).unwrap();
119 if teehistorian_team != world_team {
120 correct = false;
121 info_demo!(
122 demo.as_deref_mut(),
123 cur_time,
124 "incorrect team player_id={player_id}, world={world_team}, teehistorian={teehistorian_team}"
125 );
126 self.set_player_team(player_id, teehistorian_team);
127 }
128 }
129 }
130 for player_id in tees.len() as u32..self.max_tee_id() {
131 if let Some(world_pos) = self.tee_pos(player_id) {
132 correct = false;
133 info_demo!(
134 demo.as_deref_mut(),
135 cur_time,
136 "incorrect tee_pos player_id={}, world={:?}, teehistorian=None",
137 player_id,
138 world_pos,
139 );
140 self.set_tee_pos(player_id, None);
141 }
142 }
143 correct
144 }
145}
146
147pub trait GameReplayerAll: Game + ReplayerChecker {}
153impl<T> GameReplayerAll for T where T: Game + ReplayerChecker {}
154
155pub trait DemoWrite<World>: DemoChatWrite {
156 fn snap_and_write(
157 &mut self,
158 tick: Instant,
159 world: &World,
160 snap_buf: &mut Snap,
161 ) -> Result<(), WriteError>;
162
163 fn chat(&mut self) -> &mut (dyn DemoChatWrite + 'static);
165}
166pub trait DemoChatWrite {
167 fn write_chat(&mut self, msg: &str) -> Result<(), WriteError>;
168 fn write_player_chat(&mut self, player_id: i32, msg: &str) -> Result<(), WriteError>;
169}
170
171impl<T: Snapper> DemoWrite<T> for DemoWriter {
172 fn snap_and_write(
173 &mut self,
174 tick: Instant,
175 world: &T,
176 snap_buf: &mut Snap,
177 ) -> Result<(), WriteError> {
178 world.snap(snap_buf);
179 let res = self.write_snapshot(tick, snap_buf);
180 snap_buf.clear();
181 res
182 }
183
184 fn chat(&mut self) -> &mut (dyn DemoChatWrite + 'static) {
185 self
186 }
187}
188
189impl DemoChatWrite for DemoWriter {
190 fn write_chat(&mut self, msg: &str) -> Result<(), WriteError> {
191 self.write_chat(msg)
192 }
193 fn write_player_chat(&mut self, player_id: i32, msg: &str) -> Result<(), WriteError> {
194 self.write_player_chat(player_id, msg)
195 }
196}