From 1cd8e8a097025050935e061d0c02c4c9705f5ddb Mon Sep 17 00:00:00 2001 From: Anuken Date: Sat, 13 Oct 2018 12:21:11 -0400 Subject: [PATCH] Improved server discovery --- core/assets/bundles/bundle.properties | 2 +- core/src/io/anuke/mindustry/net/Net.java | 7 ++- .../mindustry/ui/dialogs/JoinDialog.java | 62 +++++++++++-------- kryonet/src/io/anuke/kryonet/KryoClient.java | 54 ++++++++-------- 4 files changed, 69 insertions(+), 56 deletions(-) diff --git a/core/assets/bundles/bundle.properties b/core/assets/bundles/bundle.properties index de2cba297b..c6b44927c8 100644 --- a/core/assets/bundles/bundle.properties +++ b/core/assets/bundles/bundle.properties @@ -114,7 +114,7 @@ text.hosting=[accent]Opening server... text.hosts.refresh=Refresh text.hosts.discovering=Discovering LAN games text.server.refreshing=Refreshing server -text.hosts.none=[lightgray]No LAN games found! +text.hosts.none=[lightgray]No local games found! text.host.invalid=[scarlet]Can't connect to host. text.trace=Trace Player text.trace.playername=Player name: [accent]{0} diff --git a/core/src/io/anuke/mindustry/net/Net.java b/core/src/io/anuke/mindustry/net/Net.java index 9755436487..f4e1fe79c1 100644 --- a/core/src/io/anuke/mindustry/net/Net.java +++ b/core/src/io/anuke/mindustry/net/Net.java @@ -137,8 +137,8 @@ public class Net{ * Starts discovering servers on a different thread. Does not work with GWT. * Callback is run on the main libGDX thread. */ - public static void discoverServers(Consumer> cons){ - clientProvider.discover(cons); + public static void discoverServers(Consumer cons, Runnable done){ + clientProvider.discover(cons, done); } /** @@ -374,8 +374,9 @@ 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. + * @param done is the callback that should run after discovery. */ - void discover(Consumer> callback); + void discover(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); diff --git a/core/src/io/anuke/mindustry/ui/dialogs/JoinDialog.java b/core/src/io/anuke/mindustry/ui/dialogs/JoinDialog.java index eef848b911..edbd6133c2 100644 --- a/core/src/io/anuke/mindustry/ui/dialogs/JoinDialog.java +++ b/core/src/io/anuke/mindustry/ui/dialogs/JoinDialog.java @@ -30,6 +30,7 @@ public class JoinDialog extends FloatingDialog{ Table local = new Table(); Table remote = new Table(); Table hosts = new Table(); + int totalHosts; public JoinDialog(){ super("$text.joingame"); @@ -185,15 +186,6 @@ public class JoinDialog extends FloatingDialog{ }); } - void refreshLocal(){ - if(!Vars.gwt){ - local.clear(); - local.background("button"); - local.label(() -> "[accent]" + Bundles.get("text.hosts.discovering") + Strings.animated(4, 10f, ".")).pad(10f); - Net.discoverServers(this::addLocalHosts); - } - } - void setup(){ float w = targetWidth(); @@ -255,32 +247,50 @@ public class JoinDialog extends FloatingDialog{ }); } - void addLocalHosts(Array array){ - float w = targetWidth(); + void refreshLocal(){ + if(!Vars.gwt){ + totalHosts = 0; - local.clear(); + local.clear(); + local.background((Drawable)null); + local.table("button", t -> { + t.label(() -> "[accent]" + Bundles.get("text.hosts.discovering") + Strings.animated(4, 10f, ".")).pad(10f); + }).growX(); + Net.discoverServers(this::addLocalHost, this::finishLocalHosts); + } + } - if(array.size == 0){ + void finishLocalHosts(){ + if(totalHosts == 0){ + local.clear(); + local.background("button"); local.add("$text.hosts.none").pad(10f); local.add().growX(); local.addImageButton("icon-loading", 16 * 2f, this::refreshLocal).pad(-10f).padLeft(0).padTop(-6).size(70f, 74f); }else{ - for(Host a : array){ - TextButton button = local.addButton("[accent]" + a.name, "clear", () -> connect(a.address, port)) - .width(w).height(80f).pad(4f).get(); - button.left(); - button.row(); - button.add("[lightgray]" + (a.players != 1 ? Bundles.format("text.players", a.players) : - Bundles.format("text.players.single", a.players))); - button.row(); - button.add("[lightgray]" + a.address).pad(4).left(); - - local.row(); - local.background((Drawable) null); - } + local.background((Drawable) null); } } + void addLocalHost(Host host){ + if(totalHosts == 0){ + local.clear(); + } + totalHosts ++; + float w = targetWidth(); + + local.row(); + + TextButton button = local.addButton("[accent]" + host.name, "clear", () -> connect(host.address, port)) + .width(w).height(80f).pad(4f).get(); + button.left(); + button.row(); + button.add("[lightgray]" + (host.players != 1 ? Bundles.format("text.players", host.players) : + Bundles.format("text.players.single", host.players))); + button.row(); + button.add("[lightgray]" + host.address).pad(4).left(); + } + void connect(String ip, int port){ ui.loadfrag.show("$text.connecting"); diff --git a/kryonet/src/io/anuke/kryonet/KryoClient.java b/kryonet/src/io/anuke/kryonet/KryoClient.java index 2fddd60cc6..1c42c67917 100644 --- a/kryonet/src/io/anuke/kryonet/KryoClient.java +++ b/kryonet/src/io/anuke/kryonet/KryoClient.java @@ -2,8 +2,6 @@ package io.anuke.kryonet; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.utils.Array; -import com.badlogic.gdx.utils.ObjectMap; -import com.badlogic.gdx.utils.ObjectSet; import com.esotericsoftware.kryonet.*; import com.esotericsoftware.minlog.Log; import io.anuke.mindustry.net.Host; @@ -20,19 +18,17 @@ import net.jpountz.lz4.LZ4Factory; import net.jpountz.lz4.LZ4FastDecompressor; import java.io.IOException; -import java.net.DatagramPacket; -import java.net.DatagramSocket; -import java.net.InetAddress; +import java.net.*; import java.nio.ByteBuffer; import java.nio.channels.ClosedSelectorException; -import java.util.List; import static io.anuke.mindustry.Vars.*; import static io.anuke.mindustry.net.Net.packetPoolLock; public class KryoClient implements ClientProvider{ Client client; - ObjectMap addresses = new ObjectMap<>(); + Consumer lastCallback; + Array foundAddresses = new Array<>(); ClientDiscoveryHandler handler; LZ4FastDecompressor decompressor = LZ4Factory.fastestInstance().fastDecompressor(); @@ -48,8 +44,14 @@ public class KryoClient implements ClientProvider{ @Override public void onDiscoveredHost(DatagramPacket datagramPacket) { ByteBuffer buffer = ByteBuffer.wrap(datagramPacket.getData()); - Host address = NetworkIO.readServerData(datagramPacket.getAddress().getHostAddress(), buffer); - addresses.put(datagramPacket.getAddress(), address); + Host host = NetworkIO.readServerData(datagramPacket.getAddress().getHostAddress(), buffer); + for(InetAddress address : foundAddresses){ + if(address.equals(datagramPacket.getAddress()) || (isLocal(address) && isLocal(datagramPacket.getAddress()))){ + return; + } + } + Gdx.app.postRunnable(() -> lastCallback.accept(host)); + foundAddresses.add(datagramPacket.getAddress()); } @Override @@ -108,6 +110,17 @@ public class KryoClient implements ClientProvider{ } } + 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 byte[] decompressSnapshot(byte[] input, int size){ byte[] result = new byte[size]; @@ -170,7 +183,7 @@ public class KryoClient implements ClientProvider{ socket.setSoTimeout(2000); - addresses.clear(); + lastCallback = valid; DatagramPacket packet = handler.onRequestNewDatagramPacket(); @@ -187,23 +200,12 @@ public class KryoClient implements ClientProvider{ } @Override - public void discover(Consumer> callback){ + public void discover(Consumer callback, Runnable done){ runAsync(() -> { - addresses.clear(); - List list = client.discoverHosts(port, 3000); - ObjectSet hostnames = new ObjectSet<>(); - Array result = new Array<>(); - - for(InetAddress a : list){ - if(!hostnames.contains(a.getHostName())) { - Host address = addresses.get(a); - if(address != null) result.add(address); - - } - hostnames.add(a.getHostName()); - } - - Gdx.app.postRunnable(() -> callback.accept(result)); + foundAddresses.clear(); + lastCallback = callback; + client.discoverHosts(port, 3000); + Gdx.app.postRunnable(done); }); }