0.1.0-0-pre.9
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
<!-- file: Android/README.md -->
|
<!-- file: Android/README.md -->
|
||||||
<!-- version: 5 -->
|
<!-- version: 6 -->
|
||||||
|
|
||||||
# Android
|
# Android
|
||||||
|
|
||||||
@@ -20,7 +20,7 @@ Chaque module `game-*` est une application Android indépendante ; il dépend du
|
|||||||
|
|
||||||
## Toolchain de référence
|
## Toolchain de référence
|
||||||
|
|
||||||
Pour la baseline `0.1.0-0-pre.8` :
|
Pour la baseline `0.1.0-0-pre.9` :
|
||||||
|
|
||||||
- Android Gradle Plugin `9.4.0` ;
|
- Android Gradle Plugin `9.4.0` ;
|
||||||
- Gradle `9.6.0` attendu par AGP 9.4 ;
|
- Gradle `9.6.0` attendu par AGP 9.4 ;
|
||||||
@@ -73,6 +73,21 @@ Le script extrait temporairement `libSDL3.so` de l'AAR uniquement pour fournir l
|
|||||||
|
|
||||||
## Bridge JNI et tactile
|
## Bridge JNI et tactile
|
||||||
|
|
||||||
À partir de `0.1.0-0-pre.8`, `SaseGameActivity` vérifie au démarrage la version du bridge Java/JNI chargé depuis la bibliothèque Rust.
|
À partir de `0.1.0-0-pre.9`, `SaseGameActivity` vérifie au démarrage la version du bridge Java/JNI chargé depuis la bibliothèque Rust.
|
||||||
|
|
||||||
Les touch events standards restent traités via SDL3 et ne transitent pas par JNI.
|
Les touch events standards restent traités via SDL3 et ne transitent pas par JNI.
|
||||||
|
|
||||||
|
## Packaging des assets
|
||||||
|
|
||||||
|
Les modules applications exécutent `stageSasedevAssets` avant `preBuild`.
|
||||||
|
|
||||||
|
La tâche compose les sources :
|
||||||
|
|
||||||
|
```text
|
||||||
|
assets/common/ -> assets/common/
|
||||||
|
assets/<game>/ -> assets/game/
|
||||||
|
```
|
||||||
|
|
||||||
|
dans un répertoire généré sous `Android/<module>/build/generated/sasedevAssets/main/`.
|
||||||
|
|
||||||
|
Aucune ressource source n'est copiée durablement dans un module Android ou une crate Rust.
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: Android/game-reflex-poc/build.gradle
|
// file: Android/game-reflex-poc/build.gradle
|
||||||
// version: 6
|
// version: 7
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id 'com.android.application'
|
id 'com.android.application'
|
||||||
@@ -16,7 +16,7 @@ android {
|
|||||||
minSdk 21
|
minSdk 21
|
||||||
targetSdk 36
|
targetSdk 36
|
||||||
versionCode 1
|
versionCode 1
|
||||||
versionName '0.1.0-0-pre.8'
|
versionName '0.1.0-0-pre.9'
|
||||||
}
|
}
|
||||||
|
|
||||||
compileOptions {
|
compileOptions {
|
||||||
@@ -29,3 +29,6 @@ dependencies {
|
|||||||
implementation files("../libs/${sdl3AarName}")
|
implementation files("../libs/${sdl3AarName}")
|
||||||
implementation project(':common')
|
implementation project(':common')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ext.sasedevGameAssetDirectory = "game-reflex-poc"
|
||||||
|
apply from: rootProject.file("gradle/sasedev-assets.gradle")
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: Android/game-snake-poc/build.gradle
|
// file: Android/game-snake-poc/build.gradle
|
||||||
// version: 6
|
// version: 7
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id 'com.android.application'
|
id 'com.android.application'
|
||||||
@@ -16,7 +16,7 @@ android {
|
|||||||
minSdk 21
|
minSdk 21
|
||||||
targetSdk 36
|
targetSdk 36
|
||||||
versionCode 1
|
versionCode 1
|
||||||
versionName '0.1.0-0-pre.8'
|
versionName '0.1.0-0-pre.9'
|
||||||
}
|
}
|
||||||
|
|
||||||
compileOptions {
|
compileOptions {
|
||||||
@@ -29,3 +29,6 @@ dependencies {
|
|||||||
implementation files("../libs/${sdl3AarName}")
|
implementation files("../libs/${sdl3AarName}")
|
||||||
implementation project(':common')
|
implementation project(':common')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ext.sasedevGameAssetDirectory = "game-snake-poc"
|
||||||
|
apply from: rootProject.file("gradle/sasedev-assets.gradle")
|
||||||
|
|||||||
22
Android/gradle/sasedev-assets.gradle
Normal file
22
Android/gradle/sasedev-assets.gradle
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
// file: Android/gradle/sasedev-assets.gradle
|
||||||
|
// version: 1
|
||||||
|
|
||||||
|
def generatedAssetsDirectory = layout.buildDirectory.dir("generated/sasedevAssets/main")
|
||||||
|
def sourceCommonAssets = rootProject.file("../assets/common")
|
||||||
|
def sourceGameAssets = rootProject.file("../assets/${project.ext.sasedevGameAssetDirectory}")
|
||||||
|
|
||||||
|
tasks.register("stageSasedevAssets", Sync) {
|
||||||
|
into(generatedAssetsDirectory)
|
||||||
|
from(sourceCommonAssets) {
|
||||||
|
into("common")
|
||||||
|
}
|
||||||
|
from(sourceGameAssets) {
|
||||||
|
into("game")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
android.sourceSets.main.assets.srcDir(generatedAssetsDirectory)
|
||||||
|
|
||||||
|
tasks.named("preBuild").configure {
|
||||||
|
dependsOn(tasks.named("stageSasedevAssets"))
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
# file: Cargo.toml
|
# file: Cargo.toml
|
||||||
# version: 16
|
# version: 17
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
resolver = "3"
|
resolver = "3"
|
||||||
@@ -11,12 +11,13 @@ members = [
|
|||||||
"crates/games/game-snake-poc",
|
"crates/games/game-snake-poc",
|
||||||
"crates/apps/game-reflex-poc-desktop",
|
"crates/apps/game-reflex-poc-desktop",
|
||||||
"crates/apps/game-snake-poc-desktop",
|
"crates/apps/game-snake-poc-desktop",
|
||||||
|
"crates/common/game-assets-lib",
|
||||||
"crates/common/game-logging-lib",
|
"crates/common/game-logging-lib",
|
||||||
"crates/apps/game-android-entrypoint",
|
"crates/apps/game-android-entrypoint",
|
||||||
]
|
]
|
||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
version = "0.1.0-0-pre.8"
|
version = "0.1.0-0-pre.9"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
repository = "https://git.sasedev.com/Sasedev/games"
|
repository = "https://git.sasedev.com/Sasedev/games"
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<!-- file: README.md -->
|
<!-- file: README.md -->
|
||||||
<!-- version: 4 -->
|
<!-- version: 5 -->
|
||||||
|
|
||||||
# games.sasedev
|
# games.sasedev
|
||||||
|
|
||||||
@@ -39,4 +39,4 @@ Les deux premiers jeux sont des POC structurels : `game-reflex-poc` et `game-sna
|
|||||||
|
|
||||||
## Diagnostics et tests
|
## Diagnostics et tests
|
||||||
|
|
||||||
Le socle transverse `crates/common/game-logging-lib` fournit le tracing commun. Les tests unitaires résident hors `src/` sous `unit_tests/`; les tests d’intégration/environnement résident sous `tests/`.
|
Les socles transverses `crates/common/game-assets-lib` et `crates/common/game-logging-lib` fournissent respectivement la résolution logique des assets et le tracing commun. Les tests unitaires résident hors `src/` sous `unit_tests/`; les tests d’intégration/environnement résident sous `tests/`.
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<!-- file: ROADMAP.md -->
|
<!-- file: ROADMAP.md -->
|
||||||
<!-- version: 6 -->
|
<!-- version: 7 -->
|
||||||
|
|
||||||
# Roadmap
|
# Roadmap
|
||||||
|
|
||||||
@@ -11,8 +11,8 @@
|
|||||||
- [x] `0-pre.4` — architecture de tests hors `src/`, stratégie de tests ciblés et socle `tracing`/`tracing-subscriber`/`tracing-appender`.
|
- [x] `0-pre.4` — architecture de tests hors `src/`, stratégie de tests ciblés et socle `tracing`/`tracing-subscriber`/`tracing-appender`.
|
||||||
- [x] `0-pre.5` — intégration SDL3 Desktop réelle et premier rendu POC Reflex.
|
- [x] `0-pre.5` — intégration SDL3 Desktop réelle et premier rendu POC Reflex.
|
||||||
- [x] `0-pre.6` — fondation Gradle Android, SDL3 AAR, Activity Java commune et politique de fenêtre Desktop redimensionnable.
|
- [x] `0-pre.6` — fondation Gradle Android, SDL3 AAR, Activity Java commune et politique de fenêtre Desktop redimensionnable.
|
||||||
- [ ] `0-pre.7` — compilation Rust Android `cdylib`, point d'entrée SDL Android et premier lancement APK sur appareil/émulateur.
|
- [x] `0-pre.7` — compilation Rust Android `cdylib`, point d'entrée SDL Android et premier lancement APK sur appareil/émulateur.
|
||||||
- [ ] `0-pre.8` — bridge Java/JNI minimal et input tactile Android.
|
- [x] `0-pre.8` — bridge Java/JNI minimal et input tactile Android.
|
||||||
- [ ] `0-pre.9` — assets communs + spécifiques empaquetés sans copie dans les crates.
|
- [ ] `0-pre.9` — assets communs + spécifiques empaquetés sans copie dans les crates.
|
||||||
- [ ] `0-pre.10` — POC Reflex jouable Desktop + Android.
|
- [ ] `0-pre.10` — POC Reflex jouable Desktop + Android.
|
||||||
- [ ] `0-pre.11` — POC Snake jouable et validation de la réutilisation du moteur.
|
- [ ] `0-pre.11` — POC Snake jouable et validation de la réutilisation du moteur.
|
||||||
|
|||||||
31
assets/README.md
Normal file
31
assets/README.md
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
<!-- file: assets/README.md -->
|
||||||
|
<!-- version: 1 -->
|
||||||
|
|
||||||
|
# Assets source
|
||||||
|
|
||||||
|
Ce répertoire est l'unique source versionnée des ressources runtime.
|
||||||
|
|
||||||
|
```text
|
||||||
|
assets/
|
||||||
|
├── common/
|
||||||
|
├── game-reflex-poc/
|
||||||
|
└── game-snake-poc/
|
||||||
|
```
|
||||||
|
|
||||||
|
Les crates Rust ne contiennent aucune copie source des assets.
|
||||||
|
|
||||||
|
Le layout de package canonique est :
|
||||||
|
|
||||||
|
```text
|
||||||
|
common/<relative-path>
|
||||||
|
game/<relative-path>
|
||||||
|
```
|
||||||
|
|
||||||
|
Ainsi :
|
||||||
|
|
||||||
|
```text
|
||||||
|
common://data/runtime.json -> common/data/runtime.json
|
||||||
|
game://data/game.json -> game/data/game.json
|
||||||
|
```
|
||||||
|
|
||||||
|
Les répertoires de staging générés par Desktop ou Android ne sont pas des sources et ne doivent pas être commités.
|
||||||
5
assets/common/data/runtime.json
Normal file
5
assets/common/data/runtime.json
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"schema": 1,
|
||||||
|
"scope": "common",
|
||||||
|
"engine_generation": 1
|
||||||
|
}
|
||||||
5
assets/game-reflex-poc/data/game.json
Normal file
5
assets/game-reflex-poc/data/game.json
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"schema": 1,
|
||||||
|
"game": "game-reflex-poc",
|
||||||
|
"kind": "reflex"
|
||||||
|
}
|
||||||
5
assets/game-snake-poc/data/game.json
Normal file
5
assets/game-snake-poc/data/game.json
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"schema": 1,
|
||||||
|
"game": "game-snake-poc",
|
||||||
|
"kind": "snake"
|
||||||
|
}
|
||||||
17
crates/common/game-assets-lib/Cargo.toml
Normal file
17
crates/common/game-assets-lib/Cargo.toml
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
# file: crates/common/game-assets-lib/Cargo.toml
|
||||||
|
# version: 1
|
||||||
|
|
||||||
|
[package]
|
||||||
|
name = "game-assets-lib"
|
||||||
|
version.workspace = true
|
||||||
|
edition.workspace = true
|
||||||
|
license.workspace = true
|
||||||
|
repository.workspace = true
|
||||||
|
authors.workspace = true
|
||||||
|
publish.workspace = true
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
game-logging-lib = { path = "../game-logging-lib" }
|
||||||
|
|
||||||
|
[lints]
|
||||||
|
workspace = true
|
||||||
25
crates/common/game-assets-lib/src/error.rs
Normal file
25
crates/common/game-assets-lib/src/error.rs
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
// file: crates/common/game-assets-lib/src/error.rs
|
||||||
|
// version: 1
|
||||||
|
|
||||||
|
/// Asset URI validation or resolution failure.
|
||||||
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
|
pub enum AssetError {
|
||||||
|
/// The URI does not use a supported logical asset namespace.
|
||||||
|
UnsupportedScheme,
|
||||||
|
/// The URI contains no relative asset path.
|
||||||
|
EmptyPath,
|
||||||
|
/// The URI attempts to escape its logical asset namespace.
|
||||||
|
InvalidPath,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl core::fmt::Display for AssetError {
|
||||||
|
fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
return match self {
|
||||||
|
crate::AssetError::UnsupportedScheme => formatter.write_str("unsupported asset URI scheme"),
|
||||||
|
crate::AssetError::EmptyPath => formatter.write_str("asset URI path is empty"),
|
||||||
|
crate::AssetError::InvalidPath => formatter.write_str("asset URI path is invalid"),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::error::Error for AssetError {}
|
||||||
16
crates/common/game-assets-lib/src/lib.rs
Normal file
16
crates/common/game-assets-lib/src/lib.rs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
// file: crates/common/game-assets-lib/src/lib.rs
|
||||||
|
// version: 1
|
||||||
|
|
||||||
|
#![warn(missing_docs)]
|
||||||
|
#![deny(unreachable_pub)]
|
||||||
|
#![forbid(unsafe_code)]
|
||||||
|
|
||||||
|
//! Logical asset URI validation and source-root resolution for games.sasedev.
|
||||||
|
|
||||||
|
mod error;
|
||||||
|
mod resolver;
|
||||||
|
|
||||||
|
/// Re-export of asset URI and resolution failures.
|
||||||
|
pub use self::error::AssetError;
|
||||||
|
/// Re-export of the source-root asset resolver.
|
||||||
|
pub use self::resolver::AssetResolver;
|
||||||
45
crates/common/game-assets-lib/src/resolver.rs
Normal file
45
crates/common/game-assets-lib/src/resolver.rs
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
// file: crates/common/game-assets-lib/src/resolver.rs
|
||||||
|
// version: 1
|
||||||
|
|
||||||
|
/// Resolves logical asset URIs against separate common and game roots.
|
||||||
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
|
pub struct AssetResolver {
|
||||||
|
common_root: std::path::PathBuf,
|
||||||
|
game_root: std::path::PathBuf,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AssetResolver {
|
||||||
|
/// Creates a resolver from physical common and game asset roots.
|
||||||
|
#[must_use]
|
||||||
|
pub fn new(common_root: impl Into<std::path::PathBuf>, game_root: impl Into<std::path::PathBuf>) -> Self {
|
||||||
|
return Self { common_root: common_root.into(), game_root: game_root.into() };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Resolves `common://` or `game://` URI into a physical path without touching the filesystem.
|
||||||
|
pub fn resolve(&self, uri: &str) -> Result<std::path::PathBuf, crate::AssetError> {
|
||||||
|
let (root, relative) = if let Some(relative) = uri.strip_prefix("common://") {
|
||||||
|
(&self.common_root, relative)
|
||||||
|
} else if let Some(relative) = uri.strip_prefix("game://") {
|
||||||
|
(&self.game_root, relative)
|
||||||
|
} else {
|
||||||
|
return Err(crate::AssetError::UnsupportedScheme);
|
||||||
|
};
|
||||||
|
if relative.is_empty() {
|
||||||
|
return Err(crate::AssetError::EmptyPath);
|
||||||
|
}
|
||||||
|
let relative_path = std::path::Path::new(relative);
|
||||||
|
if relative_path.is_absolute() || relative.contains('\\') {
|
||||||
|
return Err(crate::AssetError::InvalidPath);
|
||||||
|
}
|
||||||
|
for component in relative_path.components() {
|
||||||
|
if !matches!(component, std::path::Component::Normal(_)) {
|
||||||
|
return Err(crate::AssetError::InvalidPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Ok(root.join(relative_path));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
#[path = "../unit_tests/resolver.rs"]
|
||||||
|
mod tests;
|
||||||
21
crates/common/game-assets-lib/unit_tests/resolver.rs
Normal file
21
crates/common/game-assets-lib/unit_tests/resolver.rs
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
// file: crates/common/game-assets-lib/unit_tests/resolver.rs
|
||||||
|
// version: 1
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn logical_namespaces_resolve_to_separate_roots() {
|
||||||
|
game_logging_lib::with_test_tracing(|| {
|
||||||
|
let resolver = crate::AssetResolver::new("assets/common", "assets/game-reflex-poc");
|
||||||
|
assert_eq!(resolver.resolve("common://data/runtime.json"), Ok(std::path::PathBuf::from("assets/common/data/runtime.json")));
|
||||||
|
assert_eq!(resolver.resolve("game://data/game.json"), Ok(std::path::PathBuf::from("assets/game-reflex-poc/data/game.json")));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn traversal_and_unknown_schemes_are_rejected() {
|
||||||
|
game_logging_lib::with_test_tracing(|| {
|
||||||
|
let resolver = crate::AssetResolver::new("assets/common", "assets/game-reflex-poc");
|
||||||
|
assert_eq!(resolver.resolve("game://../secret"), Err(crate::AssetError::InvalidPath));
|
||||||
|
assert_eq!(resolver.resolve("file://data/game.json"), Err(crate::AssetError::UnsupportedScheme));
|
||||||
|
assert_eq!(resolver.resolve("common://"), Err(crate::AssetError::EmptyPath));
|
||||||
|
});
|
||||||
|
}
|
||||||
147
deltas/0.1.0/0-pre.9.md
Normal file
147
deltas/0.1.0/0-pre.9.md
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
<!-- file: deltas/0.1.0/0-pre.9.md -->
|
||||||
|
<!-- version: 1 -->
|
||||||
|
|
||||||
|
# Delta 0.1.0-0-pre.9
|
||||||
|
|
||||||
|
## Base
|
||||||
|
|
||||||
|
Base validée : `0.1.0-0-pre.8`.
|
||||||
|
|
||||||
|
## Objectif
|
||||||
|
|
||||||
|
Stabiliser la source, les namespaces, la résolution et le packaging multi-plateforme des assets avant le premier gameplay visuel complet.
|
||||||
|
|
||||||
|
## Source unique
|
||||||
|
|
||||||
|
Les ressources versionnées restent exclusivement sous :
|
||||||
|
|
||||||
|
```text
|
||||||
|
assets/common/
|
||||||
|
assets/game-reflex-poc/
|
||||||
|
assets/game-snake-poc/
|
||||||
|
```
|
||||||
|
|
||||||
|
Aucune crate Rust et aucun module Android ne reçoit de copie source durable.
|
||||||
|
|
||||||
|
## game-assets-lib
|
||||||
|
|
||||||
|
Nouveau crate transverse :
|
||||||
|
|
||||||
|
```text
|
||||||
|
crates/common/game-assets-lib
|
||||||
|
```
|
||||||
|
|
||||||
|
Il fournit `AssetResolver` et `AssetError`.
|
||||||
|
|
||||||
|
Namespaces canoniques :
|
||||||
|
|
||||||
|
```text
|
||||||
|
common://...
|
||||||
|
game://...
|
||||||
|
```
|
||||||
|
|
||||||
|
Le resolver rejette les chemins vides, absolus, traversants et les schemes inconnus.
|
||||||
|
|
||||||
|
## Ressources de validation
|
||||||
|
|
||||||
|
Premiers assets réels :
|
||||||
|
|
||||||
|
```text
|
||||||
|
assets/common/data/runtime.json
|
||||||
|
assets/game-reflex-poc/data/game.json
|
||||||
|
assets/game-snake-poc/data/game.json
|
||||||
|
```
|
||||||
|
|
||||||
|
Ils servent de contrats de packaging, pas de contenu graphique définitif.
|
||||||
|
|
||||||
|
## Desktop
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 scripts/stage_game_assets.py reflex
|
||||||
|
python3 scripts/stage_game_assets.py snake
|
||||||
|
```
|
||||||
|
|
||||||
|
Le layout généré est :
|
||||||
|
|
||||||
|
```text
|
||||||
|
common/
|
||||||
|
game/
|
||||||
|
```
|
||||||
|
|
||||||
|
sous `../builds/sasedev-games/assets/<game>/` par défaut.
|
||||||
|
|
||||||
|
## Android
|
||||||
|
|
||||||
|
Chaque application utilise la tâche Gradle commune `stageSasedevAssets`.
|
||||||
|
|
||||||
|
Le package final doit contenir :
|
||||||
|
|
||||||
|
```text
|
||||||
|
assets/common/data/runtime.json
|
||||||
|
assets/game/data/game.json
|
||||||
|
```
|
||||||
|
|
||||||
|
sans inclure les assets de l'autre jeu.
|
||||||
|
|
||||||
|
## 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 Android deltas history
|
||||||
|
|
||||||
|
cargo check --workspace
|
||||||
|
cargo clippy --workspace --all-targets --all-features -- -D warnings
|
||||||
|
|
||||||
|
cargo test -p game-assets-lib --all-targets --all-features
|
||||||
|
```
|
||||||
|
|
||||||
|
## Validation staging Desktop
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 scripts/stage_game_assets.py reflex
|
||||||
|
python3 scripts/stage_game_assets.py snake
|
||||||
|
|
||||||
|
test -f ../builds/sasedev-games/assets/game-reflex-poc/common/data/runtime.json
|
||||||
|
test -f ../builds/sasedev-games/assets/game-reflex-poc/game/data/game.json
|
||||||
|
test -f ../builds/sasedev-games/assets/game-snake-poc/common/data/runtime.json
|
||||||
|
test -f ../builds/sasedev-games/assets/game-snake-poc/game/data/game.json
|
||||||
|
```
|
||||||
|
|
||||||
|
## Validation Android
|
||||||
|
|
||||||
|
Les bibliothèques natives de `0-pre.8` peuvent être réutilisées car ce delta ne modifie pas le code natif Android.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd Android
|
||||||
|
|
||||||
|
gradle :game-reflex-poc:assembleDebug
|
||||||
|
gradle :game-snake-poc:assembleDebug
|
||||||
|
|
||||||
|
cd ..
|
||||||
|
```
|
||||||
|
|
||||||
|
Vérifier le contenu :
|
||||||
|
|
||||||
|
```bash
|
||||||
|
unzip -l Android/game-reflex-poc/build/outputs/apk/debug/game-reflex-poc-debug.apk | grep 'assets/common/data/runtime.json'
|
||||||
|
unzip -l Android/game-reflex-poc/build/outputs/apk/debug/game-reflex-poc-debug.apk | grep 'assets/game/data/game.json'
|
||||||
|
|
||||||
|
unzip -l Android/game-snake-poc/build/outputs/apk/debug/game-snake-poc-debug.apk | grep 'assets/common/data/runtime.json'
|
||||||
|
unzip -l Android/game-snake-poc/build/outputs/apk/debug/game-snake-poc-debug.apk | grep 'assets/game/data/game.json'
|
||||||
|
```
|
||||||
|
|
||||||
|
Vérifier aussi qu'aucun package n'embarque le namespace source de l'autre jeu :
|
||||||
|
|
||||||
|
```bash
|
||||||
|
! unzip -l Android/game-reflex-poc/build/outputs/apk/debug/game-reflex-poc-debug.apk | grep -q 'game-snake-poc'
|
||||||
|
! unzip -l Android/game-snake-poc/build/outputs/apk/debug/game-snake-poc-debug.apk | grep -q 'game-reflex-poc'
|
||||||
|
```
|
||||||
|
|
||||||
|
## Règle de transition
|
||||||
|
|
||||||
|
Si ces gates sont propres, passer automatiquement à `0.1.0-0-pre.10` pour rendre le POC Reflex réellement jouable Desktop + Android.
|
||||||
|
|
||||||
|
En cas d'échec imputable au projet, produire `0.1.0-0-pre.9.fix.1`.
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
<!-- file: docs/000-README.md -->
|
<!-- file: docs/000-README.md -->
|
||||||
<!-- version: 10 -->
|
<!-- version: 11 -->
|
||||||
|
|
||||||
# Documentation games.sasedev
|
# Documentation games.sasedev
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<!-- file: docs/architecture/001-WORKSPACE_ARCHITECTURE.md -->
|
<!-- file: docs/architecture/001-WORKSPACE_ARCHITECTURE.md -->
|
||||||
<!-- version: 2 -->
|
<!-- version: 3 -->
|
||||||
|
|
||||||
# Architecture du workspace
|
# Architecture du workspace
|
||||||
|
|
||||||
@@ -8,6 +8,9 @@
|
|||||||
```text
|
```text
|
||||||
games.sasedev/
|
games.sasedev/
|
||||||
├── crates/
|
├── crates/
|
||||||
|
│ ├── common/
|
||||||
|
│ │ ├── game-assets-lib/
|
||||||
|
│ │ └── game-logging-lib/
|
||||||
│ ├── engines/
|
│ ├── engines/
|
||||||
│ │ ├── engine-v1-common/
|
│ │ ├── engine-v1-common/
|
||||||
│ │ ├── engine-v1-platform-api/
|
│ │ ├── engine-v1-platform-api/
|
||||||
@@ -36,7 +39,9 @@ games.sasedev/
|
|||||||
|
|
||||||
Une génération `engine-vN` est une ligne de compatibilité. Un jeu peut rester sur `engine-v1` pendant qu'un nouveau jeu expérimente `engine-v2`. Les anciens jeux sont ensuite migrés individuellement.
|
Une génération `engine-vN` est une ligne de compatibilité. Un jeu peut rester sur `engine-v1` pendant qu'un nouveau jeu expérimente `engine-v2`. Les anciens jeux sont ensuite migrés individuellement.
|
||||||
|
|
||||||
Les subdivisions sont exprimées par crates afin de ne pas forcer un jeu à dépendre de capacités inutiles. La baseline sépare déjà :
|
Les capacités transverses réutilisables sont séparées des générations moteur. `game-assets-lib` porte la résolution logique des assets et `game-logging-lib` le tracing commun.
|
||||||
|
|
||||||
|
Les subdivisions moteur sont exprimées par crates afin de ne pas forcer un jeu à dépendre de capacités inutiles. La baseline sépare déjà :
|
||||||
|
|
||||||
- `engine-v1-common` : types et comportements génériques indépendants des plateformes ;
|
- `engine-v1-common` : types et comportements génériques indépendants des plateformes ;
|
||||||
- `engine-v1-platform-api` : contrats abstraits des services plateforme ;
|
- `engine-v1-platform-api` : contrats abstraits des services plateforme ;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<!-- file: docs/architecture/005-ASSET_ARCHITECTURE.md -->
|
<!-- file: docs/architecture/005-ASSET_ARCHITECTURE.md -->
|
||||||
<!-- version: 1 -->
|
<!-- version: 2 -->
|
||||||
|
|
||||||
# Architecture des assets
|
# Architecture des assets
|
||||||
|
|
||||||
@@ -47,3 +47,48 @@ Desktop, Android et Web peuvent utiliser des mécanismes de packaging différent
|
|||||||
## Évolution
|
## Évolution
|
||||||
|
|
||||||
Un AssetManager commun pourra ultérieurement prendre en charge cache, loaders, textures, audio, fonts, données, erreurs, hot-reload de développement et éventuellement bundles. Ces capacités ne doivent être ajoutées qu'au rythme des besoins réels des jeux.
|
Un AssetManager commun pourra ultérieurement prendre en charge cache, loaders, textures, audio, fonts, données, erreurs, hot-reload de développement et éventuellement bundles. Ces capacités ne doivent être ajoutées qu'au rythme des besoins réels des jeux.
|
||||||
|
|
||||||
|
## Contrat V1 concret
|
||||||
|
|
||||||
|
À partir de `0.1.0-0-pre.9`, les URI logiques canoniques sont :
|
||||||
|
|
||||||
|
```text
|
||||||
|
common://<relative-path>
|
||||||
|
game://<relative-path>
|
||||||
|
```
|
||||||
|
|
||||||
|
`game-assets-lib` valide ces URI et les résout à partir de deux racines physiques explicites. Il interdit les chemins absolus, les traversées `..` et les séparateurs Windows injectés dans une URI logique.
|
||||||
|
|
||||||
|
Le layout runtime généré est identique sur toutes les plateformes :
|
||||||
|
|
||||||
|
```text
|
||||||
|
common/<relative-path>
|
||||||
|
game/<relative-path>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Desktop
|
||||||
|
|
||||||
|
Le script `scripts/stage_game_assets.py` compose un package généré hors des sources.
|
||||||
|
|
||||||
|
Par défaut :
|
||||||
|
|
||||||
|
```text
|
||||||
|
../builds/sasedev-games/assets/<game>/
|
||||||
|
├── common/
|
||||||
|
└── game/
|
||||||
|
```
|
||||||
|
|
||||||
|
Le développement peut aussi résoudre directement les deux racines source sans staging.
|
||||||
|
|
||||||
|
## Android
|
||||||
|
|
||||||
|
Une tâche Gradle `stageSasedevAssets` compose les assets sous `build/generated/` avant `preBuild`.
|
||||||
|
|
||||||
|
Le contenu final de l'APK conserve les namespaces :
|
||||||
|
|
||||||
|
```text
|
||||||
|
assets/common/...
|
||||||
|
assets/game/...
|
||||||
|
```
|
||||||
|
|
||||||
|
Les répertoires `build/generated/` restent des artefacts jetables.
|
||||||
|
|||||||
38
history/0.1.0/0-pre.8.md
Normal file
38
history/0.1.0/0-pre.8.md
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
<!-- file: history/0.1.0/0-pre.8.md -->
|
||||||
|
<!-- version: 1 -->
|
||||||
|
|
||||||
|
# Historique 0.1.0-0-pre.8
|
||||||
|
|
||||||
|
## Statut
|
||||||
|
|
||||||
|
Validé par l'utilisateur le 2026-09-16.
|
||||||
|
|
||||||
|
## Gates validées
|
||||||
|
|
||||||
|
Après `cargo clean` :
|
||||||
|
|
||||||
|
- `cargo fmt --all` ;
|
||||||
|
- `cargo fmt --all -- --check` ;
|
||||||
|
- audits Rust/workspace et Markdown ;
|
||||||
|
- `cargo check --workspace` ;
|
||||||
|
- Clippy global strict ;
|
||||||
|
- tests ciblés `engine-v1-common`, `engine-v1-sdl` et `game-android-entrypoint` ;
|
||||||
|
- builds Rust Android Reflex et Snake ;
|
||||||
|
- builds APK Reflex et Snake.
|
||||||
|
|
||||||
|
## Vérification native
|
||||||
|
|
||||||
|
`readelf -Ws` confirme dans les deux bibliothèques Android :
|
||||||
|
|
||||||
|
```text
|
||||||
|
SDL_main
|
||||||
|
Java_com_sasedev_games_common_NativeBridge_nativeContractVersion
|
||||||
|
```
|
||||||
|
|
||||||
|
## Limite
|
||||||
|
|
||||||
|
Aucun appareil ou émulateur n'était attaché à `adb` ; le smoke runtime Android reste donc non exécuté.
|
||||||
|
|
||||||
|
## Portée durable
|
||||||
|
|
||||||
|
Ce jalon valide `PointerState`, le mapping tactile SDL3, le contrat Java/JNI V1 et son export natif dans les deux applications.
|
||||||
40
scripts/stage_game_assets.py
Normal file
40
scripts/stage_game_assets.py
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# file: scripts/stage_game_assets.py
|
||||||
|
# version: 1
|
||||||
|
|
||||||
|
"""Stage common and game-specific source assets into the canonical runtime layout."""
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import pathlib
|
||||||
|
import shutil
|
||||||
|
|
||||||
|
GAME_DIRECTORIES = {
|
||||||
|
"reflex": "game-reflex-poc",
|
||||||
|
"snake": "game-snake-poc",
|
||||||
|
}
|
||||||
|
|
||||||
|
def copy_tree(source: pathlib.Path, destination: pathlib.Path) -> None:
|
||||||
|
"""Copy one source asset tree into one generated destination tree."""
|
||||||
|
if destination.exists():
|
||||||
|
shutil.rmtree(destination)
|
||||||
|
shutil.copytree(source, destination)
|
||||||
|
|
||||||
|
def main() -> int:
|
||||||
|
"""Stage one game's assets and print the generated root."""
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
parser.add_argument("game", choices=sorted(GAME_DIRECTORIES))
|
||||||
|
parser.add_argument("--output")
|
||||||
|
arguments = parser.parse_args()
|
||||||
|
root = pathlib.Path(__file__).resolve().parent.parent
|
||||||
|
game_directory = GAME_DIRECTORIES[arguments.game]
|
||||||
|
output = pathlib.Path(arguments.output).resolve() if arguments.output else (root.parent / "builds" / "sasedev-games" / "assets" / game_directory)
|
||||||
|
if output.exists():
|
||||||
|
shutil.rmtree(output)
|
||||||
|
output.mkdir(parents=True, exist_ok=True)
|
||||||
|
copy_tree(root / "assets" / "common", output / "common")
|
||||||
|
copy_tree(root / "assets" / game_directory, output / "game")
|
||||||
|
print(output)
|
||||||
|
return 0
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
raise SystemExit(main())
|
||||||
Reference in New Issue
Block a user