Skip to content

Commit cfb3d00

Browse files
committed
fix(mixin): adapt to MC 1.20.2/1.20.5+ API changes
- Target ServerCommonPacketListenerImpl for MC >= 1.20.2 packet listener split - Use onEffectsRemoved (Collection) for MC 1.20.5+ effect removal - Fix playSeededSound entity signature (Player source, not Entity) - Simplify MixinLeashable to interface, drop sendPacket param in dropLeash - docs: replace template README with ServerFlashback documentation
1 parent 01c4bb3 commit cfb3d00

File tree

6 files changed

+109
-154
lines changed

6 files changed

+109
-154
lines changed

README-CN.md

Lines changed: 32 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,74 +1,51 @@
1-
# Preprocessor Example Mod
1+
# ServerFlashback
22

33
[English](README.md) | **简体中文**
44

5-
这是为 [Fallen_Breath](https://github.com/Fallen-Breath)
6-
[preprocessor 实现](https://github.com/Fallen-Breath/preprocessor)
7-
而制作的示例模组,该实现基于 [ReplayMod](https://github.com/ReplayMod)
8-
[preprocessor](https://github.com/ReplayMod/preprocessor)
5+
服务端 Fabric 模组,以 FlashBack 原生格式录制 Minecraft 回放。支持区域录制,录制范围可超出服务器已加载区块。输出文件可直接被 [FlashBack](https://github.com/Flashback-MC/Flashback) 客户端模组用于播放。
96

10-
## 免责声明
7+
## 支持版本
118

12-
如果你是Fabric模组开发的新手,那这个模板不适合你。请先学习使用 [Fabric Example Mod](https://github.com/FabricMC/fabric-example-mod)
9+
- Minecraft 1.17.1
10+
- Minecraft 1.21.4
1311

14-
## 特性
12+
## 依赖
1513

16-
这个仓库提供了一些其他preprocessor模板仓库没有的独特功能,包括:
14+
- Fabric Loader
15+
- Fabric API
16+
- 仅服务端(录制无需客户端安装)
1717

18-
- 自动创建和链接Gradle子项目(主要由 @Jog_Ming 实现)
19-
- GitHub Actions矩阵发布和缓存刷新(主要由 @XRain66 实现)
18+
## 安装
2019

21-
## 设置
20+
1.[Releases](https://github.com/Trirrin/ServerFlashback/releases) 下载对应 Minecraft 版本的构建
21+
2. 将 JAR 放入服务器的 `mods` 文件夹
22+
3. 重启服务器
2223

23-
要设置项目,你需要修改以下指定文件中的字段:
24+
## 命令
2425

25-
- `gradle.properties` 中的 `group``id`(对应传统fabric模组中的 `maven_group``archives_base_name`
26-
- `src/main` 中的目录和文件名
27-
- `fabric.mod.json` 中的 `name``description``authors``contact``entrypoints` 字段
28-
- `<modid>.mixins.json` 中的 `package` 字段
26+
所有命令需要 OP 等级 2:
2927

30-
这个模板默认使用Minecraft版本1.17.1。要更改版本,只需重命名 `versions` 文件夹中的目录,并将 `versions/mainProject`
31-
的内容改为所需的版本,然后同步Gradle。要添加对新版本的支持,请执行以下操作:
28+
| 命令 | 说明 |
29+
|------|------|
30+
| `/serverflashback start <坐标> <半径> [名称]` | 在指定方块坐标开始录制,半径 16–4096 格 |
31+
| `/serverflashback stop [名称]` | 停止录制。省略名称时,若仅有一个活跃录制则停止该录制 |
32+
| `/serverflashback pause [名称]` | 暂停录制 |
33+
| `/serverflashback resume [名称]` | 恢复暂停的录制 |
34+
| `/serverflashback list` | 列出活跃录制 |
35+
| `/serverflashback mark <名称> [描述]` | 向当前录制添加标记点(需以玩家身份执行) |
3236

33-
-`versions` 中创建一个新文件夹,命名为你想要添加的版本
34-
-`versions` 中创建一个名为 `mapping-<旧版本>-<新版本>.txt` 的txt文件
35-
- 这个文件是必需的,但可以留空。它也可以包含旧版本和新版本之间类的转换关系。
36-
- 例如,在1.17.1中,负责生成幻翼的类是 `net.minecraft.world.gen.PhantomSpawner`
37-
- 然而,在1.20.1中,它被重命名(移动)为 `net.minecraft.world.spawner.PhantomSpawner`
38-
- 为了让这个转换自动进行,在 `versions/mapping-1.17.1-1.20.1.txt` 中写入条目
39-
`net.minecraft.world.gen.PhantomSpawner net.minecraft.world.spawner.PhantomSpawner`
40-
- 每个转换条目应占一行。
41-
- 如果你想要支持两个以上的版本,请确保映射文件是按从旧到新的顺序链接的
42-
- 例如,如果你想支持三个版本:1.17.1、1.20.1和1.21.1,映射文件应该是 `versions/mapping-1.17.1-1.20.1.txt`
43-
`versions/mapping-1.20.1-1.21.1.txt`
44-
- 添加映射txt文件后,同步Gradle。
45-
- 如果你想专注于开发1.17.1以外的版本,修改文件 `versions/mainProject`,然后同步Gradle
46-
- 在每个版本文件夹中,创建一个 `gradle.properties` 文件和一个 `<modid>.accesswidener` 文件。
47-
-`gradle.properties` 中,指定要使用的fabric加载器和yarn映射版本。如果不确定,请参考
48-
`versions/1.17.1/gradle.properties` 中的示例
49-
- `<modid>.accesswidener` 是必需的,但可以没有条目(不过必须在文件开头声明 `accessWidener v2 named`
37+
## 输出
5038

51-
若要添加一个模组作为依赖,则需要进行更多更改:
39+
回放文件(`.zip`)保存在 `<服务器根目录>/serverflashback/replays/`。使用 FlashBack 客户端模组打开即可播放。
5240

53-
-`common.gradle` 中声明模组仓库。
54-
- 转到 `fabric.mod.json` 并留出空间插入依赖声明。
55-
- 找到 `"depends"` 键并插入依赖声明,如下所示:`"depends": { ..., "carpet": ">=$carpet_version", ... }`
56-
- 在每个版本文件夹的 `gradle.properties` 文件中,创建条目来存储依赖的版本信息。每个依赖很可能需要两个条目:一个用于依赖声明中使用的完整版本字符串(例如
57-
`carpet_full_version=1.17.1-1.4.57+v220119`),另一个用于 `fabric.mod.json` 中使用的简化版本字符串(例如
58-
`carpet_version=1.4.57`
59-
-`common.gradle``dependencies` 部分,插入相应的 `modImplementation``include` 声明。(例如
60-
`modImplementation "carpet:fabric-carpet:$carpet_full_version"`
61-
-`common.gradle``processResources` 部分,找到以 `filesMatching('fabric.mod.json')` 开头的行,并插入一个键值对,如下所示:
62-
`filesMatching('fabric.mod.json') { expand ..., carpet_version: carpet_version, ... }`
41+
## 构建
6342

64-
如果你希望GitHub的 `release` 操作自动发布到Modrinth,请确保设置环境变量 `MODRINTH_ID` 和密钥 `MODRINTH_TOKEN`。如果你有模组依赖,请在
65-
`.github/workflows/release.yml``dependencies`
66-
字段中声明它们。有关声明语法,请参考 [mc-publish](https://github.com/Kira-NT/mc-publish)
67-
68-
有关preprocessor的更多信息,包括其确切用法、目的和机制,请参考 [ReplayMod的仓库](https://github.com/ReplayMod/preprocessor)
69-
70-
有关Fallen_Breath所做更改的更多信息,请参考 [他的仓库](https://github.com/Fallen-Breath/preprocessor)
43+
```bash
44+
./gradlew :1.21.4:build # 构建 MC 1.21.4 版本
45+
./gradlew :1.17.1:build --no-parallel # 构建 MC 1.17.1 版本
46+
./gradlew :1.21.4:compileJava # 仅编译(更快)
47+
```
7148

7249
## 许可证
7350

74-
本模板在GPL-3.0许可证下提供。
51+
GPL-3.0

README.md

Lines changed: 45 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -1,87 +1,51 @@
1-
# Preprocessor Example Mod
1+
# ServerFlashback
22

33
**English** | [简体中文](README-CN.md)
44

5-
Example mod for [Fallen_Breath](https://github.com/Fallen-Breath)'
6-
s [implementation](https://github.com/Fallen-Breath/preprocessor) of [ReplayMod](https://github.com/ReplayMod)'
7-
s [preprocessor](https://github.com/ReplayMod/preprocessor).
8-
9-
## Disclaimer
10-
11-
If you are new to fabric modding, this template is not for you. Please learn to
12-
use [Fabric Example Mod](https://github.com/FabricMC/fabric-example-mod) instead.
13-
14-
## Features
15-
16-
This repository provides some unique features that no other preprocessor template repositories provide, including:
17-
18-
- Automatic Gradle subproject creation and linking (majorly implemented by @Jog_Ming )
19-
- GitHub Actions matrix publishing and cache refresh (majorly implemented by @XRain66 )
20-
21-
## Setup
22-
23-
To set up the project, you will need to change the following fields in specified files:
24-
25-
- `group` and `id` (corresponding to `maven_group` and `archives_base_name` in conventional fabric mods) in
26-
`gradle.properties`
27-
- The directory and file names in `src/main`
28-
- The `name`, `description`, `authors`, `contact`, and `entrypoints` field in `fabric.mod.json`
29-
- The `package` field in `<modid>.mixins.json`
30-
31-
This template defaults to Minecraft version 1.17.1. To change that, simply rename the folder in `versions` and change
32-
the contents of `versions/mainProject` to the version of desire, and sync Gradle.
33-
34-
To add support for a new version, do:
35-
36-
- Create a new folder in `versions` named to be the version you want to add
37-
- Create a txt file in `versions` named `mapping-<old version>-<new version>.txt`
38-
- This file is required, but could be left blank. It could also feature the translations of classes between the old
39-
version and the new version.
40-
- For example, in 1.17.1, the class responsible for spawning phantoms is `net.minecraft.world.gen.PhantomSpawner`
41-
- However, in 1.20.1, it was renamed (moved) to `net.minecraft.world.spawner.PhantomSpawner`.
42-
- To allow this translation to happen automatically, write the entry
43-
`net.minecraft.world.gen.PhantomSpawner net.minecraft.world.spawner.PhantomSpawner` in
44-
`versions/mapping-1.17.1-1.20.1.txt`.
45-
- Each translation entry should occupy one line.
46-
- If you want to add support for more than two versions, make sure that the mapping files are chained together from
47-
older versions to newer versions
48-
- For example, if you want to support three versions, 1.17.1, 1.20.1 and 1.21.1, the mapping files should be
49-
`versions/mapping-1.17.1-1.20.1.txt` and `versions/mapping-1.20.1-1.21.1.txt`
50-
- After adding the mapping txt files, sync Gradle.
51-
- If you want to focus development on a version other than 1.17.1, modify the file `versions/mainProject`, and sync
52-
Gradle
53-
- In each of the version folders, create a `gradle.properties` file and a `<modid>.accesswidener` file.
54-
- In `gradle.properties`, specify the fabric loader and the yarn mapping versions to be used. Refer to the examples
55-
given in `versions/1.17.1/gradle.properties` if unsure
56-
- `<modid>.accesswidener` is necessary but could have no entries (must declare `accessWidener v2 named` at the very
57-
start of the file though)
58-
59-
To add a mod as dependency, more changes had to be made:
60-
61-
- Declare the mod repository in `common.gradle`.
62-
- Go to `fabric.mod.json` and make space to insert the dependency declarations.
63-
- Find the `"depends"` key and insert the dependency declaration like this:
64-
`"depends": { ..., "carpet": ">=$carpet_version", ... }`
65-
- In every version folder's `gradle.properties` file, create entries to store the dependency's version information. You
66-
will likely need two entries for each dependency, one for the full version string to be used in dependency
67-
declaration (such as `carpet_full_version=1.17.1-1.4.57+v220119`), and another for the simplified version string to be
68-
used in `fabric.mod.json` (such as `carpet_version=1.4.57`)
69-
- In the `dependencies` section of `common.gradle`, insert the respective `modImplementation` or `include`
70-
declarations. (i.e. `modImplementation "carpet:fabric-carpet:$carpet_full_version"`)
71-
- In the `processResources` section of `common.gradle`, find the line starting with `filesMatching('fabric.mod.json')`,
72-
and insert a key-value pair like this:
73-
`filesMatching('fabric.mod.json') { expand ..., carpet_version: carpet_version, ... }`
74-
75-
If you want the GitHub `release` action to automatically publish to Modrinth, make sure to set the environment variable
76-
`MODRINTH_ID` and secret `MODRINTH_TOKEN`. If you have mod dependencies, declare them in `.github/workflows/release.yml`
77-
in the `dependencies` field. Refer to [mc-publish](https://github.com/Kira-NT/mc-publish) for declaration syntax.
78-
79-
For more information on preprocessor, including its exact usage, purpose, and mechanics, refer
80-
to [ReplayMod's repository](https://github.com/ReplayMod/preprocessor).
81-
82-
For more information on changes made by Fallen_Breath, refer
83-
to [his repository](https://github.com/Fallen-Breath/preprocessor).
5+
Server-side Fabric mod that records Minecraft replays in FlashBack's native format. Supports area-based recording with extended chunk range beyond the server's loaded chunks. Output files are directly compatible with the [FlashBack](https://github.com/Flashback-MC/Flashback) client mod for playback.
6+
7+
## Supported Versions
8+
9+
- Minecraft 1.17.1
10+
- Minecraft 1.21.4
11+
12+
## Requirements
13+
14+
- Fabric Loader
15+
- Fabric API
16+
- Server-side only (no client installation required for recording)
17+
18+
## Installation
19+
20+
1. Download the build for your Minecraft version from [Releases](https://github.com/Trirrin/ServerFlashback/releases)
21+
2. Place the JAR in your server's `mods` folder
22+
3. Restart the server
23+
24+
## Commands
25+
26+
All commands require OP level 2:
27+
28+
| Command | Description |
29+
|---------|-------------|
30+
| `/serverflashback start <pos> <radius> [name]` | Start recording at block position with given radius (16–4096 blocks) |
31+
| `/serverflashback stop [name]` | Stop recording. Omit name to stop the only active recording |
32+
| `/serverflashback pause [name]` | Pause recording |
33+
| `/serverflashback resume [name]` | Resume paused recording |
34+
| `/serverflashback list` | List active recordings |
35+
| `/serverflashback mark <name> [description]` | Add a marker to the current recording (run as player) |
36+
37+
## Output
38+
39+
Replay files (`.zip`) are saved to `<server_root>/serverflashback/replays/`. Open them with the FlashBack client mod for playback.
40+
41+
## Build
42+
43+
```bash
44+
./gradlew :1.21.4:build # Build for MC 1.21.4
45+
./gradlew :1.17.1:build --no-parallel # Build for MC 1.17.1
46+
./gradlew :1.21.4:compileJava # Compile only (faster)
47+
```
8448

8549
## License
8650

87-
This template is available under the GPL-3.0 license.
51+
GPL-3.0

src/main/java/cn/net/rms/serverflashback/mixin/MixinLeashable.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
//#else
2020
//$$ @Mixin(Mob.class)
2121
//#endif
22-
public class MixinLeashable {
22+
public interface MixinLeashable {
2323

2424
//#if MC >= 12005
2525
@Inject(method = "setLeashedTo", at = @At("RETURN"))
@@ -35,8 +35,7 @@ public class MixinLeashable {
3535
}
3636

3737
@Inject(method = "dropLeash", at = @At("HEAD"))
38-
private void serverflashback$onDropLeash(boolean sendPacket, boolean dropItem, CallbackInfo ci) {
39-
if (!sendPacket) return;
38+
private void serverflashback$onDropLeash(CallbackInfo ci) {
4039
Entity self = (Entity) (Object) this;
4140
if (self.level() instanceof ServerLevel serverLevel
4241
&& RecordingManager.getInstance().hasActiveRecordings()) {

src/main/java/cn/net/rms/serverflashback/mixin/MixinLivingEntityEffects.java

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import net.minecraft.world.entity.Entity;
1010
import net.minecraft.world.entity.LivingEntity;
1111
//#if MC >= 12005
12+
import java.util.Collection;
1213
import net.minecraft.core.Holder;
1314
//#endif
1415
import org.spongepowered.asm.mixin.Mixin;
@@ -60,15 +61,17 @@ public class MixinLivingEntityEffects {
6061
}
6162

6263
//#if MC >= 12005
63-
@Inject(method = "onEffectRemoved", at = @At("RETURN"))
64-
private void serverflashback$onEffectRemoved(MobEffectInstance effect, CallbackInfo ci) {
64+
@Inject(method = "onEffectsRemoved", at = @At("RETURN"))
65+
private void serverflashback$onEffectRemoved(Collection<MobEffectInstance> effects, CallbackInfo ci) {
6566
Entity self = (Entity) (Object) this;
6667
if (self.level() instanceof ServerLevel serverLevel
6768
&& RecordingManager.getInstance().hasActiveRecordings()) {
68-
ClientboundRemoveMobEffectPacket packet = new ClientboundRemoveMobEffectPacket(
69-
self.getId(), effect.getEffect());
70-
RecordingManager.getInstance().onPositionedGamePacket(
71-
serverLevel, self.getX(), self.getZ(), packet);
69+
for (MobEffectInstance effect : effects) {
70+
ClientboundRemoveMobEffectPacket packet = new ClientboundRemoveMobEffectPacket(
71+
self.getId(), effect.getEffect());
72+
RecordingManager.getInstance().onPositionedGamePacket(
73+
serverLevel, self.getX(), self.getZ(), packet);
74+
}
7275
}
7376
}
7477
//#else

src/main/java/cn/net/rms/serverflashback/mixin/MixinServerGamePacketListenerImpl.java

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
import net.minecraft.network.protocol.game.*;
66
import net.minecraft.server.level.ServerLevel;
77
import net.minecraft.server.level.ServerPlayer;
8+
//#if MC >= 12002
9+
import net.minecraft.server.network.ServerCommonPacketListenerImpl;
10+
//#endif
811
import net.minecraft.server.network.ServerGamePacketListenerImpl;
912
import org.spongepowered.asm.mixin.Mixin;
1013
import org.spongepowered.asm.mixin.Shadow;
@@ -15,10 +18,16 @@
1518

1619
import java.util.Set;
1720

18-
@Mixin(ServerGamePacketListenerImpl.class)
21+
//#if MC >= 12002
22+
@Mixin(ServerCommonPacketListenerImpl.class)
23+
//#else
24+
//$$ @Mixin(ServerGamePacketListenerImpl.class)
25+
//#endif
1926
public class MixinServerGamePacketListenerImpl {
2027

21-
@Shadow public ServerPlayer player;
28+
//#if MC < 12002
29+
//$$ @Shadow public ServerPlayer player;
30+
//#endif
2231

2332
@Unique
2433
private static final Set<Class<?>> CAPTURED_PACKETS = Set.of(
@@ -34,14 +43,17 @@ public class MixinServerGamePacketListenerImpl {
3443
private void serverflashback$onSend(Packet<?> packet, CallbackInfo ci) {
3544
if (!CAPTURED_PACKETS.contains(packet.getClass())) return;
3645
if (!RecordingManager.getInstance().hasActiveRecordings()) return;
37-
3846
//#if MC >= 12002
39-
ServerLevel level = (ServerLevel) this.player.level();
47+
if (!((Object) this instanceof ServerGamePacketListenerImpl gameListener)) return;
48+
ServerLevel level = (ServerLevel) gameListener.player.level();
49+
RecordingManager.getInstance().onPositionedGamePacket(
50+
level, gameListener.player.getX(), gameListener.player.getZ(),
51+
(Packet<? super ClientGamePacketListener>) packet);
4052
//#else
4153
//$$ ServerLevel level = (ServerLevel) this.player.level;
54+
//$$ RecordingManager.getInstance().onPositionedGamePacket(
55+
//$$ level, this.player.getX(), this.player.getZ(),
56+
//$$ (Packet<? super ClientGamePacketListener>) packet);
4257
//#endif
43-
RecordingManager.getInstance().onPositionedGamePacket(
44-
level, this.player.getX(), this.player.getZ(),
45-
(Packet<? super ClientGamePacketListener>) packet);
4658
}
4759
}

src/main/java/cn/net/rms/serverflashback/mixin/MixinServerLevelRecording.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,9 @@ public class MixinServerLevelRecording {
5454
}
5555
}
5656

57-
@Inject(method = "playSeededSound(Lnet/minecraft/world/entity/Entity;Lnet/minecraft/world/entity/Entity;Lnet/minecraft/core/Holder;Lnet/minecraft/sounds/SoundSource;FFJ)V",
57+
@Inject(method = "playSeededSound(Lnet/minecraft/world/entity/player/Player;Lnet/minecraft/world/entity/Entity;Lnet/minecraft/core/Holder;Lnet/minecraft/sounds/SoundSource;FFJ)V",
5858
at = @At("HEAD"))
59-
private void serverflashback$onPlaySoundEntity(Entity source, Entity entity, Holder<SoundEvent> sound,
59+
private void serverflashback$onPlaySoundEntity(Player source, Entity entity, Holder<SoundEvent> sound,
6060
SoundSource soundSource, float volume, float pitch, long seed, CallbackInfo ci) {
6161
if (RecordingManager.getInstance().hasActiveRecordings()) {
6262
RecordingManager.getInstance().onEntitySound(

0 commit comments

Comments
 (0)