game process now run on a separate thread

This commit is contained in:
ALEZ-DEV 2024-12-18 21:29:11 +01:00
parent 85bb1e2c59
commit a2e0b6315f
3 changed files with 162 additions and 105 deletions

View File

@ -1,112 +1,11 @@
use babylonia_terminal_sdk::game_state::GameState;
use log::debug;
use manager::run_game;
use relm4::{
gtk::{
self,
prelude::{BoxExt, ButtonExt, GtkWindowExt, OrientableExt, WidgetExt},
},
loading_widgets::LoadingWidgets,
prelude::{AsyncComponent, AsyncComponentParts, SimpleAsyncComponent},
view, ComponentParts, RelmWidgetExt, SimpleComponent,
};
use relm4::{Component, RelmApp};
use relm4::RelmApp;
mod manager;
mod ui;
pub fn run() {
debug!("Start GUI!");
let app = RelmApp::new("moe.celica.BabyloniaTerminal").with_args(vec![]);
app.run_async::<MainWindow>(None);
}
#[derive(Debug)]
pub enum MainWindowMsg {
RunGame,
}
struct MainWindow {
game_state: GameState,
is_game_running: bool,
}
impl MainWindow {
fn new(game_state: GameState) -> Self {
MainWindow {
game_state,
is_game_running: false,
}
}
}
#[relm4::component(async)]
impl SimpleAsyncComponent for MainWindow {
type Input = MainWindowMsg;
type Output = ();
type Init = Option<GameState>;
view! {
#[root]
gtk::Window {
gtk::Box {
set_orientation: gtk::Orientation::Vertical,
set_spacing: 5,
set_margin_all: 5,
#[name(start_button)]
gtk::Button {
set_label: "Start game",
connect_clicked => MainWindowMsg::RunGame,
},
}
}
}
async fn init(
game_state: Self::Init,
root: Self::Root,
sender: relm4::AsyncComponentSender<Self>,
) -> AsyncComponentParts<Self> {
let model;
if game_state.is_none() {
model = MainWindow::new(
babylonia_terminal_sdk::game_state::GameState::get_current_state()
.await
.unwrap(),
);
} else {
model = MainWindow::new(game_state.unwrap());
}
let widgets = view_output!();
AsyncComponentParts { model, widgets }
}
fn init_loading_widgets(root: Self::Root) -> Option<LoadingWidgets> {
view! {
#[local]
root {
set_title: Some("Babylonia Terminal"),
set_default_width: 700,
set_default_height: 300,
#[name(spinner)]
gtk::Spinner {
start: (),
set_halign: gtk::Align::Center,
}
}
}
Some(LoadingWidgets::new(root, spinner))
}
async fn update(&mut self, message: Self::Input, _sender: relm4::AsyncComponentSender<Self>) {
match message {
MainWindowMsg::RunGame => run_game().await,
}
}
ui::run(app);
}

View File

@ -3,9 +3,14 @@ use babylonia_terminal_sdk::{
game_manager::GameManager,
};
use log::error;
use relm4::tokio::sync::OnceCell;
use relm4::{
tokio::{self, sync::OnceCell},
Worker,
};
use wincompatlib::prelude::Proton;
use crate::ui;
static PROTON: OnceCell<Proton> = OnceCell::const_new();
pub async fn get_proton() -> Proton {
@ -31,3 +36,38 @@ pub async fn run_game() {
GameManager::start_game(&proton, game_dir.unwrap(), None, false).await;
}
#[derive(Debug)]
pub enum HandleGameProcessMsg {
RunGame,
}
pub struct HandleGameProcess;
impl Worker for HandleGameProcess {
type Init = ();
type Input = HandleGameProcessMsg;
type Output = ui::MainWindowMsg;
fn init(_init: Self::Init, _sender: relm4::ComponentSender<Self>) -> Self {
Self
}
fn update(&mut self, message: Self::Input, sender: relm4::ComponentSender<Self>) {
match message {
HandleGameProcessMsg::RunGame => {
tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.unwrap()
.block_on(async {
sender.output(ui::MainWindowMsg::SetIsGameRunning(true));
run_game().await;
sender.output(ui::MainWindowMsg::SetIsGameRunning(false));
});
}
}
}
}

View File

@ -0,0 +1,118 @@
use std::convert::identity;
use crate::manager;
use babylonia_terminal_sdk::game_state::GameState;
use relm4::{
gtk::{
self,
prelude::{BoxExt, ButtonExt, GtkWindowExt, OrientableExt, WidgetExt},
},
loading_widgets::LoadingWidgets,
prelude::{AsyncComponentParts, SimpleAsyncComponent},
view, Component, RelmApp, RelmWidgetExt, WorkerController,
};
pub fn run(app: RelmApp<MainWindowMsg>) {
app.run_async::<MainWindow>(None);
}
#[derive(Debug)]
pub enum MainWindowMsg {
SetIsGameRunning(bool),
}
struct MainWindow {
game_handler: WorkerController<manager::HandleGameProcess>,
game_state: GameState,
is_game_running: bool,
}
impl MainWindow {
fn new(game_state: GameState, sender: relm4::AsyncComponentSender<Self>) -> Self {
MainWindow {
game_state,
is_game_running: false,
game_handler: manager::HandleGameProcess::builder()
.detach_worker(())
.forward(sender.input_sender(), identity),
}
}
}
#[relm4::component(async)]
impl SimpleAsyncComponent for MainWindow {
type Input = MainWindowMsg;
type Output = ();
type Init = Option<GameState>;
view! {
#[root]
gtk::Window {
gtk::Box {
set_orientation: gtk::Orientation::Vertical,
set_spacing: 5,
set_margin_all: 5,
#[name(start_button)]
gtk::Button {
#[watch]
set_sensitive: !model.is_game_running,
set_label: "Start game",
connect_clicked[sender = model.game_handler.sender().clone()] => move |_| {
sender.send(manager::HandleGameProcessMsg::RunGame).unwrap();
},
},
}
}
}
async fn init(
game_state: Self::Init,
root: Self::Root,
sender: relm4::AsyncComponentSender<Self>,
) -> AsyncComponentParts<Self> {
let model;
if game_state.is_none() {
model = MainWindow::new(
babylonia_terminal_sdk::game_state::GameState::get_current_state()
.await
.unwrap(),
sender,
);
} else {
model = MainWindow::new(game_state.unwrap(), sender);
}
let widgets = view_output!();
AsyncComponentParts { model, widgets }
}
fn init_loading_widgets(root: Self::Root) -> Option<LoadingWidgets> {
view! {
#[local]
root {
set_title: Some("Babylonia Terminal"),
set_default_width: 700,
set_default_height: 300,
#[name(spinner)]
gtk::Spinner {
start: (),
set_halign: gtk::Align::Center,
}
}
}
Some(LoadingWidgets::new(root, spinner))
}
async fn update(&mut self, message: Self::Input, _sender: relm4::AsyncComponentSender<Self>) {
match message {
MainWindowMsg::SetIsGameRunning(value) => self.is_game_running = value,
}
}
}