veloren_common/
event.rs

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;
33/// Plugin identifier (sha256)
34pub type PluginHash = [u8; 32];
35
36pub enum LocalEvent {
37    /// Applies upward force to entity's `Vel`
38    Jump(EcsEntity, f32),
39    /// Applies the `impulse` to `entity`'s `Vel`
40    ApplyImpulse {
41        entity: EcsEntity,
42        impulse: Vec3<f32>,
43    },
44    /// Applies `vel` velocity to `entity`
45    Boost { entity: EcsEntity, vel: Vec3<f32> },
46    /// Creates an outcome
47    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
177// These events are generated only by server systems
178//
179// TODO: we may want to move these into the server crate, this may allow moving
180// other types out of `common` and would also narrow down where we know specific
181// events will be emitted (if done it should probably be setup so they can
182// easily be moved back here if needed).
183
184pub 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
221/// Inserts default components for a character when loading into the game.
222pub 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
293// These events are generated in common systems in addition to server systems
294// (but note on the client the event buses aren't registered and these events
295// aren't actually emitted).
296
297pub struct ChatEvent {
298    pub msg: UnresolvedChatMsg,
299    // We warn when the server tries to generate non plain `Content` messags
300    // that appear from a player since we currently filter those out.
301    //
302    // But we don't want to spam warnings if this is from a client, so track that here.
303    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    // This should be the owner entity
387    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    /// If set to false, players wont be transformed unless with a Possessor
453    /// presence kind
454    pub allow_players: bool,
455    /// Whether the entity should be deleted if transforming fails (only applies
456    /// to non-players)
457    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
490/// Attempt to mine a block, turning it into an item.
491pub 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    /// Is Some if this change should be persisted.
541    ///
542    /// Only applies to player characters.
543    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    /// Saturates to u8::MAX and is never reset.
584    ///
585    /// Used in the first tick to check for if certain event types are handled
586    /// and only handled once.
587    #[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/// Define ecs read data for event busses. And a way to convert them all to
680/// emitters.
681///
682/// # Example:
683/// ```
684/// mod some_mod_is_necessary_for_the_test {
685///     use veloren_common::event_emitters;
686///     pub struct Foo;
687///     pub struct Bar;
688///     pub struct Baz;
689///     event_emitters!(
690///       pub struct ReadEvents[EventEmitters] {
691///           foo: Foo, bar: Bar, baz: Baz,
692///       }
693///     );
694/// }
695/// ```
696#[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}