- Add responsive :
     - Lobby page
     - Fix bug profil/login/settings'
This commit is contained in:
Mathis Degryck
2024-10-05 23:50:34 +02:00
26 changed files with 561 additions and 156 deletions

View File

@ -12,7 +12,7 @@ RUN apt install -y python3.12 postgresql-client
RUN curl https://bootstrap.pypa.io/get-pip.py -o /root/get-pip.py RUN curl https://bootstrap.pypa.io/get-pip.py -o /root/get-pip.py
RUN python3.12 /root/get-pip.py RUN python3.12 /root/get-pip.py
RUN pip3 install requests django psycopg "channels[daphne]" RUN pip3 install requests django psycopg "channels[daphne]" multimethod
ARG DB_HOST ARG DB_HOST
ARG DB_NAME ARG DB_NAME

View File

@ -0,0 +1,15 @@
# **************************************************************************** #
# #
# ::: :::::::: #
# Bot.py :+: :+: :+: #
# +:+ +:+ +:+ #
# By: tomoron <tomoron@student.42.fr> +#+ +:+ +#+ #
# +#+#+#+#+#+ +#+ #
# Created: 2024/10/05 03:54:20 by tomoron #+# #+# #
# Updated: 2024/10/05 03:55:31 by tomoron ### ########.fr #
# #
# **************************************************************************** #
class Bot(Player):
def __init__(self):
print("I am a bot boop boop beep boop")

View File

