提供: Minecraft Modding Wiki
移動先: 案内検索
(一時的にソースコード削除。【重要】チュートリアルコードにマルチ環境のserver側で実行できない致命的なバグ(モデル関連メソッドがserver側で呼び出される)があったので修正予定)
(ソースコード差替。クライアント側の処理にproxyを使用するように変更)
11行目: 11行目:
  
 
=== ソースコード ===
 
=== ソースコード ===
 +
「[[ComputerCraft_API/1.76/ツールタイプTurtleの追加|MC1.8 ツールタイプTurtleの追加]]」や「[[ComputerCraft_API/1.76/周辺機器の追加|MC1.8 周辺機器の追加]]」の解説を元にして、変更部分のみを解説します。<br>
 +
Packageは適宜設定してください。
 +
 +
==== SampleUpgradeCore.java ====
 +
<source lang = "java">
 +
package mods.sample.upgrade;
 +
 +
import net.minecraftforge.fml.common.Mod;
 +
import net.minecraftforge.fml.common.Mod.EventHandler;
 +
import net.minecraftforge.fml.common.SidedProxy;
 +
import net.minecraftforge.fml.common.event.FMLInitializationEvent;
 +
import dan200.computercraft.api.ComputerCraftAPI;
 +
 +
@Mod(modid=SampleUpgradeCore.MOD_ID,
 +
name=SampleUpgradeCore.MOD_NAME,
 +
version=SampleUpgradeCore.MOD_VERSION,
 +
dependencies = SampleUpgradeCore.MOD_DEPENDENCIES)
 +
public class SampleUpgradeCore {
 +
 +
public static final String MOD_ID = "sampleupgrademod";
 +
public static final String MOD_NAME = "Sample Upgrade Mod";
 +
public static final String MOD_VERSION = "1.0";
 +
public static final String MOD_DEPENDENCIES = "after:ComputerCraft";
 +
 +
@SidedProxy(clientSide = "mods.sample.upgrade.client.ClientProxy", serverSide = "mods.sample.upgrade.CommonProxy")
 +
public static CommonProxy proxy;
 +
 +
@EventHandler
 +
public void init(FMLInitializationEvent event) {
 +
proxy.registerEventHandlers();
 +
ComputerCraftAPI.registerTurtleUpgrade(new TurtleSample());
 +
}
 +
 +
}
 +
</source>
 +
 +
==== CommonProxy.java ====
 +
<source lang = "java">
 +
package mods.sample.upgrade;
 +
 +
import java.util.Set;
 +
 +
import net.minecraft.client.resources.model.ModelResourceLocation;
 +
import net.minecraft.util.ResourceLocation;
 +
 +
public class CommonProxy {
 +
 +
public void registerEventHandlers() {
 +
 +
}
 +
 +
public Set<ResourceLocation> getModelLocations() {
 +
return null;
 +
}
 +
 +
public ModelResourceLocation loadModelLocation(String domain, String path) {
 +
return null;
 +
}
 +
 +
}
 +
</source>
 +
 +
==== ClientProxy.java ====
 +
<source lang = "java">
 +
package mods.sample.upgrade.client;
 +
 +
import java.util.HashSet;
 +
import java.util.Set;
 +
 +
import mods.sample.upgrade.CommonProxy;
 +
import net.minecraft.client.resources.model.ModelResourceLocation;
 +
import net.minecraft.util.ResourceLocation;
 +
import net.minecraftforge.common.MinecraftForge;
 +
import net.minecraftforge.fml.relauncher.Side;
 +
import net.minecraftforge.fml.relauncher.SideOnly;
 +
 +
@SideOnly(Side.CLIENT)
 +
public class ClientProxy extends CommonProxy {
 +
 +
private Set<ResourceLocation> modelLocations = new HashSet<ResourceLocation>();
 +
 +
@Override
 +
public void registerEventHandlers() {
 +
MinecraftForge.EVENT_BUS.register(new ModelBakeEventHandler());
 +
}
 +
 +
@Override
 +
public Set<ResourceLocation> getModelLocations() {
 +
return modelLocations;
 +
}
 +
 +
@Override
 +
public ModelResourceLocation loadModelLocation(String domain, String path) {
 +
ResourceLocation location = new ResourceLocation(domain, path);
 +
modelLocations.add(location);
 +
return new ModelResourceLocation(location, "inventory");
 +
}
 +
 +
}
 +
</source>
 +
 +
==== TurtleSample.java ====
 +
<source lang = "java">
 +
package mods.sample.upgrade;
 +
 +
import javax.vecmath.Matrix4f;
 +
 +
import net.minecraft.client.Minecraft;
 +
import net.minecraft.client.resources.model.IBakedModel;
 +
import net.minecraft.client.resources.model.ModelManager;
 +
import net.minecraft.client.resources.model.ModelResourceLocation;
 +
import net.minecraft.init.Blocks;
 +
import net.minecraft.item.ItemStack;
 +
import net.minecraft.util.EnumFacing;
 +
import net.minecraft.util.ResourceLocation;
 +
import net.minecraftforge.fml.relauncher.Side;
 +
import net.minecraftforge.fml.relauncher.SideOnly;
 +
 +
import org.apache.commons.lang3.tuple.Pair;
 +
 +
