1use crate::{
2 Explosion,
3 character::CharacterId,
4 combat::{AttackSource, AttackTarget, CombatEffect, DeathEffects, RiderEffects},
5 comp::{
6 self, ArcProperties, DisconnectReason, LootOwner, Ori, Pos, UnresolvedChatMsg, Vel,
7 ability::Dodgeable,
8 agent::Sound,
9 beam,
10 invite::{InviteKind, InviteResponse},
11 slot::EquipSlot,
12 },
13 generation::{EntityInfo, SpecialEntity},
14 interaction::Interaction,
15 lottery::LootSpec,
16 mounting::VolumePos,
17 outcome::Outcome,
18 resources::{BattleMode, Secs},
19 rtsim::{self, RtSimEntity},
20 states::basic_summon::BeamPillarIndicatorSpecifier,
21 terrain::SpriteKind,
22 trade::{TradeAction, TradeId},
23 uid::Uid,
24 util::Dir,
25};
26use serde::{Deserialize, Serialize};
27use specs::Entity as EcsEntity;
28use std::{collections::VecDeque, sync::Mutex, time::Duration};
29use uuid::Uuid;
30use vek::*;
31
32pub type SiteId = u64;
33pub type PluginHash = [u8; 32];
35
36pub enum LocalEvent {
37 Jump(EcsEntity, f32),
39 ApplyImpulse {
41 entity: EcsEntity,
42 impulse: Vec3<f32>,
43 },
44 Boost { entity: EcsEntity, vel: Vec3<f32> },
46 CreateOutcome(Outcome),
48}
49
50#[derive(Clone, Debug, Default, Deserialize, Serialize)]
51pub struct UpdateCharacterMetadata {
52 pub skill_set_persistence_load_error: Option<comp::skillset::SkillsPersistenceError>,
53}
54
55pub struct NpcBuilder {
56 pub stats: comp::Stats,
57 pub skill_set: comp::SkillSet,
58 pub health: Option<comp::Health>,
59 pub poise: comp::Poise,
60 pub inventory: comp::inventory::Inventory,
61 pub body: comp::Body,
62 pub agent: Option<comp::Agent>,
63 pub alignment: comp::Alignment,
64 pub scale: comp::Scale,
65 pub anchor: Option<comp::Anchor>,
66 pub loot: LootSpec<String>,
67 pub pets: Vec<(NpcBuilder, Vec3<f32>)>,
68 pub rtsim_entity: Option<RtSimEntity>,
69 pub projectile: Option<comp::Projectile>,
70 pub heads: Option<comp::body::parts::Heads>,
71 pub death_effects: Option<DeathEffects>,
72 pub rider_effects: Option<RiderEffects>,
73 pub rider: Option<Box<Self>>,
74}
75
76impl NpcBuilder {
77 pub fn new(stats: comp::Stats, body: comp::Body, alignment: comp::Alignment) -> Self {
78 Self {
79 stats,
80 skill_set: comp::SkillSet::default(),
81 health: None,
82 poise: comp::Poise::new(body),
83 inventory: comp::Inventory::with_empty(),
84 body,
85 agent: None,
86 alignment,
87 scale: comp::Scale(1.0),
88 anchor: None,
89 loot: LootSpec::Nothing,
90 rtsim_entity: None,
91 projectile: None,
92 pets: Vec::new(),
93 heads: None,
94 death_effects: None,
95 rider_effects: None,
96 rider: None,
97 }
98 }
99
100 pub fn with_rider(mut self, rider: impl Into<Option<NpcBuilder>>) -> Self {
101 let rider: Option<NpcBuilder> = rider.into();
102 self.rider = rider.map(Box::new);
103 self
104 }
105
106 pub fn with_heads(mut self, heads: impl Into<Option<comp::body::parts::Heads>>) -> Self {
107 self.heads = heads.into();
108 self
109 }
110
111 pub fn with_health(mut self, health: impl Into<Option<comp::Health>>) -> Self {
112 self.health = health.into();
113 self
114 }
115
116 pub fn with_poise(mut self, poise: comp::Poise) -> Self {
117 self.poise = poise;
118 self
119 }
120
121 pub fn with_agent(mut self, agent: impl Into<Option<comp::Agent>>) -> Self {
122 self.agent = agent.into();
123 self
124 }
125
126 pub fn with_anchor(mut self, anchor: comp::Anchor) -> Self {
127 self.anchor = Some(anchor);
128 self
129 }
130
131 pub fn with_rtsim(mut self, rtsim: RtSimEntity) -> Self {
132 self.rtsim_entity = Some(rtsim);
133 self
134 }
135
136 pub fn with_projectile(mut self, projectile: impl Into<Option<comp::Projectile>>) -> Self {
137 self.projectile = projectile.into();
138 self
139 }
140
141 pub fn with_scale(mut self, scale: comp::Scale) -> Self {
142 self.scale = scale;
143 self
144 }
145
146 pub fn with_inventory(mut self, inventory: comp::Inventory) -> Self {
147 self.inventory = inventory;
148 self
149 }
150
151 pub fn with_skill_set(mut self, skill_set: comp::SkillSet) -> Self {
152 self.skill_set = skill_set;
153 self
154 }
155
156 pub fn with_loot(mut self, loot: LootSpec<String>) -> Self {
157 self.loot = loot;
158 self
159 }
160
161 pub fn with_pets(mut self, pets: Vec<(NpcBuilder, Vec3<f32>)>) -> Self {
162 self.pets = pets;
163 self
164 }
165
166 pub fn with_death_effects(mut self, death_effects: Option<DeathEffects>) -> Self {
167 self.death_effects = death_effects;
168 self
169 }
170
171 pub fn with_rider_effects(mut self, rider_effects: Option<RiderEffects>) -> Self {
172 self.rider_effects = rider_effects;
173 self
174 }
175}
176
177pub struct ClientDisconnectEvent(pub EcsEntity, pub DisconnectReason);
185
186pub struct ClientDisconnectWithoutPersistenceEvent(pub EcsEntity);
187
188pub struct CommandEvent(pub EcsEntity, pub String, pub Vec<String>);
189
190pub struct CreateSpecialEntityEvent {
191 pub pos: Vec3<f32>,
192 pub entity: SpecialEntity,
193}
194
195pub struct CreateShipEvent {
196 pub pos: Pos,
197 pub ori: Ori,
198 pub ship: comp::ship::Body,
199 pub rtsim_entity: Option<RtSimEntity>,
200 pub driver: Option<NpcBuilder>,
201}
202
203pub struct CreateItemDropEvent {
204 pub pos: Pos,
205 pub vel: Vel,
206 pub ori: Ori,
207 pub item: comp::PickupItem,
208 pub loot_owner: Option<LootOwner>,
209}
210
211pub struct CreateObjectEvent {
212 pub pos: Pos,
213 pub vel: Vel,
214 pub body: comp::object::Body,
215 pub object: Option<comp::Object>,
216 pub item: Option<comp::PickupItem>,
217 pub light_emitter: Option<comp::LightEmitter>,
218 pub stats: Option<comp::Stats>,
219}
220
221pub struct InitializeCharacterEvent {
223 pub entity: EcsEntity,
224 pub character_id: CharacterId,
225 pub requested_view_distances: crate::ViewDistances,
226}
227
228pub struct InitializeSpectatorEvent(pub EcsEntity, pub crate::ViewDistances);
229
230pub struct UpdateCharacterDataEvent {
231 pub entity: EcsEntity,
232 pub components: (
233 comp::Body,
234 Option<comp::Hardcore>,
235 comp::Stats,
236 comp::SkillSet,
237 comp::Inventory,
238 Option<comp::Waypoint>,
239 Vec<(comp::Pet, comp::Body, comp::Stats)>,
240 comp::ActiveAbilities,
241 Option<comp::MapMarker>,
242 ),
243 pub metadata: UpdateCharacterMetadata,
244}
245
246pub struct ExitIngameEvent {
247 pub entity: EcsEntity,
248}
249
250pub struct RequestSiteInfoEvent {
251 pub entity: EcsEntity,
252 pub id: SiteId,
253}
254
255pub struct TamePetEvent {
256 pub pet_entity: EcsEntity,
257 pub owner_entity: EcsEntity,
258}
259
260pub struct UpdateMapMarkerEvent {
261 pub entity: EcsEntity,
262 pub update: comp::MapMarkerChange,
263}
264
265pub struct MakeAdminEvent {
266 pub entity: EcsEntity,
267 pub admin: comp::Admin,
268 pub uuid: Uuid,
269}
270
271pub struct DeleteCharacterEvent {
272 pub entity: EcsEntity,
273 pub requesting_player_uuid: String,
274 pub character_id: CharacterId,
275}
276
277pub struct TeleportToPositionEvent {
278 pub entity: EcsEntity,
279 pub position: Vec3<f32>,
280}
281
282#[cfg(feature = "plugins")]
283pub struct RequestPluginsEvent {
284 pub entity: EcsEntity,
285 pub plugins: Vec<PluginHash>,
286}
287
288pub struct SetBattleModeEvent {
289 pub entity: EcsEntity,
290 pub battle_mode: BattleMode,
291}
292
293pub struct ChatEvent {
298 pub msg: UnresolvedChatMsg,
299 pub from_client: bool,
304}
305
306pub struct CreateNpcEvent {
307 pub pos: Pos,
308 pub ori: Ori,
309 pub npc: NpcBuilder,
310}
311
312pub struct CreateNpcGroupEvent {
313 pub npcs: Vec<CreateNpcEvent>,
314}
315
316pub struct CreateAuraEntityEvent {
317 pub auras: comp::Auras,
318 pub pos: Pos,
319 pub creator_uid: Uid,
320 pub duration: Option<Secs>,
321}
322
323pub struct ExplosionEvent {
324 pub pos: Vec3<f32>,
325 pub explosion: Explosion,
326 pub owner: Option<Uid>,
327}
328
329pub struct ArcingEvent {
330 pub arc: ArcProperties,
331 pub owner: Option<Uid>,
332 pub target: Uid,
333 pub pos: Pos,
334}
335
336pub struct CreatePoolEvent {
337 pub properties: comp::pool::PoolProperties,
338 pub owner: Option<Uid>,
339 pub pos: Pos,
340 pub ori: Ori,
341}
342
343pub struct BonkEvent {
344 pub pos: Vec3<f32>,
345 pub owner: Option<Uid>,
346 pub target: Option<Uid>,
347}
348
349pub struct HealthChangeEvent {
350 pub entity: EcsEntity,
351 pub change: comp::HealthChange,
352}
353
354pub struct KillEvent {
355 pub entity: EcsEntity,
356}
357
358pub struct HelpDownedEvent {
359 pub helper: Option<Uid>,
360 pub target: Uid,
361}
362
363pub struct DownedEvent {
364 pub entity: EcsEntity,
365}
366
367pub struct PoiseChangeEvent {
368 pub entity: EcsEntity,
369 pub change: comp::PoiseChange,
370}
371
372pub struct DeleteEvent(pub EcsEntity);
373
374pub struct DestroyEvent {
375 pub entity: EcsEntity,
376 pub cause: comp::HealthChange,
377}
378
379pub struct InventoryManipEvent(pub EcsEntity, pub comp::InventoryManip);
380
381pub struct GroupManipEvent(pub EcsEntity, pub comp::GroupManip);
382
383pub struct RespawnEvent(pub EcsEntity);
384
385pub struct ShootEvent {
386 pub entity: Option<EcsEntity>,
388 pub source_vel: Option<Vel>,
389 pub pos: Pos,
390 pub dir: Dir,
391 pub body: comp::Body,
392 pub light: Option<comp::LightEmitter>,
393 pub projectile: comp::Projectile,
394 pub speed: f32,
395 pub object: Option<comp::Object>,
396 pub marker: Option<comp::FrontendMarker>,
397}
398
399pub struct ThrowEvent {
400 pub entity: EcsEntity,
401 pub pos: Pos,
402 pub dir: Dir,
403 pub light: Option<comp::LightEmitter>,
404 pub projectile: comp::Projectile,
405 pub speed: f32,
406 pub object: Option<comp::Object>,
407 pub equip_slot: EquipSlot,
408}
409
410pub struct ShockwaveEvent {
411 pub properties: comp::shockwave::Properties,
412 pub pos: Pos,
413 pub ori: Ori,
414}
415
416pub struct KnockbackEvent {
417 pub entity: EcsEntity,
418 pub impulse: Vec3<f32>,
419}
420
421pub struct LandOnGroundEvent {
422 pub entity: EcsEntity,
423 pub vel: Vec3<f32>,
424 pub surface_normal: Vec3<f32>,
425}
426
427pub struct SetLanternEvent(pub EcsEntity, pub bool);
428
429pub struct NpcInteractEvent(pub EcsEntity, pub EcsEntity);
430
431pub struct DialogueEvent(pub EcsEntity, pub EcsEntity, pub rtsim::Dialogue);
432
433pub struct InviteResponseEvent(pub EcsEntity, pub InviteResponse);
434
435pub struct InitiateInviteEvent(pub EcsEntity, pub Uid, pub InviteKind);
436
437pub struct ProcessTradeActionEvent(pub EcsEntity, pub TradeId, pub TradeAction);
438
439pub enum MountEvent {
440 MountEntity(EcsEntity, EcsEntity),
441 MountVolume(EcsEntity, VolumePos),
442 Unmount(EcsEntity),
443}
444
445pub struct SetPetStayEvent(pub EcsEntity, pub EcsEntity, pub bool);
446
447pub struct PossessEvent(pub Uid, pub Uid);
448
449pub struct TransformEvent {
450 pub target_entity: Uid,
451 pub entity_info: EntityInfo,
452 pub allow_players: bool,
455 pub delete_on_failure: bool,
458}
459
460pub struct StartInteractionEvent(pub Interaction);
461
462pub struct AuraEvent {
463 pub entity: EcsEntity,
464 pub aura_change: comp::AuraChange,
465}
466
467pub struct BuffEvent {
468 pub entity: EcsEntity,
469 pub buff_change: comp::BuffChange,
470}
471
472pub struct EnergyChangeEvent {
473 pub entity: EcsEntity,
474 pub change: f32,
475 pub reset_rate: bool,
476}
477
478pub struct ComboChangeEvent {
479 pub entity: EcsEntity,
480 pub change: i32,
481}
482
483pub struct ParryHookEvent {
484 pub defender: EcsEntity,
485 pub attacker: Option<EcsEntity>,
486 pub source: AttackSource,
487 pub poise_multiplier: f32,
488}
489
490pub struct MineBlockEvent {
492 pub entity: EcsEntity,
493 pub pos: Vec3<i32>,
494 pub tool: Option<comp::tool::ToolKind>,
495}
496
497pub struct TeleportToEvent {
498 pub entity: EcsEntity,
499 pub target: Uid,
500 pub max_range: Option<f32>,
501}
502
503pub struct SoundEvent {
504 pub sound: Sound,
505}
506
507pub struct CreateSpriteEvent {
508 pub pos: Vec3<i32>,
509 pub sprite: SpriteKind,
510 pub del_timeout: Option<(f32, f32)>,
511}
512
513pub struct EntityAttackedHookEvent {
514 pub entity: EcsEntity,
515 pub attacker: Option<EcsEntity>,
516 pub attack_dir: Dir,
517 pub damage_dealt: f32,
518 pub attack_source: AttackSource,
519}
520
521pub struct ChangeAbilityEvent {
522 pub entity: EcsEntity,
523 pub slot: usize,
524 pub auxiliary_key: comp::ability::AuxiliaryKey,
525 pub new_ability: comp::ability::AuxiliaryAbility,
526}
527
528pub struct ChangeStanceEvent {
529 pub entity: EcsEntity,
530 pub stance: comp::Stance,
531}
532
533pub struct PermanentChange {
534 pub expected_old_body: comp::Body,
535}
536
537pub struct ChangeBodyEvent {
538 pub entity: EcsEntity,
539 pub new_body: comp::Body,
540 pub permanent_change: Option<PermanentChange>,
544}
545
546pub struct RemoveLightEmitterEvent {
547 pub entity: EcsEntity,
548}
549
550pub struct StartTeleportingEvent {
551 pub entity: EcsEntity,
552 pub portal: EcsEntity,
553}
554
555pub struct ToggleSpriteLightEvent {
556 pub entity: EcsEntity,
557 pub pos: Vec3<i32>,
558 pub enable: bool,
559}
560
561pub struct RegrowHeadEvent {
562 pub entity: EcsEntity,
563}
564
565pub struct SummonBeamPillarsEvent {
566 pub summoner: EcsEntity,
567 pub target: AttackTarget,
568 pub buildup_duration: Duration,
569 pub attack_duration: Duration,
570 pub beam_duration: Duration,
571 pub radius: f32,
572 pub height: f32,
573 pub damage: f32,
574 pub damage_effect: Option<CombatEffect>,
575 pub dodgeable: Dodgeable,
576 pub tick_rate: f32,
577 pub specifier: beam::FrontendSpecifier,
578 pub indicator_specifier: BeamPillarIndicatorSpecifier,
579}
580
581struct EventBusInner<E> {
582 queue: VecDeque<E>,
583 #[cfg(debug_assertions)]
588 recv_count: u8,
589}
590
591pub struct EventBus<E> {
592 inner: Mutex<EventBusInner<E>>,
593}
594
595impl<E> Default for EventBus<E> {
596 fn default() -> Self {
597 Self {
598 inner: Mutex::new(EventBusInner {
599 queue: VecDeque::new(),
600 #[cfg(debug_assertions)]
601 recv_count: 0,
602 }),
603 }
604 }
605}
606
607impl<E> EventBus<E> {
608 pub fn emitter(&self) -> Emitter<'_, E> {
609 Emitter {
610 bus: self,
611 events: VecDeque::new(),
612 }
613 }
614
615 pub fn emit_now(&self, event: E) {
616 self.inner.lock().expect("Poisoned").queue.push_back(event);
617 }
618
619 pub fn recv_all(&self) -> impl ExactSizeIterator<Item = E> + use<E> {
620 {
621 let mut guard = self.inner.lock().expect("Poisoned");
622 #[cfg(debug_assertions)]
623 {
624 guard.recv_count = guard.recv_count.saturating_add(1);
625 }
626 core::mem::take(&mut guard.queue)
627 }
628 .into_iter()
629 }
630
631 pub fn recv_all_mut(&mut self) -> impl ExactSizeIterator<Item = E> + use<E> {
632 let inner = self.inner.get_mut().expect("Poisoned");
633 #[cfg(debug_assertions)]
634 {
635 inner.recv_count = inner.recv_count.saturating_add(1);
636 }
637 core::mem::take(&mut inner.queue).into_iter()
638 }
639
640 #[cfg(debug_assertions)]
641 pub fn recv_count(&mut self) -> u8 { self.inner.get_mut().expect("Poisoned").recv_count }
642}
643
644pub struct Emitter<'a, E> {
645 bus: &'a EventBus<E>,
646 pub events: VecDeque<E>,
647}
648
649impl<E> Emitter<'_, E> {
650 pub fn emit(&mut self, event: E) { self.events.push_back(event); }
651
652 pub fn emit_many(&mut self, events: impl IntoIterator<Item = E>) { self.events.extend(events); }
653
654 pub fn append(&mut self, other: &mut VecDeque<E>) { self.events.append(other) }
655
656 pub fn append_vec(&mut self, vec: Vec<E>) {
657 if self.events.is_empty() {
658 self.events = vec.into();
659 } else {
660 self.events.extend(vec);
661 }
662 }
663}
664
665impl<E> Drop for Emitter<'_, E> {
666 fn drop(&mut self) {
667 if !self.events.is_empty() {
668 let mut guard = self.bus.inner.lock().expect("Poision");
669 guard.queue.append(&mut self.events);
670 }
671 }
672}
673
674pub trait EmitExt<E> {
675 fn emit(&mut self, event: E);
676 fn emit_many(&mut self, events: impl IntoIterator<Item = E>);
677}
678
679#[macro_export]
697macro_rules! event_emitters {
698 ($($vis:vis struct $read_data:ident[$emitters:ident] { $($(#[$($tt:tt)*])? $ev_ident:ident: $ty:ty),+ $(,)? })+) => {
699 mod event_emitters {
700 use super::*;
701 use specs::shred;
702 $(
703 #[derive(specs::SystemData)]
704 pub struct $read_data<'a> {
705 $($(#[$($tt)*])? $ev_ident: Option<specs::Read<'a, $crate::event::EventBus<$ty>>>),+
706 }
707
708 impl<'a> $read_data<'a> {
709 pub fn get_emitters(&self) -> $emitters<'_> {
710 $emitters {
711 $($(#[$($tt)*])? $ev_ident: self.$ev_ident.as_ref().map(|e| e.emitter())),+
712 }
713 }
714 }
715
716 pub struct $emitters<'a> {
717 $($(#[$($tt)*])? $ev_ident: Option<$crate::event::Emitter<'a, $ty>>),+
718 }
719
720 impl<'a> $emitters<'a> {
721 #[expect(unused)]
722 pub fn append(&mut self, mut other: Self) {
723 $(
724 $(#[$($tt)*])?
725 {self.$ev_ident.as_mut().zip(other.$ev_ident).map(|(a, mut b)| a.append(&mut b.events));}
726 )+
727 }
728 }
729
730 $(
731 $(#[$($tt)*])?
732 impl<'a> $crate::event::EmitExt<$ty> for $emitters<'a> {
733 fn emit(&mut self, event: $ty) { self.$ev_ident.as_mut().map(|e| e.emit(event)); }
734 fn emit_many(&mut self, events: impl IntoIterator<Item = $ty>) { self.$ev_ident.as_mut().map(|e| e.emit_many(events)); }
735 }
736 )+
737 )+
738 }
739 $(
740 $vis use event_emitters::{$read_data, $emitters};
741 )+
742 }
743}