singleplayer server initialization status

This commit is contained in:
Maxicarlos08 2023-09-28 00:26:47 +02:00
parent 9a3f53c32c
commit 66f6b81575
No known key found for this signature in database
18 changed files with 185 additions and 21 deletions

View File

@ -2,6 +2,8 @@ common-username = username
common-singleplayer = Singleplayer
common-multiplayer = Multiplayer
common-servers = Servers
common-server = Server
common-client = Client
common-quit = Quit
common-settings = Settings
common-languages = Languages

View File

@ -61,10 +61,18 @@ hud-sit = Sit
hud-steer = Steer
hud-portal = Portal
hud-init-stage-singleplayer = Starting singleplayer server...
hud-init-stage-server-db-migrations = [{ common-server }]: Applying database migrations
hud-init-stage-server-db-vacuum = [{ common-server }]: Cleaning up database
hud-init-stage-server-worldsim-erosion = [{ common-server }]: Erosion { $percentage }%
hud-init-stage-server-worldciv-civcreate = [{ common-server }]: Generated { $generated } out of { $total } civilizations
hud-init-stage-server-worldciv-site = [{ common-server }]: Generating sites
hud-init-stage-server-economysim = [{ common-server }]: Simulating economy
hud-init-stage-server-spotgen = [{ common-server }]: Generating spots
hud-init-stage-server-starting = [{ common-server }]: Starting server...
hud-init-stage-multiplayer = Starting multiplayer
hud-init-stage-client-connection-establish = Establishing connection to server
hud-init-stage-client-request-server-version = Wating for server version
hud-init-stage-client-authentication = Authenticating
hud-init-stage-client-load-init-data = Loading initialization data from server
hud-init-stage-client-starting-client = Preparing Client...
hud-init-stage-client-connection-establish = [{ common-client }]: Establishing connection to server
hud-init-stage-client-request-server-version = [{ common-client }]: Wating for server version
hud-init-stage-client-authentication = [{ common-client }]: Authenticating
hud-init-stage-client-load-init-data = [{ common-client }]: Loading initialization data from server
hud-init-stage-client-starting-client = [{ common-client }]: Preparing Client...
hud-init-stage-render-pipeline = Creating render pipeline ({ $done }/{ $total })

View File

@ -192,6 +192,7 @@ fn main() -> io::Result<()> {
editable_settings,
database_settings,
&server_data_dir,
None,
runtime,
)
.expect("Failed to create server instance!");

View File

