Initial Fyrox project

This commit is contained in:
Alexey 2025-07-08 17:43:03 +03:00
commit 27d327933e
23 changed files with 5633 additions and 0 deletions

3
.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
/target
*.log

4596
Cargo.lock generated Normal file

File diff suppressed because it is too large Load diff

26
Cargo.toml Normal file
View file

@ -0,0 +1,26 @@
[workspace]
members = ["editor", "executor", "executor-wasm", "executor-android", "game", "game-dylib"]
resolver = "2"
[workspace.dependencies.fyrox]
version = " 0.36.2"
default-features = false
[workspace.dependencies.fyroxed_base]
version = " 0.36.2"
default-features = false
# Separate build profiles for hot reloading. These profiles ensures that build artifacts for
# hot reloading will be placed into their own folders and does not interfere with standard (static)
# linking.
[profile.dev-hot-reload]
inherits = "dev"
[profile.release-hot-reload]
inherits = "release"
# Optimize the engine in debug builds, but leave project's code non-optimized.
# By using this technique, you can still debug you code, but engine will be fully
# optimized and debug builds won't be terribly slow. With this option, you can
# compile your game in debug mode, which is much faster (at least x3), than release.
[profile.dev.package."*"]
opt-level = 3

BIN
data/scene.rgs Normal file

Binary file not shown.

14
editor/Cargo.toml Normal file
View file

@ -0,0 +1,14 @@
[package]
name = "editor"
version = "0.1.0"
edition = "2021"
[dependencies]
fyrox = { workspace = true }
fyroxed_base = { workspace = true }
red_dragon_pon = { path = "../game", optional = true }
[features]
default = ["red_dragon_pon", "fyroxed_base/default"]
dylib = ["fyroxed_base/dylib_engine"]

35
editor/src/main.rs Normal file
View file

@ -0,0 +1,35 @@
//! Editor with your game connected to it as a plugin.
use fyroxed_base::{fyrox::event_loop::EventLoop, Editor, StartupData, fyrox::core::log::Log};
fn main() {
Log::set_file_name("red_dragon_pon.log");
let event_loop = EventLoop::new().unwrap();
let mut editor = Editor::new(
Some(StartupData {
working_directory: Default::default(),
scenes: vec!["data/scene.rgs".into()],
}),
);
// Dynamic linking with hot reloading.
#[cfg(feature = "dylib")]
{
#[cfg(target_os = "windows")]
let file_name = "game_dylib.dll";
#[cfg(target_os = "linux")]
let file_name = "libgame_dylib.so";
#[cfg(target_os = "macos")]
let file_name = "libgame_dylib.dylib";
editor.add_dynamic_plugin(file_name, true, true).unwrap();
}
// Static linking.
#[cfg(not(feature = "dylib"))]
{
use red_dragon_pon::Game;
editor.add_game_plugin(Game::default());
}
editor.run(event_loop)
}

View file

@ -0,0 +1,27 @@
[package]
name = "executor-android"
version = "0.1.0"
edition = "2021"
[package.metadata.android]
# This folder is used as a temporary storage for assets. Project exporter will clone everything
# from data folder to this folder and cargo-apk will create the apk with these assets.
assets = "assets"
strip = "strip"
[package.metadata.android.sdk]
min_sdk_version = 26
target_sdk_version = 30
max_sdk_version = 29
[package.metadata.android.signing.release]
path = "release.keystore"
keystore_password = "fyrox-template"
[lib]
crate-type = ["cdylib"]
[dependencies]
fyrox = { workspace = true }
red_dragon_pon = { path = "../game" }

View file

@ -0,0 +1,5 @@
## Android Build Instructions
- `cargo-apk apk run --target=armv7-linux-androideabi`
TODO: Add more detailed instructions.

Binary file not shown.

View file

@ -0,0 +1,17 @@
//! Android executor with your game connected to it as a plugin.
use fyrox::{
core::io, engine::executor::Executor, event_loop::EventLoopBuilder,
platform::android::EventLoopBuilderExtAndroid,
};
use red_dragon_pon::Game;
#[no_mangle]
fn android_main(app: fyrox::platform::android::activity::AndroidApp) {
io::ANDROID_APP
.set(app.clone())
.expect("ANDROID_APP cannot be set twice.");
let event_loop = EventLoopBuilder::new().with_android_app(app).build().unwrap();
let mut executor = Executor::from_params(event_loop, Default::default());
executor.add_plugin(Game::default());
executor.run()
}

12
executor-wasm/Cargo.toml Normal file
View file

