From 31e2536d696142e411fc4a99adb317e998bafd77 Mon Sep 17 00:00:00 2001 From: Tandashi <18377875+Tandashi@users.noreply.github.com> Date: Fri, 17 May 2024 16:16:52 +0200 Subject: [PATCH 1/2] refactor: remove server-mods as they are depricated --- src/main/index.ts | 89 ------------------- src/preload/index.d.ts | 3 - src/preload/index.ts | 28 ------ src/renderer/src/components/LaunchSection.tsx | 5 -- src/renderer/src/components/popUp/index.tsx | 3 - .../popUp/views/InstallingServerMods.tsx | 37 -------- 6 files changed, 165 deletions(-) delete mode 100644 src/renderer/src/components/popUp/views/InstallingServerMods.tsx diff --git a/src/main/index.ts b/src/main/index.ts index 9ee27ce..60be798 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -13,8 +13,6 @@ import killProcess from 'tree-kill' import discordRPC from 'discord-rpc' import { is } from '@electron-toolkit/utils' import { IncomingMessage } from 'http' -import lodash from 'lodash' -import JSZip from 'jszip' remote.initialize() @@ -161,93 +159,6 @@ function createWindow(): void { win.focus() }) - ipcMain.on( - 'clean-gamedir-mods', - async (event, args: { basePath: string; gameVersion: number }) => { - event.returnValue = undefined - - const gameDirPath = path.join( - args.basePath, - args.gameVersion == 1 ? 'highRes' : 'lowRes', - 'KnockoutCity' - ) - - const outDirPath = path.join(gameDirPath, 'out') - fs.rmSync(outDirPath, { recursive: true, force: true }) - - const viperRootPath = path.join(gameDirPath, '.viper_root') - fs.rmSync(viperRootPath, { recursive: true, force: true }) - - const versionsPath = path.join(gameDirPath, 'version.json') - fs.rmSync(versionsPath, { recursive: true, force: true }) - - event.sender.send('cleaned-gamedir-mods') - } - ) - - ipcMain.on( - 'install-server-mods', - async ( - event, - args: { basePath: string; gameVersion: number; server: { name: string; addr: string } } - ) => { - event.returnValue = undefined - - const serverModsDownloadPath = path.join(args.basePath, 'downloads', 'mods', args.server.name) - const serverModsVersionPath = path.join(serverModsDownloadPath, 'version.json') - const gameDirPath = path.join( - args.basePath, - args.gameVersion == 1 ? 'highRes' : 'lowRes', - 'KnockoutCity' - ) - - const result = (await axios.get(`http://${args.server.addr}/mods/list`)).data - - const downloadMods = async (): Promise => { - if (result.length === 0) { - return - } - - const content = await ( - await fetch(`http://${args.server.addr}/mods/download`) - ).arrayBuffer() - - const zip = new JSZip() - await zip.loadAsync(content).then(async (contents) => { - for (const filename of Object.keys(contents.files)) { - await zip - .file(filename) - ?.async('nodebuffer') - .then((content) => { - const filePath = path.join(serverModsDownloadPath, filename) - fs.mkdirSync(path.dirname(filePath), { recursive: true }) - fs.writeFileSync(filePath, content) - }) - } - }) - - fs.rmSync(serverModsDownloadPath, { recursive: true, force: true }) - fs.mkdirSync(serverModsDownloadPath, { recursive: true }) - fs.writeFileSync(serverModsVersionPath, JSON.stringify(result, null, 2)) - } - - fs.mkdirSync(serverModsDownloadPath, { recursive: true }) - if (fs.existsSync(serverModsVersionPath) && fs.statSync(serverModsVersionPath).isFile()) { - const versions = JSON.parse(fs.readFileSync(serverModsVersionPath).toString('utf-8')) - - if (!lodash.isEqual(versions, result)) { - await downloadMods() - } - } else { - await downloadMods() - } - - fse.copySync(serverModsDownloadPath, gameDirPath) - - event.sender.send('installed-server-mods') - } - ) - ipcMain.on('set-RPCstate', async (event, arg) => { event.returnValue = 'ok' diff --git a/src/preload/index.d.ts b/src/preload/index.d.ts index 3842b47..bcbbe02 100644 --- a/src/preload/index.d.ts +++ b/src/preload/index.d.ts @@ -23,9 +23,6 @@ declare global { getGameState: () => 'installed' | 'deprecated' | 'notInstalled' getGameInstalls: () => string[] - cleanGameDirMods: () => Promise - installServerMods: () => Promise - installGame: (props: { setGameState: (state: 'installed' | 'deprecated' | 'notInstalled' | 'installing') => void setInstallData: (data: number) => void diff --git a/src/preload/index.ts b/src/preload/index.ts index 425eb56..e8a8972 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -111,34 +111,6 @@ window.addEventListener('DOMContentLoaded', () => { return result } - // @ts-ignore (define in dts) - window.cleanGameDirMods = (): Promise => { - return new Promise((resolve) => { - ipcRenderer.once('cleaned-gamedir-mods', () => resolve()) - - ipcRenderer.sendSync('clean-gamedir-mods', { - basePath: localStorage.getItem('gameDirectory'), - gameVersion: localStorage.getItem('gameVersion') - }) - }) - } - - // @ts-ignore (define in dts) - window.installServerMods = (): Promise => { - return new Promise((resolve) => { - ipcRenderer.once('installed-server-mods', () => resolve()) - - ipcRenderer.sendSync('install-server-mods', { - basePath: localStorage.getItem('gameDirectory'), - gameVersion: localStorage.getItem('gameVersion'), - server: { - name: localStorage.getItem('currServerName'), - addr: localStorage.getItem('currServer') - } - }) - }) - } - // @ts-ignore (define in dts) window.getGameInstalls = (): string[] => { const result = ipcRenderer.sendSync('get-game-installs', { diff --git a/src/renderer/src/components/LaunchSection.tsx b/src/renderer/src/components/LaunchSection.tsx index d78d157..28dd16e 100644 --- a/src/renderer/src/components/LaunchSection.tsx +++ b/src/renderer/src/components/LaunchSection.tsx @@ -50,8 +50,6 @@ function LaunchSection(): JSX.Element { setCurrServerName(localStorage.getItem('currServerName') || 'localhost') setCurrServerType(localStorage.getItem('currServerType') || 'private') - await window.cleanGameDirMods() - if (localStorage.getItem('currServerType') === 'public') { setPopUpState('authenticating') if ( @@ -62,9 +60,6 @@ function LaunchSection(): JSX.Element { return alert('You must be logged in to use public servers!') } - setPopUpState('installing-server-mods') - await window.installServerMods() - setPopUpState('authenticating') const res = await axios diff --git a/src/renderer/src/components/popUp/index.tsx b/src/renderer/src/components/popUp/index.tsx index 69ceb32..47bdfb4 100644 --- a/src/renderer/src/components/popUp/index.tsx +++ b/src/renderer/src/components/popUp/index.tsx @@ -11,7 +11,6 @@ import AccountSettings from './views/AccountSettings' // States import { useUIState } from '@renderer/states/uiState' import { useAuthState } from '@renderer/states/authState' -import InstallingServerMods from './views/InstallingServerMods' function PopUp(): JSX.Element { const [popUpLoading, setPopUpLoading] = useState(false) @@ -272,8 +271,6 @@ function PopUp(): JSX.Element { return case 'accountSettings': return - case 'installing-server-mods': - return default: return <> } diff --git a/src/renderer/src/components/popUp/views/InstallingServerMods.tsx b/src/renderer/src/components/popUp/views/InstallingServerMods.tsx deleted file mode 100644 index 964cc70..0000000 --- a/src/renderer/src/components/popUp/views/InstallingServerMods.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import { Backdrop, Box, LinearProgress, Typography } from '@mui/material' - -export default function InstallingServerMods(): JSX.Element { - return ( - - - - Installing Server Mods - -

Hang on while we are trying to install the required server mods

- -
-
- ) -} From 74d6e11aeef5d435f432999225e0c6d659ad56fe Mon Sep 17 00:00:00 2001 From: Tandashi <18377875+Tandashi@users.noreply.github.com> Date: Fri, 17 May 2024 16:33:21 +0200 Subject: [PATCH 2/2] feat: add patching of game client --- src/main/index.ts | 66 +++++++++++++++++++ src/preload/index.d.ts | 2 + src/preload/index.ts | 12 ++++ src/renderer/src/components/LaunchSection.tsx | 3 + src/renderer/src/components/popUp/index.tsx | 3 + .../popUp/views/PatchGameClient.tsx | 39 +++++++++++ 6 files changed, 125 insertions(+) create mode 100644 src/renderer/src/components/popUp/views/PatchGameClient.tsx diff --git a/src/main/index.ts b/src/main/index.ts index 60be798..19ea8cf 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -159,6 +159,72 @@ function createWindow(): void { win.focus() }) + ipcMain.on( + 'patch-game-client', + async (event, args: { basePath: string; gameVersion: number }) => { + event.returnValue = undefined + + const gameDirPath = path.join( + args.basePath, + args.gameVersion == 1 ? 'highRes' : 'lowRes', + 'KnockoutCity' + ) + + const gameExePath = path.join(gameDirPath, 'KnockoutCity.exe') + const backupGameExePath = path.join(gameDirPath, 'KnockoutCity.exe.bak') + + if (!fse.existsSync(backupGameExePath)) { + console.log(`Backing up ${gameExePath} to ${backupGameExePath}`) + fse.copySync(gameExePath, backupGameExePath) + } + + let data = await fse.readFile(backupGameExePath).catch((error) => { + console.error('Failed to read Game File:', error) + throw Error('Failed to read Game File') + }) + + const patches: { + name: string + startAddress: number + endAddress: number + replacement: () => Buffer + }[] = [ + { + name: 'Signature Verification', + startAddress: 0x3cad481, + endAddress: 0x3cad485, + replacement: () => Buffer.from([0xb8, 0x01, 0x00, 0x00]) + }, + { + name: 'Auth Provider', + startAddress: 0x4f97230, + endAddress: 0x4f97233, + replacement: () => Buffer.from([0x78, 0x79, 0x7a]) + } + ] + + for (const patch of patches) { + console.log(`Patching ${patch.name}...`) + const startBuffer = data.subarray(0, patch.startAddress) + const endBuffer = data.subarray(patch.endAddress) + + console.log('data pre') + console.log(data.subarray(patch.startAddress, patch.endAddress)) + data = Buffer.concat([startBuffer, patch.replacement(), endBuffer]) + + console.log('data post') + console.log(data.subarray(patch.startAddress, patch.endAddress)) + } + + await fse.writeFile(gameExePath, data).catch((error) => { + console.error('Failed to write patched Game File:', error) + throw Error('Failed to write patched Game File') + }) + + event.sender.send('patched-game-client') + } + ) + ipcMain.on('set-RPCstate', async (event, arg) => { event.returnValue = 'ok' diff --git a/src/preload/index.d.ts b/src/preload/index.d.ts index bcbbe02..1e0c742 100644 --- a/src/preload/index.d.ts +++ b/src/preload/index.d.ts @@ -23,6 +23,8 @@ declare global { getGameState: () => 'installed' | 'deprecated' | 'notInstalled' getGameInstalls: () => string[] + patchGameClient: () => Promise + installGame: (props: { setGameState: (state: 'installed' | 'deprecated' | 'notInstalled' | 'installing') => void setInstallData: (data: number) => void diff --git a/src/preload/index.ts b/src/preload/index.ts index e8a8972..10ea583 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -111,6 +111,18 @@ window.addEventListener('DOMContentLoaded', () => { return result } + // @ts-ignore (define in dts) + window.patchGameClient = (): Promise => { + return new Promise((resolve) => { + ipcRenderer.once('patched-game-client', () => resolve()) + + ipcRenderer.sendSync('patch-game-client', { + basePath: localStorage.getItem('gameDirectory'), + gameVersion: localStorage.getItem('gameVersion') + }) + }) + } + // @ts-ignore (define in dts) window.getGameInstalls = (): string[] => { const result = ipcRenderer.sendSync('get-game-installs', { diff --git a/src/renderer/src/components/LaunchSection.tsx b/src/renderer/src/components/LaunchSection.tsx index 28dd16e..6c511ea 100644 --- a/src/renderer/src/components/LaunchSection.tsx +++ b/src/renderer/src/components/LaunchSection.tsx @@ -50,6 +50,9 @@ function LaunchSection(): JSX.Element { setCurrServerName(localStorage.getItem('currServerName') || 'localhost') setCurrServerType(localStorage.getItem('currServerType') || 'private') + setPopUpState('patchGameClient') + await window.patchGameClient() + if (localStorage.getItem('currServerType') === 'public') { setPopUpState('authenticating') if ( diff --git a/src/renderer/src/components/popUp/index.tsx b/src/renderer/src/components/popUp/index.tsx index 47bdfb4..581c184 100644 --- a/src/renderer/src/components/popUp/index.tsx +++ b/src/renderer/src/components/popUp/index.tsx @@ -7,6 +7,7 @@ import Authenticating from './views/Authenticating' import ConfirmLogout from './views/ConfirmLogout' import SelectUninstall from './views/SelectUninstall' import AccountSettings from './views/AccountSettings' +import PatchGameClient from './views/PatchGameClient' // States import { useUIState } from '@renderer/states/uiState' @@ -271,6 +272,8 @@ function PopUp(): JSX.Element { return case 'accountSettings': return + case 'patchGameClient': + return default: return <> } diff --git a/src/renderer/src/components/popUp/views/PatchGameClient.tsx b/src/renderer/src/components/popUp/views/PatchGameClient.tsx new file mode 100644 index 0000000..353bbb2 --- /dev/null +++ b/src/renderer/src/components/popUp/views/PatchGameClient.tsx @@ -0,0 +1,39 @@ +import { Backdrop, Box, Typography, LinearProgress } from '@mui/material' + +function PatchGameClient(): JSX.Element { + return ( + + + + Patch Game Client + +

Patching the Game Client...

+ +
+
+ ) +} + +export default PatchGameClient