創建實時腳本 - Amazon GameLift

本文為英文版的機器翻譯版本,如內容有任何歧義或不一致之處,概以英文版為準。

創建實時腳本

要在遊戲中使用實時服務器,您需要提供腳本(以某些JavaScript代碼的形式)來配置和可選地自定義實時服務器群。本主題涵蓋建立即時指令碼的關鍵步驟。指令碼準備就緒後,將其上傳到 Amazon GameLift 服務並使用它來建立叢集 (請參閱將實時服務器腳本上傳到亞馬遜 GameLift)。

要準備腳本以與實時服務器一起使用,請將以下功能添加到實時腳本中。

管理遊戲工作階段生命週期 (必要)

Realtime 腳本至少必須包含該Init()函數,該函數可準備實時服務器以啟動遊戲會話。我們也強烈建議您提供終止遊戲工作階段的方式,確保新的遊戲工作階段可以繼續在您的叢集上啟動。

Init()調用時,回調函數會傳遞一個實時會話對象,該對象包含實時服務器的接口。請參閱 實時服務器接口以取得此界面的詳細資訊。

為了正常結束遊戲會話,腳本還必須調用實時服務器的session.processEnding函數。這需要一些機制,來判斷結束工作階段的時機。指令碼範例程式碼會示範簡易的機制,檢查玩家連線,並在指定的時間長度內沒有任何玩家連線到工作階段時觸發遊戲工作階段的終止。

具有最基本配置-服務器進程初始化和終止的實時服務器-基本上充當無狀態中繼服務器。實時服務器在連接到遊戲的遊戲客戶端之間轉發消息和遊戲數據,但不會採取獨立的操作來處理數據或執行邏輯。根據您遊戲的需求,您可以選用地新增遊戲邏輯,由遊戲事件或其他機制觸發。

添加服務器端遊戲邏輯(可選)

您可以選擇將遊戲邏輯添加到實時腳本中。例如,您可以執行以下任何或所有的作業:指令碼範例程式碼會提供說明。請參閱 亞馬遜GameLift即時伺服器指令碼

  • 新增事件驅動邏輯。實作回呼函數,以回應用戶端 ‒ 伺服器事件。如需完整的回呼清單,請參閱 實時服務器的腳本回調

  • 透過傳送訊息至伺服器來觸發邏輯。針對從遊戲用戶端傳送到伺服器的訊息,建立一組特殊操作程式碼,並新增函數來處理接收。使用回呼 onMessage,並使用 gameMessage 界面來剖析訊息內容 (請參閱 gameMessage. 操作碼)。

  • 啟用遊戲邏輯以存取您的其他AWS資源。如需詳細資訊,請參閱 與您車隊的其他AWS資源進行溝通

  • 允許遊戲邏輯存取執行所在執行執行個體的叢集資訊。如需詳細資訊,請參閱 取得 Amazon GameLift 執行個體的叢集資料

實時服務器腳本示例

此示例說明了部署實時服務器以及一些自定義邏輯所需的基本腳本。它包含必要的 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 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, 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 };