File size: 2,667 Bytes
40a9423
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
import http from "node:http";
import fs from "node:fs";
import path from "node:path";
import { WebSocketServer } from "ws";

const PORT = 8787;
const ROOT = path.resolve(process.cwd());

const server = http.createServer((req, res) => {
  const url = req.url === "/" ? "/index.html" : req.url;
  const filePath = path.join(ROOT, url);

  fs.readFile(filePath, (err, data) => {
    if (err) {
      res.writeHead(404, { "Content-Type": "text/plain" });
      res.end("Not found");
      return;
    }

    const ext = path.extname(filePath);
    const typeMap = {
      ".html": "text/html",
      ".css": "text/css",
      ".js": "text/javascript"
    };
    res.writeHead(200, { "Content-Type": typeMap[ext] ?? "text/plain" });
    res.end(data);
  });
});

const wss = new WebSocketServer({ server });

const palette = ["#4ed1ff", "#ffb347", "#7cff6b", "#ff6be5", "#9f8bff", "#ffd86b"];
let paletteIndex = 0;

const players = new Map();

const broadcast = (payload) => {
  const message = JSON.stringify(payload);
  for (const client of wss.clients) {
    if (client.readyState === 1) {
      client.send(message);
    }
  }
};

wss.on("connection", (socket) => {
  let playerId = null;

  socket.on("message", (data) => {
    let message;
    try {
      message = JSON.parse(data.toString());
    } catch {
      return;
    }

    if (message.type === "join") {
      playerId = `p_${Math.random().toString(36).slice(2, 9)}`;
      const color = palette[paletteIndex % palette.length];
      paletteIndex += 1;
      const name = String(message.name || "Pilot").slice(0, 14);
      players.set(playerId, {
        id: playerId,
        name,
        color,
        x: 0,
        y: 0,
        score: 0,
        shield: 100,
        wave: 1,
        lastSeen: Date.now()
      });
      socket.send(JSON.stringify({ type: "welcome", id: playerId, color }));
      return;
    }

    if (message.type === "state" && playerId && players.has(playerId)) {
      const player = players.get(playerId);
      player.x = message.x;
      player.y = message.y;
      player.score = message.score;
      player.shield = message.shield;
      player.wave = message.wave;
      player.lastSeen = Date.now();
    }
  });

  socket.on("close", () => {
    if (playerId) {
      players.delete(playerId);
    }
  });
});

setInterval(() => {
  const now = Date.now();
  for (const [id, player] of players.entries()) {
    if (now - player.lastSeen > 8000) {
      players.delete(id);
    }
  }
  broadcast({ type: "roster", players: Array.from(players.values()) });
}, 120);

server.listen(PORT, () => {
  console.log(`Star Courier running on http://localhost:${PORT}`);
});