feat: full de-Angularization with Bootstrap 5 migration
- Removed AngularJS 1.4.8 completely — no more framework dependency - Extracted core RCON logic into pure vanilla JS (js/rcon.js) - Added lightweight hash-based SPA router (js/router.js) - Rewrote all 6 pages as vanilla JS modules in js/pages/ - Migrated Bootstrap 3 → Bootstrap 5 (cards instead of panels, bootstrap-icons instead of glyphicons) - Removed jQuery dependency (Bootstrap 5 is vanilla JS) - Purged all Angular templates from html/ - Removed npm artifacts (package.json, node_modules) - Added nginx.example.conf for static hosting - Updated rcon.css dark theme for BS5 variables - Static file only — no Node.js backend required Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
+181
@@ -0,0 +1,181 @@
|
||||
/**
|
||||
* Rust WebSocket RCON Client — pure vanilla JS, zero dependencies.
|
||||
*
|
||||
* Protocol:
|
||||
* - Connect: ws://host:port/password
|
||||
* - Outgoing: { Identifier: int, Message: string, Name: "WebRcon" }
|
||||
* - Identifier > 1000 → request/response (callback tracked)
|
||||
* - Identifier <= 1000 → unsolicited server push (Generic/Log/Error/Warning/Chat)
|
||||
*
|
||||
* Usage:
|
||||
* Rcon.on('connected', () => { ... });
|
||||
* Rcon.on('disconnected', (ev) => { ... });
|
||||
* Rcon.on('message', (msg) => { ... });
|
||||
* Rcon.connect('192.168.1.100:28016', 'mypassword');
|
||||
* Rcon.request('serverinfo', (msg) => { console.log(JSON.parse(msg.Message)); });
|
||||
* Rcon.command('say Hello world');
|
||||
* Rcon.getPlayers((players) => { ... });
|
||||
*/
|
||||
var Rcon = (function () {
|
||||
'use strict';
|
||||
|
||||
var ConnectionStatus = {
|
||||
CONNECTING: 0,
|
||||
OPEN: 1,
|
||||
CLOSING: 2,
|
||||
CLOSED: 3
|
||||
};
|
||||
|
||||
var socket = null;
|
||||
var address = null;
|
||||
var callbacks = {};
|
||||
var lastIndex = 1001;
|
||||
var listeners = {};
|
||||
|
||||
/**
|
||||
* Simple pub/sub event emitter.
|
||||
*/
|
||||
function emit(event, data) {
|
||||
var cbs = listeners[event];
|
||||
if (cbs) {
|
||||
for (var i = 0; i < cbs.length; i++) {
|
||||
cbs[i](data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect to a Rust server's WebSocket RCON.
|
||||
* @param {string} addr - "host:port"
|
||||
* @param {string} pass - RCON password
|
||||
*/
|
||||
function connect(addr, pass) {
|
||||
disconnect();
|
||||
address = addr;
|
||||
socket = new WebSocket('ws://' + addr + '/' + pass);
|
||||
|
||||
socket.onopen = function () {
|
||||
emit('connected');
|
||||
};
|
||||
|
||||
socket.onclose = function (ev) {
|
||||
emit('disconnected', ev);
|
||||
};
|
||||
|
||||
socket.onerror = function (ev) {
|
||||
emit('error', ev);
|
||||
};
|
||||
|
||||
socket.onmessage = function (e) {
|
||||
var data = JSON.parse(e.data);
|
||||
|
||||
// Targeted response — match to pending callback
|
||||
if (data.Identifier > 1000) {
|
||||
var cb = callbacks[data.Identifier];
|
||||
if (cb) {
|
||||
cb(data);
|
||||
}
|
||||
delete callbacks[data.Identifier];
|
||||
return;
|
||||
}
|
||||
|
||||
// Unsolicited server push (console, chat, etc.)
|
||||
emit('message', data);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Disconnect from the server.
|
||||
*/
|
||||
function disconnect() {
|
||||
callbacks = {};
|
||||
if (socket) {
|
||||
socket.close();
|
||||
socket = null;
|
||||
}
|
||||
address = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a fire-and-forget command (no response expected).
|
||||
* @param {string} msg - RCON command
|
||||
* @param {number} [identifier] - Defaults to -1
|
||||
*/
|
||||
function command(msg, identifier) {
|
||||
if (!socket || !isConnected()) return;
|
||||
if (identifier === undefined || identifier === null) {
|
||||
identifier = -1;
|
||||
}
|
||||
socket.send(JSON.stringify({
|
||||
Identifier: identifier,
|
||||
Message: msg,
|
||||
Name: 'WebRcon'
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a command and invoke callback with the response.
|
||||
* @param {string} msg - RCON command
|
||||
* @param {function} cb - callback(response)
|
||||
*/
|
||||
function request(msg, cb) {
|
||||
lastIndex++;
|
||||
callbacks[lastIndex] = cb;
|
||||
command(msg, lastIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the WebSocket is open.
|
||||
*/
|
||||
function isConnected() {
|
||||
return socket !== null && socket.readyState === ConnectionStatus.OPEN;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current connected address (host:port).
|
||||
*/
|
||||
function getAddress() {
|
||||
return address;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience: fetch the player list.
|
||||
* @param {function} cb - callback(playersArray)
|
||||
*/
|
||||
function getPlayers(cb) {
|
||||
request('playerlist', function (response) {
|
||||
cb(JSON.parse(response.Message));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Register an event listener.
|
||||
* Events: 'connected', 'disconnected', 'error', 'message'
|
||||
*/
|
||||
function on(event, cb) {
|
||||
if (!listeners[event]) listeners[event] = [];
|
||||
listeners[event].push(cb);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an event listener.
|
||||
*/
|
||||
function off(event, cb) {
|
||||
var cbs = listeners[event];
|
||||
if (cbs) {
|
||||
listeners[event] = cbs.filter(function (f) { return f !== cb; });
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
connect: connect,
|
||||
disconnect: disconnect,
|
||||
command: command,
|
||||
request: request,
|
||||
isConnected: isConnected,
|
||||
getAddress: getAddress,
|
||||
getPlayers: getPlayers,
|
||||
on: on,
|
||||
off: off
|
||||
};
|
||||
})();
|
||||
Reference in New Issue
Block a user