@ -0,0 +1,12 @@
[package]
name = "executor-wasm"
version = "0.1.0"
edition = "2021"
[lib]
crate-type = ["cdylib", "rlib"]
[dependencies]
fyrox = {workspace = true}
red_dragon_pon = { path = "../game" }

14
executor-wasm/README.md Normal file
View file

@ -0,0 +1,14 @@
## Build instructions
1. Make sure you have `wasm32-unknown-unknown` target installed in rustup (if not, do: `rustup target add wasm32-unknown-unknown`)
2. Make sure you have `wasm-pack` installed (if not, do: `cargo install wasm-pack`)
3. To build the executor, do: `wasm-pack build --target web --release`
## How to run the game on localhost
1. Make sure you have `basic-http-server` installed (if not, do: `cargo install basic-http-server`).
2. Clone assets to the `executor-wasm` directory. Alternatively, clone everything except `Cargo.toml` and `src` directory
to the root of your project (`../`).
3. Execute `basic-http-server` in `executor-wasm` directory (or in root folder if you you've used alternative path).
If everything has succeeded, open a web browser at http://localhost:4000/, click "Start" button and your game shoud load.

42
executor-wasm/index.html Normal file
View file

@ -0,0 +1,42 @@
<!--
~ Copyright (c) 2019-present Dmitry Stepanov and Fyrox Engine contributors.
~
~ Permission is hereby granted, free of charge, to any person obtaining a copy
~ of this software and associated documentation files (the "Software"), to deal
~ in the Software without restriction, including without limitation the rights
~ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
~ copies of the Software, and to permit persons to whom the Software is
~ furnished to do so, subject to the following conditions:
~
~ The above copyright notice and this permission notice shall be included in all
~ copies or substantial portions of the Software.
~
~ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
~ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
~ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
~ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
~ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
~ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
~ SOFTWARE.
-->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>My Game</title>
<link rel="stylesheet" href="styles.css" />
<script type="module" defer src="main.js"></script>
</head>
<body>
<noscript>This page contains WebAssembly and JavaScript content, please enable JavaScript in your browser.</noscript>
<main id="main">
<button class="button-3d" id="button-start" type="button" role="button">
Start
</button>
</main>
</body>
</html>

45
executor-wasm/main.js Normal file
View file

@ -0,0 +1,45 @@
/*
* Copyright (c) 2019-present Dmitry Stepanov and Fyrox Engine contributors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
const moduleGame = import('./pkg/executor_wasm.js').then(({ default: init, main }) =>
init().then(() => main)
)
const elementTargetButton = document.querySelector('#button-start')
const elementMain = document.querySelector('#main')
const run = async () => {
elementTargetButton.removeEventListener('click', run)
elementMain.remove()
const context = new AudioContext()
if (context.state !== 'running') {
await context.resume()
}
return (await moduleGame)()
}
elementTargetButton.addEventListener('click', run, {
once: true,
passive: true,
})

45
executor-wasm/src/lib.rs Normal file
View file

@ -0,0 +1,45 @@
//! Executor with your game connected to it as a plugin.
use fyrox::engine::executor::Executor;
use red_dragon_pon::Game;
use fyrox::core::wasm_bindgen::{self, prelude::*};
#[wasm_bindgen]
extern "C" {
#[wasm_bindgen(js_namespace = console)]
fn error(msg: String);
type Error;
#[wasm_bindgen(constructor)]
fn new() -> Error;
#[wasm_bindgen(structural, method, getter)]
fn stack(error: &Error) -> String;
}
fn custom_panic_hook(info: &std::panic::PanicHookInfo) {
let mut msg = info.to_string();
msg.push_str("\n\nStack:\n\n");
let e = Error::new();
let stack = e.stack();
msg.push_str(&stack);
msg.push_str("\n\n");
error(msg);
}
#[inline]
pub fn set_panic_hook() {
use std::sync::Once;
static SET_HOOK: Once = Once::new();
SET_HOOK.call_once(|| {
std::panic::set_hook(Box::new(custom_panic_hook));
});
}
#[wasm_bindgen]
pub fn main() {
set_panic_hook();
let mut executor = Executor::new();
executor.add_plugin(Game::default());
executor.run()
}

70
executor-wasm/styles.css Normal file
View file

@ -0,0 +1,70 @@
/*
* Copyright (c) 2019-present Dmitry Stepanov and Fyrox Engine contributors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
html {
box-sizing: border-box;
}
*,
*:before,
*:after {
box-sizing: inherit;
}
body {
height: 100vh;
width: 100vw;
padding: 0;
margin: 0;
position: relative;
/* Need to exclude the scrollbar */
min-width: calc(100vw - (100vw - 100%));
overflow: hidden;
}
#main {
height: 100%;
width: 100%;
justify-content: center;
display: flex;
align-items: center;
flex-direction: column;
}
.button-3d {
display: block;
position: relative;
margin: 0.5em 0;
padding: 0.8em 2.2em;
cursor: pointer;
background: #fff;
border: none;
border-radius: 0.4em;
text-transform: uppercase;
font-size: 1.4em;
font-family: 'Work Sans', sans-serif;
font-weight: 500;
letter-spacing: 0.04em;
mix-blend-mode: color-dodge;
perspective: 500px;
transform-style: preserve-3d;
background-color: yellowgreen;
}

