veloren_server/
lib.rs

1#![deny(unsafe_code)]
2#![expect(
3    clippy::option_map_unit_fn,
4    clippy::needless_pass_by_ref_mut // until we find a better way for specs
5)]
6#![deny(clippy::clone_on_ref_ptr)]
7#![feature(box_patterns, let_chains, option_zip, const_type_name)]
8
9pub mod automod;
10mod character_creator;
11pub mod chat;
12pub mod chunk_generator;
13mod chunk_serialize;
14pub mod client;
15pub mod cmd;
16pub mod connection_handler;
17mod data_dir;
18pub mod error;
19pub mod events;
20pub mod input;
21pub mod location;
22pub mod lod;
23pub mod login_provider;
24pub mod metrics;
25pub mod persistence;
26mod pet;
27pub mod presence;
28pub mod rtsim;
29pub mod settings;
30pub mod state_ext;
31pub mod sys;
32#[cfg(feature = "persistent_world")]
33pub mod terrain_persistence;
34#[cfg(not(feature = "worldgen"))] mod test_world;
35
36#[cfg(feature = "worldgen")] mod weather;
37
38pub mod wiring;
39
40// Reexports
41pub use crate::{
42    data_dir::DEFAULT_DATA_DIR_NAME,
43    error::Error,
44    events::Event,
45    input::Input,
46    settings::{CalendarMode, EditableSettings, Settings},
47};
48
49#[cfg(feature = "persistent_world")]
50use crate::terrain_persistence::TerrainPersistence;
51use crate::{
52    automod::AutoMod,
53    chunk_generator::ChunkGenerator,
54    client::Client,
55    cmd::ChatCommandExt,
56    connection_handler::ConnectionHandler,
57    data_dir::DataDir,
58    location::Locations,
59    login_provider::LoginProvider,
60    persistence::PersistedComponents,
61    presence::{RegionSubscription, RepositionOnChunkLoad},
62    state_ext::StateExt,
63    sys::sentinel::DeletedEntities,
64};
65use censor::Censor;
66#[cfg(not(feature = "worldgen"))]
67use common::grid::Grid;
68#[cfg(feature = "worldgen")]
69use common::terrain::CoordinateConversions;
70#[cfg(feature = "worldgen")]
71use common::terrain::TerrainChunkSize;
72use common::{
73    assets::AssetExt,
74    calendar::Calendar,
75    character::{CharacterId, CharacterItem},
76    cmd::ServerChatCommand,
77    comp::{self, ChatType, Content},
78    event::{
79        ClientDisconnectEvent, ClientDisconnectWithoutPersistenceEvent, EventBus, ExitIngameEvent,
80        UpdateCharacterDataEvent,
81    },
82    link::Is,
83    mounting::{Volume, VolumeRider},
84    region::RegionMap,
85    resources::{BattleMode, GameMode, Time, TimeOfDay},
86    rtsim::RtSimEntity,
87    shared_server_config::ServerConstants,
88    slowjob::SlowJobPool,
89    terrain::TerrainChunk,
90    uid::Uid,
91    util::GIT_DATE_TIMESTAMP,
92    vol::RectRasterableVol,
93};
94use common_base::prof_span;
95use common_ecs::run_now;
96use common_net::{
97    msg::{ClientType, DisconnectReason, PlayerListUpdate, ServerGeneral, ServerInfo, ServerMsg},
98    sync::WorldSyncExt,
99};
100use common_state::{AreasContainer, BlockDiff, BuildArea, State};
101use common_systems::add_local_systems;
102use metrics::{EcsSystemMetrics, PhysicsMetrics, TickMetrics};
103use network::{ListenAddr, Network, Pid};
104use persistence::{
105    character_loader::{CharacterLoader, CharacterUpdaterMessage},
106    character_updater::CharacterUpdater,
107};
108use prometheus::Registry;
109use rustls::pki_types::{CertificateDer, PrivateKeyDer};
110use specs::{
111    Builder, Entity as EcsEntity, Entity, Join, LendJoin, WorldExt, shred::SendDispatcher,
112};
113use std::{
114    ops::{Deref, DerefMut},
115    sync::{Arc, Mutex},
116    time::{Duration, Instant},
117};
118#[cfg(not(feature = "worldgen"))]
119use test_world::{IndexOwned, World};
120use tokio::runtime::Runtime;
121use tracing::{debug, error, info, trace, warn};
122use vek::*;
123use veloren_query_server::server::QueryServer;
124pub use world::{WorldGenerateStage, civ::WorldCivStage, sim::WorldSimStage};
125
126use crate::{
127    persistence::{DatabaseSettings, SqlLogMode},
128    sys::terrain,
129};
130use hashbrown::HashMap;
131use std::sync::RwLock;
132
133use crate::settings::Protocol;
134
135#[cfg(feature = "plugins")]
136use {
137    common::uid::IdMaps,
138    common_state::plugin::{PluginMgr, memory_manager::EcsWorld},
139};
140
141use crate::{chat::ChatCache, persistence::character_loader::CharacterScreenResponseKind};
142use common::comp::Anchor;
143#[cfg(feature = "worldgen")]
144pub use world::{
145    IndexOwned, World,
146    sim::{DEFAULT_WORLD_MAP, DEFAULT_WORLD_SEED, FileOpts, GenOpts, WorldOpts},
147};
148
149/// Number of seconds a player must wait before they can change their battle
150/// mode after each change.
151///
152/// TODO: Discuss time
153const BATTLE_MODE_COOLDOWN: f64 = 60.0 * 5.0;
154
155/// SpawnPoint corresponds to the default location that players are positioned
156/// at if they have no waypoint. Players *should* always have a waypoint, so
157/// this should basically never be used in practice.
158#[derive(Copy, Clone)]
159pub struct SpawnPoint(pub Vec3<f32>);
160
161impl Default for SpawnPoint {
162    fn default() -> Self { Self(Vec3::new(0.0, 0.0, 256.0)) }
163}
164
165// This is the minimum chunk range that is kept loaded around each player
166// server-side. This is independent of the client's view distance and exists to
167// avoid exploits such as small view distance chunk reloading and also to keep
168// various mechanics working fluidly (i.e: not unloading nearby entities).
169pub const MIN_VD: u32 = 6;
170
171// Tick count used for throttling network updates
172// Note this doesn't account for dt (so update rate changes with tick rate)
173#[derive(Copy, Clone, Default)]
174pub struct Tick(u64);
175
176#[derive(Clone)]
177pub struct HwStats {
178    hardware_threads: u32,
179    rayon_threads: u32,
180}
181
182#[derive(Clone, Copy, PartialEq)]
183enum DisconnectType {
184    WithPersistence,
185    WithoutPersistence,
186}
187
188// Start of Tick, used for metrics
189#[derive(Copy, Clone)]
190pub struct TickStart(Instant);
191
192/// Store of BattleMode cooldowns for players while they go offline
193#[derive(Clone, Default, Debug)]
194pub struct BattleModeBuffer {
195    map: HashMap<CharacterId, (BattleMode, Time)>,
196}
197
198impl BattleModeBuffer {
199    pub fn push(&mut self, char_id: CharacterId, save: (BattleMode, Time)) {
200        self.map.insert(char_id, save);
201    }
202
203    pub fn get(&self, char_id: &CharacterId) -> Option<&(BattleMode, Time)> {
204        self.map.get(char_id)
205    }
206
207    pub fn pop(&mut self, char_id: &CharacterId) -> Option<(BattleMode, Time)> {
208        self.map.remove(char_id)
209    }
210}
211
212pub struct ChunkRequest {
213    entity: EcsEntity,
214    key: Vec2<i32>,
215}
216
217#[derive(Debug)]
218pub enum ServerInitStage {
219    DbMigrations,
220    DbVacuum,
221    WorldGen(WorldGenerateStage),
222    StartingSystems,
223}
224
225pub struct Server {
226    state: State,
227    world: Arc<World>,
228    index: IndexOwned,
229
230    connection_handler: ConnectionHandler,
231
232    runtime: Arc<Runtime>,
233
234    metrics_registry: Arc<Registry>,
235    chat_cache: ChatCache,
236    database_settings: Arc<RwLock<DatabaseSettings>>,
237    disconnect_all_clients_requested: bool,
238
239    event_dispatcher: SendDispatcher<'static>,
240}
241
242impl Server {
243    /// Create a new `Server`
244    pub fn new(
245        settings: Settings,
246        editable_settings: EditableSettings,
247        database_settings: DatabaseSettings,
248        data_dir: &std::path::Path,
249        report_stage: &(dyn Fn(ServerInitStage) + Send + Sync),
250        runtime: Arc<Runtime>,
251    ) -> Result<Self, Error> {
252        prof_span!("Server::new");
253        info!("Server data dir is: {}", data_dir.display());
254        if settings.auth_server_address.is_none() {
255            info!("Authentication is disabled");
256        }
257
258        report_stage(ServerInitStage::DbMigrations);
259        // Run pending DB migrations (if any)
260        debug!("Running DB migrations...");
261        persistence::run_migrations(&database_settings);
262
263        report_stage(ServerInitStage::DbVacuum);
264        // Vacuum database
265        debug!("Vacuuming database...");
266        persistence::vacuum_database(&database_settings);
267
268        let database_settings = Arc::new(RwLock::new(database_settings));
269
270        let registry = Arc::new(Registry::new());
271        let chunk_gen_metrics = metrics::ChunkGenMetrics::new(&registry).unwrap();
272        let job_metrics = metrics::JobMetrics::new(&registry).unwrap();
273        let network_request_metrics = metrics::NetworkRequestMetrics::new(&registry).unwrap();
274        let player_metrics = metrics::PlayerMetrics::new(&registry).unwrap();
275        let ecs_system_metrics = EcsSystemMetrics::new(&registry).unwrap();
276        let tick_metrics = TickMetrics::new(&registry).unwrap();
277        let physics_metrics = PhysicsMetrics::new(&registry).unwrap();
278        let server_event_metrics = metrics::ServerEventMetrics::new(&registry).unwrap();
279        let query_server_metrics = metrics::QueryServerMetrics::new(&registry).unwrap();
280
281        let battlemode_buffer = BattleModeBuffer::default();
282
283        let pools = State::pools(GameMode::Server);
284
285        // Load plugins before generating the world.
286        #[cfg(feature = "plugins")]
287        let plugin_mgr = PluginMgr::from_asset_or_default();
288
289        debug!("Generating world, seed: {}", settings.world_seed);
290        #[cfg(feature = "worldgen")]
291        let (world, index) = World::generate(
292            settings.world_seed,
293            WorldOpts {
294                seed_elements: true,
295                world_file: if let Some(ref opts) = settings.map_file {
296                    opts.clone()
297                } else {
298                    // Load default map from assets.
299                    FileOpts::LoadAsset(DEFAULT_WORLD_MAP.into())
300                },
301                calendar: Some(settings.calendar_mode.calendar_now()),
302            },
303            &pools,
304            &|stage| {
305                report_stage(ServerInitStage::WorldGen(stage));
306            },
307        );
308        #[cfg(not(feature = "worldgen"))]
309        let (world, index) = World::generate(settings.world_seed);
310
311        #[cfg(feature = "worldgen")]
312        let map = world.get_map_data(index.as_index_ref(), &pools);
313        #[cfg(not(feature = "worldgen"))]
314        let map = common_net::msg::WorldMapMsg {
315            dimensions_lg: Vec2::zero(),
316            max_height: 1.0,
317            rgba: Grid::new(Vec2::new(1, 1), 1),
318            horizons: [(vec![0], vec![0]), (vec![0], vec![0])],
319            alt: Grid::new(Vec2::new(1, 1), 1),
320            sites: Vec::new(),
321            possible_starting_sites: Vec::new(),
322            pois: Vec::new(),
323            default_chunk: Arc::new(world.generate_oob_chunk()),
324        };
325
326        #[cfg(feature = "worldgen")]
327        let map_size_lg = world.sim().map_size_lg();
328        #[cfg(not(feature = "worldgen"))]
329        let map_size_lg = world.map_size_lg();
330
331        let lod = lod::Lod::from_world(&world, index.as_index_ref(), &pools);
332
333        report_stage(ServerInitStage::StartingSystems);
334
335        let mut state = State::server(
336            Arc::clone(&pools),
337            map_size_lg,
338            Arc::clone(&map.default_chunk),
339            |dispatcher_builder| {
340                add_local_systems(dispatcher_builder);
341                sys::msg::add_server_systems(dispatcher_builder);
342                sys::add_server_systems(dispatcher_builder);
343                #[cfg(feature = "worldgen")]
344                {
345                    rtsim::add_server_systems(dispatcher_builder);
346                    weather::add_server_systems(dispatcher_builder);
347                }
348            },
349            #[cfg(feature = "plugins")]
350            plugin_mgr,
351        );
352        events::register_event_busses(state.ecs_mut());
353        state.ecs_mut().insert(battlemode_buffer);
354        state.ecs_mut().insert(settings.clone());
355        state.ecs_mut().insert(editable_settings);
356        state.ecs_mut().insert(DataDir {
357            path: data_dir.to_owned(),
358        });
359
360        state.ecs_mut().insert(Vec::<ChunkRequest>::new());
361        state
362            .ecs_mut()
363            .insert(EventBus::<chunk_serialize::ChunkSendEntry>::default());
364        state.ecs_mut().insert(Locations::default());
365        state.ecs_mut().insert(LoginProvider::new(
366            settings.auth_server_address.clone(),
367            Arc::clone(&runtime),
368        ));
369        state.ecs_mut().insert(HwStats {
370            hardware_threads: num_cpus::get() as u32,
371            rayon_threads: num_cpus::get() as u32,
372        });
373        state.ecs_mut().insert(ServerConstants {
374            day_cycle_coefficient: settings.day_cycle_coefficient(),
375        });
376        state.ecs_mut().insert(Tick(0));
377        state.ecs_mut().insert(TickStart(Instant::now()));
378        state.ecs_mut().insert(job_metrics);
379        state.ecs_mut().insert(network_request_metrics);
380        state.ecs_mut().insert(player_metrics);
381        state.ecs_mut().insert(ecs_system_metrics);
382        state.ecs_mut().insert(tick_metrics);
383        state.ecs_mut().insert(physics_metrics);
384        state.ecs_mut().insert(server_event_metrics);
385        state.ecs_mut().insert(query_server_metrics);
386        if settings.experimental_terrain_persistence {
387            #[cfg(feature = "persistent_world")]
388            {
389                warn!(
390                    "Experimental terrain persistence support is enabled. This feature may break, \
391                     be disabled, or otherwise change under your feet at *any time*. \
392                     Additionally, it is expected to be replaced in the future *without* \
393                     migration or warning. You have been warned."
394                );
395                state
396                    .ecs_mut()
397                    .insert(TerrainPersistence::new(data_dir.to_owned()));
398            }
399            #[cfg(not(feature = "persistent_world"))]
400            error!(
401                "Experimental terrain persistence support was requested, but the server was not \
402                 compiled with the feature. Terrain modifications will *not* be persisted."
403            );
404        }
405        {
406            let pool = state.ecs_mut().write_resource::<SlowJobPool>();
407            pool.configure("CHUNK_DROP", |_n| 1);
408            pool.configure("CHUNK_GENERATOR", |n| n / 2 + n / 4);
409            pool.configure("CHUNK_SERIALIZER", |n| n / 2);
410            pool.configure("RTSIM_SAVE", |_| 1);
411            pool.configure("WEATHER", |_| 1);
412        }
413        state
414            .ecs_mut()
415            .insert(ChunkGenerator::new(chunk_gen_metrics));
416        {
417            let (sender, receiver) =
418                crossbeam_channel::bounded::<chunk_serialize::SerializedChunk>(10_000);
419            state.ecs_mut().insert(sender);
420            state.ecs_mut().insert(receiver);
421        }
422
423        state.ecs_mut().insert(CharacterUpdater::new(
424            Arc::<RwLock<DatabaseSettings>>::clone(&database_settings),
425        )?);
426
427        let ability_map = comp::item::tool::AbilityMap::<comp::AbilityItem>::load_expect_cloned(
428            "common.abilities.ability_set_manifest",
429        );
430        state.ecs_mut().insert(ability_map);
431
432        let msm = comp::inventory::item::MaterialStatManifest::load().cloned();
433        state.ecs_mut().insert(msm);
434
435        let rbm = common::recipe::RecipeBookManifest::load().cloned();
436        state.ecs_mut().insert(rbm);
437
438        state.ecs_mut().insert(CharacterLoader::new(
439            Arc::<RwLock<DatabaseSettings>>::clone(&database_settings),
440        )?);
441
442        // System schedulers to control execution of systems
443        state
444            .ecs_mut()
445            .insert(sys::PersistenceScheduler::every(Duration::from_secs(10)));
446
447        // Region map (spatial structure for entity synchronization)
448        state.ecs_mut().insert(RegionMap::new());
449
450        // Server-only components
451        state.ecs_mut().register::<RegionSubscription>();
452        state.ecs_mut().register::<Client>();
453        state.ecs_mut().register::<comp::Presence>();
454        state.ecs_mut().register::<wiring::WiringElement>();
455        state.ecs_mut().register::<wiring::Circuit>();
456        state.ecs_mut().register::<Anchor>();
457        state.ecs_mut().register::<comp::Pet>();
458        state.ecs_mut().register::<login_provider::PendingLogin>();
459        state.ecs_mut().register::<RepositionOnChunkLoad>();
460        state.ecs_mut().register::<RtSimEntity>();
461
462        // Load banned words list
463        let banned_words = settings.moderation.load_banned_words(data_dir);
464        let censor = Arc::new(Censor::Custom(banned_words.into_iter().collect()));
465        state.ecs_mut().insert(Arc::clone(&censor));
466
467        // Init automod
468        state
469            .ecs_mut()
470            .insert(AutoMod::new(&settings.moderation, censor));
471
472        state.ecs_mut().insert(map);
473
474        #[cfg(feature = "worldgen")]
475        let spawn_point = SpawnPoint({
476            use world::civ::SiteKind;
477
478            let index = index.as_index_ref();
479            // NOTE: all of these `.map(|e| e as [type])` calls should compile into no-ops,
480            // but are needed to be explicit about casting (and to make the compiler stop
481            // complaining)
482
483            // Search for town defined by spawn_town server setting. If this fails, or is
484            // None, set spawn to the nearest town to the centre of the world
485            let center_chunk = world.sim().map_size_lg().chunks().map(i32::from) / 2;
486            let spawn_chunk = world
487                .civs()
488                .sites()
489                .filter(|site| matches!(site.kind, SiteKind::Settlement | SiteKind::Refactor))
490                .map(|site| site.center)
491                .min_by_key(|site_pos| site_pos.distance_squared(center_chunk))
492                .unwrap_or(center_chunk);
493
494            world.find_accessible_pos(index, TerrainChunkSize::center_wpos(spawn_chunk), false)
495        });
496        #[cfg(not(feature = "worldgen"))]
497        let spawn_point = SpawnPoint::default();
498
499        // Set the spawn point we calculated above
500        state.ecs_mut().insert(spawn_point);
501
502        // Insert a default AABB for the world
503        // TODO: prevent this from being deleted
504        {
505            #[cfg(feature = "worldgen")]
506            let size = world.sim().get_size();
507            #[cfg(not(feature = "worldgen"))]
508            let size = world.map_size_lg().chunks().map(u32::from);
509
510            let world_size = size.map(|e| e as i32) * TerrainChunk::RECT_SIZE.map(|e| e as i32);
511            let world_aabb = Aabb {
512                min: Vec3::new(0, 0, -32768),
513                max: Vec3::new(world_size.x, world_size.y, 32767),
514            }
515            .made_valid();
516
517            state
518                .ecs()
519                .write_resource::<AreasContainer<BuildArea>>()
520                .insert("world".to_string(), world_aabb)
521                .expect("The initial insert should always work.");
522        }
523
524        // Insert the world into the ECS (todo: Maybe not an Arc?)
525        let world = Arc::new(world);
526        state.ecs_mut().insert(Arc::clone(&world));
527        state.ecs_mut().insert(lod);
528        state.ecs_mut().insert(index.clone());
529
530        // Set starting time for the server.
531        state.ecs_mut().write_resource::<TimeOfDay>().0 = settings.world.start_time;
532
533        // Register trackers
534        sys::sentinel::UpdateTrackers::register(state.ecs_mut());
535
536        state.ecs_mut().insert(DeletedEntities::default());
537
538        let network = Network::new_with_registry(Pid::new(), &runtime, &registry);
539        let (chat_cache, chat_tracker) = ChatCache::new(Duration::from_secs(60), &runtime);
540        state.ecs_mut().insert(chat_tracker);
541
542        let mut printed_quic_warning = false;
543        for protocol in &settings.gameserver_protocols {
544            match protocol {
545                Protocol::Tcp { address } => {
546                    runtime.block_on(network.listen(ListenAddr::Tcp(*address)))?;
547                },
548                Protocol::Quic {
549                    address,
550                    cert_file_path,
551                    key_file_path,
552                } => {
553                    use rustls_pemfile::Item;
554                    use std::fs;
555
556                    match || -> Result<_, Box<dyn std::error::Error>> {
557                        let key = fs::read(key_file_path)?;
558                        let key = if key_file_path.extension().is_some_and(|x| x == "der") {
559                            PrivateKeyDer::try_from(key).map_err(|_| "No valid pem key in file")?
560                        } else {
561                            debug!("convert pem key to der");
562                            rustls_pemfile::read_all(&mut key.as_slice())
563                                .find_map(|item| match item {
564                                    Ok(Item::Pkcs1Key(v)) => Some(PrivateKeyDer::Pkcs1(v)),
565                                    Ok(Item::Pkcs8Key(v)) => Some(PrivateKeyDer::Pkcs8(v)),
566                                    Ok(Item::Sec1Key(v)) => Some(PrivateKeyDer::Sec1(v)),
567                                    Ok(Item::Crl(_)) => None,
568                                    Ok(Item::Csr(_)) => None,
569                                    Ok(Item::X509Certificate(_)) => None,
570                                    Ok(_) => None,
571                                    Err(e) => {
572                                        tracing::warn!(?e, "error while reading key_file");
573                                        None
574                                    },
575                                })
576                                .ok_or("No valid pem key in file")?
577                        };
578                        let cert_chain = fs::read(cert_file_path)?;
579                        let cert_chain = if cert_file_path.extension().is_some_and(|x| x == "der") {
580                            vec![CertificateDer::from(cert_chain)]
581                        } else {
582                            debug!("convert pem cert to der");
583                            rustls_pemfile::certs(&mut cert_chain.as_slice())
584                                .filter_map(|item| match item {
585                                    Ok(cert) => Some(cert),
586                                    Err(e) => {
587                                        tracing::warn!(?e, "error while reading cert_file");
588                                        None
589                                    },
590                                })
591                                .collect()
592                        };
593                        let server_config = quinn::ServerConfig::with_single_cert(cert_chain, key)?;
594                        Ok(server_config)
595                    }() {
596                        Ok(server_config) => {
597                            runtime.block_on(
598                                network.listen(ListenAddr::Quic(*address, server_config.clone())),
599                            )?;
600
601                            if !printed_quic_warning {
602                                warn!(
603                                    "QUIC is enabled. This is experimental and not recommended in \
604                                     production"
605                                );
606                                printed_quic_warning = true;
607                            }
608                        },
609                        Err(e) => {
610                            error!(
611                                ?e,
612                                "Failed to load the TLS certificate, running without QUIC {}",
613                                *address
614                            );
615                        },
616                    }
617                },
618            }
619        }
620
621        if let Some(addr) = settings.query_address {
622            use veloren_query_server::proto::ServerInfo;
623
624            const QUERY_SERVER_RATELIMIT: u16 = 120;
625
626            let (query_server_info_tx, query_server_info_rx) =
627                tokio::sync::watch::channel(ServerInfo {
628                    git_hash: *sys::server_info::GIT_HASH,
629                    git_timestamp: *GIT_DATE_TIMESTAMP,
630                    players_count: 0,
631                    player_cap: settings.max_players,
632                    battlemode: settings.gameplay.battle_mode.into(),
633                });
634            let mut query_server =
635                QueryServer::new(addr, query_server_info_rx, QUERY_SERVER_RATELIMIT);
636            let query_server_metrics =
637                Arc::new(Mutex::new(veloren_query_server::server::Metrics::default()));
638            let query_server_metrics2 = Arc::clone(&query_server_metrics);
639            runtime.spawn(async move {
640                let err = query_server.run(query_server_metrics2).await.err();
641                error!(?err, "Query server stopped unexpectedly");
642            });
643            state.ecs_mut().insert(query_server_info_tx);
644            state.ecs_mut().insert(query_server_metrics);
645        }
646
647        runtime.block_on(network.listen(ListenAddr::Mpsc(14004)))?;
648
649        let connection_handler = ConnectionHandler::new(network, &runtime);
650
651        // Init rtsim, loading it from disk if possible
652        #[cfg(feature = "worldgen")]
653        {
654            match rtsim::RtSim::new(
655                &settings.world,
656                index.as_index_ref(),
657                &world,
658                data_dir.to_owned(),
659            ) {
660                Ok(rtsim) => {
661                    state.ecs_mut().insert(rtsim.state().data().time_of_day);
662                    state.ecs_mut().insert(rtsim);
663                },
664                Err(err) => {
665                    error!("Failed to load rtsim: {}", err);
666                    return Err(Error::RtsimError(err));
667                },
668            }
669            weather::init(&mut state);
670        }
671
672        let this = Self {
673            state,
674            world,
675            index,
676            connection_handler,
677            runtime,
678
679            metrics_registry: registry,
680            chat_cache,
681            database_settings,
682            disconnect_all_clients_requested: false,
683
684            event_dispatcher: Self::create_event_dispatcher(pools),
685        };
686
687        debug!(?settings, "created veloren server with");
688
689        let git_hash = *common::util::GIT_HASH;
690        let git_date = common::util::GIT_DATE.clone();
691        let git_time = *common::util::GIT_TIME;
692        let version = common::util::DISPLAY_VERSION_LONG.clone();
693        info!(?version, "Server version");
694        debug!(?git_hash, ?git_date, ?git_time, "detailed Server version");
695
696        Ok(this)
697    }
698
699    pub fn get_server_info(&self) -> ServerInfo {
700        let settings = self.state.ecs().fetch::<Settings>();
701
702        ServerInfo {
703            name: settings.server_name.clone(),
704            git_hash: common::util::GIT_HASH.to_string(),
705            git_date: common::util::GIT_DATE.to_string(),
706            auth_provider: settings.auth_server_address.clone(),
707        }
708    }
709
710    /// Get a reference to the server's settings
711    pub fn settings(&self) -> impl Deref<Target = Settings> + '_ {
712        self.state.ecs().fetch::<Settings>()
713    }
714
715    /// Get a mutable reference to the server's settings
716    pub fn settings_mut(&self) -> impl DerefMut<Target = Settings> + '_ {
717        self.state.ecs().fetch_mut::<Settings>()
718    }
719
720    /// Get a mutable reference to the server's editable settings
721    pub fn editable_settings_mut(&self) -> impl DerefMut<Target = EditableSettings> + '_ {
722        self.state.ecs().fetch_mut::<EditableSettings>()
723    }
724
725    /// Get a reference to the server's editable settings
726    pub fn editable_settings(&self) -> impl Deref<Target = EditableSettings> + '_ {
727        self.state.ecs().fetch::<EditableSettings>()
728    }
729
730    /// Get path to the directory that the server info into
731    pub fn data_dir(&self) -> impl Deref<Target = DataDir> + '_ {
732        self.state.ecs().fetch::<DataDir>()
733    }
734
735    /// Get a reference to the server's game state.
736    pub fn state(&self) -> &State { &self.state }
737
738    /// Get a mutable reference to the server's game state.
739    pub fn state_mut(&mut self) -> &mut State { &mut self.state }
740
741    /// Get a reference to the server's world.
742    pub fn world(&self) -> &World { &self.world }
743
744    /// Get a reference to the Metrics Registry
745    pub fn metrics_registry(&self) -> &Arc<Registry> { &self.metrics_registry }
746
747    /// Get a reference to the Chat Cache
748    pub fn chat_cache(&self) -> &ChatCache { &self.chat_cache }
749
750    fn parse_locations(&self, character_list_data: &mut [CharacterItem]) {
751        character_list_data.iter_mut().for_each(|c| {
752            let name = c
753                .location
754                .as_ref()
755                .and_then(|s| {
756                    persistence::parse_waypoint(s)
757                        .ok()
758                        .and_then(|(waypoint, _)| waypoint.map(|w| w.get_pos()))
759                })
760                .and_then(|wpos| {
761                    self.world
762                        .get_location_name(self.index.as_index_ref(), wpos.xy().as_::<i32>())
763                });
764            c.location = name;
765        });
766    }
767
768    /// Execute a single server tick, handle input and update the game state by
769    /// the given duration.
770    pub fn tick(&mut self, _input: Input, dt: Duration) -> Result<Vec<Event>, Error> {
771        self.state.ecs().write_resource::<Tick>().0 += 1;
772        self.state.ecs().write_resource::<TickStart>().0 = Instant::now();
773
774        // Update calendar events as time changes
775        // TODO: If a lot of calendar events get added, this might become expensive.
776        // Maybe don't do this every tick?
777        let new_calendar = self
778            .state
779            .ecs()
780            .read_resource::<Settings>()
781            .calendar_mode
782            .calendar_now();
783        *self.state.ecs_mut().write_resource::<Calendar>() = new_calendar;
784
785        // This tick function is the centre of the Veloren universe. Most server-side
786        // things are managed from here, and as such it's important that it
787        // stays organised. Please consult the core developers before making
788        // significant changes to this code. Here is the approximate order of
789        // things. Please update it as this code changes.
790        //
791        // 1) Collect input from the frontend, apply input effects to the state of the
792        //    game
793        // 2) Go through any events (timer-driven or otherwise) that need handling and
794        //    apply them to the state of the game
795        // 3) Go through all incoming client network communications, apply them to the
796        //    game state
797        // 4) Perform a single LocalState tick (i.e: update the world and entities in
798        //    the world)
799        // 5) Go through the terrain update queue and apply all changes to the terrain
800        // 6) Send relevant state updates to all clients
801        // 7) Check for persistence updates related to character data, and message the
802        //    relevant entities
803        // 8) Update Metrics with current data
804        // 9) Finish the tick, passing control of the main thread back to the frontend
805
806        // 1) Build up a list of events for this frame, to be passed to the frontend.
807        let mut frontend_events = Vec::new();
808
809        // 2)
810
811        let before_new_connections = Instant::now();
812
813        // 3) Handle inputs from clients
814        self.handle_new_connections(&mut frontend_events);
815
816        let before_state_tick = Instant::now();
817
818        fn on_block_update(ecs: &specs::World, changes: Vec<BlockDiff>) {
819            // When a resource block updates, inform rtsim
820            if changes
821                .iter()
822                .any(|c| c.old.get_rtsim_resource() != c.new.get_rtsim_resource())
823            {
824                ecs.write_resource::<rtsim::RtSim>().hook_block_update(
825                    &ecs.read_resource::<Arc<world::World>>(),
826                    ecs.read_resource::<world::IndexOwned>().as_index_ref(),
827                    changes,
828                );
829            }
830        }
831
832        // 4) Tick the server's LocalState.
833        // 5) Fetch any generated `TerrainChunk`s and insert them into the terrain.
834        // in sys/terrain.rs
835        let mut state_tick_metrics = Default::default();
836        let server_constants = (*self.state.ecs().read_resource::<ServerConstants>()).clone();
837        self.state.tick(
838            dt,
839            false,
840            Some(&mut state_tick_metrics),
841            &server_constants,
842            on_block_update,
843        );
844
845        let before_handle_events = Instant::now();
846
847        // Process any pending request to disconnect all clients, the disconnections
848        // will be processed once handle_events() is called below
849        let disconnect_type = self.disconnect_all_clients_if_requested();
850
851        // Handle entity links (such as mounting)
852        self.state.maintain_links();
853
854        // Handle game events
855        frontend_events.append(&mut self.handle_events());
856
857        let before_update_terrain_and_regions = Instant::now();
858
859        // Apply terrain changes and update the region map after processing server
860        // events so that changes made by server events will be immediately
861        // visible to client synchronization systems, minimizing the latency of
862        // `ServerEvent` mediated effects
863        self.update_region_map();
864        // NOTE: apply_terrain_changes sends the *new* value since it is not being
865        // synchronized during the tick.
866        self.state.apply_terrain_changes(on_block_update);
867
868        let before_sync = Instant::now();
869
870        // 6) Synchronise clients with the new state of the world.
871        sys::run_sync_systems(self.state.ecs_mut());
872
873        let before_world_tick = Instant::now();
874
875        // Tick the world
876        self.world.tick(dt);
877
878        let before_entity_cleanup = Instant::now();
879
880        // In the event of a request to disconnect all players without persistence, we
881        // must run the terrain system a second time after the messages to
882        // perform client disconnections have been processed. This ensures that any
883        // items on the ground are deleted.
884        if let Some(DisconnectType::WithoutPersistence) = disconnect_type {
885            run_now::<terrain::Sys>(self.state.ecs_mut());
886        }
887
888        // Hook rtsim chunk unloads
889        #[cfg(feature = "worldgen")]
890        {
891            let mut rtsim = self.state.ecs().write_resource::<rtsim::RtSim>();
892            let world = self.state.ecs().read_resource::<Arc<World>>();
893            for chunk in &self.state.terrain_changes().removed_chunks {
894                rtsim.hook_unload_chunk(*chunk, &world);
895            }
896        }
897
898        // Prevent anchor entity chains which are not currently supported due to:
899        // * potential cycles?
900        // * unloading a chain could occur across an unbounded number of ticks with the
901        //   current implementation.
902        // * in particular, we want to be able to unload all entities in a
903        //   limited number of ticks when a database error occurs and kicks all
904        //   players (not quiet sure on exact time frame, since it already
905        //   takes a tick after unloading all chunks for entities to despawn?),
906        //   see this thread and the discussion linked from there:
907        //   https://gitlab.com/veloren/veloren/-/merge_requests/2668#note_634913847
908        let anchors = self.state.ecs().read_storage::<Anchor>();
909        let anchored_anchor_entities: Vec<Entity> = (
910            &self.state.ecs().entities(),
911            &self.state.ecs().read_storage::<Anchor>(),
912        )
913            .join()
914            .filter_map(|(_, anchor)| match anchor {
915                Anchor::Entity(anchor_entity) => Some(*anchor_entity),
916                _ => None,
917            })
918            // We allow Anchor::Entity(_) -> Anchor::Chunk(_) connections, since they can't chain further.
919            //
920            // NOTE: The entity with `Anchor::Entity` will unload one tick after the entity with `Anchor::Chunk`.
921            .filter(|anchor_entity| match anchors.get(*anchor_entity) {
922                Some(Anchor::Entity(_)) => true,
923                Some(Anchor::Chunk(_)) | None => false
924            })
925            .collect();
926        drop(anchors);
927
928        for entity in anchored_anchor_entities {
929            if cfg!(debug_assertions) {
930                panic!("Entity anchor chain detected");
931            }
932            error!(
933                "Detected an anchor entity that itself has an anchor entity - anchor chains are \
934                 not currently supported. The entity's Anchor component has been deleted"
935            );
936            self.state.delete_component::<Anchor>(entity);
937        }
938
939        // Remove NPCs that are outside the view distances of all players
940        // This is done by removing NPCs in unloaded chunks
941        let to_delete = {
942            let terrain = self.state.terrain();
943            (
944                &self.state.ecs().entities(),
945                &self.state.ecs().read_storage::<comp::Pos>(),
946                !&self.state.ecs().read_storage::<comp::Presence>(),
947                self.state.ecs().read_storage::<Anchor>().maybe(),
948                self.state.ecs().read_storage::<Is<VolumeRider>>().maybe(),
949            )
950                .join()
951                .filter(|(_, pos, _, anchor, is_volume_rider)| {
952                    let pos = is_volume_rider
953                        .and_then(|is_volume_rider| match is_volume_rider.pos.kind {
954                            Volume::Terrain => None,
955                            Volume::Entity(e) => {
956                                let e = self.state.ecs().entity_from_uid(e)?;
957                                let pos = self
958                                    .state
959                                    .ecs()
960                                    .read_storage::<comp::Pos>()
961                                    .get(e)
962                                    .copied()?;
963
964                                Some(pos.0)
965                            },
966                        })
967                        .unwrap_or(pos.0);
968                    let chunk_key = terrain.pos_key(pos.map(|e| e.floor() as i32));
969                    match anchor {
970                        Some(Anchor::Chunk(hc)) => {
971                            // Check if both this chunk and the NPCs `home_chunk` is unloaded. If
972                            // so, we delete them. We check for
973                            // `home_chunk` in order to avoid duplicating
974                            // the entity under some circumstances.
975                            terrain.get_key_real(chunk_key).is_none()
976                                && terrain.get_key_real(*hc).is_none()
977                        },
978                        Some(Anchor::Entity(entity)) => !self.state.ecs().is_alive(*entity),
979                        None => terrain.get_key_real(chunk_key).is_none(),
980                    }
981                })
982                .map(|(entity, _, _, _, _)| entity)
983                .collect::<Vec<_>>()
984        };
985
986        #[cfg(feature = "worldgen")]
987        {
988            let mut rtsim = self.state.ecs().write_resource::<rtsim::RtSim>();
989            let rtsim_entities = self.state.ecs().read_storage();
990            for entity in &to_delete {
991                if let Some(rtsim_entity) = rtsim_entities.get(*entity) {
992                    rtsim.hook_rtsim_entity_unload(*rtsim_entity);
993                }
994            }
995        }
996
997        // Actually perform entity deletion
998        for entity in to_delete {
999            if let Err(e) = self.state.delete_entity_recorded(entity) {
1000                error!(?e, "Failed to delete agent outside the terrain");
1001            }
1002        }
1003
1004        if let Some(DisconnectType::WithoutPersistence) = disconnect_type {
1005            info!(
1006                "Disconnection of all players without persistence complete, signalling to \
1007                 persistence thread that character updates may continue to be processed"
1008            );
1009            self.state
1010                .ecs()
1011                .fetch_mut::<CharacterUpdater>()
1012                .disconnected_success();
1013        }
1014
1015        // 7 Persistence updates
1016        let before_persistence_updates = Instant::now();
1017
1018        let character_loader = self.state.ecs().read_resource::<CharacterLoader>();
1019
1020        let mut character_updater = self.state.ecs().write_resource::<CharacterUpdater>();
1021        let updater_messages: Vec<CharacterUpdaterMessage> = character_updater.messages().collect();
1022
1023        // Get character-related database responses and notify the requesting client
1024        character_loader
1025            .messages()
1026            .chain(updater_messages)
1027            .for_each(|message| match message {
1028                CharacterUpdaterMessage::DatabaseBatchCompletion(batch_id) => {
1029                    character_updater.process_batch_completion(batch_id);
1030                },
1031                CharacterUpdaterMessage::CharacterScreenResponse(response) => {
1032                    match response.response_kind {
1033                        CharacterScreenResponseKind::CharacterList(result) => match result {
1034                            Ok(mut character_list_data) => {
1035                                self.parse_locations(&mut character_list_data);
1036                                self.notify_client(
1037                                    response.target_entity,
1038                                    ServerGeneral::CharacterListUpdate(character_list_data),
1039                                )
1040                            },
1041                            Err(error) => self.notify_client(
1042                                response.target_entity,
1043                                ServerGeneral::CharacterActionError(error.to_string()),
1044                            ),
1045                        },
1046                        CharacterScreenResponseKind::CharacterCreation(result) => match result {
1047                            Ok((character_id, mut list)) => {
1048                                self.parse_locations(&mut list);
1049                                self.notify_client(
1050                                    response.target_entity,
1051                                    ServerGeneral::CharacterListUpdate(list),
1052                                );
1053                                self.notify_client(
1054                                    response.target_entity,
1055                                    ServerGeneral::CharacterCreated(character_id),
1056                                );
1057                            },
1058                            Err(error) => self.notify_client(
1059                                response.target_entity,
1060                                ServerGeneral::CharacterActionError(error.to_string()),
1061                            ),
1062                        },
1063                        CharacterScreenResponseKind::CharacterEdit(result) => match result {
1064                            Ok((character_id, mut list)) => {
1065                                self.parse_locations(&mut list);
1066                                self.notify_client(
1067                                    response.target_entity,
1068                                    ServerGeneral::CharacterListUpdate(list),
1069                                );
1070                                self.notify_client(
1071                                    response.target_entity,
1072                                    ServerGeneral::CharacterEdited(character_id),
1073                                );
1074                            },
1075                            Err(error) => self.notify_client(
1076                                response.target_entity,
1077                                ServerGeneral::CharacterActionError(error.to_string()),
1078                            ),
1079                        },
1080                        CharacterScreenResponseKind::CharacterData(result) => {
1081                            match *result {
1082                                Ok((character_data, skill_set_persistence_load_error)) => {
1083                                    let PersistedComponents {
1084                                        body,
1085                                        hardcore,
1086                                        stats,
1087                                        skill_set,
1088                                        inventory,
1089                                        waypoint,
1090                                        pets,
1091                                        active_abilities,
1092                                        map_marker,
1093                                    } = character_data;
1094                                    let character_data = (
1095                                        body,
1096                                        hardcore,
1097                                        stats,
1098                                        skill_set,
1099                                        inventory,
1100                                        waypoint,
1101                                        pets,
1102                                        active_abilities,
1103                                        map_marker,
1104                                    );
1105                                    // TODO: Does this need to be a server event? E.g. we could
1106                                    // just handle it here.
1107                                    self.state.emit_event_now(UpdateCharacterDataEvent {
1108                                        entity: response.target_entity,
1109                                        components: character_data,
1110                                        metadata: skill_set_persistence_load_error,
1111                                    })
1112                                },
1113                                Err(error) => {
1114                                    // We failed to load data for the character from the DB. Notify
1115                                    // the client to push the state back to character selection,
1116                                    // with the error to display
1117                                    self.notify_client(
1118                                        response.target_entity,
1119                                        ServerGeneral::CharacterDataLoadResult(Err(
1120                                            error.to_string()
1121                                        )),
1122                                    );
1123
1124                                    // Clean up the entity data on the server
1125                                    self.state.emit_event_now(ExitIngameEvent {
1126                                        entity: response.target_entity,
1127                                    })
1128                                },
1129                            }
1130                        },
1131                    }
1132                },
1133            });
1134
1135        drop(character_loader);
1136        drop(character_updater);
1137
1138        {
1139            // Check for new chunks; cancel and regenerate all chunks if the asset has been
1140            // reloaded. Note that all of these assignments are no-ops, so the
1141            // only work we do here on the fast path is perform a relaxed read on an atomic.
1142            // boolean.
1143            let index = &mut self.index;
1144            let world = &mut self.world;
1145            let ecs = self.state.ecs_mut();
1146            let slow_jobs = ecs.write_resource::<SlowJobPool>();
1147
1148            index.reload_if_changed(|index| {
1149                let mut chunk_generator = ecs.write_resource::<ChunkGenerator>();
1150                let client = ecs.read_storage::<Client>();
1151                let mut terrain = ecs.write_resource::<common::terrain::TerrainGrid>();
1152                #[cfg(feature = "worldgen")]
1153                let rtsim = ecs.read_resource::<rtsim::RtSim>();
1154                #[cfg(not(feature = "worldgen"))]
1155                let rtsim = ();
1156
1157                // Cancel all pending chunks.
1158                chunk_generator.cancel_all();
1159
1160                if client.is_empty() {
1161                    // No clients, so just clear all terrain.
1162                    terrain.clear();
1163                } else {
1164                    // There's at least one client, so regenerate all chunks.
1165                    terrain.iter().for_each(|(pos, _)| {
1166                        chunk_generator.generate_chunk(
1167                            None,
1168                            pos,
1169                            &slow_jobs,
1170                            Arc::clone(world),
1171                            &rtsim,
1172                            index.clone(),
1173                            (
1174                                *ecs.read_resource::<TimeOfDay>(),
1175                                (*ecs.read_resource::<Calendar>()).clone(),
1176                            ),
1177                        );
1178                    });
1179                }
1180            });
1181        }
1182
1183        let end_of_server_tick = Instant::now();
1184
1185        // 8) Update Metrics
1186        run_now::<sys::metrics::Sys>(self.state.ecs());
1187
1188        {
1189            // Report timing info
1190            let tick_metrics = self.state.ecs().read_resource::<TickMetrics>();
1191
1192            let tt = &tick_metrics.tick_time;
1193            tt.with_label_values(&["new connections"])
1194                .set((before_state_tick - before_new_connections).as_nanos() as i64);
1195            tt.with_label_values(&["handle server events"])
1196                .set((before_update_terrain_and_regions - before_handle_events).as_nanos() as i64);
1197            tt.with_label_values(&["update terrain and region map"])
1198                .set((before_sync - before_update_terrain_and_regions).as_nanos() as i64);
1199            tt.with_label_values(&["state"])
1200                .set((before_handle_events - before_state_tick).as_nanos() as i64);
1201            tt.with_label_values(&["world tick"])
1202                .set((before_entity_cleanup - before_world_tick).as_nanos() as i64);
1203            tt.with_label_values(&["entity cleanup"])
1204                .set((before_persistence_updates - before_entity_cleanup).as_nanos() as i64);
1205            tt.with_label_values(&["persistence_updates"])
1206                .set((end_of_server_tick - before_persistence_updates).as_nanos() as i64);
1207            for (label, duration) in state_tick_metrics.timings {
1208                tick_metrics
1209                    .state_tick_time
1210                    .with_label_values(&[label])
1211                    .set(duration.as_nanos() as i64);
1212            }
1213            tick_metrics.tick_time_hist.observe(
1214                end_of_server_tick
1215                    .duration_since(before_state_tick)
1216                    .as_secs_f64(),
1217            );
1218        }
1219
1220        // 9) Finish the tick, pass control back to the frontend.
1221
1222        Ok(frontend_events)
1223    }
1224
1225    /// Clean up the server after a tick.
1226    pub fn cleanup(&mut self) {
1227        // Cleanup the local state
1228        self.state.cleanup();
1229
1230        // Maintain persisted terrain
1231        #[cfg(feature = "persistent_world")]
1232        self.state
1233            .ecs()
1234            .try_fetch_mut::<TerrainPersistence>()
1235            .map(|mut t| t.maintain());
1236    }
1237
1238    // Run RegionMap tick to update entity region occupancy
1239    fn update_region_map(&mut self) {
1240        prof_span!("Server::update_region_map");
1241        let ecs = self.state().ecs();
1242        ecs.write_resource::<RegionMap>().tick(
1243            ecs.read_storage::<comp::Pos>(),
1244            ecs.read_storage::<comp::Vel>(),
1245            ecs.read_storage::<comp::Presence>(),
1246            ecs.entities(),
1247        );
1248    }
1249
1250    fn initialize_client(&mut self, client: connection_handler::IncomingClient) -> Entity {
1251        let entity = self
1252            .state
1253            .ecs_mut()
1254            .create_entity_synced()
1255            .with(client)
1256            .build();
1257        self.state
1258            .ecs()
1259            .read_resource::<metrics::PlayerMetrics>()
1260            .clients_connected
1261            .inc();
1262        entity
1263    }
1264
1265    /// Disconnects all clients if requested by either an admin command or
1266    /// due to a persistence transaction failure and returns the processed
1267    /// DisconnectionType
1268    fn disconnect_all_clients_if_requested(&mut self) -> Option<DisconnectType> {
1269        let mut character_updater = self.state.ecs().fetch_mut::<CharacterUpdater>();
1270
1271        let disconnect_type = self.get_disconnect_all_clients_requested(&mut character_updater);
1272        if let Some(disconnect_type) = disconnect_type {
1273            let with_persistence = disconnect_type == DisconnectType::WithPersistence;
1274            let clients = self.state.ecs().read_storage::<Client>();
1275            let entities = self.state.ecs().entities();
1276
1277            info!(
1278                "Disconnecting all clients ({} persistence) as requested",
1279                if with_persistence { "with" } else { "without" }
1280            );
1281            for (_, entity) in (&clients, &entities).join() {
1282                info!("Emitting client disconnect event for entity: {:?}", entity);
1283                if with_persistence {
1284                    self.state.emit_event_now(ClientDisconnectEvent(
1285                        entity,
1286                        comp::DisconnectReason::Kicked,
1287                    ))
1288                } else {
1289                    self.state
1290                        .emit_event_now(ClientDisconnectWithoutPersistenceEvent(entity))
1291                };
1292            }
1293
1294            self.disconnect_all_clients_requested = false;
1295        }
1296
1297        disconnect_type
1298    }
1299
1300    fn get_disconnect_all_clients_requested(
1301        &self,
1302        character_updater: &mut CharacterUpdater,
1303    ) -> Option<DisconnectType> {
1304        let without_persistence_requested = character_updater.disconnect_all_clients_requested();
1305        let with_persistence_requested = self.disconnect_all_clients_requested;
1306
1307        if without_persistence_requested {
1308            return Some(DisconnectType::WithoutPersistence);
1309        };
1310        if with_persistence_requested {
1311            return Some(DisconnectType::WithPersistence);
1312        };
1313        None
1314    }
1315
1316    /// Handle new client connections.
1317    fn handle_new_connections(&mut self, frontend_events: &mut Vec<Event>) {
1318        while let Ok(sender) = self.connection_handler.info_requester_receiver.try_recv() {
1319            // can fail, e.g. due to timeout or network prob.
1320            trace!("sending info to connection_handler");
1321            let _ = sender.send(connection_handler::ServerInfoPacket {
1322                info: self.get_server_info(),
1323                time: self.state.get_time(),
1324            });
1325        }
1326
1327        while let Ok(incoming) = self.connection_handler.client_receiver.try_recv() {
1328            let entity = self.initialize_client(incoming);
1329            frontend_events.push(Event::ClientConnected { entity });
1330        }
1331    }
1332
1333    pub fn notify_client<S>(&self, entity: EcsEntity, msg: S)
1334    where
1335        S: Into<ServerMsg>,
1336    {
1337        if let Some(client) = self.state.ecs().read_storage::<Client>().get(entity) {
1338            client.send_fallible(msg);
1339        }
1340    }
1341
1342    pub fn notify_players(&mut self, msg: ServerGeneral) { self.state.notify_players(msg); }
1343
1344    pub fn generate_chunk(&mut self, entity: EcsEntity, key: Vec2<i32>) {
1345        let ecs = self.state.ecs();
1346        let slow_jobs = ecs.read_resource::<SlowJobPool>();
1347        #[cfg(feature = "worldgen")]
1348        let rtsim = ecs.read_resource::<rtsim::RtSim>();
1349        #[cfg(not(feature = "worldgen"))]
1350        let rtsim = ();
1351        ecs.write_resource::<ChunkGenerator>().generate_chunk(
1352            Some(entity),
1353            key,
1354            &slow_jobs,
1355            Arc::clone(&self.world),
1356            &rtsim,
1357            self.index.clone(),
1358            (
1359                *ecs.read_resource::<TimeOfDay>(),
1360                (*ecs.read_resource::<Calendar>()).clone(),
1361            ),
1362        );
1363    }
1364
1365    fn process_command(&mut self, entity: EcsEntity, name: String, args: Vec<String>) {
1366        // Find the command object and run its handler.
1367        if let Ok(command) = name.parse::<ServerChatCommand>() {
1368            command.execute(self, entity, args);
1369        } else {
1370            #[cfg(feature = "plugins")]
1371            {
1372                let mut plugin_manager = self.state.ecs().write_resource::<PluginMgr>();
1373                let ecs_world = EcsWorld {
1374                    entities: &self.state.ecs().entities(),
1375                    health: self.state.ecs().read_component().into(),
1376                    uid: self.state.ecs().read_component().into(),
1377                    id_maps: &self.state.ecs().read_resource::<IdMaps>().into(),
1378                    player: self.state.ecs().read_component().into(),
1379                };
1380                let uid = if let Some(uid) = ecs_world.uid.get(entity).copied() {
1381                    uid
1382                } else {
1383                    self.notify_client(
1384                        entity,
1385                        ServerGeneral::server_msg(
1386                            comp::ChatType::CommandError,
1387                            common::comp::Content::Plain(
1388                                "Can't get player UUID (player may be disconnected?)".to_string(),
1389                            ),
1390                        ),
1391                    );
1392                    return;
1393                };
1394                match plugin_manager.command_event(&ecs_world, &name, args.as_slice(), uid) {
1395                    Err(common_state::plugin::CommandResults::UnknownCommand) => self
1396                        .notify_client(
1397                            entity,
1398                            ServerGeneral::server_msg(
1399                                comp::ChatType::CommandError,
1400                                common::comp::Content::Plain(format!(
1401                                    "Unknown command '/{name}'.\nType '/help' for available \
1402                                     commands",
1403                                )),
1404                            ),
1405                        ),
1406                    Ok(value) => {
1407                        self.notify_client(
1408                            entity,
1409                            ServerGeneral::server_msg(
1410                                comp::ChatType::CommandInfo,
1411                                common::comp::Content::Plain(value.join("\n")),
1412                            ),
1413                        );
1414                    },
1415                    Err(common_state::plugin::CommandResults::PluginError(err)) => {
1416                        self.notify_client(
1417                            entity,
1418                            ServerGeneral::server_msg(
1419                                comp::ChatType::CommandError,
1420                                common::comp::Content::Plain(format!(
1421                                    "Error occurred while executing command '/{name}'.\n{err}"
1422                                )),
1423                            ),
1424                        );
1425                    },
1426                    Err(common_state::plugin::CommandResults::HostError(err)) => {
1427                        error!(?err, ?name, ?args, "Can't execute command");
1428                        self.notify_client(
1429                            entity,
1430                            ServerGeneral::server_msg(
1431                                comp::ChatType::CommandError,
1432                                common::comp::Content::Plain(format!(
1433                                    "Internal error {err:?} while executing '/{name}'.\nContact \
1434                                     the server administrator",
1435                                )),
1436                            ),
1437                        );
1438                    },
1439                }
1440            }
1441        }
1442    }
1443
1444    fn entity_admin_role(&self, entity: EcsEntity) -> Option<comp::AdminRole> {
1445        self.state
1446            .read_component_copied::<comp::Admin>(entity)
1447            .map(|admin| admin.0)
1448    }
1449
1450    pub fn number_of_players(&self) -> i64 {
1451        self.state.ecs().read_storage::<Client>().join().count() as i64
1452    }
1453
1454    /// NOTE: Do *not* allow this to be called from any command that doesn't go
1455    /// through the CLI!
1456    pub fn add_admin(&mut self, username: &str, role: comp::AdminRole) {
1457        let mut editable_settings = self.editable_settings_mut();
1458        let login_provider = self.state.ecs().fetch::<LoginProvider>();
1459        let data_dir = self.data_dir();
1460        if let Some(entity) = add_admin(
1461            username,
1462            role,
1463            &login_provider,
1464            &mut editable_settings,
1465            &data_dir.path,
1466        )
1467        .and_then(|uuid| {
1468            let state = &self.state;
1469            (
1470                &state.ecs().entities(),
1471                &state.read_storage::<comp::Player>(),
1472            )
1473                .join()
1474                .find(|(_, player)| player.uuid() == uuid)
1475                .map(|(e, _)| e)
1476        }) {
1477            drop((data_dir, login_provider, editable_settings));
1478            // Add admin component if the player is ingame; if they are not, we can ignore
1479            // the write failure.
1480            self.state
1481                .write_component_ignore_entity_dead(entity, comp::Admin(role));
1482        };
1483    }
1484
1485    /// NOTE: Do *not* allow this to be called from any command that doesn't go
1486    /// through the CLI!
1487    pub fn remove_admin(&self, username: &str) {
1488        let mut editable_settings = self.editable_settings_mut();
1489        let login_provider = self.state.ecs().fetch::<LoginProvider>();
1490        let data_dir = self.data_dir();
1491        if let Some(entity) = remove_admin(
1492            username,
1493            &login_provider,
1494            &mut editable_settings,
1495            &data_dir.path,
1496        )
1497        .and_then(|uuid| {
1498            let state = &self.state;
1499            (
1500                &state.ecs().entities(),
1501                &state.read_storage::<comp::Player>(),
1502            )
1503                .join()
1504                .find(|(_, player)| player.uuid() == uuid)
1505                .map(|(e, _)| e)
1506        }) {
1507            // Remove admin component if the player is ingame
1508            self.state
1509                .ecs()
1510                .write_storage::<comp::Admin>()
1511                .remove(entity);
1512        };
1513    }
1514
1515    /// Useful for testing without a client
1516    /// view_distance: distance in chunks that are persisted, this acts like the
1517    /// player view distance so it is actually a bit farther due to a buffer
1518    /// zone
1519    #[cfg(feature = "worldgen")]
1520    pub fn create_centered_persister(&mut self, view_distance: u32) {
1521        let world_dims_chunks = self.world.sim().get_size();
1522        let world_dims_blocks = TerrainChunkSize::blocks(world_dims_chunks);
1523        // NOTE: origin is in the corner of the map
1524        // TODO: extend this function to have picking a random position or specifying a
1525        // position as options
1526        //let mut rng = rand::thread_rng();
1527        // // Pick a random position but not to close to the edge
1528        // let rand_pos = world_dims_blocks.map(|e| e as i32).map(|e| e / 2 +
1529        // rng.gen_range(-e/2..e/2 + 1));
1530        let pos = comp::Pos(Vec3::from(world_dims_blocks.map(|e| e as f32 / 2.0)));
1531        self.state
1532            .create_persister(pos, view_distance, &self.world, &self.index)
1533            .build();
1534    }
1535
1536    /// Used by benchmarking code.
1537    pub fn chunks_pending(&mut self) -> bool {
1538        self.state_mut()
1539            .mut_resource::<ChunkGenerator>()
1540            .pending_chunks()
1541            .next()
1542            .is_some()
1543    }
1544
1545    /// Sets the SQL log mode at runtime
1546    pub fn set_sql_log_mode(&mut self, sql_log_mode: SqlLogMode) {
1547        // Unwrap is safe here because we only perform a variable assignment with the
1548        // RwLock taken meaning that no panic can occur that would cause the
1549        // RwLock to become poisoned. This justification also means that calling
1550        // unwrap() on the associated read() calls for this RwLock is also safe
1551        // as long as no code that can panic is introduced here.
1552        let mut database_settings = self.database_settings.write().unwrap();
1553        database_settings.sql_log_mode = sql_log_mode;
1554        // Drop the RwLockWriteGuard to avoid performing unnecessary actions (logging)
1555        // with the lock taken.
1556        drop(database_settings);
1557        info!("SQL log mode changed to {:?}", sql_log_mode);
1558    }
1559
1560    pub fn disconnect_all_clients(&mut self) {
1561        info!("Disconnecting all clients due to local console command");
1562        self.disconnect_all_clients_requested = true;
1563    }
1564
1565    /// Sends the given client a message with their current battle mode and
1566    /// whether they can change it.
1567    ///
1568    /// This function expects the `EcsEntity` to represent a player, otherwise
1569    /// it will log an error.
1570    pub fn get_battle_mode_for(&mut self, client: EcsEntity) {
1571        let ecs = self.state.ecs();
1572        let time = ecs.read_resource::<Time>();
1573        let settings = ecs.read_resource::<Settings>();
1574        let players = ecs.read_storage::<comp::Player>();
1575        let get_player_result = players.get(client).ok_or_else(|| {
1576            error!("Can't get player component for client.");
1577
1578            Content::Plain("Can't get player component for client.".to_string())
1579        });
1580        let player = match get_player_result {
1581            Ok(player) => player,
1582            Err(content) => {
1583                self.notify_client(
1584                    client,
1585                    ServerGeneral::server_msg(ChatType::CommandError, content),
1586                );
1587                return;
1588            },
1589        };
1590
1591        let mut msg = format!("Current battle mode: {:?}.", player.battle_mode);
1592
1593        if settings.gameplay.battle_mode.allow_choosing() {
1594            msg.push_str(" Possible to change.");
1595        } else {
1596            msg.push_str(" Global.");
1597        }
1598
1599        if let Some(change) = player.last_battlemode_change {
1600            let Time(time) = *time;
1601            let Time(change) = change;
1602            let elapsed = time - change;
1603            let next = BATTLE_MODE_COOLDOWN - elapsed;
1604
1605            if next > 0.0 {
1606                let notice = format!(" Next change will be available in: {:.0} seconds", next);
1607                msg.push_str(&notice);
1608            }
1609        }
1610
1611        self.notify_client(
1612            client,
1613            ServerGeneral::server_msg(ChatType::CommandInfo, Content::Plain(msg)),
1614        );
1615    }
1616
1617    /// Sets the battle mode for the given client or informs them if they are
1618    /// not allowed to change it.
1619    ///
1620    /// This function expects the `EcsEntity` to represent a player, otherwise
1621    /// it will log an error.
1622    pub fn set_battle_mode_for(&mut self, client: EcsEntity, battle_mode: BattleMode) {
1623        let ecs = self.state.ecs();
1624        let time = ecs.read_resource::<Time>();
1625        let settings = ecs.read_resource::<Settings>();
1626
1627        if !settings.gameplay.battle_mode.allow_choosing() {
1628            self.notify_client(
1629                client,
1630                ServerGeneral::server_msg(
1631                    ChatType::CommandInfo,
1632                    Content::localized("command-disabled-by-settings"),
1633                ),
1634            );
1635
1636            return;
1637        }
1638
1639        #[cfg(feature = "worldgen")]
1640        let in_town = {
1641            let pos = if let Some(pos) = self
1642                .state
1643                .ecs()
1644                .read_storage::<comp::Pos>()
1645                .get(client)
1646                .copied()
1647            {
1648                pos
1649            } else {
1650                self.notify_client(
1651                    client,
1652                    ServerGeneral::server_msg(
1653                        ChatType::CommandInfo,
1654                        Content::localized_with_args("command-position-unavailable", [(
1655                            "target", "target",
1656                        )]),
1657                    ),
1658                );
1659
1660                return;
1661            };
1662
1663            let wpos = pos.0.xy().map(|x| x as i32);
1664            let chunk_pos = wpos.wpos_to_cpos();
1665            self.world.civs().sites().any(|site| {
1666                // empirical
1667                const RADIUS: f32 = 9.0;
1668                let delta = site
1669                    .center
1670                    .map(|x| x as f32)
1671                    .distance(chunk_pos.map(|x| x as f32));
1672                delta < RADIUS
1673            })
1674        };
1675
1676        #[cfg(not(feature = "worldgen"))]
1677        let in_town = true;
1678
1679        if !in_town {
1680            self.notify_client(
1681                client,
1682                ServerGeneral::server_msg(
1683                    ChatType::CommandInfo,
1684                    Content::localized("command-battlemode-intown"),
1685                ),
1686            );
1687
1688            return;
1689        }
1690
1691        let mut players = ecs.write_storage::<comp::Player>();
1692        let mut player = if let Some(info) = players.get_mut(client) {
1693            info
1694        } else {
1695            error!("Failed to get info for player.");
1696
1697            return;
1698        };
1699
1700        if let Some(Time(last_change)) = player.last_battlemode_change {
1701            let Time(time) = *time;
1702            let elapsed = time - last_change;
1703            if elapsed < BATTLE_MODE_COOLDOWN {
1704                let next = BATTLE_MODE_COOLDOWN - elapsed;
1705
1706                self.notify_client(
1707                    client,
1708                    ServerGeneral::server_msg(
1709                        ChatType::CommandInfo,
1710                        Content::Plain(format!(
1711                            "Next change will be available in {next:.0} seconds."
1712                        )),
1713                    ),
1714                );
1715
1716                return;
1717            }
1718        }
1719
1720        if player.battle_mode == battle_mode {
1721            self.notify_client(
1722                client,
1723                ServerGeneral::server_msg(
1724                    ChatType::CommandInfo,
1725                    Content::localized("command-battlemode-same"),
1726                ),
1727            );
1728
1729            return;
1730        }
1731
1732        player.battle_mode = battle_mode;
1733        player.last_battlemode_change = Some(*time);
1734
1735        self.notify_client(
1736            client,
1737            ServerGeneral::server_msg(
1738                ChatType::CommandInfo,
1739                Content::localized_with_args("command-battlemode-updated", [(
1740                    "battlemode",
1741                    format!("{battle_mode:?}"),
1742                )]),
1743            ),
1744        );
1745
1746        drop(players);
1747
1748        let uid = ecs.read_storage::<Uid>().get(client).copied().unwrap();
1749
1750        self.state().notify_players(ServerGeneral::PlayerListUpdate(
1751            PlayerListUpdate::UpdateBattleMode(uid, battle_mode),
1752        ));
1753    }
1754}
1755
1756impl Drop for Server {
1757    fn drop(&mut self) {
1758        self.state
1759            .notify_players(ServerGeneral::Disconnect(DisconnectReason::Shutdown));
1760
1761        #[cfg(feature = "persistent_world")]
1762        self.state
1763            .ecs()
1764            .try_fetch_mut::<TerrainPersistence>()
1765            .map(|mut terrain_persistence| {
1766                info!("Unloading terrain persistence...");
1767                terrain_persistence.unload_all()
1768            });
1769
1770        #[cfg(feature = "worldgen")]
1771        {
1772            debug!("Saving rtsim state...");
1773            self.state.ecs().write_resource::<rtsim::RtSim>().save(true);
1774        }
1775    }
1776}
1777
1778#[must_use]
1779pub fn handle_edit<T, S: settings::EditableSetting>(
1780    data: T,
1781    result: Option<(String, Result<(), settings::SettingError<S>>)>,
1782) -> Option<T> {
1783    use crate::settings::SettingError;
1784    let (info, result) = result?;
1785    match result {
1786        Ok(()) => {
1787            info!("{}", info);
1788            Some(data)
1789        },
1790        Err(SettingError::Io(err)) => {
1791            warn!(
1792                ?err,
1793                "Failed to write settings file to disk, but succeeded in memory (success message: \
1794                 {})",
1795                info,
1796            );
1797            Some(data)
1798        },
1799        Err(SettingError::Integrity(err)) => {
1800            error!(?err, "Encountered an error while validating the request",);
1801            None
1802        },
1803    }
1804}
1805
1806/// If successful returns the Some(uuid) of the added admin
1807///
1808/// NOTE: Do *not* allow this to be called from any command that doesn't go
1809/// through the CLI!
1810#[must_use]
1811pub fn add_admin(
1812    username: &str,
1813    role: comp::AdminRole,
1814    login_provider: &LoginProvider,
1815    editable_settings: &mut EditableSettings,
1816    data_dir: &std::path::Path,
1817) -> Option<common::uuid::Uuid> {
1818    use crate::settings::EditableSetting;
1819    let role_ = role.into();
1820    match login_provider.username_to_uuid(username) {
1821        Ok(uuid) => handle_edit(
1822            uuid,
1823            editable_settings.admins.edit(data_dir, |admins| {
1824                match admins.insert(uuid, settings::AdminRecord {
1825                    username_when_admined: Some(username.into()),
1826                    date: chrono::Utc::now(),
1827                    role: role_,
1828                }) {
1829                    None => Some(format!(
1830                        "Successfully added {} ({}) as {:?}!",
1831                        username, uuid, role
1832                    )),
1833                    Some(old_admin) if old_admin.role == role_ => {
1834                        info!("{} ({}) already has role: {:?}!", username, uuid, role);
1835                        None
1836                    },
1837                    Some(old_admin) => Some(format!(
1838                        "{} ({}) role changed from {:?} to {:?}!",
1839                        username, uuid, old_admin.role, role
1840                    )),
1841                }
1842            }),
1843        ),
1844        Err(err) => {
1845            error!(
1846                ?err,
1847                "Could not find uuid for this name; either the user does not exist or there was \
1848                 an error communicating with the auth server."
1849            );
1850            None
1851        },
1852    }
1853}
1854
1855/// If successful returns the Some(uuid) of the removed admin
1856///
1857/// NOTE: Do *not* allow this to be called from any command that doesn't go
1858/// through the CLI!
1859#[must_use]
1860pub fn remove_admin(
1861    username: &str,
1862    login_provider: &LoginProvider,
1863    editable_settings: &mut EditableSettings,
1864    data_dir: &std::path::Path,
1865) -> Option<common::uuid::Uuid> {
1866    use crate::settings::EditableSetting;
1867    match login_provider.username_to_uuid(username) {
1868        Ok(uuid) => handle_edit(
1869            uuid,
1870            editable_settings.admins.edit(data_dir, |admins| {
1871                if let Some(admin) = admins.remove(&uuid) {
1872                    Some(format!(
1873                        "Successfully removed {} ({}) with role {:?} from the admins list",
1874                        username, uuid, admin.role,
1875                    ))
1876                } else {
1877                    info!("{} ({}) is not an admin!", username, uuid);
1878                    None
1879                }
1880            }),
1881        ),
1882        Err(err) => {
1883            error!(
1884                ?err,
1885                "Could not find uuid for this name; either the user does not exist or there was \
1886                 an error communicating with the auth server."
1887            );
1888            None
1889        },
1890    }
1891}