From e6679487628e6b3ecfb3aad335e9c0371a2a4d2e Mon Sep 17 00:00:00 2001
From: Sara Freimer <sara@freimer.com>
Date: Sat, 4 Jan 2025 16:40:40 -0600
Subject: [PATCH] Allow specifying names for watched chunks, and display ticket
 level changes for watched chunks

---
 .../2ca94f3a6e22cb9eec299788405fc6e4ad158c09  |   4 +-
 .../c10fcd8abbb6a520fc3ac2cf14b627d36958dd55  |   6 +-
 .../mekanism/assets/mekanism/lang/en_ud.json  |   7 +
 .../mekanism/assets/mekanism/lang/en_us.json  |   7 +
 .../data/mekanism/computer_help/jekyll.md     |   2 +-
 .../client/lang/MekanismLangProvider.java     |   7 +
 .../java/mekanism/common/MekanismLang.java    |   7 +
 .../mekanism/common/command/ChunkCommand.java | 135 +++++++++++++-----
 8 files changed, 136 insertions(+), 39 deletions(-)

diff --git a/src/datagen/generated/mekanism/.cache/2ca94f3a6e22cb9eec299788405fc6e4ad158c09 b/src/datagen/generated/mekanism/.cache/2ca94f3a6e22cb9eec299788405fc6e4ad158c09
index 2459bef7e61..ab7a9b6038a 100644
--- a/src/datagen/generated/mekanism/.cache/2ca94f3a6e22cb9eec299788405fc6e4ad158c09
+++ b/src/datagen/generated/mekanism/.cache/2ca94f3a6e22cb9eec299788405fc6e4ad158c09
@@ -1,6 +1,6 @@
-// 1.21.1	2024-09-28T11:00:15.3012517	ComputerHelp: mekanism
+// 1.21.1	2025-01-04T16:31:15.2109054	ComputerHelp: mekanism
 740491ca25c3ff0efd50f10d2427e4ceccb0c1e1 data/mekanism/computer_help/enums.csv
 e43ab1d9e5ae68f834035609a85af3c6ce4161a4 data/mekanism/computer_help/enums.json
-a61020bfab0b0525339f1b49417d2f7e17dbc7df data/mekanism/computer_help/jekyll.md
+0e0954dc2f5f02d275cfcfb4e70af3912268fa5e data/mekanism/computer_help/jekyll.md
 0442f235be0f36db5728aaf942c99e9c8638f662 data/mekanism/computer_help/methods.csv
 366f1717990ae1860dc4739af4a8f441cd0acad5 data/mekanism/computer_help/methods.json
diff --git a/src/datagen/generated/mekanism/.cache/c10fcd8abbb6a520fc3ac2cf14b627d36958dd55 b/src/datagen/generated/mekanism/.cache/c10fcd8abbb6a520fc3ac2cf14b627d36958dd55
index 00b567bbc28..d7f8794f09c 100644
--- a/src/datagen/generated/mekanism/.cache/c10fcd8abbb6a520fc3ac2cf14b627d36958dd55
+++ b/src/datagen/generated/mekanism/.cache/c10fcd8abbb6a520fc3ac2cf14b627d36958dd55
@@ -1,5 +1,5 @@
-// 1.21.1	2024-09-28T11:00:15.3042814	Languages: en_us for mod: mekanism
+// 1.21.1	2025-01-04T16:37:51.1646312	Languages: en_us for mod: mekanism
 8d915354d84b3a4d60c65f9280fdd7ceca016c35 assets/mekanism/lang/en_au.json
 8d915354d84b3a4d60c65f9280fdd7ceca016c35 assets/mekanism/lang/en_gb.json
