diff --git a/build/appicon.png b/build/appicon.png index 0803fc3..17f1a96 100644 Binary files a/build/appicon.png and b/build/appicon.png differ diff --git a/build/darwin/icons.icns b/build/darwin/icons.icns index 290fdbc..69878f1 100644 Binary files a/build/darwin/icons.icns and b/build/darwin/icons.icns differ diff --git a/build/windows/icon.ico b/build/windows/icon.ico index 66651f3..52a03c7 100644 Binary files a/build/windows/icon.ico and b/build/windows/icon.ico differ diff --git a/build/windows/icons.ico b/build/windows/icons.ico index 5779397..a62ee1d 100644 Binary files a/build/windows/icons.ico and b/build/windows/icons.ico differ diff --git a/frontend/bindings/firefly-launcher/internal/appservice.js b/frontend/bindings/firefly-launcher/internal/appservice.js new file mode 100644 index 0000000..81c8ca3 --- /dev/null +++ b/frontend/bindings/firefly-launcher/internal/appservice.js @@ -0,0 +1,24 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import {Call as $Call, Create as $Create} from "@wailsio/runtime"; + +/** + * @param {number} timeout + * @returns {Promise<[boolean, string]> & { cancel(): void }} + */ +export function CloseAppAfterTimeout(timeout) { + let $resultPromise = /** @type {any} */($Call.ByID(982751559, timeout)); + return $resultPromise; +} + +/** + * @returns {Promise<[boolean, string]> & { cancel(): void }} + */ +export function GetCurrentLauncherVersion() { + let $resultPromise = /** @type {any} */($Call.ByID(2542090372)); + return $resultPromise; +} diff --git a/frontend/bindings/firefly-launcher/internal/gitservice.js b/frontend/bindings/firefly-launcher/internal/gitservice.js index 1bbe0ab..659c557 100644 --- a/frontend/bindings/firefly-launcher/internal/gitservice.js +++ b/frontend/bindings/firefly-launcher/internal/gitservice.js @@ -25,20 +25,26 @@ export function DownloadServerProgress(version) { } /** - * @param {string} oldVersion * @returns {Promise<[boolean, string, string]> & { cancel(): void }} */ -export function GetLatestProxyVersion(oldVersion) { - let $resultPromise = /** @type {any} */($Call.ByID(1462362449, oldVersion)); +export function GetLatestLauncherVersion() { + let $resultPromise = /** @type {any} */($Call.ByID(3191972855)); return $resultPromise; } /** - * @param {string} oldVersion * @returns {Promise<[boolean, string, string]> & { cancel(): void }} */ -export function GetLatestServerVersion(oldVersion) { - let $resultPromise = /** @type {any} */($Call.ByID(1447982978, oldVersion)); +export function GetLatestProxyVersion() { + let $resultPromise = /** @type {any} */($Call.ByID(1462362449)); + return $resultPromise; +} + +/** + * @returns {Promise<[boolean, string, string]> & { cancel(): void }} + */ +export function GetLatestServerVersion() { + let $resultPromise = /** @type {any} */($Call.ByID(1447982978)); return $resultPromise; } @@ -57,3 +63,12 @@ export function UnzipServer() { let $resultPromise = /** @type {any} */($Call.ByID(4110296071)); return $resultPromise; } + +/** + * @param {string} version + * @returns {Promise<[boolean, string]> & { cancel(): void }} + */ +export function UpdateLauncherProgress(version) { + let $resultPromise = /** @type {any} */($Call.ByID(2648938152, version)); + return $resultPromise; +} diff --git a/frontend/bindings/firefly-launcher/internal/index.js b/frontend/bindings/firefly-launcher/internal/index.js index 91c053c..5d00e90 100644 --- a/frontend/bindings/firefly-launcher/internal/index.js +++ b/frontend/bindings/firefly-launcher/internal/index.js @@ -2,11 +2,13 @@ // Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL // This file is automatically generated. DO NOT EDIT +import * as AppService from "./appservice.js"; import * as FSService from "./fsservice.js"; import * as GitService from "./gitservice.js"; import * as HdiffzService from "./hdiffzservice.js"; import * as LanguageService from "./languageservice.js"; export { + AppService, FSService, GitService, HdiffzService, diff --git a/frontend/public/ff-launcher.png b/frontend/public/ff-launcher.png new file mode 100644 index 0000000..17f1a96 Binary files /dev/null and b/frontend/public/ff-launcher.png differ diff --git a/frontend/src/helper/index.ts b/frontend/src/helper/index.ts new file mode 100644 index 0000000..cd6531c --- /dev/null +++ b/frontend/src/helper/index.ts @@ -0,0 +1,4 @@ +export * from "./server" +export * from "./proxy" +export * from "./launcher" +export * from "./sleep" diff --git a/frontend/src/helper/launcher.ts b/frontend/src/helper/launcher.ts new file mode 100644 index 0000000..b4e1874 --- /dev/null +++ b/frontend/src/helper/launcher.ts @@ -0,0 +1,38 @@ +import useLauncherStore from "@/stores/launcherStore"; +import { AppService, GitService } from "@bindings/firefly-launcher/internal"; +import { toast } from "react-toastify"; +import { sleep } from "./sleep"; + +export async function CheckUpdateLauncher() : Promise<{isUpdate: boolean, version: string}> { + let isUpdateLauncher = false + let version = "" + const [launcherCurrentOk, launcherCurrentVersion] = await AppService.GetCurrentLauncherVersion() + if (!launcherCurrentOk) { + toast.error("Launcher error: cannot get current version") + } else { + const [launcherNewOk, launcherNewVersion, launcherNewError] = await GitService.GetLatestLauncherVersion() + version = launcherCurrentVersion + if (launcherNewOk && launcherNewVersion && launcherNewVersion !== launcherCurrentVersion) { + isUpdateLauncher = true + version = launcherNewVersion + } else if (!launcherNewOk) { + toast.error("Launcher error: " + launcherNewError) + } + } + return { isUpdate: isUpdateLauncher, version: version } +} + +export async function UpdateLauncher(launcherVersion: string) : Promise { + const {setDownloadType } = useLauncherStore.getState() + setDownloadType("update:launcher:downloading") + const [ok, error] = await GitService.UpdateLauncherProgress(launcherVersion) + if (ok) { + setDownloadType("update:launcher:success") + + } else { + toast.error(error) + setDownloadType("update:launcher:failed") + } + AppService.CloseAppAfterTimeout(5) + await sleep(5000) +} \ No newline at end of file diff --git a/frontend/src/helper/proxy.ts b/frontend/src/helper/proxy.ts new file mode 100644 index 0000000..0e59b94 --- /dev/null +++ b/frontend/src/helper/proxy.ts @@ -0,0 +1,35 @@ +import useLauncherStore from "@/stores/launcherStore"; +import useSettingStore from "@/stores/settingStore"; +import { FSService, GitService } from "@bindings/firefly-launcher/internal"; +import { toast } from "react-toastify"; + +export async function CheckUpdateProxy(proxyPath: string, proxyVersion: string) : Promise<{isUpdate: boolean, version: string}> { + let isUpdateProxy = false + let version = "" + const [proxyOk, proxyNewVersion, proxyError] = await GitService.GetLatestProxyVersion() + const proxyExists = await FSService.FileExists(proxyPath) + if (proxyOk && proxyNewVersion && proxyNewVersion !== proxyVersion || !proxyExists) { + isUpdateProxy = true + version = proxyNewVersion + } else if (!proxyOk) { + toast.error("Proxy error: " + proxyError) + } + return { isUpdate: isUpdateProxy, version: version } +} + +export async function UpdateProxy(proxyVersion: string) : Promise { + const {setDownloadType } = useLauncherStore.getState() + const {setProxyPath, setProxyVersion} = useSettingStore.getState() + setDownloadType("Downloading proxy...") + const [ok, error] = await GitService.DownloadProxyProgress(proxyVersion) + if (ok) { + setDownloadType("Unzipping proxy...") + GitService.UnzipProxy() + setDownloadType("Download proxy successfully") + setProxyVersion(proxyVersion) + setProxyPath("./proxy/FireflyProxy.exe") + } else { + toast.error(error) + setDownloadType("Download proxy failed") + } +} \ No newline at end of file diff --git a/frontend/src/helper/server.ts b/frontend/src/helper/server.ts new file mode 100644 index 0000000..02bdf7f --- /dev/null +++ b/frontend/src/helper/server.ts @@ -0,0 +1,35 @@ +import useLauncherStore from '@/stores/launcherStore'; +import useSettingStore from '@/stores/settingStore'; +import { FSService, GitService } from '@bindings/firefly-launcher/internal'; +import { toast } from 'react-toastify'; + +export async function CheckUpdateServer(serverPath: string, serverVersion: string) : Promise<{isUpdate: boolean, version: string}> { + let isUpdateServer = false + let version = "" + const [serverOk, serverNewVersion, serverError] = await GitService.GetLatestServerVersion() + const serverExists = await FSService.FileExists(serverPath) + if (serverOk && serverNewVersion && serverNewVersion !== serverVersion || !serverExists) { + isUpdateServer = true + version = serverNewVersion + } else if (!serverOk) { + toast.error("Server error: " + serverError) + } + return { isUpdate: isUpdateServer, version: version } +} + +export async function UpdateServer(serverVersion: string) : Promise { + const {setDownloadType } = useLauncherStore.getState() + const {setServerPath, setServerVersion} = useSettingStore.getState() + setDownloadType("Downloading server...") + const [ok, error] = await GitService.DownloadServerProgress(serverVersion) + if (ok) { + setDownloadType("Unzipping server...") + GitService.UnzipServer() + setDownloadType("Download server successfully") + setServerVersion(serverVersion) + setServerPath("./server/firefly-go_win.exe") + } else { + toast.error(error) + setDownloadType("Download server failed") + } +} \ No newline at end of file diff --git a/frontend/src/helper/sleep.ts b/frontend/src/helper/sleep.ts new file mode 100644 index 0000000..9721cc4 --- /dev/null +++ b/frontend/src/helper/sleep.ts @@ -0,0 +1,3 @@ +export function sleep(ms: number) { + return new Promise(resolve => setTimeout(resolve, ms)); +} \ No newline at end of file diff --git a/frontend/src/hooks/useGlobalEvents.tsx b/frontend/src/hooks/useGlobalEvents.tsx index 4b50447..dba5f5f 100644 --- a/frontend/src/hooks/useGlobalEvents.tsx +++ b/frontend/src/hooks/useGlobalEvents.tsx @@ -65,6 +65,7 @@ export function useGlobalEvents({ Events.Off("proxy:exit"); Events.Off("hdiffz:progress"); Events.Off("hdiffz:message"); + Events.Off("version:check"); }; }, []); } diff --git a/frontend/src/pages/about/index.tsx b/frontend/src/pages/about/index.tsx index 7649738..a4f5c47 100644 --- a/frontend/src/pages/about/index.tsx +++ b/frontend/src/pages/about/index.tsx @@ -14,7 +14,7 @@ export default function AboutPage() { I created a lightweight and modern Game Launcher to help users easily launch and manage their games with better performance and simplicity.

- The launcher is built using Go + Wails, with a clean and responsive interface styled with Tailwind CSS and DaisyUI. + The launcher is built using Go + Wails3, with a clean and responsive interface styled with Tailwind CSS and DaisyUI.

My goal is to make tools that are fast, efficient, and enjoyable to use — and this launcher is just the beginning. diff --git a/frontend/src/pages/analysis/index.tsx b/frontend/src/pages/analysis/index.tsx index bcfd19b..68a6023 100644 --- a/frontend/src/pages/analysis/index.tsx +++ b/frontend/src/pages/analysis/index.tsx @@ -78,12 +78,12 @@ export default function AnalysisPage() { Backup Website - https://sr-analysis.vercel.app/ + https://firefly-sranalysis.vercel.app/ diff --git a/frontend/src/pages/launcher/index.tsx b/frontend/src/pages/launcher/index.tsx index 6ef90b9..1a2b0d9 100644 --- a/frontend/src/pages/launcher/index.tsx +++ b/frontend/src/pages/launcher/index.tsx @@ -1,6 +1,6 @@ import { useEffect } from 'react'; import { Play, Menu, FolderOpen, MessageCircleQuestionMark } from 'lucide-react'; -import { FSService, GitService } from '@bindings/firefly-launcher/internal'; +import { FSService, AppService } from '@bindings/firefly-launcher/internal'; import { toast } from 'react-toastify'; import path from 'path-browserify' import useSettingStore from '@/stores/settingStore'; @@ -8,6 +8,7 @@ import useModalStore from '@/stores/modalStore'; import useLauncherStore from '@/stores/launcherStore'; import { motion } from 'motion/react'; import { Link } from '@tanstack/react-router'; +import { CheckUpdateLauncher, CheckUpdateProxy, CheckUpdateServer, sleep, UpdateLauncher, UpdateProxy, UpdateServer } from '@/helper'; export default function LauncherPage() { const { gamePath, @@ -18,15 +19,17 @@ export default function LauncherPage() { gameDir, serverVersion, proxyVersion, - setServerVersion, - setProxyVersion, - setServerPath, - setProxyPath } = useSettingStore() - const { isOpenNotification, setIsOpenNotification } = useModalStore() + const { + isOpenDownloadDataModal, + isOpenUpdateDataModal, + isOpenSelfUpdateModal, + setIsOpenDownloadDataModal, + setIsOpenUpdateDataModal, + setIsOpenSelfUpdateModal + } = useModalStore() const { isLoading, - modelName, downloadType, serverReady, proxyReady, @@ -36,8 +39,10 @@ export default function LauncherPage() { gameRunning, progressDownload, downloadSpeed, + updateData, + launcherVersion, + setLauncherVersion, setIsLoading, - setModelName, setDownloadType, setServerReady, setProxyReady, @@ -45,9 +50,9 @@ export default function LauncherPage() { setServerRunning, setProxyRunning, setGameRunning, + setUpdateData, } = useLauncherStore() - useEffect(() => { const check = async () => { if (!serverPath || !proxyPath || !serverVersion || !proxyVersion) { @@ -67,30 +72,49 @@ export default function LauncherPage() { useEffect(() => { const checkStartUp = async (): Promise => { + const [_, version] = await AppService.GetCurrentLauncherVersion() + setLauncherVersion(version) let isExists = false if (serverPath && proxyPath && serverVersion && proxyVersion) { isExists = true setServerReady(true) setProxyReady(true) } - const dataUpdate = await handlerCheckUpdate() + const launcherData = await CheckUpdateLauncher() + if (launcherData.isUpdate) { + setUpdateData({ + server: { isUpdate: false, version: "" }, + proxy: { isUpdate: false, version: "" }, + launcher: launcherData + }) + setIsOpenSelfUpdateModal(true) + return + } + const serverData = await CheckUpdateServer(serverPath, serverVersion) + const proxyData = await CheckUpdateProxy(proxyPath, proxyVersion) const exitGame = await FSService.FileExists(gamePath) if (!exitGame) { setGameRunning(false) setGamePath("") setGameDir("") } - if (!dataUpdate.server.isUpdate && !dataUpdate.proxy.isUpdate) { + if (!serverData.isUpdate && !proxyData.isUpdate) { setServerReady(true) setProxyReady(true) return } if (!isExists) { + setUpdateData({ + server: serverData, + proxy: proxyData, + launcher: launcherData + }) setServerReady(false) setProxyReady(false) + setIsOpenDownloadDataModal(true) + return } - setModelName(!isExists ? "Download Data" : "Update Data") - setIsOpenNotification(true) + setIsOpenUpdateDataModal(true) } checkStartUp() }, []); @@ -121,9 +145,7 @@ export default function LauncherPage() { } } - function sleep(ms: number) { - return new Promise(resolve => setTimeout(resolve, ms)); - } + const handleStartGame = async () => { if (!gamePath) { @@ -174,84 +196,43 @@ export default function LauncherPage() { } } - const handlerCheckUpdate = async (): Promise> => { - let isUpdateServer = false - let isUpdateProxy = false - const [serverOk, serverNewVersion, serverError] = await GitService.GetLatestServerVersion(serverVersion) - const serverExists = await FSService.FileExists(serverPath) - if (serverOk) { - if (serverNewVersion !== serverVersion || !serverExists) { - isUpdateServer = true - } - } else { - toast.error("Server error: " + serverError) - } - const [proxyOk, proxyNewVersion, proxyError] = await GitService.GetLatestProxyVersion(proxyVersion) - const proxyExists = await FSService.FileExists(proxyPath) - if (proxyOk) { - if (proxyNewVersion !== proxyVersion || !proxyExists) { - isUpdateProxy = true - } - } else { - toast.error("Proxy error: " + proxyError) - } - return { server: { isUpdate: isUpdateServer, version: serverNewVersion }, proxy: { isUpdate: isUpdateProxy, version: proxyNewVersion } } - } - - const handlerUpdateServer = async (updateInfo: Record) => { + const handlerUpdateServer = async () => { setIsDownloading(true) - for (const [key, value] of Object.entries(updateInfo)) { - if (value.isUpdate) { + for (const [key, value] of Object.entries(updateData)) { + if (!value.isUpdate) { if (key === "server") { - setDownloadType("Downloading server...") - const [ok, error] = await GitService.DownloadServerProgress(value.version) - if (ok) { - setDownloadType("Unzipping server...") - GitService.UnzipServer() - setDownloadType("Download server successfully") - setServerVersion(value.version) - - setServerPath("./server/firefly-go_win.exe") - } else { - toast.error(error) - setDownloadType("Download server failed") - } + setServerReady(true) } else if (key === "proxy") { - setDownloadType("Downloading proxy...") - const [ok, error] = await GitService.DownloadProxyProgress(value.version) - if (ok) { - setDownloadType("Unzipping proxy...") - GitService.UnzipProxy() - setDownloadType("Download proxy successfully") - setProxyVersion(value.version) - setProxyPath("./proxy/FireflyProxy.exe") - - } else { - toast.error(error) - setDownloadType("Download proxy failed") - } + setProxyReady(true) } - + continue + } + if (key === "server") { + await UpdateServer(value.version) + setServerReady(true) + } else if (key === "proxy") { + await UpdateProxy(value.version) + setProxyReady(true) + } else if (key === "launcher") { + await UpdateLauncher(value.version) } } setDownloadType("") setIsDownloading(false) - setServerReady(true) - setProxyReady(true) } // Handle ESC key to close modal useEffect(() => { const handleEscKey = (event: KeyboardEvent) => { if (event.key === 'Escape') { - setIsOpenNotification(false); + setIsOpenDownloadDataModal(false); + setIsOpenUpdateDataModal(false); + setIsOpenSelfUpdateModal(false); } - }; - window.addEventListener('keydown', handleEscKey); return () => window.removeEventListener('keydown', handleEscKey); - }, [isOpenNotification]); + }, [isOpenDownloadDataModal, isOpenUpdateDataModal, isOpenSelfUpdateModal]); return ( @@ -280,11 +261,10 @@ export default function LauncherPage() { href="https://sranalysis.kain.id.vn/" > SRAnalysis Logo - @@ -295,7 +275,7 @@ export default function LauncherPage() { href="https://srtools.kain.id.vn/" > SRTools Logo @@ -369,10 +349,16 @@ export default function LauncherPage() {