import dan200.computercraft.api.peripheral.IPeripheral;
 +
import dan200.computercraft.api.turtle.ITurtleAccess;
 +
import dan200.computercraft.api.turtle.ITurtleUpgrade;
 +
import dan200.computercraft.api.turtle.TurtleCommandResult;
 +
import dan200.computercraft.api.turtle.TurtleSide;
 +
import dan200.computercraft.api.turtle.TurtleUpgradeType;
 +
import dan200.computercraft.api.turtle.TurtleVerb;
 +
 +
public class TurtleSample implements ITurtleUpgrade {
 +
 +
private ResourceLocation upgradeID = new ResourceLocation(SampleUpgradeCore.MOD_ID, "samplep");
 +
private ItemStack upgradeItem = new ItemStack(Blocks.stone);
 +
 +
private ModelResourceLocation modelLeft;
 +
private ModelResourceLocation modelRight;
 +
 +
public TurtleSample() {
 +
String modid = SampleUpgradeCore.MOD_ID;
 +
modelLeft = SampleUpgradeCore.proxy.loadModelLocation(modid, "block/turtle_sample_left");
 +
modelRight = SampleUpgradeCore.proxy.loadModelLocation(modid, "block/turtle_sample_right");
 +
}
 +
 +
@Override
 +
public ResourceLocation getUpgradeID() {
 +
return upgradeID;
 +
}
 +
 +
@Override
 +
public int getLegacyUpgradeID() {
 +
return -1;
 +
}
 +
 +
@Override
 +
public String getUnlocalisedAdjective() {
 +
return "Sample";
 +
}
 +
 +
@Override
 +
public TurtleUpgradeType getType() {
 +
return TurtleUpgradeType.Peripheral;
 +
}
 +
 +
@Override
 +
public ItemStack getCraftingItem() {
 +
return upgradeItem;
 +
}
 +
 +
@Override
 +
public IPeripheral createPeripheral(ITurtleAccess turtle, TurtleSide side) {
 +
return new SamplePeripheral(turtle, side);
 +
}
 +
 +
@Override
 +
public TurtleCommandResult useTool(ITurtleAccess turtle, TurtleSide side,
 +
TurtleVerb verb, EnumFacing direction) {
 +
return null;
 +
}
 +
 +
@Override
 +
@SideOnly(Side.CLIENT)
 +
public Pair<IBakedModel, Matrix4f> getModel(ITurtleAccess turtle, TurtleSide side) {
 +
Minecraft mc = Minecraft.getMinecraft();
 +
ModelManager modelManager = mc.getRenderItem().getItemModelMesher().getModelManager();
 +
 +
if (side == TurtleSide.Left) {
 +
return Pair.of(modelManager.getModel(modelLeft), null);
 +
} else {
 +
return Pair.of(modelManager.getModel(modelRight), null);
 +
}
 +
}
 +
 +
@Override
 +
public void update(ITurtleAccess turtle, TurtleSide side) {
 +
 +
}
 +
 +
}
 +
</source>
 +
 +
==== SamplePeripheral.java ====
 +
<source lang = "java">
 +
package mods.sample.upgrade;
 +
 +
import dan200.computercraft.api.lua.ILuaContext;
 +
import dan200.computercraft.api.lua.LuaException;
 +
import dan200.computercraft.api.peripheral.IComputerAccess;
 +
import dan200.computercraft.api.peripheral.IPeripheral;
 +
import dan200.computercraft.api.turtle.ITurtleAccess;
 +
import dan200.computercraft.api.turtle.TurtleSide;
 +
 +
public class SamplePeripheral implements IPeripheral {
 +
 +
private final ITurtleAccess turtleAccess;
 +
private final TurtleSide turtleSide;
 +
 +
public SamplePeripheral(ITurtleAccess turtle, TurtleSide side) {
 +
turtleAccess = turtle;
 +
turtleSide = side;
 +
}
 +
 +
@Override
 +
public String getType() {
 +
return "sample";
 +
}
 +
 +
@Override
 +
public String[] getMethodNames() {
 +
return new String[] {};
 +
}
 +
 +
@Override
 +
public Object[] callMethod(IComputerAccess computer, ILuaContext context,
 +
int method, Object[] arguments) throws LuaException,
 +
InterruptedException {
 +
switch(method) {
 +
 +
}
 +
 +
return null;
 +
}
 +
 +
@Override
 +
public void attach(IComputerAccess computer) {
 +
 +
}
 +
 +
@Override
 +
public void detach(IComputerAccess computer) {
 +
 +
}
 +
 +
@Override
 +
public boolean equals(IPeripheral other) {
 +
if ((other != null) && (other instanceof SamplePeripheral)) {
 +
return other == this;
 +
}
 +
 +
return false;
 +
}
 +
 +
}
 +
</source>
 +
 +
==== ModelBakeEventHandler.java ====
 +
<source lang = "java">
 +
package mods.sample.upgrade.client;
 +
 +
import java.util.Set;
 +
 +
import mods.sample.upgrade.SampleUpgradeCore;
 +
import net.minecraft.client.Minecraft;
 +
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
 +
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
 +
import net.minecraft.client.resources.model.IBakedModel;
 +
import net.minecraft.client.resources.model.ModelResourceLocation;
 +