-2cd403d1dc72293441a4c86b1b1ddc05978130e8 assets/mekanism/lang/en_ud.json
-525157cafd0048a56223755ff5b66a357d3008b4 assets/mekanism/lang/en_us.json
+471fdc7ece86c06b7b15bc05491624e542a26c10 assets/mekanism/lang/en_ud.json
+6a732ccdeb564cb51389a2ac7832c1cf6231b359 assets/mekanism/lang/en_us.json
diff --git a/src/datagen/generated/mekanism/assets/mekanism/lang/en_ud.json b/src/datagen/generated/mekanism/assets/mekanism/lang/en_ud.json
index 0d8f56e1208..8672b800ba4 100644
--- a/src/datagen/generated/mekanism/assets/mekanism/lang/en_ud.json
+++ b/src/datagen/generated/mekanism/assets/mekanism/lang/en_ud.json
@@ -677,14 +677,21 @@
   "command.mekanism.chunk.clear": "˙ʇsᴉꞁ ɥɔʇɐʍ ɯoɹɟ pǝʌoɯǝɹ sʞunɥɔ %s",
   "command.mekanism.chunk.flush": "˙pǝpɐoꞁun sʞunɥɔ %s",
   "command.mekanism.chunk.loaded": "˙(%s) ʞunɥɔ pǝpɐoꞀ",
+  "command.mekanism.chunk.loaded.named": "˙(%2$s) %1$s ʞunɥɔ pǝpɐoꞀ",
+  "command.mekanism.chunk.ticket_level_changed": "˙%3$s :oʇ %s :ɯoɹɟ pǝᵷuɐɥɔ ꞁǝʌǝꞁ ʇǝʞɔᴉʇ (%1$s) ʞunɥƆ",
+  "command.mekanism.chunk.ticket_level_changed.named": "˙%4$s :oʇ %3$s :ɯoɹɟ pǝᵷuɐɥɔ ꞁǝʌǝꞁ ʇǝʞɔᴉʇ (%2$s) %1$s ʞunɥƆ",
   "command.mekanism.chunk.unloaded": "˙(%s) ʞunɥɔ pǝpɐoꞁu∩",
+  "command.mekanism.chunk.unloaded.named": "˙(%2$s) %1$s ʞunɥɔ pǝpɐoꞁu∩",
   "command.mekanism.chunk.unwatch": "˙ʇsᴉꞁ ɥɔʇɐʍ ɯoɹɟ pǝʌoɯǝɹ (%s) ʞunɥƆ",
+  "command.mekanism.chunk.unwatch.named": "˙ʇsᴉꞁ ɥɔʇɐʍ ɯoɹɟ pǝʌoɯǝɹ (%2$s) %1$s ʞunɥƆ",
   "command.mekanism.chunk.watch": "˙ʇsᴉꞁ ɥɔʇɐʍ oʇ pǝppɐ (%s) ʞunɥƆ",
+  "command.mekanism.chunk.watch.named": "˙ʇsᴉꞁ ɥɔʇɐʍ oʇ pǝppɐ (%2$s) %1$s ʞunɥƆ",
   "command.mekanism.debug": "˙%s :ǝpoɯ ᵷnqǝp pǝꞁᵷᵷo⟘",
   "command.mekanism.error.build.miss": "˙punoɟ ʇǝᵷɹɐʇ pᴉꞁɐʌ oN",
   "command.mekanism.error.retrogen.disabled": "˙ᵷᴉɟuoɔ ǝɥʇ uᴉ ʇᴉ ǝꞁqɐuǝ ǝsɐǝꞁd 'pǝꞁqɐsᴉp sᴉ uǝᵷoɹʇǝᴚ",
   "command.mekanism.error.retrogen.failure": "˙uǝᵷoɹʇǝɹ ɹoɟ sʞunɥɔ ʎuɐ ǝnǝnb oʇ pǝꞁᴉɐℲ",
   "command.mekanism.error.tpop.empty": "˙ʞɔɐʇs uo suoᴉʇᴉsod oN",
+  "command.mekanism.error.watch.not_watched": "˙pǝɥɔʇɐʍ ᵷuᴉǝq ʇou sᴉ (%s) ʞunɥƆ",
   "command.mekanism.radiation.add": "˙%3$s uᴉ (%s) ʇɐ uoᴉʇɐᴉpɐɹ %1$s pǝppⱯ",
   "command.mekanism.radiation.add_entity": "˙ɹǝʎɐꞁd oʇ uoᴉʇɐᴉpɐɹ %s pǝppⱯ",
   "command.mekanism.radiation.add_entity.target": "˙%2$s :ʎʇᴉʇuǝ oʇ uoᴉʇɐᴉpɐɹ %1$s pǝppⱯ",
