diff --git a/.gitignore b/.gitignore index a6ada92..ef086d3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,18 +1,9 @@ -# ---> Rust -# Generated by Cargo -# will have compiled files and executables debug/ target/ -# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries -# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html +/*.json Cargo.lock -# These are backup files generated by rustfmt **/*.rs.bk - -# MSVC Windows builds of rustc generate these, which store debugging information *.pdb - -# Visual Studio crap .vs/ diff --git a/Cargo.toml b/Cargo.toml index 66b0ddc..f6c97d7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [workspace] -members = ["gameserver", "protocol", "qwer", "qwer/qwer-derive", "sdkserver"] +members = [ "common","gameserver", "protocol", "qwer", "qwer/qwer-derive", "sdkserver"] resolver = "2" [workspace.package] @@ -41,6 +41,7 @@ tracing-subscriber = { version = "0.3.18", features = [ ] } tracing-bunyan-formatter = "0.3.9" +common = { version = "0.1.0", path = "common" } protocol = { version = "0.1.0", path = "protocol" } qwer = { version = "0.1.0", path = "qwer", features = ["full"] } qwer-derive = { version = "0.1.0", path = "qwer/qwer-derive" } diff --git a/README.md b/README.md index c756911..6cc82ed 100644 --- a/README.md +++ b/README.md @@ -55,14 +55,9 @@ run the following in a terminal: ## Configuration -The game server can be configured using a `.env` file. The file contains one configurable -option, `SKIP_TUTORIAL`, which is set to 0 by default. - -If you have a `.env` file in the **same** directory as your executable, then this -is the file that will be loaded. Otherwise, upon _first_ running the `gameserver` -executable, it will create `{FOLDERID_RoamingAppData}\nap-gameserver\.env` -(`C:\Users\{user}\AppData\Roaming\nap-gameserver\.env`) for you, and will source -from this config instead. +The game server can be configured using a `gameserver.json` file. +Configuration file with default settings will be created in +the working directory upon first startup. ## Contributing diff --git a/common/Cargo.toml b/common/Cargo.toml new file mode 100644 index 0000000..53bc00c --- /dev/null +++ b/common/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "common" +edition = "2021" +version.workspace = true + +[dependencies] diff --git a/common/src/lib.rs b/common/src/lib.rs new file mode 100644 index 0000000..812d1ed --- /dev/null +++ b/common/src/lib.rs @@ -0,0 +1 @@ +pub mod util; diff --git a/common/src/util.rs b/common/src/util.rs new file mode 100644 index 0000000..685ebdd --- /dev/null +++ b/common/src/util.rs @@ -0,0 +1,9 @@ +pub fn load_or_create_config(path: &str, defaults: &str) -> String { + std::fs::read_to_string(path).map_or_else( + |_| { + std::fs::write(path, defaults).unwrap(); + defaults.to_string() + }, + |data| data, + ) +} diff --git a/gameserver/Cargo.toml b/gameserver/Cargo.toml index 1badcab..a31462e 100644 --- a/gameserver/Cargo.toml +++ b/gameserver/Cargo.toml @@ -8,7 +8,6 @@ ansi_term.workspace = true anyhow.workspace = true atomic_refcell.workspace = true dirs.workspace = true -dotenv.workspace = true env_logger.workspace = true hex.workspace = true lazy_static.workspace = true @@ -27,6 +26,7 @@ tracing-log.workspace = true tracing-subscriber.workspace = true tracing-bunyan-formatter.workspace = true +common.workspace = true protocol.workspace = true qwer.workspace = true diff --git a/gameserver/gameserver.default.json b/gameserver/gameserver.default.json new file mode 100644 index 0000000..9ffde53 --- /dev/null +++ b/gameserver/gameserver.default.json @@ -0,0 +1,5 @@ +{ + "gateway_endpoint": "0.0.0.0:10301", + "skip_tutorial": false, + "system_resources_logging": false +} diff --git a/gameserver/src/config.rs b/gameserver/src/config.rs new file mode 100644 index 0000000..b295df5 --- /dev/null +++ b/gameserver/src/config.rs @@ -0,0 +1,22 @@ +use common::util::load_or_create_config; +use lazy_static::lazy_static; +use serde::Deserialize; + +const DEFAULT_CONFIG: &str = include_str!("../gameserver.default.json"); + +#[derive(Deserialize)] +pub struct GameServerConfig { + pub gateway_endpoint: String, + pub skip_tutorial: bool, + pub system_resources_logging: bool, +} + +lazy_static! { + pub static ref CONFIGURATION: GameServerConfig = + serde_json::from_str(&load_or_create_config("gameserver.json", DEFAULT_CONFIG)) + .expect("Failed to parse server configuration file"); +} + +pub fn init_config() { + let _configuration = &*CONFIGURATION; // init static +} diff --git a/gameserver/src/config/mod.rs b/gameserver/src/data/mod.rs similarity index 100% rename from gameserver/src/config/mod.rs rename to gameserver/src/data/mod.rs diff --git a/gameserver/src/config/templates.rs b/gameserver/src/data/templates.rs similarity index 100% rename from gameserver/src/config/templates.rs rename to gameserver/src/data/templates.rs diff --git a/gameserver/src/game/globals.rs b/gameserver/src/game/globals.rs deleted file mode 100644 index 04c2dcd..0000000 --- a/gameserver/src/game/globals.rs +++ /dev/null @@ -1,11 +0,0 @@ -use std::env; - -use lazy_static::lazy_static; - -lazy_static! { - static ref SKIP_TUTORIAL: i32 = env::var("SKIP_TUTORIAL").map_or(0, |v| v.parse().unwrap()); -} - -pub fn should_skip_tutorial() -> bool { - *SKIP_TUTORIAL != 0 -} diff --git a/gameserver/src/game/mod.rs b/gameserver/src/game/mod.rs index 7fb8650..e67697e 100644 --- a/gameserver/src/game/mod.rs +++ b/gameserver/src/game/mod.rs @@ -1,6 +1,5 @@ mod context; pub mod data; -pub mod globals; pub mod manager; pub mod util; diff --git a/gameserver/src/logging.rs b/gameserver/src/logging.rs index 63cf167..6b89231 100644 --- a/gameserver/src/logging.rs +++ b/gameserver/src/logging.rs @@ -23,7 +23,10 @@ macro_rules! log_error { }; } -pub fn init_tracing() { +pub fn init_logging() { + #[cfg(target_os = "windows")] + ansi_term::enable_ansi_support().unwrap(); + env_logger::init_from_env(env_logger::Env::new().default_filter_or("info")); } diff --git a/gameserver/src/main.rs b/gameserver/src/main.rs index 6c569ef..7bfad02 100644 --- a/gameserver/src/main.rs +++ b/gameserver/src/main.rs @@ -1,56 +1,28 @@ -use std::path::Path; - use anyhow::Result; -use config::init_assets; use tracing::Level; mod config; +mod data; mod game; mod logging; mod net; -use logging::{init_system_logging, init_tracing}; - -const GATE_HOST: &str = "0.0.0.0"; -const GATE_PORT: u16 = 10301; +use config::{init_config, CONFIGURATION}; +use data::init_assets; +use logging::{init_logging, init_system_logging}; #[tokio::main] async fn main() -> Result<()> { - #[cfg(target_os = "windows")] - ansi_term::enable_ansi_support().unwrap(); - - init_config()?; + init_logging(); + init_config(); init_assets()?; - init_tracing(); let span = tracing::span!(Level::DEBUG, "main"); let _enter = span.enter(); - init_system_logging().await; - - net::gateway::listen(GATE_HOST, GATE_PORT).await?; - Ok(()) -} - -fn init_config() -> Result<()> { - let local_dotenv = Path::new(".env"); - if local_dotenv.exists() { - dotenv::dotenv()?; - } else { - let config = dirs::config_dir() - .ok_or_else(|| anyhow::anyhow!("No config directory found"))? - .join("nap-gameserver"); - - std::fs::create_dir_all(&config)?; - - let env = config.join(".env"); - - if !env.exists() { - std::fs::write(&env, "SKIP_TUTORIAL=0")?; - } - - dotenv::from_path(&env)?; + if CONFIGURATION.system_resources_logging { + init_system_logging().await; } - Ok(()) + net::gateway::listen(&CONFIGURATION.gateway_endpoint).await } diff --git a/gameserver/src/net/gateway.rs b/gameserver/src/net/gateway.rs index e003697..325b28e 100644 --- a/gameserver/src/net/gateway.rs +++ b/gameserver/src/net/gateway.rs @@ -6,9 +6,9 @@ use crate::log_error; use super::NetworkSession; -pub async fn listen(host: &str, port: u16) -> Result<()> { - let listener = TcpListener::bind(format!("{host}:{port}")).await?; - tracing::info!("Listening at {host}:{port}"); +pub async fn listen(bind_addr: &str) -> Result<()> { + let listener = TcpListener::bind(bind_addr).await?; + tracing::info!("Listening at {bind_addr}"); loop { let Ok((client_socket, client_addr)) = listener.accept().await else { diff --git a/gameserver/src/net/handlers/world.rs b/gameserver/src/net/handlers/world.rs index 8ee0305..1a45c3a 100644 --- a/gameserver/src/net/handlers/world.rs +++ b/gameserver/src/net/handlers/world.rs @@ -2,8 +2,9 @@ use qwer::{ pdkhashmap, phashmap, phashset, PropertyDoubleKeyHashMap, PropertyHashMap, PropertyHashSet, }; -use crate::config; -use crate::game::{globals, util}; +use crate::config::CONFIGURATION; +use crate::data; +use crate::game::util; use super::*; @@ -17,7 +18,7 @@ pub async fn on_rpc_run_event_graph_arg( let unit = scene_unit_mgr.get(arg.owner_uid); let SceneUnitProtocolInfo::NpcProtocolInfo { tag, id, .. } = unit; - let main_city_object = config::get_main_city_object(tag, id).unwrap(); + let main_city_object = data::get_main_city_object(tag, id).unwrap(); let mut ptc_sync_event_info = PtcSyncEventInfoArg { owner_type: EventGraphOwnerType::SceneUnit, @@ -89,7 +90,7 @@ pub async fn on_rpc_interact_with_unit_arg( let unit = scene_unit_mgr.get(arg.unit_uid); let SceneUnitProtocolInfo::NpcProtocolInfo { tag, id, .. } = unit; - let main_city_object = config::get_main_city_object(tag, id).unwrap(); + let main_city_object = data::get_main_city_object(tag, id).unwrap(); let mut ptc_sync_event_info = PtcSyncEventInfoArg { owner_type: EventGraphOwnerType::SceneUnit, @@ -152,7 +153,7 @@ fn create_player(id: u64) -> PlayerInfo { z: 11.18, }); - if globals::should_skip_tutorial() { + if CONFIGURATION.skip_tutorial { let beginner_procedure = player.beginner_procedure_info.as_mut().unwrap(); beginner_procedure.procedure_info.replace(6); player.nick_name.replace(String::from("xeondev")); @@ -207,7 +208,7 @@ pub async fn on_rpc_enter_world_arg( item_manager.add_resource(10, 228); item_manager.add_resource(100, 1337); - for avatar_id in config::iter_avatar_config_collection() + for avatar_id in data::iter_avatar_config_collection() .map(|c| c.id) .filter(|id| *id < 2000) { @@ -215,7 +216,7 @@ pub async fn on_rpc_enter_world_arg( } let unlock_manager = session.context.unlock_manager.borrow(); - for unlock_id in config::iter_unlock_config_collection().map(|c| c.id) { + for unlock_id in data::iter_unlock_config_collection().map(|c| c.id) { unlock_manager.unlock(unlock_id); } @@ -257,7 +258,7 @@ pub async fn on_rpc_enter_world_arg( let yorozuya_quest_manager = session.context.yorozuya_quest_manager.borrow(); yorozuya_quest_manager.add_hollow_quest(102, HollowQuestType::SideQuest, 10010002); - if globals::should_skip_tutorial() { + if CONFIGURATION.skip_tutorial { Box::pin(enter_main_city(session)).await?; } else { let fresh_scene_uid = *dungeon_manager.create_fresh().unwrap();