docs: 为所有Java源代码添加Javadoc注释;版本号升至1.0.3
All checks were successful
Build / build (push) Successful in -1m14s
All checks were successful
Build / build (push) Successful in -1m14s
This commit is contained in:
32
README.md
32
README.md
@@ -26,44 +26,44 @@ mod包名为com.le.teleportmirror 。
|
||||
|
||||
## 回城魔镜
|
||||
### 初级回城魔镜
|
||||
金锭 指南针 金锭
|
||||
铁锭 蓝色染色玻璃板 铁锭
|
||||
金锭 指南针 金锭
|
||||
铁锭 蓝色染色玻璃板 铁锭
|
||||
铜锭 铁锭 铜锭
|
||||
|
||||
### 中级回城魔镜
|
||||
钻石 末影珍珠 钻石
|
||||
青金石 初级回城魔镜 青金石
|
||||
钻石 末影珍珠 钻石
|
||||
青金石 初级回城魔镜 青金石
|
||||
红石 紫水晶碎片 红石
|
||||
|
||||
### 高级回城魔镜
|
||||
钻石 末影之眼 钻石
|
||||
绿宝石 中级回城魔镜 绿宝石
|
||||
钻石 末影之眼 钻石
|
||||
绿宝石 中级回城魔镜 绿宝石
|
||||
钻石 烈焰棒 钻石
|
||||
|
||||
### 永久回城魔镜
|
||||
潜影壳 紫颂果 潜影壳
|
||||
末影珍珠 高级回城魔镜 末影珍珠
|
||||
潜影壳 紫颂果 潜影壳
|
||||
末影珍珠 高级回城魔镜 末影珍珠
|
||||
潜影壳 回响碎片 潜影壳
|
||||
|
||||
## 传送魔镜
|
||||
### 初级传送魔镜
|
||||
金锭 拴绳 金锭
|
||||
铁锭 橙色染色玻璃板 铁锭
|
||||
金锭 拴绳 金锭
|
||||
铁锭 橙色染色玻璃板 铁锭
|
||||
铜锭 铁锭 铜锭
|
||||
|
||||
### 中级传送魔镜
|
||||
钻石 末影珍珠 钻石
|
||||
青金石 初级传送魔镜 青金石
|
||||
钻石 末影珍珠 钻石
|
||||
青金石 初级传送魔镜 青金石
|
||||
红石 紫水晶碎片 红石
|
||||
|
||||
### 高级传送魔镜
|
||||
钻石 末影之眼 钻石
|
||||
绿宝石 中级传送魔镜 绿宝石
|
||||
钻石 末影之眼 钻石
|
||||
绿宝石 中级传送魔镜 绿宝石
|
||||
钻石 烈焰棒 钻石
|
||||
|
||||
### 永久传送魔镜
|
||||
潜影壳 紫颂果 潜影壳
|
||||
末影珍珠 高级传送魔镜 末影珍珠
|
||||
潜影壳 紫颂果 潜影壳
|
||||
末影珍珠 高级传送魔镜 末影珍珠
|
||||
潜影壳 回响碎片 潜影壳
|
||||
|
||||
# 五、特殊机制
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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"));
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user