Skip to content

Commit

Permalink
Add automatic reconnection and some new settings
Browse files Browse the repository at this point in the history
  • Loading branch information
Morphan1 committed Oct 5, 2016
1 parent 5ea8a20 commit 1e25a9f
Show file tree
Hide file tree
Showing 6 changed files with 149 additions and 47 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.denizenscript.depenizen.bukkit;

import net.aufdemrand.denizencore.objects.Duration;

public class Settings {

/*
Expand All @@ -8,39 +10,43 @@ public class Settings {
*/

private static final String SOCKET_ENABLED = "Socket.Enabled";
private static final String SOCKET_IP_ADDRESS = "Socket.Ip address";
private static final String SOCKET_PORT = "Socket.Port";
private static final String SOCKET_PASSWORD = "Socket.Password";
private static final String SOCKET_NAME = "Socket.Name";
private static final String SOCKET_TIMEOUT = "Socket.Timeout";
private static final String SOCKET_RECONNECT_DELAY = "Socket.Reconnect Delay";

public static boolean socketEnabled() {
return DepenizenPlugin.getCurrentInstance().getConfig().getBoolean(SOCKET_ENABLED, false);
return DepenizenPlugin.getCurrentInstance().getConfig().getBoolean("Socket.Enabled", false);
}

public static String socketIpAddress() {
return DepenizenPlugin.getCurrentInstance().getConfig().getString(SOCKET_IP_ADDRESS, null);
return DepenizenPlugin.getCurrentInstance().getConfig().getString("Socket.IP Address", null);
}

public static int socketPort() {
return DepenizenPlugin.getCurrentInstance().getConfig().getInt(SOCKET_PORT, 25578);
return DepenizenPlugin.getCurrentInstance().getConfig().getInt("Socket.Port", 25578);
}

public static String socketPassword() {
return DepenizenPlugin.getCurrentInstance().getConfig().getString(SOCKET_PASSWORD, null);
return DepenizenPlugin.getCurrentInstance().getConfig().getString("Socket.Password", null);
}

public static String socketName() {
return DepenizenPlugin.getCurrentInstance().getConfig().getString(SOCKET_NAME, null);
return DepenizenPlugin.getCurrentInstance().getConfig().getString("Socket.Name", null);
}

public static Duration socketPingDelay() {
return Duration.valueOf(DepenizenPlugin.getCurrentInstance().getConfig().getString("Socket.Ping Delay", "30s"));
}

public static Duration socketPingTimeout() {
return Duration.valueOf(DepenizenPlugin.getCurrentInstance().getConfig().getString("Socket.Ping Timeout", "30s"));
}

public static int socketTimeout() {
return DepenizenPlugin.getCurrentInstance().getConfig().getInt(SOCKET_TIMEOUT, 0);
public static Duration socketReconnectDelay() {
if (DepenizenPlugin.getCurrentInstance().getConfig().contains("Socket.Reconnection Delay")) {
return Duration.valueOf(DepenizenPlugin.getCurrentInstance().getConfig().getString("Socket.Reconnection Delay", "10s"));
}
// For backwards compatibility
return new Duration((int) DepenizenPlugin.getCurrentInstance().getConfig().getLong("Socket.Reconnect Delay", 10000) / 1000);
}

public static long socketReconnectDelay() {
return DepenizenPlugin.getCurrentInstance().getConfig().getLong(SOCKET_RECONNECT_DELAY, 10000);
public static int socketReconnectAttempts() {
return DepenizenPlugin.getCurrentInstance().getConfig().getInt("Socket.Reconnection Attempts", 10);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.denizenscript.depenizen.bukkit.support.bungee;

import com.denizenscript.depenizen.bukkit.Settings;
import com.denizenscript.depenizen.bukkit.objects.bungee.dServer;
import com.denizenscript.depenizen.common.socket.client.SocketClient;
import net.aufdemrand.denizen.BukkitScriptEntryData;
Expand Down Expand Up @@ -27,6 +28,26 @@ protected boolean isBungeeScriptCompatible() {
return true;
}

@Override
protected long getPingDelay() {
return Settings.socketPingDelay().getMillis();
}

@Override
protected long getPingTimeout() {
return Settings.socketPingTimeout().getMillis();
}

@Override
protected int getReconnectAttempts() {
return Settings.socketReconnectAttempts();
}

@Override
protected long getReconnectDelay() {
return Settings.socketReconnectDelay().getMillis();
}

@Override
public void handleAcceptRegister(String registrationName, List<String> existingServers) {
dServer.addOnlineServer(registrationName);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
import net.aufdemrand.denizencore.tags.TagContext;
import org.bukkit.Bukkit;

import java.io.IOException;

public class BungeeSupport extends Support {

public BungeeSupport() {
Expand Down Expand Up @@ -107,6 +109,10 @@ public static void startSocket() {
socketClient = new BukkitSocketClient(ipAddress, Settings.socketPort(), name, password.toCharArray());
socketClient.connect();
}
catch (IOException e) {
dB.echoError("BungeeCord Socket is not online.");
socketClient.attemptReconnect();
}
catch (Exception e) {
dB.echoError("BungeeCord Socket failed to connect due to an exception.");
dB.echoError(e);
Expand All @@ -124,7 +130,7 @@ public static boolean isSocketConnected() {

public static void closeSocket() {
if (isSocketConnected()) {
socketClient.close("Closed.");
socketClient.close("Closed.", false);
}
}
}
30 changes: 20 additions & 10 deletions bukkit/src/main/resources/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,28 @@
# between servers without the need for online players on each
# sender and recipient
Socket:
# Whether the BungeeCord Socket is enabled and should automatically attempt to
# connect on server startup.
Enabled: false
Ip address: bungee.mcmonkey.org
# The IP address that is hosting a DepenizenBungee socket server.
IP Address: "bungee.mcmonkey.org"
# The port at the IP address that is hosting a DepenizenBungee socket server.
Port: 25578
Password: MySecretPassword
# The password to connect with. Please leave this in "quotes" to keep
# YAML from thinking it's an invalid string (for example, if it's all numbers).
Password: "MySecretPassword"
# The name to connect with. Used for the Bungee dServer object.
# This does not have to be the same name that Bungee itself
# recognizes, but it is recommended to use that for consistency.
Name: MyServer1
# The time in milliseconds that this server should take while
# trying to connect to the BungeeCord Socket before giving up.
# Set this to 0 to try to connect until there's an error
Timeout: 0
# The time in milliseconds to wait between attempts at reconnecting
# to the socket
Reconnect Delay: 10000
Name: "MyServer1"
# The duration of inactivity that the client will send a ping
# packet to the server after.
Ping Delay: "30s"
# The duration that this client should wait after sending a ping
# packet before disconnecting and attempting to reconnect.
Ping Timeout: "30s"
# The duration to wait between attempts at reconnecting to the socket.
Reconnection Delay: "10s"
# The amount of times the socket should attempt to reconnect to the socket
# before giving up.
Reconnection Attempts: 10
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package com.denizenscript.depenizen.common.socket.client;

import com.denizenscript.depenizen.common.Depenizen;

import java.io.IOException;

public class ReconnectTask implements Runnable {

private SocketClient client;
private int attemptsLeft;
private long reconnectDelay;

public ReconnectTask(SocketClient client, int attempts, long reconnectDelay) {
this.client = client;
this.attemptsLeft = attempts;
this.reconnectDelay = reconnectDelay;
}

@Override
public void run() {
while (attemptsLeft > 0 && !client.isConnected()) {
try {
Thread.sleep(reconnectDelay);
}
catch (InterruptedException e) {
Depenizen.getImplementation().debugError("BungeeCord Socket failed to reconnect due to an exception.");
Depenizen.getImplementation().debugException(e);
}
try {
attemptsLeft--;
client.connect();
}
catch (IOException e) {
if (attemptsLeft <= 0) {
Depenizen.getImplementation().debugError("Failed to reconnect to BungeeCord Socket.");
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ public abstract class SocketClient implements Runnable {
private Encryption encryption;
private Socket socket;
private Thread listenThread;
private Thread reconnectThread;
private DataOutputStream output;
private DataInputStream input;
private boolean isConnected;
Expand All @@ -44,10 +45,10 @@ public SocketClient(String ipAddress, int port, String name, char[] password) th
public void connect() throws IOException {
if (!isConnected) {
socket = new Socket(ipAddress, port);
listenThread = new Thread(this);
listenThread.start();
output = new DataOutputStream(socket.getOutputStream());
input = new DataInputStream(socket.getInputStream());
listenThread = new Thread(this);
listenThread.start();
}
}

Expand All @@ -69,15 +70,15 @@ public void trySend(Packet packet) {
send(packet);
}
catch (IOException e) {
close("Server socket closed");
close("Server socket closed", true);
}
catch (Exception e) {
close("Failed to send data due to an exception");
close("Failed to send data due to an exception", true);
Depenizen.getImplementation().debugException(e);
}
}

public void close(String reason) {
public void close(String reason, boolean shouldReconnect) {
if (isConnected) {
if (reason != null) {
Depenizen.getImplementation().debugMessage("Disconnected from socket: " + reason);
Expand All @@ -93,19 +94,31 @@ public void close(String reason) {
if (socket != null) {
socket.close();
}
if (shouldReconnect) {
attemptReconnect();
}
}
catch (Exception e) {
Depenizen.getImplementation().debugException(e);
}
}
}

public void attemptReconnect() {
if (!isConnected && reconnectThread == null) {
Depenizen.getImplementation().debugMessage("Attempting to reconnect...");
reconnectThread = new Thread(new ReconnectTask(this, getReconnectAttempts(), getReconnectDelay()));
reconnectThread.start();
}
}

private int lastPingBit;

@Override
public void run() {
if (socket != null && socket.isConnected()) {
isConnected = true;
reconnectThread = null;
byte[] buffer;
try {
while (input.available() <= 0) {
Expand All @@ -126,19 +139,19 @@ public void run() {
break connectionLoop;
}
timePassed = System.currentTimeMillis() - start;
if (timePassed > 30 * 1000 && !pinged) {
if (timePassed > getPingDelay() && !pinged) {
lastPingBit = Utilities.getRandomUnsignedByte();
send(new ClientPacketOutPing(lastPingBit));
pinged = true;
}
if (timePassed > 60 * 1000) {
close("Ping timed out!");
if (timePassed > getPingDelay() + getPingTimeout()) {
close("Ping timed out!", true);
break connectionLoop;
}
Thread.sleep(50);
}
if (receivedEncryptedLength == -1) {
close("Connection failed");
close("Connection failed", true);
break;
}
buffer = new byte[receivedEncryptedLength];
Expand All @@ -150,13 +163,13 @@ public void run() {
int packetId = data.readUnsignedByte();
Packet.ClientBound packetType = Packet.ClientBound.getById(packetId);
if (packetType == null) {
close("Received invalid packet from server: " + packetId);
close("Received invalid packet from server: " + packetId, true);
break;
}
switch (packetType) {
case ACCEPT_REGISTER:
if (registered) {
close("Server tried to accept registration twice");
close("Server tried to accept registration twice", true);
break connectionLoop;
}
ClientPacketInAcceptRegister acceptRegister = new ClientPacketInAcceptRegister();
Expand All @@ -167,7 +180,7 @@ public void run() {
registered = true;
}
else {
close("Specified name in config.yml is already registered to the server");
close("Specified name in config.yml is already registered to the server", false);
break connectionLoop;
}
break;
Expand All @@ -180,7 +193,7 @@ public void run() {
ClientPacketInPong pong = new ClientPacketInPong();
pong.deserialize(data);
if (pong.getBit() != lastPingBit) {
close("Invalid ping bit: Expected " + lastPingBit + ", got " + pong.getBit());
close("Invalid ping bit: Expected " + lastPingBit + ", got " + pong.getBit(), true);
break connectionLoop;
}
break;
Expand All @@ -204,25 +217,31 @@ public void run() {
listenThread = null;
}
catch (IllegalStateException e) {
close("Password is incorrect");
close("Password is incorrect", false);
}
catch (SocketTimeoutException e) {
close("Connection timed out");
//attemptReconnect();
close("Connection timed out", true);
}
catch (IOException e) {
close("Server socket closed");
//attemptReconnect();
close("Server socket closed", true);
}
catch (Exception e) {
close("Error receiving data from server: " + e.getMessage());
close("Error receiving data from server: " + e.getMessage(), true);
Depenizen.getImplementation().debugException(e);
}
}
}

protected abstract boolean isBungeeScriptCompatible();

protected abstract long getPingDelay();

protected abstract long getPingTimeout();

protected abstract int getReconnectAttempts();

protected abstract long getReconnectDelay();

protected abstract void handleAcceptRegister(String registrationName, List<String> existingServers);

protected abstract void handleUpdateServer(String serverName, boolean registered);
Expand Down

0 comments on commit 1e25a9f

Please sign in to comment.