diff --git a/src/conn/server.rs b/src/conn/server.rs index 477902a..7b38379 100755 --- a/src/conn/server.rs +++ b/src/conn/server.rs @@ -39,7 +39,7 @@ use std::cell::RefCell; use std::fs::File; use std::io::BufReader; use std::sync::Arc; -use std::sync::mpsc::Sender; +use std::sync::mpsc::{Sender, Receiver, channel}; use std; /// This module contains all of the client facing code. It handles all of the MIO stuff, and user @@ -53,6 +53,7 @@ use std; //Setting the server as the first token pub const SERVER: mio::Token = mio::Token(0); +pub const TIMEOUT: mio::Token = mio::Token(1); /// enum for the current state of the connection. Not Logged in, Logged in, and Closed. enum State { @@ -79,78 +80,84 @@ pub struct Server { //Tried removing the Arc here connections: Slab, games: Arc>, + recv: Receiver, + send: Sender, } impl Server { /// Declares a new server with a tcp connection pub fn new(tcp: TcpListener) -> Server { - let slab = Slab::new_starting_at(mio::Token(1), 1024); + let slab = Slab::new_starting_at(mio::Token(2), 1024); + let (s, r) = channel::(); Server { server: tcp, connections: slab, - games: Arc::new(RefCell::new(Game::new())), + games: Arc::new(RefCell::new(Game::new(s.clone()))), + send: s, + recv: r, } } } impl mio::Handler for Server { - type Timeout = (); - type Message = Msg; - - /// This function is the primary way the gameloop speaks to the clients. It sends a message on - /// the main channel, and this thing reads the message and figures out what to do, and who to send - /// it to. - fn notify(&mut self, event_loop: &mut mio::EventLoop, msg: Self::Message) { - match msg { - Msg::TextOutput(token, result, message) => { - // Write message - if self.connections.contains(token) { - self.connections[token].write_text_out(result, &message); - } - if self.connections.contains(token) { - self.connections[token].reregister_writable(event_loop); - } - }, - Msg::Screen(token, screen) => { - //Write screen - if self.connections.contains(token) { - self.connections[token].write_zipped_screen(screen); - } - if self.connections.contains(token) { - self.connections[token].reregister_writable(event_loop); - } - }, - Msg::SendCommand(token, send) => { - //Tell it to send a command - //TODO Revamp this. We should not send this but once. - if self.connections.contains(token) { - self.connections[token].send_command(send); - } - }, - Msg::Hp(token, hp) => { - if self.connections.contains(token) { - self.connections[token].write_stat_all(hp, 500, 100, 100, 25, 1000000, 3000000, 6, 10); - } - if self.connections.contains(token) { - self.connections[token].reregister_writable(event_loop); - } - }, - Msg::Shout(msg) => { - let mut tokens = vec![]; - for t in self.connections.iter() { - if t.token.as_usize() != 0 { - tokens.push(t.token); + type Timeout = mio::Token; + type Message = (); + + fn timeout(&mut self, event_loop: &mut mio::EventLoop, timeout: mio::Token) { + loop { + match self.recv.try_recv() { + Ok(msg) => { + match msg { + Msg::TextOutput(token, result, message) => { + // Write message + if self.connections.contains(token) { + self.connections[token].write_text_out(result, &message); + } + if self.connections.contains(token) { + self.connections[token].reregister_writable(event_loop); + } + }, + Msg::Screen(token, screen) => { + //Write screen + if self.connections.contains(token) { + self.connections[token].write_zipped_screen(screen); + } + if self.connections.contains(token) { + self.connections[token].reregister_writable(event_loop); + } + }, + Msg::Hp(token, hp) => { + if self.connections.contains(token) { + self.connections[token].write_stat_all(hp, 500, 100, 100, 25, 1000000, 3000000, 6, 10); + } + if self.connections.contains(token) { + self.connections[token].reregister_writable(event_loop); + } + }, + Msg::Shout(msg) => { + let mut tokens = vec![]; + for t in self.connections.iter() { + if t.token.as_usize() != 0 { + tokens.push(t.token); + } + } + for token in tokens { + self.connections[token].write_text_out(4,&msg); + self.connections[token].reregister_writable(event_loop); + } + }, + _ => { + panic!("Oh no!"); + } } - } - for token in tokens { - self.connections[token].write_text_out(4,&msg); - self.connections[token].reregister_writable(event_loop); - } - }, - _ => { - panic!("Oh no!"); + }, + Err(_) => { + break; + }, } } + //Essentially this is acting as a coroutine to yield so other messages can be handled. + let _ = event_loop.timeout_ms(TIMEOUT, 1); } fn ready(&mut self, event_loop: &mut mio::EventLoop, token: mio::Token, events: mio::EventSet){ @@ -177,7 +184,7 @@ impl mio::Handler for Server { println!("Something def fucked up"); //event_loop.shutdown(); }, - }; + } }, _ => { //otherwise, call the server's ready connection. @@ -216,7 +223,6 @@ struct Connection { socket: TcpStream, token: mio::Token, to_client_queue: Vec, - from_client_queue: Vec, event_set: mio::EventSet, state: State, } @@ -229,7 +235,6 @@ impl Connection{ name: "".to_string(), token: token, to_client_queue: vec![], - from_client_queue: vec![], event_set: mio::EventSet::readable(), state: State::NotLoggedIn, } @@ -245,16 +250,9 @@ impl Connection{ }, } } - - fn send_command(&mut self, send: Sender) { - if self.from_client_queue.len() > 0 { - let command = self.from_client_queue.pop().unwrap(); - send.send(Msg::Command(self.token.clone(), command)); - } - } fn quit(&mut self, event_loop: &mut mio::EventLoop) { - let game_loop = self.games.borrow_mut().get_or_create_game_loop("map", event_loop); + let game_loop = self.games.borrow_mut().get_or_create_game_loop("map"); game_loop.borrow_mut().remove(self.token.clone()); } @@ -313,10 +311,12 @@ impl Connection{ let mut m = format!("{} shouts: {} ", self.name, msg).to_string(); //Doing this the trivially easy way, just doing a notification for //that gets pushed to everyone - let send = event_loop.channel(); + let send = self.games.borrow_mut().send.clone(); send.send(Msg::Shout(m)); } else { - self.from_client_queue.push(command.to_string()); + let game_loop = self.games.borrow_mut().get_or_create_game_loop("map"); + game_loop.borrow_mut().send_command(Msg::Command(self.token.clone(), + command.to_string())); } n = n - (2 + length); } @@ -449,7 +449,7 @@ impl Connection{ println!("Tiles"); self.reregister_writable(event_loop); println!("Writable"); - let game_loop = self.games.borrow_mut().get_or_create_game_loop("map", event_loop); + let game_loop = self.games.borrow_mut().get_or_create_game_loop("map"); game_loop.borrow_mut().join(self.token.clone(), self.name.clone()); println!("Looped"); //This is here only while it is a single user. Normally, these would be added to the game_loop, not set. diff --git a/src/game/characters/mod.rs b/src/game/characters/mod.rs index 213d54a..a3f396b 100755 --- a/src/game/characters/mod.rs +++ b/src/game/characters/mod.rs @@ -24,6 +24,7 @@ use game::gamemap::GameMap; /// deciding what tile to draw. #[derive(Clone)] pub enum Direction { + All, North, South, East, diff --git a/src/game/characters/projectile.rs b/src/game/characters/projectile.rs index 3c27cb0..cd02d8e 100755 --- a/src/game/characters/projectile.rs +++ b/src/game/characters/projectile.rs @@ -235,6 +235,7 @@ impl Projectile { Direction::NorthEast => {"NE"}, Direction::SouthWest => {"SW"}, Direction::SouthEast => {"SE"}, + _ => {"S"}, }; format!("{}{}1",self.tile, direction) //format!("{}",self.tile) diff --git a/src/game/gameloop.rs b/src/game/gameloop.rs index 58f60ae..6a63897 100755 --- a/src/game/gameloop.rs +++ b/src/game/gameloop.rs @@ -31,6 +31,7 @@ use std::thread; use std::thread::sleep; use std::time::Duration; use std::sync::RwLock; +use std::sync::Mutex; use std::sync::Arc; use game::gamemap::GameMap; @@ -44,16 +45,18 @@ pub struct GameLoop { //Map with all items & tiles game_map: Arc>, connections: Arc>>, - send: mio::Sender, + command_queue: Arc>>, + to_game_send: Sender, } impl GameLoop { ///creates a new game loop - pub fn new(mapname : &str, send: mio::Sender) -> GameLoop { + pub fn new(mapname : &str, send: Sender) -> GameLoop { let mut gloop = GameLoop { game_map: Arc::new(RwLock::new(GameMap::new(mapname).unwrap())), connections: Arc::new(RwLock::new(vec![])), - send: send, + command_queue: Arc::new(Mutex::new(vec![])), + to_game_send: send, }; gloop.start(); gloop @@ -72,41 +75,32 @@ impl GameLoop { pub fn start(&mut self) { let game_map = self.game_map.clone(); let connections = self.connections.clone(); - let to_mio = self.send.clone(); + let commands = self.command_queue.clone(); + let to_mio = self.to_game_send.clone(); thread::spawn(move || { - let (send, recv) = channel(); + //TODO give this sender to someone. loop { - let mut threads = vec![]; thread::sleep(Duration::from_millis(20)); - println!("Creating conns"); - let mutex = connections.read().unwrap(); - for connection in mutex.iter(){ - let s = send.clone(); - let c = connection.clone(); - let t = to_mio.clone(); - threads.push(thread::spawn(move|| { - let _ = t.send(Msg::SendCommand(c, s)); - })); - } - for t in threads { - t.join().unwrap(); - } let mut map = game_map.write().unwrap(); //This can cause DOS by keeping the commands from executing println!("Reading commands"); - 'outer: loop { - match recv.try_recv() { - Ok(Msg::Command(token, command)) => { - //println!("{}", command); - &map.push_command(token, command); - }, - _ => { - //println!("Nothin."); - break 'outer; - } - } + //Putting this in a scope so that the commands can be repopulated when it is executing. + { + let mut c = commands.lock().unwrap(); + for m in c.drain(..) { + match m { + Msg::Command(token, command) => { + //println!("{}", command); + &map.push_command(token.clone(), command.clone()); + }, + _ => { + //println!("Nothin."); + }, + } + } } //TODO get these responses in there somehow + let mutex = connections.read().unwrap(); let responses = map.execute(&mutex); //Cannot seem to decontruct tuples in a loop. Doing the index version instead of //iterating @@ -124,18 +118,11 @@ impl GameLoop { } let screen = map.send_portion(conn.clone()); //Need to see response from sender - match to_mio.send(Msg::Screen(conn.clone(), screen.clone())) { - Err(mio::NotifyError::Io(_)) => { - println!("IO"); - }, - Err(mio::NotifyError::Full(_)) => { - println!("FUll"); - }, - Err(mio::NotifyError::Closed(_)) => { - println!("Closed"); - }, - Ok(_) => { - }, + match screen { + Some(s) => { + to_mio.send(Msg::Screen(conn.clone(), s)); + }, + None => {}, } } println!("Finished Loop"); @@ -161,4 +148,8 @@ impl GameLoop { } } } + + pub fn send_command(&mut self, message: Msg) { + self.command_queue.lock().unwrap().push(message); + } } diff --git a/src/game/gamemap.rs b/src/game/gamemap.rs index 303bb7e..0f23f93 100755 --- a/src/game/gamemap.rs +++ b/src/game/gamemap.rs @@ -70,6 +70,7 @@ impl GameMap { let t_index = 405; tiles[t_index as usize].user = Some(MapUser::new(None,Commandable::T(Tower::new()))); + tiles[t_index as usize].blocked = true; let mut ti = Arc::new(RwLock::new(tiles)); let map = GameMap { width: 30, @@ -102,19 +103,23 @@ impl GameMap { let old = self.get_user(o); let mut tiles = self.tiles.write().unwrap(); let ref mut new = tiles[n as usize]; - match new.user { - Some(_) => { - false - }, - None => { - println!("None"); - let mut u = old.user.clone().unwrap(); - u.clear_movement_if_at_destination(n); - u.set_direction(d.clone()); - new.user = Some(u); - new.blocked = true; - true + if !new.blocked { + match new.user { + Some(_) => { + false + }, + None => { + println!("None"); + let mut u = old.user.clone().unwrap(); + u.clear_movement_if_at_destination(n); + u.set_direction(d.clone()); + new.user = Some(u); + new.blocked = true; + true + } } + } else { + false } } @@ -224,7 +229,9 @@ impl GameMap { /// Returns the x,y value of a token fn find_tile_with_token(&self, token: mio::Token) -> Option<(u32, u32)> { + println!("Finding Tiles"); let tiles = self.tiles.read().unwrap(); + println!("Found Tiles!"); let len = tiles.len(); for t in 0..len { match tiles[t as usize].user { @@ -467,32 +474,44 @@ impl GameMap { /// This pulls the HP from health. pub fn get_hp(&self, token: mio::Token) -> Option { - let (x, y) = self.find_tile_with_token(token.clone()).unwrap(); - let tiles = self.tiles.read().unwrap(); - let ref tile = tiles[y as usize * self.width as usize + x as usize]; - match tile.user { - Some(ref user) => { - match user.player { - Commandable::P(ref player) => { - Some(player.hp as i32) + match self.find_tile_with_token(token.clone()) { + Some((x,y)) => { + let tiles = self.tiles.read().unwrap(); + let ref tile = tiles[y as usize * self.width as usize + x as usize]; + match tile.user { + Some(ref user) => { + match user.player { + Commandable::P(ref player) => { + Some(player.hp as i32) + }, + _ => { + None + }, + } }, _ => { - None + None }, } }, - _ => { - None + None=> { + None }, } } /// This generates a new MapScreen based on the location of the given connection's user - pub fn send_portion(&self, token: mio::Token) -> MapScreen { + pub fn send_portion(&self, token: mio::Token) -> Option { println!("Send Portion"); //This sends the squares around the user, which will always be centered in the screen. - let (x, y) = self.find_tile_with_token(token.clone()).unwrap(); - MapScreen::new(self, x, y) + match self.find_tile_with_token(token.clone()) { + Some((x, y)) => { + Some(MapScreen::new(self, x, y)) + }, + None => { + None + }, + } } /// Adds a player to the map. Puts it at the starting location. @@ -501,42 +520,89 @@ impl GameMap { let sx = self.start_x.clone(); let sy = self.start_y.clone(); self.add_player_at(MapUser::new(Some(token.clone()), Commandable::P(Player::new(name))), - sx, sy); + sx, sy, Direction::All); } ///Recursively searches for an open location. Sadly this is a horrible algorithm, and will build characters - ///all in one direction before it tries any of the others. Ugly. - fn add_player_at(&mut self, user: MapUser, x: u8, y: u8) -> bool { + ///all in one direction before it tries any of the others. Ugly. What really kills it is that + ///it exectures on the game loop & stops everything while it searches. If the x direction is + ///full, it will take forever. + fn add_player_at(&mut self, user: MapUser, x: u8, y: u8, direction: Direction) -> bool { let mut start_user = None; - { - let mut tiles = self.tiles.write().unwrap(); - let ref mut start = tiles[y as usize * self.width as usize + x as usize]; - start_user = start.user.clone(); - } + let mut blocked = false; if x >= 0 && x < self.width && y >=0 && y < self.height { - match start_user { - None => { - println!("Open tile at {} {}", x, y); - let mut tiles = self.tiles.write().unwrap(); - let ref mut start = tiles[y as usize * self.width as usize + x as usize]; - start.user = Some(user); - true - }, - Some(_) => { - if x == 0 && y > 0 { - self.add_player_at(user.clone(), x+1, y) || self.add_player_at(user.clone(), x, y-1) || self.add_player_at(user.clone(), x, y+1) - } else if x == 0 && y == 0 { - self.add_player_at(user.clone(), x+1, y) || self.add_player_at(user.clone(), x, y+1) - } else if x > 0 && y == 0 { - self.add_player_at(user.clone(), x-1, y) || self.add_player_at(user.clone(), x+1, y) || self.add_player_at(user.clone(), x, y+1) - } else { - self.add_player_at(user.clone(), x-1, y) || self.add_player_at(user.clone(), x+1, y) || self.add_player_at(user.clone(), x, y-1) || self.add_player_at(user.clone(), x, y+1) - } - }, + { + let mut tiles = self.tiles.write().unwrap(); + let ref mut start = tiles[y as usize * self.width as usize + x as usize]; + start_user = start.user.clone(); + blocked = start.blocked; + } + if !blocked { + match start_user { + None => { + println!("Open tile at {} {}", x, y); + let mut tiles = self.tiles.write().unwrap(); + let ref mut start = tiles[y as usize * self.width as usize + x as usize]; + start.user = Some(user); + start.blocked = true; + true + }, + Some(_) => { + match direction { + Direction::All => { + (x > 0 && self.add_player_at(user.clone(), x-1, y, Direction::West)) + || self.add_player_at(user.clone(), x+1, y, Direction::East) + || (y > 0 && self.add_player_at(user.clone(), x, y-1, Direction::North)) + || self.add_player_at(user.clone(), x, y+1, Direction::South) + }, + Direction::East=> { + self.add_player_at(user.clone(), x + 1, y, Direction::East) + }, + Direction::West=> { + x > 0 && self.add_player_at(user.clone(), x - 1, y, Direction::West) + + }, + Direction::South=> { + self.add_player_at(user.clone(), x, y+1, Direction::South) + }, + Direction::North=> { + y > 0 && self.add_player_at(user.clone(), x, y-1, Direction::North) + }, + _ => { + false + }, + } + }, + } + } else { + match direction { + Direction::All => { + (x > 0 && self.add_player_at(user.clone(), x-1, y, Direction::West)) + || self.add_player_at(user.clone(), x+1, y, Direction::East) + || (y > 0 && self.add_player_at(user.clone(), x, y-1, Direction::North)) + || self.add_player_at(user.clone(), x, y+1, Direction::South) + }, + Direction::East=> { + self.add_player_at(user.clone(), x + 1, y, Direction::East) + }, + Direction::West=> { + x > 0 && self.add_player_at(user.clone(), x - 1, y, Direction::West) + }, + Direction::South=> { + self.add_player_at(user.clone(), x, y+1, Direction::South) + }, + Direction::North=> { + y > 0 && self.add_player_at(user.clone(), x, y-1, Direction::North) + }, + _ => { + false + }, + } } } else { false } + } /// Removes a player from the map. diff --git a/src/game/mod.rs b/src/game/mod.rs index 2ac5676..5ac484f 100755 --- a/src/game/mod.rs +++ b/src/game/mod.rs @@ -28,6 +28,7 @@ use std::sync::Arc; use std::cell::RefCell; use std::fs::File; use std::io::BufReader; +use std::sync::mpsc::{channel, Sender}; use game::gameloop::GameLoop; use conn::server::{Server, Msg}; @@ -38,11 +39,12 @@ use conn::server::{Server, Msg}; pub struct Game { game_loops: HashMap>>, pub mappings: HashMap, + pub send: Sender, } impl Game { ///Creates a new game struct. Initilizes a new hashmap, and reads the tile map file. - pub fn new() -> Game { + pub fn new(send: Sender) -> Game { let mut m: HashMap = HashMap::new(); let tile_file = File::open("file_full").unwrap(); let mut reader = BufReader::new(tile_file); @@ -56,18 +58,17 @@ impl Game { Game { game_loops: HashMap::new(), mappings: m, + send: send, } } ///Creates a new game loop with the given name, or finds it already in the hashmap. Starts the ///game loop if the map is created. - pub fn get_or_create_game_loop(&mut self, map_name: &str, event_loop: &mut mio::EventLoop) -> Arc> { + pub fn get_or_create_game_loop(&mut self, map_name: &str) -> Arc> { println!("{}", map_name); //This can handle all kinds of things. Checks last time user was inside, if too long it recreates. //Checks the hashmap for the Gameloop. If not there, it creates a new one, adds it and returns it. - let send = event_loop.channel(); - let _ = send.send(Msg::TextOutput(mio::Token(1), 2, "test".to_string())); - let game_loop = self.game_loops.entry(map_name.to_string()).or_insert(Arc::new(RefCell::new(GameLoop::new(map_name, send)))); + let game_loop = self.game_loops.entry(map_name.to_string()).or_insert(Arc::new(RefCell::new(GameLoop::new(map_name, self.send.clone())))); game_loop.clone() } } diff --git a/src/main.rs b/src/main.rs index 9c4a81f..fb61fed 100755 --- a/src/main.rs +++ b/src/main.rs @@ -42,7 +42,7 @@ fn main() { println!("event_loop"); event_loop.register(&server, conn::server::SERVER).unwrap(); println!("register"); - + event_loop.timeout_ms(conn::server::TIMEOUT, 1).unwrap(); let mut moba = Server::new(server); let _ = event_loop.run(&mut moba).unwrap(); }