mirror of
https://github.com/ALEZ-DEV/Babylonia-terminal.git
synced 2025-12-16 01:18:50 +00:00
can now download and install proton from GUI
This commit is contained in:
parent
c6eacbfeaa
commit
4d07600ea6
51
babylonia_terminal_launcher/lib/models/proton.dart
Normal file
51
babylonia_terminal_launcher/lib/models/proton.dart
Normal file
@ -0,0 +1,51 @@
|
||||
import 'package:fixnum/fixnum.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import './../messages/steps/proton.pb.dart';
|
||||
import './../providers/providers.dart';
|
||||
|
||||
enum ProtonInstallationState {
|
||||
idle,
|
||||
downloading,
|
||||
decompressing,
|
||||
}
|
||||
|
||||
class Proton with ChangeNotifier {
|
||||
ProtonInstallationState protonState = ProtonInstallationState.idle;
|
||||
|
||||
Int64 currentProgress = Int64(0);
|
||||
Int64 maxProgress = Int64(0);
|
||||
|
||||
Future startInstallation(BuildContext context, String protonVersion) async {
|
||||
protonState = ProtonInstallationState.downloading;
|
||||
notifyListeners();
|
||||
|
||||
StartProtonInstallation(protonVersion: protonVersion).sendSignalToRust();
|
||||
final progressStream = ProtonDownloadProgress.rustSignalStream;
|
||||
await for (final rustSignal in progressStream) {
|
||||
currentProgress = rustSignal.message.current;
|
||||
maxProgress = rustSignal.message.max;
|
||||
notifyListeners();
|
||||
|
||||
if (currentProgress >= maxProgress) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
final notificationDecompressingStream =
|
||||
NotifyProtonStartDecompressing.rustSignalStream;
|
||||
await for (final _ in notificationDecompressingStream) {
|
||||
protonState = ProtonInstallationState.decompressing;
|
||||
notifyListeners();
|
||||
break;
|
||||
}
|
||||
|
||||
final notificationInstalledStream =
|
||||
NotifiyProtonSuccessfullyInstalled.rustSignalStream;
|
||||
await for (final _ in notificationInstalledStream) {
|
||||
Provider.of<GameStateProvider>(context, listen: false).updateGameState();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,5 +1,4 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:yaru/theme.dart';
|
||||
|
||||
class SimpleButton extends StatelessWidget {
|
||||
const SimpleButton({super.key, required this.child, required this.onPressed});
|
||||
|
||||
@ -3,7 +3,8 @@ import 'package:provider/provider.dart';
|
||||
import 'package:yaru/widgets.dart';
|
||||
|
||||
import './../../models/github.dart';
|
||||
import './../../providers/providers.dart';
|
||||
import './../../models/proton.dart';
|
||||
import './../../widgets/simple_button.dart';
|
||||
|
||||
class ProtonSteps extends StatefulWidget {
|
||||
const ProtonSteps({super.key});
|
||||
@ -13,6 +14,40 @@ class ProtonSteps extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _ProtonStepsState extends State<ProtonSteps> {
|
||||
final Proton proton = Proton();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ChangeNotifierProvider(
|
||||
create: (_) => proton,
|
||||
child: Builder(
|
||||
builder: (context) {
|
||||
switch (Provider.of<Proton>(context).protonState) {
|
||||
case ProtonInstallationState.idle:
|
||||
return const InstallProton();
|
||||
case ProtonInstallationState.downloading:
|
||||
return const Center(
|
||||
child: Text('downloading...'),
|
||||
);
|
||||
case ProtonInstallationState.decompressing:
|
||||
return const Center(
|
||||
child: Text('decompressing...'),
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class InstallProton extends StatefulWidget {
|
||||
const InstallProton({super.key});
|
||||
|
||||
@override
|
||||
State<InstallProton> createState() => _InstallProtonState();
|
||||
}
|
||||
|
||||
class _InstallProtonState extends State<InstallProton> {
|
||||
bool hasLoaded = false;
|
||||
bool isLoading = false;
|
||||
late List<String> protonVersions;
|
||||
@ -39,7 +74,7 @@ class _ProtonStepsState extends State<ProtonSteps> {
|
||||
}
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
padding: const EdgeInsets.all(20.0),
|
||||
child: isLoading
|
||||
? const Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
@ -51,20 +86,33 @@ class _ProtonStepsState extends State<ProtonSteps> {
|
||||
Text('Fetching versions...'),
|
||||
],
|
||||
)
|
||||
: YaruPopupMenuButton(
|
||||
initialValue: selectedValue,
|
||||
itemBuilder: (_) => protonVersions
|
||||
.map(
|
||||
(e) => PopupMenuItem(
|
||||
value: e,
|
||||
child: Text(e),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
onSelected: (v) => setState(() {
|
||||
selectedValue = v!;
|
||||
}),
|
||||
child: Text(selectedValue!),
|
||||
: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
YaruPopupMenuButton(
|
||||
initialValue: selectedValue,
|
||||
itemBuilder: (_) => protonVersions
|
||||
.map(
|
||||
(e) => PopupMenuItem(
|
||||
value: e,
|
||||
child: Text(e),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
onSelected: (v) => setState(() {
|
||||
selectedValue = v;
|
||||
}),
|
||||
child: Text(selectedValue!),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 8.0),
|
||||
child: SimpleButton(
|
||||
onPressed: () => Provider.of<Proton>(context, listen: false)
|
||||
.startInstallation(context, selectedValue!),
|
||||
child: const Text("Install"),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
19
babylonia_terminal_launcher/messages/steps/proton.proto
Normal file
19
babylonia_terminal_launcher/messages/steps/proton.proto
Normal file
@ -0,0 +1,19 @@
|
||||
syntax = "proto3";
|
||||
package proton;
|
||||
|
||||
// [RINF:RUST-SIGNAL]
|
||||
message ProtonDownloadProgress {
|
||||
uint64 current = 1;
|
||||
uint64 max = 2;
|
||||
}
|
||||
|
||||
// [RINF:DART-SIGNAL]
|
||||
message StartProtonInstallation {
|
||||
string protonVersion = 1;
|
||||
};
|
||||
|
||||
// [RINF:RUST-SIGNAL]
|
||||
message NotifyProtonStartDecompressing {}
|
||||
|
||||
// [RINF:RUST-SIGNAL]
|
||||
message NotifiyProtonSuccessfullyInstalled {}
|
||||
2
babylonia_terminal_launcher/native/hub/Cargo.toml
Executable file → Normal file
2
babylonia_terminal_launcher/native/hub/Cargo.toml
Executable file → Normal file
@ -17,3 +17,5 @@ prost = "0.12.3"
|
||||
wasm-bindgen = "0.2.91"
|
||||
tokio_with_wasm = "0.4.3"
|
||||
babylonia-terminal-sdk = { path = "./../../../babylonia-terminal-sdk" }
|
||||
downloader = { git = "https://github.com/ALEZ-DEV/downloader" } # version = "0.2.7",
|
||||
once_cell = "1.19.0"
|
||||
|
||||
@ -11,7 +11,7 @@ use crate::messages::{
|
||||
};
|
||||
|
||||
#[warn(dead_code)]
|
||||
struct GithubInfo;
|
||||
pub struct GithubInfo;
|
||||
|
||||
impl GithubRequester for GithubInfo {
|
||||
fn set_github_release_index(&mut self, _: usize) {
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
//! This `hub` crate is the
|
||||
//! entry point of the Rust logic.
|
||||
|
||||
use proton::listen_proton_installation;
|
||||
// This `tokio` will be used by Rinf.
|
||||
// You can replace it with the original `tokio`
|
||||
// if you're not targeting the web.
|
||||
@ -10,6 +11,7 @@ mod config;
|
||||
mod game_state;
|
||||
mod github;
|
||||
mod messages;
|
||||
mod proton;
|
||||
|
||||
rinf::write_interface!();
|
||||
|
||||
@ -27,4 +29,5 @@ async fn main() {
|
||||
tokio::spawn(config::listen_config());
|
||||
tokio::spawn(config::listen_config());
|
||||
tokio::spawn(github::listen_proton_version());
|
||||
tokio::spawn(proton::listen_proton_installation());
|
||||
}
|
||||
|
||||
129
babylonia_terminal_launcher/native/hub/src/proton.rs
Normal file
129
babylonia_terminal_launcher/native/hub/src/proton.rs
Normal file
@ -0,0 +1,129 @@
|
||||
use std::{future::Future, thread};
|
||||
|
||||
use babylonia_terminal_sdk::{
|
||||
components::{
|
||||
self,
|
||||
component_downloader::ComponentDownloader,
|
||||
proton_component::{self, ProtonComponent, PROTON_DEV, PROTON_REPO},
|
||||
},
|
||||
game_state::GameState,
|
||||
utils::github_requester::{GithubRelease, GithubRequester},
|
||||
};
|
||||
use rinf::debug_print;
|
||||
use tokio_with_wasm::tokio::{
|
||||
self,
|
||||
task::{spawn_blocking, spawn_local},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
github::GithubInfo,
|
||||
messages::{
|
||||
error::ReportError,
|
||||
steps::proton::{
|
||||
NotifiyProtonSuccessfullyInstalled, NotifyProtonStartDecompressing,
|
||||
ProtonDownloadProgress, StartProtonInstallation,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
pub async fn listen_proton_installation() {
|
||||
let mut receiver = StartProtonInstallation::get_dart_signal_receiver();
|
||||
while let Some(info) = receiver.recv().await {
|
||||
let releases: Result<Vec<GithubRelease>, _> =
|
||||
GithubInfo::get_github_releases(PROTON_DEV, PROTON_REPO).await;
|
||||
if releases.is_err() {
|
||||
ReportError {
|
||||
error_message: format!("When fetching proton versions : {}", releases.unwrap_err()),
|
||||
}
|
||||
.send_signal_to_dart();
|
||||
continue;
|
||||
}
|
||||
|
||||
let checked_releases = releases.unwrap();
|
||||
let release_index = checked_releases
|
||||
.iter()
|
||||
.position(|v| v.tag_name == info.message.proton_version);
|
||||
|
||||
if release_index.is_none() {
|
||||
ReportError {
|
||||
error_message: "Failed to find Proton version".to_string(),
|
||||
}
|
||||
.send_signal_to_dart();
|
||||
continue;
|
||||
}
|
||||
|
||||
let mut proton_component = ProtonComponent::new(GameState::get_config().await.config_dir);
|
||||
|
||||
proton_component.set_github_release_index(release_index.unwrap());
|
||||
|
||||
thread::spawn(|| {
|
||||
tokio::runtime::Builder::new_current_thread()
|
||||
.enable_all()
|
||||
.build()
|
||||
.unwrap()
|
||||
.block_on(async {
|
||||
install_proton(proton_component).await;
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn install_proton(component: ProtonComponent) {
|
||||
debug_print!("starting !!!!");
|
||||
match component.install(Some(DownloadReporter::create())).await {
|
||||
Err(e) => ReportError {
|
||||
error_message: format!("Failed to install Proton : {}", e),
|
||||
}
|
||||
.send_signal_to_dart(),
|
||||
Ok(_) => NotifiyProtonSuccessfullyInstalled {}.send_signal_to_dart(),
|
||||
}
|
||||
}
|
||||
|
||||
struct DownloadReporterPrivate {
|
||||
max_progress: Option<u64>,
|
||||
last_current: u64,
|
||||
message: String,
|
||||
}
|
||||
|
||||
struct DownloadReporter {
|
||||
private: std::sync::Mutex<Option<DownloadReporterPrivate>>,
|
||||
}
|
||||
|
||||
impl DownloadReporter {
|
||||
pub fn create() -> std::sync::Arc<Self> {
|
||||
std::sync::Arc::new(Self {
|
||||
private: std::sync::Mutex::new(None),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl downloader::progress::Reporter for DownloadReporter {
|
||||
fn setup(&self, max_progress: Option<u64>, message: &str) {
|
||||
let private = DownloadReporterPrivate {
|
||||
max_progress,
|
||||
last_current: 0,
|
||||
message: message.to_owned(),
|
||||
};
|
||||
|
||||
let mut guard = self.private.lock().unwrap();
|
||||
*guard = Some(private);
|
||||
}
|
||||
|
||||
fn progress(&self, current: u64) {
|
||||
if let Some(p) = self.private.lock().unwrap().as_mut() {
|
||||
ProtonDownloadProgress {
|
||||
current: current,
|
||||
max: p.max_progress.unwrap(),
|
||||
}
|
||||
.send_signal_to_dart();
|
||||
}
|
||||
}
|
||||
|
||||
fn set_message(&self, _: &str) {}
|
||||
|
||||
fn done(&self) {
|
||||
NotifyProtonStartDecompressing {}.send_signal_to_dart();
|
||||
let mut guard = self.private.lock().unwrap();
|
||||
*guard = None;
|
||||
}
|
||||
}
|
||||
@ -49,6 +49,7 @@ dependencies:
|
||||
shared_preferences: ^2.2.3
|
||||
http: ^1.2.1
|
||||
flutter_highlight: ^0.7.0
|
||||
fixnum: ^1.1.0
|
||||
|
||||
dependency_overrides:
|
||||
media_kit:
|
||||
|
||||
Loading…
Reference in New Issue
Block a user