forked from zhdev/griefus
Fixes for copper golem logging (WIP)
This commit is contained in:
parent
1f60dcc3cf
commit
de5a064c8d
4 changed files with 269 additions and 75 deletions
|
|
@ -100,9 +100,8 @@ public class Process {
|
||||||
if (data != null) {
|
if (data != null) {
|
||||||
String user = data[0];
|
String user = data[0];
|
||||||
String uuid = data[1];
|
String uuid = data[1];
|
||||||
String normalizedUser = SyntheticUsernames.normalize(user);
|
if (user != null && ConfigHandler.playerIdCache.get(user.toLowerCase(Locale.ROOT)) == null) {
|
||||||
if (normalizedUser != null && ConfigHandler.playerIdCache.get(normalizedUser.toLowerCase(Locale.ROOT)) == null) {
|
UserStatement.loadId(connection, user, uuid);
|
||||||
UserStatement.loadId(connection, normalizedUser, uuid);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,6 @@ import net.coreprotect.event.CoreProtectPreLogEvent;
|
||||||
import net.coreprotect.utility.BlockUtils;
|
import net.coreprotect.utility.BlockUtils;
|
||||||
import net.coreprotect.utility.ItemUtils;
|
import net.coreprotect.utility.ItemUtils;
|
||||||
import net.coreprotect.utility.MaterialUtils;
|
import net.coreprotect.utility.MaterialUtils;
|
||||||
import net.coreprotect.utility.SyntheticUsernames;
|
|
||||||
import net.coreprotect.utility.WorldUtils;
|
import net.coreprotect.utility.WorldUtils;
|
||||||
import net.coreprotect.utility.serialize.ItemMetaHandler;
|
import net.coreprotect.utility.serialize.ItemMetaHandler;
|
||||||
|
|
||||||
|
|
@ -58,13 +57,7 @@ public class ContainerLogger extends Queue {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
String uniqueUser = player;
|
String loggingContainerId = player.toLowerCase(Locale.ROOT) + "." + location.getBlockX() + "." + location.getBlockY() + "." + location.getBlockZ();
|
||||||
String canonicalUser = SyntheticUsernames.normalize(uniqueUser);
|
|
||||||
if (canonicalUser == null) {
|
|
||||||
canonicalUser = uniqueUser;
|
|
||||||
}
|
|
||||||
|
|
||||||
String loggingContainerId = uniqueUser.toLowerCase(Locale.ROOT) + "." + location.getBlockX() + "." + location.getBlockY() + "." + location.getBlockZ();
|
|
||||||
List<ItemStack[]> oldList = ConfigHandler.oldContainer.get(loggingContainerId);
|
List<ItemStack[]> oldList = ConfigHandler.oldContainer.get(loggingContainerId);
|
||||||
ItemStack[] oi1 = oldList.get(0);
|
ItemStack[] oi1 = oldList.get(0);
|
||||||
ItemStack[] oldInventory = ItemUtils.getContainerState(oi1);
|
ItemStack[] oldInventory = ItemUtils.getContainerState(oi1);
|
||||||
|
|
@ -74,7 +67,7 @@ public class ContainerLogger extends Queue {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if this is a dispenser with no actual changes
|
// Check if this is a dispenser with no actual changes
|
||||||
if ("#dispenser".equals(canonicalUser) && ItemUtils.compareContainers(oldInventory, newInventory)) {
|
if ("#dispenser".equals(player) && ItemUtils.compareContainers(oldInventory, newInventory)) {
|
||||||
// No changes detected, mark this dispenser in the dispenserNoChange map
|
// No changes detected, mark this dispenser in the dispenserNoChange map
|
||||||
// Extract the location key from the loggingContainerId
|
// Extract the location key from the loggingContainerId
|
||||||
// Format: #dispenser.x.y.z
|
// Format: #dispenser.x.y.z
|
||||||
|
|
@ -102,7 +95,7 @@ public class ContainerLogger extends Queue {
|
||||||
|
|
||||||
// If we reach here, the dispenser event resulted in changes
|
// If we reach here, the dispenser event resulted in changes
|
||||||
// Remove any pending event for this dispenser
|
// Remove any pending event for this dispenser
|
||||||
if ("#dispenser".equals(canonicalUser)) {
|
if ("#dispenser".equals(player)) {
|
||||||
String[] parts = loggingContainerId.split("\\.");
|
String[] parts = loggingContainerId.split("\\.");
|
||||||
if (parts.length >= 4) {
|
if (parts.length >= 4) {
|
||||||
int x = Integer.parseInt(parts[1]);
|
int x = Integer.parseInt(parts[1]);
|
||||||
|
|
@ -198,12 +191,12 @@ public class ContainerLogger extends Queue {
|
||||||
ItemUtils.mergeItems(type, newInventory);
|
ItemUtils.mergeItems(type, newInventory);
|
||||||
|
|
||||||
if (type != Material.ENDER_CHEST) {
|
if (type != Material.ENDER_CHEST) {
|
||||||
logTransaction(preparedStmtContainer, batchCount, canonicalUser, type, faceData, oldInventory, 0, location);
|
logTransaction(preparedStmtContainer, batchCount, player, type, faceData, oldInventory, 0, location);
|
||||||
logTransaction(preparedStmtContainer, batchCount, canonicalUser, type, faceData, newInventory, 1, location);
|
logTransaction(preparedStmtContainer, batchCount, player, type, faceData, newInventory, 1, location);
|
||||||
}
|
}
|
||||||
else { // pass ender chest transactions to item logger
|
else { // pass ender chest transactions to item logger
|
||||||
ItemLogger.logTransaction(preparedStmtItems, batchCount, 0, canonicalUser, location, oldInventory, ItemLogger.ITEM_REMOVE_ENDER);
|
ItemLogger.logTransaction(preparedStmtItems, batchCount, 0, player, location, oldInventory, ItemLogger.ITEM_REMOVE_ENDER);
|
||||||
ItemLogger.logTransaction(preparedStmtItems, batchCount, 0, canonicalUser, location, newInventory, ItemLogger.ITEM_ADD_ENDER);
|
ItemLogger.logTransaction(preparedStmtItems, batchCount, 0, player, location, newInventory, ItemLogger.ITEM_ADD_ENDER);
|
||||||
}
|
}
|
||||||
|
|
||||||
oldList.remove(0);
|
oldList.remove(0);
|
||||||
|
|
|
||||||
|
|
@ -1,34 +1,44 @@
|
||||||
package net.coreprotect.paper.listener;
|
package net.coreprotect.paper.listener;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
import org.bukkit.Location;
|
import org.bukkit.Location;
|
||||||
|
import org.bukkit.Material;
|
||||||
import org.bukkit.block.Block;
|
import org.bukkit.block.Block;
|
||||||
import org.bukkit.block.BlockState;
|
import org.bukkit.block.BlockState;
|
||||||
|
import org.bukkit.entity.CopperGolem;
|
||||||
import org.bukkit.entity.Entity;
|
import org.bukkit.entity.Entity;
|
||||||
import org.bukkit.event.EventHandler;
|
import org.bukkit.event.EventHandler;
|
||||||
import org.bukkit.event.EventPriority;
|
import org.bukkit.event.EventPriority;
|
||||||
import org.bukkit.event.Listener;
|
import org.bukkit.event.Listener;
|
||||||
|
import org.bukkit.inventory.EntityEquipment;
|
||||||
|
import org.bukkit.inventory.Inventory;
|
||||||
import org.bukkit.inventory.InventoryHolder;
|
import org.bukkit.inventory.InventoryHolder;
|
||||||
import org.bukkit.inventory.ItemStack;
|
import org.bukkit.inventory.ItemStack;
|
||||||
import org.bukkit.scheduler.BukkitTask;
|
import org.bukkit.scheduler.BukkitTask;
|
||||||
|
|
||||||
import io.papermc.paper.event.entity.ItemTransportingEntityValidateTargetEvent;
|
import io.papermc.paper.event.entity.ItemTransportingEntityValidateTargetEvent;
|
||||||
import net.coreprotect.CoreProtect;
|
import net.coreprotect.CoreProtect;
|
||||||
|
import net.coreprotect.bukkit.BukkitAdapter;
|
||||||
import net.coreprotect.config.Config;
|
import net.coreprotect.config.Config;
|
||||||
import net.coreprotect.listener.player.InventoryChangeListener;
|
import net.coreprotect.listener.player.InventoryChangeListener;
|
||||||
import net.coreprotect.utility.SyntheticUsernames;
|
import net.coreprotect.utility.ItemUtils;
|
||||||
|
|
||||||
public final class CopperGolemChestListener implements Listener {
|
public final class CopperGolemChestListener implements Listener {
|
||||||
|
|
||||||
private static final String COPPER_GOLEM_NAME = "COPPER_GOLEM";
|
private static final String COPPER_GOLEM_NAME = "COPPER_GOLEM";
|
||||||
private static final String USERNAME = "#copper_golem";
|
private static final String USERNAME = "#copper_golem";
|
||||||
private static final long DELAY_TICKS = 60L;
|
private static final long INITIAL_DELAY_TICKS = 5L;
|
||||||
|
private static final long POLL_INTERVAL_TICKS = 15L;
|
||||||
|
private static final long MAX_POLL_DURATION_TICKS = 600L;
|
||||||
|
private static final long MIN_THROTTLE_MILLIS = 2800L;
|
||||||
|
|
||||||
private final CoreProtect plugin;
|
private final CoreProtect plugin;
|
||||||
private final Map<UUID, PendingTransaction> pendingTransactions = new ConcurrentHashMap<>();
|
private final Map<TransactionKey, PendingTransaction> pendingTransactions = new ConcurrentHashMap<>();
|
||||||
|
private final Map<TransactionKey, Long> throttleUntil = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
public CopperGolemChestListener(CoreProtect plugin) {
|
public CopperGolemChestListener(CoreProtect plugin) {
|
||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
|
|
@ -44,6 +54,8 @@ public final class CopperGolemChestListener implements Listener {
|
||||||
if (entity == null || entity.getType() == null || !COPPER_GOLEM_NAME.equals(entity.getType().name())) {
|
if (entity == null || entity.getType() == null || !COPPER_GOLEM_NAME.equals(entity.getType().name())) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
CopperGolem copperGolem = (CopperGolem) entity;
|
||||||
|
Material heldMaterial = getHeldItemMaterial(copperGolem);
|
||||||
|
|
||||||
Block block = event.getBlock();
|
Block block = event.getBlock();
|
||||||
if (block == null) {
|
if (block == null) {
|
||||||
|
|
@ -64,39 +76,220 @@ public final class CopperGolemChestListener implements Listener {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
scheduleTransaction(entity, location);
|
scheduleTransaction(copperGolem, blockState, heldMaterial);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void scheduleTransaction(Entity entity, Location location) {
|
private void scheduleTransaction(CopperGolem copperGolem, BlockState blockState, Material heldMaterial) {
|
||||||
UUID entityId = entity.getUniqueId();
|
Location location = blockState.getLocation();
|
||||||
String username = SyntheticUsernames.qualifyWithUuid(USERNAME, entityId);
|
if (location == null || copperGolem == null) {
|
||||||
PendingTransaction pendingTransaction = pendingTransactions.remove(entityId);
|
return;
|
||||||
if (pendingTransaction != null) {
|
|
||||||
pendingTransaction.cancel();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TransactionKey transactionKey = TransactionKey.of(location);
|
||||||
Location targetLocation = location.clone();
|
Location targetLocation = location.clone();
|
||||||
PendingTransaction scheduled = new PendingTransaction();
|
ItemStack[] baselineState = captureInventoryState(targetLocation);
|
||||||
BukkitTask task = plugin.getServer().getScheduler().runTaskLater(plugin, () -> {
|
if (baselineState == null) {
|
||||||
if (!pendingTransactions.remove(entityId, scheduled)) {
|
return;
|
||||||
|
}
|
||||||
|
if (!shouldMonitorInteraction(blockState.getType(), baselineState, heldMaterial)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Entity trackedEntity = plugin.getServer().getEntity(entityId);
|
PendingTransaction existing = pendingTransactions.get(transactionKey);
|
||||||
if (trackedEntity == null || !trackedEntity.isValid()) {
|
if (existing != null) {
|
||||||
|
existing.refresh(baselineState);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
InventoryChangeListener.inventoryTransaction(username, targetLocation, null);
|
if (isThrottled(transactionKey)) {
|
||||||
}, DELAY_TICKS);
|
return;
|
||||||
|
|
||||||
scheduled.setTask(task);
|
|
||||||
pendingTransactions.put(entityId, scheduled);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final class PendingTransaction {
|
PendingTransaction scheduled = new PendingTransaction(transactionKey, targetLocation, baselineState);
|
||||||
|
pendingTransactions.put(transactionKey, scheduled);
|
||||||
|
throttleUntil.put(transactionKey, System.currentTimeMillis() + MIN_THROTTLE_MILLIS);
|
||||||
|
scheduled.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private ItemStack[] captureInventoryState(Location location) {
|
||||||
|
if (location == null || location.getWorld() == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
BlockState blockState = location.getBlock().getState();
|
||||||
|
if (!(blockState instanceof InventoryHolder)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
InventoryHolder inventoryHolder = (InventoryHolder) blockState;
|
||||||
|
Inventory inventory = inventoryHolder.getInventory();
|
||||||
|
if (inventory == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ItemUtils.getContainerState(inventory.getContents());
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean hasInventoryChanged(ItemStack[] previousState, ItemStack[] currentState) {
|
||||||
|
if (previousState == null || currentState == null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (previousState.length != currentState.length) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < previousState.length; i++) {
|
||||||
|
ItemStack previousItem = previousState[i];
|
||||||
|
ItemStack currentItem = currentState[i];
|
||||||
|
|
||||||
|
if (previousItem == null && currentItem == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (previousItem == null || currentItem == null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!previousItem.equals(currentItem)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean shouldMonitorInteraction(Material blockType, ItemStack[] baselineState, Material heldMaterial) {
|
||||||
|
boolean isCopperChest = BukkitAdapter.ADAPTER.isCopperChest(blockType);
|
||||||
|
boolean isStandardChest = blockType == Material.CHEST || blockType == Material.TRAPPED_CHEST;
|
||||||
|
boolean golemHoldingItem = heldMaterial != null && heldMaterial != Material.AIR;
|
||||||
|
|
||||||
|
if (!isCopperChest && !isStandardChest) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isCopperChest) {
|
||||||
|
return !golemHoldingItem && !isInventoryEmpty(baselineState);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!golemHoldingItem) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isInventoryEmpty(baselineState)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return containsMaterial(baselineState, heldMaterial);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isInventoryEmpty(ItemStack[] state) {
|
||||||
|
if (state == null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (ItemStack item : state) {
|
||||||
|
if (!isEmptyItem(item)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isEmptyItem(ItemStack item) {
|
||||||
|
return item == null || item.getType().isAir() || item.getAmount() <= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Material getHeldItemMaterial(CopperGolem copperGolem) {
|
||||||
|
if (copperGolem == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
EntityEquipment equipment = copperGolem.getEquipment();
|
||||||
|
if (equipment == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
ItemStack mainHand = equipment.getItemInMainHand();
|
||||||
|
if (isEmptyItem(mainHand)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return mainHand.getType();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean containsMaterial(ItemStack[] state, Material material) {
|
||||||
|
if (state == null || material == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (ItemStack item : state) {
|
||||||
|
if (item != null && item.getType() == material && item.getAmount() > 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isThrottled(TransactionKey transactionKey) {
|
||||||
|
Long eligibleAt = throttleUntil.get(transactionKey);
|
||||||
|
if (eligibleAt == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
long now = System.currentTimeMillis();
|
||||||
|
if (eligibleAt <= now) {
|
||||||
|
throttleUntil.remove(transactionKey, eligibleAt);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private final class PendingTransaction implements Runnable {
|
||||||
|
|
||||||
|
private final TransactionKey transactionKey;
|
||||||
|
private final Location targetLocation;
|
||||||
|
private ItemStack[] baselineState;
|
||||||
private BukkitTask task;
|
private BukkitTask task;
|
||||||
|
private long ticksElapsed;
|
||||||
|
private long ticksSinceLastTrigger;
|
||||||
|
|
||||||
|
private PendingTransaction(TransactionKey transactionKey, Location targetLocation, ItemStack[] baselineState) {
|
||||||
|
this.transactionKey = transactionKey;
|
||||||
|
this.targetLocation = targetLocation;
|
||||||
|
this.baselineState = baselineState;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void start() {
|
||||||
|
task = plugin.getServer().getScheduler().runTaskTimer(plugin, this, INITIAL_DELAY_TICKS, POLL_INTERVAL_TICKS);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void refresh(ItemStack[] newBaselineState) {
|
||||||
|
ticksSinceLastTrigger = 0L;
|
||||||
|
if (newBaselineState != null) {
|
||||||
|
baselineState = newBaselineState;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
long increment = ticksElapsed == 0L ? INITIAL_DELAY_TICKS : POLL_INTERVAL_TICKS;
|
||||||
|
ticksElapsed += increment;
|
||||||
|
ticksSinceLastTrigger += increment;
|
||||||
|
if (ticksSinceLastTrigger > MAX_POLL_DURATION_TICKS) {
|
||||||
|
cancelAndRemove();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ItemStack[] currentState = captureInventoryState(targetLocation);
|
||||||
|
if (currentState == null) {
|
||||||
|
cancelAndRemove();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean stateChanged = hasInventoryChanged(baselineState, currentState);
|
||||||
|
if (stateChanged) {
|
||||||
|
InventoryChangeListener.inventoryTransaction(USERNAME, targetLocation, baselineState);
|
||||||
|
baselineState = ItemUtils.getContainerState(currentState);
|
||||||
|
// cancelAndRemove();
|
||||||
|
// return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void cancel() {
|
private void cancel() {
|
||||||
if (task != null) {
|
if (task != null) {
|
||||||
|
|
@ -104,8 +297,49 @@ public final class CopperGolemChestListener implements Listener {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setTask(BukkitTask task) {
|
private void cancelAndRemove() {
|
||||||
this.task = task;
|
cancel();
|
||||||
|
pendingTransactions.remove(transactionKey, this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final class TransactionKey {
|
||||||
|
|
||||||
|
private final UUID worldId;
|
||||||
|
private final int x;
|
||||||
|
private final int y;
|
||||||
|
private final int z;
|
||||||
|
|
||||||
|
private TransactionKey(UUID worldId, int x, int y, int z) {
|
||||||
|
this.worldId = worldId;
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
this.z = z;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static TransactionKey of(Location location) {
|
||||||
|
if (location == null || location.getWorld() == null) {
|
||||||
|
throw new IllegalArgumentException("Location must have world");
|
||||||
|
}
|
||||||
|
return new TransactionKey(location.getWorld().getUID(), location.getBlockX(), location.getBlockY(), location.getBlockZ());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!(obj instanceof TransactionKey)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
TransactionKey other = (TransactionKey) obj;
|
||||||
|
return worldId.equals(other.worldId) && x == other.x && y == other.y && z == other.z;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(worldId, x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,32 +0,0 @@
|
||||||
package net.coreprotect.utility;
|
|
||||||
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
public final class SyntheticUsernames {
|
|
||||||
|
|
||||||
private static final String UUID_SUFFIX = "_uuid:";
|
|
||||||
|
|
||||||
private SyntheticUsernames() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String normalize(String user) {
|
|
||||||
if (user == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
int index = user.indexOf(UUID_SUFFIX);
|
|
||||||
if (index == -1) {
|
|
||||||
return user;
|
|
||||||
}
|
|
||||||
|
|
||||||
return user.substring(0, index);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String qualifyWithUuid(String base, UUID uuid) {
|
|
||||||
if (base == null || uuid == null) {
|
|
||||||
return base;
|
|
||||||
}
|
|
||||||
|
|
||||||
return base + UUID_SUFFIX + uuid;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue