Repository navigation
Conversation
| } | ||
| } | ||
| val data = meta | ||
| val serverName = data?.string("serverName")?.takeIf { it.isNotBlank() } |
There was a problem hiding this comment.
🟠 [P1] Respect hidden server addresses in replay entries
Multiplayer serverName contains the actual address, which the new replay cards display even when the user has hidden server IPs. This can expose a private address while streaming. [Replay Mod’s viewer](https://github.com/ReplayMod/ReplayMod/blob/stable/src/main/java/com/replaymod/replay/gui/screen/GuiReplayViewer.java) uses the custom name when available and honors its IP-hiding setting.
| runCatching { | ||
| Files.createDirectories(folder) | ||
| atomicWrite(folder.resolve(name), data) | ||
| stale.forEach(Path::deleteIfExists) |
There was a problem hiding this comment.
🟠 [P1] Persist the replacement index before deleting captures
Evicted capture files are deleted before the new index is written. If that write fails or the process exits in between, the old index points to deleted captures and the new capture is unindexed.
| var kept = state.scenes - replaced.toSet() | ||
| var total = kept.sumOf { it.bytes } + data.size | ||
| val spare = kept.sortedWith(compareBy({ it.dataVersion == LobbySnapshot.dataVersion }, ::rank)).toMutableList() | ||
| while (total > MAX_TOTAL_BYTES && spare.isNotEmpty()) { |
There was a problem hiding this comment.
🟠 [P1] Reject an oversized capture before evicting the library
A snapshot larger than the 16 MiB budget causes the loop to evict every existing capture, then is admitted anyway. A sufficiently dense scene can therefore erase the capture library while still exceeding the limit.
| atomicWrite(indexFile, index.toByteArray(Charsets.UTF_8)) | ||
| }.onFailure { LOBBY_LOGGER.warn("Could not save a scene", it) } | ||
| } | ||
| return true |
There was a problem hiding this comment.
🟡 [P2] Report capture success only after the write succeeds
admit returns success immediately after queueing a daemon-thread write. The UI consequently says the scene was saved even if the write fails or the game exits before it runs; the capture disappears on the next launch.
| val custom = sameSpot.filter { it.renamed }.maxByOrNull { it.capturedAt } | ||
| replaced += sameFile | ||
| if (!candidate.manual) { | ||
| replaced += rivals.filter { near(it, candidate) } |
There was a problem hiding this comment.
🟡 [P2] Protect selected captures from automatic replacement
A higher-scoring automatic capture within 48 blocks can delete the backdrop the user selected and reset the selection to Automatic. The size-limit eviction also includes selected and manually saved captures.
| val to = target.coerceIn(0, list.size() - 1) | ||
| if (from == to) return | ||
| val step = if (to > from) 1 else -1 | ||
| while (from != to) { |
There was a problem hiding this comment.
🟡 [P2] Save server order once after dragging
On Minecraft 1.21.1, ServerList.swap() synchronously saves servers.dat. Dragging across a long list rewrites and backs up the file once per crossed entry on the render thread, then saves it again.
| } | ||
|
|
||
| val stale = subscribedPlayers.filter { it !in visible } | ||
| val stale = subscribedPlayers.filter { it !in visible && it !in watchedFriends } |
There was a problem hiding this comment.
🟡 [P2] Reserve cosmetic subscriptions for visible players
Watched friends remain subscribed while playing, including offline friends, and share the 512-player limit with visible players. On a large server or with a large friends list, those subscriptions can exhaust the limit and prevent nearby players from receiving cosmetic updates.
| val written = hooks.writtenTicks | ||
| val chunk = hooks.chunkTicks | ||
| if (written != null && chunk != null) { | ||
| runCatching { (written.getInt(recorder).toLong() + chunk.getInt(recorder).toLong()) * MILLIS_PER_TICK } |
There was a problem hiding this comment.
🔵 [P3] Use Flashback’s total tick count for recording duration
Flashback increments both counters for every recorded tick. Adding them makes the displayed duration too long and causes it to jump backwards when a chunk is saved.
|
|
||
| private fun hostedPlace(): Place { | ||
| val joined = P2PSessionManager.joinedSessionId | ||
| hosted?.let { (id, place) -> if (id == joined) return place } |
There was a problem hiding this comment.
🔵 [P3] Refresh hosted-world names after resolution
If the host’s name has not resolved when a hosted world is entered, the generic “Friend’s world” label is cached for that session ID. It remains in stats even after the name lookup completes.
| private fun CompoundTag.putLight(name: String, layer: DataLayer?) { | ||
| if (layer == null) return | ||
| val bytes = layer.data | ||
| if (bytes.all { it == bytes[0] }) { |
There was a problem hiding this comment.
🔵 [P3] Check both light nibbles before using fill encoding
Equal packed bytes do not necessarily represent equal light values: each byte contains two nibbles. A layer of repeated 0xf0 bytes is restored as uniform zero light, changing half its values.
Closes #261, closes #12, closes #14, closes #63, closes #236, closes #251