最新版 |
編集中の文章 |
258行目: |
258行目: |
| | | |
| === エンティティを転送する === | | === エンティティを転送する === |
− | Entityの別のディメンションへの転送方法について解説します。バニラ+Forge環境で利用可能な転送用メソッドは二種類あります。
| + | Entityの別のディメンションへの転送は、Entity#dimensionを変更したのち、出発地のディメンションに対応するWorldServerからEntityを削除、座標を変更してTeleporterを呼び出し、目的地のディメンションに対応するWorldServerインスタンスを渡してEntityのクローンを作成し、クローンを目的地に強制スポーンさせることで実現しています(Entity#changeDimensionの操作)。 |
− | | |
− | 一つ目の「net.minecraft.entity.Entity#changeDimension」は、ネザーポータル、エンドポータルによるディメンション間移動に使用されています。これは、まずEntity#dimensionを変更したのち、出発地のディメンションに対応するWorldServerからEntityを削除、座標を変更してTeleporterを呼び出し、目的地のディメンションに対応するWorldServerインスタンスを渡してEntityのクローンを作成し、クローンを目的地に強制スポーンさせることでディメンション間移動を実現しています。
| |
| | | |
| クローンのEntity#dimensionはコンストラクタでWorldServerから取得されることになり、最初の代入は無駄にも思えますが、ポータルを生成するTeleporterに渡されるのは最初の(出発地側の)Entityであるため、Teleporter内ではコンストラクタに渡される目的地のWorldServerのディメンションIDとEntity#dimensionは一致することになります(バニラでは転送先はWorldServerから取って判定しているのだが)。 | | クローンのEntity#dimensionはコンストラクタでWorldServerから取得されることになり、最初の代入は無駄にも思えますが、ポータルを生成するTeleporterに渡されるのは最初の(出発地側の)Entityであるため、Teleporter内ではコンストラクタに渡される目的地のWorldServerのディメンションIDとEntity#dimensionは一致することになります(バニラでは転送先はWorldServerから取って判定しているのだが)。 |
| | | |
− | 二つ目の「net.minecraft.server.management.PlayerList#transferEntityToWorld」は、「net.minecraft.entity.player.EntityPlayerMP」で再実装された「#changeDimension」から「PlayerList#changePlayerDimension」を経由して呼ばれています。
| + | ===== PlayerList#transferEntityToWorldを使用する手法 ===== |
− | | + | (未検証) |
− | <small>
| |
− | バニラの「PlayerList#changePlayerDimension」は、ForgeによってTeleporter引数を追加したメソッド「#transferPlayerToDimension」に処理を移されています([https://github.com/MinecraftForge/MinecraftForge/blob/1.11.x/patches/minecraft/net/minecraft/server/management/PlayerList.java.patch#L151 GitHub/MinecraftForge/PlayerList.java.patch#L151])。「#transferPlayerToDimension」は、出発地のWorldServerからEntityPlayerMPを削除したのち、「#transferEntityToWorld」を呼び出しています。
| |
− | </small>
| |
− | | |
− | <small>
| |
− | バニラの「PlayerList#transferEntityToWorld」は、ForgeによってTeleporter引数を追加した同名メソッドに処理を移されています([https://github.com/MinecraftForge/MinecraftForge/blob/1.11.x/patches/minecraft/net/minecraft/server/management/PlayerList.java.patch#L179 GitHub/MinecraftForge/PlayerList.java.patch#L179])。
| |
− | </small>
| |
| | | |
− | 「PlayerList#transferEntityToWorld」のおおよその処理は「Entity#changeDimension」と同じですが、新しくEntityのインスタンスを作り直さず、「Entity#setWorld」を呼んで出発地のWorldに属するEntityインスタンスをそのまま目的地のWorldで再利用します。
| + | ===== 強引にEntity#changeDimensionを使用する手法 ===== |
| + | # AccessTransformerを使って、「private final」な「WorldServer#worldTeleporter」、SRG名「field_85177_Q」のアクセス修飾子を「public」にする(下ソース参照、net.minecraftforge.fml.relauncher.ReflectionHelperでも可)。 |
| + | # 転送時、元々のWorldServer#worldTeleporterを退避して、好きなTeleporterインスタンスを代入。Entity#changeDimensionを呼んでEntityを移動させ、WorldServer#worldTeleporterを復元する。 |
| | | |
− | また注意すべき点として、「PlayerList#transferEntityToWorld」は、出発地のWorldからEntityを削除しません。自分で出発地のWorldに対して「World#removeEntityDangerously」を呼んで、World及びChunkからEntityを削除する必要があります。
| + | META-INF/<modid>_at.cfg |
| + | <source lang="text"> |
| + | public-f net.minecraft.world.WorldServer field_85177_Q #worldTeleporter non-final |
| + | </source> |
| | | |
− | ===== 例:「触れるとディメンションを移動するブロック」を追加する =====
| + | 2の例。ただし以下の点で不完全。 |
| + | * エンド以外から転送する場合、ネザーへの転送では座標が1/8倍、オーバーワールドへの転送では座標が8倍される。 |
| + | ** オーバーワールド-ネザー間では望ましいが、追加ディメンションからオーバーワールドに帰還するときTeleporterに渡される座標が狂う。 |
| | | |
− | BlockTeleporter.java
| |
| <source lang="java" line> | | <source lang="java" line> |
− | package samplemod.block;
| + | public static <T extends Entity> void transferEntity(World worldIn, BlockPos portalPos, T entityIn, |
| + | int destDimension, Function<WorldServer, Teleporter> funcTeleporter, |
| + | Consumer<T> callback) { |
| + | entityIn.setPortal(portalPos); |
| + | entityIn.timeUntilPortal = 10; // この2行は不要? |
| | | |
− | import net.minecraft.block.Block;
| + | if (!worldIn.isRemote && !entityIn.isDead) { |
− | import net.minecraft.block.material.Material;
| + | MinecraftServer server = entityIn.getServer(); |
− | import net.minecraft.block.state.IBlockState;
| + | WorldServer destServer = server.worldServerForDimension(destDimension); |
− | import net.minecraft.entity.Entity;
| |
− | import net.minecraft.entity.player.EntityPlayerMP;
| |
− | import net.minecraft.server.MinecraftServer;
| |
− | import net.minecraft.server.management.PlayerList;
| |
− | import net.minecraft.util.math.AxisAlignedBB;
| |
− | import net.minecraft.util.math.BlockPos;
| |
− | import net.minecraft.world.*;
| |
− | import samplemod.SampleMod;
| |
− | import samplemod.world.sample.TeleporterSample;
| |
| | | |
− | import javax.annotation.Nullable;
| + | Teleporter teleporter = funcTeleporter.apply(destServer); |
| + | Teleporter cache = destServer.worldTeleporter; // Access Transformer | private final -> public |
| | | |
− | public class BlockTeleporter extends Block {
| + | destServer.worldTeleporter = teleporter; |
| + | T clone = (T) entityIn.changeDimension(destDimension); |
| + | callback.accept(clone); // 座標設定等 |
| | | |
− | public BlockTeleporter() {
| + | destServer.worldTeleporter = cache; |
− | super(Material.ROCK); | |
− | }
| |
| | | |
− | @Nullable
| |
− | @Override
| |
− | public AxisAlignedBB getCollisionBoundingBox(IBlockState blockState, IBlockAccess worldIn, BlockPos pos) {
| |
− | return FULL_BLOCK_AABB.expandXyz(-.05d);
| |
− | }
| |
− |
| |
− | @Override
| |
− | public void onEntityCollidedWithBlock(World worldIn, BlockPos pos, IBlockState state, Entity entityIn) {
| |
− | MinecraftServer server = worldIn.getMinecraftServer();
| |
− | if (server != null) {
| |
− | PlayerList playerList = server.getPlayerList();
| |
− | int dest = entityIn.dimension == DimensionType.OVERWORLD.getId() ? SampleMod.SAMPLE_DIMENSION.getId() : DimensionType.OVERWORLD.getId();
| |
− |
| |
− | Teleporter teleporter = new TeleporterSample(server.worldServerForDimension(dest));
| |
− |
| |
− | if (entityIn instanceof EntityPlayerMP) {
| |
− | playerList.transferPlayerToDimension((EntityPlayerMP) entityIn, dest, teleporter);
| |
− | }
| |
− | else {
| |
− | int origin = entityIn.dimension;
| |
− | entityIn.dimension = dest;
| |
− | worldIn.removeEntityDangerously(entityIn);
| |
− |
| |
− | entityIn.isDead = false;
| |
− |
| |
− | playerList.transferEntityToWorld(entityIn, origin, server.worldServerForDimension(origin), server.worldServerForDimension(dest), teleporter);
| |
− | }
| |
− | }
| |
| } | | } |
| } | | } |
− |
| |
| </source> | | </source> |