diff --git a/.gitignore b/.gitignore index 8d87b564..1cc82b66 100644 --- a/.gitignore +++ b/.gitignore @@ -18,7 +18,7 @@ Thumbs.db node_modules ./srcs/requirement/nestjs/api_back/dist -./srcs/requirements/svelte/api_front/public/build/* +./srcs/requirements/svelte/api_front/public/build/ # Logs logs diff --git a/Makefile b/Makefile index 10e8bccb..27e3080f 100644 --- a/Makefile +++ b/Makefile @@ -24,5 +24,15 @@ destroy: - docker images -aq | xargs --no-run-if-empty docker rmi -f - docker volume ls -q | xargs --no-run-if-empty docker volume rm +# temp for hugo, only reinit database +db: + - docker rm -f postgresql + - docker rm -f nestjs + - docker volume rm -f srcs_data_nest_postgresql + docker compose -f ${DOCKERCOMPOSEPATH} up -d --build + @make start + @docker ps + + stop: docker compose -f ${DOCKERCOMPOSEPATH} stop diff --git a/README.md b/README.md index 45ffa171..c952c5ab 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,3 @@ -CONFLICT srcs/requirements/nestjs/api_back/src/friendship/friendship.service.ts ### Pour lancer le docker : @@ -69,15 +68,25 @@ CONFLICT srcs/requirements/nestjs/api_back/src/friendship/friendship.service.ts #### chat : -- [ ] can create chat-rooms (public/private, password protected) -- [ ] send direct messages -- [ ] block other users -- [ ] creators of chat-room are owners, untill they leave -- [ ] chat-room owner can set, change, remove password -- [ ] chat-room owner is administrator and can set other administrators -- [ ] administrators can ban or mute for a time other users -- [ ] send game invitation in chat -- [ ] view user profiles from chat +- [/] create public room +- [ ] create private room +- [/] create direct room +- [/] chat in room +- [/] join public rooms +- [ ] join private rooms +- [ ] join direct rooms +- [/] see all joignable rooms +- [/] see all my rooms +- [/] leave room +- [ ] leave direct +- [ ] invite someone in room +- [ ] make admin +- [ ] ban +- [ ] mute +- [ ] protect room with password +- [ ] bock users +- [ ] send game invitation +- [ ] view user profiles #### game : diff --git a/make_env.sh b/make_env.sh index 7c218de4..35516df3 100755 --- a/make_env.sh +++ b/make_env.sh @@ -35,6 +35,7 @@ RESET="\033[0m" function make_env_for_docker_and_svelte { docker rm -f postgresql + docker rm -f nestjs docker volume rm -f srcs_data_nest_postgresql echo -e "${BOLD_BLUE}Creating a new environment for docker${RESET}" NODE_ENV="" diff --git a/srcs/requirements/game_server/game_back/src/server/wsServer.ts b/srcs/requirements/game_server/game_back/src/server/wsServer.ts index d3bab3d2..35923df1 100644 --- a/srcs/requirements/game_server/game_back/src/server/wsServer.ts +++ b/srcs/requirements/game_server/game_back/src/server/wsServer.ts @@ -94,10 +94,15 @@ async function clientAnnounceListener(this: WebSocket, data: string) "Content-Type": "application/json", }, body: JSON.stringify(body) - }); - if (!response.ok) + }) + .catch(error => console.log("ERROR : " + error)); + if (!response || !response.ok) { - this.send(JSON.stringify( new ev.EventError((await response.json()).message) )); + let errMessage = "validate token error"; + if (response) { + errMessage = (await response.json()).message; + } + this.send(JSON.stringify( new ev.EventError(errMessage) )); clientTerminate(clientsMap.get(this.id)); return; } @@ -154,6 +159,8 @@ function publicMatchmaking(player: ClientPlayer) const compatiblePlayers: ClientPlayer[] = []; compatiblePlayers.push(player); + + /* // Replace with this code to enable the possibility to play against self for (const [id, client] of matchmakingMap) { if (client.matchOptions === matchOptions) @@ -163,10 +170,9 @@ function publicMatchmaking(player: ClientPlayer) break; } } - } + } */ - // TODO: Replace with this code to disable the possibility to play against self -/* for (const [id, client] of matchmakingMap) + for (const [id, client] of matchmakingMap) { if (client.matchOptions === matchOptions && client.username !== player.username) { @@ -175,7 +181,7 @@ function publicMatchmaking(player: ClientPlayer) break; } } - } */ + } if (compatiblePlayers.length >= minPlayersNumber) { compatiblePlayers.forEach((client) => { @@ -231,7 +237,6 @@ function privateMatchmaking(player: ClientPlayer) token : player.token }) }) - .then(x => x.json()) .catch(error => console.log("ERROR : " + error)); clientTerminate(player); } @@ -252,18 +257,26 @@ function createGameSession(playersArr: ClientPlayer[], matchOptions: en.MatchOpt gameSession.unreadyPlayersMap.set(client.id, client); client.socket.once("message", playerReadyConfirmationListener); }); + + let gameSessionPlayersIterator = gameSession.playersMap.values(); + const eventMatchmakingComplete = new ev.EventMatchmakingComplete( + (gameSessionPlayersIterator.next().value).username, + (gameSessionPlayersIterator.next().value).username + ); // REFACTORING: Not pretty, hardcoded two players. // Could be done in gameSession maybe ? - const gameSessionPlayersIterator = gameSession.playersMap.values(); + gameSessionPlayersIterator = gameSession.playersMap.values(); let player: ClientPlayer; player = (gameSessionPlayersIterator.next().value); player.racket = gameSession.components.playerLeft; - player.socket.send(JSON.stringify( new ev.EventMatchmakingComplete(en.PlayerSide.left) )); + eventMatchmakingComplete.side = en.PlayerSide.left; + player.socket.send(JSON.stringify( eventMatchmakingComplete )); player = (gameSessionPlayersIterator.next().value); player.racket = gameSession.components.playerRight; - player.socket.send(JSON.stringify( new ev.EventMatchmakingComplete(en.PlayerSide.right) )); + eventMatchmakingComplete.side = en.PlayerSide.right; + player.socket.send(JSON.stringify( eventMatchmakingComplete )); // REFACTORING setTimeout(function abortMatch() { diff --git a/srcs/requirements/game_server/game_back/src/shared_js/class/Event.ts b/srcs/requirements/game_server/game_back/src/shared_js/class/Event.ts index 0147ead8..e5938e29 100644 --- a/srcs/requirements/game_server/game_back/src/shared_js/class/Event.ts +++ b/srcs/requirements/game_server/game_back/src/shared_js/class/Event.ts @@ -18,10 +18,13 @@ export class EventAssignId extends ServerEvent { } export class EventMatchmakingComplete extends ServerEvent { - side: en.PlayerSide; - constructor(side: en.PlayerSide) { + side: en.PlayerSide = en.PlayerSide.noSide; + playerOneUsername: string; + playerTwoUsername: string; + constructor(playerOneUsername: string, playerTwoUsername: string) { super(en.EventTypes.matchmakingComplete); - this.side = side; + this.playerOneUsername = playerOneUsername; + this.playerTwoUsername = playerTwoUsername; } } diff --git a/srcs/requirements/nestjs/api_back/src/chat/chat.controller.ts b/srcs/requirements/nestjs/api_back/src/chat/chat.controller.ts index 149f9893..12c8a9b1 100644 --- a/srcs/requirements/nestjs/api_back/src/chat/chat.controller.ts +++ b/srcs/requirements/nestjs/api_back/src/chat/chat.controller.ts @@ -1,84 +1,147 @@ -import { Controller, UseGuards, HttpException, HttpStatus, Get, Post, Body, Req, Res } from '@nestjs/common'; +import { Controller, UseGuards, HttpException, HttpStatus, Get, Post, Delete, Body, Req, Res } from '@nestjs/common'; import { AuthenticateGuard, TwoFactorGuard } from 'src/auth/42/guards/42guards'; import { ConnectedSocket } from '@nestjs/websockets'; import { ChatService } from './chat.service'; import { User } from 'src/users/entities/user.entity'; import { PartialUsersDto } from 'src/users/dto/partial-users.dto'; -import { createRoomDto } from './dto/createRoom.dto'; -import { joinRoomDto } from './dto/joinRoom.dto'; +import { roomDto } from './dto/room.dto'; import { setCurrentRoomDto } from './dto/setCurrentRoom.dto'; +import { ChatGateway } from './chat.gateway'; +import { socketDto } from './dto/socket.dto'; +import { Chatroom } from './entities/chatroom.entity'; @Controller('chat') export class ChatController { constructor( private chatService: ChatService, + private chatGateway: ChatGateway, ) {} + // don't allow '+' because it's used in direct rooms name + private allowed_chars = '-#!?_'; + private escape_chars(str) + { + return str.split("").join("\\"); + } + + @UseGuards(AuthenticateGuard) @UseGuards(TwoFactorGuard) @Get('myrooms') - async getMyRooms(@Req() req, @Res() res): Promise + async getMyRooms(@Req() req, @Res() res): Promise { console.log("- in getMyRooms controller"); - const rooms = await this.chatService.getMyRooms(req.user); + + let fields = ["name", "type", "users"]; + const rooms = await this.chatService.getMyRooms(req.user.username, fields); + + res.status(HttpStatus.OK).json({ rooms: rooms }); + console.log("- out getMyRooms controller"); - return res.status(HttpStatus.OK).json({ rooms: rooms }); } @UseGuards(AuthenticateGuard) @UseGuards(TwoFactorGuard) @Get('allrooms') - async getAllRooms(@Req() req, @Res() res): Promise + async getAllRooms(@Req() req, @Res() res): Promise { console.log("- in getAllRooms controller"); - const rooms = await this.chatService.getAllNotMyRooms(req.user); + + const rooms: roomDto[] = await this.chatService.getAllOtherRoomsAndUsers(req.user.username) + console.log("--- rooms:", rooms); + res.status(HttpStatus.OK).json({ rooms: rooms }); + console.log("- out getAllRooms controller"); - return res.status(HttpStatus.OK).json({ rooms: rooms }); } @UseGuards(AuthenticateGuard) @UseGuards(TwoFactorGuard) @Get('current') - async setCurrentRoom(@Body() setCurrentRoomDto: setCurrentRoomDto, @Req() req, @Res() res): Promise + async setCurrentRoom(@Body() setCurrentRoomDto: setCurrentRoomDto, @Req() req, @Res() res): Promise { console.log("- in setCurrentRoom controller"); - const response = await this.chatService.setCurrentRoom(req.user, setCurrentRoomDto.name); + + const response = await this.chatService.setCurrentRoom(req.user.username, setCurrentRoomDto.name); + res.status(HttpStatus.OK).json({ message: response }); + console.log("- out setCurrentRoom controller"); - return res.status(HttpStatus.OK).json({ message: response }); + } + + @UseGuards(AuthenticateGuard) + @UseGuards(TwoFactorGuard) + @Get('allowedchars') + async allowedChars(@Res() res): Promise + { + console.log("- in allowedChars controller"); + + res.status(HttpStatus.OK).json({ chars: this.allowed_chars }); + + console.log("- out allowedChars controller"); } @UseGuards(AuthenticateGuard) @UseGuards(TwoFactorGuard) @Post('create') - async createRoom(@Body() createRoomDto: createRoomDto, @Req() req, @Res() res): Promise + async createRoom(@Body() room: roomDto, @Req() req, @Res() res): Promise { console.log("- in createRoom controller"); - const response = await this.chatService.addUserToNewRoom(req.user, createRoomDto); + + let chars = this.escape_chars(this.allowed_chars); + let regex_base = `[a-zA-Z0-9\\s${chars}]`; + let test_regex = new RegExp(`^${regex_base}+$`); + if (test_regex.test(room.name) === false) + { + let forbidden_chars = room.name.replace(new RegExp(regex_base, "g"), ""); + throw new HttpException(`Your room name can not contains these characters : ${forbidden_chars}`, HttpStatus.UNPROCESSABLE_ENTITY); + } + + await this.chatService.addUserToNewRoom(req.user.username, room); + res.status(HttpStatus.OK).json({ room: room }); + console.log("- out createRoom controller"); - return res.status(HttpStatus.OK).json({ room_name: createRoomDto.room_name, message: response }); } @UseGuards(AuthenticateGuard) @UseGuards(TwoFactorGuard) @Post('join') - async joinRoom(@Body() joinRoomDto: joinRoomDto, @Req() req, @Res() res): Promise + async joinRoom(@Body() room: roomDto, @Req() req, @Res() res): Promise { console.log("- in joinRoom controller"); - const response = await this.chatService.addUserToRoom(req.user, joinRoomDto); + + let response = ""; + if (room.type === 'direct') + throw new HttpException(`cannot join a direct messages room`, HttpStatus.CONFLICT); + else if (room.type === 'user') + { + room.type = 'direct'; + room.users = [room.name, req.user.username]; + room.name += ` + ${req.user.username}`; + await this.chatService.addUserToNewRoom(req.user.username, room); + } + else + await this.chatService.addUserToRoom(req.user.username, room.name); + + let socket: socketDto = this.chatGateway.sockets.get(req.user.username); + await this.chatService.socketJoinRoom(socket, room.name); + res.status(HttpStatus.OK).json({ room: room }); + console.log("- out joinRoom controller"); - return res.status(HttpStatus.OK).json({ room_name: joinRoomDto.room_name, message: response }); } @UseGuards(AuthenticateGuard) @UseGuards(TwoFactorGuard) @Post('change') - async changeRoom(@Body() joinRoomDto: joinRoomDto, @Req() req, @Res() res): Promise + async changeRoom(@Body() room: roomDto, @Req() req, @Res() res): Promise { console.log("- in changeRoom controller"); - const response = await this.chatService.setCurrentRoom(req.user, joinRoomDto.room_name); + + const response = await this.chatService.setCurrentRoom(req.user.username, room.name); + let socket: socketDto = this.chatGateway.sockets.get(req.user.username); + await this.chatService.socketChangeRoom(socket, room.name); + res.status(HttpStatus.OK).json({ room: room }); + console.log("- out changeRoom controller"); - return res.status(HttpStatus.OK).json({ room_name: joinRoomDto.room_name, message: response }); } @UseGuards(AuthenticateGuard) @@ -93,12 +156,43 @@ export class ChatController { @UseGuards(AuthenticateGuard) @UseGuards(TwoFactorGuard) @Get('messages') - async getMessages(@Req() req, @Res() res): Promise + async getMessages(@Req() req, @Res() res): Promise { console.log("- in getMessages controller"); - const messages = await this.chatService.getMessagesFromCurrentRoom(req.user); + + const messages = await this.chatService.getMessagesFromCurrentRoom(req.user.username); + res.status(HttpStatus.OK).json({ messages: messages }); + console.log("- out getMessages controller"); - return res.status(HttpStatus.OK).json({ messages: messages }); + } + + @UseGuards(AuthenticateGuard) + @UseGuards(TwoFactorGuard) + @Get('roomusers') + async getRoomUsers(@Req() req, @Res() res): Promise + { + console.log("- in getRoomUsers controller"); + + const room_name = await this.chatService.getCurrentRoomName(req.user.username); + const room = await this.chatService.getRoomByName(room_name); + const users = room.users; + res.status(HttpStatus.OK).json({ users: users }); + + console.log("- out getRoomUsers controller"); + } + + @UseGuards(AuthenticateGuard) + @UseGuards(TwoFactorGuard) + @Delete('removeuser') + async removeUser(@Req() req, @Res() res): Promise + { + console.log("- in removeUser controller"); + + const room_name = await this.chatService.getCurrentRoomName(req.user.username); + let response = await this.chatService.removeUserFromRoom(req.user.username, room_name); + res.status(HttpStatus.OK).json({ message: response }); + + console.log("- out removeUser controller"); } } diff --git a/srcs/requirements/nestjs/api_back/src/chat/chat.gateway.ts b/srcs/requirements/nestjs/api_back/src/chat/chat.gateway.ts index 5b6e257b..b3963f1b 100644 --- a/srcs/requirements/nestjs/api_back/src/chat/chat.gateway.ts +++ b/srcs/requirements/nestjs/api_back/src/chat/chat.gateway.ts @@ -1,14 +1,14 @@ import { WebSocketGateway, SubscribeMessage, WebSocketServer, MessageBody, ConnectedSocket, OnGatewayConnection, OnGatewayDisconnect } from '@nestjs/websockets'; import { UsersService } from 'src/users/users.service'; -import { PaginationQueryDto } from 'src/common/dto/pagination-query.dto'; import { ChatService } from './chat.service'; +import { socketDto } from './dto/socket.dto'; @WebSocketGateway(5000, { path: '/chat', }) export class ChatGateway - implements OnGatewayConnection, OnGatewayDisconnect +implements OnGatewayConnection, OnGatewayDisconnect { constructor ( @@ -16,40 +16,44 @@ export class ChatGateway private chatService: ChatService, ) {} + sockets = new Map(); + @WebSocketServer() server; - - // how to guard the handleConnection ? - // https://github.com/nestjs/nest/issues/882 - async handleConnection(client) { - console.log('- Client connected :', client.id, client.handshake.query.username); - client.username = client.handshake.query.username; + async handleConnection(socket: socketDto) { + console.log('- socket connected :', socket.id, socket.handshake.query.username); + socket.username = socket.handshake.query.username.toString(); + this.sockets.set(socket.username, socket); } - async handleDisconnect(client) { - console.log('- Client disconnected :', client.id, client.username); + async handleDisconnect(socket: socketDto) { + this.sockets.delete(socket.username); } @SubscribeMessage('join') - async joinRoom(@ConnectedSocket() socket, @MessageBody() room_name: string): Promise + async joinRoom(@ConnectedSocket() socket: socketDto, @MessageBody() room_name: string): Promise { console.log('- in joinRoom gateway'); socket.leave(socket.room); socket.join(room_name); socket.room = room_name; + let message = `${socket.username} has join the room`; + await socket.to(socket.room).emit('message', "SERVER", message); + await this.chatService.addMessageToRoom(room_name, "SERVER", message); + } - console.log('- out joinRoom gateway'); + @SubscribeMessage('change') + async changeRoom(@ConnectedSocket() socket: socketDto, @MessageBody() room_name: string): Promise + { + console.log('- in changeRoom gateway'); + await this.chatService.socketChangeRoom(socket, room_name); } @SubscribeMessage('message') - async handleMessage(@ConnectedSocket() socket, @MessageBody() message: string): Promise + async handleMessage(@ConnectedSocket() socket: socketDto, @MessageBody() message: string): Promise { console.log('- in handleMessage gateway'); - //let room_name = await this.chatService.getCurrentRoom(socket.username); - socket.to(socket.room).emit('message', socket.username, message); - this.chatService.addMessageToCurrentRoom(socket.username, message); - - console.log('- out handleMessage gateway'); + await this.chatService.socketIncommingMessage(socket, message); } } diff --git a/srcs/requirements/nestjs/api_back/src/chat/chat.module.ts b/srcs/requirements/nestjs/api_back/src/chat/chat.module.ts index 326abb7c..1ab18c9d 100644 --- a/srcs/requirements/nestjs/api_back/src/chat/chat.module.ts +++ b/srcs/requirements/nestjs/api_back/src/chat/chat.module.ts @@ -3,7 +3,6 @@ import { ChatController } from './chat.controller'; import { ChatService } from './chat.service'; import { ChatGateway } from './chat.gateway'; import { UsersModule } from 'src/users/users.module'; - import { TypeOrmModule } from '@nestjs/typeorm'; import { Chatroom } from './entities/chatroom.entity'; import { User } from 'src/users/entities/user.entity'; diff --git a/srcs/requirements/nestjs/api_back/src/chat/chat.service.ts b/srcs/requirements/nestjs/api_back/src/chat/chat.service.ts index 52ade59f..a59720db 100644 --- a/srcs/requirements/nestjs/api_back/src/chat/chat.service.ts +++ b/srcs/requirements/nestjs/api_back/src/chat/chat.service.ts @@ -4,9 +4,10 @@ import { UsersService } from 'src/users/users.service'; import { Chatroom } from './entities/chatroom.entity'; import { Repository } from 'typeorm'; import { InjectRepository } from '@nestjs/typeorm'; -import { createRoomDto } from './dto/createRoom.dto'; -import { joinRoomDto } from './dto/joinRoom.dto'; +import { roomDto } from './dto/room.dto'; import { messagesDto } from './dto/messages.dto'; +import { socketDto } from './dto/socket.dto'; + @Injectable() export class ChatService { @@ -17,8 +18,7 @@ export class ChatService { private readonly userRepository: Repository, @InjectRepository(Chatroom) private readonly chatroomRepository: Repository, - ) { } - + ) {} // temp for test sleep(ms) { @@ -29,86 +29,140 @@ export class ChatService { /* GETTERS ************************************************ */ - async getMyRooms(user: User) + async getMyRooms(username: string, fieldsToReturn: string[] = null): Promise { console.log("-- in getMyRooms service"); - const rooms = await this.chatroomRepository + + const queryBuilder = this.chatroomRepository .createQueryBuilder('chatroom') - .where('chatroom.users LIKE :user_name', { user_name: `%${user.username}%` }) - .getMany(); + .where('chatroom.users LIKE :user_name', { user_name: `%${username}%` }); + + if (fieldsToReturn) + { + let fields = fieldsToReturn.map(field => `chatroom.${field}`); + queryBuilder.select(fields); + } + + const rooms = await queryBuilder.getMany(); + console.log("--- rooms:", rooms); console.log("-- out getMyRooms service"); return rooms; } - async getAllRooms() + async getMyDirects(username: string): Promise + { + console.log("-- in getAllNotMyRooms service"); + + const my_rooms = await this.getMyRooms(username); + const directs = my_rooms.filter(room => room.type === 'direct'); + + console.log("-- out getAllNotMyRooms service"); + return directs; + } + + async getAllRooms(): Promise { console.log("-- in getAllRooms service"); + const rooms = await this.chatroomRepository .createQueryBuilder('chatroom') .getMany(); + console.log("--- rooms:", rooms); console.log("-- out getAllRooms service"); return rooms; } - async getAllNotMyRooms(user: User) + async getAllNotMyRooms(username: string): Promise { console.log("-- in getAllNotMyRooms service"); - const user_db = await this.getUserByName(user.username); - //const user_db = await this.usersService.findOne(user.username); + + const user_db = await this.getUserByName(username); const rooms = await this.chatroomRepository .createQueryBuilder('chatroom') .where('chatroom.type != :type', { type: 'private' }) - .andWhere('chatroom.users NOT LIKE :user_name', { user_name: `%${user.username}%` }) + .andWhere('chatroom.users NOT LIKE :user_name', { user_name: `%${username}%` }) .getMany(); - //const users = await this.getAllUsers(); - //let allRooms = [...rooms, ...users]; + console.log("--- rooms:", rooms); console.log("-- out getAllNotMyRooms service"); return rooms; } - async getMessagesFromCurrentRoom(user: User) + async getAllOtherRoomsAndUsers(username: string): Promise { - console.log("-- in getMessagesFromCurrentRoom service"); - const user_db = await this.getUserByName(user.username); - //const user_db = await this.usersService.findOne(user.username); - const currentRoom = await this.getRoomByName(user_db.currentRoom); + console.log("-- in getAllOtherRoomsAndUsers service"); - console.log("-- out getMessagesFromCurrentRoom service"); - return currentRoom.messages; + const all_rooms = await this.getAllNotMyRooms(username); + const all_users = await this.getAllUsersNotMyRooms(username); + + let row_rooms = all_rooms.map(room => { + return { + name: room.name, + type: room.type, + }; + }); + let users = all_users.map(user => { + return { + name: user.username, + type: "user", + }; + }); + let rooms = row_rooms.concat(users); + console.log("--- rooms:", rooms); + + console.log("-- in getAllOtherRoomsAndUsers service"); + return rooms; } - async getCurrentRoom(username: string) + async getMessagesFromCurrentRoom(username: string): Promise { - console.log("-- in getCurrentRoom service"); - const user_db = await this.getUserByName(username); - //const user_db = await this.usersService.findOne(username); + console.log("-- in getMessagesFromCurrentRoom service"); - console.log("-- out getCurrentRoom service"); + const user_db = await this.getUserByName(username); + const currentRoom = await this.getRoomByName(user_db.currentRoom); + let messages = null; + if (currentRoom) + messages = currentRoom.messages; + + console.log("-- out getMessagesFromCurrentRoom service"); + return messages; + } + + async getCurrentRoomName(username: string): Promise + { + console.log("-- in getCurrentRoomName service"); + + const user_db = await this.getUserByName(username); + + console.log("-- out getCurrentRoomName service"); return user_db.currentRoom; } - async getRoomByName(name: string) + async getRoomByName(room_name: string): Promise { console.log("-- in getRoomByName service"); + const room = await this.chatroomRepository .createQueryBuilder('chatroom') - .where('chatroom.name = :name', { name: name }) + .where('chatroom.name = :name', { name: room_name }) .getOne(); + console.log("--- room:", room); console.log("-- out getRoomByName service"); return room; } - async getRoomById(id: number) + async getRoomById(id: number): Promise { console.log("-- in getRoomById service"); + const room = await this.chatroomRepository .createQueryBuilder('chatroom') .where('chatroom.id = :id', { id: id }) .getOne(); + console.log("--- room:", room); console.log("-- out getRoomById service"); return room; @@ -118,102 +172,180 @@ export class ChatService { /* SETTERS ************************************************ */ - async setCurrentRoom(user: User, name: string) + async setCurrentRoom(username: string, room_name: string): Promise { console.log("-- in setCurrentRoom service"); - const user_db = await this.getUserByName(user.username); - //const user_db = await this.usersService.findOne(user.username); - user_db.currentRoom = name; + + const user_db = await this.getUserByName(username); + user_db.currentRoom = room_name; this.userRepository.save(user_db); console.log("-- out setCurrentRoom service"); - return `room "${name}" is now current room`; + return `room "${room_name}" is now current room`; } /* ADDERS ************************************************* */ - async addUserToNewRoom(user: User, createRoomDto: createRoomDto) + async addUserToNewRoom(username: string, room: roomDto): Promise { - console.log("-- in addUserToRoom service"); - const room = await this.getRoomByName(createRoomDto.room_name); - if (room) - throw new HttpException(`This room already exist`, HttpStatus.CONFLICT); + console.log("-- in addUserToNewRoom service"); + + const find_room = await this.getRoomByName(room.name); + if (find_room) + throw new HttpException(`This room name already exist`, HttpStatus.CONFLICT); // create chatroom const newChatroom = new Chatroom(); - newChatroom.name = createRoomDto.room_name; - newChatroom.type = createRoomDto.room_type; - newChatroom.owner = user.username; - newChatroom.users = [user.username]; - newChatroom.messages = [{ name: "SERVER", message: `creation of room ${createRoomDto.room_name}` }]; - this.chatroomRepository.save(newChatroom); + newChatroom.name = room.name; + newChatroom.type = room.type; + newChatroom.owner = username; + newChatroom.users = [username]; + if (room.type === 'direct') + newChatroom.users = room.users; + newChatroom.messages = [{ name: "SERVER", message: `creation of room ${room.name}` }]; + await this.chatroomRepository.save(newChatroom); - console.log("-- out addUserToRoom service"); - return "successfull room creation"; + console.log("-- out addUserToNewRoom service"); } - async addUserToRoom(user: User, joinRoomDto: joinRoomDto) + async addUserToRoom(username: string, room_name: string): Promise { console.log("-- in addUserToRoom service"); - const room = await this.getRoomByName(joinRoomDto.room_name); - if (room.users.includes(user.username)) - throw new HttpException(`your have already join this room`, HttpStatus.CONFLICT); + + const room = await this.getRoomByName(room_name); + if (room.users.includes(username)) + throw new HttpException(`your have already joined this room`, HttpStatus.CONFLICT); // update room with new user - room.users.push(user.username); + room.users.push(username); this.chatroomRepository.save(room); - const rooms = await this.getMyRooms(user); - const allRooms = await this.getAllRooms(); - console.log("-- out addUserToRoom service"); - return "successfull joining room"; } - async addMessageToCurrentRoom(username: string, message: string) + async addMessageToRoom(room_name: string, username: string, message: string): Promise { - console.log("-- in addMessageToCurrentRoom service"); - const user_db = await this.getUserByName(username); - //const user_db = await this.usersService.findOne(username); - const currentRoom = await this.getRoomByName(user_db.currentRoom); + console.log("-- in addMessageToRoom service"); + + const my_room = await this.getRoomByName(room_name); let chat_message = { name: username, message: message, }; - currentRoom.messages.push(chat_message); - this.chatroomRepository.save(currentRoom); + my_room.messages.push(chat_message); + this.chatroomRepository.save(my_room); - console.log("-- out addMessageToCurrentRoom service"); + console.log("-- out addMessageToRoom service"); } /* REMOVERS *********************************************** */ - async removeUserFromRoom(user: User, room_name: string) + async removeUserFromRoom(username: string, room_name: string): Promise { console.log("-- in removeUserFromRoom service"); - // get room - // remove user + + const room = await this.getRoomByName(room_name); + if (!room.users.includes(username)) + throw new HttpException(`your are not in this room`, HttpStatus.CONFLICT); + + // delete user from room + room.users.push(username); + room.users = room.users.filter(name => name !== username); + this.chatroomRepository.save(room); + + // set current room to nothing + await this.setCurrentRoom(username, ""); + + console.log("-- out removeUserFromRoom service"); + return "successfully leaving room"; } /* SEARCH IN USER ***************************************** */ - async getUserByName(name: string) + async getUserByName(username: string): Promise { console.log("-- in getUserByName service"); + const user = await this.userRepository .createQueryBuilder('user') - .where('user.username = :name', { name: name }) + .where('user.username = :name', { name: username }) .getOne(); + console.log("--- user:", user); console.log("-- out getUserByName service"); return user; } + async getAllUsersNotMyRooms(username: string): Promise + { + console.log("-- in getAllUsersNotMyRooms service"); + + const directs = await this.getMyDirects(username); + + // get all users from directs + let usernames = directs.map(room => { + let user = room.users[0]; + if (user === username) + user = room.users[1]; + return user; + }); + usernames.push(username); + + const users = await this.userRepository + .createQueryBuilder('user') + .where('user.username NOT IN (:...usernames)', { usernames: usernames }) + .getMany(); + console.log("--- users:", users); + + console.log("-- out getAllUsersNotMyRooms service"); + return users; + } + + + /* GATEWAY EVENTS ***************************************** + */ + + async socketIncommingMessage(socket: socketDto, message: string): Promise + { + console.log("-- in handleSocketIncommingMessage service"); + + socket.to(socket.room).emit('message', socket.username, message); + let room_name = await this.getCurrentRoomName(socket.username); + await this.addMessageToRoom(room_name, socket.username, message); + + console.log("-- out handleSocketIncommingMessage service"); + } + + async socketChangeRoom(socket: socketDto, room_name: string): Promise + { + console.log('-- in socketChangeRoom service'); + + socket.leave(socket.room); + socket.join(room_name); + socket.room = room_name; + + console.log('-- out socketChangeRoom service'); + } + + async socketJoinRoom(socket: socketDto, room_name: string): Promise + { + console.log('- in socketJoinRoom service'); + + socket.leave(socket.room); + socket.join(room_name); + socket.room = room_name; + let message = `${socket.username} has join the room`; + await socket.to(socket.room).emit('message', "SERVER", message); + await this.addMessageToRoom(room_name, "SERVER", message); + + console.log('- out socketJoinRoom service'); + } + } diff --git a/srcs/requirements/nestjs/api_back/src/chat/dto/createRoom.dto.ts b/srcs/requirements/nestjs/api_back/src/chat/dto/createRoom.dto.ts deleted file mode 100644 index 69b5543e..00000000 --- a/srcs/requirements/nestjs/api_back/src/chat/dto/createRoom.dto.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { IsBoolean, IsEmpty, IsInt, IsNotEmpty, IsNumber, IsString, IsOptional } from "class-validator"; -import { IsNull } from "typeorm"; - -export class createRoomDto -{ - @IsString() - @IsNotEmpty() - room_name: string; - - @IsString() - @IsNotEmpty() - room_type: 'public' | 'private' | 'direct'; - - @IsString() - @IsOptional() - room_password: string; -} - diff --git a/srcs/requirements/nestjs/api_back/src/chat/dto/messages.dto.ts b/srcs/requirements/nestjs/api_back/src/chat/dto/messages.dto.ts index 4933f230..53e25a2a 100644 --- a/srcs/requirements/nestjs/api_back/src/chat/dto/messages.dto.ts +++ b/srcs/requirements/nestjs/api_back/src/chat/dto/messages.dto.ts @@ -1,9 +1,13 @@ -import { IsBoolean, IsEmpty, IsInt, IsNotEmpty, IsNumber, IsString, IsOptional, IsArray } from "class-validator"; -import { IsNull } from "typeorm"; +import { IsString, IsOptional } from "class-validator"; export class messagesDto { - @IsArray() - messages: { name: string; message: string }[]; + @IsString() + @IsOptional() + name: string; + + @IsString() + @IsOptional() + message: string; } diff --git a/srcs/requirements/nestjs/api_back/src/chat/dto/room.dto.ts b/srcs/requirements/nestjs/api_back/src/chat/dto/room.dto.ts new file mode 100644 index 00000000..6dcf8099 --- /dev/null +++ b/srcs/requirements/nestjs/api_back/src/chat/dto/room.dto.ts @@ -0,0 +1,19 @@ +import { IsBoolean, IsEmpty, IsInt, IsIn, IsNotEmpty, IsNumber, IsArray, IsString, IsOptional, IsEnum } from "class-validator"; + +export class roomDto +{ + @IsString() + @IsNotEmpty() + name: string; + + @IsString() + @IsNotEmpty() + @IsIn(["public", "protected", "private", "direct", "user"]) + type: string; + + @IsArray() + @IsString({ each: true }) + @IsOptional() + users?: string[]; // usernames +} + diff --git a/srcs/requirements/nestjs/api_back/src/chat/dto/setCurrentRoom.dto.ts b/srcs/requirements/nestjs/api_back/src/chat/dto/setCurrentRoom.dto.ts index f5d0630a..41175023 100644 --- a/srcs/requirements/nestjs/api_back/src/chat/dto/setCurrentRoom.dto.ts +++ b/srcs/requirements/nestjs/api_back/src/chat/dto/setCurrentRoom.dto.ts @@ -1,5 +1,4 @@ import { IsBoolean, IsEmpty, IsInt, IsNotEmpty, IsNumber, IsString, IsOptional } from "class-validator"; -import { IsNull } from "typeorm"; export class setCurrentRoomDto { diff --git a/srcs/requirements/nestjs/api_back/src/chat/dto/joinRoom.dto.ts b/srcs/requirements/nestjs/api_back/src/chat/dto/socket.dto.ts similarity index 50% rename from srcs/requirements/nestjs/api_back/src/chat/dto/joinRoom.dto.ts rename to srcs/requirements/nestjs/api_back/src/chat/dto/socket.dto.ts index 8c81a93d..abc5350e 100644 --- a/srcs/requirements/nestjs/api_back/src/chat/dto/joinRoom.dto.ts +++ b/srcs/requirements/nestjs/api_back/src/chat/dto/socket.dto.ts @@ -1,10 +1,13 @@ import { IsBoolean, IsEmpty, IsInt, IsNotEmpty, IsNumber, IsString, IsOptional } from "class-validator"; -import { IsNull } from "typeorm"; +import { Socket } from 'socket.io'; -export class joinRoomDto +export class socketDto extends Socket { @IsString() - @IsNotEmpty() - room_name: string; + username: string; + + @IsString() + room: string; } + diff --git a/srcs/requirements/nestjs/api_back/src/chat/entities/chatroom.entity.ts b/srcs/requirements/nestjs/api_back/src/chat/entities/chatroom.entity.ts index 76810c91..89e8ff18 100644 --- a/srcs/requirements/nestjs/api_back/src/chat/entities/chatroom.entity.ts +++ b/srcs/requirements/nestjs/api_back/src/chat/entities/chatroom.entity.ts @@ -1,38 +1,36 @@ -import { - Entity, - Column, - ManyToOne, - ManyToMany, - JoinTable, - PrimaryGeneratedColumn -} from "typeorm"; +import { Entity, Column, ManyToOne, ManyToMany, JoinTable, PrimaryGeneratedColumn } from "typeorm"; +import { IsBoolean, IsEmpty, IsInt, IsIn, IsNotEmpty, IsNumber, IsArray, IsString, IsOptional, IsEnum } from "class-validator"; +import { Exclude, Expose } from 'class-transformer'; import { User } from 'src/users/entities/user.entity'; +import { messagesDto } from 'src/chat/dto/messages.dto'; @Entity('chatroom') -export class Chatroom { +export class Chatroom +{ @PrimaryGeneratedColumn() id: number; @Column() + @IsString() + @IsNotEmpty() name: string; - @Column() - type: string; - -// @ManyToOne(type => User, user => user.ownedRooms) -// owner: User; -// -// @ManyToMany(type => User) -// @JoinTable() -// users: User[]; + @Column() + @IsString() + @IsNotEmpty() + @IsIn(["public", "protected", "private", "direct", "user"]) + type: string; @Column() - owner: string; // name + owner: string; // username @Column("simple-array") - users: string[]; // names + @IsArray() + @IsString({ each: true }) + @IsOptional() + users?: string[]; // usernames @Column("json") - messages: { name: string, message: string }[]; + messages: messagesDto[]; } diff --git a/srcs/requirements/nestjs/api_back/src/game/game.service.ts b/srcs/requirements/nestjs/api_back/src/game/game.service.ts index 0f2d7556..a6f7cd02 100644 --- a/srcs/requirements/nestjs/api_back/src/game/game.service.ts +++ b/srcs/requirements/nestjs/api_back/src/game/game.service.ts @@ -92,7 +92,8 @@ export class GameService { } this.userRepository.save(user); } - if (grantTicketDto.isGameIsWithInvitation === true && user.status !== STATUS.IN_GAME) + // if (grantTicketDto.isGameIsWithInvitation === true && user.status !== STATUS.IN_GAME) // WIP: need to fix STATUS.IN_GAME + if (grantTicketDto.isGameIsWithInvitation === true) { const secondUser : Partial = await this.userService.findOne(grantTicketDto.playerTwoUsername) if (!secondUser || secondUser.username === user.username) @@ -104,17 +105,18 @@ export class GameService { tok.numberOfRegisteredUser = 0; tok.token = encryptedTextToReturn; this.tokenGameRepository.save(tok); - this.userService.updateStatus(user.id, "In Pool") + this.userService.updateStatus(user.id, STATUS.IN_POOL) return res.status(HttpStatus.OK).json({ token : encryptedTextToReturn }); } - else if (grantTicketDto.isGameIsWithInvitation === false && user.status !== STATUS.IN_GAME) { + // else if (grantTicketDto.isGameIsWithInvitation === false && user.status !== STATUS.IN_GAME) { // WIP: need to fix STATUS.IN_GAME + else if (grantTicketDto.isGameIsWithInvitation === false) { const encryptedTextToReturn = await this.encryptToken(user.username + '_' + grantTicketDto.gameOptions + '_' + grantTicketDto.isGameIsWithInvitation + '_' + new Date()) const tok = this.tokenGameRepository.create(grantTicketDto); tok.numberOfRegisteredUser = 0; tok.token = encryptedTextToReturn; this.tokenGameRepository.save(tok); - this.userService.updateStatus(user.id, "In Pool") + this.userService.updateStatus(user.id, STATUS.IN_POOL) return res.status(HttpStatus.OK).json({ token : encryptedTextToReturn }); } return res.status(HttpStatus.INTERNAL_SERVER_ERROR).json({message : "Internal Server Error"}); @@ -140,13 +142,13 @@ export class GameService { const userOne : User = await this.userRepository.createQueryBuilder('user') .where("user.username = :username", {username : tokenGame.playerOneUsername}) .getOne(); - this.userService.updateStatus(userOne.id, "In Game") + this.userService.updateStatus(userOne.id, STATUS.IN_GAME) const userTwo : User = await this.userRepository.createQueryBuilder('user') .where("user.username = :username", {username : tokenGame.playerTwoUsername}) .getOne(); this.deleteToken(userOne) this.deleteToken(userTwo) - this.userService.updateStatus(userTwo.id, "In Game") + this.userService.updateStatus(userTwo.id, STATUS.IN_GAME) } return true; } @@ -166,7 +168,7 @@ export class GameService { const user : User = await this.userRepository.createQueryBuilder('user') .where("user.username = :username", {username : tokenGame.playerOneUsername}) .getOne(); - this.userService.updateStatus(user.id, "In Game") + this.userService.updateStatus(user.id, STATUS.IN_GAME) this.deleteToken(user) return true; } @@ -196,7 +198,7 @@ export class GameService { async declineInvitation(user : User, token : string, @Res() res : Response) { - if (user.status !== "Connected") + if (user.status !== STATUS.CONNECTED) return res.status(HttpStatus.FORBIDDEN).json({message : "You must not be in game to decline an invitation"}); console.log("On décline l'invitation") const tokenGame = await this.tokenGameRepository.createQueryBuilder('tokengame') @@ -221,10 +223,10 @@ export class GameService { { const playerOne = await this.userRepository.findOneBy({username : tokenGame.playerOneUsername}) const playerTwo = await this.userRepository.findOneBy({username : tokenGame.playerTwoUsername}) - if (playerOne.status !== "Disconnected") - this.userService.updateStatus(playerOne.id, "Connected") - if (playerTwo.status !== "Disconnected") - this.userService.updateStatus(playerTwo.id, "Connected") + if (playerOne.status !== STATUS.DISCONNECTED) + this.userService.updateStatus(playerOne.id, STATUS.CONNECTED) + if (playerTwo.status !== STATUS.DISCONNECTED) + this.userService.updateStatus(playerTwo.id, STATUS.CONNECTED) return this.tokenGameRepository.remove(tokenGame); } return new HttpException("Token not found !", HttpStatus.NOT_FOUND) @@ -232,7 +234,7 @@ export class GameService { async acceptInvitation(user : User, token : string, @Res() res : Response) { - if (user.status !== "Connected") + if (user.status !== STATUS.CONNECTED) return res.status(HttpStatus.FORBIDDEN).send("") const tokenGame = await this.tokenGameRepository.createQueryBuilder('tokenGame') .andWhere('tokenGame.playerTwoUsername = :playerTwoUsername', {playerTwoUsername : user.username}) @@ -294,8 +296,8 @@ export class GameService { this.userService.incrementVictories(playerOne.id) this.userService.incrementDefeats(playerTwo.id) } - this.userService.updateStatus(playerOne.id, "Connected") - this.userService.updateStatus(playerTwo.id, "Connected") + this.userService.updateStatus(playerOne.id, STATUS.CONNECTED) + this.userService.updateStatus(playerTwo.id, STATUS.CONNECTED) return HttpStatus.OK } } diff --git a/srcs/requirements/nestjs/api_back/src/users/users.service.ts b/srcs/requirements/nestjs/api_back/src/users/users.service.ts index bd119c49..ff44e676 100644 --- a/srcs/requirements/nestjs/api_back/src/users/users.service.ts +++ b/srcs/requirements/nestjs/api_back/src/users/users.service.ts @@ -45,7 +45,6 @@ export class UsersService { isEnabledTwoFactorAuth: user.isEnabledTwoFactorAuth, status: user.status, stats: user.stats, - currentRoom: user.currentRoom, }; console.log(`Returned Partial User.` + partialUser.username + user.username); return partialUser; diff --git a/srcs/requirements/nginx/conf/.nfs000000000ef7127e000000f0 b/srcs/requirements/nginx/conf/.nfs000000000ef7127e000000f0 deleted file mode 100644 index 02ffec11..00000000 --- a/srcs/requirements/nginx/conf/.nfs000000000ef7127e000000f0 +++ /dev/null @@ -1,55 +0,0 @@ -server { - listen 8080; - listen [::]:8080; - server_name localhost; - - location /api/v2 { - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_pass http://backend_dev:3000; - } - - location /chat { - proxy_set_header Host $host; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection "Upgrade"; - proxy_pass http://backend_dev:5000/chat; - } - - location /api/v2/game/gameserver { - deny all; - } - - location /pong { - proxy_pass http://game_server:8042/pong; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection $connection_upgrade; - proxy_set_header Host $host; - } - - location / { - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_pass http://frontend_dev:8080; - } -} - -server { - listen 35729 default_server; - listen [::]:35729 default_server; - server_name localhost; - location / { - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_pass http://frontend_dev:35729; - } -} diff --git a/srcs/requirements/svelte/api_front/src/pages/game/Game.svelte b/srcs/requirements/svelte/api_front/src/pages/game/Game.svelte index 97c56656..d2d39d21 100644 --- a/srcs/requirements/svelte/api_front/src/pages/game/Game.svelte +++ b/srcs/requirements/svelte/api_front/src/pages/game/Game.svelte @@ -1,9 +1,10 @@ @@ -102,6 +111,12 @@ {#if !hiddenGame} +
+ player one avatar + '{gameState.playerOneUsername}' VS '{gameState.playerTwoUsername}' + player two avatar +
+ {#if !gameState.matchEnded}
@@ -127,7 +142,7 @@ {#if matchList.length !== 0} {#each matchList as match} - initGameSpectator(match.gameServerIdOfTheMatch, match.gameOptions)} /> + initGameSpectator(match)} /> {/each} {:else} @@ -195,10 +210,15 @@ canvas { font-size: x-large; padding: 10px; } - #match_list { font-family: 'Courier New', Courier, monospace; font-size: large; } - +.avatar { + min-height: 100px; + min-width: 100px; + + max-width: 100px; + max-height: 100px; +} diff --git a/srcs/requirements/svelte/api_front/src/pages/game/client/init.ts b/srcs/requirements/svelte/api_front/src/pages/game/client/init.ts index 863ebdbd..ba22819a 100644 --- a/srcs/requirements/svelte/api_front/src/pages/game/client/init.ts +++ b/srcs/requirements/svelte/api_front/src/pages/game/client/init.ts @@ -3,7 +3,7 @@ import * as c from "./constants.js" import * as en from "../shared_js/enums.js" import { GameArea } from "./class/GameArea.js"; import { GameComponentsClient } from "./class/GameComponentsClient.js"; -import { socket, resetGameState } from "./ws.js"; +import { socket, gameState } from "./ws.js"; import { initAudio } from "./audio.js"; import type { InitOptions } from "./class/InitOptions.js"; @@ -57,5 +57,5 @@ export function destroyBase() } setGc(null); setMatchOptions(null); - resetGameState(); + gameState.resetGameState(); } diff --git a/srcs/requirements/svelte/api_front/src/pages/game/client/pongSpectator.ts b/srcs/requirements/svelte/api_front/src/pages/game/client/pongSpectator.ts index 0b2fbeea..eb3637b5 100644 --- a/srcs/requirements/svelte/api_front/src/pages/game/client/pongSpectator.ts +++ b/srcs/requirements/svelte/api_front/src/pages/game/client/pongSpectator.ts @@ -8,7 +8,6 @@ import { initBase, destroyBase, computeMatchOptions } from "./init.js"; export { computeMatchOptions } from "./init.js"; export { MatchOptions } from "../shared_js/enums.js" -/* TODO: A way to delay the init of variables, but still use "const" not "let" ? */ import { pong, gc } from "./global.js" import { setStartFunction } from "./global.js" diff --git a/srcs/requirements/svelte/api_front/src/pages/game/client/ws.ts b/srcs/requirements/svelte/api_front/src/pages/game/client/ws.ts index c2d61abf..b3cad96b 100644 --- a/srcs/requirements/svelte/api_front/src/pages/game/client/ws.ts +++ b/srcs/requirements/svelte/api_front/src/pages/game/client/ws.ts @@ -10,16 +10,22 @@ import { muteFlag, soundRoblox } from "./audio.js" import { sleep } from "./utils.js"; import { Vector, VectorInteger } from "../shared_js/class/Vector.js"; -export const gameState = { - matchStarted: false, - matchEnded: false, - matchAborted: false -} - -export function resetGameState() { - gameState.matchStarted = false; - gameState.matchEnded = false; - gameState.matchAborted = false; +class GameState { + matchStarted: boolean; + matchEnded: boolean; + matchAborted: boolean; + playerOneUsername: string; + playerTwoUsername: string; + constructor() { + this.resetGameState(); + } + resetGameState() { + this.matchStarted = false; + this.matchEnded = false; + this.matchAborted = false; + this.playerOneUsername = ""; + this.playerTwoUsername = ""; + } } class ClientInfo { @@ -39,6 +45,7 @@ class ClientInfoSpectator { const wsUrl = "ws://" + process.env.WEBSITE_HOST + ":" + process.env.WEBSITE_PORT + "/pong"; export let socket: WebSocket; +export const gameState = new GameState(); export const clientInfo = new ClientInfo(); export const clientInfoSpectator = new ClientInfoSpectator(); // WIP, could refactor this @@ -85,6 +92,8 @@ function preMatchListener(this: WebSocket, event: MessageEvent) break; case en.EventTypes.matchmakingComplete: clientInfo.side = (data).side; + gameState.playerOneUsername = (data).playerOneUsername; + gameState.playerTwoUsername = (data).playerTwoUsername; if (clientInfo.side === en.PlayerSide.left) { clientInfo.racket = gc.playerLeft; diff --git a/srcs/requirements/svelte/api_front/src/pages/game/shared_js/class/Event.ts b/srcs/requirements/svelte/api_front/src/pages/game/shared_js/class/Event.ts index 0147ead8..e5938e29 100644 --- a/srcs/requirements/svelte/api_front/src/pages/game/shared_js/class/Event.ts +++ b/srcs/requirements/svelte/api_front/src/pages/game/shared_js/class/Event.ts @@ -18,10 +18,13 @@ export class EventAssignId extends ServerEvent { } export class EventMatchmakingComplete extends ServerEvent { - side: en.PlayerSide; - constructor(side: en.PlayerSide) { + side: en.PlayerSide = en.PlayerSide.noSide; + playerOneUsername: string; + playerTwoUsername: string; + constructor(playerOneUsername: string, playerTwoUsername: string) { super(en.EventTypes.matchmakingComplete); - this.side = side; + this.playerOneUsername = playerOneUsername; + this.playerTwoUsername = playerTwoUsername; } } diff --git a/srcs/requirements/svelte/api_front/src/pages/profile/ProfileDisplay.svelte b/srcs/requirements/svelte/api_front/src/pages/profile/ProfileDisplay.svelte index 4c440354..59791b1b 100644 --- a/srcs/requirements/svelte/api_front/src/pages/profile/ProfileDisplay.svelte +++ b/srcs/requirements/svelte/api_front/src/pages/profile/ProfileDisplay.svelte @@ -21,7 +21,7 @@
{#if user !== undefined} - + {:else}

Sorry

diff --git a/srcs/requirements/svelte/api_front/src/pages/profile/ProfileSettings.svelte b/srcs/requirements/svelte/api_front/src/pages/profile/ProfileSettings.svelte index 35a71898..e2bd0626 100644 --- a/srcs/requirements/svelte/api_front/src/pages/profile/ProfileSettings.svelte +++ b/srcs/requirements/svelte/api_front/src/pages/profile/ProfileSettings.svelte @@ -3,8 +3,9 @@ import Card from '../../pieces/Card.svelte'; import {onMount} from 'svelte'; import { push } from 'svelte-spa-router'; - + import Button from '../../pieces/Button.svelte'; + import { fetchAvatar } from "../../pieces/utils"; let user; let avatar, newAvatar; @@ -16,40 +17,21 @@ const errors = { username: '', checkbox: '', avatar: ''}; let success = {username: '', avatar: '' }; - onMount( async() => { - user = await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/user`) - .then( (x) => x.json() ); - // do a .catch? + onMount( async() => { + user = await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/user`) + .then( (x) => x.json() ); + // do a .catch? - if (user === undefined) { - console.log('User did not load, something more official should prolly happen') - } - // i don't unerstand why this is necessary but it really doesn't like it otherwise - nameTmp = user.username; - console.log(user.username) + if (user === undefined) { + console.log('User did not load, something more official should prolly happen') + } + // i don't unerstand why this is necessary but it really doesn't like it otherwise + nameTmp = user.username; - set.tfa = user.isEnabledTwoFactorAuth; + set.tfa = user.isEnabledTwoFactorAuth; - // tmp - // console.log('this is what is in the avatar before fetch') - // console.log(avatar) - - await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/user/avatar`, {method: "GET"}) - .then(response => {return response.blob()}) - .then(data => { - const url = URL.createObjectURL(data); - avatar = url; - }) - .catch(() => errors.avatar = 'Sorry your avatar could not be loaded' ); - - // .then(() => errors.avatar = '' ) // unnecessary i think cuz in on mount... - - // tmp - // console.log('this is what is in the avatar') - // console.log(avatar) - // console.log('this is what is in the NEW Avatar') - // console.log(newAvatar) - }) + avatar = await fetchAvatar(user.username); + }) const settingsHandler = async() => { // I don't really care which i use at this point... @@ -91,39 +73,33 @@ // .then(() => console.log('successful sub of new settings')) }; - const uploadAvatar = async() => { - errors.avatar = ''; - if (newAvatar === undefined) { - errors.avatar = 'You need to pick a file.' - return; - } - const data = new FormData(); - data.append("file", newAvatar[0]); + const uploadAvatar = async() => { + errors.avatar = ''; + if (newAvatar === undefined) { + errors.avatar = 'You need to pick a file.' + return; + } + const data = new FormData(); + data.append("file", newAvatar[0]); -// tmp - console.log(data); + // tmp + console.log(data); - const responseWhenChangeAvatar = fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/user/avatar`, - { - method : 'POST', - body : data, - }) - const responseFromServer = await responseWhenChangeAvatar; - if (responseFromServer.ok === true) { - uploadAvatarSuccess = true; - success.avatar = 'Your avatar has been updated'; - } - else { - errors.avatar = responseFromServer.statusText; - } - - await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/user/avatar`, {method: "GET"}) - .then(response => {return response.blob()}) - .then(data => { - const url = URL.createObjectURL(data); - avatar = url; + const responseWhenChangeAvatar = fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/user/avatar`, + { + method : 'POST', + body : data, }) - .catch(() => errors.avatar = 'Sorry your avatar could not be loaded' ); + const responseFromServer = await responseWhenChangeAvatar; + if (responseFromServer.ok === true) { + uploadAvatarSuccess = true; + success.avatar = 'Your avatar has been updated'; + } + else { + errors.avatar = responseFromServer.statusText; + } + + avatar = await fetchAvatar(user.username); } diff --git a/srcs/requirements/svelte/api_front/src/pieces/DisplayAUser.svelte b/srcs/requirements/svelte/api_front/src/pieces/DisplayAUser.svelte index 65a9da14..7b5c01be 100644 --- a/srcs/requirements/svelte/api_front/src/pieces/DisplayAUser.svelte +++ b/srcs/requirements/svelte/api_front/src/pieces/DisplayAUser.svelte @@ -39,8 +39,7 @@ {#if user !== undefined} - - + {:else}

Sorry

Failed to load user {aUsername}
diff --git a/srcs/requirements/svelte/api_front/src/pieces/GenerateUserDisplay.svelte b/srcs/requirements/svelte/api_front/src/pieces/GenerateUserDisplay.svelte index fb0471f6..58fd407e 100644 --- a/srcs/requirements/svelte/api_front/src/pieces/GenerateUserDisplay.svelte +++ b/srcs/requirements/svelte/api_front/src/pieces/GenerateUserDisplay.svelte @@ -2,9 +2,9 @@ import { onMount } from 'svelte'; + import { fetchAvatar } from "./utils"; export let user; - export let primary; // kinda useless, not sure what i was going for... Might be userful after all let rank = ''; let avatar; // avatar needs to be updated!!! @@ -12,28 +12,10 @@ // add errors let errors = {avatar: ''}; - onMount( async() => { - // console.log('Generate User Display, on mount ' + user.username) - if (primary) { - await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/user/avatar`, {method: "GET"}) - .then(response => {return response.blob()}) - .then(data => { - const url = URL.createObjectURL(data); - avatar = url; - }) - .catch(() => errors.avatar = 'Sorry your avatar could not be loaded' ); - // console.log('avatar: ') - // console.log(avatar) - } else { - await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/user/avatar?username=${user.username}`, {method: "GET"}) - .then(response => {return response.blob()}) - .then(data => { - const url = URL.createObjectURL(data); - avatar = url; - }) - .catch(() => errors.avatar = 'Sorry your avatar could not be loaded' ); - } - }) + onMount( async() => { + // console.log('Generate User Display, on mount ' + user.username) + avatar = await fetchAvatar(user.username); + }) /**** THIS IS BASICALLY ALL THE RANK LOGIC ERIC HAS MADE ****/ diff --git a/srcs/requirements/svelte/api_front/src/pieces/Match.ts b/srcs/requirements/svelte/api_front/src/pieces/Match.ts new file mode 100644 index 00000000..68e330f6 --- /dev/null +++ b/srcs/requirements/svelte/api_front/src/pieces/Match.ts @@ -0,0 +1,10 @@ + +import type { MatchOptions } from "../pages/game/client/pongSpectator"; +export { MatchOptions } from "../pages/game/client/pongSpectator"; + +export class Match { + gameServerIdOfTheMatch: string; + gameOptions: MatchOptions; + playerOneUsername: string; + playerTwoUsername: string; +} diff --git a/srcs/requirements/svelte/api_front/src/pieces/MatchListElem.svelte b/srcs/requirements/svelte/api_front/src/pieces/MatchListElem.svelte index 5433c23e..29fbaa52 100644 --- a/srcs/requirements/svelte/api_front/src/pieces/MatchListElem.svelte +++ b/srcs/requirements/svelte/api_front/src/pieces/MatchListElem.svelte @@ -1,14 +1,9 @@ @@ -57,7 +72,10 @@ {/if} - + +