use crate::{ automod::AutoMod, chat::ChatExporter, client::Client, events::{self, update_map_markers}, persistence::PersistedComponents, pet::restore_pet, presence::RepositionOnChunkLoad, rtsim::RtSim, settings::Settings, sys::sentinel::DeletedEntities, wiring, BattleModeBuffer, SpawnPoint, }; use common::{ calendar::Calendar, character::CharacterId, combat, combat::DamageContributor, comp::{ self, item::{ItemKind, MaterialStatManifest}, misc::PortalData, object, skills::{GeneralSkill, Skill}, ChatType, Content, Group, Inventory, Item, LootOwner, Object, Player, Poise, Presence, PresenceKind, BASE_ABILITY_LIMIT, }, effect::Effect, link::{Is, Link, LinkHandle}, mounting::{Mounting, Rider, VolumeMounting, VolumeRider}, resources::{Secs, Time, TimeOfDay}, rtsim::{Actor, RtSimEntity}, slowjob::SlowJobPool, tether::Tethered, uid::{IdMaps, Uid}, util::Dir, LoadoutBuilder, ViewDistances, }; use common_net::{ msg::{CharacterInfo, PlayerListUpdate, ServerGeneral}, sync::WorldSyncExt, }; use common_state::State; use rand::prelude::*; use specs::{Builder, Entity as EcsEntity, EntityBuilder as EcsEntityBuilder, Join, WorldExt}; use std::time::{Duration, Instant}; use tracing::{error, trace, warn}; use vek::*; pub trait StateExt { /// Updates a component associated with the entity based on the `Effect` fn apply_effect(&self, entity: EcsEntity, effect: Effect, source: Option); /// Build a non-player character fn create_npc( &mut self, pos: comp::Pos, ori: comp::Ori, stats: comp::Stats, skill_set: comp::SkillSet, health: Option, poise: Poise, inventory: Inventory, body: comp::Body, ) -> EcsEntityBuilder; /// Build a static object entity fn create_object(&mut self, pos: comp::Pos, object: comp::object::Body) -> EcsEntityBuilder; /// Create an item drop or merge the item with an existing drop, if a /// suitable candidate exists. fn create_item_drop( &mut self, pos: comp::Pos, vel: comp::Vel, item: Item, loot_owner: Option, ) -> Option; fn create_ship comp::Collider>( &mut self, pos: comp::Pos, ori: comp::Ori, ship: comp::ship::Body, make_collider: F, ) -> EcsEntityBuilder; /// Build a projectile fn create_projectile( &mut self, pos: comp::Pos, vel: comp::Vel, body: comp::Body, projectile: comp::Projectile, ) -> EcsEntityBuilder; /// Build a shockwave entity fn create_shockwave( &mut self, properties: comp::shockwave::Properties, pos: comp::Pos, ori: comp::Ori, ) -> EcsEntityBuilder; /// Creates a safezone fn create_safezone(&mut self, range: Option, pos: comp::Pos) -> EcsEntityBuilder; fn create_wiring( &mut self, pos: comp::Pos, object: comp::object::Body, wiring_element: wiring::WiringElement, ) -> EcsEntityBuilder; // NOTE: currently only used for testing /// Queues chunk generation in the view distance of the persister, this /// entity must be built before those chunks are received (the builder /// borrows the ecs world so that is kind of impossible in practice) fn create_persister( &mut self, pos: comp::Pos, view_distance: u32, world: &std::sync::Arc, index: &world::IndexOwned, ) -> EcsEntityBuilder; /// Creates a teleporter entity, which allows players to teleport to the /// `target` position. You might want to require the teleporting entity /// to not have agro for teleporting. fn create_teleporter(&mut self, pos: comp::Pos, portal: PortalData) -> EcsEntityBuilder; /// Insert common/default components for a new character joining the server fn initialize_character_data( &mut self, entity: EcsEntity, character_id: CharacterId, view_distances: ViewDistances, ); /// Insert common/default components for a new spectator joining the server fn initialize_spectator_data(&mut self, entity: EcsEntity, view_distances: ViewDistances); /// Update the components associated with the entity's current character. /// Performed after loading component data from the database fn update_character_data( &mut self, entity: EcsEntity, components: PersistedComponents, ) -> Result<(), String>; /// Iterates over registered clients and send each `ServerMsg` fn validate_chat_msg( &self, player: EcsEntity, chat_type: &comp::ChatType, msg: &str, ) -> bool; fn send_chat(&self, msg: comp::UnresolvedChatMsg); fn notify_players(&self, msg: ServerGeneral); fn notify_in_game_clients(&self, msg: ServerGeneral); /// Create a new link between entities (see [`common::mounting`] for an /// example). fn link(&mut self, link: L) -> Result<(), L::Error>; /// Maintain active links between entities fn maintain_links(&mut self); /// Delete an entity, recording the deletion in [`DeletedEntities`] fn delete_entity_recorded( &mut self, entity: EcsEntity, ) -> Result<(), specs::error::WrongGeneration>; /// Get the given entity as an [`Actor`], if it is one. fn entity_as_actor(&self, entity: EcsEntity) -> Option; /// Mutate the position of an entity or, if the entity is mounted, the /// mount. /// /// If `dismount_volume` is `true`, an entity mounted on a volume entity /// (such as an airship) will be dismounted to avoid teleporting the volume /// entity. fn position_mut( &mut self, entity: EcsEntity, dismount_volume: bool, f: impl for<'a> FnOnce(&'a mut comp::Pos) -> T, ) -> Result; } impl StateExt for State { fn apply_effect(&self, entity: EcsEntity, effects: Effect, source: Option) { let msm = self.ecs().read_resource::(); match effects { Effect::Health(change) => { self.ecs() .write_storage::() .get_mut(entity) .map(|mut health| health.change_by(change)); }, Effect::Damage(damage) => { let inventories = self.ecs().read_storage::(); let stats = self.ecs().read_storage::(); let groups = self.ecs().read_storage::(); let damage_contributor = source.and_then(|uid| { self.ecs().entity_from_uid(uid).map(|attacker_entity| { DamageContributor::new(uid, groups.get(attacker_entity).cloned()) }) }); let time = self.ecs().read_resource::