0.1.0-1-alpha.2

This commit is contained in:
2026-09-16 23:28:33 +02:00
parent 2ad0c5a379
commit 9256e9e2d5
24 changed files with 762 additions and 14 deletions

3
.gitignore vendored
View File

@@ -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

View File

@@ -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 {

View File

@@ -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 {

View File

@@ -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"

View File

@@ -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
View 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.

View 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>

View 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);
});

View 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;
}

View 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

View 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;
}

View 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;

View 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;
}

View 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;
}
}

View 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
View 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`.

View File

@@ -1,5 +1,5 @@
<!-- file: docs/000-README.md -->
<!-- version: 13 -->
<!-- version: 14 -->
# Documentation games.sasedev

View File

@@ -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 dintroduire `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`.

View 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.

View File

@@ -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/`.

View File

@@ -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

View File

@@ -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

View 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.

View 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())