--- a/net/minecraft/server/level/TicketType.java
+++ b/net/minecraft/server/level/TicketType.java
@@ -3,7 +_,45 @@
 import net.minecraft.core.Registry;
 import net.minecraft.core.registries.BuiltInRegistries;
 
-public record TicketType(long timeout, int flags) {
+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);
+    }
+
+    @Override
+    public final void moonrise$setTimeout(final long to) {
+        this.timeout = to;
+    }
+    // Paper end - rewrite chunk system
+
     public static final long NO_TIMEOUT = 0L;
     public static final int FLAG_PERSIST = 1;
     public static final int FLAG_LOADING = 2;
@@ -19,6 +_,11 @@
     public static final TicketType PORTAL = register("portal", 300L, 15);
     public static final TicketType ENDER_PEARL = register("ender_pearl", 40L, 14);
     public static final TicketType UNKNOWN = register("unknown", 1L, 18);
+    public static final TicketType PLUGIN = register("plugin", 0L, FLAG_LOADING | FLAG_SIMULATION); // CraftBukkit
+    public static final TicketType POST_TELEPORT = register("post_teleport", 5L, FLAG_LOADING | FLAG_SIMULATION); // Paper
+    public static final TicketType PLUGIN_TICKET = register("plugin_ticket", NO_TIMEOUT, FLAG_LOADING | FLAG_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", NO_TIMEOUT, FLAG_LOADING | FLAG_SIMULATION); // Paper
+    public static final TicketType CHUNK_LOAD = register("chunk_load", NO_TIMEOUT, FLAG_LOADING); // Paper - moonrise
 
     private static TicketType register(String name, long timeout, int flags) {
         return Registry.register(BuiltInRegistries.TICKET_TYPE, name, new TicketType(timeout, flags));
@@ -44,7 +_,49 @@
         return (this.flags & 16) != 0;
     }
 
+    // Paper start - rewrite chunk system - convert to class
+    private long timeout; // Paper - rewrite chunk system - remove final
+    private final int flags;
+
+    public TicketType(long timeout, int flags) {
+        this.timeout = timeout;
+        this.flags = flags;
+    } // long timeout, int flags
+
+    public int flags() {
+        return this.flags;
+    }
+
+    @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.flags == that.flags;
+    }
+
+    @Override
+    public int hashCode() {
+        return java.util.Objects.hash(timeout, flags);
+    }
+
+    @Override
+    public String toString() {
+        return "TicketType[" +
+            "timeout=" + timeout + ", " +
+            "flags=" + flags + ']';
+    }
+    // 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;
+    }
     public boolean hasTimeout() {
-        return this.timeout != 0L;
+        return this.timeout() != 0L;
+        // Paper end - chunk-gc config
     }
 }
