From 94d7871f754ccda1e2d763534755289c04bec2c1 Mon Sep 17 00:00:00 2001 From: Denis Tokarev Date: Wed, 24 Nov 2021 01:45:14 +0500 Subject: [PATCH 1/4] add speed effect --- Cargo.lock | 9 + Cargo.toml | 1 + feather/server/Cargo.toml | 1 + feather/server/src/client.rs | 27 +- feather/server/src/packet_handlers.rs | 8 +- .../server/src/packet_handlers/use_item.rs | 36 ++ feather/server/src/systems.rs | 2 + feather/server/src/systems/effects.rs | 133 ++++++++ libcraft/effects/Cargo.toml | 10 + libcraft/effects/src/effects.rs | 321 ++++++++++++++++++ libcraft/effects/src/lib.rs | 2 + libcraft/generators/python/effects.py | 25 ++ quill/api/src/lib.rs | 4 +- quill/common/Cargo.toml | 1 + quill/common/src/component.rs | 37 +- quill/common/src/components_effects.rs | 125 +++++++ quill/common/src/lib.rs | 1 + 17 files changed, 738 insertions(+), 5 deletions(-) create mode 100644 feather/server/src/packet_handlers/use_item.rs create mode 100644 feather/server/src/systems/effects.rs create mode 100644 libcraft/effects/Cargo.toml create mode 100644 libcraft/effects/src/effects.rs create mode 100644 libcraft/effects/src/lib.rs create mode 100644 libcraft/generators/python/effects.py create mode 100644 quill/common/src/components_effects.rs diff --git a/Cargo.lock b/Cargo.lock index 7f6eaa755..66bff8440 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -946,6 +946,7 @@ dependencies = [ "futures-lite", "hematite-nbt", "libcraft-core", + "libcraft-effects", "libcraft-items", "log", "md-5", @@ -1377,6 +1378,13 @@ dependencies = [ "vek", ] +[[package]] +name = "libcraft-effects" +version = "0.1.0" +dependencies = [ + "serde", +] + [[package]] name = "libcraft-generators" version = "0.1.0" @@ -2052,6 +2060,7 @@ dependencies = [ "bytemuck", "derive_more", "libcraft-core", + "libcraft-effects", "libcraft-particles", "libcraft-text", "quill", diff --git a/Cargo.toml b/Cargo.toml index c4b46578e..07ae527b3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,6 +9,7 @@ members = [ "libcraft/particles", "libcraft/text", "libcraft/inventory", + "libcraft/effects", # Quill "quill/sys-macros", diff --git a/feather/server/Cargo.toml b/feather/server/Cargo.toml index 4e9758fa4..1cca33856 100644 --- a/feather/server/Cargo.toml +++ b/feather/server/Cargo.toml @@ -51,6 +51,7 @@ uuid = "0.8" slab = "0.4" libcraft-core = { path = "../../libcraft/core" } libcraft-items = { path = "../../libcraft/items" } +libcraft-effects = { path = "../../libcraft/effects" } worldgen = { path = "../worldgen", package = "feather-worldgen" } [features] diff --git a/feather/server/src/client.rs b/feather/server/src/client.rs index 20211f39b..082cb064a 100644 --- a/feather/server/src/client.rs +++ b/feather/server/src/client.rs @@ -20,7 +20,8 @@ use common::{ use libcraft_items::InventorySlot; use packets::server::{Particle, SetSlot, SpawnLivingEntity, UpdateLight, WindowConfirmation}; use protocol::packets::server::{ - EntityPosition, EntityPositionAndRotation, EntityTeleport, HeldItemChange, PlayerAbilities, + EntityEffect, EntityPosition, EntityPositionAndRotation, EntityTeleport, HeldItemChange, + PlayerAbilities, RemoveEntityEffect, }; use protocol::{ packets::{ @@ -602,6 +603,30 @@ impl Client { self.send_packet(HeldItemChange { slot }); } + pub fn send_entity_effect( + &self, + network_id: NetworkId, + effect_id: u8, + amplifier: i8, + duration: i32, + flags: u8, + ) { + self.send_packet(EntityEffect { + entity_id: network_id.0, + effect_id, + amplifier, + duration, + flags, + }) + } + + pub fn send_remove_entity_effect(&self, network_id: NetworkId, effect_id: u8) { + self.send_packet(RemoveEntityEffect { + entity_id: network_id.0, + effect_id, + }) + } + fn register_entity(&self, network_id: NetworkId) { self.sent_entities.borrow_mut().insert(network_id); } diff --git a/feather/server/src/packet_handlers.rs b/feather/server/src/packet_handlers.rs index b0452c96a..9f42640da 100644 --- a/feather/server/src/packet_handlers.rs +++ b/feather/server/src/packet_handlers.rs @@ -20,6 +20,7 @@ mod entity_action; mod interaction; pub mod inventory; mod movement; +mod use_item; /// Handles a packet received from a client. pub fn handle_packet( @@ -77,6 +78,10 @@ pub fn handle_packet( entity_action::handle_entity_action(game, player_id, packet) } + ClientPlayPacket::UseItem(packet) => { + use_item::handle_use_item(game, server, packet, player_id) + } + ClientPlayPacket::TeleportConfirm(_) | ClientPlayPacket::QueryBlockNbt(_) | ClientPlayPacket::SetDifficulty(_) @@ -108,8 +113,7 @@ pub fn handle_packet( | ClientPlayPacket::UpdateJigsawBlock(_) | ClientPlayPacket::UpdateStructureBlock(_) | ClientPlayPacket::UpdateSign(_) - | ClientPlayPacket::Spectate(_) - | ClientPlayPacket::UseItem(_) => Ok(()), + | ClientPlayPacket::Spectate(_) => Ok(()), } } diff --git a/feather/server/src/packet_handlers/use_item.rs b/feather/server/src/packet_handlers/use_item.rs new file mode 100644 index 000000000..31fc40f4c --- /dev/null +++ b/feather/server/src/packet_handlers/use_item.rs @@ -0,0 +1,36 @@ +use crate::Server; +use base::TPS; +use common::Game; +use ecs::{Entity, SysResult}; +use protocol::packets::client::UseItem; +use quill_common::components_effects::*; + +pub fn handle_use_item( + game: &mut Game, + _server: &mut Server, + _packet: UseItem, + player: Entity, +) -> SysResult { + // example for effect system + let mut speed = SpeedEffect::default(); + speed.add_effect(PotionApplication { + amplifier: 0, + duration: 20 * TPS, + particle: false, + ambient: false, + icon: false, + start_tick: 0, + }); + + speed.add_effect(PotionApplication { + amplifier: 1, + duration: 10 * TPS, + particle: false, + ambient: false, + icon: false, + start_tick: 0, + }); + + game.ecs.insert(player, speed)?; + Ok(()) +} diff --git a/feather/server/src/systems.rs b/feather/server/src/systems.rs index ee7e85b37..f24f6fc71 100644 --- a/feather/server/src/systems.rs +++ b/feather/server/src/systems.rs @@ -2,6 +2,7 @@ mod block; mod chat; +mod effects; mod entity; mod particle; mod player_join; @@ -36,6 +37,7 @@ pub fn register(server: Server, game: &mut Game, systems: &mut SystemExecutor().add_system(tick_clients); } diff --git a/feather/server/src/systems/effects.rs b/feather/server/src/systems/effects.rs new file mode 100644 index 000000000..8495f0f1e --- /dev/null +++ b/feather/server/src/systems/effects.rs @@ -0,0 +1,133 @@ +use common::Game; +use ecs::{SysResult, SystemExecutor}; +use libcraft_effects::effects::Effect; +use quill_common::components_effects::{SpeedEffect, SpeedEffectModifier}; +use std::collections::HashMap; + +use crate::{ClientId, NetworkId, Server}; + +pub fn register(_game: &mut Game, systems: &mut SystemExecutor) { + systems + .group::() + .add_system(add_start_tick_to_speed_effects) + .add_system(speed_effect); +} + +fn speed_effect(game: &mut Game, server: &mut Server) -> SysResult { + let mut new_walk_speed = HashMap::new(); + for (entity, (&client_id, speed, &network_id)) in game + .ecs + .query::<(&ClientId, &mut SpeedEffect, &NetworkId)>() + .iter() + { + if speed.0.is_empty() { + continue; + } + + let end_effect = speed.ended_on_tick(game.tick_count); + + for effect in end_effect.iter() { + if let Some(client) = server.clients.get(client_id) { + client.send_remove_entity_effect(network_id, Effect::SpeedEffect.id() as u8); + } + let ok = speed.0.remove(effect); + if ok { + log::debug!("speed effect was removed with params {:?}", effect) + } + new_walk_speed.insert(entity, 0); + } + + if !end_effect.is_empty() { + for active_effect in speed.0.iter() { + if let Some(client) = server.clients.get(client_id) { + let duration = active_effect.duration as u64 + - (game.tick_count - active_effect.start_tick); + + if duration == 0 { + continue; + } + + client.send_entity_effect( + network_id, + Effect::SpeedEffect.id() as u8, + active_effect.amplifier as i8, + duration as i32, + 0x02, + ); + } + } + } + + if let Some(effect_ref) = speed.active_effect() { + let modifier = 20 * (effect_ref.amplifier + 1) as i32; + new_walk_speed.insert(entity, modifier); + }; + } + + for (entity, modifier) in new_walk_speed { + if game.ecs.get::(entity).is_err() { + game.ecs.insert(entity, SpeedEffectModifier(0))?; + } + + let walk_speed_modifier = game.ecs.get::(entity)?.0; + + if modifier == walk_speed_modifier { + continue; + } + + game.ecs + .insert(entity, SpeedEffectModifier { 0: modifier })?; + } + + let mut rem_wm = vec![]; + + for (entity, &wm) in game.ecs.query::<&SpeedEffectModifier>().iter() { + if wm.0 == 0 { + rem_wm.push(entity); + } + } + + for entity in rem_wm { + game.ecs.remove::(entity)?; + } + + Ok(()) +} + +/// Set start_tick to all effects in effects_bucket +macro_rules! add_start_tick_to_effects { + ($fn_name:ident,$type:ident) => { + fn $fn_name(game: &mut Game, server: &mut Server) -> SysResult { + for (_entity, (&client_id, effects_bucket, &network_id)) in game + .ecs + .query::<(&ClientId, &mut $type, &NetworkId)>() + .iter() + { + if effects_bucket.0.is_empty() { + continue; + } + + let not_started = effects_bucket.not_started(); + + for mut effect in not_started { + effect.start_tick = game.tick_count; + + if let Some(client) = server.clients.get(client_id) { + client.send_entity_effect( + network_id, + Effect::$type.id() as u8, + effect.amplifier as i8, + effect.duration as i32, + 0x02, + ); + } + + effects_bucket.0.replace(effect); + } + } + Ok(()) + } + }; +} + +add_start_tick_to_effects!(add_start_tick_to_speed_effects, SpeedEffect); diff --git a/libcraft/effects/Cargo.toml b/libcraft/effects/Cargo.toml new file mode 100644 index 000000000..1604777b8 --- /dev/null +++ b/libcraft/effects/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "libcraft-effects" +version = "0.1.0" +authors = ["Yui Tanabe "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +serde = { version = "1", features = ["derive"] } \ No newline at end of file diff --git a/libcraft/effects/src/effects.rs b/libcraft/effects/src/effects.rs new file mode 100644 index 000000000..c6246e59a --- /dev/null +++ b/libcraft/effects/src/effects.rs @@ -0,0 +1,321 @@ +// This file is @generated. Please do not edit. + +#[derive( + Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, serde::Deserialize, serde::Serialize, +)] +pub enum Effect { + SpeedEffect, + SlownessEffect, + HasteEffect, + MiningfatigueEffect, + StrengthEffect, + InstanthealthEffect, + InstantdamageEffect, + JumpboostEffect, + NauseaEffect, + RegenerationEffect, + ResistanceEffect, + FireresistanceEffect, + WaterbreathingEffect, + InvisibilityEffect, + BlindnessEffect, + NightvisionEffect, + HungerEffect, + WeaknessEffect, + PoisonEffect, + WitherEffect, + HealthboostEffect, + AbsorptionEffect, + SaturationEffect, + GlowingEffect, + LevitationEffect, + LuckEffect, + BadluckEffect, + SlowfallingEffect, + ConduitpowerEffect, + DolphinsgraceEffect, + BadomenEffect, + HeroofthevillageEffect, +} + +#[allow(warnings)] +#[allow(clippy::all)] +impl Effect { + /// Returns the `id` property of this `Effect`. + pub fn id(&self) -> u8 { + match self { + Effect::SpeedEffect => 1, + Effect::SlownessEffect => 2, + Effect::HasteEffect => 3, + Effect::MiningfatigueEffect => 4, + Effect::StrengthEffect => 5, + Effect::InstanthealthEffect => 6, + Effect::InstantdamageEffect => 7, + Effect::JumpboostEffect => 8, + Effect::NauseaEffect => 9, + Effect::RegenerationEffect => 10, + Effect::ResistanceEffect => 11, + Effect::FireresistanceEffect => 12, + Effect::WaterbreathingEffect => 13, + Effect::InvisibilityEffect => 14, + Effect::BlindnessEffect => 15, + Effect::NightvisionEffect => 16, + Effect::HungerEffect => 17, + Effect::WeaknessEffect => 18, + Effect::PoisonEffect => 19, + Effect::WitherEffect => 20, + Effect::HealthboostEffect => 21, + Effect::AbsorptionEffect => 22, + Effect::SaturationEffect => 23, + Effect::GlowingEffect => 24, + Effect::LevitationEffect => 25, + Effect::LuckEffect => 26, + Effect::BadluckEffect => 27, + Effect::SlowfallingEffect => 28, + Effect::ConduitpowerEffect => 29, + Effect::DolphinsgraceEffect => 31, + Effect::BadomenEffect => 32, + Effect::HeroofthevillageEffect => 32, + } + } + + /// Gets a `Effect` by its `id`. + pub fn from_id(id: u8) -> Option { + match id { + 1 => Some(Effect::SpeedEffect), + 2 => Some(Effect::SlownessEffect), + 3 => Some(Effect::HasteEffect), + 4 => Some(Effect::MiningfatigueEffect), + 5 => Some(Effect::StrengthEffect), + 6 => Some(Effect::InstanthealthEffect), + 7 => Some(Effect::InstantdamageEffect), + 8 => Some(Effect::JumpboostEffect), + 9 => Some(Effect::NauseaEffect), + 10 => Some(Effect::RegenerationEffect), + 11 => Some(Effect::ResistanceEffect), + 12 => Some(Effect::FireresistanceEffect), + 13 => Some(Effect::WaterbreathingEffect), + 14 => Some(Effect::InvisibilityEffect), + 15 => Some(Effect::BlindnessEffect), + 16 => Some(Effect::NightvisionEffect), + 17 => Some(Effect::HungerEffect), + 18 => Some(Effect::WeaknessEffect), + 19 => Some(Effect::PoisonEffect), + 20 => Some(Effect::WitherEffect), + 21 => Some(Effect::HealthboostEffect), + 22 => Some(Effect::AbsorptionEffect), + 23 => Some(Effect::SaturationEffect), + 24 => Some(Effect::GlowingEffect), + 25 => Some(Effect::LevitationEffect), + 26 => Some(Effect::LuckEffect), + 27 => Some(Effect::BadluckEffect), + 28 => Some(Effect::SlowfallingEffect), + 29 => Some(Effect::ConduitpowerEffect), + 31 => Some(Effect::DolphinsgraceEffect), + 32 => Some(Effect::BadomenEffect), + 32 => Some(Effect::HeroofthevillageEffect), + _ => None, + } + } +} +#[allow(warnings)] +#[allow(clippy::all)] +impl Effect { + /// Returns the `name` property of this `Effect`. + pub fn name(&self) -> &'static str { + match self { + Effect::SpeedEffect => "Speed", + Effect::SlownessEffect => "Slowness", + Effect::HasteEffect => "Haste", + Effect::MiningfatigueEffect => "MiningFatigue", + Effect::StrengthEffect => "Strength", + Effect::InstanthealthEffect => "InstantHealth", + Effect::InstantdamageEffect => "InstantDamage", + Effect::JumpboostEffect => "JumpBoost", + Effect::NauseaEffect => "Nausea", + Effect::RegenerationEffect => "Regeneration", + Effect::ResistanceEffect => "Resistance", + Effect::FireresistanceEffect => "FireResistance", + Effect::WaterbreathingEffect => "WaterBreathing", + Effect::InvisibilityEffect => "Invisibility", + Effect::BlindnessEffect => "Blindness", + Effect::NightvisionEffect => "NightVision", + Effect::HungerEffect => "Hunger", + Effect::WeaknessEffect => "Weakness", + Effect::PoisonEffect => "Poison", + Effect::WitherEffect => "Wither", + Effect::HealthboostEffect => "HealthBoost", + Effect::AbsorptionEffect => "Absorption", + Effect::SaturationEffect => "Saturation", + Effect::GlowingEffect => "Glowing", + Effect::LevitationEffect => "Levitation", + Effect::LuckEffect => "Luck", + Effect::BadluckEffect => "BadLuck", + Effect::SlowfallingEffect => "SlowFalling", + Effect::ConduitpowerEffect => "ConduitPower", + Effect::DolphinsgraceEffect => "DolphinsGrace", + Effect::BadomenEffect => "BadOmen", + Effect::HeroofthevillageEffect => "HeroOfTheVillage", + } + } + + /// Gets a `Effect` by its `name`. + pub fn from_name(name: &str) -> Option { + match name { + "Speed" => Some(Effect::SpeedEffect), + "Slowness" => Some(Effect::SlownessEffect), + "Haste" => Some(Effect::HasteEffect), + "MiningFatigue" => Some(Effect::MiningfatigueEffect), + "Strength" => Some(Effect::StrengthEffect), + "InstantHealth" => Some(Effect::InstanthealthEffect), + "InstantDamage" => Some(Effect::InstantdamageEffect), + "JumpBoost" => Some(Effect::JumpboostEffect), + "Nausea" => Some(Effect::NauseaEffect), + "Regeneration" => Some(Effect::RegenerationEffect), + "Resistance" => Some(Effect::ResistanceEffect), + "FireResistance" => Some(Effect::FireresistanceEffect), + "WaterBreathing" => Some(Effect::WaterbreathingEffect), + "Invisibility" => Some(Effect::InvisibilityEffect), + "Blindness" => Some(Effect::BlindnessEffect), + "NightVision" => Some(Effect::NightvisionEffect), + "Hunger" => Some(Effect::HungerEffect), + "Weakness" => Some(Effect::WeaknessEffect), + "Poison" => Some(Effect::PoisonEffect), + "Wither" => Some(Effect::WitherEffect), + "HealthBoost" => Some(Effect::HealthboostEffect), + "Absorption" => Some(Effect::AbsorptionEffect), + "Saturation" => Some(Effect::SaturationEffect), + "Glowing" => Some(Effect::GlowingEffect), + "Levitation" => Some(Effect::LevitationEffect), + "Luck" => Some(Effect::LuckEffect), + "BadLuck" => Some(Effect::BadluckEffect), + "SlowFalling" => Some(Effect::SlowfallingEffect), + "ConduitPower" => Some(Effect::ConduitpowerEffect), + "DolphinsGrace" => Some(Effect::DolphinsgraceEffect), + "BadOmen" => Some(Effect::BadomenEffect), + "HeroOfTheVillage" => Some(Effect::HeroofthevillageEffect), + _ => None, + } + } +} +#[allow(warnings)] +#[allow(clippy::all)] +impl Effect { + /// Returns the `display_name` property of this `Effect`. + pub fn display_name(&self) -> &'static str { + match self { + Effect::SpeedEffect => "Speed", + Effect::SlownessEffect => "Slowness", + Effect::HasteEffect => "Haste", + Effect::MiningfatigueEffect => "Mining Fatigue", + Effect::StrengthEffect => "Strength", + Effect::InstanthealthEffect => "Instant Health", + Effect::InstantdamageEffect => "Instant Damage", + Effect::JumpboostEffect => "Jump Boost", + Effect::NauseaEffect => "Nausea", + Effect::RegenerationEffect => "Regeneration", + Effect::ResistanceEffect => "Resistance", + Effect::FireresistanceEffect => "Fire Resistance", + Effect::WaterbreathingEffect => "Water Breathing", + Effect::InvisibilityEffect => "Invisibility", + Effect::BlindnessEffect => "Blindness", + Effect::NightvisionEffect => "Night Vision", + Effect::HungerEffect => "Hunger", + Effect::WeaknessEffect => "Weakness", + Effect::PoisonEffect => "Poison", + Effect::WitherEffect => "Wither", + Effect::HealthboostEffect => "Health Boost", + Effect::AbsorptionEffect => "Absorption", + Effect::SaturationEffect => "Saturation", + Effect::GlowingEffect => "Glowing", + Effect::LevitationEffect => "Levitation", + Effect::LuckEffect => "Luck", + Effect::BadluckEffect => "Bad Luck", + Effect::SlowfallingEffect => "Slow Falling", + Effect::ConduitpowerEffect => "Conduit Power", + Effect::DolphinsgraceEffect => "Dolphin's Grace", + Effect::BadomenEffect => "Bad Omen", + Effect::HeroofthevillageEffect => "Hero Of The Village", + } + } + + /// Gets a `Effect` by its `display_name`. + pub fn from_display_name(display_name: &str) -> Option { + match display_name { + "Speed" => Some(Effect::SpeedEffect), + "Slowness" => Some(Effect::SlownessEffect), + "Haste" => Some(Effect::HasteEffect), + "Mining Fatigue" => Some(Effect::MiningfatigueEffect), + "Strength" => Some(Effect::StrengthEffect), + "Instant Health" => Some(Effect::InstanthealthEffect), + "Instant Damage" => Some(Effect::InstantdamageEffect), + "Jump Boost" => Some(Effect::JumpboostEffect), + "Nausea" => Some(Effect::NauseaEffect), + "Regeneration" => Some(Effect::RegenerationEffect), + "Resistance" => Some(Effect::ResistanceEffect), + "Fire Resistance" => Some(Effect::FireresistanceEffect), + "Water Breathing" => Some(Effect::WaterbreathingEffect), + "Invisibility" => Some(Effect::InvisibilityEffect), + "Blindness" => Some(Effect::BlindnessEffect), + "Night Vision" => Some(Effect::NightvisionEffect), + "Hunger" => Some(Effect::HungerEffect), + "Weakness" => Some(Effect::WeaknessEffect), + "Poison" => Some(Effect::PoisonEffect), + "Wither" => Some(Effect::WitherEffect), + "Health Boost" => Some(Effect::HealthboostEffect), + "Absorption" => Some(Effect::AbsorptionEffect), + "Saturation" => Some(Effect::SaturationEffect), + "Glowing" => Some(Effect::GlowingEffect), + "Levitation" => Some(Effect::LevitationEffect), + "Luck" => Some(Effect::LuckEffect), + "Bad Luck" => Some(Effect::BadluckEffect), + "Slow Falling" => Some(Effect::SlowfallingEffect), + "Conduit Power" => Some(Effect::ConduitpowerEffect), + "Dolphin's Grace" => Some(Effect::DolphinsgraceEffect), + "Bad Omen" => Some(Effect::BadomenEffect), + "Hero Of The Village" => Some(Effect::HeroofthevillageEffect), + _ => None, + } + } +} +#[allow(warnings)] +#[allow(clippy::all)] +impl Effect { + /// Returns the `is_good` property of this `Effect`. + pub fn is_good(&self) -> bool { + match self { + Effect::SpeedEffect => true, + Effect::SlownessEffect => false, + Effect::HasteEffect => true, + Effect::MiningfatigueEffect => false, + Effect::StrengthEffect => true, + Effect::InstanthealthEffect => true, + Effect::InstantdamageEffect => false, + Effect::JumpboostEffect => true, + Effect::NauseaEffect => false, + Effect::RegenerationEffect => true, + Effect::ResistanceEffect => true, + Effect::FireresistanceEffect => true, + Effect::WaterbreathingEffect => true, + Effect::InvisibilityEffect => true, + Effect::BlindnessEffect => false, + Effect::NightvisionEffect => true, + Effect::HungerEffect => false, + Effect::WeaknessEffect => false, + Effect::PoisonEffect => false, + Effect::WitherEffect => false, + Effect::HealthboostEffect => true, + Effect::AbsorptionEffect => true, + Effect::SaturationEffect => true, + Effect::GlowingEffect => false, + Effect::LevitationEffect => false, + Effect::LuckEffect => true, + Effect::BadluckEffect => false, + Effect::SlowfallingEffect => true, + Effect::ConduitpowerEffect => true, + Effect::DolphinsgraceEffect => true, + Effect::BadomenEffect => true, + Effect::HeroofthevillageEffect => true, + } + } +} diff --git a/libcraft/effects/src/lib.rs b/libcraft/effects/src/lib.rs new file mode 100644 index 000000000..2d60ce321 --- /dev/null +++ b/libcraft/effects/src/lib.rs @@ -0,0 +1,2 @@ +pub mod effects; +pub use effects::Effect; diff --git a/libcraft/generators/python/effects.py b/libcraft/generators/python/effects.py new file mode 100644 index 000000000..861f5144b --- /dev/null +++ b/libcraft/generators/python/effects.py @@ -0,0 +1,25 @@ +from common import load_minecraft_json, camel_case, generate_enum, generate_enum_property, output + +effects = [] +ids = {} +names = {} +display_names = {} +is_good = {} + +for effect in load_minecraft_json("effects.json", "1.16.1"): + variant = camel_case(effect['name'])+'Effect' + effects.append(variant) + ids[variant] = effect['id'] + names[variant] = effect['name'] + display_names[variant] = effect['displayName'] + is_good[variant] = True if effect['type'] == "good" else False + +enumName = "Effect" + +output_data = generate_enum(enumName, effects, ["serde::Deserialize", "serde::Serialize"]) +output_data += generate_enum_property(enumName, "id", "u8", ids, True) +output_data += generate_enum_property(enumName, "name", "&str", names, True, "&'static str") +output_data += generate_enum_property(enumName, "display_name", "&str", display_names, True, "&'static str") +output_data += generate_enum_property(enumName, "is_good", "bool", is_good) + +output("effects/src/effects.rs", output_data) diff --git a/quill/api/src/lib.rs b/quill/api/src/lib.rs index 23b3a9418..e0c1f927c 100644 --- a/quill/api/src/lib.rs +++ b/quill/api/src/lib.rs @@ -22,7 +22,9 @@ pub use libcraft_particles::{Particle, ParticleKind}; pub use libcraft_text::*; #[doc(inline)] -pub use quill_common::{components, entity_init::EntityInit, events, Component}; +pub use quill_common::{ + components, components_effects, entity_init::EntityInit, events, Component, +}; #[doc(inline)] pub use uuid::Uuid; diff --git a/quill/common/Cargo.toml b/quill/common/Cargo.toml index a01736a76..498b4be2a 100644 --- a/quill/common/Cargo.toml +++ b/quill/common/Cargo.toml @@ -11,6 +11,7 @@ derive_more = "0.99.16" libcraft-core = { path = "../../libcraft/core" } libcraft-particles = { path = "../../libcraft/particles" } libcraft-text = { path = "../../libcraft/text" } +libcraft-effects = { path = "../../libcraft/effects" } serde = { version = "1", features = ["derive"] } smartstring = { version = "0.2", features = ["serde"] } uuid = { version = "0.8", features = ["serde"] } diff --git a/quill/common/src/component.rs b/quill/common/src/component.rs index f113fa29a..aaa1a79e3 100644 --- a/quill/common/src/component.rs +++ b/quill/common/src/component.rs @@ -7,6 +7,7 @@ use libcraft_particles::Particle; use uuid::Uuid; use crate::components::*; +use crate::components_effects::*; use crate::entities::*; use crate::events::*; @@ -64,6 +65,40 @@ host_component_enum! { // `Pod` components Position = 0, + // Effect Components + SpeedEffect = 1, + SlownessEffect = 2, + HasteEffect = 3, + MiningFatigueEffect = 4, + StrengthEffect = 5, + InstantHealthEffect = 6, + InstantDamageEffect = 7, + JumpBoostEffect = 8, + NauseaEffect = 9, + RegenerationEffect = 10, + ResistanceEffect = 11, + FireResistanceEffect = 12, + WaterBreathingEffect = 13, + InvisibilityEffect = 14, + BlindnessEffect = 15, + NightVisionEffect = 16, + HungerEffect = 17, + WeaknessEffect = 18, + PoisonEffect = 19, + WitherEffect = 20, + HealthBoostEffect = 21, + AbsorptionEffect = 22, + SaturationEffect = 23, + GlowingEffect = 24, + LevitationEffect = 25, + LuckEffect = 26, + BadLuckEffect = 27, + SlowFallingEffect = 28, + ConduitPowerEffect = 29, + DolphinsGraceEffect = 30, + BadOmenEffect = 31, + HeroOfTheVillageEffect = 32, + // Entity marker components AreaEffectCloud = 100, ArmorStand = 101, @@ -199,7 +234,7 @@ host_component_enum! { Instabreak = 1021, Invulnerable = 1022, - + SpeedEffectModifier = 1023, } } diff --git a/quill/common/src/components_effects.rs b/quill/common/src/components_effects.rs new file mode 100644 index 000000000..e30c2e83a --- /dev/null +++ b/quill/common/src/components_effects.rs @@ -0,0 +1,125 @@ +use serde::{Deserialize, Serialize}; +use std::cmp::Ordering; +use std::collections::BTreeSet; + +/// Storing Potion effects info. +#[derive(Copy, Clone, Debug, Hash, Serialize, PartialEq, Eq, Deserialize)] +pub struct PotionApplication { + /// Strength Level of effect. + pub amplifier: u8, + /// Tick-based duration of the effect. + pub duration: u32, + /// Whether spawn particles or not. + pub particle: bool, + /// Whether the effect was given by a beacon or not. + pub ambient: bool, + /// Show effect icon or not. + pub icon: bool, + + /// Store when effect was added, if start_tick == 0 effect not yet sent to client + pub start_tick: u64, +} + +impl PotionApplication { + pub fn start_at(&mut self, start_tick: u64) { + self.start_tick = start_tick + } +} + +impl Ord for PotionApplication { + fn cmp(&self, other: &Self) -> Ordering { + if self.amplifier > other.amplifier || self.duration > other.duration { + Ordering::Greater + } else if self.amplifier == other.amplifier || self.duration == other.duration { + Ordering::Equal + } else { + Ordering::Less + } + } +} +impl PartialOrd for PotionApplication { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +macro_rules! impl_effect { + ($ident:ident) => { + #[derive(Serialize, Deserialize, Eq, PartialEq, Hash)] + pub struct $ident(pub BTreeSet); + impl $ident { + pub fn new() -> $ident { + $ident { 0: BTreeSet::new() } + } + pub fn add_effect(&mut self, effect: PotionApplication) -> bool { + self.0.insert(effect) + } + pub fn not_started(&mut self) -> Vec { + self.0 + .iter() + .filter(|effect| effect.start_tick == 0) + .cloned() + .collect::>() + } + pub fn ended_on_tick(&mut self, tick: u64) -> Vec { + self.0 + .iter() + .filter(|effect| { + tick >= effect.start_tick + effect.duration as u64 && effect.start_tick != 0 + }) + .cloned() + .collect::>() + } + pub fn active_effect(&mut self) -> Option<&PotionApplication> { + self.0.iter().last() + } + } + impl Default for $ident { + fn default() -> Self { + $ident::new() + } + } + bincode_component_impl!($ident); + }; +} + +impl_effect!(SpeedEffect); +impl_effect!(SlownessEffect); +impl_effect!(HasteEffect); +impl_effect!(MiningFatigueEffect); +impl_effect!(StrengthEffect); +impl_effect!(InstantHealthEffect); +impl_effect!(InstantDamageEffect); +impl_effect!(JumpBoostEffect); +impl_effect!(NauseaEffect); +impl_effect!(RegenerationEffect); +impl_effect!(ResistanceEffect); +impl_effect!(FireResistanceEffect); +impl_effect!(WaterBreathingEffect); +impl_effect!(InvisibilityEffect); +impl_effect!(BlindnessEffect); +impl_effect!(NightVisionEffect); +impl_effect!(HungerEffect); +impl_effect!(WeaknessEffect); +impl_effect!(PoisonEffect); +impl_effect!(WitherEffect); +impl_effect!(HealthBoostEffect); +impl_effect!(AbsorptionEffect); +impl_effect!(SaturationEffect); +impl_effect!(GlowingEffect); +impl_effect!(LevitationEffect); +impl_effect!(LuckEffect); +impl_effect!(BadLuckEffect); +impl_effect!(SlowFallingEffect); +impl_effect!(ConduitPowerEffect); +impl_effect!(DolphinsGraceEffect); +impl_effect!(BadOmenEffect); +impl_effect!(HeroOfTheVillageEffect); + +/// A walk speed modifier in percent +#[derive( + Copy, Clone, Debug, PartialEq, Serialize, Deserialize, derive_more::Deref, derive_more::DerefMut, +)] +pub struct SpeedEffectModifier(pub i32); + +bincode_component_impl!(SpeedEffectModifier); diff --git a/quill/common/src/lib.rs b/quill/common/src/lib.rs index 53d8a262b..7bfb5625f 100644 --- a/quill/common/src/lib.rs +++ b/quill/common/src/lib.rs @@ -4,6 +4,7 @@ mod utils; pub mod component; pub mod block; pub mod components; +pub mod components_effects; pub mod entities; pub mod entity; pub mod entity_init; From 6d683e658c06082cc17ddfd6d82fa716fb58e0d5 Mon Sep 17 00:00:00 2001 From: Denis Tokarev Date: Tue, 30 Nov 2021 11:42:16 +0500 Subject: [PATCH 2/4] change modifier --- .../server/src/packet_handlers/use_item.rs | 4 +- feather/server/src/systems/effects.rs | 47 +++++++++++++------ quill/common/src/component.rs | 2 +- quill/common/src/components_effects.rs | 41 ++++++++++------ 4 files changed, 62 insertions(+), 32 deletions(-) diff --git a/feather/server/src/packet_handlers/use_item.rs b/feather/server/src/packet_handlers/use_item.rs index 31fc40f4c..480669dda 100644 --- a/feather/server/src/packet_handlers/use_item.rs +++ b/feather/server/src/packet_handlers/use_item.rs @@ -13,7 +13,7 @@ pub fn handle_use_item( ) -> SysResult { // example for effect system let mut speed = SpeedEffect::default(); - speed.add_effect(PotionApplication { + speed.add_effect(EffectApplication { amplifier: 0, duration: 20 * TPS, particle: false, @@ -22,7 +22,7 @@ pub fn handle_use_item( start_tick: 0, }); - speed.add_effect(PotionApplication { + speed.add_effect(EffectApplication { amplifier: 1, duration: 10 * TPS, particle: false, diff --git a/feather/server/src/systems/effects.rs b/feather/server/src/systems/effects.rs index 8495f0f1e..0df8b49dd 100644 --- a/feather/server/src/systems/effects.rs +++ b/feather/server/src/systems/effects.rs @@ -1,7 +1,7 @@ use common::Game; use ecs::{SysResult, SystemExecutor}; use libcraft_effects::effects::Effect; -use quill_common::components_effects::{SpeedEffect, SpeedEffectModifier}; +use quill_common::components_effects::{SpeedEffect, WalkEffectModifier}; use std::collections::HashMap; use crate::{ClientId, NetworkId, Server}; @@ -65,30 +65,47 @@ fn speed_effect(game: &mut Game, server: &mut Server) -> SysResult { } for (entity, modifier) in new_walk_speed { - if game.ecs.get::(entity).is_err() { - game.ecs.insert(entity, SpeedEffectModifier(0))?; + if game.ecs.get::(entity).is_err() { + game.ecs.insert(entity, WalkEffectModifier::new())?; } - let walk_speed_modifier = game.ecs.get::(entity)?.0; + let mut walk_speed_modifier = game.ecs.get_mut::(entity)?; + if walk_speed_modifier.0.contains_key(&Effect::SpeedEffect) { + if modifier + == *walk_speed_modifier + .0 + .get(&Effect::SpeedEffect) + .unwrap_or(&0) + { + continue; + } - if modifier == walk_speed_modifier { - continue; + walk_speed_modifier.0.insert(Effect::SpeedEffect, modifier); } - - game.ecs - .insert(entity, SpeedEffectModifier { 0: modifier })?; } - let mut rem_wm = vec![]; + let mut rem_comp = vec![]; + + for (entity, wm) in game.ecs.query::<&mut WalkEffectModifier>().iter() { + let mut rem_wm = vec![]; + + for (effect, modifier) in wm.0.iter() { + if *modifier == 0 { + rem_wm.push(*effect); + } + } + + for effect in rem_wm { + wm.0.remove(&effect); + } - for (entity, &wm) in game.ecs.query::<&SpeedEffectModifier>().iter() { - if wm.0 == 0 { - rem_wm.push(entity); + if wm.0.is_empty() { + rem_comp.push(entity); } } - for entity in rem_wm { - game.ecs.remove::(entity)?; + for entity in rem_comp { + game.ecs.remove::(entity)?; } Ok(()) diff --git a/quill/common/src/component.rs b/quill/common/src/component.rs index aaa1a79e3..0ec13e572 100644 --- a/quill/common/src/component.rs +++ b/quill/common/src/component.rs @@ -234,7 +234,7 @@ host_component_enum! { Instabreak = 1021, Invulnerable = 1022, - SpeedEffectModifier = 1023, + WalkEffectModifier = 2023, } } diff --git a/quill/common/src/components_effects.rs b/quill/common/src/components_effects.rs index e30c2e83a..bf7c94d0a 100644 --- a/quill/common/src/components_effects.rs +++ b/quill/common/src/components_effects.rs @@ -1,10 +1,11 @@ +use libcraft_effects::effects::Effect; use serde::{Deserialize, Serialize}; use std::cmp::Ordering; -use std::collections::BTreeSet; +use std::collections::{BTreeMap, BTreeSet}; -/// Storing Potion effects info. +/// Storing effects info. #[derive(Copy, Clone, Debug, Hash, Serialize, PartialEq, Eq, Deserialize)] -pub struct PotionApplication { +pub struct EffectApplication { /// Strength Level of effect. pub amplifier: u8, /// Tick-based duration of the effect. @@ -20,13 +21,13 @@ pub struct PotionApplication { pub start_tick: u64, } -impl PotionApplication { +impl EffectApplication { pub fn start_at(&mut self, start_tick: u64) { self.start_tick = start_tick } } -impl Ord for PotionApplication { +impl Ord for EffectApplication { fn cmp(&self, other: &Self) -> Ordering { if self.amplifier > other.amplifier || self.duration > other.duration { Ordering::Greater @@ -37,7 +38,7 @@ impl Ord for PotionApplication { } } } -impl PartialOrd for PotionApplication { +impl PartialOrd for EffectApplication { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } @@ -46,22 +47,22 @@ impl PartialOrd for PotionApplication { macro_rules! impl_effect { ($ident:ident) => { #[derive(Serialize, Deserialize, Eq, PartialEq, Hash)] - pub struct $ident(pub BTreeSet); + pub struct $ident(pub BTreeSet); impl $ident { pub fn new() -> $ident { $ident { 0: BTreeSet::new() } } - pub fn add_effect(&mut self, effect: PotionApplication) -> bool { + pub fn add_effect(&mut self, effect: EffectApplication) -> bool { self.0.insert(effect) } - pub fn not_started(&mut self) -> Vec { + pub fn not_started(&mut self) -> Vec { self.0 .iter() .filter(|effect| effect.start_tick == 0) .cloned() .collect::>() } - pub fn ended_on_tick(&mut self, tick: u64) -> Vec { + pub fn ended_on_tick(&mut self, tick: u64) -> Vec { self.0 .iter() .filter(|effect| { @@ -70,7 +71,7 @@ macro_rules! impl_effect { .cloned() .collect::>() } - pub fn active_effect(&mut self) -> Option<&PotionApplication> { + pub fn active_effect(&mut self) -> Option<&EffectApplication> { self.0.iter().last() } } @@ -118,8 +119,20 @@ impl_effect!(HeroOfTheVillageEffect); /// A walk speed modifier in percent #[derive( - Copy, Clone, Debug, PartialEq, Serialize, Deserialize, derive_more::Deref, derive_more::DerefMut, + Clone, Debug, PartialEq, Serialize, Deserialize, derive_more::Deref, derive_more::DerefMut, )] -pub struct SpeedEffectModifier(pub i32); +pub struct WalkEffectModifier(pub BTreeMap); -bincode_component_impl!(SpeedEffectModifier); +impl WalkEffectModifier { + pub fn new() -> WalkEffectModifier { + WalkEffectModifier { 0: BTreeMap::new() } + } +} + +impl Default for WalkEffectModifier { + fn default() -> Self { + WalkEffectModifier::new() + } +} + +bincode_component_impl!(WalkEffectModifier); From 2c7d508a9200bd1e2aca6e21e54a04605255b0b2 Mon Sep 17 00:00:00 2001 From: Denis Tokarev Date: Sat, 11 Dec 2021 17:01:35 +0500 Subject: [PATCH 3/4] add particle on effect --- feather/server/src/systems/effects.rs | 36 ++++++++++++++++++++++++--- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/feather/server/src/systems/effects.rs b/feather/server/src/systems/effects.rs index 0df8b49dd..fcf68beee 100644 --- a/feather/server/src/systems/effects.rs +++ b/feather/server/src/systems/effects.rs @@ -1,7 +1,10 @@ +use base::{Particle, ParticleKind, TPS}; use common::Game; -use ecs::{SysResult, SystemExecutor}; +use ecs::{Entity, SysResult, SystemExecutor}; +use libcraft_core::Position; use libcraft_effects::effects::Effect; use quill_common::components_effects::{SpeedEffect, WalkEffectModifier}; +use quill_common::entity_init::EntityInit; use std::collections::HashMap; use crate::{ClientId, NetworkId, Server}; @@ -111,11 +114,31 @@ fn speed_effect(game: &mut Game, server: &mut Server) -> SysResult { Ok(()) } -/// Set start_tick to all effects in effects_bucket +fn add_particles(game: &mut Game, entity: Entity) -> SysResult { + if game.tick_count % (TPS * 2) as u64 == 0 { + let position = *game.ecs.get::(entity)?; + + let mut entity_builder = game.create_entity_builder(position, EntityInit::AreaEffectCloud); + + entity_builder.add(position); + entity_builder.add(Particle { + kind: ParticleKind::Effect, + offset_x: 0.0, + offset_y: 0.0, + offset_z: 0.0, + count: 5, + }); + game.spawn_entity(entity_builder); + } + Ok(()) +} + +/// Set start_tick to all effects in effects_bucket and spawn particles macro_rules! add_start_tick_to_effects { ($fn_name:ident,$type:ident) => { fn $fn_name(game: &mut Game, server: &mut Server) -> SysResult { - for (_entity, (&client_id, effects_bucket, &network_id)) in game + let mut entities = vec![]; + for (entity, (&client_id, effects_bucket, &network_id)) in game .ecs .query::<(&ClientId, &mut $type, &NetworkId)>() .iter() @@ -124,6 +147,8 @@ macro_rules! add_start_tick_to_effects { continue; } + entities.push(entity); + let not_started = effects_bucket.not_started(); for mut effect in not_started { @@ -142,6 +167,11 @@ macro_rules! add_start_tick_to_effects { effects_bucket.0.replace(effect); } } + + for entity in entities { + add_particles(game, entity)?; + } + Ok(()) } }; From 699074c882083124e65f1f505600abe8737a6454 Mon Sep 17 00:00:00 2001 From: Denis Tokarev Date: Wed, 15 Dec 2021 23:29:31 +0500 Subject: [PATCH 4/4] cleanup and split into multiple systems --- feather/server/src/client.rs | 15 +- .../server/src/packet_handlers/use_item.rs | 16 +- feather/server/src/systems/effects.rs | 140 ++++++++++++------ quill/common/src/components_effects.rs | 11 +- 4 files changed, 124 insertions(+), 58 deletions(-) diff --git a/feather/server/src/client.rs b/feather/server/src/client.rs index 082cb064a..7f60d852f 100644 --- a/feather/server/src/client.rs +++ b/feather/server/src/client.rs @@ -43,6 +43,7 @@ use crate::{ network_id_registry::NetworkId, Options, }; +use quill_common::components_effects::EffectFlags; use slab::Slab; /// Max number of chunks to send to a client per tick. @@ -609,14 +610,24 @@ impl Client { effect_id: u8, amplifier: i8, duration: i32, - flags: u8, + flags: EffectFlags, ) { + let mut flags_bit = 0; + if flags.ambient { + flags_bit |= 1 << 0; + } + if flags.particle { + flags_bit |= 1 << 1; + } + if flags.icon { + flags_bit |= 1 << 2; + } self.send_packet(EntityEffect { entity_id: network_id.0, effect_id, amplifier, duration, - flags, + flags: flags_bit, }) } diff --git a/feather/server/src/packet_handlers/use_item.rs b/feather/server/src/packet_handlers/use_item.rs index 480669dda..64b2ee072 100644 --- a/feather/server/src/packet_handlers/use_item.rs +++ b/feather/server/src/packet_handlers/use_item.rs @@ -16,18 +16,22 @@ pub fn handle_use_item( speed.add_effect(EffectApplication { amplifier: 0, duration: 20 * TPS, - particle: false, - ambient: false, - icon: false, + flags: EffectFlags { + particle: true, + ambient: false, + icon: true, + }, start_tick: 0, }); speed.add_effect(EffectApplication { amplifier: 1, duration: 10 * TPS, - particle: false, - ambient: false, - icon: false, + flags: EffectFlags { + particle: true, + ambient: false, + icon: true, + }, start_tick: 0, }); diff --git a/feather/server/src/systems/effects.rs b/feather/server/src/systems/effects.rs index fcf68beee..d5dfb384d 100644 --- a/feather/server/src/systems/effects.rs +++ b/feather/server/src/systems/effects.rs @@ -3,20 +3,42 @@ use common::Game; use ecs::{Entity, SysResult, SystemExecutor}; use libcraft_core::Position; use libcraft_effects::effects::Effect; -use quill_common::components_effects::{SpeedEffect, WalkEffectModifier}; +use quill_common::components_effects::{EffectApplication, SpeedEffect, WalkEffectModifier}; use quill_common::entity_init::EntityInit; use std::collections::HashMap; -use crate::{ClientId, NetworkId, Server}; +use crate::{Client, ClientId, NetworkId, Server}; pub fn register(_game: &mut Game, systems: &mut SystemExecutor) { systems .group::() .add_system(add_start_tick_to_speed_effects) - .add_system(speed_effect); + .add_system(speed_effect) + .add_system(walk_effect_modifier_cleaner) + .add_system(effect_remover); } -fn speed_effect(game: &mut Game, server: &mut Server) -> SysResult { +fn speed_effect(game: &mut Game, _server: &mut Server) -> SysResult { + let mut new_walk_speed = HashMap::new(); + for (entity, speed) in game.ecs.query::<&mut SpeedEffect>().iter() { + if speed.0.is_empty() { + continue; + } + + if let Some(effect_ref) = speed.active_effect() { + let modifier = 20 * (effect_ref.amplifier + 1) as i32; + new_walk_speed.insert(entity, modifier); + }; + } + + for (entity, modifier) in new_walk_speed { + change_modifier(game, entity, modifier)?; + } + + Ok(()) +} + +fn effect_remover(game: &mut Game, server: &mut Server) -> SysResult { let mut new_walk_speed = HashMap::new(); for (entity, (&client_id, speed, &network_id)) in game .ecs @@ -33,60 +55,39 @@ fn speed_effect(game: &mut Game, server: &mut Server) -> SysResult { if let Some(client) = server.clients.get(client_id) { client.send_remove_entity_effect(network_id, Effect::SpeedEffect.id() as u8); } - let ok = speed.0.remove(effect); - if ok { + + if speed.0.remove(effect) { log::debug!("speed effect was removed with params {:?}", effect) } new_walk_speed.insert(entity, 0); } if !end_effect.is_empty() { - for active_effect in speed.0.iter() { + if let Some(active_effect) = speed.active_effect() { if let Some(client) = server.clients.get(client_id) { let duration = active_effect.duration as u64 - (game.tick_count - active_effect.start_tick); - if duration == 0 { - continue; - } - - client.send_entity_effect( - network_id, + send_effect_to_client( Effect::SpeedEffect.id() as u8, - active_effect.amplifier as i8, - duration as i32, - 0x02, + network_id, + active_effect, + client, + duration, ); } } } - - if let Some(effect_ref) = speed.active_effect() { - let modifier = 20 * (effect_ref.amplifier + 1) as i32; - new_walk_speed.insert(entity, modifier); - }; } for (entity, modifier) in new_walk_speed { - if game.ecs.get::(entity).is_err() { - game.ecs.insert(entity, WalkEffectModifier::new())?; - } - - let mut walk_speed_modifier = game.ecs.get_mut::(entity)?; - if walk_speed_modifier.0.contains_key(&Effect::SpeedEffect) { - if modifier - == *walk_speed_modifier - .0 - .get(&Effect::SpeedEffect) - .unwrap_or(&0) - { - continue; - } - - walk_speed_modifier.0.insert(Effect::SpeedEffect, modifier); - } + change_modifier(game, entity, modifier)?; } + Ok(()) +} + +fn walk_effect_modifier_cleaner(game: &mut Game, _server: &mut Server) -> SysResult { let mut rem_comp = vec![]; for (entity, wm) in game.ecs.query::<&mut WalkEffectModifier>().iter() { @@ -114,7 +115,48 @@ fn speed_effect(game: &mut Game, server: &mut Server) -> SysResult { Ok(()) } -fn add_particles(game: &mut Game, entity: Entity) -> SysResult { +fn change_modifier(game: &mut Game, entity: Entity, new_modifier: i32) -> SysResult { + if game.ecs.get::(entity).is_err() { + game.ecs.insert(entity, WalkEffectModifier::new())?; + } + + let mut walk_speed_modifier = game.ecs.get_mut::(entity)?; + if walk_speed_modifier.0.contains_key(&Effect::SpeedEffect) + && new_modifier + != *walk_speed_modifier + .0 + .get(&Effect::SpeedEffect) + .unwrap_or(&0) + { + walk_speed_modifier + .0 + .insert(Effect::SpeedEffect, new_modifier); + } + Ok(()) +} + +fn send_effect_to_client( + effect_id: u8, + network_id: NetworkId, + active_effect: &EffectApplication, + client: &Client, + duration: u64, +) { + if duration == 0 { + return; + } + + client.send_entity_effect( + network_id, + effect_id, + active_effect.amplifier as i8, + duration as i32, + active_effect.flags, + ); +} + +// todo change particle color +fn add_particles(game: &mut Game, entity: Entity, _effect_kind: Effect) -> SysResult { if game.tick_count % (TPS * 2) as u64 == 0 { let position = *game.ecs.get::(entity)?; @@ -147,7 +189,11 @@ macro_rules! add_start_tick_to_effects { continue; } - entities.push(entity); + if let Some(active_effect) = effects_bucket.active_effect() { + if active_effect.flags.particle { + entities.push((entity, Effect::$type)); + } + } let not_started = effects_bucket.not_started(); @@ -155,12 +201,12 @@ macro_rules! add_start_tick_to_effects { effect.start_tick = game.tick_count; if let Some(client) = server.clients.get(client_id) { - client.send_entity_effect( - network_id, + send_effect_to_client( Effect::$type.id() as u8, - effect.amplifier as i8, - effect.duration as i32, - 0x02, + network_id, + &effect, + client, + effect.duration as u64, ); } @@ -168,8 +214,8 @@ macro_rules! add_start_tick_to_effects { } } - for entity in entities { - add_particles(game, entity)?; + for (entity, effect_kind) in entities { + add_particles(game, entity, effect_kind)?; } Ok(()) diff --git a/quill/common/src/components_effects.rs b/quill/common/src/components_effects.rs index bf7c94d0a..caa112b33 100644 --- a/quill/common/src/components_effects.rs +++ b/quill/common/src/components_effects.rs @@ -10,15 +10,20 @@ pub struct EffectApplication { pub amplifier: u8, /// Tick-based duration of the effect. pub duration: u32, + /// Effect flags + pub flags: EffectFlags, + + /// Store when effect was added, if start_tick == 0 effect not yet sent to client + pub start_tick: u64, +} +#[derive(Copy, Clone, Debug, Hash, Serialize, PartialEq, Eq, Deserialize)] +pub struct EffectFlags { /// Whether spawn particles or not. pub particle: bool, /// Whether the effect was given by a beacon or not. pub ambient: bool, /// Show effect icon or not. pub icon: bool, - - /// Store when effect was added, if start_tick == 0 effect not yet sent to client - pub start_tick: u64, } impl EffectApplication {