1use geng::{prelude::*, TextAlign};
2use geng_net_simple as simple_net;
3
4type PlayerId = usize;
5
6#[derive(Debug, Serialize, Deserialize, Diff, Clone, PartialEq)]
7struct Player {
8 id: PlayerId,
9 position: vec2<f32>,
10}
11
12impl HasId for Player {
13 type Id = PlayerId;
14 fn id(&self) -> &PlayerId {
15 &self.id
16 }
17}
18
19#[derive(Debug, Serialize, Deserialize, Diff, Clone, PartialEq)]
20struct Model {
21 current_time: f32,
22 next_player_id: PlayerId,
23 players: Collection<Player>,
24}
25
26impl Model {
27 fn new() -> Self {
28 Self {
29 current_time: 0.0,
30 next_player_id: 1,
31 players: Collection::new(),
32 }
33 }
34}
35
36#[derive(Debug, Serialize, Deserialize, Clone)]
37pub enum Message {
38 UpdatePosition(vec2<f32>),
39}
40
41impl simple_net::Model for Model {
42 type PlayerId = PlayerId;
43 type Message = Message;
44 type Event = ();
45 const TICKS_PER_SECOND: f32 = 20.0;
46 fn new_player(&mut self, _events: &mut Vec<()>) -> Self::PlayerId {
47 let player_id = self.next_player_id;
48 self.next_player_id += 1;
49 self.players.insert(Player {
50 id: player_id,
51 position: vec2(
52 thread_rng().gen_range(-5.0..=5.0),
53 thread_rng().gen_range(-5.0..=5.0),
54 ),
55 });
56 player_id
57 }
58 fn drop_player(&mut self, _events: &mut Vec<()>, player_id: &PlayerId) {
59 self.players.remove(player_id);
60 }
61 fn handle_message(
62 &mut self,
63 _events: &mut Vec<()>,
64 player_id: &PlayerId,
65 message: Self::Message,
66 ) -> Vec<Self::Event> {
67 match message {
68 Message::UpdatePosition(position) => {
69 self.players.get_mut(player_id).unwrap().position = position;
70 }
71 }
72 vec![]
73 }
74 fn tick(&mut self, _events: &mut Vec<()>) {
75 self.current_time += 1.0 / Self::TICKS_PER_SECOND;
76 }
77}
78
79struct Game {
80 geng: Geng,
81 traffic_watcher: geng::net::TrafficWatcher,
82 next_update: f32,
83 player: Player,
84 model: simple_net::Remote<Model>,
85 current_time: f32,
86}
87
88impl Game {
89 fn new(geng: &Geng, player_id: PlayerId, model: simple_net::Remote<Model>) -> Self {
90 let current_time = model.get().current_time;
91 let player = model.get().players.get(&player_id).unwrap().clone();
92 Self {
93 geng: geng.clone(),
94 traffic_watcher: geng::net::TrafficWatcher::new(),
95 next_update: 0.0,
96 model,
97 player,
98 current_time,
99 }
100 }
101}
102
103impl geng::State for Game {
104 fn update(&mut self, delta_time: f64) {
105 self.model.update();
106 self.traffic_watcher.update(&self.model.traffic());
107 let delta_time = delta_time as f32;
108
109 self.current_time += delta_time;
110
111 const SPEED: f32 = 10.0;
112 if self.geng.window().is_key_pressed(geng::Key::ArrowLeft)
113 || self.geng.window().is_key_pressed(geng::Key::A)
114 {
115 self.player.position.x -= SPEED * delta_time;
116 }
117 if self.geng.window().is_key_pressed(geng::Key::ArrowRight)
118 || self.geng.window().is_key_pressed(geng::Key::D)
119 {
120 self.player.position.x += SPEED * delta_time;
121 }
122 if self.geng.window().is_key_pressed(geng::Key::ArrowUp)
123 || self.geng.window().is_key_pressed(geng::Key::W)
124 {
125 self.player.position.y += SPEED * delta_time;
126 }
127 if self.geng.window().is_key_pressed(geng::Key::ArrowDown)
128 || self.geng.window().is_key_pressed(geng::Key::S)
129 {
130 self.player.position.y -= SPEED * delta_time;
131 }
132
133 self.next_update -= delta_time;
134 if self.next_update < 0.0 {
135 while self.next_update < 0.0 {
136 self.next_update += 1.0 / <Model as simple_net::Model>::TICKS_PER_SECOND;
137 }
138 self.model
139 .send(Message::UpdatePosition(self.player.position));
140 }
141 }
142 fn draw(&mut self, framebuffer: &mut ugli::Framebuffer) {
143 ugli::clear(framebuffer, Some(Rgba::BLACK), None, None);
144 let camera = geng::Camera2d {
145 center: vec2(0.0, 0.0),
146 rotation: Angle::ZERO,
147 fov: 100.0,
148 };
149 let model = self.model.get();
150 for player in &model.players {
151 self.geng
152 .draw2d()
153 .circle(framebuffer, &camera, player.position, 1.0, Rgba::GRAY);
154 }
155 self.geng
156 .draw2d()
157 .circle(framebuffer, &camera, self.player.position, 1.0, Rgba::WHITE);
158 self.geng.default_font().draw(
159 framebuffer,
160 &geng::PixelPerfectCamera,
161 &format!("Server time: {:.1}", model.current_time),
162 vec2::splat(TextAlign::LEFT),
163 mat3::translate(vec2(0.0, 0.0)) * mat3::scale_uniform(32.0),
164 Rgba::WHITE,
165 );
166 self.geng.default_font().draw(
167 framebuffer,
168 &geng::PixelPerfectCamera,
169 &format!("Client time: {:.1}", self.current_time),
170 vec2::splat(TextAlign::LEFT),
171 mat3::translate(vec2(0.0, 32.0)) * mat3::scale_uniform(32.0),
172 Rgba::WHITE,
173 );
174 self.geng.default_font().draw(
175 framebuffer,
176 &geng::PixelPerfectCamera,
177 &format!("traffic: {}", self.traffic_watcher),
178 vec2::splat(TextAlign::LEFT),
179 mat3::translate(vec2(0.0, 32.0 * 2.0)) * mat3::scale_uniform(32.0),
180 Rgba::WHITE,
181 );
182 }
183}
184
185fn main() {
186 logger::init();
187 geng::setup_panic_handler();
188 simple_net::run("Multiplayer", Model::new, Game::new);
189}