在Minecraft 1.20.x版本的forge中进行MOD开发,创建一个基于时间触发的事件
以下是这部分的连续内容。
我想做的事情。
这次我们要创建一个能够检测游戏内时间并触发事件的活动。
具体来说,我们将在早上和晚上向服务器内的所有人发送系统消息。
先决条件的了解
听说Minecraft有两种时间。
-
- gametime : ゲームサーバーが起動してからの時間
- daytime : ゲーム内の1日の時間
在游戏中,一天被表示为0~24000个Tick(稍后会讨论超过24000的情况)。每个Tick大约相当于现实时间的1/20秒(0.05秒)。也就是说,每秒钟游戏时间会前进20个Tick。
请在维基百科上查看详情。
在本文中,我们将以0~12999代表白天,13000~24000代表夜晚。
代码 (Mandarin Chinese:
时间的事件处理程序
@SubscribeEvent
public void onTickStarting(TickEvent.ServerTickEvent event)
为了在服务器端检测到Tick,使用TickEvent.ServerTickEvent。
需要注意的是,这个事件会在每个Tick的开头和结尾触发。
总之,按照现在这样,每分钟实时操作会触发1200次这个处理。
所以,为了避免这个情况,可以编写以下条件来判断Tick是开始还是结束。(虽然我觉得还是有很多啦、、、)
@SubscribeEvent
public void onTickStarting(TickEvent.ServerTickEvent event) {
// 毎Tickの終わりにだけ処理 かつ サーバー側でのみ処理
if (event.phase == TickEvent.Phase.END && event.side == LogicalSide.SERVER) {
// 処理
}
}
判断早晨和晚上
// 処理を一回しかさせないフラグ
private boolean night = false;
@SubscribeEvent
public void onMorningStarting(TickEvent.ServerTickEvent event) {
if (event.phase == TickEvent.Phase.END && event.side == LogicalSide.SERVER) {
MinecraftServer server = event.getServer();
long currentDaytime = server.getWorldData().overworldData().getDayTime() % 24000;
// 朝
if (!night && currentDaytime >= 0 && currentDaytime < 13000) {
LOGGER.debug("Server DayTime: {}", currentDaytime);
}
}
}
@SubscribeEvent
public void onNightStarting(TickEvent.ServerTickEvent event) {
if (event.phase == TickEvent.Phase.END && event.side == LogicalSide.SERVER) {
MinecraftServer server = event.getServer();
long currentDaytime = server.getWorldData().overworldData().getDayTime() % 24000;
// 夜
if (night && currentDaytime >= 13000 && currentDaytime < 24000) {
LOGGER.debug("Server DayTime: {}", currentDaytime);
}
}
}
普通的代碼的確讓那些平時考慮負荷的人看了會發瘋,但這樣的形式似乎是一般情況。
曾經猶豫過在一個事件中兩次進行時間判定,還是將事件分開處理,但為了可讀性,我選擇按事件單位分開處理。
由於此事件每個Tick都會被呼叫,所以只要落在該範圍內,就每次都會運行,為了只運行一次,我使用了night變數來進行標誌管理。
求取24000的余数的原因
最初我没有加入这个处理,但有时候会出现一些不动的情况,所以我每秒都查看 server.getWorldData().overworldData().getDayTime(),发现有时候会超过24000。
虽然我知道0 != 24000,但我还是不太理解。
我觉得这是游戏自身的特性而不是bug,因为这个问题非常重要,希望能在某个地方写清楚。
出于这个原因,我通过求余数的方式来处理超过24000的情况。
给所有已登录玩家发送系统消息。
@SubscribeEvent
public void onMorningStarting(TickEvent.ServerTickEvent event) {
if (event.phase == TickEvent.Phase.END && event.side == LogicalSide.SERVER) {
MinecraftServer server = event.getServer();
List<ServerPlayer> serverPlayers = server.getPlayerList().getPlayers();
long currentDaytime = server.getWorldData().overworldData().getDayTime() % 24000;
// 朝
if (currentDaytime >= 0 && currentDaytime < 13000) {
for (Player player : serverPlayers) {
player.sendSystemMessage(Component.nullToEmpty("朝です"));
}
}
}
}
@SubscribeEvent
public void onNightStarting(TickEvent.ServerTickEvent event) {
if (event.phase == TickEvent.Phase.END && event.side == LogicalSide.SERVER) {
MinecraftServer server = event.getServer();
List<ServerPlayer> serverPlayers = server.getPlayerList().getPlayers();
long currentDaytime = server.getWorldData().overworldData().getDayTime() % 24000;
// 夜
if (currentDaytime >= 13000 && currentDaytime < 24000) {
for (Player player : serverPlayers) {
player.sendSystemMessage(Component.nullToEmpty("夜です"));
}
}
}
}
あとは消化試合で、イベントからサーバー内のプレイヤーを取得してメッセージを送ります。
以上です。
其他
请确保 Forge 版本与 Minecraft 版本至少一直到次要版本匹配。
これを検証しているとき、ちょうどMinecraftが1.20から1.20.1にアップデートされていました。
それに気づかず1.20用のFrogeを使っていたせいで何してもこのTickEvent以下のイベントが発火しませんでした。
必ず合わせましょう。
TickEvent.WorldTickEventは1.20以降では使えない
時間で発火するイベントの実装方法を調べていると以下のようにWorldTickEventを使う旨のFormが出てきます。(というかこれしか出てこない)
正確には1.19からかもしれませんが、メソッド自体無くなっているのであきらめましょう。
消した理由としてはClientTickEventとServerTickEventで対応できるからでしょうか?
请参考
已经在文章中提到了
下一步是实现自定义命令,才算真正完成。