/** * This file is part of Oracle, licensed under the MIT License (MIT). * * Copyright (c) 2015 Helion3 http://helion3.com/ * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package com.helion3.oracle.players; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.Optional; import java.util.UUID; import org.apache.commons.lang3.StringUtils; import org.spongepowered.api.entity.living.player.Player; import org.spongepowered.api.entity.living.player.User; import com.helion3.oracle.Oracle; public class PlayerIdentification { private PlayerIdentification() {} /** * Loads `oracle_players` ID for a real player into our cache. * * Runs during PlayerJoin events, so it will never be for a fake/null * player. * * @param player */ public static PluginPlayer cacheOraclePlayer( final User player ){ // Lookup the player PluginPlayer pluginPlayer = getOraclePlayer( player ); if( pluginPlayer != null ){ pluginPlayer = comparePlayerToCache( player, pluginPlayer ); Oracle.getLogger().debug("Loaded player " + player.getName() + ", id: " + pluginPlayer.getId() + " into the cache."); Oracle.oraclePlayers.put( player.getUniqueId(), pluginPlayer ); return pluginPlayer; } return pluginPlayer; } /** * Returns a `oracle_players` ID for the described player name. If * one cannot be found, returns 0. * * Used by the recorder in determining proper foreign key * * @param playerName * @return */ public static PluginPlayer getOraclePlayer( String playerName ){ Optional<Player> player = Oracle.getGame().getServer().getPlayer(playerName); if (player.isPresent()) return getOraclePlayer(player.get()); // Player not online, we need to go to cache PluginPlayer pluginPlayer = lookupByName( playerName ); // Player found! Return the id if( pluginPlayer != null ) return pluginPlayer; // No player exists! We must create one return null; } /** * Returns a `oracle_players` ID for a UUID * @param uuid * @return PluginPlayer */ public static PluginPlayer getOraclePlayer(UUID uuid) { return lookupByUUID(uuid); } /** * Returns a `oracle_players` ID for the described player object. If * one cannot be found, returns 0. * * Used by the recorder in determining proper foreign key * * @param playerName * @return */ public static PluginPlayer getOraclePlayer( User player ){ if( player.getUniqueId() == null ){ // If they have a name, we can attempt to find them that way if( player.getName() != null && !player.getName().trim().isEmpty() ){ return getOraclePlayer( player.getName() ); } // No name, no UUID, no service. return null; } PluginPlayer pluginPlayer = null; // Are they in the cache? pluginPlayer = Oracle.oraclePlayers.get( player.getUniqueId() ); if( pluginPlayer != null ) return pluginPlayer; // Lookup by UUID pluginPlayer = lookupByUUID( player.getUniqueId() ); if( pluginPlayer != null ) return pluginPlayer; // Still not found, try looking them up by name pluginPlayer = lookupByName( player.getName() ); if( pluginPlayer != null ) return pluginPlayer; return null; } /** * Compares the known player to the cached data. If there's a difference * we need to handle it. * * If usernames are different: Update `oracle_players` with new name * (@todo track historical?) * * If UUID is different, log an error. * * @param player * @param pluginPlayer * @return */ protected static PluginPlayer comparePlayerToCache( User player, PluginPlayer pluginPlayer ){ // Compare for username differences, update database if( !player.getName().equals( pluginPlayer.getName() ) ){ pluginPlayer.setName( player.getName() ); updatePlayer(pluginPlayer); } // Compare UUID if( !player.getUniqueId().equals( pluginPlayer.getUUID() ) ){ Oracle.getLogger().info("Player UUID for " +player.getName() + " does not match our cache! " +player.getUniqueId()+ " versus cache of " + pluginPlayer.getUUID()); // Update anyway... pluginPlayer.setUUID( player.getUniqueId() ); updatePlayer(pluginPlayer); } return pluginPlayer; } /** * Converts UUID to a string ready for use against database * @param player */ protected static String uuidToDbString( UUID id ){ return id.toString().replace("-", ""); } /** * Converts UUID to a string ready for use against database * @param player */ public static UUID uuidFromDbString( String uuid ){ // Positions need to be -2 String completeUuid = uuid.substring(0, 8); completeUuid += "-" + uuid.substring(8,12); completeUuid += "-" + uuid.substring(12,16); completeUuid += "-" + uuid.substring(16,20); completeUuid += "-" + uuid.substring(20, uuid.length()); completeUuid = completeUuid.toLowerCase(); return UUID.fromString(completeUuid); } /** * Saves a real player's UUID and current Username to the `oracle_players` * table. At this stage, we're pretty sure the UUID and username do not * already exist. * @param player */ public static PluginPlayer addPlayer( Player player ){ PluginPlayer pluginPlayer = new PluginPlayer( 0, player.getUniqueId(), player.getName() ); Connection conn = null; PreparedStatement s = null; ResultSet rs = null; try { conn = Oracle.dbc(); s = conn.prepareStatement( "INSERT INTO oracle_players (player,player_uuid) VALUES (?,UNHEX(?))" , Statement.RETURN_GENERATED_KEYS); s.setString(1, player.getName() ); s.setString(2, uuidToDbString( player.getUniqueId() ) ); s.executeUpdate(); rs = s.getGeneratedKeys(); if (rs.next()) { pluginPlayer.setId(rs.getInt(1)); Oracle.getLogger().debug("Saved and loaded player " + player.getName() + " (" + player.getUniqueId() + ") into the cache."); Oracle.oraclePlayers.put( player.getUniqueId(), new PluginPlayer( rs.getInt(1), player.getUniqueId(), player.getName() ) ); } else { throw new SQLException("Insert statement failed - no generated key obtained."); } } catch (SQLException e) { e.printStackTrace(); } finally { if(rs != null) try { rs.close(); } catch (SQLException e) {} if(s != null) try { s.close(); } catch (SQLException e) {} if(conn != null) try { conn.close(); } catch (SQLException e) {} } return pluginPlayer; } /** * Saves a player's UUID to the oracle_players table. We cache the current username * as well. */ protected static void updatePlayer( PluginPlayer pluginPlayer ){ Connection conn = null; PreparedStatement s = null; ResultSet rs = null; try { conn = Oracle.dbc(); s = conn.prepareStatement( "UPDATE oracle_players SET player = ?, player_uuid = UNHEX(?) WHERE player_id = ?"); s.setString(1, pluginPlayer.getName() ); s.setString(2, uuidToDbString( pluginPlayer.getUUID() ) ); s.setInt(3, pluginPlayer.getId() ); s.executeUpdate(); } catch (SQLException e) { e.printStackTrace(); } finally { if(rs != null) try { rs.close(); } catch (SQLException e) {} if(s != null) try { s.close(); } catch (SQLException e) {} if(conn != null) try { conn.close(); } catch (SQLException e) {} } } /** * Loads `oracle_players` ID for a player into our cache. */ protected static PluginPlayer lookupByName( String playerName ){ PluginPlayer pluginPlayer = null; Connection conn = null; PreparedStatement s = null; ResultSet rs = null; try { conn = Oracle.dbc(); s = conn.prepareStatement( "SELECT player_id, player, HEX(player_uuid) FROM oracle_players WHERE player = ?" ); s.setString(1, playerName); rs = s.executeQuery(); if( rs.next() ){ pluginPlayer = new PluginPlayer( rs.getInt(1), uuidFromDbString(rs.getString(3)), rs.getString(2) ); } } catch (SQLException e) { e.printStackTrace(); } finally { if(rs != null) try { rs.close(); } catch (SQLException e) {} if(s != null) try { s.close(); } catch (SQLException e) {} if(conn != null) try { conn.close(); } catch (SQLException e) {} } return pluginPlayer; } /** * Loads `oracle_players` ID for a player into our cache. */ protected static PluginPlayer lookupByUUID( UUID uuid ){ PluginPlayer pluginPlayer = null; Connection conn = null; PreparedStatement s = null; ResultSet rs = null; try { conn = Oracle.dbc(); s = conn.prepareStatement( "SELECT player_id, player, HEX(player_uuid) FROM oracle_players WHERE player_uuid = UNHEX(?)" ); s.setString(1, uuidToDbString(uuid)); rs = s.executeQuery(); if( rs.next() ){ pluginPlayer = new PluginPlayer( rs.getInt(1), uuidFromDbString(rs.getString(3)), rs.getString(2) ); } } catch (SQLException e) { e.printStackTrace(); } finally { if(rs != null) try { rs.close(); } catch (SQLException e) {} if(s != null) try { s.close(); } catch (SQLException e) {} if(conn != null) try { conn.close(); } catch (SQLException e) {} } return pluginPlayer; } /** * Build-load all online players into cache */ public static void cacheOnlinePlayerPrimaryKeys(){ String[] playerNames; playerNames = new String[ Oracle.getGame().getServer().getOnlinePlayers().size() ]; int i = 0; for( Player pl : Oracle.getGame().getServer().getOnlinePlayers() ){ playerNames[i] = pl.getName(); i++; } Connection conn = null; PreparedStatement s = null; ResultSet rs = null; try { conn = Oracle.dbc(); s = conn.prepareStatement( "SELECT player_id, player, HEX(player_uuid) FROM oracle_players WHERE player IN (?)" ); s.setString(1, "'"+StringUtils.join(playerNames, "','")+"'"); rs = s.executeQuery(); while( rs.next() ){ PluginPlayer pluginPlayer = new PluginPlayer( rs.getInt(1), uuidFromDbString(rs.getString(3)), rs.getString(2) ); Oracle.getLogger().debug("Loaded player " + rs.getString(2) + ", id: " + rs.getInt(1) + " into the cache."); Oracle.oraclePlayers.put( UUID.fromString(rs.getString(2)), pluginPlayer ); } } catch (SQLException e) { e.printStackTrace(); } finally { if(rs != null) try { rs.close(); } catch (SQLException e) {} if(s != null) try { s.close(); } catch (SQLException e) {} if(conn != null) try { conn.close(); } catch (SQLException e) {} } } }