#include "l_squirrel.h" #include "l_squirrel_register.hpp" #include #include #include #include #include #include #include #include "SqrReqScript.hpp" #include "Dio.hpp" #include #include #include #define WEBSCRIPT true; using asio::ip::tcp; static char szGamePathA[256]; void setupLogger() { getConfigPath(szGamePathA, sizeof(szGamePathA)); std::string Path = std::string(szGamePathA); std::string log_filename = "log/dps_log/" + Path.substr(Path.find("cfg") + 4) + "/log_"; char time_buffer[100]; std::time_t now = std::time(nullptr); std::tm* local_time = std::localtime(&now); strftime(time_buffer, sizeof(time_buffer), "%Y-%m-%d_%H-%M-%S", local_time); log_filename += std::string(time_buffer) + ".txt"; auto console_sink = std::make_shared(); console_sink->set_level(spdlog::level::info); console_sink->set_pattern("[multi_sink_example] [%^%l%$] %v"); auto file_sink = std::make_shared(log_filename, true); file_sink->set_level(spdlog::level::info); file_sink->set_pattern("[%Y-%m-%d %H:%M:%S] [%^%l%$] %v"); auto logger = std::make_shared("logger", spdlog::sinks_init_list{ console_sink, file_sink }); logger->set_level(spdlog::level::info); logger->flush_on(spdlog::level::info); // logger->info("欢迎使用Dps插件"); spdlog::register_logger(logger); spdlog::set_default_logger(logger); } // 虚拟机对象 HSQUIRRELVM v; // Lock std::recursive_mutex SqMtx; static void printfunc(HSQUIRRELVM v, const SQChar* s, ...) { va_list vl; va_start(vl, s); char buf[1024]; vsnprintf(buf, sizeof(buf), s, vl); va_end(vl); spdlog::info(buf); } static void errorfunc(HSQUIRRELVM v, const SQChar* s, ...) { va_list vl; va_start(vl, s); char buf[1024]; vsnprintf(buf, sizeof(buf), s, vl); va_end(vl); spdlog::error(buf); } // 加密函数 static std::string encryptDecrypt(const std::string& input, const std::string& key) { std::string output = input; for (size_t i = 0; i < input.size(); i++) { output[i] = input[i] ^ key[i % key.size()]; // 使用异或运算进行加密 } return output; } // 判断是否处理加密 static std::string IsencryptDecrypt(const std::string& input, const std::string& FileName) { if (FileName.find(".nut") != std::string::npos) return input; else return encryptDecrypt(input, "Rindro-Aurora"); } static void ReloadingScript(HSQUIRRELVM v, std::string FilePath) { // 爬取出所有的脚本文件 std::vector vec = Tool::GetListFilesR(FilePath); std::vector> SquirrelFilePath; // std::mapSquirrelFilePath; for (auto it = vec.cbegin(); it != vec.cend(); ++it) { std::string FileName = FilePath + *it; // std::cout << FileName << std::endl; if (FileName.find(".nut") == std::string::npos && FileName.find(".sut") == std::string::npos) continue; std::fstream F; F.open((FileName).c_str(), std::ios::in); std::stringstream ContentStringStream; ContentStringStream << F.rdbuf(); std::string ContentString(ContentStringStream.str()); F.close(); std::string RealContentString = IsencryptDecrypt(ContentString, FileName); SquirrelFilePath.push_back({ FileName, RealContentString }); } std::map SquirrelLastFilePath; for (auto it = SquirrelFilePath.begin(); it != SquirrelFilePath.end(); it++) { std::string Sourcename = it->first; std::string ContentString = it->second; if (SQ_SUCCEEDED(sq_compilebuffer(v, (SQChar*)(ContentString.c_str()), ContentString.length(), (SQChar*)(Sourcename.c_str()), true))) { sq_pushroottable(v); sq_call(v, 1, 1, 1); sq_pop(v, 1); } else { SquirrelLastFilePath[Sourcename] = ContentString; } } while (SquirrelLastFilePath.size() > 0) { std::map FailMapBuffer; for (auto it = SquirrelLastFilePath.begin(); it != SquirrelLastFilePath.end(); it++) { std::string Sourcename = it->first; std::string ContentString = it->second; if (SQ_SUCCEEDED(sq_compilebuffer(v, (SQChar*)(ContentString.c_str()), ContentString.length(), (SQChar*)(Sourcename.c_str()), true))) { sq_pushroottable(v); if (SQ_FAILED(sq_call(v, 1, 1, 1))) { FailMapBuffer[Sourcename] = ContentString; }; sq_pop(v, 1); }; } SquirrelLastFilePath.clear(); if (FailMapBuffer.size() > 0) { for (auto it = FailMapBuffer.begin(); it != FailMapBuffer.end(); it++) { std::string Sourcename = it->first; std::string ContentString = it->second; SquirrelLastFilePath[Sourcename] = ContentString; } } } } // 十六进制字符串转字节数组(完整实现) std::vector hex_to_bytes(const std::string& hex_str) { std::vector bytes; // 检查Hex字符串长度是否为偶数 if (hex_str.size() % 2 != 0) { throw std::invalid_argument("Hex string length must be even"); } // 逐字节转换 for (size_t i = 0; i < hex_str.size(); i += 2) { std::string byte_str = hex_str.substr(i, 2); char* end_ptr; unsigned long byte_val = strtol(byte_str.c_str(), &end_ptr, 16); // 验证转换有效性 if (*end_ptr != '\0' || byte_val > 0xFF) { throw std::invalid_argument("Invalid hex character: " + byte_str); } bytes.push_back(static_cast(byte_val)); } return bytes; } // 移除PKCS5Padding填充 std::string unpad_pkcs5(const std::string& data) { if (data.empty()) return ""; unsigned char pad_len = data.back(); // 验证填充合法性:填充长度需≤块大小,且所有填充字节一致 if (pad_len > AES_BLOCK_SIZE || pad_len == 0) return data; for (size_t i = data.size() - pad_len; i < data.size(); ++i) { if (static_cast(data[i]) != pad_len) { return data; // 填充非法,返回原数据 } } return data.substr(0, data.size() - pad_len); } // AES-ECB解密(输入:二进制数据;密钥:固定字节数组) std::string aes_ecb_decrypt(const std::vector& encrypted_data) { // 用户提供的密钥(16字节,AES-128) const signed char keyBytes[] = { 92, -2, -65, 10, 118, 103, -68, 72, -23, 124, -103, -39, 0, -128, 31, -107 }; const int key_len = sizeof(keyBytes); // 初始化AES解密密钥 AES_KEY aes_key; if (AES_set_decrypt_key(reinterpret_cast(keyBytes), key_len * 8, // 16*8=128位 &aes_key) != 0) { throw std::runtime_error("Invalid AES key"); } // ECB模式分块解密(AES块大小16字节) size_t data_len = encrypted_data.size(); std::vector decrypt_buf(data_len); for (size_t i = 0; i < data_len; i += AES_BLOCK_SIZE) { // 最后一块不足16字节时补0(ECB要求块对齐,实际加密数据应为块大小整数倍) unsigned char block[AES_BLOCK_SIZE] = { 0 }; memcpy(block, encrypted_data.data() + i, std::min(static_cast(AES_BLOCK_SIZE), data_len - i)); AES_ecb_encrypt(block, decrypt_buf.data() + i, &aes_key, AES_DECRYPT); } // 移除PKCS5填充并返回字符串 return unpad_pkcs5(std::string(reinterpret_cast(decrypt_buf.data()), data_len)); } static void LoadScript(HSQUIRRELVM v, bool Flag) { std::ifstream f("/dp_s/_DPS_/FileConfig.json"); nlohmann::json Jso = nlohmann::json::parse(f); f.close(); //加载基础脚本 for (const auto& elem : Jso["BaseScript"]) { if (elem.is_string()) { std::string line = elem.get(); std::string file_path = "/dp_s/_DPS_/_Core/" + line; // 以二进制模式打开加密文件 std::ifstream F(file_path, std::ios::binary | std::ios::in); if (!F.is_open()) { spdlog::error("无法打开脚本文件: {}", file_path); continue; } // 直接读取二进制数据到vector std::vector encrypted_data( (std::istreambuf_iterator(F)), std::istreambuf_iterator() ); F.close(); std::string ContentString; //外网解密版本 if (!Flag) { try { // 直接解密二进制数据 ContentString = aes_ecb_decrypt(encrypted_data); if (ContentString.empty()) { spdlog::warn("解密结果为空: {}", file_path); continue; } } catch (const std::runtime_error& e) { spdlog::error("AES解密失败 {}: {}", file_path, e.what()); continue; } } //本地不解密版本 else { ContentString = std::string(reinterpret_cast(encrypted_data.data()), encrypted_data.size()); } if (SQ_SUCCEEDED(sq_compilebuffer(v, (SQChar*)(ContentString.c_str()), ContentString.length(), (SQChar*)(line.c_str()), true))) { sq_pushroottable(v); sq_call(v, 1, SQTrue, SQFalse); sq_pop(v, 1); } } } //加载项目脚本 for (const auto& elem : Jso["ProjectScript"]) { for (const auto& path : elem["Script"]) { if (path.is_string()) { std::string line = path.get(); std::string ContentString = ""; std::fstream F; F.open(("/dp_s/_DPS_/_BuiltProject/" + line).c_str(), std::ios::in); if (F.is_open()) { std::stringstream ContentStringStream; ContentStringStream << F.rdbuf(); ContentString = (ContentStringStream.str()); F.close(); } if (SQ_SUCCEEDED(sq_compilebuffer(v, (SQChar*)(ContentString.c_str()), ContentString.length(), (SQChar*)(line.c_str()), true))) { sq_pushroottable(v); sq_call(v, 1, SQTrue, SQFalse); sq_pop(v, 1); } } } } } static SQInteger SqReloadScript(HSQUIRRELVM v) { if ((access("/dp_s/lib/db.ini", F_OK) != -1)) { LoadScript(v, true); } else { LoadScript(v, false); //读取网络单发脚本 ReqSquirrelScript(v); } return 0; } static std::string GetLocalScriptVersion() { std::ifstream file("/dp_s/_DPS_/version"); // 打开文件 if (!file.is_open()) { // 检查文件是否成功打开 return "-1"; } std::string content((std::istreambuf_iterator(file)), std::istreambuf_iterator()); content.pop_back(); return content; } static std::string GetRemoteScriptVersion() { try { asio::io_context io_context; HttpsClient client(io_context); // 配置GET请求 std::map headers = { {"User-Agent", "AsioHttpsClient/Sync"}, {"Accept", "application/json"}, {"Content-Type", "application/json"} // POST必须的头 }; client.setRequest("dps.senzo.online", "/script/getBaseUpdateTime", "POST", "", headers); // 同步发送请求(阻塞等待结果) HttpsClient::SyncResponse res = client.sendRequestSync(); // 处理结果 if (res.error.empty()) { spdlog::info("获取到服务器最新脚本,版本为:{}", res.response); return res.response; } else { spdlog::info("请求服务器最新脚本失败,使用本地缓存脚本! 错误原因: {}", res.error); return "error"; } } catch (const std::exception& e) { spdlog::info("请求服务器最新脚本失败,使用本地缓存脚本! 错误原因: {}", e.what()); return "error"; } } int executeCommand(const std::string& cmd) { pid_t pid = fork(); if (pid == -1) return -1; if (pid == 0) { // 子进程 // 清除LD_PRELOAD unsetenv("LD_PRELOAD"); // 执行命令(需拆分命令和参数) execl("/bin/sh", "sh", "-c", cmd.c_str(), nullptr); exit(1); // 若exec失败 } // 父进程等待子进程结束 int status; waitpid(pid, &status, 0); return WEXITSTATUS(status); } static void DownLoadScript(std::string RemoteVersion) { try { asio::io_context io_context; HttpsClient client(io_context); // 设置下载请求 client.setRequest("dps.senzo.online", "/script/getBaseSutZip", "GET"); // 下载文件 HttpsClient::FileDownloadResult result = client.downloadFileSync("/dp_s/_DPS_/cache.tar.gz"); if (!result.error.empty()) { spdlog::info("下载服务器最新脚本失败,使用本地缓存脚本! 错误原因: {}", result.error); } else { spdlog::info("服务器最新脚本下载完成! 正在解压..."); std::string command = "rm -rf \"" + std::string("/dp_s/_DPS_/_Core") + "\""; int Res1 = executeCommand(command.c_str()); std::string tarPath = "/dp_s/_DPS_/cache.tar.gz"; std::string destDir = "/dp_s/_DPS_"; std::string tarCmd = " tar -xzf \"" + tarPath + "\" -C \"" + destDir + "\""; int Res2 = executeCommand(tarCmd.c_str()); if (Res2 == 0) { spdlog::info("解压成功!"); } else { spdlog::info("在解压过程中发生了一个预期之外的错误,请联系开发者!"); } std::ofstream file("/dp_s/_DPS_/version", std::ios::out); if (!file.is_open()) { spdlog::info("发生了一个预期之外的错误,请联系开发者!"); } file << RemoteVersion << std::endl; file.close(); } } catch (const std::exception& e) { spdlog::info("下载服务器最新脚本失败,使用本地缓存脚本! 错误原因: {}", e.what()); } } void InitSquirrel() { v = sq_open(8192); // 创建虚拟机,其栈的初始大小为1024 sq_pushroottable(v); sqstd_register_bloblib(v); sqstd_register_iolib(v); sqstd_register_systemlib(v); sqstd_register_mathlib(v); sqstd_register_stringlib(v); // sqstd_register_dio(v); sqstd_seterrorhandlers(v); // 初始化日志器 setupLogger(); sq_setprintfunc(v, printfunc, errorfunc); // sets the print function // 获取根表(全局作用域) sq_pushroottable(v); // 将DP_S_VERSION添加到全局表 sq_pushstring(v, "DP_S_VERSION", -1); // 键 sq_pushfloat(v, 25.332); // 值 sq_newslot(v, -3, SQFalse); // 在根表中创建slot // 弹出根表,恢复堆栈 sq_pop(v, 1); // 注册全局NutApi GlobaRegisterSquirrel(v); //非本地模式 if ((access("/dp_s/lib/db.ini", F_OK) == -1)) { //获取本地脚本版本 std::string LocalVersion = GetLocalScriptVersion(); if (LocalVersion == "-1") { spdlog::info("本地未检测到DP-S核心脚本,正在尝试联网获取最新脚本..."); } else { spdlog::info("本地DP-S核心脚本版本为{},正在检测是否有可用更新...", LocalVersion); } //获取服务器最新脚本版本 std::string RemoteVersion = GetRemoteScriptVersion(); if (RemoteVersion != "error") { if (RemoteVersion != LocalVersion) { spdlog::info("正在下载服务器最新脚本..."); //下载最新脚本 DownLoadScript(RemoteVersion); } else { spdlog::info("当前服务器脚本已是最新版本无需更新."); } } } SqReloadScript(v); // sq_pushroottable(v); // sq_pushstring(v, "sq_ReloadScript", -1); // sq_newclosure(v, SqReloadScript, 0); // create a new function // sq_newslot(v, -3, SQFalse); // sq_pop(v, 1); // pops the root table }