Close #306 use SQLite for reconnect locations
This commit is contained in:
parent
757f8f0cb9
commit
2a421cdd8d
@ -12,7 +12,7 @@ public interface ReconnectHandler
|
|||||||
* @param player the connecting player
|
* @param player the connecting player
|
||||||
* @return the server to connect to
|
* @return the server to connect to
|
||||||
*/
|
*/
|
||||||
public ServerInfo getServer(ProxiedPlayer player);
|
ServerInfo getServer(ProxiedPlayer player);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Save the server of this player before they disconnect so it can be
|
* Save the server of this player before they disconnect so it can be
|
||||||
@ -20,12 +20,20 @@ public interface ReconnectHandler
|
|||||||
*
|
*
|
||||||
* @param player the player to save
|
* @param player the player to save
|
||||||
*/
|
*/
|
||||||
public void setServer(ProxiedPlayer player);
|
void setServer(ProxiedPlayer player); // TOOD: String + String arguments?
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Save all pending reconnect locations. Whilst not used for database
|
* Save all pending reconnect locations. Whilst not used for database
|
||||||
* connections, this method will be called at a predefined interval to allow
|
* connections, this method will be called at a predefined interval to allow
|
||||||
* the saving of reconnect files.
|
* the saving of reconnect files.
|
||||||
*/
|
*/
|
||||||
public void save();
|
void save();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close all connections indicating that the proxy is about to shutdown and
|
||||||
|
* all data should be saved. No new requests will be made after this method
|
||||||
|
* has been called.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void close();
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package net.md_5.bungee;
|
package net.md_5.bungee;
|
||||||
|
|
||||||
|
import net.md_5.bungee.reconnect.SQLReconnectHandler;
|
||||||
import net.md_5.bungee.scheduler.BungeeScheduler;
|
import net.md_5.bungee.scheduler.BungeeScheduler;
|
||||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||||
import com.ning.http.client.AsyncHttpClient;
|
import com.ning.http.client.AsyncHttpClient;
|
||||||
@ -198,7 +199,7 @@ public class BungeeCord extends ProxyServer
|
|||||||
config.load();
|
config.load();
|
||||||
if ( reconnectHandler == null )
|
if ( reconnectHandler == null )
|
||||||
{
|
{
|
||||||
reconnectHandler = new YamlReconnectHandler();
|
reconnectHandler = new SQLReconnectHandler();
|
||||||
}
|
}
|
||||||
isRunning = true;
|
isRunning = true;
|
||||||
|
|
||||||
@ -302,6 +303,7 @@ public class BungeeCord extends ProxyServer
|
|||||||
|
|
||||||
getLogger().info( "Saving reconnect locations" );
|
getLogger().info( "Saving reconnect locations" );
|
||||||
reconnectHandler.save();
|
reconnectHandler.save();
|
||||||
|
reconnectHandler.close();
|
||||||
saveThread.cancel();
|
saveThread.cancel();
|
||||||
metricsThread.cancel();
|
metricsThread.cancel();
|
||||||
|
|
||||||
|
@ -1,101 +0,0 @@
|
|||||||
package net.md_5.bungee;
|
|
||||||
|
|
||||||
import com.google.common.base.Preconditions;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileReader;
|
|
||||||
import java.io.FileWriter;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.net.InetSocketAddress;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
|
||||||
import java.util.logging.Level;
|
|
||||||
import net.md_5.bungee.api.ProxyServer;
|
|
||||||
import net.md_5.bungee.api.ReconnectHandler;
|
|
||||||
import net.md_5.bungee.api.config.ListenerInfo;
|
|
||||||
import net.md_5.bungee.api.config.ServerInfo;
|
|
||||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
|
||||||
import org.yaml.snakeyaml.Yaml;
|
|
||||||
|
|
||||||
public class YamlReconnectHandler implements ReconnectHandler
|
|
||||||
{
|
|
||||||
|
|
||||||
private final Yaml yaml = new Yaml();
|
|
||||||
private final File readFile = new File( "locations.yml" );
|
|
||||||
private final File writeFile = new File( "locations.yml~" );
|
|
||||||
/*========================================================================*/
|
|
||||||
private Map<String, String> data;
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public YamlReconnectHandler()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
readFile.createNewFile();
|
|
||||||
try ( FileReader rd = new FileReader( readFile ) )
|
|
||||||
{
|
|
||||||
data = yaml.loadAs( rd, Map.class );
|
|
||||||
}
|
|
||||||
} catch ( Exception ex )
|
|
||||||
{
|
|
||||||
ProxyServer.getInstance().getLogger().log( Level.WARNING, "Could not load reconnect locations", ex );
|
|
||||||
readFile.delete();
|
|
||||||
ProxyServer.getInstance().getLogger().log( Level.WARNING, "Reconnect locations have been reset, do NOT worry about this error" );
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( data == null )
|
|
||||||
{
|
|
||||||
data = new ConcurrentHashMap<>();
|
|
||||||
} else
|
|
||||||
{
|
|
||||||
data = new ConcurrentHashMap<>( data );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ServerInfo getServer(ProxiedPlayer player)
|
|
||||||
{
|
|
||||||
ListenerInfo listener = player.getPendingConnection().getListener();
|
|
||||||
String name;
|
|
||||||
String forced = listener.getForcedHosts().get( player.getPendingConnection().getVirtualHost().getHostName().toLowerCase() );
|
|
||||||
if ( forced == null && listener.isForceDefault() )
|
|
||||||
{
|
|
||||||
forced = listener.getDefaultServer();
|
|
||||||
}
|
|
||||||
|
|
||||||
String server = ( forced == null ) ? data.get( key( player ) ) : forced;
|
|
||||||
name = ( server != null ) ? server : listener.getDefaultServer();
|
|
||||||
ServerInfo info = ProxyServer.getInstance().getServerInfo( name );
|
|
||||||
if ( info == null )
|
|
||||||
{
|
|
||||||
info = ProxyServer.getInstance().getServerInfo( listener.getDefaultServer() );
|
|
||||||
}
|
|
||||||
Preconditions.checkState( info != null, "Default server not defined" );
|
|
||||||
return info;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setServer(ProxiedPlayer player)
|
|
||||||
{
|
|
||||||
data.put( key( player ), player.getServer().getInfo().getName() );
|
|
||||||
}
|
|
||||||
|
|
||||||
private String key(ProxiedPlayer player)
|
|
||||||
{
|
|
||||||
InetSocketAddress host = player.getPendingConnection().getVirtualHost();
|
|
||||||
return player.getName() + ";" + host.getHostString() + ":" + host.getPort();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public synchronized void save()
|
|
||||||
{
|
|
||||||
try ( FileWriter wr = new FileWriter( writeFile ) )
|
|
||||||
{
|
|
||||||
yaml.dump( data, wr );
|
|
||||||
readFile.delete();
|
|
||||||
writeFile.renameTo( readFile );
|
|
||||||
} catch ( IOException ex )
|
|
||||||
{
|
|
||||||
ProxyServer.getInstance().getLogger().log( Level.WARNING, "Could not save reconnect locations", ex );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,36 @@
|
|||||||
|
package net.md_5.bungee.reconnect;
|
||||||
|
|
||||||
|
import com.google.common.base.Preconditions;
|
||||||
|
import net.md_5.bungee.api.ProxyServer;
|
||||||
|
import net.md_5.bungee.api.ReconnectHandler;
|
||||||
|
import net.md_5.bungee.api.config.ListenerInfo;
|
||||||
|
import net.md_5.bungee.api.config.ServerInfo;
|
||||||
|
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||||
|
|
||||||
|
public abstract class AbstractReconnectManager implements ReconnectHandler
|
||||||
|
{
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ServerInfo getServer(ProxiedPlayer player)
|
||||||
|
{
|
||||||
|
ListenerInfo listener = player.getPendingConnection().getListener();
|
||||||
|
String name;
|
||||||
|
String forced = listener.getForcedHosts().get( player.getPendingConnection().getVirtualHost().getHostName().toLowerCase() );
|
||||||
|
if ( forced == null && listener.isForceDefault() )
|
||||||
|
{
|
||||||
|
forced = listener.getDefaultServer();
|
||||||
|
}
|
||||||
|
|
||||||
|
String server = ( forced == null ) ? getStoredServer( player ) : forced;
|
||||||
|
name = ( server != null ) ? server : listener.getDefaultServer();
|
||||||
|
ServerInfo info = ProxyServer.getInstance().getServerInfo( name );
|
||||||
|
if ( info == null )
|
||||||
|
{
|
||||||
|
info = ProxyServer.getInstance().getServerInfo( listener.getDefaultServer() );
|
||||||
|
}
|
||||||
|
Preconditions.checkState( info != null, "Default server not defined" );
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract String getStoredServer(ProxiedPlayer player);
|
||||||
|
}
|
@ -0,0 +1,93 @@
|
|||||||
|
package net.md_5.bungee.reconnect;
|
||||||
|
|
||||||
|
import java.sql.Connection;
|
||||||
|
import java.sql.DriverManager;
|
||||||
|
import java.sql.PreparedStatement;
|
||||||
|
import java.sql.ResultSet;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import net.md_5.bungee.api.ProxyServer;
|
||||||
|
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||||
|
|
||||||
|
public class SQLReconnectHandler extends AbstractReconnectManager
|
||||||
|
{
|
||||||
|
|
||||||
|
private final Connection connection;
|
||||||
|
|
||||||
|
public SQLReconnectHandler() throws ClassNotFoundException, SQLException
|
||||||
|
{
|
||||||
|
Class.forName( "org.sqlite.JDBC" );
|
||||||
|
connection = DriverManager.getConnection( "jdbc:sqlite:bungee.sqlite" );
|
||||||
|
|
||||||
|
try ( PreparedStatement ps = connection.prepareStatement(
|
||||||
|
"CREATE TABLE IF NOT EXISTS players ("
|
||||||
|
+ "playerId INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,"
|
||||||
|
+ "username TEXT NOT NULL UNIQUE COLLATE NOCASE,"
|
||||||
|
+ "seen INTEGER,"
|
||||||
|
+ "server TEXT"
|
||||||
|
+ ");" ) )
|
||||||
|
{
|
||||||
|
ps.executeUpdate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getStoredServer(ProxiedPlayer player)
|
||||||
|
{
|
||||||
|
String server = null;
|
||||||
|
try ( PreparedStatement ps = connection.prepareStatement( "SELECT server FROM players WHERE username = ?" ) )
|
||||||
|
{
|
||||||
|
ps.setString( 1, player.getName() );
|
||||||
|
try ( ResultSet rs = ps.executeQuery() )
|
||||||
|
{
|
||||||
|
if ( rs.next() )
|
||||||
|
{
|
||||||
|
server = rs.getString( 1 );
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
try ( PreparedStatement playerUpdate = connection.prepareStatement( "INSERT INTO players( username ) VALUES( ? )" ) )
|
||||||
|
{
|
||||||
|
playerUpdate.setString( 1, player.getName() );
|
||||||
|
playerUpdate.executeUpdate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch ( SQLException ex )
|
||||||
|
{
|
||||||
|
ProxyServer.getInstance().getLogger().log( Level.WARNING, "Could not load location for player " + player.getName(), ex );
|
||||||
|
}
|
||||||
|
return server;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setServer(ProxiedPlayer player)
|
||||||
|
{
|
||||||
|
|
||||||
|
try ( PreparedStatement ps = connection.prepareStatement( "UPDATE players SET server = ? WHERE username = ?" ) )
|
||||||
|
{
|
||||||
|
ps.setString( 1, player.getServer().getInfo().getName() );
|
||||||
|
ps.setString( 2, player.getName() );
|
||||||
|
ps.executeUpdate();
|
||||||
|
} catch ( SQLException ex )
|
||||||
|
{
|
||||||
|
ProxyServer.getInstance().getLogger().log( Level.WARNING, "Could not save location for player " + player.getName() + " on server " + player.getServer().getInfo().getName(), ex );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void save()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
connection.close();
|
||||||
|
} catch ( SQLException ex )
|
||||||
|
{
|
||||||
|
ProxyServer.getInstance().getLogger().log( Level.WARNING, "Error closing SQLite connection", ex );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user