From 701b522c948a923024efa105ebc35db78f80d8d3 Mon Sep 17 00:00:00 2001 From: Vincent Date: Sat, 7 Sep 2024 23:19:08 +0100 Subject: [PATCH] refactor: improve handling of custom death message --- .../network/packet/death_info_packet.h | 25 +++ .../actor_damage_source_wrapper.h | 145 ------------------ include/bedrock/world/level/level_interface.h | 3 +- .../world/level/player_death_manager.h | 27 +++- .../bedrock/server/server_player.cpp | 47 ++++-- 5 files changed, 83 insertions(+), 164 deletions(-) create mode 100644 include/bedrock/network/packet/death_info_packet.h delete mode 100644 include/bedrock/world/damagesource/actor_damage_source_wrapper.h diff --git a/include/bedrock/network/packet/death_info_packet.h b/include/bedrock/network/packet/death_info_packet.h new file mode 100644 index 000000000..1a2147435 --- /dev/null +++ b/include/bedrock/network/packet/death_info_packet.h @@ -0,0 +1,25 @@ +// Copyright (c) 2024, The Endstone Project. (https://endstone.dev) All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include + +#include "bedrock/network/packet.h" + +class DeathInfoPacket : public Packet { +public: + using DeathCauseMessageType = std::pair>; + DeathCauseMessageType death_cause_message; +}; diff --git a/include/bedrock/world/damagesource/actor_damage_source_wrapper.h b/include/bedrock/world/damagesource/actor_damage_source_wrapper.h deleted file mode 100644 index c88d653eb..000000000 --- a/include/bedrock/world/damagesource/actor_damage_source_wrapper.h +++ /dev/null @@ -1,145 +0,0 @@ -// Copyright (c) 2024, The Endstone Project. (https://endstone.dev) All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#pragma once - -#include -#include - -#include "bedrock/world/damagesource/actor_damage_source.h" - -namespace endstone::detail { - -class ActorDamageSourceWrapper final : public ActorDamageSource { -public: - ActorDamageSourceWrapper(const ActorDamageSource &source, std::string message, std::vector params) - : source_(source), message_(std::move(message)), params_(std::move(params)) - { - } - - ~ActorDamageSourceWrapper() override = default; - - [[nodiscard]] bool isEntitySource() const override - { - return source_.isEntitySource(); - } - - [[nodiscard]] bool isChildEntitySource() const override - { - return source_.isChildEntitySource(); - } - - [[nodiscard]] bool isBlockSource() const override - { - return source_.isBlockSource(); - } - - [[nodiscard]] bool isFire() const override - { - return source_.isFire(); - } - - [[nodiscard]] bool isReducedByResistanceEffect() const override - { - return source_.isReducedByResistanceEffect(); - } - - [[nodiscard]] bool isReducedByEnchantReduction() const override - { - return source_.isReducedByEnchantReduction(); - } - - [[nodiscard]] bool isReducedByArmorReduction() const override - { - return source_.isReducedByArmorReduction(); - } - - [[nodiscard]] bool isFallingBlockDamage() const override - { - return source_.isFallingBlockDamage(); - } - - [[nodiscard]] bool isFallDamage() const override - { - return source_.isFallDamage(); - } - - [[nodiscard]] std::pair> getDeathMessage(std::string, - ::Actor *actor) const override - { - return std::make_pair(message_, params_); - } - - [[nodiscard]] bool getIsCreative() const override - { - return source_.getIsCreative(); - } - - [[nodiscard]] bool getIsWorldBuilder() const override - { - return source_.getIsWorldBuilder(); - } - - [[nodiscard]] ActorUniqueID getEntityUniqueID() const override - { - return source_.getEntityUniqueID(); - } - - [[nodiscard]] ActorType getEntityType() const override - { - return source_.getEntityType(); - } - - [[nodiscard]] ActorCategory getEntityCategories() const override - { - return source_.getEntityCategories(); - } - - [[nodiscard]] bool getDamagingEntityIsCreative() const override - { - return source_.getDamagingEntityIsCreative(); - } - - [[nodiscard]] bool getDamagingEntityIsWorldBuilder() const override - { - return source_.getDamagingEntityIsWorldBuilder(); - } - - [[nodiscard]] ActorUniqueID getDamagingEntityUniqueID() const override - { - return source_.getDamagingEntityUniqueID(); - } - - [[nodiscard]] ActorType getDamagingEntityType() const override - { - return source_.getDamagingEntityType(); - } - - [[nodiscard]] ActorCategory getDamagingEntityCategories() const override - { - return source_.getDamagingEntityCategories(); - } - - [[nodiscard]] std::unique_ptr clone() const override - { - return std::make_unique(source_, message_, params_); - } - -private: - const ActorDamageSource &source_; - std::string message_; - std::vector params_; -}; - -} // namespace endstone::detail diff --git a/include/bedrock/world/level/level_interface.h b/include/bedrock/world/level/level_interface.h index dff2cfd0d..c635f8efb 100644 --- a/include/bedrock/world/level/level_interface.h +++ b/include/bedrock/world/level/level_interface.h @@ -437,8 +437,9 @@ class ILevel : public Bedrock::EnableNonOwnerReferences { [[nodiscard]] virtual std::weak_ptr getBlockRegistry() const = 0; virtual void pauseAndFlushTaskGroups() = 0; + virtual PlayerDeathManager *_getPlayerDeathManager() = 0; // Endstone: private -> public + private: - virtual PlayerDeathManager *_getPlayerDeathManager() = 0; virtual MapDataManager &_getMapDataManager() = 0; virtual void *getArmorTrimUnloader() = 0; [[nodiscard]] virtual void *getPlayerSleepManager() const = 0; diff --git a/include/bedrock/world/level/player_death_manager.h b/include/bedrock/world/level/player_death_manager.h index 55668557f..2157d876e 100644 --- a/include/bedrock/world/level/player_death_manager.h +++ b/include/bedrock/world/level/player_death_manager.h @@ -16,17 +16,34 @@ #include "bedrock/core/pub_sub.h" -class IPlayerDeathManagerProxy; +class IPlayerDeathManagerProxy : public Bedrock::EnableNonOwnerReferences { +public: + ~IPlayerDeathManagerProxy() override = 0; + [[nodiscard]] virtual Actor *fetchActor(ActorUniqueID) const = 0; + [[nodiscard]] virtual bool shouldShowDeathMessages() const = 0; +}; + +class PlayerDeathManagerBase { // TODO(fixme): figure out the name +public: + virtual ~PlayerDeathManagerBase() = default; +}; -class PlayerDeathManager : Bedrock::PubSub::Publisher { +class PlayerDeathManager + : public PlayerDeathManagerBase, + public Bedrock::PubSub::Publisher { public: + [[nodiscard]] gsl::not_null getPlayerDeathManagerProxy() const + { + return proxy_.get(); + } + void resetPacketSender() // Endstone { sender_.reset(); } private: - std::unique_ptr proxy_; // +128 - Bedrock::NonOwnerPointer sender_; // +136 + std::unique_ptr proxy_; // +136 + Bedrock::NonOwnerPointer sender_; // +144 }; -BEDROCK_STATIC_ASSERT_SIZE(PlayerDeathManager, 152, 112); +BEDROCK_STATIC_ASSERT_SIZE(PlayerDeathManager, 160, 120); diff --git a/src/endstone_runtime/bedrock/server/server_player.cpp b/src/endstone_runtime/bedrock/server/server_player.cpp index 3dfd0fda4..e6db7d8e1 100644 --- a/src/endstone_runtime/bedrock/server/server_player.cpp +++ b/src/endstone_runtime/bedrock/server/server_player.cpp @@ -16,10 +16,10 @@ #include -#include - #include "bedrock/locale/i18n.h" -#include "bedrock/world/damagesource/actor_damage_source_wrapper.h" +#include "bedrock/network/minecraft_packets.h" +#include "bedrock/network/packet/death_info_packet.h" +#include "bedrock/world/level/level.h" #include "endstone/detail/hook.h" #include "endstone/detail/server.h" #include "endstone/event/player/player_death_event.h" @@ -30,27 +30,48 @@ using endstone::detail::EndstoneServer; void ServerPlayer::die(const ActorDamageSource &source) { - auto death_cause_message = source.getDeathMessage(getName(), this); - auto death_message = getI18n().get(death_cause_message.first, death_cause_message.second, nullptr); + // Note: reset the packet sender in PlayerDeathManager to prevent BDS + // from sending the death message as we will take over it + auto *player_death_manager = getLevel()._getPlayerDeathManager(); + player_death_manager->resetPacketSender(); + + ENDSTONE_HOOK_CALL_ORIGINAL_NAME(&ServerPlayer::die, __FUNCDNAME__, this, source); auto &server = entt::locator::value(); endstone::Player &endstone_player = getEndstonePlayer(); endstone_player.closeForm(); - auto e = std::make_unique(endstone_player, death_message); - server.getPluginManager().callEvent(*static_cast(e.get())); + // Do a server side translation for logging + auto death_cause_message = source.getDeathMessage(getName(), this); + auto death_message = getI18n().get(death_cause_message.first, death_cause_message.second, nullptr); - if (!e->getDeathMessage().empty()) { - server.getLogger().info(e->getDeathMessage()); + // Fire player death event + const auto e = std::make_unique(endstone_player, death_message); + server.getPluginManager().callEvent(*static_cast(e.get())); + if (e->getDeathMessage() != death_message) { + death_cause_message.first = e->getDeathMessage(); + death_cause_message.second.clear(); } - if (e->getDeathMessage() != death_message) { - auto new_source = endstone::detail::ActorDamageSourceWrapper(source, e->getDeathMessage(), {}); - ENDSTONE_HOOK_CALL_ORIGINAL_NAME(&ServerPlayer::die, __FUNCDNAME__, this, new_source); + // Send death info + const auto packet = MinecraftPackets::createPacket(MinecraftPacketIds::DeathInfo); + const auto pk = std::static_pointer_cast(packet); + pk->death_cause_message = death_cause_message; + sendNetworkPacket(*packet); + + // Log death message to console if not empty + if (e->getDeathMessage().empty()) { return; } + server.getLogger().info(e->getDeathMessage()); - ENDSTONE_HOOK_CALL_ORIGINAL_NAME(&ServerPlayer::die, __FUNCDNAME__, this, source); + // Broadcast death messages + if (!player_death_manager->getPlayerDeathManagerProxy()->shouldShowDeathMessages()) { + return; + } + for (const auto &player : server.getOnlinePlayers()) { + player->sendMessage({death_cause_message.first, death_cause_message.second}); + } } void ServerPlayer::setLocalPlayerAsInitialized()