diff --git a/core/assets-raw/sprites/blocks/units/fabricator-top.png b/core/assets-raw/sprites/blocks/units/fabricator-top.png index 1a2301dfc1..7b0d16be3c 100644 Binary files a/core/assets-raw/sprites/blocks/units/fabricator-top.png and b/core/assets-raw/sprites/blocks/units/fabricator-top.png differ diff --git a/core/assets-raw/sprites/blocks/units/fabricator.png b/core/assets-raw/sprites/blocks/units/fabricator.png index 4e1478888a..7997bb60f3 100644 Binary files a/core/assets-raw/sprites/blocks/units/fabricator.png and b/core/assets-raw/sprites/blocks/units/fabricator.png differ diff --git a/core/src/mindustry/ClientLauncher.java b/core/src/mindustry/ClientLauncher.java index 23daaf01eb..9a14807ee4 100644 --- a/core/src/mindustry/ClientLauncher.java +++ b/core/src/mindustry/ClientLauncher.java @@ -8,7 +8,6 @@ import arc.graphics.*; import arc.graphics.g2d.*; import arc.math.*; import arc.util.*; -import arc.util.async.*; import mindustry.ai.*; import mindustry.core.*; import mindustry.ctype.*; @@ -18,7 +17,7 @@ import mindustry.gen.*; import mindustry.graphics.*; import mindustry.maps.*; import mindustry.mod.*; -import mindustry.net.Net; +import mindustry.net.*; import mindustry.ui.*; import static arc.Core.*; diff --git a/core/src/mindustry/ai/Pathfinder.java b/core/src/mindustry/ai/Pathfinder.java index 378a052ccd..8e78ccb284 100644 --- a/core/src/mindustry/ai/Pathfinder.java +++ b/core/src/mindustry/ai/Pathfinder.java @@ -5,7 +5,6 @@ import arc.func.*; import arc.math.geom.*; import arc.struct.*; import arc.util.*; -import arc.util.async.*; import mindustry.annotations.Annotations.*; import mindustry.content.*; import mindustry.core.*; diff --git a/core/src/mindustry/async/AsyncCore.java b/core/src/mindustry/async/AsyncCore.java index df07bda1ac..9987bc31bd 100644 --- a/core/src/mindustry/async/AsyncCore.java +++ b/core/src/mindustry/async/AsyncCore.java @@ -2,7 +2,7 @@ package mindustry.async; import arc.*; import arc.struct.*; -import arc.util.async.*; +import arc.util.*; import mindustry.game.EventType.*; import java.util.concurrent.*; diff --git a/core/src/mindustry/content/SectorPresets.java b/core/src/mindustry/content/SectorPresets.java index 71647cab35..6da5003b1c 100644 --- a/core/src/mindustry/content/SectorPresets.java +++ b/core/src/mindustry/content/SectorPresets.java @@ -110,6 +110,7 @@ public class SectorPresets{ onset = new SectorPreset("onset", erekir, 10){{ addStartingItems = true; + alwaysUnlocked = true; captureWave = 3; difficulty = 1; }}; diff --git a/core/src/mindustry/core/Renderer.java b/core/src/mindustry/core/Renderer.java index b613ca78fc..e035c57af6 100644 --- a/core/src/mindustry/core/Renderer.java +++ b/core/src/mindustry/core/Renderer.java @@ -12,7 +12,6 @@ import arc.math.geom.*; import arc.scene.ui.layout.*; import arc.struct.*; import arc.util.*; -import arc.util.async.*; import mindustry.*; import mindustry.content.*; import mindustry.game.EventType.*; diff --git a/core/src/mindustry/editor/MapGenerateDialog.java b/core/src/mindustry/editor/MapGenerateDialog.java index c1a25b4e6d..4c90d18f84 100644 --- a/core/src/mindustry/editor/MapGenerateDialog.java +++ b/core/src/mindustry/editor/MapGenerateDialog.java @@ -10,7 +10,6 @@ import arc.scene.ui.ImageButton.*; import arc.scene.ui.layout.*; import arc.struct.*; import arc.util.*; -import arc.util.async.*; import mindustry.game.*; import mindustry.gen.*; import mindustry.graphics.*; @@ -23,6 +22,8 @@ import mindustry.ui.dialogs.*; import mindustry.world.*; import mindustry.world.blocks.environment.*; +import java.util.concurrent.*; + import static mindustry.Vars.*; @SuppressWarnings("unchecked") @@ -36,8 +37,8 @@ public class MapGenerateDialog extends BaseDialog{ int scaling = mobile ? 3 : 1; Table filterTable; - AsyncExecutor executor = new AsyncExecutor(1); - AsyncResult result; + ExecutorService executor = Threads.executor(1); + Future result; boolean generating; long[] buffer1, buffer2; @@ -369,7 +370,10 @@ public class MapGenerateDialog extends BaseDialog{ void apply(){ if(result != null){ - result.get(); + //ignore errors yay + try{ + result.get(); + }catch(Exception e){} } buffer1 = null; diff --git a/core/src/mindustry/game/FogControl.java b/core/src/mindustry/game/FogControl.java index 29f2e37724..9a53f82565 100644 --- a/core/src/mindustry/game/FogControl.java +++ b/core/src/mindustry/game/FogControl.java @@ -1,6 +1,7 @@ package mindustry.game; import arc.*; +import arc.struct.Bits; import arc.struct.*; import arc.util.*; import mindustry.*; @@ -9,40 +10,48 @@ import mindustry.game.EventType.*; import mindustry.gen.*; import mindustry.io.SaveFileReader.*; import mindustry.io.*; +import mindustry.world.meta.*; import java.io.*; import static mindustry.Vars.*; +//TODO bitset + dynamic FoW public class FogControl implements CustomChunk{ private static volatile int ww, wh; - private static final int buildLight = 0; + private static final int staticUpdateInterval = 1000 / 25; //25 FPS + private static final Object notifyStatic = new Object(), notifyDynamic = new Object(); - private final Object sync = new Object(); - /** indexed by [team] [packed array tile pos] */ - private @Nullable boolean[][] fog; + /** indexed by team */ + private volatile @Nullable FogData[] fog; - private final LongSeq events = new LongSeq(); - private @Nullable Thread fogThread; + private final LongSeq staticEvents = new LongSeq(); + private final LongSeq dynamicEventQueue = new LongSeq(), unitEventQueue = new LongSeq(); + /** access must be synchronized; accessed from both threads */ + private final LongSeq dynamicEvents = new LongSeq(100); - private boolean read = false; + private @Nullable Thread staticFogThread; + private @Nullable Thread dynamicFogThread; + + private boolean read = false, justLoaded = false; public FogControl(){ Events.on(ResetEvent.class, e -> { - clear(); + stop(); }); Events.on(WorldLoadEvent.class, e -> { - clear(); + stop(); + justLoaded = true; ww = world.width(); wh = world.height(); - //all old buildings have light around them + //all old buildings have static light scheduled around them if(state.rules.fog && read){ for(var build : Groups.build){ - synchronized(events){ - events.add(FogEvent.get(build.tile.x, build.tile.y, buildLight + build.block.size, build.team.id)); + synchronized(staticEvents){ + staticEvents.add(FogEvent.get(build.tile.x, build.tile.y, build.block.size, build.team.id)); } } @@ -51,10 +60,15 @@ public class FogControl implements CustomChunk{ }); Events.on(TileChangeEvent.class, event -> { - if(state.rules.fog && event.tile.build != null && event.tile.isCenter()){ - synchronized(events){ + if(state.rules.fog && event.tile.build != null && event.tile.isCenter() && !event.tile.build.team.isAI() && event.tile.block().flags.contains(BlockFlag.hasFogRadius)){ + var data = data(event.tile.team()); + if(data != null){ + data.dynamicUpdated = true; + } + + synchronized(staticEvents){ //TODO event per team? - pushEvent(FogEvent.get(event.tile.x, event.tile.y, buildLight + event.tile.block().size, event.tile.build.team.id)); + pushEvent(FogEvent.get(event.tile.x, event.tile.y, event.tile.block().fogRadius, event.tile.build.team.id)); } } }); @@ -62,28 +76,41 @@ public class FogControl implements CustomChunk{ SaveVersion.addCustomChunk("fogdata", this); } - public @Nullable boolean[] getData(Team team){ - return fog == null ? null : fog[team.id]; + public @Nullable Bits getDiscovered(Team team){ + return fog == null || fog[team.id] == null ? null : fog[team.id].staticData; } - public boolean isCovered(Team team, int x, int y){ - var data = getData(team); + public boolean isVisible(Team team, int x, int y){ + if(!state.rules.fog) return true; + + var data = data(team); if(data == null || x < 0 || y < 0 || x >= ww || y >= wh) return false; - return !data[x + y * ww]; + return data.read.get(x + y * ww); } - void clear(){ + @Nullable FogData data(Team team){ + return fog == null || fog[team.id] == null ? null : fog[team.id]; + } + + void stop(){ fog = null; //I don't care whether the fog thread crashes here, it's about to die anyway - events.clear(); - if(fogThread != null){ - fogThread.interrupt(); - fogThread = null; + staticEvents.clear(); + if(staticFogThread != null){ + staticFogThread.interrupt(); + staticFogThread = null; + } + + dynamicEvents.clear(); + if(dynamicFogThread != null){ + dynamicFogThread.interrupt(); + dynamicFogThread = null; + Log.info("end dynamic fog"); } } void pushEvent(long event){ - events.add(event); + staticEvents.add(event); if(!headless && FogEvent.team(event) == Vars.player.team().id){ renderer.fog.handleEvent(event); } @@ -91,122 +118,213 @@ public class FogControl implements CustomChunk{ public void update(){ if(fog == null){ - fog = new boolean[256][]; + fog = new FogData[256]; } - if(fogThread == null && !net.client()){ - fogThread = new FogThread(); - fogThread.setDaemon(true); - fogThread.start(); + //TODO should it be clientside...? + if(staticFogThread == null && !net.client()){ + staticFogThread = new StaticFogThread(); + staticFogThread.setDaemon(true); + staticFogThread.start(); } + if(dynamicFogThread == null){ + dynamicFogThread = new DynamicFogThread(); + dynamicFogThread.setDaemon(true); + dynamicFogThread.start(); + } + + //TODO force update all fog on world load + + //TODO dynamic fog initialization + + //clear to prepare for queuing fog radius from units and buildings + dynamicEventQueue.clear(); + for(var team : state.teams.present){ + //AI teams do not have fog if(!team.team.isAI()){ + //separate for each team + unitEventQueue.clear(); - if(fog[team.team.id] == null){ - fog[team.team.id] = new boolean[world.width() * world.height()]; + FogData data = fog[team.team.id]; + + if(data == null){ + data = fog[team.team.id] = new FogData(); + data.dynamicUpdated = true; } - synchronized(events){ + synchronized(staticEvents){ //TODO slow? for(var unit : team.units){ int tx = unit.tileX(), ty = unit.tileY(), pos = tx + ty * ww; + long event = FogEvent.get(tx, ty, (int)unit.type.fogRadius, team.team.id); + + //always update the dynamic events, but only *flush* the results when necessary? + unitEventQueue.add(event); + if(unit.lastFogPos != pos){ - pushEvent(FogEvent.get(tx, ty, (int)unit.type.fogRadius, team.team.id)); + pushEvent(event); unit.lastFogPos = pos; + data.dynamicUpdated = true; } } } + + //if it's time for an update, flush *everything* onto the update queue + if(data.dynamicUpdated && Time.timeSinceMillis(data.lastDynamicMs) > staticUpdateInterval){ + data.dynamicUpdated = false; + data.lastDynamicMs = Time.millis(); + + //add building updates + for(var build : indexer.getFlagged(team.team, BlockFlag.hasFogRadius)){ + dynamicEventQueue.add(FogEvent.get(build.tileX(), build.tileY(), build.block.fogRadius, 0)); + } + + //add unit updates + dynamicEventQueue.addAll(unitEventQueue); + } + } + } + + if(dynamicEventQueue.size > 0){ + //flush unit events over when something happens + synchronized(dynamicEvents){ + dynamicEvents.clear(); + dynamicEvents.addAll(dynamicEventQueue); + } + dynamicEventQueue.clear(); + + //force update so visibility doesn't have a pop-in + if(justLoaded){ + updateDynamic(new Bits(256)); + justLoaded = false; + } + + //notify that it's time for rendering + //TODO this WILL block until it is done rendering, which is inherently problematic. + synchronized(notifyDynamic){ + notifyDynamic.notify(); } } //wake up, it's time to draw some circles - if(events.size > 0 && fogThread != null){ - synchronized(sync){ - sync.notify(); + if(staticEvents.size > 0 && staticFogThread != null){ + synchronized(notifyStatic){ + notifyStatic.notify(); } } } - public class FogThread extends Thread{ + class StaticFogThread extends Thread{ + + StaticFogThread(){ + super("StaticFogThread"); + } @Override public void run(){ while(true){ try{ - synchronized(sync){ + synchronized(notifyStatic){ try{ //wait until an event happens - sync.wait(); + notifyStatic.wait(); }catch(InterruptedException e){ //end thread return; } } - //I really don't like synchronizing here, but there should be some performance benefit at least - synchronized(events){ - int size = events.size; + //I really don't like synchronizing here, but there should be *some* performance benefit at least + synchronized(staticEvents){ + int size = staticEvents.size; for(int i = 0; i < size; i++){ - long event = events.items[i]; + long event = staticEvents.items[i]; int x = FogEvent.x(event), y = FogEvent.y(event), rad = FogEvent.radius(event), team = FogEvent.team(event); - var arr = fog[team]; - if(arr != null){ - circle(arr, x, y, rad); + var data = fog[team]; + if(data != null){ + circle(data.staticData, x, y, rad); } } - events.clear(); + staticEvents.clear(); } //ignore, don't want to crash this thread }catch(Exception e){} } } + } - void circle(boolean[] arr, int x, int y, int radius){ - int f = 1 - radius; - int ddFx = 1, ddFy = -2 * radius; - int px = 0, py = radius; + class DynamicFogThread extends Thread{ + final Bits cleared = new Bits(); - hline(arr, x, x, y + radius); - hline(arr, x, x, y - radius); - hline(arr, x - radius, x + radius, y); - - while(px < py){ - if(f >= 0){ - py--; - ddFy += 2; - f += ddFy; - } - px++; - ddFx += 2; - f += ddFx; - hline(arr, x - px, x + px, y + py); - hline(arr, x - px, x + px, y - py); - hline(arr, x - py, x + py, y + px); - hline(arr, x - py, x + py, y - px); - } + DynamicFogThread(){ + super("DynamicFogThread"); } - void hline(boolean[] arr, int x1, int x2, int y){ - if(y < 0 || y >= wh) return; - int tmp; + @Override + public void run(){ - if(x1 > x2){ - tmp = x1; - x1 = x2; - x2 = tmp; + while(true){ + try{ + synchronized(notifyDynamic){ + try{ + //wait until an event happens + notifyDynamic.wait(); + }catch(InterruptedException e){ + //end thread + return; + } + } + + updateDynamic(cleared); + + //ignore, don't want to crash this thread + }catch(Exception e){ + //log for debugging + e.printStackTrace(); + } } + } + } - if(x1 >= ww) return; - if(x2 < 0) return; + void updateDynamic(Bits cleared){ + cleared.clear(); - if(x1 < 0) x1 = 0; - if(x2 >= ww) x2 = ww - 1; - x2++; - int off = y * ww; + //ugly sync + synchronized(dynamicEvents){ + int size = dynamicEvents.size; - while(x1 != x2){ - arr[off + x1++] = true; + //draw step + for(int i = 0; i < size; i++){ + long event = dynamicEvents.items[i]; + int x = FogEvent.x(event), y = FogEvent.y(event), rad = FogEvent.radius(event), team = FogEvent.team(event); + + var data = fog[team]; + if(data != null){ + + //clear the buffer, since it is being re-drawn + if(!cleared.get(team)){ + cleared.set(team); + + data.write.clear(); + } + + circle(data.write, x, y, rad); + } + } + dynamicEvents.clear(); + } + + //swap step, no need for synchronization or anything + for(int i = 0; i < 256; i++){ + if(cleared.get(i)){ + var data = fog[i]; + + //swap buffers, flushing the data that was just drawn + Bits temp = data.read; + data.read = data.write; + data.write = temp; } } } @@ -225,15 +343,15 @@ public class FogControl implements CustomChunk{ for(int i = 0; i < 256; i++){ if(fog[i] != null){ stream.writeByte(i); - boolean[] data = fog[i]; + Bits data = fog[i].staticData; + int size = ww * wh; - int pos = 0, size = data.length; + int pos = 0; while(pos < size){ int consecutives = 0; - boolean cur = data[pos]; + boolean cur = data.get(pos); while(consecutives < 127 && pos < size){ - boolean next = data[pos]; - if(cur != next){ + if(cur != data.get(pos)){ break; } @@ -249,16 +367,21 @@ public class FogControl implements CustomChunk{ @Override public void read(DataInput stream) throws IOException{ - if(fog == null) fog = new boolean[256][]; + if(fog == null) fog = new FogData[256]; int teams = stream.readUnsignedByte(); int w = stream.readShort(), h = stream.readShort(); int len = w * h; + ww = w; + wh = h; + for(int ti = 0; ti < teams; ti++){ int team = stream.readUnsignedByte(); + fog[team] = new FogData(); + int pos = 0; - boolean[] bools = fog[team] = new boolean[w * h]; + Bits bools = fog[team].staticData; while(pos < len){ int data = stream.readByte() & 0xff; @@ -266,9 +389,9 @@ public class FogControl implements CustomChunk{ int consec = data & 0b0111_1111; if(sign){ - for(int i = 0; i < consec; i++){ - bools[pos ++] = true; - } + //TODO disabled for testing? + //bools.set(pos, pos + consec); + pos += consec; }else{ pos += consec; } @@ -284,6 +407,72 @@ public class FogControl implements CustomChunk{ return state.rules.fog && fog != null; } + static void circle(Bits arr, int x, int y, int radius){ + int f = 1 - radius; + int ddFx = 1, ddFy = -2 * radius; + int px = 0, py = radius; + + hline(arr, x, x, y + radius); + hline(arr, x, x, y - radius); + hline(arr, x - radius, x + radius, y); + + while(px < py){ + if(f >= 0){ + py--; + ddFy += 2; + f += ddFy; + } + px++; + ddFx += 2; + f += ddFx; + hline(arr, x - px, x + px, y + py); + hline(arr, x - px, x + px, y - py); + hline(arr, x - py, x + py, y + px); + hline(arr, x - py, x + py, y - px); + } + } + + static void hline(Bits arr, int x1, int x2, int y){ + if(y < 0 || y >= wh) return; + int tmp; + + if(x1 > x2){ + tmp = x1; + x1 = x2; + x2 = tmp; + } + + if(x1 >= ww) return; + if(x2 < 0) return; + + if(x1 < 0) x1 = 0; + if(x2 >= ww) x2 = ww - 1; + x2++; + int off = y * ww; + + arr.set(off + x1, off + x2); + } + + static class FogData{ + /** dynamic double-buffered data for dynamic (live) coverage */ + volatile Bits read, write; + /** static map exploration fog*/ + final Bits staticData; + + /** last dynamic update timestamp. */ + long lastDynamicMs = 0; + /** if true, a dynamic fog update must be scheduled. */ + boolean dynamicUpdated; + + FogData(){ + int len = ww * wh; + + read = new Bits(len); + write = new Bits(len); + staticData = new Bits(len); + } + } + @Struct class FogEventStruct{ @StructField(16) diff --git a/core/src/mindustry/game/Saves.java b/core/src/mindustry/game/Saves.java index 870d5a9634..dfb704ed3a 100644 --- a/core/src/mindustry/game/Saves.java +++ b/core/src/mindustry/game/Saves.java @@ -6,7 +6,6 @@ import arc.files.*; import arc.graphics.*; import arc.struct.*; import arc.util.*; -import arc.util.async.*; import mindustry.*; import mindustry.core.GameState.*; import mindustry.game.EventType.*; @@ -18,6 +17,7 @@ import mindustry.type.*; import java.io.*; import java.text.*; import java.util.*; +import java.util.concurrent.*; import static mindustry.Vars.*; @@ -25,7 +25,7 @@ public class Saves{ Seq saves = new Seq<>(); @Nullable SaveSlot current; private @Nullable SaveSlot lastSectorSave; - AsyncExecutor previewExecutor = new AsyncExecutor(1); + ExecutorService previewExecutor = Threads.executor(1); private boolean saving; private float time; diff --git a/core/src/mindustry/graphics/FogRenderer.java b/core/src/mindustry/graphics/FogRenderer.java index d831efb195..9de4b25b44 100644 --- a/core/src/mindustry/graphics/FogRenderer.java +++ b/core/src/mindustry/graphics/FogRenderer.java @@ -17,7 +17,7 @@ import static mindustry.Vars.*; /** Highly experimental fog-of-war renderer. */ public class FogRenderer{ - private FrameBuffer buffer = new FrameBuffer(); + private FrameBuffer staticFog = new FrameBuffer(); private LongSeq events = new LongSeq(); private Rect rect = new Rect(); private @Nullable Team lastTeam; @@ -33,16 +33,16 @@ public class FogRenderer{ events.add(event); } - public Texture getTexture(){ - return buffer.getTexture(); + public Texture getStaticTexture(){ + return staticFog.getTexture(); } public void drawFog(){ //there is no fog. - if(fogControl.getData(player.team()) == null) return; + if(fogControl.getDiscovered(player.team()) == null) return; //resize if world size changes - boolean clear = buffer.resizeCheck(world.width(), world.height()); + boolean clear = staticFog.resizeCheck(world.width(), world.height()); if(player.team() != lastTeam){ copyFromCpu(); @@ -53,16 +53,16 @@ public class FogRenderer{ //grab events if(clear || events.size > 0){ //set projection to whole map - Draw.proj(0, 0, buffer.getWidth(), buffer.getHeight()); + Draw.proj(0, 0, staticFog.getWidth(), staticFog.getHeight()); //if the buffer resized, it contains garbage now, clear it. if(clear){ - buffer.begin(Color.black); + staticFog.begin(Color.black); }else{ - buffer.begin(); + staticFog.begin(); } - ScissorStack.push(rect.set(1, 1, buffer.getWidth() - 2, buffer.getHeight() - 2)); + ScissorStack.push(rect.set(1, 1, staticFog.getWidth() - 2, staticFog.getHeight() - 2)); Draw.color(Color.white); @@ -80,29 +80,30 @@ public class FogRenderer{ events.clear(); - buffer.end(); + staticFog.end(); ScissorStack.pop(); Draw.proj(Core.camera); } - buffer.getTexture().setFilter(TextureFilter.linear); + staticFog.getTexture().setFilter(TextureFilter.linear); Draw.shader(Shaders.fog); - Draw.fbo(buffer.getTexture(), world.width(), world.height(), tilesize); + Draw.fbo(staticFog.getTexture(), world.width(), world.height(), tilesize); Draw.shader(); } public void copyFromCpu(){ - buffer.resize(world.width(), world.height()); - buffer.begin(Color.black); - Draw.proj(0, 0, buffer.getWidth(), buffer.getHeight()); + staticFog.resize(world.width(), world.height()); + staticFog.begin(Color.black); + Draw.proj(0, 0, staticFog.getWidth(), staticFog.getHeight()); Draw.color(); int ww = world.width(), wh = world.height(); - boolean[] data = fogControl.getData(player.team()); + var data = fogControl.getDiscovered(player.team()); + int len = world.width() * world.height(); if(data != null){ - for(int i = 0; i < data.length; i++){ - if(data[i]){ + for(int i = 0; i < len; i++){ + if(data.get(i)){ //TODO slow, could do scanlines instead at the very least. int x = i % ww, y = i / ww; @@ -114,7 +115,7 @@ public class FogRenderer{ } } - buffer.end(); + staticFog.end(); Draw.proj(Core.camera); } diff --git a/core/src/mindustry/graphics/MinimapRenderer.java b/core/src/mindustry/graphics/MinimapRenderer.java index c445842fe6..18e084f76f 100644 --- a/core/src/mindustry/graphics/MinimapRenderer.java +++ b/core/src/mindustry/graphics/MinimapRenderer.java @@ -144,9 +144,9 @@ public class MinimapRenderer{ zoom = z; } Draw.shader(Shaders.fog); - renderer.fog.getTexture().setFilter(TextureFilter.nearest); + renderer.fog.getStaticTexture().setFilter(TextureFilter.nearest); //crisp pixels - Tmp.tr1.set(renderer.fog.getTexture()); + Tmp.tr1.set(renderer.fog.getStaticTexture()); Tmp.tr1.set(region.u, 1f - region.v, region.u2, 1f - region.v2); Draw.rect(Tmp.tr1, x + w/2f, y + h/2f, w, h); Draw.shader(); diff --git a/core/src/mindustry/maps/Maps.java b/core/src/mindustry/maps/Maps.java index ba738767c6..27028e98a9 100644 --- a/core/src/mindustry/maps/Maps.java +++ b/core/src/mindustry/maps/Maps.java @@ -9,7 +9,6 @@ import arc.graphics.*; import arc.struct.IntSet.*; import arc.struct.*; import arc.util.*; -import arc.util.async.*; import arc.util.io.*; import arc.util.serialization.*; import mindustry.*; diff --git a/core/src/mindustry/mod/Mods.java b/core/src/mindustry/mod/Mods.java index d496a9f998..8c24c4d0ff 100644 --- a/core/src/mindustry/mod/Mods.java +++ b/core/src/mindustry/mod/Mods.java @@ -11,7 +11,6 @@ import arc.graphics.g2d.TextureAtlas.*; import arc.scene.ui.*; import arc.struct.*; import arc.util.*; -import arc.util.async.*; import arc.util.io.*; import arc.util.serialization.*; import arc.util.serialization.Jval.*; @@ -27,13 +26,14 @@ import mindustry.ui.*; import java.io.*; import java.util.*; +import java.util.concurrent.*; import static mindustry.Vars.*; public class Mods implements Loadable{ private static final String[] metaFiles = {"mod.json", "mod.hjson", "plugin.json", "plugin.hjson"}; - private AsyncExecutor async = new AsyncExecutor(); + private ExecutorService async = Threads.executor(); private Json json = new Json(); private @Nullable Scripts scripts; private ContentParser parser = new ContentParser(); @@ -128,7 +128,7 @@ public class Mods implements Loadable{ packer = new MultiPacker(); //all packing tasks to await - var tasks = new Seq>(); + var tasks = new Seq>(); eachEnabled(mod -> { Seq sprites = mod.root.child("sprites").findAll(f -> f.extension().equals("png")); @@ -179,7 +179,7 @@ public class Mods implements Loadable{ } } - private void packSprites(Seq sprites, LoadedMod mod, boolean prefix, Seq> tasks){ + private void packSprites(Seq sprites, LoadedMod mod, boolean prefix, Seq> tasks){ boolean linear = Core.settings.getBool("linear", true); for(Fi file : sprites){ diff --git a/core/src/mindustry/net/ArcNetProvider.java b/core/src/mindustry/net/ArcNetProvider.java index 907ec63920..f20f190c34 100644 --- a/core/src/mindustry/net/ArcNetProvider.java +++ b/core/src/mindustry/net/ArcNetProvider.java @@ -8,7 +8,6 @@ import arc.net.FrameworkMessage.*; import arc.struct.*; import arc.util.*; import arc.util.Log.*; -import arc.util.async.*; import arc.util.io.*; import mindustry.net.Net.*; import mindustry.net.Packets.*; @@ -25,7 +24,7 @@ import static mindustry.Vars.*; public class ArcNetProvider implements NetProvider{ final Client client; final Prov packetSupplier = () -> new DatagramPacket(new byte[512], 512); - final AsyncExecutor executor = new AsyncExecutor(Math.max(Runtime.getRuntime().availableProcessors(), 6)); + final ExecutorService executor = Threads.executor(Math.max(Runtime.getRuntime().availableProcessors(), 6)); final Server server; final CopyOnWriteArrayList connections = new CopyOnWriteArrayList<>(); diff --git a/core/src/mindustry/net/BeControl.java b/core/src/mindustry/net/BeControl.java index 3f4a7faa76..817f2d6bcb 100644 --- a/core/src/mindustry/net/BeControl.java +++ b/core/src/mindustry/net/BeControl.java @@ -4,7 +4,6 @@ import arc.*; import arc.files.*; import arc.func.*; import arc.util.*; -import arc.util.async.*; import arc.util.serialization.*; import mindustry.*; import mindustry.core.*; @@ -18,6 +17,7 @@ import mindustry.ui.dialogs.*; import java.io.*; import java.net.*; +import java.util.concurrent.*; import static mindustry.Vars.*; @@ -25,7 +25,7 @@ import static mindustry.Vars.*; public class BeControl{ private static final int updateInterval = 60; - private AsyncExecutor executor = new AsyncExecutor(1); + private ExecutorService executor = Threads.executor(1); private boolean checkUpdates = true; private boolean updateAvailable; private String updateUrl; diff --git a/core/src/mindustry/net/Net.java b/core/src/mindustry/net/Net.java index 2463c8fed9..d4dbf72b0b 100644 --- a/core/src/mindustry/net/Net.java +++ b/core/src/mindustry/net/Net.java @@ -5,7 +5,6 @@ import arc.func.*; import arc.net.*; import arc.struct.*; import arc.util.*; -import arc.util.async.*; import mindustry.gen.*; import mindustry.net.Packets.*; import mindustry.net.Streamable.*; diff --git a/core/src/mindustry/ui/dialogs/PlanetDialog.java b/core/src/mindustry/ui/dialogs/PlanetDialog.java index 0e93173719..83124bbbde 100644 --- a/core/src/mindustry/ui/dialogs/PlanetDialog.java +++ b/core/src/mindustry/ui/dialogs/PlanetDialog.java @@ -562,6 +562,7 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{ selected = null; launchSector = null; if(state.planet != planet){ + newPresets.clear(); state.planet = planet; rebuildList(); } diff --git a/core/src/mindustry/world/Block.java b/core/src/mindustry/world/Block.java index df7b0cc865..65ae60bcfe 100644 --- a/core/src/mindustry/world/Block.java +++ b/core/src/mindustry/world/Block.java @@ -249,6 +249,9 @@ public class Block extends UnlockableContent implements Senseable{ /** Radius of the light emitted by this block. */ public float lightRadius = 60f; + /** How much fog this block uncovers, in tiles. Cannot be dynamic. <= 0 to disable. */ + public int fogRadius = -1; + /** The sound that this block makes while active. One sound loop. Do not overuse. */ public Sound loopSound = Sounds.none; /** Active sound base volume. */ @@ -1038,6 +1041,10 @@ public class Block extends UnlockableContent implements Senseable{ hasShadow = false; } + if(fogRadius > 0){ + flags = flags.with(BlockFlag.hasFogRadius); + } + //initialize default health based on size if(health == -1){ boolean round = false; diff --git a/core/src/mindustry/world/meta/BlockFlag.java b/core/src/mindustry/world/meta/BlockFlag.java index 628c29fe70..762fb016c9 100644 --- a/core/src/mindustry/world/meta/BlockFlag.java +++ b/core/src/mindustry/world/meta/BlockFlag.java @@ -23,10 +23,11 @@ public enum BlockFlag{ /** Blocks that extinguishes fires. */ extinguisher, - //single-block identifiers + //special, internal identifiers launchPad, unitCargoUnloadPoint, - unitAssembler; + unitAssembler, + hasFogRadius; public final static BlockFlag[] all = values(); diff --git a/gradle.properties b/gradle.properties index d53b87638a..f322efd21b 100644 --- a/gradle.properties +++ b/gradle.properties @@ -24,4 +24,4 @@ android.useAndroidX=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=6418606527 +archash=54bf3f5289 diff --git a/tools/src/mindustry/tools/Generators.java b/tools/src/mindustry/tools/Generators.java index f1da161fa8..61078c2aaf 100644 --- a/tools/src/mindustry/tools/Generators.java +++ b/tools/src/mindustry/tools/Generators.java @@ -9,7 +9,6 @@ import arc.math.*; import arc.math.geom.*; import arc.struct.*; import arc.util.*; -import arc.util.async.*; import arc.util.noise.*; import mindustry.ctype.*; import mindustry.game.*;