From 78ad9ea3123a25b1686cdd60d1dad6811035670f Mon Sep 17 00:00:00 2001 From: Klesti Selimaj Date: Fri, 3 Apr 2026 16:23:28 +0200 Subject: [PATCH] Refactored launcherservice --- src/App.tsx | 4 +- src/components/ModsSection.tsx | 267 +++++++++++++++++++---------- src/components/PlaySection.tsx | 4 +- src/components/SettingsSection.tsx | 6 +- src/services/LauncherService.ts | 230 +++++++++++++++---------- 5 files changed, 321 insertions(+), 190 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 0c90c5f..77dacbb 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -8,7 +8,7 @@ import { User, LogOut, } from "lucide-react"; -import { launcherService, type GameVersion } from "./services/LauncherService"; +import { getVersions, type GameVersion } from "./services/LauncherService"; import { clsx, type ClassValue } from "clsx"; import { twMerge } from "tailwind-merge"; @@ -38,7 +38,7 @@ const App: React.FC = () => { useEffect(() => { const fetchVersions = async () => { - const v = await launcherService.getVersions(); + const v = await getVersions(); setVersions(v); if (v.length > 0) setSelectedVersion(v[0]); }; diff --git a/src/components/ModsSection.tsx b/src/components/ModsSection.tsx index 4404353..038b24f 100644 --- a/src/components/ModsSection.tsx +++ b/src/components/ModsSection.tsx @@ -1,102 +1,140 @@ -import React, { useState, useEffect } from 'react' -import { Search, Download, Trash2, ExternalLink, Package, Filter, CheckCircle2, XCircle, ChevronRight, X } from 'lucide-react' -import { launcherService, type Mod, type GameVersion } from '../services/LauncherService' -import { clsx, type ClassValue } from 'clsx' -import { twMerge } from 'tailwind-merge' -import { motion, AnimatePresence } from 'framer-motion' +import React, { useState, useEffect } from "react"; +import { + Search, + Download, + Trash2, + Package, + CheckCircle2, + XCircle, + X, +} from "lucide-react"; +import { + type Mod, + type GameVersion, + getInstalledMods, + discoverMods, + getVersions, + toggleMod, + installMod, +} from "../services/LauncherService"; +import { clsx, type ClassValue } from "clsx"; +import { twMerge } from "tailwind-merge"; +import { motion, AnimatePresence } from "framer-motion"; function cn(...inputs: ClassValue[]) { - return twMerge(clsx(inputs)) + return twMerge(clsx(inputs)); } interface ModsSectionProps { - version: GameVersion + version: GameVersion; } const ModsSection: React.FC = ({ version }) => { - const [activeTab, setActiveTab] = useState<'installed' | 'discover'>('installed') - const [searchQuery, setSearchQuery] = useState('') - const [mods, setMods] = useState([]) - const [isInstallDialogOpen, setIsInstallDialogOpen] = useState(false) - const [selectedModForInstall, setSelectedModForInstall] = useState(null) - const [selectedVersionsForInstall, setSelectedVersionsForInstall] = useState([]) - const [allVersions, setAllVersions] = useState([]) + const [activeTab, setActiveTab] = useState<"installed" | "discover">( + "installed", + ); + const [searchQuery, setSearchQuery] = useState(""); + const [mods, setMods] = useState([]); + const [isInstallDialogOpen, setIsInstallDialogOpen] = useState(false); + const [selectedModForInstall, setSelectedModForInstall] = + useState(null); + const [selectedVersionsForInstall, setSelectedVersionsForInstall] = useState< + string[] + >([]); + const [allVersions, setAllVersions] = useState([]); useEffect(() => { const loadMods = async () => { - if (activeTab === 'installed') { - const m = await launcherService.getInstalledMods(version.id) - setMods(m) + if (activeTab === "installed") { + const m = await getInstalledMods(version.id); + setMods(m); } else { - const m = await launcherService.discoverMods(searchQuery) - setMods(m) + const m = await discoverMods(searchQuery); + setMods(m); } - } - loadMods() - }, [activeTab, version.id, searchQuery]) + }; + loadMods(); + }, [activeTab, version.id, searchQuery]); useEffect(() => { const loadAllVersions = async () => { - const v = await launcherService.getVersions() - setAllVersions(v) - } - loadAllVersions() - }, []) + const v = await getVersions(); + setAllVersions(v); + }; + loadAllVersions(); + }, []); const handleToggleMod = async (modId: string) => { - await launcherService.toggleMod(modId) - const m = await launcherService.getInstalledMods(version.id) - setMods(m) - } + await toggleMod(modId); + const m = await getInstalledMods(version.id); + setMods(m); + }; const openInstallDialog = (mod: Mod) => { - setSelectedModForInstall(mod) - setSelectedVersionsForInstall([version.id]) - setIsInstallDialogOpen(true) - } + setSelectedModForInstall(mod); + setSelectedVersionsForInstall([version.id]); + setIsInstallDialogOpen(true); + }; const handleInstall = async () => { if (selectedModForInstall && selectedVersionsForInstall.length > 0) { - await launcherService.installMod(selectedModForInstall, selectedVersionsForInstall) - setIsInstallDialogOpen(false) - if (activeTab === 'installed') { - const m = await launcherService.getInstalledMods(version.id) - setMods(m) + await installMod(selectedModForInstall, selectedVersionsForInstall); + setIsInstallDialogOpen(false); + if (activeTab === "installed") { + const m = await getInstalledMods(version.id); + setMods(m); } } - } + }; const toggleVersionSelection = (versionId: string) => { - setSelectedVersionsForInstall(prev => - prev.includes(versionId) - ? prev.filter(v => v !== versionId) - : [...prev, versionId] - ) - } + setSelectedVersionsForInstall((prev) => + prev.includes(versionId) + ? prev.filter((v) => v !== versionId) + : [...prev, versionId], + ); + }; return (
- -
- - + setSearchQuery(e.target.value)} className="bg-saturn-panel border border-saturn-border rounded-lg pl-10 pr-4 py-2 text-sm w-64 focus:outline-none focus:border-saturn-accent/50 focus:ring-1 focus:ring-saturn-accent/50 transition-all" @@ -106,8 +144,8 @@ const ModsSection: React.FC = ({ version }) => {
- {mods.map(mod => ( - ( + = ({ version }) => {
- +

{mod.name}

- v{mod.version} - by {mod.author} + + v{mod.version} + + + by {mod.author} +
-

{mod.description}

+

+ {mod.description} +

- {activeTab === 'installed' ? ( + {activeTab === "installed" ? ( <> - ) : ( -
))} - + {mods.length === 0 && (
@@ -166,8 +219,8 @@ const ModsSection: React.FC = ({ version }) => { {/* Install Dialog */} {isInstallDialogOpen && selectedModForInstall && ( -
- + = ({ version }) => { >
-

Install {selectedModForInstall.name}

-

Select versions to install this mod to.

+

+ Install {selectedModForInstall.name} +

+

+ Select versions to install this mod to. +

-
- +
- {allVersions.map(v => { - const isSupported = selectedModForInstall.supportedVersions.includes(v.id) - const isSelected = selectedVersionsForInstall.includes(v.id) - + {allVersions.map((v) => { + const isSupported = + selectedModForInstall.supportedVersions.includes(v.id); + const isSelected = selectedVersionsForInstall.includes(v.id); + return ( -
isSupported && toggleVersionSelection(v.id)} + onClick={() => + isSupported && toggleVersionSelection(v.id) + } className={cn( "flex items-center justify-between p-3 rounded-lg border transition-all cursor-pointer", - isSelected ? "bg-saturn-accent/10 border-saturn-accent/50" : "bg-white/5 border-transparent hover:border-white/10", - !isSupported && "opacity-40 cursor-not-allowed grayscale" + isSelected + ? "bg-saturn-accent/10 border-saturn-accent/50" + : "bg-white/5 border-transparent hover:border-white/10", + !isSupported && + "opacity-40 cursor-not-allowed grayscale", )} >
-
- {isSelected && } +
+ {isSelected && ( + + )}
{v.name}
- {!isSupported && Unsupported} + {!isSupported && ( + + Unsupported + + )}
- ) + ); })}
- -
- ) -} + ); +}; -export default ModsSection +export default ModsSection; diff --git a/src/components/PlaySection.tsx b/src/components/PlaySection.tsx index 5c05a32..a9cfc36 100644 --- a/src/components/PlaySection.tsx +++ b/src/components/PlaySection.tsx @@ -1,6 +1,6 @@ import React, { useState } from "react"; import { Play, Loader2 } from "lucide-react"; -import { launcherService, type GameVersion } from "../services/LauncherService"; +import { launchGame, type GameVersion } from "../services/LauncherService"; interface PlaySectionProps { version: GameVersion; @@ -18,7 +18,7 @@ const PlaySection: React.FC = ({ version }) => { setTimeout(() => setLaunchStatus("Verifying assets..."), 500); setTimeout(() => setLaunchStatus("Applying Saturn optimizations..."), 1200); - const result = await launcherService.launchGame(version.id); + const result = await launchGame(version.id); if (result.success) { setLaunchStatus("Game running"); diff --git a/src/components/SettingsSection.tsx b/src/components/SettingsSection.tsx index 7cb865c..8722327 100644 --- a/src/components/SettingsSection.tsx +++ b/src/components/SettingsSection.tsx @@ -1,6 +1,6 @@ import React, { useState, useEffect } from 'react' import { Cpu, MemoryStick, Save, RefreshCcw, Info, AlertTriangle } from 'lucide-react' -import { launcherService, type LauncherSettings } from '../services/LauncherService' +import { getSettings, updateSettings, type LauncherSettings } from '../services/LauncherService' import { motion } from 'framer-motion' const SettingsSection: React.FC = () => { @@ -10,7 +10,7 @@ const SettingsSection: React.FC = () => { useEffect(() => { const loadSettings = async () => { - const s = await launcherService.getSettings() + const s = await getSettings() setSettings(s) } loadSettings() @@ -21,7 +21,7 @@ const SettingsSection: React.FC = () => { setIsSaving(true) setSaveStatus('Saving...') - await launcherService.updateSettings(settings) + await updateSettings(settings) setTimeout(() => { setIsSaving(false) diff --git a/src/services/LauncherService.ts b/src/services/LauncherService.ts index 590d9bc..341d7f5 100644 --- a/src/services/LauncherService.ts +++ b/src/services/LauncherService.ts @@ -16,7 +16,7 @@ export interface GameVersion { id: string; name: string; releaseDate: string; - type: 'release' | 'snapshot'; + type: "release" | "snapshot"; } export interface LauncherSettings { @@ -24,104 +24,156 @@ export interface LauncherSettings { maxMemory: number; // in MB } -class LauncherService { - private versions: GameVersion[] = [ - { id: '1.20.1', name: 'Fabric 1.20.1', releaseDate: '2023-06-07', type: 'release' }, - { id: '1.20.4', name: 'Fabric 1.20.4', releaseDate: '2023-12-07', type: 'release' }, - { id: '1.21', name: 'Fabric 1.21', releaseDate: '2024-06-13', type: 'release' }, - ]; +// --- Internal state --- +const versions: GameVersion[] = [ + { + id: "1.20.1", + name: "Fabric 1.20.1", + releaseDate: "2023-06-07", + type: "release", + }, + { + id: "1.20.4", + name: "Fabric 1.20.4", + releaseDate: "2023-12-07", + type: "release", + }, + { + id: "1.21", + name: "Fabric 1.21", + releaseDate: "2024-06-13", + type: "release", + }, +]; - private installedMods: Mod[] = [ - { - id: 'sodium', - name: 'Sodium', - version: '0.5.8', - author: 'jellysquid3', - description: 'Modern rendering engine for Minecraft.', - enabled: true, - supportedVersions: ['1.20.1', '1.20.4', '1.21'] +const installedMods: Mod[] = [ + { + id: "sodium", + name: "Sodium", + version: "0.5.8", + author: "jellysquid3", + description: "Modern rendering engine for Minecraft.", + enabled: true, + supportedVersions: ["1.20.1", "1.20.4", "1.21"], + }, + { + id: "iris", + name: "Iris", + version: "1.7.0", + author: "coderbot", + description: "A modern shaders mod for Minecraft.", + enabled: true, + supportedVersions: ["1.20.1", "1.20.4", "1.21"], + }, + { + id: "lithium", + name: "Lithium", + version: "0.11.2", + author: "jellysquid3", + description: "General-purpose optimization mod for Minecraft.", + enabled: false, + supportedVersions: ["1.20.1", "1.20.4"], + }, +]; + +let settings: LauncherSettings = { + jvmArguments: + "-XX:+UseG1GC -XX:+ParallelRefProcEnabled -XX:MaxGCPauseMillis=200", + maxMemory: 4096, +}; + +// --- Versions --- +export async function getVersions(): Promise { + return [...versions]; +} + +// --- Mods --- +export async function getInstalledMods(versionId: string): Promise { + return installedMods.filter((mod) => + mod.supportedVersions.includes(versionId), + ); +} + +export async function toggleMod(modId: string): Promise { + const mod = installedMods.find((m) => m.id === modId); + if (mod) { + mod.enabled = !mod.enabled; + } +} + +export async function discoverMods(query: string): Promise { + const allDiscoverable: Mod[] = [ + { + id: "modmenu", + name: "Mod Menu", + version: "7.2.2", + author: "TerraformersMC", + description: "Adds a mod menu to view the list of installed mods.", + enabled: false, + supportedVersions: ["1.20.1", "1.20.4", "1.21"], }, - { - id: 'iris', - name: 'Iris', - version: '1.7.0', - author: 'coderbot', - description: 'A modern shaders mod for Minecraft.', - enabled: true, - supportedVersions: ['1.20.1', '1.20.4', '1.21'] + { + id: "starlight", + name: "Starlight", + version: "1.1.2", + author: "Spottedleaf", + description: "Rewrites the light engine to fix performance issues.", + enabled: false, + supportedVersions: ["1.20.1"], }, - { - id: 'lithium', - name: 'Lithium', - version: '0.11.2', - author: 'jellysquid3', - description: 'General-purpose optimization mod for Minecraft.', - enabled: false, - supportedVersions: ['1.20.1', '1.20.4'] + { + id: "ferritecore", + name: "FerriteCore", + version: "6.0.1", + author: "malte0811", + description: "Memory usage optimizations.", + enabled: false, + supportedVersions: ["1.20.1", "1.20.4", "1.21"], }, ]; - private settings: LauncherSettings = { - jvmArguments: '-XX:+UseG1GC -XX:+ParallelRefProcEnabled -XX:MaxGCPauseMillis=200', - maxMemory: 4096, - }; + if (!query) return allDiscoverable; - // --- Versions --- - async getVersions(): Promise { - return [...this.versions]; - } + return allDiscoverable.filter((m) => + m.name.toLowerCase().includes(query.toLowerCase()), + ); +} - // --- Mods --- - async getInstalledMods(versionId: string): Promise { - // Filter mods by supported version - return this.installedMods.filter(mod => mod.supportedVersions.includes(versionId)); - } +export async function installMod( + mod: Mod, + versionsList: string[], +): Promise { + console.log(`Installing ${mod.name} to versions: ${versionsList.join(", ")}`); - async toggleMod(modId: string): Promise { - const mod = this.installedMods.find(m => m.id === modId); - if (mod) { - mod.enabled = !mod.enabled; - } - } - - async discoverMods(query: string): Promise { - // Mock discovery results - const allDiscoverable: Mod[] = [ - { id: 'modmenu', name: 'Mod Menu', version: '7.2.2', author: 'TerraformersMC', description: 'Adds a mod menu to view the list of installed mods.', enabled: false, supportedVersions: ['1.20.1', '1.20.4', '1.21'] }, - { id: 'starlight', name: 'Starlight', version: '1.1.2', author: 'Spottedleaf', description: 'Rewrites the light engine to fix performance issues.', enabled: false, supportedVersions: ['1.20.1'] }, - { id: 'ferritecore', name: 'FerriteCore', version: '6.0.1', author: 'malte0811', description: 'Memory usage optimizations.', enabled: false, supportedVersions: ['1.20.1', '1.20.4', '1.21'] }, - ]; - - if (!query) return allDiscoverable; - return allDiscoverable.filter(m => m.name.toLowerCase().includes(query.toLowerCase())); - } - - async installMod(mod: Mod, versions: string[]): Promise { - console.log(`Installing ${mod.name} to versions: ${versions.join(', ')}`); - // Mock install logic: add to installedMods if not already there - if (!this.installedMods.find(m => m.id === mod.id)) { - this.installedMods.push({ ...mod, enabled: true, supportedVersions: versions }); - } - } - - // --- Settings --- - async getSettings(): Promise { - return { ...this.settings }; - } - - async updateSettings(newSettings: Partial): Promise { - this.settings = { ...this.settings, ...newSettings }; - } - - // --- Launch --- - async launchGame(versionId: string): Promise<{ success: boolean; message: string }> { - console.log(`Launching Saturn Client with version ${versionId}...`); - return new Promise((resolve) => { - setTimeout(() => { - resolve({ success: true, message: 'Game launched successfully!' }); - }, 2000); + if (!installedMods.find((m) => m.id === mod.id)) { + installedMods.push({ + ...mod, + enabled: true, + supportedVersions: versionsList, }); } } -export const launcherService = new LauncherService(); +// --- Settings --- +export async function getSettings(): Promise { + return { ...settings }; +} + +export async function updateSettings( + newSettings: Partial, +): Promise { + settings = { ...settings, ...newSettings }; +} + +// --- Launch --- +export async function launchGame( + versionId: string, +): Promise<{ success: boolean; message: string }> { + console.log(`Launching Saturn Client with version ${versionId}...`); + + return new Promise((resolve) => { + setTimeout(() => { + resolve({ success: true, message: "Game launched successfully!" }); + }, 2000); + }); +}