mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
initial setup and convert entity_manipulation
This commit is contained in:
parent
d3a97ba34c
commit
e651b9b2ac
@ -1982,12 +1982,14 @@ impl Client {
|
|||||||
&self.connected_server_constants,
|
&self.connected_server_constants,
|
||||||
|_, _| {},
|
|_, _| {},
|
||||||
);
|
);
|
||||||
|
// TODO: TODO
|
||||||
// TODO: avoid emitting these in the first place
|
// TODO: avoid emitting these in the first place
|
||||||
let _ = self
|
// let _ = self
|
||||||
.state
|
// .state
|
||||||
.ecs()
|
// .ecs()
|
||||||
.fetch::<EventBus<common::event::ServerEvent>>()
|
// .fetch::<EventBus<common::event::ServerFrontendEvent>>()
|
||||||
.recv_all();
|
// .recv_all();
|
||||||
|
|
||||||
// TODO: avoid emitting these in the first place OR actually use outcomes
|
// TODO: avoid emitting these in the first place OR actually use outcomes
|
||||||
// generated locally on the client (if they can be deduplicated from
|
// generated locally on the client (if they can be deduplicated from
|
||||||
// ones that the server generates or if the client can reliably generate
|
// ones that the server generates or if the client can reliably generate
|
||||||
|
@ -14,7 +14,10 @@ use crate::{
|
|||||||
Alignment, Body, Buffs, CharacterState, Combo, Energy, Group, Health, HealthChange,
|
Alignment, Body, Buffs, CharacterState, Combo, Energy, Group, Health, HealthChange,
|
||||||
Inventory, Ori, Player, Poise, PoiseChange, SkillSet, Stats,
|
Inventory, Ori, Player, Poise, PoiseChange, SkillSet, Stats,
|
||||||
},
|
},
|
||||||
event::ServerEvent,
|
event::{
|
||||||
|
BuffEvent, ComboChangeEvent, EmitExt, EnergyChangeEvent, EntityAttackedHookEvent,
|
||||||
|
HealthChangeEvent, KnockbackEvent, ParryHookEvent, PoiseChangeEvent,
|
||||||
|
},
|
||||||
outcome::Outcome,
|
outcome::Outcome,
|
||||||
resources::{Secs, Time},
|
resources::{Secs, Time},
|
||||||
states::utils::StageSection,
|
states::utils::StageSection,
|
||||||
@ -144,7 +147,7 @@ impl Attack {
|
|||||||
dir: Dir,
|
dir: Dir,
|
||||||
damage: Damage,
|
damage: Damage,
|
||||||
msm: &MaterialStatManifest,
|
msm: &MaterialStatManifest,
|
||||||
mut emit: impl FnMut(ServerEvent),
|
emitters: &mut impl EmitExt<ParryHookEvent>,
|
||||||
mut emit_outcome: impl FnMut(Outcome),
|
mut emit_outcome: impl FnMut(Outcome),
|
||||||
) -> f32 {
|
) -> f32 {
|
||||||
if damage.value > 0.0 {
|
if damage.value > 0.0 {
|
||||||
@ -164,7 +167,7 @@ impl Attack {
|
|||||||
pos: target.pos,
|
pos: target.pos,
|
||||||
uid: target.uid,
|
uid: target.uid,
|
||||||
});
|
});
|
||||||
emit(ServerEvent::ParryHook {
|
emitters.emit(ParryHookEvent {
|
||||||
defender: target.entity,
|
defender: target.entity,
|
||||||
attacker: attacker.map(|a| a.entity),
|
attacker: attacker.map(|a| a.entity),
|
||||||
source,
|
source,
|
||||||
@ -203,7 +206,16 @@ impl Attack {
|
|||||||
strength_modifier: f32,
|
strength_modifier: f32,
|
||||||
attack_source: AttackSource,
|
attack_source: AttackSource,
|
||||||
time: Time,
|
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),
|
mut emit_outcome: impl FnMut(Outcome),
|
||||||
rng: &mut rand::rngs::ThreadRng,
|
rng: &mut rand::rngs::ThreadRng,
|
||||||
damage_instance_offset: u64,
|
damage_instance_offset: u64,
|
||||||
@ -255,7 +267,7 @@ impl Attack {
|
|||||||
dir,
|
dir,
|
||||||
damage.damage,
|
damage.damage,
|
||||||
msm,
|
msm,
|
||||||
&mut emit,
|
emitters,
|
||||||
&mut emit_outcome,
|
&mut emit_outcome,
|
||||||
);
|
);
|
||||||
let change = damage.damage.calculate_health_change(
|
let change = damage.damage.calculate_health_change(
|
||||||
@ -271,7 +283,7 @@ impl Attack {
|
|||||||
accumulated_damage += applied_damage;
|
accumulated_damage += applied_damage;
|
||||||
|
|
||||||
if change.amount.abs() > Health::HEALTH_EPSILON {
|
if change.amount.abs() > Health::HEALTH_EPSILON {
|
||||||
emit(ServerEvent::HealthChange {
|
emitters.emit(HealthChangeEvent {
|
||||||
entity: target.entity,
|
entity: target.entity,
|
||||||
change,
|
change,
|
||||||
});
|
});
|
||||||
@ -293,12 +305,12 @@ impl Attack {
|
|||||||
precise: precision_mult.is_some(),
|
precise: precision_mult.is_some(),
|
||||||
instance: damage_instance,
|
instance: damage_instance,
|
||||||
};
|
};
|
||||||
emit(ServerEvent::HealthChange {
|
emitters.emit(HealthChangeEvent {
|
||||||
entity: target.entity,
|
entity: target.entity,
|
||||||
change: health_change,
|
change: health_change,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
emit(ServerEvent::EnergyChange {
|
emitters.emit(EnergyChangeEvent {
|
||||||
entity: target.entity,
|
entity: target.entity,
|
||||||
change: -energy_change,
|
change: -energy_change,
|
||||||
});
|
});
|
||||||
@ -344,12 +356,12 @@ impl Attack {
|
|||||||
precise: precision_mult.is_some(),
|
precise: precision_mult.is_some(),
|
||||||
time,
|
time,
|
||||||
};
|
};
|
||||||
emit(ServerEvent::HealthChange {
|
emitters.emit(HealthChangeEvent {
|
||||||
entity: target.entity,
|
entity: target.entity,
|
||||||
change: health_change,
|
change: health_change,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
emit(ServerEvent::PoiseChange {
|
emitters.emit(PoiseChangeEvent {
|
||||||
entity: target.entity,
|
entity: target.entity,
|
||||||
change: poise_change,
|
change: poise_change,
|
||||||
});
|
});
|
||||||
@ -366,7 +378,7 @@ impl Attack {
|
|||||||
let impulse =
|
let impulse =
|
||||||
kb.calculate_impulse(dir, target.char_state) * strength_modifier;
|
kb.calculate_impulse(dir, target.char_state) * strength_modifier;
|
||||||
if !impulse.is_approx_zero() {
|
if !impulse.is_approx_zero() {
|
||||||
emit(ServerEvent::Knockback {
|
emitters.emit(KnockbackEvent {
|
||||||
entity: target.entity,
|
entity: target.entity,
|
||||||
impulse,
|
impulse,
|
||||||
});
|
});
|
||||||
@ -374,7 +386,7 @@ impl Attack {
|
|||||||
},
|
},
|
||||||
CombatEffect::EnergyReward(ec) => {
|
CombatEffect::EnergyReward(ec) => {
|
||||||
if let Some(attacker) = attacker {
|
if let Some(attacker) = attacker {
|
||||||
emit(ServerEvent::EnergyChange {
|
emitters.emit(EnergyChangeEvent {
|
||||||
entity: attacker.entity,
|
entity: attacker.entity,
|
||||||
change: *ec
|
change: *ec
|
||||||
* compute_energy_reward_mod(attacker.inventory, msm)
|
* compute_energy_reward_mod(attacker.inventory, msm)
|
||||||
@ -385,7 +397,7 @@ impl Attack {
|
|||||||
},
|
},
|
||||||
CombatEffect::Buff(b) => {
|
CombatEffect::Buff(b) => {
|
||||||
if rng.gen::<f32>() < b.chance {
|
if rng.gen::<f32>() < b.chance {
|
||||||
emit(ServerEvent::Buff {
|
emitters.emit(BuffEvent {
|
||||||
entity: target.entity,
|
entity: target.entity,
|
||||||
buff_change: BuffChange::Add(b.to_buff(
|
buff_change: BuffChange::Add(b.to_buff(
|
||||||
time,
|
time,
|
||||||
@ -409,7 +421,7 @@ impl Attack {
|
|||||||
instance: rand::random(),
|
instance: rand::random(),
|
||||||
};
|
};
|
||||||
if change.amount.abs() > Health::HEALTH_EPSILON {
|
if change.amount.abs() > Health::HEALTH_EPSILON {
|
||||||
emit(ServerEvent::HealthChange {
|
emitters.emit(HealthChangeEvent {
|
||||||
entity: attacker_entity,
|
entity: attacker_entity,
|
||||||
change,
|
change,
|
||||||
});
|
});
|
||||||
@ -435,7 +447,7 @@ impl Attack {
|
|||||||
cause: Some(damage.damage.source),
|
cause: Some(damage.damage.source),
|
||||||
time,
|
time,
|
||||||
};
|
};
|
||||||
emit(ServerEvent::PoiseChange {
|
emitters.emit(PoiseChangeEvent {
|
||||||
entity: target.entity,
|
entity: target.entity,
|
||||||
change: poise_change,
|
change: poise_change,
|
||||||
});
|
});
|
||||||
@ -451,7 +463,7 @@ impl Attack {
|
|||||||
instance: rand::random(),
|
instance: rand::random(),
|
||||||
};
|
};
|
||||||
if change.amount.abs() > Health::HEALTH_EPSILON {
|
if change.amount.abs() > Health::HEALTH_EPSILON {
|
||||||
emit(ServerEvent::HealthChange {
|
emitters.emit(HealthChangeEvent {
|
||||||
entity: target.entity,
|
entity: target.entity,
|
||||||
change,
|
change,
|
||||||
});
|
});
|
||||||
@ -460,7 +472,7 @@ impl Attack {
|
|||||||
CombatEffect::Combo(c) => {
|
CombatEffect::Combo(c) => {
|
||||||
// Not affected by strength modifier as integer
|
// Not affected by strength modifier as integer
|
||||||
if let Some(attacker_entity) = attacker.map(|a| a.entity) {
|
if let Some(attacker_entity) = attacker.map(|a| a.entity) {
|
||||||
emit(ServerEvent::ComboChange {
|
emitters.emit(ComboChangeEvent {
|
||||||
entity: attacker_entity,
|
entity: attacker_entity,
|
||||||
change: *c,
|
change: *c,
|
||||||
});
|
});
|
||||||
@ -476,7 +488,7 @@ impl Attack {
|
|||||||
change.amount *= damage;
|
change.amount *= damage;
|
||||||
change
|
change
|
||||||
};
|
};
|
||||||
emit(ServerEvent::HealthChange {
|
emitters.emit(HealthChangeEvent {
|
||||||
entity: target.entity,
|
entity: target.entity,
|
||||||
change,
|
change,
|
||||||
});
|
});
|
||||||
@ -484,7 +496,7 @@ impl Attack {
|
|||||||
},
|
},
|
||||||
CombatEffect::RefreshBuff(chance, b) => {
|
CombatEffect::RefreshBuff(chance, b) => {
|
||||||
if rng.gen::<f32>() < *chance {
|
if rng.gen::<f32>() < *chance {
|
||||||
emit(ServerEvent::Buff {
|
emitters.emit(BuffEvent {
|
||||||
entity: target.entity,
|
entity: target.entity,
|
||||||
buff_change: BuffChange::Refresh(*b),
|
buff_change: BuffChange::Refresh(*b),
|
||||||
});
|
});
|
||||||
@ -497,7 +509,7 @@ impl Attack {
|
|||||||
change.amount *= damage;
|
change.amount *= damage;
|
||||||
change
|
change
|
||||||
};
|
};
|
||||||
emit(ServerEvent::HealthChange {
|
emitters.emit(HealthChangeEvent {
|
||||||
entity: target.entity,
|
entity: target.entity,
|
||||||
change,
|
change,
|
||||||
});
|
});
|
||||||
@ -510,7 +522,7 @@ impl Attack {
|
|||||||
change.amount *= damage;
|
change.amount *= damage;
|
||||||
change
|
change
|
||||||
};
|
};
|
||||||
emit(ServerEvent::HealthChange {
|
emitters.emit(HealthChangeEvent {
|
||||||
entity: target.entity,
|
entity: target.entity,
|
||||||
change,
|
change,
|
||||||
});
|
});
|
||||||
@ -543,7 +555,7 @@ impl Attack {
|
|||||||
{
|
{
|
||||||
let sufficient_energy = e.current() >= *r;
|
let sufficient_energy = e.current() >= *r;
|
||||||
if sufficient_energy {
|
if sufficient_energy {
|
||||||
emit(ServerEvent::EnergyChange {
|
emitters.emit(EnergyChangeEvent {
|
||||||
entity,
|
entity,
|
||||||
change: -*r,
|
change: -*r,
|
||||||
});
|
});
|
||||||
@ -563,7 +575,7 @@ impl Attack {
|
|||||||
{
|
{
|
||||||
let sufficient_combo = c.counter() >= *r;
|
let sufficient_combo = c.counter() >= *r;
|
||||||
if sufficient_combo {
|
if sufficient_combo {
|
||||||
emit(ServerEvent::ComboChange {
|
emitters.emit(ComboChangeEvent {
|
||||||
entity,
|
entity,
|
||||||
change: -(*r as i32),
|
change: -(*r as i32),
|
||||||
});
|
});
|
||||||
@ -585,7 +597,7 @@ impl Attack {
|
|||||||
let impulse =
|
let impulse =
|
||||||
kb.calculate_impulse(dir, target.char_state) * strength_modifier;
|
kb.calculate_impulse(dir, target.char_state) * strength_modifier;
|
||||||
if !impulse.is_approx_zero() {
|
if !impulse.is_approx_zero() {
|
||||||
emit(ServerEvent::Knockback {
|
emitters.emit(KnockbackEvent {
|
||||||
entity: target.entity,
|
entity: target.entity,
|
||||||
impulse,
|
impulse,
|
||||||
});
|
});
|
||||||
@ -593,7 +605,7 @@ impl Attack {
|
|||||||
},
|
},
|
||||||
CombatEffect::EnergyReward(ec) => {
|
CombatEffect::EnergyReward(ec) => {
|
||||||
if let Some(attacker) = attacker {
|
if let Some(attacker) = attacker {
|
||||||
emit(ServerEvent::EnergyChange {
|
emitters.emit(EnergyChangeEvent {
|
||||||
entity: attacker.entity,
|
entity: attacker.entity,
|
||||||
change: ec
|
change: ec
|
||||||
* compute_energy_reward_mod(attacker.inventory, msm)
|
* compute_energy_reward_mod(attacker.inventory, msm)
|
||||||
@ -604,7 +616,7 @@ impl Attack {
|
|||||||
},
|
},
|
||||||
CombatEffect::Buff(b) => {
|
CombatEffect::Buff(b) => {
|
||||||
if rng.gen::<f32>() < b.chance {
|
if rng.gen::<f32>() < b.chance {
|
||||||
emit(ServerEvent::Buff {
|
emitters.emit(BuffEvent {
|
||||||
entity: target.entity,
|
entity: target.entity,
|
||||||
buff_change: BuffChange::Add(b.to_buff(
|
buff_change: BuffChange::Add(b.to_buff(
|
||||||
time,
|
time,
|
||||||
@ -628,7 +640,7 @@ impl Attack {
|
|||||||
instance: rand::random(),
|
instance: rand::random(),
|
||||||
};
|
};
|
||||||
if change.amount.abs() > Health::HEALTH_EPSILON {
|
if change.amount.abs() > Health::HEALTH_EPSILON {
|
||||||
emit(ServerEvent::HealthChange {
|
emitters.emit(HealthChangeEvent {
|
||||||
entity: attacker_entity,
|
entity: attacker_entity,
|
||||||
change,
|
change,
|
||||||
});
|
});
|
||||||
@ -654,7 +666,7 @@ impl Attack {
|
|||||||
cause: Some(attack_source.into()),
|
cause: Some(attack_source.into()),
|
||||||
time,
|
time,
|
||||||
};
|
};
|
||||||
emit(ServerEvent::PoiseChange {
|
emitters.emit(PoiseChangeEvent {
|
||||||
entity: target.entity,
|
entity: target.entity,
|
||||||
change: poise_change,
|
change: poise_change,
|
||||||
});
|
});
|
||||||
@ -670,7 +682,7 @@ impl Attack {
|
|||||||
instance: rand::random(),
|
instance: rand::random(),
|
||||||
};
|
};
|
||||||
if change.amount.abs() > Health::HEALTH_EPSILON {
|
if change.amount.abs() > Health::HEALTH_EPSILON {
|
||||||
emit(ServerEvent::HealthChange {
|
emitters.emit(HealthChangeEvent {
|
||||||
entity: target.entity,
|
entity: target.entity,
|
||||||
change,
|
change,
|
||||||
});
|
});
|
||||||
@ -679,7 +691,7 @@ impl Attack {
|
|||||||
CombatEffect::Combo(c) => {
|
CombatEffect::Combo(c) => {
|
||||||
// Not affected by strength modifier as integer
|
// Not affected by strength modifier as integer
|
||||||
if let Some(attacker_entity) = attacker.map(|a| a.entity) {
|
if let Some(attacker_entity) = attacker.map(|a| a.entity) {
|
||||||
emit(ServerEvent::ComboChange {
|
emitters.emit(ComboChangeEvent {
|
||||||
entity: attacker_entity,
|
entity: attacker_entity,
|
||||||
change: c,
|
change: c,
|
||||||
});
|
});
|
||||||
@ -689,7 +701,7 @@ impl Attack {
|
|||||||
CombatEffect::StageVulnerable(_, _) => {},
|
CombatEffect::StageVulnerable(_, _) => {},
|
||||||
CombatEffect::RefreshBuff(chance, b) => {
|
CombatEffect::RefreshBuff(chance, b) => {
|
||||||
if rng.gen::<f32>() < chance {
|
if rng.gen::<f32>() < chance {
|
||||||
emit(ServerEvent::Buff {
|
emitters.emit(BuffEvent {
|
||||||
entity: target.entity,
|
entity: target.entity,
|
||||||
buff_change: BuffChange::Refresh(b),
|
buff_change: BuffChange::Refresh(b),
|
||||||
});
|
});
|
||||||
@ -704,7 +716,7 @@ impl Attack {
|
|||||||
// Emits event to handle things that should happen for any successful 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
|
// regardless of if the attack had any damages or effects in it
|
||||||
if is_applied {
|
if is_applied {
|
||||||
emit(ServerEvent::EntityAttackedHook {
|
emitters.emit(EntityAttackedHookEvent {
|
||||||
entity: target.entity,
|
entity: target.entity,
|
||||||
attacker: attacker.map(|a| a.entity),
|
attacker: attacker.map(|a| a.entity),
|
||||||
});
|
});
|
||||||
|
@ -4,7 +4,8 @@ use crate::{
|
|||||||
ability::Capability, inventory::item::armor::Friction, item::ConsumableKind, ControlAction,
|
ability::Capability, inventory::item::armor::Friction, item::ConsumableKind, ControlAction,
|
||||||
Density, Energy, InputAttr, InputKind, Ori, Pos, Vel,
|
Density, Energy, InputAttr, InputKind, Ori, Pos, Vel,
|
||||||
},
|
},
|
||||||
event::{LocalEvent, ServerEvent},
|
event::{self, EmitExt, LocalEvent},
|
||||||
|
event_emitters,
|
||||||
resources::Time,
|
resources::Time,
|
||||||
states::{
|
states::{
|
||||||
self,
|
self,
|
||||||
@ -34,19 +35,46 @@ pub struct StateUpdate {
|
|||||||
pub character_activity: CharacterActivity,
|
pub character_activity: CharacterActivity,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct OutputEvents<'a> {
|
event_emitters! {
|
||||||
local: &'a mut Vec<LocalEvent>,
|
pub struct CharacterStateEvents[CharacterStateEventEmitters] {
|
||||||
server: &'a mut Vec<ServerEvent>,
|
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 struct OutputEvents<'a, 'b> {
|
||||||
pub fn new(local: &'a mut Vec<LocalEvent>, server: &'a mut Vec<ServerEvent>) -> Self {
|
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 }
|
Self { local, server }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn emit_local(&mut self, event: LocalEvent) { self.local.push(event); }
|
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 {
|
impl From<&JoinData<'_>> for StateUpdate {
|
||||||
|
@ -217,6 +217,13 @@ impl<G> GenericChatMsg<G> {
|
|||||||
Self { chat_type, content }
|
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> {
|
pub fn map_group<T>(self, mut f: impl FnMut(G) -> T) -> GenericChatMsg<T> {
|
||||||
let chat_type = match self.chat_type {
|
let chat_type = match self.chat_type {
|
||||||
ChatType::Online(a) => ChatType::Online(a),
|
ChatType::Online(a) => ChatType::Online(a),
|
||||||
|
@ -2,7 +2,7 @@ use crate::{comp::Alignment, uid::Uid};
|
|||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use slab::Slab;
|
use slab::Slab;
|
||||||
use specs::{Component, DerefFlaggedStorage, Join, LendJoin};
|
use specs::{storage::GenericReadStorage, Component, DerefFlaggedStorage, Join, LendJoin};
|
||||||
use tracing::{error, warn};
|
use tracing::{error, warn};
|
||||||
|
|
||||||
// Primitive group system
|
// Primitive group system
|
||||||
@ -82,7 +82,6 @@ impl<E> ChangeNotification<E> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type GroupsMut<'a> = specs::WriteStorage<'a, Group>;
|
type GroupsMut<'a> = specs::WriteStorage<'a, Group>;
|
||||||
type Groups<'a> = specs::ReadStorage<'a, Group>;
|
|
||||||
type Alignments<'a> = specs::ReadStorage<'a, Alignment>;
|
type Alignments<'a> = specs::ReadStorage<'a, Alignment>;
|
||||||
type Uids<'a> = specs::ReadStorage<'a, Uid>;
|
type Uids<'a> = specs::ReadStorage<'a, Uid>;
|
||||||
|
|
||||||
@ -98,7 +97,7 @@ fn pets(
|
|||||||
entity: specs::Entity,
|
entity: specs::Entity,
|
||||||
uid: Uid,
|
uid: Uid,
|
||||||
alignments: &Alignments,
|
alignments: &Alignments,
|
||||||
entities: &specs::Entities,
|
entities: &specs::world::EntitiesRes,
|
||||||
) -> Vec<specs::Entity> {
|
) -> Vec<specs::Entity> {
|
||||||
(entities, alignments)
|
(entities, alignments)
|
||||||
.join()
|
.join()
|
||||||
@ -112,7 +111,7 @@ fn pets(
|
|||||||
pub fn members<'a>(
|
pub fn members<'a>(
|
||||||
group: Group,
|
group: Group,
|
||||||
groups: impl Join<Type = &'a Group> + 'a,
|
groups: impl Join<Type = &'a Group> + 'a,
|
||||||
entities: &'a specs::Entities,
|
entities: &'a specs::world::EntitiesRes,
|
||||||
alignments: &'a Alignments,
|
alignments: &'a Alignments,
|
||||||
uids: &'a Uids,
|
uids: &'a Uids,
|
||||||
) -> impl Iterator<Item = (specs::Entity, Role)> + 'a {
|
) -> impl Iterator<Item = (specs::Entity, Role)> + 'a {
|
||||||
@ -331,7 +330,7 @@ impl GroupManager {
|
|||||||
groups: &mut GroupsMut,
|
groups: &mut GroupsMut,
|
||||||
alignments: &Alignments,
|
alignments: &Alignments,
|
||||||
uids: &Uids,
|
uids: &Uids,
|
||||||
entities: &specs::Entities,
|
entities: &specs::world::EntitiesRes,
|
||||||
notifier: &mut impl FnMut(specs::Entity, ChangeNotification<specs::Entity>),
|
notifier: &mut impl FnMut(specs::Entity, ChangeNotification<specs::Entity>),
|
||||||
) {
|
) {
|
||||||
self.remove_from_group(member, groups, alignments, uids, entities, notifier, true);
|
self.remove_from_group(member, groups, alignments, uids, entities, notifier, true);
|
||||||
@ -346,7 +345,7 @@ impl GroupManager {
|
|||||||
groups: &mut GroupsMut,
|
groups: &mut GroupsMut,
|
||||||
alignments: &Alignments,
|
alignments: &Alignments,
|
||||||
uids: &Uids,
|
uids: &Uids,
|
||||||
entities: &specs::Entities,
|
entities: &specs::world::EntitiesRes,
|
||||||
notifier: &mut impl FnMut(specs::Entity, ChangeNotification<specs::Entity>),
|
notifier: &mut impl FnMut(specs::Entity, ChangeNotification<specs::Entity>),
|
||||||
to_be_deleted: bool,
|
to_be_deleted: bool,
|
||||||
) {
|
) {
|
||||||
@ -493,13 +492,13 @@ impl GroupManager {
|
|||||||
|
|
||||||
// Assign new group leader
|
// Assign new group leader
|
||||||
// Does nothing if new leader is not part of a group
|
// Does nothing if new leader is not part of a group
|
||||||
pub fn assign_leader(
|
pub fn assign_leader<'a>(
|
||||||
&mut self,
|
&mut self,
|
||||||
new_leader: specs::Entity,
|
new_leader: specs::Entity,
|
||||||
groups: &Groups,
|
groups: impl GenericReadStorage<Component = Group> + Join<Type = &'a Group> + 'a,
|
||||||
entities: &specs::Entities,
|
entities: &'a specs::Entities,
|
||||||
alignments: &Alignments,
|
alignments: &'a Alignments,
|
||||||
uids: &Uids,
|
uids: &'a Uids,
|
||||||
mut notifier: impl FnMut(specs::Entity, ChangeNotification<specs::Entity>),
|
mut notifier: impl FnMut(specs::Entity, ChangeNotification<specs::Entity>),
|
||||||
) {
|
) {
|
||||||
let group = match groups.get(new_leader) {
|
let group = match groups.get(new_leader) {
|
||||||
|
@ -7,7 +7,7 @@ use crate::{
|
|||||||
dialogue::Subject,
|
dialogue::Subject,
|
||||||
invite::{InviteKind, InviteResponse},
|
invite::{InviteKind, InviteResponse},
|
||||||
misc::PortalData,
|
misc::PortalData,
|
||||||
DisconnectReason, Ori, Pos,
|
DisconnectReason, LootOwner, Ori, Pos, UnresolvedChatMsg, Vel,
|
||||||
},
|
},
|
||||||
lottery::LootSpec,
|
lottery::LootSpec,
|
||||||
mounting::VolumePos,
|
mounting::VolumePos,
|
||||||
@ -20,7 +20,7 @@ use crate::{
|
|||||||
Explosion,
|
Explosion,
|
||||||
};
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use specs::Entity as EcsEntity;
|
use specs::{Entity as EcsEntity, World};
|
||||||
use std::{collections::VecDeque, ops::DerefMut, sync::Mutex};
|
use std::{collections::VecDeque, ops::DerefMut, sync::Mutex};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
use vek::*;
|
use vek::*;
|
||||||
@ -132,217 +132,281 @@ impl NpcBuilder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::large_enum_variant)] // TODO: Pending review in #587
|
pub struct ClientConnectedEvent {
|
||||||
#[derive(strum::EnumDiscriminants)]
|
pub entity: EcsEntity,
|
||||||
#[strum_discriminants(repr(usize))]
|
}
|
||||||
#[strum_discriminants(derive(strum::EnumVariantNames))]
|
pub struct ClientDisconnectEvent(pub EcsEntity, pub DisconnectReason);
|
||||||
pub enum ServerEvent {
|
pub struct ClientDisconnectWithoutPersistenceEvent(pub EcsEntity);
|
||||||
Explosion {
|
|
||||||
pos: Vec3<f32>,
|
pub struct ChatEvent(pub UnresolvedChatMsg);
|
||||||
explosion: Explosion,
|
pub struct CommandEvent(pub EcsEntity, pub String, pub Vec<String>);
|
||||||
owner: Option<Uid>,
|
|
||||||
},
|
// Entity Creation
|
||||||
Bonk {
|
pub struct CreateWaypointEvent(pub Vec3<f32>);
|
||||||
pos: Vec3<f32>,
|
pub struct CreateTeleporterEvent(pub Vec3<f32>, pub PortalData);
|
||||||
owner: Option<Uid>,
|
|
||||||
target: Option<Uid>,
|
pub struct CreateNpcEvent {
|
||||||
},
|
pub pos: Pos,
|
||||||
HealthChange {
|
pub ori: Ori,
|
||||||
entity: EcsEntity,
|
pub npc: NpcBuilder,
|
||||||
change: comp::HealthChange,
|
pub rider: Option<NpcBuilder>,
|
||||||
},
|
}
|
||||||
PoiseChange {
|
|
||||||
entity: EcsEntity,
|
pub struct CreateShipEvent {
|
||||||
change: comp::PoiseChange,
|
pub pos: Pos,
|
||||||
},
|
pub ori: Ori,
|
||||||
Delete(EcsEntity),
|
pub ship: comp::ship::Body,
|
||||||
Destroy {
|
pub rtsim_entity: Option<RtSimEntity>,
|
||||||
entity: EcsEntity,
|
pub driver: Option<NpcBuilder>,
|
||||||
cause: comp::HealthChange,
|
}
|
||||||
},
|
|
||||||
InventoryManip(EcsEntity, comp::InventoryManip),
|
pub struct CreateItemDropEvent {
|
||||||
GroupManip(EcsEntity, comp::GroupManip),
|
pub pos: Pos,
|
||||||
Respawn(EcsEntity),
|
pub vel: Vel,
|
||||||
Shoot {
|
pub ori: Ori,
|
||||||
entity: EcsEntity,
|
pub item: comp::Item,
|
||||||
pos: Pos,
|
pub loot_owner: Option<LootOwner>,
|
||||||
dir: Dir,
|
}
|
||||||
body: comp::Body,
|
pub struct CreateObjectEvent {
|
||||||
light: Option<comp::LightEmitter>,
|
pub pos: Pos,
|
||||||
projectile: comp::Projectile,
|
pub vel: Vel,
|
||||||
speed: f32,
|
pub body: comp::object::Body,
|
||||||
object: Option<comp::Object>,
|
pub object: Option<comp::Object>,
|
||||||
},
|
pub item: Option<comp::Item>,
|
||||||
Shockwave {
|
pub light_emitter: Option<comp::LightEmitter>,
|
||||||
properties: comp::shockwave::Properties,
|
pub stats: Option<comp::Stats>,
|
||||||
pos: Pos,
|
}
|
||||||
ori: Ori,
|
|
||||||
},
|
pub struct ExplosionEvent {
|
||||||
Knockback {
|
pub pos: Vec3<f32>,
|
||||||
entity: EcsEntity,
|
pub explosion: Explosion,
|
||||||
impulse: Vec3<f32>,
|
pub owner: Option<Uid>,
|
||||||
},
|
}
|
||||||
LandOnGround {
|
|
||||||
entity: EcsEntity,
|
pub struct BonkEvent {
|
||||||
vel: Vec3<f32>,
|
pub pos: Vec3<f32>,
|
||||||
surface_normal: Vec3<f32>,
|
pub owner: Option<Uid>,
|
||||||
},
|
pub target: Option<Uid>,
|
||||||
EnableLantern(EcsEntity),
|
}
|
||||||
DisableLantern(EcsEntity),
|
|
||||||
NpcInteract(EcsEntity, EcsEntity, Subject),
|
pub struct HealthChangeEvent {
|
||||||
InviteResponse(EcsEntity, InviteResponse),
|
pub entity: EcsEntity,
|
||||||
InitiateInvite(EcsEntity, Uid, InviteKind),
|
pub change: comp::HealthChange,
|
||||||
ProcessTradeAction(EcsEntity, TradeId, TradeAction),
|
}
|
||||||
Mount(EcsEntity, EcsEntity),
|
|
||||||
MountVolume(EcsEntity, VolumePos),
|
pub struct PoiseChangeEvent {
|
||||||
Unmount(EcsEntity),
|
pub entity: EcsEntity,
|
||||||
SetPetStay(EcsEntity, EcsEntity, bool),
|
pub change: comp::PoiseChange,
|
||||||
Possess(Uid, Uid),
|
}
|
||||||
/// Inserts default components for a character when loading into the game
|
|
||||||
InitCharacterData {
|
pub struct DeleteEvent(pub EcsEntity);
|
||||||
entity: EcsEntity,
|
|
||||||
character_id: CharacterId,
|
pub struct DestroyEvent {
|
||||||
requested_view_distances: crate::ViewDistances,
|
pub entity: EcsEntity,
|
||||||
},
|
pub cause: comp::HealthChange,
|
||||||
InitSpectator(EcsEntity, crate::ViewDistances),
|
}
|
||||||
UpdateCharacterData {
|
|
||||||
entity: EcsEntity,
|
pub struct InventoryManipEvent(pub EcsEntity, pub comp::InventoryManip);
|
||||||
components: (
|
|
||||||
comp::Body,
|
pub struct GroupManipEvent(pub EcsEntity, pub comp::GroupManip);
|
||||||
comp::Stats,
|
|
||||||
comp::SkillSet,
|
pub struct RespawnEvent(pub EcsEntity);
|
||||||
comp::Inventory,
|
|
||||||
Option<comp::Waypoint>,
|
pub struct ShootEvent {
|
||||||
Vec<(comp::Pet, comp::Body, comp::Stats)>,
|
pub entity: EcsEntity,
|
||||||
comp::ActiveAbilities,
|
pub pos: Pos,
|
||||||
Option<comp::MapMarker>,
|
pub dir: Dir,
|
||||||
),
|
pub body: comp::Body,
|
||||||
metadata: UpdateCharacterMetadata,
|
pub light: Option<comp::LightEmitter>,
|
||||||
},
|
pub projectile: comp::Projectile,
|
||||||
ExitIngame {
|
pub speed: f32,
|
||||||
entity: EcsEntity,
|
pub object: Option<comp::Object>,
|
||||||
},
|
}
|
||||||
// TODO: to avoid breakage when adding new fields, perhaps have an `NpcBuilder` type?
|
|
||||||
CreateNpc {
|
pub struct ShockwaveEvent {
|
||||||
pos: Pos,
|
pub properties: comp::shockwave::Properties,
|
||||||
ori: Ori,
|
pub pos: Pos,
|
||||||
npc: NpcBuilder,
|
pub ori: Ori,
|
||||||
rider: Option<NpcBuilder>,
|
}
|
||||||
},
|
|
||||||
CreateShip {
|
pub struct KnockbackEvent {
|
||||||
pos: Pos,
|
pub entity: EcsEntity,
|
||||||
ori: Ori,
|
pub impulse: Vec3<f32>,
|
||||||
ship: comp::ship::Body,
|
}
|
||||||
rtsim_entity: Option<RtSimEntity>,
|
|
||||||
driver: Option<NpcBuilder>,
|
pub struct LandOnGroundEvent {
|
||||||
},
|
pub entity: EcsEntity,
|
||||||
CreateWaypoint(Vec3<f32>),
|
pub vel: Vec3<f32>,
|
||||||
CreateTeleporter(Vec3<f32>, PortalData),
|
pub surface_normal: Vec3<f32>,
|
||||||
ClientDisconnect(EcsEntity, DisconnectReason),
|
}
|
||||||
ClientDisconnectWithoutPersistence(EcsEntity),
|
|
||||||
Command(EcsEntity, String, Vec<String>),
|
pub struct SetLanternEvent(pub EcsEntity, pub bool);
|
||||||
/// Send a chat message to the player from an npc or other player
|
|
||||||
Chat(comp::UnresolvedChatMsg),
|
pub struct NpcInteractEvent(pub EcsEntity, pub EcsEntity, pub Subject);
|
||||||
Aura {
|
|
||||||
entity: EcsEntity,
|
pub struct InviteResponseEvent(pub EcsEntity, pub InviteResponse);
|
||||||
aura_change: comp::AuraChange,
|
|
||||||
},
|
pub struct InitiateInviteEvent(pub EcsEntity, pub Uid, pub InviteKind);
|
||||||
Buff {
|
|
||||||
entity: EcsEntity,
|
pub struct ProcessTradeActionEvent(pub EcsEntity, pub TradeId, pub TradeAction);
|
||||||
buff_change: comp::BuffChange,
|
|
||||||
},
|
pub struct MountEvent(pub EcsEntity, pub EcsEntity);
|
||||||
EnergyChange {
|
|
||||||
entity: EcsEntity,
|
pub struct MountVolumeEvent(pub EcsEntity, pub VolumePos);
|
||||||
change: f32,
|
|
||||||
},
|
pub struct UnmountEvent(pub EcsEntity);
|
||||||
ComboChange {
|
|
||||||
entity: EcsEntity,
|
pub struct SetPetStayEvent(pub EcsEntity, pub EcsEntity, pub bool);
|
||||||
change: i32,
|
|
||||||
},
|
pub struct PossessEvent(pub Uid, pub Uid);
|
||||||
ParryHook {
|
|
||||||
defender: EcsEntity,
|
pub struct InitializeCharacterEvent {
|
||||||
attacker: Option<EcsEntity>,
|
pub entity: EcsEntity,
|
||||||
source: AttackSource,
|
pub character_id: CharacterId,
|
||||||
},
|
pub requested_view_distances: crate::ViewDistances,
|
||||||
RequestSiteInfo {
|
}
|
||||||
entity: EcsEntity,
|
|
||||||
id: SiteId,
|
pub struct InitializeSpectatorEvent(pub EcsEntity, pub crate::ViewDistances);
|
||||||
},
|
|
||||||
// Attempt to mine a block, turning it into an item
|
pub struct UpdateCharacterDataEvent {
|
||||||
MineBlock {
|
pub entity: EcsEntity,
|
||||||
entity: EcsEntity,
|
pub components: (
|
||||||
pos: Vec3<i32>,
|
comp::Body,
|
||||||
tool: Option<comp::tool::ToolKind>,
|
comp::Stats,
|
||||||
},
|
comp::SkillSet,
|
||||||
TeleportTo {
|
comp::Inventory,
|
||||||
entity: EcsEntity,
|
Option<comp::Waypoint>,
|
||||||
target: Uid,
|
Vec<(comp::Pet, comp::Body, comp::Stats)>,
|
||||||
max_range: Option<f32>,
|
comp::ActiveAbilities,
|
||||||
},
|
Option<comp::MapMarker>,
|
||||||
CreateSafezone {
|
),
|
||||||
range: Option<f32>,
|
pub metadata: UpdateCharacterMetadata,
|
||||||
pos: Pos,
|
}
|
||||||
},
|
|
||||||
Sound {
|
pub struct ExitIngameEvent {
|
||||||
sound: Sound,
|
pub entity: EcsEntity,
|
||||||
},
|
}
|
||||||
CreateSprite {
|
|
||||||
pos: Vec3<i32>,
|
pub struct AuraEvent {
|
||||||
sprite: SpriteKind,
|
pub entity: EcsEntity,
|
||||||
del_timeout: Option<(f32, f32)>,
|
pub aura_change: comp::AuraChange,
|
||||||
},
|
}
|
||||||
TamePet {
|
|
||||||
pet_entity: EcsEntity,
|
pub struct BuffEvent {
|
||||||
owner_entity: EcsEntity,
|
pub entity: EcsEntity,
|
||||||
},
|
pub buff_change: comp::BuffChange,
|
||||||
EntityAttackedHook {
|
}
|
||||||
entity: EcsEntity,
|
|
||||||
attacker: Option<EcsEntity>,
|
pub struct EnergyChangeEvent {
|
||||||
},
|
pub entity: EcsEntity,
|
||||||
ChangeAbility {
|
pub change: f32,
|
||||||
entity: EcsEntity,
|
}
|
||||||
slot: usize,
|
|
||||||
auxiliary_key: comp::ability::AuxiliaryKey,
|
pub struct ComboChangeEvent {
|
||||||
new_ability: comp::ability::AuxiliaryAbility,
|
pub entity: EcsEntity,
|
||||||
},
|
pub change: i32,
|
||||||
UpdateMapMarker {
|
}
|
||||||
entity: EcsEntity,
|
|
||||||
update: comp::MapMarkerChange,
|
pub struct ParryHookEvent {
|
||||||
},
|
pub defender: EcsEntity,
|
||||||
MakeAdmin {
|
pub attacker: Option<EcsEntity>,
|
||||||
entity: EcsEntity,
|
pub source: AttackSource,
|
||||||
admin: comp::Admin,
|
}
|
||||||
uuid: Uuid,
|
|
||||||
},
|
pub struct RequestSiteInfoEvent {
|
||||||
DeleteCharacter {
|
pub entity: EcsEntity,
|
||||||
entity: EcsEntity,
|
pub id: SiteId,
|
||||||
requesting_player_uuid: String,
|
}
|
||||||
character_id: CharacterId,
|
|
||||||
},
|
// Attempt to mine a block, turning it into an item
|
||||||
ChangeStance {
|
pub struct MineBlockEvent {
|
||||||
entity: EcsEntity,
|
pub entity: EcsEntity,
|
||||||
stance: comp::Stance,
|
pub pos: Vec3<i32>,
|
||||||
},
|
pub tool: Option<comp::tool::ToolKind>,
|
||||||
ChangeBody {
|
}
|
||||||
entity: EcsEntity,
|
|
||||||
new_body: comp::Body,
|
pub struct TeleportToEvent {
|
||||||
},
|
pub entity: EcsEntity,
|
||||||
RemoveLightEmitter {
|
pub target: Uid,
|
||||||
entity: EcsEntity,
|
pub max_range: Option<f32>,
|
||||||
},
|
}
|
||||||
TeleportToPosition {
|
|
||||||
entity: EcsEntity,
|
pub struct CreateSafezoneEvent {
|
||||||
position: Vec3<f32>,
|
pub range: Option<f32>,
|
||||||
},
|
pub pos: Pos,
|
||||||
StartTeleporting {
|
}
|
||||||
entity: EcsEntity,
|
|
||||||
portal: EcsEntity,
|
pub struct SoundEvent {
|
||||||
},
|
pub sound: Sound,
|
||||||
ToggleSpriteLight {
|
}
|
||||||
entity: EcsEntity,
|
|
||||||
pos: Vec3<i32>,
|
pub struct CreateSpriteEvent {
|
||||||
enable: bool,
|
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> {
|
pub struct EventBus<E> {
|
||||||
@ -370,11 +434,15 @@ impl<E> EventBus<E> {
|
|||||||
pub fn recv_all(&self) -> impl ExactSizeIterator<Item = E> {
|
pub fn recv_all(&self) -> impl ExactSizeIterator<Item = E> {
|
||||||
std::mem::take(self.queue.lock().unwrap().deref_mut()).into_iter()
|
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> {
|
pub struct Emitter<'a, E> {
|
||||||
bus: &'a EventBus<E>,
|
bus: &'a EventBus<E>,
|
||||||
events: VecDeque<E>,
|
pub events: VecDeque<E>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, E> Emitter<'a, E> {
|
impl<'a, E> Emitter<'a, E> {
|
||||||
@ -395,3 +463,134 @@ 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:
|
||||||
|
/// ```
|
||||||
|
/// struct Foo;
|
||||||
|
/// struct Bar;
|
||||||
|
/// 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: 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.emitter()),+
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct $emitters<'a> {
|
||||||
|
$($ev_ident: $crate::event::Emitter<'a, $ty>),+
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> $emitters<'a> {
|
||||||
|
#[allow(unused)]
|
||||||
|
pub fn append(&mut self, mut other: Self) {
|
||||||
|
$(
|
||||||
|
self.$ev_ident.append(&mut other.$ev_ident.events);
|
||||||
|
)+
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$(
|
||||||
|
impl<'a> $crate::event::EmitExt<$ty> for $emitters<'a> {
|
||||||
|
fn emit(&mut self, event: $ty) { self.$ev_ident.emit(event) }
|
||||||
|
fn emit_many(&mut self, events: impl IntoIterator<Item = $ty>) { self.$ev_ident.emit_many(events) }
|
||||||
|
}
|
||||||
|
)+
|
||||||
|
)+
|
||||||
|
}
|
||||||
|
$(
|
||||||
|
$vis use event_emitters::{$read_data, $emitters};
|
||||||
|
)+
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -5,7 +5,7 @@ use crate::{
|
|||||||
character_state::OutputEvents,
|
character_state::OutputEvents,
|
||||||
CharacterState, StateUpdate,
|
CharacterState, StateUpdate,
|
||||||
},
|
},
|
||||||
event::ServerEvent,
|
event::{AuraEvent, ComboChangeEvent},
|
||||||
resources::Secs,
|
resources::Secs,
|
||||||
states::{
|
states::{
|
||||||
behavior::{CharacterBehavior, JoinData},
|
behavior::{CharacterBehavior, JoinData},
|
||||||
@ -95,12 +95,12 @@ impl CharacterBehavior for Data {
|
|||||||
(self.static_data.combo_at_cast.max(1) as f32).sqrt();
|
(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,
|
entity: data.entity,
|
||||||
change: -(self.static_data.combo_at_cast as i32),
|
change: -(self.static_data.combo_at_cast as i32),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
output_events.emit_server(ServerEvent::Aura {
|
output_events.emit_server(AuraEvent {
|
||||||
entity: data.entity,
|
entity: data.entity,
|
||||||
aura_change: AuraChange::Add(aura),
|
aura_change: AuraChange::Add(aura),
|
||||||
});
|
});
|
||||||
|
@ -5,7 +5,7 @@ use crate::{
|
|||||||
object::Body::{GrenadeClay, LaserBeam},
|
object::Body::{GrenadeClay, LaserBeam},
|
||||||
Body, CharacterState, LightEmitter, Pos, ProjectileConstructor, StateUpdate,
|
Body, CharacterState, LightEmitter, Pos, ProjectileConstructor, StateUpdate,
|
||||||
},
|
},
|
||||||
event::{LocalEvent, ServerEvent},
|
event::{LocalEvent, ShootEvent},
|
||||||
outcome::Outcome,
|
outcome::Outcome,
|
||||||
states::{
|
states::{
|
||||||
behavior::{CharacterBehavior, JoinData},
|
behavior::{CharacterBehavior, JoinData},
|
||||||
@ -127,7 +127,7 @@ impl CharacterBehavior for Data {
|
|||||||
}))
|
}))
|
||||||
.unwrap_or(data.inputs.look_dir);
|
.unwrap_or(data.inputs.look_dir);
|
||||||
// Tells server to create and shoot the projectile
|
// Tells server to create and shoot the projectile
|
||||||
output_events.emit_server(ServerEvent::Shoot {
|
output_events.emit_server(ShootEvent {
|
||||||
entity: data.entity,
|
entity: data.entity,
|
||||||
pos,
|
pos,
|
||||||
dir,
|
dir,
|
||||||
|
@ -9,7 +9,7 @@ use crate::{
|
|||||||
Body::Object,
|
Body::Object,
|
||||||
CharacterState, Projectile, StateUpdate,
|
CharacterState, Projectile, StateUpdate,
|
||||||
},
|
},
|
||||||
event::{LocalEvent, NpcBuilder, ServerEvent},
|
event::{CreateNpcEvent, LocalEvent, NpcBuilder},
|
||||||
npc::NPC_NAMES,
|
npc::NPC_NAMES,
|
||||||
outcome::Outcome,
|
outcome::Outcome,
|
||||||
skillset_builder::{self, SkillSetBuilder},
|
skillset_builder::{self, SkillSetBuilder},
|
||||||
@ -197,7 +197,7 @@ impl CharacterBehavior for Data {
|
|||||||
|
|
||||||
let mut rng = rand::thread_rng();
|
let mut rng = rand::thread_rng();
|
||||||
// Send server event to create npc
|
// Send server event to create npc
|
||||||
output_events.emit_server(ServerEvent::CreateNpc {
|
output_events.emit_server(CreateNpcEvent {
|
||||||
pos: comp::Pos(
|
pos: comp::Pos(
|
||||||
collision_vector - Vec3::unit_z() * obstacle_z + extra_height,
|
collision_vector - Vec3::unit_z() * obstacle_z + extra_height,
|
||||||
),
|
),
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
comp::{character_state::OutputEvents, CharacterState, StateUpdate},
|
comp::{character_state::OutputEvents, CharacterState, StateUpdate},
|
||||||
event::ServerEvent,
|
event::TeleportToEvent,
|
||||||
states::{
|
states::{
|
||||||
behavior::{CharacterBehavior, JoinData},
|
behavior::{CharacterBehavior, JoinData},
|
||||||
utils::*,
|
utils::*,
|
||||||
@ -52,7 +52,7 @@ impl CharacterBehavior for Data {
|
|||||||
// provided
|
// provided
|
||||||
if let Some(input_attr) = self.static_data.ability_info.input_attr {
|
if let Some(input_attr) = self.static_data.ability_info.input_attr {
|
||||||
if let Some(target) = input_attr.target_entity {
|
if let Some(target) = input_attr.target_entity {
|
||||||
output_events.emit_server(ServerEvent::TeleportTo {
|
output_events.emit_server(TeleportToEvent {
|
||||||
entity: data.entity,
|
entity: data.entity,
|
||||||
target,
|
target,
|
||||||
max_range: Some(self.static_data.max_range),
|
max_range: Some(self.static_data.max_range),
|
||||||
|
@ -4,7 +4,7 @@ use crate::{
|
|||||||
character_state::OutputEvents, projectile::ProjectileConstructor, Body, CharacterState,
|
character_state::OutputEvents, projectile::ProjectileConstructor, Body, CharacterState,
|
||||||
LightEmitter, Pos, StateUpdate,
|
LightEmitter, Pos, StateUpdate,
|
||||||
},
|
},
|
||||||
event::ServerEvent,
|
event::ShootEvent,
|
||||||
states::{
|
states::{
|
||||||
behavior::{CharacterBehavior, JoinData},
|
behavior::{CharacterBehavior, JoinData},
|
||||||
utils::*,
|
utils::*,
|
||||||
@ -123,7 +123,7 @@ impl CharacterBehavior for Data {
|
|||||||
tool_stats,
|
tool_stats,
|
||||||
self.static_data.damage_effect,
|
self.static_data.damage_effect,
|
||||||
);
|
);
|
||||||
output_events.emit_server(ServerEvent::Shoot {
|
output_events.emit_server(ShootEvent {
|
||||||
entity: data.entity,
|
entity: data.entity,
|
||||||
pos,
|
pos,
|
||||||
dir: data.inputs.look_dir,
|
dir: data.inputs.look_dir,
|
||||||
|
@ -9,7 +9,7 @@ use crate::{
|
|||||||
shockwave::{self, ShockwaveDodgeable},
|
shockwave::{self, ShockwaveDodgeable},
|
||||||
CharacterState, StateUpdate,
|
CharacterState, StateUpdate,
|
||||||
},
|
},
|
||||||
event::{LocalEvent, ServerEvent},
|
event::{ExplosionEvent, LocalEvent, ShockwaveEvent},
|
||||||
outcome::Outcome,
|
outcome::Outcome,
|
||||||
states::{
|
states::{
|
||||||
behavior::{CharacterBehavior, JoinData},
|
behavior::{CharacterBehavior, JoinData},
|
||||||
@ -182,7 +182,7 @@ impl CharacterBehavior for Data {
|
|||||||
owner: Some(*data.uid),
|
owner: Some(*data.uid),
|
||||||
specifier: self.static_data.specifier,
|
specifier: self.static_data.specifier,
|
||||||
};
|
};
|
||||||
output_events.emit_server(ServerEvent::Shockwave {
|
output_events.emit_server(ShockwaveEvent {
|
||||||
properties,
|
properties,
|
||||||
pos: *data.pos,
|
pos: *data.pos,
|
||||||
ori: *data.ori,
|
ori: *data.ori,
|
||||||
@ -214,7 +214,7 @@ impl CharacterBehavior for Data {
|
|||||||
reagent: Some(Reagent::White),
|
reagent: Some(Reagent::White),
|
||||||
min_falloff: 0.5,
|
min_falloff: 0.5,
|
||||||
};
|
};
|
||||||
output_events.emit_server(ServerEvent::Explosion {
|
output_events.emit_server(ExplosionEvent {
|
||||||
pos: data.pos.0,
|
pos: data.pos.0,
|
||||||
explosion,
|
explosion,
|
||||||
owner: Some(*data.uid),
|
owner: Some(*data.uid),
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
combat,
|
combat,
|
||||||
comp::{character_state::OutputEvents, CharacterState, MeleeConstructor, StateUpdate},
|
comp::{character_state::OutputEvents, CharacterState, MeleeConstructor, StateUpdate},
|
||||||
event::ServerEvent,
|
event::ComboChangeEvent,
|
||||||
states::{
|
states::{
|
||||||
behavior::{CharacterBehavior, JoinData},
|
behavior::{CharacterBehavior, JoinData},
|
||||||
utils::*,
|
utils::*,
|
||||||
@ -117,7 +117,7 @@ impl CharacterBehavior for Data {
|
|||||||
|
|
||||||
// Consume combo if any was required
|
// Consume combo if any was required
|
||||||
if self.static_data.minimum_combo > 0 {
|
if self.static_data.minimum_combo > 0 {
|
||||||
output_events.emit_server(ServerEvent::ComboChange {
|
output_events.emit_server(ComboChangeEvent {
|
||||||
entity: data.entity,
|
entity: data.entity,
|
||||||
change: -data.combo.map_or(0, |c| c.counter() as i32),
|
change: -data.combo.map_or(0, |c| c.counter() as i32),
|
||||||
});
|
});
|
||||||
|
@ -4,7 +4,7 @@ use crate::{
|
|||||||
character_state::OutputEvents, Body, CharacterState, LightEmitter, Pos,
|
character_state::OutputEvents, Body, CharacterState, LightEmitter, Pos,
|
||||||
ProjectileConstructor, StateUpdate,
|
ProjectileConstructor, StateUpdate,
|
||||||
},
|
},
|
||||||
event::ServerEvent,
|
event::{EnergyChangeEvent, ShootEvent},
|
||||||
states::{
|
states::{
|
||||||
behavior::{CharacterBehavior, JoinData},
|
behavior::{CharacterBehavior, JoinData},
|
||||||
utils::{StageSection, *},
|
utils::{StageSection, *},
|
||||||
@ -144,7 +144,7 @@ impl CharacterBehavior for Data {
|
|||||||
tool_stats,
|
tool_stats,
|
||||||
self.static_data.damage_effect,
|
self.static_data.damage_effect,
|
||||||
);
|
);
|
||||||
output_events.emit_server(ServerEvent::Shoot {
|
output_events.emit_server(ShootEvent {
|
||||||
entity: data.entity,
|
entity: data.entity,
|
||||||
pos,
|
pos,
|
||||||
dir: direction,
|
dir: direction,
|
||||||
@ -156,7 +156,7 @@ impl CharacterBehavior for Data {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Removes energy from character when arrow is fired
|
// Removes energy from character when arrow is fired
|
||||||
output_events.emit_server(ServerEvent::EnergyChange {
|
output_events.emit_server(EnergyChangeEvent {
|
||||||
entity: data.entity,
|
entity: data.entity,
|
||||||
change: -self.static_data.energy_cost,
|
change: -self.static_data.energy_cost,
|
||||||
});
|
});
|
||||||
|
@ -4,7 +4,7 @@ use crate::{
|
|||||||
character_state::{AttackFilters, OutputEvents},
|
character_state::{AttackFilters, OutputEvents},
|
||||||
CharacterState, StateUpdate,
|
CharacterState, StateUpdate,
|
||||||
},
|
},
|
||||||
event::ServerEvent,
|
event::BuffEvent,
|
||||||
states::{
|
states::{
|
||||||
behavior::{CharacterBehavior, JoinData},
|
behavior::{CharacterBehavior, JoinData},
|
||||||
utils::*,
|
utils::*,
|
||||||
@ -69,7 +69,7 @@ impl CharacterBehavior for Data {
|
|||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// Remove burning effect if active
|
// Remove burning effect if active
|
||||||
output_events.emit_server(ServerEvent::Buff {
|
output_events.emit_server(BuffEvent {
|
||||||
entity: data.entity,
|
entity: data.entity,
|
||||||
buff_change: BuffChange::RemoveByKind(BuffKind::Burning),
|
buff_change: BuffChange::RemoveByKind(BuffKind::Burning),
|
||||||
});
|
});
|
||||||
|
@ -4,7 +4,7 @@ use crate::{
|
|||||||
character_state::OutputEvents,
|
character_state::OutputEvents,
|
||||||
CharacterState, StateUpdate,
|
CharacterState, StateUpdate,
|
||||||
},
|
},
|
||||||
event::{LocalEvent, ServerEvent},
|
event::{BuffEvent, ComboChangeEvent, LocalEvent},
|
||||||
outcome::Outcome,
|
outcome::Outcome,
|
||||||
resources::Secs,
|
resources::Secs,
|
||||||
states::{
|
states::{
|
||||||
@ -79,7 +79,7 @@ impl CharacterBehavior for Data {
|
|||||||
} else {
|
} else {
|
||||||
self.static_data.combo_cost
|
self.static_data.combo_cost
|
||||||
};
|
};
|
||||||
output_events.emit_server(ServerEvent::ComboChange {
|
output_events.emit_server(ComboChangeEvent {
|
||||||
entity: data.entity,
|
entity: data.entity,
|
||||||
change: -(combo_consumption as i32),
|
change: -(combo_consumption as i32),
|
||||||
});
|
});
|
||||||
@ -106,7 +106,7 @@ impl CharacterBehavior for Data {
|
|||||||
if self.static_data.enforced_limit {
|
if self.static_data.enforced_limit {
|
||||||
buff_cat_ids.push(BuffCategory::SelfBuff);
|
buff_cat_ids.push(BuffCategory::SelfBuff);
|
||||||
|
|
||||||
output_events.emit_server(ServerEvent::Buff {
|
output_events.emit_server(BuffEvent {
|
||||||
entity: data.entity,
|
entity: data.entity,
|
||||||
buff_change: BuffChange::RemoveByCategory {
|
buff_change: BuffChange::RemoveByCategory {
|
||||||
all_required: vec![BuffCategory::SelfBuff],
|
all_required: vec![BuffCategory::SelfBuff],
|
||||||
@ -128,7 +128,7 @@ impl CharacterBehavior for Data {
|
|||||||
*data.time,
|
*data.time,
|
||||||
Some(data.stats),
|
Some(data.stats),
|
||||||
);
|
);
|
||||||
output_events.emit_server(ServerEvent::Buff {
|
output_events.emit_server(BuffEvent {
|
||||||
entity: data.entity,
|
entity: data.entity,
|
||||||
buff_change: BuffChange::Add(buff),
|
buff_change: BuffChange::Add(buff),
|
||||||
});
|
});
|
||||||
|
@ -8,7 +8,7 @@ use crate::{
|
|||||||
shockwave::{self, ShockwaveDodgeable},
|
shockwave::{self, ShockwaveDodgeable},
|
||||||
CharacterState, StateUpdate,
|
CharacterState, StateUpdate,
|
||||||
},
|
},
|
||||||
event::{LocalEvent, ServerEvent},
|
event::{LocalEvent, ShockwaveEvent},
|
||||||
outcome::Outcome,
|
outcome::Outcome,
|
||||||
states::{
|
states::{
|
||||||
behavior::{CharacterBehavior, JoinData},
|
behavior::{CharacterBehavior, JoinData},
|
||||||
@ -124,7 +124,7 @@ impl CharacterBehavior for Data {
|
|||||||
owner: Some(*data.uid),
|
owner: Some(*data.uid),
|
||||||
specifier: self.static_data.specifier,
|
specifier: self.static_data.specifier,
|
||||||
};
|
};
|
||||||
output_events.emit_server(ServerEvent::Shockwave {
|
output_events.emit_server(ShockwaveEvent {
|
||||||
properties,
|
properties,
|
||||||
pos: *data.pos,
|
pos: *data.pos,
|
||||||
ori: *data.ori,
|
ori: *data.ori,
|
||||||
|
@ -4,7 +4,7 @@ use crate::{
|
|||||||
character_state::OutputEvents, controller::InputKind, item::ItemDefinitionIdOwned,
|
character_state::OutputEvents, controller::InputKind, item::ItemDefinitionIdOwned,
|
||||||
slot::InvSlotId, CharacterState, InventoryManip, StateUpdate,
|
slot::InvSlotId, CharacterState, InventoryManip, StateUpdate,
|
||||||
},
|
},
|
||||||
event::{LocalEvent, ServerEvent},
|
event::{InventoryManipEvent, LocalEvent, ToggleSpriteLightEvent},
|
||||||
outcome::Outcome,
|
outcome::Outcome,
|
||||||
states::behavior::{CharacterBehavior, JoinData},
|
states::behavior::{CharacterBehavior, JoinData},
|
||||||
terrain::SpriteKind,
|
terrain::SpriteKind,
|
||||||
@ -117,17 +117,16 @@ impl CharacterBehavior for Data {
|
|||||||
sprite_pos: self.static_data.sprite_pos,
|
sprite_pos: self.static_data.sprite_pos,
|
||||||
required_item: inv_slot,
|
required_item: inv_slot,
|
||||||
};
|
};
|
||||||
|
|
||||||
match self.static_data.sprite_kind {
|
match self.static_data.sprite_kind {
|
||||||
SpriteInteractKind::ToggleLight(enable) => {
|
SpriteInteractKind::ToggleLight(enable) => {
|
||||||
output_events.emit_server(ServerEvent::ToggleSpriteLight {
|
output_events.emit_server(ToggleSpriteLightEvent {
|
||||||
entity: data.entity,
|
entity: data.entity,
|
||||||
pos: self.static_data.sprite_pos,
|
pos: self.static_data.sprite_pos,
|
||||||
enable,
|
enable,
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
_ => output_events
|
_ => 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) {
|
if matches!(self.static_data.sprite_kind, SpriteInteractKind::Unlock) {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
comp::{character_state::OutputEvents, CharacterState, StateUpdate},
|
comp::{character_state::OutputEvents, CharacterState, StateUpdate},
|
||||||
event::{LocalEvent, ServerEvent},
|
event::{CreateSpriteEvent, LocalEvent},
|
||||||
outcome::Outcome,
|
outcome::Outcome,
|
||||||
spiral::Spiral2d,
|
spiral::Spiral2d,
|
||||||
states::{
|
states::{
|
||||||
@ -173,7 +173,7 @@ impl CharacterBehavior for Data {
|
|||||||
};
|
};
|
||||||
for i in 0..layers {
|
for i in 0..layers {
|
||||||
// Send server event to create sprite
|
// 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),
|
pos: Vec3::new(sprite_pos.x, sprite_pos.y, z + i),
|
||||||
sprite: self.static_data.sprite,
|
sprite: self.static_data.sprite,
|
||||||
del_timeout: self.static_data.del_timeout,
|
del_timeout: self.static_data.del_timeout,
|
||||||
|
@ -10,7 +10,7 @@ use crate::{
|
|||||||
},
|
},
|
||||||
CharacterState, InventoryManip, StateUpdate,
|
CharacterState, InventoryManip, StateUpdate,
|
||||||
},
|
},
|
||||||
event::ServerEvent,
|
event::{BuffEvent, InventoryManipEvent},
|
||||||
states::behavior::{CharacterBehavior, JoinData},
|
states::behavior::{CharacterBehavior, JoinData},
|
||||||
};
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
@ -141,11 +141,11 @@ impl CharacterBehavior for Data {
|
|||||||
|
|
||||||
if matches!(update.character, CharacterState::Roll(_)) {
|
if matches!(update.character, CharacterState::Roll(_)) {
|
||||||
// Remove potion/saturation effect if left the use item state early by rolling
|
// 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,
|
entity: data.entity,
|
||||||
buff_change: BuffChange::RemoveByKind(BuffKind::Potion),
|
buff_change: BuffChange::RemoveByKind(BuffKind::Potion),
|
||||||
});
|
});
|
||||||
output_events.emit_server(ServerEvent::Buff {
|
output_events.emit_server(BuffEvent {
|
||||||
entity: data.entity,
|
entity: data.entity,
|
||||||
buff_change: BuffChange::RemoveByKind(BuffKind::Saturation),
|
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 {
|
if item_is_same {
|
||||||
// Create inventory manipulation event
|
// Create inventory manipulation event
|
||||||
let inv_manip = InventoryManip::Use(Slot::Inventory(state.static_data.inv_slot));
|
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,
|
StateUpdate,
|
||||||
},
|
},
|
||||||
consts::{FRIC_GROUND, GRAVITY, MAX_PICKUP_RANGE},
|
consts::{FRIC_GROUND, GRAVITY, MAX_PICKUP_RANGE},
|
||||||
event::{LocalEvent, ServerEvent},
|
event::{BuffEvent, ChangeStanceEvent, ComboChangeEvent, InventoryManipEvent, LocalEvent},
|
||||||
mounting::Volume,
|
mounting::Volume,
|
||||||
outcome::Outcome,
|
outcome::Outcome,
|
||||||
states::{behavior::JoinData, utils::CharacterState::Idle, *},
|
states::{behavior::JoinData, utils::CharacterState::Idle, *},
|
||||||
@ -1058,7 +1058,7 @@ pub fn handle_manipulate_loadout(
|
|||||||
} else {
|
} else {
|
||||||
// Else emit inventory action instantaneously
|
// Else emit inventory action instantaneously
|
||||||
let inv_manip = InventoryManip::Use(slot);
|
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) => {
|
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
|
// For inventory actions without a dedicated character state, just do action instantaneously
|
||||||
InventoryAction::Swap(equip, slot) => {
|
InventoryAction::Swap(equip, slot) => {
|
||||||
let inv_manip = InventoryManip::Swap(Slot::Equip(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) => {
|
InventoryAction::Drop(equip) => {
|
||||||
let inv_manip = InventoryManip::Drop(Slot::Equip(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 => {
|
InventoryAction::Sort => {
|
||||||
output_events.emit_server(ServerEvent::InventoryManip(
|
output_events.emit_server(InventoryManipEvent(data.entity, InventoryManip::Sort));
|
||||||
data.entity,
|
|
||||||
InventoryManip::Sort,
|
|
||||||
));
|
|
||||||
},
|
},
|
||||||
InventoryAction::Use(slot @ Slot::Equip(_)) => {
|
InventoryAction::Use(slot @ Slot::Equip(_)) => {
|
||||||
let inv_manip = InventoryManip::Use(slot);
|
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(_)) => {
|
InventoryAction::Use(Slot::Overflow(_)) => {
|
||||||
// Items in overflow slots cannot be used until moved to a real slot
|
// 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 {
|
if let Some(init_event) = ability.ability_meta().init_event {
|
||||||
match init_event {
|
match init_event {
|
||||||
AbilityInitEvent::EnterStance(stance) => {
|
AbilityInitEvent::EnterStance(stance) => {
|
||||||
output_events.emit_server(ServerEvent::ChangeStance {
|
output_events.emit_server(ChangeStanceEvent {
|
||||||
entity: data.entity,
|
entity: data.entity,
|
||||||
stance,
|
stance,
|
||||||
});
|
});
|
||||||
@ -1614,7 +1611,7 @@ impl HandInfo {
|
|||||||
|
|
||||||
pub fn leave_stance(data: &JoinData<'_>, output_events: &mut OutputEvents) {
|
pub fn leave_stance(data: &JoinData<'_>, output_events: &mut OutputEvents) {
|
||||||
if !matches!(data.stance, Some(Stance::None)) {
|
if !matches!(data.stance, Some(Stance::None)) {
|
||||||
output_events.emit_server(ServerEvent::ChangeStance {
|
output_events.emit_server(ChangeStanceEvent {
|
||||||
entity: data.entity,
|
entity: data.entity,
|
||||||
stance: Stance::None,
|
stance: Stance::None,
|
||||||
});
|
});
|
||||||
@ -1653,7 +1650,7 @@ impl ComboConsumption {
|
|||||||
Self::All => combo,
|
Self::All => combo,
|
||||||
Self::Half => (combo + 1) / 2,
|
Self::Half => (combo + 1) / 2,
|
||||||
};
|
};
|
||||||
output_events.emit_server(ServerEvent::ComboChange {
|
output_events.emit_server(ComboChangeEvent {
|
||||||
entity: data.entity,
|
entity: data.entity,
|
||||||
change: -(to_consume as i32),
|
change: -(to_consume as i32),
|
||||||
});
|
});
|
||||||
@ -1663,13 +1660,13 @@ impl ComboConsumption {
|
|||||||
fn loadout_change_hook(data: &JoinData<'_>, output_events: &mut OutputEvents, clear_combo: bool) {
|
fn loadout_change_hook(data: &JoinData<'_>, output_events: &mut OutputEvents, clear_combo: bool) {
|
||||||
if clear_combo {
|
if clear_combo {
|
||||||
// Reset combo to 0
|
// Reset combo to 0
|
||||||
output_events.emit_server(ServerEvent::ComboChange {
|
output_events.emit_server(ComboChangeEvent {
|
||||||
entity: data.entity,
|
entity: data.entity,
|
||||||
change: -data.combo.map_or(0, |c| c.counter() as i32),
|
change: -data.combo.map_or(0, |c| c.counter() as i32),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// Clear any buffs from equipped weapons
|
// Clear any buffs from equipped weapons
|
||||||
output_events.emit_server(ServerEvent::Buff {
|
output_events.emit_server(BuffEvent {
|
||||||
entity: data.entity,
|
entity: data.entity,
|
||||||
buff_change: BuffChange::RemoveByCategory {
|
buff_change: BuffChange::RemoveByCategory {
|
||||||
all_required: vec![BuffCategory::RemoveOnLoadoutChange],
|
all_required: vec![BuffCategory::RemoveOnLoadoutChange],
|
||||||
|
@ -8,4 +8,4 @@ mod special_areas;
|
|||||||
mod state;
|
mod state;
|
||||||
// TODO: breakup state module and remove glob
|
// TODO: breakup state module and remove glob
|
||||||
pub use special_areas::*;
|
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::{
|
use common::{
|
||||||
calendar::Calendar,
|
calendar::Calendar,
|
||||||
comp,
|
comp,
|
||||||
event::{EventBus, LocalEvent, ServerEvent},
|
event::{register_event_busses, EventBus, LocalEvent},
|
||||||
link::Is,
|
link::Is,
|
||||||
mounting::{Mount, Rider, VolumeRider, VolumeRiders},
|
mounting::{Mount, Rider, VolumeRider, VolumeRiders},
|
||||||
outcome::Outcome,
|
outcome::Outcome,
|
||||||
@ -331,7 +331,7 @@ impl State {
|
|||||||
ecs.insert(SlowJobPool::new(slow_limit, 10_000, thread_pool));
|
ecs.insert(SlowJobPool::new(slow_limit, 10_000, thread_pool));
|
||||||
|
|
||||||
// TODO: only register on the server
|
// TODO: only register on the server
|
||||||
ecs.insert(EventBus::<ServerEvent>::default());
|
register_event_busses(&mut ecs);
|
||||||
ecs.insert(comp::group::GroupManager::default());
|
ecs.insert(comp::group::GroupManager::default());
|
||||||
ecs.insert(SysMetrics::default());
|
ecs.insert(SysMetrics::default());
|
||||||
ecs.insert(PhysicsMetrics::default());
|
ecs.insert(PhysicsMetrics::default());
|
||||||
@ -417,6 +417,15 @@ impl State {
|
|||||||
self.ecs.read_storage().get(entity).copied()
|
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
|
/// Given mutable access to the resource R, assuming the resource
|
||||||
/// component exists (this is already the behavior of functions like `fetch`
|
/// component exists (this is already the behavior of functions like `fetch`
|
||||||
/// and `write_component_ignore_entity_dead`). Since all of our resources
|
/// and `write_component_ignore_entity_dead`). Since all of our resources
|
||||||
@ -685,9 +694,7 @@ impl State {
|
|||||||
self.dispatcher.dispatch(&self.ecs);
|
self.dispatcher.dispatch(&self.ecs);
|
||||||
drop(guard);
|
drop(guard);
|
||||||
|
|
||||||
section_span!(guard, "maintain ecs");
|
self.maintain_ecs();
|
||||||
self.ecs.maintain();
|
|
||||||
drop(guard);
|
|
||||||
|
|
||||||
if update_terrain {
|
if update_terrain {
|
||||||
self.apply_terrain_changes_internal(true, block_update);
|
self.apply_terrain_changes_internal(true, block_update);
|
||||||
@ -730,6 +737,11 @@ impl State {
|
|||||||
drop(guard);
|
drop(guard);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn maintain_ecs(&mut self) {
|
||||||
|
span!(_guard, "maintain ecs");
|
||||||
|
self.ecs.maintain();
|
||||||
|
}
|
||||||
|
|
||||||
/// Clean up the state after a tick.
|
/// Clean up the state after a tick.
|
||||||
pub fn cleanup(&mut self) {
|
pub fn cleanup(&mut self) {
|
||||||
span!(_guard, "cleanup", "State::cleanup");
|
span!(_guard, "cleanup", "State::cleanup");
|
||||||
|
@ -6,19 +6,27 @@ use common::{
|
|||||||
group::Group,
|
group::Group,
|
||||||
Alignment, Aura, Auras, BuffKind, Buffs, CharacterState, Health, Player, Pos, Stats,
|
Alignment, Aura, Auras, BuffKind, Buffs, CharacterState, Health, Player, Pos, Stats,
|
||||||
},
|
},
|
||||||
event::{Emitter, EventBus, ServerEvent},
|
event::{AuraEvent, BuffEvent, EmitExt},
|
||||||
|
event_emitters,
|
||||||
resources::Time,
|
resources::Time,
|
||||||
uid::{IdMaps, Uid},
|
uid::{IdMaps, Uid},
|
||||||
};
|
};
|
||||||
use common_ecs::{Job, Origin, Phase, System};
|
use common_ecs::{Job, Origin, Phase, System};
|
||||||
use specs::{shred, Entities, Entity as EcsEntity, Join, Read, ReadStorage, SystemData};
|
use specs::{shred, Entities, Entity as EcsEntity, Join, Read, ReadStorage, SystemData};
|
||||||
|
|
||||||
|
event_emitters! {
|
||||||
|
struct Events[Emitters] {
|
||||||
|
aura: AuraEvent,
|
||||||
|
buff: BuffEvent,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(SystemData)]
|
#[derive(SystemData)]
|
||||||
pub struct ReadData<'a> {
|
pub struct ReadData<'a> {
|
||||||
entities: Entities<'a>,
|
entities: Entities<'a>,
|
||||||
players: ReadStorage<'a, Player>,
|
players: ReadStorage<'a, Player>,
|
||||||
time: Read<'a, Time>,
|
time: Read<'a, Time>,
|
||||||
server_bus: Read<'a, EventBus<ServerEvent>>,
|
events: Events<'a>,
|
||||||
id_maps: Read<'a, IdMaps>,
|
id_maps: Read<'a, IdMaps>,
|
||||||
cached_spatial_grid: Read<'a, common::CachedSpatialGrid>,
|
cached_spatial_grid: Read<'a, common::CachedSpatialGrid>,
|
||||||
positions: ReadStorage<'a, Pos>,
|
positions: ReadStorage<'a, Pos>,
|
||||||
@ -42,7 +50,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
const PHASE: Phase = Phase::Create;
|
const PHASE: Phase = Phase::Create;
|
||||||
|
|
||||||
fn run(_job: &mut Job<Self>, read_data: Self::SystemData) {
|
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
|
// Iterate through all entities with an aura
|
||||||
for (entity, pos, auras_comp, uid) in (
|
for (entity, pos, auras_comp, uid) in (
|
||||||
@ -118,14 +126,14 @@ impl<'a> System<'a> for Sys {
|
|||||||
target_buffs,
|
target_buffs,
|
||||||
stats,
|
stats,
|
||||||
&read_data,
|
&read_data,
|
||||||
&mut server_emitter,
|
&mut emitters,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if !expired_auras.is_empty() {
|
if !expired_auras.is_empty() {
|
||||||
server_emitter.emit(ServerEvent::Aura {
|
emitters.emit(AuraEvent {
|
||||||
entity,
|
entity,
|
||||||
aura_change: AuraChange::RemoveByKey(expired_auras),
|
aura_change: AuraChange::RemoveByKey(expired_auras),
|
||||||
});
|
});
|
||||||
@ -145,7 +153,7 @@ fn activate_aura(
|
|||||||
target_buffs: &Buffs,
|
target_buffs: &Buffs,
|
||||||
stats: Option<&Stats>,
|
stats: Option<&Stats>,
|
||||||
read_data: &ReadData,
|
read_data: &ReadData,
|
||||||
server_emitter: &mut Emitter<ServerEvent>,
|
emitters: &mut impl EmitExt<BuffEvent>,
|
||||||
) {
|
) {
|
||||||
let should_activate = match aura.aura_kind {
|
let should_activate = match aura.aura_kind {
|
||||||
AuraKind::Buff { kind, source, .. } => {
|
AuraKind::Buff { kind, source, .. } => {
|
||||||
@ -223,7 +231,7 @@ fn activate_aura(
|
|||||||
&& buff.data.strength >= data.strength
|
&& buff.data.strength >= data.strength
|
||||||
});
|
});
|
||||||
if emit_buff {
|
if emit_buff {
|
||||||
server_emitter.emit(ServerEvent::Buff {
|
emitters.emit(BuffEvent {
|
||||||
entity: target,
|
entity: target,
|
||||||
buff_change: BuffChange::Add(Buff::new(
|
buff_change: BuffChange::Add(Buff::new(
|
||||||
kind,
|
kind,
|
||||||
|
@ -5,7 +5,8 @@ use common::{
|
|||||||
Alignment, Beam, Body, Buffs, CharacterState, Combo, Energy, Group, Health, Inventory, Ori,
|
Alignment, Beam, Body, Buffs, CharacterState, Combo, Energy, Group, Health, Inventory, Ori,
|
||||||
Player, Pos, Scale, Stats,
|
Player, Pos, Scale, Stats,
|
||||||
},
|
},
|
||||||
event::{EventBus, ServerEvent},
|
event::{self, EmitExt, EventBus},
|
||||||
|
event_emitters,
|
||||||
outcome::Outcome,
|
outcome::Outcome,
|
||||||
resources::{DeltaTime, Time},
|
resources::{DeltaTime, Time},
|
||||||
terrain::TerrainGrid,
|
terrain::TerrainGrid,
|
||||||
@ -21,11 +22,24 @@ use specs::{
|
|||||||
};
|
};
|
||||||
use vek::*;
|
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)]
|
#[derive(SystemData)]
|
||||||
pub struct ReadData<'a> {
|
pub struct ReadData<'a> {
|
||||||
entities: Entities<'a>,
|
entities: Entities<'a>,
|
||||||
players: ReadStorage<'a, Player>,
|
players: ReadStorage<'a, Player>,
|
||||||
server_bus: Read<'a, EventBus<ServerEvent>>,
|
|
||||||
time: Read<'a, Time>,
|
time: Read<'a, Time>,
|
||||||
dt: Read<'a, DeltaTime>,
|
dt: Read<'a, DeltaTime>,
|
||||||
terrain: ReadExpect<'a, TerrainGrid>,
|
terrain: ReadExpect<'a, TerrainGrid>,
|
||||||
@ -46,6 +60,7 @@ pub struct ReadData<'a> {
|
|||||||
character_states: ReadStorage<'a, CharacterState>,
|
character_states: ReadStorage<'a, CharacterState>,
|
||||||
buffs: ReadStorage<'a, Buffs>,
|
buffs: ReadStorage<'a, Buffs>,
|
||||||
outcomes: Read<'a, EventBus<Outcome>>,
|
outcomes: Read<'a, EventBus<Outcome>>,
|
||||||
|
events: ReadAttackEvents<'a>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This system is responsible for handling beams that heal or do damage
|
/// 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;
|
const PHASE: Phase = Phase::Create;
|
||||||
|
|
||||||
fn run(job: &mut Job<Self>, (read_data, mut beams): Self::SystemData) {
|
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();
|
let mut outcomes_emitter = read_data.outcomes.emitter();
|
||||||
|
|
||||||
(
|
(
|
||||||
@ -100,7 +114,8 @@ impl<'a> System<'a> for Sys {
|
|||||||
job.cpu_stats.measure(ParMode::Rayon);
|
job.cpu_stats.measure(ParMode::Rayon);
|
||||||
|
|
||||||
// Beams
|
// 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.entities,
|
||||||
&read_data.positions,
|
&read_data.positions,
|
||||||
&read_data.orientations,
|
&read_data.orientations,
|
||||||
@ -109,14 +124,14 @@ impl<'a> System<'a> for Sys {
|
|||||||
)
|
)
|
||||||
.par_join()
|
.par_join()
|
||||||
.fold(
|
.fold(
|
||||||
|| (Vec::new(), Vec::new(), Vec::new()),
|
|| (read_data.events.get_emitters(), Vec::new(), Vec::new()),
|
||||||
|(mut server_events, mut add_hit_entities, mut outcomes),
|
|(mut emitters, mut add_hit_entities, mut outcomes),
|
||||||
(entity, pos, ori, uid, beam)| {
|
(entity, pos, ori, uid, beam)| {
|
||||||
// Note: rayon makes it difficult to hold onto a thread-local RNG, if grabbing
|
// Note: rayon makes it difficult to hold onto a thread-local RNG, if grabbing
|
||||||
// this becomes a bottleneck we can look into alternatives.
|
// this becomes a bottleneck we can look into alternatives.
|
||||||
let mut rng = rand::thread_rng();
|
let mut rng = rand::thread_rng();
|
||||||
if rng.gen_bool(0.005) {
|
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),
|
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,
|
1.0,
|
||||||
AttackSource::Beam,
|
AttackSource::Beam,
|
||||||
*read_data.time,
|
*read_data.time,
|
||||||
|e| server_events.push(e),
|
&mut emitters,
|
||||||
|o| outcomes.push(o),
|
|o| outcomes.push(o),
|
||||||
&mut rng,
|
&mut rng,
|
||||||
0,
|
0,
|
||||||
@ -282,14 +297,14 @@ impl<'a> System<'a> for Sys {
|
|||||||
add_hit_entities.push((entity, target));
|
add_hit_entities.push((entity, target));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
(server_events, add_hit_entities, outcomes)
|
(emitters, add_hit_entities, outcomes)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.reduce(
|
.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_a, mut hit_entities_a, mut outcomes_a),
|
||||||
(mut events_b, mut hit_entities_b, mut outcomes_b)| {
|
(events_b, mut hit_entities_b, mut outcomes_b)| {
|
||||||
events_a.append(&mut events_b);
|
events_a.append(events_b);
|
||||||
hit_entities_a.append(&mut hit_entities_b);
|
hit_entities_a.append(&mut hit_entities_b);
|
||||||
outcomes_a.append(&mut outcomes_b);
|
outcomes_a.append(&mut outcomes_b);
|
||||||
(events_a, hit_entities_a, outcomes_a)
|
(events_a, hit_entities_a, outcomes_a)
|
||||||
@ -298,7 +313,6 @@ impl<'a> System<'a> for Sys {
|
|||||||
job.cpu_stats.measure(ParMode::Single);
|
job.cpu_stats.measure(ParMode::Single);
|
||||||
|
|
||||||
outcomes_emitter.emit_many(new_outcomes);
|
outcomes_emitter.emit_many(new_outcomes);
|
||||||
server_emitter.emit_many(server_events);
|
|
||||||
|
|
||||||
for (entity, hit_entity) in add_hit_entities {
|
for (entity, hit_entity) in add_hit_entities {
|
||||||
if let Some(ref mut beam) = beams.get_mut(entity) {
|
if let Some(ref mut beam) = beams.get_mut(entity) {
|
||||||
|
@ -13,7 +13,11 @@ use common::{
|
|||||||
Energy, Group, Health, HealthChange, Inventory, LightEmitter, ModifierKind, PhysicsState,
|
Energy, Group, Health, HealthChange, Inventory, LightEmitter, ModifierKind, PhysicsState,
|
||||||
Pos, Stats,
|
Pos, Stats,
|
||||||
},
|
},
|
||||||
event::{Emitter, EventBus, ServerEvent},
|
event::{
|
||||||
|
BuffEvent, ChangeBodyEvent, CreateSpriteEvent, EmitExt, EnergyChangeEvent,
|
||||||
|
HealthChangeEvent, RemoveLightEmitterEvent, SoundEvent,
|
||||||
|
},
|
||||||
|
event_emitters,
|
||||||
outcome::Outcome,
|
outcome::Outcome,
|
||||||
resources::{DeltaTime, Secs, Time},
|
resources::{DeltaTime, Secs, Time},
|
||||||
terrain::SpriteKind,
|
terrain::SpriteKind,
|
||||||
@ -29,12 +33,24 @@ use specs::{
|
|||||||
};
|
};
|
||||||
use vek::Vec3;
|
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)]
|
#[derive(SystemData)]
|
||||||
pub struct ReadData<'a> {
|
pub struct ReadData<'a> {
|
||||||
entities: Entities<'a>,
|
entities: Entities<'a>,
|
||||||
dt: Read<'a, DeltaTime>,
|
dt: Read<'a, DeltaTime>,
|
||||||
server_bus: Read<'a, EventBus<ServerEvent>>,
|
events: Events<'a>,
|
||||||
outcome_bus: Read<'a, EventBus<Outcome>>,
|
|
||||||
inventories: ReadStorage<'a, Inventory>,
|
inventories: ReadStorage<'a, Inventory>,
|
||||||
healths: ReadStorage<'a, Health>,
|
healths: ReadStorage<'a, Health>,
|
||||||
energies: ReadStorage<'a, Energy>,
|
energies: ReadStorage<'a, Energy>,
|
||||||
@ -60,8 +76,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
const PHASE: Phase = Phase::Create;
|
const PHASE: Phase = Phase::Create;
|
||||||
|
|
||||||
fn run(job: &mut Job<Self>, (read_data, mut stats): Self::SystemData) {
|
fn run(job: &mut Job<Self>, (read_data, mut stats): Self::SystemData) {
|
||||||
let mut server_emitter = read_data.server_bus.emitter();
|
let mut emitters = read_data.events.get_emitters();
|
||||||
let mut outcome = read_data.outcome_bus.emitter();
|
|
||||||
let dt = read_data.dt.0;
|
let dt = read_data.dt.0;
|
||||||
// Set to false to avoid spamming server
|
// Set to false to avoid spamming server
|
||||||
stats.set_event_emission(false);
|
stats.set_event_emission(false);
|
||||||
@ -116,11 +131,11 @@ impl<'a> System<'a> for Sys {
|
|||||||
// slower than parallel checking above
|
// slower than parallel checking above
|
||||||
for e in to_put_out_campfires {
|
for e in to_put_out_campfires {
|
||||||
{
|
{
|
||||||
server_emitter.emit(ServerEvent::ChangeBody {
|
emitters.emit(ChangeBodyEvent {
|
||||||
entity: e,
|
entity: e,
|
||||||
new_body: Body::Object(object::Body::Campfire),
|
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)
|
Some(SpriteKind::EnsnaringVines) | Some(SpriteKind::EnsnaringWeb)
|
||||||
) {
|
) {
|
||||||
// If on ensnaring vines, apply ensnared debuff
|
// If on ensnaring vines, apply ensnared debuff
|
||||||
server_emitter.emit(ServerEvent::Buff {
|
emitters.emit(BuffEvent {
|
||||||
entity,
|
entity,
|
||||||
buff_change: BuffChange::Add(Buff::new(
|
buff_change: BuffChange::Add(Buff::new(
|
||||||
BuffKind::Ensnared,
|
BuffKind::Ensnared,
|
||||||
@ -161,7 +176,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
Some(SpriteKind::SeaUrchin)
|
Some(SpriteKind::SeaUrchin)
|
||||||
) {
|
) {
|
||||||
// If touching Sea Urchin apply Bleeding buff
|
// If touching Sea Urchin apply Bleeding buff
|
||||||
server_emitter.emit(ServerEvent::Buff {
|
emitters.emit(BuffEvent {
|
||||||
entity,
|
entity,
|
||||||
buff_change: BuffChange::Add(Buff::new(
|
buff_change: BuffChange::Add(Buff::new(
|
||||||
BuffKind::Bleeding,
|
BuffKind::Bleeding,
|
||||||
@ -181,17 +196,17 @@ impl<'a> System<'a> for Sys {
|
|||||||
// TODO: Determine a better place to emit sprite change events
|
// TODO: Determine a better place to emit sprite change events
|
||||||
if let Some(pos) = read_data.positions.get(entity) {
|
if let Some(pos) = read_data.positions.get(entity) {
|
||||||
// If touching Trap - change sprite and apply Bleeding buff
|
// 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),
|
pos: Vec3::new(pos.0.x as i32, pos.0.y as i32, pos.0.z as i32 - 1),
|
||||||
sprite: SpriteKind::HaniwaTrapTriggered,
|
sprite: SpriteKind::HaniwaTrapTriggered,
|
||||||
del_timeout: Some((4.0, 1.0)),
|
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),
|
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,
|
entity,
|
||||||
buff_change: BuffChange::Add(Buff::new(
|
buff_change: BuffChange::Add(Buff::new(
|
||||||
BuffKind::Bleeding,
|
BuffKind::Bleeding,
|
||||||
@ -209,7 +224,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
Some(SpriteKind::IronSpike | SpriteKind::HaniwaTrapTriggered)
|
Some(SpriteKind::IronSpike | SpriteKind::HaniwaTrapTriggered)
|
||||||
) {
|
) {
|
||||||
// If touching Iron Spike apply Bleeding buff
|
// If touching Iron Spike apply Bleeding buff
|
||||||
server_emitter.emit(ServerEvent::Buff {
|
emitters.emit(BuffEvent {
|
||||||
entity,
|
entity,
|
||||||
buff_change: BuffChange::Add(Buff::new(
|
buff_change: BuffChange::Add(Buff::new(
|
||||||
BuffKind::Bleeding,
|
BuffKind::Bleeding,
|
||||||
@ -226,7 +241,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
Some(SpriteKind::HotSurface)
|
Some(SpriteKind::HotSurface)
|
||||||
) {
|
) {
|
||||||
// If touching a hot surface apply Burning buff
|
// If touching a hot surface apply Burning buff
|
||||||
server_emitter.emit(ServerEvent::Buff {
|
emitters.emit(BuffEvent {
|
||||||
entity,
|
entity,
|
||||||
buff_change: BuffChange::Add(Buff::new(
|
buff_change: BuffChange::Add(Buff::new(
|
||||||
BuffKind::Burning,
|
BuffKind::Burning,
|
||||||
@ -243,7 +258,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
Some(SpriteKind::IceSpike)
|
Some(SpriteKind::IceSpike)
|
||||||
) {
|
) {
|
||||||
// When standing on IceSpike, apply bleeding
|
// When standing on IceSpike, apply bleeding
|
||||||
server_emitter.emit(ServerEvent::Buff {
|
emitters.emit(BuffEvent {
|
||||||
entity,
|
entity,
|
||||||
buff_change: BuffChange::Add(Buff::new(
|
buff_change: BuffChange::Add(Buff::new(
|
||||||
BuffKind::Bleeding,
|
BuffKind::Bleeding,
|
||||||
@ -255,7 +270,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
)),
|
)),
|
||||||
});
|
});
|
||||||
// When standing on IceSpike also apply Frozen
|
// When standing on IceSpike also apply Frozen
|
||||||
server_emitter.emit(ServerEvent::Buff {
|
emitters.emit(BuffEvent {
|
||||||
entity,
|
entity,
|
||||||
buff_change: BuffChange::Add(Buff::new(
|
buff_change: BuffChange::Add(Buff::new(
|
||||||
BuffKind::Frozen,
|
BuffKind::Frozen,
|
||||||
@ -272,7 +287,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
Some(SpriteKind::FireBlock)
|
Some(SpriteKind::FireBlock)
|
||||||
) {
|
) {
|
||||||
// If on FireBlock vines, apply burning buff
|
// If on FireBlock vines, apply burning buff
|
||||||
server_emitter.emit(ServerEvent::Buff {
|
emitters.emit(BuffEvent {
|
||||||
entity,
|
entity,
|
||||||
buff_change: BuffChange::Add(Buff::new(
|
buff_change: BuffChange::Add(Buff::new(
|
||||||
BuffKind::Burning,
|
BuffKind::Burning,
|
||||||
@ -293,7 +308,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
})
|
})
|
||||||
) {
|
) {
|
||||||
// If in lava fluid, apply burning debuff
|
// If in lava fluid, apply burning debuff
|
||||||
server_emitter.emit(ServerEvent::Buff {
|
emitters.emit(BuffEvent {
|
||||||
entity,
|
entity,
|
||||||
buff_change: BuffChange::Add(Buff::new(
|
buff_change: BuffChange::Add(Buff::new(
|
||||||
BuffKind::Burning,
|
BuffKind::Burning,
|
||||||
@ -313,7 +328,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
) && buff_comp.kinds[BuffKind::Burning].is_some()
|
) && buff_comp.kinds[BuffKind::Burning].is_some()
|
||||||
{
|
{
|
||||||
// If in water fluid and currently burning, remove burning debuffs
|
// If in water fluid and currently burning, remove burning debuffs
|
||||||
server_emitter.emit(ServerEvent::Buff {
|
emitters.emit(BuffEvent {
|
||||||
entity,
|
entity,
|
||||||
buff_change: BuffChange::RemoveByKind(BuffKind::Burning),
|
buff_change: BuffChange::RemoveByKind(BuffKind::Burning),
|
||||||
});
|
});
|
||||||
@ -363,7 +378,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
};
|
};
|
||||||
if replace {
|
if replace {
|
||||||
expired_buffs.push(buff_key);
|
expired_buffs.push(buff_key);
|
||||||
server_emitter.emit(ServerEvent::Buff {
|
emitters.emit(BuffEvent {
|
||||||
entity,
|
entity,
|
||||||
buff_change: BuffChange::Add(Buff::new(
|
buff_change: BuffChange::Add(Buff::new(
|
||||||
buff.kind,
|
buff.kind,
|
||||||
@ -458,7 +473,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
energy,
|
energy,
|
||||||
entity,
|
entity,
|
||||||
buff_owner,
|
buff_owner,
|
||||||
&mut server_emitter,
|
&mut emitters,
|
||||||
dt,
|
dt,
|
||||||
*read_data.time,
|
*read_data.time,
|
||||||
expired_buffs.contains(&buff_key),
|
expired_buffs.contains(&buff_key),
|
||||||
@ -472,12 +487,12 @@ impl<'a> System<'a> for Sys {
|
|||||||
// Update body if needed.
|
// Update body if needed.
|
||||||
let new_body = body_override.unwrap_or(stat.original_body);
|
let new_body = body_override.unwrap_or(stat.original_body);
|
||||||
if new_body != *body {
|
if new_body != *body {
|
||||||
server_emitter.emit(ServerEvent::ChangeBody { entity, new_body });
|
emitters.emit(ChangeBodyEvent { entity, new_body });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove buffs that expire
|
// Remove buffs that expire
|
||||||
if !expired_buffs.is_empty() {
|
if !expired_buffs.is_empty() {
|
||||||
server_emitter.emit(ServerEvent::Buff {
|
emitters.emit(BuffEvent {
|
||||||
entity,
|
entity,
|
||||||
buff_change: BuffChange::RemoveByKey(expired_buffs),
|
buff_change: BuffChange::RemoveByKey(expired_buffs),
|
||||||
});
|
});
|
||||||
@ -485,7 +500,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
|
|
||||||
// Remove buffs that don't persist on death
|
// Remove buffs that don't persist on death
|
||||||
if health.is_dead {
|
if health.is_dead {
|
||||||
server_emitter.emit(ServerEvent::Buff {
|
emitters.emit(BuffEvent {
|
||||||
entity,
|
entity,
|
||||||
buff_change: BuffChange::RemoveByCategory {
|
buff_change: BuffChange::RemoveByCategory {
|
||||||
all_required: vec![],
|
all_required: vec![],
|
||||||
@ -515,7 +530,9 @@ fn execute_effect(
|
|||||||
energy: &Energy,
|
energy: &Energy,
|
||||||
entity: Entity,
|
entity: Entity,
|
||||||
buff_owner: Option<Uid>,
|
buff_owner: Option<Uid>,
|
||||||
server_emitter: &mut Emitter<ServerEvent>,
|
server_emitter: &mut (
|
||||||
|
impl EmitExt<HealthChangeEvent> + EmitExt<EnergyChangeEvent> + EmitExt<BuffEvent>
|
||||||
|
),
|
||||||
dt: f32,
|
dt: f32,
|
||||||
time: Time,
|
time: Time,
|
||||||
buff_will_expire: bool,
|
buff_will_expire: bool,
|
||||||
@ -577,7 +594,7 @@ fn execute_effect(
|
|||||||
DamageContributor::new(uid, read_data.groups.get(entity).cloned())
|
DamageContributor::new(uid, read_data.groups.get(entity).cloned())
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
server_emitter.emit(ServerEvent::HealthChange {
|
server_emitter.emit(HealthChangeEvent {
|
||||||
entity,
|
entity,
|
||||||
change: HealthChange {
|
change: HealthChange {
|
||||||
amount,
|
amount,
|
||||||
@ -602,7 +619,7 @@ fn execute_effect(
|
|||||||
ModifierKind::Additive => amount,
|
ModifierKind::Additive => amount,
|
||||||
ModifierKind::Multiplicative => energy.maximum() * amount,
|
ModifierKind::Multiplicative => energy.maximum() * amount,
|
||||||
};
|
};
|
||||||
server_emitter.emit(ServerEvent::EnergyChange {
|
server_emitter.emit(EnergyChangeEvent {
|
||||||
entity,
|
entity,
|
||||||
change: amount,
|
change: amount,
|
||||||
});
|
});
|
||||||
@ -718,7 +735,7 @@ fn execute_effect(
|
|||||||
},
|
},
|
||||||
BuffEffect::BuffImmunity(buff_kind) => {
|
BuffEffect::BuffImmunity(buff_kind) => {
|
||||||
if buffs_comp.contains(*buff_kind) {
|
if buffs_comp.contains(*buff_kind) {
|
||||||
server_emitter.emit(ServerEvent::Buff {
|
server_emitter.emit(BuffEvent {
|
||||||
entity,
|
entity,
|
||||||
buff_change: BuffChange::RemoveByKind(*buff_kind),
|
buff_change: BuffChange::RemoveByKind(*buff_kind),
|
||||||
});
|
});
|
||||||
|
@ -5,13 +5,13 @@ use specs::{
|
|||||||
use common::{
|
use common::{
|
||||||
comp::{
|
comp::{
|
||||||
self,
|
self,
|
||||||
character_state::OutputEvents,
|
character_state::{CharacterStateEvents, OutputEvents},
|
||||||
inventory::item::{tool::AbilityMap, MaterialStatManifest},
|
inventory::item::{tool::AbilityMap, MaterialStatManifest},
|
||||||
ActiveAbilities, Beam, Body, CharacterActivity, CharacterState, Combo, Controller, Density,
|
ActiveAbilities, Beam, Body, CharacterActivity, CharacterState, Combo, Controller, Density,
|
||||||
Energy, Health, Inventory, InventoryManip, Mass, Melee, Ori, PhysicsState, Poise, Pos,
|
Energy, Health, Inventory, InventoryManip, Mass, Melee, Ori, PhysicsState, Poise, Pos,
|
||||||
Scale, SkillSet, Stance, StateUpdate, Stats, Vel,
|
Scale, SkillSet, Stance, StateUpdate, Stats, Vel,
|
||||||
},
|
},
|
||||||
event::{EventBus, LocalEvent, ServerEvent},
|
event::{self, EventBus, KnockbackEvent, LocalEvent},
|
||||||
link::Is,
|
link::Is,
|
||||||
mounting::{Rider, VolumeRider},
|
mounting::{Rider, VolumeRider},
|
||||||
outcome::Outcome,
|
outcome::Outcome,
|
||||||
@ -28,7 +28,7 @@ use common_ecs::{Job, Origin, Phase, System};
|
|||||||
#[derive(SystemData)]
|
#[derive(SystemData)]
|
||||||
pub struct ReadData<'a> {
|
pub struct ReadData<'a> {
|
||||||
entities: Entities<'a>,
|
entities: Entities<'a>,
|
||||||
server_bus: Read<'a, EventBus<ServerEvent>>,
|
events: CharacterStateEvents<'a>,
|
||||||
local_bus: Read<'a, EventBus<LocalEvent>>,
|
local_bus: Read<'a, EventBus<LocalEvent>>,
|
||||||
dt: Read<'a, DeltaTime>,
|
dt: Read<'a, DeltaTime>,
|
||||||
time: Read<'a, Time>,
|
time: Read<'a, Time>,
|
||||||
@ -96,13 +96,12 @@ impl<'a> System<'a> for Sys {
|
|||||||
outcomes,
|
outcomes,
|
||||||
): Self::SystemData,
|
): Self::SystemData,
|
||||||
) {
|
) {
|
||||||
let mut server_emitter = read_data.server_bus.emitter();
|
|
||||||
let mut local_emitter = read_data.local_bus.emitter();
|
let mut local_emitter = read_data.local_bus.emitter();
|
||||||
let mut outcomes_emitter = outcomes.emitter();
|
let mut outcomes_emitter = outcomes.emitter();
|
||||||
|
let mut emitters = read_data.events.get_emitters();
|
||||||
|
|
||||||
let mut local_events = Vec::new();
|
let mut local_events = Vec::new();
|
||||||
let mut server_events = Vec::new();
|
let mut output_events = OutputEvents::new(&mut local_events, &mut emitters);
|
||||||
let mut output_events = OutputEvents::new(&mut local_events, &mut server_events);
|
|
||||||
|
|
||||||
let join = (
|
let join = (
|
||||||
&read_data.entities,
|
&read_data.entities,
|
||||||
@ -179,7 +178,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
state: poise_state,
|
state: poise_state,
|
||||||
});
|
});
|
||||||
if let Some(impulse_strength) = impulse_strength {
|
if let Some(impulse_strength) = impulse_strength {
|
||||||
server_emitter.emit(ServerEvent::Knockback {
|
output_events.emit_server(KnockbackEvent {
|
||||||
entity,
|
entity,
|
||||||
impulse: impulse_strength * *poise.knockback(),
|
impulse: impulse_strength * *poise.knockback(),
|
||||||
});
|
});
|
||||||
@ -255,7 +254,6 @@ impl<'a> System<'a> for Sys {
|
|||||||
});
|
});
|
||||||
|
|
||||||
local_emitter.append_vec(local_events);
|
local_emitter.append_vec(local_events);
|
||||||
server_emitter.append_vec(server_events);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -297,7 +295,7 @@ impl Sys {
|
|||||||
join.controller.queued_inputs.remove(&input);
|
join.controller.queued_inputs.remove(&input);
|
||||||
}
|
}
|
||||||
if state_update.swap_equipped_weapons {
|
if state_update.swap_equipped_weapons {
|
||||||
output_events.emit_server(ServerEvent::InventoryManip(
|
output_events.emit_server(event::InventoryManipEvent(
|
||||||
join.entity,
|
join.entity,
|
||||||
InventoryManip::SwapEquippedWeapons,
|
InventoryManip::SwapEquippedWeapons,
|
||||||
));
|
));
|
||||||
|
@ -4,7 +4,8 @@ use common::{
|
|||||||
agent::{Sound, SoundKind},
|
agent::{Sound, SoundKind},
|
||||||
Body, BuffChange, Collider, ControlEvent, Controller, Pos, Scale,
|
Body, BuffChange, Collider, ControlEvent, Controller, Pos, Scale,
|
||||||
},
|
},
|
||||||
event::{EventBus, ServerEvent},
|
event::{self, EmitExt},
|
||||||
|
event_emitters,
|
||||||
terrain::TerrainGrid,
|
terrain::TerrainGrid,
|
||||||
uid::IdMaps,
|
uid::IdMaps,
|
||||||
};
|
};
|
||||||
@ -12,11 +13,33 @@ use common_ecs::{Job, Origin, Phase, System};
|
|||||||
use specs::{shred, Entities, Join, Read, ReadExpect, ReadStorage, SystemData, WriteStorage};
|
use specs::{shred, Entities, Join, Read, ReadExpect, ReadStorage, SystemData, WriteStorage};
|
||||||
use vek::*;
|
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)]
|
#[derive(SystemData)]
|
||||||
pub struct ReadData<'a> {
|
pub struct ReadData<'a> {
|
||||||
entities: Entities<'a>,
|
entities: Entities<'a>,
|
||||||
id_maps: Read<'a, IdMaps>,
|
id_maps: Read<'a, IdMaps>,
|
||||||
server_bus: Read<'a, EventBus<ServerEvent>>,
|
events: Events<'a>,
|
||||||
terrain_grid: ReadExpect<'a, TerrainGrid>,
|
terrain_grid: ReadExpect<'a, TerrainGrid>,
|
||||||
positions: ReadStorage<'a, Pos>,
|
positions: ReadStorage<'a, Pos>,
|
||||||
bodies: ReadStorage<'a, Body>,
|
bodies: ReadStorage<'a, Body>,
|
||||||
@ -35,7 +58,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
const PHASE: Phase = Phase::Create;
|
const PHASE: Phase = Phase::Create;
|
||||||
|
|
||||||
fn run(_job: &mut Job<Self>, (read_data, mut controllers): Self::SystemData) {
|
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() {
|
for (entity, controller) in (&read_data.entities, &mut controllers).join() {
|
||||||
// Sanitize inputs to avoid clients sending bad data
|
// Sanitize inputs to avoid clients sending bad data
|
||||||
@ -46,7 +69,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
match event {
|
match event {
|
||||||
ControlEvent::Mount(mountee_uid) => {
|
ControlEvent::Mount(mountee_uid) => {
|
||||||
if let Some(mountee_entity) = read_data.id_maps.uid_entity(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) => {
|
ControlEvent::MountVolume(volume) => {
|
||||||
@ -56,51 +79,49 @@ impl<'a> System<'a> for Sys {
|
|||||||
&read_data.colliders,
|
&read_data.colliders,
|
||||||
) {
|
) {
|
||||||
if block.is_mountable() {
|
if block.is_mountable() {
|
||||||
server_emitter.emit(ServerEvent::MountVolume(entity, volume));
|
emitters.emit(event::MountVolumeEvent(entity, volume));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ControlEvent::SetPetStay(pet_uid, stay) => {
|
ControlEvent::SetPetStay(pet_uid, stay) => {
|
||||||
if let Some(pet_entity) = read_data.id_maps.uid_entity(pet_uid) {
|
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) => {
|
ControlEvent::RemoveBuff(buff_id) => {
|
||||||
server_emitter.emit(ServerEvent::Buff {
|
emitters.emit(event::BuffEvent {
|
||||||
entity,
|
entity,
|
||||||
buff_change: BuffChange::RemoveFromController(buff_id),
|
buff_change: BuffChange::RemoveFromController(buff_id),
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
ControlEvent::Unmount => server_emitter.emit(ServerEvent::Unmount(entity)),
|
ControlEvent::Unmount => emitters.emit(event::UnmountEvent(entity)),
|
||||||
ControlEvent::EnableLantern => {
|
ControlEvent::EnableLantern => {
|
||||||
server_emitter.emit(ServerEvent::EnableLantern(entity))
|
emitters.emit(event::SetLanternEvent(entity, true))
|
||||||
},
|
},
|
||||||
ControlEvent::DisableLantern => {
|
ControlEvent::DisableLantern => {
|
||||||
server_emitter.emit(ServerEvent::DisableLantern(entity))
|
emitters.emit(event::SetLanternEvent(entity, false))
|
||||||
},
|
},
|
||||||
ControlEvent::Interact(npc_uid, subject) => {
|
ControlEvent::Interact(npc_uid, subject) => {
|
||||||
if let Some(npc_entity) = read_data.id_maps.uid_entity(npc_uid) {
|
if let Some(npc_entity) = read_data.id_maps.uid_entity(npc_uid) {
|
||||||
server_emitter
|
emitters.emit(event::NpcInteractEvent(entity, npc_entity, subject));
|
||||||
.emit(ServerEvent::NpcInteract(entity, npc_entity, subject));
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ControlEvent::InitiateInvite(inviter_uid, kind) => {
|
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) => {
|
ControlEvent::InviteResponse(response) => {
|
||||||
server_emitter.emit(ServerEvent::InviteResponse(entity, response));
|
emitters.emit(event::InviteResponseEvent(entity, response));
|
||||||
},
|
},
|
||||||
ControlEvent::PerformTradeAction(trade_id, action) => {
|
ControlEvent::PerformTradeAction(trade_id, action) => {
|
||||||
server_emitter
|
emitters.emit(event::ProcessTradeActionEvent(entity, trade_id, action));
|
||||||
.emit(ServerEvent::ProcessTradeAction(entity, trade_id, action));
|
|
||||||
},
|
},
|
||||||
ControlEvent::InventoryEvent(event) => {
|
ControlEvent::InventoryEvent(event) => {
|
||||||
server_emitter.emit(ServerEvent::InventoryManip(entity, event.into()));
|
emitters.emit(event::InventoryManipEvent(entity, event.into()));
|
||||||
},
|
},
|
||||||
ControlEvent::GroupManip(manip) => {
|
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) => {
|
ControlEvent::Utterance(kind) => {
|
||||||
if let (Some(pos), Some(body), scale) = (
|
if let (Some(pos), Some(body), scale) = (
|
||||||
read_data.positions.get(entity),
|
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
|
8.0, // TODO: Come up with a better way of determining this
|
||||||
1.0,
|
1.0,
|
||||||
);
|
);
|
||||||
server_emitter.emit(ServerEvent::Sound { sound });
|
emitters.emit(event::SoundEvent { sound });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ControlEvent::ChangeAbility {
|
ControlEvent::ChangeAbility {
|
||||||
@ -122,7 +143,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
auxiliary_key,
|
auxiliary_key,
|
||||||
new_ability,
|
new_ability,
|
||||||
} => {
|
} => {
|
||||||
server_emitter.emit(ServerEvent::ChangeAbility {
|
emitters.emit(event::ChangeAbilityEvent {
|
||||||
entity,
|
entity,
|
||||||
slot,
|
slot,
|
||||||
auxiliary_key,
|
auxiliary_key,
|
||||||
@ -130,14 +151,14 @@ impl<'a> System<'a> for Sys {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
ControlEvent::LeaveStance => {
|
ControlEvent::LeaveStance => {
|
||||||
server_emitter.emit(ServerEvent::ChangeStance {
|
emitters.emit(event::ChangeStanceEvent {
|
||||||
entity,
|
entity,
|
||||||
stance: Stance::None,
|
stance: Stance::None,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
ControlEvent::ActivatePortal(portal_uid) => {
|
ControlEvent::ActivatePortal(portal_uid) => {
|
||||||
if let Some(portal) = read_data.id_maps.uid_entity(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,
|
Alignment, Body, Buffs, CharacterState, Combo, Energy, Group, Health, Inventory, Melee,
|
||||||
Ori, Player, Pos, Scale, Stats,
|
Ori, Player, Pos, Scale, Stats,
|
||||||
},
|
},
|
||||||
event::{EventBus, ServerEvent},
|
event::{self, EmitExt, EventBus},
|
||||||
|
event_emitters,
|
||||||
outcome::Outcome,
|
outcome::Outcome,
|
||||||
resources::Time,
|
resources::Time,
|
||||||
terrain::TerrainGrid,
|
terrain::TerrainGrid,
|
||||||
@ -22,6 +23,21 @@ use specs::{
|
|||||||
};
|
};
|
||||||
use vek::*;
|
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)]
|
#[derive(SystemData)]
|
||||||
pub struct ReadData<'a> {
|
pub struct ReadData<'a> {
|
||||||
time: Read<'a, Time>,
|
time: Read<'a, Time>,
|
||||||
@ -40,10 +56,10 @@ pub struct ReadData<'a> {
|
|||||||
inventories: ReadStorage<'a, Inventory>,
|
inventories: ReadStorage<'a, Inventory>,
|
||||||
groups: ReadStorage<'a, Group>,
|
groups: ReadStorage<'a, Group>,
|
||||||
char_states: ReadStorage<'a, CharacterState>,
|
char_states: ReadStorage<'a, CharacterState>,
|
||||||
server_bus: Read<'a, EventBus<ServerEvent>>,
|
|
||||||
stats: ReadStorage<'a, Stats>,
|
stats: ReadStorage<'a, Stats>,
|
||||||
combos: ReadStorage<'a, Combo>,
|
combos: ReadStorage<'a, Combo>,
|
||||||
buffs: ReadStorage<'a, Buffs>,
|
buffs: ReadStorage<'a, Buffs>,
|
||||||
|
events: ReadAttackEvents<'a>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This system is responsible for handling accepted inputs like moving or
|
/// 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;
|
const PHASE: Phase = Phase::Create;
|
||||||
|
|
||||||
fn run(_job: &mut Job<Self>, (read_data, mut melee_attacks, outcomes): Self::SystemData) {
|
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 outcomes_emitter = outcomes.emitter();
|
||||||
let mut rng = rand::thread_rng();
|
let mut rng = rand::thread_rng();
|
||||||
|
|
||||||
@ -82,7 +98,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
if melee_attack.applied {
|
if melee_attack.applied {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
server_emitter.emit(ServerEvent::Sound {
|
emitters.emit(event::SoundEvent {
|
||||||
sound: Sound::new(SoundKind::Melee, pos.0, 2.0, read_data.time.0),
|
sound: Sound::new(SoundKind::Melee, pos.0, 2.0, read_data.time.0),
|
||||||
});
|
});
|
||||||
melee_attack.applied = true;
|
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))
|
if eye_pos.distance_squared(block_pos.map(|e| e as f32 + 0.5))
|
||||||
< (rad + scale * melee_attack.range).powi(2)
|
< (rad + scale * melee_attack.range).powi(2)
|
||||||
{
|
{
|
||||||
server_emitter.emit(ServerEvent::MineBlock {
|
emitters.emit(event::MineBlockEvent {
|
||||||
entity: attacker,
|
entity: attacker,
|
||||||
pos: block_pos,
|
pos: block_pos,
|
||||||
tool,
|
tool,
|
||||||
@ -270,7 +286,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
strength,
|
strength,
|
||||||
AttackSource::Melee,
|
AttackSource::Melee,
|
||||||
*read_data.time,
|
*read_data.time,
|
||||||
|e| server_emitter.emit(e),
|
&mut emitters,
|
||||||
|o| outcomes_emitter.emit(o),
|
|o| outcomes_emitter.emit(o),
|
||||||
&mut rng,
|
&mut rng,
|
||||||
offset as u64,
|
offset as u64,
|
||||||
|
@ -7,7 +7,7 @@ use common::{
|
|||||||
PosVelOriDefer, PreviousPhysCache, Projectile, Scale, Stats, Sticky, Vel,
|
PosVelOriDefer, PreviousPhysCache, Projectile, Scale, Stats, Sticky, Vel,
|
||||||
},
|
},
|
||||||
consts::{AIR_DENSITY, FRIC_GROUND, GRAVITY},
|
consts::{AIR_DENSITY, FRIC_GROUND, GRAVITY},
|
||||||
event::{EventBus, ServerEvent},
|
event::{EventBus, LandOnGroundEvent},
|
||||||
link::Is,
|
link::Is,
|
||||||
mounting::{Rider, VolumeRider},
|
mounting::{Rider, VolumeRider},
|
||||||
outcome::Outcome,
|
outcome::Outcome,
|
||||||
@ -133,7 +133,7 @@ pub struct PhysicsRead<'a> {
|
|||||||
uids: ReadStorage<'a, Uid>,
|
uids: ReadStorage<'a, Uid>,
|
||||||
terrain: ReadExpect<'a, TerrainGrid>,
|
terrain: ReadExpect<'a, TerrainGrid>,
|
||||||
dt: Read<'a, DeltaTime>,
|
dt: Read<'a, DeltaTime>,
|
||||||
event_bus: Read<'a, EventBus<ServerEvent>>,
|
land_on_ground_event: Read<'a, EventBus<LandOnGroundEvent>>,
|
||||||
game_mode: ReadExpect<'a, GameMode>,
|
game_mode: ReadExpect<'a, GameMode>,
|
||||||
scales: ReadStorage<'a, Scale>,
|
scales: ReadStorage<'a, Scale>,
|
||||||
stickies: ReadStorage<'a, Sticky>,
|
stickies: ReadStorage<'a, Sticky>,
|
||||||
@ -1364,11 +1364,11 @@ impl<'a> PhysicsData<'a> {
|
|||||||
}
|
}
|
||||||
drop(guard);
|
drop(guard);
|
||||||
|
|
||||||
let mut event_emitter = read.event_bus.emitter();
|
let mut event_emitter = read.land_on_ground_event.emitter();
|
||||||
land_on_grounds
|
land_on_grounds
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.for_each(|(entity, vel, surface_normal)| {
|
.for_each(|(entity, vel, surface_normal)| {
|
||||||
event_emitter.emit(ServerEvent::LandOnGround {
|
event_emitter.emit(LandOnGroundEvent {
|
||||||
entity,
|
entity,
|
||||||
vel: vel.0,
|
vel: vel.0,
|
||||||
surface_normal,
|
surface_normal,
|
||||||
|
@ -5,7 +5,12 @@ use common::{
|
|||||||
projectile, Alignment, Body, Buffs, CharacterState, Combo, Energy, Group, Health,
|
projectile, Alignment, Body, Buffs, CharacterState, Combo, Energy, Group, Health,
|
||||||
Inventory, Ori, PhysicsState, Player, Pos, Projectile, Stats, Vel,
|
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,
|
outcome::Outcome,
|
||||||
resources::{DeltaTime, Time},
|
resources::{DeltaTime, Time},
|
||||||
uid::{IdMaps, Uid},
|
uid::{IdMaps, Uid},
|
||||||
@ -25,6 +30,24 @@ use vek::*;
|
|||||||
|
|
||||||
use common::terrain::TerrainGrid;
|
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)]
|
#[derive(SystemData)]
|
||||||
pub struct ReadData<'a> {
|
pub struct ReadData<'a> {
|
||||||
time: Read<'a, Time>,
|
time: Read<'a, Time>,
|
||||||
@ -32,7 +55,7 @@ pub struct ReadData<'a> {
|
|||||||
players: ReadStorage<'a, Player>,
|
players: ReadStorage<'a, Player>,
|
||||||
dt: Read<'a, DeltaTime>,
|
dt: Read<'a, DeltaTime>,
|
||||||
id_maps: Read<'a, IdMaps>,
|
id_maps: Read<'a, IdMaps>,
|
||||||
server_bus: Read<'a, EventBus<ServerEvent>>,
|
events: Events<'a>,
|
||||||
uids: ReadStorage<'a, Uid>,
|
uids: ReadStorage<'a, Uid>,
|
||||||
positions: ReadStorage<'a, Pos>,
|
positions: ReadStorage<'a, Pos>,
|
||||||
alignments: ReadStorage<'a, Alignment>,
|
alignments: ReadStorage<'a, Alignment>,
|
||||||
@ -69,7 +92,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
_job: &mut Job<Self>,
|
_job: &mut Job<Self>,
|
||||||
(read_data, mut orientations, mut projectiles, outcomes): Self::SystemData,
|
(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 outcomes_emitter = outcomes.emitter();
|
||||||
let mut rng = rand::thread_rng();
|
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));
|
.and_then(|uid| read_data.id_maps.uid_entity(uid));
|
||||||
|
|
||||||
if physics.on_surface().is_none() && rng.gen_bool(0.05) {
|
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),
|
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,
|
&read_data,
|
||||||
&mut projectile_vanished,
|
&mut projectile_vanished,
|
||||||
&mut outcomes_emitter,
|
&mut outcomes_emitter,
|
||||||
&mut server_emitter,
|
&mut emitters,
|
||||||
&mut rng,
|
&mut rng,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -200,18 +223,18 @@ impl<'a> System<'a> for Sys {
|
|||||||
.get(entity)
|
.get(entity)
|
||||||
.map_or_else(Vec3::zero, |ori| ori.look_vec());
|
.map_or_else(Vec3::zero, |ori| ori.look_vec());
|
||||||
let offset = -0.2 * projectile_direction;
|
let offset = -0.2 * projectile_direction;
|
||||||
server_emitter.emit(ServerEvent::Explosion {
|
emitters.emit(ExplosionEvent {
|
||||||
pos: pos.0 + offset,
|
pos: pos.0 + offset,
|
||||||
explosion: e,
|
explosion: e,
|
||||||
owner: projectile.owner,
|
owner: projectile.owner,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
projectile::Effect::Vanish => {
|
projectile::Effect::Vanish => {
|
||||||
server_emitter.emit(ServerEvent::Delete(entity));
|
emitters.emit(DeleteEvent(entity));
|
||||||
projectile_vanished = true;
|
projectile_vanished = true;
|
||||||
},
|
},
|
||||||
projectile::Effect::Bonk => {
|
projectile::Effect::Bonk => {
|
||||||
server_emitter.emit(ServerEvent::Bonk {
|
emitters.emit(BonkEvent {
|
||||||
pos: pos.0,
|
pos: pos.0,
|
||||||
owner: projectile.owner,
|
owner: projectile.owner,
|
||||||
target: None,
|
target: None,
|
||||||
@ -231,7 +254,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if projectile.time_left == Duration::default() {
|
if projectile.time_left == Duration::default() {
|
||||||
server_emitter.emit(ServerEvent::Delete(entity));
|
emitters.emit(DeleteEvent(entity));
|
||||||
}
|
}
|
||||||
projectile.time_left = projectile
|
projectile.time_left = projectile
|
||||||
.time_left
|
.time_left
|
||||||
@ -264,7 +287,7 @@ fn dispatch_hit(
|
|||||||
read_data: &ReadData,
|
read_data: &ReadData,
|
||||||
projectile_vanished: &mut bool,
|
projectile_vanished: &mut bool,
|
||||||
outcomes_emitter: &mut Emitter<Outcome>,
|
outcomes_emitter: &mut Emitter<Outcome>,
|
||||||
server_emitter: &mut Emitter<ServerEvent>,
|
emitters: &mut Emitters,
|
||||||
rng: &mut rand::rngs::ThreadRng,
|
rng: &mut rand::rngs::ThreadRng,
|
||||||
) {
|
) {
|
||||||
match projectile_info.effect {
|
match projectile_info.effect {
|
||||||
@ -437,7 +460,7 @@ fn dispatch_hit(
|
|||||||
1.0,
|
1.0,
|
||||||
AttackSource::Projectile,
|
AttackSource::Projectile,
|
||||||
*read_data.time,
|
*read_data.time,
|
||||||
|e| server_emitter.emit(e),
|
emitters,
|
||||||
|o| outcomes_emitter.emit(o),
|
|o| outcomes_emitter.emit(o),
|
||||||
rng,
|
rng,
|
||||||
0,
|
0,
|
||||||
@ -446,7 +469,7 @@ fn dispatch_hit(
|
|||||||
projectile::Effect::Explode(e) => {
|
projectile::Effect::Explode(e) => {
|
||||||
let Pos(pos) = *projectile_info.pos;
|
let Pos(pos) = *projectile_info.pos;
|
||||||
let owner_uid = projectile_info.owner_uid;
|
let owner_uid = projectile_info.owner_uid;
|
||||||
server_emitter.emit(ServerEvent::Explosion {
|
emitters.emit(ExplosionEvent {
|
||||||
pos,
|
pos,
|
||||||
explosion: e,
|
explosion: e,
|
||||||
owner: owner_uid,
|
owner: owner_uid,
|
||||||
@ -455,7 +478,7 @@ fn dispatch_hit(
|
|||||||
projectile::Effect::Bonk => {
|
projectile::Effect::Bonk => {
|
||||||
let Pos(pos) = *projectile_info.pos;
|
let Pos(pos) = *projectile_info.pos;
|
||||||
let owner_uid = projectile_info.owner_uid;
|
let owner_uid = projectile_info.owner_uid;
|
||||||
server_emitter.emit(ServerEvent::Bonk {
|
emitters.emit(BonkEvent {
|
||||||
pos,
|
pos,
|
||||||
owner: owner_uid,
|
owner: owner_uid,
|
||||||
target: Some(projectile_target_info.uid),
|
target: Some(projectile_target_info.uid),
|
||||||
@ -463,7 +486,7 @@ fn dispatch_hit(
|
|||||||
},
|
},
|
||||||
projectile::Effect::Vanish => {
|
projectile::Effect::Vanish => {
|
||||||
let entity = projectile_info.entity;
|
let entity = projectile_info.entity;
|
||||||
server_emitter.emit(ServerEvent::Delete(entity));
|
emitters.emit(DeleteEvent(entity));
|
||||||
*projectile_vanished = true;
|
*projectile_vanished = true;
|
||||||
},
|
},
|
||||||
projectile::Effect::Possess => {
|
projectile::Effect::Possess => {
|
||||||
@ -471,7 +494,7 @@ fn dispatch_hit(
|
|||||||
let owner_uid = projectile_info.owner_uid;
|
let owner_uid = projectile_info.owner_uid;
|
||||||
if let Some(owner_uid) = owner_uid {
|
if let Some(owner_uid) = owner_uid {
|
||||||
if target_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,
|
Alignment, Body, Buffs, CharacterState, Combo, Energy, Group, Health, Inventory, Ori,
|
||||||
PhysicsState, Player, Pos, Scale, Shockwave, ShockwaveHitEntities, Stats,
|
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,
|
outcome::Outcome,
|
||||||
resources::{DeltaTime, Time},
|
resources::{DeltaTime, Time},
|
||||||
uid::{IdMaps, Uid},
|
uid::{IdMaps, Uid},
|
||||||
@ -18,10 +23,26 @@ use rand::Rng;
|
|||||||
use specs::{shred, Entities, Join, LendJoin, Read, ReadStorage, SystemData, WriteStorage};
|
use specs::{shred, Entities, Join, LendJoin, Read, ReadStorage, SystemData, WriteStorage};
|
||||||
use vek::*;
|
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)]
|
#[derive(SystemData)]
|
||||||
pub struct ReadData<'a> {
|
pub struct ReadData<'a> {
|
||||||
entities: Entities<'a>,
|
entities: Entities<'a>,
|
||||||
server_bus: Read<'a, EventBus<ServerEvent>>,
|
events: Events<'a>,
|
||||||
time: Read<'a, Time>,
|
time: Read<'a, Time>,
|
||||||
players: ReadStorage<'a, Player>,
|
players: ReadStorage<'a, Player>,
|
||||||
dt: Read<'a, DeltaTime>,
|
dt: Read<'a, DeltaTime>,
|
||||||
@ -63,7 +84,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
_job: &mut Job<Self>,
|
_job: &mut Job<Self>,
|
||||||
(read_data, mut shockwaves, mut shockwave_hit_lists, outcomes): Self::SystemData,
|
(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 outcomes_emitter = outcomes.emitter();
|
||||||
let mut rng = rand::thread_rng();
|
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));
|
.and_then(|uid| read_data.id_maps.uid_entity(uid));
|
||||||
|
|
||||||
if rng.gen_bool(0.05) {
|
if rng.gen_bool(0.05) {
|
||||||
server_emitter.emit(ServerEvent::Sound {
|
emitters.emit(SoundEvent {
|
||||||
sound: Sound::new(SoundKind::Shockwave, pos.0, 40.0, time),
|
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
|
// 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
|
// may have traveled and produced effects a bit before reaching it's end point
|
||||||
if time > end_time {
|
if time > end_time {
|
||||||
server_emitter.emit(ServerEvent::Delete(entity));
|
emitters.emit(DeleteEvent(entity));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -249,7 +270,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
1.0,
|
1.0,
|
||||||
shockwave.dodgeable.to_attack_source(),
|
shockwave.dodgeable.to_attack_source(),
|
||||||
*read_data.time,
|
*read_data.time,
|
||||||
|e| server_emitter.emit(e),
|
&mut emitters,
|
||||||
|o| outcomes_emitter.emit(o),
|
|o| outcomes_emitter.emit(o),
|
||||||
&mut rng,
|
&mut rng,
|
||||||
0,
|
0,
|
||||||
|
@ -7,7 +7,8 @@ use common::{
|
|||||||
Body, CharacterState, Combo, Energy, Health, Inventory, Poise, Pos, SkillSet, Stats,
|
Body, CharacterState, Combo, Energy, Health, Inventory, Poise, Pos, SkillSet, Stats,
|
||||||
StatsModifier,
|
StatsModifier,
|
||||||
},
|
},
|
||||||
event::{EventBus, ServerEvent},
|
event::{DestroyEvent, EmitExt},
|
||||||
|
event_emitters,
|
||||||
resources::{DeltaTime, EntitiesDiedLastTick, Time},
|
resources::{DeltaTime, EntitiesDiedLastTick, Time},
|
||||||
};
|
};
|
||||||
use common_ecs::{Job, Origin, Phase, System};
|
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 SIT_ENERGY_REGEN_ACCEL: f32 = 2.5;
|
||||||
const POISE_REGEN_ACCEL: f32 = 2.0;
|
const POISE_REGEN_ACCEL: f32 = 2.0;
|
||||||
|
|
||||||
|
event_emitters! {
|
||||||
|
struct Events[Emitters] {
|
||||||
|
destroy: DestroyEvent,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(SystemData)]
|
#[derive(SystemData)]
|
||||||
pub struct ReadData<'a> {
|
pub struct ReadData<'a> {
|
||||||
entities: Entities<'a>,
|
entities: Entities<'a>,
|
||||||
dt: Read<'a, DeltaTime>,
|
dt: Read<'a, DeltaTime>,
|
||||||
time: Read<'a, Time>,
|
time: Read<'a, Time>,
|
||||||
server_bus: Read<'a, EventBus<ServerEvent>>,
|
events: Events<'a>,
|
||||||
positions: ReadStorage<'a, Pos>,
|
positions: ReadStorage<'a, Pos>,
|
||||||
bodies: ReadStorage<'a, Body>,
|
bodies: ReadStorage<'a, Body>,
|
||||||
char_states: ReadStorage<'a, CharacterState>,
|
char_states: ReadStorage<'a, CharacterState>,
|
||||||
@ -65,7 +72,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
): Self::SystemData,
|
): Self::SystemData,
|
||||||
) {
|
) {
|
||||||
entities_died_last_tick.0.clear();
|
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;
|
let dt = read_data.dt.0;
|
||||||
|
|
||||||
// Update stats
|
// Update stats
|
||||||
@ -84,7 +91,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
if set_dead {
|
if set_dead {
|
||||||
let cloned_entity = (entity, *pos);
|
let cloned_entity = (entity, *pos);
|
||||||
entities_died_last_tick.0.push(cloned_entity);
|
entities_died_last_tick.0.push(cloned_entity);
|
||||||
server_event_emitter.emit(ServerEvent::Destroy {
|
emitters.emit(DestroyEvent {
|
||||||
entity,
|
entity,
|
||||||
cause: health.last_change,
|
cause: health.last_change,
|
||||||
});
|
});
|
||||||
|
@ -3,7 +3,7 @@ use crate::{
|
|||||||
AVG_FOLLOW_DIST, DEFAULT_ATTACK_RANGE, IDLE_HEALING_ITEM_THRESHOLD, MAX_PATROL_DIST,
|
AVG_FOLLOW_DIST, DEFAULT_ATTACK_RANGE, IDLE_HEALING_ITEM_THRESHOLD, MAX_PATROL_DIST,
|
||||||
PARTIAL_PATH_DIST, SEPARATION_BIAS, SEPARATION_DIST, STD_AWARENESS_DECAY_RATE,
|
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::{
|
util::{
|
||||||
aim_projectile, are_our_owners_hostile, entities_have_line_of_sight, get_attacker,
|
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,
|
get_entity_by_id, is_dead_or_invulnerable, is_dressed_as_cultist, is_invulnerable,
|
||||||
@ -28,7 +28,7 @@ use common::{
|
|||||||
},
|
},
|
||||||
consts::MAX_MOUNT_RANGE,
|
consts::MAX_MOUNT_RANGE,
|
||||||
effect::{BuffEffect, Effect},
|
effect::{BuffEffect, Effect},
|
||||||
event::{Emitter, ServerEvent},
|
event::{ChatEvent, EmitExt, SoundEvent},
|
||||||
mounting::VolumePos,
|
mounting::VolumePos,
|
||||||
path::TraversalConfig,
|
path::TraversalConfig,
|
||||||
rtsim::NpcActivity,
|
rtsim::NpcActivity,
|
||||||
@ -180,7 +180,7 @@ impl<'a> AgentData<'a> {
|
|||||||
agent: &mut Agent,
|
agent: &mut Agent,
|
||||||
controller: &mut Controller,
|
controller: &mut Controller,
|
||||||
read_data: &ReadData,
|
read_data: &ReadData,
|
||||||
event_emitter: &mut Emitter<ServerEvent>,
|
emitters: &mut AgentEmitters,
|
||||||
rng: &mut impl Rng,
|
rng: &mut impl Rng,
|
||||||
) {
|
) {
|
||||||
enum ActionTimers {
|
enum ActionTimers {
|
||||||
@ -430,7 +430,7 @@ impl<'a> AgentData<'a> {
|
|||||||
agent,
|
agent,
|
||||||
controller,
|
controller,
|
||||||
read_data,
|
read_data,
|
||||||
event_emitter,
|
emitters,
|
||||||
AgentData::is_hunting_animal,
|
AgentData::is_hunting_animal,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -807,7 +807,7 @@ impl<'a> AgentData<'a> {
|
|||||||
agent: &mut Agent,
|
agent: &mut Agent,
|
||||||
controller: &mut Controller,
|
controller: &mut Controller,
|
||||||
read_data: &ReadData,
|
read_data: &ReadData,
|
||||||
event_emitter: &mut Emitter<ServerEvent>,
|
emitters: &mut AgentEmitters,
|
||||||
is_enemy: fn(&Self, EcsEntity, &ReadData) -> bool,
|
is_enemy: fn(&Self, EcsEntity, &ReadData) -> bool,
|
||||||
) {
|
) {
|
||||||
enum ActionStateTimers {
|
enum ActionStateTimers {
|
||||||
@ -856,7 +856,7 @@ impl<'a> AgentData<'a> {
|
|||||||
self.chat_npc_if_allowed_to_speak(
|
self.chat_npc_if_allowed_to_speak(
|
||||||
Content::localized("npc-speech-ambush"),
|
Content::localized("npc-speech-ambush"),
|
||||||
agent,
|
agent,
|
||||||
event_emitter,
|
emitters,
|
||||||
);
|
);
|
||||||
aggro_on = true;
|
aggro_on = true;
|
||||||
Some((entity, true))
|
Some((entity, true))
|
||||||
@ -1666,13 +1666,13 @@ impl<'a> AgentData<'a> {
|
|||||||
agent: &mut Agent,
|
agent: &mut Agent,
|
||||||
controller: &mut Controller,
|
controller: &mut Controller,
|
||||||
read_data: &ReadData,
|
read_data: &ReadData,
|
||||||
event_emitter: &mut Emitter<ServerEvent>,
|
emitters: &mut AgentEmitters,
|
||||||
rng: &mut impl Rng,
|
rng: &mut impl Rng,
|
||||||
) {
|
) {
|
||||||
agent.forget_old_sounds(read_data.time.0);
|
agent.forget_old_sounds(read_data.time.0);
|
||||||
|
|
||||||
if is_invulnerable(*self.entity, read_data) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1705,13 +1705,13 @@ impl<'a> AgentData<'a> {
|
|||||||
} else if self.below_flee_health(agent) || !follows_threatening_sounds {
|
} else if self.below_flee_health(agent) || !follows_threatening_sounds {
|
||||||
self.flee(agent, controller, read_data, &sound_pos);
|
self.flee(agent, controller, read_data, &sound_pos);
|
||||||
} else {
|
} else {
|
||||||
self.idle(agent, controller, read_data, event_emitter, rng);
|
self.idle(agent, controller, read_data, emitters, rng);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
self.idle(agent, controller, read_data, event_emitter, rng);
|
self.idle(agent, controller, read_data, emitters, rng);
|
||||||
}
|
}
|
||||||
} else {
|
} 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,
|
agent: &mut Agent,
|
||||||
read_data: &ReadData,
|
read_data: &ReadData,
|
||||||
controller: &mut Controller,
|
controller: &mut Controller,
|
||||||
event_emitter: &mut Emitter<ServerEvent>,
|
emitters: &mut AgentEmitters,
|
||||||
rng: &mut impl Rng,
|
rng: &mut impl Rng,
|
||||||
) {
|
) {
|
||||||
if let Some(Target { target, .. }) = agent.target {
|
if let Some(Target { target, .. }) = agent.target {
|
||||||
@ -1750,7 +1750,7 @@ impl<'a> AgentData<'a> {
|
|||||||
Some(tgt_pos.0),
|
Some(tgt_pos.0),
|
||||||
));
|
));
|
||||||
|
|
||||||
self.idle(agent, controller, read_data, event_emitter, rng);
|
self.idle(agent, controller, read_data, emitters, rng);
|
||||||
} else {
|
} else {
|
||||||
let target_data = TargetData::new(tgt_pos, target, read_data);
|
let target_data = TargetData::new(tgt_pos, target, read_data);
|
||||||
// TODO: Reimplement this in rtsim
|
// TODO: Reimplement this in rtsim
|
||||||
@ -1774,25 +1774,23 @@ impl<'a> AgentData<'a> {
|
|||||||
&self,
|
&self,
|
||||||
msg: Content,
|
msg: Content,
|
||||||
agent: &Agent,
|
agent: &Agent,
|
||||||
event_emitter: &mut Emitter<'_, ServerEvent>,
|
emitters: &mut AgentEmitters,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
if agent.allowed_to_speak() {
|
if agent.allowed_to_speak() {
|
||||||
self.chat_npc(msg, event_emitter);
|
self.chat_npc(msg, emitters);
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn chat_npc(&self, content: Content, event_emitter: &mut Emitter<'_, ServerEvent>) {
|
pub fn chat_npc(&self, content: Content, emitters: &mut AgentEmitters) {
|
||||||
event_emitter.emit(ServerEvent::Chat(UnresolvedChatMsg::npc(
|
emitters.emit(ChatEvent(UnresolvedChatMsg::npc(*self.uid, content)));
|
||||||
*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 {
|
if let Some(body) = self.body {
|
||||||
event_emitter.emit(ServerEvent::Sound {
|
emitters.emit(SoundEvent {
|
||||||
sound: Sound::new(
|
sound: Sound::new(
|
||||||
SoundKind::Utterance(UtteranceKind::Scream, *body),
|
SoundKind::Utterance(UtteranceKind::Scream, *body),
|
||||||
self.pos.0,
|
self.pos.0,
|
||||||
@ -1803,12 +1801,7 @@ impl<'a> AgentData<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn cry_out(
|
pub fn cry_out(&self, agent: &Agent, emitters: &mut AgentEmitters, read_data: &ReadData) {
|
||||||
&self,
|
|
||||||
agent: &Agent,
|
|
||||||
event_emitter: &mut Emitter<'_, ServerEvent>,
|
|
||||||
read_data: &ReadData,
|
|
||||||
) {
|
|
||||||
let has_enemy_alignment = matches!(self.alignment, Some(Alignment::Enemy));
|
let has_enemy_alignment = matches!(self.alignment, Some(Alignment::Enemy));
|
||||||
|
|
||||||
if has_enemy_alignment {
|
if has_enemy_alignment {
|
||||||
@ -1817,28 +1810,24 @@ impl<'a> AgentData<'a> {
|
|||||||
self.chat_npc_if_allowed_to_speak(
|
self.chat_npc_if_allowed_to_speak(
|
||||||
Content::localized("npc-speech-cultist_low_health_fleeing"),
|
Content::localized("npc-speech-cultist_low_health_fleeing"),
|
||||||
agent,
|
agent,
|
||||||
event_emitter,
|
emitters,
|
||||||
);
|
);
|
||||||
} else if is_villager(self.alignment) {
|
} else if is_villager(self.alignment) {
|
||||||
self.chat_npc_if_allowed_to_speak(
|
self.chat_npc_if_allowed_to_speak(
|
||||||
Content::localized("npc-speech-villager_under_attack"),
|
Content::localized("npc-speech-villager_under_attack"),
|
||||||
agent,
|
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(
|
pub fn exclaim_relief_about_enemy_dead(&self, agent: &Agent, emitters: &mut AgentEmitters) {
|
||||||
&self,
|
|
||||||
agent: &Agent,
|
|
||||||
event_emitter: &mut Emitter<'_, ServerEvent>,
|
|
||||||
) {
|
|
||||||
if is_villager(self.alignment) {
|
if is_villager(self.alignment) {
|
||||||
self.chat_npc_if_allowed_to_speak(
|
self.chat_npc_if_allowed_to_speak(
|
||||||
Content::localized("npc-speech-villager_enemy_killed"),
|
Content::localized("npc-speech-villager_enemy_killed"),
|
||||||
agent,
|
agent,
|
||||||
event_emitter,
|
emitters,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2002,7 +1991,7 @@ impl<'a> AgentData<'a> {
|
|||||||
controller: &mut Controller,
|
controller: &mut Controller,
|
||||||
target: EcsEntity,
|
target: EcsEntity,
|
||||||
read_data: &ReadData,
|
read_data: &ReadData,
|
||||||
event_emitter: &mut Emitter<ServerEvent>,
|
emitters: &mut AgentEmitters,
|
||||||
rng: &mut impl Rng,
|
rng: &mut impl Rng,
|
||||||
remembers_fight_with_target: bool,
|
remembers_fight_with_target: bool,
|
||||||
) {
|
) {
|
||||||
@ -2011,7 +2000,7 @@ impl<'a> AgentData<'a> {
|
|||||||
let move_dir_mag = move_dir.magnitude();
|
let move_dir_mag = move_dir.magnitude();
|
||||||
let small_chance = rng.gen::<f32>() < read_data.dt.0 * 0.25;
|
let small_chance = rng.gen::<f32>() < read_data.dt.0 * 0.25;
|
||||||
let mut chat = |content: Content| {
|
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 mut chat_villager_remembers_fighting = || {
|
||||||
let tgt_name = read_data.stats.get(target).map(|stats| stats.name.clone());
|
let tgt_name = read_data.stats.get(target).map(|stats| stats.name.clone());
|
||||||
|
@ -17,6 +17,7 @@ use common::{
|
|||||||
Stats, Vel,
|
Stats, Vel,
|
||||||
},
|
},
|
||||||
consts::GRAVITY,
|
consts::GRAVITY,
|
||||||
|
event, event_emitters,
|
||||||
link::Is,
|
link::Is,
|
||||||
mounting::{Mount, Rider, VolumeRider},
|
mounting::{Mount, Rider, VolumeRider},
|
||||||
path::TraversalConfig,
|
path::TraversalConfig,
|
||||||
@ -28,6 +29,14 @@ use common::{
|
|||||||
};
|
};
|
||||||
use specs::{shred, Entities, Entity as EcsEntity, Read, ReadExpect, ReadStorage, SystemData};
|
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
|
// TODO: Move rtsim back into AgentData after rtsim2 when it has a separate
|
||||||
// crate
|
// crate
|
||||||
pub struct AgentData<'a> {
|
pub struct AgentData<'a> {
|
||||||
|
@ -40,7 +40,10 @@ use common::{
|
|||||||
},
|
},
|
||||||
depot,
|
depot,
|
||||||
effect::Effect,
|
effect::Effect,
|
||||||
event::{EventBus, ServerEvent},
|
event::{
|
||||||
|
ClientDisconnectEvent, CreateWaypointEvent, EventBus, ExplosionEvent, GroupManipEvent,
|
||||||
|
InitiateInviteEvent, TamePetEvent,
|
||||||
|
},
|
||||||
generation::{EntityConfig, EntityInfo},
|
generation::{EntityConfig, EntityInfo},
|
||||||
link::Is,
|
link::Is,
|
||||||
mounting::{Rider, Volume, VolumeRider},
|
mounting::{Rider, Volume, VolumeRider},
|
||||||
@ -1804,9 +1807,7 @@ fn handle_spawn(
|
|||||||
|
|
||||||
// Add to group system if a pet
|
// Add to group system if a pet
|
||||||
if matches!(alignment, comp::Alignment::Owned { .. }) {
|
if matches!(alignment, comp::Alignment::Owned { .. }) {
|
||||||
let server_eventbus =
|
server.state.emit_event_now(TamePetEvent {
|
||||||
server.state.ecs().read_resource::<EventBus<ServerEvent>>();
|
|
||||||
server_eventbus.emit_now(ServerEvent::TamePet {
|
|
||||||
owner_entity: target,
|
owner_entity: target,
|
||||||
pet_entity: new_entity,
|
pet_entity: new_entity,
|
||||||
});
|
});
|
||||||
@ -2079,8 +2080,8 @@ fn handle_spawn_campfire(
|
|||||||
server
|
server
|
||||||
.state
|
.state
|
||||||
.ecs()
|
.ecs()
|
||||||
.read_resource::<EventBus<ServerEvent>>()
|
.read_resource::<EventBus<CreateWaypointEvent>>()
|
||||||
.emit_now(ServerEvent::CreateWaypoint(pos.0));
|
.emit_now(CreateWaypointEvent(pos.0));
|
||||||
|
|
||||||
server.notify_client(
|
server.notify_client(
|
||||||
client,
|
client,
|
||||||
@ -2907,26 +2908,23 @@ fn handle_explosion(
|
|||||||
.read_storage::<Uid>()
|
.read_storage::<Uid>()
|
||||||
.get(target)
|
.get(target)
|
||||||
.copied();
|
.copied();
|
||||||
server
|
server.state.emit_event_now(ExplosionEvent {
|
||||||
.state
|
pos: pos.0,
|
||||||
.mut_resource::<EventBus<ServerEvent>>()
|
explosion: Explosion {
|
||||||
.emit_now(ServerEvent::Explosion {
|
effects: vec![
|
||||||
pos: pos.0,
|
RadiusEffect::Entity(Effect::Damage(Damage {
|
||||||
explosion: Explosion {
|
source: DamageSource::Explosion,
|
||||||
effects: vec![
|
kind: DamageKind::Energy,
|
||||||
RadiusEffect::Entity(Effect::Damage(Damage {
|
value: 100.0 * power,
|
||||||
source: DamageSource::Explosion,
|
})),
|
||||||
kind: DamageKind::Energy,
|
RadiusEffect::TerrainDestruction(power, Rgb::black()),
|
||||||
value: 100.0 * power,
|
],
|
||||||
})),
|
radius: 3.0 * power,
|
||||||
RadiusEffect::TerrainDestruction(power, Rgb::black()),
|
reagent: None,
|
||||||
],
|
min_falloff: 0.0,
|
||||||
radius: 3.0 * power,
|
},
|
||||||
reagent: None,
|
owner,
|
||||||
min_falloff: 0.0,
|
});
|
||||||
},
|
|
||||||
owner,
|
|
||||||
});
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3261,8 +3259,7 @@ fn handle_group_invite(
|
|||||||
|
|
||||||
server
|
server
|
||||||
.state
|
.state
|
||||||
.mut_resource::<EventBus<ServerEvent>>()
|
.emit_event_now(InitiateInviteEvent(target, uid, InviteKind::Group));
|
||||||
.emit_now(ServerEvent::InitiateInvite(target, uid, InviteKind::Group));
|
|
||||||
|
|
||||||
if client != target {
|
if client != target {
|
||||||
server.notify_client(
|
server.notify_client(
|
||||||
@ -3301,8 +3298,7 @@ fn handle_group_kick(
|
|||||||
|
|
||||||
server
|
server
|
||||||
.state
|
.state
|
||||||
.mut_resource::<EventBus<ServerEvent>>()
|
.emit_event_now(GroupManipEvent(target, comp::GroupManip::Kick(uid)));
|
||||||
.emit_now(ServerEvent::GroupManip(target, comp::GroupManip::Kick(uid)));
|
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(Content::Plain(action.help_string()))
|
Err(Content::Plain(action.help_string()))
|
||||||
@ -3318,8 +3314,7 @@ fn handle_group_leave(
|
|||||||
) -> CmdResult<()> {
|
) -> CmdResult<()> {
|
||||||
server
|
server
|
||||||
.state
|
.state
|
||||||
.mut_resource::<EventBus<ServerEvent>>()
|
.emit_event_now(GroupManipEvent(target, comp::GroupManip::Leave));
|
||||||
.emit_now(ServerEvent::GroupManip(target, comp::GroupManip::Leave));
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3337,11 +3332,7 @@ fn handle_group_promote(
|
|||||||
|
|
||||||
server
|
server
|
||||||
.state
|
.state
|
||||||
.mut_resource::<EventBus<ServerEvent>>()
|
.emit_event_now(GroupManipEvent(target, comp::GroupManip::AssignLeader(uid)));
|
||||||
.emit_now(ServerEvent::GroupManip(
|
|
||||||
target,
|
|
||||||
comp::GroupManip::AssignLeader(uid),
|
|
||||||
));
|
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(Content::Plain(action.help_string()))
|
Err(Content::Plain(action.help_string()))
|
||||||
@ -3944,8 +3935,8 @@ fn kick_player(
|
|||||||
);
|
);
|
||||||
server
|
server
|
||||||
.state
|
.state
|
||||||
.mut_resource::<EventBus<ServerEvent>>()
|
.mut_resource::<EventBus<ClientDisconnectEvent>>()
|
||||||
.emit_now(ServerEvent::ClientDisconnect(
|
.emit_now(ClientDisconnectEvent(
|
||||||
target_player,
|
target_player,
|
||||||
comp::DisconnectReason::Kicked,
|
comp::DisconnectReason::Kicked,
|
||||||
));
|
));
|
||||||
|
@ -3,25 +3,24 @@ use crate::{
|
|||||||
presence::RepositionOnChunkLoad, sys, CharacterUpdater, Server, StateExt,
|
presence::RepositionOnChunkLoad, sys, CharacterUpdater, Server, StateExt,
|
||||||
};
|
};
|
||||||
use common::{
|
use common::{
|
||||||
character::CharacterId,
|
|
||||||
comp::{
|
comp::{
|
||||||
self,
|
self,
|
||||||
aura::{Aura, AuraKind, AuraTarget},
|
aura::{Aura, AuraKind, AuraTarget},
|
||||||
buff::{BuffCategory, BuffData, BuffKind, BuffSource},
|
buff::{BuffCategory, BuffData, BuffKind, BuffSource},
|
||||||
misc::PortalData,
|
|
||||||
ship::figuredata::VOXEL_COLLIDER_MANIFEST,
|
ship::figuredata::VOXEL_COLLIDER_MANIFEST,
|
||||||
shockwave, Alignment, BehaviorCapability, Body, ItemDrops, LightEmitter, Object, Ori, Pos,
|
Alignment, BehaviorCapability, ItemDrops, LightEmitter, Ori, Pos, TradingBehavior, Vel,
|
||||||
Projectile, TradingBehavior, Vel, WaypointArea,
|
WaypointArea,
|
||||||
|
},
|
||||||
|
event::{
|
||||||
|
CreateNpcEvent, CreateShipEvent, CreateTeleporterEvent, CreateWaypointEvent, EventBus,
|
||||||
|
InitializeCharacterEvent, InitializeSpectatorEvent, ShockwaveEvent, ShootEvent,
|
||||||
|
UpdateCharacterDataEvent,
|
||||||
},
|
},
|
||||||
event::{EventBus, NpcBuilder, UpdateCharacterMetadata},
|
|
||||||
mounting::{Mounting, Volume, VolumeMounting, VolumePos},
|
mounting::{Mounting, Volume, VolumeMounting, VolumePos},
|
||||||
outcome::Outcome,
|
outcome::Outcome,
|
||||||
resources::{Secs, Time},
|
resources::{Secs, Time},
|
||||||
rtsim::RtSimEntity,
|
|
||||||
uid::{IdMaps, Uid},
|
uid::{IdMaps, Uid},
|
||||||
util::Dir,
|
|
||||||
vol::IntoFullVolIterator,
|
vol::IntoFullVolIterator,
|
||||||
ViewDistances,
|
|
||||||
};
|
};
|
||||||
use common_net::{msg::ServerGeneral, sync::WorldSyncExt};
|
use common_net::{msg::ServerGeneral, sync::WorldSyncExt};
|
||||||
use specs::{Builder, Entity as EcsEntity, WorldExt};
|
use specs::{Builder, Entity as EcsEntity, WorldExt};
|
||||||
@ -29,56 +28,57 @@ use vek::{Rgb, Vec3};
|
|||||||
|
|
||||||
use super::group_manip::update_map_markers;
|
use super::group_manip::update_map_markers;
|
||||||
|
|
||||||
pub fn handle_initialize_character(
|
pub fn handle_initialize_character(server: &mut Server, ev: InitializeCharacterEvent) {
|
||||||
server: &mut Server,
|
|
||||||
entity: EcsEntity,
|
|
||||||
character_id: CharacterId,
|
|
||||||
requested_view_distances: ViewDistances,
|
|
||||||
) {
|
|
||||||
let updater = server.state.ecs().fetch::<CharacterUpdater>();
|
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);
|
drop(updater);
|
||||||
|
|
||||||
if !pending_database_action {
|
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
|
server
|
||||||
.state
|
.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.
|
// Correct client if its requested VD is too high.
|
||||||
if requested_view_distances.terrain != clamped_vds.terrain {
|
if ev.requested_view_distances.terrain != clamped_vds.terrain {
|
||||||
server.notify_client(entity, ServerGeneral::SetViewDistance(clamped_vds.terrain));
|
server.notify_client(
|
||||||
|
ev.entity,
|
||||||
|
ServerGeneral::SetViewDistance(clamped_vds.terrain),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// A character delete or update was somehow initiated after the login commenced,
|
// 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
|
// so kick the client out of "ingame" without saving any data and abort
|
||||||
// the character loading process.
|
// the character loading process.
|
||||||
handle_exit_ingame(server, entity, true);
|
handle_exit_ingame(server, ev.entity, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_initialize_spectator(
|
pub fn handle_initialize_spectator(server: &mut Server, ev: InitializeSpectatorEvent) {
|
||||||
server: &mut Server,
|
let clamped_vds = ev.1.clamp(server.settings().max_view_distance);
|
||||||
entity: EcsEntity,
|
server.state.initialize_spectator_data(ev.0, clamped_vds);
|
||||||
requested_view_distances: ViewDistances,
|
|
||||||
) {
|
|
||||||
let clamped_vds = requested_view_distances.clamp(server.settings().max_view_distance);
|
|
||||||
server.state.initialize_spectator_data(entity, clamped_vds);
|
|
||||||
// Correct client if its requested VD is too high.
|
// Correct client if its requested VD is too high.
|
||||||
if requested_view_distances.terrain != clamped_vds.terrain {
|
if ev.1.terrain != clamped_vds.terrain {
|
||||||
server.notify_client(entity, ServerGeneral::SetViewDistance(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(
|
pub fn handle_loaded_character_data(server: &mut Server, ev: UpdateCharacterDataEvent) {
|
||||||
server: &mut Server,
|
let loaded_components = PersistedComponents {
|
||||||
entity: EcsEntity,
|
body: ev.components.0,
|
||||||
loaded_components: PersistedComponents,
|
stats: ev.components.1,
|
||||||
metadata: UpdateCharacterMetadata,
|
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 {
|
if let Some(marker) = loaded_components.map_marker {
|
||||||
server.notify_client(
|
server.notify_client(
|
||||||
entity,
|
ev.entity,
|
||||||
ServerGeneral::MapMarker(comp::MapMarkerUpdate::Owned(comp::MapMarkerChange::Update(
|
ServerGeneral::MapMarker(comp::MapMarkerUpdate::Owned(comp::MapMarkerChange::Update(
|
||||||
marker.0,
|
marker.0,
|
||||||
))),
|
))),
|
||||||
@ -87,68 +87,62 @@ pub fn handle_loaded_character_data(
|
|||||||
|
|
||||||
let result_msg = if let Err(err) = server
|
let result_msg = if let Err(err) = server
|
||||||
.state
|
.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))
|
ServerGeneral::CharacterDataLoadResult(Err(err))
|
||||||
} else {
|
} 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.
|
// 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(
|
pub fn handle_create_npc(server: &mut Server, mut ev: CreateNpcEvent) -> EcsEntity {
|
||||||
server: &mut Server,
|
|
||||||
pos: Pos,
|
|
||||||
ori: Ori,
|
|
||||||
mut npc: NpcBuilder,
|
|
||||||
rider: Option<NpcBuilder>,
|
|
||||||
) -> EcsEntity {
|
|
||||||
let entity = server
|
let entity = server
|
||||||
.state
|
.state
|
||||||
.create_npc(
|
.create_npc(
|
||||||
pos,
|
ev.pos,
|
||||||
ori,
|
ev.ori,
|
||||||
npc.stats,
|
ev.npc.stats,
|
||||||
npc.skill_set,
|
ev.npc.skill_set,
|
||||||
npc.health,
|
ev.npc.health,
|
||||||
npc.poise,
|
ev.npc.poise,
|
||||||
npc.inventory,
|
ev.npc.inventory,
|
||||||
npc.body,
|
ev.npc.body,
|
||||||
)
|
)
|
||||||
.with(npc.scale);
|
.with(ev.npc.scale);
|
||||||
|
|
||||||
if let Some(agent) = &mut npc.agent {
|
if let Some(agent) = &mut ev.npc.agent {
|
||||||
if let Alignment::Owned(_) = &npc.alignment {
|
if let Alignment::Owned(_) = &ev.npc.alignment {
|
||||||
agent.behavior.allow(BehaviorCapability::TRADE);
|
agent.behavior.allow(BehaviorCapability::TRADE);
|
||||||
agent.behavior.trading_behavior = TradingBehavior::AcceptFood;
|
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)
|
entity.with(agent)
|
||||||
} else {
|
} else {
|
||||||
entity
|
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))
|
entity.with(ItemDrops(drop_items))
|
||||||
} else {
|
} else {
|
||||||
entity
|
entity
|
||||||
};
|
};
|
||||||
|
|
||||||
let entity = if let Some(home_chunk) = npc.anchor {
|
let entity = if let Some(home_chunk) = ev.npc.anchor {
|
||||||
entity.with(home_chunk)
|
entity.with(home_chunk)
|
||||||
} else {
|
} else {
|
||||||
entity
|
entity
|
||||||
};
|
};
|
||||||
|
|
||||||
// Rtsim entity added to IdMaps below.
|
// 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 {
|
entity.with(rtsim_entity).with(RepositionOnChunkLoad {
|
||||||
needs_ground: false,
|
needs_ground: false,
|
||||||
})
|
})
|
||||||
@ -156,7 +150,7 @@ pub fn handle_create_npc(
|
|||||||
entity
|
entity
|
||||||
};
|
};
|
||||||
|
|
||||||
let entity = if let Some(projectile) = npc.projectile {
|
let entity = if let Some(projectile) = ev.npc.projectile {
|
||||||
entity.with(projectile)
|
entity.with(projectile)
|
||||||
} else {
|
} else {
|
||||||
entity
|
entity
|
||||||
@ -164,7 +158,7 @@ pub fn handle_create_npc(
|
|||||||
|
|
||||||
let new_entity = entity.build();
|
let new_entity = entity.build();
|
||||||
|
|
||||||
if let Some(rtsim_entity) = npc.rtsim_entity {
|
if let Some(rtsim_entity) = ev.npc.rtsim_entity {
|
||||||
server
|
server
|
||||||
.state()
|
.state()
|
||||||
.ecs()
|
.ecs()
|
||||||
@ -173,7 +167,7 @@ pub fn handle_create_npc(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add to group system if a pet
|
// 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 state = server.state();
|
||||||
let uids = state.ecs().read_storage::<Uid>();
|
let uids = state.ecs().read_storage::<Uid>();
|
||||||
let clients = state.ecs().read_storage::<Client>();
|
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::Wild => None,
|
||||||
Alignment::Passive => None,
|
Alignment::Passive => None,
|
||||||
Alignment::Enemy => Some(comp::group::ENEMY),
|
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);
|
let _ = server.state.ecs().write_storage().insert(new_entity, group);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(rider) = rider {
|
if let Some(rider) = ev.rider {
|
||||||
let rider_entity = handle_create_npc(server, pos, Ori::default(), rider, None);
|
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 uids = server.state().ecs().read_storage::<Uid>();
|
||||||
let link = Mounting {
|
let link = Mounting {
|
||||||
mount: *uids.get(new_entity).expect("We just created this entity"),
|
mount: *uids.get(new_entity).expect("We just created this entity"),
|
||||||
@ -231,21 +230,13 @@ pub fn handle_create_npc(
|
|||||||
new_entity
|
new_entity
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_create_ship(
|
pub fn handle_create_ship(server: &mut Server, ev: CreateShipEvent) {
|
||||||
server: &mut Server,
|
let collider = ev.ship.make_collider();
|
||||||
pos: Pos,
|
|
||||||
ori: Ori,
|
|
||||||
ship: comp::ship::Body,
|
|
||||||
rtsim_entity: Option<RtSimEntity>,
|
|
||||||
driver: Option<NpcBuilder>,
|
|
||||||
passengers: Vec<NpcBuilder>,
|
|
||||||
) {
|
|
||||||
let collider = ship.make_collider();
|
|
||||||
let voxel_colliders_manifest = VOXEL_COLLIDER_MANIFEST.read();
|
let voxel_colliders_manifest = VOXEL_COLLIDER_MANIFEST.read();
|
||||||
|
|
||||||
// TODO: Find better solution for this, maybe something like a serverside block
|
// TODO: Find better solution for this, maybe something like a serverside block
|
||||||
// of interests.
|
// of interests.
|
||||||
let (mut steering, mut seats) = {
|
let (mut steering, mut _seats) = {
|
||||||
let mut steering = Vec::new();
|
let mut steering = Vec::new();
|
||||||
let mut seats = Vec::new();
|
let mut seats = Vec::new();
|
||||||
|
|
||||||
@ -263,7 +254,9 @@ pub fn handle_create_ship(
|
|||||||
(steering.into_iter(), seats.into_iter())
|
(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 {
|
if let Some(mut agent) = agent {
|
||||||
let (kp, ki, kd) = pid_coefficients(&Body::Ship(ship));
|
let (kp, ki, kd) = pid_coefficients(&Body::Ship(ship));
|
||||||
@ -273,13 +266,18 @@ pub fn handle_create_ship(
|
|||||||
entity = entity.with(agent);
|
entity = entity.with(agent);
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
if let Some(rtsim_vehicle) = rtsim_entity {
|
if let Some(rtsim_vehicle) = ev.rtsim_entity {
|
||||||
entity = entity.with(rtsim_vehicle);
|
entity = entity.with(rtsim_vehicle);
|
||||||
}
|
}
|
||||||
let entity = entity.build();
|
let entity = entity.build();
|
||||||
|
|
||||||
if let Some(driver) = driver {
|
if let Some(driver) = ev.driver {
|
||||||
let npc_entity = handle_create_npc(server, pos, ori, driver, None);
|
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 uids = server.state.ecs().read_storage::<Uid>();
|
||||||
let (rider_uid, mount_uid) = uids
|
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(
|
for passenger in ev.passengers {
|
||||||
server,
|
let npc_entity = handle_create_npc(server, CreateNpcEvent {
|
||||||
Pos(pos.0 + Vec3::unit_z() * 5.0),
|
pos: Pos(ev.pos.0 + Vec3::unit_z() * 5.0),
|
||||||
ori,
|
ori: ev.ori,
|
||||||
passenger,
|
npc: passenger,
|
||||||
None,
|
rider: None,
|
||||||
);
|
});
|
||||||
if let Some((rider_pos, rider_block)) = seats.next() {
|
if let Some((rider_pos, rider_block)) = seats.next() {
|
||||||
let uids = server.state.ecs().read_storage::<Uid>();
|
let uids = server.state.ecs().read_storage::<Uid>();
|
||||||
let (rider_uid, mount_uid) = uids
|
let (rider_uid, mount_uid) = uids
|
||||||
@ -342,62 +340,54 @@ pub fn handle_create_ship(
|
|||||||
.expect("Failed to link passanger to ship");
|
.expect("Failed to link passanger to ship");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_shoot(
|
pub fn handle_shoot(server: &mut Server, ev: ShootEvent) {
|
||||||
server: &mut Server,
|
|
||||||
entity: EcsEntity,
|
|
||||||
pos: Pos,
|
|
||||||
dir: Dir,
|
|
||||||
body: Body,
|
|
||||||
light: Option<LightEmitter>,
|
|
||||||
projectile: Projectile,
|
|
||||||
speed: f32,
|
|
||||||
object: Option<Object>,
|
|
||||||
) {
|
|
||||||
let state = server.state_mut();
|
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
|
+ state
|
||||||
.ecs()
|
.ecs()
|
||||||
.read_storage::<Vel>()
|
.read_storage::<Vel>()
|
||||||
.get(entity)
|
.get(ev.entity)
|
||||||
.map_or(Vec3::zero(), |v| v.0);
|
.map_or(Vec3::zero(), |v| v.0);
|
||||||
|
|
||||||
// Add an outcome
|
// Add an outcome
|
||||||
state
|
state
|
||||||
.ecs()
|
.ecs()
|
||||||
.read_resource::<EventBus<Outcome>>()
|
.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);
|
let mut builder = state.create_projectile(Pos(pos), Vel(vel), ev.body, ev.projectile);
|
||||||
if let Some(light) = light {
|
if let Some(light) = ev.light {
|
||||||
builder = builder.with(light)
|
builder = builder.with(light)
|
||||||
}
|
}
|
||||||
if let Some(object) = object {
|
if let Some(object) = ev.object {
|
||||||
builder = builder.with(object)
|
builder = builder.with(object)
|
||||||
}
|
}
|
||||||
|
|
||||||
builder.build();
|
builder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_shockwave(
|
pub fn handle_shockwave(server: &mut Server, ev: ShockwaveEvent) {
|
||||||
server: &mut Server,
|
|
||||||
properties: shockwave::Properties,
|
|
||||||
pos: Pos,
|
|
||||||
ori: Ori,
|
|
||||||
) {
|
|
||||||
let state = server.state_mut();
|
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();
|
let time = server.state.get_time();
|
||||||
server
|
server
|
||||||
.state
|
.state
|
||||||
.create_object(Pos(pos), comp::object::Body::CampfireLit)
|
.create_object(Pos(ev.0), comp::object::Body::CampfireLit)
|
||||||
.with(LightEmitter {
|
.with(LightEmitter {
|
||||||
col: Rgb::new(1.0, 0.3, 0.1),
|
col: Rgb::new(1.0, 0.3, 0.1),
|
||||||
strength: 5.0,
|
strength: 5.0,
|
||||||
@ -435,9 +425,9 @@ pub fn handle_create_waypoint(server: &mut Server, pos: Vec3<f32>) {
|
|||||||
.build();
|
.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
|
server
|
||||||
.state
|
.state
|
||||||
.create_teleporter(comp::Pos(pos), portal)
|
.create_teleporter(comp::Pos(ev.0), ev.1)
|
||||||
.build();
|
.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::{
|
use common::{
|
||||||
comp::{
|
comp::{
|
||||||
self,
|
self,
|
||||||
@ -6,26 +6,24 @@ use common::{
|
|||||||
invite::{InviteKind, PendingInvites},
|
invite::{InviteKind, PendingInvites},
|
||||||
ChatType, GroupManip,
|
ChatType, GroupManip,
|
||||||
},
|
},
|
||||||
uid::Uid,
|
event::GroupManipEvent,
|
||||||
};
|
uid::{IdMaps, Uid},
|
||||||
use common_net::{msg::ServerGeneral, sync::WorldSyncExt};
|
|
||||||
use common_state::State;
|
|
||||||
use specs::{
|
|
||||||
world::{Entity, WorldExt},
|
|
||||||
ReadStorage, WriteStorage,
|
|
||||||
};
|
};
|
||||||
|
use common_net::msg::ServerGeneral;
|
||||||
|
use specs::{world::Entity, Entities, ReadExpect, ReadStorage, WriteExpect, WriteStorage};
|
||||||
|
|
||||||
|
use super::ServerEvent;
|
||||||
|
|
||||||
pub fn can_invite(
|
pub fn can_invite(
|
||||||
state: &State,
|
|
||||||
clients: &ReadStorage<'_, Client>,
|
clients: &ReadStorage<'_, Client>,
|
||||||
|
groups: &ReadStorage<'_, Group>,
|
||||||
|
group_manager: &GroupManager,
|
||||||
pending_invites: &mut WriteStorage<'_, PendingInvites>,
|
pending_invites: &mut WriteStorage<'_, PendingInvites>,
|
||||||
max_group_size: u32,
|
max_group_size: u32,
|
||||||
inviter: Entity,
|
inviter: Entity,
|
||||||
invitee: Entity,
|
invitee: Entity,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
// Disallow inviting entity that is already in your group
|
// 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| {
|
let already_in_same_group = groups.get(inviter).map_or(false, |group| {
|
||||||
group_manager
|
group_manager
|
||||||
.group_info(*group)
|
.group_info(*group)
|
||||||
@ -45,9 +43,7 @@ pub fn can_invite(
|
|||||||
|
|
||||||
// Check if group max size is already reached
|
// Check if group max size is already reached
|
||||||
// Adding the current number of pending invites
|
// Adding the current number of pending invites
|
||||||
let group_size_limit_reached = state
|
let group_size_limit_reached = groups
|
||||||
.ecs()
|
|
||||||
.read_storage()
|
|
||||||
.get(inviter)
|
.get(inviter)
|
||||||
.copied()
|
.copied()
|
||||||
.and_then(|group| {
|
.and_then(|group| {
|
||||||
@ -112,94 +108,31 @@ pub fn update_map_markers<'a>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: turn chat messages into enums
|
impl ServerEvent for GroupManipEvent {
|
||||||
pub fn handle_group(server: &mut Server, entity: Entity, manip: GroupManip) {
|
type SystemData<'a> = (
|
||||||
match manip {
|
Entities<'a>,
|
||||||
GroupManip::Leave => {
|
WriteExpect<'a, GroupManager>,
|
||||||
let state = server.state_mut();
|
ReadExpect<'a, IdMaps>,
|
||||||
let clients = state.ecs().read_storage::<Client>();
|
WriteStorage<'a, Group>,
|
||||||
let uids = state.ecs().read_storage::<Uid>();
|
ReadStorage<'a, Client>,
|
||||||
let mut group_manager = state.ecs().write_resource::<GroupManager>();
|
ReadStorage<'a, Uid>,
|
||||||
let map_markers = state.ecs().read_storage::<comp::MapMarker>();
|
ReadStorage<'a, comp::Alignment>,
|
||||||
group_manager.leave_group(
|
ReadStorage<'a, comp::MapMarker>,
|
||||||
entity,
|
);
|
||||||
&mut state.ecs().write_storage(),
|
|
||||||
&state.ecs().read_storage(),
|
|
||||||
&uids,
|
|
||||||
&state.ecs().entities(),
|
|
||||||
&mut |entity, group_change| {
|
|
||||||
clients
|
|
||||||
.get(entity)
|
|
||||||
.and_then(|c| {
|
|
||||||
group_change
|
|
||||||
.try_map_ref(|e| uids.get(*e).copied())
|
|
||||||
.map(|g| (g, c))
|
|
||||||
})
|
|
||||||
.map(|(g, c)| {
|
|
||||||
update_map_markers(&map_markers, &uids, c, &group_change);
|
|
||||||
c.send_fallible(ServerGeneral::GroupUpdate(g));
|
|
||||||
});
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
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) {
|
fn handle(
|
||||||
Some(t) => t,
|
events: impl ExactSizeIterator<Item = Self>,
|
||||||
None => {
|
(entities, mut group_manager, id_maps, mut groups, clients, uids, alignments, map_markers): Self::SystemData<'_>,
|
||||||
// Inform of failure
|
) {
|
||||||
if let Some(client) = clients.get(entity) {
|
for GroupManipEvent(entity, manip) in events {
|
||||||
client.send_fallible(ServerGeneral::server_msg(
|
match manip {
|
||||||
ChatType::Meta,
|
GroupManip::Leave => {
|
||||||
"Kick failed, target does not exist.",
|
|
||||||
));
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
// Can't kick pet
|
|
||||||
if matches!(alignments.get(target), Some(comp::Alignment::Owned(owner)) if uids.get(target).map_or(true, |u| u != owner))
|
|
||||||
{
|
|
||||||
if let Some(general_stream) = clients.get(entity) {
|
|
||||||
general_stream.send_fallible(ServerGeneral::server_msg(
|
|
||||||
ChatType::Meta,
|
|
||||||
"Kick failed, you can't kick pets.",
|
|
||||||
));
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Can't kick yourself
|
|
||||||
if uids.get(entity).map_or(false, |u| *u == uid) {
|
|
||||||
if let Some(client) = clients.get(entity) {
|
|
||||||
client.send_fallible(ServerGeneral::server_msg(
|
|
||||||
ChatType::Meta,
|
|
||||||
"Kick failed, you can't kick yourself.",
|
|
||||||
));
|
|
||||||
}
|
|
||||||
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)
|
|
||||||
.and_then(|group| group_manager.group_info(*group))
|
|
||||||
{
|
|
||||||
Some(info) if info.leader == entity => {
|
|
||||||
// Remove target from group
|
|
||||||
group_manager.leave_group(
|
group_manager.leave_group(
|
||||||
target,
|
entity,
|
||||||
&mut groups,
|
&mut groups,
|
||||||
&state.ecs().read_storage(),
|
&alignments,
|
||||||
&uids,
|
&uids,
|
||||||
&state.ecs().entities(),
|
&entities,
|
||||||
&mut |entity, group_change| {
|
&mut |entity, group_change| {
|
||||||
clients
|
clients
|
||||||
.get(entity)
|
.get(entity)
|
||||||
@ -214,123 +147,195 @@ pub fn handle_group(server: &mut Server, entity: Entity, manip: GroupManip) {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
// Tell them the have been kicked
|
|
||||||
if let Some(client) = clients.get(target) {
|
|
||||||
client.send_fallible(ServerGeneral::server_msg(
|
|
||||||
ChatType::Meta,
|
|
||||||
"You were removed from the group.",
|
|
||||||
));
|
|
||||||
}
|
|
||||||
// Tell kicker that they were successful
|
|
||||||
if let Some(client) = clients.get(entity) {
|
|
||||||
client.send_fallible(ServerGeneral::server_msg(
|
|
||||||
ChatType::Meta,
|
|
||||||
"Player kicked.",
|
|
||||||
));
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
Some(_) => {
|
GroupManip::Kick(uid) => {
|
||||||
// Inform kicker that they are not the leader
|
let target = match id_maps.uid_entity(uid) {
|
||||||
if let Some(client) = clients.get(entity) {
|
Some(t) => t,
|
||||||
client.send_fallible(ServerGeneral::server_msg(
|
None => {
|
||||||
ChatType::Meta,
|
// Inform of failure
|
||||||
"Kick failed: You are not the leader of the target's group.",
|
if let Some(client) = clients.get(entity) {
|
||||||
));
|
client.send_fallible(ServerGeneral::server_msg(
|
||||||
}
|
ChatType::Meta,
|
||||||
},
|
"Kick failed, target does not exist.",
|
||||||
None => {
|
));
|
||||||
// Inform kicker that the target is not in a group
|
}
|
||||||
if let Some(client) = clients.get(entity) {
|
return;
|
||||||
client.send_fallible(ServerGeneral::server_msg(
|
|
||||||
ChatType::Meta,
|
|
||||||
"Kick failed: Your target is not in a group.",
|
|
||||||
));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
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) {
|
|
||||||
Some(t) => t,
|
|
||||||
None => {
|
|
||||||
// Inform of failure
|
|
||||||
if let Some(client) = clients.get(entity) {
|
|
||||||
client.send_fallible(ServerGeneral::server_msg(
|
|
||||||
ChatType::Meta,
|
|
||||||
"Leadership transfer failed, target does not exist",
|
|
||||||
));
|
|
||||||
}
|
|
||||||
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)
|
|
||||||
.and_then(|group| group_manager.group_info(*group))
|
|
||||||
{
|
|
||||||
Some(info) if info.leader == entity => {
|
|
||||||
// Assign target as group leader
|
|
||||||
group_manager.assign_leader(
|
|
||||||
target,
|
|
||||||
&groups,
|
|
||||||
&state.ecs().entities(),
|
|
||||||
&state.ecs().read_storage(),
|
|
||||||
&uids,
|
|
||||||
|entity, group_change| {
|
|
||||||
clients
|
|
||||||
.get(entity)
|
|
||||||
.and_then(|c| {
|
|
||||||
group_change
|
|
||||||
.try_map_ref(|e| uids.get(*e).copied())
|
|
||||||
.map(|g| (g, c))
|
|
||||||
})
|
|
||||||
.map(|(g, c)| {
|
|
||||||
update_map_markers(&map_markers, &uids, c, &group_change);
|
|
||||||
c.send_fallible(ServerGeneral::GroupUpdate(g));
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
);
|
};
|
||||||
// Tell them they are the leader
|
|
||||||
if let Some(client) = clients.get(target) {
|
// Can't kick pet
|
||||||
client.send_fallible(ServerGeneral::server_msg(
|
if matches!(alignments.get(target), Some(comp::Alignment::Owned(owner)) if uids.get(target).map_or(true, |u| u != owner))
|
||||||
ChatType::Meta,
|
{
|
||||||
"You are the group leader now.",
|
if let Some(general_stream) = clients.get(entity) {
|
||||||
));
|
general_stream.send_fallible(ServerGeneral::server_msg(
|
||||||
|
ChatType::Meta,
|
||||||
|
"Kick failed, you can't kick pets.",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
// Tell the old leader that the transfer was succesful
|
// Can't kick yourself
|
||||||
if let Some(client) = clients.get(entity) {
|
if uids.get(entity).map_or(false, |u| *u == uid) {
|
||||||
client.send_fallible(ServerGeneral::server_msg(
|
if let Some(client) = clients.get(entity) {
|
||||||
ChatType::Meta,
|
client.send_fallible(ServerGeneral::server_msg(
|
||||||
"You are no longer the group leader.",
|
ChatType::Meta,
|
||||||
));
|
"Kick failed, you can't kick yourself.",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure kicker is the group leader
|
||||||
|
match groups
|
||||||
|
.get(target)
|
||||||
|
.and_then(|group| group_manager.group_info(*group))
|
||||||
|
{
|
||||||
|
Some(info) if info.leader == entity => {
|
||||||
|
// Remove target from group
|
||||||
|
group_manager.leave_group(
|
||||||
|
target,
|
||||||
|
&mut groups,
|
||||||
|
&alignments,
|
||||||
|
&uids,
|
||||||
|
&entities,
|
||||||
|
&mut |entity, group_change| {
|
||||||
|
clients
|
||||||
|
.get(entity)
|
||||||
|
.and_then(|c| {
|
||||||
|
group_change
|
||||||
|
.try_map_ref(|e| uids.get(*e).copied())
|
||||||
|
.map(|g| (g, c))
|
||||||
|
})
|
||||||
|
.map(|(g, c)| {
|
||||||
|
update_map_markers(
|
||||||
|
&map_markers,
|
||||||
|
&uids,
|
||||||
|
c,
|
||||||
|
&group_change,
|
||||||
|
);
|
||||||
|
c.send_fallible(ServerGeneral::GroupUpdate(g));
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// Tell them the have been kicked
|
||||||
|
if let Some(client) = clients.get(target) {
|
||||||
|
client.send_fallible(ServerGeneral::server_msg(
|
||||||
|
ChatType::Meta,
|
||||||
|
"You were removed from the group.",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
// Tell kicker that they were successful
|
||||||
|
if let Some(client) = clients.get(entity) {
|
||||||
|
client.send_fallible(ServerGeneral::server_msg(
|
||||||
|
ChatType::Meta,
|
||||||
|
"Player kicked.",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Some(_) => {
|
||||||
|
// Inform kicker that they are not the leader
|
||||||
|
if let Some(client) = clients.get(entity) {
|
||||||
|
client.send_fallible(ServerGeneral::server_msg(
|
||||||
|
ChatType::Meta,
|
||||||
|
"Kick failed: You are not the leader of the target's group.",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
// Inform kicker that the target is not in a group
|
||||||
|
if let Some(client) = clients.get(entity) {
|
||||||
|
client.send_fallible(ServerGeneral::server_msg(
|
||||||
|
ChatType::Meta,
|
||||||
|
"Kick failed: Your target is not in a group.",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Some(_) => {
|
GroupManip::AssignLeader(uid) => {
|
||||||
// Inform transferer that they are not the leader
|
let target = match id_maps.uid_entity(uid) {
|
||||||
if let Some(client) = clients.get(entity) {
|
Some(t) => t,
|
||||||
client.send_fallible(ServerGeneral::server_msg(
|
None => {
|
||||||
ChatType::Meta,
|
// Inform of failure
|
||||||
"Transfer failed: You are not the leader of the target's group.",
|
if let Some(client) = clients.get(entity) {
|
||||||
));
|
client.send_fallible(ServerGeneral::server_msg(
|
||||||
}
|
ChatType::Meta,
|
||||||
},
|
"Leadership transfer failed, target does not exist",
|
||||||
None => {
|
));
|
||||||
// Inform transferer that the target is not in a group
|
}
|
||||||
if let Some(client) = clients.get(entity) {
|
return;
|
||||||
client.send_fallible(ServerGeneral::server_msg(
|
},
|
||||||
ChatType::Meta,
|
};
|
||||||
"Transfer failed: Your target is not in a group.",
|
// Make sure assigner is the group leader
|
||||||
));
|
match groups
|
||||||
|
.get(target)
|
||||||
|
.and_then(|group| group_manager.group_info(*group))
|
||||||
|
{
|
||||||
|
Some(info) if info.leader == entity => {
|
||||||
|
// Assign target as group leader
|
||||||
|
group_manager.assign_leader(
|
||||||
|
target,
|
||||||
|
&groups,
|
||||||
|
&entities,
|
||||||
|
&alignments,
|
||||||
|
&uids,
|
||||||
|
|entity, group_change| {
|
||||||
|
clients
|
||||||
|
.get(entity)
|
||||||
|
.and_then(|c| {
|
||||||
|
group_change
|
||||||
|
.try_map_ref(|e| uids.get(*e).copied())
|
||||||
|
.map(|g| (g, c))
|
||||||
|
})
|
||||||
|
.map(|(g, c)| {
|
||||||
|
update_map_markers(
|
||||||
|
&map_markers,
|
||||||
|
&uids,
|
||||||
|
c,
|
||||||
|
&group_change,
|
||||||
|
);
|
||||||
|
c.send_fallible(ServerGeneral::GroupUpdate(g));
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
// Tell them they are the leader
|
||||||
|
if let Some(client) = clients.get(target) {
|
||||||
|
client.send_fallible(ServerGeneral::server_msg(
|
||||||
|
ChatType::Meta,
|
||||||
|
"You are the group leader now.",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
// Tell the old leader that the transfer was succesful
|
||||||
|
if let Some(client) = clients.get(entity) {
|
||||||
|
client.send_fallible(ServerGeneral::server_msg(
|
||||||
|
ChatType::Meta,
|
||||||
|
"You are no longer the group leader.",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Some(_) => {
|
||||||
|
// Inform transferer that they are not the leader
|
||||||
|
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.",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
// Inform transferer that the target is not in a group
|
||||||
|
if let Some(client) = clients.get(entity) {
|
||||||
|
client.send_fallible(ServerGeneral::server_msg(
|
||||||
|
ChatType::Meta,
|
||||||
|
"Transfer failed: Your target is not in a group.",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,52 +1,62 @@
|
|||||||
use crate::{client::Client, Server};
|
use crate::client::Client;
|
||||||
|
use common::event::RequestSiteInfoEvent;
|
||||||
use common_net::msg::{world_msg::EconomyInfo, ServerGeneral};
|
use common_net::msg::{world_msg::EconomyInfo, ServerGeneral};
|
||||||
use specs::{Entity as EcsEntity, WorldExt};
|
use specs::{ReadExpect, ReadStorage};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use world::IndexOwned;
|
||||||
|
|
||||||
|
use super::ServerEvent;
|
||||||
|
|
||||||
#[cfg(not(feature = "worldgen"))]
|
#[cfg(not(feature = "worldgen"))]
|
||||||
pub fn handle_site_info(server: &Server, entity: EcsEntity, id: u64) {
|
impl ServerEvent for RequestSiteInfoEvent {
|
||||||
let info = EconomyInfo {
|
type SystemData<'a> = ReadStorage<'a, Client>;
|
||||||
id,
|
|
||||||
population: 0,
|
fn handle(events: impl ExactSizeIterator<Item = Self>, clients: Self::SystemData<'_>) {
|
||||||
stock: HashMap::new(),
|
for ev in events {
|
||||||
labor_values: HashMap::new(),
|
if let Some(client) = clients.get(ev.entity) {
|
||||||
values: HashMap::new(),
|
let info = EconomyInfo {
|
||||||
labors: Vec::new(),
|
id: ev.id,
|
||||||
last_exports: HashMap::new(),
|
population: 0,
|
||||||
resources: HashMap::new(),
|
stock: HashMap::new(),
|
||||||
};
|
labor_values: HashMap::new(),
|
||||||
let msg = ServerGeneral::SiteEconomy(info);
|
values: HashMap::new(),
|
||||||
server
|
labors: Vec::new(),
|
||||||
.state
|
last_exports: HashMap::new(),
|
||||||
.ecs()
|
resources: HashMap::new(),
|
||||||
.read_storage::<Client>()
|
};
|
||||||
.get(entity)
|
let msg = ServerGeneral::SiteEconomy(info);
|
||||||
.map(|c| c.send(msg));
|
client.send_fallible(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "worldgen")]
|
#[cfg(feature = "worldgen")]
|
||||||
pub fn handle_site_info(server: &Server, entity: EcsEntity, id: u64) {
|
impl ServerEvent for RequestSiteInfoEvent {
|
||||||
let site_id = server.index.sites.recreate_id(id);
|
type SystemData<'a> = (ReadExpect<'a, IndexOwned>, ReadStorage<'a, Client>);
|
||||||
let info = if let Some(site_id) = site_id {
|
|
||||||
let site = server.index.sites.get(site_id);
|
fn handle(events: impl ExactSizeIterator<Item = Self>, (index, clients): Self::SystemData<'_>) {
|
||||||
site.economy.get_information(site_id)
|
for ev in events {
|
||||||
} else {
|
if let Some(client) = clients.get(ev.entity) {
|
||||||
EconomyInfo {
|
let site_id = index.sites.recreate_id(ev.id);
|
||||||
id,
|
let info = if let Some(site_id) = site_id {
|
||||||
population: 0,
|
let site = index.sites.get(site_id);
|
||||||
stock: HashMap::new(),
|
site.economy.get_information(site_id)
|
||||||
labor_values: HashMap::new(),
|
} else {
|
||||||
values: HashMap::new(),
|
EconomyInfo {
|
||||||
labors: Vec::new(),
|
id: ev.id,
|
||||||
last_exports: HashMap::new(),
|
population: 0,
|
||||||
resources: HashMap::new(),
|
stock: HashMap::new(),
|
||||||
|
labor_values: HashMap::new(),
|
||||||
|
values: HashMap::new(),
|
||||||
|
labors: Vec::new(),
|
||||||
|
last_exports: HashMap::new(),
|
||||||
|
resources: HashMap::new(),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let msg = ServerGeneral::SiteEconomy(info);
|
||||||
|
client.send_fallible(msg);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
let msg = ServerGeneral::SiteEconomy(info);
|
|
||||||
server
|
|
||||||
.state
|
|
||||||
.ecs()
|
|
||||||
.read_storage::<Client>()
|
|
||||||
.get(entity)
|
|
||||||
.map(|c| c.send(msg));
|
|
||||||
}
|
}
|
||||||
|
@ -1,286 +1,169 @@
|
|||||||
use specs::{world::WorldExt, Entity as EcsEntity, Join};
|
use common_state::{BlockChange, ScheduledBlockChange};
|
||||||
|
use specs::{DispatcherBuilder, Join, ReadExpect, ReadStorage, WriteExpect, WriteStorage};
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
use common::{
|
use common::{
|
||||||
assets::{self, Concatenate},
|
assets::{self, Concatenate},
|
||||||
comp::{
|
comp::{
|
||||||
self,
|
self,
|
||||||
agent::{AgentEvent, Sound, SoundKind},
|
agent::{AgentEvent, SoundKind},
|
||||||
dialogue::Subject,
|
|
||||||
inventory::slot::EquipSlot,
|
inventory::slot::EquipSlot,
|
||||||
item::{flatten_counted_items, MaterialStatManifest},
|
item::{flatten_counted_items, MaterialStatManifest},
|
||||||
loot_owner::LootOwnerKind,
|
loot_owner::LootOwnerKind,
|
||||||
pet::is_mountable,
|
tool::AbilityMap,
|
||||||
tool::{AbilityMap, ToolKind},
|
|
||||||
Inventory, LootOwner, Pos, SkillGroupKind,
|
|
||||||
},
|
},
|
||||||
consts::{
|
consts::{MAX_INTERACT_RANGE, MAX_NPCINTERACT_RANGE, SOUND_TRAVEL_DIST_PER_VOLUME},
|
||||||
MAX_INTERACT_RANGE, MAX_MOUNT_RANGE, MAX_NPCINTERACT_RANGE, MAX_SPRITE_MOUNT_RANGE,
|
event::{
|
||||||
SOUND_TRAVEL_DIST_PER_VOLUME,
|
CreateItemDropEvent, CreateSpriteEvent, EventBus, MineBlockEvent, NpcInteractEvent,
|
||||||
|
SetLanternEvent, SetPetStayEvent, SoundEvent, TamePetEvent, ToggleSpriteLightEvent,
|
||||||
},
|
},
|
||||||
event::EventBus,
|
|
||||||
link::Is,
|
link::Is,
|
||||||
mounting::{Mount, Mounting, Rider, VolumeMounting, VolumePos, VolumeRider},
|
mounting::Mount,
|
||||||
outcome::Outcome,
|
outcome::Outcome,
|
||||||
rtsim::RtSimEntity,
|
terrain::{Block, SpriteKind, TerrainGrid},
|
||||||
terrain::{Block, SpriteKind},
|
uid::Uid,
|
||||||
uid::{IdMaps, Uid},
|
util::Dir,
|
||||||
vol::ReadVol,
|
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 crate::pet::tame_pet;
|
||||||
use hashbrown::{HashMap, HashSet};
|
use hashbrown::{HashMap, HashSet};
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use std::{iter::FromIterator, sync::Arc};
|
use std::iter::FromIterator;
|
||||||
|
|
||||||
pub fn handle_lantern(server: &mut Server, entity: EcsEntity, enable: bool) {
|
use super::{event_dispatch, mounting::within_mounting_range, ServerEvent};
|
||||||
let ecs = server.state_mut().ecs();
|
|
||||||
|
|
||||||
let lantern_exists = ecs
|
pub(super) fn register_event_systems(builder: &mut DispatcherBuilder) {
|
||||||
.read_storage::<comp::LightEmitter>()
|
event_dispatch::<SetLanternEvent>(builder);
|
||||||
.get(entity)
|
event_dispatch::<NpcInteractEvent>(builder);
|
||||||
.map_or(false, |light| light.strength > 0.0);
|
event_dispatch::<SetPetStayEvent>(builder);
|
||||||
|
event_dispatch::<MineBlockEvent>(builder);
|
||||||
|
event_dispatch::<SoundEvent>(builder);
|
||||||
|
event_dispatch::<CreateSpriteEvent>(builder);
|
||||||
|
event_dispatch::<ToggleSpriteLightEvent>(builder);
|
||||||
|
}
|
||||||
|
|
||||||
if lantern_exists != enable {
|
impl ServerEvent for SetLanternEvent {
|
||||||
if !enable {
|
type SystemData<'a> = (
|
||||||
server
|
WriteStorage<'a, comp::LightEmitter>,
|
||||||
.state_mut()
|
ReadStorage<'a, comp::Inventory>,
|
||||||
.ecs()
|
ReadStorage<'a, comp::Health>,
|
||||||
.write_storage::<comp::LightEmitter>()
|
);
|
||||||
.remove(entity);
|
|
||||||
} else if ecs // Only enable lantern if entity is alive
|
fn handle(
|
||||||
.read_storage::<comp::Health>()
|
events: impl ExactSizeIterator<Item = Self>,
|
||||||
.get(entity)
|
(mut light_emitters, inventories, healths): Self::SystemData<'_>,
|
||||||
.map_or(true, |h| !h.is_dead)
|
) {
|
||||||
{
|
for SetLanternEvent(entity, enable) in events {
|
||||||
let inventory_storage = ecs.read_storage::<Inventory>();
|
let lantern_exists = light_emitters
|
||||||
let lantern_info = inventory_storage
|
|
||||||
.get(entity)
|
.get(entity)
|
||||||
.and_then(|inventory| inventory.equipped(EquipSlot::Lantern))
|
.map_or(false, |light| light.strength > 0.0);
|
||||||
.and_then(|item| {
|
|
||||||
if let comp::item::ItemKind::Lantern(l) = &*item.kind() {
|
if lantern_exists != enable {
|
||||||
Some((l.color(), l.strength(), l.flicker()))
|
if !enable {
|
||||||
} else {
|
light_emitters.remove(entity);
|
||||||
None
|
}
|
||||||
}
|
// Only enable lantern if entity is alive
|
||||||
});
|
else if healths.get(entity).map_or(true, |h| !h.is_dead) {
|
||||||
if let Some((col, strength, flicker)) = lantern_info {
|
let lantern_info = inventories
|
||||||
let _ =
|
.get(entity)
|
||||||
ecs.write_storage::<comp::LightEmitter>()
|
.and_then(|inventory| inventory.equipped(EquipSlot::Lantern))
|
||||||
.insert(entity, comp::LightEmitter {
|
.and_then(|item| {
|
||||||
|
if let comp::item::ItemKind::Lantern(l) = &*item.kind() {
|
||||||
|
Some((l.color(), l.strength(), l.flicker()))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if let Some((col, strength, flicker)) = lantern_info {
|
||||||
|
let _ = light_emitters.insert(entity, comp::LightEmitter {
|
||||||
col,
|
col,
|
||||||
strength,
|
strength,
|
||||||
flicker,
|
flicker,
|
||||||
animated: true,
|
animated: true,
|
||||||
});
|
});
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn handle_npc_interaction(
|
}
|
||||||
server: &mut Server,
|
|
||||||
interactor: EcsEntity,
|
|
||||||
npc_entity: EcsEntity,
|
|
||||||
subject: Subject,
|
|
||||||
) {
|
|
||||||
let state = server.state_mut();
|
|
||||||
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)
|
|
||||||
})
|
|
||||||
};
|
|
||||||
|
|
||||||
if within_range
|
|
||||||
&& let Some(agent) = state
|
|
||||||
.ecs()
|
|
||||||
.write_storage::<comp::Agent>()
|
|
||||||
.get_mut(npc_entity)
|
|
||||||
&& agent.target.is_none()
|
|
||||||
{
|
|
||||||
if let Some(interactor_uid) = state.ecs().uid_from_entity(interactor) {
|
|
||||||
agent
|
|
||||||
.inbox
|
|
||||||
.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(),
|
|
||||||
);
|
|
||||||
|
|
||||||
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) {
|
impl ServerEvent for NpcInteractEvent {
|
||||||
let state = server.state_mut();
|
type SystemData<'a> = (
|
||||||
state.ecs().write_storage::<Is<Rider>>().remove(rider);
|
WriteStorage<'a, comp::Agent>,
|
||||||
state.ecs().write_storage::<Is<VolumeRider>>().remove(rider);
|
ReadStorage<'a, comp::Pos>,
|
||||||
}
|
ReadStorage<'a, Uid>,
|
||||||
|
);
|
||||||
|
|
||||||
pub fn handle_set_pet_stay(
|
fn handle(
|
||||||
server: &mut Server,
|
events: impl ExactSizeIterator<Item = Self>,
|
||||||
command_giver: EcsEntity,
|
(mut agents, positions, uids): Self::SystemData<'_>,
|
||||||
pet: EcsEntity,
|
) {
|
||||||
stay: bool,
|
for NpcInteractEvent(interactor, npc_entity, subject) in events {
|
||||||
) {
|
let within_range = {
|
||||||
let state = server.state_mut();
|
positions
|
||||||
let positions = state.ecs().read_storage::<Pos>();
|
.get(interactor)
|
||||||
let is_owner = state
|
.zip(positions.get(npc_entity))
|
||||||
.ecs()
|
.map_or(false, |(interactor_pos, npc_pos)| {
|
||||||
.uid_from_entity(command_giver)
|
interactor_pos.0.distance_squared(npc_pos.0)
|
||||||
.map_or(false, |owner_uid| {
|
<= MAX_NPCINTERACT_RANGE.powi(2)
|
||||||
matches!(
|
})
|
||||||
state
|
};
|
||||||
.ecs()
|
|
||||||
.read_storage::<comp::Alignment>()
|
|
||||||
.get(pet),
|
|
||||||
Some(comp::Alignment::Owned(pet_owner)) if *pet_owner == owner_uid,
|
|
||||||
)
|
|
||||||
});
|
|
||||||
|
|
||||||
let current_pet_position = positions.get(pet).copied();
|
if within_range && let Some(agent) = agents
|
||||||
let stay = stay && current_pet_position.is_some();
|
.get_mut(npc_entity) && agent.target.is_none()
|
||||||
if is_owner
|
{
|
||||||
&& within_mounting_range(positions.get(command_giver), positions.get(pet))
|
if let Some(interactor_uid) = uids.get(interactor) {
|
||||||
&& state.ecs().read_storage::<Is<Mount>>().get(pet).is_none()
|
agent
|
||||||
{
|
.inbox
|
||||||
state
|
.push_back(AgentEvent::Talk(*interactor_uid, subject));
|
||||||
.ecs()
|
}
|
||||||
.write_storage::<comp::CharacterActivity>()
|
}
|
||||||
.get_mut(pet)
|
}
|
||||||
.map(|mut activity| activity.is_pet_staying = stay);
|
|
||||||
state
|
|
||||||
.ecs()
|
|
||||||
.write_storage::<comp::Agent>()
|
|
||||||
.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 {
|
impl ServerEvent for SetPetStayEvent {
|
||||||
match (player_position, mount_position) {
|
type SystemData<'a> = (
|
||||||
(Some(ppos), Some(ipos)) => ppos.0.distance_squared(ipos.0) < MAX_MOUNT_RANGE.powi(2),
|
WriteStorage<'a, comp::Agent>,
|
||||||
_ => false,
|
WriteStorage<'a, comp::CharacterActivity>,
|
||||||
|
ReadStorage<'a, comp::Pos>,
|
||||||
|
ReadStorage<'a, comp::Alignment>,
|
||||||
|
ReadStorage<'a, Is<Mount>>,
|
||||||
|
ReadStorage<'a, 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!(
|
||||||
|
alignments.get(pet),
|
||||||
|
Some(comp::Alignment::Owned(pet_owner)) if *pet_owner == *owner_uid,
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
let current_pet_position = positions.get(pet).copied();
|
||||||
|
let stay = stay && current_pet_position.is_some();
|
||||||
|
if is_owner
|
||||||
|
&& within_mounting_range(positions.get(command_giver), positions.get(pet))
|
||||||
|
&& is_mounts.get(pet).is_none()
|
||||||
|
{
|
||||||
|
character_activities
|
||||||
|
.get_mut(pet)
|
||||||
|
.map(|mut activity| activity.is_pet_staying = stay);
|
||||||
|
agents
|
||||||
|
.get_mut(pet)
|
||||||
|
.map(|s| s.stay_pos = current_pet_position.filter(|_| stay));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -303,209 +186,244 @@ lazy_static! {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_mine_block(
|
impl ServerEvent for MineBlockEvent {
|
||||||
server: &mut Server,
|
type SystemData<'a> = (
|
||||||
entity: EcsEntity,
|
WriteExpect<'a, BlockChange>,
|
||||||
pos: Vec3<i32>,
|
ReadExpect<'a, TerrainGrid>,
|
||||||
tool: Option<ToolKind>,
|
ReadExpect<'a, MaterialStatManifest>,
|
||||||
) {
|
ReadExpect<'a, AbilityMap>,
|
||||||
let state = server.state_mut();
|
ReadExpect<'a, EventBus<CreateItemDropEvent>>,
|
||||||
if state.can_set_block(pos) {
|
ReadExpect<'a, EventBus<Outcome>>,
|
||||||
let block = state.terrain().get(pos).ok().copied();
|
WriteStorage<'a, comp::SkillSet>,
|
||||||
if let Some(block) = block.filter(|b| b.mine_tool().map_or(false, |t| Some(t) == tool)) {
|
ReadStorage<'a, Uid>,
|
||||||
// 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);
|
|
||||||
|
|
||||||
if let Some(mut skillset) = state
|
fn handle(
|
||||||
.ecs()
|
events: impl ExactSizeIterator<Item = Self>,
|
||||||
.write_storage::<comp::SkillSet>()
|
(
|
||||||
.get_mut(entity)
|
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))
|
||||||
{
|
{
|
||||||
if let (Some(tool), Some(uid), exp_reward @ 1..) = (
|
// Drop item if one is recoverable from the block
|
||||||
tool,
|
if let Some(items) = comp::Item::try_reclaim_from_block(block) {
|
||||||
maybe_uid,
|
let mut items: Vec<_> =
|
||||||
items
|
flatten_counted_items(&items, &ability_map, &msm).collect();
|
||||||
.iter()
|
let maybe_uid = uids.get(ev.entity).copied();
|
||||||
.filter_map(|item| {
|
|
||||||
item.item_definition_id().itemdef_id().and_then(|id| {
|
if let Some(mut skillset) = skill_sets.get_mut(ev.entity) {
|
||||||
RESOURCE_EXPERIENCE_MANIFEST.read().0.get(id).copied()
|
if let (Some(tool), Some(uid), exp_reward @ 1..) = (
|
||||||
})
|
ev.tool,
|
||||||
})
|
maybe_uid,
|
||||||
.sum(),
|
items
|
||||||
) {
|
.iter()
|
||||||
let skill_group = SkillGroupKind::Weapon(tool);
|
.filter_map(|item| {
|
||||||
let outcome_bus = state.ecs().read_resource::<EventBus<Outcome>>();
|
item.item_definition_id().itemdef_id().and_then(|id| {
|
||||||
if let Some(level_outcome) =
|
RESOURCE_EXPERIENCE_MANIFEST.read().0.get(id).copied()
|
||||||
skillset.add_experience(skill_group, exp_reward)
|
})
|
||||||
{
|
})
|
||||||
outcome_bus.emit_now(Outcome::SkillPointGain {
|
.sum(),
|
||||||
uid,
|
) {
|
||||||
skill_tree: skill_group,
|
let skill_group = comp::SkillGroupKind::Weapon(tool);
|
||||||
total_points: level_outcome,
|
if let Some(level_outcome) =
|
||||||
|
skillset.add_experience(skill_group, exp_reward)
|
||||||
|
{
|
||||||
|
outcome_emitter.emit(Outcome::SkillPointGain {
|
||||||
|
uid,
|
||||||
|
skill_tree: skill_group,
|
||||||
|
total_points: level_outcome,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
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};
|
||||||
|
|
||||||
|
let need_double_ore = |rng: &mut rand::rngs::ThreadRng| {
|
||||||
|
let chance_mod = f64::from(SKILL_MODIFIERS.mining_tree.ore_gain);
|
||||||
|
let skill_level = skillset
|
||||||
|
.skill_level(Skill::Pick(MiningSkill::OreGain))
|
||||||
|
.unwrap_or(0);
|
||||||
|
|
||||||
|
rng.gen_bool(chance_mod * f64::from(skill_level))
|
||||||
|
};
|
||||||
|
let need_double_gem = |rng: &mut rand::rngs::ThreadRng| {
|
||||||
|
let chance_mod = f64::from(SKILL_MODIFIERS.mining_tree.gem_gain);
|
||||||
|
let skill_level = skillset
|
||||||
|
.skill_level(Skill::Pick(MiningSkill::GemGain))
|
||||||
|
.unwrap_or(0);
|
||||||
|
|
||||||
|
rng.gen_bool(chance_mod * f64::from(skill_level))
|
||||||
|
};
|
||||||
|
for item in items.iter_mut() {
|
||||||
|
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))
|
||||||
|
});
|
||||||
|
|
||||||
|
if double_gain {
|
||||||
|
// Ignore non-stackable errors
|
||||||
|
let _ = item.increase_amount(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for item in items {
|
||||||
|
let loot_owner = maybe_uid
|
||||||
|
.map(LootOwnerKind::Player)
|
||||||
|
.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,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
outcome_bus.emit_now(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| {
|
block_change.set(ev.pos, block.into_vacant());
|
||||||
let chance_mod = f64::from(SKILL_MODIFIERS.mining_tree.ore_gain);
|
outcome_emitter.emit(Outcome::BreakBlock {
|
||||||
let skill_level = skillset
|
pos: ev.pos,
|
||||||
.skill_level(Skill::Pick(MiningSkill::OreGain))
|
color: block.get_color(),
|
||||||
.unwrap_or(0);
|
});
|
||||||
|
|
||||||
rng.gen_bool(chance_mod * f64::from(skill_level))
|
|
||||||
};
|
|
||||||
let need_double_gem = |rng: &mut rand::rngs::ThreadRng| {
|
|
||||||
let chance_mod = f64::from(SKILL_MODIFIERS.mining_tree.gem_gain);
|
|
||||||
let skill_level = skillset
|
|
||||||
.skill_level(Skill::Pick(MiningSkill::GemGain))
|
|
||||||
.unwrap_or(0);
|
|
||||||
|
|
||||||
rng.gen_bool(chance_mod * f64::from(skill_level))
|
|
||||||
};
|
|
||||||
for item in items.iter_mut() {
|
|
||||||
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))
|
|
||||||
});
|
|
||||||
|
|
||||||
if double_gain {
|
|
||||||
// Ignore non-stackable errors
|
|
||||||
let _ = item.increase_amount(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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()),
|
|
||||||
item,
|
|
||||||
loot_owner,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
state.set_block(pos, block.into_vacant());
|
|
||||||
state
|
|
||||||
.ecs()
|
|
||||||
.read_resource::<EventBus<Outcome>>()
|
|
||||||
.emit_now(Outcome::BreakBlock {
|
|
||||||
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>();
|
|
||||||
|
|
||||||
// TODO: Reduce the complexity of this problem by using spatial partitioning
|
|
||||||
// system
|
|
||||||
for (agent, agent_pos) in (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);
|
|
||||||
|
|
||||||
let vol_dropoff = agent_dist_sqrd / sound_travel_dist_sqrd * sound.vol;
|
|
||||||
let propagated_sound = sound.with_new_vol(sound.vol - vol_dropoff);
|
|
||||||
|
|
||||||
let can_hear_sound = propagated_sound.vol > 0.00;
|
|
||||||
let should_hear_sound = agent_dist_sqrd < agent.psyche.listen_dist.powi(2);
|
|
||||||
|
|
||||||
if can_hear_sound && should_hear_sound {
|
|
||||||
agent
|
|
||||||
.inbox
|
|
||||||
.push_back(AgentEvent::ServerSound(propagated_sound));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Attempt to turn this sound into an outcome to be received by frontends.
|
|
||||||
if let Some(outcome) = match sound.kind {
|
|
||||||
SoundKind::Utterance(kind, body) => Some(Outcome::Utterance {
|
|
||||||
kind,
|
|
||||||
pos: sound.pos,
|
|
||||||
body,
|
|
||||||
}),
|
|
||||||
_ => None,
|
|
||||||
} {
|
|
||||||
ecs.read_resource::<EventBus<Outcome>>().emit_now(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();
|
|
||||||
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);
|
|
||||||
// Remove sprite after del_timeout and offset if specified
|
|
||||||
if let Some((timeout, del_offset)) = 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 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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_tame_pet(server: &mut Server, pet_entity: EcsEntity, owner_entity: EcsEntity) {
|
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 (&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);
|
||||||
|
|
||||||
|
let vol_dropoff = agent_dist_sqrd / sound_travel_dist_sqrd * sound.vol;
|
||||||
|
let propagated_sound = sound.with_new_vol(sound.vol - vol_dropoff);
|
||||||
|
|
||||||
|
let can_hear_sound = propagated_sound.vol > 0.00;
|
||||||
|
let should_hear_sound = agent_dist_sqrd < agent.psyche.listen_dist.powi(2);
|
||||||
|
|
||||||
|
if can_hear_sound && should_hear_sound {
|
||||||
|
agent
|
||||||
|
.inbox
|
||||||
|
.push_back(AgentEvent::ServerSound(propagated_sound));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attempt to turn this sound into an outcome to be received by frontends.
|
||||||
|
if let Some(outcome) = match sound.kind {
|
||||||
|
SoundKind::Utterance(kind, body) => Some(Outcome::Utterance {
|
||||||
|
kind,
|
||||||
|
pos: sound.pos,
|
||||||
|
body,
|
||||||
|
}),
|
||||||
|
_ => None,
|
||||||
|
} {
|
||||||
|
outcome_emitter.emit(outcome);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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(ev.sprite);
|
||||||
|
block_change.set(ev.pos, new_block);
|
||||||
|
// Remove sprite after del_timeout and offset if specified
|
||||||
|
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 = time.0;
|
||||||
|
let replace_time = current_time + (timeout + offset) as f64;
|
||||||
|
if old_block != new_block {
|
||||||
|
scheduled_block_change.set(ev.pos, old_block, replace_time);
|
||||||
|
scheduled_block_change.outcome_set(ev.pos, new_block, replace_time);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ServerEvent for ToggleSpriteLightEvent {
|
||||||
|
type SystemData<'a> = (
|
||||||
|
WriteExpect<'a, BlockChange>,
|
||||||
|
ReadExpect<'a, TerrainGrid>,
|
||||||
|
ReadStorage<'a, comp::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) = terrain.get(ev.pos).ok().and_then(|block| block.with_toggle_light(ev.enable)) {
|
||||||
|
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
|
// TODO: Raise outcome to send to clients to play sound/render an indicator
|
||||||
// showing taming success?
|
// showing taming success?
|
||||||
tame_pet(server.state.ecs(), pet_entity, owner_entity);
|
tame_pet(server.state.ecs(), ev.pet_entity, ev.owner_entity);
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
if let Some(new_block) = state
|
|
||||||
.terrain()
|
|
||||||
.get(pos)
|
|
||||||
.ok()
|
|
||||||
.and_then(|block| block.with_toggle_light(enable))
|
|
||||||
{
|
|
||||||
state.set_block(pos, new_block);
|
|
||||||
// TODO: Emit outcome
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,23 +1,27 @@
|
|||||||
use super::group_manip::{self, update_map_markers};
|
use super::{
|
||||||
use crate::{client::Client, Server};
|
event_dispatch,
|
||||||
|
group_manip::{self, update_map_markers},
|
||||||
|
ServerEvent,
|
||||||
|
};
|
||||||
|
use crate::{client::Client, Settings};
|
||||||
use common::{
|
use common::{
|
||||||
comp::{
|
comp::{
|
||||||
self,
|
self,
|
||||||
agent::{Agent, AgentEvent},
|
agent::{Agent, AgentEvent},
|
||||||
group::GroupManager,
|
group::GroupManager,
|
||||||
invite::{Invite, InviteKind, InviteResponse, PendingInvites},
|
invite::{Invite, InviteKind, InviteResponse, PendingInvites},
|
||||||
ChatType, Pos,
|
ChatType, Group, Pos,
|
||||||
},
|
},
|
||||||
consts::MAX_TRADE_RANGE,
|
consts::MAX_TRADE_RANGE,
|
||||||
|
event::{InitiateInviteEvent, InviteResponseEvent},
|
||||||
trade::{TradeResult, Trades},
|
trade::{TradeResult, Trades},
|
||||||
uid::Uid,
|
uid::{IdMaps, Uid},
|
||||||
};
|
};
|
||||||
use common_net::{
|
use common_net::msg::{InviteAnswer, ServerGeneral};
|
||||||
msg::{InviteAnswer, ServerGeneral},
|
use specs::{
|
||||||
sync::WorldSyncExt,
|
shred, DispatcherBuilder, Entities, Entity, ReadExpect, ReadStorage, SystemData, WriteExpect,
|
||||||
|
WriteStorage,
|
||||||
};
|
};
|
||||||
use common_state::State;
|
|
||||||
use specs::{world::WorldExt, Entity};
|
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
use tracing::{error, warn};
|
use tracing::{error, warn};
|
||||||
|
|
||||||
@ -26,206 +30,266 @@ const INVITE_TIMEOUT_DUR: Duration = Duration::from_secs(31);
|
|||||||
/// Reduced duration shown to the client to help alleviate latency issues
|
/// Reduced duration shown to the client to help alleviate latency issues
|
||||||
const PRESENTED_INVITE_TIMEOUT_DUR: Duration = Duration::from_secs(30);
|
const PRESENTED_INVITE_TIMEOUT_DUR: Duration = Duration::from_secs(30);
|
||||||
|
|
||||||
pub fn handle_invite(server: &mut Server, inviter: Entity, invitee_uid: Uid, kind: InviteKind) {
|
pub(super) fn register_event_systems(builder: &mut DispatcherBuilder) {
|
||||||
let max_group_size = server.settings().max_player_group_size;
|
event_dispatch::<InitiateInviteEvent>(builder);
|
||||||
let state = server.state_mut();
|
event_dispatch::<InviteResponseEvent>(builder);
|
||||||
let clients = state.ecs().read_storage::<Client>();
|
}
|
||||||
let invitee = match state.ecs().entity_from_uid(invitee_uid) {
|
|
||||||
Some(t) => t,
|
|
||||||
None => {
|
|
||||||
// Inform of failure
|
|
||||||
if let Some(client) = clients.get(inviter) {
|
|
||||||
client.send_fallible(ServerGeneral::server_msg(
|
|
||||||
ChatType::Meta,
|
|
||||||
"Invite failed, target does not exist.",
|
|
||||||
));
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
let uids = state.ecs().read_storage::<Uid>();
|
impl ServerEvent for InitiateInviteEvent {
|
||||||
|
type SystemData<'a> = (
|
||||||
|
WriteExpect<'a, Trades>,
|
||||||
|
ReadExpect<'a, Settings>,
|
||||||
|
ReadExpect<'a, IdMaps>,
|
||||||
|
ReadExpect<'a, GroupManager>,
|
||||||
|
WriteStorage<'a, PendingInvites>,
|
||||||
|
WriteStorage<'a, Agent>,
|
||||||
|
WriteStorage<'a, Invite>,
|
||||||
|
ReadStorage<'a, Uid>,
|
||||||
|
ReadStorage<'a, Client>,
|
||||||
|
ReadStorage<'a, Pos>,
|
||||||
|
ReadStorage<'a, Group>,
|
||||||
|
);
|
||||||
|
|
||||||
// Check if entity is trying to invite themselves
|
fn handle(
|
||||||
if uids
|
events: impl ExactSizeIterator<Item = Self>,
|
||||||
.get(inviter)
|
(
|
||||||
.map_or(false, |inviter_uid| *inviter_uid == invitee_uid)
|
mut trades,
|
||||||
{
|
settings,
|
||||||
warn!("Entity tried to invite themselves into a group/trade");
|
id_maps,
|
||||||
return;
|
group_manager,
|
||||||
}
|
mut pending_invites,
|
||||||
|
mut agents,
|
||||||
let mut pending_invites = state.ecs().write_storage::<PendingInvites>();
|
mut invites,
|
||||||
let mut agents = state.ecs().write_storage::<Agent>();
|
uids,
|
||||||
let mut invites = state.ecs().write_storage::<Invite>();
|
clients,
|
||||||
|
positions,
|
||||||
if let InviteKind::Trade = kind {
|
groups,
|
||||||
// Check whether the inviter is in range of the invitee
|
): Self::SystemData<'_>,
|
||||||
let positions = state.ecs().read_storage::<Pos>();
|
) {
|
||||||
if !within_trading_range(positions.get(inviter), positions.get(invitee)) {
|
for InitiateInviteEvent(inviter, invitee_uid, kind) in events {
|
||||||
return;
|
let max_group_size = settings.max_player_group_size;
|
||||||
}
|
let invitee = match id_maps.uid_entity(invitee_uid) {
|
||||||
}
|
Some(t) => t,
|
||||||
|
None => {
|
||||||
if let InviteKind::Group = kind {
|
// Inform of failure
|
||||||
if !group_manip::can_invite(
|
if let Some(client) = clients.get(inviter) {
|
||||||
state,
|
client.send_fallible(ServerGeneral::server_msg(
|
||||||
&clients,
|
ChatType::Meta,
|
||||||
&mut pending_invites,
|
"Invite failed, target does not exist.",
|
||||||
max_group_size,
|
|
||||||
inviter,
|
|
||||||
invitee,
|
|
||||||
) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} 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))
|
|
||||||
.map(|e| {
|
|
||||||
if let Some(client) = clients.get(e) {
|
|
||||||
client
|
|
||||||
.send_fallible(ServerGeneral::FinishedTrade(TradeResult::Declined));
|
|
||||||
}
|
|
||||||
if let Some(agent) = agents.get_mut(e) {
|
|
||||||
agent
|
|
||||||
.inbox
|
|
||||||
.push_back(AgentEvent::FinishedTrade(TradeResult::Declined));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if invites.contains(invitee) {
|
|
||||||
// Inform inviter that there is already an invite
|
|
||||||
if let Some(client) = clients.get(inviter) {
|
|
||||||
client.send_fallible(ServerGeneral::server_msg(
|
|
||||||
ChatType::Meta,
|
|
||||||
"This player already has a pending invite.",
|
|
||||||
));
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut invite_sent = false;
|
|
||||||
// Returns true if insertion was successful
|
|
||||||
let mut send_invite = || {
|
|
||||||
match invites.insert(invitee, Invite { inviter, kind }) {
|
|
||||||
Err(err) => {
|
|
||||||
error!("Failed to insert Invite component: {:?}", err);
|
|
||||||
false
|
|
||||||
},
|
|
||||||
Ok(_) => {
|
|
||||||
match pending_invites.entry(inviter) {
|
|
||||||
Ok(entry) => {
|
|
||||||
entry.or_insert_with(|| PendingInvites(Vec::new())).0.push((
|
|
||||||
invitee,
|
|
||||||
kind,
|
|
||||||
Instant::now() + INVITE_TIMEOUT_DUR,
|
|
||||||
));
|
));
|
||||||
invite_sent = true;
|
}
|
||||||
true
|
return;
|
||||||
},
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// Check if entity is trying to invite themselves
|
||||||
|
if uids
|
||||||
|
.get(inviter)
|
||||||
|
.map_or(false, |inviter_uid| *inviter_uid == invitee_uid)
|
||||||
|
{
|
||||||
|
warn!("Entity tried to invite themselves into a group/trade");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if matches!(kind, InviteKind::Trade) {
|
||||||
|
// Check whether the inviter is in range of the invitee
|
||||||
|
if !within_trading_range(positions.get(inviter), positions.get(invitee)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let InviteKind::Group = kind {
|
||||||
|
if !group_manip::can_invite(
|
||||||
|
&clients,
|
||||||
|
&groups,
|
||||||
|
&group_manager,
|
||||||
|
&mut pending_invites,
|
||||||
|
max_group_size,
|
||||||
|
inviter,
|
||||||
|
invitee,
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// cancel current trades for inviter before inviting someone else to trade
|
||||||
|
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| id_maps.uid_entity(u))
|
||||||
|
.map(|e| {
|
||||||
|
if let Some(client) = clients.get(e) {
|
||||||
|
client.send_fallible(ServerGeneral::FinishedTrade(
|
||||||
|
TradeResult::Declined,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
if let Some(agent) = agents.get_mut(e) {
|
||||||
|
agent.inbox.push_back(AgentEvent::FinishedTrade(
|
||||||
|
TradeResult::Declined,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if invites.contains(invitee) {
|
||||||
|
// Inform inviter that there is already an invite
|
||||||
|
if let Some(client) = clients.get(inviter) {
|
||||||
|
client.send_fallible(ServerGeneral::server_msg(
|
||||||
|
ChatType::Meta,
|
||||||
|
"This player already has a pending invite.",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut invite_sent = false;
|
||||||
|
// Returns true if insertion was successful
|
||||||
|
let mut send_invite = || {
|
||||||
|
match invites.insert(invitee, Invite { inviter, kind }) {
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
error!(
|
error!("Failed to insert Invite component: {:?}", err);
|
||||||
"Failed to get entry for pending invites component: {:?}",
|
|
||||||
err
|
|
||||||
);
|
|
||||||
// Cleanup
|
|
||||||
invites.remove(invitee);
|
|
||||||
false
|
false
|
||||||
},
|
},
|
||||||
|
Ok(_) => {
|
||||||
|
match pending_invites.entry(inviter) {
|
||||||
|
Ok(entry) => {
|
||||||
|
entry.or_insert_with(|| PendingInvites(Vec::new())).0.push((
|
||||||
|
invitee,
|
||||||
|
kind,
|
||||||
|
Instant::now() + INVITE_TIMEOUT_DUR,
|
||||||
|
));
|
||||||
|
invite_sent = true;
|
||||||
|
true
|
||||||
|
},
|
||||||
|
Err(err) => {
|
||||||
|
error!(
|
||||||
|
"Failed to get entry for pending invites component: {:?}",
|
||||||
|
err
|
||||||
|
);
|
||||||
|
// Cleanup
|
||||||
|
invites.remove(invitee);
|
||||||
|
false
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// If client comp
|
// If client comp
|
||||||
if let (Some(client), Some(inviter)) = (clients.get(invitee), uids.get(inviter).copied()) {
|
if let (Some(client), Some(inviter)) =
|
||||||
if send_invite() {
|
(clients.get(invitee), uids.get(inviter).copied())
|
||||||
client.send_fallible(ServerGeneral::Invite {
|
{
|
||||||
inviter,
|
if send_invite() {
|
||||||
timeout: PRESENTED_INVITE_TIMEOUT_DUR,
|
client.send_fallible(ServerGeneral::Invite {
|
||||||
kind,
|
inviter,
|
||||||
});
|
timeout: PRESENTED_INVITE_TIMEOUT_DUR,
|
||||||
}
|
kind,
|
||||||
} else if let Some(agent) = agents.get_mut(invitee) {
|
});
|
||||||
if send_invite() {
|
}
|
||||||
if let Some(inviter) = uids.get(inviter) {
|
} else if let Some(agent) = agents.get_mut(invitee) {
|
||||||
agent.inbox.push_back(AgentEvent::TradeInvite(*inviter));
|
if send_invite() {
|
||||||
invite_sent = true;
|
if let Some(inviter) = uids.get(inviter) {
|
||||||
|
agent.inbox.push_back(AgentEvent::TradeInvite(*inviter));
|
||||||
|
invite_sent = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if let Some(client) = clients.get(inviter) {
|
||||||
|
client.send_fallible(ServerGeneral::server_msg(
|
||||||
|
ChatType::Meta,
|
||||||
|
"Can't invite, not a player or npc",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Notify inviter that the invite is pending
|
||||||
|
if invite_sent {
|
||||||
|
if let Some(client) = clients.get(inviter) {
|
||||||
|
client.send_fallible(ServerGeneral::InvitePending(invitee_uid));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if let Some(client) = clients.get(inviter) {
|
|
||||||
client.send_fallible(ServerGeneral::server_msg(
|
|
||||||
ChatType::Meta,
|
|
||||||
"Can't invite, not a player or npc",
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Notify inviter that the invite is pending
|
#[derive(SystemData)]
|
||||||
if invite_sent {
|
pub struct InviteResponseData<'a> {
|
||||||
if let Some(client) = clients.get(inviter) {
|
entities: Entities<'a>,
|
||||||
client.send_fallible(ServerGeneral::InvitePending(invitee_uid));
|
group_manager: WriteExpect<'a, GroupManager>,
|
||||||
|
trades: WriteExpect<'a, Trades>,
|
||||||
|
index: ReadExpect<'a, world::IndexOwned>,
|
||||||
|
id_maps: ReadExpect<'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_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) {
|
pub fn handle_invite_accept(data: &mut InviteResponseData, entity: Entity) {
|
||||||
let index = server.index.clone();
|
if let Some((inviter, kind)) = get_inviter_and_kind(entity, data) {
|
||||||
let state = server.state_mut();
|
handle_invite_answer(data, inviter, entity, InviteAnswer::Accepted, kind);
|
||||||
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>();
|
|
||||||
|
|
||||||
match kind {
|
match kind {
|
||||||
InviteKind::Group => {
|
InviteKind::Group => {
|
||||||
let map_markers = state.ecs().read_storage::<comp::MapMarker>();
|
data.group_manager.add_group_member(
|
||||||
let mut group_manager = state.ecs().write_resource::<GroupManager>();
|
|
||||||
group_manager.add_group_member(
|
|
||||||
inviter,
|
inviter,
|
||||||
entity,
|
entity,
|
||||||
&state.ecs().entities(),
|
&data.entities,
|
||||||
&mut state.ecs().write_storage(),
|
&mut data.groups,
|
||||||
&state.ecs().read_storage(),
|
&data.alignments,
|
||||||
&uids,
|
&data.uids,
|
||||||
|entity, group_change| {
|
|entity, group_change| {
|
||||||
clients
|
data.clients
|
||||||
.get(entity)
|
.get(entity)
|
||||||
.and_then(|c| {
|
.and_then(|c| {
|
||||||
group_change
|
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| (g, c))
|
||||||
})
|
})
|
||||||
.map(|(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));
|
c.send_fallible(ServerGeneral::GroupUpdate(g));
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
InviteKind::Trade => {
|
InviteKind::Trade => {
|
||||||
if let (Some(inviter_uid), Some(invitee_uid)) =
|
if let (Some(inviter_uid), Some(invitee_uid)) = (
|
||||||
(uids.get(inviter).copied(), uids.get(entity).copied())
|
data.uids.get(inviter).copied(),
|
||||||
{
|
data.uids.get(entity).copied(),
|
||||||
let mut trades = state.ecs().write_resource::<Trades>();
|
) {
|
||||||
// check if the person that invited me has started a new trade since the
|
// check if the person that invited me has started a new trade since the
|
||||||
// invitation was sent
|
// invitation was sent
|
||||||
if trades.entity_trades.get(&inviter_uid).copied().is_some() {
|
if data
|
||||||
for client in clients.get(entity).into_iter().chain(clients.get(inviter)) {
|
.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(
|
client.send_fallible(ServerGeneral::server_msg(
|
||||||
ChatType::Meta,
|
ChatType::Meta,
|
||||||
"Trade failed, inviter initiated new trade since sending trade \
|
"Trade failed, inviter initiated new trade since sending trade \
|
||||||
@ -234,39 +298,40 @@ pub fn handle_invite_accept(server: &mut Server, entity: Entity) {
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let id = trades.begin_trade(inviter_uid, invitee_uid);
|
let id = data.trades.begin_trade(inviter_uid, invitee_uid);
|
||||||
let trade = trades.trades[&id].clone();
|
let trade = data.trades.trades[&id].clone();
|
||||||
if let Some(agent) = agents.get_mut(inviter) {
|
if let Some(agent) = data.agents.get_mut(inviter) {
|
||||||
agent
|
agent
|
||||||
.inbox
|
.inbox
|
||||||
.push_back(AgentEvent::TradeAccepted(invitee_uid));
|
.push_back(AgentEvent::TradeAccepted(invitee_uid));
|
||||||
}
|
}
|
||||||
#[cfg(feature = "worldgen")]
|
#[cfg(feature = "worldgen")]
|
||||||
let pricing = agents
|
let pricing = data
|
||||||
|
.agents
|
||||||
.get(inviter)
|
.get(inviter)
|
||||||
.and_then(|a| {
|
.and_then(|a| {
|
||||||
a.behavior
|
a.behavior
|
||||||
.trade_site()
|
.trade_site()
|
||||||
.and_then(|id| index.get_site_prices(id))
|
.and_then(|id| data.index.get_site_prices(id))
|
||||||
})
|
})
|
||||||
.or_else(|| {
|
.or_else(|| {
|
||||||
agents.get(entity).and_then(|a| {
|
data.agents.get(entity).and_then(|a| {
|
||||||
a.behavior
|
a.behavior
|
||||||
.trade_site()
|
.trade_site()
|
||||||
.and_then(|id| index.get_site_prices(id))
|
.and_then(|id| data.index.get_site_prices(id))
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
#[cfg(not(feature = "worldgen"))]
|
#[cfg(not(feature = "worldgen"))]
|
||||||
let pricing = None;
|
let pricing = None;
|
||||||
|
|
||||||
clients.get(inviter).map(|c| {
|
data.clients.get(inviter).map(|c| {
|
||||||
c.send(ServerGeneral::UpdatePendingTrade(
|
c.send(ServerGeneral::UpdatePendingTrade(
|
||||||
id,
|
id,
|
||||||
trade.clone(),
|
trade.clone(),
|
||||||
pricing.clone(),
|
pricing.clone(),
|
||||||
))
|
))
|
||||||
});
|
});
|
||||||
clients
|
data.clients
|
||||||
.get(entity)
|
.get(entity)
|
||||||
.map(|c| c.send(ServerGeneral::UpdatePendingTrade(id, trade, pricing)));
|
.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)> {
|
fn get_inviter_and_kind(
|
||||||
let mut invites = state.ecs().write_storage::<Invite>();
|
entity: Entity,
|
||||||
invites.remove(entity).and_then(|invite| {
|
data: &mut InviteResponseData,
|
||||||
|
) -> Option<(Entity, InviteKind)> {
|
||||||
|
data.invites.remove(entity).and_then(|invite| {
|
||||||
let Invite { inviter, kind } = invite;
|
let Invite { inviter, kind } = invite;
|
||||||
let mut pending_invites = state.ecs().write_storage::<PendingInvites>();
|
let pending = &mut data.pending_invites.get_mut(inviter)?.0;
|
||||||
let pending = &mut pending_invites.get_mut(inviter)?.0;
|
|
||||||
// Check that inviter has a pending invite and remove it from the list
|
// Check that inviter has a pending invite and remove it from the list
|
||||||
let invite_index = pending.iter().position(|p| p.0 == entity)?;
|
let invite_index = pending.iter().position(|p| p.0 == entity)?;
|
||||||
pending.swap_remove(invite_index);
|
pending.swap_remove(invite_index);
|
||||||
// If no pending invites remain remove the component
|
// If no pending invites remain remove the component
|
||||||
if pending.is_empty() {
|
if pending.is_empty() {
|
||||||
pending_invites.remove(inviter);
|
data.pending_invites.remove(inviter);
|
||||||
}
|
}
|
||||||
|
|
||||||
Some((inviter, kind))
|
Some((inviter, kind))
|
||||||
@ -294,28 +360,25 @@ fn get_inviter_and_kind(entity: Entity, state: &mut State) -> Option<(Entity, In
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn handle_invite_answer(
|
fn handle_invite_answer(
|
||||||
state: &mut State,
|
data: &mut InviteResponseData,
|
||||||
inviter: Entity,
|
inviter: Entity,
|
||||||
entity: Entity,
|
entity: Entity,
|
||||||
invite_answer: InviteAnswer,
|
invite_answer: InviteAnswer,
|
||||||
kind: InviteKind,
|
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) {
|
if matches!(kind, InviteKind::Trade) && matches!(invite_answer, InviteAnswer::Accepted) {
|
||||||
// invitee must close current trade if one exists before accepting new one
|
// invitee must close current trade if one exists before accepting new one
|
||||||
if let Some(invitee_uid) = uids.get(entity).copied() {
|
if let Some(invitee_uid) = data.uids.get(entity).copied() {
|
||||||
let mut trades = state.ecs().write_resource::<Trades>();
|
if let Some(active_trade) = data.trades.entity_trades.get(&invitee_uid).copied() {
|
||||||
if let Some(active_trade) = trades.entity_trades.get(&invitee_uid).copied() {
|
data.trades
|
||||||
trades
|
|
||||||
.decline_trade(active_trade, invitee_uid)
|
.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| {
|
.map(|e| {
|
||||||
if let Some(client) = clients.get(e) {
|
if let Some(client) = data.clients.get(e) {
|
||||||
client
|
client
|
||||||
.send_fallible(ServerGeneral::FinishedTrade(TradeResult::Declined));
|
.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
|
agent
|
||||||
.inbox
|
.inbox
|
||||||
.push_back(AgentEvent::FinishedTrade(TradeResult::Declined));
|
.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 {
|
client.send_fallible(ServerGeneral::InviteComplete {
|
||||||
target,
|
target,
|
||||||
answer: invite_answer,
|
answer: invite_answer,
|
||||||
@ -333,11 +398,10 @@ fn handle_invite_answer(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_invite_decline(server: &mut Server, entity: Entity) {
|
pub fn handle_invite_decline(data: &mut InviteResponseData, entity: Entity) {
|
||||||
let state = server.state_mut();
|
if let Some((inviter, kind)) = get_inviter_and_kind(entity, data) {
|
||||||
if let Some((inviter, kind)) = get_inviter_and_kind(entity, state) {
|
|
||||||
// Inform inviter of rejection
|
// 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,31 @@
|
|||||||
use crate::{
|
use std::{marker::PhantomData, sync::Arc};
|
||||||
events::{
|
|
||||||
entity_creation::handle_create_teleporter,
|
use crate::{state_ext::StateExt, Server};
|
||||||
entity_manipulation::{handle_start_teleporting, handle_teleport_to_position},
|
use common::event::{
|
||||||
interaction::{handle_mount_volume, handle_tame_pet},
|
ChatEvent, ClientDisconnectEvent, ClientDisconnectWithoutPersistenceEvent, CommandEvent,
|
||||||
},
|
EventBus, ExitIngameEvent,
|
||||||
persistence::PersistedComponents,
|
};
|
||||||
state_ext::StateExt,
|
use common_base::span;
|
||||||
Server,
|
use common_ecs::{dispatch, System};
|
||||||
};
|
use specs::{DispatcherBuilder, Entity as EcsEntity, ReadExpect, WorldExt};
|
||||||
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 crate::events::player::handle_character_delete;
|
|
||||||
pub use group_manip::update_map_markers;
|
pub use group_manip::update_map_markers;
|
||||||
pub(crate) use trade::cancel_trades_for;
|
pub(crate) use trade::cancel_trades_for;
|
||||||
|
|
||||||
|
use self::{
|
||||||
|
entity_creation::{
|
||||||
|
handle_create_npc, handle_create_ship, handle_create_teleporter, handle_create_waypoint,
|
||||||
|
handle_initialize_character, handle_initialize_spectator, handle_loaded_character_data,
|
||||||
|
handle_shockwave, handle_shoot,
|
||||||
|
},
|
||||||
|
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_creation;
|
||||||
mod entity_manipulation;
|
mod entity_manipulation;
|
||||||
mod group_manip;
|
mod group_manip;
|
||||||
@ -45,9 +33,56 @@ mod information;
|
|||||||
mod interaction;
|
mod interaction;
|
||||||
mod inventory_manip;
|
mod inventory_manip;
|
||||||
mod invite;
|
mod invite;
|
||||||
|
mod mounting;
|
||||||
mod player;
|
mod player;
|
||||||
mod trade;
|
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> System<'a> for EventHandler<T> {
|
||||||
|
type SystemData = (
|
||||||
|
ReadExpect<'a, crate::metrics::ServerEventMetrics>,
|
||||||
|
ReadExpect<'a, EventBus<T>>,
|
||||||
|
T::SystemData<'a>,
|
||||||
|
);
|
||||||
|
|
||||||
|
const NAME: &'static str = T::NAME;
|
||||||
|
const ORIGIN: common_ecs::Origin = common_ecs::Origin::Server;
|
||||||
|
// TODO: Maybe do another phase here?
|
||||||
|
const PHASE: common_ecs::Phase = common_ecs::Phase::Apply;
|
||||||
|
|
||||||
|
fn run(_job: &mut common_ecs::Job<Self>, (metrics, ev, data): Self::SystemData) {
|
||||||
|
let events = ev.recv_all();
|
||||||
|
metrics
|
||||||
|
.event_count
|
||||||
|
.with_label_values(&[Self::NAME])
|
||||||
|
.inc_by(events.len() as u64);
|
||||||
|
T::handle(events, data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn event_dispatch<T: ServerEvent>(builder: &mut DispatcherBuilder) {
|
||||||
|
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 {
|
pub enum Event {
|
||||||
ClientConnected {
|
ClientConnected {
|
||||||
entity: EcsEntity,
|
entity: EcsEntity,
|
||||||
@ -62,280 +97,91 @@ pub enum Event {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Server {
|
impl Server {
|
||||||
pub fn handle_events(&mut self) -> Vec<Event> {
|
fn handle_serial_events<T: Send + 'static, F: FnMut(&mut Self, T)>(&mut self, mut f: F) {
|
||||||
span!(_guard, "handle_events", "Server::handle_events");
|
if let Some(bus) = self.state.ecs_mut().get_mut::<EventBus<T>>() {
|
||||||
let mut frontend_events = Vec::new();
|
let events = bus.recv_all_mut();
|
||||||
|
|
||||||
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),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
let server_event_metrics = self
|
let server_event_metrics = self
|
||||||
.state
|
.state
|
||||||
.ecs()
|
.ecs()
|
||||||
.read_resource::<crate::metrics::ServerEventMetrics>();
|
.read_resource::<crate::metrics::ServerEventMetrics>();
|
||||||
event_counts
|
server_event_metrics
|
||||||
.into_iter()
|
.event_count
|
||||||
.zip(ServerEventDiscriminants::VARIANTS)
|
.with_label_values(&[std::any::type_name::<T>()])
|
||||||
.for_each(|(count, event_name)| {
|
.inc_by(events.len() as u64);
|
||||||
server_event_metrics
|
drop(server_event_metrics);
|
||||||
.event_count
|
|
||||||
.with_label_values(&[event_name])
|
|
||||||
.inc_by(count.into());
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
for (entity, name, args) in commands {
|
for ev in events {
|
||||||
self.process_command(entity, name, args);
|
f(self, ev)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for msg in chat_messages {
|
fn handle_all_serial_events(&mut self, frontend_events: &mut Vec<Event>) {
|
||||||
self.state.send_chat(msg);
|
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_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);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn handle_events(&mut self) -> Vec<Event> {
|
||||||
|
let mut frontend_events = Vec::new();
|
||||||
|
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(Arc::clone(self.state.thread_pool()));
|
||||||
|
register_event_systems(&mut dispatch_builder);
|
||||||
|
// This dispatches all the systems in parallel.
|
||||||
|
let mut dispatcher = dispatch_builder.build();
|
||||||
|
drop(guard);
|
||||||
|
|
||||||
|
span!(guard, "run event systems");
|
||||||
|
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
|
frontend_events
|
||||||
}
|
}
|
||||||
|
137
server/src/events/mounting.rs
Normal file
137
server/src/events/mounting.rs
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
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,
|
state_ext::StateExt, BattleModeBuffer, Server,
|
||||||
};
|
};
|
||||||
use common::{
|
use common::{
|
||||||
character::CharacterId,
|
|
||||||
comp,
|
comp,
|
||||||
comp::{group, pet::is_tameable, Presence, PresenceKind},
|
comp::{group, pet::is_tameable, Presence, PresenceKind},
|
||||||
|
event::{DeleteCharacterEvent, PossessEvent},
|
||||||
resources::Time,
|
resources::Time,
|
||||||
uid::{IdMaps, Uid},
|
uid::{IdMaps, Uid},
|
||||||
};
|
};
|
||||||
@ -16,12 +16,7 @@ use common_state::State;
|
|||||||
use specs::{Builder, Entity as EcsEntity, Join, WorldExt};
|
use specs::{Builder, Entity as EcsEntity, Join, WorldExt};
|
||||||
use tracing::{debug, error, trace, warn, Instrument};
|
use tracing::{debug, error, trace, warn, Instrument};
|
||||||
|
|
||||||
pub fn handle_character_delete(
|
pub fn handle_character_delete(server: &mut Server, ev: DeleteCharacterEvent) {
|
||||||
server: &mut Server,
|
|
||||||
entity: EcsEntity,
|
|
||||||
requesting_player_uuid: String,
|
|
||||||
character_id: CharacterId,
|
|
||||||
) {
|
|
||||||
// Can't process a character delete for a player that has an in-game presence,
|
// Can't process a character delete for a player that has an in-game presence,
|
||||||
// so kick them out before processing the delete.
|
// so kick them out before processing the delete.
|
||||||
// NOTE: This relies on StateExt::handle_initialize_character adding the
|
// NOTE: This relies on StateExt::handle_initialize_character adding the
|
||||||
@ -29,19 +24,19 @@ pub fn handle_character_delete(
|
|||||||
// is in-game.
|
// is in-game.
|
||||||
let has_presence = {
|
let has_presence = {
|
||||||
let presences = server.state.ecs().read_storage::<Presence>();
|
let presences = server.state.ecs().read_storage::<Presence>();
|
||||||
presences.get(entity).is_some()
|
presences.get(ev.entity).is_some()
|
||||||
};
|
};
|
||||||
if has_presence {
|
if has_presence {
|
||||||
warn!(
|
warn!(
|
||||||
?requesting_player_uuid,
|
?ev.requesting_player_uuid,
|
||||||
?character_id,
|
?ev.character_id,
|
||||||
"Character delete received while in-game, disconnecting client."
|
"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>();
|
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) {
|
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
|
/// 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
|
/// comment it out, but it needs to be fixed for a variety of reasons. Get rid
|
||||||
/// of this ASAP!
|
/// 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 crate::presence::RegionSubscription;
|
||||||
use common::{
|
use common::{
|
||||||
comp::{inventory::slot::EquipSlot, item, slot::Slot, Inventory},
|
comp::{inventory::slot::EquipSlot, item, slot::Slot, Inventory},
|
||||||
|
@ -7,7 +7,8 @@ use common::{
|
|||||||
Inventory,
|
Inventory,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
trade::{PendingTrade, ReducedInventory, TradeAction, TradeId, TradeResult, Trades},
|
event::ProcessTradeActionEvent,
|
||||||
|
trade::{PendingTrade, ReducedInventory, TradeAction, TradeResult, Trades},
|
||||||
};
|
};
|
||||||
use common_net::{
|
use common_net::{
|
||||||
msg::ServerGeneral,
|
msg::ServerGeneral,
|
||||||
@ -19,8 +20,8 @@ use std::cmp::Ordering;
|
|||||||
use tracing::{error, trace};
|
use tracing::{error, trace};
|
||||||
use world::IndexOwned;
|
use world::IndexOwned;
|
||||||
|
|
||||||
fn notify_agent_simple(
|
pub fn notify_agent_simple(
|
||||||
mut agents: specs::WriteStorage<Agent>,
|
agents: &mut specs::WriteStorage<Agent>,
|
||||||
entity: EcsEntity,
|
entity: EcsEntity,
|
||||||
event: AgentEvent,
|
event: AgentEvent,
|
||||||
) {
|
) {
|
||||||
@ -55,9 +56,7 @@ fn notify_agent_prices(
|
|||||||
/// Invoked when the trade UI is up, handling item changes, accepts, etc
|
/// Invoked when the trade UI is up, handling item changes, accepts, etc
|
||||||
pub(super) fn handle_process_trade_action(
|
pub(super) fn handle_process_trade_action(
|
||||||
server: &mut Server,
|
server: &mut Server,
|
||||||
entity: EcsEntity,
|
ProcessTradeActionEvent(entity, trade_id, action): ProcessTradeActionEvent,
|
||||||
trade_id: TradeId,
|
|
||||||
action: TradeAction,
|
|
||||||
) {
|
) {
|
||||||
if let Some(uid) = server.state.ecs().uid_from_entity(entity) {
|
if let Some(uid) = server.state.ecs().uid_from_entity(entity) {
|
||||||
let mut trades = server.state.ecs().write_resource::<Trades>();
|
let mut trades = server.state.ecs().write_resource::<Trades>();
|
||||||
@ -68,7 +67,7 @@ pub(super) fn handle_process_trade_action(
|
|||||||
.map(|e| {
|
.map(|e| {
|
||||||
server.notify_client(e, ServerGeneral::FinishedTrade(TradeResult::Declined));
|
server.notify_client(e, ServerGeneral::FinishedTrade(TradeResult::Declined));
|
||||||
notify_agent_simple(
|
notify_agent_simple(
|
||||||
server.state.ecs().write_storage::<Agent>(),
|
&mut server.state.ecs().write_storage(),
|
||||||
e,
|
e,
|
||||||
AgentEvent::FinishedTrade(TradeResult::Declined),
|
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) {
|
if let Some(e) = server.state.ecs().entity_from_uid(*party) {
|
||||||
server.notify_client(e, ServerGeneral::FinishedTrade(result.clone()));
|
server.notify_client(e, ServerGeneral::FinishedTrade(result.clone()));
|
||||||
notify_agent_simple(
|
notify_agent_simple(
|
||||||
server.state.ecs().write_storage::<Agent>(),
|
&mut server.state.ecs().write_storage(),
|
||||||
e,
|
e,
|
||||||
AgentEvent::FinishedTrade(result.clone()),
|
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));
|
c.send_fallible(ServerGeneral::FinishedTrade(TradeResult::Declined));
|
||||||
}
|
}
|
||||||
notify_agent_simple(
|
notify_agent_simple(
|
||||||
ecs.write_storage::<Agent>(),
|
&mut ecs.write_storage::<Agent>(),
|
||||||
e,
|
e,
|
||||||
AgentEvent::FinishedTrade(TradeResult::Declined),
|
AgentEvent::FinishedTrade(TradeResult::Declined),
|
||||||
);
|
);
|
||||||
|
@ -5,7 +5,14 @@
|
|||||||
clippy::needless_pass_by_ref_mut //until we find a better way for specs
|
clippy::needless_pass_by_ref_mut //until we find a better way for specs
|
||||||
)]
|
)]
|
||||||
#![deny(clippy::clone_on_ref_ptr)]
|
#![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;
|
pub mod automod;
|
||||||
mod character_creator;
|
mod character_creator;
|
||||||
@ -72,7 +79,10 @@ use common::{
|
|||||||
character::{CharacterId, CharacterItem},
|
character::{CharacterId, CharacterItem},
|
||||||
cmd::ServerChatCommand,
|
cmd::ServerChatCommand,
|
||||||
comp,
|
comp,
|
||||||
event::{EventBus, ServerEvent},
|
event::{
|
||||||
|
register_event_busses, ClientDisconnectEvent, ClientDisconnectWithoutPersistenceEvent,
|
||||||
|
EventBus, ExitIngameEvent, UpdateCharacterDataEvent,
|
||||||
|
},
|
||||||
link::Is,
|
link::Is,
|
||||||
mounting::{Volume, VolumeRider},
|
mounting::{Volume, VolumeRider},
|
||||||
region::RegionMap,
|
region::RegionMap,
|
||||||
@ -324,7 +334,8 @@ impl Server {
|
|||||||
state.ecs_mut().insert(DataDir {
|
state.ecs_mut().insert(DataDir {
|
||||||
path: data_dir.to_owned(),
|
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().insert(Vec::<ChunkRequest>::new());
|
||||||
state
|
state
|
||||||
.ecs_mut()
|
.ecs_mut()
|
||||||
@ -989,7 +1000,7 @@ impl Server {
|
|||||||
),
|
),
|
||||||
},
|
},
|
||||||
CharacterScreenResponseKind::CharacterData(result) => {
|
CharacterScreenResponseKind::CharacterData(result) => {
|
||||||
let message = match *result {
|
match *result {
|
||||||
Ok((character_data, skill_set_persistence_load_error)) => {
|
Ok((character_data, skill_set_persistence_load_error)) => {
|
||||||
let PersistedComponents {
|
let PersistedComponents {
|
||||||
body,
|
body,
|
||||||
@ -1013,11 +1024,11 @@ impl Server {
|
|||||||
);
|
);
|
||||||
// TODO: Does this need to be a server event? E.g. we could
|
// TODO: Does this need to be a server event? E.g. we could
|
||||||
// just handle it here.
|
// just handle it here.
|
||||||
ServerEvent::UpdateCharacterData {
|
self.state.emit_event_now(UpdateCharacterDataEvent {
|
||||||
entity: response.target_entity,
|
entity: response.target_entity,
|
||||||
components: character_data,
|
components: character_data,
|
||||||
metadata: skill_set_persistence_load_error,
|
metadata: skill_set_persistence_load_error,
|
||||||
}
|
})
|
||||||
},
|
},
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
// We failed to load data for the character from the DB. Notify
|
// We failed to load data for the character from the DB. Notify
|
||||||
@ -1031,16 +1042,11 @@ impl Server {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Clean up the entity data on the server
|
// Clean up the entity data on the server
|
||||||
ServerEvent::ExitIngame {
|
self.state.emit_event_now(ExitIngameEvent {
|
||||||
entity: response.target_entity,
|
entity: response.target_entity,
|
||||||
}
|
})
|
||||||
},
|
},
|
||||||
};
|
}
|
||||||
|
|
||||||
self.state
|
|
||||||
.ecs()
|
|
||||||
.read_resource::<EventBus<ServerEvent>>()
|
|
||||||
.emit_now(message);
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -1194,15 +1200,15 @@ impl Server {
|
|||||||
);
|
);
|
||||||
for (_, entity) in (&clients, &entities).join() {
|
for (_, entity) in (&clients, &entities).join() {
|
||||||
info!("Emitting client disconnect event for entity: {:?}", entity);
|
info!("Emitting client disconnect event for entity: {:?}", entity);
|
||||||
let event = if with_persistence {
|
if with_persistence {
|
||||||
ServerEvent::ClientDisconnect(entity, comp::DisconnectReason::Kicked)
|
self.state.emit_event_now(ClientDisconnectEvent(
|
||||||
|
entity,
|
||||||
|
comp::DisconnectReason::Kicked,
|
||||||
|
))
|
||||||
} else {
|
} else {
|
||||||
ServerEvent::ClientDisconnectWithoutPersistence(entity)
|
self.state
|
||||||
|
.emit_event_now(ClientDisconnectWithoutPersistenceEvent(entity))
|
||||||
};
|
};
|
||||||
self.state
|
|
||||||
.ecs()
|
|
||||||
.read_resource::<EventBus<ServerEvent>>()
|
|
||||||
.emit_now(event);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.disconnect_all_clients_requested = false;
|
self.disconnect_all_clients_requested = false;
|
||||||
|
@ -5,7 +5,7 @@ use crate::sys::terrain::NpcData;
|
|||||||
use common::{
|
use common::{
|
||||||
calendar::Calendar,
|
calendar::Calendar,
|
||||||
comp::{self, Agent, Body, Presence, PresenceKind},
|
comp::{self, Agent, Body, Presence, PresenceKind},
|
||||||
event::{EventBus, NpcBuilder, ServerEvent},
|
event::{CreateNpcEvent, CreateShipEvent, DeleteEvent, EventBus, NpcBuilder},
|
||||||
generation::{BodyBuilder, EntityConfig, EntityInfo},
|
generation::{BodyBuilder, EntityConfig, EntityInfo},
|
||||||
resources::{DeltaTime, Time, TimeOfDay},
|
resources::{DeltaTime, Time, TimeOfDay},
|
||||||
rtsim::{Actor, NpcId, RtSimEntity},
|
rtsim::{Actor, NpcId, RtSimEntity},
|
||||||
@ -236,7 +236,9 @@ impl<'a> System<'a> for Sys {
|
|||||||
Read<'a, DeltaTime>,
|
Read<'a, DeltaTime>,
|
||||||
Read<'a, Time>,
|
Read<'a, Time>,
|
||||||
Read<'a, TimeOfDay>,
|
Read<'a, TimeOfDay>,
|
||||||
Read<'a, EventBus<ServerEvent>>,
|
Read<'a, EventBus<CreateShipEvent>>,
|
||||||
|
Read<'a, EventBus<CreateNpcEvent>>,
|
||||||
|
Read<'a, EventBus<DeleteEvent>>,
|
||||||
WriteExpect<'a, RtSim>,
|
WriteExpect<'a, RtSim>,
|
||||||
ReadExpect<'a, Arc<world::World>>,
|
ReadExpect<'a, Arc<world::World>>,
|
||||||
ReadExpect<'a, world::IndexOwned>,
|
ReadExpect<'a, world::IndexOwned>,
|
||||||
@ -259,7 +261,9 @@ impl<'a> System<'a> for Sys {
|
|||||||
dt,
|
dt,
|
||||||
time,
|
time,
|
||||||
time_of_day,
|
time_of_day,
|
||||||
server_event_bus,
|
create_ship_events,
|
||||||
|
create_npc_events,
|
||||||
|
delete_events,
|
||||||
mut rtsim,
|
mut rtsim,
|
||||||
world,
|
world,
|
||||||
index,
|
index,
|
||||||
@ -271,7 +275,9 @@ impl<'a> System<'a> for Sys {
|
|||||||
calendar,
|
calendar,
|
||||||
): Self::SystemData,
|
): 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 rtsim = &mut *rtsim;
|
||||||
let calendar_data = (*time_of_day, (*calendar).clone());
|
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 {
|
let mut create_event = |id: NpcId, npc: &Npc, steering: Option<NpcBuilder>| match npc.body {
|
||||||
Body::Ship(body) => {
|
Body::Ship(body) => {
|
||||||
emitter.emit(ServerEvent::CreateShip {
|
create_ship_emitter.emit(CreateShipEvent {
|
||||||
pos: comp::Pos(npc.wpos),
|
pos: comp::Pos(npc.wpos),
|
||||||
ori: comp::Ori::from(Dir::new(npc.dir.with_z(0.0))),
|
ori: comp::Ori::from(Dir::new(npc.dir.with_z(0.0))),
|
||||||
ship: body,
|
ship: body,
|
||||||
@ -333,7 +339,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
Some(&calendar_data),
|
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 {
|
NpcData::Data {
|
||||||
pos,
|
pos,
|
||||||
stats,
|
stats,
|
||||||
@ -346,7 +352,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
alignment,
|
alignment,
|
||||||
scale,
|
scale,
|
||||||
loot,
|
loot,
|
||||||
} => ServerEvent::CreateNpc {
|
} => CreateNpcEvent {
|
||||||
pos,
|
pos,
|
||||||
ori: comp::Ori::from(Dir::new(npc.dir.with_z(0.0))),
|
ori: comp::Ori::from(Dir::new(npc.dir.with_z(0.0))),
|
||||||
npc: NpcBuilder::new(stats, body, alignment)
|
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
|
// Synchronise rtsim NPC with entity data
|
||||||
for (entity, pos, rtsim_entity, agent) in (
|
for (entity, pos, rtsim_entity, agent) in (
|
||||||
&entities,
|
&entities,
|
||||||
@ -489,7 +494,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
SimulationMode::Simulated => {
|
SimulationMode::Simulated => {
|
||||||
emitter.emit(ServerEvent::Delete(entity));
|
delete_emitter.emit(DeleteEvent(entity));
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -42,7 +42,10 @@ use common_net::{
|
|||||||
};
|
};
|
||||||
use common_state::State;
|
use common_state::State;
|
||||||
use rand::prelude::*;
|
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 std::time::{Duration, Instant};
|
||||||
use tracing::{error, trace, warn};
|
use tracing::{error, trace, warn};
|
||||||
use vek::*;
|
use vek::*;
|
||||||
@ -1253,72 +1256,85 @@ impl StateExt for State {
|
|||||||
dismount_volume: bool,
|
dismount_volume: bool,
|
||||||
f: impl for<'a> FnOnce(&'a mut comp::Pos) -> T,
|
f: impl for<'a> FnOnce(&'a mut comp::Pos) -> T,
|
||||||
) -> Result<T, Content> {
|
) -> Result<T, Content> {
|
||||||
if dismount_volume {
|
let ecs = self.ecs_mut();
|
||||||
self.ecs().write_storage::<Is<VolumeRider>>().remove(entity);
|
position_mut(
|
||||||
}
|
entity,
|
||||||
|
dismount_volume,
|
||||||
let entity = self
|
f,
|
||||||
.read_storage::<Is<Rider>>()
|
&ecs.read_resource(),
|
||||||
.get(entity)
|
&mut ecs.write_storage(),
|
||||||
.and_then(|is_rider| {
|
ecs.write_storage(),
|
||||||
self.ecs()
|
ecs.write_storage(),
|
||||||
.read_resource::<IdMaps>()
|
ecs.read_storage(),
|
||||||
.uid_entity(is_rider.mount)
|
ecs.read_storage(),
|
||||||
})
|
ecs.read_storage(),
|
||||||
.map(Ok)
|
)
|
||||||
.or_else(|| {
|
|
||||||
self.read_storage::<Is<VolumeRider>>()
|
|
||||||
.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)?)
|
|
||||||
},
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.unwrap_or(Ok(entity))?;
|
|
||||||
|
|
||||||
let mut maybe_pos = None;
|
|
||||||
|
|
||||||
let res = self
|
|
||||||
.ecs()
|
|
||||||
.write_storage::<comp::Pos>()
|
|
||||||
.get_mut(entity)
|
|
||||||
.map(|pos| {
|
|
||||||
let res = f(pos);
|
|
||||||
maybe_pos = Some(pos.0);
|
|
||||||
res
|
|
||||||
})
|
|
||||||
.ok_or(Content::localized_with_args(
|
|
||||||
"command-position-unavailable",
|
|
||||||
[("target", "entity")],
|
|
||||||
));
|
|
||||||
|
|
||||||
if let Some(pos) = maybe_pos {
|
|
||||||
if self
|
|
||||||
.ecs()
|
|
||||||
.read_storage::<Presence>()
|
|
||||||
.get(entity)
|
|
||||||
.map(|presence| presence.kind == PresenceKind::Spectator)
|
|
||||||
.unwrap_or(false)
|
|
||||||
{
|
|
||||||
self.read_storage::<Client>().get(entity).map(|client| {
|
|
||||||
client.send_fallible(ServerGeneral::SpectatePosition(pos));
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
self.ecs()
|
|
||||||
.write_storage::<comp::ForceUpdate>()
|
|
||||||
.get_mut(entity)
|
|
||||||
.map(|force_update| force_update.update());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
res
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
is_volume_riders.remove(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
let entity = is_riders
|
||||||
|
.get(entity)
|
||||||
|
.and_then(|is_rider| id_maps.uid_entity(is_rider.mount))
|
||||||
|
.map(Ok)
|
||||||
|
.or_else(|| {
|
||||||
|
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(id_maps.uid_entity(uid)?),
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.unwrap_or(Ok(entity))?;
|
||||||
|
|
||||||
|
let mut maybe_pos = None;
|
||||||
|
|
||||||
|
let res = positions
|
||||||
|
.get_mut(entity)
|
||||||
|
.map(|pos| {
|
||||||
|
let res = f(pos);
|
||||||
|
maybe_pos = Some(pos.0);
|
||||||
|
res
|
||||||
|
})
|
||||||
|
.ok_or(Content::localized_with_args(
|
||||||
|
"command-position-unavailable",
|
||||||
|
[("target", "entity")],
|
||||||
|
));
|
||||||
|
|
||||||
|
if let Some(pos) = maybe_pos {
|
||||||
|
if presences
|
||||||
|
.get(entity)
|
||||||
|
.map(|presence| presence.kind == PresenceKind::Spectator)
|
||||||
|
.unwrap_or(false)
|
||||||
|
{
|
||||||
|
clients.get(entity).map(|client| {
|
||||||
|
client.send_fallible(ServerGeneral::SpectatePosition(pos));
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
force_updates
|
||||||
|
.get_mut(entity)
|
||||||
|
.map(|force_update| force_update.update());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
res
|
||||||
|
}
|
||||||
|
|
||||||
fn send_to_group(g: &Group, ecs: &specs::World, msg: &comp::ChatMsg) {
|
fn send_to_group(g: &Group, ecs: &specs::World, msg: &comp::ChatMsg) {
|
||||||
for (client, group) in (&ecs.read_storage::<Client>(), &ecs.read_storage::<Group>()).join() {
|
for (client, group) in (&ecs.read_storage::<Client>(), &ecs.read_storage::<Group>()).join() {
|
||||||
if g == group {
|
if g == group {
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
pub mod behavior_tree;
|
pub mod behavior_tree;
|
||||||
|
use server_agent::data::AgentEvents;
|
||||||
pub use server_agent::{action_nodes, attack, consts, data, util};
|
pub use server_agent::{action_nodes, attack, consts, data, util};
|
||||||
use vek::Vec3;
|
use vek::Vec3;
|
||||||
|
|
||||||
@ -11,7 +12,6 @@ use common::{
|
|||||||
self, inventory::slot::EquipSlot, item::ItemDesc, Agent, Alignment, Body, CharacterState,
|
self, inventory::slot::EquipSlot, item::ItemDesc, Agent, Alignment, Body, CharacterState,
|
||||||
Controller, Health, InputKind, Scale,
|
Controller, Health, InputKind, Scale,
|
||||||
},
|
},
|
||||||
event::{EventBus, ServerEvent},
|
|
||||||
mounting::Volume,
|
mounting::Volume,
|
||||||
path::TraversalConfig,
|
path::TraversalConfig,
|
||||||
};
|
};
|
||||||
@ -19,7 +19,7 @@ use common_base::prof_span;
|
|||||||
use common_ecs::{Job, Origin, ParMode, Phase, System};
|
use common_ecs::{Job, Origin, ParMode, Phase, System};
|
||||||
use rand::thread_rng;
|
use rand::thread_rng;
|
||||||
use rayon::iter::ParallelIterator;
|
use rayon::iter::ParallelIterator;
|
||||||
use specs::{LendJoin, ParJoin, Read, WriteStorage};
|
use specs::{LendJoin, ParJoin, WriteStorage};
|
||||||
|
|
||||||
/// This system will allow NPCs to modify their controller
|
/// This system will allow NPCs to modify their controller
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
@ -27,7 +27,7 @@ pub struct Sys;
|
|||||||
impl<'a> System<'a> for Sys {
|
impl<'a> System<'a> for Sys {
|
||||||
type SystemData = (
|
type SystemData = (
|
||||||
ReadData<'a>,
|
ReadData<'a>,
|
||||||
Read<'a, EventBus<ServerEvent>>,
|
AgentEvents<'a>,
|
||||||
WriteStorage<'a, Agent>,
|
WriteStorage<'a, Agent>,
|
||||||
WriteStorage<'a, Controller>,
|
WriteStorage<'a, Controller>,
|
||||||
);
|
);
|
||||||
@ -38,7 +38,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
job: &mut Job<Self>,
|
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);
|
job.cpu_stats.measure(ParMode::Rayon);
|
||||||
|
|
||||||
@ -97,7 +97,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
rtsim_entity,
|
rtsim_entity,
|
||||||
(_, is_rider, is_volume_rider),
|
(_, is_rider, is_volume_rider),
|
||||||
)| {
|
)| {
|
||||||
let mut event_emitter = event_bus.emitter();
|
let mut emitters = events.get_emitters();
|
||||||
let mut rng = thread_rng();
|
let mut rng = thread_rng();
|
||||||
|
|
||||||
// The entity that is moving, if riding it's the mount, otherwise it's itself
|
// 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,
|
||||||
agent_data: data,
|
agent_data: data,
|
||||||
read_data: &read_data,
|
read_data: &read_data,
|
||||||
event_emitter: &mut event_emitter,
|
emitters: &mut emitters,
|
||||||
controller,
|
controller,
|
||||||
rng: &mut rng,
|
rng: &mut rng,
|
||||||
};
|
};
|
||||||
|
@ -8,11 +8,11 @@ use common::{
|
|||||||
Agent, Alignment, BehaviorCapability, BehaviorState, Body, BuffKind, ControlAction,
|
Agent, Alignment, BehaviorCapability, BehaviorState, Body, BuffKind, ControlAction,
|
||||||
ControlEvent, Controller, InputKind, InventoryEvent, Pos, UtteranceKind,
|
ControlEvent, Controller, InputKind, InventoryEvent, Pos, UtteranceKind,
|
||||||
},
|
},
|
||||||
event::{Emitter, ServerEvent},
|
|
||||||
path::TraversalConfig,
|
path::TraversalConfig,
|
||||||
rtsim::{NpcAction, RtSimEntity},
|
rtsim::{NpcAction, RtSimEntity},
|
||||||
};
|
};
|
||||||
use rand::{prelude::ThreadRng, thread_rng, Rng};
|
use rand::{prelude::ThreadRng, thread_rng, Rng};
|
||||||
|
use server_agent::data::AgentEmitters;
|
||||||
use specs::Entity as EcsEntity;
|
use specs::Entity as EcsEntity;
|
||||||
use vek::{Vec2, Vec3};
|
use vek::{Vec2, Vec3};
|
||||||
|
|
||||||
@ -39,7 +39,7 @@ pub struct BehaviorData<'a, 'b, 'c> {
|
|||||||
pub agent: &'a mut Agent,
|
pub agent: &'a mut Agent,
|
||||||
pub agent_data: AgentData<'a>,
|
pub agent_data: AgentData<'a>,
|
||||||
pub read_data: &'a ReadData<'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 controller: &'a mut Controller,
|
||||||
pub rng: &'b mut ThreadRng,
|
pub rng: &'b mut ThreadRng,
|
||||||
}
|
}
|
||||||
@ -454,7 +454,7 @@ fn attack_if_owner_hurt(bdata: &mut BehaviorData) -> bool {
|
|||||||
bdata.agent,
|
bdata.agent,
|
||||||
bdata.read_data,
|
bdata.read_data,
|
||||||
bdata.controller,
|
bdata.controller,
|
||||||
bdata.event_emitter,
|
bdata.emitters,
|
||||||
bdata.rng,
|
bdata.rng,
|
||||||
);
|
);
|
||||||
return true;
|
return true;
|
||||||
@ -519,7 +519,7 @@ fn handle_rtsim_actions(bdata: &mut BehaviorData) -> bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
bdata.controller.push_utterance(UtteranceKind::Greeting);
|
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) => {
|
NpcAction::Attack(target) => {
|
||||||
@ -574,7 +574,7 @@ fn handle_timed_events(bdata: &mut BehaviorData) -> bool {
|
|||||||
bdata.agent,
|
bdata.agent,
|
||||||
bdata.controller,
|
bdata.controller,
|
||||||
bdata.read_data,
|
bdata.read_data,
|
||||||
bdata.event_emitter,
|
bdata.emitters,
|
||||||
AgentData::is_enemy,
|
AgentData::is_enemy,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
@ -582,7 +582,7 @@ fn handle_timed_events(bdata: &mut BehaviorData) -> bool {
|
|||||||
bdata.agent,
|
bdata.agent,
|
||||||
bdata.controller,
|
bdata.controller,
|
||||||
bdata.read_data,
|
bdata.read_data,
|
||||||
bdata.event_emitter,
|
bdata.emitters,
|
||||||
bdata.rng,
|
bdata.rng,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -728,7 +728,7 @@ fn do_combat(bdata: &mut BehaviorData) -> bool {
|
|||||||
agent,
|
agent,
|
||||||
agent_data,
|
agent_data,
|
||||||
read_data,
|
read_data,
|
||||||
event_emitter,
|
emitters,
|
||||||
controller,
|
controller,
|
||||||
rng,
|
rng,
|
||||||
} = bdata;
|
} = bdata;
|
||||||
@ -777,7 +777,7 @@ fn do_combat(bdata: &mut BehaviorData) -> bool {
|
|||||||
[ActionStateBehaviorTreeTimers::TimerBehaviorTree as usize]
|
[ActionStateBehaviorTreeTimers::TimerBehaviorTree as usize]
|
||||||
== 0.0
|
== 0.0
|
||||||
{
|
{
|
||||||
agent_data.cry_out(agent, event_emitter, read_data);
|
agent_data.cry_out(agent, emitters, read_data);
|
||||||
agent.behavior_state.timers
|
agent.behavior_state.timers
|
||||||
[ActionStateBehaviorTreeTimers::TimerBehaviorTree as usize] = 0.01;
|
[ActionStateBehaviorTreeTimers::TimerBehaviorTree as usize] = 0.01;
|
||||||
agent.flee_from_pos = {
|
agent.flee_from_pos = {
|
||||||
@ -803,12 +803,12 @@ fn do_combat(bdata: &mut BehaviorData) -> bool {
|
|||||||
[ActionStateBehaviorTreeTimers::TimerBehaviorTree as usize] = 0.0;
|
[ActionStateBehaviorTreeTimers::TimerBehaviorTree as usize] = 0.0;
|
||||||
agent.target = None;
|
agent.target = None;
|
||||||
agent.flee_from_pos = 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) {
|
} 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.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)
|
} else if is_invulnerable(target, read_data)
|
||||||
|| stop_pursuing(
|
|| stop_pursuing(
|
||||||
dist_sqrd,
|
dist_sqrd,
|
||||||
@ -820,7 +820,7 @@ fn do_combat(bdata: &mut BehaviorData) -> bool {
|
|||||||
)
|
)
|
||||||
{
|
{
|
||||||
agent.target = None;
|
agent.target = None;
|
||||||
agent_data.idle(agent, controller, read_data, event_emitter, rng);
|
agent_data.idle(agent, controller, read_data, emitters, rng);
|
||||||
} else {
|
} else {
|
||||||
let is_time_to_retarget =
|
let is_time_to_retarget =
|
||||||
read_data.time.0 - selected_at > RETARGETING_THRESHOLD_SECONDS;
|
read_data.time.0 - selected_at > RETARGETING_THRESHOLD_SECONDS;
|
||||||
@ -830,7 +830,7 @@ fn do_combat(bdata: &mut BehaviorData) -> bool {
|
|||||||
agent,
|
agent,
|
||||||
controller,
|
controller,
|
||||||
read_data,
|
read_data,
|
||||||
event_emitter,
|
emitters,
|
||||||
AgentData::is_enemy,
|
AgentData::is_enemy,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -849,7 +849,7 @@ fn do_combat(bdata: &mut BehaviorData) -> bool {
|
|||||||
controller,
|
controller,
|
||||||
target,
|
target,
|
||||||
read_data,
|
read_data,
|
||||||
event_emitter,
|
emitters,
|
||||||
rng,
|
rng,
|
||||||
remembers_fight_with(agent_data.rtsim_entity, read_data, target),
|
remembers_fight_with(agent_data.rtsim_entity, read_data, target),
|
||||||
);
|
);
|
||||||
|
@ -9,7 +9,7 @@ use common::{
|
|||||||
BehaviorState, Content, ControlAction, Item, TradingBehavior, UnresolvedChatMsg,
|
BehaviorState, Content, ControlAction, Item, TradingBehavior, UnresolvedChatMsg,
|
||||||
UtteranceKind,
|
UtteranceKind,
|
||||||
},
|
},
|
||||||
event::ServerEvent,
|
event::{ChatEvent, EmitExt, ProcessTradeActionEvent},
|
||||||
rtsim::{Actor, NpcInput, PersonalityTrait},
|
rtsim::{Actor, NpcInput, PersonalityTrait},
|
||||||
trade::{TradeAction, TradePhase, TradeResult},
|
trade::{TradeAction, TradePhase, TradeResult},
|
||||||
};
|
};
|
||||||
@ -76,7 +76,7 @@ pub fn handle_inbox_talk(bdata: &mut BehaviorData) -> bool {
|
|||||||
agent,
|
agent,
|
||||||
agent_data,
|
agent_data,
|
||||||
read_data,
|
read_data,
|
||||||
event_emitter,
|
emitters,
|
||||||
controller,
|
controller,
|
||||||
..
|
..
|
||||||
} = bdata;
|
} = bdata;
|
||||||
@ -171,7 +171,7 @@ pub fn handle_inbox_talk(bdata: &mut BehaviorData) -> bool {
|
|||||||
standard_response_msg()
|
standard_response_msg()
|
||||||
};
|
};
|
||||||
// TODO: Localise
|
// TODO: Localise
|
||||||
agent_data.chat_npc(Content::Plain(msg), event_emitter);
|
agent_data.chat_npc(Content::Plain(msg), emitters);
|
||||||
} else {
|
} else {
|
||||||
let mut rng = thread_rng();
|
let mut rng = thread_rng();
|
||||||
agent_data.chat_npc(
|
agent_data.chat_npc(
|
||||||
@ -179,7 +179,7 @@ pub fn handle_inbox_talk(bdata: &mut BehaviorData) -> bool {
|
|||||||
.rtsim_controller
|
.rtsim_controller
|
||||||
.personality
|
.personality
|
||||||
.get_generic_comment(&mut rng),
|
.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(
|
agent_data.chat_npc_if_allowed_to_speak(
|
||||||
Content::localized("npc-speech-merchant_advertisement"),
|
Content::localized("npc-speech-merchant_advertisement"),
|
||||||
agent,
|
agent,
|
||||||
event_emitter,
|
emitters,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
agent_data.chat_npc_if_allowed_to_speak(
|
agent_data.chat_npc_if_allowed_to_speak(
|
||||||
Content::localized("npc-speech-merchant_busy"),
|
Content::localized("npc-speech-merchant_busy"),
|
||||||
agent,
|
agent,
|
||||||
event_emitter,
|
emitters,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -206,7 +206,7 @@ pub fn handle_inbox_talk(bdata: &mut BehaviorData) -> bool {
|
|||||||
agent_data.chat_npc_if_allowed_to_speak(
|
agent_data.chat_npc_if_allowed_to_speak(
|
||||||
Content::localized("npc-speech-villager_decline_trade"),
|
Content::localized("npc-speech-villager_decline_trade"),
|
||||||
agent,
|
agent,
|
||||||
event_emitter,
|
emitters,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -224,7 +224,7 @@ pub fn handle_inbox_talk(bdata: &mut BehaviorData) -> bool {
|
|||||||
"{} ? I think it's {} {} from here!",
|
"{} ? I think it's {} {} from here!",
|
||||||
location.name, dist, dir
|
location.name, dist, dir
|
||||||
);
|
);
|
||||||
agent_data.chat_npc(Content::Plain(msg), event_emitter);
|
agent_data.chat_npc(Content::Plain(msg), emitters);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Subject::Person(person) => {
|
Subject::Person(person) => {
|
||||||
@ -259,7 +259,7 @@ pub fn handle_inbox_talk(bdata: &mut BehaviorData) -> bool {
|
|||||||
person.name()
|
person.name()
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
agent_data.chat_npc(Content::Plain(msg), event_emitter);
|
agent_data.chat_npc(Content::Plain(msg), emitters);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Subject::Work => {},
|
Subject::Work => {},
|
||||||
@ -276,7 +276,7 @@ pub fn handle_inbox_trade_invite(bdata: &mut BehaviorData) -> bool {
|
|||||||
agent,
|
agent,
|
||||||
agent_data,
|
agent_data,
|
||||||
read_data,
|
read_data,
|
||||||
event_emitter,
|
emitters,
|
||||||
controller,
|
controller,
|
||||||
..
|
..
|
||||||
} = bdata;
|
} = bdata;
|
||||||
@ -313,7 +313,7 @@ pub fn handle_inbox_trade_invite(bdata: &mut BehaviorData) -> bool {
|
|||||||
agent_data.chat_npc_if_allowed_to_speak(
|
agent_data.chat_npc_if_allowed_to_speak(
|
||||||
Content::localized("npc-speech-merchant_busy"),
|
Content::localized("npc-speech-merchant_busy"),
|
||||||
agent,
|
agent,
|
||||||
event_emitter,
|
emitters,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -322,7 +322,7 @@ pub fn handle_inbox_trade_invite(bdata: &mut BehaviorData) -> bool {
|
|||||||
agent_data.chat_npc_if_allowed_to_speak(
|
agent_data.chat_npc_if_allowed_to_speak(
|
||||||
Content::localized("npc-speech-villager_decline_trade"),
|
Content::localized("npc-speech-villager_decline_trade"),
|
||||||
agent,
|
agent,
|
||||||
event_emitter,
|
emitters,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -364,7 +364,7 @@ pub fn handle_inbox_finished_trade(bdata: &mut BehaviorData) -> bool {
|
|||||||
let BehaviorData {
|
let BehaviorData {
|
||||||
agent,
|
agent,
|
||||||
agent_data,
|
agent_data,
|
||||||
event_emitter,
|
emitters,
|
||||||
..
|
..
|
||||||
} = bdata;
|
} = bdata;
|
||||||
|
|
||||||
@ -379,14 +379,14 @@ pub fn handle_inbox_finished_trade(bdata: &mut BehaviorData) -> bool {
|
|||||||
agent_data.chat_npc_if_allowed_to_speak(
|
agent_data.chat_npc_if_allowed_to_speak(
|
||||||
Content::localized("npc-speech-merchant_trade_successful"),
|
Content::localized("npc-speech-merchant_trade_successful"),
|
||||||
agent,
|
agent,
|
||||||
event_emitter,
|
emitters,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
_ => {
|
_ => {
|
||||||
agent_data.chat_npc_if_allowed_to_speak(
|
agent_data.chat_npc_if_allowed_to_speak(
|
||||||
Content::localized("npc-speech-merchant_trade_declined"),
|
Content::localized("npc-speech-merchant_trade_declined"),
|
||||||
agent,
|
agent,
|
||||||
event_emitter,
|
emitters,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -404,7 +404,7 @@ pub fn handle_inbox_update_pending_trade(bdata: &mut BehaviorData) -> bool {
|
|||||||
agent,
|
agent,
|
||||||
agent_data,
|
agent_data,
|
||||||
read_data,
|
read_data,
|
||||||
event_emitter,
|
emitters,
|
||||||
..
|
..
|
||||||
} = bdata;
|
} = bdata;
|
||||||
|
|
||||||
@ -422,13 +422,13 @@ pub fn handle_inbox_update_pending_trade(bdata: &mut BehaviorData) -> bool {
|
|||||||
.as_ref()
|
.as_ref()
|
||||||
.and_then(|tgt_data| read_data.uids.get(tgt_data.target))
|
.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,
|
*agent_data.uid,
|
||||||
*with,
|
*with,
|
||||||
content,
|
content,
|
||||||
)));
|
)));
|
||||||
} else {
|
} else {
|
||||||
event_emitter.emit(ServerEvent::Chat(UnresolvedChatMsg::npc_say(
|
emitters.emit(ChatEvent(UnresolvedChatMsg::npc_say(
|
||||||
*agent_data.uid,
|
*agent_data.uid,
|
||||||
content,
|
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
|
// the phase is included in the message, this shouldn't
|
||||||
// result in fully accepting an unfavourable trade))
|
// result in fully accepting an unfavourable trade))
|
||||||
if !pending.accept_flags[who] && !pending.is_empty_trade() {
|
if !pending.accept_flags[who] && !pending.is_empty_trade() {
|
||||||
event_emitter.emit(ServerEvent::ProcessTradeAction(
|
emitters.emit(ProcessTradeActionEvent(
|
||||||
*agent_data.entity,
|
*agent_data.entity,
|
||||||
tradeid,
|
tradeid,
|
||||||
TradeAction::Accept(pending.phase),
|
TradeAction::Accept(pending.phase),
|
||||||
@ -482,7 +482,7 @@ pub fn handle_inbox_update_pending_trade(bdata: &mut BehaviorData) -> bool {
|
|||||||
// decline
|
// decline
|
||||||
agent.behavior.unset(BehaviorState::TRADING);
|
agent.behavior.unset(BehaviorState::TRADING);
|
||||||
agent.target = None;
|
agent.target = None;
|
||||||
event_emitter.emit(ServerEvent::ProcessTradeAction(
|
emitters.emit(ProcessTradeActionEvent(
|
||||||
*agent_data.entity,
|
*agent_data.entity,
|
||||||
tradeid,
|
tradeid,
|
||||||
TradeAction::Decline,
|
TradeAction::Decline,
|
||||||
@ -517,7 +517,7 @@ pub fn handle_inbox_update_pending_trade(bdata: &mut BehaviorData) -> bool {
|
|||||||
&& !pending.offers[1 - who].is_empty()
|
&& !pending.offers[1 - who].is_empty()
|
||||||
&& only_food
|
&& only_food
|
||||||
{
|
{
|
||||||
event_emitter.emit(ServerEvent::ProcessTradeAction(
|
emitters.emit(ProcessTradeActionEvent(
|
||||||
*agent_data.entity,
|
*agent_data.entity,
|
||||||
tradeid,
|
tradeid,
|
||||||
TradeAction::Accept(pending.phase),
|
TradeAction::Accept(pending.phase),
|
||||||
@ -527,7 +527,7 @@ pub fn handle_inbox_update_pending_trade(bdata: &mut BehaviorData) -> bool {
|
|||||||
TradingBehavior::None => {
|
TradingBehavior::None => {
|
||||||
agent.behavior.unset(BehaviorState::TRADING);
|
agent.behavior.unset(BehaviorState::TRADING);
|
||||||
agent.target = None;
|
agent.target = None;
|
||||||
event_emitter.emit(ServerEvent::ProcessTradeAction(
|
emitters.emit(ProcessTradeActionEvent(
|
||||||
*agent_data.entity,
|
*agent_data.entity,
|
||||||
tradeid,
|
tradeid,
|
||||||
TradeAction::Decline,
|
TradeAction::Decline,
|
||||||
@ -549,7 +549,7 @@ pub fn handle_inbox_cancel_interactions(bdata: &mut BehaviorData) -> bool {
|
|||||||
let BehaviorData {
|
let BehaviorData {
|
||||||
agent,
|
agent,
|
||||||
agent_data,
|
agent_data,
|
||||||
event_emitter,
|
emitters,
|
||||||
controller,
|
controller,
|
||||||
..
|
..
|
||||||
} = bdata;
|
} = bdata;
|
||||||
@ -566,7 +566,7 @@ pub fn handle_inbox_cancel_interactions(bdata: &mut BehaviorData) -> bool {
|
|||||||
agent_data.chat_npc_if_allowed_to_speak(
|
agent_data.chat_npc_if_allowed_to_speak(
|
||||||
Content::localized("npc-speech-villager_busy"),
|
Content::localized("npc-speech-villager_busy"),
|
||||||
agent,
|
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(
|
agent_data.chat_npc_if_allowed_to_speak(
|
||||||
Content::localized("npc-speech-merchant_busy"),
|
Content::localized("npc-speech-merchant_busy"),
|
||||||
agent,
|
agent,
|
||||||
event_emitter,
|
emitters,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
agent_data.chat_npc_if_allowed_to_speak(
|
agent_data.chat_npc_if_allowed_to_speak(
|
||||||
Content::localized("npc-speech-villager_busy"),
|
Content::localized("npc-speech-villager_busy"),
|
||||||
agent,
|
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(
|
agent_data.chat_npc_if_allowed_to_speak(
|
||||||
Content::localized("npc-speech-merchant_trade_successful"),
|
Content::localized("npc-speech-merchant_trade_successful"),
|
||||||
agent,
|
agent,
|
||||||
event_emitter,
|
emitters,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
_ => {
|
_ => {
|
||||||
agent_data.chat_npc_if_allowed_to_speak(
|
agent_data.chat_npc_if_allowed_to_speak(
|
||||||
Content::localized("npc-speech-merchant_trade_declined"),
|
Content::localized("npc-speech-merchant_trade_declined"),
|
||||||
agent,
|
agent,
|
||||||
event_emitter,
|
emitters,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -622,7 +622,7 @@ pub fn handle_inbox_cancel_interactions(bdata: &mut BehaviorData) -> bool {
|
|||||||
let (tradeid, _pending, _prices, _inventories) = &**boxval;
|
let (tradeid, _pending, _prices, _inventories) = &**boxval;
|
||||||
agent.behavior.unset(BehaviorState::TRADING);
|
agent.behavior.unset(BehaviorState::TRADING);
|
||||||
agent.target = None;
|
agent.target = None;
|
||||||
event_emitter.emit(ServerEvent::ProcessTradeAction(
|
emitters.emit(ProcessTradeActionEvent(
|
||||||
*agent_data.entity,
|
*agent_data.entity,
|
||||||
*tradeid,
|
*tradeid,
|
||||||
TradeAction::Decline,
|
TradeAction::Decline,
|
||||||
@ -630,7 +630,7 @@ pub fn handle_inbox_cancel_interactions(bdata: &mut BehaviorData) -> bool {
|
|||||||
agent_data.chat_npc_if_allowed_to_speak(
|
agent_data.chat_npc_if_allowed_to_speak(
|
||||||
Content::localized("npc-speech-merchant_trade_cancelled_hostile"),
|
Content::localized("npc-speech-merchant_trade_cancelled_hostile"),
|
||||||
agent,
|
agent,
|
||||||
event_emitter,
|
emitters,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
AgentEvent::ServerSound(_) | AgentEvent::Hurt => return false,
|
AgentEvent::ServerSound(_) | AgentEvent::Hurt => return false,
|
||||||
|
@ -12,21 +12,35 @@ use crate::{
|
|||||||
};
|
};
|
||||||
use common::{
|
use common::{
|
||||||
comp::{Admin, AdminRole, ChatType, Player, Presence, Waypoint},
|
comp::{Admin, AdminRole, ChatType, Player, Presence, Waypoint},
|
||||||
event::{EventBus, ServerEvent},
|
event::{
|
||||||
|
ChatEvent, ClientDisconnectEvent, DeleteCharacterEvent, EmitExt, InitializeCharacterEvent,
|
||||||
|
InitializeSpectatorEvent,
|
||||||
|
},
|
||||||
|
event_emitters,
|
||||||
resources::Time,
|
resources::Time,
|
||||||
terrain::TerrainChunkSize,
|
terrain::TerrainChunkSize,
|
||||||
uid::Uid,
|
uid::Uid,
|
||||||
};
|
};
|
||||||
use common_ecs::{Job, Origin, Phase, System};
|
use common_ecs::{Job, Origin, Phase, System};
|
||||||
use common_net::msg::{ClientGeneral, ServerGeneral};
|
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 std::sync::{atomic::Ordering, Arc};
|
||||||
use tracing::{debug, error};
|
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 {
|
impl Sys {
|
||||||
#[allow(clippy::too_many_arguments)] // Shhhh, go bother someone else clippy
|
#[allow(clippy::too_many_arguments)] // Shhhh, go bother someone else clippy
|
||||||
fn handle_client_character_screen_msg(
|
fn handle_client_character_screen_msg(
|
||||||
server_emitter: &mut common::event::Emitter<'_, ServerEvent>,
|
emitters: &mut Emitters,
|
||||||
entity: specs::Entity,
|
entity: specs::Entity,
|
||||||
client: &Client,
|
client: &Client,
|
||||||
character_loader: &ReadExpect<'_, CharacterLoader>,
|
character_loader: &ReadExpect<'_, CharacterLoader>,
|
||||||
@ -65,9 +79,7 @@ impl Sys {
|
|||||||
|
|
||||||
if !client.login_msg_sent.load(Ordering::Relaxed) {
|
if !client.login_msg_sent.load(Ordering::Relaxed) {
|
||||||
if let Some(player_uid) = uids.get(entity) {
|
if let Some(player_uid) = uids.get(entity) {
|
||||||
server_emitter.emit(ServerEvent::Chat(
|
emitters.emit(ChatEvent(ChatType::Online(*player_uid).into_plain_msg("")));
|
||||||
ChatType::Online(*player_uid).into_plain_msg(""),
|
|
||||||
));
|
|
||||||
|
|
||||||
client.login_msg_sent.store(true, Ordering::Relaxed);
|
client.login_msg_sent.store(true, Ordering::Relaxed);
|
||||||
}
|
}
|
||||||
@ -82,8 +94,7 @@ impl Sys {
|
|||||||
{
|
{
|
||||||
send_join_messages()?;
|
send_join_messages()?;
|
||||||
|
|
||||||
server_emitter
|
emitters.emit(InitializeSpectatorEvent(entity, requested_view_distances));
|
||||||
.emit(ServerEvent::InitSpectator(entity, requested_view_distances));
|
|
||||||
} else {
|
} else {
|
||||||
debug!("dropped Spectate msg from unprivileged client")
|
debug!("dropped Spectate msg from unprivileged client")
|
||||||
}
|
}
|
||||||
@ -131,7 +142,7 @@ impl Sys {
|
|||||||
|
|
||||||
// Start inserting non-persisted/default components for the entity
|
// Start inserting non-persisted/default components for the entity
|
||||||
// while we load the DB data
|
// while we load the DB data
|
||||||
server_emitter.emit(ServerEvent::InitCharacterData {
|
emitters.emit(InitializeCharacterEvent {
|
||||||
entity,
|
entity,
|
||||||
character_id,
|
character_id,
|
||||||
requested_view_distances,
|
requested_view_distances,
|
||||||
@ -242,7 +253,7 @@ impl Sys {
|
|||||||
},
|
},
|
||||||
ClientGeneral::DeleteCharacter(character_id) => {
|
ClientGeneral::DeleteCharacter(character_id) => {
|
||||||
if let Some(player) = players.get(entity) {
|
if let Some(player) = players.get(entity) {
|
||||||
server_emitter.emit(ServerEvent::DeleteCharacter {
|
emitters.emit(DeleteCharacterEvent {
|
||||||
entity,
|
entity,
|
||||||
requesting_player_uuid: player.uuid().to_string(),
|
requesting_player_uuid: player.uuid().to_string(),
|
||||||
character_id,
|
character_id,
|
||||||
@ -251,7 +262,7 @@ impl Sys {
|
|||||||
},
|
},
|
||||||
_ => {
|
_ => {
|
||||||
debug!("Kicking possibly misbehaving client due to invalid character request");
|
debug!("Kicking possibly misbehaving client due to invalid character request");
|
||||||
server_emitter.emit(ServerEvent::ClientDisconnect(
|
emitters.emit(ClientDisconnectEvent(
|
||||||
entity,
|
entity,
|
||||||
common::comp::DisconnectReason::NetworkError,
|
common::comp::DisconnectReason::NetworkError,
|
||||||
));
|
));
|
||||||
@ -267,7 +278,7 @@ pub struct Sys;
|
|||||||
impl<'a> System<'a> for Sys {
|
impl<'a> System<'a> for Sys {
|
||||||
type SystemData = (
|
type SystemData = (
|
||||||
Entities<'a>,
|
Entities<'a>,
|
||||||
Read<'a, EventBus<ServerEvent>>,
|
Events<'a>,
|
||||||
ReadExpect<'a, CharacterLoader>,
|
ReadExpect<'a, CharacterLoader>,
|
||||||
WriteExpect<'a, CharacterUpdater>,
|
WriteExpect<'a, CharacterUpdater>,
|
||||||
ReadStorage<'a, Uid>,
|
ReadStorage<'a, Uid>,
|
||||||
@ -291,7 +302,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
_job: &mut Job<Self>,
|
_job: &mut Job<Self>,
|
||||||
(
|
(
|
||||||
entities,
|
entities,
|
||||||
server_event_bus,
|
events,
|
||||||
character_loader,
|
character_loader,
|
||||||
mut character_updater,
|
mut character_updater,
|
||||||
uids,
|
uids,
|
||||||
@ -307,12 +318,12 @@ impl<'a> System<'a> for Sys {
|
|||||||
world,
|
world,
|
||||||
): Self::SystemData,
|
): Self::SystemData,
|
||||||
) {
|
) {
|
||||||
let mut server_emitter = server_event_bus.emitter();
|
let mut emitters = events.get_emitters();
|
||||||
|
|
||||||
for (entity, client) in (&entities, &mut clients).join() {
|
for (entity, client) in (&entities, &mut clients).join() {
|
||||||
let _ = super::try_recv_all(client, 1, |client, msg| {
|
let _ = super::try_recv_all(client, 1, |client, msg| {
|
||||||
Self::handle_client_character_screen_msg(
|
Self::handle_client_character_screen_msg(
|
||||||
&mut server_emitter,
|
&mut emitters,
|
||||||
entity,
|
entity,
|
||||||
client,
|
client,
|
||||||
&character_loader,
|
&character_loader,
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
use crate::client::Client;
|
use crate::client::Client;
|
||||||
use common::{
|
use common::{
|
||||||
comp::{ChatMode, ChatType, Content, Group, Player},
|
comp::{ChatMode, ChatType, Content, Group, Player},
|
||||||
event::{EventBus, ServerEvent},
|
event::{self, EmitExt},
|
||||||
|
event_emitters,
|
||||||
resources::ProgramTime,
|
resources::ProgramTime,
|
||||||
uid::Uid,
|
uid::Uid,
|
||||||
};
|
};
|
||||||
@ -11,9 +12,18 @@ use rayon::prelude::*;
|
|||||||
use specs::{Entities, LendJoin, ParJoin, Read, ReadStorage, WriteStorage};
|
use specs::{Entities, LendJoin, ParJoin, Read, ReadStorage, WriteStorage};
|
||||||
use tracing::{debug, error, warn};
|
use tracing::{debug, error, warn};
|
||||||
|
|
||||||
|
event_emitters! {
|
||||||
|
struct Events[Emitters] {
|
||||||
|
command: event::CommandEvent,
|
||||||
|
client_disconnect: event::ClientDisconnectEvent,
|
||||||
|
chat: event::ChatEvent,
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Sys {
|
impl Sys {
|
||||||
fn handle_general_msg(
|
fn handle_general_msg(
|
||||||
server_emitter: &mut common::event::Emitter<'_, ServerEvent>,
|
emitters: &mut Emitters,
|
||||||
entity: specs::Entity,
|
entity: specs::Entity,
|
||||||
client: &Client,
|
client: &Client,
|
||||||
player: Option<&Player>,
|
player: Option<&Player>,
|
||||||
@ -35,7 +45,7 @@ impl Sys {
|
|||||||
groups.get(entity).copied(),
|
groups.get(entity).copied(),
|
||||||
) {
|
) {
|
||||||
Ok(message) => {
|
Ok(message) => {
|
||||||
server_emitter.emit(ServerEvent::Chat(message));
|
emitters.emit(event::ChatEvent(message));
|
||||||
},
|
},
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
client.send_fallible(ServerGeneral::ChatMsg(
|
client.send_fallible(ServerGeneral::ChatMsg(
|
||||||
@ -52,19 +62,19 @@ impl Sys {
|
|||||||
},
|
},
|
||||||
ClientGeneral::Command(name, args) => {
|
ClientGeneral::Command(name, args) => {
|
||||||
if player.is_some() {
|
if player.is_some() {
|
||||||
server_emitter.emit(ServerEvent::Command(entity, name, args));
|
emitters.emit(event::CommandEvent(entity, name, args));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ClientGeneral::Terminate => {
|
ClientGeneral::Terminate => {
|
||||||
debug!(?entity, "Client send message to terminate session");
|
debug!(?entity, "Client send message to terminate session");
|
||||||
server_emitter.emit(ServerEvent::ClientDisconnect(
|
emitters.emit(event::ClientDisconnectEvent(
|
||||||
entity,
|
entity,
|
||||||
common::comp::DisconnectReason::ClientRequested,
|
common::comp::DisconnectReason::ClientRequested,
|
||||||
));
|
));
|
||||||
},
|
},
|
||||||
_ => {
|
_ => {
|
||||||
debug!("Kicking possible misbehaving client due to invalid message request");
|
debug!("Kicking possible misbehaving client due to invalid message request");
|
||||||
server_emitter.emit(ServerEvent::ClientDisconnect(
|
emitters.emit(event::ClientDisconnectEvent(
|
||||||
entity,
|
entity,
|
||||||
common::comp::DisconnectReason::NetworkError,
|
common::comp::DisconnectReason::NetworkError,
|
||||||
));
|
));
|
||||||
@ -80,7 +90,7 @@ pub struct Sys;
|
|||||||
impl<'a> System<'a> for Sys {
|
impl<'a> System<'a> for Sys {
|
||||||
type SystemData = (
|
type SystemData = (
|
||||||
Entities<'a>,
|
Entities<'a>,
|
||||||
Read<'a, EventBus<ServerEvent>>,
|
Events<'a>,
|
||||||
Read<'a, ProgramTime>,
|
Read<'a, ProgramTime>,
|
||||||
ReadStorage<'a, Uid>,
|
ReadStorage<'a, Uid>,
|
||||||
ReadStorage<'a, ChatMode>,
|
ReadStorage<'a, ChatMode>,
|
||||||
@ -95,16 +105,16 @@ impl<'a> System<'a> for Sys {
|
|||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
_job: &mut Job<Self>,
|
_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())
|
(&entities, &mut clients, players.maybe())
|
||||||
.par_join()
|
.par_join()
|
||||||
.for_each_init(
|
.for_each_init(
|
||||||
|| server_event_bus.emitter(),
|
|| events.get_emitters(),
|
||||||
|server_emitter, (entity, client, player)| {
|
|emitters, (entity, client, player)| {
|
||||||
let res = super::try_recv_all(client, 3, |client, msg| {
|
let res = super::try_recv_all(client, 3, |client, msg| {
|
||||||
Self::handle_general_msg(
|
Self::handle_general_msg(
|
||||||
server_emitter,
|
emitters,
|
||||||
entity,
|
entity,
|
||||||
client,
|
client,
|
||||||
player,
|
player,
|
||||||
|
@ -6,7 +6,8 @@ use common::{
|
|||||||
Admin, AdminRole, CanBuild, ControlEvent, Controller, ForceUpdate, Health, Ori, Player,
|
Admin, AdminRole, CanBuild, ControlEvent, Controller, ForceUpdate, Health, Ori, Player,
|
||||||
Pos, Presence, PresenceKind, SkillSet, Vel,
|
Pos, Presence, PresenceKind, SkillSet, Vel,
|
||||||
},
|
},
|
||||||
event::{EventBus, ServerEvent},
|
event::{self, EmitExt},
|
||||||
|
event_emitters,
|
||||||
link::Is,
|
link::Is,
|
||||||
mounting::{Rider, VolumeRider},
|
mounting::{Rider, VolumeRider},
|
||||||
resources::{DeltaTime, PlayerPhysicsSetting, PlayerPhysicsSettings},
|
resources::{DeltaTime, PlayerPhysicsSetting, PlayerPhysicsSettings},
|
||||||
@ -42,10 +43,19 @@ struct RareWrites<'a, 'b> {
|
|||||||
_terrain_persistence: &'b mut TerrainPersistenceData<'a>,
|
_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 {
|
impl Sys {
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
fn handle_client_in_game_msg(
|
fn handle_client_in_game_msg(
|
||||||
server_emitter: &mut common::event::Emitter<'_, ServerEvent>,
|
emitters: &mut Emitters,
|
||||||
entity: specs::Entity,
|
entity: specs::Entity,
|
||||||
client: &Client,
|
client: &Client,
|
||||||
maybe_presence: &mut Option<&mut Presence>,
|
maybe_presence: &mut Option<&mut Presence>,
|
||||||
@ -78,7 +88,7 @@ impl Sys {
|
|||||||
match msg {
|
match msg {
|
||||||
// Go back to registered state (char selection screen)
|
// Go back to registered state (char selection screen)
|
||||||
ClientGeneral::ExitInGame => {
|
ClientGeneral::ExitInGame => {
|
||||||
server_emitter.emit(ServerEvent::ExitIngame { entity });
|
emitters.emit(event::ExitIngameEvent { entity });
|
||||||
client.send(ServerGeneral::ExitInGameSuccess)?;
|
client.send(ServerGeneral::ExitInGameSuccess)?;
|
||||||
*maybe_presence = None;
|
*maybe_presence = None;
|
||||||
},
|
},
|
||||||
@ -212,7 +222,7 @@ impl Sys {
|
|||||||
.transpose();
|
.transpose();
|
||||||
},
|
},
|
||||||
ClientGeneral::RequestSiteInfo(id) => {
|
ClientGeneral::RequestSiteInfo(id) => {
|
||||||
server_emitter.emit(ServerEvent::RequestSiteInfo { entity, id });
|
emitters.emit(event::RequestSiteInfoEvent { entity, id });
|
||||||
},
|
},
|
||||||
ClientGeneral::RequestPlayerPhysics {
|
ClientGeneral::RequestPlayerPhysics {
|
||||||
server_authoritative,
|
server_authoritative,
|
||||||
@ -227,7 +237,7 @@ impl Sys {
|
|||||||
presence.lossy_terrain_compression = lossy_terrain_compression;
|
presence.lossy_terrain_compression = lossy_terrain_compression;
|
||||||
},
|
},
|
||||||
ClientGeneral::UpdateMapMarker(update) => {
|
ClientGeneral::UpdateMapMarker(update) => {
|
||||||
server_emitter.emit(ServerEvent::UpdateMapMarker { entity, update });
|
emitters.emit(event::UpdateMapMarkerEvent { entity, update });
|
||||||
},
|
},
|
||||||
ClientGeneral::SpectatePosition(pos) => {
|
ClientGeneral::SpectatePosition(pos) => {
|
||||||
if let Some(admin) = maybe_admin
|
if let Some(admin) = maybe_admin
|
||||||
@ -251,7 +261,7 @@ impl Sys {
|
|||||||
| ClientGeneral::Command(..)
|
| ClientGeneral::Command(..)
|
||||||
| ClientGeneral::Terminate => {
|
| ClientGeneral::Terminate => {
|
||||||
debug!("Kicking possibly misbehaving client due to invalid client in game request");
|
debug!("Kicking possibly misbehaving client due to invalid client in game request");
|
||||||
server_emitter.emit(ServerEvent::ClientDisconnect(
|
emitters.emit(event::ClientDisconnectEvent(
|
||||||
entity,
|
entity,
|
||||||
common::comp::DisconnectReason::NetworkError,
|
common::comp::DisconnectReason::NetworkError,
|
||||||
));
|
));
|
||||||
@ -268,7 +278,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
#[allow(clippy::type_complexity)]
|
#[allow(clippy::type_complexity)]
|
||||||
type SystemData = (
|
type SystemData = (
|
||||||
Entities<'a>,
|
Entities<'a>,
|
||||||
Read<'a, EventBus<ServerEvent>>,
|
Events<'a>,
|
||||||
ReadExpect<'a, TerrainGrid>,
|
ReadExpect<'a, TerrainGrid>,
|
||||||
ReadExpect<'a, SlowJobPool>,
|
ReadExpect<'a, SlowJobPool>,
|
||||||
ReadStorage<'a, CanBuild>,
|
ReadStorage<'a, CanBuild>,
|
||||||
@ -301,7 +311,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
_job: &mut Job<Self>,
|
_job: &mut Job<Self>,
|
||||||
(
|
(
|
||||||
entities,
|
entities,
|
||||||
server_event_bus,
|
events,
|
||||||
terrain,
|
terrain,
|
||||||
slow_jobs,
|
slow_jobs,
|
||||||
can_build,
|
can_build,
|
||||||
@ -353,8 +363,8 @@ impl<'a> System<'a> for Sys {
|
|||||||
// NOTE: Required because Specs has very poor work splitting for sparse joins.
|
// NOTE: Required because Specs has very poor work splitting for sparse joins.
|
||||||
.par_bridge()
|
.par_bridge()
|
||||||
.map_init(
|
.map_init(
|
||||||
|| server_event_bus.emitter(),
|
|| events.get_emitters(),
|
||||||
|server_emitter, (
|
|emitters, (
|
||||||
entity,
|
entity,
|
||||||
client,
|
client,
|
||||||
mut maybe_presence,
|
mut maybe_presence,
|
||||||
@ -382,7 +392,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
let mut player_physics = None;
|
let mut player_physics = None;
|
||||||
let _ = super::try_recv_all(client, 2, |client, msg| {
|
let _ = super::try_recv_all(client, 2, |client, msg| {
|
||||||
Self::handle_client_in_game_msg(
|
Self::handle_client_in_game_msg(
|
||||||
server_emitter,
|
emitters,
|
||||||
entity,
|
entity,
|
||||||
client,
|
client,
|
||||||
&mut clearable_maybe_presence,
|
&mut clearable_maybe_presence,
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use crate::{client::Client, Settings};
|
use crate::{client::Client, Settings};
|
||||||
use common::{
|
use common::{
|
||||||
event::{EventBus, ServerEvent},
|
event::{ClientDisconnectEvent, EventBus},
|
||||||
resources::ProgramTime,
|
resources::ProgramTime,
|
||||||
};
|
};
|
||||||
use common_ecs::{Job, Origin, Phase, System};
|
use common_ecs::{Job, Origin, Phase, System};
|
||||||
@ -25,7 +25,7 @@ pub struct Sys;
|
|||||||
impl<'a> System<'a> for Sys {
|
impl<'a> System<'a> for Sys {
|
||||||
type SystemData = (
|
type SystemData = (
|
||||||
Entities<'a>,
|
Entities<'a>,
|
||||||
Read<'a, EventBus<ServerEvent>>,
|
Read<'a, EventBus<ClientDisconnectEvent>>,
|
||||||
Read<'a, ProgramTime>,
|
Read<'a, ProgramTime>,
|
||||||
WriteStorage<'a, Client>,
|
WriteStorage<'a, Client>,
|
||||||
Read<'a, Settings>,
|
Read<'a, Settings>,
|
||||||
@ -37,11 +37,11 @@ impl<'a> System<'a> for Sys {
|
|||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
_job: &mut Job<Self>,
|
_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(
|
(&entities, &mut clients).par_join().for_each_init(
|
||||||
|| server_event_bus.emitter(),
|
|| client_disconnect.emitter(),
|
||||||
|server_emitter, (entity, client)| {
|
|client_disconnect_emitter, (entity, client)| {
|
||||||
// ignore network events
|
// ignore network events
|
||||||
while let Some(Ok(Some(_))) =
|
while let Some(Ok(Some(_))) =
|
||||||
client.participant.as_mut().map(|p| p.try_fetch_event())
|
client.participant.as_mut().map(|p| p.try_fetch_event())
|
||||||
@ -52,7 +52,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
match res {
|
match res {
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
debug!(?entity, ?e, "network error with client, disconnecting");
|
debug!(?entity, ?e, "network error with client, disconnecting");
|
||||||
server_emitter.emit(ServerEvent::ClientDisconnect(
|
client_disconnect_emitter.emit(ClientDisconnectEvent(
|
||||||
entity,
|
entity,
|
||||||
common::comp::DisconnectReason::NetworkError,
|
common::comp::DisconnectReason::NetworkError,
|
||||||
));
|
));
|
||||||
@ -67,7 +67,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
// Timeout
|
// Timeout
|
||||||
{
|
{
|
||||||
info!(?entity, "timeout error with client, disconnecting");
|
info!(?entity, "timeout error with client, disconnecting");
|
||||||
server_emitter.emit(ServerEvent::ClientDisconnect(
|
client_disconnect_emitter.emit(ClientDisconnectEvent(
|
||||||
entity,
|
entity,
|
||||||
common::comp::DisconnectReason::Timeout,
|
common::comp::DisconnectReason::Timeout,
|
||||||
));
|
));
|
||||||
|
@ -7,7 +7,7 @@ use crate::{
|
|||||||
};
|
};
|
||||||
use common::{
|
use common::{
|
||||||
comp::{self, Admin, Player, Stats},
|
comp::{self, Admin, Player, Stats},
|
||||||
event::{EventBus, ServerEvent},
|
event::{ClientDisconnectEvent, EventBus, MakeAdminEvent},
|
||||||
recipe::{default_component_recipe_book, default_recipe_book, default_repair_recipe_book},
|
recipe::{default_component_recipe_book, default_recipe_book, default_repair_recipe_book},
|
||||||
resources::TimeOfDay,
|
resources::TimeOfDay,
|
||||||
shared_server_config::ServerConstants,
|
shared_server_config::ServerConstants,
|
||||||
@ -42,7 +42,8 @@ pub struct ReadData<'a> {
|
|||||||
entities: Entities<'a>,
|
entities: Entities<'a>,
|
||||||
stats: ReadStorage<'a, Stats>,
|
stats: ReadStorage<'a, Stats>,
|
||||||
uids: ReadStorage<'a, Uid>,
|
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>,
|
login_provider: ReadExpect<'a, LoginProvider>,
|
||||||
player_metrics: ReadExpect<'a, PlayerMetrics>,
|
player_metrics: ReadExpect<'a, PlayerMetrics>,
|
||||||
settings: ReadExpect<'a, Settings>,
|
settings: ReadExpect<'a, Settings>,
|
||||||
@ -62,7 +63,6 @@ pub struct ReadData<'a> {
|
|||||||
pub struct Sys;
|
pub struct Sys;
|
||||||
impl<'a> System<'a> for Sys {
|
impl<'a> System<'a> for Sys {
|
||||||
type SystemData = (
|
type SystemData = (
|
||||||
Read<'a, EventBus<ServerEvent>>,
|
|
||||||
ReadData<'a>,
|
ReadData<'a>,
|
||||||
WriteStorage<'a, Client>,
|
WriteStorage<'a, Client>,
|
||||||
WriteStorage<'a, Player>,
|
WriteStorage<'a, Player>,
|
||||||
@ -75,8 +75,9 @@ impl<'a> System<'a> for Sys {
|
|||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
_job: &mut Job<Self>,
|
_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
|
// 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
|
// have to do a linear scan over all entities on each login to see if
|
||||||
// it's a duplicate).
|
// 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.
|
// NOTE: Required because Specs has very poor work splitting for sparse joins.
|
||||||
.par_bridge()
|
.par_bridge()
|
||||||
.for_each_init(
|
.for_each_init(
|
||||||
|| read_data.server_event_bus.emitter(),
|
|| read_data.client_disconnect_events.emitter(),
|
||||||
|server_emitter, (entity, uid, client, _, pending)| {
|
|client_disconnect_emitter, (entity, uid, client, _, pending)| {
|
||||||
prof_span!("msg::register login");
|
prof_span!("msg::register login");
|
||||||
if let Err(e) = || -> Result<(), crate::error::Error> {
|
if let Err(e) = || -> Result<(), crate::error::Error> {
|
||||||
let extra_checks = |username: String, uuid: authc::Uuid| {
|
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
|
// NOTE: Done only on error to avoid doing extra work within
|
||||||
// the lock.
|
// the lock.
|
||||||
trace!(?e, "pending login returned error");
|
trace!(?e, "pending login returned error");
|
||||||
server_emitter.emit(ServerEvent::ClientDisconnect(
|
client_disconnect_emitter.emit(ClientDisconnectEvent(
|
||||||
entity,
|
entity,
|
||||||
common::comp::DisconnectReason::Kicked,
|
common::comp::DisconnectReason::Kicked,
|
||||||
));
|
));
|
||||||
@ -302,7 +303,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
// Remove old client
|
// Remove old client
|
||||||
server_emitter.emit(ServerEvent::ClientDisconnect(
|
client_disconnect_emitter.emit(ClientDisconnectEvent(
|
||||||
old_entity,
|
old_entity,
|
||||||
common::comp::DisconnectReason::NewerLogin,
|
common::comp::DisconnectReason::NewerLogin,
|
||||||
));
|
));
|
||||||
@ -408,7 +409,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
if let Some(admin) = admin {
|
if let Some(admin) = admin {
|
||||||
// We need to defer writing to the Admin storage since it's borrowed immutably
|
// We need to defer writing to the Admin storage since it's borrowed immutably
|
||||||
// by this system via TrackedStorages.
|
// by this system via TrackedStorages.
|
||||||
event_bus.emit_now(ServerEvent::MakeAdmin {
|
make_admin_emitter.emit(MakeAdminEvent {
|
||||||
entity,
|
entity,
|
||||||
admin: Admin(admin.role.into()),
|
admin: Admin(admin.role.into()),
|
||||||
uuid,
|
uuid,
|
||||||
|
@ -4,7 +4,7 @@ use crate::{
|
|||||||
};
|
};
|
||||||
use common::{
|
use common::{
|
||||||
comp::{Pos, Presence},
|
comp::{Pos, Presence},
|
||||||
event::{EventBus, ServerEvent},
|
event::{ClientDisconnectEvent, EventBus},
|
||||||
spiral::Spiral2d,
|
spiral::Spiral2d,
|
||||||
terrain::{CoordinateConversions, TerrainChunkSize, TerrainGrid},
|
terrain::{CoordinateConversions, TerrainChunkSize, TerrainGrid},
|
||||||
vol::RectVolSize,
|
vol::RectVolSize,
|
||||||
@ -21,7 +21,7 @@ pub struct Sys;
|
|||||||
impl<'a> System<'a> for Sys {
|
impl<'a> System<'a> for Sys {
|
||||||
type SystemData = (
|
type SystemData = (
|
||||||
Entities<'a>,
|
Entities<'a>,
|
||||||
Read<'a, EventBus<ServerEvent>>,
|
Read<'a, EventBus<ClientDisconnectEvent>>,
|
||||||
ReadExpect<'a, EventBus<ChunkSendEntry>>,
|
ReadExpect<'a, EventBus<ChunkSendEntry>>,
|
||||||
ReadExpect<'a, TerrainGrid>,
|
ReadExpect<'a, TerrainGrid>,
|
||||||
ReadExpect<'a, Lod>,
|
ReadExpect<'a, Lod>,
|
||||||
@ -40,7 +40,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
job: &mut Job<Self>,
|
job: &mut Job<Self>,
|
||||||
(
|
(
|
||||||
entities,
|
entities,
|
||||||
server_event_bus,
|
client_disconnect_events,
|
||||||
chunk_send_bus,
|
chunk_send_bus,
|
||||||
terrain,
|
terrain,
|
||||||
lod,
|
lod,
|
||||||
@ -57,8 +57,8 @@ impl<'a> System<'a> for Sys {
|
|||||||
// NOTE: Required because Specs has very poor work splitting for sparse joins.
|
// NOTE: Required because Specs has very poor work splitting for sparse joins.
|
||||||
.par_bridge()
|
.par_bridge()
|
||||||
.map_init(
|
.map_init(
|
||||||
|| (chunk_send_bus.emitter(), server_event_bus.emitter()),
|
|| (chunk_send_bus.emitter(), client_disconnect_events.emitter()),
|
||||||
|(chunk_send_emitter, server_emitter), (entity, client, maybe_presence)| {
|
|(chunk_send_emitter, client_disconnect_emitter), (entity, client, maybe_presence)| {
|
||||||
let mut chunk_requests = Vec::new();
|
let mut chunk_requests = Vec::new();
|
||||||
let _ = super::try_recv_all(client, 5, |client, msg| {
|
let _ = super::try_recv_all(client, 5, |client, msg| {
|
||||||
// SPECIAL CASE: LOD zone requests can be sent by non-present players
|
// 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 \
|
"Kicking possibly misbehaving client due to invalud terrain \
|
||||||
request"
|
request"
|
||||||
);
|
);
|
||||||
server_emitter.emit(ServerEvent::ClientDisconnect(
|
client_disconnect_emitter.emit(ClientDisconnectEvent(
|
||||||
entity,
|
entity,
|
||||||
common::comp::DisconnectReason::NetworkError,
|
common::comp::DisconnectReason::NetworkError,
|
||||||
));
|
));
|
||||||
|
@ -2,7 +2,8 @@ use common::{
|
|||||||
comp::{object, Body, Object, PhysicsState, Pos, Teleporting, Vel},
|
comp::{object, Body, Object, PhysicsState, Pos, Teleporting, Vel},
|
||||||
consts::TELEPORTER_RADIUS,
|
consts::TELEPORTER_RADIUS,
|
||||||
effect::Effect,
|
effect::Effect,
|
||||||
event::{EventBus, ServerEvent},
|
event::{ChangeBodyEvent, DeleteEvent, EmitExt, EventBus, ExplosionEvent, ShootEvent},
|
||||||
|
event_emitters,
|
||||||
outcome::Outcome,
|
outcome::Outcome,
|
||||||
resources::{DeltaTime, Time},
|
resources::{DeltaTime, Time},
|
||||||
CachedSpatialGrid, Damage, DamageKind, DamageSource, Explosion, RadiusEffect,
|
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 specs::{Entities, Join, LendJoin, Read, ReadStorage};
|
||||||
use vek::Rgb;
|
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
|
/// This system is responsible for handling misc object behaviours
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct Sys;
|
pub struct Sys;
|
||||||
impl<'a> System<'a> for Sys {
|
impl<'a> System<'a> for Sys {
|
||||||
type SystemData = (
|
type SystemData = (
|
||||||
Entities<'a>,
|
Entities<'a>,
|
||||||
|
Events<'a>,
|
||||||
Read<'a, DeltaTime>,
|
Read<'a, DeltaTime>,
|
||||||
Read<'a, Time>,
|
Read<'a, Time>,
|
||||||
Read<'a, EventBus<ServerEvent>>,
|
|
||||||
Read<'a, EventBus<Outcome>>,
|
Read<'a, EventBus<Outcome>>,
|
||||||
Read<'a, CachedSpatialGrid>,
|
Read<'a, CachedSpatialGrid>,
|
||||||
ReadStorage<'a, Pos>,
|
ReadStorage<'a, Pos>,
|
||||||
@ -38,9 +48,9 @@ impl<'a> System<'a> for Sys {
|
|||||||
_job: &mut Job<Self>,
|
_job: &mut Job<Self>,
|
||||||
(
|
(
|
||||||
entities,
|
entities,
|
||||||
|
events,
|
||||||
_dt,
|
_dt,
|
||||||
time,
|
time,
|
||||||
server_bus,
|
|
||||||
outcome_bus,
|
outcome_bus,
|
||||||
spatial_grid,
|
spatial_grid,
|
||||||
positions,
|
positions,
|
||||||
@ -51,7 +61,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
teleporting,
|
teleporting,
|
||||||
): Self::SystemData,
|
): Self::SystemData,
|
||||||
) {
|
) {
|
||||||
let mut server_emitter = server_bus.emitter();
|
let mut emitters = events.get_emitters();
|
||||||
|
|
||||||
// Objects
|
// Objects
|
||||||
for (entity, pos, vel, physics, object, body) in (
|
for (entity, pos, vel, physics, object, body) in (
|
||||||
@ -67,8 +77,8 @@ impl<'a> System<'a> for Sys {
|
|||||||
match object {
|
match object {
|
||||||
Object::Bomb { owner } => {
|
Object::Bomb { owner } => {
|
||||||
if physics.on_surface().is_some() {
|
if physics.on_surface().is_some() {
|
||||||
server_emitter.emit(ServerEvent::Delete(entity));
|
emitters.emit(DeleteEvent(entity));
|
||||||
server_emitter.emit(ServerEvent::Explosion {
|
emitters.emit(ExplosionEvent {
|
||||||
pos: pos.0,
|
pos: pos.0,
|
||||||
explosion: Explosion {
|
explosion: Explosion {
|
||||||
effects: vec![
|
effects: vec![
|
||||||
@ -132,7 +142,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
phi.sin(),
|
phi.sin(),
|
||||||
))
|
))
|
||||||
.expect("nonzero vector should normalize");
|
.expect("nonzero vector should normalize");
|
||||||
server_emitter.emit(ServerEvent::Shoot {
|
emitters.emit(ShootEvent {
|
||||||
entity,
|
entity,
|
||||||
pos: *pos,
|
pos: *pos,
|
||||||
dir,
|
dir,
|
||||||
@ -160,8 +170,8 @@ impl<'a> System<'a> for Sys {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
server_emitter.emit(ServerEvent::Delete(entity));
|
emitters.emit(DeleteEvent(entity));
|
||||||
server_emitter.emit(ServerEvent::Explosion {
|
emitters.emit(ExplosionEvent {
|
||||||
pos: pos.0,
|
pos: pos.0,
|
||||||
explosion: Explosion {
|
explosion: Explosion {
|
||||||
effects: vec![
|
effects: vec![
|
||||||
@ -186,7 +196,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
timeout,
|
timeout,
|
||||||
} => {
|
} => {
|
||||||
if (time.0 - spawned_at.0).max(0.0) > timeout.as_secs_f64() {
|
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 { .. } => {
|
Object::Portal { .. } => {
|
||||||
@ -204,7 +214,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (*body == Body::Object(object::Body::PortalActive)) != is_active {
|
if (*body == Body::Object(object::Body::PortalActive)) != is_active {
|
||||||
server_bus.emit_now(ServerEvent::ChangeBody {
|
emitters.emit(ChangeBodyEvent {
|
||||||
entity,
|
entity,
|
||||||
new_body: Body::Object(if is_active {
|
new_body: Body::Object(if is_active {
|
||||||
outcome_bus.emit_now(Outcome::PortalActivated { pos: pos.0 });
|
outcome_bus.emit_now(Outcome::PortalActivated { pos: pos.0 });
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use common::{
|
use common::{
|
||||||
comp::{Agent, Alignment, CharacterState, Object, Pos, Teleporting},
|
comp::{Agent, Alignment, CharacterState, Object, Pos, Teleporting},
|
||||||
consts::TELEPORTER_RADIUS,
|
consts::TELEPORTER_RADIUS,
|
||||||
event::{EventBus, ServerEvent},
|
event::{EventBus, TeleportToPositionEvent},
|
||||||
outcome::Outcome,
|
outcome::Outcome,
|
||||||
resources::Time,
|
resources::Time,
|
||||||
uid::Uid,
|
uid::Uid,
|
||||||
@ -33,7 +33,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
ReadStorage<'a, CharacterState>,
|
ReadStorage<'a, CharacterState>,
|
||||||
Read<'a, CachedSpatialGrid>,
|
Read<'a, CachedSpatialGrid>,
|
||||||
Read<'a, Time>,
|
Read<'a, Time>,
|
||||||
Read<'a, EventBus<ServerEvent>>,
|
Read<'a, EventBus<TeleportToPositionEvent>>,
|
||||||
Read<'a, EventBus<Outcome>>,
|
Read<'a, EventBus<Outcome>>,
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -54,10 +54,12 @@ impl<'a> System<'a> for Sys {
|
|||||||
character_states,
|
character_states,
|
||||||
spatial_grid,
|
spatial_grid,
|
||||||
time,
|
time,
|
||||||
server_bus,
|
teleport_to_position_events,
|
||||||
outcome_bus,
|
outcome_bus,
|
||||||
): Self::SystemData,
|
): 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>| {
|
let check_aggro = |entity, pos: Vec3<f32>| {
|
||||||
spatial_grid
|
spatial_grid
|
||||||
.0
|
.0
|
||||||
@ -126,11 +128,11 @@ impl<'a> System<'a> for Sys {
|
|||||||
|
|
||||||
for entity in nearby {
|
for entity in nearby {
|
||||||
cancel_teleporting.push(entity);
|
cancel_teleporting.push(entity);
|
||||||
server_bus.emit_now(ServerEvent::TeleportToPosition {
|
teleport_to_position_emitter.emit(TeleportToPositionEvent {
|
||||||
entity,
|
entity,
|
||||||
position: *target,
|
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,
|
self, agent, biped_small, bird_medium, misc::PortalData, skillset::skills,
|
||||||
BehaviorCapability, ForceUpdate, Pos, Presence, Waypoint,
|
BehaviorCapability, ForceUpdate, Pos, Presence, Waypoint,
|
||||||
},
|
},
|
||||||
event::{EventBus, NpcBuilder, ServerEvent},
|
event::{
|
||||||
|
CreateNpcEvent, CreateTeleporterEvent, CreateWaypointEvent, EmitExt, EventBus, NpcBuilder,
|
||||||
|
},
|
||||||
|
event_emitters,
|
||||||
generation::{EntityInfo, SpecialEntity},
|
generation::{EntityInfo, SpecialEntity},
|
||||||
lottery::LootSpec,
|
lottery::LootSpec,
|
||||||
resources::{Time, TimeOfDay},
|
resources::{Time, TimeOfDay},
|
||||||
@ -51,6 +54,14 @@ type RtSimData<'a> = WriteExpect<'a, rtsim::RtSim>;
|
|||||||
#[cfg(not(feature = "worldgen"))]
|
#[cfg(not(feature = "worldgen"))]
|
||||||
type RtSimData<'a> = ();
|
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
|
/// This system will handle loading generated chunks and unloading
|
||||||
/// unneeded chunks.
|
/// unneeded chunks.
|
||||||
/// 1. Inserts newly generated chunks into the TerrainGrid
|
/// 1. Inserts newly generated chunks into the TerrainGrid
|
||||||
@ -62,7 +73,7 @@ pub struct Sys;
|
|||||||
impl<'a> System<'a> for Sys {
|
impl<'a> System<'a> for Sys {
|
||||||
#[allow(clippy::type_complexity)]
|
#[allow(clippy::type_complexity)]
|
||||||
type SystemData = (
|
type SystemData = (
|
||||||
Read<'a, EventBus<ServerEvent>>,
|
Events<'a>,
|
||||||
Read<'a, Tick>,
|
Read<'a, Tick>,
|
||||||
Read<'a, Settings>,
|
Read<'a, Settings>,
|
||||||
Read<'a, TimeOfDay>,
|
Read<'a, TimeOfDay>,
|
||||||
@ -94,7 +105,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
fn run(
|
fn run(
|
||||||
_job: &mut Job<Self>,
|
_job: &mut Job<Self>,
|
||||||
(
|
(
|
||||||
server_event_bus,
|
events,
|
||||||
tick,
|
tick,
|
||||||
server_settings,
|
server_settings,
|
||||||
time_of_day,
|
time_of_day,
|
||||||
@ -119,7 +130,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
time,
|
time,
|
||||||
): Self::SystemData,
|
): Self::SystemData,
|
||||||
) {
|
) {
|
||||||
let mut server_emitter = server_event_bus.emitter();
|
let mut emitters = events.get_emitters();
|
||||||
|
|
||||||
// Generate requested chunks
|
// Generate requested chunks
|
||||||
//
|
//
|
||||||
@ -196,7 +207,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
let data = NpcData::from_entity_info(entity);
|
let data = NpcData::from_entity_info(entity);
|
||||||
match data {
|
match data {
|
||||||
NpcData::Waypoint(pos) => {
|
NpcData::Waypoint(pos) => {
|
||||||
server_emitter.emit(ServerEvent::CreateWaypoint(pos));
|
emitters.emit(CreateWaypointEvent(pos));
|
||||||
},
|
},
|
||||||
NpcData::Data {
|
NpcData::Data {
|
||||||
pos,
|
pos,
|
||||||
@ -211,7 +222,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
scale,
|
scale,
|
||||||
loot,
|
loot,
|
||||||
} => {
|
} => {
|
||||||
server_emitter.emit(ServerEvent::CreateNpc {
|
emitters.emit(CreateNpcEvent {
|
||||||
pos,
|
pos,
|
||||||
ori: comp::Ori::from(Dir::random_2d(&mut rng)),
|
ori: comp::Ori::from(Dir::random_2d(&mut rng)),
|
||||||
npc: NpcBuilder::new(stats, body, alignment)
|
npc: NpcBuilder::new(stats, body, alignment)
|
||||||
@ -227,7 +238,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
NpcData::Teleporter(pos, teleporter) => {
|
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 crate::wiring::{Circuit, WiringElement};
|
||||||
use common::{
|
use common::{
|
||||||
comp::{LightEmitter, PhysicsState, Pos},
|
comp::{LightEmitter, PhysicsState, Pos},
|
||||||
event::{EventBus, ServerEvent},
|
event, event_emitters,
|
||||||
resources::EntitiesDiedLastTick,
|
resources::EntitiesDiedLastTick,
|
||||||
};
|
};
|
||||||
use common_ecs::{Job, Origin, Phase, System};
|
use common_ecs::{Job, Origin, Phase, System};
|
||||||
@ -20,13 +20,19 @@ pub struct ReadData<'a> {
|
|||||||
entities_died_last_tick: Read<'a, EntitiesDiedLastTick>,
|
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)
|
/// This system is responsible for handling wiring (signals and wiring systems)
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct Sys;
|
pub struct Sys;
|
||||||
impl<'a> System<'a> for Sys {
|
impl<'a> System<'a> for Sys {
|
||||||
type SystemData = (
|
type SystemData = (
|
||||||
ReadData<'a>,
|
ReadData<'a>,
|
||||||
Read<'a, EventBus<ServerEvent>>,
|
Events<'a>,
|
||||||
WriteStorage<'a, WiringElement>,
|
WriteStorage<'a, WiringElement>,
|
||||||
WriteStorage<'a, LightEmitter>, // maybe
|
WriteStorage<'a, LightEmitter>, // maybe
|
||||||
Write<'a, BlockChange>,
|
Write<'a, BlockChange>,
|
||||||
@ -38,9 +44,9 @@ impl<'a> System<'a> for Sys {
|
|||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
_job: &mut Job<Self>,
|
_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
|
// Compute the output for each wiring element by computing
|
||||||
// the output for each `OutputFormula` and store each value per output per
|
// 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,
|
&wiring_element.inputs,
|
||||||
physics_state,
|
physics_state,
|
||||||
&read_data.entities_died_last_tick.0,
|
&read_data.entities_died_last_tick.0,
|
||||||
&mut server_emitter,
|
&mut emitters,
|
||||||
pos,
|
pos,
|
||||||
&mut block_change,
|
&mut block_change,
|
||||||
light_emitter.as_deref_mut(),
|
light_emitter.as_deref_mut(),
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use common::{
|
use common::{
|
||||||
comp::{item::tool, object, Body, LightEmitter, PhysicsState, Pos, ProjectileConstructor},
|
comp::{item::tool, object, Body, LightEmitter, PhysicsState, Pos, ProjectileConstructor},
|
||||||
event::{Emitter, ServerEvent},
|
event::{EmitExt, ShootEvent},
|
||||||
terrain::{Block, TerrainChunkSize},
|
terrain::{Block, TerrainChunkSize},
|
||||||
util::Dir,
|
util::Dir,
|
||||||
vol::RectVolSize,
|
vol::RectVolSize,
|
||||||
@ -162,7 +162,7 @@ impl WiringAction {
|
|||||||
inputs: &HashMap<String, f32>,
|
inputs: &HashMap<String, f32>,
|
||||||
physics_state: Option<&PhysicsState>,
|
physics_state: Option<&PhysicsState>,
|
||||||
entities_died_last_tick: &Vec<(Entity, Pos)>,
|
entities_died_last_tick: &Vec<(Entity, Pos)>,
|
||||||
server_emitter: &mut Emitter<'_, ServerEvent>,
|
emitters: &mut impl EmitExt<ShootEvent>,
|
||||||
pos: Option<&Pos>,
|
pos: Option<&Pos>,
|
||||||
block_change: &mut BlockChange,
|
block_change: &mut BlockChange,
|
||||||
mut light_emitter: Option<&mut LightEmitter>,
|
mut light_emitter: Option<&mut LightEmitter>,
|
||||||
@ -183,7 +183,7 @@ impl WiringAction {
|
|||||||
},
|
},
|
||||||
WiringActionEffect::SpawnProjectile { constr } => {
|
WiringActionEffect::SpawnProjectile { constr } => {
|
||||||
if let Some(&pos) = pos {
|
if let Some(&pos) = pos {
|
||||||
server_emitter.emit(ServerEvent::Shoot {
|
emitters.emit(ShootEvent {
|
||||||
entity,
|
entity,
|
||||||
pos,
|
pos,
|
||||||
dir: Dir::forward(),
|
dir: Dir::forward(),
|
||||||
|
Loading…
Reference in New Issue
Block a user