Numdrassl

Numdrassl

Numdrassl 是一款用Java编写的高性能、可扩展的Hytale代理服务器,它提供服务器间传输、插件支持以及外部供应商集成功能。

早期插件

Numdrassl - Hytale 代理服务器

加入 Discord

一款用 Java 编写的高性能、可扩展的 Hytale 代理服务器,支持多服务器组网、玩家跨服传送、插件系统以及灵活的权限体系。


什么是 Numdrassl?

Numdrassl 是 Hytale 的 BungeeCord/Velocity 风格代理。它位于玩家和您的后端服务器之间,使您能够:

  • 将多个 Hytale 服务器连接到一个网络中
  • 在服务器之间无缝传送玩家
  • 拦截并修改数据包
  • 创建带有自定义指令和事件的插件
  • 使用分组和通配符管理权限

功能特性

  • 多服务器组网 - 关联多个后端 Hytale 服务器(如大厅、小游戏等)
  • 玩家传送 - 使用 /server <名称> 在服务器间无缝移动玩家
  • 插件系统 - 通过自定义插件扩展功能
  • 权限系统 - 内置基于 YAML 的权限系统,支持分组、通配符和外部提供者
  • 事件系统 - 可挂钩玩家连接、聊天、指令等事件
  • QUIC 协议 - 原生支持 Hytale 的 QUIC 传输,并采用 BBR 拥塞控制
  • 安全认证 - 代理与后端之间使用 HMAC 签名的引荐机制

系统要求

  • Java 25 或更高版本
  • Hytale 服务器 作为后端使用

安装指南

步骤 1:下载

请下载最新版本:

  • proxy-*.jar - 代理服务器
  • bridge-*.jar - 后端服务器用的插件

步骤 2:运行代理

java -jar proxy-1.0-SNAPSHOT.jar

首次运行时,代理将:

  1. certs/ 目录下生成 TLS 证书
  2. 创建 config/proxy.yml 配置文件
  3. 提示您使用 Hytale 进行身份认证

步骤 3:使用 Hytale 认证

在代理控制台中运行:

auth login

访问显示的网址并输入设备码以关联您的 Hytale 账号。


代理配置

编辑 config/proxy.yml

# 网络设置
bindAddress: "0.0.0.0"
bindPort: 45585

# 公共地址(用于服务器传送)
publicAddress: "play.yourserver.com"
publicPort: 45585

# 共享密钥 - 必须与你的 Bridge 插件配置一致!
proxySecret: "change-this-to-a-secure-secret"

# 后端服务器
backends:
  - name: "lobby"
    host: "127.0.0.1"
    port: 5520
    defaultServer: true    # 玩家优先加入此服务器

  - name: "survival"
    host: "127.0.0.1"
    port: 5521
    defaultServer: false

  - name: "minigames"
    host: "192.168.1.100"
    port: 5520
    defaultServer: false

配置选项

