Repository navigation
fix: 审查问题修复(换维度清理、流式音乐、网络线程、网格背压、粒子上限、JS 沙箱) - #5
Merged
Merged
Conversation
S2A carries an i32 count; a huge value pushed millions of particles in one call before tick trimmed them to MAX_PARTICLES, stalling the main thread or exhausting memory. Clamp the spawn count to MAX_PARTICLES.
Vanilla replaces the WorldClient when the dimension changes. Proxies switch servers with a Respawn into another dimension followed by the target one and the old server never sends ChunkUnload, so its chunks and entities stayed in memory and lingered as ghost terrain/entities. Reset the world and all per-world side tables (except the local player) and have the renderer drop every chunk mesh and in-flight mesh job.
Music went through the static-sound path: each new track was fully decoded to PCM on the main thread (multi-second stall for a 4-minute ogg) and the ~90 MB result was cached forever. Stream music via kira's StreamingSoundData (decoded on its own thread, never cached), skip caching clips longer than ~10 s and flush the effect cache once it exceeds ~64 MB of PCM.
…ad death - The network thread alternated a 10 ms timed read with draining the send queue, so every outbound packet (attacks, flying, transaction acks) waited up to 10 ms on a quiet connection. Split the socket into read and write halves: the writer blocks on the queue and writes immediately, the reader blocks on the socket. Dropping the handle shuts the socket down so both threads exit. - Reject frame lengths and zlib uncompressed lengths outside vanilla's 2097152 limit (negative lengths panicked with_capacity, huge ones grew the buffer or inflated unbounded) and validate list counts in 0x13/0x22/0x26 and team packets against the remaining packet size. - A panic on the reader thread is caught and sent as Disconnected, and pump_network treats a disconnected event channel as a lost connection instead of leaving the player frozen in the world.
- Each queued mesh job pins a 1-2 MB column snapshot and the job channel was unbounded: under a chunk burst the main thread kept submitting 40 sections a frame regardless of worker progress. Track in-flight jobs and stop taking ordinary dirty sections once the backlog reaches 16 per worker thread; they stay dirty for later frames. - A panic in build_section_mesh_neighborhood killed its worker and dropped the job. Catch it per job, log the section, count it and deliver an empty mesh; recover a poisoned queue lock instead of unwrapping it. - F3 shows the mesh queue depth and, if any, the failure count.
mouse_factor, fps_limit and the other derived getters took Settings by value, so every mouse event and frame cloned the whole struct (Vec/String fields included). Borrow instead and drop the clones at the call sites.
Applying extension block tints rebuilt and uploaded every loaded section synchronously on the main thread (upload_world), freezing the frame when tint mods reloaded in game. Install the tint table and mark all sections dirty instead, like the resource pack reload path.
…e copies - Recycling one of the 64 player skin rows left the old owner marked as requested, so it never got a row again and showed the default skin for the rest of the session. Keep decoded skins cached and give rows to the players drawn this frame (spawned players, skull owners, local player), recycling the least recently wanted row; a returning player gets its row back from the cache without a re-download. - Downloads ran on one new thread per uuid with no timeout. Use four shared worker threads and a 15 s ureq timeout. - Filter the roster by is_requested before copying the base64 textures property, instead of cloning every property every frame.
JS mod hooks run synchronously on the render thread with no interrupt handler or memory limit, so a while(true) or unbounded allocation in a mod froze or exhausted the client. Install a QuickJS interrupt handler that aborts a hook after 50 ms (2 s for top-level code at load); an aborted or out-of-memory hook panics so the manager disables the mod as it does for other faulting hooks. Enforce a 128 MiB heap cap through a counting allocator, since rquickjs ignores set_memory_limit with rust-alloc.
ChunkLayer kept freed slots as separate (vertex, index) pairs that never merged, so long flights fragmented pages until new 128 MB pages were created, and pages were never freed. Allocate each page's vertex and index ranges with a first-fit allocator that coalesces neighbours and rewinds the bump cursor, place sections on the lowest page with room, release trailing empty pages, and keep only one page on clear().
The 128 MiB heap cap was shared by every JS mod, so one mod leaking up to it made every other mod's allocations fail too. JsRuntime::load now consumes the runtime, and the manager creates one per mod: a leak is confined to the mod that leaks.
QuickJS raises out-of-memory as a catchable exception and the prelude wraps every handler in try/catch, so the string check on the dispatcher error almost never fired: a mod at its cap stayed enabled, spinning on failed allocations every hook. The allocator now records a refusal and JsPlugin::call disables the mod on it.
Records carry "stream": true in vanilla's sounds.json. They went through the static path, so every play decoded a multi-minute ogg on the main thread, and since such clips are now too long to cache, each play did it again. Honor the flag and stream those entries like music.
insert() frees a section's old slot before placing its new mesh. When that section was alone on the last page, the page was released and a new 128 MB page created right after, on every re-mesh that grew it. Release trailing empty pages from remove() only.
The per-packet clamp did not bound the total: pump_network drains every queued packet in one frame and only tick() trimmed the Vec, so many huge-count packets could still push millions of particles. spawn now evicts the oldest up front so the live set never exceeds MAX_PARTICLES.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
修复全量审查中的 extreme-1~12。验证:cargo test 全工作区通过(229+95+23+69+152)。没有在客户端里实际运行。