format root dir

This commit is contained in:
Evan
2025-02-12 08:38:56 -08:00
parent a3236653d3
commit 2df3db7292
13 changed files with 16586 additions and 16566 deletions
+16 -16
View File
@@ -6,19 +6,19 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
submodules: recursive
- name: Setup node
uses: actions/setup-node@v4
with:
node-version: 20
- name: Setup npm
run: npm install
- name: Build
run: npm run build-prod
- uses: actions/upload-artifact@v4
with:
path: out/index.html
retention-days: 1
- name: Checkout repository
uses: actions/checkout@v4
with:
submodules: recursive
- name: Setup node
uses: actions/setup-node@v4
with:
node-version: 20
- name: Setup npm
run: npm install
- name: Build
run: npm run build-prod
- uses: actions/upload-artifact@v4
with:
path: out/index.html
retention-days: 1
+15 -19
View File
@@ -1,20 +1,16 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Launch Program",
"skipFiles": [
"<node_internals>/**"
],
"program": "${workspaceFolder}/src/server/GameManager.ts",
"outFiles": [
"${workspaceFolder}/**/*.js"
]
}
]
}
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Launch Program",
"skipFiles": ["<node_internals>/**"],
"program": "${workspaceFolder}/src/server/GameManager.ts",
"outFiles": ["${workspaceFolder}/**/*.js"]
}
]
}
+31 -24
View File
@@ -1,31 +1,38 @@
steps:
# Build the Docker image
- name: 'gcr.io/cloud-builders/docker'
args: [
'build',
'-t', 'us-central1-docker.pkg.dev/$PROJECT_ID/openfrontio/game-server:${TAG_NAME}-${SHORT_SHA}',
'--build-arg', 'GAME_ENV=${_GAME_ENV}',
'.'
]
# Push the image to Artifact Registry
- name: 'gcr.io/cloud-builders/docker'
args: ['push', 'us-central1-docker.pkg.dev/$PROJECT_ID/openfrontio/game-server:${TAG_NAME}-${SHORT_SHA}']
# Update the GCE instance with the new container image
- name: 'gcr.io/cloud-builders/gcloud'
- name: "gcr.io/cloud-builders/docker"
args:
- 'compute'
- 'instances'
- 'update-container'
- '${_INSTANCE_NAME}'
- '--container-image'
- 'us-central1-docker.pkg.dev/$PROJECT_ID/openfrontio/game-server:${TAG_NAME}-${SHORT_SHA}'
- '--zone=us-central1-a'
[
"build",
"-t",
"us-central1-docker.pkg.dev/$PROJECT_ID/openfrontio/game-server:${TAG_NAME}-${SHORT_SHA}",
"--build-arg",
"GAME_ENV=${_GAME_ENV}",
".",
]
# Push the image to Artifact Registry
- name: "gcr.io/cloud-builders/docker"
args:
[
"push",
"us-central1-docker.pkg.dev/$PROJECT_ID/openfrontio/game-server:${TAG_NAME}-${SHORT_SHA}",
]
# Update the GCE instance with the new container image
- name: "gcr.io/cloud-builders/gcloud"
args:
- "compute"
- "instances"
- "update-container"
- "${_INSTANCE_NAME}"
- "--container-image"
- "us-central1-docker.pkg.dev/$PROJECT_ID/openfrontio/game-server:${TAG_NAME}-${SHORT_SHA}"
- "--zone=us-central1-a"
substitutions:
_INSTANCE_NAME: 'openfrontio-dev-instance'
_GAME_ENV: 'preprod' # Default to preprod
TAG_NAME: 'dev'
_INSTANCE_NAME: "openfrontio-dev-instance"
_GAME_ENV: "preprod" # Default to preprod
TAG_NAME: "dev"
options:
substitutionOption: 'ALLOW_LOOSE'
substitutionOption: "ALLOW_LOOSE"
logging: CLOUD_LOGGING_ONLY
images:
- 'us-central1-docker.pkg.dev/$PROJECT_ID/openfrontio/game-server:${TAG_NAME}-${SHORT_SHA}'
- "us-central1-docker.pkg.dev/$PROJECT_ID/openfrontio/game-server:${TAG_NAME}-${SHORT_SHA}"
+2 -2
View File
@@ -1,4 +1,4 @@
version: '3'
version: "3"
services:
game-server:
build: .
@@ -15,4 +15,4 @@ services:
- ./nginx.conf:/etc/nginx/nginx.conf
- /etc/letsencrypt:/etc/letsencrypt
depends_on:
- game-server
- game-server
+5 -5
View File
@@ -1,6 +1,6 @@
module.exports = {
transform: {'^.+\\.ts?$': 'ts-jest'},
testEnvironment: 'node',
testRegex: '/tests/.*\\.(test|spec)?\\.(ts|tsx)$',
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node']
};
transform: { "^.+\\.ts?$": "ts-jest" },
testEnvironment: "node",
testRegex: "/tests/.*\\.(test|spec)?\\.(ts|tsx)$",
moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node"],
};
+16218 -16218
View File
File diff suppressed because it is too large Load Diff
+5 -5
View File
@@ -1,6 +1,6 @@
export default {
plugins: {
tailwindcss: {},
autoprefixer: {},
}
}
plugins: {
tailwindcss: {},
autoprefixer: {},
},
};
+1 -1
View File
@@ -268,4 +268,4 @@
"flag": "td"
}
]
}
}
+6 -8
View File
@@ -1,11 +1,9 @@
// tailwind.config.js
/** @type {import('tailwindcss').Config} */
export default {
content: [
"./src/**/*.{html,ts,js}"
],
theme: {
extend: {},
},
plugins: [],
}
content: ["./src/**/*.{html,ts,js}"],
theme: {
extend: {},
},
plugins: [],
};
+144 -132
View File
@@ -1,170 +1,182 @@
import {GameImpl, PlayerImpl} from '../src/core/GameImpl';
import {EventBus} from '../src/core/EventBus';
import {Game, Cell, MutablePlayer, PlayerInfo, TerrainMap, TerrainTypes, Tile} from '../src/core/Game';
import { GameImpl, PlayerImpl } from "../src/core/GameImpl";
import { EventBus } from "../src/core/EventBus";
import {
Game,
Cell,
MutablePlayer,
PlayerInfo,
TerrainMap,
TerrainTypes,
Tile,
} from "../src/core/Game";
describe('borderTilesWith', () => {
let game: GameImpl;
let player1: PlayerImpl;
let player2: PlayerImpl;
let terrainMap: TerrainMap;
describe("borderTilesWith", () => {
let game: GameImpl;
let player1: PlayerImpl;
let player2: PlayerImpl;
let terrainMap: TerrainMap;
beforeEach(() => {
// Create a 5x5 terrain map
terrainMap = {
terrain: jest.fn().mockReturnValue(TerrainTypes.Land),
width: jest.fn().mockReturnValue(5),
height: jest.fn().mockReturnValue(5)
};
const eventBus = new EventBus();
game = new GameImpl(terrainMap, eventBus);
player1 = game.addPlayer(new PlayerInfo('Player 1', false)) as PlayerImpl;
player2 = game.addPlayer(new PlayerInfo('Player 2', false)) as PlayerImpl;
});
beforeEach(() => {
// Create a 5x5 terrain map
terrainMap = {
terrain: jest.fn().mockReturnValue(TerrainTypes.Land),
width: jest.fn().mockReturnValue(5),
height: jest.fn().mockReturnValue(5),
};
const eventBus = new EventBus();
game = new GameImpl(terrainMap, eventBus);
player1 = game.addPlayer(new PlayerInfo("Player 1", false)) as PlayerImpl;
player2 = game.addPlayer(new PlayerInfo("Player 2", false)) as PlayerImpl;
});
test('should return an empty set when players have no bordering tiles', () => {
const borderTiles = player1.borderTilesWith(player2);
expect(borderTiles.size).toBe(0);
});
test("should return an empty set when players have no bordering tiles", () => {
const borderTiles = player1.borderTilesWith(player2);
expect(borderTiles.size).toBe(0);
});
test('should return correct border tiles when players are adjacent', () => {
game.conquer(player1, new Cell(0, 0));
game.conquer(player2, new Cell(1, 0));
test("should return correct border tiles when players are adjacent", () => {
game.conquer(player1, new Cell(0, 0));
game.conquer(player2, new Cell(1, 0));
const borderTilesP1 = player1.borderTilesWith(player2);
const borderTilesP2 = player2.borderTilesWith(player1);
const borderTilesP1 = player1.borderTilesWith(player2);
const borderTilesP2 = player2.borderTilesWith(player1);
expect(borderTilesP1.size).toBe(1);
expect(borderTilesP2.size).toBe(1);
expect(borderTilesP1.size).toBe(1);
expect(borderTilesP2.size).toBe(1);
const p1BorderTile = Array.from(borderTilesP1)[0];
const p2BorderTile = Array.from(borderTilesP2)[0];
const p1BorderTile = Array.from(borderTilesP1)[0];
const p2BorderTile = Array.from(borderTilesP2)[0];
expect(p1BorderTile.cell()).toEqual(new Cell(0, 0));
expect(p2BorderTile.cell()).toEqual(new Cell(1, 0));
});
expect(p1BorderTile.cell()).toEqual(new Cell(0, 0));
expect(p2BorderTile.cell()).toEqual(new Cell(1, 0));
});
test('should update border tiles when a new tile is conquered', () => {
game.conquer(player1, new Cell(0, 0));
game.conquer(player2, new Cell(2, 0));
test("should update border tiles when a new tile is conquered", () => {
game.conquer(player1, new Cell(0, 0));
game.conquer(player2, new Cell(2, 0));
expect(player1.borderTilesWith(player2).size).toBe(0);
expect(player1.borderTilesWith(player2).size).toBe(0);
game.conquer(player2, new Cell(1, 0));
game.conquer(player2, new Cell(1, 0));
const borderTiles = player1.borderTilesWith(player2);
expect(borderTiles.size).toBe(1);
expect(Array.from(borderTiles)[0].cell()).toEqual(new Cell(0, 0));
});
const borderTiles = player1.borderTilesWith(player2);
expect(borderTiles.size).toBe(1);
expect(Array.from(borderTiles)[0].cell()).toEqual(new Cell(0, 0));
});
test('should handle multiple border tiles correctly', () => {
game.conquer(player1, new Cell(0, 0));
game.conquer(player1, new Cell(0, 1));
game.conquer(player2, new Cell(1, 0));
game.conquer(player2, new Cell(1, 1));
test("should handle multiple border tiles correctly", () => {
game.conquer(player1, new Cell(0, 0));
game.conquer(player1, new Cell(0, 1));
game.conquer(player2, new Cell(1, 0));
game.conquer(player2, new Cell(1, 1));
const borderTiles = player1.borderTilesWith(player2);
expect(borderTiles.size).toBe(2);
const borderTiles = player1.borderTilesWith(player2);
expect(borderTiles.size).toBe(2);
const borderCells = Array.from(borderTiles).map(tile => tile.cell());
expect(borderCells).toEqual(expect.arrayContaining([new Cell(0, 0), new Cell(0, 1)]));
});
const borderCells = Array.from(borderTiles).map((tile) => tile.cell());
expect(borderCells).toEqual(
expect.arrayContaining([new Cell(0, 0), new Cell(0, 1)]),
);
});
test('should update border tiles when a tile changes ownership', () => {
game.conquer(player1, new Cell(0, 0));
game.conquer(player1, new Cell(1, 0));
game.conquer(player2, new Cell(2, 0));
test("should update border tiles when a tile changes ownership", () => {
game.conquer(player1, new Cell(0, 0));
game.conquer(player1, new Cell(1, 0));
game.conquer(player2, new Cell(2, 0));
expect(player1.borderTilesWith(player2).size).toBe(1);
expect(player1.borderTilesWith(player2).size).toBe(1);
game.conquer(player2, new Cell(1, 0));
game.conquer(player2, new Cell(1, 0));
const borderTilesP1 = player1.borderTilesWith(player2);
const borderTilesP2 = player2.borderTilesWith(player1);
const borderTilesP1 = player1.borderTilesWith(player2);
const borderTilesP2 = player2.borderTilesWith(player1);
expect(borderTilesP1.size).toBe(1);
expect(borderTilesP2.size).toBe(1);
expect(borderTilesP1.size).toBe(1);
expect(borderTilesP2.size).toBe(1);
expect(Array.from(borderTilesP1)[0].cell()).toEqual(new Cell(0, 0));
expect(Array.from(borderTilesP2).map(t => t.cell())).toEqual(
expect.arrayContaining([new Cell(1, 0)])
);
});
expect(Array.from(borderTilesP1)[0].cell()).toEqual(new Cell(0, 0));
expect(Array.from(borderTilesP2).map((t) => t.cell())).toEqual(
expect.arrayContaining([new Cell(1, 0)]),
);
});
test('should handle border tiles with TerraNullius', () => {
game.conquer(player1, new Cell(0, 0));
test("should handle border tiles with TerraNullius", () => {
game.conquer(player1, new Cell(0, 0));
const borderWithTerraNullius = player1.borderTilesWith(game.terraNullius());
expect(borderWithTerraNullius.size).toBe(1);
const borderWithTerraNullius = player1.borderTilesWith(game.terraNullius());
expect(borderWithTerraNullius.size).toBe(1);
const borderCells = Array.from(borderWithTerraNullius).map(tile => tile.cell());
expect(borderCells).toEqual(expect.arrayContaining([new Cell(0, 0)]));
});
const borderCells = Array.from(borderWithTerraNullius).map((tile) =>
tile.cell(),
);
expect(borderCells).toEqual(expect.arrayContaining([new Cell(0, 0)]));
});
test('should not include diagonal tiles as borders', () => {
game.conquer(player1, new Cell(0, 0));
game.conquer(player2, new Cell(1, 1));
test("should not include diagonal tiles as borders", () => {
game.conquer(player1, new Cell(0, 0));
game.conquer(player2, new Cell(1, 1));
expect(player1.borderTilesWith(player2).size).toBe(0);
expect(player2.borderTilesWith(player1).size).toBe(0);
});
expect(player1.borderTilesWith(player2).size).toBe(0);
expect(player2.borderTilesWith(player1).size).toBe(0);
});
// test('should handle complex border scenarios', () => {
// // Create a more complex border scenario
// // 0 1 2 3 4
// // 0 1 1 2 2 2
// // 1 1 1 2 2 2
// // 2 1 1 1 2 2
// // 3 1 1 1 1 2
// // 4 1 1 1 1 1
// test('should handle complex border scenarios', () => {
// // Create a more complex border scenario
// // 0 1 2 3 4
// // 0 1 1 2 2 2
// // 1 1 1 2 2 2
// // 2 1 1 1 2 2
// // 3 1 1 1 1 2
// // 4 1 1 1 1 1
// for (let y = 0; y < 5; y++) {
// for (let x = 0; x < 5; x++) {
// if (x + y < 6) {
// game.conquer(player1, new Cell(x, y));
// } else {
// game.conquer(player2, new Cell(x, y));
// }
// }
// }
// for (let y = 0; y < 5; y++) {
// for (let x = 0; x < 5; x++) {
// if (x + y < 6) {
// game.conquer(player1, new Cell(x, y));
// } else {
// game.conquer(player2, new Cell(x, y));
// }
// }
// }
// const borderTilesP1 = player1.borderTilesWith(player2);
// const borderTilesP2 = player2.borderTilesWith(player1);
// const borderTilesP1 = player1.borderTilesWith(player2);
// const borderTilesP2 = player2.borderTilesWith(player1);
// expect(borderTilesP1.size).toBe(5);
// expect(borderTilesP2.size).toBe(5);
// expect(borderTilesP1.size).toBe(5);
// expect(borderTilesP2.size).toBe(5);
// const expectedBorderP1 = [
// new Cell(2, 0),
// new Cell(2, 1),
// new Cell(3, 2),
// new Cell(3, 3),
// new Cell(4, 3)
// ];
// const expectedBorderP1 = [
// new Cell(2, 0),
// new Cell(2, 1),
// new Cell(3, 2),
// new Cell(3, 3),
// new Cell(4, 3)
// ];
// const expectedBorderP2 = [
// new Cell(2, 2),
// new Cell(3, 1),
// new Cell(3, 2),
// new Cell(4, 1),
// new Cell(4, 2)
// ];
// const expectedBorderP2 = [
// new Cell(2, 2),
// new Cell(3, 1),
// new Cell(3, 2),
// new Cell(4, 1),
// new Cell(4, 2)
// ];
// const actualBorderP1 = Array.from(borderTilesP1).map(t => t.cell());
// const actualBorderP2 = Array.from(borderTilesP2).map(t => t.cell());
// const actualBorderP1 = Array.from(borderTilesP1).map(t => t.cell());
// const actualBorderP2 = Array.from(borderTilesP2).map(t => t.cell());
// expect(actualBorderP1).toEqual(expect.arrayContaining(expectedBorderP1));
// expect(actualBorderP2).toEqual(expect.arrayContaining(expectedBorderP2));
// });
// expect(actualBorderP1).toEqual(expect.arrayContaining(expectedBorderP1));
// expect(actualBorderP2).toEqual(expect.arrayContaining(expectedBorderP2));
// });
test('should handle border updates when a player loses all tiles', () => {
game.conquer(player1, new Cell(0, 0));
game.conquer(player2, new Cell(1, 0));
test("should handle border updates when a player loses all tiles", () => {
game.conquer(player1, new Cell(0, 0));
game.conquer(player2, new Cell(1, 0));
expect(player1.borderTilesWith(player2).size).toBe(1);
expect(player1.borderTilesWith(player2).size).toBe(1);
game.conquer(player1, new Cell(1, 0)); // Player 1 takes Player 2's only tile
game.conquer(player1, new Cell(1, 0)); // Player 1 takes Player 2's only tile
expect(player1.borderTilesWith(player2).size).toBe(0);
expect(player2.borderTilesWith(player1).size).toBe(0);
});
});
expect(player1.borderTilesWith(player2).size).toBe(0);
expect(player2.borderTilesWith(player1).size).toBe(0);
});
});
+1 -2
View File
@@ -1,7 +1,6 @@
// import {Game, Player, Tile, Cell, TerraNullius, PlayerInfo} from '../src/core/GameApi';
// import {placeName, calculateBoundingBox, createGrid, findLargestInscribedRectangle, largestRectangleInHistogram, calculateFontSize} from '../src/client/NameBoxCalculator';
// class MockPlayer implements Player {
// constructor(private playerTiles: [number, number][]) { }
@@ -191,4 +190,4 @@
// expect(fontSize).toBe(25); // 50 / 2 = 25 (height constrained)
// });
// });
// });
+3 -5
View File
@@ -8,7 +8,7 @@
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"experimentalDecorators": true,
"useDefineForClassFields": false,
"useDefineForClassFields": false
},
"include": [
"src/**/*",
@@ -17,7 +17,5 @@
"test/core/GameImpl.test.ts",
"src/scripts"
],
"exclude": [
"node_modules"
]
}
"exclude": ["node_modules"]
}
+139 -129
View File
@@ -1,137 +1,147 @@
import path from 'path';
import { fileURLToPath } from 'url';
import HtmlWebpackPlugin from 'html-webpack-plugin';
import webpack from 'webpack';
import path from "path";
import { fileURLToPath } from "url";
import HtmlWebpackPlugin from "html-webpack-plugin";
import webpack from "webpack";
import CopyPlugin from "copy-webpack-plugin";
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
export default (env, argv) => {
const isProduction = argv.mode === 'production';
const isProduction = argv.mode === "production";
return {
entry: './src/client/Main.ts',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'out'),
clean: true
},
module: {
rules: [
{
test: /\.bin$/,
use: 'raw-loader'
},
{
test: /\.txt$/,
use: 'raw-loader'
},
{
test: /\.ts$/,
use: 'ts-loader',
exclude: /node_modules/,
},
{
test: /\.css$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 1
}
},
{
loader: 'postcss-loader',
options: {
postcssOptions: {
plugins: [
'tailwindcss',
'autoprefixer',
],
}
}
}
]
},
{
test: /\.(png|jpe?g|gif)$/i,
type: 'asset/resource',
generator: {
filename: 'images/[hash][ext][query]'
}
},
{
test: /\.html$/,
use: ['html-loader']
},
{
test: /\.svg$/,
type: 'asset/inline',
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
use: [
{
loader: 'file-loader',
options: {
name: '[name].[ext]',
outputPath: 'fonts/',
publicPath: '../fonts/', // This is important
}
}
]
}
],
},
resolve: {
extensions: ['.tsx', '.ts', '.js'],
alias: {
'protobufjs/minimal': path.resolve(__dirname, 'node_modules/protobufjs/minimal.js')
}
},
plugins: [
new HtmlWebpackPlugin({
template: './src/client/index.html',
filename: 'index.html'
}),
new webpack.DefinePlugin({
'process.env.WEBSOCKET_URL': JSON.stringify(isProduction ? '' : 'localhost:3000')
}),
new webpack.DefinePlugin({
'process.env.GAME_ENV': JSON.stringify(isProduction ? 'prod' : 'dev')
}),
new CopyPlugin({
patterns: [
{ from: "resources", to: path.resolve(__dirname, 'out') },
],
options: {
concurrency: 100,
},
}),
],
devServer: isProduction ? {} : {
devMiddleware: { writeToDisk: true },
static: {
directory: path.join(__dirname, 'out'),
},
compress: true,
port: 9000,
proxy: [
{
context: ['/socket'],
target: 'ws://localhost:3000',
ws: true,
},
{
context: ['/lobbies', '/join_game', '/join_lobby', '/private_lobby', '/start_private_lobby',
'/lobby', '/archive_singleplayer_game', '/validate-username'],
target: 'http://localhost:3000',
secure: false,
changeOrigin: true,
}
],
},
};
return {
entry: "./src/client/Main.ts",
output: {
filename: "bundle.js",
path: path.resolve(__dirname, "out"),
clean: true,
},
module: {
rules: [
{
test: /\.bin$/,
use: "raw-loader",
},
{
test: /\.txt$/,
use: "raw-loader",
},
{
test: /\.ts$/,
use: "ts-loader",
exclude: /node_modules/,
},
{
test: /\.css$/,
use: [
"style-loader",
{
loader: "css-loader",
options: {
importLoaders: 1,
},
},
{
loader: "postcss-loader",
options: {
postcssOptions: {
plugins: ["tailwindcss", "autoprefixer"],
},
},
},
],
},
{
test: /\.(png|jpe?g|gif)$/i,
type: "asset/resource",
generator: {
filename: "images/[hash][ext][query]",
},
},
{
test: /\.html$/,
use: ["html-loader"],
},
{
test: /\.svg$/,
type: "asset/inline",
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
use: [
{
loader: "file-loader",
options: {
name: "[name].[ext]",
outputPath: "fonts/",
publicPath: "../fonts/", // This is important
},
},
],
},
],
},
resolve: {
extensions: [".tsx", ".ts", ".js"],
alias: {
"protobufjs/minimal": path.resolve(
__dirname,
"node_modules/protobufjs/minimal.js",
),
},
},
plugins: [
new HtmlWebpackPlugin({
template: "./src/client/index.html",
filename: "index.html",
}),
new webpack.DefinePlugin({
"process.env.WEBSOCKET_URL": JSON.stringify(
isProduction ? "" : "localhost:3000",
),
}),
new webpack.DefinePlugin({
"process.env.GAME_ENV": JSON.stringify(isProduction ? "prod" : "dev"),
}),
new CopyPlugin({
patterns: [{ from: "resources", to: path.resolve(__dirname, "out") }],
options: {
concurrency: 100,
},
}),
],
devServer: isProduction
? {}
: {
devMiddleware: { writeToDisk: true },
static: {
directory: path.join(__dirname, "out"),
},
compress: true,
port: 9000,
proxy: [
{
context: ["/socket"],
target: "ws://localhost:3000",
ws: true,
},
{
context: [
"/lobbies",
"/join_game",
"/join_lobby",
"/private_lobby",
"/start_private_lobby",
"/lobby",
"/archive_singleplayer_game",
"/validate-username",
],
target: "http://localhost:3000",
secure: false,
changeOrigin: true,
},
],
},
};
};