Skip to content

Commit d60b4f2

Browse files
committed
Merge remote-tracking branch 'origin/1.20.1' into 1.19.4
2 parents 4e92536 + 5b67590 commit d60b4f2

File tree

13 files changed

+192
-44
lines changed

13 files changed

+192
-44
lines changed

README.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,11 +155,15 @@ After you boot the server a new file will be generated in the path
155155
"player_recording_path": "./recordings/players",
156156
"max_file_size": "0GB",
157157
"restart_after_max_file_size": false,
158+
"include_compressed_in_status": true,
159+
"fixed_daylight_cycle": -1,
158160
"pause_unloaded_chunks": false,
159161
"pause_notify_players": true,
160162
"fix_carpet_bot_view_distance": false,
161163
"ignore_sound_packets": false,
162164
"ignore_light_packets": true,
165+
"ignore_chat_packets": false,
166+
"ignore_scoreboard_packets": false,
163167
"optimize_explosion_packets": true,
164168
"optimize_entity_packets": false,
165169
"player_predicate": {
@@ -181,9 +185,12 @@ After you boot the server a new file will be generated in the path
181185
| `"pause_notify_players"` | <p> If `pause_unloaded_chunks` is enabled and this is enabled then when the recording for the chunk area is paused or resumed all online players will be notified. </p> |
182186
| `"restart_after_max_file_size"` | <p> If a max file size is set and this limit is reached then the replay recording will automatically restart creating a new replay file. </p> |
183187
| `"include_compressed_in_status"` | <p> Includes the compressed file size of the replays when you do `/replay status`, for long replays this may cause the status message to take a while to be displayed, so you can disable it. </p> |
184-
| `"fix_carpet_bot_view_distance"` | <p> If you are recording carpet bots you want to enable this as it sets the view distance to the server view distance. Otherwise it will only record a distance of 2 chunks around the bot. |
188+
| `"fixed_daylight_cycle"` | <p> This fixes the daylight cycle in the replay if you do not want the constant day-night cycle in long timelapses. This should be set to the time of day in ticks, e.g. `6000` (midday). To disable the fixed daylight cycle set the value to `-1`. </p> |
189+
| `"fix_carpet_bot_view_distance"` | <p> If you are recording carpet bots you want to enable this as it sets the view distance to the server view distance. Otherwise it will only record a distance of 2 chunks around the bot. </p> |
185190
| `"ignore_sound_packets"` | <p> If you are recording a large area for a timelapse it's unlikely you'll want to record any sounds, these can eat up significant storage space. </p> |
186191
| `"ignore_light_packets"` | <p> Light is calculated on the client as well as on the server so light packets are mostly redundant. </p> |
192+
| `"ignore_chat_packets"` | <p> Stops chat packets (from both the server and other players) from being recorded if they are not necessary for your replay. </p> |
193+
| `"ignore_scoreboard_packets"` | <p> Stops scoreboard packets from being recorded (for example, if you have a scoreboard displaying digs then this will not appear, and player's scores will also not be recorded). </p> |
187194
| `"optimize_explosion_packets"` | <p> This reduces the file size greatly by not sending the client explosion packets instead just sending the explosion particles and sounds. </p> |
188195
| `"optimize_entity_packets"` | <p> This reduces the file size by letting the client handle the logic for some entities, e.g. projectiles and tnt. This may cause some inconsistencies however it will likely be negligible. </p> |
189196
| `"player_predicate"` | <p> The predicate for recording players automatically, more information in the [Predicates](#predicates-config) section. </p> |

build.gradle.kts

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ plugins {
22
kotlin("jvm")
33
kotlin("plugin.serialization") version "1.9.21"
44
id("me.modmuss50.mod-publish-plugin") version "0.4.5"
5+
id("com.github.johnrengelman.shadow") version "8.1.1"
56
id("fabric-loom")
67
`maven-publish`
78
java
@@ -10,6 +11,10 @@ plugins {
1011
group = property("maven_group")!!
1112
version = property("mod_version")!!
1213

14+
val releaseVersion = "${project.version}+mc${project.property("minecraft_version")}"
15+
16+
val shade by configurations.creating
17+
1318
repositories {
1419
maven {
1520
url = uri("https://maven.parchmentmc.org/")
@@ -43,8 +48,11 @@ dependencies {
4348
// I've had some issues with ReplayStudio and slf4j (in dev)
4449
// Simplest workaround that I've found is just to unzip the
4550
// jar and yeet the org.slf4j packages then rezip the jar.
46-
include(modImplementation("com.github.ReplayMod:ReplayStudio:6cd39b0874") {
51+
shade(modImplementation("com.github.ReplayMod:ReplayStudio:6cd39b0874") {
4752
exclude(group = "org.slf4j")
53+
exclude(group = "it.unimi.dsi")
54+
exclude(group = "org.apache.commons")
55+
exclude(group = "commons-cli")
4856
exclude(group = "com.google.guava", module = "guava-jdk5")
4957
exclude(group = "com.google.guava", module = "guava")
5058
exclude(group = "com.google.code.gson", module = "gson")
@@ -66,10 +74,6 @@ loom {
6674
}
6775
}
6876

69-
tasks.remapJar {
70-
archiveVersion.set("${project.version}+mc${project.property("minecraft_version")}")
71-
}
72-
7377
tasks {
7478
processResources {
7579
inputs.property("version", project.version)
@@ -78,15 +82,37 @@ tasks {
7882
}
7983
}
8084

81-
jar {
85+
remapJar {
86+
archiveVersion.set(releaseVersion)
87+
88+
inputFile.set(shadowJar.get().archiveFile)
89+
}
90+
91+
remapSourcesJar {
92+
archiveVersion.set(releaseVersion)
93+
}
94+
95+
shadowJar {
96+
destinationDirectory.set(File("./build/devlibs"))
97+
isZip64 = true
98+
8299
from("LICENSE")
100+
101+
relocate("com.github.steveice10", "shadow.server_replay.com.github.steveice10")
102+
configurations = listOf(shade)
103+
104+
archiveClassifier = "shaded"
83105
}
84106

85107
publishMods {
86108
file = remapJar.get().archiveFile
87109
changelog.set(
88110
"""
89-
- Port to 1.20.1
111+
- Added some extra meta-data to replay files (to help with debugging)
112+
- Added extra configurations:
113+
- `fixed_daylight_cycle` - allows you to set a fixed time of day for the recording
114+
- `ignore_chat_packets` - ignore all chat packets
115+
- `ignore_scoreboard_packets` - ignore all scoreboard packets
90116
""".trimIndent()
91117
)
92118
type = STABLE

gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,6 @@ permissions_version=0.3-SNAPSHOT
1515
fabric_version=0.87.2+1.19.4
1616
carpet_version=1.4.101
1717

18-
mod_version=1.0.4
18+
mod_version=1.0.5
1919

2020
org.gradle.jvmargs=-Xmx4000m
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package me.senseiwells.replay.api
2+
3+
import me.senseiwells.replay.chunk.ChunkRecorder
4+
import me.senseiwells.replay.player.PlayerRecorder
5+
6+
interface RejoinedPacketSender {
7+
fun recordAdditionalPlayerPackets(recorder: PlayerRecorder)
8+
9+
fun recordAdditionalChunkPackets(recorder: ChunkRecorder)
10+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package me.senseiwells.replay.api
2+
3+
object ReplaySenders {
4+
internal val senders = ArrayList<RejoinedPacketSender>()
5+
6+
fun addSender(sender: RejoinedPacketSender) {
7+
this.senders.add(sender)
8+
}
9+
}

src/main/kotlin/me/senseiwells/replay/chunk/ChunkRecorder.kt

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package me.senseiwells.replay.chunk
22

3+
import kotlinx.serialization.json.JsonElement
4+
import kotlinx.serialization.json.JsonPrimitive
35
import com.mojang.authlib.GameProfile
46
import me.senseiwells.replay.ServerReplay
57
import me.senseiwells.replay.mixin.chunk.WitherBossAccessor
@@ -20,6 +22,7 @@ import net.minecraft.world.entity.Entity
2022
import net.minecraft.world.entity.boss.wither.WitherBoss
2123
import net.minecraft.world.level.ChunkPos
2224
import net.minecraft.world.level.levelgen.Heightmap
25+
import org.apache.commons.lang3.builder.ToStringBuilder
2326
import org.jetbrains.annotations.ApiStatus.Internal
2427
import java.nio.file.Path
2528
import java.util.concurrent.CompletableFuture
@@ -101,6 +104,20 @@ class ChunkRecorder internal constructor(
101104
return super.getTimestamp() - this.totalPausedTime - this.getCurrentPause()
102105
}
103106

107+
override fun appendToStatus(builder: ToStringBuilder) {
108+
builder.append("chunks_world", this.chunks.level.dimension().location())
109+
builder.append("chunks_from", this.chunks.from)
110+
builder.append("chunks_to", this.chunks.to)
111+
}
112+
113+
override fun addMetadata(map: MutableMap<String, JsonElement>) {
114+
super.addMetadata(map)
115+
map["chunks_world"] = JsonPrimitive(this.chunks.level.dimension().location().toString())
116+
map["chunks_from"] = JsonPrimitive(this.chunks.from.toString())
117+
map["chunks_to"] = JsonPrimitive(this.chunks.to.toString())
118+
map["paused_time"] = JsonPrimitive(this.totalPausedTime)
119+
}
120+
104121
override fun canContinueRecording(): Boolean {
105122
return true
106123
}

src/main/kotlin/me/senseiwells/replay/commands/ReplayCommand.kt

Lines changed: 4 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import me.senseiwells.replay.chunk.ChunkRecorders
1414
import me.senseiwells.replay.config.ReplayConfig
1515
import me.senseiwells.replay.player.PlayerRecorders
1616
import me.senseiwells.replay.recorder.ReplayRecorder
17-
import me.senseiwells.replay.util.FileUtils
1817
import net.minecraft.commands.CommandSourceStack
1918
import net.minecraft.commands.Commands
2019
import net.minecraft.commands.SharedSuggestionProvider
@@ -23,8 +22,6 @@ import net.minecraft.commands.arguments.EntityArgument
2322
import net.minecraft.network.chat.Component
2423
import net.minecraft.server.level.ServerLevel
2524
import net.minecraft.world.level.ChunkPos
26-
import org.apache.commons.lang3.builder.StandardToStringStyle
27-
import org.apache.commons.lang3.builder.ToStringBuilder
2825
import java.util.concurrent.CompletableFuture
2926

3027
object ReplayCommand {
@@ -290,19 +287,12 @@ object ReplayCommand {
290287
}
291288

292289
private fun status(context: CommandContext<CommandSourceStack>): Int {
293-
val style = StandardToStringStyle().apply {
294-
fieldSeparator = ", "
295-
fieldNameValueSeparator = " = "
296-
isUseClassName = false
297-
isUseIdentityHashCode = false
298-
}
299-
300290
val builder = StringBuilder("ServerReplay is ")
301291
.append(if (ServerReplay.config.enabled) "enabled" else "disabled")
302292
.append("\n")
303293

304-
val players = this.getStatusFuture("Players", PlayerRecorders.all(), style)
305-
val chunks = this.getStatusFuture("Chunks", ChunkRecorders.all(), style)
294+
val players = this.getStatusFuture("Players", PlayerRecorders.all())
295+
val chunks = this.getStatusFuture("Chunks", ChunkRecorders.all())
306296

307297
CompletableFuture.runAsync {
308298
for (player in players) {
@@ -332,31 +322,13 @@ object ReplayCommand {
332322

333323
private fun getStatusFuture(
334324
type: String,
335-
recorders: Collection<ReplayRecorder>,
336-
style: StandardToStringStyle,
325+
recorders: Collection<ReplayRecorder>
337326
): List<CompletableFuture<String>> {
338327
if (recorders.isNotEmpty()) {
339328
val futures = ArrayList<CompletableFuture<String>>()
340329
futures.add(CompletableFuture.completedFuture("Currently Recording $type:\n"))
341330
for (recorder in recorders) {
342-
val seconds = recorder.getTotalRecordingTime() / 1000
343-
val hours = seconds / 3600
344-
val minutes = seconds % 3600 / 60
345-
val secs = seconds % 60
346-
val time = "%02d:%02d:%02d".format(hours, minutes, secs)
347-
348-
val sub = ToStringBuilder(recorder, style)
349-
.append("name", recorder.getName())
350-
.append("time", time)
351-
.append("raw", FileUtils.formatSize(recorder.getRawRecordingSize()))
352-
if (ServerReplay.config.includeCompressedReplaySizeInStatus) {
353-
val compressed = recorder.getCompressedRecordingSize()
354-
futures.add(compressed.thenApplyAsync {
355-
"${sub.append("compressed", FileUtils.formatSize(it))}\n"
356-
})
357-
} else {
358-
futures.add(CompletableFuture.completedFuture("$sub\n"))
359-
}
331+
futures.add(recorder.getStatusWithSize())
360332
}
361333
return futures
362334
}

src/main/kotlin/me/senseiwells/replay/config/ReplayConfig.kt

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,7 @@ import kotlinx.serialization.EncodeDefault.Mode
55
import kotlinx.serialization.ExperimentalSerializationApi
66
import kotlinx.serialization.SerialName
77
import kotlinx.serialization.Serializable
8-
import kotlinx.serialization.json.Json
9-
import kotlinx.serialization.json.decodeFromStream
10-
import kotlinx.serialization.json.encodeToStream
8+
import kotlinx.serialization.json.*
119
import me.senseiwells.replay.ServerReplay
1210
import me.senseiwells.replay.chunk.ChunkRecorders
1311
import me.senseiwells.replay.config.chunk.ChunkAreaConfig
@@ -59,6 +57,9 @@ class ReplayConfig {
5957
@SerialName("include_compressed_in_status")
6058
var includeCompressedReplaySizeInStatus = true
6159

60+
@SerialName("fixed_daylight_cycle")
61+
var fixedDaylightCycle = -1L
62+
6263
@SerialName("pause_unloaded_chunks")
6364
var skipWhenChunksUnloaded = false
6465
@SerialName("pause_notify_players")
@@ -69,6 +70,10 @@ class ReplayConfig {
6970
var ignoreSoundPackets = false
7071
@SerialName("ignore_light_packets")
7172
var ignoreLightPackets = true
73+
@SerialName("ignore_chat_packets")
74+
var ignoreChatPackets = false
75+
@SerialName("ignore_scoreboard_packets")
76+
var ignoreScoreboardPackets = false
7277
@SerialName("optimize_explosion_packets")
7378
var optimizeExplosionPackets = true
7479
@SerialName("optimize_entity_packets")
@@ -147,5 +152,9 @@ class ReplayConfig {
147152
ServerReplay.logger.error("Failed to serialize replay config", e)
148153
}
149154
}
155+
156+
internal fun toJson(config: ReplayConfig): JsonElement {
157+
return json.encodeToJsonElement(config)
158+
}
150159
}
151160
}

src/main/kotlin/me/senseiwells/replay/player/PlayerRecorder.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package me.senseiwells.replay.player
22

33
import com.mojang.authlib.GameProfile
4+
import kotlinx.serialization.json.JsonElement
5+
import kotlinx.serialization.json.JsonPrimitive
46
import me.senseiwells.replay.mixin.rejoin.ChunkMapAccessor
57
import me.senseiwells.replay.mixin.rejoin.TrackedEntityAccessor
68
import me.senseiwells.replay.recorder.ChunkSender
@@ -73,6 +75,11 @@ class PlayerRecorder internal constructor(
7375
this.record(ClientboundRemoveEntitiesPacket(player.id))
7476
}
7577

78+
override fun addMetadata(map: MutableMap<String, JsonElement>) {
79+
super.addMetadata(map)
80+
map["player_name"] = JsonPrimitive(this.profile.name)
81+
}
82+
7683
override fun canContinueRecording(): Boolean {
7784
return this.player != null
7885
}

src/main/kotlin/me/senseiwells/replay/player/PlayerRecorders.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@ package me.senseiwells.replay.player
22

33
import com.mojang.authlib.GameProfile
44
import me.senseiwells.replay.ServerReplay
5+
import me.senseiwells.replay.api.RejoinedPacketSender
56
import me.senseiwells.replay.recorder.ReplayRecorder
67
import me.senseiwells.replay.rejoin.RejoinedReplayPlayer
78
import net.minecraft.server.MinecraftServer
89
import net.minecraft.server.level.ServerPlayer
910
import java.util.*
1011
import java.util.concurrent.CompletableFuture
12+
import kotlin.collections.ArrayList
1113

1214
object PlayerRecorders {
1315
private val players = LinkedHashMap<UUID, PlayerRecorder>()

0 commit comments

Comments
 (0)