Skip to content

Commit

Permalink
新增对NATS的支持,新增服务端信息透传
Browse files Browse the repository at this point in the history
  • Loading branch information
TiyaAnlite committed Jun 24, 2024
1 parent 7735dba commit efec548
Show file tree
Hide file tree
Showing 16 changed files with 241 additions and 45 deletions.
58 changes: 43 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ Minecodecraft服务端专用MOD
- 自定义服务器登录欢迎信息,开服时间统计
- 定时服务器轮播消息
- 同原版数据一同存储扩展用户数据,支持用户在线时间,累计挖掘和历史上线时间统计
- 接入分布式消息系统[NATS](https://nats.io/),可供透传服务器运行信息与事件

## 指令

Expand Down Expand Up @@ -81,25 +82,52 @@ Minecodecraft服务端专用MOD

`/minecodecraft save` 手动存档

## NATS

事件主题格式为:`[nats.prefix].[event_type]`,数据格式为JSON,事件类型`event_type`见下文

- 每个类型的事件必含`time`字段,为事件生成的时间,格式为RFC3339,形如`2024-06-24T18:07:16+08:00`,下文表格将不再包含

### 服务器生命周期事件(serverAction)

| 属性 | 值类型 | 说明 |
| ------ | ------ | ----------------------------------------------- |
| action | string | 服务器启动状态,`lunch`为启动中,`stop`为停止中 |

### 玩家事件(playerAction)

| 属性 | 值类型 | 说明 |
| ---------- | ------ | ------------------------------------------------------------ |
| action | string | 玩家事件类型,`join`为玩家加入,`disconnect`为玩家退出,`saving`为每次保存玩家数据时触发 |
| name | string | 玩家显示名称 |
| uuid | string | 玩家UUID |
| ip | string | 玩家的客户端IP |
| onlineTime | int | 玩家的累计在线时间 |
| blockBreak | int | 玩家的累计方块破坏数 |

- 对于`saving`玩家事件,并不意味着玩家此刻离线,服务端会每隔一段时间自动保存一次在线玩家的数据。特别地,因为玩家在触发`disconnect`时会进行保存,因此也会同时触发`saving`事件

## 配置

所有配置项在`config/minecodecraft.json`

`/minecodecraft config [save|reload]` 保存运行时配置至文件/从配置文件重载配置,**需要OP权限**

| 配置项 | 值类型 | 说明 |
| ----------------------------- | ------------- | -------------------------- |
| `gameRule.creeperExplosion` | bool | 允许苦力怕破坏方块 |
| `tpPlayer.interval` | int | 玩家传送等待时间 |
| `tpPlayer.homePos` | Object(x,y,z) | 家的坐标 |
| `serverName` | string | 服务器名称 |
| `lunchTime` | string | 开服时间,格式为yyyy-mm-dd |
| `tips.interval` | int | 全服轮播消息间隔 |
| `tips.tips` | []string | 全服轮播消息 |
| `notice` | []string | 登录欢迎消息 |
| `worldAutoSaveInterval` | int | 自动保存间隔,非`0`时生效 |
| `playerHereGlowingTime` | int | 玩家位置共享高亮时间 |
| `playerWhereRequestExpire` | int | 位置共享请求超时时间 |
| `playerLatencyUpdateInterval` | int | 玩家延迟检测间隔 |
| `copyRight` | bool | 展示MOD信息 |
| 配置项 | 值类型 | 说明 |
| ----------------------------- | ------------- | ------------------------------------------------------- |
| `gameRule.creeperExplosion` | bool | 允许苦力怕破坏方块 |
| `tpPlayer.interval` | int | 玩家传送等待时间,单位为秒 |
| `tpPlayer.homePos` | Object(x,y,z) | 家的坐标 |
| `serverName` | string | 服务器名称 |
| `lunchTime` | string | 开服时间,格式为yyyy-mm-dd |
| `nats.server` | string | 消息队列系统NATS的连接地址,格式为`nats://address:port` |
| `nats.prefix` | string | NATS消息主题前缀,默认为`minecodecraft` |
| `tips.interval` | int | 全服轮播消息间隔 |
| `tips.tips` | []string | 全服轮播消息 |
| `notice` | []string | 登录欢迎消息 |
| `worldAutoSaveInterval` | int | 自动保存间隔,非`0`时生效,单位为秒 |
| `playerHereGlowingTime` | int | 玩家位置共享高亮时间,单位为秒 |
| `playerWhereRequestExpire` | int | 位置共享请求超时时间,单位为秒 |
| `playerLatencyUpdateInterval` | int | 玩家延迟检测间隔,单位为秒 |
| `copyRight` | bool | 展示MOD信息 |

22 changes: 22 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import net.fabricmc.loom.task.RemapJarTask

plugins {
id 'com.github.johnrengelman.shadow' version '7.1.2'
id 'fabric-loom' version '1.7-SNAPSHOT'
id 'maven-publish'
}
Expand Down Expand Up @@ -39,6 +42,8 @@ dependencies {
// Fabric API. This is technically optional, but you probably want it anyway.
modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}"

implementation "io.nats:jnats:${project.nats_version}"

}

