提供: Minecraft Modding Wiki
この記事は"Minecraft Forge Universal 10.12.1.1090~"を前提MODとしています。 |
この記事は執筆中です。加筆してくださる人を募集しています。 |
目次
- 1 プレイヤーへのカスタムデータの追加
- 2 ソースコード
- 2.1 SampleModクラス
- 2.2 SampleEntityPropertiesEventHandlerクラス
- 2.3 ExtendedPlayerPropertiesクラス
- 2.4 CommonProxyクラス
- 2.5 ClientProxyクラス
- 2.6 PacketHandlerクラス
- 2.7 MessagePlayerPropertiesクラス
- 2.8 MessagePlayerPropertiesHandlerクラス
- 2.9 MessagePlayerJoinInAnnouncementクラス
- 2.10 MessagePlayerJoinInAnoucementHandlerクラス
- 3 解説
プレイヤーへのカスタムデータの追加
各プレイヤーにカスタムデータを追加する。 方法としては、EntityクラスのgetEntityDataメソッドの利用か、ForgeのIExtendedEntityPropertiesインターフェースの利用の二通りある。
このチュートリアルでは、IExtendedEntityPropertiesによる実装方法を解説する。
ソースコード
SampleModクラス
package sampleMod; import cpw.mods.fml.common.FMLCommonHandler; import cpw.mods.fml.common.Mod; import cpw.mods.fml.common.Mod.EventHandler; import cpw.mods.fml.common.event.FMLInitializationEvent; import cpw.mods.fml.common.event.FMLPreInitializationEvent; import cpw.mods.fml.common.eventhandler.SubscribeEvent; import cpw.mods.fml.common.gameevent.PlayerEvent; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.nbt.NBTTagCompound; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.event.entity.EntityEvent; import net.minecraftforge.event.entity.EntityJoinWorldEvent; import net.minecraftforge.event.entity.living.LivingDeathEvent; import java.util.HashMap; import java.util.Map; //FMLのパケットシステムはForge 10.12.1.1090以降でないと動作しないので、dependenciesでそれ以降を指定すること。 @Mod(modid = "SampleMod", name = "SampleMod", version = "1.0", dependencies = "required-after:Forge@[10.12.1.1090,)", useMetadata = true) public class SampleMod { @SidedProxy(clientSide = "sampleMod.ClientProxy", serverSide = "sampleMod.CommonProxy") public static CommonProxy proxy; @EventHandler public void preInit(FMLPreInitializationEvent event) { //Messageの登録呼び出し PacketHandler.init(); } @EventHandler public void init(FMLInitializationEvent event) { //二箇所に登録するので、先にインスタンスを生成しておく。 SampleEntityPropertiesEventHandler sampleEntityPropertiesEventHandler = new SampleEntityPropertiesEventHandler(); //Forge Eventの登録。EntityEvent.EntityConstructingとLivingDeathEventとEntityJoinWorldEvent MinecraftForge.EVENT_BUS.register(sampleEntityPropertiesEventHandler); //FML Eventの登録。PlayerRespawnEvent FMLCommonHandler.instance().bus().register(sampleEntityPropertiesEventHandler); } }
SampleEntityPropertiesEventHandlerクラス
package sampleMod; import cpw.mods.fml.common.eventhandler.SubscribeEvent; import cpw.mods.fml.common.gameevent.PlayerEvent; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.nbt.NBTTagCompound; import net.minecraftforge.common.IExtendedEntityProperties; import net.minecraftforge.event.entity.EntityEvent; import net.minecraftforge.event.entity.EntityJoinWorldEvent; public class SampleEntityPropertiesEventHandler { /*IExtendedEntityPropertiesを登録する処理を呼び出す*/ @SubscribeEvent public void onEntityConstructing(EntityEvent.EntityConstructing event) { if (event.entity instanceof EntityPlayer) { ExtendedPlayerProperties.register((EntityPlayer)event.entity); } } @SubscribeEvent /*ワールドに入った時に呼ばれるイベント。ここでIExtendedEntityPropertiesを読み込む処理を呼び出す*/ public void onEntityJoinWorld(EntityJoinWorldEvent event) { if (event.world.isRemote && event.entity instanceof EntityPlayer) { EntityPlayer player = (EntityPlayer)event.entity; PacketHandler.INSTANCE.sendToServer(new MessagePlayerJoinInAnnouncement(player)); } } @SubscribeEvent //Dimension移動時や、リスポーン時に呼ばれるイベント。古いインスタンスと新しいインスタンスの両方を参照できる。 public void onClonePlayer(net.minecraftforge.event.entity.player.PlayerEvent.Clone event) { //死亡時に呼ばれてるかどうか if (event.wasDeath) { //古いカスタムデータ IExtendedEntityProperties oldEntityProperties = event.original.getExtendedProperties(ExtendedPlayerProperties.EXT_PROP_NAME); //新しいカスタムデータ IExtendedEntityProperties newEntityProperties = event.entityPlayer.getExtendedProperties(ExtendedPlayerProperties.EXT_PROP_NAME); NBTTagCompound playerData = new NBTTagCompound(); //データの吸い出し oldEntityProperties.saveNBTData(playerData); //データの書き込み newEntityProperties.loadNBTData(playerData); } } @SubscribeEvent /*リスポーン時に呼ばれるイベント。Serverとの同期を取る*/ public void respawnEvent(PlayerEvent.PlayerRespawnEvent event) { if (!event.player.worldObj.isRemote) { PacketHandler.INSTANCE.sendTo(new MessagePlayerProperties(event.player), (EntityPlayerMP)event.player); } } }
ExtendedPlayerPropertiesクラス
package sampleMod; import net.minecraft.entity.Entity; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTTagList; import net.minecraft.world.World; import net.minecraftforge.common.IExtendedEntityProperties; import net.minecraftforge.common.util.Constants; public class ExtendedPlayerProperties implements IExtendedEntityProperties { /* MOD固有の文字列。EntityPlayerに登録時に使用。 MOD内で複数のIExtendedEntityPropertiesを使う場合は、別の文字列をそれぞれ割り当てること。*/ public final static String EXT_PROP_NAME = "samplePlayerData"; private int sampleInt = 0; private double sampleDouble = 0.0D; private boolean sampleBoolean = false; private String sampleString = ""; private ItemStack sampleItemStack = new ItemStack(Items.apple); private ItemStack[] sampleItemStacks = new ItemStack[10]; private NBTTagCompound sampleNBTTagCompound = new NBTTagCompound(); /*EntityPlayerにIExtendedEntityPropertiesを登録。登録文字列はMOD固有のものを割り当てること*/ public static void register(EntityPlayer player) { player.registerExtendedProperties(EXT_PROP_NAME, new ExtendedPlayerProperties()); } /*IExtendedEntityPropertiesをEntityPlayerインスタンスから取得する*/ public static ExtendedPlayerProperties get(EntityPlayer player) { return (ExtendedPlayerProperties)player.getExtendedProperties(EXT_PROP_NAME); } @Override public void saveNBTData(NBTTagCompound compound) { NBTTagCompound nbt = new NBTTagCompound(); nbt.setInteger("sampleInt", this.sampleInt); nbt.setDouble("sampleDouble", this.sampleDouble); nbt.setBoolean("sampleBoolean", this.sampleBoolean); nbt.setString("sampleString", this.sampleString); nbt.setTag("sampleTag", this.sampleNBTTagCompound); //ItemStackの保存 NBTTagCompound itemNBT = new NBTTagCompound(); this.sampleItemStack.writeToNBT(itemNBT); nbt.setTag("sampleItemStack", itemNBT); //ItemStackの配列の保存 NBTTagList itemsTagList = new NBTTagList(); for (int i = 0; i < this.sampleItemStacks.length; ++i) { if (this.sampleItemStacks[i] != null) { NBTTagCompound var4 = new NBTTagCompound(); var4.setByte("Slot", (byte)i); this.sampleItemStacks[i].writeToNBT(var4); itemsTagList.appendTag(var4); } } nbt.setTag("Items", itemsTagList); compound.setTag(EXT_PROP_NAME, nbt); } @Override public void loadNBTData(NBTTagCompound compound) { NBTTagCompound nbt = (NBTTagCompound)compound.getTag(EXT_PROP_NAME); this.sampleInt = nbt.getInteger("sampleInt"); this.sampleDouble = nbt.getDouble("sampleDouble"); this.sampleBoolean = nbt.getBoolean("sampleBoolean"); this.sampleString = nbt.getString("sampleString"); this.sampleNBTTagCompound = nbt.getCompoundTag("sampleTag"); //ItemStackの読み込み this.sampleItemStack = ItemStack.loadItemStackFromNBT(nbt.getCompoundTag("sampleItemStack")); //ItemStackの配列の読み込み NBTTagList itemsTagList = nbt.getTagList("Items", Constants.NBT.TAG_COMPOUND); this.sampleItemStacks = new ItemStack[10]; for (int i = 0; i < itemsTagList.tagCount(); ++i) { NBTTagCompound var4 = itemsTagList.getCompoundTagAt(i); int slot = var4.getByte("Slot") & 255; if (slot >= 0 && slot < this.sampleItemStacks.length) { this.sampleItemStacks[slot] = ItemStack.loadItemStackFromNBT(var4); } } } @Override /*初期化メソッド。今のところ使う必要はない。*/ public void init(Entity entity, World world) {} /*以降、各変数のGetterおよびSetter。 * 使い方としては、EntityPlayerのインスタンスが取得できるメソッド内で、 * ExtendedPlayerProperties.get(playerインスタンス).setSampleInt(sample) * と呼び出す。*/ public int getSampleInt() { return sampleInt; } public void setSampleInt(int sampleInt) { this.sampleInt = sampleInt; } public double getSampleDouble() { return sampleDouble; } public void setSampleDouble(double sampleDouble) { this.sampleDouble = sampleDouble; } public boolean isSampleBoolean() { return sampleBoolean; } public void setSampleBoolean(boolean sampleBoolean) { this.sampleBoolean = sampleBoolean; } public String getSampleString() { return sampleString; } public void setSampleString(String sampleString) { this.sampleString = sampleString; } public ItemStack getSampleItemStack() { return sampleItemStack; } public void setSampleItemStack(ItemStack sampleItemStack) { this.sampleItemStack = sampleItemStack; } public ItemStack[] getSampleItemStacks() { return sampleItemStacks; } public void setSampleItemStacks(ItemStack[] sampleItemStacks) { this.sampleItemStacks = sampleItemStacks; } public NBTTagCompound getSampleNBTTagCompound() { return sampleNBTTagCompound; } public void setSampleNBTTagCompound(NBTTagCompound sampleNBTTagCompound) { this.sampleNBTTagCompound = sampleNBTTagCompound; } }
CommonProxyクラス
package sampleMod; import net.minecraft.entity.player.EntityPlayer; public class CommonProxy { public EntityPlayer getEntityPlayerInstance() {return null;} }
ClientProxyクラス
package sampleMod; import net.minecraft.client.Minecraft; import net.minecraft.entity.player.EntityPlayer; public class ClientProxy extends CommonProxy { @Override public EntityPlayer getEntityPlayerInstance() { return Minecraft.getMinecraft().thePlayer; } }
PacketHandlerクラス
package sampleMod; import cpw.mods.fml.common.network.NetworkRegistry; import cpw.mods.fml.common.network.simpleimpl.SimpleNetworkWrapper; import cpw.mods.fml.relauncher.Side; public class PacketHandler { /*MOD固有のSimpleNetworkWrapperを取得。 * 文字列は他のMODと被らないようにMOD_IDを指定しておくと良い*/ public static final SimpleNetworkWrapper INSTANCE = NetworkRegistry.INSTANCE.newSimpleChannel("samplemod"); public static void init() { /*Messageクラスの登録。 * 第一引数:IMessageHandlerクラス * 第二引数:送るMessageクラス * 第三引数:登録番号。255個まで * 第四引数:ClientとServerのどちらに送るか。送り先*/ INSTANCE.registerMessage(MessagePlayerPropertiesHandler.class, MessagePlayerProperties.class, 0, Side.CLIENT); INSTANCE.registerMessage(MessagePlayerJoinInAnoucementHandler.class, MessagePlayerJoinInAnnouncement.class, 1, Side.SERVER); } }
MessagePlayerPropertiesクラス
package sampleMod; import cpw.mods.fml.common.network.ByteBufUtils; import cpw.mods.fml.common.network.simpleimpl.IMessage; import io.netty.buffer.ByteBuf; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.nbt.NBTTagCompound; public class MessagePlayerProperties implements IMessage { public NBTTagCompound data; public MessagePlayerProperties(){} public MessagePlayerProperties(EntityPlayer entityPlayer) { this.data = new NBTTagCompound(); //EntityPlayerからIExtendedEntityPropertiesを取得。 ExtendedPlayerProperties.get(entityPlayer).saveNBTData(data); } @Override public void fromBytes(ByteBuf buf) { data = ByteBufUtils.readTag(buf); } @Override public void toBytes(ByteBuf buf) { ByteBufUtils.writeTag(buf, data); } }
MessagePlayerPropertiesHandlerクラス
package sampleMod; import cpw.mods.fml.common.network.simpleimpl.IMessage; import cpw.mods.fml.common.network.simpleimpl.IMessageHandler; import cpw.mods.fml.common.network.simpleimpl.MessageContext; import io.netty.buffer.ByteBuf; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.nbt.NBTTagCompound; public class MessagePlayerPropertiesHandler implements IMessageHandler<MessagePlayerProperties, IMessage> { @Override public IMessage onMessage(MessagePlayerProperties message, MessageContext ctx) { //Client側にIExtendedEntityPropertiesを渡す。 ExtendedPlayerProperties.get(SampleMod.proxy.getEntityPlayerInstance()).loadNBTData(message.data); //REPLYは送らないので、nullを返す。 return null; } }
MessagePlayerJoinInAnnouncementクラス
package sampleMod; import cpw.mods.fml.common.network.ByteBufUtils; import cpw.mods.fml.common.network.simpleimpl.IMessage; import io.netty.buffer.ByteBuf; import net.minecraft.entity.player.EntityPlayer; public class MessagePlayerJoinInAnnouncement implements IMessage { private String uuid; public MessagePlayerJoinInAnnouncement(){} public MessagePlayerJoinInAnnouncement(EntityPlayer player) { //PlayerのUUIDを文字列で取得 this.uuid = player.getGameProfile().getId().toString(); } @Override public void fromBytes(ByteBuf buf) { this.uuid = ByteBufUtils.readUTF8String(buf); } @Override public void toBytes(ByteBuf buf) { ByteBufUtils.writeUTF8String(buf, this.uuid); } public String getUuid() { return uuid; } public void setUuid(String uuid) { this.uuid = uuid; } }
MessagePlayerJoinInAnoucementHandlerクラス
package sampleMod; import cpw.mods.fml.common.network.simpleimpl.IMessageHandler; import cpw.mods.fml.common.network.simpleimpl.MessageContext; import net.minecraft.entity.player.EntityPlayer; public class MessagePlayerJoinInAnoucementHandler implements IMessageHandler<MessagePlayerJoinInAnnouncement, MessagePlayerProperties> { @Override public MessagePlayerProperties onMessage(MessagePlayerJoinInAnnouncement message, MessageContext ctx) { //UUIDの文字列を受け取る String uuidString = message.getUuid(); EntityPlayer player = ctx.getServerHandler().playerEntity; //取得したPlayerが同一UUIDを持つか判定 if (player.getGameProfile().getId().toString().equals(uuidString)) { //クライアント側にデータを送る return new MessagePlayerProperties(player); } //UUIDが違っていた場合、同期処理を呼ばない return null; } }
解説
細かい説明はソースコード内に付記したので、そちらを参照のこと。
使用の流れを簡単に書くと、
EntityEvent.EntityConstructingにて、IExtendedEntityPropertiesを登録。
ExtendedPlayerProperties.get(playerインスタンス).getSampleInt()という形でデータを呼び出す。
ExtendedPlayerProperties.get(playerインスタンス).setSampleInt(sample)という形でデータを書き込む。
死亡時等でカスタムデータが初期化されるので、CloneEventでデータを新しいインスタンスに移行し、EntityJoinWorldEventとRespawnEventで、クライアント側にデータを送って同期させている。