Conversation
PlayerConnectionConfirmed called ModifyWorld.syncBuild(build) for every building in Groups.build. That overload broadcasts one blockSnapshot to every player, and blockSnapshot is an unreliable packet, so a full castle map pushed thousands of datagrams into the server's 41000-byte UDP write buffer in a single frame. On overflow arc.net.Server.sendToAllUDP closes the connection itself (con.close(DcReason.error)), which is why the joining player vanished ~1s after 'has connected' with no error in the server log -- the server only noticed later, as 'disappeared'. Now one batched ModifyWorld.syncBuilds(con, Groups.build) to the joining player only. build.gradle: jar.archivePath was removed in Gradle 9.
…, leaks CastleUtils.replaceTurretBullets: the loop ran 0..<ammo.size-1, so it never visited the last entry, and with a single entry the range was empty -- the whole else branch (clamp a lone ammo stack to 25) was dead code. Iterating backwards also makes remove() safe, which the old fixed bound was not. While that branch was dead it also called build.update() and build.updateTile(); BuildingComp.update() already is updateConsumption() + updateTile(), so enabling it as written would tick the building twice per frame out of band. Dropped both calls. totalAmmo is the ammo total, not 1. PlayerData: Player.con is @nullable (PlayerComp.java:46) -- three unguarded reads fixed. Main: PlayerLeave now drops the player's HoldTask; it held a Player reference and was only removed the next time the task happened to run. TeamData: ObjectMap<Team, TeamData> instead of a Seq with a linear find -- getData is called several times per player per HUD update, 10 times a second. PlayerData.update: read team() once, and each unit counter once instead of twice. Every counter walks the team's whole unit list, so this halves that work.
Cheats stop TurretBuild.useAmmo from spending anything (Turret.java:686), so the loaded ammo never drains and vanilla never lets another type in: acceptItem/acceptStack want room under maxAmmo, the liquid turrets want a nearly empty tank. replaceTurretBullets worked around that by sweeping every building on the map 10 times a second, clearing ammo stacks and broadcasting a block snapshot per turret it touched. TurretPatches replaces the build type of every ItemTurret, LiquidTurret and ContinuousLiquidTurret once at init, and handles it where it happens: a different ammo type is always accepted and drops what is loaded, the same type follows vanilla rules. Two things the sweep was also doing, kept on purpose: - the loaded amount stays one ammo unit below maxAmmo. A vanilla client runs acceptStack against its own copy before it will even send a transfer (InputHandler:2298) -- with a full magazine it drops the item on the ground instead. The old 25 was exactly this headroom for maxAmmo 30; it is now derived per block, since maxAmmo is 40 and 45 on some turrets. - syncBuild on a type swap and on a hand transfer. The client mirrors ammo only from block snapshots, which go out every 6 s (NetServer.blockSyncTime).
trim() keeps the magazine one ammo unit below maxAmmo so a vanilla client will hand over items, which also left that unit permanently free for block intake -- a conveyor kept feeding, one item per cycle, with nothing to stop it. acceptItem now stops at the same cap; acceptStack, the hand path, still sees the headroom.
The 0.1s interval ran the HUD update, every room's update() and a full Groups.unit scan. Money/HUD and room ticks do not need 10 Hz, and the border sweep does not need every unit on the map. - HUD, room updates and the out-of-bounds unit sweep move to the existing 1 Hz round interval; the 10 Hz interval keeps only unitOnBorderKill. - unitOnBorderKill asks the unit quadtree for the border band instead of walking Groups.unit, collecting hits into two reused Seqs because killing or moving a unit during the query mutates the tree. - CastleCommandAI.target() holds its air/ground filters in fields, so the two Boolf instances are singletons instead of a pair of allocations per unit per tick. - Rooms are indexed by tile in Main.roomsByTile, filled in Room.spawn() and cleared on reset, so a tap or a held click is one IntMap lookup instead of a linear scan over every room.
Moving the HUD into the 1 Hz loop made a purchase look like it did nothing for up to a second. Income and the round timer stay per second, the HUD gets its own 0.5s interval.
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.
No description provided.