diff --git a/annotations/src/main/java/io/anuke/annotations/RemoteReadGenerator.java b/annotations/src/main/java/io/anuke/annotations/RemoteReadGenerator.java index d23bd592c9..6bbc34ed0d 100644 --- a/annotations/src/main/java/io/anuke/annotations/RemoteReadGenerator.java +++ b/annotations/src/main/java/io/anuke/annotations/RemoteReadGenerator.java @@ -118,7 +118,7 @@ public class RemoteReadGenerator{ if(entry.forward && entry.where.isServer && needsPlayer){ //call forwarded method readBlock.addStatement(packageName + "." + entry.className + "." + entry.element.getSimpleName() + - "__forward(player.con.id" + (varResult.length() == 0 ? "" : ", ") + varResult.toString() + ")"); + "__forward(player.con" + (varResult.length() == 0 ? "" : ", ") + varResult.toString() + ")"); } readBlock.nextControlFlow("catch (java.lang.Exception e)"); diff --git a/annotations/src/main/java/io/anuke/annotations/RemoteWriteGenerator.java b/annotations/src/main/java/io/anuke/annotations/RemoteWriteGenerator.java index 401e862202..d28ff02a2d 100644 --- a/annotations/src/main/java/io/anuke/annotations/RemoteWriteGenerator.java +++ b/annotations/src/main/java/io/anuke/annotations/RemoteWriteGenerator.java @@ -85,12 +85,12 @@ public class RemoteWriteGenerator{ //if toAll is false, it's a 'send to one player' variant, so add the player as a parameter if(!toAll){ - method.addParameter(int.class, "playerClientID"); + method.addParameter(ClassName.bestGuess("io.anuke.mindustry.net.NetConnection"), "playerConnection"); } //add sender to ignore if(forwarded){ - method.addParameter(int.class, "exceptSenderID"); + method.addParameter(ClassName.bestGuess("io.anuke.mindustry.net.NetConnection"), "exceptConnection"); } //call local method if applicable, shouldn't happen when forwarding method as that already happens by default @@ -194,18 +194,18 @@ public class RemoteWriteGenerator{ if(forwarded){ //forward packet if(!methodEntry.local.isClient){ //if the client doesn't get it called locally, forward it back after validation - sendString = "send("; + sendString = "io.anuke.mindustry.Vars.net.send("; }else{ - sendString = "sendExcept(exceptSenderID, "; + sendString = "io.anuke.mindustry.Vars.net.sendExcept(exceptConnection, "; } }else if(toAll){ //send to all players / to server - sendString = "send("; + sendString = "io.anuke.mindustry.Vars.net.send("; }else{ //send to specific client from server - sendString = "sendTo(playerClientID, "; + sendString = "playerConnection.send("; } //send the actual packet - method.addStatement("io.anuke.mindustry.Vars.net." + sendString + "packet, " + + method.addStatement(sendString + "packet, " + (methodEntry.unreliable ? "io.anuke.mindustry.net.Net.SendMode.udp" : "io.anuke.mindustry.net.Net.SendMode.tcp") + ")"); diff --git a/core/src/io/anuke/mindustry/Vars.java b/core/src/io/anuke/mindustry/Vars.java index 6782e83908..ddb31ed56a 100644 --- a/core/src/io/anuke/mindustry/Vars.java +++ b/core/src/io/anuke/mindustry/Vars.java @@ -174,7 +174,6 @@ public class Vars implements Loadable{ public static EntityGroup fireGroup; public static EntityGroup[] unitGroups; - /** all local players, currently only has one player. may be used for local co-op in the future */ public static Player player; @Override diff --git a/core/src/io/anuke/mindustry/core/NetClient.java b/core/src/io/anuke/mindustry/core/NetClient.java index 597352c0bd..ee3d4e18a7 100644 --- a/core/src/io/anuke/mindustry/core/NetClient.java +++ b/core/src/io/anuke/mindustry/core/NetClient.java @@ -201,7 +201,7 @@ public class NetClient implements ApplicationListener{ @Remote(targets = Loc.client) public static void onPing(Player player, long time){ - Call.onPingResponse(player.id, time); + Call.onPingResponse(player.con, time); } @Remote(variants = Variant.one) diff --git a/core/src/io/anuke/mindustry/core/NetServer.java b/core/src/io/anuke/mindustry/core/NetServer.java index 0e3c588917..fea0c88db5 100644 --- a/core/src/io/anuke/mindustry/core/NetServer.java +++ b/core/src/io/anuke/mindustry/core/NetServer.java @@ -60,7 +60,7 @@ public class NetServer implements ApplicationListener{ net.handleServer(Disconnect.class, (con, packet) -> { if(con.player != null){ - onDisconnect(player, packet.reason); + onDisconnect(con.player, packet.reason); } }); @@ -104,7 +104,7 @@ public class NetServer implements ApplicationListener{ info.lastName = packet.name; info.id = packet.uuid; admins.save(); - Call.onInfoMessage(con.id, "You are not whitelisted here."); + Call.onInfoMessage(con, "You are not whitelisted here."); Log.info("&lcDo &lywhitelist-add {0}&lc to whitelist the player &lb'{1}'", packet.uuid, packet.name); con.kick(KickReason.whitelist); return; @@ -180,7 +180,7 @@ public class NetServer implements ApplicationListener{ Log.info("Auto-assigned player {0} to team {1}.", player.name, player.getTeam()); } - sendWorldData(player, con.id); + sendWorldData(player); platform.updateRPC(); @@ -293,14 +293,14 @@ public class NetServer implements ApplicationListener{ for(Player p : playerGroup.all()){ if(p.isAdmin || p.con == null || p == player) continue; - builder.append("[lightgray] ").append(p.name).append("[accent] (#").append(p.con.id).append(")\n"); + builder.append("[lightgray] ").append(p.name).append("[accent] (#").append(p.id).append(")\n"); } player.sendMessage(builder.toString()); }else{ Player found; if(args[0].length() > 1 && args[0].startsWith("#") && Strings.canParseInt(args[0].substring(1))){ int id = Strings.parseInt(args[0].substring(1)); - found = playerGroup.find(p -> p.con != null && p.con.id == id); + found = playerGroup.find(p -> p.id == id); }else{ found = playerGroup.find(p -> p.name.equalsIgnoreCase(args[0])); } @@ -356,8 +356,8 @@ public class NetServer implements ApplicationListener{ if(player.isLocal){ player.sendMessage("[scarlet]Re-synchronizing as the host is pointless."); }else{ - Call.onWorldDataBegin(player.con.id); - netServer.sendWorldData(player, player.con.id); + Call.onWorldDataBegin(player.con); + netServer.sendWorldData(player); } }); } @@ -382,13 +382,13 @@ public class NetServer implements ApplicationListener{ }); } - public void sendWorldData(Player player, int clientID){ + public void sendWorldData(Player player){ ByteArrayOutputStream stream = new ByteArrayOutputStream(); DeflaterOutputStream def = new FastDeflaterOutputStream(stream); NetworkIO.writeWorld(player, def); WorldStream data = new WorldStream(); data.stream = new ByteArrayInputStream(stream.toByteArray()); - net.sendStream(clientID, data); + player.con.sendStream(data); Log.debug("Packed {0} compressed bytes of world data.", stream.size()); } @@ -487,7 +487,7 @@ public class NetServer implements ApplicationListener{ newx = x; newy = y; }else if(Mathf.dst(x, y, newx, newy) > correctDist){ - Call.onPositionSet(player.con.id, newx, newy); //teleport and correct position when necessary + Call.onPositionSet(player.con, newx, newy); //teleport and correct position when necessary } //reset player to previous synced position so it gets interpolated @@ -530,7 +530,7 @@ public class NetServer implements ApplicationListener{ }else if(action == AdminAction.trace){ TraceInfo info = new TraceInfo(other.con.address, other.uuid, other.con.modclient, other.con.mobile); if(player.con != null){ - Call.onTraceInfo(player.con.id, other, info); + Call.onTraceInfo(player.con, other, info); }else{ NetClient.onTraceInfo(other, info); } @@ -599,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, 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); @@ -630,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, (byte)group.getID(), (short)sent, (short)syncBytes.length, net.compressSnapshot(syncBytes)); sent = 0; syncStream.reset(); } @@ -640,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, (byte)group.getID(), (short)sent, (short)syncBytes.length, net.compressSnapshot(syncBytes)); } } } diff --git a/core/src/io/anuke/mindustry/entities/type/Player.java b/core/src/io/anuke/mindustry/entities/type/Player.java index f2dd06b48f..5cc5b7551f 100644 --- a/core/src/io/anuke/mindustry/entities/type/Player.java +++ b/core/src/io/anuke/mindustry/entities/type/Player.java @@ -780,7 +780,7 @@ public class Player extends Unit implements BuilderMinerTrait, ShooterTrait{ Vars.ui.chatfrag.addMessage(text, null); } }else{ - Call.sendMessage(con.id, text, null, null); + Call.sendMessage(con, text, null, null); } } @@ -794,7 +794,7 @@ public class Player extends Unit implements BuilderMinerTrait, ShooterTrait{ Vars.ui.chatfrag.addMessage(text, fromName); } }else{ - Call.sendMessage(con.id, text, fromName, from); + Call.sendMessage(con, text, fromName, from); } } diff --git a/core/src/io/anuke/mindustry/net/Net.java b/core/src/io/anuke/mindustry/net/Net.java index 3c3bb00362..6d23d5f7f4 100644 --- a/core/src/io/anuke/mindustry/net/Net.java +++ b/core/src/io/anuke/mindustry/net/Net.java @@ -134,7 +134,7 @@ public class Net{ */ public void closeServer(){ for(NetConnection con : getConnections()){ - Call.onKick(con.id, KickReason.serverClose); + Call.onKick(con, KickReason.serverClose); } provider.closeServer(); @@ -176,43 +176,24 @@ public class Net{ return (Iterable)provider.getConnections(); } - /** - * Returns a connection by 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. - */ + /** Send an object to all connected clients, or to the server if this is a client.*/ public void send(Object object, SendMode mode){ if(server){ - provider.sendServer(object, mode); + for(NetConnection con : provider.getConnections()){ + con.send(object, mode); + } }else{ provider.sendClient(object, mode); } } - /** - * Send an object to a certain client. Server-side only - */ - 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 void sendExcept(int id, Object object, SendMode mode){ - provider.sendServerExcept(id, object, mode); - } - - /** - * Send a stream to a specific client. Server-side only. - */ - public void sendStream(int id, Streamable stream){ - provider.sendServerStream(id, stream); + /** Send an object to everyone EXCEPT a certain client. Server-side only.*/ + public void sendExcept(NetConnection except, Object object, SendMode mode){ + for(NetConnection con : getConnections()){ + if(con != except){ + con.send(object, mode); + } + } } /** @@ -329,7 +310,7 @@ public class Net{ /** * 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. + * Callback should be run on the main thread. * @param done is the callback that should run after discovery. */ void discoverServers(Consumer callback, Runnable done); @@ -340,65 +321,6 @@ public class Net{ /** Host a server at specified port. */ 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 = getConnection(id); - if(connection == null) return; - try{ - int cid; - StreamBegin begin = new StreamBegin(); - begin.total = stream.stream.available(); - begin.type = Registrator.getID(stream.getClass()); - connection.send(begin, SendMode.tcp); - cid = begin.id; - - while(stream.stream.available() > 0){ - byte[] bytes = new byte[Math.min(512, stream.stream.available())]; - stream.stream.read(bytes); - - StreamChunk chunk = new StreamChunk(); - chunk.id = cid; - chunk.data = bytes; - connection.send(chunk, SendMode.tcp); - } - }catch(IOException e){ - throw new RuntimeException(e); - } - } - - default void sendServer(Object object, SendMode mode){ - for(NetConnection con : getConnections()){ - con.send(object, mode); - } - } - - default void sendServerTo(int id, Object object, SendMode mode){ - NetConnection conn = getConnection(id); - if(conn == null){ - Log.err("Failed to find connection with ID {0}.", id); - return; - } - conn.send(object, mode); - } - - default void sendServerExcept(int id, Object object, SendMode mode){ - for(NetConnection con : getConnections()){ - if(con.id != id){ - con.send(object, mode); - } - } - } - - /** 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(); diff --git a/core/src/io/anuke/mindustry/net/NetConnection.java b/core/src/io/anuke/mindustry/net/NetConnection.java index 037cb87c1e..36bde0afbb 100644 --- a/core/src/io/anuke/mindustry/net/NetConnection.java +++ b/core/src/io/anuke/mindustry/net/NetConnection.java @@ -8,16 +8,13 @@ import io.anuke.mindustry.net.Administration.*; import io.anuke.mindustry.net.Net.*; import io.anuke.mindustry.net.Packets.*; +import java.io.*; + import static io.anuke.mindustry.Vars.netServer; public abstract class NetConnection{ - private static int lastID; - - public final int id; public final String address; - - public boolean modclient; - public boolean mobile; + public boolean mobile, modclient; public @Nullable Player player; /** ID of last recieved client snapshot. */ @@ -25,18 +22,16 @@ public abstract class NetConnection{ /** Timestamp of last recieved snapshot. */ public long lastRecievedClientTime; - public boolean hasConnected = false; - public boolean hasBegunConnecting = false; + public boolean hasConnected, hasBegunConnecting; public float viewWidth, viewHeight, viewX, viewY; /** Assigns this connection a unique ID. No two connections will ever have the same ID.*/ public NetConnection(String address){ - this.id = lastID++; this.address = address; } public void kick(KickReason reason){ - Log.info("Kicking connection #{0} / IP: {1}. Reason: {2}", this.id, address, reason.name()); + Log.info("Kicking connection {0}; Reason: {2}", 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); @@ -44,7 +39,7 @@ public abstract class NetConnection{ info.lastKicked = Math.max(Time.millis(), info.lastKicked); } - Call.onKick(id, reason); + Call.onKick(this, reason); Time.runTask(2f, this::close); @@ -55,6 +50,29 @@ public abstract class NetConnection{ return true; } + public void sendStream(Streamable stream){ + try{ + int cid; + StreamBegin begin = new StreamBegin(); + begin.total = stream.stream.available(); + begin.type = Registrator.getID(stream.getClass()); + send(begin, SendMode.tcp); + cid = begin.id; + + while(stream.stream.available() > 0){ + byte[] bytes = new byte[Math.min(512, stream.stream.available())]; + stream.stream.read(bytes); + + StreamChunk chunk = new StreamChunk(); + chunk.id = cid; + chunk.data = bytes; + send(chunk, SendMode.tcp); + } + }catch(IOException e){ + throw new RuntimeException(e); + } + } + public abstract void send(Object object, SendMode mode); public abstract void close(); diff --git a/core/src/io/anuke/mindustry/net/Packets.java b/core/src/io/anuke/mindustry/net/Packets.java index d7d79bfbb3..117c9348c4 100644 --- a/core/src/io/anuke/mindustry/net/Packets.java +++ b/core/src/io/anuke/mindustry/net/Packets.java @@ -41,7 +41,6 @@ public class Packets{ } public static class Connect implements Packet{ - public int id; public String addressTCP; @Override @@ -51,7 +50,6 @@ public class Packets{ } public static class Disconnect implements Packet{ - public int id; public String reason; @Override diff --git a/desktop/src/io/anuke/mindustry/desktop/steam/SteamCoreNetImpl.java b/desktop/src/io/anuke/mindustry/desktop/steam/SteamCoreNetImpl.java index 39db284506..82f6b9cbb3 100644 --- a/desktop/src/io/anuke/mindustry/desktop/steam/SteamCoreNetImpl.java +++ b/desktop/src/io/anuke/mindustry/desktop/steam/SteamCoreNetImpl.java @@ -34,6 +34,7 @@ public class SteamCoreNetImpl implements SteamNetworkingCallback, SteamMatchmaki final ByteBuffer readBuffer = ByteBuffer.allocateDirect(1024 * 4); final CopyOnWriteArrayList connections = new CopyOnWriteArrayList<>(); + final CopyOnWriteArrayList connectionsOut = new CopyOnWriteArrayList<>(); final IntMap steamConnections = new IntMap<>(); //maps steam ID -> valid net connection final ObjectMap lobbyIDs = new ObjectMap<>(); @@ -149,11 +150,12 @@ public class SteamCoreNetImpl implements SteamNetworkingCallback, SteamMatchmaki @Override public void pingHost(String address, int port, Consumer valid, Consumer failed){ - //no + provider.pingHost(address, port, valid, failed); } @Override public void hostServer(int port) throws IOException{ + provider.hostServer(port); smat.createLobby(Core.settings.getBool("publichost") ? LobbyType.Public : LobbyType.FriendsOnly, 32); } @@ -165,6 +167,8 @@ public class SteamCoreNetImpl implements SteamNetworkingCallback, SteamMatchmaki @Override public void closeServer(){ + provider.closeServer(); + if(currentLobby != null){ smat.leaveLobby(currentLobby); for(SteamConnection con : steamConnections.values()){ @@ -178,7 +182,11 @@ public class SteamCoreNetImpl implements SteamNetworkingCallback, SteamMatchmaki @Override public Iterable getConnections(){ - return connections; + //merge provider connections + connectionsOut.clear(); + connectionsOut.addAll(connections); + for(NetConnection c : provider.getConnections()) connectionsOut.add(c); + return connectionsOut; } void disconnectSteamUser(SteamID steamid){ @@ -351,7 +359,6 @@ public class SteamCoreNetImpl implements SteamNetworkingCallback, SteamMatchmaki if(!steamConnections.containsKey(steamIDRemote.getAccountID())){ SteamConnection con = new SteamConnection(steamIDRemote); Connect c = new Connect(); - c.id = con.id; c.addressTCP = "steam:" + steamIDRemote.getAccountID(); Log.info("&bRecieved connection: {0}", c.addressTCP); diff --git a/net/src/io/anuke/mindustry/net/ArcNetImpl.java b/net/src/io/anuke/mindustry/net/ArcNetImpl.java index 3609264947..db0edd6fa2 100644 --- a/net/src/io/anuke/mindustry/net/ArcNetImpl.java +++ b/net/src/io/anuke/mindustry/net/ArcNetImpl.java @@ -34,7 +34,6 @@ public class ArcNetImpl implements NetProvider{ 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)); @@ -83,7 +82,6 @@ public class ArcNetImpl implements NetProvider{ ArcConnection kn = new ArcConnection(ip, connection); Connect c = new Connect(); - c.id = kn.id; c.addressTCP = ip; Log.debug("&bRecieved connection: {0}", c.addressTCP); @@ -98,7 +96,6 @@ public class ArcNetImpl implements NetProvider{ if(k == null) return; Disconnect c = new Disconnect(); - c.id = k.id; c.reason = reason.toString(); Core.app.post(() -> { @@ -228,10 +225,11 @@ public class ArcNetImpl implements NetProvider{ @Override public void dispose(){ + disconnectClient(); + closeServer(); try{ client.dispose(); - }catch(IOException e){ - throw new RuntimeException(e); + }catch(IOException ignored){ } } @@ -240,34 +238,6 @@ public class ArcNetImpl implements NetProvider{ 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(); @@ -314,6 +284,31 @@ public class ArcNetImpl implements NetProvider{ return connection.isConnected(); } + @Override + public void sendStream(Streamable stream){ + 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.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 send(Object object, SendMode mode){ try{ diff --git a/net/src/io/anuke/mindustry/net/PacketSerializer.java b/net/src/io/anuke/mindustry/net/PacketSerializer.java index a101af5145..ae24e240e3 100644 --- a/net/src/io/anuke/mindustry/net/PacketSerializer.java +++ b/net/src/io/anuke/mindustry/net/PacketSerializer.java @@ -38,7 +38,6 @@ public class PacketSerializer implements NetSerializer{ } } - public static void writeFramework(ByteBuffer buffer, FrameworkMessage message){ if(message instanceof Ping){ Ping p = (Ping)message; diff --git a/server/src/io/anuke/mindustry/server/ServerControl.java b/server/src/io/anuke/mindustry/server/ServerControl.java index 83d85762ae..66c153c6a7 100644 --- a/server/src/io/anuke/mindustry/server/ServerControl.java +++ b/server/src/io/anuke/mindustry/server/ServerControl.java @@ -816,7 +816,7 @@ public class ServerControl implements ApplicationListener{ if(state.rules.pvp){ p.setTeam(netServer.assignTeam(p, new ArrayIterable<>(players))); } - netServer.sendWorldData(p, p.con.id); + netServer.sendWorldData(p); } inExtraRound = false; };