From 302cab722aaf41b9aefbec29f6acf22aa678ed0e Mon Sep 17 00:00:00 2001 From: iceBear67 <48877375+iceBear67@users.noreply.github.com> Date: Mon, 3 Aug 2026 23:47:18 +0800 Subject: [PATCH] add: allow elytra teleport into portal instantly (#50) --- .../io/ib67/sfcraft/SFCraftInitializer.java | 2 + .../io/ib67/sfcraft/config/GameConfig.java | 6 +++ .../gameplay/NetherPortalBlockMixin.java | 27 +++++++++++ .../game/portal/ElytraPortalModule.java | 47 +++++++++++++++++++ src/main/resources/sfcraft.mixins.json | 1 + 5 files changed, 83 insertions(+) create mode 100644 src/main/java/io/ib67/sfcraft/mixin/gameplay/NetherPortalBlockMixin.java create mode 100644 src/main/java/io/ib67/sfcraft/module/game/portal/ElytraPortalModule.java diff --git a/src/main/java/io/ib67/sfcraft/SFCraftInitializer.java b/src/main/java/io/ib67/sfcraft/SFCraftInitializer.java index 2703d14..d3f3a39 100644 --- a/src/main/java/io/ib67/sfcraft/SFCraftInitializer.java +++ b/src/main/java/io/ib67/sfcraft/SFCraftInitializer.java @@ -22,6 +22,7 @@ import io.ib67.sfcraft.module.game.commander.CommanderModule; import io.ib67.sfcraft.module.game.crystal.GravityCrystalModule; import io.ib67.sfcraft.module.game.lunchbox.LunchBoxModule; import io.ib67.sfcraft.module.game.mount.MountModule; +import io.ib67.sfcraft.module.game.portal.ElytraPortalModule; import io.ib67.sfcraft.module.game.token.TokenModule; import io.ib67.sfcraft.module.game.totem.TotemModule; import io.ib67.sfcraft.registry.CauldronRecipeRegistry; @@ -103,6 +104,7 @@ public class SFCraftInitializer extends GuiceModInitializer { registerFeature(CommanderModule.class); registerFeature(MountModule.class); registerFeature(TotemModule.class); + registerFeature(ElytraPortalModule.class); // ItemGroupModule 必须最后注册:它冻结前面各模块被动贡献到 ItemGroupService 的物品,构建创意物品栏 registerFeature(ItemGroupModule.class); } diff --git a/src/main/java/io/ib67/sfcraft/config/GameConfig.java b/src/main/java/io/ib67/sfcraft/config/GameConfig.java index a2d3738..a39e32f 100644 --- a/src/main/java/io/ib67/sfcraft/config/GameConfig.java +++ b/src/main/java/io/ib67/sfcraft/config/GameConfig.java @@ -16,6 +16,12 @@ public class GameConfig { public Mount mount = new Mount(); public Gps gps = new Gps(); public StandingFirmTotem standingFirmTotem = new StandingFirmTotem(); + public ElytraPortal elytraPortal = new ElytraPortal(); + + public static class ElytraPortal { + // 鞘翅滑翔冲进下界传送门时跳过等待(立即传送)的最低速度,单位米/秒;<= 0 关闭本特性 + public double minSpeedBlocksPerSecond = 25.0; + } public static class StandingFirmTotem { // 可反复使用的图腾变种:触发时扣掉一部分总经验,并进入冷却 diff --git a/src/main/java/io/ib67/sfcraft/mixin/gameplay/NetherPortalBlockMixin.java b/src/main/java/io/ib67/sfcraft/mixin/gameplay/NetherPortalBlockMixin.java new file mode 100644 index 0000000..19b0029 --- /dev/null +++ b/src/main/java/io/ib67/sfcraft/mixin/gameplay/NetherPortalBlockMixin.java @@ -0,0 +1,27 @@ +package io.ib67.sfcraft.mixin.gameplay; + +import io.ib67.sfcraft.SFCraft; +import io.ib67.sfcraft.module.game.portal.ElytraPortalModule; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.level.block.NetherPortalBlock; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +/** + * 鞘翅冲出下界传送门时跳过等待:本方法每 tick 被 {@code PortalProcessor} 调用来决定 + * 「还要在门里待多久」,命中条件时覆写为 1 tick(与原版创造模式一致),下一 tick 即跨维度; + * 否则不动 cir,等待时间完全由原版游戏规则决定。判定逻辑见 {@link ElytraPortalModule#shouldTeleportNow}。 + */ +@Mixin(NetherPortalBlock.class) +public abstract class NetherPortalBlockMixin { + @Inject(method = "getPortalTransitionTime", at = @At("HEAD"), cancellable = true) + private void sfcraft$elytraRushPortal(ServerLevel level, Entity entity, CallbackInfoReturnable cir) { + var module = SFCraft.getInjector().getInstance(ElytraPortalModule.class); + if (module.shouldTeleportNow(entity)) { + cir.setReturnValue(module.getInstantTransitionTicks()); + } + } +} diff --git a/src/main/java/io/ib67/sfcraft/module/game/portal/ElytraPortalModule.java b/src/main/java/io/ib67/sfcraft/module/game/portal/ElytraPortalModule.java new file mode 100644 index 0000000..81662e7 --- /dev/null +++ b/src/main/java/io/ib67/sfcraft/module/game/portal/ElytraPortalModule.java @@ -0,0 +1,47 @@ +package io.ib67.sfcraft.module.game.portal; + +import com.google.inject.Inject; +import io.ib67.sfcraft.ServerModule; +import io.ib67.sfcraft.config.GameConfigService; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.entity.Entity; + +/** + * 鞘翅冲门:玩家使用鞘翅滑翔、以不低于配置阈值(米/秒)的速度冲进下界传送门时, + * 跳过原本的传送门等待(生存默认 80 tick),下一 tick 直接跨维度。 + *

+ * 本模块不注册任何注册表内容;行为由 {@code NetherPortalBlockMixin} 在 + * {@code NetherPortalBlock#getPortalTransitionTime} 头部委托 {@link #shouldTeleportNow} + * 实现:命中条件时把等待时间覆写为 1 tick,否则放行原版(游戏规则)逻辑。 + * 配置经 {@link GameConfigService} 每次现读,/sfcraft reload 即时生效。 + */ +public class ElytraPortalModule extends ServerModule { + /** 冲门判定时覆写的等待 tick 数:与原版的创造模式进一致。 */ + private static final int INSTANT_TRANSITION_TICKS = 1; + + @Inject + private GameConfigService config; + + @Override + public void onInitialize() { + // 无注册表内容、无事件订阅:特性完全由 mixin 委托驱动 + } + + /** mixin 在下界传送门询问等待时间时调用;命中即把本次等待覆写为 {@link #INSTANT_TRANSITION_TICKS}。 */ + public boolean shouldTeleportNow(Entity entity) { + if (!(entity instanceof ServerPlayer player) || !player.isFallFlying()) { + return false; + } + double threshold = config.get().elytraPortal.minSpeedBlocksPerSecond; + // 速度阈值 <= 0 视为关闭本特性 + if (threshold <= 0) { + return false; + } + // deltaMovement 单位为 方块/tick,乘 20 换算成米/秒与配置同单位 + return player.getDeltaMovement().length() * 20.0 >= threshold; + } + + public int getInstantTransitionTicks() { + return INSTANT_TRANSITION_TICKS; + } +} diff --git a/src/main/resources/sfcraft.mixins.json b/src/main/resources/sfcraft.mixins.json index ce3f702..ffe6f49 100644 --- a/src/main/resources/sfcraft.mixins.json +++ b/src/main/resources/sfcraft.mixins.json @@ -19,6 +19,7 @@ "common.fixes.EndermanEntityMixin", "common.fixes.LivingEntityMixin", "gameplay.MobCommanderMixin", + "gameplay.NetherPortalBlockMixin", "gameplay.TotemProtectionMixin", "gameplay.ZombieMixin", "gameplay.mount.HoglinMountMixin",