--- a/net/minecraft/world/level/chunk/PalettedContainer.java
+++ b/net/minecraft/world/level/chunk/PalettedContainer.java
@@ -27,26 +_,35 @@
 public class PalettedContainer<T> implements PaletteResize<T>, PalettedContainerRO<T> {
     private static final int MIN_PALETTE_BITS = 0;
     private final PaletteResize<T> dummyPaletteResize = (bits, objectAdded) -> 0;
-    private final IdMap<T> registry;
-    private volatile PalettedContainer.Data<T> data;
+    public final IdMap<T> registry;
+    private final T @org.jetbrains.annotations.Nullable [] presetValues; // Paper - Anti-Xray - Add preset values
+    public volatile PalettedContainer.Data<T> data; // Paper - optimise collisions - public
     private final PalettedContainer.Strategy strategy;
-    private final ThreadingDetector threadingDetector = new ThreadingDetector("PalettedContainer");
+    //private final ThreadingDetector threadingDetector = new ThreadingDetector("PalettedContainer"); // Paper - unused
 
     public void acquire() {
-        this.threadingDetector.checkAndLock();
+        // this.threadingDetector.checkAndLock(); // Paper - disable this - use proper synchronization
     }
 
     public void release() {
-        this.threadingDetector.checkAndUnlock();
+        // this.threadingDetector.checkAndUnlock(); // Paper - disable this - use proper synchronization
     }
 
+    // Paper start - Anti-Xray - Add preset values
+    @Deprecated @io.papermc.paper.annotation.DoNotUse
     public static <T> Codec<PalettedContainer<T>> codecRW(IdMap<T> registry, Codec<T> codec, PalettedContainer.Strategy strategy, T value) {
-        PalettedContainerRO.Unpacker<T, PalettedContainer<T>> unpacker = PalettedContainer::unpack;
+        return PalettedContainer.codecRW(registry, codec, strategy, value, null);
+    }
+    public static <T> Codec<PalettedContainer<T>> codecRW(IdMap<T> registry, Codec<T> codec, PalettedContainer.Strategy strategy, T value, T @org.jetbrains.annotations.Nullable [] presetValues) {
+        PalettedContainerRO.Unpacker<T, PalettedContainer<T>> unpacker = (idListx, paletteProviderx, serialized) -> {
+            return unpack(idListx, paletteProviderx, serialized, value, presetValues);
+        };
+        // Paper end
         return codec(registry, codec, strategy, value, unpacker);
     }
 
     public static <T> Codec<PalettedContainerRO<T>> codecRO(IdMap<T> registry, Codec<T> codec, PalettedContainer.Strategy strategy, T value) {
-        PalettedContainerRO.Unpacker<T, PalettedContainerRO<T>> unpacker = (registry1, strategy1, packedData) -> unpack(registry1, strategy1, packedData)
+        PalettedContainerRO.Unpacker<T, PalettedContainerRO<T>> unpacker = (registry1, strategy1, packedData) -> unpack(registry1, strategy1, packedData, value, null) // Paper - Anti-Xray - Add preset values
             .map(container -> (PalettedContainerRO<T>)container);
         return codec(registry, codec, strategy, value, unpacker);
     }
@@ -66,31 +_,100 @@
             );
     }
 
