Merge pull request #4 from wed150/main

Adapt to Levilamina 26.10.5
This commit is contained in:
wed150
2026-06-21 19:47:55 +08:00
committed by GitHub
Unverified
17 changed files with 839 additions and 335 deletions
+1 -2
View File
@@ -1,4 +1,3 @@
# Generated from CLion Inspection settings
--- ---
Checks: '-*, Checks: '-*,
bugprone-argument-comment, bugprone-argument-comment,
@@ -123,7 +122,6 @@ portability-simd-intrinsics,
readability-avoid-const-params-in-decls, readability-avoid-const-params-in-decls,
readability-const-return-type, readability-const-return-type,
readability-container-size-empty, readability-container-size-empty,
readability-convert-member-functions-to-static,
readability-delete-null-pointer, readability-delete-null-pointer,
readability-deleted-default, readability-deleted-default,
readability-inconsistent-declaration-parameter-name, readability-inconsistent-declaration-parameter-name,
@@ -143,3 +141,4 @@ readability-static-definition-in-anonymous-namespace,
readability-string-compare, readability-string-compare,
readability-uniqueptr-delete-release, readability-uniqueptr-delete-release,
readability-use-anyofallof' readability-use-anyofallof'
...
+4 -6
View File
@@ -7,9 +7,7 @@ Diagnostics:
CompileFlags: CompileFlags:
Add: Add:
- "-ferror-limit=0" - "-ferror-limit=0"
- "-D__FUNCTION__=\"dummy\"" - '-D__FUNCTION__="dummy"'
- "-Yumc/_HeaderOutputPredefine.h" - "-Dnsel_CONFIG_SELECT_EXPECTED=nsel_EXPECTED_NONSTD"
- "-FImc/_HeaderOutputPredefine.h" # clangd bug can't find pch file - "/std:c++20"
Remove: - "-D_HAS_CXX23=1"
- "/Yu_HeaderOutputPredefine.h"
- "/FI_HeaderOutputPredefine.h"
+21 -15
View File
@@ -7,26 +7,32 @@ jobs:
build: build:
runs-on: windows-latest runs-on: windows-latest
steps: steps:
- name: Checkout repository - name: Checkout
uses: actions/checkout@v2 uses: actions/checkout@v4
- name: Setup XMake
uses: xmake-io/github-action-setup-xmake@v1
- name: Cache
uses: actions/cache@v4
with: with:
submodules: recursive path: |
~/AppData/Local/.xmake
key: xmake-${{ hashFiles('xmake.lua') }}
restore-keys: |
xmake-
- uses: actions/checkout@v4 - name: Update Repo
run: xmake repo -u
- uses: xmake-io/github-action-setup-xmake@v1 - name: Update Project
with: run: xmake f -a x64 -m release -p windows -v -y
xmake-version: branch@master
- run: |
xmake repo -u
- run: | - name: Build Project
xmake f -a x64 -m release -p windows -v -y run: xmake -v -y
- run: | - name: Upload Artifact
xmake -w -y uses: actions/upload-artifact@v4
- uses: actions/upload-artifact@v4
with: with:
name: ${{ github.event.repository.name }}-windows-x64-${{ github.sha }} name: ${{ github.event.repository.name }}-windows-x64-${{ github.sha }}
path: | path: |
+35 -27
View File
@@ -7,26 +7,32 @@ jobs:
build: build:
runs-on: windows-latest runs-on: windows-latest
steps: steps:
- name: Checkout repository - name: Checkout
uses: actions/checkout@v2 uses: actions/checkout@v4
- name: Setup XMake
uses: xmake-io/github-action-setup-xmake@v1
- name: Cache
uses: actions/cache@v4
with: with:
submodules: recursive path: |
~/AppData/Local/.xmake
key: xmake-${{ hashFiles('xmake.lua') }}
restore-keys: |
xmake-
- uses: actions/checkout@v4 - name: Update Repo
run: xmake repo -u
- uses: xmake-io/github-action-setup-xmake@v1 - name: Update Project
with: run: xmake f -a x64 -m release -p windows -v -y
xmake-version: branch@master
- run: |
xmake repo -u
- run: | - name: Build Project
xmake f -a x64 -m release -p windows -v -y run: xmake -v -y
- run: | - name: Upload Artifact
xmake -w -y uses: actions/upload-artifact@v4
- uses: actions/upload-artifact@v4
with: with:
name: ${{ github.event.repository.name }}-windows-x64-${{ github.sha }} name: ${{ github.event.repository.name }}-windows-x64-${{ github.sha }}
path: | path: |
@@ -39,24 +45,26 @@ jobs:
contents: write contents: write
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - name: Checkout
uses: actions/checkout@v4
- uses: actions/download-artifact@v4 - name: Download Artifact
uses: actions/download-artifact@v4
with: with:
name: ${{ github.event.repository.name }}-windows-x64-${{ github.sha }} name: ${{ github.event.repository.name }}-windows-x64-${{ github.sha }}
path: release/ path: artifact
- run: | - name: Copy Files
cp LICENSE README.md release/
- name: Archive release
run: | run: |
cd release cp LICENSE README.md artifact/
zip -r ../${{ github.event.repository.name }}-windows-x64.zip *
cd ..
- uses: softprops/action-gh-release@v1 - name: Compress Artifact
run: |
zip -r ../${{ github.event.repository.name }}-windows-x64.zip *
working-directory: artifact
- name: Create Release
uses: softprops/action-gh-release@v1
with: with:
append_body: true
files: | files: |
${{ github.event.repository.name }}-windows-x64.zip ${{ github.event.repository.name }}-windows-x64.zip
+1
View File
@@ -16,3 +16,4 @@ build/
/CMakeLists.txt /CMakeLists.txt
/bin /bin
/.cache /.cache
/.idea
+16
View File
@@ -1,6 +1,22 @@
# DeathMessages # DeathMessages
Better Death Messages on MCBE Better Death Messages on MCBE
## Dependencies
### Required Dependency
`GMLIB`
### Optional Dependency
`ModAPI`
Loading *ModAPI* enables client-side translation.
That allows players to freely switch languages on their own.
# 开源许可 # 开源许可
## 源代码可用性 ## 源代码可用性
- 您可以自由地获取、使用和修改本插件的源代码,无论是个人使用还是商业目的。 - 您可以自由地获取、使用和修改本插件的源代码,无论是个人使用还是商业目的。
+6 -1
View File
@@ -2,12 +2,17 @@
"name": "${modName}", "name": "${modName}",
"entry": "${modFile}", "entry": "${modFile}",
"type": "native", "type": "native",
"version": "0.13.1", "version": "0.14.0",
"author": "GroupMountain", "author": "GroupMountain",
"description": "Better Death Messages", "description": "Better Death Messages",
"dependencies": [ "dependencies": [
{ {
"name": "GMLIB" "name": "GMLIB"
} }
],
"optionalDependencies": [
{
"name": "ModAPI"
}
] ]
} }
+315 -91
View File
@@ -1,143 +1,367 @@
#include "Entry.h" #include "Entry.h"
#include "GMLIB/Mod/Addons/CustomDamageCause.h"
#include "Global.h" #include "ll/api/chrono/GameChrono.h"
#include "ll/api/event/player/PlayerConnectEvent.h" #include "ll/api/memory/Hook.h"
#include "ll/api/event/player/PlayerDisconnectEvent.h" #include "ll/api/service/Bedrock.h"
#include "mc/world/actor/ActorDamageSource.h"
#include "mc/world/actor/ActorDamageByActorSource.h"
#include "mc/world/actor/Actor.h"
#include "mc/world/actor/Mob.h"
#include "mc/world/level/Level.h"
//===============GMLIB 0.13.5 ActorDamageCause.cc===============
phmap::flat_hash_map<int64, float> mFallHeightMap;
phmap::flat_hash_map<int64, ActorUniqueID> mHurtByEntityMap;
phmap::flat_hash_map<int, std::string_view> mVanillaCauseMessage;
phmap::flat_hash_map<SharedTypes::Legacy::ActorDamageCause, phmap::flat_hash_map<std::string_view, std::string_view>> mHardCodedDeathMessage;
bool isCrystal = false;
std::string getCause(SharedTypes::Legacy::ActorDamageCause cause) {
auto id = (int)cause;
if (mVanillaCauseMessage.count(id)){return std::string(mVanillaCauseMessage[id]);}
return "";
}
Actor* getDamagingEntity(ActorDamageSource* ads) {
auto id = ads->getDamagingEntityUniqueID();
return ll::service::getLevel()->fetchEntity(id,true);
}
// %entity.warden.name
std::string getResourcePackKey(std::string const& name, Actor* ac) {
if (ac->hasCategory(ActorCategory::Player)){return name;}
auto type = ac->getTypeName();
ll::utils::string_utils::replaceAll(type, "minecraft:", "");
std::string res = "%entity." + type + ".name";
return res;
}
// 构造死亡信息输出
DeathMessageResult makeDeathMessage(
int cause,
DeathMessageResult& deathMessage,
std::string& name,
Actor* killer,
std::string& weaponName,
bool isEscaping,
bool isHardCodedMessage = false
) {
// 自定义类型构造
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) {
msg = deathMessage.first;}
// 没有武器名不使用.item结尾
if (weaponName.empty()) {
// 试图逃离 xx 使用.player
if (killer) {
if (isEscaping){ll::utils::string_utils::replaceAll(msg, ".item", ".player");}
} else {
ll::utils::string_utils::replaceAll(msg, ".item", "");
}
}
DeathMessageResult res = {msg, {name}};
if (killer) {
if (killer->isPlayer()) {
res.second.push_back(((Player*)killer)->getRealName());
} else {
res.second.push_back(getResourcePackKey(name, killer));
}
auto weaponname = killer->getCarriedItem().getCustomName();
if (!weaponname.empty()) {
weaponname = "[§b§o" + weaponname + "§r]";
res.second.push_back(weaponname);
} else {
ll::utils::string_utils::replaceAll(res.first, ".item", "");
}
} else {
// 如果没有击杀者和试图逃离,那么不可能使用 .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值
return deathMessage;
}
DeathMessageResult tryTranslateHardCodedFallingDeathMessage(
DeathMessageResult& deathMessage,
std::string& name,
Actor* killer,
std::string& directKiller,
std::string& weaponName,
bool isEscaping,
int64 deadUniqueId
) {
if (!directKiller.empty()) {
deathMessage.first = "death.fell.finish.item";
}
// 低处
else if (deathMessage.first == "death.attack.fall") {
deathMessage.first = "death.attack.fall.item";
}
// 高处
else if (deathMessage.first == "death.fell.accident.generic") {
// 试图逃离而亡
if (killer) {
std::string weapon = killer->getCarriedItem().getCustomName();
if (!weapon.empty()) {
deathMessage.first = "death.fell.assist.item";
} else {
deathMessage.first = "death.fell.assist";
}
}
// 超高
else if (mFallHeightMap[deadUniqueId] >= 100) {
deathMessage.first = "death.fell.killer";
} else {
deathMessage.first = "death.fell.accident.generic";
}
} else {
deathMessage.first = "death.attack.generic";
}
return makeDeathMessage((int)SharedTypes::Legacy::ActorDamageCause::Fall, deathMessage, name, killer, weaponName, false, true);
}
std::optional<DeathMessageResult> tryTranslateHardCodedDeathMessage(
SharedTypes::Legacy::ActorDamageCause cause,
DeathMessageResult& deathMessage,
std::string& name,
Actor* killer,
std::string& directKiller,
std::string& weaponName,
bool isEscaping,
int64 deadUniqueId
) {
switch (cause) {
// 硬编码摔死信息
case SharedTypes::Legacy::ActorDamageCause::Fall: {
return tryTranslateHardCodedFallingDeathMessage(
deathMessage,
name,
killer,
directKiller,
weaponName,
isEscaping,
deadUniqueId
);
}
// 硬编码爆炸信息
case SharedTypes::Legacy::ActorDamageCause::BlockExplosion: {
if (!isCrystal) {
deathMessage.first = "death.attack.badRespawnPoint.message";
} else {
deathMessage.first = "death.attack.explosion.item";
}
return makeDeathMessage((int)cause, deathMessage, name, killer, weaponName, isEscaping, true);
}
case SharedTypes::Legacy::ActorDamageCause::Contact: {
switch (ll::hash_utils::doHash(deathMessage.first)) {
case ll::hash_utils::doHash("death.attack.cactus"):
deathMessage.first = "death.attack.cactus.item";
break;
case ll::hash_utils::doHash("death.attack.sweetBerry"):
deathMessage.first = "death.attack.sweetBerry.item";
break;
default:
deathMessage.first = "death.attack.generic.item";
break;
}
return makeDeathMessage((int)cause, deathMessage, name, killer, weaponName, isEscaping, true);
}
}
if (mHardCodedDeathMessage.contains(cause)) {
if (directKiller.empty()) {
directKiller = "empty";
}
if (mHardCodedDeathMessage[cause].contains(directKiller)) {
deathMessage.first = std::string(mHardCodedDeathMessage[cause][directKiller]);
return makeDeathMessage((int)cause, deathMessage, name, killer, weaponName, isEscaping, true);
} else if (mHardCodedDeathMessage[cause].contains("all")) {
deathMessage.first = std::string(mHardCodedDeathMessage[cause]["all"]);
return makeDeathMessage((int)cause, deathMessage, name, killer, weaponName, isEscaping, true);
}
}
return {};
}
DeathMessageResult translateDeathMessage(DeathMessageResult origin, std::string name, Actor* dead, ActorDamageSource* ads) {
name = getResourcePackKey(name, dead);
if (origin.first.length() > 65565) {
return {"death.attack.even_more_magic", {name}};
}
auto damageCause = ads->mCause; // 伤害类型
Actor* killer = nullptr; // 击杀者
std::string directKiller; // 直接击杀实体
std::string weaponName; // 是否持有武器
bool isEscaping = false;
int64 uid = dead->getOrCreateUniqueID().rawID;
if (ads->isEntitySource()) {
directKiller = getDamagingEntity(ads)->getTypeName();
if (ads->isChildEntitySource()) {
killer = getDamagingEntity(ads)->getOwner();
} else {
killer = getDamagingEntity(ads);
}
}
if (mHurtByEntityMap.count(uid)) {
if (killer == nullptr) {
killer = ll::service::getLevel()->fetchEntity(mHurtByEntityMap[uid],true);
}
isEscaping = true;
}
if (killer != nullptr) {
weaponName = killer->getCarriedItem().getCustomName();
}
// Vanilla的硬编码死亡信息类型
auto result =
tryTranslateHardCodedDeathMessage(damageCause, origin, name, killer, directKiller, weaponName, isEscaping, uid);
if (result.has_value()) {
return result.value();
}
return makeDeathMessage((int)damageCause, origin, name, killer, weaponName, isEscaping);
}
bool setVanillaCauseMessage(SharedTypes::Legacy::ActorDamageCause cause, std::string_view msg) {
auto id = (int)cause;
if (!mVanillaCauseMessage.count(id)) {
mVanillaCauseMessage[id] = msg;
return true;
}
return false;
}
bool registerCustomDeathMessage(
SharedTypes::Legacy::ActorDamageCause cause,
std::string_view message,
std::string_view killerType
) {
if (mHardCodedDeathMessage.contains(cause)) {
if (mHardCodedDeathMessage[cause].contains(killerType)) {
return false;
}
}
mHardCodedDeathMessage[cause][killerType] = message;
return true;
}
//===============Copy finish===============
void RegisterDamageDefinition() { void RegisterDamageDefinition() {
GMLIB::Mod::DamageCause::DamageCause::setCustomDamageCauseEnabled(); setVanillaCauseMessage(SharedTypes::Legacy::ActorDamageCause::Anvil, "anvil");
GMLIB::Mod::DamageCause::DamageCause::setVanillaCauseMessage(ActorDamageCause::Anvil, "anvil"); setVanillaCauseMessage(SharedTypes::Legacy::ActorDamageCause::EntityAttack, "mob");
GMLIB::Mod::DamageCause::DamageCause::setVanillaCauseMessage(ActorDamageCause::EntityAttack, "mob"); setVanillaCauseMessage(SharedTypes::Legacy::ActorDamageCause::RamAttack, "mob");
GMLIB::Mod::DamageCause::DamageCause::setVanillaCauseMessage(ActorDamageCause::RamAttack, "mob"); setVanillaCauseMessage(SharedTypes::Legacy::ActorDamageCause::Drowning, "drown");
GMLIB::Mod::DamageCause::DamageCause::setVanillaCauseMessage(ActorDamageCause::Drowning, "drown"); setVanillaCauseMessage(SharedTypes::Legacy::ActorDamageCause::FallingBlock, "fallingBlock");
GMLIB::Mod::DamageCause::DamageCause::setVanillaCauseMessage(ActorDamageCause::FallingBlock, "fallingBlock"); setVanillaCauseMessage(SharedTypes::Legacy::ActorDamageCause::Fire, "inFire");
GMLIB::Mod::DamageCause::DamageCause::setVanillaCauseMessage(ActorDamageCause::Fire, "inFire"); setVanillaCauseMessage(SharedTypes::Legacy::ActorDamageCause::FireTick, "onFire");
GMLIB::Mod::DamageCause::DamageCause::setVanillaCauseMessage(ActorDamageCause::FireTick, "onFire"); setVanillaCauseMessage(SharedTypes::Legacy::ActorDamageCause::Fireworks, "fireworks");
GMLIB::Mod::DamageCause::DamageCause::setVanillaCauseMessage(ActorDamageCause::Fireworks, "fireworks"); setVanillaCauseMessage(SharedTypes::Legacy::ActorDamageCause::FlyIntoWall, "flyIntoWall");
GMLIB::Mod::DamageCause::DamageCause::setVanillaCauseMessage(ActorDamageCause::FlyIntoWall, "flyIntoWall"); setVanillaCauseMessage(SharedTypes::Legacy::ActorDamageCause::Freezing, "freeze");
GMLIB::Mod::DamageCause::DamageCause::setVanillaCauseMessage(ActorDamageCause::Freezing, "freeze"); setVanillaCauseMessage(SharedTypes::Legacy::ActorDamageCause::Lava, "lava");
GMLIB::Mod::DamageCause::DamageCause::setVanillaCauseMessage(ActorDamageCause::Lava, "lava"); setVanillaCauseMessage(SharedTypes::Legacy::ActorDamageCause::Lightning, "lightningBolt");
GMLIB::Mod::DamageCause::DamageCause::setVanillaCauseMessage(ActorDamageCause::Lightning, "lightningBolt"); setVanillaCauseMessage(SharedTypes::Legacy::ActorDamageCause::Wither, "wither");
GMLIB::Mod::DamageCause::DamageCause::setVanillaCauseMessage(ActorDamageCause::Wither, "wither"); setVanillaCauseMessage(SharedTypes::Legacy::ActorDamageCause::Magic, "indirectMagic");
GMLIB::Mod::DamageCause::DamageCause::setVanillaCauseMessage(ActorDamageCause::Magic, "indirectMagic"); setVanillaCauseMessage(SharedTypes::Legacy::ActorDamageCause::Magma, "hotFloor");
GMLIB::Mod::DamageCause::DamageCause::setVanillaCauseMessage(ActorDamageCause::Magma, "hotFloor"); setVanillaCauseMessage(SharedTypes::Legacy::ActorDamageCause::Stalactite, "stalactite");
GMLIB::Mod::DamageCause::DamageCause::setVanillaCauseMessage(ActorDamageCause::Stalactite, "stalactite"); setVanillaCauseMessage(SharedTypes::Legacy::ActorDamageCause::Stalagmite, "stalagmite");
GMLIB::Mod::DamageCause::DamageCause::setVanillaCauseMessage(ActorDamageCause::Stalagmite, "stalagmite"); setVanillaCauseMessage(SharedTypes::Legacy::ActorDamageCause::Starve, "starve");
GMLIB::Mod::DamageCause::DamageCause::setVanillaCauseMessage(ActorDamageCause::Starve, "starve"); setVanillaCauseMessage(SharedTypes::Legacy::ActorDamageCause::Suffocation, "inWall");
GMLIB::Mod::DamageCause::DamageCause::setVanillaCauseMessage(ActorDamageCause::Suffocation, "inWall"); setVanillaCauseMessage(SharedTypes::Legacy::ActorDamageCause::SonicBoom, "sonicBoom");
GMLIB::Mod::DamageCause::DamageCause::setVanillaCauseMessage(ActorDamageCause::SonicBoom, "sonicBoom"); setVanillaCauseMessage(SharedTypes::Legacy::ActorDamageCause::Void, "outOfWorld");
GMLIB::Mod::DamageCause::DamageCause::setVanillaCauseMessage(ActorDamageCause::Void, "outOfWorld"); setVanillaCauseMessage(SharedTypes::Legacy::ActorDamageCause::SelfDestruct, "genericKill");
GMLIB::Mod::DamageCause::DamageCause::setVanillaCauseMessage(ActorDamageCause::SelfDestruct, "genericKill"); setVanillaCauseMessage(SharedTypes::Legacy::ActorDamageCause::Projectile, "thrown");
GMLIB::Mod::DamageCause::DamageCause::setVanillaCauseMessage(ActorDamageCause::Projectile, "thrown"); setVanillaCauseMessage(SharedTypes::Legacy::ActorDamageCause::Thorns, "thorns");
GMLIB::Mod::DamageCause::DamageCause::setVanillaCauseMessage(ActorDamageCause::Thorns, "thorns"); registerCustomDeathMessage(
GMLIB::Mod::DamageCause::DamageCause::registerCustomDeathMessage( SharedTypes::Legacy::ActorDamageCause::Projectile,
ActorDamageCause::Projectile,
"death.attack.fireball.item", "death.attack.fireball.item",
"minecraft:small_fireball" "minecraft:small_fireball"
); );
GMLIB::Mod::DamageCause::DamageCause::registerCustomDeathMessage( registerCustomDeathMessage(
ActorDamageCause::Projectile, SharedTypes::Legacy::ActorDamageCause::Projectile,
"death.attack.trident.item", "death.attack.trident.item",
"minecraft:thrown_trident" "minecraft:thrown_trident"
); );
GMLIB::Mod::DamageCause::DamageCause::registerCustomDeathMessage( registerCustomDeathMessage(
ActorDamageCause::Projectile, SharedTypes::Legacy::ActorDamageCause::Projectile,
"death.attack.spit", "death.attack.spit",
"minecraft:llama_spit" "minecraft:llama_spit"
); );
GMLIB::Mod::DamageCause::DamageCause::registerCustomDeathMessage( registerCustomDeathMessage(
ActorDamageCause::Projectile, SharedTypes::Legacy::ActorDamageCause::Projectile,
"death.attack.bullet", "death.attack.bullet",
"minecraft:shulker_bullet" "minecraft:shulker_bullet"
); );
GMLIB::Mod::DamageCause::DamageCause::registerCustomDeathMessage( registerCustomDeathMessage(
ActorDamageCause::Projectile, SharedTypes::Legacy::ActorDamageCause::Projectile,
"death.attack.arrow.item", "death.attack.arrow.item",
"minecraft:arrow" "minecraft:arrow"
); );
GMLIB::Mod::DamageCause::DamageCause::registerCustomDeathMessage( registerCustomDeathMessage(
ActorDamageCause::Projectile, SharedTypes::Legacy::ActorDamageCause::Projectile,
"death.attack.explosion.player", "death.attack.explosion.player",
"minecraft:fireball" "minecraft:fireball"
); );
GMLIB::Mod::DamageCause::DamageCause::registerCustomDeathMessage( registerCustomDeathMessage(
ActorDamageCause::Projectile, SharedTypes::Legacy::ActorDamageCause::Projectile,
"death.attack.mob", "death.attack.mob",
"minecraft:breeze_wind_charge_projectile" "minecraft:breeze_wind_charge_projectile"
); );
GMLIB::Mod::DamageCause::DamageCause::registerCustomDeathMessage( registerCustomDeathMessage(
ActorDamageCause::Magic, SharedTypes::Legacy::ActorDamageCause::Magic,
"death.attack.magic", "death.attack.magic",
"empty" "empty"
); );
GMLIB::Mod::DamageCause::DamageCause::registerCustomDeathMessage( registerCustomDeathMessage(
ActorDamageCause::SonicBoom, SharedTypes::Legacy::ActorDamageCause::SonicBoom,
"death.attack.sonicBoom", "death.attack.sonicBoom",
"minecraft:warden" "minecraft:warden"
); );
GMLIB::Mod::DamageCause::DamageCause::registerCustomDeathMessage( registerCustomDeathMessage(
ActorDamageCause::Thorns, SharedTypes::Legacy::ActorDamageCause::Thorns,
"death.attack.generic", "death.attack.generic",
"empty" "empty"
); );
GMLIB::Mod::DamageCause::DamageCause::registerCustomDeathMessage( registerCustomDeathMessage(
ActorDamageCause::EntityExplosion, SharedTypes::Legacy::ActorDamageCause::EntityExplosion,
"death.attack.explosion.player", "death.attack.explosion.player",
"all" "all"
); );
GMLIB::Mod::DamageCause::DamageCause::registerCustomDeathMessage( registerCustomDeathMessage(
ActorDamageCause::EntityExplosion, SharedTypes::Legacy::ActorDamageCause::EntityExplosion,
"minecraft:wither_skull", "minecraft:wither_skull",
"death.attack.witherSkull.item" "death.attack.witherSkull.item"
); );
GMLIB::Mod::DamageCause::DamageCause::registerCustomDeathMessage( registerCustomDeathMessage(
ActorDamageCause::EntityExplosion, SharedTypes::Legacy::ActorDamageCause::EntityExplosion,
"minecraft:wither_skull_dangerous", "minecraft:wither_skull_dangerous",
"death.attack.witherSkull.item" "death.attack.witherSkull.item"
); );
GMLIB::Mod::DamageCause::DamageCause::registerCustomDeathMessage( registerCustomDeathMessage(
ActorDamageCause::EntityAttack, SharedTypes::Legacy::ActorDamageCause::EntityAttack,
"minecraft:player", "minecraft:player",
"death.attack.player.item" "death.attack.player.item"
); );
GMLIB::Mod::DamageCause::DamageCause::registerCustomDeathMessage( registerCustomDeathMessage(
ActorDamageCause::EntityAttack, SharedTypes::Legacy::ActorDamageCause::EntityAttack,
"minecraft:bee", "minecraft:bee",
"death.attack.sting.item" "death.attack.sting.item"
); );
GMLIB::Mod::DamageCause::DamageCause::registerCustomDeathMessage( registerCustomDeathMessage(
ActorDamageCause::RamAttack, SharedTypes::Legacy::ActorDamageCause::RamAttack,
"minecraft:player", "minecraft:player",
"death.attack.player.item" "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>([](ll::event::player::PlayerConnectEvent& ev) {
infoLogger->info(fmt::format(fg(fmt::color::yellow), tr("multiplayer.player.joined", {ev.self().getName()}))
);
});
}
if (config.ConsoleLog.LeftMessage) {
eventBus.emplaceListener<ll::event::player::PlayerDisconnectEvent>(
[](ll::event::player::PlayerDisconnectEvent& ev) {
infoLogger->info(
fmt::format(fg(fmt::color::yellow), tr("multiplayer.player.left", {ev.self().getName()}))
);
}
);
}
if (config.ConsoleLog.DeathMessage) {
eventBus.emplaceListener<GMLIB::Event::EntityEvent::DeathMessageAfterEvent>(
[](GMLIB::Event::EntityEvent::DeathMessageAfterEvent& ev) {
auto msg = ev.getDeathMessage();
auto info = tr(msg.first, msg.second);
deathLogger->info(info);
ev.getDeathMessage() = {info, msg.second};
}
);
}
}
+53 -16
View File
@@ -2,11 +2,28 @@
#include "Global.h" #include "Global.h"
#include "Language.h" #include "Language.h"
#include "ll/api/Config.h"
#include "ll/api/data/IndirectValue.h"
#include "ll/api/io/PatternFormatter.h"
#include "ll/api/io/Logger.h"
#include "ll/api/io/LoggerRegistry.h"
#include "ll/api/io/FileSink.h"
#include "ll/api/mod/Mod.h"
#include "ll/api/mod/ModManagerRegistry.h"
#include "ll/api/mod/RegisterHelper.h"
#include "ll/api/service/Bedrock.h"
#include "gmlib/gm/i18n/LangI18n.h"
#include "gmlib/gm/i18n/ResourceI18n.h"
ll::io::LoggerRegistry& loggerRegistry = ll::io::LoggerRegistry::getInstance(); ll::io::LoggerRegistry& loggerRegistry = ll::io::LoggerRegistry::getInstance();
std::shared_ptr<ll::io::Logger> logger = loggerRegistry.getOrCreate(MOD_NAME); std::shared_ptr<ll::io::Logger> logger = loggerRegistry.getOrCreate(MOD_NAME);
std::shared_ptr<ll::io::Logger> deathLogger = loggerRegistry.getOrCreate("Death"); std::shared_ptr<ll::io::Logger> deathLogger = loggerRegistry.getOrCreate("Death");
std::shared_ptr<ll::io::Logger> infoLogger = loggerRegistry.getOrCreate("Server"); std::shared_ptr<ll::io::Logger> infoLogger = loggerRegistry.getOrCreate("Server");
extern void uninstallHooks();
extern void RegisterDamageDefinition();
extern void uninstallEventListeners();
extern void ListenEvents();
namespace DeathMessages { namespace DeathMessages {
@@ -16,25 +33,30 @@ Entry& Entry::getInstance() {
} }
bool Entry::load() { bool Entry::load() {
isResourceI18nLoaded=false;
mConfig.emplace(); mConfig.emplace();
if (!ll::config::loadConfig(*mConfig, getSelf().getConfigDir() / u8"config.json")) { if (!ll::config::loadConfig(*mConfig, getSelf().getConfigDir() / u8"config.json")) {
ll::config::saveConfig(*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) { if (getConfig().FileLog.Enabled) {
auto& path = getConfig().FileLog.Path; auto& path = getConfig().FileLog.Path;
ll::Polymorphic<ll::io::PatternFormatter> polymorphicFormatter = ll::Polymorphic<ll::io::PatternFormatter> polymorphicFormatter =
ll::makePolymorphic<ll::io::PatternFormatter>("[{3:.3%F %T.} {2}][{1}] {0}", false); ll::makePolymorphic<ll::io::PatternFormatter>("[{3:.3%F %T.} {2}][{1}] {0}", false);
deathLogger->addSink(std::make_shared<ll::io::FileSink>(path, polymorphicFormatter)); deathLogger->addSink(std::make_shared<ll::io::FileSink>(path, polymorphicFormatter));
} }
loadI18n();
if ((!getConfig().ServerSideTranslation.Enabled)&&ll::mod::ModManagerRegistry::getInstance().hasMod("ModAPI")) {
loadResourcePack();
}
return true; return true;
} }
bool Entry::enable() { bool Entry::enable() {
if (!ll::mod::ModManagerRegistry::getInstance().hasMod("ModAPI")) {
getSelf().getLogger().warn(tr("plugin.deathmessages.modapi.warn"));
}
RegisterDamageDefinition(); RegisterDamageDefinition();
ListenEvents(); ListenEvents();
return true; return true;
@@ -45,36 +67,51 @@ bool Entry::disable() { return true; }
bool Entry::unload() { bool Entry::unload() {
mConfig.reset(); mConfig.reset();
mI18n.reset(); mI18n.reset();
uninstallHooks();
loggerRegistry.erase(MOD_NAME);
loggerRegistry.erase("Death");
loggerRegistry.erase("Server");
// getInstance().reset(); // getInstance().reset();
return true; return true;
} }
Config& Entry::getConfig() { return mConfig.value(); } Config& Entry::getConfig() { return mConfig.value(); }
std::optional<GMLIB::Files::I18n::LangI18n> Entry::getI18n() { return mI18n; } gmlib::i18n::LangI18n& Entry::getI18n() {
return *mI18n;
}
void Entry::loadI18n() { void Entry::loadI18n() {
mI18n.emplace(getSelf().getLangDir(), getConfig().ServerSideTranslation.Language); mI18n= std::make_unique<gmlib::i18n::LangI18n>(getSelf().getLangDir(), getConfig().ServerSideTranslation.Language);
mI18n->updateOrCreateLanguage("en_US", en_US); mI18n->updateOrCreateLanguage("en_US", en_US);
mI18n->updateOrCreateLanguage("zh_CN", zh_CN); mI18n->updateOrCreateLanguage("zh_CN", zh_CN);
mI18n->loadAllLanguages(); mI18n->loadAllLanguages();
mI18n->setDefaultLanguage("zh_CN"); mI18n->setDefaultLanguage("zh_CN");
} }
void Entry::loadResourcePack() { void Entry::loadResourcePack() {
GMLIB::Mod::VanillaFix::setFixI18nEnabled(); auto mResource = std::make_unique<gmlib::i18n::ResourceI18n>(getSelf().getModDir() / u8"lang", MOD_NAME, 0, 16, 1);
auto resource = GMLIB::Files::ResourceLanguage(getSelf().getModDir() / u8"resource", MOD_NAME, 0, 8, 0); mResource->addLanguage("en_US", en_US);
resource.addLanguage("en_US", en_US); mResource->addLanguage("zh_CN", zh_CN);
resource.addLanguage("zh_CN", zh_CN); mResource->loadAllLanguages();
isResourceI18nLoaded=true;
} }
} // namespace DeathMessages } // namespace DeathMessages
LL_REGISTER_MOD(DeathMessages::Entry, DeathMessages::Entry::getInstance()); LL_REGISTER_MOD(DeathMessages::Entry, DeathMessages::Entry::getInstance());
std::string tr(std::string const& key, std::vector<std::string> const& params) { std::string tr(std::string const& key, std::vector<std::string> const& params) {
if (auto i18n = DeathMessages::Entry::getInstance().getI18n()) { auto& i18n = DeathMessages::Entry::getInstance().getI18n();
return i18n->get(key, params); return i18n.get(key, params);
}
return ::I18nAPI::get(key, params);
return key;
} }
+9 -2
View File
@@ -2,6 +2,11 @@
#include "Config.h" #include "Config.h"
#include "Global.h" #include "Global.h"
#include "ll/api/mod/Mod.h"
#include <ll/api/mod/NativeMod.h>
#include <gmlib/gm/i18n/LangI18n.h>
namespace DeathMessages { namespace DeathMessages {
class Entry { class Entry {
@@ -27,16 +32,18 @@ public:
Config& getConfig(); Config& getConfig();
std::optional<GMLIB::Files::I18n::LangI18n> getI18n(); gmlib::i18n::LangI18n& getI18n();
void loadI18n(); void loadI18n();
void loadResourcePack(); void loadResourcePack();
bool isResourceI18nLoaded;
private: private:
ll::mod::NativeMod& mSelf; ll::mod::NativeMod& mSelf;
std::optional<Config> mConfig; std::optional<Config> mConfig;
std::optional<GMLIB::Files::I18n::LangI18n> mI18n; std::unique_ptr<gmlib::i18n::LangI18n> mI18n;
}; };
} // namespace DeathMessages } // namespace DeathMessages
+11 -4
View File
@@ -1,13 +1,20 @@
#pragma once #pragma once
#include <include_all.h> #include <ll/api/io/Logger.h>
#include <mc/legacy/ActorUniqueID.h>
#include <mc/world/actor/ActorDamageSource.h>
#include <parallel_hashmap/phmap.h>
#define MOD_NAME "DeathMessages" #define MOD_NAME "DeathMessages"
extern std::shared_ptr<ll::io::Logger> logger; extern std::shared_ptr<ll::io::Logger> logger;
extern std::shared_ptr<ll::io::Logger> deathLogger; extern std::shared_ptr<ll::io::Logger> deathLogger;
extern std::shared_ptr<ll::io::Logger> infoLogger; extern std::shared_ptr<ll::io::Logger> infoLogger;
extern phmap::flat_hash_map<int64, float> mFallHeightMap;
extern phmap::flat_hash_map<int64, ActorUniqueID> mHurtByEntityMap;
extern bool isCrystal;
extern std::string tr(std::string const& key, std::vector<std::string> const& data = {}); using DeathMessageResult = std::pair<std::string, std::vector<std::string>>;
extern void RegisterDamageDefinition(); extern DeathMessageResult translateDeathMessage(DeathMessageResult origin, std::string name, Actor* dead, ActorDamageSource* ads);
extern void ListenEvents(); extern std::string tr(std::string const& key, std::vector<std::string> const& params = {});
+140
View File
@@ -0,0 +1,140 @@
#include "Entry.h"
#include "Global.h"
#include "ll/api/memory/Hook.h"
#include "ll/api/chrono/GameChrono.h"
#include "ll/api/thread/ThreadPoolExecutor.h"
#include "mc/world/actor/Mob.h"
#include "mc/world/actor/monster/EnderCrystal.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/ActorHurtResult.h"
#include "mc/world/actor/Actor.h"
#include <chrono>
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();
}
if (hasCategory(ActorCategory::Player) || isTame()) {
if (mLastHurtByMobTime != 0) {
auto hm = getLastHurtByMob();
mHurtByEntityMap[uid.rawID] = hm->getOrCreateUniqueID();
}
}
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);
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);
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);
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);
auto res = translateDeathMessage(originl, a1, a2, this);
return res;
}
void uninstallHooks() {
MobDieHook::unhook();
CryatslHurtHook::unhook();
ActorDamageHook::unhook();
ActorDamageByActorHook::unhook();
ActorDamageByBlockHook::unhook();
ActorDamageByChildHook::unhook();
}
+123 -109
View File
@@ -35,6 +35,7 @@ death.attack.flyIntoWall.item=%1 felt the force of kinetic energy while trying t
death.attack.generic=%1$s died death.attack.generic=%1$s died
death.attack.generic.player=%1$s died from %2$s death.attack.generic.player=%1$s died from %2$s
death.attack.indirectMagic=%1$s was killed by magic used by %2$s death.attack.indirectMagic=%1$s was killed by magic used by %2$s
death.attack.indirectMagic.player=%1$s was killed by magic used by %2$s
death.attack.indirectMagic.item=%1$s was killed by %2$s using %3$s death.attack.indirectMagic.item=%1$s was killed by %2$s using %3$s
death.attack.inFire=%1$s was consumed by fire death.attack.inFire=%1$s was consumed by fire
death.attack.inFire.player=%1$s accidentally walked into fire while fighting %2$s death.attack.inFire.player=%1$s accidentally walked into fire while fighting %2$s
@@ -72,8 +73,10 @@ death.attack.sweetBerry=%1$s was pricked to death by sweet berry bushes
death.attack.sweetBerry.player=%1$s was pricked to death by sweet berry bushes while trying to escape %2$s death.attack.sweetBerry.player=%1$s was pricked to death by sweet berry bushes while trying to escape %2$s
death.attack.sweetBerry.item=%1$s was pricked to death by sweet berry bushes while trying to escape %2$s holding %3$s death.attack.sweetBerry.item=%1$s was pricked to death by sweet berry bushes while trying to escape %2$s holding %3$s
death.attack.thorns=%1$s was killed trying to hurt %2$s death.attack.thorns=%1$s was killed trying to hurt %2$s
death.attack.thorns.player=%1$s was killed trying to hurt %2$s
death.attack.thorns.item=%1$s was killed by %3$s when trying to hurt %2$s death.attack.thorns.item=%1$s was killed by %3$s when trying to hurt %2$s
death.attack.thrown=%1$s was pummeled by %2$s death.attack.thrown=%1$s was pummeled by %2$s
death.attack.thrown.player=%1$s was pummeled by %2$s
death.attack.thrown.item=%1$s was pummeled by %2$s using %3$s death.attack.thrown.item=%1$s was pummeled by %2$s using %3$s
death.attack.trident=%1$s was impaled by %2$s death.attack.trident=%1$s was impaled by %2$s
death.attack.trident.item=%1$s was impaled by %2$s using %3$s death.attack.trident.item=%1$s was impaled by %2$s using %3$s
@@ -84,6 +87,7 @@ death.attack.freeze=%1$s froze to death
death.attack.freeze.player=%1$s was frozen to death by %2$s death.attack.freeze.player=%1$s was frozen to death by %2$s
death.attack.freeze.item=%1$s was frozen to death by %2$s holding %3$s death.attack.freeze.item=%1$s was frozen to death by %2$s holding %3$s
death.attack.sonicBoom=%1$s was obliterated by a sonic boom death.attack.sonicBoom=%1$s was obliterated by a sonic boom
death.attack.sonicBoom.item=%1$s was obliterated by a sonic boom while trying to escape %2$s
death.attack.sonicBoom.player=%1$s was obliterated by a sonic boom while trying to escape %2$s death.attack.sonicBoom.player=%1$s was obliterated by a sonic boom while trying to escape %2$s
death.attack.stalactite=%1$s was impaled by a falling stalactite death.attack.stalactite=%1$s was impaled by a falling stalactite
death.attack.stalactite.player=%1$s was impaled by a falling stalactite while battling %2$s death.attack.stalactite.player=%1$s was impaled by a falling stalactite while battling %2$s
@@ -107,6 +111,7 @@ death.fell.accident.generic=%1$s fell from a high place
death.fell.assist=%1$s was doomed to fall by %2$s death.fell.assist=%1$s was doomed to fall by %2$s
death.fell.assist.item=%1$s was doomed to fall by %2$s using %3$s death.fell.assist.item=%1$s was doomed to fall by %2$s using %3$s
death.fell.finish=%1$s fell too far and was finished by %2$s death.fell.finish=%1$s fell too far and was finished by %2$s
death.fell.finish.player=%1$s fell too far and was finished by %2$s
death.fell.finish.item=%1$s fell too far and was finished by %2$s using %3$s death.fell.finish.item=%1$s fell too far and was finished by %2$s using %3$s
death.fell.killer=%1$s was doomed to fall death.fell.killer=%1$s was doomed to fall
entity.area_effect_cloud.name=Area Effect Cloud entity.area_effect_cloud.name=Area Effect Cloud
@@ -250,116 +255,124 @@ entity.breeze.name=Breeze
entity.creaking.name=Creaking entity.creaking.name=Creaking
multiplayer.player.joined=%s joined the game multiplayer.player.joined=%s joined the game
multiplayer.player.left=%s left the game multiplayer.player.left=%s left the game
plugin.deathmessages.modapi.warn=The optional Dependency plugin ModAPI is not loaded, so client-side translation is unavailable. Only server-side translation can be used.
)"; )";
std::string zh_CN = R"( std::string zh_CN = R"(
death.attack.anvil=%1$s被坠落的铁砧压扁了 death.attack.anvil=%1$s
death.attack.anvil.player=%1$sz在与%2$s战斗时被坠落的铁砧压扁了 death.attack.anvil.player=%1$s %2$s
death.attack.anvil.item=%1$s在与持有%3$s的%2$s战斗时被坠落的铁砧压扁了 death.attack.anvil.item=%1$s %3$s %2$s
death.attack.arrow=%1$s被%2$s射杀 death.attack.arrow=%1$s被 %2$s
death.attack.arrow.item=%1$s被%2$s用%3$s射杀 death.attack.arrow.item=%1$s %2$s %3$s
death.attack.bullet=%1$s被%2$s狙击了 death.attack.bullet=%1$s被 %2$s
death.attack.cactus=%1$s被戳死了 death.attack.cactus=%1$s
death.attack.cactus.player=%1$s在试图逃离%2$s时撞上了仙人掌 death.attack.cactus.player=%1$s %2$s
death.attack.cactus.item=%1$s在试图逃离持有%3$s的%2$s时撞上了仙人掌 death.attack.cactus.item=%1$s %3$s %2$s
death.attack.drown=%1$s淹死了 death.attack.drown=%1$s
death.attack.drown.player=%1$s在试图逃离%2$s时淹死了 death.attack.drown.player=%1$s %2$s
death.attack.drown.item=%1$s在试图逃离持有%3$s的%2$s时淹死了 death.attack.drown.item=%1$s %3$s %2$s
death.attack.explosion=%1$s爆炸了 death.attack.explosion=%1$s
death.attack.explosion.player=%1$s被%2$s炸死了 death.attack.explosion.player=%1$s %2$s
death.attack.explosion.item=%1$s被%2$s使用%3$s炸死了 death.attack.explosion.item=%1$s %2$s 使 %3$s
death.attack.badRespawnPoint.message=%1$s被[] death.attack.badRespawnPoint.message=%1$s []
death.attack.fall=%1$s落地过猛 death.attack.fall=%1$s
death.attack.fall.player=%1$s在试图逃离%2$s时落地过猛 death.attack.fall.player=%1$s %2$s
death.attack.fall.item=%1$s在试图逃离持有%3$s的%2$s时落地过猛 death.attack.fall.item=%1$s %3$s %2$s
death.attack.fallingBlock=%1$s被坠落的方块压扁了 death.attack.fallingBlock=%1$s
death.attack.fallingBlock.player=%1$sz在与%2$s战斗时被坠落的方块压扁了 death.attack.fallingBlock.player=%1$s %2$s
death.attack.fallingBlock.item=%1$s在与持有%3$s的%2$s战斗时被坠落的方块压扁了 death.attack.fallingBlock.item=%1$s %3$s %2$s
death.attack.fireball=%1$s被%2$s用火球烧死了 death.attack.fireball=%1$s被 %2$s
death.attack.fireball.item=%1$s被%2$s用%3$s发射的火球烧死了 death.attack.fireball.item=%1$s被 %2$s %3$s
death.attack.fireworks=%1$s随着一声巨响消失了 death.attack.fireworks=%1$s
death.attack.fireworks.player=%1$s在与%2$s战斗时随着一声巨响消失了 death.attack.fireworks.player=%1$s %2$s
death.attack.fireworks.item=%1$s随着%2$s用%3$s发射的烟花发出的巨响消失了 death.attack.fireworks.item=%1$s %2$s %3$s
death.attack.flyIntoWall=%1$s感受到了动能 death.attack.flyIntoWall=%1$s
death.attack.flyIntoWall.player=%1$s在试图逃离%2$s时感受到了动能 death.attack.flyIntoWall.player=%1$s %2$s
death.attack.flyIntoWall.item=%1$s在试图逃离持有%3$s的%2$s时感受到了动能 death.attack.flyIntoWall.item=%1$s %3$s %2$s
death.attack.generic=%1$s死了 death.attack.generic=%1$s
death.attack.generic.player=%1$s死于%2$s death.attack.generic.player=%1$s %2$s
death.attack.indirectMagic=%1$s被%2$s使用的魔法杀死了 death.attack.indirectMagic=%1$s %2$s 使
death.attack.indirectMagic.item=%1$s被%2$s用%3$s杀死了 death.attack.indirectMagic.player=%1$s %2$s 使
death.attack.inFire=%1$s浴火焚身 death.attack.indirectMagic.item=%1$s %2$s %3$s
death.attack.inFire.player=%1$s在与%2$s战斗时不慎走入了火中 death.attack.inFire=%1$s
death.attack.inFire.item=%1$s在与持有%3$s的%2$s战斗时不慎走入了火中 death.attack.inFire.player=%1$s %2$s
death.attack.inWall=%1$s在墙里窒息而亡 death.attack.inFire.item=%1$s %3$s %2$s
death.attack.inWall.player=%1$s在与%2$s战斗时在墙里窒息而亡 death.attack.inWall=%1$s
death.attack.inWall.item=%1$s在与持有%3$s的%2$s战斗时在墙里窒息而亡 death.attack.inWall.player=%1$s %2$s
death.attack.lava=%1$s试图在熔岩里游泳 death.attack.inWall.item=%1$s %3$s %2$s
death.attack.lava.player=%1$s在试图逃离%2$s时试图在熔岩里游泳 death.attack.lava=%1$s
death.attack.lava.item=%1$s在试图逃离持有%3$s的%2$s时试图在熔岩里游泳 death.attack.lava.player=%1$s %2$s
death.attack.lightningBolt=%1$s被闪电击中 death.attack.lava.item=%1$s %3$s %2$s
death.attack.lightningBolt.player=%1$s在与%2$s战斗时被闪电击中 death.attack.lightningBolt=%1$s
death.attack.lightningBolt.item=%1$s在与持有%3$s的%2$s战斗时被闪电击中 death.attack.lightningBolt.player=%1$s %2$s
death.attack.magic=%1$s被魔法杀死了 death.attack.lightningBolt.item=%1$s %3$s %2$s
death.attack.hotFloor=%1$s发现地板是熔岩做的 death.attack.magic=%1$s
death.attack.hotFloor.player=%1$s因%2$s而步入危险之地 death.attack.hotFloor=%1$s
death.attack.hotFloor.item=%1$s因持有%3$s的%2$s而步入危险之地 death.attack.hotFloor.player=%1$s %2$s
death.attack.mob=%1$s被%2$s杀死了 death.attack.hotFloor.item=%1$s %3$s %2$s
death.attack.mob.item=%1$s被%2$s用%3$s杀死了 death.attack.mob=%1$s %2$s
death.attack.sting=%1$s被%2$s蛰死了 death.attack.mob.player=%1$s %2$s
death.attack.sting.item=%1$s被%2$s用%3$s蛰死了 death.attack.mob.item=%1$s %2$s %3$s
death.attack.onFire=%1$s被烧死了 death.attack.sting=%1$s %2$s
death.attack.onFire.player=%1$s在与%2$s战斗时被烤的酥脆 death.attack.sting.item=%1$s %2$s %3$s
death.attack.onFire.item=%1$s在与持有%3$s的%2$s战斗时被烤的酥脆 death.attack.onFire=%1$s
death.attack.outOfWorld=%1$s掉出了这个世界 death.attack.onFire.player=%1$s %2$s
death.attack.outOfWorld.player=%1$s与%2$s不共戴天 death.attack.onFire.item=%1$s %3$s %2$s
death.attack.outOfWorld.item=%1$s与持有%3$s的%2$s不共戴天 death.attack.outOfWorld=%1$s
death.attack.player=%1$s被%2$s杀死了 death.attack.outOfWorld.player=%1$s %2$s
death.attack.player.item=%1$s被%2$s用%3$s杀死了 death.attack.outOfWorld.item=%1$s %3$s %2$s
death.attack.spit=%1$s被%2$s的口水淹死了 death.attack.player=%1$s %2$s
death.attack.starve=%1$s饿死了 death.attack.player.item=%1$s %2$s %3$s
death.attack.starve.player=%1$s在与%2$s战斗时饿死了 death.attack.spit=%1$s %2$s
death.attack.starve.item=%1$s在与持有%3$s的%2$s战斗时饿死了 death.attack.starve=%1$s 饿
death.attack.sweetBerry=%1$s被甜浆果丛刺死了 death.attack.starve.player=%1$s %2$s 饿
death.attack.sweetBerry.player=%1$s在试图逃离%2$s时被甜浆果丛刺死了 death.attack.starve.item=%1$s %3$s %2$s 饿
death.attack.sweetBerry.item=%1$s在试图逃离持有%3$s的%2$s时被甜浆果丛刺死了 death.attack.sweetBerry=%1$s
death.attack.thorns=%1$s在试图伤害%2$s时被杀 death.attack.sweetBerry.player=%1$s %2$s
death.attack.thorns.item=%1$s在试图伤害%2$s时被%3$s杀死 death.attack.sweetBerry.item=%1$s %3$s %2$s
death.attack.thrown=%1$s被%2$s给砸死了 death.attack.thorns=%1$s %2$s
death.attack.thrown.item=%1$s被%2$s用%3$s给砸死了 death.attack.thorns.player=%1$s %2$s
death.attack.trident=%1$s被%2$s刺穿了 death.attack.thorns.item=%1$s %2$s %3$s
death.attack.trident.item=%1$s被%2$s使用%3$s刺穿了 death.attack.thrown=%1$s %2$s
death.attack.wither=%1$s凋零了 death.attack.thrown.player=%1$s %2$s
death.attack.wither.player=%1$s在与%2$s战斗时凋零了 death.attack.thrown.item=%1$s %2$s %3$s
death.attack.wither.item=%1$s在与持有%3$s的%2$s战斗时凋零了 death.attack.trident=%1$s %2$s 穿
death.attack.freeze=%1$s被冻死了 death.attack.trident.item=%1$s %2$s 使 %3$s 穿
death.attack.freeze.player=%1$s被%2$s冻死了 death.attack.wither=%1$s
death.attack.freeze.item=%1$s被持有%3$s的%2$s冻死了 death.attack.wither.player=%1$s %2$s
death.attack.sonicBoom=%1$s被一道音波尖啸抹除了 death.attack.wither.item=%1$s %3$s %2$s
death.attack.sonicBoom.player=%1$s在试图逃离%2$s时被一道音波尖啸抹除了 death.attack.freeze=%1$s
death.attack.stalactite=%1$s被坠落的钟乳石刺穿了 death.attack.freeze.player=%1$s %2$s
death.attack.stalactite.player=%1$s在与%2$s战斗时被坠落的钟乳石刺穿了 death.attack.freeze.item=%1$s %3$s %2$s
death.attack.stalactite.item=%1$s在与持有%3$s的%2$s战斗时被坠落的钟乳石刺穿了 death.attack.sonicBoom=%1$s
death.attack.stalagmite=%1$s被石笋刺穿了 death.attack.sonicBoom.player=%1$s %2$s
death.attack.stalagmite.player=%1$s在与%2$s战斗时被石笋刺穿了 death.attack.sonicBoom.item=%1$s %2$s
death.attack.stalagmite.item=%1$s在与持有%3$s的%2$s战斗时被石笋刺穿了 death.attack.stalactite=%1$s 穿
death.attack.witherSkull=%1$s被%2$s发射的头颅射杀 death.attack.stalactite.player=%1$s %2$s 穿
death.attack.witherSkull.item=%1$s被%2$s用%3$s发射的头颅射杀 death.attack.stalactite.item=%1$s %3$s %2$s 穿
death.attack.even_more_magic=%1$s被不为人知的魔法杀死了 death.attack.stalagmite=%1$s 穿
death.attack.genericKill=%1$s被杀死了 death.attack.stalagmite.player=%1$s %2$s 穿
death.attack.genericKill.player=%1$s在与%2$s战斗时被杀死了 death.attack.stalagmite.item=%1$s %3$s %2$s 穿
death.attack.genericKill.item=%1$s在与持有%3$s的%2$s战斗时被杀死了 death.attack.witherSkull=%1$s %2$s
death.attack.dragonBreath=%1$s被龙息烤熟了 death.attack.witherSkull.item=%1$s %2$s %3$s
death.attack.dragonBreath.player=%1$s被%2$s的龙息烤熟了 death.attack.even_more_magic=%1$s
death.attack.dryout=%1$s因脱水而死 death.attack.genericKill=%1$s
death.attack.dryout.player=%1$s在试图逃离%2$s时因脱水而死 death.attack.genericKill.player=%1$s %2$s
death.attack.outsideBorder=%1$s脱离了这个世界 death.attack.genericKill.item=%1$s %3$s %2$s
death.attack.outsideBorder.player=%1$s在与%2$s战斗时脱离了这个世界 death.attack.dragonBreath=%1$s
death.fell.accident.generic=%1$s从高处摔了下来 death.attack.dragonBreath.player=%1$s %2$s
death.fell.assist=%1$s因为%2$s注定要摔死 death.attack.dryout=%1$s
death.fell.assist.item=%1$s因为%2$s使用了%3$s注定要摔死 death.attack.dryout.player=%1$s %2$s
death.fell.finish=%1$s摔伤得太重并被%2$s完结了生命 death.attack.outsideBorder=%1$s
death.fell.finish.item=%1$s摔伤得太重并被%2$s用%3$s完结了生命 death.attack.outsideBorder.player=%1$s %2$s
death.fell.killer=%1$s注定要摔死 death.fell.accident.generic=%1$s
death.fell.assist=%1$s %2$s
death.fell.assist.item=%1$s %2$s 使 %3$s
death.fell.finish=%1$s %2$s
death.fell.finish.player=%1$s %2$s
death.fell.finish.item=%1$s %2$s %3$s
death.fell.killer=%1$s
entity.area_effect_cloud.name= entity.area_effect_cloud.name=
entity.armor_stand.name= entity.armor_stand.name=
entity.arrow.name= entity.arrow.name=
@@ -500,6 +513,7 @@ entity.wind_charge_projectile.name=风弹
entity.breeze_wind_charge_projectile.name= entity.breeze_wind_charge_projectile.name=
entity.breeze.name= entity.breeze.name=
entity.creaking.name= entity.creaking.name=
multiplayer.player.joined=%s加入了游戏 multiplayer.player.joined=%s
multiplayer.player.left=%s退出了游戏 multiplayer.player.left=%s 退
plugin.deathmessages.modapi.warn=ModAPI没有被加载使
)"; )";
+49
View File
@@ -0,0 +1,49 @@
#include "Entry.h"
#include "Global.h"
#include "ll/api/event/player/PlayerConnectEvent.h"
#include "ll/api/event/player/PlayerDieEvent.h"
#include "ll/api/event/player/PlayerDisconnectEvent.h"
#include "ll/api/event/EventBus.h"
ll::event::ListenerPtr playerConnectEventListener;
ll::event::ListenerPtr playerDieEventListener;
ll::event::ListenerPtr playerDisconnectEventListener;
void ListenEvents() {
auto& config = DeathMessages::Entry::getInstance().getConfig();
auto& eventBus = ll::event::EventBus::getInstance();
if (config.ConsoleLog.JoinMessage) {
playerConnectEventListener=eventBus.emplaceListener<ll::event::player::PlayerConnectEvent>([](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) {
playerDisconnectEventListener=eventBus.emplaceListener<ll::event::player::PlayerDisconnectEvent>(
[](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) {
playerDieEventListener=eventBus.emplaceListener<ll::event::player::PlayerDieEvent>(
[](ll::event::player::PlayerDieEvent& ev) {
auto [key, params] = ev.source().getDeathMessage(ev.self().getRealName(), &ev.self());
auto info = tr(key, params);
deathLogger->info(info);
}
);
}
}
void uninstallEventListeners() {
auto& eventBus = ll::event::EventBus::getInstance();
eventBus.removeListener(playerDieEventListener);
eventBus.removeListener(playerConnectEventListener);
eventBus.removeListener(playerDisconnectEventListener);
}
+27 -10
View File
@@ -1,26 +1,43 @@
{ {
"format_version": 2, "format_version": 3,
"format_uuid": "289f771f-2c9a-4d73-9f3f-8492495a924d",
"tooth": "github.com/GroupMountain/DeathMessages", "tooth": "github.com/GroupMountain/DeathMessages",
"version": "0.13.1", "version": "0.14.0",
"info": { "info": {
"name": "DeathMessages", "name": "DeathMessages",
"description": "Better Death Messages on MCBE", "description": "Better Death Messages on MCBE",
"author": "GroupMountain", "author": "GroupMountain",
"source": "github.com/GroupMountain/DeathMessages",
"tags": [ "tags": [
"LeviLamina", "platform:levilamina",
"DeathMessages" "type:mod",
"DeathMessage",
"gmlib"
] ]
}, },
"asset_url": "https://github.com/GroupMountain/DeathMessages/releases/download/v0.13.1/DeathMessages-windows-x64.zip",
"dependencies": { "variants": [
"github.com/GroupMountain/GMLIB": ">=0.13.10"
},
"files": {
"place": [
{ {
"platform": "win-x64",
"dependencies": {
"github.com/GroupMountain/GMLIB-Release": ">=26.10.0"
},
"assets": [
{
"type": "self",
"urls": [],
"placements": [
{
"type": "dir",
"src": "DeathMessages/*", "src": "DeathMessages/*",
"dest": "plugins/DeathMessages" "dest": "plugins/DeathMessages"
} }
] ]
} }
],
"remove_files": [
"plugins/DeathMessages"
]
}
]
} }
+13 -37
View File
@@ -7,45 +7,21 @@ if not has_config("vs_runtime") then
set_runtimes("MD") set_runtimes("MD")
end end
add_requires("levilamina 1.0.0") add_requires("levilamina 26.10.5",{configs = {target_type = "server"}})
add_requires("levibuildscript 0.3.0") add_requires("levibuildscript")
add_requires("gmlib 0.13.10") add_requires("gmlib 26.10.0")
add_requires("modapi 26.10.0")
target("DeathMessages") -- Change this to your mod name. target("DeathMessages") -- Change this to your mod name.
add_cxflags(
"/EHa",
"/utf-8"
)
add_files(
"src/**.cpp"
)
add_includedirs(
"src"
)
add_packages(
"levilamina",
"gmlib"
)
add_defines(
"NOMINMAX",
"UNICODE",
"_HAS_CXX17",
"_HAS_CXX20",
"_HAS_CXX23"
)
add_rules("@levibuildscript/linkrule") add_rules("@levibuildscript/linkrule")
set_exceptions("none") add_rules("@levibuildscript/modpacker")
add_cxflags( "/EHa", "/utf-8", "/W4", "/w44265", "/w44289", "/w44296", "/w45263", "/w44738", "/w45204")
add_defines("NOMINMAX", "UNICODE")
add_packages("levilamina","gmlib","modapi")
set_exceptions("none") -- To avoid conflicts with /EHa.
set_kind("shared") set_kind("shared")
set_languages("cxx20") set_languages("c++23")
set_symbols("debug") set_symbols("debug")
add_headerfiles("src/**.h")
after_build(function (target) add_files("src/**.cpp")
local mod_packer = import("scripts.after_build") add_includedirs("src")
local mod_define = {
modName = target:name(),
modFile = path.filename(target:targetfile()),
}
mod_packer.pack_mod(target,mod_define)
end)