From 228763aaeb03b128b54bf8fb5aba5ed119af7045 Mon Sep 17 00:00:00 2001 From: MineFact Date: Thu, 29 Feb 2024 07:37:25 -0600 Subject: [PATCH] added players sync --- src/index.ts | 5 + src/routes/teams/POST_Playerlist.ts | 53 +++++++ src/struct/core/buildteam.ts | 51 +++++++ src/struct/core/network.ts | 38 +++++ swagger.json | 211 +++++++++++++++++----------- 5 files changed, 276 insertions(+), 82 deletions(-) create mode 100644 src/routes/teams/POST_Playerlist.ts diff --git a/src/index.ts b/src/index.ts index 1ccb6ee..a367dc9 100644 --- a/src/index.ts +++ b/src/index.ts @@ -188,6 +188,11 @@ router.use(helmet()); Joi, network ); +(await import("./routes/teams/POST_Playerlist.js")).initRoutes( + router, + Joi, + network +); // Init PUT Routes for the API (await import("./routes/teams/PUT_TeamHasBuildTeamToolsInstalled.js")).initRoutes( diff --git a/src/routes/teams/POST_Playerlist.ts b/src/routes/teams/POST_Playerlist.ts new file mode 100644 index 0000000..a6b6a22 --- /dev/null +++ b/src/routes/teams/POST_Playerlist.ts @@ -0,0 +1,53 @@ +import { Router } from "express"; +import Network, { BuildTeamIdentifier } from "../../struct/core/network.js"; + +export async function initRoutes(app: Router, joi: any, network: Network) { + + app.post('/api/teams/:apikey/playerlist', async function (req, res) { + + // Validate that the API key is a valid GUID + if(!await network.validateAPIKey(req, res)) + return; + + const buildTeam = await network.getBuildTeam(req.params.apikey, BuildTeamIdentifier.APIKey); + + if(buildTeam == null) { + res.status(400).send({ error: 'Build Team not found' }); + return; + } + + // Validate the parameters with joi + + // Schema for a single UUID and username pair + const uuidUsernameSchema = joi.array().ordered( + joi.string().guid({ version: 'uuidv4' }), // Validates a UUID (version 4) + joi.string() // Validates a simple string for the username + ).length(2); + + // Schema for the main array, containing multiple UUID-username pairs + const schema = joi.array().items(uuidUsernameSchema); + + const validation = schema.validate(req.body); + + // If the validation failed, return an error + if(validation.error != null){ + res.status(400).send({success: false, error: validation.error.details[0].message}); + return; + } + + + const result = await buildTeam.updatePlayerlist(req.body); + + + + // If the playerlist was not updated, return an error + if(result == false){ + res.status(400).send({success: false, error: 'An error occurred while updating the playerlist'}); + return; + }else{ + // Return the order id to the client + res.setHeader('Content-Type', 'application/json'); + res.send({success: true}) + } + }) +} \ No newline at end of file diff --git a/src/struct/core/buildteam.ts b/src/struct/core/buildteam.ts index afd7e96..f268867 100644 --- a/src/struct/core/buildteam.ts +++ b/src/struct/core/buildteam.ts @@ -28,12 +28,15 @@ export default class BuildTeam { private static readonly SERVER_UPDATE_INTERVAL: number = 60 * 24; // 24 hours private static readonly FTP_CONFIGURATION_UPDATE_INTERVAL: number = 60 * 24; // 24 hours private static readonly BUILD_TEAM_INFO_UPDATE_INTERVAL: number = 60 * 1; // 1 hour + private static readonly PLAYER_LIST_CLEAR_INTERVAL: number = 60 * 1; // 1 hours private apiKey: string; private buildTeamID: string | null = null; private network: Network; private psDatabase: DatabaseHandler private nwDatabase: DatabaseHandler + private playerList: any[] = []; + private lastPlayerListUpdate: Date | null = null; private psBuildTeamID: string | null = null; private psCities: Map = new Map() // Map @@ -78,6 +81,15 @@ export default class BuildTeam { if(this.psFTPConfiguration != null && this.network.getUpdateCacheTicks() % BuildTeam.FTP_CONFIGURATION_UPDATE_INTERVAL == 0) this.psFTPConfiguration.clear(); + + // If the last player list update is older than the player list clear interval, clear the player list + if(this.playerList != null && this.lastPlayerListUpdate != null && this.network.getUpdateCacheTicks() % BuildTeam.PLAYER_LIST_CLEAR_INTERVAL == 0){ + const timeDifference = (new Date().getTime() - this.lastPlayerListUpdate.getTime()) / 1000; + if(timeDifference > BuildTeam.PLAYER_LIST_CLEAR_INTERVAL){ + this.playerList = []; + this.lastPlayerListUpdate = null; + } + } } // Resets the cache for the build team @@ -87,6 +99,8 @@ export default class BuildTeam { this.psCountries.clear(); this.psServers.clear(); this.psFTPConfiguration.clear(); + this.playerList = []; + this.lastPlayerListUpdate = null; } async loadBuildTeamData(){ @@ -594,6 +608,43 @@ export default class BuildTeam { + /* ======================================= */ + /* Playerlist */ + /* ======================================= */ + + + /** Updates the playerlist of the build team. + * + * @param players The list of players + * + * @returns Returns true if the playerlist was updated successfully, otherwise false. + **/ + async updatePlayerlist(players: any[]) { + // Validate that the build team id is loaded + if(this.buildTeamID == null) + await this.loadBuildTeamData(); + if(this.buildTeamID == null) + return false; + + // Take the data that is needed for the BuildTeamOnlinePlayers table + const tempArray: any[] = []; + + players.forEach((subArray) => { + tempArray.push([subArray[0], subArray[1], this.buildTeamID]); + }); + + this.lastPlayerListUpdate = new Date(); + this.playerList = tempArray; + + return true; + } + + getPlayerList() { + return this.playerList; + } + + + /* ======================================= */ /* PlotSystem */ diff --git a/src/struct/core/network.ts b/src/struct/core/network.ts index 68cdd0c..19bdb38 100644 --- a/src/struct/core/network.ts +++ b/src/struct/core/network.ts @@ -115,6 +115,8 @@ export default class Network { if (isStarting == true) bar?.tick(); } + this.syncPlayerList(); + this.updateCacheTicks++; if(this.updateCacheTicks >= Number.MAX_SAFE_INTEGER - 100) @@ -437,7 +439,15 @@ export default class Network { return json.display_name; } + async syncPlayerList(): Promise{ + let globalPlayerList: any[] = []; + + // Create a two dimensional array with one array for each player + for (const buildTeam of this.buildTeams.values()) + globalPlayerList = globalPlayerList.concat(buildTeam.getPlayerList()); + return this.syncPlayerListInDatabase(globalPlayerList); + } @@ -579,4 +589,32 @@ export default class Network { const SQL = "SELECT * FROM BuildTeamWarpGroups"; return await this.networkDatabase.query(SQL); } + + + /* =================================================== */ + /* DATABASE POST REQUESTS */ + /* =================================================== */ + + private async syncPlayerListInDatabase(playerList: any[]): Promise { + try { + // Truncate the table first + await this.networkDatabase.query("TRUNCATE TABLE BuildTeamOnlinePlayers"); + + // Insert new data if available + if (playerList && playerList.length > 0) { + const placeholders = playerList.map(() => "(?, ?, ?)").join(", "); + const flatValues = playerList.flat(); + const insertSQL = `INSERT INTO BuildTeamOnlinePlayers (UUID, Name, BuildTeam) VALUES ${placeholders}`; + const insertResult = await this.networkDatabase.query(insertSQL, flatValues); + + if (insertResult.affectedRows > 0) + return insertResult.affectedRows; + } + } catch (error) { + console.error(error); + return false; + } + + return false; + } } diff --git a/swagger.json b/swagger.json index eaf2dda..c0f4b6b 100644 --- a/swagger.json +++ b/swagger.json @@ -356,6 +356,60 @@ } } }, + "/api/teams/countries": { + "get": { + "tags": [ + "Build Teams" + ], + "summary": "Get Countries", + "description": "Get a list of all countries along with their Build Team.", + "responses": { + "400": { + "description": "No countries found." + } + } + } + }, + "/api/teams/warps": { + "get": { + "tags": [ + "Build Teams" + ], + "summary": "Get Warps", + "description": "Get a list of all warps in BuildTheEarth.", + "parameters": [ + { + "name": "country", + "description": "Filter the warps by country code.", + "in": "query", + "schema": { + "type": "string" + }, + "required": false + } + ], + "responses": { + "400": { + "description": "No warps found." + } + } + } + }, + "/api/teams/warpgroups": { + "get": { + "tags": [ + "Build Teams" + ], + "summary": "Get Warps", + "description": "Get a list of all warp groups in BuildTheEarth.", + "parameters": [], + "responses": { + "400": { + "description": "No warp groups found." + } + } + } + }, "/api/teams/%KEY%": { "get": { "tags": [ @@ -697,7 +751,81 @@ } } }, - + "/api/teams/%KEY%/warpgroups": { + "get": { + "tags": [ + "Build Teams" + ], + "summary": "Get Build Team Warp Groups", + "description": "Returns the warp groups of a build team of BuildTheEarth.", + "parameters": [ + { + "name": "KEY", + "description": "The API Key, Build Team ID, Build Team Tag, or Build Team Server of the Build Team.", + "in": "path", + "schema": { + "type": "string" + }, + "required": true + } + ], + "responses": { + "200": { + "description": "Success" + } + } + } + }, + "/api/teams/%API_KEY%/playerlist": { + "get": { + "tags": [ + "Build Teams" + ], + "summary": "Sync Build Team Playerlist", + "description": "Sync the Build Team online Playerlist with the network", + "parameters": [ + { + "name": "api_key", + "in": "path", + "schema": { + "type": "string" + }, + "required": true + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "description": "The UUID of the player.", + "required": false + }, + "name": { + "type": "string", + "description": "The name of the player." + } + + }, + "example": [ + ["0000000-0000-0000-0000-000000000000", "Player 1"], + ["0000000-0000-0000-0000-000000000001", "Player 2"], + ["0000000-0000-0000-0000-000000000002", "Player 3"] + ] + } + } + } + }, + "responses": { + "200": { + "description": "Success" + } + } + } + }, "/api/teams/%API_KEY%/warps": { "post": { "tags": [ @@ -978,72 +1106,6 @@ } } }, - "/api/teams/countries": { - "get": { - "tags": [ - "Build Teams" - ], - "summary": "Get Countries", - "description": "Get a list of all countries along with their Build Team.", - "responses": { - "400": { - "description": "No countries found." - } - } - } - }, - "/api/teams/warps": { - "get": { - "tags": [ - "Build Teams" - ], - "summary": "Get Warps", - "description": "Get a list of all warps in BuildTheEarth.", - "parameters": [ - { - "name": "country", - "description": "Filter the warps by country code.", - "in": "query", - "schema": { - "type": "string" - }, - "required": false - } - ], - "responses": { - "400": { - "description": "No warps found." - } - } - } - }, - - "/api/teams/%KEY%/warpgroups": { - "get": { - "tags": [ - "Build Teams" - ], - "summary": "Get Build Team Warp Groups", - "description": "Returns the warp groups of a build team of BuildTheEarth.", - "parameters": [ - { - "name": "KEY", - "description": "The API Key, Build Team ID, Build Team Tag, or Build Team Server of the Build Team.", - "in": "path", - "schema": { - "type": "string" - }, - "required": true - } - ], - "responses": { - "200": { - "description": "Success" - } - } - } - }, - "/api/teams/%API_KEY%/warpgroups": { "post": { "tags": [ @@ -1186,21 +1248,6 @@ } } } - }, - "/api/teams/warpgroups": { - "get": { - "tags": [ - "Build Teams" - ], - "summary": "Get Warps", - "description": "Get a list of all warp groups in BuildTheEarth.", - "parameters": [], - "responses": { - "400": { - "description": "No warp groups found." - } - } - } } } }