From 7feac5a55e1c71f6bb7b1d31501a9f3daf46bd0f Mon Sep 17 00:00:00 2001 From: Ipmake Date: Sat, 23 Dec 2023 15:38:54 +0100 Subject: [PATCH] Added winter theme and basic linux support --- electron-builder.yml | 2 +- package.json | 2 +- src/main/index.ts | 56 +++++++++++-------- src/preload/index.d.ts | 1 + src/preload/index.ts | 17 ++++-- src/renderer/src/App.css | 8 +++ src/renderer/src/components/ServersMenu.tsx | 2 +- src/renderer/src/components/SnowFall.tsx | 59 +++++++++++++++++++++ src/renderer/src/main.tsx | 5 +- 9 files changed, 120 insertions(+), 32 deletions(-) create mode 100644 src/renderer/src/components/SnowFall.tsx diff --git a/electron-builder.yml b/electron-builder.yml index 1970628..704fdd2 100644 --- a/electron-builder.yml +++ b/electron-builder.yml @@ -25,7 +25,7 @@ linux: target: - AppImage # - snap - # - deb + - deb maintainer: kocity.xyz category: Game appImage: diff --git a/package.json b/package.json index 1a9f3af..3a2fae5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "knockoutcitylauncher", - "version": "2.0.0", + "version": "2.0.1", "description": "Unofficial Knockout City Launcher", "main": "./out/main/index.js", "author": "IPGsystems", diff --git a/src/main/index.ts b/src/main/index.ts index 2090c3e..0ed62d0 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -161,35 +161,49 @@ function createWindow(): void { win.focus() }) - ipcMain.on('clean-gamedir-mods', async (event, args: { basePath: string; gameVersion: number; }) => { - event.returnValue = undefined + 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 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 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 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}) + const versionsPath = path.join(gameDirPath, 'version.json') + fs.rmSync(versionsPath, { recursive: true, force: true }) + + event.sender.send('cleaned-gamedir-mods') + } + ) - event.sender.send('cleaned-gamedir-mods') - }) - ipcMain.on( 'install-server-mods', - async (event, args: { basePath: string; gameVersion: number; server: { name: string, addr: string } }) => { + 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 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 () => { + const downloadMods = async (): Promise => { if (result.length === 0) { return } @@ -478,7 +492,7 @@ function createWindow(): void { ? `mkdir "${arg.path}" && icacls "${arg.path}" /grant "${ os.userInfo().username }":(OI)(CI)F /T` - : `mkdir "${arg.path}" && chown -R ${os.userInfo().username} "${arg.path}"`, + : `mkdir -p "${arg.path}" && chown -R ${os.userInfo().username} "${arg.path}"`, { name: 'Knockout City Launcher' }, (error) => { if (error) reject(new Error(error.message)), console.log(error) @@ -891,11 +905,9 @@ function createWindow(): void { contextIsolation: false } }) - if (is.dev && process.env['ELECTRON_RENDERER_URL']) { - cmd.loadURL(process.env['ELECTRON_RENDERER_URL'] + '/shell.html') - } else { - cmd.loadFile(path.join(__dirname, '../renderer/shell.html')) - } + if (is.dev && process.env['ELECTRON_RENDERER_URL']) + cmd.loadFile(path.join(process.env['ELECTRON_RENDERER_URL'], '../../resources/shell.html')) + else cmd.loadFile(path.join(__dirname, '../../resources/shell.html')) } server.stdout.on('data', (data) => { diff --git a/src/preload/index.d.ts b/src/preload/index.d.ts index e213b75..3842b47 100644 --- a/src/preload/index.d.ts +++ b/src/preload/index.d.ts @@ -8,6 +8,7 @@ declare global { interface Window { version: string + isLinux: boolean electron: typeof electron getCurrentWindow: () => Electron.BrowserWindow openMenu: (x: number, y: number) => void diff --git a/src/preload/index.ts b/src/preload/index.ts index f2e91fa..53a63b6 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -1,14 +1,18 @@ import { ipcRenderer } from 'electron' import { getCurrentWindow } from '@electron/remote' import { BrowserWindow } from 'electron' +const os = require('os') // Use `contextBridge` APIs to expose Electron APIs to // renderer only if context isolation is enabled, otherwise // just add to the DOM global. +// @ts-ignore (define in dts) +window.isLinux = os.platform() === 'linux' + if (!localStorage.getItem('username')) { // get username from system - const username = require('os').userInfo().username + const username = os.userInfo().username localStorage.setItem('username', username) } if (!localStorage.getItem('gameVersion')) localStorage.setItem('gameVersion', '1') @@ -17,6 +21,11 @@ if (!localStorage.getItem('currServerName')) localStorage.setItem('currServerNam if (!localStorage.getItem('currServer')) localStorage.setItem('currServer', '127.0.0.1') if (localStorage.getItem('servers') === null) localStorage.setItem('servers', '[{"name":"localhost","ip":"127.0.0.1"}]') +if (!localStorage.getItem('gameDirectory')) + localStorage.setItem( + 'gameDirectory', + os.Platform() === 'win32' ? 'C:/Program Files/KOCity' : `${os.homedir()}/Games/KOCity` + ) if (localStorage.getItem('discordRPC:enabled') === null) localStorage.setItem('discordRPC:enabled', 'true') @@ -111,7 +120,7 @@ window.addEventListener('DOMContentLoaded', () => { basePath: localStorage.getItem('gameDirectory'), gameVersion: localStorage.getItem('gameVersion') }) - }) + }) } // @ts-ignore (define in dts) @@ -124,10 +133,10 @@ window.addEventListener('DOMContentLoaded', () => { gameVersion: localStorage.getItem('gameVersion'), server: { name: localStorage.getItem('currServerName'), - addr: localStorage.getItem('currServer'), + addr: localStorage.getItem('currServer') } }) - }) + }) } // @ts-ignore (define in dts) diff --git a/src/renderer/src/App.css b/src/renderer/src/App.css index 713edf3..b970c6c 100644 --- a/src/renderer/src/App.css +++ b/src/renderer/src/App.css @@ -193,4 +193,12 @@ html, body { .buttonCell { width: 40px !important; max-width: 40px !important; +} + +.snowflake { + position: absolute; + aspect-ratio: 1/1; + + background: white; + border-radius: 50%; } \ No newline at end of file diff --git a/src/renderer/src/components/ServersMenu.tsx b/src/renderer/src/components/ServersMenu.tsx index ad2db64..a8a2c6d 100644 --- a/src/renderer/src/components/ServersMenu.tsx +++ b/src/renderer/src/components/ServersMenu.tsx @@ -55,7 +55,7 @@ function ServersMenu(): JSX.Element { > - + { + const parent = document.getElementById('snowfall') + for (let i = 0; i < snowflakeCount; i++) { + const x = Math.random() * window.innerWidth + const y = Math.random() * window.innerHeight + const size = Math.random() * snowflakeSize + const speed = Math.random() * snowflakeSpeed + + const snowflake = document.createElement('div') + snowflake.classList.add('snowflake') + snowflake.id = `snowflake-${i}` + snowflake.style.width = `${size}px` + + snowflake.style.left = `${x}px` + snowflake.style.top = `${y}px` + snowflake.style.animationDuration = `${speed}s` + + parent?.appendChild(snowflake) + } + const interval = setInterval(() => { + const snowflakes = document.querySelectorAll('.snowflake') + snowflakes.forEach((snowflake: Element, index) => { + // update snowflake position + const top = parseFloat((snowflake as HTMLElement).style.top) + const speed = parseFloat((snowflake as HTMLElement).style.animationDuration) + ;(snowflake as HTMLElement).style.top = `${top + speed}px` + // reset snowflake position if it goes off screen + if (snowflake.getBoundingClientRect().top > window.innerHeight) + document.getElementById(`snowflake-${index}`)?.style.setProperty('top', '0px') + }) + }, 1000 / 60) + return () => clearInterval(interval) + }, []) + + return ( + + ) +} + +export default SnowFall diff --git a/src/renderer/src/main.tsx b/src/renderer/src/main.tsx index ba7e1d7..b322fbc 100644 --- a/src/renderer/src/main.tsx +++ b/src/renderer/src/main.tsx @@ -2,6 +2,7 @@ import ReactDOM from 'react-dom/client' import './index.css' import App from './App' import { CssBaseline, ThemeProvider, createTheme } from '@mui/material' +import SnowFall from './components/SnowFall' const darkTheme = createTheme({ palette: { @@ -15,13 +16,11 @@ const darkTheme = createTheme({ } }) -if (!localStorage.getItem('gameDirectory')) - localStorage.setItem('gameDirectory', 'C:/Program Files/KOCity') - const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement) root.render( + )