Skip to content

Commit

Permalink
API docs
Browse files Browse the repository at this point in the history
  • Loading branch information
RedstoneWizard08 committed May 31, 2024
1 parent d2fff54 commit c5f92c5
Show file tree
Hide file tree
Showing 17 changed files with 171 additions and 101 deletions.
3 changes: 3 additions & 0 deletions crates/api/src/install/install.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//! The module responsible for actually installing mods.
use anyhow::Result;
use data::{conv::DbIntoArg, instance::Instance, Conn};
use install::{extract::extract_file, magic::detect_file_type};
Expand All @@ -7,6 +9,7 @@ use whcore::progress::ProgressCallback;

use crate::plugin::Plugin;

/// Install a mod.
pub async fn install_mod(
db: &mut Conn,
item: Mod,
Expand Down
2 changes: 2 additions & 0 deletions crates/api/src/install/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//! The mod installer API.
pub mod install;
pub mod progress;
pub mod uninstall;
4 changes: 4 additions & 0 deletions crates/api/src/install/progress.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
//! The progress API.
use std::{future::Future, pin::Pin};

use tauri::Manager;
use tauri_specta::Event as TEvent;

use crate::{EVENT_BUS, TAURI_HANDLE};

/// A progress payload.
#[derive(
Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Serialize, Deserialize, Type,
)]
Expand All @@ -27,6 +30,7 @@ impl TEvent for ProgressPayload {
const NAME: &'static str = "progress_callback";
}

/// A progress callback for Tauri.
pub fn tauri_progress(
total: u64,
current: u64,
Expand Down
4 changes: 4 additions & 0 deletions crates/api/src/install/uninstall.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
//! The uninstall API.
//! This module really hates the installer API, on an emotional level.
use std::{fs, path::PathBuf};

use anyhow::Result;
Expand All @@ -9,6 +12,7 @@ use data::{
Conn,
};

/// Uninstall a mod.
pub async fn uninstall_mod(db: &mut Conn, item: DbMod, instance: Instance) -> Result<()> {
let paths = serde_json::from_str::<Vec<PathBuf>>(&item.path)?;

Expand Down
21 changes: 18 additions & 3 deletions crates/api/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
#![warn(missing_docs, rustdoc::broken_intra_doc_links)]
//! # Wormhole's API.
//!
//! This implements all of the plugins, mod installers,
//! game supports, assets, and a lot more.
//!
//! In the future, this will hopefully be able to support
//! WASI-based plugins for extensibility.
use std::sync::Arc;

use ::tauri::AppHandle;
Expand Down Expand Up @@ -33,21 +42,27 @@ pub mod register;
pub mod res;
pub mod tauri;

#[cfg(test)]
pub mod test_util;

// This isn't just lazily initialized, it's also "lazy" because
// I'm too lazy to actually pass around a struct.
lazy_static! {
/// A handle to the Tauri app.
/// This will be [`None`] if the app hasn't been started yet, or
/// this is running in the web UI.
pub static ref TAURI_HANDLE: Arc<Mutex<Option<AppHandle>>> = Arc::new(Mutex::new(None));

/// The "event bus". This is really just an unbounded channel
/// of [`ProgressPayload`]s. This will eventually be expanded
/// to support more event types.
pub static ref EVENT_BUS: Arc<(Sender<ProgressPayload>, Receiver<ProgressPayload>)> =
Arc::new(unbounded());
}

/// Initializes the API, registering all of the default plugins.
pub async fn init() {
register_defaults().await;
}

/// Gets the [`TypeMap`] for the API.
pub fn type_map() -> TypeMap {
let mut map = TypeMap::default();

Expand Down
7 changes: 7 additions & 0 deletions crates/api/src/macros.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
//! Macros for the API.
/// Create a function alias to a member function
/// in a local variable.
#[macro_export]
macro_rules! fn_alias {
($var: ident::$real: ident => $name: ident: ($($arg: ident: $ty: ident),*)$( -> $ret: ident)?) => {
#[allow(missing_docs)]
#[tauri::command]
#[specta::specta]
fn $name(me: tauri::State<'_, $var>, $($arg: $ty),*) $(-> $ret)? {
Expand All @@ -9,6 +14,7 @@ macro_rules! fn_alias {
};

(dyn $var: ident::$real: ident => $name: ident: ($($arg: ident: $ty: ident),*)$( -> $ret: ident)?) => {
#[allow(missing_docs)]
#[tauri::command]
#[specta::specta]
fn $name(me: tauri::State<'_, std::sync::Arc<Box<dyn $var + Send + Sync>>>, $($arg: $ty),*) $(-> $ret)? {
Expand All @@ -17,6 +23,7 @@ macro_rules! fn_alias {
};

(dyn $var: ident::$real: ident => $name: ident: async ($($arg: ident: $ty: ident),*)$( -> $ret: ident)?) => {
#[allow(missing_docs)]
#[tauri::command]
#[specta::specta]
async fn $name<'a>(me: tauri::State<'a, std::sync::Arc<Box<dyn $var + Send + Sync + 'static>>>, $($arg: $ty),*) $(-> $ret)? {
Expand Down
33 changes: 32 additions & 1 deletion crates/api/src/plugin.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//! The plugin API.
use std::{collections::HashMap, path::PathBuf, sync::Arc};

use anyhow::Result;
Expand All @@ -18,25 +20,49 @@ use whcore::{dirs::Dirs, manager::CoreManager};
use crate::install::{install::install_mod, progress::tauri_progress, uninstall::uninstall_mod};

lazy_static! {
/// A map of plugin identifiers to their resolvers.
/// This is a cache, as some resolvers are expensive to create
/// (e.g. CKAN, which refreshes two git repos every time it's created).
pub static ref RESOLVERS: Arc<Mutex<HashMap<&'static str, Vec<Arc<Box<dyn Resolver + Send + Sync>>>>>> =
Arc::new(Mutex::new(HashMap::new()));
}

/// A plugin's metadata. This is useful for getting information
/// about the plugin on the frontend.
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, Type)]
pub struct PluginInfo {
/// The plugin's identifier.
pub id: &'static str,

/// The plugin's game ID.
pub game: i32,

/// The plugin's display name.
pub display_name: String,

/// The plugin's icon URL.
pub icon_url: String,

/// The plugin's banner URL.
pub banner_url: String,

/// The plugin's fallback mod install directory.
/// If the installer can't automatically determine
/// where to install a mod, this will be used.
pub fallback_dir: Option<&'static str>,

/// The plugin's query resolvers (IDs).
pub resolvers: Vec<SourceMapping>,
}

unsafe impl Send for PluginInfo {}
unsafe impl Sync for PluginInfo {}

// TODO: Install hook (for Minecraft loader installation)
/// A plugin.
///
/// This is the main interface for interacting with plugins.
/// This is essentially a support module for a game. Every operation
/// that Wormhole does goes through a plugin.
#[async_trait]
pub trait Plugin: Send + Sync {
/// Create a new instance.
Expand Down Expand Up @@ -131,6 +157,7 @@ pub trait Plugin: Send + Sync {
None
}

/// Get the plugin as a [`PluginInfo`].
async fn as_info(&self) -> Option<PluginInfo> {
Some(PluginInfo {
id: self.id(),
Expand All @@ -149,8 +176,10 @@ pub trait Plugin: Send + Sync {
})
}

/// Launch the game instance.
async fn launch(&self, instance: Instance) -> Result<Child>;

/// Install a mod to the provided instance.
async fn install_mod(
&self,
db: &mut Conn,
Expand All @@ -174,6 +203,7 @@ pub trait Plugin: Send + Sync {
Ok(())
}

/// Uninstall a mod from the provided instance.
async fn uninstall_mod(&self, db: &mut Conn, item: DbMod, instance: Instance) -> Result<()>
where
Self: Sized,
Expand All @@ -183,6 +213,7 @@ pub trait Plugin: Send + Sync {
Ok(())
}

/// Install an instance after creation.
async fn install_instance(&self, _inst: &Instance) -> Result<()> {
Ok(())
}
Expand Down
2 changes: 2 additions & 0 deletions crates/api/src/plugins/common/mod.rs
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
//! Common functions for plugins.
pub mod unity;
26 changes: 26 additions & 0 deletions crates/api/src/plugins/common/unity.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//! The Unity game support module.
use std::path::PathBuf;

use anyhow::Result;
Expand All @@ -8,21 +10,45 @@ use tokio::process::{Child, Command};

use crate::plugin::Plugin;

/// A plugin implementation for Unity games.
/// This will automatically implement the [`Plugin`] trait.
#[async_trait]
pub trait UnityPlugin: Send + Sync {
/// Create a new instance of the plugin.
fn new() -> Self
where
Self: Sized;

/// The id of the plugin.
fn id(&self) -> &'static str;

/// The game of the plugin.
fn game(&self) -> i32;

/// The icon of the plugin.
fn icon(&self) -> String;

/// The banner of the plugin.
fn banner(&self) -> String;

/// The display name of the plugin.
fn display(&self) -> String;

/// The fallback install dir of the plugin.
fn fallback(&self) -> Option<&'static str>;

/// The executable for the plugin to run when
/// launching the game.
fn executable(&self) -> &'static str;

/// The install dir of the plugin.
fn find(&self) -> Option<PathBuf>;

/// The name of the plugin.
fn name(&self) -> &'static str;

/// Create the resolvers for the plugin.
async fn resolvers(&self) -> Vec<Box<dyn Resolver + Send + Sync>>;
}

Expand Down
17 changes: 11 additions & 6 deletions crates/api/src/plugins/ksp1.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//! The KSP1 support module.
use std::path::PathBuf;

use base64::{engine::general_purpose::STANDARD, Engine};
Expand All @@ -6,15 +8,18 @@ use whcore::finder::{finder::InstallFinder, pdlauncher::PrivateDivision, steam::

use super::common::unity::UnityPlugin;

pub const ICON_BYTES: &[u8] = include_bytes!("../assets/ksp1/icon.png");
pub const BANNER_BYTES: &[u8] = include_bytes!("../assets/ksp1/banner.png");
const ICON_BYTES: &[u8] = include_bytes!("../assets/ksp1/icon.png");
const BANNER_BYTES: &[u8] = include_bytes!("../assets/ksp1/banner.png");

// The expected size of KSP1's `steam_api64.dll` in bytes.
// This helps to make sure that the game is not pirated.
// File path: `[KSP1_ROOT]/KSP_x64_Data/Plugins/x86_64/steam_api64.dll`
// Information from: SteamDB, DepotDownloader, KSP1 Installed Files
/// The expected size of KSP1's `steam_api64.dll` in bytes.
/// This helps to make sure that the game is not pirated.
/// File path: `[KSP1_ROOT]/KSP_x64_Data/Plugins/x86_64/steam_api64.dll`
/// Information from: SteamDB, DepotDownloader, KSP1 Installed Files
///
/// TODO: Actually use this information somewhere.
pub const KSP1_STEAM_API_SIZE: u64 = 249120;

/// The plugin for KSP1.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Kerbal1Plugin;

Expand Down
17 changes: 11 additions & 6 deletions crates/api/src/plugins/ksp2.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//! The KSP2 support module.
use std::path::PathBuf;

use base64::{engine::general_purpose::STANDARD, Engine};
Expand All @@ -6,15 +8,18 @@ use whcore::finder::{finder::InstallFinder, pdlauncher::PrivateDivision, steam::

use super::common::unity::UnityPlugin;

pub const ICON_BYTES: &[u8] = include_bytes!("../assets/ksp2/icon.png");
pub const BANNER_BYTES: &[u8] = include_bytes!("../assets/ksp2/banner.png");
const ICON_BYTES: &[u8] = include_bytes!("../assets/ksp2/icon.png");
const BANNER_BYTES: &[u8] = include_bytes!("../assets/ksp2/banner.png");

// The expected size of KSP2's `steam_api64.dll` in bytes.
// This helps to make sure that the game is not pirated.
// File path: `[KSP2_ROOT]/KSP2_x64_Data/Plugins/x86_64/steam_api64.dll`
// Information from: SteamDB, DepotDownloader, KSP2 Installed Files
/// The expected size of KSP2's `steam_api64.dll` in bytes.
/// This helps to make sure that the game is not pirated.
/// File path: `[KSP2_ROOT]/KSP2_x64_Data/Plugins/x86_64/steam_api64.dll`
/// Information from: SteamDB, DepotDownloader, KSP2 Installed Files
///
/// TODO: Actually use this information somewhere.
pub const KSP2_STEAM_API_SIZE: u64 = 295336;

/// The plugin for KSP2.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Kerbal2Plugin;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//! The Minecraft support module.
use std::path::PathBuf;

use anyhow::Result;
Expand All @@ -11,9 +13,10 @@ use whcore::manager::CoreManager;

use crate::plugin::Plugin;

pub const ICON_BYTES: &[u8] = include_bytes!("../../assets/minecraft/icon.svg");
pub const BANNER_BYTES: &[u8] = include_bytes!("../../assets/minecraft/banner.jpg");
const ICON_BYTES: &[u8] = include_bytes!("../assets/minecraft/icon.svg");
const BANNER_BYTES: &[u8] = include_bytes!("../assets/minecraft/banner.jpg");

/// The plugin for Minecraft.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct MinecraftPlugin;

Expand Down
4 changes: 4 additions & 0 deletions crates/api/src/plugins/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//! Default plugins.
pub mod common;
pub mod ksp1;
pub mod ksp2;
Expand All @@ -9,6 +11,7 @@ pub use minecraft::MinecraftPlugin;

use crate::{plugin::Plugin, register::register_plugin, tauri::TauriPluginTrait};

/// Default plugins.
pub fn default_plugins() -> Vec<Box<dyn TauriPluginTrait>> {
vec![
Box::new(Kerbal1Plugin::new()),
Expand All @@ -17,6 +20,7 @@ pub fn default_plugins() -> Vec<Box<dyn TauriPluginTrait>> {
]
}

/// Register default plugins.
pub async fn register_defaults() {
for plugin in default_plugins() {
register_plugin(plugin).await;
Expand Down
5 changes: 5 additions & 0 deletions crates/api/src/register.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
//! Happily registering plugins since 2024.
use std::{collections::HashMap, sync::Arc};

use tokio::sync::Mutex;

use crate::tauri::TauriPluginTrait;

lazy_static! {
/// A map of game IDs to their plugin.
/// This is where plugins are registered.
pub static ref PLUGINS: Arc<Mutex<HashMap<i32, Box<dyn TauriPluginTrait + Send + Sync>>>> =
Arc::new(Mutex::new(HashMap::new()));
}

/// Registers a plugin to the map.
pub async fn register_plugin(plugin: Box<dyn TauriPluginTrait + Send + Sync>) {
PLUGINS.lock().await.insert(plugin.game(), plugin);
}
Loading

0 comments on commit c5f92c5

Please sign in to comment.