提供: Minecraft Modding Wiki
2015年3月14日 (土) 01:42時点におけるPino (トーク | 投稿記録)による版 (誤字及び脱字の修正)
移動先: 案内検索

この記事は"Minecraft Forge Universal 11.14.0~"を前提MODとしています。

Stone pickaxe.png
中級者向けのチュートリアルです。
C player.png
Playerに関係のあるチュートリアルです。
この記事は執筆中です。加筆してくださる人を募集しています。

プレイヤーへのカスタムデータの追加

各プレイヤーにカスタムデータを追加する。 方法としては、EntityクラスのgetEntityDataメソッドの利用か、ForgeのIExtendedEntityPropertiesインターフェースの利用の二通りある。

このチュートリアルでは、IExtendedEntityPropertiesによる実装方法を解説する。

ソースコード

SampleModクラス

package com.example.examplemod;

import net.minecraftforge.fml.common.FMLCommonHandler;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.Mod.EventHandler;
import net.minecraftforge.fml.common.event.FMLInitializationEvent;
import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.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;

//1.8のforgeを指定
@Mod(modid = "SampleMod", name = "SampleMod", version = "1.0", dependencies = "required-after:Forge@[11.14.0.1296,)", useMetadata = true)
public class SampleMod {
    @SidedProxy(clientSide = "sampleMod.ClientProxy", serverSide = "sampleMod.CommonProxy")
    public static CommonProxy proxy;
    //Player毎のIExtendedEntityPropertiesを保存するMap。ダイヤモンド演算子はJava7以降なので注意。
    private static final Map<String, NBTTagCompound> extendedEntityData = new HashMap<>();

    @EventHandler
    public void preInit(FMLPreInitializationEvent event) {
        //Messageの登録呼び出し
        PacketHandler.init();
    }

    @EventHandler
    public void init(FMLInitializationEvent event) {
        //Forge Eventの登録。EntityEvent.EntityConstructingとLivingDeathEventとEntityJoinWorldEvent
        MinecraftForge.EVENT_BUS.register(this);
        //FML Eventの登録。PlayerChangedDimensionEventとPlayerRespawnEvent
        FMLCommonHandler.instance().bus().register(this);
    }

    @SubscribeEvent
    /*IExtendedEntityPropertiesを登録する処理を呼び出す*/
    public void onEntityConstructing(EntityEvent.EntityConstructing event) {
        if (event.entity instanceof EntityPlayer) {
            ExtendedPlayerProperties.register((EntityPlayer)event.entity);
        }
    }

    @SubscribeEvent
    /*死亡時に呼ばれるイベント。ここで、IExtendedEntityPropertiesを保存する処理を呼び出す*/
    public void onLivingDeathEvent(LivingDeathEvent event)  {
        if (event.entityLiving instanceof EntityPlayer && !event.entity.worldObj.isRemote) {
            NBTTagCompound playerData = new NBTTagCompound();
            (event.entity.getExtendedProperties(ExtendedPlayerProperties.EXT_PROP_NAME)).saveNBTData(playerData);
            storeEntityData(((EntityPlayer) event.entity).getGameProfile().getId().toString(), playerData);
        }
    }

    @SubscribeEvent
    /*ワールドに入った時に呼ばれるイベント。ここでIExtendedEntityPropertiesを読み込む処理を呼び出す*/
    public void onEntityJoinWorld(EntityJoinWorldEvent event)  {
        if (event.entity.worldObj.isRemote && event.entity instanceof EntityPlayer) {
            EntityPlayer player = (EntityPlayer)event.entity;
            PacketHandler.INSTANCE.sendToServer(new MessagePlayerJoinInAnnouncement(player));
        }
    }

    @SubscribeEvent
    /*リスポーン時に呼ばれるイベント。Serverとの同期を取る*/
    public void respawnEvent(PlayerEvent.PlayerRespawnEvent event) {
        if (!event.player.worldObj.isRemote) {
            PacketHandler.INSTANCE.sendTo(new MessagePlayerProperties(event.player), (EntityPlayerMP)event.player);
        }
    }

