Amazon GameLift
开发人员指南 (版本 )
AWS 文档中描述的 AWS 服务或功能可能因区域而异。要查看适用于中国区域的差异,请参阅中国的 AWS 服务入门

创建 Realtime 脚本

要为您的游戏使用 Realtime Servers,您需要提供脚本(以一些 JavaScript 代码的格式)来配置并(可选)自定义 Realtime Servers 的队组。本主题涵盖了创建 Realtime 脚本的关键步骤。脚本准备就绪后,将它上传至 Amazon GameLift 服务并使用它来创建队组(请参阅将 Realtime Servers 脚本上传到 Amazon GameLift)。

要准备用于 Realtime Servers 的脚本,请将以下功能添加到 Realtime 脚本。

管理游戏会话生命周期(必需)

至少,Realtime 脚本必须包含 Init() 函数,该函数准备 Realtime 服务器来启动游戏会话。同时还强烈建议您提供一种终止游戏会话的方式,以确保队组上可以继续启动新游戏会话。

Init() 回调函数,该函数在调用时传递 Realtime 会话对象,其中包含 Realtime 服务器的接口。有关此接口的详细信息,请参阅 Realtime Servers 接口

要顺利结束游戏会话,脚本还必须调用 Realtime 服务器的 session.processEnding 函数。这需要一些机制来确定何时结束会话。此脚本示例代码演示了一个简单的机制,该机制检查玩家连接,并在指定时间长度中没有玩家连接到会话时触发游戏会话中止。

具有最基本配置(服务器进程初始化和终止)的 Realtime Servers 实际上是充当无状态的中继服务器。Realtime 服务器中继连接到游戏的游戏客户端之间的消息和游戏数据,但不会采取独立操作来处理数据或执行逻辑。您可以根据需要选择性地为游戏添加游戏逻辑(由游戏事件或其他机制触发)。

添加服务器端游戏逻辑(可选)

您可以选择将游戏逻辑添加到 Realtime 脚本。例如,您可以执行以下任一或所有操作。脚本示例代码提供了说明。请参阅 Amazon GameLift Realtime Servers 脚本参考

  • 添加事件驱动的逻辑。 实施回调函数来响应客户端-服务器事件。有关终端节点的完整列表,请参阅

  • 通过发送消息到服务器来触发逻辑。 为从游戏客户端发送到服务器的消息创建一组特殊操作代码,并添加函数来处理接收。使用回调 onMessage,并使用 gameMessage 接口解析消息内容(请参阅 gameMessage.opcode)。

Realtime Servers 脚本示例

此示例说明了部署 Realtime Servers 以及一些自定义逻辑所需的基本脚本。它包含必需的 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 };