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            for chunk in &self.state.terrain_changes().removed_chunks {
893                rtsim.hook_unload_chunk(*chunk);
894            }
895        }
896
897        // Prevent anchor entity chains which are not currently supported due to:
898        // * potential cycles?
899        // * unloading a chain could occur across an unbounded number of ticks with the
900        //   current implementation.
901        // * in particular, we want to be able to unload all entities in a
902        //   limited number of ticks when a database error occurs and kicks all
903        //   players (not quiet sure on exact time frame, since it already
904        //   takes a tick after unloading all chunks for entities to despawn?),
905        //   see this thread and the discussion linked from there:
906        //   https://gitlab.com/veloren/veloren/-/merge_requests/2668#note_634913847
907        let anchors = self.state.ecs().read_storage::<Anchor>();
908        let anchored_anchor_entities: Vec<Entity> = (
909            &self.state.ecs().entities(),
910            &self.state.ecs().read_storage::<Anchor>(),
911        )
912            .join()
913            .filter_map(|(_, anchor)| match anchor {
914                Anchor::Entity(anchor_entity) => Some(*anchor_entity),
915                _ => None,
916            })
917            // We allow Anchor::Entity(_) -> Anchor::Chunk(_) connections, since they can't chain further.
918            //
919            // NOTE: The entity with `Anchor::Entity` will unload one tick after the entity with `Anchor::Chunk`.
920            .filter(|anchor_entity| match anchors.get(*anchor_entity) {
921                Some(Anchor::Entity(_)) => true,
922                Some(Anchor::Chunk(_)) | None => false
923            })
924            .collect();
925        drop(anchors);
926
927        for entity in anchored_anchor_entities {
928            if cfg!(debug_assertions) {
929                panic!("Entity anchor chain detected");
930            }
931            error!(
932                "Detected an anchor entity that itself has an anchor entity - anchor chains are \
933                 not currently supported. The entity's Anchor component has been deleted"
934            );
935            self.state.delete_component::<Anchor>(entity);
936        }
937
938        // Remove NPCs that are outside the view distances of all players
939        // This is done by removing NPCs in unloaded chunks
940        let to_delete = {
941            let terrain = self.state.terrain();
942            (
943                &self.state.ecs().entities(),
944                &self.state.ecs().read_storage::<comp::Pos>(),
945                !&self.state.ecs().read_storage::<comp::Presence>(),
946                self.state.ecs().read_storage::<Anchor>().maybe(),
947                self.state.ecs().read_storage::<Is<VolumeRider>>().maybe(),
948            )
949                .join()
950                .filter(|(_, pos, _, anchor, is_volume_rider)| {
951                    let pos = is_volume_rider
952                        .and_then(|is_volume_rider| match is_volume_rider.pos.kind {
953                            Volume::Terrain => None,
954                            Volume::Entity(e) => {
955                                let e = self.state.ecs().entity_from_uid(e)?;
956                                let pos = self
957                                    .state
958                                    .ecs()
959                                    .read_storage::<comp::Pos>()
960                                    .get(e)
961                                    .copied()?;
962
963                                Some(pos.0)
964                            },
965                        })
966                        .unwrap_or(pos.0);
967                    let chunk_key = terrain.pos_key(pos.map(|e| e.floor() as i32));
968                    match anchor {
969                        Some(Anchor::Chunk(hc)) => {
970                            // Check if both this chunk and the NPCs `home_chunk` is unloaded. If
971                            // so, we delete them. We check for
972                            // `home_chunk` in order to avoid duplicating
973                            // the entity under some circumstances.
974                            terrain.get_key_real(chunk_key).is_none()
975                                && terrain.get_key_real(*hc).is_none()
976                        },
977                        Some(Anchor::Entity(entity)) => !self.state.ecs().is_alive(*entity),
978                        None => terrain.get_key_real(chunk_key).is_none(),
979                    }
980                })
981                .map(|(entity, _, _, _, _)| entity)
982                .collect::<Vec<_>>()
983        };
984
985        #[cfg(feature = "worldgen")]
986        {
987            let mut rtsim = self.state.ecs().write_resource::<rtsim::RtSim>();
988            let rtsim_entities = self.state.ecs().read_storage();
989            for entity in &to_delete {
990                if let Some(rtsim_entity) = rtsim_entities.get(*entity) {
991                    rtsim.hook_rtsim_entity_unload(*rtsim_entity);
992                }
993            }
994        }
995
996        // Actually perform entity deletion
997        for entity in to_delete {
998            if let Err(e) = self.state.delete_entity_recorded(entity) {
999                error!(?e, "Failed to delete agent outside the terrain");
1000            }
1001        }
1002
1003        if let Some(DisconnectType::WithoutPersistence) = disconnect_type {
1004            info!(
1005                "Disconnection of all players without persistence complete, signalling to \
1006                 persistence thread that character updates may continue to be processed"
1007            );
1008            self.state
1009                .ecs()
1010                .fetch_mut::<CharacterUpdater>()
1011                .disconnected_success();
1012        }
1013
1014        // 7 Persistence updates
1015        let before_persistence_updates = Instant::now();
1016
1017        let character_loader = self.state.ecs().read_resource::<CharacterLoader>();
1018
1019        let mut character_updater = self.state.ecs().write_resource::<CharacterUpdater>();
1020        let updater_messages: Vec<CharacterUpdaterMessage> = character_updater.messages().collect();
1021
1022        // Get character-related database responses and notify the requesting client
1023        character_loader
1024            .messages()
1025            .chain(updater_messages)
1026            .for_each(|message| match message {
1027                CharacterUpdaterMessage::DatabaseBatchCompletion(batch_id) => {
1028                    character_updater.process_batch_completion(batch_id);
1029                },
1030                CharacterUpdaterMessage::CharacterScreenResponse(response) => {
1031                    match response.response_kind {
1032                        CharacterScreenResponseKind::CharacterList(result) => match result {
1033                            Ok(mut character_list_data) => {
1034                                self.parse_locations(&mut character_list_data);
1035                                self.notify_client(
1036                                    response.target_entity,
1037                                    ServerGeneral::CharacterListUpdate(character_list_data),
1038                                )
1039                            },
1040                            Err(error) => self.notify_client(
1041                                response.target_entity,
1042                                ServerGeneral::CharacterActionError(error.to_string()),
1043                            ),
1044                        },
1045                        CharacterScreenResponseKind::CharacterCreation(result) => match result {
1046                            Ok((character_id, mut list)) => {
1047                                self.parse_locations(&mut list);
1048                                self.notify_client(
1049                                    response.target_entity,
1050                                    ServerGeneral::CharacterListUpdate(list),
1051                                );
1052                                self.notify_client(
1053                                    response.target_entity,
1054                                    ServerGeneral::CharacterCreated(character_id),
1055                                );
1056                            },
1057                            Err(error) => self.notify_client(
1058                                response.target_entity,
1059                                ServerGeneral::CharacterActionError(error.to_string()),
1060                            ),
1061                        },
1062                        CharacterScreenResponseKind::CharacterEdit(result) => match result {
1063                            Ok((character_id, mut list)) => {
1064                                self.parse_locations(&mut list);
1065                                self.notify_client(
1066                                    response.target_entity,
1067                                    ServerGeneral::CharacterListUpdate(list),
1068                                );
1069                                self.notify_client(
1070                                    response.target_entity,
1071                                    ServerGeneral::CharacterEdited(character_id),
1072                                );
1073                            },
1074                            Err(error) => self.notify_client(
1075                                response.target_entity,
1076                                ServerGeneral::CharacterActionError(error.to_string()),
1077                            ),
1078                        },
1079                        CharacterScreenResponseKind::CharacterData(result) => {
1080                            match *result {
1081                                Ok((character_data, skill_set_persistence_load_error)) => {
1082                                    let PersistedComponents {
1083                                        body,
1084                                        hardcore,
1085                                        stats,
1086                                        skill_set,
1087                                        inventory,
1088                                        waypoint,
1089                                        pets,
1090                                        active_abilities,
1091                                        map_marker,
1092                                    } = character_data;
1093                                    let character_data = (
1094                                        body,
1095                                        hardcore,
1096                                        stats,
1097                                        skill_set,
1098                                        inventory,
1099                                        waypoint,
1100                                        pets,
1101                                        active_abilities,
1102                                        map_marker,
1103                                    );
1104                                    // TODO: Does this need to be a server event? E.g. we could
1105                                    // just handle it here.
1106                                    self.state.emit_event_now(UpdateCharacterDataEvent {
1107                                        entity: response.target_entity,
1108                                        components: character_data,
1109                                        metadata: skill_set_persistence_load_error,
1110                                    })
1111                                },
1112                                Err(error) => {
1113                                    // We failed to load data for the character from the DB. Notify
1114                                    // the client to push the state back to character selection,
1115                                    // with the error to display
1116                                    self.notify_client(
1117                                        response.target_entity,
1118                                        ServerGeneral::CharacterDataLoadResult(Err(
1119                                            error.to_string()
1120                                        )),
1121                                    );
1122
1123                                    // Clean up the entity data on the server
1124                                    self.state.emit_event_now(ExitIngameEvent {
1125                                        entity: response.target_entity,
1126                                    })
1127                                },
1128                            }
1129                        },
1130                    }
1131                },
1132            });
1133
1134        drop(character_loader);
1135        drop(character_updater);
1136
1137        {
1138            // Check for new chunks; cancel and regenerate all chunks if the asset has been
1139            // reloaded. Note that all of these assignments are no-ops, so the
1140            // only work we do here on the fast path is perform a relaxed read on an atomic.
1141            // boolean.
1142            let index = &mut self.index;
1143            let world = &mut self.world;
1144            let ecs = self.state.ecs_mut();
1145            let slow_jobs = ecs.write_resource::<SlowJobPool>();
1146
1147            index.reload_if_changed(|index| {
1148                let mut chunk_generator = ecs.write_resource::<ChunkGenerator>();
1149                let client = ecs.read_storage::<Client>();
1150                let mut terrain = ecs.write_resource::<common::terrain::TerrainGrid>();
1151                #[cfg(feature = "worldgen")]
1152                let rtsim = ecs.read_resource::<rtsim::RtSim>();
1153                #[cfg(not(feature = "worldgen"))]
1154                let rtsim = ();
1155
1156                // Cancel all pending chunks.
1157                chunk_generator.cancel_all();
1158
1159                if client.is_empty() {
1160                    // No clients, so just clear all terrain.
1161                    terrain.clear();
1162                } else {
1163                    // There's at least one client, so regenerate all chunks.
1164                    terrain.iter().for_each(|(pos, _)| {
1165                        chunk_generator.generate_chunk(
1166                            None,
1167                            pos,
1168                            &slow_jobs,
1169                            Arc::clone(world),
1170                            &rtsim,
1171                            index.clone(),
1172                            (
1173                                *ecs.read_resource::<TimeOfDay>(),
1174                                (*ecs.read_resource::<Calendar>()).clone(),
1175                            ),
1176                        );
1177                    });
1178                }
1179            });
1180        }
1181
1182        let end_of_server_tick = Instant::now();
1183
1184        // 8) Update Metrics
1185        run_now::<sys::metrics::Sys>(self.state.ecs());
1186
1187        {
1188            // Report timing info
1189            let tick_metrics = self.state.ecs().read_resource::<TickMetrics>();
1190
1191            let tt = &tick_metrics.tick_time;
1192            tt.with_label_values(&["new connections"])
1193                .set((before_state_tick - before_new_connections).as_nanos() as i64);
1194            tt.with_label_values(&["handle server events"])
1195                .set((before_update_terrain_and_regions - before_handle_events).as_nanos() as i64);
1196            tt.with_label_values(&["update terrain and region map"])
1197                .set((before_sync - before_update_terrain_and_regions).as_nanos() as i64);
1198            tt.with_label_values(&["state"])
1199                .set((before_handle_events - before_state_tick).as_nanos() as i64);
1200            tt.with_label_values(&["world tick"])
1201                .set((before_entity_cleanup - before_world_tick).as_nanos() as i64);
1202            tt.with_label_values(&["entity cleanup"])
1203                .set((before_persistence_updates - before_entity_cleanup).as_nanos() as i64);
1204            tt.with_label_values(&["persistence_updates"])
1205                .set((end_of_server_tick - before_persistence_updates).as_nanos() as i64);
1206            for (label, duration) in state_tick_metrics.timings {
1207                tick_metrics
1208                    .state_tick_time
1209                    .with_label_values(&[label])
1210                    .set(duration.as_nanos() as i64);
1211            }
1212            tick_metrics.tick_time_hist.observe(
1213                end_of_server_tick
1214                    .duration_since(before_state_tick)
1215                    .as_secs_f64(),
1216            );
1217        }
1218
1219        // 9) Finish the tick, pass control back to the frontend.
1220
1221        Ok(frontend_events)
1222    }
1223
1224    /// Clean up the server after a tick.
1225    pub fn cleanup(&mut self) {
1226        // Cleanup the local state
1227        self.state.cleanup();
1228
1229        // Maintain persisted terrain
1230        #[cfg(feature = "persistent_world")]
1231        self.state
1232            .ecs()
1233            .try_fetch_mut::<TerrainPersistence>()
1234            .map(|mut t| t.maintain());
1235    }
1236
1237    // Run RegionMap tick to update entity region occupancy
1238    fn update_region_map(&mut self) {
1239        prof_span!("Server::update_region_map");
1240        let ecs = self.state().ecs();
1241        ecs.write_resource::<RegionMap>().tick(
1242            ecs.read_storage::<comp::Pos>(),
1243            ecs.read_storage::<comp::Vel>(),
1244            ecs.read_storage::<comp::Presence>(),
1245            ecs.entities(),
1246        );
1247    }
1248
1249    fn initialize_client(&mut self, client: connection_handler::IncomingClient) -> Entity {
1250        let entity = self
1251            .state
1252            .ecs_mut()
1253            .create_entity_synced()
1254            .with(client)
1255            .build();
1256        self.state
1257            .ecs()
1258            .read_resource::<metrics::PlayerMetrics>()
1259            .clients_connected
1260            .inc();
1261        entity
1262    }
1263
1264    /// Disconnects all clients if requested by either an admin command or
1265    /// due to a persistence transaction failure and returns the processed
1266    /// DisconnectionType
1267    fn disconnect_all_clients_if_requested(&mut self) -> Option<DisconnectType> {
1268        let mut character_updater = self.state.ecs().fetch_mut::<CharacterUpdater>();
1269
1270        let disconnect_type = self.get_disconnect_all_clients_requested(&mut character_updater);
1271        if let Some(disconnect_type) = disconnect_type {
1272            let with_persistence = disconnect_type == DisconnectType::WithPersistence;
1273            let clients = self.state.ecs().read_storage::<Client>();
1274            let entities = self.state.ecs().entities();
1275
1276            info!(
1277                "Disconnecting all clients ({} persistence) as requested",
1278                if with_persistence { "with" } else { "without" }
1279            );
1280            for (_, entity) in (&clients, &entities).join() {
1281                info!("Emitting client disconnect event for entity: {:?}", entity);
1282                if with_persistence {
1283                    self.state.emit_event_now(ClientDisconnectEvent(
1284                        entity,
1285                        comp::DisconnectReason::Kicked,
1286                    ))
1287                } else {
1288                    self.state
1289                        .emit_event_now(ClientDisconnectWithoutPersistenceEvent(entity))
1290                };
1291            }
1292
1293            self.disconnect_all_clients_requested = false;
1294        }
1295
1296        disconnect_type
1297    }
1298
1299    fn get_disconnect_all_clients_requested(
1300        &self,
1301        character_updater: &mut CharacterUpdater,
1302    ) -> Option<DisconnectType> {
1303        let without_persistence_requested = character_updater.disconnect_all_clients_requested();
1304        let with_persistence_requested = self.disconnect_all_clients_requested;
1305
1306        if without_persistence_requested {
1307            return Some(DisconnectType::WithoutPersistence);
1308        };
1309        if with_persistence_requested {
1310            return Some(DisconnectType::WithPersistence);
1311        };
1312        None
1313    }
1314
1315    /// Handle new client connections.
1316    fn handle_new_connections(&mut self, frontend_events: &mut Vec<Event>) {
1317        while let Ok(sender) = self.connection_handler.info_requester_receiver.try_recv() {
1318            // can fail, e.g. due to timeout or network prob.
1319            trace!("sending info to connection_handler");
1320            let _ = sender.send(connection_handler::ServerInfoPacket {
1321                info: self.get_server_info(),
1322                time: self.state.get_time(),
1323            });
1324        }
1325
1326        while let Ok(incoming) = self.connection_handler.client_receiver.try_recv() {
1327            let entity = self.initialize_client(incoming);
1328            frontend_events.push(Event::ClientConnected { entity });
1329        }
1330    }
1331
1332    pub fn notify_client<S>(&self, entity: EcsEntity, msg: S)
1333    where
1334        S: Into<ServerMsg>,
1335    {
1336        if let Some(client) = self.state.ecs().read_storage::<Client>().get(entity) {
1337            client.send_fallible(msg);
1338        }
1339    }
1340
1341    pub fn notify_players(&mut self, msg: ServerGeneral) { self.state.notify_players(msg); }
1342
1343    pub fn generate_chunk(&mut self, entity: EcsEntity, key: Vec2<i32>) {
1344        let ecs = self.state.ecs();
1345        let slow_jobs = ecs.read_resource::<SlowJobPool>();
1346        #[cfg(feature = "worldgen")]
1347        let rtsim = ecs.read_resource::<rtsim::RtSim>();
1348        #[cfg(not(feature = "worldgen"))]
1349        let rtsim = ();
1350        ecs.write_resource::<ChunkGenerator>().generate_chunk(
1351            Some(entity),
1352            key,
1353            &slow_jobs,
1354            Arc::clone(&self.world),
1355            &rtsim,
1356            self.index.clone(),
1357            (
1358                *ecs.read_resource::<TimeOfDay>(),
1359                (*ecs.read_resource::<Calendar>()).clone(),
1360            ),
1361        );
1362    }
1363
1364    fn process_command(&mut self, entity: EcsEntity, name: String, args: Vec<String>) {
1365        // Find the command object and run its handler.
1366        if let Ok(command) = name.parse::<ServerChatCommand>() {
1367            command.execute(self, entity, args);
1368        } else {
1369            #[cfg(feature = "plugins")]
1370            {
1371                let mut plugin_manager = self.state.ecs().write_resource::<PluginMgr>();
1372                let ecs_world = EcsWorld {
1373                    entities: &self.state.ecs().entities(),
1374                    health: self.state.ecs().read_component().into(),
1375                    uid: self.state.ecs().read_component().into(),
1376                    id_maps: &self.state.ecs().read_resource::<IdMaps>().into(),
1377                    player: self.state.ecs().read_component().into(),
1378                };
1379                let uid = if let Some(uid) = ecs_world.uid.get(entity).copied() {
1380                    uid
1381                } else {
1382                    self.notify_client(
1383                        entity,
1384                        ServerGeneral::server_msg(
1385                            comp::ChatType::CommandError,
1386                            common::comp::Content::Plain(
1387                                "Can't get player UUID (player may be disconnected?)".to_string(),
1388                            ),
1389                        ),
1390                    );
1391                    return;
1392                };
1393                match plugin_manager.command_event(&ecs_world, &name, args.as_slice(), uid) {
1394                    Err(common_state::plugin::CommandResults::UnknownCommand) => self
1395                        .notify_client(
1396                            entity,
1397                            ServerGeneral::server_msg(
1398                                comp::ChatType::CommandError,
1399                                common::comp::Content::Plain(format!(
1400                                    "Unknown command '/{name}'.\nType '/help' for available \
1401                                     commands",
1402                                )),
1403                            ),
1404                        ),
1405                    Ok(value) => {
1406                        self.notify_client(
1407                            entity,
1408                            ServerGeneral::server_msg(
1409                                comp::ChatType::CommandInfo,
1410                                common::comp::Content::Plain(value.join("\n")),
1411                            ),
1412                        );
1413                    },
1414                    Err(common_state::plugin::CommandResults::PluginError(err)) => {
1415                        self.notify_client(
1416                            entity,
1417                            ServerGeneral::server_msg(
1418                                comp::ChatType::CommandError,
1419                                common::comp::Content::Plain(format!(
1420                                    "Error occurred while executing command '/{name}'.\n{err}"
1421                                )),
1422                            ),
1423                        );
1424                    },
1425                    Err(common_state::plugin::CommandResults::HostError(err)) => {
1426                        error!(?err, ?name, ?args, "Can't execute command");
1427                        self.notify_client(
1428                            entity,
1429                            ServerGeneral::server_msg(
1430                                comp::ChatType::CommandError,
1431                                common::comp::Content::Plain(format!(
1432                                    "Internal error {err:?} while executing '/{name}'.\nContact \
1433                                     the server administrator",
1434                                )),
1435                            ),
1436                        );
1437                    },
1438                }
1439            }
1440        }
1441    }
1442
1443    fn entity_admin_role(&self, entity: EcsEntity) -> Option<comp::AdminRole> {
1444        self.state
1445            .read_component_copied::<comp::Admin>(entity)
1446            .map(|admin| admin.0)
1447    }
1448
1449    pub fn number_of_players(&self) -> i64 {
1450        self.state.ecs().read_storage::<Client>().join().count() as i64
1451    }
1452
1453    /// NOTE: Do *not* allow this to be called from any command that doesn't go
1454    /// through the CLI!
1455    pub fn add_admin(&mut self, username: &str, role: comp::AdminRole) {
1456        let mut editable_settings = self.editable_settings_mut();
1457        let login_provider = self.state.ecs().fetch::<LoginProvider>();
1458        let data_dir = self.data_dir();
1459        if let Some(entity) = add_admin(
1460            username,
1461            role,
1462            &login_provider,
1463            &mut editable_settings,
1464            &data_dir.path,
1465        )
1466        .and_then(|uuid| {
1467            let state = &self.state;
1468            (
1469                &state.ecs().entities(),
1470                &state.read_storage::<comp::Player>(),
1471            )
1472                .join()
1473                .find(|(_, player)| player.uuid() == uuid)
1474                .map(|(e, _)| e)
1475        }) {
1476            drop((data_dir, login_provider, editable_settings));
1477            // Add admin component if the player is ingame; if they are not, we can ignore
1478            // the write failure.
1479            self.state
1480                .write_component_ignore_entity_dead(entity, comp::Admin(role));
1481        };
1482    }
1483
1484    /// NOTE: Do *not* allow this to be called from any command that doesn't go
1485    /// through the CLI!
1486    pub fn remove_admin(&self, username: &str) {
1487        let mut editable_settings = self.editable_settings_mut();
1488        let login_provider = self.state.ecs().fetch::<LoginProvider>();
1489        let data_dir = self.data_dir();
1490        if let Some(entity) = remove_admin(
1491            username,
1492            &login_provider,
1493            &mut editable_settings,
1494            &data_dir.path,
1495        )
1496        .and_then(|uuid| {
1497            let state = &self.state;
1498            (
1499                &state.ecs().entities(),
1500                &state.read_storage::<comp::Player>(),
1501            )
1502                .join()
1503                .find(|(_, player)| player.uuid() == uuid)
1504                .map(|(e, _)| e)
1505        }) {
1506            // Remove admin component if the player is ingame
1507            self.state
1508                .ecs()
1509                .write_storage::<comp::Admin>()
1510                .remove(entity);
1511        };
1512    }
1513
1514    /// Useful for testing without a client
1515    /// view_distance: distance in chunks that are persisted, this acts like the
1516    /// player view distance so it is actually a bit farther due to a buffer
1517    /// zone
1518    #[cfg(feature = "worldgen")]
1519    pub fn create_centered_persister(&mut self, view_distance: u32) {
1520        let world_dims_chunks = self.world.sim().get_size();
1521        let world_dims_blocks = TerrainChunkSize::blocks(world_dims_chunks);
1522        // NOTE: origin is in the corner of the map
1523        // TODO: extend this function to have picking a random position or specifying a
1524        // position as options
1525        //let mut rng = rand::thread_rng();
1526        // // Pick a random position but not to close to the edge
1527        // let rand_pos = world_dims_blocks.map(|e| e as i32).map(|e| e / 2 +
1528        // rng.gen_range(-e/2..e/2 + 1));
1529        let pos = comp::Pos(Vec3::from(world_dims_blocks.map(|e| e as f32 / 2.0)));
1530        self.state
1531            .create_persister(pos, view_distance, &self.world, &self.index)
1532            .build();
1533    }
1534
1535    /// Used by benchmarking code.
1536    pub fn chunks_pending(&mut self) -> bool {
1537        self.state_mut()
1538            .mut_resource::<ChunkGenerator>()
1539            .pending_chunks()
1540            .next()
1541            .is_some()
1542    }
1543
1544    /// Sets the SQL log mode at runtime
1545    pub fn set_sql_log_mode(&mut self, sql_log_mode: SqlLogMode) {
1546        // Unwrap is safe here because we only perform a variable assignment with the
1547        // RwLock taken meaning that no panic can occur that would cause the
1548        // RwLock to become poisoned. This justification also means that calling
1549        // unwrap() on the associated read() calls for this RwLock is also safe
1550        // as long as no code that can panic is introduced here.
1551        let mut database_settings = self.database_settings.write().unwrap();
1552        database_settings.sql_log_mode = sql_log_mode;
1553        // Drop the RwLockWriteGuard to avoid performing unnecessary actions (logging)
1554        // with the lock taken.
1555        drop(database_settings);
1556        info!("SQL log mode changed to {:?}", sql_log_mode);
1557    }
1558
1559    pub fn disconnect_all_clients(&mut self) {
1560        info!("Disconnecting all clients due to local console command");
1561        self.disconnect_all_clients_requested = true;
1562    }
1563
1564    /// Sends the given client a message with their current battle mode and
1565    /// whether they can change it.
1566    ///
1567    /// This function expects the `EcsEntity` to represent a player, otherwise
1568    /// it will log an error.
1569    pub fn get_battle_mode_for(&mut self, client: EcsEntity) {
1570        let ecs = self.state.ecs();
1571        let time = ecs.read_resource::<Time>();
1572        let settings = ecs.read_resource::<Settings>();
1573        let players = ecs.read_storage::<comp::Player>();
1574        let get_player_result = players.get(client).ok_or_else(|| {
1575            error!("Can't get player component for client.");
1576
1577            Content::Plain("Can't get player component for client.".to_string())
1578        });
1579        let player = match get_player_result {
1580            Ok(player) => player,
1581            Err(content) => {
1582                self.notify_client(
1583                    client,
1584                    ServerGeneral::server_msg(ChatType::CommandError, content),
1585                );
1586                return;
1587            },
1588        };
1589
1590        let mut msg = format!("Current battle mode: {:?}.", player.battle_mode);
1591
1592        if settings.gameplay.battle_mode.allow_choosing() {
1593            msg.push_str(" Possible to change.");
1594        } else {
1595            msg.push_str(" Global.");
1596        }
1597
1598        if let Some(change) = player.last_battlemode_change {
1599            let Time(time) = *time;
1600            let Time(change) = change;
1601            let elapsed = time - change;
1602            let next = BATTLE_MODE_COOLDOWN - elapsed;
1603
1604            if next > 0.0 {
1605                let notice = format!(" Next change will be available in: {:.0} seconds", next);
1606                msg.push_str(&notice);
1607            }
1608        }
1609
1610        self.notify_client(
1611            client,
1612            ServerGeneral::server_msg(ChatType::CommandInfo, Content::Plain(msg)),
1613        );
1614    }
1615
1616    /// Sets the battle mode for the given client or informs them if they are
1617    /// not allowed to change it.
1618    ///
1619    /// This function expects the `EcsEntity` to represent a player, otherwise
1620    /// it will log an error.
1621    pub fn set_battle_mode_for(&mut self, client: EcsEntity, battle_mode: BattleMode) {
1622        let ecs = self.state.ecs();
1623        let time = ecs.read_resource::<Time>();
1624        let settings = ecs.read_resource::<Settings>();
1625
1626        if !settings.gameplay.battle_mode.allow_choosing() {
1627            self.notify_client(
1628                client,
1629                ServerGeneral::server_msg(
1630                    ChatType::CommandInfo,
1631                    Content::localized("command-disabled-by-settings"),
1632                ),
1633            );
1634
1635            return;
1636        }
1637
1638        #[cfg(feature = "worldgen")]
1639        let in_town = {
1640            let pos = if let Some(pos) = self
1641                .state
1642                .ecs()
1643                .read_storage::<comp::Pos>()
1644                .get(client)
1645                .copied()
1646            {
1647                pos
1648            } else {
1649                self.notify_client(
1650                    client,
1651                    ServerGeneral::server_msg(
1652                        ChatType::CommandInfo,
1653                        Content::localized_with_args("command-position-unavailable", [(
1654                            "target", "target",
1655                        )]),
1656                    ),
1657                );
1658
1659                return;
1660            };
1661
1662            let wpos = pos.0.xy().map(|x| x as i32);
1663            let chunk_pos = wpos.wpos_to_cpos();
1664            self.world.civs().sites().any(|site| {
1665                // empirical
1666                const RADIUS: f32 = 9.0;
1667                let delta = site
1668                    .center
1669                    .map(|x| x as f32)
1670                    .distance(chunk_pos.map(|x| x as f32));
1671                delta < RADIUS
1672            })
1673        };
1674
1675        #[cfg(not(feature = "worldgen"))]
1676        let in_town = true;
1677
1678        if !in_town {
1679            self.notify_client(
1680                client,
1681                ServerGeneral::server_msg(
1682                    ChatType::CommandInfo,
1683                    Content::localized("command-battlemode-intown"),
1684                ),
1685            );
1686
1687            return;
1688        }
1689
1690        let mut players = ecs.write_storage::<comp::Player>();
1691        let mut player = if let Some(info) = players.get_mut(client) {
1692            info
1693        } else {
1694            error!("Failed to get info for player.");
1695
1696            return;
1697        };
1698
1699        if let Some(Time(last_change)) = player.last_battlemode_change {
1700            let Time(time) = *time;
1701            let elapsed = time - last_change;
1702            if elapsed < BATTLE_MODE_COOLDOWN {
1703                let next = BATTLE_MODE_COOLDOWN - elapsed;
1704
1705                self.notify_client(
1706                    client,
1707                    ServerGeneral::server_msg(
1708                        ChatType::CommandInfo,
1709                        Content::Plain(format!(
1710                            "Next change will be available in {next:.0} seconds."
1711                        )),
1712                    ),
1713                );
1714
1715                return;
1716            }
1717        }
1718
1719        if player.battle_mode == battle_mode {
1720            self.notify_client(
1721                client,
1722                ServerGeneral::server_msg(
1723                    ChatType::CommandInfo,
1724                    Content::localized("command-battlemode-same"),
1725                ),
1726            );
1727
1728            return;
1729        }
1730
1731        player.battle_mode = battle_mode;
1732        player.last_battlemode_change = Some(*time);
1733
1734        self.notify_client(
1735            client,
1736            ServerGeneral::server_msg(
1737                ChatType::CommandInfo,
1738                Content::localized_with_args("command-battlemode-updated", [(
1739                    "battlemode",
1740                    format!("{battle_mode:?}"),
1741                )]),
1742            ),
1743        );
1744
1745        drop(players);
1746
1747        let uid = ecs.read_storage::<Uid>().get(client).copied().unwrap();
1748
1749        self.state().notify_players(ServerGeneral::PlayerListUpdate(
1750            PlayerListUpdate::UpdateBattleMode(uid, battle_mode),
1751        ));
1752    }
1753}
1754
1755impl Drop for Server {
1756    fn drop(&mut self) {
1757        self.state
1758            .notify_players(ServerGeneral::Disconnect(DisconnectReason::Shutdown));
1759
1760        #[cfg(feature = "persistent_world")]
1761        self.state
1762            .ecs()
1763            .try_fetch_mut::<TerrainPersistence>()
1764            .map(|mut terrain_persistence| {
1765                info!("Unloading terrain persistence...");
1766                terrain_persistence.unload_all()
1767            });
1768
1769        #[cfg(feature = "worldgen")]
1770        {
1771            debug!("Saving rtsim state...");
1772            self.state.ecs().write_resource::<rtsim::RtSim>().save(true);
1773        }
1774    }
1775}
1776
1777#[must_use]
1778pub fn handle_edit<T, S: settings::EditableSetting>(
1779    data: T,
1780    result: Option<(String, Result<(), settings::SettingError<S>>)>,
1781) -> Option<T> {
1782    use crate::settings::SettingError;
1783    let (info, result) = result?;
1784    match result {
1785        Ok(()) => {
1786            info!("{}", info);
1787            Some(data)
1788        },
1789        Err(SettingError::Io(err)) => {
1790            warn!(
1791                ?err,
1792                "Failed to write settings file to disk, but succeeded in memory (success message: \
1793                 {})",
1794                info,
1795            );
1796            Some(data)
1797        },
1798        Err(SettingError::Integrity(err)) => {
1799            error!(?err, "Encountered an error while validating the request",);
1800            None
1801        },
1802    }
1803}
1804
1805/// If successful returns the Some(uuid) of the added admin
1806///
1807/// NOTE: Do *not* allow this to be called from any command that doesn't go
1808/// through the CLI!
1809#[must_use]
1810pub fn add_admin(
1811    username: &str,
1812    role: comp::AdminRole,
1813    login_provider: &LoginProvider,
1814    editable_settings: &mut EditableSettings,
1815    data_dir: &std::path::Path,
1816) -> Option<common::uuid::Uuid> {
1817    use crate::settings::EditableSetting;
1818    let role_ = role.into();
1819    match login_provider.username_to_uuid(username) {
1820        Ok(uuid) => handle_edit(
1821            uuid,
1822            editable_settings.admins.edit(data_dir, |admins| {
1823                match admins.insert(uuid, settings::AdminRecord {
1824                    username_when_admined: Some(username.into()),
1825                    date: chrono::Utc::now(),
1826                    role: role_,
1827                }) {
1828                    None => Some(format!(
1829                        "Successfully added {} ({}) as {:?}!",
1830                        username, uuid, role
1831                    )),
1832                    Some(old_admin) if old_admin.role == role_ => {
1833                        info!("{} ({}) already has role: {:?}!", username, uuid, role);
1834                        None
1835                    },
1836                    Some(old_admin) => Some(format!(
1837                        "{} ({}) role changed from {:?} to {:?}!",
1838                        username, uuid, old_admin.role, role
1839                    )),
1840                }
1841            }),
1842        ),
1843        Err(err) => {
1844            error!(
1845                ?err,
1846                "Could not find uuid for this name; either the user does not exist or there was \
1847                 an error communicating with the auth server."
1848            );
1849            None
1850        },
1851    }
1852}
1853
1854/// If successful returns the Some(uuid) of the removed admin
1855///
1856/// NOTE: Do *not* allow this to be called from any command that doesn't go
1857/// through the CLI!
1858#[must_use]
1859pub fn remove_admin(
1860    username: &str,
1861    login_provider: &LoginProvider,
1862    editable_settings: &mut EditableSettings,
1863    data_dir: &std::path::Path,
1864) -> Option<common::uuid::Uuid> {
1865    use crate::settings::EditableSetting;
1866    match login_provider.username_to_uuid(username) {
1867        Ok(uuid) => handle_edit(
1868            uuid,
1869            editable_settings.admins.edit(data_dir, |admins| {
1870                if let Some(admin) = admins.remove(&uuid) {
1871                    Some(format!(
1872                        "Successfully removed {} ({}) with role {:?} from the admins list",
1873                        username, uuid, admin.role,
1874                    ))
1875                } else {
1876                    info!("{} ({}) is not an admin!", username, uuid);
1877                    None
1878                }
1879            }),
1880        ),
1881        Err(err) => {
1882            error!(
1883                ?err,
1884                "Could not find uuid for this name; either the user does not exist or there was \
1885                 an error communicating with the auth server."
1886            );
1887            None
1888        },
1889    }
1890}