Skip to main content

twgame_core/
lib.rs

1pub mod config;
2pub mod console;
3pub mod database;
4mod display;
5pub mod net_msg;
6pub mod replay;
7
8/// [`teehistorian`](https://crates.io/crates/teehistorian) crate
9pub use teehistorian;
10/// [`TwSnap`](https://crates.io/crates/twsnap) crate
11pub use twsnap;
12
13pub use display::DisplayChunk;
14
15use crate::console::Command;
16use serde::{Deserialize, Serialize};
17use std::collections::HashMap;
18use twsnap::time::Instant;
19use twsnap::Snap;
20use uuid::Uuid;
21use vek::Vec2;
22
23#[derive(Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize)]
24#[repr(C)]
25pub struct Input {
26    pub direction: i32,
27    pub target_x: i32,
28    pub target_y: i32,
29    pub jump: i32,
30    pub fire: i32,
31    pub hook: i32,
32    /// range 0 - 256
33    pub player_flags: i32,
34    pub wanted_weapon: i32,
35    pub next_weapon: i32,
36    pub prev_weapon: i32,
37}
38
39impl Input {
40    /// Creates new Input with non-centered cursor
41    pub fn new() -> Input {
42        Input {
43            direction: 0,
44            target_x: 0,
45            target_y: -1,
46            jump: 0,
47            fire: 0,
48            hook: 0,
49            player_flags: 0,
50            wanted_weapon: 0,
51            next_weapon: 0,
52            prev_weapon: 0,
53        }
54    }
55
56    fn disallow_target_center(mut self) -> Self {
57        if self.target_x == 0 && self.target_y == 0 {
58            self.target_y = -1;
59        }
60        self
61    }
62
63    // CInputCount CountInput(int Prev, int Cur)
64    fn count_presses(mut cur: i32, mut prev: i32) -> i32 {
65        // TODO: something like (((cur - prev) & 63) + 1) / 2
66        let mut count = 0;
67        prev &= 63; // TODO: INPUT_STATE_MASK
68        cur &= 63;
69        while prev != cur {
70            prev = (prev + 1) & 63;
71            if prev & 1 != 0 {
72                count += 1;
73            }
74        }
75        count
76    }
77
78    /// count how often weapon next was pressed since the last input
79    pub fn count_weapon_next(&self, prev: &Input) -> i32 {
80        Input::count_presses(self.next_weapon, prev.next_weapon)
81    }
82
83    /// count how often weapon prev was pressed since the last input
84    pub fn count_weapon_prev(&self, prev: &Input) -> i32 {
85        Input::count_presses(self.prev_weapon, prev.prev_weapon)
86    }
87
88    /// count how often weapon fire was pressed since the last input
89    pub fn count_weapon_fire(&self, prev: &Input) -> i32 {
90        Input::count_presses(self.fire, prev.fire)
91    }
92
93    /// returns cursor coordinates converted to f32
94    pub fn cursor(&self) -> Vec2<f32> {
95        Vec2::new(self.target_x as f32, self.target_y as f32)
96    }
97
98    /// returns if at the end of the last input firing was hold
99    pub fn firing(&self) -> bool {
100        (self.fire & 1) != 0
101    }
102
103    /// returns the player flag for whether the spec cam is on
104    pub fn spec_cam_active(&self) -> bool {
105        (self.player_flags & (1 << 5)) != 0
106    }
107}
108
109impl From<[i32; 10]> for Input {
110    fn from(input: [i32; 10]) -> Self {
111        Input {
112            direction: input[0],
113            target_x: input[1],
114            target_y: input[2],
115            jump: input[3],
116            fire: input[4],
117            hook: input[5],
118            // range 0 - 256
119            player_flags: input[6],
120            wanted_weapon: input[7],
121            next_weapon: input[8],
122            prev_weapon: input[9],
123        }
124        .disallow_target_center()
125    }
126}
127
128impl Input {
129    pub fn add_input_diff(&mut self, input_diff: [i32; 10]) {
130        self.direction += input_diff[0];
131        self.target_x += input_diff[1];
132        self.target_y += input_diff[2];
133        self.jump += input_diff[3];
134        self.fire += input_diff[4];
135        self.hook += input_diff[5];
136        self.player_flags += input_diff[6];
137        self.wanted_weapon += input_diff[7];
138        self.next_weapon += input_diff[8];
139        self.prev_weapon += input_diff[9];
140    }
141}
142
143pub trait Game {
144    // input functions
145    fn player_join(&mut self, id: u32);
146    fn player_ready(&mut self, id: u32);
147    fn player_input(&mut self, id: u32, input: &Input);
148    fn player_leave(&mut self, id: u32);
149
150    fn on_net_msg(&mut self, id: u32, msg: &net_msg::ClNetMessage);
151    fn on_command(&mut self, id: u32, command: &Command);
152
153    fn swap_tees(&mut self, id1: u32, id2: u32);
154
155    // actions before player position check in teehistorian
156    fn tick(&mut self, cur_time: Instant);
157
158    /// returns whether there are still players or spawnable entities in the world
159    /// When true, tick doesn't need to be called until next `player_join` event.
160    fn is_empty(&self) -> bool;
161}
162
163pub trait Snapper {
164    fn snap(&self, snapshot: &mut Snap);
165}
166
167#[derive(Deserialize, Debug)]
168pub struct ThHeader {
169    pub game_uuid: Uuid,
170    pub server_version: String,
171    pub prng_description: Option<String>,
172    pub start_time: String,
173    pub map_name: String,
174    pub map_sha256: Option<String>,
175    pub map_crc: String,
176    pub map_size: String,
177    pub version_minor: Option<String>,
178    pub config: HashMap<String, String>,
179    pub tuning: HashMap<String, String>,
180}
181
182impl ThHeader {
183    pub fn from_buf(buf: &[u8]) -> Self {
184        let buf = String::from_utf8_lossy(buf);
185        serde_json::from_str(&buf).unwrap()
186    }
187}
188
189/// Implement normalize function, because order of operation is important for physics
190/// teehistorian_replayer_res_physics_1_weapon_shotgun_other_right_teehistorian would fail
191/// otherwise. See commit fcdd5ebd375c57cf0bc4ddd92610d813f68126ad
192pub fn normalize(v: Vec2<f32>) -> Vec2<f32> {
193    // float divisor = length(v);
194    let divisor = v.magnitude();
195    // if(divisor == 0.0f)
196    if divisor == 0.0 {
197        // return vector2_base<float>(0.0f, 0.0f);
198        return Vec2::zero();
199    }
200    // float l = (float)(1.0f / divisor);
201    let l = 1.0 / divisor;
202    // return vector2_base<float>(v.x * l, v.y * l);
203    v * l
204}