Merge pull request #56 from modrinth/mod-management
Profile mod management
42
.github/workflows/cli-build.yml
vendored
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
name: CLI Build + Lint
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ master ]
|
||||||
|
pull_request:
|
||||||
|
env:
|
||||||
|
CARGO_TERM_COLOR: always
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
working-directory: ./theseus_cli
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: install dependencies (ubuntu only)
|
||||||
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.0-dev libappindicator3-dev librsvg2-dev patchelf
|
||||||
|
- name: Get build cache
|
||||||
|
id: cache-build
|
||||||
|
uses: actions/cache@v2
|
||||||
|
with:
|
||||||
|
path: ../target/**
|
||||||
|
key: ${{ runner.os }}-theseus
|
||||||
|
- name: Install toolchain
|
||||||
|
uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
toolchain: stable
|
||||||
|
override: true
|
||||||
|
components: rustfmt, clippy
|
||||||
|
- uses: actions-rs/cargo@v1
|
||||||
|
name: Build program
|
||||||
|
with:
|
||||||
|
command: build
|
||||||
|
args: --bin theseus_cli
|
||||||
|
- name: Run Lint
|
||||||
|
uses: actions-rs/clippy-check@v1
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
args: --bin theseus_cli
|
||||||
34
.github/workflows/gui-build.yml
vendored
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
name: GUI Build + Lint
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [master]
|
||||||
|
pull_request:
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
working-directory: ./theseus_gui
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
- name: Use Node.js
|
||||||
|
uses: actions/setup-node@v3
|
||||||
|
with:
|
||||||
|
node-version: 18.x
|
||||||
|
- name: Get yarn cache
|
||||||
|
id: yarn-cache
|
||||||
|
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||||
|
- uses: actions/cache@v3
|
||||||
|
with:
|
||||||
|
path: ${{ steps.yarn-cache.outputs.dir }}
|
||||||
|
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-yarn-
|
||||||
|
- name: Install dependencies
|
||||||
|
run: yarn install --immutable --immutable-cache --check-cache
|
||||||
|
- name: Run Lint
|
||||||
|
run: npm run lint
|
||||||
|
- name: Build
|
||||||
|
run: npm run build
|
||||||
59
.github/workflows/tauri-build.yml
vendored
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
name: 'Tauri GUI Build'
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ master ]
|
||||||
|
pull_request:
|
||||||
|
jobs:
|
||||||
|
test-tauri:
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
platform: [macos-latest, windows-latest]
|
||||||
|
|
||||||
|
runs-on: ${{ matrix.platform }}
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
working-directory: ./theseus_gui
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- name: setup node
|
||||||
|
uses: actions/setup-node@v3
|
||||||
|
with:
|
||||||
|
node-version: 16
|
||||||
|
- name: Install toolchain
|
||||||
|
uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
toolchain: stable
|
||||||
|
override: true
|
||||||
|
components: rustfmt, clippy
|
||||||
|
- name: install dependencies (ubuntu only)
|
||||||
|
if: matrix.platform == 'ubuntu-20.04'
|
||||||
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.0-dev libappindicator3-dev librsvg2-dev patchelf
|
||||||
|
- name: Get build cache
|
||||||
|
id: cache-build
|
||||||
|
uses: actions/cache@v2
|
||||||
|
with:
|
||||||
|
path: ../target/**
|
||||||
|
key: ${{ runner.os }}-theseus
|
||||||
|
- name: Get yarn cache
|
||||||
|
id: yarn-cache
|
||||||
|
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||||
|
- uses: actions/cache@v3
|
||||||
|
with:
|
||||||
|
path: ${{ steps.yarn-cache.outputs.dir }}
|
||||||
|
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-yarn-
|
||||||
|
- name: install frontend dependencies
|
||||||
|
run: yarn install --immutable --immutable-cache --check-cache
|
||||||
|
- uses: tauri-apps/tauri-action@v0
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
- name: Run Lint
|
||||||
|
if: matrix.platform == 'ubuntu-20.04'
|
||||||
|
uses: actions-rs/clippy-check@v1
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
args: --bin theseus_cli
|
||||||
3
.gitmodules
vendored
@@ -1,3 +0,0 @@
|
|||||||
[submodule "theseus_gui/locales"]
|
|
||||||
path = theseus_gui/locales
|
|
||||||
url = git@github.com:modrinth/translations.git
|
|
||||||
13
COPYING.md
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
# Copying
|
||||||
|
|
||||||
|
The source code of the knossos repository is licensed under the GNU Affero General Public License, Version 3 only, which is provided in the file [LICENSE](./LICENSE). However, some files listed below are licensed under a different license.
|
||||||
|
|
||||||
|
## Modrinth logo
|
||||||
|
|
||||||
|
Any files depicting the Modrinth branding, including the wrench-in-labyrinth logo, the landing image, and variations thereof, are licensed as follows:
|
||||||
|
|
||||||
|
> All rights reserved. © 2020-2023 Rinth, Inc.
|
||||||
|
|
||||||
|
This includes, but may not be limited to, the following files:
|
||||||
|
|
||||||
|
- theseus_gui/src-tauri/icons
|
||||||
1465
Cargo.lock
generated
103
flake.lock
generated
@@ -1,103 +0,0 @@
|
|||||||
{
|
|
||||||
"nodes": {
|
|
||||||
"fenix": {
|
|
||||||
"inputs": {
|
|
||||||
"nixpkgs": [
|
|
||||||
"nixpkgs"
|
|
||||||
],
|
|
||||||
"rust-analyzer-src": "rust-analyzer-src"
|
|
||||||
},
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1655706580,
|
|
||||||
"narHash": "sha256-7DshIT1Ya5W9NAW7UdnYCHsGmXfOXJZCEHbbB/cCX7g=",
|
|
||||||
"owner": "nix-community",
|
|
||||||
"repo": "fenix",
|
|
||||||
"rev": "d895003d8e03ac2fc8ffe2aa898299cbef1a7048",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "nix-community",
|
|
||||||
"repo": "fenix",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"naersk": {
|
|
||||||
"inputs": {
|
|
||||||
"nixpkgs": [
|
|
||||||
"nixpkgs"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1655042882,
|
|
||||||
"narHash": "sha256-9BX8Fuez5YJlN7cdPO63InoyBy7dm3VlJkkmTt6fS1A=",
|
|
||||||
"owner": "nix-community",
|
|
||||||
"repo": "naersk",
|
|
||||||
"rev": "cddffb5aa211f50c4b8750adbec0bbbdfb26bb9f",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "nix-community",
|
|
||||||
"repo": "naersk",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"nixpkgs": {
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1655624069,
|
|
||||||
"narHash": "sha256-7g1zwTdp35GMTERnSzZMWJ7PG3QdDE8VOX3WsnOkAtM=",
|
|
||||||
"owner": "nixos",
|
|
||||||
"repo": "nixpkgs",
|
|
||||||
"rev": "0d68d7c857fe301d49cdcd56130e0beea4ecd5aa",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "nixos",
|
|
||||||
"ref": "nixos-unstable",
|
|
||||||
"repo": "nixpkgs",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"root": {
|
|
||||||
"inputs": {
|
|
||||||
"fenix": "fenix",
|
|
||||||
"naersk": "naersk",
|
|
||||||
"nixpkgs": "nixpkgs",
|
|
||||||
"utils": "utils"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"rust-analyzer-src": {
|
|
||||||
"flake": false,
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1655654433,
|
|
||||||
"narHash": "sha256-auHQ0XPCiaTPSn+R3Yu4J7oZ5Zq/FS5/Da1ivvdYb/Y=",
|
|
||||||
"owner": "rust-lang",
|
|
||||||
"repo": "rust-analyzer",
|
|
||||||
"rev": "427061da19723f2206fe4dcb175c9c43b9a6193d",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "rust-lang",
|
|
||||||
"ref": "nightly",
|
|
||||||
"repo": "rust-analyzer",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"utils": {
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1653893745,
|
|
||||||
"narHash": "sha256-0jntwV3Z8//YwuOjzhV2sgJJPt+HY6KhU7VZUL0fKZQ=",
|
|
||||||
"owner": "numtide",
|
|
||||||
"repo": "flake-utils",
|
|
||||||
"rev": "1ed9fb1935d260de5fe1c2f7ee0ebaae17ed2fa1",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "numtide",
|
|
||||||
"repo": "flake-utils",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"root": "root",
|
|
||||||
"version": 7
|
|
||||||
}
|
|
||||||
72
flake.nix
@@ -1,72 +0,0 @@
|
|||||||
{
|
|
||||||
description = "The official Modrinth launcher";
|
|
||||||
|
|
||||||
inputs = {
|
|
||||||
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
|
|
||||||
utils.url = "github:numtide/flake-utils";
|
|
||||||
naersk = {
|
|
||||||
url = "github:nix-community/naersk";
|
|
||||||
inputs.nixpkgs.follows = "nixpkgs";
|
|
||||||
};
|
|
||||||
fenix = {
|
|
||||||
url = "github:nix-community/fenix";
|
|
||||||
inputs.nixpkgs.follows = "nixpkgs";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
outputs = inputs@{self, ...}:
|
|
||||||
inputs.utils.lib.eachDefaultSystem (system: let
|
|
||||||
pkgs = import inputs.nixpkgs { inherit system; };
|
|
||||||
fenix = inputs.fenix.packages.${system};
|
|
||||||
utils = inputs.utils.lib;
|
|
||||||
|
|
||||||
toolchain = with fenix;
|
|
||||||
combine [
|
|
||||||
minimal.rustc minimal.cargo
|
|
||||||
];
|
|
||||||
|
|
||||||
naersk = inputs.naersk.lib.${system}.override {
|
|
||||||
rustc = toolchain;
|
|
||||||
cargo = toolchain;
|
|
||||||
};
|
|
||||||
|
|
||||||
deps = with pkgs; {
|
|
||||||
global = [
|
|
||||||
openssl pkg-config gcc
|
|
||||||
];
|
|
||||||
gui = [
|
|
||||||
gtk4 gdk-pixbuf atk webkitgtk dbus
|
|
||||||
];
|
|
||||||
shell = [
|
|
||||||
(with fenix; combine [toolchain default.clippy complete.rust-src rust-analyzer])
|
|
||||||
git
|
|
||||||
jdk17 jdk8
|
|
||||||
];
|
|
||||||
};
|
|
||||||
in {
|
|
||||||
packages = {
|
|
||||||
theseus-cli = naersk.buildPackage {
|
|
||||||
pname = "theseus_cli";
|
|
||||||
src = ./.;
|
|
||||||
buildInputs = deps.global;
|
|
||||||
cargoBuildOptions = x: x ++ ["-p" "theseus_cli"];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
apps = {
|
|
||||||
cli = utils.mkApp {
|
|
||||||
drv = self.packages.${system}.theseus-cli;
|
|
||||||
};
|
|
||||||
cli-dev = utils.mkApp {
|
|
||||||
drv = self.packages.${system}.theseus-cli.overrideAttrs (old: old // {
|
|
||||||
release = false;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
devShell = pkgs.mkShell {
|
|
||||||
buildInputs = with deps;
|
|
||||||
global ++ gui ++ shell;
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@@ -11,7 +11,9 @@ bytes = "1"
|
|||||||
bincode = { version = "2.0.0-rc.1", features = ["serde"] }
|
bincode = { version = "2.0.0-rc.1", features = ["serde"] }
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
sha1 = { version = "0.6.0", features = ["std"]}
|
toml = "0.7.3"
|
||||||
|
sha1 = { version = "0.6.1", features = ["std"]}
|
||||||
|
sha2 = "0.9.9"
|
||||||
sled = { version = "0.34.7", features = ["compression"] }
|
sled = { version = "0.34.7", features = ["compression"] }
|
||||||
url = "2.2"
|
url = "2.2"
|
||||||
uuid = { version = "1.1", features = ["serde", "v4"] }
|
uuid = { version = "1.1", features = ["serde", "v4"] }
|
||||||
@@ -29,7 +31,7 @@ tracing = "0.1"
|
|||||||
tracing-error = "0.2"
|
tracing-error = "0.2"
|
||||||
|
|
||||||
|
|
||||||
async-tungstenite = { version = "0.17", features = ["tokio-runtime", "tokio-native-tls"] }
|
async-tungstenite = { version = "0.20.0", features = ["tokio-runtime", "tokio-native-tls"] }
|
||||||
futures = "0.3"
|
futures = "0.3"
|
||||||
once_cell = "1.9.0"
|
once_cell = "1.9.0"
|
||||||
reqwest = { version = "0.11", features = ["json"] }
|
reqwest = { version = "0.11", features = ["json"] }
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ pub async fn has_user(user: uuid::Uuid) -> crate::Result<bool> {
|
|||||||
let state = State::get().await?;
|
let state = State::get().await?;
|
||||||
let users = state.users.read().await;
|
let users = state.users.read().await;
|
||||||
|
|
||||||
Ok(users.contains(user)?)
|
users.contains(user)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a copy of the list of all user credentials
|
/// Get a copy of the list of all user credentials
|
||||||
|
|||||||
@@ -68,12 +68,7 @@ pub async fn is_managed(profile: &Path) -> crate::Result<bool> {
|
|||||||
pub async fn is_loaded(profile: &Path) -> crate::Result<bool> {
|
pub async fn is_loaded(profile: &Path) -> crate::Result<bool> {
|
||||||
let state = State::get().await?;
|
let state = State::get().await?;
|
||||||
let profiles = state.profiles.read().await;
|
let profiles = state.profiles.read().await;
|
||||||
Ok(profiles
|
Ok(profiles.0.get(profile).and_then(Option::as_ref).is_some())
|
||||||
.0
|
|
||||||
.get(profile)
|
|
||||||
.map(Option::as_ref)
|
|
||||||
.flatten()
|
|
||||||
.is_some())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Edit a profile using a given asynchronous closure
|
/// Edit a profile using a given asynchronous closure
|
||||||
@@ -138,8 +133,8 @@ pub async fn run(
|
|||||||
})?;
|
})?;
|
||||||
let version_info = d::minecraft::fetch_version_info(version).await?;
|
let version_info = d::minecraft::fetch_version_info(version).await?;
|
||||||
|
|
||||||
let ref pre_launch_hooks =
|
let pre_launch_hooks =
|
||||||
profile.hooks.as_ref().unwrap_or(&settings.hooks).pre_launch;
|
&profile.hooks.as_ref().unwrap_or(&settings.hooks).pre_launch;
|
||||||
for hook in pre_launch_hooks.iter() {
|
for hook in pre_launch_hooks.iter() {
|
||||||
// TODO: hook parameters
|
// TODO: hook parameters
|
||||||
let mut cmd = hook.split(' ');
|
let mut cmd = hook.split(' ');
|
||||||
@@ -190,7 +185,7 @@ pub async fn run(
|
|||||||
.as_error());
|
.as_error());
|
||||||
}
|
}
|
||||||
|
|
||||||
let ref java_args = profile
|
let java_args = profile
|
||||||
.java
|
.java
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.and_then(|it| it.extra_arguments.as_ref())
|
.and_then(|it| it.extra_arguments.as_ref())
|
||||||
@@ -201,18 +196,18 @@ pub async fn run(
|
|||||||
.as_ref()
|
.as_ref()
|
||||||
.map_or(&settings.hooks.wrapper, |it| &it.wrapper);
|
.map_or(&settings.hooks.wrapper, |it| &it.wrapper);
|
||||||
|
|
||||||
let ref memory = profile.memory.unwrap_or(settings.memory);
|
let memory = profile.memory.unwrap_or(settings.memory);
|
||||||
let ref resolution = profile.resolution.unwrap_or(settings.game_resolution);
|
let resolution = profile.resolution.unwrap_or(settings.game_resolution);
|
||||||
|
|
||||||
crate::launcher::launch_minecraft(
|
crate::launcher::launch_minecraft(
|
||||||
&profile.metadata.game_version,
|
&profile.metadata.game_version,
|
||||||
&profile.metadata.loader_version,
|
&profile.metadata.loader_version,
|
||||||
&profile.path,
|
&profile.path,
|
||||||
&java_install,
|
java_install,
|
||||||
&java_args,
|
java_args,
|
||||||
&wrapper,
|
wrapper,
|
||||||
memory,
|
&memory,
|
||||||
resolution,
|
&resolution,
|
||||||
credentials,
|
credentials,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ pub static REQWEST_CLIENT: Lazy<reqwest::Client> = Lazy::new(|| {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
pub const MODRINTH_API_URL: &str = "https://api.modrinth.com/v2/";
|
||||||
|
|
||||||
pub fn sled_config() -> sled::Config {
|
pub fn sled_config() -> sled::Config {
|
||||||
sled::Config::default().use_compression(true)
|
sled::Config::default().use_compression(true)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ pub fn get_class_paths_jar<T: AsRef<str>>(
|
|||||||
pub fn get_lib_path(libraries_path: &Path, lib: &str) -> crate::Result<String> {
|
pub fn get_lib_path(libraries_path: &Path, lib: &str) -> crate::Result<String> {
|
||||||
let mut path = libraries_path.to_path_buf();
|
let mut path = libraries_path.to_path_buf();
|
||||||
|
|
||||||
path.push(get_path_from_artifact(lib.as_ref())?);
|
path.push(get_path_from_artifact(lib)?);
|
||||||
|
|
||||||
let path = &path.canonicalize().map_err(|_| {
|
let path = &path.canonicalize().map_err(|_| {
|
||||||
crate::ErrorKind::LauncherError(format!(
|
crate::ErrorKind::LauncherError(format!(
|
||||||
@@ -164,8 +164,7 @@ fn parse_jvm_argument(
|
|||||||
))
|
))
|
||||||
.as_error()
|
.as_error()
|
||||||
})?
|
})?
|
||||||
.to_string_lossy()
|
.to_string_lossy(),
|
||||||
.to_string(),
|
|
||||||
)
|
)
|
||||||
.replace("${classpath_separator}", classpath_separator())
|
.replace("${classpath_separator}", classpath_separator())
|
||||||
.replace("${launcher_name}", "theseus")
|
.replace("${launcher_name}", "theseus")
|
||||||
@@ -219,7 +218,6 @@ pub fn get_minecraft_arguments(
|
|||||||
resolution,
|
resolution,
|
||||||
)?
|
)?
|
||||||
.split(' ')
|
.split(' ')
|
||||||
.into_iter()
|
|
||||||
.map(|x| x.to_string())
|
.map(|x| x.to_string())
|
||||||
.collect())
|
.collect())
|
||||||
} else {
|
} else {
|
||||||
@@ -260,8 +258,7 @@ fn parse_minecraft_argument(
|
|||||||
))
|
))
|
||||||
.as_error()
|
.as_error()
|
||||||
})?
|
})?
|
||||||
.to_string_lossy()
|
.to_string_lossy(),
|
||||||
.to_owned(),
|
|
||||||
)
|
)
|
||||||
.replace(
|
.replace(
|
||||||
"${assets_root}",
|
"${assets_root}",
|
||||||
@@ -274,8 +271,7 @@ fn parse_minecraft_argument(
|
|||||||
))
|
))
|
||||||
.as_error()
|
.as_error()
|
||||||
})?
|
})?
|
||||||
.to_string_lossy()
|
.to_string_lossy(),
|
||||||
.to_owned(),
|
|
||||||
)
|
)
|
||||||
.replace(
|
.replace(
|
||||||
"${game_assets}",
|
"${game_assets}",
|
||||||
@@ -288,8 +284,7 @@ fn parse_minecraft_argument(
|
|||||||
))
|
))
|
||||||
.as_error()
|
.as_error()
|
||||||
})?
|
})?
|
||||||
.to_string_lossy()
|
.to_string_lossy(),
|
||||||
.to_owned(),
|
|
||||||
)
|
)
|
||||||
.replace("${version_type}", version_type.as_str())
|
.replace("${version_type}", version_type.as_str())
|
||||||
.replace("${resolution_width}", &resolution.0.to_string())
|
.replace("${resolution_width}", &resolution.0.to_string())
|
||||||
@@ -366,7 +361,7 @@ pub fn get_processor_arguments<T: AsRef<str>>(
|
|||||||
pub async fn get_processor_main_class(
|
pub async fn get_processor_main_class(
|
||||||
path: String,
|
path: String,
|
||||||
) -> crate::Result<Option<String>> {
|
) -> crate::Result<Option<String>> {
|
||||||
Ok(tokio::task::spawn_blocking(move || {
|
tokio::task::spawn_blocking(move || {
|
||||||
let zipfile = std::fs::File::open(&path)?;
|
let zipfile = std::fs::File::open(&path)?;
|
||||||
let mut archive = zip::ZipArchive::new(zipfile).map_err(|_| {
|
let mut archive = zip::ZipArchive::new(zipfile).map_err(|_| {
|
||||||
crate::ErrorKind::LauncherError(format!(
|
crate::ErrorKind::LauncherError(format!(
|
||||||
@@ -400,5 +395,5 @@ pub async fn get_processor_main_class(
|
|||||||
Ok::<Option<String>, crate::Error>(None)
|
Ok::<Option<String>, crate::Error>(None)
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
.unwrap()?)
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,12 +3,14 @@ use async_tungstenite as ws;
|
|||||||
use bincode::{Decode, Encode};
|
use bincode::{Decode, Encode};
|
||||||
use chrono::{prelude::*, Duration};
|
use chrono::{prelude::*, Duration};
|
||||||
use futures::prelude::*;
|
use futures::prelude::*;
|
||||||
use once_cell::sync::*;
|
use lazy_static::lazy_static;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
pub const HYDRA_URL: Lazy<Url> =
|
lazy_static! {
|
||||||
Lazy::new(|| Url::parse("https://hydra.modrinth.com").unwrap());
|
static ref HYDRA_URL: Url =
|
||||||
|
Url::parse("https://hydra.modrinth.com").unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
// Socket messages
|
// Socket messages
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
@@ -65,7 +67,7 @@ pub struct HydraAuthFlow<S: AsyncRead + AsyncWrite + Unpin> {
|
|||||||
impl HydraAuthFlow<ws::tokio::ConnectStream> {
|
impl HydraAuthFlow<ws::tokio::ConnectStream> {
|
||||||
pub async fn new() -> crate::Result<Self> {
|
pub async fn new() -> crate::Result<Self> {
|
||||||
let sock_url = wrap_ref_builder!(
|
let sock_url = wrap_ref_builder!(
|
||||||
it = HYDRA_URL =>
|
it = HYDRA_URL.clone() =>
|
||||||
{ it.set_scheme("wss").ok() }
|
{ it.set_scheme("wss").ok() }
|
||||||
);
|
);
|
||||||
let (socket, _) = ws::tokio::connect_async(sock_url.clone()).await?;
|
let (socket, _) = ws::tokio::connect_async(sock_url.clone()).await?;
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ pub async fn download_client(
|
|||||||
st: &State,
|
st: &State,
|
||||||
version_info: &GameVersionInfo,
|
version_info: &GameVersionInfo,
|
||||||
) -> crate::Result<()> {
|
) -> crate::Result<()> {
|
||||||
let ref version = version_info.id;
|
let version = &version_info.id;
|
||||||
log::debug!("Locating client for version {version}");
|
log::debug!("Locating client for version {version}");
|
||||||
let client_download = version_info
|
let client_download = version_info
|
||||||
.downloads
|
.downloads
|
||||||
@@ -143,7 +143,7 @@ pub async fn download_assets(
|
|||||||
stream::iter(index.objects.iter())
|
stream::iter(index.objects.iter())
|
||||||
.map(Ok::<(&String, &Asset), crate::Error>)
|
.map(Ok::<(&String, &Asset), crate::Error>)
|
||||||
.try_for_each_concurrent(None, |(name, asset)| async move {
|
.try_for_each_concurrent(None, |(name, asset)| async move {
|
||||||
let ref hash = asset.hash;
|
let hash = &asset.hash;
|
||||||
let resource_path = st.directories.object_dir(hash);
|
let resource_path = st.directories.object_dir(hash);
|
||||||
let url = format!(
|
let url = format!(
|
||||||
"https://resources.download.minecraft.net/{sub_hash}/{hash}",
|
"https://resources.download.minecraft.net/{sub_hash}/{hash}",
|
||||||
@@ -158,7 +158,7 @@ pub async fn download_assets(
|
|||||||
let resource = fetch_cell
|
let resource = fetch_cell
|
||||||
.get_or_try_init(|| fetch(&url, Some(hash), &permit))
|
.get_or_try_init(|| fetch(&url, Some(hash), &permit))
|
||||||
.await?;
|
.await?;
|
||||||
write(&resource_path, &resource, &permit).await?;
|
write(&resource_path, resource, &permit).await?;
|
||||||
log::info!("Fetched asset with hash {hash}");
|
log::info!("Fetched asset with hash {hash}");
|
||||||
}
|
}
|
||||||
Ok::<_, crate::Error>(())
|
Ok::<_, crate::Error>(())
|
||||||
@@ -172,7 +172,7 @@ pub async fn download_assets(
|
|||||||
let resource_path = st.directories.legacy_assets_dir().join(
|
let resource_path = st.directories.legacy_assets_dir().join(
|
||||||
name.replace('/', &String::from(std::path::MAIN_SEPARATOR))
|
name.replace('/', &String::from(std::path::MAIN_SEPARATOR))
|
||||||
);
|
);
|
||||||
write(&resource_path, &resource, &permit).await?;
|
write(&resource_path, resource, &permit).await?;
|
||||||
log::info!("Fetched legacy asset with hash {hash}");
|
log::info!("Fetched legacy asset with hash {hash}");
|
||||||
}
|
}
|
||||||
Ok::<_, crate::Error>(())
|
Ok::<_, crate::Error>(())
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ macro_rules! processor_rules {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
#[tracing::instrument(skip_all, fields(path = ?instance_path))]
|
#[tracing::instrument(skip_all, fields(path = ?instance_path))]
|
||||||
pub async fn launch_minecraft(
|
pub async fn launch_minecraft(
|
||||||
game_version: &str,
|
game_version: &str,
|
||||||
@@ -75,7 +76,7 @@ pub async fn launch_minecraft(
|
|||||||
|
|
||||||
let mut version_info = download::download_version_info(
|
let mut version_info = download::download_version_info(
|
||||||
&state,
|
&state,
|
||||||
&version,
|
version,
|
||||||
loader_version.as_ref(),
|
loader_version.as_ref(),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|||||||
@@ -122,6 +122,12 @@ impl DirectoryInfo {
|
|||||||
self.config_dir.join("settings.json")
|
self.config_dir.join("settings.json")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the cache directory for Theseus
|
||||||
|
#[inline]
|
||||||
|
pub fn caches_dir(&self) -> PathBuf {
|
||||||
|
self.config_dir.join("caches")
|
||||||
|
}
|
||||||
|
|
||||||
/// Get path from environment variable
|
/// Get path from environment variable
|
||||||
#[inline]
|
#[inline]
|
||||||
fn env_path(name: &str) -> Option<PathBuf> {
|
fn env_path(name: &str) -> Option<PathBuf> {
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ impl Metadata {
|
|||||||
|
|
||||||
if let Some(ref meta_bin) = db.get(METADATA_DB_FIELD)? {
|
if let Some(ref meta_bin) = db.get(METADATA_DB_FIELD)? {
|
||||||
match bincode::decode_from_slice::<Self, _>(
|
match bincode::decode_from_slice::<Self, _>(
|
||||||
&meta_bin,
|
meta_bin,
|
||||||
*BINCODE_CONFIG,
|
*BINCODE_CONFIG,
|
||||||
) {
|
) {
|
||||||
Ok((meta, _)) => metadata = Some(meta),
|
Ok((meta, _)) => metadata = Some(meta),
|
||||||
|
|||||||
@@ -16,6 +16,9 @@ pub use self::profiles::*;
|
|||||||
mod settings;
|
mod settings;
|
||||||
pub use self::settings::*;
|
pub use self::settings::*;
|
||||||
|
|
||||||
|
mod projects;
|
||||||
|
pub use self::projects::*;
|
||||||
|
|
||||||
mod users;
|
mod users;
|
||||||
pub use self::users::*;
|
pub use self::users::*;
|
||||||
|
|
||||||
@@ -62,7 +65,7 @@ impl State {
|
|||||||
// Launcher data
|
// Launcher data
|
||||||
let (metadata, profiles) = tokio::try_join! {
|
let (metadata, profiles) = tokio::try_join! {
|
||||||
Metadata::init(&database),
|
Metadata::init(&database),
|
||||||
Profiles::init(&database),
|
Profiles::init(&database, &directories),
|
||||||
}?;
|
}?;
|
||||||
let users = Users::init(&database)?;
|
let users = Users::init(&database)?;
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
use super::settings::{Hooks, MemorySettings, WindowSize};
|
use super::settings::{Hooks, MemorySettings, WindowSize};
|
||||||
use crate::config::BINCODE_CONFIG;
|
use crate::config::BINCODE_CONFIG;
|
||||||
|
use crate::data::DirectoryInfo;
|
||||||
|
use crate::state::projects::Project;
|
||||||
use daedalus::modded::LoaderVersion;
|
use daedalus::modded::LoaderVersion;
|
||||||
use futures::prelude::*;
|
use futures::prelude::*;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
@@ -16,7 +18,7 @@ pub(crate) struct Profiles(pub HashMap<PathBuf, Option<Profile>>);
|
|||||||
|
|
||||||
// TODO: possibly add defaults to some of these values
|
// TODO: possibly add defaults to some of these values
|
||||||
pub const CURRENT_FORMAT_VERSION: u32 = 1;
|
pub const CURRENT_FORMAT_VERSION: u32 = 1;
|
||||||
pub const SUPPORTED_ICON_FORMATS: &[&'static str] = &[
|
pub const SUPPORTED_ICON_FORMATS: &[&str] = &[
|
||||||
"bmp", "gif", "jpeg", "jpg", "jpe", "png", "svg", "svgz", "webp", "rgb",
|
"bmp", "gif", "jpeg", "jpg", "jpe", "png", "svg", "svgz", "webp", "rgb",
|
||||||
"mp4",
|
"mp4",
|
||||||
];
|
];
|
||||||
@@ -27,6 +29,7 @@ pub struct Profile {
|
|||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
pub path: PathBuf,
|
pub path: PathBuf,
|
||||||
pub metadata: ProfileMetadata,
|
pub metadata: ProfileMetadata,
|
||||||
|
pub projects: HashMap<PathBuf, Project>,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub java: Option<JavaSettings>,
|
pub java: Option<JavaSettings>,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
@@ -51,26 +54,23 @@ pub struct ProfileMetadata {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Quilt?
|
// TODO: Quilt?
|
||||||
#[derive(Debug, Eq, PartialEq, Clone, Copy, Deserialize, Serialize)]
|
#[derive(
|
||||||
|
Debug, Eq, PartialEq, Clone, Copy, Deserialize, Serialize, Default,
|
||||||
|
)]
|
||||||
#[serde(rename_all = "lowercase")]
|
#[serde(rename_all = "lowercase")]
|
||||||
pub enum ModLoader {
|
pub enum ModLoader {
|
||||||
|
#[default]
|
||||||
Vanilla,
|
Vanilla,
|
||||||
Forge,
|
Forge,
|
||||||
Fabric,
|
Fabric,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for ModLoader {
|
|
||||||
fn default() -> Self {
|
|
||||||
ModLoader::Vanilla
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::fmt::Display for ModLoader {
|
impl std::fmt::Display for ModLoader {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
f.write_str(match self {
|
f.write_str(match *self {
|
||||||
&Self::Vanilla => "Vanilla",
|
Self::Vanilla => "Vanilla",
|
||||||
&Self::Forge => "Forge",
|
Self::Forge => "Forge",
|
||||||
&Self::Fabric => "Fabric",
|
Self::Fabric => "Fabric",
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -107,6 +107,7 @@ impl Profile {
|
|||||||
loader_version: None,
|
loader_version: None,
|
||||||
format_version: CURRENT_FORMAT_VERSION,
|
format_version: CURRENT_FORMAT_VERSION,
|
||||||
},
|
},
|
||||||
|
projects: HashMap::new(),
|
||||||
java: None,
|
java: None,
|
||||||
memory: None,
|
memory: None,
|
||||||
resolution: None,
|
resolution: None,
|
||||||
@@ -200,7 +201,10 @@ impl Profile {
|
|||||||
|
|
||||||
impl Profiles {
|
impl Profiles {
|
||||||
#[tracing::instrument(skip(db))]
|
#[tracing::instrument(skip(db))]
|
||||||
pub async fn init(db: &sled::Db) -> crate::Result<Self> {
|
pub async fn init(
|
||||||
|
db: &sled::Db,
|
||||||
|
dirs: &DirectoryInfo,
|
||||||
|
) -> crate::Result<Self> {
|
||||||
let profile_db = db.get(PROFILE_SUBTREE)?.map_or(
|
let profile_db = db.get(PROFILE_SUBTREE)?.map_or(
|
||||||
Ok(Default::default()),
|
Ok(Default::default()),
|
||||||
|bytes| {
|
|bytes| {
|
||||||
@@ -212,7 +216,7 @@ impl Profiles {
|
|||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let profiles = stream::iter(profile_db.iter())
|
let mut profiles = stream::iter(profile_db.iter())
|
||||||
.then(|it| async move {
|
.then(|it| async move {
|
||||||
let path = PathBuf::from(it);
|
let path = PathBuf::from(it);
|
||||||
let prof = match Self::read_profile_from_dir(&path).await {
|
let prof = match Self::read_profile_from_dir(&path).await {
|
||||||
@@ -227,6 +231,37 @@ impl Profiles {
|
|||||||
.collect::<HashMap<PathBuf, Option<Profile>>>()
|
.collect::<HashMap<PathBuf, Option<Profile>>>()
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
|
// project path, parent profile path
|
||||||
|
let mut files: HashMap<PathBuf, PathBuf> = HashMap::new();
|
||||||
|
{
|
||||||
|
for (profile_path, _profile_opt) in profiles.iter() {
|
||||||
|
let mut read_paths = |path: &str| {
|
||||||
|
for path in std::fs::read_dir(profile_path.join(path))? {
|
||||||
|
files.insert(path?.path(), profile_path.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok::<(), crate::Error>(())
|
||||||
|
};
|
||||||
|
read_paths("mods")?;
|
||||||
|
read_paths("shaders")?;
|
||||||
|
read_paths("resourcepacks")?;
|
||||||
|
read_paths("datapacks")?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let inferred = super::projects::infer_data_from_files(
|
||||||
|
files.keys().cloned().collect(),
|
||||||
|
dirs.caches_dir(),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
for (key, value) in inferred {
|
||||||
|
if let Some(profile_path) = files.get(&key) {
|
||||||
|
if let Some(Some(profile)) = profiles.get_mut(profile_path) {
|
||||||
|
profile.projects.insert(key, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Ok(Self(profiles))
|
Ok(Self(profiles))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -296,63 +331,3 @@ impl Profiles {
|
|||||||
Ok(profile)
|
Ok(profile)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
use pretty_assertions::{assert_eq, assert_str_eq};
|
|
||||||
use std::collections::HashSet;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn profile_test() -> Result<(), serde_json::Error> {
|
|
||||||
let profile = Profile {
|
|
||||||
path: PathBuf::new(),
|
|
||||||
metadata: ProfileMetadata {
|
|
||||||
name: String::from("Example Pack"),
|
|
||||||
icon: None,
|
|
||||||
game_version: String::from("1.18.2"),
|
|
||||||
loader: ModLoader::Vanilla,
|
|
||||||
loader_version: None,
|
|
||||||
format_version: CURRENT_FORMAT_VERSION,
|
|
||||||
},
|
|
||||||
java: Some(JavaSettings {
|
|
||||||
install: Some(PathBuf::from("/usr/bin/java")),
|
|
||||||
extra_arguments: Some(Vec::new()),
|
|
||||||
}),
|
|
||||||
memory: Some(MemorySettings {
|
|
||||||
minimum: None,
|
|
||||||
maximum: 8192,
|
|
||||||
}),
|
|
||||||
resolution: Some(WindowSize(1920, 1080)),
|
|
||||||
hooks: Some(Hooks {
|
|
||||||
pre_launch: HashSet::new(),
|
|
||||||
wrapper: None,
|
|
||||||
post_exit: HashSet::new(),
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
let json = serde_json::json!({
|
|
||||||
"metadata": {
|
|
||||||
"name": "Example Pack",
|
|
||||||
"game_version": "1.18.2",
|
|
||||||
"format_version": 1u32,
|
|
||||||
"loader": "vanilla",
|
|
||||||
},
|
|
||||||
"java": {
|
|
||||||
"extra_arguments": [],
|
|
||||||
"install": "/usr/bin/java",
|
|
||||||
},
|
|
||||||
"memory": {
|
|
||||||
"maximum": 8192u32,
|
|
||||||
},
|
|
||||||
"resolution": (1920u16, 1080u16),
|
|
||||||
"hooks": {},
|
|
||||||
});
|
|
||||||
|
|
||||||
assert_eq!(serde_json::to_value(profile.clone())?, json.clone());
|
|
||||||
assert_str_eq!(
|
|
||||||
format!("{:?}", serde_json::from_value::<Profile>(json)?),
|
|
||||||
format!("{:?}", profile),
|
|
||||||
);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
428
theseus/src/state/projects.rs
Normal file
@@ -0,0 +1,428 @@
|
|||||||
|
//! Project management + inference
|
||||||
|
|
||||||
|
use crate::config::{MODRINTH_API_URL, REQWEST_CLIENT};
|
||||||
|
use chrono::{DateTime, Utc};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use serde_json::json;
|
||||||
|
use sha2::Digest;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::ffi::OsStr;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::{Read, Write};
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
use tokio::io::AsyncReadExt;
|
||||||
|
use zip::ZipArchive;
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||||
|
pub struct Project {
|
||||||
|
pub sha512: String,
|
||||||
|
pub disabled: bool,
|
||||||
|
pub metadata: ProjectMetadata,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||||
|
pub struct ModrinthProject {
|
||||||
|
pub id: String,
|
||||||
|
pub slug: Option<String>,
|
||||||
|
pub project_type: String,
|
||||||
|
pub team: String,
|
||||||
|
pub title: String,
|
||||||
|
pub description: String,
|
||||||
|
pub body: String,
|
||||||
|
|
||||||
|
pub published: DateTime<Utc>,
|
||||||
|
pub updated: DateTime<Utc>,
|
||||||
|
|
||||||
|
pub client_side: String,
|
||||||
|
pub server_side: String,
|
||||||
|
|
||||||
|
pub downloads: u32,
|
||||||
|
pub followers: u32,
|
||||||
|
|
||||||
|
pub categories: Vec<String>,
|
||||||
|
pub additional_categories: Vec<String>,
|
||||||
|
pub game_versions: Vec<String>,
|
||||||
|
pub loaders: Vec<String>,
|
||||||
|
|
||||||
|
pub versions: Vec<String>,
|
||||||
|
|
||||||
|
pub icon_url: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||||
|
#[serde(tag = "type", rename_all = "snake_case")]
|
||||||
|
pub enum ProjectMetadata {
|
||||||
|
Modrinth(Box<ModrinthProject>),
|
||||||
|
Inferred {
|
||||||
|
title: Option<String>,
|
||||||
|
description: Option<String>,
|
||||||
|
authors: Vec<String>,
|
||||||
|
version: Option<String>,
|
||||||
|
icon: Option<PathBuf>,
|
||||||
|
},
|
||||||
|
Unknown,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn infer_data_from_files(
|
||||||
|
paths: Vec<PathBuf>,
|
||||||
|
cache_dir: PathBuf,
|
||||||
|
) -> crate::Result<HashMap<PathBuf, Project>> {
|
||||||
|
let mut file_path_hashes = HashMap::new();
|
||||||
|
|
||||||
|
// TODO: Make this concurrent and use progressive hashing to avoid loading each JAR in memory
|
||||||
|
for path in paths.clone() {
|
||||||
|
let mut file = tokio::fs::File::open(path.clone()).await?;
|
||||||
|
|
||||||
|
let mut buffer = Vec::new();
|
||||||
|
file.read_to_end(&mut buffer).await?;
|
||||||
|
|
||||||
|
let hash = format!("{:x}", sha2::Sha512::digest(&buffer));
|
||||||
|
file_path_hashes.insert(hash, path.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: add disabled mods
|
||||||
|
// TODO: add retrying
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct ModrinthVersion {
|
||||||
|
pub project_id: String,
|
||||||
|
}
|
||||||
|
let files: HashMap<String, ModrinthVersion> = REQWEST_CLIENT
|
||||||
|
.post(format!("{}version_files", MODRINTH_API_URL))
|
||||||
|
.json(&json!({
|
||||||
|
"hashes": file_path_hashes.keys().collect::<Vec<_>>(),
|
||||||
|
"algorithm": "sha512",
|
||||||
|
}))
|
||||||
|
.send()
|
||||||
|
.await?
|
||||||
|
.json()
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let projects: Vec<ModrinthProject> = REQWEST_CLIENT
|
||||||
|
.get(format!(
|
||||||
|
"{}projects?ids={}",
|
||||||
|
MODRINTH_API_URL,
|
||||||
|
serde_json::to_string(
|
||||||
|
&files
|
||||||
|
.values()
|
||||||
|
.map(|x| x.project_id.clone())
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
)?
|
||||||
|
))
|
||||||
|
.send()
|
||||||
|
.await?
|
||||||
|
.json()
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let mut return_projects = HashMap::new();
|
||||||
|
let mut further_analyze_projects: Vec<(String, PathBuf)> = Vec::new();
|
||||||
|
|
||||||
|
for (hash, path) in file_path_hashes {
|
||||||
|
if let Some(file) = files.get(&hash) {
|
||||||
|
if let Some(project) =
|
||||||
|
projects.iter().find(|x| file.project_id == x.id)
|
||||||
|
{
|
||||||
|
return_projects.insert(
|
||||||
|
path,
|
||||||
|
Project {
|
||||||
|
sha512: hash,
|
||||||
|
disabled: false,
|
||||||
|
metadata: ProjectMetadata::Modrinth(Box::new(
|
||||||
|
project.clone(),
|
||||||
|
)),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
further_analyze_projects.push((hash, path));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (hash, path) in further_analyze_projects {
|
||||||
|
let file = File::open(path.clone())?;
|
||||||
|
|
||||||
|
// TODO: get rid of below unwrap
|
||||||
|
let mut zip = ZipArchive::new(file).unwrap();
|
||||||
|
|
||||||
|
let read_icon_from_file =
|
||||||
|
|icon_path: Option<String>| -> crate::Result<Option<PathBuf>> {
|
||||||
|
if let Some(icon_path) = icon_path {
|
||||||
|
// we have to repoen the zip twice here :(
|
||||||
|
let zip_file = File::open(path.clone())?;
|
||||||
|
if let Ok(mut zip) = ZipArchive::new(zip_file) {
|
||||||
|
if let Ok(mut file) = zip.by_name(&icon_path) {
|
||||||
|
let mut bytes = Vec::new();
|
||||||
|
if file.read_to_end(&mut bytes).is_ok() {
|
||||||
|
let extension = Path::new(&icon_path)
|
||||||
|
.extension()
|
||||||
|
.and_then(OsStr::to_str);
|
||||||
|
let hash = sha1::Sha1::from(&bytes).hexdigest();
|
||||||
|
let path = cache_dir.join("icons").join(
|
||||||
|
if let Some(ext) = extension {
|
||||||
|
format!("{hash}.{ext}")
|
||||||
|
} else {
|
||||||
|
hash
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
if !path.exists() {
|
||||||
|
if let Some(parent) = path.parent() {
|
||||||
|
std::fs::create_dir_all(parent)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut file = File::create(path.clone())?;
|
||||||
|
file.write_all(&bytes)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(Some(path));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(None)
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Ok(mut file) = zip.by_name("META-INF/mods.toml") {
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
struct ForgeModInfo {
|
||||||
|
pub mods: Vec<ForgeMod>,
|
||||||
|
}
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
struct ForgeMod {
|
||||||
|
mod_id: String,
|
||||||
|
version: Option<String>,
|
||||||
|
display_name: Option<String>,
|
||||||
|
description: Option<String>,
|
||||||
|
logo_file: Option<String>,
|
||||||
|
authors: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut file_str = String::new();
|
||||||
|
if file.read_to_string(&mut file_str).is_ok() {
|
||||||
|
if let Ok(pack) =
|
||||||
|
serde_json::from_str::<ForgeModInfo>(&file_str)
|
||||||
|
{
|
||||||
|
if let Some(pack) = pack.mods.first() {
|
||||||
|
let icon = read_icon_from_file(pack.logo_file.clone())?;
|
||||||
|
|
||||||
|
return_projects.insert(
|
||||||
|
path.clone(),
|
||||||
|
Project {
|
||||||
|
sha512: hash,
|
||||||
|
disabled: false,
|
||||||
|
metadata: ProjectMetadata::Inferred {
|
||||||
|
title: Some(
|
||||||
|
pack.display_name
|
||||||
|
.clone()
|
||||||
|
.unwrap_or(pack.mod_id.clone()),
|
||||||
|
),
|
||||||
|
description: pack.description.clone(),
|
||||||
|
authors: pack
|
||||||
|
.authors
|
||||||
|
.clone()
|
||||||
|
.map(|x| vec![x])
|
||||||
|
.unwrap_or_default(),
|
||||||
|
version: pack.version.clone(),
|
||||||
|
icon,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Ok(mut file) = zip.by_name("mcmod.info") {
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
struct ForgeMod {
|
||||||
|
modid: String,
|
||||||
|
name: String,
|
||||||
|
description: Option<String>,
|
||||||
|
version: Option<String>,
|
||||||
|
author_list: Option<Vec<String>>,
|
||||||
|
logo_file: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut file_str = String::new();
|
||||||
|
if file.read_to_string(&mut file_str).is_ok() {
|
||||||
|
if let Ok(pack) = serde_json::from_str::<ForgeMod>(&file_str) {
|
||||||
|
let icon = read_icon_from_file(pack.logo_file)?;
|
||||||
|
|
||||||
|
return_projects.insert(
|
||||||
|
path.clone(),
|
||||||
|
Project {
|
||||||
|
sha512: hash,
|
||||||
|
disabled: false,
|
||||||
|
metadata: ProjectMetadata::Inferred {
|
||||||
|
title: Some(if pack.name.is_empty() {
|
||||||
|
pack.modid
|
||||||
|
} else {
|
||||||
|
pack.name
|
||||||
|
}),
|
||||||
|
description: pack.description,
|
||||||
|
authors: pack.author_list.unwrap_or_default(),
|
||||||
|
version: pack.version,
|
||||||
|
icon,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Ok(mut file) = zip.by_name("fabric.mod.json") {
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
enum FabricAuthor {
|
||||||
|
String(String),
|
||||||
|
Object { name: String },
|
||||||
|
}
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
struct FabricMod {
|
||||||
|
id: String,
|
||||||
|
version: String,
|
||||||
|
name: Option<String>,
|
||||||
|
description: Option<String>,
|
||||||
|
authors: Vec<FabricAuthor>,
|
||||||
|
icon: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut file_str = String::new();
|
||||||
|
if file.read_to_string(&mut file_str).is_ok() {
|
||||||
|
if let Ok(pack) = serde_json::from_str::<FabricMod>(&file_str) {
|
||||||
|
let icon = read_icon_from_file(pack.icon)?;
|
||||||
|
|
||||||
|
return_projects.insert(
|
||||||
|
path.clone(),
|
||||||
|
Project {
|
||||||
|
sha512: hash,
|
||||||
|
disabled: false,
|
||||||
|
metadata: ProjectMetadata::Inferred {
|
||||||
|
title: Some(pack.name.unwrap_or(pack.id)),
|
||||||
|
description: pack.description,
|
||||||
|
authors: pack
|
||||||
|
.authors
|
||||||
|
.into_iter()
|
||||||
|
.map(|x| match x {
|
||||||
|
FabricAuthor::String(name) => name,
|
||||||
|
FabricAuthor::Object { name } => name,
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
version: Some(pack.version),
|
||||||
|
icon,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Ok(mut file) = zip.by_name("quilt.mod.json") {
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
struct QuiltMetadata {
|
||||||
|
pub name: Option<String>,
|
||||||
|
pub description: Option<String>,
|
||||||
|
pub contributors: Option<HashMap<String, String>>,
|
||||||
|
pub icon: Option<String>,
|
||||||
|
}
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
struct QuiltMod {
|
||||||
|
id: String,
|
||||||
|
version: String,
|
||||||
|
metadata: Option<QuiltMetadata>,
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut file_str = String::new();
|
||||||
|
if file.read_to_string(&mut file_str).is_ok() {
|
||||||
|
if let Ok(pack) = serde_json::from_str::<QuiltMod>(&file_str) {
|
||||||
|
let icon = read_icon_from_file(
|
||||||
|
pack.metadata.as_ref().and_then(|x| x.icon.clone()),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
return_projects.insert(
|
||||||
|
path.clone(),
|
||||||
|
Project {
|
||||||
|
sha512: hash,
|
||||||
|
disabled: false,
|
||||||
|
metadata: ProjectMetadata::Inferred {
|
||||||
|
title: Some(
|
||||||
|
pack.metadata
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|x| x.name.clone())
|
||||||
|
.unwrap_or(pack.id),
|
||||||
|
),
|
||||||
|
description: pack
|
||||||
|
.metadata
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|x| x.description.clone()),
|
||||||
|
authors: pack
|
||||||
|
.metadata
|
||||||
|
.map(|x| {
|
||||||
|
x.contributors
|
||||||
|
.unwrap_or_default()
|
||||||
|
.keys()
|
||||||
|
.cloned()
|
||||||
|
.collect()
|
||||||
|
})
|
||||||
|
.unwrap_or_default(),
|
||||||
|
version: Some(pack.version),
|
||||||
|
icon,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Ok(mut file) = zip.by_name("pack.mcmeta") {
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
struct Pack {
|
||||||
|
description: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut file_str = String::new();
|
||||||
|
if file.read_to_string(&mut file_str).is_ok() {
|
||||||
|
if let Ok(pack) = serde_json::from_str::<Pack>(&file_str) {
|
||||||
|
let icon =
|
||||||
|
read_icon_from_file(Some("pack.png".to_string()))?;
|
||||||
|
|
||||||
|
return_projects.insert(
|
||||||
|
path.clone(),
|
||||||
|
Project {
|
||||||
|
sha512: hash,
|
||||||
|
disabled: false,
|
||||||
|
metadata: ProjectMetadata::Inferred {
|
||||||
|
title: None,
|
||||||
|
description: pack.description,
|
||||||
|
authors: Vec::new(),
|
||||||
|
version: None,
|
||||||
|
icon,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return_projects.insert(
|
||||||
|
path,
|
||||||
|
Project {
|
||||||
|
sha512: hash,
|
||||||
|
disabled: false,
|
||||||
|
metadata: ProjectMetadata::Unknown,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(return_projects)
|
||||||
|
}
|
||||||
@@ -103,7 +103,7 @@ impl Default for WindowSize {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Game initialization hooks
|
/// Game initialization hooks
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub struct Hooks {
|
pub struct Hooks {
|
||||||
#[serde(skip_serializing_if = "HashSet::is_empty")]
|
#[serde(skip_serializing_if = "HashSet::is_empty")]
|
||||||
@@ -113,13 +113,3 @@ pub struct Hooks {
|
|||||||
#[serde(skip_serializing_if = "HashSet::is_empty")]
|
#[serde(skip_serializing_if = "HashSet::is_empty")]
|
||||||
pub post_exit: HashSet<String>,
|
pub post_exit: HashSet<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Hooks {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
pre_launch: HashSet::<String>::new(),
|
|
||||||
wrapper: None,
|
|
||||||
post_exit: HashSet::<String>::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ pub async fn fetch_mirrors(
|
|||||||
let _permits = sem.acquire_many(permits).await.unwrap();
|
let _permits = sem.acquire_many(permits).await.unwrap();
|
||||||
let sem = Arc::new(Semaphore::new(permits.try_into().unwrap()));
|
let sem = Arc::new(Semaphore::new(permits.try_into().unwrap()));
|
||||||
|
|
||||||
future::select_ok(urls.into_iter().map(|url| {
|
future::select_ok(urls.iter().map(|url| {
|
||||||
let sha1 = sha1.map(String::from);
|
let sha1 = sha1.map(String::from);
|
||||||
let url = String::from(*url);
|
let url = String::from(*url);
|
||||||
let sem = Arc::clone(&sem);
|
let sem = Arc::clone(&sem);
|
||||||
|
|||||||
@@ -185,11 +185,11 @@ pub fn get_all_jre_path() -> Result<HashSet<JavaVersion>, JREError> {
|
|||||||
|
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
const JAVA_BIN: &'static str = "java.exe";
|
const JAVA_BIN: &str = "java.exe";
|
||||||
|
|
||||||
#[cfg(not(target_os = "windows"))]
|
#[cfg(not(target_os = "windows"))]
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
const JAVA_BIN: &'static str = "java";
|
const JAVA_BIN: &str = "java";
|
||||||
|
|
||||||
// For example filepath 'path', attempt to resolve it and get a Java version at this path
|
// For example filepath 'path', attempt to resolve it and get a Java version at this path
|
||||||
// If no such path exists, or no such valid java at this path exists, returns None
|
// If no such path exists, or no such valid java at this path exists, returns None
|
||||||
|
|||||||
@@ -31,11 +31,7 @@ pub fn os_rule(rule: &OsRule) -> bool {
|
|||||||
let mut rule_match = true;
|
let mut rule_match = true;
|
||||||
|
|
||||||
if let Some(ref arch) = rule.arch {
|
if let Some(ref arch) = rule.arch {
|
||||||
rule_match &= match arch.as_str() {
|
rule_match &= !matches!(arch.as_str(), "x86" | "arm");
|
||||||
"x86" => cfg!(any(target_arch = "x86", target_arch = "x86_64")),
|
|
||||||
"arm" => cfg!(target_arch = "arm"),
|
|
||||||
_ => true,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(name) = &rule.name {
|
if let Some(name) = &rule.name {
|
||||||
|
|||||||
@@ -202,7 +202,7 @@ impl ProfileInit {
|
|||||||
let filter = |it: &LoaderVersion| match version.as_str() {
|
let filter = |it: &LoaderVersion| match version.as_str() {
|
||||||
"latest" => true,
|
"latest" => true,
|
||||||
"stable" => it.stable,
|
"stable" => it.stable,
|
||||||
id => it.id == String::from(id),
|
id => it.id == *id,
|
||||||
};
|
};
|
||||||
|
|
||||||
let loader_data = match loader {
|
let loader_data = match loader {
|
||||||
@@ -211,7 +211,7 @@ impl ProfileInit {
|
|||||||
_ => eyre::bail!("Could not get manifest for loader {loader}. This is a bug in the CLI!"),
|
_ => eyre::bail!("Could not get manifest for loader {loader}. This is a bug in the CLI!"),
|
||||||
};
|
};
|
||||||
|
|
||||||
let ref loaders = loader_data.game_versions
|
let loaders = &loader_data.game_versions
|
||||||
.iter()
|
.iter()
|
||||||
.find(|it| it.id == game_version)
|
.find(|it| it.id == game_version)
|
||||||
.ok_or_else(|| eyre::eyre!("Modloader {loader} unsupported for Minecraft version {game_version}"))?
|
.ok_or_else(|| eyre::eyre!("Modloader {loader} unsupported for Minecraft version {game_version}"))?
|
||||||
|
|||||||
13
theseus_gui/.editorconfig
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
end_of_line = lf
|
||||||
|
charset = utf-8
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
insert_final_newline = true
|
||||||
|
max_line_length = 100
|
||||||
|
|
||||||
|
[*.md]
|
||||||
|
trim_trailing_whitespace = false
|
||||||
@@ -4,29 +4,18 @@
|
|||||||
"es2021": true,
|
"es2021": true,
|
||||||
"node": true
|
"node": true
|
||||||
},
|
},
|
||||||
"extends": [
|
"extends": ["eslint:recommended", "plugin:vue/vue3-recommended", "prettier"],
|
||||||
"eslint:recommended",
|
|
||||||
"plugin:vue/vue3-recommended",
|
|
||||||
"plugin:@typescript-eslint/recommended",
|
|
||||||
"prettier"
|
|
||||||
],
|
|
||||||
"parserOptions": {
|
"parserOptions": {
|
||||||
"ecmaVersion": "latest",
|
"ecmaVersion": "latest",
|
||||||
"parser": "@typescript-eslint/parser",
|
|
||||||
"sourceType": "module"
|
"sourceType": "module"
|
||||||
},
|
},
|
||||||
"plugins": [
|
"plugins": ["vue"],
|
||||||
"vue",
|
|
||||||
"@typescript-eslint"
|
|
||||||
],
|
|
||||||
"rules": {
|
"rules": {
|
||||||
"no-console": "off",
|
"no-console": "off",
|
||||||
"vue/no-v-html": "off",
|
"vue/no-v-html": "off",
|
||||||
"comma-dangle": [
|
"comma-dangle": ["error", "only-multiline"],
|
||||||
"error",
|
"vue/comment-directive": "off",
|
||||||
"only-multiline"
|
|
||||||
],
|
|
||||||
"vue/multi-word-component-names": "off",
|
"vue/multi-word-component-names": "off",
|
||||||
"import/no-named-as-default": "off"
|
"import/no-named-as-default": "off"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3,4 +3,4 @@
|
|||||||
"semi": false,
|
"semi": false,
|
||||||
"singleQuote": true,
|
"singleQuote": true,
|
||||||
"endOfLine": "auto"
|
"endOfLine": "auto"
|
||||||
}
|
}
|
||||||
|
|||||||
6
theseus_gui/.vscode/extensions.json
vendored
@@ -1,7 +1,3 @@
|
|||||||
{
|
{
|
||||||
"recommendations": [
|
"recommendations": ["Vue.volar", "tauri-apps.tauri-vscode", "rust-lang.rust-analyzer"]
|
||||||
"Vue.volar",
|
|
||||||
"tauri-apps.tauri-vscode",
|
|
||||||
"rust-lang.rust-analyzer"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,10 @@
|
|||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
"build": "vite build",
|
"build": "vite build",
|
||||||
"preview": "vite preview",
|
"preview": "vite preview",
|
||||||
"tauri": "tauri"
|
"tauri": "tauri",
|
||||||
|
"lint:js": "eslint --ext .js,.vue,.ts,.jsx,.tsx,.html,.vue .",
|
||||||
|
"lint": "npm run lint:js && prettier --check .",
|
||||||
|
"fix": "eslint --fix --ext .js,.vue,.ts,.jsx,.tsx,.html,.vue ."
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@tauri-apps/api": "^1.2.0",
|
"@tauri-apps/api": "^1.2.0",
|
||||||
@@ -25,7 +28,7 @@
|
|||||||
"eslint": "^8.35.0",
|
"eslint": "^8.35.0",
|
||||||
"eslint-config-prettier": "^8.6.0",
|
"eslint-config-prettier": "^8.6.0",
|
||||||
"eslint-plugin-vue": "^9.9.0",
|
"eslint-plugin-vue": "^9.9.0",
|
||||||
"prettier": "^2.8.4",
|
"prettier": "^2.8.7",
|
||||||
"sass": "^1.58.3",
|
"sass": "^1.58.3",
|
||||||
"vite": "^4.0.0",
|
"vite": "^4.0.0",
|
||||||
"vite-plugin-eslint": "^1.8.1"
|
"vite-plugin-eslint": "^1.8.1"
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
fn main() {
|
fn main() {
|
||||||
tauri_build::build()
|
tauri_build::build()
|
||||||
}
|
}
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 6.8 KiB After Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 974 B After Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 7.6 KiB After Width: | Height: | Size: 40 KiB |
|
Before Width: | Height: | Size: 903 B After Width: | Height: | Size: 2.4 KiB |
|
Before Width: | Height: | Size: 8.4 KiB After Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 4.6 KiB |
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 8.5 KiB |
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 5.5 KiB |
|
Before Width: | Height: | Size: 85 KiB After Width: | Height: | Size: 56 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 76 KiB |
@@ -27,9 +27,13 @@
|
|||||||
},
|
},
|
||||||
"externalBin": [],
|
"externalBin": [],
|
||||||
"icon": [
|
"icon": [
|
||||||
"icons/favicon.ico"
|
"icons/32x32.png",
|
||||||
|
"icons/128x128.png",
|
||||||
|
"icons/128x128@2x.png",
|
||||||
|
"icons/icon.icns",
|
||||||
|
"icons/icon.ico"
|
||||||
],
|
],
|
||||||
"identifier": "com.tauri.dev",
|
"identifier": "com.modrinth.theseus",
|
||||||
"longDescription": "",
|
"longDescription": "",
|
||||||
"macOS": {
|
"macOS": {
|
||||||
"entitlements": null,
|
"entitlements": null,
|
||||||
@@ -65,4 +69,4 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,12 +9,10 @@ import {
|
|||||||
ClientIcon,
|
ClientIcon,
|
||||||
PlusIcon,
|
PlusIcon,
|
||||||
SettingsIcon,
|
SettingsIcon,
|
||||||
Button,
|
|
||||||
Avatar,
|
Avatar,
|
||||||
} from 'omorphia'
|
} from 'omorphia'
|
||||||
import { useTheming, useInstances } from '@/store/state'
|
import { useTheming, useInstances } from '@/store/state'
|
||||||
import { toggleTheme } from '@/helpers/theme'
|
import { toggleTheme } from '@/helpers/theme'
|
||||||
import Instance from '@/components/ui/Instance.vue'
|
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
@@ -39,7 +37,9 @@ watch(theme, (newState) => {
|
|||||||
<RouterLink to="/" class="button-base nav-button"><ClientIcon /></RouterLink>
|
<RouterLink to="/" class="button-base nav-button"><ClientIcon /></RouterLink>
|
||||||
<RouterLink to="/browse" class="button-base nav-button"> <SearchIcon /></RouterLink>
|
<RouterLink to="/browse" class="button-base nav-button"> <SearchIcon /></RouterLink>
|
||||||
<RouterLink to="/library" class="button-base nav-button"> <BookIcon /></RouterLink>
|
<RouterLink to="/library" class="button-base nav-button"> <BookIcon /></RouterLink>
|
||||||
<button color="primary" class="button-base primary nav-button" icon-only><PlusIcon /></button>
|
<button color="primary" class="button-base primary nav-button" icon-only>
|
||||||
|
<PlusIcon />
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="settings pages-list">
|
<div class="settings pages-list">
|
||||||
@@ -194,7 +194,7 @@ watch(theme, (newState) => {
|
|||||||
.nav-button {
|
.nav-button {
|
||||||
height: 3rem;
|
height: 3rem;
|
||||||
width: 3rem;
|
width: 3rem;
|
||||||
padding: .75rem;
|
padding: 0.75rem;
|
||||||
border-radius: var(--radius-md);
|
border-radius: var(--radius-md);
|
||||||
|
|
||||||
svg {
|
svg {
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
export { default as PlayIcon } from './play.svg'
|
export { default as PlayIcon } from './play.svg'
|
||||||
export { default as OpenFolderIcon } from './folder-open.svg'
|
export { default as OpenFolderIcon } from './folder-open.svg'
|
||||||
export { default as BrowseIcon } from './folder-search.svg'
|
export { default as BrowseIcon } from './folder-search.svg'
|
||||||
|
|||||||
@@ -1,13 +1,25 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import {ChevronLeftIcon, ChevronRightIcon} from 'omorphia'
|
import { ChevronLeftIcon, ChevronRightIcon } from 'omorphia'
|
||||||
import Instance from '@/components/ui/Instance.vue'
|
import Instance from '@/components/ui/Instance.vue'
|
||||||
import News from '@/components/ui/News.vue'
|
import { ref } from 'vue'
|
||||||
import {onMounted, onUnmounted, ref} from 'vue'
|
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
instances: Array,
|
instances: {
|
||||||
news: Array,
|
type: Array,
|
||||||
label: String,
|
default() {
|
||||||
|
return []
|
||||||
|
},
|
||||||
|
},
|
||||||
|
news: {
|
||||||
|
type: Array,
|
||||||
|
default() {
|
||||||
|
return []
|
||||||
|
},
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
canPaginate: Boolean,
|
canPaginate: Boolean,
|
||||||
})
|
})
|
||||||
const allowPagination = ref(false)
|
const allowPagination = ref(false)
|
||||||
@@ -16,9 +28,6 @@ const newsRow = ref(null)
|
|||||||
// Remove after state is populated with real data
|
// Remove after state is populated with real data
|
||||||
const shouldRenderNormalInstances = props.instances && props.instances?.length !== 0
|
const shouldRenderNormalInstances = props.instances && props.instances?.length !== 0
|
||||||
const shouldRenderNews = props.news && props.news?.length !== 0
|
const shouldRenderNews = props.news && props.news?.length !== 0
|
||||||
onUnmounted(() => {
|
|
||||||
if (props.canPaginate) window.removeEventListener('resize', handlePaginationDisplay)
|
|
||||||
})
|
|
||||||
const handleLeftPage = () => {
|
const handleLeftPage = () => {
|
||||||
if (shouldRenderNormalInstances) modsRow.value.scrollLeft -= 170
|
if (shouldRenderNormalInstances) modsRow.value.scrollLeft -= 170
|
||||||
else if (shouldRenderNews) newsRow.value.scrollLeft -= 170
|
else if (shouldRenderNews) newsRow.value.scrollLeft -= 170
|
||||||
@@ -32,10 +41,10 @@ const handleRightPage = () => {
|
|||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<p>{{ props.label }}</p>
|
<p>{{ props.label }}</p>
|
||||||
<hr>
|
<hr />
|
||||||
<div v-if="allowPagination" class="pagination">
|
<div v-if="allowPagination" class="pagination">
|
||||||
<ChevronLeftIcon @click="handleLeftPage"/>
|
<ChevronLeftIcon @click="handleLeftPage" />
|
||||||
<ChevronRightIcon @click="handleRightPage"/>
|
<ChevronRightIcon @click="handleRightPage" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<section ref="modsRow" class="instances">
|
<section ref="modsRow" class="instances">
|
||||||
@@ -129,4 +138,4 @@ const handleRightPage = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,13 +1,26 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import {ChevronLeftIcon, ChevronRightIcon} from 'omorphia'
|
import { ChevronLeftIcon, ChevronRightIcon } from 'omorphia'
|
||||||
import Instance from '@/components/ui/Instance.vue'
|
import Instance from '@/components/ui/Instance.vue'
|
||||||
import News from '@/components/ui/News.vue'
|
import News from '@/components/ui/News.vue'
|
||||||
import {onMounted, onUnmounted, ref} from 'vue'
|
import { onMounted, onUnmounted, ref } from 'vue'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
instances: Array,
|
instances: {
|
||||||
news: Array,
|
type: Array,
|
||||||
label: String,
|
default() {
|
||||||
|
return []
|
||||||
|
},
|
||||||
|
},
|
||||||
|
news: {
|
||||||
|
type: Array,
|
||||||
|
default() {
|
||||||
|
return []
|
||||||
|
},
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
canPaginate: Boolean,
|
canPaginate: Boolean,
|
||||||
})
|
})
|
||||||
const allowPagination = ref(false)
|
const allowPagination = ref(false)
|
||||||
@@ -48,23 +61,23 @@ const handleRightPage = () => {
|
|||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<p>{{ props.label }}</p>
|
<p>{{ props.label }}</p>
|
||||||
<hr aria-hidden="true"/>
|
<hr aria-hidden="true" />
|
||||||
<div v-if="allowPagination" class="pagination">
|
<div v-if="allowPagination" class="pagination">
|
||||||
<ChevronLeftIcon @click="handleLeftPage"/>
|
<ChevronLeftIcon @click="handleLeftPage" />
|
||||||
<ChevronRightIcon @click="handleRightPage"/>
|
<ChevronRightIcon @click="handleRightPage" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<section ref="modsRow" class="instances" v-if="shouldRenderNormalInstances">
|
<section v-if="shouldRenderNormalInstances" ref="modsRow" class="instances">
|
||||||
<Instance
|
<Instance
|
||||||
v-for="instance in props.instances"
|
v-for="instance in props.instances"
|
||||||
:key="instance.id"
|
:key="instance.id"
|
||||||
display="card"
|
display="card"
|
||||||
:instance="instance"
|
:instance="instance"
|
||||||
class="row-instance"
|
class="row-instance"
|
||||||
/>
|
/>
|
||||||
</section>
|
</section>
|
||||||
<section ref="newsRow" class="news" v-else-if="shouldRenderNews">
|
<section v-else-if="shouldRenderNews" ref="newsRow" class="news">
|
||||||
<News v-for="news in props.news" :key="news.id" :news="news"/>
|
<News v-for="actualNews in props.news" :key="actualNews.id" :news="actualNews" />
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -171,4 +184,4 @@ const handleRightPage = () => {
|
|||||||
min-width: 12rem;
|
min-width: 12rem;
|
||||||
max-width: 12rem;
|
max-width: 12rem;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -3,17 +3,32 @@ import { RouterLink } from 'vue-router'
|
|||||||
import { Card } from 'omorphia'
|
import { Card } from 'omorphia'
|
||||||
import { PlayIcon } from '@/assets/icons'
|
import { PlayIcon } from '@/assets/icons'
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
display: String,
|
display: {
|
||||||
instance: Object,
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
instance: {
|
||||||
|
type: Object,
|
||||||
|
default() {
|
||||||
|
return {}
|
||||||
|
},
|
||||||
|
},
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<RouterLink v-if="display === 'list'" class="instance-list-item" :to="`/instance/${props.instance.id}`">{{
|
<RouterLink
|
||||||
props.instance.name
|
v-if="display === 'list'"
|
||||||
}}</RouterLink>
|
class="instance-list-item"
|
||||||
<Card class="instance-card-item" v-else-if="display === 'card'" @click="this.$router.push(`/instance/${props.instance.id}`)">
|
:to="`/instance/${props.instance.id}`"
|
||||||
|
>{{ props.instance.name }}</RouterLink
|
||||||
|
>
|
||||||
|
<Card
|
||||||
|
v-else-if="display === 'card'"
|
||||||
|
class="instance-card-item"
|
||||||
|
@click="$router.push(`/instance/${props.instance.id}`)"
|
||||||
|
>
|
||||||
<img :src="props.instance.img" alt="Trending mod card" />
|
<img :src="props.instance.img" alt="Trending mod card" />
|
||||||
<div class="project-info">
|
<div class="project-info">
|
||||||
<p class="title">{{ props.instance.name }}</p>
|
<p class="title">{{ props.instance.name }}</p>
|
||||||
@@ -113,4 +128,4 @@ const props = defineProps({
|
|||||||
color: #000;
|
color: #000;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,7 +1,12 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { Card, ChevronRightIcon } from 'omorphia'
|
import { Card, ChevronRightIcon } from 'omorphia'
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
news: Object,
|
news: {
|
||||||
|
type: Object,
|
||||||
|
default() {
|
||||||
|
return {}
|
||||||
|
},
|
||||||
|
},
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -83,4 +88,4 @@ const props = defineProps({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -14,9 +14,9 @@ const popularInstances = instances.instances.filter((i) => i.downloads > 50 || i
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="page-container">
|
<div class="page-container">
|
||||||
<RowDisplay label="Jump back in" :instances="recentInstances" :canPaginate="false" />
|
<RowDisplay label="Jump back in" :instances="recentInstances" :can-paginate="false" />
|
||||||
<RowDisplay label="Popular packs" :instances="popularInstances" :canPaginate="true" />
|
<RowDisplay label="Popular packs" :instances="popularInstances" :can-paginate="true" />
|
||||||
<RowDisplay label="News & updates" :news="news.news" :canPaginate="true" />
|
<RowDisplay label="News & updates" :news="news.news" :can-paginate="true" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -2,9 +2,9 @@
|
|||||||
<div class="instance-container">
|
<div class="instance-container">
|
||||||
<div class="side-cards">
|
<div class="side-cards">
|
||||||
<Card class="instance-card">
|
<Card class="instance-card">
|
||||||
<Avatar size="lg" :src="getInstance(instances).img"/>
|
<Avatar size="lg" :src="getInstance(instances).img" />
|
||||||
<div class="instance-info">
|
<div class="instance-info">
|
||||||
<h2 class="name">{{getInstance(instances).name}}</h2>
|
<h2 class="name">{{ getInstance(instances).name }}</h2>
|
||||||
Fabric {{ getInstance(instances).version }}
|
Fabric {{ getInstance(instances).version }}
|
||||||
</div>
|
</div>
|
||||||
<span class="button-group">
|
<span class="button-group">
|
||||||
@@ -18,40 +18,40 @@
|
|||||||
</span>
|
</span>
|
||||||
</Card>
|
</Card>
|
||||||
<div class="pages-list">
|
<div class="pages-list">
|
||||||
<RouterLink :to="`/instance/${this.$route.params.id}/`" class="btn">
|
<RouterLink :to="`/instance/${$route.params.id}/`" class="btn">
|
||||||
<BoxIcon/>
|
<BoxIcon />
|
||||||
Mods
|
Mods
|
||||||
</RouterLink>
|
</RouterLink>
|
||||||
<RouterLink :to="`/instance/${this.$route.params.id}/options`" class="btn">
|
<RouterLink :to="`/instance/${$route.params.id}/options`" class="btn">
|
||||||
<SettingsIcon/>
|
<SettingsIcon />
|
||||||
Options
|
Options
|
||||||
</RouterLink>
|
</RouterLink>
|
||||||
<RouterLink :to="`/instance/${this.$route.params.id}/logs`" class="btn">
|
<RouterLink :to="`/instance/${$route.params.id}/logs`" class="btn">
|
||||||
<FileIcon/>
|
<FileIcon />
|
||||||
Logs
|
Logs
|
||||||
</RouterLink>
|
</RouterLink>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<Promotion />
|
<Promotion />
|
||||||
<router-view/>
|
<router-view />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script setup>
|
<script setup>
|
||||||
import {BoxIcon, SettingsIcon, FileIcon, Button, Avatar, Card, Promotion} from 'omorphia'
|
import { BoxIcon, SettingsIcon, FileIcon, Button, Avatar, Card, Promotion } from 'omorphia'
|
||||||
import {PlayIcon, OpenFolderIcon} from "@/assets/icons";
|
import { PlayIcon, OpenFolderIcon } from '@/assets/icons'
|
||||||
import {useInstances} from '@/store/state'
|
import { useInstances } from '@/store/state'
|
||||||
|
|
||||||
const instances = useInstances();
|
const instances = useInstances()
|
||||||
instances.fetchInstances();
|
instances.fetchInstances()
|
||||||
</script>
|
</script>
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
methods: {
|
methods: {
|
||||||
getInstance(instances) {
|
getInstance(instances) {
|
||||||
return instances.instances.find((i) => i.id === parseInt(this.$route.params.id));
|
return instances.instances.find((i) => i.id === parseInt(this.$route.params.id))
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
@@ -266,4 +266,4 @@ Button {
|
|||||||
height: 1px;
|
height: 1px;
|
||||||
margin: var(--gap-xl) 0;
|
margin: var(--gap-xl) 0;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -4,21 +4,21 @@
|
|||||||
<DropdownSelect :options="['logs/latest.log']" />
|
<DropdownSelect :options="['logs/latest.log']" />
|
||||||
<div class="button-group">
|
<div class="button-group">
|
||||||
<Button>
|
<Button>
|
||||||
<ClipboardCopyIcon/>
|
<ClipboardCopyIcon />
|
||||||
Copy
|
Copy
|
||||||
</Button>
|
</Button>
|
||||||
<Button color="primary">
|
<Button color="primary">
|
||||||
<SendIcon/>
|
<SendIcon />
|
||||||
Share
|
Share
|
||||||
</Button>
|
</Button>
|
||||||
<Button color="danger">
|
<Button color="danger">
|
||||||
<TrashIcon/>
|
<TrashIcon />
|
||||||
Delete
|
Delete
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="log-text">
|
<div class="log-text">
|
||||||
<div v-for="line in fileContents.value.split('\n')"> {{ line }} </div>
|
<div v-for="(line, index) in fileContents.value.split('\n')" :key="index">{{ line }}</div>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
</template>
|
</template>
|
||||||
@@ -27,66 +27,66 @@
|
|||||||
import { Card, Button, TrashIcon, SendIcon, ClipboardCopyIcon, DropdownSelect } from 'omorphia'
|
import { Card, Button, TrashIcon, SendIcon, ClipboardCopyIcon, DropdownSelect } from 'omorphia'
|
||||||
</script>
|
</script>
|
||||||
<script>
|
<script>
|
||||||
|
export default {
|
||||||
export default {
|
data() {
|
||||||
data() {
|
return {
|
||||||
return {
|
fileContents: {
|
||||||
fileContents: {
|
value:
|
||||||
value: "'ServerLevel[New World]'/minecraft:the_end\n" +
|
"'ServerLevel[New World]'/minecraft:the_end\n" +
|
||||||
"[22:13:02] [Server thread/INFO]: venashial lost connection: Disconnected\n" +
|
'[22:13:02] [Server thread/INFO]: venashial lost connection: Disconnected\n' +
|
||||||
"[22:13:02] [Server thread/INFO]: venashial left the game\n" +
|
'[22:13:02] [Server thread/INFO]: venashial left the game\n' +
|
||||||
"[22:13:02] [Server thread/INFO]: Stopping singleplayer server as player logged out\n" +
|
'[22:13:02] [Server thread/INFO]: Stopping singleplayer server as player logged out\n' +
|
||||||
"[22:13:02] [Server thread/INFO]: Stopping server\n" +
|
'[22:13:02] [Server thread/INFO]: Stopping server\n' +
|
||||||
"[22:13:02] [Server thread/INFO]: Saving players\n" +
|
'[22:13:02] [Server thread/INFO]: Saving players\n' +
|
||||||
"[22:13:02] [Server thread/INFO]: Saving worlds\n" +
|
'[22:13:02] [Server thread/INFO]: Saving worlds\n' +
|
||||||
"[22:13:02] [Server thread/INFO]: Saving chunks for level 'ServerLevel[New World]'/minecraft:overworld\n" +
|
"[22:13:02] [Server thread/INFO]: Saving chunks for level 'ServerLevel[New World]'/minecraft:overworld\n" +
|
||||||
"[22:13:05] [Server thread/INFO]: Saving chunks for level 'ServerLevel[New World]'/minecraft:the_nether\n" +
|
"[22:13:05] [Server thread/INFO]: Saving chunks for level 'ServerLevel[New World]'/minecraft:the_nether\n" +
|
||||||
"[22:13:05] [Server thread/INFO]: Saving chunks for level 'ServerLevel[New World]'/minecraft:the_end\n" +
|
"[22:13:05] [Server thread/INFO]: Saving chunks for level 'ServerLevel[New World]'/minecraft:the_end\n" +
|
||||||
"[22:13:05] [Server thread/INFO]: ThreadedAnvilChunkStorage (New World): All chunks are saved\n" +
|
'[22:13:05] [Server thread/INFO]: ThreadedAnvilChunkStorage (New World): All chunks are saved\n' +
|
||||||
"[22:13:05] [Server thread/INFO]: ThreadedAnvilChunkStorage (DIM-1): All chunks are saved\n" +
|
'[22:13:05] [Server thread/INFO]: ThreadedAnvilChunkStorage (DIM-1): All chunks are saved\n' +
|
||||||
"[22:13:05] [Server thread/INFO]: ThreadedAnvilChunkStorage (DIM1): All chunks are saved\n" +
|
'[22:13:05] [Server thread/INFO]: ThreadedAnvilChunkStorage (DIM1): All chunks are saved\n' +
|
||||||
"[22:13:05] [Server thread/INFO]: ThreadedAnvilChunkStorage: All dimensions are saved\n" +
|
'[22:13:05] [Server thread/INFO]: ThreadedAnvilChunkStorage: All dimensions are saved\n' +
|
||||||
"[22:13:06] [Render thread/INFO]: Stopping worker threads\n" +
|
'[22:13:06] [Render thread/INFO]: Stopping worker threads\n' +
|
||||||
"[22:13:07] [Render thread/INFO]: Stopping!\n" +
|
'[22:13:07] [Render thread/INFO]: Stopping!\n' +
|
||||||
"[22:13:07] [CraftPresence-ShutDown-Handler/INFO]: Shutting down CraftPresence...\n" +
|
'[22:13:07] [CraftPresence-ShutDown-Handler/INFO]: Shutting down CraftPresence...\n' +
|
||||||
"'ServerLevel[New World]'/minecraft:the_end\n" +
|
"'ServerLevel[New World]'/minecraft:the_end\n" +
|
||||||
"[22:13:02] [Server thread/INFO]: venashial lost connection: Disconnected\n" +
|
'[22:13:02] [Server thread/INFO]: venashial lost connection: Disconnected\n' +
|
||||||
"[22:13:02] [Server thread/INFO]: venashial left the game\n" +
|
'[22:13:02] [Server thread/INFO]: venashial left the game\n' +
|
||||||
"[22:13:02] [Server thread/INFO]: Stopping singleplayer server as player logged out\n" +
|
'[22:13:02] [Server thread/INFO]: Stopping singleplayer server as player logged out\n' +
|
||||||
"[22:13:02] [Server thread/INFO]: Stopping server\n" +
|
'[22:13:02] [Server thread/INFO]: Stopping server\n' +
|
||||||
"[22:13:02] [Server thread/INFO]: Saving players\n" +
|
'[22:13:02] [Server thread/INFO]: Saving players\n' +
|
||||||
"[22:13:02] [Server thread/INFO]: Saving worlds\n" +
|
'[22:13:02] [Server thread/INFO]: Saving worlds\n' +
|
||||||
"[22:13:02] [Server thread/INFO]: Saving chunks for level 'ServerLevel[New World]'/minecraft:overworld\n" +
|
"[22:13:02] [Server thread/INFO]: Saving chunks for level 'ServerLevel[New World]'/minecraft:overworld\n" +
|
||||||
"[22:13:05] [Server thread/INFO]: Saving chunks for level 'ServerLevel[New World]'/minecraft:the_nether\n" +
|
"[22:13:05] [Server thread/INFO]: Saving chunks for level 'ServerLevel[New World]'/minecraft:the_nether\n" +
|
||||||
"[22:13:05] [Server thread/INFO]: Saving chunks for level 'ServerLevel[New World]'/minecraft:the_end\n" +
|
"[22:13:05] [Server thread/INFO]: Saving chunks for level 'ServerLevel[New World]'/minecraft:the_end\n" +
|
||||||
"[22:13:05] [Server thread/INFO]: ThreadedAnvilChunkStorage (New World): All chunks are saved\n" +
|
'[22:13:05] [Server thread/INFO]: ThreadedAnvilChunkStorage (New World): All chunks are saved\n' +
|
||||||
"[22:13:05] [Server thread/INFO]: ThreadedAnvilChunkStorage (DIM-1): All chunks are saved\n" +
|
'[22:13:05] [Server thread/INFO]: ThreadedAnvilChunkStorage (DIM-1): All chunks are saved\n' +
|
||||||
"[22:13:05] [Server thread/INFO]: ThreadedAnvilChunkStorage (DIM1): All chunks are saved\n" +
|
'[22:13:05] [Server thread/INFO]: ThreadedAnvilChunkStorage (DIM1): All chunks are saved\n' +
|
||||||
"[22:13:05] [Server thread/INFO]: ThreadedAnvilChunkStorage: All dimensions are saved\n" +
|
'[22:13:05] [Server thread/INFO]: ThreadedAnvilChunkStorage: All dimensions are saved\n' +
|
||||||
"[22:13:06] [Render thread/INFO]: Stopping worker threads\n" +
|
'[22:13:06] [Render thread/INFO]: Stopping worker threads\n' +
|
||||||
"[22:13:07] [Render thread/INFO]: Stopping!\n" +
|
'[22:13:07] [Render thread/INFO]: Stopping!\n' +
|
||||||
"[22:13:07] [CraftPresence-ShutDown-Handler/INFO]: Shutting down CraftPresence...\n" +
|
'[22:13:07] [CraftPresence-ShutDown-Handler/INFO]: Shutting down CraftPresence...\n' +
|
||||||
"'ServerLevel[New World]'/minecraft:the_end\n" +
|
"'ServerLevel[New World]'/minecraft:the_end\n" +
|
||||||
"[22:13:02] [Server thread/INFO]: venashial lost connection: Disconnected\n" +
|
'[22:13:02] [Server thread/INFO]: venashial lost connection: Disconnected\n' +
|
||||||
"[22:13:02] [Server thread/INFO]: venashial left the game\n" +
|
'[22:13:02] [Server thread/INFO]: venashial left the game\n' +
|
||||||
"[22:13:02] [Server thread/INFO]: Stopping singleplayer server as player logged out\n" +
|
'[22:13:02] [Server thread/INFO]: Stopping singleplayer server as player logged out\n' +
|
||||||
"[22:13:02] [Server thread/INFO]: Stopping server\n" +
|
'[22:13:02] [Server thread/INFO]: Stopping server\n' +
|
||||||
"[22:13:02] [Server thread/INFO]: Saving players\n" +
|
'[22:13:02] [Server thread/INFO]: Saving players\n' +
|
||||||
"[22:13:02] [Server thread/INFO]: Saving worlds\n" +
|
'[22:13:02] [Server thread/INFO]: Saving worlds\n' +
|
||||||
"[22:13:02] [Server thread/INFO]: Saving chunks for level 'ServerLevel[New World]'/minecraft:overworld\n" +
|
"[22:13:02] [Server thread/INFO]: Saving chunks for level 'ServerLevel[New World]'/minecraft:overworld\n" +
|
||||||
"[22:13:05] [Server thread/INFO]: Saving chunks for level 'ServerLevel[New World]'/minecraft:the_nether\n" +
|
"[22:13:05] [Server thread/INFO]: Saving chunks for level 'ServerLevel[New World]'/minecraft:the_nether\n" +
|
||||||
"[22:13:05] [Server thread/INFO]: Saving chunks for level 'ServerLevel[New World]'/minecraft:the_end\n" +
|
"[22:13:05] [Server thread/INFO]: Saving chunks for level 'ServerLevel[New World]'/minecraft:the_end\n" +
|
||||||
"[22:13:05] [Server thread/INFO]: ThreadedAnvilChunkStorage (New World): All chunks are saved\n" +
|
'[22:13:05] [Server thread/INFO]: ThreadedAnvilChunkStorage (New World): All chunks are saved\n' +
|
||||||
"[22:13:05] [Server thread/INFO]: ThreadedAnvilChunkStorage (DIM-1): All chunks are saved\n" +
|
'[22:13:05] [Server thread/INFO]: ThreadedAnvilChunkStorage (DIM-1): All chunks are saved\n' +
|
||||||
"[22:13:05] [Server thread/INFO]: ThreadedAnvilChunkStorage (DIM1): All chunks are saved\n" +
|
'[22:13:05] [Server thread/INFO]: ThreadedAnvilChunkStorage (DIM1): All chunks are saved\n' +
|
||||||
"[22:13:05] [Server thread/INFO]: ThreadedAnvilChunkStorage: All dimensions are saved\n" +
|
'[22:13:05] [Server thread/INFO]: ThreadedAnvilChunkStorage: All dimensions are saved\n' +
|
||||||
"[22:13:06] [Render thread/INFO]: Stopping worker threads\n" +
|
'[22:13:06] [Render thread/INFO]: Stopping worker threads\n' +
|
||||||
"[22:13:07] [Render thread/INFO]: Stopping!\n" +
|
'[22:13:07] [Render thread/INFO]: Stopping!\n' +
|
||||||
"[22:13:07] [CraftPresence-ShutDown-Handler/INFO]: Shutting down CraftPresence..."
|
'[22:13:07] [CraftPresence-ShutDown-Handler/INFO]: Shutting down CraftPresence...',
|
||||||
}
|
},
|
||||||
};
|
|
||||||
}
|
}
|
||||||
};
|
},
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@@ -118,4 +118,4 @@ import { Card, Button, TrashIcon, SendIcon, ClipboardCopyIcon, DropdownSelect }
|
|||||||
overflow: auto;
|
overflow: auto;
|
||||||
white-space: normal;
|
white-space: normal;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -2,63 +2,59 @@
|
|||||||
<Card class="mod-card">
|
<Card class="mod-card">
|
||||||
<div class="card-row">
|
<div class="card-row">
|
||||||
<div class="iconified-input">
|
<div class="iconified-input">
|
||||||
<SearchIcon/>
|
<SearchIcon />
|
||||||
<input
|
<input v-model="searchFilter" type="text" placeholder="Search Mods" />
|
||||||
type="text"
|
|
||||||
placeholder="Search Mods"
|
|
||||||
v-model="searchFilter"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<span class="manage">
|
<span class="manage">
|
||||||
<span class="text-combo">
|
<span class="text-combo">
|
||||||
Sort By
|
Sort By
|
||||||
<DropdownSelect :options="['Name', 'Version', 'Author']" v-model="sortFilter" default-value="Name" class="dropdown"/>
|
<DropdownSelect
|
||||||
|
v-model="sortFilter"
|
||||||
|
:options="['Name', 'Version', 'Author']"
|
||||||
|
default-value="Name"
|
||||||
|
class="dropdown"
|
||||||
|
/>
|
||||||
</span>
|
</span>
|
||||||
<Button color="primary">
|
<Button color="primary">
|
||||||
<PlusIcon />
|
<PlusIcon />
|
||||||
Add Mods
|
Add Mods
|
||||||
</Button>
|
</Button>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="table-container">
|
<div class="table-container">
|
||||||
<div class="table-row table-head">
|
<div class="table-row table-head">
|
||||||
<div class="table-cell table-text">
|
<div class="table-cell table-text">
|
||||||
<Button color="success" iconOnly>
|
<Button color="success" icon-only>
|
||||||
<UpdatedIcon />
|
<UpdatedIcon />
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<div class="table-cell table-text name-cell"> Name </div>
|
<div class="table-cell table-text name-cell">Name</div>
|
||||||
<div class="table-cell table-text"> Version </div>
|
<div class="table-cell table-text">Version</div>
|
||||||
<div class="table-cell table-text"> Author </div>
|
<div class="table-cell table-text">Author</div>
|
||||||
<div class="table-cell table-text"> Actions </div>
|
<div class="table-cell table-text">Actions</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="table-row" v-for="mod in search" :key="mod.name">
|
<div v-for="mod in search" :key="mod.name" class="table-row">
|
||||||
<div class="table-cell table-text">
|
<div class="table-cell table-text">
|
||||||
<Button v-if="mod.outdated" iconOnly>
|
<Button v-if="mod.outdated" icon-only>
|
||||||
<UpdatedIcon />
|
<UpdatedIcon />
|
||||||
</Button>
|
</Button>
|
||||||
<Button v-else disabled iconOnly>
|
<Button v-else disabled icon-only>
|
||||||
<CheckCircleIcon/>
|
<CheckCircleIcon />
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<div class="table-cell table-text name-cell">
|
<div class="table-cell table-text name-cell">
|
||||||
<span class="mod-text">
|
<span class="mod-text">
|
||||||
<Avatar :src="mod.icon"/>
|
<Avatar :src="mod.icon" />
|
||||||
{{ mod.name }}
|
{{ mod.name }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="table-cell table-text"> {{ mod.version }} </div>
|
<div class="table-cell table-text">{{ mod.version }}</div>
|
||||||
<div class="table-cell table-text"> {{ mod.author }} </div>
|
<div class="table-cell table-text">{{ mod.author }}</div>
|
||||||
<div class="table-cell table-text manage">
|
<div class="table-cell table-text manage">
|
||||||
<Button iconOnly>
|
<Button icon-only>
|
||||||
<TrashIcon />
|
<TrashIcon />
|
||||||
</Button>
|
</Button>
|
||||||
<input
|
<input id="switch-1" type="checkbox" class="switch stylized-toggle" checked />
|
||||||
type="checkbox"
|
|
||||||
class="switch stylized-toggle"
|
|
||||||
id="switch-1"
|
|
||||||
checked
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -67,55 +63,66 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
name: "Mods",
|
name: 'Mods',
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
searchFilter: "",
|
searchFilter: '',
|
||||||
sortFilter: "",
|
sortFilter: '',
|
||||||
mods: [
|
mods: [
|
||||||
{
|
{
|
||||||
name: "Fabric API",
|
name: 'Fabric API',
|
||||||
icon: "https://cdn.modrinth.com/data/P7dR8mSH/icon.png",
|
icon: 'https://cdn.modrinth.com/data/P7dR8mSH/icon.png',
|
||||||
version: "0.76.0+1.19.4",
|
version: '0.76.0+1.19.4',
|
||||||
author: "modmuss50",
|
author: 'modmuss50',
|
||||||
description: "Lightweight and modular API providing common hooks and intercompatibility measures utilized by mods using the Fabric toolchain.",
|
description:
|
||||||
outdated: true
|
'Lightweight and modular API providing common hooks and intercompatibility measures utilized by mods using the Fabric toolchain.',
|
||||||
|
outdated: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Spirit",
|
name: 'Spirit',
|
||||||
icon: "https://cdn.modrinth.com/data/b1LdOZlE/465598dc5d89f67fb8f8de6def21240fa35e3a54.png",
|
icon: 'https://cdn.modrinth.com/data/b1LdOZlE/465598dc5d89f67fb8f8de6def21240fa35e3a54.png',
|
||||||
version: "2.2.4",
|
version: '2.2.4',
|
||||||
author: "CodexAdrian",
|
author: 'CodexAdrian',
|
||||||
description: "Create your own configurable mob spawner!",
|
description: 'Create your own configurable mob spawner!',
|
||||||
outdated: true
|
outdated: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Botarium",
|
name: 'Botarium',
|
||||||
icon: "https://cdn.modrinth.com/data/2u6LRnMa/98b286b0d541ad4f9409e0af3df82ad09403f179.gif",
|
icon: 'https://cdn.modrinth.com/data/2u6LRnMa/98b286b0d541ad4f9409e0af3df82ad09403f179.gif',
|
||||||
version: "2.0.5",
|
version: '2.0.5',
|
||||||
author: "CodexAdrian",
|
author: 'CodexAdrian',
|
||||||
description: "A crossplatform API for devs that makes transfer and storage of items, fluids and energy easier, as well as some other helpful things",
|
description:
|
||||||
outdated: true
|
'A crossplatform API for devs that makes transfer and storage of items, fluids and energy easier, as well as some other helpful things',
|
||||||
|
outdated: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Tempad",
|
name: 'Tempad',
|
||||||
icon: "https://cdn.modrinth.com/data/gKNwt7xu/icon.gif",
|
icon: 'https://cdn.modrinth.com/data/gKNwt7xu/icon.gif',
|
||||||
version: "2.2.4",
|
version: '2.2.4',
|
||||||
author: "CodexAdrian",
|
author: 'CodexAdrian',
|
||||||
description: "Create a portal to anywhere from anywhere",
|
description: 'Create a portal to anywhere from anywhere',
|
||||||
outdated: false
|
outdated: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Sodium",
|
name: 'Sodium',
|
||||||
icon: "https://cdn.modrinth.com/data/AANobbMI/icon.png",
|
icon: 'https://cdn.modrinth.com/data/AANobbMI/icon.png',
|
||||||
version: "0.4.10",
|
version: '0.4.10',
|
||||||
author: "jellysquid3",
|
author: 'jellysquid3',
|
||||||
description: "Modern rendering engine and client-side optimization mod for Minecraft",
|
description: 'Modern rendering engine and client-side optimization mod for Minecraft',
|
||||||
outdated: false
|
outdated: false,
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
computed: {
|
||||||
|
search() {
|
||||||
|
const filtered = this.mods.filter((mod) => {
|
||||||
|
return mod.name.toLowerCase().includes(this.searchFilter.toLowerCase())
|
||||||
|
})
|
||||||
|
|
||||||
|
return this.updateSort(filtered, this.sortFilter)
|
||||||
|
},
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
updateSort(projects, sort) {
|
updateSort(projects, sort) {
|
||||||
switch (sort) {
|
switch (sort) {
|
||||||
@@ -148,23 +155,24 @@ export default {
|
|||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
return 0
|
return 0
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
computed: {
|
|
||||||
search() {
|
|
||||||
const filtered = this.mods.filter((mod) => {
|
|
||||||
return mod.name.toLowerCase().includes(this.searchFilter.toLowerCase())
|
|
||||||
})
|
|
||||||
|
|
||||||
return this.updateSort(filtered, this.sortFilter);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<script setup>
|
<script setup>
|
||||||
import { Avatar, Button, TrashIcon, PlusIcon, Card, CheckCircleIcon, SearchIcon, UpdatedIcon, DropdownSelect } from 'omorphia'
|
import {
|
||||||
|
Avatar,
|
||||||
|
Button,
|
||||||
|
TrashIcon,
|
||||||
|
PlusIcon,
|
||||||
|
Card,
|
||||||
|
CheckCircleIcon,
|
||||||
|
SearchIcon,
|
||||||
|
UpdatedIcon,
|
||||||
|
DropdownSelect,
|
||||||
|
} from 'omorphia'
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@@ -244,4 +252,4 @@ import { Avatar, Button, TrashIcon, PlusIcon, Card, CheckCircleIcon, SearchIcon,
|
|||||||
.dropdown {
|
.dropdown {
|
||||||
width: 7rem !important;
|
width: 7rem !important;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,172 +1,132 @@
|
|||||||
<template>
|
<template>
|
||||||
<Card class="settings-card">
|
<Card class="settings-card">
|
||||||
<h2 class="settings-title"> Java </h2>
|
<h2 class="settings-title">Java</h2>
|
||||||
<div class="settings-group">
|
<div class="settings-group">
|
||||||
<h3>Installation</h3>
|
<h3>Installation</h3>
|
||||||
<input
|
<input
|
||||||
type="text"
|
ref="javaPath"
|
||||||
class="input installation-input"
|
v-model="javaPath"
|
||||||
placeholder="/Library/Java/JavaVirtualMachines/zulu-17.jdk/Contents/Home"
|
type="text"
|
||||||
ref="javaPath"
|
class="input installation-input"
|
||||||
v-model="javaPath"
|
placeholder="/Library/Java/JavaVirtualMachines/zulu-17.jdk/Contents/Home"
|
||||||
/>
|
/>
|
||||||
<span class="installation-buttons">
|
<span class="installation-buttons">
|
||||||
<Button @click="saveJavaPath">
|
<Button @click="saveJavaPath">
|
||||||
<SearchIcon/>
|
<SearchIcon />
|
||||||
Auto Detect
|
Auto Detect
|
||||||
</Button>
|
</Button>
|
||||||
<Button @click="saveJavaPath">
|
<Button @click="saveJavaPath">
|
||||||
<BrowseIcon/>
|
<BrowseIcon />
|
||||||
Browse
|
Browse
|
||||||
</Button>
|
</Button>
|
||||||
<Button @click="saveJavaPath">
|
<Button @click="saveJavaPath">
|
||||||
<PlayIcon/>
|
<PlayIcon />
|
||||||
Test
|
Test
|
||||||
</Button>
|
</Button>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<hr class="card-divider">
|
<hr class="card-divider" />
|
||||||
<div class="settings-group">
|
<div class="settings-group">
|
||||||
<h3>Arguments</h3>
|
<h3>Arguments</h3>
|
||||||
<input
|
<input ref="javaArgs" v-model="javaArgs" type="text" class="input installation-input" />
|
||||||
type="text"
|
|
||||||
class="input installation-input"
|
|
||||||
ref="javaArgs"
|
|
||||||
v-model="javaArgs"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<hr class="card-divider">
|
<hr class="card-divider" />
|
||||||
<div class="settings-group">
|
<div class="settings-group">
|
||||||
<div class="sliders">
|
<div class="sliders">
|
||||||
<span class="slider">
|
|
||||||
Minimum Memory
|
|
||||||
<Slider
|
|
||||||
v-model="javaMemory"
|
|
||||||
:min="1024"
|
|
||||||
:max="8192"
|
|
||||||
:step="1024"
|
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
<span class="slider">
|
<span class="slider">
|
||||||
Maximum Memory
|
Minimum Memory
|
||||||
<Slider
|
<Slider v-model="javaMemory" :min="1024" :max="8192" :step="1024" />
|
||||||
v-model="javaMemory"
|
</span>
|
||||||
:min="1024"
|
<span class="slider">
|
||||||
:max="8192"
|
Maximum Memory
|
||||||
:step="1024"
|
<Slider v-model="javaMemory" :min="1024" :max="8192" :step="1024" />
|
||||||
/>
|
</span>
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
<Card class="settings-card">
|
<Card class="settings-card">
|
||||||
<h2 class="settings-title"> Window </h2>
|
<h2 class="settings-title">Window</h2>
|
||||||
<div class="settings-group">
|
<div class="settings-group">
|
||||||
<div class="settings-group">
|
<div class="settings-group">
|
||||||
<div class="sliders">
|
<div class="sliders">
|
||||||
<span class="slider">
|
<span class="slider">
|
||||||
Width
|
Width
|
||||||
<Slider
|
<Slider v-model="javaMemory" :min="1024" :max="8192" :step="1024" />
|
||||||
v-model="javaMemory"
|
|
||||||
:min="1024"
|
|
||||||
:max="8192"
|
|
||||||
:step="1024"
|
|
||||||
/>
|
|
||||||
</span>
|
</span>
|
||||||
<span class="slider">
|
<span class="slider">
|
||||||
Height
|
Height
|
||||||
<Slider
|
<Slider v-model="javaMemory" :min="1024" :max="8192" :step="1024" />
|
||||||
v-model="javaMemory"
|
|
||||||
:min="1024"
|
|
||||||
:max="8192"
|
|
||||||
:step="1024"
|
|
||||||
/>
|
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="toggle-setting">
|
<div class="toggle-setting">
|
||||||
Start in Fullscreen
|
Start in Fullscreen
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
id="fullscreen"
|
||||||
id="fullscreen"
|
v-model="fullscreen"
|
||||||
name="fullscreen"
|
type="checkbox"
|
||||||
v-model="fullscreen"
|
name="fullscreen"
|
||||||
class="switch stylized-toggle"
|
class="switch stylized-toggle"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<hr class="card-divider">
|
<hr class="card-divider" />
|
||||||
<div class="settings-group">
|
<div class="settings-group">
|
||||||
<h3>Console</h3>
|
<h3>Console</h3>
|
||||||
<div class="toggle-setting">
|
<div class="toggle-setting">
|
||||||
Show console while game is running
|
Show console while game is running
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
id="fullscreen"
|
||||||
id="fullscreen"
|
v-model="fullscreen"
|
||||||
name="fullscreen"
|
type="checkbox"
|
||||||
v-model="fullscreen"
|
name="fullscreen"
|
||||||
class="switch stylized-toggle"
|
class="switch stylized-toggle"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="toggle-setting">
|
<div class="toggle-setting">
|
||||||
Close console when game quits
|
Close console when game quits
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
id="fullscreen"
|
||||||
id="fullscreen"
|
v-model="fullscreen"
|
||||||
name="fullscreen"
|
type="checkbox"
|
||||||
v-model="fullscreen"
|
name="fullscreen"
|
||||||
class="switch stylized-toggle"
|
class="switch stylized-toggle"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="toggle-setting">
|
<div class="toggle-setting">
|
||||||
Show console when game crashes
|
Show console when game crashes
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
id="fullscreen"
|
||||||
id="fullscreen"
|
v-model="fullscreen"
|
||||||
name="fullscreen"
|
type="checkbox"
|
||||||
v-model="fullscreen"
|
name="fullscreen"
|
||||||
class="switch stylized-toggle"
|
class="switch stylized-toggle"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
<Card class="settings-card">
|
<Card class="settings-card">
|
||||||
<h2 class="settings-title"> Commands </h2>
|
<h2 class="settings-title">Commands</h2>
|
||||||
<div class="settings-group">
|
<div class="settings-group">
|
||||||
<div class="toggle-setting">
|
<div class="toggle-setting">
|
||||||
Pre Launch
|
Pre Launch
|
||||||
<input
|
<input ref="javaArgs" v-model="javaArgs" type="text" class="input" />
|
||||||
type="text"
|
|
||||||
class="input"
|
|
||||||
ref="javaArgs"
|
|
||||||
v-model="javaArgs"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="toggle-setting">
|
<div class="toggle-setting">
|
||||||
Wrapper
|
Wrapper
|
||||||
<input
|
<input ref="javaArgs" v-model="javaArgs" type="text" class="input" />
|
||||||
type="text"
|
|
||||||
class="input"
|
|
||||||
ref="javaArgs"
|
|
||||||
v-model="javaArgs"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="toggle-setting">
|
<div class="toggle-setting">
|
||||||
Post Launch
|
Post Launch
|
||||||
<input
|
<input ref="javaArgs" v-model="javaArgs" type="text" class="input" />
|
||||||
type="text"
|
|
||||||
class="input"
|
|
||||||
ref="javaArgs"
|
|
||||||
v-model="javaArgs"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import {Card, Button, SearchIcon, Slider } from 'omorphia'
|
import { Card, Button, SearchIcon, Slider } from 'omorphia'
|
||||||
import {BrowseIcon, PlayIcon} from "@/assets/icons";
|
import { BrowseIcon, PlayIcon } from '@/assets/icons'
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@@ -177,7 +137,7 @@ import {BrowseIcon, PlayIcon} from "@/assets/icons";
|
|||||||
}
|
}
|
||||||
|
|
||||||
.settings-title {
|
.settings-title {
|
||||||
color: var(--color-contrast)
|
color: var(--color-contrast);
|
||||||
}
|
}
|
||||||
|
|
||||||
.settings-group {
|
.settings-group {
|
||||||
@@ -195,9 +155,8 @@ import {BrowseIcon, PlayIcon} from "@/assets/icons";
|
|||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: .5rem;
|
gap: 0.5rem;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.sliders {
|
.sliders {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import Index from './Index.vue'
|
import Index from './Index.vue'
|
||||||
import Mods from "./Mods.vue";
|
import Mods from './Mods.vue'
|
||||||
import Options from "./Options.vue";
|
import Options from './Options.vue'
|
||||||
import Logs from "./Logs.vue";
|
import Logs from './Logs.vue'
|
||||||
|
|
||||||
export { Index, Mods, Options, Logs }
|
export { Index, Mods, Options, Logs }
|
||||||
|
|||||||
@@ -45,9 +45,9 @@ export default new createRouter({
|
|||||||
props: true,
|
props: true,
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: '',
|
path: '',
|
||||||
name: 'Mods',
|
name: 'Mods',
|
||||||
component: Instance.Mods,
|
component: Instance.Mods,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'options',
|
path: 'options',
|
||||||
@@ -58,9 +58,9 @@ export default new createRouter({
|
|||||||
path: 'logs',
|
path: 'logs',
|
||||||
name: 'Logs',
|
name: 'Logs',
|
||||||
component: Instance.Logs,
|
component: Instance.Logs,
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
linkActiveClass: 'router-link-active',
|
linkActiveClass: 'router-link-active',
|
||||||
linkExactActiveClass: 'router-link-exact-active',
|
linkExactActiveClass: 'router-link-exact-active',
|
||||||
|
|||||||
@@ -2,7 +2,8 @@ import { defineConfig } from 'vite'
|
|||||||
import vue from '@vitejs/plugin-vue'
|
import vue from '@vitejs/plugin-vue'
|
||||||
import alias from '@rollup/plugin-alias'
|
import alias from '@rollup/plugin-alias'
|
||||||
import { resolve } from 'path'
|
import { resolve } from 'path'
|
||||||
import svgLoader from "vite-svg-loader";
|
import eslint from 'vite-plugin-eslint'
|
||||||
|
import svgLoader from 'vite-svg-loader'
|
||||||
|
|
||||||
const projectRootDir = resolve(__dirname)
|
const projectRootDir = resolve(__dirname)
|
||||||
|
|
||||||
@@ -18,6 +19,7 @@ export default defineConfig({
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
|
eslint(),
|
||||||
svgLoader({
|
svgLoader({
|
||||||
svgoConfig: {
|
svgoConfig: {
|
||||||
plugins: [
|
plugins: [
|
||||||
|
|||||||
@@ -1315,7 +1315,7 @@ prelude-ls@^1.2.1:
|
|||||||
resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396"
|
resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396"
|
||||||
integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==
|
integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==
|
||||||
|
|
||||||
prettier@^2.8.4:
|
prettier@^2.8.7:
|
||||||
version "2.8.7"
|
version "2.8.7"
|
||||||
resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.7.tgz#bb79fc8729308549d28fe3a98fce73d2c0656450"
|
resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.7.tgz#bb79fc8729308549d28fe3a98fce73d2c0656450"
|
||||||
integrity sha512-yPngTo3aXUUmyuTjeTUT75txrf+aMh9FiD7q9ZE/i6r0bPb22g4FsE6Y338PQX1bmfy08i9QQCB7/rcUAVntfw==
|
integrity sha512-yPngTo3aXUUmyuTjeTUT75txrf+aMh9FiD7q9ZE/i6r0bPb22g4FsE6Y338PQX1bmfy08i9QQCB7/rcUAVntfw==
|
||||||
|
|||||||