feat: Finally, a proper client state management
This commit is contained in:
parent
af9c628612
commit
337c4878fd
14 changed files with 298 additions and 158 deletions
25
src-tauri/Cargo.lock
generated
25
src-tauri/Cargo.lock
generated
|
|
@ -2377,6 +2377,7 @@ dependencies = [
|
|||
"tauri-build",
|
||||
"tauri-plugin-http",
|
||||
"tauri-plugin-shell",
|
||||
"tokio",
|
||||
"uuid",
|
||||
"zip",
|
||||
]
|
||||
|
|
@ -2970,6 +2971,15 @@ dependencies = [
|
|||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook-registry"
|
||||
version = "1.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "simd-adler32"
|
||||
version = "0.3.7"
|
||||
|
|
@ -3649,11 +3659,25 @@ dependencies = [
|
|||
"libc",
|
||||
"mio",
|
||||
"num_cpus",
|
||||
"parking_lot",
|
||||
"pin-project-lite",
|
||||
"signal-hook-registry",
|
||||
"socket2",
|
||||
"tokio-macros",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-macros"
|
||||
version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.52",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-util"
|
||||
version = "0.7.10"
|
||||
|
|
@ -3941,6 +3965,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "f00cc9702ca12d3c81455259621e676d0f7251cec66a21e98fe2e9a37db93b2a"
|
||||
dependencies = [
|
||||
"getrandom 0.2.12",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ tauri-build = { version = "2.0.0-beta", features = [] }
|
|||
|
||||
[dependencies]
|
||||
tauri = { version = "2.0.0-beta", features = [] }
|
||||
tokio = { version = "1", features = [ "full" ] }
|
||||
serde = { version = "1", features = [ "derive" ] }
|
||||
serde_json = "1"
|
||||
tauri-plugin-shell = "2.0.0-beta.2"
|
||||
|
|
@ -17,7 +18,7 @@ tauri-plugin-http = "2.0.0-beta"
|
|||
flate2 = "1.0.28"
|
||||
tar = "0.4.40"
|
||||
zip = "0.6.6"
|
||||
uuid = "1.7.0"
|
||||
uuid = { version = "1.7.0", features = [ "serde" ] }
|
||||
async-trait = "0.1.78"
|
||||
|
||||
[features]
|
||||
|
|
|
|||
|
|
@ -2,8 +2,12 @@ use tauri_build::{Attributes, InlinedPlugin};
|
|||
|
||||
fn main() {
|
||||
let _ = tauri_build::try_build(Attributes::new()
|
||||
.plugin(
|
||||
"auth",
|
||||
InlinedPlugin::new().commands(&["login_msa"])
|
||||
));
|
||||
.plugin("auth", InlinedPlugin::new().commands(&[
|
||||
"login_msa"
|
||||
]))
|
||||
.plugin("game", InlinedPlugin::new().commands(&[
|
||||
"launch_game",
|
||||
"set_selected_client"
|
||||
]))
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,8 @@
|
|||
"window:allow-destroy",
|
||||
"window:allow-minimize",
|
||||
"window:allow-start-dragging",
|
||||
"event:default"
|
||||
"event:default",
|
||||
"auth:default",
|
||||
"game:default"
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
[default]
|
||||
description = "Default permissions for the plugin"
|
||||
permissions = [ "launch_game" ]
|
||||
permissions = [
|
||||
"allow-launch-game",
|
||||
"allow-set-selected-client"
|
||||
]
|
||||
|
|
|
|||
|
|
@ -1,81 +1,128 @@
|
|||
use std::{env, error::Error, fs, path::PathBuf};
|
||||
|
||||
use async_trait::async_trait;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
use tauri::{AppHandle, Manager, Runtime};
|
||||
use tauri::{AppHandle, Manager};
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::utils::{file, http};
|
||||
|
||||
use super::{manifest::Manifest, JavaVersion};
|
||||
use super::{clients::vanilla::{VanillaClient, VanillaManifest}, JavaVersion};
|
||||
|
||||
#[async_trait::async_trait]
|
||||
#[macro_export]
|
||||
macro_rules! create_manifest {
|
||||
($manifest_name:ident { $($name:ident: $type:ty),* }) => {
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||
pub struct $manifest_name {
|
||||
$(pub $name: $type,)*
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! create_client {
|
||||
($client_name:ident { $($name:ident: $type:ty),* }) => {
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct $client_name {
|
||||
handle: tauri::AppHandle,
|
||||
details: crate::game::client::GameClientDetails,
|
||||
$(pub $name: $type,)*
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(tag = "type", content = "manifest")]
|
||||
pub enum GameClientType {
|
||||
Vanilla(VanillaManifest),
|
||||
// Forge(ForgeManifest),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct GameClientDetails {
|
||||
pub uuid: Uuid,
|
||||
pub name: String,
|
||||
pub version: String,
|
||||
pub main_class: String,
|
||||
pub java_version: JavaVersion,
|
||||
pub startup_args: Vec<String>,
|
||||
pub client_type: GameClientType,
|
||||
}
|
||||
|
||||
pub fn get_game_client(handle: AppHandle, details: GameClientDetails) -> Box<dyn GameClient> {
|
||||
Box::new(match details.client_type {
|
||||
GameClientType::Vanilla(_) => VanillaClient::new(handle, details),
|
||||
// GameClientType::Forge(_) => Box::new(ForgeClient::new(handle, details)),
|
||||
})
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
pub trait GameClient {
|
||||
fn new(handle: AppHandle, details: GameClientDetails) -> Self where Self: Sized;
|
||||
|
||||
fn get_handle(&self) -> &AppHandle;
|
||||
fn get_manifest(&self) -> &Manifest;
|
||||
fn get_details(&self) -> &GameClientDetails;
|
||||
fn get_client(&self) -> &GameClientType {
|
||||
&self.get_details().client_type
|
||||
}
|
||||
|
||||
async fn launch(&self) -> Result<(), Box<dyn Error>>;
|
||||
async fn install(&self) -> Result<(), Box<dyn Error>>;
|
||||
|
||||
async fn download_java(&self) -> Result<PathBuf, JavaDownloadError> {
|
||||
download_java(self.get_handle(), self.get_manifest().java_version.to_owned()).await
|
||||
let config_dir = match self.get_handle().path().app_config_dir() {
|
||||
Ok(dir) => dir,
|
||||
Err(_) => return Err(JavaDownloadError::UnsupportedOS)
|
||||
};
|
||||
|
||||
let java_dir = config_dir.join("java");
|
||||
|
||||
if !java_dir.exists() {
|
||||
match fs::create_dir_all(&java_dir) {
|
||||
Ok(_) => (),
|
||||
Err(_) => return Err(JavaDownloadError::PermissionDenied)
|
||||
};
|
||||
}
|
||||
|
||||
let java_version = &self.get_details().java_version;
|
||||
let os = env::consts::OS;
|
||||
let archive_type = match os {
|
||||
"windows" => "zip",
|
||||
"macos" => "tar.gz",
|
||||
"linux" => "tar.gz",
|
||||
_ => return Err(JavaDownloadError::UnsupportedOS)
|
||||
};
|
||||
|
||||
let archive_name = format!("zulu-{}.{}", java_version.to_string(), archive_type);
|
||||
let archive = java_dir.join(&archive_name);
|
||||
let dest = java_dir.join(format!("zulu-{}-{}", java_version.to_string(), get_arch()));
|
||||
|
||||
if archive.exists() && dest.exists() {
|
||||
let _ = fs::remove_file(archive.as_path());
|
||||
} else if archive.exists() && !dest.exists() {
|
||||
extract(&archive, &dest)?;
|
||||
} else if !archive.exists() && !dest.exists() {
|
||||
download(java_version, os, archive_type, &archive).await?;
|
||||
extract(&archive, &dest)?;
|
||||
}
|
||||
|
||||
if let Ok(mut files) = fs::read_dir(&dest) {
|
||||
let file = match files.nth(0) {
|
||||
Some(file) => file.unwrap().path(),
|
||||
None => return Err(JavaDownloadError::NoJavaVersionFound)
|
||||
};
|
||||
|
||||
return Ok(file.join("bin").join("java"));
|
||||
}
|
||||
|
||||
Err(JavaDownloadError::NoJavaVersionFound)
|
||||
}
|
||||
}
|
||||
|
||||
async fn download_java<R: Runtime>(handle: &AppHandle<R>, java_version: JavaVersion) -> Result<PathBuf, JavaDownloadError> {
|
||||
let config_dir = match handle.path().app_config_dir() {
|
||||
Ok(dir) => dir,
|
||||
Err(_) => return Err(JavaDownloadError::UnsupportedOS)
|
||||
};
|
||||
|
||||
let java_dir = config_dir.join("java");
|
||||
|
||||
if !java_dir.exists() {
|
||||
match fs::create_dir_all(&java_dir) {
|
||||
Ok(_) => (),
|
||||
Err(_) => return Err(JavaDownloadError::PermissionDenied)
|
||||
};
|
||||
}
|
||||
|
||||
// let java_version = &self.get_manifest().java_version;
|
||||
let os = env::consts::OS;
|
||||
let archive_type = match os {
|
||||
"windows" => "zip",
|
||||
"macos" => "tar.gz",
|
||||
"linux" => "tar.gz",
|
||||
_ => return Err(JavaDownloadError::UnsupportedOS)
|
||||
};
|
||||
|
||||
let archive_name = format!("zulu-{}.{}", java_version.to_string(), archive_type);
|
||||
let archive = java_dir.join(&archive_name);
|
||||
let dest = java_dir.join(format!("zulu-{}-{}", java_version.to_string(), get_arch()));
|
||||
|
||||
if archive.exists() && dest.exists() {
|
||||
let _ = fs::remove_file(archive.as_path());
|
||||
} else if archive.exists() && !dest.exists() {
|
||||
extract(&archive, &dest)?;
|
||||
} else if !archive.exists() && !dest.exists() {
|
||||
download(java_version, os, archive_type, &archive).await?;
|
||||
extract(&archive, &dest)?;
|
||||
}
|
||||
|
||||
if let Ok(mut files) = fs::read_dir(&dest) {
|
||||
let file = match files.nth(0) {
|
||||
Some(file) => file.unwrap().path(),
|
||||
None => return Err(JavaDownloadError::NoJavaVersionFound)
|
||||
};
|
||||
|
||||
return Ok(file.join("bin").join("java"));
|
||||
}
|
||||
|
||||
Err(JavaDownloadError::NoJavaVersionFound)
|
||||
}
|
||||
|
||||
pub fn get_arch() -> String {
|
||||
let arch = env::consts::ARCH;
|
||||
match arch {
|
||||
"x86" => "x86",
|
||||
"x86_64" => "x64",
|
||||
"arm" => "aarch32",
|
||||
"aarch64" => "aarch64",
|
||||
_ => "unsupported"
|
||||
}.to_string()
|
||||
#[async_trait]
|
||||
pub trait ModdedGameClient: GameClient {
|
||||
async fn install_mods(&self) -> Result<(), Box<dyn Error>>;
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
|
@ -88,7 +135,18 @@ pub enum JavaDownloadError {
|
|||
DownloadError,
|
||||
}
|
||||
|
||||
async fn get_java_versions(java_version: JavaVersion, os: &str, archive_type: &str) -> Result<Value, JavaDownloadError> {
|
||||
fn get_arch() -> String {
|
||||
let arch = env::consts::ARCH;
|
||||
match arch {
|
||||
"x86" => "x86",
|
||||
"x86_64" => "x64",
|
||||
"arm" => "aarch32",
|
||||
"aarch64" => "aarch64",
|
||||
_ => "unsupported"
|
||||
}.to_string()
|
||||
}
|
||||
|
||||
async fn get_java_versions(java_version: &JavaVersion, os: &str, archive_type: &str) -> Result<Value, JavaDownloadError> {
|
||||
let url = format!("
|
||||
https://api.azul.com/metadata/v1/zulu/packages/\
|
||||
?java_version={}\
|
||||
|
|
@ -116,7 +174,8 @@ async fn get_java_versions(java_version: JavaVersion, os: &str, archive_type: &s
|
|||
|
||||
Ok(response)
|
||||
}
|
||||
async fn download(java_version: JavaVersion, os: &str, archive_type: &str, archive: &PathBuf) -> Result<(), JavaDownloadError> {
|
||||
|
||||
async fn download(java_version: &JavaVersion, os: &str, archive_type: &str, archive: &PathBuf) -> Result<(), JavaDownloadError> {
|
||||
let response = get_java_versions(java_version, os, archive_type).await?;
|
||||
let latest = response.as_array().unwrap().first().unwrap();
|
||||
let download_url = latest.get("download_url").unwrap().as_str().unwrap();
|
||||
|
|
|
|||
1
src-tauri/src/game/clients/mod.rs
Normal file
1
src-tauri/src/game/clients/mod.rs
Normal file
|
|
@ -0,0 +1 @@
|
|||
pub mod vanilla;
|
||||
49
src-tauri/src/game/clients/vanilla.rs
Normal file
49
src-tauri/src/game/clients/vanilla.rs
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
use std::error::Error;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use tauri::AppHandle;
|
||||
use tokio::io::{stdout, AsyncWriteExt};
|
||||
|
||||
use crate::{create_client, create_manifest, game::client::{GameClient, GameClientDetails, GameClientType}};
|
||||
|
||||
create_manifest!(VanillaManifest {});
|
||||
create_client!(VanillaClient {});
|
||||
|
||||
#[async_trait]
|
||||
impl GameClient for VanillaClient {
|
||||
fn new(handle: AppHandle, details: GameClientDetails) -> Self {
|
||||
Self {
|
||||
handle: handle.clone(),
|
||||
details
|
||||
}
|
||||
}
|
||||
|
||||
fn get_handle(&self) -> &AppHandle {
|
||||
&self.handle
|
||||
}
|
||||
|
||||
fn get_details(&self) -> &GameClientDetails {
|
||||
&self.details
|
||||
}
|
||||
|
||||
fn get_client(&self) -> &GameClientType {
|
||||
&self.get_details().client_type
|
||||
}
|
||||
|
||||
async fn install(&self) -> Result<(), Box<dyn Error>> {
|
||||
println!("Installing Vanilla client");
|
||||
tokio::time::sleep(std::time::Duration::from_secs(5)).await;
|
||||
println!("Vanilla client installed");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn launch(&self) -> Result<(), Box<dyn Error>> {
|
||||
println!("Launching Vanilla client");
|
||||
tokio::time::sleep(std::time::Duration::from_secs(5)).await;
|
||||
println!("Vanilla client launched");
|
||||
tokio::time::sleep(std::time::Duration::from_secs(5)).await;
|
||||
println!("Vanilla client exited");
|
||||
stdout().flush().await?; // why...
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::JavaVersion;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct Manifest {
|
||||
pub name: String,
|
||||
pub version: String,
|
||||
pub main_class: String,
|
||||
pub java_version: JavaVersion,
|
||||
}
|
||||
|
|
@ -1,12 +1,14 @@
|
|||
use std::sync::Mutex;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tauri::{plugin::TauriPlugin, Manager};
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
use self::client::get_game_client;
|
||||
|
||||
pub mod client;
|
||||
pub mod manifest;
|
||||
mod clients;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub enum JavaVersion {
|
||||
V8,
|
||||
V16,
|
||||
|
|
@ -29,7 +31,7 @@ pub struct GameManagerState {
|
|||
}
|
||||
|
||||
unsafe impl Send for GameManagerState {}
|
||||
|
||||
unsafe impl Sync for GameManagerState {}
|
||||
impl Default for GameManagerState {
|
||||
fn default() -> Self {
|
||||
GameManagerState {
|
||||
|
|
@ -38,31 +40,38 @@ impl Default for GameManagerState {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn init<R: tauri::Runtime>() -> TauriPlugin<R> {
|
||||
pub fn init() -> TauriPlugin<tauri::Wry> {
|
||||
tauri::plugin::Builder::new("game")
|
||||
.setup(|app, _| {
|
||||
app.manage(Mutex::new(GameManagerState::default()));
|
||||
|
||||
Ok(())
|
||||
})
|
||||
.invoke_handler(tauri::generate_handler![
|
||||
launch_game
|
||||
launch_game,
|
||||
set_selected_client
|
||||
])
|
||||
.build()
|
||||
}
|
||||
|
||||
enum LaunchGameError {
|
||||
NoGameSelected
|
||||
#[tauri::command]
|
||||
async fn launch_game(state: tauri::State<'_, Mutex<GameManagerState>>) -> Result<(), String> {
|
||||
let state = state.lock().await;
|
||||
let selected_client = match &state.selected_client {
|
||||
Some(client) => client,
|
||||
None => return Err("No client selected".into())
|
||||
};
|
||||
|
||||
let res = selected_client.launch().await;
|
||||
match res {
|
||||
Ok(_) => Ok(()),
|
||||
Err(e) => Err(format!("An error has occurred: {}", e))
|
||||
}
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
async fn launch_game<R: tauri::Runtime>(app: tauri::AppHandle<R>) -> Result<(), u8> {
|
||||
let state = app.state::<Mutex<GameManagerState>>();
|
||||
let state = state.lock().unwrap();
|
||||
match state.selected_client {
|
||||
Some(ref client) => println!("Launching game: {:?}", client.get_manifest().name),
|
||||
None => println!("No game selected")
|
||||
}
|
||||
|
||||
Err(LaunchGameError::NoGameSelected as u8)
|
||||
async fn set_selected_client(handle: tauri::AppHandle, state: tauri::State<'_, Mutex<GameManagerState>>, details: client::GameClientDetails) -> Result<(), String> {
|
||||
let mut state = state.lock().await;
|
||||
let client = get_game_client(handle, details);
|
||||
state.selected_client = Some(client);
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -3,13 +3,14 @@
|
|||
|
||||
use std::error::Error;
|
||||
|
||||
use polyfrost_launcher::auth;
|
||||
use polyfrost_launcher::{auth, game};
|
||||
use tauri::{menu::Menu, App};
|
||||
|
||||
fn main() {
|
||||
tauri::Builder::default()
|
||||
.plugin(tauri_plugin_shell::init())
|
||||
.plugin(auth::init())
|
||||
.plugin(game::init())
|
||||
.menu(|handle| Menu::new(handle))
|
||||
.setup(setup)
|
||||
.run(tauri::generate_context!())
|
||||
|
|
|
|||
21
src/types/game.d.ts
vendored
21
src/types/game.d.ts
vendored
|
|
@ -1,3 +1,22 @@
|
|||
declare namespace game {
|
||||
export type Client = 'Fabric' | 'Forge' | 'Quilt' | 'Vanilla' | 'Other';
|
||||
export interface GameClientDetails {
|
||||
uuid: Uuid;
|
||||
name: string;
|
||||
version: string;
|
||||
main_class: string;
|
||||
java_version: JavaVersion;
|
||||
startup_args: Vec<string>;
|
||||
client_type: {
|
||||
type: keyof GameClientType;
|
||||
manifest: GameClientType[keyof GameClientType];
|
||||
};
|
||||
}
|
||||
|
||||
export interface GameClientType {
|
||||
Vanilla: VanillaManifest;
|
||||
}
|
||||
|
||||
export type ClientType = keyof GameClientType;
|
||||
|
||||
export interface VanillaManifest {}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,15 +1,40 @@
|
|||
import { invoke } from '@tauri-apps/api/core';
|
||||
import * as uuid from 'uuid';
|
||||
import Button from '../components/base/Button';
|
||||
|
||||
function BrowserPage() {
|
||||
function downloadJava() {
|
||||
invoke('download_java_test');
|
||||
}
|
||||
|
||||
return (
|
||||
<div class="flex flex-col gap-y-4">
|
||||
<h1>Browser</h1>
|
||||
<Button onClick={() => downloadJava()}>Download java</Button>
|
||||
<Button
|
||||
onClick={async () => {
|
||||
const value = await invoke('plugin:game|launch_game');
|
||||
console.log(`hello${value}`);
|
||||
}}
|
||||
>
|
||||
Test Button
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
onClick={() => {
|
||||
invoke('plugin:game|set_selected_client', {
|
||||
details: {
|
||||
uuid: uuid.v4(),
|
||||
name: 'my vanilla instance',
|
||||
version: '1.8.9',
|
||||
main_class: 'net.minecraft.client.main.Main',
|
||||
java_version: 'v8',
|
||||
startup_args: ['--username', 'player', '--password', 'password'],
|
||||
client_type: {
|
||||
type: 'Vanilla',
|
||||
manifest: {},
|
||||
},
|
||||
} as game.GameClientDetails,
|
||||
});
|
||||
}}
|
||||
>
|
||||
Test Button2
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ import Button from '../components/base/Button';
|
|||
import Tag from '../components/base/Tag';
|
||||
import TextField from '../components/base/TextField';
|
||||
import defaultCover from '../../assets/images/default_instance_cover.jpg';
|
||||
import { upperFirst } from '../../utils/string';
|
||||
|
||||
// TODO: Replace this into it's own component
|
||||
function OneConfigLogo() {
|
||||
|
|
@ -62,7 +61,7 @@ function Banner() {
|
|||
interface InstanceCardProps {
|
||||
name: string;
|
||||
version: string;
|
||||
client: game.Client;
|
||||
clientType: game.ClientType;
|
||||
id: number;
|
||||
mods?: number;
|
||||
cover?: string;
|
||||
|
|
@ -100,7 +99,7 @@ function InstanceCard(props: InstanceCardProps) {
|
|||
<div class="flex flex-col gap-1.5">
|
||||
<p class="h-4 font-semibold">{props.name}</p>
|
||||
<p class="h-4 text-xs">
|
||||
{props.client}
|
||||
{props.clientType}
|
||||
{' '}
|
||||
{props.version}
|
||||
{' '}
|
||||
|
|
@ -167,56 +166,10 @@ function HomePage() {
|
|||
const myInstances: InstanceCardProps[] = [
|
||||
{
|
||||
name: 'Hypixel',
|
||||
client: 'Vanilla',
|
||||
clientType: 'Vanilla',
|
||||
version: '1.8.9',
|
||||
id: 1,
|
||||
},
|
||||
{
|
||||
name: 'OneConfig ??',
|
||||
client: 'Forge',
|
||||
version: '1.8.9',
|
||||
mods: 12,
|
||||
id: 2,
|
||||
},
|
||||
{
|
||||
name: 'Fabulously Optimised',
|
||||
client: 'Fabric',
|
||||
version: '1.20.4',
|
||||
mods: 48,
|
||||
id: 3,
|
||||
},
|
||||
{
|
||||
name: 'sus client',
|
||||
client: 'Other',
|
||||
version: '1.8.9',
|
||||
id: 4,
|
||||
},
|
||||
{
|
||||
name: 'Hypixel',
|
||||
client: 'Vanilla',
|
||||
version: '1.8.9',
|
||||
id: 5,
|
||||
},
|
||||
{
|
||||
name: 'OneConfig ??',
|
||||
client: 'Forge',
|
||||
version: '1.8.9',
|
||||
mods: 12,
|
||||
id: 6,
|
||||
},
|
||||
{
|
||||
name: 'Fabulously Optimised',
|
||||
client: 'Fabric',
|
||||
version: '1.20.4',
|
||||
mods: 48,
|
||||
id: 7,
|
||||
},
|
||||
{
|
||||
name: 'sus client',
|
||||
client: 'Other',
|
||||
version: '1.8.9',
|
||||
id: 8,
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue