本文為英文版的機器翻譯版本,如內容有任何歧義或不一致之處,概以英文版為準。
建立Realtime指令碼
若要將 Amazon GameLift ServersRealtime 用於您的遊戲,您需要提供指令碼 (以某些 JavaScript 程式碼的形式) 來設定和選擇性地自訂 的機群Amazon GameLift ServersRealtime。本主題涵蓋建立 Realtime 指令碼的重要步驟。指令碼準備就緒後,請上傳到 Amazon GameLift Servers 服務,並使用它來建立叢集 (請參閱部署 的指令碼 Amazon GameLift ServersRealtime)。
若要準備指令碼以搭配 使用Amazon GameLift ServersRealtime,請將下列功能新增至您的Realtime指令碼。
管理遊戲工作階段生命週期 (必要)
Realtime 指令碼必須至少包含 Init()
函數,用來準備 Realtime 伺服器啟動遊戲工作階段。我們也強烈建議您提供終止遊戲工作階段的方式,確保新的遊戲工作階段可以繼續在您的叢集上啟動。
Init()
回呼函數在受到呼叫的時候會獲得傳遞一個 Realtime 工作階段物件,其中包含 Realtime 伺服器的界面。請參閱 Amazon GameLift ServersRealtime 介面以取得此界面的詳細資訊。
若要順利結束遊戲工作階段,指令碼也必須呼叫 Realtime 伺服器的 session.processEnding
函數。這需要一些機制,來判斷結束工作階段的時機。指令碼範例程式碼會示範簡易的機制,檢查玩家連線,並在指定的時間長度內沒有任何玩家連線到工作階段時觸發遊戲工作階段的終止。
Amazon GameLift ServersRealtime 具有最基本的組態 - 伺服器程序初始化和終止 - 基本上充當無狀態轉送伺服器。Realtime 伺服器會在連線到遊戲的遊戲用戶端之間轉送訊息和遊戲資料,但不會採取獨立的動作來處理資料或執行邏輯。根據您遊戲的需求,您可以選用地新增遊戲邏輯,由遊戲事件或其他機制觸發。
新增伺服器端遊戲邏輯 (選用)
您可以選用地將遊戲邏輯新增到您的 Realtime 指令碼。例如,您可以執行以下任何或所有的作業:指令碼範例程式碼會提供說明。請參閱 的指令碼參考 Amazon GameLift ServersRealtime。
-
新增事件驅動邏輯。實作回呼函數,以回應用戶端 ‒ 伺服器事件。如需完整的回呼清單,請參閱 的指令碼回呼 Amazon GameLift ServersRealtime。
-
透過傳送訊息至伺服器來觸發邏輯。針對從遊戲用戶端傳送到伺服器的訊息,建立一組特殊操作程式碼,並新增函數來處理接收。使用回呼
onMessage
,並使用gameMessage
界面來剖析訊息內容 (請參閱 gameMessage.opcode)。 -
啟用遊戲邏輯以存取您的其他 AWS 資源。如需詳細資訊,請參閱與機群中的其他 AWS 資源通訊。
-
允許遊戲邏輯存取其執行所在執行個體的機群資訊。如需詳細資訊,請參閱取得Amazon GameLift Servers執行個體的機群資料。
Amazon GameLift ServersRealtime 指令碼範例
此範例說明部署所需的基本指令碼,Amazon GameLift ServersRealtime以及一些自訂邏輯。它包含必要的 Init()
函數,並會使用計時器機制來根據沒有任何玩家連線的時間長度,觸發遊戲工作階段的終止。它還包含一些自訂邏輯的勾點,其中包括一些回呼實作。
// Example Realtime Server Script 'use strict'; // Example override configuration const configuration = { pingIntervalTime: 30000, maxPlayers: 32 }; // 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. // When referring to user-defined groups, "-1" represents all groups, "0" is reserved. 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 Amazon GameLift Servers // 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, playerId); } }); 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 };