4925 Commits
Author SHA1 Message Date
Stefan MaierandClaude Opus 5.5 9ef32d4304 Merge upstream/main (2026-10-24): OpenCE network version 38
OpenCE gametypes named in the server browser (network version 37), and
the host may leave the shield effects to each player (SHIELD EFFECTS'
PLAYERS' OWN, network version 38). The conflict: our Android listing
declarations stay beside upstream's p2p_set_game_listing with its new
kind.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
build-316
2026-10-24 10:00:39 +02:00
MrBruh 5e27f3a27b Merge pull request #468 from OpenCommunityEdition/testing
Testing
2026-10-24 14:08:46 +13:00
MrBruh 68d8048333 Let the host leave the shield effects to each player with SHIELD EFFECTS' PLAYERS' OWN, still synced by default, in network version 38. 2026-10-24 14:00:35 +13:00
MrBruh dc7feb1c41 Name OpenCE gametypes in the server browser (SWAT, HC CTF), sending each game's kind with its LAN advertisement and internet listing in network version 37. 2026-10-24 13:44:16 +13:00
Stefan MaierandClaude Opus 5.5 24ddaecd78 Merge upstream/main (2026-10-23): OpenCE network version 36
New gametypes (SWAT, Infection, Gun Game, Hardcore), shield effects the
host's choice in network games, co-op ally friendly fire, network fixes
(scenery shields, tripmines, co-op teleports), Custom Edition HUD and
texture fixes, ASTC for Android GPUs without S3TC.

Left out on purpose: upstream's two Android workarounds for Mali phones,
the GL shadow state refreshed before every draw (prepare_draw) and the
geometry cache bypassed (mirror_range). Both would cost every draw of
both eyes on the Quest, which never showed the corruption they work
around; their tests are skipped. Conflicts: the GL capabilities log
keeps multiview and adds ASTC; xbox_textures.c keeps vr.c's texture work
counters; README.md stays the player's.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 21:58:11 +02:00
bnunu dedb3755d4 Merge pull request #462 from OpenCommunityEdition/testing
Merge Testing
2026-10-23 14:03:01 -04:00
bnunu d86e8b01ee Merge branch 'main' into testing 2026-10-23 13:34:20 -04:00
bnunu 3a917551ca Merge pull request #460 from bnunu/halo-ce-universal/coop-client-floor-plane
Co-op clients take a teleport that leaves their feet on a floor's plane
2026-10-23 09:35:48 -04:00
Jonas VolmanandClaude Opus 5.5 d17ecdd6ad Co-op clients take a teleport that leaves their feet on a floor's plane
After 343 Guilty Spark's flood cutscene (x50) with three or more players, a
client could stay on its own screen where the cutscene had parked its
player (at player0_int_base or player1_int_base) while the host had it in
the room. That client saw everyone but itself in the first Flood fight.

A client keeps its own player where it is while the host's position for it
is outside the client's loaded BSP (a BSP switch still on its way). That
was tested at the unit's feet. A teleport can leave the feet a few
millionths below the floor's plane (z -3.1e-6 on the control room's floor
at 0), which bsp3d_test_point counts as solid, so the client never took the
host's position. In a two-player game the only client is player0, whose
spot happens to test inside.

The test now uses the unit's middle: the host's position plus the client's
own feet-to-bounding-sphere offset, against its own bounding sphere center.

Checked on Windows: a hidden c10 host, two joining copies and a system link
bot, the cutscene played and skipped. Before, both clients stayed at the
parking spot; after, every machine has all four players in the room, and
each client still waited at the cutscene's two BSP switches until it had
loaded the BSP. test_elevator_correction passes (its fake object gains
bounding_sphere_center). No network version change: nothing the machines
send or agree on changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 06:33:13 -07:00
bnunu 3c7fe4594c Merge pull request #463 from bnunu/host-shield-effects
Make the shield effects the host's choice in network games
2026-10-23 09:12:00 -04:00
bnunuandClaude Opus 5.5 63fd36775e Make the shield effects the host's choice in network games
The shield glow (display.shield_glow and its Light Show intensity) and the
overshield effect (display.overshield_effect) were each player's own, in
network games too. The glow lights what is around a shield as it flares,
so a player who had it saw enemies a player without it did not, and the
overshield effect keeps an overshielded player lit: anyone who did not
turn them on was at a disadvantage.