import net.minecraft.util.ResourceLocation;
 +
import net.minecraftforge.client.event.ModelBakeEvent;
 +
import net.minecraftforge.client.model.IModel;
 +
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
 +
 +
import com.google.common.base.Function;
 +
 +
public class ModelBakeEventHandler {
 +
 +
@SubscribeEvent
 +
public void onModelBakeEvent(ModelBakeEvent event) {
 +
bakeModels(event);
 +
}
 +
 +
private void bakeModels(ModelBakeEvent event) {
 +
Set<ResourceLocation> modelLocations = SampleUpgradeCore.proxy.getModelLocations();
 +
 +
if (modelLocations == null) {
 +
return;
 +
}
 +
 +
for (ResourceLocation location : modelLocations) {
 +
try {
 +
IModel model = event.modelLoader.getModel(location);
 +
IBakedModel bakedModel = model.bake(model.getDefaultState(),
 +
DefaultVertexFormats.ITEM,
 +
new Function<ResourceLocation, TextureAtlasSprite>() {
 +
 +
@Override
 +
public TextureAtlasSprite apply(ResourceLocation location) {
 +
Minecraft mc = Minecraft.getMinecraft();
 +
return mc.getTextureMapBlocks().getAtlasSprite(location.toString());
 +
}
 +
 +
});
 +
ModelResourceLocation modelLocation = new ModelResourceLocation(location, "inventory");
 +
event.modelRegistry.putObject(modelLocation, bakedModel);
 +
} catch (Exception e) {
 +
e.printStackTrace();
 +
}
 +
}
 +
}
 +
 +
}
 +
</source>
 +
 +
==== turtle_sample_left.json (models/block) ====
 +
assets\sampleupgrademod\models\block ディレクトリに設置します。
 +
<source lang = "javascript">
 +
{
 +
"parent": "computercraft:block/turtle_upgrade_base_left",
 +
"textures": {
 +
"texture": "blocks/stone"
 +
}
 +
}
 +
</source>
 +
 +
==== turtle_sample_right.json (models/block) ====
 +
assets\sampleupgrademod\models\block ディレクトリに設置します。
 +
<source lang = "javascript">
 +
{
 +
"parent": "computercraft:block/turtle_upgrade_base_right",
 +
"textures": {
 +
"texture": "blocks/stone"
 +
}
 +
}
 +
</source>
 +
 +
 +
=== 解説 ===
 +
==== SampleUpgradeCore.java ====
 +
Modのコアとなるクラス
 +
 +
*プロキシクラスの指定
 +
<source lang = "java">
 +
@SidedProxy(clientSide = "mods.sample.upgrade.client.ClientProxy", serverSide = "mods.sample.upgrade.CommonProxy")
 +
public static CommonProxy proxy;
 +
</source>
 +
Turtle Upgradeのモデルに関する処理はクライアント側でのみ行うため、今回の例では[[プロキシシステムについて|プロキシシステム]]を利用しています。
 +
 +
*ModelBakeEvent受信クラスの登録
 +
<source lang = "java">
 +
proxy.registerEventHandlers();
 +
</source>
 +
プロキシを介して登録用メソッドを呼び出し、クライアント側でのみ登録されるようにしています。
 +
 +
==== CommonProxy.java ====
 +
サーバー側でのみ呼び出されるプロキシクラス
 +
 +
クライアント側のプロキシクラスでオーバーライドするためのメソッドを実装します。<br>
 +
クライアント側での処理がメインとなるため、こちらでは何もしません。
 +
 +
==== ClientProxy.java ====
 +
クライアント側でのみ呼び出されるプロキシクラス
 +
 +
*ModelBakeEvent受信クラスの登録
 +
<source lang = "java">
 +
@Override
 +
public void registerEventHandlers() {
 +
MinecraftForge.EVENT_BUS.register(new ModelBakeEventHandler());
 +
}
 +
</source>
 +
ModelBakeEvent受信クラス(ModelBakeEventHandler)のインスタンスをForgeに登録します。
 +
 +
*モデル関連のフィールドやメソッド
 +
<source lang = "java">
 +
private Set<ResourceLocation> modelLocations = new HashSet<ResourceLocation>();
 +
 +
@Override
 +
public Set<ResourceLocation> getModelLocations() {
 +
return modelLocations;
 +
}
 +
 +
@Override
 +
public ModelResourceLocation loadModelLocation(String domain, String path) {
 +
ResourceLocation location = new ResourceLocation(domain, path);
 +
modelLocations.add(location);
 +
return new ModelResourceLocation(location, "inventory");
 +
}
 +
</source>
 +
今回の例ではTurtle Upgradeの各モデルをModelBakeEventでまとめてbakeするために、モデルのJSONファイルの位置を登録するためのリストとメソッドを用意しています。<br>
 +
モデルの位置は各ITurtleUpgrade実装クラスのコンストラクタでloadModelLocationメソッドを呼び出して登録します。<br>
 +
 +
==== TurtleSample.java ====
 +
Turtle Upgradeの機能を定義するクラス
 +
 +
*TurtleUpgradeのモデル登録
 +
<source lang = "java">
 +
private ModelResourceLocation modelLeft;
 +
private ModelResourceLocation modelRight;
 +
 +
