Minecraft服务器源码跟踪(1).启动服务器
Minecraft服务器源码跟踪
Minecraft游戏版本为:1.20.4,反混淆使用官方映射表
入口
主入口 net.minecraft.server.Main.main
方法。
在入口中主要做的事情有:
- 处理传入的启动参数(nogui、safemode等)
- 校验以及处理配置文件。
- server.properties(如果没有该文件,生成默认配置)
- eula.txt(判断eula是否通过,不存在就生成默认配置,存在就校验eula=true,失败就退出)
- 读world文件夹,加载世界(没有就建新的世界)
加载数据包,加载维度
加载registry
待确定- 初始化mc服务器本体,类为:
net.minecraft.server.dedicated.DedicatedServer
(MinecraftServer类的实现) - 初始化服务器退出hook线程
DedicatedServer —— MC服务器本体
DecicatedServer继承了net.minecraft.server.MinecraftServer
,是MCServer的具体实现。
初始化阶段
- 加载command处理线程 Server console handler
- 一些校验与日志
- 设置必要的参数(pvp,白名单,允许飞行等配置)
- 初始化KeyPair (猜测是正版验证校验用的)
- 启动监听tcp连接的服务端(通过配置中的ip以及端口)
- (非旧版本迁移新版本不需要进行此步骤)处理旧玩家数据,包括在各种配置文件中以及存档的playerdata文件夹中的player 如图,如果启用了正版验证,则会通过
YggdrasilGameProfileRepository
处理playername(外置登录其实就是通过改这里的Repository来指向非官方玩家账号库的),如果未启用正版验证,则通过UUIDUtil生成离线的profile。所有player处理完后,将profile缓存写入文件中 - 加载玩家相关数据(ban,banip,白名单,模拟距离,视距等)
- 启动SkullEntityEntity(拉头颅相关的数据?不确定)
- 开始加载世界,加载结束后统计耗时,发送
Done ({?.??s})! For help, type "help"
这行日志 - 设置是否公布玩家成就(announcePlayerAchievements)
- 根据配置启动
QueryGs4
和Rcon
(前者不知道是啥东西,没用过) - 根据配置启动
WatchDog
- 根据配置启动
JMXMonitoring
- 结束
loadLevels方法
加载世界入口在: net.minecraft.server.MinecraftServer#loadLevel
主要做了三件事:
- 获取区块处理监听器
chunkProgressListener
- 通过该监听器创建levels
- 通过该监听器准备levels
获取监听器
此处的chunkProgressListener
是在Main创建DedicatedServer时传入的LoggerChunkProgressListener
创建levels
创建levels前,需要有worldData。world的数据已经在启动服务器之前生成,所以此时已经有了worldData,如下为worldData的实现——PrimaryLevelData的字段。
- 从服务器的reigstry中拿到注册表(个人理解为里面存了服务器的所有结构化的数据)
- 根据服务器的种子进行混淆,得到obfuscateSeed(如果输入的种子是字符串,则会取字符串的hashcode,否则直接用数字)
- 创建自定义spawner。当前是固定的,分别为: PhantomSpawner(幻翼), PatrolSpawner(掠夺者小队), CatSpawner(猫), VillageSiege(僵尸围村), WanderingTraderSpawner(游商)
- 创建主世界实例 - ServerLevel,并放到服务器实例的levels中
- 创建命令存储CommandStorage
- 设置寻找出生点以及生成奖励箱逻辑。
net.minecraft.server.MinecraftServer#setInitialSpawn
- 添加世界边界监听器(不知道具体干啥的)
- 加载自定义boss事件(不知道具体干啥的)
- 读取worldData,创建其他维度的世界实例,添加世界边界监听器,放到服务器实例levels中
- 将世界边界设置作用于当前的worldData(猜测是监听存档的数据是否符合世界边界的约束?)
准备Levels(此处会出现tick概念,但是与后文的gt时间不同,此处为10ms)
当前levels已经生成好了,接下来需要让这个实例运行起来!做一些准备工作
关键日志节点: Preparing start region for dimension ….
- 获取到一个BlockPos(方块位置)作为出生点,同时区块处理监听器会根据它更新对应的ChunkPos(区块位置),此区块为出生点区块。
- 先记一下当前的tick。加载以出生点区块为中心,21*21的区块
- 循环判断如果当前已加载的区块不是441(也就是21*21)个,就设置下个tick后,等下个tick再次判断,直到区块全部加载完成
- 出循环后再等1tick
- 遍历所有的维度,找强加载区块列表,如果所有世界都没有,等1tick,设置怪物生成tag。
- 如果有强制加载的区块,遍历这些区块位置,在区块加载系统中更新为强制加载
结束
至此,服务器已经初始化完毕,但是server还没有实际run起来,接下来需要让这个服务器跑起来了!
All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.