diff --git a/leaf-server/minecraft-patches/features/0109-Leaves-Protocol-Core.patch b/leaf-server/minecraft-patches/features/0109-Leaves-Protocol-Core.patch index cef35168..f5312be2 100644 --- a/leaf-server/minecraft-patches/features/0109-Leaves-Protocol-Core.patch +++ b/leaf-server/minecraft-patches/features/0109-Leaves-Protocol-Core.patch @@ -6,7 +6,7 @@ Subject: [PATCH] Leaves: Protocol Core Original license: GPLv3 Original project: https://github.com/LeavesMC/Leaves -Commit: aaa2323d60912b65b64c16e7583c5d55f8b4c7c9 +Commit: c581f1311edc4787b08fe31c5a9996ab9b369a7c diff --git a/net/minecraft/network/protocol/common/custom/CustomPacketPayload.java b/net/minecraft/network/protocol/common/custom/CustomPacketPayload.java index fb263fa1f30a7dfcb7ec2656abfb38e5fe88eac9..56fd1ed7ccaf96e7eedea60fbdbf7f934939d563 100644 diff --git a/leaf-server/paper-patches/features/0029-Leaves-Protocol-Core.patch b/leaf-server/paper-patches/features/0029-Leaves-Protocol-Core.patch index e25a79ba..aeb1e5d0 100644 --- a/leaf-server/paper-patches/features/0029-Leaves-Protocol-Core.patch +++ b/leaf-server/paper-patches/features/0029-Leaves-Protocol-Core.patch @@ -6,7 +6,7 @@ Subject: [PATCH] Leaves: Protocol Core Original license: GPLv3 Original project: https://github.com/LeavesMC/Leaves -Commit: aaa2323d60912b65b64c16e7583c5d55f8b4c7c9 +Commit: c581f1311edc4787b08fe31c5a9996ab9b369a7c diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java index f81c0fef5428ec6ec98c10e0b520496dc68f8961..01de5ccfe7e99936ae503f36d9abb02f4f134bc5 100644 diff --git a/leaf-server/src/main/java/org/leavesmc/leaves/protocol/core/invoker/AbstractInvokerHolder.java b/leaf-server/src/main/java/org/leavesmc/leaves/protocol/core/invoker/AbstractInvokerHolder.java index 1e974b1b..906b7264 100644 --- a/leaf-server/src/main/java/org/leavesmc/leaves/protocol/core/invoker/AbstractInvokerHolder.java +++ b/leaf-server/src/main/java/org/leavesmc/leaves/protocol/core/invoker/AbstractInvokerHolder.java @@ -4,6 +4,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.leavesmc.leaves.protocol.core.LeavesProtocol; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; @@ -65,6 +66,8 @@ public abstract class AbstractInvokerHolder { } else { return invoker.invoke(owner, args); } + } catch (InvocationTargetException e) { + throw new RuntimeException(e.getCause()); } catch (Exception e) { throw new RuntimeException(e); } diff --git a/leaf-server/src/main/java/org/leavesmc/leaves/replay/Recorder.java b/leaf-server/src/main/java/org/leavesmc/leaves/replay/Recorder.java index 8ab96394..629c5a86 100644 --- a/leaf-server/src/main/java/org/leavesmc/leaves/replay/Recorder.java +++ b/leaf-server/src/main/java/org/leavesmc/leaves/replay/Recorder.java @@ -26,14 +26,17 @@ import net.minecraft.network.protocol.configuration.ClientboundUpdateEnabledFeat import net.minecraft.network.protocol.game.ClientboundAddEntityPacket; import net.minecraft.network.protocol.game.ClientboundGameEventPacket; import net.minecraft.network.protocol.game.ClientboundPlayerChatPacket; +import net.minecraft.network.protocol.game.ClientboundPlayerPositionPacket; import net.minecraft.network.protocol.game.ClientboundSetTimePacket; import net.minecraft.network.protocol.game.ClientboundSystemChatPacket; +import net.minecraft.network.protocol.game.ClientboundTrackedWaypointPacket; import net.minecraft.network.protocol.login.ClientboundLoginFinishedPacket; import net.minecraft.server.MinecraftServer; import net.minecraft.server.RegistryLayer; import net.minecraft.server.packs.repository.KnownPack; import net.minecraft.tags.TagNetworkSerialization; import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.PositionMoveRotation; import net.minecraft.world.flag.FeatureFlags; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -41,23 +44,26 @@ import org.leavesmc.leaves.LeavesLogger; import java.io.File; import java.io.IOException; +import java.util.Collections; import java.util.List; import java.util.Optional; import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionException; -import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicBoolean; public class Recorder extends Connection { - public static final Executor saveService = Executors.newVirtualThreadPerTaskExecutor(); public static final LeavesLogger LOGGER = LeavesLogger.LOGGER; + public final ExecutorService saveService = Executors.newSingleThreadExecutor(); private final ReplayFile replayFile; private final ServerPhotographer photographer; private final RecorderOption recorderOption; private final RecordMetaData metaData; + private final AtomicBoolean isSaving = new AtomicBoolean(false); private boolean stopped = false; private boolean paused = false; @@ -68,7 +74,6 @@ public class Recorder extends Connection { private long timeShift = 0; private boolean isSaved; - private boolean isSaving; private ConnectionProtocol state = ConnectionProtocol.LOGIN; public Recorder(ServerPhotographer photographer, RecorderOption recorderOption, File replayFile) throws IOException { @@ -77,7 +82,7 @@ public class Recorder extends Connection { this.photographer = photographer; this.recorderOption = recorderOption; this.metaData = new RecordMetaData(); - this.replayFile = new ReplayFile(replayFile); + this.replayFile = new ReplayFile(replayFile, saveService); this.channel = new LocalChannel(); } @@ -93,6 +98,8 @@ public class Recorder extends Connection { this.savePacket(new ClientboundLoginFinishedPacket(photographer.getGameProfile()), ConnectionProtocol.LOGIN); this.startConfiguration(); + savePacket(ClientboundPlayerPositionPacket.of(photographer.getId(), PositionMoveRotation.of(photographer), Collections.emptySet())); + if (recorderOption.forceWeather != null) { setWeather(recorderOption.forceWeather); } @@ -170,40 +177,46 @@ public class Recorder extends Connection { @Override public void send(@NotNull Packet packet, @Nullable ChannelFutureListener callbacks, boolean flush) { - if (!stopped) { - if (packet instanceof BundlePacket packet1) { + if (stopped) { + return; + } + switch (packet) { + case BundlePacket packet1 -> { packet1.subPackets().forEach(subPacket -> send(subPacket, null)); return; } - - if (packet instanceof ClientboundAddEntityPacket packet1) { + case ClientboundAddEntityPacket packet1 -> { if (packet1.getType() == EntityType.PLAYER) { metaData.players.add(packet1.getUUID()); saveMetadata(); } } - - if (packet instanceof ClientboundDisconnectPacket) { + case ClientboundDisconnectPacket ignored -> { return; } - - if (recorderOption.forceDayTime != -1 && packet instanceof ClientboundSetTimePacket packet1) { - packet = new ClientboundSetTimePacket(packet1.dayTime(), recorderOption.forceDayTime, false); - } - - if (recorderOption.forceWeather != null && packet instanceof ClientboundGameEventPacket packet1) { - ClientboundGameEventPacket.Type type = packet1.getEvent(); - if (type == ClientboundGameEventPacket.START_RAINING || type == ClientboundGameEventPacket.STOP_RAINING || type == ClientboundGameEventPacket.RAIN_LEVEL_CHANGE || type == ClientboundGameEventPacket.THUNDER_LEVEL_CHANGE) { - return; - } - } - - if (recorderOption.ignoreChat && (packet instanceof ClientboundSystemChatPacket || packet instanceof ClientboundPlayerChatPacket)) { + case ClientboundTrackedWaypointPacket ignored -> { return; } - - savePacket(packet); + default -> { + } } + + if (recorderOption.forceDayTime != -1 && packet instanceof ClientboundSetTimePacket packet1) { + packet = new ClientboundSetTimePacket(packet1.dayTime(), recorderOption.forceDayTime, false); + } + + if (recorderOption.forceWeather != null && packet instanceof ClientboundGameEventPacket packet1) { + ClientboundGameEventPacket.Type type = packet1.getEvent(); + if (type == ClientboundGameEventPacket.START_RAINING || type == ClientboundGameEventPacket.STOP_RAINING || type == ClientboundGameEventPacket.RAIN_LEVEL_CHANGE || type == ClientboundGameEventPacket.THUNDER_LEVEL_CHANGE) { + return; + } + } + + if (recorderOption.ignoreChat && (packet instanceof ClientboundSystemChatPacket || packet instanceof ClientboundPlayerChatPacket)) { + return; + } + + savePacket(packet); } private void saveMetadata() { @@ -235,36 +248,23 @@ public class Recorder extends Connection { } public CompletableFuture saveRecording(File dest, boolean save) { - isSaved = true; - if (!isSaving) { - isSaving = true; - metaData.duration = (int) lastPacket; - return CompletableFuture.runAsync(() -> { - saveMetadata(); - boolean interrupted = false; - try { - if (save) { - replayFile.closeAndSave(dest); - } else { - replayFile.closeNotSave(); - } - } catch (IOException e) { - e.printStackTrace(); - throw new CompletionException(e); - } finally { - if (interrupted) { - Thread.currentThread().interrupt(); - } - } - }, runnable -> { - final Thread thread = new Thread(runnable, "Recording file save thread"); - thread.start(); - }); - } else { + if (!isSaving.compareAndSet(false, true)) { LOGGER.warning("saveRecording() called twice"); - return CompletableFuture.supplyAsync(() -> { - throw new IllegalStateException("saveRecording() called twice"); - }); + return CompletableFuture.failedFuture(new IllegalStateException("saveRecording() called twice")); } + isSaved = true; + metaData.duration = (int) lastPacket; + return CompletableFuture.runAsync(() -> { + try { + replayFile.saveMetaData(metaData); + if (save) { + replayFile.closeAndSave(dest); + } else { + replayFile.closeNotSave(); + } + } catch (IOException e) { + throw new CompletionException(e); + } + }, saveService); } } diff --git a/leaf-server/src/main/java/org/leavesmc/leaves/replay/ReplayFile.java b/leaf-server/src/main/java/org/leavesmc/leaves/replay/ReplayFile.java index 4c93b3f5..7d27c7da 100644 --- a/leaf-server/src/main/java/org/leavesmc/leaves/replay/ReplayFile.java +++ b/leaf-server/src/main/java/org/leavesmc/leaves/replay/ReplayFile.java @@ -34,12 +34,12 @@ import java.nio.file.Files; import java.util.List; import java.util.Map; import java.util.UUID; +import java.util.concurrent.ExecutorService; import java.util.zip.CRC32; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; import static org.leavesmc.leaves.replay.Recorder.LOGGER; -import static org.leavesmc.leaves.replay.Recorder.saveService; public class ReplayFile { @@ -59,8 +59,10 @@ public class ReplayFile { private final File metaFile; private final Map> protocols; + private final ExecutorService saveService; - public ReplayFile(@NotNull File name) throws IOException { + public ReplayFile(@NotNull File name, ExecutorService saveService) throws IOException { + this.saveService = saveService; this.tmpDir = new File(name.getParentFile(), name.getName() + ".tmp"); if (tmpDir.exists()) { if (!ReplayFile.deleteDir(tmpDir)) { diff --git a/leaf-server/src/main/java/org/leavesmc/leaves/replay/ServerPhotographer.java b/leaf-server/src/main/java/org/leavesmc/leaves/replay/ServerPhotographer.java index bb45d241..a1845ecc 100644 --- a/leaf-server/src/main/java/org/leavesmc/leaves/replay/ServerPhotographer.java +++ b/leaf-server/src/main/java/org/leavesmc/leaves/replay/ServerPhotographer.java @@ -55,6 +55,8 @@ public class ServerPhotographer extends ServerPlayer { GameProfile profile = new GameProfile(UUID.randomUUID(), state.id); ServerPhotographer photographer = new ServerPhotographer(server, world, profile); + photographer.absSnapTo(state.loc.x(), state.loc.y(), state.loc.z(), state.loc.getYaw(), state.loc.getPitch()); + photographer.recorder = new Recorder(photographer, state.option, new File("replay", state.id)); photographer.saveFile = new File("replay", state.id + ".mcpr"); photographer.createState = state; @@ -146,6 +148,10 @@ public class ServerPhotographer extends ServerPlayer { this.followPlayer = followPlayer; } + public ServerPlayer getFollowPlayer() { + return followPlayer; + } + public void setSaveFile(File saveFile) { this.saveFile = saveFile; }