+    // Paper start - optimise palette reads
+    private void updateData(final PalettedContainer.Data<T> data) {
+        if (data != null) {
+            ((ca.spottedleaf.moonrise.patches.fast_palette.FastPaletteData<T>)(Object)data).moonrise$setPalette(
+                ((ca.spottedleaf.moonrise.patches.fast_palette.FastPalette<T>)data.palette).moonrise$getRawPalette((ca.spottedleaf.moonrise.patches.fast_palette.FastPaletteData<T>)(Object)data)
+            );
+        }
+    }
+
+    private T readPaletteSlow(final PalettedContainer.Data<T> data, final int paletteIdx) {
+        return data.palette.valueFor(paletteIdx);
+    }
+
+    private T readPalette(final PalettedContainer.Data<T> data, final int paletteIdx) {
+        final T[] palette = ((ca.spottedleaf.moonrise.patches.fast_palette.FastPaletteData<T>)(Object)data).moonrise$getPalette();
+        if (palette == null) {
+            return this.readPaletteSlow(data, paletteIdx);
+        }
+
+        final T ret = palette[paletteIdx];
+        if (ret == null) {
+            throw new IllegalArgumentException("Palette index out of bounds");
+        }
+        return ret;
+    }
+    // Paper end - optimise palette reads
+
+    // Paper start - Anti-Xray - Add preset values
+    @Deprecated @io.papermc.paper.annotation.DoNotUse
+    public PalettedContainer(IdMap<T> registry, PalettedContainer.Strategy strategy, PalettedContainer.Configuration<T> configuration, BitStorage storage, List<T> values) {
+        this(registry, strategy, configuration, storage, values, null, null);
+    }
     public PalettedContainer(
-        IdMap<T> registry, PalettedContainer.Strategy strategy, PalettedContainer.Configuration<T> configuration, BitStorage storage, List<T> values
+        IdMap<T> registry, PalettedContainer.Strategy strategy, PalettedContainer.Configuration<T> configuration, BitStorage storage, List<T> values, T defaultValue, T @org.jetbrains.annotations.Nullable [] presetValues
     ) {
+        this.presetValues = presetValues;
         this.registry = registry;
         this.strategy = strategy;
         this.data = new PalettedContainer.Data<>(configuration, storage, configuration.factory().create(configuration.bits(), registry, this, values));
+        if (presetValues != null && (configuration.factory() == PalettedContainer.Strategy.SINGLE_VALUE_PALETTE_FACTORY ? this.data.palette.valueFor(0) != defaultValue : configuration.factory() != PalettedContainer.Strategy.GLOBAL_PALETTE_FACTORY)) {
+            // In 1.18 Mojang unfortunately removed code that already handled possible resize operations on read from disk for us
+            // We readd this here but in a smarter way than it was before
+            int maxSize = 1 << configuration.bits();
+
+            for (T presetValue : presetValues) {
+                if (this.data.palette.getSize() >= maxSize) {
+                    java.util.Set<T> allValues = new java.util.HashSet<>(values);
+                    allValues.addAll(Arrays.asList(presetValues));
+                    int newBits = Mth.ceillog2(allValues.size());
+
+                    if (newBits > configuration.bits()) {
+                        this.onResize(newBits, null);
+                    }
+
+                    break;
+                }
+
+                this.data.palette.idFor(presetValue);
+            }
+        }
+        // Paper end
+        this.updateData(this.data); // Paper - optimise palette reads
     }
 
