docs: 为所有Java源代码添加Javadoc注释;版本号升至1.0.3
All checks were successful
Build / build (push) Successful in -1m14s

This commit is contained in:
LiuEnder
2026-04-29 09:23:32 +08:00
parent a3f3a23c42
commit 609f6f83fc
10 changed files with 277 additions and 19 deletions

View File

@@ -32,7 +32,7 @@ mod_name=Teleport Mirror
# The license of the mod. Review your options at https://choosealicense.com/. All Rights Reserved is the default.
mod_license=MIT
# The mod version. See https://semver.org/
mod_version=1.0.2
mod_version=1.0.3
# The group ID for the mod. It is only important when publishing as an artifact to a Maven repository.
# This should match the base package used for the mod sources.
# See https://maven.apache.org/guides/mini/guide-naming-conventions.html

View File

@@ -2,6 +2,22 @@ package com.le.teleportmirror;
import net.neoforged.neoforge.common.ModConfigSpec;
/**
* Mod 配置项
* <p>
* 使用 NeoForge 的 {@link ModConfigSpec} 定义所有可配置项。
* 所有配置项按功能分为以下几个部分:
* <ul>
* <li>General - 通用设置(冷却时间、蓄力时间)</li>
* <li>Cross-Dimension - 跨维度传送开关</li>
* <li>Teleport Restrictions - 传送限制(同队伍限制)</li>
* <li>Durability - 各级魔镜耐久度</li>
* <li>Food Cost - 饱食度/饱和度消耗</li>
* <li>Side Effects - 副作用效果配置</li>
* <li>Recipe Toggles - 配方启用开关</li>
* <li>Recipe Overrides - 自定义配方覆盖</li>
* </ul>
*/
public class Config {
private static final ModConfigSpec.Builder BUILDER = new ModConfigSpec.Builder();

View File

@@ -20,34 +20,60 @@ import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.UseAnim;
import net.minecraft.world.level.Level;
/**
* 魔镜物品类
* <p>
* 魔镜是一个可蓄力使用的物品,长按右键蓄力完成后触发传送效果。
* 支持两种功能类型(回城/传送)和四个等级(初级/中级/高级/永久)。
* <p>
* 使用流程:
* <ol>
* <li>玩家右键开始蓄力(显示弓的动画)</li>
* <li>达到蓄力时间后触发效果</li>
* <li>回城魔镜直接传送;传送魔镜打开玩家选择界面</li>
* <li>消耗耐久、附加副作用、设置冷却</li>
* </ol>
*/
public class MirrorItem extends Item {
/** 魔镜等级 */
private final MirrorTier tier;
/** 魔镜功能类型 */
private final MirrorType type;
/**
* @param tier 魔镜等级
* @param type 魔镜类型(回城或传送)
* @param properties 物品属性(耐久度、堆叠数等)
*/
public MirrorItem(MirrorTier tier, MirrorType type, Properties properties) {
super(properties);
this.tier = tier;
this.type = type;
}
/** 获取魔镜等级 */
public MirrorTier getTier() {
return tier;
}
/** 获取魔镜功能类型 */
public MirrorType getMirrorType() {
return type;
}
/** 使用弓的拉动动画来表现蓄力过程 */
@Override
public UseAnim getUseAnimation(ItemStack stack) {
return UseAnim.BOW;
}
/** 允许无限蓄力(实际在 onUseTick 中检测是否达到蓄力阈值) */
@Override
public int getUseDuration(ItemStack stack, LivingEntity entity) {
return 72000;
}
/** 右键开始使用魔镜,若在冷却中则无法使用 */
@Override
public InteractionResultHolder<ItemStack> use(Level level, Player player, InteractionHand hand) {
ItemStack stack = player.getItemInHand(hand);
@@ -58,6 +84,10 @@ public class MirrorItem extends Item {
return InteractionResultHolder.consume(stack);
}
/**
* 每 tick 调用,检测蓄力是否达到设定阈值。
* 到达阈值后停止蓄力,并根据魔镜类型执行回城传送或打开玩家选择界面。
*/
@Override
public void onUseTick(Level level, LivingEntity livingEntity, ItemStack stack, int remainingUseDuration) {
if (level.isClientSide) {
@@ -86,6 +116,9 @@ public class MirrorItem extends Item {
public void releaseUsing(ItemStack stack, Level level, LivingEntity entity, int timeCharged) {
}
/**
* 检查当前等级的魔镜是否允许跨维度传送
*/
private boolean canCrossDimension() {
return switch (tier) {
case BASIC -> Config.ALLOW_CROSS_DIMENSION_BASIC.get();
@@ -95,6 +128,12 @@ public class MirrorItem extends Item {
};
}
/**
* 执行回城传送:将玩家传送到其重生点或世界出生点
*
* @param player 使用魔镜的玩家
* @param stack 魔镜物品堆
*/
private void performReturnTeleport(ServerPlayer player, ItemStack stack) {
ServerLevel serverLevel = player.serverLevel();
BlockPos targetPos;
@@ -102,6 +141,7 @@ public class MirrorItem extends Item {
ResourceKey<Level> respawnDimension = player.getRespawnDimension();
BlockPos respawnPos = player.getRespawnPosition();
// 优先使用玩家的重生点(床/重生锚)
if (respawnPos != null) {
targetLevel = player.server.getLevel(respawnDimension);
if (targetLevel == null) {
@@ -109,6 +149,7 @@ public class MirrorItem extends Item {
}
targetPos = respawnPos;
} else {
// 否则使用主世界的全局出生点
targetLevel = player.server.getLevel(Level.OVERWORLD);
if (targetLevel == null) {
targetLevel = serverLevel;
@@ -128,6 +169,13 @@ public class MirrorItem extends Item {
targetLevel.playSound(null, targetPos, SoundEvents.ENDERMAN_TELEPORT, SoundSource.PLAYERS, 1.0f, 1.0f);
}
/**
* 执行传送到目标玩家身边
*
* @param player 使用魔镜的玩家
* @param target 目标玩家
* @param stack 魔镜物品堆
*/
public void performTeleportToPlayer(ServerPlayer player, ServerPlayer target, ItemStack stack) {
ServerLevel targetLevel = target.serverLevel();
BlockPos targetPos = target.blockPosition();
@@ -144,6 +192,10 @@ public class MirrorItem extends Item {
targetLevel.playSound(null, targetPos, SoundEvents.ENDERMAN_TELEPORT, SoundSource.PLAYERS, 1.0f, 1.0f);
}
/**
* 获取配置中的最大耐久度
* 永久等级返回 {@link Integer#MAX_VALUE}
*/
private int getConfigMaxDamage() {
return switch (tier) {
case BASIC -> Config.BASIC_DURABILITY.get();
@@ -153,6 +205,9 @@ public class MirrorItem extends Item {
};
}
/**
* 消耗一次魔镜使用:附加副作用、设置冷却、扣除耐久
*/
private void consumeMirrorUse(ServerPlayer player, ItemStack stack) {
applySideEffects(player);
applyCooldown(player);
@@ -160,6 +215,7 @@ public class MirrorItem extends Item {
if (!tier.isPermanent()) {
int maxDamage = getConfigMaxDamage();
if (stack.getDamageValue() + 1 >= maxDamage) {
// 耐久耗尽,物品销毁
stack.shrink(1);
player.level().playSound(null, player.blockPosition(),
SoundEvents.ITEM_BREAK, SoundSource.PLAYERS, 1.0f, 1.0f);
@@ -169,11 +225,13 @@ public class MirrorItem extends Item {
}
}
/** 设置冷却时间(以 tick 为单位1秒=20tick */
private void applyCooldown(Player player) {
int cooldownTicks = Config.COOLDOWN_SECONDS.get() * 20;
player.getCooldowns().addCooldown(this, cooldownTicks);
}
/** 根据魔镜类型和等级获取副作用配置字符串 */
private String getEffectsConfig() {
return switch (type) {
case RETURN -> switch (tier) {
@@ -191,6 +249,7 @@ public class MirrorItem extends Item {
};
}
/** 根据魔镜等级获取饱食度消耗配置字符串 */
private String getFoodLevelConfig() {
return switch (tier) {
case BASIC -> Config.FOOD_LEVEL_BASIC.get();
@@ -200,6 +259,7 @@ public class MirrorItem extends Item {
};
}
/** 根据魔镜等级获取饱和度消耗配置字符串 */
private String getSaturationConfig() {
return switch (tier) {
case BASIC -> Config.SATURATION_COST_BASIC.get();
@@ -209,6 +269,10 @@ public class MirrorItem extends Item {
};
}
/**
* 给玩家附加副作用效果和饱食度消耗
* 副作用配置格式:"effect_id,dur_seconds,amplifier;..."
*/
private void applySideEffects(Player player) {
String effectsConfig = getEffectsConfig();
if (effectsConfig == null || effectsConfig.isBlank()) {
@@ -245,12 +309,21 @@ public class MirrorItem extends Item {
applyFoodCost(player);
}
/** 扣减饱食度和饱和度 */
private void applyFoodCost(Player player) {
FoodData foodData = player.getFoodData();
applyCostValue(getFoodLevelConfig(), (float) foodData.getFoodLevel(), newVal -> foodData.setFoodLevel(newVal.intValue()));
applyCostValue(getSaturationConfig(), foodData.getSaturationLevel(), newVal -> foodData.setSaturation(newVal));
}
/**
* 通用数值扣减方法
* 支持百分比模式(以%结尾)和固定值模式
*
* @param config 配置字符串,如 "50%" 或 "4.0"
* @param currentValue 当前数值
* @param setter 数值设置回调
*/
private void applyCostValue(String config, float currentValue, java.util.function.Consumer<Float> setter) {
if (config == null || config.isBlank()) {
return;
@@ -272,11 +345,13 @@ public class MirrorItem extends Item {
}
}
/** 魔镜不可附魔 */
@Override
public boolean isEnchantable(ItemStack stack) {
return false;
}
/** 永久魔镜显示附魔光效 */
@Override
public boolean isFoil(ItemStack stack) {
return tier == MirrorTier.PERMANENT;

View File

@@ -15,8 +15,22 @@ import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
/**
* 魔镜的网络通信层
* <p>
* 负责定义和注册自定义网络负载CustomPacketPayload
* 处理客户端与服务端之间的魔镜相关通信:
* <ul>
* <li>OpenPlayerSelectionPayload - 服务端→客户端,打开玩家选择界面</li>
* <li>RequestTeleportPayload - 客户端→服务端,请求传送到目标玩家</li>
* </ul>
*/
public class MirrorNetwork {
/**
* 注册所有的网络负载处理器
* 在 RegisterPayloadHandlersEvent 事件中调用
*/
public static void registerPayloads(final RegisterPayloadHandlersEvent event) {
final PayloadRegistrar registrar = event.registrar(TeleportMirrorMod.MODID).versioned("1.0.0");
@@ -33,15 +47,23 @@ public class MirrorNetwork {
);
}
/**
* 向指定客户端发送打开玩家选择界面的请求
*
* @param player 目标玩家
* @param tier 魔镜等级
*/
public static void sendOpenSelectionToClient(ServerPlayer player, MirrorTier tier) {
List<String> names = new ArrayList<>();
List<UUID> uuids = new ArrayList<>();
boolean teamOnly = Config.TELEPORT_TEAM_ONLY.get();
for (ServerPlayer onlinePlayer : player.server.getPlayerList().getPlayers()) {
// 排除使用者自身
if (onlinePlayer.getUUID().equals(player.getUUID())) {
continue;
}
// 如果开启了同队伍限制,仅保留同队伍玩家
if (teamOnly) {
var playerTeam = player.getTeam();
var targetTeam = onlinePlayer.getTeam();
@@ -59,6 +81,10 @@ public class MirrorNetwork {
PacketDistributor.sendToPlayer(player, new OpenPlayerSelectionPayload(names, uuids, tier.getName()));
}
/**
* 处理服务端发来的打开选择界面负载
* 通过反射调用客户端专用类以避免在服务端加载客户端代码
*/
private static void handleOpenSelection(OpenPlayerSelectionPayload payload, IPayloadContext context) {
context.enqueueWork(() -> {
try {
@@ -71,6 +97,10 @@ public class MirrorNetwork {
});
}
/**
* 处理客户端发来的传送请求
* 验证玩家手中的魔镜类型和等级后执行传送
*/
private static void handleRequestTeleport(RequestTeleportPayload payload, IPayloadContext context) {
context.enqueueWork(() -> {
ServerPlayer sender = (ServerPlayer) context.player();
@@ -84,6 +114,7 @@ public class MirrorNetwork {
ItemStack mainHand = sender.getMainHandItem();
ItemStack offHand = sender.getOffhandItem();
// 检查主手和副手中是否有正确的传送魔镜
ItemStack mirrorStack = null;
if (mainHand.getItem() instanceof MirrorItem mirrorItem && mirrorItem.getMirrorType() == MirrorType.TELEPORT) {
mirrorStack = mainHand;
@@ -104,6 +135,9 @@ public class MirrorNetwork {
});
}
/**
* 打开玩家选择界面的网络负载(服务端 → 客户端)
*/
public record OpenPlayerSelectionPayload(List<String> playerNames, List<UUID> playerUuids,
String tierName) implements CustomPacketPayload {
public static final Type<OpenPlayerSelectionPayload> TYPE = new Type<>(
@@ -140,6 +174,9 @@ public class MirrorNetwork {
}
}
/**
* 请求传送到目标玩家的网络负载(客户端 → 服务端)
*/
public record RequestTeleportPayload(UUID targetUuid, String tierName) implements CustomPacketPayload {
public static final Type<RequestTeleportPayload> TYPE = new Type<>(
ResourceLocation.fromNamespaceAndPath(TeleportMirrorMod.MODID, "request_teleport"));

View File

@@ -5,9 +5,21 @@ import net.minecraft.client.Minecraft;
import net.neoforged.api.distmarker.Dist;
import net.neoforged.api.distmarker.OnlyIn;
/**
* 客户端网络处理器
* <p>
* 仅编译到客户端(通过 @OnlyIn(Dist.CLIENT) 注解),
* 负责接收服务端发来的网络负载并打开对应的客户端界面。
* 在专用服务端环境中此类不会被加载。
*/
@OnlyIn(Dist.CLIENT)
public class MirrorNetworkClient {
/**
* 接收服务端发送的玩家选择请求,在客户端打开玩家选择界面
*
* @param payload 包含可选玩家列表、UUID 和魔镜等级的网络负载
*/
public static void openPlayerSelection(MirrorNetwork.OpenPlayerSelectionPayload payload) {
Minecraft client = Minecraft.getInstance();
MirrorTier tier = MirrorTier.valueOf(payload.tierName().toUpperCase());

View File

@@ -1,12 +1,25 @@
package com.le.teleportmirror;
/**
* 魔镜的等级
* <p>
* 不同等级影响耐久度、冷却时间、副作用强度等属性
* <ul>
* <li>BASIC (初级) - 默认耐久10</li>
* <li>INTERMEDIATE (中级) - 默认耐久50</li>
* <li>ADVANCED (高级) - 默认耐久100</li>
* <li>PERMANENT (永久) - 无限耐久,无副作用</li>
* </ul>
*/
public enum MirrorTier {
BASIC(10, "basic"),
INTERMEDIATE(50, "intermediate"),
ADVANCED(100, "advanced"),
PERMANENT(-1, "permanent");
/** 默认耐久度 */
private final int defaultDurability;
/** 等级名称,用于注册和序列化 */
private final String name;
MirrorTier(int defaultDurability, String name) {
@@ -14,14 +27,17 @@ public enum MirrorTier {
this.name = name;
}
/** 获取该等级的默认耐久度 */
public int getDefaultDurability() {
return defaultDurability;
}
/** 获取等级名称 */
public String getName() {
return name;
}
/** 是否为永久等级(无限耐久) */
public boolean isPermanent() {
return this == PERMANENT;
}

View File

@@ -1,15 +1,23 @@
package com.le.teleportmirror;
/**
* 魔镜的功能类型
* <p>
* RETURN - 回城魔镜:将使用者传送回出生点
* TELEPORT - 传送魔镜:将使用者传送到另一个玩家身边
*/
public enum MirrorType {
RETURN("return"),
TELEPORT("teleport");
/** 类型名称,用于注册和序列化 */
private final String name;
MirrorType(String name) {
this.name = name;
}
/** 获取类型名称 */
public String getName() {
return name;
}

View File

@@ -30,46 +30,74 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Teleport Mirror Mod 主类
* <p>
* NeoForge Mod 的入口点(通过 {@link Mod} 注解)。
* 负责:
* <ul>
* <li>注册所有魔镜物品8种回城/传送 × 4个等级</li>
* <li>创建创造模式标签页</li>
* <li>注册配置文件</li>
* <li>注册网络负载处理器</li>
* <li>在服务端启动时注入配置覆盖的配方</li>
* </ul>
*/
@Mod(TeleportMirrorMod.MODID)
public class TeleportMirrorMod {
public static final String MODID = "teleportmirror";
/** 物品注册器 */
public static final DeferredRegister.Items ITEMS = DeferredRegister.createItems(MODID);
/** 创造模式标签页注册器 */
public static final DeferredRegister<CreativeModeTab> CREATIVE_MODE_TABS =
DeferredRegister.create(Registries.CREATIVE_MODE_TAB, MODID);
// ==================== 回城魔镜 ====================
/** 初级回城魔镜 */
public static final DeferredItem<MirrorItem> BASIC_RETURN_MIRROR = ITEMS.register("basic_return_mirror",
() -> new MirrorItem(MirrorTier.BASIC, MirrorType.RETURN,
new Item.Properties().stacksTo(1).durability(MirrorTier.BASIC.getDefaultDurability())));
/** 中级回城魔镜 */
public static final DeferredItem<MirrorItem> INTERMEDIATE_RETURN_MIRROR = ITEMS.register("intermediate_return_mirror",
() -> new MirrorItem(MirrorTier.INTERMEDIATE, MirrorType.RETURN,
new Item.Properties().stacksTo(1).durability(MirrorTier.INTERMEDIATE.getDefaultDurability())));
/** 高级回城魔镜 */
public static final DeferredItem<MirrorItem> ADVANCED_RETURN_MIRROR = ITEMS.register("advanced_return_mirror",
() -> new MirrorItem(MirrorTier.ADVANCED, MirrorType.RETURN,
new Item.Properties().stacksTo(1).durability(MirrorTier.ADVANCED.getDefaultDurability())));
/** 永久回城魔镜 */
public static final DeferredItem<MirrorItem> PERMANENT_RETURN_MIRROR = ITEMS.register("permanent_return_mirror",
() -> new MirrorItem(MirrorTier.PERMANENT, MirrorType.RETURN,
new Item.Properties().stacksTo(1)));
// ==================== 传送魔镜 ====================
/** 初级传送魔镜 */
public static final DeferredItem<MirrorItem> BASIC_TELEPORT_MIRROR = ITEMS.register("basic_teleport_mirror",
() -> new MirrorItem(MirrorTier.BASIC, MirrorType.TELEPORT,
new Item.Properties().stacksTo(1).durability(MirrorTier.BASIC.getDefaultDurability())));
/** 中级传送魔镜 */
public static final DeferredItem<MirrorItem> INTERMEDIATE_TELEPORT_MIRROR = ITEMS.register("intermediate_teleport_mirror",
() -> new MirrorItem(MirrorTier.INTERMEDIATE, MirrorType.TELEPORT,
new Item.Properties().stacksTo(1).durability(MirrorTier.INTERMEDIATE.getDefaultDurability())));
/** 高级传送魔镜 */
public static final DeferredItem<MirrorItem> ADVANCED_TELEPORT_MIRROR = ITEMS.register("advanced_teleport_mirror",
() -> new MirrorItem(MirrorTier.ADVANCED, MirrorType.TELEPORT,
new Item.Properties().stacksTo(1).durability(MirrorTier.ADVANCED.getDefaultDurability())));
/** 永久传送魔镜 */
public static final DeferredItem<MirrorItem> PERMANENT_TELEPORT_MIRROR = ITEMS.register("permanent_teleport_mirror",
() -> new MirrorItem(MirrorTier.PERMANENT, MirrorType.TELEPORT,
new Item.Properties().stacksTo(1)));
/** 创造模式 "魔镜" 标签页包含所有8种魔镜 */
public static final DeferredHolder<CreativeModeTab, CreativeModeTab> MIRROR_TAB =
CREATIVE_MODE_TABS.register("mirror_tab", () -> CreativeModeTab.builder()
.title(Component.translatable("itemGroup.teleportmirror"))
@@ -86,6 +114,11 @@ public class TeleportMirrorMod {
output.accept(PERMANENT_TELEPORT_MIRROR.get());
}).build());
/**
* Mod 构造器
* 注册物品、创造标签页、配置文件、网络负载处理器,
* 并在服务端启动时注入配置覆盖的配方
*/
public TeleportMirrorMod(IEventBus modEventBus, ModContainer modContainer) {
ITEMS.register(modEventBus);
CREATIVE_MODE_TABS.register(modEventBus);
@@ -96,14 +129,19 @@ public class TeleportMirrorMod {
NeoForge.EVENT_BUS.addListener(this::onServerStarted);
}
/** 注册网络负载处理器 */
private void onRegisterPayloads(final RegisterPayloadHandlersEvent event) {
MirrorNetwork.registerPayloads(event);
}
/** 服务端启动后,注入配置覆盖的配方 */
private void onServerStarted(final ServerStartedEvent event) {
registerConfigRecipes(event.getServer().getRecipeManager());
}
/**
* 检查所有8种魔镜的配方配置将配置覆盖的配方注入到配方管理器
*/
private void registerConfigRecipes(net.minecraft.world.item.crafting.RecipeManager manager) {
tryRegisterRecipe(manager, Config.ENABLE_BASIC_RECIPE.get(),
Config.RECIPE_OVERRIDE_RETURN_BASIC.get(), BASIC_RETURN_MIRROR.get(), "basic_return_mirror");
@@ -123,6 +161,19 @@ public class TeleportMirrorMod {
Config.RECIPE_OVERRIDE_TELEPORT_PERMANENT.get(), PERMANENT_TELEPORT_MIRROR.get(), "permanent_teleport_mirror");
}
/**
* 尝试将配置覆盖的配方注入到配方管理器
* <p>
* 如果 override 为空则跳过(使用默认 JSON 配方)。
* 如果 enabled 为 false 也跳过(配方被禁用)。
* 通过反射访问配方管理器的内部 Map 来注入动态配方。
*
* @param manager 配方管理器
* @param enabled 配方是否启用
* @param override 配方覆盖字符串
* @param result 配方输出物品
* @param name 配方名称
*/
@SuppressWarnings("unchecked")
private void tryRegisterRecipe(net.minecraft.world.item.crafting.RecipeManager manager,
boolean enabled, String override, Item result, String name) {
@@ -150,6 +201,17 @@ public class TeleportMirrorMod {
}
}
/**
* 将配方覆盖字符串解析为 RecipeHolder
* <p>
* 格式:"row1;row2;row3|key=item_id;..."
* 示例:" C ;CGC; C |C=minecraft:copper_ingot;G=minecraft:glass_pane"
*
* @param override 配方覆盖字符串
* @param result 配方输出物品
* @param name 配方名称
* @return 解析后的 RecipeHolder解析失败返回 null
*/
private RecipeHolder<?> parseRecipeOverride(String override, Item result, String name) {
String[] parts = override.split("\\|");
if (parts.length < 2) return null;

View File

@@ -11,13 +11,35 @@ import net.neoforged.neoforge.network.PacketDistributor;
import java.util.List;
import java.util.UUID;
/**
* 玩家选择界面
* <p>
* 当玩家使用传送魔镜长按右键蓄力完成后,客户端会打开此界面。
* 界面列出当前在线的可选玩家,玩家点击按钮选择目标后,
* 通过网络包向服务端发送传送请求,服务端执行实际传送逻辑。
* <p>
* 支持分页:每页最多显示 {@value #BUTTONS_PER_PAGE} 名玩家,
* 通过滚动按钮切换页码。
* <p>
* 该界面不暂停游戏isPauseScreen 返回 false
*/
public class PlayerSelectionScreen extends Screen {
/** 可选玩家名称列表 */
private final List<String> playerNames;
/** 可选玩家 UUID 列表(与 playerNames 一一对应) */
private final List<UUID> playerUuids;
/** 当前使用的魔镜等级 */
private final MirrorTier tier;
/** 当前滚动偏移量 */
private int scrollOffset = 0;
/** 每页最多显示的玩家按钮数量 */
private static final int BUTTONS_PER_PAGE = 8;
/**
* @param playerNames 可选玩家的显示名称
* @param playerUuids 可选玩家的 UUID
* @param tier 魔镜等级
*/
public PlayerSelectionScreen(List<String> playerNames, List<UUID> playerUuids, MirrorTier tier) {
super(Component.translatable("screen.teleportmirror.select_player"));
this.playerNames = playerNames;
@@ -31,6 +53,10 @@ public class PlayerSelectionScreen extends Screen {
rebuildButtons();
}
/**
* 重建按钮布局
* 根据 scrollOffset 显示当前页的玩家按钮,以及滚动按钮和取消按钮
*/
private void rebuildButtons() {
this.clearWidgets();
@@ -55,15 +81,16 @@ public class PlayerSelectionScreen extends Screen {
.build());
}
// 如果玩家数量超过单页容量,显示滚动按钮
if (playerNames.size() > BUTTONS_PER_PAGE) {
this.addRenderableWidget(Button.builder(
Component.literal("\u25B2"),
Component.literal("\u25B2"), // 上箭头 ▲
btn -> { scrollOffset = Math.max(0, scrollOffset - 1); rebuildButtons(); })
.pos(centerX + buttonWidth / 2 + 5, startY)
.size(20, 20)
.build());
this.addRenderableWidget(Button.builder(
Component.literal("\u25BC"),
Component.literal("\u25BC"), // 下箭头 ▼
btn -> { scrollOffset = Math.min(maxOffset, scrollOffset + 1); rebuildButtons(); })
.pos(centerX + buttonWidth / 2 + 5, startY + BUTTONS_PER_PAGE * (buttonHeight + spacing) - spacing)
.size(20, 20)
@@ -78,6 +105,11 @@ public class PlayerSelectionScreen extends Screen {
.build());
}
/**
* 玩家点击某个目标后,向服务端发送传送请求并关闭界面
*
* @param index 所选玩家在列表中的索引
*/
private void selectPlayer(int index) {
if (index >= 0 && index < playerUuids.size()) {
UUID targetUuid = playerUuids.get(index);