Skip to content

Commit

Permalink
Finalize first version of NameHistory cache. (needs testing)
Browse files Browse the repository at this point in the history
  • Loading branch information
Jonas Becker committed Jun 21, 2019
1 parent 46fb2aa commit 60a25c4
Show file tree
Hide file tree
Showing 4 changed files with 125 additions and 44 deletions.
56 changes: 29 additions & 27 deletions src/main/java/de/iani/playerUUIDCache/NameHistory.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,18 @@
import java.util.UUID;

public class NameHistory {

public static class NameChange implements Comparable<NameChange> {

private String newName;
private long date;

public NameChange(String newName, long date) {
com.google.common.base.Preconditions.checkNotNull(newName);
this.newName = newName;
this.date = date;
}

/**
* Returns the name the player adopted with this change.
*
Expand All @@ -27,7 +27,7 @@ public NameChange(String newName, long date) {
public String getNewName() {
return this.newName;
}

/**
* Returns the date at which this change was performed, as a unix epoch timestamp in ms.
*
Expand All @@ -36,53 +36,53 @@ public String getNewName() {
public long getDate() {
return this.date;
}

@Override
public int hashCode() {
int result = this.newName.hashCode();
result += 31 * Long.hashCode(this.date);
return result;
}

@Override
public boolean equals(Object other) {
if (!(other instanceof NameChange)) {
return false;
}

NameChange onc = (NameChange) other;
return this.newName.equals(onc.newName) && this.date == onc.date;
}

@Override
public int compareTo(NameChange other) {
if (other == null) {
throw new NullPointerException();
}

return Long.compare(this.date, other.date);
}
}

private final UUID uuid;
private final String firstName;
private final List<NameChange> changes;

private long cacheLoadTime;

public NameHistory(UUID uuid, String firstName, List<NameChange> changes, long cacheLoadTime) {
com.google.common.base.Preconditions.checkNotNull(uuid);
com.google.common.base.Preconditions.checkNotNull(firstName);
com.google.common.base.Preconditions.checkNotNull(changes);

this.uuid = uuid;
this.firstName = firstName;
changes.sort(null);
this.changes = Collections.unmodifiableList(new ArrayList<>(changes));

this.cacheLoadTime = cacheLoadTime;
}

/**
* Returns the uuid of the player with this history.
*
Expand All @@ -91,7 +91,7 @@ public NameHistory(UUID uuid, String firstName, List<NameChange> changes, long c
public UUID getUUID() {
return this.uuid;
}

/**
* Returns the first name the player with this history ever had.
*
Expand All @@ -100,7 +100,7 @@ public UUID getUUID() {
public String getFirstName() {
return this.firstName;
}

/**
* Returns a list of all name changes the player with this history had, in chronological order.
*
Expand All @@ -109,7 +109,7 @@ public String getFirstName() {
public List<NameChange> getNameChanges() {
return this.changes;
}

/**
* Returns the name the player with this history had at the given date.
*
Expand All @@ -118,7 +118,8 @@ public List<NameChange> getNameChanges() {
* all dates lying in the future, the current name is returned. If the given date matches that
* of one change exactly, that changes new name is returned.
*
* @param date a date as unix epoch timestamp in ms
* @param date
* a date as unix epoch timestamp in ms
* @return the name the player with this history had at the given date
*/
public String getName(long date) {
Expand All @@ -128,7 +129,7 @@ public String getName(long date) {
}
return change.getNewName();
}

/**
* Returns the name the player with this history had at the given date.
*
Expand All @@ -137,18 +138,19 @@ public String getName(long date) {
* all dates lying in the future, the current name is returned. If the given date matches that
* of one change exactly, that changes new name is returned.
*
* @param date a date
* @param date
* a date
* @return the name the player with this history had at the given date
*/
public String getName(Date date) {
return getName(date.getTime());
}

private NameChange getLastChange(long date) {
if (this.changes.isEmpty() || this.changes.get(0).getDate() > date) {
return null;
}

int upper = this.changes.size();
int lower = 0;
while (upper - lower > 1) {
Expand All @@ -159,12 +161,12 @@ private NameChange getLastChange(long date) {
upper = index;
}
}

return this.changes.get(lower);
}

long getCacheLoadTime() {
return this.cacheLoadTime;
}

}
68 changes: 59 additions & 9 deletions src/main/java/de/iani/playerUUIDCache/PlayerUUIDCache.java
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,11 @@ public void onPlayerLogin(PlayerLoginEvent e) {
long now = System.currentTimeMillis();
updateEntries(true, new CachedPlayer(uuid, name, now, now));

// TODO: name histories?
NameHistory oldHistory = getNameHistory(e.getPlayer());
if (oldHistory == null || (!e.getPlayer().getName().equals(oldHistory.getName(System.currentTimeMillis())))) {
PlayerUUIDCache.this.getNameHistoryAsynchronously(e.getPlayer().getUniqueId(), history -> {
});
}
}

@EventHandler(priority = EventPriority.LOWEST)
Expand All @@ -270,6 +274,12 @@ public void onPlayerQuit(PlayerQuitEvent e) {
UUID uuid = e.getPlayer().getUniqueId();
long now = System.currentTimeMillis();
updateEntries(true, new CachedPlayer(uuid, name, now, now));

NameHistory oldHistory = getNameHistory(e.getPlayer());
if (oldHistory == null || (!e.getPlayer().getName().equals(oldHistory.getName(System.currentTimeMillis())))) {
PlayerUUIDCache.this.getNameHistoryAsynchronously(e.getPlayer().getUniqueId(), history -> {
});
}
}
}

