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