1use borger_plugin_sdk::TickID;
2use std::fmt::{Debug, Error, Formatter};
3use std::time::Duration;
4use web_time::Instant;
5
6#[cfg(feature = "server")]
7use {crate::thread_comms::SimToClientChannel, borger_plugin_sdk::primitive::usize32};
8
9pub struct TickInfo {
10 first: Instant,
14
15 sim_dt: f32,
18
19 pub(crate) id_consensus: TickID,
26
27 #[cfg(feature = "client")]
30 pub(crate) id_consensus_received: TickID,
31
32 #[cfg(feature = "client")]
38 pub(crate) id_cur_received: TickID,
39
40 pub(crate) id_cur: TickID,
45
46 pub(crate) id_target: TickID,
48
49 #[cfg(feature = "client")]
60 pub(crate) pending_received_buffers: Vec<Vec<u8>>,
61}
62
63impl TickInfo {
64 pub(crate) fn new(sim_dt: f32, id_start: TickID, fast_forward_ticks: TickID) -> Self {
65 assert!(
66 sim_dt.is_finite() && sim_dt > 0.0,
67 "sim_dt must be a positive number of seconds"
68 );
69
70 TickInfo {
71 first: Instant::now()
72 - Duration::from_secs_f64((id_start + fast_forward_ticks) as f64 * sim_dt as f64),
73
74 sim_dt,
75
76 id_consensus: id_start,
77
78 #[cfg(feature = "client")]
79 id_consensus_received: id_start,
80
81 #[cfg(feature = "client")]
82 id_cur_received: id_start,
83
84 id_cur: id_start,
85 id_target: id_start + fast_forward_ticks,
86
87 #[cfg(feature = "client")]
88 pending_received_buffers: Vec::new(),
89 }
90 }
91
92 pub fn id(&self) -> TickID {
93 self.id_cur
94 }
95
96 #[cfg(feature = "server")]
100 pub(crate) fn has_consensus(&self) -> bool {
101 self.id_consensus > self.id_cur
106 }
107
108 #[cfg(feature = "server")]
111 #[doc(hidden)]
112 pub unsafe fn _has_consensus_tradeoff(&self) -> bool {
113 self.has_consensus()
114 }
115
116 #[cfg(feature = "client")]
119 pub(crate) fn is_fresh(&self) -> bool {
120 self.id_cur == self.id_target - 1
121 }
122
123 #[cfg(any(feature = "server", feature = "singlethreaded"))]
124 pub(crate) fn get_ticks(&self, dur: Duration) -> TickID {
125 f32::round(dur.as_secs_f32() / self.sim_dt) as TickID
126 }
127
128 pub(crate) fn get_duration(&self, offset: TickID) -> Duration {
129 Duration::from_secs_f64(self.sim_dt as f64 * offset as f64)
130 }
131
132 pub(crate) fn get_instant_at(&self, id: TickID) -> Instant {
133 self.first + self.get_duration(id)
134 }
135
136 #[cfg(any(feature = "server", feature = "singlethreaded"))]
137 pub(crate) fn get_tick_at(&self, instant: Instant) -> TickID {
138 let duration = instant - self.first;
139 self.get_ticks(duration)
140 }
141
142 pub(crate) fn get_now(&self) -> Instant {
143 self.get_instant_at(self.id_cur)
144 }
145
146 #[cfg(feature = "client")]
151 pub(crate) fn recalibrate(&mut self, offset_from_server: i16) {
152 if offset_from_server >= 0 {
153 let offset_from_server = offset_from_server as TickID;
155 self.first += self.get_duration(offset_from_server);
156 } else {
159 let offset_from_server = (-offset_from_server) as TickID;
161 self.first -= self.get_duration(offset_from_server);
162 self.id_target += offset_from_server;
163 }
164 }
165}
166
167impl Debug for TickInfo {
168 fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
169 #[derive(Debug)]
170 #[allow(dead_code)]
171 struct TickInfo {
172 id_consensus: TickID,
173 id_cur: TickID,
174 }
175
176 Debug::fmt(
177 &TickInfo {
178 id_consensus: self.id_consensus,
179 id_cur: self.id_cur,
180 },
181 f,
182 )
183 }
184}
185
186#[cfg(feature = "server")]
197pub(crate) enum UnrollbackableNetEvent {
198 ServerStart,
199 ClientConnect(SimToClientChannel),
200 ClientDisconnect(usize32),
201}