├── README.md ├── generate_map.py ├── map-bw.png ├── maze_gameserver ├── Cargo.toml ├── maze.map └── src │ ├── aluminium │ ├── antiddos.rs │ ├── clientman.rs │ ├── globaleventqueue.rs │ ├── listener.rs │ └── mod.rs │ ├── gamelogic │ ├── antispam.rs │ ├── characterstate.rs │ ├── clientkey.rs │ ├── identity.rs │ ├── map.rs │ ├── mod.rs │ ├── packets.rs │ ├── rabbitcolor.rs │ ├── raceman.rs │ ├── score.rs │ ├── scoreboard_manager.rs │ ├── types.rs │ └── unlocks.rs │ └── main.rs ├── maze_masterserver ├── Cargo.toml └── src │ ├── characterstate.rs │ ├── clientkey.rs │ ├── main.rs │ ├── rabbitcolor.rs │ ├── score.rs │ └── unlocks.rs └── whiterabbit_bot ├── whiterabbit.py ├── whiterabbit_data └── whiterabbit_get_there_data /README.md: -------------------------------------------------------------------------------- 1 | # Maze Server 2 | 3 | An experimental server for LiveOverflow's game "Maze", which was developed as part of the CSCG 2020. 4 | If everything goes to plan, the code in this repository will soon be run on a DigitalOcean server for everyone interested to try out. 5 | 6 | ## Update 7 | 8 | Unfortunately, there is no public server anymore, but you can still run one locally or even publicly yourself. 9 | 10 | ## Features 11 | 12 | - [x] Basic functionality of the game such as moving around and showing emojis 13 | - [x] Multiplayer support for the features above 14 | - [x] Unlocking of emojis and places 15 | - [x] Fur colors 16 | - [x] Teleporting to places 17 | - [x] "Anti Cheat" for passing through walls 18 | - [x] "Anti Cheat" for speed hax 19 | - [x] Dying due to lava 20 | - [x] Race support 21 | - [x] Flags 22 | - [x] White Rabbit Bot 23 | - [x] Highscore board 24 | - [ ] Proper Anti-DDos 25 | 26 | 27 | ## Instructions for running locally 28 | 29 | Please clone the repository and build & run the the two projects inside with `cargo run --release`. 30 | Please note that you need the nightly version of rustc to compile the master server. 31 | If you are on Linux, run these two commands to redirect the traffic to the locally running servers : 32 | 33 | `sudo iptables -t nat -I OUTPUT -p udp -d 147.75.85.99 --dport 1337:1357 -j DNAT --to-destination 127.0.0.1:1337` 34 | `sudo iptables -t nat -I OUTPUT -p tcp -d 147.75.85.99 --dport 80 -j DNAT --to-destination 127.0.0.1:8000` 35 | 36 | -------------------------------------------------------------------------------- /generate_map.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | import cv2 as cv 3 | import numpy as np 4 | from collections import deque as deque 5 | 6 | # this is basically because I wasn't able to get opencv imported into the rust project 7 | 8 | img = cv.imread("map-bw.png",0) 9 | (rows,cols) = img.shape 10 | 11 | stack = [] 12 | 13 | temp_array = bytearray() 14 | 15 | for i in range(rows): 16 | for j in range(cols): 17 | temp_array.append(img[i,j]) 18 | stack.append(temp_array) 19 | temp_array = bytearray() 20 | 21 | stack.reverse() 22 | 23 | final = bytearray() 24 | 25 | for row in stack: 26 | for px in row: 27 | final.append(px) 28 | 29 | open("maze_gameserver/maze.map", "wb+").write(final) 30 | -------------------------------------------------------------------------------- /map-bw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pluxtore/maze-server/5e02137e061315b7b86fb9e7052f2b654b6b66eb/map-bw.png -------------------------------------------------------------------------------- /maze_gameserver/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "aluminium" 3 | version = "0.1.0" 4 | authors = ["pluxtore "] 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 | threadpool = "1.8.1" 11 | serde = {version = "1.0.114", features = ["derive"]} 12 | serde_derive = "1.0.114" 13 | hex = "0.4.2" 14 | log = "0.4.11" 15 | bincode = "1.0" 16 | fern = {version = "0.5", features = ["colored"]} 17 | chrono = "0.4.13" 18 | rand = "0.7.3" 19 | byteorder = "1.3.4" 20 | lazy_static = "1.4.0" 21 | -------------------------------------------------------------------------------- /maze_gameserver/maze.map: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pluxtore/maze-server/5e02137e061315b7b86fb9e7052f2b654b6b66eb/maze_gameserver/maze.map -------------------------------------------------------------------------------- /maze_gameserver/src/aluminium/antiddos.rs: -------------------------------------------------------------------------------- 1 | use std::net::IpAddr; 2 | use std::collections::HashMap; 3 | use std::process::Command; 4 | 5 | 6 | struct AntiDdos { 7 | suspicious : HashMap, 8 | } 9 | 10 | impl AntiDdos { 11 | pub fn evaluate(&mut self,ip : IpAddr) { 12 | if self.suspicious.contains_key(&ip) { 13 | *self.suspicious.get_mut(&ip).unwrap() += 1; 14 | if self.suspicious.get_mut(&ip).unwrap() > &mut 100 { 15 | let mut cmd = "block ".to_string(); 16 | cmd.push_str(ip.to_string().as_str()); 17 | Command::new(cmd); 18 | } 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /maze_gameserver/src/aluminium/clientman.rs: -------------------------------------------------------------------------------- 1 | use crate::gamelogic::{self,Logic,clientkey::ClientKey}; 2 | use std::collections::{HashMap}; 3 | use std::net::{SocketAddr, UdpSocket}; 4 | use std::sync::mpsc::{self, Receiver, Sender}; 5 | use std::sync::{Arc, Mutex}; 6 | use std::thread; 7 | use std::time::Duration; 8 | use super::globaleventqueue::GlobalEventQueue; 9 | 10 | pub struct Message { 11 | pub socket: UdpSocket, 12 | pub peer_addr: SocketAddr, 13 | pub payload: Vec, 14 | } 15 | 16 | impl Message { 17 | pub fn new(socket: UdpSocket, peer_addr: SocketAddr, payload: Vec) -> Self { 18 | Self { 19 | socket: socket, 20 | peer_addr: peer_addr, 21 | payload: payload, 22 | } 23 | } 24 | } 25 | 26 | 27 | impl Clone for Message { 28 | fn clone(&self) -> Self { 29 | let newsocket = match self.socket.try_clone() { 30 | Ok(e) => { 31 | e 32 | } 33 | _ => { 34 | log::error!("fatal error : could not clone socket"); 35 | panic!("fatal error : could not clone socket"); 36 | } 37 | }; 38 | 39 | 40 | Self { 41 | socket : newsocket, 42 | peer_addr : self.peer_addr.clone(), 43 | payload : self.payload.clone(), 44 | } 45 | } 46 | } 47 | 48 | pub struct ClientManager { 49 | receiver: Receiver, 50 | pool: ClientPool, 51 | feedback: Sender, 52 | } 53 | impl ClientManager { 54 | /*constructor for client manager*/ 55 | pub fn new(receiver: Receiver, feedback: Sender) -> Self { 56 | log::info!("new ClientManager instance created"); 57 | Self { 58 | receiver: receiver, 59 | pool: ClientPool::new(), 60 | feedback: feedback, 61 | } 62 | } 63 | /* runmethod that is executed as worker thread, does not terminate ! */ 64 | pub fn run(&mut self) -> ! { 65 | log::info!("ClientManager entering runmethod"); 66 | loop { 67 | for msg in self.receiver.recv() { 68 | loop { 69 | match self.pool.shutdown_endpoint.try_recv() { 70 | Ok(e) => { 71 | self.pool.client_endpoints.remove(&e); 72 | log::info!("removed client {:x?} out of in-memory dict", e) 73 | } 74 | _ => break, 75 | }; 76 | } 77 | self.pool.route(msg); 78 | } 79 | } 80 | } 81 | } 82 | pub struct ClientPool { 83 | /* route packets to clients */ 84 | client_endpoints: HashMap>, 85 | global_event_queue: Arc>, 86 | shutdown_endpoint: Receiver, 87 | shutdown_sender: Sender, 88 | } 89 | 90 | impl ClientPool { 91 | pub fn new() -> Self { 92 | log::info!("new ClientPool instance created"); 93 | let (tx, rx) = mpsc::channel(); 94 | Self { 95 | client_endpoints: HashMap::new(), 96 | global_event_queue: Arc::new(Mutex::new(GlobalEventQueue::new())), 97 | shutdown_endpoint: rx, 98 | shutdown_sender: tx, 99 | } 100 | } 101 | pub fn route(&mut self, message: Message) { 102 | let payload_tmp = message.payload.clone(); 103 | match self.client_endpoints.get(&Logic::pkt_get_client_key(&payload_tmp)) { 104 | Some(e) => { 105 | match e.send(message) { 106 | Ok(_) => (), 107 | Err(_) => { 108 | log::warn!("could not route packet to client receiver, shutting down endpoint"); 109 | self.client_endpoints.remove(&Logic::pkt_get_client_key(&payload_tmp)); 110 | return; 111 | } 112 | }; 113 | } 114 | None => { 115 | self.add_client(message); 116 | } 117 | } 118 | } 119 | fn add_client(&mut self, message: Message) { 120 | let (tx, rx) = mpsc::channel(); 121 | let peer_addr = message.peer_addr; 122 | 123 | let logic = match Logic::new(message) { 124 | Some(e) => e, 125 | None => { 126 | log::warn!("{} sent invalid handshake", peer_addr); 127 | return 128 | }, 129 | }; 130 | 131 | let key = logic.get_identitiy().get_key().clone(); 132 | let _key = key.clone(); 133 | 134 | let geq = self.global_event_queue.clone(); 135 | self.client_endpoints.insert( _key, tx); 136 | geq.lock().unwrap().add_client(logic.get_identitiy().get_key().clone()); 137 | 138 | client_init( 139 | logic, 140 | rx, 141 | geq, 142 | self.shutdown_sender.clone(), 143 | ); 144 | 145 | } 146 | } 147 | 148 | pub fn client_init( 149 | mut logic: Logic, 150 | receiver: Receiver, 151 | geq: Arc>, 152 | shutdown_tx: Sender, 153 | ) { 154 | thread::spawn(move || { 155 | /* event loop*/ 156 | let mut timeout = 0; 157 | 158 | loop { 159 | let update = receiver.recv_timeout(Duration::from_millis(timeout)); 160 | 161 | if update.is_ok() { 162 | let update = update.unwrap(); 163 | 164 | if update.peer_addr.ip()!=logic.get_identitiy().peer_addr.ip() { 165 | break; 166 | } 167 | 168 | match logic.inbound_parse(update) { 169 | ParseFeedback::NewEvent(inbound_event) => { 170 | geq.lock().unwrap().push_update(inbound_event, logic.get_identitiy().get_key()); 171 | }, 172 | ParseFeedback::Ignore => continue, 173 | ParseFeedback::PullNewEvents => (), 174 | ParseFeedback::ForceTermination => break 175 | }; 176 | 177 | } else { 178 | if timeout != 0 { 179 | break; 180 | } 181 | } 182 | 183 | logic.outbound_create(geq.lock().unwrap().pull_updates(&logic.get_identitiy().get_key())); 184 | 185 | timeout = *gamelogic::client_thread_timeout; 186 | } 187 | 188 | log::info!( 189 | "client {:x?} suspending activity, thread terminating", 190 | logic.get_identitiy().get_key() 191 | ); 192 | 193 | logic.shutdown(); 194 | geq.lock().unwrap().delete_client(logic.get_identitiy().get_key()); 195 | 196 | match shutdown_tx.send(logic.get_identitiy().get_key().clone()) { 197 | Ok(_e) => log::info!( 198 | "client {:x?} sending shutdown command to router", 199 | logic.get_identitiy().get_key() 200 | ), 201 | Err(_e) => { 202 | log::warn!("client {:x?} could not send shutdown command, this will very likely cause errors when reconnecting", logic.get_identitiy().get_key()); 203 | } 204 | } 205 | }); 206 | } 207 | 208 | pub enum ParseFeedback { 209 | NewEvent(T), 210 | PullNewEvents, 211 | Ignore, 212 | ForceTermination, 213 | } -------------------------------------------------------------------------------- /maze_gameserver/src/aluminium/globaleventqueue.rs: -------------------------------------------------------------------------------- 1 | use std::collections::{VecDeque,HashMap}; 2 | use crate::gamelogic::clientkey::ClientKey; 3 | use crate::gamelogic::types::*; 4 | use std::sync::{Mutex,Arc}; 5 | 6 | pub struct GlobalEventQueue { 7 | client_queues: HashMap>>>, 8 | } 9 | impl GlobalEventQueue { 10 | pub fn new() -> Self { 11 | Self { 12 | client_queues: HashMap::new(), 13 | } 14 | } 15 | 16 | pub fn add_client(&mut self, key : ClientKey) { 17 | self.client_queues.insert(key, VecDeque::new()); 18 | } 19 | 20 | pub fn delete_client(&mut self, key : &ClientKey) { 21 | self.client_queues.remove(key); 22 | } 23 | 24 | pub fn push_update(&mut self, ev: Arc>, key : &ClientKey) { 25 | for (current_key, queue) in &mut self.client_queues { 26 | if current_key!=key { 27 | if queue.len()>25 { 28 | queue.pop_front(); // ddos miltigation 29 | } 30 | queue.push_back(ev.clone()); 31 | } 32 | } 33 | } 34 | pub fn pull_updates(&mut self, key: &ClientKey) -> Option>>> { 35 | let mut new_events : Vec<_> = Vec::new(); 36 | 37 | match self.client_queues.get_mut(key) { 38 | Some(queue) => { 39 | match queue.is_empty() { 40 | false => { 41 | loop { 42 | match queue.pop_front() { 43 | Some(e) => { 44 | new_events.push(e); 45 | } 46 | None => break, 47 | } 48 | } 49 | } 50 | true => { 51 | return None; 52 | } 53 | } 54 | }, 55 | None => {log::warn!("bad key");return None}, 56 | }; 57 | if !new_events.is_empty() { 58 | } 59 | Some(new_events) 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /maze_gameserver/src/aluminium/listener.rs: -------------------------------------------------------------------------------- 1 | /* listens on one udp port to put packets into FIFO queue for arriving packets */ 2 | use crate::aluminium::clientman::Message; 3 | use crate::gamelogic::Logic; 4 | use std::ops::Add; 5 | use std::sync::mpsc::Sender; 6 | 7 | pub struct Listener { 8 | socket: std::net::UdpSocket, 9 | sender: Sender, 10 | port: u16, 11 | } 12 | impl Listener { 13 | /* constructor for the listener */ 14 | pub fn new(port: u16, sender: Sender) -> Self { 15 | let bindaddr = String::from("0.0.0.0:").add(&port.to_string()); 16 | let tmp = match std::net::UdpSocket::bind(bindaddr) { 17 | Err(_e) => { 18 | log::error!( 19 | "{}", 20 | String::from("could not bind to port ").add(&port.to_string()) 21 | ); 22 | panic!("could not bind to port") 23 | } 24 | Ok(e) => e, 25 | }; 26 | log::info!("new instance on port {} created ", port); 27 | Self { 28 | socket: tmp, 29 | sender: sender, 30 | port: port, 31 | } 32 | } 33 | /* runmethod that is executed as worker thread, does not terminate ! */ 34 | pub fn run(&self) -> ! { 35 | let mut recvbuffer: [u8; 256] = [0; 256]; 36 | log::info!("instance for port {} entering runmethod", self.port); 37 | loop { 38 | for (nbytes, src) in self.socket.recv_from(&mut recvbuffer) { 39 | if nbytes < 10 || nbytes > 48 { 40 | continue; 41 | } 42 | let cloned_socket = match self.socket.try_clone() { 43 | Ok(e) => e, 44 | Err(_e) => { 45 | log::error!( 46 | "could not clone socket on local port {}, ignoring packet", 47 | self.port 48 | ); 49 | continue; 50 | } 51 | }; 52 | 53 | match self.sender.send(Message::new( 54 | cloned_socket, 55 | src, 56 | Logic::decode_pkt(&recvbuffer[0..nbytes]), 57 | )) { 58 | Ok(_e) => (), 59 | Err(_e) => { 60 | log::warn!("failed to send payload into FIFO mpsc, out of memory?"); 61 | continue; 62 | } 63 | }; 64 | } 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /maze_gameserver/src/aluminium/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod clientman; 2 | pub mod antiddos; 3 | mod listener; 4 | mod globaleventqueue; 5 | 6 | use std::sync::mpsc::{self, Receiver, Sender}; 7 | use std::sync::{Arc, Mutex}; 8 | use std::thread::{self, JoinHandle}; 9 | 10 | use clientman::ClientManager; 11 | 12 | pub struct Server { 13 | listeners: Vec>>, 14 | thread_handles: Vec>, 15 | ports: (u16, u16), // start,end 16 | feedback_rx: Arc>>, 17 | } 18 | 19 | impl Server { 20 | pub fn start(ports: (u16, u16)) -> Self { 21 | let (feedback_tx, feedback_rx): (Sender, Receiver) = mpsc::channel(); 22 | let (sender_inbound, receiver_inbound) = mpsc::channel(); 23 | 24 | let mut thread_handles = Vec::new(); 25 | let mut listeners = Vec::new(); 26 | 27 | for port in ports.0..ports.1 { 28 | listeners.push(Arc::new(Mutex::new(listener::Listener::new( 29 | port, 30 | sender_inbound.clone(), 31 | )))) 32 | } 33 | 34 | for list in &listeners { 35 | let tmp = list.clone(); 36 | thread_handles.push(thread::spawn(move || { 37 | tmp.clone().lock().unwrap().run(); 38 | })) 39 | } 40 | thread_handles.push(std::thread::spawn(move || { 41 | ClientManager::new(receiver_inbound, feedback_tx).run(); 42 | })); 43 | 44 | Self { 45 | listeners: listeners, 46 | thread_handles: thread_handles, 47 | ports: ports, 48 | feedback_rx: Arc::new(Mutex::new(feedback_rx)), 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /maze_gameserver/src/gamelogic/antispam.rs: -------------------------------------------------------------------------------- 1 | pub struct AntiSpam { 2 | packets_per_second : u16, 3 | last_time : f32, 4 | } 5 | 6 | impl AntiSpam { 7 | pub fn new() -> Self { 8 | Self { 9 | packets_per_second : 0, 10 | last_time : 0.0, 11 | } 12 | } 13 | pub fn tick_and_is_ok(&mut self) -> bool { 14 | if super::timer.elapsed().as_secs_f32()>self.last_time+1.0 { 15 | self.last_time+=1.0; 16 | self.packets_per_second = 0; 17 | } 18 | self.packets_per_second+=1; 19 | self.packets_per_second < *super::packets_per_second_limit 20 | } 21 | } -------------------------------------------------------------------------------- /maze_gameserver/src/gamelogic/characterstate.rs: -------------------------------------------------------------------------------- 1 | use super::unlocks::Unlocks; 2 | use super::rabbitcolor::RabbitColor; 3 | use std::fs::File; 4 | use serde::{Serialize, Deserialize}; 5 | use super::clientkey::ClientKey; 6 | use std::io::prelude::*; 7 | use std::ops::{Add}; 8 | 9 | #[derive(Serialize, Deserialize)] 10 | pub struct CharacterState { 11 | name: Vec, 12 | position: (i32, i32, i32), 13 | is_alive: bool, 14 | is_new: bool, 15 | pub unlocks: Unlocks, 16 | pub color : RabbitColor, 17 | highscore : f32, 18 | } 19 | 20 | impl CharacterState { 21 | pub fn new(name: &Vec) -> Self { 22 | Self { 23 | position: super::random_mid_spawn(), 24 | name: name.clone(), 25 | is_alive: true, 26 | is_new: true, 27 | unlocks: Unlocks::new(), 28 | color : RabbitColor::new(), 29 | highscore : 100000000000000.0, 30 | } 31 | } 32 | pub fn setpos(&mut self, x: i32, y: i32, z: i32) { 33 | self.position.0 = x; 34 | self.position.1 = y; 35 | self.position.2 = z; 36 | } 37 | pub fn is_alive(&self) -> bool { 38 | self.is_alive 39 | } 40 | 41 | pub fn set_alive(&mut self,value : bool) { 42 | self.is_alive = value; 43 | } 44 | 45 | pub fn update_name(&mut self, newname: &Vec) { 46 | self.name = newname.clone(); 47 | } 48 | 49 | pub fn set_highscore(&mut self, hs : f32) { 50 | self.highscore = hs; 51 | } 52 | 53 | pub fn get_highscore(&self) -> f32 { 54 | self.highscore 55 | } 56 | 57 | pub fn shutdown(&mut self, key : &ClientKey) { 58 | self.is_new = false; 59 | let mut f = match File::create("clients/".to_string().add(hex::encode(key.get_raw_bytes()).as_str())) { 60 | Ok(e) => e, 61 | Err(_e) => { 62 | log::error!("no such directory \"clients\""); 63 | return; 64 | } 65 | }; 66 | bincode::serialize_into(&mut f, self).unwrap(); 67 | } 68 | 69 | pub fn try_activate(key: &ClientKey) -> Option { 70 | let mut f = match File::open("clients/".to_string().add(hex::encode(key.get_raw_bytes()).as_str())) { 71 | Ok(e) => e, 72 | Err(_e) => return None, 73 | }; 74 | 75 | let mut readbuf = Vec::new(); 76 | 77 | match f.read_to_end(&mut readbuf) { 78 | Ok(e) => e, 79 | Err(_e) => { 80 | println!("could not read from file"); 81 | return None; 82 | } 83 | }; 84 | 85 | Some(bincode::deserialize(&readbuf[..]).unwrap()) 86 | } 87 | 88 | pub fn getpos(&self) -> (i32, i32, i32) { 89 | self.position 90 | } 91 | } -------------------------------------------------------------------------------- /maze_gameserver/src/gamelogic/clientkey.rs: -------------------------------------------------------------------------------- 1 | use serde::Serialize; 2 | 3 | #[derive(Eq)] 4 | #[derive(PartialEq)] 5 | #[derive(Debug)] 6 | #[derive(Hash)] 7 | #[derive(Clone)] 8 | #[derive(Serialize)] 9 | pub struct ClientKey { 10 | key : [u8;8], 11 | } 12 | 13 | impl ClientKey { 14 | pub fn new(key : [u8;8]) -> Self { 15 | Self { 16 | key : key, 17 | } 18 | } 19 | 20 | pub fn get_raw_bytes(&self) -> &[u8] { 21 | &self.key 22 | } 23 | } -------------------------------------------------------------------------------- /maze_gameserver/src/gamelogic/identity.rs: -------------------------------------------------------------------------------- 1 | use super::clientkey::ClientKey; 2 | use std::net::{SocketAddr,UdpSocket}; 3 | use std::hash::{Hash, Hasher}; 4 | use crate::aluminium::clientman::Message; 5 | use super::*; 6 | 7 | pub struct Identity { 8 | pub secret: ClientKey, 9 | pub socket: UdpSocket, 10 | pub peer_addr: SocketAddr, 11 | pub name: Vec, 12 | pub uid : [u8;4], 13 | pub handshake_payload : Vec, 14 | } 15 | 16 | impl Identity { 17 | pub fn new(handshake_msg: Message) -> Option { 18 | if handshake_msg.payload[0] != 0x4c || handshake_msg.payload.len()<42 { 19 | return None; 20 | } 21 | 22 | let mut name = handshake_msg.payload[10..].to_vec(); 23 | let mut len = 0; 24 | 25 | for &byte in &name { 26 | if byte == 0x0 || byte == 0xa { 27 | break; 28 | } 29 | len+=1; 30 | } 31 | name.truncate(len); 32 | 33 | Some(Self { 34 | secret: Logic::pkt_get_client_key(&handshake_msg.payload), 35 | socket: handshake_msg.socket, 36 | peer_addr: handshake_msg.peer_addr, 37 | name: name, 38 | uid : *b"0000", 39 | handshake_payload : handshake_msg.payload, 40 | }) 41 | } 42 | 43 | pub fn get_key(&self) -> &ClientKey { 44 | &self.secret 45 | } 46 | } 47 | impl Eq for Identity {} 48 | impl PartialEq for Identity { 49 | fn eq(&self, other: &Self) -> bool { 50 | self.get_key() == other.get_key() 51 | } 52 | } 53 | 54 | impl Hash for Identity { 55 | fn hash(&self, state: &mut H) { 56 | self.get_key().hash(state); 57 | } 58 | } -------------------------------------------------------------------------------- /maze_gameserver/src/gamelogic/map.rs: -------------------------------------------------------------------------------- 1 | use std::fs::File; 2 | use std::io::prelude::*; 3 | 4 | pub struct Map { 5 | in_mem_map : Vec>, 6 | } 7 | 8 | impl Map { 9 | pub fn new(fname : &str) -> Self { 10 | let mut file = match File::open(fname) { 11 | Ok(e) => e, 12 | _ => {log::error!("could not open map file");panic!("could not open map file");} 13 | }; 14 | 15 | let mut tmp = [0;1068]; 16 | let mut finl = Vec::new(); 17 | 18 | for _ in 0..1068 { 19 | match file.read(&mut tmp) { 20 | Ok(_) => (), 21 | _ => {log::error!("could not read map file");panic!("could not read map file")} 22 | } 23 | finl.push(tmp.to_vec()); 24 | } 25 | 26 | Self { 27 | in_mem_map : finl, 28 | } 29 | } 30 | 31 | pub fn eval_position(&self, mut x: i32, mut z : i32) -> LocationType { 32 | x-=41004; 33 | z-=24735; 34 | 35 | let mut floatx = x as f32; 36 | let mut floatz = z as f32; 37 | 38 | floatx /= 4497.191011236; 39 | floatz /= 4497.191011236; 40 | 41 | let (x,z) = (floatx as i32, floatz as i32); 42 | 43 | if (0<= x && x<1067) && (0<= z && z<1067) { 44 | match self.in_mem_map.get( z as usize ).unwrap().get( x as usize ).unwrap() { 45 | 0x82 => return LocationType::OnPlace(Place::Lava), 46 | 0x83 => return LocationType::OnPlace(Place::Tower), 47 | 0x84 => return LocationType::OnPlace(Place::RaceStart), 48 | 0x85 => return LocationType::OnPlace(Place::Hangar), 49 | 50 | 0x00 => return LocationType::Allowed, 51 | 0xff => return LocationType::NotAllowed, 52 | 0x40 => return LocationType::FlagArea(Flag::Lava), 53 | 0x41 => return LocationType::FlagArea(Flag::Tower), 54 | 0x7c => return LocationType::DieZone, 55 | 56 | 0xd0 => return LocationType::TeleportAway(Place::Hangar), 57 | 0xd1 => return LocationType::TeleportAway(Place::Tower), 58 | 0xd2 => return LocationType::TeleportAway(Place::RaceStart), 59 | 0xd3 => return LocationType::TeleportAway(Place::Lava), 60 | 0x6c => return LocationType::Checkpoint(0), 61 | 0x60 => return LocationType::Checkpoint(1), 62 | 0x61 => return LocationType::Checkpoint(2), 63 | 0x62 => return LocationType::Checkpoint(3), 64 | 0x63 => return LocationType::Checkpoint(4), 65 | 0x64 => return LocationType::Checkpoint(5), 66 | 0x65 => return LocationType::Checkpoint(6), 67 | 0x66 => return LocationType::Checkpoint(7), 68 | 0x67 => return LocationType::Checkpoint(8), 69 | 0x68 => return LocationType::Checkpoint(9), 70 | 0x69 => return LocationType::Checkpoint(10), 71 | 0x6a => return LocationType::Checkpoint(11), 72 | 0x6b => return LocationType::Checkpoint(12), 73 | 74 | _ => return LocationType::NotAllowed, 75 | }; 76 | } 77 | 78 | LocationType::NotAllowed 79 | } 80 | } 81 | 82 | 83 | 84 | pub enum Place { 85 | RaceStart, 86 | Hangar, 87 | Tower, 88 | Lava, 89 | Mid, 90 | RandomMid, 91 | Custom, 92 | } 93 | 94 | pub enum Flag { 95 | Tower, 96 | Lava 97 | } 98 | 99 | 100 | 101 | 102 | pub enum LocationType { 103 | Allowed, 104 | NotAllowed, 105 | DieZone, 106 | FlagArea(Flag), 107 | TeleportAway(Place), 108 | OnPlace(Place), 109 | Checkpoint(u8), 110 | } 111 | -------------------------------------------------------------------------------- /maze_gameserver/src/gamelogic/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod clientkey; 2 | pub mod types; 3 | mod characterstate; 4 | mod identity; 5 | mod map; 6 | mod packets; 7 | mod rabbitcolor; 8 | mod unlocks; 9 | mod antispam; 10 | mod raceman; 11 | mod scoreboard_manager; 12 | mod score; 13 | 14 | use std::sync::{Arc,Mutex}; 15 | use std::collections::{HashMap}; 16 | use std::time::Instant; 17 | 18 | use std::convert::TryInto; 19 | use characterstate::CharacterState; 20 | use identity::Identity; 21 | use unlocks::Unlocks; 22 | use rabbitcolor::RabbitColor; 23 | use map::{Map,Place}; 24 | use crate::aluminium::clientman::{Message,ParseFeedback}; 25 | use clientkey::ClientKey; 26 | use types::*; 27 | use antispam::AntiSpam; 28 | use raceman::RaceManager; 29 | use scoreboard_manager::push_score; 30 | 31 | #[macro_use] 32 | 33 | lazy_static::lazy_static! { 34 | pub static ref client_version: u8 = 2; 35 | pub static ref client_thread_timeout : u64 = 30000; // you can reconnect until this time is up 36 | pub static ref timer : std::time::Instant = Instant::now(); 37 | pub static ref uid_dict : Mutex, Unlocks,RabbitColor)>> = Mutex::new(HashMap::new()); 38 | pub static ref mazemap : Map = Map::new("maze.map"); 39 | pub static ref packets_per_second_limit : u16 = 35; 40 | pub static ref max_distance_per_packet : f64 = 100000.0; 41 | pub static ref mid_spawn : (i32,i32,i32 ) = (2658 * 1000, 10 *10000, 2393 * 1000); 42 | pub static ref tower_spawn : (i32,i32,i32) = (380365, 3 * 10000, 4019494); 43 | pub static ref race_spawn : (i32,i32,i32) = (2124889, 3 * 10000, 1995128); 44 | pub static ref lava_spawn : (i32,i32,i32) = (4052942, 3 * 10000, 4514700); 45 | pub static ref hangar_spawn : (i32,i32,i32) = (1812125, 3 * 10000, 645054); 46 | } 47 | 48 | 49 | pub fn random_mid_spawn() -> (i32,i32,i32) { 50 | let mut spawn = *mid_spawn; 51 | spawn.0 += rand::random::() % 80000; 52 | spawn.2 += rand::random::() % 80000; 53 | spawn 54 | } 55 | 56 | 57 | pub struct Logic { 58 | pub cs : CharacterState, 59 | id : Identity, 60 | npacket: u128, 61 | client_time: u64, 62 | 63 | /* cooldown related */ 64 | last_posupdate : f32, 65 | last_emoji : f32, 66 | last_text : f32, 67 | last_flag : f32, 68 | 69 | has_new_unlock : bool, 70 | anti_spam : AntiSpam, 71 | race_manager : RaceManager, 72 | } 73 | 74 | impl Logic { 75 | /* Logic instance should be inside the client thread*/ 76 | 77 | /* SECTION 1 - PUBLIC INTERFACE */ 78 | 79 | /* new client logic */ 80 | pub fn new(message : Message) -> Option { 81 | 82 | let identity = match Identity::new(message.clone()) { 83 | Some(e) => e, 84 | None => { 85 | return None; 86 | } 87 | }; 88 | 89 | let state = match CharacterState::try_activate(identity.get_key()) { 90 | Some(mut e) => { 91 | log::info!( 92 | "existing client {:x?} at {} loaded and entering runmethod", 93 | identity.get_key(), 94 | identity.peer_addr 95 | ); 96 | e.update_name(&identity.name); 97 | e 98 | } 99 | None => { 100 | log::info!( 101 | "new client with idnum {:x?} at {} created and entering runmethod", 102 | identity.get_key(), 103 | identity.peer_addr 104 | ); 105 | CharacterState::new(&identity.name) 106 | } 107 | 108 | }; 109 | 110 | let mut preself = Self { 111 | cs : state, 112 | id : identity, 113 | npacket: 0, 114 | client_time: 0, 115 | last_posupdate : timer.elapsed().as_secs_f32(), 116 | last_emoji : timer.elapsed().as_secs_f32(), 117 | last_text : timer.elapsed().as_secs_f32(), 118 | last_flag : timer.elapsed().as_secs_f32(), 119 | has_new_unlock : false, 120 | anti_spam : AntiSpam::new(), 121 | race_manager : RaceManager::new(), 122 | }; 123 | 124 | preself.inbound_parse(message); 125 | Some(preself) 126 | } 127 | 128 | /* encryption and decryption related */ 129 | pub fn decode_pkt(buf: &[u8]) -> Vec { 130 | let mut firstrandom: u8 = buf[0]; 131 | let secondrandom: u8 = buf[1]; 132 | let mut decoded = Vec::new(); 133 | 134 | for i in 0..buf.len() - 2 { 135 | decoded.push(buf[i + 2] ^ firstrandom); 136 | let v21: u64 = (firstrandom as u64 + secondrandom as u64) as u64; 137 | firstrandom = (v21 + ((2155905153 * (v21)) >> 39)) as u8; 138 | } 139 | decoded 140 | } 141 | 142 | pub fn encode_pkt(buf: Vec) -> Vec { 143 | let mut firstrandom = rand::random::(); 144 | let secondrandom = rand::random::(); 145 | let mut encoded = Vec::new(); 146 | encoded.push(firstrandom); 147 | encoded.push(secondrandom); 148 | 149 | for i in 0..buf.len() { 150 | encoded.push(buf[i] ^ firstrandom); 151 | let v21: u64 = (firstrandom as u64 + secondrandom as u64) as u64; 152 | firstrandom = (v21 + ((2155905153 * (v21)) >> 39)) as u8; 153 | } 154 | encoded 155 | } 156 | 157 | /*once decrypted, how to route packet ? */ 158 | pub fn pkt_get_client_key(payload: &Vec) -> ClientKey { 159 | if payload[0] == 0x3c { 160 | return ClientKey::new(payload.as_slice()[2..10].try_into().unwrap()); 161 | } 162 | ClientKey::new(payload.as_slice()[1..9].try_into().unwrap()) 163 | } 164 | 165 | 166 | /* every new packet for this client is parsed here */ 167 | pub fn inbound_parse( 168 | &mut self, 169 | mut message : Message, 170 | ) -> ParseFeedback>> { 171 | 172 | if !self.anti_spam.tick_and_is_ok() { 173 | self.kick_client(); 174 | return ParseFeedback::ForceTermination; 175 | } 176 | 177 | 178 | let operation = message.payload[0]; 179 | 180 | { 181 | if !self.cs.is_alive() && (operation==0x45 || operation==0x50) { 182 | self.send_text("you died, didn't you?"); 183 | self.cs.set_alive(true); 184 | self.send_teleport(Place::RandomMid); 185 | } 186 | 187 | if !self.cs.is_alive() && operation==0x4c { 188 | self.send_text("you died, didn't you?"); 189 | self.cs.set_alive(true); 190 | } 191 | 192 | if self.has_new_unlock { // update entry if rabbit went white 193 | self.has_new_unlock = false; 194 | let mut dict = uid_dict.lock().unwrap(); 195 | match dict.remove(&self.id.uid) { 196 | _ => (), 197 | } 198 | dict.insert(self.id.uid, (self.id.name.clone(),self.cs.unlocks,self.cs.color)); 199 | } 200 | } 201 | 202 | match operation { 203 | 0x4c => { // login packet 204 | self.id.peer_addr = message.peer_addr; 205 | match self.parse_login_pkt(message.payload.as_mut_slice()) { 206 | PacketParseResult::HardError => {self.kick_client();return ParseFeedback::ForceTermination}, 207 | _ => (), 208 | } 209 | self.send_teleport(Place::RandomMid); 210 | } 211 | 0x3c => { // heartbeat packet 212 | match self.parse_heartbeat_pkt(message.payload.as_mut_slice()) { 213 | PacketParseResult::HardError => {self.kick_client();return ParseFeedback::ForceTermination}, 214 | _ => (), 215 | } 216 | } 217 | 0x50 => { // position packet 218 | match self.parse_position_pkt(message.payload.as_mut_slice()) { 219 | PacketParseResult::OkDispatch(update) => return ParseFeedback::NewEvent(Arc::new(Mutex::new(update))), 220 | PacketParseResult::HardError => {self.kick_client();return ParseFeedback::ForceTermination}, 221 | _ => (), 222 | } 223 | } 224 | 0x45 => { // emoji packet 225 | match self.parse_emoji_pkt(message.payload.as_mut_slice()) { 226 | PacketParseResult::OkDispatch(update) => return ParseFeedback::NewEvent(Arc::new(Mutex::new(update))), 227 | PacketParseResult::HardError => {self.kick_client();return ParseFeedback::ForceTermination}, 228 | _ => (), 229 | } 230 | } 231 | 0x49 => { // info packet 232 | match self.parse_info_pkt(message.payload.as_mut_slice()) { 233 | PacketParseResult::HardError => {self.kick_client();return ParseFeedback::ForceTermination} 234 | _ => (), 235 | } 236 | } 237 | _ => { 238 | self.kick_client(); 239 | return ParseFeedback::ForceTermination 240 | } 241 | }; 242 | ParseFeedback::PullNewEvents 243 | } 244 | 245 | pub fn outbound_create(&mut self, events: Option>>>) { 246 | let mut msg : Vec= Vec::new(); 247 | 248 | match events { 249 | Some(e) => { 250 | match e.is_empty() { 251 | false => { 252 | for event in e { 253 | let update = event.lock().unwrap().dispatch(self); 254 | 255 | match update { 256 | Some(mut e) => { 257 | match e.1 { 258 | DispatchType::PosUpdate => { 259 | if msg.len() > 256 { 260 | self.send(0x50, &mut msg); 261 | } else { 262 | if msg.len()!=0 { 263 | msg.push(0x50); 264 | } 265 | msg.append(&mut e.0); 266 | } 267 | } 268 | DispatchType::Emoji => { 269 | if msg.len() != 0 { 270 | self.send(0x50, &mut msg); 271 | } 272 | self.send(0x45, &mut e.0); 273 | 274 | } 275 | } 276 | }, 277 | None => {} 278 | }; 279 | } 280 | if msg.len() != 0 { 281 | self.send(0x50, &mut msg); 282 | } 283 | } 284 | _ => (), 285 | } 286 | } 287 | None => {()} 288 | }; 289 | } 290 | 291 | 292 | pub fn get_state(&mut self) -> &mut CharacterState { 293 | &mut self.cs 294 | } 295 | 296 | pub fn get_identitiy(&self) -> &Identity { 297 | &self.id 298 | } 299 | 300 | pub fn shutdown(&mut self) { 301 | uid_dict.lock().unwrap().remove(&self.id.uid); 302 | self.cs.shutdown(self.id.get_key()); 303 | } 304 | 305 | pub fn get_uid(&self) -> [u8;4] { 306 | self.id.uid 307 | } 308 | 309 | 310 | /*SECTION 2 - PRIVATE INTERFACE */ 311 | 312 | fn send(&mut self, operation: u8, msg: &mut Vec) -> bool { 313 | let mut to_encode = Vec::new(); 314 | to_encode.push(operation); 315 | if operation == 0x3c { 316 | to_encode.push(0x33); 317 | } 318 | to_encode.append(msg); 319 | self.npacket += 1; 320 | 321 | match self.id 322 | .socket 323 | .send_to(Logic::encode_pkt(to_encode).as_slice(), self.id.peer_addr) 324 | { 325 | Ok(_e) => return true, 326 | _ => { 327 | log::warn!("could not send packet back to {}", self.id.peer_addr); 328 | return false; 329 | } 330 | } 331 | } 332 | } 333 | -------------------------------------------------------------------------------- /maze_gameserver/src/gamelogic/packets.rs: -------------------------------------------------------------------------------- 1 | use std::mem; 2 | use super::*; 3 | use super::map::{LocationType,Flag}; 4 | use super::types::*; 5 | use byteorder::{LittleEndian, WriteBytesExt}; 6 | use std::convert::TryInto; 7 | use super::score::Score; 8 | 9 | /* ######## packet types ######## */ 10 | #[derive(Clone)] 11 | pub struct PosUpdate { 12 | uid : [u8;4], 13 | timestamp : u32, 14 | pos_x : i32, 15 | pos_y : i32, 16 | pos_z : i32, 17 | euler_x : i32, 18 | euler_y : i32, 19 | euler_z : i32, 20 | add_ren_info : [u8;5], 21 | } 22 | 23 | #[derive(Clone)] 24 | pub struct Emoji { 25 | uid : [u8;4], 26 | emoji : u8, 27 | } 28 | 29 | impl Logic { 30 | pub fn parse_login_pkt(&mut self, payload : &mut [u8]) -> PacketParseResult{ 31 | if payload.len() < 42 { 32 | return PacketParseResult::HardError; 33 | } 34 | /* login response */ 35 | let mut msg = Vec::new(); 36 | if self.npacket == 0 { 37 | self.id.uid = rand::random::<[u8;4]>(); 38 | while uid_dict.lock().unwrap().get(&self.id.uid).is_some() { 39 | self.id.uid = rand::random::<[u8;4]>(); 40 | } 41 | uid_dict.lock().unwrap().insert(self.id.uid, (self.id.name.clone(), self.cs.unlocks, self.cs.color)); 42 | } 43 | msg.append(&mut self.id.uid.to_vec()); 44 | msg.push(self.cs.unlocks.get_raw()); 45 | msg.push(self.cs.color.get_raw()); 46 | msg.push(*client_version); 47 | self.send(0x4c, &mut msg); 48 | msg.clear(); 49 | 50 | self.client_time = 0; 51 | PacketParseResult::Ignore 52 | } 53 | 54 | pub fn parse_position_pkt(&mut self, payload : &mut [u8]) -> PacketParseResult { 55 | if payload.len() != 46 { 56 | return PacketParseResult::HardError 57 | } 58 | 59 | if timer.elapsed().as_secs_f32()-self.last_posupdate<0.05 { // cap position updates at 20Hz 60 | return PacketParseResult::Ignore 61 | } 62 | 63 | let pkt_uid = self.get_uid(); 64 | let pkt_timestamp = unsafe { mem::transmute::<[u8; 4], u32>(payload[9..13].try_into().unwrap()) }; 65 | let pkt_pos_x = unsafe { mem::transmute::<[u8; 4], i32>(payload[17..21].try_into().unwrap()) }; 66 | let pkt_pos_y = unsafe { mem::transmute::<[u8; 4], i32>(payload[21..25].try_into().unwrap()) }; 67 | let pkt_pos_z = unsafe { mem::transmute::<[u8; 4], i32>(payload[25..29].try_into().unwrap()) }; 68 | let pkt_euler_x = unsafe { mem::transmute::<[u8; 4], i32>(payload[29..33].try_into().unwrap()) }; 69 | let pkt_euler_y = unsafe { mem::transmute::<[u8; 4], i32>(payload[33..37].try_into().unwrap()) }; 70 | let pkt_euler_z = unsafe { mem::transmute::<[u8; 4], i32>(payload[37..41].try_into().unwrap()) }; 71 | let pkt_add_ren_info = payload[41..46].try_into().unwrap(); 72 | 73 | 74 | if pkt_timestamp < self.client_time as u32 { 75 | return PacketParseResult::Ignore; 76 | } 77 | 78 | /* "anti speed hack" */ /* originally, i thought you had to tamper the timestamp, but apperently you should not go more than 10000units per update */ 79 | { 80 | let (oldx,_,oldz) = self.cs.getpos(); 81 | let (relx,relz) : (i64,i64) = ( ( oldx-pkt_pos_x ).abs() as i64, ( oldz-pkt_pos_z ).abs() as i64); 82 | let travelled_distance = ( ( ( i64::pow(relx,2) + i64::pow(relz,2) ) ) as f64 ).sqrt(); 83 | if travelled_distance > *max_distance_per_packet { 84 | self.send_teleport(Place::Custom); 85 | return PacketParseResult::Ignore; 86 | } 87 | } 88 | 89 | 90 | match mazemap.eval_position(pkt_pos_x,pkt_pos_z) { 91 | LocationType::Allowed => { 92 | self.cs.setpos(pkt_pos_x, pkt_pos_y, pkt_pos_z); 93 | self.client_time = pkt_timestamp as u64; 94 | }, 95 | LocationType::NotAllowed => { 96 | self.send_teleport(Place::Custom); 97 | return PacketParseResult::Ignore; 98 | }, 99 | LocationType::OnPlace(place) => { 100 | match place { 101 | Place::RaceStart => { 102 | if !self.cs.unlocks.get(1) { 103 | self.cs.unlocks.set(1); 104 | if self.cs.unlocks.get_total()>=4 { 105 | self.cs.color.make_white(); 106 | } 107 | self.send_unlocks(); 108 | } 109 | 110 | }, 111 | Place::Hangar => { 112 | if !self.cs.unlocks.get(3) { 113 | self.cs.unlocks.set(3); 114 | if self.cs.unlocks.get_total()>=4 { 115 | self.cs.color.make_white(); 116 | } 117 | self.send_unlocks(); 118 | } 119 | }, 120 | Place::Lava => { 121 | if !self.cs.unlocks.get(2) { 122 | self.cs.unlocks.set(2); 123 | if self.cs.unlocks.get_total()>=4 { 124 | self.cs.color.make_white(); 125 | } 126 | self.send_unlocks(); 127 | } 128 | }, 129 | Place::Tower => { 130 | if !self.cs.unlocks.get(0) { 131 | self.cs.unlocks.set(0); 132 | if self.cs.unlocks.get_total()>=4 { 133 | self.cs.color.make_white(); 134 | } 135 | self.send_unlocks(); 136 | 137 | } 138 | }, 139 | _ => (), 140 | } 141 | self.cs.setpos(pkt_pos_x, pkt_pos_y, pkt_pos_z); 142 | self.client_time = pkt_timestamp as u64; 143 | } 144 | LocationType::TeleportAway(place) => { 145 | let is_ok = match place { 146 | Place::RaceStart => {self.cs.unlocks.get(1)}, 147 | Place::Hangar => {self.cs.unlocks.get(3)}, 148 | Place::Lava => {self.cs.unlocks.get(2)}, 149 | Place::Tower => {self.cs.unlocks.get(0)}, 150 | _ => false, 151 | }; 152 | if is_ok { 153 | self.send_teleport(place); 154 | } else { 155 | self.send_text("Teleporter locked. Area not discovered yet."); 156 | self.cs.setpos(pkt_pos_x, pkt_pos_y, pkt_pos_z); 157 | } 158 | self.client_time = pkt_timestamp as u64; 159 | }, 160 | LocationType::DieZone => { 161 | if pkt_pos_y<5000 && pkt_pos_y>-5000 { 162 | self.send(0x44, &mut b"\x20\x00".to_vec()); // death packet 163 | self.cs.set_alive(false); 164 | } 165 | self.cs.setpos(pkt_pos_x, pkt_pos_y, pkt_pos_z); 166 | self.client_time = pkt_timestamp as u64; 167 | } 168 | LocationType::FlagArea(flag) => { 169 | match flag { 170 | Flag::Lava => { 171 | self.send_flag("{U_haz_been_banned_by_B4TTL3Y3}", true); 172 | if !self.cs.unlocks.get(4) { 173 | self.cs.unlocks.set(4); 174 | if self.cs.unlocks.get_total()>=4 { 175 | self.cs.color.make_white(); 176 | } 177 | self.send_unlocks(); 178 | 179 | } 180 | } 181 | Flag::Tower => { 182 | if pkt_pos_y>471998 { 183 | self.send_flag("{D1d_you_so1v3_th3_m4z3?}", true); 184 | if !self.cs.unlocks.get(5) { 185 | self.cs.unlocks.set(5); 186 | if self.cs.unlocks.get_total()>=4 { 187 | self.cs.color.make_white(); 188 | } 189 | self.send_unlocks(); 190 | } 191 | } 192 | } 193 | } 194 | self.cs.setpos(pkt_pos_x, pkt_pos_y, pkt_pos_z); 195 | self.client_time = pkt_timestamp as u64; 196 | } 197 | LocationType::Checkpoint(checkpoint) => { 198 | if self.race_manager.eval(checkpoint) { 199 | self.send_checkpoint(checkpoint); 200 | 201 | if checkpoint == 12 { 202 | let score = self.race_manager.get_total_time(); 203 | 204 | if self.cs.get_highscore() > score { 205 | log::debug!("updating hs from {} to {}", self.cs.get_highscore(), score); 206 | push_score(&Score::new(self.id.get_key().clone(), self.id.name.clone(),score)); 207 | self.cs.set_highscore(score); 208 | } 209 | 210 | if score < 5.0 { 211 | self.send_flag("{fffffresh}",true); 212 | self.race_manager.reset(); 213 | } 214 | 215 | self.send_flag("{tHosE_aRe_jUsT_gAmiNg_sKiLls}", true); 216 | if !self.cs.unlocks.get(6) { 217 | self.cs.unlocks.set(6); 218 | if self.cs.unlocks.get_total()>=4 { 219 | self.cs.color.make_white(); 220 | } 221 | self.send_unlocks(); 222 | } 223 | } 224 | 225 | } 226 | self.cs.setpos(pkt_pos_x, pkt_pos_y, pkt_pos_z); 227 | self.client_time = pkt_timestamp as u64; 228 | } 229 | }; 230 | 231 | self.last_posupdate = timer.elapsed().as_secs_f32(); 232 | PacketParseResult::OkDispatch(PosUpdate{ 233 | uid : pkt_uid, 234 | timestamp : pkt_timestamp, 235 | pos_x : pkt_pos_x, 236 | pos_y : pkt_pos_y, 237 | pos_z : pkt_pos_z, 238 | euler_x : pkt_euler_x, 239 | euler_y : pkt_euler_y, 240 | euler_z : pkt_euler_z, 241 | add_ren_info : pkt_add_ren_info, 242 | }) 243 | } 244 | 245 | 246 | pub fn parse_heartbeat_pkt(&mut self, payload : &mut [u8]) -> PacketParseResult<()>{ 247 | if payload.len() != 18 { 248 | return PacketParseResult::HardError; 249 | } 250 | let pkt_timestamp = unsafe { mem::transmute::<[u8; 8], u64>(payload[10..18].try_into().unwrap()) }; 251 | 252 | if pkt_timestamp < self.client_time { 253 | return PacketParseResult::Ignore; 254 | } 255 | 256 | 257 | let mut msg = payload[10..18].to_vec(); 258 | let mut bs = [0u8; mem::size_of::()]; 259 | match bs 260 | .as_mut() 261 | .write_u64::( ( timer.elapsed().as_nanos() / 10000000 ) as u64) 262 | { 263 | _ => (), 264 | }; 265 | msg.append(&mut bs.to_vec()); 266 | //msg.append(&mut ); 267 | self.send(0x3c, &mut msg); 268 | PacketParseResult::Ignore 269 | } 270 | 271 | 272 | pub fn parse_emoji_pkt(&mut self, payload : &mut [u8]) -> PacketParseResult{ 273 | if payload.len() != 10 { 274 | return PacketParseResult::HardError; 275 | } 276 | 277 | if timer.elapsed().as_secs_f32()-self.last_emoji<3.0 { 278 | return PacketParseResult::Ignore; 279 | } 280 | 281 | let pkt_emoji = payload[9]; 282 | let pkt_uid = self.id.uid; 283 | 284 | let mut timestamp = [0u8; mem::size_of::()]; 285 | match timestamp.as_mut().write_u32::(timer.elapsed().as_secs_f32() as u32) { 286 | _ => (), 287 | }; 288 | 289 | let mut msg = Vec::new(); 290 | msg.append(&mut pkt_uid.to_vec()); 291 | msg.append(&mut timestamp.to_vec()); 292 | msg.push(pkt_emoji); 293 | self.send(0x45, &mut msg); 294 | if pkt_emoji == 0x0d { 295 | self.send_flag("{Th3_BND_w4ntz2know_your_loc4tion}", true); 296 | if !self.cs.unlocks.get(7) { 297 | self.cs.unlocks.set(7); 298 | if self.cs.unlocks.get_total()>=4 { 299 | self.cs.color.make_white(); 300 | } 301 | self.send_unlocks(); 302 | } 303 | } 304 | self.last_emoji = timer.elapsed().as_secs_f32(); 305 | PacketParseResult::OkDispatch( 306 | Emoji { 307 | uid : pkt_uid, 308 | emoji : pkt_emoji, 309 | } 310 | ) 311 | } 312 | 313 | 314 | pub fn parse_info_pkt(&mut self, payload : &mut [u8]) -> PacketParseResult<()> { 315 | if payload.len() != 13 { 316 | return PacketParseResult::HardError; 317 | } 318 | 319 | let mut playerinfo = match uid_dict.lock().unwrap().get( &mut payload[9..13]) { 320 | Some(e) => e.clone(), 321 | _ => {log::warn!("client [{:x?}] sent bad info request (invalid uid : {:x?})", &self.id.get_key(), &payload[9..13]);return PacketParseResult::Ignore}, 322 | }; 323 | 324 | let mut msg = Vec::new(); 325 | msg.append(&mut payload[9..13].to_vec()); // uid 326 | msg.push(playerinfo.1.get_raw()); // unlocks 327 | msg.push(playerinfo.2.get_raw()); // color 328 | msg.push(playerinfo.0.len() as u8); 329 | msg.append(&mut playerinfo.0); 330 | self.send(0x49, &mut msg); 331 | PacketParseResult::Ignore 332 | } 333 | 334 | 335 | pub fn send_text(&mut self, text : &str) { 336 | if timer.elapsed().as_secs_f32()-self.last_text>7.0 { 337 | self.send(0x20, &mut text.as_bytes().to_vec()); 338 | self.last_text = timer.elapsed().as_secs_f32(); 339 | } 340 | } 341 | 342 | pub fn send_flag(&mut self, flag : &str, cooldown : bool) { 343 | if timer.elapsed().as_secs_f32()-self.last_flag>2.0 || !cooldown { 344 | let mut msg = Vec::new(); 345 | msg.append(&mut b"SCG".to_vec()); 346 | msg.append(&mut flag.as_bytes().to_vec()); 347 | self.send(0x43, &mut msg); 348 | self.last_flag = timer.elapsed().as_secs_f32(); 349 | } 350 | } 351 | 352 | pub fn send_checkpoint(&mut self, checkpoint : u8) { 353 | self.send(0x52, &mut [checkpoint].to_vec()); 354 | } 355 | 356 | pub fn send_unlocks(&mut self) { 357 | let mut msg = Vec::new(); 358 | msg.push(self.cs.unlocks.get_raw()); // new emoji 359 | msg.push(self.cs.color.get_raw()); // rabbitcolor 360 | self.send(0x55, &mut msg); 361 | self.has_new_unlock = true; 362 | } 363 | 364 | pub fn send_teleport(&mut self, place : Place) { 365 | 366 | let (x,y,z) = match place { 367 | Place::Hangar => *hangar_spawn, 368 | Place::Lava => *lava_spawn, 369 | Place::RaceStart => *race_spawn, 370 | Place::Tower => *tower_spawn, 371 | Place::Mid => *mid_spawn, 372 | Place::RandomMid => random_mid_spawn(), 373 | Place::Custom => self.cs.getpos() 374 | }; 375 | 376 | self.cs.setpos(x, y, z); 377 | 378 | let mut msg = Vec::new(); 379 | msg.push(1); 380 | let mut bs = [0u8; mem::size_of::()]; 381 | match bs.as_mut().write_i32::(x) { 382 | _ => (), 383 | }; 384 | msg.append(&mut bs.to_vec()); 385 | match bs.as_mut().write_i32::(y) { 386 | _ => (), 387 | }; 388 | msg.append(&mut bs.to_vec()); 389 | match bs.as_mut().write_i32::(z) { 390 | _ => (), 391 | }; 392 | msg.append(&mut bs.to_vec()); 393 | self.send(0x54, &mut msg); 394 | } 395 | 396 | pub fn kick_client(&mut self) { 397 | self.send(0x58, &mut Vec::new()); 398 | } 399 | } 400 | 401 | impl Dispatch for PosUpdate { 402 | fn dispatch(&self, _logic : &Logic) -> Option<(Vec,DispatchType)> { 403 | let mut msg = Vec::new(); 404 | let mut bs = [0u8; mem::size_of::()]; 405 | 406 | msg.append(&mut self.uid.to_vec()); 407 | 408 | 409 | 410 | match bs.as_mut().write_u32::(self.timestamp) { 411 | Ok(_e) => {msg.append(&mut bs.to_vec())}, 412 | _ => return None 413 | }; 414 | 415 | msg.append(&mut b"\x00\x00\x00\x00".to_vec()); 416 | 417 | match bs.as_mut().write_i32::(self.pos_x) { 418 | Ok(_e) => {msg.append(&mut bs.to_vec())}, 419 | _ => return None, 420 | }; 421 | 422 | match bs.as_mut().write_i32::(self.pos_y) { 423 | Ok(_e) => {msg.append(&mut bs.to_vec())}, 424 | _ => return None, 425 | }; 426 | 427 | match bs.as_mut().write_i32::(self.pos_z) { 428 | Ok(_e) => {msg.append(&mut bs.to_vec())}, 429 | _ => return None, 430 | }; 431 | 432 | match bs.as_mut().write_i32::(self.euler_x) { 433 | Ok(_e) => {msg.append(&mut bs.to_vec())}, 434 | _ => return None, 435 | }; 436 | 437 | match bs.as_mut().write_i32::(self.euler_y) { 438 | Ok(_e) => {msg.append(&mut bs.to_vec())}, 439 | _ => return None, 440 | }; 441 | 442 | match bs.as_mut().write_i32::(self.euler_z) { 443 | Ok(_e) => {msg.append(&mut bs.to_vec())}, 444 | _ => return None, 445 | }; 446 | 447 | msg.append(&mut self.add_ren_info.to_vec()); 448 | 449 | 450 | Some((msg,DispatchType::PosUpdate)) 451 | } 452 | } 453 | 454 | impl Dispatch for Emoji { 455 | fn dispatch(&self, _logic : &Logic) -> Option<(Vec,DispatchType)> { 456 | let mut msg = Vec::new(); 457 | 458 | msg.append(&mut self.uid.to_vec()); 459 | let mut timestamp = [0u8; mem::size_of::()]; 460 | match timestamp.as_mut().write_u32::(timer.elapsed().as_secs_f32() as u32) { 461 | _ => (), 462 | }; 463 | msg.append(&mut timestamp.to_vec()); 464 | msg.push(self.emoji); 465 | Some((msg,DispatchType::Emoji)) 466 | } 467 | } 468 | -------------------------------------------------------------------------------- /maze_gameserver/src/gamelogic/rabbitcolor.rs: -------------------------------------------------------------------------------- 1 | use serde::{Serialize, Deserialize}; 2 | #[derive(Serialize, Deserialize,Clone,Copy)] 3 | pub struct RabbitColor { 4 | raw : u8 5 | } 6 | 7 | 8 | impl RabbitColor { 9 | pub fn new() -> Self { 10 | Self { raw : rand::random::() % 5} 11 | } 12 | 13 | pub fn make_white(&mut self) { 14 | self.raw=5; 15 | } 16 | 17 | pub fn get_raw(&self) -> u8 { 18 | self.raw 19 | } 20 | } -------------------------------------------------------------------------------- /maze_gameserver/src/gamelogic/raceman.rs: -------------------------------------------------------------------------------- 1 | use super::timer; 2 | 3 | pub struct RaceManager { 4 | last_checkpoint : u8, 5 | last_checkpoint_time : f32, 6 | first_checkpoint_time : f32, 7 | } 8 | 9 | impl RaceManager { 10 | pub fn new() -> Self{ 11 | Self { 12 | last_checkpoint : 255, // no last checkpoint 13 | last_checkpoint_time : timer.elapsed().as_secs_f32(), 14 | first_checkpoint_time : 0.0, 15 | } 16 | } 17 | pub fn eval(&mut self, checkpoint : u8) -> bool { 18 | if self.last_checkpoint == 255 { 19 | self.first_checkpoint_time = timer.elapsed().as_secs_f32(); 20 | } 21 | 22 | if ( timer.elapsed().as_secs_f32() - self.last_checkpoint_time ) <= 10.0 || self.last_checkpoint == 255 { 23 | if checkpoint == self.last_checkpoint+1 { 24 | self.last_checkpoint = checkpoint; 25 | self.last_checkpoint_time = timer.elapsed().as_secs_f32(); 26 | return true 27 | } else if checkpoint == self.last_checkpoint { 28 | return false 29 | } else { 30 | self.last_checkpoint = 255; 31 | return false; 32 | } 33 | } else { 34 | self.last_checkpoint = 255; 35 | return false; 36 | } 37 | } 38 | 39 | pub fn reset(&mut self) { 40 | self.last_checkpoint = 255; 41 | } 42 | 43 | pub fn get_total_time(&self) -> f32 { 44 | timer.elapsed().as_secs_f32() - self.first_checkpoint_time 45 | } 46 | } -------------------------------------------------------------------------------- /maze_gameserver/src/gamelogic/score.rs: -------------------------------------------------------------------------------- 1 | use serde::Serialize; 2 | use super::ClientKey; 3 | 4 | #[derive(Serialize)] 5 | pub struct Score { 6 | key : ClientKey, 7 | name : Vec, 8 | score : f32, 9 | } 10 | 11 | impl Score { 12 | pub fn new(key : ClientKey, name : Vec, score_f32 : f32) -> Self { 13 | Self { 14 | key : key, 15 | name : name, 16 | score : score_f32, 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /maze_gameserver/src/gamelogic/scoreboard_manager.rs: -------------------------------------------------------------------------------- 1 | use std::net::TcpStream; 2 | use std::io::prelude::*; 3 | use std::time::Duration; 4 | use super::score::Score; 5 | 6 | 7 | const addr : &str = "127.0.0.1:42069"; 8 | 9 | 10 | pub fn push_score(score : &Score) { 11 | 12 | let mut s = match TcpStream::connect(addr) { 13 | Ok(e) => e, 14 | _ => { 15 | log::error!("could not connect to master server on {}", addr); 16 | panic!("could not connect to master server on {}", addr) 17 | } 18 | }; 19 | 20 | match s.set_write_timeout(Some(Duration::from_millis(50))) { 21 | Ok(e) => e, 22 | _ => { 23 | log::error!("could not set write timeout on tcp socket"); 24 | } 25 | } 26 | 27 | let mut msg = Vec::new(); 28 | bincode::serialize_into(&mut msg, score).unwrap(); 29 | 30 | match s.write(msg.as_mut_slice()) { 31 | Ok(_) => (), 32 | _ => { 33 | log::error!("failed to push score update to master server") 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /maze_gameserver/src/gamelogic/types.rs: -------------------------------------------------------------------------------- 1 | use super::Logic; 2 | 3 | #[derive(PartialEq)] 4 | pub enum DispatchType { 5 | PosUpdate, 6 | Emoji, 7 | } 8 | 9 | pub enum PacketParseResult { 10 | OkDispatch(T), 11 | Ignore, 12 | HardError, 13 | } 14 | 15 | pub trait Dispatch { 16 | fn dispatch(&self, logic : &Logic) -> Option<(Vec,DispatchType)>; 17 | } -------------------------------------------------------------------------------- /maze_gameserver/src/gamelogic/unlocks.rs: -------------------------------------------------------------------------------- 1 | use serde::{Serialize, Deserialize}; 2 | #[derive(Debug, Clone, Serialize, Deserialize,Copy)] 3 | pub struct Unlocks { 4 | raw: u8, 5 | } 6 | 7 | impl Unlocks { 8 | pub fn new() -> Self { 9 | Self { raw: 0 } 10 | } 11 | 12 | pub fn get(&self,index : u8) -> bool { 13 | ( self.raw>>index ) & 1 == 1 14 | } 15 | 16 | pub fn get_raw(&self) -> u8 { 17 | self.raw 18 | } 19 | 20 | pub fn get_total(&self) -> i32 { 21 | let mut ret = 0; 22 | for i in 0..7 { 23 | if self.get(i) { 24 | ret+=1; 25 | } 26 | } 27 | ret 28 | } 29 | 30 | pub fn set(&mut self,index : u8) { 31 | self.raw = self.raw | (1< Result<(), fern::InitError> { 14 | // configure colors for the whole line 15 | let colors_line = ColoredLevelConfig::new() 16 | .error(Color::Red) 17 | .warn(Color::Yellow) 18 | // we actually don't need to specify the color for debug and info, they are white by default 19 | .info(Color::White) 20 | .debug(Color::Cyan) 21 | // depending on the terminals color scheme, this is the same as the background color 22 | .trace(Color::BrightBlack); 23 | 24 | // configure colors for the name of the level. 25 | // since almost all of them are the same as the color for the whole line, we 26 | // just clone `colors_line` and overwrite our changes 27 | let colors_level = colors_line.clone().info(Color::Green); 28 | // here we set up our fern Dispatch 29 | fern::Dispatch::new() 30 | .format(move |out, message, record| { 31 | out.finish(format_args!( 32 | "{color_line}[{date}][{target}][{level}{color_line}] {message}\x1B[0m", 33 | color_line = format_args!( 34 | "\x1B[{}m", 35 | colors_line.get_color(&record.level()).to_fg_str() 36 | ), 37 | date = chrono::Local::now().format("%Y-%m-%d %H:%M:%S"), 38 | target = record.target(), 39 | level = colors_level.color(record.level()), 40 | message = message, 41 | )); 42 | }) 43 | // set the default log level. to filter out verbose log messages from dependencies, set 44 | // this to Warn and overwrite the log level for your crate. 45 | .level(log::LevelFilter::Trace) 46 | // change log levels for individual modules. Note: This looks for the record's target 47 | // field which defaults to the module path but can be overwritten with the `target` 48 | // parameter: 49 | // `info!(target="special_target", "This log message is about special_target");` 50 | .level_for("pretty_colored", log::LevelFilter::Trace) 51 | // output to stdout 52 | .chain(std::io::stdout()) 53 | .chain(fern::log_file("aluminium.log")?) 54 | .apply() 55 | .unwrap(); 56 | 57 | Ok(()) 58 | } 59 | 60 | fn _main() { 61 | match set_up_logging() { 62 | Ok(_e) => (), 63 | Err(_e) => println!("failed to initialize logger"), 64 | } 65 | let word = b"hello test test test abc lul f f f f fnsdfasdfjoisdfgjdf dsfhjadfhgosdfgojd fjggdfh adfggodnfgdfigdfgnadongojfdnb"; 66 | 67 | if word.to_vec() == gamelogic::Logic::decode_pkt(gamelogic::Logic::encode_pkt(word.to_vec()).as_mut_slice()) { 68 | log::info!("decode / encode test passed successfully"); 69 | } else { 70 | log::error!("decode / encode test failed"); 71 | } 72 | 73 | 74 | if (fs::metadata("clients/")).is_err() { 75 | match fs::create_dir("clients") { 76 | Ok(_e) => log::info!("created directory clients/ "), 77 | Err(_e) => log::error!( 78 | "lacking permissions to create clients directory, this will lead to future errors" 79 | ), 80 | }; 81 | } 82 | 83 | log::info!("starting game server..."); 84 | aluminium::Server::start((1337, 1339)); 85 | } 86 | 87 | fn main() { 88 | _main(); 89 | std::thread::park(); 90 | } 91 | -------------------------------------------------------------------------------- /maze_masterserver/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "maze-master" 3 | version = "0.1.0" 4 | authors = ["pluxtore "] 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 | hex = "0.4.2" 11 | rocket = "0.4.5" 12 | lazy_static = "1.4.0" 13 | serde = {version = "1.0.114", features = ["derive"]} 14 | serde_derive = "1.0.114" 15 | bincode = "1.0" -------------------------------------------------------------------------------- /maze_masterserver/src/characterstate.rs: -------------------------------------------------------------------------------- 1 | use super::unlocks::Unlocks; 2 | use super::rabbitcolor::RabbitColor; 3 | use serde::{Serialize, Deserialize}; 4 | 5 | #[derive(Serialize, Deserialize)] 6 | pub struct CharacterState { 7 | name: Vec, 8 | position: (i32, i32, i32), 9 | is_alive: bool, 10 | is_new: bool, 11 | pub unlocks: Unlocks, 12 | pub color : RabbitColor, 13 | highscore : f32, 14 | } 15 | -------------------------------------------------------------------------------- /maze_masterserver/src/clientkey.rs: -------------------------------------------------------------------------------- 1 | use serde::Deserialize; 2 | 3 | #[derive(Eq)] 4 | #[derive(PartialEq)] 5 | #[derive(Debug)] 6 | #[derive(Hash)] 7 | #[derive(Clone)] 8 | #[derive(Deserialize)] 9 | pub struct ClientKey { 10 | key : [u8;8], 11 | } 12 | 13 | impl ClientKey { 14 | pub fn new(key : [u8;8]) -> Self { 15 | Self { 16 | key : key, 17 | } 18 | } 19 | 20 | pub fn get_raw_bytes(&self) -> &[u8] { 21 | &self.key 22 | } 23 | } -------------------------------------------------------------------------------- /maze_masterserver/src/main.rs: -------------------------------------------------------------------------------- 1 | #![feature(proc_macro_hygiene, decl_macro)] 2 | #![allow(non_upper_case_globals)] 3 | #[macro_use] extern crate rocket; 4 | #[macro_use] extern crate lazy_static; 5 | mod clientkey; 6 | mod characterstate; 7 | mod score; 8 | mod unlocks; 9 | mod rabbitcolor; 10 | 11 | use std::sync::Mutex; 12 | use std::net::{TcpListener, TcpStream, Shutdown}; 13 | use std::io::prelude::*; 14 | use clientkey::ClientKey; 15 | use score::Score; 16 | use std::collections::HashMap; 17 | use std::ops::DerefMut; 18 | use std::convert::TryInto; 19 | 20 | lazy_static! { 21 | pub static ref board : Mutex = Mutex::new(String::new()); 22 | pub static ref allscores : Mutex> = Mutex::new(HashMap::new()); 23 | } 24 | 25 | #[get("/api/")] 26 | fn api(req: String) -> String { 27 | match req.as_str() { 28 | "health" => String::from("yes"), 29 | "min_port" => String::from("1337"), 30 | "max_port" => String::from("1338"), 31 | "user_queue" => String::from("0"), 32 | "rate_limit" => String::from("20"), 33 | "hostname" => String::from("maze.pluxtore.de"), 34 | "highscore" => board.lock().unwrap().clone(), 35 | "welcome" => String::from("you are not using the official server"), 36 | _ => String::from("invalid req lul :/"), 37 | } 38 | } 39 | 40 | 41 | #[get("/api/highscore/")] 42 | fn highscore(req : String) -> String { 43 | println!("new highscore request"); 44 | let key = match hex::decode(req.clone()) { 45 | Ok(e) => e, 46 | _ => return "".to_string() 47 | }; 48 | if key.len() != 8 { 49 | return "".to_string(); 50 | } 51 | let ckey = ClientKey::new(key.as_slice().try_into().unwrap()); 52 | 53 | 54 | match allscores.lock().unwrap().get(&ckey) { 55 | Some(e) => { 56 | println!("taking from allscores..."); 57 | let mut hs = e.to_string(); 58 | match hs.split_off(3) { 59 | _ => (), 60 | } 61 | return hs; 62 | }, 63 | None => { 64 | return "".to_string(); 65 | }, 66 | }; 67 | } 68 | 69 | 70 | fn main() { 71 | std::thread::spawn(move || { 72 | Handler::new().handler(); 73 | }); 74 | rocket::ignite().mount("/", routes![api,highscore]).launch(); 75 | } 76 | 77 | struct Handler { 78 | scorelist : HashMap, 79 | lowest_hs : f32 80 | } 81 | 82 | impl Handler { 83 | fn new () -> Self { 84 | Self { 85 | scorelist : HashMap::new(), 86 | lowest_hs : 50.0, 87 | } 88 | } 89 | 90 | fn handler(&mut self) { 91 | let listener = match TcpListener::bind("127.0.0.1:42069") { 92 | Ok(e) => e, 93 | _ => { 94 | panic!("could not bind to socket"); 95 | } 96 | }; 97 | 98 | loop { 99 | for stream in listener.incoming() { 100 | match stream { 101 | Ok(e) => { 102 | self.process(e); 103 | } 104 | _ => { 105 | } 106 | } 107 | } 108 | } 109 | } 110 | 111 | fn process(&mut self,mut stream : TcpStream) { 112 | let mut buf : [u8;500] = [0;500]; 113 | let len = match stream.read(&mut buf) { 114 | Ok(e) => e, 115 | _ => return, 116 | }; 117 | 118 | match stream.shutdown(Shutdown::Both) { 119 | _ => (), 120 | } 121 | 122 | 123 | let score : Score = bincode::deserialize(&buf[..len]).unwrap(); 124 | 125 | if !allscores.lock().unwrap().contains_key(&score.key) { 126 | allscores.lock().unwrap().insert(score.key.clone(), score.score); 127 | } else { 128 | * allscores.lock().unwrap().get_mut(&score.key).unwrap() = score.score; 129 | } 130 | 131 | 132 | if self.lowest_hs > score.score || self.scorelist.len() < 10 { 133 | /* create or update entry */ 134 | if !self.scorelist.contains_key(&score.key) { 135 | self.scorelist.insert(score.key, (score.score, String::from_utf8_lossy(score.name.as_slice()).to_string())); 136 | } else { 137 | * self.scorelist.get_mut(&score.key).unwrap() = (score.score, String::from_utf8_lossy(score.name.as_slice()).to_string()); 138 | } 139 | 140 | let mut tmp = Vec::new(); 141 | 142 | for (_,entry) in &self.scorelist { 143 | tmp.push(entry.clone()); 144 | } 145 | 146 | /* order entries */ 147 | tmp.sort_by(|a,b| a.0.partial_cmp(&b.0).unwrap()); 148 | 149 | 150 | let mut lboard = board.lock().unwrap(); 151 | lboard.clear(); 152 | 153 | /* write */ 154 | let mut count = 1; 155 | for entry in tmp { 156 | lboard.deref_mut().push_str(&count.to_string()); 157 | lboard.deref_mut().push_str(". " ); 158 | lboard.deref_mut().push_str(&entry.0.to_string()); 159 | lboard.deref_mut().push_str( " "); 160 | lboard.deref_mut().push_str(&entry.1); 161 | lboard.deref_mut().push('\n'); 162 | count+=1; 163 | } 164 | } 165 | } 166 | } -------------------------------------------------------------------------------- /maze_masterserver/src/rabbitcolor.rs: -------------------------------------------------------------------------------- 1 | use serde::{Serialize, Deserialize}; 2 | #[derive(Serialize, Deserialize,Clone,Copy)] 3 | pub struct RabbitColor { 4 | raw : u8 5 | } -------------------------------------------------------------------------------- /maze_masterserver/src/score.rs: -------------------------------------------------------------------------------- 1 | use serde::Deserialize; 2 | use super::ClientKey; 3 | 4 | #[derive(Deserialize)] 5 | pub struct Score { 6 | pub key : ClientKey, 7 | pub name : Vec, 8 | pub score : f32, 9 | } -------------------------------------------------------------------------------- /maze_masterserver/src/unlocks.rs: -------------------------------------------------------------------------------- 1 | use serde::{Serialize, Deserialize}; 2 | #[derive(Debug, Clone, Serialize, Deserialize,Copy)] 3 | pub struct Unlocks { 4 | raw: u8, 5 | } 6 | 7 | impl Unlocks { 8 | pub fn new() -> Self { 9 | Self { raw: 0 } 10 | } 11 | 12 | pub fn get(&self,index : u8) -> bool { 13 | ( self.raw>>index ) & 1 == 1 14 | } 15 | 16 | pub fn get_raw(&self) -> u8 { 17 | self.raw 18 | } 19 | 20 | pub fn get_total(&self) -> i32 { 21 | let mut ret = 0; 22 | for i in 0..7 { 23 | if self.get(i) { 24 | ret+=1; 25 | } 26 | } 27 | ret 28 | } 29 | 30 | pub fn set(&mut self,index : u8) { 31 | self.raw = self.raw | (1<> 39)) & 0xff 24 | 25 | return encoded 26 | 27 | 28 | def decode(r): 29 | first_random = r[0] 30 | second_random = r[1] 31 | decoded = bytearray() 32 | for i in range(0, len(r) - 2): 33 | decoded.append(first_random ^ r[i+2]) 34 | v21 = first_random + second_random 35 | first_random = (v21 + ((2155905153 * v21) >> 39)) & 0xff 36 | 37 | return decoded 38 | 39 | draw_data = open("whiterabbit_get_there_data", "rb").read() + open("whiterabbit_data", "rb").read() 40 | secret = b"\x33" * 8 # changed for obvious reasons 41 | name = b"The White Rabbit" 42 | padding = ( 42 - (2 + len(name) + len(secret)) ) * b"\x00" 43 | 44 | sock = socket.socket(socket.AF_INET,socket.SOCK_DGRAM) 45 | sock.sendto(encode(b"\x4c" + secret + b"\x00" + name + padding),SERVER) # login 46 | time.sleep(0.5) 47 | indx = 0 48 | basetime = int(time.time()) % 2**32 49 | while True: 50 | sock.sendto(encode(b"\x3c\x33" + secret + struct.pack('d', basetime)),SERVER) # heartbeat 51 | sock.recvfrom(1024) 52 | time.sleep(0.2) 53 | sock.sendto(encode(b"\x50" + secret + struct.pack('i', basetime) + b"\x00" *4 + draw_data[indx:indx+29]),SERVER) # pos updates 54 | indx+=29 55 | if (indx+29)>=len(draw_data): 56 | indx = len(open("whiterabbit_get_there_data", "rb").read()) 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /whiterabbit_bot/whiterabbit_data: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pluxtore/maze-server/5e02137e061315b7b86fb9e7052f2b654b6b66eb/whiterabbit_bot/whiterabbit_data -------------------------------------------------------------------------------- /whiterabbit_bot/whiterabbit_get_there_data: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pluxtore/maze-server/5e02137e061315b7b86fb9e7052f2b654b6b66eb/whiterabbit_bot/whiterabbit_get_there_data --------------------------------------------------------------------------------