Server configuration rework

This commit is contained in:
xeon 2024-05-24 19:45:45 +03:00
parent 934ad89fdf
commit eb641d67fb
17 changed files with 75 additions and 81 deletions

11
.gitignore vendored
View file

@ -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/

View file

@ -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" }

View file

@ -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

6
common/Cargo.toml Normal file
View file

@ -0,0 +1,6 @@
[package]
name = "common"
edition = "2021"
version.workspace = true
[dependencies]

1
common/src/lib.rs Normal file
View file

@ -0,0 +1 @@
pub mod util;

9
common/src/util.rs Normal file
View file

@ -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,
)
}

View file

@ -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

View file

@ -0,0 +1,5 @@
{
"gateway_endpoint": "0.0.0.0:10301",
"skip_tutorial": false,
"system_resources_logging": false
}

22
gameserver/src/config.rs Normal file
View file

@ -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
}

View file

@ -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
}

View file

@ -1,6 +1,5 @@
mod context;
pub mod data;
pub mod globals;
pub mod manager;
pub mod util;

View file

@ -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"));
}

View file

@ -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();
if CONFIGURATION.system_resources_logging {
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)?;
}
Ok(())
net::gateway::listen(&CONFIGURATION.gateway_endpoint).await
}

View file

@ -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 {

View file

@ -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();