diff --git a/package.json b/package.json index a271bc6..56596be 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "knockoutcitylauncher", "description": "Unofficial Knockout City Launcher", - "version": "0.3.0", + "version": "0.3.1", "private": true, "homepage": "./", "main": "public/electron.js", diff --git a/public/electron.js b/public/electron.js index c818b0c..d9478cc 100644 --- a/public/electron.js +++ b/public/electron.js @@ -20,6 +20,8 @@ let rpcSettings = {} const gotTheLock = app.requestSingleInstanceLock() +let mainWindow; + function createWindow () { // Create the browser window. const win = new BrowserWindow({ @@ -28,6 +30,7 @@ function createWindow () { frame: false, resizable: false, movable: true, + icon: app.isPackaged ? undefined : 'public/icon.png', titleBarStyle: 'hidden', webPreferences: { nodeIntegration: true, @@ -37,6 +40,8 @@ function createWindow () { } }) + mainWindow = win + remote.enable(win.webContents) //load the index.html from a url @@ -109,15 +114,84 @@ function createWindow () { const result = await dialog.showOpenDialog(win, { properties: ['openDirectory'] }) - if (!result.canceled) { - // write it to localStorage - // win.webContents.executeJavaScript(`localStorage.setItem("gameDirectory", "${result.filePaths[0].replaceAll("\\", "/")}")`) + console.log(result) + if (!result.canceled) { + + if(result.filePaths[0].replaceAll("\\", "/") == arg.path) { + event.returnValue = null + return dialog.showErrorBox("Error", "Selected directory is identical.") + } + + const files = fs.readdirSync(arg.path) + + if(files.length != 0) { + let hasDownloadFiles = false; + for(let file of files) if(file.startsWith("files-") && file.endsWith(".zip")) hasDownloadFiles = true + + if(files.includes("highRes") || files.includes("lowRes") || hasDownloadFiles) { + + const action = await dialog.showMessageBox(win, { + type: "warning", + title: "Warning", + message: "The previous directory contains traces of a KOcity installation. What do you want to do with the previous installation?", + buttons: ["Keep", "Move", "Delete", "Cancel"] + }) + console.log(action) + switch(action.response) { + case 0: + // Keep + event.returnValue = result + break; + case 1: + // Move + // Check for permissions + try { + let res = await setUpPermission(result.filePaths[0]) + console.log(res) + } catch (error) { + console.log(error) + dialog.showErrorBox("Error", "Failed to set up permissions. Please try again.") + return event.returnValue = null + } + + try { + await new Promise((resolve, reject) => { + for(let file of files) { + if(file.startsWith("files-") && file.endsWith(".zip") || file == "highRes" || file == "lowRes") { + fs.renameSync(`${arg.path}/${file}`, `${result.filePaths[0]}/${file}`) + } + } + resolve() + }) + } catch (error) { + console.error(error) + dialog.showErrorBox("Error", "Failed to copy files. " + error) + return event.returnValue = null + } + event.returnValue = result + break; + case 2: + // Delete + await new Promise((resolve, reject) => { + fs.rmSync(arg.path, { recursive: true }) + resolve() + }) + event.returnValue = result + break; + case 3: + // Cancel + event.returnValue = null + break; + } + + } + } else { + event.returnValue = result + } - // send it back to the renderer - // win.webContents.send('selected-dirs', result.filePaths[0].replaceAll("\\", "/")) - event.returnValue = result } else { event.returnValue = null + console.log("NotSent") } }) @@ -144,42 +218,40 @@ function createWindow () { event.returnValue = "downloading" // Check write permissions - if(!fs.existsSync(arg.path)) { + if (!fs.existsSync(arg.path)) { // check if this process is allowed to write to the directory try { - fs.mkdirSync(arg.path) + fs.mkdirSync(arg.path); } catch (error) { - console.log(error.message) + console.log(error.message); // Make the directory using sudoer and edit the permissions of the directory to allow everyone to write to it windows only - await new Promise((resolve, reject) => { sudo.exec(`mkdir "${arg.path}" && icacls "${arg.path}" /grant ${os.userInfo().username}:(OI)(CI)F /T`, { name: 'Knockout City Launcher' }, (error, stdout, stderr) => { - if(error) reject("Could not create directory") + if (error) + reject("Could not create directory"); else { - resolve() - // sudo.exec(`icacls "${arg.path}" /grant ${os.userInfo().username}:(OI)(CI)F /T`, { name: 'Knockout City Launcher' }, (error, stdout, stderr) => { - // if(error) reject("Could not raise permissions") - // else resolve() - // }) + resolve(); } - }) - }) + }); + }); } } else { // check if this process is allowed to write to the directory try { - fs.writeFileSync(`${arg.path}/test.txt`, "test") + fs.writeFileSync(`${arg.path}/test.txt`, "test"); } catch (error) { - console.log(error.message) + console.log(error.message); // Make the directory using sudoer and edit the permissions of the directory to allow everyone to write to it windows only await new Promise((resolve, reject) => { sudo.exec(`icacls "${arg.path}" /grant ${os.userInfo().username}:(OI)(CI)F /T`, { name: 'Knockout City Launcher' }, (error, stdout, stderr) => { - if(error) reject("Could not raise permissions"), console.log(error) - else resolve() - }) - }) + if (error) + reject("Could not raise permissions"), console.log(error); + else + resolve(); + }); + }); } finally { - fs.existsSync(`${arg.path}/test.txt`) && fs.unlinkSync(`${arg.path}/test.txt`) + fs.existsSync(`${arg.path}/test.txt`) && fs.unlinkSync(`${arg.path}/test.txt`); } } @@ -193,6 +265,10 @@ function createWindow () { // Check if there are redundant files (() => { console.log("Checking for redundant files") + + if(fs.existsSync(`${arg.path}/files-1.zip`)) fs.rmSync(`${arg.path}/files-1.zip`) + if(fs.existsSync(`${arg.path}/files-2.zip`)) fs.rmSync(`${arg.path}/files-2.zip`) + const files = fs.readdirSync(arg.path) for (const file of files) { @@ -600,10 +676,51 @@ app.on('activate', () => { } }) +async function setUpPermission(path) { + // check if this process is allowed to write to the directory + try { + fs.writeFileSync(path + "/test.txt", "test") + console.log(`Writable ${fs.existsSync(path + "/test.txt")}`) + console.log("Has permissions") + } catch (error) { + console.log(error.message); + // Make the directory using sudoer and edit the permissions of the directory to allow everyone to write to it windows only + await new Promise((resolve, reject) => { + sudo.exec(`icacls "${path}" /grant ${os.userInfo().username}:(OI)(CI)F /T`, { name: 'Knockout City Launcher' }, (error, stdout, stderr) => { + if (error) reject("Could not raise permissions"), console.log(error); + else resolve(); + }); + }).catch((err) => { + return Promise.reject(err); + }); + } finally { + if(fs.existsSync(path + "/test.txt")) fs.rmSync(path + "/test.txt") + return Promise.resolve(); + } +} + // In this file you can include the rest of your app's specific main process // code. You can also put them in separate files and require them here. // Function that rounds a number to a certain number of decimal places function roundToDecimalPlace(number, decimalPlaces) { return Math.round(number * Math.pow(10, decimalPlaces)) / Math.pow(10, decimalPlaces); -} \ No newline at end of file +} + +process.on('uncaughtException', function (err) { + dialog.showMessageBox(mainWindow, { + type: "error", + title: "Unexpected Error", + message: "An unexpected error occurred. Error: " + err.message + }) + console.log(err); +}); + +process.on('unhandledRejection', function (err) { + dialog.showMessageBox(mainWindow, { + type: "error", + title: "Unexpected Error", + message: "An unexpected error occurred. Error: " + err.message + }) + console.log(err); +}); \ No newline at end of file diff --git a/public/icon.png b/public/icon.png index be523d5..fd11b57 100644 Binary files a/public/icon.png and b/public/icon.png differ diff --git a/public/preload.js b/public/preload.js index f22bd65..9531f7f 100644 --- a/public/preload.js +++ b/public/preload.js @@ -54,8 +54,11 @@ window.addEventListener("DOMContentLoaded", () => { window.closeWindow = closeWindow; window.selectGameDir = async () => { - const result = await ipcRenderer.sendSync('select-dirs') - if (!result) { + const result = await ipcRenderer.sendSync('select-dirs', { + path: localStorage.getItem("gameDirectory"), + }) + console.log(result) + if (result && !result.error) { return Promise.resolve(result.filePaths[0].replaceAll("\\", "/")) } } diff --git a/src/App.css b/src/App.css index 19b1e3c..ca5adc2 100644 --- a/src/App.css +++ b/src/App.css @@ -1,117 +1,178 @@ -.App { - display: grid; - grid-template-columns: 1.25fr 1fr; - grid-template-rows: 1fr; - margin-top: 3vh; -} - -html, body { - overflow: hidden; -} - -.bgImg { - position: absolute; - top: 0; - left: 0; - overflow: hidden; - overflow-x: hidden; - white-space: nowrap; - height: 100%; - z-index: -1; - /* Darken the img */ - filter: brightness(0.25); -} - -.wrapper { - display: flex; - justify-content: center; -} - -@keyframes slideL { - 0% { - transform: translateX(-20px); - } - 100% { - transform: translateX(0); - } -} - -@keyframes slideR { - 0% { - transform: translateX(-20px); - } - 100% { - transform: translateX(-40px); - } -} - -.cta { - display: flex; - padding: 10px 45px; - text-decoration: none; - font-family: 'Poppins', sans-serif; - font-size: 40px; - color: white; - background: #6225e6; - transition: 1s; - box-shadow: 6px 6px 0 black; - transform: skewX(-15deg); - cursor: pointer; -} - -.cta:focus { - outline: none; -} - -.cta:hover { - transition: 0.5s; - box-shadow: 7px 7px 0 #FBC638; -} - -.cta span:nth-child(2) { - transition: 0.5s; - margin-right: 0px; -} - -.cta:hover span:nth-child(2) { - transition: 0.5s; - margin-right: 45px; -} - -.cta span { - transform: skewX(15deg) - } - -.cta span:nth-child(2) { - width: 20px; - margin-left: 30px; - position: relative; - top: 12%; -} - -.cta:active { - transform: translateY(3px); - transition: 0.15s; - box-shadow: 7px 7px 7px #FBC638; -} - -/**************SVG****************/ - -/* SVG animations */ - -@keyframes color_anim { - 0% { - fill: white; - } - 50% { - fill: #FBC638; - } - 100% { - fill: white; - } -} - -.buttonCell { - width: 40px !important; - max-width: 40px !important; +.App { + display: grid; + grid-template-columns: 1.25fr 1fr; + grid-template-rows: 1fr; + margin-top: 3vh; +} + +html, body { + overflow: hidden; +} + +.bgImg { + position: absolute; + top: 0; + left: 0; + overflow: hidden; + overflow-x: hidden; + white-space: nowrap; + height: 100%; + z-index: -1; + /* Darken the img */ + filter: brightness(0.25); +} + +.wrapper { + display: flex; + justify-content: center; +} + +@keyframes slideL { + 0% { + transform: translateX(-20px); + } + 100% { + transform: translateX(0); + } +} + +@keyframes slideR { + 0% { + transform: translateX(-20px); + } + 100% { + transform: translateX(-40px); + } +} + +.hoverLink { + text-decoration: none; + color: white; + font-size: 20px; + transition: 0.5s; + margin: 0 10px; +} + +/* animated underline hover effect */ +.hoverLink::after { + content: ''; + display: block; + width: 0; + height: 2px; + background: white; + transition: width .3s; +} + +.hoverLink:hover::after { + width: 100%; + transition: width .3s; +} + + + +.hoverButton { + transition: 1s !important; + background-color: #4B006E !important; + color: #ffffff !important; +} + +.hoverButton:hover { + transition: 0.5s !important; + background-color: #690099 !important; +} + +.hoverButton:disabled { + background-color: grey !important; + color: #ffffff !important; +} + +/* A flowing animation that goes from left to right and keeps going in the same direction */ +@keyframes gradientAnimated { + 0% { + background-position: 200% 100%; + } + 100% { + background-position: -200% -100%; + } +} + +.cta { + display: flex; + padding: 10px 45px; + text-decoration: none; + font-family: 'Poppins', sans-serif; + font-size: 40px; + color: white; + transition: all 0.5s ease-in-out; + + animation: gradientAnimated 10s linear infinite; + background: linear-gradient(90deg, #4B006E, #4B006E, #6c009e, #4B006E, #4B006E); + background-size: 200% 100%; + background-position: 100% 0; + + border: 2px solid transparent; + border-image: linear-gradient(90deg, #4B006E, #4B006E, #6c009e, #4B006E, #4B006E) 1; + border-image-slice: 1; + + box-shadow: 5px 4px 5px #320853; + transform: skewX(-15deg); + cursor: pointer; +} + + +.cta:focus { + outline: none; +} + +.cta:hover { + transition: 0.5s; + box-shadow: 8px 7px 0 #FFF000; +} + +.cta span:nth-child(2) { + transition: 0.5s; + margin-right: 0px; +} + +.cta:hover span:nth-child(2) { + transition: 0.5s; + margin-right: 45px; +} + +.cta span { + transform: skewX(15deg) + } + +.cta span:nth-child(2) { + width: 20px; + margin-left: 30px; + position: relative; + top: 12%; +} + +.cta:active { + transform: translateY(3px); + transition: 0.15s; + box-shadow: 7px 7px 7px #FFF000; +} + +/**************SVG****************/ + +/* SVG animations */ + +@keyframes color_anim { + 0% { + fill: white; + } + 50% { + fill: #FFF000; + } + 100% { + fill: white; + } +} + +.buttonCell { + width: 40px !important; + max-width: 40px !important; } \ No newline at end of file diff --git a/src/App.js b/src/App.js index 89ad107..7282cbc 100644 --- a/src/App.js +++ b/src/App.js @@ -16,14 +16,21 @@ import logoImg from './images/logo.png'; import bg1 from './images/backgrounds/1.jpg'; import bg2 from './images/backgrounds/2.jpg'; import bg3 from './images/backgrounds/3.jpg'; -import bg4 from './images/backgrounds/4.jpg'; +import bg4 from './images/backgrounds/4.png'; import bg5 from './images/backgrounds/5.jpg'; import bg6 from './images/backgrounds/6.jpg'; import bg7 from './images/backgrounds/7.jpg'; import bg8 from './images/backgrounds/8.jpg'; const boxes = { height: '97vh' } -const links = { fontSize: '20px', color: 'white', textDecoration: 'none', width: 'fit-content' } +const links = { + fontSize: '20px', + color: 'white', + textDecoration: 'none', + width: 'fit-content', + fontFamily: 'Azbuka', + fontWeight: 'bold', +} let backgrounds = [ bg1, @@ -90,7 +97,7 @@ function App() { ))} - logo + logo - Official Site {/* eslint-disable-line */} - About {/* eslint-disable-line */} - Discord {/* eslint-disable-line */} - Store {/* eslint-disable-line */} + OFFICIAL SITE {/* eslint-disable-line */} + ABOUT {/* eslint-disable-line */} + DISCORD {/* eslint-disable-line */} + STORE {/* eslint-disable-line */} - + setTab(val)}> @@ -128,7 +135,7 @@ function App() { {tab === 0 && } {tab === 1 && } - {tab === 2 && } + {tab === 2 && } diff --git a/src/components/HostingSection.jsx b/src/components/HostingSection.jsx index 628702d..0e0356e 100644 --- a/src/components/HostingSection.jsx +++ b/src/components/HostingSection.jsx @@ -65,9 +65,7 @@ function HostingSection(props) { case 'starting': return ( <> - @@ -91,9 +89,7 @@ function HostingSection(props) { case 'stopping': return ( <> - @@ -101,9 +97,7 @@ function HostingSection(props) { ) default: return ( - @@ -79,7 +80,7 @@ function SettingsMenu({ gameState }) { Allow Joining - diff --git a/src/components/fancyButton.jsx b/src/components/fancyButton.jsx index cabcfc6..2eb2b42 100644 --- a/src/components/fancyButton.jsx +++ b/src/components/fancyButton.jsx @@ -1,20 +1,25 @@ -import React from 'react' - -function fancyButton(props) { - return ( - - ) -} - +import React from 'react' + +function fancyButton(props) { + return ( + + ) +} + export default fancyButton \ No newline at end of file diff --git a/src/fonts/Azbuka/CSS/fonts.css b/src/fonts/Azbuka/CSS/fonts.css new file mode 100644 index 0000000..6d1b1b4 --- /dev/null +++ b/src/fonts/Azbuka/CSS/fonts.css @@ -0,0 +1,23 @@ +@font-face { + font-family: 'Azbuka'; + src: url('../Fonts/AzbukaPro-Bold.ttf') format('truetype'); + font-weight: bold; +} + +@font-face { + font-family: 'Azbuka'; + src: url('../Fonts/AzbukaPro-BoldItalic.ttf') format('truetype'); + font-weight: bold; + font-style: italic; +} + +@font-face { + font-family: 'Azbuka'; + src: url('../Fonts/AzbukaPro-Italic.ttf') format('truetype'); + font-style: italic; +} + +@font-face { + font-family: 'Azbuka'; + src: url('../Fonts/AzbukaPro.ttf') format('truetype'); +} \ No newline at end of file diff --git a/src/fonts/Azbuka/Fonts/AzbukaPro-Bold.ttf b/src/fonts/Azbuka/Fonts/AzbukaPro-Bold.ttf new file mode 100644 index 0000000..ed0890c Binary files /dev/null and b/src/fonts/Azbuka/Fonts/AzbukaPro-Bold.ttf differ diff --git a/src/fonts/Azbuka/Fonts/AzbukaPro-BoldItalic.ttf b/src/fonts/Azbuka/Fonts/AzbukaPro-BoldItalic.ttf new file mode 100644 index 0000000..32ba98c Binary files /dev/null and b/src/fonts/Azbuka/Fonts/AzbukaPro-BoldItalic.ttf differ diff --git a/src/fonts/Azbuka/Fonts/AzbukaPro-Italic.ttf b/src/fonts/Azbuka/Fonts/AzbukaPro-Italic.ttf new file mode 100644 index 0000000..909d406 Binary files /dev/null and b/src/fonts/Azbuka/Fonts/AzbukaPro-Italic.ttf differ diff --git a/src/fonts/Azbuka/Fonts/AzbukaPro.ttf b/src/fonts/Azbuka/Fonts/AzbukaPro.ttf new file mode 100644 index 0000000..9a2e3b6 Binary files /dev/null and b/src/fonts/Azbuka/Fonts/AzbukaPro.ttf differ diff --git a/src/fonts/Brda/CSS/fonts.css b/src/fonts/Brda/CSS/fonts.css new file mode 100644 index 0000000..fcdadfc --- /dev/null +++ b/src/fonts/Brda/CSS/fonts.css @@ -0,0 +1,13 @@ +@font-face { + font-family: 'Brda'; + src: url('../Fonts/BrdaStd-ExtraBold.otf') format('opentype'); + font-weight: "bold"; + font-style: normal; +} + +@font-face { + font-family: 'Brda'; + src: url('../Fonts/BrdaStd-ExtraBoldItalic.otf') format('opentype'); + font-weight: "bold"; + font-style: italic; +} \ No newline at end of file diff --git a/src/fonts/Brda/Fonts/BrdaCom-ExtraBold.ttf b/src/fonts/Brda/Fonts/BrdaCom-ExtraBold.ttf new file mode 100644 index 0000000..b63ad14 Binary files /dev/null and b/src/fonts/Brda/Fonts/BrdaCom-ExtraBold.ttf differ diff --git a/src/fonts/Brda/Fonts/BrdaCom-ExtraBoldItalic.ttf b/src/fonts/Brda/Fonts/BrdaCom-ExtraBoldItalic.ttf new file mode 100644 index 0000000..33d3390 Binary files /dev/null and b/src/fonts/Brda/Fonts/BrdaCom-ExtraBoldItalic.ttf differ diff --git a/src/fonts/Brda/Fonts/BrdaStd-ExtraBold.otf b/src/fonts/Brda/Fonts/BrdaStd-ExtraBold.otf new file mode 100644 index 0000000..8b533ee Binary files /dev/null and b/src/fonts/Brda/Fonts/BrdaStd-ExtraBold.otf differ diff --git a/src/fonts/Brda/Fonts/BrdaStd-ExtraBoldItalic.otf b/src/fonts/Brda/Fonts/BrdaStd-ExtraBoldItalic.otf new file mode 100644 index 0000000..b9b6335 Binary files /dev/null and b/src/fonts/Brda/Fonts/BrdaStd-ExtraBoldItalic.otf differ diff --git a/src/fonts/Loew/Fonts/loew-heavy-small.otf b/src/fonts/Loew/Fonts/loew-heavy-small.otf new file mode 100644 index 0000000..fa21c1c Binary files /dev/null and b/src/fonts/Loew/Fonts/loew-heavy-small.otf differ diff --git a/src/fonts/Loew/css/fonts.css b/src/fonts/Loew/css/fonts.css new file mode 100644 index 0000000..e79a06a --- /dev/null +++ b/src/fonts/Loew/css/fonts.css @@ -0,0 +1,4 @@ +@font-face { + font-family: 'Loew'; + src: url('../Fonts/loew-heavy-small.otf') format('opentype'); +} \ No newline at end of file diff --git a/src/images/backgrounds/4.jpg b/src/images/backgrounds/4.jpg deleted file mode 100644 index 73d236b..0000000 Binary files a/src/images/backgrounds/4.jpg and /dev/null differ diff --git a/src/images/backgrounds/4.png b/src/images/backgrounds/4.png new file mode 100644 index 0000000..32e6933 Binary files /dev/null and b/src/images/backgrounds/4.png differ diff --git a/src/images/logo.png b/src/images/logo.png index 07c3f57..0f038f1 100644 Binary files a/src/images/logo.png and b/src/images/logo.png differ diff --git a/src/index.css b/src/index.css index 6016101..f6dbbd8 100644 --- a/src/index.css +++ b/src/index.css @@ -1,8 +1,12 @@ +@import url('./fonts/Brda/CSS/fonts.css'); +@import url('./fonts/Azbuka/CSS/fonts.css'); +@import url('./fonts/Loew/css/fonts.css'); + body { margin: 0; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', - 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', - sans-serif; + 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', + sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } diff --git a/src/index.js b/src/index.js index c28e871..ecbf5a0 100644 --- a/src/index.js +++ b/src/index.js @@ -9,6 +9,9 @@ const darkTheme = createTheme({ mode: 'dark', primary: { main: '#ffffff', // #743a8d + }, + secondary: { + main: '#9619FA', } }, });