From 492cb49805e640d84767f8620507ce129749de6a Mon Sep 17 00:00:00 2001 From: Anuken Date: Sun, 18 Aug 2019 22:52:46 -0400 Subject: [PATCH 1/5] bugfixes --- .../io/anuke/mindustry/ai/BlockIndexer.java | 2 +- core/src/io/anuke/mindustry/core/Control.java | 4 ++-- core/src/io/anuke/mindustry/core/World.java | 19 ++++++++++++------- .../entities/type/base/RepairDrone.java | 5 +++++ .../anuke/mindustry/server/ServerControl.java | 4 ++-- 5 files changed, 22 insertions(+), 12 deletions(-) diff --git a/core/src/io/anuke/mindustry/ai/BlockIndexer.java b/core/src/io/anuke/mindustry/ai/BlockIndexer.java index 5307e4d202..19fdfcfb4a 100644 --- a/core/src/io/anuke/mindustry/ai/BlockIndexer.java +++ b/core/src/io/anuke/mindustry/ai/BlockIndexer.java @@ -119,7 +119,7 @@ public class BlockIndexer{ ObjectSet set = damagedTiles[team.ordinal()]; for(Tile tile : set){ - if((tile.entity == null || tile.entity.getTeam() != team || !tile.entity.damaged()) && !(tile.block() instanceof BuildBlock)){ + if((tile.entity == null || tile.entity.getTeam() != team || !tile.entity.damaged()) || tile.block() instanceof BuildBlock){ returnArray.add(tile); } } diff --git a/core/src/io/anuke/mindustry/core/Control.java b/core/src/io/anuke/mindustry/core/Control.java index b0e2eed073..18d41588f2 100644 --- a/core/src/io/anuke/mindustry/core/Control.java +++ b/core/src/io/anuke/mindustry/core/Control.java @@ -84,7 +84,7 @@ public class Control implements ApplicationListener{ }); Events.on(PlayEvent.class, event -> { - player.setTeam(defaultTeam); + player.setTeam(state.rules.pvp ? netServer.assignTeam(playerGroup.all()) : defaultTeam); player.setDead(true); player.add(); @@ -237,7 +237,7 @@ public class Control implements ApplicationListener{ public void playMap(Map map, Rules rules){ ui.loadAnd(() -> { logic.reset(); - world.loadMap(map); + world.loadMap(map, rules); state.rules = rules; logic.play(); }); diff --git a/core/src/io/anuke/mindustry/core/World.java b/core/src/io/anuke/mindustry/core/World.java index 59cda8703f..1c330ce397 100644 --- a/core/src/io/anuke/mindustry/core/World.java +++ b/core/src/io/anuke/mindustry/core/World.java @@ -220,6 +220,10 @@ public class World implements ApplicationListener{ } public void loadMap(Map map){ + loadMap(map, new Rules()); + } + + public void loadMap(Map map, Rules checkRules){ try{ SaveIO.load(map.file, new FilterContext(map)); }catch(Exception e){ @@ -238,20 +242,21 @@ public class World implements ApplicationListener{ invalidMap = false; if(!headless){ - if(state.teams.get(defaultTeam).cores.size == 0){ + if(state.teams.get(defaultTeam).cores.size == 0 && !checkRules.pvp){ ui.showError("$map.nospawn"); invalidMap = true; - }else if(state.rules.pvp){ //pvp maps need two cores to be valid - invalidMap = true; + }else if(checkRules.pvp){ //pvp maps need two cores to be valid + int teams = 0; for(Team team : Team.all){ - if(state.teams.get(team).cores.size != 0 && team != defaultTeam){ - invalidMap = false; + if(state.teams.get(team).cores.size != 0){ + teams ++; } } - if(invalidMap){ + if(teams < 2){ + invalidMap = true; ui.showError("$map.nospawn.pvp"); } - }else if(state.rules.attackMode){ //pvp maps need two cores to be valid + }else if(checkRules.attackMode){ //attack maps need two cores to be valid invalidMap = state.teams.get(waveTeam).cores.isEmpty(); if(invalidMap){ ui.showError("$map.nospawn.attack"); diff --git a/core/src/io/anuke/mindustry/entities/type/base/RepairDrone.java b/core/src/io/anuke/mindustry/entities/type/base/RepairDrone.java index e5be4d3d07..be16cd1724 100644 --- a/core/src/io/anuke/mindustry/entities/type/base/RepairDrone.java +++ b/core/src/io/anuke/mindustry/entities/type/base/RepairDrone.java @@ -5,6 +5,7 @@ import io.anuke.mindustry.entities.type.TileEntity; import io.anuke.mindustry.entities.units.UnitState; import io.anuke.mindustry.world.Pos; import io.anuke.mindustry.world.Tile; +import io.anuke.mindustry.world.blocks.*; import java.io.*; @@ -23,6 +24,10 @@ public class RepairDrone extends BaseDrone{ target = Units.findDamagedTile(team, x, y); } + if(target instanceof TileEntity && ((TileEntity)target).block instanceof BuildBlock){ + target = null; + } + if(target != null){ if(target.dst(RepairDrone.this) > type.range){ circle(type.range * 0.9f); diff --git a/server/src/io/anuke/mindustry/server/ServerControl.java b/server/src/io/anuke/mindustry/server/ServerControl.java index 8eeeba9e1c..0cffac9591 100644 --- a/server/src/io/anuke/mindustry/server/ServerControl.java +++ b/server/src/io/anuke/mindustry/server/ServerControl.java @@ -157,7 +157,7 @@ public class ServerControl implements ApplicationListener{ info("Selected next map to be {0}.", map.name()); - play(true, () -> world.loadMap(map)); + play(true, () -> world.loadMap(map, map.applyRules(lastMode))); } }else{ netServer.kickAll(KickReason.gameover); @@ -231,7 +231,7 @@ public class ServerControl implements ApplicationListener{ logic.reset(); lastMode = preset; try{ - world.loadMap(result); + world.loadMap(result, result.applyRules(lastMode)); state.rules = result.applyRules(preset); logic.play(); From 191c6e90161e81e0a09949c1f1cf956033c5ea48 Mon Sep 17 00:00:00 2001 From: Anuken Date: Mon, 19 Aug 2019 09:23:16 -0400 Subject: [PATCH 2/5] crash fix --- core/src/io/anuke/mindustry/entities/traits/BuilderTrait.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/io/anuke/mindustry/entities/traits/BuilderTrait.java b/core/src/io/anuke/mindustry/entities/traits/BuilderTrait.java index 92d48aa655..55368d1d57 100644 --- a/core/src/io/anuke/mindustry/entities/traits/BuilderTrait.java +++ b/core/src/io/anuke/mindustry/entities/traits/BuilderTrait.java @@ -135,7 +135,7 @@ public interface BuilderTrait extends Entity, TeamTrait{ default void writeBuilding(DataOutput output) throws IOException{ BuildRequest request = buildRequest(); - if(request != null){ + if(request != null && (request.block != null || request.breaking)){ output.writeByte(request.breaking ? 1 : 0); output.writeInt(Pos.get(request.x, request.y)); output.writeFloat(request.progress); From 8eb4d25638eb51891a326fd1de3adc072a633849 Mon Sep 17 00:00:00 2001 From: Anuken Date: Mon, 19 Aug 2019 09:42:45 -0400 Subject: [PATCH 3/5] Fixed error on exiting with custom map on server --- core/src/io/anuke/mindustry/ui/dialogs/PausedDialog.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/io/anuke/mindustry/ui/dialogs/PausedDialog.java b/core/src/io/anuke/mindustry/ui/dialogs/PausedDialog.java index 40fb91dc9b..124b90df6d 100644 --- a/core/src/io/anuke/mindustry/ui/dialogs/PausedDialog.java +++ b/core/src/io/anuke/mindustry/ui/dialogs/PausedDialog.java @@ -106,7 +106,7 @@ public class PausedDialog extends FloatingDialog{ return; } - if(control.saves.getCurrent() == null || !control.saves.getCurrent().isAutosave() || state.rules.tutorial){ + if(control.saves.getCurrent() == null || !control.saves.getCurrent().isAutosave() || state.rules.tutorial || Net.client()){ state.set(State.menu); logic.reset(); return; From c6777bd2191efb625dddf3dbb164abd48a297916 Mon Sep 17 00:00:00 2001 From: Anuken Date: Mon, 19 Aug 2019 18:03:21 -0400 Subject: [PATCH 4/5] Update bundle.properties --- core/assets/bundles/bundle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/assets/bundles/bundle.properties b/core/assets/bundles/bundle.properties index 99669a7b73..4f6f4be95d 100644 --- a/core/assets/bundles/bundle.properties +++ b/core/assets/bundles/bundle.properties @@ -124,7 +124,7 @@ save.new = New Save save.overwrite = Are you sure you want to overwrite\nthis save slot? overwrite = Overwrite save.none = No saves found! -saveload = [accent]Saving... +saveload = Saving... savefail = Failed to save game! save.delete.confirm = Are you sure you want to delete this save? save.delete = Delete From a873560d4e25494b6ba82738a27cbed9a7e521fd Mon Sep 17 00:00:00 2001 From: Anuken Date: Mon, 19 Aug 2019 22:16:43 -0400 Subject: [PATCH 5/5] Multiplayer: Smooth building + rotation / Disabled UDP / Cleanup --- build.gradle | 1 - core/src/io/anuke/mindustry/core/Control.java | 50 +++--- .../io/anuke/mindustry/core/NetServer.java | 2 +- .../entities/traits/BuilderTrait.java | 58 +++---- .../io/anuke/mindustry/net/Interpolator.java | 19 +- core/src/io/anuke/mindustry/net/Net.java | 62 +++++-- .../io/anuke/mindustry/net/NetConnection.java | 7 +- .../io/anuke/mindustry/net/ArcNetClient.java | 19 +- .../io/anuke/mindustry/net/ArcNetServer.java | 162 ++---------------- 9 files changed, 145 insertions(+), 235 deletions(-) diff --git a/build.gradle b/build.gradle index f051cf3437..adc3e55d78 100644 --- a/build.gradle +++ b/build.gradle @@ -312,6 +312,5 @@ project(":net"){ dependencies{ compile project(":core") compile "org.lz4:lz4-java:1.4.1" - compile 'com.github.Anuken:WaifUPnP:05eb46bc577fd7674596946ba288c96c0cedd893' } } diff --git a/core/src/io/anuke/mindustry/core/Control.java b/core/src/io/anuke/mindustry/core/Control.java index 18d41588f2..b4fbdc2151 100644 --- a/core/src/io/anuke/mindustry/core/Control.java +++ b/core/src/io/anuke/mindustry/core/Control.java @@ -350,35 +350,37 @@ public class Control implements ApplicationListener{ //display UI scale changed dialog if(Core.settings.getBool("uiscalechanged", false)){ - FloatingDialog dialog = new FloatingDialog("$confirm"); + Core.app.post(() -> Core.app.post(() -> { + FloatingDialog dialog = new FloatingDialog("$confirm"); + dialog.setFillParent(true); - float[] countdown = {60 * 11}; - Runnable exit = () -> { - Core.settings.put("uiscale", 100); - Core.settings.put("uiscalechanged", false); - settings.save(); - dialog.hide(); - Core.app.exit(); - }; + float[] countdown = {60 * 11}; + Runnable exit = () -> { + Core.settings.put("uiscale", 100); + Core.settings.put("uiscalechanged", false); + settings.save(); + dialog.hide(); + Core.app.exit(); + }; - dialog.setFillParent(false); - dialog.cont.label(() -> { - if(countdown[0] <= 0){ - exit.run(); - } - return Core.bundle.format("uiscale.reset", (int)((countdown[0] -= Time.delta()) / 60f)); - }).pad(10f).expand().left(); + dialog.cont.label(() -> { + if(countdown[0] <= 0){ + exit.run(); + } + return Core.bundle.format("uiscale.reset", (int)((countdown[0] -= Time.delta()) / 60f)); + }).pad(10f).expand().center(); - dialog.buttons.defaults().size(200f, 60f); - dialog.buttons.addButton("$uiscale.cancel", exit); + dialog.buttons.defaults().size(200f, 60f); + dialog.buttons.addButton("$uiscale.cancel", exit); - dialog.buttons.addButton("$ok", () -> { - Core.settings.put("uiscalechanged", false); - settings.save(); - dialog.hide(); - }); + dialog.buttons.addButton("$ok", () -> { + Core.settings.put("uiscalechanged", false); + settings.save(); + dialog.hide(); + }); - Core.app.post(dialog::show); + dialog.show(); + })); } } diff --git a/core/src/io/anuke/mindustry/core/NetServer.java b/core/src/io/anuke/mindustry/core/NetServer.java index b9c8510b02..ede04df856 100644 --- a/core/src/io/anuke/mindustry/core/NetServer.java +++ b/core/src/io/anuke/mindustry/core/NetServer.java @@ -40,7 +40,7 @@ import static io.anuke.mindustry.Vars.*; public class NetServer implements ApplicationListener{ public final static int maxSnapshotSize = 430; - private final static float serverSyncTime = 15, kickDuration = 30 * 1000; + private final static float serverSyncTime = 12, kickDuration = 30 * 1000; private final static Vector2 vector = new Vector2(); private final static Rectangle viewport = new Rectangle(); /** If a player goes away of their server-side coordinates by this distance, they get teleported back. */ diff --git a/core/src/io/anuke/mindustry/entities/traits/BuilderTrait.java b/core/src/io/anuke/mindustry/entities/traits/BuilderTrait.java index 55368d1d57..1b601d3d7f 100644 --- a/core/src/io/anuke/mindustry/entities/traits/BuilderTrait.java +++ b/core/src/io/anuke/mindustry/entities/traits/BuilderTrait.java @@ -1,33 +1,28 @@ package io.anuke.mindustry.entities.traits; import io.anuke.annotations.Annotations.*; -import io.anuke.arc.Core; -import io.anuke.arc.Events; -import io.anuke.arc.collection.Array; +import io.anuke.arc.*; import io.anuke.arc.collection.Queue; +import io.anuke.arc.collection.*; import io.anuke.arc.graphics.g2d.*; -import io.anuke.arc.math.Angles; -import io.anuke.arc.math.Mathf; -import io.anuke.arc.math.geom.Vector2; -import io.anuke.arc.util.Time; -import io.anuke.mindustry.Vars; -import io.anuke.mindustry.content.Blocks; -import io.anuke.mindustry.entities.type.TileEntity; -import io.anuke.mindustry.entities.type.Unit; -import io.anuke.mindustry.game.EventType.BuildSelectEvent; -import io.anuke.mindustry.gen.Call; -import io.anuke.mindustry.graphics.Pal; -import io.anuke.mindustry.net.Net; +import io.anuke.arc.math.*; +import io.anuke.arc.math.geom.*; +import io.anuke.arc.util.*; +import io.anuke.mindustry.*; +import io.anuke.mindustry.content.*; +import io.anuke.mindustry.entities.type.*; +import io.anuke.mindustry.game.EventType.*; +import io.anuke.mindustry.gen.*; +import io.anuke.mindustry.graphics.*; import io.anuke.mindustry.world.*; -import io.anuke.mindustry.world.blocks.BuildBlock; -import io.anuke.mindustry.world.blocks.BuildBlock.BuildEntity; +import io.anuke.mindustry.world.blocks.*; +import io.anuke.mindustry.world.blocks.BuildBlock.*; import java.io.*; -import java.util.Arrays; +import java.util.*; import static io.anuke.mindustry.Vars.*; -import static io.anuke.mindustry.entities.traits.BuilderTrait.BuildDataStatic.removal; -import static io.anuke.mindustry.entities.traits.BuilderTrait.BuildDataStatic.tmptr; +import static io.anuke.mindustry.entities.traits.BuilderTrait.BuildDataStatic.*; /** Interface for units that build things.*/ public interface BuilderTrait extends Entity, TeamTrait{ @@ -106,19 +101,14 @@ public interface BuilderTrait extends Entity, TeamTrait{ unit.rotation = Mathf.slerpDelta(unit.rotation, unit.angleTo(entity), 0.4f); } - //progress is synced, thus not updated clientside - if(!Net.client()){ - //deconstructing is 2x as fast - if(current.breaking){ - entity.deconstruct(unit, core, 2f / entity.buildCost * Time.delta() * getBuildPower(tile) * state.rules.buildSpeedMultiplier); - }else{ - entity.construct(unit, core, 1f / entity.buildCost * Time.delta() * getBuildPower(tile) * state.rules.buildSpeedMultiplier); - } - - current.progress = entity.progress(); + //deconstructing is 2x as fast + if(current.breaking){ + entity.deconstruct(unit, core, 2f / entity.buildCost * Time.delta() * getBuildPower(tile) * state.rules.buildSpeedMultiplier); }else{ - entity.progress = current.progress; + entity.construct(unit, core, 1f / entity.buildCost * Time.delta() * getBuildPower(tile) * state.rules.buildSpeedMultiplier); } + + current.progress = entity.progress; } /** Returns the queue for storing build requests. */ @@ -174,7 +164,11 @@ public interface BuilderTrait extends Entity, TeamTrait{ if(applyChanges){ buildQueue().addLast(request); }else if(isBuilding()){ - buildRequest().progress = progress; + BuildRequest last = buildRequest(); + last.progress = progress; + if(last.tile() != null && last.tile().entity instanceof BuildEntity){ + ((BuildEntity)last.tile().entity).progress = progress; + } } } } diff --git a/core/src/io/anuke/mindustry/net/Interpolator.java b/core/src/io/anuke/mindustry/net/Interpolator.java index 0201f980ce..dbc9b422ca 100644 --- a/core/src/io/anuke/mindustry/net/Interpolator.java +++ b/core/src/io/anuke/mindustry/net/Interpolator.java @@ -1,14 +1,15 @@ package io.anuke.mindustry.net; -import io.anuke.arc.math.Mathf; -import io.anuke.arc.math.geom.Vector2; -import io.anuke.arc.util.Time; +import io.anuke.arc.math.*; +import io.anuke.arc.math.geom.*; +import io.anuke.arc.util.*; public class Interpolator{ //used for movement public Vector2 target = new Vector2(); public Vector2 last = new Vector2(); public float[] targets = {}; + public float[] lasts = {}; public long lastUpdated, updateSpacing; //current state @@ -21,6 +22,12 @@ public class Interpolator{ lastUpdated = Time.millis(); targets = target1ds; + if(lasts.length != values.length){ + lasts = new float[values.length]; + } + for(int i = 0; i < values.length; i++){ + lasts[i] = values[i]; + } last.set(cx, cy); target.set(x, y); } @@ -46,8 +53,12 @@ public class Interpolator{ values = new float[targets.length]; } + if(lasts.length != targets.length){ + lasts = new float[targets.length]; + } + for(int i = 0; i < values.length; i++){ - values[i] = Mathf.slerp(values[i], targets[i], alpha); + values[i] = Mathf.slerp(lasts[i], targets[i], alpha); } }else{ pos.set(target); diff --git a/core/src/io/anuke/mindustry/net/Net.java b/core/src/io/anuke/mindustry/net/Net.java index d0965c3cb8..19f459ea60 100644 --- a/core/src/io/anuke/mindustry/net/Net.java +++ b/core/src/io/anuke/mindustry/net/Net.java @@ -162,8 +162,8 @@ public class Net{ /** * Returns a list of all connections IDs. */ - public static Array getConnections(){ - return (Array)serverProvider.getConnections(); + public static Iterable getConnections(){ + return (Iterable)serverProvider.getConnections(); } /** @@ -326,7 +326,7 @@ public class Net{ public static void dispose(){ if(clientProvider != null) clientProvider.dispose(); - if(serverProvider != null) serverProvider.dispose(); + if(serverProvider != null) serverProvider.close(); clientProvider = null; serverProvider = null; server = false; @@ -377,16 +377,53 @@ public class Net{ void host(int port) throws IOException; /** Sends a large stream of data to a specific client. */ - void sendStream(int id, Streamable stream); + default void sendStream(int id, Streamable stream){ + NetConnection connection = getByID(id); + if(connection == null) return; + try{ + int cid; + StreamBegin begin = new StreamBegin(); + begin.total = stream.stream.available(); + begin.type = Registrator.getID(stream.getClass()); + connection.send(begin, SendMode.tcp); + cid = begin.id; - /** Send an object to everyone connected. */ - void send(Object object, SendMode mode); + while(stream.stream.available() > 0){ + byte[] bytes = new byte[Math.min(512, stream.stream.available())]; + stream.stream.read(bytes); - /** Send an object to a specific client ID. */ - void sendTo(int id, Object object, SendMode mode); + StreamChunk chunk = new StreamChunk(); + chunk.id = cid; + chunk.data = bytes; + connection.send(chunk, SendMode.tcp); + } + }catch(IOException e){ + throw new RuntimeException(e); + } + } - /** Send an object to everyone except a client ID. */ - void sendExcept(int id, Object object, SendMode mode); + default void send(Object object, SendMode mode){ + for(NetConnection con : getConnections()){ + con.send(object, mode); + } + } + + default void sendTo(int id, Object object, SendMode mode){ + NetConnection conn = getByID(id); + if(conn == null){ + Log.err("Failed to find connection with ID {0}.", id); + return; + } + conn.send(object, mode); + } + + default void sendExcept(int id, Object object, SendMode mode){ + for(NetConnection con : getConnections()){ + if(con.id != id){ + con.send(object, mode); + } + } + } /** Close the server connection. */ void close(); @@ -395,12 +432,9 @@ public class Net{ byte[] compressSnapshot(byte[] input); /** Return all connected users. */ - Array getConnections(); + Iterable getConnections(); /** Returns a connection by ID. */ NetConnection getByID(int id); - - /** Close all connections. */ - void dispose(); } } diff --git a/core/src/io/anuke/mindustry/net/NetConnection.java b/core/src/io/anuke/mindustry/net/NetConnection.java index 2bb53fb9f7..ba2a004a03 100644 --- a/core/src/io/anuke/mindustry/net/NetConnection.java +++ b/core/src/io/anuke/mindustry/net/NetConnection.java @@ -3,6 +3,8 @@ package io.anuke.mindustry.net; import io.anuke.mindustry.net.Net.SendMode; public abstract class NetConnection{ + private static int lastID; + public final int id; public final String address; @@ -18,8 +20,9 @@ public abstract class NetConnection{ public boolean hasBegunConnecting = false; public float viewWidth, viewHeight, viewX, viewY; - public NetConnection(int id, String address){ - this.id = id; + /** Assigns this connection a unique ID. No two connections will ever have the same ID.*/ + public NetConnection(String address){ + this.id = lastID++; this.address = address; } diff --git a/net/src/io/anuke/mindustry/net/ArcNetClient.java b/net/src/io/anuke/mindustry/net/ArcNetClient.java index 485235724f..618aea5eb4 100644 --- a/net/src/io/anuke/mindustry/net/ArcNetClient.java +++ b/net/src/io/anuke/mindustry/net/ArcNetClient.java @@ -4,6 +4,7 @@ import io.anuke.arc.*; import io.anuke.arc.collection.*; import io.anuke.arc.function.*; import io.anuke.arc.net.*; +import io.anuke.arc.util.async.*; import io.anuke.arc.util.pooling.*; import io.anuke.mindustry.net.Net.*; import io.anuke.mindustry.net.Packets.*; @@ -84,7 +85,7 @@ public class ArcNetClient implements ClientProvider{ @Override public void connect(String ip, int port, Runnable success){ - runAsync(() -> { + Threads.daemon(() -> { try{ //just in case client.stop(); @@ -99,7 +100,7 @@ public class ArcNetClient implements ClientProvider{ updateThread.setDaemon(true); updateThread.start(); - client.connect(5000, ip, port, port); + client.connect(5000, ip, port); success.run(); }catch(Exception e){ handleException(e); @@ -115,11 +116,7 @@ public class ArcNetClient implements ClientProvider{ @Override public void send(Object object, SendMode mode){ try{ - if(mode == SendMode.tcp){ - client.sendTCP(object); - }else{ - client.sendUDP(object); - } + client.sendTCP(object); //sending things can cause an under/overflow, catch it and disconnect instead of crashing }catch(BufferOverflowException | BufferUnderflowException e){ Net.showError(e); @@ -140,7 +137,7 @@ public class ArcNetClient implements ClientProvider{ @Override public void pingHost(String address, int port, Consumer valid, Consumer invalid){ - runAsync(() -> { + Threads.daemon(() -> { try{ DatagramSocket socket = new DatagramSocket(); socket.send(new DatagramPacket(new byte[]{-2, 1}, 2, InetAddress.getByName(address), port)); @@ -189,12 +186,6 @@ public class ArcNetClient implements ClientProvider{ } } - private void runAsync(Runnable run){ - Thread thread = new Thread(run, "Client Async Run"); - thread.setDaemon(true); - thread.start(); - } - private void handleException(Exception e){ if(e instanceof ArcNetException){ Core.app.post(() -> Net.showError(new IOException("mismatch"))); diff --git a/net/src/io/anuke/mindustry/net/ArcNetServer.java b/net/src/io/anuke/mindustry/net/ArcNetServer.java index ed8496a8e2..08c571c119 100644 --- a/net/src/io/anuke/mindustry/net/ArcNetServer.java +++ b/net/src/io/anuke/mindustry/net/ArcNetServer.java @@ -1,11 +1,9 @@ package io.anuke.mindustry.net; -import com.dosse.upnp.*; import io.anuke.arc.*; -import io.anuke.arc.collection.*; import io.anuke.arc.net.*; import io.anuke.arc.util.*; -import io.anuke.mindustry.*; +import io.anuke.arc.util.async.*; import io.anuke.mindustry.net.Net.*; import io.anuke.mindustry.net.Packets.*; import net.jpountz.lz4.*; @@ -19,14 +17,10 @@ import static io.anuke.mindustry.Vars.*; public class ArcNetServer implements ServerProvider{ final Server server; - final CopyOnWriteArrayList connections = new CopyOnWriteArrayList<>(); - final CopyOnWriteArraySet missing = new CopyOnWriteArraySet<>(); - final Array array = new Array<>(); + final CopyOnWriteArrayList connections = new CopyOnWriteArrayList<>(); final LZ4Compressor compressor = LZ4Factory.fastestInstance().fastCompressor(); Thread serverThread; - int lastconnection = 0; - public ArcNetServer(){ server = new Server(4096 * 2, 4096, new PacketSerializer()); server.setMulticast(multicastGroup, multicastPort); @@ -42,7 +36,7 @@ public class ArcNetServer implements ServerProvider{ public void connected(Connection connection){ String ip = connection.getRemoteAddressTCP().getAddress().getHostAddress(); - KryoConnection kn = new KryoConnection(lastconnection++, ip, connection); + ArcConnection kn = new ArcConnection(ip, connection); Connect c = new Connect(); c.id = kn.id; @@ -56,7 +50,7 @@ public class ArcNetServer implements ServerProvider{ @Override public void disconnected(Connection connection){ - KryoConnection k = getByKryoID(connection.getID()); + ArcConnection k = getByKryoID(connection.getID()); if(k == null) return; Disconnect c = new Disconnect(); @@ -70,7 +64,7 @@ public class ArcNetServer implements ServerProvider{ @Override public void received(Connection connection, Object object){ - KryoConnection k = getByKryoID(connection.getID()); + ArcConnection k = getByKryoID(connection.getID()); if(object instanceof FrameworkMessage || k == null) return; Core.app.post(() -> { @@ -99,18 +93,14 @@ public class ArcNetServer implements ServerProvider{ } @Override - public Array getConnections(){ - array.clear(); - for(KryoConnection c : connections){ - array.add(c); - } - return array; + public Iterable getConnections(){ + return connections; } @Override - public KryoConnection getByID(int id){ + public ArcConnection getByID(int id){ for(int i = 0; i < connections.size(); i++){ - KryoConnection con = connections.get(i); + ArcConnection con = connections.get(i); if(con.id == id){ return con; } @@ -121,28 +111,14 @@ public class ArcNetServer implements ServerProvider{ @Override public void host(int port) throws IOException{ - //attempt to open default ports if they're not already open - //this only opens the default port due to security concerns (?) - if(port == Vars.port){ - async(() -> { - try{ - if(!UPnP.isMappedTCP(port)) UPnP.openPortTCP(port); - if(!UPnP.isMappedUDP(port)) UPnP.openPortUDP(port); - }catch(Throwable ignored){ - } - }); - } - - lastconnection = 0; connections.clear(); - missing.clear(); - server.bind(port, port); + server.bind(port); serverThread = new Thread(() -> { try{ server.run(); }catch(Throwable e){ - if(!(e instanceof ClosedSelectorException)) handleException(e); + if(!(e instanceof ClosedSelectorException)) Threads.throwAppException(e); } }, "Net Server"); serverThread.setDaemon(true); @@ -152,102 +128,12 @@ public class ArcNetServer implements ServerProvider{ @Override public void close(){ connections.clear(); - lastconnection = 0; - - async(server::stop); + Threads.daemon(server::stop); } - @Override - public void sendStream(int id, Streamable stream){ - KryoConnection connection = getByID(id); - if(connection == null) return; - try{ - - if(connection.connection != null){ - - connection.connection.addListener(new InputStreamSender(stream.stream, 512){ - int id; - - protected void start(){ - //send an object so the receiving side knows how to handle the following chunks - StreamBegin begin = new StreamBegin(); - begin.total = stream.stream.available(); - begin.type = Registrator.getID(stream.getClass()); - connection.connection.sendTCP(begin); - id = begin.id; - } - - protected Object next(byte[] bytes){ - StreamChunk chunk = new StreamChunk(); - chunk.id = id; - chunk.data = bytes; - return chunk; //wrap the byte[] with an object so the receiving side knows how to handle it. - } - }); - }else{ - int cid; - StreamBegin begin = new StreamBegin(); - begin.total = stream.stream.available(); - begin.type = Registrator.getID(stream.getClass()); - connection.send(begin, SendMode.tcp); - cid = begin.id; - - while(stream.stream.available() > 0){ - byte[] bytes = new byte[Math.min(512, stream.stream.available())]; - stream.stream.read(bytes); - - StreamChunk chunk = new StreamChunk(); - chunk.id = cid; - chunk.data = bytes; - connection.send(chunk, SendMode.tcp); - } - } - }catch(IOException e){ - throw new RuntimeException(e); - } - } - - @Override - public void send(Object object, SendMode mode){ + ArcConnection getByKryoID(int id){ for(int i = 0; i < connections.size(); i++){ - connections.get(i).send(object, mode); - } - } - - @Override - public void sendTo(int id, Object object, SendMode mode){ - NetConnection conn = getByID(id); - if(conn == null){ - if(!missing.contains(id)) - Log.err("Failed to find connection with ID {0}.", id); - missing.add(id); - return; - } - conn.send(object, mode); - } - - @Override - public void sendExcept(int id, Object object, SendMode mode){ - for(int i = 0; i < connections.size(); i++){ - KryoConnection conn = connections.get(i); - if(conn.id != id) conn.send(object, mode); - } - } - - @Override - public void dispose(){ - close(); - } - - private void handleException(Throwable e){ - Time.run(0f, () -> { - throw new RuntimeException(e); - }); - } - - KryoConnection getByKryoID(int id){ - for(int i = 0; i < connections.size(); i++){ - KryoConnection con = connections.get(i); + ArcConnection con = connections.get(i); if(con.connection != null && con.connection.getID() == id){ return con; } @@ -256,17 +142,11 @@ public class ArcNetServer implements ServerProvider{ return null; } - void async(Runnable run){ - Thread thread = new Thread(run); - thread.setDaemon(true); - thread.start(); - } - - class KryoConnection extends NetConnection{ + class ArcConnection extends NetConnection{ public final Connection connection; - public KryoConnection(int id, String address, Connection connection){ - super(id, address); + public ArcConnection(String address, Connection connection){ + super(address); this.connection = connection; } @@ -278,17 +158,13 @@ public class ArcNetServer implements ServerProvider{ @Override public void send(Object object, SendMode mode){ try{ - if(mode == SendMode.tcp){ - connection.sendTCP(object); - }else{ - connection.sendUDP(object); - } + connection.sendTCP(object); }catch(Exception e){ Log.err(e); Log.info("Error sending packet. Disconnecting invalid client!"); connection.close(); - KryoConnection k = getByKryoID(connection.getID()); + ArcConnection k = getByKryoID(connection.getID()); if(k != null) connections.remove(k); } }