const mysql = require('mysql2/promise'); const WebSocket = require('ws'); const axios = require('axios'); const events = require('events'); // Database configuration const dbConfig = { host: "95.217.113.161", user: "Likima", password: "27As$r0q1", database: "jedaiito", charset: "utf8" }; // Create a MySQL connection pool const pool = mysql.createPool({ ...dbConfig, waitForConnections: true, connectionLimit: 10, queueLimit: 0 }); // Elo Rating Calculation function calculateElo(currentRating, opponentRating, actualScore, kFactor = 32) { const expectedScore = 1 / (1 + Math.pow(10, (opponentRating - currentRating) / 400)); const newRating = currentRating + kFactor * (actualScore - expectedScore); return newRating; } // Simplified Player class class Player { constructor(id, ip, name, guid, elo = 1000, score = 0, team = '') { this.id = id; this.ip = ip; this.name = name; this.guid = guid; this.elo = elo; this.score = score; this.team = team; this.is_alive = true; } } // Utility function to remove color codes function removeColorCodes(name) { return name.replace(/\^\d/g, ''); } // Update Elo ratings in the database async function updatePlayerElo(player) { const connection = await pool.getConnection(); try { const updateQuery = ` UPDATE players SET elo = ? WHERE guid = ? `; await connection.query(updateQuery, [player.elo, player.guid]); } finally { connection.release(); } } // Event handling class GameEventEmitter extends events.EventEmitter {} const gameEventEmitter = new GameEventEmitter(); // Player lists const currentPlayers = []; const playerQueue = []; // Handle round end event gameEventEmitter.on('RoundEnd', async (redScore, blueScore, playerScores) => { console.log(`Round ended. Red: ${redScore}, Blue: ${blueScore}`); let winningTeam, losingTeam; if (redScore > blueScore) { winningTeam = 'red'; losingTeam = 'blue'; } else if (blueScore > redScore) { winningTeam = 'blue'; losingTeam = 'red'; } else { console.log("It's a draw. No Elo change."); return; } const kFactor = 32; // You can adjust this as needed const winningPlayers = currentPlayers.filter(player => player.team === winningTeam); const losingPlayers = currentPlayers.filter(player => player.team === losingTeam); const averageWinningElo = winningPlayers.reduce((acc, player) => acc + player.elo, 0) / winningPlayers.length; const averageLosingElo = losingPlayers.reduce((acc, player) => acc + player.elo, 0) / losingPlayers.length; for (const player of winningPlayers) { const individualPerformanceFactor = playerScores[player.id] / redScore; // Adjust based on individual contribution player.elo = calculateElo(player.elo, averageLosingElo, 1 * individualPerformanceFactor, kFactor); await updatePlayerElo(player); } for (const player of losingPlayers) { const individualPerformanceFactor = playerScores[player.id] / blueScore; // Adjust based on individual contribution player.elo = calculateElo(player.elo, averageWinningElo, 0 * individualPerformanceFactor, kFactor); await updatePlayerElo(player); } console.log("Elo ratings updated."); }); // Function to request a new token async function requestNewToken() { const API_URL = 'https://panel.pineriver.net/api/client/servers/35e1d7ab/websocket'; // Corrected API endpoint const API_KEY = 'Bearer ptlc_uohTuXbtbTmVaSAhwtD16R2oDynhQgTWBZKN2m5lWdh'; // Your actual API key try { const response = await axios.get(API_URL, { headers: { 'Authorization': API_KEY, 'Accept': 'application/json', 'Content-Type': 'application/json' } }); const { data } = response; return data.data.token; } catch (error) { console.error('Error requesting new token:', error); throw error; } } // Main function to start WebSocket connection and handle console output async function startWebSocketConnection() { try { const API_URL = 'https://panel.pineriver.net/api/client/servers/35e1d7ab/websocket'; const API_KEY = 'Bearer ptlc_uohTuXbtbTmVaSAhwtD16R2oDynhQgTWBZKN2m5lWdh'; // Request the token and WebSocket URL const response = await axios.get(API_URL, { headers: { 'Authorization': API_KEY, 'Accept': 'application/json', 'Content-Type': 'application/json' } }); const { token, socket } = response.data.data; // Create WebSocket connection const ws = new WebSocket(socket); ws.on('open', () => { ws.send(JSON.stringify({ event: 'auth', args: [token] })); console.log('WebSocket connection established and authenticated'); }); ws.on('message', async (message) => { const parsedMessage = JSON.parse(message); if (parsedMessage.event === 'console output') { const consoleOutput = parsedMessage.args[0]; handleConsoleOutput(consoleOutput); } if (parsedMessage.event === 'token expiring') { console.log('Token is expiring soon, requesting a new token...'); const newToken = await requestNewToken(); ws.send(JSON.stringify({ event: 'auth', args: [newToken] })); console.log('WebSocket re-authenticated with new token'); } if (parsedMessage.event === 'token expired') { console.log('Token has expired, reconnecting with a new token...'); ws.close(); // Close the current WebSocket connection startWebSocketConnection(); // Start a new WebSocket connection } }); ws.on('close', () => { console.log('WebSocket connection closed'); }); ws.on('error', (error) => { console.error('WebSocket error:', error); }); } catch (error) { console.error('Error starting WebSocket connection:', error); } } // Handle console output received via WebSocket function handleConsoleOutput(line) { const playerScores = {}; if (eventClientConnect(line, line, playerQueue)) return; if (eventPlayer(line, line, currentPlayers, playerQueue)) return; if (eventDisconnect(line, line, currentPlayers, playerQueue)) return; if (eventKill(line, line, currentPlayers)) return; if (eventSay(line, line, currentPlayers)) return; if (eventBroadcast(line, line, currentPlayers)) return; if (eventShutdown(line, currentPlayers, playerQueue)) return; if (line.startsWith('>>> red:') && line.includes(' blue:')) { const redScore = parseInt(line.split('red:')[1].split(' ')[0]); const blueScore = parseInt(line.split('blue:')[1]); gameEventEmitter.emit('RoundEnd', redScore, blueScore, playerScores); } else if (line.startsWith('>>> score:')) { const parts = line.split(' '); const score = parseInt(parts[2]); const clientId = parseInt(parts[6]); const playerName = parts.slice(7).join(' '); const player = currentPlayers.find(p => p.id === clientId); if (player) { player.score = score; playerScores[clientId] = score; console.log(`Player ${playerName} (ID: ${clientId}) has a score of ${score}`); } } if (checkLine(line)) { // Additional event handling can go here gameEventEmitter.emit('SomeOtherEvent', line); } } // Event Handlers async function eventClientConnect(event, eventContent, playerQueue) { if (event.includes("ClientConnect")) { console.log("Client connecting..."); const pid = parseInt(eventContent.split("ID: ")[1].split(" ")[0], 10); const guid = eventContent.includes("\\ja_guid\\") ? eventContent.split("\\ja_guid\\")[1].split("\\")[0] : null; if (!guid) { console.log("GUID not found in event content, skipping player initialization."); return false; } if (currentPlayers.some(player => player.guid === guid)) { return false; } console.log(`Adding player to queue: [PID: ${pid}] [GUID: ${guid}]`); playerQueue.push({ guid: guid, id: pid, ip: null, name: null }); return true; } return false; } async function eventPlayer(event, eventContent, currentPlayers, playerQueue) { if (event.startsWith("Player")) { try { // Extract GUID, name, and IP from eventContent const guid = eventContent.split("\\ja_guid\\", 1)[1].split("\\", 1)[0]; const name = eventContent.split("\\name\\", 1)[1].split("\\", 1)[0]; const ip = eventContent.split("\\ip\\", 1)[1].split("\\", 1)[0].split(":", 1)[0]; if (guid === "0") { console.log(`Skipping player with invalid GUID: ${guid}`); return false; } // Use GUID to load or create the player const playerInfo = await loadPlayerByGuid(guid, name, ip, currentPlayers); playerInfo.id = parseInt(event.split(" ")[1], 10); // Set session-specific ID console.log(`Player event detected. Player ID: ${playerInfo.id}`); return true; } catch (error) { console.log("Error processing player event:", error); return false; } } return false; } async function loadPlayerByGuid(guid, name, ip, currentPlayers) { let player = currentPlayers.find(p => p.guid === guid); if (!player) { // Check if the player already exists in the database const playerExists = await checkIfPlayerExists(guid); if (playerExists) { player = await getPlayerFromDatabase(guid); console.log(`Loaded player from database: ${player.name} (GUID: ${guid})`); } else { // Create a new player in the database player = new Player(null, ip, name, guid); await insertNewPlayer(player); console.log(`New player added to the database: ${name} (GUID: ${guid})`); } // Add to current players currentPlayers.push(player); } else { console.log(`Player already in current session: ${name} (GUID: ${guid})`); } return player; } // Check if a player with the given GUID already exists in the database async function checkIfPlayerExists(guid) { const connection = await pool.getConnection(); try { const query = `SELECT COUNT(*) AS count FROM players WHERE guid = ?`; const [rows] = await connection.execute(query, [guid]); return rows[0].count > 0; } finally { if (connection) connection.release(); } } // Get player data from the database async function getPlayerFromDatabase(guid) { const connection = await pool.getConnection(); try { const query = `SELECT * FROM players WHERE guid = ?`; const [rows] = await connection.execute(query, [guid]); const playerData = rows[0]; return new Player(null, playerData.ip, playerData.name, playerData.guid, playerData.elo); } finally { if (connection) connection.release(); } } // Insert a new player into the database async function insertNewPlayer(player) { const connection = await pool.getConnection(); try { const aliasesStr = JSON.stringify(player.aliases || []); const insertQuery = ` INSERT INTO players (guid, name, ip, elo, aliases) VALUES (?, ?, ?, ?, ?) `; await connection.execute(insertQuery, [player.guid, player.name, player.ip, player.elo, aliasesStr]); console.log(`Player with GUID: ${player.guid} inserted into the database.`); } finally { if (connection) connection.release(); } } function searchByNameOrGuid(identifier, currentPlayers) { const cleanIdentifier = removeColorCodes(identifier); for (let i = 0; i < currentPlayers.length; i++) { const player = currentPlayers[i]; if (player.guid === cleanIdentifier || removeColorCodes(player.name).toLowerCase().includes(cleanIdentifier.toLowerCase())) { return i; } } return -1; } async function eventSay(event, eventContent, currentPlayers) { if (!event.includes('say')) { return false; } let name = eventContent.split(": ", 1)[0]; const chat = eventContent.split(": ", 2)[1]; const pIndex = searchByNameOrGuid(name, currentPlayers); if (pIndex === -1) return false; const player = currentPlayers[pIndex]; const parts = chat.split(" "); // Check if the player typed "!rank" if (parts[0] === "!rank") { const playerElo = player.elo; // Get the player's Elo rating const message = `say ${player.name}, your current Elo rating is: ${playerElo}`; console.log(message); // Debugging: log the message network.sendCmd(message); // Send the message back to the game server return true; } // Additional chat commands handling // ... return true; } async function eventBroadcast(event, eventContent, currentPlayers) { if (event.includes("broadcast")) { if (eventContent.includes("print ")) { const templine = eventContent.split("print ")[1].trim(); console.log(`Debug: Broadcast event detected: ${templine}`); // Additional broadcast event handling // ... } } return false; } function eventShutdown(event, currentPlayers, playerQueue) { if (event.toLowerCase().includes("shutdown")) { console.log(">>>>>ROUND END<<<<<<<<<"); playerQueue.length = 0; // Clear the player queue currentPlayers.length = 0; // Clear the current players list return true; } else { return false; } } async function eventDisconnect(event, eventContent, currentPlayers, playerQueue) { if (!event.includes("ClientD")) { return false; } const pid = parseInt(eventContent.split(" ")[0], 10); console.log("Disconnected id:", pid); const pindex = searchById(pid, currentPlayers); if (pindex === -1) { console.log("Uninitialized client disconnected ID:", pid); return false; } const player = currentPlayers[pindex]; if (player.name) { network.sendCmd(`say ${player.name} ^1has left the server to pick ^6flowers.`); } console.log("REMOVING AND SAVING PLAYER...\n"); currentPlayers.splice(pindex, 1); // Remove the player from the currentPlayers array const queueIndex = searchById(pid, playerQueue); if (queueIndex !== -1) { playerQueue.splice(queueIndex, 1); // Remove the player from the playerQueue array } return true; } async function eventKill(event, eventContent, currentPlayers) { if (event.includes("Kill")) { const pid = parseInt(eventContent.split(" ")[1], 10); console.log("Death ID:", pid); const deadPlayerIndex = searchById(pid, currentPlayers); if (deadPlayerIndex === -1) { console.log("Client not initialized, please reconnect.", pid); return false; } // Additional kill event handling // ... } return false; } function checkLine(line) { if (line.startsWith("Pl")) return true; if (line.startsWith("Shut")) return true; if (line.startsWith("ClientConne")) return true; if (line.startsWith("ClientDisconn")) return true; if (line.startsWith("Kill")) return true; if (line.includes("say:")) return true; if (line.startsWith("ClientUser")) return true; if (line.startsWith("broadcast")) return true; return false; } // Start the WebSocket connection startWebSocketConnection();