注目キーワード

【革命】Minecraft内でAIを呼び出す方法を徹底解説 – WebSocketServerの概要【マイクラ統合版】

はじめに

近年、AI技術の発展により、私たちの生活は大きく変わりつつあります。特に、「ChatGPT」をはじめとする生成系AIが各所に与えた影響は計り知れません。これは、ゲームの世界、とりわけ「マインクラフト」においても例外ではないのです。

WebSocketServer

そもそも、マインクラフトは単なるブロックゲームではありません。実は統合版には「WebSocketServer」と呼ばれる外部のウェブサーバーとマインクラフトとの間で通信を行うシステムが存在し、これを使えば従来のScriptAPIではできなかった様々なことが可能になるのです。AIの導入はその一つです。

というのも、従来のScriptAPIでは完全に外部のモジュールをimportすることは不可能でした。従って、AIの導入といった外部のモジュールを必要とする機能を実装するためには、このように新たな方法を用いる必要があるわけですね。

ここでは、このWebSocketServerを用いてゲーム内のチャット欄を監視し、それに対してサーバー側でAIの返答を用意することでマインクラフトにAIを導入していきます。

本記事を読めばできること

本記事を読むことで、ゲーム内にAIを導入し、チャット欄を用いてAIと会話ができるようなチャットBOTを作成できるようになります。さらに、これを応用することでプレイヤーの意図をくみ取って自動で適切なコマンドを返すBOTも作ることができます(別記事)。これによって、ゲームプレイの質の向上に資することを目的とします。

Youtube動画

本記事の内容について解説したYoutube動画もありますので、こちらを併せてご覧いただくとより理解が深まると思います。

概要

まず、必要なものがいくつかありますので、そちらのインストールから始めましょう。

必要なソフト名 ダウンロード先 備考
Minecraft amazon 言わずと知れたゲームソフト。Win10版が望ましい。
vscode https://code.visualstudio.com/ コードの記述に特化したテキストエディタ(無料)。既に別のテキストエディタを持っている場合は不要。
node.js https://nodejs.org/ javascriptをパソコン上で実行するためのツール(無料)。ダウンロード後、msiファイルを実行。

今回はjavascriptでサーバーを構築するのでnode.jsは必須ですが、PythonやCといったほかの言語で構築する場合は不要です。

準備

それが完了したら、任意の作業フォルダを作成し、その中にmain.jsといったスクリプト用のファイルを作成します。その後、当該フォルダ内でターミナルを開いてそのパスに移動し、「npm init」と入力して初期化を行います。

それが完了したら、「npm install ws uuid」と入力し、2つのモジュールをインストールします。

これで準備は完了しました。これらが終了すると、フォルダ内に多数のファイルが生成されていることが分かると思います。

スクリプト

まずは、WebSocketServerの挙動確認をしてみましょう。まずは、以下のようなスクリプトを書いてみます(Javascriptの場合)。

const WebSocket = require("ws");
const uuid = require("uuid");

const WebSocketServer = new WebSocket.Server({
    port: 19131
})

WebSocketServer.on("connection", socket => {
    console.log("接続されました");
}

その後、コマンドプロンプトを起動し、管理者権限で以下のコマンドを実行します。

CheckNetIsolation.exe LoopbackExempt -a -n="Microsoft.MinecraftUWP_8wekyb3d8bbwe"

それが完了したら、先ほどのスクリプトをF5キーを押して実行し、マインクラフトを起動します。ゲーム内でチートをオンにし、「connect localhost:19131」というコマンドを入力しましょう。

すると無事サーバーに接続できたことが分かると思います。以下のように「接続されました」というメッセージが表示されていればOKです。

チャット欄を監視

続いて、チャット欄を監視し、チャット欄に何らかのメッセージが入力されたときに「say hello」というコマンドを実行するスクリプトを書いていきましょう。

const WebSocket = require("ws");
const uuid = require("uuid");

const WebSocketServer = new WebSocket.Server({
    port: 19131
})

WebSocketServer.on("connection", socket => {
    console.log("接続されました");

    const subscribeMessage = {
        header: {
            version : 1,
            requestId: uuid.v4(),
            messageType: "commandRequest",
            messagePurpose: "subscribe",
        },
        body : {
            eventName : "PlayerMessage"
        }
    }
    socket.send(JSON.stringify(subscribeMessage));

    socket.on("message", function(rawData , isBinary){
        const data = JSON.parse(rawData);
        const runCommand = {
            header:{
                version : 1,
                requestId: uuid.v4(),
                messageType: "commandResponse",
                messagePurpose: "commandRequest"
            },
            body : {
                version : 1,
                origin : {
                    type : "player"
                }
            }
        }
        runCommand.body.commandLine = "say hello!"; //ここでコマンドの内容を定義
        socket.send(JSON.stringify(runCommand));
    })
})

runCommandオブジェクトのbody部のcommandLineを定義することで任意のコマンドの実行が可能です。これとsend関数を用いてマイクラ側にコマンドの実行を命令しています。

同じようにこれを動かしてワールドを起動したのち、何らかのメッセージを入力すると、「say hello」が自動で実行されることが分かるでしょうか。

AIの呼び出し

最後に、これらを組み合わせてチャットの内容に応じてAIの返答を取得し、それをtellraw(またはsay)コマンドを用いてマイクラ側に出力していきましょう。そのために、まずAIを呼び出す関数を定義します:

async function open_ai(content) {
    let error_log = 0;
    if(content == ""){
        error_log = 2;
    }

    if (error_log == 0){
        const apiKey = "APIキー"; // ここにChatGPTのAPIキーを入力してください

        const response = await fetch('https://api.openai.com/v1/chat/completions', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
                'Authorization': `Bearer ${apiKey}`
            },
            body: JSON.stringify({
                model: "gpt-4o-mini", 
                messages: [
                    {
                        role: "system",
                        content: 'You are an advisor for the game "Minecraft." Provide brief information about adventures in up to 5 lines.'
                    },
                    {
                        role: "user",
                        content: content
                    }
                ]
            })
        });

        if (!response.ok) {
            throw new Error(`Error: ${response.statusText}`);
            return `エラーが発生しました。${response.statusText}`;
        }

        const data = await response.json();
        const resultTexts = data.choices[0].message.content;       
        return resultTexts;
    } else if (error_log == 2) {
        return "テキストを入力してください";
    }
}

