From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: JellySquid Date: Tue, 4 Aug 2020 21:46:05 +0200 Subject: [PATCH] lithium-MixinDataWatcher Original code by JellySquid, licensed under GNU Lesser General Public License v3.0 you can find the original code on https://github.com/jellysquid3/lithium-fabric/tree/1.16.x/fabric (Yarn mappings) Co-authored-by: tr7zw diff --git a/src/main/java/net/minecraft/server/DataWatcher.java b/src/main/java/net/minecraft/server/DataWatcher.java index be8e705fc3f2c31e4f84f59c977488d8ecd9cae1..d8147567b4dfdc48b86f6349fca084fdb4381c42 100644 --- a/src/main/java/net/minecraft/server/DataWatcher.java +++ b/src/main/java/net/minecraft/server/DataWatcher.java @@ -6,6 +6,7 @@ import io.netty.handler.codec.DecoderException; import io.netty.handler.codec.EncoderException; import java.io.IOException; import java.util.ArrayList; +import java.util.Arrays; import java.util.Iterator; import java.util.List; import java.util.Map; @@ -26,6 +27,77 @@ public class DataWatcher { private boolean f = true; private boolean g; + // Yatopia lithium start + private static final int DEFAULT_ENTRY_COUNT = 10, GROW_FACTOR = 8; + /** Mirrors the vanilla backing entries map. Each DataWatcher.Item can be accessed in this array through its ID. **/ + private DataWatcher.Item[] entriesArray = new DataWatcher.Item[DEFAULT_ENTRY_COUNT]; + + /** + * We redirect the call to add a tracked data to the internal map so we can add it to our new storage structure. This + * should only ever occur during entity initialization. Type-erasure is a bit of a pain here since we must redirect + * a calls to the generic Map interface. + */ + private Object onAddTrackedDataInsertMap(it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap> map, int keyRaw, DataWatcher.Item valueRaw) { + int k = keyRaw; + DataWatcher.Item v = (DataWatcher.Item) valueRaw; + + DataWatcher.Item[] storage = this.entriesArray; + + // Check if we need to grow the backing array to accommodate the new key range + if (storage.length <= k) { + // Grow the array to accommodate 8 entries after this one, but limit it to never be larger + // than 256 entries as per the vanilla limit + int newSize = Math.min(k + GROW_FACTOR, 256); + + this.entriesArray = storage = Arrays.copyOf(storage, newSize); + } + + // Update the storage + storage[k] = v; + + // Ensure that the vanilla backing storage is still updated appropriately + return this.entries.put(k, v); + } + + /** + * @reason Avoid integer boxing/unboxing and use our array-based storage + * @author JellySquid + */ + private DataWatcher.Item getEntry(DataWatcherObject data) { + try { + DataWatcher.Item[] array = this.entriesArray; + + int id = data.a(); + + // The vanilla implementation will simply return null if the tracker doesn't contain the specified entry. However, + // accessing an array with an invalid pointer will throw a OOB exception, where-as a HashMap would simply + // return null. We check this case (which should be free, even if so insignificant, as the subsequent bounds + // check will hopefully be eliminated) + if (id < 0 || id >= array.length) { + return null; + } + + // This cast can fail if trying to access a entry which doesn't belong to this tracker, as the ID could + // instead point to an entry of a different type. However, that is also vanilla behaviour. + // noinspection unchecked + return (DataWatcher.Item) array[id]; + } catch (Throwable cause) { + // Move to another method so this function can be in-lined better + throw onGetException(cause, data); + } + } + + private static ReportedException onGetException(Throwable cause, DataWatcherObject data) { + CrashReport report = CrashReport.a(cause, "Getting synced entity data"); + + CrashReportSystemDetails section = report.a("Synced entity data"); + section.a("Data ID", data); + + return new ReportedException(report); + } + + // Yatopia lithium end + public DataWatcher(Entity entity) { this.entity = entity; } @@ -90,7 +162,8 @@ public class DataWatcher { DataWatcher.Item datawatcher_item = new DataWatcher.Item<>(datawatcherobject, t0); // this.lock.writeLock().lock(); // Spigot - not required - this.entries.put(datawatcherobject.a(), datawatcher_item); + //this.entries.put(datawatcherobject.a(), datawatcher_item); + this.onAddTrackedDataInsertMap(this.entries, datawatcherobject.a(), datawatcher_item); // Yatopia lithium this.f = false; // this.lock.writeLock().unlock(); // Spigot - not required } @@ -121,7 +194,8 @@ public class DataWatcher { } public T get(DataWatcherObject datawatcherobject) { - return this.b(datawatcherobject).b(); + return getEntry(datawatcherobject).b(); // Yatopia + //return this.b(datawatcherobject).b(); } public void set(DataWatcherObject datawatcherobject, T t0) {