mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Merge branch 'isse/event-systems' into 'master'
Seperate events to seperate event busses and use systems to handle them See merge request veloren/veloren!4189
This commit is contained in:
commit
3ac2729b99
@ -2140,12 +2140,7 @@ impl Client {
|
||||
&self.connected_server_constants,
|
||||
|_, _| {},
|
||||
);
|
||||
// TODO: avoid emitting these in the first place
|
||||
let _ = self
|
||||
.state
|
||||
.ecs()
|
||||
.fetch::<EventBus<common::event::ServerEvent>>()
|
||||
.recv_all();
|
||||
|
||||
// TODO: avoid emitting these in the first place OR actually use outcomes
|
||||
// generated locally on the client (if they can be deduplicated from
|
||||
// ones that the server generates or if the client can reliably generate
|
||||
|
@ -75,7 +75,7 @@ slotmap = { version = "1.0", features = ["serde"] }
|
||||
indexmap = { version = "1.9.3", features = ["rayon"] }
|
||||
|
||||
# ECS
|
||||
specs = { workspace = true, features = ["serde", "storage-event-control"] }
|
||||
specs = { workspace = true, features = ["serde", "storage-event-control", "shred-derive"] }
|
||||
|
||||
[dev-dependencies]
|
||||
#bench
|
||||
|
@ -14,7 +14,10 @@ use crate::{
|
||||
Alignment, Body, Buffs, CharacterState, Combo, Energy, Group, Health, HealthChange,
|
||||
Inventory, Ori, Player, Poise, PoiseChange, SkillSet, Stats,
|
||||
},
|
||||
event::ServerEvent,
|
||||
event::{
|
||||
BuffEvent, ComboChangeEvent, EmitExt, EnergyChangeEvent, EntityAttackedHookEvent,
|
||||
HealthChangeEvent, KnockbackEvent, ParryHookEvent, PoiseChangeEvent,
|
||||
},
|
||||
outcome::Outcome,
|
||||
resources::{Secs, Time},
|
||||
states::utils::StageSection,
|
||||
@ -144,7 +147,7 @@ impl Attack {
|
||||
dir: Dir,
|
||||
damage: Damage,
|
||||
msm: &MaterialStatManifest,
|
||||
mut emit: impl FnMut(ServerEvent),
|
||||
emitters: &mut impl EmitExt<ParryHookEvent>,
|
||||
mut emit_outcome: impl FnMut(Outcome),
|
||||
) -> f32 {
|
||||
if damage.value > 0.0 {
|
||||
@ -164,7 +167,7 @@ impl Attack {
|
||||
pos: target.pos,
|
||||
uid: target.uid,
|
||||
});
|
||||
emit(ServerEvent::ParryHook {
|
||||
emitters.emit(ParryHookEvent {
|
||||
defender: target.entity,
|
||||
attacker: attacker.map(|a| a.entity),
|
||||
source,
|
||||
@ -203,7 +206,16 @@ impl Attack {
|
||||
strength_modifier: f32,
|
||||
attack_source: AttackSource,
|
||||
time: Time,
|
||||
mut emit: impl FnMut(ServerEvent),
|
||||
emitters: &mut (
|
||||
impl EmitExt<HealthChangeEvent>
|
||||
+ EmitExt<EnergyChangeEvent>
|
||||
+ EmitExt<ParryHookEvent>
|
||||
+ EmitExt<KnockbackEvent>
|
||||
+ EmitExt<BuffEvent>
|
||||
+ EmitExt<PoiseChangeEvent>
|
||||
+ EmitExt<ComboChangeEvent>
|
||||
+ EmitExt<EntityAttackedHookEvent>
|
||||
),
|
||||
mut emit_outcome: impl FnMut(Outcome),
|
||||
rng: &mut rand::rngs::ThreadRng,
|
||||
damage_instance_offset: u64,
|
||||
@ -255,7 +267,7 @@ impl Attack {
|
||||
dir,
|
||||
damage.damage,
|
||||
msm,
|
||||
&mut emit,
|
||||
emitters,
|
||||
&mut emit_outcome,
|
||||
);
|
||||
let change = damage.damage.calculate_health_change(
|
||||
@ -271,7 +283,7 @@ impl Attack {
|
||||
accumulated_damage += applied_damage;
|
||||
|
||||
if change.amount.abs() > Health::HEALTH_EPSILON {
|
||||
emit(ServerEvent::HealthChange {
|
||||
emitters.emit(HealthChangeEvent {
|
||||
entity: target.entity,
|
||||
change,
|
||||
});
|
||||
@ -293,12 +305,12 @@ impl Attack {
|
||||
precise: precision_mult.is_some(),
|
||||
instance: damage_instance,
|
||||
};
|
||||
emit(ServerEvent::HealthChange {
|
||||
emitters.emit(HealthChangeEvent {
|
||||
entity: target.entity,
|
||||
change: health_change,
|
||||
});
|
||||
}
|
||||
emit(ServerEvent::EnergyChange {
|
||||
emitters.emit(EnergyChangeEvent {
|
||||
entity: target.entity,
|
||||
change: -energy_change,
|
||||
});
|
||||
@ -344,12 +356,12 @@ impl Attack {
|
||||
precise: precision_mult.is_some(),
|
||||
time,
|
||||
};
|
||||
emit(ServerEvent::HealthChange {
|
||||
emitters.emit(HealthChangeEvent {
|
||||
entity: target.entity,
|
||||
change: health_change,
|
||||
});
|
||||
} else {
|
||||
emit(ServerEvent::PoiseChange {
|
||||
emitters.emit(PoiseChangeEvent {
|
||||
entity: target.entity,
|
||||
change: poise_change,
|
||||
});
|
||||
@ -366,7 +378,7 @@ impl Attack {
|
||||
let impulse =
|
||||
kb.calculate_impulse(dir, target.char_state) * strength_modifier;
|
||||
if !impulse.is_approx_zero() {
|
||||
emit(ServerEvent::Knockback {
|
||||
emitters.emit(KnockbackEvent {
|
||||
entity: target.entity,
|
||||
impulse,
|
||||
});
|
||||
@ -374,7 +386,7 @@ impl Attack {
|
||||
},
|
||||
CombatEffect::EnergyReward(ec) => {
|
||||
if let Some(attacker) = attacker {
|
||||
emit(ServerEvent::EnergyChange {
|
||||
emitters.emit(EnergyChangeEvent {
|
||||
entity: attacker.entity,
|
||||
change: *ec
|
||||
* compute_energy_reward_mod(attacker.inventory, msm)
|
||||
@ -385,7 +397,7 @@ impl Attack {
|
||||
},
|
||||
CombatEffect::Buff(b) => {
|
||||
if rng.gen::<f32>() < b.chance {
|
||||
emit(ServerEvent::Buff {
|
||||
emitters.emit(BuffEvent {
|
||||
entity: target.entity,
|
||||
buff_change: BuffChange::Add(b.to_buff(
|
||||
time,
|
||||
@ -409,7 +421,7 @@ impl Attack {
|
||||
instance: rand::random(),
|
||||
};
|
||||
if change.amount.abs() > Health::HEALTH_EPSILON {
|
||||
emit(ServerEvent::HealthChange {
|
||||
emitters.emit(HealthChangeEvent {
|
||||
entity: attacker_entity,
|
||||
change,
|
||||
});
|
||||
@ -435,7 +447,7 @@ impl Attack {
|
||||
cause: Some(damage.damage.source),
|
||||
time,
|
||||
};
|
||||
emit(ServerEvent::PoiseChange {
|
||||
emitters.emit(PoiseChangeEvent {
|
||||
entity: target.entity,
|
||||
change: poise_change,
|
||||
});
|
||||
@ -451,7 +463,7 @@ impl Attack {
|
||||
instance: rand::random(),
|
||||
};
|
||||
if change.amount.abs() > Health::HEALTH_EPSILON {
|
||||
emit(ServerEvent::HealthChange {
|
||||
emitters.emit(HealthChangeEvent {
|
||||
entity: target.entity,
|
||||
change,
|
||||
});
|
||||
@ -460,7 +472,7 @@ impl Attack {
|
||||
CombatEffect::Combo(c) => {
|
||||
// Not affected by strength modifier as integer
|
||||
if let Some(attacker_entity) = attacker.map(|a| a.entity) {
|
||||
emit(ServerEvent::ComboChange {
|
||||
emitters.emit(ComboChangeEvent {
|
||||
entity: attacker_entity,
|
||||
change: *c,
|
||||
});
|
||||
@ -476,7 +488,7 @@ impl Attack {
|
||||
change.amount *= damage;
|
||||
change
|
||||
};
|
||||
emit(ServerEvent::HealthChange {
|
||||
emitters.emit(HealthChangeEvent {
|
||||
entity: target.entity,
|
||||
change,
|
||||
});
|
||||
@ -484,7 +496,7 @@ impl Attack {
|
||||
},
|
||||
CombatEffect::RefreshBuff(chance, b) => {
|
||||
if rng.gen::<f32>() < *chance {
|
||||
emit(ServerEvent::Buff {
|
||||
emitters.emit(BuffEvent {
|
||||
entity: target.entity,
|
||||
buff_change: BuffChange::Refresh(*b),
|
||||
});
|
||||
@ -497,7 +509,7 @@ impl Attack {
|
||||
change.amount *= damage;
|
||||
change
|
||||
};
|
||||
emit(ServerEvent::HealthChange {
|
||||
emitters.emit(HealthChangeEvent {
|
||||
entity: target.entity,
|
||||
change,
|
||||
});
|
||||
@ -510,7 +522,7 @@ impl Attack {
|
||||
change.amount *= damage;
|
||||
change
|
||||
};
|
||||
emit(ServerEvent::HealthChange {
|
||||
emitters.emit(HealthChangeEvent {
|
||||
entity: target.entity,
|
||||
change,
|
||||
});
|
||||
@ -543,7 +555,7 @@ impl Attack {
|
||||
{
|
||||
let sufficient_energy = e.current() >= *r;
|
||||
if sufficient_energy {
|
||||
emit(ServerEvent::EnergyChange {
|
||||
emitters.emit(EnergyChangeEvent {
|
||||
entity,
|
||||
change: -*r,
|
||||
});
|
||||
@ -563,7 +575,7 @@ impl Attack {
|
||||
{
|
||||
let sufficient_combo = c.counter() >= *r;
|
||||
if sufficient_combo {
|
||||
emit(ServerEvent::ComboChange {
|
||||
emitters.emit(ComboChangeEvent {
|
||||
entity,
|
||||
change: -(*r as i32),
|
||||
});
|
||||
@ -585,7 +597,7 @@ impl Attack {
|
||||
let impulse =
|
||||
kb.calculate_impulse(dir, target.char_state) * strength_modifier;
|
||||
if !impulse.is_approx_zero() {
|
||||
emit(ServerEvent::Knockback {
|
||||
emitters.emit(KnockbackEvent {
|
||||
entity: target.entity,
|
||||
impulse,
|
||||
});
|
||||
@ -593,7 +605,7 @@ impl Attack {
|
||||
},
|
||||
CombatEffect::EnergyReward(ec) => {
|
||||
if let Some(attacker) = attacker {
|
||||
emit(ServerEvent::EnergyChange {
|
||||
emitters.emit(EnergyChangeEvent {
|
||||
entity: attacker.entity,
|
||||
change: ec
|
||||
* compute_energy_reward_mod(attacker.inventory, msm)
|
||||
@ -604,7 +616,7 @@ impl Attack {
|
||||
},
|
||||
CombatEffect::Buff(b) => {
|
||||
if rng.gen::<f32>() < b.chance {
|
||||
emit(ServerEvent::Buff {
|
||||
emitters.emit(BuffEvent {
|
||||
entity: target.entity,
|
||||
buff_change: BuffChange::Add(b.to_buff(
|
||||
time,
|
||||
@ -628,7 +640,7 @@ impl Attack {
|
||||
instance: rand::random(),
|
||||
};
|
||||
if change.amount.abs() > Health::HEALTH_EPSILON {
|
||||
emit(ServerEvent::HealthChange {
|
||||
emitters.emit(HealthChangeEvent {
|
||||
entity: attacker_entity,
|
||||
change,
|
||||
});
|
||||
@ -654,7 +666,7 @@ impl Attack {
|
||||
cause: Some(attack_source.into()),
|
||||
time,
|
||||
};
|
||||
emit(ServerEvent::PoiseChange {
|
||||
emitters.emit(PoiseChangeEvent {
|
||||
entity: target.entity,
|
||||
change: poise_change,
|
||||
});
|
||||
@ -670,7 +682,7 @@ impl Attack {
|
||||
instance: rand::random(),
|
||||
};
|
||||
if change.amount.abs() > Health::HEALTH_EPSILON {
|
||||
emit(ServerEvent::HealthChange {
|
||||
emitters.emit(HealthChangeEvent {
|
||||
entity: target.entity,
|
||||
change,
|
||||
});
|
||||
@ -679,7 +691,7 @@ impl Attack {
|
||||
CombatEffect::Combo(c) => {
|
||||
// Not affected by strength modifier as integer
|
||||
if let Some(attacker_entity) = attacker.map(|a| a.entity) {
|
||||
emit(ServerEvent::ComboChange {
|
||||
emitters.emit(ComboChangeEvent {
|
||||
entity: attacker_entity,
|
||||
change: c,
|
||||
});
|
||||
@ -689,7 +701,7 @@ impl Attack {
|
||||
CombatEffect::StageVulnerable(_, _) => {},
|
||||
CombatEffect::RefreshBuff(chance, b) => {
|
||||
if rng.gen::<f32>() < chance {
|
||||
emit(ServerEvent::Buff {
|
||||
emitters.emit(BuffEvent {
|
||||
entity: target.entity,
|
||||
buff_change: BuffChange::Refresh(b),
|
||||
});
|
||||
@ -704,7 +716,7 @@ impl Attack {
|
||||
// Emits event to handle things that should happen for any successful attack,
|
||||
// regardless of if the attack had any damages or effects in it
|
||||
if is_applied {
|
||||
emit(ServerEvent::EntityAttackedHook {
|
||||
emitters.emit(EntityAttackedHookEvent {
|
||||
entity: target.entity,
|
||||
attacker: attacker.map(|a| a.entity),
|
||||
});
|
||||
|
@ -4,7 +4,8 @@ use crate::{
|
||||
ability::Capability, inventory::item::armor::Friction, item::ConsumableKind, ControlAction,
|
||||
Density, Energy, InputAttr, InputKind, Ori, Pos, Vel,
|
||||
},
|
||||
event::{LocalEvent, ServerEvent},
|
||||
event::{self, EmitExt, LocalEvent},
|
||||
event_emitters,
|
||||
resources::Time,
|
||||
states::{
|
||||
self,
|
||||
@ -34,19 +35,46 @@ pub struct StateUpdate {
|
||||
pub character_activity: CharacterActivity,
|
||||
}
|
||||
|
||||
pub struct OutputEvents<'a> {
|
||||
local: &'a mut Vec<LocalEvent>,
|
||||
server: &'a mut Vec<ServerEvent>,
|
||||
event_emitters! {
|
||||
pub struct CharacterStateEvents[CharacterStateEventEmitters] {
|
||||
combo: event::ComboChangeEvent,
|
||||
event: event::AuraEvent,
|
||||
shoot: event::ShootEvent,
|
||||
teleport_to: event::TeleportToEvent,
|
||||
shockwave: event::ShockwaveEvent,
|
||||
explosion: event::ExplosionEvent,
|
||||
buff: event::BuffEvent,
|
||||
inventory_manip: event::InventoryManipEvent,
|
||||
sprite_summon: event::CreateSpriteEvent,
|
||||
change_stance: event::ChangeStanceEvent,
|
||||
create_npc: event::CreateNpcEvent,
|
||||
energy_change: event::EnergyChangeEvent,
|
||||
knockback: event::KnockbackEvent,
|
||||
sprite_light: event::ToggleSpriteLightEvent,
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> OutputEvents<'a> {
|
||||
pub fn new(local: &'a mut Vec<LocalEvent>, server: &'a mut Vec<ServerEvent>) -> Self {
|
||||
pub struct OutputEvents<'a, 'b> {
|
||||
local: &'a mut Vec<LocalEvent>,
|
||||
server: &'a mut CharacterStateEventEmitters<'b>,
|
||||
}
|
||||
|
||||
impl<'a, 'b: 'a> OutputEvents<'a, 'b> {
|
||||
pub fn new(
|
||||
local: &'a mut Vec<LocalEvent>,
|
||||
server: &'a mut CharacterStateEventEmitters<'b>,
|
||||
) -> Self {
|
||||
Self { local, server }
|
||||
}
|
||||
|
||||
pub fn emit_local(&mut self, event: LocalEvent) { self.local.push(event); }
|
||||
|
||||
pub fn emit_server(&mut self, event: ServerEvent) { self.server.push(event); }
|
||||
pub fn emit_server<E>(&mut self, event: E)
|
||||
where
|
||||
CharacterStateEventEmitters<'b>: EmitExt<E>,
|
||||
{
|
||||
self.server.emit(event);
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&JoinData<'_>> for StateUpdate {
|
||||
|
@ -217,6 +217,13 @@ impl<G> GenericChatMsg<G> {
|
||||
Self { chat_type, content }
|
||||
}
|
||||
|
||||
pub fn death(kill_source: KillSource, victim: Uid) -> Self {
|
||||
Self {
|
||||
chat_type: ChatType::Kill(kill_source, victim),
|
||||
content: Content::Plain(String::new()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn map_group<T>(self, mut f: impl FnMut(G) -> T) -> GenericChatMsg<T> {
|
||||
let chat_type = match self.chat_type {
|
||||
ChatType::Online(a) => ChatType::Online(a),
|
||||
|
@ -2,7 +2,7 @@ use crate::{comp::Alignment, uid::Uid};
|
||||
use hashbrown::HashMap;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use slab::Slab;
|
||||
use specs::{Component, DerefFlaggedStorage, Join, LendJoin};
|
||||
use specs::{storage::GenericReadStorage, Component, DerefFlaggedStorage, Join, LendJoin};
|
||||
use tracing::{error, warn};
|
||||
|
||||
// Primitive group system
|
||||
@ -82,7 +82,6 @@ impl<E> ChangeNotification<E> {
|
||||
}
|
||||
|
||||
type GroupsMut<'a> = specs::WriteStorage<'a, Group>;
|
||||
type Groups<'a> = specs::ReadStorage<'a, Group>;
|
||||
type Alignments<'a> = specs::ReadStorage<'a, Alignment>;
|
||||
type Uids<'a> = specs::ReadStorage<'a, Uid>;
|
||||
|
||||
@ -98,7 +97,7 @@ fn pets(
|
||||
entity: specs::Entity,
|
||||
uid: Uid,
|
||||
alignments: &Alignments,
|
||||
entities: &specs::Entities,
|
||||
entities: &specs::world::EntitiesRes,
|
||||
) -> Vec<specs::Entity> {
|
||||
(entities, alignments)
|
||||
.join()
|
||||
@ -112,7 +111,7 @@ fn pets(
|
||||
pub fn members<'a>(
|
||||
group: Group,
|
||||
groups: impl Join<Type = &'a Group> + 'a,
|
||||
entities: &'a specs::Entities,
|
||||
entities: &'a specs::world::EntitiesRes,
|
||||
alignments: &'a Alignments,
|
||||
uids: &'a Uids,
|
||||
) -> impl Iterator<Item = (specs::Entity, Role)> + 'a {
|
||||
@ -331,7 +330,7 @@ impl GroupManager {
|
||||
groups: &mut GroupsMut,
|
||||
alignments: &Alignments,
|
||||
uids: &Uids,
|
||||
entities: &specs::Entities,
|
||||
entities: &specs::world::EntitiesRes,
|
||||
notifier: &mut impl FnMut(specs::Entity, ChangeNotification<specs::Entity>),
|
||||
) {
|
||||
self.remove_from_group(member, groups, alignments, uids, entities, notifier, true);
|
||||
@ -346,7 +345,7 @@ impl GroupManager {
|
||||
groups: &mut GroupsMut,
|
||||
alignments: &Alignments,
|
||||
uids: &Uids,
|
||||
entities: &specs::Entities,
|
||||
entities: &specs::world::EntitiesRes,
|
||||
notifier: &mut impl FnMut(specs::Entity, ChangeNotification<specs::Entity>),
|
||||
to_be_deleted: bool,
|
||||
) {
|
||||
@ -493,13 +492,13 @@ impl GroupManager {
|
||||
|
||||
// Assign new group leader
|
||||
// Does nothing if new leader is not part of a group
|
||||
pub fn assign_leader(
|
||||
pub fn assign_leader<'a>(
|
||||
&mut self,
|
||||
new_leader: specs::Entity,
|
||||
groups: &Groups,
|
||||
entities: &specs::Entities,
|
||||
alignments: &Alignments,
|
||||
uids: &Uids,
|
||||
groups: impl GenericReadStorage<Component = Group> + Join<Type = &'a Group> + 'a,
|
||||
entities: &'a specs::Entities,
|
||||
alignments: &'a Alignments,
|
||||
uids: &'a Uids,
|
||||
mut notifier: impl FnMut(specs::Entity, ChangeNotification<specs::Entity>),
|
||||
) {
|
||||
let group = match groups.get(new_leader) {
|
||||
|
@ -7,7 +7,7 @@ use crate::{
|
||||
dialogue::Subject,
|
||||
invite::{InviteKind, InviteResponse},
|
||||
misc::PortalData,
|
||||
DisconnectReason, Ori, Pos,
|
||||
DisconnectReason, LootOwner, Ori, Pos, UnresolvedChatMsg, Vel,
|
||||
},
|
||||
lottery::LootSpec,
|
||||
mounting::VolumePos,
|
||||
@ -20,7 +20,7 @@ use crate::{
|
||||
Explosion,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use specs::Entity as EcsEntity;
|
||||
use specs::{Entity as EcsEntity, World};
|
||||
use std::{collections::VecDeque, ops::DerefMut, sync::Mutex};
|
||||
use uuid::Uuid;
|
||||
use vek::*;
|
||||
@ -132,82 +132,145 @@ impl NpcBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::large_enum_variant)] // TODO: Pending review in #587
|
||||
#[derive(strum::EnumDiscriminants)]
|
||||
#[strum_discriminants(repr(usize))]
|
||||
#[strum_discriminants(derive(strum::EnumVariantNames))]
|
||||
pub enum ServerEvent {
|
||||
Explosion {
|
||||
pos: Vec3<f32>,
|
||||
explosion: Explosion,
|
||||
owner: Option<Uid>,
|
||||
},
|
||||
Bonk {
|
||||
pos: Vec3<f32>,
|
||||
owner: Option<Uid>,
|
||||
target: Option<Uid>,
|
||||
},
|
||||
HealthChange {
|
||||
entity: EcsEntity,
|
||||
change: comp::HealthChange,
|
||||
},
|
||||
PoiseChange {
|
||||
entity: EcsEntity,
|
||||
change: comp::PoiseChange,
|
||||
},
|
||||
Delete(EcsEntity),
|
||||
Destroy {
|
||||
entity: EcsEntity,
|
||||
cause: comp::HealthChange,
|
||||
},
|
||||
InventoryManip(EcsEntity, comp::InventoryManip),
|
||||
GroupManip(EcsEntity, comp::GroupManip),
|
||||
Respawn(EcsEntity),
|
||||
Shoot {
|
||||
entity: EcsEntity,
|
||||
pos: Pos,
|
||||
dir: Dir,
|
||||
body: comp::Body,
|
||||
light: Option<comp::LightEmitter>,
|
||||
projectile: comp::Projectile,
|
||||
speed: f32,
|
||||
object: Option<comp::Object>,
|
||||
},
|
||||
Shockwave {
|
||||
properties: comp::shockwave::Properties,
|
||||
pos: Pos,
|
||||
ori: Ori,
|
||||
},
|
||||
Knockback {
|
||||
entity: EcsEntity,
|
||||
impulse: Vec3<f32>,
|
||||
},
|
||||
LandOnGround {
|
||||
entity: EcsEntity,
|
||||
vel: Vec3<f32>,
|
||||
surface_normal: Vec3<f32>,
|
||||
},
|
||||
EnableLantern(EcsEntity),
|
||||
DisableLantern(EcsEntity),
|
||||
NpcInteract(EcsEntity, EcsEntity, Subject),
|
||||
InviteResponse(EcsEntity, InviteResponse),
|
||||
InitiateInvite(EcsEntity, Uid, InviteKind),
|
||||
ProcessTradeAction(EcsEntity, TradeId, TradeAction),
|
||||
Mount(EcsEntity, EcsEntity),
|
||||
MountVolume(EcsEntity, VolumePos),
|
||||
Unmount(EcsEntity),
|
||||
SetPetStay(EcsEntity, EcsEntity, bool),
|
||||
Possess(Uid, Uid),
|
||||
/// Inserts default components for a character when loading into the game
|
||||
InitCharacterData {
|
||||
entity: EcsEntity,
|
||||
character_id: CharacterId,
|
||||
requested_view_distances: crate::ViewDistances,
|
||||
},
|
||||
InitSpectator(EcsEntity, crate::ViewDistances),
|
||||
UpdateCharacterData {
|
||||
entity: EcsEntity,
|
||||
components: (
|
||||
pub struct ClientConnectedEvent {
|
||||
pub entity: EcsEntity,
|
||||
}
|
||||
pub struct ClientDisconnectEvent(pub EcsEntity, pub DisconnectReason);
|
||||
pub struct ClientDisconnectWithoutPersistenceEvent(pub EcsEntity);
|
||||
|
||||
pub struct ChatEvent(pub UnresolvedChatMsg);
|
||||
pub struct CommandEvent(pub EcsEntity, pub String, pub Vec<String>);
|
||||
|
||||
// Entity Creation
|
||||
pub struct CreateWaypointEvent(pub Vec3<f32>);
|
||||
pub struct CreateTeleporterEvent(pub Vec3<f32>, pub PortalData);
|
||||
|
||||
pub struct CreateNpcEvent {
|
||||
pub pos: Pos,
|
||||
pub ori: Ori,
|
||||
pub npc: NpcBuilder,
|
||||
pub rider: Option<NpcBuilder>,
|
||||
}
|
||||
|
||||
pub struct CreateShipEvent {
|
||||
pub pos: Pos,
|
||||
pub ori: Ori,
|
||||
pub ship: comp::ship::Body,
|
||||
pub rtsim_entity: Option<RtSimEntity>,
|
||||
pub driver: Option<NpcBuilder>,
|
||||
}
|
||||
|
||||
pub struct CreateItemDropEvent {
|
||||
pub pos: Pos,
|
||||
pub vel: Vel,
|
||||
pub ori: Ori,
|
||||
pub item: comp::Item,
|
||||
pub loot_owner: Option<LootOwner>,
|
||||
}
|
||||
pub struct CreateObjectEvent {
|
||||
pub pos: Pos,
|
||||
pub vel: Vel,
|
||||
pub body: comp::object::Body,
|
||||
pub object: Option<comp::Object>,
|
||||
pub item: Option<comp::Item>,
|
||||
pub light_emitter: Option<comp::LightEmitter>,
|
||||
pub stats: Option<comp::Stats>,
|
||||
}
|
||||
|
||||
pub struct ExplosionEvent {
|
||||
pub pos: Vec3<f32>,
|
||||
pub explosion: Explosion,
|
||||
pub owner: Option<Uid>,
|
||||
}
|
||||
|
||||
pub struct BonkEvent {
|
||||
pub pos: Vec3<f32>,
|
||||
pub owner: Option<Uid>,
|
||||
pub target: Option<Uid>,
|
||||
}
|
||||
|
||||
pub struct HealthChangeEvent {
|
||||
pub entity: EcsEntity,
|
||||
pub change: comp::HealthChange,
|
||||
}
|
||||
|
||||
pub struct PoiseChangeEvent {
|
||||
pub entity: EcsEntity,
|
||||
pub change: comp::PoiseChange,
|
||||
}
|
||||
|
||||
pub struct DeleteEvent(pub EcsEntity);
|
||||
|
||||
pub struct DestroyEvent {
|
||||
pub entity: EcsEntity,
|
||||
pub cause: comp::HealthChange,
|
||||
}
|
||||
|
||||
pub struct InventoryManipEvent(pub EcsEntity, pub comp::InventoryManip);
|
||||
|
||||
pub struct GroupManipEvent(pub EcsEntity, pub comp::GroupManip);
|
||||
|
||||
pub struct RespawnEvent(pub EcsEntity);
|
||||
|
||||
pub struct ShootEvent {
|
||||
pub entity: EcsEntity,
|
||||
pub pos: Pos,
|
||||
pub dir: Dir,
|
||||
pub body: comp::Body,
|
||||
pub light: Option<comp::LightEmitter>,
|
||||
pub projectile: comp::Projectile,
|
||||
pub speed: f32,
|
||||
pub object: Option<comp::Object>,
|
||||
}
|
||||
|
||||
pub struct ShockwaveEvent {
|
||||
pub properties: comp::shockwave::Properties,
|
||||
pub pos: Pos,
|
||||
pub ori: Ori,
|
||||
}
|
||||
|
||||
pub struct KnockbackEvent {
|
||||
pub entity: EcsEntity,
|
||||
pub impulse: Vec3<f32>,
|
||||
}
|
||||
|
||||
pub struct LandOnGroundEvent {
|
||||
pub entity: EcsEntity,
|
||||
pub vel: Vec3<f32>,
|
||||
pub surface_normal: Vec3<f32>,
|
||||
}
|
||||
|
||||
pub struct SetLanternEvent(pub EcsEntity, pub bool);
|
||||
|
||||
pub struct NpcInteractEvent(pub EcsEntity, pub EcsEntity, pub Subject);
|
||||
|
||||
pub struct InviteResponseEvent(pub EcsEntity, pub InviteResponse);
|
||||
|
||||
pub struct InitiateInviteEvent(pub EcsEntity, pub Uid, pub InviteKind);
|
||||
|
||||
pub struct ProcessTradeActionEvent(pub EcsEntity, pub TradeId, pub TradeAction);
|
||||
|
||||
pub struct MountEvent(pub EcsEntity, pub EcsEntity);
|
||||
|
||||
pub struct MountVolumeEvent(pub EcsEntity, pub VolumePos);
|
||||
|
||||
pub struct UnmountEvent(pub EcsEntity);
|
||||
|
||||
pub struct SetPetStayEvent(pub EcsEntity, pub EcsEntity, pub bool);
|
||||
|
||||
pub struct PossessEvent(pub Uid, pub Uid);
|
||||
|
||||
pub struct InitializeCharacterEvent {
|
||||
pub entity: EcsEntity,
|
||||
pub character_id: CharacterId,
|
||||
pub requested_view_distances: crate::ViewDistances,
|
||||
}
|
||||
|
||||
pub struct InitializeSpectatorEvent(pub EcsEntity, pub crate::ViewDistances);
|
||||
|
||||
pub struct UpdateCharacterDataEvent {
|
||||
pub entity: EcsEntity,
|
||||
pub components: (
|
||||
comp::Body,
|
||||
comp::Stats,
|
||||
comp::SkillSet,
|
||||
@ -217,132 +280,133 @@ pub enum ServerEvent {
|
||||
comp::ActiveAbilities,
|
||||
Option<comp::MapMarker>,
|
||||
),
|
||||
metadata: UpdateCharacterMetadata,
|
||||
},
|
||||
ExitIngame {
|
||||
entity: EcsEntity,
|
||||
},
|
||||
// TODO: to avoid breakage when adding new fields, perhaps have an `NpcBuilder` type?
|
||||
CreateNpc {
|
||||
pos: Pos,
|
||||
ori: Ori,
|
||||
npc: NpcBuilder,
|
||||
rider: Option<NpcBuilder>,
|
||||
},
|
||||
CreateShip {
|
||||
pos: Pos,
|
||||
ori: Ori,
|
||||
ship: comp::ship::Body,
|
||||
rtsim_entity: Option<RtSimEntity>,
|
||||
driver: Option<NpcBuilder>,
|
||||
},
|
||||
CreateWaypoint(Vec3<f32>),
|
||||
CreateTeleporter(Vec3<f32>, PortalData),
|
||||
ClientDisconnect(EcsEntity, DisconnectReason),
|
||||
ClientDisconnectWithoutPersistence(EcsEntity),
|
||||
Command(EcsEntity, String, Vec<String>),
|
||||
/// Send a chat message to the player from an npc or other player
|
||||
Chat(comp::UnresolvedChatMsg),
|
||||
Aura {
|
||||
entity: EcsEntity,
|
||||
aura_change: comp::AuraChange,
|
||||
},
|
||||
Buff {
|
||||
entity: EcsEntity,
|
||||
buff_change: comp::BuffChange,
|
||||
},
|
||||
EnergyChange {
|
||||
entity: EcsEntity,
|
||||
change: f32,
|
||||
},
|
||||
ComboChange {
|
||||
entity: EcsEntity,
|
||||
change: i32,
|
||||
},
|
||||
ParryHook {
|
||||
defender: EcsEntity,
|
||||
attacker: Option<EcsEntity>,
|
||||
source: AttackSource,
|
||||
},
|
||||
RequestSiteInfo {
|
||||
entity: EcsEntity,
|
||||
id: SiteId,
|
||||
},
|
||||
// Attempt to mine a block, turning it into an item
|
||||
MineBlock {
|
||||
entity: EcsEntity,
|
||||
pos: Vec3<i32>,
|
||||
tool: Option<comp::tool::ToolKind>,
|
||||
},
|
||||
TeleportTo {
|
||||
entity: EcsEntity,
|
||||
target: Uid,
|
||||
max_range: Option<f32>,
|
||||
},
|
||||
CreateSafezone {
|
||||
range: Option<f32>,
|
||||
pos: Pos,
|
||||
},
|
||||
Sound {
|
||||
sound: Sound,
|
||||
},
|
||||
CreateSprite {
|
||||
pos: Vec3<i32>,
|
||||
sprite: SpriteKind,
|
||||
del_timeout: Option<(f32, f32)>,
|
||||
},
|
||||
TamePet {
|
||||
pet_entity: EcsEntity,
|
||||
owner_entity: EcsEntity,
|
||||
},
|
||||
EntityAttackedHook {
|
||||
entity: EcsEntity,
|
||||
attacker: Option<EcsEntity>,
|
||||
},
|
||||
ChangeAbility {
|
||||
entity: EcsEntity,
|
||||
slot: usize,
|
||||
auxiliary_key: comp::ability::AuxiliaryKey,
|
||||
new_ability: comp::ability::AuxiliaryAbility,
|
||||
},
|
||||
UpdateMapMarker {
|
||||
entity: EcsEntity,
|
||||
update: comp::MapMarkerChange,
|
||||
},
|
||||
MakeAdmin {
|
||||
entity: EcsEntity,
|
||||
admin: comp::Admin,
|
||||
uuid: Uuid,
|
||||
},
|
||||
DeleteCharacter {
|
||||
entity: EcsEntity,
|
||||
requesting_player_uuid: String,
|
||||
character_id: CharacterId,
|
||||
},
|
||||
ChangeStance {
|
||||
entity: EcsEntity,
|
||||
stance: comp::Stance,
|
||||
},
|
||||
ChangeBody {
|
||||
entity: EcsEntity,
|
||||
new_body: comp::Body,
|
||||
},
|
||||
RemoveLightEmitter {
|
||||
entity: EcsEntity,
|
||||
},
|
||||
TeleportToPosition {
|
||||
entity: EcsEntity,
|
||||
position: Vec3<f32>,
|
||||
},
|
||||
StartTeleporting {
|
||||
entity: EcsEntity,
|
||||
portal: EcsEntity,
|
||||
},
|
||||
ToggleSpriteLight {
|
||||
entity: EcsEntity,
|
||||
pos: Vec3<i32>,
|
||||
enable: bool,
|
||||
},
|
||||
pub metadata: UpdateCharacterMetadata,
|
||||
}
|
||||
|
||||
pub struct ExitIngameEvent {
|
||||
pub entity: EcsEntity,
|
||||
}
|
||||
|
||||
pub struct AuraEvent {
|
||||
pub entity: EcsEntity,
|
||||
pub aura_change: comp::AuraChange,
|
||||
}
|
||||
|
||||
pub struct BuffEvent {
|
||||
pub entity: EcsEntity,
|
||||
pub buff_change: comp::BuffChange,
|
||||
}
|
||||
|
||||
pub struct EnergyChangeEvent {
|
||||
pub entity: EcsEntity,
|
||||
pub change: f32,
|
||||
}
|
||||
|
||||
pub struct ComboChangeEvent {
|
||||
pub entity: EcsEntity,
|
||||
pub change: i32,
|
||||
}
|
||||
|
||||
pub struct ParryHookEvent {
|
||||
pub defender: EcsEntity,
|
||||
pub attacker: Option<EcsEntity>,
|
||||
pub source: AttackSource,
|
||||
}
|
||||
|
||||
pub struct RequestSiteInfoEvent {
|
||||
pub entity: EcsEntity,
|
||||
pub id: SiteId,
|
||||
}
|
||||
|
||||
// Attempt to mine a block, turning it into an item
|
||||
pub struct MineBlockEvent {
|
||||
pub entity: EcsEntity,
|
||||
pub pos: Vec3<i32>,
|
||||
pub tool: Option<comp::tool::ToolKind>,
|
||||
}
|
||||
|
||||
pub struct TeleportToEvent {
|
||||
pub entity: EcsEntity,
|
||||
pub target: Uid,
|
||||
pub max_range: Option<f32>,
|
||||
}
|
||||
|
||||
pub struct CreateSafezoneEvent {
|
||||
pub range: Option<f32>,
|
||||
pub pos: Pos,
|
||||
}
|
||||
|
||||
pub struct SoundEvent {
|
||||
pub sound: Sound,
|
||||
}
|
||||
|
||||
pub struct CreateSpriteEvent {
|
||||
pub pos: Vec3<i32>,
|
||||
pub sprite: SpriteKind,
|
||||
pub del_timeout: Option<(f32, f32)>,
|
||||
}
|
||||
|
||||
pub struct TamePetEvent {
|
||||
pub pet_entity: EcsEntity,
|
||||
pub owner_entity: EcsEntity,
|
||||
}
|
||||
|
||||
pub struct EntityAttackedHookEvent {
|
||||
pub entity: EcsEntity,
|
||||
pub attacker: Option<EcsEntity>,
|
||||
}
|
||||
|
||||
pub struct ChangeAbilityEvent {
|
||||
pub entity: EcsEntity,
|
||||
pub slot: usize,
|
||||
pub auxiliary_key: comp::ability::AuxiliaryKey,
|
||||
pub new_ability: comp::ability::AuxiliaryAbility,
|
||||
}
|
||||
|
||||
pub struct UpdateMapMarkerEvent {
|
||||
pub entity: EcsEntity,
|
||||
pub update: comp::MapMarkerChange,
|
||||
}
|
||||
|
||||
pub struct MakeAdminEvent {
|
||||
pub entity: EcsEntity,
|
||||
pub admin: comp::Admin,
|
||||
pub uuid: Uuid,
|
||||
}
|
||||
|
||||
pub struct DeleteCharacterEvent {
|
||||
pub entity: EcsEntity,
|
||||
pub requesting_player_uuid: String,
|
||||
pub character_id: CharacterId,
|
||||
}
|
||||
|
||||
pub struct ChangeStanceEvent {
|
||||
pub entity: EcsEntity,
|
||||
pub stance: comp::Stance,
|
||||
}
|
||||
|
||||
pub struct ChangeBodyEvent {
|
||||
pub entity: EcsEntity,
|
||||
pub new_body: comp::Body,
|
||||
}
|
||||
|
||||
pub struct RemoveLightEmitterEvent {
|
||||
pub entity: EcsEntity,
|
||||
}
|
||||
|
||||
pub struct TeleportToPositionEvent {
|
||||
pub entity: EcsEntity,
|
||||
pub position: Vec3<f32>,
|
||||
}
|
||||
|
||||
pub struct StartTeleportingEvent {
|
||||
pub entity: EcsEntity,
|
||||
pub portal: EcsEntity,
|
||||
}
|
||||
pub struct ToggleSpriteLightEvent {
|
||||
pub entity: EcsEntity,
|
||||
pub pos: Vec3<i32>,
|
||||
pub enable: bool,
|
||||
}
|
||||
|
||||
pub struct EventBus<E> {
|
||||
@ -370,11 +434,15 @@ impl<E> EventBus<E> {
|
||||
pub fn recv_all(&self) -> impl ExactSizeIterator<Item = E> {
|
||||
std::mem::take(self.queue.lock().unwrap().deref_mut()).into_iter()
|
||||
}
|
||||
|
||||
pub fn recv_all_mut(&mut self) -> impl ExactSizeIterator<Item = E> {
|
||||
std::mem::take(self.queue.get_mut().unwrap()).into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Emitter<'a, E> {
|
||||
bus: &'a EventBus<E>,
|
||||
events: VecDeque<E>,
|
||||
pub events: VecDeque<E>,
|
||||
}
|
||||
|
||||
impl<'a, E> Emitter<'a, E> {
|
||||
@ -395,3 +463,137 @@ impl<'a, E> Drop for Emitter<'a, E> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait EmitExt<E> {
|
||||
fn emit(&mut self, event: E);
|
||||
fn emit_many(&mut self, events: impl IntoIterator<Item = E>);
|
||||
}
|
||||
|
||||
pub fn register_event_busses(ecs: &mut World) {
|
||||
ecs.insert(EventBus::<ClientConnectedEvent>::default());
|
||||
ecs.insert(EventBus::<ClientDisconnectEvent>::default());
|
||||
ecs.insert(EventBus::<ClientDisconnectWithoutPersistenceEvent>::default());
|
||||
ecs.insert(EventBus::<ChatEvent>::default());
|
||||
ecs.insert(EventBus::<CommandEvent>::default());
|
||||
ecs.insert(EventBus::<CreateWaypointEvent>::default());
|
||||
ecs.insert(EventBus::<CreateTeleporterEvent>::default());
|
||||
ecs.insert(EventBus::<CreateNpcEvent>::default());
|
||||
ecs.insert(EventBus::<CreateShipEvent>::default());
|
||||
ecs.insert(EventBus::<CreateItemDropEvent>::default());
|
||||
ecs.insert(EventBus::<CreateObjectEvent>::default());
|
||||
ecs.insert(EventBus::<ExplosionEvent>::default());
|
||||
ecs.insert(EventBus::<BonkEvent>::default());
|
||||
ecs.insert(EventBus::<HealthChangeEvent>::default());
|
||||
ecs.insert(EventBus::<PoiseChangeEvent>::default());
|
||||
ecs.insert(EventBus::<DeleteEvent>::default());
|
||||
ecs.insert(EventBus::<DestroyEvent>::default());
|
||||
ecs.insert(EventBus::<InventoryManipEvent>::default());
|
||||
ecs.insert(EventBus::<GroupManipEvent>::default());
|
||||
ecs.insert(EventBus::<RespawnEvent>::default());
|
||||
ecs.insert(EventBus::<ShootEvent>::default());
|
||||
ecs.insert(EventBus::<ShockwaveEvent>::default());
|
||||
ecs.insert(EventBus::<KnockbackEvent>::default());
|
||||
ecs.insert(EventBus::<LandOnGroundEvent>::default());
|
||||
ecs.insert(EventBus::<SetLanternEvent>::default());
|
||||
ecs.insert(EventBus::<NpcInteractEvent>::default());
|
||||
ecs.insert(EventBus::<InviteResponseEvent>::default());
|
||||
ecs.insert(EventBus::<InitiateInviteEvent>::default());
|
||||
ecs.insert(EventBus::<ProcessTradeActionEvent>::default());
|
||||
ecs.insert(EventBus::<MountEvent>::default());
|
||||
ecs.insert(EventBus::<MountVolumeEvent>::default());
|
||||
ecs.insert(EventBus::<UnmountEvent>::default());
|
||||
ecs.insert(EventBus::<SetPetStayEvent>::default());
|
||||
ecs.insert(EventBus::<PossessEvent>::default());
|
||||
ecs.insert(EventBus::<InitializeCharacterEvent>::default());
|
||||
ecs.insert(EventBus::<InitializeSpectatorEvent>::default());
|
||||
ecs.insert(EventBus::<UpdateCharacterDataEvent>::default());
|
||||
ecs.insert(EventBus::<ExitIngameEvent>::default());
|
||||
ecs.insert(EventBus::<AuraEvent>::default());
|
||||
ecs.insert(EventBus::<BuffEvent>::default());
|
||||
ecs.insert(EventBus::<EnergyChangeEvent>::default());
|
||||
ecs.insert(EventBus::<ComboChangeEvent>::default());
|
||||
ecs.insert(EventBus::<ParryHookEvent>::default());
|
||||
ecs.insert(EventBus::<RequestSiteInfoEvent>::default());
|
||||
ecs.insert(EventBus::<MineBlockEvent>::default());
|
||||
ecs.insert(EventBus::<TeleportToEvent>::default());
|
||||
ecs.insert(EventBus::<CreateSafezoneEvent>::default());
|
||||
ecs.insert(EventBus::<SoundEvent>::default());
|
||||
ecs.insert(EventBus::<CreateSpriteEvent>::default());
|
||||
ecs.insert(EventBus::<TamePetEvent>::default());
|
||||
ecs.insert(EventBus::<EntityAttackedHookEvent>::default());
|
||||
ecs.insert(EventBus::<ChangeAbilityEvent>::default());
|
||||
ecs.insert(EventBus::<UpdateMapMarkerEvent>::default());
|
||||
ecs.insert(EventBus::<MakeAdminEvent>::default());
|
||||
ecs.insert(EventBus::<DeleteCharacterEvent>::default());
|
||||
ecs.insert(EventBus::<ChangeStanceEvent>::default());
|
||||
ecs.insert(EventBus::<ChangeBodyEvent>::default());
|
||||
ecs.insert(EventBus::<RemoveLightEmitterEvent>::default());
|
||||
ecs.insert(EventBus::<TeleportToPositionEvent>::default());
|
||||
ecs.insert(EventBus::<StartTeleportingEvent>::default());
|
||||
ecs.insert(EventBus::<ToggleSpriteLightEvent>::default());
|
||||
}
|
||||
|
||||
/// Define ecs read data for event busses. And a way to convert them all to
|
||||
/// emitters.
|
||||
///
|
||||
/// # Example:
|
||||
/// ```
|
||||
/// mod some_mod_is_necessary_for_the_test {
|
||||
/// use veloren_common::event_emitters;
|
||||
/// pub struct Foo;
|
||||
/// pub struct Bar;
|
||||
/// pub struct Baz;
|
||||
/// event_emitters!(
|
||||
/// pub struct ReadEvents[EventEmitters] {
|
||||
/// foo: Foo, bar: Bar, baz: Baz,
|
||||
/// }
|
||||
/// );
|
||||
/// }
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! event_emitters {
|
||||
($($vis:vis struct $read_data:ident[$emitters:ident] { $($ev_ident:ident: $ty:ty),+ $(,)? })+) => {
|
||||
mod event_emitters {
|
||||
use super::*;
|
||||
use specs::shred;
|
||||
$(
|
||||
#[derive(specs::SystemData)]
|
||||
pub struct $read_data<'a> {
|
||||
$($ev_ident: Option<specs::Read<'a, $crate::event::EventBus<$ty>>>),+
|
||||
}
|
||||
|
||||
impl<'a> $read_data<'a> {
|
||||
#[allow(unused)]
|
||||
pub fn get_emitters(&self) -> $emitters {
|
||||
$emitters {
|
||||
$($ev_ident: self.$ev_ident.as_ref().map(|e| e.emitter())),+
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct $emitters<'a> {
|
||||
$($ev_ident: Option<$crate::event::Emitter<'a, $ty>>),+
|
||||
}
|
||||
|
||||
impl<'a> $emitters<'a> {
|
||||
#[allow(unused)]
|
||||
pub fn append(&mut self, mut other: Self) {
|
||||
$(
|
||||
self.$ev_ident.as_mut().zip(other.$ev_ident).map(|(a, mut b)| a.append(&mut b.events));
|
||||
)+
|
||||
}
|
||||
}
|
||||
|
||||
$(
|
||||
impl<'a> $crate::event::EmitExt<$ty> for $emitters<'a> {
|
||||
fn emit(&mut self, event: $ty) { self.$ev_ident.as_mut().map(|e| e.emit(event)); }
|
||||
fn emit_many(&mut self, events: impl IntoIterator<Item = $ty>) { self.$ev_ident.as_mut().map(|e| e.emit_many(events)); }
|
||||
}
|
||||
)+
|
||||
)+
|
||||
}
|
||||
$(
|
||||
$vis use event_emitters::{$read_data, $emitters};
|
||||
)+
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ use crate::{
|
||||
character_state::OutputEvents,
|
||||
CharacterState, StateUpdate,
|
||||
},
|
||||
event::ServerEvent,
|
||||
event::{AuraEvent, ComboChangeEvent},
|
||||
resources::Secs,
|
||||
states::{
|
||||
behavior::{CharacterBehavior, JoinData},
|
||||
@ -95,12 +95,12 @@ impl CharacterBehavior for Data {
|
||||
(self.static_data.combo_at_cast.max(1) as f32).sqrt();
|
||||
},
|
||||
}
|
||||
output_events.emit_server(ServerEvent::ComboChange {
|
||||
output_events.emit_server(ComboChangeEvent {
|
||||
entity: data.entity,
|
||||
change: -(self.static_data.combo_at_cast as i32),
|
||||
});
|
||||
}
|
||||
output_events.emit_server(ServerEvent::Aura {
|
||||
output_events.emit_server(AuraEvent {
|
||||
entity: data.entity,
|
||||
aura_change: AuraChange::Add(aura),
|
||||
});
|
||||
|
@ -5,7 +5,7 @@ use crate::{
|
||||
object::Body::{GrenadeClay, LaserBeam},
|
||||
Body, CharacterState, LightEmitter, Pos, ProjectileConstructor, StateUpdate,
|
||||
},
|
||||
event::{LocalEvent, ServerEvent},
|
||||
event::{LocalEvent, ShootEvent},
|
||||
outcome::Outcome,
|
||||
states::{
|
||||
behavior::{CharacterBehavior, JoinData},
|
||||
@ -127,7 +127,7 @@ impl CharacterBehavior for Data {
|
||||
}))
|
||||
.unwrap_or(data.inputs.look_dir);
|
||||
// Tells server to create and shoot the projectile
|
||||
output_events.emit_server(ServerEvent::Shoot {
|
||||
output_events.emit_server(ShootEvent {
|
||||
entity: data.entity,
|
||||
pos,
|
||||
dir,
|
||||
|
@ -9,7 +9,7 @@ use crate::{
|
||||
Body::Object,
|
||||
CharacterState, Projectile, StateUpdate,
|
||||
},
|
||||
event::{LocalEvent, NpcBuilder, ServerEvent},
|
||||
event::{CreateNpcEvent, LocalEvent, NpcBuilder},
|
||||
npc::NPC_NAMES,
|
||||
outcome::Outcome,
|
||||
skillset_builder::{self, SkillSetBuilder},
|
||||
@ -197,7 +197,7 @@ impl CharacterBehavior for Data {
|
||||
|
||||
let mut rng = rand::thread_rng();
|
||||
// Send server event to create npc
|
||||
output_events.emit_server(ServerEvent::CreateNpc {
|
||||
output_events.emit_server(CreateNpcEvent {
|
||||
pos: comp::Pos(
|
||||
collision_vector - Vec3::unit_z() * obstacle_z + extra_height,
|
||||
),
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::{
|
||||
comp::{character_state::OutputEvents, CharacterState, StateUpdate},
|
||||
event::ServerEvent,
|
||||
event::TeleportToEvent,
|
||||
states::{
|
||||
behavior::{CharacterBehavior, JoinData},
|
||||
utils::*,
|
||||
@ -52,7 +52,7 @@ impl CharacterBehavior for Data {
|
||||
// provided
|
||||
if let Some(input_attr) = self.static_data.ability_info.input_attr {
|
||||
if let Some(target) = input_attr.target_entity {
|
||||
output_events.emit_server(ServerEvent::TeleportTo {
|
||||
output_events.emit_server(TeleportToEvent {
|
||||
entity: data.entity,
|
||||
target,
|
||||
max_range: Some(self.static_data.max_range),
|
||||
|
@ -4,7 +4,7 @@ use crate::{
|
||||
character_state::OutputEvents, projectile::ProjectileConstructor, Body, CharacterState,
|
||||
LightEmitter, Pos, StateUpdate,
|
||||
},
|
||||
event::ServerEvent,
|
||||
event::ShootEvent,
|
||||
states::{
|
||||
behavior::{CharacterBehavior, JoinData},
|
||||
utils::*,
|
||||
@ -123,7 +123,7 @@ impl CharacterBehavior for Data {
|
||||
tool_stats,
|
||||
self.static_data.damage_effect,
|
||||
);
|
||||
output_events.emit_server(ServerEvent::Shoot {
|
||||
output_events.emit_server(ShootEvent {
|
||||
entity: data.entity,
|
||||
pos,
|
||||
dir: data.inputs.look_dir,
|
||||
|
@ -9,7 +9,7 @@ use crate::{
|
||||
shockwave::{self, ShockwaveDodgeable},
|
||||
CharacterState, StateUpdate,
|
||||
},
|
||||
event::{LocalEvent, ServerEvent},
|
||||
event::{ExplosionEvent, LocalEvent, ShockwaveEvent},
|
||||
outcome::Outcome,
|
||||
states::{
|
||||
behavior::{CharacterBehavior, JoinData},
|
||||
@ -182,7 +182,7 @@ impl CharacterBehavior for Data {
|
||||
owner: Some(*data.uid),
|
||||
specifier: self.static_data.specifier,
|
||||
};
|
||||
output_events.emit_server(ServerEvent::Shockwave {
|
||||
output_events.emit_server(ShockwaveEvent {
|
||||
properties,
|
||||
pos: *data.pos,
|
||||
ori: *data.ori,
|
||||
@ -214,7 +214,7 @@ impl CharacterBehavior for Data {
|
||||
reagent: Some(Reagent::White),
|
||||
min_falloff: 0.5,
|
||||
};
|
||||
output_events.emit_server(ServerEvent::Explosion {
|
||||
output_events.emit_server(ExplosionEvent {
|
||||
pos: data.pos.0,
|
||||
explosion,
|
||||
owner: Some(*data.uid),
|
||||
|
@ -1,7 +1,7 @@
|
||||
use crate::{
|
||||
combat,
|
||||
comp::{character_state::OutputEvents, CharacterState, MeleeConstructor, StateUpdate},
|
||||
event::ServerEvent,
|
||||
event::ComboChangeEvent,
|
||||
states::{
|
||||
behavior::{CharacterBehavior, JoinData},
|
||||
utils::*,
|
||||
@ -117,7 +117,7 @@ impl CharacterBehavior for Data {
|
||||
|
||||
// Consume combo if any was required
|
||||
if self.static_data.minimum_combo > 0 {
|
||||
output_events.emit_server(ServerEvent::ComboChange {
|
||||
output_events.emit_server(ComboChangeEvent {
|
||||
entity: data.entity,
|
||||
change: -data.combo.map_or(0, |c| c.counter() as i32),
|
||||
});
|
||||
|
@ -4,7 +4,7 @@ use crate::{
|
||||
character_state::OutputEvents, Body, CharacterState, LightEmitter, Pos,
|
||||
ProjectileConstructor, StateUpdate,
|
||||
},
|
||||
event::ServerEvent,
|
||||
event::{EnergyChangeEvent, ShootEvent},
|
||||
states::{
|
||||
behavior::{CharacterBehavior, JoinData},
|
||||
utils::{StageSection, *},
|
||||
@ -144,7 +144,7 @@ impl CharacterBehavior for Data {
|
||||
tool_stats,
|
||||
self.static_data.damage_effect,
|
||||
);
|
||||
output_events.emit_server(ServerEvent::Shoot {
|
||||
output_events.emit_server(ShootEvent {
|
||||
entity: data.entity,
|
||||
pos,
|
||||
dir: direction,
|
||||
@ -156,7 +156,7 @@ impl CharacterBehavior for Data {
|
||||
});
|
||||
|
||||
// Removes energy from character when arrow is fired
|
||||
output_events.emit_server(ServerEvent::EnergyChange {
|
||||
output_events.emit_server(EnergyChangeEvent {
|
||||
entity: data.entity,
|
||||
change: -self.static_data.energy_cost,
|
||||
});
|
||||
|
@ -4,7 +4,7 @@ use crate::{
|
||||
character_state::{AttackFilters, OutputEvents},
|
||||
CharacterState, StateUpdate,
|
||||
},
|
||||
event::ServerEvent,
|
||||
event::BuffEvent,
|
||||
states::{
|
||||
behavior::{CharacterBehavior, JoinData},
|
||||
utils::*,
|
||||
@ -69,7 +69,7 @@ impl CharacterBehavior for Data {
|
||||
});
|
||||
} else {
|
||||
// Remove burning effect if active
|
||||
output_events.emit_server(ServerEvent::Buff {
|
||||
output_events.emit_server(BuffEvent {
|
||||
entity: data.entity,
|
||||
buff_change: BuffChange::RemoveByKind(BuffKind::Burning),
|
||||
});
|
||||
|
@ -4,7 +4,7 @@ use crate::{
|
||||
character_state::OutputEvents,
|
||||
CharacterState, StateUpdate,
|
||||
},
|
||||
event::{LocalEvent, ServerEvent},
|
||||
event::{BuffEvent, ComboChangeEvent, LocalEvent},
|
||||
outcome::Outcome,
|
||||
resources::Secs,
|
||||
states::{
|
||||
@ -79,7 +79,7 @@ impl CharacterBehavior for Data {
|
||||
} else {
|
||||
self.static_data.combo_cost
|
||||
};
|
||||
output_events.emit_server(ServerEvent::ComboChange {
|
||||
output_events.emit_server(ComboChangeEvent {
|
||||
entity: data.entity,
|
||||
change: -(combo_consumption as i32),
|
||||
});
|
||||
@ -106,7 +106,7 @@ impl CharacterBehavior for Data {
|
||||
if self.static_data.enforced_limit {
|
||||
buff_cat_ids.push(BuffCategory::SelfBuff);
|
||||
|
||||
output_events.emit_server(ServerEvent::Buff {
|
||||
output_events.emit_server(BuffEvent {
|
||||
entity: data.entity,
|
||||
buff_change: BuffChange::RemoveByCategory {
|
||||
all_required: vec![BuffCategory::SelfBuff],
|
||||
@ -128,7 +128,7 @@ impl CharacterBehavior for Data {
|
||||
*data.time,
|
||||
Some(data.stats),
|
||||
);
|
||||
output_events.emit_server(ServerEvent::Buff {
|
||||
output_events.emit_server(BuffEvent {
|
||||
entity: data.entity,
|
||||
buff_change: BuffChange::Add(buff),
|
||||
});
|
||||
|
@ -8,7 +8,7 @@ use crate::{
|
||||
shockwave::{self, ShockwaveDodgeable},
|
||||
CharacterState, StateUpdate,
|
||||
},
|
||||
event::{LocalEvent, ServerEvent},
|
||||
event::{LocalEvent, ShockwaveEvent},
|
||||
outcome::Outcome,
|
||||
states::{
|
||||
behavior::{CharacterBehavior, JoinData},
|
||||
@ -124,7 +124,7 @@ impl CharacterBehavior for Data {
|
||||
owner: Some(*data.uid),
|
||||
specifier: self.static_data.specifier,
|
||||
};
|
||||
output_events.emit_server(ServerEvent::Shockwave {
|
||||
output_events.emit_server(ShockwaveEvent {
|
||||
properties,
|
||||
pos: *data.pos,
|
||||
ori: *data.ori,
|
||||
|
@ -4,7 +4,7 @@ use crate::{
|
||||
character_state::OutputEvents, controller::InputKind, item::ItemDefinitionIdOwned,
|
||||
slot::InvSlotId, CharacterState, InventoryManip, StateUpdate,
|
||||
},
|
||||
event::{LocalEvent, ServerEvent},
|
||||
event::{InventoryManipEvent, LocalEvent, ToggleSpriteLightEvent},
|
||||
outcome::Outcome,
|
||||
states::behavior::{CharacterBehavior, JoinData},
|
||||
terrain::SpriteKind,
|
||||
@ -117,17 +117,16 @@ impl CharacterBehavior for Data {
|
||||
sprite_pos: self.static_data.sprite_pos,
|
||||
required_item: inv_slot,
|
||||
};
|
||||
|
||||
match self.static_data.sprite_kind {
|
||||
SpriteInteractKind::ToggleLight(enable) => {
|
||||
output_events.emit_server(ServerEvent::ToggleSpriteLight {
|
||||
output_events.emit_server(ToggleSpriteLightEvent {
|
||||
entity: data.entity,
|
||||
pos: self.static_data.sprite_pos,
|
||||
enable,
|
||||
})
|
||||
},
|
||||
_ => output_events
|
||||
.emit_server(ServerEvent::InventoryManip(data.entity, inv_manip)),
|
||||
.emit_server(InventoryManipEvent(data.entity, inv_manip)),
|
||||
}
|
||||
|
||||
if matches!(self.static_data.sprite_kind, SpriteInteractKind::Unlock) {
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::{
|
||||
comp::{character_state::OutputEvents, CharacterState, StateUpdate},
|
||||
event::{LocalEvent, ServerEvent},
|
||||
event::{CreateSpriteEvent, LocalEvent},
|
||||
outcome::Outcome,
|
||||
spiral::Spiral2d,
|
||||
states::{
|
||||
@ -173,7 +173,7 @@ impl CharacterBehavior for Data {
|
||||
};
|
||||
for i in 0..layers {
|
||||
// Send server event to create sprite
|
||||
output_events.emit_server(ServerEvent::CreateSprite {
|
||||
output_events.emit_server(CreateSpriteEvent {
|
||||
pos: Vec3::new(sprite_pos.x, sprite_pos.y, z + i),
|
||||
sprite: self.static_data.sprite,
|
||||
del_timeout: self.static_data.del_timeout,
|
||||
|
@ -10,7 +10,7 @@ use crate::{
|
||||
},
|
||||
CharacterState, InventoryManip, StateUpdate,
|
||||
},
|
||||
event::ServerEvent,
|
||||
event::{BuffEvent, InventoryManipEvent},
|
||||
states::behavior::{CharacterBehavior, JoinData},
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
@ -141,11 +141,11 @@ impl CharacterBehavior for Data {
|
||||
|
||||
if matches!(update.character, CharacterState::Roll(_)) {
|
||||
// Remove potion/saturation effect if left the use item state early by rolling
|
||||
output_events.emit_server(ServerEvent::Buff {
|
||||
output_events.emit_server(BuffEvent {
|
||||
entity: data.entity,
|
||||
buff_change: BuffChange::RemoveByKind(BuffKind::Potion),
|
||||
});
|
||||
output_events.emit_server(ServerEvent::Buff {
|
||||
output_events.emit_server(BuffEvent {
|
||||
entity: data.entity,
|
||||
buff_change: BuffChange::RemoveByKind(BuffKind::Saturation),
|
||||
});
|
||||
@ -217,6 +217,6 @@ fn use_item(data: &JoinData, output_events: &mut OutputEvents, state: &Data) {
|
||||
if item_is_same {
|
||||
// Create inventory manipulation event
|
||||
let inv_manip = InventoryManip::Use(Slot::Inventory(state.static_data.inv_slot));
|
||||
output_events.emit_server(ServerEvent::InventoryManip(data.entity, inv_manip));
|
||||
output_events.emit_server(InventoryManipEvent(data.entity, inv_manip));
|
||||
}
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ use crate::{
|
||||
StateUpdate,
|
||||
},
|
||||
consts::{FRIC_GROUND, GRAVITY, MAX_PICKUP_RANGE},
|
||||
event::{LocalEvent, ServerEvent},
|
||||
event::{BuffEvent, ChangeStanceEvent, ComboChangeEvent, InventoryManipEvent, LocalEvent},
|
||||
mounting::Volume,
|
||||
outcome::Outcome,
|
||||
states::{behavior::JoinData, utils::CharacterState::Idle, *},
|
||||
@ -1058,7 +1058,7 @@ pub fn handle_manipulate_loadout(
|
||||
} else {
|
||||
// Else emit inventory action instantaneously
|
||||
let inv_manip = InventoryManip::Use(slot);
|
||||
output_events.emit_server(ServerEvent::InventoryManip(data.entity, inv_manip));
|
||||
output_events.emit_server(InventoryManipEvent(data.entity, inv_manip));
|
||||
}
|
||||
},
|
||||
InventoryAction::Collect(sprite_pos) => {
|
||||
@ -1137,21 +1137,18 @@ pub fn handle_manipulate_loadout(
|
||||
// For inventory actions without a dedicated character state, just do action instantaneously
|
||||
InventoryAction::Swap(equip, slot) => {
|
||||
let inv_manip = InventoryManip::Swap(Slot::Equip(equip), slot);
|
||||
output_events.emit_server(ServerEvent::InventoryManip(data.entity, inv_manip));
|
||||
output_events.emit_server(InventoryManipEvent(data.entity, inv_manip));
|
||||
},
|
||||
InventoryAction::Drop(equip) => {
|
||||
let inv_manip = InventoryManip::Drop(Slot::Equip(equip));
|
||||
output_events.emit_server(ServerEvent::InventoryManip(data.entity, inv_manip));
|
||||
output_events.emit_server(InventoryManipEvent(data.entity, inv_manip));
|
||||
},
|
||||
InventoryAction::Sort => {
|
||||
output_events.emit_server(ServerEvent::InventoryManip(
|
||||
data.entity,
|
||||
InventoryManip::Sort,
|
||||
));
|
||||
output_events.emit_server(InventoryManipEvent(data.entity, InventoryManip::Sort));
|
||||
},
|
||||
InventoryAction::Use(slot @ Slot::Equip(_)) => {
|
||||
let inv_manip = InventoryManip::Use(slot);
|
||||
output_events.emit_server(ServerEvent::InventoryManip(data.entity, inv_manip));
|
||||
output_events.emit_server(InventoryManipEvent(data.entity, inv_manip));
|
||||
},
|
||||
InventoryAction::Use(Slot::Overflow(_)) => {
|
||||
// Items in overflow slots cannot be used until moved to a real slot
|
||||
@ -1279,7 +1276,7 @@ fn handle_ability(
|
||||
if let Some(init_event) = ability.ability_meta().init_event {
|
||||
match init_event {
|
||||
AbilityInitEvent::EnterStance(stance) => {
|
||||
output_events.emit_server(ServerEvent::ChangeStance {
|
||||
output_events.emit_server(ChangeStanceEvent {
|
||||
entity: data.entity,
|
||||
stance,
|
||||
});
|
||||
@ -1614,7 +1611,7 @@ impl HandInfo {
|
||||
|
||||
pub fn leave_stance(data: &JoinData<'_>, output_events: &mut OutputEvents) {
|
||||
if !matches!(data.stance, Some(Stance::None)) {
|
||||
output_events.emit_server(ServerEvent::ChangeStance {
|
||||
output_events.emit_server(ChangeStanceEvent {
|
||||
entity: data.entity,
|
||||
stance: Stance::None,
|
||||
});
|
||||
@ -1653,7 +1650,7 @@ impl ComboConsumption {
|
||||
Self::All => combo,
|
||||
Self::Half => (combo + 1) / 2,
|
||||
};
|
||||
output_events.emit_server(ServerEvent::ComboChange {
|
||||
output_events.emit_server(ComboChangeEvent {
|
||||
entity: data.entity,
|
||||
change: -(to_consume as i32),
|
||||
});
|
||||
@ -1663,13 +1660,13 @@ impl ComboConsumption {
|
||||
fn loadout_change_hook(data: &JoinData<'_>, output_events: &mut OutputEvents, clear_combo: bool) {
|
||||
if clear_combo {
|
||||
// Reset combo to 0
|
||||
output_events.emit_server(ServerEvent::ComboChange {
|
||||
output_events.emit_server(ComboChangeEvent {
|
||||
entity: data.entity,
|
||||
change: -data.combo.map_or(0, |c| c.counter() as i32),
|
||||
});
|
||||
}
|
||||
// Clear any buffs from equipped weapons
|
||||
output_events.emit_server(ServerEvent::Buff {
|
||||
output_events.emit_server(BuffEvent {
|
||||
entity: data.entity,
|
||||
buff_change: BuffChange::RemoveByCategory {
|
||||
all_required: vec![BuffCategory::RemoveOnLoadoutChange],
|
||||
|
@ -8,4 +8,4 @@ mod special_areas;
|
||||
mod state;
|
||||
// TODO: breakup state module and remove glob
|
||||
pub use special_areas::*;
|
||||
pub use state::{BlockChange, BlockDiff, State, TerrainChanges};
|
||||
pub use state::{BlockChange, BlockDiff, ScheduledBlockChange, State, TerrainChanges};
|
||||
|
@ -8,7 +8,7 @@ use common::uid::IdMaps;
|
||||
use common::{
|
||||
calendar::Calendar,
|
||||
comp,
|
||||
event::{EventBus, LocalEvent, ServerEvent},
|
||||
event::{EventBus, LocalEvent},
|
||||
link::Is,
|
||||
mounting::{Mount, Rider, VolumeRider, VolumeRiders},
|
||||
outcome::Outcome,
|
||||
@ -331,7 +331,6 @@ impl State {
|
||||
ecs.insert(SlowJobPool::new(slow_limit, 10_000, thread_pool));
|
||||
|
||||
// TODO: only register on the server
|
||||
ecs.insert(EventBus::<ServerEvent>::default());
|
||||
ecs.insert(comp::group::GroupManager::default());
|
||||
ecs.insert(SysMetrics::default());
|
||||
ecs.insert(PhysicsMetrics::default());
|
||||
@ -417,6 +416,15 @@ impl State {
|
||||
self.ecs.read_storage().get(entity).copied()
|
||||
}
|
||||
|
||||
/// # Panics
|
||||
/// Panics if `EventBus<E>` is borrowed
|
||||
pub fn emit_event_now<E>(&self, event: E)
|
||||
where
|
||||
EventBus<E>: Resource,
|
||||
{
|
||||
self.ecs.write_resource::<EventBus<E>>().emit_now(event)
|
||||
}
|
||||
|
||||
/// Given mutable access to the resource R, assuming the resource
|
||||
/// component exists (this is already the behavior of functions like `fetch`
|
||||
/// and `write_component_ignore_entity_dead`). Since all of our resources
|
||||
@ -685,9 +693,7 @@ impl State {
|
||||
self.dispatcher.dispatch(&self.ecs);
|
||||
drop(guard);
|
||||
|
||||
section_span!(guard, "maintain ecs");
|
||||
self.ecs.maintain();
|
||||
drop(guard);
|
||||
self.maintain_ecs();
|
||||
|
||||
if update_terrain {
|
||||
self.apply_terrain_changes_internal(true, block_update);
|
||||
@ -730,6 +736,11 @@ impl State {
|
||||
drop(guard);
|
||||
}
|
||||
|
||||
pub fn maintain_ecs(&mut self) {
|
||||
span!(_guard, "maintain ecs");
|
||||
self.ecs.maintain();
|
||||
}
|
||||
|
||||
/// Clean up the state after a tick.
|
||||
pub fn cleanup(&mut self) {
|
||||
span!(_guard, "cleanup", "State::cleanup");
|
||||
|
@ -6,19 +6,27 @@ use common::{
|
||||
group::Group,
|
||||
Alignment, Aura, Auras, BuffKind, Buffs, CharacterState, Health, Player, Pos, Stats,
|
||||
},
|
||||
event::{Emitter, EventBus, ServerEvent},
|
||||
event::{AuraEvent, BuffEvent, EmitExt},
|
||||
event_emitters,
|
||||
resources::Time,
|
||||
uid::{IdMaps, Uid},
|
||||
};
|
||||
use common_ecs::{Job, Origin, Phase, System};
|
||||
use specs::{shred, Entities, Entity as EcsEntity, Join, Read, ReadStorage, SystemData};
|
||||
|
||||
event_emitters! {
|
||||
struct Events[Emitters] {
|
||||
aura: AuraEvent,
|
||||
buff: BuffEvent,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(SystemData)]
|
||||
pub struct ReadData<'a> {
|
||||
entities: Entities<'a>,
|
||||
players: ReadStorage<'a, Player>,
|
||||
time: Read<'a, Time>,
|
||||
server_bus: Read<'a, EventBus<ServerEvent>>,
|
||||
events: Events<'a>,
|
||||
id_maps: Read<'a, IdMaps>,
|
||||
cached_spatial_grid: Read<'a, common::CachedSpatialGrid>,
|
||||
positions: ReadStorage<'a, Pos>,
|
||||
@ -42,7 +50,7 @@ impl<'a> System<'a> for Sys {
|
||||
const PHASE: Phase = Phase::Create;
|
||||
|
||||
fn run(_job: &mut Job<Self>, read_data: Self::SystemData) {
|
||||
let mut server_emitter = read_data.server_bus.emitter();
|
||||
let mut emitters = read_data.events.get_emitters();
|
||||
|
||||
// Iterate through all entities with an aura
|
||||
for (entity, pos, auras_comp, uid) in (
|
||||
@ -118,14 +126,14 @@ impl<'a> System<'a> for Sys {
|
||||
target_buffs,
|
||||
stats,
|
||||
&read_data,
|
||||
&mut server_emitter,
|
||||
&mut emitters,
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
if !expired_auras.is_empty() {
|
||||
server_emitter.emit(ServerEvent::Aura {
|
||||
emitters.emit(AuraEvent {
|
||||
entity,
|
||||
aura_change: AuraChange::RemoveByKey(expired_auras),
|
||||
});
|
||||
@ -145,7 +153,7 @@ fn activate_aura(
|
||||
target_buffs: &Buffs,
|
||||
stats: Option<&Stats>,
|
||||
read_data: &ReadData,
|
||||
server_emitter: &mut Emitter<ServerEvent>,
|
||||
emitters: &mut impl EmitExt<BuffEvent>,
|
||||
) {
|
||||
let should_activate = match aura.aura_kind {
|
||||
AuraKind::Buff { kind, source, .. } => {
|
||||
@ -223,7 +231,7 @@ fn activate_aura(
|
||||
&& buff.data.strength >= data.strength
|
||||
});
|
||||
if emit_buff {
|
||||
server_emitter.emit(ServerEvent::Buff {
|
||||
emitters.emit(BuffEvent {
|
||||
entity: target,
|
||||
buff_change: BuffChange::Add(Buff::new(
|
||||
kind,
|
||||
|
@ -5,7 +5,8 @@ use common::{
|
||||
Alignment, Beam, Body, Buffs, CharacterState, Combo, Energy, Group, Health, Inventory, Ori,
|
||||
Player, Pos, Scale, Stats,
|
||||
},
|
||||
event::{EventBus, ServerEvent},
|
||||
event::{self, EmitExt, EventBus},
|
||||
event_emitters,
|
||||
outcome::Outcome,
|
||||
resources::{DeltaTime, Time},
|
||||
terrain::TerrainGrid,
|
||||
@ -21,11 +22,24 @@ use specs::{
|
||||
};
|
||||
use vek::*;
|
||||
|
||||
event_emitters! {
|
||||
struct ReadAttackEvents[AttackEmitters] {
|
||||
health_change: event::HealthChangeEvent,
|
||||
energy_change: event::EnergyChangeEvent,
|
||||
poise_change: event::PoiseChangeEvent,
|
||||
sound: event::SoundEvent,
|
||||
parry_hook: event::ParryHookEvent,
|
||||
kockback: event::KnockbackEvent,
|
||||
entity_attack_hoow: event::EntityAttackedHookEvent,
|
||||
combo_change: event::ComboChangeEvent,
|
||||
buff: event::BuffEvent,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(SystemData)]
|
||||
pub struct ReadData<'a> {
|
||||
entities: Entities<'a>,
|
||||
players: ReadStorage<'a, Player>,
|
||||
server_bus: Read<'a, EventBus<ServerEvent>>,
|
||||
time: Read<'a, Time>,
|
||||
dt: Read<'a, DeltaTime>,
|
||||
terrain: ReadExpect<'a, TerrainGrid>,
|
||||
@ -46,6 +60,7 @@ pub struct ReadData<'a> {
|
||||
character_states: ReadStorage<'a, CharacterState>,
|
||||
buffs: ReadStorage<'a, Buffs>,
|
||||
outcomes: Read<'a, EventBus<Outcome>>,
|
||||
events: ReadAttackEvents<'a>,
|
||||
}
|
||||
|
||||
/// This system is responsible for handling beams that heal or do damage
|
||||
@ -59,7 +74,6 @@ impl<'a> System<'a> for Sys {
|
||||
const PHASE: Phase = Phase::Create;
|
||||
|
||||
fn run(job: &mut Job<Self>, (read_data, mut beams): Self::SystemData) {
|
||||
let mut server_emitter = read_data.server_bus.emitter();
|
||||
let mut outcomes_emitter = read_data.outcomes.emitter();
|
||||
|
||||
(
|
||||
@ -100,7 +114,8 @@ impl<'a> System<'a> for Sys {
|
||||
job.cpu_stats.measure(ParMode::Rayon);
|
||||
|
||||
// Beams
|
||||
let (server_events, add_hit_entities, new_outcomes) = (
|
||||
// Emitters will append their events when dropped.
|
||||
let (_emitters, add_hit_entities, new_outcomes) = (
|
||||
&read_data.entities,
|
||||
&read_data.positions,
|
||||
&read_data.orientations,
|
||||
@ -109,14 +124,14 @@ impl<'a> System<'a> for Sys {
|
||||
)
|
||||
.par_join()
|
||||
.fold(
|
||||
|| (Vec::new(), Vec::new(), Vec::new()),
|
||||
|(mut server_events, mut add_hit_entities, mut outcomes),
|
||||
|| (read_data.events.get_emitters(), Vec::new(), Vec::new()),
|
||||
|(mut emitters, mut add_hit_entities, mut outcomes),
|
||||
(entity, pos, ori, uid, beam)| {
|
||||
// Note: rayon makes it difficult to hold onto a thread-local RNG, if grabbing
|
||||
// this becomes a bottleneck we can look into alternatives.
|
||||
let mut rng = rand::thread_rng();
|
||||
if rng.gen_bool(0.005) {
|
||||
server_events.push(ServerEvent::Sound {
|
||||
emitters.emit(event::SoundEvent {
|
||||
sound: Sound::new(SoundKind::Beam, pos.0, 13.0, read_data.time.0),
|
||||
});
|
||||
}
|
||||
@ -273,7 +288,7 @@ impl<'a> System<'a> for Sys {
|
||||
1.0,
|
||||
AttackSource::Beam,
|
||||
*read_data.time,
|
||||
|e| server_events.push(e),
|
||||
&mut emitters,
|
||||
|o| outcomes.push(o),
|
||||
&mut rng,
|
||||
0,
|
||||
@ -282,14 +297,14 @@ impl<'a> System<'a> for Sys {
|
||||
add_hit_entities.push((entity, target));
|
||||
}
|
||||
});
|
||||
(server_events, add_hit_entities, outcomes)
|
||||
(emitters, add_hit_entities, outcomes)
|
||||
},
|
||||
)
|
||||
.reduce(
|
||||
|| (Vec::new(), Vec::new(), Vec::new()),
|
||||
|| (read_data.events.get_emitters(), Vec::new(), Vec::new()),
|
||||
|(mut events_a, mut hit_entities_a, mut outcomes_a),
|
||||
(mut events_b, mut hit_entities_b, mut outcomes_b)| {
|
||||
events_a.append(&mut events_b);
|
||||
(events_b, mut hit_entities_b, mut outcomes_b)| {
|
||||
events_a.append(events_b);
|
||||
hit_entities_a.append(&mut hit_entities_b);
|
||||
outcomes_a.append(&mut outcomes_b);
|
||||
(events_a, hit_entities_a, outcomes_a)
|
||||
@ -298,7 +313,6 @@ impl<'a> System<'a> for Sys {
|
||||
job.cpu_stats.measure(ParMode::Single);
|
||||
|
||||
outcomes_emitter.emit_many(new_outcomes);
|
||||
server_emitter.emit_many(server_events);
|
||||
|
||||
for (entity, hit_entity) in add_hit_entities {
|
||||
if let Some(ref mut beam) = beams.get_mut(entity) {
|
||||
|
@ -13,7 +13,11 @@ use common::{
|
||||
Energy, Group, Health, HealthChange, Inventory, LightEmitter, ModifierKind, PhysicsState,
|
||||
Pos, Stats,
|
||||
},
|
||||
event::{Emitter, EventBus, ServerEvent},
|
||||
event::{
|
||||
BuffEvent, ChangeBodyEvent, CreateSpriteEvent, EmitExt, EnergyChangeEvent,
|
||||
HealthChangeEvent, RemoveLightEmitterEvent, SoundEvent,
|
||||
},
|
||||
event_emitters,
|
||||
outcome::Outcome,
|
||||
resources::{DeltaTime, Secs, Time},
|
||||
terrain::SpriteKind,
|
||||
@ -29,12 +33,24 @@ use specs::{
|
||||
};
|
||||
use vek::Vec3;
|
||||
|
||||
event_emitters! {
|
||||
struct Events[EventEmitters] {
|
||||
buff: BuffEvent,
|
||||
change_body: ChangeBodyEvent,
|
||||
remove_light: RemoveLightEmitterEvent,
|
||||
health_change: HealthChangeEvent,
|
||||
energy_change: EnergyChangeEvent,
|
||||
sound: SoundEvent,
|
||||
create_sprite: CreateSpriteEvent,
|
||||
outcome: Outcome,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(SystemData)]
|
||||
pub struct ReadData<'a> {
|
||||
entities: Entities<'a>,
|
||||
dt: Read<'a, DeltaTime>,
|
||||
server_bus: Read<'a, EventBus<ServerEvent>>,
|
||||
outcome_bus: Read<'a, EventBus<Outcome>>,
|
||||
events: Events<'a>,
|
||||
inventories: ReadStorage<'a, Inventory>,
|
||||
healths: ReadStorage<'a, Health>,
|
||||
energies: ReadStorage<'a, Energy>,
|
||||
@ -60,8 +76,7 @@ impl<'a> System<'a> for Sys {
|
||||
const PHASE: Phase = Phase::Create;
|
||||
|
||||
fn run(job: &mut Job<Self>, (read_data, mut stats): Self::SystemData) {
|
||||
let mut server_emitter = read_data.server_bus.emitter();
|
||||
let mut outcome = read_data.outcome_bus.emitter();
|
||||
let mut emitters = read_data.events.get_emitters();
|
||||
let dt = read_data.dt.0;
|
||||
// Set to false to avoid spamming server
|
||||
stats.set_event_emission(false);
|
||||
@ -116,11 +131,11 @@ impl<'a> System<'a> for Sys {
|
||||
// slower than parallel checking above
|
||||
for e in to_put_out_campfires {
|
||||
{
|
||||
server_emitter.emit(ServerEvent::ChangeBody {
|
||||
emitters.emit(ChangeBodyEvent {
|
||||
entity: e,
|
||||
new_body: Body::Object(object::Body::Campfire),
|
||||
});
|
||||
server_emitter.emit(ServerEvent::RemoveLightEmitter { entity: e });
|
||||
emitters.emit(RemoveLightEmitterEvent { entity: e });
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -144,7 +159,7 @@ impl<'a> System<'a> for Sys {
|
||||
Some(SpriteKind::EnsnaringVines) | Some(SpriteKind::EnsnaringWeb)
|
||||
) {
|
||||
// If on ensnaring vines, apply ensnared debuff
|
||||
server_emitter.emit(ServerEvent::Buff {
|
||||
emitters.emit(BuffEvent {
|
||||
entity,
|
||||
buff_change: BuffChange::Add(Buff::new(
|
||||
BuffKind::Ensnared,
|
||||
@ -161,7 +176,7 @@ impl<'a> System<'a> for Sys {
|
||||
Some(SpriteKind::SeaUrchin)
|
||||
) {
|
||||
// If touching Sea Urchin apply Bleeding buff
|
||||
server_emitter.emit(ServerEvent::Buff {
|
||||
emitters.emit(BuffEvent {
|
||||
entity,
|
||||
buff_change: BuffChange::Add(Buff::new(
|
||||
BuffKind::Bleeding,
|
||||
@ -181,17 +196,17 @@ impl<'a> System<'a> for Sys {
|
||||
// TODO: Determine a better place to emit sprite change events
|
||||
if let Some(pos) = read_data.positions.get(entity) {
|
||||
// If touching Trap - change sprite and apply Bleeding buff
|
||||
server_emitter.emit(ServerEvent::CreateSprite {
|
||||
emitters.emit(CreateSpriteEvent {
|
||||
pos: Vec3::new(pos.0.x as i32, pos.0.y as i32, pos.0.z as i32 - 1),
|
||||
sprite: SpriteKind::HaniwaTrapTriggered,
|
||||
del_timeout: Some((4.0, 1.0)),
|
||||
});
|
||||
server_emitter.emit(ServerEvent::Sound {
|
||||
emitters.emit(SoundEvent {
|
||||
sound: Sound::new(SoundKind::Trap, pos.0, 12.0, read_data.time.0),
|
||||
});
|
||||
outcome.emit(Outcome::Slash { pos: pos.0 });
|
||||
emitters.emit(Outcome::Slash { pos: pos.0 });
|
||||
|
||||
server_emitter.emit(ServerEvent::Buff {
|
||||
emitters.emit(BuffEvent {
|
||||
entity,
|
||||
buff_change: BuffChange::Add(Buff::new(
|
||||
BuffKind::Bleeding,
|
||||
@ -209,7 +224,7 @@ impl<'a> System<'a> for Sys {
|
||||
Some(SpriteKind::IronSpike | SpriteKind::HaniwaTrapTriggered)
|
||||
) {
|
||||
// If touching Iron Spike apply Bleeding buff
|
||||
server_emitter.emit(ServerEvent::Buff {
|
||||
emitters.emit(BuffEvent {
|
||||
entity,
|
||||
buff_change: BuffChange::Add(Buff::new(
|
||||
BuffKind::Bleeding,
|
||||
@ -226,7 +241,7 @@ impl<'a> System<'a> for Sys {
|
||||
Some(SpriteKind::HotSurface)
|
||||
) {
|
||||
// If touching a hot surface apply Burning buff
|
||||
server_emitter.emit(ServerEvent::Buff {
|
||||
emitters.emit(BuffEvent {
|
||||
entity,
|
||||
buff_change: BuffChange::Add(Buff::new(
|
||||
BuffKind::Burning,
|
||||
@ -243,7 +258,7 @@ impl<'a> System<'a> for Sys {
|
||||
Some(SpriteKind::IceSpike)
|
||||
) {
|
||||
// When standing on IceSpike, apply bleeding
|
||||
server_emitter.emit(ServerEvent::Buff {
|
||||
emitters.emit(BuffEvent {
|
||||
entity,
|
||||
buff_change: BuffChange::Add(Buff::new(
|
||||
BuffKind::Bleeding,
|
||||
@ -255,7 +270,7 @@ impl<'a> System<'a> for Sys {
|
||||
)),
|
||||
});
|
||||
// When standing on IceSpike also apply Frozen
|
||||
server_emitter.emit(ServerEvent::Buff {
|
||||
emitters.emit(BuffEvent {
|
||||
entity,
|
||||
buff_change: BuffChange::Add(Buff::new(
|
||||
BuffKind::Frozen,
|
||||
@ -272,7 +287,7 @@ impl<'a> System<'a> for Sys {
|
||||
Some(SpriteKind::FireBlock)
|
||||
) {
|
||||
// If on FireBlock vines, apply burning buff
|
||||
server_emitter.emit(ServerEvent::Buff {
|
||||
emitters.emit(BuffEvent {
|
||||
entity,
|
||||
buff_change: BuffChange::Add(Buff::new(
|
||||
BuffKind::Burning,
|
||||
@ -293,7 +308,7 @@ impl<'a> System<'a> for Sys {
|
||||
})
|
||||
) {
|
||||
// If in lava fluid, apply burning debuff
|
||||
server_emitter.emit(ServerEvent::Buff {
|
||||
emitters.emit(BuffEvent {
|
||||
entity,
|
||||
buff_change: BuffChange::Add(Buff::new(
|
||||
BuffKind::Burning,
|
||||
@ -313,7 +328,7 @@ impl<'a> System<'a> for Sys {
|
||||
) && buff_comp.kinds[BuffKind::Burning].is_some()
|
||||
{
|
||||
// If in water fluid and currently burning, remove burning debuffs
|
||||
server_emitter.emit(ServerEvent::Buff {
|
||||
emitters.emit(BuffEvent {
|
||||
entity,
|
||||
buff_change: BuffChange::RemoveByKind(BuffKind::Burning),
|
||||
});
|
||||
@ -363,7 +378,7 @@ impl<'a> System<'a> for Sys {
|
||||
};
|
||||
if replace {
|
||||
expired_buffs.push(buff_key);
|
||||
server_emitter.emit(ServerEvent::Buff {
|
||||
emitters.emit(BuffEvent {
|
||||
entity,
|
||||
buff_change: BuffChange::Add(Buff::new(
|
||||
buff.kind,
|
||||
@ -458,7 +473,7 @@ impl<'a> System<'a> for Sys {
|
||||
energy,
|
||||
entity,
|
||||
buff_owner,
|
||||
&mut server_emitter,
|
||||
&mut emitters,
|
||||
dt,
|
||||
*read_data.time,
|
||||
expired_buffs.contains(&buff_key),
|
||||
@ -472,12 +487,12 @@ impl<'a> System<'a> for Sys {
|
||||
// Update body if needed.
|
||||
let new_body = body_override.unwrap_or(stat.original_body);
|
||||
if new_body != *body {
|
||||
server_emitter.emit(ServerEvent::ChangeBody { entity, new_body });
|
||||
emitters.emit(ChangeBodyEvent { entity, new_body });
|
||||
}
|
||||
|
||||
// Remove buffs that expire
|
||||
if !expired_buffs.is_empty() {
|
||||
server_emitter.emit(ServerEvent::Buff {
|
||||
emitters.emit(BuffEvent {
|
||||
entity,
|
||||
buff_change: BuffChange::RemoveByKey(expired_buffs),
|
||||
});
|
||||
@ -485,7 +500,7 @@ impl<'a> System<'a> for Sys {
|
||||
|
||||
// Remove buffs that don't persist on death
|
||||
if health.is_dead {
|
||||
server_emitter.emit(ServerEvent::Buff {
|
||||
emitters.emit(BuffEvent {
|
||||
entity,
|
||||
buff_change: BuffChange::RemoveByCategory {
|
||||
all_required: vec![],
|
||||
@ -515,7 +530,9 @@ fn execute_effect(
|
||||
energy: &Energy,
|
||||
entity: Entity,
|
||||
buff_owner: Option<Uid>,
|
||||
server_emitter: &mut Emitter<ServerEvent>,
|
||||
server_emitter: &mut (
|
||||
impl EmitExt<HealthChangeEvent> + EmitExt<EnergyChangeEvent> + EmitExt<BuffEvent>
|
||||
),
|
||||
dt: f32,
|
||||
time: Time,
|
||||
buff_will_expire: bool,
|
||||
@ -577,7 +594,7 @@ fn execute_effect(
|
||||
DamageContributor::new(uid, read_data.groups.get(entity).cloned())
|
||||
})
|
||||
});
|
||||
server_emitter.emit(ServerEvent::HealthChange {
|
||||
server_emitter.emit(HealthChangeEvent {
|
||||
entity,
|
||||
change: HealthChange {
|
||||
amount,
|
||||
@ -602,7 +619,7 @@ fn execute_effect(
|
||||
ModifierKind::Additive => amount,
|
||||
ModifierKind::Multiplicative => energy.maximum() * amount,
|
||||
};
|
||||
server_emitter.emit(ServerEvent::EnergyChange {
|
||||
server_emitter.emit(EnergyChangeEvent {
|
||||
entity,
|
||||
change: amount,
|
||||
});
|
||||
@ -718,7 +735,7 @@ fn execute_effect(
|
||||
},
|
||||
BuffEffect::BuffImmunity(buff_kind) => {
|
||||
if buffs_comp.contains(*buff_kind) {
|
||||
server_emitter.emit(ServerEvent::Buff {
|
||||
server_emitter.emit(BuffEvent {
|
||||
entity,
|
||||
buff_change: BuffChange::RemoveByKind(*buff_kind),
|
||||
});
|
||||
|
@ -5,13 +5,13 @@ use specs::{
|
||||
use common::{
|
||||
comp::{
|
||||
self,
|
||||
character_state::OutputEvents,
|
||||
character_state::{CharacterStateEvents, OutputEvents},
|
||||
inventory::item::{tool::AbilityMap, MaterialStatManifest},
|
||||
ActiveAbilities, Beam, Body, CharacterActivity, CharacterState, Combo, Controller, Density,
|
||||
Energy, Health, Inventory, InventoryManip, Mass, Melee, Ori, PhysicsState, Poise, Pos,
|
||||
Scale, SkillSet, Stance, StateUpdate, Stats, Vel,
|
||||
},
|
||||
event::{EventBus, LocalEvent, ServerEvent},
|
||||
event::{self, EventBus, KnockbackEvent, LocalEvent},
|
||||
link::Is,
|
||||
mounting::{Rider, VolumeRider},
|
||||
outcome::Outcome,
|
||||
@ -28,7 +28,7 @@ use common_ecs::{Job, Origin, Phase, System};
|
||||
#[derive(SystemData)]
|
||||
pub struct ReadData<'a> {
|
||||
entities: Entities<'a>,
|
||||
server_bus: Read<'a, EventBus<ServerEvent>>,
|
||||
events: CharacterStateEvents<'a>,
|
||||
local_bus: Read<'a, EventBus<LocalEvent>>,
|
||||
dt: Read<'a, DeltaTime>,
|
||||
time: Read<'a, Time>,
|
||||
@ -96,13 +96,12 @@ impl<'a> System<'a> for Sys {
|
||||
outcomes,
|
||||
): Self::SystemData,
|
||||
) {
|
||||
let mut server_emitter = read_data.server_bus.emitter();
|
||||
let mut local_emitter = read_data.local_bus.emitter();
|
||||
let mut outcomes_emitter = outcomes.emitter();
|
||||
let mut emitters = read_data.events.get_emitters();
|
||||
|
||||
let mut local_events = Vec::new();
|
||||
let mut server_events = Vec::new();
|
||||
let mut output_events = OutputEvents::new(&mut local_events, &mut server_events);
|
||||
let mut output_events = OutputEvents::new(&mut local_events, &mut emitters);
|
||||
|
||||
let join = (
|
||||
&read_data.entities,
|
||||
@ -179,7 +178,7 @@ impl<'a> System<'a> for Sys {
|
||||
state: poise_state,
|
||||
});
|
||||
if let Some(impulse_strength) = impulse_strength {
|
||||
server_emitter.emit(ServerEvent::Knockback {
|
||||
output_events.emit_server(KnockbackEvent {
|
||||
entity,
|
||||
impulse: impulse_strength * *poise.knockback(),
|
||||
});
|
||||
@ -255,7 +254,6 @@ impl<'a> System<'a> for Sys {
|
||||
});
|
||||
|
||||
local_emitter.append_vec(local_events);
|
||||
server_emitter.append_vec(server_events);
|
||||
}
|
||||
}
|
||||
|
||||
@ -297,7 +295,7 @@ impl Sys {
|
||||
join.controller.queued_inputs.remove(&input);
|
||||
}
|
||||
if state_update.swap_equipped_weapons {
|
||||
output_events.emit_server(ServerEvent::InventoryManip(
|
||||
output_events.emit_server(event::InventoryManipEvent(
|
||||
join.entity,
|
||||
InventoryManip::SwapEquippedWeapons,
|
||||
));
|
||||
|
@ -4,7 +4,8 @@ use common::{
|
||||
agent::{Sound, SoundKind},
|
||||
Body, BuffChange, Collider, ControlEvent, Controller, Pos, Scale,
|
||||
},
|
||||
event::{EventBus, ServerEvent},
|
||||
event::{self, EmitExt},
|
||||
event_emitters,
|
||||
terrain::TerrainGrid,
|
||||
uid::IdMaps,
|
||||
};
|
||||
@ -12,11 +13,33 @@ use common_ecs::{Job, Origin, Phase, System};
|
||||
use specs::{shred, Entities, Join, Read, ReadExpect, ReadStorage, SystemData, WriteStorage};
|
||||
use vek::*;
|
||||
|
||||
event_emitters! {
|
||||
struct Events[EventEmitters] {
|
||||
mount: event::MountEvent,
|
||||
mount_volume: event::MountVolumeEvent,
|
||||
set_pet_stay: event::SetPetStayEvent,
|
||||
unmount: event::UnmountEvent,
|
||||
lantern: event::SetLanternEvent,
|
||||
npc_interact: event::NpcInteractEvent,
|
||||
initiate_invite: event::InitiateInviteEvent,
|
||||
invite_response: event::InviteResponseEvent,
|
||||
process_trade_action: event::ProcessTradeActionEvent,
|
||||
inventory_manip: event::InventoryManipEvent,
|
||||
group_manip: event::GroupManipEvent,
|
||||
respawn: event::RespawnEvent,
|
||||
sound: event::SoundEvent,
|
||||
change_ability: event::ChangeAbilityEvent,
|
||||
change_stance: event::ChangeStanceEvent,
|
||||
start_teleporting: event::StartTeleportingEvent,
|
||||
buff: event::BuffEvent,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(SystemData)]
|
||||
pub struct ReadData<'a> {
|
||||
entities: Entities<'a>,
|
||||
id_maps: Read<'a, IdMaps>,
|
||||
server_bus: Read<'a, EventBus<ServerEvent>>,
|
||||
events: Events<'a>,
|
||||
terrain_grid: ReadExpect<'a, TerrainGrid>,
|
||||
positions: ReadStorage<'a, Pos>,
|
||||
bodies: ReadStorage<'a, Body>,
|
||||
@ -35,7 +58,7 @@ impl<'a> System<'a> for Sys {
|
||||
const PHASE: Phase = Phase::Create;
|
||||
|
||||
fn run(_job: &mut Job<Self>, (read_data, mut controllers): Self::SystemData) {
|
||||
let mut server_emitter = read_data.server_bus.emitter();
|
||||
let mut emitters = read_data.events.get_emitters();
|
||||
|
||||
for (entity, controller) in (&read_data.entities, &mut controllers).join() {
|
||||
// Sanitize inputs to avoid clients sending bad data
|
||||
@ -46,7 +69,7 @@ impl<'a> System<'a> for Sys {
|
||||
match event {
|
||||
ControlEvent::Mount(mountee_uid) => {
|
||||
if let Some(mountee_entity) = read_data.id_maps.uid_entity(mountee_uid) {
|
||||
server_emitter.emit(ServerEvent::Mount(entity, mountee_entity));
|
||||
emitters.emit(event::MountEvent(entity, mountee_entity));
|
||||
}
|
||||
},
|
||||
ControlEvent::MountVolume(volume) => {
|
||||
@ -56,51 +79,49 @@ impl<'a> System<'a> for Sys {
|
||||
&read_data.colliders,
|
||||
) {
|
||||
if block.is_mountable() {
|
||||
server_emitter.emit(ServerEvent::MountVolume(entity, volume));
|
||||
emitters.emit(event::MountVolumeEvent(entity, volume));
|
||||
}
|
||||
}
|
||||
},
|
||||
ControlEvent::SetPetStay(pet_uid, stay) => {
|
||||
if let Some(pet_entity) = read_data.id_maps.uid_entity(pet_uid) {
|
||||
server_emitter.emit(ServerEvent::SetPetStay(entity, pet_entity, stay));
|
||||
emitters.emit(event::SetPetStayEvent(entity, pet_entity, stay));
|
||||
}
|
||||
},
|
||||
ControlEvent::RemoveBuff(buff_id) => {
|
||||
server_emitter.emit(ServerEvent::Buff {
|
||||
emitters.emit(event::BuffEvent {
|
||||
entity,
|
||||
buff_change: BuffChange::RemoveFromController(buff_id),
|
||||
});
|
||||
},
|
||||
ControlEvent::Unmount => server_emitter.emit(ServerEvent::Unmount(entity)),
|
||||
ControlEvent::Unmount => emitters.emit(event::UnmountEvent(entity)),
|
||||
ControlEvent::EnableLantern => {
|
||||
server_emitter.emit(ServerEvent::EnableLantern(entity))
|
||||
emitters.emit(event::SetLanternEvent(entity, true))
|
||||
},
|
||||
ControlEvent::DisableLantern => {
|
||||
server_emitter.emit(ServerEvent::DisableLantern(entity))
|
||||
emitters.emit(event::SetLanternEvent(entity, false))
|
||||
},
|
||||
ControlEvent::Interact(npc_uid, subject) => {
|
||||
if let Some(npc_entity) = read_data.id_maps.uid_entity(npc_uid) {
|
||||
server_emitter
|
||||
.emit(ServerEvent::NpcInteract(entity, npc_entity, subject));
|
||||
emitters.emit(event::NpcInteractEvent(entity, npc_entity, subject));
|
||||
}
|
||||
},
|
||||
ControlEvent::InitiateInvite(inviter_uid, kind) => {
|
||||
server_emitter.emit(ServerEvent::InitiateInvite(entity, inviter_uid, kind));
|
||||
emitters.emit(event::InitiateInviteEvent(entity, inviter_uid, kind));
|
||||
},
|
||||
ControlEvent::InviteResponse(response) => {
|
||||
server_emitter.emit(ServerEvent::InviteResponse(entity, response));
|
||||
emitters.emit(event::InviteResponseEvent(entity, response));
|
||||
},
|
||||
ControlEvent::PerformTradeAction(trade_id, action) => {
|
||||
server_emitter
|
||||
.emit(ServerEvent::ProcessTradeAction(entity, trade_id, action));
|
||||
emitters.emit(event::ProcessTradeActionEvent(entity, trade_id, action));
|
||||
},
|
||||
ControlEvent::InventoryEvent(event) => {
|
||||
server_emitter.emit(ServerEvent::InventoryManip(entity, event.into()));
|
||||
emitters.emit(event::InventoryManipEvent(entity, event.into()));
|
||||
},
|
||||
ControlEvent::GroupManip(manip) => {
|
||||
server_emitter.emit(ServerEvent::GroupManip(entity, manip))
|
||||
emitters.emit(event::GroupManipEvent(entity, manip))
|
||||
},
|
||||
ControlEvent::Respawn => server_emitter.emit(ServerEvent::Respawn(entity)),
|
||||
ControlEvent::Respawn => emitters.emit(event::RespawnEvent(entity)),
|
||||
ControlEvent::Utterance(kind) => {
|
||||
if let (Some(pos), Some(body), scale) = (
|
||||
read_data.positions.get(entity),
|
||||
@ -114,7 +135,7 @@ impl<'a> System<'a> for Sys {
|
||||
8.0, // TODO: Come up with a better way of determining this
|
||||
1.0,
|
||||
);
|
||||
server_emitter.emit(ServerEvent::Sound { sound });
|
||||
emitters.emit(event::SoundEvent { sound });
|
||||
}
|
||||
},
|
||||
ControlEvent::ChangeAbility {
|
||||
@ -122,7 +143,7 @@ impl<'a> System<'a> for Sys {
|
||||
auxiliary_key,
|
||||
new_ability,
|
||||
} => {
|
||||
server_emitter.emit(ServerEvent::ChangeAbility {
|
||||
emitters.emit(event::ChangeAbilityEvent {
|
||||
entity,
|
||||
slot,
|
||||
auxiliary_key,
|
||||
@ -130,14 +151,14 @@ impl<'a> System<'a> for Sys {
|
||||
});
|
||||
},
|
||||
ControlEvent::LeaveStance => {
|
||||
server_emitter.emit(ServerEvent::ChangeStance {
|
||||
emitters.emit(event::ChangeStanceEvent {
|
||||
entity,
|
||||
stance: Stance::None,
|
||||
});
|
||||
},
|
||||
ControlEvent::ActivatePortal(portal_uid) => {
|
||||
if let Some(portal) = read_data.id_maps.uid_entity(portal_uid) {
|
||||
server_emitter.emit(ServerEvent::StartTeleporting { entity, portal });
|
||||
emitters.emit(event::StartTeleportingEvent { entity, portal });
|
||||
}
|
||||
},
|
||||
}
|
||||
|
@ -6,7 +6,8 @@ use common::{
|
||||
Alignment, Body, Buffs, CharacterState, Combo, Energy, Group, Health, Inventory, Melee,
|
||||
Ori, Player, Pos, Scale, Stats,
|
||||
},
|
||||
event::{EventBus, ServerEvent},
|
||||
event::{self, EmitExt, EventBus},
|
||||
event_emitters,
|
||||
outcome::Outcome,
|
||||
resources::Time,
|
||||
terrain::TerrainGrid,
|
||||
@ -22,6 +23,21 @@ use specs::{
|
||||
};
|
||||
use vek::*;
|
||||
|
||||
event_emitters! {
|
||||
struct ReadAttackEvents[AttackEmitters] {
|
||||
health_change: event::HealthChangeEvent,
|
||||
energy_change: event::EnergyChangeEvent,
|
||||
poise_change: event::PoiseChangeEvent,
|
||||
sound: event::SoundEvent,
|
||||
mine_block: event::MineBlockEvent,
|
||||
parry_hook: event::ParryHookEvent,
|
||||
kockback: event::KnockbackEvent,
|
||||
entity_attack_hook: event::EntityAttackedHookEvent,
|
||||
combo_change: event::ComboChangeEvent,
|
||||
buff: event::BuffEvent,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(SystemData)]
|
||||
pub struct ReadData<'a> {
|
||||
time: Read<'a, Time>,
|
||||
@ -40,10 +56,10 @@ pub struct ReadData<'a> {
|
||||
inventories: ReadStorage<'a, Inventory>,
|
||||
groups: ReadStorage<'a, Group>,
|
||||
char_states: ReadStorage<'a, CharacterState>,
|
||||
server_bus: Read<'a, EventBus<ServerEvent>>,
|
||||
stats: ReadStorage<'a, Stats>,
|
||||
combos: ReadStorage<'a, Combo>,
|
||||
buffs: ReadStorage<'a, Buffs>,
|
||||
events: ReadAttackEvents<'a>,
|
||||
}
|
||||
|
||||
/// This system is responsible for handling accepted inputs like moving or
|
||||
@ -63,7 +79,7 @@ impl<'a> System<'a> for Sys {
|
||||
const PHASE: Phase = Phase::Create;
|
||||
|
||||
fn run(_job: &mut Job<Self>, (read_data, mut melee_attacks, outcomes): Self::SystemData) {
|
||||
let mut server_emitter = read_data.server_bus.emitter();
|
||||
let mut emitters = read_data.events.get_emitters();
|
||||
let mut outcomes_emitter = outcomes.emitter();
|
||||
let mut rng = rand::thread_rng();
|
||||
|
||||
@ -82,7 +98,7 @@ impl<'a> System<'a> for Sys {
|
||||
if melee_attack.applied {
|
||||
continue;
|
||||
}
|
||||
server_emitter.emit(ServerEvent::Sound {
|
||||
emitters.emit(event::SoundEvent {
|
||||
sound: Sound::new(SoundKind::Melee, pos.0, 2.0, read_data.time.0),
|
||||
});
|
||||
melee_attack.applied = true;
|
||||
@ -103,7 +119,7 @@ impl<'a> System<'a> for Sys {
|
||||
if eye_pos.distance_squared(block_pos.map(|e| e as f32 + 0.5))
|
||||
< (rad + scale * melee_attack.range).powi(2)
|
||||
{
|
||||
server_emitter.emit(ServerEvent::MineBlock {
|
||||
emitters.emit(event::MineBlockEvent {
|
||||
entity: attacker,
|
||||
pos: block_pos,
|
||||
tool,
|
||||
@ -270,7 +286,7 @@ impl<'a> System<'a> for Sys {
|
||||
strength,
|
||||
AttackSource::Melee,
|
||||
*read_data.time,
|
||||
|e| server_emitter.emit(e),
|
||||
&mut emitters,
|
||||
|o| outcomes_emitter.emit(o),
|
||||
&mut rng,
|
||||
offset as u64,
|
||||
|
@ -7,7 +7,8 @@ use common::{
|
||||
PosVelOriDefer, PreviousPhysCache, Projectile, Scale, Stats, Sticky, Vel,
|
||||
},
|
||||
consts::{AIR_DENSITY, FRIC_GROUND, GRAVITY},
|
||||
event::{EventBus, ServerEvent},
|
||||
event::{EmitExt, EventBus, LandOnGroundEvent},
|
||||
event_emitters,
|
||||
link::Is,
|
||||
mounting::{Rider, VolumeRider},
|
||||
outcome::Outcome,
|
||||
@ -123,6 +124,12 @@ fn calc_z_limit(char_state_maybe: Option<&CharacterState>, collider: &Collider)
|
||||
collider.get_z_limits(modifier)
|
||||
}
|
||||
|
||||
event_emitters! {
|
||||
struct Events[Emitters] {
|
||||
land_on_ground: LandOnGroundEvent,
|
||||
}
|
||||
}
|
||||
|
||||
/// This system applies forces and calculates new positions and velocities.
|
||||
#[derive(Default)]
|
||||
pub struct Sys;
|
||||
@ -130,10 +137,10 @@ pub struct Sys;
|
||||
#[derive(SystemData)]
|
||||
pub struct PhysicsRead<'a> {
|
||||
entities: Entities<'a>,
|
||||
events: Events<'a>,
|
||||
uids: ReadStorage<'a, Uid>,
|
||||
terrain: ReadExpect<'a, TerrainGrid>,
|
||||
dt: Read<'a, DeltaTime>,
|
||||
event_bus: Read<'a, EventBus<ServerEvent>>,
|
||||
game_mode: ReadExpect<'a, GameMode>,
|
||||
scales: ReadStorage<'a, Scale>,
|
||||
stickies: ReadStorage<'a, Sticky>,
|
||||
@ -1364,16 +1371,16 @@ impl<'a> PhysicsData<'a> {
|
||||
}
|
||||
drop(guard);
|
||||
|
||||
let mut event_emitter = read.event_bus.emitter();
|
||||
let mut emitters = read.events.get_emitters();
|
||||
emitters.emit_many(
|
||||
land_on_grounds
|
||||
.into_iter()
|
||||
.for_each(|(entity, vel, surface_normal)| {
|
||||
event_emitter.emit(ServerEvent::LandOnGround {
|
||||
.map(|(entity, vel, surface_normal)| LandOnGroundEvent {
|
||||
entity,
|
||||
vel: vel.0,
|
||||
surface_normal,
|
||||
});
|
||||
});
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
fn update_cached_spatial_grid(&mut self) {
|
||||
|
@ -5,7 +5,12 @@ use common::{
|
||||
projectile, Alignment, Body, Buffs, CharacterState, Combo, Energy, Group, Health,
|
||||
Inventory, Ori, PhysicsState, Player, Pos, Projectile, Stats, Vel,
|
||||
},
|
||||
event::{Emitter, EventBus, ServerEvent},
|
||||
event::{
|
||||
BonkEvent, BuffEvent, ComboChangeEvent, DeleteEvent, EmitExt, Emitter, EnergyChangeEvent,
|
||||
EntityAttackedHookEvent, EventBus, ExplosionEvent, HealthChangeEvent, KnockbackEvent,
|
||||
ParryHookEvent, PoiseChangeEvent, PossessEvent, SoundEvent,
|
||||
},
|
||||
event_emitters,
|
||||
outcome::Outcome,
|
||||
resources::{DeltaTime, Time},
|
||||
uid::{IdMaps, Uid},
|
||||
@ -25,6 +30,24 @@ use vek::*;
|
||||
|
||||
use common::terrain::TerrainGrid;
|
||||
|
||||
event_emitters! {
|
||||
struct Events[Emitters] {
|
||||
sound: SoundEvent,
|
||||
delete: DeleteEvent,
|
||||
explosion: ExplosionEvent,
|
||||
health_change: HealthChangeEvent,
|
||||
energy_change: EnergyChangeEvent,
|
||||
poise_change: PoiseChangeEvent,
|
||||
parry_hook: ParryHookEvent,
|
||||
kockback: KnockbackEvent,
|
||||
entity_attack_hoow: EntityAttackedHookEvent,
|
||||
combo_change: ComboChangeEvent,
|
||||
buff: BuffEvent,
|
||||
bonk: BonkEvent,
|
||||
possess: PossessEvent,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(SystemData)]
|
||||
pub struct ReadData<'a> {
|
||||
time: Read<'a, Time>,
|
||||
@ -32,7 +55,7 @@ pub struct ReadData<'a> {
|
||||
players: ReadStorage<'a, Player>,
|
||||
dt: Read<'a, DeltaTime>,
|
||||
id_maps: Read<'a, IdMaps>,
|
||||
server_bus: Read<'a, EventBus<ServerEvent>>,
|
||||
events: Events<'a>,
|
||||
uids: ReadStorage<'a, Uid>,
|
||||
positions: ReadStorage<'a, Pos>,
|
||||
alignments: ReadStorage<'a, Alignment>,
|
||||
@ -69,7 +92,7 @@ impl<'a> System<'a> for Sys {
|
||||
_job: &mut Job<Self>,
|
||||
(read_data, mut orientations, mut projectiles, outcomes): Self::SystemData,
|
||||
) {
|
||||
let mut server_emitter = read_data.server_bus.emitter();
|
||||
let mut emitters = read_data.events.get_emitters();
|
||||
let mut outcomes_emitter = outcomes.emitter();
|
||||
let mut rng = rand::thread_rng();
|
||||
|
||||
@ -88,7 +111,7 @@ impl<'a> System<'a> for Sys {
|
||||
.and_then(|uid| read_data.id_maps.uid_entity(uid));
|
||||
|
||||
if physics.on_surface().is_none() && rng.gen_bool(0.05) {
|
||||
server_emitter.emit(ServerEvent::Sound {
|
||||
emitters.emit(SoundEvent {
|
||||
sound: Sound::new(SoundKind::Projectile, pos.0, 4.0, read_data.time.0),
|
||||
});
|
||||
}
|
||||
@ -174,7 +197,7 @@ impl<'a> System<'a> for Sys {
|
||||
&read_data,
|
||||
&mut projectile_vanished,
|
||||
&mut outcomes_emitter,
|
||||
&mut server_emitter,
|
||||
&mut emitters,
|
||||
&mut rng,
|
||||
);
|
||||
}
|
||||
@ -200,18 +223,18 @@ impl<'a> System<'a> for Sys {
|
||||
.get(entity)
|
||||
.map_or_else(Vec3::zero, |ori| ori.look_vec());
|
||||
let offset = -0.2 * projectile_direction;
|
||||
server_emitter.emit(ServerEvent::Explosion {
|
||||
emitters.emit(ExplosionEvent {
|
||||
pos: pos.0 + offset,
|
||||
explosion: e,
|
||||
owner: projectile.owner,
|
||||
});
|
||||
},
|
||||
projectile::Effect::Vanish => {
|
||||
server_emitter.emit(ServerEvent::Delete(entity));
|
||||
emitters.emit(DeleteEvent(entity));
|
||||
projectile_vanished = true;
|
||||
},
|
||||
projectile::Effect::Bonk => {
|
||||
server_emitter.emit(ServerEvent::Bonk {
|
||||
emitters.emit(BonkEvent {
|
||||
pos: pos.0,
|
||||
owner: projectile.owner,
|
||||
target: None,
|
||||
@ -231,7 +254,7 @@ impl<'a> System<'a> for Sys {
|
||||
}
|
||||
|
||||
if projectile.time_left == Duration::default() {
|
||||
server_emitter.emit(ServerEvent::Delete(entity));
|
||||
emitters.emit(DeleteEvent(entity));
|
||||
}
|
||||
projectile.time_left = projectile
|
||||
.time_left
|
||||
@ -264,7 +287,7 @@ fn dispatch_hit(
|
||||
read_data: &ReadData,
|
||||
projectile_vanished: &mut bool,
|
||||
outcomes_emitter: &mut Emitter<Outcome>,
|
||||
server_emitter: &mut Emitter<ServerEvent>,
|
||||
emitters: &mut Emitters,
|
||||
rng: &mut rand::rngs::ThreadRng,
|
||||
) {
|
||||
match projectile_info.effect {
|
||||
@ -437,7 +460,7 @@ fn dispatch_hit(
|
||||
1.0,
|
||||
AttackSource::Projectile,
|
||||
*read_data.time,
|
||||
|e| server_emitter.emit(e),
|
||||
emitters,
|
||||
|o| outcomes_emitter.emit(o),
|
||||
rng,
|
||||
0,
|
||||
@ -446,7 +469,7 @@ fn dispatch_hit(
|
||||
projectile::Effect::Explode(e) => {
|
||||
let Pos(pos) = *projectile_info.pos;
|
||||
let owner_uid = projectile_info.owner_uid;
|
||||
server_emitter.emit(ServerEvent::Explosion {
|
||||
emitters.emit(ExplosionEvent {
|
||||
pos,
|
||||
explosion: e,
|
||||
owner: owner_uid,
|
||||
@ -455,7 +478,7 @@ fn dispatch_hit(
|
||||
projectile::Effect::Bonk => {
|
||||
let Pos(pos) = *projectile_info.pos;
|
||||
let owner_uid = projectile_info.owner_uid;
|
||||
server_emitter.emit(ServerEvent::Bonk {
|
||||
emitters.emit(BonkEvent {
|
||||
pos,
|
||||
owner: owner_uid,
|
||||
target: Some(projectile_target_info.uid),
|
||||
@ -463,7 +486,7 @@ fn dispatch_hit(
|
||||
},
|
||||
projectile::Effect::Vanish => {
|
||||
let entity = projectile_info.entity;
|
||||
server_emitter.emit(ServerEvent::Delete(entity));
|
||||
emitters.emit(DeleteEvent(entity));
|
||||
*projectile_vanished = true;
|
||||
},
|
||||
projectile::Effect::Possess => {
|
||||
@ -471,7 +494,7 @@ fn dispatch_hit(
|
||||
let owner_uid = projectile_info.owner_uid;
|
||||
if let Some(owner_uid) = owner_uid {
|
||||
if target_uid != owner_uid {
|
||||
server_emitter.emit(ServerEvent::Possess(owner_uid, target_uid));
|
||||
emitters.emit(PossessEvent(owner_uid, target_uid));
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -6,7 +6,12 @@ use common::{
|
||||
Alignment, Body, Buffs, CharacterState, Combo, Energy, Group, Health, Inventory, Ori,
|
||||
PhysicsState, Player, Pos, Scale, Shockwave, ShockwaveHitEntities, Stats,
|
||||
},
|
||||
event::{EventBus, ServerEvent},
|
||||
event::{
|
||||
BuffEvent, ComboChangeEvent, DeleteEvent, EmitExt, EnergyChangeEvent,
|
||||
EntityAttackedHookEvent, EventBus, HealthChangeEvent, KnockbackEvent, MineBlockEvent,
|
||||
ParryHookEvent, PoiseChangeEvent, SoundEvent,
|
||||
},
|
||||
event_emitters,
|
||||
outcome::Outcome,
|
||||
resources::{DeltaTime, Time},
|
||||
uid::{IdMaps, Uid},
|
||||
@ -18,10 +23,26 @@ use rand::Rng;
|
||||
use specs::{shred, Entities, Join, LendJoin, Read, ReadStorage, SystemData, WriteStorage};
|
||||
use vek::*;
|
||||
|
||||
event_emitters! {
|
||||
struct Events[Emitters] {
|
||||
health_change: HealthChangeEvent,
|
||||
energy_change: EnergyChangeEvent,
|
||||
poise_change: PoiseChangeEvent,
|
||||
sound: SoundEvent,
|
||||
mine_block: MineBlockEvent,
|
||||
parry_hook: ParryHookEvent,
|
||||
kockback: KnockbackEvent,
|
||||
entity_attack_hoow: EntityAttackedHookEvent,
|
||||
combo_change: ComboChangeEvent,
|
||||
buff: BuffEvent,
|
||||
delete: DeleteEvent,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(SystemData)]
|
||||
pub struct ReadData<'a> {
|
||||
entities: Entities<'a>,
|
||||
server_bus: Read<'a, EventBus<ServerEvent>>,
|
||||
events: Events<'a>,
|
||||
time: Read<'a, Time>,
|
||||
players: ReadStorage<'a, Player>,
|
||||
dt: Read<'a, DeltaTime>,
|
||||
@ -63,7 +84,7 @@ impl<'a> System<'a> for Sys {
|
||||
_job: &mut Job<Self>,
|
||||
(read_data, mut shockwaves, mut shockwave_hit_lists, outcomes): Self::SystemData,
|
||||
) {
|
||||
let mut server_emitter = read_data.server_bus.emitter();
|
||||
let mut emitters = read_data.events.get_emitters();
|
||||
let mut outcomes_emitter = outcomes.emitter();
|
||||
let mut rng = rand::thread_rng();
|
||||
|
||||
@ -93,7 +114,7 @@ impl<'a> System<'a> for Sys {
|
||||
.and_then(|uid| read_data.id_maps.uid_entity(uid));
|
||||
|
||||
if rng.gen_bool(0.05) {
|
||||
server_emitter.emit(ServerEvent::Sound {
|
||||
emitters.emit(SoundEvent {
|
||||
sound: Sound::new(SoundKind::Shockwave, pos.0, 40.0, time),
|
||||
});
|
||||
}
|
||||
@ -101,7 +122,7 @@ impl<'a> System<'a> for Sys {
|
||||
// If shockwave is out of time emit destroy event but still continue since it
|
||||
// may have traveled and produced effects a bit before reaching it's end point
|
||||
if time > end_time {
|
||||
server_emitter.emit(ServerEvent::Delete(entity));
|
||||
emitters.emit(DeleteEvent(entity));
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -249,7 +270,7 @@ impl<'a> System<'a> for Sys {
|
||||
1.0,
|
||||
shockwave.dodgeable.to_attack_source(),
|
||||
*read_data.time,
|
||||
|e| server_emitter.emit(e),
|
||||
&mut emitters,
|
||||
|o| outcomes_emitter.emit(o),
|
||||
&mut rng,
|
||||
0,
|
||||
|
@ -7,7 +7,8 @@ use common::{
|
||||
Body, CharacterState, Combo, Energy, Health, Inventory, Poise, Pos, SkillSet, Stats,
|
||||
StatsModifier,
|
||||
},
|
||||
event::{EventBus, ServerEvent},
|
||||
event::{DestroyEvent, EmitExt},
|
||||
event_emitters,
|
||||
resources::{DeltaTime, EntitiesDiedLastTick, Time},
|
||||
};
|
||||
use common_ecs::{Job, Origin, Phase, System};
|
||||
@ -19,12 +20,18 @@ const ENERGY_REGEN_ACCEL: f32 = 1.0;
|
||||
const SIT_ENERGY_REGEN_ACCEL: f32 = 2.5;
|
||||
const POISE_REGEN_ACCEL: f32 = 2.0;
|
||||
|
||||
event_emitters! {
|
||||
struct Events[Emitters] {
|
||||
destroy: DestroyEvent,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(SystemData)]
|
||||
pub struct ReadData<'a> {
|
||||
entities: Entities<'a>,
|
||||
dt: Read<'a, DeltaTime>,
|
||||
time: Read<'a, Time>,
|
||||
server_bus: Read<'a, EventBus<ServerEvent>>,
|
||||
events: Events<'a>,
|
||||
positions: ReadStorage<'a, Pos>,
|
||||
bodies: ReadStorage<'a, Body>,
|
||||
char_states: ReadStorage<'a, CharacterState>,
|
||||
@ -65,7 +72,7 @@ impl<'a> System<'a> for Sys {
|
||||
): Self::SystemData,
|
||||
) {
|
||||
entities_died_last_tick.0.clear();
|
||||
let mut server_event_emitter = read_data.server_bus.emitter();
|
||||
let mut emitters = read_data.events.get_emitters();
|
||||
let dt = read_data.dt.0;
|
||||
|
||||
// Update stats
|
||||
@ -84,7 +91,7 @@ impl<'a> System<'a> for Sys {
|
||||
if set_dead {
|
||||
let cloned_entity = (entity, *pos);
|
||||
entities_died_last_tick.0.push(cloned_entity);
|
||||
server_event_emitter.emit(ServerEvent::Destroy {
|
||||
emitters.emit(DestroyEvent {
|
||||
entity,
|
||||
cause: health.last_change,
|
||||
});
|
||||
|
@ -3,7 +3,7 @@ use crate::{
|
||||
AVG_FOLLOW_DIST, DEFAULT_ATTACK_RANGE, IDLE_HEALING_ITEM_THRESHOLD, MAX_PATROL_DIST,
|
||||
PARTIAL_PATH_DIST, SEPARATION_BIAS, SEPARATION_DIST, STD_AWARENESS_DECAY_RATE,
|
||||
},
|
||||
data::{AgentData, AttackData, Path, ReadData, Tactic, TargetData},
|
||||
data::{AgentData, AgentEmitters, AttackData, Path, ReadData, Tactic, TargetData},
|
||||
util::{
|
||||
aim_projectile, are_our_owners_hostile, entities_have_line_of_sight, get_attacker,
|
||||
get_entity_by_id, is_dead_or_invulnerable, is_dressed_as_cultist, is_invulnerable,
|
||||
@ -28,7 +28,7 @@ use common::{
|
||||
},
|
||||
consts::MAX_MOUNT_RANGE,
|
||||
effect::{BuffEffect, Effect},
|
||||
event::{Emitter, ServerEvent},
|
||||
event::{ChatEvent, EmitExt, SoundEvent},
|
||||
mounting::VolumePos,
|
||||
path::TraversalConfig,
|
||||
rtsim::NpcActivity,
|
||||
@ -180,7 +180,7 @@ impl<'a> AgentData<'a> {
|
||||
agent: &mut Agent,
|
||||
controller: &mut Controller,
|
||||
read_data: &ReadData,
|
||||
event_emitter: &mut Emitter<ServerEvent>,
|
||||
emitters: &mut AgentEmitters,
|
||||
rng: &mut impl Rng,
|
||||
) {
|
||||
enum ActionTimers {
|
||||
@ -430,7 +430,7 @@ impl<'a> AgentData<'a> {
|
||||
agent,
|
||||
controller,
|
||||
read_data,
|
||||
event_emitter,
|
||||
emitters,
|
||||
AgentData::is_hunting_animal,
|
||||
);
|
||||
}
|
||||
@ -807,7 +807,7 @@ impl<'a> AgentData<'a> {
|
||||
agent: &mut Agent,
|
||||
controller: &mut Controller,
|
||||
read_data: &ReadData,
|
||||
event_emitter: &mut Emitter<ServerEvent>,
|
||||
emitters: &mut AgentEmitters,
|
||||
is_enemy: fn(&Self, EcsEntity, &ReadData) -> bool,
|
||||
) {
|
||||
enum ActionStateTimers {
|
||||
@ -856,7 +856,7 @@ impl<'a> AgentData<'a> {
|
||||
self.chat_npc_if_allowed_to_speak(
|
||||
Content::localized("npc-speech-ambush"),
|
||||
agent,
|
||||
event_emitter,
|
||||
emitters,
|
||||
);
|
||||
aggro_on = true;
|
||||
Some((entity, true))
|
||||
@ -1666,13 +1666,13 @@ impl<'a> AgentData<'a> {
|
||||
agent: &mut Agent,
|
||||
controller: &mut Controller,
|
||||
read_data: &ReadData,
|
||||
event_emitter: &mut Emitter<ServerEvent>,
|
||||
emitters: &mut AgentEmitters,
|
||||
rng: &mut impl Rng,
|
||||
) {
|
||||
agent.forget_old_sounds(read_data.time.0);
|
||||
|
||||
if is_invulnerable(*self.entity, read_data) {
|
||||
self.idle(agent, controller, read_data, event_emitter, rng);
|
||||
self.idle(agent, controller, read_data, emitters, rng);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1705,13 +1705,13 @@ impl<'a> AgentData<'a> {
|
||||
} else if self.below_flee_health(agent) || !follows_threatening_sounds {
|
||||
self.flee(agent, controller, read_data, &sound_pos);
|
||||
} else {
|
||||
self.idle(agent, controller, read_data, event_emitter, rng);
|
||||
self.idle(agent, controller, read_data, emitters, rng);
|
||||
}
|
||||
} else {
|
||||
self.idle(agent, controller, read_data, event_emitter, rng);
|
||||
self.idle(agent, controller, read_data, emitters, rng);
|
||||
}
|
||||
} else {
|
||||
self.idle(agent, controller, read_data, event_emitter, rng);
|
||||
self.idle(agent, controller, read_data, emitters, rng);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1720,7 +1720,7 @@ impl<'a> AgentData<'a> {
|
||||
agent: &mut Agent,
|
||||
read_data: &ReadData,
|
||||
controller: &mut Controller,
|
||||
event_emitter: &mut Emitter<ServerEvent>,
|
||||
emitters: &mut AgentEmitters,
|
||||
rng: &mut impl Rng,
|
||||
) {
|
||||
if let Some(Target { target, .. }) = agent.target {
|
||||
@ -1750,7 +1750,7 @@ impl<'a> AgentData<'a> {
|
||||
Some(tgt_pos.0),
|
||||
));
|
||||
|
||||
self.idle(agent, controller, read_data, event_emitter, rng);
|
||||
self.idle(agent, controller, read_data, emitters, rng);
|
||||
} else {
|
||||
let target_data = TargetData::new(tgt_pos, target, read_data);
|
||||
// TODO: Reimplement this in rtsim
|
||||
@ -1774,25 +1774,23 @@ impl<'a> AgentData<'a> {
|
||||
&self,
|
||||
msg: Content,
|
||||
agent: &Agent,
|
||||
event_emitter: &mut Emitter<'_, ServerEvent>,
|
||||
emitters: &mut AgentEmitters,
|
||||
) -> bool {
|
||||
if agent.allowed_to_speak() {
|
||||
self.chat_npc(msg, event_emitter);
|
||||
self.chat_npc(msg, emitters);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn chat_npc(&self, content: Content, event_emitter: &mut Emitter<'_, ServerEvent>) {
|
||||
event_emitter.emit(ServerEvent::Chat(UnresolvedChatMsg::npc(
|
||||
*self.uid, content,
|
||||
)));
|
||||
pub fn chat_npc(&self, content: Content, emitters: &mut AgentEmitters) {
|
||||
emitters.emit(ChatEvent(UnresolvedChatMsg::npc(*self.uid, content)));
|
||||
}
|
||||
|
||||
fn emit_scream(&self, time: f64, event_emitter: &mut Emitter<'_, ServerEvent>) {
|
||||
fn emit_scream(&self, time: f64, emitters: &mut AgentEmitters) {
|
||||
if let Some(body) = self.body {
|
||||
event_emitter.emit(ServerEvent::Sound {
|
||||
emitters.emit(SoundEvent {
|
||||
sound: Sound::new(
|
||||
SoundKind::Utterance(UtteranceKind::Scream, *body),
|
||||
self.pos.0,
|
||||
@ -1803,12 +1801,7 @@ impl<'a> AgentData<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cry_out(
|
||||
&self,
|
||||
agent: &Agent,
|
||||
event_emitter: &mut Emitter<'_, ServerEvent>,
|
||||
read_data: &ReadData,
|
||||
) {
|
||||
pub fn cry_out(&self, agent: &Agent, emitters: &mut AgentEmitters, read_data: &ReadData) {
|
||||
let has_enemy_alignment = matches!(self.alignment, Some(Alignment::Enemy));
|
||||
|
||||
if has_enemy_alignment {
|
||||
@ -1817,28 +1810,24 @@ impl<'a> AgentData<'a> {
|
||||
self.chat_npc_if_allowed_to_speak(
|
||||
Content::localized("npc-speech-cultist_low_health_fleeing"),
|
||||
agent,
|
||||
event_emitter,
|
||||
emitters,
|
||||
);
|
||||
} else if is_villager(self.alignment) {
|
||||
self.chat_npc_if_allowed_to_speak(
|
||||
Content::localized("npc-speech-villager_under_attack"),
|
||||
agent,
|
||||
event_emitter,
|
||||
emitters,
|
||||
);
|
||||
self.emit_scream(read_data.time.0, event_emitter);
|
||||
self.emit_scream(read_data.time.0, emitters);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn exclaim_relief_about_enemy_dead(
|
||||
&self,
|
||||
agent: &Agent,
|
||||
event_emitter: &mut Emitter<'_, ServerEvent>,
|
||||
) {
|
||||
pub fn exclaim_relief_about_enemy_dead(&self, agent: &Agent, emitters: &mut AgentEmitters) {
|
||||
if is_villager(self.alignment) {
|
||||
self.chat_npc_if_allowed_to_speak(
|
||||
Content::localized("npc-speech-villager_enemy_killed"),
|
||||
agent,
|
||||
event_emitter,
|
||||
emitters,
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -2002,7 +1991,7 @@ impl<'a> AgentData<'a> {
|
||||
controller: &mut Controller,
|
||||
target: EcsEntity,
|
||||
read_data: &ReadData,
|
||||
event_emitter: &mut Emitter<ServerEvent>,
|
||||
emitters: &mut AgentEmitters,
|
||||
rng: &mut impl Rng,
|
||||
remembers_fight_with_target: bool,
|
||||
) {
|
||||
@ -2011,7 +2000,7 @@ impl<'a> AgentData<'a> {
|
||||
let move_dir_mag = move_dir.magnitude();
|
||||
let small_chance = rng.gen::<f32>() < read_data.dt.0 * 0.25;
|
||||
let mut chat = |content: Content| {
|
||||
self.chat_npc_if_allowed_to_speak(content, agent, event_emitter);
|
||||
self.chat_npc_if_allowed_to_speak(content, agent, emitters);
|
||||
};
|
||||
let mut chat_villager_remembers_fighting = || {
|
||||
let tgt_name = read_data.stats.get(target).map(|stats| stats.name.clone());
|
||||
|
@ -17,6 +17,7 @@ use common::{
|
||||
Stats, Vel,
|
||||
},
|
||||
consts::GRAVITY,
|
||||
event, event_emitters,
|
||||
link::Is,
|
||||
mounting::{Mount, Rider, VolumeRider},
|
||||
path::TraversalConfig,
|
||||
@ -28,6 +29,14 @@ use common::{
|
||||
};
|
||||
use specs::{shred, Entities, Entity as EcsEntity, Read, ReadExpect, ReadStorage, SystemData};
|
||||
|
||||
event_emitters! {
|
||||
pub struct AgentEvents[AgentEmitters] {
|
||||
chat: event::ChatEvent,
|
||||
sound: event::SoundEvent,
|
||||
process_trade_action: event::ProcessTradeActionEvent,
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Move rtsim back into AgentData after rtsim2 when it has a separate
|
||||
// crate
|
||||
pub struct AgentData<'a> {
|
||||
|
@ -40,7 +40,10 @@ use common::{
|
||||
},
|
||||
depot,
|
||||
effect::Effect,
|
||||
event::{EventBus, ServerEvent},
|
||||
event::{
|
||||
ClientDisconnectEvent, CreateWaypointEvent, EventBus, ExplosionEvent, GroupManipEvent,
|
||||
InitiateInviteEvent, TamePetEvent,
|
||||
},
|
||||
generation::{EntityConfig, EntityInfo},
|
||||
link::Is,
|
||||
mounting::{Rider, Volume, VolumeRider},
|
||||
@ -490,6 +493,7 @@ fn handle_drop_all(
|
||||
pos.0.y + rng.gen_range(5.0..10.0),
|
||||
pos.0.z + 5.0,
|
||||
)),
|
||||
comp::Ori::default(),
|
||||
comp::Vel(vel),
|
||||
item,
|
||||
None,
|
||||
@ -1804,9 +1808,7 @@ fn handle_spawn(
|
||||
|
||||
// Add to group system if a pet
|
||||
if matches!(alignment, comp::Alignment::Owned { .. }) {
|
||||
let server_eventbus =
|
||||
server.state.ecs().read_resource::<EventBus<ServerEvent>>();
|
||||
server_eventbus.emit_now(ServerEvent::TamePet {
|
||||
server.state.emit_event_now(TamePetEvent {
|
||||
owner_entity: target,
|
||||
pet_entity: new_entity,
|
||||
});
|
||||
@ -2079,8 +2081,8 @@ fn handle_spawn_campfire(
|
||||
server
|
||||
.state
|
||||
.ecs()
|
||||
.read_resource::<EventBus<ServerEvent>>()
|
||||
.emit_now(ServerEvent::CreateWaypoint(pos.0));
|
||||
.read_resource::<EventBus<CreateWaypointEvent>>()
|
||||
.emit_now(CreateWaypointEvent(pos.0));
|
||||
|
||||
server.notify_client(
|
||||
client,
|
||||
@ -2907,10 +2909,7 @@ fn handle_explosion(
|
||||
.read_storage::<Uid>()
|
||||
.get(target)
|
||||
.copied();
|
||||
server
|
||||
.state
|
||||
.mut_resource::<EventBus<ServerEvent>>()
|
||||
.emit_now(ServerEvent::Explosion {
|
||||
server.state.emit_event_now(ExplosionEvent {
|
||||
pos: pos.0,
|
||||
explosion: Explosion {
|
||||
effects: vec![
|
||||
@ -3261,8 +3260,7 @@ fn handle_group_invite(
|
||||
|
||||
server
|
||||
.state
|
||||
.mut_resource::<EventBus<ServerEvent>>()
|
||||
.emit_now(ServerEvent::InitiateInvite(target, uid, InviteKind::Group));
|
||||
.emit_event_now(InitiateInviteEvent(target, uid, InviteKind::Group));
|
||||
|
||||
if client != target {
|
||||
server.notify_client(
|
||||
@ -3301,8 +3299,7 @@ fn handle_group_kick(
|
||||
|
||||
server
|
||||
.state
|
||||
.mut_resource::<EventBus<ServerEvent>>()
|
||||
.emit_now(ServerEvent::GroupManip(target, comp::GroupManip::Kick(uid)));
|
||||
.emit_event_now(GroupManipEvent(target, comp::GroupManip::Kick(uid)));
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Content::Plain(action.help_string()))
|
||||
@ -3318,8 +3315,7 @@ fn handle_group_leave(
|
||||
) -> CmdResult<()> {
|
||||
server
|
||||
.state
|
||||
.mut_resource::<EventBus<ServerEvent>>()
|
||||
.emit_now(ServerEvent::GroupManip(target, comp::GroupManip::Leave));
|
||||
.emit_event_now(GroupManipEvent(target, comp::GroupManip::Leave));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -3337,11 +3333,7 @@ fn handle_group_promote(
|
||||
|
||||
server
|
||||
.state
|
||||
.mut_resource::<EventBus<ServerEvent>>()
|
||||
.emit_now(ServerEvent::GroupManip(
|
||||
target,
|
||||
comp::GroupManip::AssignLeader(uid),
|
||||
));
|
||||
.emit_event_now(GroupManipEvent(target, comp::GroupManip::AssignLeader(uid)));
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Content::Plain(action.help_string()))
|
||||
@ -3944,8 +3936,8 @@ fn kick_player(
|
||||
);
|
||||
server
|
||||
.state
|
||||
.mut_resource::<EventBus<ServerEvent>>()
|
||||
.emit_now(ServerEvent::ClientDisconnect(
|
||||
.mut_resource::<EventBus<ClientDisconnectEvent>>()
|
||||
.emit_now(ClientDisconnectEvent(
|
||||
target_player,
|
||||
comp::DisconnectReason::Kicked,
|
||||
));
|
||||
|
@ -3,25 +3,24 @@ use crate::{
|
||||
presence::RepositionOnChunkLoad, sys, CharacterUpdater, Server, StateExt,
|
||||
};
|
||||
use common::{
|
||||
character::CharacterId,
|
||||
comp::{
|
||||
self,
|
||||
aura::{Aura, AuraKind, AuraTarget},
|
||||
buff::{BuffCategory, BuffData, BuffKind, BuffSource},
|
||||
misc::PortalData,
|
||||
ship::figuredata::VOXEL_COLLIDER_MANIFEST,
|
||||
shockwave, Alignment, BehaviorCapability, Body, ItemDrops, LightEmitter, Object, Ori, Pos,
|
||||
Projectile, TradingBehavior, Vel, WaypointArea,
|
||||
Alignment, BehaviorCapability, ItemDrops, LightEmitter, Ori, Pos, TradingBehavior, Vel,
|
||||
WaypointArea,
|
||||
},
|
||||
event::{
|
||||
CreateItemDropEvent, CreateNpcEvent, CreateObjectEvent, CreateShipEvent,
|
||||
CreateTeleporterEvent, CreateWaypointEvent, EventBus, InitializeCharacterEvent,
|
||||
InitializeSpectatorEvent, ShockwaveEvent, ShootEvent, UpdateCharacterDataEvent,
|
||||
},
|
||||
event::{EventBus, NpcBuilder, UpdateCharacterMetadata},
|
||||
mounting::{Mounting, Volume, VolumeMounting, VolumePos},
|
||||
outcome::Outcome,
|
||||
resources::{Secs, Time},
|
||||
rtsim::RtSimEntity,
|
||||
uid::{IdMaps, Uid},
|
||||
util::Dir,
|
||||
vol::IntoFullVolIterator,
|
||||
ViewDistances,
|
||||
};
|
||||
use common_net::{msg::ServerGeneral, sync::WorldSyncExt};
|
||||
use specs::{Builder, Entity as EcsEntity, WorldExt};
|
||||
@ -29,56 +28,57 @@ use vek::{Rgb, Vec3};
|
||||
|
||||
use super::group_manip::update_map_markers;
|
||||
|
||||
pub fn handle_initialize_character(
|
||||
server: &mut Server,
|
||||
entity: EcsEntity,
|
||||
character_id: CharacterId,
|
||||
requested_view_distances: ViewDistances,
|
||||
) {
|
||||
pub fn handle_initialize_character(server: &mut Server, ev: InitializeCharacterEvent) {
|
||||
let updater = server.state.ecs().fetch::<CharacterUpdater>();
|
||||
let pending_database_action = updater.has_pending_database_action(character_id);
|
||||
let pending_database_action = updater.has_pending_database_action(ev.character_id);
|
||||
drop(updater);
|
||||
|
||||
if !pending_database_action {
|
||||
let clamped_vds = requested_view_distances.clamp(server.settings().max_view_distance);
|
||||
let clamped_vds = ev
|
||||
.requested_view_distances
|
||||
.clamp(server.settings().max_view_distance);
|
||||
server
|
||||
.state
|
||||
.initialize_character_data(entity, character_id, clamped_vds);
|
||||
.initialize_character_data(ev.entity, ev.character_id, clamped_vds);
|
||||
// Correct client if its requested VD is too high.
|
||||
if requested_view_distances.terrain != clamped_vds.terrain {
|
||||
server.notify_client(entity, ServerGeneral::SetViewDistance(clamped_vds.terrain));
|
||||
if ev.requested_view_distances.terrain != clamped_vds.terrain {
|
||||
server.notify_client(
|
||||
ev.entity,
|
||||
ServerGeneral::SetViewDistance(clamped_vds.terrain),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// A character delete or update was somehow initiated after the login commenced,
|
||||
// so kick the client out of "ingame" without saving any data and abort
|
||||
// the character loading process.
|
||||
handle_exit_ingame(server, entity, true);
|
||||
handle_exit_ingame(server, ev.entity, true);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_initialize_spectator(
|
||||
server: &mut Server,
|
||||
entity: EcsEntity,
|
||||
requested_view_distances: ViewDistances,
|
||||
) {
|
||||
let clamped_vds = requested_view_distances.clamp(server.settings().max_view_distance);
|
||||
server.state.initialize_spectator_data(entity, clamped_vds);
|
||||
pub fn handle_initialize_spectator(server: &mut Server, ev: InitializeSpectatorEvent) {
|
||||
let clamped_vds = ev.1.clamp(server.settings().max_view_distance);
|
||||
server.state.initialize_spectator_data(ev.0, clamped_vds);
|
||||
// Correct client if its requested VD is too high.
|
||||
if requested_view_distances.terrain != clamped_vds.terrain {
|
||||
server.notify_client(entity, ServerGeneral::SetViewDistance(clamped_vds.terrain));
|
||||
if ev.1.terrain != clamped_vds.terrain {
|
||||
server.notify_client(ev.0, ServerGeneral::SetViewDistance(clamped_vds.terrain));
|
||||
}
|
||||
sys::subscription::initialize_region_subscription(server.state.ecs(), entity);
|
||||
sys::subscription::initialize_region_subscription(server.state.ecs(), ev.0);
|
||||
}
|
||||
|
||||
pub fn handle_loaded_character_data(
|
||||
server: &mut Server,
|
||||
entity: EcsEntity,
|
||||
loaded_components: PersistedComponents,
|
||||
metadata: UpdateCharacterMetadata,
|
||||
) {
|
||||
pub fn handle_loaded_character_data(server: &mut Server, ev: UpdateCharacterDataEvent) {
|
||||
let loaded_components = PersistedComponents {
|
||||
body: ev.components.0,
|
||||
stats: ev.components.1,
|
||||
skill_set: ev.components.2,
|
||||
inventory: ev.components.3,
|
||||
waypoint: ev.components.4,
|
||||
pets: ev.components.5,
|
||||
active_abilities: ev.components.6,
|
||||
map_marker: ev.components.7,
|
||||
};
|
||||
if let Some(marker) = loaded_components.map_marker {
|
||||
server.notify_client(
|
||||
entity,
|
||||
ev.entity,
|
||||
ServerGeneral::MapMarker(comp::MapMarkerUpdate::Owned(comp::MapMarkerChange::Update(
|
||||
marker.0,
|
||||
))),
|
||||
@ -87,68 +87,62 @@ pub fn handle_loaded_character_data(
|
||||
|
||||
let result_msg = if let Err(err) = server
|
||||
.state
|
||||
.update_character_data(entity, loaded_components)
|
||||
.update_character_data(ev.entity, loaded_components)
|
||||
{
|
||||
handle_exit_ingame(server, entity, false); // remove client from in-game state
|
||||
handle_exit_ingame(server, ev.entity, false); // remove client from in-game state
|
||||
ServerGeneral::CharacterDataLoadResult(Err(err))
|
||||
} else {
|
||||
sys::subscription::initialize_region_subscription(server.state.ecs(), entity);
|
||||
sys::subscription::initialize_region_subscription(server.state.ecs(), ev.entity);
|
||||
// We notify the client with the metadata result from the operation.
|
||||
ServerGeneral::CharacterDataLoadResult(Ok(metadata))
|
||||
ServerGeneral::CharacterDataLoadResult(Ok(ev.metadata))
|
||||
};
|
||||
server.notify_client(entity, result_msg);
|
||||
server.notify_client(ev.entity, result_msg);
|
||||
}
|
||||
|
||||
pub fn handle_create_npc(
|
||||
server: &mut Server,
|
||||
pos: Pos,
|
||||
ori: Ori,
|
||||
mut npc: NpcBuilder,
|
||||
rider: Option<NpcBuilder>,
|
||||
) -> EcsEntity {
|
||||
pub fn handle_create_npc(server: &mut Server, mut ev: CreateNpcEvent) -> EcsEntity {
|
||||
let entity = server
|
||||
.state
|
||||
.create_npc(
|
||||
pos,
|
||||
ori,
|
||||
npc.stats,
|
||||
npc.skill_set,
|
||||
npc.health,
|
||||
npc.poise,
|
||||
npc.inventory,
|
||||
npc.body,
|
||||
ev.pos,
|
||||
ev.ori,
|
||||
ev.npc.stats,
|
||||
ev.npc.skill_set,
|
||||
ev.npc.health,
|
||||
ev.npc.poise,
|
||||
ev.npc.inventory,
|
||||
ev.npc.body,
|
||||
)
|
||||
.with(npc.scale);
|
||||
.with(ev.npc.scale);
|
||||
|
||||
if let Some(agent) = &mut npc.agent {
|
||||
if let Alignment::Owned(_) = &npc.alignment {
|
||||
if let Some(agent) = &mut ev.npc.agent {
|
||||
if let Alignment::Owned(_) = &ev.npc.alignment {
|
||||
agent.behavior.allow(BehaviorCapability::TRADE);
|
||||
agent.behavior.trading_behavior = TradingBehavior::AcceptFood;
|
||||
}
|
||||
}
|
||||
|
||||
let entity = entity.with(npc.alignment);
|
||||
let entity = entity.with(ev.npc.alignment);
|
||||
|
||||
let entity = if let Some(agent) = npc.agent {
|
||||
let entity = if let Some(agent) = ev.npc.agent {
|
||||
entity.with(agent)
|
||||
} else {
|
||||
entity
|
||||
};
|
||||
|
||||
let entity = if let Some(drop_items) = npc.loot.to_items() {
|
||||
let entity = if let Some(drop_items) = ev.npc.loot.to_items() {
|
||||
entity.with(ItemDrops(drop_items))
|
||||
} else {
|
||||
entity
|
||||
};
|
||||
|
||||
let entity = if let Some(home_chunk) = npc.anchor {
|
||||
let entity = if let Some(home_chunk) = ev.npc.anchor {
|
||||
entity.with(home_chunk)
|
||||
} else {
|
||||
entity
|
||||
};
|
||||
|
||||
// Rtsim entity added to IdMaps below.
|
||||
let entity = if let Some(rtsim_entity) = npc.rtsim_entity {
|
||||
let entity = if let Some(rtsim_entity) = ev.npc.rtsim_entity {
|
||||
entity.with(rtsim_entity).with(RepositionOnChunkLoad {
|
||||
needs_ground: false,
|
||||
})
|
||||
@ -156,7 +150,7 @@ pub fn handle_create_npc(
|
||||
entity
|
||||
};
|
||||
|
||||
let entity = if let Some(projectile) = npc.projectile {
|
||||
let entity = if let Some(projectile) = ev.npc.projectile {
|
||||
entity.with(projectile)
|
||||
} else {
|
||||
entity
|
||||
@ -164,7 +158,7 @@ pub fn handle_create_npc(
|
||||
|
||||
let new_entity = entity.build();
|
||||
|
||||
if let Some(rtsim_entity) = npc.rtsim_entity {
|
||||
if let Some(rtsim_entity) = ev.npc.rtsim_entity {
|
||||
server
|
||||
.state()
|
||||
.ecs()
|
||||
@ -173,7 +167,7 @@ pub fn handle_create_npc(
|
||||
}
|
||||
|
||||
// Add to group system if a pet
|
||||
if let comp::Alignment::Owned(owner_uid) = npc.alignment {
|
||||
if let comp::Alignment::Owned(owner_uid) = ev.npc.alignment {
|
||||
let state = server.state();
|
||||
let uids = state.ecs().read_storage::<Uid>();
|
||||
let clients = state.ecs().read_storage::<Client>();
|
||||
@ -204,7 +198,7 @@ pub fn handle_create_npc(
|
||||
},
|
||||
);
|
||||
}
|
||||
} else if let Some(group) = match npc.alignment {
|
||||
} else if let Some(group) = match ev.npc.alignment {
|
||||
Alignment::Wild => None,
|
||||
Alignment::Passive => None,
|
||||
Alignment::Enemy => Some(comp::group::ENEMY),
|
||||
@ -214,8 +208,13 @@ pub fn handle_create_npc(
|
||||
let _ = server.state.ecs().write_storage().insert(new_entity, group);
|
||||
}
|
||||
|
||||
if let Some(rider) = rider {
|
||||
let rider_entity = handle_create_npc(server, pos, Ori::default(), rider, None);
|
||||
if let Some(rider) = ev.rider {
|
||||
let rider_entity = handle_create_npc(server, CreateNpcEvent {
|
||||
pos: ev.pos,
|
||||
ori: Ori::default(),
|
||||
npc: rider,
|
||||
rider: None,
|
||||
});
|
||||
let uids = server.state().ecs().read_storage::<Uid>();
|
||||
let link = Mounting {
|
||||
mount: *uids.get(new_entity).expect("We just created this entity"),
|
||||
@ -231,21 +230,13 @@ pub fn handle_create_npc(
|
||||
new_entity
|
||||
}
|
||||
|
||||
pub fn handle_create_ship(
|
||||
server: &mut Server,
|
||||
pos: Pos,
|
||||
ori: Ori,
|
||||
ship: comp::ship::Body,
|
||||
rtsim_entity: Option<RtSimEntity>,
|
||||
driver: Option<NpcBuilder>,
|
||||
passengers: Vec<NpcBuilder>,
|
||||
) {
|
||||
let collider = ship.make_collider();
|
||||
pub fn handle_create_ship(server: &mut Server, ev: CreateShipEvent) {
|
||||
let collider = ev.ship.make_collider();
|
||||
let voxel_colliders_manifest = VOXEL_COLLIDER_MANIFEST.read();
|
||||
|
||||
// TODO: Find better solution for this, maybe something like a serverside block
|
||||
// of interests.
|
||||
let (mut steering, mut seats) = {
|
||||
let (mut steering, mut _seats) = {
|
||||
let mut steering = Vec::new();
|
||||
let mut seats = Vec::new();
|
||||
|
||||
@ -263,7 +254,9 @@ pub fn handle_create_ship(
|
||||
(steering.into_iter(), seats.into_iter())
|
||||
};
|
||||
|
||||
let mut entity = server.state.create_ship(pos, ori, ship, |_| collider);
|
||||
let mut entity = server
|
||||
.state
|
||||
.create_ship(ev.pos, ev.ori, ev.ship, |_| collider);
|
||||
/*
|
||||
if let Some(mut agent) = agent {
|
||||
let (kp, ki, kd) = pid_coefficients(&Body::Ship(ship));
|
||||
@ -273,13 +266,18 @@ pub fn handle_create_ship(
|
||||
entity = entity.with(agent);
|
||||
}
|
||||
*/
|
||||
if let Some(rtsim_vehicle) = rtsim_entity {
|
||||
if let Some(rtsim_vehicle) = ev.rtsim_entity {
|
||||
entity = entity.with(rtsim_vehicle);
|
||||
}
|
||||
let entity = entity.build();
|
||||
|
||||
if let Some(driver) = driver {
|
||||
let npc_entity = handle_create_npc(server, pos, ori, driver, None);
|
||||
if let Some(driver) = ev.driver {
|
||||
let npc_entity = handle_create_npc(server, CreateNpcEvent {
|
||||
pos: ev.pos,
|
||||
ori: ev.ori,
|
||||
npc: driver,
|
||||
rider: None,
|
||||
});
|
||||
|
||||
let uids = server.state.ecs().read_storage::<Uid>();
|
||||
let (rider_uid, mount_uid) = uids
|
||||
@ -312,14 +310,14 @@ pub fn handle_create_ship(
|
||||
}
|
||||
}
|
||||
|
||||
for passenger in passengers {
|
||||
let npc_entity = handle_create_npc(
|
||||
server,
|
||||
Pos(pos.0 + Vec3::unit_z() * 5.0),
|
||||
ori,
|
||||
passenger,
|
||||
None,
|
||||
);
|
||||
/*
|
||||
for passenger in ev.passengers {
|
||||
let npc_entity = handle_create_npc(server, CreateNpcEvent {
|
||||
pos: Pos(ev.pos.0 + Vec3::unit_z() * 5.0),
|
||||
ori: ev.ori,
|
||||
npc: passenger,
|
||||
rider: None,
|
||||
});
|
||||
if let Some((rider_pos, rider_block)) = seats.next() {
|
||||
let uids = server.state.ecs().read_storage::<Uid>();
|
||||
let (rider_uid, mount_uid) = uids
|
||||
@ -342,62 +340,54 @@ pub fn handle_create_ship(
|
||||
.expect("Failed to link passanger to ship");
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
pub fn handle_shoot(
|
||||
server: &mut Server,
|
||||
entity: EcsEntity,
|
||||
pos: Pos,
|
||||
dir: Dir,
|
||||
body: Body,
|
||||
light: Option<LightEmitter>,
|
||||
projectile: Projectile,
|
||||
speed: f32,
|
||||
object: Option<Object>,
|
||||
) {
|
||||
pub fn handle_shoot(server: &mut Server, ev: ShootEvent) {
|
||||
let state = server.state_mut();
|
||||
|
||||
let pos = pos.0;
|
||||
let pos = ev.pos.0;
|
||||
|
||||
let vel = *dir * speed
|
||||
let vel = *ev.dir * ev.speed
|
||||
+ state
|
||||
.ecs()
|
||||
.read_storage::<Vel>()
|
||||
.get(entity)
|
||||
.get(ev.entity)
|
||||
.map_or(Vec3::zero(), |v| v.0);
|
||||
|
||||
// Add an outcome
|
||||
state
|
||||
.ecs()
|
||||
.read_resource::<EventBus<Outcome>>()
|
||||
.emit_now(Outcome::ProjectileShot { pos, body, vel });
|
||||
.emit_now(Outcome::ProjectileShot {
|
||||
pos,
|
||||
body: ev.body,
|
||||
vel,
|
||||
});
|
||||
|
||||
let mut builder = state.create_projectile(Pos(pos), Vel(vel), body, projectile);
|
||||
if let Some(light) = light {
|
||||
let mut builder = state.create_projectile(Pos(pos), Vel(vel), ev.body, ev.projectile);
|
||||
if let Some(light) = ev.light {
|
||||
builder = builder.with(light)
|
||||
}
|
||||
if let Some(object) = object {
|
||||
if let Some(object) = ev.object {
|
||||
builder = builder.with(object)
|
||||
}
|
||||
|
||||
builder.build();
|
||||
}
|
||||
|
||||
pub fn handle_shockwave(
|
||||
server: &mut Server,
|
||||
properties: shockwave::Properties,
|
||||
pos: Pos,
|
||||
ori: Ori,
|
||||
) {
|
||||
pub fn handle_shockwave(server: &mut Server, ev: ShockwaveEvent) {
|
||||
let state = server.state_mut();
|
||||
state.create_shockwave(properties, pos, ori).build();
|
||||
state
|
||||
.create_shockwave(ev.properties, ev.pos, ev.ori)
|
||||
.build();
|
||||
}
|
||||
|
||||
pub fn handle_create_waypoint(server: &mut Server, pos: Vec3<f32>) {
|
||||
pub fn handle_create_waypoint(server: &mut Server, ev: CreateWaypointEvent) {
|
||||
let time = server.state.get_time();
|
||||
server
|
||||
.state
|
||||
.create_object(Pos(pos), comp::object::Body::CampfireLit)
|
||||
.create_object(Pos(ev.0), comp::object::Body::CampfireLit)
|
||||
.with(LightEmitter {
|
||||
col: Rgb::new(1.0, 0.3, 0.1),
|
||||
strength: 5.0,
|
||||
@ -435,9 +425,40 @@ pub fn handle_create_waypoint(server: &mut Server, pos: Vec3<f32>) {
|
||||
.build();
|
||||
}
|
||||
|
||||
pub fn handle_create_teleporter(server: &mut Server, pos: Vec3<f32>, portal: PortalData) {
|
||||
pub fn handle_create_teleporter(server: &mut Server, ev: CreateTeleporterEvent) {
|
||||
server
|
||||
.state
|
||||
.create_teleporter(comp::Pos(pos), portal)
|
||||
.create_teleporter(comp::Pos(ev.0), ev.1)
|
||||
.build();
|
||||
}
|
||||
|
||||
pub fn handle_create_item_drop(server: &mut Server, ev: CreateItemDropEvent) {
|
||||
println!("Handle item drop event.");
|
||||
dbg!(ev.vel);
|
||||
server
|
||||
.state
|
||||
.create_item_drop(ev.pos, ev.ori, ev.vel, ev.item, ev.loot_owner);
|
||||
}
|
||||
|
||||
pub fn handle_create_object(
|
||||
server: &mut Server,
|
||||
CreateObjectEvent {
|
||||
pos,
|
||||
vel,
|
||||
body,
|
||||
object,
|
||||
item,
|
||||
light_emitter,
|
||||
stats,
|
||||
}: CreateObjectEvent,
|
||||
) {
|
||||
server
|
||||
.state
|
||||
.create_object(pos, body)
|
||||
.with(vel)
|
||||
.maybe_with(object)
|
||||
.maybe_with(item)
|
||||
.maybe_with(light_emitter)
|
||||
.maybe_with(stats)
|
||||
.build();
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,4 +1,4 @@
|
||||
use crate::{client::Client, Server};
|
||||
use crate::client::Client;
|
||||
use common::{
|
||||
comp::{
|
||||
self,
|
||||
@ -6,26 +6,24 @@ use common::{
|
||||
invite::{InviteKind, PendingInvites},
|
||||
ChatType, GroupManip,
|
||||
},
|
||||
uid::Uid,
|
||||
};
|
||||
use common_net::{msg::ServerGeneral, sync::WorldSyncExt};
|
||||
use common_state::State;
|
||||
use specs::{
|
||||
world::{Entity, WorldExt},
|
||||
ReadStorage, WriteStorage,
|
||||
event::GroupManipEvent,
|
||||
uid::{IdMaps, Uid},
|
||||
};
|
||||
use common_net::msg::ServerGeneral;
|
||||
use specs::{world::Entity, Entities, Read, ReadStorage, Write, WriteStorage};
|
||||
|
||||
use super::ServerEvent;
|
||||
|
||||
pub fn can_invite(
|
||||
state: &State,
|
||||
clients: &ReadStorage<'_, Client>,
|
||||
groups: &ReadStorage<'_, Group>,
|
||||
group_manager: &GroupManager,
|
||||
pending_invites: &mut WriteStorage<'_, PendingInvites>,
|
||||
max_group_size: u32,
|
||||
inviter: Entity,
|
||||
invitee: Entity,
|
||||
) -> bool {
|
||||
// Disallow inviting entity that is already in your group
|
||||
let groups = state.ecs().read_storage::<Group>();
|
||||
let group_manager = state.ecs().read_resource::<GroupManager>();
|
||||
let already_in_same_group = groups.get(inviter).map_or(false, |group| {
|
||||
group_manager
|
||||
.group_info(*group)
|
||||
@ -45,9 +43,7 @@ pub fn can_invite(
|
||||
|
||||
// Check if group max size is already reached
|
||||
// Adding the current number of pending invites
|
||||
let group_size_limit_reached = state
|
||||
.ecs()
|
||||
.read_storage()
|
||||
let group_size_limit_reached = groups
|
||||
.get(inviter)
|
||||
.copied()
|
||||
.and_then(|group| {
|
||||
@ -112,21 +108,31 @@ pub fn update_map_markers<'a>(
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: turn chat messages into enums
|
||||
pub fn handle_group(server: &mut Server, entity: Entity, manip: GroupManip) {
|
||||
impl ServerEvent for GroupManipEvent {
|
||||
type SystemData<'a> = (
|
||||
Entities<'a>,
|
||||
Write<'a, GroupManager>,
|
||||
Read<'a, IdMaps>,
|
||||
WriteStorage<'a, Group>,
|
||||
ReadStorage<'a, Client>,
|
||||
ReadStorage<'a, Uid>,
|
||||
ReadStorage<'a, comp::Alignment>,
|
||||
ReadStorage<'a, comp::MapMarker>,
|
||||
);
|
||||
|
||||
fn handle(
|
||||
events: impl ExactSizeIterator<Item = Self>,
|
||||
(entities, mut group_manager, id_maps, mut groups, clients, uids, alignments, map_markers): Self::SystemData<'_>,
|
||||
) {
|
||||
for GroupManipEvent(entity, manip) in events {
|
||||
match manip {
|
||||
GroupManip::Leave => {
|
||||
let state = server.state_mut();
|
||||
let clients = state.ecs().read_storage::<Client>();
|
||||
let uids = state.ecs().read_storage::<Uid>();
|
||||
let mut group_manager = state.ecs().write_resource::<GroupManager>();
|
||||
let map_markers = state.ecs().read_storage::<comp::MapMarker>();
|
||||
group_manager.leave_group(
|
||||
entity,
|
||||
&mut state.ecs().write_storage(),
|
||||
&state.ecs().read_storage(),
|
||||
&mut groups,
|
||||
&alignments,
|
||||
&uids,
|
||||
&state.ecs().entities(),
|
||||
&entities,
|
||||
&mut |entity, group_change| {
|
||||
clients
|
||||
.get(entity)
|
||||
@ -143,12 +149,7 @@ pub fn handle_group(server: &mut Server, entity: Entity, manip: GroupManip) {
|
||||
);
|
||||
},
|
||||
GroupManip::Kick(uid) => {
|
||||
let state = server.state_mut();
|
||||
let clients = state.ecs().read_storage::<Client>();
|
||||
let uids = state.ecs().read_storage::<Uid>();
|
||||
let alignments = state.ecs().read_storage::<comp::Alignment>();
|
||||
|
||||
let target = match state.ecs().entity_from_uid(uid) {
|
||||
let target = match id_maps.uid_entity(uid) {
|
||||
Some(t) => t,
|
||||
None => {
|
||||
// Inform of failure
|
||||
@ -184,9 +185,6 @@ pub fn handle_group(server: &mut Server, entity: Entity, manip: GroupManip) {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut groups = state.ecs().write_storage::<Group>();
|
||||
let mut group_manager = state.ecs().write_resource::<GroupManager>();
|
||||
let map_markers = state.ecs().read_storage::<comp::MapMarker>();
|
||||
// Make sure kicker is the group leader
|
||||
match groups
|
||||
.get(target)
|
||||
@ -197,9 +195,9 @@ pub fn handle_group(server: &mut Server, entity: Entity, manip: GroupManip) {
|
||||
group_manager.leave_group(
|
||||
target,
|
||||
&mut groups,
|
||||
&state.ecs().read_storage(),
|
||||
&alignments,
|
||||
&uids,
|
||||
&state.ecs().entities(),
|
||||
&entities,
|
||||
&mut |entity, group_change| {
|
||||
clients
|
||||
.get(entity)
|
||||
@ -209,7 +207,12 @@ pub fn handle_group(server: &mut Server, entity: Entity, manip: GroupManip) {
|
||||
.map(|g| (g, c))
|
||||
})
|
||||
.map(|(g, c)| {
|
||||
update_map_markers(&map_markers, &uids, c, &group_change);
|
||||
update_map_markers(
|
||||
&map_markers,
|
||||
&uids,
|
||||
c,
|
||||
&group_change,
|
||||
);
|
||||
c.send_fallible(ServerGeneral::GroupUpdate(g));
|
||||
});
|
||||
},
|
||||
@ -251,10 +254,7 @@ pub fn handle_group(server: &mut Server, entity: Entity, manip: GroupManip) {
|
||||
}
|
||||
},
|
||||
GroupManip::AssignLeader(uid) => {
|
||||
let state = server.state_mut();
|
||||
let clients = state.ecs().read_storage::<Client>();
|
||||
let uids = state.ecs().read_storage::<Uid>();
|
||||
let target = match state.ecs().entity_from_uid(uid) {
|
||||
let target = match id_maps.uid_entity(uid) {
|
||||
Some(t) => t,
|
||||
None => {
|
||||
// Inform of failure
|
||||
@ -267,9 +267,6 @@ pub fn handle_group(server: &mut Server, entity: Entity, manip: GroupManip) {
|
||||
return;
|
||||
},
|
||||
};
|
||||
let groups = state.ecs().read_storage::<Group>();
|
||||
let mut group_manager = state.ecs().write_resource::<GroupManager>();
|
||||
let map_markers = state.ecs().read_storage::<comp::MapMarker>();
|
||||
// Make sure assigner is the group leader
|
||||
match groups
|
||||
.get(target)
|
||||
@ -280,8 +277,8 @@ pub fn handle_group(server: &mut Server, entity: Entity, manip: GroupManip) {
|
||||
group_manager.assign_leader(
|
||||
target,
|
||||
&groups,
|
||||
&state.ecs().entities(),
|
||||
&state.ecs().read_storage(),
|
||||
&entities,
|
||||
&alignments,
|
||||
&uids,
|
||||
|entity, group_change| {
|
||||
clients
|
||||
@ -292,7 +289,12 @@ pub fn handle_group(server: &mut Server, entity: Entity, manip: GroupManip) {
|
||||
.map(|g| (g, c))
|
||||
})
|
||||
.map(|(g, c)| {
|
||||
update_map_markers(&map_markers, &uids, c, &group_change);
|
||||
update_map_markers(
|
||||
&map_markers,
|
||||
&uids,
|
||||
c,
|
||||
&group_change,
|
||||
);
|
||||
c.send_fallible(ServerGeneral::GroupUpdate(g));
|
||||
});
|
||||
},
|
||||
@ -317,7 +319,8 @@ pub fn handle_group(server: &mut Server, entity: Entity, manip: GroupManip) {
|
||||
if let Some(client) = clients.get(entity) {
|
||||
client.send_fallible(ServerGeneral::server_msg(
|
||||
ChatType::Meta,
|
||||
"Transfer failed: You are not the leader of the target's group.",
|
||||
"Transfer failed: You are not the leader of the target's \
|
||||
group.",
|
||||
));
|
||||
}
|
||||
},
|
||||
@ -333,4 +336,6 @@ pub fn handle_group(server: &mut Server, entity: Entity, manip: GroupManip) {
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +1,21 @@
|
||||
use crate::{client::Client, Server};
|
||||
use crate::client::Client;
|
||||
use common::event::RequestSiteInfoEvent;
|
||||
use common_net::msg::{world_msg::EconomyInfo, ServerGeneral};
|
||||
use specs::{Entity as EcsEntity, WorldExt};
|
||||
use specs::{ReadExpect, ReadStorage};
|
||||
use std::collections::HashMap;
|
||||
use world::IndexOwned;
|
||||
|
||||
use super::ServerEvent;
|
||||
|
||||
#[cfg(not(feature = "worldgen"))]
|
||||
pub fn handle_site_info(server: &Server, entity: EcsEntity, id: u64) {
|
||||
impl ServerEvent for RequestSiteInfoEvent {
|
||||
type SystemData<'a> = ReadStorage<'a, Client>;
|
||||
|
||||
fn handle(events: impl ExactSizeIterator<Item = Self>, clients: Self::SystemData<'_>) {
|
||||
for ev in events {
|
||||
if let Some(client) = clients.get(ev.entity) {
|
||||
let info = EconomyInfo {
|
||||
id,
|
||||
id: ev.id,
|
||||
population: 0,
|
||||
stock: HashMap::new(),
|
||||
labor_values: HashMap::new(),
|
||||
@ -16,23 +25,26 @@ pub fn handle_site_info(server: &Server, entity: EcsEntity, id: u64) {
|
||||
resources: HashMap::new(),
|
||||
};
|
||||
let msg = ServerGeneral::SiteEconomy(info);
|
||||
server
|
||||
.state
|
||||
.ecs()
|
||||
.read_storage::<Client>()
|
||||
.get(entity)
|
||||
.map(|c| c.send(msg));
|
||||
client.send_fallible(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "worldgen")]
|
||||
pub fn handle_site_info(server: &Server, entity: EcsEntity, id: u64) {
|
||||
let site_id = server.index.sites.recreate_id(id);
|
||||
impl ServerEvent for RequestSiteInfoEvent {
|
||||
type SystemData<'a> = (ReadExpect<'a, IndexOwned>, ReadStorage<'a, Client>);
|
||||
|
||||
fn handle(events: impl ExactSizeIterator<Item = Self>, (index, clients): Self::SystemData<'_>) {
|
||||
for ev in events {
|
||||
if let Some(client) = clients.get(ev.entity) {
|
||||
let site_id = index.sites.recreate_id(ev.id);
|
||||
let info = if let Some(site_id) = site_id {
|
||||
let site = server.index.sites.get(site_id);
|
||||
let site = index.sites.get(site_id);
|
||||
site.economy.get_information(site_id)
|
||||
} else {
|
||||
EconomyInfo {
|
||||
id,
|
||||
id: ev.id,
|
||||
population: 0,
|
||||
stock: HashMap::new(),
|
||||
labor_values: HashMap::new(),
|
||||
@ -43,10 +55,8 @@ pub fn handle_site_info(server: &Server, entity: EcsEntity, id: u64) {
|
||||
}
|
||||
};
|
||||
let msg = ServerGeneral::SiteEconomy(info);
|
||||
server
|
||||
.state
|
||||
.ecs()
|
||||
.read_storage::<Client>()
|
||||
.get(entity)
|
||||
.map(|c| c.send(msg));
|
||||
client.send_fallible(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,64 +1,74 @@
|
||||
use specs::{world::WorldExt, Entity as EcsEntity, Join};
|
||||
use common_state::{BlockChange, ScheduledBlockChange};
|
||||
use specs::{DispatcherBuilder, Join, ReadExpect, ReadStorage, WriteExpect, WriteStorage};
|
||||
use vek::*;
|
||||
|
||||
use common::{
|
||||
assets::{self, Concatenate},
|
||||
comp::{
|
||||
self,
|
||||
agent::{AgentEvent, Sound, SoundKind},
|
||||
dialogue::Subject,
|
||||
agent::{AgentEvent, SoundKind},
|
||||
inventory::slot::EquipSlot,
|
||||
item::{flatten_counted_items, MaterialStatManifest},
|
||||
loot_owner::LootOwnerKind,
|
||||
pet::is_mountable,
|
||||
tool::{AbilityMap, ToolKind},
|
||||
Inventory, LootOwner, Pos, SkillGroupKind,
|
||||
tool::AbilityMap,
|
||||
},
|
||||
consts::{
|
||||
MAX_INTERACT_RANGE, MAX_MOUNT_RANGE, MAX_NPCINTERACT_RANGE, MAX_SPRITE_MOUNT_RANGE,
|
||||
SOUND_TRAVEL_DIST_PER_VOLUME,
|
||||
consts::{MAX_INTERACT_RANGE, MAX_NPCINTERACT_RANGE, SOUND_TRAVEL_DIST_PER_VOLUME},
|
||||
event::{
|
||||
CreateItemDropEvent, CreateSpriteEvent, EventBus, MineBlockEvent, NpcInteractEvent,
|
||||
SetLanternEvent, SetPetStayEvent, SoundEvent, TamePetEvent, ToggleSpriteLightEvent,
|
||||
},
|
||||
event::EventBus,
|
||||
link::Is,
|
||||
mounting::{Mount, Mounting, Rider, VolumeMounting, VolumePos, VolumeRider},
|
||||
mounting::Mount,
|
||||
outcome::Outcome,
|
||||
rtsim::RtSimEntity,
|
||||
terrain::{Block, SpriteKind},
|
||||
uid::{IdMaps, Uid},
|
||||
terrain::{Block, SpriteKind, TerrainGrid},
|
||||
uid::Uid,
|
||||
util::Dir,
|
||||
vol::ReadVol,
|
||||
};
|
||||
use common_net::sync::WorldSyncExt;
|
||||
|
||||
use crate::{rtsim::RtSim, state_ext::StateExt, Server, Time};
|
||||
use crate::{Server, Time};
|
||||
|
||||
use crate::pet::tame_pet;
|
||||
use hashbrown::{HashMap, HashSet};
|
||||
use lazy_static::lazy_static;
|
||||
use serde::Deserialize;
|
||||
use std::{iter::FromIterator, sync::Arc};
|
||||
use std::iter::FromIterator;
|
||||
|
||||
pub fn handle_lantern(server: &mut Server, entity: EcsEntity, enable: bool) {
|
||||
let ecs = server.state_mut().ecs();
|
||||
use super::{event_dispatch, mounting::within_mounting_range, ServerEvent};
|
||||
|
||||
let lantern_exists = ecs
|
||||
.read_storage::<comp::LightEmitter>()
|
||||
pub(super) fn register_event_systems(builder: &mut DispatcherBuilder) {
|
||||
event_dispatch::<SetLanternEvent>(builder);
|
||||
event_dispatch::<NpcInteractEvent>(builder);
|
||||
event_dispatch::<SetPetStayEvent>(builder);
|
||||
event_dispatch::<MineBlockEvent>(builder);
|
||||
event_dispatch::<SoundEvent>(builder);
|
||||
event_dispatch::<CreateSpriteEvent>(builder);
|
||||
event_dispatch::<ToggleSpriteLightEvent>(builder);
|
||||
}
|
||||
|
||||
impl ServerEvent for SetLanternEvent {
|
||||
type SystemData<'a> = (
|
||||
WriteStorage<'a, comp::LightEmitter>,
|
||||
ReadStorage<'a, comp::Inventory>,
|
||||
ReadStorage<'a, comp::Health>,
|
||||
);
|
||||
|
||||
fn handle(
|
||||
events: impl ExactSizeIterator<Item = Self>,
|
||||
(mut light_emitters, inventories, healths): Self::SystemData<'_>,
|
||||
) {
|
||||
for SetLanternEvent(entity, enable) in events {
|
||||
let lantern_exists = light_emitters
|
||||
.get(entity)
|
||||
.map_or(false, |light| light.strength > 0.0);
|
||||
|
||||
if lantern_exists != enable {
|
||||
if !enable {
|
||||
server
|
||||
.state_mut()
|
||||
.ecs()
|
||||
.write_storage::<comp::LightEmitter>()
|
||||
.remove(entity);
|
||||
} else if ecs // Only enable lantern if entity is alive
|
||||
.read_storage::<comp::Health>()
|
||||
.get(entity)
|
||||
.map_or(true, |h| !h.is_dead)
|
||||
{
|
||||
let inventory_storage = ecs.read_storage::<Inventory>();
|
||||
let lantern_info = inventory_storage
|
||||
light_emitters.remove(entity);
|
||||
}
|
||||
// Only enable lantern if entity is alive
|
||||
else if healths.get(entity).map_or(true, |h| !h.is_dead) {
|
||||
let lantern_info = inventories
|
||||
.get(entity)
|
||||
.and_then(|inventory| inventory.equipped(EquipSlot::Lantern))
|
||||
.and_then(|item| {
|
||||
@ -69,9 +79,7 @@ pub fn handle_lantern(server: &mut Server, entity: EcsEntity, enable: bool) {
|
||||
}
|
||||
});
|
||||
if let Some((col, strength, flicker)) = lantern_info {
|
||||
let _ =
|
||||
ecs.write_storage::<comp::LightEmitter>()
|
||||
.insert(entity, comp::LightEmitter {
|
||||
let _ = light_emitters.insert(entity, comp::LightEmitter {
|
||||
col,
|
||||
strength,
|
||||
flicker,
|
||||
@ -80,181 +88,65 @@ pub fn handle_lantern(server: &mut Server, entity: EcsEntity, enable: bool) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_npc_interaction(
|
||||
server: &mut Server,
|
||||
interactor: EcsEntity,
|
||||
npc_entity: EcsEntity,
|
||||
subject: Subject,
|
||||
) {
|
||||
let state = server.state_mut();
|
||||
impl ServerEvent for NpcInteractEvent {
|
||||
type SystemData<'a> = (
|
||||
WriteStorage<'a, comp::Agent>,
|
||||
ReadStorage<'a, comp::Pos>,
|
||||
ReadStorage<'a, Uid>,
|
||||
);
|
||||
|
||||
fn handle(
|
||||
events: impl ExactSizeIterator<Item = Self>,
|
||||
(mut agents, positions, uids): Self::SystemData<'_>,
|
||||
) {
|
||||
for NpcInteractEvent(interactor, npc_entity, subject) in events {
|
||||
let within_range = {
|
||||
let positions = state.ecs().read_storage::<Pos>();
|
||||
positions
|
||||
.get(interactor)
|
||||
.zip(positions.get(npc_entity))
|
||||
.map_or(false, |(interactor_pos, npc_pos)| {
|
||||
interactor_pos.0.distance_squared(npc_pos.0) <= MAX_NPCINTERACT_RANGE.powi(2)
|
||||
interactor_pos.0.distance_squared(npc_pos.0)
|
||||
<= MAX_NPCINTERACT_RANGE.powi(2)
|
||||
})
|
||||
};
|
||||
|
||||
if within_range
|
||||
&& let Some(agent) = state
|
||||
.ecs()
|
||||
.write_storage::<comp::Agent>()
|
||||
.get_mut(npc_entity)
|
||||
&& let Some(agent) = agents.get_mut(npc_entity)
|
||||
&& agent.target.is_none()
|
||||
{
|
||||
if let Some(interactor_uid) = state.ecs().uid_from_entity(interactor) {
|
||||
if let Some(interactor_uid) = uids.get(interactor) {
|
||||
agent
|
||||
.inbox
|
||||
.push_back(AgentEvent::Talk(interactor_uid, subject));
|
||||
.push_back(AgentEvent::Talk(*interactor_uid, subject));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_mount(server: &mut Server, rider: EcsEntity, mount: EcsEntity) {
|
||||
let state = server.state_mut();
|
||||
|
||||
let within_range = {
|
||||
let positions = state.ecs().read_storage::<Pos>();
|
||||
within_mounting_range(positions.get(rider), positions.get(mount))
|
||||
};
|
||||
|
||||
if within_range {
|
||||
let uids = state.ecs().read_storage::<Uid>();
|
||||
if let (Some(rider_uid), Some(mount_uid)) =
|
||||
(uids.get(rider).copied(), uids.get(mount).copied())
|
||||
{
|
||||
let is_pet_of = |mount, rider_uid| {
|
||||
matches!(
|
||||
state
|
||||
.ecs()
|
||||
.read_storage::<comp::Alignment>()
|
||||
.get(mount),
|
||||
Some(comp::Alignment::Owned(owner)) if *owner == rider_uid,
|
||||
)
|
||||
};
|
||||
|
||||
let can_ride = state
|
||||
.ecs()
|
||||
.read_storage()
|
||||
.get(mount)
|
||||
.map_or(false, |mount_body| {
|
||||
is_mountable(mount_body, state.ecs().read_storage().get(rider))
|
||||
});
|
||||
|
||||
let is_stay = state
|
||||
.ecs()
|
||||
.read_storage::<comp::Agent>()
|
||||
.get(mount)
|
||||
.and_then(|x| x.stay_pos)
|
||||
.is_some();
|
||||
|
||||
if (is_pet_of(mount, rider_uid) || is_pet_of(rider, mount_uid)) && can_ride && !is_stay
|
||||
{
|
||||
drop(uids);
|
||||
let _ = state.link(Mounting {
|
||||
mount: mount_uid,
|
||||
rider: rider_uid,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_mount_volume(server: &mut Server, rider: EcsEntity, volume_pos: VolumePos) {
|
||||
let state = server.state_mut();
|
||||
|
||||
let block_transform = volume_pos.get_block_and_transform(
|
||||
&state.terrain(),
|
||||
&state.ecs().read_resource(),
|
||||
|e| {
|
||||
state
|
||||
.read_storage()
|
||||
.get(e)
|
||||
.copied()
|
||||
.zip(state.read_storage().get(e).copied())
|
||||
},
|
||||
&state.read_storage(),
|
||||
impl ServerEvent for SetPetStayEvent {
|
||||
type SystemData<'a> = (
|
||||
WriteStorage<'a, comp::Agent>,
|
||||
WriteStorage<'a, comp::CharacterActivity>,
|
||||
ReadStorage<'a, comp::Pos>,
|
||||
ReadStorage<'a, comp::Alignment>,
|
||||
ReadStorage<'a, Is<Mount>>,
|
||||
ReadStorage<'a, Uid>,
|
||||
);
|
||||
|
||||
if let Some((mat, _, block)) = block_transform
|
||||
&& let Some(mount_offset) = block.mount_offset()
|
||||
{
|
||||
let mount_pos = (mat * mount_offset.0.with_w(1.0)).xyz();
|
||||
let within_range = {
|
||||
let positions = state.ecs().read_storage::<Pos>();
|
||||
positions.get(rider).map_or(false, |pos| {
|
||||
pos.0.distance_squared(mount_pos) < MAX_SPRITE_MOUNT_RANGE.powi(2)
|
||||
})
|
||||
};
|
||||
|
||||
let maybe_uid = state.ecs().read_storage::<Uid>().get(rider).copied();
|
||||
|
||||
if let Some(rider) = maybe_uid
|
||||
&& within_range
|
||||
{
|
||||
let _link_successful = state
|
||||
.link(VolumeMounting {
|
||||
pos: volume_pos,
|
||||
block,
|
||||
rider,
|
||||
})
|
||||
.is_ok();
|
||||
#[cfg(feature = "worldgen")]
|
||||
if _link_successful {
|
||||
let uid_allocator = state.ecs().read_resource::<IdMaps>();
|
||||
if let Some(rider_entity) = uid_allocator.uid_entity(rider)
|
||||
&& let Some(rider_actor) = state.entity_as_actor(rider_entity)
|
||||
&& let Some(volume_pos) = volume_pos.try_map_entity(|uid| {
|
||||
let entity = uid_allocator.uid_entity(uid)?;
|
||||
state.read_storage::<RtSimEntity>().get(entity).map(|v| v.0)
|
||||
})
|
||||
{
|
||||
state
|
||||
.ecs()
|
||||
.write_resource::<RtSim>()
|
||||
.hook_character_mount_volume(
|
||||
&state.ecs().read_resource::<Arc<world::World>>(),
|
||||
state
|
||||
.ecs()
|
||||
.read_resource::<world::IndexOwned>()
|
||||
.as_index_ref(),
|
||||
volume_pos,
|
||||
rider_actor,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_unmount(server: &mut Server, rider: EcsEntity) {
|
||||
let state = server.state_mut();
|
||||
state.ecs().write_storage::<Is<Rider>>().remove(rider);
|
||||
state.ecs().write_storage::<Is<VolumeRider>>().remove(rider);
|
||||
}
|
||||
|
||||
pub fn handle_set_pet_stay(
|
||||
server: &mut Server,
|
||||
command_giver: EcsEntity,
|
||||
pet: EcsEntity,
|
||||
stay: bool,
|
||||
) {
|
||||
let state = server.state_mut();
|
||||
let positions = state.ecs().read_storage::<Pos>();
|
||||
let is_owner = state
|
||||
.ecs()
|
||||
.uid_from_entity(command_giver)
|
||||
.map_or(false, |owner_uid| {
|
||||
fn handle(
|
||||
events: impl ExactSizeIterator<Item = Self>,
|
||||
(mut agents, mut character_activities, positions, alignments, is_mounts, uids): Self::SystemData<'_>,
|
||||
) {
|
||||
for SetPetStayEvent(command_giver, pet, stay) in events {
|
||||
let is_owner = uids.get(command_giver).map_or(false, |owner_uid| {
|
||||
matches!(
|
||||
state
|
||||
.ecs()
|
||||
.read_storage::<comp::Alignment>()
|
||||
.get(pet),
|
||||
Some(comp::Alignment::Owned(pet_owner)) if *pet_owner == owner_uid,
|
||||
alignments.get(pet),
|
||||
Some(comp::Alignment::Owned(pet_owner)) if *pet_owner == *owner_uid,
|
||||
)
|
||||
});
|
||||
|
||||
@ -262,25 +154,16 @@ pub fn handle_set_pet_stay(
|
||||
let stay = stay && current_pet_position.is_some();
|
||||
if is_owner
|
||||
&& within_mounting_range(positions.get(command_giver), positions.get(pet))
|
||||
&& state.ecs().read_storage::<Is<Mount>>().get(pet).is_none()
|
||||
&& is_mounts.get(pet).is_none()
|
||||
{
|
||||
state
|
||||
.ecs()
|
||||
.write_storage::<comp::CharacterActivity>()
|
||||
character_activities
|
||||
.get_mut(pet)
|
||||
.map(|mut activity| activity.is_pet_staying = stay);
|
||||
state
|
||||
.ecs()
|
||||
.write_storage::<comp::Agent>()
|
||||
agents
|
||||
.get_mut(pet)
|
||||
.map(|s| s.stay_pos = current_pet_position.filter(|_| stay));
|
||||
}
|
||||
}
|
||||
|
||||
fn within_mounting_range(player_position: Option<&Pos>, mount_position: Option<&Pos>) -> bool {
|
||||
match (player_position, mount_position) {
|
||||
(Some(ppos), Some(ipos)) => ppos.0.distance_squared(ipos.0) < MAX_MOUNT_RANGE.powi(2),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -303,30 +186,50 @@ lazy_static! {
|
||||
);
|
||||
}
|
||||
|
||||
pub fn handle_mine_block(
|
||||
server: &mut Server,
|
||||
entity: EcsEntity,
|
||||
pos: Vec3<i32>,
|
||||
tool: Option<ToolKind>,
|
||||
) {
|
||||
let state = server.state_mut();
|
||||
if state.can_set_block(pos) {
|
||||
let block = state.terrain().get(pos).ok().copied();
|
||||
if let Some(block) = block.filter(|b| b.mine_tool().map_or(false, |t| Some(t) == tool)) {
|
||||
impl ServerEvent for MineBlockEvent {
|
||||
type SystemData<'a> = (
|
||||
WriteExpect<'a, BlockChange>,
|
||||
ReadExpect<'a, TerrainGrid>,
|
||||
ReadExpect<'a, MaterialStatManifest>,
|
||||
ReadExpect<'a, AbilityMap>,
|
||||
ReadExpect<'a, EventBus<CreateItemDropEvent>>,
|
||||
ReadExpect<'a, EventBus<Outcome>>,
|
||||
WriteStorage<'a, comp::SkillSet>,
|
||||
ReadStorage<'a, Uid>,
|
||||
);
|
||||
|
||||
fn handle(
|
||||
events: impl ExactSizeIterator<Item = Self>,
|
||||
(
|
||||
mut block_change,
|
||||
terrain,
|
||||
msm,
|
||||
ability_map,
|
||||
create_item_drop_events,
|
||||
outcomes,
|
||||
mut skill_sets,
|
||||
uids,
|
||||
): Self::SystemData<'_>,
|
||||
) {
|
||||
use rand::Rng;
|
||||
let mut rng = rand::thread_rng();
|
||||
let mut create_item_drop_emitter = create_item_drop_events.emitter();
|
||||
let mut outcome_emitter = outcomes.emitter();
|
||||
for ev in events {
|
||||
if block_change.can_set_block(ev.pos) {
|
||||
let block = terrain.get(ev.pos).ok().copied();
|
||||
if let Some(block) =
|
||||
block.filter(|b| b.mine_tool().map_or(false, |t| Some(t) == ev.tool))
|
||||
{
|
||||
// Drop item if one is recoverable from the block
|
||||
if let Some(items) = comp::Item::try_reclaim_from_block(block) {
|
||||
let msm = &MaterialStatManifest::load().read();
|
||||
let ability_map = &AbilityMap::load().read();
|
||||
let mut items: Vec<_> = flatten_counted_items(&items, ability_map, msm).collect();
|
||||
let maybe_uid = state.ecs().uid_from_entity(entity);
|
||||
let mut items: Vec<_> =
|
||||
flatten_counted_items(&items, &ability_map, &msm).collect();
|
||||
let maybe_uid = uids.get(ev.entity).copied();
|
||||
|
||||
if let Some(mut skillset) = state
|
||||
.ecs()
|
||||
.write_storage::<comp::SkillSet>()
|
||||
.get_mut(entity)
|
||||
{
|
||||
if let Some(mut skillset) = skill_sets.get_mut(ev.entity) {
|
||||
if let (Some(tool), Some(uid), exp_reward @ 1..) = (
|
||||
tool,
|
||||
ev.tool,
|
||||
maybe_uid,
|
||||
items
|
||||
.iter()
|
||||
@ -337,26 +240,23 @@ pub fn handle_mine_block(
|
||||
})
|
||||
.sum(),
|
||||
) {
|
||||
let skill_group = SkillGroupKind::Weapon(tool);
|
||||
let outcome_bus = state.ecs().read_resource::<EventBus<Outcome>>();
|
||||
let skill_group = comp::SkillGroupKind::Weapon(tool);
|
||||
if let Some(level_outcome) =
|
||||
skillset.add_experience(skill_group, exp_reward)
|
||||
{
|
||||
outcome_bus.emit_now(Outcome::SkillPointGain {
|
||||
outcome_emitter.emit(Outcome::SkillPointGain {
|
||||
uid,
|
||||
skill_tree: skill_group,
|
||||
total_points: level_outcome,
|
||||
});
|
||||
}
|
||||
outcome_bus.emit_now(Outcome::ExpChange {
|
||||
outcome_emitter.emit(Outcome::ExpChange {
|
||||
uid,
|
||||
exp: exp_reward,
|
||||
xp_pools: HashSet::from_iter(vec![skill_group]),
|
||||
});
|
||||
}
|
||||
use common::comp::skills::{MiningSkill, Skill, SKILL_MODIFIERS};
|
||||
use rand::Rng;
|
||||
let mut rng = rand::thread_rng();
|
||||
|
||||
let need_double_ore = |rng: &mut rand::rngs::ThreadRng| {
|
||||
let chance_mod = f64::from(SKILL_MODIFIERS.mining_tree.ore_gain);
|
||||
@ -378,7 +278,8 @@ pub fn handle_mine_block(
|
||||
let double_gain =
|
||||
item.item_definition_id().itemdef_id().map_or(false, |id| {
|
||||
(id.contains("mineral.ore.") && need_double_ore(&mut rng))
|
||||
|| (id.contains("mineral.gem.") && need_double_gem(&mut rng))
|
||||
|| (id.contains("mineral.gem.")
|
||||
&& need_double_gem(&mut rng))
|
||||
});
|
||||
|
||||
if double_gain {
|
||||
@ -390,36 +291,44 @@ pub fn handle_mine_block(
|
||||
for item in items {
|
||||
let loot_owner = maybe_uid
|
||||
.map(LootOwnerKind::Player)
|
||||
.map(|owner| LootOwner::new(owner, false));
|
||||
state.create_item_drop(
|
||||
Pos(pos.map(|e| e as f32) + Vec3::new(0.5, 0.5, 0.0)),
|
||||
comp::Vel(Vec3::zero()),
|
||||
.map(|owner| comp::LootOwner::new(owner, false));
|
||||
create_item_drop_emitter.emit(CreateItemDropEvent {
|
||||
pos: comp::Pos(ev.pos.map(|e| e as f32) + Vec3::new(0.5, 0.5, 0.0)),
|
||||
vel: comp::Vel(Vec3::zero()),
|
||||
ori: comp::Ori::from(Dir::random_2d(&mut rng)),
|
||||
item,
|
||||
loot_owner,
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
state.set_block(pos, block.into_vacant());
|
||||
state
|
||||
.ecs()
|
||||
.read_resource::<EventBus<Outcome>>()
|
||||
.emit_now(Outcome::BreakBlock {
|
||||
pos,
|
||||
block_change.set(ev.pos, block.into_vacant());
|
||||
outcome_emitter.emit(Outcome::BreakBlock {
|
||||
pos: ev.pos,
|
||||
color: block.get_color(),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_sound(server: &mut Server, sound: &Sound) {
|
||||
let ecs = &server.state.ecs();
|
||||
let positions = &ecs.read_storage::<Pos>();
|
||||
let agents = &mut ecs.write_storage::<comp::Agent>();
|
||||
impl ServerEvent for SoundEvent {
|
||||
type SystemData<'a> = (
|
||||
ReadExpect<'a, EventBus<Outcome>>,
|
||||
WriteStorage<'a, comp::Agent>,
|
||||
ReadStorage<'a, comp::Pos>,
|
||||
);
|
||||
|
||||
fn handle(
|
||||
events: impl ExactSizeIterator<Item = Self>,
|
||||
(outcomes, mut agents, positions): Self::SystemData<'_>,
|
||||
) {
|
||||
let mut outcome_emitter = outcomes.emitter();
|
||||
for SoundEvent { sound } in events {
|
||||
// TODO: Reduce the complexity of this problem by using spatial partitioning
|
||||
// system
|
||||
for (agent, agent_pos) in (agents, positions).join() {
|
||||
for (agent, agent_pos) in (&mut agents, &positions).join() {
|
||||
// TODO: Use pathfinding for more dropoff around obstacles
|
||||
let agent_dist_sqrd = agent_pos.0.distance_squared(sound.pos);
|
||||
let sound_travel_dist_sqrd = (sound.vol * SOUND_TRAVEL_DIST_PER_VOLUME).powi(2);
|
||||
@ -446,66 +355,80 @@ pub fn handle_sound(server: &mut Server, sound: &Sound) {
|
||||
}),
|
||||
_ => None,
|
||||
} {
|
||||
ecs.read_resource::<EventBus<Outcome>>().emit_now(outcome);
|
||||
outcome_emitter.emit(outcome);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_create_sprite(
|
||||
server: &mut Server,
|
||||
pos: Vec3<i32>,
|
||||
sprite: SpriteKind,
|
||||
del_timeout: Option<(f32, f32)>,
|
||||
) {
|
||||
let state = server.state_mut();
|
||||
if state.can_set_block(pos) {
|
||||
let block = state.terrain().get(pos).ok().copied();
|
||||
impl ServerEvent for CreateSpriteEvent {
|
||||
type SystemData<'a> = (
|
||||
WriteExpect<'a, BlockChange>,
|
||||
WriteExpect<'a, ScheduledBlockChange>,
|
||||
ReadExpect<'a, TerrainGrid>,
|
||||
ReadExpect<'a, Time>,
|
||||
);
|
||||
|
||||
fn handle(
|
||||
events: impl ExactSizeIterator<Item = Self>,
|
||||
(mut block_change, mut scheduled_block_change, terrain, time): Self::SystemData<'_>,
|
||||
) {
|
||||
for ev in events {
|
||||
if block_change.can_set_block(ev.pos) {
|
||||
let block = terrain.get(ev.pos).ok().copied();
|
||||
if block.map_or(false, |b| (*b).is_fluid()) {
|
||||
let old_block = block.unwrap_or_else(|| Block::air(SpriteKind::Empty));
|
||||
let new_block = old_block.with_sprite(sprite);
|
||||
state.set_block(pos, new_block);
|
||||
let new_block = old_block.with_sprite(ev.sprite);
|
||||
block_change.set(ev.pos, new_block);
|
||||
// Remove sprite after del_timeout and offset if specified
|
||||
if let Some((timeout, del_offset)) = del_timeout {
|
||||
if let Some((timeout, del_offset)) = ev.del_timeout {
|
||||
use rand::Rng;
|
||||
let mut rng = rand::thread_rng();
|
||||
let offset = rng.gen_range(0.0..del_offset);
|
||||
let current_time: f64 = state.ecs().read_resource::<Time>().0;
|
||||
let current_time: f64 = time.0;
|
||||
let replace_time = current_time + (timeout + offset) as f64;
|
||||
if old_block != new_block {
|
||||
server
|
||||
.state
|
||||
.schedule_set_block(pos, old_block, new_block, replace_time)
|
||||
scheduled_block_change.set(ev.pos, old_block, replace_time);
|
||||
scheduled_block_change.outcome_set(ev.pos, new_block, replace_time);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_tame_pet(server: &mut Server, pet_entity: EcsEntity, owner_entity: EcsEntity) {
|
||||
// TODO: Raise outcome to send to clients to play sound/render an indicator
|
||||
// showing taming success?
|
||||
tame_pet(server.state.ecs(), pet_entity, owner_entity);
|
||||
}
|
||||
impl ServerEvent for ToggleSpriteLightEvent {
|
||||
type SystemData<'a> = (
|
||||
WriteExpect<'a, BlockChange>,
|
||||
ReadExpect<'a, TerrainGrid>,
|
||||
ReadStorage<'a, comp::Pos>,
|
||||
);
|
||||
|
||||
pub fn handle_toggle_sprite_light(
|
||||
server: &mut Server,
|
||||
entity: EcsEntity,
|
||||
pos: Vec3<i32>,
|
||||
enable: bool,
|
||||
) {
|
||||
let state = server.state_mut();
|
||||
// TODO: Implement toggling lights on volume entities
|
||||
if let Some(entity_pos) = state.ecs().read_storage::<Pos>().get(entity)
|
||||
&& entity_pos.0.distance_squared(pos.as_()) < MAX_INTERACT_RANGE.powi(2)
|
||||
&& state.can_set_block(pos)
|
||||
fn handle(
|
||||
events: impl ExactSizeIterator<Item = Self>,
|
||||
(mut block_change, terrain, positions): Self::SystemData<'_>,
|
||||
) {
|
||||
for ev in events.into_iter() {
|
||||
if let Some(entity_pos) = positions.get(ev.entity)
|
||||
&& entity_pos.0.distance_squared(ev.pos.as_()) < MAX_INTERACT_RANGE.powi(2)
|
||||
&& block_change.can_set_block(ev.pos)
|
||||
{
|
||||
if let Some(new_block) = state
|
||||
.terrain()
|
||||
.get(pos)
|
||||
if let Some(new_block) = terrain
|
||||
.get(ev.pos)
|
||||
.ok()
|
||||
.and_then(|block| block.with_toggle_light(enable))
|
||||
.and_then(|block| block.with_toggle_light(ev.enable))
|
||||
{
|
||||
state.set_block(pos, new_block);
|
||||
block_change.set(ev.pos, new_block);
|
||||
// TODO: Emit outcome
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_tame_pet(server: &mut Server, ev: TamePetEvent) {
|
||||
// TODO: Raise outcome to send to clients to play sound/render an indicator
|
||||
// showing taming success?
|
||||
tame_pet(server.state.ecs(), ev.pet_entity, ev.owner_entity);
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,23 +1,27 @@
|
||||
use super::group_manip::{self, update_map_markers};
|
||||
use crate::{client::Client, Server};
|
||||
use super::{
|
||||
event_dispatch,
|
||||
group_manip::{self, update_map_markers},
|
||||
ServerEvent,
|
||||
};
|
||||
use crate::{client::Client, Settings};
|
||||
use common::{
|
||||
comp::{
|
||||
self,
|
||||
agent::{Agent, AgentEvent},
|
||||
group::GroupManager,
|
||||
invite::{Invite, InviteKind, InviteResponse, PendingInvites},
|
||||
ChatType, Pos,
|
||||
ChatType, Group, Pos,
|
||||
},
|
||||
consts::MAX_TRADE_RANGE,
|
||||
event::{InitiateInviteEvent, InviteResponseEvent},
|
||||
trade::{TradeResult, Trades},
|
||||
uid::Uid,
|
||||
uid::{IdMaps, Uid},
|
||||
};
|
||||
use common_net::{
|
||||
msg::{InviteAnswer, ServerGeneral},
|
||||
sync::WorldSyncExt,
|
||||
use common_net::msg::{InviteAnswer, ServerGeneral};
|
||||
use specs::{
|
||||
shred, DispatcherBuilder, Entities, Entity, Read, ReadExpect, ReadStorage, SystemData, Write,
|
||||
WriteStorage,
|
||||
};
|
||||
use common_state::State;
|
||||
use specs::{world::WorldExt, Entity};
|
||||
use std::time::{Duration, Instant};
|
||||
use tracing::{error, warn};
|
||||
|
||||
@ -26,11 +30,45 @@ const INVITE_TIMEOUT_DUR: Duration = Duration::from_secs(31);
|
||||
/// Reduced duration shown to the client to help alleviate latency issues
|
||||
const PRESENTED_INVITE_TIMEOUT_DUR: Duration = Duration::from_secs(30);
|
||||
|
||||
pub fn handle_invite(server: &mut Server, inviter: Entity, invitee_uid: Uid, kind: InviteKind) {
|
||||
let max_group_size = server.settings().max_player_group_size;
|
||||
let state = server.state_mut();
|
||||
let clients = state.ecs().read_storage::<Client>();
|
||||
let invitee = match state.ecs().entity_from_uid(invitee_uid) {
|
||||
pub(super) fn register_event_systems(builder: &mut DispatcherBuilder) {
|
||||
event_dispatch::<InitiateInviteEvent>(builder);
|
||||
event_dispatch::<InviteResponseEvent>(builder);
|
||||
}
|
||||
|
||||
impl ServerEvent for InitiateInviteEvent {
|
||||
type SystemData<'a> = (
|
||||
Write<'a, Trades>,
|
||||
Read<'a, Settings>,
|
||||
Read<'a, IdMaps>,
|
||||
Read<'a, GroupManager>,
|
||||
WriteStorage<'a, PendingInvites>,
|
||||
WriteStorage<'a, Agent>,
|
||||
WriteStorage<'a, Invite>,
|
||||
ReadStorage<'a, Uid>,
|
||||
ReadStorage<'a, Client>,
|
||||
ReadStorage<'a, Pos>,
|
||||
ReadStorage<'a, Group>,
|
||||
);
|
||||
|
||||
fn handle(
|
||||
events: impl ExactSizeIterator<Item = Self>,
|
||||
(
|
||||
mut trades,
|
||||
settings,
|
||||
id_maps,
|
||||
group_manager,
|
||||
mut pending_invites,
|
||||
mut agents,
|
||||
mut invites,
|
||||
uids,
|
||||
clients,
|
||||
positions,
|
||||
groups,
|
||||
): Self::SystemData<'_>,
|
||||
) {
|
||||
for InitiateInviteEvent(inviter, invitee_uid, kind) in events {
|
||||
let max_group_size = settings.max_player_group_size;
|
||||
let invitee = match id_maps.uid_entity(invitee_uid) {
|
||||
Some(t) => t,
|
||||
None => {
|
||||
// Inform of failure
|
||||
@ -44,8 +82,6 @@ pub fn handle_invite(server: &mut Server, inviter: Entity, invitee_uid: Uid, kin
|
||||
},
|
||||
};
|
||||
|
||||
let uids = state.ecs().read_storage::<Uid>();
|
||||
|
||||
// Check if entity is trying to invite themselves
|
||||
if uids
|
||||
.get(inviter)
|
||||
@ -55,13 +91,8 @@ pub fn handle_invite(server: &mut Server, inviter: Entity, invitee_uid: Uid, kin
|
||||
return;
|
||||
}
|
||||
|
||||
let mut pending_invites = state.ecs().write_storage::<PendingInvites>();
|
||||
let mut agents = state.ecs().write_storage::<Agent>();
|
||||
let mut invites = state.ecs().write_storage::<Invite>();
|
||||
|
||||
if let InviteKind::Trade = kind {
|
||||
if matches!(kind, InviteKind::Trade) {
|
||||
// Check whether the inviter is in range of the invitee
|
||||
let positions = state.ecs().read_storage::<Pos>();
|
||||
if !within_trading_range(positions.get(inviter), positions.get(invitee)) {
|
||||
return;
|
||||
}
|
||||
@ -69,8 +100,9 @@ pub fn handle_invite(server: &mut Server, inviter: Entity, invitee_uid: Uid, kin
|
||||
|
||||
if let InviteKind::Group = kind {
|
||||
if !group_manip::can_invite(
|
||||
state,
|
||||
&clients,
|
||||
&groups,
|
||||
&group_manager,
|
||||
&mut pending_invites,
|
||||
max_group_size,
|
||||
inviter,
|
||||
@ -80,21 +112,21 @@ pub fn handle_invite(server: &mut Server, inviter: Entity, invitee_uid: Uid, kin
|
||||
}
|
||||
} else {
|
||||
// cancel current trades for inviter before inviting someone else to trade
|
||||
let mut trades = state.ecs().write_resource::<Trades>();
|
||||
if let Some(inviter_uid) = uids.get(inviter).copied() {
|
||||
if let Some(active_trade) = trades.entity_trades.get(&inviter_uid).copied() {
|
||||
trades
|
||||
.decline_trade(active_trade, inviter_uid)
|
||||
.and_then(|u| state.ecs().entity_from_uid(u))
|
||||
.and_then(|u| id_maps.uid_entity(u))
|
||||
.map(|e| {
|
||||
if let Some(client) = clients.get(e) {
|
||||
client
|
||||
.send_fallible(ServerGeneral::FinishedTrade(TradeResult::Declined));
|
||||
client.send_fallible(ServerGeneral::FinishedTrade(
|
||||
TradeResult::Declined,
|
||||
));
|
||||
}
|
||||
if let Some(agent) = agents.get_mut(e) {
|
||||
agent
|
||||
.inbox
|
||||
.push_back(AgentEvent::FinishedTrade(TradeResult::Declined));
|
||||
agent.inbox.push_back(AgentEvent::FinishedTrade(
|
||||
TradeResult::Declined,
|
||||
));
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -146,7 +178,9 @@ pub fn handle_invite(server: &mut Server, inviter: Entity, invitee_uid: Uid, kin
|
||||
};
|
||||
|
||||
// If client comp
|
||||
if let (Some(client), Some(inviter)) = (clients.get(invitee), uids.get(inviter).copied()) {
|
||||
if let (Some(client), Some(inviter)) =
|
||||
(clients.get(invitee), uids.get(inviter).copied())
|
||||
{
|
||||
if send_invite() {
|
||||
client.send_fallible(ServerGeneral::Invite {
|
||||
inviter,
|
||||
@ -174,58 +208,88 @@ pub fn handle_invite(server: &mut Server, inviter: Entity, invitee_uid: Uid, kin
|
||||
client.send_fallible(ServerGeneral::InvitePending(invitee_uid));
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn handle_invite_response(server: &mut Server, entity: Entity, response: InviteResponse) {
|
||||
match response {
|
||||
InviteResponse::Accept => handle_invite_accept(server, entity),
|
||||
InviteResponse::Decline => handle_invite_decline(server, entity),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_invite_accept(server: &mut Server, entity: Entity) {
|
||||
let index = server.index.clone();
|
||||
let state = server.state_mut();
|
||||
if let Some((inviter, kind)) = get_inviter_and_kind(entity, state) {
|
||||
handle_invite_answer(state, inviter, entity, InviteAnswer::Accepted, kind);
|
||||
let clients = state.ecs().read_storage::<Client>();
|
||||
let uids = state.ecs().read_storage::<Uid>();
|
||||
let mut agents = state.ecs().write_storage::<Agent>();
|
||||
#[derive(SystemData)]
|
||||
pub struct InviteResponseData<'a> {
|
||||
entities: Entities<'a>,
|
||||
group_manager: Write<'a, GroupManager>,
|
||||
trades: Write<'a, Trades>,
|
||||
index: ReadExpect<'a, world::IndexOwned>,
|
||||
id_maps: Read<'a, IdMaps>,
|
||||
invites: WriteStorage<'a, Invite>,
|
||||
pending_invites: WriteStorage<'a, PendingInvites>,
|
||||
groups: WriteStorage<'a, Group>,
|
||||
agents: WriteStorage<'a, comp::Agent>,
|
||||
uids: ReadStorage<'a, Uid>,
|
||||
clients: ReadStorage<'a, Client>,
|
||||
alignments: ReadStorage<'a, comp::Alignment>,
|
||||
map_markers: ReadStorage<'a, comp::MapMarker>,
|
||||
}
|
||||
|
||||
impl ServerEvent for InviteResponseEvent {
|
||||
type SystemData<'a> = InviteResponseData<'a>;
|
||||
|
||||
fn handle(events: impl ExactSizeIterator<Item = Self>, mut data: Self::SystemData<'_>) {
|
||||
for InviteResponseEvent(entity, response) in events {
|
||||
match response {
|
||||
InviteResponse::Accept => handle_invite_accept(&mut data, entity),
|
||||
InviteResponse::Decline => handle_invite_decline(&mut data, entity),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_invite_accept(data: &mut InviteResponseData, entity: Entity) {
|
||||
if let Some((inviter, kind)) = get_inviter_and_kind(entity, data) {
|
||||
handle_invite_answer(data, inviter, entity, InviteAnswer::Accepted, kind);
|
||||
|
||||
match kind {
|
||||
InviteKind::Group => {
|
||||
let map_markers = state.ecs().read_storage::<comp::MapMarker>();
|
||||
let mut group_manager = state.ecs().write_resource::<GroupManager>();
|
||||
group_manager.add_group_member(
|
||||
data.group_manager.add_group_member(
|
||||
inviter,
|
||||
entity,
|
||||
&state.ecs().entities(),
|
||||
&mut state.ecs().write_storage(),
|
||||
&state.ecs().read_storage(),
|
||||
&uids,
|
||||
&data.entities,
|
||||
&mut data.groups,
|
||||
&data.alignments,
|
||||
&data.uids,
|
||||
|entity, group_change| {
|
||||
clients
|
||||
data.clients
|
||||
.get(entity)
|
||||
.and_then(|c| {
|
||||
group_change
|
||||
.try_map_ref(|e| uids.get(*e).copied())
|
||||
.try_map_ref(|e| data.uids.get(*e).copied())
|
||||
.map(|g| (g, c))
|
||||
})
|
||||
.map(|(g, c)| {
|
||||
update_map_markers(&map_markers, &uids, c, &group_change);
|
||||
update_map_markers(&data.map_markers, &data.uids, c, &group_change);
|
||||
c.send_fallible(ServerGeneral::GroupUpdate(g));
|
||||
});
|
||||
},
|
||||
);
|
||||
},
|
||||
InviteKind::Trade => {
|
||||
if let (Some(inviter_uid), Some(invitee_uid)) =
|
||||
(uids.get(inviter).copied(), uids.get(entity).copied())
|
||||
{
|
||||
let mut trades = state.ecs().write_resource::<Trades>();
|
||||
if let (Some(inviter_uid), Some(invitee_uid)) = (
|
||||
data.uids.get(inviter).copied(),
|
||||
data.uids.get(entity).copied(),
|
||||
) {
|
||||
// check if the person that invited me has started a new trade since the
|
||||
// invitation was sent
|
||||
if trades.entity_trades.get(&inviter_uid).copied().is_some() {
|
||||
for client in clients.get(entity).into_iter().chain(clients.get(inviter)) {
|
||||
if data
|
||||
.trades
|
||||
.entity_trades
|
||||
.get(&inviter_uid)
|
||||
.copied()
|
||||
.is_some()
|
||||
{
|
||||
for client in data
|
||||
.clients
|
||||
.get(entity)
|
||||
.into_iter()
|
||||
.chain(data.clients.get(inviter))
|
||||
{
|
||||
client.send_fallible(ServerGeneral::server_msg(
|
||||
ChatType::Meta,
|
||||
"Trade failed, inviter initiated new trade since sending trade \
|
||||
@ -234,39 +298,40 @@ pub fn handle_invite_accept(server: &mut Server, entity: Entity) {
|
||||
}
|
||||
return;
|
||||
}
|
||||
let id = trades.begin_trade(inviter_uid, invitee_uid);
|
||||
let trade = trades.trades[&id].clone();
|
||||
if let Some(agent) = agents.get_mut(inviter) {
|
||||
let id = data.trades.begin_trade(inviter_uid, invitee_uid);
|
||||
let trade = data.trades.trades[&id].clone();
|
||||
if let Some(agent) = data.agents.get_mut(inviter) {
|
||||
agent
|
||||
.inbox
|
||||
.push_back(AgentEvent::TradeAccepted(invitee_uid));
|
||||
}
|
||||
#[cfg(feature = "worldgen")]
|
||||
let pricing = agents
|
||||
let pricing = data
|
||||
.agents
|
||||
.get(inviter)
|
||||
.and_then(|a| {
|
||||
a.behavior
|
||||
.trade_site()
|
||||
.and_then(|id| index.get_site_prices(id))
|
||||
.and_then(|id| data.index.get_site_prices(id))
|
||||
})
|
||||
.or_else(|| {
|
||||
agents.get(entity).and_then(|a| {
|
||||
data.agents.get(entity).and_then(|a| {
|
||||
a.behavior
|
||||
.trade_site()
|
||||
.and_then(|id| index.get_site_prices(id))
|
||||
.and_then(|id| data.index.get_site_prices(id))
|
||||
})
|
||||
});
|
||||
#[cfg(not(feature = "worldgen"))]
|
||||
let pricing = None;
|
||||
|
||||
clients.get(inviter).map(|c| {
|
||||
data.clients.get(inviter).map(|c| {
|
||||
c.send(ServerGeneral::UpdatePendingTrade(
|
||||
id,
|
||||
trade.clone(),
|
||||
pricing.clone(),
|
||||
))
|
||||
});
|
||||
clients
|
||||
data.clients
|
||||
.get(entity)
|
||||
.map(|c| c.send(ServerGeneral::UpdatePendingTrade(id, trade, pricing)));
|
||||
}
|
||||
@ -275,18 +340,19 @@ pub fn handle_invite_accept(server: &mut Server, entity: Entity) {
|
||||
}
|
||||
}
|
||||
|
||||
fn get_inviter_and_kind(entity: Entity, state: &mut State) -> Option<(Entity, InviteKind)> {
|
||||
let mut invites = state.ecs().write_storage::<Invite>();
|
||||
invites.remove(entity).and_then(|invite| {
|
||||
fn get_inviter_and_kind(
|
||||
entity: Entity,
|
||||
data: &mut InviteResponseData,
|
||||
) -> Option<(Entity, InviteKind)> {
|
||||
data.invites.remove(entity).and_then(|invite| {
|
||||
let Invite { inviter, kind } = invite;
|
||||
let mut pending_invites = state.ecs().write_storage::<PendingInvites>();
|
||||
let pending = &mut pending_invites.get_mut(inviter)?.0;
|
||||
let pending = &mut data.pending_invites.get_mut(inviter)?.0;
|
||||
// Check that inviter has a pending invite and remove it from the list
|
||||
let invite_index = pending.iter().position(|p| p.0 == entity)?;
|
||||
pending.swap_remove(invite_index);
|
||||
// If no pending invites remain remove the component
|
||||
if pending.is_empty() {
|
||||
pending_invites.remove(inviter);
|
||||
data.pending_invites.remove(inviter);
|
||||
}
|
||||
|
||||
Some((inviter, kind))
|
||||
@ -294,28 +360,25 @@ fn get_inviter_and_kind(entity: Entity, state: &mut State) -> Option<(Entity, In
|
||||
}
|
||||
|
||||
fn handle_invite_answer(
|
||||
state: &mut State,
|
||||
data: &mut InviteResponseData,
|
||||
inviter: Entity,
|
||||
entity: Entity,
|
||||
invite_answer: InviteAnswer,
|
||||
kind: InviteKind,
|
||||
) {
|
||||
let clients = state.ecs().read_storage::<Client>();
|
||||
let uids = state.ecs().read_storage::<Uid>();
|
||||
if matches!(kind, InviteKind::Trade) && matches!(invite_answer, InviteAnswer::Accepted) {
|
||||
// invitee must close current trade if one exists before accepting new one
|
||||
if let Some(invitee_uid) = uids.get(entity).copied() {
|
||||
let mut trades = state.ecs().write_resource::<Trades>();
|
||||
if let Some(active_trade) = trades.entity_trades.get(&invitee_uid).copied() {
|
||||
trades
|
||||
if let Some(invitee_uid) = data.uids.get(entity).copied() {
|
||||
if let Some(active_trade) = data.trades.entity_trades.get(&invitee_uid).copied() {
|
||||
data.trades
|
||||
.decline_trade(active_trade, invitee_uid)
|
||||
.and_then(|u| state.ecs().entity_from_uid(u))
|
||||
.and_then(|u| data.id_maps.uid_entity(u))
|
||||
.map(|e| {
|
||||
if let Some(client) = clients.get(e) {
|
||||
if let Some(client) = data.clients.get(e) {
|
||||
client
|
||||
.send_fallible(ServerGeneral::FinishedTrade(TradeResult::Declined));
|
||||
}
|
||||
if let Some(agent) = state.ecs().write_storage::<Agent>().get_mut(e) {
|
||||
if let Some(agent) = data.agents.get_mut(e) {
|
||||
agent
|
||||
.inbox
|
||||
.push_back(AgentEvent::FinishedTrade(TradeResult::Declined));
|
||||
@ -324,7 +387,9 @@ fn handle_invite_answer(
|
||||
}
|
||||
};
|
||||
}
|
||||
if let (Some(client), Some(target)) = (clients.get(inviter), uids.get(entity).copied()) {
|
||||
if let (Some(client), Some(target)) =
|
||||
(data.clients.get(inviter), data.uids.get(entity).copied())
|
||||
{
|
||||
client.send_fallible(ServerGeneral::InviteComplete {
|
||||
target,
|
||||
answer: invite_answer,
|
||||
@ -333,11 +398,10 @@ fn handle_invite_answer(
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_invite_decline(server: &mut Server, entity: Entity) {
|
||||
let state = server.state_mut();
|
||||
if let Some((inviter, kind)) = get_inviter_and_kind(entity, state) {
|
||||
pub fn handle_invite_decline(data: &mut InviteResponseData, entity: Entity) {
|
||||
if let Some((inviter, kind)) = get_inviter_and_kind(entity, data) {
|
||||
// Inform inviter of rejection
|
||||
handle_invite_answer(state, inviter, entity, InviteAnswer::Declined, kind)
|
||||
handle_invite_answer(data, inviter, entity, InviteAnswer::Declined, kind)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,43 +1,34 @@
|
||||
use crate::{
|
||||
events::{
|
||||
entity_creation::handle_create_teleporter,
|
||||
entity_manipulation::{handle_start_teleporting, handle_teleport_to_position},
|
||||
interaction::{handle_mount_volume, handle_tame_pet},
|
||||
},
|
||||
persistence::PersistedComponents,
|
||||
state_ext::StateExt,
|
||||
Server,
|
||||
};
|
||||
use common::event::{EventBus, ServerEvent, ServerEventDiscriminants};
|
||||
use common_base::span;
|
||||
use entity_creation::{
|
||||
handle_create_npc, handle_create_ship, handle_create_waypoint, handle_initialize_character,
|
||||
handle_initialize_spectator, handle_loaded_character_data, handle_shockwave, handle_shoot,
|
||||
};
|
||||
use entity_manipulation::{
|
||||
handle_aura, handle_bonk, handle_buff, handle_change_ability, handle_change_body,
|
||||
handle_combo_change, handle_delete, handle_destroy, handle_energy_change,
|
||||
handle_entity_attacked_hook, handle_explosion, handle_health_change, handle_knockback,
|
||||
handle_land_on_ground, handle_make_admin, handle_parry_hook, handle_poise,
|
||||
handle_remove_light_emitter, handle_respawn, handle_stance_change, handle_teleport_to,
|
||||
handle_update_map_marker,
|
||||
};
|
||||
use group_manip::handle_group;
|
||||
use information::handle_site_info;
|
||||
use interaction::{
|
||||
handle_create_sprite, handle_lantern, handle_mine_block, handle_mount, handle_npc_interaction,
|
||||
handle_set_pet_stay, handle_sound, handle_toggle_sprite_light, handle_unmount,
|
||||
};
|
||||
use inventory_manip::handle_inventory;
|
||||
use invite::{handle_invite, handle_invite_response};
|
||||
use player::{handle_client_disconnect, handle_exit_ingame, handle_possess};
|
||||
use specs::{Builder, Entity as EcsEntity, WorldExt};
|
||||
use trade::handle_process_trade_action;
|
||||
use std::{marker::PhantomData, sync::Arc};
|
||||
|
||||
use crate::{state_ext::StateExt, Server};
|
||||
use common::event::{
|
||||
ChatEvent, ClientDisconnectEvent, ClientDisconnectWithoutPersistenceEvent, CommandEvent,
|
||||
EventBus, ExitIngameEvent,
|
||||
};
|
||||
use common_base::span;
|
||||
use specs::{
|
||||
shred::SendDispatcher, DispatcherBuilder, Entity as EcsEntity, ReadExpect, WorldExt,
|
||||
WriteExpect,
|
||||
};
|
||||
|
||||
use crate::events::player::handle_character_delete;
|
||||
pub use group_manip::update_map_markers;
|
||||
pub(crate) use trade::cancel_trades_for;
|
||||
|
||||
use self::{
|
||||
entity_creation::{
|
||||
handle_create_item_drop, handle_create_npc, handle_create_object, handle_create_ship,
|
||||
handle_create_teleporter, handle_create_waypoint, handle_initialize_character,
|
||||
handle_initialize_spectator, handle_loaded_character_data, handle_shockwave, handle_shoot,
|
||||
},
|
||||
entity_manipulation::handle_delete,
|
||||
interaction::handle_tame_pet,
|
||||
mounting::{handle_mount, handle_mount_volume, handle_unmount},
|
||||
player::{
|
||||
handle_character_delete, handle_client_disconnect, handle_exit_ingame, handle_possess,
|
||||
},
|
||||
trade::handle_process_trade_action,
|
||||
};
|
||||
|
||||
mod entity_creation;
|
||||
mod entity_manipulation;
|
||||
mod group_manip;
|
||||
@ -45,9 +36,65 @@ mod information;
|
||||
mod interaction;
|
||||
mod inventory_manip;
|
||||
mod invite;
|
||||
mod mounting;
|
||||
mod player;
|
||||
mod trade;
|
||||
|
||||
pub trait ServerEvent: Send + Sync + 'static {
|
||||
type SystemData<'a>: specs::SystemData<'a>;
|
||||
|
||||
const NAME: &'static str = std::any::type_name::<Self>();
|
||||
|
||||
fn handle(events: impl ExactSizeIterator<Item = Self>, data: Self::SystemData<'_>);
|
||||
}
|
||||
|
||||
struct EventHandler<T>(PhantomData<T>);
|
||||
impl<T> Default for EventHandler<T> {
|
||||
fn default() -> Self { Self(PhantomData) }
|
||||
}
|
||||
|
||||
impl<'a, T: ServerEvent> common_ecs::System<'a> for EventHandler<T> {
|
||||
type SystemData = (
|
||||
ReadExpect<'a, crate::metrics::ServerEventMetrics>,
|
||||
WriteExpect<'a, EventBus<T>>,
|
||||
T::SystemData<'a>,
|
||||
);
|
||||
|
||||
const NAME: &'static str = T::NAME;
|
||||
const ORIGIN: common_ecs::Origin = common_ecs::Origin::Server;
|
||||
const PHASE: common_ecs::Phase = common_ecs::Phase::Apply;
|
||||
|
||||
fn run(_job: &mut common_ecs::Job<Self>, (metrics, mut ev, data): Self::SystemData) {
|
||||
span!(guard, "Recv events");
|
||||
let events = ev.recv_all_mut();
|
||||
drop(guard);
|
||||
|
||||
span!(guard, "Add metrics for event");
|
||||
metrics
|
||||
.event_count
|
||||
.with_label_values(&[T::NAME])
|
||||
.inc_by(events.len() as u64);
|
||||
drop(guard);
|
||||
|
||||
span!(guard, "Handle events");
|
||||
T::handle(events, data);
|
||||
drop(guard);
|
||||
}
|
||||
}
|
||||
|
||||
fn event_dispatch<T: ServerEvent>(builder: &mut DispatcherBuilder) {
|
||||
// TODO: We currently don't consider the order of these event. But as
|
||||
// some events produce other events that might be worth doing.
|
||||
common_ecs::dispatch::<EventHandler<T>>(builder, &[]);
|
||||
}
|
||||
|
||||
pub fn register_event_systems(builder: &mut DispatcherBuilder) {
|
||||
inventory_manip::register_event_systems(builder);
|
||||
entity_manipulation::register_event_systems(builder);
|
||||
interaction::register_event_systems(builder);
|
||||
invite::register_event_systems(builder);
|
||||
}
|
||||
|
||||
pub enum Event {
|
||||
ClientConnected {
|
||||
entity: EcsEntity,
|
||||
@ -62,281 +109,100 @@ pub enum Event {
|
||||
}
|
||||
|
||||
impl Server {
|
||||
pub fn handle_events(&mut self) -> Vec<Event> {
|
||||
span!(_guard, "handle_events", "Server::handle_events");
|
||||
let mut frontend_events = Vec::new();
|
||||
|
||||
let mut commands = Vec::new();
|
||||
let mut chat_messages = Vec::new();
|
||||
|
||||
let events = self
|
||||
.state
|
||||
.ecs()
|
||||
.read_resource::<EventBus<ServerEvent>>()
|
||||
.recv_all();
|
||||
|
||||
use strum::VariantNames;
|
||||
let mut event_counts = vec![0u32; ServerEventDiscriminants::VARIANTS.len()];
|
||||
|
||||
for event in events {
|
||||
// Count events by variant for metrics
|
||||
event_counts[ServerEventDiscriminants::from(&event) as usize] += 1;
|
||||
|
||||
match event {
|
||||
ServerEvent::Explosion {
|
||||
pos,
|
||||
explosion,
|
||||
owner,
|
||||
} => handle_explosion(self, pos, explosion, owner),
|
||||
ServerEvent::Bonk { pos, owner, target } => handle_bonk(self, pos, owner, target),
|
||||
ServerEvent::Shoot {
|
||||
entity,
|
||||
pos,
|
||||
dir,
|
||||
body,
|
||||
light,
|
||||
projectile,
|
||||
speed,
|
||||
object,
|
||||
} => handle_shoot(
|
||||
self, entity, pos, dir, body, light, projectile, speed, object,
|
||||
),
|
||||
ServerEvent::Shockwave {
|
||||
properties,
|
||||
pos,
|
||||
ori,
|
||||
} => handle_shockwave(self, properties, pos, ori),
|
||||
ServerEvent::Knockback { entity, impulse } => {
|
||||
handle_knockback(self, entity, impulse)
|
||||
},
|
||||
ServerEvent::HealthChange { entity, change } => {
|
||||
handle_health_change(self, entity, change)
|
||||
},
|
||||
ServerEvent::PoiseChange { entity, change } => handle_poise(self, entity, change),
|
||||
ServerEvent::Delete(entity) => handle_delete(self, entity),
|
||||
ServerEvent::Destroy { entity, cause } => handle_destroy(self, entity, cause),
|
||||
ServerEvent::InventoryManip(entity, manip) => handle_inventory(self, entity, manip),
|
||||
ServerEvent::GroupManip(entity, manip) => handle_group(self, entity, manip),
|
||||
ServerEvent::Respawn(entity) => handle_respawn(self, entity),
|
||||
ServerEvent::LandOnGround {
|
||||
entity,
|
||||
vel,
|
||||
surface_normal,
|
||||
} => handle_land_on_ground(self, entity, vel, surface_normal),
|
||||
ServerEvent::EnableLantern(entity) => handle_lantern(self, entity, true),
|
||||
ServerEvent::DisableLantern(entity) => handle_lantern(self, entity, false),
|
||||
ServerEvent::NpcInteract(interactor, target, subject) => {
|
||||
handle_npc_interaction(self, interactor, target, subject)
|
||||
},
|
||||
ServerEvent::InitiateInvite(interactor, target, kind) => {
|
||||
handle_invite(self, interactor, target, kind)
|
||||
},
|
||||
ServerEvent::InviteResponse(entity, response) => {
|
||||
handle_invite_response(self, entity, response)
|
||||
},
|
||||
ServerEvent::ProcessTradeAction(entity, trade_id, action) => {
|
||||
handle_process_trade_action(self, entity, trade_id, action);
|
||||
},
|
||||
ServerEvent::Mount(mounter, mountee) => handle_mount(self, mounter, mountee),
|
||||
ServerEvent::MountVolume(mounter, volume) => {
|
||||
handle_mount_volume(self, mounter, volume)
|
||||
},
|
||||
ServerEvent::Unmount(mounter) => handle_unmount(self, mounter),
|
||||
ServerEvent::SetPetStay(command_giver, pet, stay) => {
|
||||
handle_set_pet_stay(self, command_giver, pet, stay)
|
||||
},
|
||||
ServerEvent::Possess(possessor_uid, possesse_uid) => {
|
||||
handle_possess(self, possessor_uid, possesse_uid)
|
||||
},
|
||||
ServerEvent::InitCharacterData {
|
||||
entity,
|
||||
character_id,
|
||||
requested_view_distances,
|
||||
} => handle_initialize_character(
|
||||
self,
|
||||
entity,
|
||||
character_id,
|
||||
requested_view_distances,
|
||||
),
|
||||
ServerEvent::InitSpectator(entity, requested_view_distances) => {
|
||||
handle_initialize_spectator(self, entity, requested_view_distances)
|
||||
},
|
||||
ServerEvent::DeleteCharacter {
|
||||
entity,
|
||||
requesting_player_uuid,
|
||||
character_id,
|
||||
} => handle_character_delete(self, entity, requesting_player_uuid, character_id),
|
||||
ServerEvent::UpdateCharacterData {
|
||||
entity,
|
||||
components,
|
||||
metadata,
|
||||
} => {
|
||||
let (
|
||||
body,
|
||||
stats,
|
||||
skill_set,
|
||||
inventory,
|
||||
waypoint,
|
||||
pets,
|
||||
active_abilities,
|
||||
map_marker,
|
||||
) = components;
|
||||
let components = PersistedComponents {
|
||||
body,
|
||||
stats,
|
||||
skill_set,
|
||||
inventory,
|
||||
waypoint,
|
||||
pets,
|
||||
active_abilities,
|
||||
map_marker,
|
||||
};
|
||||
handle_loaded_character_data(self, entity, components, metadata);
|
||||
},
|
||||
ServerEvent::ExitIngame { entity } => {
|
||||
handle_exit_ingame(self, entity, false);
|
||||
},
|
||||
ServerEvent::CreateNpc {
|
||||
pos,
|
||||
ori,
|
||||
npc,
|
||||
rider,
|
||||
} => {
|
||||
handle_create_npc(self, pos, ori, npc, rider);
|
||||
},
|
||||
ServerEvent::CreateShip {
|
||||
pos,
|
||||
ori,
|
||||
ship,
|
||||
rtsim_entity,
|
||||
driver,
|
||||
} => handle_create_ship(self, pos, ori, ship, rtsim_entity, driver, Vec::new()),
|
||||
ServerEvent::CreateWaypoint(pos) => handle_create_waypoint(self, pos),
|
||||
ServerEvent::CreateTeleporter(pos, portal) => {
|
||||
handle_create_teleporter(self, pos, portal)
|
||||
},
|
||||
ServerEvent::ClientDisconnect(entity, reason) => {
|
||||
frontend_events.push(handle_client_disconnect(self, entity, reason, false))
|
||||
},
|
||||
ServerEvent::ClientDisconnectWithoutPersistence(entity) => {
|
||||
frontend_events.push(handle_client_disconnect(
|
||||
self,
|
||||
entity,
|
||||
common::comp::DisconnectReason::Kicked,
|
||||
true,
|
||||
))
|
||||
},
|
||||
ServerEvent::Command(entity, name, args) => {
|
||||
commands.push((entity, name, args));
|
||||
},
|
||||
ServerEvent::Chat(msg) => {
|
||||
chat_messages.push(msg);
|
||||
},
|
||||
ServerEvent::Aura {
|
||||
entity,
|
||||
aura_change,
|
||||
} => handle_aura(self, entity, aura_change),
|
||||
ServerEvent::Buff {
|
||||
entity,
|
||||
buff_change,
|
||||
} => handle_buff(self, entity, buff_change),
|
||||
ServerEvent::EnergyChange { entity, change } => {
|
||||
handle_energy_change(self, entity, change)
|
||||
},
|
||||
ServerEvent::ComboChange { entity, change } => {
|
||||
handle_combo_change(self, entity, change)
|
||||
},
|
||||
ServerEvent::ParryHook {
|
||||
defender,
|
||||
attacker,
|
||||
source,
|
||||
} => handle_parry_hook(self, defender, attacker, source),
|
||||
ServerEvent::RequestSiteInfo { entity, id } => handle_site_info(self, entity, id),
|
||||
ServerEvent::MineBlock { entity, pos, tool } => {
|
||||
handle_mine_block(self, entity, pos, tool)
|
||||
},
|
||||
ServerEvent::TeleportTo {
|
||||
entity,
|
||||
target,
|
||||
max_range,
|
||||
} => handle_teleport_to(self, entity, target, max_range),
|
||||
ServerEvent::CreateSafezone { range, pos } => {
|
||||
self.state.create_safezone(range, pos).build();
|
||||
},
|
||||
ServerEvent::Sound { sound } => handle_sound(self, &sound),
|
||||
ServerEvent::CreateSprite {
|
||||
pos,
|
||||
sprite,
|
||||
del_timeout,
|
||||
} => handle_create_sprite(self, pos, sprite, del_timeout),
|
||||
ServerEvent::TamePet {
|
||||
pet_entity,
|
||||
owner_entity,
|
||||
} => handle_tame_pet(self, pet_entity, owner_entity),
|
||||
ServerEvent::EntityAttackedHook { entity, attacker } => {
|
||||
handle_entity_attacked_hook(self, entity, attacker)
|
||||
},
|
||||
ServerEvent::ChangeAbility {
|
||||
entity,
|
||||
slot,
|
||||
auxiliary_key,
|
||||
new_ability,
|
||||
} => handle_change_ability(self, entity, slot, auxiliary_key, new_ability),
|
||||
ServerEvent::UpdateMapMarker { entity, update } => {
|
||||
handle_update_map_marker(self, entity, update)
|
||||
},
|
||||
ServerEvent::MakeAdmin {
|
||||
entity,
|
||||
admin,
|
||||
uuid,
|
||||
} => handle_make_admin(self, entity, admin, uuid),
|
||||
ServerEvent::ChangeStance { entity, stance } => {
|
||||
handle_stance_change(self, entity, stance)
|
||||
},
|
||||
ServerEvent::ChangeBody { entity, new_body } => {
|
||||
handle_change_body(self, entity, new_body)
|
||||
},
|
||||
ServerEvent::RemoveLightEmitter { entity } => {
|
||||
handle_remove_light_emitter(self, entity)
|
||||
},
|
||||
ServerEvent::TeleportToPosition { entity, position } => {
|
||||
handle_teleport_to_position(self, entity, position)
|
||||
},
|
||||
ServerEvent::StartTeleporting { entity, portal } => {
|
||||
handle_start_teleporting(self, entity, portal)
|
||||
},
|
||||
ServerEvent::ToggleSpriteLight {
|
||||
entity,
|
||||
pos,
|
||||
enable,
|
||||
} => handle_toggle_sprite_light(self, entity, pos, enable),
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
fn handle_serial_events<T: Send + 'static, F: FnMut(&mut Self, T)>(&mut self, mut f: F) {
|
||||
if let Some(bus) = self.state.ecs_mut().get_mut::<EventBus<T>>() {
|
||||
let events = bus.recv_all_mut();
|
||||
let server_event_metrics = self
|
||||
.state
|
||||
.ecs()
|
||||
.read_resource::<crate::metrics::ServerEventMetrics>();
|
||||
event_counts
|
||||
.into_iter()
|
||||
.zip(ServerEventDiscriminants::VARIANTS)
|
||||
.for_each(|(count, event_name)| {
|
||||
server_event_metrics
|
||||
.event_count
|
||||
.with_label_values(&[event_name])
|
||||
.inc_by(count.into());
|
||||
})
|
||||
.with_label_values(&[std::any::type_name::<T>()])
|
||||
.inc_by(events.len() as u64);
|
||||
drop(server_event_metrics);
|
||||
|
||||
for ev in events {
|
||||
f(self, ev)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (entity, name, args) in commands {
|
||||
self.process_command(entity, name, args);
|
||||
fn handle_all_serial_events(&mut self, frontend_events: &mut Vec<Event>) {
|
||||
self.handle_serial_events(handle_initialize_character);
|
||||
self.handle_serial_events(handle_initialize_spectator);
|
||||
self.handle_serial_events(handle_loaded_character_data);
|
||||
self.handle_serial_events(|this, ev| {
|
||||
handle_create_npc(this, ev);
|
||||
});
|
||||
self.handle_serial_events(handle_create_ship);
|
||||
self.handle_serial_events(handle_shoot);
|
||||
self.handle_serial_events(handle_shockwave);
|
||||
self.handle_serial_events(handle_create_waypoint);
|
||||
self.handle_serial_events(handle_create_teleporter);
|
||||
self.handle_serial_events(handle_create_item_drop);
|
||||
self.handle_serial_events(handle_create_object);
|
||||
self.handle_serial_events(handle_delete);
|
||||
|
||||
self.handle_serial_events(handle_character_delete);
|
||||
self.handle_serial_events(|this, ev: ExitIngameEvent| {
|
||||
handle_exit_ingame(this, ev.entity, false)
|
||||
});
|
||||
self.handle_serial_events(|this, ev: ClientDisconnectEvent| {
|
||||
handle_client_disconnect(this, ev.0, ev.1, false);
|
||||
});
|
||||
self.handle_serial_events(|this, ev: ClientDisconnectEvent| {
|
||||
frontend_events.push(handle_client_disconnect(this, ev.0, ev.1, false));
|
||||
});
|
||||
self.handle_serial_events(|this, ev: ClientDisconnectWithoutPersistenceEvent| {
|
||||
frontend_events.push(handle_client_disconnect(
|
||||
this,
|
||||
ev.0,
|
||||
common::comp::DisconnectReason::Kicked,
|
||||
true,
|
||||
));
|
||||
});
|
||||
self.handle_serial_events(handle_possess);
|
||||
self.handle_serial_events(|this, ev: CommandEvent| {
|
||||
this.process_command(ev.0, ev.1, ev.2);
|
||||
});
|
||||
self.handle_serial_events(|this, ev: ChatEvent| {
|
||||
this.state.send_chat(ev.0);
|
||||
});
|
||||
self.handle_serial_events(handle_mount);
|
||||
self.handle_serial_events(handle_mount_volume);
|
||||
self.handle_serial_events(handle_unmount);
|
||||
self.handle_serial_events(handle_tame_pet);
|
||||
self.handle_serial_events(handle_process_trade_action);
|
||||
}
|
||||
|
||||
for msg in chat_messages {
|
||||
self.state.send_chat(msg);
|
||||
}
|
||||
pub fn handle_events(&mut self) -> Vec<Event> {
|
||||
let mut frontend_events = Vec::new();
|
||||
|
||||
span!(guard, "run event systems");
|
||||
// This dispatches all the systems in parallel.
|
||||
self.event_dispatcher.dispatch(self.state.ecs());
|
||||
drop(guard);
|
||||
|
||||
span!(guard, "handle serial events");
|
||||
self.handle_all_serial_events(&mut frontend_events);
|
||||
drop(guard);
|
||||
|
||||
self.state.maintain_ecs();
|
||||
|
||||
frontend_events
|
||||
}
|
||||
|
||||
pub fn create_event_dispatcher(pools: Arc<rayon::ThreadPool>) -> SendDispatcher<'static> {
|
||||
span!(_guard, "create event dispatcher");
|
||||
// Run systems to handle events.
|
||||
// Create and run a dispatcher for ecs systems.
|
||||
let mut dispatch_builder = DispatcherBuilder::new().with_pool(pools);
|
||||
register_event_systems(&mut dispatch_builder);
|
||||
dispatch_builder
|
||||
.build()
|
||||
.try_into_sendable()
|
||||
.ok()
|
||||
.expect("This should be sendable")
|
||||
}
|
||||
}
|
||||
|
151
server/src/events/mounting.rs
Normal file
151
server/src/events/mounting.rs
Normal file
@ -0,0 +1,151 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use common::{
|
||||
comp::{self, pet::is_mountable},
|
||||
consts::{MAX_MOUNT_RANGE, MAX_SPRITE_MOUNT_RANGE},
|
||||
event::{MountEvent, MountVolumeEvent, UnmountEvent},
|
||||
link::Is,
|
||||
mounting::{Mounting, Rider, VolumeMounting, VolumeRider},
|
||||
rtsim::RtSimEntity,
|
||||
uid::IdMaps,
|
||||
};
|
||||
use plugin_api::Uid;
|
||||
use specs::WorldExt;
|
||||
|
||||
use crate::{rtsim::RtSim, state_ext::StateExt, Server};
|
||||
|
||||
pub fn within_mounting_range(
|
||||
player_position: Option<&comp::Pos>,
|
||||
mount_position: Option<&comp::Pos>,
|
||||
) -> bool {
|
||||
match (player_position, mount_position) {
|
||||
(Some(ppos), Some(ipos)) => ppos.0.distance_squared(ipos.0) < MAX_MOUNT_RANGE.powi(2),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_mount(server: &mut Server, MountEvent(rider, mount): MountEvent) {
|
||||
let state = server.state_mut();
|
||||
|
||||
let within_range = {
|
||||
let positions = state.ecs().read_storage::<comp::Pos>();
|
||||
within_mounting_range(positions.get(rider), positions.get(mount))
|
||||
};
|
||||
|
||||
if within_range {
|
||||
let uids = state.ecs().read_storage::<Uid>();
|
||||
if let (Some(rider_uid), Some(mount_uid)) =
|
||||
(uids.get(rider).copied(), uids.get(mount).copied())
|
||||
{
|
||||
let is_pet_of = |mount, rider_uid| {
|
||||
matches!(
|
||||
state
|
||||
.ecs()
|
||||
.read_storage::<comp::Alignment>()
|
||||
.get(mount),
|
||||
Some(comp::Alignment::Owned(owner)) if *owner == rider_uid,
|
||||
)
|
||||
};
|
||||
|
||||
let can_ride = state
|
||||
.ecs()
|
||||
.read_storage()
|
||||
.get(mount)
|
||||
.map_or(false, |mount_body| {
|
||||
is_mountable(mount_body, state.ecs().read_storage().get(rider))
|
||||
});
|
||||
|
||||
let is_stay = state
|
||||
.ecs()
|
||||
.read_storage::<comp::Agent>()
|
||||
.get(mount)
|
||||
.and_then(|x| x.stay_pos)
|
||||
.is_some();
|
||||
|
||||
if (is_pet_of(mount, rider_uid) || is_pet_of(rider, mount_uid)) && can_ride && !is_stay
|
||||
{
|
||||
drop(uids);
|
||||
let _ = state.link(Mounting {
|
||||
mount: mount_uid,
|
||||
rider: rider_uid,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_mount_volume(
|
||||
server: &mut Server,
|
||||
MountVolumeEvent(rider, volume_pos): MountVolumeEvent,
|
||||
) {
|
||||
let state = server.state_mut();
|
||||
|
||||
let block_transform = volume_pos.get_block_and_transform(
|
||||
&state.terrain(),
|
||||
&state.ecs().read_resource(),
|
||||
|e| {
|
||||
state
|
||||
.read_storage()
|
||||
.get(e)
|
||||
.copied()
|
||||
.zip(state.read_storage().get(e).copied())
|
||||
},
|
||||
&state.read_storage(),
|
||||
);
|
||||
|
||||
if let Some((mat, _, block)) = block_transform
|
||||
&& let Some(mount_offset) = block.mount_offset()
|
||||
{
|
||||
let mount_pos = (mat * mount_offset.0.with_w(1.0)).xyz();
|
||||
let within_range = {
|
||||
let positions = state.ecs().read_storage::<comp::Pos>();
|
||||
positions.get(rider).map_or(false, |pos| {
|
||||
pos.0.distance_squared(mount_pos) < MAX_SPRITE_MOUNT_RANGE.powi(2)
|
||||
})
|
||||
};
|
||||
|
||||
let maybe_uid = state.ecs().read_storage::<Uid>().get(rider).copied();
|
||||
|
||||
if let Some(rider) = maybe_uid
|
||||
&& within_range
|
||||
{
|
||||
let _link_successful = state
|
||||
.link(VolumeMounting {
|
||||
pos: volume_pos,
|
||||
block,
|
||||
rider,
|
||||
})
|
||||
.is_ok();
|
||||
#[cfg(feature = "worldgen")]
|
||||
if _link_successful {
|
||||
let uid_allocator = state.ecs().read_resource::<IdMaps>();
|
||||
if let Some(rider_entity) = uid_allocator.uid_entity(rider)
|
||||
&& let Some(rider_actor) = state.entity_as_actor(rider_entity)
|
||||
&& let Some(volume_pos) = volume_pos.try_map_entity(|uid| {
|
||||
let entity = uid_allocator.uid_entity(uid)?;
|
||||
state.read_storage::<RtSimEntity>().get(entity).map(|v| v.0)
|
||||
})
|
||||
{
|
||||
state
|
||||
.ecs()
|
||||
.write_resource::<RtSim>()
|
||||
.hook_character_mount_volume(
|
||||
&state.ecs().read_resource::<Arc<world::World>>(),
|
||||
state
|
||||
.ecs()
|
||||
.read_resource::<world::IndexOwned>()
|
||||
.as_index_ref(),
|
||||
volume_pos,
|
||||
rider_actor,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_unmount(server: &mut Server, UnmountEvent(rider): UnmountEvent) {
|
||||
let state = server.state_mut();
|
||||
state.ecs().write_storage::<Is<Rider>>().remove(rider);
|
||||
state.ecs().write_storage::<Is<VolumeRider>>().remove(rider);
|
||||
}
|
@ -4,9 +4,9 @@ use crate::{
|
||||
state_ext::StateExt, BattleModeBuffer, Server,
|
||||
};
|
||||
use common::{
|
||||
character::CharacterId,
|
||||
comp,
|
||||
comp::{group, pet::is_tameable, Presence, PresenceKind},
|
||||
event::{DeleteCharacterEvent, PossessEvent},
|
||||
resources::Time,
|
||||
uid::{IdMaps, Uid},
|
||||
};
|
||||
@ -16,12 +16,7 @@ use common_state::State;
|
||||
use specs::{Builder, Entity as EcsEntity, Join, WorldExt};
|
||||
use tracing::{debug, error, trace, warn, Instrument};
|
||||
|
||||
pub fn handle_character_delete(
|
||||
server: &mut Server,
|
||||
entity: EcsEntity,
|
||||
requesting_player_uuid: String,
|
||||
character_id: CharacterId,
|
||||
) {
|
||||
pub fn handle_character_delete(server: &mut Server, ev: DeleteCharacterEvent) {
|
||||
// Can't process a character delete for a player that has an in-game presence,
|
||||
// so kick them out before processing the delete.
|
||||
// NOTE: This relies on StateExt::handle_initialize_character adding the
|
||||
@ -29,19 +24,19 @@ pub fn handle_character_delete(
|
||||
// is in-game.
|
||||
let has_presence = {
|
||||
let presences = server.state.ecs().read_storage::<Presence>();
|
||||
presences.get(entity).is_some()
|
||||
presences.get(ev.entity).is_some()
|
||||
};
|
||||
if has_presence {
|
||||
warn!(
|
||||
?requesting_player_uuid,
|
||||
?character_id,
|
||||
?ev.requesting_player_uuid,
|
||||
?ev.character_id,
|
||||
"Character delete received while in-game, disconnecting client."
|
||||
);
|
||||
handle_exit_ingame(server, entity, true);
|
||||
handle_exit_ingame(server, ev.entity, true);
|
||||
}
|
||||
|
||||
let mut updater = server.state.ecs().fetch_mut::<CharacterUpdater>();
|
||||
updater.queue_character_deletion(requesting_player_uuid, character_id);
|
||||
updater.queue_character_deletion(ev.requesting_player_uuid, ev.character_id);
|
||||
}
|
||||
|
||||
pub fn handle_exit_ingame(server: &mut Server, entity: EcsEntity, skip_persistence: bool) {
|
||||
@ -341,7 +336,10 @@ fn persist_entity(state: &mut State, entity: EcsEntity) -> EcsEntity {
|
||||
/// FIXME: This code is dangerous and needs to be refactored. We can't just
|
||||
/// comment it out, but it needs to be fixed for a variety of reasons. Get rid
|
||||
/// of this ASAP!
|
||||
pub fn handle_possess(server: &mut Server, possessor_uid: Uid, possessee_uid: Uid) {
|
||||
pub fn handle_possess(
|
||||
server: &mut Server,
|
||||
PossessEvent(possessor_uid, possessee_uid): PossessEvent,
|
||||
) {
|
||||
use crate::presence::RegionSubscription;
|
||||
use common::{
|
||||
comp::{inventory::slot::EquipSlot, item, slot::Slot, Inventory},
|
||||
|
@ -7,7 +7,8 @@ use common::{
|
||||
Inventory,
|
||||
},
|
||||
},
|
||||
trade::{PendingTrade, ReducedInventory, TradeAction, TradeId, TradeResult, Trades},
|
||||
event::ProcessTradeActionEvent,
|
||||
trade::{PendingTrade, ReducedInventory, TradeAction, TradeResult, Trades},
|
||||
};
|
||||
use common_net::{
|
||||
msg::ServerGeneral,
|
||||
@ -19,8 +20,8 @@ use std::cmp::Ordering;
|
||||
use tracing::{error, trace};
|
||||
use world::IndexOwned;
|
||||
|
||||
fn notify_agent_simple(
|
||||
mut agents: specs::WriteStorage<Agent>,
|
||||
pub fn notify_agent_simple(
|
||||
agents: &mut specs::WriteStorage<Agent>,
|
||||
entity: EcsEntity,
|
||||
event: AgentEvent,
|
||||
) {
|
||||
@ -55,9 +56,7 @@ fn notify_agent_prices(
|
||||
/// Invoked when the trade UI is up, handling item changes, accepts, etc
|
||||
pub(super) fn handle_process_trade_action(
|
||||
server: &mut Server,
|
||||
entity: EcsEntity,
|
||||
trade_id: TradeId,
|
||||
action: TradeAction,
|
||||
ProcessTradeActionEvent(entity, trade_id, action): ProcessTradeActionEvent,
|
||||
) {
|
||||
if let Some(uid) = server.state.ecs().uid_from_entity(entity) {
|
||||
let mut trades = server.state.ecs().write_resource::<Trades>();
|
||||
@ -68,7 +67,7 @@ pub(super) fn handle_process_trade_action(
|
||||
.map(|e| {
|
||||
server.notify_client(e, ServerGeneral::FinishedTrade(TradeResult::Declined));
|
||||
notify_agent_simple(
|
||||
server.state.ecs().write_storage::<Agent>(),
|
||||
&mut server.state.ecs().write_storage(),
|
||||
e,
|
||||
AgentEvent::FinishedTrade(TradeResult::Declined),
|
||||
);
|
||||
@ -95,7 +94,7 @@ pub(super) fn handle_process_trade_action(
|
||||
if let Some(e) = server.state.ecs().entity_from_uid(*party) {
|
||||
server.notify_client(e, ServerGeneral::FinishedTrade(result.clone()));
|
||||
notify_agent_simple(
|
||||
server.state.ecs().write_storage::<Agent>(),
|
||||
&mut server.state.ecs().write_storage(),
|
||||
e,
|
||||
AgentEvent::FinishedTrade(result.clone()),
|
||||
);
|
||||
@ -185,7 +184,7 @@ pub(crate) fn cancel_trades_for(state: &mut common_state::State, entity: EcsEnti
|
||||
c.send_fallible(ServerGeneral::FinishedTrade(TradeResult::Declined));
|
||||
}
|
||||
notify_agent_simple(
|
||||
ecs.write_storage::<Agent>(),
|
||||
&mut ecs.write_storage::<Agent>(),
|
||||
e,
|
||||
AgentEvent::FinishedTrade(TradeResult::Declined),
|
||||
);
|
||||
|
@ -5,7 +5,14 @@
|
||||
clippy::needless_pass_by_ref_mut //until we find a better way for specs
|
||||
)]
|
||||
#![deny(clippy::clone_on_ref_ptr)]
|
||||
#![feature(box_patterns, let_chains, never_type, option_zip, unwrap_infallible)]
|
||||
#![feature(
|
||||
box_patterns,
|
||||
let_chains,
|
||||
never_type,
|
||||
option_zip,
|
||||
unwrap_infallible,
|
||||
const_type_name
|
||||
)]
|
||||
|
||||
pub mod automod;
|
||||
mod character_creator;
|
||||
@ -72,7 +79,10 @@ use common::{
|
||||
character::{CharacterId, CharacterItem},
|
||||
cmd::ServerChatCommand,
|
||||
comp,
|
||||
event::{EventBus, ServerEvent},
|
||||
event::{
|
||||
register_event_busses, ClientDisconnectEvent, ClientDisconnectWithoutPersistenceEvent,
|
||||
EventBus, ExitIngameEvent, UpdateCharacterDataEvent,
|
||||
},
|
||||
link::Is,
|
||||
mounting::{Volume, VolumeRider},
|
||||
region::RegionMap,
|
||||
@ -98,7 +108,9 @@ use persistence::{
|
||||
character_updater::CharacterUpdater,
|
||||
};
|
||||
use prometheus::Registry;
|
||||
use specs::{Builder, Entity as EcsEntity, Entity, Join, LendJoin, WorldExt};
|
||||
use specs::{
|
||||
shred::SendDispatcher, Builder, Entity as EcsEntity, Entity, Join, LendJoin, WorldExt,
|
||||
};
|
||||
use std::{
|
||||
i32,
|
||||
ops::{Deref, DerefMut},
|
||||
@ -220,6 +232,7 @@ pub struct Server {
|
||||
disconnect_all_clients_requested: bool,
|
||||
|
||||
server_constants: ServerConstants,
|
||||
event_dispatcher: SendDispatcher<'static>,
|
||||
}
|
||||
|
||||
impl Server {
|
||||
@ -304,7 +317,7 @@ impl Server {
|
||||
report_stage(ServerInitStage::StartingSystems);
|
||||
|
||||
let mut state = State::server(
|
||||
pools,
|
||||
Arc::clone(&pools),
|
||||
world.sim().map_size_lg(),
|
||||
Arc::clone(&map.default_chunk),
|
||||
|dispatcher_builder| {
|
||||
@ -318,13 +331,15 @@ impl Server {
|
||||
}
|
||||
},
|
||||
);
|
||||
register_event_busses(state.ecs_mut());
|
||||
state.ecs_mut().insert(battlemode_buffer);
|
||||
state.ecs_mut().insert(settings.clone());
|
||||
state.ecs_mut().insert(editable_settings);
|
||||
state.ecs_mut().insert(DataDir {
|
||||
path: data_dir.to_owned(),
|
||||
});
|
||||
state.ecs_mut().insert(EventBus::<ServerEvent>::default());
|
||||
|
||||
register_event_busses(state.ecs_mut());
|
||||
state.ecs_mut().insert(Vec::<ChunkRequest>::new());
|
||||
state
|
||||
.ecs_mut()
|
||||
@ -610,6 +625,8 @@ impl Server {
|
||||
disconnect_all_clients_requested: false,
|
||||
|
||||
server_constants,
|
||||
|
||||
event_dispatcher: Self::create_event_dispatcher(pools),
|
||||
};
|
||||
|
||||
debug!(?settings, "created veloren server with");
|
||||
@ -989,7 +1006,7 @@ impl Server {
|
||||
),
|
||||
},
|
||||
CharacterScreenResponseKind::CharacterData(result) => {
|
||||
let message = match *result {
|
||||
match *result {
|
||||
Ok((character_data, skill_set_persistence_load_error)) => {
|
||||
let PersistedComponents {
|
||||
body,
|
||||
@ -1013,11 +1030,11 @@ impl Server {
|
||||
);
|
||||
// TODO: Does this need to be a server event? E.g. we could
|
||||
// just handle it here.
|
||||
ServerEvent::UpdateCharacterData {
|
||||
self.state.emit_event_now(UpdateCharacterDataEvent {
|
||||
entity: response.target_entity,
|
||||
components: character_data,
|
||||
metadata: skill_set_persistence_load_error,
|
||||
}
|
||||
})
|
||||
},
|
||||
Err(error) => {
|
||||
// We failed to load data for the character from the DB. Notify
|
||||
@ -1031,16 +1048,11 @@ impl Server {
|
||||
);
|
||||
|
||||
// Clean up the entity data on the server
|
||||
ServerEvent::ExitIngame {
|
||||
self.state.emit_event_now(ExitIngameEvent {
|
||||
entity: response.target_entity,
|
||||
}
|
||||
})
|
||||
},
|
||||
};
|
||||
|
||||
self.state
|
||||
.ecs()
|
||||
.read_resource::<EventBus<ServerEvent>>()
|
||||
.emit_now(message);
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
@ -1194,15 +1206,15 @@ impl Server {
|
||||
);
|
||||
for (_, entity) in (&clients, &entities).join() {
|
||||
info!("Emitting client disconnect event for entity: {:?}", entity);
|
||||
let event = if with_persistence {
|
||||
ServerEvent::ClientDisconnect(entity, comp::DisconnectReason::Kicked)
|
||||
if with_persistence {
|
||||
self.state.emit_event_now(ClientDisconnectEvent(
|
||||
entity,
|
||||
comp::DisconnectReason::Kicked,
|
||||
))
|
||||
} else {
|
||||
ServerEvent::ClientDisconnectWithoutPersistence(entity)
|
||||
};
|
||||
self.state
|
||||
.ecs()
|
||||
.read_resource::<EventBus<ServerEvent>>()
|
||||
.emit_now(event);
|
||||
.emit_event_now(ClientDisconnectWithoutPersistenceEvent(entity))
|
||||
};
|
||||
}
|
||||
|
||||
self.disconnect_all_clients_requested = false;
|
||||
|
@ -5,7 +5,7 @@ use crate::sys::terrain::NpcData;
|
||||
use common::{
|
||||
calendar::Calendar,
|
||||
comp::{self, Agent, Body, Presence, PresenceKind},
|
||||
event::{EventBus, NpcBuilder, ServerEvent},
|
||||
event::{CreateNpcEvent, CreateShipEvent, DeleteEvent, EventBus, NpcBuilder},
|
||||
generation::{BodyBuilder, EntityConfig, EntityInfo},
|
||||
resources::{DeltaTime, Time, TimeOfDay},
|
||||
rtsim::{Actor, NpcId, RtSimEntity},
|
||||
@ -236,7 +236,9 @@ impl<'a> System<'a> for Sys {
|
||||
Read<'a, DeltaTime>,
|
||||
Read<'a, Time>,
|
||||
Read<'a, TimeOfDay>,
|
||||
Read<'a, EventBus<ServerEvent>>,
|
||||
Read<'a, EventBus<CreateShipEvent>>,
|
||||
Read<'a, EventBus<CreateNpcEvent>>,
|
||||
Read<'a, EventBus<DeleteEvent>>,
|
||||
WriteExpect<'a, RtSim>,
|
||||
ReadExpect<'a, Arc<world::World>>,
|
||||
ReadExpect<'a, world::IndexOwned>,
|
||||
@ -259,7 +261,9 @@ impl<'a> System<'a> for Sys {
|
||||
dt,
|
||||
time,
|
||||
time_of_day,
|
||||
server_event_bus,
|
||||
create_ship_events,
|
||||
create_npc_events,
|
||||
delete_events,
|
||||
mut rtsim,
|
||||
world,
|
||||
index,
|
||||
@ -271,7 +275,9 @@ impl<'a> System<'a> for Sys {
|
||||
calendar,
|
||||
): Self::SystemData,
|
||||
) {
|
||||
let mut emitter = server_event_bus.emitter();
|
||||
let mut create_ship_emitter = create_ship_events.emitter();
|
||||
let mut create_npc_emitter = create_npc_events.emitter();
|
||||
let mut delete_emitter = delete_events.emitter();
|
||||
let rtsim = &mut *rtsim;
|
||||
let calendar_data = (*time_of_day, (*calendar).clone());
|
||||
|
||||
@ -317,7 +323,7 @@ impl<'a> System<'a> for Sys {
|
||||
|
||||
let mut create_event = |id: NpcId, npc: &Npc, steering: Option<NpcBuilder>| match npc.body {
|
||||
Body::Ship(body) => {
|
||||
emitter.emit(ServerEvent::CreateShip {
|
||||
create_ship_emitter.emit(CreateShipEvent {
|
||||
pos: comp::Pos(npc.wpos),
|
||||
ori: comp::Ori::from(Dir::new(npc.dir.with_z(0.0))),
|
||||
ship: body,
|
||||
@ -333,7 +339,7 @@ impl<'a> System<'a> for Sys {
|
||||
Some(&calendar_data),
|
||||
);
|
||||
|
||||
emitter.emit(match NpcData::from_entity_info(entity_info) {
|
||||
create_npc_emitter.emit(match NpcData::from_entity_info(entity_info) {
|
||||
NpcData::Data {
|
||||
pos,
|
||||
stats,
|
||||
@ -346,7 +352,7 @@ impl<'a> System<'a> for Sys {
|
||||
alignment,
|
||||
scale,
|
||||
loot,
|
||||
} => ServerEvent::CreateNpc {
|
||||
} => CreateNpcEvent {
|
||||
pos,
|
||||
ori: comp::Ori::from(Dir::new(npc.dir.with_z(0.0))),
|
||||
npc: NpcBuilder::new(stats, body, alignment)
|
||||
@ -458,7 +464,6 @@ impl<'a> System<'a> for Sys {
|
||||
}
|
||||
}
|
||||
|
||||
let mut emitter = server_event_bus.emitter();
|
||||
// Synchronise rtsim NPC with entity data
|
||||
for (entity, pos, rtsim_entity, agent) in (
|
||||
&entities,
|
||||
@ -489,7 +494,7 @@ impl<'a> System<'a> for Sys {
|
||||
}
|
||||
},
|
||||
SimulationMode::Simulated => {
|
||||
emitter.emit(ServerEvent::Delete(entity));
|
||||
delete_emitter.emit(DeleteEvent(entity));
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -42,7 +42,10 @@ use common_net::{
|
||||
};
|
||||
use common_state::State;
|
||||
use rand::prelude::*;
|
||||
use specs::{Builder, Entity as EcsEntity, EntityBuilder as EcsEntityBuilder, Join, WorldExt};
|
||||
use specs::{
|
||||
storage::{GenericReadStorage, GenericWriteStorage},
|
||||
Builder, Entity as EcsEntity, EntityBuilder as EcsEntityBuilder, Join, WorldExt, WriteStorage,
|
||||
};
|
||||
use std::time::{Duration, Instant};
|
||||
use tracing::{error, trace, warn};
|
||||
use vek::*;
|
||||
@ -69,6 +72,7 @@ pub trait StateExt {
|
||||
fn create_item_drop(
|
||||
&mut self,
|
||||
pos: comp::Pos,
|
||||
ori: comp::Ori,
|
||||
vel: comp::Vel,
|
||||
item: Item,
|
||||
loot_owner: Option<LootOwner>,
|
||||
@ -335,6 +339,7 @@ impl StateExt for State {
|
||||
fn create_item_drop(
|
||||
&mut self,
|
||||
pos: comp::Pos,
|
||||
ori: comp::Ori,
|
||||
vel: comp::Vel,
|
||||
item: Item,
|
||||
loot_owner: Option<LootOwner>,
|
||||
@ -397,6 +402,7 @@ impl StateExt for State {
|
||||
.create_entity_synced()
|
||||
.with(item)
|
||||
.with(pos)
|
||||
.with(ori)
|
||||
.with(vel)
|
||||
.with(item_drop.orientation(&mut thread_rng()))
|
||||
.with(item_drop.mass())
|
||||
@ -1253,28 +1259,47 @@ impl StateExt for State {
|
||||
dismount_volume: bool,
|
||||
f: impl for<'a> FnOnce(&'a mut comp::Pos) -> T,
|
||||
) -> Result<T, Content> {
|
||||
let ecs = self.ecs_mut();
|
||||
position_mut(
|
||||
entity,
|
||||
dismount_volume,
|
||||
f,
|
||||
&ecs.read_resource(),
|
||||
&mut ecs.write_storage(),
|
||||
ecs.write_storage(),
|
||||
ecs.write_storage(),
|
||||
ecs.read_storage(),
|
||||
ecs.read_storage(),
|
||||
ecs.read_storage(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn position_mut<T>(
|
||||
entity: EcsEntity,
|
||||
dismount_volume: bool,
|
||||
f: impl for<'a> FnOnce(&'a mut comp::Pos) -> T,
|
||||
id_maps: &IdMaps,
|
||||
is_volume_riders: &mut WriteStorage<Is<VolumeRider>>,
|
||||
mut positions: impl GenericWriteStorage<Component = comp::Pos>,
|
||||
mut force_updates: impl GenericWriteStorage<Component = comp::ForceUpdate>,
|
||||
is_riders: impl GenericReadStorage<Component = Is<Rider>>,
|
||||
presences: impl GenericReadStorage<Component = Presence>,
|
||||
clients: impl GenericReadStorage<Component = Client>,
|
||||
) -> Result<T, Content> {
|
||||
if dismount_volume {
|
||||
self.ecs().write_storage::<Is<VolumeRider>>().remove(entity);
|
||||
is_volume_riders.remove(entity);
|
||||
}
|
||||
|
||||
let entity = self
|
||||
.read_storage::<Is<Rider>>()
|
||||
let entity = is_riders
|
||||
.get(entity)
|
||||
.and_then(|is_rider| {
|
||||
self.ecs()
|
||||
.read_resource::<IdMaps>()
|
||||
.uid_entity(is_rider.mount)
|
||||
})
|
||||
.and_then(|is_rider| id_maps.uid_entity(is_rider.mount))
|
||||
.map(Ok)
|
||||
.or_else(|| {
|
||||
self.read_storage::<Is<VolumeRider>>()
|
||||
.get(entity)
|
||||
.and_then(|volume_rider| {
|
||||
is_volume_riders.get(entity).and_then(|volume_rider| {
|
||||
Some(match volume_rider.pos.kind {
|
||||
common::mounting::Volume::Terrain => Err("Tried to move the world."),
|
||||
common::mounting::Volume::Entity(uid) => {
|
||||
Ok(self.ecs().read_resource::<IdMaps>().uid_entity(uid)?)
|
||||
},
|
||||
common::mounting::Volume::Entity(uid) => Ok(id_maps.uid_entity(uid)?),
|
||||
})
|
||||
})
|
||||
})
|
||||
@ -1282,9 +1307,7 @@ impl StateExt for State {
|
||||
|
||||
let mut maybe_pos = None;
|
||||
|
||||
let res = self
|
||||
.ecs()
|
||||
.write_storage::<comp::Pos>()
|
||||
let res = positions
|
||||
.get_mut(entity)
|
||||
.map(|pos| {
|
||||
let res = f(pos);
|
||||
@ -1297,26 +1320,22 @@ impl StateExt for State {
|
||||
));
|
||||
|
||||
if let Some(pos) = maybe_pos {
|
||||
if self
|
||||
.ecs()
|
||||
.read_storage::<Presence>()
|
||||
if presences
|
||||
.get(entity)
|
||||
.map(|presence| presence.kind == PresenceKind::Spectator)
|
||||
.unwrap_or(false)
|
||||
{
|
||||
self.read_storage::<Client>().get(entity).map(|client| {
|
||||
clients.get(entity).map(|client| {
|
||||
client.send_fallible(ServerGeneral::SpectatePosition(pos));
|
||||
});
|
||||
} else {
|
||||
self.ecs()
|
||||
.write_storage::<comp::ForceUpdate>()
|
||||
force_updates
|
||||
.get_mut(entity)
|
||||
.map(|force_update| force_update.update());
|
||||
}
|
||||
}
|
||||
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
fn send_to_group(g: &Group, ecs: &specs::World, msg: &comp::ChatMsg) {
|
||||
|
@ -1,4 +1,5 @@
|
||||
pub mod behavior_tree;
|
||||
use server_agent::data::AgentEvents;
|
||||
pub use server_agent::{action_nodes, attack, consts, data, util};
|
||||
use vek::Vec3;
|
||||
|
||||
@ -11,7 +12,6 @@ use common::{
|
||||
self, inventory::slot::EquipSlot, item::ItemDesc, Agent, Alignment, Body, CharacterState,
|
||||
Controller, Health, InputKind, Scale,
|
||||
},
|
||||
event::{EventBus, ServerEvent},
|
||||
mounting::Volume,
|
||||
path::TraversalConfig,
|
||||
};
|
||||
@ -19,7 +19,7 @@ use common_base::prof_span;
|
||||
use common_ecs::{Job, Origin, ParMode, Phase, System};
|
||||
use rand::thread_rng;
|
||||
use rayon::iter::ParallelIterator;
|
||||
use specs::{LendJoin, ParJoin, Read, WriteStorage};
|
||||
use specs::{LendJoin, ParJoin, WriteStorage};
|
||||
|
||||
/// This system will allow NPCs to modify their controller
|
||||
#[derive(Default)]
|
||||
@ -27,7 +27,7 @@ pub struct Sys;
|
||||
impl<'a> System<'a> for Sys {
|
||||
type SystemData = (
|
||||
ReadData<'a>,
|
||||
Read<'a, EventBus<ServerEvent>>,
|
||||
AgentEvents<'a>,
|
||||
WriteStorage<'a, Agent>,
|
||||
WriteStorage<'a, Controller>,
|
||||
);
|
||||
@ -38,7 +38,7 @@ impl<'a> System<'a> for Sys {
|
||||
|
||||
fn run(
|
||||
job: &mut Job<Self>,
|
||||
(read_data, event_bus, mut agents, mut controllers): Self::SystemData,
|
||||
(read_data, events, mut agents, mut controllers): Self::SystemData,
|
||||
) {
|
||||
job.cpu_stats.measure(ParMode::Rayon);
|
||||
|
||||
@ -97,7 +97,7 @@ impl<'a> System<'a> for Sys {
|
||||
rtsim_entity,
|
||||
(_, is_rider, is_volume_rider),
|
||||
)| {
|
||||
let mut event_emitter = event_bus.emitter();
|
||||
let mut emitters = events.get_emitters();
|
||||
let mut rng = thread_rng();
|
||||
|
||||
// The entity that is moving, if riding it's the mount, otherwise it's itself
|
||||
@ -263,7 +263,7 @@ impl<'a> System<'a> for Sys {
|
||||
agent,
|
||||
agent_data: data,
|
||||
read_data: &read_data,
|
||||
event_emitter: &mut event_emitter,
|
||||
emitters: &mut emitters,
|
||||
controller,
|
||||
rng: &mut rng,
|
||||
};
|
||||
|
@ -8,11 +8,11 @@ use common::{
|
||||
Agent, Alignment, BehaviorCapability, BehaviorState, Body, BuffKind, ControlAction,
|
||||
ControlEvent, Controller, InputKind, InventoryEvent, Pos, UtteranceKind,
|
||||
},
|
||||
event::{Emitter, ServerEvent},
|
||||
path::TraversalConfig,
|
||||
rtsim::{NpcAction, RtSimEntity},
|
||||
};
|
||||
use rand::{prelude::ThreadRng, thread_rng, Rng};
|
||||
use server_agent::data::AgentEmitters;
|
||||
use specs::Entity as EcsEntity;
|
||||
use vek::{Vec2, Vec3};
|
||||
|
||||
@ -39,7 +39,7 @@ pub struct BehaviorData<'a, 'b, 'c> {
|
||||
pub agent: &'a mut Agent,
|
||||
pub agent_data: AgentData<'a>,
|
||||
pub read_data: &'a ReadData<'a>,
|
||||
pub event_emitter: &'a mut Emitter<'c, ServerEvent>,
|
||||
pub emitters: &'a mut AgentEmitters<'c>,
|
||||
pub controller: &'a mut Controller,
|
||||
pub rng: &'b mut ThreadRng,
|
||||
}
|
||||
@ -454,7 +454,7 @@ fn attack_if_owner_hurt(bdata: &mut BehaviorData) -> bool {
|
||||
bdata.agent,
|
||||
bdata.read_data,
|
||||
bdata.controller,
|
||||
bdata.event_emitter,
|
||||
bdata.emitters,
|
||||
bdata.rng,
|
||||
);
|
||||
return true;
|
||||
@ -519,7 +519,7 @@ fn handle_rtsim_actions(bdata: &mut BehaviorData) -> bool {
|
||||
}
|
||||
}
|
||||
bdata.controller.push_utterance(UtteranceKind::Greeting);
|
||||
bdata.agent_data.chat_npc(msg, bdata.event_emitter);
|
||||
bdata.agent_data.chat_npc(msg, bdata.emitters);
|
||||
}
|
||||
},
|
||||
NpcAction::Attack(target) => {
|
||||
@ -574,7 +574,7 @@ fn handle_timed_events(bdata: &mut BehaviorData) -> bool {
|
||||
bdata.agent,
|
||||
bdata.controller,
|
||||
bdata.read_data,
|
||||
bdata.event_emitter,
|
||||
bdata.emitters,
|
||||
AgentData::is_enemy,
|
||||
);
|
||||
} else {
|
||||
@ -582,7 +582,7 @@ fn handle_timed_events(bdata: &mut BehaviorData) -> bool {
|
||||
bdata.agent,
|
||||
bdata.controller,
|
||||
bdata.read_data,
|
||||
bdata.event_emitter,
|
||||
bdata.emitters,
|
||||
bdata.rng,
|
||||
);
|
||||
}
|
||||
@ -728,7 +728,7 @@ fn do_combat(bdata: &mut BehaviorData) -> bool {
|
||||
agent,
|
||||
agent_data,
|
||||
read_data,
|
||||
event_emitter,
|
||||
emitters,
|
||||
controller,
|
||||
rng,
|
||||
} = bdata;
|
||||
@ -777,7 +777,7 @@ fn do_combat(bdata: &mut BehaviorData) -> bool {
|
||||
[ActionStateBehaviorTreeTimers::TimerBehaviorTree as usize]
|
||||
== 0.0
|
||||
{
|
||||
agent_data.cry_out(agent, event_emitter, read_data);
|
||||
agent_data.cry_out(agent, emitters, read_data);
|
||||
agent.behavior_state.timers
|
||||
[ActionStateBehaviorTreeTimers::TimerBehaviorTree as usize] = 0.01;
|
||||
agent.flee_from_pos = {
|
||||
@ -803,12 +803,12 @@ fn do_combat(bdata: &mut BehaviorData) -> bool {
|
||||
[ActionStateBehaviorTreeTimers::TimerBehaviorTree as usize] = 0.0;
|
||||
agent.target = None;
|
||||
agent.flee_from_pos = None;
|
||||
agent_data.idle(agent, controller, read_data, event_emitter, rng);
|
||||
agent_data.idle(agent, controller, read_data, emitters, rng);
|
||||
}
|
||||
} else if is_dead(target, read_data) {
|
||||
agent_data.exclaim_relief_about_enemy_dead(agent, event_emitter);
|
||||
agent_data.exclaim_relief_about_enemy_dead(agent, emitters);
|
||||
agent.target = None;
|
||||
agent_data.idle(agent, controller, read_data, event_emitter, rng);
|
||||
agent_data.idle(agent, controller, read_data, emitters, rng);
|
||||
} else if is_invulnerable(target, read_data)
|
||||
|| stop_pursuing(
|
||||
dist_sqrd,
|
||||
@ -820,7 +820,7 @@ fn do_combat(bdata: &mut BehaviorData) -> bool {
|
||||
)
|
||||
{
|
||||
agent.target = None;
|
||||
agent_data.idle(agent, controller, read_data, event_emitter, rng);
|
||||
agent_data.idle(agent, controller, read_data, emitters, rng);
|
||||
} else {
|
||||
let is_time_to_retarget =
|
||||
read_data.time.0 - selected_at > RETARGETING_THRESHOLD_SECONDS;
|
||||
@ -830,7 +830,7 @@ fn do_combat(bdata: &mut BehaviorData) -> bool {
|
||||
agent,
|
||||
controller,
|
||||
read_data,
|
||||
event_emitter,
|
||||
emitters,
|
||||
AgentData::is_enemy,
|
||||
);
|
||||
}
|
||||
@ -849,7 +849,7 @@ fn do_combat(bdata: &mut BehaviorData) -> bool {
|
||||
controller,
|
||||
target,
|
||||
read_data,
|
||||
event_emitter,
|
||||
emitters,
|
||||
rng,
|
||||
remembers_fight_with(agent_data.rtsim_entity, read_data, target),
|
||||
);
|
||||
|
@ -9,7 +9,7 @@ use common::{
|
||||
BehaviorState, Content, ControlAction, Item, TradingBehavior, UnresolvedChatMsg,
|
||||
UtteranceKind,
|
||||
},
|
||||
event::ServerEvent,
|
||||
event::{ChatEvent, EmitExt, ProcessTradeActionEvent},
|
||||
rtsim::{Actor, NpcInput, PersonalityTrait},
|
||||
trade::{TradeAction, TradePhase, TradeResult},
|
||||
};
|
||||
@ -76,7 +76,7 @@ pub fn handle_inbox_talk(bdata: &mut BehaviorData) -> bool {
|
||||
agent,
|
||||
agent_data,
|
||||
read_data,
|
||||
event_emitter,
|
||||
emitters,
|
||||
controller,
|
||||
..
|
||||
} = bdata;
|
||||
@ -171,7 +171,7 @@ pub fn handle_inbox_talk(bdata: &mut BehaviorData) -> bool {
|
||||
standard_response_msg()
|
||||
};
|
||||
// TODO: Localise
|
||||
agent_data.chat_npc(Content::Plain(msg), event_emitter);
|
||||
agent_data.chat_npc(Content::Plain(msg), emitters);
|
||||
} else {
|
||||
let mut rng = thread_rng();
|
||||
agent_data.chat_npc(
|
||||
@ -179,7 +179,7 @@ pub fn handle_inbox_talk(bdata: &mut BehaviorData) -> bool {
|
||||
.rtsim_controller
|
||||
.personality
|
||||
.get_generic_comment(&mut rng),
|
||||
event_emitter,
|
||||
emitters,
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -191,13 +191,13 @@ pub fn handle_inbox_talk(bdata: &mut BehaviorData) -> bool {
|
||||
agent_data.chat_npc_if_allowed_to_speak(
|
||||
Content::localized("npc-speech-merchant_advertisement"),
|
||||
agent,
|
||||
event_emitter,
|
||||
emitters,
|
||||
);
|
||||
} else {
|
||||
agent_data.chat_npc_if_allowed_to_speak(
|
||||
Content::localized("npc-speech-merchant_busy"),
|
||||
agent,
|
||||
event_emitter,
|
||||
emitters,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
@ -206,7 +206,7 @@ pub fn handle_inbox_talk(bdata: &mut BehaviorData) -> bool {
|
||||
agent_data.chat_npc_if_allowed_to_speak(
|
||||
Content::localized("npc-speech-villager_decline_trade"),
|
||||
agent,
|
||||
event_emitter,
|
||||
emitters,
|
||||
);
|
||||
}
|
||||
},
|
||||
@ -224,7 +224,7 @@ pub fn handle_inbox_talk(bdata: &mut BehaviorData) -> bool {
|
||||
"{} ? I think it's {} {} from here!",
|
||||
location.name, dist, dir
|
||||
);
|
||||
agent_data.chat_npc(Content::Plain(msg), event_emitter);
|
||||
agent_data.chat_npc(Content::Plain(msg), emitters);
|
||||
}
|
||||
},
|
||||
Subject::Person(person) => {
|
||||
@ -259,7 +259,7 @@ pub fn handle_inbox_talk(bdata: &mut BehaviorData) -> bool {
|
||||
person.name()
|
||||
)
|
||||
};
|
||||
agent_data.chat_npc(Content::Plain(msg), event_emitter);
|
||||
agent_data.chat_npc(Content::Plain(msg), emitters);
|
||||
}
|
||||
},
|
||||
Subject::Work => {},
|
||||
@ -276,7 +276,7 @@ pub fn handle_inbox_trade_invite(bdata: &mut BehaviorData) -> bool {
|
||||
agent,
|
||||
agent_data,
|
||||
read_data,
|
||||
event_emitter,
|
||||
emitters,
|
||||
controller,
|
||||
..
|
||||
} = bdata;
|
||||
@ -313,7 +313,7 @@ pub fn handle_inbox_trade_invite(bdata: &mut BehaviorData) -> bool {
|
||||
agent_data.chat_npc_if_allowed_to_speak(
|
||||
Content::localized("npc-speech-merchant_busy"),
|
||||
agent,
|
||||
event_emitter,
|
||||
emitters,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
@ -322,7 +322,7 @@ pub fn handle_inbox_trade_invite(bdata: &mut BehaviorData) -> bool {
|
||||
agent_data.chat_npc_if_allowed_to_speak(
|
||||
Content::localized("npc-speech-villager_decline_trade"),
|
||||
agent,
|
||||
event_emitter,
|
||||
emitters,
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -364,7 +364,7 @@ pub fn handle_inbox_finished_trade(bdata: &mut BehaviorData) -> bool {
|
||||
let BehaviorData {
|
||||
agent,
|
||||
agent_data,
|
||||
event_emitter,
|
||||
emitters,
|
||||
..
|
||||
} = bdata;
|
||||
|
||||
@ -379,14 +379,14 @@ pub fn handle_inbox_finished_trade(bdata: &mut BehaviorData) -> bool {
|
||||
agent_data.chat_npc_if_allowed_to_speak(
|
||||
Content::localized("npc-speech-merchant_trade_successful"),
|
||||
agent,
|
||||
event_emitter,
|
||||
emitters,
|
||||
);
|
||||
},
|
||||
_ => {
|
||||
agent_data.chat_npc_if_allowed_to_speak(
|
||||
Content::localized("npc-speech-merchant_trade_declined"),
|
||||
agent,
|
||||
event_emitter,
|
||||
emitters,
|
||||
);
|
||||
},
|
||||
}
|
||||
@ -404,7 +404,7 @@ pub fn handle_inbox_update_pending_trade(bdata: &mut BehaviorData) -> bool {
|
||||
agent,
|
||||
agent_data,
|
||||
read_data,
|
||||
event_emitter,
|
||||
emitters,
|
||||
..
|
||||
} = bdata;
|
||||
|
||||
@ -422,13 +422,13 @@ pub fn handle_inbox_update_pending_trade(bdata: &mut BehaviorData) -> bool {
|
||||
.as_ref()
|
||||
.and_then(|tgt_data| read_data.uids.get(tgt_data.target))
|
||||
{
|
||||
event_emitter.emit(ServerEvent::Chat(UnresolvedChatMsg::npc_tell(
|
||||
emitters.emit(ChatEvent(UnresolvedChatMsg::npc_tell(
|
||||
*agent_data.uid,
|
||||
*with,
|
||||
content,
|
||||
)));
|
||||
} else {
|
||||
event_emitter.emit(ServerEvent::Chat(UnresolvedChatMsg::npc_say(
|
||||
emitters.emit(ChatEvent(UnresolvedChatMsg::npc_say(
|
||||
*agent_data.uid,
|
||||
content,
|
||||
)));
|
||||
@ -457,7 +457,7 @@ pub fn handle_inbox_update_pending_trade(bdata: &mut BehaviorData) -> bool {
|
||||
// the phase is included in the message, this shouldn't
|
||||
// result in fully accepting an unfavourable trade))
|
||||
if !pending.accept_flags[who] && !pending.is_empty_trade() {
|
||||
event_emitter.emit(ServerEvent::ProcessTradeAction(
|
||||
emitters.emit(ProcessTradeActionEvent(
|
||||
*agent_data.entity,
|
||||
tradeid,
|
||||
TradeAction::Accept(pending.phase),
|
||||
@ -482,7 +482,7 @@ pub fn handle_inbox_update_pending_trade(bdata: &mut BehaviorData) -> bool {
|
||||
// decline
|
||||
agent.behavior.unset(BehaviorState::TRADING);
|
||||
agent.target = None;
|
||||
event_emitter.emit(ServerEvent::ProcessTradeAction(
|
||||
emitters.emit(ProcessTradeActionEvent(
|
||||
*agent_data.entity,
|
||||
tradeid,
|
||||
TradeAction::Decline,
|
||||
@ -517,7 +517,7 @@ pub fn handle_inbox_update_pending_trade(bdata: &mut BehaviorData) -> bool {
|
||||
&& !pending.offers[1 - who].is_empty()
|
||||
&& only_food
|
||||
{
|
||||
event_emitter.emit(ServerEvent::ProcessTradeAction(
|
||||
emitters.emit(ProcessTradeActionEvent(
|
||||
*agent_data.entity,
|
||||
tradeid,
|
||||
TradeAction::Accept(pending.phase),
|
||||
@ -527,7 +527,7 @@ pub fn handle_inbox_update_pending_trade(bdata: &mut BehaviorData) -> bool {
|
||||
TradingBehavior::None => {
|
||||
agent.behavior.unset(BehaviorState::TRADING);
|
||||
agent.target = None;
|
||||
event_emitter.emit(ServerEvent::ProcessTradeAction(
|
||||
emitters.emit(ProcessTradeActionEvent(
|
||||
*agent_data.entity,
|
||||
tradeid,
|
||||
TradeAction::Decline,
|
||||
@ -549,7 +549,7 @@ pub fn handle_inbox_cancel_interactions(bdata: &mut BehaviorData) -> bool {
|
||||
let BehaviorData {
|
||||
agent,
|
||||
agent_data,
|
||||
event_emitter,
|
||||
emitters,
|
||||
controller,
|
||||
..
|
||||
} = bdata;
|
||||
@ -566,7 +566,7 @@ pub fn handle_inbox_cancel_interactions(bdata: &mut BehaviorData) -> bool {
|
||||
agent_data.chat_npc_if_allowed_to_speak(
|
||||
Content::localized("npc-speech-villager_busy"),
|
||||
agent,
|
||||
event_emitter,
|
||||
emitters,
|
||||
);
|
||||
}
|
||||
},
|
||||
@ -581,13 +581,13 @@ pub fn handle_inbox_cancel_interactions(bdata: &mut BehaviorData) -> bool {
|
||||
agent_data.chat_npc_if_allowed_to_speak(
|
||||
Content::localized("npc-speech-merchant_busy"),
|
||||
agent,
|
||||
event_emitter,
|
||||
emitters,
|
||||
);
|
||||
} else {
|
||||
agent_data.chat_npc_if_allowed_to_speak(
|
||||
Content::localized("npc-speech-villager_busy"),
|
||||
agent,
|
||||
event_emitter,
|
||||
emitters,
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -602,14 +602,14 @@ pub fn handle_inbox_cancel_interactions(bdata: &mut BehaviorData) -> bool {
|
||||
agent_data.chat_npc_if_allowed_to_speak(
|
||||
Content::localized("npc-speech-merchant_trade_successful"),
|
||||
agent,
|
||||
event_emitter,
|
||||
emitters,
|
||||
);
|
||||
},
|
||||
_ => {
|
||||
agent_data.chat_npc_if_allowed_to_speak(
|
||||
Content::localized("npc-speech-merchant_trade_declined"),
|
||||
agent,
|
||||
event_emitter,
|
||||
emitters,
|
||||
);
|
||||
},
|
||||
}
|
||||
@ -622,7 +622,7 @@ pub fn handle_inbox_cancel_interactions(bdata: &mut BehaviorData) -> bool {
|
||||
let (tradeid, _pending, _prices, _inventories) = &**boxval;
|
||||
agent.behavior.unset(BehaviorState::TRADING);
|
||||
agent.target = None;
|
||||
event_emitter.emit(ServerEvent::ProcessTradeAction(
|
||||
emitters.emit(ProcessTradeActionEvent(
|
||||
*agent_data.entity,
|
||||
*tradeid,
|
||||
TradeAction::Decline,
|
||||
@ -630,7 +630,7 @@ pub fn handle_inbox_cancel_interactions(bdata: &mut BehaviorData) -> bool {
|
||||
agent_data.chat_npc_if_allowed_to_speak(
|
||||
Content::localized("npc-speech-merchant_trade_cancelled_hostile"),
|
||||
agent,
|
||||
event_emitter,
|
||||
emitters,
|
||||
);
|
||||
},
|
||||
AgentEvent::ServerSound(_) | AgentEvent::Hurt => return false,
|
||||
|
@ -12,21 +12,35 @@ use crate::{
|
||||
};
|
||||
use common::{
|
||||
comp::{Admin, AdminRole, ChatType, Player, Presence, Waypoint},
|
||||
event::{EventBus, ServerEvent},
|
||||
event::{
|
||||
ChatEvent, ClientDisconnectEvent, DeleteCharacterEvent, EmitExt, InitializeCharacterEvent,
|
||||
InitializeSpectatorEvent,
|
||||
},
|
||||
event_emitters,
|
||||
resources::Time,
|
||||
terrain::TerrainChunkSize,
|
||||
uid::Uid,
|
||||
};
|
||||
use common_ecs::{Job, Origin, Phase, System};
|
||||
use common_net::msg::{ClientGeneral, ServerGeneral};
|
||||
use specs::{Entities, Join, Read, ReadExpect, ReadStorage, WriteExpect, WriteStorage};
|
||||
use specs::{Entities, Join, ReadExpect, ReadStorage, WriteExpect, WriteStorage};
|
||||
use std::sync::{atomic::Ordering, Arc};
|
||||
use tracing::{debug, error};
|
||||
|
||||
event_emitters! {
|
||||
struct Events[Emitters] {
|
||||
init_spectator: InitializeSpectatorEvent,
|
||||
init_character_data: InitializeCharacterEvent,
|
||||
delete_character: DeleteCharacterEvent,
|
||||
client_disconnect: ClientDisconnectEvent,
|
||||
chat: ChatEvent,
|
||||
}
|
||||
}
|
||||
|
||||
impl Sys {
|
||||
#[allow(clippy::too_many_arguments)] // Shhhh, go bother someone else clippy
|
||||
fn handle_client_character_screen_msg(
|
||||
server_emitter: &mut common::event::Emitter<'_, ServerEvent>,
|
||||
emitters: &mut Emitters,
|
||||
entity: specs::Entity,
|
||||
client: &Client,
|
||||
character_loader: &ReadExpect<'_, CharacterLoader>,
|
||||
@ -65,9 +79,7 @@ impl Sys {
|
||||
|
||||
if !client.login_msg_sent.load(Ordering::Relaxed) {
|
||||
if let Some(player_uid) = uids.get(entity) {
|
||||
server_emitter.emit(ServerEvent::Chat(
|
||||
ChatType::Online(*player_uid).into_plain_msg(""),
|
||||
));
|
||||
emitters.emit(ChatEvent(ChatType::Online(*player_uid).into_plain_msg("")));
|
||||
|
||||
client.login_msg_sent.store(true, Ordering::Relaxed);
|
||||
}
|
||||
@ -82,8 +94,7 @@ impl Sys {
|
||||
{
|
||||
send_join_messages()?;
|
||||
|
||||
server_emitter
|
||||
.emit(ServerEvent::InitSpectator(entity, requested_view_distances));
|
||||
emitters.emit(InitializeSpectatorEvent(entity, requested_view_distances));
|
||||
} else {
|
||||
debug!("dropped Spectate msg from unprivileged client")
|
||||
}
|
||||
@ -131,7 +142,7 @@ impl Sys {
|
||||
|
||||
// Start inserting non-persisted/default components for the entity
|
||||
// while we load the DB data
|
||||
server_emitter.emit(ServerEvent::InitCharacterData {
|
||||
emitters.emit(InitializeCharacterEvent {
|
||||
entity,
|
||||
character_id,
|
||||
requested_view_distances,
|
||||
@ -242,7 +253,7 @@ impl Sys {
|
||||
},
|
||||
ClientGeneral::DeleteCharacter(character_id) => {
|
||||
if let Some(player) = players.get(entity) {
|
||||
server_emitter.emit(ServerEvent::DeleteCharacter {
|
||||
emitters.emit(DeleteCharacterEvent {
|
||||
entity,
|
||||
requesting_player_uuid: player.uuid().to_string(),
|
||||
character_id,
|
||||
@ -251,7 +262,7 @@ impl Sys {
|
||||
},
|
||||
_ => {
|
||||
debug!("Kicking possibly misbehaving client due to invalid character request");
|
||||
server_emitter.emit(ServerEvent::ClientDisconnect(
|
||||
emitters.emit(ClientDisconnectEvent(
|
||||
entity,
|
||||
common::comp::DisconnectReason::NetworkError,
|
||||
));
|
||||
@ -267,7 +278,7 @@ pub struct Sys;
|
||||
impl<'a> System<'a> for Sys {
|
||||
type SystemData = (
|
||||
Entities<'a>,
|
||||
Read<'a, EventBus<ServerEvent>>,
|
||||
Events<'a>,
|
||||
ReadExpect<'a, CharacterLoader>,
|
||||
WriteExpect<'a, CharacterUpdater>,
|
||||
ReadStorage<'a, Uid>,
|
||||
@ -291,7 +302,7 @@ impl<'a> System<'a> for Sys {
|
||||
_job: &mut Job<Self>,
|
||||
(
|
||||
entities,
|
||||
server_event_bus,
|
||||
events,
|
||||
character_loader,
|
||||
mut character_updater,
|
||||
uids,
|
||||
@ -307,12 +318,12 @@ impl<'a> System<'a> for Sys {
|
||||
world,
|
||||
): Self::SystemData,
|
||||
) {
|
||||
let mut server_emitter = server_event_bus.emitter();
|
||||
let mut emitters = events.get_emitters();
|
||||
|
||||
for (entity, client) in (&entities, &mut clients).join() {
|
||||
let _ = super::try_recv_all(client, 1, |client, msg| {
|
||||
Self::handle_client_character_screen_msg(
|
||||
&mut server_emitter,
|
||||
&mut emitters,
|
||||
entity,
|
||||
client,
|
||||
&character_loader,
|
||||
|
@ -1,7 +1,8 @@
|
||||
use crate::client::Client;
|
||||
use common::{
|
||||
comp::{ChatMode, ChatType, Content, Group, Player},
|
||||
event::{EventBus, ServerEvent},
|
||||
event::{self, EmitExt},
|
||||
event_emitters,
|
||||
resources::ProgramTime,
|
||||
uid::Uid,
|
||||
};
|
||||
@ -11,9 +12,18 @@ use rayon::prelude::*;
|
||||
use specs::{Entities, LendJoin, ParJoin, Read, ReadStorage, WriteStorage};
|
||||
use tracing::{debug, error, warn};
|
||||
|
||||
event_emitters! {
|
||||
struct Events[Emitters] {
|
||||
command: event::CommandEvent,
|
||||
client_disconnect: event::ClientDisconnectEvent,
|
||||
chat: event::ChatEvent,
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
impl Sys {
|
||||
fn handle_general_msg(
|
||||
server_emitter: &mut common::event::Emitter<'_, ServerEvent>,
|
||||
emitters: &mut Emitters,
|
||||
entity: specs::Entity,
|
||||
client: &Client,
|
||||
player: Option<&Player>,
|
||||
@ -35,7 +45,7 @@ impl Sys {
|
||||
groups.get(entity).copied(),
|
||||
) {
|
||||
Ok(message) => {
|
||||
server_emitter.emit(ServerEvent::Chat(message));
|
||||
emitters.emit(event::ChatEvent(message));
|
||||
},
|
||||
Err(error) => {
|
||||
client.send_fallible(ServerGeneral::ChatMsg(
|
||||
@ -52,19 +62,19 @@ impl Sys {
|
||||
},
|
||||
ClientGeneral::Command(name, args) => {
|
||||
if player.is_some() {
|
||||
server_emitter.emit(ServerEvent::Command(entity, name, args));
|
||||
emitters.emit(event::CommandEvent(entity, name, args));
|
||||
}
|
||||
},
|
||||
ClientGeneral::Terminate => {
|
||||
debug!(?entity, "Client send message to terminate session");
|
||||
server_emitter.emit(ServerEvent::ClientDisconnect(
|
||||
emitters.emit(event::ClientDisconnectEvent(
|
||||
entity,
|
||||
common::comp::DisconnectReason::ClientRequested,
|
||||
));
|
||||
},
|
||||
_ => {
|
||||
debug!("Kicking possible misbehaving client due to invalid message request");
|
||||
server_emitter.emit(ServerEvent::ClientDisconnect(
|
||||
emitters.emit(event::ClientDisconnectEvent(
|
||||
entity,
|
||||
common::comp::DisconnectReason::NetworkError,
|
||||
));
|
||||
@ -80,7 +90,7 @@ pub struct Sys;
|
||||
impl<'a> System<'a> for Sys {
|
||||
type SystemData = (
|
||||
Entities<'a>,
|
||||
Read<'a, EventBus<ServerEvent>>,
|
||||
Events<'a>,
|
||||
Read<'a, ProgramTime>,
|
||||
ReadStorage<'a, Uid>,
|
||||
ReadStorage<'a, ChatMode>,
|
||||
@ -95,16 +105,16 @@ impl<'a> System<'a> for Sys {
|
||||
|
||||
fn run(
|
||||
_job: &mut Job<Self>,
|
||||
(entities, server_event_bus, program_time, uids, chat_modes, players, groups, mut clients): Self::SystemData,
|
||||
(entities, events, program_time, uids, chat_modes, players, groups, mut clients): Self::SystemData,
|
||||
) {
|
||||
(&entities, &mut clients, players.maybe())
|
||||
.par_join()
|
||||
.for_each_init(
|
||||
|| server_event_bus.emitter(),
|
||||
|server_emitter, (entity, client, player)| {
|
||||
|| events.get_emitters(),
|
||||
|emitters, (entity, client, player)| {
|
||||
let res = super::try_recv_all(client, 3, |client, msg| {
|
||||
Self::handle_general_msg(
|
||||
server_emitter,
|
||||
emitters,
|
||||
entity,
|
||||
client,
|
||||
player,
|
||||
|
@ -6,7 +6,8 @@ use common::{
|
||||
Admin, AdminRole, CanBuild, ControlEvent, Controller, ForceUpdate, Health, Ori, Player,
|
||||
Pos, Presence, PresenceKind, SkillSet, Vel,
|
||||
},
|
||||
event::{EventBus, ServerEvent},
|
||||
event::{self, EmitExt},
|
||||
event_emitters,
|
||||
link::Is,
|
||||
mounting::{Rider, VolumeRider},
|
||||
resources::{DeltaTime, PlayerPhysicsSetting, PlayerPhysicsSettings},
|
||||
@ -42,10 +43,19 @@ struct RareWrites<'a, 'b> {
|
||||
_terrain_persistence: &'b mut TerrainPersistenceData<'a>,
|
||||
}
|
||||
|
||||
event_emitters! {
|
||||
struct Events[Emitters] {
|
||||
exit_ingame: event::ExitIngameEvent,
|
||||
request_site_info: event::RequestSiteInfoEvent,
|
||||
update_map_marker: event::UpdateMapMarkerEvent,
|
||||
client_disconnect: event::ClientDisconnectEvent,
|
||||
}
|
||||
}
|
||||
|
||||
impl Sys {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn handle_client_in_game_msg(
|
||||
server_emitter: &mut common::event::Emitter<'_, ServerEvent>,
|
||||
emitters: &mut Emitters,
|
||||
entity: specs::Entity,
|
||||
client: &Client,
|
||||
maybe_presence: &mut Option<&mut Presence>,
|
||||
@ -78,7 +88,7 @@ impl Sys {
|
||||
match msg {
|
||||
// Go back to registered state (char selection screen)
|
||||
ClientGeneral::ExitInGame => {
|
||||
server_emitter.emit(ServerEvent::ExitIngame { entity });
|
||||
emitters.emit(event::ExitIngameEvent { entity });
|
||||
client.send(ServerGeneral::ExitInGameSuccess)?;
|
||||
*maybe_presence = None;
|
||||
},
|
||||
@ -212,7 +222,7 @@ impl Sys {
|
||||
.transpose();
|
||||
},
|
||||
ClientGeneral::RequestSiteInfo(id) => {
|
||||
server_emitter.emit(ServerEvent::RequestSiteInfo { entity, id });
|
||||
emitters.emit(event::RequestSiteInfoEvent { entity, id });
|
||||
},
|
||||
ClientGeneral::RequestPlayerPhysics {
|
||||
server_authoritative,
|
||||
@ -227,7 +237,7 @@ impl Sys {
|
||||
presence.lossy_terrain_compression = lossy_terrain_compression;
|
||||
},
|
||||
ClientGeneral::UpdateMapMarker(update) => {
|
||||
server_emitter.emit(ServerEvent::UpdateMapMarker { entity, update });
|
||||
emitters.emit(event::UpdateMapMarkerEvent { entity, update });
|
||||
},
|
||||
ClientGeneral::SpectatePosition(pos) => {
|
||||
if let Some(admin) = maybe_admin
|
||||
@ -251,7 +261,7 @@ impl Sys {
|
||||
| ClientGeneral::Command(..)
|
||||
| ClientGeneral::Terminate => {
|
||||
debug!("Kicking possibly misbehaving client due to invalid client in game request");
|
||||
server_emitter.emit(ServerEvent::ClientDisconnect(
|
||||
emitters.emit(event::ClientDisconnectEvent(
|
||||
entity,
|
||||
common::comp::DisconnectReason::NetworkError,
|
||||
));
|
||||
@ -268,7 +278,7 @@ impl<'a> System<'a> for Sys {
|
||||
#[allow(clippy::type_complexity)]
|
||||
type SystemData = (
|
||||
Entities<'a>,
|
||||
Read<'a, EventBus<ServerEvent>>,
|
||||
Events<'a>,
|
||||
ReadExpect<'a, TerrainGrid>,
|
||||
ReadExpect<'a, SlowJobPool>,
|
||||
ReadStorage<'a, CanBuild>,
|
||||
@ -301,7 +311,7 @@ impl<'a> System<'a> for Sys {
|
||||
_job: &mut Job<Self>,
|
||||
(
|
||||
entities,
|
||||
server_event_bus,
|
||||
events,
|
||||
terrain,
|
||||
slow_jobs,
|
||||
can_build,
|
||||
@ -353,8 +363,8 @@ impl<'a> System<'a> for Sys {
|
||||
// NOTE: Required because Specs has very poor work splitting for sparse joins.
|
||||
.par_bridge()
|
||||
.map_init(
|
||||
|| server_event_bus.emitter(),
|
||||
|server_emitter, (
|
||||
|| events.get_emitters(),
|
||||
|emitters, (
|
||||
entity,
|
||||
client,
|
||||
mut maybe_presence,
|
||||
@ -382,7 +392,7 @@ impl<'a> System<'a> for Sys {
|
||||
let mut player_physics = None;
|
||||
let _ = super::try_recv_all(client, 2, |client, msg| {
|
||||
Self::handle_client_in_game_msg(
|
||||
server_emitter,
|
||||
emitters,
|
||||
entity,
|
||||
client,
|
||||
&mut clearable_maybe_presence,
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::{client::Client, Settings};
|
||||
use common::{
|
||||
event::{EventBus, ServerEvent},
|
||||
event::{ClientDisconnectEvent, EventBus},
|
||||
resources::ProgramTime,
|
||||
};
|
||||
use common_ecs::{Job, Origin, Phase, System};
|
||||
@ -25,7 +25,7 @@ pub struct Sys;
|
||||
impl<'a> System<'a> for Sys {
|
||||
type SystemData = (
|
||||
Entities<'a>,
|
||||
Read<'a, EventBus<ServerEvent>>,
|
||||
Read<'a, EventBus<ClientDisconnectEvent>>,
|
||||
Read<'a, ProgramTime>,
|
||||
WriteStorage<'a, Client>,
|
||||
Read<'a, Settings>,
|
||||
@ -37,11 +37,11 @@ impl<'a> System<'a> for Sys {
|
||||
|
||||
fn run(
|
||||
_job: &mut Job<Self>,
|
||||
(entities, server_event_bus, program_time, mut clients, settings): Self::SystemData,
|
||||
(entities, client_disconnect, program_time, mut clients, settings): Self::SystemData,
|
||||
) {
|
||||
(&entities, &mut clients).par_join().for_each_init(
|
||||
|| server_event_bus.emitter(),
|
||||
|server_emitter, (entity, client)| {
|
||||
|| client_disconnect.emitter(),
|
||||
|client_disconnect_emitter, (entity, client)| {
|
||||
// ignore network events
|
||||
while let Some(Ok(Some(_))) =
|
||||
client.participant.as_mut().map(|p| p.try_fetch_event())
|
||||
@ -52,7 +52,7 @@ impl<'a> System<'a> for Sys {
|
||||
match res {
|
||||
Err(e) => {
|
||||
debug!(?entity, ?e, "network error with client, disconnecting");
|
||||
server_emitter.emit(ServerEvent::ClientDisconnect(
|
||||
client_disconnect_emitter.emit(ClientDisconnectEvent(
|
||||
entity,
|
||||
common::comp::DisconnectReason::NetworkError,
|
||||
));
|
||||
@ -67,7 +67,7 @@ impl<'a> System<'a> for Sys {
|
||||
// Timeout
|
||||
{
|
||||
info!(?entity, "timeout error with client, disconnecting");
|
||||
server_emitter.emit(ServerEvent::ClientDisconnect(
|
||||
client_disconnect_emitter.emit(ClientDisconnectEvent(
|
||||
entity,
|
||||
common::comp::DisconnectReason::Timeout,
|
||||
));
|
||||
|
@ -7,7 +7,7 @@ use crate::{
|
||||
};
|
||||
use common::{
|
||||
comp::{self, Admin, Player, Stats},
|
||||
event::{EventBus, ServerEvent},
|
||||
event::{ClientDisconnectEvent, EventBus, MakeAdminEvent},
|
||||
recipe::{default_component_recipe_book, default_recipe_book, default_repair_recipe_book},
|
||||
resources::TimeOfDay,
|
||||
shared_server_config::ServerConstants,
|
||||
@ -42,7 +42,8 @@ pub struct ReadData<'a> {
|
||||
entities: Entities<'a>,
|
||||
stats: ReadStorage<'a, Stats>,
|
||||
uids: ReadStorage<'a, Uid>,
|
||||
server_event_bus: Read<'a, EventBus<ServerEvent>>,
|
||||
client_disconnect_events: Read<'a, EventBus<ClientDisconnectEvent>>,
|
||||
make_admin_events: Read<'a, EventBus<MakeAdminEvent>>,
|
||||
login_provider: ReadExpect<'a, LoginProvider>,
|
||||
player_metrics: ReadExpect<'a, PlayerMetrics>,
|
||||
settings: ReadExpect<'a, Settings>,
|
||||
@ -62,7 +63,6 @@ pub struct ReadData<'a> {
|
||||
pub struct Sys;
|
||||
impl<'a> System<'a> for Sys {
|
||||
type SystemData = (
|
||||
Read<'a, EventBus<ServerEvent>>,
|
||||
ReadData<'a>,
|
||||
WriteStorage<'a, Client>,
|
||||
WriteStorage<'a, Player>,
|
||||
@ -75,8 +75,9 @@ impl<'a> System<'a> for Sys {
|
||||
|
||||
fn run(
|
||||
_job: &mut Job<Self>,
|
||||
(event_bus, read_data, mut clients, mut players, mut pending_logins): Self::SystemData,
|
||||
(read_data, mut clients, mut players, mut pending_logins): Self::SystemData,
|
||||
) {
|
||||
let mut make_admin_emitter = read_data.make_admin_events.emitter();
|
||||
// Player list to send new players, and lookup from UUID to entity (so we don't
|
||||
// have to do a linear scan over all entities on each login to see if
|
||||
// it's a duplicate).
|
||||
@ -165,8 +166,8 @@ impl<'a> System<'a> for Sys {
|
||||
// NOTE: Required because Specs has very poor work splitting for sparse joins.
|
||||
.par_bridge()
|
||||
.for_each_init(
|
||||
|| read_data.server_event_bus.emitter(),
|
||||
|server_emitter, (entity, uid, client, _, pending)| {
|
||||
|| read_data.client_disconnect_events.emitter(),
|
||||
|client_disconnect_emitter, (entity, uid, client, _, pending)| {
|
||||
prof_span!("msg::register login");
|
||||
if let Err(e) = || -> Result<(), crate::error::Error> {
|
||||
let extra_checks = |username: String, uuid: authc::Uuid| {
|
||||
@ -238,7 +239,7 @@ impl<'a> System<'a> for Sys {
|
||||
// NOTE: Done only on error to avoid doing extra work within
|
||||
// the lock.
|
||||
trace!(?e, "pending login returned error");
|
||||
server_emitter.emit(ServerEvent::ClientDisconnect(
|
||||
client_disconnect_emitter.emit(ClientDisconnectEvent(
|
||||
entity,
|
||||
common::comp::DisconnectReason::Kicked,
|
||||
));
|
||||
@ -302,7 +303,7 @@ impl<'a> System<'a> for Sys {
|
||||
);
|
||||
}
|
||||
// Remove old client
|
||||
server_emitter.emit(ServerEvent::ClientDisconnect(
|
||||
client_disconnect_emitter.emit(ClientDisconnectEvent(
|
||||
old_entity,
|
||||
common::comp::DisconnectReason::NewerLogin,
|
||||
));
|
||||
@ -408,7 +409,7 @@ impl<'a> System<'a> for Sys {
|
||||
if let Some(admin) = admin {
|
||||
// We need to defer writing to the Admin storage since it's borrowed immutably
|
||||
// by this system via TrackedStorages.
|
||||
event_bus.emit_now(ServerEvent::MakeAdmin {
|
||||
make_admin_emitter.emit(MakeAdminEvent {
|
||||
entity,
|
||||
admin: Admin(admin.role.into()),
|
||||
uuid,
|
||||
|
@ -4,7 +4,7 @@ use crate::{
|
||||
};
|
||||
use common::{
|
||||
comp::{Pos, Presence},
|
||||
event::{EventBus, ServerEvent},
|
||||
event::{ClientDisconnectEvent, EventBus},
|
||||
spiral::Spiral2d,
|
||||
terrain::{CoordinateConversions, TerrainChunkSize, TerrainGrid},
|
||||
vol::RectVolSize,
|
||||
@ -21,8 +21,8 @@ pub struct Sys;
|
||||
impl<'a> System<'a> for Sys {
|
||||
type SystemData = (
|
||||
Entities<'a>,
|
||||
Read<'a, EventBus<ServerEvent>>,
|
||||
ReadExpect<'a, EventBus<ChunkSendEntry>>,
|
||||
Read<'a, EventBus<ClientDisconnectEvent>>,
|
||||
Read<'a, EventBus<ChunkSendEntry>>,
|
||||
ReadExpect<'a, TerrainGrid>,
|
||||
ReadExpect<'a, Lod>,
|
||||
ReadExpect<'a, NetworkRequestMetrics>,
|
||||
@ -40,7 +40,7 @@ impl<'a> System<'a> for Sys {
|
||||
job: &mut Job<Self>,
|
||||
(
|
||||
entities,
|
||||
server_event_bus,
|
||||
client_disconnect_events,
|
||||
chunk_send_bus,
|
||||
terrain,
|
||||
lod,
|
||||
@ -57,8 +57,8 @@ impl<'a> System<'a> for Sys {
|
||||
// NOTE: Required because Specs has very poor work splitting for sparse joins.
|
||||
.par_bridge()
|
||||
.map_init(
|
||||
|| (chunk_send_bus.emitter(), server_event_bus.emitter()),
|
||||
|(chunk_send_emitter, server_emitter), (entity, client, maybe_presence)| {
|
||||
|| (chunk_send_bus.emitter(), client_disconnect_events.emitter()),
|
||||
|(chunk_send_emitter, client_disconnect_emitter), (entity, client, maybe_presence)| {
|
||||
let mut chunk_requests = Vec::new();
|
||||
let _ = super::try_recv_all(client, 5, |client, msg| {
|
||||
// SPECIAL CASE: LOD zone requests can be sent by non-present players
|
||||
@ -112,7 +112,7 @@ impl<'a> System<'a> for Sys {
|
||||
"Kicking possibly misbehaving client due to invalud terrain \
|
||||
request"
|
||||
);
|
||||
server_emitter.emit(ServerEvent::ClientDisconnect(
|
||||
client_disconnect_emitter.emit(ClientDisconnectEvent(
|
||||
entity,
|
||||
common::comp::DisconnectReason::NetworkError,
|
||||
));
|
||||
|
@ -2,7 +2,8 @@ use common::{
|
||||
comp::{object, Body, Object, PhysicsState, Pos, Teleporting, Vel},
|
||||
consts::TELEPORTER_RADIUS,
|
||||
effect::Effect,
|
||||
event::{EventBus, ServerEvent},
|
||||
event::{ChangeBodyEvent, DeleteEvent, EmitExt, EventBus, ExplosionEvent, ShootEvent},
|
||||
event_emitters,
|
||||
outcome::Outcome,
|
||||
resources::{DeltaTime, Time},
|
||||
CachedSpatialGrid, Damage, DamageKind, DamageSource, Explosion, RadiusEffect,
|
||||
@ -11,15 +12,24 @@ use common_ecs::{Job, Origin, Phase, System};
|
||||
use specs::{Entities, Join, LendJoin, Read, ReadStorage};
|
||||
use vek::Rgb;
|
||||
|
||||
event_emitters! {
|
||||
struct Events[Emitters] {
|
||||
delete: DeleteEvent,
|
||||
explosion: ExplosionEvent,
|
||||
shoot: ShootEvent,
|
||||
change_body: ChangeBodyEvent,
|
||||
}
|
||||
}
|
||||
|
||||
/// This system is responsible for handling misc object behaviours
|
||||
#[derive(Default)]
|
||||
pub struct Sys;
|
||||
impl<'a> System<'a> for Sys {
|
||||
type SystemData = (
|
||||
Entities<'a>,
|
||||
Events<'a>,
|
||||
Read<'a, DeltaTime>,
|
||||
Read<'a, Time>,
|
||||
Read<'a, EventBus<ServerEvent>>,
|
||||
Read<'a, EventBus<Outcome>>,
|
||||
Read<'a, CachedSpatialGrid>,
|
||||
ReadStorage<'a, Pos>,
|
||||
@ -38,9 +48,9 @@ impl<'a> System<'a> for Sys {
|
||||
_job: &mut Job<Self>,
|
||||
(
|
||||
entities,
|
||||
events,
|
||||
_dt,
|
||||
time,
|
||||
server_bus,
|
||||
outcome_bus,
|
||||
spatial_grid,
|
||||
positions,
|
||||
@ -51,7 +61,7 @@ impl<'a> System<'a> for Sys {
|
||||
teleporting,
|
||||
): Self::SystemData,
|
||||
) {
|
||||
let mut server_emitter = server_bus.emitter();
|
||||
let mut emitters = events.get_emitters();
|
||||
|
||||
// Objects
|
||||
for (entity, pos, vel, physics, object, body) in (
|
||||
@ -67,8 +77,8 @@ impl<'a> System<'a> for Sys {
|
||||
match object {
|
||||
Object::Bomb { owner } => {
|
||||
if physics.on_surface().is_some() {
|
||||
server_emitter.emit(ServerEvent::Delete(entity));
|
||||
server_emitter.emit(ServerEvent::Explosion {
|
||||
emitters.emit(DeleteEvent(entity));
|
||||
emitters.emit(ExplosionEvent {
|
||||
pos: pos.0,
|
||||
explosion: Explosion {
|
||||
effects: vec![
|
||||
@ -132,7 +142,7 @@ impl<'a> System<'a> for Sys {
|
||||
phi.sin(),
|
||||
))
|
||||
.expect("nonzero vector should normalize");
|
||||
server_emitter.emit(ServerEvent::Shoot {
|
||||
emitters.emit(ShootEvent {
|
||||
entity,
|
||||
pos: *pos,
|
||||
dir,
|
||||
@ -160,8 +170,8 @@ impl<'a> System<'a> for Sys {
|
||||
});
|
||||
}
|
||||
}
|
||||
server_emitter.emit(ServerEvent::Delete(entity));
|
||||
server_emitter.emit(ServerEvent::Explosion {
|
||||
emitters.emit(DeleteEvent(entity));
|
||||
emitters.emit(ExplosionEvent {
|
||||
pos: pos.0,
|
||||
explosion: Explosion {
|
||||
effects: vec![
|
||||
@ -186,7 +196,7 @@ impl<'a> System<'a> for Sys {
|
||||
timeout,
|
||||
} => {
|
||||
if (time.0 - spawned_at.0).max(0.0) > timeout.as_secs_f64() {
|
||||
server_emitter.emit(ServerEvent::Delete(entity));
|
||||
emitters.emit(DeleteEvent(entity));
|
||||
}
|
||||
},
|
||||
Object::Portal { .. } => {
|
||||
@ -204,7 +214,7 @@ impl<'a> System<'a> for Sys {
|
||||
});
|
||||
|
||||
if (*body == Body::Object(object::Body::PortalActive)) != is_active {
|
||||
server_bus.emit_now(ServerEvent::ChangeBody {
|
||||
emitters.emit(ChangeBodyEvent {
|
||||
entity,
|
||||
new_body: Body::Object(if is_active {
|
||||
outcome_bus.emit_now(Outcome::PortalActivated { pos: pos.0 });
|
||||
|
@ -1,7 +1,7 @@
|
||||
use common::{
|
||||
comp::{Agent, Alignment, CharacterState, Object, Pos, Teleporting},
|
||||
consts::TELEPORTER_RADIUS,
|
||||
event::{EventBus, ServerEvent},
|
||||
event::{EventBus, TeleportToPositionEvent},
|
||||
outcome::Outcome,
|
||||
resources::Time,
|
||||
uid::Uid,
|
||||
@ -33,7 +33,7 @@ impl<'a> System<'a> for Sys {
|
||||
ReadStorage<'a, CharacterState>,
|
||||
Read<'a, CachedSpatialGrid>,
|
||||
Read<'a, Time>,
|
||||
Read<'a, EventBus<ServerEvent>>,
|
||||
Read<'a, EventBus<TeleportToPositionEvent>>,
|
||||
Read<'a, EventBus<Outcome>>,
|
||||
);
|
||||
|
||||
@ -54,10 +54,12 @@ impl<'a> System<'a> for Sys {
|
||||
character_states,
|
||||
spatial_grid,
|
||||
time,
|
||||
server_bus,
|
||||
teleport_to_position_events,
|
||||
outcome_bus,
|
||||
): Self::SystemData,
|
||||
) {
|
||||
let mut teleport_to_position_emitter = teleport_to_position_events.emitter();
|
||||
let mut outcome_emitter = outcome_bus.emitter();
|
||||
let check_aggro = |entity, pos: Vec3<f32>| {
|
||||
spatial_grid
|
||||
.0
|
||||
@ -126,11 +128,11 @@ impl<'a> System<'a> for Sys {
|
||||
|
||||
for entity in nearby {
|
||||
cancel_teleporting.push(entity);
|
||||
server_bus.emit_now(ServerEvent::TeleportToPosition {
|
||||
teleport_to_position_emitter.emit(TeleportToPositionEvent {
|
||||
entity,
|
||||
position: *target,
|
||||
});
|
||||
outcome_bus.emit_now(Outcome::TeleportedByPortal { pos: *target });
|
||||
outcome_emitter.emit(Outcome::TeleportedByPortal { pos: *target });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,10 @@ use common::{
|
||||
self, agent, biped_small, bird_medium, misc::PortalData, skillset::skills,
|
||||
BehaviorCapability, ForceUpdate, Pos, Presence, Waypoint,
|
||||
},
|
||||
event::{EventBus, NpcBuilder, ServerEvent},
|
||||
event::{
|
||||
CreateNpcEvent, CreateTeleporterEvent, CreateWaypointEvent, EmitExt, EventBus, NpcBuilder,
|
||||
},
|
||||
event_emitters,
|
||||
generation::{EntityInfo, SpecialEntity},
|
||||
lottery::LootSpec,
|
||||
resources::{Time, TimeOfDay},
|
||||
@ -51,6 +54,14 @@ type RtSimData<'a> = WriteExpect<'a, rtsim::RtSim>;
|
||||
#[cfg(not(feature = "worldgen"))]
|
||||
type RtSimData<'a> = ();
|
||||
|
||||
event_emitters! {
|
||||
struct Events[Emitters] {
|
||||
create_npc: CreateNpcEvent,
|
||||
create_waypoint: CreateWaypointEvent,
|
||||
create_teleporter: CreateTeleporterEvent,
|
||||
}
|
||||
}
|
||||
|
||||
/// This system will handle loading generated chunks and unloading
|
||||
/// unneeded chunks.
|
||||
/// 1. Inserts newly generated chunks into the TerrainGrid
|
||||
@ -62,7 +73,7 @@ pub struct Sys;
|
||||
impl<'a> System<'a> for Sys {
|
||||
#[allow(clippy::type_complexity)]
|
||||
type SystemData = (
|
||||
Read<'a, EventBus<ServerEvent>>,
|
||||
Events<'a>,
|
||||
Read<'a, Tick>,
|
||||
Read<'a, Settings>,
|
||||
Read<'a, TimeOfDay>,
|
||||
@ -94,7 +105,7 @@ impl<'a> System<'a> for Sys {
|
||||
fn run(
|
||||
_job: &mut Job<Self>,
|
||||
(
|
||||
server_event_bus,
|
||||
events,
|
||||
tick,
|
||||
server_settings,
|
||||
time_of_day,
|
||||
@ -119,7 +130,7 @@ impl<'a> System<'a> for Sys {
|
||||
time,
|
||||
): Self::SystemData,
|
||||
) {
|
||||
let mut server_emitter = server_event_bus.emitter();
|
||||
let mut emitters = events.get_emitters();
|
||||
|
||||
// Generate requested chunks
|
||||
//
|
||||
@ -196,7 +207,7 @@ impl<'a> System<'a> for Sys {
|
||||
let data = NpcData::from_entity_info(entity);
|
||||
match data {
|
||||
NpcData::Waypoint(pos) => {
|
||||
server_emitter.emit(ServerEvent::CreateWaypoint(pos));
|
||||
emitters.emit(CreateWaypointEvent(pos));
|
||||
},
|
||||
NpcData::Data {
|
||||
pos,
|
||||
@ -211,7 +222,7 @@ impl<'a> System<'a> for Sys {
|
||||
scale,
|
||||
loot,
|
||||
} => {
|
||||
server_emitter.emit(ServerEvent::CreateNpc {
|
||||
emitters.emit(CreateNpcEvent {
|
||||
pos,
|
||||
ori: comp::Ori::from(Dir::random_2d(&mut rng)),
|
||||
npc: NpcBuilder::new(stats, body, alignment)
|
||||
@ -227,7 +238,7 @@ impl<'a> System<'a> for Sys {
|
||||
});
|
||||
},
|
||||
NpcData::Teleporter(pos, teleporter) => {
|
||||
server_emitter.emit(ServerEvent::CreateTeleporter(pos, teleporter));
|
||||
emitters.emit(CreateTeleporterEvent(pos, teleporter));
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
use crate::wiring::{Circuit, WiringElement};
|
||||
use common::{
|
||||
comp::{LightEmitter, PhysicsState, Pos},
|
||||
event::{EventBus, ServerEvent},
|
||||
event, event_emitters,
|
||||
resources::EntitiesDiedLastTick,
|
||||
};
|
||||
use common_ecs::{Job, Origin, Phase, System};
|
||||
@ -20,13 +20,19 @@ pub struct ReadData<'a> {
|
||||
entities_died_last_tick: Read<'a, EntitiesDiedLastTick>,
|
||||
}
|
||||
|
||||
event_emitters! {
|
||||
struct Events[Emitters] {
|
||||
shoot: event::ShootEvent,
|
||||
}
|
||||
}
|
||||
|
||||
/// This system is responsible for handling wiring (signals and wiring systems)
|
||||
#[derive(Default)]
|
||||
pub struct Sys;
|
||||
impl<'a> System<'a> for Sys {
|
||||
type SystemData = (
|
||||
ReadData<'a>,
|
||||
Read<'a, EventBus<ServerEvent>>,
|
||||
Events<'a>,
|
||||
WriteStorage<'a, WiringElement>,
|
||||
WriteStorage<'a, LightEmitter>, // maybe
|
||||
Write<'a, BlockChange>,
|
||||
@ -38,9 +44,9 @@ impl<'a> System<'a> for Sys {
|
||||
|
||||
fn run(
|
||||
_job: &mut Job<Self>,
|
||||
(read_data, event_bus, mut wiring_elements, mut light_emitters, mut block_change): Self::SystemData,
|
||||
(read_data, events, mut wiring_elements, mut light_emitters, mut block_change): Self::SystemData,
|
||||
) {
|
||||
let mut server_emitter = event_bus.emitter();
|
||||
let mut emitters = events.get_emitters();
|
||||
|
||||
// Compute the output for each wiring element by computing
|
||||
// the output for each `OutputFormula` and store each value per output per
|
||||
@ -128,7 +134,7 @@ impl<'a> System<'a> for Sys {
|
||||
&wiring_element.inputs,
|
||||
physics_state,
|
||||
&read_data.entities_died_last_tick.0,
|
||||
&mut server_emitter,
|
||||
&mut emitters,
|
||||
pos,
|
||||
&mut block_change,
|
||||
light_emitter.as_deref_mut(),
|
||||
|
@ -56,7 +56,7 @@ impl<'a> System<'a> for Sys {
|
||||
Write<'a, Option<WeatherJob>>,
|
||||
WriteExpect<'a, WeatherGrid>,
|
||||
WriteExpect<'a, SlowJobPool>,
|
||||
ReadExpect<'a, EventBus<Outcome>>,
|
||||
Read<'a, EventBus<Outcome>>,
|
||||
ReadExpect<'a, Arc<World>>,
|
||||
ReadStorage<'a, Client>,
|
||||
ReadStorage<'a, comp::Pos>,
|
||||
|
@ -1,6 +1,6 @@
|
||||
use common::{
|
||||
comp::{item::tool, object, Body, LightEmitter, PhysicsState, Pos, ProjectileConstructor},
|
||||
event::{Emitter, ServerEvent},
|
||||
event::{EmitExt, ShootEvent},
|
||||
terrain::{Block, TerrainChunkSize},
|
||||
util::Dir,
|
||||
vol::RectVolSize,
|
||||
@ -162,7 +162,7 @@ impl WiringAction {
|
||||
inputs: &HashMap<String, f32>,
|
||||
physics_state: Option<&PhysicsState>,
|
||||
entities_died_last_tick: &Vec<(Entity, Pos)>,
|
||||
server_emitter: &mut Emitter<'_, ServerEvent>,
|
||||
emitters: &mut impl EmitExt<ShootEvent>,
|
||||
pos: Option<&Pos>,
|
||||
block_change: &mut BlockChange,
|
||||
mut light_emitter: Option<&mut LightEmitter>,
|
||||
@ -183,7 +183,7 @@ impl WiringAction {
|
||||
},
|
||||
WiringActionEffect::SpawnProjectile { constr } => {
|
||||
if let Some(&pos) = pos {
|
||||
server_emitter.emit(ServerEvent::Shoot {
|
||||
emitters.emit(ShootEvent {
|
||||
entity,
|
||||
pos,
|
||||
dir: Dir::forward(),
|
||||
|
Loading…
Reference in New Issue
Block a user