提供: Minecraft Modding Wiki
移動先: 案内検索
(FML仕様のパケットシステムに変更。)
1行目: 1行目:
 
{{前提チュートリアル|page=1.7のパケットについて}}
 
{{前提チュートリアル|page=1.7のパケットについて}}
{{前提MOD|reqmod="Minecraft Forge Universal 10.12.0.xxx~"}}
+
{{前提MOD|reqmod="Minecraft Forge Universal 10.12.1.1090~"}}
  
  
36行目: 36行目:
 
}
 
}
 
</source>
 
</source>
 +
PacketHandler.class
 +
<source lang = "java">
 +
 +
package yourpackage;
 +
 +
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を生成。チャンネルの文字列は固有であれば何でも良い。MODIDの利用を推奨。
 +
    public static final SimpleNetworkWrapper INSTANCE = NetworkRegistry.INSTANCE.newSimpleChannel("KeySampleMod");
 +
 +
 +
    public static void init() {
 +
        INSTANCE.registerMessage(MessageSample.class, MessageSample.class, 0, Side.SERVER);
 +
    }
 +
}</source>
 
SampleKeyCore.java(importは省略)
 
SampleKeyCore.java(importは省略)
 
<source lang = "java">
 
<source lang = "java">
44行目: 64行目:
 
     @SidedProxy(clientSide = "yourpackage.Client.ClientProxy", serverSide = "yourpackage.CommonProxy")
 
     @SidedProxy(clientSide = "yourpackage.Client.ClientProxy", serverSide = "yourpackage.CommonProxy")
 
     public static CommonProxy proxy;
 
     public static CommonProxy proxy;
    //パケットハンドラー。解説は前提記事を参照のこと。
+
 
    public static final PacketPipeline packetPipeline = new PacketPipeline();
 
 
     @EventHandler
 
     @EventHandler
 
     public void load(FMLInitializationEvent event) {
 
     public void load(FMLInitializationEvent event) {
        packetPipeline.initialise();
 
        packetPipeline.registerPacket(KeyHandlingPacket.class);//パケットの登録
 
 
         FMLCommonHandler.instance().bus().register(this);//KeyHandlingEvent用
 
         FMLCommonHandler.instance().bus().register(this);//KeyHandlingEvent用
        MinecraftForge.EVENT_BUS.register(this);//LivingUpdate用
 
    }
 
    @EventHandler
 
    public void postInit(FMLPostInitializationEvent event) {
 
        packetPipeline.postInitialise();
 
 
     }
 
     }
    //キーが押されたかどうかを保存する変数
 
    public static boolean pressSampleKey = false;
 
 
     //キーが“押された時”に呼ばれる。“押しっぱなし”の判定は別途用意する必要あり。
 
     //キーが“押された時”に呼ばれる。“押しっぱなし”の判定は別途用意する必要あり。
 
     @SubscribeEvent
 
     @SubscribeEvent
 
     public void KeyHandlingEvent(KeyInputEvent event) {
 
     public void KeyHandlingEvent(KeyInputEvent event) {
         while (ClientProxy.sampleKey.isPressed()) {
+
         if (ClientProxy.sampleKey.isPressed()) {
             pressSampleKey = true;//クライアント側のみの変更。
+
             PacketHandler.INSTANCE.sendToServer(new MessageKeyPressed(1));//1をサーバーに送る。
        }
 
    }
 
    @SubscribeEvent
 
    public void LivingUpdate(LivingUpdateEvent event) {
 
        if (event.entityLiving != null && event.entityLiving instanceof EntityPlayer) {
 
            if (event.entityLiving.worldObj.isRemote) {
 
                //ここで、クライアントでの変数の変更をサーバーに伝える。
 
                packetPipeline.sendToServer(new KeyHandlingPacket(pressSampleKey ));
 
            }
 
            if (pressSampleKey) {
 
                //クライアントとサーバーの両方で呼ばれる。ここに必要な処理を記述
 
                pressSampleKey = false;//呼ばれ続けるため、ここでクリア
 
            }
 
 
         }
 
         }
 
     }
 
     }
 
}
 
}
 
</source>
 
</source>
KeyHandlingPacket.java
+
MessageKeyPressed.java
 
<source lang = "java">
 