これはプロンプトを引数として、AIの返答を取得する関数です。なお、この関数の実行にあたってはAPIキーが必要ですので、予め準備しておきましょう。このAPIキーを、定数apiKeyに格納します。

APIキーの取得方法

以下のOpenAIのサイトにアクセスして、APIキーを取得します。その後、クレジットカード情報を登録し、完了です(現在、5ドル分が無料で付与されますので、それを超過した分のみが引き落とされます)。

https://platform.openai.com/api-keys

その後、例えば以下のようにしてAIの返答をコマンドラインに格納します:

        if (String(data.body.message).startsWith("?")){
            open_ai(data.body.message).then(response => {
                console.log(response);
                response = String(response).replace(/。/g, "。\n");
                let lines = String(response).split("\n");
                lines.forEach(line => line.split("。"));
                for (let i = 0; i < lines.length;i++){
                    runCommand.body.commandLine = `tellraw ${String(data.body.sender)} {"rawtext":[{"text":"${lines[i]}"}]}`
                    socket.send(JSON.stringify(runCommand));
                }
            })         
        }

ここではメッセージが?で始まるものをプロンプトとみなして先ほど作成した関数を発火させ、得た返答を点や改行で分割してそれぞれをtellrawによって出力しています。というのも、tellrawは長すぎる文章を出力できないようなので、このようにしています。

おしまい

コードの全文は以下です(APIキーはご自身のものを入力してください):

const WebSocket = require("ws");
const uuid = require("uuid");

async function open_ai(content) {
    let error_log = 0;
    if(content == ""){
        error_log = 2;
    }

    if (error_log == 0){
        const apiKey = "APIキーを入力してください";

        const response = await fetch('https://api.openai.com/v1/chat/completions', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
                'Authorization': `Bearer ${apiKey}`
            },
            body: JSON.stringify({
                model: "gpt-4o", 
                messages: [
                    {
                        role: "system",
                        content: 'You are an advisor for the game "Minecraft." Provide brief information about adventures in up to 5 lines.'
                    },
                    {
                        role: "user",
                        content: content
                    }
                ]
            })
        });

        if (!response.ok) {
            throw new Error(`Error: ${response.statusText}`);
            return `エラーが発生しました。${response.statusText}`;
        }

        const data = await response.json();
        const resultTexts = data.choices[0].message.content;       
        return resultTexts;
    } else if (error_log == 2) {
        return "テキストを入力してください";
    }
}

const WebSocketServer = new WebSocket.Server({
    port: 19131
})

WebSocketServer.on("connection", socket => {
    console.log("接続されました");

    const subscribeMessage = {
        header: {
            version : 1,
            requestId: uuid.v4(),
            messageType: "commandRequest",
            messagePurpose: "subscribe",
        },
        body : {
            eventName : "PlayerMessage"
        }
    }
    socket.send(JSON.stringify(subscribeMessage));

    socket.on("message", function(rawData , isBinary){
        const data = JSON.parse(rawData);
        const runCommand = {
            header:{
                version : 1,
                requestId: uuid.v4(),
                messageType: "commandResponse",
                messagePurpose: "commandRequest"
            },
            body : {
                version : 1,
                origin : {
                    type : "player"
                }
            }
        }
        if (String(data.body.message).startsWith("?")){
            open_ai(data.body.message).then(response => {
                console.log(response);
                response = String(response).replace(/。/g, "。\n");
                let lines = String(response).split("\n");
                lines.forEach(line => line.split("。"));
                for (let i = 0; i < lines.length;i++){
                    runCommand.body.commandLine = `tellraw ${String(data.body.sender)} {"rawtext":[{"text":"${lines[i]}"}]}`
                    socket.send(JSON.stringify(runCommand));
                }
            })         
        }
    })
})

本記事では、マイクラにAIを導入する方法について解説しました。総じて、マインクラフトにAIを導入することは、プレイヤーのゲーム体験を豊かにし、コミュニティを活性化させる大きな可能性を秘めています。AIチャットボットはただのツールではなく、プレイヤーの頼れるパートナーとなります。この新しい技術を活用することで、マインクラフトはさらに面白く、奥深いゲーム世界となるでしょう。ぜひ、皆さんもこのAIの世界に触れ、マインクラフトの新たな楽しみ方を体験してみてください。

参考動画

制作にあたってはこちらの動画を参考にさせていただきました。

最新情報をチェックしよう!