diff --git a/src/main/java/io/ib67/sfcraft/module/room/CreativeRoomModule.java b/src/main/java/io/ib67/sfcraft/module/room/CreativeRoomModule.java index 6744f19..f08488a 100644 --- a/src/main/java/io/ib67/sfcraft/module/room/CreativeRoomModule.java +++ b/src/main/java/io/ib67/sfcraft/module/room/CreativeRoomModule.java @@ -1,6 +1,7 @@ package io.ib67.sfcraft.module.room; import com.google.inject.Inject; +import com.mojang.brigadier.arguments.IntegerArgumentType; import com.mojang.brigadier.Command; import com.mojang.brigadier.CommandDispatcher; import com.mojang.brigadier.builder.LiteralArgumentBuilder; @@ -29,10 +30,13 @@ import net.minecraft.commands.Commands; import net.minecraft.commands.SharedSuggestionProvider; import net.minecraft.commands.arguments.EntityArgument; import net.minecraft.commands.arguments.GameModeArgument; +import net.minecraft.commands.arguments.TimeArgument; +import net.minecraft.core.Holder; import net.minecraft.network.chat.Component; import net.minecraft.server.MinecraftServer; import net.minecraft.server.level.ServerPlayer; import net.minecraft.util.CommonColors; +import net.minecraft.world.clock.WorldClock; import net.minecraft.world.level.GameType; import net.minecraft.world.level.gamerules.GameRules; import org.jetbrains.annotations.NotNull; @@ -108,6 +112,20 @@ public class CreativeRoomModule extends ServerModule { LiteralArgumentBuilder.literal("playgrd") .requires(it -> it.isPlayer() && SFConsts.COMMAND_PLAYGROUND.hasPermission(it.getPlayer())) .executes(this::gotoPlayground) + .then(Commands.literal("time") + .requires(it -> it.isPlayer() && SFConsts.COMMAND_PLAYGROUND_TIME.hasPermission(it.getPlayer())) + .then(Commands.literal("set") + .then(Commands.argument("time", TimeArgument.time()) + .executes(ctx -> onSetTime(ctx, IntegerArgumentType.getInteger(ctx, "time"))))) + .then(Commands.literal("add") + .then(Commands.argument("time", TimeArgument.time(Integer.MIN_VALUE)) + .executes(ctx -> onAddTime(ctx, IntegerArgumentType.getInteger(ctx, "time"))))) + .then(Commands.literal("query").executes(this::onQueryTime)) + .then(Commands.literal("day").executes(ctx -> onSetNamedTime(ctx, 1000))) + .then(Commands.literal("noon").executes(ctx -> onSetNamedTime(ctx, 6000))) + .then(Commands.literal("night").executes(ctx -> onSetNamedTime(ctx, 13000))) + .then(Commands.literal("midnight").executes(ctx -> onSetNamedTime(ctx, 18000))) + ) ); dispatcher.register( Commands.literal("gm") @@ -170,13 +188,74 @@ public class CreativeRoomModule extends ServerModule { return 0; } + // ── /playgrd time ──────────────────────────────────────────────── + // 只操作游乐场自己的 WorldClock(sfcraft:playground),绝不触碰其它钟; + // 必须身处游乐场才能使用。时间值存原始 tick(可超 24000),展示时按 24000 取余。 + + private int onSetTime(CommandContext ctx, int ticks) { + var player = requirePlaygroundPlayer(ctx); + if (player == null) return Command.SINGLE_SUCCESS; + ctx.getSource().getServer().clockManager().setTotalTicks(playgroundClock(ctx.getSource()), ticks); + player.sendSystemMessage(Component.translatable("message.sfcraft.playground.time_set", wrapTime(ticks))); + return Command.SINGLE_SUCCESS; + } + + private int onAddTime(CommandContext ctx, int ticks) { + var player = requirePlaygroundPlayer(ctx); + if (player == null) return Command.SINGLE_SUCCESS; + ctx.getSource().getServer().clockManager().addTicks(playgroundClock(ctx.getSource()), ticks); + player.sendSystemMessage(Component.translatable("message.sfcraft.playground.time_added", wrapTime(ticks))); + return Command.SINGLE_SUCCESS; + } + + private int onQueryTime(CommandContext ctx) { + var player = requirePlaygroundPlayer(ctx); + if (player == null) return Command.SINGLE_SUCCESS; + long ticks = ctx.getSource().getServer().clockManager().getTotalTicks(playgroundClock(ctx.getSource())); + player.sendSystemMessage(Component.translatable("message.sfcraft.playground.time_query", wrapTime(ticks))); + return Command.SINGLE_SUCCESS; + } + + private int onSetNamedTime(CommandContext ctx, int ticks) { + var player = requirePlaygroundPlayer(ctx); + if (player == null) return Command.SINGLE_SUCCESS; + ctx.getSource().getServer().clockManager().setTotalTicks(playgroundClock(ctx.getSource()), ticks); + player.sendSystemMessage(Component.translatable("message.sfcraft.playground.time_set", wrapTime(ticks))); + return Command.SINGLE_SUCCESS; + } + + /** 游乐场世界钟;必须身处游乐场(此时世界必然已加载),从维度类型解析。 */ + private Holder playgroundClock(CommandSourceStack source) { + var level = source.getServer().getLevel(CreativeSpaceRoom.WORLD); + if (level == null) { + throw new IllegalStateException("playground level not loaded"); + } + return level.dimensionType().defaultClock() + .orElseThrow(() -> new IllegalStateException("playground dimension type has no default clock")); + } + + private ServerPlayer requirePlaygroundPlayer(CommandContext ctx) { + var p = ctx.getSource().getPlayer(); + if (p == null || !p.level().dimension().equals(CreativeSpaceRoom.WORLD)) { + if (p != null) { + p.sendSystemMessage(Component.translatable("message.sfcraft.playground.only_here")); + } + return null; + } + return p; + } + + private static int wrapTime(long ticks) { + return (int) Math.floorMod(ticks, 24000L); + } + public void onPlayerJoin(ServerPlayer player) { player.sendSystemMessage(Component.translatable("message.sfcraft.playground.welcome")); player.sendSystemMessage(Component.translatable("message.sfcraft.playground.features_available").withColor(CommonColors.WHITE) .append(Component.translatable("message.sfcraft.playground.features_list").withColor(Color.MAGENTA.getRGB()))); player.sendSystemMessage(Component.nullToEmpty(" ")); player.sendSystemMessage(Component.translatable("message.sfcraft.playground.upload_hint").withColor(CommonColors.GRAY) - .append(Component.literal("/upload schematic").withColor(CommonColors.BLUE).withStyle(it -> it.withUnderlined(true)))); + .append(Component.literal("/upload schematic").withColor(CommonColors.SOFT_YELLOW).withStyle(it -> it.withUnderlined(true)))); player.sendSystemMessage(Component.translatable("message.sfcraft.playground.mobs_no_escape").withColor(CommonColors.GRAY)); player.sendSystemMessage(Component.translatable("message.sfcraft.playground.how_to_leave").withColor(CommonColors.GRAY)); player.setGameMode(GameType.CREATIVE); diff --git a/src/main/java/io/ib67/sfcraft/util/SFConsts.java b/src/main/java/io/ib67/sfcraft/util/SFConsts.java index a556af2..75e60fb 100644 --- a/src/main/java/io/ib67/sfcraft/util/SFConsts.java +++ b/src/main/java/io/ib67/sfcraft/util/SFConsts.java @@ -24,6 +24,7 @@ public class SFConsts { public static final Permission COMMAND_PLAYGROUND = ofSFCPermission("command.playground", true); public static final Permission COMMAND_PLAYGROUND_GAMEMODE = ofSFCPermission("command.playground.gamemode", true); public static final Permission COMMAND_PLAYGROUND_TELEPORT = ofSFCPermission("command.playground.teleport", true); + public static final Permission COMMAND_PLAYGROUND_TIME = ofSFCPermission("command.playground.time", true); public static final Permission COMMAND_RECO = ofSFCPermission("command.reco", true); public static Permission ofSFCPermission(String key, boolean byDefault) { diff --git a/src/main/resources/assets/sfcraft/lang/en_us.json b/src/main/resources/assets/sfcraft/lang/en_us.json index b7865b2..53eaaec 100644 --- a/src/main/resources/assets/sfcraft/lang/en_us.json +++ b/src/main/resources/assets/sfcraft/lang/en_us.json @@ -53,10 +53,13 @@ "message.sfcraft.playground.only_here": "You can only use this in playground!", "message.sfcraft.playground.welcome": "You are now in the creative playground. Use //schem list to view available schematics.", "message.sfcraft.playground.features_available": "The following features are now available: ", - "message.sfcraft.playground.features_list": "/player /track /summon /setblock /gm (gamemode) /pt (teleport) /polymer creative and all WorldEdit commands.", + "message.sfcraft.playground.features_list": "/player, /track, /summon, /setblock, /gm (gamemode), /pt (teleport), /playgrd time, /polymer creative and all WorldEdit commands.", "message.sfcraft.playground.upload_hint": "How to upload schematics to the playground: ", "message.sfcraft.playground.mobs_no_escape": "Mobs in the playground cannot escape to other dimensions.", "message.sfcraft.playground.how_to_leave": "Use /reco or rejoin the game to leave.", + "message.sfcraft.playground.time_set": "Playground time set to %s", + "message.sfcraft.playground.time_added": "Playground time increased by %s", + "message.sfcraft.playground.time_query": "Playground time: %s", "message.sfcraft.schem.no_permission": "You don't have permission to use the upload feature. Please contact an administrator.", "message.sfcraft.schem.click_to_upload": "Click this URL to upload schematic files.", "message.sfcraft.schem.saved": "Schematic %s has been saved! Use //schem list to find it", diff --git a/src/main/resources/assets/sfcraft/lang/zh_cn.json b/src/main/resources/assets/sfcraft/lang/zh_cn.json index f265da1..52de19a 100644 --- a/src/main/resources/assets/sfcraft/lang/zh_cn.json +++ b/src/main/resources/assets/sfcraft/lang/zh_cn.json @@ -53,10 +53,13 @@ "message.sfcraft.playground.only_here": "只能在游乐园中使用此功能!", "message.sfcraft.playground.welcome": "你现在正在创造游乐园中,使用 //schem list 查看可用投影。", "message.sfcraft.playground.features_available": "以下功能现在可用:", - "message.sfcraft.playground.features_list": "/player /track /summon /setblock /gm (gamemode) /pt (teleport) 及 WorldEdit 的所有命令。", + "message.sfcraft.playground.features_list": "/player, /track, /summon, /setblock, /gm (gamemode), /pt (teleport), /playgrd time, /polymer creative 及 WorldEdit 的所有命令。", "message.sfcraft.playground.upload_hint": "如何上传投影到游乐场: ", "message.sfcraft.playground.mobs_no_escape": "游乐园中的生物不能逃逸到其他维度。", "message.sfcraft.playground.how_to_leave": "使用 /reco 或重新加入游戏即可离开。", + "message.sfcraft.playground.time_set": "已设置游乐园时间: %s", + "message.sfcraft.playground.time_added": "已增加游乐园时间: %s", + "message.sfcraft.playground.time_query": "游乐园当前时间: %s", "message.sfcraft.schem.no_permission": "你没有使用上传功能的权限!请联系管理员申请", "message.sfcraft.schem.click_to_upload": "点击此链接以上传投影文件。", "message.sfcraft.schem.saved": "投影 %s 已保存!使用 //schem list 查找它", diff --git a/src/main/resources/data/sfcraft/dimension/playground.json b/src/main/resources/data/sfcraft/dimension/playground.json index ef7c66b..f5efc16 100644 --- a/src/main/resources/data/sfcraft/dimension/playground.json +++ b/src/main/resources/data/sfcraft/dimension/playground.json @@ -1,5 +1,5 @@ { - "type": "overworld", + "type": "sfcraft:playground", "generator": { "type": "flat", "settings": { diff --git a/src/main/resources/data/sfcraft/dimension_type/playground.json b/src/main/resources/data/sfcraft/dimension_type/playground.json new file mode 100644 index 0000000..a173593 --- /dev/null +++ b/src/main/resources/data/sfcraft/dimension_type/playground.json @@ -0,0 +1,55 @@ +{ + "ambient_light": 0.0, + "attributes": { + "minecraft:audio/ambient_sounds": { + "mood": { + "block_search_extent": 8, + "offset": 2.0, + "sound": "minecraft:ambient.cave", + "tick_delay": 6000 + } + }, + "minecraft:audio/background_music": { + "creative": { + "max_delay": 24000, + "min_delay": 12000, + "sound": "minecraft:music.creative" + }, + "default": { + "max_delay": 24000, + "min_delay": 12000, + "sound": "minecraft:music.game" + } + }, + "minecraft:gameplay/bed_rule": { + "can_set_spawn": "always", + "can_sleep": "when_dark", + "error_message": { + "translate": "block.minecraft.bed.no_sleep" + } + }, + "minecraft:gameplay/nether_portal_spawns_piglin": true, + "minecraft:gameplay/respawn_anchor_works": false, + "minecraft:visual/ambient_light_color": "#0a0a0a", + "minecraft:visual/cloud_color": "#ccffffff", + "minecraft:visual/cloud_height": 192.33, + "minecraft:visual/fog_color": "#c0d8ff", + "minecraft:visual/sky_color": "#78a7ff" + }, + "coordinate_scale": 1.0, + "default_clock": "sfcraft:playground", + "has_ceiling": false, + "has_ender_dragon_fight": false, + "has_skylight": true, + "height": 384, + "infiniburn": "#minecraft:infiniburn_overworld", + "logical_height": 384, + "min_y": -64, + "monster_spawn_block_light_limit": 0, + "monster_spawn_light_level": { + "type": "minecraft:uniform", + "max_inclusive": 7, + "min_inclusive": 0 + }, + "timelines": "#minecraft:in_overworld" +} diff --git a/src/main/resources/data/sfcraft/world_clock/playground.json b/src/main/resources/data/sfcraft/world_clock/playground.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/src/main/resources/data/sfcraft/world_clock/playground.json @@ -0,0 +1 @@ +{} \ No newline at end of file