package xyz.thewhitedog9487 import com.mojang.brigadier.Command import com.mojang.brigadier.arguments.IntegerArgumentType import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback import net.minecraft.commands.CommandSourceStack import net.minecraft.commands.Commands import net.minecraft.commands.arguments.EntityArgument import net.minecraft.commands.arguments.coordinates.Vec2Argument import net.minecraft.core.BlockPos import net.minecraft.network.chat.Component import net.minecraft.server.commands.TeleportCommand import net.minecraft.server.level.ServerLevel import net.minecraft.world.entity.Entity import net.minecraft.world.level.block.Block import net.minecraft.world.level.block.Blocks import net.minecraft.world.level.chunk.status.ChunkStatus import net.minecraft.world.level.levelgen.Heightmap.Types.MOTION_BLOCKING_NO_LEAVES import net.minecraft.world.phys.Vec2 import net.minecraft.world.phys.Vec3 import java.util.* import kotlin.math.absoluteValue import kotlin.math.max import kotlin.math.min import kotlin.random.Random import kotlin.random.nextInt /** * 传送后用于生成保护平台的方块 */ val TargetBlock = Blocks.GLASS /** * 传送后会被 [TargetBlock] 替换掉的方块 *

* 替换中心:被传送目标脚下方块 *

* 替换范围:替换中心周围半径为一的正方形区域 */ val ReplaceToTargetBlock = setOf( Blocks.AIR, Blocks.VOID_AIR, Blocks.CAVE_AIR, Blocks.WATER, Blocks.LAVA, Blocks.SHORT_GRASS, Blocks.VINE ) /** * 世界边界 *

* @see Minecraft Wiki (中文) * @see Minecraft Wiki (English) **/ const val WorldBorder = 2.9e7.toInt() /** * 执行命令所需权限等级 * @see TeleportCommand * @see Commands * @see Minecraft Wiki (中文) * @see Minecraft Wiki (English) */ val PermissionLevel = Commands.LEVEL_GAMEMASTERS /** * 在传送之前记录当前位置,以支持传送回去 */ var OldPositions: MutableMap = mutableMapOf() /** * 命令执行失败时的返回值 * @see Minecraft Wiki (中文) * @see Minecraft Wiki (English) */ const val CommandExecuteFailure = 0 /** * 根命令名 */ val CommandRootNodeName = setOf( "随机传送", "rtp" ) /** * 使用Fabric API向游戏内注册命令 *
* @see Fabric Docs (中文) * @see Fabric Wiki (中文) * @see Fabric Docs (English) * @see Fabric Wiki (English) */ fun CommandRegister() { for (RootNodeName in CommandRootNodeName) { CommandRegistrationCallback.EVENT.register { dispatcher, context, selection -> dispatcher.register(Commands.literal(RootNodeName) // /rtp .requires(Commands.hasPermission(PermissionLevel)) .executes{ commandContext -> ExecuteCommand(commandContext.source) } // /rtp back .then(Commands.literal("back") .requires(Commands.hasPermission(PermissionLevel)) .executes { commandContext -> TeleportBack(commandContext.source) } ) // /rtp back .then(Commands.literal("back") .then(Commands.argument(CommandArgumentName_Target, EntityArgument.entity()) .requires(Commands.hasPermission(PermissionLevel)) .executes { commandContext -> TeleportBack(commandContext.source, EntityArgument.getEntity(commandContext, CommandArgumentName_Target)) } ) ) // /rtp back .then(Commands.argument(CommandArgumentName_Target, EntityArgument.entity()) .then(Commands.literal("back") .requires(Commands.hasPermission(PermissionLevel)) .executes { commandContext -> TeleportBack(commandContext.source, EntityArgument.getEntity(commandContext, CommandArgumentName_Target)) } ) ) // /rtp .then(Commands.argument(CommandArgumentName_Radius, IntegerArgumentType.integer(0)) .requires(Commands.hasPermission(PermissionLevel)) .executes { commandContext -> ExecuteCommand(commandContext.source, Radius = IntegerArgumentType.getInteger(commandContext, CommandArgumentName_Radius)) } ) // /rtp .then(Commands.argument(CommandArgumentName_Target, EntityArgument.entity()) .requires(Commands.hasPermission(PermissionLevel)) .executes { commandContext -> ExecuteCommand(commandContext.source, Entity = EntityArgument.getEntity(commandContext, CommandArgumentName_Target)) } ) // /rtp .then(Commands.argument(CommandArgumentName_Radius, IntegerArgumentType.integer(0)) .then(Commands.argument(CommandArgumentName_Target, EntityArgument.entity()) .requires(Commands.hasPermission(PermissionLevel)) .executes { commandContext -> ExecuteCommand(commandContext.source, Radius = IntegerArgumentType.getInteger(commandContext, CommandArgumentName_Radius), Entity = EntityArgument.getEntity(commandContext, CommandArgumentName_Target)) } ) ) // /rtp .then(Commands.argument(CommandArgumentName_Target, EntityArgument.entity()) .then(Commands.argument(CommandArgumentName_Radius, IntegerArgumentType.integer(0)) .requires(Commands.hasPermission(PermissionLevel)) .executes { commandContext -> ExecuteCommand(commandContext.source, Radius = IntegerArgumentType.getInteger(commandContext, CommandArgumentName_Radius), Entity = EntityArgument.getEntity(commandContext, CommandArgumentName_Target)) } ) ) // /rtp .then(Commands.argument(CommandArgumentName_Radius, IntegerArgumentType.integer(0)) .then(Commands.argument(CommandArgumentName_OriginPosition, Vec2Argument.vec2()) .requires(Commands.hasPermission(PermissionLevel)) .executes { commandContext -> ExecuteCommand(commandContext.source, Radius = IntegerArgumentType.getInteger(commandContext, CommandArgumentName_Radius), Origin = Vec2Argument.getVec2(commandContext, CommandArgumentName_OriginPosition)) } ) ) // /rtp .then(Commands.argument(CommandArgumentName_Radius, IntegerArgumentType.integer(0)) .then(Commands.argument(CommandArgumentName_Target, EntityArgument.entity()) .then(Commands.argument(CommandArgumentName_OriginEntity, EntityArgument.entity()) .requires(Commands.hasPermission(PermissionLevel)) .executes { commandContext -> ExecuteCommand(commandContext.source, Radius = IntegerArgumentType.getInteger(commandContext, CommandArgumentName_Radius), Entity = EntityArgument.getEntity(commandContext, CommandArgumentName_Target), Origin = Vec2( EntityArgument.getEntity(commandContext, CommandArgumentName_OriginEntity).position().x.toFloat(), EntityArgument.getEntity(commandContext, CommandArgumentName_OriginEntity).position().z.toFloat() ) ) } ) ) ) // /rtp .then(Commands.argument(CommandArgumentName_Radius, IntegerArgumentType.integer(0)) .then(Commands.argument(CommandArgumentName_Target, EntityArgument.entity()) .then(Commands.argument(CommandArgumentName_OriginPosition, Vec2Argument.vec2()) .requires(Commands.hasPermission(PermissionLevel)) .executes { commandContext -> ExecuteCommand(commandContext.source, Radius = IntegerArgumentType.getInteger(commandContext, CommandArgumentName_Radius), Entity = EntityArgument.getEntity(commandContext, CommandArgumentName_Target), Origin = Vec2Argument.getVec2(commandContext, CommandArgumentName_OriginPosition) ) } ) ) ) // /rtp .then(Commands.argument(CommandArgumentName_Target, EntityArgument.entity()) .then(Commands.argument(CommandArgumentName_Radius, IntegerArgumentType.integer(0)) .then(Commands.argument(CommandArgumentName_OriginEntity, EntityArgument.entity()) .requires(Commands.hasPermission(PermissionLevel)) .executes { commandContext -> ExecuteCommand(commandContext.source, Radius = IntegerArgumentType.getInteger(commandContext, CommandArgumentName_Radius), Entity = EntityArgument.getEntity(commandContext, CommandArgumentName_Target), Origin = Vec2( EntityArgument.getEntity(commandContext, CommandArgumentName_OriginEntity).position().x.toFloat(), EntityArgument.getEntity(commandContext, CommandArgumentName_OriginEntity).position().z.toFloat() ) ) } ) ) ) // /rtp .then(Commands.argument(CommandArgumentName_Target, EntityArgument.entity()) .then(Commands.argument(CommandArgumentName_Radius, IntegerArgumentType.integer(0)) .then(Commands.argument(CommandArgumentName_OriginPosition, Vec2Argument.vec2()) .requires(Commands.hasPermission(PermissionLevel)) .executes { commandContext -> ExecuteCommand(commandContext.source, Radius = IntegerArgumentType.getInteger(commandContext, CommandArgumentName_Radius), Entity = EntityArgument.getEntity(commandContext, CommandArgumentName_Target), Origin = Vec2Argument.getVec2(commandContext, CommandArgumentName_OriginPosition) ) } ) ) ) // /rtp .then(Commands.argument(CommandArgumentName_RegionFromPosition, Vec2Argument.vec2()) .then(Commands.argument(CommandArgumentName_RegionToPosition, Vec2Argument.vec2()) .requires(Commands.hasPermission(PermissionLevel)) .executes { commandContext -> ExecuteCommand(commandContext.source, RegionFrom = Vec2Argument.getVec2(commandContext, CommandArgumentName_RegionFromPosition), RegionTo = Vec2Argument.getVec2(commandContext, CommandArgumentName_RegionToPosition) ) } ) ) // /rtp .then(Commands.argument(CommandArgumentName_RegionFromPosition, Vec2Argument.vec2()) .then(Commands.argument(CommandArgumentName_RegionToPosition, Vec2Argument.vec2()) .then(Commands.argument(CommandArgumentName_Target, EntityArgument.entity()) .requires(Commands.hasPermission(PermissionLevel)) .executes { commandContext -> ExecuteCommand(commandContext.source, Entity = EntityArgument.getEntity(commandContext, CommandArgumentName_Target), RegionFrom = Vec2Argument.getVec2(commandContext, CommandArgumentName_RegionFromPosition), RegionTo = Vec2Argument.getVec2(commandContext, CommandArgumentName_RegionToPosition) ) } ) ) ) // /rtp .then(Commands.argument(CommandArgumentName_Target, EntityArgument.entity()) .then(Commands.argument(CommandArgumentName_RegionFromPosition, Vec2Argument.vec2()) .then(Commands.argument(CommandArgumentName_RegionToPosition, Vec2Argument.vec2()) .requires(Commands.hasPermission(PermissionLevel)) .executes { commandContext -> ExecuteCommand(commandContext.source, Entity = EntityArgument.getEntity(commandContext, CommandArgumentName_Target), RegionFrom = Vec2Argument.getVec2(commandContext, CommandArgumentName_RegionFromPosition), RegionTo = Vec2Argument.getVec2(commandContext, CommandArgumentName_RegionToPosition) ) } ) ) ) // /rtp .then(Commands.argument(CommandArgumentName_RegionFromEntity, EntityArgument.entity()) .then(Commands.argument(CommandArgumentName_RegionToEntity, EntityArgument.entity()) .requires(Commands.hasPermission(PermissionLevel)) .executes { commandContext -> ExecuteCommand(commandContext.source, RegionFrom = Vec2( EntityArgument.getEntity(commandContext, CommandArgumentName_RegionFromEntity).position().x.toFloat(), EntityArgument.getEntity(commandContext, CommandArgumentName_RegionFromEntity).position().z.toFloat() ), RegionTo = Vec2( EntityArgument.getEntity(commandContext, CommandArgumentName_RegionToEntity).position().x.toFloat(), EntityArgument.getEntity(commandContext, CommandArgumentName_RegionToEntity).position().z.toFloat() ) ) } ) ) // /rtp .then(Commands.argument(CommandArgumentName_RegionFromEntity, EntityArgument.entity()) .then(Commands.argument(CommandArgumentName_RegionToEntity, EntityArgument.entity()) .then(Commands.argument(CommandArgumentName_Target, EntityArgument.entity()) .requires(Commands.hasPermission(PermissionLevel)) .executes { commandContext -> ExecuteCommand(commandContext.source, Entity = EntityArgument.getEntity(commandContext, CommandArgumentName_Target), RegionFrom = Vec2( EntityArgument.getEntity(commandContext, CommandArgumentName_RegionFromEntity).position().x.toFloat(), EntityArgument.getEntity(commandContext, CommandArgumentName_RegionFromEntity).position().z.toFloat() ), RegionTo = Vec2( EntityArgument.getEntity(commandContext, CommandArgumentName_RegionToEntity).position().x.toFloat(), EntityArgument.getEntity(commandContext, CommandArgumentName_RegionToEntity).position().z.toFloat() ) ) } ) ) ) ) } } } fun ExecuteCommand( Source: CommandSourceStack, Radius: Int = WorldBorder - 1e4.toInt(), Entity: Entity? = Source.player, Origin: Vec2? = null, RegionFrom: Vec2? = null, RegionTo: Vec2? = null ): Int { val TargetEntity = Entity ?: run { Source.sendFailure( Component.translatableWithFallback("error.no_target","不存在被传送目标,由非玩家物体执行命令时请显式指定被传送玩家ID") ) return CommandExecuteFailure } val RandomRadius = Radius.absoluteValue.toFloat() var (RandomRegionFrom, RandomRegionTo) = if (RegionFrom == null && RegionTo == null) Pair( Vec2(-RandomRadius, -RandomRadius), Vec2(RandomRadius, RandomRadius) ) else Pair( Vec2(min(RegionFrom!!.x, RegionTo!!.x), min(RegionFrom.y, RegionTo.y)), Vec2(max(RegionFrom.x, RegionTo.x), max(RegionFrom.y, RegionTo.y))) if (Origin != null) { RandomRegionFrom = Vec2(Origin.x - RandomRadius, Origin.y - RandomRadius) RandomRegionTo = Vec2(Origin.x + RandomRadius, Origin.y + RandomRadius) } var (TargetX, TargetZ) = Pair( Random.nextInt(RandomRegionFrom.x.toInt() until RandomRegionTo.x.toInt() + 1), Random.nextInt(RandomRegionFrom.y.toInt() until RandomRegionTo.y.toInt() + 1) ) TargetX = Math.clamp(TargetX.toLong(), -(WorldBorder - 1e4).toInt(), WorldBorder - 1e4.toInt() ) TargetZ = Math.clamp(TargetZ.toLong(), -(WorldBorder - 1e4).toInt(), WorldBorder - 1e4.toInt() ) val EntityWorld = TargetEntity.level() EntityWorld.getChunk(TargetX shr 4, TargetZ shr 4, ChunkStatus.FULL, true) val TargetY = EntityWorld.getHeight(MOTION_BLOCKING_NO_LEAVES, TargetX, TargetZ) val BlockPosInstance = BlockPos.MutableBlockPos() for (x in -1..1) { for (z in -1..1) { BlockPosInstance.set(TargetX - x, TargetY - 1, TargetZ - z) val CurrentBlock = EntityWorld.getBlockState(BlockPosInstance).block if (ReplaceToTargetBlock.contains(CurrentBlock)) { EntityWorld.setBlockAndUpdate(BlockPosInstance, TargetBlock.defaultBlockState()) } } } OldPositions[TargetEntity.uuid] = TargetEntity.position() TargetEntity.teleportTo(EntityWorld as ServerLevel, TargetX + 0.5, TargetY.toDouble(), TargetZ + 0.5, setOf(), TargetEntity.yRot, TargetEntity.xRot, false) val FeedbackFallbackString = String.format("已将玩家%s传送到%d %d %d", TargetEntity.name.string, TargetX, TargetY, TargetZ) Source.sendSuccess({ Component.translatableWithFallback("info.success", FeedbackFallbackString, TargetEntity.name, TargetX, TargetY, TargetZ) }, true) return Command.SINGLE_SUCCESS } fun TeleportBack(Source: CommandSourceStack, TargetEntity: Entity? = Source.player): Int { if (TargetEntity == null) { Source.sendFailure( Component.translatableWithFallback( "error.no_target", "不存在被传送目标,由非玩家物体执行命令时请显式指定被传送玩家ID")) return CommandExecuteFailure } OldPositions[TargetEntity.uuid].also { if (it == null) { Source.sendFailure( Component.translatableWithFallback( "error.no_old_position", "%s还未使用过随机传送,因此无法回溯", TargetEntity.name.string ) ) return@TeleportBack CommandExecuteFailure } TargetEntity.teleportTo( TargetEntity.level() as ServerLevel, it.x, it.y, it.z, setOf(), TargetEntity.yRot, TargetEntity.xRot, false) Source.sendSuccess({ Component.translatableWithFallback( "info.success.back", "已将玩家%s传送回原位置", TargetEntity.name.string ) }, true) return@TeleportBack Command.SINGLE_SUCCESS } }