Now a network game's are the host's, for every machine: SHIELD EFFECTS in
Server Setup > Player Options (network.shield_effects): STOCK (the
default), GLOW, LIGHT SHOW, OVERSHIELD, GLOW+OVERSHIELD or
SHOW+OVERSHIELD. Each player's own settings apply offline only (the
campaign, split screen), and Video Setup's helps say so.

- network_shield_effects.c: the host sends its choice to each client
  reliably when it is ready (distributed message 91) and to them all when
  it changes, as the aim assist policy is sent; a client takes it only
  from the host's stream, whole and valid, and draws stock until it has it.
  shield_glow.c and overshield_effect.c draw what it says in a network
  game.
- Network version 36: a build that would draw its player's own cannot
  join. network_format.txt recorded.
- Player Options' rows 27 apart in place of 30 (ce_menus.py's
  CHILD_OFFSETS), so that the tenth fits above the help, which keeps its
  three lines, and the screen's divider and buttons stay where they were;
  test_aim_assist.py's layout check follows.

Checked: ninja android builds, the changed files compile for the web, and
CI's tests pass (936). On the Android emulator, Player Options shows the
row and steps through its values with each value's help, and Extra
Effects shows its new helps. Not played as a network game.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 05:57:35 -07:00
bnunu 83ae96e2d7 Merge pull request #456 from bnunu/android-etc2-transcode
Give Android GPUs without S3TC DXT1 textures as ASTC 4x4, by moving bits (no encoder)
2026-10-23 08:08:53 -04:00
Jonas VolmanandClaude Opus 5.5 99c2030784 Give Android GPUs without S3TC DXT1 textures as ASTC 4x4, by moving bits
On Android GPUs without S3TC (Mali), DXT textures are decoded on the CPU
and uploaded as 32-bit texels, 8 times DXT1's 4 bits. A DXT1 block is two
565 colors and a 2-bit index per texel, which an ASTC 4x4 block holds
nearly as it is, so where the driver has KHR_texture_compression_astc_ldr
each DXT1 block is now rewritten as an ASTC block (dxt1_astc_block):
8 bits a texel on the GPU, with no encoder, no search and no threads.
- Four colors (c0 > c1): RGB direct endpoints of 8 bits, weights of 0,
  21, 43 and 64 sixty-fourths for DXT's thirds.
- Three colors, none transparent: weights of 0, 32 and 64 (exact).
- Three colors with transparent texels: two planes (alpha's weights of
  its own) and RGBA endpoints of 7 bits, as many as fit; a transparent
  texel takes the darker color where DXT's is black.
- The endpoints are ordered so that the decoder does not swap them and
  blend blue into the others.

DXT3 and DXT5, whose alpha does not fit beside full colors, are decoded as
before, as are volume textures, the web, and GPUs with S3TC or without
ASTC. display.dxt1_astc (Android only, true) turns it off.

On the four maps measured for the earlier encoder (928 of 1,469 DXT
textures DXT1), DXT1's GPU memory goes from 287 MiB to 72 MiB, all
textures' from 439 MiB to about 224 MiB.

Checked: a one-off check, not
kept, decoded 65,536 blocks with astcenc 5.7.0's decoder: no invalid
blocks, colors within 2 of the
game's DXT1 decode, alpha exact. ninja android builds; the CI test suite
passes. Not run on a device.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 04:43:36 -07:00
bnunu fbc71b42de Merge pull request #461 from bnunu/fix-vehicle-set-strings
Show the PC vehicle set's name and help instead of <missing string>
2026-10-23 07:16:08 -04:00
MrBruh cfb212fed9 Add SWAT, Infection, Gun Game and Hardcore as OpenCE gametypes in their own OPENCE bank of Select Gametype, with an OPENCE OPTIONS screen in the gametype editor. 2026-10-23 23:55:39 +13:00
bnunuandClaude Opus 5.5 782f4ce1bd Show the PC vehicle set's name and help instead of <missing string>
Vehicle Options' VEHICLE SET spinner indexes its strings as the sets 0
to 7, then PC (8) and CUSTOM (9) (menu_functions.c's VEHICLE_PRESET_PC
and VEHICLE_PRESET_CUSTOM), but its list had no PC: a side whose set is
CUSTOM showed "<missing string>", and one whose set is PC showed CUSTOM.

