--- a/net/minecraft/server/level/TicketType.java
+++ b/net/minecraft/server/level/TicketType.java
@@ -3,7 +_,40 @@
 import net.minecraft.core.Registry;
 import net.minecraft.core.registries.BuiltInRegistries;
 
-public record TicketType(long timeout, boolean persist, TicketType.TicketUse use) {
+public final class TicketType<T> implements ca.spottedleaf.moonrise.patches.chunk_system.ticket.ChunkSystemTicketType<T> { // Paper - rewrite chunk system
+    // Paper start - rewrite chunk system
+    private static final java.util.concurrent.atomic.AtomicLong ID_GENERATOR = new java.util.concurrent.atomic.AtomicLong();
+    private final long id = ID_GENERATOR.getAndIncrement();
+    private java.util.Comparator<T> identifierComparator;
+    private volatile long[] counterTypes;
+
+    @Override
+    public final long moonrise$getId() {
+        return this.id;
+    }
+
+    @Override
+    public final java.util.Comparator<T> moonrise$getIdentifierComparator() {
+        return this.identifierComparator;
+    }
+
+    @Override
+    public final void moonrise$setIdentifierComparator(final java.util.Comparator<T> comparator) {
+        this.identifierComparator = comparator;
+    }
+
+    @Override
+    public final long[] moonrise$getCounterTypes() {
+        // need to lazy init this because we cannot check if we are FORCED during construction
+        final long[] types = this.counterTypes;
+        if (types != null) {
+            return types;
+        }
+
+        return this.counterTypes = ca.spottedleaf.moonrise.common.PlatformHooks.get().getCounterTypesUncached((TicketType)(Object)this);
+    }
+    // Paper end - rewrite chunk system
+
     public static final long NO_TIMEOUT = 0L;
     public static final TicketType START = register("start", 0L, false, TicketType.TicketUse.LOADING_AND_SIMULATION);
     public static final TicketType DRAGON = register("dragon", 0L, false, TicketType.TicketUse.LOADING_AND_SIMULATION);
@@ -13,11 +_,67 @@
     public static final TicketType PORTAL = register("portal", 300L, true, TicketType.TicketUse.LOADING_AND_SIMULATION);
     public static final TicketType ENDER_PEARL = register("ender_pearl", 40L, false, TicketType.TicketUse.LOADING_AND_SIMULATION);
     public static final TicketType UNKNOWN = register("unknown", 1L, false, TicketType.TicketUse.LOADING);
+    public static final TicketType PLUGIN = register("plugin", 0L, false, TicketType.TicketUse.LOADING_AND_SIMULATION); // CraftBukkit
+    public static final TicketType POST_TELEPORT = register("post_teleport", 5L, false, TicketType.TicketUse.LOADING_AND_SIMULATION); // Paper
+    public static final TicketType PLUGIN_TICKET = register("plugin_ticket", TicketType.NO_TIMEOUT, false, TicketType.TicketUse.LOADING_AND_SIMULATION); static { ((TicketType<org.bukkit.plugin.Plugin>)PLUGIN_TICKET).moonrise$setIdentifierComparator((org.bukkit.plugin.Plugin p1, org.bukkit.plugin.Plugin p2) -> p1.getName().compareTo(p2.getName())); } // Paper // Paper - rewrite chunk system
+    public static final TicketType FUTURE_AWAIT = register("future_await", TicketType.NO_TIMEOUT, false, TicketType.TicketUse.LOADING_AND_SIMULATION); // Paper
+    public static final TicketType CHUNK_LOAD = register("chunk_load", TicketType.NO_TIMEOUT, false, TicketType.TicketUse.LOADING); // Paper - moonrise
 
-    private static TicketType register(String name, long timeout, boolean persist, TicketType.TicketUse use) {
+    public static TicketType register(String name, long timeout, boolean persist, TicketType.TicketUse use) {
         return Registry.register(BuiltInRegistries.TICKET_TYPE, name, new TicketType(timeout, persist, use));
     }
 
+    // Paper start - rewrite chunk system - convert to class
+    private final long timeout;
+    private final boolean persist;
+    private final net.minecraft.server.level.TicketType.TicketUse use;
+
+    public TicketType(long timeout, boolean persist, net.minecraft.server.level.TicketType.TicketUse use) {
+        this.timeout = timeout;
+        this.persist = persist;
+        this.use = use;
+    }
+
+    public boolean persist() {
+        return this.persist;
+    }
+
+    public net.minecraft.server.level.TicketType.TicketUse use() {
+        return this.use;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == this) return true;
+        if (obj == null || obj.getClass() != this.getClass()) return false;
+        var that = (net.minecraft.server.level.TicketType) obj;
+        return this.timeout == that.timeout &&
+            this.persist == that.persist &&
+            java.util.Objects.equals(this.use, that.use);
+    }
+
+    @Override
+    public int hashCode() {
+        return java.util.Objects.hash(timeout, persist, use);
+    }
+
+    @Override
+    public String toString() {
+        return "TicketType[" +
+            "timeout=" + timeout + ", " +
+            "persist=" + persist + ", " +
+            "use=" + use + ']';
+    }
+    // Paper end - rewrite chunk system - convert to class
+
+    // Paper start - chunk-gc config
+    public static int PLUGIN_TYPE_TIMEOUT = 600;
+    // Paper - rewrite chunk system - convert to class
+    public long timeout() {
+        return this == PLUGIN ? PLUGIN_TYPE_TIMEOUT : this.timeout;
+    }
+    // Paper end - chunk-gc config
+
     public boolean doesLoad() {
         return this.use == TicketType.TicketUse.LOADING || this.use == TicketType.TicketUse.LOADING_AND_SIMULATION;
     }
@@ -27,7 +_,7 @@
     }
 
     public boolean hasTimeout() {
-        return this.timeout != 0L;
+        return this.timeout() != 0L; // Paper - chunk-gc config
     }
 
     public static enum TicketUse {
