From 0b9c5f5f6bb5a7f6c0bc2a55c916479504e901b6 Mon Sep 17 00:00:00 2001 From: Jaime Martinez Rincon Date: Wed, 28 Jun 2017 23:24:24 +0200 Subject: [PATCH] Some changes to the TODOs Removed the matcher thingy, I will just support regex --- README.md | 11 +- pom.xml | 2 +- .../configuration/ConfigEntries.java | 1 + .../connection/ConnectionIntent.java | 8 +- .../lobbybalancer/utils/ServerListPing.java | 200 ++++++++++++++++++ src/main/resources/config.yml | 33 ++- 6 files changed, 238 insertions(+), 17 deletions(-) create mode 100644 src/main/java/me/jaimemartz/lobbybalancer/utils/ServerListPing.java diff --git a/README.md b/README.md index d23b42b..eca5e18 100644 --- a/README.md +++ b/README.md @@ -3,14 +3,15 @@ [![Build Status](https://travis-ci.com/jaime29010/LobbyBalancer.svg?token=2yUi9WpA9QzSbJx9eTmy&branch=master)](https://travis-ci.com/jaime29010/LobbyBalancer) -### Things to do: +### Things- to do: - [x] Get dummy sections able to have already registered servers on other sections -- [ ] Make `marker-descs` work per section +- [x] Add a new message for when a player gets connected to a server and repurpose the connecting one +- [ ] Make the feature `marker-descs` work per section - [ ] Unify the code that loads server into a section (duplicated at SectionManager and ServerSection) - [ ] Unify some of the code used in the FallbackCommand and SectionCommand -- [ ] Make the way of matching a string configurable (wildcard, contains, similar, regex) -- [ ] Make the section initialization work in stages instead of being hardcoded -- [ ] Ditch the faucet dependency and use [ConfigMe](https://github.com/AuthMe/ConfigMe) and [DependencyInjector](https://github.com/ljacqu/DependencyInjector) instead +- [ ] Use https://github.com/kennedyoliveira/pastebin4j instead of jpaste +- [ ] (!) Make the section initialization work in stages instead of being hardcoded +- [ ] (!) Ditch the faucet dependency and use [ConfigMe](https://github.com/AuthMe/ConfigMe) and [DependencyInjector](https://github.com/ljacqu/DependencyInjector) instead - [ ] Use a separate file for configuring the sections, must be done alongside the forth item - [ ] Separate the types of connections in classes instead of being in ConnectionIntent - [ ] Make the plugin API be not so dependent on a instance of LobbyBalancer diff --git a/pom.xml b/pom.xml index e1853ad..12cbc54 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ me.jaimemartz lobbybalancer - 2.0.9.8 + 2.0.9.9 LobbyBalancer diff --git a/src/main/java/me/jaimemartz/lobbybalancer/configuration/ConfigEntries.java b/src/main/java/me/jaimemartz/lobbybalancer/configuration/ConfigEntries.java index 19ad7cb..d386c13 100644 --- a/src/main/java/me/jaimemartz/lobbybalancer/configuration/ConfigEntries.java +++ b/src/main/java/me/jaimemartz/lobbybalancer/configuration/ConfigEntries.java @@ -42,6 +42,7 @@ public class ConfigEntries implements ConfigEntryHolder { public static final ConfigEntry SERVERS_UPDATE = new ConfigEntry<>(0, "settings.servers-update", false); public static final ConfigEntry CONNECTING_MESSAGE = new ConfigEntry<>(0, "settings.messages.connecting-server", null); + public static final ConfigEntry CONNECTED_MESSAGE = new ConfigEntry<>(0, "settings.messages.connecting-server", null); public static final ConfigEntry FAILURE_MESSAGE = new ConfigEntry<>(0, "settings.messages.misc-failure", null); public static final ConfigEntry UNKNOWN_SECTION_MESSAGE = new ConfigEntry<>(0, "settings.messages.unknown-section", null); public static final ConfigEntry INVALID_INPUT_MESSAGE = new ConfigEntry<>(0, "settings.messages.invalid-input", null); diff --git a/src/main/java/me/jaimemartz/lobbybalancer/connection/ConnectionIntent.java b/src/main/java/me/jaimemartz/lobbybalancer/connection/ConnectionIntent.java index 8855891..570ddd5 100644 --- a/src/main/java/me/jaimemartz/lobbybalancer/connection/ConnectionIntent.java +++ b/src/main/java/me/jaimemartz/lobbybalancer/connection/ConnectionIntent.java @@ -24,6 +24,10 @@ public abstract class ConnectionIntent { this.player = player; this.section = section; + new Messager(player).send(ConfigEntries.CONNECTING_MESSAGE.get(), + new Replacement("{section}", section.getName()) + ); + if (servers == section.getServers()) { throw new IllegalStateException("The servers list parameter is the same object as the section servers list, this cannot happen"); } @@ -31,10 +35,10 @@ public abstract class ConnectionIntent { if (section.getProvider() != ProviderType.NONE) { ServerInfo target = this.fetchServer(plugin, player, section, provider, servers); if (target != null) { - new Messager(player).send(ConfigEntries.CONNECTING_MESSAGE.get(), + this.connect(target); + new Messager(player).send(ConfigEntries.CONNECTED_MESSAGE.get(), new Replacement("{server}", target.getName()) ); - this.connect(target); } else { new Messager(player).send(ConfigEntries.FAILURE_MESSAGE.get()); } diff --git a/src/main/java/me/jaimemartz/lobbybalancer/utils/ServerListPing.java b/src/main/java/me/jaimemartz/lobbybalancer/utils/ServerListPing.java new file mode 100644 index 0000000..e99ce74 --- /dev/null +++ b/src/main/java/me/jaimemartz/lobbybalancer/utils/ServerListPing.java @@ -0,0 +1,200 @@ +package me.jaimemartz.lobbybalancer.utils; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import net.md_5.bungee.api.chat.BaseComponent; +import net.md_5.bungee.api.chat.TextComponent; +import net.md_5.bungee.chat.ComponentSerializer; +import net.md_5.bungee.chat.TextComponentSerializer; +import net.md_5.bungee.chat.TranslatableComponentSerializer; + +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.util.List; + +public class ServerListPing { + private int timeout = 7000; + private static Gson gson = new GsonBuilder() + .registerTypeAdapter(BaseComponent.class, new ComponentSerializer()) + .registerTypeAdapter(TextComponent.class, new TextComponentSerializer()) + .registerTypeAdapter(TranslatableComponentSerializer.class, new TranslatableComponentSerializer()) + .create(); + + private static int readVarInt(DataInputStream in) throws IOException { + int i = 0; + int j = 0; + while (true) { + int k = in.readByte(); + i |= (k & 0x7F) << j++ * 7; + if (j > 5) throw new RuntimeException("VarInt too big"); + if ((k & 0x80) != 128) break; + } + return i; + } + + private static void writeVarInt(DataOutputStream out, int paramInt) throws IOException { + while (true) { + if ((paramInt & 0xFFFFFF80) == 0) { + out.writeByte(paramInt); + return; + } + + out.writeByte(paramInt & 0x7F | 0x80); + paramInt >>>= 7; + } + } + + public StatusResponse ping(InetSocketAddress host) throws IOException { + try (Socket socket = new Socket()) { + socket.setSoTimeout(timeout); + socket.connect(host, timeout); + + try (DataOutputStream dataOutputStream = new DataOutputStream(socket.getOutputStream()); + DataInputStream dataInputStream = new DataInputStream(socket.getInputStream())) { + ByteArrayOutputStream b = new ByteArrayOutputStream(); + DataOutputStream handshake = new DataOutputStream(b); + handshake.writeByte(0x00); //packet id for handshake + writeVarInt(handshake, 4); //protocol version + writeVarInt(handshake, host.getHostString().length()); //host length + handshake.writeBytes(host.getHostString()); //host string + handshake.writeShort(host.getPort()); //port + writeVarInt(handshake, 1); //state (1 for handshake) + + writeVarInt(dataOutputStream, b.size()); //prepend size + dataOutputStream.write(b.toByteArray()); //write handshake packet + + + dataOutputStream.writeByte(0x01); //size is only 1 + dataOutputStream.writeByte(0x00); //packet id for ping + int size = readVarInt(dataInputStream); //size of packet + int id = readVarInt(dataInputStream); //packet id + + if (id == -1) { + throw new IOException("Premature end of stream."); + } + + if (id != 0x00) { //we want a status response + throw new IOException("Invalid packetID"); + } + + int length = readVarInt(dataInputStream); //length of json string + if (length == -1) { + throw new IOException("Premature end of stream."); + } + + if (length == 0) { + throw new IOException("Invalid string length."); + } + + byte[] in = new byte[length]; + dataInputStream.readFully(in); //read json string + String json = new String(in); + + + long now = System.currentTimeMillis(); + dataOutputStream.writeByte(0x09); //size of packet + dataOutputStream.writeByte(0x01); //0x01 for ping + dataOutputStream.writeLong(now); //time!? + + readVarInt(dataInputStream); + id = readVarInt(dataInputStream); + if (id == -1) { + throw new IOException("Premature end of stream."); + } + + if (id != 0x01) { + throw new IOException("Invalid packetID"); + } + long pingtime = dataInputStream.readLong(); //read response + + StatusResponse response = gson.fromJson(json, StatusResponse.class); + response.time = (int) (now - pingtime); + return response; + } + } + } + + public void setTimeout(int timeout) { + this.timeout = timeout; + } + + public int getTimeout() { + return timeout; + } + + public static class StatusResponse { + private BaseComponent description; + private Players players; + private Version version; + private String favicon; + private int time; + + public BaseComponent getDescription() { + return description; + } + + public Players getPlayers() { + return players; + } + + public Version getVersion() { + return version; + } + + public String getFavicon() { + return favicon; + } + + public int getTime() { + return time; + } + + public static class Players { + private int max; + private int online; + private List sample; + + public int getMax() { + return max; + } + + public int getOnline() { + return online; + } + + public List getSample() { + return sample; + } + } + + public static class Player { + private String name; + private String id; + + public String getName() { + return name; + } + + public String getId() { + return id; + } + } + + public static class Version { + private String name; + private String protocol; + + public String getName() { + return name; + } + + public String getProtocol() { + return protocol; + } + } + } +} diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index f425a07..e7d50ad 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -91,6 +91,23 @@ settings: rules: 'section-from': 'section-to' + # This will determine how the plugin if a string matches other string + string-matcher: + # Matches when the string is like the other string + # ? for one character, * for any number of characters + wildcard: true + + # Matches when the string contains the other string + contains: false + + # Matches when the string matches the pattern of the other string + regex: false + + # Matches when both strings are very similar + # See https://en.wikipedia.org/wiki/Levenshtein_distance + # Set to 100 to disable it, or other value to match when the result is higher than it + similar: 100 + # This will reload the plugin every time you execute /greload auto-reload: true @@ -110,7 +127,8 @@ settings: # Comment a message to disable it messages: - connecting-server: '&aConnecting to {server}' + # connecting-server: '&aConnecting to a {section} server' + connected-server: '&aConnected to {server}' misc-failure: '&cCould not find a server to get connected' unknown-section: '&cCould not find a section with that name' invalid-input: '&cThis is an invalid input type for this command' @@ -137,32 +155,29 @@ sections: auth-lobbies: provider: RANDOM servers: ["Auth1", "Auth2", "Auth3"] - general-lobbies: parent: 'auth-lobbies' - principal: true + principal: true # Cannot get above the principal section provider: RANDOM servers: ["Lobby1", "Lobby2", "Lobby3"] - skywars-lobbies: parent: 'general-lobbies' parent: LOWEST servers: ["SWLobby1", "SWLobby2", "SWLobby3"] - skywars-games: provider: FILLER parent: 'skywars-lobbies' servers: [ - "SW_A1", "SW_A2", "SW_A3", "SW_A4", "SW_A5", - "SW_B1", "SW_B2", "SW_B3", "SW_B4", "SW_B5" + "SW_A1", "SW_A2", "SW_A3", "SW_A4", "SW_A5", + "SW_B1", "SW_B2", "SW_B3", "SW_B4", "SW_B5" ] - section-server: 'randomskywars' + section-server: 'randomskywars' # Creates a fake server you can use, ex: @randomskywars section-command: name: 'playskywars' permission: '' aliases: ["skywars"] skywars-typea: - dummy: true + dummy: true # Use servers already registered on skywars-games parent: 'skywars-lobbies' servers: ["SW_A1", "SW_A2", "SW_A3", "SW_A4", "SW_A5"] section-server: 'skywarstypea'