aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorJake Potrebic <[email protected]>2024-11-23 13:27:37 -0800
committerGitHub <[email protected]>2024-11-23 13:27:37 -0800
commiteef40b78c278df085b3c852be21e68a59680def4 (patch)
tree015ad6a88c25b69fb9267a65b239e729a3e634a8
parentaee6f7abe161976fb6cb0558aeda5aef49845787 (diff)
downloadPaper-eef40b78c278df085b3c852be21e68a59680def4.tar.gz
Paper-eef40b78c278df085b3c852be21e68a59680def4.zip
Configurable Entity Despawn Time (#11454)
* Configurable Entity Despawn Time Co-authored-by: Kevin Raneri <[email protected]> * Rebase * Rebase * rebase * throw exceptions for this map --------- Co-authored-by: Kevin Raneri <[email protected]>
-rw-r--r--patches/server/0005-Paper-config-files.patch532
-rw-r--r--patches/server/0022-Remap-reflection-calls-in-plugins-using-internals.patch10
-rw-r--r--patches/server/0928-Add-onboarding-message-for-initial-server-start.patch6
-rw-r--r--patches/server/1070-Configurable-Entity-Despawn-Time.patch39
4 files changed, 388 insertions, 199 deletions
diff --git a/patches/server/0005-Paper-config-files.patch b/patches/server/0005-Paper-config-files.patch
index 8115fc2d48..d5545fac61 100644
--- a/patches/server/0005-Paper-config-files.patch
+++ b/patches/server/0005-Paper-config-files.patch
@@ -121,10 +121,10 @@ index 0000000000000000000000000000000000000000..042478cf7ce150f1f1bc5cddd7fa40f8
+}
diff --git a/src/main/java/io/papermc/paper/configuration/Configurations.java b/src/main/java/io/papermc/paper/configuration/Configurations.java
new file mode 100644
-index 0000000000000000000000000000000000000000..d9502ba028a96f9cc846f9ed428bd8066b857ca3
+index 0000000000000000000000000000000000000000..007e01d329a31acf7f4ed4c6dc4de7ad54ccad04
--- /dev/null
+++ b/src/main/java/io/papermc/paper/configuration/Configurations.java
-@@ -0,0 +1,360 @@
+@@ -0,0 +1,358 @@
+package io.papermc.paper.configuration;
+
+import com.google.common.base.Preconditions;
@@ -132,34 +132,32 @@ index 0000000000000000000000000000000000000000..d9502ba028a96f9cc846f9ed428bd806
+import io.leangen.geantyref.TypeToken;
+import io.papermc.paper.configuration.constraint.Constraint;
+import io.papermc.paper.configuration.constraint.Constraints;
++import java.io.IOException;
++import java.lang.reflect.Type;
++import java.nio.file.AccessDeniedException;
++import java.nio.file.Files;
++import java.nio.file.Path;
++import java.util.HashMap;
++import java.util.Map;
++import java.util.NoSuchElementException;
++import java.util.Objects;
++import java.util.function.UnaryOperator;
+import net.minecraft.core.RegistryAccess;
+import net.minecraft.resources.ResourceLocation;
+import net.minecraft.server.level.ServerLevel;
+import net.minecraft.world.level.GameRules;
-+import org.checkerframework.checker.nullness.qual.Nullable;
+import org.jetbrains.annotations.MustBeInvokedByOverriders;
++import org.jspecify.annotations.Nullable;
+import org.slf4j.Logger;
+import org.spongepowered.configurate.CommentedConfigurationNode;
+import org.spongepowered.configurate.ConfigurateException;
+import org.spongepowered.configurate.ConfigurationNode;
+import org.spongepowered.configurate.ConfigurationOptions;
-+import org.spongepowered.configurate.NodePath;
+import org.spongepowered.configurate.objectmapping.ObjectMapper;
+import org.spongepowered.configurate.serialize.SerializationException;
+import org.spongepowered.configurate.util.CheckedFunction;
+import org.spongepowered.configurate.yaml.YamlConfigurationLoader;
+
-+import java.io.IOException;
-+import java.lang.reflect.Type;
-+import java.nio.file.AccessDeniedException;
-+import java.nio.file.Files;
-+import java.nio.file.Path;
-+import java.util.HashMap;
-+import java.util.Map;
-+import java.util.NoSuchElementException;
-+import java.util.Objects;
-+import java.util.function.UnaryOperator;
-+
+public abstract class Configurations<G, W> {
+
+ private static final Logger LOGGER = LogUtils.getClassLogger();
@@ -487,7 +485,7 @@ index 0000000000000000000000000000000000000000..d9502ba028a96f9cc846f9ed428bd806
+}
diff --git a/src/main/java/io/papermc/paper/configuration/GlobalConfiguration.java b/src/main/java/io/papermc/paper/configuration/GlobalConfiguration.java
new file mode 100644
-index 0000000000000000000000000000000000000000..f0d470d7770e119f734b9e72021c806d0ea8ecbd
+index 0000000000000000000000000000000000000000..0ea9eba1367858dfa5284524a8dd2f79daf6fc69
--- /dev/null
+++ b/src/main/java/io/papermc/paper/configuration/GlobalConfiguration.java
@@ -0,0 +1,330 @@
@@ -501,7 +499,7 @@ index 0000000000000000000000000000000000000000..f0d470d7770e119f734b9e72021c806d
+import net.kyori.adventure.text.format.NamedTextColor;
+import net.minecraft.network.protocol.Packet;
+import net.minecraft.network.protocol.game.ServerboundPlaceRecipePacket;
-+import org.checkerframework.checker.nullness.qual.Nullable;
++import org.jspecify.annotations.Nullable;
+import org.slf4j.Logger;
+import org.spongepowered.configurate.objectmapping.ConfigSerializable;
+import org.spongepowered.configurate.objectmapping.meta.Comment;
@@ -823,21 +821,20 @@ index 0000000000000000000000000000000000000000..f0d470d7770e119f734b9e72021c806d
+}
diff --git a/src/main/java/io/papermc/paper/configuration/NestedSetting.java b/src/main/java/io/papermc/paper/configuration/NestedSetting.java
new file mode 100644
-index 0000000000000000000000000000000000000000..69add4a7f1147015806bc9b63a8340d1893356c1
+index 0000000000000000000000000000000000000000..b8c42cc2624f325dc8725ebab68bbff0addb3855
--- /dev/null
+++ b/src/main/java/io/papermc/paper/configuration/NestedSetting.java
-@@ -0,0 +1,32 @@
+@@ -0,0 +1,31 @@
+package io.papermc.paper.configuration;
+
-+import org.checkerframework.checker.nullness.qual.Nullable;
-+import org.spongepowered.configurate.objectmapping.meta.NodeResolver;
-+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.lang.reflect.AnnotatedElement;
++import org.jspecify.annotations.Nullable;
++import org.spongepowered.configurate.objectmapping.meta.NodeResolver;
+
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
@@ -861,10 +858,10 @@ index 0000000000000000000000000000000000000000..69add4a7f1147015806bc9b63a8340d1
+}
diff --git a/src/main/java/io/papermc/paper/configuration/PaperConfigurations.java b/src/main/java/io/papermc/paper/configuration/PaperConfigurations.java
new file mode 100644
-index 0000000000000000000000000000000000000000..1029b6de6f36b08bf634b4056ef5701383f6f258
+index 0000000000000000000000000000000000000000..c5644d8d64f12073e39bc6ed79c8714f4560ff89
--- /dev/null
+++ b/src/main/java/io/papermc/paper/configuration/PaperConfigurations.java
-@@ -0,0 +1,467 @@
+@@ -0,0 +1,470 @@
+package io.papermc.paper.configuration;
+
+import com.google.common.base.Suppliers;
@@ -903,6 +900,8 @@ index 0000000000000000000000000000000000000000..1029b6de6f36b08bf634b4056ef57013
+import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap;
+import it.unimi.dsi.fastutil.objects.Reference2LongMap;
+import it.unimi.dsi.fastutil.objects.Reference2LongOpenHashMap;
++import it.unimi.dsi.fastutil.objects.Reference2ObjectMap;
++import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap;
+import java.io.File;
+import java.io.IOException;
+import java.lang.reflect.Type;
@@ -925,8 +924,8 @@ index 0000000000000000000000000000000000000000..1029b6de6f36b08bf634b4056ef57013
+import org.apache.commons.lang3.RandomStringUtils;
+import org.bukkit.configuration.ConfigurationSection;
+import org.bukkit.configuration.file.YamlConfiguration;
-+import org.checkerframework.checker.nullness.qual.Nullable;
+import org.jetbrains.annotations.VisibleForTesting;
++import org.jspecify.annotations.Nullable;
+import org.slf4j.Logger;
+import org.spigotmc.SpigotConfig;
+import org.spigotmc.SpigotWorldConfig;
@@ -1108,6 +1107,7 @@ index 0000000000000000000000000000000000000000..1029b6de6f36b08bf634b4056ef57013
+ .serializers(serializers -> serializers
+ .register(new TypeToken<Reference2IntMap<?>>() {}, new FastutilMapSerializer.SomethingToPrimitive<Reference2IntMap<?>>(Reference2IntOpenHashMap::new, Integer.TYPE))
+ .register(new TypeToken<Reference2LongMap<?>>() {}, new FastutilMapSerializer.SomethingToPrimitive<Reference2LongMap<?>>(Reference2LongOpenHashMap::new, Long.TYPE))
++ .register(new TypeToken<Reference2ObjectMap<?, ?>>() {}, new FastutilMapSerializer.SomethingToSomething<Reference2ObjectMap<?, ?>>(Reference2ObjectOpenHashMap::new))
+ .register(new TypeToken<Table<?, ?, ?>>() {}, new TableSerializer())
+ .register(DespawnRange.class, DespawnRange.SERIALIZER)
+ .register(StringRepresentableSerializer::isValidFor, new StringRepresentableSerializer())
@@ -1423,10 +1423,10 @@ index 0000000000000000000000000000000000000000..279b24c689b9979884b65df7eb1f0590
+}
diff --git a/src/main/java/io/papermc/paper/configuration/WorldConfiguration.java b/src/main/java/io/papermc/paper/configuration/WorldConfiguration.java
new file mode 100644
-index 0000000000000000000000000000000000000000..dad3fcc689ec806f985122a7cbd501a7d0fd0d36
+index 0000000000000000000000000000000000000000..b1c917d65076a3805e5b78cb946753f0c101e214
--- /dev/null
+++ b/src/main/java/io/papermc/paper/configuration/WorldConfiguration.java
-@@ -0,0 +1,581 @@
+@@ -0,0 +1,590 @@
+package io.papermc.paper.configuration;
+
+import com.google.common.collect.HashBasedTable;
@@ -1436,6 +1436,7 @@ index 0000000000000000000000000000000000000000..dad3fcc689ec806f985122a7cbd501a7
+import io.papermc.paper.configuration.legacy.RequiresSpigotInitialization;
+import io.papermc.paper.configuration.mapping.MergeMap;
+import io.papermc.paper.configuration.serializer.NbtPathSerializer;
++import io.papermc.paper.configuration.serializer.collections.MapSerializer;
+import io.papermc.paper.configuration.transformation.world.FeatureSeedsGeneration;
+import io.papermc.paper.configuration.type.BooleanOrDefault;
+import io.papermc.paper.configuration.type.DespawnRange;
@@ -1451,6 +1452,8 @@ index 0000000000000000000000000000000000000000..dad3fcc689ec806f985122a7cbd501a7
+import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap;
+import it.unimi.dsi.fastutil.objects.Reference2LongMap;
+import it.unimi.dsi.fastutil.objects.Reference2LongOpenHashMap;
++import it.unimi.dsi.fastutil.objects.Reference2ObjectMap;
++import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap;
+import java.util.Arrays;
+import java.util.IdentityHashMap;
+import java.util.List;
@@ -1628,6 +1631,12 @@ index 0000000000000000000000000000000000000000..dad3fcc689ec806f985122a7cbd501a7
+ }
+ }
+
++ @MapSerializer.ThrowExceptions
++ public Reference2ObjectMap<EntityType<?>, IntOr.Disabled> despawnTime = Util.make(new Reference2ObjectOpenHashMap<>(), map -> {
++ map.put(EntityType.SNOWBALL, IntOr.Disabled.DISABLED);
++ map.put(EntityType.LLAMA_SPIT, IntOr.Disabled.DISABLED);
++ });
++
+ @PostProcess
+ public void precomputeDespawnDistances() throws SerializationException {
+ for (Map.Entry<MobCategory, DespawnRangePair> entry : this.despawnRanges.entrySet()) {
@@ -2046,7 +2055,7 @@ index 0000000000000000000000000000000000000000..514be9a11e2ca368ea72dd2bac1b84bf
+}
diff --git a/src/main/java/io/papermc/paper/configuration/constraint/Constraints.java b/src/main/java/io/papermc/paper/configuration/constraint/Constraints.java
new file mode 100644
-index 0000000000000000000000000000000000000000..2d8c91007d5ebc051623bb308cf973bdad3f3273
+index 0000000000000000000000000000000000000000..9cab83a5b47b29d394bdf6e5c5f8e2c9952d9156
--- /dev/null
+++ b/src/main/java/io/papermc/paper/configuration/constraint/Constraints.java
@@ -0,0 +1,43 @@
@@ -2058,7 +2067,7 @@ index 0000000000000000000000000000000000000000..2d8c91007d5ebc051623bb308cf973bd
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.lang.reflect.Type;
-+import org.checkerframework.checker.nullness.qual.Nullable;
++import org.jspecify.annotations.Nullable;
+import org.spongepowered.configurate.objectmapping.meta.Constraint;
+import org.spongepowered.configurate.serialize.SerializationException;
+
@@ -2095,13 +2104,13 @@ index 0000000000000000000000000000000000000000..2d8c91007d5ebc051623bb308cf973bd
+}
diff --git a/src/main/java/io/papermc/paper/configuration/legacy/MaxEntityCollisionsInitializer.java b/src/main/java/io/papermc/paper/configuration/legacy/MaxEntityCollisionsInitializer.java
new file mode 100644
-index 0000000000000000000000000000000000000000..62b43280f59163f7910f79cc901b50d05cdd024e
+index 0000000000000000000000000000000000000000..ddef1ed3ff6fef52a70ee8bbf0b7607f6588ae6d
--- /dev/null
+++ b/src/main/java/io/papermc/paper/configuration/legacy/MaxEntityCollisionsInitializer.java
@@ -0,0 +1,29 @@
+package io.papermc.paper.configuration.legacy;
+
-+import org.checkerframework.checker.nullness.qual.Nullable;
++import org.jspecify.annotations.Nullable;
+import org.spigotmc.SpigotWorldConfig;
+import org.spongepowered.configurate.ConfigurationNode;
+import org.spongepowered.configurate.objectmapping.meta.NodeResolver;
@@ -2130,18 +2139,14 @@ index 0000000000000000000000000000000000000000..62b43280f59163f7910f79cc901b50d0
+}
diff --git a/src/main/java/io/papermc/paper/configuration/legacy/RequiresSpigotInitialization.java b/src/main/java/io/papermc/paper/configuration/legacy/RequiresSpigotInitialization.java
new file mode 100644
-index 0000000000000000000000000000000000000000..611bdbcef3d52e09179aa8b1677ab1e198c70b02
+index 0000000000000000000000000000000000000000..81c64a2ffad4bcd69f0012f04567a7d15f2d6dd5
--- /dev/null
+++ b/src/main/java/io/papermc/paper/configuration/legacy/RequiresSpigotInitialization.java
-@@ -0,0 +1,51 @@
+@@ -0,0 +1,48 @@
+package io.papermc.paper.configuration.legacy;
+
+import com.google.common.collect.HashBasedTable;
+import com.google.common.collect.Table;
-+import org.checkerframework.checker.nullness.qual.Nullable;
-+import org.spigotmc.SpigotWorldConfig;
-+import org.spongepowered.configurate.objectmapping.meta.NodeResolver;
-+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
@@ -2149,8 +2154,9 @@ index 0000000000000000000000000000000000000000..611bdbcef3d52e09179aa8b1677ab1e1
+import java.lang.annotation.Target;
+import java.lang.reflect.AnnotatedElement;
+import java.lang.reflect.Constructor;
-+import java.util.Map;
-+import java.util.concurrent.ConcurrentHashMap;
++import org.jspecify.annotations.Nullable;
++import org.spigotmc.SpigotWorldConfig;
++import org.spongepowered.configurate.objectmapping.meta.NodeResolver;
+
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
@@ -2220,21 +2226,19 @@ index 0000000000000000000000000000000000000000..fe5cc1c097f8d8c135e6ead6f458426b
+}
diff --git a/src/main/java/io/papermc/paper/configuration/mapping/InnerClassFieldDiscoverer.java b/src/main/java/io/papermc/paper/configuration/mapping/InnerClassFieldDiscoverer.java
new file mode 100644
-index 0000000000000000000000000000000000000000..8f23276796037d048eb114952891a01a40971b3e
+index 0000000000000000000000000000000000000000..05339a176083af667c16f77d76dc1878dafce3f0
--- /dev/null
+++ b/src/main/java/io/papermc/paper/configuration/mapping/InnerClassFieldDiscoverer.java
-@@ -0,0 +1,54 @@
+@@ -0,0 +1,52 @@
+package io.papermc.paper.configuration.mapping;
+
+import io.papermc.paper.configuration.ConfigurationPart;
-+import io.papermc.paper.configuration.Configurations;
-+import io.papermc.paper.configuration.PaperConfigurations;
+import io.papermc.paper.configuration.WorldConfiguration;
+import java.lang.reflect.AnnotatedType;
+import java.lang.reflect.Field;
+import java.util.Collections;
+import java.util.Map;
-+import org.checkerframework.checker.nullness.qual.Nullable;
++import org.jspecify.annotations.Nullable;
+import org.spongepowered.configurate.objectmapping.FieldDiscoverer;
+import org.spongepowered.configurate.serialize.SerializationException;
+
@@ -2280,10 +2284,10 @@ index 0000000000000000000000000000000000000000..8f23276796037d048eb114952891a01a
+}
diff --git a/src/main/java/io/papermc/paper/configuration/mapping/InnerClassInstanceFactory.java b/src/main/java/io/papermc/paper/configuration/mapping/InnerClassInstanceFactory.java
new file mode 100644
-index 0000000000000000000000000000000000000000..cec678ae24a7d99a46fa672be907f4c28fe4da96
+index 0000000000000000000000000000000000000000..25e3152c3307175da734a1cad7f7a4166e233021
--- /dev/null
+++ b/src/main/java/io/papermc/paper/configuration/mapping/InnerClassInstanceFactory.java
-@@ -0,0 +1,65 @@
+@@ -0,0 +1,64 @@
+package io.papermc.paper.configuration.mapping;
+
+import java.lang.reflect.AnnotatedType;
@@ -2291,7 +2295,6 @@ index 0000000000000000000000000000000000000000..cec678ae24a7d99a46fa672be907f4c2
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Objects;
-+import org.checkerframework.checker.nullness.qual.Nullable;
+import org.spongepowered.configurate.objectmapping.FieldDiscoverer;
+import org.spongepowered.configurate.serialize.SerializationException;
+
@@ -2323,7 +2326,7 @@ index 0000000000000000000000000000000000000000..cec678ae24a7d99a46fa672be907f4c2
+ Map.Entry<Field, Object> entry = iter.next();
+ if (entry.getKey().isAnnotationPresent(MergeMap.class) && Map.class.isAssignableFrom(entry.getKey().getType()) && intermediate.get(entry.getKey()) instanceof Map<?, ?> map) {
+ iter.remove();
-+ @Nullable Map<Object, Object> existingMap = (Map<Object, Object>) entry.getKey().get(instance);
++ Map<Object, Object> existingMap = (Map<Object, Object>) entry.getKey().get(instance);
+ if (existingMap != null) {
+ existingMap.putAll(map);
+ } else {
@@ -2351,7 +2354,7 @@ index 0000000000000000000000000000000000000000..cec678ae24a7d99a46fa672be907f4c2
+}
diff --git a/src/main/java/io/papermc/paper/configuration/mapping/InnerClassInstanceSupplier.java b/src/main/java/io/papermc/paper/configuration/mapping/InnerClassInstanceSupplier.java
new file mode 100644
-index 0000000000000000000000000000000000000000..8d8bc050441c02cf65dfcb6400978363d6b8ef10
+index 0000000000000000000000000000000000000000..3778c47f563fd0011659234fc8394e3d59325782
--- /dev/null
+++ b/src/main/java/io/papermc/paper/configuration/mapping/InnerClassInstanceSupplier.java
@@ -0,0 +1,72 @@
@@ -2364,7 +2367,7 @@ index 0000000000000000000000000000000000000000..8d8bc050441c02cf65dfcb6400978363
+import java.util.HashMap;
+import java.util.Map;
+import java.util.function.Supplier;
-+import org.checkerframework.checker.nullness.qual.Nullable;
++import org.jspecify.annotations.Nullable;
+import org.spongepowered.configurate.serialize.SerializationException;
+import org.spongepowered.configurate.util.CheckedFunction;
+import org.spongepowered.configurate.util.CheckedSupplier;
@@ -2400,7 +2403,7 @@ index 0000000000000000000000000000000000000000..8d8bc050441c02cf65dfcb6400978363
+ final Constructor<?> constructor;
+ final CheckedSupplier<Object, ReflectiveOperationException> instanceSupplier;
+ if (type.getEnclosingClass() != null && !Modifier.isStatic(type.getModifiers())) {
-+ final @Nullable Object instance = this.instanceMap.get(type.getEnclosingClass());
++ final Object instance = this.instanceMap.get(type.getEnclosingClass());
+ if (instance == null) {
+ throw new SerializationException("Cannot create a new instance of an inner class " + type.getName() + " without an instance of its enclosing class " + type.getEnclosingClass().getName());
+ }
@@ -2453,18 +2456,26 @@ index 0000000000000000000000000000000000000000..471b161ac51900672434c6608595bb73
+@Retention(RetentionPolicy.RUNTIME)
+public @interface MergeMap {
+}
+diff --git a/src/main/java/io/papermc/paper/configuration/mapping/package-info.java b/src/main/java/io/papermc/paper/configuration/mapping/package-info.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..454e6ec7ebf9c54a38d6ba3a73e7c197c67c2c00
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/configuration/mapping/package-info.java
+@@ -0,0 +1,4 @@
++@NullMarked
++package io.papermc.paper.configuration.mapping;
++
++import org.jspecify.annotations.NullMarked;
diff --git a/src/main/java/io/papermc/paper/configuration/package-info.java b/src/main/java/io/papermc/paper/configuration/package-info.java
new file mode 100644
-index 0000000000000000000000000000000000000000..4e3bcd7c478096384fcc643d48771ab94318deb3
+index 0000000000000000000000000000000000000000..11bf7bb357305cf90c201444be08dc71c14b7505
--- /dev/null
+++ b/src/main/java/io/papermc/paper/configuration/package-info.java
-@@ -0,0 +1,5 @@
-+@DefaultQualifier(NonNull.class)
+@@ -0,0 +1,4 @@
++@NullMarked
+package io.papermc.paper.configuration;
+
-+import org.checkerframework.checker.nullness.qual.NonNull;
-+import org.checkerframework.framework.qual.DefaultQualifier;
-\ No newline at end of file
++import org.jspecify.annotations.NullMarked;
diff --git a/src/main/java/io/papermc/paper/configuration/serializer/ComponentSerializer.java b/src/main/java/io/papermc/paper/configuration/serializer/ComponentSerializer.java
new file mode 100644
index 0000000000000000000000000000000000000000..9c339ef178ebc3b0251095f320e4a7a3656d3521
@@ -2538,24 +2549,23 @@ index 0000000000000000000000000000000000000000..27c0679d376bb31ab52131dfea74b3b5
+}
diff --git a/src/main/java/io/papermc/paper/configuration/serializer/EnumValueSerializer.java b/src/main/java/io/papermc/paper/configuration/serializer/EnumValueSerializer.java
new file mode 100644
-index 0000000000000000000000000000000000000000..8535c748a5e355362e77e6c5103e11c4c318a138
+index 0000000000000000000000000000000000000000..d24d1480e3ee7e5004c2dcbe826823aa427f787a
--- /dev/null
+++ b/src/main/java/io/papermc/paper/configuration/serializer/EnumValueSerializer.java
-@@ -0,0 +1,50 @@
+@@ -0,0 +1,49 @@
+package io.papermc.paper.configuration.serializer;
+
+import com.mojang.logging.LogUtils;
+import io.leangen.geantyref.TypeToken;
-+import org.checkerframework.checker.nullness.qual.Nullable;
-+import org.slf4j.Logger;
-+import org.spongepowered.configurate.serialize.ScalarSerializer;
-+import org.spongepowered.configurate.serialize.SerializationException;
-+import org.spongepowered.configurate.util.EnumLookup;
-+
+import java.lang.reflect.Type;
+import java.util.Arrays;
+import java.util.List;
+import java.util.function.Predicate;
++import org.jspecify.annotations.Nullable;
++import org.slf4j.Logger;
++import org.spongepowered.configurate.serialize.ScalarSerializer;
++import org.spongepowered.configurate.serialize.SerializationException;
++import org.spongepowered.configurate.util.EnumLookup;
+
+import static io.leangen.geantyref.GenericTypeReflector.erase;
+
@@ -2575,14 +2585,14 @@ index 0000000000000000000000000000000000000000..8535c748a5e355362e77e6c5103e11c4
+ public @Nullable Enum<?> deserialize(final Type type, final Object obj) throws SerializationException {
+ final String enumConstant = obj.toString();
+ final Class<? extends Enum> typeClass = erase(type).asSubclass(Enum.class);
-+ @Nullable Enum<?> ret = EnumLookup.lookupEnum(typeClass, enumConstant);
++ Enum<?> ret = EnumLookup.lookupEnum(typeClass, enumConstant);
+ if (ret == null) {
+ ret = EnumLookup.lookupEnum(typeClass, enumConstant.replace("-", "_"));
+ }
+ if (ret == null) {
+ boolean longer = typeClass.getEnumConstants().length > 10;
+ List<String> options = Arrays.stream(typeClass.getEnumConstants()).limit(10L).map(Enum::name).toList();
-+ LOGGER.error("Invalid enum constant provided, expected one of [" + String.join(", " ,options) + (longer ? ", ..." : "") + "], but got " + enumConstant);
++ LOGGER.error("Invalid enum constant provided, expected one of [{}{}], but got {}", String.join(", ", options), longer ? ", ..." : "", enumConstant);
+ }
+ return ret;
+ }
@@ -2652,10 +2662,10 @@ index 0000000000000000000000000000000000000000..b44b2dc28f619594e302417848e95c00
+}
diff --git a/src/main/java/io/papermc/paper/configuration/serializer/PacketClassSerializer.java b/src/main/java/io/papermc/paper/configuration/serializer/PacketClassSerializer.java
new file mode 100644
-index 0000000000000000000000000000000000000000..893ad5e7c2d32ccd64962d95d146bbd317c28ab8
+index 0000000000000000000000000000000000000000..b61935052154e76b1b8cb49868c96c52f34a41d1
--- /dev/null
+++ b/src/main/java/io/papermc/paper/configuration/serializer/PacketClassSerializer.java
-@@ -0,0 +1,86 @@
+@@ -0,0 +1,85 @@
+package io.papermc.paper.configuration.serializer;
+
+import com.google.common.collect.BiMap;
@@ -2664,16 +2674,15 @@ index 0000000000000000000000000000000000000000..893ad5e7c2d32ccd64962d95d146bbd3
+import io.leangen.geantyref.TypeToken;
+import io.papermc.paper.configuration.serializer.collections.MapSerializer;
+import io.papermc.paper.util.ObfHelper;
-+import net.minecraft.network.protocol.Packet;
-+import org.checkerframework.checker.nullness.qual.Nullable;
-+import org.slf4j.Logger;
-+import org.spongepowered.configurate.serialize.ScalarSerializer;
-+import org.spongepowered.configurate.serialize.SerializationException;
-+
+import java.lang.reflect.Type;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Predicate;
++import net.minecraft.network.protocol.Packet;
++import org.jspecify.annotations.Nullable;
++import org.slf4j.Logger;
++import org.spongepowered.configurate.serialize.ScalarSerializer;
++import org.spongepowered.configurate.serialize.SerializationException;
+
+@SuppressWarnings("Convert2Diamond")
+public final class PacketClassSerializer extends ScalarSerializer<Class<? extends Packet<?>>> implements MapSerializer.WriteBack {
@@ -2703,14 +2712,14 @@ index 0000000000000000000000000000000000000000..893ad5e7c2d32ccd64962d95d146bbd3
+ @SuppressWarnings("unchecked")
+ @Override
+ public Class<? extends Packet<?>> deserialize(final Type type, final Object obj) throws SerializationException {
-+ @Nullable Class<?> packetClass = null;
++ Class<?> packetClass = null;
+ for (final String subpackage : SUBPACKAGES) {
+ final String fullClassName = "net.minecraft.network.protocol." + subpackage + "." + obj;
+ try {
+ packetClass = Class.forName(fullClassName);
+ break;
+ } catch (final ClassNotFoundException ex) {
-+ final @Nullable String spigotClassName = MOJANG_TO_OBF.get(fullClassName);
++ final String spigotClassName = MOJANG_TO_OBF.get(fullClassName);
+ if (spigotClassName != null) {
+ try {
+ packetClass = Class.forName(spigotClassName);
@@ -2744,23 +2753,22 @@ index 0000000000000000000000000000000000000000..893ad5e7c2d32ccd64962d95d146bbd3
+}
diff --git a/src/main/java/io/papermc/paper/configuration/serializer/StringRepresentableSerializer.java b/src/main/java/io/papermc/paper/configuration/serializer/StringRepresentableSerializer.java
new file mode 100644
-index 0000000000000000000000000000000000000000..7fc0905fc6b8f5df762b4cea573f935dc00b8bc1
+index 0000000000000000000000000000000000000000..629012cb8ea8d8d81f99033794226bef19ee6c80
--- /dev/null
+++ b/src/main/java/io/papermc/paper/configuration/serializer/StringRepresentableSerializer.java
-@@ -0,0 +1,52 @@
+@@ -0,0 +1,51 @@
+package io.papermc.paper.configuration.serializer;
+
-+import net.minecraft.util.StringRepresentable;
-+import net.minecraft.world.entity.MobCategory;
-+import org.checkerframework.checker.nullness.qual.Nullable;
-+import org.spongepowered.configurate.serialize.ScalarSerializer;
-+import org.spongepowered.configurate.serialize.SerializationException;
-+
+import java.lang.reflect.Type;
+import java.util.Collections;
+import java.util.Map;
+import java.util.function.Function;
+import java.util.function.Predicate;
++import net.minecraft.util.StringRepresentable;
++import net.minecraft.world.entity.MobCategory;
++import org.jspecify.annotations.Nullable;
++import org.spongepowered.configurate.serialize.ScalarSerializer;
++import org.spongepowered.configurate.serialize.SerializationException;
+
+public final class StringRepresentableSerializer extends ScalarSerializer<StringRepresentable> {
+ private static final Map<Type, Function<String, StringRepresentable>> TYPES = Collections.synchronizedMap(Map.ofEntries(
@@ -2802,52 +2810,61 @@ index 0000000000000000000000000000000000000000..7fc0905fc6b8f5df762b4cea573f935d
+}
diff --git a/src/main/java/io/papermc/paper/configuration/serializer/collections/FastutilMapSerializer.java b/src/main/java/io/papermc/paper/configuration/serializer/collections/FastutilMapSerializer.java
new file mode 100644
-index 0000000000000000000000000000000000000000..4af710e144b70933d750c22edfe484c18e4a3540
+index 0000000000000000000000000000000000000000..68ed5ad7b6f28a9fdda35e25c12a13a2619e1449
--- /dev/null
+++ b/src/main/java/io/papermc/paper/configuration/serializer/collections/FastutilMapSerializer.java
-@@ -0,0 +1,69 @@
+@@ -0,0 +1,91 @@
+package io.papermc.paper.configuration.serializer.collections;
+
+import io.leangen.geantyref.GenericTypeReflector;
+import io.leangen.geantyref.TypeFactory;
-+import org.checkerframework.checker.nullness.qual.Nullable;
-+import org.spongepowered.configurate.ConfigurationNode;
-+import org.spongepowered.configurate.serialize.SerializationException;
-+import org.spongepowered.configurate.serialize.TypeSerializer;
-+
++import java.lang.annotation.Annotation;
++import java.lang.reflect.AnnotatedParameterizedType;
++import java.lang.reflect.AnnotatedType;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.Collections;
+import java.util.Map;
+import java.util.function.Function;
++import org.jspecify.annotations.Nullable;
++import org.spongepowered.configurate.ConfigurationNode;
++import org.spongepowered.configurate.serialize.SerializationException;
++import org.spongepowered.configurate.serialize.TypeSerializer;
+
+@SuppressWarnings("rawtypes")
-+public abstract class FastutilMapSerializer<M extends Map<?, ?>> implements TypeSerializer<M> {
-+ private final Function<Map, ? extends M> factory;
++public abstract class FastutilMapSerializer<M extends Map<?, ?>> implements TypeSerializer.Annotated<M> {
+
-+ protected FastutilMapSerializer(final Function<Map, ? extends M> factory) {
++ private final Function<? super Map, ? extends M> factory;
++
++ protected FastutilMapSerializer(final Function<? super Map, ? extends M> factory) {
+ this.factory = factory;
+ }
+
+ @Override
-+ public M deserialize(final Type type, final ConfigurationNode node) throws SerializationException {
-+ @Nullable final Map map = (Map) node.get(this.createBaseMapType((ParameterizedType) type));
++ public M deserialize(final AnnotatedType annotatedType, final ConfigurationNode node) throws SerializationException {
++ final Map map = (Map) node.get(this.createAnnotatedMapType((AnnotatedParameterizedType) annotatedType));
+ return this.factory.apply(map == null ? Collections.emptyMap() : map);
+ }
+
+ @Override
-+ public void serialize(final Type type, @Nullable final M obj, final ConfigurationNode node) throws SerializationException {
++ public void serialize(final AnnotatedType annotatedType, final @Nullable M obj, final ConfigurationNode node) throws SerializationException {
+ if (obj == null || obj.isEmpty()) {
+ node.raw(null);
+ } else {
-+ final Type baseMapType = this.createBaseMapType((ParameterizedType) type);
++ final AnnotatedType baseMapType = this.createAnnotatedMapType((AnnotatedParameterizedType) annotatedType);
+ node.set(baseMapType, obj);
+ }
+ }
+
++ private AnnotatedType createAnnotatedMapType(final AnnotatedParameterizedType type) {
++ final Type baseType = this.createBaseMapType((ParameterizedType) type.getType());
++ return GenericTypeReflector.annotate(baseType, type.getAnnotations());
++ }
++
+ protected abstract Type createBaseMapType(final ParameterizedType type);
+
+ public static final class SomethingToPrimitive<M extends Map<?, ?>> extends FastutilMapSerializer<M> {
++
+ private final Type primitiveType;
+
+ public SomethingToPrimitive(final Function<Map, ? extends M> factory, final Type primitiveType) {
@@ -2862,6 +2879,7 @@ index 0000000000000000000000000000000000000000..4af710e144b70933d750c22edfe484c1
+ }
+
+ public static final class PrimitiveToSomething<M extends Map<?, ?>> extends FastutilMapSerializer<M> {
++
+ private final Type primitiveType;
+
+ public PrimitiveToSomething(final Function<Map, ? extends M> factory, final Type primitiveType) {
@@ -2874,26 +2892,32 @@ index 0000000000000000000000000000000000000000..4af710e144b70933d750c22edfe484c1
+ return TypeFactory.parameterizedClass(Map.class, GenericTypeReflector.box(this.primitiveType), type.getActualTypeArguments()[0]);
+ }
+ }
++
++ public static final class SomethingToSomething<M extends Map<?, ?>> extends FastutilMapSerializer<M> {
++
++ public SomethingToSomething(final Function<? super Map, ? extends M> factory) {
++ super(factory);
++ }
++
++ @Override
++ protected Type createBaseMapType(final ParameterizedType type) {
++ return TypeFactory.parameterizedClass(Map.class, type.getActualTypeArguments()[0], type.getActualTypeArguments()[1]);
++ }
++ }
+}
diff --git a/src/main/java/io/papermc/paper/configuration/serializer/collections/MapSerializer.java b/src/main/java/io/papermc/paper/configuration/serializer/collections/MapSerializer.java
new file mode 100644
-index 0000000000000000000000000000000000000000..e7e997796ec47c742cc1f98e61db75a4231e6f98
+index 0000000000000000000000000000000000000000..6bb8304b9d98bf2ba53274a3808e607e08f0787f
--- /dev/null
+++ b/src/main/java/io/papermc/paper/configuration/serializer/collections/MapSerializer.java
-@@ -0,0 +1,162 @@
+@@ -0,0 +1,182 @@
+package io.papermc.paper.configuration.serializer.collections;
+
+import com.mojang.logging.LogUtils;
+import io.leangen.geantyref.TypeToken;
-+import org.checkerframework.checker.nullness.qual.Nullable;
-+import org.slf4j.Logger;
-+import org.spongepowered.configurate.BasicConfigurationNode;
-+import org.spongepowered.configurate.ConfigurationNode;
-+import org.spongepowered.configurate.ConfigurationOptions;
-+import org.spongepowered.configurate.NodePath;
-+import org.spongepowered.configurate.serialize.SerializationException;
-+import org.spongepowered.configurate.serialize.TypeSerializer;
-+
++import java.lang.annotation.Retention;
++import java.lang.annotation.RetentionPolicy;
++import java.lang.reflect.AnnotatedType;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.Collections;
@@ -2901,27 +2925,45 @@ index 0000000000000000000000000000000000000000..e7e997796ec47c742cc1f98e61db75a4
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Set;
++import org.jspecify.annotations.Nullable;
++import org.slf4j.Logger;
++import org.spongepowered.configurate.BasicConfigurationNode;
++import org.spongepowered.configurate.ConfigurationNode;
++import org.spongepowered.configurate.ConfigurationOptions;
++import org.spongepowered.configurate.NodePath;
++import org.spongepowered.configurate.serialize.SerializationException;
++import org.spongepowered.configurate.serialize.TypeSerializer;
++import org.spongepowered.configurate.serialize.TypeSerializerCollection;
+
+import static java.util.Objects.requireNonNull;
+
+/**
+ * Map serializer that does not throw errors on individual entry serialization failures.
+ */
-+public class MapSerializer implements TypeSerializer<Map<?, ?>> {
++public class MapSerializer implements TypeSerializer.Annotated<Map<?, ?>> {
+
+ public static final TypeToken<Map<?, ?>> TYPE = new TypeToken<Map<?, ?>>() {};
+
+ private static final Logger LOGGER = LogUtils.getClassLogger();
+
+ private final boolean clearInvalids;
++ private final TypeSerializer<Map<?, ?>> fallback;
+
+ public MapSerializer(boolean clearInvalids) {
+ this.clearInvalids = clearInvalids;
++ this.fallback = requireNonNull(TypeSerializerCollection.defaults().get(TYPE), "Could not find default Map<?, ?> serializer");
+ }
+
++ @Retention(RetentionPolicy.RUNTIME)
++ public @interface ThrowExceptions {}
++
+ @Override
-+ public Map<?, ?> deserialize(Type type, ConfigurationNode node) throws SerializationException {
++ public Map<?, ?> deserialize(AnnotatedType annotatedType, ConfigurationNode node) throws SerializationException {
++ if (annotatedType.isAnnotationPresent(ThrowExceptions.class)) {
++ return this.fallback.deserialize(annotatedType, node);
++ }
+ final Map<Object, Object> map = new LinkedHashMap<>();
++ final Type type = annotatedType.getType();
+ if (node.isMap()) {
+ if (!(type instanceof ParameterizedType parameterizedType)) {
+ throw new SerializationException(type, "Raw types are not supported for collections");
@@ -2975,7 +3017,12 @@ index 0000000000000000000000000000000000000000..e7e997796ec47c742cc1f98e61db75a4
+ }
+
+ @Override
-+ public void serialize(Type type, @Nullable Map<?, ?> obj, ConfigurationNode node) throws SerializationException {
++ public void serialize(AnnotatedType annotatedType, @Nullable Map<?, ?> obj, ConfigurationNode node) throws SerializationException {
++ if (annotatedType.isAnnotationPresent(ThrowExceptions.class)) {
++ this.fallback.serialize(annotatedType, obj, node);
++ return;
++ }
++ final Type type = annotatedType.getType();
+ if (!(type instanceof ParameterizedType parameterizedType)) {
+ throw new SerializationException(type, "Raw types are not supported for collections");
+ }
@@ -3036,7 +3083,10 @@ index 0000000000000000000000000000000000000000..e7e997796ec47c742cc1f98e61db75a4
+ }
+
+ @Override
-+ public @Nullable Map<?, ?> emptyValue(Type specificType, ConfigurationOptions options) {
++ public @Nullable Map<?, ?> emptyValue(AnnotatedType specificType, ConfigurationOptions options) {
++ if (specificType.isAnnotationPresent(ThrowExceptions.class)) {
++ return this.fallback.emptyValue(specificType, options);
++ }
+ return new LinkedHashMap<>();
+ }
+
@@ -3045,28 +3095,27 @@ index 0000000000000000000000000000000000000000..e7e997796ec47c742cc1f98e61db75a4
+}
diff --git a/src/main/java/io/papermc/paper/configuration/serializer/collections/TableSerializer.java b/src/main/java/io/papermc/paper/configuration/serializer/collections/TableSerializer.java
new file mode 100644
-index 0000000000000000000000000000000000000000..36ca88b677e1b55b41c52750948d5b6de7ecd007
+index 0000000000000000000000000000000000000000..c02b2aa2565645d64c2709310d2aba9e32fd536d
--- /dev/null
+++ b/src/main/java/io/papermc/paper/configuration/serializer/collections/TableSerializer.java
-@@ -0,0 +1,89 @@
+@@ -0,0 +1,88 @@
+package io.papermc.paper.configuration.serializer.collections;
+
+import com.google.common.collect.HashBasedTable;
+import com.google.common.collect.ImmutableTable;
+import com.google.common.collect.Table;
+import io.leangen.geantyref.TypeFactory;
-+import org.checkerframework.checker.nullness.qual.Nullable;
++import java.lang.reflect.ParameterizedType;
++import java.lang.reflect.Type;
++import java.util.Map;
++import java.util.Objects;
++import org.jspecify.annotations.Nullable;
+import org.spongepowered.configurate.BasicConfigurationNode;
+import org.spongepowered.configurate.ConfigurationNode;
+import org.spongepowered.configurate.ConfigurationOptions;
+import org.spongepowered.configurate.serialize.SerializationException;
+import org.spongepowered.configurate.serialize.TypeSerializer;
+
-+import java.lang.reflect.ParameterizedType;
-+import java.lang.reflect.Type;
-+import java.util.Map;
-+import java.util.Objects;
-+
+public class TableSerializer implements TypeSerializer<Table<?, ?, ?>> {
+ private static final int ROW_TYPE_ARGUMENT_INDEX = 0;
+ private static final int COLUMN_TYPE_ARGUMENT_INDEX = 1;
@@ -3087,13 +3136,13 @@ index 0000000000000000000000000000000000000000..36ca88b677e1b55b41c52750948d5b6d
+ final Type columnType = type.getActualTypeArguments()[COLUMN_TYPE_ARGUMENT_INDEX];
+ final Type valueType = type.getActualTypeArguments()[VALUE_TYPE_ARGUMENT_INDEX];
+
-+ final @Nullable TypeSerializer<R> rowKeySerializer = (TypeSerializer<R>) node.options().serializers().get(rowType);
++ final TypeSerializer<R> rowKeySerializer = (TypeSerializer<R>) node.options().serializers().get(rowType);
+ if (rowKeySerializer == null) {
+ throw new SerializationException("Could not find serializer for table row type " + rowType);
+ }
+
+ final Type mapType = TypeFactory.parameterizedClass(Map.class, columnType, valueType);
-+ final @Nullable TypeSerializer<Map<C, V>> columnValueSerializer = (TypeSerializer<Map<C, V>>) node.options().serializers().get(mapType);
++ final TypeSerializer<Map<C, V>> columnValueSerializer = (TypeSerializer<Map<C, V>>) node.options().serializers().get(mapType);
+ if (columnValueSerializer == null) {
+ throw new SerializationException("Could not find serializer for table column-value map " + type);
+ }
@@ -3108,7 +3157,7 @@ index 0000000000000000000000000000000000000000..36ca88b677e1b55b41c52750948d5b6d
+ }
+
+ @Override
-+ public void serialize(final Type type, @Nullable final Table<?, ?, ?> table, final ConfigurationNode node) throws SerializationException {
++ public void serialize(final Type type, final @Nullable Table<?, ?, ?> table, final ConfigurationNode node) throws SerializationException {
+ if (table != null) {
+ this.serialize0(table, (ParameterizedType) type, node);
+ }
@@ -3120,7 +3169,7 @@ index 0000000000000000000000000000000000000000..36ca88b677e1b55b41c52750948d5b6d
+ final Type columnType = type.getActualTypeArguments()[COLUMN_TYPE_ARGUMENT_INDEX];
+ final Type valueType = type.getActualTypeArguments()[VALUE_TYPE_ARGUMENT_INDEX];
+
-+ final @Nullable TypeSerializer rowKeySerializer = node.options().serializers().get(rowType);
++ final TypeSerializer rowKeySerializer = node.options().serializers().get(rowType);
+ if (rowKeySerializer == null) {
+ throw new SerializationException("Could not find a serializer for table row type " + rowType);
+ }
@@ -3138,12 +3187,32 @@ index 0000000000000000000000000000000000000000..36ca88b677e1b55b41c52750948d5b6d
+ return ImmutableTable.of();
+ }
+}
+diff --git a/src/main/java/io/papermc/paper/configuration/serializer/collections/package-info.java b/src/main/java/io/papermc/paper/configuration/serializer/collections/package-info.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..78899dc1357626f993119efabf29c52396e15b2b
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/configuration/serializer/collections/package-info.java
+@@ -0,0 +1,4 @@
++@NullMarked
++package io.papermc.paper.configuration.serializer.collections;
++
++import org.jspecify.annotations.NullMarked;
+diff --git a/src/main/java/io/papermc/paper/configuration/serializer/package-info.java b/src/main/java/io/papermc/paper/configuration/serializer/package-info.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..757de4562e038a2d62f276e18e86ecab7c7332bf
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/configuration/serializer/package-info.java
+@@ -0,0 +1,4 @@
++@NullMarked
++package io.papermc.paper.configuration.serializer;
++
++import org.jspecify.annotations.NullMarked;
diff --git a/src/main/java/io/papermc/paper/configuration/serializer/registry/RegistryEntrySerializer.java b/src/main/java/io/papermc/paper/configuration/serializer/registry/RegistryEntrySerializer.java
new file mode 100644
-index 0000000000000000000000000000000000000000..cb0de1a639578320fd38177a915bfa5d1e9a73bd
+index 0000000000000000000000000000000000000000..6b600a133f0689a70d1619c684c5e3c0f313b42d
--- /dev/null
+++ b/src/main/java/io/papermc/paper/configuration/serializer/registry/RegistryEntrySerializer.java
-@@ -0,0 +1,64 @@
+@@ -0,0 +1,63 @@
+package io.papermc.paper.configuration.serializer.registry;
+
+import io.leangen.geantyref.TypeToken;
@@ -3153,7 +3222,6 @@ index 0000000000000000000000000000000000000000..cb0de1a639578320fd38177a915bfa5d
+import net.minecraft.core.RegistryAccess;
+import net.minecraft.resources.ResourceKey;
+import net.minecraft.resources.ResourceLocation;
-+import org.checkerframework.checker.nullness.qual.Nullable;
+import org.spongepowered.configurate.serialize.ScalarSerializer;
+import org.spongepowered.configurate.serialize.SerializationException;
+
@@ -3201,7 +3269,7 @@ index 0000000000000000000000000000000000000000..cb0de1a639578320fd38177a915bfa5d
+ }
+
+ private ResourceKey<R> deserializeKey(final Object input) throws SerializationException {
-+ final @Nullable ResourceLocation key = ResourceLocation.tryParse(input.toString());
++ final ResourceLocation key = ResourceLocation.tryParse(input.toString());
+ if (key == null) {
+ throw new SerializationException("Could not create a key from " + input);
+ }
@@ -3289,6 +3357,16 @@ index 0000000000000000000000000000000000000000..6831b7b72c5e1f79eff36019ca2ff565
+ return this.registry().getResourceKey(value).orElseThrow();
+ }
+}
+diff --git a/src/main/java/io/papermc/paper/configuration/serializer/registry/package-info.java b/src/main/java/io/papermc/paper/configuration/serializer/registry/package-info.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..5185a314b555dffb6dea355dc9e2b005e5808843
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/configuration/serializer/registry/package-info.java
+@@ -0,0 +1,4 @@
++@NullMarked
++package io.papermc.paper.configuration.serializer.registry;
++
++import org.jspecify.annotations.NullMarked;
diff --git a/src/main/java/io/papermc/paper/configuration/transformation/Transformations.java b/src/main/java/io/papermc/paper/configuration/transformation/Transformations.java
new file mode 100644
index 0000000000000000000000000000000000000000..96e8d03bd4a4d43633a94bb251054610ac07315a
@@ -3338,14 +3416,15 @@ index 0000000000000000000000000000000000000000..96e8d03bd4a4d43633a94bb251054610
+}
diff --git a/src/main/java/io/papermc/paper/configuration/transformation/global/LegacyPaperConfig.java b/src/main/java/io/papermc/paper/configuration/transformation/global/LegacyPaperConfig.java
new file mode 100644
-index 0000000000000000000000000000000000000000..ef0e834c164b0ccc1a61b349348e6799733d66d9
+index 0000000000000000000000000000000000000000..7b984ec88a72a5e2581cb717d1f228b01cffbcda
--- /dev/null
+++ b/src/main/java/io/papermc/paper/configuration/transformation/global/LegacyPaperConfig.java
-@@ -0,0 +1,223 @@
+@@ -0,0 +1,221 @@
+package io.papermc.paper.configuration.transformation.global;
+
+import com.mojang.logging.LogUtils;
+import io.papermc.paper.configuration.Configuration;
++import java.util.function.Predicate;
+import net.kyori.adventure.text.Component;
+import net.kyori.adventure.text.format.NamedTextColor;
+import net.kyori.adventure.text.minimessage.MiniMessage;
@@ -3353,14 +3432,11 @@ index 0000000000000000000000000000000000000000..ef0e834c164b0ccc1a61b349348e6799
+import net.minecraft.network.protocol.game.ServerboundPlaceRecipePacket;
+import org.bukkit.ChatColor;
+import org.bukkit.configuration.file.YamlConfiguration;
-+import org.checkerframework.checker.nullness.qual.Nullable;
+import org.slf4j.Logger;
+import org.spongepowered.configurate.ConfigurationNode;
+import org.spongepowered.configurate.transformation.ConfigurationTransformation;
+import org.spongepowered.configurate.transformation.TransformAction;
+
-+import java.util.function.Predicate;
-+
+import static org.spongepowered.configurate.NodePath.path;
+
+public final class LegacyPaperConfig {
@@ -3425,7 +3501,7 @@ index 0000000000000000000000000000000000000000..ef0e834c164b0ccc1a61b349348e6799
+ .addAction(path("allow-perm-block-break-exploits"), (path, value) -> new Object[]{"settings", "unsupported-settings", "allow-permanent-block-break-exploits"})
+ .addAction(path("settings", "unsupported-settings", "allow-tnt-duplication"), TransformAction.rename("allow-piston-duplication"))
+ .addAction(path("settings", "save-player-data"), (path, value) -> {
-+ final @Nullable Object val = value.raw();
++ final Object val = value.raw();
+ if (val instanceof Boolean bool) {
+ spigotConfiguration.set("players.disable-saving", !bool);
+ }
@@ -3433,7 +3509,7 @@ index 0000000000000000000000000000000000000000..ef0e834c164b0ccc1a61b349348e6799
+ return null;
+ })
+ .addAction(path("settings", "log-named-entity-deaths"), (path, value) -> {
-+ final @Nullable Object val = value.raw();
++ final Object val = value.raw();
+ if (val instanceof Boolean bool && !bool) {
+ spigotConfiguration.set("settings.log-named-deaths", false);
+ }
@@ -3461,7 +3537,7 @@ index 0000000000000000000000000000000000000000..ef0e834c164b0ccc1a61b349348e6799
+ .addAction(path("packet-limiter", "limits", "all"), (path, value) -> new Object[]{"packet-limiter", "all-packets"})
+ .addAction(path("packet-limiter", "limits"), (path, value) -> new Object[]{"packet-limiter", "overrides"})
+ .addAction(path("packet-limiter", "overrides", ConfigurationTransformation.WILDCARD_OBJECT), (path, value) -> {
-+ final @Nullable Object keyValue = value.key();
++ final Object keyValue = value.key();
+ if (keyValue != null && keyValue.toString().equals("PacketPlayInAutoRecipe")) { // add special cast to handle the default for moj-mapped servers that upgrade the config
+ return path.with(path.size() - 1, ServerboundPlaceRecipePacket.class.getSimpleName()).array();
+ }
@@ -3520,7 +3596,7 @@ index 0000000000000000000000000000000000000000..ef0e834c164b0ccc1a61b349348e6799
+ }
+ private static void miniMessageWithTranslatable(final ConfigurationTransformation.Builder builder, final Predicate<String> englishCheck, final Component component, final String... strPath) {
+ builder.addAction(path((Object[]) strPath), (path, value) -> {
-+ final @Nullable Object val = value.raw();
++ final Object val = value.raw();
+ if (val != null) {
+ final String strVal = val.toString();
+ if (!englishCheck.test(strVal)) {
@@ -3535,7 +3611,7 @@ index 0000000000000000000000000000000000000000..ef0e834c164b0ccc1a61b349348e6799
+
+ private static void miniMessage(final ConfigurationTransformation.Builder builder, final String... strPath) {
+ builder.addAction(path((Object[]) strPath), (path, value) -> {
-+ final @Nullable Object val = value.raw();
++ final Object val = value.raw();
+ if (val != null) {
+ value.set(miniMessage(val.toString()));
+ }
@@ -3565,9 +3641,19 @@ index 0000000000000000000000000000000000000000..ef0e834c164b0ccc1a61b349348e6799
+ });
+ }
+}
+diff --git a/src/main/java/io/papermc/paper/configuration/transformation/global/package-info.java b/src/main/java/io/papermc/paper/configuration/transformation/global/package-info.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..241101a25821d3e506308ef9d7edb36aecd76232
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/configuration/transformation/global/package-info.java
+@@ -0,0 +1,4 @@
++@NullMarked
++package io.papermc.paper.configuration.transformation.global;
++
++import org.jspecify.annotations.NullMarked;
diff --git a/src/main/java/io/papermc/paper/configuration/transformation/global/versioned/V29_LogIPs.java b/src/main/java/io/papermc/paper/configuration/transformation/global/versioned/V29_LogIPs.java
new file mode 100644
-index 0000000000000000000000000000000000000000..66073f7a6a96405348cc4044ad1e6922158b13ba
+index 0000000000000000000000000000000000000000..bf5de965d26a59f7b4ba5ade3cdab35c37f42be2
--- /dev/null
+++ b/src/main/java/io/papermc/paper/configuration/transformation/global/versioned/V29_LogIPs.java
@@ -0,0 +1,44 @@
@@ -3576,7 +3662,7 @@ index 0000000000000000000000000000000000000000..66073f7a6a96405348cc4044ad1e6922
+import java.util.Properties;
+import net.minecraft.server.MinecraftServer;
+import net.minecraft.server.dedicated.DedicatedServer;
-+import org.checkerframework.checker.nullness.qual.Nullable;
++import org.jspecify.annotations.Nullable;
+import org.spongepowered.configurate.ConfigurateException;
+import org.spongepowered.configurate.ConfigurationNode;
+import org.spongepowered.configurate.NodePath;
@@ -3615,9 +3701,29 @@ index 0000000000000000000000000000000000000000..66073f7a6a96405348cc4044ad1e6922
+ }
+
+}
+diff --git a/src/main/java/io/papermc/paper/configuration/transformation/global/versioned/package-info.java b/src/main/java/io/papermc/paper/configuration/transformation/global/versioned/package-info.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..d9f90a01e88fb402b796a354bb3419193566b4b5
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/configuration/transformation/global/versioned/package-info.java
+@@ -0,0 +1,4 @@
++@NullMarked
++package io.papermc.paper.configuration.transformation.global.versioned;
++
++import org.jspecify.annotations.NullMarked;
+diff --git a/src/main/java/io/papermc/paper/configuration/transformation/package-info.java b/src/main/java/io/papermc/paper/configuration/transformation/package-info.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..7a1220c80c3eb4d365dd317958bdca0a6c8321aa
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/configuration/transformation/package-info.java
+@@ -0,0 +1,4 @@
++@NullMarked
++package io.papermc.paper.configuration.transformation;
++
++import org.jspecify.annotations.NullMarked;
diff --git a/src/main/java/io/papermc/paper/configuration/transformation/world/FeatureSeedsGeneration.java b/src/main/java/io/papermc/paper/configuration/transformation/world/FeatureSeedsGeneration.java
new file mode 100644
-index 0000000000000000000000000000000000000000..cb1f5f65c098470dc8553b015d0f0f29f28ed956
+index 0000000000000000000000000000000000000000..d670865dc9bf75ccc5309222e8af7fc10325c5fb
--- /dev/null
+++ b/src/main/java/io/papermc/paper/configuration/transformation/world/FeatureSeedsGeneration.java
@@ -0,0 +1,71 @@
@@ -3633,7 +3739,7 @@ index 0000000000000000000000000000000000000000..cb1f5f65c098470dc8553b015d0f0f29
+import net.minecraft.resources.ResourceLocation;
+import net.minecraft.server.MinecraftServer;
+import net.minecraft.world.level.levelgen.feature.ConfiguredFeature;
-+import org.checkerframework.checker.nullness.qual.Nullable;
++import org.jspecify.annotations.Nullable;
+import org.slf4j.Logger;
+import org.spongepowered.configurate.ConfigurateException;
+import org.spongepowered.configurate.ConfigurationNode;
@@ -3694,14 +3800,19 @@ index 0000000000000000000000000000000000000000..cb1f5f65c098470dc8553b015d0f0f29
+}
diff --git a/src/main/java/io/papermc/paper/configuration/transformation/world/LegacyPaperWorldConfig.java b/src/main/java/io/papermc/paper/configuration/transformation/world/LegacyPaperWorldConfig.java
new file mode 100644
-index 0000000000000000000000000000000000000000..77e530830dc8ebc861b2e70f787f9b71524a54d2
+index 0000000000000000000000000000000000000000..7af8f613ac63f04f01e373ec80747336f744baa4
--- /dev/null
+++ b/src/main/java/io/papermc/paper/configuration/transformation/world/LegacyPaperWorldConfig.java
-@@ -0,0 +1,322 @@
+@@ -0,0 +1,320 @@
+package io.papermc.paper.configuration.transformation.world;
+
+import io.papermc.paper.configuration.Configuration;
+import io.papermc.paper.configuration.WorldConfiguration;
++import java.util.HashMap;
++import java.util.List;
++import java.util.Locale;
++import java.util.Map;
++import java.util.Optional;
+import net.minecraft.core.Holder;
+import net.minecraft.core.registries.BuiltInRegistries;
+import net.minecraft.core.registries.Registries;
@@ -3710,16 +3821,9 @@ index 0000000000000000000000000000000000000000..77e530830dc8ebc861b2e70f787f9b71
+import net.minecraft.world.entity.MobCategory;
+import net.minecraft.world.item.Item;
+import org.bukkit.Material;
-+import org.checkerframework.checker.nullness.qual.Nullable;
+import org.spongepowered.configurate.transformation.ConfigurationTransformation;
+import org.spongepowered.configurate.transformation.TransformAction;
+
-+import java.util.HashMap;
-+import java.util.List;
-+import java.util.Locale;
-+import java.util.Map;
-+import java.util.Optional;
-+
+import static io.papermc.paper.configuration.transformation.Transformations.moveFromRoot;
+import static io.papermc.paper.configuration.transformation.Transformations.moveFromRootAndRename;
+import static org.spongepowered.configurate.NodePath.path;
@@ -3758,14 +3862,14 @@ index 0000000000000000000000000000000000000000..77e530830dc8ebc861b2e70f787f9b71
+ }).build())
+ .addVersion(19, ConfigurationTransformation.builder()
+ .addAction(path("anti-xray", "hidden-blocks"), (path, value) -> {
-+ @Nullable final List<String> hiddenBlocks = value.getList(String.class);
++ final List<String> hiddenBlocks = value.getList(String.class);
+ if (hiddenBlocks != null) {
+ hiddenBlocks.remove("lit_redstone_ore");
+ }
+ return null;
+ })
+ .addAction(path("anti-xray", "replacement-blocks"), (path, value) -> {
-+ @Nullable final List<String> replacementBlocks = value.getList(String.class);
++ final List<String> replacementBlocks = value.getList(String.class);
+ if (replacementBlocks != null) {
+ final int index = replacementBlocks.indexOf("planks");
+ if (index != -1) {
@@ -3838,9 +3942,9 @@ index 0000000000000000000000000000000000000000..77e530830dc8ebc861b2e70f787f9b71
+ value.childrenMap().forEach((key, node) -> {
+ String itemName = key.toString();
+ final Optional<Holder.Reference<Item>> itemHolder = BuiltInRegistries.ITEM.get(ResourceKey.create(Registries.ITEM, ResourceLocation.parse(itemName.toLowerCase(Locale.ROOT))));
-+ final @Nullable String item;
++ final String item;
+ if (itemHolder.isEmpty()) {
-+ final @Nullable Material bukkitMat = Material.matchMaterial(itemName);
++ final Material bukkitMat = Material.matchMaterial(itemName);
+ item = bukkitMat != null ? bukkitMat.getKey().getKey() : null;
+ } else {
+ item = itemHolder.get().unwrapKey().orElseThrow().location().getPath();
@@ -3976,7 +4080,7 @@ index 0000000000000000000000000000000000000000..77e530830dc8ebc861b2e70f787f9b71
+
+ builder.addAction(path("feature-seeds", ConfigurationTransformation.WILDCARD_OBJECT), (path, value) -> {
+ final String key = path.array()[path.size() - 1].toString();
-+ if (!key.equals("generate-random-seeds-for-all")) {
++ if (!"generate-random-seeds-for-all".equals(key)) {
+ return new Object[]{"feature-seeds", "features", key};
+ }
+ return null;
@@ -3994,7 +4098,7 @@ index 0000000000000000000000000000000000000000..77e530830dc8ebc861b2e70f787f9b71
+ });
+
+ builder.addAction(path("redstone-implementation"), (path, value) -> {
-+ if (value.require(String.class).equalsIgnoreCase("alternate-current")) {
++ if ("alternate-current".equalsIgnoreCase(value.require(String.class))) {
+ value.set("alternate_current");
+ }
+ return new Object[]{"misc", "redstone-implementation"};
@@ -4020,16 +4124,26 @@ index 0000000000000000000000000000000000000000..77e530830dc8ebc861b2e70f787f9b71
+ moveFromRootAndRename(builder, path("game-mechanics", oldKey), newKey, parents);
+ }
+}
+diff --git a/src/main/java/io/papermc/paper/configuration/transformation/world/package-info.java b/src/main/java/io/papermc/paper/configuration/transformation/world/package-info.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..0db4d187f780a0cf90c9c2936d1f33415d004137
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/configuration/transformation/world/package-info.java
+@@ -0,0 +1,4 @@
++@NullMarked
++package io.papermc.paper.configuration.transformation.world;
++
++import org.jspecify.annotations.NullMarked;
diff --git a/src/main/java/io/papermc/paper/configuration/transformation/world/versioned/V29_ZeroWorldHeight.java b/src/main/java/io/papermc/paper/configuration/transformation/world/versioned/V29_ZeroWorldHeight.java
new file mode 100644
-index 0000000000000000000000000000000000000000..6e481d509d091e65a4909d79014ac94ea63c8455
+index 0000000000000000000000000000000000000000..a1e8ce5407f2c5f188b2ce2d768512d3d42ad64b
--- /dev/null
+++ b/src/main/java/io/papermc/paper/configuration/transformation/world/versioned/V29_ZeroWorldHeight.java
@@ -0,0 +1,49 @@
+package io.papermc.paper.configuration.transformation.world.versioned;
+
+import io.papermc.paper.configuration.type.number.IntOr;
-+import org.checkerframework.checker.nullness.qual.Nullable;
++import org.jspecify.annotations.Nullable;
+import org.spongepowered.configurate.ConfigurateException;
+import org.spongepowered.configurate.ConfigurationNode;
+import org.spongepowered.configurate.NodePath;
@@ -4108,7 +4222,7 @@ index 0000000000000000000000000000000000000000..d08b65234192d5b639cead675114f64b
+}
diff --git a/src/main/java/io/papermc/paper/configuration/transformation/world/versioned/V31_SpawnLoadedRangeToGameRule.java b/src/main/java/io/papermc/paper/configuration/transformation/world/versioned/V31_SpawnLoadedRangeToGameRule.java
new file mode 100644
-index 0000000000000000000000000000000000000000..d872b1948df52759fed9c3d892aed6abfdfc8068
+index 0000000000000000000000000000000000000000..cb05980256f20df7b3c30e33eaa2c3185f2a38f8
--- /dev/null
+++ b/src/main/java/io/papermc/paper/configuration/transformation/world/versioned/V31_SpawnLoadedRangeToGameRule.java
@@ -0,0 +1,55 @@
@@ -4116,7 +4230,7 @@ index 0000000000000000000000000000000000000000..d872b1948df52759fed9c3d892aed6ab
+
+import io.papermc.paper.configuration.Configurations;
+import net.minecraft.world.level.GameRules;
-+import org.checkerframework.checker.nullness.qual.Nullable;
++import org.jspecify.annotations.Nullable;
+import org.spongepowered.configurate.ConfigurationNode;
+import org.spongepowered.configurate.NodePath;
+import org.spongepowered.configurate.transformation.ConfigurationTransformation;
@@ -4167,22 +4281,31 @@ index 0000000000000000000000000000000000000000..d872b1948df52759fed9c3d892aed6ab
+ }
+ }
+}
+diff --git a/src/main/java/io/papermc/paper/configuration/transformation/world/versioned/package-info.java b/src/main/java/io/papermc/paper/configuration/transformation/world/versioned/package-info.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..087aa63ae612aaabf4c161c56c78d47ef5b591d3
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/configuration/transformation/world/versioned/package-info.java
+@@ -0,0 +1,4 @@
++@NullMarked
++package io.papermc.paper.configuration.transformation.world.versioned;
++
++import org.jspecify.annotations.NullMarked;
diff --git a/src/main/java/io/papermc/paper/configuration/type/BooleanOrDefault.java b/src/main/java/io/papermc/paper/configuration/type/BooleanOrDefault.java
new file mode 100644
-index 0000000000000000000000000000000000000000..a3eaa47cfcfc4fd2a607f9b375230fada35620d3
+index 0000000000000000000000000000000000000000..1e73f51b7f6d06a1e86b150b001f90dd179b8ec8
--- /dev/null
+++ b/src/main/java/io/papermc/paper/configuration/type/BooleanOrDefault.java
-@@ -0,0 +1,53 @@
+@@ -0,0 +1,52 @@
+package io.papermc.paper.configuration.type;
+
-+import org.apache.commons.lang3.BooleanUtils;
-+import org.checkerframework.checker.nullness.qual.Nullable;
-+import org.spongepowered.configurate.serialize.ScalarSerializer;
-+import org.spongepowered.configurate.serialize.SerializationException;
-+
+import java.lang.reflect.Type;
+import java.util.Locale;
+import java.util.function.Predicate;
++import org.apache.commons.lang3.BooleanUtils;
++import org.jspecify.annotations.Nullable;
++import org.spongepowered.configurate.serialize.ScalarSerializer;
++import org.spongepowered.configurate.serialize.SerializationException;
+
+public record BooleanOrDefault(@Nullable Boolean value) {
+ private static final String DEFAULT_VALUE = "default";
@@ -4217,7 +4340,7 @@ index 0000000000000000000000000000000000000000..a3eaa47cfcfc4fd2a607f9b375230fad
+
+ @Override
+ protected Object serialize(BooleanOrDefault item, Predicate<Class<?>> typeSupported) {
-+ final @Nullable Boolean value = item.value;
++ final Boolean value = item.value;
+ if (value != null) {
+ return value.toString();
+ } else {
@@ -4228,7 +4351,7 @@ index 0000000000000000000000000000000000000000..a3eaa47cfcfc4fd2a607f9b375230fad
+}
diff --git a/src/main/java/io/papermc/paper/configuration/type/DespawnRange.java b/src/main/java/io/papermc/paper/configuration/type/DespawnRange.java
new file mode 100644
-index 0000000000000000000000000000000000000000..8a792a000e924b3ddc572edc788598811e9ef71c
+index 0000000000000000000000000000000000000000..7779edcbbce36d7da177a92807dac73fbe24c9fa
--- /dev/null
+++ b/src/main/java/io/papermc/paper/configuration/type/DespawnRange.java
@@ -0,0 +1,109 @@
@@ -4236,7 +4359,7 @@ index 0000000000000000000000000000000000000000..8a792a000e924b3ddc572edc78859881
+
+import io.papermc.paper.configuration.type.number.IntOr;
+import java.lang.reflect.Type;
-+import org.checkerframework.checker.nullness.qual.Nullable;
++import org.jspecify.annotations.Nullable;
+import org.spongepowered.configurate.ConfigurationNode;
+import org.spongepowered.configurate.serialize.SerializationException;
+import org.spongepowered.configurate.serialize.TypeSerializer;
@@ -4343,26 +4466,25 @@ index 0000000000000000000000000000000000000000..8a792a000e924b3ddc572edc78859881
+}
diff --git a/src/main/java/io/papermc/paper/configuration/type/Duration.java b/src/main/java/io/papermc/paper/configuration/type/Duration.java
new file mode 100644
-index 0000000000000000000000000000000000000000..422ccb0b332b3e94be228b9b94f379467d6461a5
+index 0000000000000000000000000000000000000000..ad1c77388da868b61d99dd8d7ab272bf6f7142a9
--- /dev/null
+++ b/src/main/java/io/papermc/paper/configuration/type/Duration.java
-@@ -0,0 +1,97 @@
+@@ -0,0 +1,96 @@
+package io.papermc.paper.configuration.type;
+
-+import org.checkerframework.checker.nullness.qual.Nullable;
-+import org.spongepowered.configurate.serialize.ScalarSerializer;
-+import org.spongepowered.configurate.serialize.SerializationException;
-+
+import java.lang.reflect.Type;
+import java.util.Objects;
+import java.util.function.Predicate;
+import java.util.regex.Pattern;
++import org.jspecify.annotations.Nullable;
++import org.spongepowered.configurate.serialize.ScalarSerializer;
++import org.spongepowered.configurate.serialize.SerializationException;
+
+public final class Duration {
+
+ private static final Pattern SPACE = Pattern.compile(" ");
+ private static final Pattern NOT_NUMERIC = Pattern.compile("[^-\\d.]");
-+ public static final Serializer SERIALIZER = new Serializer();
++ public static final ScalarSerializer<Duration> SERIALIZER = new Serializer();
+
+ private final long seconds;
+ private final String value;
@@ -4746,23 +4868,21 @@ index 0000000000000000000000000000000000000000..a3a1d398d783c37914fb6d646e11361a
+}
diff --git a/src/main/java/io/papermc/paper/configuration/type/fallback/FallbackValueSerializer.java b/src/main/java/io/papermc/paper/configuration/type/fallback/FallbackValueSerializer.java
new file mode 100644
-index 0000000000000000000000000000000000000000..8d0fcd038e12c70a3a5aaf2669452589d9055255
+index 0000000000000000000000000000000000000000..8def3c63b146905df287779cbe2502ff89ecd4bd
--- /dev/null
+++ b/src/main/java/io/papermc/paper/configuration/type/fallback/FallbackValueSerializer.java
-@@ -0,0 +1,55 @@
+@@ -0,0 +1,53 @@
+package io.papermc.paper.configuration.type.fallback;
+
-+import net.minecraft.server.MinecraftServer;
-+import org.checkerframework.checker.nullness.qual.Nullable;
-+import org.spigotmc.SpigotWorldConfig;
-+import org.spongepowered.configurate.serialize.ScalarSerializer;
-+import org.spongepowered.configurate.serialize.SerializationException;
-+
+import java.lang.reflect.Type;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.function.Predicate;
+import java.util.function.Supplier;
++import net.minecraft.server.MinecraftServer;
++import org.spigotmc.SpigotWorldConfig;
++import org.spongepowered.configurate.serialize.ScalarSerializer;
++import org.spongepowered.configurate.serialize.SerializationException;
+
+import static io.leangen.geantyref.GenericTypeReflector.erase;
+
@@ -4789,7 +4909,7 @@ index 0000000000000000000000000000000000000000..8d0fcd038e12c70a3a5aaf2669452589
+
+ @Override
+ public FallbackValue deserialize(Type type, Object obj) throws SerializationException {
-+ final @Nullable FallbackCreator<?> creator = REGISTRY.get(erase(type));
++ final FallbackCreator<?> creator = REGISTRY.get(erase(type));
+ if (creator == null) {
+ throw new SerializationException(type + " does not have a FallbackCreator registered");
+ }
@@ -4821,6 +4941,16 @@ index 0000000000000000000000000000000000000000..70cc7b45e7355f6c8476a74a070f1266
+ return value < 0 ? OptionalInt.empty() : OptionalInt.of(value);
+ }
+}
+diff --git a/src/main/java/io/papermc/paper/configuration/type/fallback/package-info.java b/src/main/java/io/papermc/paper/configuration/type/fallback/package-info.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..7daf9e0ec8f35b68373d4f025ec2366ab110e22e
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/configuration/type/fallback/package-info.java
+@@ -0,0 +1,4 @@
++@NullMarked
++package io.papermc.paper.configuration.type.fallback;
++
++import org.jspecify.annotations.NullMarked;
diff --git a/src/main/java/io/papermc/paper/configuration/type/number/BelowZeroToEmpty.java b/src/main/java/io/papermc/paper/configuration/type/number/BelowZeroToEmpty.java
new file mode 100644
index 0000000000000000000000000000000000000000..31068170086aeac51a2adb952b19672e875ba528
@@ -5073,6 +5203,26 @@ index 0000000000000000000000000000000000000000..614aba60bb07946a144650fd3aedb316
+
+ protected abstract boolean belowZero(O value);
+}
+diff --git a/src/main/java/io/papermc/paper/configuration/type/number/package-info.java b/src/main/java/io/papermc/paper/configuration/type/number/package-info.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..a1e8c3a3922aea672bc75c810496718ea3acfe0a
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/configuration/type/number/package-info.java
+@@ -0,0 +1,4 @@
++@NullMarked
++package io.papermc.paper.configuration.type.number;
++
++import org.jspecify.annotations.NullMarked;
+diff --git a/src/main/java/io/papermc/paper/configuration/type/package-info.java b/src/main/java/io/papermc/paper/configuration/type/package-info.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..af8bcae3fb6aeb75350d0783599582d0d0cd3912
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/configuration/type/package-info.java
+@@ -0,0 +1,4 @@
++@NullMarked
++package io.papermc.paper.configuration.type;
++
++import org.jspecify.annotations.NullMarked;
diff --git a/src/main/java/net/minecraft/server/Main.java b/src/main/java/net/minecraft/server/Main.java
index fc5c42e77a76b0ca946b13435144584b9c4bfafa..9bd6056bba6ba48bada7e9cd5883b0a171b0bbc4 100644
--- a/src/main/java/net/minecraft/server/Main.java
@@ -5226,7 +5376,7 @@ index 9cf0c141fefe67893828e300cba4f8a8545ba25f..c8e49c1904c80c4ede40ca5c26efad9b
this.world = new CraftWorld((ServerLevel) this, gen, biomeProvider, env);
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
-index 60805f6a2badca9b9cdfa54a9273ceba6311b16c..6c8a69b7c1b45549b2c388a8df2258184c587309 100644
+index 46b067fdfbbdeb3c0005b37d24ae248ec2d6bb90..d5451cb1976ca3675dd19b07bd8a2d363f82db86 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -967,6 +967,7 @@ public final class CraftServer implements Server {
@@ -5238,7 +5388,7 @@ index 60805f6a2badca9b9cdfa54a9273ceba6311b16c..6c8a69b7c1b45549b2c388a8df225818
world.serverLevelData.setDifficulty(config.difficulty);
world.setSpawnSettings(config.spawnMonsters);
diff --git a/src/main/java/org/bukkit/craftbukkit/Main.java b/src/main/java/org/bukkit/craftbukkit/Main.java
-index 17e10c4373b4281cc74b748c4a1e173e36eb9196..755b1d77418ecae0dc9ec5197275d0cd914e7bee 100644
+index 5e75839c0620f94e5b58942c984938e564d3b936..3ee69736494467aa6a1450baf868e73c94733e5b 100644
--- a/src/main/java/org/bukkit/craftbukkit/Main.java
+++ b/src/main/java/org/bukkit/craftbukkit/Main.java
@@ -142,6 +142,19 @@ public class Main {
diff --git a/patches/server/0022-Remap-reflection-calls-in-plugins-using-internals.patch b/patches/server/0022-Remap-reflection-calls-in-plugins-using-internals.patch
index 047ec2d29f..d04608c8b2 100644
--- a/patches/server/0022-Remap-reflection-calls-in-plugins-using-internals.patch
+++ b/patches/server/0022-Remap-reflection-calls-in-plugins-using-internals.patch
@@ -6,7 +6,7 @@ Subject: [PATCH] Remap reflection calls in plugins using internals
Co-authored-by: Jason Penilla <[email protected]>
diff --git a/build.gradle.kts b/build.gradle.kts
-index 24f3d0c96fe9d70b1a7cf528e09ebfc4366577ed..7aee6d9849f0a9c64db0368d2faa03c0633a72a4 100644
+index 8678e5bd59a7e085cb1b4e38f29e06ce36d2c1de..8d05216e246bfaec5945cdd55d08b6a388a769e8 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -62,6 +62,12 @@ dependencies {
@@ -23,7 +23,7 @@ index 24f3d0c96fe9d70b1a7cf528e09ebfc4366577ed..7aee6d9849f0a9c64db0368d2faa03c0
paperweight {
diff --git a/src/main/java/io/papermc/paper/configuration/serializer/PacketClassSerializer.java b/src/main/java/io/papermc/paper/configuration/serializer/PacketClassSerializer.java
-index 893ad5e7c2d32ccd64962d95d146bbd317c28ab8..3d73ea0e63c97b2b08e719b7be7af3894fb2d4e8 100644
+index b61935052154e76b1b8cb49868c96c52f34a41d1..a2fe12513b93ded71517955ef3e52c925f56f7d1 100644
--- a/src/main/java/io/papermc/paper/configuration/serializer/PacketClassSerializer.java
+++ b/src/main/java/io/papermc/paper/configuration/serializer/PacketClassSerializer.java
@@ -5,6 +5,7 @@ import com.google.common.collect.ImmutableBiMap;
@@ -32,9 +32,9 @@ index 893ad5e7c2d32ccd64962d95d146bbd317c28ab8..3d73ea0e63c97b2b08e719b7be7af389
import io.papermc.paper.configuration.serializer.collections.MapSerializer;
+import io.papermc.paper.util.MappingEnvironment;
import io.papermc.paper.util.ObfHelper;
- import net.minecraft.network.protocol.Packet;
- import org.checkerframework.checker.nullness.qual.Nullable;
-@@ -69,7 +70,7 @@ public final class PacketClassSerializer extends ScalarSerializer<Class<? extend
+ import java.lang.reflect.Type;
+ import java.util.List;
+@@ -68,7 +69,7 @@ public final class PacketClassSerializer extends ScalarSerializer<Class<? extend
@Override
protected @Nullable Object serialize(final Class<? extends Packet<?>> packetClass, final Predicate<Class<?>> typeSupported) {
final String name = packetClass.getName();
diff --git a/patches/server/0928-Add-onboarding-message-for-initial-server-start.patch b/patches/server/0928-Add-onboarding-message-for-initial-server-start.patch
index 64a7380197..b9d8134356 100644
--- a/patches/server/0928-Add-onboarding-message-for-initial-server-start.patch
+++ b/patches/server/0928-Add-onboarding-message-for-initial-server-start.patch
@@ -5,10 +5,10 @@ Subject: [PATCH] Add onboarding message for initial server start
diff --git a/src/main/java/io/papermc/paper/configuration/Configurations.java b/src/main/java/io/papermc/paper/configuration/Configurations.java
-index d9502ba028a96f9cc846f9ed428bd8066b857ca3..87e5f614ba988547a827486740db217e28585773 100644
+index 007e01d329a31acf7f4ed4c6dc4de7ad54ccad04..8cf720f08514e8e4f62f4ad196f1277bd761c6b2 100644
--- a/src/main/java/io/papermc/paper/configuration/Configurations.java
+++ b/src/main/java/io/papermc/paper/configuration/Configurations.java
-@@ -129,6 +129,7 @@ public abstract class Configurations<G, W> {
+@@ -127,6 +127,7 @@ public abstract class Configurations<G, W> {
if (Files.notExists(configFile)) {
node = CommentedConfigurationNode.root(loader.defaultOptions());
node.node(Configuration.VERSION_FIELD).raw(this.globalConfigVersion());
@@ -17,7 +17,7 @@ index d9502ba028a96f9cc846f9ed428bd8066b857ca3..87e5f614ba988547a827486740db217e
node = loader.load();
this.verifyGlobalConfigVersion(node);
diff --git a/src/main/java/io/papermc/paper/configuration/GlobalConfiguration.java b/src/main/java/io/papermc/paper/configuration/GlobalConfiguration.java
-index 56798215644d8bca1695856b3a941e8089f49e48..46c37c8db8ecf3cc808fcf59f6bee5fe6ca49b75 100644
+index e0cef37664b64f5db95943c5c118424436163e06..5e9c471ab20b0391e7e41351c65d96745fc8ce4a 100644
--- a/src/main/java/io/papermc/paper/configuration/GlobalConfiguration.java
+++ b/src/main/java/io/papermc/paper/configuration/GlobalConfiguration.java
@@ -25,6 +25,7 @@ public class GlobalConfiguration extends ConfigurationPart {
diff --git a/patches/server/1070-Configurable-Entity-Despawn-Time.patch b/patches/server/1070-Configurable-Entity-Despawn-Time.patch
new file mode 100644
index 0000000000..9f3d4aa488
--- /dev/null
+++ b/patches/server/1070-Configurable-Entity-Despawn-Time.patch
@@ -0,0 +1,39 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Kevin Raneri <[email protected]>
+Date: Mon, 30 Sep 2024 09:50:55 -0700
+Subject: [PATCH] Configurable Entity Despawn Time
+
+
+diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
+index 8be1b051543cda2b2e9e3d337834757e53f442de..ed5b00620527c1776722d25b1b45f1544802a341 100644
+--- a/src/main/java/net/minecraft/world/entity/Entity.java
++++ b/src/main/java/net/minecraft/world/entity/Entity.java
+@@ -432,6 +432,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
+ private UUID originWorld;
+ public boolean freezeLocked = false; // Paper - Freeze Tick Lock API
+ public boolean fixedPose = false; // Paper - Expand Pose API
++ private final int despawnTime; // Paper - entity despawn time limit
+
+ public void setOrigin(@javax.annotation.Nonnull Location location) {
+ this.origin = location.toVector();
+@@ -614,6 +615,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
+
+ public Entity(EntityType<?> type, Level world) {
+ this.id = Entity.ENTITY_COUNTER.incrementAndGet();
++ this.despawnTime = type == EntityType.PLAYER ? -1 : world.paperConfig().entities.spawning.despawnTime.getOrDefault(type, io.papermc.paper.configuration.type.number.IntOr.Disabled.DISABLED).or(-1); // Paper - entity despawn time limit
+ this.passengers = ImmutableList.of();
+ this.deltaMovement = Vec3.ZERO;
+ this.bb = Entity.INITIAL_AABB;
+@@ -912,6 +914,12 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
+ }
+
+ public void tick() {
++ // Paper start - entity despawn time limit
++ if (this.despawnTime >= 0 && this.tickCount >= this.despawnTime) {
++ this.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.DESPAWN);
++ return;
++ }
++ // Paper end - entity despawn time limit
+ this.baseTick();
+ }
+