diff --git a/TODO.txt b/TODO.txt index 31ffdd3e4..5faa36061 100644 --- a/TODO.txt +++ b/TODO.txt @@ -22,11 +22,11 @@ * delete players when territories too small DONE 8/16/2024 * double attack add troops DONE 8/16/2024 * Have some time for spawning before game starts DONE 8/16/2024 +* fix desync DONE 8/16/2024 +* fix server memory leak DONE 8/16/2024 * only send delta turns on connect/reconnect DONE 8/17/2024 -* Create separate game config dev vs prod -* improve front page, only one game at a time every 30s -* fix desync -* fix server memory leak +* Create separate game config dev vs prod DONE 8/17/2024 +* improve front page, only one game at a time every 30s DONE 8/17/2024 * BUG: boats not going to destination, coast not being recognized * BUG: boats freeze game on path calculation * better algorithm for name render placement diff --git a/package.json b/package.json index e8b7cbdf2..a8c3bbf0c 100644 --- a/package.json +++ b/package.json @@ -5,8 +5,9 @@ "build-dev": "webpack --config webpack.config.js --mode development", "build-prod": "webpack --config webpack.config.js --mode production", "start:client": "webpack serve --open --node-env development", - "start:server": "node --loader ts-node/esm --experimental-specifier-resolution=node src/server/Server.ts", - "dev": "concurrently \"npm run start:client\" \"npm run start:server\"", + "start:server": "GAME_ENV=prod node --loader ts-node/esm --experimental-specifier-resolution=node src/server/Server.ts", + "start:server-dev": "GAME_ENV=dev node --loader ts-node/esm --experimental-specifier-resolution=node src/server/Server.ts", + "dev": "GAME_ENV=dev concurrently \"npm run start:client\" \"npm run start:server-dev\"", "test": "jest" }, "devDependencies": { @@ -53,4 +54,4 @@ "zod": "^3.23.8" }, "type": "module" -} +} \ No newline at end of file diff --git a/resources/images/World.png b/resources/images/World.png new file mode 100644 index 000000000..3244572db Binary files /dev/null and b/resources/images/World.png differ diff --git a/resources/images/watercolor_worldmap.jpg b/resources/images/watercolor_worldmap.jpg new file mode 100755 index 000000000..a57c2e72f Binary files /dev/null and b/resources/images/watercolor_worldmap.jpg differ diff --git a/resources/images/world-map.svg b/resources/images/world-map.svg new file mode 100755 index 000000000..8c64a9ed6 --- /dev/null +++ b/resources/images/world-map.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/resources/images/worldmap.jpg b/resources/images/worldmap.jpg new file mode 100755 index 000000000..833ec4873 Binary files /dev/null and b/resources/images/worldmap.jpg differ diff --git a/src/client/Client.ts b/src/client/Client.ts index 707919bbb..1f87691bf 100644 --- a/src/client/Client.ts +++ b/src/client/Client.ts @@ -7,6 +7,8 @@ import {GameID, Lobby, ServerMessage, ServerMessageSchema} from "../core/Schemas import {loadTerrainMap} from "../core/TerrainMapLoader"; import {ClientGame, createClientGame} from "./ClientGame"; import {v4 as uuidv4} from 'uuid'; +import backgroundImage from '../../resources/images/World.png'; + // import WebSocket from 'ws'; @@ -52,17 +54,27 @@ class Client { lobbies.forEach(lobby => { const button = document.createElement('button'); - button.textContent = `Join Lobby ${lobby.id} (${Math.floor((lobby.startTime - Date.now()) / 1000)}s)`; + button.className = 'lobby-button'; + + const nameElement = document.createElement('div'); + nameElement.className = 'lobby-name'; + nameElement.textContent = `Lobby ${lobby.id}`; + + const timerElement = document.createElement('div'); + timerElement.className = 'lobby-timer'; + const timeRemaining = Math.max(0, Math.floor((lobby.startTime - Date.now()) / 1000)); + timerElement.textContent = `Starts in: ${timeRemaining}s`; + + const playerCountElement = document.createElement('div'); + playerCountElement.className = 'player-count'; + + button.appendChild(nameElement); + button.appendChild(timerElement); + button.appendChild(playerCountElement); + button.onclick = () => this.joinLobby(lobby); this.lobbiesContainer.appendChild(button); }); - - // // Join first lobby - // if (!this.hasJoined && lobbies.length > 0) { - // this.hasJoined = true - // console.log(`joining lobby ${lobbies[0].id}`) - // this.joinLobby(lobbies[0].id) - // } } async fetchLobbies(): Promise { @@ -81,16 +93,33 @@ class Client { } private async joinLobby(lobby: Lobby) { - clearInterval(this.lobbiesInterval) - this.lobbiesContainer.innerHTML = `Joining: ${lobby.id}`; // Clear existing lobbies + clearInterval(this.lobbiesInterval); + + if (this.lobbiesContainer) { + // Clear existing content + this.lobbiesContainer.innerHTML = ''; + + // Ensure the container takes up the full height of the viewport + this.lobbiesContainer.style.display = 'flex'; + this.lobbiesContainer.style.justifyContent = 'center'; + this.lobbiesContainer.style.alignItems = 'center'; + this.lobbiesContainer.style.minHeight = '100vh'; + + // Create and add the joining message + const joiningMessage = document.createElement('div'); + joiningMessage.textContent = `Joining: ${lobby.id}`; + joiningMessage.className = 'joining-message'; + + this.lobbiesContainer.appendChild(joiningMessage); + } + this.terrainMap.then((map) => { if (this.game != null) { - return + return; } - // TODO make id more random, if two player join same millisecond get same id. - this.game = createClientGame(getUsername(), new PseudoRandom(Date.now()).nextID(), lobby.id, getConfig(), map) - this.game.join() - }) + this.game = createClientGame(getUsername(), new PseudoRandom(Date.now()).nextID(), lobby.id, getConfig(), map); + this.game.join(); + }); } } @@ -106,4 +135,7 @@ function getUsername(): string { // Initialize the client when the DOM is loaded document.addEventListener('DOMContentLoaded', () => { new Client().initialize(); -}); \ No newline at end of file +}); + +document.body.style.backgroundImage = `url(${backgroundImage})`; + diff --git a/src/client/ClientGame.ts b/src/client/ClientGame.ts index 47616c68e..307e04c87 100644 --- a/src/client/ClientGame.ts +++ b/src/client/ClientGame.ts @@ -154,9 +154,9 @@ export class ClientGame { } private inputEvent(event: MouseDownEvent) { - if (this.turns.length < this.config.turnsUntilGameStart()) { - return - } + // if (this.turns.length < this.config.turnsUntilGameStart()) { + // return + // } if (!this.isActive) { return } diff --git a/src/client/index.html b/src/client/index.html index 2dab6c4fd..1c366dd75 100644 --- a/src/client/index.html +++ b/src/client/index.html @@ -8,71 +8,126 @@ -

