fix: rewrote ingress emitting system. should fix https://github.com/Polyfrost/OneLauncher/issues/419 and https://github.com/Polyfrost/OneLauncher/issues/420
This commit is contained in:
parent
d5a5bd7f05
commit
05657bf8b3
2 changed files with 58 additions and 92 deletions
|
|
@ -236,7 +236,7 @@ export type SettingProfileModel = { name: string; java_id: number | null; res: R
|
|||
|
||||
export type Settings = { global_game_settings: SettingProfileModel; allow_parallel_running_clusters: boolean; enable_gamemode: boolean; discord_enabled: boolean; seen_onboarding: boolean; mod_list_use_grid: boolean; parallel_mod_downloading: boolean; max_concurrent_requests: number; settings_version: number; native_window_frame: boolean; show_tanstack_dev_tools: boolean }
|
||||
|
||||
export type SettingsOsExtra = Record<string, never>
|
||||
export type SettingsOsExtra = { enable_gamemode: boolean | null }
|
||||
|
||||
export type SkinVariant = "classic" | "slim"
|
||||
|
||||
|
|
@ -301,8 +301,19 @@ export type VersionType =
|
|||
*/
|
||||
"old_beta"
|
||||
|
||||
const ARGS_MAP = { 'debug':'{"getType":[],"getGitCommitHash":[],"getBuildTimestamp":[],"getPlatform":[],"getOsVersion":[],"getArch":[],"getPackageVersion":[],"openDevTools":[],"getFamily":[],"isInDev":[],"getLocale":[]}', 'oneclient':'{"getClustersGroupedByMajor":[],"getVersions":[],"getBundlesFor":["cluster_id"]}', 'events':'{"message":["event"],"process":["event"],"ingress":["event"]}', 'core':'{"getScreenshots":["id"],"getPackageBody":["provider","body"],"convertUsernameUUID":["username_uuid"],"writeSettings":["setting"],"createCluster":["options"],"createSettingsProfile":["name"],"getMultiplePackages":["provider","slugs"],"updateClusterProfile":["name","profile"],"downloadExternalPackage":["package","cluster_id","force","skip_compatibility"],"openMsaLogin":[],"getClusters":[],"installModpack":["modpack","cluster_id"],"fetchMinecraftProfile":["uuid"],"killProcess":["pid"],"getUsers":[],"searchPackages":["provider","query"],"readSettings":[],"getLoadersForVersion":["mc_version"],"getRunningProcessesByClusterId":["cluster_id"],"getProfileOrDefault":["name"],"uploadSkinBytes":["access_token","skin_data","image_format","skin_variant"],"getUser":["uuid"],"getRunningProcesses":[],"fetchLoggedInProfile":["access_token"],"getLogs":["id"],"isClusterRunning":["cluster_id"],"getWorlds":["id"],"removeCape":["access_token"],"updateClusterById":["id","request"],"getGameVersions":[],"launchCluster":["id","uuid"],"removeCluster":["id"],"getPackageVersions":["provider","slug","mc_version","loader","offset","limit"],"getLinkedPackages":["cluster_id"],"getClusterById":["id"],"downloadPackage":["provider","package_id","version_id","cluster_id","skip_compatibility"],"removeUser":["uuid"],"changeSkin":["access_token","skin_url","skin_variant"],"getUsersFromAuthor":["provider","author"],"getGlobalProfile":[],"changeCape":["access_token","cape_uuid"],"getLogByName":["id","name"],"getDefaultUser":["fallback"],"setDiscordRPCMessage":["message"],"getPackage":["provider","slug"],"open":["input"],"setDefaultUser":["uuid"]}', 'folders':'{"openCluster":["folder_name"],"fromCluster":["folder_name"]}' }
|
||||
export type Router = { 'events': { ingress: (event: IngressPayload) => Promise<void>,
|
||||
const ARGS_MAP = { 'core':'{"changeCape":["access_token","cape_uuid"],"searchPackages":["provider","query"],"getWorlds":["id"],"getUsersFromAuthor":["provider","author"],"getRunningProcesses":[],"getDefaultUser":["fallback"],"writeSettings":["setting"],"getMultiplePackages":["provider","slugs"],"getUser":["uuid"],"downloadPackage":["provider","package_id","version_id","cluster_id","skip_compatibility"],"updateClusterProfile":["name","profile"],"setDefaultUser":["uuid"],"isClusterRunning":["cluster_id"],"changeSkin":["access_token","skin_url","skin_variant"],"getLoadersForVersion":["mc_version"],"removeCape":["access_token"],"open":["input"],"removeUser":["uuid"],"getUsers":[],"getGlobalProfile":[],"setDiscordRPCMessage":["message"],"getClusterById":["id"],"launchCluster":["id","uuid"],"getRunningProcessesByClusterId":["cluster_id"],"getScreenshots":["id"],"updateClusterById":["id","request"],"killProcess":["pid"],"installModpack":["modpack","cluster_id"],"openMsaLogin":[],"getProfileOrDefault":["name"],"getLinkedPackages":["cluster_id"],"getPackageBody":["provider","body"],"getPackage":["provider","slug"],"removeCluster":["id"],"readSettings":[],"getPackageVersions":["provider","slug","mc_version","loader","offset","limit"],"downloadExternalPackage":["package","cluster_id","force","skip_compatibility"],"fetchMinecraftProfile":["uuid"],"createCluster":["options"],"getLogs":["id"],"getClusters":[],"uploadSkinBytes":["access_token","skin_data","image_format","skin_variant"],"convertUsernameUUID":["username_uuid"],"getLogByName":["id","name"],"createSettingsProfile":["name"],"getGameVersions":[],"fetchLoggedInProfile":["access_token"]}', 'events':'{"message":["event"],"process":["event"],"ingress":["event"]}', 'folders':'{"fromCluster":["folder_name"],"openCluster":["folder_name"]}', 'oneclient':'{"getBundlesFor":["cluster_id"],"getVersions":[],"getClustersGroupedByMajor":[]}', 'debug':'{"openDevTools":[],"getOsVersion":[],"isInDev":[],"getGitCommitHash":[],"getBuildTimestamp":[],"getPackageVersion":[],"getLocale":[],"getType":[],"getFamily":[],"getArch":[],"getPlatform":[]}' }
|
||||
export type Router = { 'debug': { openDevTools: () => Promise<void>,
|
||||
isInDev: () => Promise<boolean>,
|
||||
getArch: () => Promise<string>,
|
||||
getFamily: () => Promise<string>,
|
||||
getLocale: () => Promise<string | null>,
|
||||
getType: () => Promise<string>,
|
||||
getPlatform: () => Promise<string>,
|
||||
getOsVersion: () => Promise<string>,
|
||||
getGitCommitHash: () => Promise<string>,
|
||||
getBuildTimestamp: () => Promise<string>,
|
||||
getPackageVersion: () => Promise<string> },
|
||||
'events': { ingress: (event: IngressPayload) => Promise<void>,
|
||||
message: (event: MessagePayload) => Promise<void>,
|
||||
process: (event: ProcessPayload) => Promise<void> },
|
||||
'oneclient': { getClustersGroupedByMajor: () => Promise<Partial<{ [key in number]: ClusterModel[] }>>,
|
||||
|
|
@ -355,17 +366,6 @@ removeCape: (accessToken: string) => Promise<MojangFullPlayerProfile>,
|
|||
convertUsernameUUID: (usernameUuid: string) => Promise<MowojangProfile>,
|
||||
setDiscordRPCMessage: (message: string) => Promise<null>,
|
||||
open: (input: string) => Promise<null> },
|
||||
'debug': { openDevTools: () => Promise<void>,
|
||||
isInDev: () => Promise<boolean>,
|
||||
getArch: () => Promise<string>,
|
||||
getFamily: () => Promise<string>,
|
||||
getLocale: () => Promise<string | null>,
|
||||
getType: () => Promise<string>,
|
||||
getPlatform: () => Promise<string>,
|
||||
getOsVersion: () => Promise<string>,
|
||||
getGitCommitHash: () => Promise<string>,
|
||||
getBuildTimestamp: () => Promise<string>,
|
||||
getPackageVersion: () => Promise<string> },
|
||||
'folders': { fromCluster: (folderName: string) => Promise<string>,
|
||||
openCluster: (folderName: string) => Promise<null> } };
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
use std::collections::{HashMap, VecDeque};
|
||||
use std::collections::HashMap;
|
||||
use std::fmt::Debug;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
use tauri::{AppHandle, Manager};
|
||||
use tokio::sync::{Mutex, Notify};
|
||||
use tokio::time::Instant;
|
||||
use tokio::sync::mpsc;
|
||||
use tokio::time::MissedTickBehavior;
|
||||
use tracing::{error, warn};
|
||||
use uuid::Uuid;
|
||||
|
||||
|
|
@ -33,28 +33,13 @@ impl ProxyTauri {
|
|||
tauri::webview_version().unwrap_or_else(|_| "unknown".into())
|
||||
);
|
||||
|
||||
let emitter = Arc::new(LauncherEventEmitter::new(handle.clone()));
|
||||
|
||||
Self {
|
||||
emitter: Arc::new(LauncherEventEmitter::new(handle.clone())),
|
||||
emitter: Arc::clone(&emitter),
|
||||
handle,
|
||||
ingress_queue: IngressQueue::new(),
|
||||
ingress_queue: IngressQueue::new(Arc::clone(&emitter), Duration::from_millis(100)),
|
||||
}
|
||||
.init()
|
||||
}
|
||||
|
||||
fn init(self) -> Self {
|
||||
let ingress_queue = Arc::clone(&self.ingress_queue);
|
||||
let emitter = Arc::clone(&self.emitter);
|
||||
|
||||
// spawn the processor on another thread
|
||||
tokio::spawn(async move {
|
||||
ingress_queue
|
||||
.process_queue(move |payload| {
|
||||
let _ = emitter.ingress(payload);
|
||||
})
|
||||
.await;
|
||||
});
|
||||
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -71,7 +56,7 @@ impl LauncherProxy for ProxyTauri {
|
|||
|
||||
Ok(match event {
|
||||
LauncherEvent::Ingress(ingress) => {
|
||||
self.ingress_queue.push(ingress).await;
|
||||
self.ingress_queue.push(ingress);
|
||||
|
||||
()
|
||||
}
|
||||
|
|
@ -101,71 +86,52 @@ impl LauncherProxy for ProxyTauri {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct IngressQueue {
|
||||
queue: Mutex<VecDeque<IngressPayload>>,
|
||||
last_sent: Mutex<HashMap<Uuid, Instant>>,
|
||||
notify: Notify,
|
||||
debounce: Duration,
|
||||
}
|
||||
|
||||
impl Debug for IngressQueue {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("IngressQueue")
|
||||
.field("debounce", &self.debounce)
|
||||
.finish()
|
||||
}
|
||||
tx: mpsc::UnboundedSender<IngressPayload>,
|
||||
}
|
||||
|
||||
impl IngressQueue {
|
||||
pub fn new() -> Arc<Self> {
|
||||
Arc::new(Self {
|
||||
queue: Mutex::new(VecDeque::new()),
|
||||
last_sent: Mutex::new(HashMap::new()),
|
||||
notify: Notify::new(),
|
||||
debounce: Duration::from_millis(100),
|
||||
})
|
||||
}
|
||||
pub fn new(
|
||||
emitter: Arc<LauncherEventEmitter<tauri::Wry>>,
|
||||
debounce_duration: Duration,
|
||||
) -> Arc<Self> {
|
||||
let (tx, mut rx) = mpsc::unbounded_channel::<IngressPayload>();
|
||||
|
||||
pub async fn push(&self, payload: IngressPayload) {
|
||||
let now = Instant::now();
|
||||
tokio::spawn(async move {
|
||||
let mut pending: HashMap<Uuid, IngressPayload> = HashMap::new();
|
||||
|
||||
// check last sent time for debouncing
|
||||
let mut last_sent = self.last_sent.lock().await;
|
||||
if let Some(&instant) = last_sent.get(&payload.id) {
|
||||
if now.duration_since(instant) < self.debounce {
|
||||
// skip update or merge with existing queued message
|
||||
let mut queue = self.queue.lock().await;
|
||||
if let Some(existing) = queue.iter_mut().find(|q| q.id == payload.id) {
|
||||
existing.percent = payload.percent.or(existing.percent);
|
||||
existing.message = payload.message;
|
||||
return;
|
||||
let mut tick = tokio::time::interval(debounce_duration);
|
||||
tick.set_missed_tick_behavior(MissedTickBehavior::Skip);
|
||||
|
||||
loop {
|
||||
tokio::select! {
|
||||
Some(payload) = rx.recv() => {
|
||||
// last write wins
|
||||
pending.insert(payload.id, payload);
|
||||
}
|
||||
|
||||
_ = tick.tick() => {
|
||||
if pending.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (_, payload) in pending.drain() {
|
||||
let emitter = Arc::clone(&emitter);
|
||||
tokio::spawn(async move {
|
||||
let _ = emitter.ingress(payload);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
last_sent.insert(payload.id, now);
|
||||
|
||||
let mut queue = self.queue.lock().await;
|
||||
queue.push_back(payload);
|
||||
self.notify.notify_one(); // wake processor
|
||||
Arc::new(Self { tx })
|
||||
}
|
||||
|
||||
pub async fn process_queue<F>(self: Arc<Self>, mut sender: F)
|
||||
where
|
||||
F: FnMut(IngressPayload) + Send + 'static,
|
||||
{
|
||||
loop {
|
||||
let queued = {
|
||||
let mut queue = self.queue.lock().await;
|
||||
queue.pop_front()
|
||||
};
|
||||
|
||||
if let Some(payload) = queued {
|
||||
sender(payload);
|
||||
} else {
|
||||
// wait until next push, no CPU spin
|
||||
self.notify.notified().await;
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
pub fn push(&self, payload: IngressPayload) {
|
||||
let _ = self.tx.send(payload);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue