diff --git a/.gitignore b/.gitignore index 8c4868c..161fac9 100644 --- a/.gitignore +++ b/.gitignore @@ -56,3 +56,5 @@ _deps ## Project specific # Output files /bin/ + +.vscode \ No newline at end of file diff --git a/src/plugin/API.cpp b/src/plugin/API.cpp new file mode 100644 index 0000000..f3db965 --- /dev/null +++ b/src/plugin/API.cpp @@ -0,0 +1,291 @@ +#include "Event.h" +#include "LLMoney.h" +#include "Settings.h" +#include "ll/api/Logger.h" +#include "sqlitecpp/SQLiteCpp.h" +#include +#include + + +static std::unique_ptr db; +ll::Logger moneylog("LLMoney"); +#undef snprintf + +struct cleanSTMT { + SQLite::Statement& get; + + cleanSTMT(SQLite::Statement& g) : get(g) {} + + ~cleanSTMT() { + get.reset(); + get.clearBindings(); + } +}; + +void ConvertData(); + +bool initDB() { + try { + db = std::make_unique( + "plugins\\LLMoney\\economy.db", + SQLite::OPEN_CREATE | SQLite::OPEN_READWRITE + ); + db->exec("PRAGMA journal_mode = MEMORY"); + db->exec("PRAGMA synchronous = NORMAL"); + db->exec("CREATE TABLE IF NOT EXISTS money ( \ + XUID TEXT PRIMARY KEY \ + UNIQUE \ + NOT NULL, \ + Money NUMERIC NOT NULL \ + ) \ + WITHOUT ROWID; "); + db->exec("CREATE TABLE IF NOT EXISTS mtrans ( \ + tFrom TEXT NOT NULL, \ + tTo TEXT NOT NULL, \ + Money NUMERIC NOT NULL, \ + Time NUMERIC NOT NULL \ + DEFAULT(strftime('%s', 'now')), \ + Note TEXT \ + );"); + db->exec("CREATE INDEX IF NOT EXISTS idx ON mtrans ( \ + Time COLLATE BINARY COLLATE BINARY DESC \ + ); "); + } catch (std::exception const& e) { + moneylog.error("Database error: {}", e.what()); + return false; + } + ConvertData(); + return true; +} + +long long LLMoneyGet(std::string xuid) { + try { + SQLite::Statement get{*db, "select Money from money where XUID=?"}; + get.bindNoCopy(1, xuid); + long long rv = Settings::def_money; + bool fg = false; + while (get.executeStep()) { + rv = (long long)get.getColumn(0).getInt64(); + fg = true; + } + get.reset(); + get.clearBindings(); + if (!fg) { + SQLite::Statement set{*db, "insert into money values (?,?)"}; + set.bindNoCopy(1, xuid); + set.bind(2, Settings::def_money); + set.exec(); + set.reset(); + set.clearBindings(); + } + return rv; + } catch (std::exception const& e) { + moneylog.error("Database error: {}\n", e.what()); + return -1; + } +} + +bool isRealTrans = true; + +bool LLMoneyTrans(std::string from, std::string to, long long val, std::string const& note) { + bool isRealTrans = ::isRealTrans; + ::isRealTrans = true; + if (isRealTrans) + if (!CallBeforeEvent(LLMoneyEvent::Trans, from, to, val)) return false; + + if (val < 0 || from == to) return false; + try { + db->exec("begin"); + SQLite::Statement set{*db, "update money set Money=? where XUID=?"}; + if (from != "") { + auto fmoney = LLMoneyGet(from); + if (fmoney < val) { + db->exec("rollback"); + return false; + } + fmoney -= val; + { + + set.bindNoCopy(2, from); + set.bind(1, fmoney); + set.exec(); + set.reset(); + set.clearBindings(); + } + } + if (to != "") { + auto tmoney = LLMoneyGet(to); + tmoney += val; + if (tmoney < 0) { + db->exec("rollback"); + return false; + } + { + set.bindNoCopy(2, to); + set.bind(1, tmoney); + set.exec(); + set.reset(); + set.clearBindings(); + } + } + { + SQLite::Statement addTrans{*db, "insert into mtrans (tFrom,tTo,Money,Note) values (?,?,?,?)"}; + addTrans.bindNoCopy(1, from); + addTrans.bindNoCopy(2, to); + addTrans.bind(3, val); + addTrans.bindNoCopy(4, note); + addTrans.exec(); + addTrans.reset(); + addTrans.clearBindings(); + } + db->exec("commit"); + + if (isRealTrans) CallAfterEvent(LLMoneyEvent::Trans, from, to, val); + return true; + } catch (std::exception const& e) { + db->exec("rollback"); + moneylog.error("Database error: {}\n", e.what()); + return false; + } +} + +bool LLMoneyAdd(std::string xuid, long long money) { + if (!CallBeforeEvent(LLMoneyEvent::Add, "", xuid, money)) return false; + + isRealTrans = false; + bool res = LLMoneyTrans("", xuid, money, "add " + std::to_string(money)); + if (res) CallAfterEvent(LLMoneyEvent::Add, "", xuid, money); + return res; +} + +bool LLMoneyReduce(std::string xuid, long long money) { + if (!CallBeforeEvent(LLMoneyEvent::Reduce, "", xuid, money)) return false; + + isRealTrans = false; + bool res = LLMoneyTrans(xuid, "", money, "reduce " + std::to_string(money)); + if (res) CallAfterEvent(LLMoneyEvent::Reduce, "", xuid, money); + return res; +} + +bool LLMoneySet(std::string xuid, long long money) { + if (!CallBeforeEvent(LLMoneyEvent::Set, "", xuid, money)) return false; + long long now = LLMoneyGet(xuid), diff; + std::string from, to; + if (money >= now) { + from = ""; + to = xuid; + diff = money - now; + } else { + from = xuid; + to = ""; + diff = now - money; + } + + isRealTrans = false; + bool res = LLMoneyTrans(from, to, diff, "set to " + std::to_string(money)); + if (res) CallAfterEvent(LLMoneyEvent::Reduce, "", xuid, money); + return res; +} + +std::vector> LLMoneyRanking(unsigned short num) { + try { + SQLite::Statement get{*db, "select * from money ORDER BY money DESC LIMIT ?"}; + std::vector> mapTemp; + get.bind(1, num); + while (get.executeStep()) { + std::string xuid = get.getColumn(0).getString(); + long long balance = get.getColumn(1).getInt64(); + // std::cout << xuid << " " << balance << "\n"; + mapTemp.push_back(std::pair(xuid, balance)); + } + get.reset(); + get.clearBindings(); + return mapTemp; + } catch (std::exception const& e) { + moneylog.error("Database error: {}\n", e.what()); + return {}; + } +} + +std::string LLMoneyGetHist(std::string xuid, int timediff) { + try { + SQLite::Statement get{ + *db, + "select tFrom,tTo,Money,datetime(Time,'unixepoch', 'localtime'),Note from mtrans where " + "strftime('%s','now')-time from, to; + from = PlayerInfo::fromXuid(get.getColumn(0).getString()); + to = PlayerInfo::fromXuid(get.getColumn(1).getString()); + if (from.Set() && to.Set()) + if (from.val() == "") { + from.val() = "System"; + } else if (to.val() == "") { + to.val() = "System"; + } + rv += from.val() + " -> " + to.val() + " " + std::to_string((long long)get.getColumn(2).getInt64()) + " " + + get.getColumn(3).getText() + " (" + get.getColumn(4).getText() + ")\n"; + } + get.reset(); + get.clearBindings(); + return rv; + } catch (std::exception const& e) { + moneylog.error("Database error: {}\n", e.what()); + return "failed"; + } +} + +void LLMoneyClearHist(int difftime) { + try { + db->exec("DELETE FROM mtrans WHERE strftime('%s','now')-time>" + std::to_string(difftime)); + } catch (std::exception&) {} +} + +void ConvertData() { + if (std::filesystem::exists("plugins\\LLMoney\\money.db")) { + moneylog.info("Old money data detected, try to convert old data to new data"); + try { + std::unique_ptr db2 = std::make_unique( + "plugins\\LLMoney\\money.db", + SQLite::OPEN_CREATE | SQLite::OPEN_READWRITE + ); + SQLite::Statement get{*db2, "select hex(XUID),Money from money"}; + SQLite::Statement set{*db, "insert into money values (?,?)"}; + while (get.executeStep()) { + std::string blob = get.getColumn(0).getText(); + unsigned long long value; + std::istringstream iss(blob); + iss >> std::hex >> value; + unsigned long long xuid = _byteswap_uint64(value); + long long money = get.getColumn(1).getInt64(); + set.bindNoCopy(1, std::to_string(xuid)); + set.bind(2, money); + set.exec(); + set.reset(); + set.clearBindings(); + } + get.reset(); + } catch (std::exception& e) { + moneylog.error("{}", e.what()); + } + std::filesystem::rename("plugins\\LLMoney\\money.db", "plugins\\LLMoney\\money_old.db"); + moneylog.info("Conversion completed"); + } +} +// #include +// #define EXPORTAPI(T) RemoteCall::exportAs("LLMoney", #T, T); + +// void RemoteCallInit() { +// EXPORTAPI(LLMoneyGet); +// EXPORTAPI(LLMoneyTrans); +// EXPORTAPI(LLMoneyAdd); +// EXPORTAPI(LLMoneyReduce); +// EXPORTAPI(LLMoneySet); +// EXPORTAPI(LLMoneyGetHist); +// EXPORTAPI(LLMoneyClearHist); +// } \ No newline at end of file diff --git a/src/plugin/Event.cpp b/src/plugin/Event.cpp new file mode 100644 index 0000000..a7197f1 --- /dev/null +++ b/src/plugin/Event.cpp @@ -0,0 +1,28 @@ +#include "Event.h" +#include "LLMoney.h" +#include + +using namespace std; + +vector beforeCallbacks, afterCallbacks; + +bool CallBeforeEvent(LLMoneyEvent event, std::string from, std::string to, long long value) { + bool isCancelled = false; + for (auto& callback : beforeCallbacks) { + if (!callback(event, from, to, value)) { + isCancelled = true; + break; + } + } + return !isCancelled; +} + +void CallAfterEvent(LLMoneyEvent event, std::string from, std::string to, long long value) { + for (auto& callback : afterCallbacks) { + callback(event, from, to, value); + } +} + +void LLMoneyListenBeforeEvent(LLMoneyCallback callback) { beforeCallbacks.push_back(callback); } + +void LLMoneyListenAfterEvent(LLMoneyCallback callback) { afterCallbacks.push_back(callback); } \ No newline at end of file diff --git a/src/plugin/Event.h b/src/plugin/Event.h new file mode 100644 index 0000000..45dd52e --- /dev/null +++ b/src/plugin/Event.h @@ -0,0 +1,7 @@ +#pragma once + +#include "LLMoney.h" + +bool CallBeforeEvent(LLMoneyEvent event, std::string from, std::string to, long long value); + +void CallAfterEvent(LLMoneyEvent event, std::string from, std::string to, long long value); \ No newline at end of file diff --git a/src/plugin/LLMoney.cpp b/src/plugin/LLMoney.cpp new file mode 100644 index 0000000..b65dc6d --- /dev/null +++ b/src/plugin/LLMoney.cpp @@ -0,0 +1,500 @@ +#include "LLMoney.h" +#include "Plugin.h" +#include "Settings.h" +#include "ll/api/command/CommandRegistrar.h" +#include "ll/api/event/command/SetupCommandEvent.h" +#include "mc/server/ServerPlayer.h" +#include "mc/world/actor/player/Player.h" +#include "sqlitecpp/SQLiteCpp.h" + +ll::Logger logger("LegacyMoney"); + +#define JSON1(key, val) \ + if (json.find(key) != json.end()) { \ + const nlohmann::json& out = json.at(key); \ + out.get_to(val); \ + } + +namespace Settings { +std::string language = "en"; +int def_money = 0; +float pay_tax = 0.0; +bool enable_ranking = true; + +nlohmann::json globaljson() { + nlohmann::json json; + json["language"] = language; + json["def_money"] = def_money; + json["pay_tax"] = pay_tax; + json["enable_ranking"] = enable_ranking; + return json; +} + +void initjson(nlohmann::json json) { + JSON1("language", language); + JSON1("def_money", def_money); + JSON1("pay_tax", pay_tax); + JSON1("enable_ranking", enable_ranking); +} +void WriteDefaultConfig(const std::string& fileName) { + std::ofstream file(fileName); + if (!file.is_open()) { + logger.error("Can't open file {}", fileName); + return; + } + auto json = globaljson(); + file << json.dump(4); + file.close(); +} + +void LoadConfigFromJson(const std::string& fileName) { + std::ifstream file(fileName); + if (!file.is_open()) { + logger.error("Can't open file {}", fileName); + return; + } + nlohmann::json json; + file >> json; + file.close(); + initjson(json); + WriteDefaultConfig(fileName); +} +void reloadJson(const std::string& fileName) { + std::ofstream file(fileName); + if (file) { + file << globaljson().dump(4); + } else { + logger.error("Configuration File Creation failed!"); + } + file.close(); +} +} // namespace Settings + +bool initDB(); + +bool cmp(std::pair a, std::pair b) { return a.second > b.second; } + +class MoneyCommand : public Command { + enum MoneyOP : int { query = 1, hist = 2, pay = 3, set = 4, add = 5, reduce = 6, purge = 7, top = 8 } op; + std::string dst; + bool dst_isSet; + bool difftime_isSet; + int moneynum; + int difftime; + +public: + void execute(CommandOrigin const& ori, CommandOutput& outp) const { + std::string dstxuid, myuid; + switch (op) { + case query: + case hist: + if (dst_isSet && (int)ori.getPermissionsLevel() > 0) { + dstxuid = PlayerInfo::getXuid(dst); + } else { + if (ori.getOriginType() != (CommandOriginType)OriginType::Player) { + outp.error(tr("money.dontuseinconsole")); + return; + } + dstxuid = ori.getPlayer()->getXuid(); + } + if (dstxuid == "") { + outp.error(tr("money.no.target")); + return; + } + break; + case pay: + if (ori.getOriginType() != (CommandOriginType)OriginType::Player) { + outp.error(tr("money.dontuseinconsole")); + return; + } + case set: + case add: + case reduce: + dstxuid = PlayerInfo::getXuid(dst); + if (dstxuid == "") { + outp.error(tr("money.no.target")); + return; + } + break; + case purge: + if (ori.getOriginType() != (CommandOriginType)OriginType::Player) { + outp.error(tr("money.dontuseinconsole")); + return; + } + if ((int)ori.getPermissionsLevel() < 1) { + outp.error(tr("money.no.perm")); + return; + } + break; + case top: + if (!Settings::enable_ranking) { + outp.error("Balance ranking not enabled"); + return; + } + } + switch (op) { + case query: + outp.addMessage("Balance: " + std::to_string(LLMoneyGet(dstxuid))); + break; + case hist: + outp.addMessage(LLMoneyGetHist(dstxuid)); + break; + case pay: { + if (moneynum <= 0) { + outp.error(tr("money.invalid.arg")); + return; + } + myuid = ori.getPlayer()->getXuid(); + if (LLMoneyTrans(myuid, dstxuid, moneynum, "money pay")) { + long long fee = (long long)(moneynum * Settings::pay_tax); + if (fee) LLMoneyTrans(dstxuid, "", fee, "money pay fee"); + outp.success(tr("money.pay.succ")); + } else { + outp.error(tr("money.not.enough")); + } + return; + } break; + case set: + if ((int)ori.getPermissionsLevel() < 1) { + outp.error(tr("money.no.perm")); + return; + } + if (LLMoneySet(dstxuid, moneynum)) { + outp.success(tr("money.set.succ")); + } else { + outp.error(tr("money.invalid.arg")); + } + break; + case add: + if ((int)ori.getPermissionsLevel() < 1) { + outp.error(tr("money.no.perm")); + return; + } + if (LLMoneyAdd(dstxuid, moneynum)) { + outp.success(tr("money.add.succ")); + } else { + outp.error(tr("money.invalid.arg")); + } + break; + case reduce: + if ((int)ori.getPermissionsLevel() < 1) { + outp.error(tr("money.no.perm")); + return; + } + if (LLMoneyReduce(dstxuid, moneynum)) { + outp.success(tr("money.reduce.succ")); + } else { + outp.error(tr("money.invalid.arg")); + } + break; + case purge: + if (difftime_isSet) LLMoneyClearHist(difftime); + else LLMoneyClearHist(0); + break; + case top: + vector> mapTemp = LLMoneyRanking(); + sort(mapTemp.begin(), mapTemp.end(), cmp); + outp.success("===== Ranking ====="); + for (auto it = mapTemp.begin(); it != mapTemp.end(); it++) { + outp.addMessage( + (PlayerInfo::fromXuid(it->first).empty() ? "NULL" : PlayerInfo::fromXuid(it->first)) + " " + + std::to_string(it->second) + ); + } + outp.addMessage("==================="); + } + return; + } + + static void setup(CommandRegistry* registry) { + + // registerCommand + registry->registerCommand( + "money", + "Economy system", + CommandPermissionLevel::Any, + {(CommandFlagValue)0}, + {(CommandFlagValue)0x80} + ); + // addEnum + registry->addEnum( + "MoneyOP1", + { + {"query", MoneyOP::query}, + {"hist", MoneyOP::hist }, + {"top", MoneyOP::top } + } + ); + registry->addEnum( + "MoneyOP2", + { + {"add", MoneyOP::add }, + {"pay", MoneyOP::pay }, + {"reduce", MoneyOP::reduce}, + {"set", MoneyOP::set } + } + ); + registry->addEnum( + "MoneyOP3", + { + {"purge", MoneyOP::purge} + } + ); + + // registerOverload + registry->registerOverload( + "money", + makeMandatory(&MoneyCommand::op, "optional", "MoneyOP1"), + makeOptional(&MoneyCommand::dst, "PlayerName", &MoneyCommand::dst_isSet) + ); + registry->registerOverload( + "money", + makeMandatory(&MoneyCommand::op, "optional", "MoneyOP2"), + makeMandatory(&MoneyCommand::dst, "PlayerName"), + makeMandatory(&MoneyCommand::moneynum, "num") + ); + registry->registerOverload( + "money", + makeMandatory(&MoneyCommand::op, "optional", "MoneyOP3"), + makeOptional(&MoneyCommand::difftime, "time", &MoneyCommand::difftime_isSet) + ); + } +}; + +class MoneySCommand : public Command { + enum MoneyOP : int { + query = 1, + hist = 2, + pay = 3, + set = 4, + add = 5, + reduce = 6, + } op; + CommandSelector player; + bool dst_isSet; + bool difftime_isSet; + int moneynum; + int difftime; + +public: + void execute(CommandOrigin const& ori, CommandOutput& outp) const { + vector dstxuidlist; + optional dstxuid; + std::string myuid; + switch (op) { + case query: + case hist: + if (dst_isSet) { + if ((int)ori.getPermissionsLevel() > 0) { + if (!player.results(ori).empty()) { + dstxuid = player.results(ori).begin().operator*()->getXuid(); + } + } else { + outp.error("You don't have permission to do this"); + return; + } + } else { + if (ori.getOriginType() != (CommandOriginType)OriginType::Player) { + outp.error(tr("money.dontuseinconsole")); + return; + } + dstxuid = ori.getPlayer()->getXuid(); + } + if (dstxuid.val() == "") { + outp.error(tr("money.no.target")); + return; + } + break; + case pay: { + if (ori.getOriginType() != (CommandOriginType)OriginType::Player) { + outp.error(tr("money.dontuseinconsole")); + return; + } + if (player.results(ori).empty()) { + outp.error(tr("money.no.target")); + return; + } + for (auto resu : player.results(ori)) { + dstxuid = resu->getXuid(); + if (!dstxuid.Set()) { + outp.error(tr("money.no.target")); + return; + } + } + break; + } + case set: + case add: + case reduce: + if (player.results(ori).empty()) { + outp.error(tr("money.no.target")); + return; + } + for (auto resu : player.results(ori)) { + dstxuidlist.push_back(resu->getXuid()); + } + if (dstxuidlist.empty()) { + outp.error(tr("money.no.target")); + return; + } + break; + } + switch (op) { + case query: + outp.addMessage("Balance: " + std::to_string(LLMoneyGet(dstxuid.val()))); + break; + case hist: + outp.addMessage(LLMoneyGetHist(dstxuid.val())); + break; + case pay: + if (moneynum <= 0) { + outp.error(tr("money.invalid.arg")); + return; + } + myuid = ori.getPlayer()->getXuid(); + if (myuid == "") { + outp.error(tr("money.no.target")); + return; + } + if (LLMoneyTrans(myuid, dstxuid.val(), moneynum, "money pay")) { + long long fee = (long long)(moneynum * Settings::pay_tax); + if (fee) LLMoneyTrans(dstxuid.val(), "", fee, "money pay fee"); + outp.success(tr("money.pay.succ")); + } else { + outp.error(tr("money.not.enough")); + } + break; + case set: { + if ((int)ori.getPermissionsLevel() < 1) { + outp.error(tr("money.no.perm")); + return; + } + bool su = 0; + for (auto i : dstxuidlist) { + if (LLMoneySet(i, moneynum)) { + su = 1; + } + } + if (su) { + outp.success(tr("money.set.succ")); + } else { + outp.error(tr("money.invalid.arg")); + } + break; + } + case add: { + if ((int)ori.getPermissionsLevel() < 1) { + outp.error(tr("money.no.perm")); + return; + } + bool su = 0; + for (auto i : dstxuidlist) { + if (LLMoneyAdd(i, moneynum)) { + su = 1; + } + } + if (su) { + outp.success(tr("money.add.succ")); + } else { + outp.error(tr("money.invalid.arg")); + } + break; + } + case reduce: { + if ((int)ori.getPermissionsLevel() < 1) { + outp.error(tr("money.no.perm")); + return; + } + bool su = 0; + for (auto i : dstxuidlist) { + if (LLMoneyReduce(i, moneynum)) { + su = 1; + } + } + if (su) { + outp.success(tr("money.reduce.succ")); + } else { + outp.error(tr("money.invalid.arg")); + } + break; + } + } + return; + } + + static void setup(CommandRegistry* registry) { + // registerCommand + registry->registerCommand( + "money_s", + "Economy system(Selector)", + CommandPermissionLevel::Any, + {(CommandFlagValue)0}, + {(CommandFlagValue)0x80} + ); + + // addEnum + registry->addEnum( + "MoneyOP1", + { + {"query", MoneyOP::query}, + {"hist", MoneyOP::hist } + } + ); + registry->addEnum( + "MoneyOP2", + { + {"add", MoneyOP::add }, + {"pay", MoneyOP::pay }, + {"reduce", MoneyOP::reduce}, + {"set", MoneyOP::set } + } + ); + + // registerOverload + registry->registerOverload( + "money_s", + makeMandatory(&MoneySCommand::op, "optional", "MoneyOP1"), + makeOptional(&MoneySCommand::player, "PlayerName", &MoneySCommand::dst_isSet) + ); + + registry->registerOverload( + "money_s", + makeMandatory(&MoneySCommand::op, "optional", "MoneyOP2"), + makeMandatory(&MoneySCommand::player, "PlayerName"), + makeMandatory(&MoneySCommand::moneynum, "num") + ); + } +}; +#include "lang.h" + +void loadCfg() { + // config + if (!std::filesystem::exists("plugins/LLMoney")) std::filesystem::create_directories("plugins/LLMoney"); + if (std::filesystem::exists("plugins/LLMoney/money.json")) { + try { + Settings::LoadConfigFromJson("plugins/LLMoney/money.json"); + } catch (std::exception& e) { + logger.error("Configuration file is Invalid, Error: {}", e.what()); + } catch (...) { + logger.error("Configuration file is Invalid"); + } + } else { + Settings::WriteDefaultConfig("plugins/LLMoney/money.json"); + } +} + +// void RemoteCallInit(); +void entry() { + loadCfg(); + if (!initDB()) { + return; + } + Event::RegCmdEvent::subscribe([](const Event::RegCmdEvent& ev) { + MoneyCommand::setup(ev.mCommandRegistry); + MoneySCommand::setup(ev.mCommandRegistry); + return true; + }); + Translation::load("plugins\\LLMoney\\language.json", Settings::language, defaultLangData); + logger.info("Loaded version: {}", LLMONEY_VERSION.toString()); + RemoteCallInit(); +} diff --git a/src/plugin/LLMoney.h b/src/plugin/LLMoney.h new file mode 100644 index 0000000..89c4e5c --- /dev/null +++ b/src/plugin/LLMoney.h @@ -0,0 +1,45 @@ +#pragma once + +#include "ll/api/service/PlayerInfo.h" + +#ifdef LLMONEY_EXPORTS +#define LLMONEY_API __declspec(dllexport) +#else +#define LLMONEY_API __declspec(dllimport) +#endif + +#include + +enum LLMoneyEvent { Set, Add, Reduce, Trans }; + +typedef bool (*LLMoneyCallback)(LLMoneyEvent type, std::string from, std::string to, long long value); + +#ifdef __cplusplus +extern "C" { +#endif +LLMONEY_API long long LLMoneyGet(std::string xuid); +LLMONEY_API bool LLMoneySet(std::string xuid, long long money); +LLMONEY_API bool LLMoneyTrans(std::string from, std::string to, long long val, std::string const& note = ""); +LLMONEY_API bool LLMoneyAdd(std::string xuid, long long money); +LLMONEY_API bool LLMoneyReduce(std::string xuid, long long money); + +LLMONEY_API std::string LLMoneyGetHist(std::string xuid, int timediff = 24 * 60 * 60); +LLMONEY_API void LLMoneyClearHist(int difftime = 0); + +LLMONEY_API void LLMoneyListenBeforeEvent(LLMoneyCallback callback); +LLMONEY_API void LLMoneyListenAfterEvent(LLMoneyCallback callback); +#ifdef __cplusplus +} +#endif +LLMONEY_API std::vector> LLMoneyRanking(unsigned short num = 5); +// Old interface +// Just for compatibility +// Do not use +namespace Money { +LLMONEY_API long long getMoney(std::string xuid); +LLMONEY_API std::string getTransHist(std::string xuid, int timediff = 24 * 60 * 60); +LLMONEY_API bool createTrans(std::string from, std::string to, long long val, std::string const& note = ""); +LLMONEY_API bool setMoney(std::string xuid, long long money); +LLMONEY_API bool reduceMoney(std::string xuid, long long money); +LLMONEY_API void purgeHist(int difftime = 0); +} // namespace Money \ No newline at end of file diff --git a/src/plugin/Plugin.cpp b/src/plugin/Plugin.cpp index 4be0f0e..19d39d1 100644 --- a/src/plugin/Plugin.cpp +++ b/src/plugin/Plugin.cpp @@ -5,6 +5,7 @@ #include +extern void entry(); namespace plugins { bool Plugin::load(ll::plugin::Plugin& self) { @@ -27,7 +28,7 @@ bool Plugin::enable(ll::plugin::Plugin& self) { throw std::runtime_error("plugin is enabled by a different instance"); } - // Code for enabling the plugin goes here. + entry(); mIsEnabled = true; return true; diff --git a/src/plugin/Settings.h b/src/plugin/Settings.h new file mode 100644 index 0000000..570f001 --- /dev/null +++ b/src/plugin/Settings.h @@ -0,0 +1,18 @@ +#include "Nlohmann/json.hpp" + +namespace Settings { +extern std::string language; +extern int def_money; +extern float pay_tax; +extern bool enable_ranking; + +nlohmann::json globaljson(); + +void initjson(nlohmann::json json); + +void WriteDefaultConfig(const std::string& fileName); + +void LoadConfigFromJson(const std::string& fileName); + +void reloadJson(const std::string& fileName); +} // namespace Settings diff --git a/src/plugin/lang.h b/src/plugin/lang.h new file mode 100644 index 0000000..cf3dedf --- /dev/null +++ b/src/plugin/lang.h @@ -0,0 +1,49 @@ +#pragma once +#include "ll/api/i18n/I18nAPI.h" + +static const ll::i18n::I18N::LangData defaultLangData = { + {"en", + {{"money.no.target", "Can not find target player."}, + {"money.invalid.arg", "Invalid command argument, or unknown error."}, + {"money.succ", "Command successfully executed."}, + {"money.not.enough", "You have insufficient balance."}, + {"money.no.perm", "You do not have the required permissions to execute this command."}, + {"money.pay.succ", "Transfer successful."}, + {"money.add.succ", "Balance successfully increased."}, + {"money.reduce.succ", "Balance successfully reduced."}, + {"money.set.succ", "Balance successfully set."}, + {"money.dontuseinconsole", "Don't use this command in console."}} }, + {"ru", + {{"money.no.target", "Не удаётся найти игрока."}, + {"money.invalid.arg", "Неправильный аргумент команды, или неизвестная ошибка."}, + {"money.succ", "Команда выполнена успешно."}, + {"money.not.enough", "У вас недостаточный баланс."}, + {"money.no.perm", "У вас нет прав для использования этой команды."}, + {"money.pay.succ", "Переведено успешно."}, + {"money.add.succ", "Баланс успешно увеличен."}, + {"money.reduce.succ", "Баланс успешно уменьшен."}, + {"money.set.succ", "Баланс успешно установлен."}, + {"money.dontuseinconsole", "Не используйте эту команду в консоли."}}}, + {"zh_CN", + {{"money.no.target", u8"找不到目标"}, + {"money.invalid.arg", u8"参数错误或未知错误"}, + {"money.succ", u8"执行成功"}, + {"money.not.enough", u8"金钱不足"}, + {"money.no.perm", u8"你无权执行"}, + {"money.pay.succ", u8"支付成功"}, + {"money.add.succ", u8"添加成功"}, + {"money.reduce.succ", u8"移除成功"}, + {"money.set.succ", u8"设置成功"}, + {"money.dontuseinconsole", u8"不要在控制台使用这个指令"}} }, + {"zh_TW", + {{"money.no.target", u8"找不到目標"}, + {"money.invalid.arg", u8"參數錯誤或未知錯誤"}, + {"money.succ", u8"執行成功"}, + {"money.not.enough", u8"金錢不足"}, + {"money.no.perm", u8"你沒有權限使用此指令!"}, + {"money.pay.succ", u8"支付成功"}, + {"money.add.succ", u8"添加成功"}, + {"money.reduce.succ", u8"移除成功"}, + {"money.set.succ", u8"設置成功"}, + {"money.dontuseinconsole", u8"不要在控制台使用這個指令"}} } +}; diff --git a/xmake.lua b/xmake.lua index 4b77f69..e8f9003 100644 --- a/xmake.lua +++ b/xmake.lua @@ -1,11 +1,14 @@ add_repositories("liteldev-repo https://github.com/LiteLDev/xmake-repo.git") -add_requires("levilamina 0.2.1") -- Change this to your expected version. +add_requires("levilamina develop") -- Change this to your expected version. + +add_requires("sqlitecpp 3.2.1") +add_requires("openssl 1.1.1-t") if not has_config("vs_runtime") then set_runtimes("MD") end -target("levilamina-plugin-template") -- Change this to your plugin name. +target("LegacyMoney") -- Change this to your plugin name. add_cxflags( "/utf-8", "/permissive-", @@ -41,10 +44,11 @@ target("levilamina-plugin-template") -- Change this to your plugin name. "src" ) add_packages( - "levilamina" + "levilamina", + "sqlitecpp", + "openssl" ) add_rules( - "mode.debug", "mode.release", "mode.releasedbg" ) @@ -69,3 +73,27 @@ target("levilamina-plugin-template") -- Change this to your plugin name. plugin_packer.pack_plugin(target,plugin_define) end) + +package("levilamina") + add_urls("https://github.com/LiteLDev/LeviLamina.git") + + -- Dependencies from xmake-repo. + 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") + + -- Dependencies from liteldev-repo. + add_deps("ctre 3.8.1") + add_deps("pcg_cpp 1.0.0") + add_deps("pfr 2.1.1") + add_deps("preloader 1.3.1") + add_deps("symbolprovider 1.1.0") + add_deps("bdslibrary 1.20.50.03") + + on_install(function (package) + import("package.tools.xmake").install(package) + end) \ No newline at end of file