mirror of
https://github.com/Cesarus85/ringfall-mjolnir-vr.git
synced 2026-10-17 13:00:25 +02:00
Compare commits
4
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0fc85d6c67 | ||
|
|
c0a08f395e | ||
|
|
06cb70fb58 | ||
|
|
1709698053 |
No files matched your search
@@ -0,0 +1,74 @@
|
||||
# Steam Frame
|
||||
|
||||
The same APK as for the Quest runs on the Steam Frame, in Lepton (SteamOS's
|
||||
Android, a Waydroid fork: Android 11, arm64), with SteamVR's OpenXR runtime.
|
||||
What differs is found at run time, not by a build of its own.
|
||||
|
||||
## The device
|
||||
|
||||
- Steam Settings > System > Enable Developer Mode; then start "Lepton
|
||||
Development" from the library. While the headset sleeps Lepton stops, and
|
||||
adb with it.
|
||||
- adb over the network: `adb connect <frame-ip>:5555`; over USB:
|
||||
`adb forward tcp:5555 tcp:5555` then `adb connect localhost:5555`.
|
||||
Always name the device (`adb -s <frame-ip>:5555 ...`): a Quest may be
|
||||
connected as well.
|
||||
- Install with `adb install -r --no-streaming`. It prints "Lepton post
|
||||
installation failed due to APP_PATH being unset", but installs. Start the
|
||||
game with `am start -n com.halo.decomp/.LauncherActivity`.
|
||||
- Lepton empties `/sdcard/Android/data` whenever it starts again: the game's
|
||||
data must live elsewhere (stage 2). `/sdcard/Documents` and
|
||||
`/sdcard/Download` are the SteamOS user's folders and are kept.
|
||||
|
||||
## What the port does for it
|
||||
|
||||
- SDL3's clipboard handler takes a clipboard service for granted, which
|
||||
Lepton has not: `tools/android_build.py` patches it to do without.
|
||||
- SteamVR lists an implicit OpenXR layer for Vulkan apps whose library an
|
||||
app cannot load: the APK carries a pass-through layer under its name
|
||||
(`port/android/xr_layer/fdm_injection_stub.c`).
|
||||
|
||||
## Stages
|
||||
|
||||
| # | Stage | State |
|
||||
| --- | --- | --- |
|
||||
| 1 | Base: the fixes above on the current main | done |
|
||||
| 2 | The game's data where Lepton keeps it | done: `/home/steamos/Documents/HaloCE` |
|
||||
| 3 | Menus, HUD and text visible (quad layers) | done: their alpha from their colour |
|
||||
| 4 | The freeze at the cryo bay's calibration | not seen again (2026-10-04) |
|
||||
| 5 | The Frame's controllers | |
|
||||
| 6 | Frame rate, resolution | |
|
||||
| 7 | Multiplayer, voice, cross-play with the Quest | |
|
||||
| 8 | Installing it, for others | |
|
||||
|
||||
## The game's data
|
||||
|
||||
Lepton uninstalls the app and empties its folders whenever it starts again.
|
||||
The game's data lives in the SteamOS user's Documents instead: when
|
||||
`/home/steamos/Documents/HaloCE` holds `maps/ui.map` and is writable, the
|
||||
launcher and the game take it as their data folder (config.toml, saves and
|
||||
debug.txt go there too). Once:
|
||||
|
||||
adb -s <frame-ip>:5555 shell mkdir -p /home/steamos/Documents/HaloCE/maps
|
||||
adb -s <frame-ip>:5555 push assets/maps/. /home/steamos/Documents/HaloCE/maps/
|
||||
adb -s <frame-ip>:5555 shell chmod -R 777 /home/steamos/Documents/HaloCE
|
||||
|
||||
After a Lepton restart only the APK is installed again. (Use the path under
|
||||
/home, not /sdcard/Documents: Android 11's scoped storage stands between the
|
||||
app and the latter.)
|
||||
|
||||
## Findings
|
||||
|
||||
- 2026-10-02: runs flat and in VR (GLES 3.2 by Mesa zink on Turnip, eyes
|
||||
1728x1728, 72 Hz, XR_FB_display_refresh_rate absent). Quad layers
|
||||
(menus, HUD, instructions) are not shown; the projection layer is. Froze
|
||||
at the cryo bay's look calibration; its log was lost to Lepton's wipe.
|
||||
- 2026-10-04: SteamVR draws a quad's texels by their alpha alone; the
|
||||
game's menus and HUD have none in their additive parts, so nothing showed.
|
||||
On SteamVR (by the runtime's name) the overlays' alpha now comes from their
|
||||
colour (host_xr.c; `HALO_XR_QUAD_ALPHA=colour|keep` overrides). With it,
|
||||
menus, HUD and the campaign ran well (the user: "lief 1a"); no freeze.
|
||||
- 2026-10-04: no online games found: the public MQTT brokers had banned the
|
||||
home connection for a while (too many connections from tests that day);
|
||||
broker.emqx.io came back within minutes. Lepton's network is the Frame's
|
||||
own (same IP), DNS works.
|
||||
@@ -35,6 +35,7 @@ import java.nio.channels.FileChannel;
|
||||
*/
|
||||
public class LauncherActivity extends Activity {
|
||||
private static final int PICK_IMAGE = 1;
|
||||
private static final String SHARED_DATA_ROOT = "/home/steamos/Documents/HaloCE";
|
||||
|
||||
private File dataRoot;
|
||||
private TextView status;
|
||||
@@ -46,6 +47,12 @@ public class LauncherActivity extends Activity {
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
dataRoot = getExternalFilesDir(null);
|
||||
// the Steam Frame: Lepton empties the app's own folders whenever it
|
||||
// starts again; the game's data lives in the SteamOS user's
|
||||
// Documents instead, when it is there (port/android/STEAM_FRAME.md;
|
||||
// the same choice as host_main.c's)
|
||||
if (new File(SHARED_DATA_ROOT, "maps/ui.map").isFile() && new File(SHARED_DATA_ROOT).canWrite())
|
||||
dataRoot = new File(SHARED_DATA_ROOT);
|
||||
// created by the app, so that files pushed into it with adb stay
|
||||
// readable (a directory adb creates there belongs to the shell user)
|
||||
if (dataRoot != null)
|
||||
|
||||
@@ -90,6 +90,8 @@ int host_errno(void)
|
||||
|
||||
static char data_root[512];
|
||||
static char save_root[512];
|
||||
/* where a device that empties the app's folders keeps the game's data */
|
||||
#define SHARED_DATA_ROOT "/home/steamos/Documents/HaloCE"
|
||||
|
||||
void host_android_path(int which, char *buffer, uint32_t size)
|
||||
{
|
||||
@@ -269,6 +271,12 @@ static void *game_main(void *unused)
|
||||
external = SDL_GetAndroidExternalStoragePath();
|
||||
if (!external)
|
||||
host_fatal("Android storage is unavailable: %s", SDL_GetError());
|
||||
/* the Steam Frame: Lepton empties the app's own folders whenever it
|
||||
starts again; the game's data lives in the SteamOS user's Documents
|
||||
instead, when it is there (port/android/STEAM_FRAME.md; the same choice
|
||||
as LauncherActivity's) */
|
||||
if (directory_has_maps(SHARED_DATA_ROOT) && access(SHARED_DATA_ROOT, W_OK) == 0)
|
||||
external = SHARED_DATA_ROOT;
|
||||
snprintf(data_root, sizeof(data_root), "%s", external);
|
||||
snprintf(save_root, sizeof(save_root), "%s/save", external);
|
||||
/* readable by adb (the shell user), for managing saves */
|
||||
|
||||
@@ -122,6 +122,11 @@ static struct
|
||||
GLint rect_uniform;
|
||||
GLint cut_disc_uniform;
|
||||
GLint vignette_uniform;
|
||||
GLint alpha_from_colour_uniform;
|
||||
/* the runtime draws a quad's texels by their alpha alone, ignoring the
|
||||
colour of those with none (SteamVR on the Steam Frame): the game's
|
||||
overlays, whose additive parts have none, get alpha from their colour */
|
||||
int quad_alpha_from_colour;
|
||||
GLint tangents_uniform;
|
||||
float vignette;
|
||||
/* the same copy from a layer of a texture array (the eyes drawn at
|
||||
@@ -223,6 +228,7 @@ static const char copy_fragment_shader[] =
|
||||
"precision highp float;\n"
|
||||
"uniform sampler2D source;\n"
|
||||
"uniform float cut_disc;\n"
|
||||
"uniform float alpha_from_colour; /* 1: as opaque as it is bright (a quad's, SteamVR) */\n"
|
||||
"uniform float vignette; /* 0..1: the eye images darken from their edges */\n"
|
||||
"uniform vec4 tangents; /* the eye's edges: left, right, down, up */\n"
|
||||
"in vec2 coordinate;\n"
|
||||
@@ -236,6 +242,8 @@ static const char copy_fragment_shader[] =
|
||||
"{\n"
|
||||
" vec4 texel = texture(source, coordinate);\n"
|
||||
" color = vec4(to_linear(texel.rgb), texel.a);\n"
|
||||
" if (alpha_from_colour > 0.5)\n"
|
||||
" color.a = max(color.a, max(color.r, max(color.g, color.b)));\n"
|
||||
" if (cut_disc > 0.5)\n"
|
||||
" {\n"
|
||||
" float inside = 1.0 - smoothstep(0.95, 1.0, length(disc));\n"
|
||||
@@ -326,6 +334,7 @@ static int copy_initialize(void)
|
||||
xr.rect_uniform = glGetUniformLocation(xr.program, "rect");
|
||||
xr.cut_disc_uniform = glGetUniformLocation(xr.program, "cut_disc");
|
||||
xr.vignette_uniform = glGetUniformLocation(xr.program, "vignette");
|
||||
xr.alpha_from_colour_uniform = glGetUniformLocation(xr.program, "alpha_from_colour");
|
||||
xr.tangents_uniform = glGetUniformLocation(xr.program, "tangents");
|
||||
xr.copy_array_layer = -1;
|
||||
{
|
||||
@@ -895,6 +904,12 @@ int host_xr_probe(float resolution_scale, int *eye_width, int *eye_height)
|
||||
/* the loader and the runtime keep the activity */
|
||||
activity = (*environment)->NewGlobalRef(environment, activity);
|
||||
|
||||
/* (The Steam Frame lists an implicit API layer of its own for Vulkan
|
||||
apps, XR_APILAYER_VALVE_fdm_injection, whose library an app cannot load
|
||||
from its namespace, failing xrCreateInstance; the Android loader reads
|
||||
no variable to turn it off, so the app carries a pass-through layer
|
||||
under that library's name: port/android/xr_layer/fdm_injection_stub.c) */
|
||||
|
||||
if (!XR_SUCCEEDED(xrGetInstanceProcAddr(XR_NULL_HANDLE, "xrInitializeLoaderKHR",
|
||||
(PFN_xrVoidFunction *)&initialize_loader)) || !initialize_loader)
|
||||
{
|
||||
@@ -960,6 +975,22 @@ int host_xr_probe(float resolution_scale, int *eye_width, int *eye_height)
|
||||
}
|
||||
xrGetSystemProperties(xr.instance, xr.system, &properties);
|
||||
strncpy(xr.system_name, properties.systemName, sizeof(xr.system_name) - 1);
|
||||
{
|
||||
/* (SteamVR: the quads' alpha from their colour; HALO_XR_QUAD_ALPHA
|
||||
"colour" or "keep" says otherwise, for trying) */
|
||||
XrInstanceProperties instance = { XR_TYPE_INSTANCE_PROPERTIES };
|
||||
const char *choice = getenv("HALO_XR_QUAD_ALPHA");
|
||||
|
||||
if (XR_SUCCEEDED(xrGetInstanceProperties(xr.instance, &instance)))
|
||||
{
|
||||
xr.quad_alpha_from_colour = strstr(instance.runtimeName, "SteamVR") != NULL;
|
||||
host_logf(HOST_LOG_INFO, "xr: runtime %s", instance.runtimeName);
|
||||
}
|
||||
if (choice)
|
||||
xr.quad_alpha_from_colour = !strcmp(choice, "colour");
|
||||
if (xr.quad_alpha_from_colour)
|
||||
host_logf(HOST_LOG_INFO, "xr: the overlays' alpha from their colour");
|
||||
}
|
||||
|
||||
views[0].type = views[1].type = XR_TYPE_VIEW_CONFIGURATION_VIEW;
|
||||
views[0].next = views[1].next = NULL;
|
||||
@@ -1316,6 +1347,8 @@ int host_xr_copy_layer_region(int layer, uint32_t texture, int clear_alpha, floa
|
||||
glUniform1i(xr.source_uniform, 0);
|
||||
glUniform4f(xr.rect_uniform, left, top, right, bottom);
|
||||
glUniform1f(xr.cut_disc_uniform, clear_alpha == HALO_XR_COPY_DISC ? 1.0f : 0.0f);
|
||||
glUniform1f(xr.alpha_from_colour_uniform, xr.quad_alpha_from_colour && clear_alpha == HALO_XR_COPY_ALPHA &&
|
||||
layer != HALO_XR_LAYER_EYE_LEFT && layer != HALO_XR_LAYER_EYE_RIGHT ? 1.0f : 0.0f);
|
||||
if ((layer == HALO_XR_LAYER_EYE_LEFT || layer == HALO_XR_LAYER_EYE_RIGHT) && xr.vignette > 0.001f)
|
||||
{
|
||||
const XrFovf *fov = &xr.views[layer - HALO_XR_LAYER_EYE_LEFT].fov;
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
FDM_INJECTION_STUB.C
|
||||
|
||||
An OpenXR API layer that does nothing, named as the Steam Frame's
|
||||
XR_APILAYER_VALVE_fdm_injection names its library
|
||||
(libVkLayer_VALVE_fdm_injection.so). The Frame lists that layer as implicit
|
||||
for every OpenXR app (/vendor/etc/openxr/1/api_layers/implicit.d), but an
|
||||
Android app (in Lepton) cannot load the system's library from its own
|
||||
namespace, and the Khronos loader then fails xrCreateInstance altogether;
|
||||
nor does the Android loader read the variable that would turn the layer
|
||||
off. With this one in the app's libraries the loader finds a layer of that
|
||||
name, which hands every call on to the next. (The real layer adds fragment
|
||||
density maps to Vulkan apps; this game draws with OpenGL ES.) On a Quest no
|
||||
manifest names it and it is never loaded.
|
||||
*/
|
||||
|
||||
#include <openxr/openxr.h>
|
||||
#include <openxr/openxr_loader_negotiation.h>
|
||||
#include <stddef.h>
|
||||
|
||||
static PFN_xrGetInstanceProcAddr next_get_instance_proc_addr;
|
||||
|
||||
static XRAPI_ATTR XrResult XRAPI_CALL layer_get_instance_proc_addr(XrInstance instance, const char *name,
|
||||
PFN_xrVoidFunction *function)
|
||||
{
|
||||
if (!next_get_instance_proc_addr)
|
||||
return XR_ERROR_HANDLE_INVALID;
|
||||
return next_get_instance_proc_addr(instance, name, function);
|
||||
}
|
||||
|
||||
static XRAPI_ATTR XrResult XRAPI_CALL layer_create_api_layer_instance(const XrInstanceCreateInfo *info,
|
||||
const XrApiLayerCreateInfo *layer_info, XrInstance *instance)
|
||||
{
|
||||
XrApiLayerCreateInfo next_layer_info;
|
||||
|
||||
if (!layer_info || !layer_info->nextInfo)
|
||||
return XR_ERROR_INITIALIZATION_FAILED;
|
||||
next_get_instance_proc_addr = layer_info->nextInfo->nextGetInstanceProcAddr;
|
||||
next_layer_info = *layer_info;
|
||||
next_layer_info.nextInfo = layer_info->nextInfo->next;
|
||||
return layer_info->nextInfo->nextCreateApiLayerInstance(info, &next_layer_info, instance);
|
||||
}
|
||||
|
||||
__attribute__((visibility("default")))
|
||||
XRAPI_ATTR XrResult XRAPI_CALL xrNegotiateLoaderApiLayerInterface(const XrNegotiateLoaderInfo *loader_info,
|
||||
const char *layer_name, XrNegotiateApiLayerRequest *request)
|
||||
{
|
||||
(void)layer_name;
|
||||
if (!loader_info || !request || loader_info->structType != XR_LOADER_INTERFACE_STRUCT_LOADER_INFO ||
|
||||
request->structType != XR_LOADER_INTERFACE_STRUCT_API_LAYER_REQUEST ||
|
||||
loader_info->minInterfaceVersion > XR_CURRENT_LOADER_API_LAYER_VERSION ||
|
||||
loader_info->maxInterfaceVersion < XR_CURRENT_LOADER_API_LAYER_VERSION)
|
||||
{
|
||||
return XR_ERROR_INITIALIZATION_FAILED;
|
||||
}
|
||||
request->layerInterfaceVersion = XR_CURRENT_LOADER_API_LAYER_VERSION;
|
||||
request->layerApiVersion = loader_info->maxApiVersion < XR_CURRENT_API_VERSION ? loader_info->maxApiVersion :
|
||||
XR_CURRENT_API_VERSION;
|
||||
request->getInstanceProcAddr = layer_get_instance_proc_addr;
|
||||
request->createApiLayerInstance = layer_create_api_layer_instance;
|
||||
return XR_SUCCESS;
|
||||
}
|
||||
@@ -231,7 +231,6 @@ symbols in this file:
|
||||
#include "units/units.h"
|
||||
#ifdef HALO_ANDROID
|
||||
#include "halo_vr.h"
|
||||
#include "physics/collisions.h"
|
||||
#endif
|
||||
|
||||
/* port/linux/game/pal_tags.c's */
|
||||
@@ -2206,10 +2205,6 @@ static void trigger_create_projectiles(
|
||||
long target_object_index= NONE;
|
||||
long projectile_definition_index;
|
||||
short projectile_count;
|
||||
#ifdef HALO_ANDROID
|
||||
/* VR: fired from the muzzle of the weapon in the hand */
|
||||
boolean vr_from_muzzle= FALSE;
|
||||
#endif
|
||||
|
||||
if (!TEST_FLAG(trigger_definition->flags, _weapon_trigger_projectiles_cannot_be_aimed_bit) &&
|
||||
unit &&
|
||||
@@ -2287,7 +2282,6 @@ static void trigger_create_projectiles(
|
||||
origin.x = vr_origin[0];
|
||||
origin.y = vr_origin[1];
|
||||
origin.z = vr_origin[2];
|
||||
vr_from_muzzle = TRUE;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -2389,26 +2383,6 @@ static void trigger_create_projectiles(
|
||||
real_point3d camera_position;
|
||||
|
||||
unit_get_camera_position(owner_object_index, &camera_position);
|
||||
#ifdef HALO_ANDROID
|
||||
if (vr_from_muzzle)
|
||||
{
|
||||
/* VR: from the eyes down to the hand the line runs through
|
||||
the player's own body, which the game's test hits, moving the
|
||||
shot up towards the eyes (it then flies parallel to the aim,
|
||||
above it): only the level stops it, a hand through a wall */
|
||||
struct collision_result collision;
|
||||
struct object_datum *projectile_object= object_get(projectile_object_index);
|
||||
|
||||
if (collision_test_line(FLAG(_collision_test_structure_bit) | FLAG(_collision_test_front_facing_surfaces_bit),
|
||||
&camera_position, &projectile_object->object.position, owner_object_index, &collision) &&
|
||||
collision.location.cluster_index!=NONE)
|
||||
{
|
||||
object_translate(projectile_object_index, &collision.point, &collision.location);
|
||||
object_compute_node_matrices(projectile_object_index);
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
object_force_inside_bsp(projectile_object_index, &camera_position);
|
||||
}
|
||||
if (target_object_index!=NONE)
|
||||
|
||||
+53
-2
@@ -169,6 +169,45 @@ def _find_ndk() -> Optional[Path]:
|
||||
return None
|
||||
|
||||
|
||||
# SDL3's Java takes the clipboard service for granted: Lepton, the Steam
|
||||
# Frame's Android, has none, and the activity died starting. These edits let
|
||||
# it do without one (the clipboard is then empty and keeps nothing).
|
||||
SDL_JAVA_PATCHES = [
|
||||
(" mClipMgr = (ClipboardManager) SDL.getContext().getSystemService(Context.CLIPBOARD_SERVICE);\n"
|
||||
" mClipMgr.addPrimaryClipChangedListener(this);\n",
|
||||
" mClipMgr = (ClipboardManager) SDL.getContext().getSystemService(Context.CLIPBOARD_SERVICE);\n"
|
||||
" if (mClipMgr != null) /* halo-ce-vr: none in Lepton */\n"
|
||||
" mClipMgr.addPrimaryClipChangedListener(this);\n"),
|
||||
(" public boolean clipboardHasText() {\n",
|
||||
" public boolean clipboardHasText() {\n"
|
||||
" if (mClipMgr == null) /* halo-ce-vr */\n"
|
||||
" return false;\n"),
|
||||
(" public String clipboardGetText() {\n",
|
||||
" public String clipboardGetText() {\n"
|
||||
" if (mClipMgr == null) /* halo-ce-vr */\n"
|
||||
" return null;\n"),
|
||||
(" public void clipboardSetText(String string) {\n",
|
||||
" public void clipboardSetText(String string) {\n"
|
||||
" if (mClipMgr == null) /* halo-ce-vr */\n"
|
||||
" return;\n"),
|
||||
]
|
||||
|
||||
|
||||
def patch_sdl_java() -> None:
|
||||
"""SDL_JAVA_PATCHES, once, into the cloned SDL3's Java."""
|
||||
path = SDL_DIR / "android-project" / "app" / "src" / "main" / "java" / "org" / "libsdl" / "app" / "SDLActivity.java"
|
||||
if not path.is_file():
|
||||
return
|
||||
text = path.read_text()
|
||||
if "halo-ce-vr: none in Lepton" in text:
|
||||
return
|
||||
for old, new in SDL_JAVA_PATCHES:
|
||||
if text.count(old) != 1:
|
||||
raise SystemExit(f"cannot patch {path}: SDL3's clipboard handler has changed")
|
||||
text = text.replace(old, new)
|
||||
path.write_text(text)
|
||||
|
||||
|
||||
def fetch_third_party() -> None:
|
||||
"""Download musl and SDL3 (configure time, once)."""
|
||||
THIRD_PARTY.mkdir(parents=True, exist_ok=True)
|
||||
@@ -182,6 +221,7 @@ def fetch_third_party() -> None:
|
||||
print(f"Cloning SDL3 {SDL_TAG}")
|
||||
subprocess.run(["git", "clone", "-q", "--depth", "1", "--branch", SDL_TAG, SDL_URL, str(SDL_DIR)],
|
||||
check=True)
|
||||
patch_sdl_java()
|
||||
if not OPENXR_DIR.is_dir():
|
||||
print(f"Downloading {OPENXR_URL}")
|
||||
import zipfile
|
||||
@@ -571,6 +611,17 @@ def generate_android_build(n: Writer, sln: Any) -> None:
|
||||
)
|
||||
n.build(outputs=libmain, rule="android_host_link", inputs=host_objects, implicit=[libsdl])
|
||||
|
||||
# a do-nothing OpenXR layer under the Steam Frame's implicit layer's
|
||||
# library name (port/android/xr_layer/fdm_injection_stub.c)
|
||||
fdm_stub = jni_dir / "libVkLayer_VALVE_fdm_injection.so"
|
||||
n.rule(
|
||||
name="android_xr_layer",
|
||||
command=(f"$android_host_cc -O2 -fPIC -shared -fvisibility=hidden -I{OPENXR_DIR}/include -o $out $in "
|
||||
"-Wl,-z,max-page-size=16384"),
|
||||
description="ANDROID XR LAYER $out",
|
||||
)
|
||||
n.build(outputs=fdm_stub, rule="android_xr_layer", inputs=PORT_DIR / "xr_layer" / "fdm_injection_stub.c")
|
||||
|
||||
# ---------- staging for Gradle
|
||||
|
||||
staged_sdl = jni_dir / "libSDL3.so"
|
||||
@@ -578,7 +629,7 @@ def generate_android_build(n: Writer, sln: Any) -> None:
|
||||
n.rule(name="android_copy", command="cp $in $out", description="ANDROID STAGE $out")
|
||||
n.build(outputs=staged_sdl, rule="android_copy", inputs=libsdl)
|
||||
n.build(outputs=staged_image, rule="android_copy", inputs=image)
|
||||
n.build(outputs="android", rule="phony", inputs=[libmain, staged_sdl, staged_image])
|
||||
n.build(outputs="android", rule="phony", inputs=[libmain, staged_sdl, staged_image, fdm_stub])
|
||||
|
||||
apk = PORT_DIR / "app" / "build" / "outputs" / "apk" / "debug" / "app-debug.apk"
|
||||
n.rule(
|
||||
@@ -589,6 +640,6 @@ def generate_android_build(n: Writer, sln: Any) -> None:
|
||||
description="ANDROID GRADLE $out",
|
||||
pool="console",
|
||||
)
|
||||
n.build(outputs=apk, rule="android_gradle", inputs=[libmain, staged_sdl, staged_image])
|
||||
n.build(outputs=apk, rule="android_gradle", inputs=[libmain, staged_sdl, staged_image, fdm_stub])
|
||||
n.build(outputs="android_apk", rule="phony", inputs=apk)
|
||||
n.newline()
|
||||
Reference in new issue
Block a user