Private
Public Access
1
0
Files
it232Abschied/static/js/network.js
Sebastian Unterschütz 669c783a06
All checks were successful
Dynamic Branch Deploy / build-and-deploy (push) Successful in 2m18s
add music, better sync, particles
2025-11-29 23:37:57 +01:00

377 lines
13 KiB
JavaScript

// ==========================================
// NETZWERK LOGIK (WEBSOCKET + RTT SYNC)
// ==========================================
/*
GLOBALE VARIABLEN (aus state.js):
- socket
- obstacleBuffer, platformBuffer
- currentLatencyMs, pingInterval
- isGameRunning, isGameOver
- score, currentTick
*/
function connectGame() {
// Alte Verbindung schließen
if (socket) {
socket.close();
}
// Ping Timer stoppen falls aktiv
if (typeof pingInterval !== 'undefined' && pingInterval) {
clearInterval(pingInterval);
}
// Protokoll automatisch wählen (ws:// oder wss://)
const proto = location.protocol === 'https:' ? 'wss:' : 'ws:';
const url = proto + "//" + location.host + "/ws";
console.log("Verbinde zu:", url);
socket = new WebSocket(url);
// --- 1. VERBINDUNG GEÖFFNET ---
socket.onopen = () => {
console.log("🟢 WS Verbunden. Spiel startet.");
// Alles zurücksetzen
obstacleBuffer = [];
platformBuffer = [];
obstacles = [];
platforms = [];
currentLatencyMs = 0; // Reset Latenz
isGameRunning = true;
isGameOver = false;
isLoaded = true;
// PING LOOP STARTEN (Jede Sekunde messen)
pingInterval = setInterval(sendPing, 1000);
// Game Loop anwerfen
requestAnimationFrame(gameLoop);
};
// --- 2. NACHRICHT VOM SERVER ---
socket.onmessage = (event) => {
try {
const msg = JSON.parse(event.data);
// A. PONG (Latenzmessung)
if (msg.type === "pong") {
const now = Date.now();
const sentTime = msg.ts; // Server schickt unseren Timestamp zurück
// Round Trip Time (Hin + Zurück)
const rtt = now - sentTime;
// One Way Latency (Latenz in eine Richtung)
const latency = rtt / 2;
// Glätten (Exponential Moving Average), damit Werte nicht springen
// Wenn es der erste Wert ist, nehmen wir ihn direkt.
if (currentLatencyMs === 0) {
currentLatencyMs = latency;
} else {
// 90% alter Wert, 10% neuer Wert
currentLatencyMs = (currentLatencyMs * 0.9) + (latency * 0.1);
}
// Optional: Debugging im Log
// console.log(`📡 Ping: ${rtt}ms | Latenz: ${currentLatencyMs.toFixed(1)}ms`);
}
// B. CHUNK (Objekte empfangen)
if (msg.type === "chunk") {
// 1. CLOCK SYNC (Die Zeitmaschine)
// Wenn der Server bei Tick 204 ist und wir bei 182, müssen wir aufholen!
// Wir addieren die geschätzte Latenz (in Ticks) auf die Serverzeit.
// 60 FPS = 16ms/Tick. 20 TPS = 50ms/Tick.
const msPerTick = 1000 / 20; // WICHTIG: Wir laufen auf 20 TPS Basis!
const latencyInTicks = Math.floor(currentLatencyMs / msPerTick);
// Ziel-Zeit: Server-Zeit + Übertragungsweg
const targetTick = msg.serverTick + latencyInTicks;
const drift = targetTick - currentTick;
// Wenn wir mehr als 2 Ticks abweichen -> Korrigieren
if (Math.abs(drift) > 2) {
// console.log(`⏰ Clock Sync: ${currentTick} -> ${targetTick} (Drift: ${drift})`);
currentTick = targetTick; // Harter Sync, damit Physik stimmt
}
// 2. PIXEL KORREKTUR (Sanfter!)
// Wir berechnen den Speed
let sTick = msg.serverTick;
// Formel aus logic.js (Base 15 + Zeit)
let currentSpeedPerTick = 15.0 + (sTick / 1000.0) * 1.5;
if (currentSpeedPerTick > 36) currentSpeedPerTick = 36;
const speedPerMs = currentSpeedPerTick / msPerTick; // Speed pro MS
// Korrektur: Latenz * Speed
// FIX: Wir kappen die Korrektur bei max 100px, damit Objekte nicht "teleportieren".
let dynamicCorrection = (currentLatencyMs * speedPerMs) + 5;
if (dynamicCorrection > 100) dynamicCorrection = 100; // Limit
// Puffer füllen (mit Limit)
if (msg.obstacles) {
msg.obstacles.forEach(o => {
o.x -= dynamicCorrection;
// Init für Interpolation
o.prevX = o.x;
obstacleBuffer.push(o);
});
}
if (msg.platforms) {
msg.platforms.forEach(p => {
p.x -= dynamicCorrection;
p.prevX = p.x;
platformBuffer.push(p);
});
}
if (msg.score !== undefined) score = msg.score;
// Powerups übernehmen (für Anzeige)
if (msg.powerups) {
godModeLives = msg.powerups.godLives;
hasBat = msg.powerups.hasBat;
bootTicks = msg.powerups.bootTicks;
}
}
if (msg.type === "init") {
console.log("📩 INIT EMPFANGEN:", msg); // <--- DEBUG LOG
if (msg.sessionId) {
sessionID = msg.sessionId; // Globale Variable setzen
console.log("🔑 Session ID gesetzt auf:", sessionID);
} else {
console.error("❌ INIT FEHLER: Keine sessionId im Paket!", msg);
}
}
// C. TOD (Server Authoritative)
if (msg.type === "dead") {
console.log("💀 Server sagt: Game Over");
if (msg.score) score = msg.score;
// Verbindung sauber trennen
socket.close();
if (pingInterval) clearInterval(pingInterval);
gameOver("Vom Server gestoppt");
}
if (msg.type === "debug_sync") {
// 1. CLIENT SPEED BERECHNEN (Formel aus logic.js)
// Wir nutzen hier 'score', da logic.js das auch tut
let clientSpeed = BASE_SPEED + (currentTick / 1000.0) * 1.5;
if (clientSpeed > 36.0) clientSpeed = 36.0;
// 2. SERVER SPEED HOLEN
let serverSpeed = msg.currentSpeed || 0;
// 3. DIFF BERECHNEN
let diffSpeed = clientSpeed - serverSpeed;
let speedIcon = Math.abs(diffSpeed) < 0.01 ? "✅" : "❌";
console.group(`📊 SYNC REPORT (Tick: ${currentTick} vs Server: ${msg.serverTick})`);
// --- DER NEUE SPEED CHECK ---
console.log(`🚀 SPEED CHECK: ${speedIcon}`);
console.log(` Client: ${clientSpeed.toFixed(4)} px/tick (Basis: Tick ${currentTick})`);
console.log(` Server: ${serverSpeed.toFixed(4)} px/tick (Basis: Tick ${msg.serverTick})`);
if (Math.abs(diffSpeed) > 0.01) {
console.warn(`⚠️ ACHTUNG: Geschwindigkeiten weichen ab! Diff: ${diffSpeed.toFixed(4)}`);
console.warn("Ursache: Client nutzt 'Score', Server nutzt 'Ticks'. Sind diese synchron?");
}
// -----------------------------
// 1. Hindernisse vergleichen
generateSyncTable("Obstacles", obstacles, msg.obstacles);
// 2. Plattformen vergleichen
generateSyncTable("Platforms", platforms, msg.platforms);
console.groupEnd();
}
} catch (e) {
console.error("Fehler beim Verarbeiten der Nachricht:", e);
}
};
// --- 3. VERBINDUNG GETRENNT ---
socket.onclose = () => {
console.log("🔴 WS Verbindung getrennt.");
if (pingInterval) clearInterval(pingInterval);
};
socket.onerror = (error) => {
console.error("WS Fehler:", error);
};
}
// ==========================================
// PING SENDEN
// ==========================================
function sendPing() {
if (socket && socket.readyState === WebSocket.OPEN) {
// Wir senden den aktuellen Zeitstempel
// Der Server muss diesen im "tick" Feld zurückschicken (siehe websocket.go)
socket.send(JSON.stringify({
type: "ping",
tick: Date.now() // Timestamp als Integer
}));
}
}
// ==========================================
// INPUT SENDEN
// ==========================================
function sendInput(type, action) {
if (socket && socket.readyState === WebSocket.OPEN) {
socket.send(JSON.stringify({
type: "input",
input: action
}));
}
}
// Helper für die Tabelle
function generateSyncTable(label, clientList, serverList) {
if (!serverList) serverList = [];
console.log(`--- ${label} Analyse (Ping: ${Math.round(currentLatencyMs)}ms) ---`);
const report = [];
const matchedServerIndices = new Set();
// 1. Parameter für Latenz-Korrektur berechnen
// Damit wir wissen: "Wo MÜSSTE das Server-Objekt auf dem Client sein?"
const msPerTick = 50; // Bei 20 TPS
// Speed Schätzung (gleiche Formel wie in logic.js)
let debugSpeed = 15.0 + (score / 1000.0) * 1.5;
if (debugSpeed > 36) debugSpeed = 36;
const speedPerMs = debugSpeed / msPerTick;
// Pixel, die das Objekt wegen Ping weiter "links" sein müsste
const latencyPx = currentLatencyMs * speedPerMs;
// 2. Client Objekte durchgehen
clientList.forEach((cObj) => {
let bestMatch = null;
let bestDist = 9999;
let bestSIdx = -1;
// ID sicherstellen
const cID = cObj.def ? cObj.def.id : (cObj.id || "unknown");
// Passendes Server-Objekt suchen
serverList.forEach((sObj, sIdx) => {
if (matchedServerIndices.has(sIdx)) return;
const sID = sObj.id || "unknown";
// Match Kriterien:
// 1. Gleiche ID (oder Plattform)
// 2. Nähe (Wir vergleichen hier die korrigierte Position!)
const sPosCorrected = sObj.x - latencyPx;
const dist = Math.abs(cObj.x - sPosCorrected);
const isTypeMatch = (label === "Platforms") || (cID === sID);
// Toleranter Suchradius (500px), falls Drift groß ist
if (isTypeMatch && dist < bestDist && dist < 500) {
bestDist = dist;
bestMatch = sObj;
bestSIdx = sIdx;
}
});
// Datenzeile bauen
let serverXRaw = "---";
let serverXCorrected = "---";
let diffReal = "---";
let status = "👻 GHOST (Client only)";
if (bestMatch) {
matchedServerIndices.add(bestSIdx);
serverXRaw = bestMatch.x;
serverXCorrected = bestMatch.x - latencyPx; // Hier rechnen wir den Ping raus
// Der "Wahrs" Drift: Differenz nach Latenz-Abzug
diffReal = cObj.x - serverXCorrected;
// Status Bestimmung
const absDiff = Math.abs(diffReal);
if (absDiff < 20) status = "✅ PERFECT";
else if (absDiff < 60) status = "🆗 OK";
else if (absDiff < 150) status = "⚠️ DRIFT";
else status = "🔥 BROKEN";
}
report.push({
"ID": cID,
"Client X": Math.round(cObj.x),
"Server X (Raw)": Math.round(serverXRaw),
"Server X (Sim)": Math.round(serverXCorrected), // Wo es sein sollte
"Diff (Real)": typeof diffReal === 'number' ? Math.round(diffReal) : "---",
"Status": status
});
});
// 3. Fehlende Server Objekte finden
serverList.forEach((sObj, sIdx) => {
if (!matchedServerIndices.has(sIdx)) {
// Prüfen, ob es vielleicht einfach noch unsichtbar ist (Zukunft)
const sPosCorrected = sObj.x - latencyPx;
let status = "❌ MISSING";
if (sPosCorrected > 850) status = "🔮 FUTURE (Buffer)"; // Noch rechts vom Screen
if (sPosCorrected < -100) status = "🗑️ OLD (Server lag)"; // Schon links raus
report.push({
"ID": sObj.id || "?",
"Client X": "---",
"Server X (Raw)": Math.round(sObj.x),
"Server X (Sim)": Math.round(sPosCorrected),
"Diff (Real)": "---",
"Status": status
});
}
});
// 4. Sortieren nach Position (links nach rechts)
report.sort((a, b) => {
const valA = (typeof a["Client X"] === 'number') ? a["Client X"] : a["Server X (Sim)"];
const valB = (typeof b["Client X"] === 'number') ? b["Client X"] : b["Server X (Sim)"];
return valA - valB;
});
if (report.length > 0) console.table(report);
else console.log("Leer.");
}
function sendPhysicsSync(y, vy) {
if (socket && socket.readyState === WebSocket.OPEN) {
socket.send(JSON.stringify({
type: "sync",
y: y,
vy: vy,
tick: currentTick
}));
}
}