Handle player online check in AsyncPlayerPreLoginEvent

Apparently, online players with the same username are kicked before PlayerLoginEvent is called. This commit moves the online player check to AsyncPlayerPreloginEvent so the default behaviour can still be modified

Fixes #642
This commit is contained in:
JeromSar 2015-05-30 20:51:49 +02:00
parent a4d8f4646e
commit c002fbc537
8 changed files with 133 additions and 188 deletions

View file

@ -1,3 +1,3 @@
#Build Number for ANT. Do not edit!
#Sat May 30 20:10:27 CEST 2015
build.number=1043
#Sat May 30 20:46:17 CEST 2015
build.number=1052

View file

@ -57,7 +57,8 @@ public class Command_saconfig extends TFM_Command
{
final TFM_Admin admin = TFM_AdminList.getEntry(sender_p);
if (admin == null) {
if (admin == null)
{
playerMsg("Could not find your admin entry! Please notify a developer.", ChatColor.RED);
return true;
}

View file

@ -45,6 +45,7 @@ import org.bukkit.event.Listener;
import org.bukkit.event.block.Action;
import org.bukkit.event.block.LeavesDecayEvent;
import org.bukkit.event.player.AsyncPlayerChatEvent;
import org.bukkit.event.player.AsyncPlayerPreLoginEvent;
import org.bukkit.event.player.PlayerCommandPreprocessEvent;
import org.bukkit.event.player.PlayerDropItemEvent;
import org.bukkit.event.player.PlayerInteractEvent;
@ -388,7 +389,7 @@ public class TFM_PlayerListener implements Listener
final Player player = event.getPlayer();
final TFM_PlayerData playerdata = TFM_PlayerData.getPlayerData(player);
// Check absolute value to account for negatives
if(Math.abs(event.getTo().getX()) >= MAX_XY_COORD || Math.abs(event.getTo().getZ()) >= MAX_XY_COORD)
if (Math.abs(event.getTo().getX()) >= MAX_XY_COORD || Math.abs(event.getTo().getZ()) >= MAX_XY_COORD)
{
event.setCancelled(true); // illegal position, cancel it
}
@ -791,7 +792,7 @@ public class TFM_PlayerListener implements Listener
final TFM_Player playerEntry;
TFM_Log.info("[JOIN] " + TFM_Util.formatPlayer(player) + " joined the game with IP address: " + ip, true);
// Check absolute value to account for negatives
if(Math.abs(player.getLocation().getX()) >= MAX_XY_COORD || Math.abs(player.getLocation().getZ()) >= MAX_XY_COORD)
if (Math.abs(player.getLocation().getX()) >= MAX_XY_COORD || Math.abs(player.getLocation().getZ()) >= MAX_XY_COORD)
{
player.teleport(player.getWorld().getSpawnLocation()); // Illegal position, teleport to spawn
}
@ -911,26 +912,15 @@ public class TFM_PlayerListener implements Listener
}.runTaskLater(TotalFreedomMod.plugin, 20L * 1L);
}
@EventHandler(priority = EventPriority.HIGHEST)
public void onPlayerPreLogin(AsyncPlayerPreLoginEvent event)
{
TFM_ServerInterface.handlePlayerPreLogin(event);
}
@EventHandler(priority = EventPriority.HIGHEST)
public void onPlayerLogin(PlayerLoginEvent event)
{
if (TFM_ConfigEntry.FORCE_IP_ENABLED.getBoolean())
{
final String hostname = event.getHostname().replace("FML", ""); // Forge fix - https://github.com/TotalFreedom/TotalFreedomMod/issues/493
final String connectAddress = TFM_ConfigEntry.SERVER_ADDRESS.getString();
final int connectPort = TotalFreedomMod.server.getPort();
if (!hostname.equalsIgnoreCase(connectAddress + ":" + connectPort) && !hostname.equalsIgnoreCase(connectAddress + ".:" + connectPort))
{
final int forceIpPort = TFM_ConfigEntry.FORCE_IP_PORT.getInteger();
event.disallow(PlayerLoginEvent.Result.KICK_OTHER,
TFM_ConfigEntry.FORCE_IP_KICKMSG.getString()
.replace("%address%", TFM_ConfigEntry.SERVER_ADDRESS.getString() + (forceIpPort == DEFAULT_PORT ? "" : ":" + forceIpPort)));
return;
}
}
TFM_ServerInterface.handlePlayerLogin(event);
}
}

