second commit
This commit is contained in:
parent
3f86ab488e
commit
0d055b2ce5
35
database.js
35
database.js
@ -1,35 +0,0 @@
|
|||||||
// database.js
|
|
||||||
const mysql = require('mysql2/promise');
|
|
||||||
|
|
||||||
const dbConfig = {
|
|
||||||
host: "95.217.113.161",
|
|
||||||
user: "Likima",
|
|
||||||
password: "27As$r0q1",
|
|
||||||
database: "jedaiito",
|
|
||||||
charset: "utf8"
|
|
||||||
};
|
|
||||||
|
|
||||||
const pool = mysql.createPool({
|
|
||||||
...dbConfig,
|
|
||||||
waitForConnections: true,
|
|
||||||
connectionLimit: 10,
|
|
||||||
queueLimit: 0
|
|
||||||
});
|
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
updatePlayerElo,
|
|
||||||
};
|
|
10
elo.js
10
elo.js
@ -1,10 +0,0 @@
|
|||||||
// elo.js
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
calculateElo,
|
|
||||||
};
|
|
54
events.js
54
events.js
@ -1,54 +0,0 @@
|
|||||||
// events.js
|
|
||||||
const events = require('events');
|
|
||||||
const { calculateElo } = require('./elo');
|
|
||||||
const { updatePlayerElo } = require('./database');
|
|
||||||
|
|
||||||
class GameEventEmitter extends events.EventEmitter {}
|
|
||||||
|
|
||||||
const gameEventEmitter = new GameEventEmitter();
|
|
||||||
|
|
||||||
function handleRoundEnd(redScore, blueScore, playerScores, currentPlayers) {
|
|
||||||
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;
|
|
||||||
|
|
||||||
winningPlayers.forEach(async (player) => {
|
|
||||||
const individualPerformanceFactor = playerScores[player.id] / redScore; // Adjust based on individual contribution
|
|
||||||
player.elo = calculateElo(player.elo, averageLosingElo, 1 * individualPerformanceFactor, kFactor);
|
|
||||||
await updatePlayerElo(player);
|
|
||||||
});
|
|
||||||
|
|
||||||
losingPlayers.forEach(async (player) => {
|
|
||||||
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.");
|
|
||||||
}
|
|
||||||
|
|
||||||
gameEventEmitter.on('RoundEnd', handleRoundEnd);
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
gameEventEmitter,
|
|
||||||
handleRoundEnd,
|
|
||||||
};
|
|
257
main.js
257
main.js
@ -1,46 +1,229 @@
|
|||||||
// main.js
|
const mysql = require('mysql2/promise');
|
||||||
const { spawn } = require('child_process');
|
const WebSocket = require('ws');
|
||||||
const { gameEventEmitter } = require('./events');
|
const axios = require('axios');
|
||||||
const { Player } = require('./player');
|
const events = require('events');
|
||||||
|
|
||||||
let currentPlayers = [];
|
// Database configuration
|
||||||
let playerScores = {};
|
const dbConfig = {
|
||||||
|
host: "95.217.113.161",
|
||||||
|
user: "Likima",
|
||||||
|
password: "27As$r0q1",
|
||||||
|
database: "jedaiito",
|
||||||
|
charset: "utf8"
|
||||||
|
};
|
||||||
|
|
||||||
function runCommand(command) {
|
// Create a MySQL connection pool
|
||||||
const p = spawn('sh', ['-c', command], { stdio: 'pipe' });
|
const pool = mysql.createPool({
|
||||||
|
...dbConfig,
|
||||||
|
waitForConnections: true,
|
||||||
|
connectionLimit: 10,
|
||||||
|
queueLimit: 0
|
||||||
|
});
|
||||||
|
|
||||||
p.stderr.on('data', (data) => {
|
// Elo Rating Calculation
|
||||||
const line = data.toString().trim();
|
function calculateElo(currentRating, opponentRating, actualScore, kFactor = 32) {
|
||||||
console.log(">>> " + line);
|
const expectedScore = 1 / (1 + Math.pow(10, (opponentRating - currentRating) / 400));
|
||||||
|
const newRating = currentRating + kFactor * (actualScore - expectedScore);
|
||||||
|
return newRating;
|
||||||
|
}
|
||||||
|
|
||||||
if (line.startsWith('>>> red:') && line.includes(' blue:')) {
|
// Simplified Player class
|
||||||
const redScore = parseInt(line.split('red:')[1].split(' ')[0]);
|
class Player {
|
||||||
const blueScore = parseInt(line.split('blue:')[1]);
|
constructor(id, ip, name, guid, elo = 1000, score = 0, team = '') {
|
||||||
gameEventEmitter.emit('RoundEnd', redScore, blueScore, playerScores, currentPlayers);
|
this.id = id;
|
||||||
playerScores = {}; // Reset playerScores after round end
|
this.ip = ip;
|
||||||
} else if (line.startsWith('>>> score:')) {
|
this.name = name;
|
||||||
const parts = line.split(' ');
|
this.guid = guid;
|
||||||
const score = parseInt(parts[2]);
|
this.elo = elo;
|
||||||
const clientId = parseInt(parts[6]);
|
this.score = score;
|
||||||
const playerName = parts.slice(7).join(' ');
|
this.team = team;
|
||||||
|
this.is_alive = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const player = currentPlayers.find(p => p.id === clientId);
|
// Utility function to remove color codes
|
||||||
if (player) {
|
function removeColorCodes(name) {
|
||||||
player.score = score;
|
return name.replace(/\^\d/g, '');
|
||||||
playerScores[clientId] = score;
|
}
|
||||||
console.log(`Player ${playerName} (ID: ${clientId}) has a score of ${score}`);
|
|
||||||
|
// Update Elo ratings in the database
|
||||||
|
async function updatePlayerElo(player) {
|
||||||
|
const connection = await pool.getConnection();
|
||||||
|
try {
|
||||||
|
const updateQuery = `
|
||||||
|
UPDATE jedaii_comp
|
||||||
|
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/68f7edce/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'
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
|
||||||
if (checkLine(line)) {
|
const { data } = response;
|
||||||
// Additional event handling can go here
|
return data.data.token;
|
||||||
gameEventEmitter.emit('SomeOtherEvent', line);
|
} catch (error) {
|
||||||
}
|
console.error('Error requesting new token:', error);
|
||||||
});
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
p.on('close', (code) => {
|
// Main function to start WebSocket connection and handle console output
|
||||||
console.log(`Process exited with code ${code}`);
|
async function startWebSocketConnection() {
|
||||||
});
|
try {
|
||||||
|
const API_URL = 'https://panel.pineriver.net/api/client/servers/68f7edce/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 (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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkLine(line) {
|
function checkLine(line) {
|
||||||
@ -55,5 +238,5 @@ function checkLine(line) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Running the game command
|
// Start the WebSocket connection
|
||||||
runCommand("sh start.sh");
|
startWebSocketConnection();
|
||||||
|
25
package.json
Normal file
25
package.json
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"name": "game-server-management",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "A Node.js application for managing game server events and player Elo ratings.",
|
||||||
|
"main": "index.js",
|
||||||
|
"scripts": {
|
||||||
|
"start": "node index.js"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"axios": "^1.4.0",
|
||||||
|
"events": "^3.3.0",
|
||||||
|
"mysql2": "^3.1.0",
|
||||||
|
"ws": "^8.12.0"
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"game",
|
||||||
|
"server",
|
||||||
|
"elo",
|
||||||
|
"websocket",
|
||||||
|
"mysql"
|
||||||
|
],
|
||||||
|
"author": "Your Name",
|
||||||
|
"license": "MIT"
|
||||||
|
}
|
||||||
|
|
22
player.js
22
player.js
@ -1,22 +0,0 @@
|
|||||||
// player.js
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function removeColorCodes(name) {
|
|
||||||
return name.replace(/\^\d/g, '');
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
Player,
|
|
||||||
removeColorCodes,
|
|
||||||
};
|
|
Loading…
Reference in New Issue
Block a user