Better Teleporter
基本信息
| 属性 | 值 |
|---|---|
| 类名 | net.blay09.mods.netherportalfix.BetterTeleporter |
| 父类 | net.minecraft.world.Teleporter |
| 注册范围 | Overworld (dimensionId == 0) 和 Nether (dimensionId == -1) |
| 安装方式 | 替换 WorldServer.worldTeleporter 字段 |
| 触发事件 | WorldEvent.Load(NetherPortalFix.onWorldLoad) |
| modid | netherportalfix |
功能
原版 Minecraft 的 Teleporter 在多人模式中存在一个传送目的地错配问题:玩家 A 进入下界时原版会缓存 目的地坐标,但如果另一位玩家 B 之后又从同一区块使用过同一对传送门,原版算法会把缓存的"目的地坐标"覆盖为 B 当时进入的方向,导致 A 返回主世界时被错误地传送到 B 的出口位置(而非自己的入口)。
BetterTeleporter 通过在玩家个体 NBT 上保存返回门户历史(每条记录含 From/To X/Y/Z 与 From/To Dim),并在 placeInPortal(玩家出传送门)与 placeInExistingPortal(玩家进入已存在传送门)两个时机维护这份历史,确保玩家返回主世界时仍能落在自己出发的那个传送门附近。
非玩家实体(僵尸、动物等)走原版 Teleporter 路径,不享受修复。
触发条件
- 仅
WorldServer且维度为0(主世界)或-1(下界)时被替换。 - 世界加载事件
WorldEvent.Load触发时一次性安装(@SubscribeEvent)。 - 仅在玩家进入/退出传送门时被调用,对正常移动无影响。
关键常量
| 常量 | 值 | 含义 |
|---|---|---|
PORTAL_RANGE_SQR |
9 |
返回门户匹配时的平方距离阈值(实际欧氏距离 ≤ 3 格) |
NBT_RETURN_PORTALS |
"ReturnPortals" |
玩家 NBT 中存储返回门户历史列表的 tag 名 |
NBT_FROM_X/Y/Z/DIM |
"FromX"/"FromY"/"FromZ"/"FromDim" |
来源侧(出发时)坐标与维度 |
NBT_TO_X/Y/Z/DIM |
"ToX"/"ToY"/"ToZ"/"ToDim" |
目的地侧(到达后)坐标与维度 |
返回门户历史以 NBTTagList 形式存放在玩家 EntityPlayer.PERSISTED_NBT_TAG 标签下,跨维度持久化。
placeInPortal(玩家出传送门)
调用时机:玩家到达目标维度并即将在传送门中生成。
算法:
- 在调用
super.placeInPortal之前先构造from = new PortalPositionAndDimension(oldX, oldY, oldZ)记录出发地。 - 调
super.placeInPortal(entity, oldX, oldY, oldZ, rotationYaw),由原版逻辑决定玩家实际落地坐标to = new PortalPositionAndDimension(player.getPlayerCoordinates())。 - 反向遍历
tagList(从末尾到头),把所有to与当前to在同一维度且平方距离 ≤PORTAL_RANGE_SQR(≤3 格)的旧记录全部清除,避免同一出口被重复登记。 - 把当前的
(from, to)打包成新NBTTagCompound并appendTag到列表末尾。
效果:每次玩家"出"传送门就在 NBT 中追加一条 (from, to) 记录。
placeInExistingPortal(玩家进入已存在的传送门)
调用时机:玩家进入传送门(通常是返回主世界)。
算法:
- 反向遍历
tagList(从末尾到头),逐条检查候选to:- 若
to.dimensionId == entity.worldObj.provider.dimensionId(同维度) - 且
to.getDistanceSquaredToChunkCoordinates(lastPortalPosition) ≤ PORTAL_RANGE_SQR(≤3 格) - 则视为匹配当前出口位置。
- 若
- 取出该条记录的
from(原始入口坐标与维度),用ChunkCoordIntPair.chunkXZ2Int(x, y)作为 key 把它临时注入到destinationCoordinateCache:- 若该 key 不存在则同步追加到
destinationCoordinateKeys。 - 如果
destinationCoordinateCache之前已有旧值,先把它备份到oldValue。
- 若该 key 不存在则同步追加到
- 调用
super.placeInExistingPortal,原版逻辑此时会按缓存中的from把玩家放到原入口附近。 placeInExistingPortal返回后,把oldValue(如有)还原回缓存——这样下一次其他玩家进入同一区块时不会被本玩家的"私人目的地"污染。- 移除本次匹配的
tagList条目并写回玩家 NBT。
效果:把玩家私人的入口处作为目的地,避开原版"任何人都能覆盖全局缓存"的并发问题。
PortalPositionAndDimension
BetterTeleporter 内的内部类:
| 字段 | 类型 | 含义 |
|---|---|---|
posX/posY/posZ |
int(继承自 PortalPosition) |
坐标 |
lastUpdateTime |
long(继承自 PortalPosition) |
时间戳,由 super(...) 写入 world.getWorldTime() |
dimensionId |
int |
玩家所在维度 ID(在主世界为 0,在下界为 -1) |
三个构造器分别接受 ChunkCoordinates、(x,y,z)(使用当前 world.provider.dimensionId)、(x,y,z,dim)。
用于把"维度 + 坐标"绑定到一处比较距离时只需比较坐标(前提是已校验维度相等)。
数值
| 数值 | 默认值 | 说明 |
|---|---|---|
| 匹配半径(平方) | 9 | 平方欧氏距离 ≤ 9 → 实际 ≤ 3 格 |
| 单玩家 NBT 列表上限 | 无显式限制 | 每次出传送门新增一条,列表随玩家出入持续增长 |
| 玩家实体支持 | EntityPlayer |
非玩家实体走原版路径,不享受修复 |
| 维度支持 | 0 / -1 | 仅主世界与下界;末地(1)走原版 Teleporter |
限制
- 末地(维度 1)未安装:从末地返回主世界不会走
BetterTeleporter,原版的错配问题仍存在。 - 非玩家实体未保护:僵尸、动物、掉落物走原版路径。
- NBT 列表无界:长时间在线玩家若频繁进出传送门,
ReturnPortals列表可能膨胀。 - 异步竞态:
destinationCoordinateCache是WorldServer单例,依赖"放入-调用-还原"三步序列在同一个 tick 内完成;其他 mod 若在placeInExistingPortal内插入事件,可能破坏还原时序。
第三方 mod 集成
本类只重写 Teleporter 的两个方法,不调用任何跨 mod API。无需第三方 mod 依赖即可工作。