Files
DP-S_Script/dp_s_development.skill
Lenheart aac9cd20c7 feat: 添加露露定制内容系统和开发技能文档
新增露露定制内容系统,实现稀有物品掉落功能
添加DP-S游戏服务器插件开发技能文档,包含框架架构、模块开发规范和常用API参考
2026-04-09 23:48:15 +08:00

958 lines
24 KiB
Plaintext
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.
# 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]`