public TurtleSample() {
 +
String modid = SampleUpgradeCore.MOD_ID;
 +
modelLeft = SampleUpgradeCore.proxy.loadModelLocation(modid, "block/turtle_sample_left");
 +
modelRight = SampleUpgradeCore.proxy.loadModelLocation(modid, "block/turtle_sample_right");
 +
}
 +
</source>
 +
プロキシクラスで用意したloadModelLocationメソッドをコンストラクタで呼び出して(クライアント側でのみ)Turtle UpgradeのモデルのJSONファイルの位置を登録します。<br>
 +
戻り値はgetModel()で使うためメンバフィールドに保存します。
 +
 +
*getType()
 +
<source lang = "java">
 +
@Override
 +
public TurtleUpgradeType getType() {
 +
return TurtleUpgradeType.Peripheral;
 +
}
 +
</source>
 +
Turtle Upgradeの種類をTurtleUpgradeTypeの値で指定します。
 +
 +
今回は周辺機器タイプなので TurtleUpgradeType.Peripheral を返しています。
 +
 +
*getCraftingItem()
 +
<source lang = "java">
 +
private ItemStack upgradeItem = new ItemStack(Blocks.stone);
 +
 +
@Override
 +
public ItemStack getCraftingItem() {
 +
return upgradeItem;
 +
}
 +
</source>
 +
Turtle Upgradeを装着するためのアイテムをItemStackで指定します。
 +
 +
今回はバニラブロックの石(焼石)を指定していますが、modで追加した独自アイテムも同様に指定できます。
 +
 +
*createPeripheral()
 +
<source lang = "java">
 +
@Override
 +
public IPeripheral createPeripheral(ITurtleAccess turtle, TurtleSide side) {
 +
return new SamplePeripheral(turtle, side);
 +
}
 +
</source>
 +
周辺機器タイプのTurtle Upgradeの場合、周辺機器の動作を定義したIPeripheral実装クラスのインスタンスを返します。
 +
 +
IPeripheral実装クラスにはコンストラクタの引数でTurtle Upgradeの情報を渡しています。
 +
 +
*useTool()
 +
<source lang = "java">
 +
@Override
 +
public TurtleCommandResult useTool(ITurtleAccess turtle, TurtleSide side,
 +
TurtleVerb verb, EnumFacing direction) {
 +
return null;
 +
}
 +
</source>
 +
周辺機器タイプのTurtle Upgradeでは呼び出されることは無いため、単にnullを返しています。
 +
 +
*getModel()
 +
<source lang = "java">
 +
@Override
 +
@SideOnly(Side.CLIENT)
 +
public Pair<IBakedModel, Matrix4f> getModel(ITurtleAccess turtle, TurtleSide side) {
 +
Minecraft mc = Minecraft.getMinecraft();
 +
ModelManager modelManager = mc.getRenderItem().getItemModelMesher().getModelManager();
 +
 +
if (side == TurtleSide.Left) {
 +
return Pair.of(modelManager.getModel(modelLeft), null);
 +
} else {
 +
return Pair.of(modelManager.getModel(modelRight), null);
 +
}
 +
}
 +
</source>
 +
Turtleに装着されたTurtle Upgradeの外観を指定します。<br>
 +
戻り値はPair<IBakedModel, Matrix4f>で、IBakedModelがモデル、Matrix4fがモデルを変形する座標変換行列です。
 +
 +
今回はCC1.76の実装を参考にしています。<br>
 +
コンストラクタで登録してModelBakeEventでbakeされたモデルをModelManager.getModel()で取得して返しています。<br>
 +
Turtle Upgradeを装着した位置がTurtleの左側ならば左用のモデルを、右側ならば右用のモデルを描画するようにしています。
 +
 +
*update()
 +
<source lang = "java">
 +
@Override
 +
public void update(ITurtleAccess turtle, TurtleSide side) {
 +
 +
}
 +
</source>
 +
Turtle UpgradeがTurtleに装着されている間、毎tick呼び出されます。<br>
 +
ロードされているワールド上で装着されている数だけ呼び出されますが、各パラメータでどれに対する呼び出しなのかが判別が可能です。<br>
 +
また、サーバ側とクライアント側でそれぞれ別に呼び出されますが、どちら側の呼び出しなのかはturtle.getWorld().isRemoteの値で判別可能です。<br>
 +
ITurtleAccess.getPeripheral()で装着されている周辺機器Upgradeの周辺機器クラスのインスタンスを取得できます。<br>
 +
turtle.getUpgradeNBTData()で読み書き可能なNBTTagCompoundを取得できます。このNBTTagCompoundはTurtleに記録され、ゲームを終了しても消去されません(Turtle Upgradeを取り外したりTurtleが破壊されると消えます)。
 +
 +
今回は何もしていません。
 +
 +
==== SamplePeripheral.java ====
 +
周辺機器を定義するクラス
 +
 +
*装着されたTurtleの情報取得・保存
 +
<source lang = "java">
 +
private final ITurtleAccess turtleAccess;
 +
private final TurtleSide turtleSide;
 +
 +
public SamplePeripheral(ITurtleAccess turtle, TurtleSide side) {
 +
turtleAccess = turtle;
 +
turtleSide = side;
 +
}
 +