@ -6,11 +6,12 @@
# By: edbernar <edbernar@student.42angouleme. +#+ +:+ +#+ # # By: edbernar <edbernar@student.42angouleme. +#+ +:+ +#+ #
# +#+#+#+#+#+ +#+ # # +#+#+#+#+#+ +#+ #
# Created: 2024/09/13 16:20:58 by tomoron #+# #+# # # Created: 2024/09/13 16:20:58 by tomoron #+# #+# #
# Updated: 2024/10/03 23:54:53 by tomoron ### ########.fr # # Updated: 2024/10/05 03:50:25 by tomoron ### ########.fr #
# # # #
# **************************************************************************** # # **************************************************************************** #
from asgiref.sync import sync_to_async from asgiref.sync import sync_to_async
from .Player import Player
from .models import GameResults, User from .models import GameResults, User
import time import time
import json import json
@ -64,33 +65,22 @@ class Game:
maxScore = 5 maxScore = 5
def __init__(self, socket, withBot, skinId = 0, opponent = None): def __init__(self, socket, withBot, skinId = 0, opponent = None):
self.p1 = None self.p1 = Player()
self.p2 = None self.p2 = Player()
self.p1Ready = False
self.p2Ready = False
self.bot = withBot
self.started = False self.started = False
self.end = False self.end = False
self.left = None self.left = None
self.winner = None self.winner = None
self.gameStart = 0 self.gameStart = 0
self.expSleepTime = 0
self.p1Pos = {"pos":0, "up": False}
self.p2Pos = {"pos":0, "up": False}
self.ballPos = {"pos":(0, 0), "up": False} self.ballPos = {"pos":(0, 0), "up": False}
self.ballVel = (0, 0)
self.speed = Game.startSpeed self.speed = Game.startSpeed
self.ballVel = (self.speed, 0)
self.score = [0, 0] self.score = [0, 0]
self.obstacles = [] self.obstacles = []
self.lastWin = 2 self.lastWin = 2
self.p1Skin = 0
self.p2Skin = 0
self.opponentLock = opponent self.opponentLock = opponent
if(withBot): if(withBot):
self.join(socket, skinId) self.join(socket, skinId)
elif(opponent != None): elif(opponent != None):
@ -138,40 +128,40 @@ class Game:
else: else:
self.obstacles.append(Game.jumpersPos[i + 1]) self.obstacles.append(Game.jumpersPos[i + 1])
self.p1.sync_send({"type":"game", "content":{"action":7, "content":self.obstacles}}) self.p1.socket.sync_send({"type":"game", "content":{"action":7, "content":self.obstacles}})
self.obstaclesInvLength() self.obstaclesInvLength()
self.p2.sync_send({"type":"game", "content":{"action":7, "content":self.obstacles}}) self.p2.socket.sync_send({"type":"game", "content":{"action":7, "content":self.obstacles}})
self.obstaclesInvLength() self.obstaclesInvLength()
def join(self, socket, skin = 0): def join(self, socket, skin = 0):
try: try:
if(self.p1 == None): if(self.p1.socket == None):
print("game created, set as player 1") print("game created, set as player 1")
self.p1 = socket self.p1.socket = socket
self.p1Skin = skin self.p1.skin = skin
else: else:
if(self.opponentLock != None and self.opponentLock != socket.id): if(self.opponentLock != None and self.opponentLock != socket.id):
socket.sendError("You are not invited to this game", 9103) socket.sendError("You are not invited to this game", 9103)
return; return;
print("joined game, set as player 2") print("joined game, set as player 2")
self.p2 = socket self.p2.socket = socket
self.p2Skin = skin self.p2.skin = skin
socket.game = self socket.game = self
if(self.p2 != None and self.p1 != None): if(self.p2.socket != None and self.p1.socket != None):
print("both players here, send opponent to both players") print("both players here, send opponent to both players")
self.p1.sync_send({"type":"game", "content":{"action":1,"id":self.p2.id,"username":self.p2.username, "skin":self.p2Skin}}) self.p1.socket.sync_send({"type":"game", "content":{"action":1,"id":self.p2.socket.id,"username":self.p2.socket.username, "skin":self.p2.skin}})
self.p2.sync_send({"type":"game", "content":{"action":1,"id":self.p1.id,"username":self.p1.username, "skin":self.p1Skin}}) self.p2.socket.sync_send({"type":"game", "content":{"action":1,"id":self.p1.socket.id,"username":self.p1.socket.username, "skin":self.p1.skin}})
except Exception as e: except Exception as e:
socket.sendError("invalid request", 9005, e) socket.sendError("invalid request", 9005, e)
async def setReady(self, socket): async def setReady(self, socket):
if(socket == self.p1): if(socket == self.p1.socket):
self.p1Ready = True self.p1.ready = True
elif (socket == self.p2): elif (socket == self.p2.socket):
self.p2Ready = True self.p2.ready = True
else: else:
return(0) return(0)
if(self.p1Ready and self.p2Ready): if(self.p1.ready and self.p2.ready):
print("both players are ready, starting game") print("both players are ready, starting game")
self.genObstacles() self.genObstacles()
print("obstacles generated :", self.obstacles) print("obstacles generated :", self.obstacles)
@ -181,14 +171,14 @@ class Game:
def endGame(self, winner): def endGame(self, winner):
if(self.end): if(self.end):
return return
self.p1.sync_send({"type":"game","content":{"action":10,"won":winner==1, "opponentLeft":self.left == 2}}) self.p1.socket.sync_send({"type":"game","content":{"action":10,"won":winner==1, "opponentLeft":self.left == 2}})
self.p2.sync_send({"type":"game","content":{"action":10,"won":winner==2, "opponentLeft":self.left == 1}}) self.p2.socket.sync_send({"type":"game","content":{"action":10,"won":winner==2, "opponentLeft":self.left == 1}})
self.winner = winner self.winner = winner
self.end = True self.end = True
def leave(self, socket): def leave(self, socket):
socket.game = None socket.game = None
if (socket == self.p1): if (socket == self.p1.socket):
self.left = 1 self.left = 1
else: else:
self.left = 2 self.left = 2
@ -204,17 +194,17 @@ class Game:
def sendPlayers(self, data): def sendPlayers(self, data):
data_raw = json.dumps({"type":"game","content":data}) data_raw = json.dumps({"type":"game","content":data})
self.p1.sync_send(data_raw) self.p1.socket.sync_send(data_raw)
self.p2.sync_send(data_raw) self.p2.socket.sync_send(data_raw)
def move(self, socket, pos, up): def move(self, socket, pos, up):
opponent = self.p1 if socket != self.p1 else self.p2 opponent = self.p1.socket if socket != self.p1.socket else self.p2.socket
if(socket == self.p1): if(socket == self.p1.socket):
self.p1Pos["pos"] = pos self.p1.pos["pos"] = pos
self.p1Pos["up"] = up; self.p1.pos["up"] = up;
else: else:
self.p2Pos["pos"] = -pos; self.p2.pos["pos"] = -pos;
self.p2Pos["up"] = up self.p2.pos["up"] = up
if(opponent != None): if(opponent != None):
opponent.sync_send({"type":"game","content":{"action":3, "pos":-pos, "up":up, "is_opponent":True}}) opponent.sync_send({"type":"game","content":{"action":3, "pos":-pos, "up":up, "is_opponent":True}})
@ -225,14 +215,14 @@ class Game:
self.gameStart = time.time() * 1000 self.gameStart = time.time() * 1000
else: else:
gameTime = (time.time() * 1000) - self.gameStart gameTime = (time.time() * 1000) - self.gameStart
if(self.p1): if(self.p1.socket):
self.p1.sync_send({"type":"game", "content":{"action":5, self.p1.socket.sync_send({"type":"game", "content":{"action":5,
"pos" : [self.ballPos["pos"][0],self.ballPos["pos"][1]], "pos" : [self.ballPos["pos"][0],self.ballPos["pos"][1]],
"velocity":[self.ballVel[0], self.ballVel[1]], "velocity":[self.ballVel[0], self.ballVel[1]],
"game_time":gameTime "game_time":gameTime
}}) }})
if(self.p2): if(self.p2.socket):
self.p2.sync_send({"type":"game","content":{"action":5, self.p2.socket.sync_send({"type":"game","content":{"action":5,
"pos" : [-self.ballPos["pos"][0],-self.ballPos["pos"][1]], "pos" : [-self.ballPos["pos"][0],-self.ballPos["pos"][1]],
"velocity":[-self.ballVel[0], -self.ballVel[1]], "velocity":[-self.ballVel[0], -self.ballVel[1]],
"game_time":gameTime "game_time":gameTime
@ -355,8 +345,8 @@ class Game:
print("a player suffured from a major skill issue") print("a player suffured from a major skill issue")
self.score[player-1] += 1 self.score[player-1] += 1
print("new score :", self.score) print("new score :", self.score)
self.p1.sync_send({"type":"game","content":{"action":6, "is_opponent": player == 2}}) self.p1.socket.sync_send({"type":"game","content":{"action":6, "is_opponent": player == 2}})
self.p2.sync_send({"type":"game","content":{"action":6, "is_opponent": player == 1}}) self.p2.socket.sync_send({"type":"game","content":{"action":6, "is_opponent": player == 1}})
await asyncio.sleep(4.5) await asyncio.sleep(4.5)
if(self.checkGameEndGoal()): if(self.checkGameEndGoal()):
return return
@ -375,8 +365,8 @@ class Game:
if(self.obstacles[i]["isUp"] != self.ballPos["up"]): if(self.obstacles[i]["isUp"] != self.ballPos["up"]):
continue continue
if(self.twoPointsDistance((self.obstacles[i]["pos"]["x"], self.obstacles[i]["pos"]["z"]), ballPos) < Game.jumperRadius): if(self.twoPointsDistance((self.obstacles[i]["pos"]["x"], self.obstacles[i]["pos"]["z"]), ballPos) < Game.jumperRadius):
self.p1.sync_send({"type":"game", "content":{"action":8,"name":self.obstacles[i]["name"]}}) self.p1.socket.sync_send({"type":"game", "content":{"action":8,"name":self.obstacles[i]["name"]}})
self.p2.sync_send({"type":"game", "content":{"action":8,"name":self.obstacles[i]["name"]}}) self.p2.socket.sync_send({"type":"game", "content":{"action":8,"name":self.obstacles[i]["name"]}})
self.ballPos["up"] = not self.ballPos["up"] self.ballPos["up"] = not self.ballPos["up"]
def checkWallsColision(self, ballPos): def checkWallsColision(self, ballPos):
@ -402,22 +392,21 @@ class Game:
now = time.time() now = time.time()
delta = now - self.lastUpdate delta = now - self.lastUpdate
print("delta :", delta) print("delta :", delta)
print("\033[31msleep time diff :", (delta - self.expSleepTime) * 1000, "ms")
currentBallPos = self.ballPos["pos"] currentBallPos = self.ballPos["pos"]
velX = self.ballVel[0] velX = self.ballVel[0]
velZ = self.ballVel[1] velZ = self.ballVel[1]
newBallPos = (round(currentBallPos[0] + (delta * velX), 5), newBallPos = (round(currentBallPos[0] + (delta * velX), 5),
round(currentBallPos[1] + (delta * velZ), 5)) round(currentBallPos[1] + (delta * velZ), 5))
if(newBallPos[1] <= Game.limits["back"] or newBallPos[1] >= Game.limits["front"]): if(newBallPos[1] <= Game.limits["back"] or newBallPos[1] >= Game.limits["front"]):
player = self.p2Pos if newBallPos[1] < 0 else self.p1Pos player = self.p2.pos if newBallPos[1] < 0 else self.p1.pos
playerDistance = self.getPlayerDistance(player, newBallPos) playerDistance = self.getPlayerDistance(player, newBallPos)
if(playerDistance >= -(Game.playerLength / 2) and playerDistance <= Game.playerLength / 2 and player["up"] == self.ballPos["up"]): if(playerDistance >= -(Game.playerLength / 2) and playerDistance <= Game.playerLength / 2 and player["up"] == self.ballPos["up"]):
velX = -((self.speed * 0.80) * (playerDistance / (Game.playerLength / 2))) velX = -((self.speed * 0.80) * (playerDistance / (Game.playerLength / 2)))
velZ = self.speed - abs(velX) velZ = self.speed - abs(velX)
if(newBallPos[1] > 0): if(newBallPos[1] > 0):
velZ = -velZ velZ = -velZ
self.p1.sync_send({"type":"game","content":{"action":4, "is_opponent": newBallPos[1] < 0}}) self.p1.socket.sync_send({"type":"game","content":{"action":4, "is_opponent": newBallPos[1] < 0}})
self.p2.sync_send({"type":"game","content":{"action":4, "is_opponent": newBallPos[1] > 0}}) self.p2.socket.sync_send({"type":"game","content":{"action":4, "is_opponent": newBallPos[1] > 0}})
else: else:
await self.scoreGoal(1 if newBallPos[1] < 0 else 2) await self.scoreGoal(1 if newBallPos[1] < 0 else 2)
return; return;
@ -458,12 +447,11 @@ class Game:
break; break;
sleep_time = self.getSleepTime() sleep_time = self.getSleepTime()
print("sleep time : " , sleep_time) print("sleep time : " , sleep_time)
self.expSleepTime = sleep_time
await asyncio.sleep(sleep_time) await asyncio.sleep(sleep_time)
print("game end") print("game end")
await self.saveResults() await self.saveResults()
self.p1.game = None self.p1.socket.game = None
self.p2.game = None self.p2.socket.game = None
@sync_to_async @sync_to_async
def saveResults(self): def saveResults(self):
@ -472,8 +460,8 @@ class Game:
print("unkown winner, setting to 1") print("unkown winner, setting to 1")
self.winner = 1 self.winner = 1
print("saving results") print("saving results")
p1DbUser = User.objects.get(id=self.p1.id) p1DbUser = User.objects.get(id=self.p1.socket.id)
p2DbUser = User.objects.get(id=self.p2.id) p2DbUser = User.objects.get(id=self.p2.socket.id)
results = GameResults.objects.create( results = GameResults.objects.create(
player1 = p1DbUser, player1 = p1DbUser,
player2 = p2DbUser, player2 = p2DbUser,
@ -485,5 +473,5 @@ class Game:
results.save() results.save()
print("results saved") print("results saved")
except Exception as e: except Exception as e:
self.p1.sendError("Couldn't save last game results", 9104, e) self.p1.socket.sendError("Couldn't save last game results", 9104, e)
self.p2.sendError("Couldn't save last game results", 9104, e) self.p2.socket.sendError("Couldn't save last game results", 9104, e)

View File

@ -0,0 +1,18 @@
# **************************************************************************** #
# #
# ::: :::::::: #
# Player.py :+: :+: :+: #
# +:+ +:+ +:+ #
# By: tomoron <tomoron@student.42.fr> +#+ +:+ +#+ #
# +#+#+#+#+#+ +#+ #
# Created: 2024/10/05 03:22:32 by tomoron #+# #+# #
# Updated: 2024/10/05 03:46:24 by tomoron ### ########.fr #
# #
# **************************************************************************** #
class Player():
def __init__(self):
self.socket = None
self.ready = False
self.pos = {"pos":0, "up": False}
self.skin = 0

View File

@ -0,0 +1,78 @@
# **************************************************************************** #
# #
# ::: :::::::: #
# Tournament.py :+: :+: :+: #
# +:+ +:+ +:+ #
# By: edbernar <edbernar@student.42angouleme. +#+ +:+ +#+ #
# +#+#+#+#+#+ +#+ #
# Created: 2024/10/04 17:17:07 by tomoron #+# #+# #
# Updated: 2024/10/05 04:31:00 by tomoron ### ########.fr #
# #
# **************************************************************************** #
import string
import asyncio
from .utils import genString
class Tournament:
currentTournamentsLock = False
currentTournaments = {}
playerLimit = 8
def __init__(self, socket):
self.messages = []
self.players = []
while(Tournament.currentTournamentsLock):
continue;
Tournament.currentTournamentsLock = True
self.genCode()
Tournament.currentTournaments[self.code] = self
Tournament.currentTournamentsLock = False
self.join(socket)
def genCode(self):
nbChar = 4
self.code = genString(nbChar, string.ascii_uppercase)
nbIter = 0
while(self.code in Tournament.currentTournaments):
if(nbIter == 100):
nbInter = 0
nbChar += 1
self.code = genString(nbChar, string.ascii_uppercase)
nbIter += 1
def broadcast(self, content):
for x in self.players:
x.sync_send("tournament",content)
def sendAllInfo(self, socket):
players = []
for x in self.players:
players.append({"id":x.id,"username":x.username, "pfp":x.pfp})
socket.sync_send("tournament",{"action":5, "players":players, "messages" : self.messages})
def sendMessage(self, socket, message):
self.messages.append({"username":socket.username, "message":message})
if(len(self.messages) > 20):
self.messages.pop(0)
self.broadcast({"action":3, "username":socket.username, "message":message})
def leave(self, socket):
if(socket not in self.players):
return;
index = self.players.index(socket)
self.players.pop(index)
socket.tournament = None
self.broadcast({"action":2,"id":socket.id})
def join(self, socket):
if(socket.tournament != None):
socket.sendError("already in a tournament", 9036)
return
if(len(self.players) == Tournament.playerLimit):
socket.sync_send("tournament",{"action":0, "isFull":True})
return
socket.tournament = self
self.players.append(socket)
socket.sync_send("tournament",{"action":0,"isFull":False, "isStarted":False,"exist":True, "code":self.code})
self.broadcast({"action":1, "id":socket.id, "username": socket.username,"pfp":socket.pfp})

View File

@ -193,8 +193,7 @@
</div> </div>
<span class="line"></span> <span class="line"></span>
<p style="text-align: center; margin-bottom: 20px;">Tournament code</p> <p style="text-align: center; margin-bottom: 20px;">Tournament code</p>
<input id="tournamentCode" class="search-input" type="text" placeholder="Enter the tournament code (empty for create one)"> <input id="tournamentCode" class="search-input" type="text" placeholder="Enter the tournament code (empty to create one)">
<span class="line" id="tournament-line2"></span>
</div> </div>
<div class="skin-select"> <div class="skin-select">
<div class="barSelection" id="bar2"> <div class="barSelection" id="bar2">

View File

@ -6,7 +6,7 @@
# By: edbernar <edbernar@student.42angouleme. +#+ +:+ +#+ # # By: edbernar <edbernar@student.42angouleme. +#+ +:+ +#+ #
# +#+#+#+#+#+ +#+ # # +#+#+#+#+#+ +#+ #
# Created: 2024/09/23 23:35:41 by edbernar #+# #+# # # Created: 2024/09/23 23:35:41 by edbernar #+# #+# #
# Updated: 2024/09/27 03:37:33 by tomoron ### ########.fr # # Updated: 2024/10/04 21:13:28 by tomoron ### ########.fr #
# # # #
# **************************************************************************** # # **************************************************************************** #
@ -23,9 +23,12 @@ def changePfp(socket, content):
generate_name = genString(50) generate_name = genString(50)
if (not User.objects.filter(pfp=f"/pfp/{generate_name}.jpg").exists()): if (not User.objects.filter(pfp=f"/pfp/{generate_name}.jpg").exists()):
break break
with open(f"/var/www/djangoserver/pfp/{generate_name}.jpg", "wb") as image_file:
image_file.write(base64.b64decode(content["img"]))
user = User.objects.get(id=socket.id) user = User.objects.get(id=socket.id)
user.pfp = f"/pfp/{generate_name}.jpg" user.pfp = f"/pfp/{generate_name}.jpg"
user.save() user.save()
with open(f"/var/www/djangoserver/pfp/{generate_name}.jpg", "wb") as image_file: socket.pfp = user.pfp
image_file.write(base64.b64decode(content["img"])) socket.scope["session"]["pfp"] = user.pfp
socket.scope["session"].save()
socket.sync_send(json.dumps({"type": "change_pfp", "content": {'pfp': user.pfp}})) socket.sync_send(json.dumps({"type": "change_pfp", "content": {'pfp': user.pfp}}))

View File

@ -6,7 +6,7 @@
# By: tomoron <tomoron@student.42.fr> +#+ +:+ +#+ # # By: tomoron <tomoron@student.42.fr> +#+ +:+ +#+ #
# +#+#+#+#+#+ +#+ # # +#+#+#+#+#+ +#+ #
# Created: 2024/09/09 16:10:26 by tomoron #+# #+# # # Created: 2024/09/09 16:10:26 by tomoron #+# #+# #
# Updated: 2024/10/01 16:35:10 by tomoron ### ########.fr # # Updated: 2024/10/04 17:10:39 by tomoron ### ########.fr #
# # # #
# **************************************************************************** # # **************************************************************************** #
@ -73,6 +73,9 @@ from .gameActions.ping import ping
action_list = [start, ready, leave, move, ping] action_list = [start, ready, leave, move, ping]
async def gameRequest(socket, content): async def gameRequest(socket, content):
if("action" not in content):
socket.sendError("missing action parameter",9035)
return
action = content["action"] action = content["action"]
if(action < 0 or action > len(action_list)): if(action < 0 or action > len(action_list)):
socket.sendError("Action out of range", 9100) socket.sendError("Action out of range", 9100)

View File

@ -6,7 +6,7 @@
# By: edbernar <edbernar@student.42angouleme. +#+ +:+ +#+ # # By: edbernar <edbernar@student.42angouleme. +#+ +:+ +#+ #
# +#+#+#+#+#+ +#+ # # +#+#+#+#+#+ +#+ #
# Created: 2024/08/03 08:10:38 by edbernar #+# #+# # # Created: 2024/08/03 08:10:38 by edbernar #+# #+# #
# Updated: 2024/09/30 19:43:54 by tomoron ### ########.fr # # Updated: 2024/10/04 23:35:03 by tomoron ### ########.fr #
# # # #
# **************************************************************************** # # **************************************************************************** #
@ -25,26 +25,29 @@ def userExists(mail, password):
if(not user.exists()): if(not user.exists()):
return({"found":False}) return({"found":False})
else: else:
return({"found":True, "id":user[0].id, "username":user[0].username, "mail_verified":user[0].mail_verified}) return({"found":True, "id":user[0].id, "username":user[0].username, "mail_verified":user[0].mail_verified, "pfp":user[0].pfp})
async def loginByPass(socket, content): async def loginByPass(socket, content):
u_info = await userExists(content["mail"],content["password"]) try:
if(u_info["found"]): u_info = await userExists(content["mail"],content["password"])
if(not u_info["mail_verified"]): if(u_info["found"]):
socket.sendError("Account not verified, please verify your account before logging in",9025) if(not u_info["mail_verified"]):
return socket.sendError("Account not verified, please verify your account before logging in",9025)
if(await socket.login(u_info["id"], u_info["username"])): return
await socket.setLastLogin() if(await socket.login(u_info["id"], u_info["username"], u_info["pfp"])):
socket.sync_send(json.dumps({"type":"logged_in", "content":{ await socket.setLastLogin()
"status":True, socket.sync_send(json.dumps({"type":"logged_in", "content":{
"username":u_info["username"], "status":True,
"id": u_info["id"], "username":u_info["username"],
"haveUnreadMessage": await getUnreadStatus(u_info["id"]) "id": u_info["id"],
}})) "haveUnreadMessage": await getUnreadStatus(u_info["id"])
}}))
else:
socket.sendError("An unknown error occured",9027)
else: else:
socket.sendError("An unknown error occured",9027) socket.sync_send(json.dumps({"type": "error", "content": "Invalid email or password", "code": 9007}))
else: except Exception as e:
socket.sync_send(json.dumps({"type": "error", "content": "Invalid email or password", "code": 9007})) socket.sendError("Invalid request", 9005, e)
async def login(socket, content): async def login(socket, content):
try: try:

View File

@ -0,0 +1,14 @@
# **************************************************************************** #
# #
# ::: :::::::: #
# fetchAllData.py :+: :+: :+: #
# +:+ +:+ +:+ #
# By: tomoron <tomoron@student.42.fr> +#+ +:+ +#+ #
# +#+#+#+#+#+ +#+ #
# Created: 2024/10/05 02:08:12 by tomoron #+# #+# #
# Updated: 2024/10/05 02:08:52 by tomoron ### ########.fr #
# #
# **************************************************************************** #
async def fetchAllData(socket, content):
socket.tournament.sendAllInfo(socket)

View File

@ -0,0 +1,18 @@
# **************************************************************************** #
# #
# ::: :::::::: #
# leave.py :+: :+: :+: #
# +:+ +:+ +:+ #
# By: tomoron <tomoron@student.42.fr> +#+ +:+ +#+ #
# +#+#+#+#+#+ +#+ #
# Created: 2024/10/04 18:05:07 by tomoron #+# #+# #
# Updated: 2024/10/05 02:28:42 by tomoron ### ########.fr #
# #
# **************************************************************************** #
async def tournamentLeave(socket, content):
print("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" * 100)
if(socket.tournament == None):
socket.sendError("you're not in a tournament", 9037)
return;
socket.tournament.leave(socket)

View File

@ -0,0 +1,17 @@
# **************************************************************************** #
# #
# ::: :::::::: #
# sendMessage.py :+: :+: :+: #
# +:+ +:+ +:+ #
# By: tomoron <tomoron@student.42.fr> +#+ +:+ +#+ #
# +#+#+#+#+#+ +#+ #
# Created: 2024/10/04 18:05:27 by tomoron #+# #+# #
# Updated: 2024/10/05 03:27:45 by tomoron ### ########.fr #
# #
# **************************************************************************** #
async def sendMessage(socket, content):
if("message" not in content):
socket.sendError("missing message field",9038)
return
socket.tournament.sendMessage(socket, content["message"])

View File

@ -0,0 +1,22 @@
# **************************************************************************** #
# #
# ::: :::::::: #
# start.py :+: :+: :+: #
# +:+ +:+ +:+ #
# By: tomoron <tomoron@student.42.fr> +#+ +:+ +#+ #
# +#+#+#+#+#+ +#+ #
# Created: 2024/10/04 17:16:02 by tomoron #+# #+# #
# Updated: 2024/10/05 02:28:50 by tomoron ### ########.fr #
# #
# **************************************************************************** #
from ...Tournament import Tournament
async def tournamentStart(socket, content):
if("code" in content and len(content["code"])):
if(content["code"] in Tournament.currentTournaments):
Tournament.currentTournaments[content["code"]].join(socket)
else:
socket.sync_send("tournament",{"action":0, "exist":False})
else:
Tournament(socket)

View File

@ -6,26 +6,31 @@
# By: edbernar <edbernar@student.42angouleme. +#+ +:+ +#+ # # By: edbernar <edbernar@student.42angouleme. +#+ +:+ +#+ #
# +#+#+#+#+#+ +#+ # # +#+#+#+#+#+ +#+ #
# Created: 2024/10/01 13:16:39 by edbernar #+# #+# # # Created: 2024/10/01 13:16:39 by edbernar #+# #+# #
# Updated: 2024/10/03 01:29:01 by edbernar ### ########.fr # # Updated: 2024/10/05 02:33:45 by tomoron ### ########.fr #
# # # #
# **************************************************************************** # # **************************************************************************** #
from .tournamentActions.start import tournamentStart
from .tournamentActions.leave import tournamentLeave
from .tournamentActions.sendMessage import sendMessage
from .tournamentActions.fetchAllData import fetchAllData
# tournament request format : {"type":"tournament", "content":{"action": 1, ...}} # tournament request format : {"type":"tournament", "content":{"action": 1, ...}}
#server actions (actions sent by the server): #server actions (actions sent by the server):
# 0 : start : tell the client if tournament is full or not exist. If not, tell the client can start the tournament # 0 : start : tell the client if tournament is full or not exist. If not, tell the client can start the tournament
# - exist : true/false # - exists : true/false
# - isFull : true/false # - isFull : true/false
# - started : true/false # - started : true/false
# - code : code of the tournament # - code : code of the tournament
# #
# 1 : someoneJoin : tell the client someone join the tournament (considering clients will place selon the order of the join) # 1 : someoneJoined : tell the client someone joined the tournament (considering clients will place depending on the order of the join)
# - id : id of the player # - id : id of the player
# - username : name of the player # - username : name of the player
# - pfp : pfp of the player # - pfp : pfp of the player
# #
# 2 : someoneLeave : tell the client someone leave the tournament (if game not started, players will be replaced in the order of the join) # 2 : someoneLeft : tell the client someone left the tournament (if game not started, players will be replaced in the order of the join)
# - id : id of the player who leave # - id : id of the player who left
# #
# 3 : message : send a message to the tournament chat # 3 : message : send a message to the tournament chat
# - username : name of the player who send the message # - username : name of the player who send the message
@ -35,11 +40,15 @@
# - id : id of the player # - id : id of the player
# - username : name of the player # - username : name of the player
# #
# 5 : allData : gives tournament data to the client
# - players : [{id, username, pfp}, ...]
# - messages : [{username, message}]
#client actions (actions sent by the client) : #client actions (actions sent by the client) :
# 0 : start : start a tournament. if code == "", create a new tournament, else join the tournament with the code # 0 : start : start a tournament. if code == "", create a new tournament, else join the tournament with the code
# - code : code of the tournament # - code : code of the tournament
#
# 1 : leave : leave the tournament # 1 : leave : leave the tournament
# #
# 2 : message tournament : send a message to the tournament chat # 2 : message tournament : send a message to the tournament chat
@ -48,4 +57,16 @@
# 3 : fetchAllData : fetch all data of the tournament # 3 : fetchAllData : fetch all data of the tournament
# --> server will send all the data of the tournament (players, messages, etc...) with his actions # --> server will send all the data of the tournament (players, messages, etc...) with his actions
actionList = [tournamentStart, tournamentLeave, sendMessage, fetchAllData]
async def tournamentRequest(socket, content):
if("action" not in content):
socket.sendError("missing action parameter",9035)
return
action = content["action"]
if(action < 0 or action > len(actionList)):
socket.sendError("Action out of range", 9100)
return;
if(action != 0 and socket.tournament == None):
socket.sendError("you're not in a tournament",9037)
return ;
await actionList[action](socket,content)

View File

@ -25,8 +25,9 @@ urlpatterns = [
path("multiOnlineGamePage", views.multiOnlineGamePage, name='multiOnlineGamePage'), path("multiOnlineGamePage", views.multiOnlineGamePage, name='multiOnlineGamePage'),
path("waitingGamePage", views.waitingGamePage, name='waitingGamePage'), path("waitingGamePage", views.waitingGamePage, name='waitingGamePage'),
path("profilPage", views.profilPage, name='profilPage'), path("profilPage", views.profilPage, name='profilPage'),
path("game", views.game, name='game'), # path("game", views.game, name='game'),
path("wait_game", views.game, name='wait_game'), path("wait_game", views.game, name='wait_game'),
path("tournament", views.tournament, name='tournament'),
path("login42", views.login42, name='login42'), path("login42", views.login42, name='login42'),
path("logout", views.logout, name='logout'), path("logout", views.logout, name='logout'),
path("verify", views.verify, name='verify'), path("verify", views.verify, name='verify'),

View File

@ -6,12 +6,12 @@
# By: edbernar <edbernar@student.42angouleme. +#+ +:+ +#+ # # By: edbernar <edbernar@student.42angouleme. +#+ +:+ +#+ #
# +#+#+#+#+#+ +#+ # # +#+#+#+#+#+ +#+ #
# Created: 2024/09/27 03:36:08 by tomoron #+# #+# # # Created: 2024/09/27 03:36:08 by tomoron #+# #+# #
# Updated: 2024/09/27 11:23:51 by edbernar ### ########.fr # # Updated: 2024/10/04 18:59:25 by tomoron ### ########.fr #
# # # #
# **************************************************************************** # # **************************************************************************** #
import random import random
import string
def genString(length): def genString(length, letters=string.ascii_letters+string.digits):
letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
return(''.join(random.choice(letters) for i in range(length))) return(''.join(random.choice(letters) for i in range(length)))

View File

@ -122,6 +122,8 @@ def login42(request):
request.session["logged_in"] = True request.session["logged_in"] = True
request.session["username"] = db_user[0].username request.session["username"] = db_user[0].username
request.session["id"] = db_user[0].id request.session["id"] = db_user[0].id
request.session["pfp"] = db_user[0].pfp
request.session.save()
return redirect("/") return redirect("/")
def logout(request): def logout(request):
@ -164,4 +166,7 @@ def tournamentPage(request):
return index(request) return index(request)
if(not request.session.get("logged_in", False)): if(not request.session.get("logged_in", False)):
return(HttpResponse("you are not logged in",status=403)) return(HttpResponse("you are not logged in",status=403))
return render(request, "tournamentPage.html", {}) return render(request, "tournamentPage.html", {})
def tournament(request):
return redirect("/lobby")

View File

@ -6,12 +6,13 @@
# By: edbernar <edbernar@student.42angouleme. +#+ +:+ +#+ # # By: edbernar <edbernar@student.42angouleme. +#+ +:+ +#+ #
# +#+#+#+#+#+ +#+ # # +#+#+#+#+#+ +#+ #
# Created: 2024/09/09 14:31:30 by tomoron #+# #+# # # Created: 2024/09/09 14:31:30 by tomoron #+# #+# #
# Updated: 2024/09/30 19:42:45 by tomoron ### ########.fr # # Updated: 2024/10/04 21:06:20 by tomoron ### ########.fr #
# # # #
# **************************************************************************** # # **************************************************************************** #
from channels.generic.websocket import AsyncWebsocketConsumer from channels.generic.websocket import AsyncWebsocketConsumer
from asgiref.sync import sync_to_async from asgiref.sync import sync_to_async
from multimethod import multimethod
from typing import Union from typing import Union
import json import json
import asyncio import asyncio
@ -36,16 +37,17 @@ from .typeRequests.changePrivateInfo import changePrivateInfo
from .typeRequests.changePfp import changePfp from .typeRequests.changePfp import changePfp
from .typeRequests.statusMessage import statusMessage,getUnreadStatus from .typeRequests.statusMessage import statusMessage,getUnreadStatus
from .typeRequests.readMessage import readMessage from .typeRequests.readMessage import readMessage
from .typeRequests.tournamentRequest import tournamentRequest
typeRequest = ["login", "get_private_list_user", "get_private_list_message", typeRequest = ["login", "get_private_list_user", "get_private_list_message",
"send_private_message", "create_account", "get_all_list_user", "game", "send_private_message", "create_account", "get_all_list_user",
"search_user", "get_user_info", "change_pfp", "change_banner", "game", "search_user", "get_user_info", "change_pfp", "change_banner",
"get_private_info", "change_private_info","status_message", "read_message" "get_private_info", "change_private_info","status_message", "read_message", "tournament"
] ]
functionRequest = [login, getPrivateListUser, getPrivateListMessage, functionRequest = [login, getPrivateListUser, getPrivateListMessage,
sendPrivateMessage, createAccount, getAllListUser, gameRequest, sendPrivateMessage, createAccount, getAllListUser, gameRequest,
searchUser, getUserInfo, changePfp, changeBanner, searchUser, getUserInfo, changePfp, changeBanner,
getPrivateInfo, changePrivateInfo, statusMessage, readMessage getPrivateInfo, changePrivateInfo, statusMessage, readMessage, tournamentRequest
] ]
from random import randint from random import randint
@ -83,7 +85,7 @@ class WebsocketHandler(AsyncWebsocketConsumer):
print("\033[32monline : ", self.onlinePlayers) print("\033[32monline : ", self.onlinePlayers)
return(0) return(0)
async def login(self, uid: int, username: str) -> int: async def login(self, uid: int, username: str, pfp : str) -> int:
if(await self.session_get("logged_in", False)): if(await self.session_get("logged_in", False)):
print("already logged in") print("already logged in")
return(0) return(0)
@ -93,15 +95,18 @@ class WebsocketHandler(AsyncWebsocketConsumer):
await self.session_set("logged_in",True) await self.session_set("logged_in",True)
await self.session_set("id",uid) await self.session_set("id",uid)
await self.session_set("username",username) await self.session_set("username",username)
await self.session_set("pfp", pfp)
await self.session_save() await self.session_save()
self.logged_in = True self.logged_in = True
self.id = uid self.id = uid
self.username = username self.username = username
self.pfp = pfp
return(1) return(1)
async def connect(self): async def connect(self):
self.logged_in = False self.logged_in = False
self.game = None self.game = None
self.tournament = None
self.id = 0 self.id = 0
self.username = None self.username = None
self.online = True self.online = True
@ -114,6 +119,7 @@ class WebsocketHandler(AsyncWebsocketConsumer):
return; return;
self.id = await self.session_get("id",0) self.id = await self.session_get("id",0)
self.username = await self.session_get("username", None) self.username = await self.session_get("username", None)
self.pfp = await self.session_get("pfp",None)
self.logged_in = True self.logged_in = True
await self.send(text_data=json.dumps({"type":"logged_in", "content":{ await self.send(text_data=json.dumps({"type":"logged_in", "content":{
"status":await self.session_get("logged_in",False), "status":await self.session_get("logged_in",False),
@ -133,6 +139,8 @@ class WebsocketHandler(AsyncWebsocketConsumer):
del self.onlinePlayers[uid] del self.onlinePlayers[uid]
if(self.game !=None): if(self.game !=None):
self.game.leave(self) self.game.leave(self)
if(self.tournament !=None):
self.tournament.leave(self)
async def receive(self, text_data): async def receive(self, text_data):
try: try:
@ -153,7 +161,12 @@ class WebsocketHandler(AsyncWebsocketConsumer):
self.sendError("Invalid type", 9004) self.sendError("Invalid type", 9004)
except Exception as e: except Exception as e:
self.sendError("Invalid request", 9005, e) self.sendError("Invalid request", 9005, e)
@multimethod
def sync_send(self, reqType : str, content:dict):
self.sync_send({"type":reqType,"content":content})
@multimethod
def sync_send(self, data: Union[dict,str]): def sync_send(self, data: Union[dict,str]):
if(not self.online): if(not self.online):
return return

View File

@ -172,11 +172,6 @@ function startTournmament()
{ {
const code = document.getElementById('tournamentCode').value; const code = document.getElementById('tournamentCode').value;
if (code.length != 6 && code.length != 0)
{
CN.new("Information", "The code must be 6 characters long or empty");
return ;
}
sendRequest("tournament", {action: 0, code: code}); sendRequest("tournament", {action: 0, code: code});
} }

View File

@ -6,13 +6,16 @@
/* By: edbernar <edbernar@student.42angouleme. +#+ +:+ +#+ */ /* By: edbernar <edbernar@student.42angouleme. +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */ /* +#+#+#+#+#+ +#+ */
/* Created: 2024/08/18 00:30:31 by edbernar #+# #+# */ /* Created: 2024/08/18 00:30:31 by edbernar #+# #+# */
/* Updated: 2024/10/03 14:48:51 by edbernar ### ########.fr */ /* Updated: 2024/10/05 02:41:30 by edbernar ### ########.fr */
/* */ /* */
/* ************************************************************************** */ /* ************************************************************************** */
import { fetchProfile, MotionController } from '/static/javascript/three/examples/jsm/libs/motion-controllers.module.js'
import { XRControllerModelFactory } from '/static/javascript/three/examples/jsm/webxr/XRControllerModelFactory.js'
import { scene, renderer, isInVrMode } from '/static/javascript/multiOnlineGame/multiOnlineGamePage.js'
import { lastSelectedGoal, availableGoals } from '/static/javascript/lobbyPage/3d.js';
import * as THREE from '/static/javascript/three/build/three.module.js' import * as THREE from '/static/javascript/three/build/three.module.js'
import { layoutSelected } from '/static/javascript/lobbyPage/main.js' import { layoutSelected } from '/static/javascript/lobbyPage/main.js'
import { lastSelectedGoal, availableGoals } from '/static/javascript/lobbyPage/3d.js';
/* /*
Explication du code : Explication du code :
@ -45,12 +48,16 @@ import { lastSelectedGoal, availableGoals } from '/static/javascript/lobbyPage/3
- Ajouter une fonction pour l'animation de point marqué (OK) - Ajouter une fonction pour l'animation de point marqué (OK)
*/ */
let playerExist = false; let playerExist = false;
let isOnPointAnim = false; let isOnPointAnim = false;
let pressedButton = []; let pressedButton = [];
let mapLength = 0; let mapLength = 0;
const goalAnimation = ["triangle", "cylinder", "star", "box", "rectangle", "ring"]; const goalAnimation = ["triangle", "cylinder", "star", "box", "rectangle", "ring"];
let key = null; const controllerModelFactory = new XRControllerModelFactory();
let motionController = null;
let key = null;
let controller1 = null;
let controller2 = null;
class Player class Player
{ {
@ -67,6 +74,8 @@ class Player
opponent = null; opponent = null;
playerGoalAnimation = null; playerGoalAnimation = null;
opponentGoal = null; opponentGoal = null;
controller1 = null;
controller2 = null;
constructor (object, map, opponent, indexGoalAnimation, goalIdOppenent) constructor (object, map, opponent, indexGoalAnimation, goalIdOppenent)
{ {
@ -280,6 +289,7 @@ class Player
update() update()
{ {
const gamepads = navigator.getGamepads();
const currentTime = Date.now(); const currentTime = Date.now();
this.deltaTime = (currentTime - this.previousTime) / 1000; this.deltaTime = (currentTime - this.previousTime) / 1000;
this.previousTime = currentTime; this.previousTime = currentTime;
@ -287,6 +297,23 @@ class Player
let i; let i;
i = 0; i = 0;
for (let i = 0; i< gamepads.length; i++)
{
if (gamepads[i])
{
const xAxis = gamepads[i].axes[0];
const yAxis = gamepads[i].axes[1];
if (!gamepads[i].buttons[0].touched)
this.buttonACheck = false;
else if (this.buttonACheck == false && gamepads[i].buttons[0].touched)
{
this.buttonACheck = true;
this.buttonAAction = true;
}
this.joysticksMove(xAxis, yAxis);
this.buttonAAction = false;
}
}
while (i < pressedButton.length) while (i < pressedButton.length)
{ {
if (pressedButton[i] == key.up && this.object.position.y < this.limits.up) if (pressedButton[i] == key.up && this.object.position.y < this.limits.up)
@ -336,6 +363,40 @@ class Player
} }
i++; i++;
} }
if (isInVrMode)
{
if (controller1.userData.inputSource && controller1.userData.inputSource.gamepad)
{
const gamepad = controller1.userData.inputSource.gamepad;
const [a, b, xAxis, yAxis] = gamepad.axes;
this.joysticksMove(xAxis, yAxis);
}
}
}
buttonACheck = false;
buttonAAction = false;
joysticksMove(xAxis, yAxis)
{
if (yAxis > 0.75 || this.buttonAAction)
addKeyInArr({key: key.down})
else
remKeyInArr({key: key.down});
if (yAxis < -0.75 || this.buttonAAction)
addKeyInArr({key: key.up})
else
remKeyInArr({key: key.up});
if (xAxis > 0.5)
addKeyInArr({key: key.right})
else
remKeyInArr({key: key.right});
if (xAxis < -0.5)
addKeyInArr({key: key.left})
else
remKeyInArr({key: key.left});
} }
setCameraPosition(x, y, z) setCameraPosition(x, y, z)
@ -369,6 +430,37 @@ class Player
}, i * 10); }, i * 10);
} }
} }
configureVrController()
{
controller1 = renderer.xr.getController(0);
controller2 = renderer.xr.getController(1);
scene.add(controller1);
scene.add(controller2);
for (let i = 0; i < scene.children.length; i++)
{
if (scene.children[i].name === 'vrHeadset')
{
const controllerGrip1 = renderer.xr.getControllerGrip(0);
controllerGrip1.add(controllerModelFactory.createControllerModel(controllerGrip1));
scene.children[i].add(controllerGrip1);
const controllerGrip2 = renderer.xr.getControllerGrip(1);
controllerGrip2.add(controllerModelFactory.createControllerModel(controllerGrip2));
scene.children[i].add(controllerGrip2);
}
}
controller1.addEventListener('connected', (event) => {
controller1.userData.inputSource = event.data;
});
controller2.addEventListener('connected', (event) => {
controller2.userData.inputSource = event.data;
});
}
}; };
function addKeyInArr(e) function addKeyInArr(e)
@ -447,7 +539,6 @@ function showGamePad()
gamePad.style.display = 'flex'; gamePad.style.display = 'flex';
document.addEventListener('touchstart', (event) => { document.addEventListener('touchstart', (event) => {
const key = event.target.getAttribute("id"); const key = event.target.getAttribute("id");
for (let i = 0; i < keyList.length; i++) for (let i = 0; i < keyList.length; i++)
{ {

View File

@ -6,20 +6,20 @@
/* By: edbernar <edbernar@student.42angouleme. +#+ +:+ +#+ */ /* By: edbernar <edbernar@student.42angouleme. +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */ /* +#+#+#+#+#+ +#+ */
/* Created: 2024/08/18 00:53:53 by edbernar #+# #+# */ /* Created: 2024/08/18 00:53:53 by edbernar #+# #+# */
/* Updated: 2024/10/03 14:27:34 by edbernar ### ########.fr */ /* Updated: 2024/10/04 21:47:35 by edbernar ### ########.fr */
/* */ /* */
/* ************************************************************************** */ /* ************************************************************************** */
import { availableSkins, lastSelectedGoal } from '/static/javascript/lobbyPage/3d.js';
import * as THREE from '/static/javascript/three/build/three.module.js'
import { OrbitControls } from '/static/javascript/three/examples/jsm/controls/OrbitControls.js' import { OrbitControls } from '/static/javascript/three/examples/jsm/controls/OrbitControls.js'
import { sendRequest } from "/static/javascript/websocket.js"; import { availableSkins, lastSelectedGoal } from '/static/javascript/lobbyPage/3d.js';
import { Player } from '/static/javascript/multiOnlineGame/Player.js' import { VRButton } from "/static/javascript/three/examples/jsm/webxr/VRButton.js"
import { Map } from '/static/javascript/multiOnlineGame/Map.js'
import { Ball } from '/static/javascript/multiOnlineGame/Ball.js'
import { pageRenderer, isMobile } from '/static/javascript/main.js'
import { Opponent } from '/static/javascript/multiOnlineGame/Opponent.js' import { Opponent } from '/static/javascript/multiOnlineGame/Opponent.js'
import * as THREE from '/static/javascript/three/build/three.module.js'
import { Player } from '/static/javascript/multiOnlineGame/Player.js'
import { pageRenderer, isMobile } from '/static/javascript/main.js'
import { Ball } from '/static/javascript/multiOnlineGame/Ball.js'
import { Map } from '/static/javascript/multiOnlineGame/Map.js'
import { sendRequest } from "/static/javascript/websocket.js";
/* /*
Controls : Controls :
- w : monter - w : monter
@ -49,22 +49,32 @@ Controls :
- k : recreate et augmente le score de opponent - k : recreate et augmente le score de opponent
*/ */
const scoreMax = 5; let scene = null;
let map = null;
let ball = null;
let renderer = null;
let player = null;
let spotLight = null;
let ambiantLight = null;
let opponent = null;
let interval = null;
let intervalPing = null;
let debug = false;
let lastPingTime = 0;
let lastFpsTime = 0;
let lastFpsDisplayed = 0;
let lastFpsArr = [60];
let VrButton = null;
let isInVrMode = false;
let scene = null; const observer = new MutationObserver((mutationsList) => {
let map = null; mutationsList.forEach((mutation) => {
let ball = null; if (VrButton.innerText == 'VR NOT SUPPORTED')
let renderer = null; document.getElementById('newButtonVr').style.display = 'none';
let player = null; if (mutation.attributeName === 'style')
let spotLight = null; VrButton.style.display = 'none';
let ambiantLight = null; });
let opponent = null; });
let interval = null;
let intervalPing = null;
let debug = false;
let lastPingTime = 0;
let lastFpsTime = 0;
let lastFpsDisplayed = 0;
// ------------------- (need to be remove) -------------------- // // ------------------- (need to be remove) -------------------- //
const cameraTmp = new THREE.PerspectiveCamera(90, window.innerWidth / window.innerHeight); const cameraTmp = new THREE.PerspectiveCamera(90, window.innerWidth / window.innerHeight);
@ -75,7 +85,6 @@ class MultiOnlineGamePage
{ {
static create(skin) static create(skin)
{ {
console.log(lastSelectedGoal);
if (!skin) if (!skin)
skin = {player: 0, opponent: 0}; skin = {player: 0, opponent: 0};
const bar1 = createBarPlayer(availableSkins[skin.player]); const bar1 = createBarPlayer(availableSkins[skin.player]);
@ -89,6 +98,8 @@ class MultiOnlineGamePage
renderer.shadowMap.type = THREE.PCFSoftShadowMap; renderer.shadowMap.type = THREE.PCFSoftShadowMap;
renderer.domElement.style.animation = 'fadeOutStartGames 1s'; renderer.domElement.style.animation = 'fadeOutStartGames 1s';
renderer.domElement.style.filter = 'brightness(1)'; renderer.domElement.style.filter = 'brightness(1)';
vrMode();
opponent = new Opponent(bar2, map, Math.floor(Math.random() * 100 % 6)); opponent = new Opponent(bar2, map, Math.floor(Math.random() * 100 % 6));
player = new Player(bar1, map, opponent, Math.floor(Math.random() * 100 % 6), skin.goalId); player = new Player(bar1, map, opponent, Math.floor(Math.random() * 100 % 6), skin.goalId);
spotLight = new THREE.SpotLight(0xffffff, 10000, 0, 0.2); spotLight = new THREE.SpotLight(0xffffff, 10000, 0, 0.2);
@ -175,6 +186,8 @@ class MultiOnlineGamePage
static dispose() static dispose()
{ {
observer.disconnect();
VrButton = null;
window.removeEventListener('resize', windowUpdater); window.removeEventListener('resize', windowUpdater);
if (interval) if (interval)
clearInterval(interval); clearInterval(interval);
@ -298,7 +311,6 @@ function loop()
renderer.render(scene, player.camera); renderer.render(scene, player.camera);
} }
let lastFpsArr = [10, 3, 5];
function showFps() function showFps()
{ {
@ -316,4 +328,40 @@ function showFps()
lastFpsTime = now; lastFpsTime = now;
} }
export { MultiOnlineGamePage, player, opponent, ball, map}; function vrMode()
{
const supportsXR = 'xr' in window.navigator;
const newButton = configButton();
if (!supportsXR)
return ;
renderer.xr.enabled = true;
document.body.appendChild( VRButton.createButton(renderer) );
VrButton = document.getElementById('VRButton');
observer.observe(VrButton, { attributes: true });
if (VrButton.innerText !== 'VR NOT SUPPORTED')
document.body.append(newButton);
}
function configButton()
{
const newButton = document.createElement('button');
const cameraGroup = new THREE.Group();
cameraGroup.name = "vrHeadset";
newButton.innerText = "Vr mode";
newButton.setAttribute('id', 'newButtonVr');
newButton.addEventListener('click', () => {
VrButton.click();
scene.add(cameraGroup);
scene.remove(player.camera);
player.configureVrController();
cameraGroup.add(player.camera);
cameraGroup.position.set(0, 0.5, 7.5);
isInVrMode = true;
});
return (newButton);
}
export { MultiOnlineGamePage, player, opponent, ball, map, scene, renderer, isInVrMode };

View File

@ -150,14 +150,14 @@ function findNodes( motionController, scene ) {
// If the extents cannot be found, skip this animation // If the extents cannot be found, skip this animation
if ( ! visualResponse.minNode ) { if ( ! visualResponse.minNode ) {
console.warn( `Could not find ${minNodeName} in the model` ); // console.warn( `Could not find ${minNodeName} in the model` );
return; return;
} }
if ( ! visualResponse.maxNode ) { if ( ! visualResponse.maxNode ) {
console.warn( `Could not find ${maxNodeName} in the model` ); // console.warn( `Could not find ${maxNodeName} in the model` );
return; return;
} }

View File

@ -6,7 +6,7 @@
/* By: edbernar <edbernar@student.42angouleme. +#+ +:+ +#+ */ /* By: edbernar <edbernar@student.42angouleme. +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */ /* +#+#+#+#+#+ +#+ */
/* Created: 2024/10/01 13:42:29 by edbernar #+# #+# */ /* Created: 2024/10/01 13:42:29 by edbernar #+# #+# */
/* Updated: 2024/10/03 02:27:58 by edbernar ### ########.fr */ /* Updated: 2024/10/05 03:48:19 by edbernar ### ########.fr */
/* */ /* */
/* ************************************************************************** */ /* ************************************************************************** */
@ -38,7 +38,9 @@ class TournamentPage
divInfo = document.getElementsByClassName('infoo')[0]; divInfo = document.getElementsByClassName('infoo')[0];
divChat = document.getElementsByClassName('chat')[0]; divChat = document.getElementsByClassName('chat')[0];
document.getElementById('code-tournament').innerText = "Code : " + code; document.getElementById('code-tournament').innerText = "Code : " + code;
sendRequest("tournament", {action: 3});
divTopInfo.innerText = 'Tournament'; divTopInfo.innerText = 'Tournament';
initTournamentChat();
} }
static dispose() static dispose()
@ -103,7 +105,6 @@ class TournamentPage
while (i < playerNb.length - 1) while (i < playerNb.length - 1)
{ {
playerList['player' + playerNb[i]] = playerList['player' + playerNb[i + 1]]; playerList['player' + playerNb[i]] = playerList['player' + playerNb[i + 1]];
console.log(playerList['player' + playerNb[i]]);
document.getElementById('user-' + playerNb[i]).innerText = playerList['player' + playerNb[i]].username; document.getElementById('user-' + playerNb[i]).innerText = playerList['player' + playerNb[i]].username;
document.getElementById('pfp-' + playerNb[i]).style.backgroundImage = `url(${playerList['player' + playerNb[i]].pfp})`; document.getElementById('pfp-' + playerNb[i]).style.backgroundImage = `url(${playerList['player' + playerNb[i]].pfp})`;
i++; i++;
@ -121,6 +122,14 @@ class TournamentPage
divChat.appendChild(newText); divChat.appendChild(newText);
} }
static fetchAllData(content)
{
for (let i = 0; i < content.messages.length; i++)
this.newMessage(content.messages[i]);
for (let i = 0; i < content.players.length; i++)
this.newOpponent(content.players[i]);
}
static startGame(content) static startGame(content)
{ {
pageRenderer.changePage("waitingGamePage", false, {username: content.username, id: content.id, isTournament: true}) pageRenderer.changePage("waitingGamePage", false, {username: content.username, id: content.id, isTournament: true})
@ -136,4 +145,30 @@ function newInfo(message)
divInfo.appendChild(newDiv); divInfo.appendChild(newDiv);
} }
function initTournamentChat()
{
const inputMessage = document.getElementById('inputMessage');
const sendButton = document.getElementById("sendButton");
sendButton.style.cursor = "pointer";
sendButton.addEventListener("click", () => {
sendRequest("tournament", {action: 2, message: inputMessage.value});
inputMessage.value = "";
inputMessage.focus();
});
inputMessage.addEventListener("keyup", (event) => {
if (event.key === "Enter" && inputMessage.value.trim() !== "")
{
event.preventDefault();
sendRequest("tournament", {action: 2, message: inputMessage.value});
inputMessage.value = "";
inputMessage.focus();
}
});
inputMessage.addEventListener("keydown", (event) => {
if (event.key === "Enter")
event.preventDefault();
});
}
export { TournamentPage } export { TournamentPage }

View File

@ -6,7 +6,7 @@
/* By: edbernar <edbernar@student.42angouleme. +#+ +:+ +#+ */ /* By: edbernar <edbernar@student.42angouleme. +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */ /* +#+#+#+#+#+ +#+ */
/* Created: 2024/10/01 13:29:50 by edbernar #+# #+# */ /* Created: 2024/10/01 13:29:50 by edbernar #+# #+# */
/* Updated: 2024/10/03 01:17:26 by edbernar ### ########.fr */ /* Updated: 2024/10/05 03:02:38 by edbernar ### ########.fr */
/* */ /* */
/* ************************************************************************** */ /* ************************************************************************** */
@ -38,6 +38,8 @@ function typeTournament(content)
TournamentPage.newMessage(content); TournamentPage.newMessage(content);
else if (content.action == 4) else if (content.action == 4)
TournamentPage.startGame(content); TournamentPage.startGame(content);
else if (content.action == 5)
TournamentPage.fetchAllData(content);
} }
else else
console.warn("Request tournament not for this page..."); console.warn("Request tournament not for this page...");

View File

@ -6,7 +6,7 @@
/* By: edbernar <edbernar@student.42angouleme. +#+ +:+ +#+ */ /* By: edbernar <edbernar@student.42angouleme. +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */ /* +#+#+#+#+#+ +#+ */
/* Created: 2024/08/20 11:23:41 by edbernar #+# #+# */ /* Created: 2024/08/20 11:23:41 by edbernar #+# #+# */
/* Updated: 2024/09/29 13:28:28 by edbernar ### ########.fr */ /* Updated: 2024/10/05 02:14:49 by edbernar ### ########.fr */
/* */ /* */
/* ************************************************************************** */ /* ************************************************************************** */
@ -122,8 +122,8 @@ body {
.gamePad .gamePadLeft { .gamePad .gamePadLeft {
display: flex; display: flex;
position: absolute; position: absolute;
left: 50px; left: 30px;
bottom: 50px; bottom: 30px;
user-select: none; user-select: none;
-webkit-user-select: none; -webkit-user-select: none;
-ms-user-select: none; -ms-user-select: none;
@ -135,8 +135,8 @@ body {
display: flex; display: flex;
position: absolute; position: absolute;
flex-direction: column; flex-direction: column;
right: 50px; right: 30px;
bottom: 50px; bottom: 30px;
user-select: none; user-select: none;
-webkit-user-select: none; -webkit-user-select: none;
-ms-user-select: none; -ms-user-select: none;
@ -159,7 +159,7 @@ body {
} }
.gamePadLeft #padRight { .gamePadLeft #padRight {
margin-left: 50px; margin-left: 10px;
transform: rotate(180deg); transform: rotate(180deg);
user-select: none; user-select: none;
-webkit-user-select: none; -webkit-user-select: none;
@ -168,7 +168,7 @@ body {
} }
.gamePadRight #padTop { .gamePadRight #padTop {
margin-bottom: 20px; margin-bottom: 10px;
transform: rotate(90deg); transform: rotate(90deg);
user-select: none; user-select: none;
-webkit-user-select: none; -webkit-user-select: none;
@ -252,4 +252,16 @@ body {
color: white; color: white;
text-align: center; text-align: center;
margin-top: 50px; margin-top: 50px;
}
#newButtonVr {
position: absolute;
left: 10px;
top: 55px;
width: 152px;
height: 40px;
z-index: 900;
padding: 0;
color: white;
background-color: rgba(0, 0, 0, 0.367);
} }

View File

@ -36,6 +36,10 @@
- 9032 : Your opponent isn't online - 9032 : Your opponent isn't online
- 9033 : Skin id out of range - 9033 : Skin id out of range
- 9034 : missing field - 9034 : missing field
- 9035 : missing action parameter
- 9036 : already in a tournament
- 9037 : you're not in a tournament
- 9038 : missing message field
- 9100 : Action out of range - 9100 : Action out of range
- 9101 : No game started - 9101 : No game started