0x0. 北斗服务端启动过程
graph LR
A(ServerApplication.main) --> B[ServerManager.run] --> C[Server.init]
ServerApplication.main
读取 application.yml 取得mysql的链接地址和账户密码。通过 url 路径得到数据库名称。组合sql判断数据库是否存在。如果不存在调用sql创建并初始化。
Server.init
- 加载WZ
- 重置登录状态和雇佣商店状态
- 清空失效的现金物品
- 重载倍率卡
- 接受未完成的改名
- 接受转区
- 加载玩家排名
- 加载自动封禁配置
- 主动清理每日零点需要清理的数据
- world初始化后需要加载的
- initLoginServer 初始化登录端口
- loadCommandsExecutor 加载GM执行命令
- 所有频道加载事件脚本
- 修改变量服务端启动
加载WZ
创建了虚拟线程负责同时加载4个WZ对应回调。future.get(); 保证所有wz都加载完毕后才会继续往下走init后续逻辑.
- 技能.SkillFactory::loadAllSkills,
- 商店.CashItemFactory::loadAllCashItems,
- 任务.Quest::loadAllQuests,
- 技能书.SkillbookInformationProvider::loadAllSkillbookInformati
world 的初始化
从 game_config.world 中取出创建世界的最大值。Server.initWorld 负责创建并初始化。从 game_config 中读取 经验倍率.exp_rate, meso_rate, 掉落倍率.drop_rate, BOOS掉落倍率.boss_drop_rate, 任务倍率.quest_rate, 旅行倍率.travel_rate, 钓鱼倍率.fishing_rate, flag 初始化必要的字段。
注册定时器
注册世界后台定时器,需要持续定时执行的任务。
| 变量 | 作用 | 回调 | 重复周期 | 首次延迟 |
|---|---|---|---|---|
| petsSchedule | 宠物饥饿处理 | PetFullnessTask | 1分钟 | 1分钟 |
| srvMessagesSchedule | 处理服务器消息显示状态,注释里说是为 Boss HP 条存在时临时禁用 server message。 | ServerMessageTask | 10秒 | 10秒 |
| mountsSchedule | 坐骑疲劳处理 | MountTirednessTask | 1分钟 | 1分钟 |
| merchantSchedule | 雇佣商店相关定时处理 | HiredMerchantTask | 10分钟 | 10分钟 |
| timedMapObjectsSchedule | 地图中的定时对象处理 | TimedMapObjectTask | 1分钟 | 1分钟 |
| charactersSchedule | 自动保存在线角色到数据库,并保存 HpMpAlert 数据 | CharacterAutosaverTask | 1小时 | 1小时 |
| marriagesSchedule | 遍历 world 下所有 channel,检查并启动教堂/礼堂婚礼预约。 | WeddingReservationTask | 分钟.game_config.wedding_reservation_interval | 分钟.game_config.wedding_reservation_interval |
| mapOwnershipSchedule | 地图归属系统检查 | MapOwnershipTask | 20秒 | 20秒 |
| fishingSchedule | 钓鱼系统检查 | FishingTask | 10秒 | 10秒 |
| partySearchSchedule | 组队搜索信息刷新 | PartySearchTask | 10秒 | 10秒 |
| timeoutSchedule | :扫描在线角色,如果当前时间减去 lastPacket 大于 timeout_duration,就自动断开连接。 | TimeoutTask | 10秒 | 10秒 |
| hpDecSchedule | 地图环境持续掉血处理 | CharacterHpDecreaseTask | game_config.map_damage_overtime_interval | game_config.map_damage_overtime_interval |
| use_family_system | 家族系统 | FamilyDailyResetTask | 1分钟 | 24小时之后就 |
频道的初始化
每个世界能创建多少个频道都是从 game_config.channel_size 中取出频道。频道的类是 Channel。频道的IP和端口从 application.yml.wan-host 中读出,端口默认从 7575 开始。
initServer 频道网络的初始化
initServer(频道端口, 世界ID, 频道ID) 函数接受三个参数。通过 ChannelServer.nettyChannel 来创建TCP网络。new ChannelServerInitializer 这个类继承了 class ChannelServerInitializer extends ServerChannelInitializer 所以当有TCP进入绑定的频道端口时,会自动触发连接到 ChannelServerInitializer.initChannel() 回调中。
Channel.eventSM 暂且不谈,这个是脚本事件的初始化。
Channel.services 暂且不谈,还不知道这个具体内容
最终频道初始化完毕后存储在 World.addChannel().channels.list 中。每个新建的世界最终存储在 Server.worlds.list 中。所有频道信息都存储在 Server.channels 中
客户端启动连接的TCP端口的初始化
initLoginServer() 启动端口的初始化和频道网络的初始化过程一致,只是回调是 LoginServerInitializer 当客户端启动时TCP连接触发 LoginServerInitializer.initChannel 回调
NPC信息加载
世界初始化后 loadPlayerNpcMapStepFromDb() 函数负责从数据库表 playernpcs_field 中加载出所有NPC信息。然后根据 playernpcs_field.world 找到对应的世界调用 world.setPlayerNpcMapData()。不过这个表中字段是空的????
初始化收发包协议
OpcodeConstants.generateOpcodeNames(); 中判断当前版本是否是083,如果是则对其 OpcodeConstants.sendOpcodeNames = SendOpcode; OpcodeConstants.recvOpcodeNames = RecvOpcode;
初始化GM命令
CommandsExecutor.getInstance().loadCommandsExecutor(); 通过从数据库表 command_info 中读取所有GM命令存储在 CommandsExecutor.registeredCommands 和 CommandsExecutor.commandsNameDesc 中
加载事件脚本
EventScriptManager 脚本引擎管理器。ch.reloadEventScriptManager() 逻辑为
- 从路径 scripts\event 目录下读取所有脚本文件,并且在 initializeEventEntry 中加载到系统中
- 把脚本接口, Channel, 脚本名称通过 EventManager 绑定在一起。
- 最后通过 EventEntry 把脚本接口和 EventManager 包装一起存储在 EventScriptManager.events 中
结束
至此服务端初始化完毕。