</source>
 +
周辺機器でTurtle Upgradeの情報を利用したい場合、コンストラクタで受け取って周辺機器側で保存しておきます。<br>
 +
 +
==== ModelBakeEventHandler.java ====
 +
ModelBakeEventを受信するためのクラス
 +
 +
*ModelBakeEvent
 +
<source lang = "java">
 +
@SubscribeEvent
 +
public void onModelBakeEvent(ModelBakeEvent event) {
 +
bakeModels(event);
 +
}
 +
 +
*モデルのbake
 +
private void bakeModels(ModelBakeEvent event) {
 +
Set<ResourceLocation> modelLocations = SampleUpgradeCore.proxy.getModelLocations();
 +
 +
if (modelLocations == null) {
 +
return;
 +
}
 +
 +
for (ResourceLocation location : modelLocations) {
 +
try {
 +
IModel model = event.modelLoader.getModel(location);
 +
IBakedModel bakedModel = model.bake(model.getDefaultState(),
 +
DefaultVertexFormats.ITEM,
 +
new Function<ResourceLocation, TextureAtlasSprite>() {
 +
 +
@Override
 +
public TextureAtlasSprite apply(ResourceLocation location) {
 +
Minecraft mc = Minecraft.getMinecraft();
 +
return mc.getTextureMapBlocks().getAtlasSprite(location.toString());
 +
}
 +
 +
});
 +
ModelResourceLocation modelLocation = new ModelResourceLocation(location, "inventory");
 +
event.modelRegistry.putObject(modelLocation, bakedModel);
 +
} catch (Exception e) {
 +
e.printStackTrace();
 +
}
 +
}
 +
}
 +
</source>
 +
ClientProxy.modelLocationsに登録されたモデルをすべてbakeし、modelRegistryに登録します。<br>
 +
これにより、ITurtleUpgrade実装クラスのgetModel()で指定したモデルが描画されるようになります。
 +
 +
==== turtle_sample_left.json (models/block) ====
 +
Turtleの左側に装着された時のTurtle Upgradeのモデルを指定するJSONファイルです。<br>
 +
 +
今回の例ではJSONファイルの位置を "block/turtle_sample_left" と指定したため(TurtleSampleのコンストラクタ)、assets\<MOD_ID>\models\block ディレクトリに設置します。
 +
 +
<source lang = "javascript">
 +
"parent": "computercraft:block/turtle_upgrade_base_left",
 +
"textures": {
 +
"texture": "blocks/stone"
 +
}
 +
</source>
 +
モデル自体はComputerCraft本体で定義されている物を流用しており、テクスチャのみ独自に設定(今回はバニラの石ブロック)しています。
 +
 +
==== turtle_sample_right.json (models/block) ====
 +
Turtleの右側に装着された時のTurtle Upgradeのモデルを指定するJSONファイルです。<br>
 +
解説はturtle_sample_left.jsonの項目を参照してください。

2016年1月10日 (日) 17:26時点における版

ComputerCraft API >

この記事は"Minecraft Forge Universal 10.14.4.x~"及び"ComputerCraft 1.76~"を前提MODとしています。

周辺機器タイプTurtleの追加

周辺機器タイプのTurtle Upgradeを追加します。

  • ITurtleUpgrade(周辺機器タイプ)の実装
  • TurtleUpgradeの登録
  • IPeripheralの実装
  • Turtle Upgradeの外観の実装

ソースコード

MC1.8 ツールタイプTurtleの追加」や「MC1.8 周辺機器の追加」の解説を元にして、変更部分のみを解説します。
Packageは適宜設定してください。

SampleUpgradeCore.java

package mods.sample.upgrade;

import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.Mod.EventHandler;
import net.minecraftforge.fml.common.SidedProxy;
import net.minecraftforge.fml.common.event.FMLInitializationEvent;
import dan200.computercraft.api.ComputerCraftAPI;

@Mod(modid=SampleUpgradeCore.MOD_ID,
	name=SampleUpgradeCore.MOD_NAME,
	version=SampleUpgradeCore.MOD_VERSION,
	dependencies = SampleUpgradeCore.MOD_DEPENDENCIES)
public class SampleUpgradeCore {

	public static final String MOD_ID = "sampleupgrademod";
	public static final String MOD_NAME = "Sample Upgrade Mod";
	public static final String MOD_VERSION = "1.0";
	public static final String MOD_DEPENDENCIES = "after:ComputerCraft";

	@SidedProxy(clientSide = "mods.sample.upgrade.client.ClientProxy", serverSide = "mods.sample.upgrade.CommonProxy")
	public static CommonProxy proxy;

	@EventHandler
	public void init(FMLInitializationEvent event) {
		proxy.registerEventHandlers();
		ComputerCraftAPI.registerTurtleUpgrade(new TurtleSample());
	}

}

CommonProxy.java

package mods.sample.upgrade;

import java.util.Set;

import net.minecraft.client.resources.model.ModelResourceLocation;
import net.minecraft.util.ResourceLocation;

public class CommonProxy {

	public void registerEventHandlers() {

	}

	public Set<ResourceLocation> getModelLocations() {
		return null;
	}

	public ModelResourceLocation loadModelLocation(String domain, String path) {
		return null;
	}

}

