From 7ec6e093325a501330049a1f6fc01911acd54978 Mon Sep 17 00:00:00 2001 From: Louis Pearson Date: Wed, 17 Apr 2019 07:41:34 -0600 Subject: [PATCH 1/3] Add basic singleplayer support Former-commit-id: 929b05faca5d2b95a96349dfc17a62b3165289a1 --- Cargo.lock | 1 + voxygen/.gitignore | 1 + voxygen/Cargo.toml | 1 + voxygen/src/menu/main/mod.rs | 7 ++++++ voxygen/src/menu/main/singleplayer.rs | 36 +++++++++++++++++++++++++++ voxygen/src/menu/main/ui.rs | 15 ++++++++++- 6 files changed, 60 insertions(+), 1 deletion(-) create mode 100644 voxygen/src/menu/main/singleplayer.rs diff --git a/Cargo.lock b/Cargo.lock index 6777d2e7e4..1a1ab9e19a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1960,6 +1960,7 @@ dependencies = [ "vek 0.9.7 (registry+https://github.com/rust-lang/crates.io-index)", "veloren-client 0.2.0", "veloren-common 0.2.0", + "veloren-server 0.2.0", "winit 0.18.1 (registry+https://github.com/rust-lang/crates.io-index)", ] diff --git a/voxygen/.gitignore b/voxygen/.gitignore index 693699042b..4fb75fb34b 100644 --- a/voxygen/.gitignore +++ b/voxygen/.gitignore @@ -1,3 +1,4 @@ /target **/*.rs.bk Cargo.lock +settings.toml diff --git a/voxygen/Cargo.toml b/voxygen/Cargo.toml index 0db23602aa..4b9c11fa2b 100644 --- a/voxygen/Cargo.toml +++ b/voxygen/Cargo.toml @@ -12,6 +12,7 @@ default = ["gl"] [dependencies] common = { package = "veloren-common", path = "../common" } client = { package = "veloren-client", path = "../client" } +server = { package = "veloren-server", path = "../server" } # Graphics gfx = "0.17" diff --git a/voxygen/src/menu/main/mod.rs b/voxygen/src/menu/main/mod.rs index 7461b53066..a427890201 100644 --- a/voxygen/src/menu/main/mod.rs +++ b/voxygen/src/menu/main/mod.rs @@ -1,5 +1,6 @@ mod client_init; mod ui; +mod singleplayer; use super::char_selection::CharSelectionState; use crate::{ @@ -9,6 +10,7 @@ use crate::{ use client_init::{ClientInit, Error as InitError}; use common::{clock::Clock, comp}; use std::time::Duration; +use std::thread; use ui::{Event as MainMenuEvent, MainMenuUi}; use vek::*; @@ -100,6 +102,11 @@ impl PlayState for MainMenuState { ), ))); } + MainMenuEvent::StartSingleplayer => { + thread::spawn(move || { + singleplayer::run_server(); + }); + } MainMenuEvent::Quit => return PlayStateResult::Shutdown, } } diff --git a/voxygen/src/menu/main/singleplayer.rs b/voxygen/src/menu/main/singleplayer.rs new file mode 100644 index 0000000000..93f07030ec --- /dev/null +++ b/voxygen/src/menu/main/singleplayer.rs @@ -0,0 +1,36 @@ +use std::time::Duration; +use log::info; +use server::{Input, Event, Server}; +use common::clock::Clock; + +const TPS: u64 = 30; + +pub fn run_server() { + 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"); + + loop { + let events = server.tick(Input::default(), clock.get_last_delta()) + .expect("Failed to tick server"); + + for event in events { + match event { + Event::ClientConnected { entity } => info!("Client connected!"), + Event::ClientDisconnected { entity } => info!("Client disconnected!"), + Event::Chat { entity, msg } => info!("[Client] {}", msg), + } + } + + // Clean up the server after a tick + server.cleanup(); + + // Wait for the next tick + clock.tick(Duration::from_millis(1000 / TPS)); + } +} diff --git a/voxygen/src/menu/main/ui.rs b/voxygen/src/menu/main/ui.rs index c5e7f83504..da2551e0c5 100644 --- a/voxygen/src/menu/main/ui.rs +++ b/voxygen/src/menu/main/ui.rs @@ -91,6 +91,7 @@ pub enum Event { username: String, server_address: String, }, + StartSingleplayer, Quit, } @@ -172,6 +173,18 @@ impl MainMenuUi { }); }; } + + macro_rules! singleplayer { + () => { + self.login_error = None; + events.push(Event::StartSingleplayer); + events.push(Event::LoginAttempt { + username: "singleplayer".to_string(), + server_address: "localhost".to_string(), + }); + }; + } + const TEXT_COLOR: Color = Color::Rgba(1.0, 1.0, 1.0, 1.0); // Username // TODO: get a lower resolution and cleaner input_bg.png @@ -296,7 +309,7 @@ impl MainMenuUi { .set(self.ids.singleplayer_button, ui_widgets) .was_clicked() { - login!(); + singleplayer!(); } // Quit if Button::image(self.imgs.button) From 76adbec3bbce74fbf9e40ffd69fff4250dcf853e Mon Sep 17 00:00:00 2001 From: Louis Pearson Date: Wed, 17 Apr 2019 08:57:23 -0600 Subject: [PATCH 2/3] Add method to drop singleplayer thread Former-commit-id: b62a90728d4e83a005687538e134cee427491052 --- voxygen/src/main.rs | 7 +++- voxygen/src/menu/main/mod.rs | 6 +-- voxygen/src/{menu/main => }/singleplayer.rs | 45 ++++++++++++++++++++- 3 files changed, 52 insertions(+), 6 deletions(-) rename voxygen/src/{menu/main => }/singleplayer.rs (53%) diff --git a/voxygen/src/main.rs b/voxygen/src/main.rs index b31bef5e61..ccb2ea0804 100644 --- a/voxygen/src/main.rs +++ b/voxygen/src/main.rs @@ -13,12 +13,14 @@ pub mod session; pub mod ui; pub mod window; pub mod settings; +pub mod singleplayer; // Reexports pub use crate::error::Error; // Standard use std::mem; +use std::thread; // Library use log; @@ -28,13 +30,15 @@ use pretty_env_logger; use crate::{ menu::main::MainMenuState, window::Window, - settings::Settings + settings::Settings, + singleplayer::Singleplayer, }; /// A type used to store state that is shared between all play states pub struct GlobalState { settings: Settings, window: Window, + singleplayer: Option, } impl GlobalState { @@ -88,6 +92,7 @@ fn main() { let mut global_state = GlobalState { settings, window, + singleplayer: None, }; // Set up the initial play state diff --git a/voxygen/src/menu/main/mod.rs b/voxygen/src/menu/main/mod.rs index a427890201..05412179d2 100644 --- a/voxygen/src/menu/main/mod.rs +++ b/voxygen/src/menu/main/mod.rs @@ -1,11 +1,11 @@ mod client_init; mod ui; -mod singleplayer; use super::char_selection::CharSelectionState; use crate::{ window::{Event, Window}, GlobalState, PlayState, PlayStateResult, + singleplayer::Singleplayer, }; use client_init::{ClientInit, Error as InitError}; use common::{clock::Clock, comp}; @@ -103,9 +103,7 @@ impl PlayState for MainMenuState { ))); } MainMenuEvent::StartSingleplayer => { - thread::spawn(move || { - singleplayer::run_server(); - }); + global_state.singleplayer = Some(Singleplayer::new()); } MainMenuEvent::Quit => return PlayStateResult::Shutdown, } diff --git a/voxygen/src/menu/main/singleplayer.rs b/voxygen/src/singleplayer.rs similarity index 53% rename from voxygen/src/menu/main/singleplayer.rs rename to voxygen/src/singleplayer.rs index 93f07030ec..11cfa45da2 100644 --- a/voxygen/src/menu/main/singleplayer.rs +++ b/voxygen/src/singleplayer.rs @@ -2,10 +2,45 @@ use std::time::Duration; use log::info; use server::{Input, Event, Server}; use common::clock::Clock; +use std::{ + thread, + thread::JoinHandle +}; +use std::sync::mpsc::{ + channel, Receiver, Sender +}; const TPS: u64 = 30; -pub fn run_server() { +enum Msg { + Stop, +} + +pub struct Singleplayer { + server_thread: JoinHandle<()>, + sender: Sender, +} + +impl Singleplayer { + pub fn new() -> Self { + let (sender, reciever) = channel(); + let thread = thread::spawn(move || { + run_server(reciever); + }); + Singleplayer { + server_thread: thread, + sender, + } + } +} + +impl Drop for Singleplayer { + fn drop(&mut self) { + self.sender.send(Msg::Stop); + } +} + +fn run_server(rec: Receiver) { info!("Starting server-cli..."); // Set up an fps clock @@ -30,6 +65,14 @@ pub fn run_server() { // Clean up the server after a tick server.cleanup(); + match rec.try_recv() { + Ok(msg) => break, + Err(err) => match err { + Empty => (), + Disconnected => break, + }, + } + // Wait for the next tick clock.tick(Duration::from_millis(1000 / TPS)); } From a768971c6c92f3b024e9614369e92f04d3eaf807 Mon Sep 17 00:00:00 2001 From: Louis Pearson Date: Wed, 17 Apr 2019 09:22:26 -0600 Subject: [PATCH 3/3] Stop singleplayer thread whenever a session is left Former-commit-id: 16fcf35cbc856bebc12944781c2ac1a4daab5550 --- voxygen/src/menu/char_selection/mod.rs | 10 ++++++++-- voxygen/src/session.rs | 10 ++++++++-- voxygen/src/singleplayer.rs | 8 +++++--- 3 files changed, 21 insertions(+), 7 deletions(-) diff --git a/voxygen/src/menu/char_selection/mod.rs b/voxygen/src/menu/char_selection/mod.rs index 3ba0130622..04504bb6c6 100644 --- a/voxygen/src/menu/char_selection/mod.rs +++ b/voxygen/src/menu/char_selection/mod.rs @@ -45,7 +45,10 @@ impl PlayState for CharSelectionState { // Handle window events for event in global_state.window.fetch_events() { match event { - Event::Close => return PlayStateResult::Shutdown, + Event::Close => { + global_state.singleplayer = None; + return PlayStateResult::Shutdown; + }, // Pass events to ui Event::Ui(event) => { self.char_selection_ui.handle_event(event); @@ -60,7 +63,10 @@ impl PlayState for CharSelectionState { // Maintain the UI for event in self.char_selection_ui.maintain(global_state.window.renderer_mut()) { match event { - ui::Event::Logout => return PlayStateResult::Pop, + ui::Event::Logout => { + global_state.singleplayer = None; + return PlayStateResult::Pop; + }, ui::Event::Play => return PlayStateResult::Push( Box::new(SessionState::new(&mut global_state.window, self.client.clone())) ), diff --git a/voxygen/src/session.rs b/voxygen/src/session.rs index fc9ec54126..46003c7278 100644 --- a/voxygen/src/session.rs +++ b/voxygen/src/session.rs @@ -125,7 +125,10 @@ impl PlayState for SessionState { continue; } let _handled = match event { - Event::Close => return PlayStateResult::Shutdown, + Event::Close => { + global_state.singleplayer = None; + return PlayStateResult::Shutdown; + }, // Toggle cursor grabbing Event::KeyDown(Key::ToggleCursor) => { global_state @@ -166,7 +169,10 @@ impl PlayState for SessionState { self.client.borrow_mut().send_chat(msg); }, HudEvent::Logout => return PlayStateResult::Pop, - HudEvent::Quit => return PlayStateResult::Shutdown, + HudEvent::Quit => { + global_state.singleplayer = None; + return PlayStateResult::Shutdown; + }, } } diff --git a/voxygen/src/singleplayer.rs b/voxygen/src/singleplayer.rs index 11cfa45da2..735834e0ee 100644 --- a/voxygen/src/singleplayer.rs +++ b/voxygen/src/singleplayer.rs @@ -7,7 +7,7 @@ use std::{ thread::JoinHandle }; use std::sync::mpsc::{ - channel, Receiver, Sender + channel, Receiver, Sender, TryRecvError, }; const TPS: u64 = 30; @@ -16,6 +16,8 @@ enum Msg { Stop, } +/// Used to start and stop the background thread running the server +/// when in singleplayer mode. pub struct Singleplayer { server_thread: JoinHandle<()>, sender: Sender, @@ -68,8 +70,8 @@ fn run_server(rec: Receiver) { match rec.try_recv() { Ok(msg) => break, Err(err) => match err { - Empty => (), - Disconnected => break, + TryRecvError::Empty => (), + TryRecvError::Disconnected => break, }, }