6b1d81c7 added the PC string to strings.xml by hand, and f97b3339 made
strings.xml again with tools/ce_menus.py, which wrote only what
port_settings.py says, so it went. It is now one of port_settings.py's
STRING_INSERTS, where making strings.xml again keeps it, and so is its
help: the helps are the rows' values' in turn (gametype_option_help),
so without one, PC showed CUSTOM's help, CUSTOM the first vehicle
count's, and every count the one before its own.

tools/harness/tests/test_vehicle_set_strings.py checks that the list has
the code's sets, that each value has its help, and that strings.xml has
port_settings.py's inserts. On testing, all three fail.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 03:10:44 -07:00
bnunu f1066974ca Merge pull request #453 from chestahh/opence-customedition-hudfixes
Fix Custom Edition custom HUD placements, textures and bitmap rendering
2026-10-22 18:55:32 -04:00
chestahh 384d9ba5d0 Fix Custom Edition HUD replacements, meters and gameplay prompts
Match stock CE resource sheets by pixel identity and translate stock meter
redraws to CE layouts. Preserve custom artwork, correct mixed-use meter
channels and warning placement, and draw aligned controller glyphs with
High-Res HUD enabled or disabled. Scope Bigass Open v1.1 ammo changes to
its known map name and checksum.

Based on OpenCE testing 4cb621cd. Keep upstream Android texture ownership,
eviction and validation unchanged; use its residency helper in the new
stock CE upscale lookup. No unrelated main-branch commits, networking
changes, regression test files or investigation documents are included.

Validation: Windows portable release build passed; 175 local CE HUD and
Android regression checks passed, 4 asset-dependent checks skipped.
Additional residency checks cover Android-managed pixels, desktop cache
entries, identity rejection and map reset. git diff --check passed.
Android/Web targets are unavailable in this Windows build graph; no new
runtime gameplay validation was performed for this rebase.

