Created
June 29, 2026 16:14
-
-
Save EncodeTheCode/401fa6d39e2d043af46d4ebe241c3dec to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| class NetClient { | |
| constructor(url) { | |
| this.url = url; | |
| this.ws = null; | |
| this.connected = false; | |
| this.authenticated = false; | |
| this.seq = 0; | |
| // keep-alive system | |
| this.lastServerPing = Date.now(); | |
| this.heartbeatInterval = null; | |
| this.reconnectAttempts = 0; | |
| } | |
| // ========================= | |
| // CONNECT WITH AUTO-RETRY | |
| // ========================= | |
| connect() { | |
| this.ws = new WebSocket(this.url); | |
| this.ws.onopen = () => { | |
| this.connected = true; | |
| this.reconnectAttempts = 0; | |
| console.log("Connected"); | |
| this.startHeartbeat(); | |
| }; | |
| this.ws.onmessage = (e) => { | |
| const msg = JSON.parse(e.data); | |
| this.onMessage(msg); | |
| // server activity update (keep-alive tracking) | |
| this.lastServerPing = Date.now(); | |
| }; | |
| this.ws.onclose = () => { | |
| this.connected = false; | |
| this.authenticated = false; | |
| console.log("Disconnected — reconnecting..."); | |
| this.stopHeartbeat(); | |
| this.reconnect(); | |
| }; | |
| this.ws.onerror = () => { | |
| this.ws.close(); | |
| }; | |
| } | |
| // ========================= | |
| // AUTO RECONNECT (EXPONENTIAL BACKOFF) | |
| // ========================= | |
| reconnect() { | |
| const delay = Math.min(1000 * (2 ** this.reconnectAttempts), 10000); | |
| setTimeout(() => { | |
| this.reconnectAttempts++; | |
| console.log("Reconnecting attempt:", this.reconnectAttempts); | |
| this.connect(); | |
| }, delay); | |
| } | |
| // ========================= | |
| // SEND PACKET | |
| // ========================= | |
| send(data) { | |
| if (!this.connected) return; | |
| this.ws.send(JSON.stringify(data)); | |
| } | |
| // ========================= | |
| // HEARTBEAT SYSTEM (KEEP ALIVE) | |
| // ========================= | |
| startHeartbeat() { | |
| this.heartbeatInterval = setInterval(() => { | |
| // send ping heartbeat | |
| this.send({ | |
| type: "ping", | |
| t: Date.now() | |
| }); | |
| // detect server silence | |
| if (Date.now() - this.lastServerPing > 5000) { | |
| console.log("Server timeout detected, reconnecting..."); | |
| this.ws.close(); | |
| } | |
| }, 1000); | |
| } | |
| stopHeartbeat() { | |
| clearInterval(this.heartbeatInterval); | |
| } | |
| // ========================= | |
| // MESSAGE HANDLER (OVERWRITEABLE) | |
| // ========================= | |
| onMessage(msg) { | |
| if (msg.type === "auth_ok") { | |
| this.authenticated = true; | |
| console.log("Authenticated:", msg.user_id); | |
| } | |
| } | |
| } | |
| // ======================================================= | |
| // 🧍 PLAYER STATE | |
| // ======================================================= | |
| class Player { | |
| constructor() { | |
| this.user_id = null; | |
| this.x = this.y = this.z = 0; | |
| this.px = this.py = this.pz = 0; | |
| this.health = 100; | |
| this.armor = 0; | |
| this.weapon_id = 0; | |
| this.primary_ammo = 30; | |
| this.secondary_ammo = 90; | |
| this.walking = false; | |
| this.running = false; | |
| } | |
| applyServerState(s) { | |
| this.px = this.x; | |
| this.py = this.y; | |
| this.pz = this.z; | |
| this.x = s.x; | |
| this.y = s.y; | |
| this.z = s.z; | |
| this.health = s.health; | |
| this.armor = s.armor; | |
| this.weapon_id = s.weapon_id; | |
| this.primary_ammo = s.primary_ammo; | |
| this.secondary_ammo = s.secondary_ammo; | |
| this.walking = s.walking; | |
| this.running = s.running; | |
| this.user_id = s.user_id; | |
| } | |
| } | |
| // ======================================================= | |
| // 🎮 GAME CLIENT (FULL SYSTEM) | |
| // ======================================================= | |
| class GameClient { | |
| constructor(serverUrl) { | |
| this.net = new NetClient(serverUrl); | |
| this.player = new Player(); | |
| this.world = {}; | |
| this.input = {}; | |
| } | |
| start() { | |
| this.net.connect(); | |
| this.setupNetworking(); | |
| this.startLoops(); | |
| this.gameLoop(); | |
| } | |
| // ========================= | |
| // NETWORK HANDLER | |
| // ========================= | |
| setupNetworking() { | |
| this.net.onMessage = (msg) => { | |
| if (msg.type === "world") { | |
| this.world = msg.players; | |
| for (const id in msg.players) { | |
| const p = msg.players[id]; | |
| if (p.user_id === this.player.user_id) { | |
| this.player.applyServerState(p); | |
| } | |
| } | |
| } | |
| if (msg.type === "auth_ok") { | |
| this.net.authenticated = true; | |
| this.player.user_id = msg.user_id; | |
| } | |
| }; | |
| } | |
| // ========================= | |
| // SEND MOVEMENT | |
| // ========================= | |
| sendMovement(dx, dy, dz) { | |
| this.net.send({ | |
| type: "update", | |
| seq: this.net.seq++, | |
| dx, dy, dz, | |
| walking: this.player.walking, | |
| running: this.player.running | |
| }); | |
| } | |
| // ========================= | |
| // PING (EXTRA SAFETY LAYER) | |
| // ========================= | |
| sendPing() { | |
| this.net.send({ | |
| type: "ping", | |
| t: Date.now() | |
| }); | |
| } | |
| // ========================= | |
| // GAME LOOP | |
| // ========================= | |
| gameLoop() { | |
| const loop = () => { | |
| if (this.net.connected && this.net.authenticated) { | |
| // example movement tick (replace with real input system) | |
| this.sendMovement(0, 0, 0); | |
| } | |
| requestAnimationFrame(loop); | |
| }; | |
| loop(); | |
| } | |
| // ========================= | |
| // INTERVAL SYSTEMS | |
| // ========================= | |
| startLoops() { | |
| // keep-alive ping | |
| setInterval(() => { | |
| this.sendPing(); | |
| }, 1000); | |
| } | |
| } | |
| // ======================================================= | |
| // 🚀 BOOT CLIENT | |
| // ======================================================= | |
| const game = new GameClient("ws://localhost:4445"); | |
| game.start(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment