3 Commits

Author SHA1 Message Date
shwzss 429d4ed1a4 Add files via upload 2026-09-02 22:14:05 -05:00
shwzss a7530eb87b Remove project details from README.md
Removed detailed project description and setup instructions from README.md.
2026-09-02 20:12:59 -07:00
shwzss 3a1e82415c Merge pull request #1 from shwzss/custom-node-server
Add Node.js server skeleton with auth, persistence, and admin console…
2026-09-02 22:11:59 -05:00
5 changed files with 58 additions and 64 deletions
-9
View File
@@ -1,14 +1,5 @@
# .env.example
# Port the server listens on (client must connect to this)
PORT=5055
# Logging: error, warn, info, verbose, debug, silly
LOG_LEVEL=info
# Steam/RecNet
STEAM_WEB_API_KEY=
RECNET_VERIFY_URL=
# SQLite DB file
SQLITE_FILE=./helium.db
-31
View File
@@ -1,32 +1 @@
# HeliumRecServer (custom-node-server branch)
This branch adds a Node.js skeleton for running a custom Rec Room (2023) server replacement that you can adapt to work with the Photon-based client by swapping keys/addresses in the client.
What I added in this commit:
- WebSocket server skeleton (src/index.js) that accepts query-token authentication and persists minimal user data
- Protocol stub (src/protocol.js) — where Rec Room / Photon packet parsing should be implemented
- Auth helpers (src/auth.js) for Steam/RecNet token verification (placeholders that call configured endpoints)
- SQLite persistence (src/storage.js)
- Logger and test client
- package.json with dependencies
Environment variables (set in .env):
- PORT (default 5055) — WebSocket server port
- LOG_LEVEL — winston log level (info/debug/...)
- STEAM_WEB_API_KEY — Steam Web API key (if you plan to use Steam verification)
- RECNET_VERIFY_URL — RecNet verification endpoint to POST { token }
- SQLITE_FILE — path for the SQLite DB file (default ./helium.db)
How to run:
1. Copy `.env.example` to `.env` and fill in the variables you need.
2. npm install
3. npm start
Notes about Photon & Rec Room:
- The client uses Photon; this skeleton provides a WebSocket entry point you can use for reverse engineering and testing. Photon traffic is binary and has its own framing — you'll need to implement Photon protocol handling inside src/protocol.js (or add a UDP/TCP transport if Photon expects that).
- You said you can replace Photon keys & localhost yourself — point the client at this process and replace any required AppId/AppVersion in the client.
Next steps I can take after you confirm:
- Implement Photon framing and basic handshake to accept a 2023 Rec Room client (requires packet captures or protocol docs).
- Implement Steam/RecNet direct verification flow if you provide endpoints or tokens to test.
- Add more persistence (Redis) and matchmaking logic.
+1 -3
View File
@@ -10,8 +10,6 @@
"dependencies": {
"ws": "^8.13.0",
"dotenv": "^16.0.0",
"winston": "^3.8.0",
"axios": "^1.4.0",
"sqlite3": "^5.1.6"
"winston": "^3.8.0"
}
}
+49 -4
View File
@@ -1,5 +1,50 @@
// The admin console was intentionally removed from the project per repository owner request.
// If you need to restore it later, re-add an admin HTTP server (e.g., using express) and
// provide ADMIN_USER / ADMIN_PASS environment variables for basic auth.
const express = require('express');
const basicAuth = require('basic-auth');
const logger = require('./logger');
module.exports = {};
let serverInstance;
function ensureAuth(req, res, next) {
if (!process.env.ADMIN_USER || !process.env.ADMIN_PASS) return next();
const user = basicAuth(req);
if (!user || user.name !== process.env.ADMIN_USER || user.pass !== process.env.ADMIN_PASS) {
res.set('WWW-Authenticate', 'Basic realm="HeliumRecAdmin"');
return res.status(401).send('Authentication required');
}
return next();
}
function start({ port = 5056, getConnections }) {
const app = express();
app.use(express.json());
app.get('/health', (req, res) => res.json({ ok: true }));
app.use(ensureAuth);
app.get('/connections', (req, res) => {
const conns = [];
const map = getConnections();
map.forEach((v, k) => {
conns.push({ id: k, user: v.user, connectedAt: v.connectedAt, remote: v.ws?._meta?.remote });
});
res.json(conns);
});
app.post('/broadcast', (req, res) => {
const { message } = req.body || {};
if (!message) return res.status(400).json({ error: 'message required' });
const map = getConnections();
let sent = 0;
map.forEach((v) => {
try { v.ws.send(typeof message === 'string' ? message : JSON.stringify(message)); sent++; } catch (e) { logger.warn('broadcast failed', e.message); }
});
res.json({ sent });
});
serverInstance = app.listen(port, () => logger.info(`Admin console listening on http://0.0.0.0:${port}`));
}
function stop() { if (serverInstance) serverInstance.close(); }
module.exports = { start, stop };
+6 -15
View File
@@ -8,28 +8,16 @@ const logger = require('./logger');
const protocol = require('./protocol');
const auth = require('./auth');
const storage = require('./storage');
const admin = require('./admin');
const PORT = process.env.PORT || 5055;
const ADMIN_PORT = process.env.ADMIN_PORT || 5056;
(async () => {
await storage.init();
// Start WebSocket server on its own HTTP server so we can inspect req.url
const server = http.createServer((req, res) => {
// Provide a minimal informational page for HTTP GETs so browsers don't see "Cannot GET /"
if (req.method === 'GET' && (req.url === '/' || req.url === '/index.html')) {
res.writeHead(200, { 'Content-Type': 'text/html' });
res.end(`<html><body>
<h1>HeliumRecServer</h1>
<p>This port is a WebSocket endpoint. Connect using a WebSocket client.</p>
<p>Server listens on ws://0.0.0.0:${PORT}</p>
</body></html>`);
return;
}
// leave other requests to return 404
res.writeHead(404); res.end('Not Found');
});
const server = http.createServer();
const wss = new WebSocket.Server({ noServer: true });
const connections = new Map(); // key -> { ws, user }
@@ -91,4 +79,7 @@ const PORT = process.env.PORT || 5055;
});
server.listen(PORT, () => logger.info(`HeliumRecServer WebSocket listening on ws://0.0.0.0:${PORT}`));
// Start admin console (HTTP) bound to ADMIN_PORT
admin.start({ port: ADMIN_PORT, getConnections: () => connections });
})();