diff --git a/MyProject/A.nut b/MyProject/A.nut index 3035ba9..39029b8 100644 --- a/MyProject/A.nut +++ b/MyProject/A.nut @@ -116,10 +116,13 @@ Gm_InputFunc_Handle["点券"] <- function(SUser, CmdString) { Gm_InputFunc_Handle["test"] <- function(SUser, CmdString) { - Sq_CallFunc(S_Ptr("0x0866C46A"), "void", ["pointer"], SUser.C_Object); + SUser.DropItem(26058, 200, 200); + } + + Timer.SetTimeOut(function() { // _Dps_Equ2AvaJewel_Main_() // local Pack = Packet(); @@ -145,6 +148,10 @@ Timer.SetTimeOut(function() { +Gm_InputFunc_Handle["我要点券"] <- function(SUser, CmdString) { + SUser.RechargeCera(500000); +} + // // //玩家新增道具时 // Cb_wdzsdfge_Enter_Func <- {}; // Cb_wdzsdfge_Leave_Func <- {}; @@ -170,5 +177,3 @@ Gm_InputFunc_Handle["我要升级"] <- function(SUser, CmdString) { Gm_InputFunc_Handle["南瓜村"] <- function(SUser, CmdString) { getroottable()._NewTitle_.AddTitle(SUser, 1); } - - diff --git a/MyProject/DPS登录器.nut b/MyProject/DPS登录器.nut deleted file mode 100644 index f87dae2..0000000 --- a/MyProject/DPS登录器.nut +++ /dev/null @@ -1,168 +0,0 @@ -class _DPS_Login_Gateway_ { - - //数据库连接 - MysqlObject = null; - - PackHandleMap = null; - - constructor() { - PackHandleMap = {}; - - MysqlObject = Mysql(Str_Ptr("127.0.0.1"), 3306, Str_Ptr("taiwan_cain"), Str_Ptr("game"), Str_Ptr("uu5!^%jg")); - MysqlObject.Exec_Sql(format("SET NAMES %s", "latin1")); - - //创建一个Http Server - try { - local HS = HttpServer("0.0.0.0", "41817"); - HS.Listen(function(SocketObject, Header, Msg) { - print("HS.Listen") - getroottable()._DPS_Login_Gateway_Object_._HttpServer_Event_DPS_(SocketObject, Header, Msg); - }); - } catch (exception) { - - } - - PackHandleMap["/GetConfig"] <- function(SocketObject, Header, Jso) { - local Config = sq_ReadJsonFile("/dp_s/OfficialConfig/登录器配置.json"); - //读取DPS登录器的配置并发送 - SocketObject.Write(Config); - } - - - PackHandleMap["/Login"] <- function(SocketObject, Header, Jso) { - if (!Jso.rawin("account")) return; - if (!Jso.rawin("passwd")) return; - local account = Jso.account; - local passwd = Jso.passwd; - local passwd_hash = MD5_Hash(passwd); - - - //正则表达式 只允许字母和数字 - local regex = regexp("^[A-Za-z0-9]{1,19}$"); - if (!regex.match(account)) { - SocketObject.Write({ - error = "账号只能包含字母和数字且长度小于20" - }); - return; - } - - local sql = format("select UID from d_taiwan.accounts where accountname='%s' and password='%s';", account, passwd_hash); - local Ret = MysqlObject.Select(sql, ["int"]); - if (Ret && Ret.len() > 0) { - local Uid = Ret[0][0]; - local str = format("%08x010101010101010101010101010101010101010101010101010101010101010155914510010403030101", Uid); - local Byte = Sq_Rsa_Private_Encrypt(str); - local EnStr = MD5.base64_encode(Byte); - //发送登录Token - SocketObject.Write({ - token = EnStr - }); - } else { - SocketObject.Write({ - error = "账号或密码错误!" - }); - } - }.bindenv(this); - - - PackHandleMap["/Register"] <- function(SocketObject, Header, Jso) { - - if (!Jso.rawin("account")) return; - if (!Jso.rawin("passwd")) return; - - local account = Jso.account; - local passwd = Jso.passwd; - - - //正则表达式 只允许字母和数字 - local regex = regexp("^[A-Za-z0-9]{1,19}$"); - if (account.len() == 0 || !regex.match(account)) { - SocketObject.Write({ - error = "账号只能包含字母和数字且长度大于0小于20" - }); - return; - } - if (passwd.len() == 0 || !regex.match(passwd)) { - SocketObject.Write({ - error = "密码只能包含字母和数字且长度大于0小于20" - }); - return; - } - if (GetAccountByName(account) != null) { - SocketObject.Write({ - error = "账号已存在" - }) - return; - } - local passwd_hash = MD5_Hash(passwd); - - - local sql = format("INSERT INTO d_taiwan.accounts(accountname, password)VALUES( '%s' , '%s');", account, passwd_hash); - MysqlObject.Exec_Sql(sql); - local Ret = MysqlObject.Select("SELECT LAST_INSERT_ID()", ["int"]); - if (Ret && Ret.len() > 0) { - local Uid = Ret[0][0].tostring(); - MysqlObject.Exec_Sql(format("INSERT INTO d_taiwan.member_info(m_id, user_id)VALUES('%s','%s');", Uid, Uid)); - MysqlObject.Exec_Sql(format("INSERT INTO d_taiwan.member_white_account(m_id)VALUES('%s')", Uid)); - MysqlObject.Exec_Sql(format("INSERT INTO taiwan_login.member_login(m_id)VALUES('%s')", Uid)); - MysqlObject.Exec_Sql(format("INSERT INTO taiwan_billing.cash_cera(account, cera,mod_tran,mod_date, reg_date)VALUES('%s',0,0,NOW(), NOW())", Uid)); - MysqlObject.Exec_Sql(format("INSERT INTO taiwan_billing.cash_cera_point(account, cera_point,mod_date, reg_date)VALUES('%s',0,NOW(), NOW())", Uid)); - SocketObject.Write({ - success = "账号注册成功,现在您可以登录了!" - }); - return; - } - - }.bindenv(this); - - - } - - - function GetAccountByName(account) { - local sql = format("select UID , accountname, password from d_taiwan.accounts where accountname='%s'", account); - local Ret = MysqlObject.Select(sql, ["int", "string", "string"]); - if (!Ret || Ret.len() == 0) { - return null; - } - - return { - uid = Ret[0][0], - account = Ret[0][1], - passwd = Ret[0][2] - } - } - - function _HttpServer_Event_DPS_(SocketObject, Header, Msg) { - print("_HttpServer_Event_DPS_") - if (PackHandleMap.rawin(Header.path)) { - local Jso = null; - try { - Jso = Json.Decode(Msg); - } catch (exception) { - - } - if (!Jso) return; - PackHandleMap[Header.path](SocketObject, Header, Jso); - - } else { - SocketObject.Write({ - error = "error url" - }); - } - } - - function MD5_Hash(str) { - local Ctx = Memory.alloc(0x100); - MD5.MD5_Init(Ctx.C_Object); - MD5.MD5_Update(Ctx.C_Object, Memory.allocUtf8String(str).C_Object, str.len()); - local Result = Memory.alloc(16); - MD5.MD5_Final(Result.C_Object, Ctx.C_Object); - return MD5.hex_encode(Result.readUtf8String(16)); - } -} - - -Timer.SetTimeOut(function() { - getroottable()._DPS_Login_Gateway_Object_ <- _DPS_Login_Gateway_(); -}, 1); \ No newline at end of file diff --git a/_DPS_/_BuiltProject/露露定制内容/露露定制内容.nut b/_DPS_/_BuiltProject/露露定制内容/露露定制内容.nut new file mode 100644 index 0000000..7637db5 --- /dev/null +++ b/_DPS_/_BuiltProject/露露定制内容/露露定制内容.nut @@ -0,0 +1,53 @@ +/* +文件名:露露定制内容.nut +路径:_DPS_/_BuiltProject/露露定制内容/露露定制内容.nut +创建日期:2026-03-23 11:05 +文件用途: +*/ + +class LuluCustomizedSystemC { + Data = null; + + constructor() { + InitPvf(); + + ClientSocketPackFuncMap.rawset(21016001, function(SUser, Jso) { + SUser.DropItem(Jso.id, Jso.posX, Jso.posY); + }.bindenv(this)); + + ClientSocketPackFuncMap.rawset(21016003, function(SUser, Jso) { + if (Data.rawin(Jso.rarity)) { + local Index = MathClass.Rand(0, Data[Jso.rarity].len()); + local ItemId = Data[Jso.rarity][Index]; + SUser.DropItem(ItemId, Jso.posX, Jso.posY); + } + }.bindenv(this)); + } + + function InitPvf() { + // getroottable()._Script_Data_Init_ <- false; + Script(); + Data = {}; + ScriptData.GetFileData("etc/uniquehellpartydroptable.etc", function(_n, DataA) { + while (!DataA.Eof()) { + local Fragment = DataA.Get(); + if (Fragment.find("[") >= 0 && Fragment.find("/") == null) { + local key = Fragment.slice(1, -1); + Data[key] <- []; + while (true) { + local Id = DataA.Get(); + if (typeof Id == "string" && Id.find("[/") != null) { + break; + } + Data[key].push(Id); + } + } + } + }.bindenv(this)); + print(Data); + } +} + +Timer.SetTimeOut(function() { + getroottable()._LuluCustomizedSystemC_ <- LuluCustomizedSystemC(); +}, 1); \ No newline at end of file diff --git a/_DPS_/_Core/main.nut b/_DPS_/_Core/main.nut index 057fe0f..c110eea 100644 --- a/_DPS_/_Core/main.nut +++ b/_DPS_/_Core/main.nut @@ -15,4 +15,7 @@ function InitPluginInfo() { function main() { InitPluginInfo(); -} \ No newline at end of file +} + + + diff --git a/dp_s_development.skill b/dp_s_development.skill new file mode 100644 index 0000000..ecb0e91 --- /dev/null +++ b/dp_s_development.skill @@ -0,0 +1,957 @@ +# DP-S Game Server Plugin Development Skill + +## 🎯 框架架构概述 + +### 整体架构层次 +``` +MyProject/ (用户自定义脚本层) - GM命令、自定义功能、逻辑重写 +_DPS_/_BuiltProject/ (内置功能层) - 团本系统、装备系统、交易系统等 +_DPS_/_Core/ (核心框架层) - BaseClass类库、回调系统、钩子系统 +Native C++ 服务器 (游戏原生代码) - 原生游戏逻辑 +``` + +### 核心类体系 +- User: 玩家对象核心类 +- GameManager: 游戏管理器 +- Packet Dungeon: 副本对象类 +- Mysql/MysqlPool: 数据库连接池 +- Timer: 定时器类 +- NativePointer: 内存操作类 + +--- + +## 📐 官方模块开发规范 + +### OfficialProject/ 目录规范 + +**OfficialProject** 是官方功能模块目录,包含标准化的服务器功能模块。 + +#### 已有官方模块列表 +| 模块名称 | 功能描述 | 配置文件 | +|----------|----------|----------| +| 二狗宝珠继承 | 装备宝珠属性继承系统 | 无 | +| 交易邮件播报 | 交易和邮件系统播报 | 交易邮件播报.json | +| 任务清除卷 | 任务相关功能 | 任务相关配置_南瓜.json | +| 副本播报 | 副本通关/放弃播报 | 副本播报配置_Nangua.json | +| 史诗掉落奖励 | 史诗/神器/稀有掉落奖励 | 史诗掉落奖励配置_南瓜.json | +| 心悦播报 | 心悦会员登录播报 | 心悦播报配置_Nangua.json | +| 战力榜 | 战力排行榜功能 | 战力榜配置_南瓜.json | +| 杂项功能 | 多种游戏功能开关 | 杂项功能开关_Nangua.json | +| 根据任务进入不同副本 | 任务控制副本进入 | 根据任务进入不同副本_Lenheart.json | +| 深渊爆率实时控制 | 深渊爆率调整 | 深渊爆率实时控制_Nangua.json | +| 独立掉落 | 自定义掉落系统 | 无 | +| 自动刷新GM工具的邮件 | GM工具邮件管理 | 无 | +| 装备镶嵌与时装镶嵌 | 装备和时装镶嵌扩展 | 装备镶嵌与时装镶嵌_Lenheart.json | +| 记录服务器聊天内容 | 聊天内容记录 | 无 | +| 门票进入副本 | 门票控制副本进入 | 门票进入副本配置_Maomi.json | + +#### OfficialConfig/ 目录规范 + +**OfficialConfig** 是官方配置文件目录,存储所有官方模块的配置。 + +##### 配置文件命名规范 +``` +功能名称.json # 通用配置 +功能名称_服务器名.json # 服务器特定配置 +功能名称_管理员昵称.json # 管理员自定义配置 +``` + +##### 通用配置结构 +```json +{ + "功能配置": { + "开关控制": true, + "提示信息": "这是提示信息", + "配置项1": "值1", + "配置项2": ["数组值1", "数组值2"], + "配置项3": { + "子项1": "子值1", + "子项2": "子值2" + } + }, + "数据库配置": { + "IP": "127.0.0.1", + "端口": 3306, + "用户名": "game", + "密码": "password" + } +} +``` + +--- + +## 🏢 官方模块开发模式 + +### 标准模块结构 + +OfficialProject/ 目录下的每个模块应遵循以下结构: + +``` +功能模块名/ +├── 功能模块名.nut # 主脚本文件 +└── Proj.ifo # 项目信息文件(可选) +``` + +#### 主脚本文件模板 +```nut +/* +文件名:功能模块名.nut +路径:OfficialProject/功能模块名/功能模块名.nut +创建日期:YYYY-MM-DD HH:MM +文件用途:功能模块描述 +*/ + +function _功能模块名_Main_() { + // 加载配置 + local config = GlobalConfig.Get("功能模块名配置.json"); + + // 读取配置开关 + if (config["功能配置"]["开关控制"]) { + // 执行功能初始化 + + // 注册回调 + RegisterCallbacks(); + + // 初始化数据 + InitData(); + } + + // 注册回调和钩子 + function RegisterCallbacks() { + // 注册GM命令 + Gm_InputFunc_Handle["命令名"] <- function(SUser, CmdString) { + // 命令处理逻辑 + }.bindenv(this); + + // 注册事件回调 + Cb_reach_game_world_Func["功能模块名"] <- function(SUser) { + // 事件处理逻辑 + }.bindenv(this); + + // 注册Hook回调 + Cb_HookName_Enter_Func["功能模块名"] <- function(args) { + // Hook处理逻辑 + return value; + }.bindenv(this); + } + + // 初始化数据 + function InitData() { + // 初始化数据库表 + // 加载初始数据 + } +} +``` + +### 配置文件管理 + +#### 读取配置 +```nut +// 读取配置文件 +local config = GlobalConfig.Get("配置文件名.json"); + +// 访问配置项 +local switchState = config["功能配置"]["开关控制"]; +local someValue = config["功能配置"]["配置项1"]; +local arrayValue = config["功能配置"]["配置项2"][0]; +``` + +#### 配置热重载 +```nut +// 在Main.nut中注册重载函数 +GlobalOfficial_Project.ReloadProjectMap <- { + "配置文件名" = "_功能模块名_Main_" +}; + +// 重载函数 +function _功能模块名_Main_(OldConfig) { + // 使用旧配置清理资源(如果有) + if (OldConfig) { + // 清理逻辑 + } + + // 重新加载配置 + local config = GlobalConfig.Get("配置文件名.json"); + + // 重新初始化 + // ... +} +``` + +### 官方模块最佳实践 + +#### 1. 战力榜模块模式 +```nut +// 获取战力值 +function GetRankNumber(charac_no) { + local Config = GlobalConfig.Get("战力榜配置_服务器名.json"); + local currentDB = Config["战力榜配置"]["当前选择"]; + local dbConfig = Config["战力榜配置"]["数据库选择"][currentDB]; + + local SqlObj = MysqlPool.GetInstance().GetConnect(); + local query = "SELECT " + dbConfig["字段名"] + + " FROM " + dbConfig["数据库名"] + "." + dbConfig["表名"] + + " WHERE " + dbConfig["条件字段"] + "=" + charac_no; + + local result = SqlObj.Select(query, ["string"]); + MysqlPool.GetInstance().PutConnect(SqlObj); + + if (result.len() > 0) { + return result[0][0].tointeger(); + } + return 0; +} +``` + +#### 2. 副本播报模块模式 +```nut +// 配置驱动的播报系统 +function ReportDungeonClear(SUser, dgnName, timeUsed, monsterCount) { + local config = GlobalConfig.Get("副本播报配置_服务器名.json"); + + // 检查开关 + if (!config["副本播报开关"]) return; + + // 检查排除列表 + local excludeDgns = config["不需要播报的副本ID"]; + local excludeCIDs = config["指定角色CID不播报"]; + + if (excludeDgns.find(dgnName) != null) return; + if (excludeCIDs.find(SUser.GetCID()) != null) return; + + // 构建播报消息 + local msg = format(config["通关播报信息"], + SUser.GetCharacName(), + dgnName, + GetDungeonDifficulty(dgnName), + timeUsed, + monsterCount + ); + + // 发送播报 + AdMsg().PutEquipment(msg, config["发送信息位置"]); +} +``` + +#### 3. 杂项功能模块模式 +```nut +// 多功能开关控制 +function _杂项功能_Main_() { + local config = GlobalConfig.Get("杂项功能开关_服务器名.json"); + + // 功能1: 设置最大等级 + if (config["杂项功能开关"]["设置最高等级"][0]) { + local level = config["杂项功能开关"]["设置最高等级"][1]; + GameManager.SetGameMaxLevel(level); + } + + // 功能2: 装备解锁时间 + if (config["杂项功能开关"]["装备解锁(单位:秒)"][0]) { + local time = config["杂项功能开关"]["装备解锁(单位:秒)"][1]; + GameManager.SetItemLockTime(time); + } + + // 功能3: 创建缔造者 + if (config["杂项功能开关"]["创建缔造者"]) { + GameManager.OpenCreateJob_CreatorMage(); + } + + // Hook回调和Leave Hook返回值拦截 + Cb_Item_IsBanRedeemItem_Leave_Func.BanRedeemItem <- function(args) { + if (config["杂项功能开关"]["关闭商店回购"]) { + return 1; // 返回1拦截默认行为 + } + } +} +``` + +#### 4. 史诗掉落奖励模块模式 +```nut +// 配置驱动的奖励系统 +function GiveEpicReward(SUser, epicCount) { + local config = GlobalConfig.Get("史诗掉落奖励配置_服务器名.json"); + + // 检查开关 + if (!config["奖励控制"]["多件史诗奖励控制开关"]) return; + + // 获取奖励配置 + local rewardConfig = config["奖励控制"]["多件SS对应奖励"][epicCount.tostring()]; + + if (rewardConfig) { + foreach(reward in rewardConfig) { + local itemId = reward[0]; + local quantity = reward[1]; + + if (itemId == 0) { + // 点券 + SUser.RechargeCera(quantity); + } else { + // 道具 + SUser.GiveItem(itemId, quantity); + } + } + + // 发送播报 + local title = config["信息播报"]["标题"]; + local contentColor = config["信息播报"]["内容颜色"]; + local ending = config["信息播报"]["结尾"]; + + AdMsg().PutEquipment(title, 14); + AdMsg().PutEquipment(ending, 14); + } +} +``` + +#### 5. 心悦播报模块模式 +```nut +// 任务驱动的播报系统 +function CheckXinyueStatus(SUser) { + local config = GlobalConfig.Get("心悦播报配置_服务器名.json"); + local xinyueConfig = config["心悦播报配置"]["心悦会员登录播报"]; + + // 检查开关 + if (!xinyueConfig["开关"]) return; + + // 检查任务完成情况 + local taskConfigs = config["心悦播报配置"]["任务等级配置"]; + foreach(taskConfig in taskConfigs) { + local taskId = taskConfig["任务ID"]; + + if (SUser.CheckQuest(taskId)) { + // 构建播报消息 + local msg = format("%s[%s]%s%s", + xinyueConfig["标题"], + taskConfig["心悦等级"], + SUser.GetCharacName(), + xinyueConfig["结尾信息内容"] + ); + + // 发送播报 + AdMsg().PutEquipment(msg, xinyueConfig["信息播报发送位置"]); + break; + } + } +} +``` + +### 游戏管理器常用方法 + +```nut +// 设置游戏最大等级 +GameManager.SetGameMaxLevel(85); + +// 设置装备解锁时间(秒) +GameManager.SetItemLockTime(1); + +// 开启缔造者创建 +GameManager.OpenCreateJob_CreatorMage(); + +// 自动解除魔法封印 +GameManager.OpenRandomAutomaticUnblocking(); + +// 开启装备与时装镶嵌 +GameManager.FixEquipUseJewel(); + +// 修复下线卡城镇 +GameManager.FixSaveTown(); + +// 修复绝望之塔金币异常 +GameManager.FixDespairGold(); + +// 设置每日可交易金币上限 +GameManager.FixGlodTradeDaily(50000000); + +// 强化13以上免刷新 +GameManager.Fix_13Upgrade(); + +// 开启14格技能栏 +GameManager.Fix14Skill(); + +// 修复拍卖行消耗品上架 +GameManager.Fix_Auction_Regist_Item(); + +// 设置副本内可丢弃物品品级 +GameManager.FixDungeonDropGrade(3); + +// 修复邮件验证 +GameManager.FixEmailRemovalVerification(); +``` + +### 公告系统配置 + +```nut +// 登录器配置示例 +{ + "noticeclass": ["公告", "活动", "玩法"], + "notice": [ + { + "type": "img", + "category": 1, + "text": "公告标题", + "link": "https://example.com", + "content": "http://192.168.200.25:3000/image.png" + }, + { + "type": "link", + "category": 1, + "text": "链接标题", + "link": "https://example.com" + } + ], + "scriptMd5": "", + "pluginsMd5": [], + "updateUrl": "www.baidu.com" +} +``` + +--- + +## 🔧 钩子系统 + +### Hook 注册方法 +```nut +// 步骤1: 声明回调表 +Cb_HookName_Enter_Func <- {}; +Cb_HookName_Leave_Func <- {}; + +// 步骤2: 注册Hook +_Hook_Register_Currency_Func_( + "0x内存地址", + ["参数类型数组"], + Cb_HookName_Enter_Func, + Cb_HookName_Leave_Func +); + +// 步骤3: 添加处理函数 +Cb_HookName_Enter_Func["模块名"] <- function(args) { + local SUser = User(args[0]); // args是参数数组 + // 处理逻辑 + return value; // 可返回null或修改后的值 +}.bindenv(this); +``` + +### 常用钩子点 +| 钩子名称 | 触发时机 | 参数说明 | +|----------|----------|----------| +| Cb_reach_game_world | 玩家进入游戏世界 | (C_User) | +| Cb_player_exit | 玩家下线 | (C_User) | +| Cb_User_Insert_Item | 玩家获得道具 | (C_User, ItemId, 数量, ...) | +| Cb_Move_Area | 玩家区域移动 | (C_User, TownIndex, AreaIndex) | +| Cb_base_input | 玩家普通输入 | (C_User, CmdString) | +| Cb_gm_input | GM命令输入 | (C_User, CmdString) | +| Cb_Use_Item_Sp | 特殊道具使用 | (C_User, ItemId) | +| Cb_CInventory_ChangeEquip | 切换装备 | (C_User, EquipSlot) | +| Cb_CParty_OnKillMonster | 击杀怪物 | (C_Party, C_User, MonsterId) | + +--- + +## 📡 回调系统 + +### 回调注册模式 +```nut +// 简单回调 +if (!("Cb_event_name_Func" in getroottable())) Cb_event_name_Func <- {}; + +Cb_event_name_Func["模块名"] <- function(SUser) { + // 处理逻辑 +}.bindenv(this); +``` + +### 常用回调类型 +```nut +// 玩家上线 +Cb_reach_game_world_Func["模块名"] <- function(SUser) { + local name = SUser.GetCharacName(); + local level = SUser.GetCharacLevel(); +} + +// 玩家下线 +Cb_player_exit_Func["模块名"] <- function(SUser) { + // 清理玩家数据 +} + +// 每帧执行 +Cb_timer_dispatch_Func["模块名"] <- function() { + // 每帧执行逻辑 +} + +// 副本通关 +Cb_ClearDungeon_Enter_Func["模块名"] <- function(args) { + local SUser = User(args[0]); + // 奖励处理 +} +``` + +--- + +## 💻 GM 命令开发 + +### 基本命令注册 +```nut +Gm_InputFunc_Handle["命令名"] <- function(SUser, CmdString) { + // 参数解析 + local handler = []; + local pos = 0; + do { + local start = pos; + pos = CmdString.find(" ", pos + 1); + if (pos != null) { + handler.append(CmdString.slice(start + 1, pos)); + } else + handler.append(CmdString.slice(start + 1)); + } while (pos != null); + + // 业务逻辑 + if (handler.len() >= 1) { + local itemId = handler[1].tointeger(); + local quantity = (handler.len() >= 2) ? handler[2].tointeger() : 1; + SUser.GiveItem(itemId, quantity); + SUser.SendNotiPacketMessage("道具已发放", 8); + } +} +``` + +### 快捷命令 +```nut +Gm_InputFunc_Handle["满级"] <- function(SUser, CmdString) { + SUser.SetCharacLevel(85); + SUser.SendNotiPacketMessage("已满级", 8); +} + +Gm_InputFunc_Handle["回城"] <- function(SUser, CmdString) { + SUser.ChangeTown(0, 1, 0, 0); // 城镇, 区域, X, Y +} +``` + +--- + +## 🗄️ 数据库操作 + +### 基本查询 +```nut +// 单连接模式 +MysqlObject = Mysql(Str_Ptr("127.0.0.1"), 3306, + Str_Ptr("数据库名"), Str_Ptr("账号"), Str_Ptr("密码")); +MysqlObject.Exec_Sql("SET NAMES 'latin1'"); + +// 查询 +local Ret = MysqlObject.Select("SELECT id, name, level FROM users WHERE id = " + uid, + ["int", "string", "int"]); +foreach(Row in Ret) { + local id = Row[0]; + local name = Row[1]; + local level = Row[2]; +} + +// 插入/更新 +MysqlObject.Exec_Sql(format("UPDATE users SET level = %d WHERE id = %d", newLevel, id)); + +// 获取自增ID +MysqlObject.Exec_Sql("INSERT INTO users (name) VALUES ('test')"); +local Ret = MysqlObject.Select("SELECT LAST_INSERT_ID()", ["int"]); +local newId = Ret[0][0]; +``` + +### 连接池模式 +```nut +// 获取连接 +local SqlObj = MysqlPool.GetInstance().GetConnect(); + +// 查询 +local Ret = SqlObj.Select("SELECT * FROM table", ["int", "string"]); +foreach(row in Ret) { + // 处理数据 +} + +// 执行 +SqlObj.Exec_Sql("UPDATE table SET field = value"); + +// 归还连接 +MysqlPool.GetInstance().PutConnect(SqlObj); +``` + +--- + +## 🎒 装备操作 + +### 装备基本操作 +```nut +local InvenObj = SUser.GetInven(); + +// 获取装备 +local Equip = InvenObj.GetSlot(Inven.INVENTORY_TYPE_BODY, 槽位); +ifif (!Equip) return; + +// 读取属性 +local upgrade = Equip.GetUpgrade(); // 强化等级 +local amplification = Equip.GetAmplification(); // 增幅等级 +local enchanting = Equip.GetEnchanting(); // 附魔ID +local forging = Equip.GetForging(); // 镶嵌数据 +local itemId = Equip.GetIndex(); // 道具ID + +// 修改属性 +Equip.SetUpgrade(12); +Equip.SetAmplification(3); +Equip.SetEnchanting(1001); +Equip.Flush(); // 必须调用 + +// 刷新客户端 +SUser.SendUpdateItemList(1, 3, 槽位); +``` + +### 装备继承模式 +```nut +// 读取源装备属性 +local sourceEquip = InvenObj.GetSlot(Inven.INVENTORY_TYPE_ITEM, 9); +local forging = sourceEquip.GetForging(); +local upgrade = sourceEquip.GetUpgrade(); +local amplification = sourceEquip.GetAmplification(); +local enchanting = sourceEquip.GetEnchanting(); + +// 应用到目标装备 +local targetEquip = InvenObj.GetSlot(Inven.INVENTORY_TYPE_BODY, 10); +targetEquip.SetForging(forging); +targetEquip.SetUpgrade(upgrade); +targetEquip.SetAmplification(amplification); +targetEquip.SetEnchanting(enchanting); +targetEquip.Flush(); + +// 刷新客户端 +SUser.SendUpdateItemList(1, 0, 9); +SUser.SendUpdateItemList(1, 3, 10); +``` + +--- + +## ⏰ 定时器系统 + +### 延迟任务 +```nut +// 延迟执行(单位:游戏tick,60tick≈1秒) +Timer.SetTimeOut(function() { + SUser.SendNotiPacketMessage("延迟执行", 8); +}, 300); // 5秒后执行 + +// 带参数的延迟执行 +Timer.SetTimeOut(function(arg1, arg2) { + print("参数1: " + arg1 + ", 参数2: " + arg2); +}, 300, "hello", 123); +``` + +### 定时任务 +```nut +// 移除旧任务 +Timer.RemoveCronTask("任务名称"); + +// 创建定时任务 +Timer.SetCronTask(function() { + // 定时执行的代码 + print("定时任务执行"); + return true; // 返回false取消下次执行 +}.bindenv(this), { + Cron = "0 */5 * * * *", // Cron表达式:每5分钟 + Name = "任务名称" +}); + +// Cron表达式格式:秒 分 时 日 月 周 +// 示例: +// "0 0 * * * *" - 每小时 +// "0 */5 * * * *" - 每5分钟 +// "0 0 0 * * *" - 每天0点 +``` + +--- + +## 🔍 内存操作 + +### 基本读写 +```nut +// 写入操作 +NativePointer("0x8360C38").add(3).writeU8(MaxLevel); +NativePointer("0x081E907E").writeInt(value); + +// 读取操作 +local value = NativePointer("0x8360C38").add(3).readS8(); +local value = NativePointer(address).readInt(); +local value = NativePointer(address).readPointer(); +local str = NativePointer(address).readUtf8String(); +``` + +### 字节数组操作 +```nut +// 读取字节数组 +local arr = NativePointer(address).readByteArray(size); + +// 写入字节数组(内存补丁) +Sq_WriteByteArr(S_Ptr("0x081E907E"), [0x90, 0x90, 0x90, 0x90, 0x90]); +``` + +### 原生函数调用 +```nut +// 调用原生函数 +local result = Sq_CallFunc( + S_Ptr("0x082947A4"), // 函数地址 + "pointer", // 返回类型 + ["pointer", "int"], // 参数类型 + arg1, arg2 // 参数值 +); + +// 常用原生函数 +Sq_CallFunc(S_Ptr("0x0858EFDE"), "int", ["pointer", "pointer", "int"], + S_Ptr("0x0"), SUser.C_Object, newLevel); +``` + +--- + +## 🏗️ 模块开发模板 + +### 标准模块结构 +```nut +/* +文件名:模块名.nut +路径:MyProject/模块名.nut +创建日期:2026-03-16 HH:MM +文件用途:模块功能描述 +*/ + +Timer.SetTimeOut(function() { + // 创建模块实例并挂载到根表 + getroottable()._模块名_ <- ModuleClass(); + print("模块名 - 已加载"); +}, 1); + +class ModuleClass { + MysqlObject = null; + data = null; + + constructor() { + // 初始化数据库 + MysqlObject = Mysql(Str_Ptr("127.0.0.1"), 3306, + Str_Ptr("数据库名"), Str_Ptr("账号"), Str_Ptr("密码")); + + // 初始化数据 + data = {}; + + // 注册回调 + RegisterCallbacks(); + + // 初始化数据 + InitData(); + } + + function RegisterCallbacks() { + // 注册GM命令 + Gm_InputFunc_Handle["模块命令"] <- function(SUser, CmdString) { + // 命令处理 + }.bindenv(this); + + // 注册事件回调 + Cb_reach_game_world_Func["模块名"] <- function(SUser) { + // 玩家上线处理 + }.bindenv(this); + + // 注册Hook回调 + Cb_HookName_Enter_Func["模块名"] <- function(args) { + // Hook处理 + }.bindenv(this); + } + + function InitData() { + // 初始化配置和数据 + try { + MysqlObject.Exec_Sql("CREATE TABLE IF NOT EXISTS table_name (...)"); + print("数据表初始化成功"); + } catch (exception) { + print("数据表初始化失败"); + } + } + + function SaveData() { + // 保存数据到数据库 + } + + function LoadData() { + // 从数据库加载数据 + } +} +``` + +--- + +## 🐛 调试与测试 + +### 调试命令 +``` +// 在游戏中输入 +T // 显示当前玩家调试信息 +ResetScript // 重载所有脚本 +``` + +### 调试输出 +```nut +// 启用调试模式 +getroottable().DebugModelFlag <- true; + +// 启用包调试 +getroottable().PacketDebugModel <- true; + +// 输出调试信息 +print("调试信息: " + value); + +// 输出表格 +printT(TableObject); + +// 查看回调注册 +print(Cb_reach_game_world_Func); +``` + +### 错误处理 +```nut +try { + // 可能失败的操作 + local result = SomeOperation(); +} catch (exception) { + print("Error: " + exception); + if (SUser) { + SUser.SendNotiPacketMessage("操作失败", 8); + } +} + +// 数据验证 +if (!SUser) return; +if (!InvenObj) return; +if (!Equip) return; +``` + +--- + +## 📋 常见任务清单 + +### 任务1: 添加GM命令 +- 创建命令处理函数 +- 解析参数 +- 实现功能逻辑 +- 发送反馈消息 + +### 任务2: 监听游戏事件 +- 确定事件类型 +- 找到对应回调表 +- 注册处理函数 +- 处理事件数据 + +### 任务3: 修改游戏配置 +- 找到配置内存地址 +- 使用NativePointer写入 +- 测试修改效果 + +### 任务4: 创建自定义道具 +- 注册道具使用回调 +- 实现道具效果 +- 验证和反馈 + +### 任务5: 实现新功能模块 +- 创建类结构 +- 初始化数据库 +- 注册回调和Hook +- 实现业务逻辑 +- 添加定时任务 + +--- + +## 🎓 开发建议 + +### 通用开发规范 +1. **命名规范**: 使用 `_模块名_` 作为全局表前缀,避免冲突 +2. **延迟加载**: 使用 `Timer.SetTimeOut` 避免初始化顺序问题 +3. **错误处理**: 关键操作使用 try-catch 包裹 +4. **内存管理**: 及时删除 Packet 对象,释放内存 +5. **数据验证**: 检查 `rawin()` 和对象有效性 +6. **性能优化**: 高频查询使用连接池,定时任务批量保存 +7. **代码注释**: 添加文件头注释说明用途 +8. **版本检查**: 使用 `DP_S_VERSION` 判断服务器版本 + +### OfficialProject 开发规范 + +#### 1. 模块命名 +- 使用中文命名,清晰表达功能 +- 避免使用特殊字符和空格 +- 示例: `杂项功能`, `战力榜`, `副本播报` + +#### 2. 文件结构 +- 主脚本文件名与目录名一致 +- 必须包含标准文件头注释 +- 配置文件放在 OfficialConfig/ 目录 + +#### 3. 主函数命名 +- 使用 `_模块名_Main_` 格式 +- 用于模块初始化和重载 +- 示例: `_杂项功能_Main_()`, `_战力榜_Main_()` + +#### 4. 配置管理 +- 使用 GlobalConfig.Get() 读取配置 +- 配置文件使用 JSON 格式 +- 支持配置热重载 + +#### 5. Hook 回调命名 +- 使用 `模块名.功能名` 格式 +- 示例: `杂项功能.BanRedeemItem`, `战力榜.UpdateRank` + +#### 6. 数据库操作 +- 使用连接池模式 +- 使用 try-catch 处理异常 +- 及时归还连接 + +#### 7. 播报系统 +- 使用 AdMsg() 类发送公告 +- 支持富文本和链接 +- 注意公告频率控制 + +### OfficialConfig 开发规范 + +#### 1. 配置文件命名 +- `功能名.json` - 通用配置 +- `功能名_服务器名.json` - 服务器特定配置 +- `功能名_管理员昵称.json` - 管理员自定义配置 + +#### 2. JSON 结构规范 +- 使用中文键名,提高可读性 +- 包含必要的注释字段(如 "提示", "说明") +- 支持嵌套结构 + +#### 3. 数据库配置 +- 使用单独的配置节 +- 包含 IP、端口、用户名、密码 +- 提供默认值和提示 + +#### 4. 开关控制 +- 使用布尔值控制功能开关 +- 数组类型的配置使用 `[开关, 值]` 格式 +- 提供清晰的说明 + +#### 5. 排除列表 +- 使用数组存储排除项 +- 支持 CID、道具ID、副本ID 等 +- 提供注释说明如何获取 + +--- + +## 📞 快速参考 + +### User常用方法 +- GetArea(), GetLocation(), GetUID(), GetCID() +- GetCharacLevel(), SetCharacLevel() +- GetCharacName(), GetCharacJob() +- GiveItem(itemId, quantity) +- SendNotiPacketMessage(msg, type) +- ChangeTown(town, area, x, y) + +### Packet方法 +- Put_Header(op, sub), Put_Int(v), Put_Str(s) +- Send(SUser) + +### 数据类型 +- 整数: `tointeger()` +- 字符串: `tostring()` +- 布尔: `true` / `false` +- 表: `{key = value}` +- 数组: `[value1, value2]` diff --git a/lib/libAurora.so b/lib/libAurora.so index 7261219..5b40939 100644 Binary files a/lib/libAurora.so and b/lib/libAurora.so differ diff --git a/temp_content.txt b/temp_content.txt new file mode 100644 index 0000000..e69de29