feat: 添加露露定制内容系统和开发技能文档

新增露露定制内容系统,实现稀有物品掉落功能
添加DP-S游戏服务器插件开发技能文档,包含框架架构、模块开发规范和常用API参考
This commit is contained in:
2026-04-09 23:48:15 +08:00
parent e4061fab4c
commit aac9cd20c7
7 changed files with 1022 additions and 172 deletions

View File

@@ -116,10 +116,13 @@ Gm_InputFunc_Handle["点券"] <- function(SUser, CmdString) {
Gm_InputFunc_Handle["test"] <- 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() { Timer.SetTimeOut(function() {
// _Dps_Equ2AvaJewel_Main_() // _Dps_Equ2AvaJewel_Main_()
// local Pack = Packet(); // 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_Enter_Func <- {};
// Cb_wdzsdfge_Leave_Func <- {}; // Cb_wdzsdfge_Leave_Func <- {};
@@ -170,5 +177,3 @@ Gm_InputFunc_Handle["我要升级"] <- function(SUser, CmdString) {
Gm_InputFunc_Handle["南瓜村"] <- function(SUser, CmdString) { Gm_InputFunc_Handle["南瓜村"] <- function(SUser, CmdString) {
getroottable()._NewTitle_.AddTitle(SUser, 1); getroottable()._NewTitle_.AddTitle(SUser, 1);
} }

View File

@@ -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);

View File

@@ -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);

View File

@@ -15,4 +15,7 @@ function InitPluginInfo() {
function main() { function main() {
InitPluginInfo(); InitPluginInfo();
} }

957
dp_s_development.skill Normal file
View File

@@ -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
// 延迟执行单位游戏tick60tick≈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]`

Binary file not shown.

0
temp_content.txt Normal file
View File