0.1.0-1-alpha.2
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -63,3 +63,6 @@ __pycache__/
|
||||
*.jks
|
||||
*.keystore
|
||||
local.properties
|
||||
# Generated by scripts/build_reflex_tauri_wasm.py.
|
||||
Tauri/game-reflex-poc/frontend/game_reflex_poc_tauri.js
|
||||
Tauri/game-reflex-poc/frontend/game_reflex_poc_tauri_bg.wasm
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: Android/game-reflex-poc/build.gradle
|
||||
// version: 10
|
||||
// version: 11
|
||||
|
||||
plugins {
|
||||
id 'com.android.application'
|
||||
@@ -18,7 +18,7 @@ android {
|
||||
minSdk 21
|
||||
targetSdk 36
|
||||
versionCode 1
|
||||
versionName '0.1.0-1-alpha.1'
|
||||
versionName '0.1.0-1-alpha.2'
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: Android/game-snake-poc/build.gradle
|
||||
// version: 10
|
||||
// version: 11
|
||||
|
||||
plugins {
|
||||
id 'com.android.application'
|
||||
@@ -18,7 +18,7 @@ android {
|
||||
minSdk 21
|
||||
targetSdk 36
|
||||
versionCode 1
|
||||
versionName '0.1.0-1-alpha.1'
|
||||
versionName '0.1.0-1-alpha.2'
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# file: Cargo.toml
|
||||
# version: 27
|
||||
# version: 28
|
||||
|
||||
[workspace]
|
||||
resolver = "3"
|
||||
@@ -14,10 +14,11 @@ members = [
|
||||
"crates/common/game-assets-lib",
|
||||
"crates/common/game-logging-lib",
|
||||
"crates/apps/game-android-entrypoint",
|
||||
"crates/apps/game-reflex-poc-tauri",
|
||||
]
|
||||
|
||||
[workspace.package]
|
||||
version = "0.1.0-1-alpha.1"
|
||||
version = "0.1.0-1-alpha.2"
|
||||
edition = "2024"
|
||||
license = "MIT"
|
||||
repository = "https://git.sasedev.com/Sasedev/games"
|
||||
@@ -29,6 +30,9 @@ sdl3 = "^0.20"
|
||||
tracing = "0.1.44"
|
||||
tracing-appender = "0.2.5"
|
||||
tracing-subscriber = "0.3.23"
|
||||
tauri = "2"
|
||||
tauri-build = "2"
|
||||
wasm-bindgen = "0.2"
|
||||
|
||||
[workspace.lints.rust]
|
||||
missing_docs = "warn"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!-- file: ROADMAP.md -->
|
||||
<!-- version: 10 -->
|
||||
<!-- version: 11 -->
|
||||
|
||||
# Roadmap
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
- [x] `0-pre.9` — assets communs + spécifiques empaquetés sans copie dans les crates.
|
||||
- [x] `0-pre.10` — POC Reflex jouable Desktop + Android.
|
||||
- [x] `0-pre.11` — POC Snake jouable et validation de la réutilisation du moteur.
|
||||
- [ ] `1-alpha.1` — première API moteur V1 volontairement stabilisée et provenance runtime/input pour les futures sessions et scores.
|
||||
- [x] `1-alpha.1` — première API moteur V1 volontairement stabilisée et provenance runtime/input pour les futures sessions et scores.
|
||||
- [ ] `1-alpha.2` — POC Web/WASM embarqué dans Tauri, sans site distant, et validation de la frontière des services Web/ads.
|
||||
- [ ] `2-beta.1` — stabilisation, packaging, tests multi-appareils.
|
||||
- [ ] `3-rc.1` — candidat de release du socle 0.1.0.
|
||||
|
||||
10
Tauri/README.md
Normal file
10
Tauri/README.md
Normal file
@@ -0,0 +1,10 @@
|
||||
<!-- file: Tauri/README.md -->
|
||||
<!-- version: 1 -->
|
||||
|
||||
# Tauri
|
||||
|
||||
Cette arborescence contient les frontends statiques des variantes Desktop Tauri.
|
||||
|
||||
Le backend Rust reste une crate sous `crates/apps/`, conformément à la règle du workspace Cargo unique.
|
||||
|
||||
Les fichiers générés par `wasm-bindgen` ne sont pas versionnés.
|
||||
17
Tauri/game-reflex-poc/frontend/index.html
Normal file
17
Tauri/game-reflex-poc/frontend/index.html
Normal file
@@ -0,0 +1,17 @@
|
||||
<!doctype html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<title>Reflex POC Tauri</title>
|
||||
<link rel="stylesheet" href="./style.css">
|
||||
</head>
|
||||
<body>
|
||||
<main id="app">
|
||||
<canvas id="game" aria-label="Reflex POC"></canvas>
|
||||
<output id="score">Score : 0</output>
|
||||
<output id="runtime">Tauri · WebView · WASM</output>
|
||||
</main>
|
||||
<script type="module" src="./main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
76
Tauri/game-reflex-poc/frontend/main.js
Normal file
76
Tauri/game-reflex-poc/frontend/main.js
Normal file
@@ -0,0 +1,76 @@
|
||||
// file: Tauri/game-reflex-poc/frontend/main.js
|
||||
// version: 1
|
||||
|
||||
import init, { ReflexWasmGame } from "./game_reflex_poc_tauri.js";
|
||||
|
||||
const FRAME_MS = 16;
|
||||
|
||||
function color(red, green, blue) {
|
||||
return `rgb(${red} ${green} ${blue})`;
|
||||
}
|
||||
|
||||
function resizeCanvas(canvas) {
|
||||
const ratio = window.devicePixelRatio || 1;
|
||||
const width = Math.max(1, Math.floor(canvas.clientWidth * ratio));
|
||||
const height = Math.max(1, Math.floor(canvas.clientHeight * ratio));
|
||||
if (canvas.width !== width || canvas.height !== height) {
|
||||
canvas.width = width;
|
||||
canvas.height = height;
|
||||
}
|
||||
}
|
||||
|
||||
function render(game, canvas, context, score) {
|
||||
resizeCanvas(canvas);
|
||||
context.fillStyle = color(game.background_red(), game.background_green(), game.background_blue());
|
||||
context.fillRect(0, 0, canvas.width, canvas.height);
|
||||
|
||||
const count = game.rectangle_count();
|
||||
for (let index = 0; index < count; index += 1) {
|
||||
context.fillStyle = color(game.rectangle_red(index), game.rectangle_green(index), game.rectangle_blue(index));
|
||||
context.fillRect(
|
||||
game.rectangle_x(index) * canvas.width,
|
||||
game.rectangle_y(index) * canvas.height,
|
||||
game.rectangle_width(index) * canvas.width,
|
||||
game.rectangle_height(index) * canvas.height,
|
||||
);
|
||||
}
|
||||
|
||||
score.value = `Score : ${game.score()}`;
|
||||
}
|
||||
|
||||
async function start() {
|
||||
await init();
|
||||
|
||||
const canvas = document.querySelector("#game");
|
||||
const score = document.querySelector("#score");
|
||||
const context = canvas.getContext("2d");
|
||||
if (!(canvas instanceof HTMLCanvasElement) || !(score instanceof HTMLOutputElement) || context === null) {
|
||||
throw new Error("Reflex Tauri frontend DOM is incomplete");
|
||||
}
|
||||
|
||||
const game = new ReflexWasmGame();
|
||||
canvas.addEventListener("pointerdown", (event) => {
|
||||
const bounds = canvas.getBoundingClientRect();
|
||||
const x = (event.clientX - bounds.left) / bounds.width;
|
||||
const y = (event.clientY - bounds.top) / bounds.height;
|
||||
game.pointer_down(x, y);
|
||||
render(game, canvas, context, score);
|
||||
});
|
||||
|
||||
let previous = performance.now();
|
||||
function frame(now) {
|
||||
while (now - previous >= FRAME_MS) {
|
||||
game.tick();
|
||||
previous += FRAME_MS;
|
||||
}
|
||||
render(game, canvas, context, score);
|
||||
window.requestAnimationFrame(frame);
|
||||
}
|
||||
|
||||
render(game, canvas, context, score);
|
||||
window.requestAnimationFrame(frame);
|
||||
}
|
||||
|
||||
start().catch((error) => {
|
||||
console.error("Reflex Tauri WASM startup failed", error);
|
||||
});
|
||||
45
Tauri/game-reflex-poc/frontend/style.css
Normal file
45
Tauri/game-reflex-poc/frontend/style.css
Normal file
@@ -0,0 +1,45 @@
|
||||
/* file: Tauri/game-reflex-poc/frontend/style.css */
|
||||
/* version: 1 */
|
||||
|
||||
html,
|
||||
body {
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
background: rgb(18 18 24);
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
#app {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#game {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
touch-action: none;
|
||||
}
|
||||
|
||||
#score,
|
||||
#runtime {
|
||||
position: absolute;
|
||||
left: 12px;
|
||||
padding: 6px 8px;
|
||||
border-radius: 4px;
|
||||
background: rgb(0 0 0 / 55%);
|
||||
color: white;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
#score {
|
||||
top: 12px;
|
||||
}
|
||||
|
||||
#runtime {
|
||||
bottom: 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
32
crates/apps/game-reflex-poc-tauri/Cargo.toml
Normal file
32
crates/apps/game-reflex-poc-tauri/Cargo.toml
Normal file
@@ -0,0 +1,32 @@
|
||||
# file: crates/apps/game-reflex-poc-tauri/Cargo.toml
|
||||
# version: 1
|
||||
|
||||
[package]
|
||||
name = "game-reflex-poc-tauri"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
repository.workspace = true
|
||||
authors.workspace = true
|
||||
publish.workspace = true
|
||||
build = "build.rs"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
[dependencies]
|
||||
engine-v1-common = { path = "../../engines/engine-v1-common" }
|
||||
engine-v1-platform-api = { path = "../../engines/engine-v1-platform-api" }
|
||||
game-reflex-poc = { path = "../../games/game-reflex-poc" }
|
||||
|
||||
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
||||
tauri.workspace = true
|
||||
|
||||
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
||||
wasm-bindgen.workspace = true
|
||||
|
||||
[build-dependencies]
|
||||
tauri-build.workspace = true
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
12
crates/apps/game-reflex-poc-tauri/build.rs
Normal file
12
crates/apps/game-reflex-poc-tauri/build.rs
Normal file
@@ -0,0 +1,12 @@
|
||||
// file: crates/apps/game-reflex-poc-tauri/build.rs
|
||||
// version: 1
|
||||
|
||||
fn main() {
|
||||
let target_arch = std::env::var("CARGO_CFG_TARGET_ARCH");
|
||||
match target_arch {
|
||||
std::result::Result::Ok(value) if value == "wasm32" => return,
|
||||
_ => {},
|
||||
}
|
||||
tauri_build::build();
|
||||
return;
|
||||
}
|
||||
15
crates/apps/game-reflex-poc-tauri/src/lib.rs
Normal file
15
crates/apps/game-reflex-poc-tauri/src/lib.rs
Normal file
@@ -0,0 +1,15 @@
|
||||
// file: crates/apps/game-reflex-poc-tauri/src/lib.rs
|
||||
// version: 1
|
||||
|
||||
#![warn(missing_docs)]
|
||||
#![deny(unreachable_pub)]
|
||||
#![forbid(unsafe_code)]
|
||||
|
||||
//! WebAssembly adapter for the Reflex POC when hosted by the Tauri variant.
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
mod wasm_runtime;
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
/// Re-export of the Reflex WebAssembly runtime adapter.
|
||||
pub use self::wasm_runtime::ReflexWasmGame;
|
||||
25
crates/apps/game-reflex-poc-tauri/src/main.rs
Normal file
25
crates/apps/game-reflex-poc-tauri/src/main.rs
Normal file
@@ -0,0 +1,25 @@
|
||||
// file: crates/apps/game-reflex-poc-tauri/src/main.rs
|
||||
// version: 1
|
||||
|
||||
#![warn(missing_docs)]
|
||||
#![deny(unreachable_pub)]
|
||||
#![forbid(unsafe_code)]
|
||||
|
||||
//! Native Tauri host for the local Reflex WebAssembly POC.
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
fn main() {
|
||||
let result = tauri::Builder::default().run(tauri::generate_context!());
|
||||
match result {
|
||||
std::result::Result::Ok(()) => {},
|
||||
std::result::Result::Err(error) => {
|
||||
eprintln!("Reflex POC Tauri runner failed: {error}");
|
||||
},
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
fn main() {
|
||||
return;
|
||||
}
|
||||
152
crates/apps/game-reflex-poc-tauri/src/wasm_runtime.rs
Normal file
152
crates/apps/game-reflex-poc-tauri/src/wasm_runtime.rs
Normal file
@@ -0,0 +1,152 @@
|
||||
// file: crates/apps/game-reflex-poc-tauri/src/wasm_runtime.rs
|
||||
// version: 1
|
||||
|
||||
/// WebAssembly-owned Reflex game state consumed by the local Tauri WebView.
|
||||
#[wasm_bindgen::prelude::wasm_bindgen]
|
||||
pub struct ReflexWasmGame {
|
||||
runner: engine_v1_common::FixedStepRunner,
|
||||
state: game_reflex_poc::ReflexState,
|
||||
}
|
||||
|
||||
#[wasm_bindgen::prelude::wasm_bindgen]
|
||||
impl ReflexWasmGame {
|
||||
/// Creates a fresh Reflex POC session using the same gameplay crate as native Desktop.
|
||||
#[wasm_bindgen::prelude::wasm_bindgen(constructor)]
|
||||
pub fn new() -> Self {
|
||||
return Self {
|
||||
runner: engine_v1_common::FixedStepRunner::new(std::time::Duration::from_millis(16)),
|
||||
state: game_reflex_poc::ReflexState::new(),
|
||||
};
|
||||
}
|
||||
|
||||
/// Advances one deterministic update without an input impulse.
|
||||
pub fn tick(&mut self) {
|
||||
self.runner.tick(&mut self.state, engine_v1_common::InputState::none());
|
||||
return;
|
||||
}
|
||||
|
||||
/// Delivers one normalized primary-pointer impulse to the shared Reflex gameplay state.
|
||||
pub fn pointer_down(&mut self, x: f32, y: f32) {
|
||||
let pointer = engine_v1_common::PointerState::normalized(true, x, y);
|
||||
let input = engine_v1_common::InputState::none().with_pointer(pointer).with_action(engine_v1_common::GameAction::Primary, true);
|
||||
self.runner.tick(&mut self.state, input);
|
||||
return;
|
||||
}
|
||||
|
||||
/// Returns the current gameplay score.
|
||||
pub fn score(&self) -> u32 {
|
||||
return self.state.score().min(u32::MAX as u64) as u32;
|
||||
}
|
||||
|
||||
/// Returns the current scene background red channel.
|
||||
pub fn background_red(&self) -> u8 {
|
||||
return self.scene().background().red();
|
||||
}
|
||||
|
||||
/// Returns the current scene background green channel.
|
||||
pub fn background_green(&self) -> u8 {
|
||||
return self.scene().background().green();
|
||||
}
|
||||
|
||||
/// Returns the current scene background blue channel.
|
||||
pub fn background_blue(&self) -> u8 {
|
||||
return self.scene().background().blue();
|
||||
}
|
||||
|
||||
/// Returns the number of occupied rectangle slots in the current scene.
|
||||
pub fn rectangle_count(&self) -> u32 {
|
||||
let scene = self.scene();
|
||||
let mut count = 0_u32;
|
||||
for slot in scene.rectangles() {
|
||||
if slot.is_some() {
|
||||
count = count.saturating_add(1);
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/// Returns one rectangle's normalized left coordinate or `-1.0` if the index is absent.
|
||||
pub fn rectangle_x(&self, index: u32) -> f32 {
|
||||
return match self.rectangle(index) {
|
||||
Some(rectangle) => rectangle.rect().x(),
|
||||
None => -1.0,
|
||||
};
|
||||
}
|
||||
|
||||
/// Returns one rectangle's normalized top coordinate or `-1.0` if the index is absent.
|
||||
pub fn rectangle_y(&self, index: u32) -> f32 {
|
||||
return match self.rectangle(index) {
|
||||
Some(rectangle) => rectangle.rect().y(),
|
||||
None => -1.0,
|
||||
};
|
||||
}
|
||||
|
||||
/// Returns one rectangle's normalized width or `0.0` if the index is absent.
|
||||
pub fn rectangle_width(&self, index: u32) -> f32 {
|
||||
return match self.rectangle(index) {
|
||||
Some(rectangle) => rectangle.rect().width(),
|
||||
None => 0.0,
|
||||
};
|
||||
}
|
||||
|
||||
/// Returns one rectangle's normalized height or `0.0` if the index is absent.
|
||||
pub fn rectangle_height(&self, index: u32) -> f32 {
|
||||
return match self.rectangle(index) {
|
||||
Some(rectangle) => rectangle.rect().height(),
|
||||
None => 0.0,
|
||||
};
|
||||
}
|
||||
|
||||
/// Returns one rectangle's red channel or zero if the index is absent.
|
||||
pub fn rectangle_red(&self, index: u32) -> u8 {
|
||||
return match self.rectangle(index) {
|
||||
Some(rectangle) => rectangle.color().red(),
|
||||
None => 0,
|
||||
};
|
||||
}
|
||||
|
||||
/// Returns one rectangle's green channel or zero if the index is absent.
|
||||
pub fn rectangle_green(&self, index: u32) -> u8 {
|
||||
return match self.rectangle(index) {
|
||||
Some(rectangle) => rectangle.color().green(),
|
||||
None => 0,
|
||||
};
|
||||
}
|
||||
|
||||
/// Returns one rectangle's blue channel or zero if the index is absent.
|
||||
pub fn rectangle_blue(&self, index: u32) -> u8 {
|
||||
return match self.rectangle(index) {
|
||||
Some(rectangle) => rectangle.color().blue(),
|
||||
None => 0,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for ReflexWasmGame {
|
||||
fn default() -> Self {
|
||||
return Self::new();
|
||||
}
|
||||
}
|
||||
|
||||
impl ReflexWasmGame {
|
||||
fn scene(&self) -> engine_v1_common::EngineScene {
|
||||
return engine_v1_common::EngineGame::scene(&self.state);
|
||||
}
|
||||
|
||||
fn rectangle(&self, requested_index: u32) -> Option<engine_v1_common::RenderRect> {
|
||||
let scene = self.scene();
|
||||
let mut occupied_index = 0_u32;
|
||||
for slot in scene.rectangles() {
|
||||
match slot {
|
||||
Some(rectangle) => {
|
||||
if occupied_index == requested_index {
|
||||
return Some(*rectangle);
|
||||
}
|
||||
occupied_index = occupied_index.saturating_add(1);
|
||||
},
|
||||
None => {},
|
||||
}
|
||||
}
|
||||
return None;
|
||||
}
|
||||
}
|
||||
25
crates/apps/game-reflex-poc-tauri/tauri.conf.json
Normal file
25
crates/apps/game-reflex-poc-tauri/tauri.conf.json
Normal file
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"$schema": "https://schema.tauri.app/config/2",
|
||||
"productName": "Reflex POC Tauri",
|
||||
"version": "0.1.0-1-alpha.2",
|
||||
"identifier": "com.sasedev.games.reflex.tauri",
|
||||
"build": {
|
||||
"frontendDist": "../../../Tauri/game-reflex-poc/frontend"
|
||||
},
|
||||
"app": {
|
||||
"windows": [
|
||||
{
|
||||
"title": "Reflex POC Tauri",
|
||||
"width": 405,
|
||||
"height": 720,
|
||||
"resizable": true
|
||||
}
|
||||
],
|
||||
"security": {
|
||||
"csp": "default-src 'self'; script-src 'self' 'wasm-unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; connect-src 'self' ipc: http://ipc.localhost"
|
||||
}
|
||||
},
|
||||
"bundle": {
|
||||
"active": false
|
||||
}
|
||||
}
|
||||
129
deltas/0.1.0/1-alpha.2.md
Normal file
129
deltas/0.1.0/1-alpha.2.md
Normal file
@@ -0,0 +1,129 @@
|
||||
<!-- file: deltas/0.1.0/1-alpha.2.md -->
|
||||
<!-- version: 1 -->
|
||||
|
||||
# Delta 0.1.0-1-alpha.2
|
||||
|
||||
## Base
|
||||
|
||||
Base validée : `0.1.0-1-alpha.1`.
|
||||
|
||||
## Objectif
|
||||
|
||||
Valider un POC Reflex WebAssembly embarqué dans une fenêtre Tauri locale, sans site distant, tout en réutilisant `game-reflex-poc`.
|
||||
|
||||
## Architecture
|
||||
|
||||
La nouvelle crate :
|
||||
|
||||
```text
|
||||
crates/apps/game-reflex-poc-tauri
|
||||
```
|
||||
|
||||
porte :
|
||||
|
||||
- une face native Tauri ;
|
||||
- une face bibliothèque WASM pour `wasm32-unknown-unknown`.
|
||||
|
||||
Le frontend statique réside sous :
|
||||
|
||||
```text
|
||||
Tauri/game-reflex-poc/frontend
|
||||
```
|
||||
|
||||
Le JavaScript orchestre Canvas et pointeur mais ne contient aucune règle gameplay.
|
||||
|
||||
## Build WebAssembly
|
||||
|
||||
Préparer une fois :
|
||||
|
||||
```bash
|
||||
rustup target add wasm32-unknown-unknown
|
||||
cargo install wasm-bindgen-cli --locked
|
||||
```
|
||||
|
||||
Construire :
|
||||
|
||||
```bash
|
||||
python3 scripts/build_reflex_tauri_wasm.py
|
||||
```
|
||||
|
||||
## Prérequis Tauri Debian
|
||||
|
||||
Si nécessaire :
|
||||
|
||||
```bash
|
||||
sudo apt update
|
||||
sudo apt install libwebkit2gtk-4.1-dev build-essential curl wget file libxdo-dev libssl-dev libayatana-appindicator3-dev librsvg2-dev
|
||||
cargo install tauri-cli --version "^2.0.0" --locked
|
||||
```
|
||||
|
||||
## Validation Rust
|
||||
|
||||
```bash
|
||||
cargo fmt --all
|
||||
cargo fmt --all -- --check
|
||||
|
||||
python3 scripts/audit_rust_workspace_rules.py
|
||||
python3 scripts/audit_markdown_tables.py README.md RULES.md ROADMAP.md CHANGELOG.md docs prompts crates Tauri Android deltas history
|
||||
|
||||
cargo check --workspace
|
||||
cargo clippy --workspace --all-targets --all-features -- -D warnings
|
||||
|
||||
cargo test -p engine-v1-platform-api --all-targets --all-features
|
||||
cargo test -p game-reflex-poc --all-targets --all-features
|
||||
```
|
||||
|
||||
## Gate WASM
|
||||
|
||||
```bash
|
||||
python3 scripts/build_reflex_tauri_wasm.py
|
||||
```
|
||||
|
||||
Vérifier :
|
||||
|
||||
```bash
|
||||
test -f Tauri/game-reflex-poc/frontend/game_reflex_poc_tauri.js
|
||||
test -f Tauri/game-reflex-poc/frontend/game_reflex_poc_tauri_bg.wasm
|
||||
```
|
||||
|
||||
## Smoke Tauri
|
||||
|
||||
```bash
|
||||
cd crates/apps/game-reflex-poc-tauri
|
||||
cargo tauri dev
|
||||
```
|
||||
|
||||
Critères :
|
||||
|
||||
- fenêtre Tauri locale ;
|
||||
- cible Reflex visible ;
|
||||
- clic sur cible comptabilisé ;
|
||||
- cible déplacée après succès ;
|
||||
- score visible ;
|
||||
- aucun site distant requis.
|
||||
|
||||
## Provenance attendue
|
||||
|
||||
La variante correspond conceptuellement à :
|
||||
|
||||
```text
|
||||
PlatformFamily::Desktop
|
||||
DeviceClass::Desktop
|
||||
ExecutionModel::Wasm
|
||||
RuntimeHost::TauriWebView
|
||||
InputProfile::KeyboardMouse
|
||||
```
|
||||
|
||||
La connexion effective de cette provenance à une session/score fera partie d'une tranche ultérieure.
|
||||
|
||||
## Services Web
|
||||
|
||||
Aucune régie Ads n'est intégrée dans cette alpha.
|
||||
|
||||
Le POC valide uniquement la capacité structurelle à disposer d'une frontière Web locale dans Tauri. Les APIs de publicité, rewarded/revive et analytics resteront derrière les contrats plateforme.
|
||||
|
||||
## Transition
|
||||
|
||||
Si les gates passent, `0.1.0-1-alpha.2` valide la coexistence Desktop SDL3 natif + Tauri/WASM.
|
||||
|
||||
En cas d'échec imputable au projet, produire `0.1.0-1-alpha.2.fix.1`.
|
||||
@@ -1,5 +1,5 @@
|
||||
<!-- file: docs/000-README.md -->
|
||||
<!-- version: 13 -->
|
||||
<!-- version: 14 -->
|
||||
|
||||
# Documentation games.sasedev
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!-- file: docs/architecture/001-WORKSPACE_ARCHITECTURE.md -->
|
||||
<!-- version: 3 -->
|
||||
<!-- version: 4 -->
|
||||
|
||||
# Architecture du workspace
|
||||
|
||||
@@ -20,11 +20,14 @@ games.sasedev/
|
||||
│ │ └── game-snake-poc/
|
||||
│ └── apps/
|
||||
│ ├── game-reflex-poc-desktop/
|
||||
│ └── game-snake-poc-desktop/
|
||||
│ ├── game-snake-poc-desktop/
|
||||
│ └── game-reflex-poc-tauri/
|
||||
├── assets/
|
||||
│ ├── common/
|
||||
│ ├── game-reflex-poc/
|
||||
│ └── game-snake-poc/
|
||||
├── Tauri/
|
||||
│ └── game-reflex-poc/
|
||||
├── Android/
|
||||
│ ├── common/
|
||||
│ ├── game-reflex-poc/
|
||||
@@ -66,3 +69,14 @@ Le runtime devra conserver une distinction logique entre ressources communes et
|
||||
## Runners Desktop
|
||||
|
||||
Chaque crate de jeu reste une bibliothèque. Une crate binaire Desktop séparée sous `crates/apps/` la consomme pour permettre les itérations locales rapides. Cette séparation évite d’introduire `main`, des choix de plateforme ou du code de lancement dans le gameplay réutilisable.
|
||||
|
||||
## Variante Tauri / WebAssembly
|
||||
|
||||
La variante Tauri est distincte du runner SDL3 natif.
|
||||
|
||||
`game-reflex-poc-tauri` porte deux faces du même launcher :
|
||||
|
||||
- une bibliothèque `wasm32-unknown-unknown` qui adapte `game-reflex-poc` à `wasm-bindgen` ;
|
||||
- un binaire natif Tauri qui héberge la WebView locale.
|
||||
|
||||
Le frontend statique sous `Tauri/game-reflex-poc/frontend/` pilote un Canvas et appelle l'adaptateur WASM. Les règles Reflex restent exclusivement dans `game-reflex-poc`.
|
||||
|
||||
97
docs/development/008-WASM_TAURI_POC.md
Normal file
97
docs/development/008-WASM_TAURI_POC.md
Normal file
@@ -0,0 +1,97 @@
|
||||
<!-- file: docs/development/008-WASM_TAURI_POC.md -->
|
||||
<!-- version: 1 -->
|
||||
|
||||
# POC WebAssembly embarqué dans Tauri
|
||||
|
||||
## Objectif
|
||||
|
||||
Le POC vérifie qu'une même crate de gameplay Rust peut fonctionner :
|
||||
|
||||
- dans le runner Desktop SDL3 natif ;
|
||||
- compilée en WebAssembly ;
|
||||
- dans une WebView Tauri locale ;
|
||||
- sans site distant ni serveur applicatif.
|
||||
|
||||
Le POC ne remplace pas le runner SDL3 natif.
|
||||
|
||||
## Découpage
|
||||
|
||||
```text
|
||||
game-reflex-poc
|
||||
|
|
||||
+--> game-reflex-poc-desktop --> engine-v1-sdl --> SDL3 natif
|
||||
|
|
||||
+--> game-reflex-poc-tauri --wasm32--> wasm-bindgen --> Canvas Web
|
||||
|
|
||||
+--natif--> Tauri WebView
|
||||
```
|
||||
|
||||
Le JavaScript ne contient aucune règle Reflex. Il traduit les événements pointeur vers l'adaptateur WASM et dessine la scène générique exposée par le moteur.
|
||||
|
||||
## Frontend local
|
||||
|
||||
Tauri agit comme hôte de fichiers Web statiques locaux. `frontendDist` pointe sur :
|
||||
|
||||
```text
|
||||
Tauri/game-reflex-poc/frontend/
|
||||
```
|
||||
|
||||
Aucun site, CDN, Vite ou gestionnaire de paquets JavaScript n'est requis par ce POC.
|
||||
|
||||
## Sécurité
|
||||
|
||||
La CSP reste locale. `wasm-unsafe-eval` est autorisé uniquement parce que la WebView charge du WebAssembly.
|
||||
|
||||
Aucun script distant n'est autorisé.
|
||||
|
||||
## Build WASM
|
||||
|
||||
Préparer une fois :
|
||||
|
||||
```bash
|
||||
rustup target add wasm32-unknown-unknown
|
||||
cargo install wasm-bindgen-cli --locked
|
||||
```
|
||||
|
||||
Puis :
|
||||
|
||||
```bash
|
||||
python3 scripts/build_reflex_tauri_wasm.py
|
||||
```
|
||||
|
||||
Les fichiers générés sont ignorés par Git.
|
||||
|
||||
## Prérequis Tauri Linux
|
||||
|
||||
Le développement Tauri v2 requiert les bibliothèques système WebKitGTK/GTK correspondantes.
|
||||
|
||||
Sur Debian, vérifier notamment la présence de `libwebkit2gtk-4.1-dev` et des autres prérequis documentés par Tauri avant la gate Tauri.
|
||||
|
||||
Installer une fois le CLI si nécessaire :
|
||||
|
||||
```bash
|
||||
cargo install tauri-cli --version "^2.0.0" --locked
|
||||
```
|
||||
|
||||
## Exécution
|
||||
|
||||
Après génération du WASM :
|
||||
|
||||
```bash
|
||||
cd crates/apps/game-reflex-poc-tauri
|
||||
cargo tauri dev
|
||||
```
|
||||
|
||||
Critères :
|
||||
|
||||
- une fenêtre locale 405 × 720 s'ouvre ;
|
||||
- le Canvas affiche le même fond et la même cible que Reflex SDL3 ;
|
||||
- cliquer sur la cible incrémente le score et déplace la cible ;
|
||||
- aucun site distant n'est requis ;
|
||||
- le runtime est identifiable comme `Desktop + Wasm + TauriWebView + KeyboardMouse`.
|
||||
|
||||
## Frontière services Web
|
||||
|
||||
Ce POC ne choisit aucune régie publicitaire.
|
||||
|
||||
Il valide seulement qu'une variante Tauri possède une WebView capable d'héberger une frontière Web distincte. Ads, rewarded ads, revive, analytics et autres services resteront derrière `engine-v1-platform-api` et feront l'objet de tranches dédiées.
|
||||
@@ -1,5 +1,5 @@
|
||||
<!-- file: docs/rules/FILE_CONTRACTS.md -->
|
||||
<!-- version: 3 -->
|
||||
<!-- version: 4 -->
|
||||
|
||||
# Contrats des fichiers principaux
|
||||
|
||||
@@ -24,3 +24,9 @@
|
||||
- `assets/` contient les ressources runtime communes et spécifiques aux jeux ; aucune ressource runtime n'est placée dans une crate Rust.
|
||||
- `Android/` contient le projet Gradle multi-module et son code Java commun/spécifique.
|
||||
- `crates/apps/` contient les exécutables de développement et launchers Rust, notamment les runners Desktop par jeu.
|
||||
|
||||
## Tauri
|
||||
|
||||
- `Tauri/<game>/frontend/` contient le frontend Web statique versionné d'une variante Tauri.
|
||||
- Les bindings JavaScript et modules `.wasm` générés restent ignorés par Git.
|
||||
- Le backend Rust Tauri reste sous `crates/apps/`.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!-- file: docs/rules/RULES_COMMANDS.md -->
|
||||
<!-- version: 4 -->
|
||||
<!-- version: 5 -->
|
||||
|
||||
# Règles d'exécution des commandes
|
||||
|
||||
@@ -53,6 +53,8 @@
|
||||
|
||||
- **CMD-WEB-001** — Aucun gestionnaire de paquets JavaScript ni build Web n'est exécuté tant qu'un frontend Web réel n'a pas été introduit dans le dépôt.
|
||||
- **CMD-WEB-002** — Lorsqu'une cible Web existe, ses commandes de build et test sont documentées avant d'être ajoutées aux gates.
|
||||
- **CMD-WEB-003** — Le POC Tauri/WASM utilise `scripts/build_reflex_tauri_wasm.py`; aucun gestionnaire de paquets JavaScript ni serveur Web externe n'est requis pour cette variante.
|
||||
- **CMD-WEB-004** — Les fichiers produits par `wasm-bindgen` sont générés localement et ne sont pas commités.
|
||||
|
||||
## Git et fichiers générés
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!-- file: docs/rules/RULES_PROJECT.md -->
|
||||
<!-- version: 4 -->
|
||||
<!-- version: 5 -->
|
||||
|
||||
# Règles spécifiques games.sasedev
|
||||
|
||||
@@ -49,6 +49,8 @@
|
||||
- **GAME-PLATFORM-004** — Ads et Billing sont des capacités optionnelles ; le gameplay reste fonctionnel lorsqu'elles sont disponibles, désactivées ou non supportées.
|
||||
- **GAME-PLATFORM-005** — Un backend de monétisation est spécifique à sa plateforme et à sa distribution ; une intégration Android n'est pas réutilisée implicitement sur Web ou Desktop.
|
||||
- **GAME-PLATFORM-006** — Le runner Desktop natif SDL3 est la forme Desktop par défaut. Une variante Tauri peut coexister uniquement lorsqu'un besoin explicite le justifie et consomme la même crate lib de jeu.
|
||||
- **GAME-PLATFORM-007** — Une variante Tauri conserve son backend Rust sous `crates/apps/`; son frontend Web statique réside sous `Tauri/<game>/` et ne duplique pas le gameplay Rust.
|
||||
- **GAME-PLATFORM-008** — Le gameplay compilé en WebAssembly est adapté par une frontière dédiée ; le code JavaScript orchestre la WebView, les événements et le rendu Canvas mais ne réimplémente pas les règles du jeu.
|
||||
|
||||
## POC
|
||||
|
||||
|
||||
26
history/0.1.0/1-alpha.1.md
Normal file
26
history/0.1.0/1-alpha.1.md
Normal file
@@ -0,0 +1,26 @@
|
||||
<!-- file: history/0.1.0/1-alpha.1.md -->
|
||||
<!-- version: 1 -->
|
||||
|
||||
# Historique 0.1.0-1-alpha.1
|
||||
|
||||
## Statut
|
||||
|
||||
Validé par l'utilisateur le 2026-09-16.
|
||||
|
||||
## Gates
|
||||
|
||||
- audits Rust/workspace propres ;
|
||||
- audit Markdown : `1 table(s), 80 file(s)` ;
|
||||
- `cargo check --workspace` propre ;
|
||||
- Clippy workspace strict propre ;
|
||||
- `engine-v1-platform-api` : 3/3 tests ;
|
||||
- `engine-v1-common` : 8/8 tests ;
|
||||
- `engine-v1-sdl` : 5/5 tests ;
|
||||
- `game-reflex-poc` : 4/4 tests ;
|
||||
- `game-snake-poc` : 5/5 tests ;
|
||||
- smoke Desktop Snake propre ;
|
||||
- smoke Desktop Reflex propre.
|
||||
|
||||
## Portée validée
|
||||
|
||||
La provenance runtime/input est disponible comme donnée descriptive indépendante de la politique de classement.
|
||||
56
scripts/build_reflex_tauri_wasm.py
Normal file
56
scripts/build_reflex_tauri_wasm.py
Normal file
@@ -0,0 +1,56 @@
|
||||
#!/usr/bin/env python3
|
||||
# file: scripts/build_reflex_tauri_wasm.py
|
||||
# version: 1
|
||||
|
||||
"""Build the Reflex WASM adapter and generate browser bindings for the local Tauri frontend."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import pathlib
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
|
||||
def main() -> int:
|
||||
"""Build the WASM library and run wasm-bindgen into the committed Tauri frontend directory."""
|
||||
|
||||
root = pathlib.Path(__file__).resolve().parent.parent
|
||||
target_dir = root.parent / "builds" / "sasedev-games" / "target"
|
||||
wasm_path = target_dir / "wasm32-unknown-unknown" / "debug" / "game_reflex_poc_tauri.wasm"
|
||||
output_dir = root / "Tauri" / "game-reflex-poc" / "frontend"
|
||||
|
||||
build = subprocess.run(
|
||||
["cargo", "build", "-p", "game-reflex-poc-tauri", "--lib", "--target", "wasm32-unknown-unknown"],
|
||||
cwd=root,
|
||||
check=False,
|
||||
)
|
||||
if build.returncode != 0:
|
||||
return build.returncode
|
||||
if not wasm_path.is_file():
|
||||
print(f"WASM artifact not found: {wasm_path}", file=sys.stderr)
|
||||
return 2
|
||||
|
||||
bindings = subprocess.run(
|
||||
[
|
||||
"wasm-bindgen",
|
||||
str(wasm_path),
|
||||
"--target",
|
||||
"web",
|
||||
"--out-dir",
|
||||
str(output_dir),
|
||||
"--out-name",
|
||||
"game_reflex_poc_tauri",
|
||||
"--no-typescript",
|
||||
],
|
||||
cwd=root,
|
||||
check=False,
|
||||
)
|
||||
if bindings.returncode != 0:
|
||||
return bindings.returncode
|
||||
|
||||
print(f"Reflex Tauri WASM frontend generated in {output_dir}")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user