refactor: Lots of stuff refactored
This commit is contained in:
parent
0dd1365fa4
commit
fb14f96eea
16 changed files with 326 additions and 294 deletions
|
|
@ -65,7 +65,7 @@ pub enum ErrorKind {
|
|||
|
||||
/// Wrapper around [`crate::game::client::ClientManagerError`] to handle directory errors.
|
||||
#[error("failed to manage clients: {0}")]
|
||||
ClientManagerError(#[from] crate::game::client::ClientManagerError),
|
||||
ClientManagerError(#[from] crate::game::client_manager::ClientManagerError),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
|
|
|||
|
|
@ -1,27 +1,21 @@
|
|||
use std::{collections::HashMap, fs, path::PathBuf};
|
||||
use std::path::PathBuf;
|
||||
|
||||
use anyhow::anyhow;
|
||||
use async_trait::async_trait;
|
||||
use chrono::{DateTime, Local};
|
||||
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::utils::dirs;
|
||||
|
||||
use super::{
|
||||
minecraft::{MinecraftManifest, ReleaseType},
|
||||
vanilla::{self, VanillaClientProps},
|
||||
};
|
||||
use super::{clients::ClientType, minecraft::{MinecraftManifest, ReleaseType}};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Version {
|
||||
id: String,
|
||||
url: String,
|
||||
pub struct MinecraftVersion {
|
||||
pub id: String,
|
||||
pub url: String,
|
||||
#[serde(default)]
|
||||
release_type: ReleaseType,
|
||||
pub release_type: ReleaseType,
|
||||
#[serde(default)]
|
||||
release_time: chrono::DateTime<chrono::Utc>,
|
||||
pub release_time: chrono::DateTime<chrono::Utc>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
|
|
@ -41,30 +35,6 @@ pub struct Cluster {
|
|||
pub client: ClientType,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(tag = "type")]
|
||||
pub enum ClientType {
|
||||
Vanilla(VanillaClientProps),
|
||||
}
|
||||
|
||||
impl ClientType {
|
||||
pub async fn get_versions(&self, file: Option<&PathBuf>) -> crate::Result<Vec<Version>> {
|
||||
match self {
|
||||
ClientType::Vanilla(_) => vanilla::get_versions(file).await,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_impl<'a>(
|
||||
client: &'a ClientType,
|
||||
cluster: &'a Cluster,
|
||||
manifest: &'a Manifest,
|
||||
) -> Box<dyn ClientTrait<'a> + 'a> {
|
||||
Box::new(match client {
|
||||
ClientType::Vanilla(_) => vanilla::VanillaClient::new(cluster, manifest),
|
||||
})
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
pub trait ClientTrait<'a>: Send + Sync {
|
||||
fn new(cluster: &'a Cluster, manifest: &'a Manifest) -> Self
|
||||
|
|
@ -122,184 +92,4 @@ macro_rules! impl_game_client {
|
|||
self.manifest
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum ClientManagerError {
|
||||
#[error("Cluster not found")]
|
||||
ClusterNotFound,
|
||||
#[error("Manifest not found")]
|
||||
ManifestNotFound,
|
||||
}
|
||||
|
||||
pub struct ClientManager {
|
||||
clusters: HashMap<Uuid, Cluster>,
|
||||
manifests: HashMap<Uuid, Manifest>,
|
||||
}
|
||||
|
||||
impl ClientManager {
|
||||
pub fn new() -> crate::Result<Self> {
|
||||
let clusters = load_and_serialize::<Cluster>(&dirs::clusters_dir()?)?;
|
||||
|
||||
let manifests = load_and_serialize::<Manifest>(&dirs::manifests_dir()?)?;
|
||||
|
||||
Ok(Self {
|
||||
clusters,
|
||||
manifests,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_impl_uuid<'a>(&'a self, uuid: Uuid) -> crate::Result<Box<dyn ClientTrait<'a> + 'a>> {
|
||||
let cluster = self.get_cluster(uuid)?;
|
||||
let manifest = self.get_manifest(uuid)?;
|
||||
|
||||
Ok(get_impl(&cluster.client, &cluster, &manifest))
|
||||
}
|
||||
|
||||
pub fn get_clusters(&self) -> Vec<&Cluster> {
|
||||
self.clusters.values().collect()
|
||||
}
|
||||
|
||||
pub fn get_clusters_owned(&self) -> Vec<Cluster> {
|
||||
self.clusters.values().cloned().collect()
|
||||
}
|
||||
|
||||
pub fn get_cluster(&self, uuid: Uuid) -> crate::Result<&Cluster> {
|
||||
self.clusters
|
||||
.get(&uuid)
|
||||
.ok_or(ClientManagerError::ClusterNotFound.into())
|
||||
}
|
||||
|
||||
pub fn get_cluster_mut(&mut self, uuid: Uuid) -> crate::Result<&mut Cluster> {
|
||||
self.clusters
|
||||
.get_mut(&uuid)
|
||||
.ok_or(ClientManagerError::ClusterNotFound.into())
|
||||
}
|
||||
|
||||
pub fn get_manifest(&self, uuid: Uuid) -> crate::Result<&Manifest> {
|
||||
self.manifests
|
||||
.get(&uuid)
|
||||
.ok_or(ClientManagerError::ManifestNotFound.into())
|
||||
}
|
||||
|
||||
pub fn get_manifest_mut(&mut self, uuid: Uuid) -> crate::Result<&mut Manifest> {
|
||||
self.manifests
|
||||
.get_mut(&uuid)
|
||||
.ok_or(ClientManagerError::ManifestNotFound.into())
|
||||
}
|
||||
|
||||
pub async fn create_cluster(
|
||||
&mut self,
|
||||
name: String,
|
||||
version: String,
|
||||
cover: Option<String>,
|
||||
group: Option<Uuid>,
|
||||
client: ClientType,
|
||||
) -> crate::Result<Uuid> {
|
||||
let version_cache = dirs::app_config_dir()?.join("versions_cache.json");
|
||||
let versions = client.get_versions(Some(&version_cache)).await?;
|
||||
let url = &versions
|
||||
.iter()
|
||||
.find(|v| v.id == version)
|
||||
.ok_or(anyhow!("Version not found"))?
|
||||
.url;
|
||||
|
||||
let uuid = Uuid::new_v4();
|
||||
|
||||
// Save the manifest
|
||||
let minecraft_manifest = vanilla::retrieve_version_manifest(url).await?;
|
||||
let manifest = Manifest {
|
||||
id: uuid,
|
||||
manifest: minecraft_manifest,
|
||||
};
|
||||
|
||||
save(
|
||||
&dirs::manifests_dir()?.join(format!("{}.json", uuid)),
|
||||
&manifest,
|
||||
)?;
|
||||
self.manifests.insert(uuid, manifest);
|
||||
|
||||
// Save the cluster
|
||||
let cluster = Cluster {
|
||||
id: uuid,
|
||||
created_at: Local::now(),
|
||||
name,
|
||||
cover,
|
||||
group,
|
||||
client,
|
||||
};
|
||||
|
||||
save(
|
||||
&dirs::clusters_dir()?.join(format!("{}.json", uuid)),
|
||||
&cluster,
|
||||
)?;
|
||||
self.clusters.insert(uuid, cluster);
|
||||
|
||||
Ok(uuid)
|
||||
}
|
||||
}
|
||||
|
||||
fn save<T>(path: &PathBuf, object: T) -> crate::Result<()>
|
||||
where
|
||||
T: Serialize,
|
||||
{
|
||||
if !path.exists() {
|
||||
fs::create_dir_all(
|
||||
path.parent()
|
||||
.expect("Couldn't get parent of path whilst saving object. 'client.rs'"),
|
||||
)?;
|
||||
}
|
||||
|
||||
let content = serde_json::to_string(&object)?;
|
||||
fs::write(path, content)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn load_and_serialize<T>(dir: &PathBuf) -> crate::Result<HashMap<Uuid, T>>
|
||||
where
|
||||
T: DeserializeOwned,
|
||||
{
|
||||
let mut result = HashMap::<Uuid, T>::new();
|
||||
|
||||
if !dir.exists() {
|
||||
fs::create_dir_all(dir)?;
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
for file in fs::read_dir(dir)? {
|
||||
let file = file?;
|
||||
let file_name = file.file_name();
|
||||
let file_name = file_name
|
||||
.to_str()
|
||||
.ok_or(anyhow!("Couldn't convert OsStr to str"))?
|
||||
.replace(".json", "");
|
||||
let uuid = match Uuid::parse_str(&file_name) {
|
||||
Ok(uuid) => uuid,
|
||||
Err(_) => {
|
||||
eprintln!("Couldn't parse file name as UUID: '{:?}'", file_name);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
let path = file.path();
|
||||
let content = match fs::read_to_string(&path) {
|
||||
Ok(content) => content,
|
||||
Err(_) => {
|
||||
eprintln!("Couldn't read file: '{:?}'", path);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
let parsed = match serde_json::from_str::<T>(&content) {
|
||||
Ok(parsed) => parsed,
|
||||
Err(_) => {
|
||||
eprintln!("Couldn't parse file '{}' content as JSON", path.display());
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
result.insert(uuid, parsed);
|
||||
}
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
203
onelauncher/src/game/client_manager.rs
Normal file
203
onelauncher/src/game/client_manager.rs
Normal file
|
|
@ -0,0 +1,203 @@
|
|||
use std::{collections::HashMap, fs, path::PathBuf};
|
||||
|
||||
use anyhow::anyhow;
|
||||
use chrono::Local;
|
||||
use serde::{de::DeserializeOwned, Serialize};
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::utils::dirs;
|
||||
|
||||
use super::{client::{ClientTrait, Cluster, Manifest}, clients::{self, vanilla, ClientType}};
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum ClientManagerError {
|
||||
#[error("Cluster not found")]
|
||||
ClusterNotFound,
|
||||
#[error("Manifest not found")]
|
||||
ManifestNotFound,
|
||||
}
|
||||
|
||||
pub struct ClientManager {
|
||||
clusters: HashMap<Uuid, Cluster>,
|
||||
manifests: HashMap<Uuid, Manifest>,
|
||||
}
|
||||
|
||||
impl ClientManager {
|
||||
pub fn new() -> crate::Result<Self> {
|
||||
let clusters = load_and_serialize::<Cluster>(&dirs::clusters_dir()?)?;
|
||||
|
||||
let manifests = load_and_serialize::<Manifest>(&dirs::manifests_dir()?)?;
|
||||
|
||||
Ok(Self {
|
||||
clusters,
|
||||
manifests,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_impl_uuid<'a>(&'a self, uuid: Uuid) -> crate::Result<Box<dyn ClientTrait<'a> + 'a>> {
|
||||
let cluster = self.get_cluster(uuid)?;
|
||||
let manifest = self.get_manifest(uuid)?;
|
||||
|
||||
Ok(clients::get_impl(&cluster.client, &cluster, &manifest))
|
||||
}
|
||||
|
||||
pub fn get_clusters(&self) -> Vec<&Cluster> {
|
||||
self.clusters.values().collect()
|
||||
}
|
||||
|
||||
pub fn get_clusters_owned(&self) -> Vec<Cluster> {
|
||||
self.clusters.values().cloned().collect()
|
||||
}
|
||||
|
||||
pub fn get_cluster(&self, uuid: Uuid) -> crate::Result<&Cluster> {
|
||||
self.clusters
|
||||
.get(&uuid)
|
||||
.ok_or(ClientManagerError::ClusterNotFound.into())
|
||||
}
|
||||
|
||||
pub fn get_cluster_mut(&mut self, uuid: Uuid) -> crate::Result<&mut Cluster> {
|
||||
self.clusters
|
||||
.get_mut(&uuid)
|
||||
.ok_or(ClientManagerError::ClusterNotFound.into())
|
||||
}
|
||||
|
||||
pub fn get_manifest(&self, uuid: Uuid) -> crate::Result<&Manifest> {
|
||||
self.manifests
|
||||
.get(&uuid)
|
||||
.ok_or(ClientManagerError::ManifestNotFound.into())
|
||||
}
|
||||
|
||||
pub fn get_manifest_mut(&mut self, uuid: Uuid) -> crate::Result<&mut Manifest> {
|
||||
self.manifests
|
||||
.get_mut(&uuid)
|
||||
.ok_or(ClientManagerError::ManifestNotFound.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl ClientManager {
|
||||
pub async fn launch_cluster(
|
||||
&mut self,
|
||||
uuid: Uuid,
|
||||
) -> crate::Result<()> {
|
||||
let client = self.get_impl_uuid(uuid)?;
|
||||
client.setup().await?;
|
||||
client.launch().await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn create_cluster(
|
||||
&mut self,
|
||||
name: String,
|
||||
version: String,
|
||||
cover: Option<String>,
|
||||
group: Option<Uuid>,
|
||||
client: ClientType,
|
||||
) -> crate::Result<Uuid> {
|
||||
let version_cache = dirs::app_config_dir()?.join("versions_cache.json");
|
||||
let versions = clients::get_versions(&client, Some(&version_cache)).await?;
|
||||
let url = &versions
|
||||
.iter()
|
||||
.find(|v| v.id == version)
|
||||
.ok_or(anyhow!("Version not found"))?
|
||||
.url;
|
||||
|
||||
let uuid = Uuid::new_v4();
|
||||
|
||||
// Save the manifest
|
||||
// TODO: Is this correct?
|
||||
let minecraft_manifest = vanilla::retrieve_version_manifest(url).await?;
|
||||
let manifest = Manifest {
|
||||
id: uuid,
|
||||
manifest: minecraft_manifest,
|
||||
};
|
||||
|
||||
save(
|
||||
&dirs::manifests_dir()?.join(format!("{}.json", uuid)),
|
||||
&manifest,
|
||||
)?;
|
||||
self.manifests.insert(uuid, manifest);
|
||||
|
||||
// Save the cluster
|
||||
let cluster = Cluster {
|
||||
id: uuid,
|
||||
created_at: Local::now(),
|
||||
name,
|
||||
cover,
|
||||
group,
|
||||
client,
|
||||
};
|
||||
|
||||
save(
|
||||
&dirs::clusters_dir()?.join(format!("{}.json", uuid)),
|
||||
&cluster,
|
||||
)?;
|
||||
self.clusters.insert(uuid, cluster);
|
||||
|
||||
Ok(uuid)
|
||||
}
|
||||
}
|
||||
|
||||
fn save<T>(path: &PathBuf, object: T) -> crate::Result<()>
|
||||
where
|
||||
T: Serialize,
|
||||
{
|
||||
if !path.exists() {
|
||||
fs::create_dir_all(
|
||||
path.parent()
|
||||
.expect("Couldn't get parent of path whilst saving object. 'client.rs'"),
|
||||
)?;
|
||||
}
|
||||
|
||||
let content = serde_json::to_string(&object)?;
|
||||
fs::write(path, content)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn load_and_serialize<T>(dir: &PathBuf) -> crate::Result<HashMap<Uuid, T>>
|
||||
where
|
||||
T: DeserializeOwned,
|
||||
{
|
||||
let mut result = HashMap::<Uuid, T>::new();
|
||||
|
||||
if !dir.exists() {
|
||||
fs::create_dir_all(dir)?;
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
for file in fs::read_dir(dir)? {
|
||||
let file = file?;
|
||||
let file_name = file.file_name();
|
||||
let file_name = file_name
|
||||
.to_str()
|
||||
.ok_or(anyhow!("Couldn't convert OsStr to str"))?
|
||||
.replace(".json", "");
|
||||
let uuid = match Uuid::parse_str(&file_name) {
|
||||
Ok(uuid) => uuid,
|
||||
Err(_) => {
|
||||
eprintln!("Couldn't parse file name as UUID: '{:?}'", file_name);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
let path = file.path();
|
||||
let content = match fs::read_to_string(&path) {
|
||||
Ok(content) => content,
|
||||
Err(_) => {
|
||||
eprintln!("Couldn't read file: '{:?}'", path);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
let parsed = match serde_json::from_str::<T>(&content) {
|
||||
Ok(parsed) => parsed,
|
||||
Err(_) => {
|
||||
eprintln!("Couldn't parse file '{}' content as JSON", path.display());
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
result.insert(uuid, parsed);
|
||||
}
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
27
onelauncher/src/game/clients/mod.rs
Normal file
27
onelauncher/src/game/clients/mod.rs
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
use std::path::PathBuf;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use self::vanilla::VanillaClientProps;
|
||||
|
||||
use super::client::{ClientTrait, Cluster, Manifest, MinecraftVersion};
|
||||
|
||||
pub mod vanilla;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(tag = "type")]
|
||||
pub enum ClientType {
|
||||
Vanilla(VanillaClientProps),
|
||||
}
|
||||
|
||||
pub async fn get_versions(client: &ClientType, file: Option<&PathBuf>) -> crate::Result<Vec<MinecraftVersion>> {
|
||||
match client {
|
||||
ClientType::Vanilla(_) => vanilla::get_versions(file).await,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_impl<'a>(client: &'a ClientType, cluster: &'a Cluster, manifest: &'a Manifest) -> Box<dyn ClientTrait<'a> + 'a> {
|
||||
Box::new(match client {
|
||||
ClientType::Vanilla(_) => vanilla::VanillaClient::new(cluster, manifest),
|
||||
})
|
||||
}
|
||||
|
|
@ -1,11 +1,9 @@
|
|||
use super::{
|
||||
client::{ClientTrait, Cluster, Manifest, Version},
|
||||
minecraft::MinecraftManifest,
|
||||
};
|
||||
use crate::{
|
||||
constants::MINECRAFT_VERSIONS_MANIFEST,
|
||||
create_game_client, impl_game_client,
|
||||
utils::{dirs, file, http},
|
||||
constants::MINECRAFT_VERSIONS_MANIFEST,
|
||||
create_game_client,
|
||||
game::{client::{ClientTrait, Cluster, Manifest, MinecraftVersion}, minecraft::MinecraftManifest},
|
||||
impl_game_client,
|
||||
utils::{dirs, http}
|
||||
};
|
||||
use anyhow::anyhow;
|
||||
use async_trait::async_trait;
|
||||
|
|
@ -31,6 +29,8 @@ impl<'a> ClientTrait<'a> for VanillaClient<'a> {
|
|||
}
|
||||
|
||||
async fn setup(&self) -> crate::Result<()> {
|
||||
let client_path = self.install_game().await?;
|
||||
println!("{:?}", client_path);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
@ -42,17 +42,7 @@ impl<'a> ClientTrait<'a> for VanillaClient<'a> {
|
|||
fs::create_dir_all(&file)?;
|
||||
let artifact = &manifest.downloads.client;
|
||||
|
||||
http::download_file(&artifact.url, &file).await?;
|
||||
let file_hash = file::file_sha1(&file)?;
|
||||
|
||||
println!("Downloaded: '{}' | '{}'", file_hash, artifact.sha1);
|
||||
if file_hash != artifact.sha1 {
|
||||
return Err(std::io::Error::new(
|
||||
std::io::ErrorKind::InvalidData,
|
||||
"Hashes do not match",
|
||||
)
|
||||
.into());
|
||||
}
|
||||
http::download_file_sha1_check(&artifact.url, &file, &artifact.sha1).await?;
|
||||
}
|
||||
|
||||
Ok(file)
|
||||
|
|
@ -73,16 +63,16 @@ impl<'a> ClientTrait<'a> for VanillaClient<'a> {
|
|||
|
||||
#[derive(Clone, Serialize, Deserialize)]
|
||||
struct VersionList {
|
||||
versions: Vec<Version>,
|
||||
versions: Vec<MinecraftVersion>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct CachedVersions {
|
||||
last_updated: i64,
|
||||
versions: Vec<Version>,
|
||||
versions: Vec<MinecraftVersion>,
|
||||
}
|
||||
|
||||
pub async fn get_versions(file: Option<&PathBuf>) -> crate::Result<Vec<Version>> {
|
||||
pub async fn get_versions(file: Option<&PathBuf>) -> crate::Result<Vec<MinecraftVersion>> {
|
||||
if let Some(file) = file {
|
||||
if file.exists() && file.is_file() {
|
||||
if let Some(versions) = get_cached_versions(file).await? {
|
||||
|
|
@ -110,7 +100,7 @@ pub async fn get_versions(file: Option<&PathBuf>) -> crate::Result<Vec<Version>>
|
|||
Ok(response.versions)
|
||||
}
|
||||
|
||||
async fn get_cached_versions(file: &PathBuf) -> crate::Result<Option<Vec<Version>>> {
|
||||
async fn get_cached_versions(file: &PathBuf) -> crate::Result<Option<Vec<MinecraftVersion>>> {
|
||||
let cached = serde_json::from_str::<CachedVersions>(&fs::read_to_string(file)?)?;
|
||||
let head_request = http::create_client()?
|
||||
.head(MINECRAFT_VERSIONS_MANIFEST)
|
||||
|
|
@ -104,7 +104,7 @@ async fn download(
|
|||
let latest = response.as_array().unwrap().first().unwrap();
|
||||
let download_url = latest.get("download_url").unwrap().as_str().unwrap();
|
||||
|
||||
if let Err(err) = http::download_file(download_url, archive.as_path()).await {
|
||||
if let Err(err) = http::download_file(download_url, archive).await {
|
||||
return Err(ErrorKind::JavaError(JavaDownloadError::DownloadError(err.to_string())).into());
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -30,8 +30,8 @@ pub struct MinecraftManifest {
|
|||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ModernArguments {
|
||||
game: Vec<ModernArgumentsItem>,
|
||||
jvm: Vec<ModernArgumentsItem>,
|
||||
pub game: Vec<ModernArgumentsItem>,
|
||||
pub jvm: Vec<ModernArgumentsItem>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
|
|
@ -46,11 +46,11 @@ pub enum ModernArgumentsItem {
|
|||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ArgumentRule {
|
||||
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||
rules: Vec<Rule>,
|
||||
pub rules: Vec<Rule>,
|
||||
|
||||
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||
#[serde(deserialize_with = "string_or_seq")]
|
||||
value: Vec<String>,
|
||||
pub value: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
pub mod client;
|
||||
pub mod java;
|
||||
pub mod minecraft;
|
||||
pub mod vanilla;
|
||||
pub mod clients;
|
||||
pub mod client_manager;
|
||||
|
|
|
|||
|
|
@ -1,34 +1,39 @@
|
|||
use std::{
|
||||
fs::File,
|
||||
io::{self, Cursor},
|
||||
path::Path,
|
||||
path::PathBuf,
|
||||
};
|
||||
|
||||
use reqwest::{Client, ClientBuilder};
|
||||
|
||||
use crate::{constants::USER_AGENT, ErrorKind};
|
||||
|
||||
use super::file::file_sha1;
|
||||
|
||||
pub fn create_client() -> crate::Result<Client> {
|
||||
Ok(ClientBuilder::new()
|
||||
.user_agent(USER_AGENT)
|
||||
.build()
|
||||
.map_err(|err| ErrorKind::HTTPError(err))?)
|
||||
Ok(ClientBuilder::new().user_agent(USER_AGENT).build()?)
|
||||
}
|
||||
|
||||
// can we not make mapping the errors automatic, isnt that the whole point
|
||||
pub async fn download_file(url: &str, path: &Path) -> crate::Result<()> {
|
||||
let response = create_client()?
|
||||
.get(url)
|
||||
.send()
|
||||
.await
|
||||
.map_err(|err| ErrorKind::HTTPError(err))?;
|
||||
pub async fn download_file_sha1_check(url: &str, path: &PathBuf, sha1: &str) -> crate::Result<()> {
|
||||
download_file(url, path).await?;
|
||||
|
||||
let file_hash = file_sha1(path)?;
|
||||
if file_hash != sha1 {
|
||||
return Err(std::io::Error::new(
|
||||
std::io::ErrorKind::InvalidData,
|
||||
"Hashes do not match",
|
||||
).into());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn download_file(url: &str, path: &PathBuf) -> crate::Result<()> {
|
||||
let response = create_client()?.get(url).send().await?;
|
||||
let mut file = File::create(path).map_err(|err| ErrorKind::IOError(err))?;
|
||||
let mut content = Cursor::new(
|
||||
response
|
||||
.bytes()
|
||||
.await
|
||||
.map_err(|err| ErrorKind::HTTPError(err))?,
|
||||
);
|
||||
let mut content = Cursor::new(response.bytes().await?);
|
||||
|
||||
io::copy(&mut content, &mut file).map_err(|err| ErrorKind::IOError(err))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ fn main() {
|
|||
InlinedPlugin::new().commands(&[
|
||||
"login_msa",
|
||||
"refresh_client_manager",
|
||||
"launch_cluster",
|
||||
"create_cluster",
|
||||
"get_clusters",
|
||||
"get_cluster",
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
description = "Default permissions for the plugin"
|
||||
permissions = [
|
||||
"allow-refresh-client-manager",
|
||||
"allow-launch-cluster",
|
||||
"allow-create-cluster",
|
||||
"allow-get-clusters",
|
||||
"allow-get-cluster",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use onelauncher::game::client::{ClientType, Cluster, Manifest};
|
||||
use onelauncher::game::{client::{Cluster, Manifest}, clients::ClientType};
|
||||
use tauri::State;
|
||||
use tokio::sync::Mutex;
|
||||
use uuid::Uuid;
|
||||
|
|
@ -39,6 +39,16 @@ pub async fn get_cluster(
|
|||
Ok(instance.clone())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn launch_cluster(
|
||||
state: State<'_, Mutex<GameManagerState>>,
|
||||
uuid: Uuid,
|
||||
) -> Result<(), String> {
|
||||
let manager = &mut state.lock().await.client_manager;
|
||||
manager.launch_cluster(uuid).await?; // TODO: Change how this works
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn get_manifest(
|
||||
state: State<'_, Mutex<GameManagerState>>,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use onelauncher::game::client::ClientManager;
|
||||
use onelauncher::game::client_manager::ClientManager;
|
||||
use tauri::{plugin::TauriPlugin, Manager, State};
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
|
|
@ -27,6 +27,7 @@ pub fn init() -> TauriPlugin<tauri::Wry> {
|
|||
game::get_clusters,
|
||||
game::get_cluster,
|
||||
game::get_manifest,
|
||||
game::launch_cluster,
|
||||
refresh_client_manager,
|
||||
])
|
||||
.build()
|
||||
|
|
|
|||
|
|
@ -19,8 +19,3 @@ export async function getUuidHeadSrc(uuid: string): Promise<string> {
|
|||
// TODO implement some kind of caching + fallback
|
||||
return `https://crafatar.com/avatars/${uuid}?size=32`;
|
||||
}
|
||||
|
||||
export default {
|
||||
loginMicrosoft,
|
||||
getUuidHeadSrc,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,15 +1,17 @@
|
|||
import { invoke } from '@tauri-apps/api/core';
|
||||
|
||||
export async function getCluster(uuid: string): Promise<Core.Cluster> {
|
||||
return await invoke<Core.Cluster>('plugin:onelauncher|get_cluster', {
|
||||
uuid,
|
||||
});
|
||||
return await invoke<Core.Cluster>(
|
||||
'plugin:onelauncher|get_cluster',
|
||||
{ uuid },
|
||||
);
|
||||
}
|
||||
|
||||
export async function getManifest(uuid: string): Promise<Core.Manifest> {
|
||||
return await invoke<Core.Manifest>('plugin:onelauncher|get_manifest', {
|
||||
uuid,
|
||||
});
|
||||
return await invoke<Core.Manifest>(
|
||||
'plugin:onelauncher|get_manifest',
|
||||
{ uuid },
|
||||
);
|
||||
}
|
||||
|
||||
export async function getClustersWithManifests(): Promise<Core.ClusterWithManifest[]> {
|
||||
|
|
@ -58,3 +60,10 @@ export async function createCluster(cluster: Omit<Core.Cluster, 'id' | 'createdA
|
|||
export async function refreshClientManager(): Promise<void> {
|
||||
return await invoke('plugin:onelauncher|refresh_client_manager');
|
||||
}
|
||||
|
||||
export async function launchCluster(uuid: string): Promise<number> {
|
||||
return await invoke<number>(
|
||||
'plugin:onelauncher|launch_cluster',
|
||||
{ uuid },
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,12 +10,12 @@ import type { Accessor, Setter } from 'solid-js';
|
|||
import { For, Show, createResource, createSignal } from 'solid-js';
|
||||
import { useNavigate } from '@solidjs/router';
|
||||
import image from '../../assets/images/header.png';
|
||||
import defaultCover from '../../assets/images/default_instance_cover.jpg';
|
||||
import ContextMenu from '../components/overlay/ContextMenu';
|
||||
import { createCluster, getClustersGrouped, launchCluster } from '../../bridge/game';
|
||||
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 ContextMenu from '../components/overlay/ContextMenu';
|
||||
import { createCluster, getClustersGrouped } from '../../bridge/game';
|
||||
|
||||
// TODO: Replace this into it's own component
|
||||
// possibly with the core ui lirbary with autogenerated and optimized svgs pls
|
||||
|
|
@ -71,6 +71,14 @@ interface ClusterCardContextMenuProps {
|
|||
function ClusterCardContextMenu(props: ClusterCardContextMenuProps) {
|
||||
const navigate = useNavigate();
|
||||
|
||||
function viewCluster() {
|
||||
navigate(`/clusters/?id=${props.id}`);
|
||||
}
|
||||
|
||||
function launch() {
|
||||
launchCluster(props.id);
|
||||
}
|
||||
|
||||
return (
|
||||
<ContextMenu
|
||||
pos={props.pos}
|
||||
|
|
@ -80,9 +88,7 @@ function ClusterCardContextMenu(props: ClusterCardContextMenuProps) {
|
|||
<ContextMenu.Row
|
||||
icon={<EyeIcon />}
|
||||
text="View"
|
||||
onClick={() => {
|
||||
navigate(`/clusters/?id=${props.id}`);
|
||||
}}
|
||||
onClick={() => viewCluster()}
|
||||
/>
|
||||
|
||||
<ContextMenu.Seperator />
|
||||
|
|
@ -90,20 +96,12 @@ function ClusterCardContextMenu(props: ClusterCardContextMenuProps) {
|
|||
<ContextMenu.Row
|
||||
icon={<PlayIcon />}
|
||||
text="Launch"
|
||||
onClick={() => launch()}
|
||||
/>
|
||||
</ContextMenu>
|
||||
);
|
||||
}
|
||||
|
||||
declare module 'solid-js' {
|
||||
// eslint-disable-next-line ts/no-namespace
|
||||
namespace JSX {
|
||||
interface Directives {
|
||||
sortable: any;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function ClusterCard(props: Core.ClusterWithManifest) {
|
||||
const navigate = useNavigate();
|
||||
const [pos, setPos] = createSignal({ x: 0, y: 0 });
|
||||
|
|
@ -160,6 +158,7 @@ function ClusterCard(props: Core.ClusterWithManifest) {
|
|||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ClusterCardContextMenu
|
||||
contextMenuVisible={contextMenuVisible}
|
||||
setContextMenuVisible={setContextMenuVisible}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue