Compare commits
No commits in common. "9107c557afa3fbf98d2be164b801b3653456a2b3" and "c40492c0e312ca0d0b9204897fbe0020eaff8cc0" have entirely different histories.
9107c557af
...
c40492c0e3
35 changed files with 862 additions and 99 deletions
|
@ -7,14 +7,11 @@ module.exports = {
|
||||||
`plugin:${PluginName}/${PresetName.RecommendedWithTypeChecking}`
|
`plugin:${PluginName}/${PresetName.RecommendedWithTypeChecking}`
|
||||||
],
|
],
|
||||||
env: {
|
env: {
|
||||||
node: true,
|
node: true
|
||||||
browser: true
|
|
||||||
},
|
},
|
||||||
parserOptions: {
|
parserOptions: {
|
||||||
project: [
|
project: [
|
||||||
join(__dirname, "app.jsconfig.json"),
|
join(__dirname, "eslint.jsconfig.json")
|
||||||
join(__dirname, "eslint.jsconfig.json"),
|
|
||||||
join(__dirname, "gulp.tsconfig.json")
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,6 +1,15 @@
|
||||||
{
|
{
|
||||||
"folders": [
|
"folders": [
|
||||||
{
|
{
|
||||||
|
"name": "ConnectForce",
|
||||||
|
"path": "./packages/game"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Server",
|
||||||
|
"path": "./packages/server"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Solution Items",
|
||||||
"path": "."
|
"path": "."
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
@ -26,7 +35,7 @@
|
||||||
"label": "Build",
|
"label": "Build",
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
"options": {
|
"options": {
|
||||||
"cwd": "${workspaceFolder}"
|
"cwd": "${workspaceFolder:Solution Items}"
|
||||||
},
|
},
|
||||||
"command": "npm",
|
"command": "npm",
|
||||||
"args": [
|
"args": [
|
||||||
|
@ -63,7 +72,7 @@
|
||||||
"label": "Rebuild",
|
"label": "Rebuild",
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
"options": {
|
"options": {
|
||||||
"cwd": "${workspaceFolder}"
|
"cwd": "${workspaceFolder:Solution Items}"
|
||||||
},
|
},
|
||||||
"command": "npm",
|
"command": "npm",
|
||||||
"args": [
|
"args": [
|
||||||
|
@ -79,7 +88,7 @@
|
||||||
"label": "Lint",
|
"label": "Lint",
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
"options": {
|
"options": {
|
||||||
"cwd": "${workspaceFolder}"
|
"cwd": "${workspaceFolder:Solution Items}"
|
||||||
},
|
},
|
||||||
"command": "npm",
|
"command": "npm",
|
||||||
"args": [
|
"args": [
|
||||||
|
@ -104,11 +113,33 @@
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"name": "Launch Website in Chrome",
|
"name": "Launch Website in Chrome",
|
||||||
"url": "http://localhost:3000",
|
"url": "http://localhost:3000",
|
||||||
"webRoot": "${workspaceFolder}/lib/static",
|
"webRoot": "${workspaceFolder:ConnectForce}/lib/static",
|
||||||
"preLaunchTask": "Build",
|
"preLaunchTask": "Build",
|
||||||
"pathMapping": {
|
"pathMapping": {
|
||||||
"/": "${workspaceFolder}/src"
|
"/": "${workspaceFolder:ConnectForce}/src"
|
||||||
|
},
|
||||||
|
"presentation": {
|
||||||
|
"hidden": true
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "node",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "Launch Server",
|
||||||
|
"program": "${workspaceFolder:Server}/src/main.js",
|
||||||
|
"console": "integratedTerminal",
|
||||||
|
"presentation": {
|
||||||
|
"hidden": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"compounds": [
|
||||||
|
{
|
||||||
|
"name": "Launch Project in Chrome",
|
||||||
|
"configurations": [
|
||||||
|
"Launch Website in Chrome",
|
||||||
|
"Launch Server"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
2
LICENSE
2
LICENSE
|
@ -1,6 +1,6 @@
|
||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2022 Manuel Thalmann
|
Copyright (c) <year> <copyright holders>
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
|
39
package.json
39
package.json
|
@ -1,31 +1,26 @@
|
||||||
{
|
{
|
||||||
"name": "connect-force",
|
"name": "connect-force",
|
||||||
"version": "0.0.0",
|
"private": true,
|
||||||
"type": "module",
|
"files": [],
|
||||||
"description": "A selfmade Connect Four game.",
|
"workspaces": {
|
||||||
"author": "Manuel Thalmann <m@nuth.ch>",
|
"packages": [
|
||||||
|
"./packages/*"
|
||||||
|
]
|
||||||
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"gulp": "cross-env NODE_OPTIONS=\"--loader ts-node/esm\" gulp --",
|
"rebuild": "npm run --workspaces rebuild",
|
||||||
"build": "npm run gulp Build",
|
"watch": "concurrently --raw \"npm run --workspaces --if-present watch\"",
|
||||||
"rebuild": "npm run clean && npm run build",
|
"clean": "npm run --workspaces clean",
|
||||||
"watch": "npm run gulp Watch",
|
"lint-local": "eslint --max-warnings 0 .eslintrc.cjs",
|
||||||
"clean": "rimraf ./lib",
|
"lint-local-ide": "npm run lint-local || exit 0",
|
||||||
"lint": "eslint --max-warnings 0 ./src .eslintrc.cjs",
|
"lint": "npm run lint-local && npm run --workspaces lint",
|
||||||
"lint-ide": "npm run lint || exit 0",
|
"lint-ide": "npm run lint-local-ide && npm run --workspaces lint-ide",
|
||||||
|
"test": "npm run --workspaces test",
|
||||||
"prepare": "npm run rebuild"
|
"prepare": "npm run rebuild"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@manuth/eslint-plugin-typescript": "^4.0.1",
|
"@manuth/eslint-plugin-typescript": "^4.0.1",
|
||||||
"@manuth/tsconfig": "^3.0.2",
|
"concurrently": "^7.6.0",
|
||||||
"@types/browser-sync": "^2.26.3",
|
"eslint": "^8.29.0"
|
||||||
"@types/gulp": "^4.0.10",
|
|
||||||
"@types/node": "^18.11.11",
|
|
||||||
"browser-sync": "^2.27.10",
|
|
||||||
"cross-env": "^7.0.3",
|
|
||||||
"eslint": "^8.29.0",
|
|
||||||
"gulp": "^4.0.2",
|
|
||||||
"rimraf": "^3.0.2",
|
|
||||||
"ts-node": "^10.9.1",
|
|
||||||
"upath": "^2.0.1"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
14
packages/game/.eslintrc.cjs
Normal file
14
packages/game/.eslintrc.cjs
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
const { join } = require("node:path");
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
env: {
|
||||||
|
browser: true
|
||||||
|
},
|
||||||
|
parserOptions: {
|
||||||
|
project: [
|
||||||
|
join(__dirname, "app.jsconfig.json"),
|
||||||
|
join(__dirname, "eslint.jsconfig.json"),
|
||||||
|
join(__dirname, "gulp.tsconfig.json")
|
||||||
|
]
|
||||||
|
}
|
||||||
|
};
|
168
packages/game/.npmignore
Normal file
168
packages/game/.npmignore
Normal file
|
@ -0,0 +1,168 @@
|
||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
.pnpm-debug.log*
|
||||||
|
|
||||||
|
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||||
|
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||||
|
|
||||||
|
# Runtime data
|
||||||
|
pids
|
||||||
|
*.pid
|
||||||
|
*.seed
|
||||||
|
*.pid.lock
|
||||||
|
|
||||||
|
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||||
|
lib-cov
|
||||||
|
|
||||||
|
# Coverage directory used by tools like istanbul
|
||||||
|
coverage
|
||||||
|
*.lcov
|
||||||
|
|
||||||
|
# nyc test coverage
|
||||||
|
.nyc_output
|
||||||
|
|
||||||
|
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||||
|
.grunt
|
||||||
|
|
||||||
|
# Bower dependency directory (https://bower.io/)
|
||||||
|
bower_components
|
||||||
|
|
||||||
|
# node-waf configuration
|
||||||
|
.lock-wscript
|
||||||
|
|
||||||
|
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||||
|
build/Release
|
||||||
|
|
||||||
|
# Dependency directories
|
||||||
|
node_modules/
|
||||||
|
jspm_packages/
|
||||||
|
|
||||||
|
# Snowpack dependency directory (https://snowpack.dev/)
|
||||||
|
web_modules/
|
||||||
|
|
||||||
|
# TypeScript cache
|
||||||
|
*.tsbuildinfo
|
||||||
|
|
||||||
|
# Optional npm cache directory
|
||||||
|
.npm
|
||||||
|
|
||||||
|
# Optional eslint cache
|
||||||
|
.eslintcache
|
||||||
|
|
||||||
|
# Optional stylelint cache
|
||||||
|
.stylelintcache
|
||||||
|
|
||||||
|
# Microbundle cache
|
||||||
|
.rpt2_cache/
|
||||||
|
.rts2_cache_cjs/
|
||||||
|
.rts2_cache_es/
|
||||||
|
.rts2_cache_umd/
|
||||||
|
|
||||||
|
# Optional REPL history
|
||||||
|
.node_repl_history
|
||||||
|
|
||||||
|
# Output of 'npm pack'
|
||||||
|
*.tgz
|
||||||
|
|
||||||
|
# Yarn Integrity file
|
||||||
|
.yarn-integrity
|
||||||
|
|
||||||
|
# dotenv environment variable files
|
||||||
|
.env
|
||||||
|
.env.development.local
|
||||||
|
.env.test.local
|
||||||
|
.env.production.local
|
||||||
|
.env.local
|
||||||
|
|
||||||
|
# parcel-bundler cache (https://parceljs.org/)
|
||||||
|
.cache
|
||||||
|
.parcel-cache
|
||||||
|
|
||||||
|
# Next.js build output
|
||||||
|
.next
|
||||||
|
out
|
||||||
|
|
||||||
|
# Nuxt.js build / generate output
|
||||||
|
.nuxt
|
||||||
|
dist
|
||||||
|
|
||||||
|
# Gatsby files
|
||||||
|
.cache/
|
||||||
|
# Comment in the public line in if your project uses Gatsby and *not* Next.js
|
||||||
|
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||||
|
# public
|
||||||
|
|
||||||
|
# vuepress build output
|
||||||
|
.vuepress/dist
|
||||||
|
|
||||||
|
# vuepress v2.x temp and cache directory
|
||||||
|
.temp
|
||||||
|
.cache
|
||||||
|
|
||||||
|
# Docusaurus cache and generated files
|
||||||
|
.docusaurus
|
||||||
|
|
||||||
|
# Serverless directories
|
||||||
|
.serverless/
|
||||||
|
|
||||||
|
# FuseBox cache
|
||||||
|
.fusebox/
|
||||||
|
|
||||||
|
# DynamoDB Local files
|
||||||
|
.dynamodb/
|
||||||
|
|
||||||
|
# TernJS port file
|
||||||
|
.tern-port
|
||||||
|
|
||||||
|
# Stores VSCode versions used for testing VSCode extensions
|
||||||
|
.vscode-test
|
||||||
|
|
||||||
|
# yarn v2
|
||||||
|
.yarn/cache
|
||||||
|
.yarn/unplugged
|
||||||
|
.yarn/build-state.yml
|
||||||
|
.yarn/install-state.gz
|
||||||
|
.pnp.*
|
||||||
|
|
||||||
|
# Source-files
|
||||||
|
[Ss]rc/
|
||||||
|
|
||||||
|
# TypeScript config-files
|
||||||
|
tsconfig.json
|
||||||
|
tsconfig.*.json
|
||||||
|
|
||||||
|
# Lint config-files
|
||||||
|
.eslintrc
|
||||||
|
.eslintrc.*
|
||||||
|
|
||||||
|
# Source-maps
|
||||||
|
[Ll]ib/**/*.map
|
||||||
|
|
||||||
|
# Unit-Tests
|
||||||
|
.mocharc.*
|
||||||
|
[Ll]ib/tests/
|
||||||
|
|
||||||
|
# Visual Studio Code-Environment
|
||||||
|
.vscode/
|
||||||
|
|
||||||
|
# GitHub configuration
|
||||||
|
.github/
|
||||||
|
|
||||||
|
# CI configuration
|
||||||
|
.drone.yml
|
||||||
|
.woodpecker.yml
|
||||||
|
|
||||||
|
# Build Environment
|
||||||
|
gulp/
|
||||||
|
gulpfile.ts
|
||||||
|
|
||||||
|
# Temporary release-assets
|
||||||
|
.tagName.txt
|
||||||
|
.tagHeading.txt
|
||||||
|
.releaseNotes.md
|
||||||
|
.releaseTitle.md
|
10
packages/game/eslint.jsconfig.json
Normal file
10
packages/game/eslint.jsconfig.json
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
{
|
||||||
|
"extends": "./tsconfig.base.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"allowJs": true,
|
||||||
|
"checkJs": true
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"./.eslintrc.cjs"
|
||||||
|
]
|
||||||
|
}
|
|
@ -137,7 +137,7 @@ export let Watch: TaskFunction = async (): Promise<void> =>
|
||||||
|
|
||||||
syncer.init({
|
syncer.init({
|
||||||
open: false,
|
open: false,
|
||||||
server: join(context.StaticPath()),
|
proxy: "http://localhost:1337",
|
||||||
online: false
|
online: false
|
||||||
});
|
});
|
||||||
|
|
31
packages/game/package.json
Normal file
31
packages/game/package.json
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
{
|
||||||
|
"name": "connect-force",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"type": "module",
|
||||||
|
"description": "A selfmade Connect Four game.",
|
||||||
|
"author": "Manuel Thalmann <m@nuth.ch>",
|
||||||
|
"scripts": {
|
||||||
|
"gulp": "cross-env NODE_OPTIONS=\"--loader ts-node/esm\" gulp --",
|
||||||
|
"build": "npm run gulp Build",
|
||||||
|
"rebuild": "npm run clean && npm run build",
|
||||||
|
"watch": "npm run gulp Watch",
|
||||||
|
"clean": "rimraf ./lib",
|
||||||
|
"lint": "eslint --max-warnings 0 ./src .eslintrc.cjs",
|
||||||
|
"lint-ide": "npm run lint || exit 0",
|
||||||
|
"prepare": "npm run rebuild"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@manuth/eslint-plugin-typescript": "^4.0.1",
|
||||||
|
"@manuth/tsconfig": "^3.0.2",
|
||||||
|
"@types/browser-sync": "^2.26.3",
|
||||||
|
"@types/gulp": "^4.0.10",
|
||||||
|
"@types/node": "^18.11.11",
|
||||||
|
"browser-sync": "^2.27.10",
|
||||||
|
"cross-env": "^7.0.3",
|
||||||
|
"eslint": "^8.29.0",
|
||||||
|
"gulp": "^4.0.2",
|
||||||
|
"rimraf": "^3.0.2",
|
||||||
|
"ts-node": "^10.9.1",
|
||||||
|
"upath": "^2.0.1"
|
||||||
|
}
|
||||||
|
}
|
150
packages/game/src/js/main.js
Normal file
150
packages/game/src/js/main.js
Normal file
|
@ -0,0 +1,150 @@
|
||||||
|
import { Game } from "./Game.js";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The game that is being played.
|
||||||
|
*
|
||||||
|
* @type {Game}
|
||||||
|
*/
|
||||||
|
let game;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A value indicating whether a transfer is pending.
|
||||||
|
*/
|
||||||
|
let transferPending = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The id of the save game.
|
||||||
|
*/
|
||||||
|
let id = "";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the save button.
|
||||||
|
*
|
||||||
|
* @returns {HTMLButtonElement}
|
||||||
|
* The save button.
|
||||||
|
*/
|
||||||
|
function getSaveButton()
|
||||||
|
{
|
||||||
|
return document.querySelector(".save");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the load button.
|
||||||
|
*
|
||||||
|
* @returns {HTMLButtonElement}
|
||||||
|
* The load button.
|
||||||
|
*/
|
||||||
|
function getLoadButton()
|
||||||
|
{
|
||||||
|
return document.querySelector(".load");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets an url for storing and loading the save game.
|
||||||
|
*
|
||||||
|
* @returns {URL}
|
||||||
|
* The url for storing and loading the save game.
|
||||||
|
*/
|
||||||
|
function getUrl()
|
||||||
|
{
|
||||||
|
let result = new URL(
|
||||||
|
id,
|
||||||
|
new URL(
|
||||||
|
"/api/data/",
|
||||||
|
window.location.origin));
|
||||||
|
|
||||||
|
result.searchParams.append("token", "c4game");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the board.
|
||||||
|
*/
|
||||||
|
function initialize()
|
||||||
|
{
|
||||||
|
game = new Game("game");
|
||||||
|
game.initialize();
|
||||||
|
|
||||||
|
(/** @type {HTMLElement} */ (document.querySelector(".new-game"))).onclick = (event) =>
|
||||||
|
{
|
||||||
|
event.preventDefault();
|
||||||
|
game.reset();
|
||||||
|
};
|
||||||
|
|
||||||
|
getSaveButton().onclick = async () =>
|
||||||
|
{
|
||||||
|
if (!transferPending)
|
||||||
|
{
|
||||||
|
transferPending = true;
|
||||||
|
getSaveButton().disabled = true;
|
||||||
|
getLoadButton().disabled = true;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (id === "")
|
||||||
|
{
|
||||||
|
let result = await (await fetch(
|
||||||
|
getUrl(),
|
||||||
|
{
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-type": "application/json"
|
||||||
|
},
|
||||||
|
body: JSON.stringify(game.dump())
|
||||||
|
})).json();
|
||||||
|
|
||||||
|
({ id } = result);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await fetch(
|
||||||
|
getUrl(),
|
||||||
|
{
|
||||||
|
method: "PUT",
|
||||||
|
headers: {
|
||||||
|
"Content-type": "application/json"
|
||||||
|
},
|
||||||
|
body: JSON.stringify(game.dump())
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
|
||||||
|
getSaveButton().disabled = false;
|
||||||
|
getLoadButton().disabled = false;
|
||||||
|
transferPending = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
console.log("Already busy");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
getLoadButton().onclick = async () =>
|
||||||
|
{
|
||||||
|
if (!transferPending)
|
||||||
|
{
|
||||||
|
transferPending = true;
|
||||||
|
getSaveButton().disabled = true;
|
||||||
|
getLoadButton().disabled = true;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
game.load(
|
||||||
|
await (
|
||||||
|
await fetch(getUrl())).json());
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
|
||||||
|
getSaveButton().disabled = false;
|
||||||
|
getLoadButton().disabled = false;
|
||||||
|
transferPending = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
console.log("Already busy");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
initialize();
|
3
packages/game/tsconfig.base.json
Normal file
3
packages/game/tsconfig.base.json
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"extends": "../../tsconfig.base.json"
|
||||||
|
}
|
15
packages/game/tsconfig.json
Normal file
15
packages/game/tsconfig.json
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"extends": "./tsconfig.base.json",
|
||||||
|
"references": [
|
||||||
|
{
|
||||||
|
"path": "./app.jsconfig.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "./eslint.jsconfig.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "./gulp.tsconfig.json"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"include": []
|
||||||
|
}
|
13
packages/server/.eslintrc.cjs
Normal file
13
packages/server/.eslintrc.cjs
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
const { join } = require("node:path");
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
env: {
|
||||||
|
browser: true
|
||||||
|
},
|
||||||
|
parserOptions: {
|
||||||
|
project: [
|
||||||
|
join(__dirname, "app.jsconfig.json"),
|
||||||
|
join(__dirname, "eslint.jsconfig.json")
|
||||||
|
]
|
||||||
|
}
|
||||||
|
};
|
168
packages/server/.npmignore
Normal file
168
packages/server/.npmignore
Normal file
|
@ -0,0 +1,168 @@
|
||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
.pnpm-debug.log*
|
||||||
|
|
||||||
|
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||||
|
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||||
|
|
||||||
|
# Runtime data
|
||||||
|
pids
|
||||||
|
*.pid
|
||||||
|
*.seed
|
||||||
|
*.pid.lock
|
||||||
|
|
||||||
|
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||||
|
lib-cov
|
||||||
|
|
||||||
|
# Coverage directory used by tools like istanbul
|
||||||
|
coverage
|
||||||
|
*.lcov
|
||||||
|
|
||||||
|
# nyc test coverage
|
||||||
|
.nyc_output
|
||||||
|
|
||||||
|
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||||
|
.grunt
|
||||||
|
|
||||||
|
# Bower dependency directory (https://bower.io/)
|
||||||
|
bower_components
|
||||||
|
|
||||||
|
# node-waf configuration
|
||||||
|
.lock-wscript
|
||||||
|
|
||||||
|
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||||
|
build/Release
|
||||||
|
|
||||||
|
# Dependency directories
|
||||||
|
node_modules/
|
||||||
|
jspm_packages/
|
||||||
|
|
||||||
|
# Snowpack dependency directory (https://snowpack.dev/)
|
||||||
|
web_modules/
|
||||||
|
|
||||||
|
# TypeScript cache
|
||||||
|
*.tsbuildinfo
|
||||||
|
|
||||||
|
# Optional npm cache directory
|
||||||
|
.npm
|
||||||
|
|
||||||
|
# Optional eslint cache
|
||||||
|
.eslintcache
|
||||||
|
|
||||||
|
# Optional stylelint cache
|
||||||
|
.stylelintcache
|
||||||
|
|
||||||
|
# Microbundle cache
|
||||||
|
.rpt2_cache/
|
||||||
|
.rts2_cache_cjs/
|
||||||
|
.rts2_cache_es/
|
||||||
|
.rts2_cache_umd/
|
||||||
|
|
||||||
|
# Optional REPL history
|
||||||
|
.node_repl_history
|
||||||
|
|
||||||
|
# Output of 'npm pack'
|
||||||
|
*.tgz
|
||||||
|
|
||||||
|
# Yarn Integrity file
|
||||||
|
.yarn-integrity
|
||||||
|
|
||||||
|
# dotenv environment variable files
|
||||||
|
.env
|
||||||
|
.env.development.local
|
||||||
|
.env.test.local
|
||||||
|
.env.production.local
|
||||||
|
.env.local
|
||||||
|
|
||||||
|
# parcel-bundler cache (https://parceljs.org/)
|
||||||
|
.cache
|
||||||
|
.parcel-cache
|
||||||
|
|
||||||
|
# Next.js build output
|
||||||
|
.next
|
||||||
|
out
|
||||||
|
|
||||||
|
# Nuxt.js build / generate output
|
||||||
|
.nuxt
|
||||||
|
dist
|
||||||
|
|
||||||
|
# Gatsby files
|
||||||
|
.cache/
|
||||||
|
# Comment in the public line in if your project uses Gatsby and *not* Next.js
|
||||||
|
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||||
|
# public
|
||||||
|
|
||||||
|
# vuepress build output
|
||||||
|
.vuepress/dist
|
||||||
|
|
||||||
|
# vuepress v2.x temp and cache directory
|
||||||
|
.temp
|
||||||
|
.cache
|
||||||
|
|
||||||
|
# Docusaurus cache and generated files
|
||||||
|
.docusaurus
|
||||||
|
|
||||||
|
# Serverless directories
|
||||||
|
.serverless/
|
||||||
|
|
||||||
|
# FuseBox cache
|
||||||
|
.fusebox/
|
||||||
|
|
||||||
|
# DynamoDB Local files
|
||||||
|
.dynamodb/
|
||||||
|
|
||||||
|
# TernJS port file
|
||||||
|
.tern-port
|
||||||
|
|
||||||
|
# Stores VSCode versions used for testing VSCode extensions
|
||||||
|
.vscode-test
|
||||||
|
|
||||||
|
# yarn v2
|
||||||
|
.yarn/cache
|
||||||
|
.yarn/unplugged
|
||||||
|
.yarn/build-state.yml
|
||||||
|
.yarn/install-state.gz
|
||||||
|
.pnp.*
|
||||||
|
|
||||||
|
# Source-files
|
||||||
|
[Ss]rc/
|
||||||
|
|
||||||
|
# TypeScript config-files
|
||||||
|
tsconfig.json
|
||||||
|
tsconfig.*.json
|
||||||
|
|
||||||
|
# Lint config-files
|
||||||
|
.eslintrc
|
||||||
|
.eslintrc.*
|
||||||
|
|
||||||
|
# Source-maps
|
||||||
|
[Ll]ib/**/*.map
|
||||||
|
|
||||||
|
# Unit-Tests
|
||||||
|
.mocharc.*
|
||||||
|
[Ll]ib/tests/
|
||||||
|
|
||||||
|
# Visual Studio Code-Environment
|
||||||
|
.vscode/
|
||||||
|
|
||||||
|
# GitHub configuration
|
||||||
|
.github/
|
||||||
|
|
||||||
|
# CI configuration
|
||||||
|
.drone.yml
|
||||||
|
.woodpecker.yml
|
||||||
|
|
||||||
|
# Build Environment
|
||||||
|
gulp/
|
||||||
|
gulpfile.ts
|
||||||
|
|
||||||
|
# Temporary release-assets
|
||||||
|
.tagName.txt
|
||||||
|
.tagHeading.txt
|
||||||
|
.releaseNotes.md
|
||||||
|
.releaseTitle.md
|
11
packages/server/app.jsconfig.json
Normal file
11
packages/server/app.jsconfig.json
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
{
|
||||||
|
"extends": "./tsconfig.base.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"allowJs": true,
|
||||||
|
"checkJs": true,
|
||||||
|
"composite": true
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"./src/**/*"
|
||||||
|
]
|
||||||
|
}
|
10
packages/server/eslint.jsconfig.json
Normal file
10
packages/server/eslint.jsconfig.json
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
{
|
||||||
|
"extends": "./tsconfig.base.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"allowJs": true,
|
||||||
|
"checkJs": true
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"./.eslintrc.cjs"
|
||||||
|
]
|
||||||
|
}
|
19
packages/server/package.json
Normal file
19
packages/server/package.json
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
{
|
||||||
|
"name": "connect-force-server",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"type": "module",
|
||||||
|
"description": "A server for Storing ConnectForce Savegames",
|
||||||
|
"author": "Manuel Thalmann <m@nuth.ch>",
|
||||||
|
"scripts": {
|
||||||
|
"start": "noderel -e ./src/main.js"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"express": "^4.18.2",
|
||||||
|
"randexp": "^0.5.3"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/express": "^4.17.15",
|
||||||
|
"@types/node": "^18.11.15",
|
||||||
|
"noderel": "^1.0.13"
|
||||||
|
}
|
||||||
|
}
|
27
packages/server/src/HTTPError.js
Normal file
27
packages/server/src/HTTPError.js
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
/**
|
||||||
|
* Represents an http error.
|
||||||
|
*/
|
||||||
|
export class HTTPError extends Error
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The http status code.
|
||||||
|
*
|
||||||
|
* @type {number}
|
||||||
|
*/
|
||||||
|
status;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes a new instance of the {@link HTTPError `HTTPError`} class.
|
||||||
|
*
|
||||||
|
* @param {number} status
|
||||||
|
* The http status code.
|
||||||
|
*
|
||||||
|
* @param {string} message
|
||||||
|
* The error message.
|
||||||
|
*/
|
||||||
|
constructor(status, message)
|
||||||
|
{
|
||||||
|
super(message);
|
||||||
|
this.status = status;
|
||||||
|
}
|
||||||
|
}
|
150
packages/server/src/main.js
Normal file
150
packages/server/src/main.js
Normal file
|
@ -0,0 +1,150 @@
|
||||||
|
import { join } from "path";
|
||||||
|
import { fileURLToPath } from "url";
|
||||||
|
import express from "express";
|
||||||
|
import RandExp from "randexp";
|
||||||
|
import { HTTPError } from "./HTTPError.js";
|
||||||
|
|
||||||
|
const { randexp } = RandExp;
|
||||||
|
|
||||||
|
const dirname = fileURLToPath(new URL(".", import.meta.url));
|
||||||
|
const app = express();
|
||||||
|
|
||||||
|
const apiKeys = [
|
||||||
|
"c4game"
|
||||||
|
];
|
||||||
|
|
||||||
|
const dataPath = "/api/data";
|
||||||
|
const parametrizedDataPath = "/api/data/:id";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The data provided by the api.
|
||||||
|
*
|
||||||
|
* @type {Record<string, unknown>}
|
||||||
|
*/
|
||||||
|
let data = {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new guid.
|
||||||
|
*
|
||||||
|
* @returns {string}
|
||||||
|
* The newly created guid.
|
||||||
|
*/
|
||||||
|
function createGuid()
|
||||||
|
{
|
||||||
|
return randexp(/[0-9a-f]{8}(-[0-9a-f]{4}){4}[0-9a-f]{8}/);
|
||||||
|
}
|
||||||
|
|
||||||
|
app.use(express.static(join(dirname, "..", "..", "game", "lib", "static")));
|
||||||
|
app.use("/api", express.json());
|
||||||
|
|
||||||
|
app.use(
|
||||||
|
"/api",
|
||||||
|
(request, response, next) =>
|
||||||
|
{
|
||||||
|
const keyParam = "token";
|
||||||
|
|
||||||
|
if (keyParam in request.query)
|
||||||
|
{
|
||||||
|
let key = request.query[keyParam];
|
||||||
|
|
||||||
|
if (typeof key === "string" && apiKeys.includes(key))
|
||||||
|
{
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
next(new HTTPError(401, "The specified API token is invalid"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
next(new HTTPError(401, "An API token is required"));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
app.get(
|
||||||
|
parametrizedDataPath,
|
||||||
|
(request, response, next) =>
|
||||||
|
{
|
||||||
|
let id = request.params.id;
|
||||||
|
console.log(`Data ID \`${id}\` requested`);
|
||||||
|
|
||||||
|
if (id in data)
|
||||||
|
{
|
||||||
|
response.send(data[id]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
app.post(
|
||||||
|
dataPath,
|
||||||
|
(request, response, next) =>
|
||||||
|
{
|
||||||
|
let id = createGuid();
|
||||||
|
data[id] = request.body;
|
||||||
|
response.send({ id });
|
||||||
|
});
|
||||||
|
|
||||||
|
app.put(
|
||||||
|
parametrizedDataPath,
|
||||||
|
(request, response, next) =>
|
||||||
|
{
|
||||||
|
let id = request.params.id;
|
||||||
|
|
||||||
|
if (id in data)
|
||||||
|
{
|
||||||
|
data[id] = request.body;
|
||||||
|
response.send(data[id]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
app.delete(
|
||||||
|
parametrizedDataPath,
|
||||||
|
(request, response, next) =>
|
||||||
|
{
|
||||||
|
let id = request.params.id;
|
||||||
|
|
||||||
|
if (id in data)
|
||||||
|
{
|
||||||
|
delete data[id];
|
||||||
|
response.send();
|
||||||
|
response.status(204);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
app.use(
|
||||||
|
[
|
||||||
|
(error, request, response, next) =>
|
||||||
|
{
|
||||||
|
response.send(`${error}`);
|
||||||
|
response.status(error instanceof HTTPError ? error.status : 500);
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
|
||||||
|
app.use(
|
||||||
|
"/api",
|
||||||
|
(request, response) =>
|
||||||
|
{
|
||||||
|
response.send({ error: "Not Found" });
|
||||||
|
response.status(404);
|
||||||
|
});
|
||||||
|
|
||||||
|
app.use(
|
||||||
|
(request, response) =>
|
||||||
|
{
|
||||||
|
response.send("Not Found");
|
||||||
|
response.status(404);
|
||||||
|
});
|
||||||
|
|
||||||
|
app.listen(1337);
|
3
packages/server/tsconfig.base.json
Normal file
3
packages/server/tsconfig.base.json
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"extends": "../../tsconfig.base.json"
|
||||||
|
}
|
12
packages/server/tsconfig.json
Normal file
12
packages/server/tsconfig.json
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
{
|
||||||
|
"extends": "./tsconfig.base.json",
|
||||||
|
"references": [
|
||||||
|
{
|
||||||
|
"path": "./app.jsconfig.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "./eslint.jsconfig.json"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"include": []
|
||||||
|
}
|
|
@ -1,61 +0,0 @@
|
||||||
import { Game } from "./Game.js";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The game that is being played.
|
|
||||||
*
|
|
||||||
* @type {Game}
|
|
||||||
*/
|
|
||||||
let game;
|
|
||||||
|
|
||||||
const saveGameKey = "connect-force-save";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the save button.
|
|
||||||
*
|
|
||||||
* @returns {HTMLButtonElement}
|
|
||||||
* The save button.
|
|
||||||
*/
|
|
||||||
function getSaveButton()
|
|
||||||
{
|
|
||||||
return document.querySelector(".save");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the load button.
|
|
||||||
*
|
|
||||||
* @returns {HTMLButtonElement}
|
|
||||||
* The load button.
|
|
||||||
*/
|
|
||||||
function getLoadButton()
|
|
||||||
{
|
|
||||||
return document.querySelector(".load");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes the board.
|
|
||||||
*/
|
|
||||||
function initialize()
|
|
||||||
{
|
|
||||||
game = new Game("game");
|
|
||||||
game.initialize();
|
|
||||||
|
|
||||||
(/** @type {HTMLElement} */ (document.querySelector(".new-game"))).onclick = (event) =>
|
|
||||||
{
|
|
||||||
event.preventDefault();
|
|
||||||
game.reset();
|
|
||||||
};
|
|
||||||
|
|
||||||
getSaveButton().onclick = async (event) =>
|
|
||||||
{
|
|
||||||
event.preventDefault();
|
|
||||||
localStorage.setItem(saveGameKey, JSON.stringify(game.dump()));
|
|
||||||
};
|
|
||||||
|
|
||||||
getLoadButton().onclick = async (event) =>
|
|
||||||
{
|
|
||||||
event.preventDefault();
|
|
||||||
game.load(JSON.parse(localStorage.getItem(saveGameKey)));
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
initialize();
|
|
|
@ -1,14 +1,11 @@
|
||||||
{
|
{
|
||||||
"extends": "./tsconfig.base.json",
|
"extends": "./tsconfig.base.json",
|
||||||
"references": [
|
"references": [
|
||||||
{
|
|
||||||
"path": "./app.jsconfig.json"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"path": "./eslint.jsconfig.json"
|
"path": "./eslint.jsconfig.json"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "./gulp.tsconfig.json"
|
"path": "./packages/game"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"include": []
|
"include": []
|
||||||
|
|
Loading…
Reference in a new issue