Working launch with auth and version
This commit is contained in:
@@ -1,23 +1,28 @@
|
|||||||
|
pub mod auth;
|
||||||
pub mod downloader;
|
pub mod downloader;
|
||||||
pub mod install;
|
pub mod install;
|
||||||
pub mod auth;
|
|
||||||
|
|
||||||
use directories::ProjectDirs;
|
use directories::ProjectDirs;
|
||||||
use lyceris::minecraft::{
|
use lyceris::{
|
||||||
config::ConfigBuilder,
|
auth::microsoft::MinecraftAccount,
|
||||||
emitter::{Emitter, Event},
|
minecraft::{
|
||||||
launch::launch,
|
config::ConfigBuilder,
|
||||||
loader::fabric::Fabric,
|
emitter::{Emitter, Event},
|
||||||
|
launch::launch,
|
||||||
|
loader::fabric::Fabric,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
|
|
||||||
|
use crate::GameVersion;
|
||||||
|
|
||||||
pub static LAUNCHER_DIR: Lazy<ProjectDirs> = Lazy::new(|| {
|
pub static LAUNCHER_DIR: Lazy<ProjectDirs> = Lazy::new(|| {
|
||||||
ProjectDirs::from("org", "SaturnLauncher", "SaturnLauncher")
|
ProjectDirs::from("org", "SaturnLauncher", "SaturnLauncher")
|
||||||
.expect("Failed to create project directories")
|
.expect("Failed to create project directories")
|
||||||
});
|
});
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn launch_game() -> Result<(), String> {
|
pub async fn launch_game(version: GameVersion, account: MinecraftAccount) -> Result<(), String> {
|
||||||
// Emitter uses `EventEmitter` inside of it
|
// Emitter uses `EventEmitter` inside of it
|
||||||
// and it uses tokio::Mutex for locking.
|
// and it uses tokio::Mutex for locking.
|
||||||
// That causes emitter methods to be async.
|
// That causes emitter methods to be async.
|
||||||
@@ -58,14 +63,17 @@ pub async fn launch_game() -> Result<(), String> {
|
|||||||
let current_dir = LAUNCHER_DIR.data_dir();
|
let current_dir = LAUNCHER_DIR.data_dir();
|
||||||
|
|
||||||
let config = ConfigBuilder::new(
|
let config = ConfigBuilder::new(
|
||||||
current_dir.join("1.21.4"),
|
current_dir.join(&version.id),
|
||||||
"1.21.4".into(),
|
version.id.clone(),
|
||||||
lyceris::auth::AuthMethod::Offline {
|
lyceris::auth::AuthMethod::Microsoft {
|
||||||
username: "Lyceris".into(),
|
username: account.username,
|
||||||
uuid: None,
|
xuid: account.xuid,
|
||||||
|
uuid: account.uuid,
|
||||||
|
access_token: account.access_token,
|
||||||
|
refresh_token: account.refresh_token,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.loader(Fabric("0.18.6".to_string()).into())
|
.loader(Fabric(version.loader_version).into())
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
println!("Starting installation");
|
println!("Starting installation");
|
||||||
|
|||||||
+1
-23
@@ -21,8 +21,7 @@ pub struct Mod {
|
|||||||
pub struct GameVersion {
|
pub struct GameVersion {
|
||||||
pub id: String,
|
pub id: String,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub release_date: String,
|
pub loader_version: String,
|
||||||
pub r#type: String, // "release" | "snapshot"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Serialize, Deserialize)]
|
#[derive(Clone, Serialize, Deserialize)]
|
||||||
@@ -33,7 +32,6 @@ pub struct LauncherSettings {
|
|||||||
|
|
||||||
// --- App State ---
|
// --- App State ---
|
||||||
pub struct AppState {
|
pub struct AppState {
|
||||||
pub versions: Vec<GameVersion>,
|
|
||||||
pub mods: Vec<Mod>,
|
pub mods: Vec<Mod>,
|
||||||
pub settings: LauncherSettings,
|
pub settings: LauncherSettings,
|
||||||
}
|
}
|
||||||
@@ -41,26 +39,6 @@ pub struct AppState {
|
|||||||
impl Default for AppState {
|
impl Default for AppState {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
versions: vec![
|
|
||||||
GameVersion {
|
|
||||||
id: "1.20.1".into(),
|
|
||||||
name: "Fabric 1.20.1".into(),
|
|
||||||
release_date: "2023-06-07".into(),
|
|
||||||
r#type: "release".into(),
|
|
||||||
},
|
|
||||||
GameVersion {
|
|
||||||
id: "1.20.4".into(),
|
|
||||||
name: "Fabric 1.20.4".into(),
|
|
||||||
release_date: "2023-12-07".into(),
|
|
||||||
r#type: "release".into(),
|
|
||||||
},
|
|
||||||
GameVersion {
|
|
||||||
id: "1.21".into(),
|
|
||||||
name: "Fabric 1.21".into(),
|
|
||||||
release_date: "2024-06-13".into(),
|
|
||||||
r#type: "release".into(),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
mods: vec![
|
mods: vec![
|
||||||
Mod {
|
Mod {
|
||||||
id: "sodium".into(),
|
id: "sodium".into(),
|
||||||
|
|||||||
+12
-2
@@ -6,6 +6,7 @@ import ModsSection from "./components/ModsSection";
|
|||||||
import SettingsSection from "./components/SettingsSection";
|
import SettingsSection from "./components/SettingsSection";
|
||||||
import { versions } from "./lib/saturn";
|
import { versions } from "./lib/saturn";
|
||||||
import { GameVersion } from "./lib/types";
|
import { GameVersion } from "./lib/types";
|
||||||
|
import { MinecraftAccount } from "./lib/auth";
|
||||||
|
|
||||||
type Section = "play" | "mods" | "settings";
|
type Section = "play" | "mods" | "settings";
|
||||||
|
|
||||||
@@ -21,6 +22,11 @@ export default function App() {
|
|||||||
const versionRef = useRef<HTMLDivElement | null>(null);
|
const versionRef = useRef<HTMLDivElement | null>(null);
|
||||||
const accountRef = useRef<HTMLDivElement | null>(null);
|
const accountRef = useRef<HTMLDivElement | null>(null);
|
||||||
|
|
||||||
|
const [accounts, setAccounts] = useState<MinecraftAccount[]>([]);
|
||||||
|
const [activeAccount, setActiveAccount] = useState<MinecraftAccount | null>(
|
||||||
|
null,
|
||||||
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
function handleClickOutside(event: MouseEvent) {
|
function handleClickOutside(event: MouseEvent) {
|
||||||
const target = event.target as Node;
|
const target = event.target as Node;
|
||||||
@@ -62,11 +68,15 @@ export default function App() {
|
|||||||
setIsAccountDropdownOpen={setIsAccountDropdownOpen}
|
setIsAccountDropdownOpen={setIsAccountDropdownOpen}
|
||||||
versionRef={versionRef}
|
versionRef={versionRef}
|
||||||
accountRef={accountRef}
|
accountRef={accountRef}
|
||||||
|
accounts={accounts}
|
||||||
|
setAccounts={setAccounts}
|
||||||
|
activeAccount={activeAccount}
|
||||||
|
setActiveAccount={setActiveAccount}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="flex-1 p-8 overflow-y-scroll">
|
<div className="flex-1 p-8 overflow-y-scroll">
|
||||||
{active === "play" && selectedVersion && (
|
{active === "play" && (
|
||||||
<PlaySection version={selectedVersion} />
|
<PlaySection version={selectedVersion} account={activeAccount} />
|
||||||
)}
|
)}
|
||||||
{active === "mods" && selectedVersion && (
|
{active === "mods" && selectedVersion && (
|
||||||
<ModsSection version={selectedVersion} />
|
<ModsSection version={selectedVersion} />
|
||||||
|
|||||||
@@ -5,12 +5,15 @@ import Bg from "@/assets/bg.png";
|
|||||||
import { listen } from "@tauri-apps/api/event";
|
import { listen } from "@tauri-apps/api/event";
|
||||||
import { GameVersion } from "@/lib/types";
|
import { GameVersion } from "@/lib/types";
|
||||||
import { launch } from "@/lib/launcher";
|
import { launch } from "@/lib/launcher";
|
||||||
|
import { MinecraftAccount } from "@/lib/auth";
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
interface PlaySectionProps {
|
interface PlaySectionProps {
|
||||||
version: GameVersion;
|
version: GameVersion | null;
|
||||||
|
account: MinecraftAccount | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const PlaySection: React.FC<PlaySectionProps> = ({ version }) => {
|
const PlaySection: React.FC<PlaySectionProps> = ({ version, account }) => {
|
||||||
const [isLaunching, setIsLaunching] = useState(false);
|
const [isLaunching, setIsLaunching] = useState(false);
|
||||||
const [launchStatus, setLaunchStatus] = useState("");
|
const [launchStatus, setLaunchStatus] = useState("");
|
||||||
|
|
||||||
@@ -44,9 +47,13 @@ const PlaySection: React.FC<PlaySectionProps> = ({ version }) => {
|
|||||||
<div className="absolute -inset-1 bg-linear-to-r from-blue-600 to-cyan-500 rounded-lg blur opacity-25 group-hover:opacity-50 transition duration-1000 group-hover:duration-200"></div>
|
<div className="absolute -inset-1 bg-linear-to-r from-blue-600 to-cyan-500 rounded-lg blur opacity-25 group-hover:opacity-50 transition duration-1000 group-hover:duration-200"></div>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
onClick={() => launch()}
|
onClick={() => version && account && launch(version, account)}
|
||||||
disabled={isLaunching}
|
disabled={isLaunching || !version || !account}
|
||||||
className="relative btn-primary px-16 py-4 text-xl flex items-center gap-3 min-w-60 justify-center"
|
className={cn(
|
||||||
|
"relative btn-primary px-16 py-4 text-xl flex items-center gap-3 min-w-60 justify-center",
|
||||||
|
(isLaunching || !version || !account) &&
|
||||||
|
"cursor-not-allowed opacity-70",
|
||||||
|
)}
|
||||||
>
|
>
|
||||||
{isLaunching ? (
|
{isLaunching ? (
|
||||||
<>
|
<>
|
||||||
@@ -56,7 +63,7 @@ const PlaySection: React.FC<PlaySectionProps> = ({ version }) => {
|
|||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<Play size={24} fill="currentColor" />
|
<Play size={24} fill="currentColor" />
|
||||||
<span>LAUNCH {version.id}</span>
|
<span>LAUNCH {version?.id}</span>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -1,15 +1,16 @@
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect } from "react";
|
||||||
import { User, LogOut } from "lucide-react";
|
import { User, LogOut } from "lucide-react";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import { Props } from "./props";
|
import { Props } from "./props";
|
||||||
import { authList, authRemove, authCreateLink, authLogin } from "@/lib/auth";
|
import {
|
||||||
|
authList,
|
||||||
|
authRemove,
|
||||||
|
authCreateLink,
|
||||||
|
authLogin,
|
||||||
|
MinecraftAccount,
|
||||||
|
} from "@/lib/auth";
|
||||||
import { listen } from "@tauri-apps/api/event";
|
import { listen } from "@tauri-apps/api/event";
|
||||||
|
|
||||||
interface Account {
|
|
||||||
uuid: string;
|
|
||||||
username: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
function UserComponent({
|
function UserComponent({
|
||||||
username,
|
username,
|
||||||
uuid,
|
uuid,
|
||||||
@@ -43,10 +44,11 @@ export default function AccountDropdown({
|
|||||||
isAccountDropdownOpen,
|
isAccountDropdownOpen,
|
||||||
setIsAccountDropdownOpen,
|
setIsAccountDropdownOpen,
|
||||||
accountRef,
|
accountRef,
|
||||||
|
activeAccount,
|
||||||
|
setActiveAccount,
|
||||||
|
accounts,
|
||||||
|
setAccounts,
|
||||||
}: Props) {
|
}: Props) {
|
||||||
const [accounts, setAccounts] = useState<Account[]>([]);
|
|
||||||
const [active, setActive] = useState<Account | null>(null);
|
|
||||||
|
|
||||||
// Load accounts
|
// Load accounts
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
loadAccounts();
|
loadAccounts();
|
||||||
@@ -56,26 +58,26 @@ export default function AccountDropdown({
|
|||||||
const accs = await authList();
|
const accs = await authList();
|
||||||
setAccounts(accs);
|
setAccounts(accs);
|
||||||
|
|
||||||
if (accs.length && !active) {
|
if (accs.length && !activeAccount) {
|
||||||
setActive(accs[0]); // default
|
setActiveAccount(accs[0]); // default
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Switch account
|
// Switch account
|
||||||
function switchAccount(acc: Account) {
|
function switchAccount(acc: MinecraftAccount) {
|
||||||
setActive(acc);
|
setActiveAccount(acc);
|
||||||
setIsAccountDropdownOpen(false);
|
setIsAccountDropdownOpen(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Logout (remove account)
|
// Logout (remove account)
|
||||||
async function logout() {
|
async function logout() {
|
||||||
if (!active) return;
|
if (!activeAccount) return;
|
||||||
|
|
||||||
await authRemove(active.uuid);
|
await authRemove(activeAccount.uuid);
|
||||||
|
|
||||||
const updated = await authList();
|
const updated = await authList();
|
||||||
setAccounts(updated);
|
setAccounts(updated);
|
||||||
setActive(updated[0] || null);
|
setActiveAccount(updated[0] || null);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add account (open Microsoft login)
|
// Add account (open Microsoft login)
|
||||||
@@ -89,7 +91,7 @@ export default function AccountDropdown({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const otherAccounts = accounts.filter((a) => a.uuid !== active?.uuid);
|
const otherAccounts = accounts.filter((a) => a.uuid !== activeAccount?.uuid);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div ref={accountRef} className="relative group overflow-visible">
|
<div ref={accountRef} className="relative group overflow-visible">
|
||||||
@@ -102,13 +104,13 @@ export default function AccountDropdown({
|
|||||||
onClick={() => setIsAccountDropdownOpen((prev: boolean) => !prev)}
|
onClick={() => setIsAccountDropdownOpen((prev: boolean) => !prev)}
|
||||||
>
|
>
|
||||||
<div className="absolute -top-2 left-4 group-hover:scale-110 transition-transform duration-700 w-12 overflow-hidden z-20">
|
<div className="absolute -top-2 left-4 group-hover:scale-110 transition-transform duration-700 w-12 overflow-hidden z-20">
|
||||||
{active && (
|
{activeAccount && (
|
||||||
<img
|
<img
|
||||||
src={`https://render.crafty.gg/3d/bust/${active.uuid}`}
|
src={`https://render.crafty.gg/3d/bust/${activeAccount.uuid}`}
|
||||||
className="w-full h-full object-cover"
|
className="w-full h-full object-cover"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{!active && (
|
{!activeAccount && (
|
||||||
<img
|
<img
|
||||||
src={`https://render.crafty.gg/3d/bust/MHF_Steve`}
|
src={`https://render.crafty.gg/3d/bust/MHF_Steve`}
|
||||||
className="w-full h-full object-cover"
|
className="w-full h-full object-cover"
|
||||||
@@ -121,7 +123,7 @@ export default function AccountDropdown({
|
|||||||
Playing as
|
Playing as
|
||||||
</p>
|
</p>
|
||||||
<p className="text-sm font-bold text-white">
|
<p className="text-sm font-bold text-white">
|
||||||
{active?.username || "No Account"}
|
{activeAccount?.username || "No Account"}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
@@ -136,8 +138,11 @@ export default function AccountDropdown({
|
|||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{/* Current account */}
|
{/* Current account */}
|
||||||
{active && (
|
{activeAccount && (
|
||||||
<UserComponent username={active.username} uuid={active.uuid} />
|
<UserComponent
|
||||||
|
username={activeAccount.username}
|
||||||
|
uuid={activeAccount.uuid}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Other accounts */}
|
{/* Other accounts */}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { MinecraftAccount } from "@/lib/auth";
|
||||||
import { GameVersion } from "@/lib/types";
|
import { GameVersion } from "@/lib/types";
|
||||||
|
|
||||||
export type SetState<T> = React.Dispatch<React.SetStateAction<T>>;
|
export type SetState<T> = React.Dispatch<React.SetStateAction<T>>;
|
||||||
@@ -15,4 +16,10 @@ export interface Props {
|
|||||||
|
|
||||||
versionRef: React.RefObject<HTMLDivElement | null>;
|
versionRef: React.RefObject<HTMLDivElement | null>;
|
||||||
accountRef: React.RefObject<HTMLDivElement | null>;
|
accountRef: React.RefObject<HTMLDivElement | null>;
|
||||||
|
|
||||||
|
accounts: MinecraftAccount[];
|
||||||
|
setAccounts: SetState<MinecraftAccount[]>;
|
||||||
|
|
||||||
|
activeAccount: MinecraftAccount | null;
|
||||||
|
setActiveAccount: SetState<MinecraftAccount | null>;
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-2
@@ -1,8 +1,10 @@
|
|||||||
import { invoke } from "@tauri-apps/api/core";
|
import { invoke } from "@tauri-apps/api/core";
|
||||||
|
import { GameVersion } from "./types";
|
||||||
|
import { MinecraftAccount } from "./auth";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Launch the game with the selected account and version
|
* Launch the game with the selected account and version
|
||||||
*/
|
*/
|
||||||
export async function launch() {
|
export async function launch(version: GameVersion, account: MinecraftAccount) {
|
||||||
return await invoke("launch_game");
|
return await invoke("launch_game", { version, account });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ export const versions: GameVersion[] = [
|
|||||||
{
|
{
|
||||||
id: "1.21.4",
|
id: "1.21.4",
|
||||||
name: "Fabric 1.21.4",
|
name: "Fabric 1.21.4",
|
||||||
|
loader_version: "0.18.6",
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ export interface Mod {
|
|||||||
export interface GameVersion {
|
export interface GameVersion {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
|
loader_version: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface LauncherSettings {
|
export interface LauncherSettings {
|
||||||
|
|||||||
Reference in New Issue
Block a user