8 Commits

Author SHA1 Message Date
2d5d747202 update readme markdown files, added RUS language 2025-07-16 13:49:50 +03:00
7516ff9e47 Merge pull request 'feature-elyby-skins' (#8) from feature-elyby-skins into beta
Reviewed-on: #8
2025-07-16 13:13:03 +03:00
df9bbe3ba0 chore: add patch file to patches directory 2025-07-16 02:31:59 +03:00
362fd7f32a feat: Implement Ely By skin system
Some checks failed
AstralRinth App build / Build (x86_64-unknown-linux-gnu, ubuntu-latest) (push) Successful in 44m33s
AstralRinth App build / Build (x86_64-pc-windows-msvc, windows-latest) (push) Has been cancelled
2025-07-16 02:27:48 +03:00
adf831dab9 Merge commit 'efeac22d14fb6e782869d56d5d65685667721e4a' into feature-elyby-skins
Some checks failed
AstralRinth App build / Build (x86_64-unknown-linux-gnu, ubuntu-latest) (push) Failing after 3m24s
AstralRinth App build / Build (x86_64-pc-windows-msvc, windows-latest) (push) Has been cancelled
2025-07-11 04:41:14 +03:00
14f6450cf4 Merge commit '14bf06e4bd7a70a2c6483e6bbb9712cb9ff84486' into feature-elyby-skins
Some checks failed
AstralRinth App build / Build (x86_64-unknown-linux-gnu, ubuntu-latest) (push) Has been cancelled
2025-07-10 01:07:43 +03:00
97bd18c7b3 Merge commit '8af0288274bedbd78db03530b10aca0c5a3f13f1' into feature-elyby-skins 2025-07-10 00:01:12 +03:00
34d85a03b2 Merge commit '17cf5e31321ef9c3a4f95489eb18d33818fb2090' into feature-elyby-skins
All checks were successful
AstralRinth App build / Build (x86_64-unknown-linux-gnu, ubuntu-latest) (push) Successful in 35m22s
2025-07-09 22:58:27 +03:00
13 changed files with 1354 additions and 79 deletions

159
README.md
View File

@@ -1,76 +1,123 @@
# Navigation in this README
- [Install instructions](#install-instructions)
- [Features](#features)
- [Getting started](#getting-started)
- [Disclaimer](#disclaimer)
- [Donate](#support-our-project-crypto-wallets)
# 📘 Navigation
- [🔧 Install Instructions](#install-instructions)
- [✨ Features](#features)
- [🚀 Getting Started](#getting-started)
- [⚠️ Disclaimer](#disclaimer)
- [💰 Donate](#support-our-project-crypto-wallets)
## Other languages
> [Русский](readme/ru_ru/README.md)
## Support channel
> [Telegram](https://me.astralium.su/ref/telegram_channel)
---
# About Project
## AstralRinth • Empowering Your Minecraft Adventure
Welcome to AR • Fork of Modrinth, the ultimate game launcher designed to enhance your Minecraft experience through the Modrinth platform and their API. Whether you're a graphical interface enthusiast, or a developer integrating Modrinth projects, Theseus core is your gateway to a new level of Minecraft gaming.
## **AstralRinth • Empowering Your Minecraft Adventure**
## About Software
Introducing AstralRinth, a specialized variant of Theseus dedicated to implementing offline authorization for an even more flexible and user-centric Minecraft Modrinth experience. Roam the Minecraft realms without the constraints of online authentication, thanks to AstralRinth.
Welcome to **AstralRinth (AR)** — a powerful fork of Modrinth, reimagined to enhance your Minecraft journey. Whether you're a GUI enthusiast or a developer building with Modrinths API, **Theseus Core** is your launchpad into a new era of Minecraft gameplay.
## AR • Unlocking Minecraft's Boundless Horizon
Dive into the extraordinary world of AstralRinth, a fork of the original project with a unique focus on providing a free trial experience for Minecraft, all without the need for a license. Currently boasting:
- *Recently, improved integration with the Git Astralium API has been added.*
# Install instructions
- To install our application, you need to download a file for your operating system from our available releases or development builds • [Download variants here](https://git.astralium.su/didirus/AstralRinth/releases)
- After you have downloaded the required executable file or archive, then open it
## **About the Software**
### Downloadable file extensions
- `.msi` format for Windows OS system _(Supported popular latest versions of Microsoft Windows)_
- `.dmg` format for MacOS system _(Works on Macos Ventura / Sonoma / Sequoia, but it should be works on older OS builds)_
- `.deb` format for Linux OS systems _(Since there are quite a few distributions, we do not guarantee
**AstralRinth** is a dedicated branch of the Theseus project, focused on **offline authentication**, offering you more flexibility and control. Play Minecraft without the need for constant online verification — a user-first approach to modern modded gaming.
### Installation subjects
- Builds in releases that are signed with the following prefixes are not recommended for installation and may contain errors:
- `dev`
- `nightly`
- `dirty`
- `dirty-dev`
- `dirty-nightly`
- `dirty_dev`
- `dirty_nightly`
- Auto-updating takes place through parsing special versions from releases, so we also distribute clean types of `.msi, .dmg and .deb`
## **AR • Unlocking Minecraft's Boundless Horizon**
This unique fork introduces a **free trial Minecraft experience**, bypassing license checks while maintaining rich functionality. Currently includes:
---
# Install Instructions
To install the launcher:
1. Visit the [releases page](https://git.astralium.su/didirus/AstralRinth/releases) to download the correct version for your system.
2. Run the downloaded file or extract and launch it, depending on the format.
### Downloadable File Extensions
| Extension | OS | Notes |
| --------- | ------- | --------------------------------------------------------------------- |
| `.msi` | Windows | Supported on all recent Windows versions |
| `.dmg` | macOS | Works on Ventura, Sonoma, Sequoia _(may also support older versions)_ |
| `.deb` | Linux | Basic support; compatibility may vary by distribution |
### Installation Warnings
Avoid using builds with these prefixes — they may be unstable or experimental:
- `dev`
- `nightly`
- `dirty`
- `dirty-dev`
- `dirty-nightly`
- `dirty_dev`
- `dirty_nightly`
---
# Features
### Featured enhancement in AR
- AstralRinth offers a range of authorization options, giving users the flexibility to log in with valid licenses or even a pirate account without auth credentials breaks (_Unlike MultiMC Cracked and similar software_). Experience Minecraft on your terms, breaking free from traditional licensing constraints (_Popular in Russian Federation_).
> _The launcher provides an opportunity to use the well-known Modrinth, but with an improved user experience._
### Easy to use
- Using the launcher is intuitive, any user can figure it out.
## Included exclusive features
### Update notifies
- We have implemented notifications about the release of new updates on our Git. The launcher can also download them for you and try to install them.
- No ads in the entire launcher.
- Custom `.svg` vector icons for a distinct UI.
- Improved compatibility with both licensed and pirate accounts.
- Use **official microsoft accounts** or **offline/pirate accounts** — login won't break.
- Supports license-free access for testing or personal use.
- No dependence on official authentication services.
- Discord Rich Presence integration:
- Dynamic status messages.
- In-game timer and AFK counter.
- Strict disabling of statistics and other Modrinth metrics.
- Optimized archive/package size.
- Integrated update fetcher for seamless version management.
- Built-in update alerts for new versions posted on Git Astralium.
- Automatic download and installation capabilities.
- Database migration fixes, when error occurred (Interactive Mode) (Modrinth issue)
- ElyBy skin system integration (AuthLib / Java)
### Enhancements
- Custom .SVG vectors for a personalized touch.
- Improved compatibility for both pirate and licensed accounts.
- Beautiful Discord RPC with random messages while playing, along with an in-game timer and AFK counter.
- Forced disabling of statistics collection (modrinch metrics) with a hard patch from AstralRinth, ensuring it remains deactivated regardless of the configuration setting.
- Removal of advertisements from all launcher views.
- Optimization of packages (archives).
- Integrated update fetching feature
---
# Getting Started
To begin your AstralRinth adventure, follow these steps:
1. **Download Your OS Version**: Head over to our [releases page](https://git.astralium.su/didirus/AstralRinth/releases/) to find the right file for your operating system.
- **Choosing the Correct File**: Ensure you select the file that matches your OS requirements.
- [**How select file**](#downloadable-file-extensions)
- [**How select release**](#installation-subjects)
2. **Authentication**: Log in with a valid license or, for testing, try using a pirate account to see AstralRinth in action.
3. **Launch Minecraft**: Start your journey by launching Minecraft through AstralRinth and enjoy the adventures that await.
- **Choosing java installation**: The launcher will try to automatically detect the recommended JVM version for running the game, but you can configure everything in the launcher settings.
To begin using AstralRinth:
1. **Download Your OS Version**
- Go to the [releases page](https://git.astralium.su/didirus/AstralRinth/releases)
- [How to choose a file](#downloadable-file-extensions)
- [How to choose a release](#installation-warnings)
2. **Log In**
- Use your official Mojang/Microsoft account, or test using a non-licensed account.
3. **Launch Minecraft**
- Start Minecraft from the launcher.
- The launcher will auto-detect the recommended JVM version.
- You can also configure Java manually in the settings.
---
# Disclaimer
- AstralRinth is a project intended for experimentation and educational purposes only. It does not endorse or support piracy, and users are encouraged to obtain valid licenses for a fully-supported Minecraft experience.
- Users are reminded to respect licensing agreements and support the developers of Minecraft.
# Support our Project (Crypto Wallets)
- **AstralRinth** is intended **solely for educational and experimental use**.
- We **do not condone piracy** — users are encouraged to purchase a legitimate Minecraft license.
- Respect all relevant licensing agreements and support Minecraft developers.
---
# Support Our Project (Crypto Wallets)
If you'd like to support development, you can donate via the following crypto wallets:
- BTC (Telegram): 14g6asNYzcUoaQtB8B2QGKabgEvn55wfLj
- USDT TRC20 (Telegram): TMSmv1D5Fdf4fipUpwBCdh16WevrV45vGr
- TONCOIN (Telegram): UQAqUJ2_hVBI6k_gPyfp_jd-1K0OS61nIFPZuJWN9BwGAvKe
- TONCOIN (Telegram): UQAqUJ2_hVBI6k_gPyfp_jd-1K0OS61nIFPZuJWN9BwGAvKe

View File

@@ -50,9 +50,8 @@
color="primary"
@click="login()"
>
<LogInIcon v-if="!loginDisabled" />
<MicrosoftIcon v-if="!loginDisabled"/>
<SpinnerIcon v-else class="animate-spin" />
<MicrosoftIcon/>
</Button>
<Button v-tooltip="'Add offline'" icon-only @click="tryOfflineLogin()">
<PirateIcon />

View File

@@ -26,6 +26,7 @@ import {
type Version,
} from '@modrinth/utils'
import ConfirmModalWrapper from '@/components/ui/modal/ConfirmModalWrapper.vue'
import ModalWrapper from '@/components/ui/modal/ModalWrapper.vue'
import { get_project, get_version_many } from '@/helpers/cache'
import ModpackVersionModal from '@/components/ui/ModpackVersionModal.vue'
import dayjs from 'dayjs'
@@ -35,6 +36,11 @@ import type {
Manifest,
} from '../../../helpers/types'
import { initAuthlibPatching } from '@/helpers/utils.js'
const authLibPatchingModal = ref(null)
const isAuthLibPatchedSuccess = ref(false)
const isAuthLibPatching = ref(false)
const { formatMessage } = useVIntl()
const repairConfirmModal = ref()
@@ -447,9 +453,43 @@ const messages = defineMessages({
defaultMessage: 'reinstall',
},
})
async function handleInitAuthLibPatching(ismojang: boolean) {
isAuthLibPatching.value = true
let state = false
let instance_path = props.instance.loader_version != null ? props.instance.game_version + "-" + props.instance.loader_version : props.instance.game_version
try {
state = await initAuthlibPatching(instance_path, ismojang)
} catch (err) {
console.error(err)
}
isAuthLibPatching.value = false
isAuthLibPatchedSuccess.value = state
authLibPatchingModal.value.show()
}
</script>
<template>
<ModalWrapper
ref="authLibPatchingModal"
:header="'AuthLib installation report'"
:closable="true"
@close="authLibPatchingModal.hide()"
>
<div class="modal-body">
<h2 class="text-lg font-bold text-contrast space-y-2">
<p class="flex items-center gap-2 neon-text">
<span v-if="isAuthLibPatchedSuccess" class="neon-text">
AuthLib installation completed successfully! Now you can log in and play!
</span>
<span v-else class="neon-text">
Failed to install AuthLib. It's possible that no compatible AuthLib version was found for the selected game and/or mod loader version.
There may also be a problem with accessing resources behind CloudFlare.
</span>
</p>
</h2>
</div>
</ModalWrapper>
<ConfirmModalWrapper
ref="repairConfirmModal"
:title="formatMessage(messages.repairConfirmTitle)"
@@ -720,6 +760,24 @@ const messages = defineMessages({
</button>
</ButtonStyled>
</div>
<h2 class="m-0 mt-4 text-lg font-extrabold text-contrast block">
<div v-if="isAuthLibPatching" class="w-6 h-6 cursor-pointer hover:brightness-75 neon-icon pulse">
<SpinnerIcon class="size-4 animate-spin" />
</div>
Auth system (Skins) <span class="text-sm font-bold px-2 bg-brand-highlight text-brand rounded-full">Beta</span>
</h2>
<div class="mt-4 flex gap-2">
<ButtonStyled class="neon-button neon">
<button :disabled="isAuthLibPatching" @click="handleInitAuthLibPatching(true)">
Install Microsoft
</button>
</ButtonStyled>
<ButtonStyled class="neon-button neon">
<button :disabled="isAuthLibPatching" @click="handleInitAuthLibPatching(false) ">
Install Ely.By
</button>
</ButtonStyled>
</div>
</template>
<template v-else>
<template v-if="instance.linked_data && instance.linked_data.locked">
@@ -787,3 +845,9 @@ const messages = defineMessages({
</template>
</div>
</template>
<style lang="scss" scoped>
@import '../../../../../../packages/assets/styles/neon-button.scss';
@import '../../../../../../packages/assets/styles/neon-text.scss';
@import '../../../../../../packages/assets/styles/neon-icon.scss';
</style>

View File

@@ -1,6 +1,6 @@
import { ref } from 'vue'
import { getVersion } from '@tauri-apps/api/app'
import { getArtifact, getOS } from '@/helpers/utils.js'
import { initUpdateLauncher, getOS } from '@/helpers/utils.js'
export const allowState = ref(false)
export const installState = ref(false)
@@ -52,7 +52,7 @@ export async function getRemote(isDownloadState) {
installState.value = true;
const builds = remoteData.assets;
const fileName = getInstaller(getExtension(), builds);
result = fileName ? await getArtifact(fileName[1], fileName[0], currentOS.value, true) : false;
result = fileName ? await initUpdateLauncher(fileName[1], fileName[0], currentOS.value, true) : false;
installState.value = false;
}

View File

@@ -11,9 +11,9 @@ export async function getOS() {
}
// [AR] Feature
export async function getArtifact(downloadurl, filename, ostype, autoupdatesupported) {
export async function initUpdateLauncher(downloadurl, filename, ostype, autoupdatesupported) {
console.log('Downloading build', downloadurl, filename, ostype, autoupdatesupported)
return await invoke('plugin:utils|get_artifact', { downloadurl, filename, ostype, autoupdatesupported })
return await invoke('plugin:utils|init_update_launcher', { downloadurl, filename, ostype, autoupdatesupported })
}
// [AR] Patch fix
@@ -21,6 +21,11 @@ export async function applyMigrationFix(eol) {
return await invoke('plugin:utils|apply_migration_fix', { eol })
}
// [AR] Feature
export async function initAuthlibPatching(minecraftversion, ismojang) {
return await invoke('plugin:utils|init_authlib_patching', { minecraftversion, ismojang })
}
export async function openPath(path) {
return await invoke('plugin:utils|open_path', { path })
}

View File

@@ -218,8 +218,9 @@ fn main() {
"utils",
InlinedPlugin::new()
.commands(&[
"init_authlib_patching",
"apply_migration_fix",
"get_artifact",
"init_update_launcher",
"get_os",
"should_disable_mouseover",
"highlight_in_folder",

View File

@@ -10,14 +10,15 @@ use crate::api::{Result, TheseusSerializableError};
use dashmap::DashMap;
use std::path::{Path, PathBuf};
use theseus::prelude::canonicalize;
use url::Url;
use theseus::util::utils;
use url::Url;
pub fn init<R: tauri::Runtime>() -> tauri::plugin::TauriPlugin<R> {
tauri::plugin::Builder::new("utils")
.invoke_handler(tauri::generate_handler![
init_authlib_patching,
apply_migration_fix,
get_artifact,
init_update_launcher,
get_os,
should_disable_mouseover,
highlight_in_folder,
@@ -29,6 +30,17 @@ pub fn init<R: tauri::Runtime>() -> tauri::plugin::TauriPlugin<R> {
.build()
}
/// [AR] Feature
#[tauri::command]
pub async fn init_authlib_patching(
minecraftversion: &str,
ismojang: bool,
) -> Result<bool> {
let result =
utils::init_authlib_patching(minecraftversion, ismojang).await?;
Ok(result)
}
/// [AR] Patch fix
#[tauri::command]
pub async fn apply_migration_fix(eol: &str) -> Result<bool> {
@@ -38,8 +50,19 @@ pub async fn apply_migration_fix(eol: &str) -> Result<bool> {
/// [AR] Feature
#[tauri::command]
pub async fn get_artifact(downloadurl: &str, filename: &str, ostype: &str, autoupdatesupported: bool) -> Result<()> {
let _ = utils::init_download(downloadurl, filename, ostype, autoupdatesupported).await;
pub async fn init_update_launcher(
downloadurl: &str,
filename: &str,
ostype: &str,
autoupdatesupported: bool,
) -> Result<()> {
let _ = utils::init_update_launcher(
downloadurl,
filename,
ostype,
autoupdatesupported,
)
.await;
Ok(())
}

View File

@@ -41,7 +41,7 @@
]
},
"productName": "AstralRinth App",
"version": "0.10.303",
"version": "0.10.304",
"mainBinaryName": "AstralRinth App",
"identifier": "AstralRinthApp",
"plugins": {

View File

@@ -151,6 +151,41 @@ pub enum ErrorKind {
"A skin texture must have a dimension of either 64x64 or 64x32 pixels"
)]
InvalidSkinTexture,
#[error(
"[AR] Target minecraft {minecraft_version} version doesn't exist."
)]
InvalidMinecraftVersion {
minecraft_version: String,
},
#[error(
"[AR] Target metadata not found for minecraft version {minecraft_version}."
)]
MinecraftMetadataNotFound {
minecraft_version: String,
},
#[error(
"[AR] Network error: {error}"
)]
NetworkErrorOccurred {
error: String,
},
#[error(
"[AR] IO error: {error}"
)]
IOErrorOccurred {
error: String,
},
#[error(
"[AR] Parse error: {reason}"
)]
ParseError {
reason: String,
},
}
#[derive(Debug)]

View File

@@ -633,6 +633,7 @@ pub async fn launch_minecraft(
command.arg("--add-opens=jdk.internal/jdk.internal.misc=ALL-UNNAMED");
}
// FIXME: Fix ElyBy integration with this patch.
// [AR] Patch
if credentials.access_token == "null" && credentials.refresh_token == "null" {
if version_jar == "1.16.4" || version_jar == "1.16.5" {

View File

@@ -1,12 +1,12 @@
///
/// [AR] Feature
///
use crate::Result;
use crate::api::update;
use crate::state::db;
///
/// [AR] Feature Utils
///
use crate::{Result, State};
use serde::{Deserialize, Serialize};
use std::process;
use tokio::io;
use tokio::{fs, io};
const PACKAGE_JSON_CONTENT: &str =
// include_str!("../../../../apps/app-frontend/package.json");
@@ -17,13 +17,311 @@ pub struct Launcher {
pub version: String,
}
pub fn read_package_json() -> io::Result<Launcher> {
// Deserialize the content of package.json into a Launcher struct
let launcher: Launcher = serde_json::from_str(PACKAGE_JSON_CONTENT)?;
#[derive(Debug, Deserialize)]
struct Artifact {
path: Option<String>,
sha1: Option<String>,
url: Option<String>,
}
#[derive(Debug, Deserialize)]
struct Downloads {
artifact: Option<Artifact>,
}
#[derive(Debug, Deserialize)]
struct Library {
name: String,
downloads: Option<Downloads>,
}
#[derive(Debug, Deserialize)]
struct VersionJson {
libraries: Vec<Library>,
}
/// Deserialize the content of package.json into a Launcher struct
pub fn read_package_json() -> io::Result<Launcher> {
let launcher: Launcher = serde_json::from_str(PACKAGE_JSON_CONTENT)?;
Ok(launcher)
}
/// ### AR • Universal Write (IO) Function
/// Saves the downloaded bytes to the `libraries` directory using the given relative path.
async fn write_file_to_libraries(
relative_path: &str,
bytes: &bytes::Bytes,
) -> Result<()> {
let state = State::get().await?;
let output_path = state.directories.libraries_dir().join(relative_path);
fs::write(&output_path, bytes).await.map_err(|e| {
tracing::error!("[AR] • Failed to save file: {:?}", e);
crate::ErrorKind::IOErrorOccurred {
error: format!("Failed to save file: {e}"),
}
.as_error()
})
}
/// ### AR • AuthLib (Ely By)
/// Initializes the AuthLib patching process.
///
/// Returns `true` if the authlib patched successfully.
pub async fn init_authlib_patching(
minecraft_version: &str,
is_mojang: bool,
) -> Result<bool> {
let minecraft_library_metadata = get_minecraft_library_metadata(minecraft_version).await?;
// Parses the AuthLib version from string
// Example output: "com.mojang:authlib:6.0.58" -> "6.0.58"
let authlib_version = minecraft_library_metadata.name.split(':').nth(2).unwrap_or("unknown");
tracing::info!(
"[AR] • Attempting to download AuthLib {}.",
authlib_version
);
download_authlib(
&minecraft_library_metadata,
authlib_version,
minecraft_version,
is_mojang,
)
.await
}
/// ### AR • AuthLib (Ely By)
/// Downloads the AuthLib file from Mojang libraries or Git Astralium services.
async fn download_authlib(
minecraft_library_metadata: &Library,
authlib_version: &str,
minecraft_version: &str,
is_mojang: bool,
) -> Result<bool> {
let state = State::get().await?;
let (url, path) = extract_download_info(minecraft_library_metadata, minecraft_version)?;
let mut download_url = url.to_string();
let full_path = state.directories.libraries_dir().join(path);
if !is_mojang {
tracing::info!(
"[AR] • Attempting to download AuthLib from Git Astralium"
);
download_url = extract_ely_authlib_url(authlib_version).await?;
}
tracing::info!("[AR] • Downloading AuthLib from URL: {}", download_url);
let bytes = fetch_bytes_from_url(&download_url).await?;
tracing::info!("[AR] • Will save to path: {}", full_path.to_str().unwrap());
write_file_to_libraries(full_path.to_str().unwrap(), &bytes).await?;
tracing::info!("[AR] • Successfully saved AuthLib to {:?}", full_path);
Ok(true)
}
/// ### AR • AuthLib (Ely By)
/// Parses the ElyIntegration release JSON and returns the download URL for the given AuthLib version.
async fn extract_ely_authlib_url(authlib_version: &str) -> Result<String> {
let url = "https://git.astralium.su/api/v1/repos/didirus/ElyIntegration/releases/latest";
let response = reqwest::get(url).await.map_err(|e| {
tracing::error!(
"[AR] • Failed to fetch ElyIntegration release JSON: {:?}",
e
);
crate::ErrorKind::NetworkErrorOccurred {
error: format!("Failed to fetch ElyIntegration release JSON: {}", e),
}
.as_error()
})?;
let json: serde_json::Value = response.json().await.map_err(|e| {
tracing::error!("[AR] • Failed to parse ElyIntegration JSON: {:?}", e);
crate::ErrorKind::ParseError {
reason: format!("Failed to parse ElyIntegration JSON: {}", e),
}
.as_error()
})?;
let assets =
json.get("assets")
.and_then(|v| v.as_array())
.ok_or_else(|| {
crate::ErrorKind::ParseError {
reason: "Missing 'assets' array".into(),
}
.as_error()
})?;
let asset = assets
.iter()
.find(|a| {
a.get("name")
.and_then(|n| n.as_str())
.map(|n| n.contains(authlib_version))
.unwrap_or(false)
})
.ok_or_else(|| {
crate::ErrorKind::ParseError {
reason: format!(
"No matching asset for authlib-{}.jar",
authlib_version
),
}
.as_error()
})?;
let download_url = asset
.get("browser_download_url")
.and_then(|u| u.as_str())
.ok_or_else(|| {
crate::ErrorKind::ParseError {
reason: "Missing 'browser_download_url'".into(),
}
.as_error()
})?;
Ok(download_url.to_string())
}
/// ### AR • AuthLib (Ely By)
/// Extracts the artifact URL and Path from the library structure.
///
/// Returns a tuple of references to the URL and path strings,
/// or an error if the required metadata is missing.
fn extract_download_info<'a>(
minecraft_library_metadata: &'a Library,
minecraft_version: &str,
) -> Result<(&'a str, &'a str)> {
let artifact = minecraft_library_metadata
.downloads
.as_ref()
.and_then(|d| d.artifact.as_ref())
.ok_or_else(|| {
crate::ErrorKind::MinecraftMetadataNotFound {
minecraft_version: minecraft_version.to_string(),
}
.as_error()
})?;
let url = artifact.url.as_deref().ok_or_else(|| {
crate::ErrorKind::MinecraftMetadataNotFound {
minecraft_version: minecraft_version.to_string(),
}
.as_error()
})?;
let path = artifact.path.as_deref().ok_or_else(|| {
crate::ErrorKind::MinecraftMetadataNotFound {
minecraft_version: minecraft_version.to_string(),
}
.as_error()
})?;
Ok((url, path))
}
/// ### AR • AuthLib (Ely By)
/// Downloads bytes from the provided URL with a 15 second timeout.
async fn fetch_bytes_from_url(url: &str) -> Result<bytes::Bytes> {
// Create client instance with request timeout.
let client = reqwest::Client::new();
const TIMEOUT_SECONDS: u64 = 15;
let response = tokio::time::timeout(
std::time::Duration::from_secs(TIMEOUT_SECONDS),
client.get(url).send(),
)
.await
.map_err(|_| {
tracing::error!("[AR] • Download timed out after {} seconds", TIMEOUT_SECONDS);
crate::ErrorKind::NetworkErrorOccurred {
error: format!("Download timed out after {TIMEOUT_SECONDS} seconds").to_string(),
}
.as_error()
})?
.map_err(|e| {
tracing::error!("[AR] • Request error: {:?}", e);
crate::ErrorKind::NetworkErrorOccurred {
error: format!("Request error: {e}"),
}
.as_error()
})?;
if !response.status().is_success() {
let status = response.status().to_string();
tracing::error!("[AR] • Failed to download authlib: HTTP {}", status);
return Err(crate::ErrorKind::NetworkErrorOccurred {
error: format!("Failed to download authlib: HTTP {status}"),
}
.as_error());
}
response.bytes().await.map_err(|e| {
tracing::error!("[AR] • Failed to read response bytes: {:?}", e);
crate::ErrorKind::NetworkErrorOccurred {
error: format!("Failed to read response bytes: {e}"),
}
.as_error()
})
}
/// ### AR • AuthLib (Ely By)
/// Gets the Minecraft library metadata from the local libraries directory.
async fn get_minecraft_library_metadata(minecraft_version: &str) -> Result<Library> {
let state = State::get().await?;
let path = state
.directories
.version_dir(minecraft_version)
.join(format!("{}.json", minecraft_version));
if !path.exists() {
tracing::error!("[AR] • File not found: {:#?}", path);
return Err(crate::ErrorKind::InvalidMinecraftVersion {
minecraft_version: minecraft_version.to_string(),
}
.as_error());
}
let content = fs::read_to_string(&path).await?;
let version_data: VersionJson = serde_json::from_str(&content)?;
for lib in version_data.libraries {
if lib.name.contains("com.mojang:authlib") {
if let Some(downloads) = &lib.downloads {
if let Some(artifact) = &downloads.artifact {
if artifact.path.is_some()
&& artifact.url.is_some()
&& artifact.sha1.is_some()
{
tracing::info!("[AR] • Found AuthLib: {}", lib.name);
tracing::info!(
"[AR] • Path: {}",
artifact.path.as_ref().unwrap()
);
tracing::info!(
"[AR] • URL: {}",
artifact.url.as_ref().unwrap()
);
tracing::info!(
"[AR] • SHA1: {}",
artifact.sha1.as_ref().unwrap()
);
return Ok(lib);
}
}
}
}
}
Err(crate::ErrorKind::MinecraftMetadataNotFound {
minecraft_version: minecraft_version.to_string(),
}
.as_error())
}
/// ### AR • Migration
/// Applying migration fix for SQLite database.
pub async fn apply_migration_fix(eol: &str) -> Result<bool> {
tracing::info!("[AR] • Attempting to apply migration fix");
let patched = db::apply_migration_fix(eol).await?;
@@ -35,14 +333,19 @@ pub async fn apply_migration_fix(eol: &str) -> Result<bool> {
Ok(patched)
}
pub async fn init_download(
/// ### AR • Updater
/// Initialize the update launcher.
pub async fn init_update_launcher(
download_url: &str,
local_filename: &str,
os_type: &str,
auto_update_supported: bool,
) -> Result<()> {
println!("[AR] • Initialize downloading from • {:?}", download_url);
println!("[AR] • Save local file name • {:?}", local_filename);
tracing::info!("[AR] • Initialize downloading from • {:?}", download_url);
tracing::info!("[AR] • Save local file name • {:?}", local_filename);
tracing::info!("[AR] • OS type • {}", os_type);
tracing::info!("[AR] • Auto update supported • {}", auto_update_supported);
if let Err(e) = update::get_resource(
download_url,
local_filename,

View File

@@ -0,0 +1,675 @@
diff --git a/apps/app-frontend/src/components/ui/AccountsCard.vue b/apps/app-frontend/src/components/ui/AccountsCard.vue
index 7b03e1f39..69ee0e01e 100644
--- a/apps/app-frontend/src/components/ui/AccountsCard.vue
+++ b/apps/app-frontend/src/components/ui/AccountsCard.vue
@@ -50,9 +50,8 @@
color="primary"
@click="login()"
>
- <LogInIcon v-if="!loginDisabled" />
+ <MicrosoftIcon v-if="!loginDisabled"/>
<SpinnerIcon v-else class="animate-spin" />
- <MicrosoftIcon/>
</Button>
<Button v-tooltip="'Add offline'" icon-only @click="tryOfflineLogin()">
<PirateIcon />
diff --git a/apps/app-frontend/src/components/ui/instance_settings/InstallationSettings.vue b/apps/app-frontend/src/components/ui/instance_settings/InstallationSettings.vue
index 7810581a3..ff16faadb 100644
--- a/apps/app-frontend/src/components/ui/instance_settings/InstallationSettings.vue
+++ b/apps/app-frontend/src/components/ui/instance_settings/InstallationSettings.vue
@@ -26,6 +26,7 @@ import {
type Version,
} from '@modrinth/utils'
import ConfirmModalWrapper from '@/components/ui/modal/ConfirmModalWrapper.vue'
+import ModalWrapper from '@/components/ui/modal/ModalWrapper.vue'
import { get_project, get_version_many } from '@/helpers/cache'
import ModpackVersionModal from '@/components/ui/ModpackVersionModal.vue'
import dayjs from 'dayjs'
@@ -35,6 +36,11 @@ import type {
Manifest,
} from '../../../helpers/types'
+import { initAuthlibPatching } from '@/helpers/utils.js'
+const authLibPatchingModal = ref(null)
+const isAuthLibPatchedSuccess = ref(false)
+const isAuthLibPatching = ref(false)
+
const { formatMessage } = useVIntl()
const repairConfirmModal = ref()
@@ -447,9 +453,43 @@ const messages = defineMessages({
defaultMessage: 'reinstall',
},
})
+
+async function handleInitAuthLibPatching(ismojang: boolean) {
+ isAuthLibPatching.value = true
+ let state = false
+ let instance_path = props.instance.loader_version != null ? props.instance.game_version + "-" + props.instance.loader_version : props.instance.game_version
+ try {
+ state = await initAuthlibPatching(instance_path, ismojang)
+ } catch (err) {
+ console.error(err)
+ }
+ isAuthLibPatching.value = false
+ isAuthLibPatchedSuccess.value = state
+ authLibPatchingModal.value.show()
+}
</script>
<template>
+ <ModalWrapper
+ ref="authLibPatchingModal"
+ :header="'AuthLib installation report'"
+ :closable="true"
+ @close="authLibPatchingModal.hide()"
+ >
+ <div class="modal-body">
+ <h2 class="text-lg font-bold text-contrast space-y-2">
+ <p class="flex items-center gap-2 neon-text">
+ <span v-if="isAuthLibPatchedSuccess" class="neon-text">
+ AuthLib installation completed successfully! Now you can log in and play!
+ </span>
+ <span v-else class="neon-text">
+ Failed to install AuthLib. It's possible that no compatible AuthLib version was found for the selected game and/or mod loader version.
+ There may also be a problem with accessing resources behind CloudFlare.
+ </span>
+ </p>
+ </h2>
+ </div>
+ </ModalWrapper>
<ConfirmModalWrapper
ref="repairConfirmModal"
:title="formatMessage(messages.repairConfirmTitle)"
@@ -720,6 +760,24 @@ const messages = defineMessages({
</button>
</ButtonStyled>
</div>
+ <h2 class="m-0 mt-4 text-lg font-extrabold text-contrast block">
+ <div v-if="isAuthLibPatching" class="w-6 h-6 cursor-pointer hover:brightness-75 neon-icon pulse">
+ <SpinnerIcon class="size-4 animate-spin" />
+ </div>
+ Auth system (Skins) <span class="text-sm font-bold px-2 bg-brand-highlight text-brand rounded-full">Beta</span>
+ </h2>
+ <div class="mt-4 flex gap-2">
+ <ButtonStyled class="neon-button neon">
+ <button :disabled="isAuthLibPatching" @click="handleInitAuthLibPatching(true)">
+ Install Microsoft
+ </button>
+ </ButtonStyled>
+ <ButtonStyled class="neon-button neon">
+ <button :disabled="isAuthLibPatching" @click="handleInitAuthLibPatching(false) ">
+ Install Ely.By
+ </button>
+ </ButtonStyled>
+ </div>
</template>
<template v-else>
<template v-if="instance.linked_data && instance.linked_data.locked">
@@ -787,3 +845,9 @@ const messages = defineMessages({
</template>
</div>
</template>
+
+<style lang="scss" scoped>
+@import '../../../../../../packages/assets/styles/neon-button.scss';
+@import '../../../../../../packages/assets/styles/neon-text.scss';
+@import '../../../../../../packages/assets/styles/neon-icon.scss';
+</style>
\ No newline at end of file
diff --git a/apps/app-frontend/src/helpers/update.js b/apps/app-frontend/src/helpers/update.js
index cea6c6bd4..f7033b7ac 100644
--- a/apps/app-frontend/src/helpers/update.js
+++ b/apps/app-frontend/src/helpers/update.js
@@ -1,6 +1,6 @@
import { ref } from 'vue'
import { getVersion } from '@tauri-apps/api/app'
-import { getArtifact, getOS } from '@/helpers/utils.js'
+import { initUpdateLauncher, getOS } from '@/helpers/utils.js'
export const allowState = ref(false)
export const installState = ref(false)
@@ -52,7 +52,7 @@ export async function getRemote(isDownloadState) {
installState.value = true;
const builds = remoteData.assets;
const fileName = getInstaller(getExtension(), builds);
- result = fileName ? await getArtifact(fileName[1], fileName[0], currentOS.value, true) : false;
+ result = fileName ? await initUpdateLauncher(fileName[1], fileName[0], currentOS.value, true) : false;
installState.value = false;
}
diff --git a/apps/app-frontend/src/helpers/utils.js b/apps/app-frontend/src/helpers/utils.js
index ac950e175..99cbde38f 100644
--- a/apps/app-frontend/src/helpers/utils.js
+++ b/apps/app-frontend/src/helpers/utils.js
@@ -11,9 +11,9 @@ export async function getOS() {
}
// [AR] Feature
-export async function getArtifact(downloadurl, filename, ostype, autoupdatesupported) {
+export async function initUpdateLauncher(downloadurl, filename, ostype, autoupdatesupported) {
console.log('Downloading build', downloadurl, filename, ostype, autoupdatesupported)
- return await invoke('plugin:utils|get_artifact', { downloadurl, filename, ostype, autoupdatesupported })
+ return await invoke('plugin:utils|init_update_launcher', { downloadurl, filename, ostype, autoupdatesupported })
}
// [AR] Patch fix
@@ -21,6 +21,11 @@ export async function applyMigrationFix(eol) {
return await invoke('plugin:utils|apply_migration_fix', { eol })
}
+// [AR] Feature
+export async function initAuthlibPatching(minecraftversion, ismojang) {
+ return await invoke('plugin:utils|init_authlib_patching', { minecraftversion, ismojang })
+}
+
export async function openPath(path) {
return await invoke('plugin:utils|open_path', { path })
}
diff --git a/apps/app/build.rs b/apps/app/build.rs
index 4942497aa..5a35c88df 100644
--- a/apps/app/build.rs
+++ b/apps/app/build.rs
@@ -218,8 +218,9 @@ fn main() {
"utils",
InlinedPlugin::new()
.commands(&[
+ "init_authlib_patching",
"apply_migration_fix",
- "get_artifact",
+ "init_update_launcher",
"get_os",
"should_disable_mouseover",
"highlight_in_folder",
diff --git a/apps/app/src/api/utils.rs b/apps/app/src/api/utils.rs
index 452079157..951affa63 100644
--- a/apps/app/src/api/utils.rs
+++ b/apps/app/src/api/utils.rs
@@ -10,14 +10,15 @@ use crate::api::{Result, TheseusSerializableError};
use dashmap::DashMap;
use std::path::{Path, PathBuf};
use theseus::prelude::canonicalize;
-use url::Url;
use theseus::util::utils;
+use url::Url;
pub fn init<R: tauri::Runtime>() -> tauri::plugin::TauriPlugin<R> {
tauri::plugin::Builder::new("utils")
.invoke_handler(tauri::generate_handler![
+ init_authlib_patching,
apply_migration_fix,
- get_artifact,
+ init_update_launcher,
get_os,
should_disable_mouseover,
highlight_in_folder,
@@ -29,6 +30,17 @@ pub fn init<R: tauri::Runtime>() -> tauri::plugin::TauriPlugin<R> {
.build()
}
+/// [AR] Feature
+#[tauri::command]
+pub async fn init_authlib_patching(
+ minecraftversion: &str,
+ ismojang: bool,
+) -> Result<bool> {
+ let result =
+ utils::init_authlib_patching(minecraftversion, ismojang).await?;
+ Ok(result)
+}
+
/// [AR] Patch fix
#[tauri::command]
pub async fn apply_migration_fix(eol: &str) -> Result<bool> {
@@ -38,8 +50,19 @@ pub async fn apply_migration_fix(eol: &str) -> Result<bool> {
/// [AR] Feature
#[tauri::command]
-pub async fn get_artifact(downloadurl: &str, filename: &str, ostype: &str, autoupdatesupported: bool) -> Result<()> {
- let _ = utils::init_download(downloadurl, filename, ostype, autoupdatesupported).await;
+pub async fn init_update_launcher(
+ downloadurl: &str,
+ filename: &str,
+ ostype: &str,
+ autoupdatesupported: bool,
+) -> Result<()> {
+ let _ = utils::init_update_launcher(
+ downloadurl,
+ filename,
+ ostype,
+ autoupdatesupported,
+ )
+ .await;
Ok(())
}
diff --git a/apps/app/tauri.conf.json b/apps/app/tauri.conf.json
index 4ebb2aa5f..cfc271565 100644
--- a/apps/app/tauri.conf.json
+++ b/apps/app/tauri.conf.json
@@ -41,7 +41,7 @@
]
},
"productName": "AstralRinth App",
- "version": "0.10.303",
+ "version": "0.10.304",
"mainBinaryName": "AstralRinth App",
"identifier": "AstralRinthApp",
"plugins": {
diff --git a/packages/app-lib/src/error.rs b/packages/app-lib/src/error.rs
index 75c144f55..360984efd 100644
--- a/packages/app-lib/src/error.rs
+++ b/packages/app-lib/src/error.rs
@@ -151,6 +151,41 @@ pub enum ErrorKind {
"A skin texture must have a dimension of either 64x64 or 64x32 pixels"
)]
InvalidSkinTexture,
+
+ #[error(
+ "[AR] Target minecraft {minecraft_version} version doesn't exist."
+ )]
+ InvalidMinecraftVersion {
+ minecraft_version: String,
+ },
+
+ #[error(
+ "[AR] Target metadata not found for minecraft version {minecraft_version}."
+ )]
+ MinecraftMetadataNotFound {
+ minecraft_version: String,
+ },
+
+ #[error(
+ "[AR] Network error: {error}"
+ )]
+ NetworkErrorOccurred {
+ error: String,
+ },
+
+ #[error(
+ "[AR] IO error: {error}"
+ )]
+ IOErrorOccurred {
+ error: String,
+ },
+
+ #[error(
+ "[AR] Parse error: {reason}"
+ )]
+ ParseError {
+ reason: String,
+ },
}
#[derive(Debug)]
diff --git a/packages/app-lib/src/launcher/mod.rs b/packages/app-lib/src/launcher/mod.rs
index f1b5deb85..a7d0708e3 100644
--- a/packages/app-lib/src/launcher/mod.rs
+++ b/packages/app-lib/src/launcher/mod.rs
@@ -633,6 +633,7 @@ pub async fn launch_minecraft(
command.arg("--add-opens=jdk.internal/jdk.internal.misc=ALL-UNNAMED");
}
+ // FIXME: Fix ElyBy integration with this patch.
// [AR] Patch
if credentials.access_token == "null" && credentials.refresh_token == "null" {
if version_jar == "1.16.4" || version_jar == "1.16.5" {
diff --git a/packages/app-lib/src/util/utils.rs b/packages/app-lib/src/util/utils.rs
index adebc3a67..e4d9a2ab1 100644
--- a/packages/app-lib/src/util/utils.rs
+++ b/packages/app-lib/src/util/utils.rs
@@ -1,12 +1,12 @@
-///
-/// [AR] Feature
-///
-use crate::Result;
use crate::api::update;
use crate::state::db;
+///
+/// [AR] Feature Utils
+///
+use crate::{Result, State};
use serde::{Deserialize, Serialize};
use std::process;
-use tokio::io;
+use tokio::{fs, io};
const PACKAGE_JSON_CONTENT: &str =
// include_str!("../../../../apps/app-frontend/package.json");
@@ -17,13 +17,311 @@ pub struct Launcher {
pub version: String,
}
+#[derive(Debug, Deserialize)]
+struct Artifact {
+ path: Option<String>,
+ sha1: Option<String>,
+ url: Option<String>,
+}
+
+#[derive(Debug, Deserialize)]
+struct Downloads {
+ artifact: Option<Artifact>,
+}
+
+#[derive(Debug, Deserialize)]
+struct Library {
+ name: String,
+ downloads: Option<Downloads>,
+}
+
+#[derive(Debug, Deserialize)]
+struct VersionJson {
+ libraries: Vec<Library>,
+}
+
+/// Deserialize the content of package.json into a Launcher struct
pub fn read_package_json() -> io::Result<Launcher> {
- // Deserialize the content of package.json into a Launcher struct
let launcher: Launcher = serde_json::from_str(PACKAGE_JSON_CONTENT)?;
-
Ok(launcher)
}
+/// ### AR • Universal Write (IO) Function
+/// Saves the downloaded bytes to the `libraries` directory using the given relative path.
+async fn write_file_to_libraries(
+ relative_path: &str,
+ bytes: &bytes::Bytes,
+) -> Result<()> {
+ let state = State::get().await?;
+ let output_path = state.directories.libraries_dir().join(relative_path);
+
+ fs::write(&output_path, bytes).await.map_err(|e| {
+ tracing::error!("[AR] • Failed to save file: {:?}", e);
+ crate::ErrorKind::IOErrorOccurred {
+ error: format!("Failed to save file: {e}"),
+ }
+ .as_error()
+ })
+}
+
+/// ### AR • AuthLib (Ely By)
+/// Initializes the AuthLib patching process.
+///
+/// Returns `true` if the authlib patched successfully.
+pub async fn init_authlib_patching(
+ minecraft_version: &str,
+ is_mojang: bool,
+) -> Result<bool> {
+ let minecraft_library_metadata = get_minecraft_library_metadata(minecraft_version).await?;
+ // Parses the AuthLib version from string
+ // Example output: "com.mojang:authlib:6.0.58" -> "6.0.58"
+ let authlib_version = minecraft_library_metadata.name.split(':').nth(2).unwrap_or("unknown");
+
+ tracing::info!(
+ "[AR] • Attempting to download AuthLib {}.",
+ authlib_version
+ );
+
+ download_authlib(
+ &minecraft_library_metadata,
+ authlib_version,
+ minecraft_version,
+ is_mojang,
+ )
+ .await
+}
+
+/// ### AR • AuthLib (Ely By)
+/// Downloads the AuthLib file from Mojang libraries or Git Astralium services.
+async fn download_authlib(
+ minecraft_library_metadata: &Library,
+ authlib_version: &str,
+ minecraft_version: &str,
+ is_mojang: bool,
+) -> Result<bool> {
+ let state = State::get().await?;
+ let (url, path) = extract_download_info(minecraft_library_metadata, minecraft_version)?;
+ let mut download_url = url.to_string();
+ let full_path = state.directories.libraries_dir().join(path);
+
+ if !is_mojang {
+ tracing::info!(
+ "[AR] • Attempting to download AuthLib from Git Astralium"
+ );
+ download_url = extract_ely_authlib_url(authlib_version).await?;
+ }
+ tracing::info!("[AR] • Downloading AuthLib from URL: {}", download_url);
+ let bytes = fetch_bytes_from_url(&download_url).await?;
+ tracing::info!("[AR] • Will save to path: {}", full_path.to_str().unwrap());
+ write_file_to_libraries(full_path.to_str().unwrap(), &bytes).await?;
+ tracing::info!("[AR] • Successfully saved AuthLib to {:?}", full_path);
+ Ok(true)
+}
+
+/// ### AR • AuthLib (Ely By)
+/// Parses the ElyIntegration release JSON and returns the download URL for the given AuthLib version.
+async fn extract_ely_authlib_url(authlib_version: &str) -> Result<String> {
+ let url = "https://git.astralium.su/api/v1/repos/didirus/ElyIntegration/releases/latest";
+
+ let response = reqwest::get(url).await.map_err(|e| {
+ tracing::error!(
+ "[AR] • Failed to fetch ElyIntegration release JSON: {:?}",
+ e
+ );
+ crate::ErrorKind::NetworkErrorOccurred {
+ error: format!("Failed to fetch ElyIntegration release JSON: {}", e),
+ }
+ .as_error()
+ })?;
+
+ let json: serde_json::Value = response.json().await.map_err(|e| {
+ tracing::error!("[AR] • Failed to parse ElyIntegration JSON: {:?}", e);
+ crate::ErrorKind::ParseError {
+ reason: format!("Failed to parse ElyIntegration JSON: {}", e),
+ }
+ .as_error()
+ })?;
+
+ let assets =
+ json.get("assets")
+ .and_then(|v| v.as_array())
+ .ok_or_else(|| {
+ crate::ErrorKind::ParseError {
+ reason: "Missing 'assets' array".into(),
+ }
+ .as_error()
+ })?;
+
+ let asset = assets
+ .iter()
+ .find(|a| {
+ a.get("name")
+ .and_then(|n| n.as_str())
+ .map(|n| n.contains(authlib_version))
+ .unwrap_or(false)
+ })
+ .ok_or_else(|| {
+ crate::ErrorKind::ParseError {
+ reason: format!(
+ "No matching asset for authlib-{}.jar",
+ authlib_version
+ ),
+ }
+ .as_error()
+ })?;
+
+ let download_url = asset
+ .get("browser_download_url")
+ .and_then(|u| u.as_str())
+ .ok_or_else(|| {
+ crate::ErrorKind::ParseError {
+ reason: "Missing 'browser_download_url'".into(),
+ }
+ .as_error()
+ })?;
+
+ Ok(download_url.to_string())
+}
+
+/// ### AR • AuthLib (Ely By)
+/// Extracts the artifact URL and Path from the library structure.
+///
+/// Returns a tuple of references to the URL and path strings,
+/// or an error if the required metadata is missing.
+fn extract_download_info<'a>(
+ minecraft_library_metadata: &'a Library,
+ minecraft_version: &str,
+) -> Result<(&'a str, &'a str)> {
+ let artifact = minecraft_library_metadata
+ .downloads
+ .as_ref()
+ .and_then(|d| d.artifact.as_ref())
+ .ok_or_else(|| {
+ crate::ErrorKind::MinecraftMetadataNotFound {
+ minecraft_version: minecraft_version.to_string(),
+ }
+ .as_error()
+ })?;
+
+ let url = artifact.url.as_deref().ok_or_else(|| {
+ crate::ErrorKind::MinecraftMetadataNotFound {
+ minecraft_version: minecraft_version.to_string(),
+ }
+ .as_error()
+ })?;
+
+ let path = artifact.path.as_deref().ok_or_else(|| {
+ crate::ErrorKind::MinecraftMetadataNotFound {
+ minecraft_version: minecraft_version.to_string(),
+ }
+ .as_error()
+ })?;
+
+ Ok((url, path))
+}
+
+/// ### AR • AuthLib (Ely By)
+/// Downloads bytes from the provided URL with a 15 second timeout.
+async fn fetch_bytes_from_url(url: &str) -> Result<bytes::Bytes> {
+ // Create client instance with request timeout.
+ let client = reqwest::Client::new();
+ const TIMEOUT_SECONDS: u64 = 15;
+
+ let response = tokio::time::timeout(
+ std::time::Duration::from_secs(TIMEOUT_SECONDS),
+ client.get(url).send(),
+ )
+ .await
+ .map_err(|_| {
+ tracing::error!("[AR] • Download timed out after {} seconds", TIMEOUT_SECONDS);
+ crate::ErrorKind::NetworkErrorOccurred {
+ error: format!("Download timed out after {TIMEOUT_SECONDS} seconds").to_string(),
+ }
+ .as_error()
+ })?
+ .map_err(|e| {
+ tracing::error!("[AR] • Request error: {:?}", e);
+ crate::ErrorKind::NetworkErrorOccurred {
+ error: format!("Request error: {e}"),
+ }
+ .as_error()
+ })?;
+
+ if !response.status().is_success() {
+ let status = response.status().to_string();
+ tracing::error!("[AR] • Failed to download authlib: HTTP {}", status);
+ return Err(crate::ErrorKind::NetworkErrorOccurred {
+ error: format!("Failed to download authlib: HTTP {status}"),
+ }
+ .as_error());
+ }
+
+ response.bytes().await.map_err(|e| {
+ tracing::error!("[AR] • Failed to read response bytes: {:?}", e);
+ crate::ErrorKind::NetworkErrorOccurred {
+ error: format!("Failed to read response bytes: {e}"),
+ }
+ .as_error()
+ })
+}
+
+/// ### AR • AuthLib (Ely By)
+/// Gets the Minecraft library metadata from the local libraries directory.
+async fn get_minecraft_library_metadata(minecraft_version: &str) -> Result<Library> {
+ let state = State::get().await?;
+
+ let path = state
+ .directories
+ .version_dir(minecraft_version)
+ .join(format!("{}.json", minecraft_version));
+ if !path.exists() {
+ tracing::error!("[AR] • File not found: {:#?}", path);
+ return Err(crate::ErrorKind::InvalidMinecraftVersion {
+ minecraft_version: minecraft_version.to_string(),
+ }
+ .as_error());
+ }
+
+ let content = fs::read_to_string(&path).await?;
+ let version_data: VersionJson = serde_json::from_str(&content)?;
+
+ for lib in version_data.libraries {
+ if lib.name.contains("com.mojang:authlib") {
+ if let Some(downloads) = &lib.downloads {
+ if let Some(artifact) = &downloads.artifact {
+ if artifact.path.is_some()
+ && artifact.url.is_some()
+ && artifact.sha1.is_some()
+ {
+ tracing::info!("[AR] • Found AuthLib: {}", lib.name);
+ tracing::info!(
+ "[AR] • Path: {}",
+ artifact.path.as_ref().unwrap()
+ );
+ tracing::info!(
+ "[AR] • URL: {}",
+ artifact.url.as_ref().unwrap()
+ );
+ tracing::info!(
+ "[AR] • SHA1: {}",
+ artifact.sha1.as_ref().unwrap()
+ );
+
+ return Ok(lib);
+ }
+ }
+ }
+ }
+ }
+
+ Err(crate::ErrorKind::MinecraftMetadataNotFound {
+ minecraft_version: minecraft_version.to_string(),
+ }
+ .as_error())
+}
+
+/// ### AR • Migration
+/// Applying migration fix for SQLite database.
pub async fn apply_migration_fix(eol: &str) -> Result<bool> {
tracing::info!("[AR] • Attempting to apply migration fix");
let patched = db::apply_migration_fix(eol).await?;
@@ -35,14 +333,19 @@ pub async fn apply_migration_fix(eol: &str) -> Result<bool> {
Ok(patched)
}
-pub async fn init_download(
+/// ### AR • Updater
+/// Initialize the update launcher.
+pub async fn init_update_launcher(
download_url: &str,
local_filename: &str,
os_type: &str,
auto_update_supported: bool,
) -> Result<()> {
- println!("[AR] • Initialize downloading from • {:?}", download_url);
- println!("[AR] • Save local file name • {:?}", local_filename);
+ tracing::info!("[AR] • Initialize downloading from • {:?}", download_url);
+ tracing::info!("[AR] • Save local file name • {:?}", local_filename);
+ tracing::info!("[AR] • OS type • {}", os_type);
+ tracing::info!("[AR] • Auto update supported • {}", auto_update_supported);
+
if let Err(e) = update::get_resource(
download_url,
local_filename,

122
readme/ru_ru/README.md Normal file
View File

@@ -0,0 +1,122 @@
# 📘 Навигация
- [🔧 Установка](#установка)
- [✨ Возможности](#возможности)
- [🚀 Начало работы](#начало-работы)
- [⚠️ Отказ от ответственности](#отказ-от-ответственности)
- [💰 Поддержать](#поддержать-проект-крипто-кошельки)
## Другие языки
> [English](../../README.md)
## Канал поддержки
> [Telegram](https://me.astralium.su/ref/telegram_channel)
---
# О проекте
## **AstralRinth • Раскрой потенциал своих приключений в Minecraft**
Добро пожаловать в **AstralRinth (AR)** — мощный форк Modrinth, переосмысленный для улучшения твоего опыта в Minecraft. Независимо от того, являешься ли ты поклонником GUI или разработчиком, использующим API Modrinth, **Theseus Core** — это твоя стартовая платформа для новой эры игрового процесса в Minecraft.
- *С недавнего времени имеется улучшенная интеграция с Git Astralium API*
## **О программе**
**AstralRinth** — это специализированная ветка проекта Theseus, сосредоточенная на **офлайн-аутентификации**, предоставляя ещё большую гибкость и ориентированность на пользователя. Путешествуй по мирам Minecraft без необходимости онлайн-авторизации — благодаря AstralRinth.
## **AR • Открой безграничные горизонты Minecraft**
Уникальный форк с фокусом на предоставление **пробного игрового опыта Minecraft** — без необходимости лицензии. В настоящее время включает:
---
# Установка
Чтобы установить лаунчер:
1. Перейдите на [страницу релизов](https://git.astralium.su/didirus/AstralRinth/releases), чтобы скачать нужную версию для вашей системы.
2. После загрузки нужного исполняемого файла или архива — откройте его.
### Поддерживаемые расширения файлов
| Расширение | ОС | Примечания |
| ---------- | ------- | --------------------------------------------------------------------- |
| `.msi` | Windows | Поддерживаются актуальные версии Microsoft Windows |
| `.dmg` | macOS | Работает на MacOS Ventura / Sonoma / Sequoia, возможно и на старых |
| `.deb` | Linux | Так как дистрибутивов много, совместимость не гарантируется полностью |
### Особенности установки
Сборки с префиксами ниже **не рекомендуются к установке** и могут содержать ошибки:
- `dev`
- `nightly`
- `dirty`
- `dirty-dev`
- `dirty-nightly`
- `dirty_dev`
- `dirty_nightly`
---
# Возможности
> аунчер предоставляет возможность использовать знакомый Modrinth с улучшенным пользовательским интерфейсом._
## Уникальные функции
- Отсутствие рекламы во всём лаунчере.
- Кастомные `.svg` векторы для персонализированного интерфейса.
- Улучшенная совместимость с лицензионными и пиратскими аккаунтами.
- Используйте **официальные аккаунты Microsoft** или **пиратские аккаунты** — без сбоев в авторизации.
- Поддержка безлицензионного доступа для тестирования или личного использования.
- Нет зависимости от официальных сервисов авторизации.
- Интеграция Discord Rich Presence:
- Случайные статусные сообщения.
- Таймер игры и счётчик AFK.
- Жёсткое отключение сбора статистики (метрик Modrinth) через патч AstralRinth — работает независимо от конфигурации.
- Удаление рекламы из всех окон лаунчера.
- Оптимизация архивов (пакетов).
- Встроенная система получения обновлений:
- Уведомления о новых версиях на Git.
- Возможность автозагрузки и автоустановки.
- Исправления миграции базы данных при ошибках (интерактивный режим, проблема Modrinth).
- Интеграция системы скинов ElyBy (AuthLib / Java).
---
# Начало работы
Чтобы начать приключение с AstralRinth, выполните следующие шаги:
1. **Скачайте нужную версию под вашу ОС**
- Перейдите на [страницу релизов](https://git.astralium.su/didirus/AstralRinth/releases)
- [**Как выбрать файл**](#поддерживаемые-расширения-файлов)
- [**Как выбрать релиз**](#особенности-установки)
2. **Авторизация**
- Войдите с действующей лицензией или используйте пиратский аккаунт для теста AstralRinth.
3. **Запуск Minecraft**
- Запустите Minecraft через AstralRinth и наслаждайтесь.
- Лаунчер попытается автоматически определить рекомендованную JVM для запуска игры, но вы можете всё настроить вручную в настройках.
---
# Отказ от ответственности
- **AstralRinth** предназначен только для экспериментов и образовательных целей. Мы не поддерживаем пиратство и рекомендуем приобретать официальную лицензию Minecraft для полноценного игрового опыта.
- Пользователям напоминается соблюдать лицензионные соглашения и поддерживать разработчиков Minecraft.
---
# Поддержать проект (крипто-кошельки)
Если вы хотите поддержать разработку, вы можете отправить донат на следующие кошельки:
- BTC (Telegram): 14g6asNYzcUoaQtB8B2QGKabgEvn55wfLj
- TONCOIN (Telegram): UQAqUJ2_hVBI6k_gPyfp_jd-1K0OS61nIFPZuJWN9BwGAvKe