最新版 |
編集中の文章 |
19行目: |
19行目: |
| #* 第五引数(boolean):ディメンションをアンロードしないかどうか | | #* 第五引数(boolean):ディメンションをアンロードしないかどうか |
| # 「DimensionManager#registerDimension」を呼び出し、こちらにもディメンションを登録します。 | | # 「DimensionManager#registerDimension」を呼び出し、こちらにもディメンションを登録します。 |
− |
| |
− | 例
| |
− | <source lang="java" line>
| |
− | package samplemod;
| |
− |
| |
− | import net.minecraft.world.DimensionType;
| |
− | import net.minecraftforge.common.DimensionManager;
| |
− | import net.minecraftforge.fml.common.Mod;
| |
− | import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;
| |
− | import samplemod.world.sample.WorldProviderSample;
| |
− |
| |
− | @Mod(modid = SampleMod.MOD_ID, name = SampleMod.MOD_NAME, version = SampleMod.MOD_VERSION)
| |
− | public class SampleMod {
| |
− | public static final String MOD_ID = "samplemod";
| |
− | public static final String MOD_NAME = "Sample Mod";
| |
− | public static final String MOD_VERSION = "0.1.0";
| |
− |
| |
− | public static DimensionType SAMPLE_DIMENSION;
| |
− |
| |
− | @Mod.EventHandler
| |
− | public void preInit(FMLPreInitializationEvent event) {
| |
− | SAMPLE_DIMENSION = DimensionType.register("Sample Dimension", "_sample", DimensionManager.getNextFreeDimId(), WorldProviderSample.class, false);
| |
− | DimensionManager.registerDimension(SAMPLE_DIMENSION.getId(), SAMPLE_DIMENSION);
| |
− |
| |
− | }
| |
− |
| |
− | }
| |
− |
| |
− | </source>
| |
| | | |
| ==== WorldProvider ==== | | ==== WorldProvider ==== |
56行目: |
27行目: |
| # 「DimensionType」の項で得た、このWorldProviderに対応する「DimensionType」列挙子を「getDimensionType」から返します。 | | # 「DimensionType」の項で得た、このWorldProviderに対応する「DimensionType」列挙子を「getDimensionType」から返します。 |
| | | |
− | {| class="wikitable" | + | {| |
| !フィールド名!!影響!!参考 | | !フィールド名!!影響!!参考 |
| |- | | |- |
75行目: |
46行目: |
| | | |
| | | |
− | {| class="wikitable" | + | {| |
| !メソッド名!!影響!!参考 | | !メソッド名!!影響!!参考 |
| |- | | |- |
98行目: |
69行目: |
| * net.minecraft.client.renderer.EntityRenderer#setupFog | | * net.minecraft.client.renderer.EntityRenderer#setupFog |
| |- | | |- |
− | |getWelcomeMessage||ディメンション入場時のメッセージ?(未使用、Forge)|| | + | |getWelcomeMessage||ディメンション入場時のメッセージ?(未使用)|| |
| |- | | |- |
− | |getDepartMessage||ディメンション退場時のメッセージ?(未使用、Forge)|| | + | |getDepartMessage||ディメンション退場時のメッセージ?(未使用)|| |
| |} | | |} |
| | | |
124行目: |
95行目: |
| ### byte配列のindexに対応するバイオームIDをBiome配列から取り出して代入します(配列はオブジェクトなので、Chunkが持つ配列が変更されます)。 | | ### byte配列のindexに対応するバイオームIDをBiome配列から取り出して代入します(配列はオブジェクトなので、Chunkが持つ配列が変更されます)。 |
| | | |
− | {| class="wikitable" | + | {| |
| !メソッド名!!操作!!参考 | | !メソッド名!!操作!!参考 |
| |- | | |- |
155行目: |
126行目: |
| |} | | |} |
| | | |
− | ===== 例:岩盤の床だけのチャンクを生成する =====
| |
− | Y=0に岩盤の床があるだけのチャンクを生成します。
| |
| | | |
− | ChunkGeneratorEmpty.java
| + | === ポータルを作成する === |
− | <source lang="java" line>
| |
− | package samplemod.world.empty;
| |
− | | |
− | import net.minecraft.entity.EnumCreatureType;
| |
− | import net.minecraft.init.Blocks;
| |
− | import net.minecraft.util.math.BlockPos;
| |
− | import net.minecraft.world.World;
| |
− | import net.minecraft.world.biome.Biome;
| |
− | import net.minecraft.world.chunk.Chunk;
| |
− | import net.minecraft.world.chunk.ChunkPrimer;
| |
− | import net.minecraft.world.chunk.IChunkGenerator;
| |
− | | |
− | import javax.annotation.Nullable;
| |
− | import java.util.List;
| |
− | | |
− | public class ChunkGeneratorEmpty implements IChunkGenerator {
| |
− | private World world;
| |
− | | |
− | public ChunkGeneratorEmpty(World worldIn) {
| |
− | world = worldIn;
| |
− | }
| |
− | | |
− | public void genSurface(Chunk chunk) {
| |
− | for (int x = 0; x < 16; x++)
| |
− | for (int z = 0; z < 16; z++)
| |
− | chunk.setBlockState(new BlockPos(x, 0, z), Blocks.BEDROCK.getDefaultState());
| |
− | }
| |
− | | |
− | @Override
| |
− | public Chunk provideChunk(int x, int z) {
| |
− | ChunkPrimer chunkPrimer = new ChunkPrimer();
| |
− | | |
− | Chunk chunk = new Chunk(world, chunkPrimer, x, z);
| |
− | genSurface(chunk);
| |
− | | |
− | Biome[] abiome = world.getBiomeProvider().getBiomes(null, x * 16, z * 16, 16, 16);
| |
− | byte[] abyte = chunk.getBiomeArray();
| |
− | | |
− | for (int i = 0; i < abiome.length; i++)
| |
− | abyte[i] = (byte) Biome.getIdForBiome(abiome[i]);
| |
− | | |
− | chunk.resetRelightChecks();
| |
− | return chunk;
| |
− | }
| |
− | | |
− | @Override
| |
− | public void populate(int x, int z) {
| |
− | }
| |
− | | |
− | @Override
| |
− | public boolean generateStructures(Chunk chunkIn, int x, int z) {
| |
− | return false;
| |
− | }
| |
− | | |
− | @Override
| |
− | public List<Biome.SpawnListEntry> getPossibleCreatures(EnumCreatureType creatureType, BlockPos pos) {
| |
− | return world.getBiome(pos).getSpawnableList(creatureType);
| |
− | }
| |
− | | |
− | @Nullable
| |
− | @Override
| |
− | public BlockPos getStrongholdGen(World worldIn, String structureName, BlockPos position, boolean p_180513_4_) {
| |
− | return null;
| |
− | }
| |
− | | |
− | @Override
| |
− | public void recreateStructures(Chunk chunkIn, int x, int z) {
| |
− | | |
− | }
| |
− | }
| |
− | | |
− | </source>
| |
− | | |
− | | |
− | === ポータルを生成する ===
| |
| | | |
| ==== Teleporter ==== | | ==== Teleporter ==== |
− | 「net.minecraft.world.Teleporter」は移動するEntityの現在座標をもとにポータルを生成し、ポータル位置にEntityを移動させるクラスです。
| |
− |
| |
− | EntityがDimension移動するのに使われる「net.minecraft.entity.Entity#changeDimension」では、「net.minecraft.world.WorldServer#getDefaultTeleporter」を呼び出し、Teleporter型の「WorldServer#worldTeleporter」を得ています。
| |
− |
| |
− | また、Entityがエンド以外からネザーへ転送される時はTeleporter呼び出し前に座標が1/8倍され、エンド以外からオーバーワールドへ転送されるときは8倍されます(PlayerList#transferEntityToWorldではForgeによりデッドコード化されているため、座標は変更されない)。
| |
− |
| |
− | # 「Teleporter」のサブクラスを作成します。コンストラクタで目的地のWorldServerを受け取ってください。
| |
− | # 「makePortal」、「placeInExistingPortal」を再実装します。「placeInPortal」はエンドへの転送時の足場生成を変更する場合のみ、再実装するとよいでしょう。
| |
− |
| |
− | {| class="wikitable"
| |
− | !メソッド名!!操作
| |
− | |-
| |
− | |makePortal||バニラでは、ネザーポータルの生成位置決定と生成を行う。
| |
− | |-
| |
− | |placeInPortal||PlayerList#transferEntityToWorldから呼ばれる。バニラでは、エンド以外への転送の場合、placeInExistingPortalを呼び出し、falseが返されたらmakePortalを呼び、もう一度placeInExistingPortalを呼び出している。つまり、ポータルの存在の有無によってmakePortalが呼ばれるかどうかが変わり、makePortalで適切にポータルを生成していれば、既存のポータルまたは新しいポータルで転送できる。エンドへの転送の場合、足場となる黒曜石を生成している。
| |
− | |-
| |
− | |placeInExistingPortal||Entity#changeDimensionから呼ばれる。バニラでは、既存の「ポータルブロック」(Blocks.PORTAL)を探索し、見つからない場合falseを返す。見つかった場合、Entityをポータル座標に移動させてtrueを返す。また、キャッシュされていないポータル(座標)のキャッシュもここで行っている。
| |
− | |-
| |
− | |removeStalePortalLocations||worldTimeをもとに、古いポータル(座標)のキャッシュを削除する。
| |
− | |}
| |
− |
| |
− | === エンティティを転送する ===
| |
− | Entityの別のディメンションへの転送方法について解説します。バニラ+Forge環境で利用可能な転送用メソッドは二種類あります。
| |
− |
| |
− | 一つ目の「net.minecraft.entity.Entity#changeDimension」は、ネザーポータル、エンドポータルによるディメンション間移動に使用されています。これは、まずEntity#dimensionを変更したのち、出発地のディメンションに対応するWorldServerからEntityを削除、座標を変更してTeleporterを呼び出し、目的地のディメンションに対応するWorldServerインスタンスを渡してEntityのクローンを作成し、クローンを目的地に強制スポーンさせることでディメンション間移動を実現しています。
| |
− |
| |
− | クローンの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」を経由して呼ばれています。
| |
− |
| |
− | <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で再利用します。
| |
− |
| |
− | また注意すべき点として、「PlayerList#transferEntityToWorld」は、出発地のWorldからEntityを削除しません。自分で出発地のWorldに対して「World#removeEntityDangerously」を呼んで、World及びChunkからEntityを削除する必要があります。
| |
− |
| |
− | ===== 例:「触れるとディメンションを移動するブロック」を追加する =====
| |
− |
| |
− | BlockTeleporter.java
| |
− | <source lang="java" line>
| |
− | package samplemod.block;
| |
− |
| |
− | import net.minecraft.block.Block;
| |
− | import net.minecraft.block.material.Material;
| |
− | import net.minecraft.block.state.IBlockState;
| |
− | 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;
| |
− |
| |
− | public class BlockTeleporter extends Block {
| |
− |
| |
− | public BlockTeleporter() {
| |
− | 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>
| |