mirror of
https://github.com/Cesarus85/ringfall-mjolnir-vr.git
synced 2026-10-17 17:00:35 +02:00
Compare commits
7
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
59b1fb422b | ||
|
|
5ecd3075a0 | ||
|
|
8e8713681d | ||
|
|
d7099f3e40 | ||
|
|
4ba36d7305 | ||
|
|
855f083117 | ||
|
|
6f3786de91 |
No files matched your search
@@ -39,7 +39,37 @@ What differs is found at run time, not by a build of its own.
|
||||
| 5 | The Frame's controllers | done: their own profile, the Xbox's layout |
|
||||
| 6 | Frame rate, resolution | done: 72 Hz steady, multiview on |
|
||||
| 7 | Multiplayer, voice, cross-play with the Quest | |
|
||||
| 8 | Installing it, for others | |
|
||||
| 8 | A button in the Steam library | done: `steam_frame_launch.sh` |
|
||||
|
||||
## Starting it on the Frame
|
||||
|
||||
A button in the Steam library ("Halo VR", a non-Steam game) runs
|
||||
`~/Documents/HaloCE/halo-vr.sh` (`port/android/steam_frame_launch.sh`),
|
||||
which starts the game as Steam starts an Android title of its own:
|
||||
`lepton waitforexitandrun -- ~/Documents/HaloCE/HaloVR.apk`, in a container
|
||||
of its own (`steamlaunch-4290000001`) that Lepton installs the APK into
|
||||
anew whenever the file is newer. Updating the game is replacing that APK.
|
||||
|
||||
What the script does that Steam would for an Android title:
|
||||
- a compat data path (`steamapps/compatdata/4290000001/internal`): after an
|
||||
install Lepton links the app's data to it, a dangling link without it;
|
||||
and the shader path, which Lepton reads Steam's shader dump path against;
|
||||
- a container id of its own: with the shortcut's id, SteamVR's client in the
|
||||
container stalled in xrCreateInstance;
|
||||
- a few starts: a new APK's first start only installs it, and a start that
|
||||
ends within 30 s is tried again (up to three times), unless the game was
|
||||
quit (it writes `quit_requested.txt`).
|
||||
|
||||
Steam's "Exit game" does not reach the game (its process is the container's,
|
||||
not Steam's): quit it with the VR options' "Quit Halo" (Y in the main or
|
||||
pause menu, twice). A container ended within 30 s of its start makes
|
||||
Lepton drop the app's install, so the next start installs it again.
|
||||
|
||||
The shortcut is in `userdata/<id>/config/shortcuts.vdf` (Steam reads it when
|
||||
it starts; appid 3151454348 for this name and path), its pictures in
|
||||
`userdata/<id>/config/grid/` (from `port/android/steam_frame_grid/`). From
|
||||
the Mac, over USB the Frame's SteamOS is an adb device of its own, `frame`,
|
||||
beside Lepton's `<frame-ip>:5555`.
|
||||
|
||||
## The game's data
|
||||
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 17 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 30 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 22 KiB |
Executable
+74
@@ -0,0 +1,74 @@
|
||||
#!/bin/bash
|
||||
# Halo VR on the Steam Frame: Lepton runs the APK in a container of its own,
|
||||
# and installs it anew whenever the APK is newer than the installed one
|
||||
# (port/android/STEAM_FRAME.md). The game's data: ~/Documents/HaloCE.
|
||||
# (a container of its own, not the shortcut's id: with that, SteamVR's
|
||||
# client in the container waited on xrCreateInstance)
|
||||
export SteamAppId="${HALO_LEPTON_APP_ID:-4290000001}"
|
||||
# (what Steam gives an Android title of its own, and a non-Steam game not:
|
||||
# the app's data in the container links to the compat data path's
|
||||
# internal/, a dangling link without it; and the shader paths: Lepton reads
|
||||
# the dump path against the other)
|
||||
export STEAM_COMPAT_DATA_PATH="$HOME/.local/share/Steam/steamapps/compatdata/$SteamAppId"
|
||||
export STEAM_COMPAT_SHADER_PATH="$HOME/.local/share/Steam/steamapps/shadercache/$SteamAppId"
|
||||
mkdir -p "$STEAM_COMPAT_DATA_PATH/internal" "$STEAM_COMPAT_SHADER_PATH"
|
||||
unset STEAM_FOSSILIZE_DUMP_PATH LD_PRELOAD
|
||||
LEPTON="$HOME/.local/share/Steam/steamapps/common/Lepton/lepton"
|
||||
APK="$HOME/Documents/HaloCE/HaloVR.apk"
|
||||
LOG="$HOME/Documents/HaloCE/launcher.log"
|
||||
# (written by the game when the player quits it: no start again)
|
||||
QUIT="$HOME/Documents/HaloCE/quit_requested.txt"
|
||||
rm -f "$QUIT"
|
||||
CONTEXT="steamlaunch-$SteamAppId"
|
||||
|
||||
log() { echo "$(date '+%F %T') $*" >> "$LOG"; }
|
||||
log "start (pid $$, parent $PPID)"
|
||||
|
||||
# Steam's "Exit game" ends the game's processes as Steam knows them (this
|
||||
# script, under Steam's reaper), not the container, which podman runs: a
|
||||
# signal here is passed on to Lepton, and a watcher of its own takes the
|
||||
# container down once this script is gone (killed)
|
||||
stopping=0
|
||||
child=0
|
||||
stop()
|
||||
{
|
||||
log "signal $1"
|
||||
stopping=1
|
||||
[ "$child" -gt 0 ] && kill -TERM "$child" 2>/dev/null
|
||||
}
|
||||
trap 'stop TERM' TERM
|
||||
trap 'stop INT' INT
|
||||
trap 'stop HUP' HUP
|
||||
|
||||
script=$$
|
||||
setsid bash -c '
|
||||
while kill -0 '"$script"' 2>/dev/null; do sleep 1; done
|
||||
echo "$(date "+%F %T") watcher: the game was ended, so its container" >> "'"$LOG"'"
|
||||
"'"$LEPTON"'" kill "'"$CONTEXT"'" >/dev/null 2>&1
|
||||
' </dev/null >/dev/null 2>&1 &
|
||||
|
||||
run()
|
||||
{
|
||||
"$LEPTON" waitforexitandrun -- "$APK" &
|
||||
child=$!
|
||||
# (wait returns early when a signal comes: until Lepton is gone)
|
||||
while kill -0 "$child" 2>/dev/null; do
|
||||
wait "$child"
|
||||
done
|
||||
child=0
|
||||
}
|
||||
|
||||
# (a new APK is only installed on its first start, not started; and a start
|
||||
# right after a game that was ended hard can fail in SteamVR, which then
|
||||
# needs a moment: again, up to three times, while it ends within 30 s)
|
||||
for attempt in 1 2 3; do
|
||||
started=$SECONDS
|
||||
run
|
||||
if (( stopping || SECONDS - started >= 30 )) || [ -e "$QUIT" ]; then
|
||||
break
|
||||
fi
|
||||
log "ended within $((SECONDS - started)) s: again"
|
||||
sleep 5
|
||||
done
|
||||
rm -f "$QUIT"
|
||||
log "end"
|
||||
@@ -2863,7 +2863,15 @@ static struct program_entry *prepare_draw(BOOL immediate)
|
||||
key.count_samples = device.visibility_test_active && xgpu_capabilities.atomic_counters;
|
||||
#endif
|
||||
|
||||
entry = program_get(vertex_shader_get(program, immediate, targets_multiview), fragment_shader_get(&key));
|
||||
entry = program_get(vertex_shader_get(program, immediate,
|
||||
#ifdef HALO_ANDROID
|
||||
/* the bound targets are the layers (Android's GL_OVR_multiview2);
|
||||
on the desktop there are no layers to stand in for the back buffer */
|
||||
targets_multiview
|
||||
#else
|
||||
FALSE
|
||||
#endif
|
||||
), fragment_shader_get(&key));
|
||||
if (!entry)
|
||||
{
|
||||
stats.skipped_link++;
|
||||
@@ -4023,7 +4031,9 @@ void d3d8_clear_back_buffer(float red, float green, float blue, float alpha)
|
||||
glDepthMask(GL_TRUE);
|
||||
glStencilMask(0xff);
|
||||
glClearColor(red, green, blue, alpha);
|
||||
glClearDepthf(1.0f);
|
||||
/* (glClearDepth: on Android the define above maps it to glClearDepthf,
|
||||
ES's float entry point) */
|
||||
glClearDepth(1.0f);
|
||||
glClearStencil(0);
|
||||
glClear(GL_COLOR_BUFFER_BIT | (depth_buffer ? GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT : 0));
|
||||
xgpu_gl_state_invalidate();
|
||||
|
||||
@@ -925,8 +925,10 @@ static void platform_show_pending_message(void)
|
||||
/* ---------- events */
|
||||
|
||||
/* quits as closing the window does, when the events are next read (the
|
||||
menus' Quit: port/linux/game/menu_functions.c); Android's menus have none,
|
||||
as the system closes its apps */
|
||||
menus' Quit: port/linux/game/menu_functions.c); on Android, where the
|
||||
system closes its apps, the VR options' Quit (ui_widget.c) ends the process
|
||||
at once: on the Steam Frame, Steam cannot end the game, which runs in
|
||||
Lepton's container (port/android/STEAM_FRAME.md) */
|
||||
void platform_request_quit(void)
|
||||
{
|
||||
#ifndef HALO_ANDROID
|
||||
@@ -935,6 +937,20 @@ void platform_request_quit(void)
|
||||
memset(&event, 0, sizeof(event));
|
||||
event.type = SDL_EVENT_QUIT;
|
||||
SDL_PushEvent(&event);
|
||||
#else
|
||||
{
|
||||
/* (and says so beside the game's data: the Steam Frame's launcher
|
||||
does not then take it for a start that failed and start it again) */
|
||||
char path[512];
|
||||
FILE *file;
|
||||
|
||||
snprintf(path, sizeof(path), "%s/quit_requested.txt", platform_data_root());
|
||||
file = fopen(path, "wb");
|
||||
if (file)
|
||||
fclose(file);
|
||||
}
|
||||
platform_log("quit: the player asked");
|
||||
exit(EXIT_SUCCESS);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -6570,10 +6570,11 @@ enum
|
||||
_vr_option_crosshair,
|
||||
_vr_option_motion_tracker,
|
||||
_vr_option_controls,
|
||||
_vr_option_quit,
|
||||
NUMBER_OF_VR_OPTIONS,
|
||||
|
||||
VR_OPTIONS_TOP = 102,
|
||||
VR_OPTIONS_ROW_HEIGHT = 28,
|
||||
VR_OPTIONS_TOP = 98,
|
||||
VR_OPTIONS_ROW_HEIGHT = 26,
|
||||
VR_OPTIONS_FOOTER = 362,
|
||||
|
||||
_vr_controls_on_foot = 0,
|
||||
@@ -6588,8 +6589,12 @@ static struct
|
||||
boolean controls;
|
||||
short selection;
|
||||
short page;
|
||||
/* "Quit Halo" chosen once: a second time quits */
|
||||
boolean quit_armed;
|
||||
} ui_vr_options_globals;
|
||||
|
||||
void platform_request_quit(void);
|
||||
|
||||
static real const vr_snap_angles[] = { 30.0f, 45.0f, 60.0f, 90.0f };
|
||||
static real const vr_smooth_speeds[] = { 60.0f, 90.0f, 120.0f, 150.0f, 180.0f };
|
||||
static real const vr_vignettes[] = { 0.0f, 0.35f, 0.7f, 1.0f };
|
||||
@@ -6661,6 +6666,13 @@ static void ui_vr_options_change(
|
||||
ui_vr_options_globals.controls = TRUE;
|
||||
ui_vr_options_globals.page = _vr_controls_on_foot;
|
||||
break;
|
||||
case _vr_option_quit:
|
||||
/* (the Steam Frame's Steam cannot end the game, which runs in
|
||||
Lepton's container: it ends itself; asked twice) */
|
||||
if (ui_vr_options_globals.quit_armed)
|
||||
platform_request_quit();
|
||||
ui_vr_options_globals.quit_armed = TRUE;
|
||||
break;
|
||||
}
|
||||
ui_play_audio_feedback_sound(_ui_audio_feedback_forward);
|
||||
}
|
||||
@@ -6668,6 +6680,7 @@ static void ui_vr_options_change(
|
||||
static void ui_vr_options_close(
|
||||
void)
|
||||
{
|
||||
ui_vr_options_globals.quit_armed = FALSE;
|
||||
ui_vr_options_globals.open = FALSE;
|
||||
ui_vr_options_globals.controls = FALSE;
|
||||
}
|
||||
@@ -6754,6 +6767,7 @@ static boolean ui_vr_options_take_event(
|
||||
else if (move)
|
||||
{
|
||||
ui_vr_options_globals.selection = (short)PIN(ui_vr_options_globals.selection + move, 0, NUMBER_OF_VR_OPTIONS - 1);
|
||||
ui_vr_options_globals.quit_armed = FALSE;
|
||||
ui_play_audio_feedback_sound(_ui_audio_feedback_cursor);
|
||||
}
|
||||
else if (accept)
|
||||
@@ -6813,7 +6827,11 @@ static boolean ui_vr_options_take_pointer(
|
||||
if (x >= 118 && x < 522 && y >= VR_OPTIONS_TOP - 4 &&
|
||||
y < VR_OPTIONS_TOP - 4 + NUMBER_OF_VR_OPTIONS * VR_OPTIONS_ROW_HEIGHT)
|
||||
{
|
||||
ui_vr_options_globals.selection = (short)((y - (VR_OPTIONS_TOP - 4)) / VR_OPTIONS_ROW_HEIGHT);
|
||||
short row = (short)((y - (VR_OPTIONS_TOP - 4)) / VR_OPTIONS_ROW_HEIGHT);
|
||||
|
||||
if (row != ui_vr_options_globals.selection)
|
||||
ui_vr_options_globals.quit_armed = FALSE;
|
||||
ui_vr_options_globals.selection = row;
|
||||
if (pointer->left_clicks)
|
||||
ui_vr_options_change(1);
|
||||
}
|
||||
@@ -7066,6 +7084,10 @@ static void ui_vr_options_render(
|
||||
case _vr_option_controls:
|
||||
label = "Controller buttons...";
|
||||
break;
|
||||
case _vr_option_quit:
|
||||
label = ui_vr_options_globals.quit_armed && ui_vr_options_globals.selection == _vr_option_quit ?
|
||||
"Quit Halo? Once more to quit" : "Quit Halo";
|
||||
break;
|
||||
}
|
||||
if (row == ui_vr_options_globals.selection)
|
||||
ui_join_code_draw_quad(118, (short)(y - 4), 522, (short)(y + VR_OPTIONS_ROW_HEIGHT - 6), 0x803A6EA5);
|
||||
|
||||
@@ -703,9 +703,10 @@ typedef char network_game_server_countdown_state_offset_assert[
|
||||
offsetof(struct network_game_server, countdown_state) ==
|
||||
8 + HALO_PORT_NETWORK_GAME_SIZE + MAXIMUM_NETWORK_MACHINE_COUNT * 0x10 + 0xC ? 1 : -1];
|
||||
|
||||
#ifdef HALO_ANDROID
|
||||
/* bots' machines (below: the VR port's bots) */
|
||||
/* bots' machines (the VR port's bots, port/linux/game/bots.c; on the
|
||||
desktop none is ever marked, the writes live behind the Android guard) */
|
||||
static boolean network_game_server_bot_machines[MAXIMUM_NETWORK_MACHINE_COUNT];
|
||||
#ifdef HALO_ANDROID
|
||||
static short network_game_server_bot_machine_count(struct network_game_server *server);
|
||||
#endif
|
||||
|
||||
@@ -4340,6 +4341,19 @@ static boolean network_game_server_have_all_machines_have_precached(
|
||||
return all_machines_have_precached;
|
||||
}
|
||||
|
||||
/* whether the machine at this index is one of the host's bots (the VR
|
||||
port's bots.c asks, on every platform: on the desktop no bot machine is
|
||||
ever marked, so this is always false there) */
|
||||
boolean network_game_server_is_bot_machine(
|
||||
long machine_index)
|
||||
{
|
||||
struct network_game_server *server = global_network_game_server_get();
|
||||
|
||||
return server && VALID_INDEX(machine_index, MAXIMUM_NETWORK_MACHINE_COUNT) &&
|
||||
network_game_server_bot_machines[machine_index] &&
|
||||
server->game.machines[machine_index].machine_index == machine_index;
|
||||
}
|
||||
|
||||
#ifdef HALO_ANDROID
|
||||
/* ---------- bots (the VR port's: port/linux/game/bots.c)
|
||||
|
||||
@@ -4363,16 +4377,6 @@ static void network_game_server_bot_copy_name(
|
||||
destination[index] = 0;
|
||||
}
|
||||
|
||||
boolean network_game_server_is_bot_machine(
|
||||
long machine_index)
|
||||
{
|
||||
struct network_game_server *server = global_network_game_server_get();
|
||||
|
||||
return server && VALID_INDEX(machine_index, MAXIMUM_NETWORK_MACHINE_COUNT) &&
|
||||
network_game_server_bot_machines[machine_index] &&
|
||||
server->game.machines[machine_index].machine_index == machine_index;
|
||||
}
|
||||
|
||||
static short network_game_server_bot_machine_count(
|
||||
struct network_game_server *server)
|
||||
{
|
||||
|
||||
Reference in new issue
Block a user