From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: JellySquid <jellysquid+atwork@protonmail.com>
Date: Tue, 4 Aug 2020 21:46:05 +0200
Subject: [PATCH] lithium DataTrackerMixin

Original code by JellySquid, licensed under GNU Lesser General Public License v3.0
you can find the original code on https://github.com/CaffeineMC/lithium-fabric/ (Yarn mappings)

Co-authored-by: tr7zw <tr7zw@live.de>

diff --git a/src/main/java/net/minecraft/network/syncher/DataWatcher.java b/src/main/java/net/minecraft/network/syncher/DataWatcher.java
index bb09b7be91b488ebc336b2b6f5deafc0ccab27a4..c03f1293848192cada5e3a6238f7a5c11cd4ab10 100644
--- a/src/main/java/net/minecraft/network/syncher/DataWatcher.java
+++ b/src/main/java/net/minecraft/network/syncher/DataWatcher.java
@@ -32,6 +32,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(Int2ObjectOpenHashMap<DataWatcher.Item<?>> 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 = java.util.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 <T> DataWatcher.Item<T> getEntry(DataWatcherObject<T> 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<T>) array[id];
+        } catch (Throwable cause) {
+            // Move to another method so this function can be in-lined better
+            throw onGetException(cause, data);
+        }
+    }
+
+    private static <T> ReportedException onGetException(Throwable cause, DataWatcherObject<T> 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;
     }
@@ -96,7 +167,8 @@ public class DataWatcher {
         DataWatcher.Item<T> 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
     }
@@ -127,7 +199,8 @@ public class DataWatcher {
     }
 
     public <T> T get(DataWatcherObject<T> datawatcherobject) {
-        return this.b(datawatcherobject).b();
+	    return getEntry(datawatcherobject).b(); // Yatopia
+        //return this.b(datawatcherobject).b();
     }
 
     public <T> void set(DataWatcherObject<T> datawatcherobject, T t0) {