    @SubscribeEvent
    /*Dimensionを移動した時に呼ばれるイベント。Serverとの同期を取る*/
    public void changedDimension(PlayerEvent.PlayerChangedDimensionEvent event) {
        if (!event.player.worldObj.isRemote) {
            PacketHandler.INSTANCE.sendTo(new MessagePlayerProperties(event.player), (EntityPlayerMP)event.player);
        }
    }

    /*PlayerのIExtendedEntityPropertiesをMapに保存*/
    public static void storeEntityData(String name, NBTTagCompound compound) {
        extendedEntityData.put(name, compound);
    }

    /*PlayerのIExtendedEntityPropertiesをMapから読み込み*/
    public static NBTTagCompound getEntityData(String name) {
        return extendedEntityData.remove(name);
    }
}

ExtendedPlayerPropertiesクラス

package com.example.examplemod;

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インスタンスから外部保存時の固有文字列を返す
    *playernameは変更可能になったので、UUIDに変更 */
    private static String getSaveKey(EntityPlayer player) {
        return player.getGameProfile().getId().toString() + ":" + EXT_PROP_NAME;
    }

    /*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 com.example.examplemod;

import net.minecraft.entity.player.EntityPlayer;

public class CommonProxy {

public EntityPlayer getEntityPlayerInstance() {return null;}

}

ClientProxyクラス

package com.example.examplemod;

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 com.example.examplemod;

import net.minecraftforge.fml.common.network.NetworkRegistry;
import net.minecraftforge.fml.common.network.simpleimpl.SimpleNetworkWrapper;
import net.minecraftforge.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 com.example.examplemod;

import net.minecraftforge.fml.common.network.ByteBufUtils;
import net.minecraftforge.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 com.example.examplemod;

import net.minecraftforge.fml.common.network.simpleimpl.IMessage;
import net.minecraftforge.fml.common.network.simpleimpl.IMessageHandler;
import net.minecraftforge.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 com.example.examplemod;

import io.netty.buffer.ByteBuf;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraftforge.fml.common.network.ByteBufUtils;
import net.minecraftforge.fml.common.network.simpleimpl.IMessage;

public class MessagePlayerJoinInAnnouncement implements IMessage {

    private String uuid;

    public MessagePlayerJoinInAnnouncement(){}

    public MessagePlayerJoinInAnnouncement(EntityPlayer player) {
        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;
    }
}

MessagePlayerJoinInAnnouncementHandlerクラス

package com.example.examplemod;

import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraftforge.fml.common.network.simpleimpl.IMessageHandler;
import net.minecraftforge.fml.common.network.simpleimpl.MessageContext;

public class MessagePlayerJoinInAnoucementHandler implements IMessageHandler<MessagePlayerJoinInAnnouncement, MessagePlayerProperties> {
    @Override
    public MessagePlayerProperties onMessage(MessagePlayerJoinInAnnouncement message, MessageContext ctx) {
        //UUIDの文字列を受け取る
        String uuidString = message.getUuid();
        //UUIDからカスタムデータを取得
        NBTTagCompound playerData = SampleMod.getEntityData(uuidString);
        EntityPlayer player = ctx.getServerHandler().playerEntity;
        if (playerData != null) {
            //プレイヤーのNBTに書き込む
            (player.getExtendedProperties(ExtendedPlayerProperties.EXT_PROP_NAME)).loadNBTData(playerData);
        }
        //クライアント側にデータを送る
        return new MessagePlayerProperties(player);
    }
}

解説

細かい説明はソースコード内に付記したので、そちらを参照のこと。

使用の流れを簡単に書くと、
EntityEvent.EntityConstructingにて、IExtendedEntityPropertiesを登録。
ExtendedPlayerProperties.get(playerインスタンス).getSampleInt()という形でデータを呼び出す。
ExtendedPlayerProperties.get(playerインスタンス).setSampleInt(sample)という形でデータを書き込む。
死亡時等でクライアント側のNBTデータが初期化されるので、LivingDeathEventでデータを保存し、EntityJoinWorldEventで、保存したデータを復帰させる。
PlayerRespawnEventやPlayerChangedDimensionEventでも同期を取っているが、Forgeのビルドによっては修正されているかもしれないので、適宜確認の上、追加削除を行うこと。