0.1.0-0-pre.9
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
<!-- file: Android/README.md -->
|
||||
<!-- version: 5 -->
|
||||
<!-- version: 6 -->
|
||||
|
||||
# Android
|
||||
|
||||
@@ -20,7 +20,7 @@ Chaque module `game-*` est une application Android indépendante ; il dépend du
|
||||
|
||||
## 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` ;
|
||||
- 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
|
||||
|
||||
À 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.
|
||||
|
||||
## 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
|
||||
// version: 6
|
||||
// version: 7
|
||||
|
||||
plugins {
|
||||
id 'com.android.application'
|
||||
@@ -16,7 +16,7 @@ android {
|
||||
minSdk 21
|
||||
targetSdk 36
|
||||
versionCode 1
|
||||
versionName '0.1.0-0-pre.8'
|
||||
versionName '0.1.0-0-pre.9'
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
@@ -29,3 +29,6 @@ dependencies {
|
||||
implementation files("../libs/${sdl3AarName}")
|
||||
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
|
||||
// version: 6
|
||||
// version: 7
|
||||
|
||||
plugins {
|
||||
id 'com.android.application'
|
||||
@@ -16,7 +16,7 @@ android {
|
||||
minSdk 21
|
||||
targetSdk 36
|
||||
versionCode 1
|
||||
versionName '0.1.0-0-pre.8'
|
||||
versionName '0.1.0-0-pre.9'
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
@@ -29,3 +29,6 @@ dependencies {
|
||||
implementation files("../libs/${sdl3AarName}")
|
||||
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
|
||||
# version: 16
|
||||
# version: 17
|
||||
|
||||
[workspace]
|
||||
resolver = "3"
|
||||
@@ -11,12 +11,13 @@ members = [
|
||||
"crates/games/game-snake-poc",
|
||||
"crates/apps/game-reflex-poc-desktop",
|
||||
"crates/apps/game-snake-poc-desktop",
|
||||
"crates/common/game-assets-lib",
|
||||
"crates/common/game-logging-lib",
|
||||
"crates/apps/game-android-entrypoint",
|
||||
]
|
||||
|
||||
[workspace.package]
|
||||
version = "0.1.0-0-pre.8"
|
||||
version = "0.1.0-0-pre.9"
|
||||
edition = "2024"
|
||||
license = "MIT"
|
||||
repository = "https://git.sasedev.com/Sasedev/games"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!-- file: README.md -->
|
||||
<!-- version: 4 -->
|
||||
<!-- version: 5 -->
|
||||
|
||||
# games.sasedev
|
||||
|
||||
@@ -39,4 +39,4 @@ Les deux premiers jeux sont des POC structurels : `game-reflex-poc` et `game-sna
|
||||
|
||||
## 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 -->
|
||||
<!-- version: 6 -->
|
||||
<!-- version: 7 -->
|
||||
|
||||
# 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.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.
|
||||
- [ ] `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.7` — compilation Rust Android `cdylib`, point d'entrée SDL Android et premier lancement APK sur appareil/émulateur.
|
||||
- [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.10` — POC Reflex jouable Desktop + Android.
|
||||
- [ ] `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 -->
|
||||
<!-- version: 10 -->
|
||||
<!-- version: 11 -->
|
||||
|
||||
# Documentation games.sasedev
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!-- file: docs/architecture/001-WORKSPACE_ARCHITECTURE.md -->
|
||||
<!-- version: 2 -->
|
||||
<!-- version: 3 -->
|
||||
|
||||
# Architecture du workspace
|
||||
|
||||
@@ -8,6 +8,9 @@
|
||||
```text
|
||||
games.sasedev/
|
||||
├── crates/
|
||||
│ ├── common/
|
||||
│ │ ├── game-assets-lib/
|
||||
│ │ └── game-logging-lib/
|
||||
│ ├── engines/
|
||||
│ │ ├── engine-v1-common/
|
||||
│ │ ├── 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.
|
||||
|
||||
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-platform-api` : contrats abstraits des services plateforme ;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!-- file: docs/architecture/005-ASSET_ARCHITECTURE.md -->
|
||||
<!-- version: 1 -->
|
||||
<!-- version: 2 -->
|
||||
|
||||
# Architecture des assets
|
||||
|
||||
@@ -47,3 +47,48 @@ Desktop, Android et Web peuvent utiliser des mécanismes de packaging différent
|
||||
## É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.
|
||||
|
||||
## 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