选项 描述
bindAddress 监听 IP(0.0.0.0 表示所有接口)
bindPort 监听端口(默认:45585
publicAddress 用于服务器传送的公共主机名/IP
publicPort 用于服务器传送的公共端口
proxySecret 用于后端认证的共享密钥
backends 后端服务器列表
debugMode 启用详细日志(默认:false

后端服务器设置

每个后端服务器都需要 Bridge 插件 来认证来自代理的连接。

步骤 1:安装 Bridge 插件

bridge-1.0-SNAPSHOT.jar 复制到 Hytale 服务器的 plugins/ 文件夹。

步骤 2:以不安全模式启动服务器

java -jar HytaleServer.jar --auth-mode insecure --transport QUIC

重要: --auth-mode insecure 标志是必需的,因为代理负责处理 Hytale 认证,而非后端。

步骤 3:配置 Bridge

首次运行时,Bridge 会创建 plugins/Bridge/config.json

{
  "proxySecret": "change-this-to-a-secure-secret",
  "serverName": "lobby"
}
选项 描述
proxySecret 必须与代理配置文件中的 proxySecret 一致!
serverName 此服务器的名称(用于日志记录和标识)

步骤 4:保护后端安全(重要!)

屏蔽对后端服务器的直接连接。仅允许代理的 IP 连接:

# 示例:仅允许代理(192.168.1.50)连接 5520 端口
iptables -A INPUT -p udp --dport 5520 -s 192.168.1.50 -j ACCEPT
iptables -A INPUT -p udp --dport 5520 -j DROP

若不设置,玩家可能绕过代理直接连接!


玩家指令

指令 别名 描述
/server /srv 列出可用服务器
/server <名称> /srv <名称> 传送到指定服务器
/numdrassl /nd, /proxy 代理管理指令
/numdrassl version 显示代理版本
/numdrassl perm ... 权限管理

权限系统

权限存储在 data/permissions/ 目录下的 YAML 文件中:

data/permissions/
├── players/
│   └── {uuid}.yml       # 单个玩家权限
├── groups/
│   ├── default.yml      # 默认组(所有玩家)
│   └── admin.yml        # 自定义组
└── player-groups.yml    # 玩家与组的对应关系

玩家权限(players/{uuid}.yml

permissions:
  - numdrassl.command.server
  - numdrassl.command.help
  - -numdrassl.command.stop  # 拒绝(前缀加 -)

组权限(groups/admin.yml

name: admin
default: false
permissions:
  - numdrassl.command.*     # 通配符
  - numdrassl.admin

权限指令

指令 描述
/numdrassl perm user <玩家> info 显示玩家的组别和权限
/numdrassl perm user <玩家> add <权限> 授予权限
/numdrassl perm user <玩家> remove <权限> 移除权限
/numdrassl perm user <玩家> addgroup <组> 将玩家添加至组
/numdrassl perm user <玩家> removegroup <组> 将玩家移出组
/numdrassl perm group <组> add <权限> 为组添加权限
/numdrassl perm group <组> remove <权限> 为组移除权限
/numdrassl perm reload 从磁盘重新加载权限

控制台指令

指令 描述
auth login 使用 Hytale 认证
auth status 显示认证状态
auth logout 清除已存储的凭据
sessions 列出已连接的玩家
stop 关闭代理
help 显示可用的指令

插件开发

创建插件以扩展代理功能!插件可以:

  • 监听事件(玩家加入、聊天、指令等)
  • 注册自定义指令
  • 修改数据包
  • 与玩家和服务器互动

API 依赖:

dependencies {
    compileOnly("me.internalizable.numdrassl:api:1.0-SNAPSHOT")
}

插件示例:

@Plugin(id = "my-plugin", name = "My Plugin", version = "1.0.0")
public class MyPlugin {

    @Subscribe
    public void onPlayerChat(PlayerChatEvent event) {
        if (event.getMessage().contains("hello")) {
            event.getPlayer().sendMessage("你好呀!");
        }
    }
}

更多详情请参阅完整的插件开发指南


故障排除

"Proxy not authenticated"

在代理控制台中运行 auth login 并完成设备代码流程。

"Invalid player info message (is your proxy secret valid?)"

代理配置文件中的 proxySecret 与 Bridge 插件的 proxySecret 不一致。

"Connection timed out" 连接后端超时

  1. 检查后端服务器是否在运行
  2. 确认防火墙允许代理 IP 连接
  3. 确保配置文件中的地址和端口正确

"Cannot direct join numdrassl backend"

玩家尝试直接连接后端。请设置防火墙规则以阻止直接连接。

客户端显示 "unexpected packet"

后端服务器可能未安装 Bridge 插件,或未使用 --auth-mode insecure 启动。

Redis 连接失败(集群模式)

  1. 确认 Redis 服务器正在运行且可访问
  2. 检查配置文件中的 redis.hostredis.port
  3. 如果使用了认证,确保 redis.password 正确
  4. 确认防火墙允许连接 Redis 端口

多个代理间玩家未同步(集群模式)

  1. 确保所有代理的 cluster.enabled: true
  2. 确认所有代理连接到同一个 Redis 实例
  3. 检查每个代理的 cluster.proxyId 是否唯一

架构概览

单代理模式

┌─────────────┐                    ┌─────────────┐                    ┌─────────────┐
│   玩家     │ ──── QUIC/TLS ──── │  代理      │ ──── QUIC/TLS ──── │  后端      │
│   客户端   │                    │  Numdrassl  │                    │  服务器    │
└─────────────┘                    └─────────────┘                    └─────────────┘
                                          │
                                          ├── lobby(默认)
                                          ├── survival
                                          └── minigames

集群模式(多代理)

                              ┌──────────────────────────────────┐
                              │            Redis                 │
                              │   ┌────────────────────────┐     │
                              │   │  Pub/Sub 频道          │     │
                              │   │  • numdrassl:heartbeat │     │
                              │   │  • numdrassl:chat      │     │
                              │   │  • numdrassl:transfer  │     │
                              │   │  • numdrassl:broadcast │     │
                              │   └────────────────────────┘     │
                              │   ┌────────────────────────┐     │
                              │   │  共享状态              │     │
                              │   │  • 玩家位置            │     │
                              │   │  • 代理注册表          │     │
                              │   │  • 玩家数量            │     │
                              │   └────────────────────────┘     │
                              └──────────────┬───────────────────┘
                                             │
           ┌─────────────────────────────────┼─────────────────────────────────┐
           │                                 │                                 │
    ┌──────▼──────┐                   ┌──────▼──────┐                   ┌──────▼──────┐
    │ 代理(EU)  │                   │ 代理(US)  │                   │ 代理(Asia)│
    │ proxy-eu-1  │◄─── 传送 ───────►│ proxy-us-1  │◄─── 传送 ───────►│ proxy-as-1  │
    └──────┬──────┘                   └──────┬──────┘                   └──────┬──────┘
           │                                 │                                 │
    ┌──────▼──────┐                   ┌──────▼──────┐                   ┌──────▼──────┐
    │  后端       │                   │  后端       │                   │  后端       │
    │  • lobby    │                   │  • lobby    │                   │  • lobby    │
    │  • games    │                   │  • games    │                   │  • games    │
    └─────────────┘                   └─────────────┘                   └─────────────┘

认证流程

┌────────┐          ┌───────────┐          ┌─────────────┐          ┌─────────┐
│ 玩家   │          │  代理     │          │   Hytale    │          │ 后端   │
└───┬────┘          └─────┬─────┘          │  Sessions   │          └────┬────┘
    │                     │                └──────┬──────┘               │
    │  1. 连接            │                       │                      │
    │  (identity_token)   │                       │                      │
    │────────────────────►│                       │                      │
    │                     │  2. 请求认证授权       │                      │
    │                     │──────────────────────►│                      │
    │                     │                       │                      │
    │                     │  3. auth_grant        │                      │
    │                     │◄──────────────────────│                      │
    │  4. AuthGrant       │                       │                      │
    │◄────────────────────│                       │                      │
    │                     │                       │                      │
    │  5. AuthToken       │                       │                      │
    │────────────────────►│  6. 交换令牌           │                      │
    │                     │──────────────────────►│                      │
    │                     │◄──────────────────────│                      │
    │  7. ServerAuthToken │                       │                      │
    │◄────────────────────│                       │                      │
    │                     │                       │                      │
    │                     │  8. 连接 + HMAC 签名引荐                      │
    │                     │─────────────────────────────────────────────►│
    │                     │                       │                      │
    │                     │  9. ConnectAccept(密钥已验证)               │
    │                     │◄─────────────────────────────────────────────│
    │                     │                       │                      │

跨代理传送流程

┌────────┐     ┌──────────┐                    ┌──────────┐     ┌─────────┐
│ 玩家   │     │ 代理 A   │                    │ 代理 B   │     │ 后端   │
│        │     │ (源)     │      Redis         │ (目标)   │     │ 服务器 │
└───┬────┘     └────┬─────┘        │           └────┬─────┘     └────┬────┘
    │               │              │                │                │
    │ /server hub   │              │                │                │
    │──────────────►│              │                │                │
    │               │              │                │                │
    │               │  1. 发布     │                │                │
    │               │  TransferMsg │                │                │
    │               │─────────────►│                │                │
    │               │              │                │                │
    │               │              │  2. 订阅       │                │
    │               │              │  接收消息      │                │
    │               │              │───────────────►│                │
    │               │              │                │                │
    │ 3. ClientReferral            │                │                │
    │ (重新连接到代理 B)         │                │                │
    │◄──────────────│              │                │                │
    │               │              │                │                │
    │ 4. 新连接                    │                │                │
    │─────────────────────────────────────────────►│                │
    │               │              │                │                │
    │               │              │                │  5. 转发       │
    │               │              │                │  至后端        │
    │               │              │                │───────────────►│
  1. 玩家连接至代理,提供其 Hytale 凭据
  2. 代理向 Hytale 会话服务认证玩家
  3. 代理使用签名引荐将连接转发至默认后端
  4. 后端使用共享密钥验证引荐
  5. 数据包通过代理双向传输
  6. 在集群模式下,玩家状态和消息通过 Redis 的发布/订阅同步

集群模式(多代理网络)

对于大型网络,您可以在不同区域运行多个 Numdrassl 代理,并使用 Redis 共享状态。

概述

                                    ┌─────────────────┐
                                    │     Redis       │
                                    │  发布/订阅中心  │
                                    └────────┬────────┘
                    ┌───────────────────────┼───────────────────────┐
                    │                       │                       │
            ┌───────▼───────┐       ┌───────▼───────┐       ┌───────▼───────┐
            │ 代理(EU)    │       │ 代理(US)    │       │ 代理(亚洲) │
            │ 区域: eu      │       │ 区域: us      │       │ 区域: asia   │
            └───────┬───────┘       └───────┬───────┘       └───────┬───────┘
                    │                       │                       │
            ┌───────▼───────┐       ┌───────▼───────┐       ┌───────▼───────┐
            │   后端        │       │   后端        │       │   后端        │
            └───────────────┘       └───────────────┘       └───────────────┘

集群功能

  • 跨代理玩家追踪 - 知晓所有代理上的在线玩家
  • 跨代理消息 - 向其他代理上的玩家发送消息
  • 跨代理传送 - 在不同代理上的服务器间传送玩家
  • 负载均衡 - 将玩家路由至其所在区域负载最低的代理
  • 健康监控 - 自动心跳和失效代理检测

Redis 配置

添加至 config/proxy.yml

# 集群设置
cluster:
  enabled: true

  # 此代理实例的唯一 ID(未设置时自动生成)
  proxyId: "proxy-eu-1"

  # 用于地理负载均衡的区域标识符
  region: "eu"

  # Redis 连接设置
  redis:
    host: "redis.yourserver.com"
    port: 6379
    password: "your-redis-password"  # 可选
    database: 0

    # 连接池设置
    poolSize: 10
    timeout: 5000  # 毫秒

集群配置选项

选项 描述
cluster.enabled 启用集群模式(true/false
cluster.proxyId 此代理实例的唯一标识符
cluster.region 地理区域(例如 eu, us, asia
redis.host Redis 服务器主机名
redis.port Redis 服务器端口(默认:6379
redis.password Redis 认证密码(可选)
redis.database Redis 数据库编号(默认:0
redis.poolSize 连接池大小(默认:10
redis.timeout 连接超时时间(毫秒,默认:5000

跨代理指令

集群模式启用时:

指令 描述
/server <名称> 传送到服务器(跨代理工作)
/numdrassl cluster info 显示集群状态和已连接的代理
/numdrassl cluster players 列出所有代理上的玩家

插件消息 API

插件可跨集群发送消息:

@Plugin(id = "my-plugin", name = "My Plugin", version = "1.0.0")
public class MyPlugin {

    @Inject
    private MessagingService messaging;

    // 订阅自定义消息
    @Subscribe(channel = "my-channel")
    public void onCustomMessage(MyCustomData data) {
        // 处理来自任何代理的消息
    }

    // 向所有代理发布消息
    public void broadcastToCluster(String message) {
        messaging.publish("my-channel", new MyCustomData(message));
    }
}

系统频道

集群使用以下内部频道(插件可订阅):

频道 用途
numdrassl:heartbeat 代理健康监控
numdrassl:player_count 玩家数量同步
numdrassl:chat 跨代理聊天消息
numdrassl:transfer 跨代理玩家传送
numdrassl:broadcast 全服通告

社区

Discord 加入 Discord

相关链接


许可证

专有 - 保留所有权利。