Stuff and junk

This commit is contained in:
Kim Ravn Hansen
2025-09-02 18:49:42 +02:00
parent e588bb11c3
commit e12076260d
2 changed files with 349 additions and 9 deletions

269
server/public/index.html Executable file
View File

@@ -0,0 +1,269 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>WebSocket MUD</title>
<style>
body {
font-family: 'Courier New', monospace;
background-color: #1a1a1a;
color: #00ff00;
margin: 0;
padding: 0;
height: 100vh;
display: flex;
flex-direction: column;
}
#container {
display: flex;
flex-direction: column;
height: 100vh;
max-width: 1200px;
margin: 0 auto;
padding: 10px;
}
#output {
flex: 1;
background-color: #000;
border: 2px solid #333;
padding: 15px;
overflow-y: auto;
white-space: pre-wrap;
font-size: 14px;
line-height: 1.4;
margin-bottom: 10px;
}
#input-container {
display: flex;
gap: 10px;
}
#input {
flex: 1;
background-color: #222;
border: 2px solid #333;
color: #00ff00;
padding: 10px;
font-family: 'Courier New', monospace;
font-size: 14px;
}
#input:focus {
outline: none;
border-color: #00ff00;
}
#send {
background-color: #333;
border: 2px solid #555;
color: #00ff00;
padding: 10px 20px;
font-family: 'Courier New', monospace;
cursor: pointer;
}
#send:hover {
background-color: #444;
}
#status {
background-color: #333;
padding: 5px 15px;
margin-bottom: 10px;
border-radius: 3px;
}
.connected {
color: #00ff00;
}
.disconnected {
color: #ff4444;
}
.connecting {
color: #ffaa00;
}
.error {
color: #ff4444;
}
.system {
color: #aaaaaa;
}
.prompt {
color: #00ccff;
}
</style>
</head>
<body>
<div id="container">
<div id="status" class="connecting">Connecting...</div>
<div id="output"></div>
<div id="input-container">
<input type="text" id="input" placeholder="Enter command..." disabled>
<button id="send" disabled>Send</button>
</div>
</div>
<script>
class MUDClient {
constructor() {
this.ws = null;
this.output = document.getElementById('output');
this.input = document.getElementById('input');
this.sendButton = document.getElementById('send');
this.status = document.getElementById('status');
this.setupEventListeners();
this.connect();
}
connect() {
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
const wsUrl = `${protocol}//${window.location.host}`;
this.updateStatus('Connecting...', 'connecting');
try {
this.ws = new WebSocket(wsUrl);
this.ws.onopen = () => {
this.updateStatus('Connected', 'connected');
this.input.disabled = false;
this.sendButton.disabled = false;
this.input.focus();
};
this.ws.onmessage = (event) => {
const data = JSON.parse(event.data);
this.handleMessage(data);
};
this.ws.onclose = () => {
this.updateStatus('Disconnected', 'disconnected');
this.input.disabled = true;
this.sendButton.disabled = true;
// Attempt to reconnect after 3 seconds
setTimeout(() => this.connect(), 3000);
};
this.ws.onerror = (error) => {
this.updateStatus('Connection Error', 'error');
this.appendOutput('Connection error occurred. Retrying...', 'error');
};
} catch (error) {
this.updateStatus('Connection Failed', 'error');
setTimeout(() => this.connect(), 3000);
}
}
setupEventListeners() {
this.input.addEventListener('keypress', (e) => {
if (e.key === 'Enter') {
this.sendMessage();
}
});
this.sendButton.addEventListener('click', () => {
this.sendMessage();
});
// Command history
this.commandHistory = [];
this.historyIndex = -1;
this.input.addEventListener('keydown', (e) => {
if (e.key === 'ArrowUp') {
e.preventDefault();
if (this.historyIndex < this.commandHistory.length - 1) {
this.historyIndex++;
this.input.value = this.commandHistory[this.commandHistory.length - 1 - this.historyIndex];
}
} else if (e.key === 'ArrowDown') {
e.preventDefault();
if (this.historyIndex > 0) {
this.historyIndex--;
this.input.value = this.commandHistory[this.commandHistory.length - 1 - this.historyIndex];
} else if (this.historyIndex === 0) {
this.historyIndex = -1;
this.input.value = '';
}
}
});
}
sendMessage() {
const message = this.input.value.trim();
if (message && this.ws && this.ws.readyState === WebSocket.OPEN) {
// Add to command history
if (this.commandHistory[this.commandHistory.length - 1] !== message) {
this.commandHistory.push(message);
if (this.commandHistory.length > 50) {
this.commandHistory.shift();
}
}
this.historyIndex = -1;
this.ws.send(JSON.stringify({
type: 'command',
content: message
}));
this.input.value = '';
}
}
handleMessage(data) {
switch (data.type) {
case 'message':
this.appendOutput(data.content);
break;
case 'error':
this.appendOutput(data.content, 'error');
break;
case 'system':
this.appendOutput(data.content, 'system');
break;
default:
this.appendOutput(data.content);
}
}
appendOutput(text, className = '') {
const div = document.createElement('div');
if (className) {
div.className = className;
}
// Check if this looks like a prompt
if (text.includes('] > ')) {
div.className = 'prompt';
}
div.textContent = text;
this.output.appendChild(div);
this.output.scrollTop = this.output.scrollHeight;
}
updateStatus(message, className) {
this.status.textContent = `Status: ${message}`;
this.status.className = className;
}
}
// Initialize the MUD client when the page loads
document.addEventListener('DOMContentLoaded', () => {
new MUDClient();
});
</script>
</body>
</html>

View File

@@ -3,7 +3,17 @@ const http = require('http');
const path = require('path'); const path = require('path');
const fs = require('fs'); const fs = require('fs');
/**
* Player
*
* @property WebSocket websocket
*/
class Player { class Player {
/**
*
* @param {String} name
* @param {WebSocket} websocket
*/
constructor(name, websocket) { constructor(name, websocket) {
this.name = name; this.name = name;
this.websocket = websocket; this.websocket = websocket;
@@ -13,6 +23,11 @@ class Player {
this.level = 1; this.level = 1;
} }
/***
* Send a message back to the client via the websocket.
*
* @param {string} message
*/
sendMessage(message) { sendMessage(message) {
if (this.websocket.readyState === WebSocket.OPEN) { if (this.websocket.readyState === WebSocket.OPEN) {
this.websocket.send(JSON.stringify({ this.websocket.send(JSON.stringify({
@@ -28,6 +43,13 @@ class Player {
} }
class Room { class Room {
/**
*
* @param {string} id
* @param {string} name
* @param {string} description
* @param {string[]} exits
*/
constructor(id, name, description, exits = {}) { constructor(id, name, description, exits = {}) {
this.id = id; this.id = id;
this.name = name; this.name = name;
@@ -38,22 +60,45 @@ class Room {
this.npcs = []; this.npcs = [];
} }
/**
* Add a player to the list of active players.
*
* (an active player is a player that currently has an active web socketA)
*
* @param {Player} player
*/
addPlayer(player) { addPlayer(player) {
this.players.add(player); this.players.add(player);
this.broadcastToRoom(`${player.name} enters the room.`, player); this.broadcastToRoom(`${player.name} enters the room.`, player);
} }
/**
* Remove a player from the list of active players.
*
* (an active player is a player that currently has an active web socketA)
*
* @param {Player} player
*/
removePlayer(player) { removePlayer(player) {
this.players.delete(player); this.players.delete(player);
this.broadcastToRoom(`${player.name} leaves the room.`, player); this.broadcastToRoom(`${player.name} leaves the room.`, player);
} }
/**
* Send a message to all other players in this room.
*
* @param {string} message
* @param {Player} excludePlayer A single player to exclude from the broadcast
*/
broadcastToRoom(message, excludePlayer = null) { broadcastToRoom(message, excludePlayer = null) {
for (const player of this.players) { // for (const player of this.players) {
if (player !== excludePlayer) { // if (player !== excludePlayer) {
// player.sendMessage(message);
// }
// }
this.getPlayersExcept(excludePlayer).forEach((player) => {
player.sendMessage(message); player.sendMessage(message);
} })
}
} }
getPlayersExcept(excludePlayer) { getPlayersExcept(excludePlayer) {
@@ -104,6 +149,10 @@ class MudServer {
this.rooms.set('deep_forest', deepForest); this.rooms.set('deep_forest', deepForest);
} }
/**
*
* @param {WebSocket} ws
*/
handleConnection(ws) { handleConnection(ws) {
console.log('New connection established'); console.log('New connection established');
@@ -126,6 +175,12 @@ class MudServer {
}); });
} }
/**
*
* @param {WebSocket} ws
* @param {strings} message
* @returns
*/
handleMessage(ws, message) { handleMessage(ws, message) {
const player = this.players.get(ws); const player = this.players.get(ws);
@@ -147,6 +202,11 @@ class MudServer {
this.processCommand(player, message.content.trim()); this.processCommand(player, message.content.trim());
} }
/**
*
* @param {WebSocket} ws
* @param {string} name
*/
createPlayer(ws, name) { createPlayer(ws, name) {
const player = new Player(name, ws); const player = new Player(name, ws);
this.players.set(ws, player); this.players.set(ws, player);
@@ -159,6 +219,11 @@ class MudServer {
this.showRoom(player); this.showRoom(player);
} }
/**
*
* @param {Player} player
* @param {string} input
*/
processCommand(player, input) { processCommand(player, input) {
const args = input.toLowerCase().split(' '); const args = input.toLowerCase().split(' ');
const command = args[0]; const command = args[0];
@@ -232,6 +297,12 @@ class MudServer {
player.sendPrompt(); player.sendPrompt();
} }
/**
*
* @param {Player} player
* @param {*} direction
* @returns
*/
movePlayer(player, direction) { movePlayer(player, direction) {
const currentRoom = this.rooms.get(player.currentRoom); const currentRoom = this.rooms.get(player.currentRoom);
const newRoomId = currentRoom.exits[direction]; const newRoomId = currentRoom.exits[direction];