Skip to content

Commit

Permalink
Merge pull request #99 from kocxyz/refactor/remove-server-mods
Browse files Browse the repository at this point in the history
Remove Server Mods and patch game client
  • Loading branch information
Tandashi authored May 18, 2024
2 parents d85944d + 74d6e11 commit 3c3023a
Show file tree
Hide file tree
Showing 6 changed files with 60 additions and 100 deletions.
109 changes: 43 additions & 66 deletions src/main/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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()

Expand Down Expand Up @@ -162,7 +160,7 @@ function createWindow(): void {
})

ipcMain.on(
'clean-gamedir-mods',
'patch-game-client',
async (event, args: { basePath: string; gameVersion: number }) => {
event.returnValue = undefined

Expand All @@ -172,79 +170,58 @@ function createWindow(): void {
'KnockoutCity'
)

const outDirPath = path.join(gameDirPath, 'out')
fs.rmSync(outDirPath, { recursive: true, force: true })
const gameExePath = path.join(gameDirPath, 'KnockoutCity.exe')
const backupGameExePath = path.join(gameDirPath, 'KnockoutCity.exe.bak')

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'
)
if (!fse.existsSync(backupGameExePath)) {
console.log(`Backing up ${gameExePath} to ${backupGameExePath}`)
fse.copySync(gameExePath, backupGameExePath)
}

const result = (await axios.get(`http://${args.server.addr}/mods/list`)).data
let data = await fse.readFile(backupGameExePath).catch((error) => {
console.error('Failed to read Game File:', error)
throw Error('Failed to read Game File')
})

const downloadMods = async (): Promise<void> => {
if (result.length === 0) {
return
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])
}
]

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))
}
for (const patch of patches) {
console.log(`Patching ${patch.name}...`)
const startBuffer = data.subarray(0, patch.startAddress)
const endBuffer = data.subarray(patch.endAddress)

fs.mkdirSync(serverModsDownloadPath, { recursive: true })
if (fs.existsSync(serverModsVersionPath) && fs.statSync(serverModsVersionPath).isFile()) {
const versions = JSON.parse(fs.readFileSync(serverModsVersionPath).toString('utf-8'))
console.log('data pre')
console.log(data.subarray(patch.startAddress, patch.endAddress))
data = Buffer.concat([startBuffer, patch.replacement(), endBuffer])

if (!lodash.isEqual(versions, result)) {
await downloadMods()
}
} else {
await downloadMods()
console.log('data post')
console.log(data.subarray(patch.startAddress, patch.endAddress))
}

fse.copySync(serverModsDownloadPath, gameDirPath)
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('installed-server-mods')
event.sender.send('patched-game-client')
}
)

Expand Down
3 changes: 1 addition & 2 deletions src/preload/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,7 @@ declare global {
getGameState: () => 'installed' | 'deprecated' | 'notInstalled'
getGameInstalls: () => string[]

cleanGameDirMods: () => Promise<void>
installServerMods: () => Promise<void>
patchGameClient: () => Promise<void>

installGame: (props: {
setGameState: (state: 'installed' | 'deprecated' | 'notInstalled' | 'installing') => void
Expand Down
22 changes: 3 additions & 19 deletions src/preload/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,33 +112,17 @@ window.addEventListener('DOMContentLoaded', () => {
}

// @ts-ignore (define in dts)
window.cleanGameDirMods = (): Promise<void> => {
window.patchGameClient = (): Promise<void> => {
return new Promise((resolve) => {
ipcRenderer.once('cleaned-gamedir-mods', () => resolve())
ipcRenderer.once('patched-game-client', () => resolve())

ipcRenderer.sendSync('clean-gamedir-mods', {
ipcRenderer.sendSync('patch-game-client', {
basePath: localStorage.getItem('gameDirectory'),
gameVersion: localStorage.getItem('gameVersion')
})
})
}

// @ts-ignore (define in dts)
window.installServerMods = (): Promise<void> => {
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', {
Expand Down
8 changes: 3 additions & 5 deletions src/renderer/src/components/LaunchSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,9 @@ function LaunchSection(): JSX.Element {
setCurrServerName(localStorage.getItem('currServerName') || 'localhost')
setCurrServerType(localStorage.getItem('currServerType') || 'private')

await window.cleanGameDirMods()

setPopUpState('patchGameClient')
await window.patchGameClient()

if (localStorage.getItem('currServerType') === 'public') {
setPopUpState('authenticating')
if (
Expand All @@ -62,9 +63,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
Expand Down
6 changes: 3 additions & 3 deletions src/renderer/src/components/popUp/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ 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'
import { useAuthState } from '@renderer/states/authState'
import InstallingServerMods from './views/InstallingServerMods'

function PopUp(): JSX.Element {
const [popUpLoading, setPopUpLoading] = useState(false)
Expand Down Expand Up @@ -272,8 +272,8 @@ function PopUp(): JSX.Element {
return <SelectUninstall />
case 'accountSettings':
return <AccountSettings />
case 'installing-server-mods':
return <InstallingServerMods />
case 'patchGameClient':
return <PatchGameClient />
default:
return <></>
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Backdrop, Box, LinearProgress, Typography } from '@mui/material'
import { Backdrop, Box, Typography, LinearProgress } from '@mui/material'

export default function InstallingServerMods(): JSX.Element {
function PatchGameClient(): JSX.Element {
return (
<Backdrop open={true} style={{ zIndex: 1000 }}>
<Box
Expand All @@ -10,7 +10,7 @@ export default function InstallingServerMods(): JSX.Element {
alignItems: 'center',
backgroundColor: '#1a1a1a',
width: '600px',
height: '200px',
height: '175px',
borderRadius: '5px',
padding: '10px',
textAlign: 'center'
Expand All @@ -27,11 +27,13 @@ export default function InstallingServerMods(): JSX.Element {
letterSpacing: '2px'
}}
>
Installing Server Mods
Patch Game Client
</Typography>
<p>Hang on while we are trying to install the required server mods</p>
<p>Patching the Game Client...</p>
<LinearProgress color="secondary" style={{ opacity: 1, width: '200px' }} />
</Box>
</Backdrop>
)
}

export default PatchGameClient

0 comments on commit 3c3023a

Please sign in to comment.