-
Notifications
You must be signed in to change notification settings - Fork 2
Game Framework
游戏框架设计为主管理器调度各子管理器,将各功能拆分至子管理器:
| 游戏子管理器 | 对应接口名称 | 职责简述 |
|---|---|---|
| 游戏管理器 | IGameManager | 调度其他游戏子管理器 |
| 游戏规则管理器 | IGameruleManager | 设置游戏规则,切换游戏模式 |
| 游戏大厅管理器 | IGameLobbyManager | 提供大厅传送、大厅无敌 |
| 游戏物资刷新管理器 | IGameLootManager | 持续执行物资刷新 |
| 游戏进程管理器 | IGameProcessManager | 更新游戏状态、实现特定游戏类型 |
| 出生管理器 | ISpawnManager | 游戏开始时传送玩家 |
| 统计管理器 | IStatsManager | 记录游戏统计数据 |
| 队伍管理器 | ITeamManager | 管理游戏队伍、游戏玩家 |
| 区域管理器 | IZoneManager | 持续更新游戏区域 |
主管理器及子管理器都实现的接口
用指令调用主管理器执行某功能,主管理器应调度所有子管理器也执行相应功能
package xiao.battleroyale.api.game;
public interface IGameSubManager {
// 读取配置
void initGameConfig(ServerLevel serverLevel);
boolean isConfigPrepared();
// 初始化游戏
void initGame(ServerLevel serverLevel);
boolean isReady();
// 开始游戏
boolean startGame(ServerLevel serverLevel);
// 开始游戏后
void onGameTick(int gameTime);
void stopGame(@Nullable ServerLevel serverLevel);
}读取配置 → 初始化游戏 → 开始游戏 → 持续运行/提前结束
- initGameConfig:从配置文件读取游戏配置,该阶段不应有多余操作
- initGame:初始化游戏,注册相关事件处理器、集中创建玩家队伍、预计算性能密集型事务等
- startGame:若成功开始游戏,则当前
游戏时间为0,下一刻游戏时间为1并调度子管理器 - onGameTick:每
游戏时间调度子管理器,更新游戏状态 - stopGame:强制停止游戏,不进行结算
“一键开始游戏”:执行startGame时,若有子管理器未通过isReady则应自动执行一次initGame;执行initGame时,若有子管理器未通过isConfigPrepared则应自动执行一次initGameConfig
在
initGame和startGame刚执行时立即进行一次检查
连续初始化:主管理器在initGame成功后,会使自身通过isReady但不通过isConfigPrepared,使下次initGame时重新执行initGameConfig,以确保配置时效性
游戏人数检查:主管理器执行initGame后,队伍管理器(因玩家人数不够)未通过isReady应立即提示“人数不足”;若不希望在initGame时提示“人数不足”,可以将人数检查推迟至startGame以避免
模组默认的主管理器不检查队伍管理器的
isReady,使得会提示“人数不足”但不影响startGame开头的检查
package xiao.battleroyale.api.game;
public interface IGameManager extends IGameMainManager, IGameApiGetter, IGameConfigGetter,
IGameConfigSetter, IGameStatusSetter, IGameEventReceiver {
}- 主管理器调度各子管理器
- 提供其下所有子管理器的获取接口
package xiao.battleroyale.api.game;
public interface IGameMainManager extends IGameSubManager, IGameFunc, IGameInfoGetter {
@NotNull IGameProcessManager getGameProcessManager();
@NotNull IGameruleManager getGameruleManager();
@NotNull IGameLootManager getGameLootManager();
@NotNull ISpawnManager getSpawnManager();
@NotNull IGameLobbyManager getGameLobbyManager();
@NotNull IStatsManager getStatsManager();
@NotNull ITeamManager getTeamManager();
@NotNull IZoneManager getZoneManager();
}- spectateGame:由
游戏进程管理器接管,返回玩家是否能观战游戏,并使玩家观战游戏 - finishGame:区别于
stopGame,仅允许游戏进程管理器调用该方法,并在符合条件时立即结束游戏并进行胜利结算
package xiao.battleroyale.api.game;
public interface IGameFunc {
boolean spectateGame(ServerPlayer player);
void finishGame(boolean hasWinner);
}- getGameTime:获取当前
游戏时间,单位为tick - getGameId:获取当前
游戏ID - getGlobalCenterOffset:获取
全局偏移 - getWinnerTeamTotal:获取最大胜利队伍数
- getServerLevel:获取当前游戏维度
package xiao.battleroyale.api.game;
public interface IGameInfoGetter {
int getGameTime();
UUID getGameId();
Vec3 getGlobalCenterOffset();
int getWinnerTeamTotal();
ServerLevel getServerLevel();
}游戏管理器为每局游戏创建一个UUID作为游戏ID,同时还用于:
-
游戏物资刷新器刷新物品/生成实体/更新方块实体时写入游戏ID(持久化保存)以辨别是否已刷新过/需要清理 - 让其他模组刷新物品/生成实体后手动写入游戏ID进行标记,以防止被
游戏物资刷新器自动清理
package xiao.battleroyale.api.game;
public interface IGameApiGetter {
IGameIdReadApi getGameIdReadApi();
IGameIdWriteApi getGameIdWriteApi();
}package xiao.battleroyale.api.game;
public interface IGameIdReadApi {
@Nullable UUID getGameId(Entity entity);
@Nullable UUID getGameId(BlockEntity blockEntity);
@Nullable UUID getGameId(ItemStack itemStack);
}package xiao.battleroyale.api.game;
public interface IGameIdWriteApi {
void addGameId(ItemStack itemStack, UUID gameId);
void addGameId(Entity entity, UUID gameId);
void addGameId(BlockEntity blockEntity, UUID gameId);
}本模组支持任意数量的配置预设,而每局游戏只能选中其一
package xiao.battleroyale.api.game;
public interface IGameConfigGetter {
int getGameruleConfigId(); // 查看选用的游戏规则配置ID
int getSpawnConfigId(); // 查看选用的出生配置ID
// ...其余配置
}手动设置配置ID后需要再次initGameConfig以重新读取
package xiao.battleroyale.api.game;
public interface IGameConfigSetter {
boolean setGameruleConfigId(int gameId);
boolean setSpawnConfigId(int id);
// ...其余配置
}- 游戏步长用于跳过
游戏时间,特别是用于快速测试区域配置,而不应用于加速游戏 - 游戏管理器缓存一个
全局偏移,使区域管理器和出生管理器能方便地切换地图并调整出生地点 - 每局游戏在同一维度(ServerLevel)下更新游戏状态,玩家离开维度则无法获取,且无法接收游戏消息(服务端→客户端通信)
package xiao.battleroyale.api.game;
public interface IGameStatusSetter {
boolean setGameStep(int step);
boolean setGlobalCenterOffset(Vec3 offset);
void setDefaultLevel(String defaultLevelKey);
}将游戏事件委派给游戏进程管理器处理,更改部分规则即可转化为其他游戏类型
该接口已经由模组事件处理器调用,游戏管理器应只在
游戏进程管理器接管前后发送相应事件 更改事件处理应通过替换游戏进程管理器实现
package xiao.battleroyale.api.game;
public interface IGameEventReceiver {
@ApiStatus.Internal void onPlayerLoggedIn(ServerPlayer player);
@ApiStatus.Internal void onPlayerLoggedOut(ServerPlayer player);
@ApiStatus.Internal void onPlayerDown(ILivingDeathEvent event, @NotNull GamePlayer gamePlayer, @NotNull LivingEntity livingEntity);
@ApiStatus.Internal void onPlayerRevived(@NotNull GamePlayer gamePlayer);
@ApiStatus.Internal void onPlayerDeath(@Nullable ILivingDeathEvent event, @NotNull GamePlayer gamePlayer);
}- initGameConfig:读取游戏规则配置,包含MC原版规则(/gamerule)、大逃杀规则、其他规则设置
- initGame:应用部分游戏规则
- startGame:应用其余游戏规则,如设置玩家游戏模式
如果需要替换
游戏规则管理器并不需要所有玩家统一的游戏模式,可忽略
package xiao.battleroyale.api.game.gamerule;
public interface IGameruleManager extends IGameSubManager {
GameType getGameMode();
}- initGameConfig:读取配置,使传送至大厅生效
- initGame:根据配置决定是否将玩家传送至大厅,
游戏管理器应在之前调度队伍管理器以保证获取到刚创建的游戏玩家
package xiao.battleroyale.api.game.lobby;
public interface IGameLobbyManager extends IGameSubManager, IGameLobbyReadApi, ILobbyFuncApi {
}- healPlayer:恢复游戏玩家状态
- teleportToLobby:传送回大厅
package xiao.battleroyale.api.utilitity;
public interface ILobbyFuncApi {
void healPlayer(@NotNull LivingEntity livingEntity);
boolean teleportToLobby(@NotNull LivingEntity livingEntity);
}- lobbyLevelKey:大厅所在维度的ResourceKey
- canMuteki:当前位置及状态是否可以受大厅无敌保护
package xiao.battleroyale.api.game.lobby;
public interface IGameLobbyReadApi extends ILobbyReadApi {
void sendLobbyTeleportMessage(@NotNull ServerPlayer player, boolean isWinner);
}package xiao.battleroyale.api.utilitity;
public interface ILobbyReadApi {
ResourceKey<Level> lobbyLevelKey();
Vec3 lobbyPos();
Vec3 lobbyDimension();
boolean isInLobbyRange(Vec3 pos);
boolean canMuteki(@NotNull LivingEntity livingEntity);
}package xiao.battleroyale.api.game.loot;
public interface IGameLootManager extends IGameSubManager, IGameLootConfigGetter, IGameLootStatus, IGameLootTester, IGameLootOperator {
}- 本模组默认物资刷新器使用异步BFS+单tick限制刷新区块数(均摊计算量)以提高性能
- 以各玩家位置为BFS初始队列逐层遍历,使刷新尽可能公平
- 需要在服务器关闭时安全处理额外线程
package xiao.battleroyale.api.game.loot;
public interface IGameLootConfigGetter {
int getMaxLootChunkPerTick();
int getMaxLootDistance();
int getTolerantCenterDistance();
int getMaxCachedCenter();
int getMaxQueuedChunk();
int getBfsFrequency();
boolean isInstantNextBfs();
int getMaxCachedLootChunk();
int getCleanCachedChunk();
int getSimulationDistance();
}package xiao.battleroyale.api.game.loot;
public interface IGameLootOperator {
void awaitTerminationOnShutdown();
}- checkIfGameShouldEnd:完整检查游戏状态,如符合条件则直接调用
游戏管理器游戏主管理器功能结束游戏并进行胜利结算
package xiao.battleroyale.api.game.process;
public interface IGameProcessManager extends IGameSubManager, IGameManagement, IGameNotification, IGameEventHandler {
void checkIfGameShouldEnd();
}- startGame:执行一次checkAndUpdateInvalidGamePlayer,供
游戏时间为1时使用,并清除无效玩家
- checkAndUpdateInvalidGamePlayer:检查所有玩家,更新不在线时长及最后有效位置,处理无效玩家
- teleportToLobbyInGame:玩家手动传送至大厅,通常应视为离开游戏而直接淘汰
- teleportAfterGame:游戏结束后对胜利玩家与其余玩家进行一次传送
- spectateGame:只能由
游戏管理器调用,返回玩家是否能观战游戏,并使玩家观战游戏 - healGamePlayers:开始游戏时由
游戏管理器调用,恢复所有游戏玩家状态 - finishGameAddWinner:结算胜利玩家,调用
游戏管理器添加胜利玩家及胜利队伍
package xiao.battleroyale.api.game.process;
public interface IGameManagement {
void checkAndUpdateInvalidGamePlayer(ServerLevel serverLevel);
void teleportToLobbyInGame(ServerPlayer player);
void teleportAfterGame(@Nullable ServerLevel serverLevel, Set<GamePlayer> winnerGamePlayers, Set<GameTeam> winnerGameTeams,
boolean teleportWinnerAfterGame, boolean teleportAfterGame);
boolean spectateGame(ServerPlayer player);
void healGamePlayers(@NotNull ServerLevel serverLevel, List<GamePlayer> gamePlayers);
void finishGameAddWinner(boolean hasWinner);
}由游戏管理器派发的事件,如大逃杀游戏在玩家死亡时即淘汰该玩家对应的游戏玩家
该接口只能由
游戏管理器调用 如有特殊机制如大逃杀游戏可能将倒地事件判定应为淘汰事件,则应调用游戏管理器并由其发送相关事件
package xiao.battleroyale.api.game.process;
public interface IGameEventHandler {
void onPlayerLoggedIn(@NotNull ServerLevel serverLevel, ServerPlayer player, boolean onlyGamePlayerSpectate);
void onPlayerLoggedOut(boolean isInGame, ServerPlayer player);
void onPlayerDown(ILivingDeathEvent event, @NotNull GamePlayer gamePlayer, LivingEntity livingEntity, boolean removeInvalidTeam);
void onPlayerDeath(@Nullable ILivingDeathEvent event, @Nullable ServerLevel serverLevel, @NotNull GamePlayer gamePlayer);
void onPlayerRevived(@NotNull GamePlayer gamePlayer);
}- sendWinnerResult:在聊天栏发送胜利结算信息
- notifyWinner:向胜利玩家发送消息
- sendGameSpectateMessage:提供观战游戏指令快捷方式
- sendDownMessage:游戏玩家倒地时聊天栏消息
- sendReviveMessage:游戏玩家复活时聊天栏消息
- sendEliminateMessage:游戏玩家被淘汰时聊天栏消息
package xiao.battleroyale.api.game.process;
public interface IGameNotification {
void sendWinnerResult(@Nullable ServerLevel serverLevel, Set<GamePlayer> winnerGamePlayers, Set<GameTeam> winnerGameTeams, int gameTime);
void notifyWinner(@Nullable ServerLevel serverLevel, @NotNull GamePlayer gamePlayer, int winnerParticleId);
void sendGameSpectateMessage(@NotNull ServerPlayer player, boolean allowSpectate);
void sendDownMessage(@Nullable ServerLevel serverLevel, @NotNull GamePlayer gamePlayer);
void sendReviveMessage(@Nullable ServerLevel serverLevel, @NotNull GamePlayer gamePlayer);
void sendEliminateMessage(@Nullable ServerLevel serverLevel, @NotNull GamePlayer gamePlayer);
}- initGameConfig:读取游戏出生器配置
- initGame:初始化游戏出生器
getGameSpawner()应仅用于调试目的
package xiao.battleroyale.api.game.spawn;
public interface ISpawnManager extends IGameSubManager {
@Deprecated(forRemoval = false) IGameSpawner getGameSpawner();
}- initGameConfig:读取游戏统计数据配置
- initGame:清除旧统计数据
- startGame:开始记录统计数据
若开始游戏失败,正常情况下应会再次执行
initGame以重置统计数据
package xiao.battleroyale.api.game.stats;
public interface IStatsManager extends IGameSubManager, IGameEventStatsRecorder, IStatsQuery,
IZoneStatsRecorder, IGameruleStatsRecorder, ISpawnStatsRecorder {
String getStatsFilePath();
void saveStats(String filePath);
}package xiao.battleroyale.api.game.stats;
public interface IGameEventStatsRecorder {
void onRecordDamage(GamePlayer gamePlayer, DamageSource damageSource, float damage);
void onRecordDamage(GamePlayer gamePlayer, ILivingDamageEvent livingDamageEvent);
void onRecordInstantRevive(GamePlayer gamePlayer, ILivingDeathEvent event);
void onRecordDown(GamePlayer gamePlayer, ILivingDeathEvent event);
void onRecordKill(GamePlayer gamePlayer, ILivingDeathEvent event);
}package xiao.battleroyale.api.game.stats;
public interface IZoneStatsRecorder {
}package xiao.battleroyale.api.game.stats;
public interface IGameruleStatsRecorder {
}package xiao.battleroyale.api.game.stats;
public interface ISpawnStatsRecorder {
}package xiao.battleroyale.api.game.stats;
public interface IStatsQuery {
void getGamePlayerStats(int playerId);
void getGamePlayerStats(UUID playerUUID);
void getGamePlayerStats(String playerName);
void getGameTeamStats(int teamId);
void getGameruleStats(String gameruleName);
}- shouldAutoJoin:是否自动执行加入新队
- hasEnoughPlayerTeamToStart:当前游戏队伍数量是否能开始游戏
package xiao.battleroyale.api.game.team;
public interface ITeamManager extends IGameSubManager, IGameTeamReadApi, ITeamExternal, ITeamManagement, ITeamPreManagement, ITeamNotification, IVanillaTeam {
boolean shouldAutoJoin();
boolean hasEnoughPlayerTeamToStart();
}- initGameConfig:读取配置,清除无效游戏玩家
- initGame:可创建
游戏玩家,可提示游戏玩家/队伍数量是否足够 - startGame:为所有
游戏玩家创建未被淘汰的游戏玩家列表
游戏玩家应使用GamePlayer,游戏队伍应使用GameTeam
- getGamePlayerByUUID:通过UUID查询
游戏玩家 - getGamePlayerBySingleId:通过
队伍管理器管理的唯一游戏玩家ID查询游戏玩家,不小于1 - hasStandingGamePlayer:通过UUID查询
未被淘汰的游戏玩家 - getGameTeams:获取
游戏队伍列表 - getGameTeamById:通过
队伍管理器管理的唯一队伍玩家ID查询游戏队伍,不小于1 - getGamePlayers:获取
游戏玩家列表 - getStandingGamePlayers:获取
未被淘汰的游戏玩家列表 - getTotalMembers:获取所有游戏玩家总数
- getStandingPlayerTeamCount:统计未被淘汰的含非人机游戏玩家的游戏队伍总数
- getStandingTeamCount:统计
未被淘汰的游戏队伍总数
package xiao.battleroyale.api.game.team;
public interface IGameTeamReadApi {
@Nullable GamePlayer getGamePlayerByUUID(UUID uuid);
@Nullable GamePlayer getGamePlayerBySingleId(int playerId);
boolean hasStandingGamePlayer(UUID uuid);
List<GameTeam> getGameTeams();
@Nullable GameTeam getGameTeamById(int teamId);
List<GamePlayer> getGamePlayers();
List<GamePlayer> getStandingGamePlayers();
int getTotalMembers();
int getStandingPlayerTeamCount();
int getStandingTeamCount();
}- joinTeam:加入队伍
- joinTeamSpecific:加入指定队伍
- kickPlayer:将玩家踢出队伍
- invitePlayer:邀请玩家加入队伍
- requestPlayer:申请加入其他玩家队伍
- leaveTeam:离开队伍
游戏中离开队伍通常视为被淘汰,但不应从
游戏玩家列表清除GamePlayer
package xiao.battleroyale.api.game.team;
public interface ITeamExternal {
void joinTeam(ServerPlayer player);
void joinTeamSpecific(ServerPlayer player, int teamId);
void kickPlayer(ServerPlayer sender, ServerPlayer targetPlayer);
void invitePlayer(ServerPlayer sender, ServerPlayer targetPlayer);
void acceptInvite(ServerPlayer player, ServerPlayer senderPlayer);
void declineInvite(ServerPlayer player, ServerPlayer senderPlayer);
void requestPlayer(ServerPlayer sender, ServerPlayer targetPlayer);
void acceptRequest(ServerPlayer teamLeader, ServerPlayer senderPlayer);
void declineRequest(ServerPlayer teamLeader, ServerPlayer senderPlayer);
boolean leaveTeam(ServerPlayer player);
}(可选)按照游戏队伍构建Minecraft原版队伍,但原版队伍的变更不应影响游戏队伍
package xiao.battleroyale.api.game.team;
public interface IVanillaTeam {
void buildVanillaTeam(@Nullable ServerLevel serverLevel, boolean hideName);
void clearVanillaTeam(@Nullable ServerLevel serverLevel);
}- setStackZoneConfig:加载配置时叠加区域配置,用于快速测试两个区域配置合并后的效果
- randomizeZoneTickOffset:随机偏移区域功能延迟,均摊区域功能计算时间
- getCommonZoneContext:获取游戏区域更新上下文,包含
游戏玩家列表 - getZoneContextInGame:获取游戏中的游戏区域更新上下文,包含
未被淘汰的游戏玩家列表
package xiao.battleroyale.api.game.zone;
public interface IZoneManager extends IGameSubManager, IGameZoneReadApi {
void setStackZoneConfig(boolean turn);
void randomizeZoneTickOffset();
ZoneManager.ZoneContext getCommonZoneContext();
ZoneManager.ZoneContext getZoneContextInGame();
}- initGameConfig:读取区域配置
- startGame:执行
randomizeZoneTickOffset
游戏区域默认实现为GameZone,若需要替换则应基于GameZone的实现方式
- getGameZones:获取
游戏区域列表 - getCurrentGameZones:获取当前
游戏时间更新的游戏区域列表 - getCurrentGameZones:获取指定
游戏时间更新的游戏区域列表 - getGameZone:通过
区域管理器管理的唯一区域ID查询游戏区域,不小于0
默认
区域管理器维护的唯一区域ID对应GameZone的固定区域ID,不建议修改该机制
package xiao.battleroyale.api.game.zone;
public interface IGameZoneReadApi {
List<IGameZone> getGameZones();
List<IGameZone> getCurrentGameZones();
List<IGameZone> getCurrentGameZones(int gameTime);
@Nullable IGameZone getGameZone(int zoneId);
}游戏区域包含3个可排列组合的类型
package xiao.battleroyale.api.game.zone.gamezone;
public interface IGameZone extends ITickableZone, ISpatialZone, IAdditionalZone {
}package xiao.battleroyale.api.game.zone.gamezone;
public interface ITickableZone {
}package xiao.battleroyale.api.game.zone.gamezone;
public interface ISpatialZone {
}package xiao.battleroyale.api.game.zone.gamezone;
public interface IAdditionalZone extends IZoneSpecialClient {
}Game sub manager → related interface name
- Game manager → IGameManager
- Gamerule manager → IGameruleManager
- Game lobby manager → IGameLobbyManager
- Game loot manager → IGameLootManager
- Game process manager → IGameProcessManager
- Spawn manager → ISpawnManager
- Stats manager → IStatsManager
- Team manager → ITeamManager
- Zone manager → IZoneManager
🌐 Language / 语言
-
- Configuration Introduction
- Command Introcuction
- Game type introduction
- About
- Mod development tutorial