13
executor/Cargo.toml Normal file
View file

@ -0,0 +1,13 @@
[package]
name = "executor"
version = "0.1.0"
edition = "2021"
[dependencies]
fyrox = { workspace = true }
red_dragon_pon = { path = "../game", optional = true }
[features]
default = ["red_dragon_pon"]
dylib = ["fyrox/dylib"]

30
executor/src/main.rs Normal file
View file

@ -0,0 +1,30 @@
//! Executor with your game connected to it as a plugin.
use fyrox::engine::executor::Executor;
use fyrox::core::log::Log;
fn main() {
Log::set_file_name("red_dragon_pon.log");
let mut executor = Executor::new();
// Dynamic linking with hot reloading.
#[cfg(feature = "dylib")]
{
#[cfg(target_os = "windows")]
let file_name = "game_dylib.dll";
#[cfg(target_os = "linux")]
let file_name = "libgame_dylib.so";
#[cfg(target_os = "macos")]
let file_name = "libgame_dylib.dylib";
executor.add_dynamic_plugin(file_name, true, true).unwrap();
}
// Static linking.
#[cfg(not(feature = "dylib"))]
{
use red_dragon_pon::Game;
executor.add_plugin(Game::default());
}
executor.run()
}

15
game-dylib/Cargo.toml Normal file
View file

@ -0,0 +1,15 @@
[package]
name = "game_dylib"
version = "0.1.0"
edition = "2021"
[lib]
crate-type = ["cdylib"]
[dependencies]
red_dragon_pon = { path = "../game", default-features = false }
[features]
default = ["red_dragon_pon/default"]
dylib-engine = ["red_dragon_pon/dylib-engine"]

7
game-dylib/src/lib.rs Normal file
View file

@ -0,0 +1,7 @@
//! Wrapper for hot-reloadable plugin.
use red_dragon_pon::{fyrox::plugin::Plugin, Game};
#[no_mangle]
pub fn fyrox_plugin() -> Box<dyn Plugin> {
Box::new(Game::default())
}

12
game/Cargo.toml Normal file
View file

@ -0,0 +1,12 @@
[package]
name = "red_dragon_pon"
version = "0.1.0"
edition = "2021"
[dependencies]
fyrox = {workspace = true}
[features]
default = ["fyrox/default"]
dylib-engine = ["fyrox/dylib"]

69
game/src/lib.rs Normal file
View file

@ -0,0 +1,69 @@
//! Game project.
use fyrox::{
core::pool::Handle, core::visitor::prelude::*, core::reflect::prelude::*,
event::Event,
gui::message::UiMessage,
plugin::{Plugin, PluginContext, PluginRegistrationContext},
scene::Scene,
};
use std::path::Path;
// Re-export the engine.
pub use fyrox;
#[derive(Default, Visit, Reflect, Debug)]
pub struct Game {
scene: Handle<Scene>,
}
impl Plugin for Game {
fn register(&self, _context: PluginRegistrationContext) {
// Register your scripts here.
}
fn init(&mut self, scene_path: Option<&str>, context: PluginContext) {
context
.async_scene_loader
.request(scene_path.unwrap_or("data/scene.rgs"));
}
fn on_deinit(&mut self, _context: PluginContext) {
// Do a cleanup here.
}
fn update(&mut self, _context: &mut PluginContext) {
// Add your global update code here.
}
fn on_os_event(
&mut self,
_event: &Event<()>,
_context: PluginContext,
) {
// Do something on OS event here.
}
fn on_ui_message(
&mut self,
_context: &mut PluginContext,
_message: &UiMessage,
) {
// Handle UI events here.
}
fn on_scene_begin_loading(&mut self, _path: &Path, ctx: &mut PluginContext) {
if self.scene.is_some() {
ctx.scenes.remove(self.scene);
}
}
fn on_scene_loaded(
&mut self,
_path: &Path,
scene: Handle<Scene>,
_data: &[u8],
_context: &mut PluginContext,
) {
self.scene = scene;
}
}

