From 31f4ab06d0fb79ebac89ce0b59c09d9401dc5b77 Mon Sep 17 00:00:00 2001 From: Gugle Date: Mon, 28 Oct 2024 14:50:39 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E7=89=B9=E6=80=A7=EF=BC=8Cco?= =?UTF-8?q?mmandTodo=EF=BC=8CcommandHere=EF=BC=8CcommandWhereis=EF=BC=8Cco?= =?UTF-8?q?mmandLoc?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 4 + gradle.properties | 7 +- .../dev/dubhe/gugle/carpet/GcaExtension.java | 18 +- .../dev/dubhe/gugle/carpet/GcaSetting.java | 41 +++- .../gugle/carpet/commands/BotCommand.java | 118 +++------ .../gugle/carpet/commands/HereCommand.java | 78 ++++++ .../gugle/carpet/commands/LocCommand.java | 226 ++++++++++++++++++ .../gugle/carpet/commands/TodoCommand.java | 215 +++++++++++++++++ .../gugle/carpet/commands/WhereisCommand.java | 31 +++ .../mixin/WaypointSharingHandlerMixin.java | 21 ++ .../gugle/carpet/tools/DimTypeSerializer.java | 28 +++ .../dubhe/gugle/carpet/tools/FilesUtil.java | 66 +++++ .../dubhe/gugle/carpet/tools/IdGenerator.java | 153 ++++++++++++ src/main/resources/assets/gca/lang/en_us.json | 12 + src/main/resources/assets/gca/lang/zh_cn.json | 12 + src/main/resources/assets/gca/lang/zh_tw.json | 12 + src/main/resources/gca.mixins.json | 3 +- 17 files changed, 947 insertions(+), 98 deletions(-) create mode 100644 src/main/java/dev/dubhe/gugle/carpet/commands/HereCommand.java create mode 100644 src/main/java/dev/dubhe/gugle/carpet/commands/LocCommand.java create mode 100644 src/main/java/dev/dubhe/gugle/carpet/commands/TodoCommand.java create mode 100644 src/main/java/dev/dubhe/gugle/carpet/commands/WhereisCommand.java create mode 100644 src/main/java/dev/dubhe/gugle/carpet/mixin/WaypointSharingHandlerMixin.java create mode 100644 src/main/java/dev/dubhe/gugle/carpet/tools/DimTypeSerializer.java create mode 100644 src/main/java/dev/dubhe/gugle/carpet/tools/FilesUtil.java create mode 100644 src/main/java/dev/dubhe/gugle/carpet/tools/IdGenerator.java diff --git a/build.gradle b/build.gradle index 79689da..f1ce891 100644 --- a/build.gradle +++ b/build.gradle @@ -36,6 +36,10 @@ dependencies { // fabric-carpet modImplementation("curse.maven:carpet-349239:${project.carpet_core_version}") + // xaeromap + modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}" + modImplementation("curse.maven:xaeros-world-map-317780:5658242") + modImplementation("curse.maven:xaeros-mini-map-263420:5437650") } processResources { diff --git a/gradle.properties b/gradle.properties index e2acec9..0708511 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,8 +2,11 @@ org.gradle.jvmargs=-Xmx2G # Fabric Properties # check these on https://modmuss50.me/fabric.html -minecraft_version=1.21 -loader_version=0.15.11 +minecraft_version=1.21.1 +loader_version=0.16.7 + +# Fabric API +fabric_version=0.107.0+1.21.1 # parchment_version=2022.08.14 # Mod Properties mod_version=2.9.0 diff --git a/src/main/java/dev/dubhe/gugle/carpet/GcaExtension.java b/src/main/java/dev/dubhe/gugle/carpet/GcaExtension.java index c9987d2..932a0f8 100644 --- a/src/main/java/dev/dubhe/gugle/carpet/GcaExtension.java +++ b/src/main/java/dev/dubhe/gugle/carpet/GcaExtension.java @@ -11,6 +11,11 @@ import dev.dubhe.gugle.carpet.api.Consumer; import dev.dubhe.gugle.carpet.api.tools.text.ComponentTranslate; import dev.dubhe.gugle.carpet.commands.BotCommand; +import dev.dubhe.gugle.carpet.commands.HereCommand; +import dev.dubhe.gugle.carpet.commands.LocCommand; +import dev.dubhe.gugle.carpet.commands.TodoCommand; +import dev.dubhe.gugle.carpet.commands.WhereisCommand; +import dev.dubhe.gugle.carpet.tools.DimTypeSerializer; import dev.dubhe.gugle.carpet.tools.FakePlayerEnderChestContainer; import dev.dubhe.gugle.carpet.tools.FakePlayerInventoryContainer; import dev.dubhe.gugle.carpet.tools.FakePlayerResident; @@ -18,6 +23,7 @@ import net.minecraft.commands.CommandBuildContext; import net.minecraft.commands.CommandSourceStack; import net.minecraft.nbt.CompoundTag; +import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.MinecraftServer; import net.minecraft.server.level.ServerPlayer; @@ -40,7 +46,10 @@ @SuppressWarnings("unused") public class GcaExtension implements CarpetExtension, ModInitializer { - public static final Gson GSON = new GsonBuilder().setPrettyPrinting().create(); + public static final Gson GSON = new GsonBuilder() + .setPrettyPrinting() + .registerTypeHierarchyAdapter(ResourceKey.class, new DimTypeSerializer()) + .create(); public static String MOD_ID = "gca"; public static final Logger LOGGER = LogManager.getLogger(MOD_ID); @@ -70,7 +79,7 @@ public void onPlayerLoggedOut(ServerPlayer player) { @Override public void onGameStarted() { CarpetServer.settingsManager.parseSettingsClass(GcaSetting.class); - BotCommand.init(CarpetServer.minecraft_server); + BotCommand.BOT_INFO.init(CarpetServer.minecraft_server); } @Override @@ -109,6 +118,7 @@ public void onServerLoadedWorlds(MinecraftServer server) { } catch (IOException e) { GcaExtension.LOGGER.error(e.getMessage(), e); } + //noinspection ResultOfMethodCallIgnored file.delete(); } } @@ -116,6 +126,10 @@ public void onServerLoadedWorlds(MinecraftServer server) { @Override public void registerCommands(CommandDispatcher dispatcher, CommandBuildContext commandBuildContext) { BotCommand.register(dispatcher); + LocCommand.register(dispatcher); + HereCommand.register(dispatcher); + WhereisCommand.register(dispatcher); + TodoCommand.register(dispatcher); } @Override diff --git a/src/main/java/dev/dubhe/gugle/carpet/GcaSetting.java b/src/main/java/dev/dubhe/gugle/carpet/GcaSetting.java index f9e44bd..3f2bf1b 100644 --- a/src/main/java/dev/dubhe/gugle/carpet/GcaSetting.java +++ b/src/main/java/dev/dubhe/gugle/carpet/GcaSetting.java @@ -1,12 +1,15 @@ package dev.dubhe.gugle.carpet; +import carpet.CarpetSettings; import carpet.api.settings.Rule; +import carpet.api.settings.Validators; public class GcaSetting { public static final String GCA = "GCA"; public static final String EXPERIMENTAL = "experimental"; public static final String BOT = "BOT"; + public static final String COMMAND = "command"; // 允许玩家打开假人背包 @Rule( @@ -68,9 +71,43 @@ public class GcaSetting { // 方便快捷的假人管理菜单 @Rule( - categories = {GCA, EXPERIMENTAL, BOT} + categories = {GCA, EXPERIMENTAL, BOT, COMMAND}, + options = {"ops", "0", "1", "2", "3", "4", "true", "false"}, + validators = Validators.CommandLevel.class ) - public static boolean commandBot = false; + public static String commandBot = "ops"; + + // 待办事项清单 + @Rule( + categories = {GCA, EXPERIMENTAL, BOT, COMMAND}, + options = {"ops", "0", "1", "2", "3", "4", "true", "false"}, + validators = Validators.CommandLevel.class + ) + public static String commandTodo = "ops"; + + // 快速发送坐标 + @Rule( + categories = {GCA, EXPERIMENTAL, BOT, COMMAND}, + options = {"ops", "0", "1", "2", "3", "4", "true", "false"}, + validators = Validators.CommandLevel.class + ) + public static String commandHere = "ops"; + + // 快速定位玩家 + @Rule( + categories = {GCA, EXPERIMENTAL, BOT, COMMAND}, + options = {"ops", "0", "1", "2", "3", "4", "true", "false"}, + validators = Validators.CommandLevel.class + ) + public static String commandWhereis = "ops"; + + // 地标管理菜单 + @Rule( + categories = {GCA, EXPERIMENTAL, BOT, COMMAND}, + options = {"ops", "0", "1", "2", "3", "4", "true", "false"}, + validators = Validators.CommandLevel.class + ) + public static String commandLoc = "ops"; // 让放置的栅栏门与你点击的栅栏门拥有相同的方块状态 @Rule( diff --git a/src/main/java/dev/dubhe/gugle/carpet/commands/BotCommand.java b/src/main/java/dev/dubhe/gugle/carpet/commands/BotCommand.java index 1db5d2e..75afa0e 100644 --- a/src/main/java/dev/dubhe/gugle/carpet/commands/BotCommand.java +++ b/src/main/java/dev/dubhe/gugle/carpet/commands/BotCommand.java @@ -2,16 +2,8 @@ import carpet.fakes.ServerPlayerInterface; import carpet.patches.EntityPlayerMPFake; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.JsonDeserializationContext; -import com.google.gson.JsonDeserializer; -import com.google.gson.JsonElement; +import carpet.utils.CommandHelper; import com.google.gson.JsonObject; -import com.google.gson.JsonParseException; -import com.google.gson.JsonPrimitive; -import com.google.gson.JsonSerializationContext; -import com.google.gson.JsonSerializer; import com.google.gson.annotations.SerializedName; import com.mojang.brigadier.CommandDispatcher; import com.mojang.brigadier.arguments.IntegerArgumentType; @@ -23,50 +15,33 @@ import dev.dubhe.gugle.carpet.GcaExtension; import dev.dubhe.gugle.carpet.GcaSetting; import dev.dubhe.gugle.carpet.tools.FakePlayerSerializer; +import dev.dubhe.gugle.carpet.tools.FilesUtil; import net.minecraft.ChatFormatting; import net.minecraft.commands.CommandSourceStack; import net.minecraft.commands.Commands; import net.minecraft.commands.SharedSuggestionProvider; import net.minecraft.commands.arguments.EntityArgument; -import net.minecraft.core.registries.Registries; import net.minecraft.network.chat.ClickEvent; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.HoverEvent; import net.minecraft.network.chat.MutableComponent; import net.minecraft.network.chat.Style; import net.minecraft.resources.ResourceKey; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.server.MinecraftServer; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.level.GameType; import net.minecraft.world.level.Level; -import net.minecraft.world.level.storage.LevelResource; import net.minecraft.world.phys.Vec2; import net.minecraft.world.phys.Vec3; import org.jetbrains.annotations.NotNull; -import java.io.BufferedReader; -import java.io.BufferedWriter; -import java.io.File; -import java.io.IOException; -import java.lang.reflect.Type; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.util.HashMap; -import java.util.Map; import java.util.concurrent.CompletableFuture; public class BotCommand { - private static MinecraftServer server = null; - private static final Map BOT_INFO_MAP = new HashMap<>(); - private static final String BOT_GCA_JSON = "bot.gca.json"; - private static final Gson GSON = new GsonBuilder() - .setPrettyPrinting() - .registerTypeHierarchyAdapter(ResourceKey.class, new DimTypeSerializer()) - .create(); + public static final FilesUtil BOT_INFO = new FilesUtil<>("bot", Object::toString, BotInfo.class); public static void register(@NotNull CommandDispatcher dispatcher) { - dispatcher.register(Commands.literal("bot").requires(sourceStack -> GcaSetting.commandBot) + dispatcher.register(Commands.literal("bot") + .requires(stack -> CommandHelper.canUseCommand(stack, GcaSetting.commandBot)) .then( Commands.literal("list").executes(BotCommand::listBot) .then( @@ -79,7 +54,7 @@ public static void register(@NotNull CommandDispatcher dispa .then( Commands.argument("player", EntityArgument.player()) .then( - Commands.argument("desc", StringArgumentType.string()) + Commands.argument("desc", StringArgumentType.greedyString()) .executes(BotCommand::addBot) ) ) @@ -104,7 +79,7 @@ public static void register(@NotNull CommandDispatcher dispa } private static int listBot(CommandContext context) { - BotCommand.init(context); + BOT_INFO.init(context); int page; try { page = IntegerArgumentType.getInteger(context, "page"); @@ -112,11 +87,11 @@ private static int listBot(CommandContext context) { page = 1; } final int pageSize = 8; - int size = BOT_INFO_MAP.size(); + int size = BOT_INFO.map.size(); int maxPage = size / pageSize + 1; - BotInfo[] botInfos = BOT_INFO_MAP.values().toArray(new BotInfo[0]); + BotInfo[] botInfos = BOT_INFO.map.values().toArray(new BotInfo[0]); context.getSource().sendSystemMessage( - Component.literal("===== Bot List (Page %s/%s) =====".formatted(page, maxPage)) + Component.literal("======= Bot List (Page %s/%s) =======".formatted(page, maxPage)) .withStyle(ChatFormatting.YELLOW) ); for (int i = (page - 1) * pageSize; i < size && i < page * pageSize; i++) { @@ -179,21 +154,21 @@ private static MutableComponent botToComponent(BotInfo botInfo) { } private static int loadBot(CommandContext context) { - BotCommand.init(context); + BOT_INFO.init(context); CommandSourceStack source = context.getSource(); String name = StringArgumentType.getString(context, "player"); - if (server.getPlayerList().getPlayerByName(name) != null) { + if (FilesUtil.server.getPlayerList().getPlayerByName(name) != null) { source.sendFailure(Component.literal("player %s is already exist.".formatted(name))); return 0; } - BotInfo botInfo = BOT_INFO_MAP.getOrDefault(name, null); + BotInfo botInfo = BOT_INFO.map.getOrDefault(name, null); if (botInfo == null) { source.sendFailure(Component.literal("%s is not exist.")); return 0; } boolean success = EntityPlayerMPFake.createFake( name, - server, + FilesUtil.server, botInfo.pos, botInfo.facing.y, botInfo.facing.x, @@ -217,7 +192,7 @@ private static int loadBot(CommandContext context) { } private static int addBot(CommandContext context) throws CommandSyntaxException { - BotCommand.init(context); + BOT_INFO.init(context); CommandSourceStack source = context.getSource(); ServerPlayer p; if (!((p = EntityArgument.getPlayer(context, "player")) instanceof EntityPlayerMPFake player)) { @@ -225,11 +200,11 @@ private static int addBot(CommandContext context) throws Com return 0; } String name = player.getGameProfile().getName(); - if (BOT_INFO_MAP.containsKey(name)) { + if (BOT_INFO.map.containsKey(name)) { source.sendFailure(Component.literal("%s is already save.".formatted(name))); return 0; } - BotCommand.BOT_INFO_MAP.put( + BotCommand.BOT_INFO.map.put( name, new BotInfo( name, @@ -242,53 +217,26 @@ private static int addBot(CommandContext context) throws Com FakePlayerSerializer.actionPackToJson(((ServerPlayerInterface) player).getActionPack()) ) ); - BotCommand.save(); + BOT_INFO.save(); source.sendSuccess(() -> Component.literal("%s is added.".formatted(name)), false); return 1; } private static int removeBot(CommandContext context) { - BotCommand.init(context); + BOT_INFO.init(context); String name = StringArgumentType.getString(context, "player"); - BotCommand.BOT_INFO_MAP.remove(name); - CommandSourceStack source = context.getSource(); - source.sendSuccess(() -> Component.literal("%s is removed.".formatted(name)), false); - BotCommand.save(); - return 1; - } - - private static void init(@NotNull CommandContext context) { - MinecraftServer server1 = context.getSource().getServer(); - BotCommand.init(server1); - } - - public static void init(MinecraftServer server1) { - if (server1 == server) return; - BotCommand.server = server1; - BotCommand.BOT_INFO_MAP.clear(); - File file = BotCommand.server.getWorldPath(LevelResource.ROOT).resolve(BotCommand.BOT_GCA_JSON).toFile(); - if (!file.isFile()) return; - try (BufferedReader bfr = Files.newBufferedReader(file.toPath(), StandardCharsets.UTF_8)) { - for (Map.Entry entry : BotCommand.GSON.fromJson(bfr, JsonObject.class).entrySet()) { - BotCommand.BOT_INFO_MAP.put(entry.getKey(), BotCommand.GSON.fromJson(entry.getValue(), BotInfo.class)); - } - } catch (IOException e) { - GcaExtension.LOGGER.error(e.getMessage(), e); + BotInfo remove = BotCommand.BOT_INFO.map.remove(name); + if (remove == null) { + context.getSource().sendFailure(Component.literal("Bot %s is not exist.".formatted(name))); + return 0; } + context.getSource().sendSuccess(() -> Component.literal("%s is removed.".formatted(name)), false); + BOT_INFO.save(); + return 1; } private static @NotNull CompletableFuture suggestPlayer(final CommandContext context, final SuggestionsBuilder builder) { - return SharedSuggestionProvider.suggest(BOT_INFO_MAP.keySet(), builder); - } - - private static void save() { - if (BotCommand.server == null) return; - File file = BotCommand.server.getWorldPath(LevelResource.ROOT).resolve(BotCommand.BOT_GCA_JSON).toFile(); - try (BufferedWriter bw = Files.newBufferedWriter(file.toPath(), StandardCharsets.UTF_8)) { - BotCommand.GSON.toJson(BotCommand.BOT_INFO_MAP, bw); - } catch (IOException e) { - GcaExtension.LOGGER.error(e.getMessage(), e); - } + return SharedSuggestionProvider.suggest(BOT_INFO.map.keySet(), builder); } public record BotInfo( @@ -302,16 +250,4 @@ public record BotInfo( JsonObject actions ) { } - - public static class DimTypeSerializer implements JsonSerializer>, JsonDeserializer> { - @Override - public ResourceKey deserialize(@NotNull JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { - return ResourceKey.create(Registries.DIMENSION, ResourceLocation.parse(json.getAsString())); - } - - @Override - public JsonElement serialize(@NotNull ResourceKey src, Type typeOfSrc, JsonSerializationContext context) { - return new JsonPrimitive(src.location().toString()); - } - } } diff --git a/src/main/java/dev/dubhe/gugle/carpet/commands/HereCommand.java b/src/main/java/dev/dubhe/gugle/carpet/commands/HereCommand.java new file mode 100644 index 0000000..3b33050 --- /dev/null +++ b/src/main/java/dev/dubhe/gugle/carpet/commands/HereCommand.java @@ -0,0 +1,78 @@ +package dev.dubhe.gugle.carpet.commands; + +import carpet.utils.CommandHelper; +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.context.CommandContext; +import dev.dubhe.gugle.carpet.GcaSetting; +import net.minecraft.ChatFormatting; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.Commands; +import net.minecraft.network.chat.ClickEvent; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.HoverEvent; +import net.minecraft.network.chat.MutableComponent; +import net.minecraft.network.chat.Style; +import net.minecraft.resources.ResourceKey; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.level.Level; +import net.minecraft.world.phys.Vec3; +import org.jetbrains.annotations.NotNull; + +public class HereCommand { + public static void register(@NotNull CommandDispatcher dispatcher) { + dispatcher.register( + Commands.literal("here") + .requires(stack -> CommandHelper.canUseCommand(stack, GcaSetting.commandHere)) + .executes(HereCommand::execute) + ); + } + + public static int execute(CommandContext context) { + MinecraftServer server = context.getSource().getServer(); + CommandSourceStack source = context.getSource(); + if (!source.isPlayer()) return 0; + ServerPlayer player = source.getPlayer(); + if (player == null) return 0; + server.getPlayerList().broadcastSystemMessage(HereCommand.playerPos(player), false); + return 1; + } + + public static MutableComponent playerPos(ServerPlayer player) { + Vec3 position = player.position(); + ResourceKey dimension = player.level().dimension(); + String name = player.getGameProfile().getName(); + MutableComponent pos = Component.literal("[%.2f, %.2f, %.2f]".formatted(position.x, position.y, position.z)).withStyle( + Style.EMPTY + .applyFormat( + dimension == Level.OVERWORLD ? + ChatFormatting.GREEN : + dimension == Level.NETHER ? + ChatFormatting.RED : + dimension == Level.END ? + ChatFormatting.LIGHT_PURPLE : + ChatFormatting.AQUA + ) + .withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, Component.literal(dimension.location().toString()))) + ); + MutableComponent addMap = Component.literal("[+X]").withStyle( + Style.EMPTY + .applyFormat(ChatFormatting.GREEN) + .withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, Component.literal("Add to Xaero's minimap"))) + .withClickEvent(new ClickEvent( + ClickEvent.Action.RUN_COMMAND, + "/xaero_waypoint_add:%s:%s:%s:%s:%s:0:false:0:Internal_%s_waypoints".formatted( + name, + name.substring(0, 1), + (int) position.x, + (int) position.y, + (int) position.z, + dimension.location().getPath() + ) + )) + ); + return Component.literal("%s at".formatted(name)) + .append(" ").append(pos) + .append(" ").append(addMap); + } +} diff --git a/src/main/java/dev/dubhe/gugle/carpet/commands/LocCommand.java b/src/main/java/dev/dubhe/gugle/carpet/commands/LocCommand.java new file mode 100644 index 0000000..ececc86 --- /dev/null +++ b/src/main/java/dev/dubhe/gugle/carpet/commands/LocCommand.java @@ -0,0 +1,226 @@ +package dev.dubhe.gugle.carpet.commands; + +import carpet.utils.CommandHelper; +import com.google.gson.annotations.SerializedName; +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.arguments.IntegerArgumentType; +import com.mojang.brigadier.arguments.LongArgumentType; +import com.mojang.brigadier.arguments.StringArgumentType; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.brigadier.suggestion.Suggestions; +import com.mojang.brigadier.suggestion.SuggestionsBuilder; +import dev.dubhe.gugle.carpet.GcaExtension; +import dev.dubhe.gugle.carpet.GcaSetting; +import dev.dubhe.gugle.carpet.tools.FilesUtil; +import dev.dubhe.gugle.carpet.tools.IdGenerator; +import net.minecraft.ChatFormatting; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.Commands; +import net.minecraft.commands.SharedSuggestionProvider; +import net.minecraft.commands.arguments.DimensionArgument; +import net.minecraft.commands.arguments.coordinates.Vec3Argument; +import net.minecraft.network.chat.ClickEvent; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.HoverEvent; +import net.minecraft.network.chat.MutableComponent; +import net.minecraft.network.chat.Style; +import net.minecraft.resources.ResourceKey; +import net.minecraft.world.level.Level; +import net.minecraft.world.phys.Vec3; +import org.jetbrains.annotations.NotNull; + +import java.util.concurrent.CompletableFuture; + +public class LocCommand { + public static final FilesUtil LOC_POINT = new FilesUtil<>("loc", Long::decode, LocPoint.class); + + public static void register(@NotNull CommandDispatcher dispatcher) { + dispatcher.register( + Commands.literal("loc") + .requires(stack -> CommandHelper.canUseCommand(stack, GcaSetting.commandLoc)) + .executes(LocCommand::list) + .then( + Commands.literal("add") + .then( + Commands.argument("desc", StringArgumentType.greedyString()) + .executes(LocCommand::add) + .then( + Commands.argument("pos", Vec3Argument.vec3()) + .executes(LocCommand::add) + .then( + Commands.argument("dim", DimensionArgument.dimension()) + .executes(LocCommand::add) + ) + ) + ) + ) + .then( + Commands.literal("remove") + .then( + Commands.argument("id", LongArgumentType.longArg()) + .suggests(LocCommand::suggestId) + .executes(LocCommand::remove) + ) + ) + .then( + Commands.literal("list") + .executes(LocCommand::list) + .then( + Commands.argument("page", IntegerArgumentType.integer(1)) + .executes(LocCommand::list) + ) + ) + ); + } + + private static @NotNull CompletableFuture suggestId( + final CommandContext context, + final SuggestionsBuilder builder + ) { + return SharedSuggestionProvider.suggest(LOC_POINT.map.keySet().stream().map(Object::toString), builder); + } + + public static int add(CommandContext context) { + LOC_POINT.init(context); + CommandSourceStack source = context.getSource(); + long id = IdGenerator.nextId(); + String desc = StringArgumentType.getString(context, "desc"); + Vec3 pos; + try { + pos = Vec3Argument.getVec3(context, "pos"); + } catch (IllegalArgumentException ignored) { + pos = source.getPosition(); + } + ResourceKey dim; + try { + dim = DimensionArgument.getDimension(context, "dim").dimension(); + } catch (IllegalArgumentException ignored) { + dim = source.getLevel().dimension(); + } catch (CommandSyntaxException e) { + GcaExtension.LOGGER.error(e.getMessage(), e); + dim = source.getLevel().dimension(); + } + LOC_POINT.map.put(id, new LocPoint(id, desc, pos.x, pos.y, pos.z, dim)); + LOC_POINT.save(); + source.sendSuccess(() -> Component.literal("Loc %s is added.".formatted(desc)), false); + return 1; + } + + public static int remove(CommandContext context) { + LOC_POINT.init(context); + Long id = LongArgumentType.getLong(context, "id"); + LocPoint remove = LOC_POINT.map.remove(id); + if (remove == null) { + context.getSource().sendFailure(Component.literal("No such loc id %s".formatted(id))); + return 0; + } + LOC_POINT.save(); + context.getSource().sendSuccess(() -> Component.literal("Loc %s is removed.".formatted(remove.desc)), false); + return 1; + } + + public static int list(CommandContext context) { + LOC_POINT.init(context); + int page; + try { + page = IntegerArgumentType.getInteger(context, "page"); + } catch (IllegalArgumentException ignored) { + page = 1; + } + final int pageSize = 8; + int size = LOC_POINT.map.size(); + int maxPage = size / pageSize + 1; + LocPoint[] locPoints = LOC_POINT.map.values().toArray(new LocPoint[0]); + context.getSource().sendSystemMessage( + Component.literal("======= Loc List (Page %s/%s) =======".formatted(page, maxPage)) + .withStyle(ChatFormatting.YELLOW) + ); + for (int i = (page - 1) * pageSize; i < size && i < page * pageSize; i++) { + context.getSource().sendSystemMessage(locToComponent(locPoints[i])); + } + Component prevPage = page <= 1 ? + Component.literal("<<<").withStyle(ChatFormatting.GRAY) : + Component.literal("<<<").withStyle( + Style.EMPTY + .applyFormat(ChatFormatting.GREEN) + .withClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/loc list " + (page - 1))) + ); + Component nextPage = page >= maxPage ? + Component.literal(">>>").withStyle(ChatFormatting.GRAY) : + Component.literal(">>>").withStyle( + Style.EMPTY + .applyFormat(ChatFormatting.GREEN) + .withClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/loc list " + (page + 1))) + ); + context.getSource().sendSystemMessage( + Component.literal("=======") + .withStyle(ChatFormatting.YELLOW) + .append(" ") + .append(prevPage) + .append(" ") + .append(Component.literal("(Loc %s/%s)".formatted(page, maxPage)).withStyle(ChatFormatting.YELLOW)) + .append(" ") + .append(nextPage) + .append(" ") + .append(Component.literal("=======").withStyle(ChatFormatting.YELLOW)) + ); + return 1; + } + + private static @NotNull MutableComponent locToComponent(LocPoint locPoint) { + MutableComponent component = Component.literal(locPoint.desc).withStyle( + Style.EMPTY + .applyFormat(ChatFormatting.GRAY) + .withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, Component.literal(Long.toString(locPoint.id)))) + ); + MutableComponent pos = Component.literal("[%.2f, %.2f, %.2f]".formatted(locPoint.x, locPoint.y, locPoint.z)).withStyle( + Style.EMPTY + .applyFormat( + locPoint.dimType == Level.OVERWORLD ? + ChatFormatting.GREEN : + locPoint.dimType == Level.NETHER ? + ChatFormatting.RED : + locPoint.dimType == Level.END ? + ChatFormatting.LIGHT_PURPLE : + ChatFormatting.AQUA + ) + .withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, Component.literal(locPoint.dimType.location().toString()))) + ); + MutableComponent addMap = Component.literal("[+X]").withStyle( + Style.EMPTY + .applyFormat(ChatFormatting.GREEN) + .withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, Component.literal("Add to Xaero's minimap"))) + .withClickEvent(new ClickEvent( + ClickEvent.Action.RUN_COMMAND, + "/xaero_waypoint_add:%s:%s:%s:%s:%s:0:false:0:Internal_%s_waypoints".formatted( + locPoint.desc, + locPoint.desc.substring(0, 1), + (int) locPoint.x, + (int) locPoint.y, + (int) locPoint.z, + locPoint.dimType.location().getPath() + ) + )) + ); + MutableComponent remove = Component.literal("[\uD83D\uDDD1]").withStyle( + Style.EMPTY + .applyFormat(ChatFormatting.RED) + .withClickEvent(new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, "/loc remove %s".formatted(locPoint.id))) + ); + return Component.literal("▶ ").append(component) + .append(" ").append(pos) + .append(" ").append(addMap) + .append(" ").append(remove); + } + + public record LocPoint( + long id, + String desc, + double x, + double y, + double z, + @SerializedName("dim_type") ResourceKey dimType + ) { + } +} diff --git a/src/main/java/dev/dubhe/gugle/carpet/commands/TodoCommand.java b/src/main/java/dev/dubhe/gugle/carpet/commands/TodoCommand.java new file mode 100644 index 0000000..81c36e9 --- /dev/null +++ b/src/main/java/dev/dubhe/gugle/carpet/commands/TodoCommand.java @@ -0,0 +1,215 @@ +package dev.dubhe.gugle.carpet.commands; + +import carpet.utils.CommandHelper; +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.arguments.BoolArgumentType; +import com.mojang.brigadier.arguments.IntegerArgumentType; +import com.mojang.brigadier.arguments.LongArgumentType; +import com.mojang.brigadier.arguments.StringArgumentType; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.suggestion.Suggestions; +import com.mojang.brigadier.suggestion.SuggestionsBuilder; +import dev.dubhe.gugle.carpet.GcaSetting; +import dev.dubhe.gugle.carpet.tools.FilesUtil; +import dev.dubhe.gugle.carpet.tools.IdGenerator; +import net.minecraft.ChatFormatting; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.Commands; +import net.minecraft.commands.SharedSuggestionProvider; +import net.minecraft.network.chat.ClickEvent; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.HoverEvent; +import net.minecraft.network.chat.MutableComponent; +import net.minecraft.network.chat.Style; +import org.jetbrains.annotations.NotNull; + +import java.util.concurrent.CompletableFuture; + +public class TodoCommand { + public static final FilesUtil TODO = new FilesUtil<>("todo", Long::decode, Todo.class); + + public static void register(@NotNull CommandDispatcher dispatcher) { + dispatcher.register( + Commands.literal("todo") + .requires(stack -> CommandHelper.canUseCommand(stack, GcaSetting.commandTodo)) + .executes(TodoCommand::list) + .then( + Commands.literal("add") + .then( + Commands.argument("desc", StringArgumentType.greedyString()) + .executes(TodoCommand::add) + ) + ) + .then( + Commands.literal("remove") + .then( + Commands.argument("id", LongArgumentType.longArg()) + .suggests(TodoCommand::suggestId) + .executes(TodoCommand::remove) + ) + ) + .then( + Commands.literal("list") + .executes(TodoCommand::list) + .then( + Commands.argument("page", IntegerArgumentType.integer(1)) + .executes(TodoCommand::list) + ) + ) + .then( + Commands.literal("success") + .then( + Commands.argument("id", LongArgumentType.longArg()) + .suggests(TodoCommand::suggestId) + .executes(TodoCommand::success) + .then( + Commands.argument("success", BoolArgumentType.bool()) + .executes(TodoCommand::success) + ) + ) + ) + ); + } + + private static @NotNull CompletableFuture suggestId( + final CommandContext context, + final SuggestionsBuilder builder + ) { + return SharedSuggestionProvider.suggest(TODO.map.keySet().stream().map(Object::toString), builder); + } + + + public static int add(CommandContext context) { + TODO.init(context); + CommandSourceStack source = context.getSource(); + long id = IdGenerator.nextId(); + String desc = StringArgumentType.getString(context, "desc"); + TODO.map.put(id, new Todo(id, desc, false)); + TODO.save(); + source.sendSuccess(() -> Component.literal("Todo %s is added.".formatted(desc)), false); + return 1; + } + + public static int remove(CommandContext context) { + TODO.init(context); + Long id = LongArgumentType.getLong(context, "id"); + Todo todo = TODO.map.remove(id); + if (todo == null) { + context.getSource().sendFailure(Component.literal("No such todo id %s".formatted(id))); + return 0; + } + TODO.save(); + context.getSource().sendSuccess(() -> Component.literal("Todo %s is removed.".formatted(todo.desc)), false); + return 1; + } + + public static int success(CommandContext context) { + TODO.init(context); + Long id = LongArgumentType.getLong(context, "id"); + boolean success; + try { + success = BoolArgumentType.getBool(context, "success"); + } catch (IllegalArgumentException ignored) { + success = true; + } + Todo todo = TODO.map.get(id); + if (todo == null) { + context.getSource().sendFailure(Component.literal("No such todo id %s".formatted(id))); + return 0; + } + todo.success = success; + TODO.save(); + boolean finalSuccess = success; + context.getSource().sendSuccess(() -> Component.literal("Todo %s has be %s.".formatted(todo.desc, finalSuccess ? "done" : "undone")), false); + return 1; + } + + public static int list(CommandContext context) { + TODO.init(context); + int page; + try { + page = IntegerArgumentType.getInteger(context, "page"); + } catch (IllegalArgumentException ignored) { + page = 1; + } + final int pageSize = 8; + int size = TODO.map.size(); + int maxPage = size / pageSize + 1; + Todo[] todos = TODO.map.values().toArray(new Todo[0]); + context.getSource().sendSystemMessage( + Component.literal("======= Todo List (Page %s/%s) =======".formatted(page, maxPage)) + .withStyle(ChatFormatting.YELLOW) + ); + for (int i = (page - 1) * pageSize; i < size && i < page * pageSize; i++) { + context.getSource().sendSystemMessage(TodoToComponent(todos[i])); + } + Component prevPage = page <= 1 ? + Component.literal("<<<").withStyle(ChatFormatting.GRAY) : + Component.literal("<<<").withStyle( + Style.EMPTY + .applyFormat(ChatFormatting.GREEN) + .withClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/todo list " + (page - 1))) + ); + Component nextPage = page >= maxPage ? + Component.literal(">>>").withStyle(ChatFormatting.GRAY) : + Component.literal(">>>").withStyle( + Style.EMPTY + .applyFormat(ChatFormatting.GREEN) + .withClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/todo list " + (page + 1))) + ); + context.getSource().sendSystemMessage( + Component.literal("=======") + .withStyle(ChatFormatting.YELLOW) + .append(" ") + .append(prevPage) + .append(" ") + .append(Component.literal("(Todo %s/%s)".formatted(page, maxPage)).withStyle(ChatFormatting.YELLOW)) + .append(" ") + .append(nextPage) + .append(" ") + .append(Component.literal("=======").withStyle(ChatFormatting.YELLOW)) + ); + return 1; + } + + private static @NotNull MutableComponent TodoToComponent(Todo todo) { + MutableComponent component = Component.literal(todo.desc).withStyle( + Style.EMPTY + .withStrikethrough(todo.success) + .applyFormat(ChatFormatting.GRAY) + .withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, Component.literal(Long.toString(todo.id)))) + ); + MutableComponent success = Component.literal("[✔]").withStyle( + Style.EMPTY + .applyFormat(ChatFormatting.GREEN) + .withClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/todo success %s".formatted(todo.id))) + ); + MutableComponent unSuccess = Component.literal("[❌]").withStyle( + Style.EMPTY + .applyFormat(ChatFormatting.RED) + .withClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/todo success %s false".formatted(todo.id))) + ); + MutableComponent remove = Component.literal("[\uD83D\uDDD1]").withStyle( + Style.EMPTY + .applyFormat(ChatFormatting.RED) + .withClickEvent(new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, "/todo remove %s".formatted(todo.id))) + ); + return Component.literal(todo.success ? "☑" : "☐") + .append(" ").append(component) + .append(" ").append(todo.success ? unSuccess : success) + .append(" ").append(remove); + } + + + public static class Todo { + public final Long id; + public final String desc; + public boolean success; + + Todo(Long id, String desc, boolean success) { + this.id = id; + this.desc = desc; + this.success = success; + } + } +} diff --git a/src/main/java/dev/dubhe/gugle/carpet/commands/WhereisCommand.java b/src/main/java/dev/dubhe/gugle/carpet/commands/WhereisCommand.java new file mode 100644 index 0000000..b774c13 --- /dev/null +++ b/src/main/java/dev/dubhe/gugle/carpet/commands/WhereisCommand.java @@ -0,0 +1,31 @@ +package dev.dubhe.gugle.carpet.commands; + +import carpet.utils.CommandHelper; +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import dev.dubhe.gugle.carpet.GcaSetting; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.Commands; +import net.minecraft.commands.arguments.EntityArgument; +import net.minecraft.server.level.ServerPlayer; +import org.jetbrains.annotations.NotNull; + +public class WhereisCommand { + public static void register(@NotNull CommandDispatcher dispatcher) { + dispatcher.register( + Commands.literal("whereis") + .requires(stack -> CommandHelper.canUseCommand(stack, GcaSetting.commandWhereis)) + .then( + Commands.argument("player", EntityArgument.player()) + .executes(WhereisCommand::execute) + ) + ); + } + + public static int execute(CommandContext context) throws CommandSyntaxException { + ServerPlayer player = EntityArgument.getPlayer(context, "player"); + context.getSource().sendSuccess(() -> HereCommand.playerPos(player), false); + return 1; + } +} diff --git a/src/main/java/dev/dubhe/gugle/carpet/mixin/WaypointSharingHandlerMixin.java b/src/main/java/dev/dubhe/gugle/carpet/mixin/WaypointSharingHandlerMixin.java new file mode 100644 index 0000000..e1ca630 --- /dev/null +++ b/src/main/java/dev/dubhe/gugle/carpet/mixin/WaypointSharingHandlerMixin.java @@ -0,0 +1,21 @@ +package dev.dubhe.gugle.carpet.mixin; + +import com.llamalad7.mixinextras.sugar.Local; +import dev.dubhe.gugle.carpet.GcaExtension; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import xaero.common.minimap.waypoints.WaypointSharingHandler; + +@Mixin(WaypointSharingHandler.class) +abstract class WaypointSharingHandlerMixin { + @Inject( + method = "onWaypointReceived", + at = @At(value = "INVOKE", target = "Lnet/minecraft/network/chat/MutableComponent;setStyle(Lnet/minecraft/network/chat/Style;)Lnet/minecraft/network/chat/MutableComponent;"), + remap = false + ) + private void onWaypointReceived(String playerName, String text, CallbackInfo ci, @Local(ordinal = 6) String addCommand) { + GcaExtension.LOGGER.info(addCommand); + } +} diff --git a/src/main/java/dev/dubhe/gugle/carpet/tools/DimTypeSerializer.java b/src/main/java/dev/dubhe/gugle/carpet/tools/DimTypeSerializer.java new file mode 100644 index 0000000..68a3697 --- /dev/null +++ b/src/main/java/dev/dubhe/gugle/carpet/tools/DimTypeSerializer.java @@ -0,0 +1,28 @@ +package dev.dubhe.gugle.carpet.tools; + +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonParseException; +import com.google.gson.JsonPrimitive; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; +import net.minecraft.core.registries.Registries; +import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.level.Level; +import org.jetbrains.annotations.NotNull; + +import java.lang.reflect.Type; + +public class DimTypeSerializer implements JsonSerializer>, JsonDeserializer> { + @Override + public ResourceKey deserialize(@NotNull JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { + return ResourceKey.create(Registries.DIMENSION, ResourceLocation.parse(json.getAsString())); + } + + @Override + public JsonElement serialize(@NotNull ResourceKey src, Type typeOfSrc, JsonSerializationContext context) { + return new JsonPrimitive(src.location().toString()); + } +} diff --git a/src/main/java/dev/dubhe/gugle/carpet/tools/FilesUtil.java b/src/main/java/dev/dubhe/gugle/carpet/tools/FilesUtil.java new file mode 100644 index 0000000..8ca0a09 --- /dev/null +++ b/src/main/java/dev/dubhe/gugle/carpet/tools/FilesUtil.java @@ -0,0 +1,66 @@ +package dev.dubhe.gugle.carpet.tools; + +import com.google.gson.Gson; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.mojang.brigadier.context.CommandContext; +import dev.dubhe.gugle.carpet.GcaExtension; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.server.MinecraftServer; +import net.minecraft.world.level.storage.LevelResource; +import org.jetbrains.annotations.NotNull; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.util.Map; +import java.util.TreeMap; +import java.util.function.Function; + +public class FilesUtil, V> { + public final Map map = new TreeMap<>(); + public static MinecraftServer server = null; + private final String gcaJson; + private static final Gson GSON = GcaExtension.GSON; + Function keyCodec; + Class vClass; + + public FilesUtil(String jsonPrefix, Function keyCodec, Class vClass) { + this.gcaJson = "%s.gca.json".formatted(jsonPrefix); + this.keyCodec = keyCodec; + this.vClass = vClass; + } + + public void init(@NotNull CommandContext context) { + MinecraftServer server1 = context.getSource().getServer(); + this.init(server1); + } + + public void init(MinecraftServer server1) { + if (server1 == server) return; + FilesUtil.server = server1; + this.map.clear(); + File file = FilesUtil.server.getWorldPath(LevelResource.ROOT).resolve(this.gcaJson).toFile(); + if (!file.isFile()) return; + try (BufferedReader bfr = Files.newBufferedReader(file.toPath(), StandardCharsets.UTF_8)) { + for (Map.Entry entry : FilesUtil.GSON.fromJson(bfr, JsonObject.class).entrySet()) { + this.map.put(keyCodec.apply(entry.getKey()), FilesUtil.GSON.fromJson(entry.getValue(), this.vClass)); + } + } catch (IOException e) { + GcaExtension.LOGGER.error(e.getMessage(), e); + } + } + + public void save() { + if (FilesUtil.server == null) return; + File file = FilesUtil.server.getWorldPath(LevelResource.ROOT).resolve(this.gcaJson).toFile(); + try (BufferedWriter bw = Files.newBufferedWriter(file.toPath(), StandardCharsets.UTF_8)) { + FilesUtil.GSON.toJson(this.map, bw); + } catch (IOException e) { + GcaExtension.LOGGER.error(e.getMessage(), e); + } + } +} diff --git a/src/main/java/dev/dubhe/gugle/carpet/tools/IdGenerator.java b/src/main/java/dev/dubhe/gugle/carpet/tools/IdGenerator.java new file mode 100644 index 0000000..d58b467 --- /dev/null +++ b/src/main/java/dev/dubhe/gugle/carpet/tools/IdGenerator.java @@ -0,0 +1,153 @@ +package dev.dubhe.gugle.carpet.tools; + +import dev.dubhe.gugle.carpet.GcaExtension; + +import java.net.NetworkInterface; +import java.net.SocketException; +import java.util.Enumeration; + +public class IdGenerator { + /** + * 工作id 也就是机器id + */ + private static final long workerId; + /** + * 数据中心id + */ + private static final long dataCenterId; + /** + * 序列号 + */ + private static long sequence; + /** + * 初始时间戳 + */ + private static final long startTimestamp = 1288834974657L; + /** + * 工作id长度为5位 + */ + private static final long workerIdBits = 5L; + /** + * 数据中心id长度为5位 + */ + private static final long dataCenterIdBits = 5L; + /** + * 工作id最大值 + */ + private static final long maxWorkerId = ~(-1L << workerIdBits); + /** + * 数据中心id最大值 + */ + private static final long maxDataCenterId = ~(-1L << dataCenterIdBits); + /** + * 序列号长度 + */ + private static final long sequenceBits = 12L; + /** + * 序列号最大值 + */ + private static final long sequenceMask = ~(-1L << sequenceBits); + /** + * 工作id需要左移的位数,12位 + */ + private static final long workerIdShift = sequenceBits; + /** + * 数据id需要左移位数 12+5=17位 + */ + private static final long dataCenterIdShift = sequenceBits + workerIdBits; + /** + * 时间戳需要左移位数 12+5+5=22位 + */ + private static final long timestampLeftShift = sequenceBits + workerIdBits + dataCenterIdBits; + /** + * 上次时间戳,初始值为负数 + */ + private static long lastTimestamp = -1L; + + static { + workerId = getMachineNum() & maxWorkerId; + dataCenterId = getMachineNum() & maxDataCenterId; + sequence = 0L; + } + + /** + * 获取机器编号 + * + * @return MachineNum + */ + private static long getMachineNum() { + long machinePiece; + StringBuilder sb = new StringBuilder(); + Enumeration e = null; + try { + e = NetworkInterface.getNetworkInterfaces(); + } catch (SocketException e1) { + GcaExtension.LOGGER.error(e1); + } + if (e != null) { + while (e.hasMoreElements()) { + NetworkInterface ni = e.nextElement(); + sb.append(ni.toString()); + } + } + machinePiece = sb.toString().hashCode(); + return machinePiece; + } + + //下一个ID生成算法 + public synchronized static long nextId() { + long timestamp = timeGen(); + // 获取当前时间戳如果小于上次时间戳,则表示时间戳获取出现异常 + if (timestamp < lastTimestamp) { + throw new RuntimeException(String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp)); + } + // 获取当前时间戳如果等于上次时间戳 + // 说明:还处在同一毫秒内,则在序列号加1;否则序列号赋值为0,从0开始。 + // 0 - 4095 + if (lastTimestamp == timestamp) { + sequence = (sequence + 1) & sequenceMask; + if (sequence == 0) { + timestamp = tilNextMillis(lastTimestamp); + } + } else { + sequence = 0; + } + //将上次时间戳值刷新 + lastTimestamp = timestamp; + /* + 返回结果: + (timestamp - twepoch) << timestampLeftShift) 表示将时间戳减去初始时间戳,再左移相应位数 + (datacenterId << datacenterIdShift) 表示将数据id左移相应位数 + (workerId << workerIdShift) 表示将工作id左移相应位数 + | 是按位或运算符,例如:x | y,只有当x,y都为0的时候结果才为0,其它情况结果都为1。 + 因为个部分只有相应位上的值有意义,其它位上都是0,所以将各部分的值进行 | 运算就能得到最终拼接好的id + */ + return ((timestamp - startTimestamp) << timestampLeftShift) | + (dataCenterId << dataCenterIdShift) | + (workerId << workerIdShift) | + sequence; + } + + /** + * 获取时间戳,并与上次时间戳比较 + * + * @param lastTimestamp 上次的时间戳,单位是毫秒 + * @return 时间戳 + */ + private static long tilNextMillis(long lastTimestamp) { + long timestamp = timeGen(); + while (timestamp <= lastTimestamp) { + timestamp = timeGen(); + } + return timestamp; + } + + /** + * 获取系统时间戳 + * + * @return 系统时间戳 + */ + private static long timeGen() { + return System.currentTimeMillis(); + } +} diff --git a/src/main/resources/assets/gca/lang/en_us.json b/src/main/resources/assets/gca/lang/en_us.json index 2ba3956..cbc099f 100644 --- a/src/main/resources/assets/gca/lang/en_us.json +++ b/src/main/resources/assets/gca/lang/en_us.json @@ -50,6 +50,18 @@ "carpet.rule.commandBot.name": "Bot Management", "carpet.rule.commandBot.desc": "A Bot Management Menu", + "carpet.rule.commandLoc.name": "Loc Management", + "carpet.rule.commandLoc.desc": "A Loc Management Menu", + + "carpet.rule.commandTodo.name": "Todo Management", + "carpet.rule.commandTodo.desc": "A Todo Management Menu", + + "carpet.rule.commandHere.name": "/here Command", + "carpet.rule.commandHere.desc": "Quickly send position", + + "carpet.rule.commandWhereis.name": "/whereis Command", + "carpet.rule.commandWhereis.desc": "Quickly locate players", + "gca.action.attack.interval.12": "Attack every 12 gt: %s", "gca.action.attack.continuous": "Attack continuous: %s", "gca.action.use.continuous": "Use continuous: %s", diff --git a/src/main/resources/assets/gca/lang/zh_cn.json b/src/main/resources/assets/gca/lang/zh_cn.json index 93c2464..959b521 100644 --- a/src/main/resources/assets/gca/lang/zh_cn.json +++ b/src/main/resources/assets/gca/lang/zh_cn.json @@ -50,6 +50,18 @@ "carpet.rule.commandBot.name": "假人管理", "carpet.rule.commandBot.desc": "一个假人管理菜单", + "carpet.rule.commandLoc.name": "地标管理", + "carpet.rule.commandLoc.desc": "地标管理菜单", + + "carpet.rule.commandTodo.name": "待办事项管理", + "carpet.rule.commandTodo.desc": "待办事项管理菜单", + + "carpet.rule.commandHere.name": "/here 命令", + "carpet.rule.commandHere.desc": "快速发送位置", + + "carpet.rule.commandWhereis.name": "/whereis 命令", + "carpet.rule.commandWhereis.desc": "快速定位玩家", + "gca.action.attack.interval.12": "间隔 12 gt攻击:%s", "gca.action.attack.continuous": "一直左键: %s", "gca.action.use.continuous": "一直右键:%s", diff --git a/src/main/resources/assets/gca/lang/zh_tw.json b/src/main/resources/assets/gca/lang/zh_tw.json index 4b3e4f0..041d1fb 100644 --- a/src/main/resources/assets/gca/lang/zh_tw.json +++ b/src/main/resources/assets/gca/lang/zh_tw.json @@ -50,6 +50,18 @@ "carpet.rule.commandBot.name": "Bot Management", "carpet.rule.commandBot.desc": "A Bot Management Menu", + "carpet.rule.commandLoc.name": "Loc Management", + "carpet.rule.commandLoc.desc": "A Loc Management Menu", + + "carpet.rule.commandTodo.name": "Todo Management", + "carpet.rule.commandTodo.desc": "A Todo Management Menu", + + "carpet.rule.commandHere.name": "/here Command", + "carpet.rule.commandHere.desc": "Quickly send position", + + "carpet.rule.commandWhereis.name": "/whereis Command", + "carpet.rule.commandWhereis.desc": "Quickly locate players", + "gca.action.attack.interval.12": "間隔 12 gt攻擊:%s", "gca.action.attack.continuous": "連續攻擊:%s", "gca.action.use.continuous": "連續使用:%s", diff --git a/src/main/resources/gca.mixins.json b/src/main/resources/gca.mixins.json index 7353143..7d24ab2 100644 --- a/src/main/resources/gca.mixins.json +++ b/src/main/resources/gca.mixins.json @@ -19,7 +19,8 @@ "PlayerMixin", "ServerLevelMixin", "ServerPlaceRecipeMixin", - "SignBlockMixin" + "SignBlockMixin", + "WaypointSharingHandlerMixin" ], "server": [ ],