Merge remote-tracking branch 'origin/master' into eric_front_and_back

This commit is contained in:
Me
2023-01-09 21:17:01 +01:00
56 changed files with 25882 additions and 641 deletions

106
README.md
View File

@@ -1,5 +1,4 @@
- CONFLICT srcs/requirements/svelte/api_front/public/build/bundle.js CONFLICT srcs/requirements/nestjs/api_back/src/friendship/friendship.service.ts
- CONFLICT srcs/requirements/svelte/api_front/public/build/bundle.js.map
### Pour lancer le docker : ### Pour lancer le docker :
@@ -132,3 +131,106 @@
- [docker](https://github.com/docker/docker-install) - [docker](https://github.com/docker/docker-install)
---
## http status :
```
- '100': 'CONTINUE',
- '101': 'SWITCHING_PROTOCOLS',
- '102': 'PROCESSING',
- '103': 'EARLYHINTS',
- '200': 'OK',
- '201': 'CREATED',
- '202': 'ACCEPTED',
- '203': 'NON_AUTHORITATIVE_INFORMATION',
- '204': 'NO_CONTENT',
- '205': 'RESET_CONTENT',
- '206': 'PARTIAL_CONTENT',
- '300': 'AMBIGUOUS',
- '301': 'MOVED_PERMANENTLY',
- '302': 'FOUND',
- '303': 'SEE_OTHER',
- '304': 'NOT_MODIFIED',
- '307': 'TEMPORARY_REDIRECT',
- '308': 'PERMANENT_REDIRECT',
- '400': 'BAD_REQUEST',
- '401': 'UNAUTHORIZED',
- '402': 'PAYMENT_REQUIRED',
- '403': 'FORBIDDEN',
- '404': 'NOT_FOUND',
- '405': 'METHOD_NOT_ALLOWED',
- '406': 'NOT_ACCEPTABLE',
- '407': 'PROXY_AUTHENTICATION_REQUIRED',
- '408': 'REQUEST_TIMEOUT',
- '409': 'CONFLICT',
- '410': 'GONE',
- '411': 'LENGTH_REQUIRED',
- '412': 'PRECONDITION_FAILED',
- '413': 'PAYLOAD_TOO_LARGE',
- '414': 'URI_TOO_LONG',
- '415': 'UNSUPPORTED_MEDIA_TYPE',
- '416': 'REQUESTED_RANGE_NOT_SATISFIABLE',
- '417': 'EXPECTATION_FAILED',
- '418': 'I_AM_A_TEAPOT',
- '421': 'MISDIRECTED',
- '422': 'UNPROCESSABLE_ENTITY',
- '424': 'FAILED_DEPENDENCY',
- '428': 'PRECONDITION_REQUIRED',
- '429': 'TOO_MANY_REQUESTS',
- '500': 'INTERNAL_SERVER_ERROR',
- '501': 'NOT_IMPLEMENTED',
- '502': 'BAD_GATEWAY',
- '503': 'SERVICE_UNAVAILABLE',
- '504': 'GATEWAY_TIMEOUT',
- '505': 'HTTP_VERSION_NOT_SUPPORTED',
- CONTINUE: 100,
- SWITCHING_PROTOCOLS: 101,
- PROCESSING: 102,
- EARLYHINTS: 103,
- OK: 200,
- CREATED: 201,
- ACCEPTED: 202,
- NON_AUTHORITATIVE_INFORMATION: 203,
- NO_CONTENT: 204,
- RESET_CONTENT: 205,
- PARTIAL_CONTENT: 206,
- AMBIGUOUS: 300,
- MOVED_PERMANENTLY: 301,
- FOUND: 302,
- SEE_OTHER: 303,
- NOT_MODIFIED: 304,
- TEMPORARY_REDIRECT: 307,
- PERMANENT_REDIRECT: 308,
- BAD_REQUEST: 400,
- UNAUTHORIZED: 401,
- PAYMENT_REQUIRED: 402,
- FORBIDDEN: 403,
- NOT_FOUND: 404,
- METHOD_NOT_ALLOWED: 405,
- NOT_ACCEPTABLE: 406,
- PROXY_AUTHENTICATION_REQUIRED: 407,
- REQUEST_TIMEOUT: 408,
- CONFLICT: 409,
- GONE: 410,
- LENGTH_REQUIRED: 411,
- PRECONDITION_FAILED: 412,
- PAYLOAD_TOO_LARGE: 413,
- URI_TOO_LONG: 414,
- UNSUPPORTED_MEDIA_TYPE: 415,
- REQUESTED_RANGE_NOT_SATISFIABLE: 416,
- EXPECTATION_FAILED: 417,
- I_AM_A_TEAPOT: 418,
- MISDIRECTED: 421,
- UNPROCESSABLE_ENTITY: 422,
- FAILED_DEPENDENCY: 424,
- PRECONDITION_REQUIRED: 428,
- TOO_MANY_REQUESTS: 429,
- INTERNAL_SERVER_ERROR: 500,
- NOT_IMPLEMENTED: 501,
- BAD_GATEWAY: 502,
- SERVICE_UNAVAILABLE: 503,
- GATEWAY_TIMEOUT: 504,
- HTTP_VERSION_NOT_SUPPORTED: 505
```

View File

@@ -229,8 +229,7 @@ export class GameSession {
this._forfeit(); this._forfeit();
} }
else { else {
// WIP: envoyer un truc à Nest ? Genre "match draw" this._matchEnd(en.PlayerSide.noSide, true);
this.destroy();
} }
return true; return true;
} }
@@ -250,10 +249,10 @@ export class GameSession {
} }
} }
private async _matchEnd(winner: en.PlayerSide, forfeit_flag: boolean = false) private async _matchEnd(winner: en.PlayerSide, forfeitFlag: boolean = false)
{ {
this.matchEnded = true; this.matchEnded = true;
const eventEnd = new ev.EventMatchEnd(winner, forfeit_flag); const eventEnd = new ev.EventMatchEnd(winner, forfeitFlag);
this.playersMap.forEach( (client) => { this.playersMap.forEach( (client) => {
client.socket.send(JSON.stringify(eventEnd)); client.socket.send(JSON.stringify(eventEnd));
}); });
@@ -261,19 +260,23 @@ export class GameSession {
client.socket.send(JSON.stringify(eventEnd)); client.socket.send(JSON.stringify(eventEnd));
}); });
// TODO: mettre à jour la route pour gerer les forfaits (actuellement le plus haut score gagne par defaut)
const gc = this.components; const gc = this.components;
console.log("================================= MATCH ENDED"); console.log("================================= MATCH ENDED");
if (forfeit_flag) { if (forfeitFlag) {
if (winner === en.PlayerSide.left) if (winner === en.PlayerSide.left)
{ {
gc.scoreLeft = 3 gc.scoreLeft = 3;
gc.scoreRight = 0 gc.scoreRight = 0;
}
else if (winner === en.PlayerSide.right)
{
gc.scoreLeft = 0;
gc.scoreRight = 3;
} }
else else
{ {
gc.scoreLeft = 0 gc.scoreLeft = 0;
gc.scoreRight = 3 gc.scoreRight = 0;
} }
} }
await fetch(c.addressBackEnd + "/game/gameserver/updategame", await fetch(c.addressBackEnd + "/game/gameserver/updategame",

View File

@@ -150,10 +150,10 @@ function publicMatchmaking(player: ClientPlayer)
{ {
const minPlayersNumber = 2; const minPlayersNumber = 2;
const maxPlayersNumber = 2; const maxPlayersNumber = 2;
matchmakingMap.set(player.id, player);
const matchOptions = player.matchOptions; const matchOptions = player.matchOptions;
const compatiblePlayers: ClientPlayer[] = []; const compatiblePlayers: ClientPlayer[] = [];
compatiblePlayers.push(player);
for (const [id, client] of matchmakingMap) for (const [id, client] of matchmakingMap)
{ {
if (client.matchOptions === matchOptions) if (client.matchOptions === matchOptions)
@@ -165,12 +165,27 @@ function publicMatchmaking(player: ClientPlayer)
} }
} }
// TODO: Replace with this code to disable the possibility to play against self
/* for (const [id, client] of matchmakingMap)
{
if (client.matchOptions === matchOptions && client.username !== player.username)
{
compatiblePlayers.push(client);
if (compatiblePlayers.length === maxPlayersNumber) {
break;
}
}
} */
if (compatiblePlayers.length >= minPlayersNumber) { if (compatiblePlayers.length >= minPlayersNumber) {
compatiblePlayers.forEach((client) => { compatiblePlayers.forEach((client) => {
matchmakingMap.delete(client.id); matchmakingMap.delete(client.id);
}); });
createGameSession(compatiblePlayers, matchOptions); createGameSession(compatiblePlayers, matchOptions);
} }
else {
matchmakingMap.set(player.id, player);
}
} }
@@ -178,11 +193,11 @@ function privateMatchmaking(player: ClientPlayer)
{ {
const minPlayersNumber = 2; const minPlayersNumber = 2;
const maxPlayersNumber = 2; const maxPlayersNumber = 2;
privateMatchmakingMap.set(player.id, player);
const matchOptions = player.matchOptions; const matchOptions = player.matchOptions;
const token = player.token; const token = player.token;
const compatiblePlayers: ClientPlayer[] = []; const compatiblePlayers: ClientPlayer[] = [];
compatiblePlayers.push(player);
for (const [id, client] of privateMatchmakingMap) for (const [id, client] of privateMatchmakingMap)
{ {
if (client.token === token) if (client.token === token)
@@ -202,6 +217,7 @@ function privateMatchmaking(player: ClientPlayer)
} }
else else
{ {
privateMatchmakingMap.set(player.id, player);
setTimeout(async function abortMatch() { setTimeout(async function abortMatch() {
if (!player.gameSession) if (!player.gameSession)
{ {
@@ -226,7 +242,6 @@ function privateMatchmaking(player: ClientPlayer)
function createGameSession(playersArr: ClientPlayer[], matchOptions: en.MatchOptions) function createGameSession(playersArr: ClientPlayer[], matchOptions: en.MatchOptions)
{ {
// const id = c.gameSessionIdPLACEHOLDER; // Force ID, TESTING SPECTATOR
const id = uuidv4(); const id = uuidv4();
const gameSession = new GameSession(id, matchOptions); const gameSession = new GameSession(id, matchOptions);
gameSessionsMap.set(id, gameSession); gameSessionsMap.set(id, gameSession);

View File

@@ -31,6 +31,7 @@ export enum InputEnum {
} }
export enum PlayerSide { export enum PlayerSide {
noSide = 0,
left = 1, left = 1,
right right
} }

View File

@@ -1,22 +0,0 @@
NODE_ENV=development
POSTGRES_HOST=postgresql
POSTGRES_PORT=5432
POSTGRES_USERNAME=postgres
POSTGRES_PASSWORD=9pKpKEgiamxwk5P7Ggsz
POSTGRES_DATABASE=transcendance_db
# OAUTH2 42 API
FORTYTWO_CLIENT_ID=u-s4t2ud-49dc7b539bcfe1acb48b928b2b281671c99fc5bfab1faca57a536ab7e0075500
FORTYTWO_CLIENT_SECRET=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
FORTYTWO_CALLBACK_URL=http://transcendance:8080/api/v2/auth/redirect
COOKIE_SECRET=248cdc831110eec8796d7c1edbf79835
# JWT
JWT_SECRET=442d774798979fcc14a4a2b6b7535902
# Misc
PORT=3000
#Redis
REDIS_HOST=redis
REDIS_PORT=6379
REDIS_PASSWORD=1a5e04138b91b3d683c708e4689454c2
#2fa
TWO_FACTOR_AUTHENTICATION_APP_NAME=Transcendance

View File

@@ -9,6 +9,7 @@ import { AuthenticationModule } from './auth/42/authentication.module';
import { PassportModule } from '@nestjs/passport'; import { PassportModule } from '@nestjs/passport';
import { GameModule } from './game/game.module'; import { GameModule } from './game/game.module';
import { ChatGateway } from './chat/chat.gateway'; import { ChatGateway } from './chat/chat.gateway';
import { ChatModule } from './chat/chat.module';
@Module({ @Module({
imports: [ imports: [
@@ -30,12 +31,10 @@ import { ChatGateway } from './chat/chat.gateway';
//avec une classe pour le module //avec une classe pour le module
synchronize: true, synchronize: true,
}), }),
ChatModule,
// GameModule, // GameModule,
], ],
controllers: [AppController], controllers: [AppController],
providers: [ providers: [AppService],
AppService,
ChatGateway,
],
}) })
export class AppModule {} export class AppModule {}

View File

@@ -0,0 +1,105 @@
import { Controller, UseGuards, HttpException, HttpStatus, Get, Post, 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 { setCurrentRoomDto } from './dto/setCurrentRoom.dto';
@Controller('chat')
export class ChatController {
constructor(
private chatService: ChatService,
) {}
@UseGuards(AuthenticateGuard)
@UseGuards(TwoFactorGuard)
@Get('myrooms')
async getMyRooms(@Req() req, @Res() res): Promise<object>
{
console.log("- in getMyRooms controller");
const rooms = await this.chatService.getMyRooms(req.user);
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<object>
{
console.log("- in getAllRooms controller");
const rooms = await this.chatService.getAllNotMyRooms(req.user);
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<object>
{
console.log("- in setCurrentRoom controller");
const response = await this.chatService.setCurrentRoom(req.user, setCurrentRoomDto.name);
console.log("- out setCurrentRoom controller");
return res.status(HttpStatus.OK).json({ message: response });
}
@UseGuards(AuthenticateGuard)
@UseGuards(TwoFactorGuard)
@Post('create')
async createRoom(@Body() createRoomDto: createRoomDto, @Req() req, @Res() res): Promise<object>
{
console.log("- in createRoom controller");
const response = await this.chatService.addUserToNewRoom(req.user, createRoomDto);
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<object>
{
console.log("- in joinRoom controller");
const response = await this.chatService.addUserToRoom(req.user, joinRoomDto);
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<object>
{
console.log("- in changeRoom controller");
const response = await this.chatService.setCurrentRoom(req.user, joinRoomDto.room_name);
console.log("- out changeRoom controller");
return res.status(HttpStatus.OK).json({ room_name: joinRoomDto.room_name, message: response });
}
@UseGuards(AuthenticateGuard)
@UseGuards(TwoFactorGuard)
@Post('leave')
async leaveRoom(@Body() body)
{
console.log("- in leaveRoom controller");
console.log("- out leaveRoom controller");
}
@UseGuards(AuthenticateGuard)
@UseGuards(TwoFactorGuard)
@Get('messages')
async getMessages(@Req() req, @Res() res): Promise<object>
{
console.log("- in getMessages controller");
const messages = await this.chatService.getMessagesFromCurrentRoom(req.user);
console.log("- out getMessages controller");
return res.status(HttpStatus.OK).json({ messages: messages });
}
}

View File

@@ -1,13 +1,7 @@
import { import { WebSocketGateway, SubscribeMessage, WebSocketServer, MessageBody, ConnectedSocket, OnGatewayConnection, OnGatewayDisconnect } from '@nestjs/websockets';
WebSocketGateway,
SubscribeMessage,
WebSocketServer,
MessageBody,
OnGatewayConnection,
OnGatewayDisconnect,
} from '@nestjs/websockets';
import { UsersService } from 'src/users/users.service'; import { UsersService } from 'src/users/users.service';
import { PaginationQueryDto } from 'src/common/dto/pagination-query.dto'; import { PaginationQueryDto } from 'src/common/dto/pagination-query.dto';
import { ChatService } from './chat.service';
@WebSocketGateway(5000, { @WebSocketGateway(5000, {
path: '/chat', path: '/chat',
@@ -19,26 +13,43 @@ export class ChatGateway
constructor constructor
( (
private usersService: UsersService, private usersService: UsersService,
private chatService: ChatService,
) {} ) {}
@WebSocketServer() @WebSocketServer()
server; server;
// how to guard the handleConnection ? // how to guard the handleConnection ?
// https://github.com/nestjs/nest/issues/882 // https://github.com/nestjs/nest/issues/882
async handleConnection(client) { async handleConnection(client) {
// const paginationQuery = new PaginationQueryDto(); console.log('- Client connected :', client.id, client.handshake.query.username);
// const users = await this.usersService.findAll(paginationQuery); client.username = client.handshake.query.username;
}
async handleDisconnect(client) {
console.log('- Client disconnected :', client.id, client.username);
}
// const users = await this.usersService.findAll(client); @SubscribeMessage('join')
// const users = await this.usersService.findAll(client); async joinRoom(@ConnectedSocket() socket, @MessageBody() room_name: string): Promise<void>
console.log('---- Client connected :', client.id); {
// console.log('users :', users); console.log('- in joinRoom gateway');
socket.leave(socket.room);
socket.join(room_name);
socket.room = room_name;
console.log('- out joinRoom gateway');
} }
handleDisconnect(client) {
console.log('---- client disconnected :', client.id); @SubscribeMessage('message')
async handleMessage(@ConnectedSocket() socket, @MessageBody() message: string): Promise<void>
{
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');
} }
/*
*/
} }

View File

@@ -0,0 +1,28 @@
import { Module } from '@nestjs/common';
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';
@Module({
imports: [
TypeOrmModule.forFeature([Chatroom, User]),
UsersModule,
],
controllers: [
ChatController,
],
exports: [
],
providers: [
ChatService,
ChatGateway,
],
})
export class ChatModule {}

View File

@@ -0,0 +1,219 @@
import { HttpException, HttpStatus, Injectable, Res } from '@nestjs/common';
import { User } from 'src/users/entities/user.entity';
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 { messagesDto } from './dto/messages.dto';
@Injectable()
export class ChatService {
constructor(
private usersService: UsersService,
@InjectRepository(User)
private readonly userRepository: Repository<User>,
@InjectRepository(Chatroom)
private readonly chatroomRepository: Repository<Chatroom>,
) { }
// temp for test
sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
/* GETTERS ************************************************
*/
async getMyRooms(user: User)
{
console.log("-- in getMyRooms service");
const rooms = await this.chatroomRepository
.createQueryBuilder('chatroom')
.where('chatroom.users LIKE :user_name', { user_name: `%${user.username}%` })
.getMany();
console.log("-- out getMyRooms service");
return rooms;
}
async getAllRooms()
{
console.log("-- in getAllRooms service");
const rooms = await this.chatroomRepository
.createQueryBuilder('chatroom')
.getMany();
console.log("-- out getAllRooms service");
return rooms;
}
async getAllNotMyRooms(user: User)
{
console.log("-- in getAllNotMyRooms service");
const user_db = await this.getUserByName(user.username);
//const user_db = await this.usersService.findOne(user.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}%` })
.getMany();
//const users = await this.getAllUsers();
//let allRooms = [...rooms, ...users];
console.log("-- out getAllNotMyRooms service");
return rooms;
}
async getMessagesFromCurrentRoom(user: User)
{
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("-- out getMessagesFromCurrentRoom service");
return currentRoom.messages;
}
async getCurrentRoom(username: string)
{
console.log("-- in getCurrentRoom service");
const user_db = await this.getUserByName(username);
//const user_db = await this.usersService.findOne(username);
console.log("-- out getCurrentRoom service");
return user_db.currentRoom;
}
async getRoomByName(name: string)
{
console.log("-- in getRoomByName service");
const room = await this.chatroomRepository
.createQueryBuilder('chatroom')
.where('chatroom.name = :name', { name: name })
.getOne();
console.log("-- out getRoomByName service");
return room;
}
async getRoomById(id: number)
{
console.log("-- in getRoomById service");
const room = await this.chatroomRepository
.createQueryBuilder('chatroom')
.where('chatroom.id = :id', { id: id })
.getOne();
console.log("-- out getRoomById service");
return room;
}
/* SETTERS ************************************************
*/
async setCurrentRoom(user: User, name: string)
{
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;
this.userRepository.save(user_db);
console.log("-- out setCurrentRoom service");
return `room "${name}" is now current room`;
}
/* ADDERS *************************************************
*/
async addUserToNewRoom(user: User, createRoomDto: createRoomDto)
{
console.log("-- in addUserToRoom service");
const room = await this.getRoomByName(createRoomDto.room_name);
if (room)
throw new HttpException(`This room 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);
console.log("-- out addUserToRoom service");
return "successfull room creation";
}
async addUserToRoom(user: User, joinRoomDto: joinRoomDto)
{
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);
// update room with new user
room.users.push(user.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)
{
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);
let chat_message = {
name: username,
message: message,
};
currentRoom.messages.push(chat_message);
this.chatroomRepository.save(currentRoom);
console.log("-- out addMessageToCurrentRoom service");
}
/* REMOVERS ***********************************************
*/
async removeUserFromRoom(user: User, room_name: string)
{
console.log("-- in removeUserFromRoom service");
// get room
// remove user
}
/* SEARCH IN USER *****************************************
*/
async getUserByName(name: string)
{
console.log("-- in getUserByName service");
const user = await this.userRepository
.createQueryBuilder('user')
.where('user.username = :name', { name: name })
.getOne();
console.log("-- out getUserByName service");
return user;
}
}

View File

@@ -0,0 +1,18 @@
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;
}

View File

@@ -0,0 +1,10 @@
import { IsBoolean, IsEmpty, IsInt, IsNotEmpty, IsNumber, IsString, IsOptional } from "class-validator";
import { IsNull } from "typeorm";
export class joinRoomDto
{
@IsString()
@IsNotEmpty()
room_name: string;
}

View File

@@ -0,0 +1,9 @@
import { IsBoolean, IsEmpty, IsInt, IsNotEmpty, IsNumber, IsString, IsOptional, IsArray } from "class-validator";
import { IsNull } from "typeorm";
export class messagesDto
{
@IsArray()
messages: { name: string; message: string }[];
}

View File

@@ -0,0 +1,10 @@
import { IsBoolean, IsEmpty, IsInt, IsNotEmpty, IsNumber, IsString, IsOptional } from "class-validator";
import { IsNull } from "typeorm";
export class setCurrentRoomDto
{
@IsString()
@IsNotEmpty()
name: string;
}

View File

@@ -0,0 +1,38 @@
import {
Entity,
Column,
ManyToOne,
ManyToMany,
JoinTable,
PrimaryGeneratedColumn
} from "typeorm";
import { User } from 'src/users/entities/user.entity';
@Entity('chatroom')
export class Chatroom {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
@Column()
type: string;
// @ManyToOne(type => User, user => user.ownedRooms)
// owner: User;
//
// @ManyToMany(type => User)
// @JoinTable()
// users: User[];
@Column()
owner: string; // name
@Column("simple-array")
users: string[]; // names
@Column("json")
messages: { name: string, message: string }[];
}

View File

@@ -254,7 +254,6 @@ export class FriendshipService {
// const friendship = await this.friendshipRepository.findOneBy({ sender: creator, receiver: receiver }); // const friendship = await this.friendshipRepository.findOneBy({ sender: creator, receiver: receiver });
const friendship = await this.friendshipRepository const friendship = await this.friendshipRepository
.createQueryBuilder('friendship') .createQueryBuilder('friendship')
.leftJoinAndSelect('friendship.sender', 'sender') .leftJoinAndSelect('friendship.sender', 'sender')

View File

@@ -87,10 +87,12 @@ export class GameService {
if (user.status === STATUS.IN_POOL || user.status === STATUS.IN_GAME) if (user.status === STATUS.IN_POOL || user.status === STATUS.IN_GAME)
{ {
await this.deleteToken(user); await this.deleteToken(user);
if (user.status === STATUS.IN_POOL) {
user.status = STATUS.CONNECTED; user.status = STATUS.CONNECTED;
}
this.userRepository.save(user); this.userRepository.save(user);
} }
if (grantTicketDto.isGameIsWithInvitation === true) if (grantTicketDto.isGameIsWithInvitation === true && user.status !== STATUS.IN_GAME)
{ {
const secondUser : Partial<User> = await this.userService.findOne(grantTicketDto.playerTwoUsername) const secondUser : Partial<User> = await this.userService.findOne(grantTicketDto.playerTwoUsername)
if (!secondUser || secondUser.username === user.username) if (!secondUser || secondUser.username === user.username)
@@ -105,7 +107,7 @@ export class GameService {
this.userService.updateStatus(user.id, "In Pool") this.userService.updateStatus(user.id, "In Pool")
return res.status(HttpStatus.OK).json({ token : encryptedTextToReturn }); return res.status(HttpStatus.OK).json({ token : encryptedTextToReturn });
} }
else if (grantTicketDto.isGameIsWithInvitation === false) { else if (grantTicketDto.isGameIsWithInvitation === false && user.status !== STATUS.IN_GAME) {
const encryptedTextToReturn = await this.encryptToken(user.username + '_' const encryptedTextToReturn = await this.encryptToken(user.username + '_'
+ grantTicketDto.gameOptions + '_' + grantTicketDto.isGameIsWithInvitation + '_' + new Date()) + grantTicketDto.gameOptions + '_' + grantTicketDto.isGameIsWithInvitation + '_' + new Date())
const tok = this.tokenGameRepository.create(grantTicketDto); const tok = this.tokenGameRepository.create(grantTicketDto);
@@ -280,6 +282,7 @@ export class GameService {
{ {
this.userService.incrementDraws(playerOne.id) this.userService.incrementDraws(playerOne.id)
this.userService.incrementDraws(playerTwo.id) this.userService.incrementDraws(playerTwo.id)
console.log("DRAW NEST");
} }
else if (game.playerOneUsernameResult < game.playerTwoUsernameResult) else if (game.playerOneUsernameResult < game.playerTwoUsernameResult)
{ {

View File

@@ -52,4 +52,9 @@ export class User {
@JoinColumn() @JoinColumn()
@OneToOne(() => UserStats, { cascade: true }) @OneToOne(() => UserStats, { cascade: true })
stats: UserStats; stats: UserStats;
// ROOMS :
@Column({ nullable: true })
currentRoom: string; // chatroom name
} }

View File

@@ -45,6 +45,7 @@ export class UsersService {
isEnabledTwoFactorAuth: user.isEnabledTwoFactorAuth, isEnabledTwoFactorAuth: user.isEnabledTwoFactorAuth,
status: user.status, status: user.status,
stats: user.stats, stats: user.stats,
currentRoom: user.currentRoom,
}; };
console.log(`Returned Partial User.` + partialUser.username + user.username); console.log(`Returned Partial User.` + partialUser.username + user.username);
return partialUser; return partialUser;
@@ -193,7 +194,7 @@ export class UsersService {
.getOne(); .getOne();
if (!user.stats || !user) if (!user.stats || !user)
throw new HttpException(`The user's stats could not be found.`,HttpStatus.NOT_FOUND); throw new HttpException(`The user's stats could not be found.`,HttpStatus.NOT_FOUND);
user.stats.winGame++; user.stats.drawGame++;
user.stats.totalGame++; user.stats.totalGame++;
this.userRepository.save(user); this.userRepository.save(user);
} }

View File

@@ -0,0 +1,55 @@
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;
}
}

View File

@@ -0,0 +1,2 @@
WEBSITE_HOST=localhost
WEBSITE_PORT=8080

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -13,26 +13,22 @@
let allUsers; let allUsers;
//Game's stuff //Game's stuff
let optionsAreNotSet = true;
const options = new pong.InitOptions(); const options = new pong.InitOptions();
//Game's stuff client side only
const gameAreaId = "game_area"; const gameAreaId = "game_area";
//html boolean for pages //boolean for html page
let showWaitPage = false;
let showInvitations = false;
let showGameOption = true;
let showError = false;
let hiddenGame = true; let hiddenGame = true;
let showMatchEnded = false; let optionsAreNotSet = true;
let showGameOptions = true;
let showInvitations = false;
let showError = false;
let errorMessage = "";
let showWaitPage = false;
let isThereAnyInvitation = false;
let invitations = []; let invitations = [];
let waitingMessage = "Please wait..." let watchGameStateInterval;
let errorMessageWhenAttemptingToGetATicket = ""; const watchGameStateIntervalRate = 142;
let idOfIntevalCheckTerminationOfTheMatch;
onMount( async() => { onMount( async() => {
user = await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/user`) user = await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/user`)
@@ -43,17 +39,28 @@
}) })
onDestroy( async() => { onDestroy( async() => {
clearInterval(idOfIntevalCheckTerminationOfTheMatch); clearInterval(watchGameStateInterval);
pong.destroy(); pong.destroy();
}) })
function resetPage() {
hiddenGame = true;
optionsAreNotSet = true;
showGameOptions = true;
showInvitations = false;
showError = false;
showWaitPage = false;
options.reset(user.username);
pong.destroy();
};
const initGame = async() => const initGame = async() =>
{ {
optionsAreNotSet = false; optionsAreNotSet = false;
showWaitPage = true; showWaitPage = true;
const matchOptions = pong.computeMatchOptions(options); const matchOptions = pong.computeMatchOptions(options);
try {
const responseWhenGrantToken = fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/game/ticket`, { const response = await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/game/ticket`, {
method : "POST", method : "POST",
headers : {'Content-Type': 'application/json'}, headers : {'Content-Type': 'application/json'},
body : JSON.stringify({ body : JSON.stringify({
@@ -62,89 +69,82 @@
gameOptions : matchOptions, gameOptions : matchOptions,
isGameIsWithInvitation : options.isSomeoneIsInvited isGameIsWithInvitation : options.isSomeoneIsInvited
}) })
}) });
const responseFromServer = await responseWhenGrantToken; console.log("status : " + response.status);
const responseInjson = await responseFromServer.json(); const responseBody = await response.json();
const token : string = responseInjson.token; const token : string = responseBody.token;
showWaitPage = false; showWaitPage = false;
console.log("status : " + responseFromServer.status) if (response.ok && token)
if (responseFromServer.status != 200)
{ {
console.log(responseInjson) watchGameStateInterval = setInterval(watchGameState, watchGameStateIntervalRate);
console.log("On refuse le ticket"); pong.init(matchOptions, options, gameAreaId, token);
errorMessageWhenAttemptingToGetATicket = responseInjson.message;
showError = true;
options.reset();
options.playerOneUsername = user.username;
setTimeout(() => {
optionsAreNotSet = true
showError = false;
// showWaitPage = false // ???
}, 5000);
}
else if (token)
{
idOfIntevalCheckTerminationOfTheMatch = setInterval(matchTermitation, 1000);
// options.isInvitedPerson = false // ???
pong.init(options, gameAreaId, token);
hiddenGame = false; hiddenGame = false;
} }
// TODO: Un "else" peut-être ? Si pas de token on fait un truc ? else
// Si on ne rentre pas dans le else if, du coup il ne se passe rien. {
console.log(responseBody);
console.log("On refuse le ticket");
errorMessage = responseBody.message;
showError = true;
options.reset(user.username);
setTimeout(() => {
optionsAreNotSet = true;
showError = false;
errorMessage = "";
}, 5000);
}
} catch (e) {
console.log(e);
}
} }
const initGameForInvitedPlayer = async(invitation : any) => const initGameForInvitedPlayer = async(invitation : any) =>
{ {
optionsAreNotSet = false optionsAreNotSet = false;
showWaitPage = true showWaitPage = true;
console.log("invitation : ") console.log("invitation : ");
console.log(invitation) console.log(invitation);
if (invitation.token) if (invitation.token)
{ {
idOfIntevalCheckTerminationOfTheMatch = setInterval(matchTermitation, 1000); watchGameStateInterval = setInterval(watchGameState, watchGameStateIntervalRate);
options.playerOneUsername = invitation.playerOneUsername; options.playerOneUsername = invitation.playerOneUsername;
options.playerTwoUsername = invitation.playerTwoUsername; options.playerTwoUsername = invitation.playerTwoUsername;
options.isSomeoneIsInvited = true; options.isSomeoneIsInvited = true;
options.isInvitedPerson = true options.isInvitedPerson = true;
pong.init(options, gameAreaId, invitation.token); pong.init(invitation.gameOptions, options, gameAreaId, invitation.token);
showWaitPage = false showWaitPage = false;
hiddenGame = false; hiddenGame = false;
} }
} }
const matchTermitation = () => { const watchGameState = () => {
console.log("Ping matchTermitation") console.log("watchGameState");
if (gameState.matchAbort || gameState.matchEnded) if (gameState) { // trigger Svelte reactivity
gameState.matchStarted = gameState.matchStarted;
gameState.matchEnded = gameState.matchEnded;
gameState.matchAborted = gameState.matchAborted;
}
if (gameState.matchAborted || gameState.matchEnded)
{ {
clearInterval(idOfIntevalCheckTerminationOfTheMatch); clearInterval(watchGameStateInterval);
console.log("matchTermitation was called") console.log("watchGameState, end");
showWaitPage = false
gameState.matchAbort ?
errorMessageWhenAttemptingToGetATicket = "The match has been aborted"
: errorMessageWhenAttemptingToGetATicket = "The match is finished !"
gameState.matchAbort ? showError = true : showMatchEnded = true;
setTimeout(() => { setTimeout(() => {
resetPage(); resetPage();
errorMessageWhenAttemptingToGetATicket = ""; console.log("watchGameState : setTimeout");
isThereAnyInvitation = false;
invitations = []; // ???
console.log("matchTermitation : setTimeout")
}, 5000); }, 5000);
} }
} }
const switchToGameOptions = () => {
const showOptions = () => { showGameOptions = true;
showGameOption = true showInvitations = false;
showInvitations = false
} }
const showInvitation = async() => { const fetchInvitations = async() => {
showGameOption = false; showGameOptions = false;
showInvitations = true; showInvitations = true;
invitations = await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/game/invitations`) invitations = await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/game/invitations`)
.then(x => x.json()) .then(x => x.json());
invitations.length !== 0 ? isThereAnyInvitation = true : isThereAnyInvitation = false
} }
const rejectInvitation = async(invitation) => { const rejectInvitation = async(invitation) => {
@@ -156,46 +156,30 @@
}) })
}) })
.then(x => x.json()) .then(x => x.json())
.catch(error => console.log(error)) .catch(error => console.log(error));
showInvitation() fetchInvitations();
} }
const acceptInvitation = async(invitation : any) => { const acceptInvitation = async(invitation : any) =>
{
const res = await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/game/accept`, { const res = await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/game/accept`, {
method: "POST", method: "POST",
headers: { 'Content-Type': 'application/json'}, headers: { 'Content-Type': 'application/json'},
body: JSON.stringify({ body: JSON.stringify({
token : invitation.token token : invitation.token
}) })
}) }).catch(error => console.log(error));
.then(x => x.json())
.catch(error => {
console.log(error)
})
if (res.status === 200)
{
showInvitation()
initGameForInvitedPlayer(invitation)
}
//Au final c'est utile !
initGameForInvitedPlayer(invitation) // Luke: normal de initGameForInvitedPlayer() sur un "res.status" different de 200 ? if (res && res.ok) {
initGameForInvitedPlayer(invitation);
}
} }
function leaveMatch() { function leaveMatch() {
clearInterval(watchGameStateInterval);
resetPage(); resetPage();
}; };
function resetPage() {
hiddenGame = true;
optionsAreNotSet = true
showError = false;
showMatchEnded = false;
options.reset();
options.playerOneUsername = user.username;
pong.destroy();
};
</script> </script>
<Header /> <Header />
@@ -203,47 +187,61 @@
Might become useless after CSS rework. --> Might become useless after CSS rework. -->
<div id="game_page"> <div id="game_page">
{#if showMatchEnded === true} {#if showError}
<div id="div_game" in:fly="{{ y: 10, duration: 1000 }}"> <div class="div_game" in:fly="{{ y: 10, duration: 1000 }}">
<p>{errorMessageWhenAttemptingToGetATicket}</p>
</div>
{/if}
{#if showError === true}
<div id="div_game" in:fly="{{ y: 10, duration: 1000 }}">
<fieldset> <fieldset>
<legend>Error</legend> <legend>Error</legend>
<p>{errorMessageWhenAttemptingToGetATicket}</p> <p>{errorMessage}</p>
</fieldset> </fieldset>
</div> </div>
{/if} {/if}
{#if !hiddenGame}
{#if gameState.matchEnded}
<div class="div_game" in:fly="{{ y: 10, duration: 1000 }}">
<p>The match is finished !</p>
</div>
{:else if gameState.matchAborted}
<div class="div_game" in:fly="{{ y: 10, duration: 1000 }}">
<p>The match has been aborted</p>
</div>
{/if}
{/if}
<div id="canvas_container" hidden={hiddenGame}> <div id="canvas_container" hidden={hiddenGame}>
<canvas id={gameAreaId}/> <canvas id={gameAreaId}/>
</div> </div>
{#if !hiddenGame} {#if !hiddenGame}
<div id="div_game"> {#if gameState.matchStarted && !gameState.matchEnded}
<button id="pong_button" on:click={leaveMatch}>forfeit</button> <div class="div_game">
<button class="pong_button" on:click={leaveMatch}>forfeit</button>
</div> </div>
{:else if !gameState.matchStarted}
<div class="div_game">
<button class="pong_button" on:click={leaveMatch}>leave matchmaking</button>
</div>
{/if}
{/if} {/if}
{#if showWaitPage === true} {#if showWaitPage}
<div id="div_game" in:fly="{{ y: 10, duration: 1000 }}"> <div class="div_game" in:fly="{{ y: 10, duration: 1000 }}">
<fieldset> <fieldset>
<legend>Connecting to the game...</legend> <legend>Connecting to the game...</legend>
<p>{waitingMessage}</p> <p>Please wait...</p>
</fieldset> </fieldset>
</div> </div>
{/if} {/if}
<!-- -->
{#if optionsAreNotSet} {#if optionsAreNotSet}
{#if showGameOption === true} {#if showGameOptions}
<div id="game_option"> <div id="game_option">
<div id="div_game"> <div class="div_game">
<button id="pong_button" on:click={showInvitation}>Show invitations</button> <button class="pong_button" on:click={fetchInvitations}>Show invitations</button>
<fieldset> <fieldset in:fly="{{ y: 10, duration: 1000 }}">
<legend>game options</legend> <legend>game options</legend>
<div> <div>
<input type="checkbox" id="multi_balls" name="multi_balls" bind:checked={options.multi_balls}> <input type="checkbox" id="multi_balls" name="multi_balls" bind:checked={options.multi_balls}>
@@ -264,7 +262,7 @@
<input type="checkbox" id="isSomeoneIsInvited" bind:checked={options.isSomeoneIsInvited}> <input type="checkbox" id="isSomeoneIsInvited" bind:checked={options.isSomeoneIsInvited}>
<label for="moving_walls">Invite a friend</label> <label for="moving_walls">Invite a friend</label>
</div> </div>
{#if options.isSomeoneIsInvited === true} {#if options.isSomeoneIsInvited}
<select bind:value={options.playerTwoUsername}> <select bind:value={options.playerTwoUsername}>
{#each allUsers as user } {#each allUsers as user }
<option value={user.username}>{user.username}</option> <option value={user.username}>{user.username}</option>
@@ -272,7 +270,7 @@
</select> </select>
{/if} {/if}
<div> <div>
<button id="pong_button" on:click={initGame}>PLAY</button> <button class="pong_button" on:click={initGame}>PLAY</button>
</div> </div>
</fieldset> </fieldset>
</div> </div>
@@ -280,27 +278,24 @@
{/if} {/if}
{#if showInvitations} {#if showInvitations}
<div id="invitations_options" in:fly="{{ y: 10, duration: 1000 }}"> <div class="div_game">
<div id="div_game"> <button class="pong_button" on:click={switchToGameOptions}>Play a Game</button>
<button id="pong_button" on:click={showOptions}>Play a Game</button> <fieldset in:fly="{{ y: 10, duration: 1000 }}">
<fieldset> <legend>invitations</legend>
<legend>Current invitation(s)</legend> <button class="pong_button" on:click={fetchInvitations}>Reload</button>
{#if isThereAnyInvitation} {#if invitations.length !== 0}
{#each invitations as invitation } {#each invitations as invitation}
<div> <div>
{invitation.playerOneUsername} has invited you to play a pong ! {invitation.playerOneUsername} has invited you to play a pong !
<button id="pong_button" on:click={() => acceptInvitation(invitation)}>V</button> <button class="pong_button" on:click={() => acceptInvitation(invitation)}>V</button>
<button id="pong_button" on:click={() => rejectInvitation(invitation)}>X</button> <button class="pong_button" on:click={() => rejectInvitation(invitation)}>X</button>
</div> </div>
{/each} {/each}
{/if} {:else}
{#if isThereAnyInvitation === false}
<p>Currently, no one asked to play with you.</p> <p>Currently, no one asked to play with you.</p>
<button id="pong_button" on:click={showInvitation}>Reload</button>
{/if} {/if}
</fieldset> </fieldset>
</div> </div>
</div>
{/if} {/if}
{/if} {/if}
@@ -319,6 +314,7 @@
} }
#game_page { #game_page {
margin: 0; margin: 0;
padding: 20px;
background-color: #222425; background-color: #222425;
position: relative; position: relative;
width: 100%; width: 100%;
@@ -339,22 +335,21 @@ canvas {
width: 80%; width: 80%;
} }
#div_game { .div_game {
margin-top: 20px;
text-align: center; text-align: center;
font-family: "Bit5x3"; font-family: "Bit5x3";
color: rgb(245, 245, 245); color: rgb(245, 245, 245);
font-size: x-large; font-size: x-large;
} }
#div_game fieldset { .div_game fieldset {
max-width: 50vw; max-width: 50vw;
width: auto; width: auto;
margin: 0 auto; margin: 0 auto;
} }
#div_game fieldset div { .div_game fieldset div {
padding: 10px; padding: 10px;
} }
#pong_button { .pong_button {
font-family: "Bit5x3"; font-family: "Bit5x3";
color: rgb(245, 245, 245); color: rgb(245, 245, 245);
background-color: #333333; background-color: #333333;
@@ -362,18 +357,4 @@ canvas {
padding: 10px; padding: 10px;
} }
#users_name { /* UNUSED */
text-align: center;
font-family: "Bit5x3";
color: rgb(245, 245, 245);
font-size: x-large;
}
#error_notification { /* UNUSED */
text-align: center;
display: block;
font-family: "Bit5x3";
color: rgb(143, 19, 19);
font-size: x-large;
}
</style> </style>

View File

@@ -8,7 +8,6 @@
import * as pongSpectator from "./client/pongSpectator"; import * as pongSpectator from "./client/pongSpectator";
import { gameState } from "./client/ws"; import { gameState } from "./client/ws";
import { gameSessionIdPLACEHOLDER } from "./shared_js/constants";
//user's stuff //user's stuff
let user; let user;
@@ -17,79 +16,71 @@
//Game's stuff client side only //Game's stuff client side only
const gameAreaId = "game_area"; const gameAreaId = "game_area";
let sound = "off"; let sound = "off";
// const dummyMatchList = [
// {
// gameSessionId: gameSessionIdPLACEHOLDER,
// matchOptions: pongSpectator.MatchOptions.noOption,
// playerOneUsername: "toto",
// playerTwoUsername: "bruno",
// },
// {
// gameSessionId: gameSessionIdPLACEHOLDER,
// matchOptions: pongSpectator.MatchOptions.multiBalls,
// playerOneUsername: "pl1",
// playerTwoUsername: "pl2",
// },
// {
// gameSessionId: "id6543",
// matchOptions: pongSpectator.MatchOptions.movingWalls | pongSpectator.MatchOptions.multiBalls,
// playerOneUsername: "bertand",
// playerTwoUsername: "cassandre",
// },
// {
// gameSessionId: "id3452",
// matchOptions: pongSpectator.MatchOptions.multiBalls,
// playerOneUsername: "madeleine",
// playerTwoUsername: "jack",
// },
// ];
let matchList = []; let matchList = [];
//html boolean for pages //html boolean for pages
let hiddenGame = true; let hiddenGame = true;
let hiddenMatchList = false;
let watchGameStateInterval;
const watchGameStateIntervalRate = 142;
onMount( async() => { onMount( async() => {
user = await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/user`) user = await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/user`)
.then( x => x.json() ); .then( x => x.json() );
allUsers = await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/user/all`) allUsers = await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/user/all`)
.then( x => x.json() ); .then( x => x.json() );
// WIP: fetch for match list here
const responseForMatchList = await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/game/match/all`) const responseForMatchList = await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/game/match/all`)
const jsonForMatchList = await responseForMatchList.json(); const jsonForMatchList = await responseForMatchList.json();
matchList = jsonForMatchList; matchList = jsonForMatchList;
console.log("matchList");
if (matchList.length <= 0)
hiddenMatchList = true;
console.log(matchList);
}) })
onDestroy( async() => { onDestroy( async() => {
clearInterval(watchGameStateInterval);
pongSpectator.destroy(); pongSpectator.destroy();
}) })
async function initGameSpectator(gameSessionId: string, matchOptions: pongSpectator.MatchOptions) { async function initGameSpectator(gameSessionId: string, matchOptions: pongSpectator.MatchOptions)
{
watchGameStateInterval = setInterval(watchGameState, watchGameStateIntervalRate);
pongSpectator.init(matchOptions, sound, gameAreaId, gameSessionId); pongSpectator.init(matchOptions, sound, gameAreaId, gameSessionId);
hiddenGame = false; hiddenGame = false;
}; };
const watchGameState = () => {
console.log("watchGameState")
if (gameState) { // trigger Svelte reactivity
gameState.matchStarted = gameState.matchStarted;
gameState.matchEnded = gameState.matchEnded;
gameState.matchAborted = gameState.matchAborted;
}
if (gameState.matchAborted || gameState.matchEnded)
{
clearInterval(watchGameStateInterval);
console.log("watchGameState, end")
setTimeout(() => {
resetPage();
console.log("watchGameState : setTimeout")
}, 5000);
}
}
function leaveMatch() { function leaveMatch() {
clearInterval(watchGameStateInterval);
resetPage(); resetPage();
}; };
async function resetPage() { async function resetPage() {
hiddenGame = true; hiddenGame = true;
pongSpectator.destroy(); pongSpectator.destroy();
// WIP: fetch for match list here fetchMatchList();
};
async function fetchMatchList() {
matchList = await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/game/match/all`) matchList = await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/game/match/all`)
.then( x => x.json() ); .then( x => x.json() );
console.log("matchList");
if (matchList.length <= 0)
hiddenMatchList = true;
console.log(matchList);
}; };
</script> </script>
<!-- --> <!-- -->
@@ -98,18 +89,33 @@
Might become useless after CSS rework. --> Might become useless after CSS rework. -->
<div id="game_page"> <div id="game_page">
{#if !hiddenGame}
{#if gameState.matchEnded}
<div class="div_game" in:fly="{{ y: 10, duration: 1000 }}">
<p>The match is finished !</p>
</div>
{/if}
{/if}
<div id="canvas_container" hidden={hiddenGame}> <div id="canvas_container" hidden={hiddenGame}>
<canvas id={gameAreaId}/> <canvas id={gameAreaId}/>
</div> </div>
{#if !hiddenGame}
{#if !gameState.matchEnded}
<div class="div_game">
<button class="pong_button" on:click={leaveMatch}>leave</button>
</div>
{/if}
{/if}
<!-- -->
{#if hiddenGame} {#if hiddenGame}
<div id="div_game"> <div class="div_game" in:fly="{{ y: 10, duration: 1000 }}">
<div id="game_options">
<fieldset> <fieldset>
{#if hiddenMatchList}
<legend>no match available</legend>
{:else}
<legend>options</legend> <legend>options</legend>
<button class="pong_button" on:click={fetchMatchList}>Reload</button>
<div> <div>
<p>sound :</p> <p>sound :</p>
<input type="radio" id="sound_on" name="sound_selector" bind:group={sound} value="on"> <input type="radio" id="sound_on" name="sound_selector" bind:group={sound} value="on">
@@ -117,19 +123,16 @@
<input type="radio" id="sound_off" name="sound_selector" bind:group={sound} value="off"> <input type="radio" id="sound_off" name="sound_selector" bind:group={sound} value="off">
<label for="sound_off">off</label> <label for="sound_off">off</label>
</div> </div>
</fieldset>
{#if matchList.length !== 0}
<menu id="match_list"> <menu id="match_list">
{#each matchList as match} {#each matchList as match}
<MatchListElem match={match} on:click={(e) => initGameSpectator(match.gameServerIdOfTheMatch, match.gameOptions)} /> <MatchListElem match={match} on:click={(e) => initGameSpectator(match.gameServerIdOfTheMatch, match.gameOptions)} />
{/each} {/each}
</menu> </menu>
{/if}
</fieldset>
</div>
</div>
{:else} {:else}
<div id="div_game"> <p>no match ongoing</p>
<button id="pong_button" on:click={leaveMatch}>leave match</button> {/if}
</div> </div>
{/if} {/if}
@@ -147,8 +150,10 @@
font-style: normal; font-style: normal;
font-display: swap; font-display: swap;
} }
#game_page { #game_page {
margin: 0; margin: 0;
padding: 20px;
background-color: #222425; background-color: #222425;
position: relative; position: relative;
width: 100%; width: 100%;
@@ -157,37 +162,43 @@
#canvas_container { #canvas_container {
margin-top: 20px; margin-top: 20px;
text-align: center; text-align: center;
/* border: dashed rgb(245, 245, 245) 5px; */
/* max-height: 80vh; */
/* overflow: hidden; */
} }
canvas { canvas {
/* background-color: #ff0000; */
background-color: #333333; background-color: #333333;
max-width: 75vw; max-width: 75vw;
/* max-height: 100vh; */
width: 80%; width: 80%;
} }
#div_game { .div_game {
margin-top: 20px;
text-align: center; text-align: center;
font-family: "Bit5x3"; font-family: "Bit5x3";
color: rgb(245, 245, 245); color: rgb(245, 245, 245);
font-size: x-large; font-size: x-large;
} }
#div_game fieldset { .div_game fieldset {
max-width: 50vw; max-width: 50vw;
width: auto; width: auto;
margin: 0 auto; margin: 0 auto;
} }
#div_game fieldset div { .div_game fieldset div {
padding: 10px; padding: 10px;
} }
#match_list { .pong_button {
font-family: 'Courier New', Courier, monospace;
font-size: large;
}
#pong_button {
font-family: "Bit5x3"; font-family: "Bit5x3";
color: rgb(245, 245, 245); color: rgb(245, 245, 245);
background-color: #333333; background-color: #333333;
font-size: x-large; font-size: x-large;
padding: 10px; padding: 10px;
} }
#match_list {
font-family: 'Courier New', Courier, monospace;
font-size: large;
}
</style> </style>

View File

@@ -27,30 +27,24 @@
} }
</script> </script>
<Header /> <Header />
<br />
<div class="container"> <div class="principal-div">
<div class="row"> <table class="stats-table">
<div class="col-12">
<h1>Ranking</h1>
</div>
</div>
<div class="row">
<div class="col-12">
<table class="table table-striped">
<thead> <thead>
<tr> <tr>
<th scope="col">#</th> <th>#</th>
<th scope="col">Username</th> <th>Username</th>
<th scope="col">Win</th> <th>Win</th>
<th scope="col">Lose</th> <th>Lose</th>
<th scope="col">Draw</th> <th>Draw</th>
<th scope="col">Games Played</th> <th>Games Played</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{#each allUsers as user, i} {#each allUsers as user, i}
<tr> <tr>
<th scope="row">{i + 1}</th> <th>{i + 1}</th>
{#if user.username === currentUser.username} {#if user.username === currentUser.username}
<td><b>You ({user.username})</b></td> <td><b>You ({user.username})</b></td>
{:else} {:else}
@@ -64,11 +58,47 @@
{/each} {/each}
</tbody> </tbody>
</table> </table>
</div>
</div>
</div> </div>
<style> <style>
.principal-div {
display: flex;
justify-content: center;
align-items: center;
}
.stats-table {
margin-left: auto;
margin-right: auto;
font-size: 0.9em;
font-family: sans-serif;
min-width: 400px;
}
.stats-table thead tr {
background-color: #618174;
color: #ffffff;
}
.stats-table th,
.stats-table td {
padding: 12px 15px;
}
.stats-table tbody tr {
border-bottom: 1px solid #dddddd;
}
.stats-table tbody tr:nth-of-type(even) {
background-color: #f3f3f3;
}
.stats-table tbody tr:last-of-type {
border-bottom: 2px solid #618174;
}
.stats-table tbody tr.active-row {
font-weight: bold;
color: #618174;
}
</style> </style>

View File

@@ -2,26 +2,37 @@
import * as c from "./constants.js" import * as c from "./constants.js"
// export const soundPongArr: HTMLAudioElement[] = []; // export const soundPongArr: HTMLAudioElement[] = [];
export const soundPongArr: HTMLAudioElement[] = [ export let muteFlag: boolean;
new Audio("http://" + process.env.WEBSITE_HOST + ":" + process.env.WEBSITE_PORT + "/sound/pong/"+1+".ogg"), export const soundPongArr: HTMLAudioElement[] = [];
new Audio("http://" + process.env.WEBSITE_HOST + ":" + process.env.WEBSITE_PORT + "/sound/pong/"+2+".ogg") export let soundRoblox: HTMLAudioElement;
];
export const soundRoblox = new Audio("http://" + process.env.WEBSITE_HOST + ":" + process.env.WEBSITE_PORT + "/sound/roblox-oof.ogg");
export function initAudio(sound: string) export function initAudio(sound: string)
{ {
let muteFlag: boolean; soundPongArr.length = 0;
soundRoblox = null;
if (sound === "on") { if (sound === "on") {
muteFlag = false; muteFlag = false;
} }
else { else {
muteFlag = true; muteFlag = true;
return; // Could be changed
/*
Stop initAudio() here because in the current state of the game
there no way to change muteFlag after game start.
If it becomes an option,
we should continue initAudio() regardless of the muteFlag.
*/
} }
soundPongArr.push(new Audio("http://" + process.env.WEBSITE_HOST + ":" + process.env.WEBSITE_PORT + "/sound/pong/"+1+".ogg"));
soundPongArr.push(new Audio("http://" + process.env.WEBSITE_HOST + ":" + process.env.WEBSITE_PORT + "/sound/pong/"+2+".ogg"));
soundPongArr.forEach((value) => { soundPongArr.forEach((value) => {
value.volume = c.soundRobloxVolume; value.volume = c.soundRobloxVolume;
value.muted = muteFlag; value.muted = muteFlag;
}); });
soundRoblox = new Audio("http://" + process.env.WEBSITE_HOST + ":" + process.env.WEBSITE_PORT + "/sound/roblox-oof.ogg");
soundRoblox.volume = c.soundRobloxVolume; soundRoblox.volume = c.soundRobloxVolume;
soundRoblox.muted = muteFlag; soundRoblox.muted = muteFlag;
} }

View File

@@ -6,6 +6,7 @@ export class GameArea {
handleInputInterval: number = 0; handleInputInterval: number = 0;
gameLoopInterval: number = 0; gameLoopInterval: number = 0;
drawLoopInterval: number = 0; drawLoopInterval: number = 0;
timeoutArr: number[] = [];
canvas: HTMLCanvasElement; canvas: HTMLCanvasElement;
ctx: CanvasRenderingContext2D; ctx: CanvasRenderingContext2D;
constructor(canvas_id: string) { constructor(canvas_id: string) {

View File

@@ -7,13 +7,13 @@ export class InitOptions {
isInvitedPerson = false; isInvitedPerson = false;
playerOneUsername = ""; playerOneUsername = "";
playerTwoUsername = ""; playerTwoUsername = "";
reset() { reset(playerOneUsername: string) {
this.sound = "off"; this.sound = "off";
this.multi_balls = false; this.multi_balls = false;
this.moving_walls = false; this.moving_walls = false;
this.isSomeoneIsInvited = false; this.isSomeoneIsInvited = false;
this.isInvitedPerson = false; this.isInvitedPerson = false;
this.playerOneUsername = ""; this.playerOneUsername = playerOneUsername;
this.playerTwoUsername = ""; this.playerTwoUsername = "";
} }
} }

View File

@@ -2,7 +2,7 @@
import { Vector, VectorInteger } from "../../shared_js/class/Vector.js"; import { Vector, VectorInteger } from "../../shared_js/class/Vector.js";
import type { GraphicComponent } from "../../shared_js/class/interface.js"; import type { GraphicComponent } from "../../shared_js/class/interface.js";
import { Rectangle, MovingRectangle, Racket, Ball } from "../../shared_js/class/Rectangle.js"; import { Rectangle, MovingRectangle, Racket, Ball } from "../../shared_js/class/Rectangle.js";
import { soundPongArr } from "../audio.js" import { muteFlag, soundPongArr } from "../audio.js"
import { random } from "../utils.js"; import { random } from "../utils.js";
function updateRectangle(this: RectangleClient) { function updateRectangle(this: RectangleClient) {
@@ -70,6 +70,7 @@ export class BallClient extends Ball implements GraphicComponent {
color: string; color: string;
update: () => void; update: () => void;
clear: (pos?: VectorInteger) => void; clear: (pos?: VectorInteger) => void;
soundSwitch = false;
constructor(pos: VectorInteger, size: number, baseSpeed: number, speedIncrease: number, constructor(pos: VectorInteger, size: number, baseSpeed: number, speedIncrease: number,
ctx: CanvasRenderingContext2D, color: string) ctx: CanvasRenderingContext2D, color: string)
{ {
@@ -81,9 +82,14 @@ export class BallClient extends Ball implements GraphicComponent {
} }
bounce(collider?: Rectangle) { bounce(collider?: Rectangle) {
this._bounceAlgo(collider); this._bounceAlgo(collider);
let i = Math.floor(random(0, soundPongArr.length)); if (!muteFlag)
soundPongArr[ i ].play(); {
console.log(`sound_i=${i}`); // debug log this.soundSwitch = !this.soundSwitch;
soundPongArr[this.soundSwitch ? 1 : 0].play();
// let i = Math.floor(random(0, soundPongArr.length));
// soundPongArr[ i ].play();
// console.log(`sound_i=${i}`); // debug log
}
} }
} }

View File

@@ -34,15 +34,28 @@ export function initBase(matchOptions: en.MatchOptions, sound: string, gameAreaI
export function destroyBase() export function destroyBase()
{ {
if (pong) if (socket && (socket.OPEN || socket.CONNECTING)) {
{
clearInterval(pong.handleInputInterval);
clearInterval(pong.gameLoopInterval);
clearInterval(pong.drawLoopInterval);
setPong(null);
}
if (socket && socket.OPEN) {
socket.close(); socket.close();
} }
if (pong)
{
pong.timeoutArr.forEach((value) => {
clearTimeout(value);
});
pong.timeoutArr = [];
clearInterval(pong.handleInputInterval);
pong.handleInputInterval = null;
clearInterval(pong.gameLoopInterval);
pong.gameLoopInterval = null;
clearInterval(pong.drawLoopInterval);
pong.drawLoopInterval = null;
setPong(null);
}
setGc(null);
setMatchOptions(null);
resetGameState(); resetGameState();
} }

View File

@@ -1,25 +1,24 @@
import * as c from "./constants.js" import * as c from "./constants.js"
import type * as en from "../shared_js/enums.js"
import { handleInput } from "./handleInput.js"; import { handleInput } from "./handleInput.js";
import { gameLoop } from "./gameLoop.js" import { gameLoop } from "./gameLoop.js"
import { drawLoop } from "./draw.js"; import { drawLoop } from "./draw.js";
import { countdown } from "./utils.js"; import { countdown } from "./utils.js";
import { initWebSocket } from "./ws.js"; import { gameState, initWebSocket } from "./ws.js";
import type { InitOptions } from "./class/InitOptions.js"; import type { InitOptions } from "./class/InitOptions.js";
export { InitOptions } from "./class/InitOptions.js"; export { InitOptions } from "./class/InitOptions.js";
import { initBase, destroyBase, computeMatchOptions } from "./init.js"; import { initBase, destroyBase, computeMatchOptions } from "./init.js";
export { computeMatchOptions } from "./init.js"; export { computeMatchOptions } from "./init.js";
/* TODO: A way to delay the init of variables, but still use "const" not "let" ? */
import { pong, gc } from "./global.js" import { pong, gc } from "./global.js"
import { setStartFunction } from "./global.js" import { setStartFunction } from "./global.js"
let abortControllerKeydown: AbortController; let abortControllerKeydown: AbortController;
let abortControllerKeyup: AbortController; let abortControllerKeyup: AbortController;
export function init(options: InitOptions, gameAreaId: string, token: string) export function init(matchOptions: en.MatchOptions, options: InitOptions, gameAreaId: string, token: string)
{ {
const matchOptions = computeMatchOptions(options);
initBase(matchOptions, options.sound, gameAreaId); initBase(matchOptions, options.sound, gameAreaId);
setStartFunction(start); setStartFunction(start);
@@ -47,11 +46,12 @@ export function destroy()
function start() function start()
{ {
gc.text1.pos.assign(c.w*0.5, c.h*0.75); gc.text1.pos.assign(c.w*0.5, c.h*0.75);
countdown(c.matchStartDelay/1000, (count: number) => { const countdownArr = countdown(c.matchStartDelay, 1000, (count: number) => {
gc.text1.clear(); gc.text1.clear();
gc.text1.text = `${count}`; gc.text1.text = `${count/1000}`;
gc.text1.update(); gc.text1.update();
}, start_after_countdown); }, start_after_countdown);
pong.timeoutArr = pong.timeoutArr.concat(countdownArr);
} }
function start_after_countdown() function start_after_countdown()
@@ -66,7 +66,7 @@ function start_after_countdown()
abortControllerKeyup = new AbortController(); abortControllerKeyup = new AbortController();
window.addEventListener( window.addEventListener(
'keyup', 'keyup',
(e) => { pong.deleteKey(e.key);}, (e) => { pong.deleteKey(e.key); },
{signal: abortControllerKeyup.signal} {signal: abortControllerKeyup.signal}
); );

View File

@@ -1,16 +1,25 @@
export * from "../shared_js/utils.js" export * from "../shared_js/utils.js"
export function countdown(count: number, callback?: (count: number) => void, endCallback?: () => void) export function countdown(count: number, step: number, callback: (count: number) => void, endCallback?: () => void) : number[]
{ {
const timeoutArr: number[] = [];
if (endCallback) {
timeoutArr.push( window.setTimeout(endCallback, count) );
}
let reverseCount = 0;
while (count > 0)
{
timeoutArr.push( window.setTimeout((count: number) => {
console.log("countdown ", count); console.log("countdown ", count);
if (count > 0) {
if (callback) {
callback(count); callback(count);
}, reverseCount, count)
);
count -= step;
reverseCount += step;
} }
setTimeout(countdown, 1000, --count, callback, endCallback);
} return timeoutArr;
else if (endCallback) {
endCallback();
}
} }

View File

@@ -6,18 +6,20 @@ import * as en from "../shared_js/enums.js"
import * as msg from "./message.js"; import * as msg from "./message.js";
import type { RacketClient } from "./class/RectangleClient.js"; import type { RacketClient } from "./class/RectangleClient.js";
import { repeatInput } from "./handleInput.js"; import { repeatInput } from "./handleInput.js";
import { soundRoblox } from "./audio.js" import { muteFlag, soundRoblox } from "./audio.js"
import { sleep } from "./utils.js"; import { sleep } from "./utils.js";
import { Vector, VectorInteger } from "../shared_js/class/Vector.js"; import { Vector, VectorInteger } from "../shared_js/class/Vector.js";
export const gameState = { export const gameState = {
matchStarted: false,
matchEnded: false, matchEnded: false,
matchAbort: false matchAborted: false
} }
export function resetGameState() { export function resetGameState() {
gameState.matchStarted = false;
gameState.matchEnded = false; gameState.matchEnded = false;
gameState.matchAbort = false; gameState.matchAborted = false;
} }
class ClientInfo { class ClientInfo {
@@ -36,7 +38,7 @@ class ClientInfoSpectator {
} }
const wsUrl = "ws://" + process.env.WEBSITE_HOST + ":" + process.env.WEBSITE_PORT + "/pong"; const wsUrl = "ws://" + process.env.WEBSITE_HOST + ":" + process.env.WEBSITE_PORT + "/pong";
export let socket: WebSocket; /* TODO: A way to still use "const" not "let" ? */ export let socket: WebSocket;
export const clientInfo = new ClientInfo(); export const clientInfo = new ClientInfo();
export const clientInfoSpectator = new ClientInfoSpectator(); // WIP, could refactor this export const clientInfoSpectator = new ClientInfoSpectator(); // WIP, could refactor this
@@ -99,12 +101,13 @@ function preMatchListener(this: WebSocket, event: MessageEvent)
msg.matchmakingComplete(); msg.matchmakingComplete();
break; break;
case en.EventTypes.matchStart: case en.EventTypes.matchStart:
gameState.matchStarted = true;
socket.removeEventListener("message", preMatchListener); socket.removeEventListener("message", preMatchListener);
socket.addEventListener("message", inGameListener); socket.addEventListener("message", inGameListener);
startFunction(); startFunction();
break; break;
case en.EventTypes.matchAbort: case en.EventTypes.matchAbort:
gameState.matchAbort = true; gameState.matchAborted = true;
socket.removeEventListener("message", preMatchListener); socket.removeEventListener("message", preMatchListener);
msg.matchAbort(); msg.matchAbort();
break; break;
@@ -195,12 +198,15 @@ function gameUpdate(data: ev.EventGameUpdate)
function scoreUpdate(data: ev.EventScoreUpdate) function scoreUpdate(data: ev.EventScoreUpdate)
{ {
// console.log("scoreUpdate"); // console.log("scoreUpdate");
if (!muteFlag)
{
if (clientInfo.side === en.PlayerSide.left && data.scoreRight > gc.scoreRight.value) { if (clientInfo.side === en.PlayerSide.left && data.scoreRight > gc.scoreRight.value) {
soundRoblox.play(); soundRoblox.play();
} }
else if (clientInfo.side === en.PlayerSide.right && data.scoreLeft > gc.scoreLeft.value) { else if (clientInfo.side === en.PlayerSide.right && data.scoreLeft > gc.scoreLeft.value) {
soundRoblox.play(); soundRoblox.play();
} }
}
gc.scoreLeft.value = data.scoreLeft; gc.scoreLeft.value = data.scoreLeft;
gc.scoreRight.value = data.scoreRight; gc.scoreRight.value = data.scoreRight;
} }
@@ -242,6 +248,7 @@ export function preMatchListenerSpectator(this: WebSocket, event: MessageEvent)
const data: ev.ServerEvent = JSON.parse(event.data); const data: ev.ServerEvent = JSON.parse(event.data);
if (data.type === en.EventTypes.matchStart) if (data.type === en.EventTypes.matchStart)
{ {
gameState.matchStarted = true;
socket.removeEventListener("message", preMatchListenerSpectator); socket.removeEventListener("message", preMatchListenerSpectator);
socket.addEventListener("message", inGameListenerSpectator); socket.addEventListener("message", inGameListenerSpectator);
socket.send(JSON.stringify( new ev.ClientEvent(en.EventTypes.clientSpectatorReady) )); socket.send(JSON.stringify( new ev.ClientEvent(en.EventTypes.clientSpectatorReady) ));
@@ -322,10 +329,5 @@ function matchEndSpectator(data: ev.EventMatchEnd)
console.log("matchEndSpectator"); console.log("matchEndSpectator");
gameState.matchEnded = true; gameState.matchEnded = true;
socket.close(); socket.close();
// WIP
/* msg.win();
if (data.forfeit) {
msg.forfeit(clientInfo.side);
} */
} }

View File

@@ -31,6 +31,7 @@ export enum InputEnum {
} }
export enum PlayerSide { export enum PlayerSide {
noSide = 0,
left = 1, left = 1,
right right
} }

View File

@@ -1,54 +1,11 @@
<script lang="ts"> <script lang="ts">
import Layouts from './Chat_layouts.svelte'; import Layouts from './Chat_layouts.svelte';
import { init_socket } from './Socket_init';
export let color = "transparent"; export let color = "transparent";
/* web sockets with socket.io init_socket();
*/
import { onMount } from 'svelte';
import io from 'socket.io-client';
const socket = io(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}`, {
path: '/chat'
});
onMount(async => {
socket.on('connect', function(){
console.log("socket.io connected");
});
socket.on('disconnect', function(){
console.log("socket.io disconnected");
});
socket.on('connect_error', function(){
console.log("socket.io connect_error");
});
socket.on('connect_timeout', function(){
console.log("socket.io connect_timeout");
});
socket.on('error', function(){
console.log("socket.io error");
});
socket.on('reconnect', function(){
console.log("socket.io reconnect");
});
socket.on('reconnect_attempt', function(){
console.log("socket.io reconnect_attempt");
});
socket.on('reconnecting', function(){
console.log("socket.io reconnecting");
});
socket.on('reconnect_error', function(){
console.log("socket.io reconnect_error");
});
socket.on('reconnect_failed', function(){
console.log("socket.io reconnect_failed");
});
socket.on('ping', function(){
console.log("socket.io ping");
});
socket.on('pong', function(){
console.log("socket.io pong");
});
});
</script> </script>

View File

@@ -1,12 +1,12 @@
<script lang="ts"> <script lang="ts">
import { layout } from './Store_chat';
export let color; export let color;
export let layout;
</script> </script>
<div class="{layout} chat_box" style="background-color: {color};"> <div class="{$layout} chat_box" style="background-color: {color};">
<slot></slot> <slot></slot>
</div> </div>

View File

@@ -1,6 +1,8 @@
<script lang="ts"> <script lang="ts">
import Debug from './tmp_debug.svelte';
import { layout } from './Store_chat';
import ChatBox from './Chat_box_css.svelte'; import ChatBox from './Chat_box_css.svelte';
import CloseLayout from './Layout_close.svelte'; import CloseLayout from './Layout_close.svelte';
@@ -14,68 +16,75 @@
import MuteLayout from './Layout_mute.svelte'; import MuteLayout from './Layout_mute.svelte';
import UserLayout from './Layout_user.svelte'; import UserLayout from './Layout_user.svelte';
import Button from './Chat_button.svelte'; import Button from './Element_button.svelte';
/* global variables /* global variables
*/ */
export let color; export let color;
let room = ""; let room = "";
let admin = false; let admin = false;
let layout = "close";
let layouts = ["home", "home"]; let layouts = ["home", "home"];
/* hold previous version of layout, to go back /* hold previous version of layout, to go back
*/ */
function set_layouts(layout) function set_layouts($layout)
{ {
if (layout === "close") console.log("layouts:", layouts);
console.log("layout:", $layout);
if ($layout.length === 0)
layout.set(layouts[0]);
else if ($layout === "close")
return; return;
if (layout === layouts[0]) else if ($layout === layouts[0])
return; return;
if (layout === layouts[1]) else if ($layout === layouts[1])
layouts = [layout, "home"]; layouts = [$layout, "home"];
else else
layouts = [layout, layouts[0]]; layouts = [$layout, layouts[0]];
console.log("- layouts:", layouts);
} }
$: set_layouts(layout); $: set_layouts($layout);
</script> </script>
<ChatBox layout={layout} color={color}> <ChatBox color={color}>
{#if layout === "home"} {#if $layout === "home"}
<HomeLayout bind:layout /> <HomeLayout />
{:else if layout === "close"} {:else if $layout === "close"}
<CloseLayout bind:layout /> <CloseLayout back={layouts[0]} />
{:else if layout === "room"} {:else if $layout === "room"}
<RoomLayout bind:layout back={layouts[1]} /> <RoomLayout back={layouts[1]} />
{:else if layout === "new"} {:else if $layout === "new"}
<NewLayout bind:layout back={layouts[1]} /> <NewLayout back={layouts[1]} />
{:else if layout === "settings"} {:else if $layout === "settings"}
<SettingsLayout bind:layout back={layouts[1]} /> <SettingsLayout back={layouts[1]} />
{:else if layout === "room_set"} {:else if $layout === "room_set"}
<RoomsetLayout bind:layout back={layouts[1]} /> <RoomsetLayout back={layouts[1]} />
{:else if layout === "protected"} {:else if $layout === "protected"}
<ProtectedLayout bind:layout back={layouts[1]} /> <ProtectedLayout back={layouts[1]} />
{:else if layout === "create"} {:else if $layout === "create"}
<CreateLayout bind:layout back={layouts[1]} /> <CreateLayout back={layouts[1]} />
{:else if layout === "mute"} {:else if $layout === "mute"}
<MuteLayout bind:layout back={layouts[1]} /> <MuteLayout back={layouts[1]} />
{:else if layout === "user"} {:else if $layout === "user"}
<UserLayout bind:layout back={layouts[1]} /> <UserLayout back={layouts[1]} />
{/if} {/if}
</ChatBox> </ChatBox>
<!-- TMP DEBUG -->
<Debug bind:layouts />
<style></style> <style></style>

View File

@@ -1,4 +1,3 @@
<!-- <!--
<Button <Button
bind:layout bind:layout
@@ -13,14 +12,15 @@
<script lang="ts"> <script lang="ts">
import { layout } from './Store_chat';
export let my_class = ""; export let my_class = "";
export let my_title = ""; export let my_title = "";
export let layout = "";
export let new_layout = ""; export let new_layout = "";
export let on_click = ""; export let on_click = "";
function update_layout() { function update_layout() {
layout = new_layout; layout.set(new_layout);
} }
</script> </script>
@@ -67,7 +67,7 @@
} }
/* for btn list /* .list
*/ */
.list:not(:hover) { .list:not(:hover) {
background-color: rgb(240, 240, 240); background-color: rgb(240, 240, 240);
@@ -77,14 +77,14 @@
} }
/* for transparent btn /* .transparent
*/ */
.transparent:not(:hover) { .transparent:not(:hover) {
background-color: transparent; background-color: transparent;
} }
/* for deactivated btn /* .deactivated
*/ */
.deactivate { .deactivate {
background-color: transparent; background-color: transparent;
@@ -92,7 +92,40 @@
} }
/* for icon /* .border
*/
.border {
border: 1px solid rgb(150, 150, 150);
}
/* .light
*/
.light {
background-color: rgb(233, 233, 233);
}
.light.border {
border: 1px solid rgb(204, 204, 204);
}
.light:hover {
background-color: rgb(220, 220, 220);
}
.light.border:hover {
border-color: rgb(200, 200, 200);
}
.light:active {
background-color: rgb(210, 210, 210);
}
/* .thin
*/
.thin p {
padding: 5px;
}
/* .icon
*/ */
.icon p { .icon p {
display: none; display: none;
@@ -107,7 +140,7 @@
} }
/* for 3 dots btn /* .dots
*/ */
.dots::after { .dots::after {
content: '\2807'; content: '\2807';
@@ -123,7 +156,7 @@
} }
/* for close btn /* .close
*/ */
.close::before { .close::before {
content: ""; content: "";
@@ -136,7 +169,7 @@
} }
/* for back btn /* .back
*/ */
.back::before { .back::before {
content: ""; content: "";
@@ -151,7 +184,7 @@
} }
/* for blocked user /* .blocked
https://www.fileformat.info/info/unicode/category/So/list.htm https://www.fileformat.info/info/unicode/category/So/list.htm
U+1F512 LOCK 🔒 U+1F512 LOCK 🔒
U+1F513 OPEN LOCK 🔓 U+1F513 OPEN LOCK 🔓

View File

@@ -0,0 +1,11 @@
<script>
export let content = "warning";
export let bg_color = "rgb(201, 87, 34)";
export let color = "rgb(240, 240, 240)";
</script>
<p style="background-color: {bg_color}; color: {color};">
{content}
</p>

View File

@@ -1,13 +1,14 @@
<script lang="ts">
<script> import { layout } from './Store_chat';
import Button from './Element_button.svelte';
import Button from './Chat_button.svelte'; export let back = "";
export let layout;
</script> </script>
<div class="grid_box"> <div class="grid_box">
<Button bind:layout new_layout="home" my_class="chat"> <Button new_layout={back} my_class="chat">
chat chat
</Button> </Button>
</div> </div>

View File

@@ -1,16 +1,41 @@
<script lang="ts">
<script> import { msgs, layout } from './Store_chat';
import { change_room, create_room } from './Request_rooms';
import Button from './Element_button.svelte';
import Warning from './Element_warning.svelte';
import Button from './Chat_button.svelte';
export let layout = "";
export let back = ""; export let back = "";
let room_name: string;
let room_type: string;
let room_password: string;
let response = {
status: 0,
message: "",
}
async function handleSubmit(evt)
{
let formIsValid = evt.target.checkValidity();
if (!formIsValid)
return;
// send the new room
response = await create_room(room_name, room_type);
// go to room
if (response.status === 200)
await change_room(room_name);
}
</script> </script>
<div class="grid_box"> <div class="grid_box">
<!-- back --> <!-- back -->
<Button bind:layout new_layout={back} my_class="back icon" my_title="go back {back}"> <Button new_layout={back} my_class="back icon" my_title="go back {back}">
back back
</Button> </Button>
@@ -20,37 +45,45 @@
</Button> </Button>
<!-- close --> <!-- close -->
<Button bind:layout new_layout="close" my_class="close icon"> <Button new_layout="close" my_class="close icon">
close close
</Button> </Button>
<!-- panel_create --> <!-- panel_create -->
<div class="panel panel_create"> <div class="panel panel_create __border_top">
<form> <form on:submit|preventDefault={handleSubmit}>
{#if response.status >= 300}
<Warning content={response.message}/>
{/if}
<!-- name: --> <!-- name: -->
<label for="chat_name"><p>new room name :</p></label> <label for="chat_name"><p>new room name :</p></label>
<input id="chat_name" required> <input id="chat_name" bind:value={room_name} name="room_name" required>
<!-- [ ] pubic --> <!-- [ ] pubic -->
<input id="chat_public" type="radio" name="chat_create_type" checked> <label for="chat_public" class="_radio">
<label for="chat_public" class="_radio"><p>public</p></label> <p>public</p>
<input id="chat_public" bind:group={room_type} type="radio" name="room_type" value="public" required>
</label>
<!-- [ ] private --> <!-- [ ] private -->
<input id="chat_private" type="radio" name="chat_create_type"> <label for="chat_private" class="_radio hide">
<label for="chat_private" class="_radio"><p>private</p></label> <p>private</p>
<input id="chat_private" bind:group={room_type} type="radio" name="room_type" value="private" required>
</label>
<!-- [ ] protected --> <!-- [ ] protected -->
<input id="chat_protected" class="__check_change_next" type="radio" name="chat_create_type"> <label for="chat_protected" class="_radio hide">
<label for="chat_protected" class="_radio"><p>protected</p></label> <p>protected</p>
<input id="chat_protected" bind:group={room_type} type="radio" name="room_type" value="protected" required>
</label>
<!-- [x] protected --> <!-- [x] protected -->
<div class="__to_show"> {#if room_type === 'protected'}
<div>
<label for="chat_pswd"><p>choose a password :</p></label> <label for="chat_pswd"><p>choose a password :</p></label>
<input id="chat_pswd" type="password" placeholder="minimum 8 characters" minlength="8"> <input id="chat_pswd" bind:value={room_password} type="password" placeholder="minimum 8 characters" minlength="8" name="password" required>
<p>confirm password :</p>
<input type="password">
</div> </div>
{/if}
<input type="submit" value="&#x2BA1"> <input type="submit" value="&#x2BA1">
</form> </form>
</div> </div>
</div> </div>
<style> <style>
@@ -68,42 +101,32 @@
/ auto 1fr auto ; / auto 1fr auto ;
} }
/* temp
*/
.hide {
display: none !important;
}
/* radio elements style check /* radio elements style check
*/ */
form input[type=radio] { .panel label._radio {
display: none; display: inline;
} margin: 10px 0px 0px auto;
form label._radio {
margin: 0px 20px 0px auto;
padding-right: 10px; padding-right: 10px;
cursor: pointer; cursor: pointer;
} }
form label._radio p { .panel label._radio * {
display: inline;
}
.panel label._radio p {
margin-top: 0px; margin-top: 0px;
margin-bottom: 0px; margin-bottom: 0px;
} }
form label._radio::after {
content: "";
position: absolute;
top: calc(50% - 6px);
right: 0px;
width: 12px;
height: 12px;
border-radius: 6px;
border: 2px solid rgb(150, 150, 150);
box-sizing: border-box;
cursor: pointer;
}
form input[type=radio]:checked
+ label._radio::after {
background-color: rgb(200, 200, 200);
}
/* submit /* submit
*/ */
form input[type=submit] { .panel input[type=submit] {
margin-top: 20px; margin-top: 20px;
} }

View File

@@ -1,25 +1,36 @@
<script> <script>
import Button from './Chat_button.svelte'; import { layout, msgs, user } from './Store_chat';
export let layout; import { change_room, get_room_messages, get_all_rooms } from './Request_rooms';
import { onMount } from 'svelte';
import Button from './Element_button.svelte';
let rooms = get_all_rooms();
// go to clicked room
async function go_to_room(evt)
{
console.log("inside go_to_room");
await change_room(evt.target.innerText);
await get_room_messages();
}
</script> </script>
<div class="grid_box"> <div class="grid_box">
<!-- settings --> <!-- settings -->
<Button bind:layout new_layout="settings" my_class="settings dots icon"> <Button new_layout="settings" my_class="settings dots icon">
settings settings
</Button> </Button>
<!-- new --> <!-- new -->
<Button bind:layout new_layout="new" my_class="new transparent"> <Button new_layout="new" my_class="new transparent">
new new
</Button> </Button>
<!-- close --> <!-- close -->
<Button bind:layout new_layout="close" my_class="close icon"> <Button new_layout="close" my_class="close icon">
close close
</Button> </Button>
@@ -30,18 +41,16 @@
<div class="__show_if_only_child"> <div class="__show_if_only_child">
<p class="__center">/ you have no chat room yet /</p> <p class="__center">/ you have no chat room yet /</p>
</div> </div>
<!-- placeholders {#await rooms}
<Button bind:layout new_layout="room" my_class="list"> <!-- promise is pending -->
a room <p>rooms are loaded...</p>
{:then rooms}
{#each rooms as room}
<Button my_class="list" on_click={go_to_room}>
{room.name}
</Button> </Button>
<Button bind:layout new_layout="room" my_class="list"> {/each}
another room {/await}
</Button>
<Button bind:layout new_layout="room" my_class="list">
placeholder
</Button>
------------- -->
<!-- END placeholders -->
</div> </div>
</div> </div>

View File

@@ -1,8 +1,8 @@
<script> <script>
import Button from './Chat_button.svelte'; import { layout } from './Store_chat';
export let layout = ""; import Button from './Element_button.svelte';
export let back = ""; export let back = "";
</script> </script>
@@ -10,7 +10,7 @@
<div class="grid_box"> <div class="grid_box">
<!-- back --> <!-- back -->
<Button bind:layout new_layout={back} my_class="back icon" my_title="go back {back}"> <Button new_layout={back} my_class="back icon" my_title="go back {back}">
back back
</Button> </Button>
@@ -20,7 +20,7 @@
</Button> </Button>
<!-- close --> <!-- close -->
<Button bind:layout new_layout="close" my_class="close icon"> <Button new_layout="close" my_class="close icon">
close close
</Button> </Button>

View File

@@ -1,16 +1,40 @@
<script> <script>
import Button from './Chat_button.svelte'; import { layout, msgs, user, socket } from './Store_chat';
export let layout = ""; import { join_room, change_room, get_room_messages } from './Request_rooms';
import Button from './Element_button.svelte';
export let back = ""; export let back = "";
let rooms = [];
// ask api for the rooms
const get_rooms = fetch('/api/v2/chat/allrooms')
.then(resp => resp.json())
.then(data =>
{
console.log("data.rooms:", data.rooms);
for (let room of data.rooms)
console.log(room.name);
rooms = data.rooms;
});
// join the room
async function join_rooms(evt)
{
console.log("inside join_room");
let room_name = evt.target.innerText;
await join_room(room_name);
await change_room(room_name);
}
</script> </script>
<div class="grid_box"> <div class="grid_box">
<!-- back --> <!-- back -->
<Button bind:layout new_layout={back} my_class="back icon" my_title="go back {back}"> <Button new_layout={back} my_class="back icon" my_title="go back {back}">
back back
</Button> </Button>
@@ -20,13 +44,13 @@
</Button> </Button>
<!-- close --> <!-- close -->
<Button bind:layout new_layout="close" my_class="close icon"> <Button new_layout="close" my_class="close icon">
close close
</Button> </Button>
<!-- panel_new --> <!-- panel_new -->
<div class="panel panel_new __border_top"> <div class="panel panel_new __border_top">
<Button bind:layout new_layout="create" my_class="create"> <Button new_layout="create" my_class="create">
create create
</Button> </Button>
<p>join room :</p> <p>join room :</p>
@@ -34,36 +58,16 @@
<div class="__show_if_only_child"> <div class="__show_if_only_child">
<p class="__center">/ there are no public rooms yet /</p> <p class="__center">/ there are no public rooms yet /</p>
</div> </div>
<!-- placeholders {#await get_rooms}
<Button bind:layout new_layout="room" my_class="list"> <!-- promise is pending -->
placeholder <p>rooms are loaded...</p>
{:then}
{#each rooms as room}
<Button my_class="list" on_click={join_rooms}>
{room.name}
</Button> </Button>
<Button bind:layout new_layout="room" my_class="list"> {/each}
join room {/await}
</Button>
<Button bind:layout new_layout="room" my_class="list">
one room
</Button>
<Button bind:layout new_layout="room" my_class="list">
another room
</Button>
<Button bind:layout new_layout="room" my_class="list">
one room
</Button>
<Button bind:layout new_layout="room" my_class="list">
another room
</Button>
<Button bind:layout new_layout="room" my_class="list">
one room
</Button>
<Button bind:layout new_layout="room" my_class="list">
another room
</Button>
<Button bind:layout new_layout="room" my_class="list">
one more room
</Button>
------------- -->
<!-- END placeholders -->
</div> </div>
</div> </div>

View File

@@ -1,8 +1,8 @@
<script> <script>
import Button from './Chat_button.svelte'; import { layout } from './Store_chat';
export let layout = ""; import Button from './Element_button.svelte';
export let back = ""; export let back = "";
</script> </script>
@@ -10,7 +10,7 @@
<div class="grid_box"> <div class="grid_box">
<!-- back --> <!-- back -->
<Button bind:layout new_layout={back} my_class="back icon" my_title="go back {back}"> <Button new_layout={back} my_class="back icon" my_title="go back {back}">
back back
</Button> </Button>
@@ -20,7 +20,7 @@
</Button> </Button>
<!-- close --> <!-- close -->
<Button bind:layout new_layout="close" my_class="close icon"> <Button new_layout="close" my_class="close icon">
close close
</Button> </Button>

View File

@@ -1,36 +1,28 @@
<script> <script>
import Button from './Chat_button.svelte'; import { layout, socket, msgs, add_msg, room_name } from './Store_chat';
import Msg from './Chat_msg.svelte'; import Button from './Element_button.svelte';
import io from 'socket.io-client'; import Msg from './Element_msg.svelte';
export let layout = "";
export let back = ""; export let back = "";
let msg = ""; let msg = "";
let text_area; let text_area;
let msgs = [];
function add_msg(from, the_msg)
{
msgs = [...msgs, { content: the_msg, name: from }];
}
function send_msg() function send_msg()
{ {
msg = msg.trim(); msg = msg.trim();
if (msg.length > 0) { if (msg.length > 0) {
//socket.emit('sendmsg', msg); socket.emit('message', msg);
add_msg("me", msg); add_msg("me", msg);
console.log(msgs);
} }
msg = ""; msg = "";
text_area.focus(); text_area.focus();
} }
function send_msg_if(evt) function enter_send_msg(evt)
{ {
if (evt.shiftKey && evt.key === "Enter") if (evt.shiftKey && evt.key === "Enter")
{ {
@@ -44,25 +36,25 @@
<div class="grid_box"> <div class="grid_box">
<!-- back --> <!-- back -->
<Button bind:layout new_layout={back} my_class="back icon" my_title="go back {back}"> <Button new_layout={back} my_class="back icon" my_title="go back {back}">
back back
</Button> </Button>
<!-- room_name --> <!-- room_name -->
<Button bind:layout new_layout="room_set" my_class="room_name transparent"> <Button new_layout="room_set" my_class="room_name transparent">
&lt;room_name&gt; {$room_name}
</Button> </Button>
<!-- close --> <!-- close -->
<Button bind:layout new_layout="close" my_class="close icon"> <Button new_layout="close" my_class="close icon">
close close
</Button> </Button>
<!-- msg --> <!-- msg -->
<div class="panel panel_msg"> <div class="panel panel_msg">
<div class="msg_thread"> <div class="msg_thread">
{#each msgs as msg} {#each $msgs as msg}
<Msg name={msg.name}>{@html msg.content}</Msg> <Msg name={msg.name}>{@html msg.message}</Msg>
{/each} {/each}
</div> </div>
</div> </div>
@@ -73,7 +65,7 @@
class="text_area" class="text_area"
bind:innerHTML={msg} bind:innerHTML={msg}
bind:this={text_area} bind:this={text_area}
on:keypress={send_msg_if} on:keypress={enter_send_msg}
contenteditable="true" contenteditable="true"
></div> ></div>
</div> </div>

View File

@@ -1,8 +1,8 @@
<script> <script>
import Button from './Chat_button.svelte'; import { layout } from './Store_chat';
export let layout = ""; import Button from './Element_button.svelte';
export let back = ""; export let back = "";
</script> </script>
@@ -10,7 +10,7 @@
<div class="grid_box"> <div class="grid_box">
<!-- back --> <!-- back -->
<Button bind:layout new_layout={back} my_class="back icon" my_title="go back {back}"> <Button new_layout={back} my_class="back icon" my_title="go back {back}">
back back
</Button> </Button>
@@ -20,13 +20,13 @@
</Button> </Button>
<!-- close --> <!-- close -->
<Button bind:layout new_layout="close" my_class="close icon"> <Button new_layout="close" my_class="close icon">
close close
</Button> </Button>
<!-- panel_room_set --> <!-- panel_room_set -->
<div class="panel panel_room_set __border_top"> <div class="panel panel_room_set __border_top">
<Button bind:layout new_layout="create" my_class="create"> <Button new_layout="create" my_class="create">
leave leave
</Button> </Button>
<p>room users :</p> <p>room users :</p>
@@ -36,16 +36,16 @@
</div> </div>
<!-- placeholders <!-- placeholders
------------- --> ------------- -->
<Button bind:layout new_layout="user" my_class="list"> <Button new_layout="user" my_class="list">
user 1 user 1
</Button> </Button>
<Button bind:layout new_layout="user" my_class="list blocked"> <Button new_layout="user" my_class="list blocked">
user 2 user 2
</Button> </Button>
<Button bind:layout new_layout="user" my_class="list"> <Button new_layout="user" my_class="list">
user 3 user 3
</Button> </Button>
<Button bind:layout new_layout="user" my_class="list"> <Button new_layout="user" my_class="list">
user 4 user 4
</Button> </Button>
<!-- END placeholders --> <!-- END placeholders -->

View File

@@ -1,8 +1,8 @@
<script> <script>
import Button from './Chat_button.svelte'; import { layout } from './Store_chat';
export let layout = ""; import Button from './Element_button.svelte';
export let back = ""; export let back = "";
</script> </script>
@@ -10,7 +10,7 @@
<div class="grid_box"> <div class="grid_box">
<!-- back --> <!-- back -->
<Button bind:layout new_layout={back} my_class="back icon" my_title="go back {back}"> <Button new_layout={back} my_class="back icon" my_title="go back {back}">
back back
</Button> </Button>
@@ -20,7 +20,7 @@
</Button> </Button>
<!-- close --> <!-- close -->
<Button bind:layout new_layout="close" my_class="close icon"> <Button new_layout="close" my_class="close icon">
close close
</Button> </Button>

View File

@@ -1,8 +1,8 @@
<script> <script>
import Button from './Chat_button.svelte'; import { layout } from './Store_chat';
export let layout = ""; import Button from './Element_button.svelte';
export let back = ""; export let back = "";
let mute = "mute"; let mute = "mute";
@@ -14,7 +14,7 @@
<div class="grid_box"> <div class="grid_box">
<!-- back --> <!-- back -->
<Button bind:layout new_layout={back} my_class="back icon" my_title="go back {back}"> <Button new_layout={back} my_class="back icon" my_title="go back {back}">
back back
</Button> </Button>
@@ -24,7 +24,7 @@
</Button> </Button>
<!-- close --> <!-- close -->
<Button bind:layout new_layout="close" my_class="close icon"> <Button new_layout="close" my_class="close icon">
close close
</Button> </Button>

View File

@@ -0,0 +1,103 @@
import { msgs, user, layout, socket, room_name } from './Store_chat';
export async function get_room_messages()
{
console.log("in get_room_messages");
const response = await fetch('/api/v2/chat/messages');
const data = await response.json();
const messages = data.messages;
messages.forEach(function(item) {
if (item.name === user.username) {
item.name = "me";
}
});
msgs.set(messages);
}
export async function create_room(room_name, room_type)
{
console.log("in create_room");
let form_data = {
room_name: room_name,
room_type: room_type,
};
// send the new room
const response = await fetch('/api/v2/chat/create', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(form_data),
});
// get response status and message
let response_status = response.status;
let data = await response.json();
let response_message = "";
if (data.message)
response_message = data.message;
return {
status: response_status,
message: response_message
};
}
export async function join_room(room_name)
{
console.log("in join_room");
let name = {
room_name: room_name,
}
const response = await fetch('/api/v2/chat/join', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(name),
});
let data = await response.json();
console.log(data.message);
socket.emit('join', room_name);
}
export async function change_room(name)
{
console.log("in change_room");
let r_name = {
room_name: name,
}
const response = await fetch('/api/v2/chat/change', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(r_name),
});
let data = await response.json();
console.log(data.message);
await get_room_messages();
socket.emit('join', name);
room_name.set(name);
layout.set("room");
}
export async function get_all_rooms()
{
console.log("in get_all_rooms");
// ask api for the rooms
const response = await fetch('/api/v2/chat/myrooms');
const data = await response.json();
console.log("data.rooms:", data.rooms);
for (let room of data.rooms)
console.log(room.name);
let rooms = data.rooms;
return rooms;
}

View File

@@ -0,0 +1,12 @@
import { user, msgs } from './Store_chat';
export function socket_events(socket)
{
socket.on('message', function(from, message)
{
console.log("received msg:", message, from);
if (from === user.username)
from = "me";
msgs.update(msgs => [...msgs, { name: from, message: message }]);
});
}

View File

@@ -0,0 +1,39 @@
import io from 'socket.io-client';
import { set_socket, set_user } from './Store_chat';
import { socket_events } from './Socket_events';
const address = `http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}`;
export async function init_socket()
{
const response = await fetch(`${address}/api/v2/user`);
const response_data = await response.json();
set_user(response_data);
let socket = await io(address,
{
path: '/chat',
query:
{
username: response_data.username,
},
});
set_socket(socket);
socket.on('connect', function(){ console.log("socket.io connected"); });
socket.on('disconnect', function(){ console.log("socket.io disconnected"); });
socket.on('connect_error', function(){ console.log("socket.io connect_error"); });
socket.on('connect_timeout', function(){ console.log("socket.io connect_timeout"); });
socket.on('error', function(){ console.log("socket.io error"); });
socket.on('reconnect', function(){ console.log("socket.io reconnect"); });
socket.on('reconnect_attempt', function(){ console.log("socket.io reconnect_attempt"); });
socket.on('reconnecting', function(){ console.log("socket.io reconnecting"); });
socket.on('reconnect_error', function(){ console.log("socket.io reconnect_error"); });
socket.on('reconnect_failed', function(){ console.log("socket.io reconnect_failed"); });
socket.on('ping', function(){ console.log("socket.io ping"); });
socket.on('pong', function(){ console.log("socket.io pong"); });
socket_events(socket);
}

View File

@@ -0,0 +1,16 @@
import { writable } from 'svelte/store';
export let msgs = writable([]);
export let layout = writable("close");
export let room_name = writable("");
export let user;
export let socket;
export function set_user(new_user) { user = new_user; }
export function set_socket(new_socket) { socket = new_socket; }
export function add_msg(name: string, message: string)
{
msgs.update(msgs => [...msgs, { name: "me", message: message }]);
}

View File

@@ -0,0 +1,27 @@
<script>
export let layout = "";
export let layouts = [];
</script>
<div style="display: flex; flex-direction: column; font-size: 12px; position: fixed; top: 20px; left: 20px; background-color: white;">
<p>temp, for testing :</p>
<button on:click={function(){layout = "close" }}>close</button>
<button on:click={function(){layout = "home" }}>home</button>
<button on:click={function(){layout = "room" }}>room</button>
<button on:click={function(){layout = "new" }}>new</button>
<button on:click={function(){layout = "settings" }}>settings</button>
<button on:click={function(){layout = "room_set" }}>room_set</button>
<button on:click={function(){layout = "protected"}}>protected</button>
<button on:click={function(){layout = "create" }}>create</button>
<button on:click={function(){layout = "mute" }}>mute</button>
<button on:click={function(){
layouts = ["settings", "settings"];
layout = "user";
}}>user from settings</button>
<button on:click={function(){
layouts = ["room_set", "room_set"];
layout = "user";
}}>user from room_set</button>
</div>