@ -112,11 +112,13 @@ use test_world::{IndexOwned, World};
use tokio::{runtime::Runtime, sync::Notify};
use tracing::{debug, error, info, trace, warn};
use vek::*;
pub use world::{civ::WorldCivStage, sim::WorldSimStage, WorldGenerateStage};
use crate::{
persistence::{DatabaseSettings, SqlLogMode},
sys::terrain,
};
use crossbeam_channel::Sender;
use hashbrown::HashMap;
use std::sync::RwLock;
@ -198,6 +200,13 @@ pub struct ChunkRequest {
key: Vec2<i32>,
}
pub enum ServerInitStage {
DbMigrations,
DbVacuum,
WorldGen(WorldGenerateStage),
StartingSystems,
}
pub struct Server {
state: State,
world: Arc<World>,
@ -221,6 +230,7 @@ impl Server {
editable_settings: EditableSettings,
database_settings: DatabaseSettings,
data_dir: &std::path::Path,
stage_report_tx: Option<Sender<ServerInitStage>>,
runtime: Arc<Runtime>,
) -> Result<Self, Error> {
prof_span!("Server::new");
@ -229,10 +239,18 @@ impl Server {
info!("Authentication is disabled");
}
let report_stage = |stage: ServerInitStage| {
if let Some(stage_report_tx) = &stage_report_tx {
let _ = stage_report_tx.send(stage);
}
};
report_stage(ServerInitStage::DbMigrations);
// Run pending DB migrations (if any)
debug!("Running DB migrations...");
persistence::run_migrations(&database_settings);
report_stage(ServerInitStage::DbVacuum);
// Vacuum database
debug!("Vacuuming database...");
persistence::vacuum_database(&database_settings);
@ -253,6 +271,7 @@ impl Server {
let pools = State::pools(GameMode::Server);
let world_generate_status_tx = stage_report_tx.clone();
#[cfg(feature = "worldgen")]
let (world, index) = World::generate(
settings.world_seed,
@ -267,6 +286,11 @@ impl Server {
calendar: Some(settings.calendar_mode.calendar_now()),
},
&pools,
Arc::new(move |stage| {
if let Some(stage_report_tx) = &world_generate_status_tx {
let _ = stage_report_tx.send(ServerInitStage::WorldGen(stage));
}
}),
);
#[cfg(not(feature = "worldgen"))]
let (world, index) = World::generate(settings.world_seed);
@ -287,6 +311,8 @@ impl Server {
let lod = lod::Lod::from_world(&world, index.as_index_ref(), &pools);
report_stage(ServerInitStage::StartingSystems);
let mut state = State::server(
pools,
world.sim().map_size_lg(),

View File

@ -25,6 +25,7 @@ pub fn criterion_benchmark(c: &mut Criterion) {
calendar: None,
},
&pool,
Arc::new(|_| {}),
);
let mut terrain = TerrainGrid::new(
world.sim().map_size_lg(),

View File

@ -21,6 +21,7 @@ use common::comp;
use common_base::span;
use i18n::LocalizationHandle;
use scene::Scene;
use server::ServerInitStage;
use std::sync::Arc;
use tokio::runtime;
use tracing::error;
@ -29,6 +30,7 @@ use ui::{Event as MainMenuEvent, MainMenuUi};
pub enum DetailedInitializationStage {
// TODO: Map generation and server startup progress
Singleplayer,
SingleplayerServer(ServerInitStage),
StartingMultiplayer,
Client(ClientInitStage),
CreatingRenderPipeline(usize, usize),
@ -106,6 +108,12 @@ impl PlayState for MainMenuState {
#[cfg(feature = "singleplayer")]
{
if let Some(singleplayer) = global_state.singleplayer.as_running() {
if let Ok(stage_update) = singleplayer.init_stage_receiver.try_recv() {
self.main_menu_ui.update_stage(
DetailedInitializationStage::SingleplayerServer(stage_update),
);
}
match singleplayer.receiver.try_recv() {
Ok(Ok(())) => {
// Attempt login after the server is finished initializing

View File

@ -16,6 +16,7 @@ use i18n::Localization;
use iced::{button, Align, Column, Container, Length, Row, Space, Text};
use keyboard_keynames::key_layout::KeyLayout;
use serde::{Deserialize, Serialize};
use server::{ServerInitStage, WorldCivStage, WorldGenerateStage, WorldSimStage};
struct LoadingAnimation {
speed_factor: f32,
@ -141,6 +142,43 @@ impl Screen {
DetailedInitializationStage::Singleplayer => {
i18n.get_msg("hud-init-stage-singleplayer")
},
DetailedInitializationStage::SingleplayerServer(server_stage) => {
match server_stage {
ServerInitStage::DbMigrations => {
i18n.get_msg("hud-init-stage-server-db-migrations")
},
ServerInitStage::DbVacuum => {
i18n.get_msg("hud-init-stage-server-db-vacuum")
},
ServerInitStage::WorldGen(worldgen_stage) => match worldgen_stage {
WorldGenerateStage::WorldSimGenerate(worldsim_stage) => {
match worldsim_stage {
WorldSimStage::Erosion(done) => i18n
.get_msg_ctx(
"hud-init-stage-server-worldsim-erosion",
&i18n::fluent_args! { "percentage" => format!("{done:.0}") }
),
}
},
WorldGenerateStage::WorldCivGenerate(worldciv_stage) => {
match worldciv_stage {
WorldCivStage::CivCreation(generated, total) => i18n
.get_msg_ctx(
"hud-init-stage-server-worldciv-civcreate",
&i18n::fluent_args! {
"generated" => generated.to_string(),
"total" => total.to_string(),
}
),
WorldCivStage::SiteGeneration => i18n.get_msg("hud-init-stage-server-worldciv-site"),
}
},
WorldGenerateStage::EconomySimulation => i18n.get_msg("hud-init-stage-server-economysim"),
WorldGenerateStage::SpotGeneration => i18n.get_msg("hud-init-stage-server-spotgen"),
},
ServerInitStage::StartingSystems => i18n.get_msg("hud-init-stage-server-starting"),
}
},
DetailedInitializationStage::StartingMultiplayer => {
i18n.get_msg("hud-init-stage-multiplayer")
},

View File

@ -2,7 +2,7 @@ use common::clock::Clock;
use crossbeam_channel::{bounded, unbounded, Receiver, Sender, TryRecvError};
use server::{
persistence::{DatabaseSettings, SqlLogMode},
Error as ServerError, Event, Input, Server,
Error as ServerError, Event, Input, Server, ServerInitStage,
};
use std::{
sync::{
@ -26,6 +26,7 @@ pub struct Singleplayer {
_server_thread: JoinHandle<()>,
stop_server_s: Sender<()>,
pub receiver: Receiver<Result<(), ServerError>>,
pub init_stage_receiver: Receiver<ServerInitStage>,
// Wether the server is stopped or not
paused: Arc<AtomicBool>,
}
@ -89,6 +90,8 @@ impl SingleplayerState {
let (stop_server_s, stop_server_r) = unbounded();
let (server_stage_tx, server_stage_rx) = unbounded();
// Create server
// Relative to data_dir
@ -119,6 +122,7 @@ impl SingleplayerState {
editable_settings,
database_settings,
&server_data_dir,
Some(server_stage_tx),
runtime,
) {
Ok(server) => (Some(server), Ok(())),
@ -143,6 +147,7 @@ impl SingleplayerState {
*self = SingleplayerState::Running(Singleplayer {
_server_thread: thread,
stop_server_s,
init_stage_receiver: server_stage_rx,
receiver: result_receiver,
paused,
});

View File

@ -2,6 +2,7 @@ use std::{
collections::HashMap,
fs::File,
io::{prelude::*, SeekFrom},
sync::Arc,
};
type Result = std::io::Result<()>;
@ -32,6 +33,7 @@ fn main() -> Result {
calendar: None,
},
&pool,
Arc::new(|_| {}),
);
println!("Loaded world");
let export_path = "dungeon.vox";

View File

@ -4,7 +4,7 @@ use common::{
};
use rayon::ThreadPoolBuilder;
use rusqlite::{Connection, ToSql};
use std::error::Error;
use std::{error::Error, sync::Arc};
use strum::IntoEnumIterator;
use vek::Vec2;
use veloren_world::{
@ -169,6 +169,7 @@ fn main() {
calendar: None,
},
&pool,
Arc::new(|_| {}),
);
println!("Loaded world");

View File

@ -1,4 +1,7 @@
use std::ops::{Add, Mul, Sub};
use std::{
ops::{Add, Mul, Sub},
sync::Arc,
};
use vek::*;
use veloren_world::{sim::WorldOpts, util::Sampler, World};
@ -14,6 +17,7 @@ fn main() {
..WorldOpts::default()
},
&threadpool,
Arc::new(|_| {}),
);
let index = index.as_index_ref();

View File

@ -7,7 +7,7 @@ use common::{
vol::RectVolSize,
};
use rayon::prelude::*;
use std::{f64, io::Write, path::PathBuf, time::SystemTime};
use std::{f64, io::Write, path::PathBuf, sync::Arc, time::SystemTime};
use tracing::{warn, Level};
use tracing_subscriber::{
filter::{EnvFilter, LevelFilter},
@ -53,6 +53,7 @@ fn main() {
calendar: None,
},
&threadpool,
Arc::new(|_| {}),
);
let index = index.as_index_ref();
tracing::info!("Sampling data...");

View File

@ -1,4 +1,4 @@
use std::time::Instant;
use std::{sync::Arc, time::Instant};
use veloren_world::{
sim::{FileOpts, WorldOpts, DEFAULT_WORLD_MAP},
World,
@ -17,6 +17,7 @@ fn main() {
calendar: None,
},
&threadpool,
Arc::new(|_| {}),
);
core::hint::black_box((world, index));
println!("{} ms", start.elapsed().as_nanos() / 1_000_000);

View File

@ -27,6 +27,7 @@ use core::{fmt, hash::BuildHasherDefault, ops::Range};
use fxhash::FxHasher64;
use rand::prelude::*;
use rand_chacha::ChaChaRng;
use std::sync::Arc;
use tracing::{debug, info, warn};
use vek::*;
@ -223,8 +224,20 @@ impl<'a, R: Rng> GenCtx<'a, R> {
}
}
pub enum WorldCivStage {
/// Civilization creation, how many out of how many civilizations have been
/// generated yet
CivCreation(u32, u32),
SiteGeneration,
}
impl Civs {
pub fn generate(seed: u32, sim: &mut WorldSim, index: &mut Index) -> Self {
pub fn generate(
seed: u32,
sim: &mut WorldSim,
index: &mut Index,
report_stage: Arc<dyn Fn(WorldCivStage)>,
) -> Self {
prof_span!("Civs::generate");
let mut this = Self::default();
let rng = ChaChaRng::from_seed(seed_expan::rng_state(seed));
@ -247,16 +260,18 @@ impl Civs {
info!("starting civilisation creation");
prof_span!(guard, "create civs");
for _ in 0..initial_civ_count {
for i in 0..initial_civ_count {
prof_span!("create civ");
debug!("Creating civilisation...");
if this.birth_civ(&mut ctx.reseed()).is_none() {
warn!("Failed to find starting site for civilisation.");
}
report_stage(WorldCivStage::CivCreation(i, initial_civ_count));
}
drop(guard);
info!(?initial_civ_count, "all civilisations created");
report_stage(WorldCivStage::SiteGeneration);
prof_span!(guard, "find locations and establish sites");
let world_dims = ctx.sim.get_aabr();
for _ in 0..initial_civ_count * 3 {

View File

@ -32,9 +32,11 @@ pub use crate::{
layer::PathLocals,
};
pub use block::BlockGen;
use civ::WorldCivStage;
pub use column::ColumnSample;
pub use common::terrain::site::{DungeonKindMeta, SettlementKindMeta};
pub use index::{IndexOwned, IndexRef};
use sim::WorldSimStage;
use crate::{
column::ColumnGen,
@ -62,7 +64,7 @@ use enum_map::EnumMap;
use rand::{prelude::*, Rng};
use rand_chacha::ChaCha8Rng;
use serde::Deserialize;
use std::time::Duration;
use std::{sync::Arc, time::Duration};
use vek::*;
#[cfg(all(feature = "be-dyn-lib", feature = "use-dyn-lib"))]
@ -85,6 +87,13 @@ pub enum Error {
Other(String),
}
pub enum WorldGenerateStage {
WorldSimGenerate(WorldSimStage),
WorldCivGenerate(WorldCivStage),
EconomySimulation,
SpotGeneration,
}
pub struct World {
sim: sim::WorldSim,
civs: civ::Civs,
@ -110,6 +119,7 @@ impl World {
seed: u32,
opts: sim::WorldOpts,
threadpool: &rayon::ThreadPool,
report_stage: Arc<dyn Fn(WorldGenerateStage) + Send + Sync>,
) -> (Self, IndexOwned) {
prof_span!("World::generate");
// NOTE: Generating index first in order to quickly fail if the color manifest
@ -117,12 +127,26 @@ impl World {
threadpool.install(|| {
let mut index = Index::new(seed);
let mut sim = sim::WorldSim::generate(seed, opts, threadpool);
let sim_stage_tx = Arc::clone(&report_stage);
let mut sim = sim::WorldSim::generate(
seed,
opts,
threadpool,
Arc::new(move |stage| sim_stage_tx(WorldGenerateStage::WorldSimGenerate(stage))),
);
let civs = civ::Civs::generate(seed, &mut sim, &mut index);
let civ_stage_tx = Arc::clone(&report_stage);
let civs = civ::Civs::generate(
seed,
&mut sim,
&mut index,
Arc::new(move |stage| civ_stage_tx(WorldGenerateStage::WorldCivGenerate(stage))),
);
report_stage(WorldGenerateStage::EconomySimulation);
sim2::simulate(&mut index, &mut sim);
report_stage(WorldGenerateStage::SpotGeneration);
Spot::generate(&mut sim);
(Self { sim, civs }, IndexOwned::new(index))

View File

@ -19,6 +19,7 @@ use std::{
cmp::{Ordering, Reverse},
collections::BinaryHeap,
f32, fmt, mem,
rc::Rc,
time::Instant,
u32,
};
@ -2540,6 +2541,7 @@ pub fn do_erosion(
k_d_scale: f64,
k_da_scale: impl Fn(f64) -> f64,
threadpool: &rayon::ThreadPool,
report_progress: Rc<dyn Fn(f64)>,
) -> (Box<[Alt]>, Box<[Alt]> /* , Box<[Alt]> */) {
debug!("Initializing erosion arrays...");
let oldh_ = (0..map_size_lg.chunks_len())
@ -2644,6 +2646,7 @@ pub fn do_erosion(
// Print out the percentage complete. Do this at most 20 times.
if i % std::cmp::max(n_steps / 20, 1) == 0 {
let pct = (i as f64 / n_steps as f64) * 100.0;
report_progress(pct);
info!("{:.2}% complete", pct);
}

View File

@ -69,6 +69,7 @@ use std::{
io::{BufReader, BufWriter},
ops::{Add, Div, Mul, Neg, Sub},
path::PathBuf,
rc::Rc,
sync::Arc,
};
use strum::IntoEnumIterator;
@ -645,6 +646,11 @@ impl WorldFile {
}
}
pub enum WorldSimStage {
// TODO: Add more stages
Erosion(f64),
}
pub struct WorldSim {
pub seed: u32,
/// Base 2 logarithm of the map size.
@ -663,7 +669,12 @@ pub struct WorldSim {
}
impl WorldSim {
pub fn generate(seed: u32, opts: WorldOpts, threadpool: &rayon::ThreadPool) -> Self {
pub fn generate(
seed: u32,
opts: WorldOpts,
threadpool: &rayon::ThreadPool,
stage_report: Arc<dyn Fn(WorldSimStage)>,
) -> Self {
prof_span!("WorldSim::generate");
let calendar = opts.calendar; // separate lifetime of elements
let world_file = opts.world_file;
@ -1250,6 +1261,9 @@ impl WorldSim {
// Perform some erosion.
let report_erosion: Rc<dyn Fn(f64)> =
Rc::new(move |progress: f64| stage_report(WorldSimStage::Erosion(progress)));
let (alt, basement) = if let Some(map) = parsed_world_file {
(map.alt, map.basement)
} else {
@ -1278,6 +1292,7 @@ impl WorldSim {
k_d_scale(n_approx),
k_da_scale,
threadpool,
Rc::clone(&report_erosion),
);
// Quick "small scale" erosion cycle in order to lower extreme angles.
@ -1302,6 +1317,7 @@ impl WorldSim {
k_d_scale(n_approx),
k_da_scale,
threadpool,
Rc::clone(&report_erosion),
)
};
@ -1351,6 +1367,7 @@ impl WorldSim {
k_d_scale(n_approx),
k_da_scale,
threadpool,
Rc::clone(&report_erosion),
)
};

View File

@ -328,6 +328,8 @@ mod tests {
#[test]
#[ignore]
fn test_economy0() {
use std::sync::Arc;
execute_with_tracing(Level::INFO, || {
let threadpool = rayon::ThreadPoolBuilder::new().build().unwrap();
info!("init");
@ -340,9 +342,9 @@ mod tests {
};
let mut index = crate::index::Index::new(seed);
info!("Index created");
let mut sim = sim::WorldSim::generate(seed, opts, &threadpool);
let mut sim = sim::WorldSim::generate(seed, opts, &threadpool, Arc::new(|_| {}));
info!("World loaded");
let _civs = crate::civ::Civs::generate(seed, &mut sim, &mut index);
let _civs = crate::civ::Civs::generate(seed, &mut sim, &mut index, Arc::new(|_| {}));
info!("Civs created");
crate::sim2::simulate(&mut index, &mut sim);
show_economy(&index.sites, &None);
@ -354,6 +356,8 @@ mod tests {
#[test]
#[ignore]
fn test_economy1() {
use std::sync::Arc;
execute_with_tracing(Level::INFO, || {
let threadpool = rayon::ThreadPoolBuilder::new().build().unwrap();
info!("init");
@ -366,12 +370,13 @@ mod tests {
};
let mut index = crate::index::Index::new(seed);
info!("Index created");
let mut sim = sim::WorldSim::generate(seed, opts, &threadpool);
let mut sim = sim::WorldSim::generate(seed, opts, &threadpool, Arc::new(|_| {}));
info!("World loaded");
let mut names = None;
let regenerate_input = false;
if regenerate_input {
let _civs = crate::civ::Civs::generate(seed, &mut sim, &mut index);
let _civs =
crate::civ::Civs::generate(seed, &mut sim, &mut index, Arc::new(|_| {}));
info!("Civs created");
let mut outarr: Vec<EconomySetup> = Vec::new();
for i in index.sites.values() {
@ -470,6 +475,8 @@ mod tests {
#[test]
/// test whether a site in moderate climate can survive on its own
fn test_economy_moderate_standalone() {
use std::sync::Arc;
fn add_settlement(
env: &mut Simenv,
name: &str,
@ -505,7 +512,7 @@ mod tests {
};
let index = crate::index::Index::new(seed);
info!("Index created");
let mut sim = sim::WorldSim::generate(seed, opts, &threadpool);
let mut sim = sim::WorldSim::generate(seed, opts, &threadpool, Arc::new(|_| {}));
info!("World loaded");
let rng = ChaChaRng::from_seed(seed_expan::rng_state(seed));
let mut env = Simenv {