seems like the merge worked
This commit is contained in:
@@ -8,3 +8,4 @@
|
||||
!api_front/*.json
|
||||
!api_front/*.html
|
||||
!api_front/*.lock
|
||||
!api_front/.env
|
||||
|
||||
@@ -1,107 +0,0 @@
|
||||
# This repo is no longer maintained. Consider using `npm init vite` and selecting the `svelte` option or — if you want a full-fledged app framework and don't mind using pre-1.0 software — use [SvelteKit](https://kit.svelte.dev), the official application framework for Svelte.
|
||||
|
||||
---
|
||||
|
||||
# svelte app
|
||||
|
||||
This is a project template for [Svelte](https://svelte.dev) apps. It lives at https://github.com/sveltejs/template.
|
||||
|
||||
To create a new project based on this template using [degit](https://github.com/Rich-Harris/degit):
|
||||
|
||||
```bash
|
||||
npx degit sveltejs/template svelte-app
|
||||
cd svelte-app
|
||||
```
|
||||
|
||||
*Note that you will need to have [Node.js](https://nodejs.org) installed.*
|
||||
|
||||
|
||||
## Get started
|
||||
|
||||
Install the dependencies...
|
||||
|
||||
```bash
|
||||
cd svelte-app
|
||||
npm install
|
||||
```
|
||||
|
||||
...then start [Rollup](https://rollupjs.org):
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
```
|
||||
|
||||
Navigate to [localhost:8080](http://localhost:8080). You should see your app running. Edit a component file in `src`, save it, and reload the page to see your changes.
|
||||
|
||||
By default, the server will only respond to requests from localhost. To allow connections from other computers, edit the `sirv` commands in package.json to include the option `--host 0.0.0.0`.
|
||||
|
||||
If you're using [Visual Studio Code](https://code.visualstudio.com/) we recommend installing the official extension [Svelte for VS Code](https://marketplace.visualstudio.com/items?itemName=svelte.svelte-vscode). If you are using other editors you may need to install a plugin in order to get syntax highlighting and intellisense.
|
||||
|
||||
## Building and running in production mode
|
||||
|
||||
To create an optimised version of the app:
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
```
|
||||
|
||||
You can run the newly built app with `npm run start`. This uses [sirv](https://github.com/lukeed/sirv), which is included in your package.json's `dependencies` so that the app will work when you deploy to platforms like [Heroku](https://heroku.com).
|
||||
|
||||
|
||||
## Single-page app mode
|
||||
|
||||
By default, sirv will only respond to requests that match files in `public`. This is to maximise compatibility with static fileservers, allowing you to deploy your app anywhere.
|
||||
|
||||
If you're building a single-page app (SPA) with multiple routes, sirv needs to be able to respond to requests for *any* path. You can make it so by editing the `"start"` command in package.json:
|
||||
|
||||
```js
|
||||
"start": "sirv public --single"
|
||||
```
|
||||
|
||||
## Using TypeScript
|
||||
|
||||
This template comes with a script to set up a TypeScript development environment, you can run it immediately after cloning the template with:
|
||||
|
||||
```bash
|
||||
node scripts/setupTypeScript.js
|
||||
```
|
||||
|
||||
Or remove the script via:
|
||||
|
||||
```bash
|
||||
rm scripts/setupTypeScript.js
|
||||
```
|
||||
|
||||
If you want to use `baseUrl` or `path` aliases within your `tsconfig`, you need to set up `@rollup/plugin-alias` to tell Rollup to resolve the aliases. For more info, see [this StackOverflow question](https://stackoverflow.com/questions/63427935/setup-tsconfig-path-in-svelte).
|
||||
|
||||
## Deploying to the web
|
||||
|
||||
### With [Vercel](https://vercel.com)
|
||||
|
||||
Install `vercel` if you haven't already:
|
||||
|
||||
```bash
|
||||
npm install -g vercel
|
||||
```
|
||||
|
||||
Then, from within your project folder:
|
||||
|
||||
```bash
|
||||
cd public
|
||||
vercel deploy --name my-project
|
||||
```
|
||||
|
||||
### With [surge](https://surge.sh/)
|
||||
|
||||
Install `surge` if you haven't already:
|
||||
|
||||
```bash
|
||||
npm install -g surge
|
||||
```
|
||||
|
||||
Then, from within your project folder:
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
surge public my-project.surge.sh
|
||||
```
|
||||
@@ -1,81 +0,0 @@
|
||||
<script lang="ts">
|
||||
// routing
|
||||
// may not need {link} here
|
||||
import Router, { link } from "svelte-spa-router";
|
||||
import { routes } from "../routes.js";
|
||||
|
||||
import LoginPage from "./LoginPage.svelte";
|
||||
import UserPage from "../UserPage.svelte";
|
||||
import NotFound from "../pages/NotFound.svelte";
|
||||
|
||||
// Ideally fuck all this shit in the long run
|
||||
let pages = ['login', 'user', 'account'];
|
||||
// make this a $: currentPage ?
|
||||
let currentPage = 'booba';
|
||||
// prolly change this? yea idk...
|
||||
// let userIndex;
|
||||
// tmp for testing
|
||||
let userId = 0;
|
||||
// horrible naming... can be HOME or ACCOUNT
|
||||
let currentType = 'account';
|
||||
|
||||
// this page should handle the SPA history management...
|
||||
|
||||
// set to false later for actual security
|
||||
let loggedIn = true;
|
||||
|
||||
// not sure if this is how i want to do this...
|
||||
// might do differently cuz URL route manangement...
|
||||
const handleLogin = () => {
|
||||
currentPage = 'user';
|
||||
loggedIn = true;
|
||||
};
|
||||
|
||||
// I don't know if i should but i'm tempted to put this here...
|
||||
|
||||
// import axios from 'axios';
|
||||
// import { onMount } from 'svelte';
|
||||
// import { push } from 'svelte-spa-router';
|
||||
|
||||
// let user = {logedIn: false};
|
||||
|
||||
// onMount(async () => {
|
||||
// // console.log('PROFIL SVELTE');
|
||||
// const {data} = await axios.get('http://transcendance:8080/api/v2/user');
|
||||
// if (data)
|
||||
// user.logedIn = true;
|
||||
// });
|
||||
|
||||
// $: submit = async() => {
|
||||
// window.location.href = 'http://transcendance:8080/api/v2/auth';
|
||||
// }
|
||||
|
||||
// $: logout = async() => {
|
||||
// await fetch('http://transcendance:8080/api/v2/auth/logout',);
|
||||
// user.logedIn = false;
|
||||
// }
|
||||
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<!-- eventually we will do this with routes, but for now use a prop -->
|
||||
<!-- {#if currentPage === 'login'}
|
||||
<LoginPage {pages} {currentPage} {userId} on:loggedIn={handleLogin}/>
|
||||
{:else if currentPage === 'user'}
|
||||
<UserPage {pages} {currentPage} {userId} {currentType}/>
|
||||
{:else}
|
||||
<NotFound />
|
||||
{/if} -->
|
||||
<Router {routes}/>
|
||||
|
||||
<style>
|
||||
|
||||
/* doesn't work... */
|
||||
/* body{
|
||||
background: bisque;
|
||||
} */
|
||||
</style>
|
||||
|
||||
|
||||
@@ -1,167 +0,0 @@
|
||||
<!-- <script lang="ts"> -->
|
||||
<script>
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
let canvas;
|
||||
|
||||
// $: scaleRatio = window.innerWidth / 10;
|
||||
$: scaleRatio = 30;
|
||||
|
||||
onMount(() => {
|
||||
// we're invoking JS methods of the canvas element
|
||||
const ctx = canvas.getContext('2d');
|
||||
|
||||
ctx.width = window.innerWidth;
|
||||
ctx.height = window.innerHeight;
|
||||
|
||||
// ctx.beginPath();
|
||||
// ctx.moveTo(50,50);
|
||||
// ctx.lineTo(70,70);
|
||||
// ctx.stroke();
|
||||
|
||||
// attempting to import an image
|
||||
const img = new Image();
|
||||
// we may have to call an onMount or something to make sure the image has loaded...
|
||||
// doing it in JS for now, ideally in Svelte later..
|
||||
// img.addEventListener('load', () => {
|
||||
// // execute drawImage statements here
|
||||
// }, false);
|
||||
img.src = 'img/potato_logo.png'; // seems like this does need to be above onload()
|
||||
img.onload = () => {
|
||||
// ctx.drawImage(img, 0, 0, img.width * (ctx.width / 15), img.height * (ctx.height / 15));
|
||||
// ctx.drawImage(img, 0, 0, ctx.width / 15, ctx.height / 15);
|
||||
|
||||
// i think i don't want this cuz it'll get in the way?
|
||||
// ctx.drawImage(img, 0, 0, img.width / scaleRatio, img.height / scaleRatio);
|
||||
|
||||
// it would seem you need to redraw the images when you change the Window size, it's not automatically responsive
|
||||
};
|
||||
|
||||
let startX = 200;
|
||||
let startY = 200;
|
||||
let dx = 3;
|
||||
let dy = -1;
|
||||
|
||||
// Time for some math
|
||||
// lets say i want 6 rows
|
||||
// ok so we're gonna draw all the potatos at the same time and each of them gets animated, like i have it now
|
||||
// 6 in a row so # of rows aka x = width * 6/height
|
||||
|
||||
function Potato(x, y) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
// ctx.drawImage(img, x, y, img.width / scaleRatio, img.height / scaleRatio);
|
||||
|
||||
this.draw = function() {
|
||||
ctx.drawImage(img, x, y, img.width / scaleRatio, img.height / scaleRatio);
|
||||
}
|
||||
|
||||
this.animate = function() {
|
||||
this.x += dx;
|
||||
this.y += dy;
|
||||
|
||||
this.draw();
|
||||
}
|
||||
}
|
||||
|
||||
// let spacing = canvas.width * ((6 + 1) / canvas.height);
|
||||
let spacing = 70;
|
||||
let Potatos = [];
|
||||
//check the math...
|
||||
// for (let i = 0; i < 6 * spacing; i++) {
|
||||
for (let i = 0; i < 2; i++) {
|
||||
// for (let j = 0; j < 6; j++) {
|
||||
for (let j = 0; i < 2; j++) {
|
||||
Potatos.push(new Potato(spacing + (i * spacing), spacing + (j * spacing)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// now i'm trying to move 1 potato
|
||||
let frame = requestAnimationFrame(loop);
|
||||
// function loop(t) {
|
||||
// ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
// frame = requestAnimationFrame(loop);
|
||||
// ctx.drawImage(img, startX, startY, img.width / scaleRatio, img.height / scaleRatio);
|
||||
// startX += dx;
|
||||
// startY += dy;
|
||||
// }
|
||||
|
||||
// new Potato(70, 70).animate();
|
||||
|
||||
|
||||
function loop(t) {
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
frame = requestAnimationFrame(loop);
|
||||
for (let i = 0; i < Potatos.length; i++) {
|
||||
Potatos[i].animate();
|
||||
}
|
||||
// Potatos[0].animate();
|
||||
}
|
||||
|
||||
loop();
|
||||
|
||||
// Lets try again with a single loop
|
||||
|
||||
|
||||
|
||||
|
||||
// THis shit makes the cool Gradient
|
||||
// let frame = requestAnimationFrame(loop);
|
||||
// function loop(t) {
|
||||
// frame = requestAnimationFrame(loop);
|
||||
|
||||
// // const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
||||
|
||||
// for (let p = 0; p < imageData.data.length; p += 4) {
|
||||
// const i = p / 4;
|
||||
// const x = i % canvas.width;
|
||||
// const y = i / canvas.width >>> 0;
|
||||
|
||||
// const r = 64 + (128 * x / canvas.width) + (64 * Math.sin(t / 1000));
|
||||
// const g = 64 + (128 * y / canvas.height) + (64 * Math.cos(t / 1000));
|
||||
// const b = 128;
|
||||
|
||||
// imageData.data[p + 0] = r;
|
||||
// imageData.data[p + 1] = g;
|
||||
// imageData.data[p + 2] = b;
|
||||
// imageData.data[p + 3] = 255;
|
||||
// }
|
||||
|
||||
// ctx.putImageData(imageData, 0, 0);
|
||||
// }
|
||||
|
||||
return () => {
|
||||
cancelAnimationFrame(frame);
|
||||
// prolly something else...
|
||||
};
|
||||
});
|
||||
</script>
|
||||
|
||||
<canvas
|
||||
bind:this={canvas}
|
||||
width={window.innerWidth}
|
||||
height={window.innerHeight}
|
||||
></canvas>
|
||||
<!-- widht and height were 32, trying stuff -->
|
||||
|
||||
<!-- I don't have the /svelte-logo-mask.svg, i guess i'll go get something -->
|
||||
<style>
|
||||
canvas {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: #666;
|
||||
|
||||
|
||||
/* testing something */
|
||||
/* -webkit-mask-image: radial-gradient(circle, black 50%, rgba(0, 0, 0, 0.5) 50%);
|
||||
mask-image: radial-gradient(circle, black 50%, rgba(0, 0, 0, 0.5) 50%); */
|
||||
/* Holy shit that worked! i got a mask */
|
||||
/* it also works without a mask! i have a canvas! */
|
||||
|
||||
/* -webkit-mask: url(/svelte-logo-mask.svg) 50% 50% no-repeat; */
|
||||
/* -webkit-mask: url(img/cartoon_potato3.jpg) 50% 50% no-repeat; */
|
||||
/* mask: url(/svelte-logo-mask.svg) 50% 50% no-repeat; */
|
||||
/* mask: url(img/cartoon_potato3.jpg) 50% 50% no-repeat; */
|
||||
}
|
||||
</style>
|
||||
@@ -1,45 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { onMount, setContext } from "svelte";
|
||||
|
||||
let canvas;
|
||||
|
||||
// what do i want?
|
||||
// lets start with an image of my potato
|
||||
// then get it to move
|
||||
// then have many displayed in an offset grid
|
||||
|
||||
const drawFunctions = [];
|
||||
|
||||
|
||||
setContext('canvas', {
|
||||
register(drawFn) {
|
||||
drawFunctions.push(drawFn);
|
||||
},
|
||||
unregister(drawFn) {
|
||||
drawFunctions.splice(drawFunctions.indexOf(drawFn), 1);
|
||||
}
|
||||
})
|
||||
|
||||
onMount(() => {
|
||||
// not sure what this does...
|
||||
const ctx = canvas.getContext('2d');
|
||||
|
||||
// no idea what this does...
|
||||
function update() {
|
||||
|
||||
ctx.clearRect()
|
||||
drawFunctions.forEach(drawFn => {
|
||||
drawFn(ctx);
|
||||
});
|
||||
|
||||
frameId = requestAnimationFrame(update);
|
||||
}
|
||||
|
||||
let frameId = requestAnimationFrame(update);
|
||||
return () => {
|
||||
cancelAnimationFrame(update);
|
||||
};
|
||||
});
|
||||
</script>
|
||||
|
||||
<canvas bind:this={canvas} />
|
||||
@@ -1,112 +0,0 @@
|
||||
<script lang="ts">
|
||||
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
import { location } from 'svelte-spa-router';
|
||||
import GenerateUserDisplay from './GenerateUserDisplay.svelte';
|
||||
|
||||
// using location won't work cuz i do a fetch but i don't change the page fragment, so means nothing to location...
|
||||
|
||||
// this is how you access /:first for example
|
||||
// export let params = {}
|
||||
// <p>Your name is: <b>{params.first}</b> <b>{#if params.last}{params.last}{/if}</b></p>
|
||||
|
||||
// If i export these vars, maybe as an nice tidy object, i could pass whatever i like to them
|
||||
// The current user, some other user, whatever, and thus reuse this Componente for the user and their friends or whatever
|
||||
// will have to coordinate with Back, will know more once the Game stats are in the back
|
||||
// wait maybe this won't work, cuz like it's still going through a route, i would have to update a Store Var each time...
|
||||
// not sure if that's what i want...
|
||||
|
||||
|
||||
// maybe the rank is determined dynamically just in the front based on win loss ratio or something no one cares about
|
||||
// why bother storing that shit in the back...
|
||||
// maybe i need a Rank.svelte component
|
||||
// ohhh i could make above a certain rank glitter! like that CSS tutorial showed me!
|
||||
|
||||
export let aUsername;
|
||||
|
||||
let user;
|
||||
let rank = '';
|
||||
let avatar;
|
||||
|
||||
// i think i don't need to do this once i sort out the {wrap} conditions: in theory i could pass values to the Route
|
||||
// once the async authentication check is done
|
||||
onMount( async() => {
|
||||
console.log('Display aUser username: '+ aUsername)
|
||||
// http://transcendance:8080/api/v2/user?username=NomDuUserATrouver
|
||||
user = await fetch(`http://transcendance:8080/api/v2/user?username=${aUsername}`)
|
||||
.then( (x) => x.json() );
|
||||
|
||||
console.log('Display a user: ' + user.username)
|
||||
|
||||
|
||||
// console.log('profile display did my fetch')
|
||||
// should i be updating the userStore or is that unnecessary?
|
||||
|
||||
if (user.loseGame > user.winGame) {
|
||||
rank = 'Bitch Ass Loser!'
|
||||
} else if (user.loseGame === user.winGame) {
|
||||
rank = 'Fine i guess...'
|
||||
} else {
|
||||
rank = 'Yea you da Boss!'
|
||||
}
|
||||
|
||||
await fetch("http://transcendance:8080/api/v2/user/avatar", {method: "GET"})
|
||||
.then(response => {return response.blob()})
|
||||
.then(data => {
|
||||
const url = URL.createObjectURL(data);
|
||||
avatar = url;
|
||||
});
|
||||
|
||||
// tmp
|
||||
// console.log('mounted Profile Display')
|
||||
// console.log(user);
|
||||
|
||||
|
||||
})
|
||||
|
||||
// Glittery Stars and such for Rank
|
||||
|
||||
let index = 0, interval = 1000;
|
||||
|
||||
const rand = (min, max) =>
|
||||
Math.floor(Math.random() * (max - min + 1)) + min;
|
||||
|
||||
// it's unhappy that "star" isn't typeset, no idea what to do about it...
|
||||
const animate = (star) => {
|
||||
// the if seems to have fixed the type issue
|
||||
if (star) {
|
||||
star.style.setProperty("--star-left", `${rand(-10, 100)}%`);
|
||||
star.style.setProperty("--star-top", `${rand(-40, 80)}%`);
|
||||
|
||||
star.style.animation = "none";
|
||||
star.offsetHeight;
|
||||
star.style.animation = "";
|
||||
}
|
||||
}
|
||||
|
||||
// This is the part i invented, it was kinda a fucking nightmare...
|
||||
let stars = [];
|
||||
|
||||
for (let i = 0; i < 3; i++) {
|
||||
setTimeout(() => {
|
||||
animate(stars[i]);
|
||||
|
||||
setInterval(() => animate(stars[i]), 1000);
|
||||
}, index++ * (interval / 3))
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
{#if user !== undefined}
|
||||
<GenerateUserDisplay {user}/>
|
||||
{:else}
|
||||
<h2>Sorry</h2>
|
||||
<div>Failed to load user {aUsername}</div>
|
||||
{/if}
|
||||
|
||||
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
@@ -1,24 +0,0 @@
|
||||
<script lang="ts">
|
||||
// might rename...
|
||||
// no idea what i'm doing...
|
||||
|
||||
import { getContext, onMount } from 'svelte';
|
||||
|
||||
// here you have to export the vars you need to draw this shit...
|
||||
|
||||
const { register, unregister } = getContext('canvas');
|
||||
|
||||
onMount(() => {
|
||||
register(draw);
|
||||
|
||||
return () => {
|
||||
unregister(draw);
|
||||
}
|
||||
});
|
||||
|
||||
function draw(ctx) {
|
||||
ctx.beginPath();
|
||||
ctx.fillStyle = fill;
|
||||
}
|
||||
|
||||
</script>
|
||||
@@ -1,124 +0,0 @@
|
||||
<script lang="ts">
|
||||
|
||||
// Fucking having a header that can change size, i don't really want the larger one
|
||||
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
|
||||
let dispatch = createEventDispatcher();
|
||||
|
||||
let types: string[] = ['home', 'regular', 'none'];
|
||||
// let currentType: string = 'regular';
|
||||
export let currentType = 'regular';
|
||||
// apparently Regular is the only one i use...
|
||||
|
||||
let handleClickHome = () => {
|
||||
dispatch('clickedHome');
|
||||
};
|
||||
|
||||
let handleClickLogout = () => {
|
||||
dispatch('clickedLogout');
|
||||
};
|
||||
|
||||
</script>
|
||||
|
||||
<!-- Make it so you can have a Big Home page header and a regular header or no header -->
|
||||
<!-- So far my CSS is super Gross, i guess i'll get Hugo to help me with it -->
|
||||
|
||||
<header class={currentType}>
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<img class={currentType} src="/img/potato_logo.png" alt="Potato Pong Logo" on:click={handleClickHome}>
|
||||
<!-- {#if currentType === 'home'} -->
|
||||
<h1 class={currentType}>Potato Pong</h1>
|
||||
<!-- {/if} -->
|
||||
<nav class={currentType}>
|
||||
<!-- <a href=""></a> -->
|
||||
<!-- i might change these to links rather than buttons, i kinda hate the buttons -->
|
||||
<button>My Stats</button>
|
||||
<button>Stream</button>
|
||||
<button on:click={handleClickLogout}>Log Out</button>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<style>
|
||||
/* See "possible_fonts.css" for more font options... */
|
||||
@font-face {
|
||||
font-family: 'Bondi';
|
||||
src:url('/fonts/Bondi.ttf.woff') format('woff'),
|
||||
url('/fonts/Bondi.ttf.svg#Bondi') format('svg'),
|
||||
url('/fonts/Bondi.ttf.eot'),
|
||||
url('/fonts/Bondi.ttf.eot?#iefix') format('embedded-opentype');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
|
||||
/* There is a bunch of unncessary shit in here... why so many flex grids, why is everything the same class? just seemed easier but... */
|
||||
|
||||
|
||||
header{
|
||||
/* background: #f7f7f7; */
|
||||
background: #618174;
|
||||
/* padding: 20px; */
|
||||
margin: 0;
|
||||
/* does nothing so far... */
|
||||
/* display: flex; */
|
||||
}
|
||||
|
||||
header.home{
|
||||
/* position: sticky; */
|
||||
}
|
||||
header.regular{
|
||||
/* for some reason this doesn't do shit! */
|
||||
position: sticky;
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr 1fr;
|
||||
}
|
||||
|
||||
/* Headers */
|
||||
h1{
|
||||
font-family: 'Bondi';
|
||||
}
|
||||
h1.home {
|
||||
margin: 0;
|
||||
text-align: center;
|
||||
/* max-width: 100px; */
|
||||
}
|
||||
h1.regular{
|
||||
margin: 0;
|
||||
text-align: left;
|
||||
/* max-width: 40px; */
|
||||
/* this helped with the weird extra space under the image... */
|
||||
display: flex;
|
||||
justify-self: center;
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
/* Images */
|
||||
img.home{
|
||||
/* text-align: center; */
|
||||
/* get the image squarely in the middle... */
|
||||
cursor: pointer;
|
||||
max-width: 100px;
|
||||
}
|
||||
img.regular{
|
||||
cursor: pointer;
|
||||
max-width: 40px;
|
||||
padding: 7px 20px;
|
||||
justify-self: left;
|
||||
}
|
||||
|
||||
nav{
|
||||
display: flex;
|
||||
justify-content: right;
|
||||
}
|
||||
|
||||
nav button{
|
||||
margin: 7px 20px;
|
||||
/* padding: 5px; */
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
/* .none{
|
||||
|
||||
} */
|
||||
</style>
|
||||
@@ -1,102 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
|
||||
let dispatch = createEventDispatcher();
|
||||
|
||||
let types: string[] = ['home', 'regular', 'none'];
|
||||
// let currentType: string = 'regular';
|
||||
export let currentType = 'home';
|
||||
|
||||
let handleClick = () => {
|
||||
dispatch('clickedHome');
|
||||
};
|
||||
|
||||
</script>
|
||||
|
||||
<!-- Make it so you can have a Big Home page header and a regular header or no header -->
|
||||
<!-- So far my CSS is super Gross, i guess i'll get Hugo to help me with it -->
|
||||
|
||||
<header class={currentType}>
|
||||
<h1 class={currentType}>
|
||||
<img class={currentType} src="/img/potato_logo.png" alt="Potato Pong Logo" on:click={handleClick}>
|
||||
</h1>
|
||||
<!-- {#if currentType === 'home'} -->
|
||||
<h1 class={currentType}>Potato Pong</h1>
|
||||
<!-- {/if} -->
|
||||
</header>
|
||||
|
||||
<style>
|
||||
/* See "possible_fonts.css" for more font options... */
|
||||
@font-face {
|
||||
font-family: 'Bondi';
|
||||
src:url('/fonts/Bondi.ttf.woff') format('woff'),
|
||||
url('/fonts/Bondi.ttf.svg#Bondi') format('svg'),
|
||||
url('/fonts/Bondi.ttf.eot'),
|
||||
url('/fonts/Bondi.ttf.eot?#iefix') format('embedded-opentype');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
|
||||
/* There is a bunch of unncessary shit in here... why so many flex grids, why is everything the same class? just seemed easier but... */
|
||||
|
||||
|
||||
header{
|
||||
/* background: #f7f7f7; */
|
||||
background: #618174;
|
||||
/* padding: 20px; */
|
||||
margin: 0;
|
||||
font-family: 'Bondi';
|
||||
/* does nothing so far... */
|
||||
/* display: flex; */
|
||||
}
|
||||
|
||||
header.home{
|
||||
/* position: sticky; */
|
||||
}
|
||||
header.regular{
|
||||
position: sticky;
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr 1fr;
|
||||
}
|
||||
header.regular > h1:first-child{
|
||||
justify-self: left;
|
||||
}
|
||||
header.regular > h1:nth-child(2){
|
||||
justify-items: center;
|
||||
}
|
||||
|
||||
/* Headers */
|
||||
h1.home {
|
||||
margin: 0;
|
||||
text-align: center;
|
||||
/* max-width: 100px; */
|
||||
}
|
||||
h1.regular{
|
||||
margin: 0;
|
||||
text-align: left;
|
||||
/* max-width: 40px; */
|
||||
/* this helped with the weird extra space under the image... */
|
||||
display: flex;
|
||||
justify-self: center;
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
/* Images */
|
||||
h1 img.home{
|
||||
/* text-align: center; */
|
||||
/* get the image squarely in the middle... */
|
||||
cursor: pointer;
|
||||
max-width: 100px;
|
||||
}
|
||||
h1 img.regular{
|
||||
cursor: pointer;
|
||||
max-width: 40px;
|
||||
padding: 7px 15px;
|
||||
}
|
||||
|
||||
|
||||
/* .none{
|
||||
|
||||
} */
|
||||
</style>
|
||||
@@ -1,172 +0,0 @@
|
||||
<script lang="ts">
|
||||
|
||||
// Now called LoginPage
|
||||
|
||||
// import Header from "./Header.svelte";
|
||||
import Footer from "../components/Footer.svelte";
|
||||
import Login from "./Login.svelte";
|
||||
import Tabs from "../shared/Tabs.svelte"
|
||||
import Card from "../pieces/Card.svelte"
|
||||
// tmp
|
||||
let login = { username: '', password: ''};
|
||||
// let's us track any errors in a submited form
|
||||
let errors = { username: '', password: ''};
|
||||
let valid:boolean = false;
|
||||
const loginHandler = () => {
|
||||
console.log('hi');
|
||||
};
|
||||
const createAccountHandler = () => {
|
||||
console.log('hi');
|
||||
};
|
||||
|
||||
// Tabs
|
||||
let items: string[] = ['Login', 'Create Account'];
|
||||
let activeItem: string = 'Login';
|
||||
|
||||
const tabChange = (e) => {
|
||||
activeItem = e.detail;
|
||||
};
|
||||
|
||||
// TMP for switching page, down the line this will be down by modifying url
|
||||
|
||||
</script>
|
||||
|
||||
<!-- New New Approach -->
|
||||
<!-- if i were to do all this with CSS Grids how would i do it? -->
|
||||
<!-- Things i want -->
|
||||
<!-- A title button, some nav buttons, a giant dope canvas and words over it -->
|
||||
<!-- not sure if i want: login and create account -->
|
||||
<!-- let's start with just the canvas -->
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- New aproach -->
|
||||
<!-- just make the html in order you can move it around all you want into special Compoenents later -->
|
||||
|
||||
<!-- Ok i think all of this needs to go in a Home Page Component -->
|
||||
<!-- and then i make another master component for the main page once you're logged in, no idea what that should look like -->
|
||||
|
||||
<!-- what if i kept the special canvas header in here and made another generic header for the rest of the site as a component -->
|
||||
|
||||
<header class="banner">
|
||||
<!-- top left corner, sticky -->
|
||||
<h1>Potato Pong</h1>
|
||||
<!-- top right but it takes you down the page -->
|
||||
<h2>Login</h2>
|
||||
|
||||
<!-- all this used to be in Welcome Section -->
|
||||
<!-- the amazing backround! not sure yet if it should scroll with us or be the size of the View Port... -->
|
||||
<!-- <canvas></canvas> -->
|
||||
<!-- i think maybe the canvas needs to be in the header -->
|
||||
<!-- using an image for now as a placehodler for the canvase -->
|
||||
<img src="/img/tmp_mario_banner.png" alt="tmp Mario banner">
|
||||
<div class="welcome">
|
||||
<h2>Welcome to <br><span>Potato Pong</span></h2>
|
||||
</div>
|
||||
<!-- I want some sort of arrow pointing down and blinking to indicate you should scroll -->
|
||||
</header>
|
||||
<!-- <section class="banner"> -->
|
||||
<section class="welcome">
|
||||
|
||||
</section>
|
||||
<!-- no nav on home page -->
|
||||
<section class="register">
|
||||
|
||||
<!-- i could have a toggle tab to login or create a new account -->
|
||||
|
||||
<Tabs items={items} {activeItem} on:tabChange={tabChange}/>
|
||||
{#if activeItem === 'Login'}
|
||||
<div class="card">
|
||||
<Card>
|
||||
<h2>Login</h2>
|
||||
<form on:submit|preventDefault={loginHandler}>
|
||||
<div class="form-field">
|
||||
<input type="text" id="username" placeholder="username" bind:value={login.username}>
|
||||
<div class="error">{ errors.username }</div>
|
||||
</div>
|
||||
<div class="form-field">
|
||||
<input type="password" id="password" placeholder="password" bind:value={login.password}>
|
||||
<div class="error">{ errors.password }</div>
|
||||
</div>
|
||||
<!-- type="" but flat={} cuz type is a string but flat is a bool-->
|
||||
<!-- <Button type="secondary" flat={true}>Add Poll</Button> -->
|
||||
<button>Login</button>
|
||||
</form>
|
||||
</Card>
|
||||
</div>
|
||||
{:else if activeItem = 'Create Account'}
|
||||
<!-- Create Account -->
|
||||
<div class="card">
|
||||
<Card>
|
||||
<h3>Create Account</h3>
|
||||
<form on:submit|preventDefault={createAccountHandler}>
|
||||
<div class="form-field">
|
||||
<input type="text" id="username" placeholder="username" bind:value={login.username}>
|
||||
<div class="error">{ errors.username }</div>
|
||||
</div>
|
||||
<div class="form-field">
|
||||
<input type="password" id="password" placeholder="password" bind:value={login.password}>
|
||||
<div class="error">{ errors.password }</div>
|
||||
</div>
|
||||
<!-- type="" but flat={} cuz type is a string but flat is a bool-->
|
||||
<!-- <Button type="secondary" flat={true}>Add Poll</Button> -->
|
||||
<button>Login</button>
|
||||
</form>
|
||||
</Card>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
|
||||
</section>
|
||||
<!-- below this i could say, this is where i might have put an explanation of what you can do on this page but fuck you i didn't -->
|
||||
<!-- or maybe in the end i will, something like: Fun, Game, Colors, enjoy Friendship, or don't, it's your choice! -->
|
||||
<Footer />
|
||||
|
||||
|
||||
|
||||
<style>
|
||||
/* hearder stuff */
|
||||
|
||||
/* Clearly i have yet to master floating stuff... */
|
||||
/* i need to put box-sizing in here somewhere for the login... */
|
||||
|
||||
/* starting again with CSS Grid */
|
||||
/* .banner{
|
||||
position: relative;
|
||||
}
|
||||
.banner img{
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.banner h1{
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 10px;
|
||||
}
|
||||
.banner h2{
|
||||
position: absolute;
|
||||
left: 90%;
|
||||
top: 10px;
|
||||
}
|
||||
.banner .welcome{
|
||||
background-color: #feb614;
|
||||
color:white;
|
||||
padding: 30px;
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
}
|
||||
.banner .welcome h2{
|
||||
font-size: 58px;
|
||||
}
|
||||
.banner .welcome h2 span{
|
||||
font-size: 1.3em;
|
||||
}
|
||||
|
||||
.register{
|
||||
|
||||
} */
|
||||
|
||||
|
||||
</style>
|
||||
@@ -1,90 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { now } from "svelte/internal";
|
||||
import Card from "../pieces/Card.svelte";
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
// import UserStore from './stores/UserStore';
|
||||
|
||||
// prolly Typescript-ify
|
||||
//let fields{question:string, answerA:string, answerB: string} = { question: '', answerA: '', answerB: ''};
|
||||
let login = { username: '', password: ''};
|
||||
// let's us track any errors in a submited form
|
||||
let errors = { username: '', password: ''};
|
||||
let valid:boolean = false;
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
const loginHandler = () => {
|
||||
valid = true;
|
||||
|
||||
if (login.username !== $UserStore.username) {
|
||||
valid = false;
|
||||
errors.username = "wrong example username."
|
||||
} else {
|
||||
// reseting the value of errors
|
||||
errors.username = "";
|
||||
}
|
||||
if (login.password !== $UserStore.password) {
|
||||
valid = false;
|
||||
errors.password = "wrong example password."
|
||||
} else {
|
||||
// reseting the value of errors
|
||||
errors.password = "";
|
||||
}
|
||||
|
||||
if (valid) {
|
||||
$UserStore.loggedIn = true;
|
||||
$UserStore.status = 'online';
|
||||
|
||||
dispatch('login');
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<div class="login">
|
||||
<Card>
|
||||
|
||||
<form on:submit|preventDefault={loginHandler}>
|
||||
<div class="form-field">
|
||||
<input type="text" id="username" placeholder="username" bind:value={login.username}>
|
||||
<div class="error">{ errors.username }</div>
|
||||
</div>
|
||||
<div class="form-field">
|
||||
<input type="password" id="password" placeholder="password" bind:value={login.password}>
|
||||
<div class="error">{ errors.password }</div>
|
||||
</div>
|
||||
<!-- type="" but flat={} cuz type is a string but flat is a bool-->
|
||||
<!-- <Button type="secondary" flat={true}>Add Poll</Button> -->
|
||||
<button>Login</button>
|
||||
</form>
|
||||
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
form{
|
||||
width: 200px;
|
||||
margin: 0 auto;
|
||||
text-align: center;
|
||||
}
|
||||
.form-field{
|
||||
margin: 18px auto;
|
||||
}
|
||||
input{
|
||||
width: 100%;
|
||||
border-radius: 6px;
|
||||
}
|
||||
.login{
|
||||
/* display: grid;
|
||||
grid-template-columns: 1fr; */
|
||||
max-width: 350px;
|
||||
}
|
||||
|
||||
.error{
|
||||
font-weight: bold;
|
||||
font-size: 12px;
|
||||
color: #d91b42;
|
||||
}
|
||||
</style>
|
||||
@@ -1,414 +0,0 @@
|
||||
<script lang="ts">
|
||||
// import Header from "./Header.svelte";
|
||||
import Footer from "../components/Footer.svelte";
|
||||
import Tabs from "../shared/Tabs.svelte";
|
||||
import Card from "../pieces/Card.svelte";
|
||||
import Canvas from "../pieces/Canvas.svelte";
|
||||
import ScrollTo from "../shared/ScrollTo.svelte";
|
||||
import UserStore from "./UserStore.js";
|
||||
import { createEventDispatcher } from "svelte";
|
||||
import {push} from "svelte-spa-router";
|
||||
|
||||
let dispatch = createEventDispatcher();
|
||||
// Tabs
|
||||
let items: string[] = ['Login', 'Create Account'];
|
||||
let activeItem: string = 'Login';
|
||||
|
||||
const tabChange = (e) => {
|
||||
activeItem = e.detail;
|
||||
};
|
||||
|
||||
|
||||
import axios from 'axios';
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
let user = {logedIn: false};
|
||||
|
||||
onMount(async () => {
|
||||
// console.log('PROFIL SVELTE');
|
||||
const {data} = await axios.get('http://transcendance:8080/api/v2/user');
|
||||
if (data)
|
||||
user.logedIn = true;
|
||||
});
|
||||
|
||||
const submit = async() => {
|
||||
document.body.scrollIntoView();
|
||||
push
|
||||
window.location.href = 'http://transcendance:8080/api/v2/auth';
|
||||
}
|
||||
|
||||
const logout = async() => {
|
||||
await fetch('http://transcendance:8080/api/v2/auth/logout',);
|
||||
user.logedIn = false;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// for toLogin
|
||||
|
||||
let bottomHalf;
|
||||
// console.log(bottomHalf);
|
||||
// const element = document.body;
|
||||
|
||||
|
||||
// in theory this could be a Store, but for now this will do
|
||||
// also in future we'll do this with urls
|
||||
export let pages;
|
||||
export let currentPage;
|
||||
// this shit has overstayed it's welcome, fuck having userId all over the place
|
||||
export let userId;
|
||||
|
||||
|
||||
// maybe we put this in the login Component?
|
||||
// tmp
|
||||
let login = { username: '', password: ''};
|
||||
// let's us track any errors in a submited form
|
||||
let errors = { username: '', password: ''};
|
||||
let valid:boolean = false;
|
||||
|
||||
const loginHandler = () => {
|
||||
console.log('hi from loginHandler');
|
||||
|
||||
|
||||
//
|
||||
// Basic Checks
|
||||
//
|
||||
valid = false;
|
||||
|
||||
// checkin Username
|
||||
if (login.username.length < 1)
|
||||
{
|
||||
valid = false;
|
||||
errors.username = 'please enter a username';
|
||||
} else {
|
||||
valid = true;
|
||||
errors.username = '';
|
||||
}
|
||||
|
||||
// Checking Password
|
||||
if (login.password.length < 1)
|
||||
{
|
||||
valid = false;
|
||||
errors.password = 'please enter your password';
|
||||
} else {
|
||||
valid = true;
|
||||
errors.password = '';
|
||||
}
|
||||
|
||||
//
|
||||
// Advanded Checks
|
||||
//
|
||||
|
||||
// Comparing to UserStore
|
||||
let users;
|
||||
const unsubscribe = UserStore.subscribe(objs => {
|
||||
users = objs;
|
||||
console.log('subscribed');
|
||||
});
|
||||
|
||||
// could i do $users.length ? doesn't look like it...
|
||||
// let len = $users.length;
|
||||
|
||||
// userId = 0;
|
||||
// userId = users.filter(user => user.username === login.username);
|
||||
// this shit returns an array, it would be nice if it weren't an array
|
||||
// let user = users.filter(user => user.username === login.username);
|
||||
let user = users.find(user => user.username === login.username);
|
||||
|
||||
console.log(user);
|
||||
// console.log(user.password);
|
||||
|
||||
|
||||
// all this shit is a bit wordy... maybe a better way to handle this stuff?
|
||||
|
||||
// if (userIndex > users.length) {
|
||||
if (!user) {
|
||||
valid = false;
|
||||
errors.username = 'user not found';
|
||||
// something better?
|
||||
} else {
|
||||
valid = true;
|
||||
errors.username = '';
|
||||
}
|
||||
|
||||
// if (users[userIndex].password !== login.password) {
|
||||
if (user && user.password !== login.password) {
|
||||
valid = false;
|
||||
errors.password = 'Wrong Password';
|
||||
// Maybe clear the fields?
|
||||
} else {
|
||||
valid = true;
|
||||
errors.password = '';
|
||||
}
|
||||
|
||||
//
|
||||
// Validation
|
||||
//
|
||||
unsubscribe();
|
||||
|
||||
if (valid) {
|
||||
// yea don't modify this here...
|
||||
currentPage = 'user';
|
||||
// something like: indicate that userIndex is the one we want...
|
||||
console.log('valid Credentials');
|
||||
|
||||
// making sure we start at the top of the page, way less jarring
|
||||
// leave for now just in case...
|
||||
document.body.scrollIntoView();
|
||||
|
||||
// may not actually want a dispatch?
|
||||
// pass data userIndex?
|
||||
// dispatch('loggedIn');
|
||||
// from svelte-spa-router
|
||||
push("/user");
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
const createAccountHandler = () => {
|
||||
console.log('hi from accunt handler');
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<header class="grid-container">
|
||||
|
||||
<!-- <div on:mouseenter={enter} on:mouseleave={leave} class:active > -->
|
||||
<h1>Potato Pong</h1>
|
||||
<!-- </div> -->
|
||||
<nav>
|
||||
<!-- placeholder links -->
|
||||
<a href="/">Somewhere</a>
|
||||
<!-- <a href="/">SomewhereElse</a> -->
|
||||
{#if !user.logedIn}
|
||||
<ScrollTo element={bottomHalf}/>
|
||||
{:else}
|
||||
<div class="logout" on:click={logout}>Log Out</div>
|
||||
{/if}
|
||||
<!-- one of these will be login and it will scroll you down to the login part -->
|
||||
</nav>
|
||||
<h2>
|
||||
<div>Welcome to</div>
|
||||
<div>Potato Pong</div>
|
||||
</h2>
|
||||
|
||||
<!-- here i want a flashing arrow pointing down to the login part -->
|
||||
|
||||
</header>
|
||||
<!-- <Canvas class=".canvas2"/> -->
|
||||
<Canvas/>
|
||||
|
||||
|
||||
|
||||
<section class="register" bind:this={bottomHalf}>
|
||||
|
||||
<!-- My beautiful tabs are useless now, whatever, kill your darlings... -->
|
||||
<!-- i could have a toggle tab to login or create a new account -->
|
||||
<!-- This shit is kinda unnecessary cuz there is no Create Account option... -->
|
||||
|
||||
<Tabs items={items} {activeItem} on:tabChange={tabChange}/>
|
||||
{#if activeItem === 'Login'}
|
||||
<div class="card">
|
||||
<Card>
|
||||
<h2>Login</h2>
|
||||
<form on:submit|preventDefault={loginHandler}>
|
||||
<div class="form-field">
|
||||
<input type="text" id="username" placeholder="username" bind:value={login.username}>
|
||||
<div class="error">{ errors.username }</div>
|
||||
</div>
|
||||
<div class="form-field">
|
||||
<input type="password" id="password" placeholder="password" bind:value={login.password}>
|
||||
<div class="error">{ errors.password }</div>
|
||||
</div>
|
||||
<!-- type="" but flat={} cuz type is a string but flat is a bool-->
|
||||
<!-- <Button type="secondary" flat={true}>Add Poll</Button> -->
|
||||
<button>Login</button>
|
||||
</form>
|
||||
</Card>
|
||||
</div>
|
||||
{:else if activeItem = 'Create Account'}
|
||||
<!-- Create Account -->
|
||||
<div class="card">
|
||||
<Card>
|
||||
<h2>Create Account</h2>
|
||||
<form on:submit|preventDefault={createAccountHandler}>
|
||||
<div class="form-field">
|
||||
<input type="text" id="username" placeholder="username" bind:value={login.username}>
|
||||
<div class="error">{ errors.username }</div>
|
||||
</div>
|
||||
<div class="form-field">
|
||||
<input type="password" id="password" placeholder="password" bind:value={login.password}>
|
||||
<div class="error">{ errors.password }</div>
|
||||
</div>
|
||||
<!-- type="" but flat={} cuz type is a string but flat is a bool-->
|
||||
<!-- <Button type="secondary" flat={true}>Add Poll</Button> -->
|
||||
<button>Login</button>
|
||||
</form>
|
||||
</Card>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
|
||||
</section>
|
||||
<!-- below this i could say, this is where i might have put an explanation of what you can do on this page but fuck you i didn't -->
|
||||
<!-- or maybe in the end i will, something like: Fun, Game, Colors, enjoy Friendship, or don't, it's your choice! -->
|
||||
|
||||
<Footer />
|
||||
|
||||
<!-- </div> -->
|
||||
|
||||
|
||||
<style>
|
||||
/* currently useless */
|
||||
/* No styles get applied to the canvas from here, maybe i should move them to the Canvas Component... */
|
||||
/* tho tbh, why bother, i'm gonna change it anyway... */
|
||||
.canvas{
|
||||
/* grid-column: 1 / 13;
|
||||
grid-row: 1 / 3; */
|
||||
/* don't rely on Z-Index!!!! */
|
||||
z-index: -1;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
white-space: nowrap;
|
||||
|
||||
/* Tmp? */
|
||||
/* background-color: #666; */
|
||||
|
||||
/* somehow this got rid of they annoying white space under the canvas */
|
||||
padding-bottom: 0;
|
||||
margin-bottom: 0px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.canvas2{
|
||||
z-index: -1;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
white-space: nowrap;
|
||||
|
||||
/* Tmp? */
|
||||
/* background-color: #666; */
|
||||
|
||||
/* somehow this got rid of they annoying white space under the canvas */
|
||||
padding-bottom: 0;
|
||||
margin-bottom: 0px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
|
||||
/* .canvas .grid-container{ */
|
||||
.grid-container{
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
|
||||
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
white-space: nowrap;
|
||||
/* padding-bottom: 0; */
|
||||
margin-bottom: 0px;
|
||||
overflow: hidden;
|
||||
padding: 20px 40px;
|
||||
|
||||
|
||||
display: grid;
|
||||
grid-template-columns: repeat(12, 1fr);
|
||||
grid-template-rows: 1fr 1fr 1fr 1fr 1fr;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
header h1, header nav a{
|
||||
/* tmp ? well i kinda like it */
|
||||
color: bisque;
|
||||
}
|
||||
header h1{
|
||||
grid-column: 1 / 7;
|
||||
grid-row: 1;
|
||||
/* grid-column: span 6; */
|
||||
/* tmp? */
|
||||
padding: 20px;
|
||||
border: 1px solid bisque;
|
||||
}
|
||||
header nav{
|
||||
/* make it a flexbox? */
|
||||
grid-column: 7 / 13;
|
||||
grid-row: 1;
|
||||
justify-self: end;
|
||||
/* tmp? */
|
||||
padding: 20px;
|
||||
border: 1px solid bisque;
|
||||
}
|
||||
header nav a{
|
||||
margin-left: 10px;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* testing */
|
||||
header nav a:hover{
|
||||
font-weight: bold;
|
||||
background-color: blue;
|
||||
}
|
||||
header h2:hover{
|
||||
background: blue;
|
||||
}
|
||||
|
||||
|
||||
header h2{
|
||||
grid-row: 3;
|
||||
grid-column: 5 / span 4;
|
||||
justify-self: center;
|
||||
/* tmp */
|
||||
border: 1px solid black;
|
||||
z-index: 3;
|
||||
}
|
||||
header h2 div{
|
||||
font-size: 2em;
|
||||
}
|
||||
|
||||
/* the login / register part */
|
||||
|
||||
/* What do i want?
|
||||
I want it to be the same size as a full screen so you don't see the canvas at all anymore */
|
||||
|
||||
/* doesn't work... */
|
||||
/* body{
|
||||
background: bisque;
|
||||
} */
|
||||
|
||||
.bottom-half{
|
||||
/* doesn't quite work... */
|
||||
background: bisque;
|
||||
/* also doesn't work... */
|
||||
/* height: 1vw; */
|
||||
|
||||
/* testing */
|
||||
/* position: absolute; */
|
||||
}
|
||||
|
||||
section.register{
|
||||
min-height: 400px;
|
||||
}
|
||||
|
||||
.error{
|
||||
font-size: 0.8em;
|
||||
font-weight: bold;
|
||||
color: red;
|
||||
}
|
||||
|
||||
|
||||
</style>
|
||||
@@ -1,305 +0,0 @@
|
||||
<script lang="ts">
|
||||
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
import { location } from 'svelte-spa-router';
|
||||
// this is how you access /:first for example
|
||||
// export let params = {}
|
||||
// <p>Your name is: <b>{params.first}</b> <b>{#if params.last}{params.last}{/if}</b></p>
|
||||
|
||||
// If i export these vars, maybe as an nice tidy object, i could pass whatever i like to them
|
||||
// The current user, some other user, whatever, and thus reuse this Componente for the user and their friends or whatever
|
||||
// will have to coordinate with Back, will know more once the Game stats are in the back
|
||||
// wait maybe this won't work, cuz like it's still going through a route, i would have to update a Store Var each time...
|
||||
// not sure if that's what i want...
|
||||
|
||||
|
||||
// maybe the rank is determined dynamically just in the front based on win loss ratio or something no one cares about
|
||||
// why bother storing that shit in the back...
|
||||
// maybe i need a Rank.svelte component
|
||||
// ohhh i could make above a certain rank glitter! like that CSS tutorial showed me!
|
||||
|
||||
let user;
|
||||
let rank = '';
|
||||
let avatar;
|
||||
|
||||
// i think i don't need to do this once i sort out the {wrap} conditions: in theory i could pass values to the Route
|
||||
// once the async authentication check is done
|
||||
onMount( async() => {
|
||||
// console.log('mounting profile display')
|
||||
user = await fetch('http://transcendance:8080/api/v2/user')
|
||||
.then( (x) => x.json() );
|
||||
|
||||
// console.log('profile display did my fetch')
|
||||
// should i be updating the userStore or is that unnecessary?
|
||||
|
||||
if (user.loseGame > user.winGame) {
|
||||
rank = 'Bitch Ass Loser!'
|
||||
} else if (user.loseGame === user.winGame) {
|
||||
rank = 'Fine i guess...'
|
||||
} else {
|
||||
rank = 'Yea you da Boss!'
|
||||
}
|
||||
|
||||
await fetch("http://transcendance:8080/api/v2/user/avatar", {method: "GET"})
|
||||
.then(response => {return response.blob()})
|
||||
.then(data => {
|
||||
const url = URL.createObjectURL(data);
|
||||
avatar = url;
|
||||
});
|
||||
|
||||
// tmp
|
||||
// console.log('mounted Profile Display')
|
||||
// console.log(user);
|
||||
|
||||
|
||||
})
|
||||
|
||||
// Glittery Stars and such for Rank
|
||||
|
||||
let index = 0, interval = 1000;
|
||||
|
||||
const rand = (min, max) =>
|
||||
Math.floor(Math.random() * (max - min + 1)) + min;
|
||||
|
||||
// it's unhappy that "star" isn't typeset, no idea what to do about it...
|
||||
const animate = (star) => {
|
||||
// the if seems to have fixed the type issue
|
||||
if (star) {
|
||||
star.style.setProperty("--star-left", `${rand(-10, 100)}%`);
|
||||
star.style.setProperty("--star-top", `${rand(-40, 80)}%`);
|
||||
|
||||
star.style.animation = "none";
|
||||
star.offsetHeight;
|
||||
star.style.animation = "";
|
||||
}
|
||||
}
|
||||
|
||||
// This is the part i invented, it was kinda a fucking nightmare...
|
||||
let stars = [];
|
||||
|
||||
for (let i = 0; i < 3; i++) {
|
||||
setTimeout(() => {
|
||||
animate(stars[i]);
|
||||
|
||||
setInterval(() => animate(stars[i]), 1000);
|
||||
}, index++ * (interval / 3))
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<!-- is this if excessive? -->
|
||||
<div class="outer">
|
||||
{#if user !== undefined}
|
||||
<main>
|
||||
<!-- <img class="icon" src="img/default_user_icon.png" alt="default user icon"> -->
|
||||
<!-- <img class="icon" src="{user.image_url}" alt="default user icon"> -->
|
||||
<img class="avatar" src="{avatar}" alt="default user icon">
|
||||
<div class="username">{user.username}</div>
|
||||
<div class="rank">Rank:
|
||||
<span class="glitter">
|
||||
<span bind:this={stars[0]} class="glitter-star">
|
||||
<svg viewBox="0 0 512 512">
|
||||
<path d="M512 255.1c0 11.34-7.406 20.86-18.44 23.64l-171.3 42.78l-42.78 171.1C276.7 504.6 267.2 512 255.9 512s-20.84-7.406-23.62-18.44l-42.66-171.2L18.47 279.6C7.406 276.8 0 267.3 0 255.1c0-11.34 7.406-20.83 18.44-23.61l171.2-42.78l42.78-171.1C235.2 7.406 244.7 0 256 0s20.84 7.406 23.62 18.44l42.78 171.2l171.2 42.78C504.6 235.2 512 244.6 512 255.1z" />
|
||||
</svg>
|
||||
</span>
|
||||
<span bind:this={stars[1]} class="glitter-star">
|
||||
<svg viewBox="0 0 512 512">
|
||||
<path d="M512 255.1c0 11.34-7.406 20.86-18.44 23.64l-171.3 42.78l-42.78 171.1C276.7 504.6 267.2 512 255.9 512s-20.84-7.406-23.62-18.44l-42.66-171.2L18.47 279.6C7.406 276.8 0 267.3 0 255.1c0-11.34 7.406-20.83 18.44-23.61l171.2-42.78l42.78-171.1C235.2 7.406 244.7 0 256 0s20.84 7.406 23.62 18.44l42.78 171.2l171.2 42.78C504.6 235.2 512 244.6 512 255.1z" />
|
||||
</svg>
|
||||
</span>
|
||||
<span bind:this={stars[2]} class="glitter-star">
|
||||
<svg viewBox="0 0 512 512">
|
||||
<path d="M512 255.1c0 11.34-7.406 20.86-18.44 23.64l-171.3 42.78l-42.78 171.1C276.7 504.6 267.2 512 255.9 512s-20.84-7.406-23.62-18.44l-42.66-171.2L18.47 279.6C7.406 276.8 0 267.3 0 255.1c0-11.34 7.406-20.83 18.44-23.61l171.2-42.78l42.78-171.1C235.2 7.406 244.7 0 256 0s20.84 7.406 23.62 18.44l42.78 171.2l171.2 42.78C504.6 235.2 512 244.6 512 255.1z" />
|
||||
</svg>
|
||||
</span>
|
||||
<span class="glitter-text">{rank}</span>
|
||||
</span>
|
||||
</div>
|
||||
<section class="main-stats">
|
||||
<h4>Match Statistics</h4>
|
||||
<p>Total: {user.stats.totalGame}</p>
|
||||
<p>Victories: {user.stats.winGame}</p>
|
||||
<p>Losses: {user.stats.loseGame}</p>
|
||||
<p>Draws: {user.stats.drawGame}</p>
|
||||
</section>
|
||||
</main>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div>testing when there's tons of stuff</div>
|
||||
<div>testing when there's tons of stuff</div>
|
||||
<div>testing when there's tons of stuff</div>
|
||||
<div>testing when there's tons of stuff</div>
|
||||
<div>testing when there's tons of stuff</div>
|
||||
<div>testing when there's tons of stuff</div>
|
||||
<div>testing when there's tons of stuff</div>
|
||||
<div>testing when there's tons of stuff</div>
|
||||
<div>testing when there's tons of stuff</div>
|
||||
<div>testing when there's tons of stuff</div>
|
||||
<div>testing when there's tons of stuff</div>
|
||||
<div>testing when there's tons of stuff</div>
|
||||
<div>testing when there's tons of stuff</div>
|
||||
<div>testing when there's tons of stuff</div>
|
||||
<div>testing when there's tons of stuff</div>
|
||||
<div>testing when there's tons of stuff</div>
|
||||
<div>testing when there's tons of stuff</div>
|
||||
<div>testing when there's tons of stuff</div>
|
||||
<div>testing when there's tons of stuff</div>
|
||||
<div>testing when there's tons of stuff</div>
|
||||
<div>testing when there's tons of stuff</div>
|
||||
<div>testing when there's tons of stuff</div>
|
||||
<div>testing when there's tons of stuff</div>
|
||||
<div>testing when there's tons of stuff</div>
|
||||
<div>testing when there's tons of stuff</div>
|
||||
<div>testing when there's tons of stuff</div>
|
||||
<div>testing when there's tons of stuff</div>
|
||||
<div>testing when there's tons of stuff</div>
|
||||
<div>testing when there's tons of stuff</div>
|
||||
<div>testing when there's tons of stuff</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<style>
|
||||
|
||||
div.outer{
|
||||
max-width: 960px;
|
||||
margin: 40px auto;
|
||||
}
|
||||
|
||||
/* The main part */
|
||||
main{
|
||||
max-width: 960px;
|
||||
margin: 40px auto;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* Normal CSS stuff */
|
||||
.avatar{
|
||||
max-width: 150px;
|
||||
/* padding: 5px; */
|
||||
}
|
||||
|
||||
/* The variable rich section */
|
||||
section.main-stats{
|
||||
max-width: 600px;
|
||||
margin: 40px auto;
|
||||
text-align: center;
|
||||
/* i think i want to use a grid? */
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
/* not sure about this, maybe top should be larger? */
|
||||
grid-template-rows: repeat(3, 1fr);
|
||||
}
|
||||
|
||||
/* the stuff in the grid*/
|
||||
section.main-stats h4{
|
||||
grid-column: 1 / span 3;
|
||||
}
|
||||
|
||||
div.username{
|
||||
font-size: 1.5em;
|
||||
font-weight: bold;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
|
||||
div.rank {
|
||||
/* color: black; */
|
||||
font-size: 1.2em;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
|
||||
/* Glittery Star Stuff */
|
||||
|
||||
|
||||
:root {
|
||||
--purple: rgb(123, 31, 162);
|
||||
--violet: rgb(103, 58, 183);
|
||||
--pink: rgb(244, 143, 177);
|
||||
/* make shit gold? */
|
||||
}
|
||||
|
||||
@keyframes background-pan {
|
||||
from {
|
||||
background-position: 0% center;
|
||||
}
|
||||
|
||||
to {
|
||||
background-position: -200% center;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes scale {
|
||||
from, to {
|
||||
transform: scale(0);
|
||||
}
|
||||
|
||||
50% {
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes rotate {
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
|
||||
to {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
div > .glitter {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
div > .glitter > .glitter-star {
|
||||
--size: clamp(20px, 1.5vw, 30px);
|
||||
|
||||
animation: scale 700ms ease forwards;
|
||||
display: block;
|
||||
height: var(--size);
|
||||
left: var(--star-left);
|
||||
position: absolute;
|
||||
top: var(--star-top);
|
||||
width: var(--size);
|
||||
}
|
||||
|
||||
div > .glitter > .glitter-star > svg {
|
||||
animation: rotate 1000ms linear infinite;
|
||||
display: block;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
div > .glitter > .glitter-star > svg > path {
|
||||
fill: var(--violet);
|
||||
}
|
||||
|
||||
div > .glitter > .glitter-text {
|
||||
animation: background-pan 3s linear infinite;
|
||||
/* background-image: linear-gradient( */
|
||||
background: linear-gradient(
|
||||
to right,
|
||||
var(--purple),
|
||||
var(--violet),
|
||||
var(--pink),
|
||||
var(--purple)
|
||||
);
|
||||
background-size: 200%;
|
||||
|
||||
/* Keep these for Safari and chrome */
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
|
||||
/* These are for Firefox */
|
||||
background-clip: text;
|
||||
color: transparent;
|
||||
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -1,137 +0,0 @@
|
||||
<script lang="ts">
|
||||
|
||||
import { onMount } from 'svelte';
|
||||
import { loginStatus } from '../stores/loginStatusStore';
|
||||
|
||||
// Cherif's code
|
||||
|
||||
let qrCodeImg;
|
||||
let qrCode = "";
|
||||
let wrongCode = "";
|
||||
let maxTry = 3;
|
||||
// const fetchQrCodeImg = (async() => {
|
||||
// await fetch("http://transcendance:8080/api/v2/auth/2fa/generate",
|
||||
// {
|
||||
// method: 'POST',
|
||||
// })
|
||||
// .then(response => {return response.blob()})
|
||||
// .then(blob => {
|
||||
// const url = URL.createObjectURL(blob);
|
||||
// qrCodeImg = url;
|
||||
// });
|
||||
// })()
|
||||
|
||||
// $: submit = async() => {
|
||||
// const response = await fetch("http://transcendance:8080/api/v2/auth/2fa/turn-on",
|
||||
// {
|
||||
// method : 'POST',
|
||||
// headers : {
|
||||
// "Content-Type": "application/json",
|
||||
// },
|
||||
// body : JSON.stringify({
|
||||
// "twoFaCode" : qrCode,
|
||||
// }),
|
||||
// });
|
||||
// if (response.status === 401)
|
||||
// {
|
||||
// qrCode = "";
|
||||
// wrongCode = `Wrong code, please try again. You have ${maxTry} before end session`;
|
||||
// maxTry--;
|
||||
// }
|
||||
// if (maxTry === 0)
|
||||
// {
|
||||
// await fetch("http://transcendance:8080/auth/logout",
|
||||
// {
|
||||
// method : 'POST',
|
||||
// })
|
||||
// .then(response => response.json())
|
||||
// .then(push("/login"));
|
||||
// }
|
||||
// if (response.status === 200)
|
||||
// {
|
||||
// push("/");
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
|
||||
// My code
|
||||
|
||||
|
||||
|
||||
let auth;
|
||||
// we're expecting secret and otpauth
|
||||
|
||||
onMount( async() => {
|
||||
// auth = await fetch('http://transcendance:8080/api/v2/auth/2fa/generate', {
|
||||
// method: 'POST'
|
||||
// })
|
||||
// .then((resp) => resp.json());
|
||||
// console.log(auth.secret);
|
||||
|
||||
await fetch("http://transcendance:8080/api/v2/auth/2fa/generate", {
|
||||
method: 'POST',
|
||||
})
|
||||
.then(response => {return response.blob()})
|
||||
.then(blob => {
|
||||
const url = URL.createObjectURL(blob);
|
||||
qrCodeImg = url;
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
// testing loginStatus Custom Store
|
||||
|
||||
const toggleTFA = () => {
|
||||
loginStatus.toggleTFA();
|
||||
console.log($loginStatus.tfa);
|
||||
}
|
||||
|
||||
|
||||
// testing
|
||||
|
||||
let auth2
|
||||
const TFA = async() => {
|
||||
// ok no idea what goes in here...
|
||||
auth2 = await fetch('http://transcendance:8080/api/v2/auth/2fa/generate', {
|
||||
method: 'POST'
|
||||
})
|
||||
// .then((resp) => resp.json());
|
||||
|
||||
// console.log(auth2.secret);
|
||||
console.log(auth2);
|
||||
};
|
||||
|
||||
// if ($loginStatus.tfa && $loginStatus.fortyTwo)
|
||||
|
||||
</script>
|
||||
|
||||
<h1>2FA Test</h1>
|
||||
|
||||
<div>
|
||||
<button on:click={TFA}>TFA</button>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
{#if auth2}
|
||||
<p>{auth2.json()}</p>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<img src={qrCodeImg} alt="A QRCodeImg you must scan with google authenticator" id="qrcodeImg" />
|
||||
|
||||
|
||||
<!-- <p>FortyTwo: {$loginStatus.fortyTwo}</p>
|
||||
<p>TFA: {$loginStatus.tfa}</p>
|
||||
<p>isLogged: {loginStatus.isLogged}</p>
|
||||
|
||||
<div>
|
||||
<button on:click={toggleTFA}>toggleTFA</button>
|
||||
</div> -->
|
||||
|
||||
|
||||
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
@@ -1,149 +0,0 @@
|
||||
<script lang="ts">
|
||||
|
||||
// The User Page can have several Flavors ?
|
||||
// like a HomePage vibe, and an AccountPage vibe or whatever, but we'll still be serving this page just with diff props
|
||||
|
||||
import UserStore from "./UserStore";
|
||||
import Header from "../components/Header.svelte";
|
||||
import Footer from "../components/Footer.svelte";
|
||||
import { createEventDispatcher } from "svelte";
|
||||
import { loginStatus } from '../stores/loginStatusStore';
|
||||
import { push } from "svelte-spa-router";
|
||||
|
||||
let dispatch = createEventDispatcher();
|
||||
|
||||
// i fucking hate these vars, the will have to go
|
||||
export let pages;
|
||||
export let currentPage;
|
||||
export let userId;
|
||||
|
||||
// This shit is so redundant...
|
||||
let types = ['home', 'account']
|
||||
export let currentType = 'account';
|
||||
|
||||
// this is also stupid...
|
||||
let sidebar = true;
|
||||
|
||||
|
||||
// would i prefer to forward this?
|
||||
let clickedHome = () => {
|
||||
console.log('clicked home');
|
||||
// do something...
|
||||
currentType = 'home';
|
||||
};
|
||||
|
||||
let clickedLogout = async() => {
|
||||
console.log('clicked logout');
|
||||
await fetch('http://transcendance:8080/api/v2/auth/logout',);
|
||||
// $loginStatus = false;
|
||||
// maybe use replace() ?
|
||||
push('/');
|
||||
};
|
||||
|
||||
// All the variables that will eventually be replaced by the real values
|
||||
|
||||
let username = 'Username';
|
||||
let games = { total: 7, won: 4, lost: 3};
|
||||
let rank = 'gold or whatever the fuck who cares...';
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<!-- remove ={clickedHome} if you want to forward the event to App.svelte-->
|
||||
<!-- god this is some gross code... -->
|
||||
<Header on:clickedHome={clickedHome} currentType="{currentType === 'home' ? 'home' : 'regular'}" on:clickedLogout={clickedLogout}/>
|
||||
|
||||
<!-- The Wave -->
|
||||
<!-- <div class="spacer layer1"></div> -->
|
||||
|
||||
<!-- this is the thing that will let me offset -->
|
||||
<div class='{sidebar ? "main-grid" : "none"}'>
|
||||
{#if sidebar}
|
||||
<section class="sidebar">
|
||||
<p>i am a sidebar</p>
|
||||
</section>
|
||||
{/if}
|
||||
|
||||
<main class:offset={sidebar}>
|
||||
<!-- what the fuck do we even want in here? messges, about the user, STATISTICS!!! -->
|
||||
<!-- <div>some stuff goes here</div> -->
|
||||
<img class="icon" src="img/default_user_icon.png" alt="default user icon">
|
||||
<div>{username}</div>
|
||||
<div>Rank: {rank}</div>
|
||||
<section class="main-stats">
|
||||
<h4>Match Statistics</h4>
|
||||
<p>Total: {games.total}</p>
|
||||
<p>Victories: {games.won}</p>
|
||||
<p>Losses: {games.lost}</p>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
</div>
|
||||
|
||||
<Footer />
|
||||
|
||||
|
||||
<style>
|
||||
|
||||
|
||||
|
||||
/* from Haikei */
|
||||
/* for any Haikei image */
|
||||
.spacer{
|
||||
aspect-ratio: 900/300;
|
||||
width: 100%;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
background-size: cover;
|
||||
}
|
||||
/* the specific image we use, you need both classes */
|
||||
.layer1{
|
||||
background-image: url('/img/wave-haikei.svg');
|
||||
}
|
||||
|
||||
div.main-grid{
|
||||
display: grid;
|
||||
grid-template-columns: repeat(12, 1fr);
|
||||
/* max-height: calc(100vh - 30vh); */
|
||||
height: 85vh;
|
||||
}
|
||||
|
||||
section.sidebar{
|
||||
grid-column: 1 / span 2;
|
||||
background: white;
|
||||
}
|
||||
|
||||
/* The main part */
|
||||
main{
|
||||
max-width: 960px;
|
||||
margin: 40px auto;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
main.offset{
|
||||
grid-column: 3 / span 10;
|
||||
}
|
||||
|
||||
/* Normal CSS stuff */
|
||||
.icon{
|
||||
max-width: 150px;
|
||||
}
|
||||
|
||||
/* The variable rich section */
|
||||
section.main-stats{
|
||||
max-width: 600px;
|
||||
margin: 40px auto;
|
||||
text-align: center;
|
||||
/* i think i want to use a grid? */
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
/* not sure about this, maybe top should be larger? */
|
||||
grid-template-rows: repeat(3, 1fr);
|
||||
}
|
||||
|
||||
/* the stuff in the grid*/
|
||||
section.main-stats h4{
|
||||
grid-column: 1 / span 3;
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -1,45 +0,0 @@
|
||||
import { writable } from "svelte/store";
|
||||
|
||||
// ok yea this doesn't make a lot of sense, what am i trying to do?
|
||||
// have an array of objects that are all the users?
|
||||
// or an object that is the one user?
|
||||
// For now as a placeholder i'll have one user in one obj
|
||||
|
||||
// should it not be a const? yea seems like it
|
||||
// export const users = writable(
|
||||
const UserStore = writable(
|
||||
[{
|
||||
// this is an example user
|
||||
id: 1,
|
||||
username: 'chaboi',
|
||||
email: 'nope@fu.com',
|
||||
// surely there's a better way to do this!
|
||||
password: '1234',
|
||||
// maybe an object listing friends' usernames?
|
||||
friends: 0,
|
||||
loggedIn: false,
|
||||
// i imagine the user uploading their Avatare and it being put somehwere in the DB which could be referenced like a URL
|
||||
// so if this field is empty then use the default avatar
|
||||
avatar: '',
|
||||
// online, offline, gaming
|
||||
status: 'offline',
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
username: 'itsame',
|
||||
email: 'mario@nintendo.com',
|
||||
// surely there's a better way to do this!
|
||||
password: '1234',
|
||||
// maybe an object listing friends' usernames?
|
||||
friends: 0,
|
||||
loggedIn: false,
|
||||
// i imagine the user uploading their Avatare and it being put somehwere in the DB which could be referenced like a URL
|
||||
// so if this field is empty then use the default avatar
|
||||
avatar: '',
|
||||
// online, offline, gaming
|
||||
status: 'offline',
|
||||
}]
|
||||
);
|
||||
|
||||
export default UserStore;
|
||||
// export default users;
|
||||
@@ -1,24 +0,0 @@
|
||||
import { writable } from "svelte/store";
|
||||
|
||||
// an alternative way of doing things where i have a svelte store connected to localStorage
|
||||
|
||||
// do in need to adapt this to work with 2fa?
|
||||
|
||||
let _user = localStorage.getItem('42User');
|
||||
|
||||
// turns out a simple store is actually the easiest :)
|
||||
// export const userStore = writable(_user ? JSON.parse(_user) : null); // we start with no user, but go get one if one exists
|
||||
// export const userStore = writable(null);
|
||||
|
||||
// ok so this will happen no matter what, basically we are telling it what to do if the store containing the user changes
|
||||
userStore.subscribe((value) => {
|
||||
if (value)
|
||||
localStorage.setItem('42User', JSON.stringify(value));
|
||||
else
|
||||
localStorage.removeItem('42User'); // for logout
|
||||
});
|
||||
|
||||
export const userLogout = () => userStore.set(null);
|
||||
|
||||
|
||||
// export const tmpStore = userStore
|
||||
@@ -1,129 +0,0 @@
|
||||
import { writable } from "svelte/store";
|
||||
|
||||
// This is a "Custom Store" see that chapter in the Svelte Tutorial, should be fine
|
||||
// NVM this is definitely overkill
|
||||
// function createLogin() {
|
||||
// const { subscribe, update } = writable(false);
|
||||
|
||||
// return {
|
||||
// subscribe,
|
||||
// login: () => update(s => s = true),
|
||||
// logout: () => update(s => s = false),
|
||||
// }
|
||||
// }
|
||||
// export const loginStatus = createLogin();
|
||||
|
||||
// export const loginStatus = writable({
|
||||
// 42: false,
|
||||
// tfa: false,
|
||||
// });
|
||||
|
||||
// function createLoginStatus() {
|
||||
|
||||
// //ok it really hated all this
|
||||
|
||||
// // const store = writable({
|
||||
// // fortyTwo: false,
|
||||
// // tfa: false,
|
||||
// // });
|
||||
|
||||
// // return {
|
||||
// // ...store,
|
||||
// // subscribe,
|
||||
// // // toggle42: () => update( l => l.fortyTwo = !l.fortyTwo ),
|
||||
// // toggle42: () => store.update( fortyTwo => !fortyTwo ),
|
||||
// // // toggleTFA: () => update( l => l.tfa = !l.tfa ),
|
||||
// // toggleTFA: () => store.update( tfa => !tfa ),
|
||||
// // isLogged: () => store.fortyTwo && store.tfa,
|
||||
// // // isLogged: this.fortyTwo && this.tfa,
|
||||
// // // it really doesn't like "this."
|
||||
// // // isLogged: () => (this.tfa && this.fortyTwo),
|
||||
// // // this. ? or (l) => l.tfa ... ?
|
||||
// // }
|
||||
|
||||
|
||||
// // doesn't seem to work...
|
||||
// const { subscribe, update } = writable({
|
||||
// fortyTwo: false,
|
||||
// tfa: false,
|
||||
// });
|
||||
|
||||
// return {
|
||||
// subscribe,
|
||||
// // toggle42: () => update( l => l.fortyTwo = !l.fortyTwo ),
|
||||
// toggle42: () => update( fortyTwo => !fortyTwo ),
|
||||
// // toggleTFA: () => update( l => l.tfa = !l.tfa ),
|
||||
// toggleTFA: () => update( tfa => !tfa ),
|
||||
// // isLogged: () => fortyTwo && tfa,
|
||||
// // isLogged: this.fortyTwo && this.tfa,
|
||||
// // it really doesn't like "this."
|
||||
// // isLogged: () => (this.tfa && this.fortyTwo),
|
||||
// // this. ? or (l) => l.tfa ... ?
|
||||
// isLogged() {
|
||||
// return fortyTwo && tfa;
|
||||
// },
|
||||
// }
|
||||
|
||||
// // possible other way of doing this
|
||||
|
||||
// // const store = writable({
|
||||
// // fortyTwo: false,
|
||||
// // tfa: false,
|
||||
// // });
|
||||
|
||||
// // return {
|
||||
// // ...store,
|
||||
// // subscribe,
|
||||
// // // toggle42: () => update( l => l.fortyTwo = !l.fortyTwo ),
|
||||
// // toggle42: () => store.update( l.fortyTwo => !l.fortyTwo ),
|
||||
// // toggleTFA: () => store.update( l => l.tfa = !l.tfa ),
|
||||
// // isLogged: store.fortyTwo && store.tfa,
|
||||
// // // isLogged: () => (this.tfa && this.fortyTwo),
|
||||
// // // this. ? or (l) => l.tfa ... ?
|
||||
// // }
|
||||
|
||||
// }
|
||||
|
||||
function createLoginStatus() {
|
||||
const { subscribe, update } = writable({
|
||||
fortyTwo: false,
|
||||
tfa: false,
|
||||
});
|
||||
|
||||
function toggle42() {
|
||||
update( (old) => ({...old, fortyTwo: !old.fortyTwo}) );
|
||||
};
|
||||
|
||||
function toggleTFA() {
|
||||
// update( () => {
|
||||
// self.tfa = !self.tfa;
|
||||
// return self;
|
||||
// })
|
||||
// console.log("testing");
|
||||
update( (old) => ({...old, tfa: !old.tfa}) );
|
||||
};
|
||||
|
||||
function isLogged() {
|
||||
// return (l) => {l.fortyTwo && l.tfa};
|
||||
// return self.fortyTwo && self.tfa;
|
||||
// return fortyTwo && tfa;
|
||||
};
|
||||
|
||||
return { subscribe, update, toggle42, toggleTFA, isLogged };
|
||||
}
|
||||
|
||||
export const loginStatus = createLoginStatus();
|
||||
|
||||
// OK let's try a totally new approach
|
||||
|
||||
// const _loginStatus = writable({
|
||||
// fortyTwo: false,
|
||||
// tfa: false,
|
||||
// })
|
||||
|
||||
// export const loginStatus = {
|
||||
// subscribe: _loginStatus.subscribe,
|
||||
// set: _loginStatus.set,
|
||||
// update: _loginStatus.update,
|
||||
// toggle42: () =>
|
||||
// }
|
||||
@@ -1,54 +0,0 @@
|
||||
@font-face {
|
||||
font-family: 'Monocode-Regular-Demo';
|
||||
src:url('/fonts/Monocode-Regular-Demo.ttf.woff') format('woff'),
|
||||
url('Monocode-Regular-Demo.ttf.svg#Monocode-Regular-Demo') format('svg'),
|
||||
url('Monocode-Regular-Demo.ttf.eot'),
|
||||
url('Monocode-Regular-Demo.ttf.eot?#iefix') format('embedded-opentype');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Air-Conditioner';
|
||||
src:url('/fonts/Air-Conditioner.ttf.woff') format('woff'),
|
||||
url('Air-Conditioner.ttf.svg#Air-Conditioner') format('svg'),
|
||||
url('Air-Conditioner.ttf.eot'),
|
||||
url('Air-Conditioner.ttf.eot?#iefix') format('embedded-opentype');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
@font-face {
|
||||
font-family: '1968-Odyssey-3D';
|
||||
src:url('/fonts/1968-Odyssey-3D.ttf.woff') format('woff'),
|
||||
url('1968-Odyssey-3D.ttf.svg#1968-Odyssey-3D') format('svg'),
|
||||
url('1968-Odyssey-3D.ttf.eot'),
|
||||
url('1968-Odyssey-3D.ttf.eot?#iefix') format('embedded-opentype');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
@font-face {
|
||||
font-family: '1968-Odyssey-Gradient';
|
||||
src:url('/fonts/1968-Odyssey-Gradient.ttf.woff') format('woff'),
|
||||
url('1968-Odyssey-Gradient.ttf.svg#1968-Odyssey-Gradient') format('svg'),
|
||||
url('1968-Odyssey-Gradient.ttf.eot'),
|
||||
url('1968-Odyssey-Gradient.ttf.eot?#iefix') format('embedded-opentype');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'AddFatMan';
|
||||
src:url('/fonts/AddFatMan.ttf.woff') format('woff'),
|
||||
url('/fonts/AddFatMan.ttf.svg#AddFatMan') format('svg'),
|
||||
url('/fonts/AddFatMan.ttf.eot'),
|
||||
url('/fonts/AddFatMan.ttf.eot?#iefix') format('embedded-opentype');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Bondi';
|
||||
src:url('/fonts/Bondi.ttf.woff') format('woff'),
|
||||
url('/fonts/Bondi.ttf.svg#Bondi') format('svg'),
|
||||
url('/fonts/Bondi.ttf.eot'),
|
||||
url('/fonts/Bondi.ttf.eot?#iefix') format('embedded-opentype');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
@@ -1,177 +0,0 @@
|
||||
<script lang="ts">
|
||||
import NotFound from "../src/pages/NotFound.svelte";
|
||||
import ProfilePage from "../src/pages/profile/ProfilePage.svelte";
|
||||
import SplashPage from "../src/pages/SplashPage.svelte";
|
||||
import TwoFactorAuthentication from '../src/pages/TwoFactorAuthentication.svelte';
|
||||
import UnauthorizedAccessPage from '../src/pages/UnauthorizedAccessPage.svelte';
|
||||
import { wrap } from 'svelte-spa-router/wrap'
|
||||
import { get } from 'svelte/store';
|
||||
|
||||
import TestPage from '../src/pages/TmpTestPage.svelte';
|
||||
import { userStore, userLogout } from "../src/stores/loginStatusStore";
|
||||
|
||||
|
||||
|
||||
// "/article/:title": Article, // this is how you would do parameters!
|
||||
// "/": LoginPage,
|
||||
|
||||
// TMP not using this cuz need to work out how to authentical both 42 and 2FA from the backend
|
||||
|
||||
// export const primaryRoutes = {
|
||||
// '/': SplashPage,
|
||||
// // '/2fa': TwoFactorAuthentication,
|
||||
// '/2fa': wrap({
|
||||
// component: TwoFactorAuthentication,
|
||||
// conditions: [
|
||||
// (detail) => {
|
||||
// // let loggedIn;
|
||||
// // loginStatus.subscribe(value => {
|
||||
// // loggedIn = value;
|
||||
// // });
|
||||
|
||||
// const { fortyTwo, tfa } = get(loginStatus);
|
||||
|
||||
// console.log('condition in /2fa');
|
||||
// // return (loginStatus.fortyTwo && loginStatus.tfa);
|
||||
// // console.log($loginStatus.fortyTwo)
|
||||
// console.log(fortyTwo);
|
||||
// console.log(tfa);
|
||||
// return true;
|
||||
// }
|
||||
// ]
|
||||
// }),
|
||||
// '/profile': wrap({
|
||||
// component: ProfilePage,
|
||||
// conditions: [
|
||||
// (detail) => {
|
||||
// const { fortyTwo, tfa } = get(loginStatus);
|
||||
// // console.log(fortyTwo);
|
||||
// // console.log(tfa);
|
||||
// // return true;
|
||||
// return (fortyTwo && tfa);
|
||||
// }
|
||||
// ]
|
||||
// }),
|
||||
// '/profile/*': wrap({
|
||||
// component: ProfilePage,
|
||||
// conditions: [
|
||||
// (detail) => {
|
||||
// const { fortyTwo, tfa } = get(loginStatus);
|
||||
// // console.log(fortyTwo);
|
||||
// // console.log(tfa);
|
||||
// // return true;
|
||||
// return (fortyTwo && tfa);
|
||||
// }
|
||||
// ]
|
||||
// }),
|
||||
// '/profile': wrap({
|
||||
// // Use a dynamically-loaded component for this
|
||||
// asyncComponent: () => import('./ProfilePage.svelte'),
|
||||
// // Adding one pre-condition that's an async function
|
||||
// conditions: [
|
||||
// async (detail) => {
|
||||
// // Make a network request, which are async operations
|
||||
// const response = await fetch('http://transcendance:8080/api/v2/user')
|
||||
// const data = await response.json()
|
||||
// // Return true to continue loading the component, or false otherwise
|
||||
// if (data.isAdmin) {
|
||||
// return true
|
||||
// }
|
||||
// else {
|
||||
// return false
|
||||
// }
|
||||
// }
|
||||
// ]
|
||||
// }),
|
||||
// '/unauthorized-access': UnauthorizedAccessPage,
|
||||
// '*': NotFound
|
||||
// };
|
||||
|
||||
export const primaryRoutes = {
|
||||
"/": SplashPage,
|
||||
'/test': wrap({
|
||||
component: TestPage,
|
||||
conditions: [
|
||||
(detail) => {
|
||||
// const user = get(userStore); // seems like get(store) is not an option
|
||||
// // const user = userStore;
|
||||
// // console.log(fortyTwo);
|
||||
// // console.log(tfa);
|
||||
// console.log('in /test what is in user')
|
||||
// console.log(user)
|
||||
|
||||
// you moron $userStore is a Svelte Abreviation, this is .JS, duh
|
||||
// let user = $userStore;
|
||||
let user;
|
||||
const unsub = userStore.subscribe(value => {
|
||||
user = value;
|
||||
});
|
||||
console.log('in /test what is in userStore directly')
|
||||
console.log(user)
|
||||
|
||||
// return true;
|
||||
// obvi this doesn't work cuz skips to true after no user...
|
||||
// you gotta make the condition the true and the everything else false
|
||||
// if (user && user.statusCode && user.statusCode === 403)
|
||||
// if (user !== null) {
|
||||
if (user && user.username) {
|
||||
unsub();
|
||||
return true;
|
||||
} else {
|
||||
unsub();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
]
|
||||
}),
|
||||
// '/test': wrap({
|
||||
// component: TestPage,
|
||||
// conditions: [
|
||||
// async(detail) => {
|
||||
// // THIS SHIT TOTALLY WORKS
|
||||
// const user = await fetch('http://transcendance:8080/api/v2/user')
|
||||
// .then((resp) => resp.json())
|
||||
|
||||
// console.log('in /test what is in user')
|
||||
// console.log(user)
|
||||
|
||||
// if (user && user.username)
|
||||
// return true;
|
||||
// else
|
||||
// return false;
|
||||
// }
|
||||
// ]
|
||||
// }),
|
||||
'/2fa': TwoFactorAuthentication,
|
||||
"/profile": ProfilePage,
|
||||
"/profile/*": ProfilePage,
|
||||
'/unauthorized-access': UnauthorizedAccessPage,
|
||||
"*": NotFound
|
||||
};
|
||||
|
||||
// export const primaryRoutes = {
|
||||
// "/": SplashPage,
|
||||
// "/profile": ProfilePage,
|
||||
// "/game": GamePage,
|
||||
// "/chat": ChatPage,
|
||||
// "*": NotFound
|
||||
// };
|
||||
|
||||
|
||||
// i might need to add /profile/* and such to make the nested routers work
|
||||
|
||||
// ok maybe these need to be in their own files?
|
||||
|
||||
|
||||
|
||||
// export const gameRoutes = {
|
||||
// "/": GamePage,
|
||||
// "*": NotFound
|
||||
// };
|
||||
|
||||
// export const chatRoutes = {
|
||||
// "/": ChatPage,
|
||||
// "*": NotFound
|
||||
// };
|
||||
|
||||
</script>
|
||||
1308
srcs/requirements/svelte/api_front/package-lock.json
generated
1308
srcs/requirements/svelte/api_front/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -13,6 +13,7 @@
|
||||
"@rollup/plugin-node-resolve": "^11.0.0",
|
||||
"@rollup/plugin-typescript": "^8.0.0",
|
||||
"@tsconfig/svelte": "^2.0.0",
|
||||
"@types/node": "^18.11.18",
|
||||
"rollup": "^2.3.4",
|
||||
"rollup-plugin-css-only": "^3.1.0",
|
||||
"rollup-plugin-livereload": "^2.0.0",
|
||||
@@ -25,7 +26,10 @@
|
||||
"typescript": "^4.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@rollup/plugin-replace": "^5.0.2",
|
||||
"dotenv": "^16.0.3",
|
||||
"sirv-cli": "^2.0.0",
|
||||
"socket.io-client": "^4.5.4",
|
||||
"svelte-spa-router": "^3.3.0"
|
||||
}
|
||||
}
|
||||
|
||||
23467
srcs/requirements/svelte/api_front/public/build/bundle.js
Normal file
23467
srcs/requirements/svelte/api_front/public/build/bundle.js
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
BIN
srcs/requirements/svelte/api_front/public/favicon.ico
Normal file
BIN
srcs/requirements/svelte/api_front/public/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 3.1 KiB |
@@ -1,16 +0,0 @@
|
||||
|
||||
import * as c from "./constants.js"
|
||||
|
||||
export const soundPongArr: HTMLAudioElement[] = [];
|
||||
export const soundRoblox = new Audio("http://localhost:8080/sound/roblox-oof.ogg");
|
||||
|
||||
export function initAudio(muteFlag: boolean)
|
||||
{
|
||||
for (let i = 0; i <= 32; i++) {
|
||||
soundPongArr.push(new Audio("http://localhost:8080/sound/pong/"+i+".ogg"));
|
||||
soundPongArr[i].volume = c.soundPongVolume;
|
||||
soundPongArr[i].muted = muteFlag;
|
||||
}
|
||||
soundRoblox.volume = c.soundRobloxVolume;
|
||||
soundRoblox.muted = muteFlag;
|
||||
}
|
||||
@@ -1,107 +0,0 @@
|
||||
|
||||
import * as en from "../enums.js"
|
||||
|
||||
/* From Server */
|
||||
class ServerEvent {
|
||||
type: en.EventTypes;
|
||||
constructor(type: en.EventTypes = 0) {
|
||||
this.type = type;
|
||||
}
|
||||
}
|
||||
|
||||
class EventAssignId extends ServerEvent {
|
||||
id: string;
|
||||
constructor(id: string) {
|
||||
super(en.EventTypes.assignId);
|
||||
this.id = id;
|
||||
}
|
||||
}
|
||||
|
||||
class EventMatchmakingComplete extends ServerEvent {
|
||||
side: en.PlayerSide;
|
||||
constructor(side: en.PlayerSide) {
|
||||
super(en.EventTypes.matchmakingComplete);
|
||||
this.side = side;
|
||||
}
|
||||
}
|
||||
|
||||
class EventGameUpdate extends ServerEvent {
|
||||
playerLeft = {
|
||||
y: 0
|
||||
};
|
||||
playerRight = {
|
||||
y: 0
|
||||
};
|
||||
ballsArr: {
|
||||
x: number,
|
||||
y: number,
|
||||
dirX: number,
|
||||
dirY: number,
|
||||
speed: number
|
||||
}[] = [];
|
||||
wallTop? = {
|
||||
y: 0
|
||||
};
|
||||
wallBottom? = {
|
||||
y: 0
|
||||
};
|
||||
lastInputId = 0;
|
||||
constructor() { // TODO: constructor that take GameComponentsServer maybe ?
|
||||
super(en.EventTypes.gameUpdate);
|
||||
}
|
||||
}
|
||||
|
||||
class EventScoreUpdate extends ServerEvent {
|
||||
scoreLeft: number;
|
||||
scoreRight: number;
|
||||
constructor(scoreLeft: number, scoreRight: number) {
|
||||
super(en.EventTypes.scoreUpdate);
|
||||
this.scoreLeft = scoreLeft;
|
||||
this.scoreRight = scoreRight;
|
||||
}
|
||||
}
|
||||
|
||||
class EventMatchEnd extends ServerEvent {
|
||||
winner: en.PlayerSide;
|
||||
constructor(winner: en.PlayerSide) {
|
||||
super(en.EventTypes.matchEnd);
|
||||
this.winner = winner;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* From Client */
|
||||
class ClientEvent {
|
||||
type: en.EventTypes; // readonly ?
|
||||
constructor(type: en.EventTypes = 0) {
|
||||
this.type = type;
|
||||
}
|
||||
}
|
||||
|
||||
class ClientAnnounce extends ClientEvent {
|
||||
role: en.ClientRole;
|
||||
clientId: string;
|
||||
matchOptions: en.MatchOptions;
|
||||
constructor(role: en.ClientRole, matchOptions: en.MatchOptions, clientId: string = "") {
|
||||
super(en.EventTypes.clientAnnounce);
|
||||
this.role = role;
|
||||
this.clientId = clientId;
|
||||
this.matchOptions = matchOptions;
|
||||
}
|
||||
}
|
||||
|
||||
class EventInput extends ClientEvent {
|
||||
input: en.InputEnum;
|
||||
id: number;
|
||||
constructor(input: en.InputEnum = en.InputEnum.noInput, id: number = 0) {
|
||||
super(en.EventTypes.clientInput);
|
||||
this.input = input;
|
||||
this.id = id;
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
ServerEvent, EventAssignId, EventMatchmakingComplete,
|
||||
EventGameUpdate, EventScoreUpdate, EventMatchEnd,
|
||||
ClientEvent, ClientAnnounce, EventInput
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
|
||||
import * as c from "./constants.js";
|
||||
import * as en from "../shared_js/enums.js"
|
||||
import { gc, matchOptions, clientInfo } from "./global.js";
|
||||
import { wallsMovements } from "../shared_js/wallsMovement.js";
|
||||
|
||||
let actual_time: number = Date.now();
|
||||
let last_time: number;
|
||||
let delta_time: number;
|
||||
|
||||
function gameLoop()
|
||||
{
|
||||
/* last_time = actual_time;
|
||||
actual_time = Date.now();
|
||||
delta_time = (actual_time - last_time) / 1000; */
|
||||
|
||||
delta_time = c.fixedDeltaTime;
|
||||
// console.log(`delta_gameLoop: ${delta_time}`);
|
||||
|
||||
// interpolation
|
||||
// console.log(`dir.y: ${clientInfo.opponent.dir.y}, pos.y: ${clientInfo.opponent.pos.y}, opponentNextPos.y: ${clientInfo.opponentNextPos.y}`);
|
||||
if (clientInfo.opponent.dir.y != 0 ) {
|
||||
opponentInterpolation(delta_time);
|
||||
}
|
||||
|
||||
// client prediction
|
||||
gc.ballsArr.forEach((ball) => {
|
||||
ball.moveAndBounce(delta_time, [gc.wallTop, gc.wallBottom, gc.playerLeft, gc.playerRight]);
|
||||
});
|
||||
|
||||
if (matchOptions & en.MatchOptions.movingWalls) {
|
||||
wallsMovements(delta_time, gc);
|
||||
}
|
||||
}
|
||||
|
||||
function opponentInterpolation(delta: number)
|
||||
{
|
||||
// interpolation
|
||||
clientInfo.opponent.moveAndCollide(delta, [gc.wallTop, gc.wallBottom]);
|
||||
|
||||
if ((clientInfo.opponent.dir.y > 0 && clientInfo.opponent.pos.y > clientInfo.opponentNextPos.y)
|
||||
|| (clientInfo.opponent.dir.y < 0 && clientInfo.opponent.pos.y < clientInfo.opponentNextPos.y))
|
||||
{
|
||||
clientInfo.opponent.dir.y = 0;
|
||||
clientInfo.opponent.pos.y = clientInfo.opponentNextPos.y;
|
||||
}
|
||||
}
|
||||
|
||||
export {gameLoop}
|
||||
@@ -1,3 +0,0 @@
|
||||
|
||||
export {pong, gc, matchOptions} from "./pong.js"
|
||||
export {socket, clientInfo} from "./ws.js"
|
||||
@@ -1,99 +0,0 @@
|
||||
|
||||
initDom();
|
||||
function initDom() {
|
||||
document.getElementById("play_pong_button").addEventListener("click", init);
|
||||
}
|
||||
|
||||
import * as c from "./constants.js"
|
||||
import * as en from "../shared_js/enums.js"
|
||||
import { GameArea } from "./class/GameArea.js";
|
||||
import { GameComponentsClient } from "./class/GameComponentsClient.js";
|
||||
import { handleInput } from "./handleInput.js";
|
||||
// import { sendLoop } from "./handleInput.js";
|
||||
import { gameLoop } from "./gameLoop.js"
|
||||
import { drawLoop } from "./draw.js";
|
||||
import { countdown } from "./utils.js";
|
||||
import { initWebSocket } from "./ws.js";
|
||||
import { initAudio } from "./audio.js";
|
||||
|
||||
|
||||
/* Keys
|
||||
Racket: W/S OR Up/Down
|
||||
Grid On-Off: G
|
||||
*/
|
||||
|
||||
/* TODO: A way to delay the init of variables, but still use "const" not "let" ? */
|
||||
export let pong: GameArea;
|
||||
export let gc: GameComponentsClient;
|
||||
export let matchOptions: en.MatchOptions = en.MatchOptions.noOption;
|
||||
|
||||
function init()
|
||||
{
|
||||
console.log("multi_balls:"+(<HTMLInputElement>document.getElementById("multi_balls")).checked);
|
||||
console.log("moving_walls:"+(<HTMLInputElement>document.getElementById("moving_walls")).checked);
|
||||
console.log("sound_on:"+(<HTMLInputElement>document.getElementById("sound_on")).checked);
|
||||
|
||||
let soundMutedFlag = false;
|
||||
if ( (<HTMLInputElement>document.getElementById("sound_off")).checked ) {
|
||||
soundMutedFlag = true;
|
||||
}
|
||||
initAudio(soundMutedFlag);
|
||||
|
||||
if ( (<HTMLInputElement>document.getElementById("multi_balls")).checked ) {
|
||||
matchOptions |= en.MatchOptions.multiBalls;
|
||||
}
|
||||
if ( (<HTMLInputElement>document.getElementById("moving_walls")).checked ) {
|
||||
matchOptions |= en.MatchOptions.movingWalls;
|
||||
}
|
||||
|
||||
document.getElementById("div_game_options").hidden = true;
|
||||
|
||||
pong = new GameArea();
|
||||
gc = new GameComponentsClient(matchOptions, pong.ctx);
|
||||
initWebSocket(matchOptions);
|
||||
}
|
||||
|
||||
function matchmaking()
|
||||
{
|
||||
console.log("Searching an opponent...");
|
||||
gc.text1.clear();
|
||||
gc.text1.pos.assign(c.w/5, c.h_mid);
|
||||
gc.text1.text = "Searching...";
|
||||
gc.text1.update();
|
||||
}
|
||||
|
||||
function matchmakingComplete()
|
||||
{
|
||||
console.log("Match Found !");
|
||||
gc.text1.clear();
|
||||
gc.text1.pos.assign(c.w/8, c.h_mid);
|
||||
gc.text1.text = "Match Found !";
|
||||
gc.text1.update();
|
||||
}
|
||||
|
||||
function startGame() {
|
||||
gc.text1.pos.assign(c.w_mid, c.h_mid+c.h/4);
|
||||
countdown(c.matchStartDelay/1000, (count: number) => {
|
||||
gc.text1.clear();
|
||||
gc.text1.text = `${count}`;
|
||||
gc.text1.update();
|
||||
}, resumeGame);
|
||||
}
|
||||
|
||||
function resumeGame()
|
||||
{
|
||||
gc.text1.text = "";
|
||||
window.addEventListener('keydown', function (e) {
|
||||
pong.addKey(e.key);
|
||||
});
|
||||
window.addEventListener('keyup', function (e) {
|
||||
pong.deleteKey(e.key);
|
||||
});
|
||||
pong.handleInputInterval = window.setInterval(handleInput, c.handleInputIntervalMS);
|
||||
// pong.handleInputInterval = window.setInterval(sendLoop, c.sendLoopIntervalMS);
|
||||
pong.gameLoopInterval = window.setInterval(gameLoop, c.gameLoopIntervalMS);
|
||||
pong.drawLoopInterval = window.setInterval(drawLoop, c.drawLoopIntervalMS);
|
||||
}
|
||||
|
||||
|
||||
export {matchmaking, matchmakingComplete, startGame}
|
||||
@@ -1,27 +0,0 @@
|
||||
|
||||
import { MovingRectangle } from "./class/Rectangle.js";
|
||||
|
||||
function random(min: number = 0, max: number = 1) {
|
||||
return Math.random() * (max - min) + min;
|
||||
}
|
||||
|
||||
function sleep (ms: number) {
|
||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
function clamp(n: number, min: number, max: number) : number
|
||||
{
|
||||
if (n < min)
|
||||
n = min;
|
||||
else if (n > max)
|
||||
n = max;
|
||||
return (n);
|
||||
}
|
||||
|
||||
// Typescript hack, unused
|
||||
function assertMovingRectangle(value: unknown): asserts value is MovingRectangle {
|
||||
// if (value !== MovingRectangle) throw new Error("Not a MovingRectangle");
|
||||
return;
|
||||
}
|
||||
|
||||
export {random, sleep, clamp, assertMovingRectangle}
|
||||
@@ -1,3 +1,4 @@
|
||||
|
||||
html, body {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
<meta charset='utf-8'>
|
||||
<meta name='viewport' content='width=device-width,initial-scale=1'>
|
||||
|
||||
<title>Svelte app</title>
|
||||
<title>Potato Pong</title>
|
||||
|
||||
<link rel='icon' type='image/png' href='/favicon.png'>
|
||||
<link rel='icon' type='image/x-icon' href='/favicon.ico'>
|
||||
<link rel='stylesheet' href='/global.css'>
|
||||
<link rel='stylesheet' href='/build/bundle.css'>
|
||||
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -6,8 +6,13 @@ import { terser } from 'rollup-plugin-terser';
|
||||
import sveltePreprocess from 'svelte-preprocess';
|
||||
import typescript from '@rollup/plugin-typescript';
|
||||
import css from 'rollup-plugin-css-only';
|
||||
import replace from '@rollup/plugin-replace';
|
||||
import dotenv from 'dotenv';
|
||||
|
||||
|
||||
const production = !process.env.ROLLUP_WATCH;
|
||||
dotenv.config();
|
||||
|
||||
|
||||
function serve() {
|
||||
let server;
|
||||
@@ -30,6 +35,7 @@ function serve() {
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
export default {
|
||||
input: 'src/main.ts',
|
||||
output: {
|
||||
@@ -46,6 +52,10 @@ export default {
|
||||
dev: !production
|
||||
}
|
||||
}),
|
||||
replace({
|
||||
'process.env.WEBSITE_HOST': `'${process.env.WEBSITE_HOST}'`,
|
||||
'process.env.WEBSITE_PORT': `'${process.env.WEBSITE_PORT}'`,
|
||||
}),
|
||||
// we'll extract any component CSS out into
|
||||
// a separate file - better for performance
|
||||
css({ output: 'bundle.css' }),
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// routing
|
||||
// may not need {link} here
|
||||
import Router, { link, replace } from "svelte-spa-router";
|
||||
import { primaryRoutes } from "./routes/primaryRoutes.js";
|
||||
import { primaryRoutes } from "./routes/primaryRoutes.js";
|
||||
// import primaryRoutes from "./routes/primaryRoutes.svelte";
|
||||
|
||||
const conditionsFailed = (event) => {
|
||||
@@ -12,22 +12,8 @@
|
||||
replace('/unauthorized-access');
|
||||
};
|
||||
|
||||
// this might be the part where we get rid of localstorage when the app is quit?
|
||||
// onDestroy()
|
||||
// maybe done with cookie
|
||||
|
||||
</script>
|
||||
|
||||
<!-- <h1>Testing</h1> -->
|
||||
<Router routes={primaryRoutes} on:conditionsFailed={conditionsFailed}/>
|
||||
|
||||
|
||||
<style>
|
||||
|
||||
/* doesn't work... */
|
||||
/* body{
|
||||
background: bisque;
|
||||
} */
|
||||
</style>
|
||||
|
||||
|
||||
|
||||
12
srcs/requirements/svelte/api_front/src/main.js
Normal file
12
srcs/requirements/svelte/api_front/src/main.js
Normal file
@@ -0,0 +1,12 @@
|
||||
import App from './App.svelte';
|
||||
import dotenv from 'dotenv';
|
||||
|
||||
dotenv.config();
|
||||
const app = new App({
|
||||
target: document.body,
|
||||
props: {
|
||||
// name: 'world'
|
||||
}
|
||||
});
|
||||
export default app;
|
||||
//# sourceMappingURL=main.js.map
|
||||
1
srcs/requirements/svelte/api_front/src/main.js.map
Normal file
1
srcs/requirements/svelte/api_front/src/main.js.map
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"main.js","sourceRoot":"","sources":["main.ts"],"names":[],"mappings":"AAAA,OAAO,GAAG,MAAM,cAAc,CAAC;AAE/B,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC;IACnB,MAAM,EAAE,QAAQ,CAAC,IAAI;IACrB,KAAK,EAAE;IACN,gBAAgB;KAChB;CACD,CAAC,CAAC;AAEH,eAAe,GAAG,CAAC"}
|
||||
@@ -7,4 +7,4 @@ const app = new App({
|
||||
}
|
||||
});
|
||||
|
||||
export default app;
|
||||
export default app;
|
||||
|
||||
@@ -1,106 +0,0 @@
|
||||
<script lang="ts">
|
||||
|
||||
// import { initDom } from "../game/client/pong.js";
|
||||
import {onMount} from 'svelte';
|
||||
|
||||
onMount(() => {
|
||||
// initDom();
|
||||
})
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<body>
|
||||
<div id="div_game_options">
|
||||
<fieldset>
|
||||
<legend>game options</legend>
|
||||
<div>
|
||||
<input type="checkbox" id="multi_balls" name="multi_balls">
|
||||
<label for="multi_balls">multiples balls</label>
|
||||
</div>
|
||||
<div>
|
||||
<input type="checkbox" id="moving_walls" name="moving_walls">
|
||||
<label for="moving_walls">moving walls</label>
|
||||
</div>
|
||||
<div>
|
||||
<label>sound :</label>
|
||||
<input type="radio" id="sound_on" name="sound_selector" checked>
|
||||
<label for="sound_on">on</label>
|
||||
<input type="radio" id="sound_off" name="sound_selector">
|
||||
<label for="sound_off">off</label>
|
||||
</div>
|
||||
<div>
|
||||
<button id="play_pong_button">PLAY</button>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
|
||||
<div id="div_game_instructions">
|
||||
<h2>--- keys ---</h2>
|
||||
<p>move up: 'w' or 'up arrow'</p>
|
||||
<p>move down: 's' OR 'down arrow'</p>
|
||||
<p>grid on/off: 'g'</p>
|
||||
</div>
|
||||
|
||||
<div id="canvas_container">
|
||||
<!-- <p> =) </p> -->
|
||||
</div>
|
||||
|
||||
<!-- <script src="http://localhost:8080/js/pong.js" type="module" defer></script> -->
|
||||
</body>
|
||||
|
||||
<style>
|
||||
@font-face {
|
||||
font-family: "Bit5x3";
|
||||
src: url("/fonts/Bit5x3.woff2") format("woff2"),
|
||||
url("/fonts/Bit5x3.woff") format("woff");
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
body {
|
||||
margin: 0;
|
||||
background-color: #222425;
|
||||
}
|
||||
#canvas_container {
|
||||
margin-top: 20px;
|
||||
text-align: center;
|
||||
/* border: dashed rgb(245, 245, 245) 5px; */
|
||||
/* max-height: 80vh; */
|
||||
/* overflow: hidden; */
|
||||
}
|
||||
#div_game_instructions {
|
||||
text-align: center;
|
||||
font-family: "Bit5x3";
|
||||
color: rgb(245, 245, 245);
|
||||
font-size: large;
|
||||
}
|
||||
#div_game_options {
|
||||
margin-top: 20px;
|
||||
text-align: center;
|
||||
font-family: "Bit5x3";
|
||||
color: rgb(245, 245, 245);
|
||||
font-size: x-large;
|
||||
}
|
||||
#div_game_options fieldset {
|
||||
max-width: 50vw;
|
||||
width: auto;
|
||||
margin: 0 auto;
|
||||
}
|
||||
#div_game_options fieldset div {
|
||||
padding: 10px;
|
||||
}
|
||||
#play_pong_button {
|
||||
font-family: "Bit5x3";
|
||||
color: rgb(245, 245, 245);
|
||||
background-color: #333333;
|
||||
font-size: x-large;
|
||||
padding: 10px;
|
||||
}
|
||||
canvas {
|
||||
background-color: #333333;
|
||||
max-width: 75vw;
|
||||
/* max-height: 100vh; */
|
||||
width: 80%;
|
||||
}
|
||||
</style>
|
||||
@@ -2,12 +2,11 @@
|
||||
import Canvas from "../pieces/Canvas.svelte";
|
||||
import { push } from "svelte-spa-router";
|
||||
import { onMount } from 'svelte';
|
||||
import { get } from "svelte/store";
|
||||
|
||||
let user;
|
||||
|
||||
onMount(async () => {
|
||||
user = await fetch('http://transcendance:8080/api/v2/user')
|
||||
user = await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/user`)
|
||||
.then((resp) => resp.json())
|
||||
|
||||
// i mean i could do a failed to load user or some shit, maybe with a .catch or something? but atm why bother
|
||||
@@ -31,14 +30,14 @@
|
||||
});
|
||||
|
||||
const login = async() => {
|
||||
window.location.href = 'http://transcendance:8080/api/v2/auth';
|
||||
window.location.href = `http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/auth`;
|
||||
console.log('you are now logged in');
|
||||
}
|
||||
|
||||
// i could prolly put this in it's own compoent, i seem to use it in several places... or maybe just some JS? like no need for html
|
||||
// we could .then( () => replace('/') ) need the func so TS compatible...
|
||||
const logout = async() => {
|
||||
await fetch('http://transcendance:8080/api/v2/auth/logout', {
|
||||
await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/auth/logout`, {
|
||||
method: 'POST',
|
||||
});
|
||||
user = undefined;
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
<script lang="ts">
|
||||
|
||||
import { replace } from "svelte-spa-router";
|
||||
|
||||
export let user;
|
||||
|
||||
</script>
|
||||
|
||||
<div class="wrapper">
|
||||
<h1>You made it to Test</h1>
|
||||
<button on:click={ () => (replace('/'))}>Go Home</button>
|
||||
<div>{user}</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
|
||||
div.wrapper{
|
||||
display: flexbox;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -2,24 +2,13 @@
|
||||
import { onMount } from "svelte";
|
||||
import { push } from "svelte-spa-router";
|
||||
|
||||
// onMount( async() => {
|
||||
// await fetch("http://transcendance:8080/api/v2/auth/2fa/generate",
|
||||
// {
|
||||
// method: 'POST',
|
||||
// })
|
||||
// .then(response => {return response.blob()})
|
||||
// .then(blob => {
|
||||
// const url = URL.createObjectURL(blob);
|
||||
// qrCodeImg = url;
|
||||
// });
|
||||
// });
|
||||
|
||||
|
||||
let qrCodeImg;
|
||||
let qrCode = "";
|
||||
let wrongCode = "";
|
||||
const fetchQrCodeImg = (async() => {
|
||||
await fetch("http://transcendance:8080/api/v2/auth/2fa/generate",
|
||||
await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/auth/2fa/generate`,
|
||||
{
|
||||
method: 'POST',
|
||||
})
|
||||
@@ -31,7 +20,7 @@
|
||||
})()
|
||||
|
||||
const submitCode = async() => {
|
||||
const response = await fetch("http://transcendance:8080/api/v2/auth/2fa/check",
|
||||
const response = await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/auth/2fa/check`,
|
||||
{
|
||||
method : 'POST',
|
||||
headers : {
|
||||
|
||||
@@ -1,99 +1,379 @@
|
||||
|
||||
<script>
|
||||
import "public/game/pong.js"
|
||||
<script lang="ts">
|
||||
import { onMount, onDestroy } from "svelte";
|
||||
import Header from '../../pieces/Header.svelte';
|
||||
import { fade, fly } from 'svelte/transition';
|
||||
|
||||
|
||||
import * as pong from "./client/pong";
|
||||
import { gameState } from "./client/ws";
|
||||
|
||||
//user's stuff
|
||||
let user;
|
||||
let allUsers;
|
||||
|
||||
//Game's stuff
|
||||
let optionsAreNotSet = true;
|
||||
const options = new pong.InitOptions();
|
||||
|
||||
//Game's stuff client side only
|
||||
const gameAreaId = "game_area";
|
||||
|
||||
//html boolean for pages
|
||||
let showWaitPage = false;
|
||||
let showInvitations = false;
|
||||
let showGameOption = true;
|
||||
let showError = false;
|
||||
let hiddenGame = true;
|
||||
let showMatchEnded = false;
|
||||
|
||||
let isThereAnyInvitation = false;
|
||||
let invitations = [];
|
||||
|
||||
let waitingMessage = "Please wait..."
|
||||
let errorMessageWhenAttemptingToGetATicket = "";
|
||||
let idOfIntevalCheckTerminationOfTheMatch;
|
||||
|
||||
onMount( async() => {
|
||||
user = await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/user`)
|
||||
.then( x => x.json() );
|
||||
allUsers = await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/user/all`)
|
||||
.then( x => x.json() );
|
||||
options.playerOneUsername = user.username;
|
||||
})
|
||||
|
||||
onDestroy( async() => {
|
||||
clearInterval(idOfIntevalCheckTerminationOfTheMatch);
|
||||
pong.destroy();
|
||||
})
|
||||
|
||||
const initGame = async() =>
|
||||
{
|
||||
optionsAreNotSet = false;
|
||||
showWaitPage = true;
|
||||
const matchOptions = pong.computeMatchOptions(options);
|
||||
|
||||
const responseWhenGrantToken = fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/game/ticket`, {
|
||||
method : "POST",
|
||||
headers : {'Content-Type': 'application/json'},
|
||||
body : JSON.stringify({
|
||||
playerOneUsername : options.playerOneUsername,
|
||||
playerTwoUsername : options.playerTwoUsername,
|
||||
gameOptions : matchOptions,
|
||||
isGameIsWithInvitation : options.isSomeoneIsInvited
|
||||
})
|
||||
})
|
||||
const responseFromServer = await responseWhenGrantToken;
|
||||
const responseInjson = await responseFromServer.json();
|
||||
const token : string = responseInjson.token;
|
||||
showWaitPage = false;
|
||||
console.log("status : " + responseFromServer.status)
|
||||
if (responseFromServer.status != 200)
|
||||
{
|
||||
console.log(responseInjson)
|
||||
console.log("On refuse le ticket");
|
||||
errorMessageWhenAttemptingToGetATicket = responseInjson.message;
|
||||
showError = true;
|
||||
options.reset();
|
||||
options.playerOneUsername = user.username;
|
||||
setTimeout(() => {
|
||||
optionsAreNotSet = true
|
||||
showError = false;
|
||||
// showWaitPage = false // ???
|
||||
}, 5000);
|
||||
}
|
||||
else if (token)
|
||||
{
|
||||
idOfIntevalCheckTerminationOfTheMatch = setInterval(matchTermitation, 1000);
|
||||
// options.isInvitedPerson = false // ???
|
||||
pong.init(options, gameAreaId, token);
|
||||
hiddenGame = false;
|
||||
}
|
||||
// TODO: Un "else" peut-être ? Si pas de token on fait un truc ?
|
||||
// Si on ne rentre pas dans le else if, du coup il ne se passe rien.
|
||||
}
|
||||
|
||||
const initGameForInvitedPlayer = async(invitation : any) =>
|
||||
{
|
||||
optionsAreNotSet = false
|
||||
showWaitPage = true
|
||||
console.log("invitation : ")
|
||||
console.log(invitation)
|
||||
if (invitation.token)
|
||||
{
|
||||
idOfIntevalCheckTerminationOfTheMatch = setInterval(matchTermitation, 1000);
|
||||
options.playerOneUsername = invitation.playerOneUsername;
|
||||
options.playerTwoUsername = invitation.playerTwoUsername;
|
||||
options.isSomeoneIsInvited = true;
|
||||
options.isInvitedPerson = true
|
||||
pong.init(options, gameAreaId, invitation.token);
|
||||
showWaitPage = false
|
||||
hiddenGame = false;
|
||||
}
|
||||
}
|
||||
|
||||
const matchTermitation = () => {
|
||||
console.log("Ping matchTermitation")
|
||||
if (gameState.matchAbort || gameState.matchEnded)
|
||||
{
|
||||
clearInterval(idOfIntevalCheckTerminationOfTheMatch);
|
||||
console.log("matchTermitation was called")
|
||||
showWaitPage = false
|
||||
gameState.matchAbort ?
|
||||
errorMessageWhenAttemptingToGetATicket = "The match has been aborted"
|
||||
: errorMessageWhenAttemptingToGetATicket = "The match is finished !"
|
||||
gameState.matchAbort ? showError = true : showMatchEnded = true;
|
||||
setTimeout(() => {
|
||||
resetPage();
|
||||
errorMessageWhenAttemptingToGetATicket = "";
|
||||
isThereAnyInvitation = false;
|
||||
invitations = []; // ???
|
||||
console.log("matchTermitation : setTimeout")
|
||||
}, 5000);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const showOptions = () => {
|
||||
showGameOption = true
|
||||
showInvitations = false
|
||||
}
|
||||
|
||||
const showInvitation = async() => {
|
||||
showGameOption = false;
|
||||
showInvitations = true;
|
||||
invitations = await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/game/invitations`)
|
||||
.then(x => x.json())
|
||||
invitations.length !== 0 ? isThereAnyInvitation = true : isThereAnyInvitation = false
|
||||
}
|
||||
|
||||
const rejectInvitation = async(invitation) => {
|
||||
await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/game/decline`, {
|
||||
method: "POST",
|
||||
headers: { 'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({
|
||||
token : invitation.token
|
||||
})
|
||||
})
|
||||
.then(x => x.json())
|
||||
.catch(error => console.log(error))
|
||||
showInvitation()
|
||||
}
|
||||
|
||||
const acceptInvitation = async(invitation : any) => {
|
||||
const res = await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/game/accept`, {
|
||||
method: "POST",
|
||||
headers: { 'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({
|
||||
token : invitation.token
|
||||
})
|
||||
})
|
||||
.then(x => x.json())
|
||||
.catch(error => {
|
||||
console.log(error)
|
||||
})
|
||||
if (res.status === 200)
|
||||
{
|
||||
showInvitation()
|
||||
initGameForInvitedPlayer(invitation)
|
||||
}
|
||||
//Au final c'est utile !
|
||||
|
||||
initGameForInvitedPlayer(invitation) // Luke: normal de initGameForInvitedPlayer() sur un "res.status" different de 200 ?
|
||||
}
|
||||
|
||||
function leaveMatch() {
|
||||
resetPage();
|
||||
};
|
||||
|
||||
function resetPage() {
|
||||
hiddenGame = true;
|
||||
optionsAreNotSet = true
|
||||
showError = false;
|
||||
showMatchEnded = false;
|
||||
options.reset();
|
||||
options.playerOneUsername = user.username;
|
||||
pong.destroy();
|
||||
};
|
||||
|
||||
</script>
|
||||
|
||||
<Header />
|
||||
<!-- <div id="game_page"> Replacement for <body>.
|
||||
Might become useless after CSS rework. -->
|
||||
<div id="game_page">
|
||||
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="preload_font">.</div>
|
||||
|
||||
<div id="div_game_options">
|
||||
{#if showMatchEnded === true}
|
||||
<div id="div_game" in:fly="{{ y: 10, duration: 1000 }}">
|
||||
<p>{errorMessageWhenAttemptingToGetATicket}</p>
|
||||
</div>
|
||||
{/if}
|
||||
{#if showError === true}
|
||||
<div id="div_game" in:fly="{{ y: 10, duration: 1000 }}">
|
||||
<fieldset>
|
||||
<legend>game options</legend>
|
||||
<div>
|
||||
<input type="checkbox" id="multi_balls" name="multi_balls">
|
||||
<label for="multi_balls">multiples balls</label>
|
||||
</div>
|
||||
<div>
|
||||
<input type="checkbox" id="moving_walls" name="moving_walls">
|
||||
<label for="moving_walls">moving walls</label>
|
||||
</div>
|
||||
<div>
|
||||
<label>sound :</label>
|
||||
<input type="radio" id="sound_on" name="sound_selector" checked>
|
||||
<label for="sound_on">on</label>
|
||||
<input type="radio" id="sound_off" name="sound_selector">
|
||||
<label for="sound_off">off</label>
|
||||
</div>
|
||||
<div>
|
||||
<button id="play_pong_button">PLAY</button>
|
||||
</div>
|
||||
<legend>Error</legend>
|
||||
<p>{errorMessageWhenAttemptingToGetATicket}</p>
|
||||
</fieldset>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<div id="canvas_container">
|
||||
<!-- <p> =) </p> -->
|
||||
<div id="canvas_container" hidden={hiddenGame}>
|
||||
<canvas id={gameAreaId}/>
|
||||
</div>
|
||||
{#if !hiddenGame}
|
||||
<div id="div_game">
|
||||
<button id="pong_button" on:click={leaveMatch}>forfeit</button>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<script src="public/game/pong.js" type="module" defer></script>
|
||||
</body>
|
||||
|
||||
{#if showWaitPage === true}
|
||||
<div id="div_game" in:fly="{{ y: 10, duration: 1000 }}">
|
||||
<fieldset>
|
||||
<legend>Connecting to the game...</legend>
|
||||
<p>{waitingMessage}</p>
|
||||
</fieldset>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
|
||||
|
||||
{#if optionsAreNotSet}
|
||||
{#if showGameOption === true}
|
||||
<div id="game_option">
|
||||
<div id="div_game">
|
||||
<button id="pong_button" on:click={showInvitation}>Show invitations</button>
|
||||
<fieldset>
|
||||
<legend>game options</legend>
|
||||
<div>
|
||||
<input type="checkbox" id="multi_balls" name="multi_balls" bind:checked={options.multi_balls}>
|
||||
<label for="multi_balls">Multiples balls</label>
|
||||
</div>
|
||||
<div>
|
||||
<input type="checkbox" id="moving_walls" name="moving_walls" bind:checked={options.moving_walls}>
|
||||
<label for="moving_walls">Moving walls</label>
|
||||
</div>
|
||||
<div>
|
||||
<p>sound :</p>
|
||||
<input type="radio" id="sound_on" name="sound_selector" bind:group={options.sound} value="on">
|
||||
<label for="sound_on">on</label>
|
||||
<input type="radio" id="sound_off" name="sound_selector" bind:group={options.sound} value="off">
|
||||
<label for="sound_off">off</label>
|
||||
</div>
|
||||
<div>
|
||||
<input type="checkbox" id="isSomeoneIsInvited" bind:checked={options.isSomeoneIsInvited}>
|
||||
<label for="moving_walls">Invite a friend</label>
|
||||
</div>
|
||||
{#if options.isSomeoneIsInvited === true}
|
||||
<select bind:value={options.playerTwoUsername}>
|
||||
{#each allUsers as user }
|
||||
<option value={user.username}>{user.username}</option>
|
||||
{/each}
|
||||
</select>
|
||||
{/if}
|
||||
<div>
|
||||
<button id="pong_button" on:click={initGame}>PLAY</button>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if showInvitations}
|
||||
<div id="invitations_options" in:fly="{{ y: 10, duration: 1000 }}">
|
||||
<div id="div_game">
|
||||
<button id="pong_button" on:click={showOptions}>Play a Game</button>
|
||||
<fieldset>
|
||||
<legend>Current invitation(s)</legend>
|
||||
{#if isThereAnyInvitation}
|
||||
{#each invitations as invitation }
|
||||
<div>
|
||||
{invitation.playerOneUsername} has invited you to play a pong !
|
||||
<button id="pong_button" on:click={() => acceptInvitation(invitation)}>V</button>
|
||||
<button id="pong_button" on:click={() => rejectInvitation(invitation)}>X</button>
|
||||
</div>
|
||||
{/each}
|
||||
{/if}
|
||||
{#if isThereAnyInvitation === false}
|
||||
<p>Currently, no one asked to play with you.</p>
|
||||
<button id="pong_button" on:click={showInvitation}>Reload</button>
|
||||
{/if}
|
||||
</fieldset>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
</div> <!-- div "game_page" -->
|
||||
|
||||
<style>
|
||||
|
||||
@font-face {
|
||||
font-family: "Bit5x3";
|
||||
src: url("/fonts/Bit5x3.woff2") format("woff2"),local("Bit5x3"), url("/fonts/Bit5x3.woff") format("woff");
|
||||
src:
|
||||
url("/fonts/Bit5x3.woff2") format("woff2"),
|
||||
local("Bit5x3"),
|
||||
url("/fonts/Bit5x3.woff") format("woff");
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
#preload_font {
|
||||
font-family: "Bit5x3";
|
||||
opacity:0;
|
||||
height:0;
|
||||
width:0;
|
||||
display:inline-block;
|
||||
}
|
||||
body {
|
||||
#game_page {
|
||||
margin: 0;
|
||||
background-color: #222425;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
#canvas_container {
|
||||
margin-top: 20px;
|
||||
text-align: center;
|
||||
/* border: dashed rgb(245, 245, 245) 5px; */
|
||||
/* max-height: 80vh; */
|
||||
/* overflow: hidden; */
|
||||
}
|
||||
#div_game_options {
|
||||
text-align: center;
|
||||
font-family: "Bit5x3";
|
||||
color: rgb(245, 245, 245);
|
||||
font-size: x-large;
|
||||
}
|
||||
#div_game_options fieldset {
|
||||
max-width: 50vw;
|
||||
width: auto;
|
||||
margin: 0 auto;
|
||||
}
|
||||
#div_game_options fieldset div {
|
||||
padding: 10px;
|
||||
}
|
||||
#play_pong_button {
|
||||
font-family: "Bit5x3";
|
||||
color: rgb(245, 245, 245);
|
||||
background-color: #333333;
|
||||
font-size: x-large;
|
||||
padding: 10px;
|
||||
}
|
||||
canvas {
|
||||
/* background-color: #ff0000; */
|
||||
background-color: #333333;
|
||||
max-width: 75vw;
|
||||
/* max-height: 100vh; */
|
||||
width: 80%;
|
||||
}
|
||||
|
||||
#div_game {
|
||||
margin-top: 20px;
|
||||
text-align: center;
|
||||
font-family: "Bit5x3";
|
||||
color: rgb(245, 245, 245);
|
||||
font-size: x-large;
|
||||
}
|
||||
#div_game fieldset {
|
||||
max-width: 50vw;
|
||||
width: auto;
|
||||
margin: 0 auto;
|
||||
}
|
||||
#div_game fieldset div {
|
||||
padding: 10px;
|
||||
}
|
||||
#pong_button {
|
||||
font-family: "Bit5x3";
|
||||
color: rgb(245, 245, 245);
|
||||
background-color: #333333;
|
||||
font-size: x-large;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
#users_name { /* UNUSED */
|
||||
text-align: center;
|
||||
font-family: "Bit5x3";
|
||||
color: rgb(245, 245, 245);
|
||||
font-size: x-large;
|
||||
}
|
||||
#error_notification { /* UNUSED */
|
||||
text-align: center;
|
||||
display: block;
|
||||
font-family: "Bit5x3";
|
||||
color: rgb(143, 19, 19);
|
||||
font-size: x-large;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
@@ -0,0 +1,193 @@
|
||||
|
||||
<script lang="ts">
|
||||
import { onMount, onDestroy } from "svelte";
|
||||
import Header from '../../pieces/Header.svelte';
|
||||
import MatchListElem from "../../pieces/MatchListElem.svelte";
|
||||
import { fade, fly } from 'svelte/transition';
|
||||
|
||||
|
||||
import * as pongSpectator from "./client/pongSpectator";
|
||||
import { gameState } from "./client/ws";
|
||||
import { gameSessionIdPLACEHOLDER } from "./shared_js/constants";
|
||||
|
||||
//user's stuff
|
||||
let user;
|
||||
let allUsers;
|
||||
|
||||
//Game's stuff client side only
|
||||
const gameAreaId = "game_area";
|
||||
let sound = "off";
|
||||
// const dummyMatchList = [
|
||||
// {
|
||||
// gameSessionId: gameSessionIdPLACEHOLDER,
|
||||
// matchOptions: pongSpectator.MatchOptions.noOption,
|
||||
// playerOneUsername: "toto",
|
||||
// playerTwoUsername: "bruno",
|
||||
// },
|
||||
// {
|
||||
// gameSessionId: gameSessionIdPLACEHOLDER,
|
||||
// matchOptions: pongSpectator.MatchOptions.multiBalls,
|
||||
// playerOneUsername: "pl1",
|
||||
// playerTwoUsername: "pl2",
|
||||
// },
|
||||
// {
|
||||
// gameSessionId: "id6543",
|
||||
// matchOptions: pongSpectator.MatchOptions.movingWalls | pongSpectator.MatchOptions.multiBalls,
|
||||
// playerOneUsername: "bertand",
|
||||
// playerTwoUsername: "cassandre",
|
||||
// },
|
||||
// {
|
||||
// gameSessionId: "id3452",
|
||||
// matchOptions: pongSpectator.MatchOptions.multiBalls,
|
||||
// playerOneUsername: "madeleine",
|
||||
// playerTwoUsername: "jack",
|
||||
// },
|
||||
// ];
|
||||
let matchList = [];
|
||||
|
||||
//html boolean for pages
|
||||
let hiddenGame = true;
|
||||
let hiddenMatchList = false;
|
||||
|
||||
|
||||
onMount( async() => {
|
||||
user = await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/user`)
|
||||
.then( x => x.json() );
|
||||
allUsers = await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/user/all`)
|
||||
.then( x => x.json() );
|
||||
// WIP: fetch for match list here
|
||||
const responseForMatchList = await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/game/match/all`)
|
||||
const jsonForMatchList = await responseForMatchList.json();
|
||||
matchList = jsonForMatchList;
|
||||
console.log("matchList");
|
||||
if (matchList.length <= 0)
|
||||
hiddenMatchList = true;
|
||||
console.log(matchList);
|
||||
})
|
||||
|
||||
onDestroy( async() => {
|
||||
pongSpectator.destroy();
|
||||
})
|
||||
|
||||
async function initGameSpectator(gameSessionId: string, matchOptions: pongSpectator.MatchOptions) {
|
||||
pongSpectator.init(matchOptions, sound, gameAreaId, gameSessionId);
|
||||
hiddenGame = false;
|
||||
};
|
||||
|
||||
function leaveMatch() {
|
||||
resetPage();
|
||||
};
|
||||
|
||||
async function resetPage() {
|
||||
hiddenGame = true;
|
||||
pongSpectator.destroy();
|
||||
// WIP: fetch for match list here
|
||||
matchList = await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/game/match/all`)
|
||||
.then( x => x.json() );
|
||||
console.log("matchList");
|
||||
if (matchList.length <= 0)
|
||||
hiddenMatchList = true;
|
||||
console.log(matchList);
|
||||
};
|
||||
|
||||
</script>
|
||||
<!-- -->
|
||||
|
||||
<Header />
|
||||
<!-- <div id="game_page"> Replacement for <body>.
|
||||
Might become useless after CSS rework. -->
|
||||
<div id="game_page">
|
||||
|
||||
<div id="canvas_container" hidden={hiddenGame}>
|
||||
<canvas id={gameAreaId}/>
|
||||
</div>
|
||||
|
||||
{#if hiddenGame}
|
||||
<div id="div_game">
|
||||
<div id="game_options">
|
||||
<fieldset>
|
||||
{#if hiddenMatchList}
|
||||
<legend>no match available</legend>
|
||||
{:else}
|
||||
<legend>options</legend>
|
||||
<div>
|
||||
<p>sound :</p>
|
||||
<input type="radio" id="sound_on" name="sound_selector" bind:group={sound} value="on">
|
||||
<label for="sound_on">on</label>
|
||||
<input type="radio" id="sound_off" name="sound_selector" bind:group={sound} value="off">
|
||||
<label for="sound_off">off</label>
|
||||
</div>
|
||||
<menu id="match_list">
|
||||
{#each matchList as match}
|
||||
<MatchListElem match={match} on:click={(e) => initGameSpectator(match.gameServerIdOfTheMatch, match.gameOptions)} />
|
||||
{/each}
|
||||
</menu>
|
||||
{/if}
|
||||
</fieldset>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
{:else}
|
||||
<div id="div_game">
|
||||
<button id="pong_button" on:click={leaveMatch}>leave match</button>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
</div> <!-- div "game_page" -->
|
||||
|
||||
<!-- -->
|
||||
<style>
|
||||
@font-face {
|
||||
font-family: "Bit5x3";
|
||||
src:
|
||||
url("/fonts/Bit5x3.woff2") format("woff2"),
|
||||
local("Bit5x3"),
|
||||
url("/fonts/Bit5x3.woff") format("woff");
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
#game_page {
|
||||
margin: 0;
|
||||
background-color: #222425;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
#canvas_container {
|
||||
margin-top: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
canvas {
|
||||
background-color: #333333;
|
||||
max-width: 75vw;
|
||||
width: 80%;
|
||||
}
|
||||
|
||||
#div_game {
|
||||
margin-top: 20px;
|
||||
text-align: center;
|
||||
font-family: "Bit5x3";
|
||||
color: rgb(245, 245, 245);
|
||||
font-size: x-large;
|
||||
}
|
||||
#div_game fieldset {
|
||||
max-width: 50vw;
|
||||
width: auto;
|
||||
margin: 0 auto;
|
||||
}
|
||||
#div_game fieldset div {
|
||||
padding: 10px;
|
||||
}
|
||||
#match_list {
|
||||
font-family: 'Courier New', Courier, monospace;
|
||||
font-size: large;
|
||||
}
|
||||
#pong_button {
|
||||
font-family: "Bit5x3";
|
||||
color: rgb(245, 245, 245);
|
||||
background-color: #333333;
|
||||
font-size: x-large;
|
||||
padding: 10px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,74 @@
|
||||
|
||||
<script lang="ts">
|
||||
import { onMount, onDestroy } from "svelte";
|
||||
import Header from "../../pieces/Header.svelte";
|
||||
|
||||
|
||||
//user's stuff
|
||||
let currentUser;
|
||||
let allUsers = [];
|
||||
let idInterval;
|
||||
onMount( async() => {
|
||||
currentUser = await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/user`)
|
||||
.then( x => x.json() );
|
||||
allUsers = await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/game/ranking`)
|
||||
.then( x => x.json() );
|
||||
idInterval = setInterval(fetchScores, 10000);
|
||||
})
|
||||
|
||||
onDestroy( async() => {
|
||||
clearInterval(idInterval);
|
||||
})
|
||||
|
||||
function fetchScores() {
|
||||
fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/game/ranking`)
|
||||
.then( x => x.json() )
|
||||
.then( x => allUsers = x );
|
||||
}
|
||||
</script>
|
||||
<Header />
|
||||
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<h1>Ranking</h1>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">#</th>
|
||||
<th scope="col">Username</th>
|
||||
<th scope="col">Win</th>
|
||||
<th scope="col">Lose</th>
|
||||
<th scope="col">Draw</th>
|
||||
<th scope="col">Games Played</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{#each allUsers as user, i}
|
||||
<tr>
|
||||
<th scope="row">{i + 1}</th>
|
||||
{#if user.username === currentUser.username}
|
||||
<td><b>You ({user.username})</b></td>
|
||||
{:else}
|
||||
<td>{user.username}</td>
|
||||
{/if}
|
||||
<td>{user.stats.winGame}</td>
|
||||
<td>{user.stats.loseGame}</td>
|
||||
<td>{user.stats.drawGame}</td>
|
||||
<td>{user.stats.totalGame}</td>
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,27 @@
|
||||
|
||||
import * as c from "./constants.js"
|
||||
|
||||
// export const soundPongArr: HTMLAudioElement[] = [];
|
||||
export const soundPongArr: HTMLAudioElement[] = [
|
||||
new Audio("http://" + process.env.WEBSITE_HOST + ":" + process.env.WEBSITE_PORT + "/sound/pong/"+1+".ogg"),
|
||||
new Audio("http://" + process.env.WEBSITE_HOST + ":" + process.env.WEBSITE_PORT + "/sound/pong/"+2+".ogg")
|
||||
];
|
||||
export const soundRoblox = new Audio("http://" + process.env.WEBSITE_HOST + ":" + process.env.WEBSITE_PORT + "/sound/roblox-oof.ogg");
|
||||
|
||||
export function initAudio(sound: string)
|
||||
{
|
||||
let muteFlag: boolean;
|
||||
if (sound === "on") {
|
||||
muteFlag = false;
|
||||
}
|
||||
else {
|
||||
muteFlag = true;
|
||||
}
|
||||
|
||||
soundPongArr.forEach((value) => {
|
||||
value.volume = c.soundRobloxVolume;
|
||||
value.muted = muteFlag;
|
||||
});
|
||||
soundRoblox.volume = c.soundRobloxVolume;
|
||||
soundRoblox.muted = muteFlag;
|
||||
}
|
||||
@@ -1,21 +1,25 @@
|
||||
|
||||
import * as c from ".././constants.js"
|
||||
|
||||
class GameArea {
|
||||
export class GameArea {
|
||||
keys: string[] = [];
|
||||
handleInputInterval: number = 0;
|
||||
gameLoopInterval: number = 0;
|
||||
drawLoopInterval: number = 0;
|
||||
canvas: HTMLCanvasElement;
|
||||
ctx: CanvasRenderingContext2D;
|
||||
constructor() {
|
||||
this.canvas = document.createElement("canvas");
|
||||
constructor(canvas_id: string) {
|
||||
const canvas = document.getElementById("game_area");
|
||||
if (canvas && canvas instanceof HTMLCanvasElement) {
|
||||
this.canvas = canvas;
|
||||
}
|
||||
else {
|
||||
console.log("GameArea init error, invalid canvas_id");
|
||||
return;
|
||||
}
|
||||
this.ctx = this.canvas.getContext("2d") as CanvasRenderingContext2D;
|
||||
this.canvas.width = c.CanvasWidth;
|
||||
this.canvas.height = c.CanvasWidth / c.CanvasRatio;
|
||||
let container = document.getElementById("canvas_container");
|
||||
if (container)
|
||||
container.insertBefore(this.canvas, container.childNodes[0]);
|
||||
}
|
||||
addKey(key: string) {
|
||||
key = key.toLowerCase();
|
||||
@@ -34,5 +38,3 @@ class GameArea {
|
||||
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
|
||||
}
|
||||
}
|
||||
|
||||
export {GameArea}
|
||||
@@ -5,7 +5,7 @@ import { Vector, VectorInteger } from "../../shared_js/class/Vector.js";
|
||||
import { TextElem, TextNumericValue } from "./Text.js";
|
||||
import { RectangleClient, MovingRectangleClient, RacketClient, BallClient, Line } from "./RectangleClient.js";
|
||||
import { GameComponents } from "../../shared_js/class/GameComponents.js";
|
||||
import { MovingRectangle } from "../../shared_js/class/Rectangle.js";
|
||||
import type { MovingRectangle } from "../../shared_js/class/Rectangle.js";
|
||||
|
||||
class GameComponentsExtensionForClient extends GameComponents {
|
||||
wallTop: RectangleClient | MovingRectangleClient;
|
||||
@@ -62,12 +62,13 @@ class GameComponentsExtensionForClient extends GameComponents {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class GameComponentsClient extends GameComponentsExtensionForClient {
|
||||
export class GameComponentsClient extends GameComponentsExtensionForClient {
|
||||
midLine: Line;
|
||||
scoreLeft: TextNumericValue;
|
||||
scoreRight: TextNumericValue;
|
||||
text1: TextElem;
|
||||
text2: TextElem;
|
||||
text3: TextElem;
|
||||
|
||||
w_grid_mid: RectangleClient;
|
||||
w_grid_u1: RectangleClient;
|
||||
@@ -90,6 +91,8 @@ class GameComponentsClient extends GameComponentsExtensionForClient {
|
||||
// Text
|
||||
pos.assign(0, c.h_mid);
|
||||
this.text1 = new TextElem(pos, Math.floor(c.w/8), ctx, "white");
|
||||
this.text2 = new TextElem(pos, Math.floor(c.w/24), ctx, "white");
|
||||
this.text3 = new TextElem(pos, Math.floor(c.w/24), ctx, "white");
|
||||
|
||||
// Dotted Midline
|
||||
pos.assign(c.w_mid-c.midLineSize/2, 0+c.wallSize);
|
||||
@@ -110,5 +113,3 @@ class GameComponentsClient extends GameComponentsExtensionForClient {
|
||||
this.h_grid_d1 = new RectangleClient(pos, c.gridSize, c.h, ctx, "darkgreen");
|
||||
}
|
||||
}
|
||||
|
||||
export {GameComponentsClient}
|
||||
@@ -0,0 +1,19 @@
|
||||
|
||||
export class InitOptions {
|
||||
sound = "off";
|
||||
multi_balls = false;
|
||||
moving_walls = false;
|
||||
isSomeoneIsInvited = false;
|
||||
isInvitedPerson = false;
|
||||
playerOneUsername = "";
|
||||
playerTwoUsername = "";
|
||||
reset() {
|
||||
this.sound = "off";
|
||||
this.multi_balls = false;
|
||||
this.moving_walls = false;
|
||||
this.isSomeoneIsInvited = false;
|
||||
this.isInvitedPerson = false;
|
||||
this.playerOneUsername = "";
|
||||
this.playerTwoUsername = "";
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
|
||||
import * as en from "../../shared_js/enums.js"
|
||||
import * as ev from "../../shared_js/class/Event.js"
|
||||
import type * as en from "../../shared_js/enums.js"
|
||||
import type * as ev from "../../shared_js/class/Event.js"
|
||||
|
||||
class InputHistory {
|
||||
export class InputHistory {
|
||||
input: en.InputEnum;
|
||||
id: number;
|
||||
deltaTime: number;
|
||||
@@ -12,5 +12,3 @@ class InputHistory {
|
||||
this.deltaTime = deltaTime;
|
||||
}
|
||||
}
|
||||
|
||||
export {InputHistory}
|
||||
@@ -1,6 +1,6 @@
|
||||
|
||||
import { Vector, VectorInteger } from "../../shared_js/class/Vector.js";
|
||||
import { Component, GraphicComponent, Moving } from "../../shared_js/class/interface.js";
|
||||
import type { GraphicComponent } from "../../shared_js/class/interface.js";
|
||||
import { Rectangle, MovingRectangle, Racket, Ball } from "../../shared_js/class/Rectangle.js";
|
||||
import { soundPongArr } from "../audio.js"
|
||||
import { random } from "../utils.js";
|
||||
@@ -17,7 +17,7 @@ function clearRectangle(this: RectangleClient, pos?: VectorInteger) {
|
||||
this.ctx.clearRect(this.pos.x, this.pos.y, this.width, this.height);
|
||||
}
|
||||
|
||||
class RectangleClient extends Rectangle implements GraphicComponent {
|
||||
export class RectangleClient extends Rectangle implements GraphicComponent {
|
||||
ctx: CanvasRenderingContext2D;
|
||||
color: string;
|
||||
update: () => void;
|
||||
@@ -31,19 +31,9 @@ class RectangleClient extends Rectangle implements GraphicComponent {
|
||||
this.update = updateRectangle;
|
||||
this.clear = clearRectangle;
|
||||
}
|
||||
// update() {
|
||||
// this.ctx.fillStyle = this.color;
|
||||
// this.ctx.fillRect(this.pos.x, this.pos.y, this.width, this.height);
|
||||
// }
|
||||
// clear(pos?: VectorInteger) {
|
||||
// if (pos)
|
||||
// this.ctx.clearRect(pos.x, pos.y, this.width, this.height);
|
||||
// else
|
||||
// this.ctx.clearRect(this.pos.x, this.pos.y, this.width, this.height);
|
||||
// }
|
||||
}
|
||||
|
||||
class MovingRectangleClient extends MovingRectangle implements GraphicComponent {
|
||||
export class MovingRectangleClient extends MovingRectangle implements GraphicComponent {
|
||||
ctx: CanvasRenderingContext2D;
|
||||
color: string;
|
||||
update: () => void;
|
||||
@@ -59,7 +49,7 @@ class MovingRectangleClient extends MovingRectangle implements GraphicComponent
|
||||
}
|
||||
}
|
||||
|
||||
class RacketClient extends Racket implements GraphicComponent {
|
||||
export class RacketClient extends Racket implements GraphicComponent {
|
||||
ctx: CanvasRenderingContext2D;
|
||||
color: string;
|
||||
update: () => void;
|
||||
@@ -75,7 +65,7 @@ class RacketClient extends Racket implements GraphicComponent {
|
||||
}
|
||||
}
|
||||
|
||||
class BallClient extends Ball implements GraphicComponent {
|
||||
export class BallClient extends Ball implements GraphicComponent {
|
||||
ctx: CanvasRenderingContext2D;
|
||||
color: string;
|
||||
update: () => void;
|
||||
@@ -91,12 +81,10 @@ class BallClient extends Ball implements GraphicComponent {
|
||||
}
|
||||
bounce(collider?: Rectangle) {
|
||||
this._bounceAlgo(collider);
|
||||
soundPongArr[ Math.floor(random(0, soundPongArr.length)) ].play();
|
||||
let i = Math.floor(random(0, soundPongArr.length));
|
||||
soundPongArr[ i ].play();
|
||||
console.log(`sound_i=${i}`); // debug log
|
||||
}
|
||||
/* protected _bounceRacket(collider: Racket) {
|
||||
this._bounceRacketAlgo(collider);
|
||||
soundRoblox.play();
|
||||
} */
|
||||
}
|
||||
|
||||
function updateLine(this: Line) {
|
||||
@@ -105,17 +93,20 @@ function updateLine(this: Line) {
|
||||
let i = 0;
|
||||
while (i < this.segmentCount)
|
||||
{
|
||||
// for Horizontal Line
|
||||
/* Horizontal Line */
|
||||
// pos.y = this.pos.y;
|
||||
// pos.x = this.pos.x + this.segmentWidth * i;
|
||||
|
||||
/* Vertical Line */
|
||||
pos.x = this.pos.x;
|
||||
pos.y = this.pos.y + this.segmentHeight * i;
|
||||
|
||||
this.ctx.fillRect(pos.x, pos.y, this.segmentWidth, this.segmentHeight);
|
||||
i += 2;
|
||||
}
|
||||
}
|
||||
|
||||
class Line extends RectangleClient {
|
||||
export class Line extends RectangleClient {
|
||||
gapeCount: number = 0;
|
||||
segmentCount: number;
|
||||
segmentWidth: number;
|
||||
@@ -129,13 +120,12 @@ class Line extends RectangleClient {
|
||||
this.gapeCount = gapeCount;
|
||||
this.segmentCount = this.gapeCount * 2 + 1;
|
||||
|
||||
/* Vertical Line */
|
||||
this.segmentWidth = this.width;
|
||||
this.segmentHeight = this.height / this.segmentCount;
|
||||
|
||||
// for Horizontal Line
|
||||
/* Horizontal Line */
|
||||
// this.segmentWidth = this.width / this.segmentCount;
|
||||
// this.segmentHeight = this.height;
|
||||
}
|
||||
}
|
||||
|
||||
export {RectangleClient, MovingRectangleClient, RacketClient, BallClient, Line}
|
||||
@@ -1,9 +1,9 @@
|
||||
|
||||
import { Vector, VectorInteger } from "../../shared_js/class/Vector.js";
|
||||
import { Component } from "../../shared_js/class/interface.js";
|
||||
import type { Component } from "../../shared_js/class/interface.js";
|
||||
|
||||
// conflict with Text
|
||||
class TextElem implements Component {
|
||||
export class TextElem implements Component {
|
||||
ctx: CanvasRenderingContext2D;
|
||||
pos: VectorInteger;
|
||||
color: string;
|
||||
@@ -39,7 +39,7 @@ class TextElem implements Component {
|
||||
}
|
||||
}
|
||||
|
||||
class TextNumericValue extends TextElem {
|
||||
export class TextNumericValue extends TextElem {
|
||||
private _value: number = 0;
|
||||
constructor(pos: VectorInteger, size: number,
|
||||
ctx: CanvasRenderingContext2D, color: string, font?: string)
|
||||
@@ -54,5 +54,3 @@ class TextNumericValue extends TextElem {
|
||||
this.text = v.toString();
|
||||
}
|
||||
}
|
||||
|
||||
export {TextElem, TextNumericValue}
|
||||
@@ -1,10 +1,8 @@
|
||||
|
||||
import { pong, gc } from "./global.js"
|
||||
import * as c from "./constants.js"
|
||||
import * as en from "../shared_js/enums.js"
|
||||
import { gridDisplay } from "./handleInput.js";
|
||||
|
||||
function drawLoop()
|
||||
export function drawLoop()
|
||||
{
|
||||
pong.clear();
|
||||
|
||||
@@ -15,6 +13,8 @@ function drawLoop()
|
||||
drawStatic();
|
||||
|
||||
gc.text1.update();
|
||||
gc.text2.update();
|
||||
gc.text3.update();
|
||||
|
||||
drawDynamic();
|
||||
}
|
||||
@@ -47,5 +47,3 @@ function drawGrid()
|
||||
gc.h_grid_u1.update();
|
||||
gc.h_grid_d1.update();
|
||||
}
|
||||
|
||||
export {drawLoop}
|
||||
@@ -0,0 +1,72 @@
|
||||
|
||||
import * as c from "./constants.js";
|
||||
import * as en from "../shared_js/enums.js"
|
||||
import { gc, matchOptions } from "./global.js";
|
||||
import { clientInfo, clientInfoSpectator} from "./ws.js";
|
||||
import { wallsMovements } from "../shared_js/wallsMovement.js";
|
||||
import type { RacketClient } from "./class/RectangleClient.js";
|
||||
import type { VectorInteger } from "../shared_js/class/Vector.js";
|
||||
|
||||
let actual_time: number = Date.now();
|
||||
let last_time: number;
|
||||
let delta_time: number;
|
||||
|
||||
export function gameLoop()
|
||||
{
|
||||
/* last_time = actual_time;
|
||||
actual_time = Date.now();
|
||||
delta_time = (actual_time - last_time) / 1000; */
|
||||
|
||||
delta_time = c.fixedDeltaTime;
|
||||
// console.log(`delta_gameLoop: ${delta_time}`);
|
||||
|
||||
// interpolation
|
||||
// console.log(`dir.y: ${clientInfo.opponent.dir.y}, pos.y: ${clientInfo.opponent.pos.y}, opponentNextPos.y: ${clientInfo.opponentNextPos.y}`);
|
||||
if (clientInfo.opponent.dir.y != 0 ) {
|
||||
racketInterpolation(delta_time, clientInfo.opponent, clientInfo.opponentNextPos);
|
||||
}
|
||||
|
||||
// client prediction
|
||||
gc.ballsArr.forEach((ball) => {
|
||||
ball.moveAndBounce(delta_time, [gc.wallTop, gc.wallBottom, gc.playerLeft, gc.playerRight]);
|
||||
});
|
||||
|
||||
if (matchOptions & en.MatchOptions.movingWalls) {
|
||||
wallsMovements(delta_time, gc);
|
||||
}
|
||||
}
|
||||
|
||||
export function gameLoopSpectator()
|
||||
{
|
||||
delta_time = c.fixedDeltaTime;
|
||||
|
||||
// interpolation
|
||||
if (gc.playerLeft.dir.y != 0 ) {
|
||||
racketInterpolation(delta_time, gc.playerLeft, clientInfoSpectator.playerLeftNextPos);
|
||||
}
|
||||
if (gc.playerRight.dir.y != 0 ) {
|
||||
racketInterpolation(delta_time, gc.playerRight, clientInfoSpectator.playerRightNextPos);
|
||||
}
|
||||
|
||||
// client prediction
|
||||
gc.ballsArr.forEach((ball) => {
|
||||
ball.moveAndBounce(delta_time, [gc.wallTop, gc.wallBottom, gc.playerLeft, gc.playerRight]);
|
||||
});
|
||||
|
||||
if (matchOptions & en.MatchOptions.movingWalls) {
|
||||
wallsMovements(delta_time, gc);
|
||||
}
|
||||
}
|
||||
|
||||
function racketInterpolation(delta: number, racket: RacketClient, nextPos: VectorInteger)
|
||||
{
|
||||
// interpolation
|
||||
racket.moveAndCollide(delta, [gc.wallTop, gc.wallBottom]);
|
||||
|
||||
if ((racket.dir.y > 0 && racket.pos.y > nextPos.y)
|
||||
|| (racket.dir.y < 0 && racket.pos.y < nextPos.y))
|
||||
{
|
||||
racket.dir.y = 0;
|
||||
racket.pos.y = nextPos.y;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
|
||||
import * as en from "../shared_js/enums.js";
|
||||
import type { GameArea } from "./class/GameArea.js";
|
||||
import type { GameComponentsClient } from "./class/GameComponentsClient.js";
|
||||
|
||||
export let pong: GameArea;
|
||||
export let gc: GameComponentsClient;
|
||||
export let matchOptions: en.MatchOptions = en.MatchOptions.noOption;
|
||||
|
||||
export function setPong(value: GameArea) {
|
||||
pong = value;
|
||||
}
|
||||
|
||||
export function setGc(value: GameComponentsClient) {
|
||||
gc = value;
|
||||
}
|
||||
|
||||
export function setMatchOptions(value: en.MatchOptions) {
|
||||
matchOptions = value;
|
||||
}
|
||||
|
||||
export let startFunction: () => void;
|
||||
|
||||
export function setStartFunction(value: () => void) {
|
||||
startFunction = value;
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
|
||||
import { pong, gc, socket, clientInfo } from "./global.js"
|
||||
import { pong, gc } from "./global.js"
|
||||
import { socket, clientInfo, gameState } from "./ws.js"
|
||||
import * as ev from "../shared_js/class/Event.js"
|
||||
import * as en from "../shared_js/enums.js"
|
||||
import { InputHistory } from "./class/InputHistory.js"
|
||||
@@ -20,7 +21,7 @@ const inputHistoryArr: InputHistory[] = [];
|
||||
socket.send(JSON.stringify(inputState));
|
||||
} */
|
||||
|
||||
function handleInput()
|
||||
export function handleInput()
|
||||
{
|
||||
/* last_time = actual_time;
|
||||
actual_time = Date.now();
|
||||
@@ -43,7 +44,9 @@ function handleInput()
|
||||
playerMovements(delta_time, keys);
|
||||
}
|
||||
|
||||
socket.send(JSON.stringify(inputState));
|
||||
if (!gameState.matchEnded) {
|
||||
socket.send(JSON.stringify(inputState));
|
||||
}
|
||||
// setTimeout(testInputDelay, 100);
|
||||
inputHistoryArr.push(new InputHistory(inputState, delta_time));
|
||||
|
||||
@@ -86,7 +89,7 @@ function playerMovePrediction(delta: number, input: en.InputEnum)
|
||||
racket.moveAndCollide(delta, [gc.wallTop, gc.wallBottom]);
|
||||
}
|
||||
|
||||
function repeatInput(lastInputId: number)
|
||||
export function repeatInput(lastInputId: number)
|
||||
{
|
||||
// server reconciliation
|
||||
let i = inputHistoryArr.findIndex((value: InputHistory) => {
|
||||
@@ -106,5 +109,3 @@ function repeatInput(lastInputId: number)
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export {handleInput, repeatInput}
|
||||
@@ -0,0 +1,48 @@
|
||||
|
||||
import * as c from "./constants.js"
|
||||
import * as en from "../shared_js/enums.js"
|
||||
import { GameArea } from "./class/GameArea.js";
|
||||
import { GameComponentsClient } from "./class/GameComponentsClient.js";
|
||||
import { socket, resetGameState } from "./ws.js";
|
||||
import { initAudio } from "./audio.js";
|
||||
import type { InitOptions } from "./class/InitOptions.js";
|
||||
|
||||
import { pong } from "./global.js"
|
||||
import { setPong, setGc, setMatchOptions } from "./global.js"
|
||||
|
||||
export function computeMatchOptions(options: InitOptions)
|
||||
{
|
||||
let matchOptions = en.MatchOptions.noOption;
|
||||
|
||||
if (options.multi_balls === true) {
|
||||
matchOptions |= en.MatchOptions.multiBalls
|
||||
}
|
||||
if (options.moving_walls === true) {
|
||||
matchOptions |= en.MatchOptions.movingWalls
|
||||
}
|
||||
|
||||
return matchOptions;
|
||||
}
|
||||
|
||||
export function initBase(matchOptions: en.MatchOptions, sound: string, gameAreaId: string)
|
||||
{
|
||||
initAudio(sound);
|
||||
setMatchOptions(matchOptions);
|
||||
setPong(new GameArea(gameAreaId));
|
||||
setGc(new GameComponentsClient(matchOptions, pong.ctx));
|
||||
}
|
||||
|
||||
export function destroyBase()
|
||||
{
|
||||
if (pong)
|
||||
{
|
||||
clearInterval(pong.handleInputInterval);
|
||||
clearInterval(pong.gameLoopInterval);
|
||||
clearInterval(pong.drawLoopInterval);
|
||||
setPong(null);
|
||||
}
|
||||
if (socket && socket.OPEN) {
|
||||
socket.close();
|
||||
}
|
||||
resetGameState();
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
|
||||
import * as c from "./constants.js"
|
||||
import { gc, pong } from "./global.js"
|
||||
import * as en from "../shared_js/enums.js"
|
||||
|
||||
/*
|
||||
before game
|
||||
*/
|
||||
export function error(message: string)
|
||||
{
|
||||
console.log("msg.error()");
|
||||
pong.clear();
|
||||
const text = "error: " + message;
|
||||
console.log(text);
|
||||
gc.text2.clear();
|
||||
gc.text2.pos.assign(c.w*0.2, c.h*0.5);
|
||||
gc.text2.text = text;
|
||||
gc.text2.update();
|
||||
}
|
||||
|
||||
export function matchmaking()
|
||||
{
|
||||
const text = "searching...";
|
||||
console.log(text);
|
||||
gc.text1.clear();
|
||||
gc.text1.pos.assign(c.w*0.2, c.h*0.5);
|
||||
gc.text1.text = text;
|
||||
gc.text1.update();
|
||||
}
|
||||
|
||||
export function matchmakingComplete()
|
||||
{
|
||||
const text = "match found !";
|
||||
console.log(text);
|
||||
gc.text1.clear();
|
||||
gc.text1.pos.assign(c.w*0.15, c.h*0.5);
|
||||
gc.text1.text = text;
|
||||
gc.text1.update();
|
||||
}
|
||||
|
||||
export function matchAbort()
|
||||
{
|
||||
const text = "match abort";
|
||||
console.log(text);
|
||||
gc.text1.clear();
|
||||
gc.text1.pos.assign(c.w*0.15, c.h*0.5);
|
||||
gc.text1.text = text;
|
||||
gc.text1.update();
|
||||
|
||||
setTimeout(() => {
|
||||
gc.text2.pos.assign(c.w*0.44, c.h*0.6);
|
||||
gc.text2.text = "pardon =(";
|
||||
const oriSize = gc.text2.size;
|
||||
gc.text2.size = c.w*0.025;
|
||||
gc.text2.update();
|
||||
gc.text2.size = oriSize;
|
||||
}, 2500);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
in game
|
||||
*/
|
||||
export function win()
|
||||
{
|
||||
gc.text1.pos.assign(c.w*0.415, c.h*0.5);
|
||||
gc.text1.text = "WIN";
|
||||
}
|
||||
|
||||
export function lose()
|
||||
{
|
||||
gc.text1.pos.assign(c.w*0.383, c.h*0.5);
|
||||
gc.text1.text = "LOSE";
|
||||
}
|
||||
|
||||
export function forfeit(playerSide: en.PlayerSide)
|
||||
{
|
||||
if (playerSide === en.PlayerSide.left) {
|
||||
gc.text2.pos.assign(c.w*0.65, c.h*0.42);
|
||||
gc.text3.pos.assign(c.w*0.65, c.h*0.52);
|
||||
}
|
||||
else {
|
||||
gc.text2.pos.assign(c.w*0.09, c.h*0.42);
|
||||
gc.text3.pos.assign(c.w*0.09, c.h*0.52);
|
||||
}
|
||||
setTimeout(() => {
|
||||
gc.text2.text = "par forfait";
|
||||
}, 1500);
|
||||
setTimeout(() => {
|
||||
gc.text3.text = "calme ta joie";
|
||||
}, 3500);
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
|
||||
import * as c from "./constants.js"
|
||||
import { handleInput } from "./handleInput.js";
|
||||
import { gameLoop } from "./gameLoop.js"
|
||||
import { drawLoop } from "./draw.js";
|
||||
import { countdown } from "./utils.js";
|
||||
import { initWebSocket } from "./ws.js";
|
||||
import type { InitOptions } from "./class/InitOptions.js";
|
||||
export { InitOptions } from "./class/InitOptions.js";
|
||||
import { initBase, destroyBase, computeMatchOptions } from "./init.js";
|
||||
export { computeMatchOptions } from "./init.js";
|
||||
|
||||
/* TODO: A way to delay the init of variables, but still use "const" not "let" ? */
|
||||
import { pong, gc } from "./global.js"
|
||||
import { setStartFunction } from "./global.js"
|
||||
|
||||
let abortControllerKeydown: AbortController;
|
||||
let abortControllerKeyup: AbortController;
|
||||
|
||||
export function init(options: InitOptions, gameAreaId: string, token: string)
|
||||
{
|
||||
const matchOptions = computeMatchOptions(options);
|
||||
initBase(matchOptions, options.sound, gameAreaId);
|
||||
|
||||
setStartFunction(start);
|
||||
if (options.isSomeoneIsInvited) {
|
||||
initWebSocket(matchOptions, token, options.playerOneUsername, true, options.playerTwoUsername, options.isInvitedPerson);
|
||||
}
|
||||
else {
|
||||
initWebSocket(matchOptions, token, options.playerOneUsername);
|
||||
}
|
||||
}
|
||||
|
||||
export function destroy()
|
||||
{
|
||||
destroyBase();
|
||||
if (abortControllerKeydown) {
|
||||
abortControllerKeydown.abort();
|
||||
abortControllerKeydown = null;
|
||||
}
|
||||
if (abortControllerKeyup) {
|
||||
abortControllerKeyup.abort();
|
||||
abortControllerKeyup = null;
|
||||
}
|
||||
}
|
||||
|
||||
function start()
|
||||
{
|
||||
gc.text1.pos.assign(c.w*0.5, c.h*0.75);
|
||||
countdown(c.matchStartDelay/1000, (count: number) => {
|
||||
gc.text1.clear();
|
||||
gc.text1.text = `${count}`;
|
||||
gc.text1.update();
|
||||
}, start_after_countdown);
|
||||
}
|
||||
|
||||
function start_after_countdown()
|
||||
{
|
||||
abortControllerKeydown = new AbortController();
|
||||
window.addEventListener(
|
||||
'keydown',
|
||||
(e) => { pong.addKey(e.key); },
|
||||
{signal: abortControllerKeydown.signal}
|
||||
);
|
||||
|
||||
abortControllerKeyup = new AbortController();
|
||||
window.addEventListener(
|
||||
'keyup',
|
||||
(e) => { pong.deleteKey(e.key);},
|
||||
{signal: abortControllerKeyup.signal}
|
||||
);
|
||||
|
||||
resume();
|
||||
}
|
||||
|
||||
function resume()
|
||||
{
|
||||
gc.text1.text = "";
|
||||
pong.handleInputInterval = window.setInterval(handleInput, c.handleInputIntervalMS);
|
||||
// pong.handleInputInterval = window.setInterval(sendLoop, c.sendLoopIntervalMS);
|
||||
pong.gameLoopInterval = window.setInterval(gameLoop, c.gameLoopIntervalMS);
|
||||
pong.drawLoopInterval = window.setInterval(drawLoop, c.drawLoopIntervalMS);
|
||||
}
|
||||
|
||||
function pause() // unused
|
||||
{
|
||||
clearInterval(pong.handleInputInterval);
|
||||
clearInterval(pong.gameLoopInterval);
|
||||
clearInterval(pong.drawLoopInterval);
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
|
||||
import * as c from "./constants.js"
|
||||
import type * as en from "../shared_js/enums.js"
|
||||
import { gameLoopSpectator } from "./gameLoop.js"
|
||||
import { drawLoop } from "./draw.js";
|
||||
import { initWebSocketSpectator } from "./ws.js";
|
||||
import { initBase, destroyBase, computeMatchOptions } from "./init.js";
|
||||
export { computeMatchOptions } from "./init.js";
|
||||
export { MatchOptions } from "../shared_js/enums.js"
|
||||
|
||||
/* TODO: A way to delay the init of variables, but still use "const" not "let" ? */
|
||||
import { pong, gc } from "./global.js"
|
||||
import { setStartFunction } from "./global.js"
|
||||
|
||||
|
||||
export function init(matchOptions: en.MatchOptions, sound: string, gameAreaId: string, gameSessionId: string)
|
||||
{
|
||||
initBase(matchOptions, sound, gameAreaId);
|
||||
|
||||
setStartFunction(start);
|
||||
initWebSocketSpectator(gameSessionId);
|
||||
}
|
||||
|
||||
export function destroy()
|
||||
{
|
||||
destroyBase();
|
||||
}
|
||||
|
||||
function start()
|
||||
{
|
||||
resume();
|
||||
}
|
||||
|
||||
function resume()
|
||||
{
|
||||
pong.gameLoopInterval = window.setInterval(gameLoopSpectator, c.gameLoopIntervalMS);
|
||||
pong.drawLoopInterval = window.setInterval(drawLoop, c.drawLoopIntervalMS);
|
||||
}
|
||||
|
||||
function pause() // unused
|
||||
{
|
||||
clearInterval(pong.gameLoopInterval);
|
||||
clearInterval(pong.drawLoopInterval);
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
export * from "../shared_js/utils.js"
|
||||
|
||||
function countdown(count: number, callback?: (count: number) => void, endCallback?: () => void)
|
||||
export function countdown(count: number, callback?: (count: number) => void, endCallback?: () => void)
|
||||
{
|
||||
console.log("countdown ", count);
|
||||
if (count > 0) {
|
||||
@@ -14,5 +14,3 @@ function countdown(count: number, callback?: (count: number) => void, endCallbac
|
||||
endCallback();
|
||||
}
|
||||
}
|
||||
|
||||
export {countdown}
|
||||
@@ -1,15 +1,25 @@
|
||||
|
||||
import * as c from "./constants.js"
|
||||
import { gc, matchOptions } from "./global.js"
|
||||
import { gc, matchOptions, startFunction } from "./global.js"
|
||||
import * as ev from "../shared_js/class/Event.js"
|
||||
import * as en from "../shared_js/enums.js"
|
||||
import { matchmaking, matchmakingComplete, startGame } from "./pong.js";
|
||||
import { RacketClient } from "./class/RectangleClient.js";
|
||||
import * as msg from "./message.js";
|
||||
import type { RacketClient } from "./class/RectangleClient.js";
|
||||
import { repeatInput } from "./handleInput.js";
|
||||
import { soundRoblox } from "./audio.js"
|
||||
import { sleep } from "./utils.js";
|
||||
import { Vector, VectorInteger } from "../shared_js/class/Vector.js";
|
||||
|
||||
export const gameState = {
|
||||
matchEnded: false,
|
||||
matchAbort: false
|
||||
}
|
||||
|
||||
export function resetGameState() {
|
||||
gameState.matchEnded = false;
|
||||
gameState.matchAbort = false;
|
||||
}
|
||||
|
||||
class ClientInfo {
|
||||
id = "";
|
||||
side: en.PlayerSide;
|
||||
@@ -18,18 +28,34 @@ class ClientInfo {
|
||||
opponentNextPos: VectorInteger;
|
||||
}
|
||||
|
||||
const wsPort = 8042;
|
||||
const wsUrl = "ws://" + document.location.hostname + ":" + wsPort + "/pong";
|
||||
class ClientInfoSpectator {
|
||||
// side: en.PlayerSide;
|
||||
/* WIP: playerLeftNextPos and playerRightNextPos could be in clientInfo for simplicity */
|
||||
playerLeftNextPos: VectorInteger;
|
||||
playerRightNextPos: VectorInteger;
|
||||
}
|
||||
|
||||
const wsUrl = "ws://" + process.env.WEBSITE_HOST + ":" + process.env.WEBSITE_PORT + "/pong";
|
||||
export let socket: WebSocket; /* TODO: A way to still use "const" not "let" ? */
|
||||
export const clientInfo = new ClientInfo();
|
||||
export const clientInfoSpectator = new ClientInfoSpectator(); // WIP, could refactor this
|
||||
|
||||
export function initWebSocket(options: en.MatchOptions)
|
||||
|
||||
export function initWebSocket(options: en.MatchOptions, token: string, username: string, privateMatch = false, playerTwoUsername?: string, isInvitedPerson? : boolean)
|
||||
{
|
||||
socket = new WebSocket(wsUrl, "json");
|
||||
console.log("Infos from ws.ts : options => " + options + " token => " + token + " username => " + username + " priavte match => " + privateMatch
|
||||
+ " player two => " + playerTwoUsername)
|
||||
socket.addEventListener("open", (event) => {
|
||||
socket.send(JSON.stringify( new ev.ClientAnnounce(en.ClientRole.player, options, clientInfo.id) ));
|
||||
if (privateMatch) {
|
||||
socket.send(JSON.stringify( new ev.ClientAnnouncePlayer(options, token, username, privateMatch, playerTwoUsername, isInvitedPerson) ));
|
||||
}
|
||||
else {
|
||||
socket.send(JSON.stringify( new ev.ClientAnnouncePlayer(options, token, username) ));
|
||||
}
|
||||
});
|
||||
// socket.addEventListener("message", logListener); // for testing purpose
|
||||
socket.addEventListener("message", errorListener);
|
||||
socket.addEventListener("message", preMatchListener);
|
||||
}
|
||||
|
||||
@@ -37,6 +63,14 @@ function logListener(this: WebSocket, event: MessageEvent) {
|
||||
console.log("%i: " + event.data, Date.now());
|
||||
}
|
||||
|
||||
function errorListener(this: WebSocket, event: MessageEvent) {
|
||||
const data: ev.ServerEvent = JSON.parse(event.data);
|
||||
if (data.type === en.EventTypes.error) {
|
||||
console.log("actual Error");
|
||||
msg.error((data as ev.EventError).message);
|
||||
}
|
||||
}
|
||||
|
||||
function preMatchListener(this: WebSocket, event: MessageEvent)
|
||||
{
|
||||
const data: ev.ServerEvent = JSON.parse(event.data);
|
||||
@@ -45,7 +79,7 @@ function preMatchListener(this: WebSocket, event: MessageEvent)
|
||||
clientInfo.id = (<ev.EventAssignId>data).id;
|
||||
break;
|
||||
case en.EventTypes.matchmakingInProgress:
|
||||
matchmaking();
|
||||
msg.matchmaking();
|
||||
break;
|
||||
case en.EventTypes.matchmakingComplete:
|
||||
clientInfo.side = (<ev.EventMatchmakingComplete>data).side;
|
||||
@@ -62,17 +96,22 @@ function preMatchListener(this: WebSocket, event: MessageEvent)
|
||||
clientInfo.opponentNextPos = new VectorInteger(clientInfo.opponent.pos.x, clientInfo.opponent.pos.y);
|
||||
clientInfo.racket.color = "darkgreen"; // for testing purpose
|
||||
socket.send(JSON.stringify( new ev.ClientEvent(en.EventTypes.clientPlayerReady) )); // TODO: set an interval/timeout to resend until matchStart response (in case of network problem)
|
||||
matchmakingComplete();
|
||||
msg.matchmakingComplete();
|
||||
break;
|
||||
case en.EventTypes.matchStart:
|
||||
socket.removeEventListener("message", preMatchListener);
|
||||
socket.addEventListener("message", inGameListener);
|
||||
startGame();
|
||||
startFunction();
|
||||
break;
|
||||
case en.EventTypes.matchAbort:
|
||||
gameState.matchAbort = true;
|
||||
socket.removeEventListener("message", preMatchListener);
|
||||
msg.matchAbort();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function inGameListener(event: MessageEvent)
|
||||
function inGameListener(this: WebSocket, event: MessageEvent)
|
||||
{
|
||||
const data: ev.ServerEvent = JSON.parse(event.data);
|
||||
switch (data.type) {
|
||||
@@ -168,15 +207,125 @@ function scoreUpdate(data: ev.EventScoreUpdate)
|
||||
|
||||
function matchEnd(data: ev.EventMatchEnd)
|
||||
{
|
||||
gameState.matchEnded = true;
|
||||
socket.close();
|
||||
if (data.winner === clientInfo.side) {
|
||||
gc.text1.pos.assign(c.w*0.415, c.h_mid);
|
||||
gc.text1.text = "WIN";
|
||||
msg.win();
|
||||
if (data.forfeit) {
|
||||
msg.forfeit(clientInfo.side);
|
||||
}
|
||||
}
|
||||
else {
|
||||
gc.text1.pos.assign(c.w*0.383, c.h_mid);
|
||||
gc.text1.text = "LOSE";
|
||||
msg.lose();
|
||||
}
|
||||
// matchEnded = true;
|
||||
}
|
||||
|
||||
// export let matchEnded = false;
|
||||
/* Spectator */
|
||||
|
||||
export function initWebSocketSpectator(gameSessionId: string)
|
||||
{
|
||||
socket = new WebSocket(wsUrl, "json");
|
||||
socket.addEventListener("open", (event) => {
|
||||
socket.send(JSON.stringify( new ev.ClientAnnounceSpectator(gameSessionId) ));
|
||||
});
|
||||
// socket.addEventListener("message", logListener); // for testing purpose
|
||||
socket.addEventListener("message", errorListener);
|
||||
socket.addEventListener("message", preMatchListenerSpectator);
|
||||
|
||||
clientInfoSpectator.playerLeftNextPos = new VectorInteger(gc.playerLeft.pos.x, gc.playerLeft.pos.y);
|
||||
clientInfoSpectator.playerRightNextPos = new VectorInteger(gc.playerRight.pos.x, gc.playerRight.pos.y);
|
||||
|
||||
}
|
||||
|
||||
export function preMatchListenerSpectator(this: WebSocket, event: MessageEvent)
|
||||
{
|
||||
const data: ev.ServerEvent = JSON.parse(event.data);
|
||||
if (data.type === en.EventTypes.matchStart)
|
||||
{
|
||||
socket.removeEventListener("message", preMatchListenerSpectator);
|
||||
socket.addEventListener("message", inGameListenerSpectator);
|
||||
socket.send(JSON.stringify( new ev.ClientEvent(en.EventTypes.clientSpectatorReady) ));
|
||||
startFunction();
|
||||
}
|
||||
}
|
||||
|
||||
function inGameListenerSpectator(this: WebSocket, event: MessageEvent)
|
||||
{
|
||||
const data: ev.ServerEvent = JSON.parse(event.data);
|
||||
switch (data.type) {
|
||||
case en.EventTypes.gameUpdate:
|
||||
gameUpdateSpectator(data as ev.EventGameUpdate);
|
||||
break;
|
||||
case en.EventTypes.scoreUpdate:
|
||||
scoreUpdateSpectator(data as ev.EventScoreUpdate);
|
||||
break;
|
||||
case en.EventTypes.matchEnd:
|
||||
matchEndSpectator(data as ev.EventMatchEnd);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function gameUpdateSpectator(data: ev.EventGameUpdate)
|
||||
{
|
||||
console.log("gameUpdateSpectator");
|
||||
|
||||
if (matchOptions & en.MatchOptions.movingWalls) {
|
||||
gc.wallTop.pos.y = data.wallTop.y;
|
||||
gc.wallBottom.pos.y = data.wallBottom.y;
|
||||
}
|
||||
|
||||
data.ballsArr.forEach((ball, i) => {
|
||||
gc.ballsArr[i].pos.assign(ball.x, ball.y);
|
||||
gc.ballsArr[i].dir.assign(ball.dirX, ball.dirY);
|
||||
gc.ballsArr[i].speed = ball.speed;
|
||||
});
|
||||
|
||||
// interpolation
|
||||
for (const racket of [gc.playerLeft, gc.playerRight])
|
||||
{
|
||||
let nextPos: VectorInteger;
|
||||
if (racket === gc.playerLeft) {
|
||||
nextPos = clientInfoSpectator.playerLeftNextPos;
|
||||
}
|
||||
else {
|
||||
nextPos = clientInfoSpectator.playerRightNextPos;
|
||||
}
|
||||
|
||||
racket.pos.assign(nextPos.x, nextPos.y);
|
||||
if (racket === gc.playerLeft) {
|
||||
nextPos.assign(racket.pos.x, data.playerLeft.y);
|
||||
}
|
||||
else {
|
||||
nextPos.assign(racket.pos.x, data.playerRight.y);
|
||||
}
|
||||
|
||||
racket.dir = new Vector(
|
||||
nextPos.x - racket.pos.x,
|
||||
nextPos.y - racket.pos.y
|
||||
);
|
||||
|
||||
if (Math.abs(racket.dir.x) + Math.abs(racket.dir.y) !== 0) {
|
||||
racket.dir = racket.dir.normalized();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function scoreUpdateSpectator(data: ev.EventScoreUpdate)
|
||||
{
|
||||
console.log("scoreUpdateSpectator");
|
||||
gc.scoreLeft.value = data.scoreLeft;
|
||||
gc.scoreRight.value = data.scoreRight;
|
||||
}
|
||||
|
||||
function matchEndSpectator(data: ev.EventMatchEnd)
|
||||
{
|
||||
console.log("matchEndSpectator");
|
||||
gameState.matchEnded = true;
|
||||
socket.close();
|
||||
// WIP
|
||||
/* msg.win();
|
||||
if (data.forfeit) {
|
||||
msg.forfeit(clientInfo.side);
|
||||
} */
|
||||
}
|
||||
|
||||
@@ -0,0 +1,144 @@
|
||||
|
||||
import * as en from "../enums.js"
|
||||
|
||||
/* From Server */
|
||||
export class ServerEvent {
|
||||
type: en.EventTypes;
|
||||
constructor(type: en.EventTypes = 0) {
|
||||
this.type = type;
|
||||
}
|
||||
}
|
||||
|
||||
export class EventAssignId extends ServerEvent {
|
||||
id: string;
|
||||
constructor(id: string) {
|
||||
super(en.EventTypes.assignId);
|
||||
this.id = id;
|
||||
}
|
||||
}
|
||||
|
||||
export class EventMatchmakingComplete extends ServerEvent {
|
||||
side: en.PlayerSide;
|
||||
constructor(side: en.PlayerSide) {
|
||||
super(en.EventTypes.matchmakingComplete);
|
||||
this.side = side;
|
||||
}
|
||||
}
|
||||
|
||||
export class EventGameUpdate extends ServerEvent {
|
||||
playerLeft = {
|
||||
y: 0
|
||||
};
|
||||
playerRight = {
|
||||
y: 0
|
||||
};
|
||||
ballsArr: {
|
||||
x: number,
|
||||
y: number,
|
||||
dirX: number,
|
||||
dirY: number,
|
||||
speed: number
|
||||
}[] = [];
|
||||
wallTop? = {
|
||||
y: 0
|
||||
};
|
||||
wallBottom? = {
|
||||
y: 0
|
||||
};
|
||||
lastInputId = 0;
|
||||
constructor() { // TODO: constructor that take GameComponentsServer maybe ?
|
||||
super(en.EventTypes.gameUpdate);
|
||||
}
|
||||
}
|
||||
|
||||
export class EventScoreUpdate extends ServerEvent {
|
||||
scoreLeft: number;
|
||||
scoreRight: number;
|
||||
constructor(scoreLeft: number, scoreRight: number) {
|
||||
super(en.EventTypes.scoreUpdate);
|
||||
this.scoreLeft = scoreLeft;
|
||||
this.scoreRight = scoreRight;
|
||||
}
|
||||
}
|
||||
|
||||
export class EventMatchEnd extends ServerEvent {
|
||||
winner: en.PlayerSide;
|
||||
forfeit: boolean;
|
||||
constructor(winner: en.PlayerSide, forfeit = false) {
|
||||
super(en.EventTypes.matchEnd);
|
||||
this.winner = winner;
|
||||
this.forfeit = forfeit;
|
||||
}
|
||||
}
|
||||
|
||||
export class EventMatchAbort extends ServerEvent {
|
||||
constructor() {
|
||||
super(en.EventTypes.matchAbort);
|
||||
}
|
||||
}
|
||||
|
||||
export class EventError extends ServerEvent {
|
||||
message: string;
|
||||
constructor(message: string) {
|
||||
super(en.EventTypes.error);
|
||||
this.message = message;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* From Client */
|
||||
export class ClientEvent {
|
||||
type: en.EventTypes; // readonly ?
|
||||
constructor(type: en.EventTypes = 0) {
|
||||
this.type = type;
|
||||
}
|
||||
}
|
||||
|
||||
export class ClientAnnounce extends ClientEvent {
|
||||
role: en.ClientRole;
|
||||
constructor(role: en.ClientRole) {
|
||||
super(en.EventTypes.clientAnnounce);
|
||||
this.role = role;
|
||||
}
|
||||
}
|
||||
|
||||
export class ClientAnnouncePlayer extends ClientAnnounce {
|
||||
clientId: string; // unused
|
||||
matchOptions: en.MatchOptions;
|
||||
token: string;
|
||||
username: string;
|
||||
privateMatch: boolean;
|
||||
playerTwoUsername?: string;
|
||||
isInvitedPerson? : boolean;
|
||||
constructor(matchOptions: en.MatchOptions, token: string, username: string, privateMatch: boolean = false, playerTwoUsername?: string, isInvitedPerson? : boolean) {
|
||||
super(en.ClientRole.player);
|
||||
this.matchOptions = matchOptions;
|
||||
this.token = token;
|
||||
this.username = username;
|
||||
this.privateMatch = privateMatch;
|
||||
if (isInvitedPerson) {
|
||||
this.isInvitedPerson = isInvitedPerson;
|
||||
}
|
||||
if (playerTwoUsername) {
|
||||
this.playerTwoUsername = playerTwoUsername;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class ClientAnnounceSpectator extends ClientAnnounce {
|
||||
gameSessionId: string;
|
||||
constructor(gameSessionId: string) {
|
||||
super(en.ClientRole.spectator);
|
||||
this.gameSessionId = gameSessionId;
|
||||
}
|
||||
}
|
||||
|
||||
export class EventInput extends ClientEvent {
|
||||
input: en.InputEnum;
|
||||
id: number;
|
||||
constructor(input: en.InputEnum = en.InputEnum.noInput, id: number = 0) {
|
||||
super(en.EventTypes.clientInput);
|
||||
this.input = input;
|
||||
this.id = id;
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@ import { VectorInteger } from "./Vector.js";
|
||||
import { Rectangle, MovingRectangle, Racket, Ball } from "./Rectangle.js";
|
||||
import { random } from "../utils.js";
|
||||
|
||||
class GameComponents {
|
||||
export class GameComponents {
|
||||
wallTop: Rectangle | MovingRectangle;
|
||||
wallBottom: Rectangle | MovingRectangle;
|
||||
playerLeft: Racket;
|
||||
@@ -61,5 +61,3 @@ class GameComponents {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export {GameComponents}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user