OpenFront (ALPHA)

-
- -
-
- +
+

OpenFront (ALPHA)

+
+ +
+
+ +
diff --git a/src/core/configuration/Config.ts b/src/core/configuration/Config.ts index 3b5d94270..27b22cda6 100644 --- a/src/core/configuration/Config.ts +++ b/src/core/configuration/Config.ts @@ -4,12 +4,13 @@ import {devConfig} from "./DevConfig"; import {defaultConfig} from "./DefaultConfig"; export function getConfig(): Config { - if (process.env.GAME_ENV == 'prod') { - console.log('Using prod config') - return defaultConfig - } else { + // TODO: 'prod' not found in prod env + if (process.env.GAME_ENV == 'dev') { console.log('Using dev config') return devConfig + } else { + console.log('Using prod config') + return defaultConfig } } diff --git a/src/global.d.ts b/src/global.d.ts index 947f19163..648a061c2 100644 --- a/src/global.d.ts +++ b/src/global.d.ts @@ -1,4 +1,17 @@ declare module '*.png' { const content: string; export default content; -} \ No newline at end of file +} +declare module '*.jpg' { + const value: string; + export default value; +} + +declare module '*.jpeg' { + const value: string; + export default value; +} +declare module '*.svg' { + const value: string; + export default value; +} diff --git a/src/server/Lobby.ts b/src/server/Lobby.ts new file mode 100644 index 000000000..cf0256e59 --- /dev/null +++ b/src/server/Lobby.ts @@ -0,0 +1,21 @@ +import {ClientID} from "../core/Schemas"; +import {Client} from "./Client"; + +export class Lobby { + + public clients: Map = new Map() + private startGameTs: number + + + constructor(public readonly id: string, durationMs: number) { + this.startGameTs = Date.now() + durationMs + } + + public addClient(client: Client) { + this.clients.set(client.id, client) + } + + public isExpired(now: number): boolean { + return now > this.startGameTs + } +} \ No newline at end of file diff --git a/webpack.config.js b/webpack.config.js index da807a51c..585d0b230 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -28,6 +28,10 @@ export default (env, argv) => { generator: { filename: 'images/[hash][ext][query]' } + }, + { + test: /\.svg$/, + type: 'asset/inline', } ], },