Short: A QuakeWorld client for MorphOS Author: Wizardry and Steamworks Uploader: "Wizardry and Steamworks" Type: game/shoot Version: 0.32.1 Architecture: ppc-morphos URL: https://grimore.org/amiga/morphos/reworking_the_fuhquake_quakeworld_game_client -=:[ ChangeLog ]:=- 20261021 - bump version to 0.32.1 - build with GCC 15 and the MorphOS SDK 3.20 libnix/ixemul ABI - rebuild zlib 1.2.3, libpng 1.2.59 and jpeg-6b from source - replace libpng 1.2.8 with 1.2.59 (memory-corruption fixes) - fix libpng error handling and IHDR validation in image.c - fix TinyGL ARB extension wrappers for the current SDK - fix MorphOS-specific input, audio and socket type mismatches - add per-frame cap on unusable network packets and a connection status message while the signon is stalled - treat duplicate netchan packets distinctly from out-of-order packets, and skip netgraph statistics when the netgraph is off - use vertex arrays for alias model rendering, with the multitexture fullbright path kept on the immediate path - precompute and fold the alias model lighting math - build surface display lists for world geometry, gated by the gl_displaylists cvar - remove the external MP3 player control (Amplifier backend) - include a startup script and place the binaries in C/ - update README and CREDITS to 0.32.1 - add a per-frame world surface cap (r_worldpolybudget) as a temporary guard for pathological maps; later removed once the adaptive-detail work made it unnecessary - add a per-surface flat fallback: when a world surface's cluster is over budget, draw it from a solid-colour display list (no lightmap, no multitexture) instead of skipping it, so the level stays solid and no holes appear - make the flat fallback persistent and one-way: once a cluster has been reduced, it stays reduced for the session, which removes the texture popping at short range - replace the per-surface flat fallback with a per-cluster box LOD: each cluster gets a cached box list coloured from its surfaces' lightmaps. The box LOD was later removed because it truncated the level too aggressively - add a load-time density detector that flags maps whose largest cluster is far above the median; the detector set aggressive defaults so the budget could actually bite - add a soft-knee thinning curve (gl_detail_knee, gl_detail_sharpness) over the cluster's visible surface count, replacing the hard knee; the curve was later replaced by the differential controller - add an alias cull (gl_alias_farclip, gl_alias_budget) that skips non-exempt alias entities beyond a distance and limits the number drawn per frame; players, projectiles and the view weapon are exempt - add a per-cluster differential controller for the world pass: the world pass is measured each frame, the cheapest world pass seen so far this map is the baseline, and the difference drives the reduction. Views within the differential threshold of the baseline are left alone, so normal maps are not clipped on load. Views far above the baseline have the clusters that contribute most to the differential marked flat, one-way, until the differential is back within threshold - add a differential controller for the alias pass with the same shape: gl_alias_farclip and gl_alias_budget are reduced when the alias pass is over its differential threshold, bounded by their floors - add gl_detail_absolute_min_ms and gl_alias_absolute_min_ms as an absolute floor: a view is only reduced if it is both worse than the baseline by the differential threshold and slow in absolute terms. This keeps normal maps that have one moderately slow view from being touched - remove the box LOD and the density detector; the differential controller makes both unnecessary - remove the per-frame world surface cap; the differential controller bounds the world pass without a hard cap - add gl_max_lists to bound the number of display lists generated per map (VRAM guard on very large maps) - set the default differential thresholds to 80 ms (world) and 60 ms (alias), and the absolute floors to 100 ms (world) and 80 ms (alias); these are the values that leave normal maps untouched while still squashing the pathological maps - update the FuhQuake banner in the console to 0.32.1 -=:[ About ]:=- FuhQuake 0.32.1 is a revival of the MorphOS port of FuhQuake released by Mark Olsen in 2017. That port is over ten years old and was built against a MorphOS SDK that no longer matches the current one. The original port works well: the engine runs, the maps load, and the client connects to servers. This fork is therefore not a rewrite but a continuation. It builds on the working 0.31 build, patches the outstanding, known and long-standing bugs that the original left in place, and performs a set of optimizations on the client. QuakeWorld itself is in preservation mode. The network protocol and the map format have not changed in decades, and the servers that are still running speak the same protocol the 2017 client spoke. The 0.32.1 client connects to the current servers and plays the current matches exactly as the original did. What has changed is the platform underneath: TinyGL, the MorphOS 3D API, has moved on, the SDK's compiler and C library have moved on, and the security posture of the bundled image libraries has moved on. The point of the project is to bring the fork up to date with those, compile it with the current SDK compiler, and produce a client that is stable, fast and reliable by removing what is no longer needed, reviewing the code, and refactoring it to current standards where that is safe to do so. The project is maintained by Wizardry and Steamworks. The source, the detailed explanations of each change, and the running notes on the revival are at: https://grimore.org/amiga/morphos/reworking_the_fuhquake_quakeworld_game_client -=:[ Rendering notes ]:=- The rendering work in this release is aimed at maps that render very slowly on TinyGL despite the rest of the map being fast. On such maps the cost is not uniform: looking at the floor is cheap, looking at a specific dense region is very expensive. The client cannot make the expensive view as cheap as the cheap view, but it can stop the expensive view from dominating the session. The approach is a per-cluster controller with a differential trigger: - World surfaces are grouped into clusters (cells of a grid over the world bounds). - The world pass time is measured each frame, and the cheapest world pass seen so far this map is the baseline. - The difference between the current world pass and the baseline is the differential cost of the current view. - If the differential is within gl_detail_diff_threshold, the view is not pathological and the map is left alone. This is why normal maps are not clipped on load: on the first frame the baseline equals the current frame, the differential is zero, and nothing happens. - If the differential is over the threshold, the clusters in the current view that contribute most to the differential are marked over-budget and a fraction of their still- unmarked surfaces is drawn from a solid-colour fallback display list instead of the textured, lightmapped one. - The marking is one-way: a cluster that has been reduced stays reduced for the session, so revisiting the area does not restore the geometry. - The alias pass has the same shape: the alias pass time is measured, and gl_alias_farclip and gl_alias_budget are reduced when the alias pass is over its differential threshold. - An absolute floor (gl_detail_absolute_min_ms, gl_alias_absolute_min_ms) prevents a view that is moderately slow but not pathological from being reduced: a view is only reduced if it is both worse than the baseline by the differential threshold and slow in absolute terms. The result on a map with a singularity is that the singularity is squashed on first sight and stays squashed, while the rest of the map is untouched. On a normal map the differential stays under the threshold and nothing is reduced. The settings that produced this behaviour are the defaults: gl_detail_diff_threshold 80, gl_alias_diff_threshold 60, gl_detail_absolute_min_ms 100, gl_alias_absolute_min_ms 80. -=:[ Requirements ]:=- - MorphOS with a current SDK runtime (the binaries are linked against the SDK 3.20 libnix/ixemul ABI). - TinyGL at the latest version available for MorphOS. The client uses the current TinyGL rendering primitives, and an older TinyGL will not render correctly. - Some memory. The client has a fair amount of BSS usage. - The Quake data files (an ID1 folder from the official Quake 1 release). -=:[ Installation ]:=- The archive is a source tree with a ready-built client. You do not need to compile anything to play. 1. Unpack the archive. 2. Copy the folder to wherever you want to keep the client. The folder already contains the fuhquake-gl and fuhquake-sw executables, the startup script, and the configuration the client expects. 3. Copy the ID1 folder from your official Quake 1 files into the same folder, next to fuhquake-gl and fuhquake-sw. 4. Run the startup script from the C folder. After step 3 the source tree is no longer needed and can be deleted. The C folder is self-contained. -=:[ Usage ]:=- The startup script FuhQuakeGL sets a sensible default resolution and starts the GL client. Resolution and depth are command line options; edit the script to change them. The important ones: -width GL: 640 SW: 320 display width -height GL: 480 SW: 240 display height -depth GL: 24 (GL only) display depth; Voodoo 3 users should use 16 -window window mode instead of fullscreen -gamma GL: 1 (GL only) display gamma; 0.5 is a good value -mem PPC: 4 set to 64 on Apple MacMini PPC The full set of console and command line options is the upstream FuhQuake set; the upstream documentation applies to the engine, not to the MorphOS-specific build. For minimal eye candy, open the menu, choose options, FPS settings, high quality. For more, highres textures are available from http://facelift.quakedev.com/. There is a list of servers included in servers.txt so you can directly go into the multiplayer menu and select a server. We only included some very active servers where you will more than likely find someone at all times. Cvars specific to this build that implement TinyGL display lists and the adaptive rendering needed to display some maps: -gl_displaylists 1 use display lists for world geometry (default on; set to 0 to compare) -gl_adaptive_detail 1 master switch for the world adaptive detail controller -gl_detail_cellsize 256 grid cell size in world units -gl_detail_diff_threshold 80 world differential above which the controller acts -gl_detail_absolute_min_ms 100 world pass floor below which the controller does not act -gl_max_lists 4096 maximum display lists per map -gl_alias_farclip 1024 alias cull distance -gl_alias_budget 128 maximum non-exempt alias entities per frame -gl_alias_diff_threshold 60 alias differential above which the controller acts -gl_alias_absolute_min_ms 80 alias pass floor below which the controller does not act The defaults are tuned so that normal maps are not modified and pathological maps are squashed on first sight. If a map is still clipped, raise gl_detail_absolute_min_ms; if a pathological map is not squashed enough, lower gl_detail_diff_threshold. -=:[ Further Documentation ]:=- The project page contains the reasoning behind each change, the compiler and SDK details, and the notes on the rendering work: https://grimore.org/amiga/morphos/reworking_the_fuhquake_quakeworld_game_client The original FuhQuake engine documentation is at http://www.fuhquake.net/doc-help.html and applies to the engine itself. -=:[ Copyright and Credits ]:=- Id Software: Quake, released under the GPL. A Nourai: FuhQuake. Anton Gavrilov: ZQuake, the base for FuhQuake. Mark Olsen: the original MorphOS port (0.31, build 675, 2017). Wizardry and Steamworks : revival, code review, optimization and refactoring. FuhQuake is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. See COPYING for details.