mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Merge branch 'crabman/loading-status' into 'master'
Add status updates to the loading screen See merge request veloren/veloren!4119
This commit is contained in:
commit
63b001959e
@ -25,6 +25,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Portals that spawn in place of the last staircase at old style dungeons to prevent stair cheesing
|
||||
- Mutliple singleplayer worlds and map generation UI.
|
||||
- New arena building in desert cities, suitable for PVP, also NPCs like to watch the fights too
|
||||
- The loading screen now displays status updates for singleplayer server and client initialization progress
|
||||
|
||||
### Changed
|
||||
|
||||
|
@ -60,3 +60,22 @@ hud-stay= Stay
|
||||
hud-sit = Sit
|
||||
hud-steer = Steer
|
||||
hud-portal = Portal
|
||||
|
||||
-server = Server
|
||||
-client = Client
|
||||
hud-init-stage-singleplayer = Starting singleplayer server...
|
||||
hud-init-stage-server-db-migrations = [{ -server }]: Applying database migrations...
|
||||
hud-init-stage-server-db-vacuum = [{ -server }]: Cleaning up database...
|
||||
hud-init-stage-server-worldsim-erosion = [{ -server }]: Erosion { $percentage }%
|
||||
hud-init-stage-server-worldciv-civcreate = [{ -server }]: Generated { $generated } out of { $total } civilizations
|
||||
hud-init-stage-server-worldciv-site = [{ -server }]: Generating sites...
|
||||
hud-init-stage-server-economysim = [{ -server }]: Simulating economy...
|
||||
hud-init-stage-server-spotgen = [{ -server }]: Generating spots...
|
||||
hud-init-stage-server-starting = [{ -server }]: Starting server...
|
||||
hud-init-stage-multiplayer = Starting multiplayer
|
||||
hud-init-stage-client-connection-establish = [{ -client }]: Establishing connection to server...
|
||||
hud-init-stage-client-request-server-version = [{ -client }]: Waiting for server version...
|
||||
hud-init-stage-client-authentication = [{ -client }]: Authenticating...
|
||||
hud-init-stage-client-load-init-data = [{ -client }]: Loading initialization data from server...
|
||||
hud-init-stage-client-starting-client = [{ -client }]: Preparing Client...
|
||||
hud-init-stage-render-pipeline = Creating render pipeline ({ $done }/{ $total })
|
||||
|
@ -64,6 +64,7 @@ fn main() {
|
||||
&username,
|
||||
&password,
|
||||
|provider| provider == "https://auth.veloren.net",
|
||||
&|_| {},
|
||||
))
|
||||
.expect("Failed to create client instance");
|
||||
|
||||
|
@ -72,6 +72,7 @@ pub fn make_client(
|
||||
username,
|
||||
password,
|
||||
|_| true,
|
||||
&|_| {},
|
||||
))
|
||||
.ok()
|
||||
}
|
||||
|
@ -115,6 +115,22 @@ pub enum Event {
|
||||
SpectatePosition(Vec3<f32>),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ClientInitStage {
|
||||
/// A connection to the server is being created
|
||||
ConnectionEstablish,
|
||||
/// Waiting for server version
|
||||
WatingForServerVersion,
|
||||
/// We're currently authenticating with the server
|
||||
Authentication,
|
||||
/// Loading map data, site information, recipe information and other
|
||||
/// initialization data
|
||||
LoadingInitData,
|
||||
/// Prepare data received by the server to be used by the client (insert
|
||||
/// data into the ECS, render map)
|
||||
StartingClient,
|
||||
}
|
||||
|
||||
pub struct WorldData {
|
||||
/// Just the "base" layer for LOD; currently includes colors and nothing
|
||||
/// else. In the future we'll add more layers, like shadows, rivers, and
|
||||
@ -290,9 +306,11 @@ impl Client {
|
||||
username: &str,
|
||||
password: &str,
|
||||
auth_trusted: impl FnMut(&str) -> bool,
|
||||
init_stage_update: &(dyn Fn(ClientInitStage) + Send + Sync),
|
||||
) -> Result<Self, Error> {
|
||||
let network = Network::new(Pid::new(), &runtime);
|
||||
|
||||
init_stage_update(ClientInitStage::ConnectionEstablish);
|
||||
let mut participant = match addr {
|
||||
ConnectionArgs::Tcp {
|
||||
hostname,
|
||||
@ -322,6 +340,7 @@ impl Client {
|
||||
let in_game_stream = participant.opened().await?;
|
||||
let terrain_stream = participant.opened().await?;
|
||||
|
||||
init_stage_update(ClientInitStage::WatingForServerVersion);
|
||||
register_stream.send(ClientType::Game)?;
|
||||
let server_info: ServerInfo = register_stream.recv().await?;
|
||||
if server_info.git_hash != *common::util::GIT_HASH {
|
||||
@ -340,6 +359,7 @@ impl Client {
|
||||
|
||||
ping_stream.send(PingMsg::Ping)?;
|
||||
|
||||
init_stage_update(ClientInitStage::Authentication);
|
||||
// Register client
|
||||
Self::register(
|
||||
username,
|
||||
@ -350,6 +370,7 @@ impl Client {
|
||||
)
|
||||
.await?;
|
||||
|
||||
init_stage_update(ClientInitStage::LoadingInitData);
|
||||
// Wait for initial sync
|
||||
let mut ping_interval = tokio::time::interval(Duration::from_secs(1));
|
||||
let ServerInit::GameSync {
|
||||
@ -373,6 +394,7 @@ impl Client {
|
||||
}
|
||||
};
|
||||
|
||||
init_stage_update(ClientInitStage::StartingClient);
|
||||
// Spawn in a blocking thread (leaving the network thread free). This is mostly
|
||||
// useful for bots.
|
||||
let mut task = tokio::task::spawn_blocking(move || {
|
||||
@ -2978,6 +3000,7 @@ mod tests {
|
||||
username,
|
||||
password,
|
||||
|suggestion: &str| suggestion == auth_server,
|
||||
&|_| {},
|
||||
));
|
||||
let localisation = LocalizationHandle::load_expect("en");
|
||||
|
||||
|
@ -192,6 +192,7 @@ fn main() -> io::Result<()> {
|
||||
editable_settings,
|
||||
database_settings,
|
||||
&server_data_dir,
|
||||
&|_| {},
|
||||
runtime,
|
||||
)
|
||||
.expect("Failed to create server instance!");
|
||||
|
@ -112,6 +112,7 @@ 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},
|
||||
@ -198,6 +199,14 @@ pub struct ChunkRequest {
|
||||
key: Vec2<i32>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
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,
|
||||
report_stage: &(dyn Fn(ServerInitStage) + Send + Sync),
|
||||
runtime: Arc<Runtime>,
|
||||
) -> Result<Self, Error> {
|
||||
prof_span!("Server::new");
|
||||
@ -229,10 +239,12 @@ impl Server {
|
||||
info!("Authentication is disabled");
|
||||
}
|
||||
|
||||
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);
|
||||
@ -267,6 +279,9 @@ impl Server {
|
||||
calendar: Some(settings.calendar_mode.calendar_now()),
|
||||
},
|
||||
&pools,
|
||||
&|stage| {
|
||||
report_stage(ServerInitStage::WorldGen(stage));
|
||||
},
|
||||
);
|
||||
#[cfg(not(feature = "worldgen"))]
|
||||
let (world, index) = World::generate(settings.world_seed);
|
||||
@ -287,6 +302,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(),
|
||||
|
@ -25,6 +25,7 @@ pub fn criterion_benchmark(c: &mut Criterion) {
|
||||
calendar: None,
|
||||
},
|
||||
&pool,
|
||||
&|_| {},
|
||||
);
|
||||
let mut terrain = TerrainGrid::new(
|
||||
world.sim().map_size_lg(),
|
||||
|
@ -1,7 +1,7 @@
|
||||
use client::{
|
||||
addr::ConnectionArgs,
|
||||
error::{Error as ClientError, NetworkConnectError, NetworkError},
|
||||
Client, ServerInfo,
|
||||
Client, ClientInitStage, ServerInfo,
|
||||
};
|
||||
use crossbeam_channel::{unbounded, Receiver, Sender, TryRecvError};
|
||||
use std::{
|
||||
@ -39,6 +39,7 @@ pub struct AuthTrust(String, bool);
|
||||
// server).
|
||||
pub struct ClientInit {
|
||||
rx: Receiver<Msg>,
|
||||
stage_rx: Receiver<ClientInitStage>,
|
||||
trust_tx: Sender<AuthTrust>,
|
||||
cancel: Arc<AtomicBool>,
|
||||
}
|
||||
@ -51,6 +52,7 @@ impl ClientInit {
|
||||
) -> Self {
|
||||
let (tx, rx) = unbounded();
|
||||
let (trust_tx, trust_rx) = unbounded();
|
||||
let (init_stage_tx, init_stage_rx) = unbounded();
|
||||
let cancel = Arc::new(AtomicBool::new(false));
|
||||
let cancel2 = Arc::clone(&cancel);
|
||||
|
||||
@ -80,6 +82,9 @@ impl ClientInit {
|
||||
&username,
|
||||
&password,
|
||||
trust_fn,
|
||||
&|stage| {
|
||||
let _ = init_stage_tx.send(stage);
|
||||
},
|
||||
)
|
||||
.await
|
||||
{
|
||||
@ -116,6 +121,7 @@ impl ClientInit {
|
||||
|
||||
ClientInit {
|
||||
rx,
|
||||
stage_rx: init_stage_rx,
|
||||
trust_tx,
|
||||
cancel,
|
||||
}
|
||||
@ -132,6 +138,9 @@ impl ClientInit {
|
||||
}
|
||||
}
|
||||
|
||||
/// Poll for connection stage updates from the client
|
||||
pub fn stage_update(&self) -> Option<ClientInitStage> { self.stage_rx.try_recv().ok() }
|
||||
|
||||
/// Report trust status of auth server
|
||||
pub fn auth_trust(&self, auth_server: String, trusted: bool) {
|
||||
let _ = self.trust_tx.send(AuthTrust(auth_server, trusted));
|
||||
|
@ -14,20 +14,28 @@ use crate::{
|
||||
use client::{
|
||||
addr::ConnectionArgs,
|
||||
error::{InitProtocolError, NetworkConnectError, NetworkError},
|
||||
Client, ServerInfo,
|
||||
Client, ClientInitStage, ServerInfo,
|
||||
};
|
||||
use client_init::{ClientInit, Error as InitError, Msg as InitMsg};
|
||||
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;
|
||||
use ui::{Event as MainMenuEvent, MainMenuUi};
|
||||
|
||||
// TODO: show status messages for waiting on server creation, client init, and
|
||||
// pipeline creation (we can show progress of pipeline creation)
|
||||
#[derive(Debug)]
|
||||
pub enum DetailedInitializationStage {
|
||||
Singleplayer,
|
||||
SingleplayerServer(ServerInitStage),
|
||||
StartingMultiplayer,
|
||||
Client(ClientInitStage),
|
||||
CreatingRenderPipeline(usize, usize),
|
||||
}
|
||||
|
||||
enum InitState {
|
||||
None,
|
||||
// Waiting on the client initialization
|
||||
@ -98,6 +106,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
|
||||
@ -187,6 +201,12 @@ impl PlayState for MainMenuState {
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(client_stage_update) = self.init.client().and_then(|init| init.stage_update()) {
|
||||
self.main_menu_ui
|
||||
.update_stage(DetailedInitializationStage::Client(client_stage_update));
|
||||
}
|
||||
|
||||
// Poll client creation.
|
||||
match self.init.client().and_then(|init| init.poll()) {
|
||||
Some(InitMsg::Done(Ok(mut client))) => {
|
||||
@ -263,13 +283,13 @@ impl PlayState for MainMenuState {
|
||||
|
||||
// Poll renderer pipeline creation
|
||||
if let InitState::Pipeline(..) = &self.init {
|
||||
// If not complete go to char select screen
|
||||
if global_state
|
||||
.window
|
||||
.renderer()
|
||||
.pipeline_creation_status()
|
||||
.is_none()
|
||||
if let Some((done, total)) = &global_state.window.renderer().pipeline_creation_status()
|
||||
{
|
||||
self.main_menu_ui.update_stage(
|
||||
DetailedInitializationStage::CreatingRenderPipeline(*done, *total),
|
||||
);
|
||||
// If complete go to char select screen
|
||||
} else {
|
||||
// Always succeeds since we check above
|
||||
if let InitState::Pipeline(client) =
|
||||
core::mem::replace(&mut self.init, InitState::None)
|
||||
|
@ -2,6 +2,7 @@ use super::{ConnectionState, Imgs, Message};
|
||||
|
||||
use crate::{
|
||||
game_input::GameInput,
|
||||
menu::main::DetailedInitializationStage,
|
||||
settings::ControlSettings,
|
||||
ui::{
|
||||
fonts::IcedFonts as Fonts,
|
||||
@ -9,11 +10,13 @@ use crate::{
|
||||
Graphic,
|
||||
},
|
||||
};
|
||||
use client::ClientInitStage;
|
||||
use common::assets::{self, AssetExt};
|
||||
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,
|
||||
@ -85,6 +88,7 @@ impl Screen {
|
||||
fonts: &Fonts,
|
||||
imgs: &Imgs,
|
||||
connection_state: &ConnectionState,
|
||||
init_stage: &DetailedInitializationStage,
|
||||
time: f64,
|
||||
i18n: &Localization,
|
||||
button_style: style::button::Style,
|
||||
@ -133,6 +137,83 @@ impl Screen {
|
||||
Space::new(Length::Fill, Length::Fill).into()
|
||||
};
|
||||
|
||||
let stage = {
|
||||
let stage_message = match init_stage {
|
||||
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")
|
||||
},
|
||||
DetailedInitializationStage::Client(client_stage) => match client_stage {
|
||||
ClientInitStage::ConnectionEstablish => {
|
||||
i18n.get_msg("hud-init-stage-client-connection-establish")
|
||||
},
|
||||
ClientInitStage::WatingForServerVersion => {
|
||||
i18n.get_msg("hud-init-stage-client-request-server-version")
|
||||
},
|
||||
ClientInitStage::Authentication => {
|
||||
i18n.get_msg("hud-init-stage-client-authentication")
|
||||
},
|
||||
ClientInitStage::LoadingInitData => {
|
||||
i18n.get_msg("hud-init-stage-client-load-init-data")
|
||||
},
|
||||
ClientInitStage::StartingClient => {
|
||||
i18n.get_msg("hud-init-stage-client-starting-client")
|
||||
},
|
||||
},
|
||||
DetailedInitializationStage::CreatingRenderPipeline(done, total) => i18n
|
||||
.get_msg_ctx(
|
||||
"hud-init-stage-render-pipeline",
|
||||
&i18n::fluent_args! { "done" => done, "total" => total },
|
||||
),
|
||||
};
|
||||
|
||||
Container::new(Text::new(stage_message).size(fonts.cyri.scale(20)))
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fill)
|
||||
.padding(10)
|
||||
.align_x(Align::Start)
|
||||
.into()
|
||||
};
|
||||
|
||||
let cancel = Container::new(neat_button(
|
||||
&mut self.cancel_button,
|
||||
i18n.get_msg("common-cancel"),
|
||||
@ -160,13 +241,10 @@ impl Screen {
|
||||
.padding(10)
|
||||
.align_x(Align::End);
|
||||
|
||||
let bottom_content = Row::with_children(vec![
|
||||
Space::new(Length::Fill, Length::Shrink).into(),
|
||||
tip_cancel.into(),
|
||||
gear.into(),
|
||||
])
|
||||
.align_items(Align::Center)
|
||||
.width(Length::Fill);
|
||||
let bottom_content =
|
||||
Row::with_children(vec![stage, tip_cancel.into(), gear.into()])
|
||||
.align_items(Align::Center)
|
||||
.width(Length::Fill);
|
||||
|
||||
let left_art = Image::new(imgs.loading_art_l)
|
||||
.width(Length::Units(12))
|
||||
|
@ -29,6 +29,8 @@ use rand::{seq::SliceRandom, thread_rng};
|
||||
use std::time::Duration;
|
||||
use tracing::warn;
|
||||
|
||||
use super::DetailedInitializationStage;
|
||||
|
||||
// TODO: what is this? (showed up in rebase)
|
||||
//const COL1: Color = Color::Rgba(0.07, 0.1, 0.1, 0.9);
|
||||
|
||||
@ -179,6 +181,7 @@ enum Screen {
|
||||
Connecting {
|
||||
screen: connecting::Screen,
|
||||
connection_state: ConnectionState,
|
||||
init_stage: DetailedInitializationStage,
|
||||
},
|
||||
#[cfg(feature = "singleplayer")]
|
||||
WorldSelector {
|
||||
@ -405,10 +408,12 @@ impl Controls {
|
||||
Screen::Connecting {
|
||||
screen,
|
||||
connection_state,
|
||||
init_stage,
|
||||
} => screen.view(
|
||||
&self.fonts,
|
||||
&self.imgs,
|
||||
connection_state,
|
||||
init_stage,
|
||||
self.time,
|
||||
&self.i18n.read(),
|
||||
button_style,
|
||||
@ -480,6 +485,7 @@ impl Controls {
|
||||
self.screen = Screen::Connecting {
|
||||
screen: connecting::Screen::new(ui),
|
||||
connection_state: ConnectionState::InProgress,
|
||||
init_stage: DetailedInitializationStage::Singleplayer,
|
||||
};
|
||||
events.push(Event::StartSingleplayer);
|
||||
},
|
||||
@ -520,6 +526,7 @@ impl Controls {
|
||||
self.screen = Screen::Connecting {
|
||||
screen: connecting::Screen::new(ui),
|
||||
connection_state: ConnectionState::InProgress,
|
||||
init_stage: DetailedInitializationStage::StartingMultiplayer,
|
||||
};
|
||||
|
||||
events.push(Event::LoginAttempt {
|
||||
@ -629,6 +636,12 @@ impl Controls {
|
||||
}
|
||||
}
|
||||
|
||||
fn update_init_stage(&mut self, stage: DetailedInitializationStage) {
|
||||
if let Screen::Connecting { init_stage, .. } = &mut self.screen {
|
||||
*init_stage = stage
|
||||
}
|
||||
}
|
||||
|
||||
fn tab(&mut self) {
|
||||
if let Screen::Login { screen, .. } = &mut self.screen {
|
||||
// TODO: add select all function in iced
|
||||
@ -714,6 +727,11 @@ impl MainMenuUi {
|
||||
|
||||
pub fn show_info(&mut self, msg: String) { self.controls.connection_error(msg); }
|
||||
|
||||
pub fn update_stage(&mut self, stage: DetailedInitializationStage) {
|
||||
tracing::trace!(?stage, "Updating stage");
|
||||
self.controls.update_init_stage(stage);
|
||||
}
|
||||
|
||||
pub fn connected(&mut self) { self.controls.exit_connect_screen(); }
|
||||
|
||||
pub fn cancel_connection(&mut self) { self.controls.exit_connect_screen(); }
|
||||
|
@ -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,9 @@ impl SingleplayerState {
|
||||
editable_settings,
|
||||
database_settings,
|
||||
&server_data_dir,
|
||||
&|init_stage| {
|
||||
let _ = server_stage_tx.send(init_stage);
|
||||
},
|
||||
runtime,
|
||||
) {
|
||||
Ok(server) => (Some(server), Ok(())),
|
||||
@ -143,6 +149,7 @@ impl SingleplayerState {
|
||||
*self = SingleplayerState::Running(Singleplayer {
|
||||
_server_thread: thread,
|
||||
stop_server_s,
|
||||
init_stage_receiver: server_stage_rx,
|
||||
receiver: result_receiver,
|
||||
paused,
|
||||
});
|
||||
|
@ -680,6 +680,7 @@ fn main() {
|
||||
calendar: None,
|
||||
},
|
||||
&pool,
|
||||
&|_| {},
|
||||
);
|
||||
println!("Loaded world");
|
||||
const HISTOGRAMS: bool = false;
|
||||
|
@ -32,6 +32,7 @@ fn main() -> Result {
|
||||
calendar: None,
|
||||
},
|
||||
&pool,
|
||||
&|_| {},
|
||||
);
|
||||
println!("Loaded world");
|
||||
let export_path = "dungeon.vox";
|
||||
|
@ -127,6 +127,7 @@ fn main() {
|
||||
calendar: None,
|
||||
},
|
||||
&pool,
|
||||
&|_| {},
|
||||
);
|
||||
println!("Loaded world");
|
||||
|
||||
|
@ -169,6 +169,7 @@ fn main() {
|
||||
calendar: None,
|
||||
},
|
||||
&pool,
|
||||
&|_| {},
|
||||
);
|
||||
println!("Loaded world");
|
||||
|
||||
|
@ -14,6 +14,7 @@ fn main() {
|
||||
..WorldOpts::default()
|
||||
},
|
||||
&threadpool,
|
||||
&|_| {},
|
||||
);
|
||||
|
||||
let index = index.as_index_ref();
|
||||
|
@ -53,6 +53,7 @@ fn main() {
|
||||
calendar: None,
|
||||
},
|
||||
&threadpool,
|
||||
&|_| {},
|
||||
);
|
||||
let index = index.as_index_ref();
|
||||
tracing::info!("Sampling data...");
|
||||
|
@ -70,6 +70,7 @@ fn generate(db_path: &str, ymin: Option<i32>, ymax: Option<i32>) -> Result<(), B
|
||||
calendar: None,
|
||||
},
|
||||
&pool,
|
||||
&|_| {},
|
||||
);
|
||||
println!("Loaded world");
|
||||
|
||||
|
@ -17,6 +17,7 @@ fn main() {
|
||||
calendar: None,
|
||||
},
|
||||
&threadpool,
|
||||
&|_| {},
|
||||
);
|
||||
core::hint::black_box((world, index));
|
||||
println!("{} ms", start.elapsed().as_nanos() / 1_000_000);
|
||||
|
@ -223,8 +223,21 @@ impl<'a, R: Rng> GenCtx<'a, R> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
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: &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 {
|
||||
|
@ -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,
|
||||
@ -85,6 +87,14 @@ pub enum Error {
|
||||
Other(String),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum WorldGenerateStage {
|
||||
WorldSimGenerate(WorldSimStage),
|
||||
WorldCivGenerate(WorldCivStage),
|
||||
EconomySimulation,
|
||||
SpotGeneration,
|
||||
}
|
||||
|
||||
pub struct World {
|
||||
sim: sim::WorldSim,
|
||||
civs: civ::Civs,
|
||||
@ -110,6 +120,7 @@ impl World {
|
||||
seed: u32,
|
||||
opts: sim::WorldOpts,
|
||||
threadpool: &rayon::ThreadPool,
|
||||
report_stage: &(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 +128,18 @@ impl World {
|
||||
threadpool.install(|| {
|
||||
let mut index = Index::new(seed);
|
||||
|
||||
let mut sim = sim::WorldSim::generate(seed, opts, threadpool);
|
||||
let mut sim = sim::WorldSim::generate(seed, opts, threadpool, &|stage| {
|
||||
report_stage(WorldGenerateStage::WorldSimGenerate(stage))
|
||||
});
|
||||
|
||||
let civs = civ::Civs::generate(seed, &mut sim, &mut index);
|
||||
let civs = civ::Civs::generate(seed, &mut sim, &mut index, &|stage| {
|
||||
report_stage(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))
|
||||
|
@ -2540,6 +2540,7 @@ pub fn do_erosion(
|
||||
k_d_scale: f64,
|
||||
k_da_scale: impl Fn(f64) -> f64,
|
||||
threadpool: &rayon::ThreadPool,
|
||||
report_progress: &dyn Fn(f64),
|
||||
) -> (Box<[Alt]>, Box<[Alt]> /* , Box<[Alt]> */) {
|
||||
debug!("Initializing erosion arrays...");
|
||||
let oldh_ = (0..map_size_lg.chunks_len())
|
||||
@ -2644,6 +2645,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);
|
||||
}
|
||||
|
||||
|
@ -645,6 +645,12 @@ impl WorldFile {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
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: &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: &dyn Fn(f64) =
|
||||
&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,
|
||||
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,
|
||||
&report_erosion,
|
||||
)
|
||||
};
|
||||
|
||||
@ -1351,6 +1367,7 @@ impl WorldSim {
|
||||
k_d_scale(n_approx),
|
||||
k_da_scale,
|
||||
threadpool,
|
||||
&report_erosion,
|
||||
)
|
||||
};
|
||||
|
||||
|
@ -340,9 +340,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, &|_| {});
|
||||
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, &|_| {});
|
||||
info!("Civs created");
|
||||
crate::sim2::simulate(&mut index, &mut sim);
|
||||
show_economy(&index.sites, &None);
|
||||
@ -366,12 +366,12 @@ 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, &|_| {});
|
||||
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, &|_| {});
|
||||
info!("Civs created");
|
||||
let mut outarr: Vec<EconomySetup> = Vec::new();
|
||||
for i in index.sites.values() {
|
||||
@ -505,7 +505,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, &|_| {});
|
||||
info!("World loaded");
|
||||
let rng = ChaChaRng::from_seed(seed_expan::rng_state(seed));
|
||||
let mut env = Simenv {
|
||||
|
Loading…
Reference in New Issue
Block a user