Browse Source

changed to vite building and docker skeleton

master
narnaud 2 years ago
parent
commit
d0e8fa5fc8
  1. 3
      .env
  2. 26
      .gitignore
  3. 8
      .prettierrc
  4. 30
      Makefile
  5. 6
      cont/back/Dockerfile
  6. 8
      cont/front/Dockerfile
  7. 31
      docker-compose.yml
  8. 28
      package.json
  9. 63
      public/global.css
  10. BIN
      public/img/pog.jpg
  11. BIN
      public/img/pong.png
  12. BIN
      public/img/profileicon.png
  13. 17
      public/index.html
  14. 84
      rollup.config.js
  15. 121
      scripts/setupTypeScript.js
  16. 82
      src/App.svelte
  17. 80
      src/components/Friends.svelte
  18. 58
      src/components/MatchHistory.svelte
  19. 106
      src/components/NavBar.svelte
  20. 38
      src/components/Play.svelte
  21. 15
      src/components/Pong/Ball.ts
  22. 80
      src/components/Pong/Game.ts
  23. 29
      src/components/Pong/Paddle.ts
  24. 19
      src/components/Pong/Player.ts
  25. 56
      src/components/Pong/Pong.svelte
  26. 32
      src/components/Pong/constants.ts
  27. 88
      src/components/Pong/utils.ts
  28. 109
      src/components/Profile.svelte
  29. 53
      src/components/Spectate.svelte
  30. 7
      src/main.ts
  31. 32
      tsconfig.json
  32. 47
      volumes/front/README.md
  33. 17
      volumes/front/index.html
  34. 1474
      volumes/front/package-lock.json
  35. 21
      volumes/front/package.json
  36. 7
      volumes/front/svelte.config.js
  37. 21
      volumes/front/tsconfig.json
  38. 8
      volumes/front/tsconfig.node.json
  39. 7
      volumes/front/vite.config.ts

3
.env

@ -0,0 +1,3 @@
POSTGRES_USER: postgres_usr
POSTGRES_PASSWORD: postgres_pw
POSTGRES_DB: transcendence

26
.gitignore

@ -1,5 +1,23 @@
node_modules
package-lock.json
yarn.lock
*
!.gitignore
!LICENCE
!README.md
!Makefile
!docker-compose.yml
!.env
!cont/
!cont/front
!cont/back
!cont/front/*
!cont/back/*
!volumes/
!volumes/front
!volumes/back
!volumes/front/*
!volumes/back/*
public

8
.prettierrc

@ -1,8 +0,0 @@
{
"singleQuote": true,
"trailingComma": "none",
"semi": true,
"printWidth": 120,
"tabWidth": 4,
"useTabs": true
}

30
Makefile

@ -0,0 +1,30 @@
NAME = transcendence
USER = gavaniwast
CERT = volumes/ssl/$(NAME).pem
all: clean start
$(CERT):
openssl req -newkey rsa:4096 -x509 -sha256 -days 365 -nodes \
-out $(NAME).pem -keyout $(NAME).pem \
-subj "/C=FR/ST=Nice/L=Nice/O=42/OU=student/CN=$(NAME)_$(USER)/"
mkdir -p volumes/ssl
mv $(NAME).pem volumes/ssl
start: $(CERT)
sudo docker compose -f docker-compose.yml up --build
debug: $(CERT)
sudo BUILDKIT_PROGRESS=plain docker compose -f docker-compose.yml up --build
stop:
sudo docker compose -f docker-compose.yml down
clean: stop
sudo docker system prune -f
fclean: stop
sudo docker system prune -af --volumes
re: fclean start

6
cont/back/Dockerfile

@ -0,0 +1,6 @@
FROM alpine:3.15
RUN apk update && apk upgrade && apk add --no-cache npm \
&& npm install -g @nestjs/cli
WORKDIR /var/html/

8
cont/front/Dockerfile

@ -0,0 +1,8 @@
FROM alpine:3.15
RUN apk update && apk upgrade && apk add npm nginx
RUN mkdir -p /var/ssl
WORKDIR /var/www/html
ENTRYPOINT npm install && npm build && npm preview

31
docker-compose.yml

@ -0,0 +1,31 @@
version: '3.8'
networks:
transcendence:
services:
front:
container_name: front
build: cont/front/
env_file: .env
depends_on: [postgres, back]
ports: [80:80]
volumes: [./volumes/front:/var/www/html]
networks: [transcendence]
restart: always
back:
container_name: back
build: cont/back/
env_file: .env
depends_on: [postgres]
ports: [3030:3030]
networks: [transcendence]
volumes: [./volumes/back:/var/www/html]
restart: always
postgres:
container_name: postgres
image: postgres
ports: [5432:5432]
restart: always
env_file: .env

28
package.json

@ -1,28 +0,0 @@
{
"name": "svelte-app",
"version": "1.0.0",
"private": true,
"scripts": {
"build": "rollup -c",
"dev": "rollup -c -w",
"start": "sirv public --no-clear"
},
"devDependencies": {
"@rollup/plugin-commonjs": "^17.0.0",
"@rollup/plugin-node-resolve": "^11.0.0",
"@tsconfig/svelte": "^3.0.0",
"rollup": "^2.3.4",
"rollup-plugin-css-only": "^3.1.0",
"rollup-plugin-livereload": "^2.0.0",
"rollup-plugin-svelte": "^7.0.0",
"rollup-plugin-terser": "^7.0.0",
"svelte": "^3.0.0"
},
"dependencies": {
"@rollup/plugin-typescript": "^11.0.0",
"sirv-cli": "^2.0.0",
"svelte-check": "^3.0.3",
"svelte-preprocess": "^5.0.1",
"svelte-routing": "^1.6.0"
}
}

63
public/global.css

@ -1,63 +0,0 @@
html, body {
position: relative;
width: 100%;
height: 100%;
}
body {
color: #333;
margin: 0;
padding: 8px;
box-sizing: border-box;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
}
a {
color: rgb(0,100,200);
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
a:visited {
color: rgb(0,80,160);
}
label {
display: block;
}
input, button, select, textarea {
font-family: inherit;
font-size: inherit;
-webkit-padding: 0.4em 0;
padding: 0.4em;
margin: 0 0 0.5em 0;
box-sizing: border-box;
border: 1px solid #ccc;
border-radius: 2px;
}
input:disabled {
color: #ccc;
}
button {
color: #333;
background-color: #f4f4f4;
outline: none;
}
button:disabled {
color: #999;
}
button:not(:disabled):active {
background-color: #ddd;
}
button:focus {
border-color: #666;
}

BIN
public/img/pog.jpg

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.1 KiB

BIN
public/img/pong.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

BIN
public/img/profileicon.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.0 KiB

17
public/index.html

@ -1,17 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset='utf-8'>
<meta name='viewport' content='width=device-width,initial-scale=1'>
<title>Pong</title>
<link rel='icon' type='image/png' href='/img/pog.jpg'>
<link rel='stylesheet' href='/global.css'>
<link rel='stylesheet' href='/build/bundle.css'>
<script defer src='/build/bundle.js'></script>
</head>
<body>
</body>
</html>

84
rollup.config.js

@ -1,84 +0,0 @@
import svelte from "rollup-plugin-svelte";
import commonjs from "@rollup/plugin-commonjs";
import resolve from "@rollup/plugin-node-resolve";
import livereload from "rollup-plugin-livereload";
import { terser } from "rollup-plugin-terser";
import css from "rollup-plugin-css-only";
import autoPreprocess from "svelte-preprocess";
import typescript from "@rollup/plugin-typescript";
const production = !process.env.ROLLUP_WATCH;
function serve() {
let server;
function toExit() {
if (server) server.kill(0);
}
return {
writeBundle() {
if (server) return;
server = require("child_process").spawn(
"npm",
["run", "start", "--", "--dev"],
{
stdio: ["ignore", "inherit", "inherit"],
shell: true,
}
);
process.on("SIGTERM", toExit);
process.on("exit", toExit);
},
};
}
export default {
input: "src/main.ts",
output: {
sourcemap: true,
format: "iife",
name: "app",
file: "public/build/bundle.js",
},
plugins: [
svelte({
preprocess: autoPreprocess(),
compilerOptions: {
// enable run-time checks when not in production
dev: !production,
},
}),
typescript({ sourceMap: !production }),
// we'll extract any component CSS out into
// a separate file - better for performance
css({ output: "bundle.css" }),
// If you have external dependencies installed from
// npm, you'll most likely need these plugins. In
// some cases you'll need additional configuration -
// consult the documentation for details:
// https://github.com/rollup/plugins/tree/master/packages/commonjs
resolve({
browser: true,
dedupe: ["svelte"],
}),
commonjs(),
// In dev mode, call `npm run start` once
// the bundle has been generated
!production && serve(),
// Watch the `public` directory and refresh the
// browser on changes when not in production
!production && livereload("public"),
// If we're building for production (npm run build
// instead of npm run dev), minify
production && terser(),
],
watch: {
clearScreen: false,
},
};

121
scripts/setupTypeScript.js

@ -1,121 +0,0 @@
// @ts-check
/** This script modifies the project to support TS code in .svelte files like:
<script lang="ts">
export let name: string;
</script>
As well as validating the code for CI.
*/
/** To work on this script:
rm -rf test-template template && git clone sveltejs/template test-template && node scripts/setupTypeScript.js test-template
*/
const fs = require("fs")
const path = require("path")
const { argv } = require("process")
const projectRoot = argv[2] || path.join(__dirname, "..")
// Add deps to pkg.json
const packageJSON = JSON.parse(fs.readFileSync(path.join(projectRoot, "package.json"), "utf8"))
packageJSON.devDependencies = Object.assign(packageJSON.devDependencies, {
"svelte-check": "^2.0.0",
"svelte-preprocess": "^4.0.0",
"@rollup/plugin-typescript": "^8.0.0",
"typescript": "^4.0.0",
"tslib": "^2.0.0",
"@tsconfig/svelte": "^2.0.0"
})
// Add script for checking
packageJSON.scripts = Object.assign(packageJSON.scripts, {
"check": "svelte-check --tsconfig ./tsconfig.json"
})
// Write the package JSON
fs.writeFileSync(path.join(projectRoot, "package.json"), JSON.stringify(packageJSON, null, " "))
// mv src/main.js to main.ts - note, we need to edit rollup.config.js for this too
const beforeMainJSPath = path.join(projectRoot, "src", "main.js")
const afterMainTSPath = path.join(projectRoot, "src", "main.ts")
fs.renameSync(beforeMainJSPath, afterMainTSPath)
// Switch the app.svelte file to use TS
const appSveltePath = path.join(projectRoot, "src", "App.svelte")
let appFile = fs.readFileSync(appSveltePath, "utf8")
appFile = appFile.replace("<script>", '<script lang="ts">')
appFile = appFile.replace("export let name;", 'export let name: string;')
fs.writeFileSync(appSveltePath, appFile)
// Edit rollup config
const rollupConfigPath = path.join(projectRoot, "rollup.config.js")
let rollupConfig = fs.readFileSync(rollupConfigPath, "utf8")
// Edit imports
rollupConfig = rollupConfig.replace(`'rollup-plugin-terser';`, `'rollup-plugin-terser';
import sveltePreprocess from 'svelte-preprocess';
import typescript from '@rollup/plugin-typescript';`)
// Replace name of entry point
rollupConfig = rollupConfig.replace(`'src/main.js'`, `'src/main.ts'`)
// Add preprocessor
rollupConfig = rollupConfig.replace(
'compilerOptions:',
'preprocess: sveltePreprocess({ sourceMap: !production }),\n\t\t\tcompilerOptions:'
);
// Add TypeScript
rollupConfig = rollupConfig.replace(
'commonjs(),',
'commonjs(),\n\t\ttypescript({\n\t\t\tsourceMap: !production,\n\t\t\tinlineSources: !production\n\t\t}),'
);
fs.writeFileSync(rollupConfigPath, rollupConfig)
// Add TSConfig
const tsconfig = `{
"extends": "@tsconfig/svelte/tsconfig.json",
"include": ["src/**/*"],
"exclude": ["node_modules/*", "__sapper__/*", "public/*"]
}`
const tsconfigPath = path.join(projectRoot, "tsconfig.json")
fs.writeFileSync(tsconfigPath, tsconfig)
// Add global.d.ts
const dtsPath = path.join(projectRoot, "src", "global.d.ts")
fs.writeFileSync(dtsPath, `/// <reference types="svelte" />`)
// Delete this script, but not during testing
if (!argv[2]) {
// Remove the script
fs.unlinkSync(path.join(__filename))
// Check for Mac's DS_store file, and if it's the only one left remove it
const remainingFiles = fs.readdirSync(path.join(__dirname))
if (remainingFiles.length === 1 && remainingFiles[0] === '.DS_store') {
fs.unlinkSync(path.join(__dirname, '.DS_store'))
}
// Check if the scripts folder is empty
if (fs.readdirSync(path.join(__dirname)).length === 0) {
// Remove the scripts folder
fs.rmdirSync(path.join(__dirname))
}
}
// Adds the extension recommendation
fs.mkdirSync(path.join(projectRoot, ".vscode"), { recursive: true })
fs.writeFileSync(path.join(projectRoot, ".vscode", "extensions.json"), `{
"recommendations": ["svelte.svelte-vscode"]
}
`)
console.log("Converted to TypeScript.")
if (fs.existsSync(path.join(projectRoot, "node_modules"))) {
console.log("\nYou will need to re-run your dependency manager to get started.")
}

