From 1b1340d919d52f1fdf801ce0f4b82e6bd37b0848 Mon Sep 17 00:00:00 2001 From: Tsubasa6848 <116721335+Tsubasa6848@users.noreply.github.com> Date: Fri, 16 Feb 2024 13:44:47 +0800 Subject: [PATCH] feat: vote clean --- src/Features/CleanTask.cpp | 3 - src/Features/Cleaner.cpp | 13 ++-- src/Features/Cleaner.h | 17 ++++- src/Features/VoteClean.cpp | 138 +++++++++++++++++++++++++++++++++++++ src/Global.h | 7 -- src/Language.h | 18 +++-- src/Plugin.cpp | 3 + src/RegisterCommand.cpp | 36 +++++----- 8 files changed, 193 insertions(+), 42 deletions(-) diff --git a/src/Features/CleanTask.cpp b/src/Features/CleanTask.cpp index 153ba09..1dabec9 100644 --- a/src/Features/CleanTask.cpp +++ b/src/Features/CleanTask.cpp @@ -11,9 +11,6 @@ bool mSendToast = true; namespace Cleaner { -using namespace ll::schedule; -using namespace ll::chrono_literals; - static std::shared_ptr> mAutoCleanTask; static std::shared_ptr> mCheckCleanTask; diff --git a/src/Features/Cleaner.cpp b/src/Features/Cleaner.cpp index 3fc9f36..41b290d 100644 --- a/src/Features/Cleaner.cpp +++ b/src/Features/Cleaner.cpp @@ -3,8 +3,6 @@ namespace Cleaner { void loadCleaner() { - ListenEvents(); - RegisterCommands(); if (Config->getValue({"ScheduleClean", "Enabled"}, false)) { Cleaner::AutoCleanTask(Config->getValue({"ScheduleClean", "CleanInterval"}, 3000)); } @@ -21,15 +19,14 @@ void loadCleaner() { ConfigFile::mSendToast = Config->getValue({"Basic", "SendToast"}, true); } -void unloadCleaner() { - stopAllTasks(); - delete Config; - delete Language; -} +void unloadCleaner() { stopAllTasks(); } void reloadCleaner() { unloadCleaner(); - initPlugin(); + Config->init(); + std::string languageCode = Config->getValue({"Basic", "Language"}, "en_US"); + Language->loadAllLanguages(); + Language->chooseLanguage(languageCode); loadCleaner(); } diff --git a/src/Features/Cleaner.h b/src/Features/Cleaner.h index d3859f0..f085db5 100644 --- a/src/Features/Cleaner.h +++ b/src/Features/Cleaner.h @@ -1,8 +1,13 @@ #pragma once #include "Global.h" +using namespace ll::schedule; +using namespace ll::chrono_literals; + namespace Cleaner { +extern ServerTimeScheduler scheduler; + extern int ExecuteClean(); extern int CountEntities(); @@ -10,5 +15,15 @@ extern void AutoCleanTask(int seconds); extern void CheckCleanTask(int max_entities, float min_tps); extern void ListenEvents(); extern void stopAllTasks(); +extern void CleanTask(); +extern void reloadCleaner(); +extern void loadCleaner(); +extern void unloadCleaner(); -} // namespace Cleaner \ No newline at end of file +} // namespace Cleaner + +namespace VoteClean { + +extern void voteCommandExecute(Player* pl); + +} \ No newline at end of file diff --git a/src/Features/VoteClean.cpp b/src/Features/VoteClean.cpp index e69de29..1cb10c6 100644 --- a/src/Features/VoteClean.cpp +++ b/src/Features/VoteClean.cpp @@ -0,0 +1,138 @@ +#include "Cleaner.h" +#include "Global.h" + +namespace VoteClean { + +bool hasVote = false; +bool canVote = true; +int playerCount = 0; + +std::unordered_map voteList; + +static std::shared_ptr> mAutoCleanTask; + +int getPlayerCount() { + int result = 0; + ll::service::getLevel()->forEachPlayer([&](Player& pl) -> bool { + if (!pl.isSimulatedPlayer()) { + result++; + } + return true; + }); + return result; +} + +void sendVoteForm(Player* pl) { + auto fm = ll::form::ModalForm( + tr("cleaner.vote.title"), + tr("cleaner.vote.subtitle", {pl->getRealName()}), + tr("cleaner.vote.ok"), + tr("cleaner.vote.no") + ); + ll::service::getLevel()->forEachPlayer([&](Player& pl) -> bool { + fm.sendTo(pl, [](Player& player, ll::form::ModalForm::SelectedButton button) { + switch (button) { + case ll::form::ModalForm::SelectedButton::Upper: { + voteList[player.getUuid()] = true; + player.sendMessage(tr("cleaner.vote.accept")); + return; + } + case ll::form::ModalForm::SelectedButton::Lower: { + voteList[player.getUuid()] = false; + player.sendMessage(tr("cleaner.vote.deny")); + return; + } + default: + return; + } + }); + return true; + }); +} + +void checkVote() { + float percentage = Config->getValue({"VoteClean", "Percentage"}, 50.0f) / 100.0f; + int voteCount = 0; + for (auto& key : voteList) { + if (key.second == true) { + voteCount++; + } + } + float result = ((float)voteCount) / ((float)playerCount); + if (result >= percentage) { + if (ConfigFile::mAnnounce) { + Helper::broadcastMessage(tr("cleaner.vote.succeed")); + } + if (ConfigFile::mSendToast) { + Helper::broadcastToast(tr("cleaner.vote.succeed")); + } + Cleaner::CleanTask(); + } else { + if (ConfigFile::mAnnounce) { + Helper::broadcastMessage(tr("cleaner.vote.failed")); + } + if (ConfigFile::mSendToast) { + Helper::broadcastToast(tr("cleaner.vote.failed")); + } + } + hasVote = false; + playerCount = 0; +} + +void voteClean(Player* pl) { + voteList.clear(); + canVote = false; + hasVote = true; + playerCount = getPlayerCount(); + if (ConfigFile::mAnnounce) { + Helper::broadcastMessage(tr("cleaner.vote.voteMessage", {pl->getRealName()})); + } + if (ConfigFile::mSendToast) { + Helper::broadcastToast(tr("cleaner.vote.voteMessage", {pl->getRealName()})); + } + sendVoteForm(pl); + auto cooldown = std::chrono::seconds::duration(Config->getValue({"VoteClean", "Cooldown"}, 120)); + auto waitTime = std::chrono::seconds::duration(Config->getValue({"VoteClean", "CheckDelay"}, 30)); + Cleaner::scheduler.add(cooldown, [] { canVote = false; }); + Cleaner::scheduler.add(waitTime, [] { checkVote(); }); +} + +void confirmForm(Player* pl) { + auto fm = ll::form::ModalForm( + tr("cleaner.vote.title"), + tr("cleaner.vote.confirmTubtitle"), + tr("cleaner.vote.confirmOk"), + tr("cleaner.vote.confirmNo") + ); + fm.sendTo(*pl, [](Player& player, ll::form::ModalForm::SelectedButton button) { + switch (button) { + case ll::form::ModalForm::SelectedButton::Upper: { + return voteClean(&player); + } + case ll::form::ModalForm::SelectedButton::Lower: { + return player.sendMessage(tr("cleaner.vote.cancel")); + } + default: + return; + } + }); +} + +void voteCommandExecute(Player* pl) { + if (!hasVote) { + if (canVote) { + confirmForm(pl); + } else { + pl->sendMessage(tr("cleaner.vote.cooldown")); + } + } else { + if (voteList.count(pl->getUuid())) { + pl->sendMessage(tr("cleaner.vote.voted")); + } else { + voteList[pl->getUuid()] = true; + pl->sendMessage(tr("cleaner.vote.accept")); + } + } +} + +} // namespace VoteClean \ No newline at end of file diff --git a/src/Global.h b/src/Global.h index cd89680..b2e88c2 100644 --- a/src/Global.h +++ b/src/Global.h @@ -20,13 +20,6 @@ extern bool mAnnounce; extern bool mSendToast; } // namespace ConfigFile -namespace Cleaner { -extern void CleanTask(); -extern void reloadCleaner(); -extern void loadCleaner(); -extern void unloadCleaner(); -} // namespace Cleaner - namespace Helper { extern void broadcastMessage(std::string msg); extern void broadcastToast(std::string msg); diff --git a/src/Language.h b/src/Language.h index df33c9e..903c545 100644 --- a/src/Language.h +++ b/src/Language.h @@ -28,11 +28,13 @@ std::string defaultLanguage_en_US = R"({ "cleaner.vote.deny": "You have declined entity cleaning.", "cleaner.vote.no": "Deny", "cleaner.vote.ok": "Agree", - "cleaner.vote.Subtitle": "Do you agree to clean server entities now?", - "cleaner.vote.Timeout": "Vote has expired!", + "cleaner.vote.subtitle": "%1$s lunched a clean vote.\n\n Do you agree to clean server entities now?", + "cleaner.vote.timeout": "Vote has expired!", "cleaner.vote.succeed": "Vote cleaning successful!", + "cleaner.vote.failed": "Vote cleaning did not passed!", "cleaner.vote.title": "Vote Cleaning", - "cleaner.vote.toastNotice": "Notification" + "cleaner.vote.voted": "You have been voted!", + "cleaner.vote.voteMessage": "%1$s lunched a clean vote. If you agree cleaning entities but did not received a form, please type command /voteclean to vote." })"; std::string defaultLanguage_zh_CN = R"({ @@ -53,7 +55,7 @@ std::string defaultLanguage_zh_CN = R"({ "cleaner.output.reload": "已重载Cleaner插件,部分配置可能需要重启服务器才能生效。", "cleaner.output.triggerAutoCleanCount": "检测到服务器实体过多!!当前服务器存在 %1$s 个可清理实体,已启动自动清理程序。", "cleaner.output.triggerAutoCleanTps": "当前服务器TPS过低!!服务器平均TPS %1$s\n系统已启动清理程序。", - "cleaner.vote.cooldown": "投票清理冷却...", + "cleaner.vote.cooldown": "投票清理正在冷却...", "cleaner.vote.cancel": "投票已取消!", "cleaner.vote.confirmNo": "我再想想", "cleaner.vote.confirmOk": "发起投票", @@ -62,9 +64,11 @@ std::string defaultLanguage_zh_CN = R"({ "cleaner.vote.deny": "你已拒绝实体清理。", "cleaner.vote.no": "拒绝", "cleaner.vote.ok": "同意", - "cleaner.vote.Subtitle": "你是否同意现在清理服务器实体?", - "cleaner.vote.Timeout": "投票已过期!", + "cleaner.vote.subtitle": "%1$s 发起了服务器实体清理投票!\n\n你是否同意现在清理服务器实体?", + "cleaner.vote.timeout": "投票已过期!", "cleaner.vote.succeed": "投票清理成功!", + "cleaner.vote.failed": "投票清理未通过。", "cleaner.vote.title": "投票清理", - "cleaner.vote.toastNotice": "通知" + "cleaner.vote.voted": "你已经投过票了!", + "cleaner.vote.voteMessage": "%1$s 发起了服务器实体清理投票!如果同意清理但是未收到表单,请输入命令 /voteclean 投票。拒绝清理请忽略此信息。" })"; \ No newline at end of file diff --git a/src/Plugin.cpp b/src/Plugin.cpp index fe6c6dd..cba81ae 100644 --- a/src/Plugin.cpp +++ b/src/Plugin.cpp @@ -1,3 +1,4 @@ +#include "Features/Cleaner.h" #include "Global.h" ll::Logger logger("Cleaner"); @@ -10,6 +11,8 @@ Plugin::Plugin(ll::plugin::NativePlugin& self) : mSelf(self) { } bool Plugin::enable() { + Cleaner::ListenEvents(); + RegisterCommands(); Cleaner::loadCleaner(); logger.info("Cleaner Loaded!"); logger.info("Author: GroupMountain"); diff --git a/src/RegisterCommand.cpp b/src/RegisterCommand.cpp index 9a321ad..32d0fc1 100644 --- a/src/RegisterCommand.cpp +++ b/src/RegisterCommand.cpp @@ -1,3 +1,4 @@ +#include "Features/Cleaner.h" #include "Global.h" struct CleanerParam { @@ -70,21 +71,24 @@ void RegCleanerCommand() { }>(); }; -void RegisterCommands() { - RegCleanerCommand(); - // RegVoteCommand(); +void RegVoteCommand() { + auto& cmd = ll::command::CommandRegistrar::getInstance().getOrCreateCommand( + Config->getValue({"VoteClean", "VoteCleanCommand"}, "voteclean"), + tr("cleaner.command.voteclean"), + CommandPermissionLevel::Any + ); + cmd.overload().execute<[&](CommandOrigin const& origin, CommandOutput& output) { + if (origin.getOriginType() == CommandOriginType::Player) { + auto pl = (Player*)origin.getEntity(); + return VoteClean::voteCommandExecute(pl); + } + return output.error(tr("cleaner.command.error.playerOnly")); + }>(); } -/* -void RegVoteCommand() { - auto command = DynamicCommand::createCommand(registry, "voteclean", "Clean entities", CommandPermissionLevel::Any); - command->addOverload(); - command->setCallback([](DynamicCommand const& cmd, - CommandOrigin const& origin, - CommandOutput& output, - std::unordered_map& result) { - // vote clean command - }); - DynamicCommand::setup(registry, std::move(command)); -} -*/ \ No newline at end of file +void RegisterCommands() { + RegCleanerCommand(); + if (Config->getValue({"VoteClean", "Enabled"}, false)) { + RegVoteCommand(); + } +} \ No newline at end of file