mirror of
https://github.com/LeavesMC/Leaves.git
synced 2025-12-19 14:59:32 +00:00
138 lines
4.6 KiB
Diff
138 lines
4.6 KiB
Diff
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: Lumine1909 <133463833+Lumine1909@users.noreply.github.com>
|
|
Date: Fri, 9 Aug 2024 04:25:11 +0800
|
|
Subject: [PATCH] Recorder API
|
|
|
|
|
|
diff --git a/src/main/java/org/leavesmc/leaves/recorder/RecorderAPI.java b/src/main/java/org/leavesmc/leaves/recorder/RecorderAPI.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..cf5d88ff2c63f13c7130a754eabe82650bb17948
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/leavesmc/leaves/recorder/RecorderAPI.java
|
|
@@ -0,0 +1,19 @@
|
|
+package org.leavesmc.leaves.recorder;
|
|
+
|
|
+import net.minecraft.server.level.ServerPlayer;
|
|
+
|
|
+import java.io.File;
|
|
+import java.util.HashMap;
|
|
+import java.util.Map;
|
|
+
|
|
+public class RecorderAPI {
|
|
+
|
|
+ private static Map<ServerPlayer, RecorderStream> recordingPlayer = new HashMap<>();
|
|
+
|
|
+ public static RecorderStream getOrCreateStream(ServerPlayer player, File file) {
|
|
+ if (!recordingPlayer.containsKey(player)) {
|
|
+ recordingPlayer.put(player, new RecorderStream(player, file));
|
|
+ }
|
|
+ return recordingPlayer.get(player);
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/leavesmc/leaves/recorder/RecorderStream.java b/src/main/java/org/leavesmc/leaves/recorder/RecorderStream.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..7ce3700a513dab8de2d6ddb75829e30df34fe876
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/leavesmc/leaves/recorder/RecorderStream.java
|
|
@@ -0,0 +1,100 @@
|
|
+package org.leavesmc.leaves.recorder;
|
|
+
|
|
+import io.netty.buffer.ByteBuf;
|
|
+import io.netty.channel.ChannelHandlerContext;
|
|
+import io.netty.channel.ChannelOutboundHandlerAdapter;
|
|
+import io.netty.channel.ChannelPromise;
|
|
+import net.minecraft.server.level.ServerPlayer;
|
|
+import net.minecraft.world.entity.player.Player;
|
|
+import org.leavesmc.leaves.replay.DigestOutputStream;
|
|
+
|
|
+import java.io.BufferedOutputStream;
|
|
+import java.io.DataOutputStream;
|
|
+import java.io.File;
|
|
+import java.io.FileOutputStream;
|
|
+import java.io.IOException;
|
|
+import java.util.concurrent.ExecutorService;
|
|
+import java.util.concurrent.Executors;
|
|
+import java.util.zip.CRC32;
|
|
+
|
|
+public class RecorderStream {
|
|
+
|
|
+ private class RecorderInterceptor extends ChannelOutboundHandlerAdapter {
|
|
+
|
|
+ @Override
|
|
+ public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
|
|
+ if (isRecording) {
|
|
+ savePacket(getCurrentTimeAndUpdate(), (ByteBuf) msg);
|
|
+ }
|
|
+ super.write(ctx, msg, promise);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private static final ExecutorService saveService = Executors.newVirtualThreadPerTaskExecutor();
|
|
+
|
|
+ private final ServerPlayer player;
|
|
+ private final DataOutputStream packetOutStream;
|
|
+
|
|
+ private boolean isRecording = false;
|
|
+ private boolean paused = false;
|
|
+ private boolean resumeOnNextPacket = true;
|
|
+
|
|
+ private long startTime;
|
|
+ private long lastPacket;
|
|
+ private long timeShift = 0;
|
|
+
|
|
+
|
|
+ RecorderStream(ServerPlayer player, File output) {
|
|
+ try {
|
|
+ this.player = player;
|
|
+ packetOutStream = new DataOutputStream(new DigestOutputStream(new BufferedOutputStream(new FileOutputStream(output)), new CRC32()));
|
|
+ } catch (IOException e) {
|
|
+ throw new RuntimeException(e);
|
|
+ }
|
|
+ player.connection.connection.channel.pipeline().addBefore("encoder", "recorder", new RecorderInterceptor());
|
|
+ }
|
|
+
|
|
+ private synchronized long getCurrentTimeAndUpdate() {
|
|
+ long now = getRecordedTime();
|
|
+ if (paused) {
|
|
+ if (resumeOnNextPacket) {
|
|
+ paused = false;
|
|
+ }
|
|
+ timeShift += now - lastPacket;
|
|
+ return lastPacket;
|
|
+ }
|
|
+ return lastPacket = now;
|
|
+ }
|
|
+
|
|
+ public long getRecordedTime() {
|
|
+ final long base = System.currentTimeMillis() - startTime;
|
|
+ return base - timeShift;
|
|
+ }
|
|
+
|
|
+ private void savePacket(long timestamp, ByteBuf packetbuf) {
|
|
+ saveService.submit(() -> {
|
|
+ byte[] data = packetbuf.array();
|
|
+ try {
|
|
+ packetOutStream.writeInt((int) timestamp);
|
|
+ packetOutStream.writeInt(data.length);
|
|
+ packetOutStream.write(data);
|
|
+ } catch (IOException e) {
|
|
+ throw new RuntimeException(e);
|
|
+ }
|
|
+ });
|
|
+ }
|
|
+
|
|
+ public void startRecording() {
|
|
+ isRecording = true;
|
|
+ startTime = System.currentTimeMillis();
|
|
+ }
|
|
+
|
|
+ public void stopRecording() {
|
|
+ isRecording = false;
|
|
+ }
|
|
+
|
|
+ public void close() throws IOException {
|
|
+ player.connection.connection.channel.pipeline().remove("encoder");
|
|
+ packetOutStream.close();
|
|
+ }
|
|
+}
|