View file

@ -406,7 +406,7 @@ public class TFM_AdminList
public static boolean isSuperAdminSafe(UUID uuid, String ip)
{
if (TotalFreedomMod.server.getOnlineMode())
if (TotalFreedomMod.server.getOnlineMode() && uuid != null)
{
return TFM_AdminList.getSuperUUIDs().contains(uuid);
}

View file

@ -7,6 +7,7 @@ import me.StevenLawson.TotalFreedomMod.Config.TFM_Config;
public class TFM_PermbanList
{
private static final List<String> PERMBANNED_PLAYERS;
private static final List<String> PERMBANNED_IPS;

View file

@ -11,65 +11,53 @@ import me.StevenLawson.TotalFreedomMod.Config.TFM_Config;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
public class TFM_PlayerList
{
public class TFM_PlayerList {
private static final Map<UUID, TFM_Player> PLAYER_LIST = new HashMap<UUID, TFM_Player>();
private TFM_PlayerList()
{
private TFM_PlayerList() {
throw new AssertionError();
}
public static Set<TFM_Player> getAllPlayers()
{
public static Set<TFM_Player> getAllPlayers() {
return Collections.unmodifiableSet(Sets.newHashSet(PLAYER_LIST.values()));
}
public static void load()
{
public static void load() {
PLAYER_LIST.clear();
// Load online players
for (Player player : Bukkit.getOnlinePlayers())
{
for (Player player : Bukkit.getOnlinePlayers()) {
getEntry(player);
}
TFM_Log.info("Loaded playerdata for " + PLAYER_LIST.size() + " players");
}
public static void saveAll()
{
for (TFM_Player entry : PLAYER_LIST.values())
{
public static void saveAll() {
for (TFM_Player entry : PLAYER_LIST.values()) {
save(entry);
}
}
// May return null
public static TFM_Player getEntry(UUID uuid)
{
if (PLAYER_LIST.containsKey(uuid))
{
public static TFM_Player getEntry(UUID uuid) {
if (PLAYER_LIST.containsKey(uuid)) {
return PLAYER_LIST.get(uuid);
}
final File configFile = getConfigFile(uuid);
if (!configFile.exists())
{
if (!configFile.exists()) {
return null;
}
final TFM_Player entry = new TFM_Player(uuid, getConfig(uuid));
if (entry.isComplete())
{
if (entry.isComplete()) {
PLAYER_LIST.put(uuid, entry);
return entry;
}
else
{
} else {
TFM_Log.warning("Could not load entry: Entry is not complete!");
configFile.delete();
}
@ -77,13 +65,11 @@ public class TFM_PlayerList
return null;
}
public static TFM_Player getEntry(Player player)
{
public static TFM_Player getEntry(Player player) {
final UUID uuid = TFM_UuidManager.getUniqueId(player);
TFM_Player entry = getEntry(uuid);
if (entry != null)
{
if (entry != null) {
return entry;
}
@ -101,12 +87,10 @@ public class TFM_PlayerList
return entry;
}
public static void removeEntry(Player player)
{
public static void removeEntry(Player player) {
final UUID uuid = TFM_UuidManager.getUniqueId(player);
if (!PLAYER_LIST.containsKey(uuid))
{
if (!PLAYER_LIST.containsKey(uuid)) {
return;
}
@ -115,20 +99,16 @@ public class TFM_PlayerList
PLAYER_LIST.remove(uuid);
}
public static boolean existsEntry(Player player)
{
public static boolean existsEntry(Player player) {
return existsEntry(TFM_UuidManager.getUniqueId(player));
}
public static boolean existsEntry(UUID uuid)
{
public static boolean existsEntry(UUID uuid) {
return getConfigFile(uuid).exists();
}
public static void setUniqueId(TFM_Player entry, UUID newUuid)
{
if (entry.getUniqueId().equals(newUuid))
{
public static void setUniqueId(TFM_Player entry, UUID newUuid) {
if (entry.getUniqueId().equals(newUuid)) {
TFM_Log.warning("Not setting new UUID: UUIDs match!");
return;
}
@ -147,16 +127,13 @@ public class TFM_PlayerList
// Remove old entry
PLAYER_LIST.remove(entry.getUniqueId());
final File oldFile = getConfigFile(entry.getUniqueId());
if (oldFile.exists() && !oldFile.delete())
{
if (oldFile.exists() && !oldFile.delete()) {
TFM_Log.warning("Could not delete config: " + getConfigFile(entry.getUniqueId()).getName());
}
}
public static void purgeAll()
{
for (File file : getConfigFolder().listFiles())
{
public static void purgeAll() {
for (File file : getConfigFolder().listFiles()) {
file.delete();
}
@ -164,27 +141,22 @@ public class TFM_PlayerList
load();
}
public static File getConfigFolder()
{
public static File getConfigFolder() {
return new File(TotalFreedomMod.plugin.getDataFolder(), "players");
}
public static File getConfigFile(UUID uuid)
{
public static File getConfigFile(UUID uuid) {
return new File(getConfigFolder(), uuid + ".yml");
}
public static TFM_Config getConfig(UUID uuid)
{
public static TFM_Config getConfig(UUID uuid) {
final TFM_Config config = new TFM_Config(TotalFreedomMod.plugin, getConfigFile(uuid), false);
config.load();
return config;
}
public static void save(TFM_Player entry)
{
if (!entry.isComplete())
{
public static void save(TFM_Player entry) {
if (!entry.isComplete()) {
throw new IllegalArgumentException("Entry is not complete!");
}

View file

@ -5,12 +5,14 @@ import java.util.List;
import java.util.UUID;
import java.util.regex.Pattern;
import me.StevenLawson.TotalFreedomMod.Config.TFM_ConfigEntry;
import static me.StevenLawson.TotalFreedomMod.Listener.TFM_PlayerListener.DEFAULT_PORT;
import net.minecraft.server.v1_8_R2.EntityPlayer;
import net.minecraft.server.v1_8_R2.MinecraftServer;
import net.minecraft.server.v1_8_R2.PropertyManager;
import org.bukkit.ChatColor;
import org.bukkit.Server;
import org.bukkit.entity.Player;
import org.bukkit.event.player.AsyncPlayerPreLoginEvent;
import org.bukkit.event.player.PlayerLoginEvent;
import org.bukkit.event.player.PlayerLoginEvent.Result;
@ -62,6 +64,29 @@ public class TFM_ServerInterface
return MinecraftServer.getServer().getVersion();
}
public static void handlePlayerPreLogin(AsyncPlayerPreLoginEvent event)
{
final String ip = event.getAddress().getHostAddress().trim();
final boolean isAdmin = TFM_AdminList.isSuperAdminSafe(null, ip);
// Check if the player is already online
for (Player onlinePlayer : TotalFreedomMod.server.getOnlinePlayers())
{
if (!onlinePlayer.getName().equalsIgnoreCase(event.getName()))
{
continue;
}
if (!isAdmin) {
event.disallow(AsyncPlayerPreLoginEvent.Result.KICK_OTHER, "Your username is already logged into this server.");
} else {
event.allow();
TFM_Sync.playerKick(onlinePlayer, "An admin just logged in with the username you are using.");
}
return;
}
}
public static void handlePlayerLogin(PlayerLoginEvent event)
{
final Server server = TotalFreedomMod.server;
@ -70,19 +95,38 @@ public class TFM_ServerInterface
final String ip = event.getAddress().getHostAddress().trim();
final UUID uuid = TFM_UuidManager.newPlayer(player, ip);
// Perform username checks
// Check username length
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;
}
// Check username characters
if (!USERNAME_REGEX.matcher(username).find())
{
event.disallow(Result.KICK_OTHER, "Your username contains invalid characters.");
return;
}
// Check force-IP match
if (TFM_ConfigEntry.FORCE_IP_ENABLED.getBoolean())
{
final String hostname = event.getHostname().replace("FML", ""); // Forge fix - https://github.com/TotalFreedom/TotalFreedomMod/issues/493
final String connectAddress = TFM_ConfigEntry.SERVER_ADDRESS.getString();
final int connectPort = TotalFreedomMod.server.getPort();
if (!hostname.equalsIgnoreCase(connectAddress + ":" + connectPort) && !hostname.equalsIgnoreCase(connectAddress + ".:" + connectPort))
{
final int forceIpPort = TFM_ConfigEntry.FORCE_IP_PORT.getInteger();
event.disallow(PlayerLoginEvent.Result.KICK_OTHER,
TFM_ConfigEntry.FORCE_IP_KICKMSG.getString()
.replace("%address%", TFM_ConfigEntry.SERVER_ADDRESS.getString() + (forceIpPort == DEFAULT_PORT ? "" : ":" + forceIpPort)));
return;
}
}
// Check if player is admin
// 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);
@ -93,15 +137,6 @@ public class TFM_ServerInterface
// Force-allow log in
event.allow();
// Kick players with the same name
for (Player onlinePlayer : server.getOnlinePlayers())
{
if (onlinePlayer.getName().equalsIgnoreCase(username))
{
onlinePlayer.kickPlayer("An admin just logged in with the username you are using.");
}
}
int count = server.getOnlinePlayers().size();
if (count >= server.getMaxPlayers())
{
@ -151,16 +186,6 @@ public class TFM_ServerInterface
return;
}
// Username already logged in
for (Player onlinePlayer : server.getOnlinePlayers())
{
if (onlinePlayer.getName().equalsIgnoreCase(username))
{
event.disallow(Result.KICK_OTHER, "Your username is already logged into this server.");
return;
}
}
// Whitelist
if (isWhitelisted())
{

View file

@ -25,20 +25,18 @@ import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
public class TFM_UuidManager
{
public class TFM_UuidManager {
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()
{
private TFM_UuidManager() {
throw new AssertionError();
}
static
{
static {
SQL = new TFM_SqliteDatabase(
"uuids.db",
TABLE_NAME,
@ -48,40 +46,34 @@ public class TFM_UuidManager
UPDATE = SQL.addPreparedStatement("REPLACE INTO " + TABLE_NAME + " (username, uuid) VALUES (?, ?);");
}
public static void load()
{
public static void load() {
// Init DB
SQL.connect();
}
public static void close()
{
public static void close() {
SQL.close();
}
public static int purge()
{
public static int purge() {
return SQL.purge();
}
public static UUID newPlayer(Player player, String ip)
{
public static UUID newPlayer(Player player, String ip) {
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)
{
if (dbUuid != null) {
return dbUuid;
}
// Find UUID and update in DB if not found
// Try API
UUID uuid = TFM_UuidResolver.getUUIDOf(username);
if (uuid == null)
{
if (uuid == null) {
// Spoof
uuid = generateSpoofUuid(username);
}
@ -90,11 +82,9 @@ public class TFM_UuidManager
return uuid;
}
public static UUID getUniqueId(OfflinePlayer offlinePlayer)
{
public static UUID getUniqueId(OfflinePlayer offlinePlayer) {
// Online check first
if (offlinePlayer.isOnline() && TFM_PlayerData.hasPlayerData(offlinePlayer.getPlayer()))
{
if (offlinePlayer.isOnline() && TFM_PlayerData.hasPlayerData(offlinePlayer.getPlayer())) {
return TFM_PlayerData.getPlayerData(offlinePlayer.getPlayer()).getUniqueId();
}
@ -102,19 +92,16 @@ public class TFM_UuidManager
return getUniqueId(offlinePlayer.getName());
}
public static UUID getUniqueId(String username)
{
public static UUID getUniqueId(String username) {
// Look in DB
final UUID dbUuid = find(username);
if (dbUuid != null)
{
if (dbUuid != null) {
return dbUuid;
}
// Try API
final UUID apiUuid = TFM_UuidResolver.getUUIDOf(username);
if (apiUuid != null)
{
if (apiUuid != null) {
return apiUuid;
}
@ -122,10 +109,8 @@ public class TFM_UuidManager
return generateSpoofUuid(username);
}
public static void rawSetUUID(String name, UUID uuid)
{
if (name == null || uuid == null || name.isEmpty())
{
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;
}
@ -133,86 +118,67 @@ public class TFM_UuidManager
update(name.toLowerCase().trim(), uuid);
}
private static UUID find(String searchName)
{
if (!SQL.connect())
{
private static UUID find(String searchName) {
if (!SQL.connect()) {
return null;
}
final ResultSet result;
try
{
try {
final PreparedStatement statement = FIND.getStatement();
statement.clearParameters();
statement.setString(1, searchName.toLowerCase());
result = statement.executeQuery();
}
catch (Exception ex)
{
} catch (Exception ex) {
TFM_Log.severe("Could not execute find statement!");
TFM_Log.severe(ex);
return null;
}
if (!TFM_SqlUtil.hasData(result))
{
if (!TFM_SqlUtil.hasData(result)) {
TFM_SqlUtil.close(result);
return null;
}
try
{
try {
final String uuidString = result.getString("uuid");
return UUID.fromString(uuidString);
}
catch (Exception ex)
{
} catch (Exception ex) {
TFM_Log.severe(ex);
return null;
}
finally
{
} finally {
TFM_SqlUtil.close(result);
}
}
private static boolean update(String username, UUID uuid)
{
if (!SQL.connect())
{
private static boolean update(String username, UUID uuid) {
if (!SQL.connect()) {
return false;
}
try
{
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)
{
} catch (Exception ex) {
TFM_Log.severe("Could not execute update statement!");
TFM_Log.severe(ex);
return false;
}
}
private static UUID generateSpoofUuid(String name)
{
private static UUID generateSpoofUuid(String name) {
name = name.toLowerCase();
TFM_Log.info("Generating spoof UUID for " + name);
try
{
try {
final MessageDigest digest = MessageDigest.getInstance("SHA1");
final byte[] result = digest.digest(name.getBytes());
final StringBuilder builder = new StringBuilder();
for (int i = 0; i < result.length; i++)
{
for (int i = 0; i < result.length; i++) {
builder.append(Integer.toString((result[i] & 0xff) + 0x100, 16).substring(1));
}
@ -222,36 +188,30 @@ public class TFM_UuidManager
+ "-" + builder.substring(12, 16)
+ "-" + builder.substring(16, 20)
+ "-" + builder.substring(20, 32));
}
catch (NoSuchAlgorithmException ex)
{
} catch (NoSuchAlgorithmException 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>>
{
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)
{
public TFM_UuidResolver(List<String> names) {
this.names = ImmutableList.copyOf(names);
}
@Override
public Map<String, UUID> call()
{
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
{
for (int i = 0; i < requests; i++) {
try {
final URL url = new URL(PROFILE_URL);
final HttpURLConnection connection = (HttpURLConnection) url.openConnection();
@ -270,8 +230,7 @@ public class TFM_UuidManager
final JSONArray array = (JSONArray) jsonParser.parse(new InputStreamReader(connection.getInputStream()));
for (Object profile : array)
{
for (Object profile : array) {
final JSONObject jsonProfile = (JSONObject) profile;
final String id = (String) jsonProfile.get("id");
final String name = (String) jsonProfile.get("name");
@ -284,13 +243,10 @@ public class TFM_UuidManager
uuidMap.put(name, uuid);
}
if (i != requests - 1)
{
if (i != requests - 1) {
Thread.sleep(100L);
}
}
catch (Exception ex)
{
} 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);
@ -299,9 +255,9 @@ public class TFM_UuidManager
return uuidMap;
}
public static UUID getUUIDOf(String name)
{
public static UUID getUUIDOf(String name) {
return new TFM_UuidResolver(Arrays.asList(name)).call().get(name);
}
}
}