Player-Note: Fix Custom Edition HUDs, ammo counters and prompts while preserving custom artwork.
2026-10-22 23:47:28 +01:00
bnunu 4cb621cd26 Merge pull request #450 from OpenCommunityEdition/fix-scenery-shields-network
Scenery shields break, and tripmines go off, for clients as for the host
2026-10-22 18:25:20 -04:00
Stefan MaierandClaude Opus 5.5 4e9de9a11c Merge upstream/main (2026-10-22): co-op script fixes, cached high_res_hud, menu cursor
Network version stays 32. The conflict in D3DDevice_Present: the swap
stays in the window's branch (VR presents through vr.c); upstream's
visibility_read_batch, after the swap, is desktop GL only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
build-315
2026-10-22 19:23:25 +02:00
GuvenandClaude Opus 5.5 4ab042e144 The stale player damage test's world has static_target_damage [skip ci]
test_stale_player_damage compiles network_damage_deals from the sources,
which now asks static_target_damage whether damage is a static target's
own; its world had static_target but not that, so the test no longer
compiled (the Linux job's failure since the tripmine commit). Its world has
no static targets, so no damage is one's own.

Checked: test_stale_player_damage and test_static_target pass (33); the
whole harness on Windows fails the same 245 environment-bound tests as
testing, no others.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 14:05:58 +01:00
GuvenandClaude Opus 5.5 962c3eca45 A client's hit on its own copy of a tripmine reaches the host's
A tripmine (Altis94's Bigass) is scenery a grenade's effect makes, so each
machine makes its own copy, at an index of its own: the map's scenery is at
the same index everywhere, but such an object need not be. A client's hit
on its copy named an index at which the host had no such object, and was
refused: clients could not shoot their own tripmines to set them off. And
the host's damage to its copy named an index the client had none at, so the
client's copy did not break or go off on its screen.

A machine given another's index of a static target it has no object at
now takes its own nearest static target where the other had it
(network_damage.c's static_target_near): the host, for a client's hit, its
nearest to where the client had the target, within the target's size and
STATIC_TARGET_MATCH_TOLERANCE (a world unit), and then checks the hit as
any other; a client, for the host's damage, its nearest to the damage,
within REPORT_IMPACT_TOLERANCE and an explosion's radius. The map's
scenery, at the same index everywhere, is found by its index as before.
Part of network version 34 (no message changes); NETCODE.md says so.

Checked: the Windows build; network_format.py; test_static_target with
cases of the nearest static target where another machine had one, none
out of reach, no unit or unbreakable scenery, and the nearer of two, and
two negative controls. Not yet checked in a game: a client shooting its
own tripmine, and seeing the host's go off.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 13:52:25 +01:00
bnunu ce95f7deed Merge pull request #424 from eltanschauung/fix/coop-a10-softlock
Fix co-op Pillar of Autumn softlock glitch
2026-10-22 08:36:58 -04:00
bnunu a20ced034c Merge branch 'main' into fix/coop-a10-softlock 2026-10-22 08:36:44 -04:00
GuvenandClaude Opus 5.5 ef0e3be45b The host deals a client's player's tripmine going off under its vehicle
A tripmine a client's player throws (Altis94's Bigass) is scenery the
grenade's effect creates, owned by the thrower, so the explosion of its
shield breaking is theirs too. The host deals no damage of a connected
client's player's but what that client reports, and this explosion went
off where the host had it, its vehicle running over the tripmine, so no
client had a hit of it to report: the host's vehicle drove over a client's
tripmine unharmed, though a host's tripmine hurt clients.

What a static target deals itself (network_damage.c's
static_target_damage: the damage's owner object scenery or a device that
damage can break) the host now deals whoever owns it, and a client does
not report it, so that it is dealt once. Part of network version 34 with
the shields' sharing before it; NETCODE.md says so.

Checked: the Windows build; network_format.py; test_static_target with a
case of a tripmine's own damage, a destructible scenery's, a unit's or
projectile's, and none, and a negative control. Not yet checked in a game:
a client's tripmine under the host's vehicle, and whether the client sees
its own copy of that tripmine go off (a tripmine is made in the game, so
it need not be at the host's index on the client).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 13:35:58 +01:00
GuvenandClaude Opus 5.5 5744d2d174 Clients break and see broken the map's scenery shields, as the host does
A scenery or device whose shield is all damage can break (Rat Race's
energy shields, the tripmines of Altis94's Bigass) was no static target
of the netcode, which took only those with a body to destroy
(network_damage.c's static_target). So a client's hits on it were never
reported to the host and the host never sent its damage: clients could
not break the shields, and when the host broke one, clients still saw it
and stood on it while the host saw them floating.

static_target now takes those with a shield too. The host sends its
damage to them, as to destructible scenery, and takes clients' hits on
them. Clients, which otherwise run no object's shields but take the
host's state of them, run these objects' shield stun and recharge
themselves (damage.c's objects_update_shields), since the host sends no
state of the map's scenery: each machine deals the same damage and
recharges the same way, so the shield's regions go and come back on
every machine.

Network version 34: a host and a client must agree on which objects'
damage is shared. NETCODE.md says so and network_format.txt is recorded
for 34.

Checked: the Windows build; the new harness test test_static_target (a
body, a shield only on scenery and on a device, neither, a unit, no
object, and three negative controls, one the old rule); the whole harness
on Windows fails the same 245 environment-bound tests as testing does,
no others; network_format.py. Not yet checked in a game: a client
breaking Rat Race's shields and Bigass's tripmines, and seeing the host's
broken and recharged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 12:33:06 +01:00
bnunu fc22e8467d Merge pull request #442 from bnunu/cache-high-res-hud-setting
Read display.high_res_hud once per settings change, not at every bind
2026-10-22 06:56:16 -04:00
bnunu 9a1160573f Merge pull request #449 from OpenCommunityEdition/coop-ally-friendly-fire
Co-op hosts can make the players' AI allies immune to their fire
2026-10-22 06:49:23 -04:00
GuvenandClaude Opus 5.5 1d38b3f936 Co-op hosts can make the players' AI allies immune to their fire
Server Setup's Co-op Options has ALLY FRIENDLY FIRE
(network.coop_ally_friendly_fire), ON by default as in the campaign. OFF,
the players' weapons, grenades, melee and vehicles do their AI allies no
damage, so that no player can kill Captain Keyes on Truth and
Reconciliation, the Marines, or the other allies a level needs. The AI's
own hits of its allies, the enemies' damage, and FRIENDLY FIRE between
players are unchanged. A hit is the players' when its owner is a player
and the victim is a friendly unit that is no player's, so friendly-team
vehicles are immune to the players too, splatter included. An ally that
cannot die cannot raise an allegiance incident, so the players cannot turn
the Marines hostile either.

The rule is in game_engine_friendly_damage (game_engine.c), which only the
host's damage reaches: a client's hits are the host's to deal. The host
sends the choice with the game's settings, as bit 1 of network_game's
cooperative_flags beside PLAYER COLLISIONS', so network version 33: a
client of version 32 would play the game's settings without it. NETCODE.md
says so, and network_format.txt is recorded for 33.

The menu row is tools/port_settings.py's (COOP_SETUP_SCREENS); only the
server settings screen was written again. Its 8 rows fit above the help
area.

Checked: the Windows build; the harness tests, the new
test_coop_ally_friendly_fire (a player's hit of an ally ON and OFF, the
AI's own hit, players' friendly fire in every mode, no unit, the campaign
without co-op, and four negative controls) and test_coop_scaling_menu with
the eighth row; network_format.py; the Co-op Options screen drawn with the
row; and in play as the host on a10 and a50 (Keyes and the Marines
unharmed with OFF) and b30 (no splatter). Not yet: a client's hits online,
d20's ending, c20's Sentinels.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 10:45:03 +01:00
bnunuandClaude Opus 5.5 038252b1ae Read display.high_res_hud once per settings change, not at every bind
texture_entry_result, reached from every texture bind (even the recent
table's fast path in xgpu_texture_get), called
config_boolean("display.high_res_hud"): the settings' lock and a strcmp
over every setting's name, about 2000 times a frame. It is now read again
only when config_changes() moves, as the renderer's other settings are
(per_pixel_lighting in d3d8_gl.c). config_write, the only writer once the
settings are loaded, counts each change, so a change in the menus takes
effect at the next bind as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 19:48:16 -07:00
bnunu 61621e6513 Merge pull request #435 from bnunu/halo-ce-universal/menu-cursor
The menus show a blue arrow cursor in place of the system's
2026-10-21 19:41:11 -04:00
bnunuandClaude Opus 5.5 1d79584ea4 Hide the menus' cursor while a controller is used
The menus turn the pointer on whenever they are up, so a player on a
controller had the arrow left in the middle of the screen until the menu
closed (as with the system's cursor before). Now a controller's button,
or a stick or trigger past its rest (8000, as gamepad_idle has it), hides
the cursor while a menu is up, and the mouse moved or clicked shows it
again. A menu opened while the controller was used last opens without
it: platform_ui_pointer_set_active hides it again after the release's
SDL_ShowCursor.

The warps are not taken for the mouse being used: the one to the middle
as a menu opens (SDL reports it as motion), and the ones back to where
the mouse is held without relative mode. Hidden, the cursor cannot move
the focus, so the d-pad keeps it. Desktop builds only, as the cursor.

Checked: the new functions built with clang -Wall -Wextra -Werror
against SDL 3.4.16 and run on Windows (a controller hides the cursor in
the menus, not in play; the mouse shows it again). The whole game not
built here, nor played with a controller.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 16:40:04 -07:00
bnunuandClaude Opus 5.5 577f718a63 Put the menus' cursor's click point on the arrow's tip
The picture's arrow has a soft edge, so its tip is at (2, 2), not the
picture's corner, where the click point was. MENU_CURSOR_TIP is scaled to
the cursor's size with the picture (rounded): (1, 1) up to 1080p, (2, 2)
at 1440p and 2160p. From GuvenHuso's review.

Checked: built with clang -Wall -Wextra -Werror against SDL 3.4.16 and
run on Windows from 640x480 to 3840x2160, the click point SDL kept read
back at each size.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 16:32:10 -07:00
bnunuandClaude Opus 5.5 b68d259c81 Size the menus' cursor to the window, as Halo PC's was
Halo PC drew its cursor in the menus' 640 x 480, so it took the same part
of the screen at any resolution. The cursor was a fixed 20 x 29 (or SDL's
scaling of it on Windows), small at 1440p and 4K. It is now 32 of the
menus' 480 lines tall: 48 pixels at 720p, 72 at 1080p, 96 at 1440p, 144 at
2160p. The lines are the window's height, or for a window narrower than
4:3 its width's three quarters, and the cursor is never under 16 pixels.

menu_cursor_fit makes it from the embedded picture (halved while that is
twice the height or more, then scaled once) and is called at window
creation and at every event pump, where it does nothing unless the
window's size in pixels has changed: resizes and fullscreen toggles
follow. With a pixel density above 1 (macOS, Wayland at a scale) the
cursor is made at its size in points with the pixels' image as its
alternate; SDL_HINT_MOUSE_DPI_SCALE_CURSORS is no longer set, since the
size already comes from the window's pixels.

Checked: the new functions built with clang -Wall -Wextra -Werror
against SDL 3.4.16 and run on Windows at 640x480 to 3840x2160 (cursors
22x32 to 99x144, one made per size change and none when the size is the
same). The cursor SDL makes on Windows matches the picture's rows (the
upside-down arrow seen in an OBS recording was OBS's). The whole game
not built here.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 16:03:37 -07:00
bnunu 600edf7bb9 Merge pull request #433 from hkmodd/fix/visibility-readback-nosync
Ask the driver nothing before the swap in the visibility readback
2026-10-21 18:55:09 -04:00
bnunu 0b3b6f0749 Merge pull request #432 from chestahh/opence-android-customedition-fix
Fix Android rendering bugs on Custom Edition maps
2026-10-21 18:31:16 -04:00
bnunu 343716689d Merge branch 'testing' into opence-android-customedition-fix
# Conflicts:
#	.github/workflows/build.yml
2026-10-21 15:23:21 -07:00
Stefan MaierandClaude Opus 5.5 35554cd243 Rename the mod to Mjolnir VR
"Ringfall" is another game's name. Every name players see is now
"Mjolnir VR": the app, the Steam Frame installer and shortcut, the logo
(tools/mjolnir_art.py, was ringfall_art.py), the release's title and
assets (MjolnirVR.apk, mjolnir-vr-android-*.zip,
mjolnir-vr-steam-frame.zip, Install-Mjolnir-VR.desktop), the docs. The
updaters look for Cesarus85/mjolnir-vr, the repository's new name
(GitHub forwards the old one).

Builds 302 to 313 keep working: every release also carries its assets
under their old names, which those builds' updaters and the links
already shared ask for. Kept as they were: the app id, the signing key's
alias, the Steam Frame's ~/Documents/RingfallMjolnirVR and the file
names inside its bundle (an installed game's Steam shortcut starts the
launcher there). The Frame installer now also refreshes the library
pictures of a shortcut that is there already, and says how to rename a
shortcut still called "Ringfall: Mjolnir VR".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
build-314
2026-10-21 21:01:25 +02:00
hkmoddandClaude Opus 5.5 a0f22cbba7 Ask the driver nothing before the swap in the visibility readback
The desktop readback through the query buffer (711aa46d, aecdd895) asked
the driver for answers at every present before the swap: a GL error three
times, a fence's state once a batch, and a flush. A threaded driver
(NVIDIA's) answers only once its thread has carried out every command
queued before the question, here the whole frame just drawn, so the
render thread stopped working ahead of the driver at every frame. Build
220's readback asked nothing.

Now a batch is sent before the swap and read after it:

- visibility_send_batch, before the swap, writes VISIBILITY_UNCOPIED over
  the batch's places in the coherent mapping, sends the copies and a
  fence, and asks nothing. GL errors are asked for with debug.gl_debug
  only, as the renderer's other per-frame checks are, and the glFlush is
  gone: the swap sends the commands.
- visibility_read_batch, after the swap, reads a batch only once its
  fence has passed (glClientWaitSync with GL_SYNC_FLUSH_COMMANDS_BIT and
  no wait), so completion is still known from the fence; the batch sent
  that present is looked for from the next one. A batch the GPU is late
  with is waited for however long; none is given up on. A place that
  still holds VISIBILITY_UNCOPIED after the fence is a copy that failed,
  seen without asking for GL errors.
- A batch whose copies or fence failed is dropped, as before, and from
  then on the counts are read from each query, as with AMD's driver: a
  dropped batch is not used again, and with the buffer the readback would
  stop once all three had been.

AMD's CPU readback, the counts, the batches and the query ownership from
711aa46d are unchanged.

Checked: tools/harness/tests/test_visibility_readback.py, 5 cases and 7
negative controls. On the code before (its readback taken as the send,
before the swap), 2 cases fail: the driver asked 46 times before the swap
in 12 presents, and a count that was never copied taken for one.
A 4-machine a50 co-op on an RTX 4080 (Windows, 1440p, 4x MSAA,
debug.gl_debug), 120 seconds: 13000 batches sent and 13000 read, every
fence passed by the next present (none unavailable), no GL error, none
dropped, the buffer used throughout, 8649 nonzero counts. The harness
suite fails the same tests with and without the change (those that need
POSIX headers, on Windows). The Windows release build compiles.

Not checked: Linux, Mesa, Intel, AMD (whose path changes only in being
read after the swap), and a 32-player co-op game.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 18:23:30 +02:00
bnunu 9f04a51259 Merge pull request #429 from eltanschauung/fix/coop-gravity-lift
Fix co-op gravity lift soft lock glitch
2026-10-21 05:35:42 -04:00
bnunu 1ea29f7675 Merge pull request #419 from chestahh/opence-android-render-fix
Fix Android geometry & graphics corruption by bypassing the persistent geometry cache
2026-10-21 03:42:24 -04:00
Jonas VolmanandClaude Opus 5.5 47981601da Credit pugzysixtyfour for the menus' cursor (#435)
The blue arrow cursor this pull request adds (port/assets/cursor) is
pugzysixtyfour's art. AUTHORS.md says so beside the other art credits.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 23:49:00 -07:00
Jonas VolmanandClaude Opus 5.5 a4e4bd8317 The menus show a blue arrow cursor in place of the system's.
With a mouse, the menus' pointer (platform_ui_pointer_set_active) was the
system's cursor. The game window now has a cursor of its own:
port/assets/cursor/menu_cursor.png, an 80 x 116 blue arrow with a
transparent background, embedded by tools/embed_assets.py as the window
icon is and made into an SDL colour cursor when the window is created.

At 100% display scale the cursor is the picture at a quarter of its size
(20 x 29, by two halvings); the picture itself is the alternate image SDL
scales from at larger display scales (SDL_HINT_MOUSE_DPI_SCALE_CURSORS,
which SDL implements on Windows). The hot spot is the arrow's tip, the
picture's top-left corner.

Desktop builds only: the code sits beside the window icon's, so Android
and the web keep theirs. The cursor shows wherever SDL shows one over the
window, which is the menus' pointer, the scoreboard's and a mouse freed
with F12; in play the mouse stays captured and hidden as before.

Checked on Windows: the build, and one start to the main menu with a
hidden window. A check line (not in this commit) logged the cursor made
and active, 20 x 29 with the 80 x 116 alternate, and saved SDL's 20 x 29
image, which looks as intended. Not built here for Linux, where SDL uses
the 20 x 29 image at every display scale.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 23:40:55 -07:00
chestahh 3975480a3b Fix Android Custom Edition texture residency and draw state
Keep synchronous cached CE pixels and hardware headers at stable addresses
outside the fixed Xbox arena, bounded to 128 MiB and 8192 entries. Only
older-frame textures can be evicted; unregister bindings, GPU copies and
channel annotations before freeing, including map/bitmap teardown and flush.
Preserve the original readers, conversion, normal GPU caching and HUD
replacement behavior. Refresh Android GL shadow state before each draw.

Keep the player-tested Android streaming geometry bypass and its regression
tests, since current upstream main does not contain that fix yet. Desktop
and web preserve their cache, upload and draw-state behavior. No fullscreen,
simulation, networking or map-format changes are included.

The engine-owned bitmap getter and teardown require small source hooks,
each marked port:. The renderer admits heap pixels only through a registered
header and validates the resource extent against its allocation. Diagnostics
are limited to unload summaries and the first allocation warning per lifetime.

Device evidence: the tester reports correct rendering and stable FPS, with
zero allocation failures, fallback substitutions or evictions across four CE
visits on Mali-G615 and 87.5 MiB peak residency. Local tests cover stable
uploads, bounds, OOM, frame locks, eviction, cleanup, HUD residency, reduced
diagnostics, normal write tracking, Android-only state refresh and geometry
streaming. All 30 upstream-focused checks passed before final CI validation.
Full Android/web builds require CI because local targets are unavailable.
No fork packaging, branding, map adapters or unrelated changes are included.

Player-Note: Fix Android Custom Edition texture corruption and repeated cache upload hitches.
Player-Note: Prevent stretched or missing geometry on Android by streaming geometry directly.

(cherry picked from commit 4ac4685b96c39b20491095579ce469f8afcd2d3c)
2026-10-21 01:27:08 +01:00
Stefan MaierandClaude Opus 5.5 d4687b24e2 Merge feature/bots-ladders: OpenCE network version 32 (HDR off in VR, Ringfall's host bots kept), bots climb ladders and take partial paths
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
build-313
2026-10-20 22:05:49 +02:00
Stefan MaierandClaude Opus 5.5 9d65e7e63f Roadmap: network version 32, bots' ladders and paths
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 22:05:48 +02:00
Stefan MaierandClaude Opus 5.5 f3ce8c3bf0 Bots: paths to as near the goal as the search reaches; the goal's floor looked for deeper; ladders for goals above
On Damnation two of three of the bots' path searches failed (1078 against
527 in 80 s), and the goal's floor was not found 357 times: a search had
to reach the goal's surface itself, and the floor under the goal was
looked for 3 world units down. As OpenCE's bots do (bots.c), a search now
goes to as near the goal as it reaches (the bot searches again from
there), and the floor is looked for 6 units down. A path that ends short
of a goal above the bot takes a ladder near it (now within 8 units).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 21:44:14 +02:00
Stefan MaierandClaude Opus 5.5 bce5c2b560 Bots climb ladders (Damnation's), which the game's pathfinding does not take
A bot whose way is not found twice running, or that is stuck, with the
foot of a ladder going up within 5 world units, walks to the foot, turns
to the ladder and walks into it (as a biped climbs a climbable surface,
bipeds.c), and on past its top a moment; then it finds its way anew. Ten
seconds without getting up, it gives that up for fifteen. The ladders are
the level's climbable upright surfaces, near ones facing alike merged,
found the first time one is wanted in a game. Reported: bots stuck in a
gap of Damnation's that only a ladder leads out of.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 21:29:10 +02:00
eltanschauung 9044950d78 Remove unused co-op script test counter (#429)
The CI compiler treats -Wunused-but-set-global as an error. list_count was incremented by the fixture but never read, so remove it instead of weakening compiler warnings. All 12 targeted regressions, including their negative controls, pass. This changes test code only.
2026-10-20 14:23:46 -04:00
eltanschauung 9d7436bd64 Fix co-op gravity lift teleports for extra players
Match the reverse unit order of hs_players when selecting player0 and followers. The forward datum-order assumption returned no followers, leaving players beyond the two named by a50's lift script in the unloaded outdoor BSP.

Tested the actual a50 transition with a host and two clients, including the played and skipped cutscene placement sequences and settled floor positions. Regression coverage exercises 0-128 players, sparse/dead/recycled slots, output bounds, host authority, and every follower action, with negative controls. Windows and Linux release builds pass. This is host-side placement only; the network protocol is unchanged.

(cherry picked from commit 51abcfff9968eee5b91de2bba9d273b70be91850)
2026-10-20 13:25:38 -04:00