diff --git a/webclient/src/api/AuthenticationService.tsx b/webclient/src/api/AuthenticationService.tsx index 368390708..7b3a46988 100644 --- a/webclient/src/api/AuthenticationService.tsx +++ b/webclient/src/api/AuthenticationService.tsx @@ -1,5 +1,6 @@ import { StatusEnum, User, WebSocketConnectReason, WebSocketConnectOptions } from 'types'; import { SessionCommands, webClient } from 'websocket'; +import { ProtoController } from 'websocket/services/ProtoController'; export class AuthenticationService { static login(options: WebSocketConnectOptions): void { @@ -39,7 +40,7 @@ export class AuthenticationService { } static isModerator(user: User): boolean { - const moderatorLevel = webClient.protobuf.controller.ServerInfo_User.UserLevelFlag.IsModerator; + const moderatorLevel = ProtoController.root.ServerInfo_User.UserLevelFlag.IsModerator; // @TODO tell cockatrice not to do this so shittily return (user.userLevel & moderatorLevel) === moderatorLevel; } diff --git a/webclient/src/api/ModeratorService.tsx b/webclient/src/api/ModeratorService.tsx index e7c8822e8..6c22ee55e 100644 --- a/webclient/src/api/ModeratorService.tsx +++ b/webclient/src/api/ModeratorService.tsx @@ -23,7 +23,7 @@ export class ModeratorService { ModeratorCommands.viewLogHistory(filters); } - static warnUser(userName: string, reason: string, clientid?: string, removeMessage?: boolean): void { - ModeratorCommands.warnUser(userName, reason, clientid, removeMessage); + static warnUser(userName: string, reason: string, clientid?: string, removeMessages?: number): void { + ModeratorCommands.warnUser(userName, reason, clientid, removeMessages); } } diff --git a/webclient/src/api/SessionService.tsx b/webclient/src/api/SessionService.tsx index 051954f93..2787f098d 100644 --- a/webclient/src/api/SessionService.tsx +++ b/webclient/src/api/SessionService.tsx @@ -1,6 +1,4 @@ import { SessionCommands } from 'websocket'; -import { common } from 'protobufjs'; -import IBytesValue = common.IBytesValue; export class SessionService { static addToBuddyList(userName: string) { @@ -27,7 +25,7 @@ export class SessionService { SessionCommands.accountEdit(passwordCheck, realName, email, country); } - static changeAccountImage(image: IBytesValue): void { + static changeAccountImage(image: Uint8Array): void { SessionCommands.accountImage(image); } diff --git a/webclient/src/store/server/server.actions.ts b/webclient/src/store/server/server.actions.ts index ed1f06977..8af5adb75 100644 --- a/webclient/src/store/server/server.actions.ts +++ b/webclient/src/store/server/server.actions.ts @@ -1,4 +1,4 @@ -import { WebSocketConnectOptions } from 'types'; +import { DeckList, DeckStorageTreeItem, ReplayMatch, WebSocketConnectOptions } from 'types'; import { Types } from './server.types'; export const Actions = { @@ -210,4 +210,33 @@ export const Actions = { type: Types.WARN_USER, userName, }), + grantReplayAccess: (replayId: number, moderatorName: string) => ({ + type: Types.GRANT_REPLAY_ACCESS, + replayId, + moderatorName, + }), + forceActivateUser: (usernameToActivate: string, moderatorName: string) => ({ + type: Types.FORCE_ACTIVATE_USER, + usernameToActivate, + moderatorName, + }), + getAdminNotes: (userName: string, notes: string) => ({ + type: Types.GET_ADMIN_NOTES, + userName, + notes, + }), + updateAdminNotes: (userName: string, notes: string) => ({ + type: Types.UPDATE_ADMIN_NOTES, + userName, + notes, + }), + replayList: (matchList: ReplayMatch[]) => ({ type: Types.REPLAY_LIST, matchList }), + replayAdded: (matchInfo: ReplayMatch) => ({ type: Types.REPLAY_ADDED, matchInfo }), + replayModifyMatch: (gameId: number, doNotHide: boolean) => ({ type: Types.REPLAY_MODIFY_MATCH, gameId, doNotHide }), + replayDeleteMatch: (gameId: number) => ({ type: Types.REPLAY_DELETE_MATCH, gameId }), + backendDecks: (deckList: DeckList) => ({ type: Types.BACKEND_DECKS, deckList }), + deckNewDir: (path: string, dirName: string) => ({ type: Types.DECK_NEW_DIR, path, dirName }), + deckDelDir: (path: string) => ({ type: Types.DECK_DEL_DIR, path }), + deckUpload: (path: string, treeItem: DeckStorageTreeItem) => ({ type: Types.DECK_UPLOAD, path, treeItem }), + deckDelete: (deckId: number) => ({ type: Types.DECK_DELETE, deckId }), } diff --git a/webclient/src/store/server/server.dispatch.ts b/webclient/src/store/server/server.dispatch.ts index 3a958043a..3d3b6d360 100644 --- a/webclient/src/store/server/server.dispatch.ts +++ b/webclient/src/store/server/server.dispatch.ts @@ -1,7 +1,7 @@ import { reset } from 'redux-form'; import { Actions } from './server.actions'; import { store } from 'store'; -import { WebSocketConnectOptions } from 'types'; +import { DeckList, DeckStorageTreeItem, ReplayMatch, WebSocketConnectOptions } from 'types'; export const Dispatch = { initialized: () => { @@ -177,4 +177,43 @@ export const Dispatch = { warnUser: (userName) => { store.dispatch(Actions.warnUser(userName)) }, + grantReplayAccess: (replayId: number, moderatorName: string) => { + store.dispatch(Actions.grantReplayAccess(replayId, moderatorName)); + }, + forceActivateUser: (usernameToActivate: string, moderatorName: string) => { + store.dispatch(Actions.forceActivateUser(usernameToActivate, moderatorName)); + }, + getAdminNotes: (userName: string, notes: string) => { + store.dispatch(Actions.getAdminNotes(userName, notes)); + }, + updateAdminNotes: (userName: string, notes: string) => { + store.dispatch(Actions.updateAdminNotes(userName, notes)); + }, + replayList: (matchList: ReplayMatch[]) => { + store.dispatch(Actions.replayList(matchList)); + }, + replayAdded: (matchInfo: ReplayMatch) => { + store.dispatch(Actions.replayAdded(matchInfo)); + }, + replayModifyMatch: (gameId: number, doNotHide: boolean) => { + store.dispatch(Actions.replayModifyMatch(gameId, doNotHide)); + }, + replayDeleteMatch: (gameId: number) => { + store.dispatch(Actions.replayDeleteMatch(gameId)); + }, + backendDecks: (deckList: DeckList) => { + store.dispatch(Actions.backendDecks(deckList)); + }, + deckNewDir: (path: string, dirName: string) => { + store.dispatch(Actions.deckNewDir(path, dirName)); + }, + deckDelDir: (path: string) => { + store.dispatch(Actions.deckDelDir(path)); + }, + deckUpload: (path: string, treeItem: DeckStorageTreeItem) => { + store.dispatch(Actions.deckUpload(path, treeItem)); + }, + deckDelete: (deckId: number) => { + store.dispatch(Actions.deckDelete(deckId)); + }, } diff --git a/webclient/src/store/server/server.interfaces.ts b/webclient/src/store/server/server.interfaces.ts index 499197852..97e9d99da 100644 --- a/webclient/src/store/server/server.interfaces.ts +++ b/webclient/src/store/server/server.interfaces.ts @@ -1,4 +1,6 @@ -import { WarnHistoryItem, BanHistoryItem, LogItem, SortBy, User, UserSortField, WebSocketConnectOptions, WarnListItem } from 'types'; +import { + WarnHistoryItem, BanHistoryItem, DeckList, LogItem, ReplayMatch, SortBy, User, UserSortField, WebSocketConnectOptions, WarnListItem +} from 'types'; import { NotifyUserData, ServerShutdownData, UserMessageData } from 'websocket/events/session/interfaces'; export interface ServerConnectParams { @@ -67,6 +69,9 @@ export interface ServerState { }; warnListOptions: WarnListItem[]; warnUser: string; + adminNotes: { [userName: string]: string }; + replays: ReplayMatch[]; + backendDecks: DeckList | null; } export interface ServerStateStatus { diff --git a/webclient/src/store/server/server.reducer.ts b/webclient/src/store/server/server.reducer.ts index 46daa4007..a63418eeb 100644 --- a/webclient/src/store/server/server.reducer.ts +++ b/webclient/src/store/server/server.reducer.ts @@ -1,10 +1,60 @@ -import { SortDirection, StatusEnum, UserLevelFlag, UserSortField } from 'types'; +import { DeckStorageFolder, DeckStorageTreeItem, SortDirection, StatusEnum, UserLevelFlag, UserSortField } from 'types'; import { SortUtil } from '../common'; import { ServerState } from './server.interfaces' import { Types } from './server.types'; +function splitPath(path: string): string[] { + return path ? path.split('/') : []; +} + +function insertAtPath(folder: DeckStorageFolder, pathSegments: string[], item: DeckStorageTreeItem): DeckStorageFolder { + if (pathSegments.length === 0 || (pathSegments.length === 1 && pathSegments[0] === '')) { + return { items: [...folder.items, item] }; + } + const [head, ...tail] = pathSegments; + const match = folder.items.find(child => child.name === head && child.folder); + if (match) { + return { + items: folder.items.map(child => + child === match + ? { ...child, folder: insertAtPath(child.folder!, tail, item) } + : child + ), + }; + } + const created: DeckStorageTreeItem = { id: 0, name: head, file: null, folder: insertAtPath({ items: [] }, tail, item) }; + return { items: [...folder.items, created] }; +} + +function removeById(folder: DeckStorageFolder, id: number): DeckStorageFolder { + return { + items: folder.items + .filter(item => item.id !== id) + .map(item => + item.folder ? { ...item, folder: removeById(item.folder, id) } : item + ), + }; +} + +function removeByPath(folder: DeckStorageFolder, pathSegments: string[]): DeckStorageFolder { + if (pathSegments.length === 0 || (pathSegments.length === 1 && pathSegments[0] === '')) { + return folder; + } + const [head, ...tail] = pathSegments; + if (tail.length === 0) { + return { items: folder.items.filter(item => !(item.name === head && item.folder !== null)) }; + } + return { + items: folder.items.map(item => + item.name === head && item.folder + ? { ...item, folder: removeByPath(item.folder, tail) } + : item + ), + }; +} + const initialState: ServerState = { initialized: false, buddyList: [], @@ -40,6 +90,9 @@ const initialState: ServerState = { warnHistory: {}, warnListOptions: [], warnUser: '', + adminNotes: {}, + replays: [], + backendDecks: null, }; export const serverReducer = (state = initialState, action: any) => { @@ -247,7 +300,7 @@ export const serverReducer = (state = initialState, action: any) => { messages: { ...state.messages, [userName]: [ - ...state.messages[userName], + ...(state.messages[userName] ?? []), action.messageData, ], } @@ -328,6 +381,17 @@ export const serverReducer = (state = initialState, action: any) => { warnUser: userName, }; } + case Types.GET_ADMIN_NOTES: + case Types.UPDATE_ADMIN_NOTES: { + const { userName, notes } = action; + return { + ...state, + adminNotes: { + ...state.adminNotes, + [userName]: notes, + } + }; + } case Types.ADJUST_MOD: { const { userName, shouldBeMod, shouldBeJudge } = action; @@ -346,6 +410,71 @@ export const serverReducer = (state = initialState, action: any) => { }) }; } + case Types.REPLAY_LIST: { + return { ...state, replays: [...action.matchList] }; + } + case Types.REPLAY_ADDED: { + return { ...state, replays: [...state.replays, action.matchInfo] }; + } + case Types.REPLAY_MODIFY_MATCH: { + return { + ...state, + replays: state.replays.map(r => + r.gameId === action.gameId ? { ...r, doNotHide: action.doNotHide } : r + ), + }; + } + case Types.REPLAY_DELETE_MATCH: { + return { ...state, replays: state.replays.filter(r => r.gameId !== action.gameId) }; + } + case Types.BACKEND_DECKS: { + return { ...state, backendDecks: action.deckList }; + } + case Types.DECK_UPLOAD: { + if (!state.backendDecks) { + return state; + } + return { + ...state, + backendDecks: { + root: insertAtPath(state.backendDecks.root, splitPath(action.path), action.treeItem), + }, + }; + } + case Types.DECK_DELETE: { + if (!state.backendDecks) { + return state; + } + return { + ...state, + backendDecks: { + root: removeById(state.backendDecks.root, action.deckId), + }, + }; + } + case Types.DECK_NEW_DIR: { + if (!state.backendDecks) { + return state; + } + const newFolder: DeckStorageTreeItem = { id: 0, name: action.dirName, file: null, folder: { items: [] } }; + return { + ...state, + backendDecks: { + root: insertAtPath(state.backendDecks.root, splitPath(action.path), newFolder), + }, + }; + } + case Types.DECK_DEL_DIR: { + if (!state.backendDecks) { + return state; + } + return { + ...state, + backendDecks: { + root: removeByPath(state.backendDecks.root, splitPath(action.path)), + }, + }; + } default: return state; } diff --git a/webclient/src/store/server/server.selectors.ts b/webclient/src/store/server/server.selectors.ts index 263946425..fa9f82297 100644 --- a/webclient/src/store/server/server.selectors.ts +++ b/webclient/src/store/server/server.selectors.ts @@ -16,5 +16,7 @@ export const Selectors = { getUsers: ({ server }: State) => server.users, getLogs: ({ server }: State) => server.logs, getBuddyList: ({ server }: State) => server.buddyList, - getIgnoreList: ({ server }: State) => server.ignoreList + getIgnoreList: ({ server }: State) => server.ignoreList, + getReplays: ({ server }: State) => server.replays, + getBackendDecks: ({ server }: State) => server.backendDecks, } diff --git a/webclient/src/store/server/server.types.ts b/webclient/src/store/server/server.types.ts index ab9307295..fb4249011 100644 --- a/webclient/src/store/server/server.types.ts +++ b/webclient/src/store/server/server.types.ts @@ -54,4 +54,19 @@ export const Types = { WARN_HISTORY: '[Server] Warn History', WARN_LIST_OPTIONS: '[Server] Warn List Options', WARN_USER: '[Server] Warn User', + GRANT_REPLAY_ACCESS: '[Server] Grant Replay Access', + FORCE_ACTIVATE_USER: '[Server] Force Activate User', + GET_ADMIN_NOTES: '[Server] Get Admin Notes', + UPDATE_ADMIN_NOTES: '[Server] Update Admin Notes', + // Replay + REPLAY_LIST: '[Server] Replay List', + REPLAY_ADDED: '[Server] Replay Added', + REPLAY_MODIFY_MATCH: '[Server] Replay Modify Match', + REPLAY_DELETE_MATCH: '[Server] Replay Delete Match', + // Deck Storage + BACKEND_DECKS: '[Server] Backend Decks', + DECK_NEW_DIR: '[Server] Deck New Dir', + DECK_DEL_DIR: '[Server] Deck Del Dir', + DECK_UPLOAD: '[Server] Deck Upload', + DECK_DELETE: '[Server] Deck Delete', }; diff --git a/webclient/src/types/deckList.ts b/webclient/src/types/deckList.ts index 18212079e..9d7d792a6 100644 --- a/webclient/src/types/deckList.ts +++ b/webclient/src/types/deckList.ts @@ -13,6 +13,6 @@ export interface DeckStorageFile { export interface DeckStorageTreeItem { id: number; name: string; - file: DeckStorageFile; - folder: DeckStorageFolder; + file: DeckStorageFile | null; + folder: DeckStorageFolder | null; } diff --git a/webclient/src/types/index.ts b/webclient/src/types/index.ts index 00838a16d..ded9962e5 100644 --- a/webclient/src/types/index.ts +++ b/webclient/src/types/index.ts @@ -16,3 +16,4 @@ export * from './logs'; export * from './session'; export * from './deckList'; export * from './moderator'; +export * from './replay'; diff --git a/webclient/src/types/logs.ts b/webclient/src/types/logs.ts index 4c2f0364e..3cf34b486 100644 --- a/webclient/src/types/logs.ts +++ b/webclient/src/types/logs.ts @@ -1,8 +1,10 @@ export interface LogFilters { - userName: string; - ipAddress: string; - gameName: string; - gameId: string; - message: string; - logLocation: string; + userName?: string; + ipAddress?: string; + gameName?: string; + gameId?: string; + message?: string; + logLocation?: string[]; + dateRange: number; + maximumResults?: number; } diff --git a/webclient/src/types/replay.ts b/webclient/src/types/replay.ts new file mode 100644 index 000000000..dfa78538a --- /dev/null +++ b/webclient/src/types/replay.ts @@ -0,0 +1,16 @@ +export interface Replay { + replayId: number; + replayName: string; + duration: number; +} + +export interface ReplayMatch { + replayList: Replay[]; + gameId: number; + roomName: string; + timeStarted: number; + length: number; + gameName: string; + playerNames: string[]; + doNotHide: boolean; +} diff --git a/webclient/src/websocket/commands/admin/adjustMod.ts b/webclient/src/websocket/commands/admin/adjustMod.ts index 5e8a83153..fd71fece1 100644 --- a/webclient/src/websocket/commands/admin/adjustMod.ts +++ b/webclient/src/websocket/commands/admin/adjustMod.ts @@ -1,26 +1,10 @@ -import webClient from '../../WebClient'; +import { BackendService } from '../../services/BackendService'; import { AdminPersistence } from '../../persistence'; export function adjustMod(userName: string, shouldBeMod?: boolean, shouldBeJudge?: boolean): void { - const command = webClient.protobuf.controller.Command_AdjustMod.create({ userName, shouldBeMod, shouldBeJudge }); - const sc = webClient.protobuf.controller.AdminCommand.create({ '.Command_AdjustMod.ext': command }); - - webClient.protobuf.sendAdminCommand(sc, (raw) => { - const { responseCode } = raw; - - let error: string; - - switch (responseCode) { - case webClient.protobuf.controller.Response.ResponseCode.RespOk: - AdminPersistence.adjustMod(userName, shouldBeMod, shouldBeJudge); - return; - default: - error = 'Failed to reload config.'; - break; - } - - if (error) { - console.error(responseCode, error); - } + BackendService.sendAdminCommand('Command_AdjustMod', { userName, shouldBeMod, shouldBeJudge }, { + onSuccess: () => { + AdminPersistence.adjustMod(userName, shouldBeMod, shouldBeJudge); + }, }); } diff --git a/webclient/src/websocket/commands/admin/reloadConfig.ts b/webclient/src/websocket/commands/admin/reloadConfig.ts index 16d2a574b..979f3ec73 100644 --- a/webclient/src/websocket/commands/admin/reloadConfig.ts +++ b/webclient/src/websocket/commands/admin/reloadConfig.ts @@ -1,26 +1,10 @@ -import webClient from '../../WebClient'; +import { BackendService } from '../../services/BackendService'; import { AdminPersistence } from '../../persistence'; export function reloadConfig(): void { - const command = webClient.protobuf.controller.Command_ReloadConfig.create(); - const sc = webClient.protobuf.controller.AdminCommand.create({ '.Command_ReloadConfig.ext': command }); - - webClient.protobuf.sendAdminCommand(sc, (raw) => { - const { responseCode } = raw; - - let error: string; - - switch (responseCode) { - case webClient.protobuf.controller.Response.ResponseCode.RespOk: - AdminPersistence.reloadConfig(); - return; - default: - error = 'Failed to reload config.'; - break; - } - - if (error) { - console.error(responseCode, error); - } + BackendService.sendAdminCommand('Command_ReloadConfig', {}, { + onSuccess: () => { + AdminPersistence.reloadConfig(); + }, }); } diff --git a/webclient/src/websocket/commands/admin/shutdownServer.ts b/webclient/src/websocket/commands/admin/shutdownServer.ts index 0624e823d..e65c900db 100644 --- a/webclient/src/websocket/commands/admin/shutdownServer.ts +++ b/webclient/src/websocket/commands/admin/shutdownServer.ts @@ -1,26 +1,10 @@ -import webClient from '../../WebClient'; +import { BackendService } from '../../services/BackendService'; import { AdminPersistence } from '../../persistence'; export function shutdownServer(reason: string, minutes: number): void { - const command = webClient.protobuf.controller.Command_ShutdownServer.create({ reason, minutes }); - const sc = webClient.protobuf.controller.AdminCommand.create({ '.Command_ShutdownServer.ext': command }); - - webClient.protobuf.sendAdminCommand(sc, (raw) => { - const { responseCode } = raw; - - let error: string; - - switch (responseCode) { - case webClient.protobuf.controller.Response.ResponseCode.RespOk: - AdminPersistence.shutdownServer(); - return; - default: - error = 'Failed to update server message.'; - break; - } - - if (error) { - console.error(responseCode, error); - } + BackendService.sendAdminCommand('Command_ShutdownServer', { reason, minutes }, { + onSuccess: () => { + AdminPersistence.shutdownServer(); + }, }); } diff --git a/webclient/src/websocket/commands/admin/updateServerMessage.ts b/webclient/src/websocket/commands/admin/updateServerMessage.ts index cb025f5a9..e2b194514 100644 --- a/webclient/src/websocket/commands/admin/updateServerMessage.ts +++ b/webclient/src/websocket/commands/admin/updateServerMessage.ts @@ -1,26 +1,10 @@ -import webClient from '../../WebClient'; +import { BackendService } from '../../services/BackendService'; import { AdminPersistence } from '../../persistence'; export function updateServerMessage(): void { - const command = webClient.protobuf.controller.Command_UpdateServerMessage.create(); - const sc = webClient.protobuf.controller.AdminCommand.create({ '.Command_UpdateServerMessage.ext': command }); - - webClient.protobuf.sendAdminCommand(sc, (raw) => { - const { responseCode } = raw; - - let error: string; - - switch (responseCode) { - case webClient.protobuf.controller.Response.ResponseCode.RespOk: - AdminPersistence.updateServerMessage(); - return; - default: - error = 'Failed to update server message.'; - break; - } - - if (error) { - console.error(responseCode, error); - } + BackendService.sendAdminCommand('Command_UpdateServerMessage', {}, { + onSuccess: () => { + AdminPersistence.updateServerMessage(); + }, }); } diff --git a/webclient/src/websocket/commands/moderator/banFromServer.ts b/webclient/src/websocket/commands/moderator/banFromServer.ts index 028eb4606..e45e34504 100644 --- a/webclient/src/websocket/commands/moderator/banFromServer.ts +++ b/webclient/src/websocket/commands/moderator/banFromServer.ts @@ -1,29 +1,13 @@ -import webClient from '../../WebClient'; +import { BackendService } from '../../services/BackendService'; import { ModeratorPersistence } from '../../persistence'; export function banFromServer(minutes: number, userName?: string, address?: string, reason?: string, visibleReason?: string, clientid?: string, removeMessages?: number): void { - const command = webClient.protobuf.controller.Command_BanFromServer.create({ + BackendService.sendModeratorCommand('Command_BanFromServer', { minutes, userName, address, reason, visibleReason, clientid, removeMessages - }); - const sc = webClient.protobuf.controller.ModeratorCommand.create({ '.Command_BanFromServer.ext': command }); - - webClient.protobuf.sendModeratorCommand(sc, (raw) => { - const { responseCode } = raw; - - let error: string; - - switch (responseCode) { - case webClient.protobuf.controller.Response.ResponseCode.RespOk: - ModeratorPersistence.banFromServer(userName); - return; - default: - error = 'Failed to ban user.'; - break; - } - - if (error) { - console.error(responseCode, error); - } + }, { + onSuccess: () => { + ModeratorPersistence.banFromServer(userName); + }, }); } diff --git a/webclient/src/websocket/commands/moderator/forceActivateUser.ts b/webclient/src/websocket/commands/moderator/forceActivateUser.ts new file mode 100644 index 000000000..d4138a015 --- /dev/null +++ b/webclient/src/websocket/commands/moderator/forceActivateUser.ts @@ -0,0 +1,10 @@ +import { BackendService } from '../../services/BackendService'; +import { ModeratorPersistence } from '../../persistence'; + +export function forceActivateUser(usernameToActivate: string, moderatorName: string): void { + BackendService.sendModeratorCommand('Command_ForceActivateUser', { usernameToActivate, moderatorName }, { + onSuccess: () => { + ModeratorPersistence.forceActivateUser(usernameToActivate, moderatorName); + }, + }); +} diff --git a/webclient/src/websocket/commands/moderator/getAdminNotes.ts b/webclient/src/websocket/commands/moderator/getAdminNotes.ts new file mode 100644 index 000000000..d4f626aa2 --- /dev/null +++ b/webclient/src/websocket/commands/moderator/getAdminNotes.ts @@ -0,0 +1,11 @@ +import { BackendService } from '../../services/BackendService'; +import { ModeratorPersistence } from '../../persistence'; + +export function getAdminNotes(userName: string): void { + BackendService.sendModeratorCommand('Command_GetAdminNotes', { userName }, { + responseName: 'Response_GetAdminNotes', + onSuccess: (response) => { + ModeratorPersistence.getAdminNotes(userName, response.notes); + }, + }); +} diff --git a/webclient/src/websocket/commands/moderator/getBanHistory.ts b/webclient/src/websocket/commands/moderator/getBanHistory.ts index 3378f5842..dd4e90eda 100644 --- a/webclient/src/websocket/commands/moderator/getBanHistory.ts +++ b/webclient/src/websocket/commands/moderator/getBanHistory.ts @@ -1,27 +1,11 @@ -import webClient from '../../WebClient'; +import { BackendService } from '../../services/BackendService'; import { ModeratorPersistence } from '../../persistence'; export function getBanHistory(userName: string): void { - const command = webClient.protobuf.controller.Command_GetBanHistory.create({ userName }); - const sc = webClient.protobuf.controller.ModeratorCommand.create({ '.Command_GetBanHistory.ext': command }); - - webClient.protobuf.sendModeratorCommand(sc, (raw) => { - const { responseCode } = raw; - - let error: string; - - switch (responseCode) { - case webClient.protobuf.controller.Response.ResponseCode.RespOk: - const { banList } = raw['.Response_BanHistory.ext']; - ModeratorPersistence.banHistory(userName, banList); - return; - default: - error = 'Failed to get ban history.'; - break; - } - - if (error) { - console.error(responseCode, error); - } + BackendService.sendModeratorCommand('Command_GetBanHistory', { userName }, { + responseName: 'Response_BanHistory', + onSuccess: (response) => { + ModeratorPersistence.banHistory(userName, response.banList); + }, }); } diff --git a/webclient/src/websocket/commands/moderator/getWarnHistory.ts b/webclient/src/websocket/commands/moderator/getWarnHistory.ts index fce008eec..c47e2c6e4 100644 --- a/webclient/src/websocket/commands/moderator/getWarnHistory.ts +++ b/webclient/src/websocket/commands/moderator/getWarnHistory.ts @@ -1,27 +1,11 @@ -import webClient from '../../WebClient'; +import { BackendService } from '../../services/BackendService'; import { ModeratorPersistence } from '../../persistence'; export function getWarnHistory(userName: string): void { - const command = webClient.protobuf.controller.Command_GetWarnHistory.create({ userName }); - const sc = webClient.protobuf.controller.ModeratorCommand.create({ '.Command_GetWarnHistory.ext': command }); - - webClient.protobuf.sendModeratorCommand(sc, (raw) => { - const { responseCode } = raw; - - let error: string; - - switch (responseCode) { - case webClient.protobuf.controller.Response.ResponseCode.RespOk: - const { warnList } = raw['.Response_WarnHistory.ext']; - ModeratorPersistence.warnHistory(userName, warnList); - return; - default: - error = 'Failed to get warn history.'; - break; - } - - if (error) { - console.error(responseCode, error); - } + BackendService.sendModeratorCommand('Command_GetWarnHistory', { userName }, { + responseName: 'Response_WarnHistory', + onSuccess: (response) => { + ModeratorPersistence.warnHistory(userName, response.warnList); + }, }); } diff --git a/webclient/src/websocket/commands/moderator/getWarnList.ts b/webclient/src/websocket/commands/moderator/getWarnList.ts index f83e5eb1b..412aee09e 100644 --- a/webclient/src/websocket/commands/moderator/getWarnList.ts +++ b/webclient/src/websocket/commands/moderator/getWarnList.ts @@ -1,27 +1,11 @@ -import webClient from '../../WebClient'; +import { BackendService } from '../../services/BackendService'; import { ModeratorPersistence } from '../../persistence'; export function getWarnList(modName: string, userName: string, userClientid: string): void { - const command = webClient.protobuf.controller.Command_GetWarnList.create({ modName, userName, userClientid }); - const sc = webClient.protobuf.controller.ModeratorCommand.create({ '.Command_GetWarnList.ext': command }); - - webClient.protobuf.sendModeratorCommand(sc, (raw) => { - const { responseCode } = raw; - - let error: string; - - switch (responseCode) { - case webClient.protobuf.controller.Response.ResponseCode.RespOk: - const { warning } = raw['.Response_WarnList.ext']; - ModeratorPersistence.warnListOptions(warning); - return; - default: - error = 'Failed to get warn list.'; - break; - } - - if (error) { - console.error(responseCode, error); - } + BackendService.sendModeratorCommand('Command_GetWarnList', { modName, userName, userClientid }, { + responseName: 'Response_WarnList', + onSuccess: (response) => { + ModeratorPersistence.warnListOptions(response.warning); + }, }); } diff --git a/webclient/src/websocket/commands/moderator/grantReplayAccess.ts b/webclient/src/websocket/commands/moderator/grantReplayAccess.ts new file mode 100644 index 000000000..74d64d17a --- /dev/null +++ b/webclient/src/websocket/commands/moderator/grantReplayAccess.ts @@ -0,0 +1,10 @@ +import { BackendService } from '../../services/BackendService'; +import { ModeratorPersistence } from '../../persistence'; + +export function grantReplayAccess(replayId: number, moderatorName: string): void { + BackendService.sendModeratorCommand('Command_GrantReplayAccess', { replayId, moderatorName }, { + onSuccess: () => { + ModeratorPersistence.grantReplayAccess(replayId, moderatorName); + }, + }); +} diff --git a/webclient/src/websocket/commands/moderator/index.ts b/webclient/src/websocket/commands/moderator/index.ts index cb4f2455c..10bb0e1c6 100644 --- a/webclient/src/websocket/commands/moderator/index.ts +++ b/webclient/src/websocket/commands/moderator/index.ts @@ -1,6 +1,10 @@ export * from './banFromServer'; +export * from './forceActivateUser'; +export * from './getAdminNotes'; export * from './getBanHistory'; export * from './getWarnHistory'; export * from './getWarnList'; +export * from './grantReplayAccess'; +export * from './updateAdminNotes'; export * from './viewLogHistory'; export * from './warnUser'; diff --git a/webclient/src/websocket/commands/moderator/updateAdminNotes.ts b/webclient/src/websocket/commands/moderator/updateAdminNotes.ts new file mode 100644 index 000000000..c7ac315c5 --- /dev/null +++ b/webclient/src/websocket/commands/moderator/updateAdminNotes.ts @@ -0,0 +1,10 @@ +import { BackendService } from '../../services/BackendService'; +import { ModeratorPersistence } from '../../persistence'; + +export function updateAdminNotes(userName: string, notes: string): void { + BackendService.sendModeratorCommand('Command_UpdateAdminNotes', { userName, notes }, { + onSuccess: () => { + ModeratorPersistence.updateAdminNotes(userName, notes); + }, + }); +} diff --git a/webclient/src/websocket/commands/moderator/viewLogHistory.ts b/webclient/src/websocket/commands/moderator/viewLogHistory.ts index 29e3c4bd6..19a930608 100644 --- a/webclient/src/websocket/commands/moderator/viewLogHistory.ts +++ b/webclient/src/websocket/commands/moderator/viewLogHistory.ts @@ -1,28 +1,12 @@ -import webClient from '../../WebClient'; +import { BackendService } from '../../services/BackendService'; import { ModeratorPersistence } from '../../persistence'; import { LogFilters } from 'types'; export function viewLogHistory(filters: LogFilters): void { - const command = webClient.protobuf.controller.Command_ViewLogHistory.create(filters); - const sc = webClient.protobuf.controller.ModeratorCommand.create({ '.Command_ViewLogHistory.ext': command }); - - webClient.protobuf.sendModeratorCommand(sc, (raw) => { - const { responseCode } = raw; - - let error: string; - - switch (responseCode) { - case webClient.protobuf.controller.Response.ResponseCode.RespOk: - const { logMessage } = raw['.Response_ViewLogHistory.ext']; - ModeratorPersistence.viewLogs(logMessage) - return; - default: - error = 'Failed to retrieve log history.'; - break; - } - - if (error) { - console.error(responseCode, error); - } + BackendService.sendModeratorCommand('Command_ViewLogHistory', filters, { + responseName: 'Response_ViewLogHistory', + onSuccess: (response) => { + ModeratorPersistence.viewLogs(response.logMessage); + }, }); } diff --git a/webclient/src/websocket/commands/moderator/warnUser.ts b/webclient/src/websocket/commands/moderator/warnUser.ts index bef9ee003..0e0271d4b 100644 --- a/webclient/src/websocket/commands/moderator/warnUser.ts +++ b/webclient/src/websocket/commands/moderator/warnUser.ts @@ -1,26 +1,10 @@ -import webClient from '../../WebClient'; +import { BackendService } from '../../services/BackendService'; import { ModeratorPersistence } from '../../persistence'; -export function warnUser(userName: string, reason: string, clientid?: string, removeMessage?: boolean): void { - const command = webClient.protobuf.controller.Command_WarnUser.create({ userName, reason, clientid, removeMessage }); - const sc = webClient.protobuf.controller.ModeratorCommand.create({ '.Command_WarnUser.ext': command }); - - webClient.protobuf.sendModeratorCommand(sc, (raw) => { - const { responseCode } = raw; - - let error: string; - - switch (responseCode) { - case webClient.protobuf.controller.Response.ResponseCode.RespOk: - ModeratorPersistence.warnUser(userName); - return; - default: - error = 'Failed to warn user.'; - break; - } - - if (error) { - console.error(responseCode, error); - } +export function warnUser(userName: string, reason: string, clientid?: string, removeMessages?: number): void { + BackendService.sendModeratorCommand('Command_WarnUser', { userName, reason, clientid, removeMessages }, { + onSuccess: () => { + ModeratorPersistence.warnUser(userName); + }, }); } diff --git a/webclient/src/websocket/commands/room/createGame.ts b/webclient/src/websocket/commands/room/createGame.ts index e5fd66f80..62565e0e6 100644 --- a/webclient/src/websocket/commands/room/createGame.ts +++ b/webclient/src/websocket/commands/room/createGame.ts @@ -1,21 +1,11 @@ +import { BackendService } from '../../services/BackendService'; import { RoomPersistence } from '../../persistence'; -import webClient from '../../WebClient'; import { GameConfig } from 'types'; export function createGame(roomId: number, gameConfig: GameConfig): void { - const command = webClient.protobuf.controller.Command_CreateGame.create(gameConfig); - const rc = webClient.protobuf.controller.RoomCommand.create({ '.Command_CreateGame.ext': command }); - - webClient.protobuf.sendRoomCommand(roomId, rc, (raw) => { - const { responseCode } = raw; - - switch (responseCode) { - case webClient.protobuf.controller.Response.ResponseCode.RespOk: - RoomPersistence.gameCreated(roomId); - break; - default: - console.log('Failed to do the thing'); - } + BackendService.sendRoomCommand(roomId, 'Command_CreateGame', gameConfig, { + onSuccess: () => { + RoomPersistence.gameCreated(roomId); + }, }); } - diff --git a/webclient/src/websocket/commands/room/joinGame.ts b/webclient/src/websocket/commands/room/joinGame.ts index 19a672924..ef4b1fff2 100644 --- a/webclient/src/websocket/commands/room/joinGame.ts +++ b/webclient/src/websocket/commands/room/joinGame.ts @@ -1,21 +1,11 @@ +import { BackendService } from '../../services/BackendService'; import { RoomPersistence } from '../../persistence'; -import webClient from '../../WebClient'; -import { GameConfig, JoinGameParams } from 'types'; +import { JoinGameParams } from 'types'; export function joinGame(roomId: number, joinGameParams: JoinGameParams): void { - const command = webClient.protobuf.controller.Command_JoinGame.create(joinGameParams); - const rc = webClient.protobuf.controller.RoomCommand.create({ '.Command_JoinGame.ext': command }); - - webClient.protobuf.sendRoomCommand(roomId, rc, (raw) => { - const { responseCode } = raw; - - switch (responseCode) { - case webClient.protobuf.controller.Response.ResponseCode.RespOk: - RoomPersistence.joinedGame(roomId, joinGameParams.gameId); - break; - default: - console.log('Failed to do the thing'); - } + BackendService.sendRoomCommand(roomId, 'Command_JoinGame', joinGameParams, { + onSuccess: () => { + RoomPersistence.joinedGame(roomId, joinGameParams.gameId); + }, }); } - diff --git a/webclient/src/websocket/commands/room/leaveRoom.ts b/webclient/src/websocket/commands/room/leaveRoom.ts index 477ee39a0..7cd64a0e2 100644 --- a/webclient/src/websocket/commands/room/leaveRoom.ts +++ b/webclient/src/websocket/commands/room/leaveRoom.ts @@ -1,19 +1,10 @@ +import { BackendService } from '../../services/BackendService'; import { RoomPersistence } from '../../persistence'; -import webClient from '../../WebClient'; export function leaveRoom(roomId: number): void { - const command = webClient.protobuf.controller.Command_LeaveRoom.create(); - const rc = webClient.protobuf.controller.RoomCommand.create({ '.Command_LeaveRoom.ext': command }); - - webClient.protobuf.sendRoomCommand(roomId, rc, (raw) => { - const { responseCode } = raw; - - switch (responseCode) { - case webClient.protobuf.controller.Response.ResponseCode.RespOk: - RoomPersistence.leaveRoom(roomId); - break; - default: - console.log(`Failed to leave Room ${roomId} [${responseCode}] : `, raw); - } + BackendService.sendRoomCommand(roomId, 'Command_LeaveRoom', {}, { + onSuccess: () => { + RoomPersistence.leaveRoom(roomId); + }, }); } diff --git a/webclient/src/websocket/commands/room/roomSay.ts b/webclient/src/websocket/commands/room/roomSay.ts index c06abd3ab..a429845be 100644 --- a/webclient/src/websocket/commands/room/roomSay.ts +++ b/webclient/src/websocket/commands/room/roomSay.ts @@ -1,4 +1,4 @@ -import webClient from '../../WebClient'; +import { BackendService } from '../../services/BackendService'; export function roomSay(roomId: number, message: string): void { const trimmed = message.trim(); @@ -7,8 +7,5 @@ export function roomSay(roomId: number, message: string): void { return; } - const command = webClient.protobuf.controller.Command_RoomSay.create({ 'message': trimmed }); - const rc = webClient.protobuf.controller.RoomCommand.create({ '.Command_RoomSay.ext': command }); - - webClient.protobuf.sendRoomCommand(roomId, rc); + BackendService.sendRoomCommand(roomId, 'Command_RoomSay', { message: trimmed }, {}); } diff --git a/webclient/src/websocket/commands/session/accountEdit.ts b/webclient/src/websocket/commands/session/accountEdit.ts index f9210cb9c..31bf2d3f6 100644 --- a/webclient/src/websocket/commands/session/accountEdit.ts +++ b/webclient/src/websocket/commands/session/accountEdit.ts @@ -1,25 +1,10 @@ -import webClient from '../../WebClient'; +import { BackendService } from '../../services/BackendService'; import { SessionPersistence } from '../../persistence'; export function accountEdit(passwordCheck: string, realName?: string, email?: string, country?: string): void { - const command = webClient.protobuf.controller.Command_AccountEdit.create({ passwordCheck, realName, email, country }); - const sc = webClient.protobuf.controller.SessionCommand.create({ '.Command_AccountEdit.ext': command }); - - webClient.protobuf.sendSessionCommand(sc, raw => { - const { responseCode } = raw; - - switch (responseCode) { - case webClient.protobuf.controller.Response.ResponseCode.RespOk: - SessionPersistence.accountEditChanged(realName, email, country); - break; - case webClient.protobuf.controller.Response.ResponseCode.RespFunctionNotAllowed: - console.log('Not allowed'); - break; - case webClient.protobuf.controller.Response.ResponseCode.RespWrongPassword: - console.log('Wrong password'); - break; - default: - console.log('Failed to update information'); - } + BackendService.sendSessionCommand('Command_AccountEdit', { passwordCheck, realName, email, country }, { + onSuccess: () => { + SessionPersistence.accountEditChanged(realName, email, country); + }, }); } diff --git a/webclient/src/websocket/commands/session/accountImage.ts b/webclient/src/websocket/commands/session/accountImage.ts index b735696a9..cd0e24403 100644 --- a/webclient/src/websocket/commands/session/accountImage.ts +++ b/webclient/src/websocket/commands/session/accountImage.ts @@ -1,27 +1,10 @@ -import webClient from '../../WebClient'; +import { BackendService } from '../../services/BackendService'; import { SessionPersistence } from '../../persistence'; -import { common } from 'protobufjs'; -import IBytesValue = common.IBytesValue; -export function accountImage(image: IBytesValue): void { - const command = webClient.protobuf.controller.Command_AccountImage.create({ image }); - const sc = webClient.protobuf.controller.SessionCommand.create({ '.Command_AccountImage.ext': command }); - - webClient.protobuf.sendSessionCommand(sc, raw => { - const { responseCode } = raw; - - switch (responseCode) { - case webClient.protobuf.controller.Response.ResponseCode.RespOk: - SessionPersistence.accountImageChanged(image); - break; - case webClient.protobuf.controller.Response.ResponseCode.RespFunctionNotAllowed: - console.log('Not allowed'); - break; - case webClient.protobuf.controller.Response.ResponseCode.RespWrongPassword: - console.log('Wrong password'); - break; - default: - console.log('Failed to update information'); - } +export function accountImage(image: Uint8Array): void { + BackendService.sendSessionCommand('Command_AccountImage', { image }, { + onSuccess: () => { + SessionPersistence.accountImageChanged(image); + }, }); } diff --git a/webclient/src/websocket/commands/session/accountPassword.ts b/webclient/src/websocket/commands/session/accountPassword.ts index 0b5ef0111..81c7a993b 100644 --- a/webclient/src/websocket/commands/session/accountPassword.ts +++ b/webclient/src/websocket/commands/session/accountPassword.ts @@ -1,19 +1,10 @@ -import webClient from '../../WebClient'; +import { BackendService } from '../../services/BackendService'; import { SessionPersistence } from '../../persistence'; export function accountPassword(oldPassword: string, newPassword: string, hashedNewPassword: string): void { - const command = webClient.protobuf.controller.Command_AccountPassword.create({ oldPassword, newPassword, hashedNewPassword }); - const sc = webClient.protobuf.controller.SessionCommand.create({ '.Command_AccountPassword.ext': command }); - - webClient.protobuf.sendSessionCommand(sc, raw => { - const { responseCode } = raw; - - switch (responseCode) { - case webClient.protobuf.controller.Response.ResponseCode.RespOk: - SessionPersistence.accountPasswordChange(); - break; - default: - console.log('Failed to change password'); - } + BackendService.sendSessionCommand('Command_AccountPassword', { oldPassword, newPassword, hashedNewPassword }, { + onSuccess: () => { + SessionPersistence.accountPasswordChange(); + }, }); } diff --git a/webclient/src/websocket/commands/session/activate.ts b/webclient/src/websocket/commands/session/activate.ts index 74e41afeb..4cd0e8c4e 100644 --- a/webclient/src/websocket/commands/session/activate.ts +++ b/webclient/src/websocket/commands/session/activate.ts @@ -2,6 +2,8 @@ import { AccountActivationParams } from 'store'; import { StatusEnum, WebSocketConnectOptions } from 'types'; import webClient from '../../WebClient'; +import { BackendService } from '../../services/BackendService'; +import { ProtoController } from '../../services/ProtoController'; import { SessionPersistence } from '../../persistence'; import { disconnect, login, updateStatus } from './'; @@ -9,23 +11,21 @@ import { disconnect, login, updateStatus } from './'; export function activate(options: WebSocketConnectOptions, passwordSalt?: string): void { const { userName, token } = options as unknown as AccountActivationParams; - const accountActivationConfig = { + BackendService.sendSessionCommand('Command_Activate', { ...webClient.clientConfig, userName, token, - }; - - const command = webClient.protobuf.controller.Command_Activate.create(accountActivationConfig); - const sc = webClient.protobuf.controller.SessionCommand.create({ '.Command_Activate.ext': command }); - - webClient.protobuf.sendSessionCommand(sc, raw => { - if (raw.responseCode === webClient.protobuf.controller.Response.ResponseCode.RespActivationAccepted) { - SessionPersistence.accountActivationSuccess(); - login(options, passwordSalt); - } else { + }, { + onResponseCode: { + [ProtoController.root.Response.ResponseCode.RespActivationAccepted]: () => { + SessionPersistence.accountActivationSuccess(); + login(options, passwordSalt); + }, + }, + onError: () => { updateStatus(StatusEnum.DISCONNECTED, 'Account Activation Failed'); disconnect(); SessionPersistence.accountActivationFailed(); - } + }, }); } diff --git a/webclient/src/websocket/commands/session/addToList.ts b/webclient/src/websocket/commands/session/addToList.ts index 6696d39d4..c5bc3c5f0 100644 --- a/webclient/src/websocket/commands/session/addToList.ts +++ b/webclient/src/websocket/commands/session/addToList.ts @@ -1,4 +1,4 @@ -import webClient from '../../WebClient'; +import { BackendService } from '../../services/BackendService'; import { SessionPersistence } from '../../persistence'; export function addToBuddyList(userName: string): void { @@ -10,16 +10,9 @@ export function addToIgnoreList(userName: string): void { } export function addToList(list: string, userName: string): void { - const command = webClient.protobuf.controller.Command_AddToList.create({ list, userName }); - const sc = webClient.protobuf.controller.SessionCommand.create({ '.Command_AddToList.ext': command }); - - webClient.protobuf.sendSessionCommand(sc, ({ responseCode }) => { - switch (responseCode) { - case webClient.protobuf.controller.Response.ResponseCode.RespOk: - SessionPersistence.addToList(list, userName); - break; - default: - console.error('Failed to add to list', responseCode); - } + BackendService.sendSessionCommand('Command_AddToList', { list, userName }, { + onSuccess: () => { + SessionPersistence.addToList(list, userName); + }, }); } diff --git a/webclient/src/websocket/commands/session/deckDel.ts b/webclient/src/websocket/commands/session/deckDel.ts index c77d89aa3..752ce78d5 100644 --- a/webclient/src/websocket/commands/session/deckDel.ts +++ b/webclient/src/websocket/commands/session/deckDel.ts @@ -1,19 +1,10 @@ -import webClient from '../../WebClient'; +import { BackendService } from '../../services/BackendService'; import { SessionPersistence } from '../../persistence'; export function deckDel(deckId: number): void { - const command = webClient.protobuf.controller.Command_DeckDel.create({ deckId }); - const sc = webClient.protobuf.controller.SessionCommand.create({ '.Command_DeckDel.ext': command }); - - webClient.protobuf.sendSessionCommand(sc, raw => { - const { responseCode } = raw; - - switch (responseCode) { - case webClient.protobuf.controller.Response.ResponseCode.RespOk: - SessionPersistence.deckDelete(deckId); - break; - default: - console.log('Failed to do the thing'); - } + BackendService.sendSessionCommand('Command_DeckDel', { deckId }, { + onSuccess: () => { + SessionPersistence.deleteServerDeck(deckId); + }, }); } diff --git a/webclient/src/websocket/commands/session/deckDelDir.ts b/webclient/src/websocket/commands/session/deckDelDir.ts index ff4b25ec3..df5bbc223 100644 --- a/webclient/src/websocket/commands/session/deckDelDir.ts +++ b/webclient/src/websocket/commands/session/deckDelDir.ts @@ -1,19 +1,10 @@ -import webClient from '../../WebClient'; +import { BackendService } from '../../services/BackendService'; import { SessionPersistence } from '../../persistence'; export function deckDelDir(path: string): void { - const command = webClient.protobuf.controller.Command_DeckDelDir.create({ path }); - const sc = webClient.protobuf.controller.SessionCommand.create({ '.Command_DeckDelDir.ext': command }); - - webClient.protobuf.sendSessionCommand(sc, raw => { - const { responseCode } = raw; - - switch (responseCode) { - case webClient.protobuf.controller.Response.ResponseCode.RespOk: - SessionPersistence.deckDeleteDir(path); - break; - default: - console.log('Failed to do the thing'); - } + BackendService.sendSessionCommand('Command_DeckDelDir', { path }, { + onSuccess: () => { + SessionPersistence.deleteServerDeckDir(path); + }, }); } diff --git a/webclient/src/websocket/commands/session/deckDownload.ts b/webclient/src/websocket/commands/session/deckDownload.ts deleted file mode 100644 index a0c4bd461..000000000 --- a/webclient/src/websocket/commands/session/deckDownload.ts +++ /dev/null @@ -1,19 +0,0 @@ -import webClient from '../../WebClient'; -import { SessionPersistence } from '../../persistence'; - -export function deckDownload(deckId: number): void { - const command = webClient.protobuf.controller.Command_DeckDownload.create({ deckId }); - const sc = webClient.protobuf.controller.SessionCommand.create({ '.Command_DeckDownload.ext': command }); - - webClient.protobuf.sendSessionCommand(sc, raw => { - const { responseCode } = raw; - - switch (responseCode) { - case webClient.protobuf.controller.Response.ResponseCode.RespOk: - SessionPersistence.deckDownload(deckId); - break; - default: - console.log('Failed to do the thing'); - } - }); -} diff --git a/webclient/src/websocket/commands/session/deckList.ts b/webclient/src/websocket/commands/session/deckList.ts index e84b8efb3..3d5a3499a 100644 --- a/webclient/src/websocket/commands/session/deckList.ts +++ b/webclient/src/websocket/commands/session/deckList.ts @@ -1,22 +1,11 @@ -import webClient from '../../WebClient'; +import { BackendService } from '../../services/BackendService'; import { SessionPersistence } from '../../persistence'; export function deckList(): void { - const command = webClient.protobuf.controller.Command_DeckList.create(); - const sc = webClient.protobuf.controller.SessionCommand.create({ '.Command_DeckList.ext': command }); - - webClient.protobuf.sendSessionCommand(sc, raw => { - const { responseCode } = raw; - const response = raw['.Response_DeckList.ext']; - - if (response) { - switch (responseCode) { - case webClient.protobuf.controller.Response.ResponseCode.RespOk: - SessionPersistence.deckList(response); - break; - default: - console.log('Failed to do the thing'); - } - } + BackendService.sendSessionCommand('Command_DeckList', {}, { + responseName: 'Response_DeckList', + onSuccess: (response) => { + SessionPersistence.updateServerDecks(response); + }, }); } diff --git a/webclient/src/websocket/commands/session/deckNewDir.ts b/webclient/src/websocket/commands/session/deckNewDir.ts index b9bca6208..85ab16afb 100644 --- a/webclient/src/websocket/commands/session/deckNewDir.ts +++ b/webclient/src/websocket/commands/session/deckNewDir.ts @@ -1,19 +1,10 @@ -import webClient from '../../WebClient'; +import { BackendService } from '../../services/BackendService'; import { SessionPersistence } from '../../persistence'; export function deckNewDir(path: string, dirName: string): void { - const command = webClient.protobuf.controller.Command_DeckNewDir.create({ path, dirName }); - const sc = webClient.protobuf.controller.SessionCommand.create({ '.Command_DeckNewDir.ext': command }); - - webClient.protobuf.sendSessionCommand(sc, raw => { - const { responseCode } = raw; - - switch (responseCode) { - case webClient.protobuf.controller.Response.ResponseCode.RespOk: - SessionPersistence.deckNewDir(path, dirName); - break; - default: - console.log('Failed to do the thing'); - } + BackendService.sendSessionCommand('Command_DeckNewDir', { path, dirName }, { + onSuccess: () => { + SessionPersistence.createServerDeckDir(path, dirName); + }, }); } diff --git a/webclient/src/websocket/commands/session/deckUpload.ts b/webclient/src/websocket/commands/session/deckUpload.ts index af526c8fc..2679c4e8e 100644 --- a/webclient/src/websocket/commands/session/deckUpload.ts +++ b/webclient/src/websocket/commands/session/deckUpload.ts @@ -1,23 +1,11 @@ -import webClient from '../../WebClient'; +import { BackendService } from '../../services/BackendService'; import { SessionPersistence } from '../../persistence'; export function deckUpload(path: string, deckId: number, deckList: string): void { - const command = webClient.protobuf.controller.Command_DeckUpload.create({ path, deckId, deckList }); - const sc = webClient.protobuf.controller.SessionCommand.create({ '.Command_DeckUpload.ext': command }); - - webClient.protobuf.sendSessionCommand(sc, raw => { - const { responseCode } = raw; - const response = raw['.Response_DeckUpload.ext']; - - if (response) { - switch (responseCode) { - case webClient.protobuf.controller.Response.ResponseCode.RespOk: - SessionPersistence.deckUpload(response); - break; - default: - console.log('Failed to do the thing'); - } - } - + BackendService.sendSessionCommand('Command_DeckUpload', { path, deckId, deckList }, { + responseName: 'Response_DeckUpload', + onSuccess: (response) => { + SessionPersistence.uploadServerDeck(path, response.newFile); + }, }); } diff --git a/webclient/src/websocket/commands/session/forgotPasswordChallenge.ts b/webclient/src/websocket/commands/session/forgotPasswordChallenge.ts index f6399e6a7..05af1ccf9 100644 --- a/webclient/src/websocket/commands/session/forgotPasswordChallenge.ts +++ b/webclient/src/websocket/commands/session/forgotPasswordChallenge.ts @@ -2,30 +2,27 @@ import { ForgotPasswordChallengeParams } from 'store'; import { StatusEnum, WebSocketConnectOptions } from 'types'; import webClient from '../../WebClient'; +import { BackendService } from '../../services/BackendService'; import { SessionPersistence } from '../../persistence'; import { disconnect, updateStatus } from './'; export function forgotPasswordChallenge(options: WebSocketConnectOptions): void { const { userName, email } = options as unknown as ForgotPasswordChallengeParams; - const forgotPasswordChallengeConfig = { + BackendService.sendSessionCommand('Command_ForgotPasswordChallenge', { ...webClient.clientConfig, userName, email, - }; - - const command = webClient.protobuf.controller.Command_ForgotPasswordChallenge.create(forgotPasswordChallengeConfig); - const sc = webClient.protobuf.controller.SessionCommand.create({ '.Command_ForgotPasswordChallenge.ext': command }); - - webClient.protobuf.sendSessionCommand(sc, raw => { - if (raw.responseCode === webClient.protobuf.controller.Response.ResponseCode.RespOk) { + }, { + onSuccess: () => { updateStatus(StatusEnum.DISCONNECTED, null); SessionPersistence.resetPassword(); - } else { + disconnect(); + }, + onError: () => { updateStatus(StatusEnum.DISCONNECTED, null); SessionPersistence.resetPasswordFailed(); - } - - disconnect(); + disconnect(); + }, }); } diff --git a/webclient/src/websocket/commands/session/forgotPasswordRequest.ts b/webclient/src/websocket/commands/session/forgotPasswordRequest.ts index 5e5fe3bbd..23d301450 100644 --- a/webclient/src/websocket/commands/session/forgotPasswordRequest.ts +++ b/webclient/src/websocket/commands/session/forgotPasswordRequest.ts @@ -2,6 +2,7 @@ import { ForgotPasswordParams } from 'store'; import { StatusEnum, WebSocketConnectOptions } from 'types'; import webClient from '../../WebClient'; +import { BackendService } from '../../services/BackendService'; import { SessionPersistence } from '../../persistence'; import { disconnect, updateStatus } from './'; @@ -9,30 +10,25 @@ import { disconnect, updateStatus } from './'; export function forgotPasswordRequest(options: WebSocketConnectOptions): void { const { userName } = options as unknown as ForgotPasswordParams; - const forgotPasswordConfig = { + BackendService.sendSessionCommand('Command_ForgotPasswordRequest', { ...webClient.clientConfig, userName, - }; - - const command = webClient.protobuf.controller.Command_ForgotPasswordRequest.create(forgotPasswordConfig); - const sc = webClient.protobuf.controller.SessionCommand.create({ '.Command_ForgotPasswordRequest.ext': command }); - - webClient.protobuf.sendSessionCommand(sc, raw => { - if (raw.responseCode === webClient.protobuf.controller.Response.ResponseCode.RespOk) { - const resp = raw['.Response_ForgotPasswordRequest.ext']; - - if (resp.challengeEmail) { + }, { + responseName: 'Response_ForgotPasswordRequest', + onSuccess: (resp) => { + if (resp?.challengeEmail) { updateStatus(StatusEnum.DISCONNECTED, null); SessionPersistence.resetPasswordChallenge(); } else { updateStatus(StatusEnum.DISCONNECTED, null); SessionPersistence.resetPassword(); } - } else { + disconnect(); + }, + onError: () => { updateStatus(StatusEnum.DISCONNECTED, null); SessionPersistence.resetPasswordFailed(); - } - - disconnect(); + disconnect(); + }, }); } diff --git a/webclient/src/websocket/commands/session/forgotPasswordReset.ts b/webclient/src/websocket/commands/session/forgotPasswordReset.ts index 9312b71af..d9a775816 100644 --- a/webclient/src/websocket/commands/session/forgotPasswordReset.ts +++ b/webclient/src/websocket/commands/session/forgotPasswordReset.ts @@ -2,6 +2,7 @@ import { ForgotPasswordResetParams } from 'store'; import { StatusEnum, WebSocketConnectOptions } from 'types'; import webClient from '../../WebClient'; +import { BackendService } from '../../services/BackendService'; import { SessionPersistence } from '../../persistence'; import { hashPassword } from '../../utils'; @@ -10,30 +11,28 @@ import { disconnect, updateStatus } from '.'; export function forgotPasswordReset(options: WebSocketConnectOptions, passwordSalt?: string): void { const { userName, token, newPassword } = options as unknown as ForgotPasswordResetParams; - const forgotPasswordResetConfig: any = { + const params: any = { ...webClient.clientConfig, userName, token, }; if (passwordSalt) { - forgotPasswordResetConfig.hashedNewPassword = hashPassword(passwordSalt, newPassword); + params.hashedNewPassword = hashPassword(passwordSalt, newPassword); } else { - forgotPasswordResetConfig.newPassword = newPassword; + params.newPassword = newPassword; } - const command = webClient.protobuf.controller.Command_ForgotPasswordReset.create(forgotPasswordResetConfig); - const sc = webClient.protobuf.controller.SessionCommand.create({ '.Command_ForgotPasswordReset.ext': command }); - - webClient.protobuf.sendSessionCommand(sc, raw => { - if (raw.responseCode === webClient.protobuf.controller.Response.ResponseCode.RespOk) { + BackendService.sendSessionCommand('Command_ForgotPasswordReset', params, { + onSuccess: () => { updateStatus(StatusEnum.DISCONNECTED, null); SessionPersistence.resetPasswordSuccess(); - } else { + disconnect(); + }, + onError: () => { updateStatus(StatusEnum.DISCONNECTED, null); SessionPersistence.resetPasswordFailed(); - } - - disconnect(); + disconnect(); + }, }); } diff --git a/webclient/src/websocket/commands/session/getGamesOfUser.ts b/webclient/src/websocket/commands/session/getGamesOfUser.ts index 507ba3e20..8fb8aeb5b 100644 --- a/webclient/src/websocket/commands/session/getGamesOfUser.ts +++ b/webclient/src/websocket/commands/session/getGamesOfUser.ts @@ -1,26 +1,11 @@ -import webClient from '../../WebClient'; +import { BackendService } from '../../services/BackendService'; import { SessionPersistence } from '../../persistence'; export function getGamesOfUser(userName: string): void { - const command = webClient.protobuf.controller.Command_GetGamesOfUser.create({ userName }); - const sc = webClient.protobuf.controller.SessionCommand.create({ '.Command_GetGamesOfUser.ext': command }); - - webClient.protobuf.sendSessionCommand(sc, raw => { - const { responseCode } = raw; - const response = raw['.Response_GetGamesOfUser.ext']; - - switch (responseCode) { - case webClient.protobuf.controller.Response.ResponseCode.RespOk: - SessionPersistence.getGamesOfUser(userName, response); - break; - case webClient.protobuf.controller.Response.ResponseCode.RespFunctionNotAllowed: - console.log('Not allowed'); - break; - case webClient.protobuf.controller.Response.ResponseCode.RespWrongPassword: - console.log('Wrong password'); - break; - default: - console.log('Failed to update information'); - } + BackendService.sendSessionCommand('Command_GetGamesOfUser', { userName }, { + responseName: 'Response_GetGamesOfUser', + onSuccess: (response) => { + SessionPersistence.getGamesOfUser(userName, response); + }, }); } diff --git a/webclient/src/websocket/commands/session/getUserInfo.ts b/webclient/src/websocket/commands/session/getUserInfo.ts index 709cc0baa..5b0f178ae 100644 --- a/webclient/src/websocket/commands/session/getUserInfo.ts +++ b/webclient/src/websocket/commands/session/getUserInfo.ts @@ -1,26 +1,11 @@ -import webClient from '../../WebClient'; +import { BackendService } from '../../services/BackendService'; import { SessionPersistence } from '../../persistence'; export function getUserInfo(userName: string): void { - const command = webClient.protobuf.controller.Command_GetUserInfo.create({ userName }); - const sc = webClient.protobuf.controller.SessionCommand.create({ '.Command_GetUserInfo.ext': command }); - - webClient.protobuf.sendSessionCommand(sc, raw => { - const { responseCode } = raw; - - switch (responseCode) { - case webClient.protobuf.controller.Response.ResponseCode.RespOk: - const { userInfo } = raw['.Response_GetUserInfo.ext']; - SessionPersistence.getUserInfo(userInfo); - break; - case webClient.protobuf.controller.Response.ResponseCode.RespFunctionNotAllowed: - console.log('Not allowed'); - break; - case webClient.protobuf.controller.Response.ResponseCode.RespWrongPassword: - console.log('Wrong password'); - break; - default: - console.log('Failed to update information'); - } + BackendService.sendSessionCommand('Command_GetUserInfo', { userName }, { + responseName: 'Response_GetUserInfo', + onSuccess: (response) => { + SessionPersistence.getUserInfo(response.userInfo); + }, }); } diff --git a/webclient/src/websocket/commands/session/index.ts b/webclient/src/websocket/commands/session/index.ts index 829a74b71..74d0d062c 100644 --- a/webclient/src/websocket/commands/session/index.ts +++ b/webclient/src/websocket/commands/session/index.ts @@ -6,12 +6,11 @@ export * from './addToList'; export * from './connect'; export * from './deckDel'; export * from './deckDelDir'; -export * from './deckDownload'; export * from './deckList'; export * from './deckNewDir'; export * from './deckUpload'; export * from './disconnect'; -export * from './forgotPasswordChallenge' +export * from './forgotPasswordChallenge'; export * from './forgotPasswordRequest'; export * from './forgotPasswordReset'; export * from './getGamesOfUser'; @@ -24,12 +23,8 @@ export * from './message'; export * from './ping'; export * from './register'; export * from './removeFromList'; +export * from './replayDeleteMatch'; +export * from './replayList'; +export * from './replayModifyMatch'; export * from './requestPasswordSalt'; export * from './updateStatus'; - -/** TODO - * REPLAY_DELETE_MATCH - * REPLAY_DOWNLOAD - * REPLAY_LIST - * REPLAY_MODIFY_MATCH - */ diff --git a/webclient/src/websocket/commands/session/joinRoom.ts b/webclient/src/websocket/commands/session/joinRoom.ts index 9f4699708..be79976a0 100644 --- a/webclient/src/websocket/commands/session/joinRoom.ts +++ b/webclient/src/websocket/commands/session/joinRoom.ts @@ -1,37 +1,11 @@ -import webClient from '../../WebClient'; +import { BackendService } from '../../services/BackendService'; import { RoomPersistence } from '../../persistence'; export function joinRoom(roomId: number): void { - const command = webClient.protobuf.controller.Command_JoinRoom.create({ roomId }); - const sc = webClient.protobuf.controller.SessionCommand.create({ '.Command_JoinRoom.ext': command }); - - webClient.protobuf.sendSessionCommand(sc, (raw) => { - const { responseCode } = raw; - - let error: string; - - switch (responseCode) { - case webClient.protobuf.controller.Response.ResponseCode.RespOk: - const { roomInfo } = raw['.Response_JoinRoom.ext']; - - RoomPersistence.joinRoom(roomInfo); - return; - case webClient.protobuf.controller.Response.ResponseCode.RespNameNotFound: - error = 'Failed to join the room: it doesn\'t exist on the server.'; - break; - 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 webClient.protobuf.controller.Response.ResponseCode.RespUserLevelTooLow: - error = 'You do not have the required permission to join this room.'; - break; - default: - error = 'Failed to join the room due to an unknown error.'; - break; - } - - if (error) { - console.error(responseCode, error); - } + BackendService.sendSessionCommand('Command_JoinRoom', { roomId }, { + responseName: 'Response_JoinRoom', + onSuccess: (response) => { + RoomPersistence.joinRoom(response.roomInfo); + }, }); } diff --git a/webclient/src/websocket/commands/session/listRooms.ts b/webclient/src/websocket/commands/session/listRooms.ts index b1e31621f..367dada9b 100644 --- a/webclient/src/websocket/commands/session/listRooms.ts +++ b/webclient/src/websocket/commands/session/listRooms.ts @@ -1,8 +1,5 @@ -import webClient from '../../WebClient'; +import { BackendService } from '../../services/BackendService'; export function listRooms(): void { - const command = webClient.protobuf.controller.Command_ListRooms.create(); - const sc = webClient.protobuf.controller.SessionCommand.create({ '.Command_ListRooms.ext': command }); - - webClient.protobuf.sendSessionCommand(sc); + BackendService.sendSessionCommand('Command_ListRooms', {}, {}); } diff --git a/webclient/src/websocket/commands/session/listUsers.ts b/webclient/src/websocket/commands/session/listUsers.ts index 027a251d4..9b95c1344 100644 --- a/webclient/src/websocket/commands/session/listUsers.ts +++ b/webclient/src/websocket/commands/session/listUsers.ts @@ -1,23 +1,11 @@ -import webClient from '../../WebClient'; +import { BackendService } from '../../services/BackendService'; import { SessionPersistence } from '../../persistence'; export function listUsers(): void { - const command = webClient.protobuf.controller.Command_ListUsers.create(); - const sc = webClient.protobuf.controller.SessionCommand.create({ '.Command_ListUsers.ext': command }); - - webClient.protobuf.sendSessionCommand(sc, raw => { - const { responseCode } = raw; - const response = raw['.Response_ListUsers.ext']; - - if (response) { - switch (responseCode) { - case webClient.protobuf.controller.Response.ResponseCode.RespOk: - SessionPersistence.updateUsers(response.userList); - break; - default: - console.log(`Failed to fetch Server Rooms [${responseCode}] : `, raw); - } - } - + BackendService.sendSessionCommand('Command_ListUsers', {}, { + responseName: 'Response_ListUsers', + onSuccess: (response) => { + SessionPersistence.updateUsers(response.userList); + }, }); } diff --git a/webclient/src/websocket/commands/session/login.ts b/webclient/src/websocket/commands/session/login.ts index db24b4f46..6f3ec5ef5 100644 --- a/webclient/src/websocket/commands/session/login.ts +++ b/webclient/src/websocket/commands/session/login.ts @@ -1,5 +1,7 @@ import { StatusEnum, WebSocketConnectOptions } from 'types'; import webClient from '../../WebClient'; +import { BackendService } from '../../services/BackendService'; +import { ProtoController } from '../../services/ProtoController'; import { hashPassword } from '../../utils'; import { SessionPersistence } from '../../persistence'; @@ -25,13 +27,18 @@ export function login(options: WebSocketConnectOptions, passwordSalt?: string): loginConfig.password = password; } - const command = webClient.protobuf.controller.Command_Login.create(loginConfig); - const sc = webClient.protobuf.controller.SessionCommand.create({ '.Command_Login.ext': command }); + const { ResponseCode } = ProtoController.root.Response; - webClient.protobuf.sendSessionCommand(sc, raw => { - const resp = raw['.Response_Login.ext']; + const onLoginError = (message: string, extra?: () => void) => { + updateStatus(StatusEnum.DISCONNECTED, message); + extra?.(); + SessionPersistence.loginFailed(); + disconnect(); + }; - if (raw.responseCode === webClient.protobuf.controller.Response.ResponseCode.RespOk) { + BackendService.sendSessionCommand('Command_Login', loginConfig, { + responseName: 'Response_Login', + onSuccess: (resp) => { const { buddyList, ignoreList, userInfo } = resp; SessionPersistence.updateBuddyList(buddyList); @@ -43,50 +50,30 @@ export function login(options: WebSocketConnectOptions, passwordSalt?: string): listRooms(); updateStatus(StatusEnum.LOGGED_IN, 'Logged in.'); - - return; - } - - switch (raw.responseCode) { - case webClient.protobuf.controller.Response.ResponseCode.RespClientUpdateRequired: - updateStatus(StatusEnum.DISCONNECTED, 'Login failed: missing features'); - break; - - case webClient.protobuf.controller.Response.ResponseCode.RespWrongPassword: - case webClient.protobuf.controller.Response.ResponseCode.RespUsernameInvalid: - updateStatus(StatusEnum.DISCONNECTED, 'Login failed: incorrect username or password'); - break; - - case webClient.protobuf.controller.Response.ResponseCode.RespWouldOverwriteOldSession: - updateStatus(StatusEnum.DISCONNECTED, 'Login failed: duplicated user session'); - break; - - case webClient.protobuf.controller.Response.ResponseCode.RespUserIsBanned: - updateStatus(StatusEnum.DISCONNECTED, 'Login failed: banned user'); - break; - - case webClient.protobuf.controller.Response.ResponseCode.RespRegistrationRequired: - updateStatus(StatusEnum.DISCONNECTED, 'Login failed: registration required'); - break; - - case webClient.protobuf.controller.Response.ResponseCode.RespClientIdRequired: - updateStatus(StatusEnum.DISCONNECTED, 'Login failed: missing client ID'); - break; - - case webClient.protobuf.controller.Response.ResponseCode.RespContextError: - updateStatus(StatusEnum.DISCONNECTED, 'Login failed: server error'); - break; - - case webClient.protobuf.controller.Response.ResponseCode.RespAccountNotActivated: - updateStatus(StatusEnum.DISCONNECTED, 'Login failed: account not activated'); - SessionPersistence.accountAwaitingActivation(options); - break; - - default: - updateStatus(StatusEnum.DISCONNECTED, `Login failed: unknown error: ${raw.responseCode}`); - } - - SessionPersistence.loginFailed(); - disconnect(); + }, + onResponseCode: { + [ResponseCode.RespClientUpdateRequired]: () => + onLoginError('Login failed: missing features'), + [ResponseCode.RespWrongPassword]: () => + onLoginError('Login failed: incorrect username or password'), + [ResponseCode.RespUsernameInvalid]: () => + onLoginError('Login failed: incorrect username or password'), + [ResponseCode.RespWouldOverwriteOldSession]: () => + onLoginError('Login failed: duplicated user session'), + [ResponseCode.RespUserIsBanned]: () => + onLoginError('Login failed: banned user'), + [ResponseCode.RespRegistrationRequired]: () => + onLoginError('Login failed: registration required'), + [ResponseCode.RespClientIdRequired]: () => + onLoginError('Login failed: missing client ID'), + [ResponseCode.RespContextError]: () => + onLoginError('Login failed: server error'), + [ResponseCode.RespAccountNotActivated]: () => + onLoginError('Login failed: account not activated', + () => SessionPersistence.accountAwaitingActivation(options) + ), + }, + onError: (responseCode) => + onLoginError(`Login failed: unknown error: ${responseCode}`), }); } diff --git a/webclient/src/websocket/commands/session/message.ts b/webclient/src/websocket/commands/session/message.ts index 99b615562..075fc3c4b 100644 --- a/webclient/src/websocket/commands/session/message.ts +++ b/webclient/src/websocket/commands/session/message.ts @@ -1,31 +1,10 @@ -import webClient from '../../WebClient'; +import { BackendService } from '../../services/BackendService'; import { SessionPersistence } from '../../persistence'; export function message(userName: string, message: string): void { - const command = webClient.protobuf.controller.Command_Message.create({ userName, message }); - const sc = webClient.protobuf.controller.SessionCommand.create({ '.Command_Message.ext': command }); - - webClient.protobuf.sendSessionCommand(sc, raw => { - const { responseCode } = raw; - - switch (responseCode) { - case webClient.protobuf.controller.Response.ResponseCode.RespOk: - SessionPersistence.directMessageSent(userName, message); - break; - case webClient.protobuf.controller.Response.ResponseCode.RespNameNotFound: - console.log('Name not found'); - break; - case webClient.protobuf.controller.Response.ResponseCode.RespInIgnoreList: - console.log('On ignore list'); - break; - case webClient.protobuf.controller.Response.ResponseCode.RespChatFlood: - console.log('Flooding chat'); - break; - case webClient.protobuf.controller.Response.ResponseCode.RespWrongPassword: - console.log('Wrong password'); - break; - default: - console.log('Failed to send direct message'); - } + BackendService.sendSessionCommand('Command_Message', { userName, message }, { + onSuccess: () => { + SessionPersistence.directMessageSent(userName, message); + }, }); } diff --git a/webclient/src/websocket/commands/session/ping.ts b/webclient/src/websocket/commands/session/ping.ts index 795e0f71f..fea2784a2 100644 --- a/webclient/src/websocket/commands/session/ping.ts +++ b/webclient/src/websocket/commands/session/ping.ts @@ -1,8 +1,7 @@ -import webClient from '../../WebClient'; +import { BackendService } from '../../services/BackendService'; export function ping(pingReceived: Function): void { - const command = webClient.protobuf.controller.Command_Ping.create(); - const sc = webClient.protobuf.controller.SessionCommand.create({ '.Command_Ping.ext': command }); - - webClient.protobuf.sendSessionCommand(sc, pingReceived); + BackendService.sendSessionCommand('Command_Ping', {}, { + onResponse: (raw) => pingReceived(raw), + }); } diff --git a/webclient/src/websocket/commands/session/register.ts b/webclient/src/websocket/commands/session/register.ts index 45b2fa977..a25b85868 100644 --- a/webclient/src/websocket/commands/session/register.ts +++ b/webclient/src/websocket/commands/session/register.ts @@ -1,17 +1,18 @@ import { ServerRegisterParams } from 'store'; -import { WebSocketConnectOptions } from 'types'; +import { StatusEnum, WebSocketConnectOptions } from 'types'; import webClient from '../../WebClient'; +import { BackendService } from '../../services/BackendService'; +import { ProtoController } from '../../services/ProtoController'; import { SessionPersistence } from '../../persistence'; import { hashPassword } from '../../utils'; -import NormalizeService from '../../utils/NormalizeService'; -import { login, disconnect } from './'; +import { login, disconnect, updateStatus } from './'; export function register(options: WebSocketConnectOptions, passwordSalt?: string): void { const { userName, password, email, country, realName } = options as ServerRegisterParams; - const registerConfig: any = { + const params: any = { ...webClient.clientConfig, userName, email, @@ -20,55 +21,57 @@ export function register(options: WebSocketConnectOptions, passwordSalt?: string }; if (passwordSalt) { - registerConfig.hashedPassword = hashPassword(passwordSalt, password); + params.hashedPassword = hashPassword(passwordSalt, password); } else { - registerConfig.password = password; + params.password = password; } - const command = webClient.protobuf.controller.Command_Register.create(registerConfig); - const sc = webClient.protobuf.controller.SessionCommand.create({ '.Command_Register.ext': command }); - - webClient.protobuf.sendSessionCommand(sc, raw => { - if (raw.responseCode === webClient.protobuf.controller.Response.ResponseCode.RespRegistrationAccepted) { - login(options, passwordSalt); - SessionPersistence.registrationSuccess() - return; - } - - switch (raw.responseCode) { - case webClient.protobuf.controller.Response.ResponseCode.RespRegistrationAcceptedNeedsActivation: - SessionPersistence.accountAwaitingActivation(options); - break; - case webClient.protobuf.controller.Response.ResponseCode.RespUserAlreadyExists: - SessionPersistence.registrationUserNameError('Username is taken'); - break; - case webClient.protobuf.controller.Response.ResponseCode.RespUsernameInvalid: - SessionPersistence.registrationUserNameError('Invalid username'); - break; - case webClient.protobuf.controller.Response.ResponseCode.RespPasswordTooShort: - SessionPersistence.registrationPasswordError('Your password was too short'); - break; - case webClient.protobuf.controller.Response.ResponseCode.RespEmailRequiredToRegister: - SessionPersistence.registrationRequiresEmail(); - break; - case webClient.protobuf.controller.Response.ResponseCode.RespEmailBlackListed: - SessionPersistence.registrationEmailError('This email provider has been blocked'); - break; - case webClient.protobuf.controller.Response.ResponseCode.RespTooManyRequests: - SessionPersistence.registrationEmailError('Max accounts reached for this email'); - break; - case webClient.protobuf.controller.Response.ResponseCode.RespRegistrationDisabled: - SessionPersistence.registrationFailed('Registration is currently disabled'); - break; - case webClient.protobuf.controller.Response.ResponseCode.RespUserIsBanned: - SessionPersistence.registrationFailed(raw.reasonStr, raw.endTime); - break; - case webClient.protobuf.controller.Response.ResponseCode.RespRegistrationFailed: - default: - SessionPersistence.registrationFailed('Registration failed due to a server issue'); - break; - } + const { ResponseCode } = ProtoController.root.Response; + const onRegistrationError = (action: () => void) => { + action(); + updateStatus(StatusEnum.DISCONNECTED, 'Registration failed'); disconnect(); + }; + + BackendService.sendSessionCommand('Command_Register', params, { + onResponseCode: { + [ResponseCode.RespRegistrationAccepted]: () => { + login(options, passwordSalt); + SessionPersistence.registrationSuccess(); + }, + [ResponseCode.RespRegistrationAcceptedNeedsActivation]: () => { + updateStatus(StatusEnum.DISCONNECTED, 'Registration accepted, awaiting activation'); + SessionPersistence.accountAwaitingActivation(options); + disconnect(); + }, + [ResponseCode.RespUserAlreadyExists]: () => onRegistrationError( + () => SessionPersistence.registrationUserNameError('Username is taken') + ), + [ResponseCode.RespUsernameInvalid]: () => onRegistrationError( + () => SessionPersistence.registrationUserNameError('Invalid username') + ), + [ResponseCode.RespPasswordTooShort]: () => onRegistrationError( + () => SessionPersistence.registrationPasswordError('Your password was too short') + ), + [ResponseCode.RespEmailRequiredToRegister]: () => onRegistrationError( + () => SessionPersistence.registrationRequiresEmail() + ), + [ResponseCode.RespEmailBlackListed]: () => onRegistrationError( + () => SessionPersistence.registrationEmailError('This email provider has been blocked') + ), + [ResponseCode.RespTooManyRequests]: () => onRegistrationError( + () => SessionPersistence.registrationEmailError('Max accounts reached for this email') + ), + [ResponseCode.RespRegistrationDisabled]: () => onRegistrationError( + () => SessionPersistence.registrationFailed('Registration is currently disabled') + ), + [ResponseCode.RespUserIsBanned]: (raw) => onRegistrationError( + () => SessionPersistence.registrationFailed(raw.reasonStr, raw.endTime) + ), + }, + onError: () => onRegistrationError( + () => SessionPersistence.registrationFailed('Registration failed due to a server issue') + ), }); } diff --git a/webclient/src/websocket/commands/session/removeFromList.ts b/webclient/src/websocket/commands/session/removeFromList.ts index 222d9c57d..aede49c49 100644 --- a/webclient/src/websocket/commands/session/removeFromList.ts +++ b/webclient/src/websocket/commands/session/removeFromList.ts @@ -1,4 +1,4 @@ -import webClient from '../../WebClient'; +import { BackendService } from '../../services/BackendService'; import { SessionPersistence } from '../../persistence'; export function removeFromBuddyList(userName: string): void { @@ -10,16 +10,9 @@ export function removeFromIgnoreList(userName: string): void { } export function removeFromList(list: string, userName: string): void { - const command = webClient.protobuf.controller.Command_RemoveFromList.create({ list, userName }); - const sc = webClient.protobuf.controller.SessionCommand.create({ '.Command_RemoveFromList.ext': command }); - - webClient.protobuf.sendSessionCommand(sc, ({ responseCode }) => { - switch (responseCode) { - case webClient.protobuf.controller.Response.ResponseCode.RespOk: - SessionPersistence.removeFromList(list, userName); - break; - default: - console.error('Failed to remove from list', responseCode); - } + BackendService.sendSessionCommand('Command_RemoveFromList', { list, userName }, { + onSuccess: () => { + SessionPersistence.removeFromList(list, userName); + }, }); } diff --git a/webclient/src/websocket/commands/session/replayDeleteMatch.ts b/webclient/src/websocket/commands/session/replayDeleteMatch.ts new file mode 100644 index 000000000..24ac48f1c --- /dev/null +++ b/webclient/src/websocket/commands/session/replayDeleteMatch.ts @@ -0,0 +1,10 @@ +import { BackendService } from '../../services/BackendService'; +import { SessionPersistence } from '../../persistence'; + +export function replayDeleteMatch(gameId: number): void { + BackendService.sendSessionCommand('Command_ReplayDeleteMatch', { gameId }, { + onSuccess: () => { + SessionPersistence.replayDeleteMatch(gameId); + }, + }); +} diff --git a/webclient/src/websocket/commands/session/replayList.ts b/webclient/src/websocket/commands/session/replayList.ts new file mode 100644 index 000000000..f39eb279f --- /dev/null +++ b/webclient/src/websocket/commands/session/replayList.ts @@ -0,0 +1,11 @@ +import { BackendService } from '../../services/BackendService'; +import { SessionPersistence } from '../../persistence'; + +export function replayList(): void { + BackendService.sendSessionCommand('Command_ReplayList', {}, { + responseName: 'Response_ReplayList', + onSuccess: (response) => { + SessionPersistence.replayList(response.matchList); + }, + }); +} diff --git a/webclient/src/websocket/commands/session/replayModifyMatch.ts b/webclient/src/websocket/commands/session/replayModifyMatch.ts new file mode 100644 index 000000000..9825047f3 --- /dev/null +++ b/webclient/src/websocket/commands/session/replayModifyMatch.ts @@ -0,0 +1,10 @@ +import { BackendService } from '../../services/BackendService'; +import { SessionPersistence } from '../../persistence'; + +export function replayModifyMatch(gameId: number, doNotHide: boolean): void { + BackendService.sendSessionCommand('Command_ReplayModifyMatch', { gameId, doNotHide }, { + onSuccess: () => { + SessionPersistence.replayModifyMatch(gameId, doNotHide); + }, + }); +} diff --git a/webclient/src/websocket/commands/session/requestPasswordSalt.ts b/webclient/src/websocket/commands/session/requestPasswordSalt.ts index b7ab5ef92..a3d1fc05c 100644 --- a/webclient/src/websocket/commands/session/requestPasswordSalt.ts +++ b/webclient/src/websocket/commands/session/requestPasswordSalt.ts @@ -2,6 +2,8 @@ import { RequestPasswordSaltParams } from 'store'; import { StatusEnum, WebSocketConnectOptions, WebSocketConnectReason } from 'types'; import webClient from '../../WebClient'; +import { BackendService } from '../../services/BackendService'; +import { ProtoController } from '../../services/ProtoController'; import { SessionPersistence } from '../../persistence'; import { @@ -15,64 +17,48 @@ import { export function requestPasswordSalt(options: WebSocketConnectOptions): void { const { userName } = options as RequestPasswordSaltParams; - const registerConfig = { - ...webClient.clientConfig, - userName, - }; - - const command = webClient.protobuf.controller.Command_RequestPasswordSalt.create(registerConfig); - const sc = webClient.protobuf.controller.SessionCommand.create({ '.Command_RequestPasswordSalt.ext': command }); - - webClient.protobuf.sendSessionCommand(sc, raw => { - switch (raw.responseCode) { - case webClient.protobuf.controller.Response.ResponseCode.RespOk: { - const passwordSalt = raw['.Response_PasswordSalt.ext']?.passwordSalt; - - switch (options.reason) { - case WebSocketConnectReason.ACTIVATE_ACCOUNT: { - activate(options, passwordSalt); - break; - } - - case WebSocketConnectReason.PASSWORD_RESET: { - forgotPasswordReset(options, passwordSalt); - break; - } - - case WebSocketConnectReason.LOGIN: - default: { - login(options, passwordSalt); - } - } - - return; - } - case webClient.protobuf.controller.Response.ResponseCode.RespRegistrationRequired: { - updateStatus(StatusEnum.DISCONNECTED, 'Login failed: registration required'); - break; - } - default: { - updateStatus(StatusEnum.DISCONNECTED, 'Login failed: Unknown Reason'); - } - } - + const onFailure = () => { switch (options.reason) { - case WebSocketConnectReason.ACTIVATE_ACCOUNT: { + case WebSocketConnectReason.ACTIVATE_ACCOUNT: SessionPersistence.accountActivationFailed(); break; - } - - case WebSocketConnectReason.PASSWORD_RESET: { + case WebSocketConnectReason.PASSWORD_RESET: SessionPersistence.resetPasswordFailed(); break; - } - - case WebSocketConnectReason.LOGIN: - default: { + default: SessionPersistence.loginFailed(); - } } - disconnect(); + }; + + BackendService.sendSessionCommand('Command_RequestPasswordSalt', { + ...webClient.clientConfig, + userName, + }, { + responseName: 'Response_PasswordSalt', + onSuccess: (resp) => { + const passwordSalt = resp?.passwordSalt; + + switch (options.reason) { + case WebSocketConnectReason.ACTIVATE_ACCOUNT: + activate(options, passwordSalt); + break; + case WebSocketConnectReason.PASSWORD_RESET: + forgotPasswordReset(options, passwordSalt); + break; + default: + login(options, passwordSalt); + } + }, + onResponseCode: { + [ProtoController.root.Response.ResponseCode.RespRegistrationRequired]: () => { + updateStatus(StatusEnum.DISCONNECTED, 'Login failed: registration required'); + onFailure(); + }, + }, + onError: () => { + updateStatus(StatusEnum.DISCONNECTED, 'Login failed: Unknown Reason'); + onFailure(); + }, }); } diff --git a/webclient/src/websocket/events/game/index.ts b/webclient/src/websocket/events/game/index.ts index 895eadfef..a7b3277a5 100644 --- a/webclient/src/websocket/events/game/index.ts +++ b/webclient/src/websocket/events/game/index.ts @@ -4,8 +4,8 @@ import { leaveGame } from './leaveGame'; export const GameEvents: ProtobufEvents = { - '.Event_Join.ext': () => joinGame, - '.Event_Leave.ext': () => leaveGame, + '.Event_Join.ext': joinGame, + '.Event_Leave.ext': leaveGame, '.Event_GameClosed.ext': () => console.log('Event_GameClosed.ext'), '.Event_GameHostChanged.ext': () => console.log('Event_GameHostChanged.ext'), '.Event_Kicked.ext': () => console.log('Event_Kicked.ext'), diff --git a/webclient/src/websocket/events/session/connectionClosed.ts b/webclient/src/websocket/events/session/connectionClosed.ts index 9c39db940..227113059 100644 --- a/webclient/src/websocket/events/session/connectionClosed.ts +++ b/webclient/src/websocket/events/session/connectionClosed.ts @@ -1,5 +1,5 @@ import { StatusEnum } from 'types'; -import webClient from '../../WebClient'; +import { ProtoController } from '../../services/ProtoController'; import { updateStatus } from '../../commands/session'; import { ConnectionClosedData } from './interfaces'; @@ -10,29 +10,30 @@ export function connectionClosed({ reason, reasonStr }: ConnectionClosedData): v if (reasonStr) { message = reasonStr; } else { + const { CloseReason } = ProtoController.root.Event_ConnectionClosed; switch (reason) { - case webClient.protobuf.controller.Event_ConnectionClosed.CloseReason.USER_LIMIT_REACHED: + case CloseReason.USER_LIMIT_REACHED: message = 'The server has reached its maximum user capacity'; break; - case webClient.protobuf.controller.Event_ConnectionClosed.CloseReason.TOO_MANY_CONNECTIONS: + case CloseReason.TOO_MANY_CONNECTIONS: message = 'There are too many concurrent connections from your address'; break; - case webClient.protobuf.controller.Event_ConnectionClosed.CloseReason.BANNED: + case CloseReason.BANNED: message = 'You are banned'; break; - case webClient.protobuf.controller.Event_ConnectionClosed.CloseReason.DEMOTED: + case CloseReason.DEMOTED: message = 'You were demoted'; break; - case webClient.protobuf.controller.Event_ConnectionClosed.CloseReason.SERVER_SHUTDOWN: + case CloseReason.SERVER_SHUTDOWN: message = 'Scheduled server shutdown'; break; - case webClient.protobuf.controller.Event_ConnectionClosed.CloseReason.USERNAMEINVALID: + case CloseReason.USERNAMEINVALID: message = 'Invalid username'; break; - case webClient.protobuf.controller.Event_ConnectionClosed.CloseReason.LOGGEDINELSEWERE: + case CloseReason.LOGGEDINELSEWERE: message = 'You have been logged out due to logging in at another location'; break; - case webClient.protobuf.controller.Event_ConnectionClosed.CloseReason.OTHER: + case CloseReason.OTHER: default: message = 'Unknown reason'; break; diff --git a/webclient/src/websocket/events/session/index.ts b/webclient/src/websocket/events/session/index.ts index 9fe7bb1da..5b3ab198e 100644 --- a/webclient/src/websocket/events/session/index.ts +++ b/webclient/src/websocket/events/session/index.ts @@ -3,8 +3,9 @@ import { addToList } from './addToList'; import { connectionClosed } from './connectionClosed'; import { listRooms } from './listRooms'; import { notifyUser } from './notifyUser'; -import { playerPropertiesChanged } from '../common/playerPropertiesChanged'; import { removeFromList } from './removeFromList'; +import { replayAdded } from './replayAdded'; +import { serverCompleteList } from './serverCompleteList'; import { serverIdentification } from './serverIdentification'; import { serverMessage } from './serverMessage'; import { serverShutdown } from './serverShutdown'; @@ -20,8 +21,8 @@ export const SessionEvents: ProtobufEvents = { '.Event_ListRooms.ext': listRooms, '.Event_NotifyUser.ext': notifyUser, '.Event_RemoveFromList.ext': removeFromList, - '.Event_ReplayAdded.ext': () => console.log('Event_ReplayAdded'), - '.Event_ServerCompleteList.ext': () => console.log('Event_ServerCompleteList'), + '.Event_ReplayAdded.ext': replayAdded, + '.Event_ServerCompleteList.ext': serverCompleteList, '.Event_ServerIdentification.ext': serverIdentification, '.Event_ServerMessage.ext': serverMessage, '.Event_ServerShutdown.ext': serverShutdown, diff --git a/webclient/src/websocket/events/session/interfaces.ts b/webclient/src/websocket/events/session/interfaces.ts index 8a1827cbd..ddc10d103 100644 --- a/webclient/src/websocket/events/session/interfaces.ts +++ b/webclient/src/websocket/events/session/interfaces.ts @@ -1,4 +1,4 @@ -import { Game, NotificationType, Room, User } from 'types'; +import { Game, NotificationType, ReplayMatch, Room, User } from 'types'; export interface AddToListData { listName: string; @@ -13,6 +13,8 @@ export interface ConnectionClosedData { export interface GameJoinedData { gameInfo: Game; + gameTypes: any[]; + hostId: number; playerId: number; spectator: boolean; resuming: boolean; @@ -76,3 +78,13 @@ export interface UserMessageData { receiverName: string; message: string; } + +export interface ReplayAddedData { + matchInfo: ReplayMatch; +} + +export interface ServerCompleteListData { + serverId: number; + userList: User[]; + roomList: Room[]; +} diff --git a/webclient/src/websocket/events/session/replayAdded.ts b/webclient/src/websocket/events/session/replayAdded.ts new file mode 100644 index 000000000..18a4ea82d --- /dev/null +++ b/webclient/src/websocket/events/session/replayAdded.ts @@ -0,0 +1,6 @@ +import { SessionPersistence } from '../../persistence'; +import { ReplayAddedData } from './interfaces'; + +export function replayAdded({ matchInfo }: ReplayAddedData): void { + SessionPersistence.replayAdded(matchInfo); +} diff --git a/webclient/src/websocket/events/session/serverCompleteList.ts b/webclient/src/websocket/events/session/serverCompleteList.ts new file mode 100644 index 000000000..77d37a31e --- /dev/null +++ b/webclient/src/websocket/events/session/serverCompleteList.ts @@ -0,0 +1,7 @@ +import { RoomPersistence, SessionPersistence } from '../../persistence'; +import { ServerCompleteListData } from './interfaces'; + +export function serverCompleteList({ userList, roomList }: ServerCompleteListData): void { + SessionPersistence.updateUsers(userList); + RoomPersistence.updateRooms(roomList); +} diff --git a/webclient/src/websocket/events/session/serverIdentification.ts b/webclient/src/websocket/events/session/serverIdentification.ts index ce89e1f3c..87ae79453 100644 --- a/webclient/src/websocket/events/session/serverIdentification.ts +++ b/webclient/src/websocket/events/session/serverIdentification.ts @@ -1,4 +1,4 @@ -import { StatusEnum, WebSocketConnectReason } from 'types'; +import { StatusEnum, WebSocketConnectOptions, WebSocketConnectReason } from 'types'; import webClient from '../../WebClient'; import { @@ -24,48 +24,48 @@ export function serverIdentification(info: ServerIdentificationData): void { return; } - const getPasswordSalt = passwordSaltSupported(serverOptions, webClient); - const { options } = webClient; + const getPasswordSalt = passwordSaltSupported(serverOptions); + const connectOptions = { ...webClient.options }; - switch (options.reason) { + switch (connectOptions.reason) { case WebSocketConnectReason.LOGIN: updateStatus(StatusEnum.LOGGING_IN, 'Logging In...'); if (getPasswordSalt) { - requestPasswordSalt(options); + requestPasswordSalt(connectOptions); } else { - login(options); + login(connectOptions); } break; case WebSocketConnectReason.REGISTER: const passwordSalt = getPasswordSalt ? generateSalt() : null; - register(options, passwordSalt); + register(connectOptions, passwordSalt); break; case WebSocketConnectReason.ACTIVATE_ACCOUNT: if (getPasswordSalt) { - requestPasswordSalt(options); + requestPasswordSalt(connectOptions); } else { - activate(options); + activate(connectOptions); } break; case WebSocketConnectReason.PASSWORD_RESET_REQUEST: - forgotPasswordRequest(options); + forgotPasswordRequest(connectOptions); break; case WebSocketConnectReason.PASSWORD_RESET_CHALLENGE: - forgotPasswordChallenge(options); + forgotPasswordChallenge(connectOptions); break; case WebSocketConnectReason.PASSWORD_RESET: if (getPasswordSalt) { - requestPasswordSalt(options); + requestPasswordSalt(connectOptions); } else { - forgotPasswordReset(options); + forgotPasswordReset(connectOptions); } break; default: - updateStatus(StatusEnum.DISCONNECTED, 'Unknown Connection Reason: ' + options.reason); + updateStatus(StatusEnum.DISCONNECTED, 'Unknown Connection Reason: ' + connectOptions.reason); disconnect(); break; } - webClient.options = {}; + webClient.options = {} as WebSocketConnectOptions; SessionPersistence.updateInfo(serverName, serverVersion); } diff --git a/webclient/src/websocket/persistence/AdminPresistence.ts b/webclient/src/websocket/persistence/AdminPersistence.ts similarity index 100% rename from webclient/src/websocket/persistence/AdminPresistence.ts rename to webclient/src/websocket/persistence/AdminPersistence.ts diff --git a/webclient/src/websocket/persistence/ModeratorPresistence.ts b/webclient/src/websocket/persistence/ModeratorPersistence.ts similarity index 61% rename from webclient/src/websocket/persistence/ModeratorPresistence.ts rename to webclient/src/websocket/persistence/ModeratorPersistence.ts index e6a57f601..d1b991fa0 100644 --- a/webclient/src/websocket/persistence/ModeratorPresistence.ts +++ b/webclient/src/websocket/persistence/ModeratorPersistence.ts @@ -27,4 +27,20 @@ export class ModeratorPersistence { static warnUser(userName: string): void { ServerDispatch.warnUser(userName); } + + static grantReplayAccess(replayId: number, moderatorName: string): void { + ServerDispatch.grantReplayAccess(replayId, moderatorName); + } + + static forceActivateUser(usernameToActivate: string, moderatorName: string): void { + ServerDispatch.forceActivateUser(usernameToActivate, moderatorName); + } + + static getAdminNotes(userName: string, notes: string): void { + ServerDispatch.getAdminNotes(userName, notes); + } + + static updateAdminNotes(userName: string, notes: string): void { + ServerDispatch.updateAdminNotes(userName, notes); + } } diff --git a/webclient/src/websocket/persistence/SessionPersistence.ts b/webclient/src/websocket/persistence/SessionPersistence.ts index c9248745c..9962af968 100644 --- a/webclient/src/websocket/persistence/SessionPersistence.ts +++ b/webclient/src/websocket/persistence/SessionPersistence.ts @@ -1,5 +1,5 @@ import { ServerDispatch } from 'store'; -import { DeckStorageTreeItem, StatusEnum, User, WebSocketConnectOptions } from 'types'; +import { DeckList, DeckStorageTreeItem, ReplayMatch, StatusEnum, User, WebSocketConnectOptions } from 'types'; import { sanitizeHtml } from 'websocket/utils'; import { @@ -10,9 +10,6 @@ import { UserMessageData } from '../events/session/interfaces'; import NormalizeService from '../utils/NormalizeService'; -import { DeckList } from 'types'; -import { common } from 'protobufjs'; -import IBytesValue = common.IBytesValue; export class SessionPersistence { static initialized() { @@ -165,7 +162,7 @@ export class SessionPersistence { ServerDispatch.accountEditChanged({ realName, email, country }); } - static accountImageChanged(avatarBmp: IBytesValue): void { + static accountImageChanged(avatarBmp: Uint8Array): void { ServerDispatch.accountImageChanged({ avatarBmp }); } @@ -178,7 +175,8 @@ export class SessionPersistence { } static getGamesOfUser(userName: string, response: any): void { - console.log('getGamesOfUser'); + // Response_GetGamesOfUser contains a gameList field — log for now until game layer is complete + console.log('getGamesOfUser', userName, response); } static gameJoined(gameJoinedData: GameJoinedData): void { @@ -209,28 +207,40 @@ export class SessionPersistence { ServerDispatch.removeFromList(list, userName); } - static deckDelete(deckId: number): void { - console.log('deckDelete', deckId); + static deleteServerDeck(deckId: number): void { + ServerDispatch.deckDelete(deckId); } - static deckDeleteDir(path: string): void { - console.log('deckDeleteDir', path); + static updateServerDecks(deckList: DeckList): void { + ServerDispatch.backendDecks(deckList); } - static deckDownload(deckId: number): void { - console.log('deckDownload', deckId); + static uploadServerDeck(path: string, treeItem: DeckStorageTreeItem): void { + ServerDispatch.deckUpload(path, treeItem); } - static deckList(deckList: DeckList): void { - console.log('deckList', deckList); + static createServerDeckDir(path: string, dirName: string): void { + ServerDispatch.deckNewDir(path, dirName); } - static deckNewDir(path: string, dirName: string): void { - console.log('deckNewDir', path, dirName); + static deleteServerDeckDir(path: string): void { + ServerDispatch.deckDelDir(path); } - static deckUpload(treeItem: DeckStorageTreeItem): void { - console.log('deckUpload', treeItem); + static replayList(matchList: ReplayMatch[]): void { + ServerDispatch.replayList(matchList); + } + + static replayAdded(matchInfo: ReplayMatch): void { + ServerDispatch.replayAdded(matchInfo); + } + + static replayModifyMatch(gameId: number, doNotHide: boolean): void { + ServerDispatch.replayModifyMatch(gameId, doNotHide); + } + + static replayDeleteMatch(gameId: number): void { + ServerDispatch.replayDeleteMatch(gameId); } } diff --git a/webclient/src/websocket/persistence/index.ts b/webclient/src/websocket/persistence/index.ts index f4df52645..a1e34fffa 100644 --- a/webclient/src/websocket/persistence/index.ts +++ b/webclient/src/websocket/persistence/index.ts @@ -1,5 +1,5 @@ -export { AdminPersistence } from './AdminPresistence'; +export { AdminPersistence } from './AdminPersistence'; export { RoomPersistence } from './RoomPersistence'; export { SessionPersistence } from './SessionPersistence'; -export { ModeratorPersistence } from './ModeratorPresistence'; +export { ModeratorPersistence } from './ModeratorPersistence'; export { GamePersistence } from './GamePersistence'; diff --git a/webclient/src/websocket/services/BackendService.ts b/webclient/src/websocket/services/BackendService.ts new file mode 100644 index 000000000..fce741d35 --- /dev/null +++ b/webclient/src/websocket/services/BackendService.ts @@ -0,0 +1,82 @@ +import webClient from '../WebClient'; +import { ProtoController } from './ProtoController'; + +export interface CommandOptions { + responseName?: string; + onSuccess?: (response: any, raw: any) => void; + onError?: (responseCode: number, raw: any) => void; + onResponseCode?: { [code: number]: (raw: any) => void }; + onResponse?: (raw: any) => void; +} + +export class BackendService { + static sendSessionCommand(commandName: string, params: any, options: CommandOptions): void { + const command = ProtoController.root[commandName].create(params || {}); + const sc = ProtoController.root.SessionCommand.create({ + [`.${commandName}.ext`]: command, + }); + webClient.protobuf.sendSessionCommand(sc, raw => { + BackendService.handleResponse(commandName, raw, options); + }); + } + + static sendRoomCommand(roomId: number, commandName: string, params: any, options: CommandOptions): void { + const command = ProtoController.root[commandName].create(params || {}); + const rc = ProtoController.root.RoomCommand.create({ + [`.${commandName}.ext`]: command, + }); + webClient.protobuf.sendRoomCommand(roomId, rc, raw => { + BackendService.handleResponse(commandName, raw, options); + }); + } + + static sendModeratorCommand(commandName: string, params: any, options: CommandOptions): void { + const command = ProtoController.root[commandName].create(params || {}); + const mc = ProtoController.root.ModeratorCommand.create({ + [`.${commandName}.ext`]: command, + }); + webClient.protobuf.sendModeratorCommand(mc, raw => { + BackendService.handleResponse(commandName, raw, options); + }); + } + + static sendAdminCommand(commandName: string, params: any, options: CommandOptions): void { + const command = ProtoController.root[commandName].create(params || {}); + const ac = ProtoController.root.AdminCommand.create({ + [`.${commandName}.ext`]: command, + }); + webClient.protobuf.sendAdminCommand(ac, raw => { + BackendService.handleResponse(commandName, raw, options); + }); + } + + private static handleResponse(commandName: string, raw: any, options: CommandOptions): void { + if (options.onResponse) { + options.onResponse(raw); + return; + } + + const { responseCode } = raw; + + if (responseCode === ProtoController.root.Response.ResponseCode.RespOk) { + if (options.onSuccess) { + const response = options.responseName + ? raw[`.${options.responseName}.ext`] + : raw; + options.onSuccess(response, raw); + } + return; + } + + if (options.onResponseCode?.[responseCode]) { + options.onResponseCode[responseCode](raw); + return; + } + + if (options.onError) { + options.onError(responseCode, raw); + } else { + console.error(`${commandName} failed with response code: ${responseCode}`); + } + } +} diff --git a/webclient/src/websocket/services/ProtoController.ts b/webclient/src/websocket/services/ProtoController.ts new file mode 100644 index 000000000..130d5e196 --- /dev/null +++ b/webclient/src/websocket/services/ProtoController.ts @@ -0,0 +1,24 @@ +import protobuf from 'protobufjs'; + +import { SessionPersistence } from '../persistence'; +import ProtoFiles from '../../proto-files.json'; + +const PB_FILE_DIR = `${process.env.PUBLIC_URL}/pb`; + +// Leaf module — no imports from the websocket layer other than persistence. +// Both BackendService and ProtobufService import this; neither should import +// the other for controller access, avoiding circular dependency cycles. +export const ProtoController = { + root: null as any, + + load(): void { + const files = ProtoFiles.map(file => `${PB_FILE_DIR}/${file}`); + ProtoController.root = new protobuf.Root(); + ProtoController.root.load(files, { keepCase: false }, (err: Error) => { + if (err) { + throw err; + } + SessionPersistence.initialized(); + }); + }, +}; diff --git a/webclient/src/websocket/services/ProtobufService.ts b/webclient/src/websocket/services/ProtobufService.ts index 341bbeae3..20c4e8599 100644 --- a/webclient/src/websocket/services/ProtobufService.ts +++ b/webclient/src/websocket/services/ProtobufService.ts @@ -1,19 +1,13 @@ -import protobuf from 'protobufjs'; - import { CommonEvents, GameEvents, RoomEvents, SessionEvents } from '../events'; -import { SessionPersistence } from '../persistence'; import { WebClient } from '../WebClient'; import { SessionCommands } from 'websocket'; -import ProtoFiles from '../../proto-files.json'; +import { ProtoController } from './ProtoController'; 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 } = {}; @@ -21,8 +15,7 @@ export class ProtobufService { constructor(webClient: WebClient) { this.webClient = webClient; - - this.loadProtobufFiles(); + ProtoController.load(); } public resetCommands() { @@ -30,8 +23,8 @@ export class ProtobufService { this.pendingCommands = {}; } - public sendRoomCommand(roomId: number, roomCmd: number, callback?: Function) { - const cmd = this.controller.CommandContainer.create({ + public sendRoomCommand(roomId: number, roomCmd: any, callback?: Function) { + const cmd = ProtoController.root.CommandContainer.create({ 'roomId': roomId, 'roomCommand': [roomCmd] }); @@ -39,38 +32,38 @@ export class ProtobufService { this.sendCommand(cmd, raw => callback && callback(raw)); } - public sendSessionCommand(sesCmd: number, callback?: Function) { - const cmd = this.controller.CommandContainer.create({ + public sendSessionCommand(sesCmd: any, callback?: Function) { + const cmd = ProtoController.root.CommandContainer.create({ 'sessionCommand': [sesCmd] }); this.sendCommand(cmd, (raw) => callback && callback(raw)); } - public sendModeratorCommand(modCmd: number, callback?: Function) { - const cmd = this.controller.CommandContainer.create({ + public sendModeratorCommand(modCmd: any, callback?: Function) { + const cmd = ProtoController.root.CommandContainer.create({ 'moderatorCommand': [modCmd] }); this.sendCommand(cmd, (raw) => callback && callback(raw)); } - public sendAdminCommand(adminCmd: number, callback?: Function) { - const cmd = this.controller.CommandContainer.create({ + public sendAdminCommand(adminCmd: any, callback?: Function) { + const cmd = ProtoController.root.CommandContainer.create({ 'adminCommand': [adminCmd] }); this.sendCommand(cmd, (raw) => callback && callback(raw)); } - public sendCommand(cmd: number, callback: Function) { + public sendCommand(cmd: any, 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()); + this.webClient.socket.send(ProtoController.root.CommandContainer.encode(cmd).finish()); } } @@ -81,20 +74,20 @@ export class ProtobufService { public handleMessageEvent({ data }: MessageEvent): void { try { const uint8msg = new Uint8Array(data); - const msg = this.controller.ServerMessage.decode(uint8msg); + const msg = ProtoController.root.ServerMessage.decode(uint8msg); if (msg) { switch (msg.messageType) { - case this.controller.ServerMessage.MessageType.RESPONSE: + case ProtoController.root.ServerMessage.MessageType.RESPONSE: this.processServerResponse(msg.response); break; - case this.controller.ServerMessage.MessageType.ROOM_EVENT: + case ProtoController.root.ServerMessage.MessageType.ROOM_EVENT: this.processRoomEvent(msg.roomEvent, msg); break; - case this.controller.ServerMessage.MessageType.SESSION_EVENT: + case ProtoController.root.ServerMessage.MessageType.SESSION_EVENT: this.processSessionEvent(msg.sessionEvent, msg); break; - case this.controller.ServerMessage.MessageType.GAME_EVENT_CONTAINER: + case ProtoController.root.ServerMessage.MessageType.GAME_EVENT_CONTAINER: this.processGameEvent(msg.gameEvent, msg); break; default: @@ -142,17 +135,4 @@ export class ProtobufService { } } } - - 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; - } - - SessionPersistence.initialized(); - }); - } } diff --git a/webclient/src/websocket/utils/passwordHasher.ts b/webclient/src/websocket/utils/passwordHasher.ts index 77645694f..164a91823 100644 --- a/webclient/src/websocket/utils/passwordHasher.ts +++ b/webclient/src/websocket/utils/passwordHasher.ts @@ -1,5 +1,6 @@ import sha512 from 'crypto-js/sha512'; import Base64 from 'crypto-js/enc-base64'; +import { ProtoController } from '../services/ProtoController'; const HASH_ROUNDS = 1_000; const SALT_LENGTH = 16; @@ -25,7 +26,7 @@ export const generateSalt = (): string => { return salt; } -export const passwordSaltSupported = (serverOptions, webClient): number => { +export const passwordSaltSupported = (serverOptions: number): number => { // Intentional use of Bitwise operator b/c of how Servatrice Enums work - return serverOptions & webClient.protobuf.controller.Event_ServerIdentification.ServerOptions.SupportsPasswordHash; + return serverOptions & ProtoController.root.Event_ServerIdentification.ServerOptions.SupportsPasswordHash; }