diff --git a/src/datagen/generated/mekanism/assets/mekanism/lang/en_us.json b/src/datagen/generated/mekanism/assets/mekanism/lang/en_us.json
index d78477a49c4..0d555e8a64d 100644
--- a/src/datagen/generated/mekanism/assets/mekanism/lang/en_us.json
+++ b/src/datagen/generated/mekanism/assets/mekanism/lang/en_us.json
@@ -678,14 +678,21 @@
   "command.mekanism.chunk.clear": "%1$s chunks removed from watch list.",
   "command.mekanism.chunk.flush": "%1$s chunks unloaded.",
   "command.mekanism.chunk.loaded": "Loaded chunk (%1$s).",
+  "command.mekanism.chunk.loaded.named": "Loaded chunk %1$s (%2$s).",
+  "command.mekanism.chunk.ticket_level_changed": "Chunk (%1$s) ticket level changed from: %2$s to: %3$s.",
+  "command.mekanism.chunk.ticket_level_changed.named": "Chunk %1$s (%2$s) ticket level changed from: %3$s to: %4$s.",
   "command.mekanism.chunk.unloaded": "Unloaded chunk (%1$s).",
+  "command.mekanism.chunk.unloaded.named": "Unloaded chunk %1$s (%2$s).",
   "command.mekanism.chunk.unwatch": "Chunk (%1$s) removed from watch list.",
+  "command.mekanism.chunk.unwatch.named": "Chunk %1$s (%2$s) removed from watch list.",
   "command.mekanism.chunk.watch": "Chunk (%1$s) added to watch list.",
+  "command.mekanism.chunk.watch.named": "Chunk %1$s (%2$s) added to watch list.",
   "command.mekanism.debug": "Toggled debug mode: %1$s.",
   "command.mekanism.error.build.miss": "No valid target found.",
   "command.mekanism.error.retrogen.disabled": "Retrogen is disabled, please enable it in the config.",
   "command.mekanism.error.retrogen.failure": "Failed to queue any chunks for retrogen.",
   "command.mekanism.error.tpop.empty": "No positions on stack.",
+  "command.mekanism.error.watch.not_watched": "Chunk (%1$s) is not being watched.",
   "command.mekanism.radiation.add": "Added %1$s radiation at (%2$s) in %3$s.",
   "command.mekanism.radiation.add_entity": "Added %1$s radiation to player.",
   "command.mekanism.radiation.add_entity.target": "Added %1$s radiation to entity: %2$s.",
diff --git a/src/datagen/generated/mekanism/data/mekanism/computer_help/jekyll.md b/src/datagen/generated/mekanism/data/mekanism/computer_help/jekyll.md
index 8816e3b2faf..139dbdf98c6 100644
--- a/src/datagen/generated/mekanism/data/mekanism/computer_help/jekyll.md
+++ b/src/datagen/generated/mekanism/data/mekanism/computer_help/jekyll.md
@@ -4495,5 +4495,5 @@ methods:
     returns:
       java_type: boolean
       type: boolean
-version: 10.7.7
+version: 10.7.8
 ---
