0.1.0-2-beta.1.fix.3
This commit is contained in:
@@ -1,11 +1,11 @@
|
|||||||
// file: Android/common/src/main/java/com/sasedev/games/common/NativeBridge.java
|
// file: Android/common/src/main/java/com/sasedev/games/common/NativeBridge.java
|
||||||
// version: 2
|
// version: 3
|
||||||
|
|
||||||
package com.sasedev.games.common;
|
package com.sasedev.games.common;
|
||||||
|
|
||||||
/** Stable Java/JNI platform bridge contract shared by Android game applications. */
|
/** Stable Java/JNI platform bridge contract shared by Android game applications. */
|
||||||
public final class NativeBridge {
|
public final class NativeBridge {
|
||||||
private static final int CONTRACT_VERSION = 1;
|
private static final int CONTRACT_VERSION = 2;
|
||||||
|
|
||||||
private NativeBridge() {
|
private NativeBridge() {
|
||||||
}
|
}
|
||||||
@@ -26,6 +26,9 @@ public final class NativeBridge {
|
|||||||
*/
|
*/
|
||||||
public static native int nativeContractVersion();
|
public static native int nativeContractVersion();
|
||||||
|
|
||||||
|
/** Sends a platform-native Back request to the Rust SDL runtime. */
|
||||||
|
private static native void nativeRequestPlatformBack();
|
||||||
|
|
||||||
/** Verifies that the loaded native bridge matches the Java contract. */
|
/** Verifies that the loaded native bridge matches the Java contract. */
|
||||||
public static void requireCompatibleContract() {
|
public static void requireCompatibleContract() {
|
||||||
final int nativeVersion = nativeContractVersion();
|
final int nativeVersion = nativeContractVersion();
|
||||||
@@ -34,4 +37,9 @@ public final class NativeBridge {
|
|||||||
"Native bridge contract mismatch: java=" + CONTRACT_VERSION + ", native=" + nativeVersion);
|
"Native bridge contract mismatch: java=" + CONTRACT_VERSION + ", native=" + nativeVersion);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Requests Back through the common Rust engine quit policy. */
|
||||||
|
public static void requestPlatformBack() {
|
||||||
|
nativeRequestPlatformBack();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
// file: Android/common/src/main/java/com/sasedev/games/common/SaseGameActivity.java
|
// file: Android/common/src/main/java/com/sasedev/games/common/SaseGameActivity.java
|
||||||
// version: 4
|
// version: 5
|
||||||
|
|
||||||
package com.sasedev.games.common;
|
package com.sasedev.games.common;
|
||||||
|
|
||||||
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -17,6 +18,16 @@ public abstract class SaseGameActivity extends org.libsdl.app.SDLActivity {
|
|||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
NativeBridge.requireCompatibleContract();
|
NativeBridge.requireCompatibleContract();
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||||
|
Api33BackHandler.register(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@inheritDoc} */
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
@Override
|
||||||
|
public void onBackPressed() {
|
||||||
|
NativeBridge.requestPlatformBack();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** {@inheritDoc} */
|
/** {@inheritDoc} */
|
||||||
@@ -31,4 +42,16 @@ public abstract class SaseGameActivity extends org.libsdl.app.SDLActivity {
|
|||||||
* @return game identifier
|
* @return game identifier
|
||||||
*/
|
*/
|
||||||
public abstract String gameId();
|
public abstract String gameId();
|
||||||
|
|
||||||
|
/** API 33+ predictive Back bridge isolated from pre-33 class loading. */
|
||||||
|
private static final class Api33BackHandler {
|
||||||
|
private Api33BackHandler() {
|
||||||
|
}
|
||||||
|
|
||||||
|
static void register(SaseGameActivity activity) {
|
||||||
|
activity.getOnBackInvokedDispatcher().registerOnBackInvokedCallback(
|
||||||
|
android.window.OnBackInvokedDispatcher.PRIORITY_DEFAULT,
|
||||||
|
NativeBridge::requestPlatformBack);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: Android/game-reflex-poc/build.gradle
|
// file: Android/game-reflex-poc/build.gradle
|
||||||
// version: 19
|
// version: 20
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id 'com.android.application'
|
id 'com.android.application'
|
||||||
@@ -18,7 +18,7 @@ android {
|
|||||||
minSdk 21
|
minSdk 21
|
||||||
targetSdk 36
|
targetSdk 36
|
||||||
versionCode 1
|
versionCode 1
|
||||||
versionName '0.1.0-2-beta.1.fix.2'
|
versionName '0.1.0-2-beta.1.fix.3'
|
||||||
}
|
}
|
||||||
|
|
||||||
compileOptions {
|
compileOptions {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: Android/game-snake-poc/build.gradle
|
// file: Android/game-snake-poc/build.gradle
|
||||||
// version: 19
|
// version: 20
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id 'com.android.application'
|
id 'com.android.application'
|
||||||
@@ -18,7 +18,7 @@ android {
|
|||||||
minSdk 21
|
minSdk 21
|
||||||
targetSdk 36
|
targetSdk 36
|
||||||
versionCode 1
|
versionCode 1
|
||||||
versionName '0.1.0-2-beta.1.fix.2'
|
versionName '0.1.0-2-beta.1.fix.3'
|
||||||
}
|
}
|
||||||
|
|
||||||
compileOptions {
|
compileOptions {
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ members = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
version = "0.1.0-2-beta.1.fix.2"
|
version = "0.1.0-2-beta.1.fix.3"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
repository = "https://git.sasedev.com/Sasedev/games"
|
repository = "https://git.sasedev.com/Sasedev/games"
|
||||||
@@ -30,6 +30,7 @@ publish = false
|
|||||||
serde = { version = "1", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
sdl3 = "^0.20"
|
sdl3 = "^0.20"
|
||||||
tracing = "0.1.44"
|
tracing = "0.1.44"
|
||||||
|
tracing-android = "0.2.0"
|
||||||
tracing-appender = "0.2.5"
|
tracing-appender = "0.2.5"
|
||||||
tracing-subscriber = "0.3.23"
|
tracing-subscriber = "0.3.23"
|
||||||
tauri = "2"
|
tauri = "2"
|
||||||
|
|||||||
@@ -21,6 +21,8 @@ snake = ["dep:game-snake-poc"]
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
engine-v1-common = { path = "../../engines/engine-v1-common" }
|
engine-v1-common = { path = "../../engines/engine-v1-common" }
|
||||||
engine-v1-sdl = { path = "../../engines/engine-v1-sdl" }
|
engine-v1-sdl = { path = "../../engines/engine-v1-sdl" }
|
||||||
|
game-logging-lib = { path = "../../common/game-logging-lib" }
|
||||||
|
tracing.workspace = true
|
||||||
game-reflex-poc = { path = "../../games/game-reflex-poc", optional = true }
|
game-reflex-poc = { path = "../../games/game-reflex-poc", optional = true }
|
||||||
game-snake-poc = { path = "../../games/game-snake-poc", optional = true }
|
game-snake-poc = { path = "../../games/game-snake-poc", optional = true }
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: crates/apps/game-android-entrypoint/src/lib.rs
|
// file: crates/apps/game-android-entrypoint/src/lib.rs
|
||||||
// version: 2
|
// version: 3
|
||||||
|
|
||||||
#![warn(missing_docs)]
|
#![warn(missing_docs)]
|
||||||
#![deny(unreachable_pub)]
|
#![deny(unreachable_pub)]
|
||||||
@@ -21,7 +21,13 @@ extern "C" fn sdl_main(_argc: core::ffi::c_int, _argv: *mut *mut core::ffi::c_ch
|
|||||||
|
|
||||||
#[unsafe(export_name = "Java_com_sasedev_games_common_NativeBridge_nativeContractVersion")]
|
#[unsafe(export_name = "Java_com_sasedev_games_common_NativeBridge_nativeContractVersion")]
|
||||||
extern "C" fn native_contract_version(_environment: *mut core::ffi::c_void, _class: *mut core::ffi::c_void) -> core::ffi::c_int {
|
extern "C" fn native_contract_version(_environment: *mut core::ffi::c_void, _class: *mut core::ffi::c_void) -> core::ffi::c_int {
|
||||||
return 1;
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unsafe(export_name = "Java_com_sasedev_games_common_NativeBridge_nativeRequestPlatformBack")]
|
||||||
|
extern "C" fn native_request_platform_back(_environment: *mut core::ffi::c_void, _class: *mut core::ffi::c_void) {
|
||||||
|
engine_v1_sdl::request_platform_back();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "reflex")]
|
#[cfg(feature = "reflex")]
|
||||||
@@ -48,9 +54,15 @@ fn run_game<G>(runtime: &engine_v1_sdl::SdlRuntime, game: &mut G) -> core::ffi::
|
|||||||
where
|
where
|
||||||
G: engine_v1_common::EngineGame,
|
G: engine_v1_common::EngineGame,
|
||||||
{
|
{
|
||||||
|
let logging_guard = game_logging_lib::init_console_tracing();
|
||||||
|
match &logging_guard {
|
||||||
|
std::result::Result::Ok(_) => tracing::info!(target: "games::android", "Android tracing initialized"),
|
||||||
|
std::result::Result::Err(error) => eprintln!("Android tracing initialization skipped: {error}"),
|
||||||
|
}
|
||||||
match runtime.run(game) {
|
match runtime.run(game) {
|
||||||
std::result::Result::Ok(()) => return 0,
|
std::result::Result::Ok(()) => return 0,
|
||||||
std::result::Result::Err(error) => {
|
std::result::Result::Err(error) => {
|
||||||
|
tracing::error!(target: "games::android", error = %error, "Android SDL3 runtime failed");
|
||||||
eprintln!("Android SDL3 runtime error: {error}");
|
eprintln!("Android SDL3 runtime error: {error}");
|
||||||
return 1;
|
return 1;
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "game-reflex-poc-tauri",
|
"name": "game-reflex-poc-tauri",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.1.0-2-beta.1.fix.2",
|
"version": "0.1.0-2-beta.1.fix.3",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://schema.tauri.app/config/2",
|
"$schema": "https://schema.tauri.app/config/2",
|
||||||
"productName": "Reflex POC Tauri",
|
"productName": "Reflex POC Tauri",
|
||||||
"version": "0.1.0-2-beta.1.fix.2",
|
"version": "0.1.0-2-beta.1.fix.3",
|
||||||
"identifier": "com.sasedev.games.reflex.tauri",
|
"identifier": "com.sasedev.games.reflex.tauri",
|
||||||
"build": {
|
"build": {
|
||||||
"beforeDevCommand": {
|
"beforeDevCommand": {
|
||||||
|
|||||||
@@ -17,3 +17,6 @@ tracing-subscriber = { workspace = true, features = ["fmt"] }
|
|||||||
|
|
||||||
[lints]
|
[lints]
|
||||||
workspace = true
|
workspace = true
|
||||||
|
|
||||||
|
[target.'cfg(target_os = "android")'.dependencies]
|
||||||
|
tracing-android.workspace = true
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: crates/common/game-logging-lib/src/lib.rs
|
// file: crates/common/game-logging-lib/src/lib.rs
|
||||||
// version: 1
|
// version: 2
|
||||||
|
|
||||||
#![warn(missing_docs)]
|
#![warn(missing_docs)]
|
||||||
#![deny(unreachable_pub)]
|
#![deny(unreachable_pub)]
|
||||||
@@ -10,11 +10,11 @@
|
|||||||
mod runtime;
|
mod runtime;
|
||||||
mod test_support;
|
mod test_support;
|
||||||
|
|
||||||
/// Re-export of the console tracing initialization error.
|
/// Re-export of the platform tracing initialization error.
|
||||||
pub use self::runtime::LoggingInitError;
|
pub use self::runtime::LoggingInitError;
|
||||||
/// Re-export of the guard keeping the non-blocking tracing writer alive.
|
/// Re-export of the guard keeping platform tracing resources alive.
|
||||||
pub use self::runtime::LoggingWorkerGuard;
|
pub use self::runtime::LoggingWorkerGuard;
|
||||||
/// Re-export of the standard console tracing initializer.
|
/// Re-export of the platform tracing initializer.
|
||||||
pub use self::runtime::init_console_tracing;
|
pub use self::runtime::init_console_tracing;
|
||||||
/// Re-export of the scoped tracing helper intended for tests.
|
/// Re-export of the scoped tracing helper intended for tests.
|
||||||
pub use self::test_support::with_test_tracing;
|
pub use self::test_support::with_test_tracing;
|
||||||
|
|||||||
@@ -1,31 +1,50 @@
|
|||||||
// file: crates/common/game-logging-lib/src/runtime.rs
|
// file: crates/common/game-logging-lib/src/runtime.rs
|
||||||
// version: 1
|
// version: 2
|
||||||
|
|
||||||
/// Error returned when the process-global tracing subscriber is already configured.
|
/// Error returned when the process-global tracing subscriber cannot be configured.
|
||||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
pub struct LoggingInitError;
|
pub struct LoggingInitError;
|
||||||
|
|
||||||
impl std::fmt::Display for LoggingInitError {
|
impl std::fmt::Display for LoggingInitError {
|
||||||
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
return formatter.write_str("the process-global tracing subscriber is already configured");
|
return formatter.write_str("the process-global tracing subscriber could not be configured");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::error::Error for LoggingInitError {}
|
impl std::error::Error for LoggingInitError {}
|
||||||
|
|
||||||
/// Guard keeping the non-blocking tracing writer alive for the application lifetime.
|
/// Guard keeping platform tracing resources alive for the application lifetime.
|
||||||
pub struct LoggingWorkerGuard {
|
pub struct LoggingWorkerGuard {
|
||||||
|
#[cfg(not(target_os = "android"))]
|
||||||
_worker_guard: tracing_appender::non_blocking::WorkerGuard,
|
_worker_guard: tracing_appender::non_blocking::WorkerGuard,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Initializes a process-global non-blocking tracing subscriber writing formatted events to standard error.
|
/// Initializes process-global tracing for the current platform.
|
||||||
|
///
|
||||||
|
/// Native desktop/Tauri processes write formatted events to standard error.
|
||||||
|
/// Android processes write directly to logcat through the Android NDK logging API.
|
||||||
///
|
///
|
||||||
/// The returned guard must remain alive for as long as events may still be emitted.
|
/// The returned guard must remain alive for as long as events may still be emitted.
|
||||||
pub fn init_console_tracing() -> std::result::Result<LoggingWorkerGuard, LoggingInitError> {
|
pub fn init_console_tracing() -> std::result::Result<LoggingWorkerGuard, LoggingInitError> {
|
||||||
let (writer, worker_guard) = tracing_appender::non_blocking(std::io::stderr());
|
#[cfg(target_os = "android")]
|
||||||
let subscriber = tracing_subscriber::fmt().with_target(true).with_writer(writer).finish();
|
{
|
||||||
if tracing::subscriber::set_global_default(subscriber).is_err() {
|
let layer = match tracing_android::layer("games.sasedev") {
|
||||||
return std::result::Result::Err(LoggingInitError);
|
std::result::Result::Ok(value) => value,
|
||||||
|
std::result::Result::Err(_) => return std::result::Result::Err(LoggingInitError),
|
||||||
|
};
|
||||||
|
let subscriber = tracing_subscriber::layer::SubscriberExt::with(tracing_subscriber::registry(), layer);
|
||||||
|
if tracing::subscriber::set_global_default(subscriber).is_err() {
|
||||||
|
return std::result::Result::Err(LoggingInitError);
|
||||||
|
}
|
||||||
|
return std::result::Result::Ok(LoggingWorkerGuard {});
|
||||||
|
}
|
||||||
|
#[cfg(not(target_os = "android"))]
|
||||||
|
{
|
||||||
|
let (writer, worker_guard) = tracing_appender::non_blocking(std::io::stderr());
|
||||||
|
let subscriber = tracing_subscriber::fmt().with_target(true).with_writer(writer).finish();
|
||||||
|
if tracing::subscriber::set_global_default(subscriber).is_err() {
|
||||||
|
return std::result::Result::Err(LoggingInitError);
|
||||||
|
}
|
||||||
|
return std::result::Result::Ok(LoggingWorkerGuard { _worker_guard: worker_guard });
|
||||||
}
|
}
|
||||||
return std::result::Result::Ok(LoggingWorkerGuard { _worker_guard: worker_guard });
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: crates/engines/engine-v1-sdl/src/lib.rs
|
// file: crates/engines/engine-v1-sdl/src/lib.rs
|
||||||
// version: 3
|
// version: 4
|
||||||
|
|
||||||
#![warn(missing_docs)]
|
#![warn(missing_docs)]
|
||||||
#![deny(unreachable_pub)]
|
#![deny(unreachable_pub)]
|
||||||
@@ -12,3 +12,5 @@ mod unit_tests;
|
|||||||
|
|
||||||
/// Re-export of the minimal SDL3 runtime used by Desktop POC runners.
|
/// Re-export of the minimal SDL3 runtime used by Desktop POC runners.
|
||||||
pub use self::runtime::SdlRuntime;
|
pub use self::runtime::SdlRuntime;
|
||||||
|
/// Re-export of the platform-native Back request bridge.
|
||||||
|
pub use self::runtime::request_platform_back;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: crates/engines/engine-v1-sdl/src/runtime.rs
|
// file: crates/engines/engine-v1-sdl/src/runtime.rs
|
||||||
// version: 11
|
// version: 12
|
||||||
|
|
||||||
/// Minimal SDL3 runtime used by Desktop and Android POC runners.
|
/// Minimal SDL3 runtime used by Desktop and Android POC runners.
|
||||||
pub struct SdlRuntime {
|
pub struct SdlRuntime {
|
||||||
@@ -9,6 +9,17 @@ pub struct SdlRuntime {
|
|||||||
frame_duration: std::time::Duration,
|
frame_duration: std::time::Duration,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static PLATFORM_BACK_REQUESTED: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(false);
|
||||||
|
|
||||||
|
/// Requests the active SDL runtime to evaluate a platform-native Back action.
|
||||||
|
///
|
||||||
|
/// Platform adapters use this signal when the host operating system does not expose Back
|
||||||
|
/// as a normal SDL keyboard event.
|
||||||
|
pub fn request_platform_back() {
|
||||||
|
PLATFORM_BACK_REQUESTED.store(true, std::sync::atomic::Ordering::Release);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
impl SdlRuntime {
|
impl SdlRuntime {
|
||||||
/// Creates a minimal SDL3 runtime configuration.
|
/// Creates a minimal SDL3 runtime configuration.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
@@ -21,10 +32,6 @@ impl SdlRuntime {
|
|||||||
where
|
where
|
||||||
G: engine_v1_common::EngineGame,
|
G: engine_v1_common::EngineGame,
|
||||||
{
|
{
|
||||||
#[cfg(target_os = "android")]
|
|
||||||
{
|
|
||||||
let _ = sdl3::hint::set(sdl3::hint::names::ANDROID_TRAP_BACK_BUTTON, "1");
|
|
||||||
}
|
|
||||||
let sdl = match sdl3::init() {
|
let sdl = match sdl3::init() {
|
||||||
std::result::Result::Ok(value) => value,
|
std::result::Result::Ok(value) => value,
|
||||||
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
|
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
|
||||||
@@ -47,6 +54,12 @@ impl SdlRuntime {
|
|||||||
let mut pointer = engine_v1_common::PointerState::inactive();
|
let mut pointer = engine_v1_common::PointerState::inactive();
|
||||||
tracing::info!(title = %self.title, width = self.width, height = self.height, "SDL3 runtime started");
|
tracing::info!(title = %self.title, width = self.width, height = self.height, "SDL3 runtime started");
|
||||||
'running: loop {
|
'running: loop {
|
||||||
|
if take_platform_back_request() {
|
||||||
|
tracing::info!(source = "platform_back", "SDL3 platform Back requested");
|
||||||
|
if quit_requested(game, engine_v1_common::QuitSource::PlatformBack) {
|
||||||
|
break 'running;
|
||||||
|
}
|
||||||
|
}
|
||||||
let mut input = engine_v1_common::InputState::none().with_pointer(pointer);
|
let mut input = engine_v1_common::InputState::none().with_pointer(pointer);
|
||||||
for event in events.poll_iter() {
|
for event in events.poll_iter() {
|
||||||
match event {
|
match event {
|
||||||
@@ -166,6 +179,10 @@ fn render_scene(canvas: &mut sdl3::render::WindowCanvas, scene: engine_v1_common
|
|||||||
|
|
||||||
const SWIPE_DIRECTION_THRESHOLD: f32 = 0.04;
|
const SWIPE_DIRECTION_THRESHOLD: f32 = 0.04;
|
||||||
|
|
||||||
|
fn take_platform_back_request() -> bool {
|
||||||
|
return PLATFORM_BACK_REQUESTED.swap(false, std::sync::atomic::Ordering::AcqRel);
|
||||||
|
}
|
||||||
|
|
||||||
fn quit_requested<G>(game: &mut G, source: engine_v1_common::QuitSource) -> bool
|
fn quit_requested<G>(game: &mut G, source: engine_v1_common::QuitSource) -> bool
|
||||||
where
|
where
|
||||||
G: engine_v1_common::EngineGame,
|
G: engine_v1_common::EngineGame,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: crates/engines/engine-v1-sdl/unit_tests/swipe.rs
|
// file: crates/engines/engine-v1-sdl/unit_tests/swipe.rs
|
||||||
// version: 3
|
// version: 4
|
||||||
|
|
||||||
fn pointer(x: f32, y: f32) -> engine_v1_common::PointerState {
|
fn pointer(x: f32, y: f32) -> engine_v1_common::PointerState {
|
||||||
return engine_v1_common::PointerState::normalized(true, x, y);
|
return engine_v1_common::PointerState::normalized(true, x, y);
|
||||||
@@ -48,3 +48,10 @@ fn runtime_respects_game_quit_decision() {
|
|||||||
assert!(!super::quit_requested(&mut game, engine_v1_common::QuitSource::PlatformBack));
|
assert!(!super::quit_requested(&mut game, engine_v1_common::QuitSource::PlatformBack));
|
||||||
assert_eq!(game.requests, 1);
|
assert_eq!(game.requests, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn platform_back_bridge_is_consumed_once() {
|
||||||
|
super::request_platform_back();
|
||||||
|
assert!(super::take_platform_back_request());
|
||||||
|
assert!(!super::take_platform_back_request());
|
||||||
|
}
|
||||||
|
|||||||
117
deltas/0.1.0/2-beta.1.fix.3.md
Normal file
117
deltas/0.1.0/2-beta.1.fix.3.md
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
<!-- file: deltas/0.1.0/2-beta.1.fix.3.md -->
|
||||||
|
<!-- version: 1 -->
|
||||||
|
|
||||||
|
# Delta 0.1.0-2-beta.1.fix.3
|
||||||
|
|
||||||
|
## Base
|
||||||
|
|
||||||
|
Base déclarée : `0.1.0-2-beta.1.fix.2`.
|
||||||
|
|
||||||
|
## Échecs observés sur AVD API 36
|
||||||
|
|
||||||
|
Le gameplay Reflex et Snake fonctionne en x86_64, mais :
|
||||||
|
|
||||||
|
- le bouton Back de l'AVD ne ferme pas le runtime ;
|
||||||
|
- `logcat` ne montre aucun événement `tracing` du moteur.
|
||||||
|
|
||||||
|
## Back Android 16
|
||||||
|
|
||||||
|
Le projet cible API 36.
|
||||||
|
|
||||||
|
Sur Android 16, `onBackPressed()` et `KEYCODE_BACK` ne constituent plus la voie de référence. Le common Android enregistre désormais `OnBackInvokedCallback` sur API 33+.
|
||||||
|
|
||||||
|
La chaîne devient :
|
||||||
|
|
||||||
|
```text
|
||||||
|
Android Back
|
||||||
|
-> SaseGameActivity
|
||||||
|
-> NativeBridge.requestPlatformBack()
|
||||||
|
-> JNI nativeRequestPlatformBack()
|
||||||
|
-> engine-v1-sdl::request_platform_back()
|
||||||
|
-> QuitSource::PlatformBack
|
||||||
|
-> EngineGame::quit_requested()
|
||||||
|
```
|
||||||
|
|
||||||
|
Le mapping SDL `AcBack` reste accepté comme événement SDL générique, mais Android ne dépend plus du hint `SDL_ANDROID_TRAP_BACK_BUTTON`.
|
||||||
|
|
||||||
|
## Logging Android
|
||||||
|
|
||||||
|
`game-android-entrypoint` initialise désormais `game-logging-lib`.
|
||||||
|
|
||||||
|
Sur Android, `game-logging-lib` utilise `tracing-android` pour écrire directement vers logcat avec le tag :
|
||||||
|
|
||||||
|
```text
|
||||||
|
games.sasedev
|
||||||
|
```
|
||||||
|
|
||||||
|
Desktop et Tauri conservent le writer stderr existant.
|
||||||
|
|
||||||
|
## Contrat JNI
|
||||||
|
|
||||||
|
Le contrat Java/JNI passe de `1` à `2`.
|
||||||
|
|
||||||
|
Nouvel export autorisé et audité :
|
||||||
|
|
||||||
|
```text
|
||||||
|
Java_com_sasedev_games_common_NativeBridge_nativeRequestPlatformBack
|
||||||
|
```
|
||||||
|
|
||||||
|
## Validation statique/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
|
||||||
|
python3 scripts/audit_distribution_layout.py
|
||||||
|
|
||||||
|
cargo check --workspace
|
||||||
|
cargo clippy --workspace --all-targets --all-features -- -D warnings
|
||||||
|
|
||||||
|
cargo test -p engine-v1-sdl --all-targets --all-features
|
||||||
|
cargo test -p game-logging-lib --all-targets --all-features
|
||||||
|
```
|
||||||
|
|
||||||
|
## Android ARM64
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 scripts/build_android_rust.py reflex --abi arm64-v8a
|
||||||
|
python3 scripts/build_android_rust.py snake --abi arm64-v8a
|
||||||
|
|
||||||
|
cd Android
|
||||||
|
gradle :game-reflex-poc:assembleDebug
|
||||||
|
gradle :game-snake-poc:assembleDebug
|
||||||
|
cd ..
|
||||||
|
```
|
||||||
|
|
||||||
|
## Android x86_64
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 scripts/build_android_rust.py reflex --abi x86_64
|
||||||
|
python3 scripts/build_android_rust.py snake --abi x86_64
|
||||||
|
|
||||||
|
cd Android
|
||||||
|
gradle :game-reflex-poc:assembleDebug
|
||||||
|
gradle :game-snake-poc:assembleDebug
|
||||||
|
cd ..
|
||||||
|
```
|
||||||
|
|
||||||
|
## Smoke logcat correct
|
||||||
|
|
||||||
|
Vider logcat avant le lancement :
|
||||||
|
|
||||||
|
```bash
|
||||||
|
adb -s emulator-5554 logcat -c
|
||||||
|
adb -s emulator-5554 shell am start -n com.sasedev.games.reflex/.ReflexActivity
|
||||||
|
adb -s emulator-5554 logcat | grep -iE 'games\.sasedev|com\.sasedev\.games|SDL|AndroidRuntime|FATAL|panic'
|
||||||
|
```
|
||||||
|
|
||||||
|
Attendu :
|
||||||
|
|
||||||
|
- événement d'initialisation tracing Android ;
|
||||||
|
- événement `SDL3 runtime started` ;
|
||||||
|
- sur Back, événement `SDL3 platform Back requested` ;
|
||||||
|
- événement `SDL3 runtime stopped`.
|
||||||
|
|
||||||
|
Répéter pour Snake et, après validation AVD, refaire un smoke court ARM64 réel.
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
<!-- file: docs/development/007-ANDROID_JNI_BRIDGE.md -->
|
<!-- file: docs/development/007-ANDROID_JNI_BRIDGE.md -->
|
||||||
<!-- version: 1 -->
|
<!-- version: 2 -->
|
||||||
|
|
||||||
# Bridge Java/JNI Android
|
# Bridge Java/JNI Android
|
||||||
|
|
||||||
@@ -14,7 +14,7 @@ L'input tactile de base ne passe pas par JNI : SDL3 fournit directement les év
|
|||||||
La baseline expose un unique appel Java vers Rust :
|
La baseline expose un unique appel Java vers Rust :
|
||||||
|
|
||||||
```text
|
```text
|
||||||
NativeBridge.nativeContractVersion() -> 1
|
NativeBridge.nativeContractVersion() -> 2
|
||||||
```
|
```
|
||||||
|
|
||||||
`SaseGameActivity` vérifie ce contrat après `SDLActivity.onCreate()`.
|
`SaseGameActivity` vérifie ce contrat après `SDLActivity.onCreate()`.
|
||||||
@@ -45,3 +45,20 @@ L'export JNI utilise un symbole nommé explicitement et reçoit deux pointeurs o
|
|||||||
Aucun bloc `unsafe` ni fonction `unsafe` n'est autorisé.
|
Aucun bloc `unsafe` ni fonction `unsafe` n'est autorisé.
|
||||||
|
|
||||||
Si un futur service nécessite de manipuler réellement `JNIEnv`, il devra faire l'objet d'une tranche distincte avec une politique FFI réévaluée plutôt que d'élargir implicitement l'exception actuelle.
|
Si un futur service nécessite de manipuler réellement `JNIEnv`, il devra faire l'objet d'une tranche distincte avec une politique FFI réévaluée plutôt que d'élargir implicitement l'exception actuelle.
|
||||||
|
|
||||||
|
## Back Android
|
||||||
|
|
||||||
|
Le contrat v2 ajoute un pont Java/JNI explicite pour Back :
|
||||||
|
|
||||||
|
```text
|
||||||
|
SaseGameActivity
|
||||||
|
-> NativeBridge.requestPlatformBack()
|
||||||
|
-> nativeRequestPlatformBack()
|
||||||
|
-> engine-v1-sdl::request_platform_back()
|
||||||
|
-> EngineGame::quit_requested(QuitSource::PlatformBack)
|
||||||
|
```
|
||||||
|
|
||||||
|
Sur API 33+, `SaseGameActivity` enregistre un `OnBackInvokedCallback`.
|
||||||
|
Sur les versions antérieures, `onBackPressed()` reste le fallback.
|
||||||
|
|
||||||
|
Cette voie remplace la dépendance au mapping variable du bouton Back vers `SDL_SCANCODE_AC_BACK`.
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<!-- file: docs/rules/RULES_PROJECT.md -->
|
<!-- file: docs/rules/RULES_PROJECT.md -->
|
||||||
<!-- version: 8 -->
|
<!-- version: 9 -->
|
||||||
|
|
||||||
# Règles spécifiques games.sasedev
|
# Règles spécifiques games.sasedev
|
||||||
|
|
||||||
@@ -72,3 +72,6 @@
|
|||||||
- **GAME-PLATFORM-013** — Les demandes de sortie Tauri sont évaluées par la politique `EngineGame::quit_requested` avant toute fermeture native.
|
- **GAME-PLATFORM-013** — Les demandes de sortie Tauri sont évaluées par la politique `EngineGame::quit_requested` avant toute fermeture native.
|
||||||
|
|
||||||
- **GAME-BUILD-001** — Les artefacts générés Cargo, Tauri, Vite et caches frontend sont placés hors de la racine du dépôt, sous `../builds/sasedev-games/`; un répertoire racine `builds/` dans le dépôt est interdit.
|
- **GAME-BUILD-001** — Les artefacts générés Cargo, Tauri, Vite et caches frontend sont placés hors de la racine du dépôt, sous `../builds/sasedev-games/`; un répertoire racine `builds/` dans le dépôt est interdit.
|
||||||
|
|
||||||
|
- **GAME-PLATFORM-014** — Android API 33+ traite Back via `OnBackInvokedCallback`; le bridge Java/JNI doit convertir cette action en `QuitSource::PlatformBack` et laisser `EngineGame::quit_requested` décider.
|
||||||
|
- **GAME-PLATFORM-015** — Les exécutables Android initialisent le subscriber partagé et envoient les événements `tracing` vers logcat ; `stderr` n'est pas la destination Android de référence.
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<!-- file: docs/rules/RULES_RUST.md -->
|
<!-- file: docs/rules/RULES_RUST.md -->
|
||||||
<!-- version: 5 -->
|
<!-- version: 6 -->
|
||||||
|
|
||||||
# Règles Rust générales
|
# Règles Rust générales
|
||||||
|
|
||||||
@@ -8,7 +8,7 @@
|
|||||||
- **RUST-BASE-001** — L'édition Rust est Rust 2024.
|
- **RUST-BASE-001** — L'édition Rust est Rust 2024.
|
||||||
- **RUST-BASE-002** — Chaque `lib.rs` et `main.rs` active `missing_docs`, `unreachable_pub` et interdit `unsafe_code`, sauf exception FFI explicitement définie par cette règle.
|
- **RUST-BASE-002** — Chaque `lib.rs` et `main.rs` active `missing_docs`, `unreachable_pub` et interdit `unsafe_code`, sauf exception FFI explicitement définie par cette règle.
|
||||||
- **RUST-BASE-003** — Les lints communs sont déclarés au workspace et hérités par les crates ; une crate FFI exceptée peut déclarer localement le même profil avec uniquement le niveau `unsafe_code` ajusté.
|
- **RUST-BASE-003** — Les lints communs sont déclarés au workspace et hérités par les crates ; une crate FFI exceptée peut déclarer localement le même profil avec uniquement le niveau `unsafe_code` ajusté.
|
||||||
- **RUST-BASE-004** — Les blocs `unsafe` et fonctions `unsafe` sont interdits. L’unique exception actuelle est `crates/apps/game-android-entrypoint/src/lib.rs`, autorisé à porter uniquement les attributs `#[unsafe(export_name = "SDL_main")]` et `#[unsafe(export_name = "Java_com_sasedev_games_common_NativeBridge_nativeContractVersion")]` nécessaires aux frontières SDL Android et JNI. Cette exception n’autorise aucun déréférencement de pointeur brut, aucun autre attribut unsafe, aucun bloc `unsafe` et aucune fonction `unsafe`.
|
- **RUST-BASE-004** — Les blocs `unsafe` et fonctions `unsafe` sont interdits. L’unique exception actuelle est `crates/apps/game-android-entrypoint/src/lib.rs`, autorisé à porter uniquement les attributs `#[unsafe(export_name = "SDL_main")]` , `#[unsafe(export_name = "Java_com_sasedev_games_common_NativeBridge_nativeContractVersion")]` et `#[unsafe(export_name = "Java_com_sasedev_games_common_NativeBridge_nativeRequestPlatformBack")]` nécessaires aux frontières SDL Android et JNI. Cette exception n’autorise aucun déréférencement de pointeur brut, aucun autre attribut unsafe, aucun bloc `unsafe` et aucune fonction `unsafe`.
|
||||||
- **RUST-BASE-005** — Tout fichier Rust possède les en-têtes `// file: ...` et `// version: N`.
|
- **RUST-BASE-005** — Tout fichier Rust possède les en-têtes `// file: ...` et `// version: N`.
|
||||||
- **RUST-DEP-001** — Une dépendance tierce partagée déclare uniquement sa contrainte de version canonique sous `[workspace.dependencies]`, sauf exception normative explicitement documentée.
|
- **RUST-DEP-001** — Une dépendance tierce partagée déclare uniquement sa contrainte de version canonique sous `[workspace.dependencies]`, sauf exception normative explicitement documentée.
|
||||||
- **RUST-DEP-002** — Les features d'une dépendance tierce sont activées dans le `Cargo.toml` de la crate qui en a réellement besoin, via `workspace = true`; elles ne sont pas activées globalement au workspace par commodité.
|
- **RUST-DEP-002** — Les features d'une dépendance tierce sont activées dans le `Cargo.toml` de la crate qui en a réellement besoin, via `workspace = true`; elles ne sont pas activées globalement au workspace par commodité.
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<!-- file: docs/testing/002-BETA_VALIDATION_MATRIX.md -->
|
<!-- file: docs/testing/002-BETA_VALIDATION_MATRIX.md -->
|
||||||
<!-- version: 1 -->
|
<!-- version: 2 -->
|
||||||
|
|
||||||
# Matrice de validation beta 0.1.0
|
# Matrice de validation beta 0.1.0
|
||||||
|
|
||||||
@@ -128,3 +128,21 @@ cargo test --workspace --all-targets --all-features
|
|||||||
```
|
```
|
||||||
|
|
||||||
Cette suite complète ne devient pas une gate de chaque futur correctif beta.
|
Cette suite complète ne devient pas une gate de chaque futur correctif beta.
|
||||||
|
|
||||||
|
## Logcat Android
|
||||||
|
|
||||||
|
Le buffer doit être vidé avant le lancement du jeu, pas après :
|
||||||
|
|
||||||
|
```bash
|
||||||
|
adb -s <device> logcat -c
|
||||||
|
adb -s <device> shell am start -n <package>/<activity>
|
||||||
|
adb -s <device> logcat | grep -iE 'games\.sasedev|com\.sasedev\.games|SDL|AndroidRuntime|FATAL|panic'
|
||||||
|
```
|
||||||
|
|
||||||
|
Android utilise un subscriber `tracing` dédié à logcat.
|
||||||
|
|
||||||
|
## Back Android 16
|
||||||
|
|
||||||
|
Les applications ciblent API 36. Le Back système est donc traité via `OnBackInvokedCallback` sur API 33+ puis converti en `QuitSource::PlatformBack`.
|
||||||
|
|
||||||
|
Le fallback `onBackPressed()` reste réservé aux versions Android antérieures.
|
||||||
|
|||||||
@@ -522,6 +522,7 @@ def audit_file(root: pathlib.Path, path: pathlib.Path) -> list[Violation]:
|
|||||||
if ffi_export_exception:
|
if ffi_export_exception:
|
||||||
required_export_attributes = (
|
required_export_attributes = (
|
||||||
'#[unsafe(export_name = "Java_com_sasedev_games_common_NativeBridge_nativeContractVersion")]',
|
'#[unsafe(export_name = "Java_com_sasedev_games_common_NativeBridge_nativeContractVersion")]',
|
||||||
|
'#[unsafe(export_name = "Java_com_sasedev_games_common_NativeBridge_nativeRequestPlatformBack")]',
|
||||||
'#[unsafe(export_name = "SDL_main")]',
|
'#[unsafe(export_name = "SDL_main")]',
|
||||||
)
|
)
|
||||||
for export_attribute in required_export_attributes:
|
for export_attribute in required_export_attributes:
|
||||||
|
|||||||
Reference in New Issue
Block a user