Refactor websocket into separate services, clean up socket status communication (#4433)
* Refactor websocket into separate services, clean up socket status communication * cleanup * add EOF lines * fix keepalive logged in check * undo change * fix keepalive connection check * cleanup * add typings * secure connection Co-authored-by: Jeremy Letto <jeremy.letto@datasite.com>
This commit is contained in:
parent
19333c53f6
commit
e9ba195d7d
52 changed files with 815 additions and 757 deletions
|
@ -1,20 +1,22 @@
|
|||
import { StatusEnum } from "types";
|
||||
import { webClient } from "websocket";
|
||||
import { ServerConnectParams } from "store";
|
||||
import { StatusEnum, User} from "types";
|
||||
import { webClient, SessionCommands } from "websocket";
|
||||
|
||||
export default class AuthenticationService {
|
||||
static connect(options) {
|
||||
webClient.persistence.session.connectServer(options);
|
||||
}
|
||||
static disconnect() {
|
||||
webClient.persistence.session.disconnectServer();
|
||||
static connect(options: ServerConnectParams): void {
|
||||
SessionCommands.connect(options);
|
||||
}
|
||||
|
||||
static isConnected(state) {
|
||||
static disconnect(): void {
|
||||
SessionCommands.disconnect();
|
||||
}
|
||||
|
||||
static isConnected(state: number): boolean {
|
||||
return state === StatusEnum.LOGGEDIN;
|
||||
}
|
||||
|
||||
static isModerator(user) {
|
||||
const moderatorLevel = webClient.pb.ServerInfo_User.UserLevelFlag.IsModerator;
|
||||
static isModerator(user: User): boolean {
|
||||
const moderatorLevel = webClient.protobuf.controller.ServerInfo_User.UserLevelFlag.IsModerator;
|
||||
// @TODO tell cockatrice not to do this so shittily
|
||||
return (user.userLevel & moderatorLevel) === moderatorLevel;
|
||||
}
|
||||
|
@ -22,4 +24,4 @@ export default class AuthenticationService {
|
|||
static isAdmin() {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { webClient } from "websocket";
|
||||
import { SessionCommands } from "websocket";
|
||||
|
||||
export default class ModeratorService {
|
||||
static viewLogHistory(filters) {
|
||||
webClient.commands.session.viewLogHistory(filters);
|
||||
static viewLogHistory(filters): void {
|
||||
SessionCommands.viewLogHistory(filters);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
import { webClient } from "websocket";
|
||||
import { RoomCommands, SessionCommands } from "websocket";
|
||||
|
||||
export default class RoomsService {
|
||||
static joinRoom(roomId) {
|
||||
webClient.commands.session.joinRoom(roomId);
|
||||
static joinRoom(roomId: number): void {
|
||||
SessionCommands.joinRoom(roomId);
|
||||
}
|
||||
|
||||
static leaveRoom(roomId) {
|
||||
webClient.commands.room.leaveRoom(roomId);
|
||||
static leaveRoom(roomId: number): void {
|
||||
RoomCommands.leaveRoom(roomId);
|
||||
}
|
||||
|
||||
static roomSay(roomId, message) {
|
||||
webClient.commands.room.roomSay(roomId, message);
|
||||
static roomSay(roomId: number, message: string): void {
|
||||
RoomCommands.roomSay(roomId, message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,4 +4,4 @@ export class RouterService {
|
|||
resolveUrl(path, params) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,19 +1,19 @@
|
|||
import { webClient } from "websocket";
|
||||
import { SessionCommands } from "websocket";
|
||||
|
||||
export default class SessionService {
|
||||
static addToBuddyList(userName) {
|
||||
webClient.commands.session.addToBuddyList(userName);
|
||||
static addToBuddyList(userName: string) {
|
||||
SessionCommands.addToBuddyList(userName);
|
||||
}
|
||||
|
||||
static removeFromBuddyList(userName) {
|
||||
webClient.commands.session.removeFromBuddyList(userName);
|
||||
static removeFromBuddyList(userName: string) {
|
||||
SessionCommands.removeFromBuddyList(userName);
|
||||
}
|
||||
|
||||
static addToIgnoreList(userName) {
|
||||
webClient.commands.session.addToIgnoreList(userName);
|
||||
static addToIgnoreList(userName: string) {
|
||||
SessionCommands.addToIgnoreList(userName);
|
||||
}
|
||||
|
||||
static removeFromIgnoreList(userName) {
|
||||
webClient.commands.session.removeFromIgnoreList(userName);
|
||||
static removeFromIgnoreList(userName: string) {
|
||||
SessionCommands.removeFromIgnoreList(userName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
export { default as AuthenticationService } from "./AuthenticationService";
|
||||
export { default as ModeratorService } from "./ModeratorService";
|
||||
export { default as RoomsService } from "./RoomsService";
|
||||
export { default as SessionService } from "./SessionService";
|
||||
export { default as SessionService } from "./SessionService";
|
||||
|
|
|
@ -4,9 +4,6 @@ export const Actions = {
|
|||
clearStore: () => ({
|
||||
type: Types.CLEAR_STORE
|
||||
}),
|
||||
connectServer: () => ({
|
||||
type: Types.CONNECT_SERVER
|
||||
}),
|
||||
connectionClosed: reason => ({
|
||||
type: Types.CONNECTION_CLOSED,
|
||||
reason
|
||||
|
|
|
@ -6,9 +6,6 @@ export const Dispatch = {
|
|||
clearStore: () => {
|
||||
store.dispatch(Actions.clearStore());
|
||||
},
|
||||
connectServer: () => {
|
||||
store.dispatch(Actions.connectServer());
|
||||
},
|
||||
connectionClosed: reason => {
|
||||
store.dispatch(Actions.connectionClosed(reason));
|
||||
},
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
export const Types = {
|
||||
CLEAR_STORE: "[Server] Clear Store",
|
||||
CONNECT_SERVER: "[Server] Connect Server",
|
||||
CONNECTION_CLOSED: "[Server] Connection Closed",
|
||||
SERVER_MESSAGE: "[Server] Server Message",
|
||||
UPDATE_BUDDY_LIST: "[Server] Update Buddy List",
|
||||
|
|
|
@ -1,3 +1,12 @@
|
|||
export interface Game {
|
||||
description: string;
|
||||
gameId: number;
|
||||
gameType: string;
|
||||
gameTypes: string[];
|
||||
roomId: number;
|
||||
started: boolean;
|
||||
}
|
||||
|
||||
export enum GameSortField {
|
||||
START_TIME = "startTime"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,3 +8,4 @@ export * from "./user";
|
|||
export * from "./routes";
|
||||
export * from "./sort";
|
||||
export * from "./forms";
|
||||
export * from "./message";
|
||||
|
|
7
webclient/src/types/message.tsx
Normal file
7
webclient/src/types/message.tsx
Normal file
|
@ -0,0 +1,7 @@
|
|||
export interface Message {
|
||||
name: string;
|
||||
message: string;
|
||||
messageType: number;
|
||||
timeOf: number;
|
||||
timeReceived: number;
|
||||
}
|
|
@ -6,7 +6,7 @@ export interface Room {
|
|||
gameCount: number;
|
||||
gameList: any[];
|
||||
gametypeList: any[];
|
||||
gametypeMap: { [index: number]: string; };
|
||||
gametypeMap: GametypeMap;
|
||||
name: string;
|
||||
permissionlevel: RoomAccessLevel;
|
||||
playerCount: number;
|
||||
|
@ -16,6 +16,8 @@ export interface Room {
|
|||
order: number;
|
||||
}
|
||||
|
||||
export interface GametypeMap { [index: number]: string }
|
||||
|
||||
export enum RoomAccessLevel {
|
||||
"none"
|
||||
}
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
export interface ServerStatus {
|
||||
status: StatusEnum;
|
||||
description: string;
|
||||
}
|
||||
|
||||
export enum StatusEnum {
|
||||
DISCONNECTED = 0,
|
||||
CONNECTING = 1,
|
||||
|
@ -43,4 +48,10 @@ export interface Log {
|
|||
targetName: string;
|
||||
targetType: string;
|
||||
time: string;
|
||||
}
|
||||
}
|
||||
|
||||
export interface LogGroups {
|
||||
room: Log[];
|
||||
game: Log[];
|
||||
chat: Log[];
|
||||
}
|
||||
|
|
|
@ -6,4 +6,4 @@ export enum SortDirection {
|
|||
export interface SortBy {
|
||||
field: string;
|
||||
order: SortDirection;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
export interface User {
|
||||
accountageSecs: number;
|
||||
avatarBmp: Uint8Array;
|
||||
country: string;
|
||||
gender: number;
|
||||
name: string;
|
||||
privlevel: UserAccessLevel;
|
||||
realName: string;
|
||||
userLevel: UserPrivLevel;
|
||||
gender?: number;
|
||||
realName?: string;
|
||||
country?: string;
|
||||
avatarBmp?: Uint8Array;
|
||||
}
|
||||
|
||||
export enum UserAccessLevel {
|
||||
|
@ -22,4 +22,4 @@ export enum UserPrivLevel {
|
|||
|
||||
export enum UserSortField {
|
||||
NAME = "name"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,42 +1,16 @@
|
|||
import protobuf from "protobufjs";
|
||||
import { ServerConnectParams } from "store";
|
||||
import { ServerStatus, StatusEnum } from "types";
|
||||
|
||||
import { StatusEnum } from "types";
|
||||
import { ProtobufService } from './services/ProtobufService';
|
||||
import { WebSocketService, WebSocketOptions } from "./services/WebSocketService";
|
||||
|
||||
import * as roomEvents from "./events/RoomEvents";
|
||||
import * as sessionEvents from "./events/SessionEvents";
|
||||
|
||||
import { RoomService, SessionService } from "./persistence";
|
||||
import { RoomCommand, SessionCommands } from "./commands";
|
||||
|
||||
import ProtoFiles from "./ProtoFiles";
|
||||
|
||||
const roomEventKeys = Object.keys(roomEvents);
|
||||
const sessionEventKeys = Object.keys(sessionEvents);
|
||||
|
||||
interface ApplicationCommands {
|
||||
room: RoomCommand;
|
||||
session: SessionCommands;
|
||||
}
|
||||
|
||||
interface ApplicationPersistence {
|
||||
room: RoomService;
|
||||
session: SessionService;
|
||||
}
|
||||
import { RoomPersistence, SessionPersistence } from './persistence';
|
||||
|
||||
export class WebClient {
|
||||
private socket: WebSocket;
|
||||
private status: StatusEnum = StatusEnum.DISCONNECTED;
|
||||
private keepalivecb;
|
||||
private lastPingPending = false;
|
||||
private cmdId = 0;
|
||||
private pendingCommands = {};
|
||||
|
||||
public commands: ApplicationCommands;
|
||||
public persistence: ApplicationPersistence;
|
||||
public socket = new WebSocketService(this);
|
||||
public protobuf = new ProtobufService(this);
|
||||
|
||||
public protocolVersion = 14;
|
||||
public pb;
|
||||
|
||||
public clientConfig = {
|
||||
"clientver" : "webclient-1.0 (2019-10-31)",
|
||||
"clientfeatures" : [
|
||||
|
@ -57,274 +31,50 @@ export class WebClient {
|
|||
]
|
||||
};
|
||||
|
||||
public options: any = {
|
||||
public options: WebSocketOptions = {
|
||||
host: "",
|
||||
port: "",
|
||||
user: "",
|
||||
pass: "",
|
||||
debug: false,
|
||||
autojoinrooms: true,
|
||||
keepalive: 5000
|
||||
};
|
||||
|
||||
constructor() {
|
||||
const files = ProtoFiles.map(file => `${WebClient.PB_FILE_DIR}/${file}`);
|
||||
|
||||
this.pb = new protobuf.Root();
|
||||
this.pb.load(files, { keepCase: false }, (err, root) => {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
this.socket.message$.subscribe((message: MessageEvent) => {
|
||||
this.protobuf.handleMessageEvent(message);
|
||||
});
|
||||
|
||||
// This sucks. I can"t seem to get out of this
|
||||
// circular dependency trap, so this is my current best.
|
||||
this.commands = {
|
||||
room: new RoomCommand(this),
|
||||
session: new SessionCommands(this),
|
||||
};
|
||||
|
||||
this.persistence = {
|
||||
room: new RoomService(this),
|
||||
session: new SessionService(this),
|
||||
};
|
||||
this.socket.statusChange$.subscribe((status: ServerStatus) => {
|
||||
this.handleStatusChange(status);
|
||||
});
|
||||
|
||||
console.log(this);
|
||||
}
|
||||
|
||||
private clearStores() {
|
||||
this.persistence.room.clearStore();
|
||||
this.persistence.session.clearStore();
|
||||
public connect(options: ServerConnectParams) {
|
||||
this.options = { ...this.options, ...options };
|
||||
this.socket.connect(this.options);
|
||||
}
|
||||
|
||||
public updateStatus(status, description) {
|
||||
console.log(`Status: [${status}]: ${description}`);
|
||||
this.status = status;
|
||||
this.persistence.session.updateStatus(status, description);
|
||||
public handleStatusChange({ status, description }: ServerStatus) {
|
||||
SessionPersistence.updateStatus(status, description);
|
||||
|
||||
if (status === StatusEnum.DISCONNECTED) {
|
||||
this.clearStores();
|
||||
this.endPingLoop();
|
||||
this.resetConnectionvars();
|
||||
this.clearStores();
|
||||
}
|
||||
}
|
||||
|
||||
public resetConnectionvars() {
|
||||
this.cmdId = 0;
|
||||
this.pendingCommands = {};
|
||||
this.lastPingPending = false;
|
||||
this.protobuf.resetCommands();
|
||||
this.socket.keepAliveService.resetPingFlag();
|
||||
}
|
||||
|
||||
public sendCommand(cmd, callback) {
|
||||
this.cmdId++;
|
||||
cmd["cmdId"] = this.cmdId;
|
||||
|
||||
this.pendingCommands[this.cmdId] = callback;
|
||||
|
||||
if (this.socket.readyState === WebSocket.OPEN) {
|
||||
this.socket.send(this.pb.CommandContainer.encode(cmd).finish());
|
||||
this.debug(() => console.log("Sent: " + cmd.toString()));
|
||||
} else {
|
||||
this.debug(() => console.log("Send: Not connected"));
|
||||
}
|
||||
private clearStores() {
|
||||
RoomPersistence.clearStore();
|
||||
SessionPersistence.clearStore();
|
||||
}
|
||||
|
||||
public sendRoomCommand(roomId, roomCmd, callback?) {
|
||||
const cmd = this.pb.CommandContainer.create({
|
||||
"roomId" : roomId,
|
||||
"roomCommand" : [ roomCmd ]
|
||||
});
|
||||
|
||||
this.sendCommand(cmd, raw => {
|
||||
this.debug(() => console.log(raw));
|
||||
|
||||
if (callback) {
|
||||
callback(raw);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public sendSessionCommand(sesCmd, callback?) {
|
||||
const cmd = this.pb.CommandContainer.create({
|
||||
"sessionCommand" : [ sesCmd ]
|
||||
});
|
||||
|
||||
this.sendCommand(cmd, (raw) => {
|
||||
this.debug(() => console.log(raw));
|
||||
|
||||
if (callback) {
|
||||
callback(raw);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public sendModeratorCommand(modCmd, callback?) {
|
||||
const cmd = this.pb.CommandContainer.create({
|
||||
"moderatorCommand" : [ modCmd ]
|
||||
});
|
||||
|
||||
this.sendCommand(cmd, (raw) => {
|
||||
this.debug(() => console.log(raw));
|
||||
|
||||
if (callback) {
|
||||
callback(raw);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public startPingLoop() {
|
||||
this.keepalivecb = setInterval(() => {
|
||||
// check if the previous ping got no reply
|
||||
if (this.lastPingPending) {
|
||||
this.disconnect();
|
||||
|
||||
this.updateStatus(StatusEnum.DISCONNECTED, "Connection timeout");
|
||||
}
|
||||
|
||||
// stop the ping loop if we"re disconnected
|
||||
if (this.status !== StatusEnum.LOGGEDIN) {
|
||||
this.endPingLoop();
|
||||
return;
|
||||
}
|
||||
|
||||
// send a ping
|
||||
this.lastPingPending = true;
|
||||
|
||||
const ping = this.pb.Command_Ping.create();
|
||||
const command = this.pb.SessionCommand.create({
|
||||
".Command_Ping.ext" : ping
|
||||
});
|
||||
|
||||
this.sendSessionCommand(command, () => {
|
||||
|
||||
this.lastPingPending = false;
|
||||
});
|
||||
}, this.options.keepalive);
|
||||
}
|
||||
|
||||
private endPingLoop() {
|
||||
clearInterval(this.keepalivecb);
|
||||
this.keepalivecb = null;
|
||||
}
|
||||
|
||||
public connect(options) {
|
||||
this.options = { ...this.options, ...options };
|
||||
|
||||
const { host, port } = this.options;
|
||||
|
||||
this.socket = new WebSocket("wss://" + host + ":" + port);
|
||||
this.socket.binaryType = "arraybuffer"; // We are talking binary
|
||||
|
||||
this.socket.onopen = () => {
|
||||
this.updateStatus(StatusEnum.CONNECTED, "Connected");
|
||||
};
|
||||
|
||||
this.socket.onclose = () => {
|
||||
// dont overwrite failure messages
|
||||
if (this.status !== StatusEnum.DISCONNECTED) {
|
||||
this.updateStatus(StatusEnum.DISCONNECTED, "Connection Closed");
|
||||
}
|
||||
};
|
||||
|
||||
this.socket.onerror = () => {
|
||||
this.updateStatus(StatusEnum.DISCONNECTED, "Connection Failed");
|
||||
};
|
||||
|
||||
|
||||
this.socket.onmessage = (event) => {
|
||||
const msg = this.decodeServerMessage(event);
|
||||
|
||||
if (msg) {
|
||||
switch (msg.messageType) {
|
||||
case this.pb.ServerMessage.MessageType.RESPONSE:
|
||||
this.processServerResponse(msg.response);
|
||||
break;
|
||||
case this.pb.ServerMessage.MessageType.ROOM_EVENT:
|
||||
this.processRoomEvent(msg.roomEvent, msg);
|
||||
break;
|
||||
case this.pb.ServerMessage.MessageType.SESSION_EVENT:
|
||||
this.processSessionEvent(msg.sessionEvent, msg);
|
||||
break;
|
||||
case this.pb.ServerMessage.MessageType.GAME_EVENT_CONTAINER:
|
||||
// @TODO
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public disconnect() {
|
||||
if (this.socket) {
|
||||
this.socket.close();
|
||||
}
|
||||
}
|
||||
|
||||
public debug(debug) {
|
||||
if (this.options.debug) {
|
||||
debug();
|
||||
}
|
||||
}
|
||||
|
||||
private decodeServerMessage(event) {
|
||||
const uint8msg = new Uint8Array(event.data);
|
||||
let msg;
|
||||
|
||||
try {
|
||||
msg = this.pb.ServerMessage.decode(uint8msg);
|
||||
|
||||
this.debug(() => console.log(msg));
|
||||
|
||||
return msg;
|
||||
} catch (err) {
|
||||
console.error("Processing failed:", err);
|
||||
|
||||
this.debug(() => {
|
||||
let str = "";
|
||||
|
||||
for (let i = 0; i < uint8msg.length; i++) {
|
||||
str += String.fromCharCode(uint8msg[i]);
|
||||
}
|
||||
|
||||
console.log(str);
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private processServerResponse(response) {
|
||||
const cmdId = response.cmdId;
|
||||
|
||||
if (!this.pendingCommands.hasOwnProperty(cmdId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.pendingCommands[cmdId](response);
|
||||
delete this.pendingCommands[cmdId];
|
||||
}
|
||||
|
||||
private processRoomEvent(response, raw) {
|
||||
this.processEvent(response, roomEvents, roomEventKeys, raw);
|
||||
}
|
||||
|
||||
private processSessionEvent(response, raw) {
|
||||
this.processEvent(response, sessionEvents, sessionEventKeys, raw);
|
||||
}
|
||||
|
||||
private processEvent(response, events, keys, raw) {
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
const key = keys[i];
|
||||
const event = events[key];
|
||||
const payload = response[event.id];
|
||||
|
||||
if (payload) {
|
||||
events[key].action(payload, this, raw);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static PB_FILE_DIR = `${process.env.PUBLIC_URL}/pb`;
|
||||
}
|
||||
|
||||
const webClient = new WebClient();
|
||||
|
|
|
@ -1,48 +1,42 @@
|
|||
import * as _ from 'lodash';
|
||||
|
||||
import { WebClient } from "../WebClient";
|
||||
import { RoomPersistence } from '../persistence';
|
||||
import webClient from "../WebClient";
|
||||
|
||||
export default class RoomCommands {
|
||||
private webClient: WebClient;
|
||||
|
||||
constructor(webClient) {
|
||||
this.webClient = webClient;
|
||||
}
|
||||
|
||||
roomSay(roomId, message) {
|
||||
export class RoomCommands {
|
||||
static roomSay(roomId: number, message: string) {
|
||||
const trimmed = _.trim(message);
|
||||
|
||||
if (!trimmed) return;
|
||||
|
||||
var CmdRoomSay = this.webClient.pb.Command_RoomSay.create({
|
||||
var CmdRoomSay = webClient.protobuf.controller.Command_RoomSay.create({
|
||||
"message" : trimmed
|
||||
});
|
||||
|
||||
var rc = this.webClient.pb.RoomCommand.create({
|
||||
var rc = webClient.protobuf.controller.RoomCommand.create({
|
||||
".Command_RoomSay.ext" : CmdRoomSay
|
||||
});
|
||||
|
||||
this.webClient.sendRoomCommand(roomId, rc);
|
||||
webClient.protobuf.sendRoomCommand(roomId, rc);
|
||||
}
|
||||
|
||||
leaveRoom(roomId) {
|
||||
var CmdLeaveRoom = this.webClient.pb.Command_LeaveRoom.create();
|
||||
static leaveRoom(roomId: number) {
|
||||
var CmdLeaveRoom = webClient.protobuf.controller.Command_LeaveRoom.create();
|
||||
|
||||
var rc = this.webClient.pb.RoomCommand.create({
|
||||
var rc = webClient.protobuf.controller.RoomCommand.create({
|
||||
".Command_LeaveRoom.ext" : CmdLeaveRoom
|
||||
});
|
||||
|
||||
this.webClient.sendRoomCommand(roomId, rc, (raw) => {
|
||||
webClient.protobuf.sendRoomCommand(roomId, rc, (raw) => {
|
||||
const { responseCode } = raw;
|
||||
|
||||
switch (responseCode) {
|
||||
case this.webClient.pb.Response.ResponseCode.RespOk:
|
||||
this.webClient.persistence.room.leaveRoom(roomId);
|
||||
case webClient.protobuf.controller.Response.ResponseCode.RespOk:
|
||||
RoomPersistence.leaveRoom(roomId);
|
||||
break;
|
||||
default:
|
||||
console.log(`Failed to leave Room ${roomId} [${responseCode}] : `, raw);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,103 +1,106 @@
|
|||
import { ServerConnectParams } from "store";
|
||||
import { StatusEnum } from "types";
|
||||
|
||||
import { WebClient } from "../WebClient";
|
||||
import { RoomPersistence, SessionPersistence } from '../persistence';
|
||||
import webClient from "../WebClient";
|
||||
import { guid } from "../utils";
|
||||
|
||||
export default class SessionCommands {
|
||||
private webClient: WebClient;
|
||||
|
||||
constructor(webClient) {
|
||||
this.webClient = webClient;
|
||||
export class SessionCommands {
|
||||
static connect(options: ServerConnectParams) {
|
||||
webClient.socket.updateStatus(StatusEnum.CONNECTING, "Connecting...");
|
||||
webClient.connect(options);
|
||||
}
|
||||
|
||||
login() {
|
||||
static disconnect() {
|
||||
webClient.socket.updateStatus(StatusEnum.DISCONNECTING, "Disconnecting...");
|
||||
webClient.socket.disconnect();
|
||||
}
|
||||
|
||||
static login() {
|
||||
const loginConfig = {
|
||||
...this.webClient.clientConfig,
|
||||
"userName" : this.webClient.options.user,
|
||||
"password" : this.webClient.options.pass,
|
||||
...webClient.clientConfig,
|
||||
"userName" : webClient.options.user,
|
||||
"password" : webClient.options.pass,
|
||||
"clientid" : guid()
|
||||
};
|
||||
|
||||
const CmdLogin = this.webClient.pb.Command_Login.create(loginConfig);
|
||||
const CmdLogin = webClient.protobuf.controller.Command_Login.create(loginConfig);
|
||||
|
||||
const command = this.webClient.pb.SessionCommand.create({
|
||||
const command = webClient.protobuf.controller.SessionCommand.create({
|
||||
".Command_Login.ext" : CmdLogin
|
||||
});
|
||||
|
||||
this.webClient.sendSessionCommand(command, raw => {
|
||||
webClient.protobuf.sendSessionCommand(command, raw => {
|
||||
const resp = raw[".Response_Login.ext"];
|
||||
|
||||
this.webClient.debug(() => console.log(".Response_Login.ext", resp));
|
||||
|
||||
switch(raw.responseCode) {
|
||||
case this.webClient.pb.Response.ResponseCode.RespOk:
|
||||
case webClient.protobuf.controller.Response.ResponseCode.RespOk:
|
||||
const { buddyList, ignoreList, userInfo } = resp;
|
||||
|
||||
this.webClient.persistence.session.updateBuddyList(buddyList);
|
||||
this.webClient.persistence.session.updateIgnoreList(ignoreList);
|
||||
this.webClient.persistence.session.updateUser(userInfo);
|
||||
SessionPersistence.updateBuddyList(buddyList);
|
||||
SessionPersistence.updateIgnoreList(ignoreList);
|
||||
SessionPersistence.updateUser(userInfo);
|
||||
|
||||
this.webClient.commands.session.listUsers();
|
||||
this.webClient.commands.session.listRooms();
|
||||
SessionCommands.listUsers();
|
||||
SessionCommands.listRooms();
|
||||
|
||||
this.webClient.updateStatus(StatusEnum.LOGGEDIN, "Logged in.");
|
||||
this.webClient.startPingLoop();
|
||||
webClient.socket.updateStatus(StatusEnum.LOGGEDIN, "Logged in.");
|
||||
break;
|
||||
|
||||
case this.webClient.pb.Response.ResponseCode.RespClientUpdateRequired:
|
||||
this.webClient.updateStatus(StatusEnum.DISCONNECTED, "Login failed: missing features");
|
||||
case webClient.protobuf.controller.Response.ResponseCode.RespClientUpdateRequired:
|
||||
webClient.socket.updateStatus(StatusEnum.DISCONNECTED, "Login failed: missing features");
|
||||
break;
|
||||
|
||||
case this.webClient.pb.Response.ResponseCode.RespWrongPassword:
|
||||
case this.webClient.pb.Response.ResponseCode.RespUsernameInvalid:
|
||||
this.webClient.updateStatus(StatusEnum.DISCONNECTED, "Login failed: incorrect username or password");
|
||||
case webClient.protobuf.controller.Response.ResponseCode.RespWrongPassword:
|
||||
case webClient.protobuf.controller.Response.ResponseCode.RespUsernameInvalid:
|
||||
webClient.socket.updateStatus(StatusEnum.DISCONNECTED, "Login failed: incorrect username or password");
|
||||
break;
|
||||
|
||||
case this.webClient.pb.Response.ResponseCode.RespWouldOverwriteOldSession:
|
||||
this.webClient.updateStatus(StatusEnum.DISCONNECTED, "Login failed: duplicated user session");
|
||||
case webClient.protobuf.controller.Response.ResponseCode.RespWouldOverwriteOldSession:
|
||||
webClient.socket.updateStatus(StatusEnum.DISCONNECTED, "Login failed: duplicated user session");
|
||||
break;
|
||||
|
||||
case this.webClient.pb.Response.ResponseCode.RespUserIsBanned:
|
||||
this.webClient.updateStatus(StatusEnum.DISCONNECTED, "Login failed: banned user");
|
||||
case webClient.protobuf.controller.Response.ResponseCode.RespUserIsBanned:
|
||||
webClient.socket.updateStatus(StatusEnum.DISCONNECTED, "Login failed: banned user");
|
||||
break;
|
||||
|
||||
case this.webClient.pb.Response.ResponseCode.RespRegistrationRequired:
|
||||
this.webClient.updateStatus(StatusEnum.DISCONNECTED, "Login failed: registration required");
|
||||
case webClient.protobuf.controller.Response.ResponseCode.RespRegistrationRequired:
|
||||
webClient.socket.updateStatus(StatusEnum.DISCONNECTED, "Login failed: registration required");
|
||||
break;
|
||||
|
||||
case this.webClient.pb.Response.ResponseCode.RespClientIdRequired:
|
||||
this.webClient.updateStatus(StatusEnum.DISCONNECTED, "Login failed: missing client ID");
|
||||
case webClient.protobuf.controller.Response.ResponseCode.RespClientIdRequired:
|
||||
webClient.socket.updateStatus(StatusEnum.DISCONNECTED, "Login failed: missing client ID");
|
||||
break;
|
||||
|
||||
case this.webClient.pb.Response.ResponseCode.RespContextError:
|
||||
this.webClient.updateStatus(StatusEnum.DISCONNECTED, "Login failed: server error");
|
||||
case webClient.protobuf.controller.Response.ResponseCode.RespContextError:
|
||||
webClient.socket.updateStatus(StatusEnum.DISCONNECTED, "Login failed: server error");
|
||||
break;
|
||||
|
||||
case this.webClient.pb.Response.ResponseCode.RespAccountNotActivated:
|
||||
this.webClient.updateStatus(StatusEnum.DISCONNECTED, "Login failed: account not activated");
|
||||
case webClient.protobuf.controller.Response.ResponseCode.RespAccountNotActivated:
|
||||
webClient.socket.updateStatus(StatusEnum.DISCONNECTED, "Login failed: account not activated");
|
||||
break;
|
||||
|
||||
default:
|
||||
this.webClient.updateStatus(StatusEnum.DISCONNECTED, "Login failed: unknown error " + raw.responseCode);
|
||||
webClient.socket.updateStatus(StatusEnum.DISCONNECTED, "Login failed: unknown error " + raw.responseCode);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
listUsers() {
|
||||
const CmdListUsers = this.webClient.pb.Command_ListUsers.create();
|
||||
static listUsers() {
|
||||
const CmdListUsers = webClient.protobuf.controller.Command_ListUsers.create();
|
||||
|
||||
const sc = this.webClient.pb.SessionCommand.create({
|
||||
const sc = webClient.protobuf.controller.SessionCommand.create({
|
||||
".Command_ListUsers.ext" : CmdListUsers
|
||||
});
|
||||
|
||||
this.webClient.sendSessionCommand(sc, raw => {
|
||||
webClient.protobuf.sendSessionCommand(sc, raw => {
|
||||
const { responseCode } = raw;
|
||||
const response = raw[".Response_ListUsers.ext"];
|
||||
|
||||
if (response) {
|
||||
switch (responseCode) {
|
||||
case this.webClient.pb.Response.ResponseCode.RespOk:
|
||||
this.webClient.persistence.session.updateUsers(response.userList);
|
||||
case webClient.protobuf.controller.Response.ResponseCode.RespOk:
|
||||
SessionPersistence.updateUsers(response.userList);
|
||||
break;
|
||||
default:
|
||||
console.log(`Failed to fetch Server Rooms [${responseCode}] : `, raw);
|
||||
|
@ -107,44 +110,43 @@ export default class SessionCommands {
|
|||
});
|
||||
}
|
||||
|
||||
listRooms() {
|
||||
const CmdListRooms = this.webClient.pb.Command_ListRooms.create();
|
||||
static listRooms() {
|
||||
const CmdListRooms = webClient.protobuf.controller.Command_ListRooms.create();
|
||||
|
||||
const sc = this.webClient.pb.SessionCommand.create({
|
||||
const sc = webClient.protobuf.controller.SessionCommand.create({
|
||||
".Command_ListRooms.ext" : CmdListRooms
|
||||
});
|
||||
|
||||
this.webClient.sendSessionCommand(sc);
|
||||
webClient.protobuf.sendSessionCommand(sc);
|
||||
}
|
||||
|
||||
joinRoom(roomId: string) {
|
||||
const CmdJoinRoom = this.webClient.pb.Command_JoinRoom.create({
|
||||
static joinRoom(roomId: number) {
|
||||
const CmdJoinRoom = webClient.protobuf.controller.Command_JoinRoom.create({
|
||||
"roomId" : roomId
|
||||
});
|
||||
|
||||
const sc = this.webClient.pb.SessionCommand.create({
|
||||
const sc = webClient.protobuf.controller.SessionCommand.create({
|
||||
".Command_JoinRoom.ext" : CmdJoinRoom
|
||||
});
|
||||
|
||||
this.webClient.sendSessionCommand(sc, (raw) => {
|
||||
webClient.protobuf.sendSessionCommand(sc, (raw) => {
|
||||
const { responseCode } = raw;
|
||||
|
||||
let error;
|
||||
|
||||
switch(responseCode) {
|
||||
case this.webClient.pb.Response.ResponseCode.RespOk:
|
||||
case webClient.protobuf.controller.Response.ResponseCode.RespOk:
|
||||
const { roomInfo } = raw[".Response_JoinRoom.ext"];
|
||||
|
||||
this.webClient.persistence.room.joinRoom(roomInfo);
|
||||
this.webClient.debug(() => console.log("Join Room: ", roomInfo.name));
|
||||
RoomPersistence.joinRoom(roomInfo);
|
||||
return;
|
||||
case this.webClient.pb.Response.ResponseCode.RespNameNotFound:
|
||||
case webClient.protobuf.controller.Response.ResponseCode.RespNameNotFound:
|
||||
error = "Failed to join the room: it doesn\"t exist on the server.";
|
||||
break;
|
||||
case this.webClient.pb.Response.ResponseCode.RespContextError:
|
||||
case webClient.protobuf.controller.Response.ResponseCode.RespContextError:
|
||||
error = "The server thinks you are in the room but Cockatrice is unable to display it. Try restarting Cockatrice.";
|
||||
break;
|
||||
case this.webClient.pb.Response.ResponseCode.RespUserLevelTooLow:
|
||||
case webClient.protobuf.controller.Response.ResponseCode.RespUserLevelTooLow:
|
||||
error = "You do not have the required permission to join this room.";
|
||||
break;
|
||||
default:
|
||||
|
@ -158,68 +160,64 @@ export default class SessionCommands {
|
|||
});
|
||||
}
|
||||
|
||||
addToBuddyList(userName) {
|
||||
static addToBuddyList(userName: string) {
|
||||
this.addToList('buddy', userName);
|
||||
}
|
||||
|
||||
removeFromBuddyList(userName) {
|
||||
static removeFromBuddyList(userName: string) {
|
||||
this.removeFromList('buddy', userName);
|
||||
}
|
||||
|
||||
addToIgnoreList(userName) {
|
||||
static addToIgnoreList(userName: string) {
|
||||
this.addToList('ignore', userName);
|
||||
}
|
||||
|
||||
removeFromIgnoreList(userName) {
|
||||
static removeFromIgnoreList(userName: string) {
|
||||
this.removeFromList('ignore', userName);
|
||||
}
|
||||
|
||||
addToList(list: string, userName: string) {
|
||||
const CmdAddToList = this.webClient.pb.Command_AddToList.create({ list, userName });
|
||||
static addToList(list: string, userName: string) {
|
||||
const CmdAddToList = webClient.protobuf.controller.Command_AddToList.create({ list, userName });
|
||||
|
||||
const sc = this.webClient.pb.SessionCommand.create({
|
||||
const sc = webClient.protobuf.controller.SessionCommand.create({
|
||||
".Command_AddToList.ext" : CmdAddToList
|
||||
});
|
||||
|
||||
this.webClient.sendSessionCommand(sc, ({ responseCode }) => {
|
||||
webClient.protobuf.sendSessionCommand(sc, ({ responseCode }) => {
|
||||
// @TODO: filter responseCode, pop snackbar for error
|
||||
this.webClient.debug(() => console.log('Added to List Response: ', responseCode));
|
||||
});
|
||||
}
|
||||
|
||||
removeFromList(list: string, userName: string) {
|
||||
const CmdRemoveFromList = this.webClient.pb.Command_RemoveFromList.create({ list, userName });
|
||||
static removeFromList(list: string, userName: string) {
|
||||
const CmdRemoveFromList = webClient.protobuf.controller.Command_RemoveFromList.create({ list, userName });
|
||||
|
||||
const sc = this.webClient.pb.SessionCommand.create({
|
||||
const sc = webClient.protobuf.controller.SessionCommand.create({
|
||||
".Command_RemoveFromList.ext" : CmdRemoveFromList
|
||||
});
|
||||
|
||||
this.webClient.sendSessionCommand(sc, ({ responseCode }) => {
|
||||
webClient.protobuf.sendSessionCommand(sc, ({ responseCode }) => {
|
||||
// @TODO: filter responseCode, pop snackbar for error
|
||||
this.webClient.debug(() => console.log('Removed from List Response: ', responseCode));
|
||||
});
|
||||
}
|
||||
|
||||
viewLogHistory(filters) {
|
||||
const CmdViewLogHistory = this.webClient.pb.Command_ViewLogHistory.create(filters);
|
||||
static viewLogHistory(filters) {
|
||||
const CmdViewLogHistory = webClient.protobuf.controller.Command_ViewLogHistory.create(filters);
|
||||
|
||||
const sc = this.webClient.pb.ModeratorCommand.create({
|
||||
const sc = webClient.protobuf.controller.ModeratorCommand.create({
|
||||
".Command_ViewLogHistory.ext" : CmdViewLogHistory
|
||||
});
|
||||
|
||||
this.webClient.sendModeratorCommand(sc, (raw) => {
|
||||
webClient.protobuf.sendModeratorCommand(sc, (raw) => {
|
||||
const { responseCode } = raw;
|
||||
|
||||
let error;
|
||||
|
||||
switch(responseCode) {
|
||||
case this.webClient.pb.Response.ResponseCode.RespOk:
|
||||
case webClient.protobuf.controller.Response.ResponseCode.RespOk:
|
||||
const { logMessage } = raw[".Response_ViewLogHistory.ext"];
|
||||
|
||||
console.log("Response_ViewLogHistory: ", logMessage)
|
||||
this.webClient.persistence.session.viewLogs(logMessage)
|
||||
|
||||
this.webClient.debug(() => console.log("View Log History: ", logMessage));
|
||||
SessionPersistence.viewLogs(logMessage)
|
||||
return;
|
||||
default:
|
||||
error = "Failed to retrieve log history.";
|
||||
|
@ -231,4 +229,4 @@ export default class SessionCommands {
|
|||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
export { default as RoomCommand } from "./RoomCommands";
|
||||
export { default as SessionCommands } from "./SessionCommands";
|
||||
export { RoomCommands } from "./RoomCommands";
|
||||
export { SessionCommands } from "./SessionCommands";
|
||||
|
|
49
webclient/src/websocket/events/RoomEvents.tsx
Normal file
49
webclient/src/websocket/events/RoomEvents.tsx
Normal file
|
@ -0,0 +1,49 @@
|
|||
import { Game, Message, User } from 'types';
|
||||
import { RoomPersistence } from '../persistence/RoomPersistence';
|
||||
import { ProtobufEvents } from '../services/ProtobufService';
|
||||
|
||||
export const RoomEvents: ProtobufEvents = {
|
||||
".Event_JoinRoom.ext": joinRoom,
|
||||
".Event_LeaveRoom.ext": leaveRoom,
|
||||
".Event_ListGames.ext": listGames,
|
||||
".Event_RoomSay.ext": roomSay,
|
||||
};
|
||||
|
||||
function joinRoom({ userInfo }: JoinRoomData, { roomEvent }: RoomEvent) {
|
||||
const { roomId } = roomEvent;
|
||||
|
||||
RoomPersistence.userJoined(roomId, userInfo);
|
||||
}
|
||||
|
||||
function leaveRoom({ name }: LeaveRoomData, { roomEvent }: RoomEvent) {
|
||||
const { roomId } = roomEvent;
|
||||
RoomPersistence.userLeft(roomId, name);
|
||||
}
|
||||
|
||||
function listGames({ gameList }: ListGamesData, { roomEvent }: RoomEvent) {
|
||||
const { roomId } = roomEvent;
|
||||
RoomPersistence.updateGames(roomId, gameList);
|
||||
}
|
||||
|
||||
function roomSay(message: Message, { roomEvent }: RoomEvent) {
|
||||
const { roomId } = roomEvent;
|
||||
RoomPersistence.addMessage(roomId, message);
|
||||
}
|
||||
|
||||
interface RoomEvent {
|
||||
roomEvent: {
|
||||
roomId: number;
|
||||
}
|
||||
}
|
||||
|
||||
interface JoinRoomData {
|
||||
userInfo: User;
|
||||
}
|
||||
|
||||
interface LeaveRoomData {
|
||||
name: string;
|
||||
}
|
||||
|
||||
interface ListGamesData {
|
||||
gameList: Game[];
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
export const JoinRoom = {
|
||||
id: ".Event_JoinRoom.ext",
|
||||
action: ({ userInfo }, webClient, { roomEvent }) => {
|
||||
const { roomId } = roomEvent;
|
||||
webClient.persistence.room.userJoined(roomId, userInfo);
|
||||
}
|
||||
};
|
|
@ -1,7 +0,0 @@
|
|||
export const LeaveRoom = {
|
||||
id: ".Event_LeaveRoom.ext",
|
||||
action: ({ name }, webClient, { roomEvent }) => {
|
||||
const { roomId } = roomEvent;
|
||||
webClient.persistence.room.userLeft(roomId, name);
|
||||
}
|
||||
};
|
|
@ -1,7 +0,0 @@
|
|||
export const ListGames = {
|
||||
id: ".Event_ListGames.ext",
|
||||
action: ({ gameList }, webClient, { roomEvent }) => {
|
||||
const { roomId } = roomEvent;
|
||||
webClient.persistence.room.updateGames(roomId, gameList);
|
||||
}
|
||||
};
|
|
@ -1,7 +0,0 @@
|
|||
export const RoomSay = {
|
||||
id: ".Event_RoomSay.ext",
|
||||
action: (message, webClient, { roomEvent }) => {
|
||||
const { roomId } = roomEvent;
|
||||
webClient.persistence.room.addMessage(roomId, message);
|
||||
}
|
||||
};
|
|
@ -1,4 +0,0 @@
|
|||
export { JoinRoom } from "./JoinRoom";
|
||||
export { LeaveRoom } from "./LeaveRoom";
|
||||
export { ListGames } from "./ListGames";
|
||||
export { RoomSay } from "./RoomSay";
|
189
webclient/src/websocket/events/SessionEvents.tsx
Normal file
189
webclient/src/websocket/events/SessionEvents.tsx
Normal file
|
@ -0,0 +1,189 @@
|
|||
import { Room, StatusEnum, User } from "types";
|
||||
|
||||
import { SessionCommands } from "../commands";
|
||||
import { RoomPersistence, SessionPersistence } from '../persistence';
|
||||
import { ProtobufEvents } from '../services/ProtobufService';
|
||||
import webClient from '../WebClient';
|
||||
|
||||
export const SessionEvents: ProtobufEvents = {
|
||||
".Event_AddToList.ext": addToList,
|
||||
".Event_ConnectionClosed.ext": connectionClosed,
|
||||
".Event_ListRooms.ext": listRooms,
|
||||
".Event_NotifyUser.ext": notifyUser,
|
||||
".Event_PlayerPropertiesChanges.ext": playerPropertiesChanges,
|
||||
".Event_RemoveFromList.ext": removeFromList,
|
||||
".Event_ServerIdentification.ext": serverIdentification,
|
||||
".Event_ServerMessage.ext": serverMessage,
|
||||
".Event_ServerShutdown.ext": serverShutdown,
|
||||
".Event_UserJoined.ext": userJoined,
|
||||
".Event_UserLeft.ext": userLeft,
|
||||
".Event_UserMessage.ext": userMessage,
|
||||
}
|
||||
|
||||
function addToList({ listName, userInfo}: AddToListData) {
|
||||
switch (listName) {
|
||||
case 'buddy': {
|
||||
SessionPersistence.addToBuddyList(userInfo);
|
||||
break;
|
||||
}
|
||||
case 'ignore': {
|
||||
SessionPersistence.addToIgnoreList(userInfo);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
console.log('Attempted to add to unknown list: ', listName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function connectionClosed({ reason, reasonStr }: ConnectionClosedData) {
|
||||
let message = "";
|
||||
|
||||
// @TODO (5)
|
||||
if (reasonStr) {
|
||||
message = reasonStr;
|
||||
} else {
|
||||
switch(reason) {
|
||||
case webClient.protobuf.controller.Event_ConnectionClosed.CloseReason.USER_LIMIT_REACHED:
|
||||
message = "The server has reached its maximum user capacity";
|
||||
break;
|
||||
case webClient.protobuf.controller.Event_ConnectionClosed.CloseReason.TOO_MANY_CONNECTIONS:
|
||||
message = "There are too many concurrent connections from your address";
|
||||
break;
|
||||
case webClient.protobuf.controller.Event_ConnectionClosed.CloseReason.BANNED:
|
||||
message = "You are banned";
|
||||
break;
|
||||
case webClient.protobuf.controller.Event_ConnectionClosed.CloseReason.DEMOTED:
|
||||
message = "You were demoted";
|
||||
break;
|
||||
case webClient.protobuf.controller.Event_ConnectionClosed.CloseReason.SERVER_SHUTDOWN:
|
||||
message = "Scheduled server shutdown";
|
||||
break;
|
||||
case webClient.protobuf.controller.Event_ConnectionClosed.CloseReason.USERNAMEINVALID:
|
||||
message = "Invalid username";
|
||||
break;
|
||||
case webClient.protobuf.controller.Event_ConnectionClosed.CloseReason.LOGGEDINELSEWERE:
|
||||
message = "You have been logged out due to logging in at another location";
|
||||
break;
|
||||
case webClient.protobuf.controller.Event_ConnectionClosed.CloseReason.OTHER:
|
||||
default:
|
||||
message = "Unknown reason";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
webClient.socket.updateStatus(StatusEnum.DISCONNECTED, message);
|
||||
}
|
||||
|
||||
function listRooms({ roomList }: ListRoomsData) {
|
||||
RoomPersistence.updateRooms(roomList);
|
||||
|
||||
if (webClient.options.autojoinrooms) {
|
||||
roomList.forEach(({ autoJoin, roomId }) => {
|
||||
if (autoJoin) {
|
||||
SessionCommands.joinRoom(roomId);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function notifyUser(payload) {
|
||||
// console.info("Event_NotifyUser", payload);
|
||||
}
|
||||
|
||||
function playerPropertiesChanges(payload) {
|
||||
// console.info("Event_PlayerPropertiesChanges", payload);
|
||||
}
|
||||
|
||||
function removeFromList({ listName, userName }: RemoveFromListData) {
|
||||
switch (listName) {
|
||||
case 'buddy': {
|
||||
SessionPersistence.removeFromBuddyList(userName);
|
||||
break;
|
||||
}
|
||||
case 'ignore': {
|
||||
SessionPersistence.removeFromIgnoreList(userName);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
console.log('Attempted to remove from unknown list: ', listName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function serverIdentification(info: ServerIdentificationData) {
|
||||
const { serverName, serverVersion, protocolVersion } = info;
|
||||
|
||||
if (protocolVersion !== webClient.protocolVersion) {
|
||||
SessionCommands.disconnect();
|
||||
webClient.socket.updateStatus(StatusEnum.DISCONNECTED, "Protocol version mismatch: " + protocolVersion);
|
||||
return;
|
||||
}
|
||||
|
||||
webClient.resetConnectionvars();
|
||||
webClient.socket.updateStatus(StatusEnum.LOGGINGIN, "Logging in...");
|
||||
SessionPersistence.updateInfo(serverName, serverVersion);
|
||||
SessionCommands.login();
|
||||
}
|
||||
|
||||
function serverMessage({ message }: ServerMessageData) {
|
||||
SessionPersistence.serverMessage(message);
|
||||
}
|
||||
|
||||
function serverShutdown(payload) {
|
||||
// console.info("Event_ServerShutdown", payload);
|
||||
}
|
||||
|
||||
function userJoined({ userInfo }: UserJoinedData) {
|
||||
SessionPersistence.userJoined(userInfo);
|
||||
}
|
||||
|
||||
function userLeft({ name }: UserLeftData) {
|
||||
SessionPersistence.userLeft(name);
|
||||
}
|
||||
|
||||
function userMessage(payload) {
|
||||
// console.info("Event_UserMessage", payload);
|
||||
}
|
||||
|
||||
interface SessionEvent {
|
||||
sessionEvent: {}
|
||||
}
|
||||
|
||||
interface AddToListData {
|
||||
listName: string;
|
||||
userInfo: User;
|
||||
}
|
||||
|
||||
interface ConnectionClosedData {
|
||||
endTime: number;
|
||||
reason: number;
|
||||
reasonStr: string;
|
||||
}
|
||||
|
||||
interface ListRoomsData {
|
||||
roomList: Room[];
|
||||
}
|
||||
|
||||
interface RemoveFromListData {
|
||||
listName: string;
|
||||
userName: string;
|
||||
}
|
||||
|
||||
interface ServerIdentificationData {
|
||||
protocolVersion: number;
|
||||
serverName: string;
|
||||
serverVersion: string;
|
||||
}
|
||||
|
||||
interface ServerMessageData {
|
||||
message: string;
|
||||
}
|
||||
|
||||
interface UserJoinedData {
|
||||
userInfo: User;
|
||||
}
|
||||
|
||||
interface UserLeftData {
|
||||
name: string;
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
export const AddToList = {
|
||||
id: ".Event_AddToList.ext",
|
||||
action: ({ listName, userInfo}, webClient) => {
|
||||
switch (listName) {
|
||||
case 'buddy': {
|
||||
webClient.persistence.session.addToBuddyList(userInfo);
|
||||
break;
|
||||
}
|
||||
case 'ignore': {
|
||||
webClient.persistence.session.addToIgnoreList(userInfo);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
webClient.debug(() => console.log('Attempted to add to unknown list: ', listName));
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
|
@ -1,39 +0,0 @@
|
|||
import { StatusEnum } from "types";
|
||||
|
||||
export const ConnectionClosed = {
|
||||
id: ".Event_ConnectionClosed.ext",
|
||||
action: ({ reason }, webClient) => {
|
||||
let message = "";
|
||||
|
||||
// @TODO (5)
|
||||
switch(reason) {
|
||||
case webClient.pb.Event_ConnectionClosed.CloseReason.USER_LIMIT_REACHED:
|
||||
message = "The server has reached its maximum user capacity";
|
||||
break;
|
||||
case webClient.pb.Event_ConnectionClosed.CloseReason.TOO_MANY_CONNECTIONS:
|
||||
message = "There are too many concurrent connections from your address";
|
||||
break;
|
||||
case webClient.pb.Event_ConnectionClosed.CloseReason.BANNED:
|
||||
message = "You are banned";
|
||||
break;
|
||||
case webClient.pb.Event_ConnectionClosed.CloseReason.DEMOTED:
|
||||
message = "You were demoted";
|
||||
break;
|
||||
case webClient.pb.Event_ConnectionClosed.CloseReason.SERVER_SHUTDOWN:
|
||||
message = "Scheduled server shutdown";
|
||||
break;
|
||||
case webClient.pb.Event_ConnectionClosed.CloseReason.USERNAMEINVALID:
|
||||
message = "Invalid username";
|
||||
break;
|
||||
case webClient.pb.Event_ConnectionClosed.CloseReason.LOGGEDINELSEWERE:
|
||||
message = "You have been logged out due to logging in at another location";
|
||||
break;
|
||||
case webClient.pb.Event_ConnectionClosed.CloseReason.OTHER:
|
||||
default:
|
||||
message = "Unknown reason";
|
||||
break;
|
||||
}
|
||||
|
||||
webClient.updateStatus(StatusEnum.DISCONNECTED, message);
|
||||
}
|
||||
};
|
|
@ -1,16 +0,0 @@
|
|||
import * as _ from "lodash";
|
||||
|
||||
export const ListRooms = {
|
||||
id: ".Event_ListRooms.ext",
|
||||
action: ({ roomList }, webClient) => {
|
||||
webClient.persistence.room.updateRooms(roomList);
|
||||
|
||||
if (webClient.options.autojoinrooms) {
|
||||
_.each(roomList, ({ autoJoin, roomId }) => {
|
||||
if (autoJoin) {
|
||||
webClient.commands.session.joinRoom(roomId);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
|
@ -1,6 +0,0 @@
|
|||
export const NotifyUser = {
|
||||
id: ".Event_NotifyUser.ext",
|
||||
action: (payload) => {
|
||||
// console.info("Event_NotifyUser", payload);
|
||||
}
|
||||
};
|
|
@ -1,6 +0,0 @@
|
|||
export const PlayerPropertiesChanges = {
|
||||
id: ".Event_PlayerPropertiesChanges.ext",
|
||||
action: (payload) => {
|
||||
// console.info("Event_PlayerPropertiesChanges", payload);
|
||||
}
|
||||
};
|
|
@ -1,18 +0,0 @@
|
|||
export const RemoveFromList = {
|
||||
id: ".Event_RemoveFromList.ext",
|
||||
action: ({ listName, userName }, webClient) => {
|
||||
switch (listName) {
|
||||
case 'buddy': {
|
||||
webClient.persistence.session.removeFromBuddyList(userName);
|
||||
break;
|
||||
}
|
||||
case 'ignore': {
|
||||
webClient.persistence.session.removeFromIgnoreList(userName);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
webClient.debug(() => console.log('Attempted to remove from unknown list: ', listName));
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
|
@ -1,19 +0,0 @@
|
|||
import { StatusEnum } from "types";
|
||||
|
||||
export const ServerIdentification = {
|
||||
id: ".Event_ServerIdentification.ext",
|
||||
action: (info, webClient, _raw) => {
|
||||
const { serverName, serverVersion, protocolVersion } = info;
|
||||
|
||||
if (protocolVersion !== webClient.protocolVersion) {
|
||||
webClient.disconnect();
|
||||
webClient.updateStatus(StatusEnum.DISCONNECTED, "Protocol version mismatch: " + protocolVersion);
|
||||
return;
|
||||
}
|
||||
|
||||
webClient.resetConnectionvars();
|
||||
webClient.updateStatus(StatusEnum.LOGGINGIN, "Logging in...");
|
||||
webClient.persistence.session.updateInfo(serverName, serverVersion);
|
||||
webClient.commands.session.login();
|
||||
}
|
||||
};
|
|
@ -1,6 +0,0 @@
|
|||
export const ServerMessage = {
|
||||
id: ".Event_ServerMessage.ext",
|
||||
action: ({ message }, webClient) => {
|
||||
webClient.persistence.session.serverMessage(message);
|
||||
}
|
||||
};
|
|
@ -1,6 +0,0 @@
|
|||
export const ServerShutdown = {
|
||||
id: ".Event_ServerShutdown.ext",
|
||||
action: (payload, webClient) => {
|
||||
// console.info("Event_ServerShutdown", payload);
|
||||
}
|
||||
};
|
|
@ -1,6 +0,0 @@
|
|||
export const UserJoined = {
|
||||
id: ".Event_UserJoined.ext",
|
||||
action: ({ userInfo }, webClient) => {
|
||||
webClient.persistence.session.userJoined(userInfo);
|
||||
}
|
||||
};
|
|
@ -1,6 +0,0 @@
|
|||
export const UserLeft = {
|
||||
id: ".Event_UserLeft.ext",
|
||||
action: ({ name }, webClient) => {
|
||||
webClient.persistence.session.userLeft(name);
|
||||
}
|
||||
};
|
|
@ -1,6 +0,0 @@
|
|||
export const UserMessage = {
|
||||
id: ".Event_UserMessage.ext",
|
||||
action: (payload) => {
|
||||
// console.info("Event_UserMessage", payload);
|
||||
}
|
||||
};
|
|
@ -1,12 +0,0 @@
|
|||
export * from "./ConnectionClosed";
|
||||
export * from "./ListRooms";
|
||||
export * from "./AddToList";
|
||||
export * from "./RemoveFromList";
|
||||
export * from "./NotifyUser"; // @TODO
|
||||
export * from "./PlayerPropertiesChanges"; // @TODO
|
||||
export * from "./ServerIdentification";
|
||||
export * from "./ServerMessage";
|
||||
export * from "./ServerShutdown"; // @TODO
|
||||
export * from "./UserJoined";
|
||||
export * from "./UserLeft";
|
||||
export * from "./UserMessage"; // @TODO
|
2
webclient/src/websocket/events/index.ts
Normal file
2
webclient/src/websocket/events/index.ts
Normal file
|
@ -0,0 +1,2 @@
|
|||
export * from './RoomEvents';
|
||||
export * from './SessionEvents';
|
|
@ -1,6 +1,2 @@
|
|||
export { default as webClient } from './WebClient';
|
||||
export { default as ProtoFiles } from './ProtoFiles';
|
||||
|
||||
|
||||
// Export common used services
|
||||
export { NormalizeService, RoomService} from "./persistence";
|
||||
export * from './commands';
|
||||
|
|
|
@ -1,33 +1,26 @@
|
|||
import { store, RoomsDispatch, RoomsSelectors } from "store";
|
||||
import { WebClient } from "../WebClient";
|
||||
import { Game, Message, Room, User } from 'types';
|
||||
import NormalizeService from "../utils/NormalizeService";
|
||||
|
||||
import { NormalizeService } from "websocket";
|
||||
|
||||
export default class RoomService {
|
||||
webClient: WebClient;
|
||||
|
||||
constructor(webClient) {
|
||||
this.webClient = webClient;
|
||||
}
|
||||
|
||||
clearStore() {
|
||||
export class RoomPersistence {
|
||||
static clearStore() {
|
||||
RoomsDispatch.clearStore();
|
||||
}
|
||||
|
||||
joinRoom(roomInfo) {
|
||||
static joinRoom(roomInfo: Room) {
|
||||
NormalizeService.normalizeRoomInfo(roomInfo);
|
||||
RoomsDispatch.joinRoom(roomInfo);
|
||||
}
|
||||
|
||||
leaveRoom(roomId) {
|
||||
static leaveRoom(roomId: number) {
|
||||
RoomsDispatch.leaveRoom(roomId);
|
||||
}
|
||||
|
||||
updateRooms(rooms) {
|
||||
static updateRooms(rooms: Room[]) {
|
||||
RoomsDispatch.updateRooms(rooms);
|
||||
}
|
||||
|
||||
updateGames(roomId, gameList) {
|
||||
static updateGames(roomId: number, gameList: Game[]) {
|
||||
const game = gameList[0];
|
||||
|
||||
if (!game.gameType) {
|
||||
|
@ -42,17 +35,17 @@ export default class RoomService {
|
|||
RoomsDispatch.updateGames(roomId, gameList);
|
||||
}
|
||||
|
||||
addMessage(roomId, message) {
|
||||
static addMessage(roomId: number, message: Message) {
|
||||
NormalizeService.normalizeUserMessage(message);
|
||||
|
||||
RoomsDispatch.addMessage(roomId, message);
|
||||
}
|
||||
|
||||
userJoined(roomId, user) {
|
||||
static userJoined(roomId: number, user: User) {
|
||||
RoomsDispatch.userJoined(roomId, user);
|
||||
}
|
||||
|
||||
userLeft(roomId, name) {
|
||||
static userLeft(roomId: number, name: string) {
|
||||
RoomsDispatch.userLeft(roomId, name);
|
||||
}
|
||||
}
|
75
webclient/src/websocket/persistence/SessionPersistence.tsx
Normal file
75
webclient/src/websocket/persistence/SessionPersistence.tsx
Normal file
|
@ -0,0 +1,75 @@
|
|||
import { ServerDispatch } from "store";
|
||||
import { Log, StatusEnum, User } from "types";
|
||||
|
||||
import { sanitizeHtml } from "websocket/utils";
|
||||
import NormalizeService from "../utils/NormalizeService";
|
||||
|
||||
export class SessionPersistence {
|
||||
static clearStore() {
|
||||
ServerDispatch.clearStore();
|
||||
}
|
||||
|
||||
static connectionClosed(reason: number) {
|
||||
ServerDispatch.connectionClosed(reason);
|
||||
}
|
||||
|
||||
static updateBuddyList(buddyList) {
|
||||
ServerDispatch.updateBuddyList(buddyList);
|
||||
}
|
||||
|
||||
static addToBuddyList(user: User) {
|
||||
ServerDispatch.addToBuddyList(user);
|
||||
}
|
||||
|
||||
static removeFromBuddyList(userName: string) {
|
||||
ServerDispatch.removeFromBuddyList(userName);
|
||||
}
|
||||
|
||||
static updateIgnoreList(ignoreList) {
|
||||
ServerDispatch.updateIgnoreList(ignoreList);
|
||||
}
|
||||
|
||||
static addToIgnoreList(user: User) {
|
||||
ServerDispatch.addToIgnoreList(user);
|
||||
}
|
||||
|
||||
static removeFromIgnoreList(userName: string) {
|
||||
ServerDispatch.removeFromIgnoreList(userName);
|
||||
}
|
||||
|
||||
static updateInfo(name: string, version: string) {
|
||||
ServerDispatch.updateInfo(name, version);
|
||||
}
|
||||
|
||||
static updateStatus(state: number, description: string) {
|
||||
ServerDispatch.updateStatus(state, description);
|
||||
|
||||
if (state === StatusEnum.DISCONNECTED) {
|
||||
this.connectionClosed(state);
|
||||
}
|
||||
}
|
||||
|
||||
static updateUser(user: User) {
|
||||
ServerDispatch.updateUser(user);
|
||||
}
|
||||
|
||||
static updateUsers(users: User[]) {
|
||||
ServerDispatch.updateUsers(users);
|
||||
}
|
||||
|
||||
static userJoined(user: User) {
|
||||
ServerDispatch.userJoined(user);
|
||||
}
|
||||
|
||||
static userLeft(userName: string) {
|
||||
ServerDispatch.userLeft(userName);
|
||||
}
|
||||
|
||||
static viewLogs(logs: Log[]) {
|
||||
ServerDispatch.viewLogs(NormalizeService.normalizeLogs(logs));
|
||||
}
|
||||
|
||||
static serverMessage(message: string) {
|
||||
ServerDispatch.serverMessage(sanitizeHtml(message));
|
||||
}
|
||||
}
|
|
@ -1,94 +0,0 @@
|
|||
import { ServerDispatch, ServerConnectParams } from "store";
|
||||
import { StatusEnum } from "types";
|
||||
|
||||
import { sanitizeHtml } from "websocket/utils";
|
||||
import { WebClient } from "websocket/WebClient";
|
||||
|
||||
import { NormalizeService } from "websocket";
|
||||
|
||||
export default class SessionService {
|
||||
webClient: WebClient;
|
||||
|
||||
constructor(webClient) {
|
||||
this.webClient = webClient;
|
||||
}
|
||||
|
||||
clearStore() {
|
||||
ServerDispatch.clearStore();
|
||||
}
|
||||
|
||||
connectServer(options: ServerConnectParams) {
|
||||
ServerDispatch.connectServer();
|
||||
this.webClient.updateStatus(StatusEnum.CONNECTING, "Connecting...");
|
||||
this.webClient.connect(options);
|
||||
}
|
||||
|
||||
disconnectServer() {
|
||||
this.webClient.updateStatus(StatusEnum.DISCONNECTING, "Disconnecting...");
|
||||
this.webClient.disconnect();
|
||||
}
|
||||
|
||||
connectionClosed(reason) {
|
||||
ServerDispatch.connectionClosed(reason);
|
||||
}
|
||||
|
||||
updateBuddyList(buddyList) {
|
||||
ServerDispatch.updateBuddyList(buddyList);
|
||||
}
|
||||
|
||||
addToBuddyList(user) {
|
||||
ServerDispatch.addToBuddyList(user);
|
||||
}
|
||||
|
||||
removeFromBuddyList(userName) {
|
||||
ServerDispatch.removeFromBuddyList(userName);
|
||||
}
|
||||
|
||||
updateIgnoreList(ignoreList) {
|
||||
ServerDispatch.updateIgnoreList(ignoreList);
|
||||
}
|
||||
|
||||
addToIgnoreList(user) {
|
||||
ServerDispatch.addToIgnoreList(user);
|
||||
}
|
||||
|
||||
removeFromIgnoreList(userName) {
|
||||
ServerDispatch.removeFromIgnoreList(userName);
|
||||
}
|
||||
|
||||
updateInfo(name, version) {
|
||||
ServerDispatch.updateInfo(name, version);
|
||||
}
|
||||
|
||||
updateStatus(state, description) {
|
||||
ServerDispatch.updateStatus(state, description);
|
||||
|
||||
if (state === StatusEnum.DISCONNECTED) {
|
||||
this.connectionClosed({ reason: description });
|
||||
}
|
||||
}
|
||||
|
||||
updateUser(user) {
|
||||
ServerDispatch.updateUser(user);
|
||||
}
|
||||
|
||||
updateUsers(users) {
|
||||
ServerDispatch.updateUsers(users);
|
||||
}
|
||||
|
||||
userJoined(user) {
|
||||
ServerDispatch.userJoined(user);
|
||||
}
|
||||
|
||||
userLeft(userId) {
|
||||
ServerDispatch.userLeft(userId);
|
||||
}
|
||||
|
||||
viewLogs(logs) {
|
||||
ServerDispatch.viewLogs(NormalizeService.normalizeLogs(logs));
|
||||
}
|
||||
|
||||
serverMessage(message) {
|
||||
ServerDispatch.serverMessage(sanitizeHtml(message));
|
||||
}
|
||||
}
|
|
@ -1,3 +1,2 @@
|
|||
export { default as NormalizeService } from "./NormalizeService";
|
||||
export { default as RoomService } from "./RoomService";
|
||||
export { default as SessionService } from "./SessionService";
|
||||
export { RoomPersistence } from "./RoomPersistence";
|
||||
export { SessionPersistence } from "./SessionPersistence";
|
||||
|
|
43
webclient/src/websocket/services/KeepAliveService.tsx
Normal file
43
webclient/src/websocket/services/KeepAliveService.tsx
Normal file
|
@ -0,0 +1,43 @@
|
|||
import { Subject } from "rxjs";
|
||||
|
||||
import { WebSocketService } from "./WebSocketService";
|
||||
|
||||
export class KeepAliveService {
|
||||
private socket: WebSocketService;
|
||||
|
||||
private keepalivecb: NodeJS.Timeout;
|
||||
private lastPingPending: boolean;
|
||||
|
||||
public disconnected$ = new Subject<void>();
|
||||
|
||||
constructor(socket: WebSocketService) {
|
||||
this.socket = socket;
|
||||
}
|
||||
|
||||
public startPingLoop(interval: number, ping: Function): void {
|
||||
this.keepalivecb = setInterval(() => {
|
||||
// check if the previous ping got no reply
|
||||
if (this.lastPingPending) {
|
||||
this.disconnected$.next();
|
||||
}
|
||||
|
||||
// stop the ping loop if we"re disconnected
|
||||
if (!this.socket.checkReadyState(WebSocket.OPEN)) {
|
||||
this.endPingLoop();
|
||||
return;
|
||||
}
|
||||
|
||||
this.lastPingPending = true;
|
||||
ping(() => this.lastPingPending = false);
|
||||
}, interval);
|
||||
}
|
||||
|
||||
public endPingLoop() {
|
||||
clearInterval(this.keepalivecb);
|
||||
this.keepalivecb = null;
|
||||
}
|
||||
|
||||
public resetPingFlag() {
|
||||
this.lastPingPending = false;
|
||||
}
|
||||
}
|
132
webclient/src/websocket/services/ProtobufService.tsx
Normal file
132
webclient/src/websocket/services/ProtobufService.tsx
Normal file
|
@ -0,0 +1,132 @@
|
|||
import protobuf from "protobufjs";
|
||||
|
||||
import ProtoFiles from "../ProtoFiles";
|
||||
import { WebClient } from "../WebClient";
|
||||
|
||||
import { RoomEvents, SessionEvents } from '../events';
|
||||
|
||||
export interface ProtobufEvents {
|
||||
[event: string]: Function;
|
||||
}
|
||||
|
||||
export class ProtobufService {
|
||||
static PB_FILE_DIR = `${process.env.PUBLIC_URL}/pb`;
|
||||
|
||||
public controller;
|
||||
private cmdId = 0;
|
||||
private pendingCommands: { [cmdId: string]: Function } = {};
|
||||
|
||||
private webClient: WebClient;
|
||||
|
||||
constructor(webClient: WebClient) {
|
||||
this.webClient = webClient;
|
||||
|
||||
this.loadProtobufFiles();
|
||||
}
|
||||
|
||||
public resetCommands() {
|
||||
this.cmdId = 0;
|
||||
this.pendingCommands = {};
|
||||
}
|
||||
|
||||
public sendRoomCommand(roomId: number, roomCmd: number, callback?: Function) {
|
||||
const cmd = this.controller.CommandContainer.create({
|
||||
"roomId" : roomId,
|
||||
"roomCommand" : [ roomCmd ]
|
||||
});
|
||||
|
||||
this.sendCommand(cmd, raw => callback && callback(raw));
|
||||
}
|
||||
|
||||
public sendSessionCommand(sesCmd: number, callback?: Function) {
|
||||
const cmd = this.controller.CommandContainer.create({
|
||||
"sessionCommand" : [ sesCmd ]
|
||||
});
|
||||
|
||||
this.sendCommand(cmd, (raw) => callback && callback(raw));
|
||||
}
|
||||
|
||||
public sendModeratorCommand(modCmd: number, callback?: Function) {
|
||||
const cmd = this.controller.CommandContainer.create({
|
||||
"moderatorCommand" : [ modCmd ]
|
||||
});
|
||||
|
||||
this.sendCommand(cmd, (raw) => callback && callback(raw));
|
||||
}
|
||||
|
||||
public sendCommand(cmd: number, callback: Function) {
|
||||
this.cmdId++;
|
||||
|
||||
cmd["cmdId"] = this.cmdId;
|
||||
this.pendingCommands[this.cmdId] = callback;
|
||||
|
||||
if (this.webClient.socket.checkReadyState(WebSocket.OPEN)) {
|
||||
this.webClient.socket.send(this.controller.CommandContainer.encode(cmd).finish());
|
||||
}
|
||||
}
|
||||
|
||||
public handleMessageEvent({ data }: MessageEvent): void {
|
||||
try {
|
||||
const uint8msg = new Uint8Array(data);
|
||||
const msg = this.controller.ServerMessage.decode(uint8msg);
|
||||
|
||||
if (msg) {
|
||||
switch (msg.messageType) {
|
||||
case this.controller.ServerMessage.MessageType.RESPONSE:
|
||||
this.processServerResponse(msg.response);
|
||||
break;
|
||||
case this.controller.ServerMessage.MessageType.ROOM_EVENT:
|
||||
this.processRoomEvent(msg.roomEvent, msg);
|
||||
break;
|
||||
case this.controller.ServerMessage.MessageType.SESSION_EVENT:
|
||||
this.processSessionEvent(msg.sessionEvent, msg);
|
||||
break;
|
||||
case this.controller.ServerMessage.MessageType.GAME_EVENT_CONTAINER:
|
||||
// @TODO
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Processing failed:", err);
|
||||
}
|
||||
}
|
||||
|
||||
private processServerResponse(response: any) {
|
||||
const { cmdId } = response;
|
||||
|
||||
if (this.pendingCommands[cmdId]) {
|
||||
this.pendingCommands[cmdId](response);
|
||||
delete this.pendingCommands[cmdId];
|
||||
}
|
||||
}
|
||||
|
||||
private processRoomEvent(response: any, raw: any) {
|
||||
this.processEvent(response, RoomEvents, raw);
|
||||
}
|
||||
|
||||
private processSessionEvent(response: any, raw: any) {
|
||||
this.processEvent(response, SessionEvents, raw);
|
||||
}
|
||||
|
||||
private processEvent(response: any, events: ProtobufEvents, raw: any) {
|
||||
for (const event in events) {
|
||||
const payload = response[event];
|
||||
|
||||
if (payload) {
|
||||
events[event](payload, raw);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private loadProtobufFiles() {
|
||||
const files = ProtoFiles.map(file => `${ProtobufService.PB_FILE_DIR}/${file}`);
|
||||
|
||||
this.controller = new protobuf.Root();
|
||||
this.controller.load(files, { keepCase: false }, (err, root) => {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
101
webclient/src/websocket/services/WebSocketService.tsx
Normal file
101
webclient/src/websocket/services/WebSocketService.tsx
Normal file
|
@ -0,0 +1,101 @@
|
|||
import { Subject } from 'rxjs';
|
||||
|
||||
import { ServerStatus, StatusEnum } from "types";
|
||||
|
||||
import { KeepAliveService } from "./KeepAliveService";
|
||||
import { WebClient } from '../WebClient';
|
||||
|
||||
export interface WebSocketOptions {
|
||||
host: string;
|
||||
port: string;
|
||||
user: string;
|
||||
pass: string;
|
||||
autojoinrooms: boolean;
|
||||
keepalive: number;
|
||||
}
|
||||
|
||||
export class WebSocketService {
|
||||
private socket: WebSocket;
|
||||
private webClient: WebClient;
|
||||
public keepAliveService: KeepAliveService;
|
||||
|
||||
public message$: Subject<MessageEvent> = new Subject();
|
||||
public statusChange$: Subject<ServerStatus> = new Subject();
|
||||
|
||||
private status: StatusEnum = StatusEnum.DISCONNECTED;
|
||||
private keepalive: number;
|
||||
|
||||
constructor(webClient: WebClient) {
|
||||
this.webClient = webClient;
|
||||
|
||||
this.keepAliveService = new KeepAliveService(this);
|
||||
this.keepAliveService.disconnected$.subscribe(() => {
|
||||
this.disconnect();
|
||||
this.updateStatus(StatusEnum.DISCONNECTED, "Connection timeout");
|
||||
});
|
||||
}
|
||||
|
||||
public connect(options: WebSocketOptions, protocol: string = 'wss'): void {
|
||||
const { host, port, keepalive } = options;
|
||||
this.keepalive = keepalive;
|
||||
|
||||
this.socket = this.createWebSocket(`${protocol}://${host}:${port}`);
|
||||
}
|
||||
|
||||
public disconnect(): void {
|
||||
if (this.socket) {
|
||||
this.socket.close();
|
||||
}
|
||||
}
|
||||
|
||||
public checkReadyState(state: number): boolean {
|
||||
return this.socket.readyState === state;
|
||||
}
|
||||
|
||||
public send(message): void {
|
||||
this.socket.send(message);
|
||||
}
|
||||
|
||||
public updateStatus(status: StatusEnum, description: string): void {
|
||||
this.status = status;
|
||||
this.statusChange$.next({status, description});
|
||||
}
|
||||
|
||||
private createWebSocket(url: string): WebSocket {
|
||||
const socket = new WebSocket(url);
|
||||
socket.binaryType = "arraybuffer"; // We are talking binary
|
||||
|
||||
socket.onopen = () => {
|
||||
this.updateStatus(StatusEnum.CONNECTED, "Connected");
|
||||
|
||||
this.keepAliveService.startPingLoop(this.keepalive, (pingReceived: Function) => {
|
||||
const command = this.webClient.protobuf.controller.SessionCommand.create({
|
||||
".Command_Ping.ext" : this.webClient.protobuf.controller.Command_Ping.create()
|
||||
});
|
||||
|
||||
this.webClient.protobuf.sendSessionCommand(command, () => {
|
||||
pingReceived();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
socket.onclose = () => {
|
||||
// dont overwrite failure messages
|
||||
if (this.status !== StatusEnum.DISCONNECTED) {
|
||||
this.updateStatus(StatusEnum.DISCONNECTED, "Connection Closed");
|
||||
}
|
||||
|
||||
this.keepAliveService.endPingLoop();
|
||||
};
|
||||
|
||||
socket.onerror = () => {
|
||||
this.updateStatus(StatusEnum.DISCONNECTED, "Connection Failed");
|
||||
};
|
||||
|
||||
socket.onmessage = (event: MessageEvent) => {
|
||||
this.message$.next(event);
|
||||
}
|
||||
|
||||
return socket;
|
||||
}
|
||||
}
|
|
@ -1,6 +1,8 @@
|
|||
import { Game, GametypeMap, Log, LogGroups, Message, Room } from 'types';
|
||||
|
||||
export default class NormalizeService {
|
||||
// Flatten room gameTypes into map object
|
||||
static normalizeRoomInfo(roomInfo) {
|
||||
static normalizeRoomInfo(roomInfo: Room): void {
|
||||
roomInfo.gametypeMap = {};
|
||||
|
||||
const { gametypeList, gametypeMap, gameList } = roomInfo;
|
||||
|
@ -15,7 +17,7 @@ export default class NormalizeService {
|
|||
|
||||
// Flatten gameTypes[] into gameType field
|
||||
// Default sortable values ("" || 0 || -1)
|
||||
static normalizeGameObject(game, gametypeMap) {
|
||||
static normalizeGameObject(game: Game, gametypeMap: GametypeMap): void {
|
||||
const { gameTypes, description } = game;
|
||||
const hasType = gameTypes && gameTypes.length;
|
||||
game.gameType = hasType ? gametypeMap[gameTypes[0]] : "";
|
||||
|
@ -24,17 +26,17 @@ export default class NormalizeService {
|
|||
}
|
||||
|
||||
// Flatten logs[] into object mapped by targetType (room, game, chat)
|
||||
static normalizeLogs(logs) {
|
||||
static normalizeLogs(logs: Log[]): LogGroups {
|
||||
return logs.reduce((obj, log) => {
|
||||
const { targetType } = log;
|
||||
obj[targetType] = obj[targetType] || [];
|
||||
obj[targetType].push(log);
|
||||
return obj;
|
||||
}, {});
|
||||
}, {} as LogGroups);
|
||||
}
|
||||
|
||||
// messages sent by current user dont have their username prepended
|
||||
static normalizeUserMessage(message) {
|
||||
static normalizeUserMessage(message: Message): void {
|
||||
const { name } = message;
|
||||
|
||||
if (name) {
|
|
@ -1,8 +1,8 @@
|
|||
function s4() {
|
||||
function s4(): string {
|
||||
const s4 = Math.floor((1 + Math.random()) * 0x10000);
|
||||
return s4.toString(16).substring(1);
|
||||
}
|
||||
|
||||
export function guid() {
|
||||
export function guid(): string {
|
||||
return s4() + s4() + "-" + s4() + "-" + s4() + "-" + s4() + "-" + s4() + s4() + s4();
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
import $ from "jquery";
|
||||
|
||||
export function sanitizeHtml(msg) {
|
||||
export function sanitizeHtml(msg: string): string {
|
||||
const $div = $("<div>").html(msg);
|
||||
const whitelist = {
|
||||
tags: "br,a,img,center,b,font",
|
||||
|
@ -16,13 +16,13 @@ export function sanitizeHtml(msg) {
|
|||
return $div.html();
|
||||
}
|
||||
|
||||
function enforceTagWhitelist($el, tags) {
|
||||
function enforceTagWhitelist($el: JQuery<HTMLElement>, tags: string): void {
|
||||
$el.find("*").not(tags).each(function() {
|
||||
$(this).replaceWith(this.innerHTML);
|
||||
});
|
||||
}
|
||||
|
||||
function enforceAttrWhitelist($el, attrs) {
|
||||
function enforceAttrWhitelist($el: JQuery<HTMLElement>, attrs: string[]): void {
|
||||
$el.find("*").each(function() {
|
||||
var attributes = this.attributes;
|
||||
var i = attributes.length;
|
||||
|
@ -34,7 +34,7 @@ function enforceAttrWhitelist($el, attrs) {
|
|||
});
|
||||
}
|
||||
|
||||
function enforceHrefWhitelist($el, hrefs) {
|
||||
function enforceHrefWhitelist($el: JQuery<HTMLElement>, hrefs: string[]): void {
|
||||
$el.find("[href]").each(function() {
|
||||
const $_el = $(this);
|
||||
const attributeValue = $_el.attr("href");
|
||||
|
|
Loading…
Reference in a new issue