Amazon GameLift
開発者ガイド (Version )

RealTime スクリプトの作成

ゲームに リアルタイムサーバー を使用するには、リアルタイムサーバー のフリートを設定してオプションでカスタマイズするためのスクリプト (JavaScript コードの形式) を提供する必要があります。このトピックでは、RealTime スクリプトを作成するための主なステップについて説明します。スクリプトの準備ができたら、Amazon GameLift サービスにアップロードし、それを使用してフリートを作成します (「リアルタイムサーバー スクリプトを Amazon GameLift にアップロードする」を参照)。

リアルタイムサーバー で使用するスクリプトを準備するには、RealTime スクリプトに以下の機能を追加します。

ゲームセッションのライフサイクルを管理する (必須)

最低限、RealTime スクリプトに Init() 関数を含めて、この関数で、RealTime サーバーがゲームセッションの開始準備をするようにします。新しいゲームセッションを引き続きフリートで開始できるように、ゲームセッションを終了する方法も提供することを強くお勧めします。

Init() コールバック関数が呼び出されると、RealTime セッションオブジェクトを渡されます。このセッションオブジェクトには、RealTime サーバーのインターフェイスが含まれています。このインターフェイスの詳細については、「リアルタイムサーバー インターフェイス」を参照してください。

ゲームセッションを正常に終了するには、スクリプトは RealTime サーバーの session.processEnding 関数も呼び出す必要があります。この関数には、セッションをいつ終了するかを決定するための何らかのメカニズムが必要です。スクリプト例のコードは、プレイヤーの接続を確認し、指定された時間内にセッションに接続しているプレイヤーがいなければゲームセッションの終了をトリガーする、シンプルなメカニズムを示しています。

基本的な設定の リアルタイムサーバー -- サーバープロセスの開始と終了 -- 基本的にステートレスな中継サーバーとして動作します。RealTime サーバーは、ゲームに接続されているゲームクライアント間でメッセージとゲームデータを中継しますが、データを処理したりロジックを実行したりするための独立したアクションは行いません。ゲームのニーズに応じて、ゲームイベントなどのメカニズムによってトリガーされるゲームロジックをオプションで追加することができます。

サーバー側のゲームロジックを追加する (オプション)

オプションで RealTime スクリプトにゲームロジックを追加できます。たとえば、以下のいずれかまたはすべてを実行できます。スクリプト例のコードは実例を提供しています。「Amazon GameLift リアルタイムサーバー スクリプトリファレンス」を参照してください。

  • イベント駆動型のロジックを追加する。 クライアントサーバーイベントに応答するためのコールバック関数を実装します。コールバックの完全なリストについては、「」を参照してください。

  • サーバーにメッセージを送信してロジックを起動する。 ゲームクライアントからサーバーに送信されるメッセージ専用に一連のオペレーションコードを作成し、受信を処理するための関数を追加します。コールバック onMessagegameMessage を使用してメッセージの内容を解析します (「gameMessage.opcode」を参照)。

リアルタイムサーバー スクリプト例

この例は、リアルタイムサーバー をデプロイするために必要な基本スクリプトといくつかのカスタムロジックを示しています。また、必要な Init() 関数を含み、プレイヤーの接続のない期間に基づいてゲームセッションの終了をトリガーするタイマーメカニズムを使用しています。さらに、コールバックの実装など、カスタムロジック用のフックもいくつか含まれています。

