Skip to content

パケット送受信と構造

Bedrock Edition における通信はすべて、バイナリエンコードされたパケットを通じて行われます。gophertunnel では、minecraft/protocol/packet パッケージ内に Bedrock プロトコルの全パケットが Go の構造体として厳密に型定義されています。


パケットのインターフェース

すべてのパケットは packet.Packet インターフェースを実装しています。

go
type Packet interface {
    // パケットの一意なID (packet.IDText など)
    ID() uint32

    // protocol.IO (ReaderまたはWriter) を使ったシリアライズ / デシリアライズ
    Marshal(io protocol.IO)
}

1つのメソッドで双方向処理

Go では Encode / Decode を分けることが多いですが、gophertunnel では protocol.IO という読み書き両対応のインターフェースを使用し、1つの Marshal メソッドで両方の処理を記述する設計になっています。


パケットの読み込み (ReadPacket)

conn.ReadPacket() は、受信した生パケットを packet.Pool に基づいて自動デコードし、具象型のポインタを返します。

go
for {
    pk, err := conn.ReadPacket()
    if err != nil {
        log.Printf("接続終了: %v", err)
        break
    }

    // 型アサーションや型スイッチで処理を分岐
    switch p := pk.(type) {
    case *packet.Text:
        fmt.Printf("[%s] %s\n", p.SourceName, p.Message)

    case *packet.MovePlayer:
        fmt.Printf("プレイヤー移動: %v, Yaw: %.2f, Pitch: %.2f\n",
            p.Position, p.Yaw, p.Pitch)

    case *packet.LevelChunk:
        fmt.Printf("チャンク受信: X=%d, Z=%d, サブチャンク数=%d\n",
            p.Position.X(), p.Position.Z(), p.SubChunkCount)

    case *packet.Disconnect:
        fmt.Printf("キック/切断されました: %s\n", p.Message)
        return
    }
}

並行呼び出しの禁止

minecraft.ConnReadPacket()単一の goroutine からのみ呼び出す必要があります(同時に複数の goroutine から呼ぶことは禁止されています)。一方、WritePacket() は複数の goroutine から同時に呼び出しても安全です。


パケットの送信 (WritePacket)

パケットを送信したいときは、対象のパケット構造体をインスタンス化して conn.WritePacket(pk) を呼び出します。

例: チャットメッセージの送信

go
// サーバーへチャットを送信 (クライアント側)
err := conn.WritePacket(&packet.Text{
    TextType:    packet.TextTypeChat,
    NeedsTranslation: false,
    SourceName:  conn.IdentityData().DisplayName,
    Message:     "こんにちは!gophertunnelから接続しています",
})

例: コマンドの実行

go
// サーバーへコマンドリクエストを送信
err := conn.WritePacket(&packet.CommandRequest{
    CommandLine: "/time set day",
    CommandOrigin: protocol.CommandOrigin{
        Origin: protocol.CommandOriginPlayer,
        UUID:   uuid.MustParse(conn.IdentityData().Identity),
    },
    Internal: false,
})

例: プレイヤーの移動

go
// 自身の位置をサーバーに報告
err := conn.WritePacket(&packet.MovePlayer{
    EntityRuntimeID: conn.GameData().EntityRuntimeID,
    Position:        mgl32.Vec3{100, 64, 200},
    Pitch:           0,
    Yaw:             90,
    HeadYaw:         90,
    Mode:            packet.MoveModeNormal,
    OnGround:        true,
})

代表的なパケット一覧

パケット名定数 ID用途・送受信方向
packet.TextIDTextチャット、トースト、システム通知(双方向)
packet.MovePlayerIDMovePlayerプレイヤーの位置・向き・接地状態(双方向)
packet.LevelChunkIDLevelChunkチャンクブロックデータ・バイオームデータ(Server -> Client)
packet.InventoryTransactionIDInventoryTransactionアイテム使用、インベントリ移動、ブロック配置(Client -> Server)
packet.PlayerActionIDPlayerActionスニーク、ジャンプ、ブロック採掘開始/中止(Client -> Server)
packet.CommandRequestIDCommandRequest/ コマンドの実行要求(Client -> Server)
packet.DisconnectIDDisconnect理由付き切断メッセージ(Server -> Client)

Released under the MIT License.