Merge pull request #11 from jhalitaksoy/feature/user-status

Feature/user status
This commit is contained in:
Halit Aksoy 2022-07-23 00:39:07 +03:00 committed by GitHub
commit e53c7bcffc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 52 additions and 5 deletions

View File

@ -7,4 +7,5 @@ export const channel_on_game_end = "on_game_end"
export const channel_on_game_crashed = "on_game_crashed" export const channel_on_game_crashed = "on_game_crashed"
export const channel_on_game_user_leave = "on_game_user_leave" export const channel_on_game_user_leave = "on_game_user_leave"
export const channel_ping = "ping" export const channel_ping = "ping"
export const channel_pong = "pong" export const channel_pong = "pong"
export const channel_on_user_connection_change = "channel_on_user_connection_change"

View File

@ -9,17 +9,19 @@ import {
channel_on_game_crashed, channel_on_game_crashed,
channel_on_game_start, channel_on_game_start,
channel_on_game_update, channel_on_game_update,
channel_on_game_user_leave channel_on_game_user_leave,
channel_on_user_connection_change
} from "../consts/channel_names"; } from "../consts/channel_names";
import { GameMove } from "../models/GameMove"; import { GameMove } from "../models/GameMove";
import { GameCrashManager } from "./GameCrashManager"; import { GameCrashManager } from "./GameCrashManager";
import { MatchMaker } from "../matchmaker/MatchMaker"; import { MatchMaker } from "../matchmaker/MatchMaker";
import { UserConnectionInfo } from "../models/UserConnectionInfo";
export class GameManager { export class GameManager {
gameStore: GameStore; gameStore: GameStore;
rtmt: RTMT; rtmt: RTMT;
matchMaker: MatchMaker; matchMaker: MatchMaker;
constructor(params: { gameStore: GameStore, rtmt: RTMT, matchMaker: MatchMaker }) { constructor(params: { gameStore: GameStore, rtmt: RTMT, matchMaker: MatchMaker }) {
this.gameStore = params.gameStore; this.gameStore = params.gameStore;
this.rtmt = params.rtmt; this.rtmt = params.rtmt;
@ -30,6 +32,7 @@ export class GameManager {
private initialize() { private initialize() {
this.listenOnPlayersPaired(); this.listenOnPlayersPaired();
this.listenRtmtMessages(); this.listenRtmtMessages();
this.listenUserConnectionChange();
} }
private listenRtmtMessages() { private listenRtmtMessages() {
@ -43,7 +46,7 @@ export class GameManager {
this.rtmt.listenMessage(channel_leave_game, (userKey: string, message: Object) => { this.rtmt.listenMessage(channel_leave_game, (userKey: string, message: Object) => {
this.onPlayerLeave(userKey) this.onPlayerLeave(userKey)
}); });
} }
private onGameMove(userKey: string, gameMove: GameMove) { private onGameMove(userKey: string, gameMove: GameMove) {
@ -108,6 +111,16 @@ export class GameManager {
public startGame(game: MancalaGame) { public startGame(game: MancalaGame) {
this.rtmt.sendMessage(game.player1Id, channel_on_game_start, game); this.rtmt.sendMessage(game.player1Id, channel_on_game_start, game);
this.rtmt.sendMessage(game.player2Id, channel_on_game_start, game); this.rtmt.sendMessage(game.player2Id, channel_on_game_start, game);
this.sendUserConnectionInfo(game, game.player1Id);
this.sendUserConnectionInfo(game, game.player2Id);
}
public sendUserConnectionInfo(game: MancalaGame, playerId: string, isOnline?: boolean) {
//todo: reimplement when watch game feature added
const _isOnline = isOnline === undefined ? this.rtmt.isClientOnline(playerId) : isOnline;
const otherUser = game.player1Id === playerId ? game.player2Id : game.player1Id;
const userConnectionInfo: UserConnectionInfo = { userId: playerId, isOnline: _isOnline };
this.rtmt.sendMessage(otherUser, channel_on_user_connection_change, userConnectionInfo);
} }
public deleteGame(game: MancalaGame) { public deleteGame(game: MancalaGame) {
@ -116,4 +129,17 @@ export class GameManager {
this.gameStore.remove(game.player2Id); this.gameStore.remove(game.player2Id);
} }
} }
private listenUserConnectionChange() {
this.rtmt.listenOnClientConnectionChange((clientId: string, isOnline: boolean) => {
const game = this.gameStore.get(clientId);
if (game) {
this.sendUserConnectionInfo(game, clientId, isOnline);
if (isOnline) {
const otherUser = game.player1Id === clientId ? game.player2Id : game.player1Id;
this.sendUserConnectionInfo(game, otherUser);
}
}
});
}
} }

6
src/models/User.ts Normal file
View File

@ -0,0 +1,6 @@
export interface User {
id: string;
name: string;
isOnline: boolean;
isAnonymous: boolean;
}

View File

@ -0,0 +1,4 @@
export interface UserConnectionInfo {
userId: string;
isOnline: boolean;
}

View File

@ -1,8 +1,10 @@
export type Bytes = Buffer export type Bytes = Buffer
export type OnMessage = (clientID: string, message: Object) => any export type OnMessage = (clientID: string, message: Object) => any
export type OnClientConnectionChange = (clientID: string, isOnline : boolean) => any
export interface RTMT { export interface RTMT {
sendMessage: (clientID: string, channel: string, message: Object) => any; sendMessage: (clientID: string, channel: string, message: Object) => any;
listenMessage: (channel: string, callback: OnMessage) => any; listenMessage: (channel: string, callback: OnMessage) => any;
isClientOnline(clientID: string): boolean; isClientOnline(clientID: string): boolean;
listenOnClientConnectionChange(onUserConnectionChange: OnClientConnectionChange): void;
} }

View File

@ -1,5 +1,5 @@
import { decode, encode } from "./encode_decode_message" import { decode, encode } from "./encode_decode_message"
import { Bytes, OnMessage, RTMT } from "./rtmt" import { Bytes, OnClientConnectionChange, OnMessage, RTMT } from "./rtmt"
import WebSocket from "ws" import WebSocket from "ws"
import * as http from 'http'; import * as http from 'http';
@ -17,6 +17,8 @@ export class RTMTWS implements RTMT {
private pingInterval?: NodeJS.Timeout = undefined; private pingInterval?: NodeJS.Timeout = undefined;
onUserConnectionChange?: OnClientConnectionChange;
constructor() { constructor() {
this.messageChannels = new Map<String, OnMessage>() this.messageChannels = new Map<String, OnMessage>()
this.wsServer = null this.wsServer = null
@ -32,6 +34,7 @@ export class RTMTWS implements RTMT {
const regexResult = req.url.split(RegExp("\/\?userKey=")); const regexResult = req.url.split(RegExp("\/\?userKey="));
const clientID = regexResult[1] const clientID = regexResult[1]
this.clients.set(clientID, ws) this.clients.set(clientID, ws)
this.onUserConnectionChange?.(clientID, true);
ws.on("message", (messageBytes: string) => { ws.on("message", (messageBytes: string) => {
this.onWebSocketMessage(clientID, messageBytes) this.onWebSocketMessage(clientID, messageBytes)
}) })
@ -39,6 +42,7 @@ export class RTMTWS implements RTMT {
ws.on("close", (code: number, reason: string) => { ws.on("close", (code: number, reason: string) => {
console.log("WS Closed! code : " + code + " reason : " + reason); console.log("WS Closed! code : " + code + " reason : " + reason);
this.clients.delete(clientID) this.clients.delete(clientID)
this.onUserConnectionChange?.(clientID, false);
}) })
ws.on("error", (err: Error) => { ws.on("error", (err: Error) => {
@ -130,4 +134,8 @@ export class RTMTWS implements RTMT {
isClientOnline(clientID: string): boolean { isClientOnline(clientID: string): boolean {
return this.clients.has(clientID); return this.clients.has(clientID);
} }
listenOnClientConnectionChange(onUserConnectionChange: OnClientConnectionChange) {
this.onUserConnectionChange = onUserConnectionChange;
}
} }