// Example Realtime Server Script 'use strict'; // Example override configuration const configuration = { pingIntervalTime: 30000 }; // Timing mechanism used to trigger end of game session. Defines how long, in milliseconds, between each tick in the example tick loop const tickTime = 1000; // Defines how to long to wait in Seconds before beginning early termination check in the example tick loop const minimumElapsedTime = 120; var session; // The Realtime server session object var logger; // Log at appropriate level via .info(), .warn(), .error(), .debug() var startTime; // Records the time the process started var activePlayers = 0; // Records the number of connected players var onProcessStartedCalled = false; // Record if onProcessStarted has been called // Example custom op codes for user-defined messages // Any positive op code number can be defined here. These should match your client code. const OP_CODE_CUSTOM_OP1 = 111; const OP_CODE_CUSTOM_OP1_REPLY = 112; const OP_CODE_PLAYER_ACCEPTED = 113; const OP_CODE_DISCONNECT_NOTIFICATION = 114; // Example groups for user defined groups // Any positive group number can be defined here. These should match your client code. const RED_TEAM_GROUP = 1; const BLUE_TEAM_GROUP = 2; // Called when game server is initialized, passed server's object of current session function init(rtSession) { session = rtSession; logger = session.getLogger(); } // On Process Started is called when the process has begun and we need to perform any // bootstrapping. This is where the developer should insert any code to prepare // the process to be able to host a game session, for example load some settings or set state // // Return true if the process has been appropriately prepared and it is okay to invoke the // GameLift ProcessReady() call. function onProcessStarted(args) { onProcessStartedCalled = true; logger.info("Starting process with args: " + args); logger.info("Ready to host games..."); return true; } // Called when a new game session is started on the process function onStartGameSession(gameSession) { // Complete any game session set-up // Set up an example tick loop to perform server initiated actions startTime = getTimeInS(); tickLoop(); } // Handle process termination if the process is being terminated by GameLift // You do not need to call ProcessEnding here function onProcessTerminate() { // Perform any clean up } // Return true if the process is healthy function onHealthCheck() { return true; } // On Player Connect is called when a player has passed initial validation // Return true if player should connect, false to reject function onPlayerConnect(connectMsg) { // Perform any validation needed for connectMsg.payload, connectMsg.peerId return true; } // Called when a Player is accepted into the game function onPlayerAccepted(player) { // This player was accepted -- let's send them a message const msg = session.newTextGameMessage(OP_CODE_PLAYER_ACCEPTED, player.peerId, "Peer " + player.peerId + " accepted"); session.sendReliableMessage(msg, player.peerId); activePlayers++; } // On Player Disconnect is called when a player has left or been forcibly terminated // Is only called for players that actually connected to the server and not those rejected by validation // This is called before the player is removed from the player list function onPlayerDisconnect(peerId) { // send a message to each remaining player letting them know about the disconnect const outMessage = session.newTextGameMessage(OP_CODE_DISCONNECT_NOTIFICATION, session.getServerId(), "Peer " + peerId + " disconnected"); session.getPlayers().forEach((player, playerId) => { if (playerId != peerId) { session.sendReliableMessage(outMessage, peerId); } }); activePlayers--; } // Handle a message to the server function onMessage(gameMessage) { switch (gameMessage.opCode) { case OP_CODE_CUSTOM_OP1: { // do operation 1 with gameMessage.payload for example sendToGroup const outMessage = session.newTextGameMessage(OP_CODE_CUSTOM_OP1_REPLY, session.getServerId(), gameMessage.payload); session.sendGroupMessage(outMessage, RED_TEAM_GROUP); break; } } } // Return true if the send should be allowed function onSendToPlayer(gameMessage) { // This example rejects any payloads containing "Reject" return (!gameMessage.getPayloadAsText().includes("Reject")); } // Return true if the send to group should be allowed // Use gameMessage.getPayloadAsText() to get the message contents function onSendToGroup(gameMessage) { return true; } // Return true if the player is allowed to join the group function onPlayerJoinGroup(groupId, peerId) { return true; } // Return true if the player is allowed to leave the group function onPlayerLeaveGroup(groupId, peerId) { return true; } // A simple tick loop example // Checks to see if a minimum amount of time has passed before seeing if the game has ended async function tickLoop() { const elapsedTime = getTimeInS() - startTime; logger.info("Tick... " + elapsedTime + " activePlayers: " + activePlayers); // In Tick loop - see if all players have left early after a minimum period of time has passed // Call processEnding() to terminate the process and quit if ( (activePlayers == 0) && (elapsedTime > minimumElapsedTime)) { logger.info("All players disconnected. Ending game"); const outcome = await session.processEnding(); logger.info("Completed process ending with: " + outcome); process.exit(0); } else { setTimeout(tickLoop, tickTime); } } // Calculates the current time in seconds function getTimeInS() { return Math.round(new Date().getTime()/1000); } exports.ssExports = { configuration: configuration, init: init, onProcessStarted: onProcessStarted, onMessage: onMessage, onPlayerConnect: onPlayerConnect, onPlayerAccepted: onPlayerAccepted, onPlayerDisconnect: onPlayerDisconnect, onSendToPlayer: onSendToPlayer, onSendToGroup: onSendToGroup, onPlayerJoinGroup: onPlayerJoinGroup, onPlayerLeaveGroup: onPlayerLeaveGroup, onStartGameSession: onStartGameSession, onProcessTerminate: onProcessTerminate, onHealthCheck: onHealthCheck };