Expand Down Expand Up @@ -696,26 +706,66 @@ protected CachedPlayerProfile getPlayerProfileFromMojang(UUID playerUUID) {

@Override
public NameHistory getNameHistory(UUID playerUUID) {
// TODO Auto-generated method stub
return null;
return getNameHistory(playerUUID, false);
}

@Override
public NameHistory getNameHistory(UUID playerUUID, boolean queryMojangIfUnknown) {
// TODO Auto-generated method stub
return null;
nameHistoryLookups++;
NameHistory result;
synchronized (this) {
result = nameHistories.get(playerUUID);
if (result != null) {
if (config.getMemoryCacheExpirationTime() == -1 || result.getCacheLoadTime() + config.getMemoryCacheExpirationTime() > System.currentTimeMillis()) {
return result;
}
}
}

if (database != null) {
try {
result = database.getNameHistory(playerUUID);
if (result != null) {
return result;
}
} catch (SQLException e) {
getLogger().log(Level.SEVERE, "Error while trying to access the database", e);
}
}

if (!queryMojangIfUnknown) {
return null;
}

return getNameHistoryFromMojang(playerUUID);
}

@Override
public void getNameHistoryAsynchronously(UUID playerUUID, Callback<NameHistory> synchronousCallback) {
// TODO Auto-generated method stub

final NameHistory history = getNameHistory(playerUUID);
if (history != null) {
if (synchronousCallback != null) {
if (Bukkit.isPrimaryThread()) {
synchronousCallback.onComplete(history);
} else {
getServer().getScheduler().runTask(PlayerUUIDCache.this, (Runnable) () -> synchronousCallback.onComplete(history));
}
}
return;
}
getServer().getScheduler().runTaskAsynchronously(this, (Runnable) () -> {
final NameHistory h = getNameHistoryFromMojang(playerUUID);
if (synchronousCallback != null) {
getServer().getScheduler().runTask(PlayerUUIDCache.this, (Runnable) () -> synchronousCallback.onComplete(h));
}
});
}

@Override
public Future<NameHistory> loadNameHistoryAsynchronously(UUID playerUUID) {
// TODO Auto-generated method stub
return null;
FutureTask<NameHistory> futuretask = new FutureTask<>(() -> getNameHistoryFromMojang(playerUUID));
getServer().getScheduler().runTaskAsynchronously(this, futuretask);
return futuretask;
}

@Override
Expand Down
34 changes: 34 additions & 0 deletions src/main/java/de/iani/playerUUIDCache/PlayerUUIDCacheAPI.java
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,19 @@ public interface PlayerUUIDCacheAPI {
*/
NameHistory getNameHistory(UUID playerUUID);

/**
* Gets a NameHistory for a player.
* The result may be null if this player's history is not found in the cache.
* The OfflinePlayer must return a UUID.
*
* @param player
* the player
* @return the NameHistory or null
*/
default NameHistory getNameHistory(OfflinePlayer player) {
return getNameHistory(player.getUniqueId());
}

/**
* Gets a NameHistory for a UUID.
* If no entry is found in the cache this method will query Mojang if queryMojangIfUnknown is true. This
Expand All @@ -195,6 +208,24 @@ public interface PlayerUUIDCacheAPI {
*/
NameHistory getNameHistory(UUID playerUUID, boolean queryMojangIfUnknown);

/**
* Gets a NameHistory for a player.
* If no entry is found in the cache this method will query Mojang if queryMojangIfUnknown is true. This
* query is blocking, so avoid calling it in the main thread if possible.
* The result may be null if this player's history is not found in the cache.
* This method can be called from any thread.
* The OfflinePlayer must return a UUID.
*
* @param player
* the player
* @param queryMojangIfUnknown
* query Mojang if this parameter is true and no entry is found in the cache
* @return the NameHistory or null
*/
default NameHistory getNameHistory(OfflinePlayer player, boolean queryMojangIfUnknown) {
return getNameHistory(player.getUniqueId(), queryMojangIfUnknown);
}

/**
* Gets a NameHistory for a UUID asyncronously.
* If no entry is found in the cache this method will query Mojang. When the result is available,
Expand Down Expand Up @@ -228,6 +259,9 @@ public interface PlayerUUIDCacheAPI {
* Returns the UUIDs of all player known to have used the given name in the past or present.
* This method will never query mojang. If no players are found, an empty set is returned.
*
* This method will usually query the database. If no database is present, it will iterate
* over the entire memory cache. Expect according performance.
*
* @param name
* the name to search for
* @return a set of all known players associated with that name
Expand Down
11 changes: 3 additions & 8 deletions src/main/java/de/iani/playerUUIDCache/UUIDDatabase.java
Original file line number Diff line number Diff line change
Expand Up @@ -84,11 +84,11 @@ public UUIDDatabase(SQLConfig config) throws SQLException {

deleteOldPlayerProfiles = "DELETE FROM " + profilesTableName + " WHERE lastSeen < ?";

insertNameHistory = "INSERT INTO " + nameHistoriesTableName + " (uuid, firstName, cacheLoadTime) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE firstName = ?, cacheLoadTime = ?";
insertNameHistory = "INSERT IGNORE INTO " + nameHistoriesTableName + " (uuid, firstName) VALUES (?, ?)";

insertNameChange = "INSERT IGNORE INTO " + nameChangesTableName + " (uuid, date, newName) VALUES (?, ?, ?)";

selectNameHistory = "SELECT (firstName, cacheLoadTime) FROM " + nameHistoriesTableName + " WHERE uuid = ?";
selectNameHistory = "SELECT (firstName) FROM " + nameHistoriesTableName + " WHERE uuid = ?";

selectNameChanges = "SELECT (date, newName) FROM " + nameChangesTableName + " WHERE uuid = ?";

Expand All @@ -100,7 +100,6 @@ public UUIDDatabase(SQLConfig config) throws SQLException {
smt.executeUpdate("CREATE TABLE `" + nameHistoriesTableName + "` ("//
+ "`uuid` CHAR( 36 ) NOT NULL,"//
+ "`firstName` VARCHAR( 16 ) NOT NULL,"//
+ "`cacheLoadTime` BIGINT NOT NULL,"//
+ "PRIMARY KEY ( `uuid` ), INDEX ( `firstName` ) ) ENGINE = innodb");
smt.close();
}
Expand Down Expand Up @@ -276,9 +275,6 @@ public void addOrUpdateHistory(final NameHistory history) throws SQLException {
PreparedStatement smt = sqlConnection.getOrCreateStatement(insertNameHistory);
smt.setString(1, history.getUUID().toString());
smt.setString(2, history.getFirstName());
smt.setLong(3, history.getCacheLoadTime());
smt.setString(4, history.getFirstName());
smt.setLong(5, history.getCacheLoadTime());
smt.executeUpdate();

smt = sqlConnection.getOrCreateStatement(insertNameChange);
Expand All @@ -305,7 +301,6 @@ public NameHistory getNameHistory(final UUID uuid) throws SQLException {
}

String firstName = rs.getString(1);
long cacheLoadTime = rs.getLong(2);
rs.close();

smt = sqlConnection.getOrCreateStatement(selectNameChanges);
Expand All @@ -318,7 +313,7 @@ public NameHistory getNameHistory(final UUID uuid) throws SQLException {
}
rs.close();

return new NameHistory(uuid, firstName, changes, cacheLoadTime);
return new NameHistory(uuid, firstName, changes, System.currentTimeMillis());
});
}

Expand Down

0 comments on commit 60a25c4

Please sign in to comment.