server.js
const WebSocket = require('ws');
const { randomInt } = require('crypto'); // For generating random integers
const { promisify } = require('util');
const setTimeoutPromise = promisify(setTimeout);

const PORT = 25566;
const players = {};
const tileSize = 18;
const mapLayouts = [
    // Define map layouts here, example:
    [
        [1, 0, 1, 0],
        [0, 0, 0, 1],
        [1, 1, 0, 0],
        [0, 1, 1, 1],
    ],
    [
        [0, 1, 0, 1],
        [1, 0, 1, 0],
        [0, 0, 1, 1],
        [1, 1, 0, 0],
    ]
];

let currentMapLayout = mapLayouts[0];
const connections = [];

// Function to serialize data for sending to clients
function serialize(data) {
    return JSON.stringify(data);
}

// Function to get a random spawn position
function getRandomSpawnPosition() {
    const x = randomInt(0, currentMapLayout[0].length);
    const y = randomInt(0, currentMapLayout.length);
    return { x: x * tileSize, y: y * tileSize };
}

// Function to generate a random color
function generateColor() {
    return {
        r: randomInt(0, 256),
        g: randomInt(0, 256),
        b: randomInt(0, 256)
    };
}

// Function to change map layout periodically
async function changeMapLayoutPeriodically() {
    let mapIndex = 0;
    while (true) {
        await setTimeoutPromise(60000); // Change map layout every 60 seconds
        currentMapLayout = mapLayouts[mapIndex];
        mapIndex = (mapIndex + 1) % mapLayouts.length;

        console.log("Map layout changed.");

        // Send the new map layout to all connected clients
        connections.forEach(ws => {
            try {
                ws.send(serialize({ players, map_layout: currentMapLayout }));
            } catch (e) {
                console.error(`Error sending new map layout: ${e.message}`);
            }
        });
    }
}

// Create WebSocket server
const wss = new WebSocket.Server({ port: PORT });

wss.on('connection', (ws) => {
    console.log('Client connected');
    connections.push(ws);

    ws.on('message', (data) => {
        try {
            const message = JSON.parse(data);
            const playerName = message.name;

            // Generate a unique spawn position
            const spawnPosition = getRandomSpawnPosition();

            // Initialize new player with a unique color and position
            players[ws] = {
                name: playerName,
                position: spawnPosition,
                color: generateColor()
            };

            // Send initial positions and map layout to the new client
            ws.send(serialize({ players, map_layout: currentMapLayout }));

            // Handle player updates
            ws.on('message', (data) => {
                const update = JSON.parse(data);
                if (players[ws]) {
                    players[ws].position = update.position;

                    // Send updated positions and map layout to all clients
                    connections.forEach(playerWs => {
                        playerWs.send(serialize({ players, map_layout: currentMapLayout }));
                    });
                }
            });

        } catch (e) {
            console.error(`Error processing data: ${e.message}`);
        }
    });

    ws.on('close', () => {
        console.log('Client disconnected');
        delete players[ws];
        connections.splice(connections.indexOf(ws), 1);

        // Inform remaining clients about the disconnected player
        connections.forEach(playerWs => {
            playerWs.send(serialize({ players }));
        });
    });
});

// Start server and change map layout periodically
console.log(`WebSocket server running on ws://localhost:${PORT}`);
changeMapLayoutPeriodically();


game.js
import kaplay from "kaplay";
import "kaplay/global";

const k = kaplay({
    width: 640,
    height: 480,
    scale: 2,
});

const tileSize = 18;
const serverHost = 'localhost';
const serverPort = 25566;

let mapLayout = [];
let players = {};
let localPlayer = { position: { x: 0, y: 0 }, color: { r: 255, g: 255, b: 255 } };
let socket;

let tileSprites = [];
let playerSprites = [];

// Utility to deserialize data from server
function deserialize(data) {
    try {
        return JSON.parse(data);
    } catch (error) {
        console.error("Deserialization error:", error);
        return null;
    }
}

// Load sprites
loadSprite("tile", "/sprites/grass.png");
loadSprite("player", "/sprites/player.png");

scene("game", () => {
    function drawMap() {
        tileSprites.forEach(tile => tile.destroy()); // Clear previous tiles
        tileSprites = [];  // Reset tile sprites array

        mapLayout.forEach((row, y) => {
            row.forEach((tile, x) => {
                if (tile === 1) {
                    const tileSprite = add([
                        sprite("tile"),
                        pos(x * tileSize, y * tileSize),
                        area(),
                    ]);
                    tileSprites.push(tileSprite);  // Store the tile sprite
                }
            });
        });
    }

    function drawPlayers() {
        playerSprites.forEach(player => player.destroy()); // Clear previous player sprites
        playerSprites = [];  // Reset player sprites array

        Object.values(players).forEach(player => {
            const playerSprite = add([
                sprite("player"),
                pos(player.position.x, player.position.y),
                color(player.color.r, player.color.g, player.color.b),
                area(),
            ]);
            playerSprites.push(playerSprite);  // Store the player sprite
        });
    }

    // Connect to the server
    connectToServer();

    // Player movement
    onKeyDown("left", () => {
        localPlayer.position.x -= tileSize;
        sendPlayerUpdate();
    });
    onKeyDown("right", () => {
        localPlayer.position.x += tileSize;
        sendPlayerUpdate();
    });
    onKeyDown("up", () => {
        localPlayer.position.y -= tileSize;
        sendPlayerUpdate();
    });
    onKeyDown("down", () => {
        localPlayer.position.y += tileSize;
        sendPlayerUpdate();
    });

    // Send local player position to server
    function sendPlayerUpdate() {
        if (socket.readyState === WebSocket.OPEN) {
            socket.send(JSON.stringify({
                position: localPlayer.position,
            }));
        }
    }

    // Update player positions and redraw the map on each frame
    onUpdate(() => {
        drawPlayers();
    });

    // Connect to the server function
    function connectToServer() {
        socket = new WebSocket(`ws://${serverHost}:${serverPort}`);

        socket.addEventListener("open", () => {
            console.log("Connected to server");
            socket.send(JSON.stringify({ name: "Player1" }));  // You can add a dynamic name here
        });

        socket.addEventListener("message", (event) => {
            const data = deserialize(event.data);  // Directly use event.data
            if (!data) return;

            if (data.map_layout) {
                mapLayout = data.map_layout;
                drawMap();  // Call drawMap() after updating the map layout
            }
            players = data.players || players;
            drawPlayers();  // Call drawPlayers() after updating player data
        });

        socket.addEventListener("close", () => {
            console.log("Disconnected from server");
        });
    }
});

// Start the game
go("game");



fix player not show on other screens fix player size make it bigger fix map is too small