From f857e242426606f569ea310071306bcf7e5fa441 Mon Sep 17 00:00:00 2001 From: trippyone <137233897+trippyone@users.noreply.github.com> Date: Sat, 6 Jul 2024 16:39:55 -0400 Subject: [PATCH 01/21] Ported code from 3.8.3 to 3.9 --- src/callbacks/FikaRaidCallbacks.ts | 14 +++ src/controllers/FikaRaidController.ts | 96 ++++++++++++++++++- src/di/Container.ts | 3 + src/models/fika/IDedicatedClientInfo.ts | 4 + .../raid/dedicated/IStartDedicatedRequest.ts | 16 ++++ .../raid/dedicated/IStartDedicatedResponse.ts | 4 + .../raid/dedicated/IStatusDedicatedRequest.ts | 4 + .../dedicated/IStatusDedicatedResponse.ts | 4 + src/routers/static/FikaRaidStaticRouter.ts | 8 ++ src/services/FikaDedicatedRaidService.ts | 91 ++++++++++++++++++ src/services/FikaMatchService.ts | 6 ++ 11 files changed, 249 insertions(+), 1 deletion(-) create mode 100644 src/models/fika/IDedicatedClientInfo.ts create mode 100644 src/models/fika/routes/raid/dedicated/IStartDedicatedRequest.ts create mode 100644 src/models/fika/routes/raid/dedicated/IStartDedicatedResponse.ts create mode 100644 src/models/fika/routes/raid/dedicated/IStatusDedicatedRequest.ts create mode 100644 src/models/fika/routes/raid/dedicated/IStatusDedicatedResponse.ts create mode 100644 src/services/FikaDedicatedRaidService.ts diff --git a/src/callbacks/FikaRaidCallbacks.ts b/src/callbacks/FikaRaidCallbacks.ts index 01ae1a29..b2b862b8 100644 --- a/src/callbacks/FikaRaidCallbacks.ts +++ b/src/callbacks/FikaRaidCallbacks.ts @@ -8,6 +8,10 @@ import { IFikaRaidServerIdRequestData } from "../models/fika/routes/raid/IFikaRa import { IFikaRaidCreateRequestData } from "../models/fika/routes/raid/create/IFikaRaidCreateRequestData"; import { IFikaRaidJoinRequestData } from "../models/fika/routes/raid/join/IFikaRaidJoinRequestData"; import { IFikaRaidLeaveRequestData } from "../models/fika/routes/raid/leave/IFikaRaidLeaveRequestData"; +import { IStartDedicatedResponse } from "../models/fika/routes/raid/dedicated/IStartDedicatedResponse"; +import { IStartDedicatedRequest } from "../models/fika/routes/raid/dedicated/IStartDedicatedRequest"; +import { IStatusDedicatedRequest } from "../models/fika/routes/raid/dedicated/IStatusDedicatedRequest"; +import { IStatusDedicatedResponse } from "../models/fika/routes/raid/dedicated/IStatusDedicatedResponse"; @injectable() export class FikaRaidCallbacks { @@ -49,4 +53,14 @@ export class FikaRaidCallbacks { public handleRaidGetSettings(_url: string, info: IFikaRaidServerIdRequestData, _sessionID: string): string { return this.httpResponseUtil.noBody(this.fikaRaidController.handleRaidGetSettings(info)); } + + /** Handle /fika/raid/dedicated/start */ + public handleRaidStartDedicated(_url: string, info: IStartDedicatedRequest, sessionID: string): string { + return this.httpResponseUtil.noBody(this.fikaRaidController.handleRaidStartDedicated(sessionID, info)); + } + + /** Handle /fika/raid/dedicated/status */ + public handleRaidStatusDedicated(url: string, info: IStatusDedicatedRequest, sessionID: string): string { + return this.httpResponseUtil.noBody(this.fikaRaidController.handleRaidStatusDedicated(sessionID, info)); + } } diff --git a/src/controllers/FikaRaidController.ts b/src/controllers/FikaRaidController.ts index dcd9c19e..f24f0176 100644 --- a/src/controllers/FikaRaidController.ts +++ b/src/controllers/FikaRaidController.ts @@ -11,10 +11,21 @@ import { IFikaRaidJoinResponse } from "../models/fika/routes/raid/join/IFikaRaid import { IFikaRaidLeaveRequestData } from "../models/fika/routes/raid/leave/IFikaRaidLeaveRequestData"; import { IFikaRaidSpawnpointResponse } from "../models/fika/routes/raid/spawnpoint/IFikaRaidSpawnpointResponse"; import { FikaMatchService } from "../services/FikaMatchService"; +import { FikaDedicatedRaidService } from "../services/FikaDedicatedRaidService"; +import { IStartDedicatedRequest } from "../models/fika/routes/raid/dedicated/IStartDedicatedRequest"; +import { IStartDedicatedResponse } from "../models/fika/routes/raid/dedicated/IStartDedicatedResponse"; +import { WebSocket } from "ws"; +import { ILogger } from "@spt/models/spt/utils/ILogger"; +import { IStatusDedicatedRequest } from "../models/fika/routes/raid/dedicated/IStatusDedicatedRequest"; +import { IStatusDedicatedResponse } from "../models/fika/routes/raid/dedicated/IStatusDedicatedResponse"; @injectable() export class FikaRaidController { - constructor(@inject("FikaMatchService") protected fikaMatchService: FikaMatchService) { + constructor( + @inject("FikaMatchService") protected fikaMatchService: FikaMatchService, + @inject("FikaDedicatedRaidService") protected fikaDedicatedRaidService: FikaDedicatedRaidService, + @inject("WinstonLogger") protected logger: ILogger, + ) { // empty } @@ -107,4 +118,87 @@ export class FikaRaidController { playersSpawnPlace: match.raidConfig.playersSpawnPlace }; } + + /** Handle /fika/raid/dedicated/start */ + handleRaidStartDedicated(sessionID: string, info: IStartDedicatedRequest): IStartDedicatedResponse { + if (!this.fikaDedicatedRaidService.isDedicatedClientAvailable()) { + return { + matchId: null, + error: "No dedicated clients available." + }; + } + + if (sessionID in this.fikaDedicatedRaidService.dedicatedClients) { + return { + matchId: null, + error: "A dedicated client is trying to use a dedicated client?" + }; + } + + let dedicatedClient: string | undefined = undefined; + let dedicatedClientWs: WebSocket | undefined = undefined; + + for (const dedicatedSessionId in this.fikaDedicatedRaidService.dedicatedClients) { + const dedicatedClientInfo = this.fikaDedicatedRaidService.dedicatedClients[dedicatedSessionId]; + + if (dedicatedClientInfo.state != "ready") { + continue; + } + + dedicatedClientWs = this.fikaDedicatedRaidService.clientWebSockets[dedicatedSessionId]; + + if(!dedicatedClientWs) { + continue; + } + + dedicatedClient = dedicatedSessionId; + break; + } + + if (!dedicatedClient) { + return { + matchId: null, + error: "No dedicated clients available at this time" + }; + } + + this.fikaDedicatedRaidService.requestedSessions[dedicatedClient] = sessionID; + + dedicatedClientWs.send( + JSON.stringify( + { + type: "fikaDedicatedStartRaid", + ...info + } + )); + + this.logger.info(`Sent WS to ${dedicatedClient}`); + + return { + // This really isn't required, I just want to make sure on the client + matchId: dedicatedClient, + error: null + } + } + + /** Handle /fika/raid/dedicated/status */ + public handleRaidStatusDedicated(sessionId: string, info: IStatusDedicatedRequest): IStatusDedicatedResponse { + + if(info.status == "ready" && !this.fikaDedicatedRaidService.isDedicatedClientAvailable()) { + if(this.fikaDedicatedRaidService.onDedicatedClientAvailable) { + this.fikaDedicatedRaidService.onDedicatedClientAvailable(); + } + } + + this.fikaDedicatedRaidService.dedicatedClients[sessionId] = + { + state: info.status, + lastPing: Date.now() + } + + return { + sessionId: info.sessionId, + status: info.status + } + } } diff --git a/src/di/Container.ts b/src/di/Container.ts index 181ad818..001851cd 100644 --- a/src/di/Container.ts +++ b/src/di/Container.ts @@ -42,6 +42,7 @@ import { FikaItemEventRouter } from "../routers/item_events/FikaItemEventRouter" import { Fika } from "../Fika"; import { FikaServerTools } from "../utils/FikaServerTools"; +import { FikaDedicatedRaidService } from "../services/FikaDedicatedRaidService"; export class Container { public static register(container: DependencyContainer): void { @@ -80,6 +81,7 @@ export class Container { container.registerType("StaticRoutes", "FikaUpdateStaticRouter"); container.registerType("IERouters", "FikaItemEventRouter"); + container.registerType("WebSocketConnectionHandler", "FikaDedicatedRaidService"); } private static registerUtils(container: DependencyContainer): void { @@ -102,6 +104,7 @@ export class Container { container.register("FikaMatchService", FikaMatchService, { lifecycle: Lifecycle.Singleton }); container.register("FikaFriendRequestsCacheService", FikaFriendRequestsCacheService, { lifecycle: Lifecycle.Singleton }); container.register("FikaPlayerRelationsCacheService", FikaPlayerRelationsCacheService, { lifecycle: Lifecycle.Singleton }); + container.register("FikaDedicatedRaidService", FikaDedicatedRaidService, { lifecycle: Lifecycle.Singleton }); } private static registerHelpers(container: DependencyContainer): void { diff --git a/src/models/fika/IDedicatedClientInfo.ts b/src/models/fika/IDedicatedClientInfo.ts new file mode 100644 index 00000000..522bbbb7 --- /dev/null +++ b/src/models/fika/IDedicatedClientInfo.ts @@ -0,0 +1,4 @@ +export interface IDedicatedClientInfo { + state: string; + lastPing: number; +} diff --git a/src/models/fika/routes/raid/dedicated/IStartDedicatedRequest.ts b/src/models/fika/routes/raid/dedicated/IStartDedicatedRequest.ts new file mode 100644 index 00000000..63f80713 --- /dev/null +++ b/src/models/fika/routes/raid/dedicated/IStartDedicatedRequest.ts @@ -0,0 +1,16 @@ +import { BotSettings, TimeAndWeatherSettings, WavesSettings } from "@spt-aki/models/eft/match/IRaidSettings"; +import { DateTime } from "@spt-aki/models/enums/DateTime"; +import { PlayersSpawnPlace } from "@spt-aki/models/enums/PlayersSpawnPlace"; +import { SideType } from "@spt-aki/models/enums/SideType"; + +export interface IStartDedicatedRequest { + expectedNumberOfPlayers: number; + time: DateTime; + locationId: string; + spawnPlace: PlayersSpawnPlace; + metabolismDisabled: boolean; + timeAndWeatherSettings: TimeAndWeatherSettings; + botSettings: BotSettings; + wavesSettings: WavesSettings; + side: SideType; +} diff --git a/src/models/fika/routes/raid/dedicated/IStartDedicatedResponse.ts b/src/models/fika/routes/raid/dedicated/IStartDedicatedResponse.ts new file mode 100644 index 00000000..3645ead3 --- /dev/null +++ b/src/models/fika/routes/raid/dedicated/IStartDedicatedResponse.ts @@ -0,0 +1,4 @@ +export interface IStartDedicatedResponse { + matchId: string; + error: string; +} diff --git a/src/models/fika/routes/raid/dedicated/IStatusDedicatedRequest.ts b/src/models/fika/routes/raid/dedicated/IStatusDedicatedRequest.ts new file mode 100644 index 00000000..856a7643 --- /dev/null +++ b/src/models/fika/routes/raid/dedicated/IStatusDedicatedRequest.ts @@ -0,0 +1,4 @@ +export interface IStatusDedicatedRequest { + sessionId: string; + status: string; +} diff --git a/src/models/fika/routes/raid/dedicated/IStatusDedicatedResponse.ts b/src/models/fika/routes/raid/dedicated/IStatusDedicatedResponse.ts new file mode 100644 index 00000000..22f4ee4a --- /dev/null +++ b/src/models/fika/routes/raid/dedicated/IStatusDedicatedResponse.ts @@ -0,0 +1,4 @@ +export interface IStatusDedicatedResponse { + sessionId: string; + status: string; +} diff --git a/src/routers/static/FikaRaidStaticRouter.ts b/src/routers/static/FikaRaidStaticRouter.ts index 3a7e16ac..7b98c781 100644 --- a/src/routers/static/FikaRaidStaticRouter.ts +++ b/src/routers/static/FikaRaidStaticRouter.ts @@ -8,6 +8,8 @@ import { IFikaRaidServerIdRequestData } from "../../models/fika/routes/raid/IFik import { IFikaRaidCreateRequestData } from "../../models/fika/routes/raid/create/IFikaRaidCreateRequestData"; import { IFikaRaidJoinRequestData } from "../../models/fika/routes/raid/join/IFikaRaidJoinRequestData"; import { IFikaRaidLeaveRequestData } from "../../models/fika/routes/raid/leave/IFikaRaidLeaveRequestData"; +import { IStartDedicatedRequest } from "../../models/fika/routes/raid/dedicated/IStartDedicatedRequest"; +import { IStatusDedicatedRequest } from "../../models/fika/routes/raid/dedicated/IStatusDedicatedRequest"; @injectable() export class FikaRaidStaticRouter extends StaticRouter { @@ -31,6 +33,12 @@ export class FikaRaidStaticRouter extends StaticRouter { new RouteAction("/fika/raid/getsettings", async (url: string, info: IFikaRaidServerIdRequestData, sessionID: string, _output: string): Promise => { return this.fikaRaidCallbacks.handleRaidGetSettings(url, info, sessionID); }), + new RouteAction("/fika/raid/dedicated/start", async (url: string, info: IStartDedicatedRequest, sessionID: string, _output: string): Promise => { + return this.fikaRaidCallbacks.handleRaidStartDedicated(url, info, sessionID); + }), + new RouteAction("/fika/raid/dedicated/status", async (url: string, info: IStatusDedicatedRequest, sessionID: string, _output: string): Promise => { + return this.fikaRaidCallbacks.handleRaidStatusDedicated(url, info, sessionID); + }), ]); } } diff --git a/src/services/FikaDedicatedRaidService.ts b/src/services/FikaDedicatedRaidService.ts new file mode 100644 index 00000000..6f5e279f --- /dev/null +++ b/src/services/FikaDedicatedRaidService.ts @@ -0,0 +1,91 @@ +import { ILogger } from "@spt/models/spt/utils/ILogger"; +import { inject, injectable } from "tsyringe"; +import { IDedicatedClientInfo } from "../models/fika/IDedicatedClientInfo"; +import { WebSocketServer } from "@spt/servers/WebSocketServer"; +import { IWebSocketConnectionHandler } from "@spt/servers/ws/IWebSocketConnectionHandler"; +import { IncomingMessage } from "http"; +import { WebSocket } from "ws"; + +@injectable() +export class FikaDedicatedRaidService implements IWebSocketConnectionHandler{ + public clientWebSockets: Record; + public dedicatedClients: Record; + public requestedSessions: Record; + public onNoDedicatedClientAvailable?: () => void; + public onDedicatedClientAvailable?: () => void; + public onDedicatedClientResponse?: (sessionID: string) => void; + + constructor( + @inject("WinstonLogger") protected logger: ILogger, + @inject("WebSocketServer") protected webSocketServer: WebSocketServer, + ) { + this.clientWebSockets = {}; + this.dedicatedClients = {}; + this.requestedSessions = {}; + + // TODO: find a more elegant solution to keep track of dedicated clients being available. + setInterval(() => { + const currentTime = Date.now(); + + for (const dedicatedClientSessionId in this.dedicatedClients) { + const dedicatedClientLastPing = this.dedicatedClients[dedicatedClientSessionId].lastPing; + + if (currentTime - dedicatedClientLastPing > 16000) { + delete this.dedicatedClients[dedicatedClientSessionId]; + logger.info(`Dedicated client removed: ${dedicatedClientSessionId}`); + } + + if(!this.isDedicatedClientAvailable()) { + if(this.onNoDedicatedClientAvailable) { + this.onNoDedicatedClientAvailable(); + } + } + } + }, 5000); + } + + public getSocketId(): string + { + return "FikaDedicatedRaidService"; + } + + public getHookUrl(): string + { + return "/fika/dedicatedraidservice/"; + } + + public onConnection(ws: WebSocket, req: IncomingMessage): void + { + // Strip request and break it into sections + const splitUrl = req.url.substring(0, req.url.indexOf("?")).split("/"); + const sessionID = splitUrl.pop(); + + this.clientWebSockets[sessionID] = ws; + + ws.on("message", (msg) => this.onMessage(sessionID, msg.toString())); + } + + public onMessage(sessionID: string, msg: string) { + // Do Nothing + } + + public handleRequestedSessions(matchId: string): void { + if (matchId in this.requestedSessions) { + const userToJoin = this.requestedSessions[matchId]; + delete this.requestedSessions[matchId]; + + this.clientWebSockets[userToJoin].send(JSON.stringify( + { + type: "fikaDedicatedJoinMatch", + matchId: matchId + } + )); + + this.logger.info(`Told ${userToJoin} to join raid ${matchId}`); + } + } + + public isDedicatedClientAvailable(): boolean { + return Object.keys(this.dedicatedClients).length > 0; + } +} diff --git a/src/services/FikaMatchService.ts b/src/services/FikaMatchService.ts index 605d9668..b101ec03 100644 --- a/src/services/FikaMatchService.ts +++ b/src/services/FikaMatchService.ts @@ -11,6 +11,7 @@ import { IFikaPlayer } from "../models/fika/IFikaPlayer"; import { IFikaRaidCreateRequestData } from "../models/fika/routes/raid/create/IFikaRaidCreateRequestData"; import { FikaConfig } from "../utils/FikaConfig"; +import { FikaDedicatedRaidService } from "./FikaDedicatedRaidService"; @injectable() export class FikaMatchService { @@ -22,6 +23,7 @@ export class FikaMatchService { @inject("LocationController") protected locationController: LocationController, @inject("SaveServer") protected saveServer: SaveServer, @inject("FikaConfig") protected fikaConfig: FikaConfig, + @inject("FikaDedicatedRaidService") protected fikaDedicatedRaidService: FikaDedicatedRaidService, ) { this.matches = new Map(); this.timeoutIntervals = new Map(); @@ -248,6 +250,10 @@ export class FikaMatchService { } this.matches.get(matchId).status = status; + + if (status.toString() == "COMPLETE") { + this.fikaDedicatedRaidService.handleRequestedSessions(matchId); + } } /** From 9d05ea191979e4e126de3a2b997c85003054611c Mon Sep 17 00:00:00 2001 From: trippyone <137233897+trippyone@users.noreply.github.com> Date: Sun, 7 Jul 2024 16:13:15 -0400 Subject: [PATCH 02/21] Added custom websocket handle + fixes --- src/controllers/FikaRaidController.ts | 4 +- src/di/Container.ts | 16 ++++++-- src/services/FikaDedicatedRaidService.ts | 38 ++---------------- src/services/FikaDedicatedRaidWebSocket.ts | 46 ++++++++++++++++++++++ 4 files changed, 66 insertions(+), 38 deletions(-) create mode 100644 src/services/FikaDedicatedRaidWebSocket.ts diff --git a/src/controllers/FikaRaidController.ts b/src/controllers/FikaRaidController.ts index f24f0176..a2bb29e5 100644 --- a/src/controllers/FikaRaidController.ts +++ b/src/controllers/FikaRaidController.ts @@ -18,12 +18,14 @@ import { WebSocket } from "ws"; import { ILogger } from "@spt/models/spt/utils/ILogger"; import { IStatusDedicatedRequest } from "../models/fika/routes/raid/dedicated/IStatusDedicatedRequest"; import { IStatusDedicatedResponse } from "../models/fika/routes/raid/dedicated/IStatusDedicatedResponse"; +import { FikaDedicatedRaidWebSocket } from "../services/FikaDedicatedRaidWebSocket"; @injectable() export class FikaRaidController { constructor( @inject("FikaMatchService") protected fikaMatchService: FikaMatchService, @inject("FikaDedicatedRaidService") protected fikaDedicatedRaidService: FikaDedicatedRaidService, + @inject("FikaDedicatedRaidWebSocket") protected fikaDedicatedRaidWebSocket: FikaDedicatedRaidWebSocket, @inject("WinstonLogger") protected logger: ILogger, ) { // empty @@ -145,7 +147,7 @@ export class FikaRaidController { continue; } - dedicatedClientWs = this.fikaDedicatedRaidService.clientWebSockets[dedicatedSessionId]; + dedicatedClientWs = this.fikaDedicatedRaidWebSocket.clientWebSockets[dedicatedSessionId]; if(!dedicatedClientWs) { continue; diff --git a/src/di/Container.ts b/src/di/Container.ts index 001851cd..14b9e7a3 100644 --- a/src/di/Container.ts +++ b/src/di/Container.ts @@ -14,6 +14,7 @@ import { HttpRouterOverride } from "../overrides/routers/HttpRouter"; import { FikaMatchService } from "../services/FikaMatchService"; import { FikaFriendRequestsCacheService } from "../services/cache/FikaFriendRequestsCacheService"; import { FikaPlayerRelationsCacheService } from "../services/cache/FikaPlayerRelationsCacheService"; +import { FikaDedicatedRaidService } from "../services/FikaDedicatedRaidService"; import { FikaClientModHashesHelper } from "../helpers/FikaClientModHashesHelper"; import { FikaFriendRequestsHelper } from "../helpers/FikaFriendRequestsHelper"; @@ -37,12 +38,14 @@ import { FikaLocationStaticRouter } from "../routers/static/FikaLocationStaticRo import { FikaRaidStaticRouter } from "../routers/static/FikaRaidStaticRouter"; import { FikaSendItemStaticRouter } from "../routers/static/FikaSendItemStaticRouter"; import { FikaUpdateStaticRouter } from "../routers/static/FikaUpdateStaticRouter"; - import { FikaItemEventRouter } from "../routers/item_events/FikaItemEventRouter"; +import { FikaDedicatedRaidWebSocket } from "../services/FikaDedicatedRaidWebSocket"; +import { IWebSocketConnectionHandler } from "@spt/servers/ws/IWebSocketConnectionHandler"; + import { Fika } from "../Fika"; import { FikaServerTools } from "../utils/FikaServerTools"; -import { FikaDedicatedRaidService } from "../services/FikaDedicatedRaidService"; + export class Container { public static register(container: DependencyContainer): void { @@ -60,6 +63,8 @@ export class Container { Container.registerRouters(container); + Container.registerWebSockets(container); + Container.registerListTypes(container); container.register("Fika", Fika, { lifecycle: Lifecycle.Singleton }); @@ -81,7 +86,7 @@ export class Container { container.registerType("StaticRoutes", "FikaUpdateStaticRouter"); container.registerType("IERouters", "FikaItemEventRouter"); - container.registerType("WebSocketConnectionHandler", "FikaDedicatedRaidService"); + container.registerType("WebSocketConnectionHandler", "FikaDedicatedRaidWebSocket"); } private static registerUtils(container: DependencyContainer): void { @@ -139,4 +144,9 @@ export class Container { container.register("FikaItemEventRouter", { useClass: FikaItemEventRouter }); } + + private static registerWebSockets(container: DependencyContainer): void { + container.register("FikaDedicatedRaidWebSocket", FikaDedicatedRaidWebSocket, { lifecycle: Lifecycle.Singleton }); + container.register("FikaDedicatedRaidWebSocket", FikaDedicatedRaidWebSocket, { lifecycle: Lifecycle.Singleton }); + } } diff --git a/src/services/FikaDedicatedRaidService.ts b/src/services/FikaDedicatedRaidService.ts index 6f5e279f..fbf90163 100644 --- a/src/services/FikaDedicatedRaidService.ts +++ b/src/services/FikaDedicatedRaidService.ts @@ -1,14 +1,10 @@ import { ILogger } from "@spt/models/spt/utils/ILogger"; import { inject, injectable } from "tsyringe"; import { IDedicatedClientInfo } from "../models/fika/IDedicatedClientInfo"; -import { WebSocketServer } from "@spt/servers/WebSocketServer"; -import { IWebSocketConnectionHandler } from "@spt/servers/ws/IWebSocketConnectionHandler"; -import { IncomingMessage } from "http"; -import { WebSocket } from "ws"; +import { FikaDedicatedRaidWebSocket } from "./FikaDedicatedRaidWebSocket"; @injectable() -export class FikaDedicatedRaidService implements IWebSocketConnectionHandler{ - public clientWebSockets: Record; +export class FikaDedicatedRaidService { public dedicatedClients: Record; public requestedSessions: Record; public onNoDedicatedClientAvailable?: () => void; @@ -16,10 +12,9 @@ export class FikaDedicatedRaidService implements IWebSocketConnectionHandler{ public onDedicatedClientResponse?: (sessionID: string) => void; constructor( + @inject("FikaDedicatedRaidWebSocket") protected fikaDedicatedRaidWebSocket: FikaDedicatedRaidWebSocket, @inject("WinstonLogger") protected logger: ILogger, - @inject("WebSocketServer") protected webSocketServer: WebSocketServer, ) { - this.clientWebSockets = {}; this.dedicatedClients = {}; this.requestedSessions = {}; @@ -44,37 +39,12 @@ export class FikaDedicatedRaidService implements IWebSocketConnectionHandler{ }, 5000); } - public getSocketId(): string - { - return "FikaDedicatedRaidService"; - } - - public getHookUrl(): string - { - return "/fika/dedicatedraidservice/"; - } - - public onConnection(ws: WebSocket, req: IncomingMessage): void - { - // Strip request and break it into sections - const splitUrl = req.url.substring(0, req.url.indexOf("?")).split("/"); - const sessionID = splitUrl.pop(); - - this.clientWebSockets[sessionID] = ws; - - ws.on("message", (msg) => this.onMessage(sessionID, msg.toString())); - } - - public onMessage(sessionID: string, msg: string) { - // Do Nothing - } - public handleRequestedSessions(matchId: string): void { if (matchId in this.requestedSessions) { const userToJoin = this.requestedSessions[matchId]; delete this.requestedSessions[matchId]; - this.clientWebSockets[userToJoin].send(JSON.stringify( + this.fikaDedicatedRaidWebSocket.clientWebSockets[userToJoin].send(JSON.stringify( { type: "fikaDedicatedJoinMatch", matchId: matchId diff --git a/src/services/FikaDedicatedRaidWebSocket.ts b/src/services/FikaDedicatedRaidWebSocket.ts new file mode 100644 index 00000000..73f8a69a --- /dev/null +++ b/src/services/FikaDedicatedRaidWebSocket.ts @@ -0,0 +1,46 @@ +import { ILogger } from "@spt/models/spt/utils/ILogger"; +import { inject, injectable } from "tsyringe"; +import { IDedicatedClientInfo } from "../models/fika/IDedicatedClientInfo"; +import { IWebSocketConnectionHandler } from "@spt/servers/ws/IWebSocketConnectionHandler"; +import { IncomingMessage } from "http"; +import { WebSocket } from "ws"; + +@injectable() +export class FikaDedicatedRaidWebSocket implements IWebSocketConnectionHandler{ + public clientWebSockets: Record; + + constructor( + @inject("WinstonLogger") protected logger: ILogger, + ) { + + this.clientWebSockets = {}; + + } + + public getSocketId(): string + { + return "FikaDedicatedRaidService"; + } + + public getHookUrl(): string + { + return "/fika/dedicatedraidservice/"; + } + + public onConnection(ws: WebSocket, req: IncomingMessage): void + { + // Strip request and break it into sections + const splitUrl = req.url.substring(0, req.url.indexOf("?")).split("/"); + const sessionID = splitUrl.pop(); + + this.clientWebSockets[sessionID] = ws; + + this.logger.info(`${sessionID} connected to FikaDedicatedRaidService`); + + ws.on("message", (msg) => this.onMessage(sessionID, msg.toString())); + } + + public onMessage(sessionID: string, msg: string) { + // Do Nothing + } +} From c93ad851196a91532b3bfcf8bbb7c88e8f8acb9c Mon Sep 17 00:00:00 2001 From: trippyone <137233897+trippyone@users.noreply.github.com> Date: Mon, 8 Jul 2024 11:38:44 -0400 Subject: [PATCH 03/21] Added isDedicated flag. --- src/controllers/FikaUpdateController.ts | 2 +- src/models/fika/IFikaMatch.ts | 1 + .../fika/routes/update/IFikaUpdateSethostRequestData.ts | 1 + src/services/FikaMatchService.ts | 6 ++++-- 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/controllers/FikaUpdateController.ts b/src/controllers/FikaUpdateController.ts index a93fc5ca..608cf1ce 100644 --- a/src/controllers/FikaUpdateController.ts +++ b/src/controllers/FikaUpdateController.ts @@ -42,7 +42,7 @@ export class FikaUpdateController { * @param request */ public handleSethost(request: IFikaUpdateSethostRequestData): void { - this.fikaMatchService.setMatchHost(request.serverId, request.ips, request.port, request.natPunch); + this.fikaMatchService.setMatchHost(request.serverId, request.ips, request.port, request.natPunch, request.isDedicated); } /** diff --git a/src/models/fika/IFikaMatch.ts b/src/models/fika/IFikaMatch.ts index 5aed991f..79e70127 100644 --- a/src/models/fika/IFikaMatch.ts +++ b/src/models/fika/IFikaMatch.ts @@ -24,4 +24,5 @@ export interface IFikaMatch { time: FikaTime; raidCode: string; natPunch: boolean; + isDedicated: boolean; } diff --git a/src/models/fika/routes/update/IFikaUpdateSethostRequestData.ts b/src/models/fika/routes/update/IFikaUpdateSethostRequestData.ts index 30625ef4..2ab47f94 100644 --- a/src/models/fika/routes/update/IFikaUpdateSethostRequestData.ts +++ b/src/models/fika/routes/update/IFikaUpdateSethostRequestData.ts @@ -3,4 +3,5 @@ export interface IFikaUpdateSethostRequestData { ips: string[]; port: number; natPunch: boolean; + isDedicated: boolean; } diff --git a/src/services/FikaMatchService.ts b/src/services/FikaMatchService.ts index b101ec03..bb647757 100644 --- a/src/services/FikaMatchService.ts +++ b/src/services/FikaMatchService.ts @@ -204,7 +204,8 @@ export class FikaMatchService { side: data.side, time: data.time, raidCode: data.raidCode, - natPunch: false + natPunch: false, + isDedicated: false }); this.addTimeoutInterval(data.serverId); @@ -275,7 +276,7 @@ export class FikaMatchService { * @param ips * @param port */ - public setMatchHost(matchId: string, ips: string[], port: number, natPunch: boolean): void { + public setMatchHost(matchId: string, ips: string[], port: number, natPunch: boolean, isDedicated: boolean): void { if (!this.matches.has(matchId)) { return; } @@ -285,6 +286,7 @@ export class FikaMatchService { match.ips = ips; match.port = port; match.natPunch = natPunch; + match.isDedicated = isDedicated; } /** From e8e3e050a4fb19903d394cec337015b68eff3828 Mon Sep 17 00:00:00 2001 From: trippyone <137233897+trippyone@users.noreply.github.com> Date: Mon, 8 Jul 2024 15:56:38 -0400 Subject: [PATCH 04/21] Fix missing isDedicated flag --- src/controllers/FikaRaidController.ts | 1 + src/models/fika/routes/raid/gethost/IFikaRaidGethostResponse.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/src/controllers/FikaRaidController.ts b/src/controllers/FikaRaidController.ts index a2bb29e5..cb23ef65 100644 --- a/src/controllers/FikaRaidController.ts +++ b/src/controllers/FikaRaidController.ts @@ -87,6 +87,7 @@ export class FikaRaidController { ips: match.ips, port: match.port, natPunch: match.natPunch, + isDedicated: match.isDedicated }; } diff --git a/src/models/fika/routes/raid/gethost/IFikaRaidGethostResponse.ts b/src/models/fika/routes/raid/gethost/IFikaRaidGethostResponse.ts index 6da7d49e..3841dff9 100644 --- a/src/models/fika/routes/raid/gethost/IFikaRaidGethostResponse.ts +++ b/src/models/fika/routes/raid/gethost/IFikaRaidGethostResponse.ts @@ -2,4 +2,5 @@ export interface IFikaRaidGethostResponse { ips: string[]; port: number; natPunch: boolean; + isDedicated: boolean; } From e78bf037da50981ca7b33a2a840e616121d67ebe Mon Sep 17 00:00:00 2001 From: trippyone <137233897+trippyone@users.noreply.github.com> Date: Wed, 10 Jul 2024 13:09:39 -0400 Subject: [PATCH 05/21] Automatically create dedicated profiles Automatically create dedicated profiles. It will also generate a batch file in /fika-server/assets/scripts for each profile to launch the dedicated client in batch mode (headless). Usage: * Set the profile amount to the desired value in fika.jsonc. It will create as many profiles as you set in amount. * Start SPT * Copy the script to your EFT folder. * Run the script. --- assets/configs/fika.jsonc | 9 + src/Fika.ts | 9 + src/controllers/FikaRaidController.ts | 4 +- src/di/Container.ts | 6 +- src/models/fika/config/IFikaConfig.ts | 2 + .../fika/config/IFikaConfigDedicated.ts | 9 + .../{ => dedicated}/IDedicatedClientInfo.ts | 0 src/services/FikaMatchService.ts | 2 +- .../dedicated/FikaDedicatedProfileService.ts | 187 ++++++++++++++++++ .../FikaDedicatedRaidService.ts | 4 +- .../FikaDedicatedRaidWebSocket.ts | 2 +- 11 files changed, 226 insertions(+), 8 deletions(-) create mode 100644 src/models/fika/config/IFikaConfigDedicated.ts rename src/models/fika/{ => dedicated}/IDedicatedClientInfo.ts (100%) create mode 100644 src/services/dedicated/FikaDedicatedProfileService.ts rename src/services/{ => dedicated}/FikaDedicatedRaidService.ts (92%) rename src/{services => websockets}/FikaDedicatedRaidWebSocket.ts (93%) diff --git a/assets/configs/fika.jsonc b/assets/configs/fika.jsonc index ff6da2f5..71a7c719 100644 --- a/assets/configs/fika.jsonc +++ b/assets/configs/fika.jsonc @@ -23,5 +23,14 @@ "enable": false, "port": 6790, "natIntroduceAmount": 1 + }, + "dedicated": { + "profiles": { + "amount": 0 + }, + "scripts": { + "generate": true, + "forceIp": "" + } } } diff --git a/src/Fika.ts b/src/Fika.ts index 08de8609..46f157e9 100644 --- a/src/Fika.ts +++ b/src/Fika.ts @@ -4,18 +4,23 @@ import { Overrider } from "./overrides/Overrider"; import { FikaServerTools } from "./utils/FikaServerTools"; import { FikaConfig } from "./utils/FikaConfig"; import { IFikaConfigNatPunchServer } from "./models/fika/config/IFikaConfigNatPunchServer"; +import { FikaDedicatedProfileService } from "./services/dedicated/FikaDedicatedProfileService"; +import { IFikaConfigDedicated } from "./models/fika/config/IFikaConfigDedicated"; @injectable() export class Fika { protected natPunchServerConfig: IFikaConfigNatPunchServer; + protected dedicatedConfig: IFikaConfigDedicated; constructor( @inject("DatabaseServer") protected databaseServer: DatabaseServer, @inject("Overrider") protected overrider: Overrider, @inject("FikaServerTools") protected fikaServerTools: FikaServerTools, @inject("FikaConfig") protected fikaConfig: FikaConfig, + @inject("FikaDedicatedProfileService") protected fikaDedicatedProfileService: FikaDedicatedProfileService, ) { this.natPunchServerConfig = fikaConfig.getConfig().natPunchServer; + this.dedicatedConfig = fikaConfig.getConfig().dedicated; } public async preSptLoad(container: DependencyContainer): Promise { @@ -26,5 +31,9 @@ export class Fika { if(this.natPunchServerConfig.enable) { this.fikaServerTools.startService("NatPunchServer"); } + + if(this.dedicatedConfig.profiles.amount > 0) { + this.fikaDedicatedProfileService.init(); + } } } diff --git a/src/controllers/FikaRaidController.ts b/src/controllers/FikaRaidController.ts index cb23ef65..4fa416fc 100644 --- a/src/controllers/FikaRaidController.ts +++ b/src/controllers/FikaRaidController.ts @@ -11,14 +11,14 @@ import { IFikaRaidJoinResponse } from "../models/fika/routes/raid/join/IFikaRaid import { IFikaRaidLeaveRequestData } from "../models/fika/routes/raid/leave/IFikaRaidLeaveRequestData"; import { IFikaRaidSpawnpointResponse } from "../models/fika/routes/raid/spawnpoint/IFikaRaidSpawnpointResponse"; import { FikaMatchService } from "../services/FikaMatchService"; -import { FikaDedicatedRaidService } from "../services/FikaDedicatedRaidService"; +import { FikaDedicatedRaidService } from "../services/dedicated/FikaDedicatedRaidService"; import { IStartDedicatedRequest } from "../models/fika/routes/raid/dedicated/IStartDedicatedRequest"; import { IStartDedicatedResponse } from "../models/fika/routes/raid/dedicated/IStartDedicatedResponse"; import { WebSocket } from "ws"; import { ILogger } from "@spt/models/spt/utils/ILogger"; import { IStatusDedicatedRequest } from "../models/fika/routes/raid/dedicated/IStatusDedicatedRequest"; import { IStatusDedicatedResponse } from "../models/fika/routes/raid/dedicated/IStatusDedicatedResponse"; -import { FikaDedicatedRaidWebSocket } from "../services/FikaDedicatedRaidWebSocket"; +import { FikaDedicatedRaidWebSocket } from "../websockets/FikaDedicatedRaidWebSocket"; @injectable() export class FikaRaidController { diff --git a/src/di/Container.ts b/src/di/Container.ts index 14b9e7a3..4705ad66 100644 --- a/src/di/Container.ts +++ b/src/di/Container.ts @@ -14,7 +14,7 @@ import { HttpRouterOverride } from "../overrides/routers/HttpRouter"; import { FikaMatchService } from "../services/FikaMatchService"; import { FikaFriendRequestsCacheService } from "../services/cache/FikaFriendRequestsCacheService"; import { FikaPlayerRelationsCacheService } from "../services/cache/FikaPlayerRelationsCacheService"; -import { FikaDedicatedRaidService } from "../services/FikaDedicatedRaidService"; +import { FikaDedicatedRaidService } from "../services/dedicated/FikaDedicatedRaidService"; import { FikaClientModHashesHelper } from "../helpers/FikaClientModHashesHelper"; import { FikaFriendRequestsHelper } from "../helpers/FikaFriendRequestsHelper"; @@ -40,11 +40,12 @@ import { FikaSendItemStaticRouter } from "../routers/static/FikaSendItemStaticRo import { FikaUpdateStaticRouter } from "../routers/static/FikaUpdateStaticRouter"; import { FikaItemEventRouter } from "../routers/item_events/FikaItemEventRouter"; -import { FikaDedicatedRaidWebSocket } from "../services/FikaDedicatedRaidWebSocket"; +import { FikaDedicatedRaidWebSocket } from "../websockets/FikaDedicatedRaidWebSocket"; import { IWebSocketConnectionHandler } from "@spt/servers/ws/IWebSocketConnectionHandler"; import { Fika } from "../Fika"; import { FikaServerTools } from "../utils/FikaServerTools"; +import { FikaDedicatedProfileService } from "../services/dedicated/FikaDedicatedProfileService"; export class Container { @@ -110,6 +111,7 @@ export class Container { container.register("FikaFriendRequestsCacheService", FikaFriendRequestsCacheService, { lifecycle: Lifecycle.Singleton }); container.register("FikaPlayerRelationsCacheService", FikaPlayerRelationsCacheService, { lifecycle: Lifecycle.Singleton }); container.register("FikaDedicatedRaidService", FikaDedicatedRaidService, { lifecycle: Lifecycle.Singleton }); + container.register("FikaDedicatedProfileService", FikaDedicatedProfileService, { lifecycle: Lifecycle.Singleton }); } private static registerHelpers(container: DependencyContainer): void { diff --git a/src/models/fika/config/IFikaConfig.ts b/src/models/fika/config/IFikaConfig.ts index 6ad271a5..2d5e5cbe 100644 --- a/src/models/fika/config/IFikaConfig.ts +++ b/src/models/fika/config/IFikaConfig.ts @@ -1,9 +1,11 @@ import { IFikaConfigClient } from "./IFikaConfigClient"; import { IFikaConfigServer } from "./IFikaConfigServer"; import { IFikaConfigNatPunchServer } from "./IFikaConfigNatPunchServer"; +import { IFikaConfigDedicated } from "./IFikaConfigDedicated"; export interface IFikaConfig { client: IFikaConfigClient; server: IFikaConfigServer; natPunchServer: IFikaConfigNatPunchServer; + dedicated: IFikaConfigDedicated; } diff --git a/src/models/fika/config/IFikaConfigDedicated.ts b/src/models/fika/config/IFikaConfigDedicated.ts new file mode 100644 index 00000000..97842826 --- /dev/null +++ b/src/models/fika/config/IFikaConfigDedicated.ts @@ -0,0 +1,9 @@ +export interface IFikaConfigDedicated { + profiles: { + amount: number; + } + scripts: { + generate: boolean; + forceIp: string; + } +} diff --git a/src/models/fika/IDedicatedClientInfo.ts b/src/models/fika/dedicated/IDedicatedClientInfo.ts similarity index 100% rename from src/models/fika/IDedicatedClientInfo.ts rename to src/models/fika/dedicated/IDedicatedClientInfo.ts diff --git a/src/services/FikaMatchService.ts b/src/services/FikaMatchService.ts index bb647757..8070d4e4 100644 --- a/src/services/FikaMatchService.ts +++ b/src/services/FikaMatchService.ts @@ -11,7 +11,7 @@ import { IFikaPlayer } from "../models/fika/IFikaPlayer"; import { IFikaRaidCreateRequestData } from "../models/fika/routes/raid/create/IFikaRaidCreateRequestData"; import { FikaConfig } from "../utils/FikaConfig"; -import { FikaDedicatedRaidService } from "./FikaDedicatedRaidService"; +import { FikaDedicatedRaidService } from "./dedicated/FikaDedicatedRaidService"; @injectable() export class FikaMatchService { diff --git a/src/services/dedicated/FikaDedicatedProfileService.ts b/src/services/dedicated/FikaDedicatedProfileService.ts new file mode 100644 index 00000000..7f95eac9 --- /dev/null +++ b/src/services/dedicated/FikaDedicatedProfileService.ts @@ -0,0 +1,187 @@ +import { inject, injectable } from "tsyringe"; +import { LauncherController } from "@spt/controllers/LauncherController"; +import { SaveServer } from "@spt/servers/SaveServer"; +import { ILogger } from "@spt/models/spt/utils/ILogger"; +import { TimeUtil } from "@spt/utils/TimeUtil"; +import { RandomUtil } from "@spt/utils/RandomUtil"; +import { HashUtil } from "@spt/utils/HashUtil"; +import { Info, ISptProfile } from "@spt/models/eft/profile/ISptProfile"; +import { ProfileController } from "@spt/controllers/ProfileController"; +import { IProfileCreateRequestData } from "@spt/models/eft/profile/IProfileCreateRequestData"; +import { IFikaConfigDedicated } from "../../models/fika/config/IFikaConfigDedicated"; +import { FikaConfig } from "../../utils/FikaConfig"; +import path from "path"; +import fs from "fs"; +import { ConfigServer } from "@spt/servers/ConfigServer"; +import { ConfigTypes } from "@spt/models/enums/ConfigTypes"; +import { IHttpConfig } from "@spt/models/spt/config/IHttpConfig"; + +@injectable() +export class FikaDedicatedProfileService { + readonly scriptsPath = path.join(path.join(__dirname, "../../../assets/scripts")); + readonly HEAD_USEC_4 = "5fdb4139e4ed5b5ea251e4ed"; // (_parent: 5cc085e214c02e000c6bea67) + readonly VOICE_USEC_4 = "6284d6a28e4092597733b7a6"; // (_parent: 5fc100cf95572123ae738483) + + protected httpConfig: IHttpConfig; + protected dedicatedConfig: IFikaConfigDedicated; + public dedicatedProfiles: ISptProfile[] = []; + + constructor( + @inject("LauncherController") protected launcherController: LauncherController, + @inject("SaveServer") protected saveServer: SaveServer, + @inject("WinstonLogger") protected logger: ILogger, + @inject("TimeUtil") protected timeUtil: TimeUtil, + @inject("RandomUtil") protected randomUtil: RandomUtil, + @inject("HashUtil") protected hashUtil: HashUtil, + @inject("ProfileController") protected profileController: ProfileController, + @inject("FikaConfig") protected fikaConfig: FikaConfig, + @inject("ConfigServer") protected configServer: ConfigServer, + ) { + this.dedicatedConfig = fikaConfig.getConfig().dedicated; + this.httpConfig = this.configServer.getConfig(ConfigTypes.HTTP); + } + + public init() { + this.dedicatedProfiles = this.loadDedicatedProfiles(); + + this.logger.info(`Found ${this.dedicatedProfiles.length} dedicated client profiles.`) + + const profileAmount = this.dedicatedConfig.profiles.amount; + + if(this.dedicatedProfiles.length < profileAmount) { + + const createdProfiles = this.createDedicatedProfiles(profileAmount); + + this.logger.success(`Created ${createdProfiles.length} dedicated client profiles!`); + + if(this.dedicatedConfig.scripts.generate) { + let ip = this.httpConfig.backendIp; + const port = this.httpConfig.backendPort; + + const forceIp = this.dedicatedConfig.scripts.forceIp; + + if(forceIp != "") { + ip = forceIp; + } + + const backendUrl = `http://${ip}:${port}`; + + for(const profile of createdProfiles) { + this.generateLaunchScript(profile, backendUrl, this.scriptsPath); + } + } + } + } + + public loadDedicatedProfiles(): ISptProfile[] { + let profiles: ISptProfile[] = []; + + for(const profileId in this.saveServer.getProfiles()) { + const profile = this.saveServer.getProfile(profileId); + + if(profile.info.password == "fika-dedicated") { + profiles.push(profile); + } + } + + return profiles; + } + + public createDedicatedProfiles(profileAmount: number): ISptProfile[] { + let profileCount = this.dedicatedProfiles.length; + let profileAmountToCreate = profileAmount - profileCount; + let createdProfiles: ISptProfile[] = []; + + for(let i = 0; i < profileAmountToCreate; i++) { + const profile = this.createDedicatedProfile(); + createdProfiles.push(profile); + } + + return createdProfiles; + } + + public createDedicatedProfile(): ISptProfile { + + const username = `dedicated_${this.generateUniqueId()}`; // Generate a unique username + const profileId = this.createMiniProfile(username); + + const profile = this.createFullProfile(profileId); + + return profile; + } + + public createMiniProfile(username: string): string { + + const profileId = this.generateUniqueId(); + const scavId = this.generateUniqueId(); + const password = "fika-dedicated"; // Allows us to know this is a dedicated client profile + + const newProfileDetails: Info = { + id: profileId, + scavId: scavId, + aid: this.hashUtil.generateAccountId(), + username: username, + password: password, + wipe: true, + edition: "Edge Of Darkness", // Doesn't matter + }; + + this.saveServer.createProfile(newProfileDetails); + + this.saveServer.loadProfile(profileId); + this.saveServer.saveProfile(profileId); + + return profileId; + } + + public createFullProfile(profileId: string) { + const newProfileData: IProfileCreateRequestData = { + side: "usec", + nickname: "dedicated", + headId: this.HEAD_USEC_4, + voiceId: this.VOICE_USEC_4 + } + + this.profileController.createProfile(newProfileData, profileId); + + const profile = this.saveServer.getProfile(profileId); + + return profile; + } + + public generateLaunchScript(profile: ISptProfile, backendUrl: string, targetFolderPath: string) { + + const scriptName = `Start_${profile.info.username}.bat`; + const scriptPath = path.join(targetFolderPath, scriptName); + const scriptContent = `EscapeFromTarkov.exe -token=${profile.info.id} -config={"BackendUrl":"${backendUrl}","Version":"live"} -batchmode`; + + try { + if(!fs.existsSync(targetFolderPath)) { + fs.mkdirSync(targetFolderPath); + } + + fs.writeFileSync(scriptPath, scriptContent); + + this.logger.success(`Generated launch script: /fika-server/assets/scripts/${scriptName}`); + } + catch(error) { + this.logger.error(`Failed to generate launch script: ${error}`); + } + } + + // generateProfileId + protected generateUniqueId(): string + { + const timestamp = this.timeUtil.getTimestamp(); + + return this.formatID(timestamp, timestamp * this.randomUtil.getInt(1, 1000000)); + } + + protected formatID(timeStamp: number, counter: number): string + { + const timeStampStr = timeStamp.toString(16).padStart(8, "0"); + const counterStr = counter.toString(16).padStart(16, "0"); + + return timeStampStr.toLowerCase() + counterStr.toLowerCase(); + } +} diff --git a/src/services/FikaDedicatedRaidService.ts b/src/services/dedicated/FikaDedicatedRaidService.ts similarity index 92% rename from src/services/FikaDedicatedRaidService.ts rename to src/services/dedicated/FikaDedicatedRaidService.ts index fbf90163..fa628077 100644 --- a/src/services/FikaDedicatedRaidService.ts +++ b/src/services/dedicated/FikaDedicatedRaidService.ts @@ -1,7 +1,7 @@ import { ILogger } from "@spt/models/spt/utils/ILogger"; import { inject, injectable } from "tsyringe"; -import { IDedicatedClientInfo } from "../models/fika/IDedicatedClientInfo"; -import { FikaDedicatedRaidWebSocket } from "./FikaDedicatedRaidWebSocket"; +import { IDedicatedClientInfo } from "../../models/fika/dedicated/IDedicatedClientInfo"; +import { FikaDedicatedRaidWebSocket } from "../../websockets/FikaDedicatedRaidWebSocket"; @injectable() export class FikaDedicatedRaidService { diff --git a/src/services/FikaDedicatedRaidWebSocket.ts b/src/websockets/FikaDedicatedRaidWebSocket.ts similarity index 93% rename from src/services/FikaDedicatedRaidWebSocket.ts rename to src/websockets/FikaDedicatedRaidWebSocket.ts index 73f8a69a..beee3467 100644 --- a/src/services/FikaDedicatedRaidWebSocket.ts +++ b/src/websockets/FikaDedicatedRaidWebSocket.ts @@ -1,6 +1,6 @@ import { ILogger } from "@spt/models/spt/utils/ILogger"; import { inject, injectable } from "tsyringe"; -import { IDedicatedClientInfo } from "../models/fika/IDedicatedClientInfo"; +import { IDedicatedClientInfo } from "../models/fika/dedicated/IDedicatedClientInfo"; import { IWebSocketConnectionHandler } from "@spt/servers/ws/IWebSocketConnectionHandler"; import { IncomingMessage } from "http"; import { WebSocket } from "ws"; From 3393a03a36d0c71b242e457ae7de125d90bdc993 Mon Sep 17 00:00:00 2001 From: trippyone <137233897+trippyone@users.noreply.github.com> Date: Wed, 10 Jul 2024 13:17:40 -0400 Subject: [PATCH 06/21] Fix double path.join call --- src/services/dedicated/FikaDedicatedProfileService.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/services/dedicated/FikaDedicatedProfileService.ts b/src/services/dedicated/FikaDedicatedProfileService.ts index 7f95eac9..304cba93 100644 --- a/src/services/dedicated/FikaDedicatedProfileService.ts +++ b/src/services/dedicated/FikaDedicatedProfileService.ts @@ -18,9 +18,9 @@ import { IHttpConfig } from "@spt/models/spt/config/IHttpConfig"; @injectable() export class FikaDedicatedProfileService { - readonly scriptsPath = path.join(path.join(__dirname, "../../../assets/scripts")); - readonly HEAD_USEC_4 = "5fdb4139e4ed5b5ea251e4ed"; // (_parent: 5cc085e214c02e000c6bea67) - readonly VOICE_USEC_4 = "6284d6a28e4092597733b7a6"; // (_parent: 5fc100cf95572123ae738483) + readonly scriptsPath = path.join(__dirname, "../../../assets/scripts"); + readonly HEAD_USEC_4 = "5fdb4139e4ed5b5ea251e4ed"; // _parent: 5cc085e214c02e000c6bea67 + readonly VOICE_USEC_4 = "6284d6a28e4092597733b7a6"; // _parent: 5fc100cf95572123ae738483 protected httpConfig: IHttpConfig; protected dedicatedConfig: IFikaConfigDedicated; From fd11cae0412dd93f65ed6d93dc20af88db5c9cc4 Mon Sep 17 00:00:00 2001 From: trippyone <137233897+trippyone@users.noreply.github.com> Date: Wed, 10 Jul 2024 13:24:46 -0400 Subject: [PATCH 07/21] Use a unique nickname --- src/services/dedicated/FikaDedicatedProfileService.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/services/dedicated/FikaDedicatedProfileService.ts b/src/services/dedicated/FikaDedicatedProfileService.ts index 304cba93..58e526b5 100644 --- a/src/services/dedicated/FikaDedicatedProfileService.ts +++ b/src/services/dedicated/FikaDedicatedProfileService.ts @@ -135,18 +135,21 @@ export class FikaDedicatedProfileService { } public createFullProfile(profileId: string) { + + const profile = this.saveServer.getProfile(profileId); + const newProfileData: IProfileCreateRequestData = { side: "usec", - nickname: "dedicated", + nickname: profile.info.username, headId: this.HEAD_USEC_4, voiceId: this.VOICE_USEC_4 } this.profileController.createProfile(newProfileData, profileId); - const profile = this.saveServer.getProfile(profileId); + const fullProfile = this.saveServer.getProfile(profileId); - return profile; + return fullProfile; } public generateLaunchScript(profile: ISptProfile, backendUrl: string, targetFolderPath: string) { From 148449e83802888fc71f66e34bb2b7ed8555a408 Mon Sep 17 00:00:00 2001 From: trippyone <137233897+trippyone@users.noreply.github.com> Date: Wed, 10 Jul 2024 13:47:19 -0400 Subject: [PATCH 08/21] Refactoring --- .../dedicated/FikaDedicatedProfileService.ts | 43 ++++++++++--------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/src/services/dedicated/FikaDedicatedProfileService.ts b/src/services/dedicated/FikaDedicatedProfileService.ts index 58e526b5..f9470f99 100644 --- a/src/services/dedicated/FikaDedicatedProfileService.ts +++ b/src/services/dedicated/FikaDedicatedProfileService.ts @@ -101,20 +101,32 @@ export class FikaDedicatedProfileService { } public createDedicatedProfile(): ISptProfile { + // Generate a unique username + const username = `dedicated_${this.generateUniqueId()}`; + // Using a password allows us to know which profiles are dedicated client profiles. + const password = "fika-dedicated"; + // Random edition. Doesn't matter + const edition = "Edge Of Darkness"; - const username = `dedicated_${this.generateUniqueId()}`; // Generate a unique username - const profileId = this.createMiniProfile(username); + // Create mini profile + const profileId = this.createMiniProfile(username, password, edition); - const profile = this.createFullProfile(profileId); + // Random character configs. Doesn't matter. + const newProfileData: IProfileCreateRequestData = { + side: "usec", + nickname: username, // Use the username as the nickname to ensure it is unique. + headId: this.HEAD_USEC_4, + voiceId: this.VOICE_USEC_4 + } + + const profile = this.createFullProfile(newProfileData, profileId); return profile; } - public createMiniProfile(username: string): string { - + public createMiniProfile(username: string, password: string, edition: string): string { const profileId = this.generateUniqueId(); const scavId = this.generateUniqueId(); - const password = "fika-dedicated"; // Allows us to know this is a dedicated client profile const newProfileDetails: Info = { id: profileId, @@ -123,7 +135,7 @@ export class FikaDedicatedProfileService { username: username, password: password, wipe: true, - edition: "Edge Of Darkness", // Doesn't matter + edition: edition }; this.saveServer.createProfile(newProfileDetails); @@ -134,22 +146,13 @@ export class FikaDedicatedProfileService { return profileId; } - public createFullProfile(profileId: string) { + public createFullProfile(profileData: IProfileCreateRequestData, profileId: string) { - const profile = this.saveServer.getProfile(profileId); + this.profileController.createProfile(profileData, profileId); - const newProfileData: IProfileCreateRequestData = { - side: "usec", - nickname: profile.info.username, - headId: this.HEAD_USEC_4, - voiceId: this.VOICE_USEC_4 - } - - this.profileController.createProfile(newProfileData, profileId); - - const fullProfile = this.saveServer.getProfile(profileId); + const profile = this.saveServer.getProfile(profileId); - return fullProfile; + return profile; } public generateLaunchScript(profile: ISptProfile, backendUrl: string, targetFolderPath: string) { From 9bf83280cb2732bcb20725ddbc8e71de1ff232f0 Mon Sep 17 00:00:00 2001 From: trippyone <137233897+trippyone@users.noreply.github.com> Date: Thu, 11 Jul 2024 11:40:10 -0400 Subject: [PATCH 09/21] Use dedicated client requester's profile when generating bots --- src/di/Container.ts | 3 + src/overrides/controllers/BotController.ts | 60 +++++++++++++++++++ src/services/FikaMatchService.ts | 4 ++ .../dedicated/FikaDedicatedRaidService.ts | 1 - 4 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 src/overrides/controllers/BotController.ts diff --git a/src/di/Container.ts b/src/di/Container.ts index 4705ad66..36e518b5 100644 --- a/src/di/Container.ts +++ b/src/di/Container.ts @@ -46,6 +46,7 @@ import { IWebSocketConnectionHandler } from "@spt/servers/ws/IWebSocketConnectio import { Fika } from "../Fika"; import { FikaServerTools } from "../utils/FikaServerTools"; import { FikaDedicatedProfileService } from "../services/dedicated/FikaDedicatedProfileService"; +import { BotControllerOverride } from "../overrides/controllers/BotController"; export class Container { @@ -79,6 +80,7 @@ export class Container { container.registerType("Overrides", "HttpRouterOverride"); container.registerType("Overrides", "LauncherBackgroundOverride"); container.registerType("Overrides", "LocalesOverride"); + container.registerType("Overrides", "BotControllerOverride"); container.registerType("StaticRoutes", "FikaClientStaticRouter"); container.registerType("StaticRoutes", "FikaLocationStaticRouter"); @@ -103,6 +105,7 @@ export class Container { container.register("HttpRouterOverride", HttpRouterOverride, { lifecycle: Lifecycle.Singleton }); container.register("LauncherBackgroundOverride", LauncherBackgroundOverride, { lifecycle: Lifecycle.Singleton }); container.register("LocalesOverride", LocalesOverride, { lifecycle: Lifecycle.Singleton }); + container.register("BotControllerOverride", BotControllerOverride, { lifecycle: Lifecycle.Singleton }); container.register("Overrider", Overrider, { lifecycle: Lifecycle.Singleton }); } diff --git a/src/overrides/controllers/BotController.ts b/src/overrides/controllers/BotController.ts new file mode 100644 index 00000000..897f0af4 --- /dev/null +++ b/src/overrides/controllers/BotController.ts @@ -0,0 +1,60 @@ +import { injectable, inject, DependencyContainer } from "tsyringe"; +import { FikaDialogueController } from "../../controllers/FikaDialogueController"; +import { Override } from "../../di/Override"; +import { IBotBase } from "@spt/models/eft/common/tables/IBotBase"; +import { BotController } from "@spt/controllers/BotController"; +import { ProfileHelper } from "@spt/helpers/ProfileHelper"; +import { IGenerateBotsRequestData } from "@spt/models/eft/bot/IGenerateBotsRequestData"; +import { FikaDedicatedRaidService } from "../../services/dedicated/FikaDedicatedRaidService"; +import { ILogger } from "@spt/models/spt/utils/ILogger"; +import { IPmcData } from "@spt/models/eft/common/IPmcData"; + +@injectable() +export class BotControllerOverride extends Override { + constructor( + @inject("ProfileHelper") protected profileHelper: ProfileHelper, + @inject("BotController") protected botController: BotController, + @inject("FikaDedicatedRaidService") protected fikaDedicatedRaidService: FikaDedicatedRaidService, + @inject("WinstonLogger") protected logger: ILogger, + ) { + super(); + } + + public execute(container: DependencyContainer): void { + container.afterResolution( + "BotController", + (_t, result: BotController) => { + // Override the bot generate function to determine which profile to use whether we're + // generating bots for a dedicated client or a normal host. + result.generate = (sessionId: string, info: IGenerateBotsRequestData): Promise => { + + let pmcProfile: IPmcData; + const dedicatedSessions = this.fikaDedicatedRaidService.requestedSessions; + + if(dedicatedSessions.hasOwnProperty(sessionId)) { + // Use the dedicated client requester's PMC profile + const dedicatedRequesterSessionId = dedicatedSessions[sessionId]; + pmcProfile = this.profileHelper.getPmcProfile(dedicatedRequesterSessionId); + } + else { + // Use the host PMC profile + pmcProfile = this.profileHelper.getPmcProfile(sessionId); + } + + // If there's more than 1 condition, this is the first time client has requested bots + // Client sends every bot type it will need in raid + // Use this opportunity to create and cache bots for later retreval + const isFirstGen = info.conditions.length > 1; + if (isFirstGen) + { + // Error because this is a protected method. + return this.botController.generateBotsFirstTime(info, pmcProfile, sessionId); + } + // Error because this is a protected method. + return this.botController.returnSingleBotFromCache(sessionId, info); + }; + }, + { frequency: "Always" }, + ); + } +} diff --git a/src/services/FikaMatchService.ts b/src/services/FikaMatchService.ts index 8070d4e4..3181a37a 100644 --- a/src/services/FikaMatchService.ts +++ b/src/services/FikaMatchService.ts @@ -237,6 +237,10 @@ export class FikaMatchService { public endMatch(matchId: string, reason: FikaMatchEndSessionMessage): void { this.logger.info(`Coop session ${matchId} has ended: ${reason}`); + if(this.fikaDedicatedRaidService.requestedSessions.hasOwnProperty(matchId)) { + delete this.fikaDedicatedRaidService.requestedSessions[matchId]; + } + this.deleteMatch(matchId); } diff --git a/src/services/dedicated/FikaDedicatedRaidService.ts b/src/services/dedicated/FikaDedicatedRaidService.ts index fa628077..ee538683 100644 --- a/src/services/dedicated/FikaDedicatedRaidService.ts +++ b/src/services/dedicated/FikaDedicatedRaidService.ts @@ -42,7 +42,6 @@ export class FikaDedicatedRaidService { public handleRequestedSessions(matchId: string): void { if (matchId in this.requestedSessions) { const userToJoin = this.requestedSessions[matchId]; - delete this.requestedSessions[matchId]; this.fikaDedicatedRaidWebSocket.clientWebSockets[userToJoin].send(JSON.stringify( { From 0a61d40b9734e8dc79f538956d1abc4cf86ade19 Mon Sep 17 00:00:00 2001 From: trippyone <137233897+trippyone@users.noreply.github.com> Date: Thu, 11 Jul 2024 22:16:59 -0400 Subject: [PATCH 10/21] Remove error caused by protected method --- src/overrides/controllers/BotController.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/overrides/controllers/BotController.ts b/src/overrides/controllers/BotController.ts index 897f0af4..10bb296f 100644 --- a/src/overrides/controllers/BotController.ts +++ b/src/overrides/controllers/BotController.ts @@ -48,10 +48,10 @@ export class BotControllerOverride extends Override { if (isFirstGen) { // Error because this is a protected method. - return this.botController.generateBotsFirstTime(info, pmcProfile, sessionId); + return (this.botController as any).this.botController.generateBotsFirstTime(info, pmcProfile, sessionId); } // Error because this is a protected method. - return this.botController.returnSingleBotFromCache(sessionId, info); + return (this.botController as any).this.botController.returnSingleBotFromCache(sessionId, info); }; }, { frequency: "Always" }, From 3d77cef3306ceeb261ed462dbdced08d2b0e9d83 Mon Sep 17 00:00:00 2001 From: trippyone <137233897+trippyone@users.noreply.github.com> Date: Fri, 12 Jul 2024 09:50:53 -0400 Subject: [PATCH 11/21] Remove any type cast --- src/overrides/controllers/BotController.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/overrides/controllers/BotController.ts b/src/overrides/controllers/BotController.ts index 10bb296f..f692b316 100644 --- a/src/overrides/controllers/BotController.ts +++ b/src/overrides/controllers/BotController.ts @@ -47,11 +47,9 @@ export class BotControllerOverride extends Override { const isFirstGen = info.conditions.length > 1; if (isFirstGen) { - // Error because this is a protected method. - return (this.botController as any).this.botController.generateBotsFirstTime(info, pmcProfile, sessionId); + return this.botController.generateBotsFirstTime(info, pmcProfile, sessionId); } - // Error because this is a protected method. - return (this.botController as any).this.botController.returnSingleBotFromCache(sessionId, info); + return this.botController.returnSingleBotFromCache(sessionId, info); }; }, { frequency: "Always" }, From 373b92d19c3c2ef3cf26b32224ba221ccf95b110 Mon Sep 17 00:00:00 2001 From: trippyone <137233897+trippyone@users.noreply.github.com> Date: Fri, 12 Jul 2024 10:13:39 -0400 Subject: [PATCH 12/21] Fixed type cast --- src/overrides/controllers/BotController.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/overrides/controllers/BotController.ts b/src/overrides/controllers/BotController.ts index f692b316..622763e1 100644 --- a/src/overrides/controllers/BotController.ts +++ b/src/overrides/controllers/BotController.ts @@ -47,9 +47,11 @@ export class BotControllerOverride extends Override { const isFirstGen = info.conditions.length > 1; if (isFirstGen) { - return this.botController.generateBotsFirstTime(info, pmcProfile, sessionId); + // Temporary cast to remove the error caused by protected method. + return (this.botController as any).generateBotsFirstTime(info, pmcProfile, sessionId); } - return this.botController.returnSingleBotFromCache(sessionId, info); + // Temporary cast to remove the error caused by protected method. + return (this.botController as any).returnSingleBotFromCache(sessionId, info); }; }, { frequency: "Always" }, From da2dd8ac122305946194a54ce17bfa72f5f6ca8b Mon Sep 17 00:00:00 2001 From: trippyone <137233897+trippyone@users.noreply.github.com> Date: Mon, 15 Jul 2024 14:22:41 -0400 Subject: [PATCH 13/21] Use ip instead of backendIp --- src/services/dedicated/FikaDedicatedProfileService.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/services/dedicated/FikaDedicatedProfileService.ts b/src/services/dedicated/FikaDedicatedProfileService.ts index f9470f99..50120e73 100644 --- a/src/services/dedicated/FikaDedicatedProfileService.ts +++ b/src/services/dedicated/FikaDedicatedProfileService.ts @@ -55,8 +55,8 @@ export class FikaDedicatedProfileService { this.logger.success(`Created ${createdProfiles.length} dedicated client profiles!`); if(this.dedicatedConfig.scripts.generate) { - let ip = this.httpConfig.backendIp; - const port = this.httpConfig.backendPort; + let ip = this.httpConfig.ip; + const port = this.httpConfig.port; const forceIp = this.dedicatedConfig.scripts.forceIp; From e870d5b406f5b432a71ba2886f99f127d9bec173 Mon Sep 17 00:00:00 2001 From: Lacyway <20912169+Lacyway@users.noreply.github.com> Date: Tue, 16 Jul 2024 07:13:23 +0200 Subject: [PATCH 14/21] Don't show dedi profiles in receivers --- src/controllers/FikaSendItemController.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/controllers/FikaSendItemController.ts b/src/controllers/FikaSendItemController.ts index 29aafb03..663c2073 100644 --- a/src/controllers/FikaSendItemController.ts +++ b/src/controllers/FikaSendItemController.ts @@ -100,6 +100,9 @@ export class FikaSendItemController { const profiles = this.saveServer.getProfiles(); for (const profile of Object.values(profiles)) { + if (profile.info.password === "fika-dedicated") + continue; + const username = profile.info.username; if (!(username in result) && username !== sender.info.username) { result[username] = profile.info.id; From dd390e26eef161a4cbf810fad2633b3f9150027a Mon Sep 17 00:00:00 2001 From: Lacyway <20912169+Lacyway@users.noreply.github.com> Date: Tue, 16 Jul 2024 08:25:17 +0200 Subject: [PATCH 15/21] Don't show dedicated profiles when searching for friends --- src/overrides/controllers/ProfileController.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/overrides/controllers/ProfileController.ts b/src/overrides/controllers/ProfileController.ts index 7c0d937b..24b7a458 100644 --- a/src/overrides/controllers/ProfileController.ts +++ b/src/overrides/controllers/ProfileController.ts @@ -40,6 +40,9 @@ export class ProfileControllerOverride extends Override { const matches: ISearchFriendResponse[] = []; for (const profile of Object.values(profiles)) { + if (profile.info?.password === "fika-dedicated") + continue; + if (profile.characters?.pmc?.Info) { if (profile.characters.pmc.Info.Nickname.toLowerCase().startsWith(searchNicknameLowerCase)) { matches.push({ From c133ab9eb6a51c7dca470f3362f0d5c551e3e352 Mon Sep 17 00:00:00 2001 From: Lacyway <20912169+Lacyway@users.noreply.github.com> Date: Tue, 23 Jul 2024 07:41:08 +0200 Subject: [PATCH 16/21] Get average level of all players when generating bots --- src/overrides/controllers/BotController.ts | 48 ++++++++++++++++++---- 1 file changed, 40 insertions(+), 8 deletions(-) diff --git a/src/overrides/controllers/BotController.ts b/src/overrides/controllers/BotController.ts index 622763e1..b54bf5e1 100644 --- a/src/overrides/controllers/BotController.ts +++ b/src/overrides/controllers/BotController.ts @@ -8,6 +8,8 @@ import { IGenerateBotsRequestData } from "@spt/models/eft/bot/IGenerateBotsReque import { FikaDedicatedRaidService } from "../../services/dedicated/FikaDedicatedRaidService"; import { ILogger } from "@spt/models/spt/utils/ILogger"; import { IPmcData } from "@spt/models/eft/common/IPmcData"; +import { FikaMatchService } from "../../services/FikaMatchService"; +import { SaveServer } from "@spt/servers/SaveServer"; @injectable() export class BotControllerOverride extends Override { @@ -16,6 +18,8 @@ export class BotControllerOverride extends Override { @inject("BotController") protected botController: BotController, @inject("FikaDedicatedRaidService") protected fikaDedicatedRaidService: FikaDedicatedRaidService, @inject("WinstonLogger") protected logger: ILogger, + @inject("FikaMatchService") protected fikaMatchService: FikaMatchService, + @inject("SaveServer") protected saveServer: SaveServer, ) { super(); } @@ -27,31 +31,59 @@ export class BotControllerOverride extends Override { // Override the bot generate function to determine which profile to use whether we're // generating bots for a dedicated client or a normal host. result.generate = (sessionId: string, info: IGenerateBotsRequestData): Promise => { - let pmcProfile: IPmcData; const dedicatedSessions = this.fikaDedicatedRaidService.requestedSessions; + const isDedicated = dedicatedSessions.hasOwnProperty(sessionId); - if(dedicatedSessions.hasOwnProperty(sessionId)) { + if (isDedicated) { // Use the dedicated client requester's PMC profile const dedicatedRequesterSessionId = dedicatedSessions[sessionId]; pmcProfile = this.profileHelper.getPmcProfile(dedicatedRequesterSessionId); - } - else { + } else { // Use the host PMC profile pmcProfile = this.profileHelper.getPmcProfile(sessionId); } + // Get the matchId and then match + const matchId = this.fikaMatchService.getMatchIdByProfile(sessionId); + const match = this.fikaMatchService.getMatch(matchId); + + const players = match.players.keys(); + + // Loop through all the players and get their profiles + let level = 1; + for (const playerId of players) { + const player = this.saveServer.getProfile(playerId); + if (player.info.password === "fika-dedicated") + continue; + + level += player.characters.pmc.Info.Level; + } + + // Subtract by 1 if it's a dedicated session as we ignore the dedicated client's profile + const amountOfPlayers = isDedicated ? match.players.size - 1 : match.players.size; + // Get the average level + level = level / amountOfPlayers; + + // Save the current level so that we can set it back later + const originalLevel = pmcProfile.Info.Level; + pmcProfile.Info.Level = level; + // If there's more than 1 condition, this is the first time client has requested bots // Client sends every bot type it will need in raid // Use this opportunity to create and cache bots for later retreval const isFirstGen = info.conditions.length > 1; - if (isFirstGen) - { + let result: Promise; + if (isFirstGen) { // Temporary cast to remove the error caused by protected method. - return (this.botController as any).generateBotsFirstTime(info, pmcProfile, sessionId); + result = (this.botController as any).generateBotsFirstTime(info, pmcProfile, sessionId); } // Temporary cast to remove the error caused by protected method. - return (this.botController as any).returnSingleBotFromCache(sessionId, info); + result = (this.botController as any).returnSingleBotFromCache(sessionId, info); + + // Set back the original level + pmcProfile.Info.Level = originalLevel; + return result; }; }, { frequency: "Always" }, From 24a39b21448ac3d5a9571fdd4c89f655dc897973 Mon Sep 17 00:00:00 2001 From: Lacyway <20912169+Lacyway@users.noreply.github.com> Date: Tue, 23 Jul 2024 16:35:58 +0200 Subject: [PATCH 17/21] Round 'amountOfPlayers' --- src/overrides/controllers/BotController.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/overrides/controllers/BotController.ts b/src/overrides/controllers/BotController.ts index b54bf5e1..0aeed85e 100644 --- a/src/overrides/controllers/BotController.ts +++ b/src/overrides/controllers/BotController.ts @@ -63,7 +63,7 @@ export class BotControllerOverride extends Override { // Subtract by 1 if it's a dedicated session as we ignore the dedicated client's profile const amountOfPlayers = isDedicated ? match.players.size - 1 : match.players.size; // Get the average level - level = level / amountOfPlayers; + level = level / Math.round(amountOfPlayers); // Save the current level so that we can set it back later const originalLevel = pmcProfile.Info.Level; From 052fd4d726c9e4d35fe49cb903d065007b653e9a Mon Sep 17 00:00:00 2001 From: Lacyway <20912169+Lacyway@users.noreply.github.com> Date: Thu, 25 Jul 2024 13:06:09 +0200 Subject: [PATCH 18/21] Get highest profile's lvl --- src/overrides/controllers/BotController.ts | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/overrides/controllers/BotController.ts b/src/overrides/controllers/BotController.ts index 0aeed85e..24fa1caf 100644 --- a/src/overrides/controllers/BotController.ts +++ b/src/overrides/controllers/BotController.ts @@ -57,15 +57,10 @@ export class BotControllerOverride extends Override { if (player.info.password === "fika-dedicated") continue; - level += player.characters.pmc.Info.Level; + if (player.characters.pmc.Info.Level > level) + level = player.characters.pmc.Info.Level; } - // Subtract by 1 if it's a dedicated session as we ignore the dedicated client's profile - const amountOfPlayers = isDedicated ? match.players.size - 1 : match.players.size; - // Get the average level - level = level / Math.round(amountOfPlayers); - - // Save the current level so that we can set it back later const originalLevel = pmcProfile.Info.Level; pmcProfile.Info.Level = level; @@ -82,6 +77,7 @@ export class BotControllerOverride extends Override { result = (this.botController as any).returnSingleBotFromCache(sessionId, info); // Set back the original level + //pmcProfile.Info.Level = originalLevel; pmcProfile.Info.Level = originalLevel; return result; }; From fc6e1161d8a23cba714041ef62cca6307c73150a Mon Sep 17 00:00:00 2001 From: Archangel Date: Thu, 25 Jul 2024 17:29:12 +0200 Subject: [PATCH 19/21] Run headless with nographics and enable bepinex console --- src/services/dedicated/FikaDedicatedProfileService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/dedicated/FikaDedicatedProfileService.ts b/src/services/dedicated/FikaDedicatedProfileService.ts index 50120e73..dc42773a 100644 --- a/src/services/dedicated/FikaDedicatedProfileService.ts +++ b/src/services/dedicated/FikaDedicatedProfileService.ts @@ -159,7 +159,7 @@ export class FikaDedicatedProfileService { const scriptName = `Start_${profile.info.username}.bat`; const scriptPath = path.join(targetFolderPath, scriptName); - const scriptContent = `EscapeFromTarkov.exe -token=${profile.info.id} -config={"BackendUrl":"${backendUrl}","Version":"live"} -batchmode`; + const scriptContent = `EscapeFromTarkov.exe -token=${profile.info.id} -config={"BackendUrl":"${backendUrl}","Version":"live"} -batchmode -nographics --enable-console true`; try { if(!fs.existsSync(targetFolderPath)) { From 0dd744a8219c665085da4c7d80921f2eb69d80c5 Mon Sep 17 00:00:00 2001 From: Lacyway <20912169+Lacyway@users.noreply.github.com> Date: Thu, 25 Jul 2024 20:30:57 +0200 Subject: [PATCH 20/21] Support new route --- src/callbacks/FikaClientCallbacks.ts | 2 +- src/callbacks/FikaRaidCallbacks.ts | 7 ++- src/controllers/FikaRaidController.ts | 57 +++++++++++-------- .../dedicated/IGetStatusDedicatedResponse.ts | 3 + src/routers/static/FikaRaidStaticRouter.ts | 3 + 5 files changed, 47 insertions(+), 25 deletions(-) create mode 100644 src/models/fika/routes/raid/dedicated/IGetStatusDedicatedResponse.ts diff --git a/src/callbacks/FikaClientCallbacks.ts b/src/callbacks/FikaClientCallbacks.ts index ec8c87b1..58b1855d 100644 --- a/src/callbacks/FikaClientCallbacks.ts +++ b/src/callbacks/FikaClientCallbacks.ts @@ -31,7 +31,7 @@ export class FikaClientCallbacks { } /** Handle /fika/profile/download */ - public handleProfileDownload(_url: string, info: any, sessionID: string): any { + public handleProfileDownload(_url: string, _info: any, sessionID: string): any { return this.httpResponseUtil.noBody(this.fikaClientController.handleProfileDownload(sessionID)); } } diff --git a/src/callbacks/FikaRaidCallbacks.ts b/src/callbacks/FikaRaidCallbacks.ts index 7f992be8..1bdbf1f6 100644 --- a/src/callbacks/FikaRaidCallbacks.ts +++ b/src/callbacks/FikaRaidCallbacks.ts @@ -55,7 +55,12 @@ export class FikaRaidCallbacks { } /** Handle /fika/raid/dedicated/status */ - public handleRaidStatusDedicated(url: string, info: IStatusDedicatedRequest, sessionID: string): string { + public handleRaidStatusDedicated(_url: string, info: IStatusDedicatedRequest, sessionID: string): string { return this.httpResponseUtil.noBody(this.fikaRaidController.handleRaidStatusDedicated(sessionID, info)); } + + /** Handle /fika/raid/dedicated/getstatus */ + public handleRaidGetStatusDedicated(_url: string, _info: any, _sessionID: string): string { + return this.httpResponseUtil.noBody(this.fikaRaidController.handleRaidGetStatusDedicated()); + } } diff --git a/src/controllers/FikaRaidController.ts b/src/controllers/FikaRaidController.ts index c6dedfd2..3d2ca9a6 100644 --- a/src/controllers/FikaRaidController.ts +++ b/src/controllers/FikaRaidController.ts @@ -17,6 +17,7 @@ import { WebSocket } from "ws"; import { ILogger } from "@spt/models/spt/utils/ILogger"; import { IStatusDedicatedRequest } from "../models/fika/routes/raid/dedicated/IStatusDedicatedRequest"; import { IStatusDedicatedResponse } from "../models/fika/routes/raid/dedicated/IStatusDedicatedResponse"; +import { IGetStatusDedicatedResponse } from "../models/fika/routes/raid/dedicated/IGetStatusDedicatedResponse"; import { FikaDedicatedRaidWebSocket } from "../websockets/FikaDedicatedRaidWebSocket"; @injectable() @@ -53,9 +54,9 @@ export class FikaRaidController { expectedNumberOfPlayers: match.expectedNumberOfPlayers, gameVersion: match.gameVersion, fikaVersion: match.fikaVersion, - raidCode: match.raidCode + raidCode: match.raidCode, }; - } + } /** * Handle /fika/raid/leave @@ -84,7 +85,7 @@ export class FikaRaidController { ips: match.ips, port: match.port, natPunch: match.natPunch, - isDedicated: match.isDedicated + isDedicated: match.isDedicated, }; } @@ -100,7 +101,7 @@ export class FikaRaidController { return { metabolismDisabled: match.raidConfig.metabolismDisabled, - playersSpawnPlace: match.raidConfig.playersSpawnPlace + playersSpawnPlace: match.raidConfig.playersSpawnPlace, }; } @@ -109,14 +110,14 @@ export class FikaRaidController { if (!this.fikaDedicatedRaidService.isDedicatedClientAvailable()) { return { matchId: null, - error: "No dedicated clients available." + error: "No dedicated clients available.", }; } if (sessionID in this.fikaDedicatedRaidService.dedicatedClients) { return { matchId: null, - error: "A dedicated client is trying to use a dedicated client?" + error: "A dedicated client is trying to use a dedicated client?", }; } @@ -132,7 +133,7 @@ export class FikaRaidController { dedicatedClientWs = this.fikaDedicatedRaidWebSocket.clientWebSockets[dedicatedSessionId]; - if(!dedicatedClientWs) { + if (!dedicatedClientWs) { continue; } @@ -143,47 +144,57 @@ export class FikaRaidController { if (!dedicatedClient) { return { matchId: null, - error: "No dedicated clients available at this time" + error: "No dedicated clients available at this time", }; } this.fikaDedicatedRaidService.requestedSessions[dedicatedClient] = sessionID; dedicatedClientWs.send( - JSON.stringify( - { + JSON.stringify({ type: "fikaDedicatedStartRaid", - ...info - } - )); + ...info, + }), + ); this.logger.info(`Sent WS to ${dedicatedClient}`); return { // This really isn't required, I just want to make sure on the client matchId: dedicatedClient, - error: null - } + error: null, + }; } /** Handle /fika/raid/dedicated/status */ public handleRaidStatusDedicated(sessionId: string, info: IStatusDedicatedRequest): IStatusDedicatedResponse { - - if(info.status == "ready" && !this.fikaDedicatedRaidService.isDedicatedClientAvailable()) { - if(this.fikaDedicatedRaidService.onDedicatedClientAvailable) { + if (info.status == "ready" && !this.fikaDedicatedRaidService.isDedicatedClientAvailable()) { + if (this.fikaDedicatedRaidService.onDedicatedClientAvailable) { this.fikaDedicatedRaidService.onDedicatedClientAvailable(); } } - this.fikaDedicatedRaidService.dedicatedClients[sessionId] = - { + this.fikaDedicatedRaidService.dedicatedClients[sessionId] = { state: info.status, - lastPing: Date.now() - } + lastPing: Date.now(), + }; return { sessionId: info.sessionId, - status: info.status + status: info.status, + }; + } + + /** Handle /fika/raid/dedicated/getstatus */ + public handleRaidGetStatusDedicated(): IGetStatusDedicatedResponse { + if (!this.fikaDedicatedRaidService.isDedicatedClientAvailable()) { + return { + available: false + }; + } else { + return { + available: true + }; } } } diff --git a/src/models/fika/routes/raid/dedicated/IGetStatusDedicatedResponse.ts b/src/models/fika/routes/raid/dedicated/IGetStatusDedicatedResponse.ts new file mode 100644 index 00000000..bbc91974 --- /dev/null +++ b/src/models/fika/routes/raid/dedicated/IGetStatusDedicatedResponse.ts @@ -0,0 +1,3 @@ +export interface IGetStatusDedicatedResponse { + available: boolean +} diff --git a/src/routers/static/FikaRaidStaticRouter.ts b/src/routers/static/FikaRaidStaticRouter.ts index cd24322d..c55d7f9b 100644 --- a/src/routers/static/FikaRaidStaticRouter.ts +++ b/src/routers/static/FikaRaidStaticRouter.ts @@ -36,6 +36,9 @@ export class FikaRaidStaticRouter extends StaticRouter { new RouteAction("/fika/raid/dedicated/status", async (url: string, info: IStatusDedicatedRequest, sessionID: string, _output: string): Promise => { return this.fikaRaidCallbacks.handleRaidStatusDedicated(url, info, sessionID); }), + new RouteAction("/fika/raid/dedicated/getstatus", async (url: string, info: any, sessionID: string, _output: string): Promise => { + return this.fikaRaidCallbacks.handleRaidGetStatusDedicated(url, info, sessionID); + }), ]); } } From 89f633e36169ce7305b2090cbdf2796193aac020 Mon Sep 17 00:00:00 2001 From: Lacyway <20912169+Lacyway@users.noreply.github.com> Date: Thu, 25 Jul 2024 20:31:49 +0200 Subject: [PATCH 21/21] Updated dedicated build script --- src/services/dedicated/FikaDedicatedProfileService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/dedicated/FikaDedicatedProfileService.ts b/src/services/dedicated/FikaDedicatedProfileService.ts index dc42773a..59494f4d 100644 --- a/src/services/dedicated/FikaDedicatedProfileService.ts +++ b/src/services/dedicated/FikaDedicatedProfileService.ts @@ -159,7 +159,7 @@ export class FikaDedicatedProfileService { const scriptName = `Start_${profile.info.username}.bat`; const scriptPath = path.join(targetFolderPath, scriptName); - const scriptContent = `EscapeFromTarkov.exe -token=${profile.info.id} -config={"BackendUrl":"${backendUrl}","Version":"live"} -batchmode -nographics --enable-console true`; + const scriptContent = `start "" EscapeFromTarkov.exe -token=${profile.info.id} -config={"BackendUrl":"${backendUrl}","Version":"live"} -batchmode -nographics --enable-console true`; try { if(!fs.existsSync(targetFolderPath)) {