【階段チェア】

はじめに

以下のように階段ブロックの淵をスニーク移動すると階段に座れます。


座れるブロックは設定ファイルで変更できるようにしています。
現在の設定で座れるブロックは以下の通り。

※特に基準はありませんが、こちら側の任意で現在5ブロックだけ選抜登録しています。

例えば座れるブロックを追加すると以下のようにガラスブロックでも座れます。


※本機能を有効にするためにはカスタムビヘイビアパックとリソースパックが必要になります。適用方法については▶ビヘイビアパック適用その2のページをご覧ください。

サーバー側の実装

今回はスニークイベントが発生した時に送信される"PlayerTravelled"というサブスクライブイベントを使用しています。
受信したイベントデータのtravelMethodというパラメータ値(=7:スニーク)をみて判断しています。

※サブスクライブイベントの処理内容については>> こちらでご紹介しています。
スニーク時に発生するイベントデータの形式
{
    "body":
    {
        "isUnderwater":<ブール値>,
        "metersTravelled":<数字>,
        "newBiome":<数字>,
        "player":
        {
            "color":<16進数?>,
            "dimension":<数字>,
            "id":<数字>,
            "name":<文字列>,
            "position":
            {
                "x":<数字>,
                "y":<数字>,
                "z":<数字>
            },
            "type":<文字列>,
            "variant":<数字>,
            "yRot":<数字>
        },
        "travelMethod":7(スニーク)
    },
    "header":
    {
        "eventName":"PlayerTravelled",
        "messagePurpose":<文字列>,
        "version":<数字>
    }
}
                    

今回はスニーク移動を検知した際にプレイヤーの真下にあるブロックが座れるブロックなのかどうかを判断するため、以下のフレームワーク上のマインクラフト設定ファイルをご用意しています。
setting/minecraft.php
return [
    ・
    ・
    ・
    /**
     * @var array 座れる階段ブロック(複数指定可能)
     */
    'stairs_ids' =>
    [
        'stone_stairs',     // 丸石
        'oak_stairs',       // オーク
        'cherry_stairs',    // サクラ
        'brick_stairs',     // レンガ
        'quartz_stairs'     // クォーツ
    ]
];
                    

スニーク検知後の処理の流れは以下の通りです。
1)プレイヤー真下のブロックが対象のブロックかを検査
サーバーからtestforblockコマンドを発行して検査
2)プレイヤーが搭乗可能なダミーエンティティを召喚
サーバーからsummonコマンドを発行
3)プレイヤーをダミーエンティティに搭乗させる
サーバーからrideコマンドを発行
testforblockコマンドのレスポンス
{
    "body":
    {
        "matches":true(一致) or false(不一致),
        "position":{"x":<X座標>,"y":<Y座標>,"z":<Z座標>},
        "statusCode":<数字>,
        "statusMessage":<文字列>
    },
    "header":
    {
        "messagePurpose":<文字列>,
        "requestId":<UUID>,
        "version":<数字>
    }
}
                    

上記のmatchesの項目を参照して一致しているかどうかを判断しています。
testforblock発行時のレスポンスの内容は▶コマンド送信機能を使って確認しています。


キューとステータスUNITの登録


イベントを処理するための任意のコマンド名を以下のファイルへ定義します。
app/CommandUnits/CommandQueueEnumForMinecraft.php
case CHAIR = 'chair';                   // スニークイベント検知時
case CHAIR_STANDUP = 'chair-standup';   // 階段チェアを降りた時
                    

コマンド名を以下の場所へ追加して利用可能にします。
app/CommandUnits/CommandForMinecraft.php
protected const QUEUE_LIST = [
    CommandQueueEnumForMinecraft::CHAIR->value,         // スニークイベント検知時
    CommandQueueEnumForMinecraft::CHAIR_STANDUP->value  // 階段チェアを降りた時
];
                    

コマンド名と処理(関数)の関係を以下のメソッドへ追加して紐づけを行います。
app/CommandUnits/CommandForMinecraft.php
public function getUnitList(string $p_que): array
{
    $ret = [];
    ・
    ・
    ・
    if($p_que === CommandQueueEnumForMinecraft::CHAIR->value)
    {
        $ret[] = [
            'status' => CommandStatusEnumForMinecraft::START->value,
            'unit' => $this->getMinecraftChairStart()
        ];
        $ret[] = [
            'status' => CommandStatusEnumForMinecraft::CHAIR_RESPONSE->value,
            'unit' => $this->getMinecraftChairResponse()
        ];
    }
    else
    if($p_que === CommandQueueEnumForMinecraft::CHAIR_STANDUP->value)
    {
        $ret[] = [
            'status' => CommandStatusEnumForMinecraft::START->value,
            'unit' => $this->getMinecraftChairStandupStart()
        ];
    }

    return $ret;
}
                    

新規実装箇所