ClientProxy.java

package mods.sample.upgrade.client;

import java.util.HashSet;
import java.util.Set;

import mods.sample.upgrade.CommonProxy;
import net.minecraft.client.resources.model.ModelResourceLocation;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;

@SideOnly(Side.CLIENT)
public class ClientProxy extends CommonProxy {

	private Set<ResourceLocation> modelLocations = new HashSet<ResourceLocation>();

	@Override
	public void registerEventHandlers() {
		MinecraftForge.EVENT_BUS.register(new ModelBakeEventHandler());
	}

	@Override
	public Set<ResourceLocation> getModelLocations() {
		return modelLocations;
	}

	@Override
	public ModelResourceLocation loadModelLocation(String domain, String path) {
		ResourceLocation location = new ResourceLocation(domain, path);
		modelLocations.add(location);
		return new ModelResourceLocation(location, "inventory");
	}

}

TurtleSample.java

package mods.sample.upgrade;

import javax.vecmath.Matrix4f;

import net.minecraft.client.Minecraft;
import net.minecraft.client.resources.model.IBakedModel;
import net.minecraft.client.resources.model.ModelManager;
import net.minecraft.client.resources.model.ModelResourceLocation;
import net.minecraft.init.Blocks;
import net.minecraft.item.ItemStack;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;

import org.apache.commons.lang3.tuple.Pair;

import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.api.turtle.ITurtleAccess;
import dan200.computercraft.api.turtle.ITurtleUpgrade;
import dan200.computercraft.api.turtle.TurtleCommandResult;
import dan200.computercraft.api.turtle.TurtleSide;
import dan200.computercraft.api.turtle.TurtleUpgradeType;
import dan200.computercraft.api.turtle.TurtleVerb;

public class TurtleSample implements ITurtleUpgrade {

	private ResourceLocation upgradeID = new ResourceLocation(SampleUpgradeCore.MOD_ID, "samplep");
	private ItemStack upgradeItem = new ItemStack(Blocks.stone);

	private ModelResourceLocation modelLeft;
	private ModelResourceLocation modelRight;

	public TurtleSample() {
		String modid = SampleUpgradeCore.MOD_ID;
		modelLeft = SampleUpgradeCore.proxy.loadModelLocation(modid, "block/turtle_sample_left");
		modelRight = SampleUpgradeCore.proxy.loadModelLocation(modid, "block/turtle_sample_right");
	}

	@Override
	public ResourceLocation getUpgradeID() {
		return upgradeID;
	}

	@Override
	public int getLegacyUpgradeID() {
		return -1;
	}

	@Override
	public String getUnlocalisedAdjective() {
		return "Sample";
	}

	@Override
	public TurtleUpgradeType getType() {
		return TurtleUpgradeType.Peripheral;
	}

	@Override
	public ItemStack getCraftingItem() {
		return upgradeItem;
	}

	@Override
	public IPeripheral createPeripheral(ITurtleAccess turtle, TurtleSide side) {
		return new SamplePeripheral(turtle, side);
	}

	@Override
	public TurtleCommandResult useTool(ITurtleAccess turtle, TurtleSide side,
			TurtleVerb verb, EnumFacing direction) {
		return null;
	}

	@Override
	@SideOnly(Side.CLIENT)
	public Pair<IBakedModel, Matrix4f> getModel(ITurtleAccess turtle, TurtleSide side) {
		Minecraft mc = Minecraft.getMinecraft();
		ModelManager modelManager = mc.getRenderItem().getItemModelMesher().getModelManager();

		if (side == TurtleSide.Left) {
			return Pair.of(modelManager.getModel(modelLeft), null);
		} else {
			return Pair.of(modelManager.getModel(modelRight), null);
		}
	}

	@Override
	public void update(ITurtleAccess turtle, TurtleSide side) {

	}

}

SamplePeripheral.java

package mods.sample.upgrade;

import dan200.computercraft.api.lua.ILuaContext;
import dan200.computercraft.api.lua.LuaException;
import dan200.computercraft.api.peripheral.IComputerAccess;
import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.api.turtle.ITurtleAccess;
import dan200.computercraft.api.turtle.TurtleSide;

public class SamplePeripheral implements IPeripheral {

	private final ITurtleAccess turtleAccess;
	private final TurtleSide turtleSide;

	public SamplePeripheral(ITurtleAccess turtle, TurtleSide side) {
		turtleAccess = turtle;
		turtleSide = side;
	}

	@Override
	public String getType() {
		return "sample";
	}

	@Override
	public String[] getMethodNames() {
		return new String[] {};
	}

	@Override
	public Object[] callMethod(IComputerAccess computer, ILuaContext context,
			int method, Object[] arguments) throws LuaException,
			InterruptedException {
		switch(method) {

		}

		return null;
	}

	@Override
	public void attach(IComputerAccess computer) {

	}

	@Override
	public void detach(IComputerAccess computer) {

	}

	@Override
	public boolean equals(IPeripheral other) {
		if ((other != null) && (other instanceof SamplePeripheral)) {
			return other == this;
		}

		return false;
	}

}

ModelBakeEventHandler.java

package mods.sample.upgrade.client;

import java.util.Set;

