Skip to content

Port to mindustry 160.1, Turret, kill zone, hud,buy optimizations - #43

Draft
RE-AST wants to merge 7 commits into
Darkdustry-Coders:mainfrom
RE-AST:claude-6941a9be
Draft

RE-AST wants to merge 7 commits into
Darkdustry-Coders:mainfrom
RE-AST:claude-6941a9be

Conversation

@RE-AST

@RE-AST RE-AST commented Sep 12, 2026

Copy link
Copy Markdown

No description provided.

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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant