1
0
Fork 0

aah too lazy to create proper git commit

This commit is contained in:
Kim, Jimin 2022-01-30 22:05:58 +09:00
parent f5e06d3eb6
commit 1a3979a0da
29 changed files with 334 additions and 689 deletions

View file

@ -0,0 +1,14 @@
use super::util;
use super::InstallErr;
pub async fn clean(game_path: &str) -> Result<(), InstallErr> {
match util::uninstall(game_path) {
Ok(_) => {}
Err(_) => {
return Err(InstallErr::RemoveOldFilesFailed);
}
}
return Ok(());
}

View file

@ -1,12 +1,9 @@
use std::env;
use tauri::Window;
use super::InstallErr;
use crate::commands::install::{emit, InstallSteps};
use crate::util;
pub async fn install_bepinex(window: &Window, game_path: &str) -> Result<(), InstallErr> {
pub async fn install_bepinex(game_path: &str) -> Result<(), InstallErr> {
println!();
println!("Installing BepInEx");
@ -47,7 +44,6 @@ pub async fn install_bepinex(window: &Window, game_path: &str) -> Result<(), Ins
match util::unzip(bepinex_path.as_str(), &game_path) {
Ok(_) => {
println!("Successfully unzipped BepInEx.zip to {}", game_path);
emit(&window, InstallSteps::InstallBepInEx);
}
Err(err) => {
@ -65,6 +61,5 @@ pub async fn install_bepinex(window: &Window, game_path: &str) -> Result<(), Ins
// done
emit(&window, InstallSteps::DownloadBepInEx);
return Ok(());
}

View file

@ -1,9 +1,7 @@
use tauri::Window;
use super::InstallErr;
use crate::commands::install::{emit, util, InstallSteps};
use crate::commands::install::util;
pub async fn install_wbm_mod(window: &Window, game_path: &str) -> Result<(), InstallErr> {
pub async fn install_wbm_mod(game_path: &str) -> Result<(), InstallErr> {
println!();
println!("Installing WBM mod");
@ -50,9 +48,7 @@ pub async fn install_wbm_mod(window: &Window, game_path: &str) -> Result<(), Ins
// unzip file
match util::unzip(zip_path.as_str(), wbm_path.to_str().unwrap()) {
Ok(()) => {
emit(&window, InstallSteps::InstallWbm);
}
Ok(()) => {}
Err(err) => {
println!("Failed to unzip WBM.zip: ({:#?})", err);
@ -69,6 +65,5 @@ pub async fn install_wbm_mod(window: &Window, game_path: &str) -> Result<(), Ins
// done
emit(&window, InstallSteps::DownloadWbmZip);
return Ok(());
}

View file

@ -1,19 +0,0 @@
use tauri::Window;
use super::{InstallErr, InstallResult};
use crate::commands::install::{emit, InstallSteps};
pub async fn launch_game_once(
window: &Window,
was_game_launched: bool,
) -> Result<InstallResult, InstallErr> {
println!();
println!("Launch Game once");
if was_game_launched {
return Ok(InstallResult::Skip);
}
emit(&window, InstallSteps::LaunchGame);
return Ok(InstallResult::LaunchGame); // stop install
}

View file

@ -1,12 +1,9 @@
use tauri::Window;
use super::{InstallErr, InstallResult};
use crate::commands::install::{emit, InstallSteps};
use super::InstallErr;
use crate::util;
use std::fs;
pub async fn unix_launch_option_setup(window: &Window) -> Result<InstallResult, InstallErr> {
pub async fn unix_launch_option_setup() -> Result<(), InstallErr> {
// skip if the OS is not linux or macOS
match std::env::consts::OS {
"linux" | "macos" => {
@ -18,18 +15,18 @@ pub async fn unix_launch_option_setup(window: &Window) -> Result<InstallResult,
println!();
println!("Skipping unix launch option setup");
return Ok(InstallResult::Skip);
return Ok(());
}
};
if is_already_set() {
println!("Steam launch option is already set. Skipping.");
return Ok(InstallResult::Skip);
return Ok(());
}
println!("Prompt user to set launch option");
emit(&window, InstallSteps::LaunchOption);
return Ok(InstallResult::SetLaunchOption);
println!("Steam launch option is either not set or invalid.");
println!("Prompting to set launch option.");
return Err(InstallErr::LaunchOptionNotSet);
}
fn is_already_set() -> bool {
@ -51,13 +48,17 @@ fn is_already_set() -> bool {
//
match fs::read_to_string(localconfig_path) {
Ok(content) => {
Ok(_content) => {
// todo: improve logic
// 1. find line only containing "750470"
// 2. find next closest line only containing "}"
// 3. check if section contains "./run_bepinex.sh %command%"
return content.contains("./run_bepinex.sh %command%") && content.contains("750470");
// run_bepinex.sh
// %command%
// 750470
return true;
}
Err(err) => {

View file

@ -1,48 +1,28 @@
use tauri::Window;
use crate::constants;
use crate::util;
mod types;
mod clean;
mod install_bepinex;
mod install_mod;
mod launch_game;
mod launch_options;
use types::{InstallErr, InstallResult, InstallSteps};
use types::InstallErr;
// todo: show current step in the frontend
/// automated version of the [manual installation](https://github.com/War-Brokers-Mods/WBM#installation).
///
/// This function exits if it requires a user input and is called again with the user feedback as its arguments.
/// This function exits if it requires a user input and is called again with the user input as its arguments.
///
/// ## Installation procedure
///
/// This function exits at the end of each step.
///
/// 1. BepInEx installation
/// 2. Steam launch option setup (only on Linux and MacOS)
/// 3. Launch game for plugins folder generation
/// 4. Mod installation
///
/// Some part of the function are unnecessary executed each time the function is called,
/// Some parts of the function are unnecessary executed each time the function is called,
/// but the time loss is negligible and it's a sacrifice worth for code readability.
///
/// ## Arguments
///
/// All arguments except `windows` are empty by default.
///
/// * `window` - standard tauri argument. See [docs](https://tauri.studio/docs/guides/command#accessing-the-window-in-commands) for more info.
/// * `game_path` - absolute path to the game folder/directory.
/// * `was_game_launched` - whether if the game was launched once after installing BepInEx to generate the plugins folder.
#[tauri::command]
pub async fn install(
window: Window,
game_path: String,
was_game_launched: bool,
) -> Result<InstallResult, InstallErr> {
pub async fn install(game_path: String) -> Result<(), InstallErr> {
println!("install command called");
//
@ -73,39 +53,29 @@ pub async fn install(
default_game_path
} else {
// todo: check if game path is valid and tell the user
if !util::is_game_path_valid(&game_path) {
return Err(InstallErr::GamePathNotValid);
}
game_path
};
let game_path = game_path.as_str();
//
// Install BepInEx
//
//
match install_bepinex::install_bepinex(&window, game_path).await {
Ok(()) => {}
Err(err) => return Err(err),
}
//
// Setup steam launch option if OS is linux or macOS
//
match launch_options::unix_launch_option_setup(&window).await {
match clean::clean(game_path).await {
Ok(_) => {}
Err(err) => return Err(err),
}
//
// Run the game once to generate the plugins directory
// Install BepInEx
//
match launch_game::launch_game_once(&window, was_game_launched).await {
Ok(res) => {
if res != InstallResult::Skip {
return Ok(res);
}
}
match install_bepinex::install_bepinex(game_path).await {
Ok(()) => {}
Err(err) => return Err(err),
}
@ -113,21 +83,25 @@ pub async fn install(
// Install the mod
//
match install_mod::install_wbm_mod(&window, game_path).await {
match install_mod::install_wbm_mod(game_path).await {
Ok(()) => {}
Err(err) => return Err(err),
}
//
// Set steam launch option if OS is linux or macOS
//
match launch_options::unix_launch_option_setup().await {
Ok(_) => {}
Err(err) => return Err(err),
}
//
// Tell the frontend that the installation was successful
//
emit(&window, InstallSteps::Done);
println!("Install complete!");
return Ok(InstallResult::NoErr);
}
pub fn emit(window: &Window, payload: InstallSteps) {
util::emit(&window, constants::EVENT_INSTALL, payload);
return Ok(());
}

View file

@ -1,64 +1,18 @@
/// must be synced with `src/pages/Install/types.ts`
//
//
//
#[derive(Clone, Copy)]
pub enum InstallSteps {
DownloadBepInEx,
InstallBepInEx,
LaunchOption,
LaunchGame,
DownloadWbmZip,
InstallWbm,
Done,
}
impl serde::Serialize for InstallSteps {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_i64(*self as i64)
}
}
//
//
//
#[derive(Clone, Copy, PartialEq)]
pub enum InstallResult {
NoErr,
SetLaunchOption,
LaunchGame,
Skip, // only used for subcommands
}
impl serde::Serialize for InstallResult {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_i64(*self as i64)
}
}
//
//
//
#[derive(Clone, Copy)]
pub enum InstallErr {
FailedToGetGamePath,
UnsupportedOS,
FailedToGetGamePath,
GamePathNotValid,
RemoveOldFilesFailed,
BepInExDownloadFailed,
BepInExUnzipFailed,
WBMDownloadFailed,
WBMRemoveFailed,
WBMDirectoryCreationFailed,
WBMUnzipFailed,
LaunchOptionNotSet,
}
impl serde::Serialize for InstallErr {
@ -69,6 +23,3 @@ impl serde::Serialize for InstallErr {
serializer.serialize_i64(*self as i64)
}
}
// #[derive(Clone, serde::Serialize)]
// struct InstallPayload(i64);

View file

@ -1,3 +1,2 @@
pub mod install;
pub mod status;
pub mod update;
pub mod remove;

View file

@ -0,0 +1,8 @@
mod types;
use types::RemoveErr;
#[tauri::command]
pub async fn remove(_game_path: String) -> Result<(), RemoveErr> {
return Ok(());
}

View file

@ -0,0 +1,13 @@
/// must be synced with `src/pages/Remove/types.ts`
#[derive(Clone, Copy)]
pub enum RemoveErr {}
impl serde::Serialize for RemoveErr {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_i64(*self as i64)
}
}

View file

@ -1,32 +0,0 @@
// [Sync]: must be synced with `src/pages/Status/index.svelte`
use crate::util;
/// [Sync]
static LATEST_VERSION: &'static str = "LATEST_VERSION";
static GAME_PATH: &'static str = "GAME_PATH";
/// [Sync]
#[derive(serde::Serialize, Default, Clone)]
pub struct StatusData {
latest_release_version: String,
game_path: String,
}
#[tauri::command]
pub async fn status(req_type: String) -> StatusData {
let mut status_data = StatusData::default();
if req_type == LATEST_VERSION {
status_data.latest_release_version = util::get_wbm_release_data().await;
} else if req_type == GAME_PATH {
// returns an empty string if the game doesn't exist in the default path
match util::get_default_game_path() {
Some(path) => status_data.game_path = path,
// todo: send feedback to frontend
None => {}
}
}
return status_data;
}

View file

@ -1,6 +0,0 @@
/// for updating WBM
#[tauri::command]
pub fn update() {
println!("Updating WBM!")
}

View file

@ -1,3 +0,0 @@
pub const EVENT_INSTALL: &str = "install";
// pub const EVENT_STATUS: &str = "install";
// pub const EVENT_UPDATE: &str = "install";

View file

@ -4,15 +4,13 @@
)]
mod commands;
mod constants;
mod util;
fn main() {
tauri::Builder::default()
.invoke_handler(tauri::generate_handler![
commands::install::install,
commands::status::status,
commands::update::update
commands::remove::remove
])
// you might see a unresolved macro error in the IDE but it's nothing to worry about.
// rust-analyzer doesn't do well with nested macros.

View file

@ -1,5 +1,4 @@
use tauri::api::path::{cache_dir, home_dir};
use tauri::Window;
use futures_util::StreamExt;
use std::cmp::min;
@ -61,6 +60,10 @@ pub fn get_default_game_path() -> Option<String> {
return None;
}
if !is_game_path_valid(&game_path) {
return None;
}
return Some(String::from(game_path));
}
@ -68,6 +71,13 @@ pub fn get_default_game_path() -> Option<String> {
}
}
/// Checks if the path is a valid War Brokers game path
pub fn is_game_path_valid(_game_path: &str) -> bool {
// todo: implement logic
return true;
}
/// convert `Option<PathBuf>` to `Option<String>`
pub fn buf2str(input: Option<PathBuf>) -> Option<String> {
if input.is_none() {
@ -144,11 +154,6 @@ pub async fn download_zip_to_cache_dir(url: &str, file_name: &str) -> Result<Str
return Ok(path);
}
/// https://tauri.studio/docs/guides/events
pub fn emit<S: serde::Serialize>(window: &Window, event: &str, payload: S) {
window.emit(event, payload).unwrap();
}
pub fn unzip(path: &str, destination: &str) -> Result<(), zip::result::ZipError> {
let fname = std::path::Path::new(path);
let zipfile = std::fs::File::open(&fname).unwrap();
@ -156,3 +161,10 @@ pub fn unzip(path: &str, destination: &str) -> Result<(), zip::result::ZipError>
return archive.extract(destination);
}
/// Uninstall WBM and related files
pub fn uninstall(_game_path: &str) -> Result<(), ()> {
// todo: implement
return Ok(());
}

View file

@ -1,20 +1,11 @@
export enum ROUTES {
HOME = "#/",
STATUS = "#/status",
INSTALL = "#/install",
UPDATE = "#/update",
INSTALL = "#/operation?action=install",
REMOVE = "#/operation?action=remove",
}
// https://tauri.studio/docs/guides/command
export enum COMMANDS {
STATUS = "status",
INSTALL = "install",
UPDATE = "update",
}
// https://tauri.studio/docs/guides/events
export enum EVENTS {
STATUS = "status",
INSTALL = "install",
UPDATE = "update",
REMOVE = "remove",
}

View file

@ -1,30 +1,29 @@
<script lang="ts">
import FaTasks from "svelte-icons/fa/FaTasks.svelte"
import FaArrowAltCircleRight from "svelte-icons/fa/FaArrowAltCircleRight.svelte"
import Update from "svelte-icons/fa/FaRedoAlt.svelte"
import FaDownload from "svelte-icons/fa/FaDownload.svelte"
import MdDeleteSweep from "svelte-icons/md/MdDeleteSweep.svelte"
import { ROUTES } from "../../constants"
</script>
<div class="main-buttons-container">
<a href={ROUTES.STATUS}>
<div class="icon"><FaTasks /></div>
<div>Status</div>
</a>
<a href={ROUTES.INSTALL}>
<div class="icon"><FaArrowAltCircleRight /></div>
<div class="icon">
<FaDownload />
</div>
<div>Install</div>
</a>
<a href={ROUTES.UPDATE}>
<div class="icon"><Update /></div>
<div>Update</div>
<a href={ROUTES.REMOVE}>
<div class="icon">
<MdDeleteSweep />
</div>
<div>Remove</div>
</a>
</div>
<style lang="scss">
.main-buttons-container {
@apply grid gap-6 grid-cols-3;
@apply w-full flex justify-evenly;
a {
@apply p-4 w-24 h-24 text-center rounded-xl bg-red-500 text-white font-normal;

View file

@ -1,15 +1,21 @@
<script lang="ts">
import FaBook from "svelte-icons/fa/FaBook.svelte"
import FaGithub from "svelte-icons/fa/FaGithub.svelte"
import FaDiscord from "svelte-icons/fa/FaDiscord.svelte"
import FaBook from "svelte-icons/fa/FaBook.svelte"
</script>
<div class="small-button-container">
<a href="https://github.com/War-Brokers-Mods/WBM#usage" target="_blank"
><FaBook /></a
>
<a href="https://github.com/War-Brokers-Mods" target="_blank"><FaGithub /></a>
<a href="https://discord.gg/aQqamSCUcS" target="_blank"><FaDiscord /></a>
<a href="https://github.com/War-Brokers-Mods/WBM#usage" target="_blank">
<FaBook />
</a>
<a href="https://github.com/War-Brokers-Mods" target="_blank">
<FaGithub />
</a>
<a href="https://discord.gg/aQqamSCUcS" target="_blank">
<FaDiscord />
</a>
</div>
<style lang="scss">

View file

@ -1,47 +0,0 @@
<script lang="ts">
import type { InstallStatus } from "./types"
export let installStatus: InstallStatus
</script>
<div class="steps">
<div class="step {installStatus.DownloadBepInEx && 'done'}">
<div class="number">1</div>
<div class="label">Install BepInEx</div>
</div>
<div class="step {installStatus.LaunchGame && 'done'}">
<div class="number">2</div>
<div class="label">Launch game</div>
</div>
<div class="step {installStatus.InstallWbm && 'done'}">
<div class="number">3</div>
<div class="label">Install Mod</div>
</div>
</div>
<style lang="scss">
.steps {
@apply grid grid-cols-3 w-full gap-2 p-2;
.step {
/* layout */
@apply flex flex-col text-center gap-2 p-2;
/* style */
@apply rounded-xl bg-neutral-600;
.number {
@apply text-4xl font-bold;
}
.label {
@apply text-sm;
}
}
.done {
@apply bg-red-600;
}
}
</style>

View file

@ -1,212 +0,0 @@
<script lang="ts">
// components also used outside
import HomeButton from "../../components/HomeButton.svelte"
import Spinner from "../../components/Spinner.svelte"
// components only used here
import Steps from "./Steps.svelte"
import Interrupts from "./Interrupts.svelte"
import Complete from "./Complete.svelte"
// tauri stuff
import { invoke } from "@tauri-apps/api/tauri"
import { listen } from "@tauri-apps/api/event"
import { open as dialogOpen } from "@tauri-apps/api/dialog"
// types
import { COMMANDS, EVENTS } from "../../constants"
import { InstallErr, InstallResult, InstallSteps } from "./types"
import type { InstallStatus } from "./types"
interface Args {
gamePath: string
wasGameLaunched: boolean
}
//
// variables
//
let _gamePath = ""
let lastReturnStatus: InstallResult = undefined
let lastErrStaus: InstallErr = undefined
let didLastRunFail = false
let wasInstallButtonClicked = false
let spinCog = false
let installStatus: InstallStatus = {
DownloadBepInEx: false,
InstallBepInEx: false,
LaunchOption: false,
LaunchGame: false,
DownloadWbmZip: false,
InstallWbm: false,
Done: false,
}
//
// functions
//
/**
* only used inside other install functions.
* Is never called directly.
*
* @param {Args} args
*/
function _install(args: Args) {
wasInstallButtonClicked = true
spinCog = true
invoke<InstallResult>(COMMANDS.INSTALL, args as any)
.then((res) => {
lastReturnStatus = res
switch (res) {
case InstallResult.NoErr: {
break
}
case InstallResult.SetLaunchOption: {
break
}
case InstallResult.LaunchGame: {
break
}
case InstallResult.Skip: {
break
}
}
})
.catch((err: InstallErr) => {
console.log(typeof err, err)
})
}
/**
* entry point
*/
function install() {
_install({
gamePath: _gamePath,
wasGameLaunched: false,
})
}
/**
* called when default game path was not found.
*/
function selectGamePathAndInstall() {
dialogOpen({ directory: true, multiple: false }).then((value) => {
_gamePath = value as string
_install({
gamePath: _gamePath,
wasGameLaunched: false,
})
})
}
/**
* called after setting the steam launch option.
*/
function setSteamLaunchOptionAndInstall() {
_install({
gamePath: _gamePath,
wasGameLaunched: false,
})
}
/**
* called after launching the game once.
*/
function launchGameAndInstall() {
_install({
gamePath: _gamePath,
wasGameLaunched: true,
})
}
//
// Event listener
//
listen<InstallSteps>(EVENTS.INSTALL, (event) => {
switch (event.payload) {
case InstallSteps.DownloadBepInEx: {
installStatus.DownloadBepInEx = true
break
}
case InstallSteps.InstallBepInEx: {
installStatus.InstallBepInEx = true
break
}
case InstallSteps.LaunchOption: {
installStatus.LaunchOption = true
break
}
case InstallSteps.LaunchGame: {
installStatus.LaunchGame = true
break
}
case InstallSteps.DownloadWbmZip: {
installStatus.DownloadWbmZip = true
break
}
case InstallSteps.InstallWbm: {
installStatus.InstallWbm = true
break
}
case InstallSteps.Done: {
spinCog = false
installStatus.Done = true
break
}
}
})
</script>
<!-- Allow user to go back to home until they click the install button -->
{#if !wasInstallButtonClicked}
<HomeButton />
{/if}
<div class="install-page">
<Spinner activated={spinCog} />
{#if !wasInstallButtonClicked}
<button on:click={install}>Install!</button>
{/if}
<!-- show only when the install button is clicked -->
{#if wasInstallButtonClicked}
<Steps {installStatus} />
<Interrupts
{installStatus}
{lastReturnStatus}
{lastErrStaus}
{selectGamePathAndInstall}
{setSteamLaunchOptionAndInstall}
{launchGameAndInstall}
/>
{#if installStatus.Done}
<Complete />
{/if}
{/if}
</div>
<style lang="scss">
@import "./button.scss";
.install-page {
@apply flex flex-col items-center;
}
</style>

View file

@ -1,39 +0,0 @@
// types of event
export enum InstallSteps {
DownloadBepInEx,
InstallBepInEx,
LaunchOption,
LaunchGame,
DownloadWbmZip,
InstallWbm,
Done,
}
// types of install command return codes
export enum InstallResult {
NoErr,
SetLaunchOption,
LaunchGame,
Skip, // only used for subcommands
}
export enum InstallErr {
FailedToGetGamePath,
UnsupportedOS,
BepInExDownloadFailed,
BepInExUnzipFailed,
WBMDownloadFailed,
WBMRemoveFailed,
WBMDirectoryCreationFailed,
WBMUnzipFailed,
}
export interface InstallStatus {
DownloadBepInEx: boolean
InstallBepInEx: boolean
LaunchOption: boolean
LaunchGame: boolean
DownloadWbmZip: boolean
InstallWbm: boolean
Done: boolean
}

View file

@ -1,22 +1,25 @@
<script lang="ts">
/**
* Show what the user has to do during instalation
*/
import { copy } from "svelte-copy"
import { open as shellOpen } from "@tauri-apps/api/shell"
import type { InstallStatus } from "./types"
import { InstallErr } from "./types"
import { InstallResult } from "./types"
export let lastReturnStatus: InstallResult
export let lastErrStaus: InstallErr
export let installStatus: InstallStatus
export let selectGamePathAndInstall: () => void
export let setSteamLaunchOptionAndInstall: () => void
export let launchGameAndInstall: () => void
export let selectGamePathAndInstall: () => void
</script>
<div class="interrupts">
<!-- set game launch option -->
{#if installStatus.LaunchOption}
<!--
set game launch option
-->
{#if lastErrStaus == InstallErr.LaunchOptionNotSet}
<span
use:copy={"./run_bepinex.sh %command%"}
on:svelte-copy={(event) => alert(event.detail)}
@ -27,7 +30,10 @@
<button on:click={setSteamLaunchOptionAndInstall}>Resume</button>
{/if}
<!-- if the game was not found in the default install location -->
<!--
if the game was not found in the default install location
-->
{#if lastErrStaus == InstallErr.FailedToGetGamePath}
<p>
Default game install location was not found :(
@ -52,19 +58,14 @@
Select folder and Install
</button>
{/if}
{#if lastReturnStatus == InstallResult.LaunchGame}
Launch game
<button on:click={launchGameAndInstall}>Resume</button>
{/if}
</div>
<style lang="scss">
@import "./styles/button.scss";
.interrupts {
@apply mt-2;
@import "./button.scss";
p {
@apply text-center;
}

View file

@ -0,0 +1,165 @@
<script lang="ts">
// components also used outside
import HomeButton from "../../components/HomeButton.svelte"
import Spinner from "../../components/Spinner.svelte"
// components only used here
import Interrupts from "./Interrupts.svelte"
import Complete from "./Complete.svelte"
// tauri stuff
import { invoke } from "@tauri-apps/api/tauri"
import { open as dialogOpen } from "@tauri-apps/api/dialog"
// svelte stuff
import { querystring } from "svelte-spa-router"
// types
import { COMMANDS } from "../../constants"
import { InstallErr, RemoveErr } from "./types"
interface InstallArgs {
gamePath: string
}
enum OperationType {
Install,
Remove,
}
//
// variables
//
const operationType: OperationType = $querystring.includes("install")
? OperationType.Install
: OperationType.Remove
let _gamePath = "" // not used directly
let lastInstallErrStaus: InstallErr = undefined
let lastRemoveErrStatus: RemoveErr = undefined
let wasButtonClicked = false // if the install/remove button was clicked or not
let spinCog = false
let installSuccess = false
let removeSuccess = false
//
// functions
//
/**
* only used inside other install functions.
* Is never called directly.
*
* @param {InstallArgs} args
*/
function _install(args: InstallArgs) {
wasButtonClicked = true
spinCog = true
invoke<InstallErr>(COMMANDS.INSTALL, args as any)
.then((res) => {
switch (res) {
case InstallErr.FailedToGetGamePath: {
break
}
}
})
.catch((err: InstallErr) => {
console.log(typeof err, err)
})
}
/**
* entry point
*/
function install() {
_install({
gamePath: _gamePath,
})
}
/**
* called when default game path was not found.
*/
function selectGamePathAndInstall() {
dialogOpen({ directory: true, multiple: false }).then((value) => {
_gamePath = value as string
_install({
gamePath: _gamePath,
})
})
}
/**
* called after setting the steam launch option.
*/
function setSteamLaunchOptionAndInstall() {
_install({
gamePath: _gamePath,
})
}
</script>
<!-- Allow user to go back to home until they click the install button -->
{#if !wasButtonClicked}
<HomeButton />
{/if}
{#if operationType == OperationType.Install}
<div class="install-page">
<Spinner activated={spinCog} />
{#if !wasButtonClicked}
<button on:click={install}>Install!</button>
{/if}
<!-- show only when the install button is clicked -->
{#if wasButtonClicked}
<Interrupts
lastErrStaus={lastInstallErrStaus}
{selectGamePathAndInstall}
{setSteamLaunchOptionAndInstall}
/>
{#if installSuccess}
<Complete />
{/if}
{/if}
</div>
{:else}
<div class="remove-page">
<Spinner activated={spinCog} />
{#if !wasButtonClicked}
<button on:click={install}>Remove!</button>
{/if}
<!-- show only when the install button is clicked -->
{#if wasButtonClicked}
<Interrupts
lastErrStaus={lastInstallErrStaus}
{selectGamePathAndInstall}
{setSteamLaunchOptionAndInstall}
/>
{#if installSuccess}
<Complete />
{/if}
{/if}
</div>
{/if}
<style lang="scss">
@import "./styles/button.scss";
.install-page {
@apply flex flex-col items-center;
}
.remove-page {
@apply flex flex-col items-center;
}
</style>

View file

@ -0,0 +1,22 @@
/**
* Must be synced with `src-tauri/src/commands/install/types.rs`
*/
export enum InstallErr {
UnsupportedOS,
FailedToGetGamePath,
GamePathNotValid,
RemoveOldFilesFailed,
BepInExDownloadFailed,
BepInExUnzipFailed,
WBMDownloadFailed,
WBMRemoveFailed,
WBMDirectoryCreationFailed,
WBMUnzipFailed,
LaunchOptionNotSet,
}
export enum RemoveErr {
FailedToGetGamePath,
GamePathNotValid,
}

View file

@ -1,108 +0,0 @@
<script lang="ts">
// [Sync]: must be synced with `src-tauri/src/commands/status.rs`
import HomeButton from "../../components/HomeButton.svelte"
import Spinner from "../../components/Spinner.svelte"
import { invoke } from "@tauri-apps/api/tauri"
import { open as openShell } from "@tauri-apps/api/shell"
import { COMMANDS } from "../../constants"
// [Sync]
enum STATUS_REQUEST {
LATEST_VERSION = "LATEST_VERSION",
GAME_PATH = "GAME_PATH",
}
// [Sync]
interface StatusDataRaw {
latest_release_version?: string
game_path?: string
}
// [Sync]
interface StatusData {
latestReleaseVersion?: LatestReleaseVersion
gamePath?: string
}
interface LatestReleaseVersion {
name: string
url: string
}
//
//
//
let statusData: StatusData = {}
let isRunning = false
function _requestStatus(
reqType: STATUS_REQUEST,
f: (res: StatusDataRaw) => void,
f2?: (res: any) => void
) {
// fallback to default error handling if f2 is not defined
f2 ||= (err) => {
console.error(err)
}
invoke(COMMANDS.STATUS, { reqType }).then(f).catch(f2)
}
async function status() {
isRunning = true
_requestStatus(STATUS_REQUEST.LATEST_VERSION, (res) => {
const data = (JSON.parse(res.latest_release_version) as any[])[0]
statusData.latestReleaseVersion = {
name: data.name,
url: data.html_url,
}
})
_requestStatus(STATUS_REQUEST.GAME_PATH, (res) => {
statusData.gamePath = res.game_path || "steam apps folder not found"
})
}
</script>
{#if !isRunning}
<HomeButton />
{/if}
<div class="status-page">
<Spinner activated={isRunning} />
<div>
<button on:click={status}>Check status!</button>
<br />
<br />
Latest version:
{#if statusData.latestReleaseVersion}
<div
on:click={() => {
console.log(statusData.latestReleaseVersion.url)
openShell(statusData.latestReleaseVersion.url)
}}
style="display: inline-block; cursor: pointer"
>
{statusData.latestReleaseVersion.name}
</div>
{/if}
<br />
game Path:
{#if statusData.gamePath}
{statusData.gamePath}
{/if}
</div>
</div>
<style lang="scss">
.status-page {
@apply flex flex-col items-center;
}
</style>

View file

@ -1,29 +0,0 @@
<script lang="ts">
import HomeButton from "../../components/HomeButton.svelte"
import Spinner from "../../components/Spinner.svelte"
import { invoke } from "@tauri-apps/api/tauri"
import { COMMANDS } from "../../constants"
let isRunning = false
function update() {
isRunning = true
invoke(COMMANDS.UPDATE)
}
</script>
{#if !isRunning}
<HomeButton />
{/if}
<div class="update-page">
<Spinner activated={isRunning} />
<button on:click={update}>Update!</button>
</div>
<style lang="scss">
.update-page {
@apply flex flex-col items-center;
}
</style>

View file

@ -1,11 +1,7 @@
import Home from "./pages/Home/index.svelte"
import Status from "./pages/Status/index.svelte"
import Install from "./pages/Install/index.svelte"
import Update from "./pages/Update/index.svelte"
import Operation from "./pages/Operation/index.svelte"
export default {
"/": Home,
"/status": Status,
"/install": Install,
"/update": Update,
"/operation": Operation,
}