From 381522d489b14899a1721d7d7e2c41840ecfd37d Mon Sep 17 00:00:00 2001 From: senseiwells Date: Mon, 17 Jun 2024 23:25:04 +0100 Subject: [PATCH] Updated README and changelog --- README.md | 29 ++++++-- build.gradle.kts | 13 ++-- .../senseiwells/replay/config/ReplayConfig.kt | 74 ++++++++----------- .../replay/recorder/ReplayRecorder.kt | 2 +- 4 files changed, 62 insertions(+), 56 deletions(-) diff --git a/README.md b/README.md index aaafb18..5a1076e 100644 --- a/README.md +++ b/README.md @@ -71,13 +71,16 @@ By default, this will be in `./recordings/players//.mcpr`. This file can then be put in `./replay_recordings` on your client and be opened with replay mod. -An important note: if you are going to record carpet bots you most likely want to -enable `"fix_carpet_bot_view_distance"` in the config otherwise only an area of 2 chunks -around the carpet bot will be recorded. +> [!NOTE] +> If you are going to record carpet bots you most likely want to +> enable `"fix_carpet_bot_view_distance"` in the config otherwise only an area of 2 chunks +> around the carpet bot will be recorded. #### Chunks -> **IMPORTANT NOTE:** While the mod will record the chunks you specify, the Minecraft client will **not** render the outermost chunks. So to record an area of **visible** chunks, you must add one chunk to your border, e.g. recording a visible area from `-5, -5` to `5, 5` you must record between `-6, -6` and `6, 6`. +> [!NOTE] +> While the mod will record the chunks you specify, the Minecraft client will **not** render the outermost chunks. +> So to record an area of **visible** chunks, you must add one chunk to your border, e.g. recording a visible area from `-5, -5` to `5, 5` you must record between `-6, -6` and `6, 6`. To record an area of chunks on your server you can run `/replay start chunks from to in named `, for example: ``` @@ -94,8 +97,14 @@ Alternatively you can specify a chunk and a radius around it to be recorded `/re Chunk recorders are static and cannot move, they record the specified chunks. An important thing to note is that when the replay starts, the specified chunks -will be loaded (and generated if necessary). -However, after this the chunk recorder does not load the chunks. +will be loaded (and generated if necessary). +However, after this, the chunk recorder does not load the chunks. + +You can further configure this with the +`"chunk_recorder_load_radius"` setting, which will set a maximum initial radius +that the chunk recorder will load, any chunks outside this radius that are recorded +will need to be loaded 'naturally' to be recorded. + If the server stops, the replay will automatically stop and save. @@ -194,6 +203,7 @@ After you boot the server a new file will be generated in the path "recover_unsaved_replays": true, "include_compressed_in_status": true, "fixed_daylight_cycle": -1, + "chunk_recorder_load_radius": -1, "pause_unloaded_chunks": false, "pause_notify_players": true, "notify_admins_of_status": true, @@ -205,6 +215,8 @@ After you boot the server a new file will be generated in the path "optimize_explosion_packets": true, "optimize_entity_packets": false, "record_voice_chat": false, + "replay_viewer_pack_ip": null, + "replay_viewer_pack_port": 24464, "player_predicate": { "type": "none" }, @@ -226,6 +238,7 @@ After you boot the server a new file will be generated in the path | `"recover_unsaved_replays"` |

This tries to recover any unsaved replays, for example if your server crashes or stops before a replay is stopped or has finished saving, this does not guarantee that the replay will not be corrupt, but it will try to salvage what is available.

| | `"include_compressed_in_status"` |

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.