82
src/App.svelte

@ -1,82 +0,0 @@
<script lang="ts">
import Navbar from './components/NavBar.svelte';
import Profile from './components/Profile.svelte';
import MatchHistory from './components/MatchHistory.svelte';
import type { Match } from './components/MatchHistory.svelte';
import Friends from './components/Friends.svelte';
import type { Friend } from './components/Friends.svelte';
import Spectate from './components/Spectate.svelte';
import type { SpectateType } from './components/Spectate.svelte';
import Play from './components/Play.svelte';
import Pong from './components/Pong/Pong.svelte';
let isProfileOpen = false;
function clickProfile() {
isProfileOpen = true;
}
let isHistoryOpen = false;
function clickHistory() {
isHistoryOpen = true;
}
let matches: Array<Match> = [
{ winner: 'Alice', loser: 'Bob', points: -5, rank: '22' },
{ winner: 'Alice', loser: 'Bob', points: 10, rank: '24' },
{ winner: 'Alice', loser: 'Bob', points: 10, rank: '24' },
{ winner: 'Alice', loser: 'Bob', points: 7, rank: '23' },
{ winner: 'Alice', loser: 'Bob', points: 10, rank: '24' },
{ winner: 'Alice', loser: 'Bob', points: 10, rank: '24' }
];
let isFriendOpen = false;
function clickFriends() {
isFriendOpen = true;
}
let friends: Array<Friend> = [
{ username: 'Alice', status: 'online' },
{ username: 'Bob', status: 'online' },
{ username: 'Charlie', status: 'offline' },
{ username: 'Dave', status: 'offline' },
{ username: 'Eve', status: 'in a game' },
{ username: 'Frank', status: 'online' }
];
let isSpectateOpen = false;
function clickSpectate() {
isSpectateOpen = true;
}
let spectate: Array<SpectateType> = [
{ player1: 'Alice', player2: 'Bob', id: '1' },
{ player1: 'Alice', player2: 'Bob', id: '4' },
{ player1: 'Alice', player2: 'Bob', id: '6' },
{ player1: 'Alice', player2: 'Bob', id: '8' },
{ player1: 'Alice', player2: 'Bob', id: '2' },
{ player1: 'Alice', player2: 'Bob', id: '3' }
];
</script>
<main>
<Navbar {clickProfile} {clickHistory} {clickFriends} {clickSpectate} />
{#if isSpectateOpen}
<div on:click={() => (isSpectateOpen = false)} on:keydown={() => (isSpectateOpen = false)}>
<Spectate {spectate} />
</div>
{/if}
{#if isFriendOpen}
<div on:click={() => (isFriendOpen = false)} on:keydown={() => (isFriendOpen = false)}>
<Friends {friends} />
</div>
{/if}
{#if isHistoryOpen}
<div on:click={() => (isHistoryOpen = false)} on:keydown={() => (isHistoryOpen = false)}>
<MatchHistory {matches} />
</div>
{/if}
{#if isProfileOpen}
<div on:click={() => (isProfileOpen = false)} on:keydown={() => (isProfileOpen = false)}>
<Profile username="Alice" wins={10} losses={5} elo={256} rank={23} is2faEnabled={false} />
</div>
{/if}
<Play />
<Pong />
</main>
<style>
</style>

80
src/components/Friends.svelte

@ -1,80 +0,0 @@
<script lang="ts" context="module">
export interface Friend {
username: string;
status: 'online' | 'offline' | 'in a game';
}
</script>
<script lang="ts">
export let friends: Array<Friend> = [];
async function addFriend(event: any) {
console.log(typeof event);
event.preventDefault();
const usernameInput = event.target.querySelector('input[type="text"]');
console.log(usernameInput);
const username = usernameInput.value;
// const response = await fetch('', {
// method: 'POST',
// headers: {
// 'Content-Type': 'application/json'
// },
// body: JSON.stringify({ username })
// });
// if (response.ok) {
// console.log('Friend added successfully');
// } else {
// console.log('Failed to add friend');
// }
// usernameInput.value = '';
alert('Trying to add friend' + username);
}
</script>
<div class="overlay">
<div class="friends" on:click|stopPropagation on:keydown|stopPropagation>
<div>
{#if friends.length > 0}
<h2>Monkey friends</h2>
{#each friends.slice(0, 10) as friend}
<li>
<span>{friend.username} is {friend.status}</span>
</li>
{/each}
{:else}
<p>No friends to display</p>
{/if}
<div>
<h3>Add a friend</h3>
<form on:submit={addFriend}>
<input type="text" />
<button type="submit">Add</button>
</form>
</div>
</div>
</div>
</div>
<style>
.overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
z-index: 9998;
display: flex;
justify-content: center;
align-items: center;
}
.friends {
background-color: #fff;
border: 1px solid #ccc;
border-radius: 5px;
padding: 1rem;
width: 300px;
}
</style>

58
src/components/MatchHistory.svelte

@ -1,58 +0,0 @@
<script lang="ts" context="module">
export interface Match {
winner: string;
loser: string;
points: number;
rank: string;
}
</script>
<script lang="ts">
export let matches: Array<Match> = [];
</script>
<div class="overlay">
<div class="history" on:click|stopPropagation on:keydown|stopPropagation>
<div>
{#if matches.length > 0}
<h2>Last 10 monkey games</h2>
{#each matches.slice(0, 10) as match}
<li>
<span>{match.winner} 1 - 0 {match.loser}</span>
{#if match.points > 0}
<span>+{match.points}</span>
{:else}
<span>{match.points}</span>
{/if}
<span>MP | rank #{match.rank}</span>
</li>
{/each}
{:else}
<p>No matches to display</p>
{/if}
</div>
</div>
</div>
<style>
.overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
z-index: 9998;
display: flex;
justify-content: center;
align-items: center;
}
.history {
background-color: #fff;
border: 1px solid #ccc;
border-radius: 5px;
padding: 1rem;
width: 300px;
}
</style>

106
src/components/NavBar.svelte

@ -1,106 +0,0 @@
<script lang="ts">
export let links = [
{ text: 'Home', url: 'img/pong.png' },
{ text: 'Spectate' },
{ text: 'Chat' },
{ text: 'History' },
{ text: 'Friends' },
{ text: 'Profile' }
];
export let clickProfile = () => {};
export let clickHistory = () => {};
export let clickFriends = () => {};
export let clickSpectate = () => {};
</script>
<nav class="navigation-bar">
<ul>
{#each links as link}
{#if link.text === 'Spectate'}
<li>
<button on:click={clickSpectate}>
<p>Spectate</p>
</button>
</li>
{/if}
{#if link.text === 'Friends'}
<li>
<button on:click={clickFriends}>
<p>Friends</p>
</button>
</li>
{/if}
{#if link.text === 'Profile'}
<li>
<button on:click={clickProfile}>
<img src="img/profileicon.png" alt="profile icon" />
</button>
</li>
{/if}
{#if link.text === 'History'}
<li>
<button on:click={clickHistory}>
<p>History</p>
</button>
</li>
{/if}
{#if link.text === 'Home'}
<li>
<a href="/">
<img src="img/pong.png" alt="home-icon" />
</a>
</li>
{/if}
{/each}
</ul>
</nav>
<style>
.navigation-bar {
display: flex;
justify-content: center;
align-items: center;
background-color: #f5f5f5;
padding: 1rem;
}
.navigation-bar ul {
display: flex;
justify-content: center;
align-items: center;
list-style: none;
padding: 0;
}
.navigation-bar li {
margin: 0 1rem;
}
.navigation-bar img {
width: 2rem;
height: 2rem;
}
.navigation-bar button {
border: none;
}
@media (max-width: 768px) {
.navigation-bar {
flex-direction: column;
align-items: stretch;
}
.navigation-bar ul {
flex-direction: column;
align-items: center;
justify-content: center;
}
.navigation-bar li {
margin: 0;
padding: 1rem;
text-align: center;
}
}
</style>

38
src/components/Play.svelte

@ -1,38 +0,0 @@
<script lang="ts">
// import { navigate } from 'svelte-routing';
// function handleMatchmakingClick() {
// navigate('/matchmaking');
// }
// function handlePlayWithFriendClick() {
// navigate('/play-with-friend');
// }
</script>
<!-- dr -->
<main>
<h1>Choose a gamemode</h1>
<!-- <button on:click={handleMatchmakingClick}>Matchmaking</button>
<button on:click={handlePlayWithFriendClick}>Play with a friend</button> -->
<button>Matchmaking</button>
<button>Play with a friend</button>
</main>
<style>
main {
display: flex;
flex-direction: column;
align-items: center;
}
h1 {
margin-bottom: 2rem;
}
button {
font-size: 1.5rem;
padding: 1rem 2rem;
margin-bottom: 1rem;
}
</style>

15
src/components/Pong/Ball.ts

@ -1,15 +0,0 @@
import { Point, Rect } from './utils';
export class Ball {
rect: Rect;
speed: Point;
color: string | CanvasGradient | CanvasPattern = 'white';
constructor(spawn: Point, size: Point = new Point(20, 20), speed: Point = new Point(10, 2)) {
this.rect = new Rect(spawn, size);
}
draw(context: CanvasRenderingContext2D) {
this.rect.draw(context, this.color);
}
}

80
src/components/Pong/Game.ts

@ -1,80 +0,0 @@
import { Ball } from './Ball';
import { GAME_EVENTS } from './constants';
import type { GameInfo, GameUpdate } from './constants';
import { Paddle } from './Paddle';
import { Player } from './Player';
import { formatWebsocketData, Point } from './utils';
const BG_COLOR = 'black';
export class Game {
canvas: HTMLCanvasElement;
context: CanvasRenderingContext2D;
ball: Ball;
players: Player[];
my_paddle: Paddle;
constructor(canvas: HTMLCanvasElement, context: CanvasRenderingContext2D) {
this.canvas = canvas;
this.context = context;
this.players = [];
this.my_paddle = null;
}
setInfo(data: GameInfo) {
this.canvas.width = data.mapSize.x;
this.canvas.height = data.mapSize.y;
this.ball = new Ball(new Point(this.canvas.width / 2, this.canvas.height / 2), data.ballSize);
const paddle1: Paddle = new Paddle(new Point(data.playerXOffset, this.canvas.height / 2), data.paddleSize);
const paddle2: Paddle = new Paddle(
new Point(this.canvas.width - data.playerXOffset, this.canvas.height / 2),
data.paddleSize
);
this.players = [new Player(paddle1), new Player(paddle2)];
this.my_paddle = this.players[data.yourPaddleIndex].paddle;
}
start(socket: WebSocket) {
if (this.my_paddle) {
this.canvas.addEventListener('mousemove', (e) => {
this.my_paddle.move(e);
socket.send(
formatWebsocketData(GAME_EVENTS.PLAYER_MOVE, {
position: this.my_paddle.rect.center
})
);
});
console.log('Game started!');
}
}
update(data: GameUpdate) {
if (this.players[0].paddle != this.my_paddle) {
this.players[0].paddle.rect.center = data.paddlesPositions[0];
}
if (this.players[1].paddle != this.my_paddle) {
this.players[1].paddle.rect.center = data.paddlesPositions[1];
}
this.ball.rect.center = data.ballPosition;
for (let i = 0; i < data.scores.length; i++) {
this.players[i].score = data.scores[i];
}
}
draw() {
this.context.fillStyle = BG_COLOR;
this.context.fillRect(0, 0, this.canvas.width, this.canvas.height);
this.players.forEach((p) => p.draw(this.context));
this.ball.draw(this.context);
const max_width = 50;
this.context.font = '50px Arial';
const text_width = this.context.measureText('0').width;
const text_offset = 50;
this.players[0].drawScore(this.canvas.width / 2 - (text_width + text_offset), max_width, this.context);
this.players[1].drawScore(this.canvas.width / 2 + text_offset, max_width, this.context);
}
}

29
src/components/Pong/Paddle.ts

@ -1,29 +0,0 @@
import { Point, Rect } from './utils';
export class Paddle {
rect: Rect;
color: string | CanvasGradient | CanvasPattern = 'white';
constructor(spawn: Point, size: Point = new Point(6, 100)) {
this.rect = new Rect(spawn, size);
}
draw(context: CanvasRenderingContext2D) {
this.rect.draw(context, this.color);
}
move(e: MouseEvent) {
const canvas = e.target as HTMLCanvasElement;
const rect = canvas.getBoundingClientRect();
const new_y = ((e.clientY - rect.top) * canvas.height) / rect.height;
const offset: number = this.rect.size.y / 2;
if (new_y - offset < 0) {
this.rect.center.y = offset;
} else if (new_y + offset > canvas.height) {
this.rect.center.y = canvas.height - offset;
} else {
this.rect.center.y = new_y;
}
}
}

19
src/components/Pong/Player.ts

@ -1,19 +0,0 @@
import type { Paddle } from './Paddle';
export class Player {
paddle: Paddle;
score: number;
constructor(paddle: Paddle) {
this.paddle = paddle;
this.score = 0;
}
draw(context: CanvasRenderingContext2D) {
this.paddle.draw(context);
}
drawScore(score_position_x: number, max_width: number, context: CanvasRenderingContext2D) {
context.fillText(this.score.toString(), score_position_x, 50, max_width);
}
}

56
src/components/Pong/Pong.svelte

@ -1,56 +0,0 @@
<script lang="ts">
import { GAME_EVENTS } from './constants';
import { Game } from './Game';
import { formatWebsocketData } from './utils';
const FPS = 144;
const SERVER_URL = 'ws://localhost:3001';
let socket: WebSocket;
//Get canvas and its context
window.onload = () => {
const canvas: HTMLCanvasElement = document.getElementById('pong_canvas') as HTMLCanvasElement;
if (canvas) {
const context: CanvasRenderingContext2D = canvas.getContext('2d') as CanvasRenderingContext2D;
if (context) {
setupSocket(canvas, context);
}
}
};
function setupSocket(canvas: HTMLCanvasElement, context: CanvasRenderingContext2D) {
socket = new WebSocket(SERVER_URL);
const game = new Game(canvas, context);
socket.onmessage = function (e) {
const event_json = JSON.parse(e.data);
const event = event_json.event;
const data = event_json.data;
if (event == GAME_EVENTS.START_GAME) {
game.start(socket);
} else if (event == GAME_EVENTS.GAME_TICK) {
game.update(data);
} else if (event == GAME_EVENTS.GET_GAME_INFO) {
game.setInfo(data);
setInterval(() => {
game.draw();
}, 1000 / FPS);
console.log('Game loaded!');
} else {
console.log('Unknown event from server: ' + event);
}
};
socket.onopen = () => {
console.log('Connected to game server!');
socket.send(formatWebsocketData(GAME_EVENTS.GET_GAME_INFO));
};
}
</script>
<div>
<button on:click={() => socket.send(formatWebsocketData(GAME_EVENTS.START_GAME))}>Start game</button>
<br />
<br />
<canvas id="pong_canvas" />
</div>

32
src/components/Pong/constants.ts

@ -1,32 +0,0 @@
import { Point } from './utils';
export const GAME_EVENTS = {
START_GAME: 'START_GAME',
GAME_TICK: 'GAME_TICK',
PLAYER_MOVE: 'PLAYER_MOVE',
GET_GAME_INFO: 'GET_GAME_INFO'
};
export interface GameInfo extends GameInfoConstants {
yourPaddleIndex: number;
}
export interface GameInfoConstants {
mapSize: Point;
paddleSize: Point;
playerXOffset: number;
ballSize: Point;
winScore: number;
}
export const gameInfoConstants: GameInfoConstants = {
mapSize: new Point(600, 400),
paddleSize: new Point(6, 50),
playerXOffset: 50,
ballSize: new Point(20, 20),
winScore: 2
};
export interface GameUpdate {
paddlesPositions: Point[];
ballPosition: Point;
scores: number[];
}

88
src/components/Pong/utils.ts

@ -1,88 +0,0 @@
export class Point {
x: number;
y: number;
constructor(x: number, y: number) {
this.x = x;
this.y = y;
}
//Returns a new point
add(other: Point) {
return new Point(this.x + other.x, this.y + other.y);
}
//Modifies `this` point
add_inplace(other: Point) {
this.x += other.x;
this.y += other.y;
}
clone(): Point {
return new Point(this.x, this.y);
}
}
export class Rect {
center: Point;
size: Point;
constructor(center: Point, size: Point) {
this.center = center;
this.size = size;
}
draw(context: CanvasRenderingContext2D, color: string | CanvasGradient | CanvasPattern) {
const offset: Point = new Point(this.size.x / 2, this.size.y / 2);
context.fillStyle = color;
context.fillRect(this.center.x - offset.x, this.center.y - offset.y, this.size.x, this.size.y);
}
//True if `this` rect contains `other` rect in the x-axis
contains_x(other: Rect): boolean {
const offset: number = this.size.x / 2;
const offset_other: number = other.size.x / 2;
if (
this.center.x - offset <= other.center.x - offset_other &&
this.center.x + offset >= other.center.x + offset_other
)
return true;
return false;
}
//True if `this` rect contains `other` rect in the y-axis
contains_y(other: Rect): boolean {
const offset: number = this.size.y / 2;
const offset_other: number = other.size.y / 2;
if (
this.center.y - offset <= other.center.y - offset_other &&
this.center.y + offset >= other.center.y + offset_other
)
return true;
return false;
}
collides(other: Rect): boolean {
const offset: Point = new Point(this.size.x / 2, this.size.y / 2);
const offset_other: Point = new Point(other.size.x / 2, other.size.y / 2);
if (
this.center.x - offset.x < other.center.x + offset_other.x &&
this.center.x + offset.x > other.center.x - offset_other.x &&
this.center.y - offset.y < other.center.y + offset_other.y &&
this.center.y + offset.y > other.center.y - offset_other.y
)
return true;
return false;
}
}
export function formatWebsocketData(event: string, data?: any): string {
return JSON.stringify({
event,
data
});
}

109
src/components/Profile.svelte

@ -1,109 +0,0 @@
<script lang="ts">
export let username = '';
export let realname = '';
export let wins = 0;
export let losses = 0;
export let elo = 0;
export let rank = -1;
export let is2faEnabled = false;
async function handleSubmit(event: Event) {
event.preventDefault();
// const response = await fetch('', {
// method: 'POST',
// headers: {
// 'Content-Type': 'application/json'
// },
// body: JSON.stringify({
// username
// })
// });
// if (response.ok) {
// console.log('username updated');
// }
// else {
// console.log('username update failed');
// }
alert('Trying to update username to ' + username);
}
async function handleAvatarUpload(event: Event) {
event.preventDefault();
alert('Trying to upload avatar');
}
async function handle2fa(event: Event) {
event.preventDefault();
alert('Trying to ' + (is2faEnabled ? 'disable' : 'enable') + ' 2FA');
}
</script>
<div class="overlay">
<div class="profile" on:click|stopPropagation on:keydown|stopPropagation>
<div class="profile-header">
<img class="profile-img" src="img/profileicon.png" alt="Profile Icon" />
<h3>{realname}</h3>
<form on:submit={handleAvatarUpload}>
<button type="submit">Upload Avatar</button>
</form>
</div>
<div class="profile-body">
<form on:submit={handleSubmit}>
<div class="username">
<label for="username">Username</label>
<input type="text" id="username" bind:value={username} />
</div>
<button type="submit">Submit</button>
</form>
<p>Wins: {wins}</p>
<p>Losses: {losses}</p>
<p>Winrate: {(wins / (wins + losses)) * 100}%</p>
<p>Elo : {elo}</p>
<p>Rank: {rank}</p>
<form class="two-factor-auth" on:submit={handle2fa}>
<button type="submit">
{#if is2faEnabled}
Disable 2FA
{:else}
Enable 2FA
{/if}
</button>
</form>
</div>
</div>
</div>
<style>
.overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
z-index: 9998;
display: flex;
justify-content: center;
align-items: center;
}
.profile {
background-color: #fff;
border: 1px solid #ccc;
border-radius: 5px;
padding: 1rem;
width: 300px;
}
.profile-header {
display: flex;
align-items: center;
}
.profile-img {
width: 50px;
height: 50px;
margin-right: 1rem;
}
.two-factor-auth {
margin-top: 1rem;
}
</style>

53
src/components/Spectate.svelte

@ -1,53 +0,0 @@
<script lang="ts" context="module">
export interface SpectateType {
id: string;
player1: string;
player2: string;
}
</script>
<script lang="ts">
export let spectate: Array<SpectateType> = [];
export let watch = () => {};
</script>
<div class="overlay">
<div class="spectate" on:click|stopPropagation on:keydown|stopPropagation>
<div>
{#if spectate.length > 0}
<h2>Monkey spectating</h2>
{#each spectate.slice(0, 10) as _spectate}
<li>
<span>{_spectate.player1} VS {_spectate.player2}</span>
<button on:click={() => watch(_spectate.id)}>Spectate</button>
</li>
{/each}
{:else}
<p>No matches to spectate</p>
{/if}
</div>
</div>
</div>
<style>
.overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
z-index: 9998;
display: flex;
justify-content: center;
align-items: center;
}
.spectate {
background-color: #fff;
border: 1px solid #ccc;
border-radius: 5px;
padding: 1rem;
width: 300px;
}
</style>

7
src/main.ts

@ -1,7 +0,0 @@
import App from './App.svelte';
const app = new App({
target: document.body,
});
export default app;

32
tsconfig.json

@ -1,32 +0,0 @@
{
"compilerOptions": {
"target": "es6",
"module": "es6",
"strict": true,
"noImplicitAny": true,
"jsx": "preserve",
"importHelpers": true,
"moduleResolution": "node",
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"sourceMap": true,
"baseUrl": ".",
"types": [
"svelte"
],
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"skipDefaultLibCheck": true,
"strictNullChecks": false,
"declaration": false,
"downlevelIteration": true
},
"include": [
"src/**/*"
],
"exclude": [
"node_modules/*",
"public/*"
],
"extends": "@tsconfig/svelte/tsconfig.json"
}

47
volumes/front/README.md

@ -0,0 +1,47 @@
# Svelte + TS + Vite
This template should help get you started developing with Svelte and TypeScript in Vite.
## Recommended IDE Setup
[VS Code](https://code.visualstudio.com/) + [Svelte](https://marketplace.visualstudio.com/items?itemName=svelte.svelte-vscode).
## Need an official Svelte framework?
Check out [SvelteKit](https://github.com/sveltejs/kit#readme), which is also powered by Vite. Deploy anywhere with its serverless-first approach and adapt to various platforms, with out of the box support for TypeScript, SCSS, and Less, and easily-added support for mdsvex, GraphQL, PostCSS, Tailwind CSS, and more.
## Technical considerations
**Why use this over SvelteKit?**
- It brings its own routing solution which might not be preferable for some users.
- It is first and foremost a framework that just happens to use Vite under the hood, not a Vite app.
This template contains as little as possible to get started with Vite + TypeScript + Svelte, while taking into account the developer experience with regards to HMR and intellisense. It demonstrates capabilities on par with the other `create-vite` templates and is a good starting point for beginners dipping their toes into a Vite + Svelte project.
Should you later need the extended capabilities and extensibility provided by SvelteKit, the template has been structured similarly to SvelteKit so that it is easy to migrate.
**Why `global.d.ts` instead of `compilerOptions.types` inside `jsconfig.json` or `tsconfig.json`?**
Setting `compilerOptions.types` shuts out all other types not explicitly listed in the configuration. Using triple-slash references keeps the default TypeScript setting of accepting type information from the entire workspace, while also adding `svelte` and `vite/client` type information.
**Why include `.vscode/extensions.json`?**
Other templates indirectly recommend extensions via the README, but this file allows VS Code to prompt the user to install the recommended extension upon opening the project.
**Why enable `allowJs` in the TS template?**
While `allowJs: false` would indeed prevent the use of `.js` files in the project, it does not prevent the use of JavaScript syntax in `.svelte` files. In addition, it would force `checkJs: false`, bringing the worst of both worlds: not being able to guarantee the entire codebase is TypeScript, and also having worse typechecking for the existing JavaScript. In addition, there are valid use cases in which a mixed codebase may be relevant.
**Why is HMR not preserving my local component state?**
HMR state preservation comes with a number of gotchas! It has been disabled by default in both `svelte-hmr` and `@sveltejs/vite-plugin-svelte` due to its often surprising behavior. You can read the details [here](https://github.com/rixo/svelte-hmr#svelte-hmr).
If you have state that's important to retain within a component, consider creating an external store which would not be replaced by HMR.
```ts
// store.ts
// An extremely simple external store
import { writable } from 'svelte/store'
export default writable(0)
```

17
volumes/front/index.html

@ -0,0 +1,17 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset='utf-8'>
<meta name='viewport' content='width=device-width,initial-scale=1'>
<title>Pong</title>
<link rel='icon' type='image/png' href='/img/pog.jpg'>
<link rel='stylesheet' href='/global.css'>
<script type="module" src="src/main.ts"></script>
</head>
<body>
</body>
</html>

1474
volumes/front/package-lock.json

File diff suppressed because it is too large

21
volumes/front/package.json

@ -0,0 +1,21 @@
{
"name": "Transcendence",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview",
"check": "svelte-check --tsconfig ./tsconfig.json"
},
"devDependencies": {
"@sveltejs/vite-plugin-svelte": "^2.0.2",
"@tsconfig/svelte": "^3.0.0",
"svelte": "^3.55.1",
"svelte-check": "^2.10.3",
"tslib": "^2.5.0",
"typescript": "^4.9.3",
"vite": "^4.1.0"
}
}

7
volumes/front/svelte.config.js

@ -0,0 +1,7 @@
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'
export default {
// Consult https://svelte.dev/docs#compile-time-svelte-preprocess
// for more information about preprocessors
preprocess: vitePreprocess(),
}

21
volumes/front/tsconfig.json

@ -0,0 +1,21 @@
{
"extends": "@tsconfig/svelte/tsconfig.json",
"compilerOptions": {
"target": "ESNext",
"useDefineForClassFields": true,
"module": "ESNext",
"resolveJsonModule": true,
/**
* Typecheck JS in `.svelte` and `.js` files by default.
* Disable checkJs if you'd like to use dynamic types in JS.
* Note that setting allowJs false does not prevent the use
* of JS in `.svelte` files.
*/
"allowJs": true,
"checkJs": true,
"isolatedModules": true
},
"include": ["src/**/*.d.ts", "src/**/*.ts", "src/**/*.js", "src/**/*.svelte"],
"exclude": ["node_modules/*", "public/*"],
"references": [{ "path": "./tsconfig.node.json" }]
}

8
volumes/front/tsconfig.node.json

@ -0,0 +1,8 @@
{
"compilerOptions": {
"composite": true,
"module": "ESNext",
"moduleResolution": "Node"
},
"include": ["vite.config.ts"]
}

7
volumes/front/vite.config.ts

@ -0,0 +1,7 @@
import { defineConfig } from 'vite'
import { svelte } from '@sveltejs/vite-plugin-svelte'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [svelte()],
})
Loading…
Cancel
Save