diff --git a/src/datagen/main/java/mekanism/client/lang/MekanismLangProvider.java b/src/datagen/main/java/mekanism/client/lang/MekanismLangProvider.java
index 4cec72f0704..e9ffe691231 100644
--- a/src/datagen/main/java/mekanism/client/lang/MekanismLangProvider.java
+++ b/src/datagen/main/java/mekanism/client/lang/MekanismLangProvider.java
@@ -1053,12 +1053,19 @@ private void addMisc() {
         add(MekanismLang.HOLD_FOR_MODULES, "Hold %1$s for installed modules.");
         add(MekanismLang.HOLD_FOR_SUPPORTED_ITEMS, "Hold %1$s for supporting items and conflicting modules.");
         //Commands
+        add(MekanismLang.COMMAND_ERROR_NOT_WATCHED, "Chunk (%1$s) is not being watched.");
         add(MekanismLang.COMMAND_CHUNK_WATCH, "Chunk (%1$s) added to watch list.");
+        add(MekanismLang.COMMAND_CHUNK_WATCH_NAMED, "Chunk %1$s (%2$s) added to watch list.");
         add(MekanismLang.COMMAND_CHUNK_UNWATCH, "Chunk (%1$s) removed from watch list.");
+        add(MekanismLang.COMMAND_CHUNK_UNWATCH_NAMED, "Chunk %1$s (%2$s) removed from watch list.");
         add(MekanismLang.COMMAND_CHUNK_CLEAR, "%1$s chunks removed from watch list.");
         add(MekanismLang.COMMAND_CHUNK_FLUSH, "%1$s chunks unloaded.");
         add(MekanismLang.COMMAND_CHUNK_LOADED, "Loaded chunk (%1$s).");
+        add(MekanismLang.COMMAND_CHUNK_LOADED_NAMED, "Loaded chunk %1$s (%2$s).");
         add(MekanismLang.COMMAND_CHUNK_UNLOADED, "Unloaded chunk (%1$s).");
+        add(MekanismLang.COMMAND_CHUNK_UNLOADED_NAMED, "Unloaded chunk %1$s (%2$s).");
+        add(MekanismLang.COMMAND_CHUNK_TICKET_LEVEL_CHANGED, "Chunk (%1$s) ticket level changed from: %2$s to: %3$s.");
+        add(MekanismLang.COMMAND_CHUNK_TICKET_LEVEL_CHANGED_NAMED, "Chunk %1$s (%2$s) ticket level changed from: %3$s to: %4$s.");
         add(MekanismLang.COMMAND_DEBUG, "Toggled debug mode: %1$s.");
         add(MekanismLang.COMMAND_TEST_RULES, "Enabled keepInventory, and disabled doMobSpawning, doDaylightCycle, doWeatherCycle and mobGriefing!");
         add(MekanismLang.COMMAND_TP, "Teleported to (%1$s) - saved last position on stack.");
diff --git a/src/main/java/mekanism/common/MekanismLang.java b/src/main/java/mekanism/common/MekanismLang.java
index 63018a6699a..7c2d25f00ca 100644
--- a/src/main/java/mekanism/common/MekanismLang.java
+++ b/src/main/java/mekanism/common/MekanismLang.java
@@ -161,12 +161,19 @@ public enum MekanismLang implements ILangEntry {
     HOLD_FOR_MODULES("tooltip", "hold_for_modules"),
     HOLD_FOR_SUPPORTED_ITEMS("tooltip", "hold_for_supported_items"),
     //Commands
+    COMMAND_ERROR_NOT_WATCHED("command", "error.watch.not_watched"),
     COMMAND_CHUNK_WATCH("command", "chunk.watch"),
+    COMMAND_CHUNK_WATCH_NAMED("command", "chunk.watch.named"),
     COMMAND_CHUNK_UNWATCH("command", "chunk.unwatch"),
+    COMMAND_CHUNK_UNWATCH_NAMED("command", "chunk.unwatch.named"),
     COMMAND_CHUNK_CLEAR("command", "chunk.clear"),
     COMMAND_CHUNK_FLUSH("command", "chunk.flush"),
     COMMAND_CHUNK_LOADED("command", "chunk.loaded"),
+    COMMAND_CHUNK_LOADED_NAMED("command", "chunk.loaded.named"),
     COMMAND_CHUNK_UNLOADED("command", "chunk.unloaded"),
+    COMMAND_CHUNK_UNLOADED_NAMED("command", "chunk.unloaded.named"),
+    COMMAND_CHUNK_TICKET_LEVEL_CHANGED("command", "chunk.ticket_level_changed"),
+    COMMAND_CHUNK_TICKET_LEVEL_CHANGED_NAMED("command", "chunk.ticket_level_changed.named"),
     COMMAND_DEBUG("command", "debug"),
     COMMAND_TEST_RULES("command", "testrules"),
     COMMAND_TP("command", "tp"),
diff --git a/src/main/java/mekanism/common/command/ChunkCommand.java b/src/main/java/mekanism/common/command/ChunkCommand.java
index 29c1973c5b5..adcad6ac5ea 100644
--- a/src/main/java/mekanism/common/command/ChunkCommand.java
+++ b/src/main/java/mekanism/common/command/ChunkCommand.java
@@ -1,31 +1,47 @@
 package mekanism.common.command;
 
+import com.mojang.brigadier.Command;
+import com.mojang.brigadier.arguments.StringArgumentType;
 import com.mojang.brigadier.builder.ArgumentBuilder;
-import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
-import it.unimi.dsi.fastutil.longs.LongSet;
+import com.mojang.brigadier.context.CommandContext;
+import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
+import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
 import mekanism.api.text.EnumColor;
 import mekanism.api.text.ILangEntry;
+import mekanism.api.text.TextComponentUtil;
 import mekanism.common.MekanismLang;
 import mekanism.common.base.MekanismPermissions;
 import net.minecraft.commands.CommandSourceStack;
 import net.minecraft.commands.Commands;
 import net.minecraft.commands.arguments.coordinates.ColumnPosArgument;
 import net.minecraft.core.BlockPos;
+import net.minecraft.network.chat.CommonComponents;
 import net.minecraft.network.chat.Component;
 import net.minecraft.server.level.ColumnPos;
 import net.minecraft.server.level.ServerChunkCache;
 import net.minecraft.world.entity.player.Player;
 import net.minecraft.world.level.ChunkPos;
+import net.minecraft.world.level.LevelAccessor;
+import net.neoforged.bus.api.SubscribeEvent;
 import net.neoforged.neoforge.common.NeoForge;
 import net.neoforged.neoforge.event.level.ChunkEvent;
-import net.neoforged.bus.api.SubscribeEvent;
+import net.neoforged.neoforge.event.level.ChunkTicketLevelUpdatedEvent;
+import org.jetbrains.annotations.Nullable;
 
 public class ChunkCommand {
 
     private ChunkCommand() {
     }
 
-    private static final LongSet chunkWatchers = new LongOpenHashSet();
+    private static final String NAME_PARAM = "name";
+    private static final String POS_PARAM = "pos";
+
+    private static final LangData WATCH = new LangData(MekanismLang.COMMAND_CHUNK_WATCH, MekanismLang.COMMAND_CHUNK_WATCH_NAMED);
+    private static final LangData UNWATCH = new LangData(MekanismLang.COMMAND_CHUNK_UNWATCH, MekanismLang.COMMAND_CHUNK_UNWATCH_NAMED);
+    private static final LangData LOADED = new LangData(MekanismLang.COMMAND_CHUNK_LOADED, MekanismLang.COMMAND_CHUNK_LOADED_NAMED);
+    private static final LangData UNLOADED = new LangData(MekanismLang.COMMAND_CHUNK_UNLOADED, MekanismLang.COMMAND_CHUNK_UNLOADED_NAMED);
+    //TODO: Allow specifying watches for chunks in different dimensions?
+    private static final Long2ObjectMap<ChunkWatchSettings> chunkWatchers = new Long2ObjectOpenHashMap<>();
 
     static ArgumentBuilder<CommandSourceStack, ?> register() {
         NeoForge.EVENT_BUS.register(ChunkCommand.class);
@@ -42,20 +58,30 @@ private static class WatchCommand {
         static ArgumentBuilder<CommandSourceStack, ?> register() {
             return Commands.literal("watch")
                   .requires(MekanismPermissions.COMMAND_CHUNK_WATCH)
-                  .executes(ctx -> {
-                      CommandSourceStack source = ctx.getSource();
-                      return watch(source, new ChunkPos(BlockPos.containing(source.getPosition())));
-                  }).then(Commands.argument("pos", ColumnPosArgument.columnPos())
-                        .executes(ctx -> {
-                            ColumnPos column = ColumnPosArgument.getColumnPos(ctx, "pos");
-                            return watch(ctx.getSource(), column.toChunkPos());
-                        }));
+                  .executes(ctx -> watch(ctx, false, false))
+                  .then(Commands.argument(POS_PARAM, ColumnPosArgument.columnPos())
+                        .executes(ctx -> watch(ctx, true, false))
+                        .then(Commands.argument(NAME_PARAM, StringArgumentType.word())
+                              .executes(ctx -> watch(ctx, true, true))
+                        )
+                  )
+                  .then(Commands.argument(NAME_PARAM, StringArgumentType.word())
+                        .executes(ctx -> watch(ctx, false, true))
+                  );
         }
 
-        private static int watch(CommandSourceStack source, ChunkPos chunkPos) {
-            chunkWatchers.add(ChunkPos.asLong(chunkPos.x, chunkPos.z));
-            source.sendSuccess(() -> MekanismLang.COMMAND_CHUNK_WATCH.translateColored(EnumColor.GRAY, EnumColor.INDIGO, getPosition(chunkPos)), true);
-            return 0;
+        private static int watch(CommandContext<CommandSourceStack> ctx, boolean positionFromArgument, boolean hasName) {
+            ChunkPos chunkPos;
+            if (positionFromArgument) {
+                chunkPos = ColumnPosArgument.getColumnPos(ctx, POS_PARAM).toChunkPos();
+            } else {
+                chunkPos = new ChunkPos(BlockPos.containing(ctx.getSource().getPosition()));
+            }
+            String name = hasName ? StringArgumentType.getString(ctx, NAME_PARAM) : null;
+            ChunkWatchSettings settings = new ChunkWatchSettings(name, chunkPos);
+            chunkWatchers.put(ChunkPos.asLong(chunkPos.x, chunkPos.z), settings);
+            ctx.getSource().sendSuccess(() -> settings.translate(WATCH), true);
+            return Command.SINGLE_SUCCESS;
         }
     }
 
@@ -67,17 +93,21 @@ private static class UnwatchCommand {
                   .executes(ctx -> {
                       CommandSourceStack source = ctx.getSource();
                       return unwatch(source, new ChunkPos(BlockPos.containing(source.getPosition())));
-                  }).then(Commands.argument("pos", ColumnPosArgument.columnPos())
+                  }).then(Commands.argument(POS_PARAM, ColumnPosArgument.columnPos())
                         .executes(ctx -> {
-                            ColumnPos column = ColumnPosArgument.getColumnPos(ctx, "pos");
+                            ColumnPos column = ColumnPosArgument.getColumnPos(ctx, POS_PARAM);
                             return unwatch(ctx.getSource(), column.toChunkPos());
                         }));
         }
 
         private static int unwatch(CommandSourceStack source, ChunkPos chunkPos) {
-            chunkWatchers.remove(ChunkPos.asLong(chunkPos.x, chunkPos.z));
-            source.sendSuccess(() -> MekanismLang.COMMAND_CHUNK_UNWATCH.translateColored(EnumColor.GRAY, EnumColor.INDIGO, getPosition(chunkPos)), true);
-            return 0;
+            ChunkWatchSettings settings = chunkWatchers.remove(ChunkPos.asLong(chunkPos.x, chunkPos.z));
+            if (settings == null) {
+                source.sendFailure(MekanismLang.COMMAND_ERROR_NOT_WATCHED.translate(MekanismLang.GENERIC_WITH_COMMA.translate(chunkPos.x, chunkPos.z)));
+                return 0;
+            }
+            source.sendSuccess(() -> settings.translate(UNWATCH), true);
+            return Command.SINGLE_SUCCESS;
         }
     }
 
@@ -90,7 +120,7 @@ private static class ClearCommand {
                       int count = chunkWatchers.size();
                       chunkWatchers.clear();
                       ctx.getSource().sendSuccess(() -> MekanismLang.COMMAND_CHUNK_CLEAR.translateColored(EnumColor.GRAY, EnumColor.INDIGO, count), true);
-                      return 0;
+                      return Command.SINGLE_SUCCESS;
                   });
         }
     }
