同步官网文档8m_25d
195
mconline/30-网络服插件教程/4-插件制作/1-规范.md
Normal file
@@ -0,0 +1,195 @@
|
||||
---
|
||||
front: https://nie.res.netease.com/r/pic/20220408/e24733db-2cc1-49ee-bacb-c1913b2e6c82.png
|
||||
hard: 入门
|
||||
time: 10分钟
|
||||
selection: true
|
||||
---
|
||||
|
||||
# 规范
|
||||
|
||||
制作符合规范的插件,不仅可以增加可读性,还可以减少插件间的冲突问题,并且更加便于使用,方便二次开发。
|
||||
|
||||
本节将介绍插件开发的相关规范。
|
||||
|
||||
## 设计文档规范
|
||||
|
||||
在设计插件的时候,用文档将设计方案记录。一方面便于将插件功能转化成开发者所需的说明文档,另一方面便于日后二次开发。
|
||||
|
||||
文档需要包含一下几个方面:
|
||||
|
||||
- 插件功能描述(若功能流程较为复杂,最好能包含流程图)
|
||||
- UI示意图
|
||||
- 配置说明
|
||||
- API
|
||||
- 事件以及运营指令
|
||||
- 功能描述
|
||||
|
||||
[点我](./设计文档示例.html)查看随身仓库插件的设计文档示例。
|
||||
|
||||
## 插件规范
|
||||
|
||||
插件目录及命名的规范可以参考[这里](https://g.126.fm/02YRPPK)
|
||||
|
||||
## 介绍文档规范
|
||||
|
||||
介绍文档,即插件目录下的readme.txt,为了使他人更好地理解并使用你的插件,需要根据规范编写readme.txt。
|
||||
|
||||
具体规范可参考[这里](https://g.126.fm/02AskEc)
|
||||
|
||||
例如下方的neteaseMenus的readme.txt
|
||||
|
||||
```
|
||||
插件介绍:
|
||||
该服务器Mod隶属于“主菜单”插件。
|
||||
“主菜单”插件实现主菜单功能:
|
||||
- 主菜单:定义并在游戏中生效主菜单(配置于lobby/game下的mod.json)
|
||||
|
||||
插件构成:
|
||||
目前“主菜单”插件包含以下Mod:
|
||||
- neteaseMenus:部署于大厅服或游戏服
|
||||
|
||||
使用步骤:
|
||||
(1)在部署配置中,将neteaseMenus添加至需要的大厅服或者游戏服的mods列表中
|
||||
|
||||
插件api:
|
||||
(1)设置主菜单按钮状态
|
||||
适用范围:客户端
|
||||
函数:UpdateMenus(data)
|
||||
参数:
|
||||
data: dict, 设置按钮状态的字典,格式参照下面例子
|
||||
返回:
|
||||
bool, 是否设置成功,注意设置失败也有可能已经改变了菜单状态
|
||||
示例:
|
||||
import client.extraClientApi as clientApi
|
||||
data = { # key对应按钮序号,从0开始,value为(0, 1)中的一个数字,【0】代表禁用该按钮,【1】代表开启该按钮
|
||||
1: 0,
|
||||
4: 1,
|
||||
16: -1,
|
||||
}
|
||||
menusSystem = clientApi.GetSystem("neteaseMenus", "neteaseMenusBeh")
|
||||
flag = menusSystem.UpdateMenus(data)
|
||||
|
||||
(2)获取主菜单所有按钮配置
|
||||
适用范围:客户端
|
||||
函数:GetMenus()
|
||||
参数:
|
||||
无
|
||||
返回:
|
||||
menus:dict 主菜单按钮配置,结构对应mod.json中的配置,详见mod.json
|
||||
示例:
|
||||
import client.extraClientApi as clientApi
|
||||
menusSystem = clientApi.GetSystem("neteaseMenus", "neteaseMenusBeh")
|
||||
menus = menusSystem.GetMenus() # 结构对应mod.json中的配置,详见mod.json
|
||||
|
||||
插件event:
|
||||
(1)MenusNavigateEvent
|
||||
适用范围:客户端
|
||||
命名空间:namespace = 'neteaseMenus', systemname = 'neteaseMenusBeh'
|
||||
描述:玩家点击了某按钮事件
|
||||
参数:
|
||||
playerId: str, 点击按钮玩家的playerId
|
||||
order: int, 被点击按钮的序号,从0开始
|
||||
示例:
|
||||
def __init__(self, namespace, systemName):
|
||||
self.ListenForEvent('neteaseMenus', 'neteaseMenusBeh', 'MenusNavigateEvent', self, self.OnMenusNavigate)
|
||||
|
||||
def OnMenusNavigate(self, data):
|
||||
print 'OnMenusNavigate', data
|
||||
playerId = data["playerId"]
|
||||
order = data["order"]
|
||||
print '玩家 {} 点击了主菜单中的 {} 号按钮'.format(playerId, order)
|
||||
|
||||
更新列表:
|
||||
1.0.0版本:
|
||||
初始版本
|
||||
1.0.1版本:
|
||||
调整了UI资源,增加了代码注释
|
||||
1.0.2版本:
|
||||
按照新规范调整UI和代码
|
||||
1.0.3版本:
|
||||
迭代新UI和代码
|
||||
1.0.4版本:
|
||||
重构了UI和代码,配置方式也发生了变化
|
||||
1.0.5版本:
|
||||
优化插件readme文档描述
|
||||
```
|
||||
|
||||
包含了以下内容:
|
||||
|
||||
- 插件介绍
|
||||
- 插件构成
|
||||
- 使用步骤
|
||||
- 插件api
|
||||
- 插件event
|
||||
- 更新列表
|
||||
|
||||
在上方例子的插件中,没有包含**运营指令**和**前置插件**的相关介绍。如果在你编写的插件中有运营指令和前置插件的需求,也需要将它们都写上。
|
||||
|
||||
例如下方的是经济插件的运营指令的介绍。
|
||||
|
||||
```
|
||||
支持的运营指令:
|
||||
运营指令:
|
||||
(1)查询一个玩家身上的货币剩余数量(该玩家需在线)
|
||||
post url: http:masterip:masterport/query-player-doughs
|
||||
post body:{
|
||||
"uid": 996 # 玩家的uid
|
||||
}
|
||||
response:
|
||||
{
|
||||
"code": 1, # 返回码,【1】代表成功,其他代表失败
|
||||
"message": "请求成功", # 失败原因的具体描述
|
||||
"entity": { # 返回该玩家身上货币剩余数量信息,查询失败则为空字典
|
||||
"RMB": 996,
|
||||
"USD": -996
|
||||
}
|
||||
}
|
||||
(2)为一个玩家添加/减少货币(参数:玩家id、货币类型、货币数量,货币数量可为负数)
|
||||
post url: http:masterip:masterport/update-player-doughs
|
||||
post body:{
|
||||
"uid": 996, # 玩家的uid
|
||||
"mod": { # 修改货币数据的字典
|
||||
"RMB": 996,
|
||||
"USD": -996
|
||||
}
|
||||
}
|
||||
response:
|
||||
{
|
||||
"code": 1, # 返回码,【1】代表成功,其他代表失败
|
||||
"message": "请求成功", # 失败原因的具体描述
|
||||
"entity": { # 返回该玩家身上货币剩余数量信息,操作失败则为空字典
|
||||
"RMB": 1992,
|
||||
"USD": -1992
|
||||
}
|
||||
}
|
||||
(3)查询当前总共有多少个出售摊位(2.0.0新增)
|
||||
post url: http:masterip:masterport/count-stalls
|
||||
post body:{}
|
||||
response:
|
||||
{
|
||||
"code": 1, # 返回码,【1】代表成功,其他代表失败
|
||||
"message": "请求成功", # 失败原因的具体描述
|
||||
"entity": {
|
||||
"stalls": {
|
||||
"996": {
|
||||
"duration": 12, # 摆摊总时长
|
||||
"deadline": 1590462263, # 摆摊自动收摊时间戳
|
||||
"ver": 0, # 无用
|
||||
"label": "好货不便宜", # 摊位名称
|
||||
"deals": [...] # 该摊位的出售记录
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
前置要求参考下方面对面交易插件
|
||||
|
||||
```
|
||||
插件介绍:
|
||||
该服务器Mod隶属于“面对面交易”插件。
|
||||
“交易”插件实现2个主要功能,1个是交易货币,1个是交易物品:
|
||||
- 交易货币:弱依赖经济插件,需要配置对应的货币类型和货币贴图。也可以自行进行管理,只需要在transactionServerSystem.py中搜索“经济插件”,并把对应的获取货币和更新货币的地方换成自己的接口管理即可
|
||||
- 交易物品:玩家可以通过选择背包中的物品及数量进行交易
|
||||
```
|
||||
|
||||
98
mconline/30-网络服插件教程/4-插件制作/2-知识点拆解.md
Normal file
@@ -0,0 +1,98 @@
|
||||
# 知识点拆解
|
||||
|
||||
本节将通过官方插件的分析,加强概念的理解。
|
||||
|
||||
## 文件结构
|
||||
|
||||
按照下列流程,找到之前举例安装的neteaseDaily的文件夹。
|
||||
|
||||
1. 基岩版服务器
|
||||
2. 插件
|
||||
3. neteaseDaily
|
||||
4. 右键,打开目录
|
||||
|
||||
在弹出的文件夹中,可以看到插件目录下,有4个文件夹。
|
||||
|
||||

|
||||
|
||||
那么behavior_packs中所包含的,就是发送到玩家设备上运行的客户端mod;developer_mods中所包含的,是运行在服务器上的mod;resource_packs中所包含的,是客户端的资源文件,也会被发送到玩家设备;worlds是mod的地图信息。
|
||||
|
||||
如果对这部分有所遗忘,可以点击[这里](../3-插件知识进阶/3-插件文件夹结构.html)进行回顾。
|
||||
|
||||
## 回调
|
||||
|
||||
回调是编程中非常常用的一个技术,在ModSDK和Apollo插件的开发中,也经常用到它。
|
||||
|
||||
比如在监听事件时,用到的ListenForEvent中,就用到了它。
|
||||
|
||||
例如下方的语句中,self.OnServerChat就是一个回调函数,在ListenForEvent函数调用后,引擎会将其保存,并在ServerChatEvent触发时,调用这个函数。
|
||||
|
||||
```python
|
||||
self.ListenForEvent(serverApi.GetEngineNamespace(), serverApi.GetEngineSystemName(), 'ServerChatEvent', self, self.OnServerChat)
|
||||
```
|
||||
|
||||
在Apollo开发中,也同样经常用到回调函数,就比如在neteaseDaily中。
|
||||
|
||||
打开```developer_mods/neteaseDailyDev/neteaseDailyScript/dailyServerSystem.py```,在这个文件中,可以看到RequestToService之后,调用了DailyServerRender函数作为回调函数。
|
||||
|
||||
```python
|
||||
def OpenDailyReward(self, uid):
|
||||
"""
|
||||
使一个玩家打开每日登录奖励的界面
|
||||
"""
|
||||
stamp = time.time()
|
||||
print 'OpenDailyReward', uid, stamp
|
||||
playerId = netgameApi.GetPlayerIdByUid(uid)
|
||||
if not playerId:
|
||||
print 'can not get playerId by uid: %s' % uid
|
||||
return
|
||||
if self.mRewards is None:
|
||||
logout.warning('因无可用奖励配置无法打开每日登录奖励界面')
|
||||
return
|
||||
if stamp < self.mRewards[0][0]:
|
||||
print 'not yet', self.mRewards[0][0]
|
||||
return
|
||||
self.RequestToService(
|
||||
dailyConst.ModName,
|
||||
dailyConst.DisplayDailyRewardEvent,
|
||||
{'uid': uid, 'stamp': stamp},
|
||||
lambda rtn, data: rtn and self.DailyServerRender(playerId, dailyConst.DisplayDailyRewardEvent, data)
|
||||
)
|
||||
```
|
||||
|
||||
|
||||
|
||||
## 功能服
|
||||
|
||||
功能服连接所有服务器和数据库,能够获取所有服务器的信息,可以处理一些全服操作。
|
||||
|
||||
比如在聊天插件中,如果在本地频道发消息,不需要经过功能服转发,但如果要在世界频道发消息,就需要在功能服中进行转发,把消息发到所有大厅服和游戏服。
|
||||
|
||||
具体代码可以下载官方聊天插件 neteaseChatService 查看。
|
||||
|
||||
## 数据库
|
||||
|
||||
在官方插件云端玩家信息插件,neteaseCloud中,找到mod.sql文件。
|
||||
|
||||
这个文件便是插件的MySQL数据表结构。文件如下:
|
||||
|
||||
```sql
|
||||
-- ###########################version1.0.0####################
|
||||
-- 云端背包
|
||||
CREATE TABLE IF NOT EXISTS `neteaseCloudItems` (
|
||||
`_id` INT UNSIGNED NOT NULL auto_increment COMMENT '唯一ID,自增',
|
||||
`uid` BIGINT UNSIGNED NOT NULL COMMENT '玩家uid',
|
||||
`cloud_items` varchar(10000) NOT NULL DEFAULT '' COMMENT '记录云背包内容',
|
||||
PRIMARY KEY (_id) COMMENT '主键',
|
||||
INDEX `uid_idx` (`uid`) COMMENT '索引'
|
||||
)ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
-- ###########################version2.0.0####################
|
||||
alter table neteaseCloudItems add column apply_tag varchar(20) not null default "";
|
||||
ALTER TABLE neteaseCloudItems ADD INDEX apply_tag_index (`apply_tag`, `uid`);
|
||||
```
|
||||
|
||||
sql文件使用SQL语句编写,需要掌握一定的SQL知识,可以访问下方链接进行了解。
|
||||
|
||||
https://www.w3school.com.cn/sql/index.asp
|
||||
|
||||
而这个数据表,则存储了玩家的背包信息和uid,服务器可以很方便地使用uid查找到对应的背包信息。
|
||||
36
mconline/30-网络服插件教程/4-插件制作/3-插件编写——需求篇.md
Normal file
@@ -0,0 +1,36 @@
|
||||
# 插件编写——需求篇
|
||||
|
||||
在开始编写插件之前,我们需要首先明确插件需要什么功能,具体细节是什么样的以及各种用于二次开发的API需求和事件需求。
|
||||
|
||||
在接下来的篇幅中,将以抽奖插件为例,按照流程制作一个完整的插件。
|
||||
|
||||
## 插件需求
|
||||
|
||||
每日进行1期抽奖,每位玩家可随机领取3个不同的号码,号码取值是1~10000,领完为止。每日20:00开奖,通过邮件(官方neteaseAnnounced插件)告知中奖玩家并发放奖励。
|
||||
|
||||
## 细节需求
|
||||
|
||||
- 玩家可在game服或lobby服,输入cp1领取号码,成功领取后,用官方neteaseAlert插件提示“本次领取的号码是%s”
|
||||
- 奖励号码每日开奖后重置,玩家可重新领取。
|
||||
- 若超过当日可领取的上限:3,输入cp1领取时,用官方neteaseAlert插件提示“今日领取的号码已超过上限”
|
||||
- 每日开奖时间之前(上一天的20:01~当天20:00),输入cp2可查询已领取的号码。
|
||||
- 官方neteaseAlert插件提示已领取的号码情况“今日已领取%s(数量)个号码:%s、%s、%s”
|
||||
- 从1~10000中随机抽取5个号码作为本期中奖号码。
|
||||
- 中奖的邮件提示:本期中奖号码:%s、%s、%s、%s、%s。恭喜你,在本期抽奖活动中中奖,请再接再厉。邮件附件内容:黑曜石*5
|
||||
- 没中奖的邮件提示:本期中奖号码:%s、%s、%s、%s、%s。很遗憾,在本期抽奖活动中未能中奖,请继续努力。邮件附件内容:铁锭*3
|
||||
- 当天没有领取任何号码的玩家无需发放邮件。
|
||||
|
||||
## API需求
|
||||
|
||||
- 给指定玩家随机获得1个号码(必须是本期活动没有获得过的,如果没有号码则返回-1)
|
||||
- 返回玩家已获取的号码数量
|
||||
- 返回玩家已获取的号码列表
|
||||
- 重置某个玩家的号码
|
||||
- 重置所有号码
|
||||
- 执行发奖,返回获奖的号码列表以及获奖玩家列表
|
||||
|
||||
## 事件需求
|
||||
|
||||
- 发奖时刻之前触发——返回中奖号码,可修改中奖号码
|
||||
- 发奖时刻之后触发——返回中奖号码与中奖玩家
|
||||
|
||||
87
mconline/30-网络服插件教程/4-插件制作/4-插件编写——制作篇(上).md
Normal file
@@ -0,0 +1,87 @@
|
||||
# 插件编写——制作篇(上)
|
||||
|
||||
## 需求分析
|
||||
|
||||
之前提到过,Apollo插件拥有3种不同的类型,我们在开始编写前,首先需要思考哪些需求可以运行在哪个类型的服务器上。
|
||||
|
||||
结构图如下
|
||||
|
||||

|
||||
|
||||
### Game/Lobby
|
||||
|
||||
- 输入cp1领取号码,成功领取后,用官方neteaseAlert插件提示“本次领取的号码是%s”
|
||||
- 若超过当日可领取的上限:3,输入cp1领取时,用官方neteaseAlert插件提示“今日领取的号码已超过上限”
|
||||
- 每日开奖时间之前(上一天的20:01~当天20:00),输入cp2可查询已领取的号码。
|
||||
- 官方neteaseAlert插件提示已领取的号码情况“今日已领取%s(数量)个号码:%s、%s、%s”
|
||||
|
||||
> 为什么聊天输入相关的监听需要做在Game/Lobby服务器
|
||||
|
||||
因为玩家只会在Game/Lobby上在线,只有在这两种服务器上才能监听玩家的聊天信息。
|
||||
|
||||
### Service
|
||||
|
||||
- 每日20:00开奖,通过邮件(官方neteaseAnnounce插件)告知中奖玩家并发放奖励。
|
||||
- 中奖的邮件提示:本期中奖号码:%s、%s、%s、%s、%s。恭喜你,在本期抽奖活动中中奖,请再接再厉。邮件附件内容:黑曜石*5
|
||||
- 没中奖的邮件提示:本期中奖号码:%s、%s、%s、%s、%s。很遗憾,在本期抽奖活动中未能中奖,请继续努力。邮件附件内容:铁锭*3
|
||||
- 服务器API——给指定玩家随机获得1个号码(必须是本期活动没有获得过的,如果没有号码则返回-1)
|
||||
- 服务器API——返回玩家已获取的号码数量
|
||||
- 服务器API——返回玩家已获取的号码列表
|
||||
- 服务器API——重置某个玩家的号码
|
||||
- 服务器API——重置所有号码
|
||||
- 服务器API——执行发奖,返回获奖的号码列表以及获奖玩家列表
|
||||
- 发奖时刻之前触发——返回中奖号码,可修改中奖号码
|
||||
- 发奖时刻之后触发——返回中奖号码与中奖玩家
|
||||
|
||||
> 为什么“每日20:00开奖,通过邮件(官方neteaseAnnounce插件)告知中奖玩家并发放奖励。”需要做在Service服务器
|
||||
|
||||
因为发奖需要统一操作,而不可能所有玩家都在同一个Game/Lobby中,数据处理放在Service上则可以统一处理,更方便。
|
||||
|
||||
### Master
|
||||
|
||||
无
|
||||
|
||||
|
||||
|
||||
# 开发查询和参考链接
|
||||
|
||||
### Mod SDK
|
||||
|
||||
对游戏相关SDK不熟悉,可以在这里查询。
|
||||
|
||||
- 接口: <a href="../../../mcdocs/1-ModAPI/接口/通用/索引.html" rel="noopenner"> 链接 </a>
|
||||
- 事件:<a href="../../../mcdocs/1-ModAPI/事件/世界.html" rel="noopenner"> 链接 </a>
|
||||
|
||||
### Apollo
|
||||
|
||||
对Apollo的专用接口有疑问,可以在这里查询。
|
||||
|
||||
- <a href="../../../mcdocs/2-Apollo/4-SDK/1-大厅与游戏服事件.html" rel="noopenner"> 链接 </a>
|
||||
|
||||
### Apollo 插件地址
|
||||
|
||||
在这里可以查看已经制作好的插件,并了解它们各自的功能。
|
||||
|
||||
- 官方插件:<a href="../../../mcdocs/2-Apollo/5-官方插件简介.html" rel="noopenner"> 链接</a>
|
||||
- 第三方插件:<a href="../../../mcdocs/2-Apollo/6-第三方插件简介.html" rel="noopenner"> 链接</a>
|
||||
|
||||
# 代码编写
|
||||
|
||||
首先点击新建空白Addon,作品名称填写Lottery为例
|
||||
|
||||

|
||||
|
||||
点击启动编辑,会打开编辑器。但是我们创建项目的目的是为了快速生成一个Apollo插件模板,所以在弹出编辑器窗口后关闭即可。
|
||||
|
||||
随后在基岩版组件中,找到我们刚刚创建的名为Lottery的AddOn,右键选择,点击转换为服务器Mod。
|
||||
|
||||
<img src="./images/code-2.png" style="zoom:150%;" />
|
||||
|
||||
弹出的窗口中,选项不需要做特别修改,直接点击转换。
|
||||
|
||||
随后在基岩版服务器插件中,找到刚刚创建的Lottery,右键打开目录。因为我们之前已经将需求归类,总结出需要Game/Lobby服和Service服的插件,所以我们将文件夹复制,并重命名成符合规范的。这里我们复制一份Lottery并重命名成soldierLottery,再将原来的文件夹重命名成soldierLotteryService。
|
||||
|
||||
操作完成后文件夹的样子应该如截图所示。
|
||||
|
||||

|
||||
|
||||
271
mconline/30-网络服插件教程/4-插件制作/5-插件编写——制作篇(中).md
Normal file
@@ -0,0 +1,271 @@
|
||||
# 插件编写——制作篇(中)
|
||||
|
||||
## Service编写
|
||||
|
||||
根据之前对Service的了解,Service中是只应该有developer_mods文件夹的,而我们现在的soldierLotteryService文件夹,因为是从Game/Lobby复制而来的,所以需要手动将没有用的文件夹删除。只留下developer_mods文件夹,删除下图中选择的文件夹。
|
||||
|
||||

|
||||
|
||||
然后我们根据官方插件规范,将soldierLotteryService/developer_mods的Lottery文件夹,改名为soldierLotteryDev。
|
||||
|
||||
这里使用IntelliJ IDEA (PyCharm同理)为例,介绍插件代码的编写。
|
||||
|
||||
进入IDEA后,打开soldierLotteryService的项目,找到mod.json,按照官方规范进行修改,修改后的mod.json供参考。需要注意的是,support_server_type需要更改成service,否则无法在Service服部署。
|
||||
|
||||
```json
|
||||
{
|
||||
"netgame_mod_name": "soldierLotteryDev",
|
||||
"netgame_mod_version": "1.0.0",
|
||||
"min_app_version": "1.23.0.release20210722",
|
||||
"author": "Soldier",
|
||||
"module_names": "soldierLottery",
|
||||
"support_server_type": [
|
||||
"service"
|
||||
],
|
||||
"group": "soldierLottery"
|
||||
}
|
||||
```
|
||||
|
||||
随后我们就可以开始编写代码了,Service服的入口modMain和一般Mod的结构非常相似,创建一个consts.py用来存放一些常量后,就可以参照下方代码编写了。
|
||||
|
||||
modMain.py
|
||||
|
||||
```python
|
||||
# coding=utf-8
|
||||
from mod.common.mod import Mod
|
||||
import mod.server.extraServiceApi as serviceApi
|
||||
|
||||
from soldierLotteryScripts.consts import ModName, ModVersion, ServiceSystemName, ServiceSystemClsPath
|
||||
|
||||
|
||||
@Mod.Binding(name=ModName, version=ModVersion)
|
||||
class SoldierLotteryService(object):
|
||||
@Mod.InitService()
|
||||
def InitService(self):
|
||||
serviceApi.RegisterSystem(ModName, ServiceSystemName, ServiceSystemClsPath)
|
||||
print "SoldierLotteryService启动"
|
||||
|
||||
@Mod.DestroyService()
|
||||
def DestroyService(self):
|
||||
print "SoldierLotteryService卸载"
|
||||
|
||||
```
|
||||
|
||||
lotteryServiveSystem.py
|
||||
|
||||
```python
|
||||
# coding=utf-8
|
||||
from mod.server.system.serviceSystem import ServiceSystem
|
||||
|
||||
from soldierLotteryScripts.mysqlManager import MysqlManager
|
||||
|
||||
|
||||
class LotteryServiceSystem(ServiceSystem):
|
||||
|
||||
def __init__(self, namespace, systemName):
|
||||
ServiceSystem.__init__(self, namespace, systemName)
|
||||
```
|
||||
|
||||
随后我们可以开始编写MySQL数据库相关的代码。
|
||||
|
||||
在编写数据库的代码之前,我们首先需要设计好数据表结构。分析需求,可以得出该插件需要两张数据表,分别用来存储抽奖信息和玩家信息。
|
||||
|
||||
### 抽奖信息表
|
||||
|
||||
主要需要记录 抽奖id,是否开奖
|
||||
|
||||

|
||||
|
||||
因为这张表我们只需要通过id来设置是否已经开奖,而id又是主键,自带索引。所以无需创建索引,直接设计好后将SQL复制到mod.sql中即可。
|
||||
|
||||
```sql
|
||||
CREATE TABLE `soldierLotteryState` (
|
||||
`id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '自增ID既抽奖ID',
|
||||
`finish` int(1) UNSIGNED NOT NULL COMMENT '是否已经开奖',
|
||||
PRIMARY KEY (`id`)
|
||||
);
|
||||
```
|
||||
|
||||
### 玩家信息表
|
||||
|
||||
主要需要记录 玩家uid,玩家持有的抽奖id,对应抽奖的号码
|
||||
|
||||
则可以使用Navicat设计出数据表如下图所示。其中需要注意的是,玩家uid的类型必须为**无符号 int**,在sql命令中表达为 ```UNSIGNED INT```,不能是int。
|
||||
|
||||

|
||||
|
||||
由于功能上需要查询指定抽奖id的玩家拥有的号码,同时需要检索player和lottery两列数据,所以我们需要给它们加上索引来提高后续的搜索速度。
|
||||
|
||||
索引添加如下图。(索引涉及到的知识要求相对较高,如果不理解,可以在MySQL相关教程处学习,这里我们简单理解成需要检索哪一列就给哪一列添加索引)
|
||||
|
||||

|
||||
|
||||
随后在SQL预览处将SQL复制出来,自行修改表名,粘贴到mod.sql中。
|
||||
|
||||
```sql
|
||||
CREATE TABLE `soldierLotteryPlayer` (
|
||||
`id` int UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '自增ID',
|
||||
`player` int UNSIGNED NOT NULL COMMENT '玩家UID',
|
||||
`lottery` int(10) NOT NULL COMMENT '抽奖ID',
|
||||
`number` int(10) NOT NULL COMMENT '玩家持有的抽奖号码',
|
||||
PRIMARY KEY (`id`),
|
||||
INDEX `player_lottery`(`player`, `lottery`) USING BTREE COMMENT '玩家uid和抽奖id的索引'
|
||||
);
|
||||
```
|
||||
|
||||
### 数据接口
|
||||
|
||||
设计完数据表,就可以根据需求,编写可能会用到的数据相关接口。
|
||||
|
||||
这里我们对照接口一项一项编写,下面展示```mysqlManager.py```的一部分代码。
|
||||
|
||||
```python
|
||||
# coding=utf-8
|
||||
import random
|
||||
from collections import OrderedDict
|
||||
|
||||
from apolloCommon import mysqlPool
|
||||
|
||||
from soldierLotteryScripts import consts
|
||||
|
||||
|
||||
class MysqlManager(object):
|
||||
"""
|
||||
MysqlManager主要用来管理数据库数据
|
||||
提供函数快捷获取玩家数据
|
||||
"""
|
||||
|
||||
def __init__(self, serviceSystem):
|
||||
self.mServiceSystem = serviceSystem
|
||||
print "初始化MySQL数据库连接"
|
||||
mysqlPool.InitDB(10)
|
||||
self.mPlayerCache = {} # 玩家数据缓存
|
||||
self.mStateCache = OrderedDict() # 抽奖状态缓存
|
||||
self.mLotteryNumbers = {} # 抽奖号码缓存
|
||||
self.CacheLotteryStates()
|
||||
self.CacheAllNumbers()
|
||||
|
||||
def Destroy(self):
|
||||
mysqlPool.Finish()
|
||||
print "关闭MySQL连接"
|
||||
|
||||
def GetPlayerCache(self, uid):
|
||||
"""
|
||||
获取玩家缓存,没有缓存则从数据库获取,并返回None
|
||||
:param uid:
|
||||
:return:
|
||||
"""
|
||||
cache = self.mPlayerCache.get(uid)
|
||||
if cache is not None:
|
||||
return cache
|
||||
|
||||
def callback(uid, result):
|
||||
data = {}
|
||||
if result:
|
||||
for line in result:
|
||||
tmp = data.get(line[0], [])
|
||||
tmp.append(line[1])
|
||||
data[line[0]] = tmp
|
||||
print "缓存玩家{}的数据为{}".format(uid, data)
|
||||
self.mPlayerCache[uid] = data
|
||||
|
||||
sql = "SELECT `lottery`,`number` FROM `{}` WHERE `player` = %s;".format(consts.LotteryPlayerTable)
|
||||
mysqlPool.AsyncQueryWithOrderKey("cache_player_{}".format(uid), sql, (uid,),
|
||||
lambda result: callback(uid, result))
|
||||
return None
|
||||
|
||||
def RemovePlayerCache(self, uid):
|
||||
"""
|
||||
移除玩家缓存,退出时触发
|
||||
:param uid: 玩家UID
|
||||
:return:None
|
||||
"""
|
||||
cache = self.mPlayerCache.get(uid)
|
||||
if cache is None:
|
||||
# 因为正常获取过缓存数据的玩家,都存储过cache为{},不可能是None。所以此处是None的话不处理
|
||||
return
|
||||
del self.mPlayerCache[uid]
|
||||
```
|
||||
|
||||
需要从这串代码中注意的是,所有数据都采用了缓存的方式来处理。即玩家加入游戏,从MySQL获取数据并缓存;玩家退出游戏,如果玩家的数据发生了变化,则存到数据库并删除缓存,如果没变化则直接删除缓存。
|
||||
|
||||
并且,在执行SQL命令时,需要注意尽量让每个操作的**OrderKey**唯一,这样就可以使SQL命令并发执行,提高效率。
|
||||
|
||||
### 发送邮件
|
||||
|
||||
在需求中,有一条是使用官方邮箱插件发送奖励。所以我们首先需要下载官方的邮箱插件,并查看他的简介。
|
||||
|
||||
简介可以在官方插件页面下载后,在插件的右键菜单中找到。
|
||||
|
||||
在简介中可以找到发送邮件的接口
|
||||
|
||||
> (7)向一组指定uid的玩家发送私人邮件(功能服用)
|
||||
> 函数:OutSendMailToMany(touids, title, content, itemList=[], expire=None, srcName="")
|
||||
> 参数:
|
||||
> touids:list(int), 玩家唯一ID的列表
|
||||
> title:str, 邮件标题
|
||||
> content:str, 邮件正文
|
||||
> itemList:list(dict), 附件物品列表,格式与通用的【物品信息字典】相同(参照ModSDk中往背包中塞物品的字典),额外支持【durability】关键字定义耐久度。另外,如果同经济插件一起使用,还支持货币,格式为:
|
||||
> itemType为"currency"表示货币类型,itemName对应经济插件mod.json中dough_id配置,icon对应济插件mod.json中dough_icon配置,count表示货币数量。
|
||||
> expire:int, 邮件有效期,单位秒
|
||||
> srcName:str, 邮件发送者名字
|
||||
> 示例:
|
||||
> import server.extraServiceApi as serviceApi
|
||||
> itemDict = {
|
||||
> 'itemName': 'minecraft:bow',
|
||||
> 'count': 1,
|
||||
> 'enchantData': [(19, 1),],
|
||||
> 'auxValue': 0,
|
||||
> 'customTips':'§c new item §r',
|
||||
> 'extraId': 'abc'
|
||||
> }
|
||||
> currencyDict = {
|
||||
> 'itemName' : 'RMB',
|
||||
> 'itemType' : 'currency',
|
||||
> 'icon' : 'textures/ui/netease_trade/icon03@3x.png',
|
||||
> 'count' : 3
|
||||
> }
|
||||
> mailSystem = serviceApi.GetSystem("neteaseAnnounce", "neteaseAnnounceService")
|
||||
> mailSystem.OutSendMailToMany([123,234], "欢迎新人", "欢迎首次登录,开发组送上弓一把", [itemDict, currencyDict], 86400, "开发组")
|
||||
|
||||
我们可以参考介绍编写发送奖励邮件的代码。
|
||||
|
||||
### 通信
|
||||
|
||||
我们在Service服上编写的接口如果需要被Game/Lobby调用,就需要用到服务器之间的通信。下方的例子就实现了接口请求。对应代码文件```lotteryServiceSystem.py```。
|
||||
|
||||
```python
|
||||
class LotteryServiceSystem(ServiceSystem):
|
||||
|
||||
def __init__(self, namespace, systemName):
|
||||
ServiceSystem.__init__(self, namespace, systemName)
|
||||
self.mMysqlManager = MysqlManager(self)
|
||||
self.RegisterRpcMethodForMod(consts.GetPlayerRandomNumber, self.OnGetPlayerRandomNumber)
|
||||
|
||||
def OnGetPlayerRandomNumber(self, serverId, callbackId, args):
|
||||
player = args["player"]
|
||||
lottery = self.mMysqlManager.GetUnfinishedLottery()
|
||||
if lottery == -1:
|
||||
eventData = {
|
||||
"code": 1,
|
||||
"msg": -1
|
||||
}
|
||||
else:
|
||||
eventData = {
|
||||
"code": 0,
|
||||
"msg": self.mMysqlManager.GetPlayerRandomNumber(player, lottery)
|
||||
}
|
||||
|
||||
self.ResponseToServer(serverId, callbackId, eventData)
|
||||
```
|
||||
|
||||
|
||||
|
||||
### 代码下载
|
||||
|
||||
教程中仅展示了部分代码,全部代码可以在这里下载。
|
||||
|
||||
[抽奖插件——service部分](https://g79.gdl.netease.com/pluginguide04-06.zip)
|
||||
|
||||
随后我们就可以部署并测试啦!
|
||||
|
||||
122
mconline/30-网络服插件教程/4-插件制作/6-插件编写——制作篇(下).md
Normal file
@@ -0,0 +1,122 @@
|
||||
# 插件编写——制作篇(下)
|
||||
|
||||
### Game/Lobby编写
|
||||
|
||||
接下来开始制作Game/Lobby服的插件,我们按照官方要求的规范更改文件夹soldierLottery的命名。并创建脚本文件夹开始编写脚本。
|
||||
|
||||
同时由于我们这个插件不需要行为包和资源包,所以将对应文件夹删除。
|
||||
|
||||
更改命名后文件夹名字如下
|
||||
|
||||
<img src="./images/code-8.png" style="zoom:200%;" />
|
||||
|
||||
然后开始编写Game/Lobby和Service通信的部分,基础部分将不再过多讲解。
|
||||
|
||||
**下方编写的都是```lotteryServerSystem.py```中的代码**
|
||||
|
||||
```python
|
||||
def GetPlayerRandomNumber(self, uid, callback):
|
||||
"""
|
||||
为玩家获取一个随机彩票号码
|
||||
:param uid: 玩家UID
|
||||
:param callback:int 参数唯一,随机彩票号码,请求失败值为-1
|
||||
:return:
|
||||
"""
|
||||
self.RequestToServiceMod(ServiceServerType, GetPlayerRandomNumber, {"player": uid},
|
||||
lambda suc, args: callback(args["msg"]) if suc else callback(-1))
|
||||
```
|
||||
|
||||
例如这个接口,调用后就可以为玩家申请一个随机彩票号码,然后将号码带入回调函数的参数中并调用。Service端的对应相关代码,可以在上一节的最后找到。
|
||||
|
||||
同时我们还需要监听玩家加入和退出,并根据是否转服判断是否需要通知Service服务器对缓存进行处理。
|
||||
|
||||
```python
|
||||
def OnJoin(self, args):
|
||||
playerId = args["id"]
|
||||
uid = args["uid"]
|
||||
self.idUidMap[playerId] = uid
|
||||
if not args["isTransfer"]:
|
||||
print "玩家{}加入,进行缓存".format(uid)
|
||||
self.CachePlayer(uid)
|
||||
|
||||
def OnLeave(self, args):
|
||||
playerId = args["id"]
|
||||
uid = args["uid"]
|
||||
del self.idUidMap[playerId]
|
||||
if not args["isTransfer"]:
|
||||
print "玩家{}退出,删除缓存".format(uid)
|
||||
self.SavePlayer(uid)
|
||||
|
||||
def CachePlayer(self, uid):
|
||||
def callback(suc, args):
|
||||
print "缓存玩家: suc:{} ,args:{}".format(suc, args)
|
||||
|
||||
self.RequestToServiceMod(ServiceServerType, PlayerJoin, {"player": uid}, callback)
|
||||
|
||||
def SavePlayer(self, uid):
|
||||
def callback(suc, args):
|
||||
print "保存玩家: suc:{} ,args:{}".format(suc, args)
|
||||
|
||||
self.RequestToServiceMod(ServiceServerType, PlayerLeave, {"player": uid}, callback)
|
||||
```
|
||||
|
||||
然后再监听聊天事件,判断玩家是否输入cp1,cp2,并编写相应逻辑。
|
||||
|
||||
```python
|
||||
def OnServerChat(self, args):
|
||||
playerId = args["playerId"]
|
||||
message = args["message"]
|
||||
uid = self.idUidMap[playerId]
|
||||
if message == "cp1":
|
||||
args["cancel"] = True
|
||||
|
||||
def callback(number):
|
||||
if number != -1:
|
||||
self.alertSystem.Alert(playerId, '§f本次领取的号码是 §a{}'.format(number), 3)
|
||||
else:
|
||||
self.alertSystem.Alert(playerId, '§f今日领取的号码已超过上限', 3)
|
||||
|
||||
self.GetPlayerRandomNumber(uid, callback)
|
||||
|
||||
elif message == "cp2":
|
||||
args["cancel"] = True
|
||||
|
||||
def callback(numbers):
|
||||
if numbers is None:
|
||||
self.alertSystem.Alert(playerId, '§c获取失败,请稍后再试', 3)
|
||||
return
|
||||
size = len(numbers)
|
||||
if size == 0:
|
||||
self.alertSystem.Alert(playerId, '§c你还没有领取任何号码', 3)
|
||||
else:
|
||||
self.alertSystem.Alert(playerId, '§c今日已领取{}个号码: {}'.format(size, self.FormatNumbers(numbers)), 3)
|
||||
|
||||
self.GetPlayerNumbers(uid, callback)
|
||||
```
|
||||
|
||||
这里使用调用了neteaseAlert插件来发送提示,我们可以到官方插件库中下载neteaseAlert并查看readme.txt,找到我们需要的接口文档。
|
||||
|
||||
> (1)服务端向某一个玩家弹出一个提示,内容最多显示五行
|
||||
> 函数:Alert(playerId, text, seconds, xratio, yratio, priority)
|
||||
> 参数:
|
||||
> playerId: 玩家的playerId
|
||||
> text: 需要提示的内容
|
||||
> seconds: 内容显示停留的秒数,不传则用mod.json的default_show_time设置
|
||||
> xratio: 提示背景框中心与主屏横轴的位置比,0-1之间的小数,不传则用mod.json的default_xratio设置
|
||||
> yratio: 提示背景框中心与主屏纵轴的位置比,0-1之间的小数,不传则用mod.json的default_yratio设置
|
||||
> priority: 消息显示优先级,数值越大优先级越大。优先级大于0时消息按照优先级排序,顺序显示;优先级小于0时,覆盖显示当前消息,并清空之前保存的消息。默认优先级是-1
|
||||
> 示例:
|
||||
> import server.extraServerApi as serverApi
|
||||
> alertSystem = serverApi.GetSystem("neteaseAlert", "neteaseAlertDev")
|
||||
> alertSystem.Alert(playerId, '§c摊位方块只能放置于规定的摆摊区域。', 2, 0.5, 0.8, 50) # 提示框中点位于(横屏水平方向大小*0.5, 横屏竖直方向大小*0.8)处
|
||||
|
||||
|
||||
|
||||
### 代码下载
|
||||
|
||||
教程中仅展示了部分代码,全部代码可以在这里下载。
|
||||
|
||||
[彩票插件——lobby/game部分](https://g79.gdl.netease.com/pluginguide04-05.zip)
|
||||
|
||||
随后我们就可以部署并测试啦!
|
||||
|
||||
105
mconline/30-网络服插件教程/4-插件制作/7-插件编写——测试篇.md
Normal file
@@ -0,0 +1,105 @@
|
||||
# 插件编写——测试篇
|
||||
|
||||
编写完插件,就可以部署并测试了。
|
||||
|
||||
利用[插件编写——制作篇(中)](./5-插件编写——制作篇(中).html#代码下载)和[插件编写——制作篇(下)](./6-插件编写——制作篇(下).html#代码下载)下载的插件代码,可跟着教程一同部署并测试。
|
||||
|
||||
|
||||
|
||||
## 部署过程
|
||||
|
||||
1. 执行mod.sql
|
||||
2. 配置项中勾选相关插件
|
||||
3. 执行部署
|
||||
4. 启动游戏,查看效果
|
||||
|
||||
部署过程在之前已经讲过,忘记了的可以[点我](../2-初识插件/2-应用插件.html)进行回顾。
|
||||
|
||||
在勾选相关插件的步骤里,因为我们使用到了多个官方插件,所以勾选时请注意将它们全部勾选上。
|
||||
|
||||
下方列出了不同类型的服务器需要勾选的插件。
|
||||
|
||||
### Game/Lobby服
|
||||
|
||||
- neteaseAlert
|
||||
- neteaseAnnounce
|
||||
- soldierLottery
|
||||
|
||||
### Master服
|
||||
|
||||
- neteaseAnnounceMaster
|
||||
|
||||
### Service服
|
||||
|
||||
- neteaseAnnounceService
|
||||
- soldierLotteryService
|
||||
|
||||
> 需要注意的是,由于示例插件中,请求到的service服务器类型固定是"service",所以在配置Service服务器时,在类型处请填写"service"。
|
||||
>
|
||||
> 如果需要修改,可以在consts.py中修改ServiceServerType。
|
||||
|
||||
设置完成后,点击部署即可进行部署。
|
||||
|
||||
随后便可以进入游戏测试效果。
|
||||
|
||||
### 效果测试
|
||||
|
||||
聊天框输入cp1 领取号码
|
||||
|
||||

|
||||
|
||||
聊天框输入cp2 查看已领取的号码
|
||||
|
||||

|
||||
|
||||
## 调试工具与功能展示
|
||||
|
||||
### 服务器日志
|
||||
|
||||
在开发者工具中,对服务器右键,可以看到查看服务器日志选项。
|
||||
|
||||
点击即可查看所有服务器的日志。
|
||||
|
||||
在服务器脚本开发中,使用```print "msg"```语句,即可将日志信息打印到其中。
|
||||
|
||||

|
||||
|
||||
### 脚本日志
|
||||
|
||||
脚本日志即客户端日志。在客户端启动后会随之出现。界面如图。
|
||||
|
||||
在客户端脚本开发中,使用```print "msg"```语句,即可将日志信息打印到其中。
|
||||
|
||||

|
||||
|
||||
如果关闭后需要重新打开,可以对服务器右键,点击“查看脚本测试日志”重新打开。
|
||||
|
||||
### 控制台调试
|
||||
|
||||
控制台调试中分为脚本,原生指令,POST指令三个标签。
|
||||
|
||||
#### 脚本
|
||||
|
||||
在指定服务器执行python脚本。
|
||||
|
||||
例如在本示例插件中,在Service服务器中执行下方代码,即可强制抽奖进行开奖。
|
||||
|
||||
```python
|
||||
import mod.server.extraServiceApi as serviceApi
|
||||
system = serviceApi.GetSystem("soldierLottery","soldierLotteryService")
|
||||
system.FinishLottery()
|
||||
```
|
||||
|
||||

|
||||
|
||||
#### 原生指令
|
||||
|
||||
原生指令即Minecraft自带的指令,可以执行op,gamemode等类似指令。
|
||||
|
||||

|
||||
|
||||
#### Post指令
|
||||
|
||||
Post指令即Master服务器提供的HTTP指令。可以在面板中选择或输入你需要执行的指令,例如查看在线玩家列表。
|
||||
|
||||

|
||||
67
mconline/30-网络服插件教程/4-插件制作/8-插件二次开发.md
Normal file
@@ -0,0 +1,67 @@
|
||||
# 插件二次开发
|
||||
|
||||
在本节,我们将会修改之前写好的插件,对其进行二次开发。
|
||||
|
||||
## 需求
|
||||
|
||||
使用之前的抽奖插件,改造一个挖宝插件。
|
||||
|
||||
> 前一天20:01~当天20:00是挖宝时间。玩家破坏泥土方块有10%概率获得1个号码,每日每名玩家最多获得3个号码。每日20:00开奖,通过邮件(官方neteaseAnnounce插件中包含)告知中奖玩家并发放奖励。
|
||||
|
||||
- 废弃原插件输入cp1领取号码的功能
|
||||
- 玩家可在game服破坏泥土方块,有10%概率获得号码,若成功获得,用官方neteaseAlert插件提示“本次领取的号码是%s”
|
||||
- 奖励号码每日开奖后重置,玩家可重新通过破坏泥土方块获得号码。
|
||||
- 若超过当日可领取的上限:3,不能再通过挖掘泥土方块获得号码。
|
||||
- 每日开奖时间之前(上一天的20:01~当天20:00),输入cp2可查询已领取的号码。
|
||||
- 官方neteaseAlert插件提示已领取的号码情况“今日已领取%s(数量)个号码:%s、%s、%s”
|
||||
- 若玩家获得号码6666,可在发奖时领到特别大奖。
|
||||
- 从1~10000中随机抽取5个号码作为本期中奖号码。(抽取的号码不含6666)
|
||||
- 获得特别大奖者的邮件提示:恭喜你获得本期特别大奖!邮件附件内容:绿宝石*100
|
||||
- 本期没有玩家获得特别大奖——本期中奖号码:%s、%s、%s、%s、%s。恭喜你,在本期抽奖活动中中奖,请再接再厉。邮件附件内容:黑曜石*5
|
||||
- 本期有玩家获得特别大奖——本期特别大奖由%s获得。另外本期中奖号码:%s、%s、%s、%s、%s。恭喜你,在本期抽奖活动中中奖,请再接再厉。邮件附件内容:黑曜石*5
|
||||
- 本期没有玩家获得特别大奖——本期中奖号码:%s、%s、%s、%s、%s。很遗憾,在本期抽奖活动中您未能中奖,请继续努力。邮件附件内容:铁锭*3
|
||||
- 本期有玩家获得特别大奖——本期特别大奖由%s获得。另外本期中奖号码:%s、%s、%s、%s、%s。很遗憾,在本期抽奖活动中您未能中奖,请继续努力。邮件附件内容:铁锭*3
|
||||
- 当天没有领取任何号码的玩家无需发放邮件。
|
||||
|
||||
## 制作
|
||||
|
||||
### 修改
|
||||
|
||||
根据需求,我们首先需要删除cp1的相关监听。
|
||||
|
||||
然后监听破坏泥土方块事件,完成获取号码的相关逻辑。
|
||||
|
||||
并修改开奖的逻辑,判断是否有玩家获得超级大奖。
|
||||
|
||||
### 注意事项
|
||||
|
||||
由于我们需要同时筛选数据表中的lottery和number列,来查找是否有玩家获取了超级大奖。
|
||||
|
||||
之前的索引并未覆盖到这两列,所以我们需要更新一下mod.sql,增加一个索引。
|
||||
|
||||
SQL语句如下
|
||||
|
||||
```sql
|
||||
# 增加索引
|
||||
ALTER TABLE `soldierLotteryPlayer`
|
||||
ADD UNIQUE INDEX `lottery_number`(`lottery`, `number`) USING BTREE COMMENT '彩票id和彩票号码的索引';
|
||||
```
|
||||
|
||||
### 下载
|
||||
|
||||
修改了相关代码后的插件下载地址:
|
||||
|
||||
[二次开发的彩票插件](https://g79.gdl.netease.com/pluginguide04-08.zip)(含service与lobby/game部分)。
|
||||
|
||||
|
||||
|
||||
## 测试
|
||||
|
||||
在game服中挖掘泥土,可以看到有概率成功领取到号码。
|
||||
|
||||

|
||||
|
||||
测试开奖,与预期一致
|
||||
|
||||

|
||||
|
||||
BIN
mconline/30-网络服插件教程/4-插件制作/images/1.png
Normal file
|
After Width: | Height: | Size: 20 KiB |
BIN
mconline/30-网络服插件教程/4-插件制作/images/code-1.png
Normal file
|
After Width: | Height: | Size: 30 KiB |
BIN
mconline/30-网络服插件教程/4-插件制作/images/code-10.png
Normal file
|
After Width: | Height: | Size: 930 KiB |
BIN
mconline/30-网络服插件教程/4-插件制作/images/code-11.png
Normal file
|
After Width: | Height: | Size: 93 KiB |
BIN
mconline/30-网络服插件教程/4-插件制作/images/code-12.png
Normal file
|
After Width: | Height: | Size: 92 KiB |
BIN
mconline/30-网络服插件教程/4-插件制作/images/code-13.png
Normal file
|
After Width: | Height: | Size: 23 KiB |
BIN
mconline/30-网络服插件教程/4-插件制作/images/code-14.png
Normal file
|
After Width: | Height: | Size: 96 KiB |
BIN
mconline/30-网络服插件教程/4-插件制作/images/code-15.png
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
mconline/30-网络服插件教程/4-插件制作/images/code-2.png
Normal file
|
After Width: | Height: | Size: 50 KiB |
BIN
mconline/30-网络服插件教程/4-插件制作/images/code-3.png
Normal file
|
After Width: | Height: | Size: 5.0 KiB |
BIN
mconline/30-网络服插件教程/4-插件制作/images/code-4.png
Normal file
|
After Width: | Height: | Size: 54 KiB |
BIN
mconline/30-网络服插件教程/4-插件制作/images/code-5.png
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
mconline/30-网络服插件教程/4-插件制作/images/code-6.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
mconline/30-网络服插件教程/4-插件制作/images/code-7.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
mconline/30-网络服插件教程/4-插件制作/images/code-8.png
Normal file
|
After Width: | Height: | Size: 4.1 KiB |
BIN
mconline/30-网络服插件教程/4-插件制作/images/code-9.png
Normal file
|
After Width: | Height: | Size: 915 KiB |
BIN
mconline/30-网络服插件教程/4-插件制作/images/img01.png
Normal file
|
After Width: | Height: | Size: 117 KiB |
BIN
mconline/30-网络服插件教程/4-插件制作/images/mod-1.png
Normal file
|
After Width: | Height: | Size: 226 KiB |
BIN
mconline/30-网络服插件教程/4-插件制作/images/mod-2.png
Normal file
|
After Width: | Height: | Size: 147 KiB |
BIN
mconline/30-网络服插件教程/4-插件制作/images/xmind.png
Normal file
|
After Width: | Height: | Size: 102 KiB |
54
mconline/30-网络服插件教程/4-插件制作/设计文档示例.md
Normal file
@@ -0,0 +1,54 @@
|
||||
## 示例文档:随身仓库插件
|
||||
|
||||
### 1、功能描述
|
||||
|
||||
1)点击界面入口可打开随身仓库。(作为本需求的示例,入口可通过官方的主菜单插件打开即可)
|
||||
2)仓库的列数固定为7,行数可动态适配,最多32行,超出显示的部分通过翻页或滚动实现。
|
||||
3)随身仓库的界面示意,可参考下图。
|
||||
a、配置为初始行的仓库格子,状态是已解锁。其余格子是上锁状态。
|
||||
b、点击任意上锁状态的格子,弹出二次确认弹窗,弹窗内容通过本行解锁提示配置获取。
|
||||
c、上述弹窗选择确认,但物品、货币不足的情况下,用官方插件neteaseAlert进行弹窗提示“解锁所需物品或货币不足!”
|
||||
d、上述弹窗选择确认且物品、货币足够的情况下,扣除响应物品、货币并开启下一行格子。用官方插件neteaseAlert进行弹窗
|
||||
提示“成功解锁第%s行仓库格子”
|
||||
|
||||
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
### 2、配置说明
|
||||
|
||||
1)随身仓库初始行数,最小值是0。
|
||||
2)随身仓库最大行数。
|
||||
3)非初始行的解锁消耗:支持物品+货币两种形式,其中货币需要将官方的经济插件作为前置。若解锁配置设为 -1,则由开发者根据自己写的判断进行解锁。
|
||||
配置说明举例:
|
||||
初始行数 0
|
||||
最大行数 10
|
||||
解锁配置 [行数区间]:(货币dough_id,货币数量,[(物品1identifier:aux,物品数量1),(物品2identifier:aux,物品数量2),...],"本行解锁提示")
|
||||
|
||||
### 3、API需求
|
||||
|
||||
1)服务端API:某uid玩家打开随身仓库。
|
||||
2)服务端API:设置某uid玩家随身仓库解锁多少行。(假如当前已解锁3行,调用这个API,解锁行数设为3,则第4~6行进行解锁)
|
||||
3)服务端API:查询某uid玩家随身仓库已解锁的行数。
|
||||
4)服务端API:删除仓库中某一格的物品。
|
||||
5)服务端API:删除仓库中所有的物品。
|
||||
|
||||
|
||||
|
||||
### 4、事件需求
|
||||
|
||||
1)服务端事件:点击任意上锁状态格子时抛出,参数包含:玩家uid、
|
||||
2)服务端事件:成功解锁时抛出,参数包含:玩家uid、当前解锁的行数集合
|
||||
|
||||
|
||||
|
||||
### 5、运营指令
|
||||
|
||||
1)某uid玩家打开随身仓库。
|
||||
2)设置某uid玩家随身仓库解锁多少行。
|
||||
3)查询某uid玩家随身仓库已解锁的行数。
|
||||
4)设置某uid玩家随身仓库上锁多少行。原来已解锁格子中的物品,设为上锁状态后将无法放入但可以取出,直到重新解锁。
|
||||
5)删除仓库中某一格的物品。
|
||||
6)删除仓库中所有的物品。
|
||||