Last active
October 26, 2021 14:28
-
-
Save BatteryAcid/a8d687f460a5b90fc403c600d1541707 to your computer and use it in GitHub Desktop.
Unity + Amazon GameLift setup in web console Part 2 snippets
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Example Lambda Client Service for Realtime Server example | |
// Original source: https://aws.amazon.com/blogs/gametech/creating-servers-for-multiplayer-mobile-games-with-amazon-gamelift/ | |
// Contains @BatteryAcid's edits to accompany video Unity + Amazon GameLift setup in web console | |
// https://youtu.be/WaAZyqgkXDY | |
//const uuid = require('uuid'); | |
const {"v4": uuidv4} = require('uuid'); | |
const AWS = require('aws-sdk'); | |
const GameLift = new AWS.GameLift({region: 'us-east-1'}); | |
const MegaFrogRaceFleetID = "YOUR_FLEET_ID"; | |
exports.handler = async (event) => { | |
let response; | |
let gameSessions; | |
// find any sessions that have available players | |
await GameLift.searchGameSessions({ | |
FleetId: MegaFrogRaceFleetID, | |
FilterExpression: "hasAvailablePlayerSessions=true" | |
}).promise().then(data => { | |
gameSessions = data.GameSessions; | |
}).catch(err => { | |
response = err; | |
}); | |
// if the response object has any value at any point before the end of | |
// the function that indicates a failure condition so return the response | |
if(response != null) | |
{ | |
return response; | |
} | |
// if there are no sessions, then we need to create a game session | |
let selectedGameSession; | |
if(gameSessions.length == 0) | |
{ | |
console.log("No game session detected, creating a new one"); | |
await GameLift.createGameSession({ | |
MaximumPlayerSessionCount: 2, // only two players allowed per game | |
FleetId: MegaFrogRaceFleetID | |
}).promise().then(data => { | |
selectedGameSession = data.GameSession; | |
}).catch(err => { | |
response = err; | |
}); | |
if(response != null) | |
{ | |
return response; | |
} | |
} | |
else | |
{ | |
// we grab the first session we find and join it | |
selectedGameSession = gameSessions[0]; | |
console.log("Game session exists, will join session ", selectedGameSession.GameSessionId); | |
} | |
// there isn't a logical way selectedGameSession could be null at this point | |
// but it's worth checking for in case other logic is added | |
if(selectedGameSession != null) | |
{ | |
// now we have a game session one way or the other, create a session for this player | |
await GameLift.createPlayerSession({ | |
GameSessionId : selectedGameSession.GameSessionId , | |
PlayerId: uuidv4()//.v4() | |
}).promise().then(data => { | |
console.log("Created player session ID: ", data.PlayerSession.PlayerSessionId); | |
response = data.PlayerSession; | |
}).catch(err => { | |
response = err; | |
}); | |
} | |
else | |
{ | |
response = { | |
statusCode: 500, | |
body: JSON.stringify({ | |
message: "Unable to find game session, check GameLift API status" | |
}) | |
}; | |
} | |
return response; | |
}; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Example Realtime Server Script | |
// Original source: https://docs.aws.amazon.com/gamelift/latest/developerguide/realtime-script.html | |
// Contains @BatteryAcid's edits to accompany video Unity + Amazon GameLift setup in web console | |
// https://youtu.be/WaAZyqgkXDY | |
'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; | |
// @BatteryAcid | |
const THROW_OP_CODE = 201; | |
const BOX_HIT_OP_CODE = 202; | |
const GAMEOVER_OP_CODE = 209; | |
let players = []; | |
let winner = null; | |
// Called when game server is initialized, passed server's object of current session | |
function init(rtSession) { | |
session = rtSession; | |
logger = session.getLogger(); | |
logger.info("init"); | |
} | |
// 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) { | |
logger.info("onStartGameSession: "); | |
logger.info(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) { | |
logger.info("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) { | |
logger.info("onPlayerAccepted: "); | |
logger.info(player); | |
players.push(player.peerId); | |
// 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) { | |
logger.info("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--; | |
} | |
// @BatteryAcid | |
// Handle a message to the server | |
function onMessage(gameMessage) { | |
logger.info("onMessage: "); | |
logger.info(gameMessage); | |
switch (gameMessage.opCode) { | |
case THROW_OP_CODE: { | |
logger.info("Received message THROW_OP_CODE"); | |
var testReturnCode = 200; | |
const outMessage = session.newTextGameMessage(testReturnCode, session.getServerId(), "OK"); | |
var allSessionPlayers = players;//session.getPlayers(); | |
logger.info(allSessionPlayers); | |
let allPlayersLength = allSessionPlayers.length; | |
logger.info(allPlayersLength); | |
for (let index = 0; index < allPlayersLength; ++index) { | |
session.getLogger().info("[app]: sendMessage " + outMessage.toString() + " " + allSessionPlayers[index].toString()); | |
session.sendMessage(outMessage, allSessionPlayers[index]); | |
}; | |
break; | |
} | |
case BOX_HIT_OP_CODE: { | |
logger.info("Received message BOX_HIT_OP_CODE"); | |
if (winner == null) { | |
// we have a winner | |
winner = gameMessage.sender | |
logger.info("winner:"); | |
logger.info(gameMessage); | |
logger.info(winner); | |
// tell all players game is over | |
var allSessionPlayers = players; | |
let allPlayersLength = allSessionPlayers.length; | |
for (let index = 0; index < allPlayersLength; ++index) { | |
let outMessage = session.newTextGameMessage(GAMEOVER_OP_CODE, session.getServerId(), "You Lost!"); | |
if (allSessionPlayers[index] == gameMessage.sender) { | |
outMessage = session.newTextGameMessage(GAMEOVER_OP_CODE, session.getServerId(), "You Won!"); | |
} | |
session.getLogger().info("[app]: game over " + outMessage.toString() + " " + allSessionPlayers[index].toString()); | |
session.sendMessage(outMessage, allSessionPlayers[index]); | |
}; | |
} | |
break; | |
} | |
} | |
} | |
// Return true if the send should be allowed | |
function onSendToPlayer(gameMessage) { | |
logger.info("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) { | |
logger.info("onSendToGroup: " + gameMessage); | |
return true; | |
} | |
// Return true if the player is allowed to join the group | |
function onPlayerJoinGroup(groupId, peerId) { | |
logger.info("onPlayerJoinGroup: " + groupId + ", " + peerId); | |
return true; | |
} | |
// Return true if the player is allowed to leave the group | |
function onPlayerLeaveGroup(groupId, peerId) { | |
logger.info("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 | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment