Admin.js

admin ページにアクセスするには、index.html の Admin をクリックします。ユーザー名とパスワード (どちらも admin) を入力後、プレイヤーテーブルを操作して、ゲームコンテンツを操作できます。 web-admin.js のドキュメントに、アクションに使用するコードがすべて含まれています。

このファイルには 1 つの class があります。

class AdminController

AdminController class には、ストリームへの接続、適切な情報表示、ゲームへのコマンド送信を行うすべてのメソッドが含まれています。

class 以外では、 AdminController のインスタンスを作成し、それに対して start() を実行してストリームへの接続を開始します。

let admin = new AdminController("video_player_hidden");
admin.start();

以下のセクションでは、AdminController class の内容について説明します。

start

start()

start メソッドがサービスへの接続を開始します。接続が適切に実行できると、検出した IChannelJoinResponse()onChannelJoin を実行します。この処理は、 web.js ファイルと同一のものです。

    // Starts the connection to the services
    start() {
        fetch("/api/public/channels/join", {
                method: "POST"
            })
            .then(data => data.json())
            .then(res => this.onChannelJoin(res))
            .catch(error => genvid.error(`Can't get the stream info: ${error}`));
    }

onChannelJoin

onChannelJoin(joinRep)

onChannelJoin メソッドは、サービスとチャネルに接続後、Genvid クライアントを作成します。このプロセスで検出された情報と、class 作成プロセスで送信された videoPlayerId を使用してクライアントを作成します (これは AdminController class の引数です)。この処理は web.js ファイルと同様のものですが、こちらはメソッドを必要としません。

その後、この class に特定のイベントを関連付ける必要があります。

onStreamsReceived
ストリームコンテンツを受信した時にトリガされます (ゲームデータの取得に使用)

次に、 start() メソッドでクライアントを開始します。

    // Creates the genvid Client and the function listening to it
    onChannelJoin(joinRep) {
        this.client = genvid.createGenvidClient(joinRep.info, joinRep.uri, joinRep.token, this.videoPlayerId);
        this.client.onStreamsReceived((streams) => {
            this.onStreamsReceived(streams);
        });
        this.client.start();
    }

onStreamsReceived

onStreamsReceived(dataStreams)

onStreamsReceived` メソッドは、常に更新を必要とする様々なタスクを実行します。Genvid クライアント用に作成したストリームとフレームごとに、Web サイトが受信した際にコールします。この処理は、 web.js ファイルの中で全く同じ方法で行います。

  1. まず、プレイヤーテーブルが作成されていないことを確認します。

  2. さまざまな処理を行う前に、ゲームデータを取得してから、そのデータが有効であることを確認する必要があります。

  3. データの妥当性が確認できたら、データ (キューブ) からオブジェクトリストを取得し、JSON データを変換します。

  4. オブジェクトごとにループ処理を作成し、それぞれにプレイヤーテーブルを作成します。

  5. プレイヤーテーブルのボタンクリックを検知するためのイベントリスナーも追加します。

  6. ループ処理後に、シーンとカメラ変更のための 2 つのボタンを作成します。

  7. シーンとカメラ変更ボタンのクリックを検知するためのイベントリスナーを追加します。

    onStreamsReceived(dataStreams) {
        if (this.playerTableSetup) {
            return;
        } else {
            this.playerTableSetup = true;

            for (const stream of dataStreams.streams) {
                const datas = stream.frames[0];

                if (datas && stream.id === "GameData") {

                    const cubeData = JSON.parse(datas.data);

                    this.canChangeCamera = !!cubeData.camera; // will be used by changeCamera
                    this.canChangeScene = !!cubeData.scene; // will be used by changeScene


                    let cubeControlPanel = document.getElementById("admin_prototype_panel");
                    for (let cube of cubeData.cubes) {

                        let cubeControlPanelClone = cubeControlPanel.cloneNode(true);
                        cubeControlPanelClone.id = cube.name;
                        cubeControlPanelClone.querySelector(".table_name").innerText = cube.name;
                        document.querySelector(".admin_table_section").append(cubeControlPanelClone);
                        let upButton = cubeControlPanelClone.querySelector(".upDirection");
                        upButton.addEventListener("click", () => {
                            this.setDirection(cube.name, 0, 1);
                        }, false);

                        let downButton = cubeControlPanelClone.querySelector(".downDirection");
                        downButton.addEventListener("click", () => {
                            this.setDirection(cube.name, 0, -1);
                        }, false);

                        let leftButton = cubeControlPanelClone.querySelector(".leftDirection");
                        leftButton.addEventListener("click", () => {
                            this.setDirection(cube.name, -1, 0);
                        }, false);

                        let rightButton = cubeControlPanelClone.querySelector(".rightDirection");
                        rightButton.addEventListener("click", () => {
                            this.setDirection(cube.name, 1, 0);
                        }, false);

                        let resetButton = cubeControlPanelClone.querySelector(".reset");
                        resetButton.addEventListener("click", () => {
                            this.reset(cube.name);
                        }, false);

                        let slowerButton = cubeControlPanelClone.querySelector(".slower");
                        slowerButton.addEventListener("click", () => {
                            this.changeSpeed(cube.name, 0.8);
                        }, false);

                        let fasterButton = cubeControlPanelClone.querySelector(".faster");
                        fasterButton.addEventListener("click", () => {
                            this.changeSpeed(cube.name, 1.25);
                        }, false);
                    }

                    cubeControlPanel.remove();

                    let cameraButton = document.getElementsByClassName("cameraChange")[0];
                    if(this.canChangeCamera) {
                        cameraButton.addEventListener("click", () => {
                            this.changeCamera();
                        }, false);
                    } else {cameraButton.setAttribute("disabled", "");}

                    let sceneButton = document.getElementsByClassName("sceneChange")[0];
                    if (this.canChangeScene) {
                        sceneButton.addEventListener("click", () => {
                            this.changeScene();
                        }, false);
                    } else {sceneButton.setAttribute("disabled", "");}

                    this.client.close(); // This is in order to save bandwidth because we don't need the gamedata anymore.
                }
            }
        }
    }

sendCommands

sendCommands(bodyCommands, successMessage, errorMessage)

sendCommands` メソッドは AdminController クラスのメソッドから受け取ったコマンドをゲームに送信します。表示するすべてのメッセージを displayMessagedisplayErrorMessage メソッドに転送します。

    sendCommands(bodyCommands, successMessage, errorMessage) {
        fetch("/api/admin/commands/game", {
            method: "POST",
            body: JSON.stringify(bodyCommands),
            headers: {
                'Content-Type': 'application/json'
            }
        })
        .then(() => {
            this.displayMessage(successMessage);
        })
        .catch((err) => {
            this.displayErrorMessage(`Failed with error ${err} ${errorMessage}`);
        });
    }

