diff --git a/android/src/io/anuke/mindustry/AndroidLauncher.java b/android/src/io/anuke/mindustry/AndroidLauncher.java index 76b43cf7bb..7198a9d39b 100644 --- a/android/src/io/anuke/mindustry/AndroidLauncher.java +++ b/android/src/io/anuke/mindustry/AndroidLauncher.java @@ -1,12 +1,11 @@ package io.anuke.mindustry; -import android.*; import android.app.*; import android.content.*; import android.content.pm.*; import android.net.*; -import android.os.*; import android.os.Build.*; +import android.os.*; import android.provider.Settings.*; import android.telephony.*; import io.anuke.arc.*; @@ -18,13 +17,12 @@ import io.anuke.arc.util.*; import io.anuke.arc.util.serialization.*; import io.anuke.mindustry.game.Saves.*; import io.anuke.mindustry.io.*; -import io.anuke.mindustry.net.Net; import io.anuke.mindustry.net.*; +import io.anuke.mindustry.net.Net.*; import io.anuke.mindustry.ui.dialogs.*; import java.io.*; import java.lang.System; -import java.util.*; import static io.anuke.mindustry.Vars.*; @@ -41,10 +39,13 @@ public class AndroidLauncher extends AndroidApplication{ UnitScl.dp.addition = 0.5f; } - Net.setClientProvider(new ArcNetClient()); - Net.setServerProvider(new ArcNetServer()); initialize(new ClientLauncher(){ + @Override + public NetProvider getNet(){ + return new ArcNetImpl(); + } + @Override public void hide(){ moveTaskToBack(true); @@ -68,24 +69,6 @@ public class AndroidLauncher extends AndroidApplication{ } } - @Override - public void requestExternalPerms(Runnable callback){ - if(Build.VERSION.SDK_INT < Build.VERSION_CODES.M || (checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED && - checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED)){ - callback.run(); - }else{ - permCallback = callback; - ArrayList perms = new ArrayList<>(); - if(checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED){ - perms.add(Manifest.permission.WRITE_EXTERNAL_STORAGE); - } - if(checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED){ - perms.add(Manifest.permission.READ_EXTERNAL_STORAGE); - } - requestPermissions(perms.toArray(new String[0]), PERMISSION_REQUEST_CODE); - } - } - @Override public void shareFile(FileHandle file){ } diff --git a/annotations/src/main/java/io/anuke/annotations/RemoteWriteGenerator.java b/annotations/src/main/java/io/anuke/annotations/RemoteWriteGenerator.java index 411f8ead86..401e862202 100644 --- a/annotations/src/main/java/io/anuke/annotations/RemoteWriteGenerator.java +++ b/annotations/src/main/java/io/anuke/annotations/RemoteWriteGenerator.java @@ -97,7 +97,7 @@ public class RemoteWriteGenerator{ if(!forwarded && methodEntry.local != Loc.none){ //add in local checks if(methodEntry.local != Loc.both){ - method.beginControlFlow("if(" + getCheckString(methodEntry.local) + " || !io.anuke.mindustry.net.Net.active())"); + method.beginControlFlow("if(" + getCheckString(methodEntry.local) + " || !io.anuke.mindustry.Vars.net.active())"); } //concatenate parameters @@ -159,7 +159,7 @@ public class RemoteWriteGenerator{ boolean writePlayerSkipCheck = methodEntry.where == Loc.both && i == 0; if(writePlayerSkipCheck){ //write begin check - method.beginControlFlow("if(io.anuke.mindustry.net.Net.server())"); + method.beginControlFlow("if(io.anuke.mindustry.Vars.net.server())"); } if(Utils.isPrimitive(typeName)){ //check if it's a primitive, and if so write it @@ -205,7 +205,7 @@ public class RemoteWriteGenerator{ } //send the actual packet - method.addStatement("io.anuke.mindustry.net.Net." + sendString + "packet, " + + method.addStatement("io.anuke.mindustry.Vars.net." + sendString + "packet, " + (methodEntry.unreliable ? "io.anuke.mindustry.net.Net.SendMode.udp" : "io.anuke.mindustry.net.Net.SendMode.tcp") + ")"); @@ -217,8 +217,8 @@ public class RemoteWriteGenerator{ } private String getCheckString(Loc loc){ - return loc.isClient && loc.isServer ? "io.anuke.mindustry.net.Net.server() || io.anuke.mindustry.net.Net.client()" : - loc.isClient ? "io.anuke.mindustry.net.Net.client()" : - loc.isServer ? "io.anuke.mindustry.net.Net.server()" : "false"; + return loc.isClient && loc.isServer ? "io.anuke.mindustry.Vars.net.server() || io.anuke.mindustry.Vars.net.client()" : + loc.isClient ? "io.anuke.mindustry.Vars.net.client()" : + loc.isServer ? "io.anuke.mindustry.Vars.net.server()" : "false"; } } diff --git a/core/src/io/anuke/mindustry/ClientLauncher.java b/core/src/io/anuke/mindustry/ClientLauncher.java index 8e57f24058..e192a5e81d 100644 --- a/core/src/io/anuke/mindustry/ClientLauncher.java +++ b/core/src/io/anuke/mindustry/ClientLauncher.java @@ -13,6 +13,7 @@ import io.anuke.mindustry.game.EventType.*; import io.anuke.mindustry.gen.*; import io.anuke.mindustry.graphics.*; import io.anuke.mindustry.maps.*; +import io.anuke.mindustry.net.Net; import static io.anuke.arc.Core.*; import static io.anuke.mindustry.Vars.*; @@ -40,6 +41,7 @@ public abstract class ClientLauncher extends ApplicationCore implements Platform assets = new AssetManager(); assets.setLoader(Texture.class, "." + mapExtension, new MapPreviewLoader()); atlas = TextureAtlas.blankAtlas(); + Vars.net = new Net(platform.getNet()); UI.loadSystemCursors(); diff --git a/core/src/io/anuke/mindustry/Vars.java b/core/src/io/anuke/mindustry/Vars.java index 04d751358c..6782e83908 100644 --- a/core/src/io/anuke/mindustry/Vars.java +++ b/core/src/io/anuke/mindustry/Vars.java @@ -141,6 +141,7 @@ public class Vars implements Loadable{ /** list of all locales that can be switched to */ public static Locale[] locales; + public static Net net; public static ContentLoader content; public static GameState state; public static GlobalData data; @@ -231,7 +232,7 @@ public class Vars implements Loadable{ for(EntityGroup group : entities.all()){ group.setRemoveListener(entity -> { - if(entity instanceof SyncTrait && Net.client()){ + if(entity instanceof SyncTrait && net.client()){ netClient.addRemovedEntity((entity).getID()); } }); diff --git a/core/src/io/anuke/mindustry/ai/Pathfinder.java b/core/src/io/anuke/mindustry/ai/Pathfinder.java index 87b0d838f6..9df0c432c8 100644 --- a/core/src/io/anuke/mindustry/ai/Pathfinder.java +++ b/core/src/io/anuke/mindustry/ai/Pathfinder.java @@ -25,7 +25,7 @@ public class Pathfinder{ public Pathfinder(){ Events.on(WorldLoadEvent.class, event -> clear()); Events.on(TileChangeEvent.class, event -> { - if(Net.client()) return; + if(net.client()) return; for(Team team : Team.all){ TeamData data = state.teams.get(team); @@ -43,7 +43,7 @@ public class Pathfinder{ } public void update(){ - if(Net.client() || paths == null) return; + if(net.client() || paths == null) return; for(Team team : Team.all){ if(state.teams.isActive(team)){ diff --git a/core/src/io/anuke/mindustry/ai/WaveSpawner.java b/core/src/io/anuke/mindustry/ai/WaveSpawner.java index 5863594f4f..a4d815dc95 100644 --- a/core/src/io/anuke/mindustry/ai/WaveSpawner.java +++ b/core/src/io/anuke/mindustry/ai/WaveSpawner.java @@ -116,7 +116,7 @@ public class WaveSpawner{ } public boolean isSpawning(){ - return spawning && !Net.client(); + return spawning && !net.client(); } private void reset(){ diff --git a/core/src/io/anuke/mindustry/core/Control.java b/core/src/io/anuke/mindustry/core/Control.java index 3a80a5883d..e425f2778b 100644 --- a/core/src/io/anuke/mindustry/core/Control.java +++ b/core/src/io/anuke/mindustry/core/Control.java @@ -17,7 +17,6 @@ import io.anuke.mindustry.game.*; import io.anuke.mindustry.gen.*; import io.anuke.mindustry.input.*; import io.anuke.mindustry.maps.Map; -import io.anuke.mindustry.net.Net; import io.anuke.mindustry.type.*; import io.anuke.mindustry.ui.dialogs.*; import io.anuke.mindustry.world.*; @@ -29,6 +28,7 @@ import java.util.*; import static io.anuke.arc.Core.*; import static io.anuke.mindustry.Vars.*; +import static io.anuke.mindustry.Vars.net; /** * Control module. @@ -67,7 +67,7 @@ public class Control implements ApplicationListener, Loadable{ Events.on(WorldLoadEvent.class, event -> { Core.app.post(() -> Core.app.post(() -> { - if(Net.active() && player.getClosestCore() != null){ + if(net.active() && player.getClosestCore() != null){ //set to closest core since that's where the player will probably respawn; prevents camera jumps Core.camera.position.set(player.isDead() ? player.getClosestCore() : player); }else{ @@ -99,7 +99,7 @@ public class Control implements ApplicationListener, Loadable{ Effects.shake(5, 6, Core.camera.position.x, Core.camera.position.y); //the restart dialog can show info for any number of scenarios Call.onGameOver(event.winner); - if(state.rules.zone != null && !Net.client()){ + if(state.rules.zone != null && !net.client()){ //remove zone save on game over if(saves.getZoneSlot() != null && !state.rules.tutorial){ saves.getZoneSlot().delete(); @@ -109,9 +109,9 @@ public class Control implements ApplicationListener, Loadable{ //autohost for pvp maps Events.on(WorldLoadEvent.class, event -> { - if(state.rules.pvp && !Net.active()){ + if(state.rules.pvp && !net.active()){ try{ - Net.host(port); + net.host(port); player.isAdmin = true; }catch(IOException e){ ui.showError(Core.bundle.format("server.error", Strings.parseException(e, true))); @@ -210,7 +210,7 @@ public class Control implements ApplicationListener, Loadable{ public void playZone(Zone zone){ ui.loadAnd(() -> { logic.reset(); - Net.reset(); + net.reset(); world.loadGenerator(zone.generator); zone.rules.accept(state.rules); state.rules.zone = zone; @@ -229,7 +229,7 @@ public class Control implements ApplicationListener, Loadable{ Zone zone = Zones.groundZero; ui.loadAnd(() -> { logic.reset(); - Net.reset(); + net.reset(); world.beginMapLoad(); @@ -284,7 +284,7 @@ public class Control implements ApplicationListener, Loadable{ @Override public void dispose(){ content.dispose(); - Net.dispose(); + net.dispose(); Musics.dispose(); Sounds.dispose(); ui.editor.dispose(); diff --git a/core/src/io/anuke/mindustry/core/GameState.java b/core/src/io/anuke/mindustry/core/GameState.java index 1e755de6c6..0e1c7a2d4b 100644 --- a/core/src/io/anuke/mindustry/core/GameState.java +++ b/core/src/io/anuke/mindustry/core/GameState.java @@ -1,14 +1,12 @@ package io.anuke.mindustry.core; -import io.anuke.arc.Events; -import io.anuke.mindustry.entities.type.BaseUnit; -import io.anuke.mindustry.entities.type.base.BaseDrone; -import io.anuke.mindustry.game.EventType.StateChangeEvent; +import io.anuke.arc.*; +import io.anuke.mindustry.entities.type.*; +import io.anuke.mindustry.entities.type.base.*; +import io.anuke.mindustry.game.EventType.*; import io.anuke.mindustry.game.*; -import io.anuke.mindustry.net.Net; -import static io.anuke.mindustry.Vars.unitGroups; -import static io.anuke.mindustry.Vars.waveTeam; +import static io.anuke.mindustry.Vars.*; public class GameState{ /** Current wave number, can be anything in non-wave modes. */ @@ -29,7 +27,7 @@ public class GameState{ private State state = State.menu; public int enemies(){ - return Net.client() ? enemies : unitGroups[waveTeam.ordinal()].count(b -> !(b instanceof BaseDrone)); + return net.client() ? enemies : unitGroups[waveTeam.ordinal()].count(b -> !(b instanceof BaseDrone)); } public BaseUnit boss(){ @@ -46,7 +44,7 @@ public class GameState{ } public boolean isPaused(){ - return (is(State.paused) && !Net.active()) || (gameOver && !Net.active()); + return (is(State.paused) && !net.active()) || (gameOver && !net.active()); } public boolean is(State astate){ diff --git a/core/src/io/anuke/mindustry/core/Logic.java b/core/src/io/anuke/mindustry/core/Logic.java index abfb994939..70cd86c9de 100644 --- a/core/src/io/anuke/mindustry/core/Logic.java +++ b/core/src/io/anuke/mindustry/core/Logic.java @@ -190,7 +190,7 @@ public class Logic implements ApplicationListener{ } } - if(!Net.client() && state.wavetime <= 0 && state.rules.waves){ + if(!net.client() && state.wavetime <= 0 && state.rules.waves){ runWave(); } @@ -237,7 +237,7 @@ public class Logic implements ApplicationListener{ pathfinder.update(); } - if(!Net.client() && !world.isInvalidMap() && !state.isEditor()){ + if(!net.client() && !world.isInvalidMap() && !state.isEditor()){ checkGameOver(); } } diff --git a/core/src/io/anuke/mindustry/core/NetClient.java b/core/src/io/anuke/mindustry/core/NetClient.java index 9c1828df8e..597352c0bd 100644 --- a/core/src/io/anuke/mindustry/core/NetClient.java +++ b/core/src/io/anuke/mindustry/core/NetClient.java @@ -40,6 +40,7 @@ public class NetClient implements ApplicationListener{ private final static float playerSyncTime = 2; public final static float viewScale = 2f; + private long ping; private Interval timer = new Interval(5); /** Whether the client is currently connecting. */ private boolean connecting = false; @@ -60,7 +61,7 @@ public class NetClient implements ApplicationListener{ public NetClient(){ - Net.handleClient(Connect.class, packet -> { + net.handleClient(Connect.class, packet -> { Log.info("Connecting to server: {0}", packet.addressTCP); player.isAdmin = false; @@ -74,7 +75,7 @@ public class NetClient implements ApplicationListener{ ui.loadfrag.hide(); connecting = false; quiet = true; - Net.disconnect(); + net.disconnect(); }); ConnectPacket c = new ConnectPacket(); @@ -92,10 +93,10 @@ public class NetClient implements ApplicationListener{ return; } - Net.send(c, SendMode.tcp); + net.send(c, SendMode.tcp); }); - Net.handleClient(Disconnect.class, packet -> { + net.handleClient(Disconnect.class, packet -> { if(quietReset) return; connecting = false; @@ -120,14 +121,14 @@ public class NetClient implements ApplicationListener{ } }); - Net.handleClient(WorldStream.class, data -> { + net.handleClient(WorldStream.class, data -> { Log.info("Recieved world data: {0} bytes.", data.stream.available()); NetworkIO.loadWorld(new InflaterInputStream(data.stream)); finishConnecting(); }); - Net.handleClient(InvokePacket.class, packet -> { + net.handleClient(InvokePacket.class, packet -> { packet.writeBuffer.position(0); RemoteReadClient.readPacket(packet.writeBuffer, packet.type); }); @@ -198,6 +199,16 @@ public class NetClient implements ApplicationListener{ return "[#" + player.color.toString().toUpperCase() + "]" + name; } + @Remote(targets = Loc.client) + public static void onPing(Player player, long time){ + Call.onPingResponse(player.id, time); + } + + @Remote(variants = Variant.one) + public static void onPingResponse(long time){ + netClient.ping = Time.timeSinceMillis(time); + } + @Remote(variants = Variant.one) public static void onTraceInfo(Player player, TraceInfo info){ if(player != null){ @@ -233,7 +244,7 @@ public class NetClient implements ApplicationListener{ logic.reset(); ui.chatfrag.clearMessages(); - Net.setClientLoaded(false); + net.setClientLoaded(false); ui.loadfrag.show("$connecting.data"); @@ -241,7 +252,7 @@ public class NetClient implements ApplicationListener{ ui.loadfrag.hide(); netClient.connecting = false; netClient.quiet = true; - Net.disconnect(); + net.disconnect(); }); } @@ -259,7 +270,7 @@ public class NetClient implements ApplicationListener{ @Remote(variants = Variant.one, priority = PacketPriority.low, unreliable = true) public static void onEntitySnapshot(byte groupID, short amount, short dataLen, byte[] data){ try{ - netClient.byteStream.setBytes(Net.decompressSnapshot(data, dataLen)); + netClient.byteStream.setBytes(net.decompressSnapshot(data, dataLen)); DataInputStream input = netClient.dataStream; EntityGroup group = entities.get(groupID); @@ -315,7 +326,7 @@ public class NetClient implements ApplicationListener{ state.wave = wave; state.enemies = enemies; - netClient.byteStream.setBytes(Net.decompressSnapshot(coreData, coreDataLen)); + netClient.byteStream.setBytes(net.decompressSnapshot(coreData, coreDataLen)); DataInputStream input = netClient.dataStream; byte cores = input.readByte(); @@ -337,12 +348,12 @@ public class NetClient implements ApplicationListener{ @Override public void update(){ - if(!Net.client()) return; + if(!net.client()) return; if(!state.is(State.menu)){ if(!connecting) sync(); }else if(!connecting){ - Net.disconnect(); + net.disconnect(); }else{ //...must be connecting timeoutTime += Time.delta(); if(timeoutTime > dataTimeout){ @@ -350,7 +361,7 @@ public class NetClient implements ApplicationListener{ ui.loadfrag.hide(); quiet = true; ui.showError("$disconnect.data"); - Net.disconnect(); + net.disconnect(); timeoutTime = 0f; } } @@ -360,18 +371,22 @@ public class NetClient implements ApplicationListener{ return connecting; } + public int getPing(){ + return (int)ping; + } + private void finishConnecting(){ state.set(State.playing); connecting = false; ui.join.hide(); - Net.setClientLoaded(true); + net.setClientLoaded(true); Core.app.post(Call::connectConfirm); Time.runTask(40f, platform::updateRPC); Core.app.post(() -> ui.loadfrag.hide()); } private void reset(){ - Net.setClientLoaded(false); + net.setClientLoaded(false); removed.clear(); timeoutTime = 0f; connecting = true; @@ -390,13 +405,13 @@ public class NetClient implements ApplicationListener{ /** Disconnects, resetting state to the menu. */ public void disconnectQuietly(){ quiet = true; - Net.disconnect(); + net.disconnect(); } /** Disconnects, causing no further changes or reset.*/ public void disconnectNoReset(){ quiet = quietReset = true; - Net.disconnect(); + net.disconnect(); } /** When set, any disconnects will be ignored and no dialogs will be shown. */ @@ -435,7 +450,7 @@ public class NetClient implements ApplicationListener{ } if(timer.get(1, 60)){ - Net.updatePing(); + Call.onPing(Time.millis()); } } diff --git a/core/src/io/anuke/mindustry/core/NetServer.java b/core/src/io/anuke/mindustry/core/NetServer.java index ffe369e998..0e3c588917 100644 --- a/core/src/io/anuke/mindustry/core/NetServer.java +++ b/core/src/io/anuke/mindustry/core/NetServer.java @@ -19,7 +19,6 @@ import io.anuke.mindustry.game.EventType.*; import io.anuke.mindustry.game.*; import io.anuke.mindustry.gen.*; import io.anuke.mindustry.net.*; -import io.anuke.mindustry.net.Net; import io.anuke.mindustry.net.Administration.*; import io.anuke.mindustry.net.Packets.*; import io.anuke.mindustry.world.*; @@ -41,8 +40,6 @@ public class NetServer implements ApplicationListener{ public final Administration admins = new Administration(); public final CommandHandler clientCommands = new CommandHandler("/"); - /** Maps connection IDs to players. */ - private IntMap connections = new IntMap<>(); private boolean closing = false; private ByteBuffer writeBuffer = ByteBuffer.allocate(127); @@ -54,63 +51,51 @@ public class NetServer implements ApplicationListener{ private DataOutputStream dataStream = new DataOutputStream(syncStream); public NetServer(){ - Events.on(WorldLoadEvent.class, event -> { - if(!headless){ - connections.clear(); - } - }); - Net.handleServer(Connect.class, (id, connect) -> { + net.handleServer(Connect.class, (con, connect) -> { if(admins.isIPBanned(connect.addressTCP)){ - kick(id, KickReason.banned); + con.kick(KickReason.banned); } }); - Net.handleServer(Disconnect.class, (id, packet) -> { - Player player = connections.get(id); - if(player != null){ + net.handleServer(Disconnect.class, (con, packet) -> { + if(con.player != null){ onDisconnect(player, packet.reason); } - connections.remove(id); }); - Net.handleServer(ConnectPacket.class, (id, packet) -> { + net.handleServer(ConnectPacket.class, (con, packet) -> { String uuid = packet.uuid; - NetConnection connection = Net.getConnection(id); - Log.info("\n\nGET CONNECT\n\n"); + if(admins.isIPBanned(con.address)) return; - if(connection == null || - admins.isIPBanned(connection.address)) return; - - if(connection.hasBegunConnecting){ - kick(id, KickReason.idInUse); + if(con.hasBegunConnecting){ + con.kick(KickReason.idInUse); return; } - connection.hasBegunConnecting = true; - PlayerInfo info = admins.getInfo(uuid); - connection.mobile = packet.mobile; + con.hasBegunConnecting = true; + con.mobile = packet.mobile; if(admins.isIDBanned(uuid)){ - kick(id, KickReason.banned); + con.kick(KickReason.banned); return; } if(Time.millis() - info.lastKicked < kickDuration){ - kick(id, KickReason.recentKick); + con.kick(KickReason.recentKick); return; } if(admins.isIDBanned(uuid)){ - kick(id, KickReason.banned); + con.kick(KickReason.banned); return; } if(admins.getPlayerLimit() > 0 && playerGroup.size() >= admins.getPlayerLimit()){ - kick(id, KickReason.playerLimit); + con.kick(KickReason.playerLimit); return; } @@ -119,14 +104,14 @@ public class NetServer implements ApplicationListener{ info.lastName = packet.name; info.id = packet.uuid; admins.save(); - Call.onInfoMessage(id, "You are not whitelisted here."); + Call.onInfoMessage(con.id, "You are not whitelisted here."); Log.info("&lcDo &lywhitelist-add {0}&lc to whitelist the player &lb'{1}'", packet.uuid, packet.name); - kick(id, KickReason.whitelist); + con.kick(KickReason.whitelist); return; } if(packet.versionType == null || ((packet.version == -1 || !packet.versionType.equals(Version.type)) && Version.build != -1 && !admins.allowsCustomClients())){ - kick(id, !Version.type.equals(packet.versionType) ? KickReason.typeMismatch : KickReason.customClient); + con.kick(!Version.type.equals(packet.versionType) ? KickReason.typeMismatch : KickReason.customClient); return; } @@ -135,12 +120,12 @@ public class NetServer implements ApplicationListener{ if(preventDuplicates){ for(Player player : playerGroup.all()){ if(player.name.trim().equalsIgnoreCase(packet.name.trim())){ - kick(id, KickReason.nameInUse); + con.kick(KickReason.nameInUse); return; } if(player.uuid.equals(packet.uuid) || player.usid.equals(packet.usid)){ - kick(id, KickReason.idInUse); + con.kick(KickReason.idInUse); return; } } @@ -149,26 +134,26 @@ public class NetServer implements ApplicationListener{ packet.name = fixName(packet.name); if(packet.name.trim().length() <= 0){ - kick(id, KickReason.nameEmpty); + con.kick(KickReason.nameEmpty); return; } - String ip = Net.getConnection(id).address; + String ip = con.address; admins.updatePlayerJoined(uuid, ip, packet.name); if(packet.version != Version.build && Version.build != -1 && packet.version != -1){ - kick(id, packet.version > Version.build ? KickReason.serverOutdated : KickReason.clientOutdated); + con.kick(packet.version > Version.build ? KickReason.serverOutdated : KickReason.clientOutdated); return; } if(packet.version == -1){ - connection.modclient = true; + con.modclient = true; } Player player = new Player(); player.isAdmin = admins.isAdmin(uuid, packet.usid); - player.con = Net.getConnection(id); + player.con = con; player.usid = packet.usid; player.name = packet.name; player.uuid = uuid; @@ -183,29 +168,28 @@ public class NetServer implements ApplicationListener{ player.write(outputBuffer); }catch(Throwable t){ t.printStackTrace(); - kick(id, KickReason.nameEmpty); + con.kick(KickReason.nameEmpty); return; } + con.player = player; + //playing in pvp mode automatically assigns players to teams if(state.rules.pvp){ player.setTeam(assignTeam(player, playerGroup.all())); Log.info("Auto-assigned player {0} to team {1}.", player.name, player.getTeam()); } - connections.put(id, player); - - sendWorldData(player, id); + sendWorldData(player, con.id); platform.updateRPC(); Events.fire(new PlayerJoin(player)); }); - Net.handleServer(InvokePacket.class, (id, packet) -> { - Player player = connections.get(id); - if(player == null) return; - RemoteReadServer.readPacket(packet.writeBuffer, packet.type, player); + net.handleServer(InvokePacket.class, (con, packet) -> { + if(con.player == null) return; + RemoteReadServer.readPacket(packet.writeBuffer, packet.type, con.player); }); registerCommands(); @@ -277,7 +261,7 @@ public class NetServer implements ApplicationListener{ if(votes >= votesRequired() && target.isAdded() && target.con.isConnected()){ Call.sendMessage(Strings.format("[orange]Vote passed.[scarlet] {0}[orange] will be kicked from the server.", target.name)); admins.getInfo(target.uuid).lastKicked = Time.millis() + kickDuration*1000; - kick(target.con.id, KickReason.vote); + target.con.kick(KickReason.vote); map[0] = null; task.cancel(); return true; @@ -404,7 +388,7 @@ public class NetServer implements ApplicationListener{ NetworkIO.writeWorld(player, def); WorldStream data = new WorldStream(); data.stream = new ByteArrayInputStream(stream.toByteArray()); - Net.sendStream(clientID, data); + net.sendStream(clientID, data); Log.debug("Packed {0} compressed bytes of world data.", stream.size()); } @@ -422,7 +406,6 @@ public class NetServer implements ApplicationListener{ Call.onPlayerDisconnect(player.id); } player.remove(); - netServer.connections.remove(player.con.id); Log.info("&lm[{1}] &lc{0} has disconnected. &lg&fi({2})", player.name, player.uuid, reason); } @@ -539,10 +522,10 @@ public class NetServer implements ApplicationListener{ state.wavetime = 0f; }else if(action == AdminAction.ban){ netServer.admins.banPlayerIP(other.con.address); - netServer.kick(other.con.id, KickReason.banned); + other.con.kick(KickReason.banned); Log.info("&lc{0} has banned {1}.", player.name, other.name); }else if(action == AdminAction.kick){ - netServer.kick(other.con.id, KickReason.kick); + other.con.kick(KickReason.kick); Log.info("&lc{0} has kicked {1}.", player.name, other.name); }else if(action == AdminAction.trace){ TraceInfo info = new TraceInfo(other.con.address, other.uuid, other.con.modclient, other.con.mobile); @@ -580,51 +563,27 @@ public class NetServer implements ApplicationListener{ public void update(){ - if(!headless && !closing && Net.server() && state.is(State.menu)){ + if(!headless && !closing && net.server() && state.is(State.menu)){ closing = true; ui.loadfrag.show("$server.closing"); Time.runTask(5f, () -> { - Net.closeServer(); + net.closeServer(); ui.loadfrag.hide(); closing = false; }); } - if(!state.is(State.menu) && Net.server()){ + if(!state.is(State.menu) && net.server()){ sync(); } } public void kickAll(KickReason reason){ - for(NetConnection con : Net.getConnections()){ - kick(con.id, reason); + for(NetConnection con : net.getConnections()){ + con.kick(reason); } } - public void kick(int connection, KickReason reason){ - NetConnection con = Net.getConnection(connection); - if(con == null){ - Log.err("Cannot kick unknown player!"); - return; - }else{ - Log.info("Kicking connection #{0} / IP: {1}. Reason: {2}", connection, con.address, reason.name()); - } - - Player player = connections.get(con.id); - - if(player != null && (reason == KickReason.kick || reason == KickReason.banned || reason == KickReason.vote) && player.uuid != null){ - PlayerInfo info = admins.getInfo(player.uuid); - info.timesKicked++; - info.lastKicked = Math.max(Time.millis(), info.lastKicked); - } - - Call.onKick(connection, reason); - - Time.runTask(2f, con::close); - - admins.save(); - } - public void writeSnapshot(Player player) throws IOException{ syncStream.reset(); ObjectSet cores = state.teams.get(player.getTeam()).cores; @@ -640,7 +599,7 @@ public class NetServer implements ApplicationListener{ byte[] stateBytes = syncStream.toByteArray(); //write basic state data. - Call.onStateSnapshot(player.con.id, state.wavetime, state.wave, state.enemies(), (short)stateBytes.length, Net.compressSnapshot(stateBytes)); + Call.onStateSnapshot(player.con.id, state.wavetime, state.wave, state.enemies(), (short)stateBytes.length, net.compressSnapshot(stateBytes)); viewport.setSize(player.con.viewWidth, player.con.viewHeight).setCenter(player.con.viewX, player.con.viewY); @@ -671,7 +630,7 @@ public class NetServer implements ApplicationListener{ if(syncStream.size() > maxSnapshotSize){ dataStream.close(); byte[] syncBytes = syncStream.toByteArray(); - Call.onEntitySnapshot(player.con.id, (byte)group.getID(), (short)sent, (short)syncBytes.length, Net.compressSnapshot(syncBytes)); + Call.onEntitySnapshot(player.con.id, (byte)group.getID(), (short)sent, (short)syncBytes.length, net.compressSnapshot(syncBytes)); sent = 0; syncStream.reset(); } @@ -681,7 +640,7 @@ public class NetServer implements ApplicationListener{ dataStream.close(); byte[] syncBytes = syncStream.toByteArray(); - Call.onEntitySnapshot(player.con.id, (byte)group.getID(), (short)sent, (short)syncBytes.length, Net.compressSnapshot(syncBytes)); + Call.onEntitySnapshot(player.con.id, (byte)group.getID(), (short)sent, (short)syncBytes.length, net.compressSnapshot(syncBytes)); } } } @@ -747,7 +706,7 @@ public class NetServer implements ApplicationListener{ NetConnection connection = player.con; - if(connection == null || !connection.isConnected() || !connections.containsKey(connection.id)){ + if(connection == null || !connection.isConnected()){ //player disconnected, call d/c event onDisconnect(player, "disappeared"); return; diff --git a/core/src/io/anuke/mindustry/core/Platform.java b/core/src/io/anuke/mindustry/core/Platform.java index 3556279eee..a54fccf3c8 100644 --- a/core/src/io/anuke/mindustry/core/Platform.java +++ b/core/src/io/anuke/mindustry/core/Platform.java @@ -7,13 +7,17 @@ import io.anuke.arc.function.*; import io.anuke.arc.math.*; import io.anuke.arc.scene.ui.*; import io.anuke.arc.util.serialization.*; +import io.anuke.mindustry.net.Net.*; import io.anuke.mindustry.ui.dialogs.*; import static io.anuke.mindustry.Vars.mobile; public interface Platform{ - /**Steam: Update lobby visibility.*/ + /** Get the networking implementation.*/ + NetProvider getNet(); + + /** Steam: Update lobby visibility.*/ default void updateLobby(){ } @@ -41,11 +45,6 @@ public interface Platform{ }); } - /** Request external read/write perms. Run callback when complete.*/ - default void requestExternalPerms(Runnable callback){ - callback.run(); - } - /** Update discord RPC. */ default void updateRPC(){ } diff --git a/core/src/io/anuke/mindustry/entities/effect/Fire.java b/core/src/io/anuke/mindustry/entities/effect/Fire.java index 8ee58d742a..f48445298b 100644 --- a/core/src/io/anuke/mindustry/entities/effect/Fire.java +++ b/core/src/io/anuke/mindustry/entities/effect/Fire.java @@ -40,7 +40,7 @@ public class Fire extends TimedEntity implements SaveTrait, SyncTrait{ /** Start a fire on the tile. If there already is a file there, refreshes its lifetime. */ public static void create(Tile tile){ - if(Net.client() || tile == null) return; //not clientside. + if(net.client() || tile == null) return; //not clientside. Fire fire = map.get(tile.pos()); @@ -106,7 +106,7 @@ public class Fire extends TimedEntity implements SaveTrait, SyncTrait{ time = Mathf.clamp(time + Time.delta(), 0, lifetime()); map.put(tile.pos(), this); - if(Net.client()){ + if(net.client()){ return; } diff --git a/core/src/io/anuke/mindustry/entities/effect/Puddle.java b/core/src/io/anuke/mindustry/entities/effect/Puddle.java index b9885f5a20..b4d5091b43 100644 --- a/core/src/io/anuke/mindustry/entities/effect/Puddle.java +++ b/core/src/io/anuke/mindustry/entities/effect/Puddle.java @@ -83,7 +83,7 @@ public class Puddle extends SolidEntity implements SaveTrait, Poolable, DrawTrai Puddle p = map.get(tile.pos()); if(p == null){ - if(Net.client()) return; //not clientside. + if(net.client()) return; //not clientside. Puddle puddle = Pools.obtain(Puddle.class, Puddle::new); puddle.tile = tile; @@ -168,7 +168,7 @@ public class Puddle extends SolidEntity implements SaveTrait, Poolable, DrawTrai public void update(){ //no updating happens clientside - if(Net.client()){ + if(net.client()){ amount = Mathf.lerpDelta(amount, targetAmount, 0.15f); }else{ //update code diff --git a/core/src/io/anuke/mindustry/entities/type/BaseUnit.java b/core/src/io/anuke/mindustry/entities/type/BaseUnit.java index 1c38ceb45c..2d35ac82bd 100644 --- a/core/src/io/anuke/mindustry/entities/type/BaseUnit.java +++ b/core/src/io/anuke/mindustry/entities/type/BaseUnit.java @@ -48,7 +48,7 @@ public abstract class BaseUnit extends Unit implements ShooterTrait{ public static void onUnitDeath(BaseUnit unit){ if(unit == null) return; - if(Net.server() || !Net.active()){ + if(net.server() || !net.active()){ UnitDrops.dropItems(unit); } @@ -56,7 +56,7 @@ public abstract class BaseUnit extends Unit implements ShooterTrait{ unit.type.deathSound.at(unit); //visual only. - if(Net.client()){ + if(net.client()){ Tile tile = world.tile(unit.spawner); if(tile != null){ tile.block().unitRemoved(tile, unit); @@ -255,7 +255,7 @@ public abstract class BaseUnit extends Unit implements ShooterTrait{ hitTime -= Time.delta(); - if(Net.client()){ + if(net.client()){ interpolate(); status.update(this); return; @@ -297,7 +297,7 @@ public abstract class BaseUnit extends Unit implements ShooterTrait{ public void removed(){ super.removed(); Tile tile = world.tile(spawner); - if(tile != null && !Net.client()){ + if(tile != null && !net.client()){ tile.block().unitRemoved(tile, this); } diff --git a/core/src/io/anuke/mindustry/entities/type/FlyingUnit.java b/core/src/io/anuke/mindustry/entities/type/FlyingUnit.java index 95fb1821b9..2f1e8d9be0 100644 --- a/core/src/io/anuke/mindustry/entities/type/FlyingUnit.java +++ b/core/src/io/anuke/mindustry/entities/type/FlyingUnit.java @@ -123,7 +123,7 @@ public abstract class FlyingUnit extends BaseUnit{ public void update(){ super.update(); - if(!Net.client()){ + if(!net.client()){ updateRotation(); } wobble(); @@ -176,7 +176,7 @@ public abstract class FlyingUnit extends BaseUnit{ } protected void wobble(){ - if(Net.client()) return; + if(net.client()) return; x += Mathf.sin(Time.time() + id * 999, 25f, 0.05f) * Time.delta(); y += Mathf.cos(Time.time() + id * 999, 25f, 0.05f) * Time.delta(); diff --git a/core/src/io/anuke/mindustry/entities/type/Player.java b/core/src/io/anuke/mindustry/entities/type/Player.java index d443f99ffe..f2dd06b48f 100644 --- a/core/src/io/anuke/mindustry/entities/type/Player.java +++ b/core/src/io/anuke/mindustry/entities/type/Player.java @@ -1,34 +1,30 @@ package io.anuke.mindustry.entities.type; -import io.anuke.annotations.Annotations.Loc; -import io.anuke.annotations.Annotations.Remote; -import io.anuke.arc.Core; -import io.anuke.arc.collection.Queue; -import io.anuke.arc.graphics.Color; +import io.anuke.annotations.Annotations.*; +import io.anuke.arc.*; +import io.anuke.arc.collection.*; +import io.anuke.arc.graphics.*; import io.anuke.arc.graphics.g2d.*; -import io.anuke.arc.math.Angles; -import io.anuke.arc.math.Mathf; +import io.anuke.arc.math.*; import io.anuke.arc.math.geom.*; import io.anuke.arc.scene.ui.layout.*; import io.anuke.arc.util.*; -import io.anuke.arc.util.pooling.Pools; -import io.anuke.mindustry.Vars; +import io.anuke.arc.util.pooling.*; +import io.anuke.mindustry.*; import io.anuke.mindustry.content.*; import io.anuke.mindustry.core.*; import io.anuke.mindustry.entities.*; import io.anuke.mindustry.entities.traits.*; import io.anuke.mindustry.game.*; import io.anuke.mindustry.gen.*; -import io.anuke.mindustry.graphics.Pal; +import io.anuke.mindustry.graphics.*; import io.anuke.mindustry.input.*; -import io.anuke.mindustry.input.InputHandler.PlaceDraw; -import io.anuke.mindustry.io.TypeIO; +import io.anuke.mindustry.input.InputHandler.*; +import io.anuke.mindustry.io.*; import io.anuke.mindustry.net.Administration.*; -import io.anuke.mindustry.net.Net; -import io.anuke.mindustry.net.NetConnection; +import io.anuke.mindustry.net.*; import io.anuke.mindustry.type.*; -import io.anuke.mindustry.world.Block; -import io.anuke.mindustry.world.Tile; +import io.anuke.mindustry.world.*; import io.anuke.mindustry.world.blocks.*; import java.io.*; @@ -50,7 +46,7 @@ public class Player extends Unit implements BuilderMinerTrait, ShooterTrait{ public float baseRotation; public float pointerX, pointerY; public String name = "name"; - public String uuid, usid; + public @Nullable String uuid, usid; public boolean isAdmin, isTransferring, isShooting, isBoosting, isMobile, isTyping; public float boostHeat, shootHeat, destructTime; public boolean achievedFlight; @@ -59,13 +55,13 @@ public class Player extends Unit implements BuilderMinerTrait, ShooterTrait{ public SpawnerTrait spawner, lastSpawner; public int respawns; - public NetConnection con; + public @Nullable NetConnection con; public boolean isLocal = false; public Interval timer = new Interval(6); public TargetTrait target; public TargetTrait moveTarget; - public String lastText; + public @Nullable String lastText; public float textFadeTime; private float walktime, itemtime; @@ -229,7 +225,7 @@ public class Player extends Unit implements BuilderMinerTrait, ShooterTrait{ @Override public void damage(float amount){ hitTime = hitDuration; - if(!Net.client()){ + if(!net.client()){ health -= calculateDamage(amount); } @@ -530,7 +526,7 @@ public class Player extends Unit implements BuilderMinerTrait, ShooterTrait{ spawner = null; } - if(isLocal || Net.server()){ + if(isLocal || net.server()){ avoidOthers(); } @@ -561,7 +557,7 @@ public class Player extends Unit implements BuilderMinerTrait, ShooterTrait{ status.update(this); //status effect updating also happens with non locals for effect purposes updateVelocityStatus(); //velocity too, for visual purposes - if(Net.server()){ + if(net.server()){ updateShooting(); //server simulates player shooting } return; @@ -847,7 +843,7 @@ public class Player extends Unit implements BuilderMinerTrait, ShooterTrait{ }else if(spawner != null && spawner.isValid()){ spawner.updateSpawning(this); }else if(!netServer.isWaitingForPlayers()){ - if(!Net.client()){ + if(!net.client()){ if(lastSpawner != null && lastSpawner.isValid()){ this.spawner = lastSpawner; }else if(getClosestCore() != null){ diff --git a/core/src/io/anuke/mindustry/entities/type/Unit.java b/core/src/io/anuke/mindustry/entities/type/Unit.java index 2bf5f63b17..2a8b5257ad 100644 --- a/core/src/io/anuke/mindustry/entities/type/Unit.java +++ b/core/src/io/anuke/mindustry/entities/type/Unit.java @@ -78,7 +78,7 @@ public abstract class Unit extends DestructibleEntity implements SaveTrait, Targ @Override public void damage(float amount){ - if(!Net.client()){ + if(!net.client()){ super.damage(calculateDamage(amount)); } hitTime = hitDuration; @@ -308,7 +308,7 @@ public abstract class Unit extends DestructibleEntity implements SaveTrait, Targ drownTime = Mathf.clamp(drownTime); - if(drownTime >= 0.999f && !Net.client()){ + if(drownTime >= 0.999f && !net.client()){ damage(health + 1); } @@ -347,7 +347,7 @@ public abstract class Unit extends DestructibleEntity implements SaveTrait, Targ } public void applyEffect(StatusEffect effect, float duration){ - if(dead || Net.client()) return; //effects are synced and thus not applied through clients + if(dead || net.client()) return; //effects are synced and thus not applied through clients status.handleApply(this, effect, duration); } diff --git a/core/src/io/anuke/mindustry/input/DesktopInput.java b/core/src/io/anuke/mindustry/input/DesktopInput.java index caf01eeb25..874e29ae5b 100644 --- a/core/src/io/anuke/mindustry/input/DesktopInput.java +++ b/core/src/io/anuke/mindustry/input/DesktopInput.java @@ -123,7 +123,7 @@ public class DesktopInput extends InputHandler{ @Override public void update(){ - if(Net.active() && Core.input.keyTap(Binding.player_list)){ + if(net.active() && Core.input.keyTap(Binding.player_list)){ ui.listfrag.toggle(); } diff --git a/core/src/io/anuke/mindustry/input/InputHandler.java b/core/src/io/anuke/mindustry/input/InputHandler.java index f611fd9fcf..c64da21582 100644 --- a/core/src/io/anuke/mindustry/input/InputHandler.java +++ b/core/src/io/anuke/mindustry/input/InputHandler.java @@ -49,7 +49,7 @@ public abstract class InputHandler implements InputProcessor{ @Remote(targets = Loc.client, called = Loc.server) public static void dropItem(Player player, float angle){ - if(Net.server() && player.item().amount <= 0){ + if(net.server() && player.item().amount <= 0){ throw new ValidateException(player, "Player cannot drop an item."); } @@ -60,7 +60,7 @@ public abstract class InputHandler implements InputProcessor{ @Remote(targets = Loc.both, forward = true, called = Loc.server) public static void transferInventory(Player player, Tile tile){ if(!player.timer.get(Player.timerTransfer, 40)) return; - if(Net.server() && (player.item().amount <= 0 || player.isTransferring)){ + if(net.server() && (player.item().amount <= 0 || player.isTransferring)){ throw new ValidateException(player, "Player cannot transfer an item."); } diff --git a/core/src/io/anuke/mindustry/net/CrashSender.java b/core/src/io/anuke/mindustry/net/CrashSender.java index b8a5b44854..6970e9521b 100644 --- a/core/src/io/anuke/mindustry/net/CrashSender.java +++ b/core/src/io/anuke/mindustry/net/CrashSender.java @@ -17,6 +17,7 @@ import java.nio.file.Files; import java.nio.file.*; import java.text.*; import java.util.*; +import static io.anuke.mindustry.Vars.*; public class CrashSender{ @@ -78,9 +79,9 @@ public class CrashSender{ //attempt to close connections, if applicable try{ - netActive = Net.active(); - netServer = Net.server(); - Net.dispose(); + netActive = net.active(); + netServer = net.server(); + net.dispose(); }catch(Throwable ignored){ } diff --git a/core/src/io/anuke/mindustry/net/Net.java b/core/src/io/anuke/mindustry/net/Net.java index 4c698214ac..3c3bb00362 100644 --- a/core/src/io/anuke/mindustry/net/Net.java +++ b/core/src/io/anuke/mindustry/net/Net.java @@ -17,20 +17,25 @@ import static io.anuke.mindustry.Vars.*; @SuppressWarnings("unchecked") public class Net{ - private static boolean server; - private static boolean active; - private static boolean clientLoaded; - private static Array packetQueue = new Array<>(); - private static ObjectMap, Consumer> clientListeners = new ObjectMap<>(); - private static ObjectMap, BiConsumer> serverListeners = new ObjectMap<>(); - private static ClientProvider clientProvider; - private static ServerProvider serverProvider; - private static IntMap streams = new IntMap<>(); - private static final LZ4FastDecompressor decompressor = LZ4Factory.fastestInstance().fastDecompressor(); - private static final LZ4Compressor compressor = LZ4Factory.fastestInstance().fastCompressor(); + private boolean server; + private boolean active; + private boolean clientLoaded; + + private final Array packetQueue = new Array<>(); + private final ObjectMap, Consumer> clientListeners = new ObjectMap<>(); + private final ObjectMap, BiConsumer> serverListeners = new ObjectMap<>(); + private final IntMap streams = new IntMap<>(); + + private final NetProvider provider; + private final LZ4FastDecompressor decompressor = LZ4Factory.fastestInstance().fastDecompressor(); + private final LZ4Compressor compressor = LZ4Factory.fastestInstance().fastCompressor(); + + public Net(NetProvider provider){ + this.provider = provider; + } /** Display a network error. Call on the graphics thread. */ - public static void showError(Throwable e){ + public void showError(Throwable e){ if(!headless){ @@ -67,7 +72,7 @@ public class Net{ } ui.loadfrag.hide(); - if(Net.client()){ + if(client()){ netClient.disconnectQuietly(); } } @@ -78,7 +83,7 @@ public class Net{ /** * Sets the client loaded status, or whether it will recieve normal packets from the server. */ - public static void setClientLoaded(boolean loaded){ + public void setClientLoaded(boolean loaded){ clientLoaded = loaded; if(loaded){ @@ -91,7 +96,7 @@ public class Net{ packetQueue.clear(); } - public static void setClientConnected(){ + public void setClientConnected(){ active = true; server = false; } @@ -99,10 +104,10 @@ public class Net{ /** * Connect to an address. */ - public static void connect(String ip, int port, Runnable success){ + public void connect(String ip, int port, Runnable success){ try{ if(!active){ - clientProvider.connect(ip, port, success); + provider.connectClient(ip, port, success); active = true; server = false; }else{ @@ -116,8 +121,8 @@ public class Net{ /** * Host a server at an address. */ - public static void host(int port) throws IOException{ - serverProvider.host(port); + public void host(int port) throws IOException{ + provider.hostServer(port); active = true; server = true; @@ -127,32 +132,32 @@ public class Net{ /** * Closes the server. */ - public static void closeServer(){ + public void closeServer(){ for(NetConnection con : getConnections()){ Call.onKick(con.id, KickReason.serverClose); } - serverProvider.close(); + provider.closeServer(); server = false; active = false; } - public static void reset(){ + public void reset(){ closeServer(); netClient.disconnectNoReset(); } - public static void disconnect(){ - clientProvider.disconnect(); + public void disconnect(){ + provider.disconnectClient(); server = false; active = false; } - public static byte[] compressSnapshot(byte[] input){ + public byte[] compressSnapshot(byte[] input){ return compressor.compress(input); } - public static byte[] decompressSnapshot(byte[] input, int size){ + public byte[] decompressSnapshot(byte[] input, int size){ return decompressor.decompress(input, size); } @@ -160,88 +165,74 @@ public class Net{ * Starts discovering servers on a different thread. * Callback is run on the main libGDX thread. */ - public static void discoverServers(Consumer cons, Runnable done){ - clientProvider.discover(cons, done); + public void discoverServers(Consumer cons, Runnable done){ + provider.discoverServers(cons, done); } /** * Returns a list of all connections IDs. */ - public static Iterable getConnections(){ - return (Iterable)serverProvider.getConnections(); + public Iterable getConnections(){ + return (Iterable)provider.getConnections(); } /** * Returns a connection by ID */ - public static NetConnection getConnection(int id){ - return serverProvider.getByID(id); + public NetConnection getConnection(int id){ + return provider.getConnection(id); } /** * Send an object to all connected clients, or to the server if this is a client. */ - public static void send(Object object, SendMode mode){ + public void send(Object object, SendMode mode){ if(server){ - if(serverProvider != null) serverProvider.sendServer(object, mode); + provider.sendServer(object, mode); }else{ - if(clientProvider != null) clientProvider.sendClient(object, mode); + provider.sendClient(object, mode); } } /** * Send an object to a certain client. Server-side only */ - public static void sendTo(int id, Object object, SendMode mode){ - serverProvider.sendServerTo(id, object, mode); + public void sendTo(int id, Object object, SendMode mode){ + provider.sendServerTo(id, object, mode); } /** * Send an object to everyone EXCEPT certain client. Server-side only */ - public static void sendExcept(int id, Object object, SendMode mode){ - serverProvider.sendServerExcept(id, object, mode); + public void sendExcept(int id, Object object, SendMode mode){ + provider.sendServerExcept(id, object, mode); } /** * Send a stream to a specific client. Server-side only. */ - public static void sendStream(int id, Streamable stream){ - serverProvider.sendServerStream(id, stream); - } - - /** - * Sets the net clientProvider, e.g. what handles sending, recieving and connecting to a server. - */ - public static void setClientProvider(ClientProvider provider){ - Net.clientProvider = provider; - } - - /** - * Sets the net serverProvider, e.g. what handles hosting a server. - */ - public static void setServerProvider(ServerProvider provider){ - Net.serverProvider = provider; + public void sendStream(int id, Streamable stream){ + provider.sendServerStream(id, stream); } /** * Registers a client listener for when an object is recieved. */ - public static void handleClient(Class type, Consumer listener){ + public void handleClient(Class type, Consumer listener){ clientListeners.put(type, listener); } /** * Registers a server listener for when an object is recieved. */ - public static void handleServer(Class type, BiConsumer listener){ - serverListeners.put(type, (BiConsumer)listener); + public void handleServer(Class type, BiConsumer listener){ + serverListeners.put(type, (BiConsumer)listener); } /** * Call to handle a packet being recieved for the client. */ - public static void handleClientReceived(Object object){ + public void handleClientReceived(Object object){ if(object instanceof StreamBegin){ StreamBegin b = (StreamBegin)object; @@ -276,7 +267,7 @@ public class Net{ /** * Call to handle a packet being recieved for the server. */ - public static void handleServerReceived(int connection, Object object){ + public void handleServerReceived(NetConnection connection, Object object){ if(serverListeners.get(object.getClass()) != null){ if(serverListeners.get(object.getClass()) != null) @@ -290,50 +281,33 @@ public class Net{ /** * Pings a host in an new thread. If an error occured, failed() should be called with the exception. */ - public static void pingHost(String address, int port, Consumer valid, Consumer failed){ - clientProvider.pingHost(address, port, valid, failed); - } - - /** - * Update client ping. - */ - public static void updatePing(){ - clientProvider.updatePing(); - } - - /** - * Get the client ping. Only valid after updatePing(). - */ - public static int getPing(){ - return server() ? 0 : clientProvider.getPing(); + public void pingHost(String address, int port, Consumer valid, Consumer failed){ + provider.pingHost(address, port, valid, failed); } /** * Whether the net is active, e.g. whether this is a multiplayer game. */ - public static boolean active(){ + public boolean active(){ return active; } /** * Whether this is a server or not. */ - public static boolean server(){ + public boolean server(){ return server && active; } /** * Whether this is a client or not. */ - public static boolean client(){ + public boolean client(){ return !server && active; } - public static void dispose(){ - if(clientProvider != null) clientProvider.dispose(); - if(serverProvider != null) serverProvider.close(); - clientProvider = null; - serverProvider = null; + public void dispose(){ + provider.dispose(); server = false; active = false; } @@ -343,46 +317,32 @@ public class Net{ } /** Client implementation. */ - public interface ClientProvider{ + public interface NetProvider{ /** Connect to a server. */ - void connect(String ip, int port, Runnable success) throws IOException; + void connectClient(String ip, int port, Runnable success) throws IOException; /** Send an object to the server. */ void sendClient(Object object, SendMode mode); - /** Update the ping. Should be done every second or so. */ - void updatePing(); - - /** Get ping in milliseconds. Will only be valid after a call to updatePing. */ - int getPing(); - /** Disconnect from the server. */ - void disconnect(); + void disconnectClient(); /** * Discover servers. This should run the callback regardless of whether any servers are found. Should not block. * Callback should be run on libGDX main thread. * @param done is the callback that should run after discovery. */ - void discover(Consumer callback, Runnable done); + void discoverServers(Consumer callback, Runnable done); /** Ping a host. If an error occured, failed() should be called with the exception. */ void pingHost(String address, int port, Consumer valid, Consumer failed); - /** Close all connections. */ - default void dispose(){ - disconnect(); - } - } - - /** Server implementation. */ - public interface ServerProvider{ /** Host a server at specified port. */ - void host(int port) throws IOException; + void hostServer(int port) throws IOException; /** Sends a large stream of data to a specific client. */ default void sendServerStream(int id, Streamable stream){ - NetConnection connection = getByID(id); + NetConnection connection = getConnection(id); if(connection == null) return; try{ int cid; @@ -413,7 +373,7 @@ public class Net{ } default void sendServerTo(int id, Object object, SendMode mode){ - NetConnection conn = getByID(id); + NetConnection conn = getConnection(id); if(conn == null){ Log.err("Failed to find connection with ID {0}.", id); return; @@ -429,13 +389,26 @@ public class Net{ } } - /** Close the server connection. */ - void close(); + /** Returns a connection by ID. */ + default NetConnection getConnection(int id){ + for(NetConnection con : getConnections()){ + if(con.id == id){ + return con; + } + } + return null; + } /** Return all connected users. */ Iterable getConnections(); - /** Returns a connection by ID. */ - NetConnection getByID(int id); + /** Close the server connection. */ + void closeServer(); + + /** Close all connections. */ + default void dispose(){ + disconnectClient(); + closeServer(); + } } } diff --git a/core/src/io/anuke/mindustry/net/NetConnection.java b/core/src/io/anuke/mindustry/net/NetConnection.java index ba2a004a03..037cb87c1e 100644 --- a/core/src/io/anuke/mindustry/net/NetConnection.java +++ b/core/src/io/anuke/mindustry/net/NetConnection.java @@ -1,6 +1,14 @@ package io.anuke.mindustry.net; -import io.anuke.mindustry.net.Net.SendMode; +import io.anuke.annotations.Annotations.*; +import io.anuke.arc.util.*; +import io.anuke.mindustry.entities.type.*; +import io.anuke.mindustry.gen.*; +import io.anuke.mindustry.net.Administration.*; +import io.anuke.mindustry.net.Net.*; +import io.anuke.mindustry.net.Packets.*; + +import static io.anuke.mindustry.Vars.netServer; public abstract class NetConnection{ private static int lastID; @@ -10,6 +18,7 @@ public abstract class NetConnection{ public boolean modclient; public boolean mobile; + public @Nullable Player player; /** ID of last recieved client snapshot. */ public int lastRecievedClientSnapshot = -1; @@ -26,6 +35,22 @@ public abstract class NetConnection{ this.address = address; } + public void kick(KickReason reason){ + Log.info("Kicking connection #{0} / IP: {1}. Reason: {2}", this.id, address, reason.name()); + + if(player != null && (reason == KickReason.kick || reason == KickReason.banned || reason == KickReason.vote) && player.uuid != null){ + PlayerInfo info = netServer.admins.getInfo(player.uuid); + info.timesKicked++; + info.lastKicked = Math.max(Time.millis(), info.lastKicked); + } + + Call.onKick(id, reason); + + Time.runTask(2f, this::close); + + netServer.admins.save(); + } + public boolean isConnected(){ return true; } diff --git a/core/src/io/anuke/mindustry/type/Weapon.java b/core/src/io/anuke/mindustry/type/Weapon.java index 4e77b2bc4a..6f8886dd11 100644 --- a/core/src/io/anuke/mindustry/type/Weapon.java +++ b/core/src/io/anuke/mindustry/type/Weapon.java @@ -14,8 +14,8 @@ import io.anuke.mindustry.entities.bullet.*; import io.anuke.mindustry.entities.traits.*; import io.anuke.mindustry.entities.type.*; import io.anuke.mindustry.gen.*; -import io.anuke.mindustry.gen.Sounds; -import io.anuke.mindustry.net.Net; + +import static io.anuke.mindustry.Vars.net; public class Weapon{ public final String name; @@ -73,7 +73,7 @@ public class Weapon{ if(player == null) return; //clients do not see their own shoot events: they are simulated completely clientside to prevent laggy visuals //messing with the firerate or any other stats does not affect the server (take that, script kiddies!) - if(Net.client() && player == Vars.player){ + if(net.client() && player == Vars.player){ return; } @@ -154,7 +154,7 @@ public class Weapon{ } public void shoot(ShooterTrait p, float x, float y, float angle, boolean left){ - if(Net.client()){ + if(net.client()){ //call it directly, don't invoke on server shootDirect(p, x, y, angle, left); }else{ diff --git a/core/src/io/anuke/mindustry/ui/dialogs/DeployDialog.java b/core/src/io/anuke/mindustry/ui/dialogs/DeployDialog.java index 64c7e3792c..a6bf5a1fdb 100644 --- a/core/src/io/anuke/mindustry/ui/dialogs/DeployDialog.java +++ b/core/src/io/anuke/mindustry/ui/dialogs/DeployDialog.java @@ -19,7 +19,6 @@ import io.anuke.mindustry.core.GameState.*; import io.anuke.mindustry.game.Saves.*; import io.anuke.mindustry.graphics.*; import io.anuke.mindustry.io.SaveIO.*; -import io.anuke.mindustry.net.Net; import io.anuke.mindustry.type.*; import io.anuke.mindustry.type.Zone.*; import io.anuke.mindustry.ui.*; @@ -105,7 +104,7 @@ public class DeployDialog extends FloatingDialog{ hide(); ui.loadAnd(() -> { logic.reset(); - Net.reset(); + net.reset(); try{ control.saves.getZoneSlot().load(); state.set(State.playing); diff --git a/core/src/io/anuke/mindustry/ui/dialogs/FloatingDialog.java b/core/src/io/anuke/mindustry/ui/dialogs/FloatingDialog.java index 54a502627e..8a26ae5269 100644 --- a/core/src/io/anuke/mindustry/ui/dialogs/FloatingDialog.java +++ b/core/src/io/anuke/mindustry/ui/dialogs/FloatingDialog.java @@ -26,7 +26,7 @@ public class FloatingDialog extends Dialog{ hidden(() -> { if(shouldPause && !state.is(State.menu)){ - if(!wasPaused || Net.active()){ + if(!wasPaused || net.active()){ state.set(State.playing); } } diff --git a/core/src/io/anuke/mindustry/ui/dialogs/HostDialog.java b/core/src/io/anuke/mindustry/ui/dialogs/HostDialog.java index 2d655fe98a..4ecd9e72c3 100644 --- a/core/src/io/anuke/mindustry/ui/dialogs/HostDialog.java +++ b/core/src/io/anuke/mindustry/ui/dialogs/HostDialog.java @@ -1,14 +1,12 @@ package io.anuke.mindustry.ui.dialogs; -import io.anuke.arc.Core; -import io.anuke.arc.graphics.Color; -import io.anuke.arc.scene.ui.ImageButton; -import io.anuke.arc.util.Strings; -import io.anuke.arc.util.Time; -import io.anuke.mindustry.Vars; -import io.anuke.mindustry.net.Net; +import io.anuke.arc.*; +import io.anuke.arc.graphics.*; +import io.anuke.arc.scene.ui.*; +import io.anuke.arc.util.*; +import io.anuke.mindustry.*; -import java.io.IOException; +import java.io.*; import static io.anuke.mindustry.Vars.*; @@ -65,7 +63,7 @@ public class HostDialog extends FloatingDialog{ ui.loadfrag.show("$hosting"); Time.runTask(5f, () -> { try{ - Net.host(Vars.port); + net.host(Vars.port); player.isAdmin = true; }catch(IOException e){ ui.showError(Core.bundle.format("server.error", Strings.parseException(e, true))); diff --git a/core/src/io/anuke/mindustry/ui/dialogs/JoinDialog.java b/core/src/io/anuke/mindustry/ui/dialogs/JoinDialog.java index 86f0cb5b9d..ef903b0ebc 100644 --- a/core/src/io/anuke/mindustry/ui/dialogs/JoinDialog.java +++ b/core/src/io/anuke/mindustry/ui/dialogs/JoinDialog.java @@ -12,7 +12,6 @@ import io.anuke.arc.scene.ui.layout.*; import io.anuke.arc.util.*; import io.anuke.mindustry.*; import io.anuke.mindustry.game.*; -import io.anuke.mindustry.net.Net; import io.anuke.mindustry.net.*; import io.anuke.mindustry.net.Packets.*; @@ -68,7 +67,7 @@ public class JoinDialog extends FloatingDialog{ refreshRemote(); } add.hide(); - }).disabled(b -> Core.settings.getString("ip").isEmpty() || Net.active()); + }).disabled(b -> Core.settings.getString("ip").isEmpty() || net.active()); add.shown(() -> { add.title.setText(renaming != null ? "$server.edit" : "$server.add"); @@ -173,7 +172,7 @@ public class JoinDialog extends FloatingDialog{ server.content.clear(); server.content.label(() -> Core.bundle.get("server.refreshing") + Strings.animated(Time.time(), 4, 11, ".")); - Net.pingHost(server.ip, server.port, host -> setupServer(server, host), e -> { + net.pingHost(server.ip, server.port, host -> setupServer(server, host), e -> { server.content.clear(); server.content.add("$host.invalid"); }); @@ -276,9 +275,9 @@ public class JoinDialog extends FloatingDialog{ local.clear(); local.background((Drawable)null); local.table("button", t -> t.label(() -> "[accent]" + Core.bundle.get("hosts.discovering.any") + Strings.animated(Time.time(), 4, 10f, ".")).pad(10f)).growX(); - Net.discoverServers(this::addLocalHost, this::finishLocalHosts); + net.discoverServers(this::addLocalHost, this::finishLocalHosts); for(String host : defaultServers){ - Net.pingHost(host, port, this::addLocalHost, e -> {}); + net.pingHost(host, port, this::addLocalHost, e -> {}); } } @@ -325,9 +324,9 @@ public class JoinDialog extends FloatingDialog{ Time.runTask(2f, () -> { logic.reset(); - Net.reset(); + net.reset(); Vars.netClient.beginConnecting(); - Net.connect(ip, port, () -> { + net.connect(ip, port, () -> { hide(); add.hide(); }); diff --git a/core/src/io/anuke/mindustry/ui/dialogs/LoadDialog.java b/core/src/io/anuke/mindustry/ui/dialogs/LoadDialog.java index 1be788d6b8..e9ae270de6 100644 --- a/core/src/io/anuke/mindustry/ui/dialogs/LoadDialog.java +++ b/core/src/io/anuke/mindustry/ui/dialogs/LoadDialog.java @@ -13,7 +13,6 @@ import io.anuke.mindustry.core.GameState.*; import io.anuke.mindustry.game.Saves.*; import io.anuke.mindustry.io.*; import io.anuke.mindustry.io.SaveIO.*; -import io.anuke.mindustry.net.Net; import io.anuke.mindustry.ui.*; import java.io.*; @@ -193,7 +192,7 @@ public class LoadDialog extends FloatingDialog{ ui.loadAnd(() -> { try{ - Net.reset(); + net.reset(); slot.load(); state.set(State.playing); }catch(SaveException e){ diff --git a/core/src/io/anuke/mindustry/ui/dialogs/PausedDialog.java b/core/src/io/anuke/mindustry/ui/dialogs/PausedDialog.java index c55d59c3bc..538c97fb4c 100644 --- a/core/src/io/anuke/mindustry/ui/dialogs/PausedDialog.java +++ b/core/src/io/anuke/mindustry/ui/dialogs/PausedDialog.java @@ -52,7 +52,7 @@ public class PausedDialog extends FloatingDialog{ if(!world.isZone() && !state.isEditor()){ cont.row(); cont.addButton("$savegame", save::show); - cont.addButton("$loadgame", load::show).disabled(b -> Net.active()); + cont.addButton("$loadgame", load::show).disabled(b -> net.active()); } cont.row(); @@ -63,7 +63,7 @@ public class PausedDialog extends FloatingDialog{ }else{ ui.host.show(); } - }).disabled(b -> Net.active()).colspan(2).width(dw * 2 + 20f); + }).disabled(b -> net.active()).colspan(2).width(dw * 2 + 20f); } cont.row(); @@ -82,12 +82,12 @@ public class PausedDialog extends FloatingDialog{ cont.row(); - cont.addRowImageTextButton("$load", "icon-load", isize, load::show).disabled(b -> Net.active()); + cont.addRowImageTextButton("$load", "icon-load", isize, load::show).disabled(b -> net.active()); }else{ cont.row(); } - cont.addRowImageTextButton("$hostserver.mobile", "icon-host", isize, ui.host::show).disabled(b -> Net.active()); + cont.addRowImageTextButton("$hostserver.mobile", "icon-host", isize, ui.host::show).disabled(b -> net.active()); cont.addRowImageTextButton("$quit", "icon-quit", isize, this::showQuitConfirm); } @@ -99,8 +99,8 @@ public class PausedDialog extends FloatingDialog{ Core.settings.put("playedtutorial", true); Core.settings.save(); } - wasClient = Net.client(); - if(Net.client()) netClient.disconnectQuietly(); + wasClient = net.client(); + if(net.client()) netClient.disconnectQuietly(); runExitSave(); hide(); }); diff --git a/core/src/io/anuke/mindustry/ui/dialogs/SettingsMenuDialog.java b/core/src/io/anuke/mindustry/ui/dialogs/SettingsMenuDialog.java index 7ecf0b2f3f..40db1ed6c0 100644 --- a/core/src/io/anuke/mindustry/ui/dialogs/SettingsMenuDialog.java +++ b/core/src/io/anuke/mindustry/ui/dialogs/SettingsMenuDialog.java @@ -36,7 +36,7 @@ public class SettingsMenuDialog extends SettingsDialog{ hidden(() -> { Sounds.back.play(); if(!state.is(State.menu)){ - if(!wasPaused || Net.active()) + if(!wasPaused || net.active()) state.set(State.playing); } }); diff --git a/core/src/io/anuke/mindustry/ui/fragments/ChatFragment.java b/core/src/io/anuke/mindustry/ui/fragments/ChatFragment.java index ca8aab453a..b7a72b93bb 100644 --- a/core/src/io/anuke/mindustry/ui/fragments/ChatFragment.java +++ b/core/src/io/anuke/mindustry/ui/fragments/ChatFragment.java @@ -1,28 +1,23 @@ package io.anuke.mindustry.ui.fragments; -import io.anuke.arc.Core; -import io.anuke.arc.Input.TextInput; -import io.anuke.arc.collection.Array; -import io.anuke.arc.graphics.Color; +import io.anuke.arc.*; +import io.anuke.arc.Input.*; +import io.anuke.arc.collection.*; +import io.anuke.arc.graphics.*; import io.anuke.arc.graphics.g2d.*; -import io.anuke.arc.math.Mathf; -import io.anuke.arc.scene.Group; -import io.anuke.arc.scene.ui.Label; -import io.anuke.arc.scene.ui.Label.LabelStyle; -import io.anuke.arc.scene.ui.TextField; -import io.anuke.arc.scene.ui.layout.Table; -import io.anuke.arc.scene.ui.layout.UnitScl; -import io.anuke.arc.util.Align; -import io.anuke.arc.util.Time; -import io.anuke.mindustry.Vars; -import io.anuke.mindustry.gen.Call; -import io.anuke.mindustry.input.Binding; -import io.anuke.mindustry.net.Net; +import io.anuke.arc.math.*; +import io.anuke.arc.scene.*; +import io.anuke.arc.scene.ui.*; +import io.anuke.arc.scene.ui.Label.*; +import io.anuke.arc.scene.ui.layout.*; +import io.anuke.arc.util.*; +import io.anuke.mindustry.*; +import io.anuke.mindustry.gen.*; +import io.anuke.mindustry.input.*; -import static io.anuke.arc.Core.input; -import static io.anuke.arc.Core.scene; -import static io.anuke.mindustry.Vars.maxTextLength; -import static io.anuke.mindustry.Vars.mobile; +import static io.anuke.arc.Core.*; +import static io.anuke.mindustry.Vars.net; +import static io.anuke.mindustry.Vars.*; public class ChatFragment extends Table{ private final static int messagesShown = 10; @@ -53,7 +48,7 @@ public class ChatFragment extends Table{ font = scene.skin.getFont("default"); visible(() -> { - if(!Net.active() && messages.size > 0){ + if(!net.active() && messages.size > 0){ clearMessages(); if(chatOpen){ @@ -61,12 +56,12 @@ public class ChatFragment extends Table{ } } - return Net.active(); + return net.active(); }); update(() -> { - if(Net.active() && input.keyTap(Binding.chat)){ + if(net.active() && input.keyTap(Binding.chat)){ toggle(); } diff --git a/core/src/io/anuke/mindustry/ui/fragments/HudFragment.java b/core/src/io/anuke/mindustry/ui/fragments/HudFragment.java index d3cc688ebb..9496f8118c 100644 --- a/core/src/io/anuke/mindustry/ui/fragments/HudFragment.java +++ b/core/src/io/anuke/mindustry/ui/fragments/HudFragment.java @@ -23,7 +23,6 @@ import io.anuke.mindustry.game.*; import io.anuke.mindustry.gen.*; import io.anuke.mindustry.graphics.*; import io.anuke.mindustry.input.*; -import io.anuke.mindustry.net.Net; import io.anuke.mindustry.net.Packets.*; import io.anuke.mindustry.type.*; import io.anuke.mindustry.ui.*; @@ -66,13 +65,13 @@ public class HudFragment extends Fragment{ flip = select.addImageButton("icon-arrow-up", style, iconsize, this::toggleMenus).get(); select.addImageButton("icon-pause", style, iconsize, () -> { - if(Net.active()){ + if(net.active()){ ui.listfrag.toggle(); }else{ state.set(state.is(State.paused) ? State.playing : State.paused); } }).name("pause").update(i -> { - if(Net.active()){ + if(net.active()){ i.getStyle().imageUp = Core.scene.skin.getDrawable("icon-players"); }else{ i.setDisabled(false); @@ -81,7 +80,7 @@ public class HudFragment extends Fragment{ }).get(); select.addImageButton("icon-settings", style, iconsize, () -> { - if(Net.active() && mobile){ + if(net.active() && mobile){ if(ui.chatfrag.chatOpen()){ ui.chatfrag.hide(); }else{ @@ -93,7 +92,7 @@ public class HudFragment extends Fragment{ ui.database.show(); } }).update(i -> { - if(Net.active() && mobile){ + if(net.active() && mobile){ i.getStyle().imageUp = Core.scene.skin.getDrawable("icon-chat"); }else{ i.getStyle().imageUp = Core.scene.skin.getDrawable("icon-database"); @@ -244,7 +243,7 @@ public class HudFragment extends Fragment{ info.label(() -> fps.get(Core.graphics.getFramesPerSecond())).left().style("outline"); info.row(); - info.label(() -> ping.get(Net.getPing())).visible(Net::client).left().style("outline"); + info.label(() -> ping.get(netClient.getPing())).visible(net::client).left().style("outline"); }).top().left(); }); @@ -542,7 +541,7 @@ public class HudFragment extends Fragment{ private boolean inLaunchWave(){ return world.isZone() && world.getZone().metCondition() && - !Net.client() && + !net.client() && state.wave % world.getZone().launchPeriod == 0 && !spawner.isSpawning(); } @@ -639,12 +638,12 @@ public class HudFragment extends Fragment{ } private boolean canSkipWave(){ - return state.rules.waves && ((Net.server() || player.isAdmin) || !Net.active()) && state.enemies() == 0 && !spawner.isSpawning() && !state.rules.tutorial; + return state.rules.waves && ((net.server() || player.isAdmin) || !net.active()) && state.enemies() == 0 && !spawner.isSpawning() && !state.rules.tutorial; } private void addPlayButton(Table table){ table.right().addImageButton("icon-play", "right", 30f, () -> { - if(Net.client() && player.isAdmin){ + if(net.client() && player.isAdmin){ Call.onAdminRequest(player, AdminAction.wave); }else if(inLaunchWave()){ ui.showConfirm("$confirm", "$launch.skip.confirm", () -> !canSkipWave(), () -> state.wavetime = 0f); diff --git a/core/src/io/anuke/mindustry/ui/fragments/PlayerListFragment.java b/core/src/io/anuke/mindustry/ui/fragments/PlayerListFragment.java index 2d617d7f34..09796e91b8 100644 --- a/core/src/io/anuke/mindustry/ui/fragments/PlayerListFragment.java +++ b/core/src/io/anuke/mindustry/ui/fragments/PlayerListFragment.java @@ -29,7 +29,7 @@ public class PlayerListFragment extends Fragment{ parent.fill(cont -> { cont.visible(() -> visible); cont.update(() -> { - if(!(Net.active() && !state.is(State.menu))){ + if(!(net.active() && !state.is(State.menu))){ visible = false; return; } @@ -52,8 +52,8 @@ public class PlayerListFragment extends Fragment{ pane.table(menu -> { menu.defaults().growX().height(50f).fillY(); - menu.addButton("$server.bans", ui.bans::show).disabled(b -> Net.client()); - menu.addButton("$server.admins", ui.admins::show).disabled(b -> Net.client()); + menu.addButton("$server.bans", ui.bans::show).disabled(b -> net.client()); + menu.addButton("$server.admins", ui.admins::show).disabled(b -> net.client()); menu.addButton("$close", this::toggle); }).margin(0f).pad(10f).growX(); @@ -72,7 +72,7 @@ public class PlayerListFragment extends Fragment{ playerGroup.all().each(user -> { NetConnection connection = user.con; - if(connection == null && Net.server() && !user.isLocal) return; + if(connection == null && net.server() && !user.isLocal) return; Table button = new Table(); button.left(); @@ -96,9 +96,9 @@ public class PlayerListFragment extends Fragment{ button.labelWrap("[#" + user.color.toString().toUpperCase() + "]" + user.name).width(170f).pad(10); button.add().grow(); - button.addImage("icon-admin").size(iconsize).visible(() -> user.isAdmin && !(!user.isLocal && Net.server())).padRight(5).get().updateVisibility(); + button.addImage("icon-admin").size(iconsize).visible(() -> user.isAdmin && !(!user.isLocal && net.server())).padRight(5).get().updateVisibility(); - if((Net.server() || player.isAdmin) && !user.isLocal && (!user.isAdmin || Net.server())){ + if((net.server() || player.isAdmin) && !user.isLocal && (!user.isAdmin || net.server())){ button.add().growY(); float bs = (h) / 2f; @@ -114,7 +114,7 @@ public class PlayerListFragment extends Fragment{ t.row(); t.addImageButton("icon-admin-small", "clear-toggle-partial", iconsizesmall, () -> { - if(Net.client()) return; + if(net.client()) return; String id = user.uuid; @@ -125,8 +125,8 @@ public class PlayerListFragment extends Fragment{ } }) .update(b -> b.setChecked(user.isAdmin)) - .disabled(b -> Net.client()) - .touchable(() -> Net.client() ? Touchable.disabled : Touchable.enabled) + .disabled(b -> net.client()) + .touchable(() -> net.client() ? Touchable.disabled : Touchable.enabled) .checked(user.isAdmin); t.addImageButton("icon-zoom-small", "clear-partial", iconsizesmall, () -> Call.onAdminRequest(user, AdminAction.trace)); diff --git a/core/src/io/anuke/mindustry/world/Block.java b/core/src/io/anuke/mindustry/world/Block.java index c07e0b4791..706fa2e7bf 100644 --- a/core/src/io/anuke/mindustry/world/Block.java +++ b/core/src/io/anuke/mindustry/world/Block.java @@ -24,7 +24,6 @@ import io.anuke.mindustry.game.*; import io.anuke.mindustry.gen.*; import io.anuke.mindustry.graphics.*; import io.anuke.mindustry.input.InputHandler.*; -import io.anuke.mindustry.net.Net; import io.anuke.mindustry.type.*; import io.anuke.mindustry.ui.*; import io.anuke.mindustry.world.blocks.*; @@ -300,7 +299,7 @@ public class Block extends BlockStorage{ /** Called after the block is placed by anyone. */ @CallSuper public void placed(Tile tile){ - if(Net.client()) return; + if(net.client()) return; if((consumesPower && !outputsPower) || (!consumesPower && outputsPower)){ int range = 10; diff --git a/core/src/io/anuke/mindustry/world/blocks/RespawnBlock.java b/core/src/io/anuke/mindustry/world/blocks/RespawnBlock.java index 03e54e734c..c3813bd66a 100644 --- a/core/src/io/anuke/mindustry/world/blocks/RespawnBlock.java +++ b/core/src/io/anuke/mindustry/world/blocks/RespawnBlock.java @@ -4,10 +4,11 @@ import io.anuke.arc.graphics.g2d.*; import io.anuke.arc.math.*; import io.anuke.mindustry.entities.type.*; import io.anuke.mindustry.graphics.*; -import io.anuke.mindustry.net.*; import io.anuke.mindustry.type.*; import io.anuke.mindustry.world.*; +import static io.anuke.mindustry.Vars.net; + public class RespawnBlock{ public static void drawRespawn(Tile tile, float heat, float progress, float time, Player player, Mech to){ @@ -64,7 +65,7 @@ public class RespawnBlock{ } Draw.reset(); - if(Net.active() && player != null){ + if(net.active() && player != null){ tile.block().drawPlaceText(player.name, tile.x, tile.y - (Math.max((tile.block().size-1)/2, 0)), true); } } diff --git a/core/src/io/anuke/mindustry/world/blocks/power/PowerNode.java b/core/src/io/anuke/mindustry/world/blocks/power/PowerNode.java index 194df6ca88..068b5314c9 100644 --- a/core/src/io/anuke/mindustry/world/blocks/power/PowerNode.java +++ b/core/src/io/anuke/mindustry/world/blocks/power/PowerNode.java @@ -100,7 +100,7 @@ public class PowerNode extends PowerBlock{ @Override public void placed(Tile tile){ - if(Net.client()) return; + if(net.client()) return; Predicate valid = other -> other != null && other != tile && ((!other.block().outputsPower && other.block().consumesPower) || (other.block().outputsPower && !other.block().consumesPower) || other.block() instanceof PowerNode) && linkValid(tile, other) && !other.entity.proximity().contains(tile) && other.entity.power.graph != tile.entity.power.graph; diff --git a/core/src/io/anuke/mindustry/world/blocks/storage/CoreBlock.java b/core/src/io/anuke/mindustry/world/blocks/storage/CoreBlock.java index e63f3121bd..1c6c059e76 100644 --- a/core/src/io/anuke/mindustry/world/blocks/storage/CoreBlock.java +++ b/core/src/io/anuke/mindustry/world/blocks/storage/CoreBlock.java @@ -94,7 +94,7 @@ public class CoreBlock extends StorageBlock{ @Override public void handleItem(Item item, Tile tile, Tile source){ - if(Net.server() || !Net.active()){ + if(net.server() || !net.active()){ super.handleItem(item, tile, source); if(state.rules.tutorial){ Events.fire(new CoreItemDeliverEvent()); diff --git a/core/src/io/anuke/mindustry/world/blocks/units/UnitFactory.java b/core/src/io/anuke/mindustry/world/blocks/units/UnitFactory.java index 39e7fbae19..1b212ebfaa 100644 --- a/core/src/io/anuke/mindustry/world/blocks/units/UnitFactory.java +++ b/core/src/io/anuke/mindustry/world/blocks/units/UnitFactory.java @@ -13,7 +13,6 @@ import io.anuke.mindustry.entities.type.*; import io.anuke.mindustry.gen.Call; import io.anuke.mindustry.graphics.Pal; import io.anuke.mindustry.graphics.Shaders; -import io.anuke.mindustry.net.Net; import io.anuke.mindustry.type.*; import io.anuke.mindustry.ui.Bar; import io.anuke.mindustry.world.Block; @@ -23,6 +22,7 @@ import io.anuke.mindustry.world.consumers.ConsumeType; import io.anuke.mindustry.world.meta.*; import java.io.*; +import static io.anuke.mindustry.Vars.*; public class UnitFactory extends Block{ protected UnitType type; @@ -54,7 +54,7 @@ public class UnitFactory extends Block{ Effects.shake(2f, 3f, entity); Effects.effect(Fx.producesmoke, tile.drawx(), tile.drawy()); - if(!Net.client()){ + if(!net.client()){ BaseUnit unit = factory.type.create(tile.getTeam()); unit.setSpawner(tile); unit.set(tile.drawx() + Mathf.range(4), tile.drawy() + Mathf.range(4)); diff --git a/desktop/src/io/anuke/mindustry/desktop/DesktopLauncher.java b/desktop/src/io/anuke/mindustry/desktop/DesktopLauncher.java index 347a9a622a..8593c9f49c 100644 --- a/desktop/src/io/anuke/mindustry/desktop/DesktopLauncher.java +++ b/desktop/src/io/anuke/mindustry/desktop/DesktopLauncher.java @@ -18,8 +18,8 @@ import io.anuke.mindustry.*; import io.anuke.mindustry.core.GameState.*; import io.anuke.mindustry.desktop.steam.*; import io.anuke.mindustry.game.EventType.*; -import io.anuke.mindustry.net.Net; import io.anuke.mindustry.net.*; +import io.anuke.mindustry.net.Net.*; import java.net.*; import java.util.*; @@ -130,12 +130,7 @@ public class DesktopLauncher extends ClientLauncher{ } if(steam){ - SteamCoreNetImpl net = steamCore = new SteamCoreNetImpl(); - Net.setClientProvider(net); - Net.setServerProvider(net); - }else{ - Net.setClientProvider(new ArcNetClient()); - Net.setServerProvider(new ArcNetServer()); + SteamCoreNetImpl net = steamCore = new SteamCoreNetImpl(new ArcNetImpl()); } } @@ -162,6 +157,11 @@ public class DesktopLauncher extends ClientLauncher{ }); } + @Override + public NetProvider getNet(){ + return steam ? steamCore : new ArcNetImpl(); + } + @Override public void updateLobby(){ steamCore.updateLobby(); @@ -176,7 +176,7 @@ public class DesktopLauncher extends ClientLauncher{ if(!state.is(State.menu)){ String map = world.getMap() == null ? "Unknown Map" : world.isZone() ? world.getZone().localizedName : Strings.capitalize(world.getMap().name()); String mode = state.rules.pvp ? "PvP" : state.rules.attackMode ? "Attack" : "Survival"; - String players = Net.active() && playerGroup.size() > 1 ? " | " + playerGroup.size() + " Players" : ""; + String players = net.active() && playerGroup.size() > 1 ? " | " + playerGroup.size() + " Players" : ""; presence.state = mode + players; diff --git a/desktop/src/io/anuke/mindustry/desktop/steam/SteamCoreNetImpl.java b/desktop/src/io/anuke/mindustry/desktop/steam/SteamCoreNetImpl.java index 7c2cb2a633..39db284506 100644 --- a/desktop/src/io/anuke/mindustry/desktop/steam/SteamCoreNetImpl.java +++ b/desktop/src/io/anuke/mindustry/desktop/steam/SteamCoreNetImpl.java @@ -9,10 +9,9 @@ import io.anuke.arc.collection.*; import io.anuke.arc.function.*; import io.anuke.arc.util.*; import io.anuke.arc.util.pooling.*; -import io.anuke.mindustry.game.*; import io.anuke.mindustry.game.EventType.*; import io.anuke.mindustry.game.Version; -import io.anuke.mindustry.net.Net; +import io.anuke.mindustry.game.*; import io.anuke.mindustry.net.*; import io.anuke.mindustry.net.Net.*; import io.anuke.mindustry.net.Packets.*; @@ -23,11 +22,13 @@ import java.util.concurrent.*; import static io.anuke.mindustry.Vars.*; -public class SteamCoreNetImpl implements SteamNetworkingCallback, SteamMatchmakingCallback, SteamFriendsCallback, ClientProvider, ServerProvider{ +public class SteamCoreNetImpl implements SteamNetworkingCallback, SteamMatchmakingCallback, SteamFriendsCallback, NetProvider{ public final SteamNetworking snet = new SteamNetworking(this); public final SteamMatchmaking smat = new SteamMatchmaking(this); public final SteamFriends friends = new SteamFriends(this); + final NetProvider provider; + final PacketSerializer serializer = new PacketSerializer(); final ByteBuffer writeBuffer = ByteBuffer.allocateDirect(1024 * 4); final ByteBuffer readBuffer = ByteBuffer.allocateDirect(1024 * 4); @@ -40,7 +41,9 @@ public class SteamCoreNetImpl implements SteamNetworkingCallback, SteamMatchmaki Consumer lobbyCallback; Runnable lobbyDoneCallback, joinCallback; - public SteamCoreNetImpl(){ + public SteamCoreNetImpl(NetProvider provider){ + this.provider = provider; + Events.on(ClientLoadEvent.class, e -> Core.app.addListener(new ApplicationListener(){ //read packets int length; @@ -55,15 +58,15 @@ public class SteamCoreNetImpl implements SteamNetworkingCallback, SteamMatchmaki int fromID = from.getAccountID(); Object output = serializer.read(readBuffer); - if(Net.server()){ + if(net.server()){ SteamConnection con = steamConnections.get(fromID); if(con != null){ - Net.handleServerReceived(con.id, output); + net.handleServerReceived(con, output); }else{ Log.err("Unknown user with ID: {0}", fromID); } }else if(currentServer != null && fromID == currentServer.getAccountID()){ - Net.handleClientReceived(output); + net.handleClientReceived(output); } }catch(SteamException e){ e.printStackTrace(); @@ -73,68 +76,71 @@ public class SteamCoreNetImpl implements SteamNetworkingCallback, SteamMatchmaki })); Events.on(WaveEvent.class, e -> { - if(currentLobby != null && Net.server()){ + if(currentLobby != null && net.server()){ smat.setLobbyData(currentLobby, "wave", state.wave + ""); } }); } + public boolean isSteamClient(){ + return currentServer != null; + } + @Override - public void connect(String ip, int port, Runnable success) throws IOException{ + public void connectClient(String ip, int port, Runnable success) throws IOException{ if(ip.startsWith("steam:")){ String lobbyname = ip.substring("steam:".length()); SteamID lobby = lobbyIDs.get(lobbyname); if(lobby == null) throw new IOException("Lobby not found."); joinCallback = success; smat.joinLobby(lobby); + }else{ + provider.connectClient(ip, port, success); } - //else, no } @Override public void sendClient(Object object, SendMode mode){ - if(currentServer == null){ - Log.info("Not connected, quitting."); - return; + if(isSteamClient()){ + if(currentServer == null){ + Log.info("Not connected, quitting."); + return; + } + + try{ + writeBuffer.limit(writeBuffer.capacity()); + writeBuffer.position(0); + serializer.write(writeBuffer, object); + writeBuffer.flip(); + + snet.sendP2PPacket(currentServer, writeBuffer, mode == SendMode.tcp ? P2PSend.Reliable : P2PSend.UnreliableNoDelay, 0); + }catch(Exception e){ + net.showError(e); + } + Pools.free(object); + }else{ + provider.sendClient(object, mode); } - - try{ - writeBuffer.limit(writeBuffer.capacity()); - writeBuffer.position(0); - serializer.write(writeBuffer, object); - writeBuffer.flip(); - - snet.sendP2PPacket(currentServer, writeBuffer, mode == SendMode.tcp ? P2PSend.Reliable : P2PSend.UnreliableNoDelay, 0); - }catch(Exception e){ - Net.showError(e); - } - Pools.free(object); } - @Override - public void updatePing(){ - //no - } @Override - public int getPing(){ - //absolutely not - return 0; - } - - @Override - public void disconnect(){ - if(currentLobby != null){ - smat.leaveLobby(currentLobby); - snet.closeP2PSessionWithUser(currentServer); - currentServer = null; - currentLobby = null; - Net.handleClientReceived(new Disconnect()); + public void disconnectClient(){ + if(isSteamClient()){ + if(currentLobby != null){ + smat.leaveLobby(currentLobby); + snet.closeP2PSessionWithUser(currentServer); + currentServer = null; + currentLobby = null; + net.handleClientReceived(new Disconnect()); + } + }else{ + provider.disconnectClient(); } } @Override - public void discover(Consumer callback, Runnable done){ + public void discoverServers(Consumer callback, Runnable done){ smat.addRequestLobbyListResultCountFilter(32); smat.requestLobbyList(); lobbyCallback = callback; @@ -147,18 +153,18 @@ public class SteamCoreNetImpl implements SteamNetworkingCallback, SteamMatchmaki } @Override - public void host(int port) throws IOException{ + public void hostServer(int port) throws IOException{ smat.createLobby(Core.settings.getBool("publichost") ? LobbyType.Public : LobbyType.FriendsOnly, 32); } public void updateLobby(){ - if(currentLobby != null && Net.server()){ + if(currentLobby != null && net.server()){ smat.setLobbyType(currentLobby, Core.settings.getBool("publichost") ? LobbyType.Public : LobbyType.FriendsOnly); } } @Override - public void close(){ + public void closeServer(){ if(currentLobby != null){ smat.leaveLobby(currentLobby); for(SteamConnection con : steamConnections.values()){ @@ -175,18 +181,6 @@ public class SteamCoreNetImpl implements SteamNetworkingCallback, SteamMatchmaki return connections; } - @Override - public NetConnection getByID(int id){ - for(int i = 0; i < connections.size(); i++){ - SteamConnection con = connections.get(i); - if(con.id == id){ - return con; - } - } - - return null; - } - void disconnectSteamUser(SteamID steamid){ //a client left int sid = steamid.getAccountID(); @@ -194,7 +188,7 @@ public class SteamCoreNetImpl implements SteamNetworkingCallback, SteamMatchmaki if(steamConnections.containsKey(sid)){ SteamConnection con = steamConnections.get(sid); - Net.handleServerReceived(con.id, new Disconnect()); + net.handleServerReceived(con, new Disconnect()); steamConnections.remove(sid); connections.remove(con); } @@ -210,7 +204,7 @@ public class SteamCoreNetImpl implements SteamNetworkingCallback, SteamMatchmaki Log.info("lobby invite {0} {1} {2}", steamIDLobby.getAccountID(), steamIDUser.getAccountID(), gameID); //ignore invites when hosting. - if(Net.server()) return; + if(net.server()) return; ui.showConfirm("Someone has invited you to a game.", "But do you accept?", () -> { smat.joinLobby(steamIDLobby); @@ -231,8 +225,8 @@ public class SteamCoreNetImpl implements SteamNetworkingCallback, SteamMatchmaki Connect con = new Connect(); con.addressTCP = "steam:" + currentServer.getAccountID(); - Net.setClientConnected(); - Net.handleClientReceived(con); + net.setClientConnected(); + net.handleClientReceived(con); } @Override @@ -244,11 +238,11 @@ public class SteamCoreNetImpl implements SteamNetworkingCallback, SteamMatchmaki public void onLobbyChatUpdate(SteamID lobby, SteamID who, SteamID changer, ChatMemberStateChange change){ Log.info("lobby {0}: {1} caused {2}'s change: {3}", lobby.getAccountID(), who.getAccountID(), changer.getAccountID(), change); if(change == ChatMemberStateChange.Disconnected || change == ChatMemberStateChange.Left){ - if(Net.client()){ + if(net.client()){ Log.info("Current host left."); //host left, leave as well if(who == currentServer){ - disconnect(); + net.disconnect(); } }else{ //a client left @@ -306,7 +300,7 @@ public class SteamCoreNetImpl implements SteamNetworkingCallback, SteamMatchmaki @Override public void onLobbyCreated(SteamResult result, SteamID steamID){ - if(!Net.server()){ + if(!net.server()){ Log.info("Lobby created on server: {0}, ignoring.", steamID); return; } @@ -334,24 +328,24 @@ public class SteamCoreNetImpl implements SteamNetworkingCallback, SteamMatchmaki @Override public void onP2PSessionConnectFail(SteamID steamIDRemote, P2PSessionError sessionError){ - if(Net.server()){ + if(net.server()){ Log.info("{0} has disconnected: {1}", steamIDRemote.getAccountID(), sessionError); disconnectSteamUser(steamIDRemote); }else if(steamIDRemote == currentServer){ Log.info("Disconnected! {1}: {0}", steamIDRemote.getAccountID(), sessionError); - Net.handleClientReceived(new Disconnect()); + net.handleClientReceived(new Disconnect()); } } @Override public void onP2PSessionRequest(SteamID steamIDRemote){ Log.info("Connection request: {0}", steamIDRemote.getAccountID()); - if(currentServer != null && !Net.server()){ + if(currentServer != null && !net.server()){ Log.info("Am client"); if(steamIDRemote == currentServer){ snet.acceptP2PSessionWithUser(steamIDRemote); } - }else if(Net.server()){ + }else if(net.server()){ Log.info("Am server, accepting request."); //accept users on request if(!steamConnections.containsKey(steamIDRemote.getAccountID())){ @@ -364,7 +358,7 @@ public class SteamCoreNetImpl implements SteamNetworkingCallback, SteamMatchmaki steamConnections.put(steamIDRemote.getAccountID(), con); connections.add(con); - Net.handleServerReceived(c.id, c); + net.handleServerReceived(con, c); } snet.acceptP2PSessionWithUser(steamIDRemote); diff --git a/ios/src/io/anuke/mindustry/IOSLauncher.java b/ios/src/io/anuke/mindustry/IOSLauncher.java index 2977bd28da..c860cb09b4 100644 --- a/ios/src/io/anuke/mindustry/IOSLauncher.java +++ b/ios/src/io/anuke/mindustry/IOSLauncher.java @@ -10,8 +10,8 @@ import io.anuke.arc.util.io.*; import io.anuke.mindustry.game.EventType.*; import io.anuke.mindustry.game.Saves.*; import io.anuke.mindustry.io.*; -import io.anuke.mindustry.net.Net; import io.anuke.mindustry.net.*; +import io.anuke.mindustry.net.Net.*; import org.robovm.apple.foundation.*; import org.robovm.apple.uikit.*; import org.robovm.objc.block.*; @@ -28,8 +28,6 @@ public class IOSLauncher extends IOSApplication.Delegate{ @Override protected IOSApplication createApplication(){ - Net.setClientProvider(new ArcNetClient()); - Net.setServerProvider(new ArcNetServer()); if(UIDevice.getCurrentDevice().getUserInterfaceIdiom() == UIUserInterfaceIdiom.Pad){ UnitScl.dp.addition = 0.5f; @@ -40,6 +38,11 @@ public class IOSLauncher extends IOSApplication.Delegate{ IOSApplicationConfiguration config = new IOSApplicationConfiguration(); return new IOSApplication(new ClientLauncher(){ + @Override + public NetProvider getNet(){ + return new ArcNetImpl(); + } + @Override public void showFileChooser(boolean open, String extension, Consumer cons){ UIDocumentBrowserViewController cont = new UIDocumentBrowserViewController(NSArray.fromStrings("public.archive")); diff --git a/net/src/io/anuke/mindustry/net/ArcNetClient.java b/net/src/io/anuke/mindustry/net/ArcNetClient.java deleted file mode 100644 index 08124d4f5f..0000000000 --- a/net/src/io/anuke/mindustry/net/ArcNetClient.java +++ /dev/null @@ -1,192 +0,0 @@ -package io.anuke.mindustry.net; - -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.*; - -import java.io.*; -import java.net.*; -import java.nio.*; -import java.nio.channels.*; - -import static io.anuke.mindustry.Vars.*; - -public class ArcNetClient implements ClientProvider{ - final Client client; - final Supplier packetSupplier = () -> new DatagramPacket(new byte[256], 256); - - public ArcNetClient(){ - client = new Client(8192, 4096, new PacketSerializer()); - client.setDiscoveryPacket(packetSupplier); - - NetListener listener = new NetListener(){ - @Override - public void connected(Connection connection){ - Connect c = new Connect(); - c.addressTCP = connection.getRemoteAddressTCP().getAddress().getHostAddress(); - c.id = connection.getID(); - if(connection.getRemoteAddressTCP() != null) c.addressTCP = connection.getRemoteAddressTCP().toString(); - - Core.app.post(() -> Net.handleClientReceived(c)); - } - - @Override - public void disconnected(Connection connection, DcReason reason){ - if(connection.getLastProtocolError() != null){ - netClient.setQuiet(); - } - - Disconnect c = new Disconnect(); - c.reason = reason.toString(); - Core.app.post(() -> Net.handleClientReceived(c)); - } - - @Override - public void received(Connection connection, Object object){ - if(object instanceof FrameworkMessage) return; - - Core.app.post(() -> { - try{ - Net.handleClientReceived(object); - }catch(Exception e){ - handleException(e); - } - }); - - } - }; - - client.addListener(listener); - } - - private static boolean isLocal(InetAddress addr){ - if(addr.isAnyLocalAddress() || addr.isLoopbackAddress()) return true; - - try{ - return NetworkInterface.getByInetAddress(addr) != null; - }catch(Exception e){ - return false; - } - } - - @Override - public void connect(String ip, int port, Runnable success){ - Threads.daemon(() -> { - try{ - //just in case - client.stop(); - - Threads.daemon("Net Client", () -> { - try{ - client.run(); - }catch(Exception e){ - if(!(e instanceof ClosedSelectorException)) handleException(e); - } - }); - - client.connect(5000, ip, port, port); - success.run(); - }catch(Exception e){ - handleException(e); - } - }); - } - - @Override - public void disconnect(){ - client.close(); - } - - @Override - public void sendClient(Object object, SendMode mode){ - try{ - if(mode == SendMode.tcp){ - client.sendTCP(object); - }else{ - client.sendUDP(object); - } - //sending things can cause an under/overflow, catch it and disconnect instead of crashing - }catch(BufferOverflowException | BufferUnderflowException e){ - Net.showError(e); - } - - Pools.free(object); - } - - @Override - public void updatePing(){ - client.updateReturnTripTime(); - } - - @Override - public int getPing(){ - return client.getReturnTripTime(); - } - - @Override - public void pingHost(String address, int port, Consumer valid, Consumer invalid){ - Threads.daemon(() -> { - try{ - DatagramSocket socket = new DatagramSocket(); - socket.send(new DatagramPacket(new byte[]{-2, 1}, 2, InetAddress.getByName(address), port)); - socket.setSoTimeout(2000); - - DatagramPacket packet = packetSupplier.get(); - socket.receive(packet); - - ByteBuffer buffer = ByteBuffer.wrap(packet.getData()); - Host host = NetworkIO.readServerData(packet.getAddress().getHostAddress(), buffer); - - Core.app.post(() -> valid.accept(host)); - }catch(Exception e){ - Core.app.post(() -> invalid.accept(e)); - } - }); - } - - @Override - public void discover(Consumer callback, Runnable done){ - Array foundAddresses = new Array<>(); - client.discoverHosts(port, multicastGroup, multicastPort, 3000, packet -> { - Core.app.post(() -> { - try{ - if(foundAddresses.contains(address -> address.equals(packet.getAddress()) || (isLocal(address) && isLocal(packet.getAddress())))){ - return; - } - ByteBuffer buffer = ByteBuffer.wrap(packet.getData()); - Host host = NetworkIO.readServerData(packet.getAddress().getHostAddress(), buffer); - callback.accept(host); - foundAddresses.add(packet.getAddress()); - }catch(Exception e){ - //don't crash when there's an error pinging a a server or parsing data - e.printStackTrace(); - } - }); - }, () -> Core.app.post(done)); - } - - @Override - public void dispose(){ - try{ - client.dispose(); - }catch(IOException e){ - throw new RuntimeException(e); - } - } - - private void handleException(Exception e){ - if(e instanceof ArcNetException){ - Core.app.post(() -> Net.showError(new IOException("mismatch"))); - }else if(e instanceof ClosedChannelException){ - Core.app.post(() -> Net.showError(new IOException("alreadyconnected"))); - }else{ - Core.app.post(() -> Net.showError(e)); - } - } - -} \ No newline at end of file diff --git a/net/src/io/anuke/mindustry/net/ArcNetImpl.java b/net/src/io/anuke/mindustry/net/ArcNetImpl.java new file mode 100644 index 0000000000..3609264947 --- /dev/null +++ b/net/src/io/anuke/mindustry/net/ArcNetImpl.java @@ -0,0 +1,351 @@ +package io.anuke.mindustry.net; + +import io.anuke.arc.*; +import io.anuke.arc.collection.*; +import io.anuke.arc.function.*; +import io.anuke.arc.net.*; +import io.anuke.arc.util.*; +import io.anuke.arc.util.async.*; +import io.anuke.arc.util.pooling.*; +import io.anuke.mindustry.net.Net.*; +import io.anuke.mindustry.net.Packets.*; + +import java.io.*; +import java.net.*; +import java.nio.*; +import java.nio.channels.*; +import java.util.concurrent.*; + +import static io.anuke.mindustry.Vars.*; + +public class ArcNetImpl implements NetProvider{ + final Client client; + final Supplier packetSupplier = () -> new DatagramPacket(new byte[256], 256); + + final Server server; + final CopyOnWriteArrayList connections = new CopyOnWriteArrayList<>(); + Thread serverThread; + + public ArcNetImpl(){ + client = new Client(8192, 4096, new PacketSerializer()); + client.setDiscoveryPacket(packetSupplier); + client.addListener(new NetListener(){ + @Override + public void connected(Connection connection){ + Connect c = new Connect(); + c.addressTCP = connection.getRemoteAddressTCP().getAddress().getHostAddress(); + c.id = connection.getID(); + if(connection.getRemoteAddressTCP() != null) c.addressTCP = connection.getRemoteAddressTCP().toString(); + + Core.app.post(() -> net.handleClientReceived(c)); + } + + @Override + public void disconnected(Connection connection, DcReason reason){ + if(connection.getLastProtocolError() != null){ + netClient.setQuiet(); + } + + Disconnect c = new Disconnect(); + c.reason = reason.toString(); + Core.app.post(() -> net.handleClientReceived(c)); + } + + @Override + public void received(Connection connection, Object object){ + if(object instanceof FrameworkMessage) return; + + Core.app.post(() -> { + try{ + net.handleClientReceived(object); + }catch(Exception e){ + handleException(e); + } + }); + + } + }); + + server = new Server(4096 * 2, 4096, new PacketSerializer()); + server.setMulticast(multicastGroup, multicastPort); + server.setDiscoveryHandler((address, handler) -> { + ByteBuffer buffer = NetworkIO.writeServerData(); + buffer.position(0); + handler.respond(buffer); + }); + + server.addListener(new NetListener(){ + + @Override + public void connected(Connection connection){ + String ip = connection.getRemoteAddressTCP().getAddress().getHostAddress(); + + ArcConnection kn = new ArcConnection(ip, connection); + + Connect c = new Connect(); + c.id = kn.id; + c.addressTCP = ip; + + Log.debug("&bRecieved connection: {0}", c.addressTCP); + + connections.add(kn); + Core.app.post(() -> net.handleServerReceived(kn, c)); + } + + @Override + public void disconnected(Connection connection, DcReason reason){ + ArcConnection k = getByArcID(connection.getID()); + if(k == null) return; + + Disconnect c = new Disconnect(); + c.id = k.id; + c.reason = reason.toString(); + + Core.app.post(() -> { + net.handleServerReceived(k, c); + connections.remove(k); + }); + } + + @Override + public void received(Connection connection, Object object){ + ArcConnection k = getByArcID(connection.getID()); + if(object instanceof FrameworkMessage || k == null) return; + + Core.app.post(() -> { + try{ + net.handleServerReceived(k, object); + }catch(RuntimeException e){ + if(e.getCause() instanceof ValidateException){ + ValidateException v = (ValidateException)e.getCause(); + Log.err("Validation failed: {0} ({1})", v.player.name, v.getMessage()); + }else{ + e.printStackTrace(); + } + }catch(Exception e){ + e.printStackTrace(); + } + }); + } + }); + } + + private static boolean isLocal(InetAddress addr){ + if(addr.isAnyLocalAddress() || addr.isLoopbackAddress()) return true; + + try{ + return NetworkInterface.getByInetAddress(addr) != null; + }catch(Exception e){ + return false; + } + } + + @Override + public void connectClient(String ip, int port, Runnable success){ + Threads.daemon(() -> { + try{ + //just in case + client.stop(); + + Threads.daemon("Net Client", () -> { + try{ + client.run(); + }catch(Exception e){ + if(!(e instanceof ClosedSelectorException)) handleException(e); + } + }); + + client.connect(5000, ip, port, port); + success.run(); + }catch(Exception e){ + handleException(e); + } + }); + } + + @Override + public void disconnectClient(){ + client.close(); + } + + @Override + public void sendClient(Object object, SendMode mode){ + try{ + if(mode == SendMode.tcp){ + client.sendTCP(object); + }else{ + client.sendUDP(object); + } + //sending things can cause an under/overflow, catch it and disconnect instead of crashing + }catch(BufferOverflowException | BufferUnderflowException e){ + net.showError(e); + } + + Pools.free(object); + } + + @Override + public void pingHost(String address, int port, Consumer valid, Consumer invalid){ + Threads.daemon(() -> { + try{ + DatagramSocket socket = new DatagramSocket(); + socket.send(new DatagramPacket(new byte[]{-2, 1}, 2, InetAddress.getByName(address), port)); + socket.setSoTimeout(2000); + + DatagramPacket packet = packetSupplier.get(); + socket.receive(packet); + + ByteBuffer buffer = ByteBuffer.wrap(packet.getData()); + Host host = NetworkIO.readServerData(packet.getAddress().getHostAddress(), buffer); + + Core.app.post(() -> valid.accept(host)); + }catch(Exception e){ + Core.app.post(() -> invalid.accept(e)); + } + }); + } + + @Override + public void discoverServers(Consumer callback, Runnable done){ + Array foundAddresses = new Array<>(); + client.discoverHosts(port, multicastGroup, multicastPort, 3000, packet -> { + Core.app.post(() -> { + try{ + if(foundAddresses.contains(address -> address.equals(packet.getAddress()) || (isLocal(address) && isLocal(packet.getAddress())))){ + return; + } + ByteBuffer buffer = ByteBuffer.wrap(packet.getData()); + Host host = NetworkIO.readServerData(packet.getAddress().getHostAddress(), buffer); + callback.accept(host); + foundAddresses.add(packet.getAddress()); + }catch(Exception e){ + //don't crash when there's an error pinging a a server or parsing data + e.printStackTrace(); + } + }); + }, () -> Core.app.post(done)); + } + + @Override + public void dispose(){ + try{ + client.dispose(); + }catch(IOException e){ + throw new RuntimeException(e); + } + } + + @Override + public Iterable getConnections(){ + return connections; + } + + @Override + public void sendServerStream(int id, Streamable stream){ + ArcConnection connection = (ArcConnection)getConnection(id); + if(connection == null) return; + + connection.connection.addListener(new InputStreamSender(stream.stream, 512){ + int id; + + @Override + 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; + } + + @Override + 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. + } + }); + } + + @Override + public void hostServer(int port) throws IOException{ + connections.clear(); + server.bind(port, port); + + serverThread = new Thread(() -> { + try{ + server.run(); + }catch(Throwable e){ + if(!(e instanceof ClosedSelectorException)) Threads.throwAppException(e); + } + }, "Net Server"); + serverThread.setDaemon(true); + serverThread.start(); + } + + @Override + public void closeServer(){ + connections.clear(); + Threads.daemon(server::stop); + } + + ArcConnection getByArcID(int id){ + for(int i = 0; i < connections.size(); i++){ + ArcConnection con = connections.get(i); + if(con.connection != null && con.connection.getID() == id){ + return con; + } + } + + return null; + } + + class ArcConnection extends NetConnection{ + public final Connection connection; + + public ArcConnection(String address, Connection connection){ + super(address); + this.connection = connection; + } + + @Override + public boolean isConnected(){ + return connection.isConnected(); + } + + @Override + public void send(Object object, SendMode mode){ + try{ + if(mode == SendMode.tcp){ + connection.sendTCP(object); + }else{ + connection.sendUDP(object); + } + }catch(Exception e){ + Log.err(e); + Log.info("Error sending packet. Disconnecting invalid client!"); + connection.close(DcReason.error); + + ArcConnection k = getByArcID(connection.getID()); + if(k != null) connections.remove(k); + } + } + + @Override + public void close(){ + if(connection.isConnected()) connection.close(DcReason.closed); + } + } + + private void handleException(Exception e){ + if(e instanceof ArcNetException){ + Core.app.post(() -> net.showError(new IOException("mismatch"))); + }else if(e instanceof ClosedChannelException){ + Core.app.post(() -> net.showError(new IOException("alreadyconnected"))); + }else{ + Core.app.post(() -> net.showError(e)); + } + } + +} \ No newline at end of file diff --git a/net/src/io/anuke/mindustry/net/ArcNetServer.java b/net/src/io/anuke/mindustry/net/ArcNetServer.java deleted file mode 100644 index 59a4416ad7..0000000000 --- a/net/src/io/anuke/mindustry/net/ArcNetServer.java +++ /dev/null @@ -1,204 +0,0 @@ -package io.anuke.mindustry.net; - -import io.anuke.arc.*; -import io.anuke.arc.net.*; -import io.anuke.arc.util.*; -import io.anuke.arc.util.async.*; -import io.anuke.mindustry.net.Net.*; -import io.anuke.mindustry.net.Packets.*; - -import java.io.*; -import java.nio.*; -import java.nio.channels.*; -import java.util.concurrent.*; - -import static io.anuke.mindustry.Vars.*; - -public class ArcNetServer implements ServerProvider{ - final Server server; - final CopyOnWriteArrayList connections = new CopyOnWriteArrayList<>(); - Thread serverThread; - - public ArcNetServer(){ - server = new Server(4096 * 2, 4096, new PacketSerializer()); - server.setMulticast(multicastGroup, multicastPort); - server.setDiscoveryHandler((address, handler) -> { - ByteBuffer buffer = NetworkIO.writeServerData(); - buffer.position(0); - handler.respond(buffer); - }); - - NetListener listener = new NetListener(){ - - @Override - public void connected(Connection connection){ - String ip = connection.getRemoteAddressTCP().getAddress().getHostAddress(); - - ArcConnection kn = new ArcConnection(ip, connection); - - Connect c = new Connect(); - c.id = kn.id; - c.addressTCP = ip; - - Log.debug("&bRecieved connection: {0}", c.addressTCP); - - connections.add(kn); - Core.app.post(() -> Net.handleServerReceived(kn.id, c)); - } - - @Override - public void disconnected(Connection connection, DcReason reason){ - ArcConnection k = getByArcID(connection.getID()); - if(k == null) return; - - Disconnect c = new Disconnect(); - c.id = k.id; - c.reason = reason.toString(); - - Core.app.post(() -> { - Net.handleServerReceived(k.id, c); - connections.remove(k); - }); - } - - @Override - public void received(Connection connection, Object object){ - ArcConnection k = getByArcID(connection.getID()); - if(object instanceof FrameworkMessage || k == null) return; - - Core.app.post(() -> { - try{ - Net.handleServerReceived(k.id, object); - }catch(RuntimeException e){ - if(e.getCause() instanceof ValidateException){ - ValidateException v = (ValidateException)e.getCause(); - Log.err("Validation failed: {0} ({1})", v.player.name, v.getMessage()); - }else{ - e.printStackTrace(); - } - }catch(Exception e){ - e.printStackTrace(); - } - }); - } - }; - - server.addListener(listener); - } - - @Override - public Iterable getConnections(){ - return connections; - } - - @Override - public ArcConnection getByID(int id){ - for(int i = 0; i < connections.size(); i++){ - ArcConnection con = connections.get(i); - if(con.id == id){ - return con; - } - } - - return null; - } - - @Override - public void sendServerStream(int id, Streamable stream){ - ArcConnection connection = getByID(id); - if(connection == null) return; - - connection.connection.addListener(new InputStreamSender(stream.stream, 512){ - int id; - - @Override - 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; - } - - @Override - 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. - } - }); - } - - @Override - public void host(int port) throws IOException{ - connections.clear(); - server.bind(port, port); - - serverThread = new Thread(() -> { - try{ - server.run(); - }catch(Throwable e){ - if(!(e instanceof ClosedSelectorException)) Threads.throwAppException(e); - } - }, "Net Server"); - serverThread.setDaemon(true); - serverThread.start(); - } - - @Override - public void close(){ - connections.clear(); - Threads.daemon(server::stop); - } - - ArcConnection getByArcID(int id){ - for(int i = 0; i < connections.size(); i++){ - ArcConnection con = connections.get(i); - if(con.connection != null && con.connection.getID() == id){ - return con; - } - } - - return null; - } - - class ArcConnection extends NetConnection{ - public final Connection connection; - - public ArcConnection(String address, Connection connection){ - super(address); - this.connection = connection; - } - - @Override - public boolean isConnected(){ - return connection.isConnected(); - } - - @Override - public void send(Object object, SendMode mode){ - try{ - if(mode == SendMode.tcp){ - connection.sendTCP(object); - }else{ - connection.sendUDP(object); - } - }catch(Exception e){ - Log.err(e); - Log.info("Error sending packet. Disconnecting invalid client!"); - connection.close(DcReason.error); - - ArcConnection k = getByArcID(connection.getID()); - if(k != null) connections.remove(k); - } - } - - @Override - public void close(){ - if(connection.isConnected()) connection.close(DcReason.closed); - } - } - -} \ No newline at end of file diff --git a/server/src/io/anuke/mindustry/server/ServerControl.java b/server/src/io/anuke/mindustry/server/ServerControl.java index 7e22d8ad67..83d85762ae 100644 --- a/server/src/io/anuke/mindustry/server/ServerControl.java +++ b/server/src/io/anuke/mindustry/server/ServerControl.java @@ -19,9 +19,8 @@ import io.anuke.mindustry.io.*; import io.anuke.mindustry.maps.Map; import io.anuke.mindustry.maps.*; import io.anuke.mindustry.net.Administration.*; -import io.anuke.mindustry.net.Net; import io.anuke.mindustry.net.Packets.*; -import io.anuke.mindustry.plugin.Plugins; +import io.anuke.mindustry.plugin.*; import io.anuke.mindustry.plugin.Plugins.*; import io.anuke.mindustry.type.*; @@ -169,7 +168,7 @@ public class ServerControl implements ApplicationListener{ }else{ netServer.kickAll(KickReason.gameover); state.set(State.menu); - Net.closeServer(); + net.closeServer(); } }); @@ -203,12 +202,12 @@ public class ServerControl implements ApplicationListener{ handler.register("exit", "Exit the server application.", arg -> { info("Shutting down server."); - Net.dispose(); + net.dispose(); Core.app.exit(); }); handler.register("stop", "Stop hosting the server.", arg -> { - Net.closeServer(); + net.closeServer(); if(lastTask != null) lastTask.cancel(); state.set(State.menu); info("Stopped server."); @@ -534,7 +533,7 @@ public class ServerControl implements ApplicationListener{ if(target != null){ Call.sendMessage("[scarlet] " + target.name + " has been kicked by the server."); - netServer.kick(target.con.id, KickReason.kick); + target.con.kick(KickReason.kick); info("It is done."); }else{ info("Nobody with that name could be found..."); @@ -563,7 +562,7 @@ public class ServerControl implements ApplicationListener{ for(Player player : playerGroup.all()){ if(netServer.admins.isIDBanned(player.uuid)){ Call.sendMessage("[scarlet] " + player.name + " has been banned."); - netServer.kick(player.con.id, KickReason.banned); + player.con.kick(KickReason.banned); } } }); @@ -830,7 +829,7 @@ public class ServerControl implements ApplicationListener{ r.run(); }catch(MapException e){ Log.err(e.map.name() + ": " + e.getMessage()); - Net.closeServer(); + net.closeServer(); } } }; @@ -843,7 +842,7 @@ public class ServerControl implements ApplicationListener{ private void host(){ try{ - Net.host(Core.settings.getInt("port")); + net.host(Core.settings.getInt("port")); info("&lcOpened a server on port {0}.", Core.settings.getInt("port")); }catch(BindException e){ Log.err("Unable to host: Port already in use! Make sure no other servers are running on the same port in your network."); diff --git a/server/src/io/anuke/mindustry/server/ServerLauncher.java b/server/src/io/anuke/mindustry/server/ServerLauncher.java index 5b35bdccf2..17ea3027ff 100644 --- a/server/src/io/anuke/mindustry/server/ServerLauncher.java +++ b/server/src/io/anuke/mindustry/server/ServerLauncher.java @@ -1,18 +1,18 @@ package io.anuke.mindustry.server; -import io.anuke.arc.backends.headless.HeadlessApplication; +import io.anuke.arc.backends.headless.*; import io.anuke.mindustry.*; -import io.anuke.mindustry.core.*; import io.anuke.mindustry.net.*; +import static io.anuke.mindustry.Vars.platform; + public class ServerLauncher{ public static void main(String[] args){ try{ - Net.setClientProvider(new ArcNetClient()); - Net.setServerProvider(new ArcNetServer()); - Vars.platform = new Platform(){}; + Vars.platform = ArcNetImpl::new; + Vars.net = new Net(platform.getNet()); new HeadlessApplication(new MindustryServer(args), null, throwable -> CrashSender.send(throwable, f -> {})); }catch(Throwable t){ CrashSender.send(t, f -> {}); diff --git a/tests/src/test/java/ApplicationTests.java b/tests/src/test/java/ApplicationTests.java index 6661a11769..b9ef4dbf5c 100644 --- a/tests/src/test/java/ApplicationTests.java +++ b/tests/src/test/java/ApplicationTests.java @@ -14,6 +14,7 @@ import io.anuke.mindustry.entities.type.base.*; import io.anuke.mindustry.game.Team; import io.anuke.mindustry.io.SaveIO; import io.anuke.mindustry.maps.Map; +import io.anuke.mindustry.net.*; import io.anuke.mindustry.type.ContentType; import io.anuke.mindustry.type.Item; import io.anuke.mindustry.world.*; @@ -42,6 +43,7 @@ public class ApplicationTests{ @Override public void setup(){ headless = true; + net = new Net(null); Vars.init(); content.createContent();