ServerReplay

ServerReplay

一个完全服务端的fabric模组,允许你为回放模组或闪回录制回放。

管理

Server Replay

English | 中文

一个完全服务端实现的 Replay Mod 和 Flashback,此模组允许您同时录制服务器上的多名玩家或区块区域。这将生成回放文件,可配合回放模组或 Flashback 进行渲染。

![Modrinth 下载量](https://img.shields.io/modrinth/dt/server-replay?label=在 Modrinth 上下载&style=for-the-badge&logo=[已省略Base64资源])

为什么选择服务端?

与客户端 Replay Mod 或 Flashback 录制相比,服务端录制有许多优势:

  • 能够录制静态区块。
    • 您可以指定精确的区块大小(不受服务器视距限制)。
    • 录制的区块可以被卸载而不影响回放。
      • 不会出现区块闪烁(由于卸载和加载区块导致)。
      • 录制器也不会加载这些区块(例如,PCRC 会这样做)。
    • 根据区块是否被加载或是否有玩家在区块内,暂停和恢复录制器。
  • 能够录制单个玩家。
    • 玩家无需安装回放模组或 Flashback。
    • 您可以一次录制所有视角。
    • 录制可以通过配置自动进行。
  • 操作员(或任何拥有权限的人)可以随时开始录制。

然而,也存在一些缺点和已知问题:

  • 区块录制不会记录某些特性,例如自定义 Boss 血条。
  • 玩家录制可能与客户端 Replay Mod 或 Flashback 存在 100% 的一致性。
  • ServerReplay 并非专为重模组服务器设计。 实现自定义数据包的复杂模组可能不兼容。
    • 如果您遇到任何兼容性问题,请提交问题。

使用方法

此模组需要 Fabric 加载器、fabric-api 和 fabric-kotlin。

服务器上有两种录制方式,您可以配置为跟随并记录玩家的视角。 或者,您可以录制一个静态区块区域。

快速开始

注意: 本文档适用于最新版本的模组,对于旧版本模组请查看其他分支。

本文档的这一部分将简要指导您完成基本设置。 以及包含一些重要信息。

玩家

要在服务器上录制玩家,您可以运行 /replay start players <player(s)>,例如:

/replay start players senseiwells
/replay start players @a
/replay start players @a[gamemode=survival]

玩家录制器与玩家绑定,并以服务器的视距进行录制。

如果玩家离开或服务器停止,回放将自动停止并保存。

或者,如果您希望手动停止录制,可以运行 /replay stop players <player(s)> <save?>, 使用此命令您还可以不保存录制而停止,例如:

/replay stop players senseiwells
/replay stop players @r
/replay stop players senseiwells false

然后回放将保存到您的 "player_recording_path" 位置, 该位置在包含玩家 UUID 的文件夹中指定。 默认情况下,位置在 ./recordings/players/<uuid>/<日期和时间>.mcpr。

此文件可以放在客户端的 ./replay_recordings 中,并使用回放模组打开。

警告: 尝试录制地毯机器人或其他非真实玩家可能会导致意外行为。 如果您想录制大区块区域,请使用区块录制器!

区块

注意: 虽然模组会录制您指定的区块,但 Minecraft 客户端 不会 渲染最外层的区块。 因此,要录制 可见 区块区域,您必须在边界上添加一个区块,例如,录制从 -5, -5 到 5, 5 的可见区域,您必须录制 -6, -6 到 6, 6。

要录制服务器上的区块区域,您可以运行 /replay start chunks from <chunkFromX> <chunkFromZ> to <chunkToX> <chunkToZ> in <dimension?> named <name?>,例如:

/replay start chunks from -5 -5 to 5 5 in minecraft:overworld named MyChunkRecording
/replay start chunks from 54 67 to 109 124
/replay start chunks from 30 30 to 60 60 in minecraft:the_nether 

或者,您可以指定一个区块及其周围半径进行录制 /replay start chunks around <chunkX> <chunkZ> radius <radius> in <dimension?> named <name?>,例如:

/replay start chunks around 0 0 radius 5
/replay start chunks around 67 12 radius 16 in minecraft:overworld named Perimeter Recorder

区块录制器是静态的,不能移动,它们录制指定的区块。 需要注意的重要一点是,当回放开始时,指定的区块 将被加载(如有必要则生成)。 然而,之后区块录制器不会加载这些区块。

您可以通过 "chunk_recorder_load_radius" 设置进一步配置此功能, 该设置将设置区块录制器将加载的最大初始半径, 在此半径之外录制的任何区块都需要 '自然' 加载才能被录制。

如果服务器停止,回放将自动停止并保存。

或者,如果您希望手动停止录制,可以使用名称停止指定的录制器 使用 /replay stop chunks named <name> <save?>,例如:

/replay stop chunks named "Perimeter Recorder" false
/replay stop chunks named MyChunkRecording

然后回放将保存到您的 "chunk_recording_path" 位置, 该位置在包含区块录制器名称的文件夹中指定。 默认情况下,位置在 ./recordings/chunks/<name>/<日期和时间>.mcpr。

此文件可以放在客户端的 ./replay_recordings 中,并使用回放模组打开。

编码

ServerReplay 支持 Flashback(适用于较新版本)以及回放模组回放。

默认情况下,所有录制都将使用回放模组的格式录制; 但是,您可以在游戏中运行以下命令更改此设置:

/replay encoding default set flashback
/replay encoding default set replay_mod

注意: 您可以同时使用 Flashback 和回放模组录制玩家的视角。 您可以通过将默认编码设置为 flashback、开始录制, 然后将编码设置为 replay_mod 并开始另一个录制来实现这一点。

对于区块录制也可以这样做,但每个录制器必须有一个唯一的名称。

查看

回放录制完成后,您可以完全在服务端查看回放。 查看回放的玩家在查看期间将完全从实际服务器中移除,并被视为离线。

本质上,这只是"运行"另一个向客户端发送数据包的服务器。这异步运行 与主服务器,因此对性能的影响很小甚至没有。

回放录制完成后,您可以点击聊天中的绿色文本查看 刚完成的回放。 查看区块回放的命令是:/replay view chunks <name> <date-time>,对于玩家: /replay view players <uuid> <date-time>,例如:

/replay view player "d4fca8c4-e083-4300-9a73-bf438847861c" "2024-05-11--19-19-55"
/replay view chunks "Chunks (183, 166) to (203, 186)" "2024-05-11--19-19-55"

然后您将被传送到新的"服务器",回放将开始播放。 在查看回放时,您只能访问有限的命令,包括:

  • /replay view pause 暂停当前回放的播放。
  • /replay view unpause 恢复当前回放的播放。
  • /replay view speed <multiplier> 设置当前回放的播放速度。
  • /replay view restart 重新启动当前回放的播放。
  • /replay view close 关闭当前回放并带您返回服务器。
  • /replay view progress <hide|show> 隐藏或显示进度 Boss 条。

如果在观看回放时断开连接,您登录后将返回服务器。

下载

如果配置中启用了 "allow_downloading_replays" 且服务器 IP 和下载端口设置正确,您可以从服务器下载任何回放。

您可以使用 /replay download 命令获取下载指定回放的 URL,例如:

/replay download players d4fca8c4-e083-4300-9a73-bf438847861c "2024-05-11--19-19-55"
/replay download chunks "Chunks (183, 166) to (203, 186)" "2024-05-11--19-19-55"

这将向您发送一条聊天消息;您可以点击提供的链接下载文件。

命令

所有命令的说明;玩家必须拥有 op(4 级)权限,或者如果您有权限模组(例如 LuckPerms),玩家可以拥有 server-replay.commands.replay 权限来访问这些命令。

  • /replay start players <player(s)> 手动开始录制给定玩家的回放。
  • /replay start chunks from <chunkFromX> <chunkFromZ> to <chunkToX> <chunkToZ> in <dimension?> named <name?> 手动开始录制给定区块区域的回放,如果未指定维度,则使用命令用户的维度,名称决定回放文件在录制路径中的保存位置。
  • /replay start chunks around <chunkX> <chunkZ> radius <radius> in <dimension?> named <name?> 此命令与上述命令实现相同效果;但是,您可以指定给定区块周围的半径。
  • /replay stop players <player(s)> <save?> 手动停止录制给定玩家的回放, 您可以选择传入是否应保存回放;默认情况下为 true。
  • /replay stop chunks from <chunkFromX> <chunkFromZ> to <chunkToX> <chunkToZ> in <dimension?> <save?> 手动停止录制给定区块区域的回放,如果未指定维度,则使用命令用户的维度,您可以选择传入是否应保存回放;默认情况下为 true。
  • /replay stop chunks named <name> <save?> 此命令让您执行与上述命令相同的操作;但是,您可以通过名称指定区块区域。
  • /replay stop [chunks|players] all <save?> 手动停止 所有 区块或玩家回放,您可以选择传入是否应保存回放;默认情况下为 true。
  • /replay status 发送状态消息,显示回放是否启用,以及当前正在录制的所有玩家和区块列表、它们已录制的时间以及文件大小。
  • /replay reload 重新加载回放模组的配置文件。
  • /replay encoding default set <encoding-type> 设置录制回放的类型,可选择 flashback 或 replay_mod

配置

启动服务器后,将在路径 ./config/server-replay/config.json 中生成一个新文件,默认情况下应如下所示:

{
  "default_encoding": "replay_mod",
  "world_name": "World",
  "server_name": "Server",
  "chunk_recording_path": "./recordings/chunks",
  "player_recording_path": "./recordings/players",
  "player_recording_name": "{uuid}",
  "max_file_size": "0 B",
  "restart_after_max_file_size": false,
  "max_duration": "0s",
  "restart_after_max_duration": false,
  "recover_unsaved_replays": true,
  "delete_replays_after_duration": "0s",
  "log_deleted_replays": true,
  "chunk_recorder_load_radius": -1,
  "chunk_recording_strategy": "always",
  "pause_notify_players": true,
  "notify_admins_of_status": true,
  "include_resource_packs": true,
  "ignore_custom_payloads": false,
  "ignore_sound_packets": false,
  "ignore_light_packets": true,
  "ignore_chat_packets": false,
  "ignore_action_bar_packets": false,
  "ignore_scoreboard_packets": false,
  "optimize_explosion_packets": true,
  "optimize_entity_packets": false,
  "record_hotbar": false,
  "record_voice_chat": false,
  "replay_server_ip": null,
  "allow_downloading_replays": false,
  "automatically_record": false,
  "player_predicate": {
    "type": "none"
  },
  "chunks": []
}
配置项 描述
"default_encoding"

回放的编码方式,可以是 "replay_mod" 或 "flashback". 默认(如果未指定)为 "replay_mod".

"world_name"

将显示在回放文件中的世界名称。

"server_name"

将显示在回放文件中的服务器名称。

"player_recording_path"

您希望保存玩家录制的路径。

"chunk_recording_path"

您希望保存区块录制的路径。

"player_recording_name"

这决定了每个特定玩家录制目录的名称。默认为 "{uuid}",使用玩家的 UUID。您也可以使用 "{username}" 插入玩家名称。例如,您可以设置为:"Recordings for: {username} ({uuid})".

"max_file_size"

这指定了回放的 max_file_size,如果达到限制,录制将自动停止。此文件大小指的是原始录制大小,且 不是最终压缩的回放大小,通常最终压缩的回放大小要小得多。

"restart_after_max_file_size"

如果设置了 max_file_size 并达到此限制,则回放录制将自动重新开始以创建新的回放文件。

"max_duration"

设置回放的最大持续时间,一旦回放录制达到指定时间将停止,这是任意数字后跟单位(您也可以有多个单位),例如 "4h 35m 2.1s". 将此设置为 "0s" 表示没有最大持续时间限制。注意:如果录制器被暂停,其持续时间不会增加。

"restart_after_max_duration"

如果设置了 max_duration 并达到此限制,则回放录制将自动重新开始以创建新的回放文件。

"recover_unsaved_replays"

这将尝试恢复任何未保存的回放,例如,如果您的服务器崩溃或停止且在回放停止或保存完成之前,这不保证回放不会损坏,但会尝试抢救可用的内容。

"delete_replays_after_duration"

删除旧回放文件的持续时间。这是自文件上次 修改 以来的持续时间。持续时间与 max_duration 选项格式相同,例如 "5d 10h". 将此设置为 "0s" 以禁用。

"log_deleted_replays"

当因过期而删除回放时,是否输出服务器日志。delete_replays_after_duration 必须启用此功能才能生效。

"fixed_daylight_cycle"

如果您不希望长时间延时摄影中出现持续的昼夜循环,这会修复回放中的昼夜循环。应设置为一天中的时间(以 tick 为单位),例如 6000(正午)。要禁用固定昼夜循环,将该值设置为 -1.

"chunk_recorder_load_radius"

这设置区块录制器的默认加载半径,当您想录制非常大的区域且不希望所有录制的区块同时加载时,这很有用。

例如,如果您录制一个 13x13 的区块区域,您可以将半径设置为 3,使中心最靠内的 7x7 被初始加载,其余区块将在它们被 '自然' 加载时进行录制。

将此设置为 -1 以加载所有区块。

"chunk_recording_strategy"

这定义了区块录制器如何录制,有 4 个选项:

  • "always" - 始终录制区块,即使未加载(就像它们被加载一样)
  • "chunk_loaded" - 仅当区域中的任何区块被加载时录制,如果所有区块被卸载则暂停录制
  • "chunk_contains_player" - 仅当玩家在区域内时录制区块(仅支持 flashback)
  • "chunk_contains_non_spectator_player" - 与上述相同,但仅适用于非旁观者玩家(仅支持 flashback)
"pause_notify_players"

如果 chunk_recording_strategy 设置为允许录制器自动暂停的选项,并且此选项启用,则每当区块区域的录制被暂停或恢复时,所有在线玩家都会被通知。

"notify_admins_of_status"

启用后,将通知管理员何时开始回放、何时结束回放、何时回放保存完成,以及发生的任何错误。

"include_resource_packs"

如果启用,所有服务端资源包将被复制到回放文件中,以确保正确播放。禁用此选项将减小文件大小,但会在查看回放时尝试从原始源下载资源包,不保证此操作正确。(仅限回放模组)

"ignore_custom_payloads"

如果启用,所有自定义有效负载(模组数据包)将被忽略,如果其他模组的数据包导致录制问题,可以启用此选项。

"ignore_sound_packets"

如果您正在录制大区域进行延时摄影,可能不希望录制任何声音,因为声音会占用大量存储空间。

"ignore_light_packets"

光照既在客户端也在服务器上计算,因此光照数据包大多多余。

"ignore_chat_packets"

如果聊天数据包(来自服务器和其他玩家)对您的回放没有必要,则停止录制它们。

"ignore_action_bar_packets"

如果操作栏数据包对您的回放没有必要,则停止录制它们。

"ignore_scoreboard_packets"

停止录制计分板数据包(例如,如果您有显示挖掘次数的计分板,则不会出现此内容,玩家的分数也不会被录制)。

"optimize_explosion_packets"

这通过不发送客户端爆炸数据包而仅发送爆炸粒子和声音来大幅减小文件大小。

"optimize_entity_packets"

这使得客户端处理某些实体的逻辑,从而减小文件大小,例如弹射物和 TNT。这可能导致一些不一致,但可能可以忽略不计。

"record_hotbar"

这为玩家录制启用录制快捷栏,以获得更好的第一人称体验。(仅限 flashback)

"record_voice_chat"

这启用对语音聊天的录制支持,如果您安装了 simple-voice-chat 模组,在 flashback 中播放时将开箱即用,但在回放模组中观看回放时,您必须安装 replay-voice-chat.

"replay_server_ip"

如果您的服务器使用自定义服务端资源包,并且您希望在服务端回放查看器中查看这些资源包,这是必需的。如果您想允许用户下载回放,这也是必需的。

这应包含您服务器的公共 IP 地址。

"allow_downloading_replays"

决定用户是否能够下载录制的回放。

"automatically_record"

是否根据 "player_predicate" 和/或 "chunks" 配置自动录制玩家或区块。如果启用,符合定义谓词的任何玩家都将自动录制,并且服务器启动时将自动录制定义的任何区块。

"player_predicate"

用于自动录制玩家的谓词,更多信息请参阅 谓词 部分。

"chunks"

服务器启动时要自动录制的区块列表,更多信息请参阅 区块 部分。

区块配置

您可以定义在服务器启动或启用 ServerReplay 时要自动录制的区块区域。

每个区块定义必须包括:"name"、"dimension"、"from_x"、"to_x"、"from_z" 和 "to_z"。例如:

{
  // ...
  "chunks": [
    {
      "name": "My Chunks",
      "dimension": "minecraft:overworld",
      "from_x": -5,
      "from_z": -5,
      "to_x": 5,
      "to_z": 5
    },
    {
      "name": "My Nether Chunks",
      "dimension": "minecraft:the_nether",
      "from_x": 100,
      "from_z": 50,
      "to_x": 90,
      "to_z": 60
    }
    // ...
  ]
}

谓词配置

您可以定义一个谓词,用于确定服务器上哪些玩家将自动录制。 您可以通过指定玩家是否具有特定的 uuid、名称、是否在特定队伍中,或是否为操作员来实现这一点。

定义谓词后,您必须在游戏中运行 /replay reload,然后玩家需要重新登录才能被录制(如果符合谓词条件)。

最基本的选项是仅录制所有玩家,在这种情况下您可以使用:

{
  // ...
  "player_predicate": {
    "type": "all"
  }
}

如果您只想录制具有特定名称或 uuid 的玩家,可以这样做:

{
  // ...
  "player_predicate": {
    "type": "has_name",
    "names": [
      "senseiwells",
      "foobar"
    ]
  }
}
{
  // ...
  "player_predicate": {
    "type": "has_uuid",
    "uuids": [
      "41048400-886d-497d-9d97-9fe7c9b63afa",
      "71266dbd-db0a-484a-b859-3f135590d7a9",
      "47d072ca-d7a2-467c-9b60-de501907e91d",
      "0e324e7f-e78e-4777-b501-7ae08a65b1eb",
      "7d9e24c2-9d0f-479f-81c7-27389624ebb2"
    ]
  }
}

如果您只想录制操作员:

{
  // ...
  "player_predicate": {
    "type": "has_op",
    "level": 4
  }
}

如果您只想录制特定队伍的玩家,这对允许在游戏中添加和移除玩家很有用,因为您可以将玩家添加到队伍然后让他们重新登录:

{
  // ...
  "player_predicate": {
    "type": "in_team",
    "teams": [
      "Red",
      "Blue",
      "Spectators"
    ]
  }
}

您还可以使用 'not' 否定谓词,并使用 'or' 和 'and' 组合它们。 例如,如果您想录制所有非操作员且也不叫 'senseiwells' 或不在红队的玩家:

{
  // ...
  "player_predicate": {
    "type": "and",
    "predicates": [
      {
        "type": "not",
        "predicate": {
          "type": "has_op",
          "level": 4
        }
      },
      {
        "type": "not",
        "predicate": {
          "type": "or",
          "predicates": [
            {
              "type": "has_name",
              "names": [
                "senseiwells"
              ]
            },
            {
              "type": "in_team",
              "teams": [
                "Red"
              ]
            }
          ]
        }
      } 
    ]
  }
}

如果您使用地毯模组并能够生成虚拟玩家,您可能希望将它们排除在录制之外。 您可以使用 is_fake 谓词实现这一点:

{
  // ...
  "player_predicate": {
    "type": "not",
    "predicate": {
      "type": "is_fake"
    }
  }
}

致谢

感谢 ExperimentalIdea 帮助测试 Flashback 支持!