From 67e57267cca26f5f3f59ca6f866d706519553e70 Mon Sep 17 00:00:00 2001 From: Vincent Date: Sat, 25 Jan 2025 00:14:47 +0000 Subject: [PATCH] refactor: `PlayerDeathEvent` --- src/bedrock/server/server_player.h | 1 - src/bedrock/symbol_generator/symbols.toml | 2 - .../world/level/player_death_manager.h | 23 ++++----- .../handlers/player_gameplay_handler.cpp | 43 +++++++++++++++- .../event/handlers/player_gameplay_handler.h | 1 + src/endstone/core/server.cpp | 5 +- .../runtime/bedrock_hooks/server_player.cpp | 50 ------------------- 7 files changed, 55 insertions(+), 70 deletions(-) diff --git a/src/bedrock/server/server_player.h b/src/bedrock/server/server_player.h index ab11746c5..84606b0cc 100644 --- a/src/bedrock/server/server_player.h +++ b/src/bedrock/server/server_player.h @@ -20,7 +20,6 @@ class ServerPlayer : public Player { public: ~ServerPlayer() override = 0; - ENDSTONE_HOOK void die(const ActorDamageSource &source) override; ENDSTONE_HOOK void disconnect(); ENDSTONE_HOOK void setLocalPlayerAsInitialized(); }; diff --git a/src/bedrock/symbol_generator/symbols.toml b/src/bedrock/symbol_generator/symbols.toml index 50a8ffd8f..a1b390377 100644 --- a/src/bedrock/symbol_generator/symbols.toml +++ b/src/bedrock/symbol_generator/symbols.toml @@ -86,7 +86,6 @@ version = "1.21.50" "?_createNewPlayer@ServerNetworkHandler@@AEAAAEAVServerPlayer@@AEBVNetworkIdentifier@@AEBVSubClientConnectionRequest@@W4SubClientId@@@Z" = 14291952 "?_isServerTextEnabled@ServerNetworkHandler@@AEBA_NAEBW4ServerTextEvent@@@Z" = 14311280 # ServerPlayer -"?die@ServerPlayer@@UEAAXAEBVActorDamageSource@@@Z" = 20376160 "?disconnect@ServerPlayer@@QEAAXXZ" = 20376320 "?setLocalPlayerAsInitialized@ServerPlayer@@QEAAXXZ" = 20449488 # ServerScoreboard @@ -182,7 +181,6 @@ version = "1.21.50" "_ZN20ServerNetworkHandler16_createNewPlayerERK17NetworkIdentifierRK26SubClientConnectionRequest11SubClientId" = 91671520 "_ZNK20ServerNetworkHandler20_isServerTextEnabledERK15ServerTextEvent" = 91584688 # ServerPlayer -"_ZN12ServerPlayer3dieERK17ActorDamageSource" = 103631264 "_ZN12ServerPlayer10disconnectEv" = 103650656 "_ZN12ServerPlayer27setLocalPlayerAsInitializedEv" = 103657312 # ServerScoreboard diff --git a/src/bedrock/world/level/player_death_manager.h b/src/bedrock/world/level/player_death_manager.h index 32eaca0a2..742edd7ae 100644 --- a/src/bedrock/world/level/player_death_manager.h +++ b/src/bedrock/world/level/player_death_manager.h @@ -16,6 +16,7 @@ #include "bedrock/core/utility/pub_sub/publisher.h" #include "bedrock/core/utility/pub_sub/thread_model.h" +#include "bedrock/forward.h" class IPlayerDeathManagerProxy : public Bedrock::EnableNonOwnerReferences { public: @@ -24,26 +25,20 @@ class IPlayerDeathManagerProxy : public Bedrock::EnableNonOwnerReferences { [[nodiscard]] virtual bool shouldShowDeathMessages() const = 0; }; -class PlayerDeathManagerBase { // TODO(fixme): figure out the name +class IPlayerDeathManagerConnector { public: - virtual ~PlayerDeathManagerBase() = default; + virtual ~IPlayerDeathManagerConnector() = default; + virtual Bedrock::PubSub::Connector &getOnPlayerDeathConnector() = 0; }; -class PlayerDeathManager - : public PlayerDeathManagerBase, - public Bedrock::PubSub::Publisher { +class PlayerDeathManager : public IPlayerDeathManagerConnector { public: - [[nodiscard]] gsl::not_null getPlayerDeathManagerProxy() const - { - return proxy_.get(); - } - - void resetPacketSender() // Endstone - { - sender_.reset(); - } + PlayerDeathManager(std::unique_ptr); private: + friend class endstone::core::EndstoneServer; + + Bedrock::PubSub::Publisher on_player_death_publisher_; std::unique_ptr proxy_; // +136 Bedrock::NonOwnerPointer sender_; // +144 }; diff --git a/src/endstone/core/event/handlers/player_gameplay_handler.cpp b/src/endstone/core/event/handlers/player_gameplay_handler.cpp index ece16d45a..7189ff671 100644 --- a/src/endstone/core/event/handlers/player_gameplay_handler.cpp +++ b/src/endstone/core/event/handlers/player_gameplay_handler.cpp @@ -15,6 +15,8 @@ #include "endstone/core/event/handlers/player_gameplay_handler.h" #include "bedrock/entity/components/replay_state_component.h" +#include "bedrock/locale/i18n.h" +#include "bedrock/network/packet/death_info_packet.h" #include "bedrock/network/packet/update_player_game_type_packet.h" #include "bedrock/world/actor/actor.h" #include "endstone/core/block/block.h" @@ -23,6 +25,7 @@ #include "endstone/core/json.h" #include "endstone/core/player.h" #include "endstone/core/server.h" +#include "endstone/event/player/player_death_event.h" #include "endstone/event/player/player_emote_event.h" #include "endstone/event/player/player_game_mode_change_event.h" #include "endstone/event/player/player_interact_actor_event.h" @@ -40,7 +43,8 @@ HandlerResult EndstonePlayerGameplayHandler::handleEvent(const PlayerGameplayEve { auto visitor = [&](auto &&arg) -> HandlerResult { using T = std::decay_t; - if constexpr (std::is_same_v> || + if constexpr (std::is_same_v> || + std::is_same_v> || std::is_same_v>) { if (!handleEvent(arg.value())) { return HandlerResult::BypassListeners; @@ -84,6 +88,43 @@ GameplayHandlerResult EndstonePlayerGameplayHandler::handleEv return std::visit(visitor, event.variant); } +bool EndstonePlayerGameplayHandler::handleEvent(const PlayerDamageEvent &event) +{ + if (auto *player = WeakEntityRef(event.player).tryUnwrap<::Player>(); player) { + auto &server = entt::locator::value(); + auto &endstone_player = player->getEndstoneActor(); + if (player->isAlive()) { + // TODO: PlayerHurtEvent + } + else { + // Close any open form on player death + endstone_player.closeForm(); + + // Fire player death event + auto death_cause_message = event.damage_source->getDeathMessage(player->getName(), player); + auto death_message = getI18n().get(death_cause_message.first, death_cause_message.second, nullptr); + 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(); + } + + // 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; + player->sendNetworkPacket(*packet); + + // Broadcast death message if not empty + if (!e->getDeathMessage().empty()) { + server.broadcastMessage(Translatable{death_cause_message.first, death_cause_message.second}); + } + } + } + return true; +} + bool EndstonePlayerGameplayHandler::handleEvent(const PlayerFormResponseEvent &event) { if (auto *player = WeakEntityRef(event.player).tryUnwrap<::Player>(); player) { diff --git a/src/endstone/core/event/handlers/player_gameplay_handler.h b/src/endstone/core/event/handlers/player_gameplay_handler.h index ca74c04d1..2c735f04e 100644 --- a/src/endstone/core/event/handlers/player_gameplay_handler.h +++ b/src/endstone/core/event/handlers/player_gameplay_handler.h @@ -28,6 +28,7 @@ class EndstonePlayerGameplayHandler final : public PlayerGameplayHandler { GameplayHandlerResult handleEvent(MutablePlayerGameplayEvent &event) override; private: + bool handleEvent(const PlayerDamageEvent &event); bool handleEvent(const PlayerFormResponseEvent &event); bool handleEvent(const PlayerFormCloseEvent &event); bool handleEvent(const PlayerRespawnEvent &event); diff --git a/src/endstone/core/server.cpp b/src/endstone/core/server.cpp index 56c2df3e0..3b4cc0d9a 100644 --- a/src/endstone/core/server.cpp +++ b/src/endstone/core/server.cpp @@ -97,11 +97,12 @@ void EndstoneServer::setLevel(::Level &level) level_ = std::make_unique(level); scoreboard_ = std::make_unique(level.getScoreboard()); command_map_ = std::make_unique(*this); + loadResourcePacks(); + registerGameplayHandlers(); + level._getPlayerDeathManager()->sender_.reset(); // prevent BDS from sending the death message enablePlugins(PluginLoadOrder::PostWorld); ServerLoadEvent event{ServerLoadEvent::LoadType::Startup}; getPluginManager().callEvent(event); - loadResourcePacks(); - registerGameplayHandlers(); } void EndstoneServer::setResourcePackRepository(Bedrock::NotNullNonOwnerPtr repo) diff --git a/src/endstone/runtime/bedrock_hooks/server_player.cpp b/src/endstone/runtime/bedrock_hooks/server_player.cpp index 3b721e5d9..2b213f411 100644 --- a/src/endstone/runtime/bedrock_hooks/server_player.cpp +++ b/src/endstone/runtime/bedrock_hooks/server_player.cpp @@ -17,13 +17,9 @@ #include #include "bedrock/locale/i18n.h" -#include "bedrock/network/packet.h" -#include "bedrock/network/packet/death_info_packet.h" -#include "bedrock/world/level/level.h" #include "endstone/color_format.h" #include "endstone/core/message.h" #include "endstone/core/server.h" -#include "endstone/event/player/player_death_event.h" #include "endstone/event/player/player_join_event.h" #include "endstone/event/player/player_quit_event.h" #include "endstone/lang/translatable.h" @@ -32,52 +28,6 @@ using endstone::core::EndstonePlayer; using endstone::core::EndstoneServer; -void ServerPlayer::die(const ActorDamageSource &source) -{ - // 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(&ServerPlayer::die, this, source); - - auto &server = entt::locator::value(); - auto &endstone_player = getEndstoneActor(); - endstone_player.closeForm(); - - // 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); - - // 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(); - } - - // 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()); - - // Broadcast death messages - if (player_death_manager->getPlayerDeathManagerProxy()->shouldShowDeathMessages()) { - endstone::Translatable tr{death_cause_message.first, death_cause_message.second}; - for (const auto &player : server.getOnlinePlayers()) { - player->sendMessage(tr); - } - } -} - void ServerPlayer::setLocalPlayerAsInitialized() { ENDSTONE_HOOK_CALL_ORIGINAL(&ServerPlayer::setLocalPlayerAsInitialized, this);