Commits

Ricky Brent  committed a8de653

My changes to Waco's code for multiplayer. Chests do not work right, but
they work on the server with a vanilla client.

  • Participants
  • Parent commits 6fb12c6
  • Branches 1.5.1

Comments (0)

Files changed (5)

File mod/ymt/air/AirCraftCore.java

  *
  */
 public class AirCraftCore extends NekonoteCore {
-	private static final boolean DEBUG_RENDER_IMITATOR = false;
-	private static final boolean DEBUG_RENDER_INVISIBLE = false;
-	private static final boolean DEBUG_RENDER_MAT = false;
-	private static final AirCraftCore instance = new AirCraftCore();
-
-	private int blockIdPyxis = 209;
-	private int blocklimit = 2000;
-	private int craftBodySize = -1;
-	private int moveKeepTime = 20 * 60;
-
-	private int entityIdPyxis = 0;
-	private int entityIdInvisible = 0;
-	private int entityIdMobMat = 0;
-
-	private BaseMod baseMod = null;
-	private Block blockPyxis = null;
-
-	public final AirCraftNetHandler net = new AirCraftNetHandler(this);
-	private final Operator[] blockops = new Operator[Block.blocksList.length];
-	private final Collection<EntityImitator> imitatorServer = new WeakEntityCollection<EntityImitator>();
-	private final Collection<EntityImitator> imitatorClient = new WeakEntityCollection<EntityImitator>();
-
-	public final Set<Integer> targetBlockId = new TreeSet<Integer>();
-	public final Set<Integer> appendixBlockId = new TreeSet<Integer>();
-	public final Set<Integer> ignoredBlockId = new TreeSet<Integer>();
-
-	private AirCraftCore() {
-		;
-	}
-
-	public void addRenderer(Map map) {
-		// Imitator
-		if (DEBUG_RENDER_IMITATOR)
-			map.put(EntityImitator.class, new RenderDebugEntity(Block.blockDiamond));
-		else
-			map.put(EntityImitator.class, new RenderPyxis());
-
-		// Invisible
-		if (DEBUG_RENDER_INVISIBLE)
-			map.put(EntityCraftBody.class, new RenderDebugEntity(Block.glass));
-		else
-			map.put(EntityCraftBody.class, new RenderNothing());
-
-		// Mat
-		if (DEBUG_RENDER_MAT)
-			map.put(EntityMobMat.class, new RenderDebugEntity(Block.cloth));
-		else
-			map.put(EntityMobMat.class, new RenderNothing());
-	}
-
-	public int getBlockIdPyxis() {
-		return blockIdPyxis;
-	}
-
-	public Operator getBlockOperator(int blockId) {
-		if (0 < blockId && blockId < blockops.length) {
-			Operator result = blockops[blockId];
-			if (result != null) {
-				return result;
-			}
-		}
-		return NormalOperator.INSTANCE;
-	}
-
-	public Block getBlockPyxis() {
-		return blockPyxis;
-	}
-
-	public int getCraftBodySize() {
-		return craftBodySize;
-	}
-
-	public Set<Integer> getDefaultMoveableSet() {
-		Set<Integer> result = new TreeSet<Integer>();
-		for (int i = 0; i < blockops.length; i++) { // target が未指定の時には defaultMoveableSet
-			if (blockops[i] != null) {
-				result.add(i);
-			}
-		}
-		return result;
-	}
-
-	public Set<Integer> getMoveableBlockIds() {
-		Set<Integer> result = new HashSet<Integer>();
-
-		// targetBlockId
-		if (targetBlockId.isEmpty())
-			result.addAll(getDefaultMoveableSet());
-		else
-			result.addAll(targetBlockId);
-
-		// appendixBlockId
-		result.addAll(appendixBlockId);
-
-		// ignoredBlockId
-		result.removeAll(ignoredBlockId);
-
-		// その他
-		result.remove(0); // 空気ブロックは移動禁止
-		result.remove(Block.bedrock.blockID); // 岩盤は移動禁止
-
-		// 完成
-		return result;
-	}
-
-	public int getMoveKeepTime() {
-		return moveKeepTime;
-	}
-
-	public Materializer newMaterializer(World world, ImitationSpace space) {
-		Set<Integer> moveable = Utils.isServerSide(world) ? getMoveableBlockIds() : new TreeSet<Integer>();
-		return new Materializer(world, space, blocklimit, moveable);
-	}
-
-	public AirCraftMoveHandler newMoveHandler(World worldObj, EntityCraftCore craftCore, String playerName) {
-		return new AirCraftMoveHandler(craftCore, playerName, getMoveKeepTime());
-	}
-
-	public void processAppendSemiSurface(int entId, byte[] data) {
-		synchronized (imitatorClient) {
-			for (EntityImitator cli: imitatorClient) {
-				if (cli != null && cli.entityId == entId) {
-					debugPrint("receive appendSemiSurface: sender = %s, size = %s", entId, data.length);
-					cli.addClientSemiSurfaces(data);
-				}
-			}
-		}
-	}
-
-	public void processAppendSurface(int entId, byte[] data) {
-		synchronized (imitatorClient) {
-			for (EntityImitator cli: imitatorClient) {
-				if (cli != null && cli.entityId == entId) {
-					debugPrint("receive appendSurface: sender = %s, size = %s", entId, data.length);
-					cli.addClientSurfaces(data);
-				}
-			}
-		}
-	}
-
-	public void processAppendTileEntityData(int entId, byte[] data) {
-		synchronized (imitatorClient) {
-			for (EntityImitator cli: imitatorClient) {
-				if (cli != null && cli.entityId == entId) {
-					debugPrint("receive appendTileData: sender = %s, size = %s", entId, data.length);
-					cli.addClientTileData(data);
-				}
-			}
-		}
-	}
-
-	public void processMoveClient(byte type, String name) {
-		processMove(imitatorClient, type, name);
-	}
-
-	public void processMoveServer(byte type, String name) {
-		processMove(imitatorServer, type, name);
-	}
-
-	public void processRequestSurfaces(int entId) {
-		synchronized (imitatorServer) {
-			for (EntityImitator svr: imitatorServer) {
-				if (svr != null && svr.entityId == entId) {
-					debugPrint("receive requestSurface: sender = %s", entId);
-					svr.requestSurfaceFromClient();
-				}
-			}
-		}
-	}
-
-	public void registerImitator(EntityImitator imitator) {
-		if (Utils.isServerSide(imitator.worldObj)) {
-			synchronized (imitatorServer) {
-				imitatorServer.add(imitator);
-			}
-		}
-		else {
-			synchronized (imitatorClient) {
-				imitatorClient.add(imitator);
-			}
-		}
-	}
-
-	public void setBaseMod(BaseMod baseMod) {
-		this.baseMod = baseMod;
-	}
-
-	public void setBlockIdPyxis(int blockIdPyxis) {
-		this.blockIdPyxis = blockIdPyxis;
-	}
-
-	public void setBlocklimit(int blocklimit) {
-		this.blocklimit = blocklimit;
-	}
-
-	public void setBlockOperator(int blockId, Operator operator) {
-		if (0 < blockId && blockId < blockops.length) {
-			if (blockops[blockId] != null) {
-				debugPrint("BlockOperator[%d] overwrite %s -> %s", blockId, blockops[blockId], operator);
-			}
-			blockops[blockId] = operator;
-		}
-	}
-
-	public void setCraftBodySize(int craftBodySize) {
-		this.craftBodySize = craftBodySize;
-	}
-
-	public void setMoveKeepTime(int moveKeepTime) {
-		this.moveKeepTime = moveKeepTime;
-	}
-
-	public Entity spawnEntity(int entId, World world, double x, double y, double z) {
-		Entity result = null;
-		{
-			if (entId == entityIdInvisible)
-				result = new EntityCraftBody(world);
-			else if (entId == entityIdPyxis)
-				result = new EntityPyxis(world);
-			else if (entId == entityIdMobMat)
-				result = new EntityMobMat(world);
-		}
-		if (result != null) {
-			result.setPosition(x, y, z);
-		}
-		return result;
-	}
-
-	protected Operator[] getDefaultOperators() {
-		return new Operator[]{
-			NormalOperator.INSTANCE,
-			new DelicateOperator(),
-			new DirectionalOperator(),
-			new DelicateDirectionalOperator(),
-			new DoorOperator(),
-			new FluidOperator(),
-			new LadderOperator(),
-			new RailOperator(),
-			new RailPoweredOperator(),
-			new StairsOperator(),
-			new TorchOperator(),
-			new TrapdoorOperator(),
-			new VineOperator(),
-			new WoodOperator(),
-			new ButtonOperator(),
-			new LeverOperator(),
-			new InventoryBlockOperator(),
-			new ChestOperator(),
-			new EnderChestOperator(),
-			new AnvilOperator(),
-		};
-	}
-
-	@Override
-	protected void init() {
-		if (0 < blockIdPyxis) {
-			// ブロック登録
-			blockPyxis = new BlockPyxis(blockIdPyxis).setUnlocalizedName("Pyxis");
-			ModLoader.registerBlock(blockPyxis);
-			Utils.addName(blockPyxis, "Pyxis", "羅針盤");
-			ModLoader.addRecipe(new ItemStack(blockPyxis), new Object[]{
-				" C ", "DGD", "OOO", 'C', Item.compass, 'D', Item.diamond, 'G', Block.blockGold, 'O', Block.obsidian
-			});
-
-			// エンティティ登録
-			ModLoader.registerEntityID(EntityPyxis.class, "HAC_Pyxis", entityIdPyxis = Utils.getUnusedEntityID());
-			ModLoader.registerEntityID(EntityCraftBody.class, "HAC_CraftBody", entityIdInvisible = Utils.getUnusedEntityID());
-			ModLoader.registerEntityID(EntityMobMat.class, "HAC_MobMat", entityIdMobMat = Utils.getUnusedEntityID());
-			ModLoader.addEntityTracker(baseMod, EntityPyxis.class, entityIdPyxis, 256, 4, true); // 描写距離とりあえず広めにとる
-			ModLoader.addEntityTracker(baseMod, EntityCraftBody.class, entityIdInvisible, 32, 4, true);
-			ModLoader.addEntityTracker(baseMod, EntityMobMat.class, entityIdMobMat, 32, 4, true);
-
-			// チャネル登録
-			net.registerPacketChannel(baseMod);
-
-			// Operator 登録
-			for (Operator op: getDefaultOperators()) {
-				op.register(this);
-			}
-		}
-	}
-
-	protected void processMove(Collection<EntityImitator> imitators, byte type, String name) {
-		synchronized (imitators) {
-			for (EntityCraftCore imitator: imitators) {
-				if (imitator != null) {
-					imitator.processMove(null, type);
-				}
-			}
-		}
-	}
-
-	public static AirCraftCore getInstance() {
-		return instance;
-	}
+    private static final boolean DEBUG_RENDER_IMITATOR = false;
+    private static final boolean DEBUG_RENDER_INVISIBLE = false;
+    private static final boolean DEBUG_RENDER_MAT = false;
+    private static final AirCraftCore instance = new AirCraftCore();
+
+    private int blockIdPyxis = 209;
+    private int blocklimit = 2000;
+    private int craftBodySize = -1;
+    private int moveKeepTime = 20 * 60;
+
+    private int entityIdPyxis = 0;
+    private int entityIdInvisible = 0;
+    private int entityIdMobMat = 0;
+
+    private BaseMod baseMod = null;
+    private Block blockPyxis = null;
+
+    public final AirCraftNetHandler net = new AirCraftNetHandler(this);
+    private final Operator[] blockops = new Operator[Block.blocksList.length];
+    private final Collection<EntityImitator> imitatorServer = new WeakEntityCollection<EntityImitator>();
+    private final Collection<EntityImitator> imitatorClient = new WeakEntityCollection<EntityImitator>();
+
+    public final Set<Integer> targetBlockId = new TreeSet<Integer>();
+    public final Set<Integer> appendixBlockId = new TreeSet<Integer>();
+    public final Set<Integer> ignoredBlockId = new TreeSet<Integer>();
+
+    private AirCraftCore() {
+        ;
+    }
+
+    public void addRenderer(Map map) {
+        // Imitator
+        if (DEBUG_RENDER_IMITATOR)
+            map.put(EntityImitator.class, new RenderDebugEntity(Block.blockDiamond));
+        else
+            map.put(EntityImitator.class, new RenderPyxis());
+
+        // Invisible
+        if (DEBUG_RENDER_INVISIBLE)
+            map.put(EntityCraftBody.class, new RenderDebugEntity(Block.glass));
+        else
+            map.put(EntityCraftBody.class, new RenderNothing());
+
+        // Mat
+        if (DEBUG_RENDER_MAT)
+            map.put(EntityMobMat.class, new RenderDebugEntity(Block.cloth));
+        else
+            map.put(EntityMobMat.class, new RenderNothing());
+    }
+
+    public int getBlockIdPyxis() {
+        return blockIdPyxis;
+    }
+
+    public Operator getBlockOperator(int blockId) {
+        if (0 < blockId && blockId < blockops.length) {
+            Operator result = blockops[blockId];
+            if (result != null) {
+                return result;
+            }
+        }
+        return NormalOperator.INSTANCE;
+    }
+
+    public Block getBlockPyxis() {
+        return blockPyxis;
+    }
+
+    public int getCraftBodySize() {
+        return craftBodySize;
+    }
+
+    public Set<Integer> getDefaultMoveableSet() {
+        Set<Integer> result = new TreeSet<Integer>();
+        for (int i = 0; i < blockops.length; i++) { // target が未指定の時には defaultMoveableSet
+            if (blockops[i] != null) {
+                result.add(i);
+            }
+        }
+        return result;
+    }
+
+    public Set<Integer> getMoveableBlockIds() {
+        Set<Integer> result = new HashSet<Integer>();
+
+        // targetBlockId
+        if (targetBlockId.isEmpty())
+            result.addAll(getDefaultMoveableSet());
+        else
+            result.addAll(targetBlockId);
+
+        // appendixBlockId
+        result.addAll(appendixBlockId);
+
+        // ignoredBlockId
+        result.removeAll(ignoredBlockId);
+
+        // その他
+        result.remove(0); // 空気ブロックは移動禁止
+        result.remove(Block.bedrock.blockID); // 岩盤は移動禁止
+
+        // 完成
+        return result;
+    }
+
+    public int getMoveKeepTime() {
+        return moveKeepTime;
+    }
+
+    public Materializer newMaterializer(World world, ImitationSpace space) {
+        Set<Integer> moveable = Utils.isServerSide(world) ? getMoveableBlockIds() : new TreeSet<Integer>();
+        return new Materializer(world, space, blocklimit, moveable);
+    }
+
+    public AirCraftMoveHandler newMoveHandler(World worldObj, EntityCraftCore craftCore, String playerName) {
+        return new AirCraftMoveHandler(craftCore, playerName, getMoveKeepTime());
+    }
+
+    public void processAppendSemiSurface(int entId, byte[] data) {
+        synchronized (imitatorClient) {
+            for (EntityImitator cli: imitatorClient) {
+                if (cli != null && cli.entityId == entId) {
+                    debugPrint("receive appendSemiSurface: sender = %s, size = %s", entId, data.length);
+                    cli.addClientSemiSurfaces(data);
+                }
+            }
+        }
+    }
+
+    public void processAppendSurface(int entId, byte[] data) {
+        synchronized (imitatorClient) {
+            for (EntityImitator cli: imitatorClient) {
+                if (cli != null && cli.entityId == entId) {
+                    debugPrint("receive appendSurface: sender = %s, size = %s", entId, data.length);
+                    cli.addClientSurfaces(data);
+                }
+            }
+        }
+    }
+
+    public void processAppendTileEntityData(int entId, byte[] data) {
+        synchronized (imitatorClient) {
+            for (EntityImitator cli: imitatorClient) {
+                if (cli != null && cli.entityId == entId) {
+                    debugPrint("receive appendTileData: sender = %s, size = %s", entId, data.length);
+                    cli.addClientTileData(data);
+                }
+            }
+        }
+    }
+
+    public void processMoveClient(byte type, String name) {
+        processMove(imitatorClient, type, name);
+    }
+
+    public void processMoveServer(byte type, String name) {
+        processMove(imitatorServer, type, name);
+    }
+
+    public void processRequestSurfaces(int entId) {
+        synchronized (imitatorServer) {
+            for (EntityImitator svr: imitatorServer) {
+                if (svr != null && svr.entityId == entId) {
+                    debugPrint("receive requestSurface: sender = %s", entId);
+                    svr.requestSurfaceFromClient();
+                }
+            }
+        }
+    }
+
+    public void registerImitator(EntityImitator imitator) {
+        if (Utils.isServerSide(imitator.worldObj)) {
+            synchronized (imitatorServer) {
+                imitatorServer.add(imitator);
+            }
+        }
+        else {
+            synchronized (imitatorClient) {
+                imitatorClient.add(imitator);
+            }
+        }
+    }
+
+    public void setBaseMod(BaseMod baseMod) {
+        this.baseMod = baseMod;
+    }
+
+    public void setBlockIdPyxis(int blockIdPyxis) {
+        this.blockIdPyxis = blockIdPyxis;
+    }
+
+    public void setBlocklimit(int blocklimit) {
+        this.blocklimit = blocklimit;
+    }
+
+    public void setBlockOperator(int blockId, Operator operator) {
+        if (0 < blockId && blockId < blockops.length) {
+            if (blockops[blockId] != null) {
+                debugPrint("BlockOperator[%d] overwrite %s -> %s", blockId, blockops[blockId], operator);
+            }
+            blockops[blockId] = operator;
+        }
+    }
+
+    public void setCraftBodySize(int craftBodySize) {
+        this.craftBodySize = craftBodySize;
+    }
+
+    public void setMoveKeepTime(int moveKeepTime) {
+        this.moveKeepTime = moveKeepTime;
+    }
+
+    public Entity spawnEntity(int entId, World world, double x, double y, double z) {
+        Entity result = null;
+        {
+            if (entId == entityIdInvisible)
+                result = new EntityCraftBody(world);
+            else if (entId == entityIdPyxis)
+                result = new EntityPyxis(world);
+            else if (entId == entityIdMobMat)
+                result = new EntityMobMat(world);
+        }
+        if (result != null) {
+            result.setPosition(x, y, z);
+        }
+        return result;
+    }
+
+    protected Operator[] getDefaultOperators() {
+        return new Operator[]{
+            NormalOperator.INSTANCE,
+            new DelicateOperator(),
+            new DirectionalOperator(),
+            new DelicateDirectionalOperator(),
+            new DoorOperator(),
+            new FluidOperator(),
+            new LadderOperator(),
+            new RailOperator(),
+            new RailPoweredOperator(),
+            new StairsOperator(),
+            new TorchOperator(),
+            new TrapdoorOperator(),
+            new VineOperator(),
+            new WoodOperator(),
+            new ButtonOperator(),
+            new LeverOperator(),
+            new InventoryBlockOperator(),
+            new ChestOperator(),
+            new EnderChestOperator(),
+            new AnvilOperator(),
+        };
+    }
+
+    @Override
+    protected void init() {
+        if (0 < blockIdPyxis) {
+            // ブロック登録
+            blockPyxis = new BlockPyxis(blockIdPyxis); //.setUnlocalizedName("Pyxis");
+            ModLoader.registerBlock(blockPyxis);
+            Utils.addName(blockPyxis, "Pyxis", "羅針盤");
+            ModLoader.addRecipe(new ItemStack(blockPyxis), new Object[]{
+                " C ", "DGD", "OOO", 'C', Item.compass, 'D', Item.diamond, 'G', Block.blockGold, 'O', Block.obsidian
+            });
+
+            // エンティティ登録
+            ModLoader.registerEntityID(EntityPyxis.class, "HAC_Pyxis", entityIdPyxis = Utils.getUnusedEntityID());
+            ModLoader.registerEntityID(EntityCraftBody.class, "HAC_CraftBody", entityIdInvisible = Utils.getUnusedEntityID());
+            ModLoader.registerEntityID(EntityMobMat.class, "HAC_MobMat", entityIdMobMat = Utils.getUnusedEntityID());
+            ModLoader.addEntityTracker(baseMod, EntityPyxis.class, entityIdPyxis, 256, 4, true); // 描写距離とりあえず広めにとる
+            ModLoader.addEntityTracker(baseMod, EntityCraftBody.class, entityIdInvisible, 32, 4, true);
+            ModLoader.addEntityTracker(baseMod, EntityMobMat.class, entityIdMobMat, 32, 4, true);
+
+            // チャネル登録
+            net.registerPacketChannel(baseMod);
+
+            // Operator 登録
+            for (Operator op: getDefaultOperators()) {
+                op.register(this);
+            }
+        }
+    }
+
+    protected void processMove(Collection<EntityImitator> imitators, byte type, String name) {
+        synchronized (imitators) {
+            for (EntityCraftCore imitator: imitators) {
+                if (imitator != null) {
+                    EntityCraftCore owner = EntityCraftCore.getEntityCore(imitator.worldObj, imitator.getOwnerName());
+                    if (owner == null) {
+                        imitator.processMove(null, type);
+                    } else {
+                        if (name.equals(owner.getPlayerName())) {
+                            imitator.processMove(null, type);
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    public static AirCraftCore getInstance() {
+        return instance;
+    }
 }

File mod/ymt/air/EntityCraftCore.java

  *
  */
 public abstract class EntityCraftCore extends EntityAirCraft {
-	protected final List<EntityAirCraft> subEntity = new ArrayList<EntityAirCraft>();
-
-	public EntityCraftCore(World world) {
-		super(world);
-	}
-
-	public void addSubEntity(EntityAirCraft ent) {
-		subEntity.add(ent);
-	}
-
-	public boolean adjustPositionAndRotation() {
-		this.isAirBorne = true; // 強制更新
-		double x = Math.floor(posX) + 0.5;
-		double z = Math.floor(posZ) + 0.5;
-		double y = Math.round(posY);
-		int d = getDirection(this);
-		return trySetPositionAndRotation(x, y, z, d * 90, 0);
-	}
-
-	public int getDirectionOffset() {
-		return 0;
-	}
-
-	public void moveCraft(double mx, double my, double mz, float yaw, float pitch) {
-		trySetPositionAndRotation(posX + mx, posY + my, posZ + mz, rotationYaw + yaw, rotationPitch + pitch);
-	}
-
-	public abstract void processMove(String name, byte type);
-
-	@Override
-	public void setPositionAndRotation(double x, double y, double z, float yaw, float pitch) {
-		trySetPositionAndRotation(x, y, z, yaw, pitch);
-	}
-
-	@Override
-	public void stopImmediately() {
-		super.stopImmediately();
-		// サブエンティティの停止
-		for (EntityAirCraft ent: subEntity) {
-			if (ent instanceof EntityFollower) {
-				ent.stopImmediately();
-			}
-		}
-	}
-
-	private boolean trySetPositionAndRotation(double x, double y, double z, float yaw, float pitch) {
-		// 動かす前の値を保存
-		double prevPosX = posX;
-		double prevPosY = posY;
-		double prevPosZ = posZ;
-		float prevYaw = rotationYaw;
-		float prevPitch = rotationPitch;
-		// 動かす
-		EntityAirCraft collided = tryUpdatePosition(x, y, z, yaw, pitch);
-		if (collided == null) {
-			return true; // success
-		}
-		else {
-			onEntityHitBlocks(collided);
-			// 戻す
-			tryUpdatePosition(prevPosX, prevPosY, prevPosZ, prevYaw, prevPitch);
-			return false; // failure
-		}
-	}
-
-	protected void cleanSubEntity() {
-		Iterator<EntityAirCraft> iter = subEntity.iterator();
-		while (iter.hasNext()) {
-			Entity ent = iter.next();
-			if (ent == null || ent.isDead) {
-				iter.remove();
-			}
-		}
-	}
-
-	protected void onEntityHitBlocks(EntityAirCraft ent) {
-		core.debugPrint("onEntityHitBlocks %s", ent);
-	}
-
-	protected abstract void onEntityPositionUpdate();
-
-	protected EntityAirCraft tryUpdatePosition(double x, double y, double z, float yaw, float pitch) {
-		super.setNextPosition(x, y, z, yaw, pitch, 1); // これ setPositionAndRotation 使っちゃうと、描画のブレが大きくなっちゃうんだね……
-		if (isEntityHitBlocks()) {
-			return this;
-		}
-		for (EntityAirCraft ent: subEntity) {
-			if (ent instanceof EntityFollower) {
-				EntityFollower follower = (EntityFollower) ent;
-				follower.updateFollowerPosition();
-				if (follower.isEntityHitBlocks()) {
-					return follower;
-				}
-			}
-		}
-		return null;
-	}
-
-	public static EntityCraftCore getEntityCore(World world, String name) {
-		if (name != null) {
-			for (Object obj: world.loadedEntityList) {
-				if (obj instanceof EntityCraftCore) {
-					EntityCraftCore entity = (EntityCraftCore) obj;
-					if (name.equals(entity.getOwnerName())) {
-						return entity;
-					}
-				}
-			}
-		}
-		return null;
-	}
-
-	protected static int getDirection(Entity ent) {
-		return MathHelper.floor_double((ent.rotationYaw * 4.0F / 360.0F) + 0.5D) & 3;
-	}
+    protected final List<EntityAirCraft> subEntity = new ArrayList<EntityAirCraft>();
+
+    public EntityCraftCore(World world) {
+        super(world);
+    }
+
+    public void addSubEntity(EntityAirCraft ent) {
+        subEntity.add(ent);
+    }
+
+    public boolean adjustPositionAndRotation() {
+        this.isAirBorne = true; // 強制更新
+        double x = Math.floor(posX) + 0.5;
+        double z = Math.floor(posZ) + 0.5;
+        double y = Math.round(posY);
+        int d = getDirection(this);
+        return trySetPositionAndRotation(x, y, z, d * 90, 0);
+    }
+
+    public int getDirectionOffset() {
+        return 0;
+    }
+
+    public void moveCraft(double mx, double my, double mz, float yaw, float pitch) {
+        trySetPositionAndRotation(posX + mx, posY + my, posZ + mz, rotationYaw + yaw, rotationPitch + pitch);
+    }
+
+    public abstract void processMove(String name, byte type);
+
+    @Override
+    public void setPositionAndRotation(double x, double y, double z, float yaw, float pitch) {
+        trySetPositionAndRotation(x, y, z, yaw, pitch);
+    }
+
+    @Override
+    public void stopImmediately() {
+        super.stopImmediately();
+        // サブエンティティの停止
+        for (EntityAirCraft ent: subEntity) {
+            if (ent instanceof EntityFollower) {
+                ent.stopImmediately();
+            }
+        }
+    }
+
+    private boolean trySetPositionAndRotation(double x, double y, double z, float yaw, float pitch) {
+        // 動かす前の値を保存
+        double prevPosX = posX;
+        double prevPosY = posY;
+        double prevPosZ = posZ;
+        float prevYaw = rotationYaw;
+        float prevPitch = rotationPitch;
+        // 動かす
+        EntityAirCraft collided = tryUpdatePosition(x, y, z, yaw, pitch);
+        if (collided == null) {
+            return true; // success
+        }
+        else {
+            onEntityHitBlocks(collided);
+            // 戻す
+            tryUpdatePosition(prevPosX, prevPosY, prevPosZ, prevYaw, prevPitch);
+            return false; // failure
+        }
+    }
+
+    protected void cleanSubEntity() {
+        Iterator<EntityAirCraft> iter = subEntity.iterator();
+        while (iter.hasNext()) {
+            Entity ent = iter.next();
+            if (ent == null || ent.isDead) {
+                iter.remove();
+            }
+        }
+    }
+
+    protected void onEntityHitBlocks(EntityAirCraft ent) {
+        core.debugPrint("onEntityHitBlocks %s", ent);
+    }
+
+    protected abstract void onEntityPositionUpdate();
+
+    protected EntityAirCraft tryUpdatePosition(double x, double y, double z, float yaw, float pitch) {
+        super.setNextPosition(x, y, z, yaw, pitch, 1); // これ setPositionAndRotation 使っちゃうと、描画のブレが大きくなっちゃうんだね……
+        if (isEntityHitBlocks()) {
+            return this;
+        }
+        for (EntityAirCraft ent: subEntity) {
+            if (ent instanceof EntityFollower) {
+                EntityFollower follower = (EntityFollower) ent;
+                follower.updateFollowerPosition();
+                if (follower.isEntityHitBlocks()) {
+                    return follower;
+                }
+            }
+        }
+        return null;
+    }
+
+    public static EntityCraftCore getEntityCore(World world, String name) {
+        if (name != null) {
+            for (Object obj: world.loadedEntityList) {
+                if (obj instanceof EntityCraftCore) {
+                    EntityCraftCore entity = (EntityCraftCore) obj;
+                    if (name.equals(entity.getOwnerName())) {
+                        return entity;
+                    }
+                }
+            }
+        }
+        return null;
+    }
+
+    protected static int getDirection(Entity ent) {
+        return MathHelper.floor_double((ent.rotationYaw * 4.0F / 360.0F) + 0.5D) & 3;
+    }
+
+
+    public String getPlayerName() {
+        // Return null here so we may try to call it when looking for the player
+        return null;
+    }
 }

File mod/ymt/air/op/ChestOperator.java

  *
  */
 public class ChestOperator extends InventoryBlockOperator {
-	private final ModelChest modelChest = new ModelChest();
-	private final ModelChest modelLargeChest = new ModelLargeChest();
-
-	@Override
-	public boolean hasSpecialRender() {
-		return true;
-	}
-
-	@Override
-	public void renderBlock(RenderBlocks render, BlockData data) {
-		;
-	}
-
-	@Override
-	public void renderBlockSpecial(RenderManager manager, RenderBlocks render, BlockData data) {
-		Block block = data.block;
-		int metadata = data.metadata;
-
-		boolean adjacentChestZNeg = false;
-		boolean adjacentChestZPos = false;
-		boolean adjacentChestXNeg = false;
-		boolean adjacentChestXPos = false;
-		NBTTagCompound tag = getNBT(render.blockAccess, data.absPos);
-		if (tag != null) {
-			adjacentChestZNeg = tag.getBoolean("adjacentChestZNeg");
-			adjacentChestZPos = tag.getBoolean("adjacentChestZPos");
-			adjacentChestXNeg = tag.getBoolean("adjacentChestXNeg");
-			adjacentChestXPos = tag.getBoolean("adjacentChestXPos");
-		}
-		if (adjacentChestZNeg || adjacentChestXNeg) {
-			return;
-		}
-
-		ModelChest model;
-
-		if (!adjacentChestXPos && !adjacentChestZPos) {
-			model = modelChest;
-			loadTexture(manager, "/item/chest.png");
-		}
-		else {
-			model = modelLargeChest;
-			loadTexture(manager, "/item/largechest.png");
-		}
-
-		GL11.glPushMatrix();
-		{
-			GL11.glEnable(GL12.GL_RESCALE_NORMAL);
-			GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
-			GL11.glTranslatef(-0.5F, 1.0F, 0.5F);
-			GL11.glScalef(1.0F, -1.0F, -1.0F);
-			GL11.glTranslatef(0.5F, 0.5F, 0.5F);
-			short angle = 0;
-			if (metadata == 2) {
-				angle = 180;
-			}
-			if (metadata == 3) {
-				angle = 0;
-			}
-			if (metadata == 4) {
-				angle = 90;
-			}
-			if (metadata == 5) {
-				angle = -90;
-			}
-
-			if (metadata == 2 && adjacentChestXPos) {
-				GL11.glTranslatef(1.0F, 0.0F, 0.0F);
-			}
-			if (metadata == 5 && adjacentChestZPos) {
-				GL11.glTranslatef(0.0F, 0.0F, -1.0F);
-			}
-			GL11.glRotatef(angle, 0.0F, 1.0F, 0.0F);
-			GL11.glTranslatef(-0.5F, -0.5F, -0.5F);
-
-			model.chestLid.rotateAngleX = 0;
-			model.renderAll();
-			GL11.glDisable(GL12.GL_RESCALE_NORMAL);
-			GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
-		}
-		GL11.glPopMatrix();
-	}
-
-	@Override
-	protected void addMoveableBlockIds(Set<Integer> result) {
-		result.add(Block.chest.blockID);
-	}
-
-	@Override
-	protected boolean canPlaceBlockAt(World world, int x, int y, int z, int blockId, int metadata) {
-		Block block = Utils.getBlock(blockId);
-		return block == null || block.canPlaceBlockAt(world, x, y, z); //
-	}
-
-	@Override
-	protected NBTTagCompound readFromTileEntity(Materializer owner, int blockId, int metadata, Coord3D pos) {
-		NBTTagCompound tag = super.readFromTileEntity(owner, blockId, metadata, pos);
-		{
-			TileEntity tile = owner.world.getBlockTileEntity(pos.x, pos.y, pos.z);
-			if (tile instanceof TileEntityChest) {
-				TileEntityChest chest = (TileEntityChest) tile;
-				tag.setBoolean("adjacentChestZNeg", chest.adjacentChestZNeg != null);
-				tag.setBoolean("adjacentChestZPos", chest.adjacentChestZPosition != null);
-				tag.setBoolean("adjacentChestXNeg", chest.adjacentChestXNeg != null);
-				tag.setBoolean("adjacentChestXPos", chest.adjacentChestXPos != null);
-			}
-		}
-		return tag;
-	}
-
-	private static NBTTagCompound getNBT(IBlockAccess blockAccess, Coord3D pos) {
-		if (blockAccess instanceof ImitationSpace) {
-			ImitationSpace space = (ImitationSpace) blockAccess;
-			return space.getTileEntityData(pos);
-		}
-		return null;
-	}
+    private ModelChest[] models; // Use an empty array so we can try/catch if ModelChest is not present.
+
+    public ChestOperator() {
+        try {
+            ModelChest[] models = {
+                new ModelChest(),
+                new ModelLargeChest()
+            };
+        } catch (NoClassDefFoundError ex) {
+            // No class found for the chest model means this is a server
+        }
+    }
+
+    @Override
+    public boolean hasSpecialRender() {
+        return true;
+    }
+
+    @Override
+    public void renderBlock(RenderBlocks render, BlockData data) {
+        ;
+    }
+
+    @Override
+    public void renderBlockSpecial(RenderManager manager, RenderBlocks render, BlockData data) {
+        if (this.models == null) {
+            return;
+        }
+
+        Block block = data.block;
+        int metadata = data.metadata;
+
+        boolean adjacentChestZNeg = false;
+        boolean adjacentChestZPos = false;
+        boolean adjacentChestXNeg = false;
+        boolean adjacentChestXPos = false;
+        NBTTagCompound tag = getNBT(render.blockAccess, data.absPos);
+        if (tag != null) {
+            adjacentChestZNeg = tag.getBoolean("adjacentChestZNeg");
+            adjacentChestZPos = tag.getBoolean("adjacentChestZPos");
+            adjacentChestXNeg = tag.getBoolean("adjacentChestXNeg");
+            adjacentChestXPos = tag.getBoolean("adjacentChestXPos");
+        }
+        if (adjacentChestZNeg || adjacentChestXNeg) {
+            return;
+        }
+
+        ModelChest model;
+
+        if (!adjacentChestXPos && !adjacentChestZPos) {
+            model = models[0]; //ModelChest
+            loadTexture(manager, "/item/chest.png");
+        }
+        else {
+            model = models[1]; //ModelLargeChest
+            loadTexture(manager, "/item/largechest.png");
+        }
+
+        GL11.glPushMatrix();
+        {
+            GL11.glEnable(GL12.GL_RESCALE_NORMAL);
+            GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
+            GL11.glTranslatef(-0.5F, 1.0F, 0.5F);
+            GL11.glScalef(1.0F, -1.0F, -1.0F);
+            GL11.glTranslatef(0.5F, 0.5F, 0.5F);
+            short angle = 0;
+            if (metadata == 2) {
+                angle = 180;
+            }
+            if (metadata == 3) {
+                angle = 0;
+            }
+            if (metadata == 4) {
+                angle = 90;
+            }
+            if (metadata == 5) {
+                angle = -90;
+            }
+
+            if (metadata == 2 && adjacentChestXPos) {
+                GL11.glTranslatef(1.0F, 0.0F, 0.0F);
+            }
+            if (metadata == 5 && adjacentChestZPos) {
+                GL11.glTranslatef(0.0F, 0.0F, -1.0F);
+            }
+            GL11.glRotatef(angle, 0.0F, 1.0F, 0.0F);
+            GL11.glTranslatef(-0.5F, -0.5F, -0.5F);
+
+            model.chestLid.rotateAngleX = 0;
+            model.renderAll();
+            GL11.glDisable(GL12.GL_RESCALE_NORMAL);
+            GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
+        }
+        GL11.glPopMatrix();
+    }
+
+    @Override
+    protected void addMoveableBlockIds(Set<Integer> result) {
+        result.add(Block.chest.blockID);
+    }
+
+    @Override
+    protected boolean canPlaceBlockAt(World world, int x, int y, int z, int blockId, int metadata) {
+        Block block = Utils.getBlock(blockId);
+        return block == null || block.canPlaceBlockAt(world, x, y, z); //
+    }
+
+    @Override
+    protected NBTTagCompound readFromTileEntity(Materializer owner, int blockId, int metadata, Coord3D pos) {
+        NBTTagCompound tag = super.readFromTileEntity(owner, blockId, metadata, pos);
+        {
+            TileEntity tile = owner.world.getBlockTileEntity(pos.x, pos.y, pos.z);
+            if (tile instanceof TileEntityChest) {
+                TileEntityChest chest = (TileEntityChest) tile;
+                tag.setBoolean("adjacentChestZNeg", chest.adjacentChestZNeg != null);
+                tag.setBoolean("adjacentChestZPos", chest.adjacentChestZPosition != null);
+                tag.setBoolean("adjacentChestXNeg", chest.adjacentChestXNeg != null);
+                tag.setBoolean("adjacentChestXPos", chest.adjacentChestXPos != null);
+            }
+        }
+        return tag;
+    }
+
+    private static NBTTagCompound getNBT(IBlockAccess blockAccess, Coord3D pos) {
+        if (blockAccess instanceof ImitationSpace) {
+            ImitationSpace space = (ImitationSpace) blockAccess;
+            return space.getTileEntityData(pos);
+        }
+        return null;
+    }
 }

File mod/ymt/air/op/EnderChestOperator.java

  *
  */
 public class EnderChestOperator extends AbstractRotationOperator {
-	private final ModelChest modelEnderChest = new ModelChest();
+    private ModelChest[] models; // Use an empty array so we can try/catch if ModelChest is not present.
 
-	public EnderChestOperator() {
-		super(2, 5, 3, 4);
-	}
+    public EnderChestOperator() {
+        super(2, 5, 3, 4);
+        try {
+            ModelChest[] models = {
+                new ModelChest()
+            };
+        } catch (NoClassDefFoundError ex) {
+            // No class found for the chest model means this is a server
+        }
+    }
 
-	@Override
-	public boolean hasSpecialRender() {
-		return true;
-	}
+    @Override
+    public boolean hasSpecialRender() {
+        return true;
+    }
 
-	@Override
-	public void renderBlock(RenderBlocks render, BlockData data) {
-		;
-	}
+    @Override
+    public void renderBlock(RenderBlocks render, BlockData data) {
+        ;
+    }
 
-	@Override
-	public void renderBlockSpecial(RenderManager manager, RenderBlocks render, BlockData data) {
-		Block block = data.block;
-		int metadata = data.metadata;
+    @Override
+    public void renderBlockSpecial(RenderManager manager, RenderBlocks render, BlockData data) {
+        if (this.models == null) {
+            return;
+        }
 
-		final boolean adjacentChestZNeg = false;
-		final boolean adjacentChestZPos = false;
-		final boolean adjacentChestXNeg = false;
-		final boolean adjacentChestXPos = false;
-		ModelChest model;
+        Block block = data.block;
+        int metadata = data.metadata;
 
-		loadTexture(manager, "/item/enderchest.png");
+        final boolean adjacentChestZNeg = false;
+        final boolean adjacentChestZPos = false;
+        final boolean adjacentChestXNeg = false;
+        final boolean adjacentChestXPos = false;
 
-		GL11.glPushMatrix();
-		{
-			GL11.glEnable(GL12.GL_RESCALE_NORMAL);
-			GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
-			GL11.glTranslatef(-0.5F, 1.0F, 0.5F);
-			GL11.glScalef(1.0F, -1.0F, -1.0F);
-			GL11.glTranslatef(0.5F, 0.5F, 0.5F);
-			short angle = 0;
-			if (metadata == 2) {
-				angle = 180;
-			}
-			if (metadata == 3) {
-				angle = 0;
-			}
-			if (metadata == 4) {
-				angle = 90;
-			}
-			if (metadata == 5) {
-				angle = -90;
-			}
+        loadTexture(manager, "/item/enderchest.png");
 
-			GL11.glRotatef(angle, 0.0F, 1.0F, 0.0F);
-			GL11.glTranslatef(-0.5F, -0.5F, -0.5F);
+        GL11.glPushMatrix();
+        {
+            GL11.glEnable(GL12.GL_RESCALE_NORMAL);
+            GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
+            GL11.glTranslatef(-0.5F, 1.0F, 0.5F);
+            GL11.glScalef(1.0F, -1.0F, -1.0F);
+            GL11.glTranslatef(0.5F, 0.5F, 0.5F);
+            short angle = 0;
+            if (metadata == 2) {
+                angle = 180;
+            }
+            if (metadata == 3) {
+                angle = 0;
+            }
+            if (metadata == 4) {
+                angle = 90;
+            }
+            if (metadata == 5) {
+                angle = -90;
+            }
 
-			modelEnderChest.chestLid.rotateAngleX = 0;
-			modelEnderChest.renderAll();
-			GL11.glDisable(GL12.GL_RESCALE_NORMAL);
-			GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
-		}
-		GL11.glPopMatrix();
-	}
+            GL11.glRotatef(angle, 0.0F, 1.0F, 0.0F);
+            GL11.glTranslatef(-0.5F, -0.5F, -0.5F);
 
-	@Override
-	protected void addMoveableBlockIds(Set<Integer> result) {
-		result.add(Block.enderChest.blockID);
-	}
+            models[0].chestLid.rotateAngleX = 0;
+            models[0].renderAll();
+            GL11.glDisable(GL12.GL_RESCALE_NORMAL);
+            GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
+        }
+        GL11.glPopMatrix();
+    }
 
-	@Override
-	protected boolean canPlaceBlockAt(World world, int x, int y, int z, int blockId, int metadata) {
-		Block block = Utils.getBlock(blockId);
-		return block == null || block.canPlaceBlockAt(world, x, y, z);
-	}
+    @Override
+    protected void addMoveableBlockIds(Set<Integer> result) {
+        result.add(Block.enderChest.blockID);
+    }
+
+    @Override
+    protected boolean canPlaceBlockAt(World world, int x, int y, int z, int blockId, int metadata) {
+        Block block = Utils.getBlock(blockId);
+        return block == null || block.canPlaceBlockAt(world, x, y, z);
+    }
 }

File mod_HariboteAirCraft.java

 import java.util.List;
 import java.util.Map;
 import mod.ymt.air.AirCraftCore;
+import mod.ymt.cmn.Utils;
 import org.lwjgl.input.Keyboard;
 
 /**
  *
  */
 public class mod_HariboteAirCraft extends BaseMod {
-	public final KeyBinding[] keys = { // MoveManager の並びと合わせる
-		new KeyBinding("key.HAC_Stop", Keyboard.KEY_NUMPAD5),
-		new KeyBinding("key.HAC_Forward", Keyboard.KEY_NUMPAD1),
-		new KeyBinding("key.HAC_Backward", Keyboard.KEY_NUMPAD3),
-		new KeyBinding("key.HAC_TurnRight", Keyboard.KEY_NUMPAD9),
-		new KeyBinding("key.HAC_TurnLeft", Keyboard.KEY_NUMPAD7),
-		new KeyBinding("key.HAC_Up", Keyboard.KEY_NUMPAD8),
-		new KeyBinding("key.HAC_Down", Keyboard.KEY_NUMPAD2),
-		new KeyBinding("key.HAC_Right", Keyboard.KEY_NUMPAD6),
-		new KeyBinding("key.HAC_Left", Keyboard.KEY_NUMPAD4),
-	};
+    public KeyBinding[] keys;
 
-	@MLProp(min = 0)
-	public static int IdPyxis = 209;
-	@MLProp(min = 0)
-	public static int blockLimit = 2000;
-	@MLProp
-	public static int craftBodySize = -1;
-	@MLProp(min = 1)
-	public static int moveKeepTime = 60; // キープタイムデフォルト 60 秒
-	@MLProp
-	public static String blockTarget = "";
-	@MLProp
-	public static String blockAppend = "";
-	@MLProp
-	public static String blockIgnore = "2, 3, 8, 9, 10, 11, 12, 13, 31, 32, 37, 38, 78, 87, 121"; // 芝生、土、水、溶岩、砂、砂利、草、枯れ木、花、バラ、雪、ネザーラック、エンドストーン
+    @MLProp(min = 0)
+    public static int IdPyxis = 209;
+    @MLProp(min = 0)
+    public static int blockLimit = 2000;
+    @MLProp
+    public static int craftBodySize = -1;
+    @MLProp(min = 1)
+    public static int moveKeepTime = 60; // キープタイムデフォルト 60 秒
+    @MLProp
+    public static String blockTarget = "";
+    @MLProp
+    public static String blockAppend = "";
+    @MLProp
+    //public static String blockIgnore = "2, 3, 8, 9, 10, 11, 12, 13, 31, 32, 37, 38, 78, 87, 121"; // 芝生、土、水、溶岩、砂、砂利、草、枯れ木、花、バラ、雪、ネザーラック、エンドストーン
+    public static String blockIgnore = "2, 3, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 18, 21, 30, 31, 32, 37, 38, 39, 40, 56, 73, 78, 79, 81, 82, 83, 87, 99, 100, 106, 110, 111, 127, 129, 88, 97, 121 "; // Added nether, ice, leaves etc
+    @Override
+    public void addRenderer(Map map) {
+        AirCraftCore.getInstance().addRenderer(map);
+    }
 
-	@Override
-	public void addRenderer(Map map) {
-		AirCraftCore.getInstance().addRenderer(map);
-	}
+    @Override
+    public void clientCustomPayload(NetClientHandler handler, Packet250CustomPayload packet) {
+        AirCraftCore.getInstance().net.processClientCustomPayload(packet);
+    }
 
-	@Override
-	public void clientCustomPayload(NetClientHandler handler, Packet250CustomPayload packet) {
-		AirCraftCore.getInstance().net.processClientCustomPayload(packet);
-	}
+    @Override
+    public String getPriorities() {
+        return "required-after:mod_YMTLib";
+    }
 
-	@Override
-	public String getPriorities() {
-		return "required-after:mod_YMTLib";
-	}
+    @Override
+    public Packet23VehicleSpawn getSpawnPacket(Entity var1, int var2) {
+        return new Packet23VehicleSpawn(var1, var2);
+    }
 
-	@Override
-	public Packet23VehicleSpawn getSpawnPacket(Entity var1, int var2) {
-		return new Packet23VehicleSpawn(var1, var2);
-	}
+    @Override
+    public String getVersion() {
+        return "151v2 voyager"; // MP server mod is compatible with normal client
+    }
 
-	@Override
-	public String getVersion() {
-		return "151v2 voyager";
-	}
+    @Override
+    public void keyboardEvent(KeyBinding key) {
+        if (keys != null) {
+            for (int i = 0; i < keys.length; i++) {
+                if (key == keys[i]) {
+                    AirCraftCore.getInstance().net.sendKeyToServer((byte) i);
+                    break;
+                }
+            }
+        }
+    }
 
-	@Override
-	public void keyboardEvent(KeyBinding key) {
-		for (int i = 0; i < keys.length; i++) {
-			if (key == keys[i]) {
-				AirCraftCore.getInstance().net.sendKeyToServer((byte) i);
-				break;
-			}
-		}
-	}
+    @Override
+    public void load() {
+        try {
+            KeyBinding[] keys = { // MoveManager の並びと合わせる
+                new KeyBinding("key.HAC_Stop", Keyboard.KEY_NUMPAD5),
+                new KeyBinding("key.HAC_Forward", Keyboard.KEY_NUMPAD1),
+                new KeyBinding("key.HAC_Backward", Keyboard.KEY_NUMPAD3),
+                new KeyBinding("key.HAC_TurnRight", Keyboard.KEY_NUMPAD9),
+                new KeyBinding("key.HAC_TurnLeft", Keyboard.KEY_NUMPAD7),
+                new KeyBinding("key.HAC_Up", Keyboard.KEY_NUMPAD8),
+                new KeyBinding("key.HAC_Down", Keyboard.KEY_NUMPAD2),
+                new KeyBinding("key.HAC_Right", Keyboard.KEY_NUMPAD6),
+                new KeyBinding("key.HAC_Left", Keyboard.KEY_NUMPAD4),
+            };
+            this.keys = keys;
+        }
+        catch (NoClassDefFoundError ex) {
+            //ex.printStackTrace();
+        }
 
-	@Override
-	public void load() {
-		try {
-			AirCraftCore core = AirCraftCore.getInstance();
-			core.setBaseMod(this);
-			core.setBlockIdPyxis(IdPyxis);
-			core.setBlocklimit(blockLimit);
-			core.setCraftBodySize(craftBodySize);
-			core.setMoveKeepTime(moveKeepTime * 20); // 20FPS
-			core.targetBlockId.addAll(parseIdList(blockTarget));
-			core.appendixBlockId.addAll(parseIdList(blockAppend));
-			core.ignoredBlockId.addAll(parseIdList(blockIgnore));
-			core.run();
-		}
-		catch (NoClassDefFoundError ex) {
-			ex.printStackTrace();
-		}
+        try {
+            AirCraftCore core = AirCraftCore.getInstance();
+            core.setBaseMod(this);
+            core.setBlockIdPyxis(IdPyxis);
+            core.setBlocklimit(blockLimit);
+            core.setCraftBodySize(craftBodySize);
+            core.setMoveKeepTime(moveKeepTime * 20); // 20FPS
+            core.targetBlockId.addAll(parseIdList(blockTarget));
+            core.appendixBlockId.addAll(parseIdList(blockAppend));
+            core.ignoredBlockId.addAll(parseIdList(blockIgnore));
+            core.run();
+        }
+        catch (NoClassDefFoundError ex) {
+            ex.printStackTrace();
+        }
 
-		for (KeyBinding kb: keys) {
-			ModLoader.registerKey(this, kb, false);
-		}
-		ModLoader.addLocalization("key.HAC_Forward", "ja_JP", "はりぼて前進");
-		ModLoader.addLocalization("key.HAC_Backward", "ja_JP", "はりぼて後退");
-		ModLoader.addLocalization("key.HAC_TurnRight", "ja_JP", "はりぼて右旋回");
-		ModLoader.addLocalization("key.HAC_TurnLeft", "ja_JP", "はりぼて左旋回");
-		ModLoader.addLocalization("key.HAC_Up", "ja_JP", "はりぼて上昇");
-		ModLoader.addLocalization("key.HAC_Down", "ja_JP", "はりぼて下降");
-		ModLoader.addLocalization("key.HAC_Right", "ja_JP", "はりぼて右スライド");
-		ModLoader.addLocalization("key.HAC_Left", "ja_JP", "はりぼて左スライド");
-		ModLoader.addLocalization("key.HAC_Stop", "ja_JP", "はりぼて停止");
-	}
+        if (keys != null) {
+            for (KeyBinding kb: keys) {
+                    ModLoader.registerKey(this, kb, false);
+            }
+            ModLoader.addLocalization("key.HAC_Forward", "ja_JP", "はりぼて前進");
+            ModLoader.addLocalization("key.HAC_Backward", "ja_JP", "はりぼて後退");
+            ModLoader.addLocalization("key.HAC_TurnRight", "ja_JP", "はりぼて右旋回");
+            ModLoader.addLocalization("key.HAC_TurnLeft", "ja_JP", "はりぼて左旋回");
+            ModLoader.addLocalization("key.HAC_Up", "ja_JP", "はりぼて上昇");
+            ModLoader.addLocalization("key.HAC_Down", "ja_JP", "はりぼて下降");
+            ModLoader.addLocalization("key.HAC_Right", "ja_JP", "はりぼて右スライド");
+            ModLoader.addLocalization("key.HAC_Left", "ja_JP", "はりぼて左スライド");
+            ModLoader.addLocalization("key.HAC_Stop", "ja_JP", "はりぼて停止");
+        }
+    }
 
-	@Override
-	public void modsLoaded() {
-		// デバッグ表示
-		AirCraftCore core = AirCraftCore.getInstance();
-		core.debugPrint("defaultMoveableSet: %s", core.getDefaultMoveableSet());
-		core.debugPrint("targetBlockId: %s", core.targetBlockId);
-		core.debugPrint("appendixBlockId: %s", core.appendixBlockId);
-		core.debugPrint("ignoredBlockId: %s", core.ignoredBlockId);
-		core.debugPrint("MoveableBlockIds: %s", core.getMoveableBlockIds());
-	}
+    @Override
+    public void modsLoaded() {
+        // デバッグ表示
+        AirCraftCore core = AirCraftCore.getInstance();
+        core.debugPrint("defaultMoveableSet: %s", core.getDefaultMoveableSet());
+        core.debugPrint("targetBlockId: %s", core.targetBlockId);
+        core.debugPrint("appendixBlockId: %s", core.appendixBlockId);
+        core.debugPrint("ignoredBlockId: %s", core.ignoredBlockId);
+        core.debugPrint("MoveableBlockIds: %s", core.getMoveableBlockIds());
+    }
 
-	@Override
-	public void serverCustomPayload(NetServerHandler handler, Packet250CustomPayload packet) {
-		AirCraftCore.getInstance().net.processServerCustomPayload(packet);
-	}
+    @Override
+    public void serverCustomPayload(NetServerHandler handler, Packet250CustomPayload packet) {
+        AirCraftCore.getInstance().net.processServerCustomPayload(packet);
+    }
 
-	@Override
-	public Entity spawnEntity(int entId, World world, double x, double y, double z) {
-		return AirCraftCore.getInstance().spawnEntity(entId, world, x, y, z);
-	}
+    @Override
+    public Entity spawnEntity(int entId, World world, double x, double y, double z) {
+        return AirCraftCore.getInstance().spawnEntity(entId, world, x, y, z);
+    }
 
-	private static List<Integer> parseIdList(String text) {
-		List<Integer> result = new ArrayList<Integer>();
-		if (text != null) {
-			for (String value: text.split(",")) {
-				value = value.trim();
-				if (0 < value.length()) {
-					try {
-						result.add(Integer.parseInt(value));
-					}
-					catch (NumberFormatException ex) {
-						AirCraftCore.getInstance().debugPrint("IllegalNumberFormat[%s]", value);
-					}
-				}
-			}
-		}
-		return result;
-	}
+    private static List<Integer> parseIdList(String text) {
+        List<Integer> result = new ArrayList<Integer>();
+        if (text != null) {
+            for (String value: text.split(",")) {
+                value = value.trim();
+                if (0 < value.length()) {
+                    try {
+                        result.add(Integer.parseInt(value));
+                    }
+                    catch (NumberFormatException ex) {
+                        AirCraftCore.getInstance().debugPrint("IllegalNumberFormat[%s]", value);
+                    }
+                }
+            }
+        }
+        return result;
+    }
 }