Files
dps_lib/src/script/l_squirrel.cpp
2026-05-08 19:03:33 +08:00

502 lines
17 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include "l_squirrel.h"
#include "l_squirrel_register.hpp"
#include <spdlog/spdlog.h>
#include <spdlog/sinks/stdout_color_sinks.h>
#include <spdlog/sinks/basic_file_sink.h>
#include <iostream>
#include <ctime>
#include <sstream>
#include <chrono>
#include "SqrReqScript.hpp"
#include "Dio.hpp"
#include <sys/wait.h>
#include <openssl/aes.h>
#include <openssl/evp.h>
#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<spdlog::sinks::stdout_color_sink_mt>();
console_sink->set_level(spdlog::level::info);
console_sink->set_pattern("[multi_sink_example] [%^%l%$] %v");
auto file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>(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<spdlog::logger>("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<std::string> vec = Tool::GetListFilesR(FilePath);
std::vector<std::pair<std::string, std::string>> SquirrelFilePath;
// std::map<std::string, std::string>SquirrelFilePath;
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<std::string, std::string> 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<std::string, std::string> 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<unsigned char> hex_to_bytes(const std::string& hex_str) {
std::vector<unsigned char> 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<unsigned char>(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<unsigned char>(data[i]) != pad_len) {
return data; // 填充非法,返回原数据
}
}
return data.substr(0, data.size() - pad_len);
}
// AES-ECB解密输入二进制数据密钥固定字节数组
std::string aes_ecb_decrypt(const std::vector<unsigned char>& 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<const unsigned char*>(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<unsigned char> decrypt_buf(data_len);
for (size_t i = 0; i < data_len; i += AES_BLOCK_SIZE) {
// 最后一块不足16字节时补0ECB要求块对齐实际加密数据应为块大小整数倍
unsigned char block[AES_BLOCK_SIZE] = { 0 };
memcpy(block, encrypted_data.data() + i,
std::min(static_cast<size_t>(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<char*>(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>();
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<unsigned char>
std::vector<unsigned char> encrypted_data(
(std::istreambuf_iterator<char>(F)),
std::istreambuf_iterator<char>()
);
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<const char*>(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>();
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<char>(file)),
std::istreambuf_iterator<char>());
content.pop_back();
return content;
}
static std::string GetRemoteScriptVersion() {
try {
asio::io_context io_context;
HttpsClient client(io_context);
// 配置GET请求
std::map<std::string, std::string> 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
}