diff --git a/core/src/mindustry/ai/BlockIndexer.java b/core/src/mindustry/ai/BlockIndexer.java index 8bb8d66678..b55ca3571f 100644 --- a/core/src/mindustry/ai/BlockIndexer.java +++ b/core/src/mindustry/ai/BlockIndexer.java @@ -306,24 +306,26 @@ public class BlockIndexer{ return breturnArray; } - public void notifyBuildHealed(Building build){ - if(build.wasDamaged && !build.damaged() && damagedTiles[build.team.id] != null){ - damagedTiles[build.team.id].remove(build); - build.wasDamaged = false; + public void notifyHealthChanged(Building build){ + boolean damaged = build.damaged(); + + if(build.wasDamaged != damaged){ + if(damagedTiles[build.team.id] == null){ + damagedTiles[build.team.id] = new Seq<>(false); + } + + if(damaged){ + //is now damaged, add to array + damagedTiles[build.team.id].add(build); + }else{ + //no longer damaged, remove + damagedTiles[build.team.id].remove(build); + } + + build.wasDamaged = damaged; } } - public void notifyBuildDamaged(Building build){ - if(build.wasDamaged || !build.damaged()) return; - - if(damagedTiles[build.team.id] == null){ - damagedTiles[build.team.id] = new Seq<>(false); - } - - damagedTiles[build.team.id].add(build); - build.wasDamaged = true; - } - public void allBuildings(float x, float y, float range, Cons cons){ breturnArray.clear(); for(int i = 0; i < activeTeams.size; i++){ @@ -473,7 +475,7 @@ public class BlockIndexer{ data.turretTree.insert(tile.build); } - notifyBuildDamaged(tile.build); + notifyHealthChanged(tile.build); } if(blocksPresent != null){ diff --git a/core/src/mindustry/content/Liquids.java b/core/src/mindustry/content/Liquids.java index de478269af..347408fb34 100644 --- a/core/src/mindustry/content/Liquids.java +++ b/core/src/mindustry/content/Liquids.java @@ -44,7 +44,7 @@ public class Liquids{ gasColor = Color.valueOf("c1e8f5"); }}; - neoplasm = new CellLiquid("neoplasm", Color.valueOf("e05438")){{ + neoplasm = new CellLiquid("neoplasm", Color.valueOf("c33e2b")){{ heatCapacity = 0.4f; temperature = 0.54f; viscosity = 0.85f; @@ -53,8 +53,8 @@ public class Liquids{ hidden = true; spreadTarget = Liquids.water; - colorFrom = Color.valueOf("f98f4a"); - colorTo = Color.valueOf("9e172c"); + colorFrom = Color.valueOf("e8803f"); + colorTo = Color.valueOf("8c1225"); }}; arkycite = new Liquid("arkycite", Color.valueOf("84a94b")){{ diff --git a/core/src/mindustry/core/NetServer.java b/core/src/mindustry/core/NetServer.java index ecc69382d7..a8fd7cc37a 100644 --- a/core/src/mindustry/core/NetServer.java +++ b/core/src/mindustry/core/NetServer.java @@ -36,11 +36,12 @@ import static mindustry.Vars.*; public class NetServer implements ApplicationListener{ /** note that snapshots are compressed, so the max snapshot size here is above the typical UDP safe limit */ private static final int maxSnapshotSize = 800; - private static final int timerBlockSync = 0; - private static final float blockSyncTime = 60 * 6; + private static final int timerBlockSync = 0, timerHealthSync = 1; + private static final float blockSyncTime = 60 * 6, healthSyncTime = 30; private static final FloatBuffer fbuffer = FloatBuffer.allocate(20); private static final Writes dataWrites = new Writes(null); private static final IntSeq hiddenIds = new IntSeq(); + private static final IntSeq healthSeq = new IntSeq(maxSnapshotSize / 4 + 1); private static final Vec2 vector = new Vec2(); /** If a player goes away of their server-side coordinates by this distance, they get teleported back. */ private static final float correctDist = tilesize * 14f; @@ -96,7 +97,8 @@ public class NetServer implements ApplicationListener{ }; private boolean closing = false; - private Interval timer = new Interval(); + private Interval timer = new Interval(10); + private IntSet buildHealthChanged = new IntSet(); private ReusableByteOutStream writeBuffer = new ReusableByteOutStream(127); private Writes outputBuffer = new Writes(new DataOutputStream(writeBuffer)); @@ -867,6 +869,11 @@ public class NetServer implements ApplicationListener{ } } + /** Queues a building health update. This will be sent in a Call.buildHealthUpdate packet later. */ + public void buildHealthUpdate(Building build){ + buildHealthChanged.add(build.pos()); + } + /** Should only be used on the headless backend. */ public void openServer(){ try{ @@ -1050,6 +1057,34 @@ public class NetServer implements ApplicationListener{ if(Groups.player.size() > 0 && Core.settings.getBool("blocksync") && timer.get(timerBlockSync, blockSyncTime)){ writeBlockSnapshots(); } + + if(Groups.player.size() > 0 && buildHealthChanged.size > 0 && timer.get(timerHealthSync, healthSyncTime)){ + healthSeq.clear(); + + var iter = buildHealthChanged.iterator(); + while(iter.hasNext){ + int next = iter.next(); + var build = world.build(next); + + //pack pos + health into update list + if(build != null){ + healthSeq.add(next, Float.floatToRawIntBits(build.health)); + } + + //if size exceeds snapshot limit, send it out and begin building it up again + if(healthSeq.size * 4 >= maxSnapshotSize){ + Call.buildHealthUpdate(healthSeq); + healthSeq.clear(); + } + } + + //send any residual health updates + if(healthSeq.size > 0){ + Call.buildHealthUpdate(healthSeq); + } + + buildHealthChanged.clear(); + } }catch(IOException e){ Log.err(e); } diff --git a/core/src/mindustry/entities/comp/BuildingComp.java b/core/src/mindustry/entities/comp/BuildingComp.java index 6ec46ac271..9298853699 100644 --- a/core/src/mindustry/entities/comp/BuildingComp.java +++ b/core/src/mindustry/entities/comp/BuildingComp.java @@ -1762,13 +1762,13 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc, @MethodPriority(100) @Override public void heal(){ - indexer.notifyBuildHealed(self()); + healthChanged(); } @MethodPriority(100) @Override public void heal(float amount){ - indexer.notifyBuildHealed(self()); + healthChanged(); } @Override @@ -1779,7 +1779,7 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc, @Replace @Override public void kill(){ - Call.tileDestroyed(self()); + Call.buildDestroyed(self()); } @Replace @@ -1796,13 +1796,27 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc, damage = Damage.applyArmor(damage, block.armor) / dm; } - Call.tileDamage(self(), health - handleDamage(damage)); + //TODO handle this better on the client. + if(!net.client()){ + health -= handleDamage(damage); + } + + healthChanged(); if(health <= 0){ - Call.tileDestroyed(self()); + Call.buildDestroyed(self()); } } + public void healthChanged(){ + //server-side, health updates are batched. + if(net.server()){ + netServer.buildHealthUpdate(self()); + } + + indexer.notifyHealthChanged(self()); + } + @Override public double sense(LAccess sensor){ return switch(sensor){ diff --git a/core/src/mindustry/entities/part/RegionPart.java b/core/src/mindustry/entities/part/RegionPart.java index bef6a50091..9f0ddc5c7b 100644 --- a/core/src/mindustry/entities/part/RegionPart.java +++ b/core/src/mindustry/entities/part/RegionPart.java @@ -84,14 +84,14 @@ public class RegionPart extends DrawPart{ //can be null var region = drawRegion ? regions[Math.min(i, regions.length - 1)] : null; float sign = (i == 0 ? 1 : -1) * params.sideMultiplier; - Tmp.v1.set((x + mx) * sign, y + my).rotateRadExact((params.rotation - 90) * Mathf.degRad); + Tmp.v1.set((x + mx) * sign * Draw.xscl, (y + my) * Draw.yscl).rotateRadExact((params.rotation - 90) * Mathf.degRad); float rx = params.x + Tmp.v1.x, ry = params.y + Tmp.v1.y, rot = mr * sign + params.rotation - 90; - Draw.xscl = sign; + Draw.xscl *= sign; if(outline && drawRegion){ Draw.z(prevZ + outlineLayerOffset); @@ -115,7 +115,7 @@ public class RegionPart extends DrawPart{ Drawf.additive(heat, heatColor.write(Tmp.c1).a(heatProgress.getClamp(params) * heatColor.a), rx, ry, rot, turretShading ? Layer.turretHeat : Draw.z() + heatLayerOffset); } - Draw.xscl = 1f; + Draw.xscl *= sign; } Draw.z(z); diff --git a/core/src/mindustry/maps/SectorDamage.java b/core/src/mindustry/maps/SectorDamage.java index 4031c120a5..0582e86233 100644 --- a/core/src/mindustry/maps/SectorDamage.java +++ b/core/src/mindustry/maps/SectorDamage.java @@ -522,7 +522,7 @@ public class SectorDamage{ other.build.addPlan(false); other.remove(); }else{ - indexer.notifyBuildDamaged(other.build); + indexer.notifyHealthChanged(other.build); } }else if(other.solid() && !other.synthetic()){ //skip damage propagation through solid blocks diff --git a/core/src/mindustry/world/Tile.java b/core/src/mindustry/world/Tile.java index 416dee4404..f30a8be9d7 100644 --- a/core/src/mindustry/world/Tile.java +++ b/core/src/mindustry/world/Tile.java @@ -709,20 +709,22 @@ public class Tile implements Position, QuadTreeObject, Displayable{ } } - @Remote(called = Loc.server, unreliable = true) - public static void tileDamage(Building build, float health){ - if(build == null) return; - - build.health = health; - - if(build.damaged()){ - indexer.notifyBuildDamaged(build); - } - } - @Remote(called = Loc.server) - public static void tileDestroyed(Building build){ + public static void buildDestroyed(Building build){ if(build == null) return; build.killed(); } + + @Remote + public static void buildHealthUpdate(IntSeq buildings){ + for(int i = 0; i < buildings.size; i += 2){ + int pos = buildings.items[i]; + float health = Float.intBitsToFloat(buildings.items[i + 1]); + var build = world.build(pos); + if(build != null && build.health != health){ + build.health = health; + indexer.notifyHealthChanged(build); + } + } + } } diff --git a/core/src/mindustry/world/blocks/ConstructBlock.java b/core/src/mindustry/world/blocks/ConstructBlock.java index d52fe82dc0..850821c4f7 100644 --- a/core/src/mindustry/world/blocks/ConstructBlock.java +++ b/core/src/mindustry/world/blocks/ConstructBlock.java @@ -89,9 +89,7 @@ public class ConstructBlock extends Block{ } //make sure block indexer knows it's damaged - if(tile.build.damaged()){ - indexer.notifyBuildDamaged(tile.build); - } + indexer.notifyHealthChanged(tile.build); } //last builder was this local client player, call placed() diff --git a/gradle.properties b/gradle.properties index 828bd49f46..f6d423e133 100644 --- a/gradle.properties +++ b/gradle.properties @@ -25,4 +25,4 @@ org.gradle.caching=true #used for slow jitpack builds; TODO see if this actually works org.gradle.internal.http.socketTimeout=100000 org.gradle.internal.http.connectionTimeout=100000 -archash=89f5cbe7ab +archash=123fbf12b9 diff --git a/tools/src/mindustry/tools/Generators.java b/tools/src/mindustry/tools/Generators.java index b3ab557622..b5702a9945 100644 --- a/tools/src/mindustry/tools/Generators.java +++ b/tools/src/mindustry/tools/Generators.java @@ -285,9 +285,6 @@ public class Generators{ for(Block block : content.blocks()){ if(block.isAir() || block instanceof ConstructBlock || block instanceof OreBlock || block instanceof LegacyBlock) continue; - block.load(); - block.loadIcon(); - Seq toOutline = new Seq<>(); block.getRegionsToOutline(toOutline); @@ -481,6 +478,37 @@ public class Generators{ } }); + MultiPacker packer = new MultiPacker(){ + @Override + public void add(PageType type, String name, PixmapRegion region, int[] splits, int[] pads){ + String prefix = type == PageType.main ? "" : "../" + type.name() + "/"; + Log.info("@ | @x@", prefix + name, region.width, region.height); + //save(region.pixmap, prefix + name); + } + }; + + //TODO !!!!! currently just an experiment + + if(false) + generate("all-icons", () -> { + for(Seq arr : content.getContentMap()){ + for(Content cont : arr){ + if(cont instanceof UnlockableContent && !(cont instanceof Planet)){ + UnlockableContent unlock = (UnlockableContent)cont; + + if(unlock.generateIcons){ + try{ + unlock.createIcons(packer); + }catch(IllegalArgumentException e){ + Log.err(e); + Log.err("Skip: @", unlock.name); + } + } + } + } + } + }); + generate("unit-icons", () -> content.units().each(type -> { if(type.internal) return; //internal hidden units don't generate @@ -488,9 +516,6 @@ public class Generators{ try{ Unit sample = type.constructor.get(); - type.load(); - type.loadIcon(); - type.init(); Func outline = i -> i.outline(type.outlineColor, 3); Cons outliner = t -> { @@ -703,7 +728,6 @@ public class Generators{ generate("ore-icons", () -> { content.blocks().each(b -> b instanceof OreBlock, ore -> { - ore.load(); int shadowColor = Color.rgba8888(0, 0, 0, 0.3f); for(int i = 0; i < ore.variants; i++){ diff --git a/tools/src/mindustry/tools/ImagePacker.java b/tools/src/mindustry/tools/ImagePacker.java index 8990be1a79..74a287d083 100644 --- a/tools/src/mindustry/tools/ImagePacker.java +++ b/tools/src/mindustry/tools/ImagePacker.java @@ -79,6 +79,11 @@ public class ImagePacker{ return find(name); } + @Override + public PixmapRegion getPixmap(AtlasRegion region){ + return new PixmapRegion(get(region.name)); + } + @Override public boolean has(String s){ return cache.containsKey(s); @@ -88,6 +93,7 @@ public class ImagePacker{ Draw.scl = 1f / Core.atlas.find("scale_marker").width; Time.mark(); + Vars.content.load(); Generators.run(); Log.info("&ly[Generator]&lc Total time to generate: &lg@&lcms", Time.elapsed());