processResources {
Expand Down Expand Up @@ -69,6 +74,21 @@ jar {
}
}

shadowJar {
archiveClassifier = 'shaded-dev'

dependencies {
include dependency("io.nats:jnats:${project.nats_version}")
}
}

tasks.register('remapShadowJar', RemapJarTask) {
dependsOn shadowJar
input = shadowJar.archiveFile
archiveFileName = shadowJar.archiveFileName.get().replaceAll('-shaded-dev\\.jar\$', '.jar')
addNestedDependencies = true
}

// configure the maven publication
publishing {
publications {
Expand All @@ -86,3 +106,5 @@ publishing {
// retrieving dependencies.
}
}

assemble.dependsOn(remapShadowJar)
3 changes: 2 additions & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@ loader_version=0.15.11
# carpet_core_version=1.18-1.4.56+v211130

# Mod Properties
mod_version = 1.3.4
mod_version = 1.4.0
maven_group = cn.focot.codelab
archives_base_name = MineCodeCraftMod

# Dependencies
fabric_version=0.100.3+1.21
nats_version=2.19.0
4 changes: 2 additions & 2 deletions src/main/java/cn/focot/codelab/minecodecraft/Config.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ public class Config {
String confPath;
ConfigBean configBean;
Logger LOGGER = LoggerFactory.getLogger("MineCodeCraftConfig");
public static Gson gson = new Gson();
public static Gson gson_pretty = new GsonBuilder().setPrettyPrinting().create();
public static final Gson gson = new Gson();
public static final Gson gson_pretty = new GsonBuilder().setPrettyPrinting().create();

Config(@Nullable String path) {
this.confPath = "config/" + Objects.requireNonNullElse(path, "minecodecraft.json");
Expand Down
8 changes: 8 additions & 0 deletions src/main/java/cn/focot/codelab/minecodecraft/ConfigBean.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package cn.focot.codelab.minecodecraft;

import cn.focot.codelab.minecodecraft.event.ServerAction;

import java.util.Date;
import java.text.SimpleDateFormat;
import java.util.LinkedList;
Expand All @@ -10,6 +12,7 @@ public class ConfigBean {
public TpPlayer tpPlayer = new TpPlayer();
public String serverName = "MineCodeCraft";
public String lunchTime = new SimpleDateFormat("yyyy-MM-dd").format(new Date().getTime());
public Nats nats = new Nats();
public Tips tips = new Tips();
public List<String> notice = new LinkedList<>();
public int worldAutoSaveInterval = 0;
Expand Down Expand Up @@ -37,4 +40,9 @@ public class Tips {
public int interval = 20 * 60;
public List<String> tips = new LinkedList<>();
}

public class Nats {
public String server;
public String prefix = "minecodecraft";
}
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,24 @@
package cn.focot.codelab.minecodecraft;

import cn.focot.codelab.minecodecraft.handlers.PlayerHandler;
import cn.focot.codelab.minecodecraft.handlers.ServerHandler;
import io.nats.client.Connection;
import io.nats.client.Nats;
import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback;
import net.fabricmc.api.ModInitializer;
import net.fabricmc.fabric.api.entity.event.v1.ServerLivingEntityEvents;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents;
import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents;
import net.fabricmc.loader.api.FabricLoader;
import net.fabricmc.loader.api.metadata.ModMetadata;
import net.minecraft.server.MinecraftServer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.util.Objects;

public class MineCodeCraftMod implements ModInitializer {
private static final String MOD_ID = "minecodecraft";
private static final Logger LOGGER = LoggerFactory.getLogger("MineCodeCraft");
Expand All @@ -19,6 +27,9 @@ public class MineCodeCraftMod implements ModInitializer {
private static String version;
private static String description;

private static Connection nc = null;
private static String lastNcServer = "";

public static Logger getLogger() {
return LOGGER;
}
Expand Down Expand Up @@ -49,6 +60,31 @@ public void onInitialize() {
ServerPlayConnectionEvents.JOIN.register(PlayerHandler::onPlayerJoin);
ServerPlayConnectionEvents.DISCONNECT.register(PlayerHandler::onPlayerDisconnect);
ServerLivingEntityEvents.ALLOW_DEATH.register(PlayerHandler::onPlayerDeath);
ServerLifecycleEvents.SERVER_STARTING.register(ServerHandler::onServerLoaded);
ServerLifecycleEvents.SERVER_STOPPING.register(ServerHandler::onServerStopping);
ServerLifecycleEvents.SERVER_STOPPED.register(ServerHandler::onServerStopped);
ServerTickEvents.START_WORLD_TICK.register(ServerHandler::onWorldTick);
MineCodeCraftMod.loadNatsConnection();
}

public static void loadNatsConnection() {
if (config.getConfigBean().nats.server != null && !Objects.equals(config.getConfigBean().nats.server, "") && !Objects.equals(lastNcServer, config.getConfigBean().nats.server)) {
String natsServer = config.getConfigBean().nats.server;
if (nc != null) {
try {
nc.close();
nc = null;
} catch (InterruptedException ignored) {
}
}
LOGGER.info("Connect to NATS: {}", natsServer);
try {
nc = Nats.connect(natsServer);
lastNcServer = natsServer;
} catch (IOException | InterruptedException e) {
LOGGER.error("Failed to connect to nats server", e);
}
}
}

public static String getModId() {
Expand All @@ -62,4 +98,12 @@ public static String getVersion() {
public static String getDescription() {
return description;
}
}

public static boolean hasNatsConnection() {
return nc != null;
}

public static Connection getNatsConnection() {
return nc;
}
}
31 changes: 31 additions & 0 deletions src/main/java/cn/focot/codelab/minecodecraft/event/EventMsg.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package cn.focot.codelab.minecodecraft.event;

import cn.focot.codelab.minecodecraft.MineCodeCraftMod;
import com.google.gson.Gson;

import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.StringJoiner;

public abstract class EventMsg {
public static final Gson gson = new Gson();
public static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX"); // RFC3339

protected String msgSubject;
public String time = dateFormat.format(new Date());
public byte[] toBytes() {
return EventMsg.gson.toJson(this).getBytes(StandardCharsets.UTF_8);
}
public void publish() {
if (MineCodeCraftMod.hasNatsConnection()) {
MineCodeCraftMod.getNatsConnection().publish(
new StringJoiner(MineCodeCraftMod.getConfig().getConfigBean().nats.prefix, ".", this.msgSubject).toString(),
this.toBytes());
}
};

protected EventMsg(String msgSubject) {
this.msgSubject = msgSubject;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package cn.focot.codelab.minecodecraft.event;

import cn.focot.codelab.minecodecraft.helpers.PlayerData;
import net.minecraft.server.network.ServerPlayerEntity;

public class PlayerAction extends EventMsg {
public String action; // join, disconnect
public String name;
public String uuid;
public String ip;
public int onlineTime;
public int blockBreak;

protected PlayerAction(String msgSubject) {
super(msgSubject);
}

public static PlayerAction of(ServerPlayerEntity player, PlayerData data, String action) {
PlayerAction e = new PlayerAction("playerAction");
e.action = action;
e.name = player.getName().getString();
e.uuid = player.getUuidAsString();
e.ip = player.getIp();
e.onlineTime = data.getOnlineTime();
e.blockBreak = data.getBlockBreak();
return e;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package cn.focot.codelab.minecodecraft.event;

public class ServerAction extends EventMsg{
public String action; // lunch, stop

protected ServerAction(String msgSubject) {
super(msgSubject);
}

public static ServerAction of(String action) {
ServerAction e = new ServerAction("serverAction");
e.action = action;
return e;
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package cn.focot.codelab.minecodecraft.handlers;

import cn.focot.codelab.minecodecraft.event.PlayerAction;
import cn.focot.codelab.minecodecraft.helpers.PlayerData;
import cn.focot.codelab.minecodecraft.helpers.PlayerHelper;
import cn.focot.codelab.minecodecraft.helpers.StatusHelper;
Expand All @@ -22,6 +23,7 @@ public static void onPlayerJoin(ServerPlayNetworkHandler handler, PacketSender s
playerData.login();
PlayerHelper.joinMOTD(player);
PlayerHelper.sendPlayerNotice(player);
PlayerAction.of(player, playerData, "join").publish();
player.sendMessage(MessageUtil.prefixMessage("Welcome! %s[%s]".formatted(player.getName().getString(), player.getIp())));
if (!StatusHelper.hasPlayerPosHistory(player)) {
StatusHelper.updatePlayerPosHistory(player);
Expand All @@ -32,6 +34,7 @@ public static void onPlayerDisconnect(ServerPlayNetworkHandler handler, Minecraf
ServerPlayerEntity player = handler.player;
PlayerData playerData = PlayerHelper.checkedPlayerData(player);
playerData.logout();
PlayerAction.of(player, playerData, "disconnect").publish();
LOGGER.info("Player disconnect: %s[%s]".formatted(player.getName().getString(), player.getIp()));
}

Expand Down
Loading

0 comments on commit efec548

Please sign in to comment.