diff --git a/manifest.json b/manifest.json index f61c8b3..e255301 100644 --- a/manifest.json +++ b/manifest.json @@ -8,7 +8,9 @@ "dependencies": [ { "name": "GMLIB" - }, + } + ], + "optionalDependencies": [ { "name": "ModAPI" } diff --git a/src/DeathMessage.cpp b/src/DeathMessage.cpp index 0ddbcf8..db9540a 100644 --- a/src/DeathMessage.cpp +++ b/src/DeathMessage.cpp @@ -1,120 +1,34 @@ #include "Entry.h" -#include "ll/api/event/player/PlayerConnectEvent.h" -#include "ll/api/event/player/PlayerDisconnectEvent.h" #include "ll/api/memory/Hook.h" #include "ll/api/chrono/GameChrono.h" -#include "ll/api/thread/ThreadPoolExecutor.h" -#include #include "mc/world/actor/Mob.h" #include "mc/world/actor/ActorDamageSource.h" #include "mc/world/actor/ActorDamageByActorSource.h" -#include "mc/world/actor/ActorDamageByBlockSource.h" -#include "mc/world/actor/ActorDamageByChildActorSource.h" + #include "ll/api/service/Bedrock.h" #include "mc/world/level/Level.h" -#include "mc/world/actor/monster/EnderCrystal.h" -#include "mc/world/actor/ActorHurtResult.h" + #include "mc/world/actor/Actor.h" phmap::flat_hash_map mFallHeightMap; phmap::flat_hash_map mHurtByEntityMap; - -LL_AUTO_TYPE_INSTANCE_HOOK( - MobDieHook, - HookPriority::Normal, - Mob, - &Mob::$die, - void, - ActorDamageSource const& ads -) { - auto uid = getOrCreateUniqueID(); - - if (ads.mCause == SharedTypes::Legacy::ActorDamageCause::Fall) { - mFallHeightMap[uid.rawID] = getFallDistance(); - logger->info("跌落高度为 {}", getFallDistance()); - } - if (hasCategory(ActorCategory::Player) || isTame()) { - if (mLastHurtByMobTime != 0) { - auto hm = getLastHurtByMob(); - mHurtByEntityMap[uid.rawID] = hm->getOrCreateUniqueID(); - if (!getLastHurtByMob()->getNameTag().empty()) logger->info("伤害来源为 {}", getLastHurtByMob()->getNameTag()); - logger->info("伤害来源为 {}", getLastHurtByMob()->getTypeName()); - - - if (!getLastHurtByMob()->getCarriedItem().getCustomName().empty()) logger->info("伤害来源为 {}", getLastHurtByMob()->getCarriedItem().getCustomName()); - } - } - origin(ads); - - auto& executor = ll::thread::ThreadPoolExecutor::getDefault(); - auto callback = executor.executeAfter( - [] { - mHurtByEntityMap.clear(); - mFallHeightMap.clear(); - }, - ll::chrono::game::ticks(1) - ); -} -using DeathMessageResult = std::pair>; - -int mMaxCauseId = 34; std::vector> mCustomCauseMap; phmap::flat_hash_map mVanillaCauseMessage; phmap::flat_hash_map> mHardCodedDeathMessage; - bool isCrystal = false; -bool isCustomDefinition(int cause) { - if (cause >= 35) { - return true; - } - if (mVanillaCauseMessage.count(cause)) { - return true; - } - return false; -} - -bool isCustomCauseExist(std::string const& causeName) { - for (auto& key : mCustomCauseMap) { - if (key.first == causeName) { - return true; - } - } - return false; -} - -bool isCustomCauseExist(int cause) { - for (auto& key : mCustomCauseMap) { - if (key.second == cause) { - return true; - } - } - return false; -} - -SharedTypes::Legacy::ActorDamageCause getCustomCause(std::string const& causeName) { - for (auto& key : mCustomCauseMap) { - if (key.first == causeName) { - return (SharedTypes::Legacy::ActorDamageCause)key.second; - } - } - return SharedTypes::Legacy::ActorDamageCause::None; -} - -std::string getCustomCause(SharedTypes::Legacy::ActorDamageCause cause) { - for (auto& key : mCustomCauseMap) { - if ((SharedTypes::Legacy::ActorDamageCause)key.second == cause) { - return key.first; - } - } - return "none"; -} - std::string getCause(SharedTypes::Legacy::ActorDamageCause cause) { auto id = (int)cause; - if (isCustomCauseExist(id)) { - return getCustomCause(cause); + for (auto& key : mCustomCauseMap) { + if (key.second == id) { + for (auto& key1 : mCustomCauseMap) { + if ((SharedTypes::Legacy::ActorDamageCause)key1.second == cause) { + return key1.first; + } + } + return "none"; + } } if (mVanillaCauseMessage.count(id)) { return std::string(mVanillaCauseMessage[id]); @@ -151,7 +65,7 @@ DeathMessageResult makeDeathMessage( bool isHardCodedMessage = false ) { // 自定义类型构造 - if (isCustomDefinition(cause) || isHardCodedMessage) { + if (cause >= 35||mVanillaCauseMessage.count(cause) || isHardCodedMessage) { std::string msg = "death.attack.damageCause.item"; ll::utils::string_utils::replaceAll(msg, "damageCause", getCause(SharedTypes::Legacy::ActorDamageCause(cause))); if (isHardCodedMessage) { @@ -186,6 +100,10 @@ DeathMessageResult makeDeathMessage( // 如果没有击杀者和试图逃离,那么不可能使用 .player 结尾 ll::utils::string_utils::replaceAll(res.first, ".player", ""); } + if (!DeathMessages::Entry::getInstance().isResourceI18nLoaded) { + res.first=tr(res.first, res.second); + res.second = {}; + } return res; } // 未定义返回vanilla值 @@ -328,116 +246,6 @@ DeathMessageResult translateDeathMessage(DeathMessageResult origin, std::string return makeDeathMessage((int)damageCause, origin, name, killer, weaponName, isEscaping); } -LL_AUTO_TYPE_INSTANCE_HOOK( - CryatslHurtHook, - HookPriority::Normal, - EnderCrystal, - &EnderCrystal::$_hurt, - ::ActorHurtResult, - ActorDamageSource const& a1, - float a2, - bool a3, - bool a4 -) { - auto res = origin(a1, a2, a3, a4); - isCrystal = true; - auto& executor = ll::thread::ThreadPoolExecutor::getDefault(); - auto callback = executor.executeAfter( - [] { - isCrystal = false; - }, - ll::chrono::game::ticks(1) - ); - return res; -} - - -LL_AUTO_TYPE_INSTANCE_HOOK( - DeathMessageHook1, - HookPriority::Lowest, - ActorDamageSource, - &ActorDamageSource::$getDeathMessage, - - - DeathMessageResult, - - std::string a1, - Actor* a2 -) { - - - - auto originl = origin(a1, a2); - logger->info("原消息name为 {}", a1); - logger->info("原消息为 {}", originl.first); - auto res = translateDeathMessage(originl, a1, a2, this); - return res; -} - -LL_AUTO_TYPE_INSTANCE_HOOK( - DeathMessageHook2, - HookPriority::Lowest, - ActorDamageByActorSource, - &ActorDamageByActorSource::$getDeathMessage, - DeathMessageResult, - std::string a1, - Actor* a2 -) { - auto originl = origin(a1, a2); - logger->info("原消息name为 {}", a1); - logger->info("原消息为 {}", originl.first); - auto res = translateDeathMessage(originl, a1, a2, this); - return res; -} - -LL_AUTO_TYPE_INSTANCE_HOOK( - DeathMessageHook3, - HookPriority::Lowest, - ActorDamageByBlockSource, - &ActorDamageByBlockSource::$getDeathMessage, - - DeathMessageResult, - std::string a1, - Actor* a2 -) { - auto originl = origin(a1, a2); - logger->info("原消息name为 {}", a1); - logger->info("原消息为 {}", originl.first); - auto res = translateDeathMessage(originl, a1, a2, this); - return res; -} - -LL_AUTO_TYPE_INSTANCE_HOOK( - DeathMessageHook4, - HookPriority::Lowest, - ActorDamageByChildActorSource, - &ActorDamageByChildActorSource::$getDeathMessage, - - - DeathMessageResult, - std::string a1, - Actor* a2 -) { - auto originl = origin(a1, a2); - logger->info("原消息name为 {}", a1); - logger->info("原消息为 {}", originl.first); - auto res = translateDeathMessage(originl, a1, a2, this); - return res; -} -int getNextCauseId() { - mMaxCauseId++; - return mMaxCauseId; -} - -bool registerDamageCause(std::string const& causeName) { - if (!isCustomCauseExist(causeName)) { - auto id = getNextCauseId(); - mCustomCauseMap.push_back({causeName, id}); - return true; - } - return false; -} - bool setVanillaCauseMessage(SharedTypes::Legacy::ActorDamageCause cause, std::string_view msg) { auto id = (int)cause; if (!mVanillaCauseMessage.count(id)) { @@ -566,33 +374,3 @@ void RegisterDamageDefinition() { "death.attack.player.item" ); } -/* -void ListenEvents() { - auto& config = DeathMessages::Entry::getInstance().getConfig(); - auto& eventBus = ll::event::EventBus::getInstance(); - if (config.ConsoleLog.JoinMessage) { - eventBus.emplaceListener([](ll::event::player::PlayerConnectEvent& ev) { - infoLogger->info(fmt::format(fg(fmt::color::yellow), tr("multiplayer.player.joined", {ev.self().getRealName()})) - ); - }); - } - if (config.ConsoleLog.LeftMessage) { - eventBus.emplaceListener( - [](ll::event::player::PlayerDisconnectEvent& ev) { - infoLogger->info( - fmt::format(fg(fmt::color::yellow), tr("multiplayer.player.left", {ev.self().getRealName()})) - ); - } - ); - } - if (config.ConsoleLog.DeathMessage) { - eventBus.emplaceListener( - [](GMLIB::Event::EntityEvent::DeathMessageAfterEvent& ev) { - auto msg = ev.getDeathMessage(); - auto info = tr(msg.first, msg.second); - deathLogger->info(info); - ev.getDeathMessage() = {info, msg.second}; - } - ); - } -}*/ \ No newline at end of file diff --git a/src/Entry.cpp b/src/Entry.cpp index f5bba7b..b1fb611 100644 --- a/src/Entry.cpp +++ b/src/Entry.cpp @@ -11,6 +11,8 @@ #include "ll/api/data/IndirectValue.h" #include "gmlib/gm/i18n/LangI18n.h" #include "gmlib/gm/i18n/ResourceI18n.h" +#include "ll/api/mod/ModManagerRegistry.h" +#include ll::io::LoggerRegistry& loggerRegistry = ll::io::LoggerRegistry::getInstance(); std::shared_ptr logger = loggerRegistry.getOrCreate(MOD_NAME); @@ -26,27 +28,32 @@ Entry& Entry::getInstance() { } bool Entry::load() { + isResourceI18nLoaded=false; + mConfig.emplace(); if (!ll::config::loadConfig(*mConfig, getSelf().getConfigDir() / u8"config.json")) { ll::config::saveConfig(*mConfig, getSelf().getConfigDir() / u8"config.json"); } - if (getConfig().ServerSideTranslation.Enabled) { - loadI18n(); - } else { - loadResourcePack(); - } + if (getConfig().FileLog.Enabled) { auto& path = getConfig().FileLog.Path; ll::Polymorphic polymorphicFormatter = ll::makePolymorphic("[{3:.3%F %T.} {2}][{1}] {0}", false); deathLogger->addSink(std::make_shared(path, polymorphicFormatter)); } + loadI18n(); + if ((!getConfig().ServerSideTranslation.Enabled)&&ll::mod::ModManagerRegistry::getInstance().hasMod("ModAPI")) { + loadResourcePack(); + } return true; } bool Entry::enable() { - RegisterDamageDefinition(); - /* ListenEvents();*/ + if (!ll::mod::ModManagerRegistry::getInstance().hasMod("ModAPI")) { + getSelf().getLogger().warn(tr("plugin.deathmessages.modapi.warn")); + } + RegisterDamageDefinition(); + ListenEvents(); return true; } @@ -55,13 +62,16 @@ bool Entry::disable() { return true; } bool Entry::unload() { mConfig.reset(); mI18n.reset(); + uninstallHooks(); // getInstance().reset(); return true; } Config& Entry::getConfig() { return mConfig.value(); } -std::unique_ptr Entry::getI18n() { return std::move(mI18n); } +gmlib::i18n::LangI18n& Entry::getI18n() { + return *mI18n; +} void Entry::loadI18n() { mI18n= std::make_unique(getSelf().getLangDir(), getConfig().ServerSideTranslation.Language); @@ -71,24 +81,29 @@ void Entry::loadI18n() { mI18n->setDefaultLanguage("zh_CN"); } + + void Entry::loadResourcePack() { - auto resource = gmlib::i18n::ResourceI18n{getSelf().getModDir() / u8"lang", MOD_NAME, 0, 8, 0}; - resource.addLanguage("en_US", en_US); - resource.addLanguage("zh_CN", zh_CN); - resource.loadAllLanguages(); + auto mResource = std::make_unique(getSelf().getModDir() / u8"lang", MOD_NAME, 0, 16, 0); + mResource->addLanguage("en_US", en_US); + mResource->addLanguage("zh_CN", zh_CN); + mResource->loadAllLanguages(); + isResourceI18nLoaded=true; } + + + + } // namespace DeathMessages LL_REGISTER_MOD(DeathMessages::Entry, DeathMessages::Entry::getInstance()); -/*std::string tr(std::string const& key, std::vector const& params) { - if (auto i18n = DeathMessages::Entry::getInstance().getI18n()) { - return i18n->get(key, params); - } - auto i18n = DeathMessages::Entry::getI18n(); - - return i18n->get(key, params); +std::string tr(std::string const& key, std::vector const& params) { + auto& i18n = DeathMessages::Entry::getInstance().getI18n(); + return i18n.get(key, params); -}*/ \ No newline at end of file + return key; +} + diff --git a/src/Entry.h b/src/Entry.h index 73cf400..57cbbfa 100644 --- a/src/Entry.h +++ b/src/Entry.h @@ -32,12 +32,14 @@ public: Config& getConfig(); - std::unique_ptr getI18n(); + gmlib::i18n::LangI18n& getI18n(); void loadI18n(); void loadResourcePack(); + + bool isResourceI18nLoaded; private: ll::mod::NativeMod& mSelf; std::optional mConfig; diff --git a/src/Global.h b/src/Global.h index 8e296f6..bb87552 100644 --- a/src/Global.h +++ b/src/Global.h @@ -1,15 +1,26 @@ #pragma once #include +#include +#include +#include +#include "ll/api/i18n/I18n.h" + #define MOD_NAME "DeathMessages" extern std::shared_ptr logger; extern std::shared_ptr deathLogger; extern std::shared_ptr infoLogger; +extern phmap::flat_hash_map mFallHeightMap; +extern phmap::flat_hash_map mHurtByEntityMap; +extern bool isCrystal; +using DeathMessageResult = std::pair>; +using namespace ll::i18n_literals; - +extern DeathMessageResult translateDeathMessage(DeathMessageResult origin, std::string name, Actor* dead, ActorDamageSource* ads); +extern void uninstallHooks(); extern void RegisterDamageDefinition(); -/* - *extern std::string tr(std::string const& key, std::vector const& data = {}); - *extern void ListenEvents();*/ \ No newline at end of file + +extern std::string tr(std::string const& key, std::vector const& params = {}); +extern void ListenEvents(); \ No newline at end of file diff --git a/src/Hooks.cpp b/src/Hooks.cpp new file mode 100644 index 0000000..d660ac2 --- /dev/null +++ b/src/Hooks.cpp @@ -0,0 +1,161 @@ +#include "Entry.h" +#include "ll/api/memory/Hook.h" +#include "ll/api/chrono/GameChrono.h" +#include "ll/api/thread/ThreadPoolExecutor.h" +#include +#include "mc/world/actor/Mob.h" +#include "mc/world/actor/ActorDamageSource.h" +#include "mc/world/actor/ActorDamageByActorSource.h" +#include "mc/world/actor/ActorDamageByBlockSource.h" +#include "mc/world/actor/ActorDamageByChildActorSource.h" +#include "mc/world/actor/monster/EnderCrystal.h" +#include "mc/world/actor/ActorHurtResult.h" +#include "Global.h" +#include "mc/world/actor/Actor.h" + +#include "Hooks.h" + + + +LL_AUTO_TYPE_INSTANCE_HOOK( + MobDieHook, + HookPriority::Normal, + Mob, + &Mob::$die, + void, + ActorDamageSource const& ads +) { + auto uid = getOrCreateUniqueID(); + + if (ads.mCause == SharedTypes::Legacy::ActorDamageCause::Fall) { + mFallHeightMap[uid.rawID] = getFallDistance(); + logger->info("跌落高度为 {}", getFallDistance()); + } + if (hasCategory(ActorCategory::Player) || isTame()) { + if (mLastHurtByMobTime != 0) { + auto hm = getLastHurtByMob(); + mHurtByEntityMap[uid.rawID] = hm->getOrCreateUniqueID(); + if (!getLastHurtByMob()->getNameTag().empty()) logger->info("伤害来源为 {}", getLastHurtByMob()->getNameTag()); + logger->info("伤害来源为 {}", getLastHurtByMob()->getTypeName()); + + + if (!getLastHurtByMob()->getCarriedItem().getCustomName().empty()) logger->info("伤害来源为 {}", getLastHurtByMob()->getCarriedItem().getCustomName()); + } + } + origin(ads); + + auto& executor = ll::thread::ThreadPoolExecutor::getDefault(); + auto callback = executor.executeAfter( + [] { + mHurtByEntityMap.clear(); + mFallHeightMap.clear(); + }, + ll::chrono::game::ticks(1) + ); +} + +LL_AUTO_TYPE_INSTANCE_HOOK( + CryatslHurtHook, + HookPriority::Normal, + EnderCrystal, + &EnderCrystal::$_hurt, + ::ActorHurtResult, + ActorDamageSource const& a1, + float a2, + bool a3, + bool a4 +) { + auto res = origin(a1, a2, a3, a4); + isCrystal = true; + auto& executor = ll::thread::ThreadPoolExecutor::getDefault(); + auto callback = executor.executeAfter( + [] { + isCrystal = false; + }, + ll::chrono::game::ticks(1) + ); + return res; +} + + +LL_AUTO_TYPE_INSTANCE_HOOK( + ActorDamageHook, + HookPriority::Lowest, + ActorDamageSource, + &ActorDamageSource::$getDeathMessage, + + + DeathMessageResult, + + std::string a1, + Actor* a2 +) { + + + + auto originl = origin(a1, a2); + logger->info("原消息name为 {}", a1); + logger->info("原消息为 {}", originl.first); + auto res = translateDeathMessage(originl, a1, a2, this); + return res; +} + +LL_AUTO_TYPE_INSTANCE_HOOK( + ActorDamageByActorHook, + HookPriority::Lowest, + ActorDamageByActorSource, + &ActorDamageByActorSource::$getDeathMessage, + DeathMessageResult, + std::string a1, + Actor* a2 +) { + auto originl = origin(a1, a2); + logger->info("原消息name为 {}", a1); + logger->info("原消息为 {}", originl.first); + auto res = translateDeathMessage(originl, a1, a2, this); + return res; +} + +LL_AUTO_TYPE_INSTANCE_HOOK( + ActorDamageByBlockHook, + HookPriority::Lowest, + ActorDamageByBlockSource, + &ActorDamageByBlockSource::$getDeathMessage, + + DeathMessageResult, + std::string a1, + Actor* a2 +) { + auto originl = origin(a1, a2); + logger->info("原消息name为 {}", a1); + logger->info("原消息为 {}", originl.first); + auto res = translateDeathMessage(originl, a1, a2, this); + return res; +} + +LL_AUTO_TYPE_INSTANCE_HOOK( + ActorDamageByChildHook, + HookPriority::Lowest, + ActorDamageByChildActorSource, + &ActorDamageByChildActorSource::$getDeathMessage, + + + DeathMessageResult, + std::string a1, + Actor* a2 +) { + auto originl = origin(a1, a2); + logger->info("原消息name为 {}", a1); + logger->info("原消息为 {}", originl.first); + auto res = translateDeathMessage(originl, a1, a2, this); + return res; +} + +void uninstallHooks() { + MobDieHook::unhook(); + CryatslHurtHook::unhook(); + ActorDamageHook::unhook(); + ActorDamageByActorHook::unhook(); + ActorDamageByBlockHook::unhook(); + ActorDamageByChildHook::unhook(); +} diff --git a/src/Hooks.h b/src/Hooks.h new file mode 100644 index 0000000..1bc69e1 --- /dev/null +++ b/src/Hooks.h @@ -0,0 +1,17 @@ +#pragma once + + +namespace DeathMessages { + + + + +/** + * @brief Removes all installed memory hooks. + * + * This function uninstalls all previously installed hooks to restore + * original behavior. + */ +void uninstallHooks(); + +} // namespace DeathMessages diff --git a/src/Language.h b/src/Language.h index 2b77685..6070524 100644 --- a/src/Language.h +++ b/src/Language.h @@ -250,6 +250,7 @@ entity.breeze.name=Breeze entity.creaking.name=Creaking multiplayer.player.joined=%s joined the game multiplayer.player.left=%s left the game +plugin.deathmessages.modapi.warn=The plugin ModAPI is not loaded, so client-side translation is unavailable. Only server-side translation can be used. )"; std::string zh_CN = R"( @@ -503,4 +504,5 @@ entity.breeze.name=旋风人 entity.creaking.name=嘎枝 multiplayer.player.joined=%s 加入了游戏 multiplayer.player.left=%s 退出了游戏 +plugin.deathmessages.modapi.warn=插件ModAPI没有被加载,所以客户端侧翻译无法使用,正在启用服务端侧翻译 )"; \ No newline at end of file diff --git a/src/Log.cpp b/src/Log.cpp new file mode 100644 index 0000000..31c992a --- /dev/null +++ b/src/Log.cpp @@ -0,0 +1,39 @@ + +#include "Entry.h" +#include "Global.h" + +#include "ll/api/event/player/PlayerConnectEvent.h" +#include "ll/api/event/EventBus.h" +#include "ll/api/event/player/PlayerDieEvent.h" + +#include "ll/api/event/player/PlayerDisconnectEvent.h" +void ListenEvents() { + auto& config = DeathMessages::Entry::getInstance().getConfig(); + auto& eventBus = ll::event::EventBus::getInstance(); + if (config.ConsoleLog.JoinMessage) { + eventBus.emplaceListener([](ll::event::player::PlayerConnectEvent& ev) { + infoLogger->info( + fmt::format(fg(fmt::color::yellow), fmt::runtime(tr("multiplayer.player.joined", {ev.self().getRealName()}))) + ); + + }); + } + if (config.ConsoleLog.LeftMessage) { + eventBus.emplaceListener( + [](ll::event::player::PlayerDisconnectEvent& ev) { + infoLogger->info( + fmt::format(fg(fmt::color::yellow), fmt::runtime(tr("multiplayer.player.left", {ev.self().getRealName()}))) + ); + } + ); + } + if (config.ConsoleLog.DeathMessage) { + eventBus.emplaceListener( + [](ll::event::player::PlayerDieEvent& ev) { + auto [key, params] = ev.source().getDeathMessage(ev.self().getRealName(), &ev.self()); + auto info = tr(key, params); + deathLogger->info(info); + } + ); + } +} \ No newline at end of file