Skip to content

Instantly share code, notes, and snippets.

@alexandre-bruffa
Last active July 20, 2023 17:46
Show Gist options
  • Save alexandre-bruffa/e42c9c8d17c073897b497efc4f0f8015 to your computer and use it in GitHub Desktop.
Save alexandre-bruffa/e42c9c8d17c073897b497efc4f0f8015 to your computer and use it in GitHub Desktop.
// 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
var players = {}; // Dictionary of players
var meteoriteIn = 0; // Time remaining before meteorite
// Custom op codes for user-defined messages
// Any positive op code number can be defined here. These should match your client code.
const CURRENT_PLAYER_ACCEPTED = 100;
const PLAYER_ACCEPTED = 101;
const PLAYER_DISCONNECTED = 102;
const CURRENT_PLAYER_UPDATE = 200;
const PLAYER_UPDATE = 201;
const METEORITE_IN = 900;
// 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();
meteoriteIn = getRandomArbitrary(25, 35);
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(currentPlayer) {
const playersValues = Object.values(players);
const playersStr = (playersValues.length == 0) ? ('{"players":[]}') : ('{"players":[' + playersValues.join(',') + ']}');
const outPlayerAcceptedMessage = session.newTextGameMessage(PLAYER_ACCEPTED, currentPlayer.peerId, "");
const outCurrentPlayerAcceptedMessage = session.newTextGameMessage(CURRENT_PLAYER_ACCEPTED, currentPlayer.peerId, playersStr);
for (var playerId in players) {
if (players.hasOwnProperty(playerId)) {
session.sendReliableMessage(outPlayerAcceptedMessage, parseInt(playerId));
}
}
session.sendReliableMessage(outCurrentPlayerAcceptedMessage, currentPlayer.peerId);
players[currentPlayer.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) {
const outMessage = session.newTextGameMessage(PLAYER_DISCONNECTED, peerId, "");
delete players[peerId];
for (var playerId in players) {
if (players.hasOwnProperty(playerId)) {
session.sendReliableMessage(outMessage, parseInt(playerId));
}
}
activePlayers--;
}
// Handle a message to the server
function onMessage(gameMessage) {
switch (gameMessage.opCode) {
case PLAYER_UPDATE:
players[gameMessage.sender] = String.fromCharCode(...Object.values(gameMessage.payload));
const outPlayerChangedMessage = session.newTextGameMessage(PLAYER_UPDATE, gameMessage.sender, gameMessage.payload);
const outPlayerChangedCurrentMessage = session.newTextGameMessage(CURRENT_PLAYER_UPDATE, gameMessage.sender, gameMessage.payload);
for (var playerId in players) {
if (players.hasOwnProperty(playerId)) {
playerId = parseInt(playerId);
if (playerId != gameMessage.sender) {
session.sendReliableMessage(outPlayerChangedMessage, playerId);
}
}
}
session.sendReliableMessage(outPlayerChangedCurrentMessage, gameMessage.sender);
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 {
// Build message and send it to all players
const outMessage = session.newTextGameMessage(METEORITE_IN, "", '{"meteoriteIn":' + meteoriteIn + '}');
for (var playerId in players) {
if (players.hasOwnProperty(playerId)) {
session.sendReliableMessage(outMessage, parseInt(playerId));
}
}
// Update or reset counter
meteoriteIn = (meteoriteIn <= 0) ? getRandomArbitrary(25, 35) : (meteoriteIn - 1);
// Loop
setTimeout(tickLoop, tickTime);
}
}
// Getting a random number between two values
function getRandomArbitrary(min, max) {
return Math.round(Math.random() * (max - min) + min);
}
// 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