<source lang = "java">
 
package yourpackage;
 
package yourpackage;
 
+
import cpw.mods.fml.client.FMLClientHandler;
 +
import cpw.mods.fml.common.FMLCommonHandler;
 +
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 io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
 
 
import net.minecraft.entity.player.EntityPlayer;
 
import net.minecraft.entity.player.EntityPlayer;
  
//クライアント側でキー入力によって変化したboolean変数をサーバー側に伝達するパケット。AbstractPacketを継承
+
public class MessageKeyPressed implements IMessage, IMessageHandler<MessageKeyPressed, IMessage> {
public class KeyHandlingPacket extends AbstractPacket
 
{
 
//保持しておくboolean型変数
 
boolean pressSampleKey;
 
  
//引数を持つコンストラクタを追加する場合は、空のコンストラクタを用意してくれとのこと。
+
    private byte key;
public KeyHandlingPacket() {
 
}
 
  
//パケット生成を簡略化するために、boolean型変数を引数に取るコンストラクタを追加。
+
    public MessageKeyPressed(){}
public KeyHandlingPacket(boolean ver1) {
 
pressSampleKey = ver1;
 
}
 
  
@Override
+
    public MessageKeyPressed(byte keyPressed) {
public void encodeInto(ChannelHandlerContext ctx, ByteBuf buffer) {
+
        this.key = keyPressed;
//ByteBufに変数を代入。基本的にsetメソッドではなく、writeメソッドを使う。
+
    }
buffer.writeBoolean(pressSampleKey);
+
 
}
+
    @Override
 +
    public void fromBytes(ByteBuf buf) {
 +
        this.key = buf.readByte();
 +
    }
  
@Override
+
    @Override
public void decodeInto(ChannelHandlerContext ctx, ByteBuf buffer) {
+
    public void toBytes(ByteBuf buf) {
//ByteBufから変数を取得。こちらもgetメソッドではなく、readメソッドを使う。
+
        buf.writeByte(this.key);
pressSampleKey = buffer.readBoolean();
+
    }
}
 
  
@Override
+
    @Override
public void handleClientSide(EntityPlayer player) {
+
    public IMessage onMessage(MessageKeyPressed message, MessageContext ctx) {
//今回はクライアントの情報をサーバーに送るので、こちらはなにもしない。
+
        EntityPlayer entityPlayer = ctx.getServerHandler().playerEntity;
}
+
        //受け取ったMessageクラスのkey変数の数字をチャットに出力
  
@Override
+
        entityPlayer.addChatComponentMessage(new ChatComponentText(String.format("Received byte %d", message.key)));
public void handleServerSide(EntityPlayer player) {
+
        return null;
SampleKeyCore.pressSampleKey = pressSampleKey;
+
    }
}
 
 
}
 
}
 
</source>
 
</source>
130行目: 122行目:
 
===ClientProxy===
 
===ClientProxy===
 
<source lang = "java">
 
<source lang = "java">
package yourpackage.client;
+
package yourpackage.client;tProxy===
 
import yourpackage.CommonProxy;
 
import yourpackage.CommonProxy;
 
import net.minecraft.client.settings.KeyBinding;
 
import net.minecraft.client.settings.KeyBinding;
148行目: 140行目:
 
===KeyInputEvent===
 
===KeyInputEvent===
 
<source lang = "java">
 
<source lang = "java">
//キーが押されたかどうかを保存する変数
+
//キーが押されたかどうかを保存する変数tEvent===
 
public static boolean pressSampleKey = false;
 
public static boolean pressSampleKey = false;
 
//キーが“押された時”に呼ばれる。“押しっぱなし”の判定は別途用意する必要あり。
 
//キーが“押された時”に呼ばれる。“押しっぱなし”の判定は別途用意する必要あり。
@SubscribeEvent
+
    @SubscribeEvent
 
public void KeyHandlingEvent(KeyInputEvent event) {
 
public void KeyHandlingEvent(KeyInputEvent event) {
     while (ClientProxy.sampleKey.isPressed()) {
+
     if (ClientProxy.sampleKey.isPressed()) {
         pressSampleKey = true;//クライアント側のみの変更。
+
         PacketHandler.INSTANCE.sendToServer(new MessageKeyPressed(1));//1をサーバーに送る。
 
     }
 
     }
 
}
 
}
160行目: 152行目:
 
