Close #306 use SQLite for reconnect locations

This commit is contained in:
md_5 2013-06-01 17:29:14 +10:00
parent 757f8f0cb9
commit 2a421cdd8d
5 changed files with 143 additions and 105 deletions

View File

@ -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();
} }

View File

@ -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();

View File

@ -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 );
}
}
}

View File

@ -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);
}

View File

@ -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 );
}
}
}