import mods.sample.upgrade.SampleUpgradeCore;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
import net.minecraft.client.resources.model.IBakedModel;
import net.minecraft.client.resources.model.ModelResourceLocation;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.client.event.ModelBakeEvent;
import net.minecraftforge.client.model.IModel;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;

import com.google.common.base.Function;

public class ModelBakeEventHandler {

	@SubscribeEvent
	public void onModelBakeEvent(ModelBakeEvent event) {
		bakeModels(event);
	}

	private void bakeModels(ModelBakeEvent event) {
		Set<ResourceLocation> modelLocations = SampleUpgradeCore.proxy.getModelLocations();

		if (modelLocations == null) {
			return;
		}

		for (ResourceLocation location : modelLocations) {
			try {
				IModel model = event.modelLoader.getModel(location);
				IBakedModel bakedModel = model.bake(model.getDefaultState(),
						DefaultVertexFormats.ITEM,
						new Function<ResourceLocation, TextureAtlasSprite>() {

							@Override
							public TextureAtlasSprite apply(ResourceLocation location) {
								Minecraft mc = Minecraft.getMinecraft();
								return mc.getTextureMapBlocks().getAtlasSprite(location.toString());
							}

						});
				ModelResourceLocation modelLocation = new ModelResourceLocation(location, "inventory");
				event.modelRegistry.putObject(modelLocation, bakedModel);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

}

turtle_sample_left.json (models/block)

assets\sampleupgrademod\models\block ディレクトリに設置します。

{
	"parent": "computercraft:block/turtle_upgrade_base_left",
	"textures": {
		"texture": "blocks/stone"
	}
}

turtle_sample_right.json (models/block)

assets\sampleupgrademod\models\block ディレクトリに設置します。

{
	"parent": "computercraft:block/turtle_upgrade_base_right",
	"textures": {
		"texture": "blocks/stone"
	}
}


解説

SampleUpgradeCore.java

Modのコアとなるクラス

  • プロキシクラスの指定
@SidedProxy(clientSide = "mods.sample.upgrade.client.ClientProxy", serverSide = "mods.sample.upgrade.CommonProxy")
public static CommonProxy proxy;

Turtle Upgradeのモデルに関する処理はクライアント側でのみ行うため、今回の例ではプロキシシステムを利用しています。

  • ModelBakeEvent受信クラスの登録
proxy.registerEventHandlers();

プロキシを介して登録用メソッドを呼び出し、クライアント側でのみ登録されるようにしています。

CommonProxy.java

サーバー側でのみ呼び出されるプロキシクラス

クライアント側のプロキシクラスでオーバーライドするためのメソッドを実装します。
クライアント側での処理がメインとなるため、こちらでは何もしません。

ClientProxy.java

クライアント側でのみ呼び出されるプロキシクラス

  • ModelBakeEvent受信クラスの登録
@Override
public void registerEventHandlers() {
	MinecraftForge.EVENT_BUS.register(new ModelBakeEventHandler());
}

ModelBakeEvent受信クラス(ModelBakeEventHandler)のインスタンスをForgeに登録します。

  • モデル関連のフィールドやメソッド
private Set<ResourceLocation> modelLocations = new HashSet<ResourceLocation>();

@Override
public Set<ResourceLocation> getModelLocations() {
	return modelLocations;
}

@Override
public ModelResourceLocation loadModelLocation(String domain, String path) {
	ResourceLocation location = new ResourceLocation(domain, path);
	modelLocations.add(location);
	return new ModelResourceLocation(location, "inventory");
}

今回の例ではTurtle Upgradeの各モデルをModelBakeEventでまとめてbakeするために、モデルのJSONファイルの位置を登録するためのリストとメソッドを用意しています。
モデルの位置は各ITurtleUpgrade実装クラスのコンストラクタでloadModelLocationメソッドを呼び出して登録します。

TurtleSample.java

Turtle Upgradeの機能を定義するクラス

  • TurtleUpgradeのモデル登録
private ModelResourceLocation modelLeft;
private ModelResourceLocation modelRight;

public TurtleSample() {
	String modid = SampleUpgradeCore.MOD_ID;
	modelLeft = SampleUpgradeCore.proxy.loadModelLocation(modid, "block/turtle_sample_left");
	modelRight = SampleUpgradeCore.proxy.loadModelLocation(modid, "block/turtle_sample_right");
}

プロキシクラスで用意したloadModelLocationメソッドをコンストラクタで呼び出して(クライアント側でのみ)Turtle UpgradeのモデルのJSONファイルの位置を登録します。
戻り値はgetModel()で使うためメンバフィールドに保存します。

  • getType()
@Override
public TurtleUpgradeType getType() {
	return TurtleUpgradeType.Peripheral;
}

Turtle Upgradeの種類をTurtleUpgradeTypeの値で指定します。

今回は周辺機器タイプなので TurtleUpgradeType.Peripheral を返しています。

  • getCraftingItem()
private ItemStack upgradeItem = new ItemStack(Blocks.stone);

@Override
public ItemStack getCraftingItem() {
	return upgradeItem;
}

Turtle Upgradeを装着するためのアイテムをItemStackで指定します。

今回はバニラブロックの石(焼石)を指定していますが、modで追加した独自アイテムも同様に指定できます。

  • createPeripheral()
@Override
public IPeripheral createPeripheral(ITurtleAccess turtle, TurtleSide side) {
	return new SamplePeripheral(turtle, side);
}

周辺機器タイプのTurtle Upgradeの場合、周辺機器の動作を定義したIPeripheral実装クラスのインスタンスを返します。

IPeripheral実装クラスにはコンストラクタの引数でTurtle Upgradeの情報を渡しています。

  • useTool()
@Override
public TurtleCommandResult useTool(ITurtleAccess turtle, TurtleSide side,
		TurtleVerb verb, EnumFacing direction) {
	return null;
}

周辺機器タイプのTurtle Upgradeでは呼び出されることは無いため、単にnullを返しています。

  • getModel()
@Override
@SideOnly(Side.CLIENT)
public Pair<IBakedModel, Matrix4f> getModel(ITurtleAccess turtle, TurtleSide side) {
	Minecraft mc = Minecraft.getMinecraft();
	ModelManager modelManager = mc.getRenderItem().getItemModelMesher().getModelManager();

	if (side == TurtleSide.Left) {
		return Pair.of(modelManager.getModel(modelLeft), null);
	} else {
		return Pair.of(modelManager.getModel(modelRight), null);
	}
}

Turtleに装着されたTurtle Upgradeの外観を指定します。
戻り値はPair<IBakedModel, Matrix4f>で、IBakedModelがモデル、Matrix4fがモデルを変形する座標変換行列です。

今回はCC1.76の実装を参考にしています。
コンストラクタで登録してModelBakeEventでbakeされたモデルをModelManager.getModel()で取得して返しています。
Turtle Upgradeを装着した位置がTurtleの左側ならば左用のモデルを、右側ならば右用のモデルを描画するようにしています。

  • update()
@Override
public void update(ITurtleAccess turtle, TurtleSide side) {
 
}

Turtle UpgradeがTurtleに装着されている間、毎tick呼び出されます。
ロードされているワールド上で装着されている数だけ呼び出されますが、各パラメータでどれに対する呼び出しなのかが判別が可能です。
また、サーバ側とクライアント側でそれぞれ別に呼び出されますが、どちら側の呼び出しなのかはturtle.getWorld().isRemoteの値で判別可能です。
ITurtleAccess.getPeripheral()で装着されている周辺機器Upgradeの周辺機器クラスのインスタンスを取得できます。
turtle.getUpgradeNBTData()で読み書き可能なNBTTagCompoundを取得できます。このNBTTagCompoundはTurtleに記録され、ゲームを終了しても消去されません(Turtle Upgradeを取り外したりTurtleが破壊されると消えます)。

今回は何もしていません。

SamplePeripheral.java

周辺機器を定義するクラス

  • 装着されたTurtleの情報取得・保存
private final ITurtleAccess turtleAccess;
private final TurtleSide turtleSide;

public SamplePeripheral(ITurtleAccess turtle, TurtleSide side) {
	turtleAccess = turtle;
	turtleSide = side;
}

周辺機器でTurtle Upgradeの情報を利用したい場合、コンストラクタで受け取って周辺機器側で保存しておきます。

ModelBakeEventHandler.java

ModelBakeEventを受信するためのクラス

  • ModelBakeEvent
@SubscribeEvent
public void onModelBakeEvent(ModelBakeEvent event) {
	bakeModels(event);
}

*モデルのbake
private void bakeModels(ModelBakeEvent event) {
	Set<ResourceLocation> modelLocations = SampleUpgradeCore.proxy.getModelLocations();

	if (modelLocations == null) {
		return;
	}

	for (ResourceLocation location : modelLocations) {
		try {
			IModel model = event.modelLoader.getModel(location);
			IBakedModel bakedModel = model.bake(model.getDefaultState(),
					DefaultVertexFormats.ITEM,
					new Function<ResourceLocation, TextureAtlasSprite>() {

						@Override
						public TextureAtlasSprite apply(ResourceLocation location) {
							Minecraft mc = Minecraft.getMinecraft();
							return mc.getTextureMapBlocks().getAtlasSprite(location.toString());
						}

					});
			ModelResourceLocation modelLocation = new ModelResourceLocation(location, "inventory");
			event.modelRegistry.putObject(modelLocation, bakedModel);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

ClientProxy.modelLocationsに登録されたモデルをすべてbakeし、modelRegistryに登録します。
これにより、ITurtleUpgrade実装クラスのgetModel()で指定したモデルが描画されるようになります。

turtle_sample_left.json (models/block)

Turtleの左側に装着された時のTurtle Upgradeのモデルを指定するJSONファイルです。

今回の例ではJSONファイルの位置を "block/turtle_sample_left" と指定したため(TurtleSampleのコンストラクタ)、assets\<MOD_ID>\models\block ディレクトリに設置します。

"parent": "computercraft:block/turtle_upgrade_base_left",
"textures": {
	"texture": "blocks/stone"
}

モデル自体はComputerCraft本体で定義されている物を流用しており、テクスチャのみ独自に設定(今回はバニラの石ブロック)しています。

turtle_sample_right.json (models/block)

Turtleの右側に装着された時のTurtle Upgradeのモデルを指定するJSONファイルです。
解説はturtle_sample_left.jsonの項目を参照してください。