Initial commit

This commit is contained in:
luk
2026-01-25 21:02:19 +00:00
commit 0ad4b55303
43 changed files with 19721 additions and 0 deletions

View File

@@ -0,0 +1,295 @@
package coldfusion.hytaleserver;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Guards against inventory sharing between players (Issue #45).
* <p>
* This class is called by the patched LivingEntity.setInventory() method
* to validate that an Inventory isn't being shared between multiple players.
* <p>
* If a shared reference is detected, the inventory is deep-cloned to break
* the shared reference before assignment.
*/
public class InventoryOwnershipGuard {
private static final Logger LOGGER = Logger.getLogger("HyFixes");
// Track inventory -> owner UUID
private static final Map<Integer, UUID> inventoryOwners = new ConcurrentHashMap<>();
private static final Map<Integer, UUID> containerOwners = new ConcurrentHashMap<>();
// Statistics
private static volatile int sharedReferencesDetected = 0;
private static volatile int inventoriesCloned = 0;
private static volatile int validationCalls = 0;
// Cached reflection objects
private static Method getPlayerRefMethod;
private static Method getUuidMethod;
private static Class<?> playerClass;
private static boolean reflectionInitialized = false;
private static boolean reflectionFailed = false;
/**
* Called by patched LivingEntity.setInventory() BEFORE inventory assignment.
*/
public static Object validateAndClone(Object livingEntity, Object inventory) {
if (inventory == null || livingEntity == null) {
return inventory;
}
validationCalls++;
try {
if (!reflectionInitialized && !reflectionFailed) {
initializeReflection();
}
if (reflectionFailed) {
return inventory;
}
// Only validate for Player entities
if (!playerClass.isInstance(livingEntity)) {
return inventory;
}
UUID newOwnerUuid = getEntityUuid(livingEntity);
if (newOwnerUuid == null) {
return inventory;
}
int inventoryId = System.identityHashCode(inventory);
UUID currentOwner = inventoryOwners.get(inventoryId);
if (currentOwner != null && !currentOwner.equals(newOwnerUuid)) {
// SHARED REFERENCE DETECTED!
sharedReferencesDetected++;
LOGGER.log(Level.WARNING,
"[HyFix #45] INVENTORY SHARING DETECTED!\n" +
" Inventory @{0} owned by {1} being assigned to {2}\n" +
" Forcing deep clone to prevent inventory sync.",
new Object[]{
Integer.toHexString(inventoryId),
currentOwner.toString().substring(0, 8) + "...",
newOwnerUuid.toString().substring(0, 8) + "..."
}
);
Object clonedInventory = cloneInventory(inventory);
if (clonedInventory != null) {
inventoriesCloned++;
int clonedId = System.identityHashCode(clonedInventory);
inventoryOwners.put(clonedId, newOwnerUuid);
registerContainers(clonedInventory, newOwnerUuid);
return clonedInventory;
} else {
LOGGER.log(Level.SEVERE,
"[HyFix #45] CRITICAL: Failed to clone inventory!");
}
} else {
inventoryOwners.put(inventoryId, newOwnerUuid);
registerContainers(inventory, newOwnerUuid);
}
} catch (Exception e) {
if (validationCalls < 5) {
LOGGER.log(Level.WARNING,
"[HyFix] Error in inventory validation: " + e.getMessage());
}
}
return inventory;
}
private static synchronized void initializeReflection() {
if (reflectionInitialized || reflectionFailed) {
return;
}
try {
playerClass = Class.forName("com.hypixel.hytale.server.core.entity.entities.Player");
getPlayerRefMethod = playerClass.getMethod("getPlayerRef");
Class<?> playerRefClass = Class.forName("com.hypixel.hytale.server.core.universe.PlayerRef");
getUuidMethod = playerRefClass.getMethod("getUuid");
reflectionInitialized = true;
LOGGER.log(Level.INFO, "[HyFix] InventoryOwnershipGuard initialized");
} catch (Exception e) {
reflectionFailed = true;
LOGGER.log(Level.WARNING,
"[HyFix] Failed to initialize InventoryOwnershipGuard: " + e.getMessage());
}
}
private static UUID getEntityUuid(Object livingEntity) {
try {
Object playerRef = getPlayerRefMethod.invoke(livingEntity);
if (playerRef == null) {
return null;
}
return (UUID) getUuidMethod.invoke(playerRef);
} catch (Exception e) {
return null;
}
}
private static void registerContainers(Object inventory, UUID ownerUuid) {
try {
String[] containerGetters = {"getStorage", "getArmor", "getHotbar", "getUtility", "getTools", "getBackpack"};
for (String getter : containerGetters) {
try {
Method method = inventory.getClass().getMethod(getter);
Object container = method.invoke(inventory);
if (container != null) {
containerOwners.put(System.identityHashCode(container), ownerUuid);
}
} catch (NoSuchMethodException ignored) {
}
}
} catch (Exception e) {
}
}
private static Object cloneInventory(Object inventory) {
Object cloned = cloneViaCodec(inventory);
if (cloned != null) {
return cloned;
}
cloned = cloneViaContainers(inventory);
if (cloned != null) {
return cloned;
}
LOGGER.log(Level.SEVERE, "[HyFix] All inventory clone methods failed!");
return null;
}
private static Object cloneViaCodec(Object inventory) {
try {
Class<?> inventoryClass = inventory.getClass();
Field codecField = inventoryClass.getField("CODEC");
Object codec = codecField.get(null);
Class<?> extraInfoClass = Class.forName("com.hypixel.hytale.codec.ExtraInfo");
Field threadLocalField = extraInfoClass.getField("THREAD_LOCAL");
ThreadLocal<?> threadLocal = (ThreadLocal<?>) threadLocalField.get(null);
Object extraInfo = threadLocal.get();
if (extraInfo == null) {
return null;
}
Method encodeMethod = findMethod(codec.getClass(), "encode", Object.class, extraInfoClass);
if (encodeMethod == null) {
return null;
}
Object bsonValue = encodeMethod.invoke(codec, inventory, extraInfo);
Constructor<?> defaultConstructor = inventoryClass.getDeclaredConstructor();
defaultConstructor.setAccessible(true);
Object newInventory = defaultConstructor.newInstance();
Class<?> bsonValueClass = Class.forName("org.bson.BsonValue");
Method decodeMethod = findMethod(codec.getClass(), "decode", bsonValueClass, Object.class, extraInfoClass);
if (decodeMethod == null) {
return null;
}
decodeMethod.invoke(codec, bsonValue, newInventory, extraInfo);
return newInventory;
} catch (Exception e) {
return null;
}
}
private static Object cloneViaContainers(Object inventory) {
try {
Class<?> inventoryClass = inventory.getClass();
Object storage = invokeGetter(inventory, "getStorage");
Object armor = invokeGetter(inventory, "getArmor");
Object hotbar = invokeGetter(inventory, "getHotbar");
Object utility = invokeGetter(inventory, "getUtility");
Object tools = invokeGetter(inventory, "getTools");
Object backpack = invokeGetter(inventory, "getBackpack");
Object clonedStorage = cloneContainer(storage);
Object clonedArmor = cloneContainer(armor);
Object clonedHotbar = cloneContainer(hotbar);
Object clonedUtility = cloneContainer(utility);
Object clonedTools = cloneContainer(tools);
Object clonedBackpack = cloneContainer(backpack);
Class<?> containerClass = Class.forName("com.hypixel.hytale.server.core.inventory.container.ItemContainer");
Constructor<?> constructor = inventoryClass.getConstructor(
containerClass, containerClass, containerClass,
containerClass, containerClass, containerClass
);
return constructor.newInstance(
clonedStorage, clonedArmor, clonedHotbar,
clonedUtility, clonedTools, clonedBackpack
);
} catch (Exception e) {
return null;
}
}
private static Object cloneContainer(Object container) {
if (container == null) {
return null;
}
try {
Method cloneMethod = container.getClass().getMethod("clone");
return cloneMethod.invoke(container);
} catch (Exception e) {
return container;
}
}
private static Object invokeGetter(Object obj, String methodName) {
try {
Method method = obj.getClass().getMethod(methodName);
return method.invoke(obj);
} catch (Exception e) {
return null;
}
}
private static Method findMethod(Class<?> clazz, String name, Class<?>... paramTypes) {
try {
return clazz.getMethod(name, paramTypes);
} catch (NoSuchMethodException e) {
for (Method m : clazz.getMethods()) {
if (m.getName().equals(name) && m.getParameterCount() == paramTypes.length) {
return m;
}
}
return null;
}
}
public static void onPlayerDisconnect(UUID playerUuid) {
if (playerUuid == null) {
return;
}
inventoryOwners.entrySet().removeIf(entry -> playerUuid.equals(entry.getValue()));
containerOwners.entrySet().removeIf(entry -> playerUuid.equals(entry.getValue()));
}
}