multiplayer/
multiplayer.rs

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}