1.7より追加されたイベント。いづれかのキーが押された時に、呼ばれる。<br>
 
1.7より追加されたイベント。いづれかのキーが押された時に、呼ばれる。<br>
 
どのキーが押されたかはeventに保存されていないので、用意しているKeyBinding変数のメソッドを利用する必要が有る。<br>
 
どのキーが押されたかはeventに保存されていないので、用意しているKeyBinding変数のメソッドを利用する必要が有る。<br>
isPressedメソッドはキーが“押された回数”が0以上の時にtrueになるが、呼ばれた際に、押された回数を1減らす。
 
チャタリング等の防止のため、while文で押された回数が0になるまで呼んでいる。
 
このため、同じキーの登録被りが起こった場合確実にどちらかは動作しなくなる。
 
中途半端に動作するよりは良いので、この方法をとっている。<br>
 
 
このイベントはクライアント側のみであるので、サーバーにキー判定を伝えるにはパケットを送る必要が有る。
 
このイベントはクライアント側のみであるので、サーバーにキー判定を伝えるにはパケットを送る必要が有る。
===KeyHandlingPacket===
+
===MessageKeyPressed===
 
<source lang = "java">
 
<source lang = "java">
 
package yourpackage;
 
package yourpackage;
 
+
import cpw.mods.fml.client.FMLClientHandler;
 +
import cpw.mods.fml.common.FMLCommonHandler;
 +
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 io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
 
 
import net.minecraft.entity.player.EntityPlayer;
 
import net.minecraft.entity.player.EntityPlayer;
  
//クライアント側でキー入力によって変化したboolean変数をサーバー側に伝達するパケット。AbstractPacketを継承
+
public class MessageKeyPressed implements IMessage, IMessageHandler<MessageKeyPressed, IMessage> {
public class KeyHandlingPacket extends AbstractPacket
 
{
 
//保持しておくboolean型変数
 
boolean pressSampleKey;
 
  
//引数を持つコンストラクタを追加する場合は、空のコンストラクタを用意してくれとのこと。
+
    private byte key;
public KeyHandlingPacket() {
 
}
 
  
//パケット生成を簡略化するために、boolean型変数を引数に取るコンストラクタを追加。
+
    public MessageKeyPressed(){}
public KeyHandlingPacket(boolean ver1) {
 
pressSampleKey = ver1;
 
}
 
  
@Override
+
    public MessageKeyPressed(byte keyPressed) {
public void encodeInto(ChannelHandlerContext ctx, ByteBuf buffer) {
+
        this.key = keyPressed;
//ByteBufに変数を代入。基本的にsetメソッドではなく、writeメソッドを使う。
+
    }
buffer.writeBoolean(pressSampleKey);
+
 
}
+
    @Override
 +
    public void fromBytes(ByteBuf buf) {
 +
        this.key = buf.readByte();
 +
    }
  
@Override
+
    @Override
public void decodeInto(ChannelHandlerContext ctx, ByteBuf buffer) {
+
    public void toBytes(ByteBuf buf) {
//ByteBufから変数を取得。こちらもgetメソッドではなく、readメソッドを使う。
+
        buf.writeByte(this.key);
pressSampleKey = buffer.readBoolean();
+
    }
}
 
  
@Override
+
    @Override
public void handleClientSide(EntityPlayer player) {
+
    public IMessage onMessage(MessageKeyPressed message, MessageContext ctx) {
//今回はクライアントの情報をサーバーに送るので、こちらはなにもしない。
+
        EntityPlayer entityPlayer = ctx.getServerHandler().playerEntity;
}
+
        //受け取ったMessageクラスのkey変数の数字をチャットに出力
  
@Override
+
        entityPlayer.addChatComponentMessage(new ChatComponentText(String.format("Received byte %d", message.key)));
public void handleServerSide(EntityPlayer player) {
+
         return null;
SampleKeyCore.pressSampleKey = pressSampleKey;
 
}
 
}
 
</source>
 
キー判定を送るパケットクラス。
 
1つのクラスで両方向のパケットの記述が可能なため、慣れると重宝する。
 
ここでは、pressSampleKeyの内容をサーバーに送っている。
 
===キー判定後の処理部分===
 
<source lang = "java">
 
@SubscribeEvent
 
public void LivingUpdate(LivingUpdateEvent event) {
 
    if (event.entityLiving != null && event.entityLiving instanceof EntityPlayer) {
 
        if (event.entityLiving.worldObj.isRemote) {
 
            //ここで、クライアントでの変数の変更をサーバーに伝える。
 
            packetPipeline.sendToServer(new KeyHandlingPacket(pressSampleKey ));
 
         }
 
        if (pressSampleKey) {
 
            //クライアントとサーバーの両方で呼ばれる。ここに必要な処理を記述
 
            pressSampleKey = false;//呼ばれ続けるため、ここでクリア
 
        }
 
 
     }
 
     }
 
}
 
}
 
