Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions Engine/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Engine/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ members = [
"apps/setup-helper",
"crates/engine-assets",
"crates/engine-core",
"crates/engine-desktop",
"crates/engine-editor",
"crates/engine-play",
"crates/engine-platform",
Expand Down
10 changes: 10 additions & 0 deletions Engine/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,16 @@ or import a project and starts the editor as a separate native process.

## Windows installer

Installed Windows builds check GitHub Releases for updates on launch and hourly
while Rustic is open. Newer releases download automatically and prompt once per
release; a small **Update** title-bar button remains after dismissal. Updating
asks to save unsaved scenes, closes Rustic gracefully, then reopens its windows
and projects after installation succeeds. Updates after a normal exit do not
reopen the app. Windows may request
elevation for an all-users installation. Older builds need one manual upgrade to
receive the updater. See [Automatic desktop updates](docs/AUTOMATIC_UPDATES.md)
for verification, logs and source-build limitations.

From PowerShell, build a self-contained `.exe` installer with:

```powershell
Expand Down
1 change: 1 addition & 0 deletions Engine/apps/editor/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ egui_extras.workspace = true
eframe.workspace = true
engine-assets = { path = "../../crates/engine-assets", version = "0.1.0" }
engine-core = { path = "../../crates/engine-core", version = "0.1.0" }
engine-desktop = { path = "../../crates/engine-desktop", version = "0.1.0" }
engine-editor = { path = "../../crates/engine-editor", version = "0.1.0" }
engine-platform = { path = "../../crates/engine-platform", version = "0.1.0" }
engine-play = { path = "../../crates/engine-play", version = "0.1.0" }
Expand Down
69 changes: 69 additions & 0 deletions Engine/apps/editor/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,14 @@ fn main() -> ExitCode {
if let Err(error) = install_user_agent_integrations() {
eprintln!("Could not install user AI-agent skill/plugin: {error}");
}
if let Err(error) = engine_platform::start_desktop_updater() {
eprintln!("Could not start automatic updates: {error}");
}
let title = format!("{} — Rustic Editor", project.metadata().name);
let options = eframe::NativeOptions {
viewport: egui::ViewportBuilder::default()
.with_title(title)
.with_decorations(!cfg!(target_os = "windows"))
.with_inner_size([1_440.0, 900.0])
.with_min_inner_size([960.0, 600.0])
.with_icon(app_icon()),
Expand Down Expand Up @@ -118,6 +122,8 @@ fn app_icon() -> egui::IconData {
reason = "independent editor panel and interaction flags are not mutually exclusive states"
)]
struct EditorApp {
updates: engine_desktop::DesktopUpdates,
update_close_requested: bool,
document: AuthoringDocument,
project_lock: Option<engine_editor::ProjectLock>,
dock: Tree<EditorTab>,
Expand Down Expand Up @@ -223,6 +229,8 @@ impl EditorApp {
let missing_dependencies = missing_project_dependencies(document.project().root());
let show_dependency_prompt = !missing_dependencies.is_empty();
let mut app = Self {
updates: engine_desktop::DesktopUpdates::default(),
update_close_requested: false,
document,
project_lock,
dock,
Expand Down Expand Up @@ -608,6 +616,56 @@ impl EditorApp {
}
}

fn update_close_dialog(&mut self, ui: &mut egui::Ui) {
if !self.update_close_requested {
return;
}
if self.play.is_some() {
self.stop_play();
}
if self.pending_runtime_changes.is_some() {
return;
}
let mut ready = !self.document.is_modified();
let mut cancel = false;
if !ready {
egui::Window::new("Save before updating?")
.collapsible(false)
.resizable(false)
.anchor(egui::Align2::CENTER_CENTER, [0.0, 0.0])
.show(ui, |ui| {
ui.label(
"This scene has unsaved changes. Save it before installing the update.",
);
ui.horizontal(|ui| {
if ui.button("Save and update").clicked() {
self.save();
ready = !self.document.is_modified();
}
if ui.button("Cancel").clicked() {
cancel = true;
}
});
});
}
if cancel {
self.update_close_requested = false;
self.updates.cancel_install();
} else if ready {
if self
.updates
.request_install(Some(self.document.project().root()))
{
ui.ctx().send_viewport_cmd(egui::ViewportCommand::Close);
} else {
self.update_close_requested = false;
if let Some(error) = self.updates.error().map(str::to_owned) {
self.log(Severity::Error, "update", error);
}
}
}
}

fn autosave_if_needed(&mut self, scene_changed: bool) {
if !self.game_settings.autosave
|| self.document.is_read_only()
Expand Down Expand Up @@ -1286,6 +1344,16 @@ fn transform_mode_button(
impl eframe::App for EditorApp {
fn ui(&mut self, ui: &mut egui::Ui, _frame: &mut eframe::Frame) {
self.poll_background(ui.ctx());
let title = format!(
"{} — Rustic Editor",
self.document.project().metadata().name
);
if self.updates.show(ui, &title) {
self.update_close_requested = true;
}
if self.updates.take_cancellation() {
self.update_close_requested = false;
}
let scene_was_modified = self.document.is_modified();
egui::Panel::top("editor-toolbar")
.frame(
Expand Down Expand Up @@ -1360,6 +1428,7 @@ impl eframe::App for EditorApp {
self.runtime_changes_dialog(ui.ctx());
self.game_settings_dialog(ui.ctx());
self.dependency_prompt(ui.ctx());
self.update_close_dialog(ui);
let scene_changed = !scene_was_modified && self.document.is_modified();
self.autosave_if_needed(scene_changed);
}
Expand Down
1 change: 1 addition & 0 deletions Engine/apps/project-manager/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ publish = false
crossbeam-channel.workspace = true
eframe.workspace = true
engine-core = { path = "../../crates/engine-core", version = "0.1.0" }
engine-desktop = { path = "../../crates/engine-desktop", version = "0.1.0" }
engine-platform = { path = "../../crates/engine-platform", version = "0.1.0" }
engine-project = { path = "../../crates/engine-project", version = "0.1.0" }
engine-scripting = { path = "../../crates/engine-scripting", version = "0.1.0" }
Expand Down
14 changes: 14 additions & 0 deletions Engine/apps/project-manager/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,16 @@ fn run() -> Result<(), String> {
{
return Ok(());
}
if let Err(error) = engine_platform::start_desktop_updater() {
eprintln!("Could not start automatic updates: {error}");
}
let platform_paths = NativePlatformPaths::discover()
.map_err(|error| format!("could not discover platform paths: {error}"))?;
let app = ProjectManagerApp::new(platform_paths)?;
let options = eframe::NativeOptions {
viewport: egui::ViewportBuilder::default()
.with_title("Rustic Game Engine — Projects")
.with_decorations(!cfg!(target_os = "windows"))
.with_inner_size([1_080.0, 700.0])
.with_min_inner_size([760.0, 480.0])
.with_icon(app_icon()),
Expand Down Expand Up @@ -122,6 +126,7 @@ struct DialogVisibility {
}

struct ProjectManagerApp {
updates: engine_desktop::DesktopUpdates,
platform_paths: NativePlatformPaths,
process_launcher: NativeProcessLauncher,
model: ProjectManagerModel,
Expand Down Expand Up @@ -208,6 +213,7 @@ impl ProjectManagerApp {
),
);
Ok(Self {
updates: engine_desktop::DesktopUpdates::default(),
platform_paths,
process_launcher: NativeProcessLauncher,
model,
Expand Down Expand Up @@ -315,6 +321,14 @@ impl eframe::App for ProjectManagerApp {
};
ui_theme::apply(&context, dark);

if self.updates.show(ui, "Rustic Game Engine — Projects") {
if self.updates.request_install(None) {
context.send_viewport_cmd(egui::ViewportCommand::Close);
} else {
self.ui_status = self.updates.error().map(str::to_owned);
}
}

egui::Panel::top("project_manager_header")
.frame(
egui::Frame::new()
Expand Down
14 changes: 14 additions & 0 deletions Engine/crates/engine-desktop/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[package]
name = "engine-desktop"
description = "Shared desktop title bar and update notifications"
version.workspace = true
edition.workspace = true
rust-version.workspace = true
publish = false

[dependencies]
eframe.workspace = true
engine-platform = { path = "../engine-platform", version = "0.1.0" }

[lints]
workspace = true
Loading
Loading