45 Commits

18 changed files with 489 additions and 406 deletions
+42 -21
View File
@@ -6,33 +6,54 @@ on:
jobs:
build:
runs-on: windows-latest
env:
XMAKE_BINARY_REPO: https://gitee.com/xmake-mirror/build-artifacts.git
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Checkout
uses: https://github.bibk.top/actions/checkout@v4
- name: Setup XMake
uses: https://git.soulmc.cn/wed15/github-action-setup-xmake@v1
- name: Cache
uses: https://github.bibk.top/actions/cache@v2
with:
submodules: recursive
path: |
~/AppData/Local/.xmake
key: xmake-${{ hashFiles('xmake.lua') }}
restore-keys: |
xmake-
- uses: actions/checkout@v4
- name: Configure Mirror
run: |
$fileName = "github_mirror.lua"
$currentDir = Get-Location
$absoluteFilePath = Join-Path $currentDir $fileName
$luaContent = @'
function mirror(url)
url = url:gsub("github.com", "github.bibk.top")
return url
end
function main(url, host)
return true
end
'@
$luaContent | Out-File -FilePath $absoluteFilePath -Encoding utf8
Write-Host "Mirror config generated at: $absoluteFilePath"
xmake g --proxy_pac=$absoluteFilePath
shell: powershell
- uses: xmake-io/github-action-setup-xmake@v1
- name: Update Repo
run: xmake repo -u
- run: |
xmake repo -u
- name: Update Project
run: xmake f -a x64 -m release -p windows -v -y
- run: |
xmake f -a x64 -m release -p windows -v -y
- name: Build Project
run: xmake -v -y
- run: |
xmake -w -y
- uses: actions/upload-artifact@v3
- name: Upload Artifact
uses: https://github.bibk.top/actions/upload-artifact@v3
with:
name: ${{ github.event.repository.name }}-windows-x64-${{ github.sha }}
path: |
bin/DLL/
- uses: actions/upload-artifact@v3
with:
name: PDB
path: |
bin/PDB/
path: bin/
+54 -41
View File
@@ -6,36 +6,58 @@ on:
jobs:
build:
runs-on: windows-latest
env:
XMAKE_BINARY_REPO: https://gitee.com/xmake-mirror/build-artifacts.git
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Checkout
uses: https://github.bibk.top/actions/checkout@v4
- name: Setup XMake
uses: https://git.soulmc.cn/wed15/github-action-setup-xmake@v1
- name: Cache
uses: https://github.bibk.top/actions/cache@v2
with:
submodules: recursive
path: |
~/AppData/Local/.xmake
key: xmake-${{ hashFiles('xmake.lua') }}
restore-keys: |
xmake-
- uses: actions/checkout@v4
- name: Configure Mirror
run: |
$fileName = "github_mirror.lua"
$currentDir = Get-Location
$absoluteFilePath = Join-Path $currentDir $fileName
$luaContent = @'
function mirror(url)
url = url:gsub("github.com", "github.bibk.top")
return url
end
function main(url, host)
return true
end
'@
$luaContent | Out-File -FilePath $absoluteFilePath -Encoding utf8
Write-Host "Mirror config generated at: $absoluteFilePath"
xmake g --proxy_pac=$absoluteFilePath
shell: powershell
- uses: xmake-io/github-action-setup-xmake@v1
- name: Update Repo
run: xmake repo -u
- run: |
xmake repo -u
- name: Update Project
run: xmake f -a x64 -m release -p windows -v -y
- run: |
xmake f -a x64 -m release -p windows -v -y
- name: Build Project
run: xmake -v -y
- run: |
xmake -w -y
- uses: actions/upload-artifact@v3
- name: Upload Artifact
uses: https://github.bibk.top/actions/upload-artifact@v3
with:
name: ${{ github.event.repository.name }}-windows-x64-${{ github.sha }}
path: |
bin/DLL/
- uses: actions/upload-artifact@v3
with:
name: PDB
path: |
bin/PDB/
bin/
upload-to-release:
needs:
@@ -44,35 +66,26 @@ jobs:
contents: write
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Checkout
uses: https://github.bibk.top/actions/checkout@v4
- name: Download Plugin
uses: actions/download-artifact@v3
- name: Download Artifact
uses: https://github.bibk.top/actions/download-artifact@v3
with:
name: ${{ github.event.repository.name }}-windows-x64-${{ github.sha }}
path: release/Plugin/
path: artifact
- name: Download PDB
uses: actions/download-artifact@v3
with:
name: PDB
path: release/PDB/
- name: Copy additional files
- name: Copy Files
run: |
cp LICENSE README.md release/Plugin/
cp LICENSE README.md artifact/
- name: Archive release
- name: Compress Artifact
run: |
cd release/Plugin
zip -r ../${{ github.event.repository.name }}-windows-x64.zip *
cd ..
working-directory: artifact
- name: Create GitHub Release
id: create_release
uses: softprops/action-gh-release@v1
- name: Create Release
uses: https://github.bibk.top/softprops/action-gh-release@v1
with:
files: |
release/${{ github.event.repository.name }}-windows-x64.zip
release/PDB/${{ github.event.repository.name }}.pdb
${{ github.event.repository.name }}-windows-x64.zip
+2
View File
@@ -15,3 +15,5 @@ build/
/.xmake
/CMakeLists.txt
/bin
.cache/clangd/index
/.idea
Submodule SDK-GMLIB deleted from cda5bfbf85
+3 -3
View File
@@ -1,7 +1,7 @@
{
"name": "${pluginName}",
"entry": "${pluginFile}",
"version": "0.9.3",
"name": "${modName}",
"entry": "${modFile}",
"version": "0.16.1",
"author": "GroupMountain",
"description": "Check Player's Inventory",
"type": "native",
+7 -7
View File
@@ -82,7 +82,7 @@ function string_formatter(str, variables)
end)
end
function pack_plugin(target,plugin_define)
function pack_mod(target,mod_define)
import("lib.detect.find_file")
local manifest_path = find_file("manifest.json", os.projectdir())
@@ -90,9 +90,9 @@ function pack_plugin(target,plugin_define)
local manifest = io.readfile(manifest_path)
local bindir = path.join(os.projectdir(), "bin/DLL")
local pdbdir = path.join(os.projectdir(), "bin/PDB")
local outputdir = path.join(bindir, plugin_define.pluginName)
local targetfile = path.join(outputdir, plugin_define.pluginFile)
local pdbfile = path.join(pdbdir, path.basename(plugin_define.pluginFile) .. ".pdb")
local outputdir = path.join(bindir, mod_define.modName)
local targetfile = path.join(outputdir, mod_define.modFile)
local pdbfile = path.join(pdbdir, path.basename(mod_define.modFile) .. ".pdb")
local manifestfile = path.join(outputdir, "manifest.json")
local oritargetfile = target:targetfile()
local oripdbfile = path.join(path.directory(oritargetfile), path.basename(oritargetfile) .. ".pdb")
@@ -103,9 +103,9 @@ function pack_plugin(target,plugin_define)
os.cp(oripdbfile, pdbfile)
end
formattedmanifest = string_formatter(manifest, plugin_define)
formattedmanifest = string_formatter(manifest, mod_define)
io.writefile(manifestfile,formattedmanifest)
cprint("${bright green}[Plugin Packer]: ${reset}plugin already generated to " .. outputdir)
cprint("${bright green}[Mod Packer]: ${reset}mod already generated to " .. outputdir)
else
cprint("${bright yellow}warn: ${reset}not found manifest.json in root dir!")
end
@@ -113,7 +113,7 @@ end
return {
pack_plugin = pack_plugin,
pack_mod = pack_mod,
beautify_json = beautify_json,
string_formatter = string_formatter
}
+12 -4
View File
@@ -1,18 +1,26 @@
#include "Global.h"
#include "mc/server/commands/CommandRegistry.h"
#include "mc/server/commands/CommandOutput.h"
#include "ll/api/command/CommandHandle.h"
#include "ll/api/service/Bedrock.h"
#include "gmlib/mc/world/actor/Actor.h"
void RegisterCommand() {
auto& cmd = ll::command::CommandRegistrar::getInstance().getOrCreateCommand(
auto& cmd = ll::command::CommandRegistrar::getInstance(false).getOrCreateCommand(
"inventorycheck",
tr("command.inventorycheck.desc"),
CommandPermissionLevel::GameDirectors
);
ll::service::getCommandRegistry()->registerAlias("inventorycheck", "ic");
cmd.overload().execute<[&](CommandOrigin const& origin, CommandOutput& output) {
auto entity = (GMLIB_Actor*)origin.getEntity();
cmd.overload().execute([](CommandOrigin const& origin, CommandOutput& output) {
auto entity = (gmlib::GMActor*)origin.getEntity();
if (entity && entity->isPlayer()) {
auto pl = (Player*)entity;
return mainForm(*pl);
}
return output.error(tr("command.error.console"));
}>();
});
}
+5 -3
View File
@@ -1,6 +1,8 @@
#pragma once
#include "Global.h"
std::string defaultConfig = R"({
"language": "en_US"
})";
struct Config {
int version = 1;
std::string language = "en_US";
};
+43 -36
View File
@@ -1,58 +1,65 @@
#include "Entry.h"
#include "Global.h"
#include "Language.h"
ll::Logger logger(PLUGIN_NAME);
#include "ll/api/Config.h"
#include "ll/api/Versions.h"
#include "ll/api/mod/RegisterHelper.h"
#include "gmlib/gm/i18n/LangI18n.h"
#include "gmlib/gm/i18n/JsonI18n.h"
#include "gmlib/gm/data/UserCache.h"
#include "ll/api/service/Bedrock.h"
#include "ll/api/service/PlayerInfo.h"
namespace InventoryCheck {
namespace {
Entry& Entry::getInstance() {
static Entry instance;
return instance;
}
std::unique_ptr<std::reference_wrapper<ll::plugin::NativePlugin>>
selfPluginInstance; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
bool Entry::load() {
mConfig.emplace();
if (!ll::config::loadConfig(*mConfig, getSelf().getConfigDir() / u8"config.json")) {
ll::config::saveConfig(*mConfig, getSelf().getConfigDir() / u8"config.json");
}
mI18n.emplace(getSelf().getLangDir(), mConfig->language);
mI18n->updateOrCreateLanguage("en_US", en_US);
mI18n->updateOrCreateLanguage("zh_CN", zh_CN);
mI18n->loadAllLanguages();
auto disable(ll::plugin::NativePlugin& /*self*/) -> bool { return true; }
if (ll::getNetworkProtocolVersion() != TARGET_PROTOCOL) {
getSelf().getLogger().error(tr("error.protocolMismatch.info"));
getSelf().getLogger().error(tr("error.protocolMismatch.version", {std::to_string(ll::getNetworkProtocolVersion()), std::to_string(TARGET_PROTOCOL)}));
}
return true;
}
;
auto enable(ll::plugin::NativePlugin& /*self*/) -> bool {
bool Entry::enable() {
RegisterCommand();
listenEvent();
logger.info("InventoryCheck Loaded!");
logger.info("Author: GroupMountain");
logger.info("Repository: https://github.com/GroupMountain/InventoryCheck");
return true;
}
auto load(ll::plugin::NativePlugin& self) -> bool {
selfPluginInstance = std::make_unique<std::reference_wrapper<ll::plugin::NativePlugin>>(self);
initPlugin();
initPlayerDataCache();
bool Entry::disable() {
// Code for disabling the mod goes here.
return true;
}
auto unload(ll::plugin::NativePlugin& self) -> bool {
selfPluginInstance.reset();
return true;
}
} // namespace
auto getSelfPluginInstance() -> ll::plugin::NativePlugin& {
if (!selfPluginInstance) {
throw std::runtime_error("selfPluginInstance is null");
}
return *selfPluginInstance;
}
Config& Entry::getConfig() { return mConfig.value(); }
LangI18n& Entry::getI18n() { return mI18n.value(); }
} // namespace InventoryCheck
extern "C" {
_declspec(dllexport) auto ll_plugin_disable(ll::plugin::NativePlugin& self) -> bool {
return InventoryCheck::disable(self);
}
_declspec(dllexport) auto ll_plugin_enable(ll::plugin::NativePlugin& self) -> bool {
return InventoryCheck::enable(self);
}
_declspec(dllexport) auto ll_plugin_load(ll::plugin::NativePlugin& self) -> bool { return InventoryCheck::load(self); }
_declspec(dllexport) auto ll_plugin_unload(ll::plugin::NativePlugin& self) -> bool {
return InventoryCheck::unload(self);
}
LL_REGISTER_MOD(InventoryCheck::Entry, InventoryCheck::Entry::getInstance());
std::string tr(std::string const& key, std::vector<std::string> const& data) {
return InventoryCheck::Entry::getInstance().getI18n().get(key, data);
}
+41 -2
View File
@@ -1,9 +1,48 @@
#pragma once
#include <span>
#include <ll/api/plugin/NativePlugin.h>
#include "Config.h"
#include "Global.h"
#include "gmlib/gm/i18n/LangI18n.h"
#include "gmlib/gm/data/UserCache.h"
#include <memory>
namespace InventoryCheck {
[[nodiscard]] auto getSelfPluginInstance() -> ll::plugin::NativePlugin&;
using namespace gmlib::i18n;
class Entry {
public:
static Entry& getInstance();
Entry() : mSelf((*ll::mod::NativeMod::current())) {}
[[nodiscard]] ll::mod::NativeMod& getSelf() const { return mSelf; }
/// @return True if the mod is loaded successfully.
bool load();
/// @return True if the mod is enabled successfully.
bool enable();
/// @return True if the mod is disabled successfully.
bool disable();
/// @return True if the mod is unloaded successfully.
Config& getConfig();
LangI18n& getI18n();
std::vector<gmlib::UserCache::UserCacheEntry> userCache;
private:
ll::mod::NativeMod& mSelf;
std::optional<Config> mConfig;
std::optional<LangI18n> mI18n;
};
} // namespace InventoryCheck
+16 -16
View File
@@ -1,28 +1,28 @@
#pragma once
#include <include_all.h>
#define PLUGIN_NAME "InventoryCheck"
#include <span>
extern ll::Logger logger;
#include "mc/world/actor/player/Player.h"
extern std::string tr(std::string key, std::vector<std::string> data = {});
#include <string>
#include <vector>
#define MOD_NAME "InventoryCheck"
#define TARGET_PROTOCOL 944
extern std::string tr(std::string const& key, std::vector<std::string> const& data = {});
extern void initPlugin();
extern void RegisterCommand();
extern void mainForm(Player& pl);
extern void checkOnlineForm(Player& pl);
extern void checkAllForm(Player& pl);
extern void searchPlayerForm(Player& pl);
extern void searchResultForm(Player& pl, std::unordered_map<mce::UUID, std::string> resultList);
extern void searchNotFoundForm(Player& pl, std::string& name);
extern void checkPlayerForm(Player& pl, mce::UUID uuid);
extern void searchResultForm(Player& pl, std::unordered_map<mce::UUID, std::string> const& resultList);
extern void searchNotFoundForm(Player& pl, std::string const& name);
extern void checkPlayerForm(Player& pl, mce::UUID const& uuid);
extern void invalidInputForm(Player& pl);
extern void confirmWriteForm(Player& pl, mce::UUID uuid, std::string name);
extern void confirmDeleteForm(Player& pl, mce::UUID uuid, std::string name);
extern void deleteFailedForm(Player& pl, mce::UUID uuid, std::string name);
extern void listenEvent();
extern void initPlayerDataCache();
extern std::optional<std::string> getNameFromUuid(std::string uuid);
extern void confirmWriteForm(Player& pl, mce::UUID const& uuid, std::string const& name);
extern void confirmDeleteForm(Player& pl, mce::UUID const& uuid, std::string const& name);
extern void deleteFailedForm(Player& pl, mce::UUID const& uuid, std::string const& name);
-65
View File
@@ -1,65 +0,0 @@
#include "Config.h"
#include "Global.h"
#include "Language.h"
GMLIB::Files::JsonConfig* Config = nullptr;
GMLIB::Files::I18n::LangI18n* Language = nullptr;
nlohmann::json mPlayerDataCache;
std::optional<nlohmann::json> readFile(std::string path) {
if (std::filesystem::exists(path)) {
return GMLIB::Files::JsonFile::readFromFile(path);
}
return {};
}
void initPlayerDataCache() {
mPlayerDataCache = nlohmann::json::object();
auto data = readFile("./plugins/InventoryCheck/data/PlayerDataCache.json");
if (data.has_value()) {
try {
mPlayerDataCache = data.value();
} catch (...) {}
}
}
void saveCacheData() {
std::string path = "./plugins/InventoryCheck/data/PlayerDataCache.json";
GMLIB::Files::JsonFile::writeFile(path, mPlayerDataCache);
}
void initPlugin() {
Config = new GMLIB::Files::JsonConfig("./plugins/InventoryCheck/config/config.json", defaultConfig);
Config->init();
std::string langPath = "./plugins/InventoryCheck/language/";
std::string languageCode = Config->getValue<std::string>({"language"}, "en_US");
Language = new GMLIB::Files::I18n::LangI18n(langPath, languageCode);
Language->updateOrCreateLanguage("en_US", defaultLanguage_en_US);
Language->updateOrCreateLanguage("zh_CN", defaultLanguage_zh_CN);
Language->loadAllLanguages();
Language->chooseLanguage(languageCode);
if (!std::filesystem::exists("./plugins/InventoryCheck/data/")) {
std::filesystem::create_directories("./plugins/InventoryCheck/data/");
}
if (!std::filesystem::exists("./plugins/InventoryCheck/save/")) {
std::filesystem::create_directories("./plugins/InventoryCheck/save/");
}
}
std::string tr(std::string key, std::vector<std::string> data) { return Language->translate(key, data); }
void listenEvent() {
auto& eventBus = ll::event::EventBus::getInstance();
eventBus.emplaceListener<ll::event::player::PlayerConnectEvent>([](ll::event::player::PlayerConnectEvent& ev) {
auto& pl = ev.self();
mPlayerDataCache[pl.getUuid().asString()] = pl.getRealName();
saveCacheData();
});
}
std::optional<std::string> getNameFromUuid(std::string uuid) {
if (mPlayerDataCache.contains(uuid)) {
return mPlayerDataCache[uuid].get<std::string>();
}
return {};
}
+141 -81
View File
@@ -1,58 +1,92 @@
#include "Global.h"
#include "Entry.h"
bool saveInventory(Player& player) {
auto& pl = static_cast<GMLIB_Player&>(player);
#include <filesystem>
#include <unordered_map>
#include "mc/world/level/Level.h"
#include "mc/world/level/storage/DBStorage.h"
#include "mc/world/actor/player/PlayerListEntry.h"
#include "ll/api/service/Bedrock.h"
#include "ll/api/io/FileUtils.h"
#include "ll/api/form/ModalForm.h"
#include "ll/api/form/SimpleForm.h"
#include "ll/api/form/CustomForm.h"
#include "gmlib/mc/world/actor/OfflinePlayer.h"
#include <ll/api/service/PlayerInfo.h>
/**
* @brief the keys of the playr nbt that would be copied or written
*/
static constexpr const char* InvKey[] = {
"Armor","Offhand","Inventory","EnderChestInventory"
};
void saveInventory(Player& player) {
auto& pl = static_cast<gmlib::GMPlayer&>(player);
auto uuid = pl.getUuid();
std::string path = "./plugins/InventoryCheck/save/" + uuid.asString() + ".dat";
if (!std::filesystem::exists(path)) {
auto nbt = pl.getNbt()->toBinaryNbt();
std::ofstream newFile(path, std::ios::out | std::ios::binary);
if (newFile.is_open()) {
newFile.write(nbt.c_str(), nbt.size());
newFile.close();
ll::file_utils::writeFile(
"./plugins/InventoryCheck/save/" + uuid.asString() + ".dat",
pl.getNbt()->toBinaryNbt(),
true
);
}
/**
* @brief set the playerNBT easily and validly
*/
bool validSetNbt(mce::UUID uuid, const std::unique_ptr<CompoundTag>& nbt) {
if (!nbt) return false;
if (auto* p = ll::service::getLevel()->getPlayer(uuid)) {
auto& pl = static_cast<gmlib::GMPlayer&>(*p);
pl.setNbt(*nbt);
pl.refreshInventory();
return true;
}
}
auto db = ll::service::getDBStorage();
if (!db) return false;
auto tag = db->getCompoundTag("player_" + uuid.asString(), DBHelpers::Category::Player);
if (!tag || !tag->contains("ServerId"))
return false;
gmlib::OfflinePlayer(uuid, tag->at("ServerId")).setNbt(*nbt);
return true;
}
bool resumeInventory(Player& player) {
auto& pl = static_cast<GMLIB_Player&>(player);
auto& pl = static_cast<gmlib::GMPlayer&>(player);
auto uuid = pl.getUuid();
std::string path = "./plugins/InventoryCheck/save/" + uuid.asString() + ".dat";
if (std::filesystem::exists(path)) {
std::ifstream dataFile(path, std::ios::binary);
if (dataFile.is_open()) {
dataFile.seekg(0, std::ios::end);
std::streampos fileSize = dataFile.tellg();
dataFile.seekg(0, std::ios::beg);
std::vector<char> buffer(fileSize);
dataFile.read(buffer.data(), fileSize);
std::string_view binaryData(buffer.data(), fileSize);
dataFile.close();
if (auto binaryData = ll::file_utils::readFile(path, true)) {
std::filesystem::remove(path);
auto nbt = CompoundTag::fromBinaryNbt(binaryData);
if (nbt) {
GMLIB_Player::setPlayerNbtTags(uuid, *nbt, {"Offhand", "Inventory", "Armor", "EnderChestInventory"});
if (auto nbt = CompoundTag::fromBinaryNbt(binaryData.value())) {
auto playerNbt=pl.getNbt();
for (auto key : InvKey)
playerNbt->mTags[key] = nbt->at(key);
pl.setNbt(*playerNbt);
pl.refreshInventory();
return true;
}
}
}
return false;
}
void mainForm(Player& pl) {
auto fm = ll::form::SimpleForm(tr("form.main.title"), tr("form.main.content"));
fm.appendButton(tr("form.main.checkOnline"), [](Player& pl) { return checkOnlineForm(pl); });
fm.appendButton(tr("form.main.checkAll"), [](Player& pl) { return checkAllForm(pl); });
fm.appendButton(tr("form.main.searchPlayer"), [](Player& pl) { return searchPlayerForm(pl); });
fm.appendButton(tr("form.main.resumeInventory"), [](Player& pl) {
auto res = resumeInventory(pl);
if (res) {
return pl.sendMessage(tr("resumeInventory.success"));
}
return pl.sendMessage(tr("resumeInventory.failed"));
pl.sendMessage(resumeInventory(pl)
? tr("resumeInventory.success")
: tr("resumeInventory.failed"));
});
fm.sendTo(pl);
}
@@ -60,7 +94,7 @@ void mainForm(Player& pl) {
void checkOnlineForm(Player& pl) {
auto fm = ll::form::SimpleForm(tr("form.checkList.title"), tr("form.checkList.content"));
std::unordered_map<mce::UUID, std::string> onlineList;
GMLIB_Level::getLevel()->forEachPlayer([&onlineList](Player& pl) -> bool {
ll::service::getLevel()->forEachPlayer([&onlineList](Player& pl) -> bool {
if (!pl.isSimulated()) {
onlineList[pl.getUuid()] = pl.getRealName();
}
@@ -74,32 +108,33 @@ void checkOnlineForm(Player& pl) {
if (onlineList.size() == 1) {
return pl.sendMessage(tr("checkOnline.empty"));
}
fm.sendTo(pl, [](Player& pl, int index) {
fm.sendTo(pl, [](Player& pl, int index, ll::form::FormCancelReason) {
if (index == -1) {
return mainForm(pl);
}
});
}
std::string getNameFormUuid(mce::UUID& uuid) {
auto player = GMLIB_Level::getLevel()->getPlayer(uuid);
std::string getNameFormUuid(mce::UUID const& uuid) {
auto player = ll::service::getLevel()->getPlayer(uuid);
std::string name;
if (player) {
name = player->getRealName();
} else {
auto strUuid = uuid.asString();
auto cachename = getNameFromUuid(strUuid);
if (cachename.has_value()) {
name = cachename.value();
} else {
name = strUuid;
}
auto cache = ll::service::PlayerInfo::getInstance().fromUuid(uuid);
name = cache ? cache->name : uuid.asString();
}
return name;
}
std::unordered_map<mce::UUID, std::string> generateUuidMap() {
auto allUuids = GMLIB_Player::getAllUuids();
std::vector<mce::UUID> allUuids;
for (const auto& entry : ll::service::PlayerInfo::getInstance().entries()) {
allUuids.push_back(entry.uuid);
}
for (const auto& player : ll::service::getLevel()->getPlayerList()) {
allUuids.push_back(player.first);
}
std::unordered_map<mce::UUID, std::string> allList;
for (auto& uuid : allUuids) {
auto name = getNameFormUuid(uuid);
@@ -119,7 +154,7 @@ void checkAllForm(Player& pl) {
if (allList.size() == 1) {
return pl.sendMessage(tr("checkAll.empty"));
}
fm.sendTo(pl, [](Player& pl, int index) {
fm.sendTo(pl, [](Player& pl, int index, ll::form::FormCancelReason) {
if (index == -1) {
return mainForm(pl);
}
@@ -136,15 +171,15 @@ void searchPlayerForm(Player& pl) {
{tr("form.searchPlayer.fuzzySearch"), tr("form.searchPlayer.preciseSearch")},
0
);
fm.sendTo(pl, [](Player& pl, const ll::form::CustomFormResult& result) {
if (result.empty()) {
fm.sendTo(pl, [](Player& pl, ll::form::CustomFormResult const& result, ll::form::FormCancelReason) {
if (!result.has_value()) {
return mainForm(pl);
}
std::string inputName;
if (std::holds_alternative<std::string>(result.at("name"))) {
inputName = std::get<std::string>(result.at("name"));
if (std::holds_alternative<std::string>(result->at("name"))) {
inputName = std::get<std::string>(result->at("name"));
}
auto searchMode = std::get<std::string>(result.at("mode"));
auto searchMode = std::get<std::string>(result->at("mode"));
if (inputName.empty()) {
return invalidInputForm(pl);
}
@@ -170,45 +205,67 @@ void searchPlayerForm(Player& pl) {
});
}
void searchResultForm(Player& pl, std::unordered_map<mce::UUID, std::string> resultList) {
void searchResultForm(Player& pl, std::unordered_map<mce::UUID, std::string> const& resultList) {
auto fm = ll::form::SimpleForm(tr("form.checkList.title"), tr("form.checkList.content"));
for (auto& [uuid, name] : resultList) {
fm.appendButton(name, [&uuid](Player& pl) { checkPlayerForm(pl, uuid); });
fm.appendButton(name, [uuid](Player& pl) { checkPlayerForm(pl, uuid); });
}
fm.sendTo(pl, [](Player& pl, int index) {
fm.sendTo(pl, [](Player& pl, int index, ll::form::FormCancelReason) {
if (index == -1) {
return searchPlayerForm(pl);
}
});
}
void searchNotFoundForm(Player& pl, std::string& name) {
void searchNotFoundForm(Player& pl, std::string const& name) {
auto fm = ll::form::ModalForm(
tr("form.notFound.title"),
tr("form.notFound.content", {name}),
tr("form.notFound.returnSearch"),
tr("form.returnMainForm")
);
fm.sendTo(pl, [](Player& pl, ll::form::ModalForm::SelectedButton result) {
if (result == ll::form::ModalForm::SelectedButton::Upper) {
fm.sendTo(pl, [](Player& pl, ll::form::ModalFormResult result, ll::form::FormCancelReason) {
if (result == ll::form::ModalFormSelectedButton::Upper) {
return searchPlayerForm(pl);
}
return mainForm(pl);
});
}
/**
* @brief get the playerNBT conveninetly
*/
std::unique_ptr<CompoundTag> getNBTptr(mce::UUID uuid) {
if (auto player = ll::service::getLevel()->getPlayer(uuid))
return static_cast<gmlib::GMPlayer&>(*player).getNbt();
auto db = ll::service::getDBStorage();
if (!db) return nullptr;
auto playerTag = db->getCompoundTag(
"player_" + uuid.asString(),
DBHelpers::Category::Player
);
if (!playerTag||!playerTag->contains("ServerId")) return nullptr;
auto nbt =gmlib::OfflinePlayer(uuid, playerTag->at("ServerId")).getNbt();
return nbt ? std::make_unique<CompoundTag>(*nbt) : nullptr;
}
void checkPlayerForm(Player& pl, mce::UUID uuid) {
void checkPlayerForm(Player& pl, mce::UUID const& uuid) {
std::string name = getNameFormUuid(uuid);
auto fm = ll::form::SimpleForm(tr("form.checkPlayer.title"), tr("form.checkPlayer.content", {name}));
fm.appendButton(tr("form.checkPlayer.copyInventory"), [uuid, name](Player& pl) {
saveInventory(pl);
auto nbt = GMLIB_Player::getPlayerNbt(uuid);
if (!nbt) {
auto nbt =getNBTptr(uuid);
auto playerNbt=getNBTptr(pl.getUuid());
if ((!nbt)||(!playerNbt)) {
return pl.sendMessage(tr("checkPlayer.copyInventory.failed", {name}));
}
auto& self = static_cast<GMLIB_Player&>(pl);
GMLIB_Player::setPlayerNbtTags(pl.getUuid(), *nbt, {"Offhand", "Inventory", "Armor", "EnderChestInventory"});
return pl.sendMessage(tr("checkPlayer.copyInventory.success", {name}));
for (auto key : InvKey)
playerNbt->mTags[key] = nbt->at(key);
bool success= validSetNbt(pl.getUuid(), std::move(playerNbt));
pl.refreshInventory();
return pl.sendMessage(tr(success ? "checkPlayer.copyInventory.success" :
"checkPlayer.copyInventory.setFailed",{name}));
});
fm.appendButton(tr("form.checkPlayer.writeInventory"), [uuid, name](Player& pl) {
return confirmWriteForm(pl, uuid, name);
@@ -216,7 +273,7 @@ void checkPlayerForm(Player& pl, mce::UUID uuid) {
fm.appendButton(tr("form.checkPlayer.deleteData"), [uuid, name](Player& pl) {
return confirmDeleteForm(pl, uuid, name);
});
fm.sendTo(pl, [](Player& pl, int index) {
fm.sendTo(pl, [](Player& pl, int index, ll::form::FormCancelReason) {
if (index == -1) {
return mainForm(pl);
}
@@ -230,65 +287,68 @@ void invalidInputForm(Player& pl) {
tr("form.invalidInput.returnSearch"),
tr("form.returnMainForm")
);
fm.sendTo(pl, [](Player& pl, ll::form::ModalForm::SelectedButton result) {
if (result == ll::form::ModalForm::SelectedButton::Upper) {
fm.sendTo(pl, [](Player& pl, ll::form::ModalFormResult result, ll::form::FormCancelReason) {
if (result == ll::form::ModalFormSelectedButton::Upper) {
return searchPlayerForm(pl);
}
return mainForm(pl);
});
}
void confirmWriteForm(Player& pl, mce::UUID uuid, std::string name) {
void confirmWriteForm(Player& pl, mce::UUID const& uuid, std::string const& name) {
auto fm = ll::form::ModalForm(
tr("form.confirmWrite.title"),
tr("form.confirmWrite.content", {name}),
tr("form.confirmAction"),
tr("form.cancelAction")
);
fm.sendTo(pl, [uuid, name](Player& pl, ll::form::ModalForm::SelectedButton result) {
if (result == ll::form::ModalForm::SelectedButton::Upper) {
auto& self = static_cast<GMLIB_Player&>(pl);
fm.sendTo(pl, [uuid, name](Player& pl, ll::form::ModalFormResult result, ll::form::FormCancelReason) {
if (result == ll::form::ModalFormSelectedButton::Upper) {
auto& self = static_cast<gmlib::GMPlayer&>(pl);
auto nbt = self.getNbt();
auto targetNbt = GMLIB_Player::getPlayerNbt(uuid);
auto targetNbt = getNBTptr(uuid);
if (!targetNbt) {
return pl.sendMessage(tr("checkPlayer.writeInventory.failed", {name}));
}
GMLIB_Player::setPlayerNbtTags(uuid, *nbt, {"Offhand", "Inventory", "Armor", "EnderChestInventory"});
return pl.sendMessage(tr("checkPlayer.writeInventory.success", {name}));
for (auto key : InvKey)
targetNbt->mTags[key] = nbt->at(key);
bool success= validSetNbt(uuid, std::move(targetNbt));
pl.refreshInventory();
return pl.sendMessage(tr(success ? "checkPlayer.writeInventory.success" :
"checkPlayer.writeInventory.setFailed",{name}));
}
return checkPlayerForm(pl, uuid);
});
}
void confirmDeleteForm(Player& pl, mce::UUID uuid, std::string name) {
void confirmDeleteForm(Player& pl, mce::UUID const& uuid, std::string const& name) {
auto fm = ll::form::ModalForm(
tr("form.confirmDelete.title"),
tr("form.confirmDelete.content", {name}),
tr("form.confirmAction"),
tr("form.cancelAction")
);
fm.sendTo(pl, [uuid, name](Player& pl, ll::form::ModalForm::SelectedButton result) {
if (result == ll::form::ModalForm::SelectedButton::Upper) {
auto player = GMLIB_Level::getLevel()->getPlayer(uuid);
if (player) {
fm.sendTo(pl, [uuid, name](Player& pl, ll::form::ModalFormResult result, ll::form::FormCancelReason) {
if (result == ll::form::ModalFormSelectedButton::Upper) {
if (auto player = ll::service::getLevel()->getPlayer(uuid)) {
return deleteFailedForm(pl, uuid, name);
}
GMLIB_Player::deletePlayerNbt(uuid);
return pl.sendMessage(tr("checkPlayer.deleteNbt.success", {name}));
gmlib::OfflinePlayer::deletePlayerNbt(uuid);
return pl.sendMessage(tr("checkPlayer.deletePlayer.success", {name}));
}
return checkPlayerForm(pl, uuid);
});
}
void deleteFailedForm(Player& pl, mce::UUID uuid, std::string name) {
void deleteFailedForm(Player& pl, mce::UUID const& uuid, std::string const& name) {
auto fm = ll::form::ModalForm(
tr("form.deleteFailed.title"),
tr("form.deleteFailed.content", {name}),
tr("form.deleteFailed.returnCheck"),
tr("form.returnMainForm")
);
fm.sendTo(pl, [uuid](Player& pl, ll::form::ModalForm::SelectedButton result) {
if (result == ll::form::ModalForm::SelectedButton::Upper) {
fm.sendTo(pl, [uuid](Player& pl, ll::form::ModalFormResult result, ll::form::FormCancelReason) {
if (result == ll::form::ModalFormSelectedButton::Upper) {
return checkPlayerForm(pl, uuid);
}
return mainForm(pl);
+14 -6
View File
@@ -1,7 +1,7 @@
#pragma once
#include "Global.h"
#include <iostream>
std::string defaultLanguage_en_US = R"(
std::string en_US = R"(
form.main.title=Check Player Inventory
form.main.content=Please select a query mode.
form.main.checkOnline=Query online players
@@ -33,15 +33,17 @@ std::string defaultLanguage_en_US = R"(
command.inventorycheck.desc=Check player inventory.
checkPlayer.copyInventory.success=Successfully copied player %s's inventory to yours!
checkPlayer.copyInventory.failed=It is not possible to copy player %s's backpack to yours! \nPlayer %s doesn't have any data!
checkPlayer.copyInventory.setFailed=CANNOT to copy player %s's backpack to yours!
form.confirmWrite.title=Confirm Action
form.confirmWrite.content=Are you sure you want to cover player %s's backpack with your backpack? \n\nAttention! \nThis operation is irreversible!
form.confirmAction=Confirm
form.cancelAction=Cancel
checkPlayer.writeInventory.success=Successfully overwrote player %s's backpack!
checkPlayer.writeInventory.failed=Cannot cover player %s's backpack! nPlayer %s doesn't have any data!
checkPlayer.writeInventory.failed=Cannot cover player %s's backpack! \nPlayer %s doesn't have any data!
checkPlayer.writeInventory.setFailed=Cannot cover player %s's backpack!
form.confirmDelete.title=Confirm Delete Data
form.confirmDelete.content=Are you sure you want to delete all of player %s's data? \nThis contains all the player's data in the save! \nIt's not just a backpack! \n\nAttention! nThis operation is irreversible! \nPlease proceed with caution
checkPlayer.deleteNbt.success=Successfully deleted all of player %s's data!
checkPlayer.deletePlayer.success=Successfully deleted player from storage!
form.deleteFailed.title=Deletion failed
form.deleteFailed.content=Players %s online! You can't delete all of your players' data! \n\nIf you want to delete all player data, please kick the player out of the game before you do it!
form.deleteFailed.returnCheck=Return to the check menu
@@ -49,9 +51,11 @@ std::string defaultLanguage_en_US = R"(
checkAll.empty=You've played the entire archive alone, and you're still playing here to check the bag? \nWhy don't you go and recruit people?
resumeInventory.success=Your backpack has been resumed!
resumeInventory.failed=Unable to resume your backpack, no data was found on your backpack!
error.protocolMismatch.info=You are running on an unsupport protocol version! This may result in crash!
error.protocolMismatch.version=Support protocol %1$s, current protocol %2$s.
)";
std::string defaultLanguage_zh_CN = R"(
std::string zh_CN = R"(
form.main.title=
form.main.content=
form.main.checkOnline=线
@@ -83,15 +87,17 @@ std::string defaultLanguage_zh_CN = R"(
command.inventorycheck.desc=
checkPlayer.copyInventory.success= %s
checkPlayer.copyInventory.failed= %s \n玩家 %s
checkPlayer.copyInventory.setFailed= %s
form.confirmWrite.title=
form.confirmWrite.content= %s \n\n注意\n此操作不可逆
form.confirmAction=
form.cancelAction=
checkPlayer.writeInventory.success= %s
checkPlayer.writeInventory.failed= %s \n玩家 %s
checkPlayer.writeInventory.setFailed= %s
form.confirmDelete.title=
form.confirmDelete.content= %s \n这包含玩家在存档的全部数据\n不仅仅是背包\n\n注意\n此操作不可逆\n请谨慎操作
checkPlayer.deleteNbt.success= %s
checkPlayer.deletePlayer.success= %s
form.deleteFailed.title=
form.deleteFailed.content= %s 线\n\n如需删除全部玩家数据
form.deleteFailed.returnCheck=
@@ -99,4 +105,6 @@ std::string defaultLanguage_zh_CN = R"(
checkAll.empty=\n还不快去招人
resumeInventory.success=
resumeInventory.failed=
error.protocolMismatch.info=
error.protocolMismatch.version= %1$s %2$s
)";
+2 -2
View File
@@ -1,5 +1,5 @@
// This file will make your plugin use LeviLamina's memory operators by default.
// This improves the memory management of your plugin and is recommended to use.
// This file will make your mod use LeviLamina's memory operators by default.
// This improves the memory management of your mod and is recommended to use.
#define LL_MEMORY_OPERATORS
+31
View File
@@ -0,0 +1,31 @@
# 1. 定义文件名
$fileName = "github_mirror.lua"
# 2. 获取当前工作目录的绝对路径 (例如: D:\a\your-repo\your-repo)
$currentDir = Get-Location
# 3. 拼接出绝对文件路径 (例如: D:\a\your-re_repo\your-repo\github_mirror.lua)
$absoluteFilePath = Join-Path $currentDir $fileName
# 4. 定义 Lua 脚本内容
$luaContent = @'
function mirror(url)
-- 强制将 github.com 替换为镜像域名
url = url:gsub("github.com", "github.bibk.top")
return url
end
-- main 函数允许处理该 URL
function main(url, host)
return true
end
'@
# 5. 使用绝对路径写入文件
$luaContent | Out-File -FilePath $absoluteFilePath -Encoding utf8
# 6. 输出调试信息 (可选,确认路径是否正确)
Write-Host "Mirror config generated at: $absoluteFilePath"
# 7. 配置 Xmake 使用该绝对路径的配置文件
xmake g --proxy_pac=$absoluteFilePath
+24 -10
View File
@@ -1,29 +1,43 @@
{
"format_version": 2,
"format_version": 3,
"format_uuid": "289f771f-2c9a-4d73-9f3f-8492495a924d",
"tooth": "github.com/GroupMountain/InventoryCheck",
"version": "0.9.3",
"version": "0.16.0",
"info": {
"name": "InventoryCheck",
"description": "Check Player's Inventory",
"author": "GroupMountain",
"source": "github.com/GroupMountain/InventoryCheck",
"tags": [
"levilamina",
"inventory",
"platform:levilamina",
"type:mod",
"checkbag",
"gmlib"
]
},
"asset_url": "https://github.com/GroupMountain/InventoryCheck/releases/download/v0.9.3/InventoryCheck-windows-x64.zip",
"dependencies": {
"github.com/GroupMountain/GMLIB": ">=0.9.8"
},
"files": {
"place": [
"variants": [
{
"platform": "win-x64",
"dependencies": {
"github.com/GroupMountain/GMLIB-Release": ">=26.10.0"
},
"assets": [
{
"type": "self",
"urls": [],
"placements": [
{
"type": "dir",
"src": "InventoryCheck/*",
"dest": "plugins/InventoryCheck"
}
]
}
],
"remove_files": [
"plugins/InventoryCheck"
]
}
]
}
+15 -71
View File
@@ -1,82 +1,26 @@
add_rules("mode.debug", "mode.release")
add_repositories("liteldev-repo https://github.com/LiteLDev/xmake-repo.git")
add_repositories("groupmountain-repo https://github.com/GroupMountain/xmake-repo.git")
add_requires("levilamina 26.10.5", {configs = {target_type = "server"}})
add_requires("levibuildscript")
add_requires("gmlib 26.10.0")
if not has_config("vs_runtime") then
set_runtimes("MD")
end
-- Option 1: Use the latest version of LeviLamina released on GitHub.
add_requires("levilamina")
-- Option 2: Use a specific version of LeviLamina released on GitHub.
-- add_requires("levilamina x.x.x")
-- Option 3: Use the latest commit of LeviLamina on GitHub.
-- -- Here, "develop" is the branch name. You can change it to any branch name you want.
-- add_requires("levilamina develop")
-- -- You can also use debug build of LeviLamina.
-- -- add_requires("levilamina develop", {debug = true})
-- package("levilamina")
-- add_urls("https://github.com/LiteLDev/LeviLamina.git")
-- add_deps("ctre 3.8.1")
-- add_deps("entt 3.12.2")
-- add_deps("fmt 10.1.1")
-- add_deps("gsl 4.0.0")
-- add_deps("leveldb 1.23")
-- add_deps("magic_enum 0.9.0")
-- add_deps("nlohmann_json 3.11.2")
-- add_deps("rapidjson 1.1.0")
-- add_deps("pcg_cpp 1.0.0")
-- add_deps("pfr 2.1.1")
-- add_deps("preloader 1.4.0")
-- add_deps("symbolprovider 1.1.0")
-- -- You may need to change this to the target BDS version of your choice.
-- add_deps("bdslibrary 1.20.50.03")
-- on_install(function (package)
-- import("package.tools.xmake").install(package)
-- end)
target("InventoryCheck") -- Change this to your plugin name.
add_cxflags(
"/EHa",
"/utf-8"
)
add_defines(
"NOMINMAX",
"UNICODE"
)
add_files(
"src/**.cpp"
)
add_links(
"SDK-GMLIB/Lib/GMLIB"
)
add_includedirs(
"SDK-GMLIB",
"src"
)
add_packages(
"levilamina"
)
add_shflags(
"/DELAYLOAD:bedrock_server.dll" -- Magic to import symbols from BDS
)
set_exceptions("none") -- To avoid conflicts with /EHa
target("InventoryCheck")
add_rules("@levibuildscript/linkrule")
add_rules("@levibuildscript/modpacker")
add_cxflags( "/EHa", "/utf-8", "/W4", "/w44265", "/w44289", "/w44296", "/w45263", "/w44738", "/w45204")
add_defines("NOMINMAX", "UNICODE")
add_packages("levilamina","gmlib")
set_exceptions("none") -- To avoid conflicts with /EHa.
set_kind("shared")
set_languages("c++23")
set_symbols("debug")
after_build(function (target)
local plugin_packer = import("scripts.after_build")
local plugin_define = {
pluginName = target:name(),
pluginFile = path.filename(target:targetfile()),
}
plugin_packer.pack_plugin(target,plugin_define)
end)
add_headerfiles("src/**.h")
add_files("src/**.cpp")
add_includedirs("src")