</source>
 
</source>
キー判定で処理を行う方法はいくつかあるが、今回はLivingUpdateEventを利用する。<br>
+
キー判定を送るMessageクラス。
まず、EntityPlayerから呼ばれているかを判定している。
+
ここでは、byte変数keyの内容をサーバーに送って、それをチャットに出力している。
次にisRemote変数がtrue、つまりクライアント側でパケットをサーバーに送信。
+
 
同期されるので、pressSampleKey判定はクライアントとサーバー両方で、同じ結果を得る。<br>
+
 
このif文内でpressSampleKeyをfalseに戻さないと、一度キーを押したら実行されっぱなしになる。<br>
 
 
押しっぱなしかどうかを見たいのであれば、tick処理ループ内で、KeyBinding変数のgetIsKeyPressed()メソッドを監視し続ければよい。
 
押しっぱなしかどうかを見たいのであれば、tick処理ループ内で、KeyBinding変数のgetIsKeyPressed()メソッドを監視し続ければよい。

2014年5月31日 (土) 22:10時点における版

この記事は1.7のパケットについてを読んだ事を前提としています。

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


1.6におけるキーの処理

1.6までは、KeyHandlerクラスを継承して、KeyUpとKeyDownをtick単位で見ることが出来ました。 1.7では、tick処理自体がイベントになったので、キーの処理もイベントになりました。

1.7でのキーイベント追加

キーイベントの追加方法

ソースコード

CommonProxy.java

package yourpackage;
//ClientProxyのみ必要だが、拡張性も考えCommonProxyも用意。GUI等追加するなら、IGuiHandlerを実装のこと。
public class CommonProxy {

    public void registerClientInfo(){}

}

ClientProxy.java

package yourpackage.client;
import yourpackage.CommonProxy;
import net.minecraft.client.settings.KeyBinding;
import cpw.mods.fml.client.registry.ClientRegistry;
public class ClientProxy extends CommonProxy {
    //キーのUnlocalizedName、バインドするキーの対応整数値(Keyboardクラス参照のこと)、カテゴリー名
    public static final KeyBinding sampleKey = new KeyBinding("Key.sample", Keyboard.KEY_R, "CategoryName");
    @Override
    public void registerClientInfo() {
        ClientRegistry.registerKeyBinding(sampleKey);
    }

}

PacketHandler.class

package yourpackage;

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を生成。チャンネルの文字列は固有であれば何でも良い。MODIDの利用を推奨。
    public static final SimpleNetworkWrapper INSTANCE = NetworkRegistry.INSTANCE.newSimpleChannel("KeySampleMod");


    public static void init() {
        INSTANCE.registerMessage(MessageSample.class, MessageSample.class, 0, Side.SERVER);
    }
}

SampleKeyCore.java(importは省略)

package yourpackage;
@Mod(modid="KeySampleMod", name="KeySampleMod", version="1.0")
public class SampleKeyCore
{
    @SidedProxy(clientSide = "yourpackage.Client.ClientProxy", serverSide = "yourpackage.CommonProxy")
    public static CommonProxy proxy;

    @EventHandler
    public void load(FMLInitializationEvent event) {
        FMLCommonHandler.instance().bus().register(this);//KeyHandlingEvent用
    }
    //キーが“押された時”に呼ばれる。“押しっぱなし”の判定は別途用意する必要あり。
    @SubscribeEvent
    public void KeyHandlingEvent(KeyInputEvent event) {
        if (ClientProxy.sampleKey.isPressed()) {
            PacketHandler.INSTANCE.sendToServer(new MessageKeyPressed(1));//1をサーバーに送る。
        }
    }
}

