init
This commit is contained in:
101
server.js
Normal file
101
server.js
Normal file
@@ -0,0 +1,101 @@
|
||||
'use strict';
|
||||
// Serveur de sync communautaire Farm Tracker.
|
||||
// Stocke les stats de chaque joueur (identifié par UUID) et renvoie l'agrégat.
|
||||
// Lance avec : node server.js (ou PORT=3742 node server.js)
|
||||
// Aucune dépendance externe.
|
||||
|
||||
const http = require('http');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const DATA_FILE = path.join(__dirname, 'sync-data.json');
|
||||
const PORT = Number(process.env.PORT) || 3742;
|
||||
const MAX_BODY = 10 * 1024 * 1024; // 10 Mo max par requête
|
||||
|
||||
function loadData() {
|
||||
try { return JSON.parse(fs.readFileSync(DATA_FILE, 'utf8')); }
|
||||
catch { return { clients: {} }; }
|
||||
}
|
||||
|
||||
function saveData(data) {
|
||||
const tmp = DATA_FILE + '.tmp';
|
||||
fs.writeFileSync(tmp, JSON.stringify(data));
|
||||
fs.renameSync(tmp, DATA_FILE);
|
||||
}
|
||||
|
||||
function mergeInto(target, source) {
|
||||
for (const [mob, mobData] of Object.entries(source.mobs || {})) {
|
||||
if (!mob) continue;
|
||||
if (!target.mobs[mob]) target.mobs[mob] = { kills: 0, items: {} };
|
||||
const t = target.mobs[mob];
|
||||
t.kills += Number(mobData.kills) || 0;
|
||||
for (const [key, item] of Object.entries(mobData.items || {})) {
|
||||
if (!t.items[key]) t.items[key] = { rawName: '', name: '', occurrences: 0, totalQty: 0 };
|
||||
t.items[key].occurrences += Number(item.occurrences) || 0;
|
||||
t.items[key].totalQty += Number(item.totalQty) || 0;
|
||||
if (item.rawName) t.items[key].rawName = item.rawName;
|
||||
if (item.name) t.items[key].name = item.name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Recompute l'agrégat à partir des contributions individuelles de chaque client.
|
||||
// Chaque client est compté exactement une fois (la dernière soumission).
|
||||
function computeAggregate(clients) {
|
||||
const agg = { type: 'farm-tracker-dropstats', version: 1, updatedAt: new Date().toISOString(), mobs: {} };
|
||||
for (const stats of Object.values(clients)) mergeInto(agg, stats);
|
||||
return agg;
|
||||
}
|
||||
|
||||
http.createServer((req, res) => {
|
||||
res.setHeader('Access-Control-Allow-Origin', '*');
|
||||
res.setHeader('Access-Control-Allow-Methods', 'POST, OPTIONS');
|
||||
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
|
||||
|
||||
if (req.method === 'OPTIONS') { res.writeHead(204); res.end(); return; }
|
||||
|
||||
if (req.method === 'GET' && req.url === '/stats') {
|
||||
const data = loadData();
|
||||
const aggregate = computeAggregate(data.clients);
|
||||
const clientCount = Object.keys(data.clients).length;
|
||||
res.writeHead(200, { 'Content-Type': 'application/json' });
|
||||
res.end(JSON.stringify({ ok: true, dropStats: aggregate, clientCount }));
|
||||
return;
|
||||
}
|
||||
|
||||
if (req.method !== 'POST' || req.url !== '/sync') {
|
||||
res.writeHead(404); res.end('Not found'); return;
|
||||
}
|
||||
|
||||
let body = '';
|
||||
req.on('data', chunk => {
|
||||
body += chunk;
|
||||
if (body.length > MAX_BODY) req.destroy();
|
||||
});
|
||||
req.on('end', () => {
|
||||
try {
|
||||
const { clientId, dropStats } = JSON.parse(body);
|
||||
if (!clientId || typeof clientId !== 'string' || clientId.length > 64)
|
||||
throw new Error('clientId invalide');
|
||||
if (!dropStats || dropStats.type !== 'farm-tracker-dropstats')
|
||||
throw new Error('Format dropStats invalide');
|
||||
|
||||
const data = loadData();
|
||||
data.clients[clientId] = dropStats;
|
||||
const aggregate = computeAggregate(data.clients);
|
||||
saveData(data);
|
||||
|
||||
const clientCount = Object.keys(data.clients).length;
|
||||
console.log(`[${new Date().toISOString()}] sync client=${clientId.slice(0, 8)}… clients=${clientCount}`);
|
||||
|
||||
res.writeHead(200, { 'Content-Type': 'application/json' });
|
||||
res.end(JSON.stringify({ ok: true, dropStats: aggregate, clientCount }));
|
||||
} catch (e) {
|
||||
res.writeHead(400, { 'Content-Type': 'application/json' });
|
||||
res.end(JSON.stringify({ ok: false, error: e.message }));
|
||||
}
|
||||
});
|
||||
req.on('error', () => {});
|
||||
}).listen(PORT, '0.0.0.0', () => {
|
||||
console.log(`Farm Tracker sync server — port ${PORT} — data: ${DATA_FILE}`);
|
||||
});
|
||||
Reference in New Issue
Block a user