Store UUIDs in a SQLite database. Resolves #373 and resolves #406

This commit is contained in:
JeromSar 2015-02-16 17:00:38 +01:00
parent 34ebb3586c
commit af52dec24a
24 changed files with 1012 additions and 401 deletions

View file

@ -1,3 +1,3 @@
#Build Number for ANT. Do not edit!
#Mon Feb 16 15:45:38 CET 2015
build.number=976
#Mon Feb 16 16:40:10 CET 2015
build.number=982

View file

@ -75,7 +75,7 @@ public class Command_fr extends TFM_Command
public static void setAllFrozen(boolean freeze)
{
allFrozen = freeze;
for (TFM_PlayerData data : TFM_PlayerData.USER_INFO.values())
for (TFM_PlayerData data : TFM_PlayerData.PLAYER_DATA.values())
{
data.setFrozen(freeze);
}

View file

@ -165,7 +165,7 @@ public class Command_saconfig extends TFM_Command
targetName = player.getName();
}
if (!TFM_AdminList.getLowerSuperNames().contains(targetName.toLowerCase()))
if (!TFM_AdminList.getLowercaseSuperNames().contains(targetName.toLowerCase()))
{
playerMsg("Superadmin not found: " + targetName);
return true;

View file

@ -11,6 +11,7 @@ import org.bukkit.entity.Player;
@CommandParameters(description = "Shows the status of all Mojang services", usage = "/<command>")
public class Command_services extends TFM_Command
{
@Override
public boolean run(CommandSender sender, Player sender_p, Command cmd, String commandLabel, String[] args, boolean senderIsConsole)
{

View file

@ -10,83 +10,34 @@ import me.StevenLawson.TotalFreedomMod.TFM_AdminList;
import me.StevenLawson.TotalFreedomMod.TFM_Player;
import me.StevenLawson.TotalFreedomMod.TFM_PlayerList;
import me.StevenLawson.TotalFreedomMod.TFM_UuidManager;
import me.StevenLawson.TotalFreedomMod.TFM_UuidResolver;
import me.StevenLawson.TotalFreedomMod.TFM_UuidManager.TFM_UuidResolver;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
@CommandPermissions(level = AdminLevel.SENIOR, source = SourceType.ONLY_CONSOLE)
@CommandParameters(description = "Provides uuid tools", usage = "/<command> <purge | recalculate <admin | player>>")
@CommandParameters(description = "Provides uuid tools", usage = "/<command> <purge | recalculate>")
public class Command_uuid extends TFM_Command
{
@Override
public boolean run(CommandSender sender, Player sender_p, Command cmd, String commandLabel, String[] args, boolean senderIsConsole)
{
if (args.length == 0 || args.length > 2)
{
return false;
}
if (args.length == 1)
{
if (!"purge".equals(args[0]))
if (args.length != 1)
{
return false;
}
if ("purge".equals(args[0])) {
playerMsg("Purged " + TFM_UuidManager.purge() + " cached UUIDs.");
return true;
}
if ("recalculate".equals(args[0]))
{
playerMsg("Recalculating UUIDs...");
if ("admin".equals(args[1]))
{
playerMsg("Recalculating admin UUIDs...");
final Set<TFM_Admin> admins = TFM_AdminList.getAllAdmins();
final List<String> names = new ArrayList<String>();
for (TFM_Admin admin : admins)
{
names.add(admin.getLastLoginName());
}
final Map<String, UUID> uuids = new TFM_UuidResolver(names).call();
int updated = 0;
for (String name : uuids.keySet())
{
for (TFM_Admin admin : admins)
{
if (!admin.getLastLoginName().equalsIgnoreCase(name))
{
continue;
}
if (admin.getUniqueId().equals(uuids.get(name)))
{
continue;
}
TFM_AdminList.setUuid(admin, admin.getUniqueId(), uuids.get(name));
updated++;
break;
}
}
playerMsg("Done, recalculated " + updated + " UUIDs");
return true;
}
if ("player".equals(args[1]))
{
playerMsg("Recalculating player UUIDs...");
// Playerlist uuids
final Set<TFM_Player> players = TFM_PlayerList.getAllPlayers();
final List<String> names = new ArrayList<String>();
for (TFM_Player player : players)
@ -94,10 +45,10 @@ public class Command_uuid extends TFM_Command
names.add(player.getLastLoginName());
}
final Map<String, UUID> uuids = new TFM_UuidResolver(names).call();
final Map<String, UUID> playerUuids = new TFM_UuidResolver(names).call();
int updated = 0;
for (String name : uuids.keySet())
for (String name : playerUuids.keySet())
{
for (TFM_Player player : players)
{
@ -106,23 +57,55 @@ public class Command_uuid extends TFM_Command
continue;
}
if (player.getUniqueId().equals(uuids.get(name)))
if (player.getUniqueId().equals(playerUuids.get(name)))
{
continue;
}
TFM_PlayerList.setUniqueId(player, uuids.get(name));
TFM_PlayerList.setUniqueId(player, playerUuids.get(name));
TFM_UuidManager.rawSetUUID(name, playerUuids.get(name));
updated++;
break;
}
}
playerMsg("Done, recalculated " + updated + " UUIDs");
return true;
playerMsg("Recalculated " + updated + " player UUIDs");
names.clear();
// Adminlist UUIDs
final Set<TFM_Admin> admins = TFM_AdminList.getAllAdmins();
for (TFM_Admin admin : admins)
{
names.add(admin.getLastLoginName());
}
return false;
final Map<String, UUID> adminUuids = new TFM_UuidResolver(names).call();
updated = 0;
for (String name : adminUuids.keySet())
{
for (TFM_Admin admin : admins)
{
if (!admin.getLastLoginName().equalsIgnoreCase(name))
{
continue;
}
if (admin.getUniqueId().equals(adminUuids.get(name)))
{
continue;
}
TFM_AdminList.setUuid(admin, admin.getUniqueId(), adminUuids.get(name));
TFM_UuidManager.rawSetUUID(name, adminUuids.get(name));
updated++;
break;
}
}
playerMsg("Recalculated " + updated + " admin UUIDs");
return true;
}
return false;

View file

@ -4,8 +4,7 @@ import me.StevenLawson.TotalFreedomMod.HTTPD.NanoHTTPD.Response;
public class TFM_HTTPD_PageBuilder
{
private static final String TEMPLATE
= "<!DOCTYPE html>\r\n"
private static final String TEMPLATE = "<!DOCTYPE html>\r\n"
+ "<html>\r\n"
+ "<head>\r\n"
+ "<title>{$TITLE}</title>\r\n"
@ -16,8 +15,7 @@ public class TFM_HTTPD_PageBuilder
+ "<body>\r\n{$BODY}</body>\r\n"
+ "</html>\r\n";
private static final String STYLE = "<style type=\"text/css\">{$STYLE}</style>\r\n";
private static final String SCRIPT
= "<script src=\"//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js\"></script>\r\n"
private static final String SCRIPT = "<script src=\"//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js\"></script>\r\n"
+ "<script src=\"//ajax.googleapis.com/ajax/libs/jqueryui/1.10.3/jquery-ui.min.js\"></script>\r\n"
+ "<script>\r\n{$SCRIPT}\r\n</script>\r\n";
//

View file

@ -0,0 +1,102 @@
package me.StevenLawson.TotalFreedomMod.SQL;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.SQLException;
import me.StevenLawson.TotalFreedomMod.TFM_Log;
public class TFM_SqlUtil
{
public static boolean hasTable(Connection con, String table)
{
try
{
final DatabaseMetaData dbm = con.getMetaData();
final ResultSet tables = dbm.getTables(null, null, table, null);
return tables.next();
}
catch (SQLException ex)
{
TFM_Log.severe(ex);
return false;
}
}
public static ResultSet executeQuery(Connection con, String query)
{
try
{
return con.createStatement().executeQuery(query);
}
catch (SQLException ex)
{
TFM_Log.severe(ex);
return null;
}
}
public static int updateQuery(Connection con, String query)
{
try
{
return con.createStatement().executeUpdate(query);
}
catch (SQLException ex)
{
TFM_Log.severe(ex);
return -1;
}
}
public static boolean createTable(Connection con, String name, String fields)
{
try
{
con.createStatement().execute("CREATE TABLE " + name + " (" + fields + ");");
return true;
}
catch (SQLException ex)
{
TFM_Log.severe(ex);
return false;
}
}
public static void close(ResultSet result)
{
if (result == null)
{
return;
}
try
{
result.close();
}
catch (SQLException ex)
{
TFM_Log.severe(ex);
}
}
public static boolean hasData(ResultSet result)
{
if (result == null)
{
return false;
}
try
{
return result.next();
}
catch (SQLException ex)
{
TFM_Log.severe(ex);
return false;
}
}
}

View file

@ -0,0 +1,143 @@
package me.StevenLawson.TotalFreedomMod.SQL;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import me.StevenLawson.TotalFreedomMod.TFM_Log;
import me.StevenLawson.TotalFreedomMod.TotalFreedomMod;
import me.husky.Database;
import me.husky.sqlite.SQLite;
public class TFM_SqliteDatabase
{
private final Database sql;
private final String table;
private final String fields;
private final List<Statement> statements;
public TFM_SqliteDatabase(String filename, String table, String fields)
{
this.sql = new SQLite(TotalFreedomMod.plugin, filename);
this.table = table;
this.fields = fields;
this.statements = new ArrayList<Statement>();
}
public Statement addPreparedStatement(String query)
{
if (sql.checkConnection())
{
throw new IllegalStateException("Can not add prepared statements after connecting!");
}
final Statement statement = new Statement(query);
statements.add(statement);
return statement;
}
@Deprecated
public Database db()
{
return sql;
}
public boolean connect()
{
if (sql.checkConnection())
{
return true;
}
final Connection con = sql.openConnection();
if (con == null)
{
return false;
}
if (!TFM_SqlUtil.hasTable(con, table))
{
TFM_Log.info("Creating table: " + table);
if (!TFM_SqlUtil.createTable(con, table, fields))
{
TFM_Log.severe("Could not create table: " + table);
return false;
}
}
// Prepare statements
for (Statement statement : statements)
{
if (!statement.prepare())
{
return false;
}
}
return true;
}
public void close()
{
sql.closeConnection();
}
public int purge()
{
if (!connect())
{
return 0;
}
TFM_Log.warning("Truncating table: " + table);
final int result = TFM_SqlUtil.updateQuery(sql.getConnection(), "DELETE FROM " + table + ";");
if (result == -1)
{
TFM_Log.warning("Could not truncate table: " + table);
}
return result;
}
public class Statement
{
private final String query;
private PreparedStatement statement;
private Statement(String query)
{
this.query = query;
}
private boolean prepare()
{
try
{
statement = sql.getConnection().prepareStatement(query);
return true;
}
catch (SQLException ex)
{
TFM_Log.severe("Could not prepare statement: " + query);
TFM_Log.severe(ex);
return false;
}
}
public void invalidate()
{
statement = null;
statements.remove(this);
}
public PreparedStatement getStatement()
{
return statement;
}
}
}

View file

@ -72,6 +72,11 @@ public class TFM_AdminList
return Collections.unmodifiableSet(superIps);
}
public static Set<TFM_Admin> getAllAdmins()
{
return Sets.newHashSet(adminList.values());
}
public static Set<String> getSuperNames()
{
final Set<String> names = new HashSet<String>();
@ -89,7 +94,7 @@ public class TFM_AdminList
return Collections.unmodifiableSet(names);
}
public static Set<String> getLowerSuperNames()
public static Set<String> getLowercaseSuperNames()
{
final Set<String> names = new HashSet<String>();
@ -106,11 +111,6 @@ public class TFM_AdminList
return Collections.unmodifiableSet(names);
}
public static Set<TFM_Admin> getAllAdmins()
{
return Sets.newHashSet(adminList.values());
}
public static void setUuid(TFM_Admin admin, UUID oldUuid, UUID newUuid)
{
if (!adminList.containsKey(oldUuid))
@ -125,6 +125,7 @@ public class TFM_AdminList
return;
}
// Add new entry
final TFM_Admin newAdmin = new TFM_Admin(
newUuid,
admin.getLastLoginName(),
@ -133,19 +134,16 @@ public class TFM_AdminList
admin.isTelnetAdmin(),
admin.isSeniorAdmin(),
admin.isActivated());
newAdmin.addIps(admin.getIps());
adminList.remove(oldUuid);
adminList.put(newUuid, newAdmin);
save(newAdmin);
// Remove old entry
adminList.remove(oldUuid);
final TFM_Config config = new TFM_Config(TotalFreedomMod.plugin, TotalFreedomMod.SUPERADMIN_FILENAME, true);
config.load();
config.set("admins." + oldUuid.toString(), null);
config.save();
save(newAdmin);
}
public static void load()
@ -173,7 +171,7 @@ public class TFM_AdminList
for (String uuidString : section.getKeys(false))
{
if (!TFM_UuidManager.isUniqueId(uuidString))
if (!TFM_Util.isUniqueId(uuidString))
{
TFM_Log.warning("Invalid Unique ID: " + uuidString + " in superadmin.yml, ignoring");
continue;
@ -394,34 +392,15 @@ public class TFM_AdminList
saveAll();
}
public static boolean isSeniorAdmin(CommandSender sender)
public static boolean isSuperAdminSafe(UUID uuid, String ip)
{
return isSeniorAdmin(sender, false);
if (TotalFreedomMod.server.getOnlineMode())
{
return TFM_AdminList.getSuperUUIDs().contains(uuid);
}
public static boolean isSeniorAdmin(CommandSender sender, boolean verifySuperadmin)
{
if (verifySuperadmin)
{
if (!isSuperAdmin(sender))
{
return false;
}
}
if (!(sender instanceof Player))
{
return seniorConsoleNames.contains(sender.getName())
|| (TFM_MainConfig.getBoolean(TFM_ConfigEntry.CONSOLE_IS_SENIOR) && sender.getName().equals("CONSOLE"));
}
final TFM_Admin entry = getEntry((Player) sender);
if (entry != null)
{
return entry.isSeniorAdmin();
}
return false;
final TFM_Admin admin = TFM_AdminList.getEntryByIp(ip);
return admin != null && admin.isActivated();
}
public static boolean isSuperAdmin(CommandSender sender)
@ -470,6 +449,36 @@ public class TFM_AdminList
return false;
}
public static boolean isSeniorAdmin(CommandSender sender)
{
return isSeniorAdmin(sender, false);
}
public static boolean isSeniorAdmin(CommandSender sender, boolean verifySuperadmin)
{
if (verifySuperadmin)
{
if (!isSuperAdmin(sender))
{
return false;
}
}
if (!(sender instanceof Player))
{
return seniorConsoleNames.contains(sender.getName())
|| (TFM_MainConfig.getBoolean(TFM_ConfigEntry.CONSOLE_IS_SENIOR) && sender.getName().equals("CONSOLE"));
}
final TFM_Admin entry = getEntry((Player) sender);
if (entry != null)
{
return entry.isSeniorAdmin();
}
return false;
}
public static boolean isIdentityMatched(Player player)
{
if (!isSuperAdmin(player))

View file

@ -8,6 +8,7 @@ import java.util.UUID;
import me.StevenLawson.TotalFreedomMod.Config.TFM_Config;
import me.StevenLawson.TotalFreedomMod.Config.TFM_ConfigEntry;
import me.StevenLawson.TotalFreedomMod.TFM_Ban.BanType;
import me.StevenLawson.TotalFreedomMod.TFM_UuidManager.TFM_UuidResolver;
import org.bukkit.entity.Player;
public class TFM_BanManager

View file

@ -94,4 +94,14 @@ public class TFM_Log
return pluginLogger;
}
}
public static Logger getPluginLogger()
{
return (pluginLogger != null ? pluginLogger : FALLBACK_LOGGER);
}
public static Logger getServerLogger()
{
return (serverLogger != null ? serverLogger : FALLBACK_LOGGER);
}
}

View file

@ -2,10 +2,8 @@ package me.StevenLawson.TotalFreedomMod;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.UUID;
import me.StevenLawson.TotalFreedomMod.Bridge.TFM_EssentialsBridge;
import me.StevenLawson.TotalFreedomMod.Config.TFM_ConfigEntry;
@ -23,51 +21,46 @@ import org.bukkit.scheduler.BukkitTask;
public class TFM_PlayerData
{
public static final Map<Player, TFM_PlayerData> USER_INFO = new HashMap<Player, TFM_PlayerData>();
public static final Map<String, TFM_PlayerData> PLAYER_DATA = new HashMap<String, TFM_PlayerData>(); // ip,data
public static final long AUTO_PURGE = 20L * 60L * 5L;
public static boolean hasPlayerData(Player player)
{
return PLAYER_DATA.containsKey(TFM_Util.getIp(player));
}
public static TFM_PlayerData getPlayerData(Player player)
{
TFM_PlayerData playerdata = TFM_PlayerData.USER_INFO.get(player);
final String ip = TFM_Util.getIp(player);
if (playerdata != null)
TFM_PlayerData data = TFM_PlayerData.PLAYER_DATA.get(ip);
if (data != null)
{
return playerdata;
return data;
}
Iterator<Entry<Player, TFM_PlayerData>> it = USER_INFO.entrySet().iterator();
while (it.hasNext())
{
Entry<Player, TFM_PlayerData> pair = it.next();
TFM_PlayerData playerdataTest = pair.getValue();
if (playerdataTest.player.getName().equalsIgnoreCase(player.getName()))
{
if (Bukkit.getOnlineMode())
{
playerdata = playerdataTest;
break;
}
else
for (TFM_PlayerData dataTest : PLAYER_DATA.values())
{
if (playerdataTest.ip.equalsIgnoreCase(player.getAddress().getAddress().getHostAddress()))
if (dataTest.player.getName().equalsIgnoreCase(player.getName()))
{
playerdata = playerdataTest;
data = dataTest;
break;
}
}
}
}
if (playerdata != null)
if (data != null)
{
return playerdata;
return data;
}
playerdata = new TFM_PlayerData(player);
TFM_PlayerData.USER_INFO.put(player, playerdata);
data = new TFM_PlayerData(player, TFM_UuidManager.getUniqueId(player), ip);
TFM_PlayerData.PLAYER_DATA.put(ip, data);
return playerdata;
return data;
}
//
private final Player player;
@ -76,42 +69,42 @@ public class TFM_PlayerData
//
private BukkitTask unmuteTask;
private BukkitTask unfreezeTask;
private BukkitTask mp44ScheduleTask = null;
private BukkitTask lockupScheduleTask = null;
private Location freezeLocation;
private boolean isHalted = false;
private boolean isCaged = false;
private boolean inAdminchat = false;
private boolean allCommandsBlocked = false;
private boolean verifiedSuperadminId = false;
private boolean cmdspyEnabled = false;
private boolean mp44Armed = false;
private boolean mp44Firing = false;
private boolean isOrbiting = false;
private boolean mobThrowerEnabled = false;
private int messageCount = 0;
private int totalBlockDestroy = 0;
private int totalBlockPlace = 0;
private int freecamDestroyCount = 0;
private int freecamPlaceCount = 0;
private int warningCount = 0;
private double orbitStrength = 10.0;
private double mobThrowerSpeed = 4.0;
private boolean isCaged = false;
private Location cagePosition;
private Location freezeLocation;
private final List<TFM_BlockData> cageHistory = new ArrayList<TFM_BlockData>();
private final List<LivingEntity> mobThrowerQueue = new ArrayList<LivingEntity>();
private List<TFM_BlockData> cageHistory = new ArrayList<TFM_BlockData>();
private Material cageOuterMaterial = Material.GLASS;
private Material cageInnerMatterial = Material.AIR;
private String lastMessage = "";
private String lastCommand = "";
private String tag = null;
private boolean isOrbiting = false;
private double orbitStrength = 10.0;
private boolean mobThrowerEnabled = false;
private EntityType mobThrowerEntity = EntityType.PIG;
private double mobThrowerSpeed = 4.0;
private List<LivingEntity> mobThrowerQueue = new ArrayList<LivingEntity>();
private BukkitTask mp44ScheduleTask = null;
private boolean mp44Armed = false;
private boolean mp44Firing = false;
private BukkitTask lockupScheduleTask = null;
private String lastMessage = "";
private boolean inAdminchat = false;
private boolean allCommandsBlocked = false;
private boolean verifiedSuperadminId = false;
private String lastCommand = "";
private boolean cmdspyEnabled = false;
private String tag = null;
private int warningCount = 0;
private TFM_PlayerData(Player player)
private TFM_PlayerData(Player player, UUID uuid, String ip)
{
this.player = player;
this.uuid = TFM_UuidManager.getUniqueId(player.getName());
this.ip = player.getAddress().getAddress().getHostAddress();
this.uuid = uuid;
this.ip = ip;
}
public String getIpAddress()
@ -126,7 +119,7 @@ public class TFM_PlayerData
public boolean isOrbiting()
{
return this.isOrbiting;
return isOrbiting;
}
public void startOrbiting(double strength)
@ -142,7 +135,7 @@ public class TFM_PlayerData
public double orbitStrength()
{
return this.orbitStrength;
return orbitStrength;
}
public void setCaged(boolean state)
@ -160,7 +153,7 @@ public class TFM_PlayerData
public boolean isCaged()
{
return this.isCaged;
return isCaged;
}
public Material getCageMaterial(CageLayer layer)
@ -178,17 +171,17 @@ public class TFM_PlayerData
public Location getCagePos()
{
return this.cagePosition;
return cagePosition;
}
public void clearHistory()
{
this.cageHistory.clear();
cageHistory.clear();
}
public void insertHistoryBlock(Location location, Material material)
{
this.cageHistory.add(new TFM_BlockData(location, material));
cageHistory.add(new TFM_BlockData(location, material));
}
public void regenerateHistory()
@ -387,14 +380,12 @@ public class TFM_PlayerData
unmuteTask = new BukkitRunnable()
{
@Override
public void run()
{
TFM_Util.adminAction("TotalFreedom", "Unmuting " + player.getName(), false);
setMuted(false);
}
}.runTaskLater(TotalFreedomMod.plugin, AUTO_PURGE);
}

View file

@ -129,12 +129,11 @@ public class TFM_PlayerList
{
if (entry.getUniqueId().equals(newUuid))
{
throw new IllegalArgumentException("Cannot set new UUID: UUIDs match");
TFM_Log.warning("Not setting new UUID: UUIDs match!");
return;
}
final boolean reAdd = PLAYER_LIST.containsKey(entry.getUniqueId());
PLAYER_LIST.remove(entry.getUniqueId());
// Add new entry
final TFM_Player newEntry = new TFM_Player(
newUuid,
entry.getFirstLoginName(),
@ -142,15 +141,13 @@ public class TFM_PlayerList
entry.getFirstLoginUnix(),
entry.getLastLoginUnix(),
entry.getIps());
if (reAdd)
{
PLAYER_LIST.put(newUuid, newEntry);
}
newEntry.save();
PLAYER_LIST.put(newUuid, newEntry);
if (!getConfigFile(entry.getUniqueId()).delete())
// Remove old entry
PLAYER_LIST.remove(entry.getUniqueId());
final File oldFile = getConfigFile(entry.getUniqueId());
if (oldFile.exists() && !oldFile.delete())
{
TFM_Log.warning("Could not delete config: " + getConfigFile(entry.getUniqueId()).getName());
}

View file

@ -19,7 +19,6 @@ import org.bukkit.util.Vector;
public class TFM_ProtectedArea
{
public static final double MAX_RADIUS = 50.0;
private static final Map<String, SerializableProtectedRegion> PROTECTED_AREAS = new HashMap<String, SerializableProtectedRegion>();
@ -267,7 +266,6 @@ public class TFM_ProtectedArea
public static class SerializableProtectedRegion implements Serializable
{
private final double x, y, z;
private final double radius;
private final String worldName;
@ -313,7 +311,6 @@ public class TFM_ProtectedArea
public static class CantFindWorldException extends Exception
{
private static final long serialVersionUID = 1L;
public CantFindWorldException(String string)
@ -321,7 +318,5 @@ public class TFM_ProtectedArea
super(string);
}
}
}
}

View file

@ -66,11 +66,11 @@ public class TFM_ServerInterface
final Server server = TotalFreedomMod.server;
final Player player = event.getPlayer();
final String username = player.getName();
final UUID uuid = TFM_UuidManager.getUniqueId(username);
final String ip = event.getAddress().getHostAddress().trim();
final UUID uuid = TFM_UuidManager.newPlayer(player, ip);
// Perform username checks
if (username.length() < 3 || username.length() > 20)
if (username.length() < 3 || username.length() > TotalFreedomMod.MAX_USERNAME_LENGTH)
{
event.disallow(Result.KICK_OTHER, "Your username is an invalid length (must be between 3 and 20 characters long).");
return;
@ -83,24 +83,16 @@ public class TFM_ServerInterface
}
// Check if player is admin
// Not safe to use TFM_Util.isSuperAdmin for player logging in because player.getAddress() will return a null until after player login.
final boolean isAdmin;
if (server.getOnlineMode())
{
isAdmin = TFM_AdminList.getSuperUUIDs().contains(uuid);
}
else
{
final TFM_Admin admin = TFM_AdminList.getEntryByIp(ip);
isAdmin = admin != null && admin.isActivated();
}
// Not safe to use TFM_Util.isSuperAdmin(player) because player.getAddress() will return a null until after player login.
final boolean isAdmin = TFM_AdminList.isSuperAdminSafe(uuid, ip);
// Validation below this point
if (isAdmin) // Player is superadmin
{
// force-allow log in
// Force-allow log in
event.allow();
// Kick players with the same name
for (Player onlinePlayer : server.getOnlinePlayers())
{
if (onlinePlayer.getName().equalsIgnoreCase(username))
@ -132,6 +124,7 @@ public class TFM_ServerInterface
event.disallow(Result.KICK_OTHER, "The server is full and a player could not be kicked, sorry!");
return;
}
return;
}

View file

@ -154,16 +154,22 @@ public class TFM_Util
return player.getPlayer().getAddress().getAddress().getHostAddress().trim();
}
final UUID uuid = TFM_UuidManager.getUniqueId(player);
final TFM_Player entry = TFM_PlayerList.getEntry(TFM_UuidManager.getUniqueId(player));
final TFM_Player entry = TFM_PlayerList.getEntry(uuid);
if (entry == null)
{
return null;
return (entry == null ? null : entry.getIps().get(0));
}
return entry.getIps().get(0);
public static boolean isUniqueId(String uuid)
{
try
{
UUID.fromString(uuid);
return true;
}
catch (IllegalArgumentException ex)
{
return false;
}
}
public static String formatLocation(Location location)

View file

@ -1,148 +1,209 @@
package me.StevenLawson.TotalFreedomMod;
import com.google.common.collect.ImmutableList;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import me.StevenLawson.TotalFreedomMod.Config.TFM_Config;
import java.util.concurrent.Callable;
import me.StevenLawson.TotalFreedomMod.SQL.TFM_SqlUtil;
import me.StevenLawson.TotalFreedomMod.SQL.TFM_SqliteDatabase;
import me.StevenLawson.TotalFreedomMod.SQL.TFM_SqliteDatabase.Statement;
import org.apache.commons.lang3.StringUtils;
import org.bukkit.OfflinePlayer;
import org.bukkit.entity.Player;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
public class TFM_UuidManager
{
private static final Map<String, UUID> UUID_CACHE = new HashMap<String, UUID>();
public static final String TABLE_NAME = "uuids";
private static final TFM_SqliteDatabase SQL;
private static final Statement FIND;
private static final Statement UPDATE;
private TFM_UuidManager()
{
throw new AssertionError();
}
static
{
SQL = new TFM_SqliteDatabase(
"uuids.db",
TABLE_NAME,
"username VARCHAR(" + TotalFreedomMod.MAX_USERNAME_LENGTH + ") NOT NULL PRIMARY KEY, uuid CHAR(36) NOT NULL");
FIND = SQL.addPreparedStatement("SELECT * FROM " + TABLE_NAME + " WHERE lower(username) = ?;");
UPDATE = SQL.addPreparedStatement("REPLACE INTO " + TABLE_NAME + " (username, uuid) VALUES (?, ?);");
}
public static void load()
{
UUID_CACHE.clear();
final TFM_Config config = new TFM_Config(TotalFreedomMod.plugin, "uuid.yml", false);
config.load();
if (!config.isList("cache"))
{
config.set("cache", new ArrayList<String>());
config.save();
return;
// Init DB
SQL.connect();
}
for (String cache : config.getStringList("cache"))
public static void close()
{
final String[] parts = cache.split(":");
if (parts.length != 2)
{
TFM_Log.warning("Invalid cached UUID: " + cache);
continue;
}
final String playerName = parts[0];
final String uuidString = parts[1];
if (!isUniqueId(uuidString))
{
TFM_Log.warning("Invalid cached UUID: " + cache);
continue;
}
if (uuidString.startsWith("deadbeef"))
{
continue;
}
UUID_CACHE.put(playerName.toLowerCase(), UUID.fromString(uuidString));
}
TFM_Log.info("Cached " + UUID_CACHE.size() + " UUIDs");
}
public static void save()
{
final TFM_Config config = new TFM_Config(TotalFreedomMod.plugin, "uuid.yml", false);
config.load();
final List<String> uuids = new ArrayList<String>();
for (String playerName : UUID_CACHE.keySet())
{
final UUID uuid = UUID_CACHE.get(playerName);
if (uuid.toString().startsWith("deadbeef"))
{
continue;
}
uuids.add(playerName + ":" + uuid);
}
config.set("cache", uuids);
config.save();
SQL.close();
}
public static int purge()
{
final int size = UUID_CACHE.size();
UUID_CACHE.clear();
save();
return size;
return SQL.purge();
}
public static boolean isUniqueId(String uuid)
public static UUID newPlayer(Player player, String ip)
{
try
TFM_Log.info("Obtaining UUID for new player: " + player.getName());
final String username = player.getName().toLowerCase();
// Look in DB
final UUID dbUuid = find(username);
if (dbUuid != null)
{
UUID.fromString(uuid);
}
catch (IllegalArgumentException ex)
{
return false;
return dbUuid;
}
return true;
// Find UUID and update in DB if not found
// Try API
UUID uuid = TFM_UuidResolver.getUUIDOf(username);
if (uuid == null)
{
// Spoof
uuid = generateSpoofUuid(username);
}
update(username, uuid);
return uuid;
}
public static UUID getUniqueId(OfflinePlayer offlinePlayer)
{
if (offlinePlayer.isOnline())
// Online check first
if (offlinePlayer.isOnline() && TFM_PlayerData.hasPlayerData(offlinePlayer.getPlayer()))
{
return TFM_PlayerData.getPlayerData(offlinePlayer.getPlayer()).getUniqueId();
}
// DB, API, Spoof
return getUniqueId(offlinePlayer.getName());
}
public static UUID getUniqueId(String playerName)
public static UUID getUniqueId(String username)
{
if (UUID_CACHE.containsKey(playerName.toLowerCase()))
// Look in DB
final UUID dbUuid = find(username);
if (dbUuid != null)
{
return UUID_CACHE.get(playerName.toLowerCase());
return dbUuid;
}
UUID uuid = TFM_UuidResolver.getUUIDOf(playerName);
if (uuid == null)
// Try API
final UUID apiUuid = TFM_UuidResolver.getUUIDOf(username);
if (apiUuid != null)
{
uuid = generateSpoofUuid(playerName);
return apiUuid;
}
UUID_CACHE.put(playerName, uuid);
// Spoof
return generateSpoofUuid(username);
}
save();
public static void rawSetUUID(String name, UUID uuid) {
if (name == null || uuid == null || name.isEmpty()) {
TFM_Log.warning("Not setting raw UUID: name and uuid may not be null!");
return;
}
return uuid;
update(name.toLowerCase().trim(), uuid);
}
private static UUID find(String searchName)
{
if (!SQL.connect())
{
return null;
}
final ResultSet result;
try
{
final PreparedStatement statement = FIND.getStatement();
statement.clearParameters();
statement.setString(1, searchName.toLowerCase());
result = statement.executeQuery();
}
catch (Exception ex)
{
TFM_Log.severe("Could not execute find statement!");
TFM_Log.severe(ex);
return null;
}
if (!TFM_SqlUtil.hasData(result))
{
TFM_SqlUtil.close(result);
return null;
}
try
{
final String uuidString = result.getString("uuid");
return UUID.fromString(uuidString);
}
catch (Exception ex)
{
TFM_Log.severe(ex);
return null;
}
finally
{
TFM_SqlUtil.close(result);
}
}
private static boolean update(String username, UUID uuid)
{
if (!SQL.connect())
{
return false;
}
try
{
final PreparedStatement statement = UPDATE.getStatement();
statement.clearParameters();
statement.setString(1, username.toLowerCase());
statement.setString(2, uuid.toString());
statement.executeUpdate();
return true;
}
catch (Exception ex)
{
TFM_Log.severe("Could not execute update statement!");
TFM_Log.severe(ex);
return false;
}
}
private static UUID generateSpoofUuid(String name)
{
TFM_Log.info("Generating spoof UUID for " + name);
name = name.toLowerCase();
TFM_Log.info("Generating spoof UUID for " + name);
try
{
final MessageDigest digest = MessageDigest.getInstance("SHA1");
@ -162,9 +223,83 @@ public class TFM_UuidManager
}
catch (NoSuchAlgorithmException ex)
{
TFM_Log.severe(ex);
TFM_Log.warning("Could not generate spoof UUID: SHA1 algorithm not found!");
}
return UUID.randomUUID();
}
public static class TFM_UuidResolver implements Callable<Map<String, UUID>>
{
private static final double PROFILES_PER_REQUEST = 100;
private static final String PROFILE_URL = "https://api.mojang.com/profiles/minecraft";
private final JSONParser jsonParser = new JSONParser();
private final List<String> names;
public TFM_UuidResolver(List<String> names)
{
this.names = ImmutableList.copyOf(names);
}
@Override
public Map<String, UUID> call()
{
final Map<String, UUID> uuidMap = new HashMap<String, UUID>();
int requests = (int) Math.ceil(names.size() / PROFILES_PER_REQUEST);
for (int i = 0; i < requests; i++)
{
try
{
final URL url = new URL(PROFILE_URL);
final HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("POST");
connection.setRequestProperty("Content-Type", "application/json");
connection.setUseCaches(false);
connection.setDoInput(true);
connection.setDoOutput(true);
final String body = JSONArray.toJSONString(names.subList(i * 100, Math.min((i + 1) * 100, names.size())));
final OutputStream stream = connection.getOutputStream();
stream.write(body.getBytes());
stream.flush();
stream.close();
final JSONArray array = (JSONArray) jsonParser.parse(new InputStreamReader(connection.getInputStream()));
for (Object profile : array)
{
final JSONObject jsonProfile = (JSONObject) profile;
final String id = (String) jsonProfile.get("id");
final String name = (String) jsonProfile.get("name");
final UUID uuid = UUID.fromString(
id.substring(0, 8)
+ "-" + id.substring(8, 12)
+ "-" + id.substring(12, 16)
+ "-" + id.substring(16, 20)
+ "-" + id.substring(20, 32));
uuidMap.put(name, uuid);
}
if (i != requests - 1)
{
Thread.sleep(100L);
}
}
catch (Exception ex)
{
TFM_Log.severe("Could not resolve UUID(s) of "
+ StringUtils.join(names.subList(i * 100, Math.min((i + 1) * 100, names.size())), ", "));
//TFM_Log.severe(ex);
}
}
return uuidMap;
}
public static UUID getUUIDOf(String name)
{
return new TFM_UuidResolver(Arrays.asList(name)).call().get(name);
}
}
}

View file

@ -1,92 +0,0 @@
package me.StevenLawson.TotalFreedomMod;
import com.google.common.collect.ImmutableList;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.Callable;
import org.apache.commons.lang3.StringUtils;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
// Credits to evilmidget38
public class TFM_UuidResolver implements Callable<Map<String, UUID>>
{
private static final double PROFILES_PER_REQUEST = 100;
private static final String PROFILE_URL = "https://api.mojang.com/profiles/minecraft";
private final JSONParser jsonParser = new JSONParser();
private final List<String> names;
public TFM_UuidResolver(List<String> names)
{
this.names = ImmutableList.copyOf(names);
}
@Override
public Map<String, UUID> call()
{
final Map<String, UUID> uuidMap = new HashMap<String, UUID>();
int requests = (int) Math.ceil(names.size() / PROFILES_PER_REQUEST);
for (int i = 0; i < requests; i++)
{
try
{
final URL url = new URL(PROFILE_URL);
final HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("POST");
connection.setRequestProperty("Content-Type", "application/json");
connection.setUseCaches(false);
connection.setDoInput(true);
connection.setDoOutput(true);
final String body = JSONArray.toJSONString(names.subList(i * 100, Math.min((i + 1) * 100, names.size())));
final OutputStream stream = connection.getOutputStream();
stream.write(body.getBytes());
stream.flush();
stream.close();
final JSONArray array = (JSONArray) jsonParser.parse(new InputStreamReader(connection.getInputStream()));
for (Object profile : array)
{
final JSONObject jsonProfile = (JSONObject) profile;
final String id = (String) jsonProfile.get("id");
final String name = (String) jsonProfile.get("name");
final UUID uuid = UUID.fromString(
id.substring(0, 8)
+ "-" + id.substring(8, 12)
+ "-" + id.substring(12, 16)
+ "-" + id.substring(16, 20)
+ "-" + id.substring(20, 32));
uuidMap.put(name, uuid);
}
if (i != requests - 1)
{
Thread.sleep(100L);
}
}
catch (Exception ex)
{
TFM_Log.severe("Could not resolve UUID(s) of "
+ StringUtils.join(names.subList(i * 100, Math.min((i + 1) * 100, names.size())), ", "));
TFM_Log.severe(ex);
}
}
return uuidMap;
}
public static UUID getUUIDOf(String name)
{
return new TFM_UuidResolver(Arrays.asList(name)).call().get(name);
}
}

View file

@ -33,10 +33,12 @@ public class TotalFreedomMod extends JavaPlugin
{
public static final long HEARTBEAT_RATE = 5L; // Seconds
public static final long SERVICE_CHECKER_RATE = 120L;
public static final int MAX_USERNAME_LENGTH = 20;
//
public static final String CONFIG_FILENAME = "config.yml";
public static final String SUPERADMIN_FILENAME = "superadmin.yml";
public static final String PERMBAN_FILENAME = "permban.yml";
public static final String UUID_FILENAME = "uuids.db";
public static final String PROTECTED_AREA_FILENAME = "protectedareas.dat";
public static final String SAVED_FLAGS_FILENAME = "savedflags.dat";
//
@ -195,10 +197,12 @@ public class TotalFreedomMod extends JavaPlugin
@Override
public void onDisable()
{
server.getScheduler().cancelTasks(plugin);
TFM_HTTPD_Manager.stop();
TFM_BanManager.save();
TFM_UuidManager.close();
TFM_FrontDoor.stop();
server.getScheduler().cancelTasks(plugin);
TFM_Log.info("Plugin disabled");
}

View file

@ -32,6 +32,7 @@ public class CleanroomBlockPopulator extends BlockPopulator
this.layerDataValues = layerDataValues;
}
@Override
public void populate(World world, Random random, Chunk chunk)
{
if (layerDataValues != null)

View file

@ -0,0 +1,56 @@
package me.husky;
import java.sql.Connection;
import org.bukkit.plugin.Plugin;
/**
* Abstract Database class, serves as a base for any connection method (MySQL,
* SQLite, etc.)
*
* @author -_Husky_-
* @author tips48
*/
public abstract class Database
{
/**
* Plugin instance, use for plugin.getDataFolder() and plugin.getLogger()
*/
protected Plugin plugin;
/**
* Creates a new Database
*
* @param plugin Plugin instance
*/
protected Database(Plugin plugin)
{
this.plugin = plugin;
}
/**
* Opens a connection with the database
*
* @return Connection opened
*/
public abstract Connection openConnection();
/**
* Checks if a connection is open with the database
*
* @return true if a connection is open
*/
public abstract boolean checkConnection();
/**
* Gets the connection with the database
*
* @return Connection with the database, null if none
*/
public abstract Connection getConnection();
/**
* Closes the connection with the database
*/
public abstract void closeConnection();
}

View file

@ -0,0 +1,168 @@
package me.husky.mysql;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.logging.Level;
import me.husky.Database;
import org.bukkit.plugin.Plugin;
/**
* Connects to and uses a MySQL database
*
* @author -_Husky_-
* @author tips48
*/
public class MySQL extends Database
{
private final String user;
private final String database;
private final String password;
private final String port;
private final String hostname;
private Connection connection;
/**
* Creates a new MySQL instance
*
* @param plugin Plugin instance
* @param hostname Name of the host
* @param port Port number
* @param database Database name
* @param username Username
* @param password Password
*/
public MySQL(Plugin plugin, String hostname, String port, String database, String username, String password)
{
super(plugin);
this.hostname = hostname;
this.port = port;
this.database = database;
this.user = username;
this.password = password;
this.connection = null;
}
@Override
public Connection openConnection()
{
try
{
Class.forName("com.mysql.jdbc.Driver");
connection = DriverManager.getConnection("jdbc:mysql://" + this.hostname + ":" + this.port + "/" + this.database, this.user, this.password);
}
catch (SQLException e)
{
plugin.getLogger().log(Level.SEVERE, "Could not connect to MySQL server! because: " + e.getMessage());
}
catch (ClassNotFoundException e)
{
plugin.getLogger().log(Level.SEVERE, "JDBC Driver not found!");
}
return connection;
}
@Override
public boolean checkConnection()
{
return connection != null;
}
@Override
public Connection getConnection()
{
return connection;
}
@Override
public void closeConnection()
{
if (connection != null)
{
try
{
connection.close();
}
catch (SQLException e)
{
plugin.getLogger().log(Level.SEVERE, "Error closing the MySQL Connection!");
e.printStackTrace();
}
}
}
public ResultSet querySQL(String query)
{
Connection c = null;
if (checkConnection())
{
c = getConnection();
}
else
{
c = openConnection();
}
Statement s = null;
try
{
s = c.createStatement();
}
catch (SQLException e1)
{
e1.printStackTrace();
}
ResultSet ret = null;
try
{
ret = s.executeQuery(query);
}
catch (SQLException e)
{
e.printStackTrace();
}
closeConnection();
return ret;
}
public void updateSQL(String update)
{
Connection c = null;
if (checkConnection())
{
c = getConnection();
}
else
{
c = openConnection();
}
Statement s = null;
try
{
s = c.createStatement();
s.executeUpdate(update);
}
catch (SQLException e1)
{
e1.printStackTrace();
}
closeConnection();
}
}

View file

@ -0,0 +1,105 @@
package me.husky.sqlite;
import java.io.File;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.logging.Level;
import me.husky.Database;
import org.bukkit.plugin.Plugin;
/**
* Connects to and uses a SQLite database
*
* @author tips48
*/
public class SQLite extends Database
{
private final String dbLocation;
private Connection connection;
/**
* Creates a new SQLite instance
*
* @param plugin Plugin instance
* @param dbLocation Location of the Database (Must end in .db)
*/
public SQLite(Plugin plugin, String dbLocation)
{
super(plugin);
this.dbLocation = dbLocation;
this.connection = null;
}
@Override
public Connection openConnection()
{
File file = new File(dbLocation);
if (!(file.exists()))
{
try
{
file.createNewFile();
}
catch (IOException e)
{
plugin.getLogger().log(Level.SEVERE, "Unable to create database!");
}
}
try
{
Class.forName("org.sqlite.JDBC");
connection = DriverManager.getConnection("jdbc:sqlite:" + plugin.getDataFolder().toPath().toString() + "/" + dbLocation);
}
catch (SQLException e)
{
plugin.getLogger().log(Level.SEVERE, "Could not connect to SQLite server! because: " + e.getMessage());
}
catch (ClassNotFoundException e)
{
plugin.getLogger().log(Level.SEVERE, "JDBC Driver not found!");
}
return connection;
}
@Override
public boolean checkConnection()
{
try
{
return connection != null && !connection.isClosed();
}
catch (SQLException e)
{
e.printStackTrace();
return false;
}
}
@Override
public Connection getConnection()
{
return connection;
}
@Override
public void closeConnection()
{
if (connection != null)
{
try
{
connection.close();
}
catch (SQLException e)
{
plugin.getLogger().log(Level.SEVERE, "Error closing the SQLite Connection!");
e.printStackTrace();
}
}
}
}

View file

@ -55,6 +55,7 @@ import org.bukkit.scheduler.BukkitTask;
public class Metrics
{
/**
* The current revision number
*/
@ -201,6 +202,7 @@ public class Metrics
{
private boolean firstPost = true;
@Override
public void run()
{
try
@ -695,6 +697,7 @@ public class Metrics
*/
public static class Graph
{
/**
* The graph's name, alphanumeric and spaces only :) If it does not comply to the above when submitted, it is
* rejected
@ -781,6 +784,7 @@ public class Metrics
*/
public static abstract class Plotter
{
/**
* The plot's name
*/
@ -848,4 +852,5 @@ public class Metrics
return plotter.name.equals(name) && plotter.getValue() == getValue();
}
}
}