2022-12-31 03:23:35 +00:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2013-present RedisBungee contributors
|
|
|
|
*
|
|
|
|
* All rights reserved. This program and the accompanying materials
|
|
|
|
* are made available under the terms of the Eclipse Public License v1.0
|
|
|
|
* which accompanies this distribution, and is available at
|
|
|
|
*
|
|
|
|
* http://www.eclipse.org/legal/epl-v10.html
|
|
|
|
*/
|
|
|
|
|
2022-07-16 12:50:09 +00:00
|
|
|
package com.imaginarycode.minecraft.redisbungee.api.util.uuid;
|
2014-04-20 05:12:28 +00:00
|
|
|
|
|
|
|
import com.google.common.base.Charsets;
|
2015-06-22 08:00:25 +00:00
|
|
|
import com.google.common.collect.ImmutableMap;
|
2022-04-13 13:14:08 +00:00
|
|
|
import com.google.gson.Gson;
|
2022-07-16 12:50:09 +00:00
|
|
|
import com.imaginarycode.minecraft.redisbungee.api.RedisBungeePlugin;
|
2022-07-17 01:25:50 +00:00
|
|
|
import com.imaginarycode.minecraft.redisbungee.api.tasks.RedisTask;
|
2022-04-13 13:14:08 +00:00
|
|
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
2022-07-26 13:47:18 +00:00
|
|
|
import redis.clients.jedis.UnifiedJedis;
|
2014-05-28 22:29:44 +00:00
|
|
|
import redis.clients.jedis.exceptions.JedisException;
|
2014-04-20 05:12:28 +00:00
|
|
|
|
2024-04-12 18:37:02 +00:00
|
|
|
import java.util.Calendar;
|
|
|
|
import java.util.Collections;
|
|
|
|
import java.util.Map;
|
|
|
|
import java.util.UUID;
|
2014-07-30 17:46:39 +00:00
|
|
|
import java.util.concurrent.ConcurrentHashMap;
|
2014-04-20 05:12:28 +00:00
|
|
|
import java.util.regex.Pattern;
|
|
|
|
|
2014-07-30 17:46:39 +00:00
|
|
|
public final class UUIDTranslator {
|
2014-09-21 17:56:46 +00:00
|
|
|
private static final Pattern UUID_PATTERN = Pattern.compile("[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}");
|
|
|
|
private static final Pattern MOJANGIAN_UUID_PATTERN = Pattern.compile("[a-fA-F0-9]{32}");
|
2022-04-13 13:14:08 +00:00
|
|
|
private final RedisBungeePlugin<?> plugin;
|
2014-07-30 17:46:39 +00:00
|
|
|
private final Map<String, CachedUUIDEntry> nameToUuidMap = new ConcurrentHashMap<>(128, 0.5f, 4);
|
|
|
|
private final Map<UUID, CachedUUIDEntry> uuidToNameMap = new ConcurrentHashMap<>(128, 0.5f, 4);
|
2022-04-13 13:14:08 +00:00
|
|
|
private static final Gson gson = new Gson();
|
|
|
|
|
|
|
|
public UUIDTranslator(RedisBungeePlugin<?> plugin) {
|
|
|
|
this.plugin = plugin;
|
|
|
|
}
|
2014-04-20 05:12:28 +00:00
|
|
|
|
2014-07-30 17:46:39 +00:00
|
|
|
private void addToMaps(String name, UUID uuid) {
|
|
|
|
// This is why I like LocalDate...
|
|
|
|
|
|
|
|
// Cache the entry for three days.
|
|
|
|
Calendar calendar = Calendar.getInstance();
|
|
|
|
calendar.add(Calendar.DAY_OF_MONTH, 3);
|
|
|
|
|
|
|
|
// Create the entry and populate the local maps
|
2014-07-30 22:03:07 +00:00
|
|
|
CachedUUIDEntry entry = new CachedUUIDEntry(name, uuid, calendar);
|
2014-07-30 17:46:39 +00:00
|
|
|
nameToUuidMap.put(name.toLowerCase(), entry);
|
|
|
|
uuidToNameMap.put(uuid, entry);
|
|
|
|
}
|
|
|
|
|
2022-07-16 05:18:33 +00:00
|
|
|
public UUID getTranslatedUuid(@NonNull String player, boolean expensiveLookups) {
|
2014-07-30 17:46:39 +00:00
|
|
|
// If the player is online, give them their UUID.
|
|
|
|
// Remember, local data > remote data.
|
2022-04-13 13:14:08 +00:00
|
|
|
if (plugin.getPlayer(player) != null)
|
|
|
|
return plugin.getPlayerUUID(player);
|
2014-04-20 05:12:28 +00:00
|
|
|
|
2014-05-31 03:22:31 +00:00
|
|
|
// Check if it exists in the map
|
2014-07-30 17:46:39 +00:00
|
|
|
CachedUUIDEntry cachedUUIDEntry = nameToUuidMap.get(player.toLowerCase());
|
|
|
|
if (cachedUUIDEntry != null) {
|
|
|
|
if (!cachedUUIDEntry.expired())
|
|
|
|
return cachedUUIDEntry.getUuid();
|
|
|
|
else
|
|
|
|
nameToUuidMap.remove(player);
|
2014-05-31 03:22:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Check if we can exit early
|
2014-05-23 14:50:05 +00:00
|
|
|
if (UUID_PATTERN.matcher(player).find()) {
|
|
|
|
return UUID.fromString(player);
|
|
|
|
}
|
|
|
|
|
2014-05-23 14:53:38 +00:00
|
|
|
if (MOJANGIAN_UUID_PATTERN.matcher(player).find()) {
|
|
|
|
// Reconstruct the UUID
|
|
|
|
return UUIDFetcher.getUUID(player);
|
|
|
|
}
|
|
|
|
|
2014-07-30 17:46:39 +00:00
|
|
|
// If we are in offline mode, UUID generation is simple.
|
2014-12-29 16:40:34 +00:00
|
|
|
// We don't even have to cache the UUID, since this is easy to recalculate.
|
2022-04-13 13:14:08 +00:00
|
|
|
if (!plugin.isOnlineMode()) {
|
2014-07-30 17:46:39 +00:00
|
|
|
return UUID.nameUUIDFromBytes(("OfflinePlayer:" + player).getBytes(Charsets.UTF_8));
|
2014-05-31 03:22:31 +00:00
|
|
|
}
|
2022-12-25 02:26:53 +00:00
|
|
|
RedisTask<UUID> redisTask = new RedisTask<UUID>(plugin) {
|
2022-07-16 05:18:33 +00:00
|
|
|
@Override
|
2022-07-26 13:47:18 +00:00
|
|
|
public UUID unifiedJedisTask(UnifiedJedis unifiedJedis) {
|
|
|
|
String stored = unifiedJedis.hget("uuid-cache", player.toLowerCase());
|
2022-07-16 05:18:33 +00:00
|
|
|
if (stored != null) {
|
|
|
|
// Found an entry value. Deserialize it.
|
|
|
|
CachedUUIDEntry entry = gson.fromJson(stored, CachedUUIDEntry.class);
|
|
|
|
|
|
|
|
// Check for expiry:
|
|
|
|
if (entry.expired()) {
|
2022-07-26 13:47:18 +00:00
|
|
|
unifiedJedis.hdel("uuid-cache", player.toLowerCase());
|
2022-07-16 05:18:33 +00:00
|
|
|
// Doesn't hurt to also remove the UUID entry as well.
|
2022-07-26 13:47:18 +00:00
|
|
|
unifiedJedis.hdel("uuid-cache", entry.getUuid().toString());
|
2022-07-16 05:18:33 +00:00
|
|
|
} else {
|
|
|
|
nameToUuidMap.put(player.toLowerCase(), entry);
|
|
|
|
uuidToNameMap.put(entry.getUuid(), entry);
|
|
|
|
return entry.getUuid();
|
|
|
|
}
|
2014-05-28 22:29:44 +00:00
|
|
|
}
|
2014-04-20 05:12:28 +00:00
|
|
|
|
2022-07-16 05:18:33 +00:00
|
|
|
// That didn't work. Let's ask Mojang.
|
|
|
|
if (!expensiveLookups || !plugin.isOnlineMode())
|
|
|
|
return null;
|
2014-05-31 03:22:31 +00:00
|
|
|
|
2022-07-16 05:18:33 +00:00
|
|
|
Map<String, UUID> uuidMap1;
|
|
|
|
try {
|
|
|
|
uuidMap1 = new UUIDFetcher(Collections.singletonList(player)).call();
|
|
|
|
} catch (Exception e) {
|
|
|
|
plugin.logFatal("Unable to fetch UUID from Mojang for " + player);
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
for (Map.Entry<String, UUID> entry : uuidMap1.entrySet()) {
|
|
|
|
if (entry.getKey().equalsIgnoreCase(player)) {
|
2022-07-26 13:47:18 +00:00
|
|
|
persistInfo(entry.getKey(), entry.getValue(), unifiedJedis);
|
2022-07-16 05:18:33 +00:00
|
|
|
return entry.getValue();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return null;
|
2014-11-26 22:26:51 +00:00
|
|
|
}
|
2022-07-16 05:18:33 +00:00
|
|
|
};
|
|
|
|
// Let's try Redis.
|
|
|
|
try {
|
|
|
|
return redisTask.execute();
|
2014-11-26 22:26:51 +00:00
|
|
|
} catch (JedisException e) {
|
2022-04-13 13:14:08 +00:00
|
|
|
plugin.logFatal("Unable to fetch UUID for " + player);
|
2014-04-20 05:12:28 +00:00
|
|
|
}
|
2014-05-28 22:29:44 +00:00
|
|
|
|
|
|
|
return null; // Nope, game over!
|
2014-04-20 05:12:28 +00:00
|
|
|
}
|
|
|
|
|
2022-07-16 05:18:33 +00:00
|
|
|
public String getNameFromUuid(@NonNull UUID player, boolean expensiveLookups) {
|
2014-07-30 17:46:39 +00:00
|
|
|
// If the player is online, give them their UUID.
|
|
|
|
// Remember, local data > remote data.
|
2022-04-13 13:14:08 +00:00
|
|
|
if (plugin.getPlayer(player) != null)
|
|
|
|
return plugin.getPlayerName(player);
|
2014-04-20 05:12:28 +00:00
|
|
|
|
2014-07-30 17:46:39 +00:00
|
|
|
// Check if it exists in the map
|
|
|
|
CachedUUIDEntry cachedUUIDEntry = uuidToNameMap.get(player);
|
|
|
|
if (cachedUUIDEntry != null) {
|
|
|
|
if (!cachedUUIDEntry.expired())
|
|
|
|
return cachedUUIDEntry.getName();
|
|
|
|
else
|
|
|
|
uuidToNameMap.remove(player);
|
2014-05-31 03:22:31 +00:00
|
|
|
}
|
2014-04-20 05:12:28 +00:00
|
|
|
|
2022-12-25 02:26:53 +00:00
|
|
|
RedisTask<String> redisTask = new RedisTask<String>(plugin) {
|
2022-07-16 05:18:33 +00:00
|
|
|
@Override
|
2022-07-26 13:47:18 +00:00
|
|
|
public String unifiedJedisTask(UnifiedJedis unifiedJedis) {
|
|
|
|
String stored = unifiedJedis.hget("uuid-cache", player.toString());
|
2022-07-16 05:18:33 +00:00
|
|
|
if (stored != null) {
|
|
|
|
// Found an entry value. Deserialize it.
|
|
|
|
CachedUUIDEntry entry = gson.fromJson(stored, CachedUUIDEntry.class);
|
|
|
|
|
|
|
|
// Check for expiry:
|
|
|
|
if (entry.expired()) {
|
2022-07-26 13:47:18 +00:00
|
|
|
unifiedJedis.hdel("uuid-cache", player.toString());
|
2022-07-16 05:18:33 +00:00
|
|
|
// Doesn't hurt to also remove the named entry as well.
|
|
|
|
// TODO: Since UUIDs are fixed, we could look up the name and see if the UUID matches.
|
2022-07-26 13:47:18 +00:00
|
|
|
unifiedJedis.hdel("uuid-cache", entry.getName());
|
2022-07-16 05:18:33 +00:00
|
|
|
} else {
|
|
|
|
nameToUuidMap.put(entry.getName().toLowerCase(), entry);
|
|
|
|
uuidToNameMap.put(player, entry);
|
|
|
|
return entry.getName();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!expensiveLookups || !plugin.isOnlineMode())
|
|
|
|
return null;
|
|
|
|
|
2023-07-18 00:48:20 +00:00
|
|
|
// That didn't work. Let's ask PlayerDB.
|
2022-07-16 05:18:33 +00:00
|
|
|
String name;
|
|
|
|
try {
|
2023-07-18 00:48:20 +00:00
|
|
|
name = NameFetcher.getName(player);
|
2022-07-16 05:18:33 +00:00
|
|
|
} catch (Exception e) {
|
2023-07-18 00:48:20 +00:00
|
|
|
plugin.logFatal("Unable to fetch name from PlayerDB for " + player);
|
2022-07-16 05:18:33 +00:00
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (name != null) {
|
2022-07-26 13:47:18 +00:00
|
|
|
persistInfo(name, player, unifiedJedis);
|
2022-07-16 05:18:33 +00:00
|
|
|
return name;
|
|
|
|
}
|
2014-05-31 03:22:31 +00:00
|
|
|
|
2014-05-28 22:29:44 +00:00
|
|
|
return null;
|
|
|
|
}
|
2022-07-16 05:18:33 +00:00
|
|
|
};
|
2014-04-20 05:12:28 +00:00
|
|
|
|
|
|
|
|
2022-07-16 05:18:33 +00:00
|
|
|
// Okay, it wasn't locally cached. Let's try Redis.
|
|
|
|
try {
|
|
|
|
return redisTask.execute();
|
2014-05-28 22:29:44 +00:00
|
|
|
} catch (JedisException e) {
|
2022-04-13 13:14:08 +00:00
|
|
|
plugin.logFatal("Unable to fetch name for " + player);
|
2014-07-30 17:46:39 +00:00
|
|
|
return null;
|
2014-04-20 05:12:28 +00:00
|
|
|
}
|
|
|
|
}
|
2014-04-23 22:05:42 +00:00
|
|
|
|
2022-07-26 13:47:18 +00:00
|
|
|
public void persistInfo(String name, UUID uuid, UnifiedJedis unifiedJedis) {
|
2015-06-21 22:09:46 +00:00
|
|
|
addToMaps(name, uuid);
|
2022-04-13 13:14:08 +00:00
|
|
|
String json = gson.toJson(uuidToNameMap.get(uuid));
|
2022-11-11 16:30:16 +00:00
|
|
|
unifiedJedis.hset("uuid-cache", ImmutableMap.of(name.toLowerCase(), json, uuid.toString(), json));
|
2015-06-21 22:09:46 +00:00
|
|
|
}
|
|
|
|
|
2022-04-13 13:14:08 +00:00
|
|
|
|
2014-04-20 05:12:28 +00:00
|
|
|
}
|