| | `"fixed_daylight_cycle"` |

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`.

| +| `"chunk_recorder_load_radius"` |

This sets the default chunk recorder loading radius, this is useful when you want to record a very large area and you don't want all of the recorded chunks to be loaded at once.

For example if you are recording a 13x13 chunk area, you could set the radius to 3, so the center-most 7x7 would be initially loaded, the rest of the chunks will then be recorded whenever they're 'naturally' loaded.

Set this to `-1` to load all chunks.

| | `"pause_unloaded_chunks"` |

If an area of chunks is being recorded and the area is unloaded and this is set to `true` then the replay will pause the recording until the chunks are loaded again.

If set to false the chunks will be recorded as if they were loaded.

| | `"pause_notify_players"` |

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.

| | `"notify_admins_of_status"` |

When enabled this will notify admins of when a replay starts, when a replay ends, and when a replay has finished saving, as well as any errors that occur.

| @@ -236,9 +249,11 @@ After you boot the server a new file will be generated in the path | `"ignore_scoreboard_packets"` |

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).

| | `"optimize_explosion_packets"` |

This reduces the file size greatly by not sending the client explosion packets instead just sending the explosion particles and sounds.

| | `"optimize_entity_packets"` |

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.

| +| `"replay_viewer_pack_ip"` |

This is required if your server uses custom server-side resource packs and you want to be able to view these packs in the server-side replay viewer.

This should contain the public ip address of your server. This also requires `"replay_viewer_pack_port"`.

| +| `"replay_viewer_pack_port"` |

This requires `"replay_viewer_pack_ip"`. This is the port you wish to host your server-side resource packs on, you must ensure that the port is open.

| | `"record_voice_chat"` |

This enables support for recording voice-chat if you have the [simple-voice-chat](https://github.com/henkelmax/simple-voice-chat) mod installed, when watching back the replay you must have [replay-voice-chat](https://github.com/henkelmax/replay-voice-chat) installed.

| | `"player_predicate"` |

The predicate for recording players automatically, more information in the [Predicates](#predicates-config) section.

| -| `"chunks"` |

The list of chunks to automatically record when the server starts, more information in the [Chunks](#chunks-config) section.

| +| `"chunks"` |

The list of chunks to automatically record when the server starts, more information in the [Chunks](#chunks-config) section.

| ### Chunks Config diff --git a/build.gradle.kts b/build.gradle.kts index 386caa9..3e114df 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -124,11 +124,14 @@ tasks { file = remapJar.get().archiveFile changelog.set( """ - - Added support for [simple-voice-chat](https://github.com/henkelmax/simple-voice-chat) - - Added new player predicate `"type": "is_fake"` to check whether a player is not a real player (e.g. carpet bot) - - Added `max_duration` that lets you specify a maximum duration for your replay - - Added `restart_after_max_duration` that lets you automatically restart the replay if the max duration limit is met - - Fixed a bug that would cause gradual server lag if `max_file_size` was set + - Added new Server Side Replay Viewer + - You can now view your replays completely server-side! + - Added the `/replay view` command + - When your replays finish saving a message will appear, if clicked you can view the replay + - Added new config "chunk_recorder_load_radius" which allows you to specify a maximum radius that will be initially loaded + - Fixes an error when VoiceChat was enabled + - Fixes compatability with ViaVersion + - Fixes compatability with VeryManyPlayers """.trimIndent() ) type = STABLE diff --git a/src/main/kotlin/me/senseiwells/replay/config/ReplayConfig.kt b/src/main/kotlin/me/senseiwells/replay/config/ReplayConfig.kt index 8512722..fbffb86 100644 --- a/src/main/kotlin/me/senseiwells/replay/config/ReplayConfig.kt +++ b/src/main/kotlin/me/senseiwells/replay/config/ReplayConfig.kt @@ -29,82 +29,70 @@ import kotlin.time.Duration @Serializable @OptIn(ExperimentalSerializationApi::class) -class ReplayConfig { +data class ReplayConfig( @SerialName("enabled") - var enabled: Boolean = false + var enabled: Boolean = false, @SerialName("debug") @EncodeDefault(Mode.NEVER) - var debug: Boolean = false - + var debug: Boolean = false, @SerialName("world_name") - var worldName: String = "World" + var worldName: String = "World", @SerialName("server_name") - var serverName: String = "Server" - + var serverName: String = "Server", @SerialName("chunk_recording_path") @Serializable(with = PathSerializer::class) - var chunkRecordingPath: Path = recordings.resolve("chunks") + var chunkRecordingPath: Path = recordings.resolve("chunks"), @SerialName("player_recording_path") @Serializable(with = PathSerializer::class) - var playerRecordingPath: Path = recordings.resolve("players") - + var playerRecordingPath: Path = recordings.resolve("players"), @SerialName("max_file_size") - var maxFileSize = FileSize("0GB") + var maxFileSize: FileSize = FileSize("0GB"), @SerialName("restart_after_max_file_size") - var restartAfterMaxFileSize = false - + var restartAfterMaxFileSize: Boolean = false, @SerialName("max_duration") @Serializable(with = DurationSerializer::class) - var maxDuration = Duration.ZERO + var maxDuration: Duration = Duration.ZERO, @SerialName("restart_after_max_duration") - var restartAfterMaxDuration = false - + var restartAfterMaxDuration: Boolean = false, @SerialName("recover_unsaved_replays") - var recoverUnsavedReplays = true - + var recoverUnsavedReplays: Boolean = true, @SerialName("include_compressed_in_status") - var includeCompressedReplaySizeInStatus = true - + var includeCompressedReplaySizeInStatus: Boolean = true, @SerialName("fixed_daylight_cycle") - var fixedDaylightCycle = -1L - + var fixedDaylightCycle: Long = -1L, @SerialName("chunk_recorder_load_radius") - var chunkRecorderLoadRadius = -1 + var chunkRecorderLoadRadius: Int = -1, @SerialName("pause_unloaded_chunks") - var skipWhenChunksUnloaded = false + var skipWhenChunksUnloaded: Boolean = false, @SerialName("pause_notify_players") - var notifyPlayersLoadingChunks = true + var notifyPlayersLoadingChunks: Boolean = true, @SerialName("notify_admins_of_status") - var notifyAdminsOfStatus = true + var notifyAdminsOfStatus: Boolean = true, @SerialName("fix_carpet_bot_view_distance") - var fixCarpetBotViewDistance = false + var fixCarpetBotViewDistance: Boolean = false, @SerialName("ignore_sound_packets") - var ignoreSoundPackets = false + var ignoreSoundPackets: Boolean = false, @SerialName("ignore_light_packets") - var ignoreLightPackets = true + var ignoreLightPackets: Boolean = true, @SerialName("ignore_chat_packets") - var ignoreChatPackets = false + var ignoreChatPackets: Boolean = false, @SerialName("ignore_scoreboard_packets") - var ignoreScoreboardPackets = false + var ignoreScoreboardPackets: Boolean = false, @SerialName("optimize_explosion_packets") - var optimizeExplosionPackets = true + var optimizeExplosionPackets: Boolean = true, @SerialName("optimize_entity_packets") - var optimizeEntityPackets = false - + var optimizeEntityPackets: Boolean = false, @SerialName("record_voice_chat") - var recordVoiceChat = false - + var recordVoiceChat: Boolean = false, @SerialName("replay_viewer_pack_ip") - var replayViewerPackIp: String? = null + var replayViewerPackIp: String? = null, @SerialName("replay_viewer_pack_port") - var replayViewerPackPort = 24464 - + var replayViewerPackPort: Int = 24464, @SerialName("player_predicate") - private var playerPredicate: ReplayPlayerPredicate = NonePredicate - + private var playerPredicate: ReplayPlayerPredicate = NonePredicate, @SerialName("chunks") - private val chunks: List = listOf() - + private val chunks: List = listOf(), +) { fun shouldRecordPlayer(context: ReplayPlayerContext): Boolean { return this.playerPredicate.shouldRecord(context) } diff --git a/src/main/kotlin/me/senseiwells/replay/recorder/ReplayRecorder.kt b/src/main/kotlin/me/senseiwells/replay/recorder/ReplayRecorder.kt index 47c5c5f..05276b0 100644 --- a/src/main/kotlin/me/senseiwells/replay/recorder/ReplayRecorder.kt +++ b/src/main/kotlin/me/senseiwells/replay/recorder/ReplayRecorder.kt @@ -414,7 +414,7 @@ abstract class ReplayRecorder( */ protected open fun addMetadata(map: MutableMap) { map["name"] = JsonPrimitive(this.getName()) - map["settings"] = ReplayConfig.toJson(ServerReplay.config) + map["settings"] = ReplayConfig.toJson(ServerReplay.config.copy(replayViewerPackIp = "hidden")) map["location"] = JsonPrimitive(this.location.pathString) map["time"] = JsonPrimitive(System.currentTimeMillis())