133 lines
3.2 KiB
JavaScript
133 lines
3.2 KiB
JavaScript
'use strict';
|
|
|
|
const fs = require('node:fs');
|
|
const path = require('node:path');
|
|
const net = require('node:net');
|
|
|
|
const configPath = path.join(__dirname, 'config.json');
|
|
const defaultConfig = {
|
|
host: '0.0.0.0',
|
|
port: 24469,
|
|
token: ''
|
|
};
|
|
|
|
if (!fs.existsSync(configPath)) {
|
|
fs.writeFileSync(configPath, JSON.stringify(defaultConfig, null, 2));
|
|
}
|
|
|
|
const config = {
|
|
...defaultConfig,
|
|
...JSON.parse(fs.readFileSync(configPath, 'utf8'))
|
|
};
|
|
|
|
const clients = new Set();
|
|
|
|
function sendJson(socket, payload) {
|
|
socket.write(JSON.stringify(payload) + '\n');
|
|
}
|
|
|
|
function broadcast(payload) {
|
|
const line = JSON.stringify(payload) + '\n';
|
|
for (const socket of clients) {
|
|
if (!socket.destroyed) {
|
|
socket.write(line);
|
|
}
|
|
}
|
|
}
|
|
|
|
function systemMessage(message) {
|
|
return {
|
|
type: 'system',
|
|
message,
|
|
timestamp: Date.now()
|
|
};
|
|
}
|
|
|
|
const server = net.createServer((socket) => {
|
|
socket.setEncoding('utf8');
|
|
clients.add(socket);
|
|
let buffer = '';
|
|
|
|
sendJson(socket, systemMessage('Connected to IRC server.'));
|
|
console.log(`[connect] ${socket.remoteAddress}:${socket.remotePort}`);
|
|
|
|
socket.on('data', (chunk) => {
|
|
buffer += chunk;
|
|
|
|
let separatorIndex = buffer.indexOf('\n');
|
|
while (separatorIndex !== -1) {
|
|
const rawLine = buffer.slice(0, separatorIndex).trim();
|
|
buffer = buffer.slice(separatorIndex + 1);
|
|
separatorIndex = buffer.indexOf('\n');
|
|
|
|
if (!rawLine) {
|
|
continue;
|
|
}
|
|
|
|
let payload;
|
|
try {
|
|
payload = JSON.parse(rawLine);
|
|
} catch (error) {
|
|
sendJson(socket, systemMessage(`Bad JSON: ${error.message}`));
|
|
continue;
|
|
}
|
|
|
|
if (config.token && payload.token !== config.token) {
|
|
sendJson(socket, systemMessage('Invalid token.'));
|
|
continue;
|
|
}
|
|
|
|
if (payload.type === 'hello') {
|
|
const name = payload.player || 'unknown';
|
|
sendJson(socket, systemMessage(`Hello, ${name}.`));
|
|
continue;
|
|
}
|
|
|
|
if (payload.type === 'chat') {
|
|
const clientName = String(payload.client || 'UNKNOWN-CLIENT').trim();
|
|
const playerName = String(payload.player || 'unknown').trim();
|
|
const message = String(payload.message || '').trim();
|
|
|
|
if (!message) {
|
|
sendJson(socket, systemMessage('Empty chat message ignored.'));
|
|
continue;
|
|
}
|
|
|
|
const outgoing = {
|
|
type: 'chat',
|
|
client: clientName,
|
|
player: playerName,
|
|
message,
|
|
timestamp: Date.now()
|
|
};
|
|
|
|
console.log(`[chat] ${clientName} >> ${playerName}: ${message}`);
|
|
broadcast(outgoing);
|
|
continue;
|
|
}
|
|
|
|
sendJson(socket, systemMessage(`Unknown packet type: ${payload.type}`));
|
|
}
|
|
});
|
|
|
|
socket.on('close', () => {
|
|
clients.delete(socket);
|
|
console.log(`[disconnect] ${socket.remoteAddress}:${socket.remotePort}`);
|
|
});
|
|
|
|
socket.on('error', (error) => {
|
|
clients.delete(socket);
|
|
console.error(`[socket-error] ${socket.remoteAddress}:${socket.remotePort} ${error.message}`);
|
|
});
|
|
});
|
|
|
|
server.on('error', (error) => {
|
|
console.error(`[server-error] ${error.message}`);
|
|
process.exitCode = 1;
|
|
});
|
|
|
|
server.listen(config.port, config.host, () => {
|
|
console.log(`IRC server listening on ${config.host}:${config.port}`);
|
|
});
|
|
|