Files
sfcraft/src/main/java/io/ib67/sfcraft/module/room/CreativeRoomModule.java
T

270 lines
13 KiB
Java

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;
import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import io.ib67.sfcraft.ServerModule;
import io.ib67.sfcraft.callback.SFCallbacks;
import io.ib67.sfcraft.inject.MinecraftServerSupplier;
import io.ib67.sfcraft.module.RoomModule;
import io.ib67.sfcraft.module.chat.ChatPrefix;
import io.ib67.sfcraft.module.chat.ChatPrefixModule;
import io.ib67.sfcraft.registry.RoomRegistry;
import io.ib67.sfcraft.room.RoomTeleporter;
import io.ib67.sfcraft.room.create.CreativeSpaceFactory;
import io.ib67.sfcraft.room.create.CreativeSpaceRoom;
import io.ib67.sfcraft.util.Helper;
import io.ib67.sfcraft.util.SFConsts;
import me.lucko.fabric.api.permissions.v0.PermissionCheckEvent;
import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback;
import net.fabricmc.fabric.api.networking.v1.PacketSender;
import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents;
import net.fabricmc.fabric.api.util.TriState;
import net.minecraft.commands.CommandBuildContext;
import net.minecraft.commands.CommandSourceStack;
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;
import java.awt.*;
import java.util.Set;
public class CreativeRoomModule extends ServerModule {
private static final Set<String> BYPASS_PERMISSIONS = Set.of(
"carpet.command.player",
"carpet.command.track",
"minecraft.command.setblock",
"minecraft.command.summon",
"carpet.command.player.gamemode");
@Inject
private RoomRegistry roomRegistry;
@Inject
private RoomTeleporter teleporter;
@Inject
private RoomModule roomModule;
@Inject
private MinecraftServerSupplier serverSupplier;
@Inject
private ChatPrefixModule chatPrefixModule;
private CreativeSpaceRoom room;
private ChatPrefix prefix;
@Override
public void onInitialize() {
prefix = new ChatPrefix(
Component.literal("[PLAYGRD] ").withColor(CommonColors.SOFT_YELLOW),
"playgrd",
true,
20
);
CommandRegistrationCallback.EVENT
.register(this::registerCommand);
PermissionCheckEvent.EVENT.register(this::onWorldEdit);
PermissionCheckEvent.EVENT.register(this::onOtherCommands);
room = new CreativeSpaceRoom(this);
roomRegistry.registerRoomType(CreativeSpaceRoom.class, CreativeSpaceRoom.WORLD, new CreativeSpaceFactory(room));
roomRegistry.createRoomOf(CreativeSpaceRoom.class, CreativeSpaceRoom.IDENTIFIER, null, (String) null);
roomModule.enqueuePregen(CreativeSpaceRoom.WORLD, CreativeSpaceRoom.SPAWN_POS);
}
private @NotNull TriState onOtherCommands(@NotNull SharedSuggestionProvider commandSource, @NotNull String s) {
if (commandSource instanceof CommandSourceStack source && source.getPlayer() != null) {
var p = source.getPlayer();
if (p.level().dimension().equals(CreativeSpaceRoom.WORLD)) {
if (BYPASS_PERMISSIONS.contains(s)) {
return TriState.TRUE;
}
}
}
return TriState.DEFAULT;
}
private @NotNull TriState onWorldEdit(@NotNull SharedSuggestionProvider commandSource, @NotNull String s) {
if (commandSource instanceof CommandSourceStack source && source.getPlayer() != null) {
var p = source.getPlayer();
if (p.level().dimension().equals(CreativeSpaceRoom.WORLD)) {
if (s.startsWith("worldedit") && SFConsts.WORLDEDIT_AT_PLAYGROUND.hasPermission(p)) {
return TriState.TRUE;
}
}
}
return TriState.DEFAULT;
}
private void registerCommand(CommandDispatcher<CommandSourceStack> dispatcher,
CommandBuildContext registry, Commands.CommandSelection env) {
dispatcher.register(
LiteralArgumentBuilder.<CommandSourceStack>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")
.then(Commands.argument("mode", GameModeArgument.gameMode())
.requires(it -> it.isPlayer() && SFConsts.COMMAND_PLAYGROUND_GAMEMODE.hasPermission(it.getPlayer()))
.executes(this::onGameMode)
)
);
dispatcher.register(
Commands.literal("pt")
.then(Commands.argument("dest", EntityArgument.entity())
.requires(it -> it.isPlayer() && SFConsts.COMMAND_PLAYGROUND_TELEPORT.hasPermission(it.getPlayer()))
.executes(this::onTeleport))
);
}
private int onTeleport(CommandContext<CommandSourceStack> ctx) throws CommandSyntaxException {
var src = ctx.getSource();
if (src instanceof CommandSourceStack source && source.getPlayer() != null) {
var p = source.getPlayer();
var dest = EntityArgument.getEntity(ctx, "dest");
if (p.level().dimension().equals(CreativeSpaceRoom.WORLD)) {
if (dest.level() == p.level()) {
Helper.teleportSafely(
p, p.level(),
dest.getBlockX(), dest.getBlockY(), dest.getBlockZ(),
dest.getYRot(), dest.getXRot()
);
return Command.SINGLE_SUCCESS;
}
}
p.sendSystemMessage(Component.translatable("message.sfcraft.playground.must_be_in"));
}
return Command.SINGLE_SUCCESS;
}
private int onGameMode(CommandContext<CommandSourceStack> ctx) {
var src = ctx.getSource();
if (src instanceof CommandSourceStack source && source.getPlayer() != null) {
var p = source.getPlayer();
var mode = ctx.getArgument("mode", GameType.class);
if (p.level().dimension().equals(CreativeSpaceRoom.WORLD)) {
p.setGameMode(mode);
p.sendSystemMessage(Component.translatable("message.sfcraft.playground.gamemode_changed", mode.getSerializedName()));
} else {
p.sendSystemMessage(Component.translatable("message.sfcraft.playground.only_here"));
}
}
return Command.SINGLE_SUCCESS;
}
private int gotoPlayground(CommandContext<CommandSourceStack> serverCommandSourceCommandContext) {
var player = serverCommandSourceCommandContext.getSource().getPlayer();
try {
teleporter.teleportTo(room, player);
} catch (Exception e) {
player.connection.disconnect(Component.nullToEmpty(e.getMessage()));
e.printStackTrace();
}
return 0;
}
// ── /playgrd time ────────────────────────────────────────────────
// 只操作游乐场自己的 WorldClock(sfcraft:playground),绝不触碰其它钟;
// 必须身处游乐场才能使用。时间值存原始 tick(可超 24000),展示时按 24000 取余。
private int onSetTime(CommandContext<CommandSourceStack> 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<CommandSourceStack> 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<CommandSourceStack> 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<CommandSourceStack> 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<WorldClock> 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<CommandSourceStack> 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.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);
chatPrefixModule.applyPrefix(player, prefix);
}
public void onPlayerQuit(ServerPlayer player) {
chatPrefixModule.removePrefix(player, prefix);
}
}