├── .gitignore ├── server ├── src │ ├── proto │ │ ├── mod.rs │ │ └── proto_all.rs │ ├── events.rs │ ├── connection.rs │ ├── main.rs │ └── game.rs ├── README.md ├── Cargo.toml └── Cargo.lock ├── showcase.gif ├── showcase2.gif ├── client ├── README.md ├── index.html ├── websocket.js ├── sketch.js ├── init.js ├── proto-all.js └── lib │ └── pbf.js ├── proto_script.sh ├── README.md └── proto-all.proto /.gitignore: -------------------------------------------------------------------------------- 1 | /server/target -------------------------------------------------------------------------------- /server/src/proto/mod.rs: -------------------------------------------------------------------------------- 1 | // Automatically generated mod.rs 2 | pub mod proto_all; 3 | -------------------------------------------------------------------------------- /showcase.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Descrout/rust-multiplayer-physics/HEAD/showcase.gif -------------------------------------------------------------------------------- /showcase2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Descrout/rust-multiplayer-physics/HEAD/showcase2.gif -------------------------------------------------------------------------------- /client/README.md: -------------------------------------------------------------------------------- 1 | Just host this client with changing to correct IP and PORT, from [here.](https://github.com/Descrout/rust-multiplayer-physics/blob/8f3b0242cee4e36f8a417d3baab38314ded262f6/client/init.js#L1) -------------------------------------------------------------------------------- /proto_script.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Generate both js and rust protobuf codes from one .proto file. 3 | pb-rs --dont_use_cow -o server/src/proto/proto-all.rs proto-all.proto 4 | pbf proto-all.proto --browser > client/proto-all.js -------------------------------------------------------------------------------- /server/README.md: -------------------------------------------------------------------------------- 1 | [Change](https://github.com/Descrout/rust-multiplayer-physics/blob/8f3b0242cee4e36f8a417d3baab38314ded262f6/server/src/main.rs#L27) the port to suit to yourself, and run. 2 | 3 | ``cargo run --release`` -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Rust Websocket Physics Playground 2 | 3 | A multiplayer physics playground, written in rust, using tokio-tungstenite for websockets and rapier-rs for physics. 4 | 5 | ![showcase](./showcase.gif) 6 | ### Side by side 7 | ![showcase2](./showcase2.gif) -------------------------------------------------------------------------------- /server/src/events.rs: -------------------------------------------------------------------------------- 1 | use crate::connection::Connection; 2 | use crate::proto::proto_all::*; 3 | 4 | #[derive(Debug)] 5 | pub enum GameEvents { 6 | Join(Connection), 7 | Quit(u32), 8 | Input(u32, GameInput), 9 | } 10 | 11 | #[derive(Debug)] 12 | pub enum BroadcastEvents { 13 | Join(Connection), 14 | Quit(u32), 15 | StateOut(State), 16 | } 17 | -------------------------------------------------------------------------------- /server/src/connection.rs: -------------------------------------------------------------------------------- 1 | use tokio::net::TcpStream; 2 | use tokio_tungstenite::tungstenite::Message; 3 | use tokio_tungstenite::WebSocketStream; 4 | 5 | use futures_util::stream::SplitSink; 6 | 7 | #[derive(Debug)] 8 | pub struct Connection { 9 | pub id: u32, 10 | pub sender: SplitSink, Message>, 11 | } 12 | 13 | impl Connection { 14 | pub fn new(id: u32, sender: SplitSink, Message>) -> Self { 15 | Self { id: id, sender } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /proto-all.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | message Entity{ 4 | uint32 id = 1; 5 | float x = 2; 6 | float y = 3; 7 | bool pressed = 4; 8 | string color = 5; 9 | } 10 | 11 | message Body { 12 | uint32 id = 1; 13 | string color = 2; 14 | float x = 3; 15 | float y = 4; 16 | float w = 5; 17 | float h = 6; 18 | float rotation = 7; 19 | } 20 | 21 | message State { 22 | repeated Entity entities = 1; 23 | repeated Body bodies = 2; 24 | } 25 | 26 | message GameInput { 27 | float x = 1; 28 | float y = 2; 29 | bool pressed = 3; 30 | } -------------------------------------------------------------------------------- /server/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "websocket_physics" 3 | version = "0.1.0" 4 | edition = "2018" 5 | authors = ["Adil Basar "] 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | rapier2d = { version = "*", features = [ "simd-stable" ] } 11 | futures-util = { version = "0.3.17", default-features = false, features = ["async-await", "sink", "std"] } 12 | tokio = { version = "1.11.0", default-features = false, features = ["io-util", "macros", "time", "sync", "net", "rt-multi-thread"] } 13 | tokio-tungstenite = {version = "0.15.0", default-features = false} 14 | quick-protobuf = {git="https://github.com/Descrout/quick-protobuf"} -------------------------------------------------------------------------------- /client/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Rust Websocket Physics 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 16 | 17 | 18 | 19 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /client/websocket.js: -------------------------------------------------------------------------------- 1 | class Network { 2 | constructor(server) { 3 | if (!("WebSocket" in window)) { 4 | alert("WebSocket NOT supported by your Browser!"); 5 | return; 6 | } 7 | this.server = server; 8 | } 9 | 10 | connect(cb) { 11 | if (this.ws) return; 12 | this.ws = new WebSocket(this.server); 13 | this.ws.binaryType = 'arraybuffer'; 14 | this.ws.onmessage = (e) => { 15 | let data = new Uint8Array(e.data); 16 | let obj = Parser.deSerialize(data); 17 | if (obj) cb(data[0], obj); 18 | } 19 | 20 | let promise = new Promise((resolve, reject) => { 21 | this.ws.onopen = () => { 22 | this.connected(); 23 | resolve("Connected!"); 24 | }; 25 | 26 | this.ws.onclose = () => { 27 | this.disconnected(); 28 | this.ws = null; 29 | reject("Offline!"); 30 | }; 31 | }); 32 | 33 | return promise; 34 | } 35 | 36 | send(obj) { 37 | this.ws.send(Parser.serialize(obj)); 38 | } 39 | 40 | connected() { 41 | console.log("Connection successful :)"); 42 | connectionSuccess = true; 43 | } 44 | 45 | disconnected() { 46 | console.log("Websocket disconnected !"); 47 | connectionSuccess = false; 48 | } 49 | 50 | close() { 51 | this.ws.close(); 52 | connectionSuccess = false; 53 | } 54 | } -------------------------------------------------------------------------------- /client/sketch.js: -------------------------------------------------------------------------------- 1 | function setup() { 2 | canvas = createCanvas(960, 540); 3 | canvas.elt.addEventListener('contextmenu', event => event.preventDefault()); 4 | scaleMultiplier = scaleToWindow(canvas.elt); 5 | 6 | mouse = {x: 0, y: 0}; 7 | 8 | noLoop(); 9 | 10 | socket = new Network(address); 11 | 12 | let promise = socket.connect(received); 13 | if (promise) { 14 | promise.then(() => { 15 | loop(); 16 | }).catch((err) => { 17 | alert("Server is offline !"); 18 | }); 19 | } 20 | } 21 | 22 | function draw() { 23 | let dt = deltaTime / 1000; 24 | if (dt > 0.033) dt = 0.033; 25 | if(touches.length) { 26 | mouse.x = touches[0].x / scaleMultiplier; 27 | mouse.y = touches[0].y / scaleMultiplier; 28 | mouseIsPressed = true; 29 | }else { 30 | mouse.x = mouseX / scaleMultiplier; 31 | mouse.y = mouseY / scaleMultiplier; 32 | } 33 | 34 | update(dt); 35 | 36 | // Draw 37 | background(255); 38 | if(!state) return; 39 | 40 | stroke(0); 41 | strokeWeight(1); 42 | for(const body of state.bodies) { 43 | push(); 44 | fill(body.color); 45 | const half_w = body.w * 50; 46 | const half_h = body.h * 50; 47 | const x = body.x * 50 - half_w; 48 | const y = height - (body.y * 50 + half_h); 49 | translate(half_w + x, half_h + y); 50 | rotate(-body.rotation); 51 | rect(-half_w, -half_h, half_w * 2, half_h * 2); 52 | pop(); 53 | } 54 | 55 | strokeWeight(2); 56 | noStroke(); 57 | for(const player of state.entities) { 58 | if(player.pressed) stroke(0); 59 | else noStroke(); 60 | fill(player.color); 61 | circle(player.x, player.y, 10); 62 | } 63 | } 64 | 65 | function update(dt) { 66 | if(!connectionSuccess) return; 67 | socket.send({ 68 | x: mouse.x, 69 | y: mouse.y, 70 | pressed: mouseIsPressed 71 | }); 72 | } 73 | 74 | function received(header, obj) { 75 | state = obj; 76 | } 77 | 78 | function windowResized() { 79 | scaleMultiplier = scaleToWindow(canvas.elt); 80 | } -------------------------------------------------------------------------------- /client/init.js: -------------------------------------------------------------------------------- 1 | const address = `ws://127.0.0.1:6464`; 2 | 3 | let canvas, scaleMultiplier, mouse, socket; 4 | 5 | let connectionSuccess = false; 6 | 7 | let state; 8 | 9 | class Parser { 10 | static serialize(obj) { 11 | let pbf = new Pbf(); 12 | pbf.writeBytes([0]); // we only have one header so i just use 0 13 | GameInput.write(obj, pbf); 14 | return pbf.finish().slice(1); 15 | } 16 | 17 | static deSerialize(data) { 18 | let pbf = new Pbf(data.slice(1)); 19 | switch (data[0]) { 20 | case 0: return State.read(pbf); 21 | default: 22 | console.error("Receive header doesn't match"); 23 | return; 24 | } 25 | } 26 | } 27 | 28 | function scaleToWindow(canvas, backgroundColor) { 29 | let scaleX, scaleY, scale, center; 30 | 31 | scaleX = window.innerWidth / canvas.offsetWidth; 32 | scaleY = window.innerHeight / canvas.offsetHeight; 33 | 34 | scale = Math.min(scaleX, scaleY); 35 | canvas.style.transformOrigin = "0 0"; 36 | canvas.style.transform = "scale(" + scale + ")"; 37 | 38 | if (canvas.offsetWidth > canvas.offsetHeight) { 39 | if (canvas.offsetWidth * scale < window.innerWidth) { 40 | center = "horizontally"; 41 | } else { 42 | center = "vertically"; 43 | } 44 | } else { 45 | if (canvas.offsetHeight * scale < window.innerHeight) { 46 | center = "vertically"; 47 | } else { 48 | center = "horizontally"; 49 | } 50 | } 51 | 52 | let margin; 53 | if (center === "horizontally") { 54 | margin = (window.innerWidth - canvas.offsetWidth * scale) / 2; 55 | canvas.style.marginTop = 0 + "px"; 56 | canvas.style.marginBottom = 0 + "px"; 57 | canvas.style.marginLeft = margin + "px"; 58 | canvas.style.marginRight = margin + "px"; 59 | } 60 | 61 | if (center === "vertically") { 62 | margin = (window.innerHeight - canvas.offsetHeight * scale) / 2; 63 | canvas.style.marginTop = margin + "px"; 64 | canvas.style.marginBottom = margin + "px"; 65 | canvas.style.marginLeft = 0 + "px"; 66 | canvas.style.marginRight = 0 + "px"; 67 | } 68 | 69 | canvas.style.paddingLeft = 0 + "px"; 70 | canvas.style.paddingRight = 0 + "px"; 71 | canvas.style.paddingTop = 0 + "px"; 72 | canvas.style.paddingBottom = 0 + "px"; 73 | canvas.style.display = "block"; 74 | 75 | document.body.style.backgroundColor = backgroundColor; 76 | 77 | let ua = navigator.userAgent.toLowerCase(); 78 | if (ua.indexOf("safari") != -1) { 79 | if (ua.indexOf("chrome") > -1) { 80 | // Chrome 81 | } else { 82 | // Safari 83 | //canvas.style.maxHeight = "100%"; 84 | //canvas.style.minHeight = "100%"; 85 | } 86 | } 87 | 88 | return scale; 89 | } 90 | 91 | function lerp(a, b, t) { 92 | return a + (b - a) * t; 93 | } -------------------------------------------------------------------------------- /client/proto-all.js: -------------------------------------------------------------------------------- 1 | 'use strict'; // code generated by pbf v3.2.1 2 | 3 | // Entity ======================================== 4 | 5 | var Entity = self.Entity = {}; 6 | 7 | Entity.read = function (pbf, end) { 8 | return pbf.readFields(Entity._readField, {id: 0, x: 0, y: 0, pressed: false, color: ""}, end); 9 | }; 10 | Entity._readField = function (tag, obj, pbf) { 11 | if (tag === 1) obj.id = pbf.readVarint(); 12 | else if (tag === 2) obj.x = pbf.readFloat(); 13 | else if (tag === 3) obj.y = pbf.readFloat(); 14 | else if (tag === 4) obj.pressed = pbf.readBoolean(); 15 | else if (tag === 5) obj.color = pbf.readString(); 16 | }; 17 | Entity.write = function (obj, pbf) { 18 | if (obj.id) pbf.writeVarintField(1, obj.id); 19 | if (obj.x) pbf.writeFloatField(2, obj.x); 20 | if (obj.y) pbf.writeFloatField(3, obj.y); 21 | if (obj.pressed) pbf.writeBooleanField(4, obj.pressed); 22 | if (obj.color) pbf.writeStringField(5, obj.color); 23 | }; 24 | 25 | // Body ======================================== 26 | 27 | var Body = self.Body = {}; 28 | 29 | Body.read = function (pbf, end) { 30 | return pbf.readFields(Body._readField, {id: 0, color: "", x: 0, y: 0, w: 0, h: 0, rotation: 0}, end); 31 | }; 32 | Body._readField = function (tag, obj, pbf) { 33 | if (tag === 1) obj.id = pbf.readVarint(); 34 | else if (tag === 2) obj.color = pbf.readString(); 35 | else if (tag === 3) obj.x = pbf.readFloat(); 36 | else if (tag === 4) obj.y = pbf.readFloat(); 37 | else if (tag === 5) obj.w = pbf.readFloat(); 38 | else if (tag === 6) obj.h = pbf.readFloat(); 39 | else if (tag === 7) obj.rotation = pbf.readFloat(); 40 | }; 41 | Body.write = function (obj, pbf) { 42 | if (obj.id) pbf.writeVarintField(1, obj.id); 43 | if (obj.color) pbf.writeStringField(2, obj.color); 44 | if (obj.x) pbf.writeFloatField(3, obj.x); 45 | if (obj.y) pbf.writeFloatField(4, obj.y); 46 | if (obj.w) pbf.writeFloatField(5, obj.w); 47 | if (obj.h) pbf.writeFloatField(6, obj.h); 48 | if (obj.rotation) pbf.writeFloatField(7, obj.rotation); 49 | }; 50 | 51 | // State ======================================== 52 | 53 | var State = self.State = {}; 54 | 55 | State.read = function (pbf, end) { 56 | return pbf.readFields(State._readField, {entities: [], bodies: []}, end); 57 | }; 58 | State._readField = function (tag, obj, pbf) { 59 | if (tag === 1) obj.entities.push(Entity.read(pbf, pbf.readVarint() + pbf.pos)); 60 | else if (tag === 2) obj.bodies.push(Body.read(pbf, pbf.readVarint() + pbf.pos)); 61 | }; 62 | State.write = function (obj, pbf) { 63 | if (obj.entities) for (var i = 0; i < obj.entities.length; i++) pbf.writeMessage(1, Entity.write, obj.entities[i]); 64 | if (obj.bodies) for (i = 0; i < obj.bodies.length; i++) pbf.writeMessage(2, Body.write, obj.bodies[i]); 65 | }; 66 | 67 | // GameInput ======================================== 68 | 69 | var GameInput = self.GameInput = {}; 70 | 71 | GameInput.read = function (pbf, end) { 72 | return pbf.readFields(GameInput._readField, {x: 0, y: 0, pressed: false}, end); 73 | }; 74 | GameInput._readField = function (tag, obj, pbf) { 75 | if (tag === 1) obj.x = pbf.readFloat(); 76 | else if (tag === 2) obj.y = pbf.readFloat(); 77 | else if (tag === 3) obj.pressed = pbf.readBoolean(); 78 | }; 79 | GameInput.write = function (obj, pbf) { 80 | if (obj.x) pbf.writeFloatField(1, obj.x); 81 | if (obj.y) pbf.writeFloatField(2, obj.y); 82 | if (obj.pressed) pbf.writeBooleanField(3, obj.pressed); 83 | }; 84 | -------------------------------------------------------------------------------- /server/src/main.rs: -------------------------------------------------------------------------------- 1 | use futures_util::{FutureExt, SinkExt}; 2 | use quick_protobuf::{BytesReader, Writer}; 3 | mod connection; 4 | mod events; 5 | mod game; 6 | mod proto; 7 | 8 | use tokio::net::{TcpListener, TcpStream}; 9 | use tokio::sync::mpsc; 10 | use tokio::sync::mpsc::{UnboundedReceiver, UnboundedSender}; 11 | 12 | use connection::Connection; 13 | use events::{BroadcastEvents, GameEvents}; 14 | 15 | use futures_util::stream::StreamExt; 16 | use tokio::task::unconstrained; 17 | use tokio_tungstenite::tungstenite::Message; 18 | use tokio_tungstenite::WebSocketStream; 19 | 20 | use std::collections::HashMap; 21 | use std::{thread, time}; 22 | 23 | use crate::game::Game; 24 | use crate::proto::proto_all; 25 | use quick_protobuf::MessageRead; 26 | 27 | const PORT: &str = "6464"; 28 | const FIXED_TIMESTEP: f32 = 0.016; // 60FPS 29 | 30 | #[tokio::main] 31 | async fn main() { 32 | let addr = format!("0.0.0.0:{}", PORT); 33 | 34 | let listener = TcpListener::bind(&addr) 35 | .await 36 | .expect("Listening to TCP failed."); 37 | 38 | /* 39 | Broadcast data to all clients in a seperate async tokio green thread. 40 | The game loop will use 'broadcast_sender' to send the game state, 41 | and join&quit events into this function. 42 | */ 43 | let (broadcast_sender, broadcast_receiver) = mpsc::unbounded_channel::(); 44 | tokio::spawn(broadcast(broadcast_receiver)); 45 | 46 | /* 47 | Since I will only use one game loop, I'm using an actual std::thread for the game loop. 48 | This function takes ownership of the 'broadcast_sender' to send events into the 'broadcast' green thread. 49 | */ 50 | let (game_sender, game_receiver) = mpsc::unbounded_channel::(); 51 | thread::spawn(move || run(broadcast_sender, game_receiver)); 52 | 53 | println!("Listening on: {}", addr); 54 | 55 | // A counter to use as client ids. 56 | let mut id = 0; 57 | 58 | // Accept new clients. 59 | while let Ok((stream, peer)) = listener.accept().await { 60 | match tokio_tungstenite::accept_async(stream).await { 61 | Err(e) => println!("Websocket connection error : {}", e), 62 | Ok(ws_stream) => { 63 | println!("New Connection : {}", peer); 64 | id += 1; 65 | tokio::spawn(listen(game_sender.clone(), ws_stream, id)); 66 | } 67 | } 68 | } 69 | } 70 | 71 | // Serialize the game state into bytes Vec to send through websocket. 72 | fn serialize_state(state: &proto_all::State) -> Vec { 73 | let mut out = Vec::new(); 74 | let mut writer = Writer::new(&mut out); 75 | 76 | writer.write_u8(0).unwrap(); // Header, in case we wanna use different headers in the future 77 | 78 | writer 79 | .write_message_no_len(state) // https://github.com/Descrout/quick-protobuf 'no_len' version of write of message. 80 | .expect("Cannot serialize state"); 81 | 82 | out 83 | } 84 | 85 | /* 86 | 60 FPS fixed game loop. 87 | Update the internal game state and send it to broadcast green thread without any blocking. 88 | */ 89 | fn run(tx: UnboundedSender, mut receiver: UnboundedReceiver) { 90 | // Initialize the game state 91 | let mut game = Game::new(); 92 | game.init(); 93 | 94 | // Time variables 95 | let sixteen_ms = time::Duration::from_millis(16); 96 | let mut accum = 0.0; 97 | let mut dt = 0.0; 98 | 99 | // Start the loop 100 | loop { 101 | let start = time::Instant::now(); 102 | /* 103 | If we have any game event we process those events and continue to update the game. 104 | If we don't have any game event, we do nothing. 105 | We do not block here. 106 | 107 | TODO: Change 'unconstrained(receiver.recv()).now_or_never()' to 'receiver.try_recv()' when tokio implemention is done. 108 | https://github.com/tokio-rs/tokio/pull/3639 109 | */ 110 | while let Some(is_event) = unconstrained(receiver.recv()).now_or_never() { 111 | if let Some(event) = is_event { 112 | match event { 113 | GameEvents::Join(conn) => { 114 | game.add_player(conn.id); 115 | let _ = tx.send(BroadcastEvents::Join(conn)); 116 | } 117 | GameEvents::Quit(user_id) => { 118 | game.remove_player(user_id); 119 | let _ = tx.send(BroadcastEvents::Quit(user_id)); 120 | } 121 | GameEvents::Input(id, input) => { 122 | game.set_input(id, input); 123 | } 124 | } 125 | } 126 | } 127 | 128 | // Fixed game loop 129 | accum += dt; 130 | while accum >= FIXED_TIMESTEP { 131 | accum -= FIXED_TIMESTEP; 132 | 133 | // Update the game state (in our case rapier.rs physics simulation and intersection queries) 134 | game.update(); 135 | 136 | // Send the game state to broadcast green thread. 137 | let _ = tx.send(BroadcastEvents::StateOut(game.get_state())); 138 | } 139 | 140 | thread::sleep(sixteen_ms); 141 | dt = start.elapsed().as_secs_f32(); 142 | } 143 | } 144 | 145 | // Broadcast all the incoming game state to the clients. 146 | async fn broadcast(mut rx: UnboundedReceiver) { 147 | let mut connections: HashMap = HashMap::new(); 148 | 149 | while let Some(event) = rx.recv().await { 150 | match event { 151 | BroadcastEvents::Join(conn) => { 152 | connections.insert(conn.id, conn); 153 | } 154 | BroadcastEvents::Quit(id) => { 155 | connections.remove(&id); 156 | println!("Connection lost : {}", id); 157 | } 158 | BroadcastEvents::StateOut(state) => { 159 | for (_, conn) in connections.iter_mut() { 160 | let data = serialize_state(&state); 161 | let _ = conn.sender.send(Message::Binary(data)).await; 162 | } 163 | } 164 | } 165 | } 166 | } 167 | 168 | // Listen for incoming data from clients. 169 | async fn listen( 170 | game_sender: UnboundedSender, 171 | ws_stream: WebSocketStream, 172 | id: u32, 173 | ) { 174 | let (sender, mut receiver) = ws_stream.split(); 175 | let conn = Connection::new(id, sender); 176 | let _ = game_sender.send(GameEvents::Join(conn)); 177 | 178 | while let Some(msg) = receiver.next().await { 179 | if let Ok(msg) = msg { 180 | if msg.is_binary() { 181 | let mut msg = msg.into_data(); 182 | let header = msg.remove(0); 183 | let mut reader = BytesReader::from_bytes(&msg); 184 | if header == 0 { 185 | if let Ok(input) = proto_all::GameInput::from_reader(&mut reader, &msg) { 186 | let _ = game_sender.send(GameEvents::Input(id, input)); 187 | } 188 | } 189 | } else if msg.is_close() { 190 | break; // When we break, we disconnect. 191 | } 192 | } else { 193 | break; // When we break, we disconnect. 194 | } 195 | } 196 | // If we reach here, it means the client got disconnected. 197 | // Send quit event to game loop, and the game loop will send quit event to the broadcast thread. 198 | // So all cleanups will be done. 199 | game_sender.send(GameEvents::Quit(id)).unwrap(); 200 | } 201 | -------------------------------------------------------------------------------- /server/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 Entity { 18 | pub id: u32, 19 | pub x: f32, 20 | pub y: f32, 21 | pub pressed: bool, 22 | pub color: String, 23 | } 24 | 25 | impl<'a> MessageRead<'a> for Entity { 26 | fn from_reader(r: &mut BytesReader, bytes: &'a [u8]) -> Result { 27 | let mut msg = Self::default(); 28 | while !r.is_eof() { 29 | match r.next_tag(bytes) { 30 | Ok(8) => msg.id = r.read_uint32(bytes)?, 31 | Ok(21) => msg.x = r.read_float(bytes)?, 32 | Ok(29) => msg.y = r.read_float(bytes)?, 33 | Ok(32) => msg.pressed = r.read_bool(bytes)?, 34 | Ok(42) => msg.color = r.read_string(bytes)?.to_owned(), 35 | Ok(t) => { r.read_unknown(bytes, t)?; } 36 | Err(e) => return Err(e), 37 | } 38 | } 39 | Ok(msg) 40 | } 41 | } 42 | 43 | impl MessageWrite for Entity { 44 | fn get_size(&self) -> usize { 45 | 0 46 | + if self.id == 0u32 { 0 } else { 1 + sizeof_varint(*(&self.id) as u64) } 47 | + if self.x == 0f32 { 0 } else { 1 + 4 } 48 | + if self.y == 0f32 { 0 } else { 1 + 4 } 49 | + if self.pressed == false { 0 } else { 1 + sizeof_varint(*(&self.pressed) as u64) } 50 | + if self.color == String::default() { 0 } else { 1 + sizeof_len((&self.color).len()) } 51 | } 52 | 53 | fn write_message(&self, w: &mut Writer) -> Result<()> { 54 | if self.id != 0u32 { w.write_with_tag(8, |w| w.write_uint32(*&self.id))?; } 55 | if self.x != 0f32 { w.write_with_tag(21, |w| w.write_float(*&self.x))?; } 56 | if self.y != 0f32 { w.write_with_tag(29, |w| w.write_float(*&self.y))?; } 57 | if self.pressed != false { w.write_with_tag(32, |w| w.write_bool(*&self.pressed))?; } 58 | if self.color != String::default() { w.write_with_tag(42, |w| w.write_string(&**&self.color))?; } 59 | Ok(()) 60 | } 61 | } 62 | 63 | #[derive(Debug, Default, PartialEq, Clone)] 64 | pub struct Body { 65 | pub id: u32, 66 | pub color: String, 67 | pub x: f32, 68 | pub y: f32, 69 | pub w: f32, 70 | pub h: f32, 71 | pub rotation: f32, 72 | } 73 | 74 | impl<'a> MessageRead<'a> for Body { 75 | fn from_reader(r: &mut BytesReader, bytes: &'a [u8]) -> Result { 76 | let mut msg = Self::default(); 77 | while !r.is_eof() { 78 | match r.next_tag(bytes) { 79 | Ok(8) => msg.id = r.read_uint32(bytes)?, 80 | Ok(18) => msg.color = r.read_string(bytes)?.to_owned(), 81 | Ok(29) => msg.x = r.read_float(bytes)?, 82 | Ok(37) => msg.y = r.read_float(bytes)?, 83 | Ok(45) => msg.w = r.read_float(bytes)?, 84 | Ok(53) => msg.h = r.read_float(bytes)?, 85 | Ok(61) => msg.rotation = r.read_float(bytes)?, 86 | Ok(t) => { r.read_unknown(bytes, t)?; } 87 | Err(e) => return Err(e), 88 | } 89 | } 90 | Ok(msg) 91 | } 92 | } 93 | 94 | impl MessageWrite for Body { 95 | fn get_size(&self) -> usize { 96 | 0 97 | + if self.id == 0u32 { 0 } else { 1 + sizeof_varint(*(&self.id) as u64) } 98 | + if self.color == String::default() { 0 } else { 1 + sizeof_len((&self.color).len()) } 99 | + if self.x == 0f32 { 0 } else { 1 + 4 } 100 | + if self.y == 0f32 { 0 } else { 1 + 4 } 101 | + if self.w == 0f32 { 0 } else { 1 + 4 } 102 | + if self.h == 0f32 { 0 } else { 1 + 4 } 103 | + if self.rotation == 0f32 { 0 } else { 1 + 4 } 104 | } 105 | 106 | fn write_message(&self, w: &mut Writer) -> Result<()> { 107 | if self.id != 0u32 { w.write_with_tag(8, |w| w.write_uint32(*&self.id))?; } 108 | if self.color != String::default() { w.write_with_tag(18, |w| w.write_string(&**&self.color))?; } 109 | if self.x != 0f32 { w.write_with_tag(29, |w| w.write_float(*&self.x))?; } 110 | if self.y != 0f32 { w.write_with_tag(37, |w| w.write_float(*&self.y))?; } 111 | if self.w != 0f32 { w.write_with_tag(45, |w| w.write_float(*&self.w))?; } 112 | if self.h != 0f32 { w.write_with_tag(53, |w| w.write_float(*&self.h))?; } 113 | if self.rotation != 0f32 { w.write_with_tag(61, |w| w.write_float(*&self.rotation))?; } 114 | Ok(()) 115 | } 116 | } 117 | 118 | #[derive(Debug, Default, PartialEq, Clone)] 119 | pub struct State { 120 | pub entities: Vec, 121 | pub bodies: Vec, 122 | } 123 | 124 | impl<'a> MessageRead<'a> for State { 125 | fn from_reader(r: &mut BytesReader, bytes: &'a [u8]) -> Result { 126 | let mut msg = Self::default(); 127 | while !r.is_eof() { 128 | match r.next_tag(bytes) { 129 | Ok(10) => msg.entities.push(r.read_message::(bytes)?), 130 | Ok(18) => msg.bodies.push(r.read_message::(bytes)?), 131 | Ok(t) => { r.read_unknown(bytes, t)?; } 132 | Err(e) => return Err(e), 133 | } 134 | } 135 | Ok(msg) 136 | } 137 | } 138 | 139 | impl MessageWrite for State { 140 | fn get_size(&self) -> usize { 141 | 0 142 | + self.entities.iter().map(|s| 1 + sizeof_len((s).get_size())).sum::() 143 | + self.bodies.iter().map(|s| 1 + sizeof_len((s).get_size())).sum::() 144 | } 145 | 146 | fn write_message(&self, w: &mut Writer) -> Result<()> { 147 | for s in &self.entities { w.write_with_tag(10, |w| w.write_message(s))?; } 148 | for s in &self.bodies { w.write_with_tag(18, |w| w.write_message(s))?; } 149 | Ok(()) 150 | } 151 | } 152 | 153 | #[derive(Debug, Default, PartialEq, Clone)] 154 | pub struct GameInput { 155 | pub x: f32, 156 | pub y: f32, 157 | pub pressed: bool, 158 | } 159 | 160 | impl<'a> MessageRead<'a> for GameInput { 161 | fn from_reader(r: &mut BytesReader, bytes: &'a [u8]) -> Result { 162 | let mut msg = Self::default(); 163 | while !r.is_eof() { 164 | match r.next_tag(bytes) { 165 | Ok(13) => msg.x = r.read_float(bytes)?, 166 | Ok(21) => msg.y = r.read_float(bytes)?, 167 | Ok(24) => msg.pressed = r.read_bool(bytes)?, 168 | Ok(t) => { r.read_unknown(bytes, t)?; } 169 | Err(e) => return Err(e), 170 | } 171 | } 172 | Ok(msg) 173 | } 174 | } 175 | 176 | impl MessageWrite for GameInput { 177 | fn get_size(&self) -> usize { 178 | 0 179 | + if self.x == 0f32 { 0 } else { 1 + 4 } 180 | + if self.y == 0f32 { 0 } else { 1 + 4 } 181 | + if self.pressed == false { 0 } else { 1 + sizeof_varint(*(&self.pressed) as u64) } 182 | } 183 | 184 | fn write_message(&self, w: &mut Writer) -> Result<()> { 185 | if self.x != 0f32 { w.write_with_tag(13, |w| w.write_float(*&self.x))?; } 186 | if self.y != 0f32 { w.write_with_tag(21, |w| w.write_float(*&self.y))?; } 187 | if self.pressed != false { w.write_with_tag(24, |w| w.write_bool(*&self.pressed))?; } 188 | Ok(()) 189 | } 190 | } 191 | 192 | -------------------------------------------------------------------------------- /server/src/game.rs: -------------------------------------------------------------------------------- 1 | use crate::proto::proto_all; 2 | use rapier2d::prelude::*; 3 | use std::collections::HashMap; 4 | 5 | const COLORS: [u32; 50] = [ 6 | 0xFF6633, 0xFFB399, 0xFF33FF, 0xFFFF99, 0x00B3E6, 0xE6B333, 0x3366E6, 0x999966, 0x99FF99, 7 | 0xB34D4D, 0x80B300, 0x809900, 0xE6B3B3, 0x6680B3, 0x66991A, 0xFF99E6, 0xCCFF1A, 0xFF1A66, 8 | 0xE6331A, 0x33FFCC, 0x66994D, 0xB366CC, 0x4D8000, 0xB33300, 0xCC80CC, 0x66664D, 0x991AFF, 9 | 0xE666FF, 0x4DB3FF, 0x1AB399, 0xE666B3, 0x33991A, 0xCC9999, 0xB3B31A, 0x00E680, 0x4D8066, 10 | 0x809980, 0xE6FF80, 0x1AFF33, 0x999933, 0xFF3380, 0xCCCC00, 0x66E64D, 0x4D80CC, 0x9900B3, 11 | 0xE64D66, 0x4DB380, 0xFF4D4D, 0x99E6E6, 0x6666FF, 12 | ]; 13 | 14 | const WIDTH: f32 = 960.0; 15 | const HEIGHT: f32 = 540.0; 16 | 17 | // PIXELS PER METER 18 | const PPM: f32 = 50.0; 19 | 20 | struct Rect { 21 | id: usize, 22 | w: f32, 23 | h: f32, 24 | r_handle: RigidBodyHandle, 25 | } 26 | 27 | pub struct Game { 28 | players: HashMap, 29 | rects: Vec, 30 | 31 | // rapier.rs stuff 32 | islands: IslandManager, 33 | broad_phase: BroadPhase, 34 | narrow_phase: NarrowPhase, 35 | bodies: RigidBodySet, 36 | colliders: ColliderSet, 37 | joints: JointSet, 38 | ccd_solver: CCDSolver, 39 | query_pipeline: QueryPipeline, 40 | physics_pipeline: PhysicsPipeline, 41 | integration_parameters: IntegrationParameters, 42 | gravity: Vector, 43 | } 44 | 45 | impl Game { 46 | pub fn new() -> Self { 47 | Self { 48 | players: HashMap::new(), 49 | rects: Vec::new(), 50 | 51 | // rapier.rs stuff 52 | gravity: vector![0.0, -9.81], 53 | integration_parameters: IntegrationParameters::default(), 54 | islands: IslandManager::new(), 55 | broad_phase: BroadPhase::new(), 56 | narrow_phase: NarrowPhase::new(), 57 | joints: JointSet::new(), 58 | ccd_solver: CCDSolver::new(), 59 | bodies: RigidBodySet::new(), 60 | colliders: ColliderSet::new(), 61 | query_pipeline: QueryPipeline::new(), 62 | physics_pipeline: PhysicsPipeline::new(), 63 | } 64 | } 65 | 66 | pub fn init(&mut self) { 67 | /* Create the static walls. */ 68 | self.add_cuboid(0.0, HEIGHT - 10.0, WIDTH, 10.0, 0.0, true); 69 | self.add_cuboid(0.0, 0.0, WIDTH, 10.0, 0.0, true); 70 | 71 | self.add_cuboid(0.0, 0.0, 10.0, HEIGHT, 0.0, true); 72 | self.add_cuboid(WIDTH - 10.0, 0.0, 10.0, HEIGHT, 0.0, true); 73 | 74 | self.add_cuboid(WIDTH / 4.0, HEIGHT / 2.0, WIDTH / 2.0, 10.0, 0.3, true); 75 | 76 | /* Create the cuboids. */ 77 | let angle = std::f32::consts::PI / 4.0; 78 | for i in 0..9 { 79 | let i = i as f32; 80 | self.add_cuboid(50.0 + 100.0 * i, 100.0, 70.0, 70.0, angle + 0.3 * i, false); 81 | } 82 | 83 | for i in 0..11 { 84 | let i = i as f32; 85 | self.add_cuboid(50.0 + 80.0 * i, 290.0, 50.0, 50.0, angle + 0.3 * i, false); 86 | } 87 | 88 | for i in 0..10 { 89 | let i = i as f32; 90 | self.add_cuboid(50.0 + 80.0 * i, 480.0, 30.0, 30.0, angle + 0.3 * i, false); 91 | } 92 | } 93 | 94 | fn add_cuboid(&mut self, x: f32, y: f32, w: f32, h: f32, angle: f32, is_static: bool) { 95 | let id = self.rects.len(); 96 | 97 | // RigidBody 98 | let r_builder = if is_static { 99 | RigidBodyBuilder::new_static() 100 | } else { 101 | RigidBodyBuilder::new_dynamic() 102 | }; 103 | 104 | let w = (w / PPM) / 2.0; 105 | let h = (h / PPM) / 2.0; 106 | 107 | let r_body = r_builder 108 | .translation(vector![(x / PPM) + w, ((HEIGHT - y) / PPM) - h]) 109 | .rotation(angle) 110 | .user_data(COLORS[id % 50] as u128) // I'm using user_data to present cuboid's color. 111 | .build(); 112 | 113 | // Collider 114 | let c_builder = ColliderBuilder::cuboid(w, h); 115 | let collider = if is_static { 116 | c_builder.build() 117 | } else { 118 | c_builder.restitution(0.7).build() 119 | }; 120 | 121 | let r_handle = self.bodies.insert(r_body); 122 | self.colliders 123 | .insert_with_parent(collider, r_handle, &mut self.bodies); 124 | 125 | self.rects.push(Rect { id, r_handle, w, h }); 126 | } 127 | 128 | pub fn add_player(&mut self, id: u32) { 129 | self.players.insert( 130 | id, 131 | proto_all::Entity { 132 | id: id, 133 | x: 0.0, 134 | y: 0.0, 135 | pressed: false, 136 | color: format!("#{:X}", COLORS[id as usize % 50]), 137 | }, 138 | ); 139 | } 140 | 141 | pub fn remove_player(&mut self, id: u32) { 142 | self.players.remove(&id); 143 | } 144 | 145 | pub fn set_input(&mut self, id: u32, input: proto_all::GameInput) { 146 | let mut player = self.players.get_mut(&id).unwrap(); 147 | player.x = input.x; 148 | player.y = input.y; 149 | player.pressed = input.pressed; 150 | } 151 | 152 | pub fn update(&mut self) { 153 | self.physics_pipeline.step( 154 | &self.gravity, 155 | &self.integration_parameters, 156 | &mut self.islands, 157 | &mut self.broad_phase, 158 | &mut self.narrow_phase, 159 | &mut self.bodies, 160 | &mut self.colliders, 161 | &mut self.joints, 162 | &mut self.ccd_solver, 163 | &(), 164 | &(), 165 | ); 166 | 167 | // Update the query pipeline first 168 | self.query_pipeline 169 | .update(&self.islands, &self.bodies, &self.colliders); 170 | 171 | // Initialize a vec for holding colliders that intersects with player's mouse pointers. 172 | let mut collider_handles = Vec::new(); 173 | for (_, player) in self.players.iter() { 174 | // If a player does not click with the mouse, we pass. 175 | if !player.pressed { 176 | continue; 177 | } 178 | 179 | // Convert player mouse positions to rapier physics positions. 180 | let point = point![player.x / PPM, (HEIGHT - player.y) / PPM]; 181 | 182 | // Check intersection and push those who intersects into the collider_handles vec. 183 | self.query_pipeline.intersections_with_point( 184 | &self.colliders, 185 | &point, 186 | InteractionGroups::all(), 187 | None, 188 | |handle| { 189 | collider_handles.push((handle, point)); 190 | false // Make this true, to make players affect multiple cuboids. 191 | }, 192 | ); 193 | } 194 | 195 | // Apply force and impulse to the intersected boxes. 196 | for (handle, point) in collider_handles.into_iter() { 197 | let collider = self.colliders.get(handle).unwrap(); 198 | let body = self.bodies.get_mut(collider.parent().unwrap()).unwrap(); 199 | if body.is_static() { 200 | continue; 201 | } 202 | let x = body.translation().x; 203 | let y = body.translation().y; 204 | 205 | body.apply_force(vector![0.0, 9.81], true); 206 | body.apply_impulse_at_point(vector![(x - point.x), (y - point.y) * 3.0], point, true); 207 | body.apply_impulse(vector![(x - point.x), (y - point.y).abs() * 3.0], true); 208 | } 209 | } 210 | 211 | pub fn get_state(&self) -> proto_all::State { 212 | let mut state = proto_all::State { 213 | entities: Vec::new(), 214 | bodies: Vec::new(), 215 | }; 216 | 217 | for (_, entity) in self.players.iter() { 218 | state.entities.push(entity.clone()); 219 | } 220 | 221 | for rect in self.rects.iter() { 222 | let body = self.bodies.get(rect.r_handle).unwrap(); 223 | let pos = body.translation(); 224 | state.bodies.push(proto_all::Body { 225 | id: rect.id as u32, 226 | color: format!("#{:X}", body.user_data), 227 | x: pos.x, 228 | y: pos.y, 229 | w: rect.w, 230 | h: rect.h, 231 | rotation: body.rotation().angle(), 232 | }); 233 | } 234 | 235 | state 236 | } 237 | } 238 | -------------------------------------------------------------------------------- /client/lib/pbf.js: -------------------------------------------------------------------------------- 1 | !function(t){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=t();else if("function"==typeof define&&define.amd)define([],t);else{var i;i="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this,i.Pbf=t()}}(function(){return function t(i,e,r){function s(o,h){if(!e[o]){if(!i[o]){var a="function"==typeof require&&require;if(!h&&a)return a(o,!0);if(n)return n(o,!0);var u=new Error("Cannot find module '"+o+"'");throw u.code="MODULE_NOT_FOUND",u}var f=e[o]={exports:{}};i[o][0].call(f.exports,function(t){var e=i[o][1][t];return s(e?e:t)},f,f.exports,t,i,e,r)}return e[o].exports}for(var n="function"==typeof require&&require,o=0;o>4,s<128)return o(t,r,i);if(s=n[e.pos++],r|=(127&s)<<3,s<128)return o(t,r,i);if(s=n[e.pos++],r|=(127&s)<<10,s<128)return o(t,r,i);if(s=n[e.pos++],r|=(127&s)<<17,s<128)return o(t,r,i);if(s=n[e.pos++],r|=(127&s)<<24,s<128)return o(t,r,i);if(s=n[e.pos++],r|=(1&s)<<31,s<128)return o(t,r,i);throw new Error("Expected varint not more than 10 bytes")}function n(t){return t.type===r.Bytes?t.readVarint()+t.pos:t.pos+1}function o(t,i,e){return e?4294967296*i+(t>>>0):4294967296*(i>>>0)+(t>>>0)}function h(t,i){var e,r;if(t>=0?(e=t%4294967296|0,r=t/4294967296|0):(e=~(-t%4294967296),r=~(-t/4294967296),4294967295^e?e=e+1|0:(e=0,r=r+1|0)),t>=0x10000000000000000||t<-0x10000000000000000)throw new Error("Given varint doesn't fit into 10 bytes");i.realloc(10),a(e,r,i),u(r,i)}function a(t,i,e){e.buf[e.pos++]=127&t|128,t>>>=7,e.buf[e.pos++]=127&t|128,t>>>=7,e.buf[e.pos++]=127&t|128,t>>>=7,e.buf[e.pos++]=127&t|128,t>>>=7,e.buf[e.pos]=127&t}function u(t,i){var e=(7&t)<<4;i.buf[i.pos++]|=e|((t>>>=3)?128:0),t&&(i.buf[i.pos++]=127&t|((t>>>=7)?128:0),t&&(i.buf[i.pos++]=127&t|((t>>>=7)?128:0),t&&(i.buf[i.pos++]=127&t|((t>>>=7)?128:0),t&&(i.buf[i.pos++]=127&t|((t>>>=7)?128:0),t&&(i.buf[i.pos++]=127&t)))))}function f(t,i,e){var r=i<=16383?1:i<=2097151?2:i<=268435455?3:Math.ceil(Math.log(i)/(7*Math.LN2));e.realloc(r);for(var s=e.pos-1;s>=t;s--)e.buf[s+r]=e.buf[s]}function d(t,i){for(var e=0;e>>8,t[e+2]=i>>>16,t[e+3]=i>>>24}function y(t,i){return(t[i]|t[i+1]<<8|t[i+2]<<16)+(t[i+3]<<24)}function M(t,i,e){for(var r="",s=i;s239?4:n>223?3:n>191?2:1;if(s+h>e)break;var a,u,f;1===h?n<128&&(o=n):2===h?(a=t[s+1],128===(192&a)&&(o=(31&n)<<6|63&a,o<=127&&(o=null))):3===h?(a=t[s+1],u=t[s+2],128===(192&a)&&128===(192&u)&&(o=(15&n)<<12|(63&a)<<6|63&u,(o<=2047||o>=55296&&o<=57343)&&(o=null))):4===h&&(a=t[s+1],u=t[s+2],f=t[s+3],128===(192&a)&&128===(192&u)&&128===(192&f)&&(o=(15&n)<<18|(63&a)<<12|(63&u)<<6|63&f,(o<=65535||o>=1114112)&&(o=null))),null===o?(o=65533,h=1):o>65535&&(o-=65536,r+=String.fromCharCode(o>>>10&1023|55296),o=56320|1023&o),r+=String.fromCharCode(o),s+=h}return r}function S(t,i,e){for(var r,s,n=0;n55295&&r<57344){if(!s){r>56319||n+1===i.length?(t[e++]=239,t[e++]=191,t[e++]=189):s=r;continue}if(r<56320){t[e++]=239,t[e++]=191,t[e++]=189,s=r;continue}r=s-55296<<10|r-56320|65536,s=null}else s&&(t[e++]=239,t[e++]=191,t[e++]=189,s=null);r<128?t[e++]=r:(r<2048?t[e++]=r>>6|192:(r<65536?t[e++]=r>>12|224:(t[e++]=r>>18|240,t[e++]=r>>12&63|128),t[e++]=r>>6&63|128),t[e++]=63&r|128)}return e}i.exports=r;var B=t("ieee754");r.Varint=0,r.Fixed64=1,r.Bytes=2,r.Fixed32=5;var k=4294967296,P=1/k;r.prototype={destroy:function(){this.buf=null},readFields:function(t,i,e){for(e=e||this.length;this.pos>3,n=this.pos;this.type=7&r,t(s,i,this),this.pos===n&&this.skip(r)}return i},readMessage:function(t,i){return this.readFields(t,i,this.readVarint()+this.pos)},readFixed32:function(){var t=x(this.buf,this.pos);return this.pos+=4,t},readSFixed32:function(){var t=y(this.buf,this.pos);return this.pos+=4,t},readFixed64:function(){var t=x(this.buf,this.pos)+x(this.buf,this.pos+4)*k;return this.pos+=8,t},readSFixed64:function(){var t=x(this.buf,this.pos)+y(this.buf,this.pos+4)*k;return this.pos+=8,t},readFloat:function(){var t=B.read(this.buf,this.pos,!0,23,4);return this.pos+=4,t},readDouble:function(){var t=B.read(this.buf,this.pos,!0,52,8);return this.pos+=8,t},readVarint:function(t){var i,e,r=this.buf;return e=r[this.pos++],i=127&e,e<128?i:(e=r[this.pos++],i|=(127&e)<<7,e<128?i:(e=r[this.pos++],i|=(127&e)<<14,e<128?i:(e=r[this.pos++],i|=(127&e)<<21,e<128?i:(e=r[this.pos],i|=(15&e)<<28,s(i,t,this)))))},readVarint64:function(){return this.readVarint(!0)},readSVarint:function(){var t=this.readVarint();return t%2===1?(t+1)/-2:t/2},readBoolean:function(){return Boolean(this.readVarint())},readString:function(){var t=this.readVarint()+this.pos,i=M(this.buf,this.pos,t);return this.pos=t,i},readBytes:function(){var t=this.readVarint()+this.pos,i=this.buf.subarray(this.pos,t);return this.pos=t,i},readPackedVarint:function(t,i){var e=n(this);for(t=t||[];this.pos127;);else if(i===r.Bytes)this.pos=this.readVarint()+this.pos;else if(i===r.Fixed32)this.pos+=4;else{if(i!==r.Fixed64)throw new Error("Unimplemented type: "+i);this.pos+=8}},writeTag:function(t,i){this.writeVarint(t<<3|i)},realloc:function(t){for(var i=this.length||16;i268435455||t<0?void h(t,this):(this.realloc(4),this.buf[this.pos++]=127&t|(t>127?128:0),void(t<=127||(this.buf[this.pos++]=127&(t>>>=7)|(t>127?128:0),t<=127||(this.buf[this.pos++]=127&(t>>>=7)|(t>127?128:0),t<=127||(this.buf[this.pos++]=t>>>7&127)))))},writeSVarint:function(t){this.writeVarint(t<0?2*-t-1:2*t)},writeBoolean:function(t){this.writeVarint(Boolean(t))},writeString:function(t){t=String(t),this.realloc(4*t.length),this.pos++;var i=this.pos;this.pos=S(this.buf,t,this.pos);var e=this.pos-i;e>=128&&f(i,e,this),this.pos=i-1,this.writeVarint(e),this.pos+=e},writeFloat:function(t){this.realloc(4),B.write(this.buf,t,this.pos,!0,23,4),this.pos+=4},writeDouble:function(t){this.realloc(8),B.write(this.buf,t,this.pos,!0,52,8),this.pos+=8},writeBytes:function(t){var i=t.length;this.writeVarint(i),this.realloc(i);for(var e=0;e=128&&f(e,r,this),this.pos=e-1,this.writeVarint(r),this.pos+=r},writeMessage:function(t,i,e){this.writeTag(t,r.Bytes),this.writeRawMessage(i,e)},writePackedVarint:function(t,i){this.writeMessage(t,d,i)},writePackedSVarint:function(t,i){this.writeMessage(t,p,i)},writePackedBoolean:function(t,i){this.writeMessage(t,w,i)},writePackedFloat:function(t,i){this.writeMessage(t,c,i)},writePackedDouble:function(t,i){this.writeMessage(t,l,i)},writePackedFixed32:function(t,i){this.writeMessage(t,F,i)},writePackedSFixed32:function(t,i){this.writeMessage(t,b,i)},writePackedFixed64:function(t,i){this.writeMessage(t,v,i)},writePackedSFixed64:function(t,i){this.writeMessage(t,g,i)},writeBytesField:function(t,i){this.writeTag(t,r.Bytes),this.writeBytes(i)},writeFixed32Field:function(t,i){this.writeTag(t,r.Fixed32),this.writeFixed32(i)},writeSFixed32Field:function(t,i){this.writeTag(t,r.Fixed32),this.writeSFixed32(i)},writeFixed64Field:function(t,i){this.writeTag(t,r.Fixed64),this.writeFixed64(i)},writeSFixed64Field:function(t,i){this.writeTag(t,r.Fixed64),this.writeSFixed64(i)},writeVarintField:function(t,i){this.writeTag(t,r.Varint),this.writeVarint(i)},writeSVarintField:function(t,i){this.writeTag(t,r.Varint),this.writeSVarint(i)},writeStringField:function(t,i){this.writeTag(t,r.Bytes),this.writeString(i)},writeFloatField:function(t,i){this.writeTag(t,r.Fixed32),this.writeFloat(i)},writeDoubleField:function(t,i){this.writeTag(t,r.Fixed64),this.writeDouble(i)},writeBooleanField:function(t,i){this.writeVarintField(t,Boolean(i))}}},{ieee754:2}],2:[function(t,i,e){e.read=function(t,i,e,r,s){var n,o,h=8*s-r-1,a=(1<>1,f=-7,d=e?s-1:0,p=e?-1:1,c=t[i+d];for(d+=p,n=c&(1<<-f)-1,c>>=-f,f+=h;f>0;n=256*n+t[i+d],d+=p,f-=8);for(o=n&(1<<-f)-1,n>>=-f,f+=r;f>0;o=256*o+t[i+d],d+=p,f-=8);if(0===n)n=1-u;else{if(n===a)return o?NaN:(c?-1:1)*(1/0);o+=Math.pow(2,r),n-=u}return(c?-1:1)*o*Math.pow(2,n-r)},e.write=function(t,i,e,r,s,n){var o,h,a,u=8*n-s-1,f=(1<>1,p=23===s?Math.pow(2,-24)-Math.pow(2,-77):0,c=r?0:n-1,l=r?1:-1,w=i<0||0===i&&1/i<0?1:0;for(i=Math.abs(i),isNaN(i)||i===1/0?(h=isNaN(i)?1:0,o=f):(o=Math.floor(Math.log(i)/Math.LN2),i*(a=Math.pow(2,-o))<1&&(o--,a*=2),i+=o+d>=1?p/a:p*Math.pow(2,1-d),i*a>=2&&(o++,a/=2),o+d>=f?(h=0,o=f):o+d>=1?(h=(i*a-1)*Math.pow(2,s),o+=d):(h=i*Math.pow(2,d-1)*Math.pow(2,s),o=0));s>=8;t[e+c]=255&h,c+=l,h/=256,s-=8);for(o=o<0;t[e+c]=255&o,c+=l,o/=256,u-=8);t[e+c-l]|=128*w}},{}]},{},[1])(1)}); 2 | -------------------------------------------------------------------------------- /server/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "approx" 7 | version = "0.5.0" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "072df7202e63b127ab55acfe16ce97013d5b97bf160489336d3f1840fd78e99e" 10 | dependencies = [ 11 | "num-traits", 12 | ] 13 | 14 | [[package]] 15 | name = "arrayvec" 16 | version = "0.7.1" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "be4dc07131ffa69b8072d35f5007352af944213cde02545e2103680baed38fcd" 19 | 20 | [[package]] 21 | name = "autocfg" 22 | version = "1.0.1" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" 25 | 26 | [[package]] 27 | name = "base-x" 28 | version = "0.2.8" 29 | source = "registry+https://github.com/rust-lang/crates.io-index" 30 | checksum = "a4521f3e3d031370679b3b140beb36dfe4801b09ac77e30c61941f97df3ef28b" 31 | 32 | [[package]] 33 | name = "base64" 34 | version = "0.13.0" 35 | source = "registry+https://github.com/rust-lang/crates.io-index" 36 | checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" 37 | 38 | [[package]] 39 | name = "bit-vec" 40 | version = "0.6.3" 41 | source = "registry+https://github.com/rust-lang/crates.io-index" 42 | checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" 43 | 44 | [[package]] 45 | name = "bitflags" 46 | version = "1.3.2" 47 | source = "registry+https://github.com/rust-lang/crates.io-index" 48 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 49 | 50 | [[package]] 51 | name = "block-buffer" 52 | version = "0.9.0" 53 | source = "registry+https://github.com/rust-lang/crates.io-index" 54 | checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" 55 | dependencies = [ 56 | "generic-array", 57 | ] 58 | 59 | [[package]] 60 | name = "bumpalo" 61 | version = "3.7.0" 62 | source = "registry+https://github.com/rust-lang/crates.io-index" 63 | checksum = "9c59e7af012c713f529e7a3ee57ce9b31ddd858d4b512923602f74608b009631" 64 | 65 | [[package]] 66 | name = "bytemuck" 67 | version = "1.7.2" 68 | source = "registry+https://github.com/rust-lang/crates.io-index" 69 | checksum = "72957246c41db82b8ef88a5486143830adeb8227ef9837740bdec67724cf2c5b" 70 | 71 | [[package]] 72 | name = "byteorder" 73 | version = "1.4.3" 74 | source = "registry+https://github.com/rust-lang/crates.io-index" 75 | checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" 76 | 77 | [[package]] 78 | name = "bytes" 79 | version = "1.1.0" 80 | source = "registry+https://github.com/rust-lang/crates.io-index" 81 | checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" 82 | 83 | [[package]] 84 | name = "cfg-if" 85 | version = "1.0.0" 86 | source = "registry+https://github.com/rust-lang/crates.io-index" 87 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 88 | 89 | [[package]] 90 | name = "const_fn" 91 | version = "0.4.8" 92 | source = "registry+https://github.com/rust-lang/crates.io-index" 93 | checksum = "f92cfa0fd5690b3cf8c1ef2cabbd9b7ef22fa53cf5e1f92b05103f6d5d1cf6e7" 94 | 95 | [[package]] 96 | name = "cpufeatures" 97 | version = "0.2.1" 98 | source = "registry+https://github.com/rust-lang/crates.io-index" 99 | checksum = "95059428f66df56b63431fdb4e1947ed2190586af5c5a8a8b71122bdf5a7f469" 100 | dependencies = [ 101 | "libc", 102 | ] 103 | 104 | [[package]] 105 | name = "crossbeam" 106 | version = "0.8.1" 107 | source = "registry+https://github.com/rust-lang/crates.io-index" 108 | checksum = "4ae5588f6b3c3cb05239e90bd110f257254aecd01e4635400391aeae07497845" 109 | dependencies = [ 110 | "cfg-if", 111 | "crossbeam-channel", 112 | "crossbeam-deque", 113 | "crossbeam-epoch", 114 | "crossbeam-queue", 115 | "crossbeam-utils", 116 | ] 117 | 118 | [[package]] 119 | name = "crossbeam-channel" 120 | version = "0.5.1" 121 | source = "registry+https://github.com/rust-lang/crates.io-index" 122 | checksum = "06ed27e177f16d65f0f0c22a213e17c696ace5dd64b14258b52f9417ccb52db4" 123 | dependencies = [ 124 | "cfg-if", 125 | "crossbeam-utils", 126 | ] 127 | 128 | [[package]] 129 | name = "crossbeam-deque" 130 | version = "0.8.1" 131 | source = "registry+https://github.com/rust-lang/crates.io-index" 132 | checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e" 133 | dependencies = [ 134 | "cfg-if", 135 | "crossbeam-epoch", 136 | "crossbeam-utils", 137 | ] 138 | 139 | [[package]] 140 | name = "crossbeam-epoch" 141 | version = "0.9.5" 142 | source = "registry+https://github.com/rust-lang/crates.io-index" 143 | checksum = "4ec02e091aa634e2c3ada4a392989e7c3116673ef0ac5b72232439094d73b7fd" 144 | dependencies = [ 145 | "cfg-if", 146 | "crossbeam-utils", 147 | "lazy_static", 148 | "memoffset", 149 | "scopeguard", 150 | ] 151 | 152 | [[package]] 153 | name = "crossbeam-queue" 154 | version = "0.3.2" 155 | source = "registry+https://github.com/rust-lang/crates.io-index" 156 | checksum = "9b10ddc024425c88c2ad148c1b0fd53f4c6d38db9697c9f1588381212fa657c9" 157 | dependencies = [ 158 | "cfg-if", 159 | "crossbeam-utils", 160 | ] 161 | 162 | [[package]] 163 | name = "crossbeam-utils" 164 | version = "0.8.5" 165 | source = "registry+https://github.com/rust-lang/crates.io-index" 166 | checksum = "d82cfc11ce7f2c3faef78d8a684447b40d503d9681acebed6cb728d45940c4db" 167 | dependencies = [ 168 | "cfg-if", 169 | "lazy_static", 170 | ] 171 | 172 | [[package]] 173 | name = "digest" 174 | version = "0.9.0" 175 | source = "registry+https://github.com/rust-lang/crates.io-index" 176 | checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" 177 | dependencies = [ 178 | "generic-array", 179 | ] 180 | 181 | [[package]] 182 | name = "discard" 183 | version = "1.0.4" 184 | source = "registry+https://github.com/rust-lang/crates.io-index" 185 | checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0" 186 | 187 | [[package]] 188 | name = "downcast-rs" 189 | version = "1.2.0" 190 | source = "registry+https://github.com/rust-lang/crates.io-index" 191 | checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" 192 | 193 | [[package]] 194 | name = "either" 195 | version = "1.6.1" 196 | source = "registry+https://github.com/rust-lang/crates.io-index" 197 | checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" 198 | 199 | [[package]] 200 | name = "fnv" 201 | version = "1.0.7" 202 | source = "registry+https://github.com/rust-lang/crates.io-index" 203 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 204 | 205 | [[package]] 206 | name = "form_urlencoded" 207 | version = "1.0.1" 208 | source = "registry+https://github.com/rust-lang/crates.io-index" 209 | checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" 210 | dependencies = [ 211 | "matches", 212 | "percent-encoding", 213 | ] 214 | 215 | [[package]] 216 | name = "futures-core" 217 | version = "0.3.17" 218 | source = "registry+https://github.com/rust-lang/crates.io-index" 219 | checksum = "88d1c26957f23603395cd326b0ffe64124b818f4449552f960d815cfba83a53d" 220 | 221 | [[package]] 222 | name = "futures-sink" 223 | version = "0.3.17" 224 | source = "registry+https://github.com/rust-lang/crates.io-index" 225 | checksum = "36ea153c13024fe480590b3e3d4cad89a0cfacecc24577b68f86c6ced9c2bc11" 226 | 227 | [[package]] 228 | name = "futures-task" 229 | version = "0.3.17" 230 | source = "registry+https://github.com/rust-lang/crates.io-index" 231 | checksum = "1d3d00f4eddb73e498a54394f228cd55853bdf059259e8e7bc6e69d408892e99" 232 | 233 | [[package]] 234 | name = "futures-util" 235 | version = "0.3.17" 236 | source = "registry+https://github.com/rust-lang/crates.io-index" 237 | checksum = "36568465210a3a6ee45e1f165136d68671471a501e632e9a98d96872222b5481" 238 | dependencies = [ 239 | "autocfg", 240 | "futures-core", 241 | "futures-sink", 242 | "futures-task", 243 | "pin-project-lite", 244 | "pin-utils", 245 | "slab", 246 | ] 247 | 248 | [[package]] 249 | name = "generic-array" 250 | version = "0.14.4" 251 | source = "registry+https://github.com/rust-lang/crates.io-index" 252 | checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817" 253 | dependencies = [ 254 | "typenum", 255 | "version_check", 256 | ] 257 | 258 | [[package]] 259 | name = "getrandom" 260 | version = "0.2.3" 261 | source = "registry+https://github.com/rust-lang/crates.io-index" 262 | checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" 263 | dependencies = [ 264 | "cfg-if", 265 | "libc", 266 | "wasi", 267 | ] 268 | 269 | [[package]] 270 | name = "hermit-abi" 271 | version = "0.1.19" 272 | source = "registry+https://github.com/rust-lang/crates.io-index" 273 | checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" 274 | dependencies = [ 275 | "libc", 276 | ] 277 | 278 | [[package]] 279 | name = "http" 280 | version = "0.2.4" 281 | source = "registry+https://github.com/rust-lang/crates.io-index" 282 | checksum = "527e8c9ac747e28542699a951517aa9a6945af506cd1f2e1b53a576c17b6cc11" 283 | dependencies = [ 284 | "bytes", 285 | "fnv", 286 | "itoa", 287 | ] 288 | 289 | [[package]] 290 | name = "httparse" 291 | version = "1.5.1" 292 | source = "registry+https://github.com/rust-lang/crates.io-index" 293 | checksum = "acd94fdbe1d4ff688b67b04eee2e17bd50995534a61539e45adfefb45e5e5503" 294 | 295 | [[package]] 296 | name = "idna" 297 | version = "0.2.3" 298 | source = "registry+https://github.com/rust-lang/crates.io-index" 299 | checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" 300 | dependencies = [ 301 | "matches", 302 | "unicode-bidi", 303 | "unicode-normalization", 304 | ] 305 | 306 | [[package]] 307 | name = "instant" 308 | version = "0.1.10" 309 | source = "registry+https://github.com/rust-lang/crates.io-index" 310 | checksum = "bee0328b1209d157ef001c94dd85b4f8f64139adb0eac2659f4b08382b2f474d" 311 | dependencies = [ 312 | "cfg-if", 313 | "time", 314 | ] 315 | 316 | [[package]] 317 | name = "itoa" 318 | version = "0.4.8" 319 | source = "registry+https://github.com/rust-lang/crates.io-index" 320 | checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" 321 | 322 | [[package]] 323 | name = "lazy_static" 324 | version = "1.4.0" 325 | source = "registry+https://github.com/rust-lang/crates.io-index" 326 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 327 | 328 | [[package]] 329 | name = "libc" 330 | version = "0.2.101" 331 | source = "registry+https://github.com/rust-lang/crates.io-index" 332 | checksum = "3cb00336871be5ed2c8ed44b60ae9959dc5b9f08539422ed43f09e34ecaeba21" 333 | 334 | [[package]] 335 | name = "log" 336 | version = "0.4.14" 337 | source = "registry+https://github.com/rust-lang/crates.io-index" 338 | checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" 339 | dependencies = [ 340 | "cfg-if", 341 | ] 342 | 343 | [[package]] 344 | name = "matches" 345 | version = "0.1.9" 346 | source = "registry+https://github.com/rust-lang/crates.io-index" 347 | checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" 348 | 349 | [[package]] 350 | name = "matrixmultiply" 351 | version = "0.3.1" 352 | source = "registry+https://github.com/rust-lang/crates.io-index" 353 | checksum = "5a8a15b776d9dfaecd44b03c5828c2199cddff5247215858aac14624f8d6b741" 354 | dependencies = [ 355 | "rawpointer", 356 | ] 357 | 358 | [[package]] 359 | name = "memchr" 360 | version = "2.4.1" 361 | source = "registry+https://github.com/rust-lang/crates.io-index" 362 | checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" 363 | 364 | [[package]] 365 | name = "memoffset" 366 | version = "0.6.4" 367 | source = "registry+https://github.com/rust-lang/crates.io-index" 368 | checksum = "59accc507f1338036a0477ef61afdae33cde60840f4dfe481319ce3ad116ddf9" 369 | dependencies = [ 370 | "autocfg", 371 | ] 372 | 373 | [[package]] 374 | name = "mio" 375 | version = "0.7.13" 376 | source = "registry+https://github.com/rust-lang/crates.io-index" 377 | checksum = "8c2bdb6314ec10835cd3293dd268473a835c02b7b352e788be788b3c6ca6bb16" 378 | dependencies = [ 379 | "libc", 380 | "log", 381 | "miow", 382 | "ntapi", 383 | "winapi", 384 | ] 385 | 386 | [[package]] 387 | name = "miow" 388 | version = "0.3.7" 389 | source = "registry+https://github.com/rust-lang/crates.io-index" 390 | checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21" 391 | dependencies = [ 392 | "winapi", 393 | ] 394 | 395 | [[package]] 396 | name = "nalgebra" 397 | version = "0.29.0" 398 | source = "registry+https://github.com/rust-lang/crates.io-index" 399 | checksum = "d506eb7e08d6329505faa8a3a00a5dcc6de9f76e0c77e4b75763ae3c770831ff" 400 | dependencies = [ 401 | "approx", 402 | "matrixmultiply", 403 | "nalgebra-macros", 404 | "num-complex", 405 | "num-rational", 406 | "num-traits", 407 | "simba", 408 | "typenum", 409 | ] 410 | 411 | [[package]] 412 | name = "nalgebra-macros" 413 | version = "0.1.0" 414 | source = "registry+https://github.com/rust-lang/crates.io-index" 415 | checksum = "01fcc0b8149b4632adc89ac3b7b31a12fb6099a0317a4eb2ebff574ef7de7218" 416 | dependencies = [ 417 | "proc-macro2", 418 | "quote", 419 | "syn", 420 | ] 421 | 422 | [[package]] 423 | name = "ntapi" 424 | version = "0.3.6" 425 | source = "registry+https://github.com/rust-lang/crates.io-index" 426 | checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44" 427 | dependencies = [ 428 | "winapi", 429 | ] 430 | 431 | [[package]] 432 | name = "num-complex" 433 | version = "0.4.0" 434 | source = "registry+https://github.com/rust-lang/crates.io-index" 435 | checksum = "26873667bbbb7c5182d4a37c1add32cdf09f841af72da53318fdb81543c15085" 436 | dependencies = [ 437 | "num-traits", 438 | ] 439 | 440 | [[package]] 441 | name = "num-derive" 442 | version = "0.3.3" 443 | source = "registry+https://github.com/rust-lang/crates.io-index" 444 | checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" 445 | dependencies = [ 446 | "proc-macro2", 447 | "quote", 448 | "syn", 449 | ] 450 | 451 | [[package]] 452 | name = "num-integer" 453 | version = "0.1.44" 454 | source = "registry+https://github.com/rust-lang/crates.io-index" 455 | checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" 456 | dependencies = [ 457 | "autocfg", 458 | "num-traits", 459 | ] 460 | 461 | [[package]] 462 | name = "num-rational" 463 | version = "0.4.0" 464 | source = "registry+https://github.com/rust-lang/crates.io-index" 465 | checksum = "d41702bd167c2df5520b384281bc111a4b5efcf7fbc4c9c222c815b07e0a6a6a" 466 | dependencies = [ 467 | "autocfg", 468 | "num-integer", 469 | "num-traits", 470 | ] 471 | 472 | [[package]] 473 | name = "num-traits" 474 | version = "0.2.14" 475 | source = "registry+https://github.com/rust-lang/crates.io-index" 476 | checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" 477 | dependencies = [ 478 | "autocfg", 479 | ] 480 | 481 | [[package]] 482 | name = "num_cpus" 483 | version = "1.13.0" 484 | source = "registry+https://github.com/rust-lang/crates.io-index" 485 | checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" 486 | dependencies = [ 487 | "hermit-abi", 488 | "libc", 489 | ] 490 | 491 | [[package]] 492 | name = "opaque-debug" 493 | version = "0.3.0" 494 | source = "registry+https://github.com/rust-lang/crates.io-index" 495 | checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" 496 | 497 | [[package]] 498 | name = "parry2d" 499 | version = "0.7.0" 500 | source = "registry+https://github.com/rust-lang/crates.io-index" 501 | checksum = "e4399c1e027e181dab9fa32362279772d8e8ad787d87670bd078487d07a13ae3" 502 | dependencies = [ 503 | "approx", 504 | "arrayvec", 505 | "bitflags", 506 | "downcast-rs", 507 | "either", 508 | "nalgebra", 509 | "num-derive", 510 | "num-traits", 511 | "rustc-hash", 512 | "simba", 513 | "slab", 514 | "smallvec", 515 | ] 516 | 517 | [[package]] 518 | name = "paste" 519 | version = "1.0.5" 520 | source = "registry+https://github.com/rust-lang/crates.io-index" 521 | checksum = "acbf547ad0c65e31259204bd90935776d1c693cec2f4ff7abb7a1bbbd40dfe58" 522 | 523 | [[package]] 524 | name = "percent-encoding" 525 | version = "2.1.0" 526 | source = "registry+https://github.com/rust-lang/crates.io-index" 527 | checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" 528 | 529 | [[package]] 530 | name = "pin-project" 531 | version = "1.0.8" 532 | source = "registry+https://github.com/rust-lang/crates.io-index" 533 | checksum = "576bc800220cc65dac09e99e97b08b358cfab6e17078de8dc5fee223bd2d0c08" 534 | dependencies = [ 535 | "pin-project-internal", 536 | ] 537 | 538 | [[package]] 539 | name = "pin-project-internal" 540 | version = "1.0.8" 541 | source = "registry+https://github.com/rust-lang/crates.io-index" 542 | checksum = "6e8fe8163d14ce7f0cdac2e040116f22eac817edabff0be91e8aff7e9accf389" 543 | dependencies = [ 544 | "proc-macro2", 545 | "quote", 546 | "syn", 547 | ] 548 | 549 | [[package]] 550 | name = "pin-project-lite" 551 | version = "0.2.7" 552 | source = "registry+https://github.com/rust-lang/crates.io-index" 553 | checksum = "8d31d11c69a6b52a174b42bdc0c30e5e11670f90788b2c471c31c1d17d449443" 554 | 555 | [[package]] 556 | name = "pin-utils" 557 | version = "0.1.0" 558 | source = "registry+https://github.com/rust-lang/crates.io-index" 559 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 560 | 561 | [[package]] 562 | name = "ppv-lite86" 563 | version = "0.2.10" 564 | source = "registry+https://github.com/rust-lang/crates.io-index" 565 | checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" 566 | 567 | [[package]] 568 | name = "proc-macro-hack" 569 | version = "0.5.19" 570 | source = "registry+https://github.com/rust-lang/crates.io-index" 571 | checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" 572 | 573 | [[package]] 574 | name = "proc-macro2" 575 | version = "1.0.29" 576 | source = "registry+https://github.com/rust-lang/crates.io-index" 577 | checksum = "b9f5105d4fdaab20335ca9565e106a5d9b82b6219b5ba735731124ac6711d23d" 578 | dependencies = [ 579 | "unicode-xid", 580 | ] 581 | 582 | [[package]] 583 | name = "quick-protobuf" 584 | version = "0.8.0" 585 | source = "git+https://github.com/Descrout/quick-protobuf#862e42b34440477bb9ae4bebb14b851768c1ef50" 586 | dependencies = [ 587 | "byteorder", 588 | ] 589 | 590 | [[package]] 591 | name = "quote" 592 | version = "1.0.9" 593 | source = "registry+https://github.com/rust-lang/crates.io-index" 594 | checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" 595 | dependencies = [ 596 | "proc-macro2", 597 | ] 598 | 599 | [[package]] 600 | name = "rand" 601 | version = "0.8.4" 602 | source = "registry+https://github.com/rust-lang/crates.io-index" 603 | checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8" 604 | dependencies = [ 605 | "libc", 606 | "rand_chacha", 607 | "rand_core", 608 | "rand_hc", 609 | ] 610 | 611 | [[package]] 612 | name = "rand_chacha" 613 | version = "0.3.1" 614 | source = "registry+https://github.com/rust-lang/crates.io-index" 615 | checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" 616 | dependencies = [ 617 | "ppv-lite86", 618 | "rand_core", 619 | ] 620 | 621 | [[package]] 622 | name = "rand_core" 623 | version = "0.6.3" 624 | source = "registry+https://github.com/rust-lang/crates.io-index" 625 | checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" 626 | dependencies = [ 627 | "getrandom", 628 | ] 629 | 630 | [[package]] 631 | name = "rand_hc" 632 | version = "0.3.1" 633 | source = "registry+https://github.com/rust-lang/crates.io-index" 634 | checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7" 635 | dependencies = [ 636 | "rand_core", 637 | ] 638 | 639 | [[package]] 640 | name = "rapier2d" 641 | version = "0.11.0" 642 | source = "registry+https://github.com/rust-lang/crates.io-index" 643 | checksum = "4e922bd6a0c765834ce190410286fe9e832dc8df452634e3b6e6db9172e65bd8" 644 | dependencies = [ 645 | "approx", 646 | "arrayvec", 647 | "bit-vec", 648 | "bitflags", 649 | "crossbeam", 650 | "downcast-rs", 651 | "instant", 652 | "nalgebra", 653 | "num-derive", 654 | "num-traits", 655 | "parry2d", 656 | "rustc-hash", 657 | "simba", 658 | "vec_map", 659 | ] 660 | 661 | [[package]] 662 | name = "rawpointer" 663 | version = "0.2.1" 664 | source = "registry+https://github.com/rust-lang/crates.io-index" 665 | checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" 666 | 667 | [[package]] 668 | name = "rustc-hash" 669 | version = "1.1.0" 670 | source = "registry+https://github.com/rust-lang/crates.io-index" 671 | checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" 672 | 673 | [[package]] 674 | name = "rustc_version" 675 | version = "0.2.3" 676 | source = "registry+https://github.com/rust-lang/crates.io-index" 677 | checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" 678 | dependencies = [ 679 | "semver", 680 | ] 681 | 682 | [[package]] 683 | name = "ryu" 684 | version = "1.0.5" 685 | source = "registry+https://github.com/rust-lang/crates.io-index" 686 | checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" 687 | 688 | [[package]] 689 | name = "safe_arch" 690 | version = "0.6.0" 691 | source = "registry+https://github.com/rust-lang/crates.io-index" 692 | checksum = "794821e4ccb0d9f979512f9c1973480123f9bd62a90d74ab0f9426fcf8f4a529" 693 | dependencies = [ 694 | "bytemuck", 695 | ] 696 | 697 | [[package]] 698 | name = "scopeguard" 699 | version = "1.1.0" 700 | source = "registry+https://github.com/rust-lang/crates.io-index" 701 | checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" 702 | 703 | [[package]] 704 | name = "semver" 705 | version = "0.9.0" 706 | source = "registry+https://github.com/rust-lang/crates.io-index" 707 | checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" 708 | dependencies = [ 709 | "semver-parser", 710 | ] 711 | 712 | [[package]] 713 | name = "semver-parser" 714 | version = "0.7.0" 715 | source = "registry+https://github.com/rust-lang/crates.io-index" 716 | checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" 717 | 718 | [[package]] 719 | name = "serde" 720 | version = "1.0.130" 721 | source = "registry+https://github.com/rust-lang/crates.io-index" 722 | checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913" 723 | 724 | [[package]] 725 | name = "serde_derive" 726 | version = "1.0.130" 727 | source = "registry+https://github.com/rust-lang/crates.io-index" 728 | checksum = "d7bc1a1ab1961464eae040d96713baa5a724a8152c1222492465b54322ec508b" 729 | dependencies = [ 730 | "proc-macro2", 731 | "quote", 732 | "syn", 733 | ] 734 | 735 | [[package]] 736 | name = "serde_json" 737 | version = "1.0.67" 738 | source = "registry+https://github.com/rust-lang/crates.io-index" 739 | checksum = "a7f9e390c27c3c0ce8bc5d725f6e4d30a29d26659494aa4b17535f7522c5c950" 740 | dependencies = [ 741 | "itoa", 742 | "ryu", 743 | "serde", 744 | ] 745 | 746 | [[package]] 747 | name = "sha-1" 748 | version = "0.9.8" 749 | source = "registry+https://github.com/rust-lang/crates.io-index" 750 | checksum = "99cd6713db3cf16b6c84e06321e049a9b9f699826e16096d23bbcc44d15d51a6" 751 | dependencies = [ 752 | "block-buffer", 753 | "cfg-if", 754 | "cpufeatures", 755 | "digest", 756 | "opaque-debug", 757 | ] 758 | 759 | [[package]] 760 | name = "sha1" 761 | version = "0.6.0" 762 | source = "registry+https://github.com/rust-lang/crates.io-index" 763 | checksum = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d" 764 | 765 | [[package]] 766 | name = "simba" 767 | version = "0.6.0" 768 | source = "registry+https://github.com/rust-lang/crates.io-index" 769 | checksum = "f0b7840f121a46d63066ee7a99fc81dcabbc6105e437cae43528cea199b5a05f" 770 | dependencies = [ 771 | "approx", 772 | "num-complex", 773 | "num-traits", 774 | "paste", 775 | "wide", 776 | ] 777 | 778 | [[package]] 779 | name = "slab" 780 | version = "0.4.4" 781 | source = "registry+https://github.com/rust-lang/crates.io-index" 782 | checksum = "c307a32c1c5c437f38c7fd45d753050587732ba8628319fbdf12a7e289ccc590" 783 | 784 | [[package]] 785 | name = "smallvec" 786 | version = "1.6.1" 787 | source = "registry+https://github.com/rust-lang/crates.io-index" 788 | checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e" 789 | 790 | [[package]] 791 | name = "standback" 792 | version = "0.2.17" 793 | source = "registry+https://github.com/rust-lang/crates.io-index" 794 | checksum = "e113fb6f3de07a243d434a56ec6f186dfd51cb08448239fe7bcae73f87ff28ff" 795 | dependencies = [ 796 | "version_check", 797 | ] 798 | 799 | [[package]] 800 | name = "stdweb" 801 | version = "0.4.20" 802 | source = "registry+https://github.com/rust-lang/crates.io-index" 803 | checksum = "d022496b16281348b52d0e30ae99e01a73d737b2f45d38fed4edf79f9325a1d5" 804 | dependencies = [ 805 | "discard", 806 | "rustc_version", 807 | "stdweb-derive", 808 | "stdweb-internal-macros", 809 | "stdweb-internal-runtime", 810 | "wasm-bindgen", 811 | ] 812 | 813 | [[package]] 814 | name = "stdweb-derive" 815 | version = "0.5.3" 816 | source = "registry+https://github.com/rust-lang/crates.io-index" 817 | checksum = "c87a60a40fccc84bef0652345bbbbbe20a605bf5d0ce81719fc476f5c03b50ef" 818 | dependencies = [ 819 | "proc-macro2", 820 | "quote", 821 | "serde", 822 | "serde_derive", 823 | "syn", 824 | ] 825 | 826 | [[package]] 827 | name = "stdweb-internal-macros" 828 | version = "0.2.9" 829 | source = "registry+https://github.com/rust-lang/crates.io-index" 830 | checksum = "58fa5ff6ad0d98d1ffa8cb115892b6e69d67799f6763e162a1c9db421dc22e11" 831 | dependencies = [ 832 | "base-x", 833 | "proc-macro2", 834 | "quote", 835 | "serde", 836 | "serde_derive", 837 | "serde_json", 838 | "sha1", 839 | "syn", 840 | ] 841 | 842 | [[package]] 843 | name = "stdweb-internal-runtime" 844 | version = "0.1.5" 845 | source = "registry+https://github.com/rust-lang/crates.io-index" 846 | checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0" 847 | 848 | [[package]] 849 | name = "syn" 850 | version = "1.0.76" 851 | source = "registry+https://github.com/rust-lang/crates.io-index" 852 | checksum = "c6f107db402c2c2055242dbf4d2af0e69197202e9faacbef9571bbe47f5a1b84" 853 | dependencies = [ 854 | "proc-macro2", 855 | "quote", 856 | "unicode-xid", 857 | ] 858 | 859 | [[package]] 860 | name = "thiserror" 861 | version = "1.0.29" 862 | source = "registry+https://github.com/rust-lang/crates.io-index" 863 | checksum = "602eca064b2d83369e2b2f34b09c70b605402801927c65c11071ac911d299b88" 864 | dependencies = [ 865 | "thiserror-impl", 866 | ] 867 | 868 | [[package]] 869 | name = "thiserror-impl" 870 | version = "1.0.29" 871 | source = "registry+https://github.com/rust-lang/crates.io-index" 872 | checksum = "bad553cc2c78e8de258400763a647e80e6d1b31ee237275d756f6836d204494c" 873 | dependencies = [ 874 | "proc-macro2", 875 | "quote", 876 | "syn", 877 | ] 878 | 879 | [[package]] 880 | name = "time" 881 | version = "0.2.27" 882 | source = "registry+https://github.com/rust-lang/crates.io-index" 883 | checksum = "4752a97f8eebd6854ff91f1c1824cd6160626ac4bd44287f7f4ea2035a02a242" 884 | dependencies = [ 885 | "const_fn", 886 | "libc", 887 | "standback", 888 | "stdweb", 889 | "time-macros", 890 | "version_check", 891 | "winapi", 892 | ] 893 | 894 | [[package]] 895 | name = "time-macros" 896 | version = "0.1.1" 897 | source = "registry+https://github.com/rust-lang/crates.io-index" 898 | checksum = "957e9c6e26f12cb6d0dd7fc776bb67a706312e7299aed74c8dd5b17ebb27e2f1" 899 | dependencies = [ 900 | "proc-macro-hack", 901 | "time-macros-impl", 902 | ] 903 | 904 | [[package]] 905 | name = "time-macros-impl" 906 | version = "0.1.2" 907 | source = "registry+https://github.com/rust-lang/crates.io-index" 908 | checksum = "fd3c141a1b43194f3f56a1411225df8646c55781d5f26db825b3d98507eb482f" 909 | dependencies = [ 910 | "proc-macro-hack", 911 | "proc-macro2", 912 | "quote", 913 | "standback", 914 | "syn", 915 | ] 916 | 917 | [[package]] 918 | name = "tinyvec" 919 | version = "1.3.1" 920 | source = "registry+https://github.com/rust-lang/crates.io-index" 921 | checksum = "848a1e1181b9f6753b5e96a092749e29b11d19ede67dfbbd6c7dc7e0f49b5338" 922 | dependencies = [ 923 | "tinyvec_macros", 924 | ] 925 | 926 | [[package]] 927 | name = "tinyvec_macros" 928 | version = "0.1.0" 929 | source = "registry+https://github.com/rust-lang/crates.io-index" 930 | checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" 931 | 932 | [[package]] 933 | name = "tokio" 934 | version = "1.11.0" 935 | source = "registry+https://github.com/rust-lang/crates.io-index" 936 | checksum = "b4efe6fc2395938c8155973d7be49fe8d03a843726e285e100a8a383cc0154ce" 937 | dependencies = [ 938 | "autocfg", 939 | "bytes", 940 | "libc", 941 | "memchr", 942 | "mio", 943 | "num_cpus", 944 | "pin-project-lite", 945 | "tokio-macros", 946 | "winapi", 947 | ] 948 | 949 | [[package]] 950 | name = "tokio-macros" 951 | version = "1.3.0" 952 | source = "registry+https://github.com/rust-lang/crates.io-index" 953 | checksum = "54473be61f4ebe4efd09cec9bd5d16fa51d70ea0192213d754d2d500457db110" 954 | dependencies = [ 955 | "proc-macro2", 956 | "quote", 957 | "syn", 958 | ] 959 | 960 | [[package]] 961 | name = "tokio-tungstenite" 962 | version = "0.15.0" 963 | source = "registry+https://github.com/rust-lang/crates.io-index" 964 | checksum = "511de3f85caf1c98983545490c3d09685fa8eb634e57eec22bb4db271f46cbd8" 965 | dependencies = [ 966 | "futures-util", 967 | "log", 968 | "pin-project", 969 | "tokio", 970 | "tungstenite", 971 | ] 972 | 973 | [[package]] 974 | name = "tungstenite" 975 | version = "0.14.0" 976 | source = "registry+https://github.com/rust-lang/crates.io-index" 977 | checksum = "a0b2d8558abd2e276b0a8df5c05a2ec762609344191e5fd23e292c910e9165b5" 978 | dependencies = [ 979 | "base64", 980 | "byteorder", 981 | "bytes", 982 | "http", 983 | "httparse", 984 | "log", 985 | "rand", 986 | "sha-1", 987 | "thiserror", 988 | "url", 989 | "utf-8", 990 | ] 991 | 992 | [[package]] 993 | name = "typenum" 994 | version = "1.14.0" 995 | source = "registry+https://github.com/rust-lang/crates.io-index" 996 | checksum = "b63708a265f51345575b27fe43f9500ad611579e764c79edbc2037b1121959ec" 997 | 998 | [[package]] 999 | name = "unicode-bidi" 1000 | version = "0.3.6" 1001 | source = "registry+https://github.com/rust-lang/crates.io-index" 1002 | checksum = "246f4c42e67e7a4e3c6106ff716a5d067d4132a642840b242e357e468a2a0085" 1003 | 1004 | [[package]] 1005 | name = "unicode-normalization" 1006 | version = "0.1.19" 1007 | source = "registry+https://github.com/rust-lang/crates.io-index" 1008 | checksum = "d54590932941a9e9266f0832deed84ebe1bf2e4c9e4a3554d393d18f5e854bf9" 1009 | dependencies = [ 1010 | "tinyvec", 1011 | ] 1012 | 1013 | [[package]] 1014 | name = "unicode-xid" 1015 | version = "0.2.2" 1016 | source = "registry+https://github.com/rust-lang/crates.io-index" 1017 | checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" 1018 | 1019 | [[package]] 1020 | name = "url" 1021 | version = "2.2.2" 1022 | source = "registry+https://github.com/rust-lang/crates.io-index" 1023 | checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" 1024 | dependencies = [ 1025 | "form_urlencoded", 1026 | "idna", 1027 | "matches", 1028 | "percent-encoding", 1029 | ] 1030 | 1031 | [[package]] 1032 | name = "utf-8" 1033 | version = "0.7.6" 1034 | source = "registry+https://github.com/rust-lang/crates.io-index" 1035 | checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" 1036 | 1037 | [[package]] 1038 | name = "vec_map" 1039 | version = "0.8.2" 1040 | source = "registry+https://github.com/rust-lang/crates.io-index" 1041 | checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" 1042 | 1043 | [[package]] 1044 | name = "version_check" 1045 | version = "0.9.3" 1046 | source = "registry+https://github.com/rust-lang/crates.io-index" 1047 | checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" 1048 | 1049 | [[package]] 1050 | name = "wasi" 1051 | version = "0.10.2+wasi-snapshot-preview1" 1052 | source = "registry+https://github.com/rust-lang/crates.io-index" 1053 | checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" 1054 | 1055 | [[package]] 1056 | name = "wasm-bindgen" 1057 | version = "0.2.76" 1058 | source = "registry+https://github.com/rust-lang/crates.io-index" 1059 | checksum = "8ce9b1b516211d33767048e5d47fa2a381ed8b76fc48d2ce4aa39877f9f183e0" 1060 | dependencies = [ 1061 | "cfg-if", 1062 | "wasm-bindgen-macro", 1063 | ] 1064 | 1065 | [[package]] 1066 | name = "wasm-bindgen-backend" 1067 | version = "0.2.76" 1068 | source = "registry+https://github.com/rust-lang/crates.io-index" 1069 | checksum = "cfe8dc78e2326ba5f845f4b5bf548401604fa20b1dd1d365fb73b6c1d6364041" 1070 | dependencies = [ 1071 | "bumpalo", 1072 | "lazy_static", 1073 | "log", 1074 | "proc-macro2", 1075 | "quote", 1076 | "syn", 1077 | "wasm-bindgen-shared", 1078 | ] 1079 | 1080 | [[package]] 1081 | name = "wasm-bindgen-macro" 1082 | version = "0.2.76" 1083 | source = "registry+https://github.com/rust-lang/crates.io-index" 1084 | checksum = "44468aa53335841d9d6b6c023eaab07c0cd4bddbcfdee3e2bb1e8d2cb8069fef" 1085 | dependencies = [ 1086 | "quote", 1087 | "wasm-bindgen-macro-support", 1088 | ] 1089 | 1090 | [[package]] 1091 | name = "wasm-bindgen-macro-support" 1092 | version = "0.2.76" 1093 | source = "registry+https://github.com/rust-lang/crates.io-index" 1094 | checksum = "0195807922713af1e67dc66132c7328206ed9766af3858164fb583eedc25fbad" 1095 | dependencies = [ 1096 | "proc-macro2", 1097 | "quote", 1098 | "syn", 1099 | "wasm-bindgen-backend", 1100 | "wasm-bindgen-shared", 1101 | ] 1102 | 1103 | [[package]] 1104 | name = "wasm-bindgen-shared" 1105 | version = "0.2.76" 1106 | source = "registry+https://github.com/rust-lang/crates.io-index" 1107 | checksum = "acdb075a845574a1fa5f09fd77e43f7747599301ea3417a9fbffdeedfc1f4a29" 1108 | 1109 | [[package]] 1110 | name = "websocket_physics" 1111 | version = "0.1.0" 1112 | dependencies = [ 1113 | "futures-util", 1114 | "quick-protobuf", 1115 | "rapier2d", 1116 | "tokio", 1117 | "tokio-tungstenite", 1118 | ] 1119 | 1120 | [[package]] 1121 | name = "wide" 1122 | version = "0.7.0" 1123 | source = "registry+https://github.com/rust-lang/crates.io-index" 1124 | checksum = "cd89cf484471f953ee84f07c0dff0ea20e9ddf976f03cabdf5dda48b221f22e7" 1125 | dependencies = [ 1126 | "bytemuck", 1127 | "safe_arch", 1128 | ] 1129 | 1130 | [[package]] 1131 | name = "winapi" 1132 | version = "0.3.9" 1133 | source = "registry+https://github.com/rust-lang/crates.io-index" 1134 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 1135 | dependencies = [ 1136 | "winapi-i686-pc-windows-gnu", 1137 | "winapi-x86_64-pc-windows-gnu", 1138 | ] 1139 | 1140 | [[package]] 1141 | name = "winapi-i686-pc-windows-gnu" 1142 | version = "0.4.0" 1143 | source = "registry+https://github.com/rust-lang/crates.io-index" 1144 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 1145 | 1146 | [[package]] 1147 | name = "winapi-x86_64-pc-windows-gnu" 1148 | version = "0.4.0" 1149 | source = "registry+https://github.com/rust-lang/crates.io-index" 1150 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 1151 | --------------------------------------------------------------------------------