MessageKeyPressed.java

package yourpackage;
import cpw.mods.fml.client.FMLClientHandler;
import cpw.mods.fml.common.FMLCommonHandler;
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;

public class MessageKeyPressed implements IMessage, IMessageHandler<MessageKeyPressed, IMessage> {

    private byte key;

    public MessageKeyPressed(){}

    public MessageKeyPressed(byte keyPressed) {
        this.key = keyPressed;
    }

    @Override
    public void fromBytes(ByteBuf buf) {
        this.key = buf.readByte();
    }

    @Override
    public void toBytes(ByteBuf buf) {
        buf.writeByte(this.key);
    }

    @Override
    public IMessage onMessage(MessageKeyPressed message, MessageContext ctx) {
        EntityPlayer entityPlayer = ctx.getServerHandler().playerEntity;
        //受け取ったMessageクラスのkey変数の数字をチャットに出力

        entityPlayer.addChatComponentMessage(new ChatComponentText(String.format("Received byte %d", message.key)));
        return null;
    }
}

解説

ClientProxy

package yourpackage.client;tProxy===
import yourpackage.CommonProxy;
import net.minecraft.client.settings.KeyBinding;
import cpw.mods.fml.client.registry.ClientRegistry;
public class ClientProxy extends CommonProxy {
    //キーのUnlocalizedName、バインドするキーの対応整数値(Keyboardクラス参照のこと)、カテゴリー名
    public static final KeyBinding sampleKey = new KeyBinding("Key.sample", Keyboard.KEY_R, "CategoryName");
    @Override
    public void registerClientInfo() {
        ClientRegistry.registerKeyBinding(sampleKey);
    }
}

KeyBindingは1.6からインスタンス生成時の引数に“カテゴリー名”が追加されている。
キーのローカライズはlangファイル利用。
KeyBindingクラスとClientRegistryクラスはクライアント側のみなので、Proxyで分ける必要がある。

KeyInputEvent

//キーが押されたかどうかを保存する変数tEvent===
public static boolean pressSampleKey = false;
//キーが“押された時”に呼ばれる。“押しっぱなし”の判定は別途用意する必要あり。
    @SubscribeEvent
public void KeyHandlingEvent(KeyInputEvent event) {
    if (ClientProxy.sampleKey.isPressed()) {
        PacketHandler.INSTANCE.sendToServer(new MessageKeyPressed(1));//1をサーバーに送る。
    }
}

1.7より追加されたイベント。いづれかのキーが押された時に、呼ばれる。
どのキーが押されたかはeventに保存されていないので、用意しているKeyBinding変数のメソッドを利用する必要が有る。
このイベントはクライアント側のみであるので、サーバーにキー判定を伝えるにはパケットを送る必要が有る。

MessageKeyPressed

package yourpackage;
import cpw.mods.fml.client.FMLClientHandler;
import cpw.mods.fml.common.FMLCommonHandler;
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;

public class MessageKeyPressed implements IMessage, IMessageHandler<MessageKeyPressed, IMessage> {

    private byte key;

    public MessageKeyPressed(){}

    public MessageKeyPressed(byte keyPressed) {
        this.key = keyPressed;
    }

    @Override
    public void fromBytes(ByteBuf buf) {
        this.key = buf.readByte();
    }

    @Override
    public void toBytes(ByteBuf buf) {
        buf.writeByte(this.key);
    }

    @Override
    public IMessage onMessage(MessageKeyPressed message, MessageContext ctx) {
        EntityPlayer entityPlayer = ctx.getServerHandler().playerEntity;
        //受け取ったMessageクラスのkey変数の数字をチャットに出力

        entityPlayer.addChatComponentMessage(new ChatComponentText(String.format("Received byte %d", message.key)));
        return null;
    }
}

キー判定を送るMessageクラス。 ここでは、byte変数keyの内容をサーバーに送って、それをチャットに出力している。


押しっぱなしかどうかを見たいのであれば、tick処理ループ内で、KeyBinding変数のgetIsKeyPressed()メソッドを監視し続ければよい。