diff --git a/core/assets/bundles/bundle.properties b/core/assets/bundles/bundle.properties index 41e3a372f2..00fab558a9 100644 --- a/core/assets/bundles/bundle.properties +++ b/core/assets/bundles/bundle.properties @@ -38,6 +38,7 @@ text.server.kicked.invalidPassword=Invalid password! text.server.kicked.clientOutdated=Outdated client! Update your game! text.server.kicked.serverOutdated=Outdated server! Ask the host to update! text.server.kicked.banned=You are banned on this server. +text.server.kicked.recentKick=You have been kicked recently.\nWait before connecting again. text.server.connected={0} has joined. text.server.disconnected={0} has disconnected. text.nohost=Can't host server on a custom map! diff --git a/core/assets/version.properties b/core/assets/version.properties index 09c58b49fc..8311d598b7 100644 --- a/core/assets/version.properties +++ b/core/assets/version.properties @@ -1,7 +1,7 @@ #Autogenerated file. Do not modify. -#Mon Mar 19 20:53:58 EDT 2018 +#Tue Mar 20 18:24:58 EDT 2018 version=release -androidBuildCode=450 +androidBuildCode=452 name=Mindustry code=3.4 build=custom build diff --git a/core/src/io/anuke/mindustry/core/NetServer.java b/core/src/io/anuke/mindustry/core/NetServer.java index da9f56964a..312208a0e3 100644 --- a/core/src/io/anuke/mindustry/core/NetServer.java +++ b/core/src/io/anuke/mindustry/core/NetServer.java @@ -7,12 +7,10 @@ import io.anuke.mindustry.entities.SyncEntity; import io.anuke.mindustry.game.EventType.GameOverEvent; import io.anuke.mindustry.io.Platform; import io.anuke.mindustry.io.Version; -import io.anuke.mindustry.net.Administration; -import io.anuke.mindustry.net.Net; +import io.anuke.mindustry.net.*; +import io.anuke.mindustry.net.Administration.PlayerInfo; import io.anuke.mindustry.net.Net.SendMode; -import io.anuke.mindustry.net.NetworkIO; import io.anuke.mindustry.net.Packets.*; -import io.anuke.mindustry.net.TraceInfo; import io.anuke.mindustry.resource.*; import io.anuke.mindustry.world.Block; import io.anuke.mindustry.world.Placement; @@ -31,7 +29,7 @@ import java.nio.ByteBuffer; import static io.anuke.mindustry.Vars.*; public class NetServer extends Module{ - private final static float serverSyncTime = 4, itemSyncTime = 10; + private final static float serverSyncTime = 4, itemSyncTime = 10, kickDuration = 30 * 1000; private final static int timerEntitySync = 0; private final static int timerStateSync = 1; @@ -50,39 +48,48 @@ public class NetServer extends Module{ Net.handleServer(Connect.class, (id, connect) -> { if(admins.isIPBanned(connect.addressTCP)){ - Net.kickConnection(id, KickReason.banned); + kick(id, KickReason.banned); } }); Net.handleServer(ConnectPacket.class, (id, packet) -> { String uuid = new String(Base64Coder.encode(packet.uuid)); + if(Net.getConnection(id) == null || admins.isIPBanned(Net.getConnection(id).address)) return; + TraceInfo trace = admins.getTrace(Net.getConnection(id).address); + PlayerInfo info = admins.getInfo(uuid); + if(admins.isIDBanned(uuid)){ - Net.kickConnection(id, KickReason.banned); + kick(id, KickReason.banned); + return; + } + + if(TimeUtils.millis() - info.lastKicked < kickDuration){ + kick(id, KickReason.recentKick); return; } String ip = Net.getConnection(id).address; admins.updatePlayerJoined(uuid, ip, packet.name); - admins.getTrace(ip).uuid = uuid; - admins.getTrace(ip).android = packet.android; + trace.uuid = uuid; + trace.android = packet.android; if(packet.version != Version.build && Version.build != -1 && packet.version != -1){ - Net.kickConnection(id, packet.version > Version.build ? KickReason.serverOutdated : KickReason.clientOutdated); + kick(id, packet.version > Version.build ? KickReason.serverOutdated : KickReason.clientOutdated); return; } if(packet.version == -1){ - admins.getTrace(ip).modclient = true; + trace.modclient = true; } Log.info("Sending data to player '{0}' / {1}", packet.name, id); Player player = new Player(); - player.isAdmin = admins.isAdmin(Net.getConnection(id).address); + player.isAdmin = admins.isAdmin(uuid, ip); player.clientid = id; player.name = packet.name; player.isAndroid = packet.android; @@ -92,7 +99,7 @@ public class NetServer extends Module{ player.color.set(packet.color); connections.put(id, player); - admins.getTrace(ip).playerid = player.id; + trace.playerid = player.id; if(world.getMap().custom){ ByteArrayOutputStream stream = new ByteArrayOutputStream(); @@ -165,14 +172,14 @@ public class NetServer extends Module{ TraceInfo info = admins.getTrace(Net.getConnection(id).address); Weapon weapon = (Weapon)Upgrade.getByID(packet.weaponid); - float wtrc = 45f; + float wtrc = 40f; if(!Timers.get(info.ip + "-weapontrace", wtrc)){ info.fastShots ++; }else{ if(info.fastShots - 2 > (int)(wtrc / (weapon.getReload() / 2f))){ - Net.kickConnection(id, KickReason.kick); + kick(id, KickReason.kick); } info.fastShots = 0; @@ -296,10 +303,10 @@ public class NetServer extends Module{ if(packet.action == AdminAction.ban){ admins.banPlayerIP(ip); - Net.kickConnection(other.clientid, KickReason.banned); + kick(other.clientid, KickReason.banned); Log.info("&lc{0} has banned {1}.", player.name, other.name); }else if(packet.action == AdminAction.kick){ - Net.kickConnection(other.clientid, KickReason.kick); + kick(other.clientid, KickReason.kick); Log.info("&lc{0} has kicked {1}.", player.name, other.name); }else if(packet.action == AdminAction.trace){ TracePacket trace = new TracePacket(); @@ -332,6 +339,31 @@ public class NetServer extends Module{ admins.clearTraces(); } + 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); + } + + PlayerInfo info = admins.getInfo(admins.getTrace(con.address).uuid); + + if(reason == KickReason.kick || reason == KickReason.banned){ + info.timesKicked ++; + info.lastKicked = TimeUtils.millis(); + } + + KickPacket p = new KickPacket(); + p.reason = reason; + + con.send(p, SendMode.tcp); + Timers.runTask(2f, con::close); + + admins.save(); + } + void sync(){ if(timer.get(timerEntitySync, serverSyncTime)){ diff --git a/core/src/io/anuke/mindustry/net/Administration.java b/core/src/io/anuke/mindustry/net/Administration.java index b53ad196af..f34fe8aafb 100644 --- a/core/src/io/anuke/mindustry/net/Administration.java +++ b/core/src/io/anuke/mindustry/net/Administration.java @@ -133,12 +133,13 @@ public class Administration { } /**Makes a player an admin. Returns whether this player was already an admin.*/ - public boolean adminPlayer(String id){ + public boolean adminPlayer(String id, String ip){ PlayerInfo info = getCreateInfo(id); if(info.admin) return false; + info.validAdminIP = ip; info.admin = true; save(); @@ -166,8 +167,9 @@ public class Administration { return getCreateInfo(uuid).banned; } - public boolean isAdmin(String id){ - return getCreateInfo(id).admin; + public boolean isAdmin(String id, String ip){ + PlayerInfo info = getCreateInfo(id); + return info.admin && ip.equals(info.validAdminIP); } public PlayerInfo getInfo(String id){ @@ -212,6 +214,7 @@ public class Administration { public static class PlayerInfo{ public String id; public String lastName = "", lastIP = ""; + public String validAdminIP; public Array ips = new Array<>(); public Array names = new Array<>(); public int timesKicked; //TODO not implemented! @@ -219,6 +222,7 @@ public class Administration { public int totalBlockPlaced; public int totalBlocksBroken; public boolean banned, admin; + public long lastKicked; //last kicked timestamp PlayerInfo(String id){ this.id = id; diff --git a/core/src/io/anuke/mindustry/net/Net.java b/core/src/io/anuke/mindustry/net/Net.java index 636ff7dd12..e41b960694 100644 --- a/core/src/io/anuke/mindustry/net/Net.java +++ b/core/src/io/anuke/mindustry/net/Net.java @@ -12,7 +12,6 @@ import com.badlogic.gdx.utils.reflect.ClassReflection; import io.anuke.mindustry.io.Platform; import io.anuke.mindustry.net.Packet.ImportantPacket; import io.anuke.mindustry.net.Packet.UnimportantPacket; -import io.anuke.mindustry.net.Packets.KickReason; import io.anuke.mindustry.net.Streamable.StreamBegin; import io.anuke.mindustry.net.Streamable.StreamBuilder; import io.anuke.mindustry.net.Streamable.StreamChunk; @@ -101,11 +100,6 @@ public class Net{ clientProvider.discover(cons); } - /**Kick a specified connection from the server.*/ - public static void kickConnection(int id, KickReason reason){ - serverProvider.kick(id, reason); - } - /**Returns a list of all connections IDs.*/ public static Array getConnections(){ return (Array)serverProvider.getConnections(); @@ -307,10 +301,6 @@ public class Net{ Array getConnections(); /**Returns a connection by ID.*/ NetConnection getByID(int id); - /**Kick a certain connection.*/ - void kick(int connection, KickReason reason); - /**Returns the ping for a certain connection.*/ - int getPingFor(NetConnection connection); /**Close all connections.*/ void dispose(); } diff --git a/core/src/io/anuke/mindustry/net/Packets.java b/core/src/io/anuke/mindustry/net/Packets.java index 734f33988f..5c5630aeaf 100644 --- a/core/src/io/anuke/mindustry/net/Packets.java +++ b/core/src/io/anuke/mindustry/net/Packets.java @@ -377,7 +377,7 @@ public class Packets { } public enum KickReason{ - kick, invalidPassword, clientOutdated, serverOutdated, banned, gameover(true); + kick, invalidPassword, clientOutdated, serverOutdated, banned, gameover(true), recentKick; public final boolean quiet; KickReason(){ quiet = false; } diff --git a/core/src/io/anuke/mindustry/ui/fragments/PlayerListFragment.java b/core/src/io/anuke/mindustry/ui/fragments/PlayerListFragment.java index 91959c591d..7dcc657c71 100644 --- a/core/src/io/anuke/mindustry/ui/fragments/PlayerListFragment.java +++ b/core/src/io/anuke/mindustry/ui/fragments/PlayerListFragment.java @@ -130,7 +130,7 @@ public class PlayerListFragment implements Fragment{ ui.showConfirm("$text.confirm", "$text.confirmban", () -> { if(Net.server()) { netServer.admins.banPlayerIP(connection.address); - Net.kickConnection(player.clientid, KickReason.banned); + netServer.kick(player.clientid, KickReason.banned); }else{ NetEvents.handleAdministerRequest(player, AdminAction.ban); } @@ -139,7 +139,7 @@ public class PlayerListFragment implements Fragment{ t.addImageButton("icon-cancel", 14*2, () -> { if(Net.server()) { - Net.kickConnection(player.clientid, KickReason.kick); + netServer.kick(player.clientid, KickReason.kick); }else{ NetEvents.handleAdministerRequest(player, AdminAction.kick); } @@ -150,14 +150,16 @@ public class PlayerListFragment implements Fragment{ t.addImageButton("icon-admin", "toggle", 14*2, () -> { if(Net.client()) return; - if(netServer.admins.isAdmin(connection.address)){ + String id = netServer.admins.getTrace(connection.address).uuid; + + if(netServer.admins.isAdmin(id, connection.address)){ ui.showConfirm("$text.confirm", "$text.confirmunadmin", () -> { - netServer.admins.unAdminPlayer(connection.address); + netServer.admins.unAdminPlayer(id); NetEvents.handleAdminSet(player, false); }); }else{ ui.showConfirm("$text.confirm", "$text.confirmadmin", () -> { - netServer.admins.adminPlayer(connection.address); + netServer.admins.adminPlayer(id, connection.address); NetEvents.handleAdminSet(player, true); }); } diff --git a/kryonet/src/io/anuke/kryonet/KryoServer.java b/kryonet/src/io/anuke/kryonet/KryoServer.java index 9e1cd8f27f..876f0ac3a3 100644 --- a/kryonet/src/io/anuke/kryonet/KryoServer.java +++ b/kryonet/src/io/anuke/kryonet/KryoServer.java @@ -134,23 +134,6 @@ public class KryoServer implements ServerProvider { return null; } - @Override - public void kick(int connection, KickReason reason) { - KryoConnection con = getByID(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); - } - - KickPacket p = new KickPacket(); - p.reason = reason; - - con.send(p, SendMode.tcp); - Timers.runTask(2f, con::close); - } - @Override public void host(int port) throws IOException { lastconnection = 0; @@ -278,12 +261,6 @@ public class KryoServer implements ServerProvider { } } - @Override - public int getPingFor(NetConnection con) { - KryoConnection k = (KryoConnection)con; - return k.connection == null ? 0 : k.connection.getReturnTripTime(); - } - @Override public void dispose(){ close(); diff --git a/server/src/io/anuke/mindustry/server/ServerControl.java b/server/src/io/anuke/mindustry/server/ServerControl.java index ce21bdaf29..4404ab6b46 100644 --- a/server/src/io/anuke/mindustry/server/ServerControl.java +++ b/server/src/io/anuke/mindustry/server/ServerControl.java @@ -78,7 +78,7 @@ public class ServerControl extends Module { info("Game over!"); for(NetConnection connection : Net.getConnections()){ - Net.kickConnection(connection.id, KickReason.gameover); + netServer.kick(connection.id, KickReason.gameover); } if (mode != ShuffleMode.off) { @@ -127,22 +127,28 @@ public class ServerControl extends Module { Log.info("Stopped server."); }); - handler.register("host", " [mode]", "Open the server with a specific map.", arg -> { + handler.register("host", "[mapname] [mode]", "Open the server with a specific map.", arg -> { if(state.is(State.playing)){ err("Already hosting. Type 'stop' to stop hosting first."); return; } - String search = arg[0]; Map result = null; - for(Map map : world.maps().list()){ - if(map.name.equalsIgnoreCase(search)) - result = map; - } - if(result == null){ - err("No map with name &y'{0}'&lr found.", search); - return; + if(arg.length > 0) { + String search = arg[0]; + for (Map map : world.maps().list()) { + if (map.name.equalsIgnoreCase(search)) + result = map; + } + + if(result == null){ + err("No map with name &y'{0}'&lr found.", search); + return; + } + }else{ + while(result == null || result.visible) + result = world.maps().getAllMaps().random(); } GameMode mode = null; @@ -273,7 +279,7 @@ public class ServerControl extends Module { } if(target != null){ - Net.kickConnection(target.clientid, KickReason.kick); + netServer.kick(target.clientid, KickReason.kick); info("It is done."); }else{ info("Nobody with that name could be found..."); @@ -299,7 +305,7 @@ public class ServerControl extends Module { String ip = Net.getConnection(target.clientid).address; netServer.admins.banPlayerIP(ip); netServer.admins.banPlayerID(netServer.admins.getTrace(ip).uuid); - Net.kickConnection(target.clientid, KickReason.banned); + netServer.kick(target.clientid, KickReason.banned); info("Banned player by IP and ID: {0} / {1}", ip, netServer.admins.getTrace(ip).uuid); }else{ info("Nobody with that name could be found."); @@ -337,7 +343,7 @@ public class ServerControl extends Module { for(Player player : playerGroup.all()){ if(Net.getConnection(player.clientid).address.equals(arg[0])){ - Net.kickConnection(player.clientid, KickReason.banned); + netServer.kick(player.clientid, KickReason.banned); break; } } @@ -352,7 +358,7 @@ public class ServerControl extends Module { for(Player player : playerGroup.all()){ if(netServer.admins.getTrace(Net.getConnection(player.clientid).address).uuid.equals(arg[0])){ - Net.kickConnection(player.clientid, KickReason.banned); + netServer.kick(player.clientid, KickReason.banned); break; } } @@ -394,7 +400,7 @@ public class ServerControl extends Module { if(target != null){ String id = netServer.admins.getTrace(Net.getConnection(target.clientid).address).uuid; - netServer.admins.adminPlayer(id); + netServer.admins.adminPlayer(id, Net.getConnection(target.clientid).address); NetEvents.handleAdminSet(target, true); info("Admin-ed player by ID: {0} / {1}", id, arg[0]); }else{