-    private PalettedContainer(IdMap<T> registry, PalettedContainer.Strategy strategy, PalettedContainer.Data<T> data) {
+    // Paper start - Anti-Xray - Add preset values
+    private PalettedContainer(IdMap<T> registry, PalettedContainer.Strategy strategy, PalettedContainer.Data<T> data, T @org.jetbrains.annotations.Nullable [] presetValues) {
+        this.presetValues = presetValues;
+        // Paper end - Anti-Xray
         this.registry = registry;
         this.strategy = strategy;
         this.data = data;
+        this.updateData(this.data); // Paper - optimise palette reads
     }
 
-    private PalettedContainer(PalettedContainer<T> other) {
+    private PalettedContainer(PalettedContainer<T> other, T @org.jetbrains.annotations.Nullable [] presetValues) { // Paper - Anti-Xray - Add preset values
+        this.presetValues = presetValues; // Paper - Anti-Xray - Add preset values
         this.registry = other.registry;
         this.strategy = other.strategy;
         this.data = other.data.copy(this);
     }
 
+    // Paper start - Anti-Xray - Add preset values
+    @Deprecated @io.papermc.paper.annotation.DoNotUse
     public PalettedContainer(IdMap<T> registry, T palette, PalettedContainer.Strategy strategy) {
+        this(registry, palette, strategy, null);
+    }
+    public PalettedContainer(IdMap<T> registry, T palette, PalettedContainer.Strategy strategy, T @org.jetbrains.annotations.Nullable [] presetValues) {
+        this.presetValues = presetValues;
+        // Paper end - Anti-Xray
         this.strategy = strategy;
         this.registry = registry;
         this.data = this.createOrReuseData(null, 0);
         this.data.palette.idFor(palette);
+        this.updateData(this.data); // Paper - optimise palette reads
     }
 
     private PalettedContainer.Data<T> createOrReuseData(@Nullable PalettedContainer.Data<T> data, int id) {
@@ -99,15 +_,35 @@
     }
 
     @Override
-    public int onResize(int bits, T objectAdded) {
+    public synchronized int onResize(int bits, T objectAdded) { // Paper - synchronize
         PalettedContainer.Data<T> data = this.data;
+        // Paper start - Anti-Xray - Add preset values
+        if (this.presetValues != null && objectAdded != null && data.configuration().factory() == PalettedContainer.Strategy.SINGLE_VALUE_PALETTE_FACTORY) {
+            int duplicates = 0;
+            List<T> presetValues = Arrays.asList(this.presetValues);
+            duplicates += presetValues.contains(objectAdded) ? 1 : 0;
+            duplicates += presetValues.contains(data.palette.valueFor(0)) ? 1 : 0;
+            bits = Mth.ceillog2((1 << this.strategy.calculateBitsForSerialization(this.registry, 1 << bits)) + presetValues.size() - duplicates);
+        }
+        // Paper end - Anti-Xray
         PalettedContainer.Data<T> data1 = this.createOrReuseData(data, bits);
         data1.copyFrom(data.palette, data.storage);
         this.data = data1;
-        return data1.palette.idFor(objectAdded);
-    }
+        // Paper start - Anti-Xray
+        this.addPresetValues();
+        this.updateData(this.data); // Paper - optimise palette reads
+        return objectAdded == null ? -1 : data1.palette.idFor(objectAdded);
+    }
+    private void addPresetValues() {
+        if (this.presetValues != null && this.data.configuration().factory() != PalettedContainer.Strategy.GLOBAL_PALETTE_FACTORY) {
+            for (T presetValue : this.presetValues) {
+                this.data.palette.idFor(presetValue);
+            }
+        }
+    }
+    // Paper end - Anti-Xray
 
-    public T getAndSet(int x, int y, int z, T state) {
+    public synchronized T getAndSet(int x, int y, int z, T state) { // Paper - synchronize
         this.acquire();
 
         Object var5;
@@ -125,12 +_,15 @@
     }
 
     private T getAndSet(int index, T state) {
-        int i = this.data.palette.idFor(state);
-        int andSet = this.data.storage.getAndSet(index, i);
-        return this.data.palette.valueFor(andSet);
+        // Paper start - optimise palette reads
+        final int paletteIdx = this.data.palette.idFor(state);
+        final PalettedContainer.Data<T> data = this.data;
+        final int prev = data.storage.getAndSet(index, paletteIdx);
+        return this.readPalette(data, prev);
+        // Paper end - optimise palette reads
     }
 
-    public void set(int x, int y, int z, T state) {
+    public synchronized void set(int x, int y, int z, T state) { // Paper - synchronize
         this.acquire();
 
         try {
@@ -150,9 +_,11 @@
         return this.get(this.strategy.getIndex(x, y, z));
     }
 
-    protected T get(int index) {
-        PalettedContainer.Data<T> data = this.data;
-        return data.palette.valueFor(data.storage.get(index));
+    public T get(int index) { // Paper - public
+        // Paper start - optimise palette reads
+        final PalettedContainer.Data<T> data = this.data;
+        return this.readPalette(data, data.storage.get(index));
+        // Paper end - optimise palette reads
     }
 
     @Override
@@ -163,7 +_,7 @@
         set.forEach(id -> consumer.accept(palette.valueFor(id)));
     }
 
-    public void read(FriendlyByteBuf buffer) {
+    public synchronized void read(FriendlyByteBuf buffer) { // Paper - synchronize
         this.acquire();
 
         try {
@@ -172,24 +_,36 @@
             data.palette.read(buffer);
             buffer.readLongArray(data.storage.getRaw());
             this.data = data;
+            this.addPresetValues(); // Paper - Anti-Xray - Add preset values (inefficient, but this isn't used by the server)
+            this.updateData(this.data); // Paper - optimise palette reads
         } finally {
             this.release();
         }
     }
 
+    // Paper start - Anti-Xray; Add chunk packet info
     @Override
+    @Deprecated @io.papermc.paper.annotation.DoNotUse
     public void write(FriendlyByteBuf buffer) {
+        this.write(buffer, null, 0);
+    }
+    @Override
+    public synchronized void write(FriendlyByteBuf buffer, @Nullable io.papermc.paper.antixray.ChunkPacketInfo<T> chunkPacketInfo, int chunkSectionIndex) { // Paper - Synchronize
         this.acquire();
 
         try {
-            this.data.write(buffer);
+            this.data.write(buffer, chunkPacketInfo, chunkSectionIndex);
+            if (chunkPacketInfo != null) {
+                chunkPacketInfo.setPresetValues(chunkSectionIndex, this.presetValues);
+            }
+            // Paper end - Anti-Xray
         } finally {
             this.release();
         }
     }
 
     private static <T> DataResult<PalettedContainer<T>> unpack(
-        IdMap<T> registry, PalettedContainer.Strategy strategy, PalettedContainerRO.PackedData<T> packedData
+        IdMap<T> registry, PalettedContainer.Strategy strategy, PalettedContainerRO.PackedData<T> packedData, T defaultValue, T @org.jetbrains.annotations.Nullable [] presetValues // Paper - Anti-Xray - Add preset values
     ) {
         List<T> list = packedData.paletteEntries();
         int size = strategy.size();
@@ -222,11 +_,11 @@
             }
         }
 
-        return DataResult.success(new PalettedContainer<>(registry, strategy, configuration, bitStorage, list));
+        return DataResult.success(new PalettedContainer<>(registry, strategy, configuration, bitStorage, list, defaultValue, presetValues)); // Paper - Anti-Xray - Add preset values
     }
 
     @Override
-    public PalettedContainerRO.PackedData<T> pack(IdMap<T> registry, PalettedContainer.Strategy strategy) {
+    public synchronized PalettedContainerRO.PackedData<T> pack(IdMap<T> registry, PalettedContainer.Strategy strategy) { // Paper - synchronize
         this.acquire();
 
         PalettedContainerRO.PackedData var12;
@@ -280,12 +_,12 @@
 
     @Override
     public PalettedContainer<T> copy() {
-        return new PalettedContainer<>(this);
+        return new PalettedContainer<>(this, this.presetValues); // Paper - Anti-Xray - Add preset values
     }
 
     @Override
     public PalettedContainer<T> recreate() {
-        return new PalettedContainer<>(this.registry, this.data.palette.valueFor(0), this.strategy);
+        return new PalettedContainer<>(this.registry, this.data.palette.valueFor(0), this.strategy, this.presetValues); // Paper - Anti-Xray - Add preset values
     }
 
     @Override
@@ -312,7 +_,44 @@
         void accept(T state, int count);
     }
 
-    record Data<T>(PalettedContainer.Configuration<T> configuration, BitStorage storage, Palette<T> palette) {
+    // Paper start - optimise palette reads
+    public static final class Data<T> implements ca.spottedleaf.moonrise.patches.fast_palette.FastPaletteData<T> {
+
+        private final PalettedContainer.Configuration<T> configuration;
+        private final BitStorage storage;
+        private final Palette<T> palette;
+
+        private T[] moonrise$palette;
+
+        public Data(final PalettedContainer.Configuration<T> configuration, final BitStorage storage, final Palette<T> palette) {
+            this.configuration = configuration;
+            this.storage = storage;
+            this.palette = palette;
+        }
+
+        public PalettedContainer.Configuration<T> configuration() {
+            return this.configuration;
+        }
+
+        public BitStorage storage() {
+            return this.storage;
+        }
+
+        public Palette<T> palette() {
+            return this.palette;
+        }
+
+        @Override
+        public final T[] moonrise$getPalette() {
+            return this.moonrise$palette;
+        }
+
+        @Override
+        public final void moonrise$setPalette(final T[] palette) {
+            this.moonrise$palette = palette;
+        }
+        // Paper end - optimise palette reads
+
         public void copyFrom(Palette<T> palette, BitStorage bitStorage) {
             for (int i = 0; i < bitStorage.getSize(); i++) {
                 T object = palette.valueFor(bitStorage.get(i));
@@ -324,9 +_,16 @@
             return 1 + this.palette.getSerializedSize() + VarInt.getByteSize(this.storage.getRaw().length) + this.storage.getRaw().length * 8;
         }
 
-        public void write(FriendlyByteBuf buffer) {
+        // Paper start - Anti-Xray - Add chunk packet info
+        public void write(FriendlyByteBuf buffer, @Nullable io.papermc.paper.antixray.ChunkPacketInfo<T> chunkPacketInfo, int chunkSectionIndex) {
             buffer.writeByte(this.storage.getBits());
             this.palette.write(buffer);
+            if (chunkPacketInfo != null) {
+                chunkPacketInfo.setBits(chunkSectionIndex, this.configuration.bits());
+                chunkPacketInfo.setPalette(chunkSectionIndex, this.palette);
+                chunkPacketInfo.setIndex(chunkSectionIndex, buffer.writerIndex() + VarInt.getByteSize(this.storage.getRaw().length));
+            }
+            // Paper end
             buffer.writeLongArray(this.storage.getRaw());
         }
 
