diff --git a/Cargo.lock b/Cargo.lock index a4a7395b05..177191b539 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1614,6 +1614,14 @@ dependencies = [ "num-iter 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "portpicker" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "pretty_env_logger" version = "0.3.0" @@ -2360,6 +2368,7 @@ dependencies = [ "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "msgbox 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "portpicker 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)", "simplelog 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2750,6 +2759,7 @@ dependencies = [ "checksum pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)" = "676e8eb2b1b4c9043511a9b7bea0915320d7e502b0a079fb03f9635a5252b18c" "checksum png 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f0b0cabbbd20c2d7f06dbf015e06aad59b6ca3d9ed14848783e98af9aaf19925" "checksum png 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)" = "63daf481fdd0defa2d1d2be15c674fbfa1b0fd71882c303a91f9a79b3252c359" +"checksum portpicker 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5b497d05c16fe00939445c00a4fe2fa4f3d3dfc9c0401a3ab5c577afda2debb9" "checksum pretty_env_logger 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "df8b3f4e0475def7d9c2e5de8e5a1306949849761e107b360d03e98eafaffd61" "checksum proc-macro2 0.4.28 (registry+https://github.com/rust-lang/crates.io-index)" = "ba92c84f814b3f9a44c5cfca7d2ad77fa10710867d2bbb1b3d175ab5f47daa12" "checksum quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0" diff --git a/server/src/lib.rs b/server/src/lib.rs index d4e9af9777..9ea247aaad 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -8,10 +8,19 @@ pub mod input; // Reexports pub use crate::{error::Error, input::Input}; -use crate::{ - client::{Client, Clients}, - cmd::CHAT_COMMANDS, +use std::{ + collections::HashSet, + net::SocketAddr, + sync::mpsc, + time::Duration, + i32, }; +use specs::{ + join::Join, saveload::MarkedBuilder, world::EntityBuilder as EcsEntityBuilder, Builder, + Entity as EcsEntity, +}; +use threadpool::ThreadPool; +use vek::*; use common::{ comp, comp::character::Animation, @@ -20,14 +29,11 @@ use common::{ state::{State, Uid}, terrain::TerrainChunk, }; -use specs::{ - join::Join, saveload::MarkedBuilder, world::EntityBuilder as EcsEntityBuilder, Builder, - Entity as EcsEntity, -}; -use std::{collections::HashSet, i32, net::SocketAddr, sync::mpsc, time::Duration}; -use threadpool::ThreadPool; -use vek::*; use world::World; +use crate::{ + client::{Client, Clients}, + cmd::CHAT_COMMANDS, +}; const CLIENT_TIMEOUT: f64 = 20.0; // Seconds @@ -51,9 +57,15 @@ pub struct Server { } impl Server { - /// Create a new `Server`. + /// Create a new `Server` bound to the default socket. #[allow(dead_code)] pub fn new() -> Result<Self, Error> { + Self::bind(SocketAddr::from(([0; 4], 59003))) + } + + /// Create a new server bound to the given socket + #[allow(dead_code)] + pub fn bind<A: Into<SocketAddr>>(addrs: A) -> Result<Self, Error> { let (chunk_tx, chunk_rx) = mpsc::channel(); let mut state = State::new(); @@ -63,7 +75,7 @@ impl Server { state, world: World::new(), - postoffice: PostOffice::bind(SocketAddr::from(([0; 4], 59003)))?, + postoffice: PostOffice::bind(addrs.into())?, clients: Clients::empty(), thread_pool: threadpool::Builder::new() diff --git a/voxygen/Cargo.toml b/voxygen/Cargo.toml index 8a7f84fe3d..e470bc23c7 100644 --- a/voxygen/Cargo.toml +++ b/voxygen/Cargo.toml @@ -46,3 +46,4 @@ fnv = "1.0" simplelog = "0.5" msgbox = "0.1" directories = "1.0" +portpicker = "0.1" diff --git a/voxygen/src/menu/main/client_init.rs b/voxygen/src/menu/main/client_init.rs index 732b849179..82b920754c 100644 --- a/voxygen/src/menu/main/client_init.rs +++ b/voxygen/src/menu/main/client_init.rs @@ -3,6 +3,8 @@ use common::comp; use std::{ sync::mpsc::{channel, Receiver, TryRecvError}, thread::{self, JoinHandle}, + net::ToSocketAddrs, + time::Duration, }; #[derive(Debug)] @@ -20,14 +22,17 @@ pub struct ClientInit { rx: Receiver<Result<Client, Error>>, } impl ClientInit { - pub fn new(connection_args: (String, u16, bool), client_args: (comp::Player, u64)) -> Self { + pub fn new(connection_args: (String, u16, bool), client_args: (comp::Player, u64), wait: bool) -> Self { let (server_address, default_port, prefer_ipv6) = connection_args; let (player, view_distance) = client_args; let (tx, rx) = channel(); let handle = Some(thread::spawn(move || { - use std::net::ToSocketAddrs; + // Sleep the thread to wait for the single-player server to start up + if wait { + thread::sleep(Duration::from_millis(250)); + } // Parses ip address or resolves hostname // Note: if you use an ipv6 address the number after the last colon will be used as the port unless you use [] around the address match server_address diff --git a/voxygen/src/menu/main/mod.rs b/voxygen/src/menu/main/mod.rs index 78b431b9a8..417643863a 100644 --- a/voxygen/src/menu/main/mod.rs +++ b/voxygen/src/menu/main/mod.rs @@ -102,6 +102,7 @@ impl PlayState for MainMenuState { client_init = client_init.or(Some(ClientInit::new( (server_address, DEFAULT_PORT, false), (comp::Player::new(username.clone()), 300), + false, ))); } MainMenuEvent::StartSingleplayer => { diff --git a/voxygen/src/menu/main/start_singleplayer.rs b/voxygen/src/menu/main/start_singleplayer.rs index 0d3c8d81f0..c160439d04 100644 --- a/voxygen/src/menu/main/start_singleplayer.rs +++ b/voxygen/src/menu/main/start_singleplayer.rs @@ -1,19 +1,24 @@ -use super::{client_init::ClientInit, DEFAULT_PORT}; +use std::net::SocketAddr; +use common::comp; use crate::{ menu::char_selection::CharSelectionState, singleplayer::Singleplayer, Direction, GlobalState, PlayState, PlayStateResult, }; -use common::comp; +use super::{client_init::ClientInit, DEFAULT_PORT}; pub struct StartSingleplayerState { singleplayer: Singleplayer, + sock: SocketAddr, } impl StartSingleplayerState { /// Create a new `MainMenuState` pub fn new() -> Self { + let (singleplayer, sock) = Singleplayer::new(); + Self { - singleplayer: Singleplayer::new(), + singleplayer, + sock, } } } @@ -23,11 +28,12 @@ impl PlayState for StartSingleplayerState { match direction { Direction::Forwards => { let username = "singleplayer".to_owned(); - let server_address = "localhost".to_owned(); + let server_address = self.sock.ip().to_string(); let client_init = ClientInit::new( - (server_address.clone(), DEFAULT_PORT, false), + (server_address.clone(), self.sock.port(), false), (comp::Player::new(username.clone()), 300), + true, ); // Client creation diff --git a/voxygen/src/singleplayer.rs b/voxygen/src/singleplayer.rs index c4ad5f6eb9..364646ac24 100644 --- a/voxygen/src/singleplayer.rs +++ b/voxygen/src/singleplayer.rs @@ -1,9 +1,13 @@ -use common::clock::Clock; +use std::{ + sync::mpsc::{channel, Receiver, Sender, TryRecvError}, + time::Duration, + thread::{self, JoinHandle}, + net::SocketAddr, +}; use log::info; +use portpicker::pick_unused_port; +use common::clock::Clock; use server::{Event, Input, Server}; -use std::sync::mpsc::{channel, Receiver, Sender, TryRecvError}; -use std::time::Duration; -use std::{thread, thread::JoinHandle}; const TPS: u64 = 30; @@ -19,15 +23,21 @@ pub struct Singleplayer { } impl Singleplayer { - pub fn new() -> Self { + pub fn new() -> (Self, SocketAddr) { let (sender, reciever) = channel(); + + let sock = SocketAddr::from(([0; 4], pick_unused_port() + .expect("Failed to find unused port"))); + + let sock2 = sock.clone(); let thread = thread::spawn(move || { - run_server(reciever); + run_server(sock2, reciever); }); - Singleplayer { + + (Singleplayer { server_thread: thread, sender, - } + }, sock) } } @@ -37,14 +47,14 @@ impl Drop for Singleplayer { } } -fn run_server(rec: Receiver<Msg>) { +fn run_server(sock: SocketAddr, rec: Receiver<Msg>) { info!("Starting server-cli..."); // Set up an fps clock let mut clock = Clock::new(); // Create server - let mut server = Server::new().expect("Failed to create server instance"); + let mut server = Server::bind(sock).expect("Failed to create server instance"); loop { let events = server