setDirection

setDirection(cubeName, x, z)

setDirection メソッドはゲームにコマンドを送信し、プレイヤーの向きを設定します。プレイヤーの向きをクリックしたときに呼び出します。

    // Changes the direction of the cube
    setDirection(cubeName, x, z) {
        const commands = {
            id: "direction",
            value: `${cubeName}:${x}:0:${z}`
        };
        const successMessage = `Set direction to ${cubeName}:${x}:${z}`;
        const errorMessage = `to set direction to ${cubeName}:${x}:${z}`;

        this.sendCommands(commands, successMessage, errorMessage);
    }

changeSpeed

changeSpeed(cubeName, factor)

changeSpeed メソッドは、ゲームにコマンドを送信し、オブジェクトの低速化、または高速化を指示します。プレイヤーの SlowerFaster をクリックしたときに呼び出します。

    // Changes the speed of the cube
    changeSpeed(cubeName, factor) {
        const commands = {
            id: "speed",
            value: `${cubeName}:${factor}`
        };
        const successMessage = `Speed changed ${cubeName}:${factor}`;
        const errorMessage = `to changeSpeed ${cubeName}:${factor}`;

        this.sendCommands(commands, successMessage, errorMessage);
    }

reset

reset(cubeName)

reset メソッドは、ゲームにコマンドを送信し、オブジェクトを元の位置に戻します。プレイヤーの Reset をクリックしたときに呼び出します。

    // Resets the position of the cube
    reset(cubeName) {
        const commands = {
            id: "reset",
            value: cubeName
        };
        const successMessage = `${cubeName} has been reset`;
        const errorMessage = `to reset ${cubeName}`;

        this.sendCommands(commands, successMessage, errorMessage);
    }

changeCamera

changeCamera()

changeCamera メソッドは、ゲームにコマンドを送信し、現在のカメラを変更します。プレイヤーの Camera change ボタンをクリックしたときに呼び出します。

    changeCamera() {
        const commands = {
            id: "camera",
            value: "change"
        };
        const successMessage = "Camera change done";
        const errorMessage = "to change the camera";

        this.sendCommands(commands, successMessage, errorMessage);
    }

changeScene

changeScene()

changeScene メソッドは、ゲームにコマンドを送信し、現在のシーンを変更します。プレイヤーの Scene change ボタンをクリックしたときに呼び出します。

    changeScene() {
        const commands = {
            id: "scene",
            value: "change"
        };
        const successMessage = "Scene change done";
        const errorMessage = "to change the scene";

        this.sendCommands(commands, successMessage, errorMessage);
    }

displayMessage

displayMessage()

displayMessage メソッドは、実行した操作が正常に行われたことを示すメッセージを表示します。ストリームから適切にデータを取得するかどうかを確認したり、コマンド操作が成功したかどうかを確認する場合に役立ちます。

    displayMessage(message) {
        let messageErrorDiv = document.querySelector("#alert_error_cube");
        messageErrorDiv.style.visibility = "hidden";
        let messageDiv = document.querySelector("#alert_sucess_cube");
        messageDiv.style.visibility = "visible";
        let messageSpan = document.querySelector("#sucess_message_cube");
        messageSpan.textContent = message;
    }

displayErrorMessage

displayErrorMessage()

displayErrorMessage メソッドは、実行した操作のエラーメッセージを表示します。ストリームから適切にデータを取得できなかったことを確認したり、コマンド操作が失敗したかどうかを確認する場合に役立ちます。

    displayErrorMessage(message) {
        let messageDiv = document.querySelector("#alert_sucess_cube");
        messageDiv.style.visibility = "hidden";
        let messageErrorDiv = document.querySelector("#alert_error_cube");
        messageErrorDiv.style.visibility = "visible";
        let messageSpan = document.querySelector("#error_message_cube");
        messageSpan.textContent = message;
    }