Initial commit
This commit is contained in:
2
.gitattributes
vendored
Normal file
2
.gitattributes
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
# Auto detect text files and perform LF normalization
|
||||
* text=auto
|
||||
130
.gitignore
vendored
Normal file
130
.gitignore
vendored
Normal file
@@ -0,0 +1,130 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
.pnpm-debug.log*
|
||||
|
||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
*.lcov
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
bower_components
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# Snowpack dependency directory (https://snowpack.dev/)
|
||||
web_modules/
|
||||
|
||||
# TypeScript cache
|
||||
*.tsbuildinfo
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Optional stylelint cache
|
||||
.stylelintcache
|
||||
|
||||
# Microbundle cache
|
||||
.rpt2_cache/
|
||||
.rts2_cache_cjs/
|
||||
.rts2_cache_es/
|
||||
.rts2_cache_umd/
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variable files
|
||||
.env
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
.env.local
|
||||
|
||||
# parcel-bundler cache (https://parceljs.org/)
|
||||
.cache
|
||||
.parcel-cache
|
||||
|
||||
# Next.js build output
|
||||
.next
|
||||
out
|
||||
|
||||
# Nuxt.js build / generate output
|
||||
.nuxt
|
||||
dist
|
||||
|
||||
# Gatsby files
|
||||
.cache/
|
||||
# Comment in the public line in if your project uses Gatsby and not Next.js
|
||||
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||
# public
|
||||
|
||||
# vuepress build output
|
||||
.vuepress/dist
|
||||
|
||||
# vuepress v2.x temp and cache directory
|
||||
.temp
|
||||
.cache
|
||||
|
||||
# Docusaurus cache and generated files
|
||||
.docusaurus
|
||||
|
||||
# Serverless directories
|
||||
.serverless/
|
||||
|
||||
# FuseBox cache
|
||||
.fusebox/
|
||||
|
||||
# DynamoDB Local files
|
||||
.dynamodb/
|
||||
|
||||
# TernJS port file
|
||||
.tern-port
|
||||
|
||||
# Stores VSCode versions used for testing VSCode extensions
|
||||
.vscode-test
|
||||
|
||||
# yarn v2
|
||||
.yarn/cache
|
||||
.yarn/unplugged
|
||||
.yarn/build-state.yml
|
||||
.yarn/install-state.gz
|
||||
.pnp.*
|
||||
32
Speicher.txt
Normal file
32
Speicher.txt
Normal file
@@ -0,0 +1,32 @@
|
||||
Set-ExecutionPolicy RemoteSigned -Scope CurrentUser
|
||||
|
||||
|
||||
|
||||
<script src="/socket.io/socket.io.js"></script>
|
||||
|
||||
|
||||
|
||||
/**@param {} x*/
|
||||
/**@typeof {} x*/
|
||||
|
||||
|
||||
https://codedamn.com/news/nodejs/use-json-web-token-jwt-in-nodejs
|
||||
|
||||
|
||||
<div class="screen">
|
||||
<nav>
|
||||
<h1>Spiel-Code:</h1>
|
||||
<h1 class="message">1A3B5C</h1>
|
||||
</nav>
|
||||
|
||||
<div id="playerList">
|
||||
<div id="player1" class="player">
|
||||
<h1>Spieler 1</h1>
|
||||
<h2>test</h2>
|
||||
</div>
|
||||
<div id="player2" class="player">
|
||||
<h1>Spieler 2</h1>
|
||||
<h2>test</h2>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
2311
backend/package-lock.json
generated
Normal file
2311
backend/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
26
backend/package.json
Normal file
26
backend/package.json
Normal file
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"name": "doublesnake_backend",
|
||||
"version": "1.0.0",
|
||||
"description": "Eine Snake Game welches man zu zweit Spielen kann!",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"dev": "node ./src/server.js",
|
||||
"nm": "nodemon ./src/server.js"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"bcrypt": "^5.1.1",
|
||||
"body-parser": "^1.20.3",
|
||||
"dotenv": "^16.4.7",
|
||||
"express": "^4.21.2",
|
||||
"express-session": "^1.18.1",
|
||||
"mysql2": "^3.12.0",
|
||||
"socket.io": "^4.8.1",
|
||||
"socket.io-client": "^4.8.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"nodemon": "^3.1.9"
|
||||
}
|
||||
}
|
||||
23
backend/src/Database/DataBaseManager.js
Normal file
23
backend/src/Database/DataBaseManager.js
Normal file
@@ -0,0 +1,23 @@
|
||||
const mySql = require("mysql2");
|
||||
const UserManager = require("./UserManager/UserManager");
|
||||
|
||||
class DataBaseManager {
|
||||
constructor(host, database) {
|
||||
this.connection = mySql.createConnection({
|
||||
host: host,
|
||||
user: "root",
|
||||
password:"",
|
||||
database: database
|
||||
});
|
||||
|
||||
this.connection.connect((err) => {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
});
|
||||
|
||||
this.usermanager = new UserManager(this.connection);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = DataBaseManager;
|
||||
27
backend/src/Database/UserManager/User.js
Normal file
27
backend/src/Database/UserManager/User.js
Normal file
@@ -0,0 +1,27 @@
|
||||
const bcrypt = require("bcrypt");
|
||||
|
||||
class User {
|
||||
constructor(userJSON) {
|
||||
this.id = userJSON.id;
|
||||
this.username = userJSON.username;
|
||||
this.email = userJSON.email;
|
||||
this.password = userJSON.password;
|
||||
this.fullName = userJSON.fullName;
|
||||
this.createdAt = userJSON.createdAt;
|
||||
}
|
||||
|
||||
async doesPassMatch(passwordPlain) {
|
||||
return await bcrypt.compare(passwordPlain, this.password);
|
||||
}
|
||||
|
||||
toUserJSON(){
|
||||
return {
|
||||
id: this.id,
|
||||
username: this.username,
|
||||
email: this.email,
|
||||
fullName: this.fullName,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = User;
|
||||
102
backend/src/Database/UserManager/UserManager.js
Normal file
102
backend/src/Database/UserManager/UserManager.js
Normal file
@@ -0,0 +1,102 @@
|
||||
const mySql = require("mysql2")
|
||||
const bcrypt = require("bcrypt")
|
||||
const User = require("./User")
|
||||
|
||||
class UserManager {
|
||||
/**@param {mySql.Connection} connection*/
|
||||
constructor(connection) {
|
||||
this.connection = connection;
|
||||
}
|
||||
|
||||
// Gebe zumindest {username: "x", password: "x"} mit!
|
||||
// 0: Fehler (User Existiert schon oder Fehler) | 1: Funktioniert (User Erstellt!)
|
||||
async createUser(userJson) {
|
||||
if (!userJson.username || !userJson.password) return 0;
|
||||
|
||||
const user = await this.getUser({username: userJson.username});
|
||||
if(user) return 0;
|
||||
|
||||
try {
|
||||
const passHash = await bcrypt.hash(userJson.password, 10);
|
||||
this.connection.query(`INSERT INTO users (username, email, password, fullName) VALUES ("${userJson.username}",` +
|
||||
` ${userJson.email ? `"${userJson.email}"` : "NULL"}, "${passHash}", ${userJson.fullName ? `"${userJson.fullName}"` : "NULL"})`);
|
||||
return 1;
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
|
||||
// Gebe nichts mit um alle user zu bekommen oder {id: 1} oder {username: "x"} um darauf einen bestimmten zu bekommen;
|
||||
async getUser(userData) {
|
||||
let selectionString = "SELECT * FROM users";
|
||||
|
||||
if(userData?.id){
|
||||
selectionString = `SELECT * FROM users WHERE id=${userData.id}`;
|
||||
}
|
||||
else if(userData?.username){
|
||||
selectionString = `SELECT * FROM users WHERE username="${userData.username}"`;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await this.connection.promise().query(selectionString);
|
||||
|
||||
if(response[0].length === 0){
|
||||
return null;
|
||||
}
|
||||
else if(response[0].length === 1){
|
||||
const x = response[0][0];
|
||||
return new User(x);
|
||||
}
|
||||
else{
|
||||
let userArray = [];
|
||||
|
||||
response[0].forEach(x => {
|
||||
const user = new User(x);
|
||||
|
||||
userArray.push(user);
|
||||
});
|
||||
|
||||
return userArray;
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
|
||||
// 0: User Existiert nicht | 1: Funktioniert
|
||||
// id = 1 | newUserData = {email: "x", password: "xx", "fullName": "xxx"} <-- BRaucht alle Argeumente sonst null, username nicht änderbar
|
||||
async updateUser(id, newUserData){
|
||||
const user = await this.getUser({id});
|
||||
if(!user) return 0;
|
||||
if(!newUserData.password) return 0;
|
||||
|
||||
try {
|
||||
this.connection.query(
|
||||
`UPDATE users SET
|
||||
username="${newUserData.username}",
|
||||
email=${newUserData.email ? `"${newUserData.email}"` : "NULL"},
|
||||
fullName=${newUserData.fullName ? `"${newUserData.fullName}"` : "NULL"},
|
||||
password="${newUserData.password}"
|
||||
WHERE id = ${id}`
|
||||
)
|
||||
return 1;
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
}
|
||||
|
||||
// 0: Es gibt den User nicht | 1: Funktioniert
|
||||
async delteUser(id){
|
||||
const user = await this.getUser({id});
|
||||
if(!user) return 0;
|
||||
|
||||
try {
|
||||
this.connection.query(`DELETE FROM users where id=${id}`);
|
||||
return 1;
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = UserManager;
|
||||
68
backend/src/Express/ExpressManager.js
Normal file
68
backend/src/Express/ExpressManager.js
Normal file
@@ -0,0 +1,68 @@
|
||||
const express = require("express");
|
||||
const sessions = require("express-session");
|
||||
const bp = require("body-parser");
|
||||
const path = require("path");
|
||||
const DataBaseManager = require("../Database/DataBaseManager");
|
||||
const AccountRoute = require("./Routes/AccountRoute");
|
||||
const DashboardRoute = require("./Routes/DashboardRoute");
|
||||
|
||||
class ExpressManager{
|
||||
/**@param {DataBaseManager} dbManager*/
|
||||
constructor(dbManager){
|
||||
this.db = dbManager;
|
||||
|
||||
this.app = express();
|
||||
|
||||
this.sessionMiddleware = sessions({
|
||||
secret: process.env.SESSION_KEY,
|
||||
saveUninitialized: false,
|
||||
resave: false,
|
||||
cookie: {maxAge: 24 * 60 * 60 * 1000}
|
||||
});
|
||||
|
||||
this.app.use((req, res, next) => { this.logger(req, res, next) });
|
||||
this.app.use(this.sessionMiddleware);
|
||||
this.app.use((req, res, next) => { this.needAuth(req, res, next) });
|
||||
this.app.use(bp.urlencoded({extended: false}));
|
||||
this.app.use(express.json());
|
||||
this.app.use(bp.json());
|
||||
this.app.use(express.static(path.join(__dirname, "./../../../frontend")));
|
||||
|
||||
// Routen wo man für Angemeldet sein muss
|
||||
this.authRoutes = [
|
||||
"/api/dashboard",
|
||||
"/dashboard"
|
||||
];
|
||||
|
||||
// Routen Einbinden
|
||||
this.app.use("/api/account/", new AccountRoute(this.db).router);
|
||||
this.app.use("/api/dashboard/", new DashboardRoute(this.db).router);
|
||||
}
|
||||
|
||||
/**@param {express.Request} req @param {express.Response} res @param {express.NextFunction} next*/
|
||||
logger(req, res, next){
|
||||
const date = new Date();
|
||||
console.log(`${date.toTimeString().slice(0, 8)} | ${req.method} | ${req.url}`);
|
||||
next();
|
||||
}
|
||||
|
||||
/**@param {express.Request} req @param {express.Response} res @param {express.NextFunction} next*/
|
||||
needAuth(req, res, next){
|
||||
let isProtectedRoute = false;
|
||||
this.authRoutes.forEach(route => {
|
||||
if(req.url.startsWith(route)) isProtectedRoute = true;
|
||||
});
|
||||
|
||||
// Geht zum Login wenn User versucht Routen aufzurufen wofür man angemeldet sein muss
|
||||
if(isProtectedRoute && !req.session.user?.isSet) return res.redirect("/login");
|
||||
|
||||
// Geht zum Dashboard wenn der Nutzer versucht sich zu registrieren oder einzuloggen wenn er angemeldet ist
|
||||
if(req.session.user?.isSet && (req.url.startsWith("/login") || req.url.startsWith("/register"))){
|
||||
return res.redirect("/dashboard");
|
||||
}
|
||||
|
||||
next();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ExpressManager;
|
||||
84
backend/src/Express/Routes/AccountRoute.js
Normal file
84
backend/src/Express/Routes/AccountRoute.js
Normal file
@@ -0,0 +1,84 @@
|
||||
const express = require("express");
|
||||
const User = require("../../Database/UserManager/User");
|
||||
const DataBaseManager = require("../../Database/DataBaseManager");
|
||||
const bcrypt = require("bcrypt");
|
||||
|
||||
class AccountRoute {
|
||||
/**@param {DataBaseManager} dbManager*/
|
||||
constructor(dbManager) {
|
||||
this.router = express.Router();
|
||||
|
||||
this.db = dbManager;
|
||||
|
||||
this.router.post("/register", async (req, res) => await this.register(req, res));
|
||||
this.router.post("/login", async (req, res) => await this.login(req, res));
|
||||
this.router.post("/update", async (req, res) => await this.update(req, res));
|
||||
this.router.get("/logout", async (req, res) => await this.logout(req, res));
|
||||
}
|
||||
|
||||
/**@param {express.Request} req @param {express.Response}*/
|
||||
async register(req, res){
|
||||
const body = req.body;
|
||||
if(!body.username || !body.password) return res.redirect("/register?error=1");
|
||||
|
||||
const user = new User(body);
|
||||
|
||||
const result = await this.db.usermanager.createUser(user);
|
||||
if(result !== 1) return res.redirect("/register?error=2");
|
||||
|
||||
res.redirect("/login");
|
||||
}
|
||||
|
||||
/**@param {express.Request} req @param {express.Response}*/
|
||||
async login(req, res){
|
||||
const body = req.body;
|
||||
if(!body.username || !body.password) return res.redirect("/login?error=1")
|
||||
|
||||
const user = await this.db.usermanager.getUser({username: body.username});
|
||||
if(!user) return res.redirect("/login?error=2")
|
||||
|
||||
const passwordMatch = await user.doesPassMatch(body.password);
|
||||
if(!passwordMatch) return res.redirect("/login?error=2")
|
||||
|
||||
req.session.user = {
|
||||
id: user.id,
|
||||
username: user.username,
|
||||
isSet: true
|
||||
}
|
||||
|
||||
res.redirect("/dashboard")
|
||||
}
|
||||
|
||||
/**@param {express.Request} req @param {express.Response}*/
|
||||
async update(req, res){
|
||||
const user = await this.db.usermanager.getUser({id: req.session.user.id});
|
||||
const body = req.body;
|
||||
|
||||
if(user.username !== body.username){
|
||||
const checkUser = await this.db.usermanager.getUser({username: body.username});
|
||||
if(checkUser) return res.redirect("/dashboard/account/?error=1");
|
||||
|
||||
user.username = body.username;
|
||||
}
|
||||
|
||||
user.email = body.email;
|
||||
user.fullName = body.fullName;
|
||||
|
||||
if(body.password !== ""){
|
||||
const passHash = await bcrypt.hash(body.password, 10);
|
||||
user.password = passHash;
|
||||
}
|
||||
|
||||
await this.db.usermanager.updateUser(user.id, user);
|
||||
|
||||
res.redirect("/dashboard/account");
|
||||
}
|
||||
|
||||
/**@param {express.Request} req @param {express.Response}*/
|
||||
async logout(req, res){
|
||||
req.session.destroy();
|
||||
res.redirect("/");
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = AccountRoute;
|
||||
23
backend/src/Express/Routes/DashboardRoute.js
Normal file
23
backend/src/Express/Routes/DashboardRoute.js
Normal file
@@ -0,0 +1,23 @@
|
||||
const express = require("express");
|
||||
const User = require("../../Database/UserManager/User");
|
||||
const DataBaseManager = require("../../Database/DataBaseManager");
|
||||
|
||||
class DashboardRoute{
|
||||
/**@param {DataBaseManager} dbManager*/
|
||||
constructor(dbManager){
|
||||
this.router = express.Router();
|
||||
|
||||
this.db = dbManager;
|
||||
|
||||
this.router.get("/", async (req, res) => await this.userInfo(req, res))
|
||||
}
|
||||
|
||||
/**@param {express.Request} req @param {express.Response}*/
|
||||
async userInfo(req, res){
|
||||
const user = await this.db.usermanager.getUser({id: req.session.user.id});
|
||||
|
||||
res.json(user.toUserJSON());
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = DashboardRoute;
|
||||
50
backend/src/SocketIO/LobbyManager/Classes/Lobby.js
Normal file
50
backend/src/SocketIO/LobbyManager/Classes/Lobby.js
Normal file
@@ -0,0 +1,50 @@
|
||||
const socketIO = require("socket.io");
|
||||
const SocketUser = require("./SocketUser");
|
||||
|
||||
class Lobby {
|
||||
/** @param {socketIO.Server} io @param {Map<number, SocketUser>} users*/
|
||||
constructor(io, code) {
|
||||
this.io = io;
|
||||
this.code = code;
|
||||
/** @type {Array<SocketUser>} */
|
||||
this.users = [];
|
||||
}
|
||||
|
||||
addUser(user) {
|
||||
if (this.users.length >= 2) return 1;
|
||||
|
||||
this.users.push(user);
|
||||
|
||||
this.sendLobbyUserUpdate();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
removeUser(user) {
|
||||
const index = this.users.findIndex(u => u.id === user.id);
|
||||
|
||||
if (index === -1) return 1;
|
||||
|
||||
this.users.splice(index, 1);
|
||||
|
||||
this.sendLobbyUserUpdate();
|
||||
|
||||
if (this.users.length === 0) return 2;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
sendLobbyUserUpdate() {
|
||||
const data = {
|
||||
code: this.code,
|
||||
users: this.users.map(user => ({
|
||||
id: user.id,
|
||||
username: user.username
|
||||
}))
|
||||
}
|
||||
|
||||
this.io.to(`lobby-${this.code}`).emit("lobbyUserUpdate", data);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Lobby;
|
||||
12
backend/src/SocketIO/LobbyManager/Classes/SocketUser.js
Normal file
12
backend/src/SocketIO/LobbyManager/Classes/SocketUser.js
Normal file
@@ -0,0 +1,12 @@
|
||||
const socketIO = require("socket.io");
|
||||
|
||||
class SocketUser {
|
||||
/** @param {number} id @param {string} username @param {socketIO.Socket} socket */
|
||||
constructor(id, username, socket) {
|
||||
this.id = id;
|
||||
this.username = username;
|
||||
this.socket = socket;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = SocketUser;
|
||||
60
backend/src/SocketIO/LobbyManager/ClientHandler.js
Normal file
60
backend/src/SocketIO/LobbyManager/ClientHandler.js
Normal file
@@ -0,0 +1,60 @@
|
||||
const socketIO = require("socket.io");
|
||||
const LobbyManager = require("./LobbyManager");
|
||||
const SocketUser = require("./Classes/SocketUser");
|
||||
|
||||
class ClientHandler {
|
||||
/** @param {socketIO.Socket} socket @param {LobbyManager} lobbyManager */
|
||||
constructor(socket, lobbyManager) {
|
||||
this.socket = socket;
|
||||
this.lobbyManager = lobbyManager;
|
||||
|
||||
this.user = new SocketUser(
|
||||
this.socket.request.session.user.id,
|
||||
this.socket.request.session.user.username,
|
||||
this.socket
|
||||
);
|
||||
|
||||
|
||||
this.currentLobbyCode = null;
|
||||
|
||||
this.socket.on("createLobby", () => { this.createLobby() })
|
||||
this.socket.on("joinLobby", (code) => { this.joinLobby(code) })
|
||||
this.socket.on("refreshLobby", () => { this.refreshLobby() })
|
||||
this.socket.on("disconnect", () => { this.disconnect() })
|
||||
}
|
||||
|
||||
createLobby(){
|
||||
if(this.currentLobbyCode) return;
|
||||
|
||||
const lobbyCode = this.lobbyManager.createLobby(this.user);
|
||||
this.currentLobbyCode = lobbyCode;
|
||||
this.socket.join(`lobby-${lobbyCode}`);
|
||||
this.socket.emit("lobbyCreated", lobbyCode);
|
||||
this.refreshLobby();
|
||||
}
|
||||
|
||||
joinLobby(code){
|
||||
if(this.currentLobbyCode) return;
|
||||
|
||||
const response = this.lobbyManager.joinLobby(code, this.user);
|
||||
|
||||
if(response === 1) return this.socket.emit("lobbyJoinError", "Diese Lobby existiert nicht");
|
||||
if(response === 2) return this.socket.emit("lobbyJoinError", "Die Lobby ist schon voll");
|
||||
|
||||
this.currentLobbyCode = response;
|
||||
|
||||
this.socket.join(`lobby-${response}`)
|
||||
this.socket.emit("lobbyJoined", response);
|
||||
this.refreshLobby();
|
||||
}
|
||||
|
||||
refreshLobby(){
|
||||
this.lobbyManager.refreshLobby(this.currentLobbyCode);
|
||||
}
|
||||
|
||||
disconnect(){
|
||||
this.lobbyManager.removeFromLobby(this.currentLobbyCode, this.user);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ClientHandler;
|
||||
65
backend/src/SocketIO/LobbyManager/LobbyManager.js
Normal file
65
backend/src/SocketIO/LobbyManager/LobbyManager.js
Normal file
@@ -0,0 +1,65 @@
|
||||
const socketIO = require("socket.io");
|
||||
const Lobby = require("./Classes/Lobby");
|
||||
const SocketUser = require("./Classes/SocketUser");
|
||||
|
||||
class LobbyManager {
|
||||
/** @param {socketIO.Server} io */
|
||||
constructor(io) {
|
||||
this.io = io;
|
||||
|
||||
/** @type {Map<string, Lobby>}*/
|
||||
this.lobbys = new Map();
|
||||
|
||||
// Zeigt die Anzahl der Lobbys an | Für Testing
|
||||
// setInterval(() => {
|
||||
// console.log(this.lobbys.size);
|
||||
// }, 1000);
|
||||
|
||||
}
|
||||
|
||||
/** @param {SocketUser} user */
|
||||
createLobby(user){
|
||||
const code = this.generateNonExistingCode();
|
||||
const lobby = new Lobby(this.io, code);
|
||||
lobby.addUser(user);
|
||||
this.lobbys.set(code, lobby);
|
||||
return code;
|
||||
}
|
||||
|
||||
/** @param {string} code @param {SocketUser} user */
|
||||
joinLobby(code, user){
|
||||
if (!this.lobbys.has(code)) return 1;
|
||||
|
||||
const lobby = this.lobbys.get(code);
|
||||
const response = lobby.addUser(user);
|
||||
if(response === 1) return 2;
|
||||
|
||||
return lobby.code;
|
||||
}
|
||||
|
||||
generateNonExistingCode() {
|
||||
let code;
|
||||
do {
|
||||
code = Math.random().toString(36).substring(2, 8).toUpperCase();
|
||||
} while (this.lobbys.has(code));
|
||||
return code;
|
||||
}
|
||||
|
||||
/** @param {SocketUser} user */
|
||||
removeFromLobby(code, user){
|
||||
if(!this.lobbys.has(code)) return;
|
||||
const lobby = this.lobbys.get(code);
|
||||
|
||||
const reponse = lobby.removeUser(user);
|
||||
|
||||
if (reponse === 2) this.lobbys.delete(code);
|
||||
}
|
||||
|
||||
refreshLobby(code){
|
||||
if(!this.lobbys.has(code)) return;
|
||||
const lobby = this.lobbys.get(code);
|
||||
lobby.sendLobbyUserUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = LobbyManager;
|
||||
69
backend/src/SocketIO/SocketIOManager.js
Normal file
69
backend/src/SocketIO/SocketIOManager.js
Normal file
@@ -0,0 +1,69 @@
|
||||
const socketIO = require("socket.io");
|
||||
const http = require("http");
|
||||
const DataBaseManager = require("../Database/DataBaseManager");
|
||||
const ExpressManager = require("../Express/ExpressManager");
|
||||
const LobbyManager = require("./LobbyManager/LobbyManager");
|
||||
const ClientHandler = require("./LobbyManager/ClientHandler");
|
||||
require("dotenv").config();
|
||||
|
||||
class SocketIOManager {
|
||||
/** @param {DataBaseManager} dbManager @param {ExpressManager} expressManager */
|
||||
constructor(dbManager, expressManager) {
|
||||
this.db = dbManager;
|
||||
this.express = expressManager;
|
||||
|
||||
|
||||
this.server = http.createServer(this.express.app);
|
||||
|
||||
/** @type {import("socket.io").Server} */
|
||||
this.io = socketIO(this.server);
|
||||
|
||||
// Dadurch bekommen wir Zugriff auf den Eingeloggten Nutzer
|
||||
this.io.use((socket, next) => {
|
||||
this.express.sessionMiddleware(socket.request, socket.request.res || {}, next);
|
||||
});
|
||||
|
||||
this.io.use((socket, next) => {this.useAuth(socket, next)});
|
||||
|
||||
/*
|
||||
Der LobbyManager kümmert sich um alle Lobbys
|
||||
Der LobbyHandler kümmert sich um die einzelnen Anfragen der Clients
|
||||
*/
|
||||
this.lobbyManager = new LobbyManager(this.io);
|
||||
|
||||
this.io.on("connection", (socket) => { this.sendToRightManager(socket) });
|
||||
|
||||
// Startet express und socket.io
|
||||
this.server.listen(process.env.PORT, () => {
|
||||
console.log(`Der Server ist gestartet und unter http://localhost:${process.env.PORT} erreichbar!`);
|
||||
});
|
||||
}
|
||||
|
||||
/** @param {socketIO.Socket} socket @param {socketIO.ExtendedError} next */
|
||||
useAuth(socket, next){
|
||||
// User muss eingeloggt sein
|
||||
if(!socket.request.session.user) return next(new Error("Nicht Eingeloggt!"));
|
||||
|
||||
// Der User darf nur eine verbindung gleichzeitig haben
|
||||
const userId = socket.request.session.user.id;
|
||||
const socketConnections = this.io.sockets.sockets;
|
||||
socketConnections.forEach((socket) => {
|
||||
if(socket.request.session.user.id === userId) return next(new Error("Irgendwer anders spielt schon!"));
|
||||
});
|
||||
|
||||
next();
|
||||
}
|
||||
|
||||
/** @param {socketIO.Socket} socket - The socket instance. */
|
||||
sendToRightManager(socket){
|
||||
socket.on("requestIdentification", () => {
|
||||
socket.emit("identification", socket.request.session.user);
|
||||
});
|
||||
|
||||
socket.on("hereForLobby", () => {
|
||||
new ClientHandler(socket, this.lobbyManager);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = SocketIOManager;
|
||||
7
backend/src/server.js
Normal file
7
backend/src/server.js
Normal file
@@ -0,0 +1,7 @@
|
||||
const DataBaseManager = require("./Database/DataBaseManager");
|
||||
const ExpressManager = require("./Express/ExpressManager");
|
||||
const SocketIOManager = require("./SocketIO/SocketIOManager");
|
||||
|
||||
const databaseManager = new DataBaseManager("localhost", "doublesnake");
|
||||
const expressManger = new ExpressManager(databaseManager);
|
||||
const socketIOManager = new SocketIOManager(databaseManager, expressManger);
|
||||
63
frontend/dashboard/account/index.html
Normal file
63
frontend/dashboard/account/index.html
Normal file
@@ -0,0 +1,63 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Account Bearbeiten</title>
|
||||
<link rel="stylesheet" href="../../style/generalStyle.css">
|
||||
<link rel="stylesheet" href="style.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container menu">
|
||||
<h1 class="title">Account</h1>
|
||||
<h2>Ändere deine Benutzerdaten uns speichere sie ab!</h2>
|
||||
<div class="buttonMenu container">
|
||||
<form action="/api/account/update" method="post" class="container">
|
||||
<h2 class="error" id="errorMsg"></h2>
|
||||
<div class="splitContainerX">
|
||||
<div>
|
||||
<div class="formSection">
|
||||
<p>Username:</p>
|
||||
<input id="username" name="username" type="text" placeholder="Username">
|
||||
</div>
|
||||
<div class="formSection">
|
||||
<p>Email:</p>
|
||||
<input id="email" name="email" type="email" placeholder="E-mail">
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="formSection">
|
||||
<p>Voller Name:</p>
|
||||
<input id="fullName" name="fullName" type="text" placeholder="Name">
|
||||
</div>
|
||||
<div class="formSection">
|
||||
<p>Neues Passwort:</p>
|
||||
<input id="password" name="password" type="password" placeholder="Neues Password">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<input type="submit" value="Speichern">
|
||||
</form>
|
||||
|
||||
<div class="container navButtons">
|
||||
<button id="dashboardBtn">
|
||||
<div class="buttonIcon">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="35px" viewBox="0 -960 960 960" width="35px"
|
||||
fill="#ffffff">
|
||||
<path d="M520-200 80-480l440-280-137 240h497v80H383l137 240Z" />
|
||||
</svg>
|
||||
</div>
|
||||
<div class="buttonText">
|
||||
Dashboard
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
<script src="../../scripts/ButtonManager.js"></script>
|
||||
<script src="../../scripts/ErrorHandler.js"></script>
|
||||
<script src="./index.js"></script>
|
||||
|
||||
</html>
|
||||
20
frontend/dashboard/account/index.js
Normal file
20
frontend/dashboard/account/index.js
Normal file
@@ -0,0 +1,20 @@
|
||||
class AccountManager{
|
||||
constructor(){
|
||||
this.username = document.getElementById("username");
|
||||
this.email = document.getElementById("email");
|
||||
this.fullName = document.getElementById("fullName");
|
||||
|
||||
this.setUserData();
|
||||
}
|
||||
|
||||
async setUserData(){
|
||||
const respone = await fetch("/api/dashboard");
|
||||
this.data = await respone.json();
|
||||
|
||||
this.username.value = this.data.username;
|
||||
this.email.value = this.data.email;
|
||||
this.fullName.value = this.data.fullName;
|
||||
}
|
||||
}
|
||||
|
||||
new AccountManager();
|
||||
3
frontend/dashboard/account/style.css
Normal file
3
frontend/dashboard/account/style.css
Normal file
@@ -0,0 +1,3 @@
|
||||
#dashboardBtn{
|
||||
background-color: orange;
|
||||
}
|
||||
83
frontend/dashboard/index.html
Normal file
83
frontend/dashboard/index.html
Normal file
@@ -0,0 +1,83 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Dashboard</title>
|
||||
<link rel="stylesheet" href="../style/generalStyle.css">
|
||||
<link rel="stylesheet" href="./style.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="container menu">
|
||||
<h1 class="title">Dashboard</h1>
|
||||
|
||||
<div class="container buttonMenu">
|
||||
<h2 id="data" class="message"></h2>
|
||||
<button id="lobbyBtn">
|
||||
<div class="buttonIcon">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="35px" viewBox="0 -960 960 960" width="35px"
|
||||
fill="#FFFFFF">
|
||||
<path d="M640-200q-27 0-52.5-5T538-219q58-49 90-117t32-144q0-76-32-144t-90-117q24-9 49.5-14t52.5-5q117 0 198.5 81.5T920-480q0 117-81.5
|
||||
198.5T640-200Zm-320 0q-117 0-198.5-81.5T40-480q0-117 81.5-198.5T320-760q27 0 52.5 5t49.5 14q-17 14-32 30.5T362-676q-10-2-20.5-3t-21.5-1q-83
|
||||
0-141.5 58.5T120-480q0 83 58.5 141.5T320-280q11 0 21.5-1t20.5-3q13 18 28 34.5t32 30.5q-24 9-49.5 14t-52.5 5Zm160-50q-57-39-88.5-100T360-480q0-69
|
||||
31.5-130T480-710q57 39 88.5 100T600-480q0 69-31.5 130T480-250Z" />
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<div class="buttonText">
|
||||
Lobby
|
||||
</div>
|
||||
</button>
|
||||
<button id="accountBtn">
|
||||
<div class="buttonIcon">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="35px" viewBox="0 -960 960 960" width="35px"
|
||||
fill="#FFFFFF">
|
||||
<path d="M480-480q-66 0-113-47t-47-113q0-66 47-113t113-47q66 0 113 47t47 113q0 66-47 113t-113 47ZM160-160v-112q0-34
|
||||
17.5-62.5T224-378q62-31 126-46.5T480-440q66 0 130 15.5T736-378q29 15 46.5 43.5T800-272v112H160Zm80-80h480v-32q0-11
|
||||
-5.5-20T700-306q-54-27-109-40.5T480-360q-56 0-111 13.5T260-306q-9 5-14.5 14t-5.5 20v32Zm240-320q33 0 56.5-23.5T560
|
||||
-640q0-33-23.5-56.5T480-720q-33 0-56.5 23.5T400-640q0 33 23.5 56.5T480-560Zm0-80Zm0 400Z" />
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<div class="buttonText">
|
||||
Account
|
||||
</div>
|
||||
</button>
|
||||
|
||||
<div class="container navButtons">
|
||||
<button id="homeBtn">
|
||||
<div class="buttonIcon">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="35px" viewBox="0 -960 960 960" width="35px"
|
||||
fill="#ffffff">
|
||||
<path d="M520-200 80-480l440-280-137 240h497v80H383l137 240Z" />
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<div class="buttonText">
|
||||
Startseite
|
||||
</div>
|
||||
</button>
|
||||
<button id="logoutBtn">
|
||||
<div class="buttonIcon">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="35px" viewBox="0 -960 960 960" width="35px"
|
||||
fill="#FFFFFF">
|
||||
<path d="M200-120q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h280v80H200v560h280v80H200Zm440-160-55-58
|
||||
102-102H360v-80h327L585-622l55-58 200 200-200 200Z" />
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<div class="buttonText">
|
||||
Ausloggen
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
<script src="../scripts/ButtonManager.js"></script>
|
||||
<script src="./index.js"></script>
|
||||
|
||||
</html>
|
||||
15
frontend/dashboard/index.js
Normal file
15
frontend/dashboard/index.js
Normal file
@@ -0,0 +1,15 @@
|
||||
class DataFetcher {
|
||||
constructor() {
|
||||
this.greetingText = document.getElementById("data");
|
||||
this.init();
|
||||
}
|
||||
|
||||
async init() {
|
||||
const reponse = await fetch("/api/dashboard");
|
||||
this.data = await reponse.json();
|
||||
|
||||
this.greetingText.innerText = `Hallo ${this.data.fullName ? this.data.fullName : this.data.username}, starte hier Double-Snake!`
|
||||
}
|
||||
}
|
||||
|
||||
new DataFetcher();
|
||||
69
frontend/dashboard/lobby/index.html
Normal file
69
frontend/dashboard/lobby/index.html
Normal file
@@ -0,0 +1,69 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Lobby</title>
|
||||
<link rel="stylesheet" href="../../style/generalStyle.css">
|
||||
<link rel="stylesheet" href="./style.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="container menu">
|
||||
<h1 class="title">Lobby</h1>
|
||||
<main class="container buttonMenu">
|
||||
<h2 class="error" id="errorMsg"></h2>
|
||||
<div class="container navButtons" id="mainDiv">
|
||||
<button id="createBtn">
|
||||
<div class="buttonIcon">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="35px" viewBox="0 -960 960 960" width="35px"
|
||||
fill="#FFFFFF">
|
||||
<path
|
||||
d="m272-440 208 120 208-120-168-97v137h-80v-137l-168 97Zm168-189v-17q-44-13-72-49.5T340-780q0-58 41-99t99-41q58
|
||||
0 99 41t41 99q0 48-28 84.5T520-646v17l280 161q19 11 29.5 29.5T840-398v76q0 22-10.5 40.5T800-252L520-91q-19 11-40 11t-40
|
||||
-11L160-252q-19-11-29.5-29.5T120-322v-76q0-22 10.5-40.5T160-468l280-161Zm0 378L200-389v67l280 162 280-162v-67L520-251q-19
|
||||
11-40 11t-40-11Zm40-469q25 0 42.5-17.5T540-780q0-25-17.5-42.5T480-840q-25 0-42.5 17.5T420-780q0 25 17.5 42.5T480-720Zm0 560Z" />
|
||||
</svg>
|
||||
</div>
|
||||
<div class="buttonText">
|
||||
Code Erstellen
|
||||
</div>
|
||||
</button>
|
||||
<button id="joinBtn">
|
||||
<div class="buttonIcon">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="35px" viewBox="0 -960 960 960" width="35px"
|
||||
fill="#FFFFFF">
|
||||
<path
|
||||
d="M220-520 80-600v-160l140-80 140 80v160l-140 80Zm0-92 60-34v-68l-60-34-60 34v68l60 34Zm440 123v-93l140 82v280L560-80
|
||||
320-220v-280l140-82v93l-60 35v188l160 93 160-93v-188l-60-35Zm-140 89v-480h360l-80 120 80 120H600v240h-80Zm40 69ZM220-680Z" />
|
||||
</svg>
|
||||
</div>
|
||||
<div class="buttonText">
|
||||
Mit Code Beitreten
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
<div class="container">
|
||||
<button id="leaveBtn">
|
||||
<div class="buttonIcon">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="35px" viewBox="0 -960 960 960" width="35px"
|
||||
fill="#FFFFFF">
|
||||
<path
|
||||
d="M200-120q-33 0-56.5-23.5T120-200v-160h80v160h560v-560H200v160h-80v-160q0-33 23.5-56.5T200-840h560q33 0
|
||||
56.5 23.5T840-760v560q0 33-23.5 56.5T760-120H200Zm220-160-56-58 102-102H120v-80h346L364-622l56-58 200 200-200 200Z" />
|
||||
</svg>
|
||||
</div>
|
||||
<div class="buttonText">
|
||||
Verlassen
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
<script src="https://cdn.socket.io/4.7.2/socket.io.min.js"></script>
|
||||
<script src="./scripts/index.js" type="module"></script>
|
||||
|
||||
</html>
|
||||
86
frontend/dashboard/lobby/scripts/Handler/LobbyHandler.js
Normal file
86
frontend/dashboard/lobby/scripts/Handler/LobbyHandler.js
Normal file
@@ -0,0 +1,86 @@
|
||||
class LobbyHandler {
|
||||
/**@param {import("../../../../../backend/node_modules/socket.io-client".Socket} socket Autocompletions VSC*/
|
||||
constructor(socket) {
|
||||
this.socket = socket;
|
||||
|
||||
this.errorMsg = document.getElementById("errorMsg");
|
||||
|
||||
this.code = null;
|
||||
this.playerList = [];
|
||||
|
||||
this.user = null;
|
||||
|
||||
this.socket.emit("requestIdentification");
|
||||
this.socket.on("identification", (user) => { this.user = user });
|
||||
|
||||
// Für das Erstellen
|
||||
this.socket.on("lobbyCreated", (code) => { this.joinedLobby(code) });
|
||||
|
||||
// Für das Beitreten
|
||||
this.socket.on("lobbyJoinError", (msg) => { this.showJoinError(msg) });
|
||||
this.socket.on("lobbyJoined", (code) => { this.joinedLobby(code) });
|
||||
|
||||
// Für das Aktualisieren
|
||||
this.socket.on("lobbyUserUpdate", (data) => { this.lobbyUserUpdate(data) });
|
||||
}
|
||||
|
||||
handleCreateClick() {
|
||||
this.socket.emit("createLobby");
|
||||
}
|
||||
|
||||
handleJoinClick(){
|
||||
const code = prompt("Bitte gib den 6 Stelligen Code ein");
|
||||
this.socket.emit("joinLobby", code);
|
||||
}
|
||||
|
||||
showJoinError(msg){
|
||||
this.errorMsg.innerText = msg;
|
||||
this.errorMsg.style.display = "block";
|
||||
}
|
||||
|
||||
joinedLobby(code) {
|
||||
this.code = code;
|
||||
|
||||
this.updateUI();
|
||||
|
||||
console.log(`Lobby beigerteten mit Code: ${code}`);
|
||||
}
|
||||
|
||||
updateUI() {
|
||||
const lobbyContainer = document.getElementById("mainDiv");
|
||||
|
||||
let playerHTML = "";
|
||||
this.playerList.forEach((player, index) => {
|
||||
const amI = player.id === this.user.id ? " (Ich)" : "";
|
||||
playerHTML += `
|
||||
<div class="player" id="player${index + 1}">
|
||||
<h1>Spieler ${index + 1}${amI}</h1>
|
||||
<h1>${player.username}</h1>
|
||||
</div>
|
||||
`;
|
||||
|
||||
lobbyContainer.innerHTML = `
|
||||
<div class="screen">
|
||||
<nav>
|
||||
<h1>Spiel-Code:</h1>
|
||||
<h1 class="message">${this.code}</h1>
|
||||
</nav>
|
||||
<div id="playerList">
|
||||
${playerHTML}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
});
|
||||
|
||||
this.errorMsg.style.display = "none";
|
||||
}
|
||||
|
||||
|
||||
lobbyUserUpdate(data) {
|
||||
this.playerList = data.users;
|
||||
|
||||
this.updateUI();
|
||||
}
|
||||
}
|
||||
|
||||
export default LobbyHandler;
|
||||
52
frontend/dashboard/lobby/scripts/index.js
Normal file
52
frontend/dashboard/lobby/scripts/index.js
Normal file
@@ -0,0 +1,52 @@
|
||||
import LobbyHandler from "./Handler/LobbyHandler.js";
|
||||
|
||||
class ServerConnectionManager{
|
||||
constructor(){
|
||||
/**@type {import("../../../../backend/node_modules/socket.io-client".Socket} für Autocompletions VSC*/
|
||||
this.socket = io(`http://${window.location.hostname}:${window.location.port}`);
|
||||
|
||||
this.createBtn = document.getElementById("createBtn");
|
||||
this.joinBtn = document.getElementById("joinBtn");
|
||||
this.leaveBtn = document.getElementById("leaveBtn");
|
||||
|
||||
this.lobbyHandler = new LobbyHandler(this.socket);
|
||||
|
||||
this.basicSetup();
|
||||
this.addButtonHandler();
|
||||
}
|
||||
|
||||
basicSetup(){
|
||||
this.socket.emit("hereForLobby");
|
||||
|
||||
this.socket.on("connect", () => {
|
||||
console.log("Verbindung zum Server hergestellt!");
|
||||
});
|
||||
|
||||
this.socket.on("connect_error", (error) => {
|
||||
console.log(error);
|
||||
window.location.pathname = "/dashboard";
|
||||
});
|
||||
|
||||
this.socket.on("disconnect", () => {
|
||||
console.log("Die verbindung wurde unterbrochen!");
|
||||
window.location.pathname = "/dashboard";
|
||||
})
|
||||
}
|
||||
|
||||
addButtonHandler(){
|
||||
this.createBtn.addEventListener("click", () => {
|
||||
this.lobbyHandler.handleCreateClick();
|
||||
});
|
||||
|
||||
this.joinBtn.addEventListener("click", () => {
|
||||
this.lobbyHandler.handleJoinClick();
|
||||
});
|
||||
|
||||
this.leaveBtn.addEventListener("click", () => {
|
||||
this.socket.disconnect();
|
||||
window.location.pathname = "/dashboard";
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
new ServerConnectionManager();
|
||||
66
frontend/dashboard/lobby/style.css
Normal file
66
frontend/dashboard/lobby/style.css
Normal file
@@ -0,0 +1,66 @@
|
||||
button {
|
||||
width: 350px;
|
||||
}
|
||||
|
||||
#joinBtn{
|
||||
background-color: rgb(0, 119, 255);
|
||||
}
|
||||
|
||||
#leaveBtn{
|
||||
background-color: orange;
|
||||
}
|
||||
|
||||
.screen{
|
||||
background-color: rgba(0, 0, 0, 0.6);
|
||||
border: 2px solid rgba(255, 255, 255, 0.6);
|
||||
border-radius: 15px;
|
||||
|
||||
height: 35vh;
|
||||
width: 700px;
|
||||
}
|
||||
|
||||
.screen nav{
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-evenly;
|
||||
align-items: center;
|
||||
height: 20%;
|
||||
|
||||
background-color: rgba(255, 255, 255, 0.377);
|
||||
border-radius: 10px 10px 0 0;
|
||||
}
|
||||
|
||||
#playerList{
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: start;
|
||||
align-items: center;
|
||||
|
||||
height: 80%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.player{
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-evenly;
|
||||
align-items: center;
|
||||
|
||||
width: 100%;
|
||||
height: 50%;
|
||||
}
|
||||
|
||||
.player h1{
|
||||
width: 50%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#player1{
|
||||
background-color: rgba(255, 8, 0, 0.377);
|
||||
border-radius: 0 0 0 0;
|
||||
}
|
||||
|
||||
#player2{
|
||||
background-color: rgba(0, 89, 255, 0.377);
|
||||
border-radius: 0 0 10px 10px;
|
||||
}
|
||||
23
frontend/dashboard/style.css
Normal file
23
frontend/dashboard/style.css
Normal file
@@ -0,0 +1,23 @@
|
||||
button{
|
||||
width: 280px;
|
||||
}
|
||||
|
||||
.buttonMenu{
|
||||
height: 60%;
|
||||
}
|
||||
|
||||
#joinBtn{
|
||||
background-color: rgb(0, 119, 255);
|
||||
}
|
||||
|
||||
#homeBtn{
|
||||
background-color: rgb(122, 122, 0);
|
||||
}
|
||||
|
||||
#accountBtn{
|
||||
background-color: orange;
|
||||
}
|
||||
|
||||
#logoutBtn{
|
||||
background-color: rgb(103, 11, 156);
|
||||
}
|
||||
78
frontend/index.html
Normal file
78
frontend/index.html
Normal file
@@ -0,0 +1,78 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Test</title>
|
||||
<link rel="stylesheet" href="./style/generalStyle.css">
|
||||
<link rel="stylesheet" href="./style.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="container menu">
|
||||
<h1 class="title">Double-Snake</h1>
|
||||
|
||||
<div class="buttonMenu container">
|
||||
<button id="tutorialBtn">
|
||||
<div class="buttonIcon">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="35px" viewBox="0 -960 960 960" width="35px"
|
||||
fill="#ffffff">
|
||||
<path d="M320-200v-560l440 280-440 280Zm80-280Zm0 134 210-134-210-134v268Z" />
|
||||
</svg>
|
||||
</div>
|
||||
<div class="buttonText">
|
||||
Anleitung
|
||||
</div>
|
||||
</button>
|
||||
<button id="loginBtn">
|
||||
<div class="buttonIcon">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="35px" viewBox="0 -960 960 960" width="35px"
|
||||
fill="#ffffff">
|
||||
<path
|
||||
d="M480-120v-80h280v-560H480v-80h280q33
|
||||
0 56.5 23.5T840-760v560q0 33-23.5 56.5T760-120H480Zm-80-160-55-58 102-102H120v-80h327L345-622l55-58 200 200-200 200Z" />
|
||||
</svg>
|
||||
</div>
|
||||
<div class="buttonText">
|
||||
Login
|
||||
</div>
|
||||
</button>
|
||||
<button id="registerBtn">
|
||||
<div class="buttonIcon">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="35px" viewBox="0 -960 960 960" width="35px"
|
||||
fill="#ffffff">
|
||||
<path
|
||||
d="M80-160v-112q0-33
|
||||
17-62t47-44q51-26 115-44t141-18q30 0 58.5 3t55.5 9l-70 70q-11-2-21.5-2H400q-71 0-127.5 17T180-306q-9 5-14.5 14t-5.5 20v32h250l80 80H80Zm542
|
||||
16L484-282l56-56 82 82 202-202 56 56-258 258ZM400-480q-66 0-113-47t-47-113q0-66 47-113t113-47q66 0 113 47t47 113q0 66-47 113t-113
|
||||
47Zm10 240Zm-10-320q33 0 56.5-23.5T480-640q0-33-23.5-56.5T400-720q-33 0-56.5 23.5T320-640q0 33 23.5 56.5T400-560Zm0-80Z" />
|
||||
</svg>
|
||||
</div>
|
||||
<div class="buttonText">
|
||||
Registrieren
|
||||
</div>
|
||||
</button>
|
||||
<button id="dashboardBtn">
|
||||
<div class="buttonIcon">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="35px" viewBox="0 -960 960 960" width="35px"
|
||||
fill="#ffffff">
|
||||
<path d="M480-320q48 0 85.5-28.5T620-422H340q17 45 54.5 73.5T480-320ZM380-480q25 0 42.5-17.5T440-540q0-25-17.5-42.5T380-600q-25
|
||||
0-42.5 17.5T320-540q0 25 17.5 42.5T380-480Zm200 0q25 0 42.5-17.5T640-540q0-25-17.5-42.5T580-600q-25 0-42.5 17.5T520-540q0 25 17.5
|
||||
42.5T580-480ZM305-704l112-145q12-16 28.5-23.5T480-880q18 0 34.5 7.5T543-849l112 145 170 57q26 8 41 29.5t15 47.5q0 12-3.5 24T866-523L756-367l4
|
||||
164q1 35-23 59t-56 24q-2 0-22-3l-179-50-179 50q-5 2-11 2.5t-11 .5q-32 0-56-24t-23-59l4-165L95-523q-8-11-11.5-23T80-570q0-25 14.5-46.5T135-647l170-57Zm49
|
||||
69-194 64 124 179-4 191 200-55 200 56-4-192 124-177-194-66-126-165-126 165Zm126 135Z" />
|
||||
</svg>
|
||||
</div>
|
||||
<div class="buttonText">
|
||||
Dashboard
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
<script src="./scripts/ButtonManager.js"></script>
|
||||
<script src="./index.js"></script>
|
||||
|
||||
</html>
|
||||
43
frontend/index.js
Normal file
43
frontend/index.js
Normal file
@@ -0,0 +1,43 @@
|
||||
class ButtonLoginChecker{
|
||||
constructor(){
|
||||
this.loggedOutButtons = [
|
||||
{ id: "loginBtn", route: "/login" },
|
||||
{ id: "registerBtn", route: "/register" }
|
||||
];
|
||||
|
||||
this.loggedInButtons = [
|
||||
{ id: "dashboardBtn", route: "/dashboard" },
|
||||
];
|
||||
|
||||
this.init();
|
||||
}
|
||||
|
||||
async init(){
|
||||
const isLoggedIn = await this.checkIsLoggedIn();
|
||||
|
||||
this.showButton(isLoggedIn);
|
||||
}
|
||||
|
||||
showButton(isLoggedIn){
|
||||
const buttons = isLoggedIn ? this.loggedOutButtons : this.loggedInButtons;
|
||||
|
||||
buttons.forEach(button => {
|
||||
document.getElementById(button.id).style.display = "none";
|
||||
})
|
||||
}
|
||||
|
||||
async checkIsLoggedIn(){
|
||||
try{
|
||||
const response = await fetch("/api/dashboard");
|
||||
|
||||
return !response.redirected;
|
||||
}
|
||||
catch(error){
|
||||
console.error(error);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
new ButtonLoginChecker();
|
||||
65
frontend/login/index.html
Normal file
65
frontend/login/index.html
Normal file
@@ -0,0 +1,65 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Einloggen</title>
|
||||
<link rel="stylesheet" href="../style/generalStyle.css">
|
||||
<link rel="stylesheet" href="./style.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="container menu">
|
||||
<h1 class="title">Einloggen</h1>
|
||||
<div class="buttonMenu container">
|
||||
<h2 class="error" id="errorMsg"></h2>
|
||||
<form action="/api/account/login" method="post" class="container">
|
||||
<div class="formSection">
|
||||
<p>Username:</p>
|
||||
<input name="username" type="text" placeholder="Username">
|
||||
</div>
|
||||
<div class="formSection">
|
||||
<p>Passwort:</p>
|
||||
<input name="password" type="password" placeholder="Password">
|
||||
</div>
|
||||
<input type="submit" value="Einloggen" id="loginBtn">
|
||||
</form>
|
||||
|
||||
<div class="container navButtons">
|
||||
<button id="homeBtn">
|
||||
<div class="buttonIcon">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="35px" viewBox="0 -960 960 960" width="35px"
|
||||
fill="#ffffff">
|
||||
<path d="M520-200 80-480l440-280-137 240h497v80H383l137 240Z" />
|
||||
</svg>
|
||||
</div>
|
||||
<div class="buttonText">
|
||||
Startseite
|
||||
</div>
|
||||
</button>
|
||||
<button id="registerBtn">
|
||||
<div class="buttonIcon">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="35px" viewBox="0 -960 960 960" width="35px"
|
||||
fill="#ffffff">
|
||||
<path
|
||||
d="M80-160v-112q0-33
|
||||
17-62t47-44q51-26 115-44t141-18q30 0 58.5 3t55.5 9l-70 70q-11-2-21.5-2H400q-71 0-127.5 17T180-306q-9 5-14.5 14t-5.5 20v32h250l80
|
||||
80H80Zm542
|
||||
16L484-282l56-56 82 82 202-202 56 56-258 258ZM400-480q-66 0-113-47t-47-113q0-66 47-113t113-47q66 0 113 47t47 113q0 66-47 113t-113
|
||||
47Zm10 240Zm-10-320q33 0 56.5-23.5T480-640q0-33-23.5-56.5T400-720q-33 0-56.5 23.5T320-640q0 33 23.5 56.5T400-560Zm0-80Z" />
|
||||
</svg>
|
||||
</div>
|
||||
<div class="buttonText">
|
||||
Registieren
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
<script src="../scripts/ButtonManager.js"></script>
|
||||
<script src="../scripts/ErrorHandler.js"></script>
|
||||
|
||||
</html>
|
||||
11
frontend/login/style.css
Normal file
11
frontend/login/style.css
Normal file
@@ -0,0 +1,11 @@
|
||||
#homeBtn{
|
||||
background-color: rgb(0, 119, 255);
|
||||
}
|
||||
|
||||
#registerBtn{
|
||||
background-color: rgb(122, 122, 0);
|
||||
}
|
||||
|
||||
#loginBtn{
|
||||
width: 234px;
|
||||
}
|
||||
76
frontend/register/index.html
Normal file
76
frontend/register/index.html
Normal file
@@ -0,0 +1,76 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Registieren</title>
|
||||
<link rel="stylesheet" href="../style/generalStyle.css">
|
||||
<link rel="stylesheet" href="./style.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="container menu">
|
||||
<h1 class="title">Registieren</h1>
|
||||
<div class="buttonMenu container">
|
||||
<form action="/api/account/register" method="post" class="container">
|
||||
<h2 class="error" id="errorMsg"></h2>
|
||||
<div class="splitContainerX">
|
||||
<div>
|
||||
<div class="formSection">
|
||||
<p>Username:</p>
|
||||
<input name="username" type="text" placeholder="Username">
|
||||
</div>
|
||||
<div class="formSection">
|
||||
<p>Email:</p>
|
||||
<input name="email" type="email" placeholder="E-mail">
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="formSection">
|
||||
<p>Voller Name:</p>
|
||||
<input name="fullName" type="text" placeholder="Name">
|
||||
</div>
|
||||
<div class="formSection">
|
||||
<p>Passwort:</p>
|
||||
<input name="password" type="password" placeholder="Password">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<input type="submit" value="Registieren" id="registerBtn">
|
||||
</form>
|
||||
|
||||
<div class="container navButtons">
|
||||
<button id="homeBtn">
|
||||
<div class="buttonIcon">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="35px" viewBox="0 -960 960 960" width="35px"
|
||||
fill="#ffffff">
|
||||
<path d="M520-200 80-480l440-280-137 240h497v80H383l137 240Z" />
|
||||
</svg>
|
||||
</div>
|
||||
<div class="buttonText">
|
||||
Startseite
|
||||
</div>
|
||||
</button>
|
||||
<button id="loginBtn">
|
||||
<div class="buttonIcon">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="35px" viewBox="0 -960 960 960" width="35px"
|
||||
fill="#ffffff">
|
||||
<path
|
||||
d="M480-120v-80h280v-560H480v-80h280q33
|
||||
0 56.5 23.5T840-760v560q0 33-23.5 56.5T760-120H480Zm-80-160-55-58 102-102H120v-80h327L345-622l55-58 200 200-200 200Z" />
|
||||
</svg>
|
||||
</div>
|
||||
<div class="buttonText">
|
||||
Login
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
<script src="../scripts/ButtonManager.js"></script>
|
||||
<script src="../scripts/ErrorHandler.js"></script>
|
||||
|
||||
</html>
|
||||
11
frontend/register/style.css
Normal file
11
frontend/register/style.css
Normal file
@@ -0,0 +1,11 @@
|
||||
#homeBtn{
|
||||
background-color: rgb(0, 119, 255);
|
||||
}
|
||||
|
||||
#loginBtn{
|
||||
background-color: rgb(122, 122, 0);
|
||||
}
|
||||
|
||||
#registerBtn{
|
||||
width: 234px;
|
||||
}
|
||||
27
frontend/scripts/ButtonManager.js
Normal file
27
frontend/scripts/ButtonManager.js
Normal file
@@ -0,0 +1,27 @@
|
||||
class ButtonManager {
|
||||
constructor() {
|
||||
this.buttons = [
|
||||
{ id: "homeBtn", route: "/" },
|
||||
{ id: "loginBtn", route: "/login" },
|
||||
{ id: "registerBtn", route: "/register" },
|
||||
{ id: "dashboardBtn", route: "/dashboard" },
|
||||
{ id: "lobbyBtn", route: "/dashboard/lobby" },
|
||||
{ id: "accountBtn", route: "/dashboard/account" },
|
||||
{ id: "logoutBtn", route: "/api/account/logout" },
|
||||
{ id: "tutorialBtn", route: "https://www.youtube.com/watch?v=1fkV5rB13jQ" }
|
||||
];
|
||||
|
||||
this.addEventListener();
|
||||
}
|
||||
|
||||
addEventListener() {
|
||||
this.buttons.forEach(button => {
|
||||
const element = document.getElementById(button.id);
|
||||
if (!element) return;
|
||||
|
||||
element.addEventListener("click", () => { window.location.href = button.route });
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
new ButtonManager();
|
||||
54
frontend/scripts/ErrorHandler.js
Normal file
54
frontend/scripts/ErrorHandler.js
Normal file
@@ -0,0 +1,54 @@
|
||||
class ErrorHandler {
|
||||
constructor() {
|
||||
this.errorH2 = document.getElementById("errorMsg");
|
||||
|
||||
this.errorList = [
|
||||
{ route: "/login",
|
||||
errors: [
|
||||
{ code: 1, msg: "Du musst ein Username und ein Passwort eingeben!", errorHighligher: ["username", "password"] },
|
||||
{ code: 2, msg: "Username oder Passwort falsch!", errorHighligher: ["username", "password"] }
|
||||
]
|
||||
},
|
||||
{ route: "/register",
|
||||
errors: [
|
||||
{ code: 1, msg: "Du musst zumindest Username und Passwort angeben!", errorHighligher: ["username", "password"] },
|
||||
{ code: 2, msg: "Username ist schon vergeben!", errorHighligher: ["username"] }
|
||||
]
|
||||
},
|
||||
{ route: "/dashboard/account",
|
||||
errors: [
|
||||
{ code: 1, msg: "Dieser Username ist schon vergeben!", errorHighligher: ["username"] },
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
this.showPossibleError();
|
||||
}
|
||||
|
||||
showPossibleError() {
|
||||
const route = this.getCurrentRoute();
|
||||
const error = this.checkError();
|
||||
|
||||
if(!error) return;
|
||||
|
||||
const errorJSON= this.errorList.find(x => x.route === route).errors.find(x => x.code === error);
|
||||
|
||||
this.errorH2.innerText = errorJSON.msg;
|
||||
this.errorH2.style.display = "block";
|
||||
|
||||
errorJSON.errorHighligher.forEach(element => {
|
||||
document.getElementsByName(element)[0].classList.add("important");
|
||||
});
|
||||
}
|
||||
|
||||
getCurrentRoute() {
|
||||
return window.location.pathname.slice(0, -1);
|
||||
}
|
||||
|
||||
checkError() {
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
return parseInt(urlParams.get("error"));
|
||||
}
|
||||
}
|
||||
|
||||
new ErrorHandler();
|
||||
11
frontend/style.css
Normal file
11
frontend/style.css
Normal file
@@ -0,0 +1,11 @@
|
||||
#loginBtn{
|
||||
background-color: rgb(0, 119, 255);
|
||||
}
|
||||
|
||||
#registerBtn{
|
||||
background-color: rgb(122, 122, 0);
|
||||
}
|
||||
|
||||
#dashboardBtn{
|
||||
background-color: orange;
|
||||
}
|
||||
207
frontend/style/generalStyle.css
Normal file
207
frontend/style/generalStyle.css
Normal file
@@ -0,0 +1,207 @@
|
||||
@import url('https://fonts.googleapis.com/css2?family=Sigmar&display=swap');
|
||||
@import url('https://fonts.googleapis.com/css2?family=Kanit:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&family=Preahvihear&display=swap');
|
||||
@import url('https://fonts.googleapis.com/css2?family=Bebas+Neue&family=Kanit:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&family=Preahvihear&family=Sigmar&display=swap');
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
|
||||
font-family: "Preahvihear", serif;
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: rgb(40, 85, 51);
|
||||
background-image: linear-gradient(rgba(0, 0, 0, 0.5) 2px, transparent 1px),
|
||||
linear-gradient(90deg, rgba(0, 0, 0, 0.5) 2px, transparent 1px);
|
||||
background-size: 55px 55px;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-evenly;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.splitContainerX {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.navButtons{
|
||||
flex-direction: row;
|
||||
justify-content: space-evenly;
|
||||
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
@keyframes rainbow-glow {
|
||||
0% {
|
||||
text-shadow: 0 0 10px red, 0 0 20px red, 0 0 30px red;
|
||||
}
|
||||
|
||||
20% {
|
||||
text-shadow: 0 0 10px orange, 0 0 20px orange, 0 0 30px orange;
|
||||
}
|
||||
|
||||
40% {
|
||||
text-shadow: 0 0 10px rgb(204, 0, 255), 0 0 20px rgb(204, 0, 255), 0 0 30px rgb(204, 0, 255);
|
||||
}
|
||||
|
||||
60% {
|
||||
text-shadow: 0 0 10px pink, 0 0 20px pink, 0 0 30px pink;
|
||||
}
|
||||
|
||||
80% {
|
||||
text-shadow: 0 0 10px blue, 0 0 20px blue, 0 0 30px blue;
|
||||
}
|
||||
|
||||
100% {
|
||||
text-shadow: 0 0 10px purple, 0 0 20px purple, 0 0 30px purple;
|
||||
}
|
||||
}
|
||||
|
||||
.title {
|
||||
font-family: "Sigmar", serif;
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
font-size: 100px;
|
||||
|
||||
animation: rainbow-glow 15s infinite alternate;
|
||||
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
button,
|
||||
.button,
|
||||
input[type=submit] {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: start;
|
||||
|
||||
background-color: rgb(233, 47, 47);
|
||||
|
||||
border: 2px solid rgba(255, 255, 255, 0.6);
|
||||
border-radius: 10px;
|
||||
|
||||
height: 60px;
|
||||
width: 250px;
|
||||
|
||||
color: white;
|
||||
font-size: 25px;
|
||||
|
||||
transition: transform 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
.buttonIcon,
|
||||
.buttonText {
|
||||
height: 100%;
|
||||
width: 25%;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.buttonText {
|
||||
width: 75%;
|
||||
}
|
||||
|
||||
button:hover,
|
||||
.button:hover,
|
||||
input[type=submit]:hover {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
button:active,
|
||||
.button:active,
|
||||
input[type=submit]:active {
|
||||
transform: translateY(1px);
|
||||
}
|
||||
|
||||
.menu {
|
||||
height: 100vh;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.buttonMenu {
|
||||
height: 50vh;
|
||||
width: 100vw;
|
||||
}
|
||||
|
||||
input[type=text],
|
||||
input[type=email],
|
||||
input[type=password] {
|
||||
background-color: rgba(255, 255, 255, 0.75);
|
||||
|
||||
border: 2px solid rgba(0, 0, 0, 0.6);
|
||||
border-radius: 10px;
|
||||
|
||||
height: 25px;
|
||||
width: 220px;
|
||||
|
||||
padding: 5px;
|
||||
|
||||
color: black;
|
||||
font-weight: 600px;
|
||||
font-size: 20px;
|
||||
|
||||
transition: transform 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
input[type=submit] {
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
input[type=text]:focus,
|
||||
input[type=email]:focus,
|
||||
input[type=password]:focus {
|
||||
outline: none;
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
.formSection {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
.formSection p {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.important {
|
||||
border-color: red !important;
|
||||
}
|
||||
|
||||
.important:focus {
|
||||
outline: none;
|
||||
border-color: red !important;
|
||||
}
|
||||
|
||||
.error {
|
||||
display: none;
|
||||
font-family: "Kanit", serif;
|
||||
font-weight: 600;
|
||||
font-style: normal;
|
||||
font-size: 25px;
|
||||
color: red;
|
||||
background-color: rgba(255, 255, 255, 0.6);
|
||||
padding: 5px;
|
||||
border-radius: 15px;
|
||||
}
|
||||
|
||||
.message{
|
||||
font-size: 25px;
|
||||
background-color: rgba(0, 0, 0, 0.6);
|
||||
border: 2px solid rgba(255, 255, 255, 0.6);
|
||||
padding: 5px;
|
||||
border-radius: 15px;
|
||||
}
|
||||
1171
testing/SocketIO_Testing/backend/package-lock.json
generated
Normal file
1171
testing/SocketIO_Testing/backend/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
18
testing/SocketIO_Testing/backend/package.json
Normal file
18
testing/SocketIO_Testing/backend/package.json
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"name": "backend",
|
||||
"version": "1.0.0",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"dev": "node ./src/index.js"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"description": "",
|
||||
"dependencies": {
|
||||
"body-parser": "^1.20.3",
|
||||
"express": "^4.21.2",
|
||||
"socket.io": "^4.8.1",
|
||||
"socket.io-client": "^4.8.1"
|
||||
}
|
||||
}
|
||||
103
testing/SocketIO_Testing/backend/src/index.js
Normal file
103
testing/SocketIO_Testing/backend/src/index.js
Normal file
@@ -0,0 +1,103 @@
|
||||
const express = require("express");
|
||||
const http = require("http");
|
||||
const socketIO = require("socket.io");
|
||||
const bp = require("body-parser");
|
||||
const path = require("path");
|
||||
|
||||
const _PORT = 3000;
|
||||
|
||||
const app = express();
|
||||
const server = http.createServer(app);
|
||||
/** @type {import("socket.io").Server} */
|
||||
const io = socketIO(server);
|
||||
|
||||
app.use(bp.urlencoded({ extended: false }));
|
||||
app.use(express.static(path.join(__dirname, "../../frontend")));
|
||||
|
||||
app.use((req, res, next) => {
|
||||
console.log(`${req.method} | ${req.url}`);
|
||||
|
||||
next();
|
||||
})
|
||||
|
||||
const playerList = [];
|
||||
|
||||
io.on('connection', (socket) => {
|
||||
console.log("Es ist ein neuer Spieler beigetreten");
|
||||
|
||||
const player = {
|
||||
id: socket.id,
|
||||
x: 100,
|
||||
y: 100,
|
||||
socket: socket,
|
||||
};
|
||||
|
||||
playerList.push(player);
|
||||
emitPlayerMovement();
|
||||
|
||||
socket.on("moveUp", () => {
|
||||
const player = playerList.find((p) => p.id === socket.id);
|
||||
if (player) {
|
||||
player.y -= 10;
|
||||
}
|
||||
|
||||
emitPlayerMovement();
|
||||
});
|
||||
|
||||
socket.on("moveDown", () => {
|
||||
const player = playerList.find((p) => p.id === socket.id);
|
||||
if (player) {
|
||||
player.y += 10;
|
||||
}
|
||||
|
||||
emitPlayerMovement();
|
||||
});
|
||||
|
||||
socket.on("moveRight", () => {
|
||||
const player = playerList.find((p) => p.id === socket.id);
|
||||
if (player) {
|
||||
player.x += 10;
|
||||
}
|
||||
|
||||
emitPlayerMovement();
|
||||
});
|
||||
|
||||
socket.on("moveLeft", () => {
|
||||
const player = playerList.find((p) => p.id === socket.id);
|
||||
if (player) {
|
||||
player.x -= 10;
|
||||
}
|
||||
|
||||
emitPlayerMovement();
|
||||
});
|
||||
|
||||
// Handle player disconnect
|
||||
socket.on("disconnect", () => {
|
||||
console.log(`Spieler ${socket.id} hat das Spiel verlassen`);
|
||||
const index = playerList.findIndex((p) => p.id === socket.id);
|
||||
if (index !== -1) {
|
||||
playerList.splice(index, 1);
|
||||
}
|
||||
|
||||
emitPlayerMovement();
|
||||
});
|
||||
|
||||
socket.on("disconnect", () => {
|
||||
playerList.splice(playerList.findIndex(p => p.id === socket.id), 1);
|
||||
})
|
||||
});
|
||||
|
||||
function emitPlayerMovement() {
|
||||
const playerMap = playerList.map(x => {
|
||||
return {
|
||||
id: x.id,
|
||||
x: x.x,
|
||||
y: x.y
|
||||
}
|
||||
});
|
||||
io.emit("updatePos", playerMap);
|
||||
}
|
||||
|
||||
server.listen(_PORT, () => {
|
||||
console.log(`Backend gestartet auf http://localhost:${_PORT}`);
|
||||
})
|
||||
17
testing/SocketIO_Testing/frontend/index.html
Normal file
17
testing/SocketIO_Testing/frontend/index.html
Normal file
@@ -0,0 +1,17 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Erste Socket.io Seite</title>
|
||||
<link rel="stylesheet" href="style/style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<h1>Multigame</h1>
|
||||
</body>
|
||||
|
||||
<script src="https://cdn.socket.io/4.7.2/socket.io.min.js"></script>
|
||||
<script src="index.js" type="module"></script>
|
||||
|
||||
<canvas id="game" height="300px" width="300px" id> </canvas>
|
||||
</html>
|
||||
37
testing/SocketIO_Testing/frontend/index.js
Normal file
37
testing/SocketIO_Testing/frontend/index.js
Normal file
@@ -0,0 +1,37 @@
|
||||
/** @type {import("../backend/node_modules/socket.io-client").Socket} */
|
||||
const socket = io("http://localhost:3000");
|
||||
|
||||
const gameScreen = document.getElementById("game");
|
||||
const ctx = gameScreen.getContext("2d")
|
||||
|
||||
ctx.fillStyle = "red";
|
||||
|
||||
socket.on("connect", () => {
|
||||
console.log(`Connected as ${socket.id}`);
|
||||
});
|
||||
|
||||
let keysPressed = new Set();
|
||||
|
||||
document.addEventListener("keydown", (e) => {
|
||||
keysPressed.add(e.key);
|
||||
});
|
||||
|
||||
document.addEventListener("keyup", (e) => {
|
||||
keysPressed.delete(e.key);
|
||||
});
|
||||
|
||||
setInterval(() => {
|
||||
if (keysPressed.has("ArrowUp")) socket.emit("moveUp");
|
||||
if (keysPressed.has("ArrowDown")) socket.emit("moveDown");
|
||||
if (keysPressed.has("ArrowRight")) socket.emit("moveRight");
|
||||
if (keysPressed.has("ArrowLeft")) socket.emit("moveLeft");
|
||||
}, 50);
|
||||
|
||||
socket.on("updatePos", (map) => {
|
||||
ctx.clearRect(0, 0, gameScreen.width, gameScreen.height);
|
||||
map.forEach(e => {
|
||||
ctx.beginPath();
|
||||
ctx.arc(e.x, e.y, 10, 0, Math.PI * 2, false);
|
||||
ctx.fill();
|
||||
});
|
||||
})
|
||||
3
testing/SocketIO_Testing/frontend/style/style.css
Normal file
3
testing/SocketIO_Testing/frontend/style/style.css
Normal file
@@ -0,0 +1,3 @@
|
||||
#game{
|
||||
background-color: grey;
|
||||
}
|
||||
Reference in New Issue
Block a user