@@ -106,37 +136,76 @@ private static class FlushCommand {
                       int startCount = sp.getLoadedChunksCount();
                       //TODO: Check this
                       //sp.queueUnloadAll();
-                      sp.tick(() -> false, false);
+                      sp.tick(() -> true, false);
                       source.sendSuccess(() -> MekanismLang.COMMAND_CHUNK_FLUSH.translateColored(EnumColor.GRAY, EnumColor.INDIGO, startCount - sp.getLoadedChunksCount()), true);
-                      return 0;
+                      return Command.SINGLE_SUCCESS;
                   });
         }
     }
 
     @SubscribeEvent
     public static void onChunkLoad(ChunkEvent.Load event) {
-        handleChunkEvent(event, MekanismLang.COMMAND_CHUNK_LOADED);
+        handleChunkEvent(event, LOADED);
     }
 
     @SubscribeEvent
     public static void onChunkUnload(ChunkEvent.Unload event) {
-        handleChunkEvent(event, MekanismLang.COMMAND_CHUNK_UNLOADED);
+        handleChunkEvent(event, UNLOADED);
     }
 
-    private static void handleChunkEvent(ChunkEvent event, ILangEntry direction) {
-        if (event.getLevel() == null || event.getLevel().isClientSide()) {
+    @SubscribeEvent
+    public static void onTicketLevelChange(ChunkTicketLevelUpdatedEvent event) {
+        if (chunkWatchers.isEmpty() || event.getLevel().players().isEmpty()) {
             return;
         }
-        ChunkPos pos = event.getChunk().getPos();
-        if (chunkWatchers.contains(pos.toLong())) {
-            Component message = direction.translateColored(EnumColor.GRAY, EnumColor.INDIGO, getPosition(pos));
+        ChunkWatchSettings settings = chunkWatchers.get(event.getChunkPos());
+        if (settings != null) {
+            Component message = settings.translateTicketLevel(event.getOldTicketLevel(), event.getNewTicketLevel());
             for (Player player : event.getLevel().players()) {
                 player.sendSystemMessage(message);
             }
         }
     }
 
-    private static Component getPosition(ChunkPos pos) {
-        return MekanismLang.GENERIC_WITH_COMMA.translate(pos.x, pos.z);
+    private static void handleChunkEvent(ChunkEvent event, LangData direction) {
+        LevelAccessor level = event.getLevel();
+        if (level != null && !level.isClientSide()) {
+            if (chunkWatchers.isEmpty() || level.players().isEmpty()) {
+                return;
+            }
+            ChunkWatchSettings settings = chunkWatchers.get(event.getChunk().getPos().toLong());
+            if (settings != null) {
+                Component message = settings.translate(direction);
+                for (Player player : level.players()) {
+                    player.sendSystemMessage(message);
+                }
+            }
+        }
+    }
+
+    private record LangData(ILangEntry unnamed, ILangEntry named) {
+    }
+
+    private record ChunkWatchSettings(Component name, Component position) {
+
+        private ChunkWatchSettings(@Nullable String name, ChunkPos pos) {
+            this(name == null ? CommonComponents.EMPTY : TextComponentUtil.getString(name), MekanismLang.GENERIC_WITH_COMMA.translate(pos.x, pos.z));
+        }
+
+        public Component translate(LangData langData) {
+            if (name == CommonComponents.EMPTY) {
+                return langData.unnamed().translateColored(EnumColor.GRAY, EnumColor.INDIGO, position);
+            }
+            return langData.named().translateColored(EnumColor.GRAY, EnumColor.INDIGO, name, EnumColor.INDIGO, position);
+        }
+
+        public Component translateTicketLevel(int oldLevel, int newLevel) {
+            if (name == CommonComponents.EMPTY) {
+                return MekanismLang.COMMAND_CHUNK_TICKET_LEVEL_CHANGED.translateColored(EnumColor.GRAY, EnumColor.INDIGO, position, EnumColor.INDIGO, oldLevel,
+                      EnumColor.INDIGO, newLevel);
+            }
+            return MekanismLang.COMMAND_CHUNK_TICKET_LEVEL_CHANGED_NAMED.translateColored(EnumColor.GRAY, EnumColor.INDIGO, name, EnumColor.INDIGO, position,
+                  EnumColor.INDIGO, oldLevel, EnumColor.INDIGO, newLevel);
+        }
     }
 }
\ No newline at end of file