├── .gitignore ├── src ├── proto │ ├── mod.rs │ └── proto_all.rs ├── ecs │ ├── mod.rs │ ├── components.rs │ ├── systems.rs │ ├── game_state.rs │ └── game.rs ├── headers.rs ├── events.rs ├── main.rs ├── room.rs ├── connection.rs └── lobby.rs ├── Screenshot_2020-10-22_17-32-18.png ├── proto_script.sh ├── README.md ├── Cargo.toml ├── proto_files └── proto-all.proto └── Cargo.lock /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /src/proto/mod.rs: -------------------------------------------------------------------------------- 1 | // Automatically generated mod.rs 2 | pub mod proto_all; 3 | -------------------------------------------------------------------------------- /src/ecs/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod components; 2 | pub mod game; 3 | pub mod game_state; 4 | pub mod systems; 5 | -------------------------------------------------------------------------------- /Screenshot_2020-10-22_17-32-18.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Descrout/game-server/HEAD/Screenshot_2020-10-22_17-32-18.png -------------------------------------------------------------------------------- /proto_script.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | pb-rs --dont_use_cow -o src/proto/proto-all.rs proto_files/proto-all.proto 3 | pbf proto_files/proto-all.proto --browser > ../nightcomes-client/js/proto-all.js -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Webscoket Game Server Example 2 | ![alt text](./Screenshot_2020-10-22_17-32-18.png) 3 | - After I read the Gabriel Gambetta's [Fast Paced Multiplayer](https://www.gabrielgambetta.com/client-server-game-architecture.html) articles, I decided to implement it for myself. 4 | - You can access to the client counterpart [here.](https://github.com/Descrout/game-client) 5 | ### Usage 6 | - Just change the **PORT** constant in [main.rs](https://github.com/Descrout/game-server/blob/master/src/main.rs#L17) and you are good to go. 7 | -------------------------------------------------------------------------------- /src/ecs/components.rs: -------------------------------------------------------------------------------- 1 | use specs::{Component, VecStorage}; 2 | 3 | #[derive(Component, Debug, Clone)] 4 | #[storage(VecStorage)] 5 | pub struct IDComp { 6 | pub id: u32, 7 | } 8 | 9 | #[derive(Component, Debug, Clone)] 10 | #[storage(VecStorage)] 11 | pub struct Position { 12 | pub x: f32, 13 | pub y: f32, 14 | pub angle: f32, 15 | pub last_seq: u32, 16 | } 17 | 18 | #[derive(Component, Debug, Clone, Default)] 19 | #[storage(VecStorage)] 20 | pub struct Velocity { 21 | pub dx: f32, 22 | pub dy: f32, 23 | } 24 | -------------------------------------------------------------------------------- /src/ecs/systems.rs: -------------------------------------------------------------------------------- 1 | use super::components::*; 2 | use specs::prelude::*; 3 | 4 | pub struct VelSystem; 5 | 6 | impl<'a> System<'a> for VelSystem { 7 | type SystemData = (WriteStorage<'a, Position>, WriteStorage<'a, Velocity>); 8 | 9 | fn run(&mut self, data: Self::SystemData) { 10 | let (mut pos, mut vel) = data; 11 | for (pos, vel) in (&mut pos, &mut vel).join() { 12 | pos.x += vel.dx; 13 | pos.y += vel.dy; 14 | vel.dx = 0.0; 15 | vel.dy = 0.0; 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/headers.rs: -------------------------------------------------------------------------------- 1 | #[allow(non_snake_case)] 2 | pub mod SendHeader { 3 | pub const USERS: u8 = 0; 4 | pub const ROOMS: u8 = 1; 5 | pub const LOBBY_CHAT: u8 = 2; 6 | pub const ERROR: u8 = 3; 7 | pub const GAME_CHAT: u8 = 4; 8 | pub const STATE: u8 = 5; 9 | } 10 | 11 | #[allow(non_snake_case)] 12 | pub mod ReceiveHeader { 13 | pub const HANDSHAKE: u8 = 0; 14 | pub const CREATE_ROOM: u8 = 1; 15 | pub const JOIN_ROOM: u8 = 2; 16 | pub const LOBBY_CHAT: u8 = 3; 17 | pub const QUIT_TO_LOBBY: u8 = 4; 18 | pub const GAME_CHAT: u8 = 5; 19 | pub const GAME_INPUT: u8 = 6; 20 | } 21 | -------------------------------------------------------------------------------- /src/ecs/game_state.rs: -------------------------------------------------------------------------------- 1 | use crate::proto::proto_all; 2 | #[derive(Debug, Clone)] 3 | pub struct GameState { 4 | pub id: u32, 5 | pub state: proto_all::State, 6 | } 7 | 8 | impl GameState { 9 | pub fn new(id: u32) -> Self { 10 | Self { 11 | id, 12 | state: proto_all::State { 13 | last_seq: 0, 14 | entities: Vec::new(), 15 | }, 16 | } 17 | } 18 | 19 | pub fn clear(&mut self) { 20 | self.state.entities.clear(); 21 | } 22 | 23 | pub fn add_entity(&mut self, entity: proto_all::Entity) { 24 | self.state.entities.push(entity); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "nightcomes-server" 3 | version = "0.1.0" 4 | authors = ["Adil Basar "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | futures-util = { version = "0.3", default-features = false, features = ["async-await", "sink", "std"] } 11 | tokio = { version = "0.2", default-features = false, features = ["io-util", "macros", "stream", "time", "sync"] } 12 | tokio-tungstenite = "*" 13 | quick-protobuf = "0.7.0" 14 | specs = { version = "0.16.1", features = ["specs-derive"] } 15 | 16 | [dependencies.tungstenite] 17 | version = "0.11.1" 18 | default-features = false -------------------------------------------------------------------------------- /src/events.rs: -------------------------------------------------------------------------------- 1 | use crate::connection::Connection; 2 | use crate::ecs::game_state::GameState; 3 | use crate::proto::proto_all::*; 4 | use tokio::sync::mpsc::UnboundedSender; 5 | use tokio::sync::oneshot::Sender; 6 | 7 | #[derive(Debug)] 8 | pub enum LobbyEvents { 9 | Handshake(Sender, Connection), 10 | Disconnect(u32), 11 | CreateRoom(u32, Sender>, CreateRoom), 12 | JoinRoom(u32, Sender>, JoinRoom), 13 | TakeBack(Sender<()>, Connection), 14 | PlayerCount(u32, u32, usize), 15 | Chat(u32, Chat), 16 | } 17 | 18 | #[derive(Debug)] 19 | pub enum GameEvents { 20 | Join(Connection), 21 | Quit(u32, Option>), 22 | Chat(u32, Chat), 23 | Input(u32, GameInput), 24 | StateOut(Vec), 25 | } 26 | -------------------------------------------------------------------------------- /proto_files/proto-all.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | message Error{ 4 | string title = 1; 5 | string message = 2; 6 | } 7 | 8 | message User{ 9 | uint32 id = 1; 10 | string name = 2; 11 | } 12 | 13 | message Room{ 14 | uint32 id = 1; 15 | string name = 2; 16 | bool password = 3; 17 | uint32 players = 4; 18 | } 19 | 20 | message Handshake{ 21 | string name = 1; 22 | } 23 | 24 | message Rooms{ 25 | repeated Room rooms = 1; 26 | } 27 | 28 | message Users{ 29 | repeated User users = 1; 30 | uint32 me = 2; 31 | } 32 | 33 | message CreateRoom{ 34 | string name = 1; 35 | optional string password = 2; 36 | } 37 | 38 | message JoinRoom{ 39 | uint32 id = 1; 40 | optional string password = 2; 41 | } 42 | 43 | message Chat{ 44 | optional string name = 1; 45 | string message = 2; 46 | } 47 | 48 | message QuitLobby { 49 | 50 | } 51 | 52 | message Entity{ 53 | uint32 id = 1; 54 | float x = 2; 55 | float y = 3; 56 | float angle = 4; 57 | } 58 | 59 | message State { 60 | uint32 last_seq = 1; 61 | repeated Entity entities = 2; 62 | } 63 | 64 | message GameInput { 65 | float horizontalPress = 1; 66 | float verticalPress = 2; 67 | float angle = 3; 68 | uint32 sequence = 4; 69 | } -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | mod connection; 2 | mod ecs; 3 | mod events; 4 | mod headers; 5 | mod lobby; 6 | mod proto; 7 | mod room; 8 | 9 | use tokio::net::TcpListener; 10 | use tokio_tungstenite::accept_async; 11 | 12 | use connection::Connection; 13 | use events::*; 14 | use lobby::Lobby; 15 | use tokio::sync::mpsc; 16 | 17 | const PORT: &str = "6444"; 18 | 19 | #[tokio::main] 20 | async fn main() { 21 | let addr = format!("0.0.0.0:{}", PORT); 22 | 23 | let mut listener = TcpListener::bind(&addr) 24 | .await 25 | .expect("Listening TCP failed."); 26 | 27 | let (lobby_sender, lobby_receiver) = mpsc::unbounded_channel::(); 28 | 29 | // Listen lobby for room creation and chat 30 | tokio::spawn(Lobby::listen(lobby_sender.clone(), lobby_receiver)); 31 | 32 | println!("Listening on: {}", addr); 33 | 34 | // Accept new clients 35 | while let Ok((stream, peer)) = listener.accept().await { 36 | let lobby_sender = lobby_sender.clone(); 37 | //tokio::spawn(async move { 38 | match accept_async(stream).await { 39 | Err(e) => println!("Websocket connection error : {}", e), 40 | Ok(ws_stream) => { 41 | println!("New connection : {}", peer); 42 | tokio::spawn(Connection::handshake(ws_stream, lobby_sender)); 43 | } 44 | } 45 | //}); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/ecs/game.rs: -------------------------------------------------------------------------------- 1 | use super::components::*; 2 | use super::game_state::GameState; 3 | use specs::prelude::*; 4 | use std::collections::HashMap; 5 | 6 | pub const SERVER_RATE: u64 = 45; // 60fps 0.016dt 7 | 8 | pub struct Game { 9 | world: World, 10 | players: HashMap, 11 | pub states: Vec, 12 | } 13 | 14 | impl Game { 15 | pub fn new(admin_id: u32) -> Self { 16 | let mut game = Self { 17 | world: Self::setup(), 18 | players: HashMap::new(), 19 | states: Vec::new(), 20 | }; 21 | game.add_player(admin_id); 22 | game 23 | } 24 | 25 | fn setup() -> World { 26 | let mut world = World::new(); 27 | world.register::(); 28 | world.register::(); 29 | world.register::(); 30 | world 31 | } 32 | 33 | pub fn add_player(&mut self, id: u32) { 34 | let ent = self 35 | .world 36 | .create_entity() 37 | .with(Position { 38 | x: 100.0, 39 | y: 100.0, 40 | angle: 0.0, 41 | last_seq: 0, 42 | }) 43 | .with(IDComp { id }) 44 | .with(Velocity::default()) 45 | .build(); 46 | self.players.insert(id, ent); 47 | self.states.push(GameState::new(id)); 48 | } 49 | 50 | pub fn remove_player(&mut self, id: &u32) { 51 | let ent = self.players.remove(id).unwrap(); 52 | let _ = self.world.delete_entity(ent); 53 | self.states.retain(|gs| gs.id != *id); 54 | } 55 | 56 | pub fn set_input(&mut self, id: u32, input: crate::proto::proto_all::GameInput) { 57 | let ent = *self.players.get(&id).unwrap(); 58 | //let mut vel = self.world.write_storage::(); 59 | let mut pos = self.world.write_storage::(); 60 | //let vel = vel.get_mut(ent).unwrap(); 61 | let pos = pos.get_mut(ent).unwrap(); 62 | pos.x += 300.0 * input.horizontalPress; 63 | pos.y += 300.0 * input.verticalPress; 64 | pos.angle = input.angle; 65 | pos.last_seq = input.sequence; 66 | } 67 | 68 | pub fn update(&mut self) { 69 | //let mut sys = VelSystem; 70 | //sys.run_now(&self.world); 71 | 72 | self.world.maintain(); 73 | 74 | let player = self.world.read_storage::(); 75 | let pos = self.world.read_storage::(); 76 | 77 | for gs in self.states.iter_mut() { 78 | gs.clear(); 79 | for (player, pos) in (&player, &pos).join() { 80 | if player.id == gs.id { 81 | // if this is the player who will get this state as own 82 | gs.state.last_seq = pos.last_seq; 83 | } 84 | gs.add_entity(crate::proto::proto_all::Entity { 85 | id: player.id, 86 | x: pos.x, 87 | y: pos.y, 88 | angle: pos.angle, 89 | }); 90 | } 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/room.rs: -------------------------------------------------------------------------------- 1 | use crate::connection::Connection; 2 | use crate::ecs::game::Game; 3 | use crate::events::*; 4 | use crate::headers::SendHeader; 5 | use crate::proto::proto_all::*; 6 | use futures_util::sink::SinkExt; 7 | use quick_protobuf::Writer; 8 | use std::collections::HashMap; 9 | use tokio::sync::mpsc; 10 | use tokio::sync::mpsc::{UnboundedReceiver, UnboundedSender}; 11 | use tungstenite::Message; 12 | 13 | pub struct Room { 14 | players: HashMap, 15 | } 16 | 17 | impl Room { 18 | pub fn new() -> Self { 19 | Self { 20 | players: HashMap::new(), 21 | } 22 | } 23 | 24 | fn serialize_chat(&self, id: u32, mut chat: Chat) -> Vec { 25 | let conn = self.players.get(&id).unwrap(); 26 | 27 | chat.name = format!("({}) {}", id, conn.name); 28 | 29 | let mut out = Vec::new(); 30 | let mut writer = Writer::new(&mut out); 31 | writer.write_message(&chat).expect("Cannot serialize chat"); 32 | out[0] = SendHeader::GAME_CHAT; 33 | out 34 | } 35 | 36 | fn serialize_state(state: State) -> Vec { 37 | let mut out = Vec::new(); 38 | let mut writer = Writer::new(&mut out); 39 | writer 40 | .write_message(&state) 41 | .expect("Cannot serialize state"); 42 | out[0] = SendHeader::STATE; 43 | out 44 | } 45 | 46 | pub async fn listen( 47 | game_id: u32, 48 | admin: Connection, 49 | mut receiver: UnboundedReceiver, 50 | to_lobby: UnboundedSender, 51 | ) { 52 | println!("New game listening : {} admin : {}", game_id, admin.id); 53 | let mut game = Game::new(admin.id); 54 | let (tx, rx) = mpsc::unbounded_channel::(); 55 | tokio::spawn(Self::broadcast(game_id, admin, rx, to_lobby)); 56 | let mut interval = tokio::time::interval(std::time::Duration::from_millis( 57 | crate::ecs::game::SERVER_RATE, 58 | )); 59 | let mut accum = 0.0; 60 | let mut dt = 0.0; 61 | 62 | loop { 63 | let start = tokio::time::Instant::now(); 64 | 65 | while let Ok(event) = receiver.try_recv() { 66 | match event { 67 | GameEvents::Join(conn) => { 68 | game.add_player(conn.id); 69 | if tx.send(GameEvents::Join(conn)).is_err() { 70 | return; 71 | } 72 | } 73 | GameEvents::Quit(user_id, forward) => { 74 | game.remove_player(&user_id); 75 | if tx.send(GameEvents::Quit(user_id, forward)).is_err() { 76 | return; 77 | } 78 | } 79 | GameEvents::Input(id, input) => { 80 | game.set_input(id, input); 81 | } 82 | _ => { 83 | if tx.send(event).is_err() { 84 | return; 85 | } 86 | } 87 | } 88 | } 89 | 90 | accum += dt; 91 | while accum >= 0.045 { 92 | accum -= 0.045; 93 | 94 | game.update(); 95 | 96 | if tx.send(GameEvents::StateOut(game.states.clone())).is_err() { 97 | return; 98 | } 99 | } 100 | 101 | interval.tick().await; 102 | dt = start.elapsed().as_secs_f32(); 103 | } 104 | } 105 | 106 | async fn broadcast( 107 | room_id: u32, 108 | admin: Connection, 109 | mut rx: UnboundedReceiver, 110 | to_lobby: UnboundedSender, 111 | ) { 112 | let mut room = Self::new(); 113 | room.players.insert(admin.id, admin); 114 | while let Some(event) = rx.recv().await { 115 | match event { 116 | GameEvents::Join(conn) => { 117 | room.players.insert(conn.id, conn); 118 | } 119 | GameEvents::Quit(user_id, forward) => { 120 | let conn = room.players.remove(&user_id).unwrap(); 121 | if let Some(f) = forward { 122 | to_lobby.send(LobbyEvents::TakeBack(f, conn)).unwrap(); 123 | } 124 | let len = room.players.len(); 125 | to_lobby 126 | .send(LobbyEvents::PlayerCount(room_id, user_id, len)) 127 | .unwrap(); 128 | if len == 0 { 129 | println!("Game terminated : {}", room_id); 130 | rx.close(); 131 | return; 132 | } 133 | } 134 | GameEvents::Chat(id, chat) => { 135 | let data = room.serialize_chat(id, chat); 136 | for conn in room.players.values_mut() { 137 | let _ = conn.sender.send(Message::Binary(data.clone())).await; 138 | } 139 | } 140 | GameEvents::StateOut(states) => { 141 | for gs in states.iter() { 142 | let data = Self::serialize_state(gs.state.clone()); 143 | let _ = room 144 | .players 145 | .get_mut(&gs.id) 146 | .unwrap() 147 | .sender 148 | .send(Message::Binary(data)) 149 | .await; 150 | } 151 | } 152 | _ => (), 153 | } 154 | } 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /src/connection.rs: -------------------------------------------------------------------------------- 1 | use crate::events::*; 2 | use crate::headers::ReceiveHeader; 3 | use crate::proto::proto_all::*; 4 | use futures_util::stream::StreamExt; 5 | use futures_util::stream::{SplitSink, SplitStream}; 6 | use quick_protobuf::{BytesReader, MessageRead, Result}; 7 | use tokio::net::TcpStream; 8 | use tokio::sync::mpsc::UnboundedSender; 9 | use tokio::sync::oneshot; 10 | use tokio_tungstenite::WebSocketStream; 11 | use tungstenite::Message; 12 | 13 | #[derive(Debug)] 14 | pub struct Connection { 15 | pub id: u32, 16 | pub name: String, 17 | pub sender: SplitSink, Message>, 18 | } 19 | 20 | impl Connection { 21 | pub fn new(name: String, sender: SplitSink, Message>) -> Self { 22 | Self { 23 | id: 0, 24 | name, 25 | sender, 26 | } 27 | } 28 | 29 | pub fn parse_receive_game(id: u32, header: u8, msg: Vec) -> Result { 30 | let mut reader = BytesReader::from_bytes(&msg); 31 | match header { 32 | ReceiveHeader::GAME_CHAT if msg.len() < 105 => { 33 | Ok(GameEvents::Chat(id, Chat::from_reader(&mut reader, &msg)?)) 34 | } 35 | ReceiveHeader::GAME_INPUT => Ok(GameEvents::Input( 36 | id, 37 | GameInput::from_reader(&mut reader, &msg)?, 38 | )), 39 | _ => Err(quick_protobuf::Error::Message( 40 | "Undefined header.".to_string(), 41 | )), 42 | } 43 | } 44 | 45 | pub fn parse_receive_lobby( 46 | id: u32, 47 | mut msg: Vec, 48 | ) -> Result<( 49 | Option>>, 50 | LobbyEvents, 51 | )> { 52 | let header = msg.remove(0); 53 | let mut reader = BytesReader::from_bytes(&msg); 54 | match header { 55 | ReceiveHeader::CREATE_ROOM | ReceiveHeader::JOIN_ROOM => { 56 | let (tx, rx) = oneshot::channel::>(); 57 | if header == ReceiveHeader::CREATE_ROOM { 58 | Ok(( 59 | Some(rx), 60 | LobbyEvents::CreateRoom( 61 | id, 62 | tx, 63 | CreateRoom::from_reader(&mut reader, &msg)?, 64 | ), 65 | )) 66 | } else { 67 | Ok(( 68 | Some(rx), 69 | LobbyEvents::JoinRoom(id, tx, JoinRoom::from_reader(&mut reader, &msg)?), 70 | )) 71 | } 72 | } 73 | ReceiveHeader::LOBBY_CHAT if msg.len() < 105 => Ok(( 74 | None, 75 | LobbyEvents::Chat(id, Chat::from_reader(&mut reader, &msg)?), 76 | )), 77 | _ => Err(quick_protobuf::Error::Message( 78 | "Undefined header.".to_string(), 79 | )), 80 | } 81 | } 82 | 83 | pub async fn handshake( 84 | ws_stream: WebSocketStream, 85 | to_lobby: UnboundedSender, 86 | ) { 87 | let (sender, mut receiver) = ws_stream.split(); 88 | let (tx, rx) = oneshot::channel::(); 89 | 90 | if let Some(msg) = receiver.next().await { 91 | if let Ok(msg) = msg { 92 | if !msg.is_binary() || msg.len() > 200 { 93 | return; 94 | } 95 | let mut msg = msg.into_data(); 96 | let header = msg.remove(0); 97 | if header == ReceiveHeader::HANDSHAKE { 98 | let mut reader = BytesReader::from_bytes(&msg); 99 | if let Ok(hs) = Handshake::from_reader(&mut reader, &msg) { 100 | let conn = Self::new(hs.name, sender); 101 | to_lobby.send(LobbyEvents::Handshake(tx, conn)).unwrap(); 102 | } else { 103 | return; 104 | } 105 | } else { 106 | return; 107 | } 108 | } else { 109 | return; 110 | } 111 | } 112 | 113 | if let Ok(id) = rx.await { 114 | tokio::spawn(Self::listen_lobby(id, receiver, to_lobby)); 115 | } else { 116 | println!("Handshake refused."); 117 | } 118 | } 119 | 120 | fn lobby_listener_spawner( 121 | id: u32, 122 | receiver: SplitStream>, 123 | to_lobby: UnboundedSender, 124 | ) { 125 | tokio::spawn(async move { 126 | Self::listen_lobby(id, receiver, to_lobby).await; 127 | }); 128 | } 129 | 130 | async fn listen_game( 131 | id: u32, 132 | mut receiver: SplitStream>, 133 | to_lobby: UnboundedSender, 134 | to_game: UnboundedSender, 135 | ) { 136 | while let Some(msg) = receiver.next().await { 137 | if let Ok(msg) = msg { 138 | if msg.is_binary() { 139 | let mut msg = msg.into_data(); 140 | let header = msg.remove(0); 141 | if let Ok(event) = Self::parse_receive_game(id, header, msg) { 142 | to_game.send(event).unwrap(); 143 | } else if header == ReceiveHeader::QUIT_TO_LOBBY { 144 | let (tx, rx) = oneshot::channel::<()>(); 145 | to_game.send(GameEvents::Quit(id, Some(tx))).unwrap(); 146 | if let Ok(()) = rx.await { 147 | Self::lobby_listener_spawner(id, receiver, to_lobby); 148 | return; 149 | } 150 | } 151 | } else if msg.is_close() { 152 | break; 153 | } 154 | } else { 155 | break; 156 | } 157 | } 158 | to_game.send(GameEvents::Quit(id, None)).unwrap(); 159 | } 160 | 161 | async fn listen_lobby( 162 | id: u32, 163 | mut receiver: SplitStream>, 164 | to_lobby: UnboundedSender, 165 | ) { 166 | println!("Handshake accepted, ID[{}] listening...", id); 167 | while let Some(msg) = receiver.next().await { 168 | if let Ok(msg) = msg { 169 | if msg.is_binary() { 170 | if let Ok((recv, event)) = Self::parse_receive_lobby(id, msg.into_data()) { 171 | to_lobby.send(event).unwrap(); 172 | if let Some(rx) = recv { 173 | if let Ok(to_game) = rx.await { 174 | tokio::spawn(Self::listen_game(id, receiver, to_lobby, to_game)); 175 | return; 176 | } 177 | } 178 | } 179 | } else if msg.is_close() { 180 | break; 181 | } 182 | } else { 183 | break; 184 | } 185 | } 186 | to_lobby.send(LobbyEvents::Disconnect(id)).unwrap(); 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /src/lobby.rs: -------------------------------------------------------------------------------- 1 | use crate::connection::Connection; 2 | use crate::events::*; 3 | use crate::headers::SendHeader; 4 | use crate::proto::proto_all; 5 | use crate::proto::proto_all::*; 6 | use crate::room::Room; 7 | use futures_util::sink::SinkExt; 8 | use quick_protobuf::Writer; 9 | use std::collections::HashMap; 10 | use tokio::stream::StreamExt; 11 | use tokio::sync::mpsc; 12 | use tokio::sync::mpsc::UnboundedReceiver; 13 | use tokio::sync::mpsc::UnboundedSender; 14 | use tungstenite::Message; 15 | 16 | struct InfoRoom { 17 | r: proto_all::Room, 18 | sender: UnboundedSender, 19 | } 20 | 21 | pub struct Lobby { 22 | connections: HashMap, 23 | max_client: u32, 24 | connection_indices: u32, 25 | connection_index_pool: Vec, 26 | rooms: HashMap, 27 | passwords: HashMap, 28 | room_indices: u32, 29 | room_index_pool: Vec, 30 | } 31 | 32 | impl Lobby { 33 | pub fn new() -> Self { 34 | Self { 35 | connections: HashMap::new(), 36 | max_client: 100, 37 | connection_indices: 0, 38 | connection_index_pool: Vec::new(), 39 | rooms: HashMap::new(), 40 | passwords: HashMap::new(), 41 | room_indices: 0, 42 | room_index_pool: Vec::new(), 43 | } 44 | } 45 | 46 | //temporary poor mans regex 47 | fn incorrect_name(name: String, len: usize) -> bool { 48 | if name.len() < 3 || name.len() > len { 49 | return true; 50 | } 51 | for ch in name.chars() { 52 | if ch == '<' || ch == '>' || ch == '+' || ch == '&' || ch == '%' || ch == '=' { 53 | return true; 54 | } 55 | } 56 | 57 | false 58 | } 59 | 60 | pub fn add_room(&mut self, name: String) -> Result { 61 | let id = if !self.room_index_pool.is_empty() { 62 | self.room_index_pool.pop().unwrap() 63 | } else if self.room_indices < self.max_client { 64 | self.room_indices += 1; 65 | self.room_indices 66 | } else { 67 | 0 68 | }; 69 | 70 | if id == 0 { 71 | return Err(Error { 72 | title: "Max room count exceed!".to_string(), 73 | message: "Cannot create any more room, try again later.".to_string(), 74 | }); 75 | } else if Self::incorrect_name(name, 100) { 76 | return Err(Error { 77 | title: "Room name is not suitable".to_string(), 78 | message: "Room name is not suitable, please try another one.".to_string(), 79 | }); 80 | } 81 | 82 | Ok(id) 83 | } 84 | 85 | fn join_to_room(&mut self, join_room: JoinRoom) -> Result, Error> { 86 | if let Some(room) = self.rooms.get_mut(&join_room.id) { 87 | if let Some(pass) = self.passwords.get(&join_room.id) { 88 | if pass == &join_room.password { 89 | if room.r.players < 6 { 90 | room.r.players += 1; 91 | Ok(room.sender.clone()) 92 | } else { 93 | Err(Error { 94 | title: "Room is full.".to_string(), 95 | message: "Maximum player count reached.".to_string(), 96 | }) 97 | } 98 | } else { 99 | Err(Error { 100 | title: "Incorrect room password.".to_string(), 101 | message: "Incorrect password, try again.".to_string(), 102 | }) 103 | } 104 | } else if room.r.players < 6 { 105 | room.r.players += 1; 106 | Ok(room.sender.clone()) 107 | } else { 108 | Err(Error { 109 | title: "Room is full.".to_string(), 110 | message: "Maximum player count reached.".to_string(), 111 | }) 112 | } 113 | } else { 114 | Err(Error { 115 | title: "Room cannot be find.".to_string(), 116 | message: "Room is no longer valid.".to_string(), 117 | }) 118 | } 119 | } 120 | 121 | pub fn add_connection(&mut self, name: String) -> Result { 122 | let id = if !self.connection_index_pool.is_empty() { 123 | self.connection_index_pool.pop().unwrap() 124 | } else if self.connection_indices < self.max_client { 125 | self.connection_indices += 1; 126 | self.connection_indices 127 | } else { 128 | 0 129 | }; 130 | 131 | if id == 0 { 132 | return Err(Error { 133 | title: "Server is full".to_string(), 134 | message: "Server is currently full, try again later.".to_string(), 135 | }); 136 | } else if Self::incorrect_name(name, 20) { 137 | return Err(Error { 138 | title: "Username is not suitable.".to_string(), 139 | message: "Your username is not suitable, please try another one.".to_string(), 140 | }); 141 | } 142 | 143 | Ok(id) 144 | } 145 | 146 | fn serialize_error(err: Error) -> Vec { 147 | let mut out = Vec::new(); 148 | let mut writer = Writer::new(&mut out); 149 | writer.write_message(&err).expect("Cannot serialize chat"); 150 | out[0] = SendHeader::ERROR; 151 | out 152 | } 153 | 154 | fn serialize_chat(&self, id: u32, mut chat: Chat) -> Vec { 155 | let conn = self.connections.get(&id).unwrap(); 156 | 157 | chat.name = format!("({}) {}", id, conn.name); 158 | 159 | let mut out = Vec::new(); 160 | let mut writer = Writer::new(&mut out); 161 | writer.write_message(&chat).expect("Cannot serialize chat"); 162 | out[0] = SendHeader::LOBBY_CHAT; 163 | out 164 | } 165 | 166 | fn serialize_users(users: Users) -> Vec { 167 | let mut out = Vec::new(); 168 | let mut writer = Writer::new(&mut out); 169 | writer 170 | .write_message(&users) 171 | .expect("Cannot serialize lobby"); 172 | out[0] = SendHeader::USERS; 173 | out 174 | } 175 | 176 | fn serialize_rooms(&self) -> Vec { 177 | let mut rooms = Rooms { rooms: Vec::new() }; 178 | 179 | for room in self.rooms.values() { 180 | rooms.rooms.push(room.r.clone()); 181 | } 182 | 183 | let mut out = Vec::new(); 184 | let mut writer = Writer::new(&mut out); 185 | writer 186 | .write_message(&rooms) 187 | .expect("Cannot serialize lobby"); 188 | out[0] = SendHeader::ROOMS; 189 | out 190 | } 191 | 192 | pub async fn listen( 193 | to_lobby: UnboundedSender, 194 | mut receiver: UnboundedReceiver, 195 | ) { 196 | let mut lobby = Self::new(); 197 | while let Some(event) = receiver.next().await { 198 | match event { 199 | LobbyEvents::Handshake(tx, mut conn) => { 200 | match lobby.add_connection(conn.name.clone()) { 201 | Ok(id) => { 202 | conn.id = id; 203 | lobby.connections.insert(id, conn); 204 | 205 | if tx.send(id).is_ok() { 206 | lobby.broadcast_lobby_info().await; 207 | } else { 208 | lobby.connections.remove(&id).unwrap(); 209 | } 210 | } 211 | Err(err) => { 212 | let _ = conn 213 | .sender 214 | .send(Message::Binary(Self::serialize_error(err))) 215 | .await; 216 | } 217 | } 218 | } 219 | LobbyEvents::PlayerCount(room_id, user_id, len) => { 220 | lobby.connection_index_pool.push(user_id); 221 | if len == 0 { 222 | lobby.rooms.remove(&room_id).unwrap(); 223 | let _ = lobby.passwords.remove(&room_id); 224 | lobby.room_index_pool.push(room_id); 225 | } else { 226 | lobby.rooms.get_mut(&room_id).unwrap().r.players = len as u32; 227 | } 228 | lobby.broadcast(lobby.serialize_rooms()).await; 229 | } 230 | LobbyEvents::TakeBack(tx, conn) => { 231 | lobby.connections.insert(conn.id, conn); 232 | tx.send(()).unwrap(); 233 | lobby.broadcast_users().await; 234 | } 235 | LobbyEvents::Disconnect(id) => { 236 | lobby.connections.remove(&id).unwrap(); 237 | lobby.connection_index_pool.push(id); 238 | lobby.broadcast_users().await; 239 | println!("Connection lost {}", id); 240 | } 241 | LobbyEvents::CreateRoom(user_id, tx, create_room) => { 242 | match lobby.add_room(create_room.name.clone()) { 243 | Ok(room_id) => { 244 | let password = !create_room.password.is_empty(); 245 | let room = proto_all::Room { 246 | id: room_id, 247 | name: create_room.name, 248 | password, 249 | players: 1, 250 | }; 251 | 252 | let (game_sender, game_receiver) = 253 | mpsc::unbounded_channel::(); 254 | 255 | if tx.send(game_sender.clone()).is_err() { 256 | continue; 257 | } 258 | 259 | if password { 260 | lobby.passwords.insert(room_id, create_room.password); 261 | } 262 | lobby.rooms.insert( 263 | room_id, 264 | InfoRoom { 265 | r: room, 266 | sender: game_sender.clone(), 267 | }, 268 | ); 269 | 270 | tokio::spawn(Room::listen( 271 | room_id, 272 | lobby.connections.remove(&user_id).unwrap(), 273 | game_receiver, 274 | to_lobby.clone(), 275 | )); 276 | 277 | lobby.broadcast_lobby_info().await; 278 | } 279 | Err(err) => { 280 | lobby.send_to(user_id, Self::serialize_error(err)).await; 281 | } 282 | } 283 | } 284 | LobbyEvents::JoinRoom(user_id, tx, join_room) => { 285 | match lobby.join_to_room(join_room) { 286 | Ok(game_sender) => { 287 | if tx.send(game_sender.clone()).is_err() { 288 | continue; 289 | } 290 | game_sender 291 | .send(GameEvents::Join( 292 | lobby.connections.remove(&user_id).unwrap(), 293 | )) 294 | .unwrap(); 295 | } 296 | Err(err) => { 297 | lobby.send_to(user_id, Self::serialize_error(err)).await; 298 | } 299 | } 300 | } 301 | LobbyEvents::Chat(id, chat) => { 302 | let data = lobby.serialize_chat(id, chat); 303 | lobby.broadcast(data).await; 304 | } 305 | } 306 | } 307 | } 308 | 309 | async fn broadcast_lobby_info(&mut self) { 310 | let mut users = Vec::new(); 311 | for (id, conn) in self.connections.iter() { 312 | users.push(User { 313 | id: *id, 314 | name: conn.name.clone(), 315 | }); 316 | } 317 | let rooms = self.serialize_rooms(); 318 | 319 | for conn in self.connections.values_mut() { 320 | let u = Users { 321 | users: users.clone(), 322 | me: conn.id, 323 | }; 324 | let _ = conn 325 | .sender 326 | .send(Message::Binary(Self::serialize_users(u))) 327 | .await; 328 | let _ = conn.sender.send(Message::Binary(rooms.clone())).await; 329 | } 330 | } 331 | 332 | async fn broadcast_users(&mut self) { 333 | let mut users = Vec::new(); 334 | for (id, conn) in self.connections.iter() { 335 | users.push(User { 336 | id: *id, 337 | name: conn.name.clone(), 338 | }); 339 | } 340 | for conn in self.connections.values_mut() { 341 | let u = Users { 342 | users: users.clone(), 343 | me: conn.id, 344 | }; 345 | let _ = conn 346 | .sender 347 | .send(Message::Binary(Self::serialize_users(u))) 348 | .await; 349 | } 350 | } 351 | 352 | async fn send_to(&mut self, id: u32, data: Vec) { 353 | let _ = self 354 | .connections 355 | .get_mut(&id) 356 | .unwrap() 357 | .sender 358 | .send(Message::Binary(data)) 359 | .await; 360 | } 361 | 362 | async fn broadcast(&mut self, data: Vec) { 363 | for conn in self.connections.values_mut() { 364 | let _ = conn.sender.send(Message::Binary(data.clone())).await; 365 | } 366 | } 367 | } 368 | -------------------------------------------------------------------------------- /src/proto/proto_all.rs: -------------------------------------------------------------------------------- 1 | // Automatically generated rust module for 'proto-all.proto' file 2 | 3 | #![allow(non_snake_case)] 4 | #![allow(non_upper_case_globals)] 5 | #![allow(non_camel_case_types)] 6 | #![allow(unused_imports)] 7 | #![allow(unknown_lints)] 8 | #![allow(clippy::all)] 9 | #![cfg_attr(rustfmt, rustfmt_skip)] 10 | 11 | 12 | use quick_protobuf::{MessageRead, MessageWrite, BytesReader, Writer, WriterBackend, Result}; 13 | use quick_protobuf::sizeofs::*; 14 | use super::*; 15 | 16 | #[derive(Debug, Default, PartialEq, Clone)] 17 | pub struct Error { 18 | pub title: String, 19 | pub message: String, 20 | } 21 | 22 | impl<'a> MessageRead<'a> for Error { 23 | fn from_reader(r: &mut BytesReader, bytes: &'a [u8]) -> Result { 24 | let mut msg = Self::default(); 25 | while !r.is_eof() { 26 | match r.next_tag(bytes) { 27 | Ok(10) => msg.title = r.read_string(bytes)?.to_owned(), 28 | Ok(18) => msg.message = r.read_string(bytes)?.to_owned(), 29 | Ok(t) => { r.read_unknown(bytes, t)?; } 30 | Err(e) => return Err(e), 31 | } 32 | } 33 | Ok(msg) 34 | } 35 | } 36 | 37 | impl MessageWrite for Error { 38 | fn get_size(&self) -> usize { 39 | 0 40 | + if self.title == String::default() { 0 } else { 1 + sizeof_len((&self.title).len()) } 41 | + if self.message == String::default() { 0 } else { 1 + sizeof_len((&self.message).len()) } 42 | } 43 | 44 | fn write_message(&self, w: &mut Writer) -> Result<()> { 45 | if self.title != String::default() { w.write_with_tag(10, |w| w.write_string(&**&self.title))?; } 46 | if self.message != String::default() { w.write_with_tag(18, |w| w.write_string(&**&self.message))?; } 47 | Ok(()) 48 | } 49 | } 50 | 51 | #[derive(Debug, Default, PartialEq, Clone)] 52 | pub struct User { 53 | pub id: u32, 54 | pub name: String, 55 | } 56 | 57 | impl<'a> MessageRead<'a> for User { 58 | fn from_reader(r: &mut BytesReader, bytes: &'a [u8]) -> Result { 59 | let mut msg = Self::default(); 60 | while !r.is_eof() { 61 | match r.next_tag(bytes) { 62 | Ok(8) => msg.id = r.read_uint32(bytes)?, 63 | Ok(18) => msg.name = r.read_string(bytes)?.to_owned(), 64 | Ok(t) => { r.read_unknown(bytes, t)?; } 65 | Err(e) => return Err(e), 66 | } 67 | } 68 | Ok(msg) 69 | } 70 | } 71 | 72 | impl MessageWrite for User { 73 | fn get_size(&self) -> usize { 74 | 0 75 | + if self.id == 0u32 { 0 } else { 1 + sizeof_varint(*(&self.id) as u64) } 76 | + if self.name == String::default() { 0 } else { 1 + sizeof_len((&self.name).len()) } 77 | } 78 | 79 | fn write_message(&self, w: &mut Writer) -> Result<()> { 80 | if self.id != 0u32 { w.write_with_tag(8, |w| w.write_uint32(*&self.id))?; } 81 | if self.name != String::default() { w.write_with_tag(18, |w| w.write_string(&**&self.name))?; } 82 | Ok(()) 83 | } 84 | } 85 | 86 | #[derive(Debug, Default, PartialEq, Clone)] 87 | pub struct Room { 88 | pub id: u32, 89 | pub name: String, 90 | pub password: bool, 91 | pub players: u32, 92 | } 93 | 94 | impl<'a> MessageRead<'a> for Room { 95 | fn from_reader(r: &mut BytesReader, bytes: &'a [u8]) -> Result { 96 | let mut msg = Self::default(); 97 | while !r.is_eof() { 98 | match r.next_tag(bytes) { 99 | Ok(8) => msg.id = r.read_uint32(bytes)?, 100 | Ok(18) => msg.name = r.read_string(bytes)?.to_owned(), 101 | Ok(24) => msg.password = r.read_bool(bytes)?, 102 | Ok(32) => msg.players = r.read_uint32(bytes)?, 103 | Ok(t) => { r.read_unknown(bytes, t)?; } 104 | Err(e) => return Err(e), 105 | } 106 | } 107 | Ok(msg) 108 | } 109 | } 110 | 111 | impl MessageWrite for Room { 112 | fn get_size(&self) -> usize { 113 | 0 114 | + if self.id == 0u32 { 0 } else { 1 + sizeof_varint(*(&self.id) as u64) } 115 | + if self.name == String::default() { 0 } else { 1 + sizeof_len((&self.name).len()) } 116 | + if self.password == false { 0 } else { 1 + sizeof_varint(*(&self.password) as u64) } 117 | + if self.players == 0u32 { 0 } else { 1 + sizeof_varint(*(&self.players) as u64) } 118 | } 119 | 120 | fn write_message(&self, w: &mut Writer) -> Result<()> { 121 | if self.id != 0u32 { w.write_with_tag(8, |w| w.write_uint32(*&self.id))?; } 122 | if self.name != String::default() { w.write_with_tag(18, |w| w.write_string(&**&self.name))?; } 123 | if self.password != false { w.write_with_tag(24, |w| w.write_bool(*&self.password))?; } 124 | if self.players != 0u32 { w.write_with_tag(32, |w| w.write_uint32(*&self.players))?; } 125 | Ok(()) 126 | } 127 | } 128 | 129 | #[derive(Debug, Default, PartialEq, Clone)] 130 | pub struct Handshake { 131 | pub name: String, 132 | } 133 | 134 | impl<'a> MessageRead<'a> for Handshake { 135 | fn from_reader(r: &mut BytesReader, bytes: &'a [u8]) -> Result { 136 | let mut msg = Self::default(); 137 | while !r.is_eof() { 138 | match r.next_tag(bytes) { 139 | Ok(10) => msg.name = r.read_string(bytes)?.to_owned(), 140 | Ok(t) => { r.read_unknown(bytes, t)?; } 141 | Err(e) => return Err(e), 142 | } 143 | } 144 | Ok(msg) 145 | } 146 | } 147 | 148 | impl MessageWrite for Handshake { 149 | fn get_size(&self) -> usize { 150 | 0 151 | + if self.name == String::default() { 0 } else { 1 + sizeof_len((&self.name).len()) } 152 | } 153 | 154 | fn write_message(&self, w: &mut Writer) -> Result<()> { 155 | if self.name != String::default() { w.write_with_tag(10, |w| w.write_string(&**&self.name))?; } 156 | Ok(()) 157 | } 158 | } 159 | 160 | #[derive(Debug, Default, PartialEq, Clone)] 161 | pub struct Rooms { 162 | pub rooms: Vec, 163 | } 164 | 165 | impl<'a> MessageRead<'a> for Rooms { 166 | fn from_reader(r: &mut BytesReader, bytes: &'a [u8]) -> Result { 167 | let mut msg = Self::default(); 168 | while !r.is_eof() { 169 | match r.next_tag(bytes) { 170 | Ok(10) => msg.rooms.push(r.read_message::(bytes)?), 171 | Ok(t) => { r.read_unknown(bytes, t)?; } 172 | Err(e) => return Err(e), 173 | } 174 | } 175 | Ok(msg) 176 | } 177 | } 178 | 179 | impl MessageWrite for Rooms { 180 | fn get_size(&self) -> usize { 181 | 0 182 | + self.rooms.iter().map(|s| 1 + sizeof_len((s).get_size())).sum::() 183 | } 184 | 185 | fn write_message(&self, w: &mut Writer) -> Result<()> { 186 | for s in &self.rooms { w.write_with_tag(10, |w| w.write_message(s))?; } 187 | Ok(()) 188 | } 189 | } 190 | 191 | #[derive(Debug, Default, PartialEq, Clone)] 192 | pub struct Users { 193 | pub users: Vec, 194 | pub me: u32, 195 | } 196 | 197 | impl<'a> MessageRead<'a> for Users { 198 | fn from_reader(r: &mut BytesReader, bytes: &'a [u8]) -> Result { 199 | let mut msg = Self::default(); 200 | while !r.is_eof() { 201 | match r.next_tag(bytes) { 202 | Ok(10) => msg.users.push(r.read_message::(bytes)?), 203 | Ok(16) => msg.me = r.read_uint32(bytes)?, 204 | Ok(t) => { r.read_unknown(bytes, t)?; } 205 | Err(e) => return Err(e), 206 | } 207 | } 208 | Ok(msg) 209 | } 210 | } 211 | 212 | impl MessageWrite for Users { 213 | fn get_size(&self) -> usize { 214 | 0 215 | + self.users.iter().map(|s| 1 + sizeof_len((s).get_size())).sum::() 216 | + if self.me == 0u32 { 0 } else { 1 + sizeof_varint(*(&self.me) as u64) } 217 | } 218 | 219 | fn write_message(&self, w: &mut Writer) -> Result<()> { 220 | for s in &self.users { w.write_with_tag(10, |w| w.write_message(s))?; } 221 | if self.me != 0u32 { w.write_with_tag(16, |w| w.write_uint32(*&self.me))?; } 222 | Ok(()) 223 | } 224 | } 225 | 226 | #[derive(Debug, Default, PartialEq, Clone)] 227 | pub struct CreateRoom { 228 | pub name: String, 229 | pub password: String, 230 | } 231 | 232 | impl<'a> MessageRead<'a> for CreateRoom { 233 | fn from_reader(r: &mut BytesReader, bytes: &'a [u8]) -> Result { 234 | let mut msg = Self::default(); 235 | while !r.is_eof() { 236 | match r.next_tag(bytes) { 237 | Ok(10) => msg.name = r.read_string(bytes)?.to_owned(), 238 | Ok(18) => msg.password = r.read_string(bytes)?.to_owned(), 239 | Ok(t) => { r.read_unknown(bytes, t)?; } 240 | Err(e) => return Err(e), 241 | } 242 | } 243 | Ok(msg) 244 | } 245 | } 246 | 247 | impl MessageWrite for CreateRoom { 248 | fn get_size(&self) -> usize { 249 | 0 250 | + if self.name == String::default() { 0 } else { 1 + sizeof_len((&self.name).len()) } 251 | + if self.password == String::default() { 0 } else { 1 + sizeof_len((&self.password).len()) } 252 | } 253 | 254 | fn write_message(&self, w: &mut Writer) -> Result<()> { 255 | if self.name != String::default() { w.write_with_tag(10, |w| w.write_string(&**&self.name))?; } 256 | if self.password != String::default() { w.write_with_tag(18, |w| w.write_string(&**&self.password))?; } 257 | Ok(()) 258 | } 259 | } 260 | 261 | #[derive(Debug, Default, PartialEq, Clone)] 262 | pub struct JoinRoom { 263 | pub id: u32, 264 | pub password: String, 265 | } 266 | 267 | impl<'a> MessageRead<'a> for JoinRoom { 268 | fn from_reader(r: &mut BytesReader, bytes: &'a [u8]) -> Result { 269 | let mut msg = Self::default(); 270 | while !r.is_eof() { 271 | match r.next_tag(bytes) { 272 | Ok(8) => msg.id = r.read_uint32(bytes)?, 273 | Ok(18) => msg.password = r.read_string(bytes)?.to_owned(), 274 | Ok(t) => { r.read_unknown(bytes, t)?; } 275 | Err(e) => return Err(e), 276 | } 277 | } 278 | Ok(msg) 279 | } 280 | } 281 | 282 | impl MessageWrite for JoinRoom { 283 | fn get_size(&self) -> usize { 284 | 0 285 | + if self.id == 0u32 { 0 } else { 1 + sizeof_varint(*(&self.id) as u64) } 286 | + if self.password == String::default() { 0 } else { 1 + sizeof_len((&self.password).len()) } 287 | } 288 | 289 | fn write_message(&self, w: &mut Writer) -> Result<()> { 290 | if self.id != 0u32 { w.write_with_tag(8, |w| w.write_uint32(*&self.id))?; } 291 | if self.password != String::default() { w.write_with_tag(18, |w| w.write_string(&**&self.password))?; } 292 | Ok(()) 293 | } 294 | } 295 | 296 | #[derive(Debug, Default, PartialEq, Clone)] 297 | pub struct Chat { 298 | pub name: String, 299 | pub message: String, 300 | } 301 | 302 | impl<'a> MessageRead<'a> for Chat { 303 | fn from_reader(r: &mut BytesReader, bytes: &'a [u8]) -> Result { 304 | let mut msg = Self::default(); 305 | while !r.is_eof() { 306 | match r.next_tag(bytes) { 307 | Ok(10) => msg.name = r.read_string(bytes)?.to_owned(), 308 | Ok(18) => msg.message = r.read_string(bytes)?.to_owned(), 309 | Ok(t) => { r.read_unknown(bytes, t)?; } 310 | Err(e) => return Err(e), 311 | } 312 | } 313 | Ok(msg) 314 | } 315 | } 316 | 317 | impl MessageWrite for Chat { 318 | fn get_size(&self) -> usize { 319 | 0 320 | + if self.name == String::default() { 0 } else { 1 + sizeof_len((&self.name).len()) } 321 | + if self.message == String::default() { 0 } else { 1 + sizeof_len((&self.message).len()) } 322 | } 323 | 324 | fn write_message(&self, w: &mut Writer) -> Result<()> { 325 | if self.name != String::default() { w.write_with_tag(10, |w| w.write_string(&**&self.name))?; } 326 | if self.message != String::default() { w.write_with_tag(18, |w| w.write_string(&**&self.message))?; } 327 | Ok(()) 328 | } 329 | } 330 | 331 | #[derive(Debug, Default, PartialEq, Clone)] 332 | pub struct QuitLobby { } 333 | 334 | impl<'a> MessageRead<'a> for QuitLobby { 335 | fn from_reader(r: &mut BytesReader, _: &[u8]) -> Result { 336 | r.read_to_end(); 337 | Ok(Self::default()) 338 | } 339 | } 340 | 341 | impl MessageWrite for QuitLobby { } 342 | 343 | #[derive(Debug, Default, PartialEq, Clone)] 344 | pub struct Entity { 345 | pub id: u32, 346 | pub x: f32, 347 | pub y: f32, 348 | pub angle: f32, 349 | } 350 | 351 | impl<'a> MessageRead<'a> for Entity { 352 | fn from_reader(r: &mut BytesReader, bytes: &'a [u8]) -> Result { 353 | let mut msg = Self::default(); 354 | while !r.is_eof() { 355 | match r.next_tag(bytes) { 356 | Ok(8) => msg.id = r.read_uint32(bytes)?, 357 | Ok(21) => msg.x = r.read_float(bytes)?, 358 | Ok(29) => msg.y = r.read_float(bytes)?, 359 | Ok(37) => msg.angle = r.read_float(bytes)?, 360 | Ok(t) => { r.read_unknown(bytes, t)?; } 361 | Err(e) => return Err(e), 362 | } 363 | } 364 | Ok(msg) 365 | } 366 | } 367 | 368 | impl MessageWrite for Entity { 369 | fn get_size(&self) -> usize { 370 | 0 371 | + if self.id == 0u32 { 0 } else { 1 + sizeof_varint(*(&self.id) as u64) } 372 | + if self.x == 0f32 { 0 } else { 1 + 4 } 373 | + if self.y == 0f32 { 0 } else { 1 + 4 } 374 | + if self.angle == 0f32 { 0 } else { 1 + 4 } 375 | } 376 | 377 | fn write_message(&self, w: &mut Writer) -> Result<()> { 378 | if self.id != 0u32 { w.write_with_tag(8, |w| w.write_uint32(*&self.id))?; } 379 | if self.x != 0f32 { w.write_with_tag(21, |w| w.write_float(*&self.x))?; } 380 | if self.y != 0f32 { w.write_with_tag(29, |w| w.write_float(*&self.y))?; } 381 | if self.angle != 0f32 { w.write_with_tag(37, |w| w.write_float(*&self.angle))?; } 382 | Ok(()) 383 | } 384 | } 385 | 386 | #[derive(Debug, Default, PartialEq, Clone)] 387 | pub struct State { 388 | pub last_seq: u32, 389 | pub entities: Vec, 390 | } 391 | 392 | impl<'a> MessageRead<'a> for State { 393 | fn from_reader(r: &mut BytesReader, bytes: &'a [u8]) -> Result { 394 | let mut msg = Self::default(); 395 | while !r.is_eof() { 396 | match r.next_tag(bytes) { 397 | Ok(8) => msg.last_seq = r.read_uint32(bytes)?, 398 | Ok(18) => msg.entities.push(r.read_message::(bytes)?), 399 | Ok(t) => { r.read_unknown(bytes, t)?; } 400 | Err(e) => return Err(e), 401 | } 402 | } 403 | Ok(msg) 404 | } 405 | } 406 | 407 | impl MessageWrite for State { 408 | fn get_size(&self) -> usize { 409 | 0 410 | + if self.last_seq == 0u32 { 0 } else { 1 + sizeof_varint(*(&self.last_seq) as u64) } 411 | + self.entities.iter().map(|s| 1 + sizeof_len((s).get_size())).sum::() 412 | } 413 | 414 | fn write_message(&self, w: &mut Writer) -> Result<()> { 415 | if self.last_seq != 0u32 { w.write_with_tag(8, |w| w.write_uint32(*&self.last_seq))?; } 416 | for s in &self.entities { w.write_with_tag(18, |w| w.write_message(s))?; } 417 | Ok(()) 418 | } 419 | } 420 | 421 | #[derive(Debug, Default, PartialEq, Clone)] 422 | pub struct GameInput { 423 | pub horizontalPress: f32, 424 | pub verticalPress: f32, 425 | pub angle: f32, 426 | pub sequence: u32, 427 | } 428 | 429 | impl<'a> MessageRead<'a> for GameInput { 430 | fn from_reader(r: &mut BytesReader, bytes: &'a [u8]) -> Result { 431 | let mut msg = Self::default(); 432 | while !r.is_eof() { 433 | match r.next_tag(bytes) { 434 | Ok(13) => msg.horizontalPress = r.read_float(bytes)?, 435 | Ok(21) => msg.verticalPress = r.read_float(bytes)?, 436 | Ok(29) => msg.angle = r.read_float(bytes)?, 437 | Ok(32) => msg.sequence = r.read_uint32(bytes)?, 438 | Ok(t) => { r.read_unknown(bytes, t)?; } 439 | Err(e) => return Err(e), 440 | } 441 | } 442 | Ok(msg) 443 | } 444 | } 445 | 446 | impl MessageWrite for GameInput { 447 | fn get_size(&self) -> usize { 448 | 0 449 | + if self.horizontalPress == 0f32 { 0 } else { 1 + 4 } 450 | + if self.verticalPress == 0f32 { 0 } else { 1 + 4 } 451 | + if self.angle == 0f32 { 0 } else { 1 + 4 } 452 | + if self.sequence == 0u32 { 0 } else { 1 + sizeof_varint(*(&self.sequence) as u64) } 453 | } 454 | 455 | fn write_message(&self, w: &mut Writer) -> Result<()> { 456 | if self.horizontalPress != 0f32 { w.write_with_tag(13, |w| w.write_float(*&self.horizontalPress))?; } 457 | if self.verticalPress != 0f32 { w.write_with_tag(21, |w| w.write_float(*&self.verticalPress))?; } 458 | if self.angle != 0f32 { w.write_with_tag(29, |w| w.write_float(*&self.angle))?; } 459 | if self.sequence != 0u32 { w.write_with_tag(32, |w| w.write_uint32(*&self.sequence))?; } 460 | Ok(()) 461 | } 462 | } 463 | 464 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | [[package]] 4 | name = "ahash" 5 | version = "0.3.8" 6 | source = "registry+https://github.com/rust-lang/crates.io-index" 7 | checksum = "e8fd72866655d1904d6b0997d0b07ba561047d070fbe29de039031c641b61217" 8 | 9 | [[package]] 10 | name = "arrayvec" 11 | version = "0.5.1" 12 | source = "registry+https://github.com/rust-lang/crates.io-index" 13 | checksum = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8" 14 | 15 | [[package]] 16 | name = "atom" 17 | version = "0.3.5" 18 | source = "registry+https://github.com/rust-lang/crates.io-index" 19 | checksum = "3c86699c3f02778ec07158376991c8f783dd1f2f95c579ffaf0738dc984b2fe2" 20 | 21 | [[package]] 22 | name = "autocfg" 23 | version = "1.0.1" 24 | source = "registry+https://github.com/rust-lang/crates.io-index" 25 | checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" 26 | 27 | [[package]] 28 | name = "base64" 29 | version = "0.12.3" 30 | source = "registry+https://github.com/rust-lang/crates.io-index" 31 | checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff" 32 | 33 | [[package]] 34 | name = "bitflags" 35 | version = "1.2.1" 36 | source = "registry+https://github.com/rust-lang/crates.io-index" 37 | checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" 38 | 39 | [[package]] 40 | name = "block-buffer" 41 | version = "0.9.0" 42 | source = "registry+https://github.com/rust-lang/crates.io-index" 43 | checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" 44 | dependencies = [ 45 | "generic-array", 46 | ] 47 | 48 | [[package]] 49 | name = "byteorder" 50 | version = "1.3.4" 51 | source = "registry+https://github.com/rust-lang/crates.io-index" 52 | checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" 53 | 54 | [[package]] 55 | name = "bytes" 56 | version = "0.5.6" 57 | source = "registry+https://github.com/rust-lang/crates.io-index" 58 | checksum = "0e4cec68f03f32e44924783795810fa50a7035d8c8ebe78580ad7e6c703fba38" 59 | 60 | [[package]] 61 | name = "cfg-if" 62 | version = "0.1.10" 63 | source = "registry+https://github.com/rust-lang/crates.io-index" 64 | checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" 65 | 66 | [[package]] 67 | name = "cpuid-bool" 68 | version = "0.1.2" 69 | source = "registry+https://github.com/rust-lang/crates.io-index" 70 | checksum = "8aebca1129a03dc6dc2b127edd729435bbc4a37e1d5f4d7513165089ceb02634" 71 | 72 | [[package]] 73 | name = "crossbeam-channel" 74 | version = "0.4.3" 75 | source = "registry+https://github.com/rust-lang/crates.io-index" 76 | checksum = "09ee0cc8804d5393478d743b035099520087a5186f3b93fa58cec08fa62407b6" 77 | dependencies = [ 78 | "cfg-if", 79 | "crossbeam-utils", 80 | ] 81 | 82 | [[package]] 83 | name = "crossbeam-deque" 84 | version = "0.7.3" 85 | source = "registry+https://github.com/rust-lang/crates.io-index" 86 | checksum = "9f02af974daeee82218205558e51ec8768b48cf524bd01d550abe5573a608285" 87 | dependencies = [ 88 | "crossbeam-epoch", 89 | "crossbeam-utils", 90 | "maybe-uninit", 91 | ] 92 | 93 | [[package]] 94 | name = "crossbeam-epoch" 95 | version = "0.8.2" 96 | source = "registry+https://github.com/rust-lang/crates.io-index" 97 | checksum = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace" 98 | dependencies = [ 99 | "autocfg", 100 | "cfg-if", 101 | "crossbeam-utils", 102 | "lazy_static", 103 | "maybe-uninit", 104 | "memoffset", 105 | "scopeguard", 106 | ] 107 | 108 | [[package]] 109 | name = "crossbeam-queue" 110 | version = "0.2.3" 111 | source = "registry+https://github.com/rust-lang/crates.io-index" 112 | checksum = "774ba60a54c213d409d5353bda12d49cd68d14e45036a285234c8d6f91f92570" 113 | dependencies = [ 114 | "cfg-if", 115 | "crossbeam-utils", 116 | "maybe-uninit", 117 | ] 118 | 119 | [[package]] 120 | name = "crossbeam-utils" 121 | version = "0.7.2" 122 | source = "registry+https://github.com/rust-lang/crates.io-index" 123 | checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" 124 | dependencies = [ 125 | "autocfg", 126 | "cfg-if", 127 | "lazy_static", 128 | ] 129 | 130 | [[package]] 131 | name = "digest" 132 | version = "0.9.0" 133 | source = "registry+https://github.com/rust-lang/crates.io-index" 134 | checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" 135 | dependencies = [ 136 | "generic-array", 137 | ] 138 | 139 | [[package]] 140 | name = "either" 141 | version = "1.6.0" 142 | source = "registry+https://github.com/rust-lang/crates.io-index" 143 | checksum = "cd56b59865bce947ac5958779cfa508f6c3b9497cc762b7e24a12d11ccde2c4f" 144 | 145 | [[package]] 146 | name = "fnv" 147 | version = "1.0.7" 148 | source = "registry+https://github.com/rust-lang/crates.io-index" 149 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 150 | 151 | [[package]] 152 | name = "fuchsia-zircon" 153 | version = "0.3.3" 154 | source = "registry+https://github.com/rust-lang/crates.io-index" 155 | checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" 156 | dependencies = [ 157 | "bitflags", 158 | "fuchsia-zircon-sys", 159 | ] 160 | 161 | [[package]] 162 | name = "fuchsia-zircon-sys" 163 | version = "0.3.3" 164 | source = "registry+https://github.com/rust-lang/crates.io-index" 165 | checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" 166 | 167 | [[package]] 168 | name = "futures-core" 169 | version = "0.3.5" 170 | source = "registry+https://github.com/rust-lang/crates.io-index" 171 | checksum = "59f5fff90fd5d971f936ad674802482ba441b6f09ba5e15fd8b39145582ca399" 172 | 173 | [[package]] 174 | name = "futures-sink" 175 | version = "0.3.5" 176 | source = "registry+https://github.com/rust-lang/crates.io-index" 177 | checksum = "3f2032893cb734c7a05d85ce0cc8b8c4075278e93b24b66f9de99d6eb0fa8acc" 178 | 179 | [[package]] 180 | name = "futures-task" 181 | version = "0.3.5" 182 | source = "registry+https://github.com/rust-lang/crates.io-index" 183 | checksum = "bdb66b5f09e22019b1ab0830f7785bcea8e7a42148683f99214f73f8ec21a626" 184 | dependencies = [ 185 | "once_cell", 186 | ] 187 | 188 | [[package]] 189 | name = "futures-util" 190 | version = "0.3.5" 191 | source = "registry+https://github.com/rust-lang/crates.io-index" 192 | checksum = "8764574ff08b701a084482c3c7031349104b07ac897393010494beaa18ce32c6" 193 | dependencies = [ 194 | "futures-core", 195 | "futures-sink", 196 | "futures-task", 197 | "pin-project", 198 | "pin-utils", 199 | "slab", 200 | ] 201 | 202 | [[package]] 203 | name = "generic-array" 204 | version = "0.14.4" 205 | source = "registry+https://github.com/rust-lang/crates.io-index" 206 | checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817" 207 | dependencies = [ 208 | "typenum", 209 | "version_check", 210 | ] 211 | 212 | [[package]] 213 | name = "getrandom" 214 | version = "0.1.14" 215 | source = "registry+https://github.com/rust-lang/crates.io-index" 216 | checksum = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb" 217 | dependencies = [ 218 | "cfg-if", 219 | "libc", 220 | "wasi", 221 | ] 222 | 223 | [[package]] 224 | name = "hashbrown" 225 | version = "0.7.2" 226 | source = "registry+https://github.com/rust-lang/crates.io-index" 227 | checksum = "96282e96bfcd3da0d3aa9938bedf1e50df3269b6db08b4876d2da0bb1a0841cf" 228 | dependencies = [ 229 | "ahash", 230 | "autocfg", 231 | ] 232 | 233 | [[package]] 234 | name = "hermit-abi" 235 | version = "0.1.15" 236 | source = "registry+https://github.com/rust-lang/crates.io-index" 237 | checksum = "3deed196b6e7f9e44a2ae8d94225d80302d81208b1bb673fd21fe634645c85a9" 238 | dependencies = [ 239 | "libc", 240 | ] 241 | 242 | [[package]] 243 | name = "hibitset" 244 | version = "0.6.3" 245 | source = "registry+https://github.com/rust-lang/crates.io-index" 246 | checksum = "93a1bb8316a44459a7d14253c4d28dd7395cbd23cc04a68c46e851b8e46d64b1" 247 | dependencies = [ 248 | "atom", 249 | "rayon", 250 | ] 251 | 252 | [[package]] 253 | name = "http" 254 | version = "0.2.1" 255 | source = "registry+https://github.com/rust-lang/crates.io-index" 256 | checksum = "28d569972648b2c512421b5f2a405ad6ac9666547189d0c5477a3f200f3e02f9" 257 | dependencies = [ 258 | "bytes", 259 | "fnv", 260 | "itoa", 261 | ] 262 | 263 | [[package]] 264 | name = "httparse" 265 | version = "1.3.4" 266 | source = "registry+https://github.com/rust-lang/crates.io-index" 267 | checksum = "cd179ae861f0c2e53da70d892f5f3029f9594be0c41dc5269cd371691b1dc2f9" 268 | 269 | [[package]] 270 | name = "idna" 271 | version = "0.2.0" 272 | source = "registry+https://github.com/rust-lang/crates.io-index" 273 | checksum = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9" 274 | dependencies = [ 275 | "matches", 276 | "unicode-bidi", 277 | "unicode-normalization", 278 | ] 279 | 280 | [[package]] 281 | name = "input_buffer" 282 | version = "0.3.1" 283 | source = "registry+https://github.com/rust-lang/crates.io-index" 284 | checksum = "19a8a95243d5a0398cae618ec29477c6e3cb631152be5c19481f80bc71559754" 285 | dependencies = [ 286 | "bytes", 287 | ] 288 | 289 | [[package]] 290 | name = "iovec" 291 | version = "0.1.4" 292 | source = "registry+https://github.com/rust-lang/crates.io-index" 293 | checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" 294 | dependencies = [ 295 | "libc", 296 | ] 297 | 298 | [[package]] 299 | name = "itoa" 300 | version = "0.4.6" 301 | source = "registry+https://github.com/rust-lang/crates.io-index" 302 | checksum = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6" 303 | 304 | [[package]] 305 | name = "kernel32-sys" 306 | version = "0.2.2" 307 | source = "registry+https://github.com/rust-lang/crates.io-index" 308 | checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" 309 | dependencies = [ 310 | "winapi 0.2.8", 311 | "winapi-build", 312 | ] 313 | 314 | [[package]] 315 | name = "lazy_static" 316 | version = "1.4.0" 317 | source = "registry+https://github.com/rust-lang/crates.io-index" 318 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 319 | 320 | [[package]] 321 | name = "libc" 322 | version = "0.2.76" 323 | source = "registry+https://github.com/rust-lang/crates.io-index" 324 | checksum = "755456fae044e6fa1ebbbd1b3e902ae19e73097ed4ed87bb79934a867c007bc3" 325 | 326 | [[package]] 327 | name = "log" 328 | version = "0.4.11" 329 | source = "registry+https://github.com/rust-lang/crates.io-index" 330 | checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b" 331 | dependencies = [ 332 | "cfg-if", 333 | ] 334 | 335 | [[package]] 336 | name = "matches" 337 | version = "0.1.8" 338 | source = "registry+https://github.com/rust-lang/crates.io-index" 339 | checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" 340 | 341 | [[package]] 342 | name = "maybe-uninit" 343 | version = "2.0.0" 344 | source = "registry+https://github.com/rust-lang/crates.io-index" 345 | checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" 346 | 347 | [[package]] 348 | name = "memchr" 349 | version = "2.3.3" 350 | source = "registry+https://github.com/rust-lang/crates.io-index" 351 | checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400" 352 | 353 | [[package]] 354 | name = "memoffset" 355 | version = "0.5.5" 356 | source = "registry+https://github.com/rust-lang/crates.io-index" 357 | checksum = "c198b026e1bbf08a937e94c6c60f9ec4a2267f5b0d2eec9c1b21b061ce2be55f" 358 | dependencies = [ 359 | "autocfg", 360 | ] 361 | 362 | [[package]] 363 | name = "mio" 364 | version = "0.6.22" 365 | source = "registry+https://github.com/rust-lang/crates.io-index" 366 | checksum = "fce347092656428bc8eaf6201042cb551b8d67855af7374542a92a0fbfcac430" 367 | dependencies = [ 368 | "cfg-if", 369 | "fuchsia-zircon", 370 | "fuchsia-zircon-sys", 371 | "iovec", 372 | "kernel32-sys", 373 | "libc", 374 | "log", 375 | "miow", 376 | "net2", 377 | "slab", 378 | "winapi 0.2.8", 379 | ] 380 | 381 | [[package]] 382 | name = "mio-uds" 383 | version = "0.6.8" 384 | source = "registry+https://github.com/rust-lang/crates.io-index" 385 | checksum = "afcb699eb26d4332647cc848492bbc15eafb26f08d0304550d5aa1f612e066f0" 386 | dependencies = [ 387 | "iovec", 388 | "libc", 389 | "mio", 390 | ] 391 | 392 | [[package]] 393 | name = "miow" 394 | version = "0.2.1" 395 | source = "registry+https://github.com/rust-lang/crates.io-index" 396 | checksum = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" 397 | dependencies = [ 398 | "kernel32-sys", 399 | "net2", 400 | "winapi 0.2.8", 401 | "ws2_32-sys", 402 | ] 403 | 404 | [[package]] 405 | name = "mopa" 406 | version = "0.2.2" 407 | source = "registry+https://github.com/rust-lang/crates.io-index" 408 | checksum = "a785740271256c230f57462d3b83e52f998433a7062fc18f96d5999474a9f915" 409 | 410 | [[package]] 411 | name = "net2" 412 | version = "0.2.34" 413 | source = "registry+https://github.com/rust-lang/crates.io-index" 414 | checksum = "2ba7c918ac76704fb42afcbbb43891e72731f3dcca3bef2a19786297baf14af7" 415 | dependencies = [ 416 | "cfg-if", 417 | "libc", 418 | "winapi 0.3.9", 419 | ] 420 | 421 | [[package]] 422 | name = "nightcomes-server" 423 | version = "0.1.0" 424 | dependencies = [ 425 | "futures-util", 426 | "quick-protobuf", 427 | "specs", 428 | "tokio", 429 | "tokio-tungstenite", 430 | "tungstenite", 431 | ] 432 | 433 | [[package]] 434 | name = "nom" 435 | version = "5.1.2" 436 | source = "registry+https://github.com/rust-lang/crates.io-index" 437 | checksum = "ffb4262d26ed83a1c0a33a38fe2bb15797329c85770da05e6b828ddb782627af" 438 | dependencies = [ 439 | "memchr", 440 | "version_check", 441 | ] 442 | 443 | [[package]] 444 | name = "num_cpus" 445 | version = "1.13.0" 446 | source = "registry+https://github.com/rust-lang/crates.io-index" 447 | checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" 448 | dependencies = [ 449 | "hermit-abi", 450 | "libc", 451 | ] 452 | 453 | [[package]] 454 | name = "once_cell" 455 | version = "1.4.1" 456 | source = "registry+https://github.com/rust-lang/crates.io-index" 457 | checksum = "260e51e7efe62b592207e9e13a68e43692a7a279171d6ba57abd208bf23645ad" 458 | 459 | [[package]] 460 | name = "opaque-debug" 461 | version = "0.3.0" 462 | source = "registry+https://github.com/rust-lang/crates.io-index" 463 | checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" 464 | 465 | [[package]] 466 | name = "percent-encoding" 467 | version = "2.1.0" 468 | source = "registry+https://github.com/rust-lang/crates.io-index" 469 | checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" 470 | 471 | [[package]] 472 | name = "pin-project" 473 | version = "0.4.23" 474 | source = "registry+https://github.com/rust-lang/crates.io-index" 475 | checksum = "ca4433fff2ae79342e497d9f8ee990d174071408f28f726d6d83af93e58e48aa" 476 | dependencies = [ 477 | "pin-project-internal", 478 | ] 479 | 480 | [[package]] 481 | name = "pin-project-internal" 482 | version = "0.4.23" 483 | source = "registry+https://github.com/rust-lang/crates.io-index" 484 | checksum = "2c0e815c3ee9a031fdf5af21c10aa17c573c9c6a566328d99e3936c34e36461f" 485 | dependencies = [ 486 | "proc-macro2", 487 | "quote", 488 | "syn", 489 | ] 490 | 491 | [[package]] 492 | name = "pin-project-lite" 493 | version = "0.1.7" 494 | source = "registry+https://github.com/rust-lang/crates.io-index" 495 | checksum = "282adbf10f2698a7a77f8e983a74b2d18176c19a7fd32a45446139ae7b02b715" 496 | 497 | [[package]] 498 | name = "pin-utils" 499 | version = "0.1.0" 500 | source = "registry+https://github.com/rust-lang/crates.io-index" 501 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 502 | 503 | [[package]] 504 | name = "ppv-lite86" 505 | version = "0.2.9" 506 | source = "registry+https://github.com/rust-lang/crates.io-index" 507 | checksum = "c36fa947111f5c62a733b652544dd0016a43ce89619538a8ef92724a6f501a20" 508 | 509 | [[package]] 510 | name = "proc-macro2" 511 | version = "1.0.19" 512 | source = "registry+https://github.com/rust-lang/crates.io-index" 513 | checksum = "04f5f085b5d71e2188cb8271e5da0161ad52c3f227a661a3c135fdf28e258b12" 514 | dependencies = [ 515 | "unicode-xid", 516 | ] 517 | 518 | [[package]] 519 | name = "quick-protobuf" 520 | version = "0.7.0" 521 | source = "registry+https://github.com/rust-lang/crates.io-index" 522 | checksum = "e489d4a83c17ea69b0291630229b5d4c92a94a3bf0165f7f72f506e94cda8b4b" 523 | dependencies = [ 524 | "byteorder", 525 | ] 526 | 527 | [[package]] 528 | name = "quote" 529 | version = "1.0.7" 530 | source = "registry+https://github.com/rust-lang/crates.io-index" 531 | checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37" 532 | dependencies = [ 533 | "proc-macro2", 534 | ] 535 | 536 | [[package]] 537 | name = "rand" 538 | version = "0.7.3" 539 | source = "registry+https://github.com/rust-lang/crates.io-index" 540 | checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" 541 | dependencies = [ 542 | "getrandom", 543 | "libc", 544 | "rand_chacha", 545 | "rand_core", 546 | "rand_hc", 547 | ] 548 | 549 | [[package]] 550 | name = "rand_chacha" 551 | version = "0.2.2" 552 | source = "registry+https://github.com/rust-lang/crates.io-index" 553 | checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" 554 | dependencies = [ 555 | "ppv-lite86", 556 | "rand_core", 557 | ] 558 | 559 | [[package]] 560 | name = "rand_core" 561 | version = "0.5.1" 562 | source = "registry+https://github.com/rust-lang/crates.io-index" 563 | checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" 564 | dependencies = [ 565 | "getrandom", 566 | ] 567 | 568 | [[package]] 569 | name = "rand_hc" 570 | version = "0.2.0" 571 | source = "registry+https://github.com/rust-lang/crates.io-index" 572 | checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" 573 | dependencies = [ 574 | "rand_core", 575 | ] 576 | 577 | [[package]] 578 | name = "rayon" 579 | version = "1.4.0" 580 | source = "registry+https://github.com/rust-lang/crates.io-index" 581 | checksum = "cfd016f0c045ad38b5251be2c9c0ab806917f82da4d36b2a327e5166adad9270" 582 | dependencies = [ 583 | "autocfg", 584 | "crossbeam-deque", 585 | "either", 586 | "rayon-core", 587 | ] 588 | 589 | [[package]] 590 | name = "rayon-core" 591 | version = "1.8.0" 592 | source = "registry+https://github.com/rust-lang/crates.io-index" 593 | checksum = "91739a34c4355b5434ce54c9086c5895604a9c278586d1f1aa95e04f66b525a0" 594 | dependencies = [ 595 | "crossbeam-channel", 596 | "crossbeam-deque", 597 | "crossbeam-utils", 598 | "lazy_static", 599 | "num_cpus", 600 | ] 601 | 602 | [[package]] 603 | name = "scopeguard" 604 | version = "1.1.0" 605 | source = "registry+https://github.com/rust-lang/crates.io-index" 606 | checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" 607 | 608 | [[package]] 609 | name = "sha-1" 610 | version = "0.9.1" 611 | source = "registry+https://github.com/rust-lang/crates.io-index" 612 | checksum = "170a36ea86c864a3f16dd2687712dd6646f7019f301e57537c7f4dc9f5916770" 613 | dependencies = [ 614 | "block-buffer", 615 | "cfg-if", 616 | "cpuid-bool", 617 | "digest", 618 | "opaque-debug", 619 | ] 620 | 621 | [[package]] 622 | name = "shred" 623 | version = "0.10.2" 624 | source = "registry+https://github.com/rust-lang/crates.io-index" 625 | checksum = "c5f08237e667ac94ad20f8878b5943d91a93ccb231428446c57c21c57779016d" 626 | dependencies = [ 627 | "arrayvec", 628 | "hashbrown", 629 | "mopa", 630 | "rayon", 631 | "smallvec", 632 | "tynm", 633 | ] 634 | 635 | [[package]] 636 | name = "shrev" 637 | version = "1.1.1" 638 | source = "registry+https://github.com/rust-lang/crates.io-index" 639 | checksum = "b5752e017e03af9d735b4b069f53b7a7fd90fefafa04d8bd0c25581b0bff437f" 640 | 641 | [[package]] 642 | name = "slab" 643 | version = "0.4.2" 644 | source = "registry+https://github.com/rust-lang/crates.io-index" 645 | checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" 646 | 647 | [[package]] 648 | name = "smallvec" 649 | version = "1.4.2" 650 | source = "registry+https://github.com/rust-lang/crates.io-index" 651 | checksum = "fbee7696b84bbf3d89a1c2eccff0850e3047ed46bfcd2e92c29a2d074d57e252" 652 | 653 | [[package]] 654 | name = "specs" 655 | version = "0.16.1" 656 | source = "registry+https://github.com/rust-lang/crates.io-index" 657 | checksum = "fff28a29366aff703d5da8a7e2c8875dc8453ac1118f842cbc0fa70c7db51240" 658 | dependencies = [ 659 | "crossbeam-queue", 660 | "hashbrown", 661 | "hibitset", 662 | "log", 663 | "rayon", 664 | "shred", 665 | "shrev", 666 | "specs-derive", 667 | "tuple_utils", 668 | ] 669 | 670 | [[package]] 671 | name = "specs-derive" 672 | version = "0.4.1" 673 | source = "registry+https://github.com/rust-lang/crates.io-index" 674 | checksum = "3e23e09360f3d2190fec4222cd9e19d3158d5da948c0d1ea362df617dd103511" 675 | dependencies = [ 676 | "proc-macro2", 677 | "quote", 678 | "syn", 679 | ] 680 | 681 | [[package]] 682 | name = "syn" 683 | version = "1.0.39" 684 | source = "registry+https://github.com/rust-lang/crates.io-index" 685 | checksum = "891d8d6567fe7c7f8835a3a98af4208f3846fba258c1bc3c31d6e506239f11f9" 686 | dependencies = [ 687 | "proc-macro2", 688 | "quote", 689 | "unicode-xid", 690 | ] 691 | 692 | [[package]] 693 | name = "tinyvec" 694 | version = "0.3.4" 695 | source = "registry+https://github.com/rust-lang/crates.io-index" 696 | checksum = "238ce071d267c5710f9d31451efec16c5ee22de34df17cc05e56cbc92e967117" 697 | 698 | [[package]] 699 | name = "tokio" 700 | version = "0.2.22" 701 | source = "registry+https://github.com/rust-lang/crates.io-index" 702 | checksum = "5d34ca54d84bf2b5b4d7d31e901a8464f7b60ac145a284fba25ceb801f2ddccd" 703 | dependencies = [ 704 | "bytes", 705 | "fnv", 706 | "futures-core", 707 | "iovec", 708 | "lazy_static", 709 | "libc", 710 | "memchr", 711 | "mio", 712 | "mio-uds", 713 | "pin-project-lite", 714 | "slab", 715 | "tokio-macros", 716 | ] 717 | 718 | [[package]] 719 | name = "tokio-macros" 720 | version = "0.2.5" 721 | source = "registry+https://github.com/rust-lang/crates.io-index" 722 | checksum = "f0c3acc6aa564495a0f2e1d59fab677cd7f81a19994cfc7f3ad0e64301560389" 723 | dependencies = [ 724 | "proc-macro2", 725 | "quote", 726 | "syn", 727 | ] 728 | 729 | [[package]] 730 | name = "tokio-tungstenite" 731 | version = "0.11.0" 732 | source = "registry+https://github.com/rust-lang/crates.io-index" 733 | checksum = "6d9e878ad426ca286e4dcae09cbd4e1973a7f8987d97570e2469703dd7f5720c" 734 | dependencies = [ 735 | "futures-util", 736 | "log", 737 | "pin-project", 738 | "tokio", 739 | "tungstenite", 740 | ] 741 | 742 | [[package]] 743 | name = "tungstenite" 744 | version = "0.11.1" 745 | source = "registry+https://github.com/rust-lang/crates.io-index" 746 | checksum = "f0308d80d86700c5878b9ef6321f020f29b1bb9d5ff3cab25e75e23f3a492a23" 747 | dependencies = [ 748 | "base64", 749 | "byteorder", 750 | "bytes", 751 | "http", 752 | "httparse", 753 | "input_buffer", 754 | "log", 755 | "rand", 756 | "sha-1", 757 | "url", 758 | "utf-8", 759 | ] 760 | 761 | [[package]] 762 | name = "tuple_utils" 763 | version = "0.3.0" 764 | source = "registry+https://github.com/rust-lang/crates.io-index" 765 | checksum = "44834418e2c5b16f47bedf35c28e148db099187dd5feee6367fb2525863af4f1" 766 | 767 | [[package]] 768 | name = "tynm" 769 | version = "0.1.4" 770 | source = "registry+https://github.com/rust-lang/crates.io-index" 771 | checksum = "367fb781963961b4a90a3362c54b1871caaecb081f011005778242230f39d34e" 772 | dependencies = [ 773 | "nom", 774 | ] 775 | 776 | [[package]] 777 | name = "typenum" 778 | version = "1.12.0" 779 | source = "registry+https://github.com/rust-lang/crates.io-index" 780 | checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" 781 | 782 | [[package]] 783 | name = "unicode-bidi" 784 | version = "0.3.4" 785 | source = "registry+https://github.com/rust-lang/crates.io-index" 786 | checksum = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" 787 | dependencies = [ 788 | "matches", 789 | ] 790 | 791 | [[package]] 792 | name = "unicode-normalization" 793 | version = "0.1.13" 794 | source = "registry+https://github.com/rust-lang/crates.io-index" 795 | checksum = "6fb19cf769fa8c6a80a162df694621ebeb4dafb606470b2b2fce0be40a98a977" 796 | dependencies = [ 797 | "tinyvec", 798 | ] 799 | 800 | [[package]] 801 | name = "unicode-xid" 802 | version = "0.2.1" 803 | source = "registry+https://github.com/rust-lang/crates.io-index" 804 | checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" 805 | 806 | [[package]] 807 | name = "url" 808 | version = "2.1.1" 809 | source = "registry+https://github.com/rust-lang/crates.io-index" 810 | checksum = "829d4a8476c35c9bf0bbce5a3b23f4106f79728039b726d292bb93bc106787cb" 811 | dependencies = [ 812 | "idna", 813 | "matches", 814 | "percent-encoding", 815 | ] 816 | 817 | [[package]] 818 | name = "utf-8" 819 | version = "0.7.5" 820 | source = "registry+https://github.com/rust-lang/crates.io-index" 821 | checksum = "05e42f7c18b8f902290b009cde6d651262f956c98bc51bca4cd1d511c9cd85c7" 822 | 823 | [[package]] 824 | name = "version_check" 825 | version = "0.9.2" 826 | source = "registry+https://github.com/rust-lang/crates.io-index" 827 | checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" 828 | 829 | [[package]] 830 | name = "wasi" 831 | version = "0.9.0+wasi-snapshot-preview1" 832 | source = "registry+https://github.com/rust-lang/crates.io-index" 833 | checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" 834 | 835 | [[package]] 836 | name = "winapi" 837 | version = "0.2.8" 838 | source = "registry+https://github.com/rust-lang/crates.io-index" 839 | checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" 840 | 841 | [[package]] 842 | name = "winapi" 843 | version = "0.3.9" 844 | source = "registry+https://github.com/rust-lang/crates.io-index" 845 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 846 | dependencies = [ 847 | "winapi-i686-pc-windows-gnu", 848 | "winapi-x86_64-pc-windows-gnu", 849 | ] 850 | 851 | [[package]] 852 | name = "winapi-build" 853 | version = "0.1.1" 854 | source = "registry+https://github.com/rust-lang/crates.io-index" 855 | checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" 856 | 857 | [[package]] 858 | name = "winapi-i686-pc-windows-gnu" 859 | version = "0.4.0" 860 | source = "registry+https://github.com/rust-lang/crates.io-index" 861 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 862 | 863 | [[package]] 864 | name = "winapi-x86_64-pc-windows-gnu" 865 | version = "0.4.0" 866 | source = "registry+https://github.com/rust-lang/crates.io-index" 867 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 868 | 869 | [[package]] 870 | name = "ws2_32-sys" 871 | version = "0.2.1" 872 | source = "registry+https://github.com/rust-lang/crates.io-index" 873 | checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" 874 | dependencies = [ 875 | "winapi 0.2.8", 876 | "winapi-build", 877 | ] 878 | --------------------------------------------------------------------------------