Realtime 스크립트 생성 - 아마존 GameLift

기계 번역으로 제공되는 번역입니다. 제공된 번역과 원본 영어의 내용이 상충하는 경우에는 영어 버전이 우선합니다.

Realtime 스크립트 생성

게임에 Realtime 서버를 사용하려면 Realtime 서버 플릿을 구성하고 사용자 지정(선택 사항)하는 스크립트(JavaScript 코드 형식)를 제공해야 합니다. 이 주제에서는 Realtime 스크립트를 작성하는 주요 단계를 설명합니다. 스크립트가 준비되면 Amazon GameLift 서비스에 업로드한 후 이를 사용하여 플릿을 생성합니다(Amazon GameLift에 Realtime 서버 스크립트 업로드 참조).

Realtime 서버에 스크립트를 사용할 준비를 하려면 다음 기능을 Realtime 스크립트에 추가합니다.

게임 세션 수명 주기 관리(필수)

적어도 Realtime 스크립트에는 Realtime 서버가 게임 세션 시작을 준비하게 하는 Init() 기능이 포함되어야 합니다. 그리고 게임 세션을 종료하여 새 게임 세션이 플릿에서 계속 시작되도록 보장하는 방법도 제공할 것을 적극 권장합니다.

Init() 콜백 기능은 호출되면 Realtime 세션 객체로 전달되는데, 그 안에는 Realtime 서버용 인터페이스가 포함됩니다. 이 인터페이스에 관한 자세한 내용은 Realtime 서버 인터페이스 섹션을 참조하세요.

게임 세션을 명확하게 종료하려면 스크립트에서 Realtime 서버의 session.processEnding 기능도 호출해야 합니다. 이를 위해서는 세션 종료 시점을 결정하는 메커니즘이 필요합니다. 스크립트 예제 코드는 플레이어 연결을 확인하고 지정된 시간 동안 세션에 연결된 플레이어가 없을 경우 게임 세션을 종료하는 간단한 메커니즘을 설명합니다.

Realtime 서버 및 기본적인 구성(서버 프로세스 초기화 및 종료)은 기본적으로 상태 비저장 릴레이 서버로 작동합니다. Realtime 서버는 게임에 연결된 게임 클라이언트 간에 메시지와 게임 데이터를 중계하지만, 데이터를 처리하거나 로직을 수행하기 위한 독립적인 작업을 수행하지는 않습니다. 게임에 필요한 경우 게임 이벤트나 다른 메커니즘에 의해 트리거되는 게임 로직을 선택적으로 추가할 수 있습니다.

서버 측 게임 로직 추가(선택 사항)

Realtime 스크립트에 게임 로직을 추가할 수도 있습니다. 예를 들어, 다음 중 하나 또는 전부를 수행할 수 있습니다. 스크립트 예제 코드는 설명을 제공합니다. Amazon GameLift Realtime 서버 스크립트 참조 섹션을 참조하세요.

  • 이벤트 중심 로직을 추가합니다. 콜백 기능을 구현하여 클라이언트 서버 이벤트에 응답합니다. 전체 콜백 목록은 Realtime 서버용 스크립트 콜백 섹션을 참조하세요.

  • 서버로 메시지를 전송하여 로직을 트리거합니다. 게임 클라이언트에서 서버로 전송되는 메시지의 특정 작업 코드 세트를 생성하고, 수신을 처리하는 기능을 추가합니다. 콜백 onMessage를 사용하고, gameMessage 인터페이스를 이용해 메시지 내용을 분석합니다(gameMessage.opcode 참조).

  • 게임 로직을 활성화하여 다른 AWS 리소스에 액세스할 수 있습니다. 자세한 내용은 플릿에서 다른 AWS 리소스와 통신 섹션을 참조하세요.

  • 게임 로직이 실행 중인 인스턴스의 플릿 정보에 액세스할 수 있도록 허용합니다. 자세한 내용은 Amazon GameLift 인스턴스의 플릿 데이터를 가져옵니다. 섹션을 참조하세요.

Realtime 서버 스크립트 예제

이 예제는 Realtime 서버 및 사용자 지정 로직을 배포하는 데 필요한 기본 스크립트에 대해 설명합니다. 여기에는 필수 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 };