536
settings.ron Normal file
View file

@ -0,0 +1,536 @@
(
selection: (
ignore_back_faces: false,
track_selection: true,
),
graphics: (
quality: (
point_shadow_map_size: 1024,
point_soft_shadows: true,
point_shadows_enabled: true,
point_shadows_distance: 15.0,
point_shadow_map_precision: Full,
point_shadows_fade_out_range: 1.0,
spot_shadow_map_size: 1024,
spot_soft_shadows: true,
spot_shadows_enabled: true,
spot_shadows_distance: 15.0,
spot_shadow_map_precision: Full,
spot_shadows_fade_out_range: 1.0,
csm_settings: (
enabled: true,
size: 2048,
precision: Full,
pcf: true,
),
use_ssao: true,
ssao_radius: 0.5,
light_scatter_enabled: true,
fxaa: true,
use_parallax_mapping: true,
use_bloom: true,
use_occlusion_culling: false,
use_light_occlusion_culling: false,
),
z_near: 0.025,
z_far: 128.0,
draw_grid: true,
),
build: (
selected_profile: 0,
profiles: [
(
name: "Debug",
build_commands: [
(
command: "cargo",
args: [
"build",
"--package",
"executor",
],
environment_variables: [],
),
],
run_command: (
command: "cargo",
args: [
"run",
"--package",
"executor",
],
environment_variables: [],
),
),
(
name: "Debug (HR)",
build_commands: [
(
command: "cargo",
args: [
"build",
"--package",
"game_dylib",
"--no-default-features",
"--features",
"dylib-engine",
"--profile",
"dev-hot-reload",
],
environment_variables: [
(
name: "RUSTFLAGS",
value: "-C prefer-dynamic=yes",
),
],
),
(
command: "cargo",
args: [
"build",
"--package",
"executor",
"--no-default-features",
"--features",
"dylib",
"--profile",
"dev-hot-reload",
],
environment_variables: [
(
name: "RUSTFLAGS",
value: "-C prefer-dynamic=yes",
),
],
),
],
run_command: (
command: "cargo",
args: [
"run",
"--package",
"executor",
"--no-default-features",
"--features",
"dylib",
"--profile",
"dev-hot-reload",
],
environment_variables: [
(
name: "RUSTFLAGS",
value: "-C prefer-dynamic=yes",
),
],
),
),
(
name: "Release",
build_commands: [
(
command: "cargo",
args: [
"build",
"--package",
"executor",
"--release",
],
environment_variables: [],
),
],
run_command: (
command: "cargo",
args: [
"run",
"--package",
"executor",
"--release",
],
environment_variables: [],
),
),
(
name: "Release (HR)",
build_commands: [
(
command: "cargo",
args: [
"build",
"--package",
"executor",
"--release",
"--release",
],
environment_variables: [],
),
],
run_command: (
command: "cargo",
args: [
"run",
"--package",
"executor",
"--release",
"--release",
],
environment_variables: [],
),
),
],
),
general: (
show_node_removal_dialog: true,
suspend_unfocused_editor: true,
script_editor: SystemDefault,
max_history_entries: 512,
generate_previews: true,
max_log_entries: 256,
style: Dark,
),
debugging: (
show_physics: true,
show_bounds: true,
show_tbn: false,
show_terrains: false,
show_light_bounds: true,
show_camera_bounds: true,
pictogram_size: 0.33,
save_scene_in_text_form: false,
),
move_mode_settings: (
grid_snapping: false,
x_snap_step: 0.05,
y_snap_step: 0.05,
z_snap_step: 0.05,
),
rotate_mode_settings: (
angle_snapping: false,
x_snap_step: 2.5,
y_snap_step: 2.5,
z_snap_step: 2.5,
),
model: (
instantiation_scale: (1.0, 1.0, 1.0),
),
camera: (
speed: 10.0,
zoom_speed: 0.5,
zoom_range: (
start: 0.0,
end: 100.0,
),
),
navmesh: (
draw_all: true,
vertex_radius: 0.2,
),
key_bindings: (
move_forward: Some(KeyW),
move_back: Some(KeyS),
move_left: Some(KeyA),
move_right: Some(KeyD),
move_up: Some(KeyE),
move_down: Some(KeyQ),
speed_up: Some(ControlLeft),
slow_down: Some(ShiftLeft),
undo: Some(
code: KeyZ,
modifiers: (
alt: false,
shift: false,
control: true,
system: false,
),
),
redo: Some(
code: KeyY,
modifiers: (
alt: false,
shift: false,
control: true,
system: false,
),
),
enable_select_mode: Some(
code: Digit1,
modifiers: (
alt: false,
shift: false,
control: false,
system: false,
),
),
enable_move_mode: Some(
code: Digit2,
modifiers: (
alt: false,
shift: false,
control: false,
system: false,
),
),
enable_rotate_mode: Some(
code: Digit3,
modifiers: (
alt: false,
shift: false,
control: false,
system: false,
),
),
enable_scale_mode: Some(
code: Digit4,
modifiers: (
alt: false,
shift: false,
control: false,
system: false,
),
),
enable_navmesh_mode: Some(
code: Digit5,
modifiers: (
alt: false,
shift: false,
control: false,
system: false,
),
),
enable_terrain_mode: Some(
code: Digit6,
modifiers: (
alt: false,
shift: false,
control: false,
system: false,
),
),
save_scene: Some(
code: KeyS,
modifiers: (
alt: false,
shift: false,
control: true,
system: false,
),
),
save_scene_as: Some(
code: KeyS,
modifiers: (
alt: false,
shift: true,
control: true,
system: false,
),
),
save_all_scenes: Some(
code: KeyS,
modifiers: (
alt: true,
shift: false,
control: true,
system: false,
),
),
load_scene: Some(
code: KeyL,
modifiers: (
alt: false,
shift: false,
control: true,
system: false,
),
),
copy_selection: Some(
code: KeyC,
modifiers: (
alt: false,
shift: false,
control: true,
system: false,
),
),
paste: Some(
code: KeyV,
modifiers: (
alt: false,
shift: false,
control: true,
system: false,
),
),
new_scene: Some(
code: KeyN,
modifiers: (
alt: false,
shift: false,
control: true,
system: false,
),
),
close_scene: Some(
code: KeyQ,
modifiers: (
alt: false,
shift: false,
control: true,
system: false,
),
),
remove_selection: Some(
code: Delete,
modifiers: (
alt: false,
shift: false,
control: false,
system: false,
),
),
focus: Some(
code: KeyF,
modifiers: (
alt: false,
shift: false,
control: false,
system: false,
),
),
terrain_key_bindings: (
modify_height_map_mode: Some(
code: F1,
modifiers: (
alt: false,
shift: false,
control: false,
system: false,
),
),
draw_on_mask_mode: Some(
code: F2,
modifiers: (
alt: false,
shift: false,
control: false,
system: false,
),
),
flatten_slopes_mode: Some(
code: F3,
modifiers: (
alt: false,
shift: false,
control: false,
system: false,
),
),
increase_brush_size: Some(
code: BracketRight,
modifiers: (
alt: false,
shift: false,
control: false,
system: false,
),
),
decrease_brush_size: Some(
code: BracketLeft,
modifiers: (
alt: false,
shift: false,
control: false,
system: false,
),
),
increase_brush_opacity: Some(
code: Period,
modifiers: (
alt: false,
shift: false,
control: false,
system: false,
),
),
decrease_brush_opacity: Some(
code: Comma,
modifiers: (
alt: false,
shift: false,
control: false,
system: false,
),
),
prev_layer: Some(
code: Semicolon,
modifiers: (
alt: false,
shift: false,
control: false,
system: false,
),
),
next_layer: Some(
code: Quote,
modifiers: (
alt: false,
shift: false,
control: false,
system: false,
),
),
),
run_game: Some(
code: F5,
modifiers: (
alt: false,
shift: false,
control: false,
system: false,
),
),
),
scene_settings: {
"data/scene.rgs": (
camera_settings: (
position: (-1.2582655, 0.37696767, -0.41422594),
yaw: 2.317344,
pitch: 0.2702732,
projection: Perspective((
fov: 1.3089969,
z_near: 0.025,
z_far: 128.0,
)),
),
node_infos: {
(
index: 1,
generation: 1,
): (
is_expanded: true,
),
(
index: 2,
generation: 1,
): (
is_expanded: true,
),
(
index: 3,
generation: 1,
): (
is_expanded: true,
),
},
),
},
recent: (
scenes: [
"data/scene.rgs",
],
),
windows: (
window_position: (0.0, 0.0),
window_size: (979.0, 505.0),
window_maximized: false,
layout: None,
),
)