受信したイベントデータをコマンド名へ変換する処理を以下のコマンドディスパッチャーへ追加します。
app/InitClass/InitForMinecraft.php
public function getCommandDispatcher()
{
    return function(ParameterForMinecraft $p_param, $p_dat): ?string
    {
        $minecraft = $p_param->isMinecraft();
        if($minecraft === true)
        {
            ・
            ・
            ・
            if(isset($p_dat['data']['header']['eventName']) && $p_dat['data']['header']['eventName'] === 'PlayerTravelled')
            {
                ・
                ・
                ・
                if($p_dat['data']['body']['travelMethod'] === 7)
                {
                    // 階段ブロックのリストを取得
                    $ids = config('minecraft.stairs_ids');

                    // 処理対象のインデックスを設定
                    $p_param->setTempBuff(['stairs_idx' => count($ids) - 1]);

                    return CommandQueueEnumForMinecraft::CHAIR->value;
                }

                if($p_dat['data']['body']['travelMethod'] === 0)
                {
                    // 階段チェア着席フラグの取得
                    $flg = $p_param->getTempBuff(['chair_flag']);

                    if(isset($flg['chair_flag']) && $flg['chair_flag'] === true)
                    {
                        return CommandQueueEnumForMinecraft::CHAIR_STANDUP->value;
                    }
                }
                ・
                ・
                ・
            }
            ・
            ・
            ・
        }
    }
}
                    

コマンド名に紐づけた以下の処理(関数)を実装します。
app/CommandUnits/CommandForMinecraft.php
//--------------------------------------------------------------------------
// 以降はステータスUNITの定義("CHAIR"キュー)
//--------------------------------------------------------------------------
                    
protected function getMinecraftChairStart()
{
    return function(ParameterForMinecraft $p_param): ?string
    {
        $p_param->logWriter('debug', ['MINECRAFT CHAIR:START' => 'START']);

        // 階段ブロックIDのインデックスを取得
        $idx = $p_param->getTempBuff(['stairs_idx']);

        // 階段ブロックのリストを取得
        $ids = config('minecraft.stairs_ids');

        // コマンド送信
        $cmd_data = $p_param->getCommandDataForStairsTest($ids[$idx['stairs_idx']]);
        $data =
        [
            'data' => $cmd_data
        ];
        $p_param->setSendStack($data);

        // ディスパッチャー強制
        $p_param->setForcedDispatcher(true);

        return CommandStatusEnumForMinecraft::CHAIR_RESPONSE->value;
    };
}

protected function getMinecraftChairResponse()
{
    return function(ParameterForMinecraft $p_param): ?string
    {
        $p_param->logWriter('debug', ['MINECRAFT CHAIR:CHAIR_RESPONSE' => 'START']);

        // 現在のステータスを取得
        $sta = $p_param->getStatusName();

        // 受信データの取得
        $rcv = $p_param->getRecvData();
        if($rcv === null)
        {
            // ディスパッチャー強制
            $p_param->setForcedDispatcher(true);
            return $sta;
        }

        // 階段ブロックが不一致の場合
        if($rcv['data']['body']['matches'] === false)
        {
            // 階段ブロックIDのインデックスを取得
            $idx = $p_param->getTempBuff(['stairs_idx']);

            // 一致するブロックが見つからなかった
            if($idx['stairs_idx'] <= 0)
            {
                return null;
            }
            $idx['stairs_idx']--;

            // 階段ブロックのリストを取得
            $ids = config('minecraft.stairs_ids');

            // コマンド送信
            $cmd_data = $p_param->getCommandDataForStairsTest($ids[$idx['stairs_idx']]);
            $data =
            [
                'data' => $cmd_data
            ];
            $p_param->setSendStack($data);

            // 階段ブロックIDの次のインデックスを設定
            $p_param->setTempBuff(['stairs_idx' => $idx['stairs_idx']]);

            return $sta;
        }

        // 階段ブロックの座標を取得
        $x = $rcv['data']['body']['position']['x'];
        $y = $rcv['data']['body']['position']['y'];
        $z = $rcv['data']['body']['position']['z'];

        // コマンド送信(sitエンティティのデスポーン)
        $cmd_data = $p_param->getCommandDataForDespawnSit($x, $y, $z);
        $data =
        [
            'data' => $cmd_data
        ];
        $p_param->setSendStack($data);

        // コマンド送信(体力ゲージ非表示)
        $cmd_data = $p_param->getCommandDataForGaugeHide();
        $data =
        [
            'data' => $cmd_data
        ];
        $p_param->setSendStack($data);

        // コマンド送信(sitエンティティの召喚)
        $cmd_data = $p_param->getCommandDataForSummonSit($x, $y, $z);
        $data =
        [
            'data' => $cmd_data
        ];
        $p_param->setSendStack($data);

        // コマンド送信(プレイヤーの搭乗)
        $cmd_data = $p_param->getCommandDataForRidePlayer($x, $y, $z);
        $data =
        [
            'data' => $cmd_data
        ];
        $p_param->setSendStack($data);

        // 階段ブロックIDのインデックスクリア
        $p_param->setTempBuff(['stairs_idx' => null]);

        // 階段チェア着席フラグの設定
        $p_param->setTempBuff(['chair_flag' => true]);

        return null;
    };
}

//--------------------------------------------------------------------------
// 以降はステータスUNITの定義("CHAIR_STANDUP"キュー)
//--------------------------------------------------------------------------

protected function getMinecraftChairStandupStart()
{
    return function(ParameterForMinecraft $p_param): ?string
    {
        $p_param->logWriter('debug', ['MINECRAFT CHAIR_STANDUP:START' => 'START']);

        // コマンド送信(体力ゲージ非表示)
        $cmd_data = $p_param->getCommandDataForGaugeShow();
        $data =
        [
            'data' => $cmd_data
        ];
        $p_param->setSendStack($data);

        // 階段チェア着席フラグのクリア
        $p_param->setTempBuff(['chair_flag' => false]);

        return null;
    };
}