Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/1.19.4' into 1.18.2
Browse files Browse the repository at this point in the history
# Conflicts:
#	src/main/kotlin/me/senseiwells/replay/rejoin/RejoinedReplayPlayer.kt
  • Loading branch information
senseiwells committed Feb 20, 2024
2 parents c52bb97 + ad757a2 commit a7c02f6
Show file tree
Hide file tree
Showing 13 changed files with 198 additions and 52 deletions.
9 changes: 8 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -155,11 +155,15 @@ After you boot the server a new file will be generated in the path
"player_recording_path": "./recordings/players",
"max_file_size": "0GB",
"restart_after_max_file_size": false,
"include_compressed_in_status": true,
"fixed_daylight_cycle": -1,
"pause_unloaded_chunks": false,
"pause_notify_players": true,
"fix_carpet_bot_view_distance": false,
"ignore_sound_packets": false,
"ignore_light_packets": true,
"ignore_chat_packets": false,
"ignore_scoreboard_packets": false,
"optimize_explosion_packets": true,
"optimize_entity_packets": false,
"player_predicate": {
Expand All @@ -181,9 +185,12 @@ After you boot the server a new file will be generated in the path
| `"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> |
| `"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> |
| `"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> |
| `"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. |
| `"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> |
| `"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> |
| `"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> |
| `"ignore_light_packets"` | <p> Light is calculated on the client as well as on the server so light packets are mostly redundant. </p> |
| `"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> |
| `"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> |
| `"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> |
| `"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> |
| `"player_predicate"` | <p> The predicate for recording players automatically, more information in the [Predicates](#predicates-config) section. </p> |
Expand Down
40 changes: 33 additions & 7 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ plugins {
kotlin("jvm")
kotlin("plugin.serialization") version "1.9.21"
id("me.modmuss50.mod-publish-plugin") version "0.4.5"
id("com.github.johnrengelman.shadow") version "8.1.1"
id("fabric-loom")
`maven-publish`
java
Expand All @@ -10,6 +11,10 @@ plugins {
group = property("maven_group")!!
version = property("mod_version")!!

val releaseVersion = "${project.version}+mc${project.property("minecraft_version")}"

val shade by configurations.creating

repositories {
maven {
url = uri("https://maven.parchmentmc.org/")
Expand Down Expand Up @@ -43,8 +48,11 @@ dependencies {
// I've had some issues with ReplayStudio and slf4j (in dev)
// Simplest workaround that I've found is just to unzip the
// jar and yeet the org.slf4j packages then rezip the jar.
include(modImplementation("com.github.ReplayMod:ReplayStudio:6cd39b0874") {
shade(modImplementation("com.github.ReplayMod:ReplayStudio:6cd39b0874") {
exclude(group = "org.slf4j")
exclude(group = "it.unimi.dsi")
exclude(group = "org.apache.commons")
exclude(group = "commons-cli")
exclude(group = "com.google.guava", module = "guava-jdk5")
exclude(group = "com.google.guava", module = "guava")
exclude(group = "com.google.code.gson", module = "gson")
Expand All @@ -66,10 +74,6 @@ loom {
}
}

tasks.remapJar {
archiveVersion.set("${project.version}+mc${project.property("minecraft_version")}")
}

tasks {
processResources {
inputs.property("version", project.version)
Expand All @@ -78,15 +82,37 @@ tasks {
}
}

jar {
remapJar {
archiveVersion.set(releaseVersion)

inputFile.set(shadowJar.get().archiveFile)
}

remapSourcesJar {
archiveVersion.set(releaseVersion)
}

shadowJar {
destinationDirectory.set(File("./build/devlibs"))
isZip64 = true

from("LICENSE")

relocate("com.github.steveice10", "shadow.server_replay.com.github.steveice10")
configurations = listOf(shade)

archiveClassifier = "shaded"
}

publishMods {
file = remapJar.get().archiveFile
changelog.set(
"""
- Port to 1.20.1
- Added some extra meta-data to replay files (to help with debugging)
- Added extra configurations:
- `fixed_daylight_cycle` - allows you to set a fixed time of day for the recording
- `ignore_chat_packets` - ignore all chat packets
- `ignore_scoreboard_packets` - ignore all scoreboard packets
""".trimIndent()
)
type = STABLE
Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,6 @@ permissions_version=0.3-SNAPSHOT
fabric_version=0.77.0+1.18.2
carpet_version=1.4.66

mod_version=1.0.4
mod_version=1.0.5

org.gradle.jvmargs=-Xmx4000m
10 changes: 10 additions & 0 deletions src/main/kotlin/me/senseiwells/replay/api/RejoinedPacketSender.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package me.senseiwells.replay.api

import me.senseiwells.replay.chunk.ChunkRecorder
import me.senseiwells.replay.player.PlayerRecorder

interface RejoinedPacketSender {
fun recordAdditionalPlayerPackets(recorder: PlayerRecorder)

fun recordAdditionalChunkPackets(recorder: ChunkRecorder)
}
9 changes: 9 additions & 0 deletions src/main/kotlin/me/senseiwells/replay/api/ReplaySenders.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package me.senseiwells.replay.api

object ReplaySenders {
internal val senders = ArrayList<RejoinedPacketSender>()

fun addSender(sender: RejoinedPacketSender) {
this.senders.add(sender)
}
}
17 changes: 17 additions & 0 deletions src/main/kotlin/me/senseiwells/replay/chunk/ChunkRecorder.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package me.senseiwells.replay.chunk

import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.JsonPrimitive
import com.mojang.authlib.GameProfile
import me.senseiwells.replay.ServerReplay
import me.senseiwells.replay.mixin.chunk.WitherBossAccessor
Expand All @@ -22,6 +24,7 @@ import net.minecraft.world.entity.boss.wither.WitherBoss
import net.minecraft.world.entity.player.Player
import net.minecraft.world.level.ChunkPos
import net.minecraft.world.level.levelgen.Heightmap
import org.apache.commons.lang3.builder.ToStringBuilder
import org.jetbrains.annotations.ApiStatus.Internal
import java.nio.file.Path
import java.util.concurrent.CompletableFuture
Expand Down Expand Up @@ -100,6 +103,20 @@ class ChunkRecorder internal constructor(
return super.getTimestamp() - this.totalPausedTime - this.getCurrentPause()
}

override fun appendToStatus(builder: ToStringBuilder) {
builder.append("chunks_world", this.chunks.level.dimension().location())
builder.append("chunks_from", this.chunks.from)
builder.append("chunks_to", this.chunks.to)
}

override fun addMetadata(map: MutableMap<String, JsonElement>) {
super.addMetadata(map)
map["chunks_world"] = JsonPrimitive(this.chunks.level.dimension().location().toString())
map["chunks_from"] = JsonPrimitive(this.chunks.from.toString())
map["chunks_to"] = JsonPrimitive(this.chunks.to.toString())
map["paused_time"] = JsonPrimitive(this.totalPausedTime)
}

override fun canContinueRecording(): Boolean {
return true
}
Expand Down
36 changes: 4 additions & 32 deletions src/main/kotlin/me/senseiwells/replay/commands/ReplayCommand.kt
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import me.senseiwells.replay.chunk.ChunkRecorders
import me.senseiwells.replay.config.ReplayConfig
import me.senseiwells.replay.player.PlayerRecorders
import me.senseiwells.replay.recorder.ReplayRecorder
import me.senseiwells.replay.util.FileUtils
import net.minecraft.commands.CommandSourceStack
import net.minecraft.commands.Commands
import net.minecraft.commands.SharedSuggestionProvider
Expand All @@ -23,8 +22,6 @@ import net.minecraft.commands.arguments.EntityArgument
import net.minecraft.network.chat.TextComponent
import net.minecraft.server.level.ServerLevel
import net.minecraft.world.level.ChunkPos
import org.apache.commons.lang3.builder.StandardToStringStyle
import org.apache.commons.lang3.builder.ToStringBuilder
import java.util.concurrent.CompletableFuture

object ReplayCommand {
Expand Down Expand Up @@ -290,19 +287,12 @@ object ReplayCommand {
}

private fun status(context: CommandContext<CommandSourceStack>): Int {
val style = StandardToStringStyle().apply {
fieldSeparator = ", "
fieldNameValueSeparator = " = "
isUseClassName = false
isUseIdentityHashCode = false
}

val builder = StringBuilder("ServerReplay is ")
.append(if (ServerReplay.config.enabled) "enabled" else "disabled")
.append("\n")

val players = this.getStatusFuture("Players", PlayerRecorders.all(), style)
val chunks = this.getStatusFuture("Chunks", ChunkRecorders.all(), style)
val players = this.getStatusFuture("Players", PlayerRecorders.all())
val chunks = this.getStatusFuture("Chunks", ChunkRecorders.all())

CompletableFuture.runAsync {
for (player in players) {
Expand Down Expand Up @@ -330,31 +320,13 @@ object ReplayCommand {

private fun getStatusFuture(
type: String,
recorders: Collection<ReplayRecorder>,
style: StandardToStringStyle,
recorders: Collection<ReplayRecorder>
): List<CompletableFuture<String>> {
if (recorders.isNotEmpty()) {
val futures = ArrayList<CompletableFuture<String>>()
futures.add(CompletableFuture.completedFuture("Currently Recording $type:\n"))
for (recorder in recorders) {
val seconds = recorder.getTotalRecordingTime() / 1000
val hours = seconds / 3600
val minutes = seconds % 3600 / 60
val secs = seconds % 60
val time = "%02d:%02d:%02d".format(hours, minutes, secs)

val sub = ToStringBuilder(recorder, style)
.append("name", recorder.getName())
.append("time", time)
.append("raw", FileUtils.formatSize(recorder.getRawRecordingSize()))
if (ServerReplay.config.includeCompressedReplaySizeInStatus) {
val compressed = recorder.getCompressedRecordingSize()
futures.add(compressed.thenApplyAsync {
"${sub.append("compressed", FileUtils.formatSize(it))}\n"
})
} else {
futures.add(CompletableFuture.completedFuture("$sub\n"))
}
futures.add(recorder.getStatusWithSize())
}
return futures
}
Expand Down
15 changes: 12 additions & 3 deletions src/main/kotlin/me/senseiwells/replay/config/ReplayConfig.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,7 @@ import kotlinx.serialization.EncodeDefault.Mode
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.decodeFromStream
import kotlinx.serialization.json.encodeToStream
import kotlinx.serialization.json.*
import me.senseiwells.replay.ServerReplay
import me.senseiwells.replay.chunk.ChunkRecorders
import me.senseiwells.replay.config.chunk.ChunkAreaConfig
Expand Down Expand Up @@ -59,6 +57,9 @@ class ReplayConfig {
@SerialName("include_compressed_in_status")
var includeCompressedReplaySizeInStatus = true

@SerialName("fixed_daylight_cycle")
var fixedDaylightCycle = -1L

@SerialName("pause_unloaded_chunks")
var skipWhenChunksUnloaded = false
@SerialName("pause_notify_players")
Expand All @@ -69,6 +70,10 @@ class ReplayConfig {
var ignoreSoundPackets = false
@SerialName("ignore_light_packets")
var ignoreLightPackets = true
@SerialName("ignore_chat_packets")
var ignoreChatPackets = false
@SerialName("ignore_scoreboard_packets")
var ignoreScoreboardPackets = false
@SerialName("optimize_explosion_packets")
var optimizeExplosionPackets = true
@SerialName("optimize_entity_packets")
Expand Down Expand Up @@ -147,5 +152,9 @@ class ReplayConfig {
ServerReplay.logger.error("Failed to serialize replay config", e)
}
}

internal fun toJson(config: ReplayConfig): JsonElement {
return json.encodeToJsonElement(config)
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package me.senseiwells.replay.player

import com.mojang.authlib.GameProfile
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.JsonPrimitive
import me.senseiwells.replay.mixin.rejoin.ChunkMapAccessor
import me.senseiwells.replay.mixin.rejoin.TrackedEntityAccessor
import me.senseiwells.replay.recorder.ChunkSender
Expand Down Expand Up @@ -69,6 +71,11 @@ class PlayerRecorder internal constructor(
this.record(ClientboundRemoveEntitiesPacket(player.id))
}

override fun addMetadata(map: MutableMap<String, JsonElement>) {
super.addMetadata(map)
map["player_name"] = JsonPrimitive(this.profile.name)
}

override fun canContinueRecording(): Boolean {
return this.player != null
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ package me.senseiwells.replay.player

import com.mojang.authlib.GameProfile
import me.senseiwells.replay.ServerReplay
import me.senseiwells.replay.api.RejoinedPacketSender
import me.senseiwells.replay.recorder.ReplayRecorder
import me.senseiwells.replay.rejoin.RejoinedReplayPlayer
import net.minecraft.server.MinecraftServer
import net.minecraft.server.level.ServerPlayer
import java.util.*
import java.util.concurrent.CompletableFuture
import kotlin.collections.ArrayList

object PlayerRecorders {
private val players = LinkedHashMap<UUID, PlayerRecorder>()
Expand Down
Loading

0 comments on commit a7c02f6

Please sign in to comment.