Half-Life Updated (custom SDK) Created 3 years ago2021-01-13 21:05:33 UTC by Solokiller Solokiller

Created 3 years ago2021-01-13 21:05:33 UTC by Solokiller Solokiller

Posted 1 year ago2022-07-17 12:44:43 UTC Post #346728

Progress on Half-Life Unified SDK

Make sure to read the wiki for more information.

It's been a while since the last update. I've been busy with real life stuff so i haven't had much time to work on stuff, but there has been progress.

First off, i'd like to make it absolutely clear that this SDK is not ready for use yet. You shouldn't be trying to use it to make mods at this point. It also doesn't change any engine limits.

malortie has done a lot of good work updating and improving models. Related models are now consistent with each-other and are more flexible to use. He's also been improving the studiomdl compiler with important changes.

A lot of small fixes and improvements have been made to the game's code:
  • Lots of fixes and changes for work-in-progress stuff, to be expected
  • Minor code cleanup
  • delta.lst and liblist.gam are now installed to the mod directory. This means you'll have to edit these files and commit them in the source code repository. The files are closely related to the game's code, that's why they're here.
  • renamed hl library to server
  • Updated third party dependencies
  • Added .natvis config file to allow the Visual Studio debugger to show the contents of certain string_t variables like the entity's classname, netname, targetname, target. In the future i plan to change string_t to a struct to allow this to work for all instances of string_t, but i need to test that this works on all platforms
  • Enabled modernize-use-bool-literals Clang-tidy check
  • Improved game config loader to detect the use of different search paths to bypass single include detection
  • Reworked the client command registry to avoid the use of global variables. Commands that exist for the duration of the server's existence have their lifetime managed by the registry, others are managed by an object with automatic lifetime
  • Client command names are required to be lowercase and contain only alphabetic characters and _
  • Added constants for checking if a server is running in listen or dedicated mode to allow config files to check for it
  • Removed unused trigger_changelevel code (left over from Quake 1)
  • Added special targetname fired when players are activated (happens when they've fully spawned after connecting or map change)
  • OpenGL is no longer needed for linking, which simplifies Linux setup a bit
  • Changed minimum required version of CMake to 2.23
  • Github CI builds now provide artifacts containing the contents of the CMake install command to ensure that everything needed is included
  • Linux builds no longer enable DWARF debug info for all build configurations, which dramatically reduces the size of CI artifacts
  • spdlog log output now adds the short library prefix to console log output to make it clear where it's coming from (useful for code used in both client and server)
  • Added CMake option to enable developer mode (-dev command line parameter). This enables cheats, the console, sets developer to 1 and most importantly allows more console log output to be seen during startup. Most of this parameter's behavior was already provided with other options, but the early logging behavior is exclusive to this parameter
  • Reworked game title drawing to allow the use of all three games' titles. I've combined the sprites into a single HUD sprite, and the Opposing Force version uses both the Half-Life and Opposing Force sections at the same time. This greatly simplifies the logic for drawing this title, but it's still temporary since the UI needs a rewrite at some point. It will work fine for custom maps as well, but only those three titles are supported. The gametitle worldspawn keyvalue has been changed from a boolean to a string to allow selection of this title. In the future i'd like to make this flexible enough to allow for custom titles, but that's not happening any time soon.
  • Merged sentences.txt for all three games together. There are too many sentences to have them all co-exist, so i've commented out some of the scripted dialogue. I've also increased the server-only sentence group limit from 200 to 1536 to match the engine's sentence limit. Both limits will be going away with a new feature, see below.

Major changes

New music system

I've implemented a new music playback system using OpenAL. This provides the same functionality as the engine's version (minus the ability to open the CD tray) and does not stop playing on map change. It also supports Ogg files.

trigger_cdaudio and target_cdaudio have been removed, replaced by ambient_music. This entity allows you to play music for a single player or all players, allows you to loop, fade out and stop music and play music for players in a certain radius.

The existing soundtracks for each game are now copied to the mod installation and are renamed to allow them to co-exist. Existing maps are upgraded to use the new entity.

This does not replace the engine's music system. That's still there and is used to play gamestartup.mp3. Servers can also still issue commands to the original system so multiple songs could be playing at the same time. There's nothing i can do about that.

On Windows OpenAL is installed to the cl_dlls directory and located by way of delay loading. On Linux the existing OpenAL library is used, which is the version provided by the Steam runtime. Miles Sound System (used by the engine to play music) uses OpenAL so that dependency is already locked in. OpenAL is backwards compatible with a stable API so this shouldn't be a problem.

This video shows the music system in action:

Project configuration changes

Windows and Linux require the use of a toolchain file now. This is needed to enforce the use of the right compiler on Linux and to enable the use of vcpkg.

Vcpkg is a package manager used to automate the process of setting up third party dependencies. It downloads, builds and installs dependencies to enable them to be used in the project. This process occurs when you configure or generate the build files with CMake, so this slows things down a bit there. On the other hand it is no longer necessary to rebuild dependencies as part of the project which speeds things up compared to before.

Not all dependencies are acquired through vcpkg. One of them isn't available at all, another is outdated. I may contribute to those projects to get things moving on that end.

Continuous integration builds cache vcpkg artifacts, which means it doesn't have to rebuild all of them like it did before. This speeds up CI builds which is great.

Project info overlay

To help with development, bug reporting and debugging i've added an in-game overlay to show information about the project's build state:
User posted image
For Pre-Alpha and Alpha builds this is enabled by default to make it obvious that this is a development build.

More information here.

Removal of support for low resolutions

Historically Half-Life has had special support for resolutions below 640x480 in the form of smaller HUD sprites and some UI elements being smaller.

I've removed this support because it doubles the amount of sprites needed for the HUD, the results appear near identical and modern systems don't even allow a resolution that low.

Only the use of command line parameters allows the game to be forced into such a low resolution, which isn't very useful and not worth providing special support for.

The game will still work and display the UI at those resolutions but it might not perfectly fit the screen.

I'm hopeful that the UI can be rewritten altogether, which makes the use of sprites and hud.txt unnecessary but that's too much work for V1.0.0.

Changes in HalfLife.UnifiedSdk-CSharp

The packager, asset synchronizer, installer have been ported from dotnet script to become full fledged executables. They are part of this repository now.

Many improvements have been made to these tools. Ripent .ent files are no longer used to update maps, instead all changes are done exclusively through the map upgrade tool.

The packager now handles the including of additional mod directories (_addon, _hd, etc) through the configuration file to avoid hardcoding anything and allowing complete control.

Package names have the format PackageName-yyyy-MM-dd-HH-mm-ss.zip. When needed pre-alpha builds are uploaded to the main repository's releases page. As noted on the main wiki page this project uses standard terms for releases. Pre-alpha builds are not ready to be used for mod development and regular play.

A new tool has been added to upgrade maps separately from the installer, allowing the conversion of custom maps. You'll have to specify which game the map is from, it will then convert the map according to the rules for that game.

Many upgrades have been added to automate the conversion of maps that use models that malortie has updated.

The documentation for all of these tools is available on the wiki (see the sidebar): https://github.com/SamVanheer/halflife-unified-sdk/wiki

Continued in next post ==>
Posted 1 year ago2022-07-17 12:44:48 UTC Post #346729
<== Continued from previous post

Assets repository

The assets repository is up and running: https://github.com/SamVanheer/halflife-unified-sdk-assets

The AssetSynchronizer tool is used to automate the copying of files from this repository to the mod installation. This happens locally, so you'll need everything set up on your computer. To prevent accidental removal of the entire mod installation this tool does not delete files, though it does overwrite them without warning. Always make backups of everything you have.

Remaining work to be done

This is roughly the work that needs to be done now to finish V1.0.0:
  • Merge pull requests into repository (in progress)
  • Update project info overlay to use Pre-Alpha terms for dev builds (done)
  • Fix game title not showing up at the start of campaigns (requires temporary workaround since the UI code will be rewritten) (done)
  • Implement new sentence playback system (OpenAL + convert sentences.txt to JSON), add all sentences
  • Update changelog to include all changes
  • Review all changes
  • First alpha build
  • Stress test the three campaigns and fix issues that show up
  • First beta build
This is going to take some time, the sentences system will probably take up most of it. I've already got the experience needed from upgrading Quake to use OpenAL so it's mostly building a system for sentences specifically, but i can't say how much that'll take.

I will be converting sentences.txt to JSON to simplify this a bit (the file needs a different name anyway to stop the engine from trying to load it). A tool will be needed to convert the file automatically as well. Fortunately this is a straightforward task. Note that the actual sentence contents won't be converted to a complicated JSON data structure, it'll remain as a single string of text.

For future releases this is the work needed at minimum to provide full support for playing each campaign as it was originally designed:
  • Networking system to transfer data as needed (contents of sentences.json and replacement maps). I have a prototype system that sets up the connection and that syncs it with the engine's connection (player leaves -> connection terminates, etc) but it needs more work to do this stuff and there are edge cases that need covering
  • Global sound replacement (for footsteps, so it has to be client side as well)
  • Global sentence replacement (for dialogue that references the player character, mostly pre-disaster, Barney and Scientist dialogue)
  • New UI (HUD and VGUI1 code needs replacing). I need to investigate what the best option here is. VGUI2 might be useful but technically violates the Source SDK license, an HTML-based approach could be very useful if it's practical to do but i need to experiment. Moving away from HUD sprites is important since that has a bunch of limitations (sprites count as models, overall limit of 256 HUD sprites, limited palette).
  • Opposing Force HUD sprites need a version that removes the scanline effect. IIRC this affects only the weapon and night vision icons, but i haven't made a complete list. I don't know how much work is needed for this to be done, perhaps the effect is easy to reverse, perhaps not
Ideally i'd finish all of this before doing a V1.0.0 release, but the risk of feature creep setting in and V1.0.0 never releasing is too high. I also need to work on HLAM to finish a bunch of stuff there so i want to focus on that after V1.0.0 is out.

I will be releasing new betas for the three Updated projects soon once i've fixed a couple more things on that end.

See also the Half-Life Asset Manager thread for a status update about that.

I'd like to thank malortie for his continued work on the game's assets and studiomdl. Your work has been invaluable.

Thanks to everybody else that has helped out with development and testing. I really appreciate it.

Until next time.
Posted 1 year ago2022-08-06 14:01:17 UTC Post #346761

Half-Life Updated Updated betas released

Half-Life Updated, Half-Life: Opposing Force Updated and Half-Life: Blue Shift Updated betas have been released:
Half-Life Updated: https://github.com/SamVanheer/halflife-updated/releases/tag/HLU-V1.0.0-beta12
Half-Life: Opposing Force Updated: https://github.com/SamVanheer/halflife-op4-updated/releases/tag/HLOP4U-V1.0.0-beta009
Half-Life: Blue Shift Updated: https://github.com/SamVanheer/halflife-bs-updated/releases/tag/HLBSU-V1.0.0-beta009

Notable changes for all games:
  • Reset server's client FOV value so loading save games restores FOV correctly with weapon prediction disabled
  • Fixed players not being given exhaustible weapons when they get the ammo for them (e.g. giving Hand Grenade ammo now gives weapon_handgrenade)
  • Fixed weapon deploy animations and body values not working correctly in several cases
  • Cleaned up weapons code a bit (Note: some code has been moved which could break mods, make sure to double check these changes in your mod if you merge them in)
  • Updated smdlexp to use 3DS Max 2023 SDK (Note: this is a direct upgrade, there are versions of this plugin that have had more bug fixes applied. See here for an up-to-date plugin(for 3DS Max 2022))
  • Added sensible error messages to prevent this plugin from being compiled incorrectly
  • Added keyvalue allow_item_dropping to control whether NPCs can drop items. Note that level editors may not apply this keyvalue correctly to existing maps so you will need to double check NPC entities
  • env_sound effects are now restored correctly and reset to 0 when changing maps. They do not persist when going back and forth between level changes due to engine limitations
  • Added fixes from Marphy Black's Half-Life Fact Fixes
  • Added fgd files for each game to their respective code repositories
Notable changes for Opposing Force:
  • Fixed Gonome calculating gut throw direction incorrectly
  • Fixed info_ctfspawn_pickup not being called info_ctfspawn_powerup
  • Fixed human grunts being able to drop weapons in the intro map

Progress on Half-Life Unified SDK

  • smdlexp (StudioModel exporter) 3DS Max plugin is now a separate CMake project to avoid the need for special handling since it requires a third party SDK to be installed. This plugin has to be compiled as 64 bit unlike all other tools which were designed for 32 bit compilation only
  • libstdc++ is now linked statically on Linux to prevent problems when users do not have a C++20-capable version installed (Thanks a1batross)

EASTL library

I've added the EASTL library as a dependency. This is a static library maintained by Electronic Arts containing various helper classes and functions for use in games. The intended use of this library is to allow for safer and more efficient string and container usage.

For example a common task is to format a filename before opening the file. The SDK typically does this using a stack buffer which means the filename has a limited size. Modern C++ code does this using either std::string or std::filesystem::path, but both of those use dynamic memory allocation.

EASTL provides a special class called fixed_string that can be used to optimize this out. fixed_string uses a fixed size buffer to store the string, optionally allocating memory if the string is too large. Most filenames handled by this game have to abide by path size limitations from long ago: 260 characters. So using a fixed_string with that as its internal buffer size can eliminate a lot of dynamic memory allocations.

Another example is fixed_vector. It works pretty much just like fixed_string in that it has an internal buffer. For cases where most arrays have a known maximum with a few outliers this can cut down on a lot of memory allocations, like for example the words in a sentence parsed from sentences.txt.

The engine has a hard limit of 32 words, so in virtually all cases a fixed_vector with that size won't allocate additional memory, but new sentences can have more words and will allocate.

The long term goal is to use EASTL containers everywhere when it improves performance. String operations that currently use stack buffers will use fixed_string instead to eliminate string truncation problems (aka strings too large to fit in the stack buffer). You can see some compiler warnings about this here.

Uses of STL unordered containers will be replaced as needed since those are known to have worse performance than existing open source versions.

Progress on new sentences system

I've been working on the new sentences system to get that done. Sentence playback is fully functional, but time compression doesn't chop up the sound sample at the exact same boundaries as the original code and reverb and delay effects aren't implemented yet.

Implementing this system required me to implement virtually everything needed for regular sound playback as well. With the exception of looping sounds that's all working now as well, but it isn't being used because it relies on the map-specific precache calls to know which sound index maps to a filename.

Since it isn't needed for V1.0.0 i'm not planning to add support for that, but once the networking system has been implemented using this sound system for everything should be straightforward to implement.

I've also decided to leave the conversion to JSON for later. The engine ignores sentences if it reaches the limit so having an easy way to compare them both by switching back to the original code is useful for now.

Remaining work to be done

  • Merge pull requests into repository (in progress, all pull requests in Updated repos are merged)
  • Implement new sentence playback system, add all sentences (90% complete)
  • Review malortie's work and provide feedback
  • Update changelog to include all changes
  • Review all changes
  • First alpha build
  • Stress test the three campaigns and fix issues that show up
  • First beta build
Many thanks to a1batross, FreeSlave, Ronin4862 and malortie for their continued contributions to these projects. I really appreciate it.
Posted 1 year ago2022-09-17 16:35:01 UTC Post #346885

Progress on Half-Life Unified SDK

Make sure to read the wiki for more information.

There's been quite a bit of work done in the last month and a half.

SDK code changes

  • All open pull requests have been merged in
  • The new OpenAL-based sentences system has been implemented
  • Music now stops playing when disconnecting from servers, when starting a new map using the map command or when loading save games
  • Reworked the model replacement code to be more generic to allow its use for the other replacement systems. The work done also makes implementing per-entity versions of all replacement systems very easy
  • Added global sound replacement with client-side player movement sound replacement support (to replace footstep sounds)
  • Added global sentence replacement. This can replace individual sentences as well as sentence groups (to replace entire sets of responses, sets of randomly selected sentences, etc. Depends on how entities use them)
  • Updated all new code to use a consistent naming style
  • Updated new files to use better names to reflect their content more closely
  • Updated JSON library to 3.11.2
  • Updated JSON schema validator to latest commit
  • Added fmtlib as a direct dependency. fmt::format is now the way to format strings safely, std::format will not be used since it isn't available everywhere yet and will likely suffer from the same problems as other standardized APIs (can't be updated due to backward compatibility)
  • Updated EASTL to use a local port that uses the latest commit to allow it to compile when using GCC
  • Added helper commands for debugging maps, like infinite air, armor, finding entities based on classname or targetname, triggering them
  • Reworked UTIL_dtos1-4 to a single function UTIL_ToString that converts an integer value to a string without allocating memory
  • Reworked Visual Studio natvis configuration to use intrinsic functions to allow string_t values to be displayed immediately, instead of requiring manual evaluation. This allows for faster debugging
  • Added entity info hud to display the classname of the entity you're looking at as well as its health. The color of the values is determined by the entity's relationship with the player:
    • white: no relationship
    • blue: friendly
    • red: hostile
  • Talk monsters now only treat other talk monsters as potential friends to avoid monsters trying to talk to random nearby entities and crashing as a result
  • Removed obsolete Quake code used to handle the cloak powerup (doesn't exist here)
  • Renamed VModEnable client command to vmodenable adhere to new client command naming rules
  • Defined dummy vmodenable client command to silence console errors in singleplayer
  • Removed superfluous Random value from monster_otis bodystate keyvalue (was equivalent to Holstered)
  • Use proper constants for all uses of GetBodygroup and SetBodygroup
  • Reworked animation body groups for NPCs that use new submodel structure. This is to make weapon submodels more consistent across the board
  • Added new game icon to the game installation to display in the program title bar, in the task bar and in the Steam library

C# tool code changes

  • Reworked the Utilities library to reduce the number of C# objects created by merging the entity and map APIs with the provider-specific types. For any given map this reduces the number of objects created by 1/3rd
  • Added events to allow listening to entity creation, removal, keyvalue changes and keyvalue removal
  • Added diagnostics system to enable verbose logging of all changes made to a map
  • Added command line option --diagnostics-level to the Installer and MapUpgrader tools to enable diagnostics output to show what is changed in a map

Asset changes

  • Merged sentences.txt for all three games into a single one containing all unique sentences
  • Added global sound and sentence replacement files for Opposing Force and Blue Shift
  • Added source files for new game icons
  • Added more assets to PackageManifest.json to ensure game installations contain everything needed to run the game

New sentences system

The new sentences system has been fully implemented:
Although there are still a few kinks to work out it's pretty much all there.

You can also hear footstep sound replacement in Opposing Force and sentence replacement when interacting with scientists and Barneys in Blue Shift.

The new system changes some limits:
  • Maximum number of sentences: was 1536, now 65535, can be further raised by changing the network message used to send them to the client but this will increase bandwidth usage
  • Maximum number of sentence groups: was 200, now unlimited
  • Maximum number of words in a sentences: was 32, now unlimited
  • Maximum sentence name length: was 15 ASCII characters, now unlimited but warns if it exceeds 31
  • Maximum number of unique sounds: was 1024, now unlimited
  • Maximum sentence line length: was 511 characters, now unlimited
  • Maximum number of concurrent sounds of all kinds (static, dynamic, ambient): was 128, now unlimited
Sentences are now paused when the game is paused and will not be cut off.

There are console commands to play sounds to test them out. You can optionally specify volume, attenuation and pitch although attenuation isn't very useful since the sounds always play on your local player entity.

There is a cvar to toggle between the old and new sound systems, although this only works for the first 1536 sentences listed in the file.

I've also implemented sound effects using OpenAL's EFX API which is the successor to EAX. It uses almost identical parameters but since the original implementation used software mixing and fixed position 3D sound sources the results aren't identical.

Half-Life, like Quake, uses software mixing regardless of which audio API is used to actually play it. This means distance calculations are done before the audio is sent to the EAX 3D sound sources.

To avoid duplicating distance effects Half-Life has 4 3D sources placed around the player. If you imagine a box 2x2 units wide and long centered on the player, the sources are placed at the corners of the box:
Image not to scaleImage not to scale
The new sentences system lets OpenAL handle distance calculations so sound sources are handled properly by the EFX extension, so the sound effects are working properly, but sound different from the original effects.

The effects do sound correct in-game but it's always going to be different.

Regular sound playback is also fully supported, the only thing preventing its full use is that the sound precache list can't yet be sent to the client to map sound indices back to filenames.

Improved natvis debug configuration

When viewing an entity in the Visual Studio debugger you can now see certain string values directly:
User posted image
In the future i'd like to turn string_t into a strong type (as opposed to a typedef which is treated as its underlying type) to allow this to work for all of them, but i need to make sure this doesn't cause problems before i can do that.

Entity info

This feature was added for debugging purposes, as well as to show how to implement this kind of feature.
User posted image
More information can be found here: https://github.com/SamVanheer/halflife-unified-sdk/wiki/Hud-Entity-Info

New game icon

The new icon is the default Half-Life icon, modified to include the base colors from all three games (shown enlarged here):
User posted image
In the Steam library:
User posted image

Diagnostics output in Installer

Here's an example of the diagnostics output from the Installer:
User posted image
Because there is an upgrade that alters the angles of every entity there is an additional diagnostics level that filters those changes out. Otherwise the amount of log output would dramatically increase.

Continued in next post ==>
Posted 1 year ago2022-09-17 16:35:37 UTC Post #346886
<== Continued from previous post

New UI: preliminary research

I've been looking into RmlUi a bit more. There has been work done recently to refactor its render backend which would make it easier to integrate in a Half-Life mod.

I see two possible implementations: The second option is probably more efficient but means dropping Software mode support. To prevent users from reporting this as a bug i'd have to add a forced quit command when the engine is started in Software mode (IsHardware() returns 0).

The best of both worlds is to use OpenGL when the engine is loaded in OpenGL mode and the VGUI1 workaround in all other modes (D3D mode returns 2, though it obviously won't ever do that now since it was removed).

This work is planned for RmlUi 5.0 (currently at 4.4) with no ETA so i don't expect to be doing this any time soon.

In the meantime i can make a standalone test program to see how feasible it is to make a HUD with it. That's a quick way to determine if the library is an option here at all.

I'm not planning to add a new UI in V1.0.0 so it's not a big deal if it takes time. I've given them some feedback on CMake modernization they want to do to help them along.

I've also looked into how VGUI1 and VGUI2 handle TGA loading. Both use the same backend that boils down to glGenTextures and glTexImage2D calls, it doesn't pass through the engine's texture loading routines which handle loading, filtering, resampling and rescaling.

VGUI1 supports very large textures (134,217,727 RGBA32 pixels max) but doesn't free the main RAM buffer. VGUI2 limits textures to 256 x 256 pixels but doesn't keep a buffer allocated.

All backends support 24 and 32 bit uncompressed TGA with no custom origin coordinates (only game.tga and game_big.tga use a non-default origin, and game_big.tga can probably be changed since it's used by Steam and not the game), so sticking to that format will work regardless of which option we use.

This means we'll need a tool to take the sprites and convert them to TGA. For animated sprites we'll need a framework-specific solution: RmUi has sprite sheets (single large texture divided into rectangles for each frame) but VGUI1 and 2 don't have a built-in feature so that'll require splitting frames into separate textures either at the file level (filename0.tga, filename1.tga, etc) or at file load time.

Either way a separate file is needed to specify animation frames. It is possible to just use sprites here if we handle loading ourselves but i'd like to get away from the limitations of the sprite format (256 colors for all frames combined, variable framerate sprite frames which aren't supported by the engine anyway, etc).

Some other container format could work, even Source vtf might work here but i'd prefer to keep things simple and RmlUi has its way of specifying sprites already.

Only once a decision has been made on where to take the UI can i figure out how to deal with the Opposing Force image replacement problem. This depends on the UI framework since the manner in which images are referenced differs between them.

Regardless, the actual directory structure will likely be:
  • mod directory
    • gfx
      • ui
        • op4
The default UI will go in the ui directory, with Opposing Force-specific UI images going in a subdirectory, matching the structure used for other file replacements already in use.

The choice of framework will also influence how other UI-related issues will be handled, so there isn't much to gain from discussing that now.

Remaining work to be done

  • Merge pull requests into repository (done)
  • Implement new sentence playback system, add all sentences (done)
  • Review malortie's work and provide feedback (done)
  • Global sound replacement (done)
  • Global sentence replacement (done)
  • Rework the client command registry to eliminate use of shared pointers
  • Rework game configuration system to no longer rely on std::any to harden the API against incorrect use
  • Update Packager tool to provide list of files not included in the package to spot missing files more easily
  • Update changelog to include all changes
  • Write documentation for all new features (partially complete)
  • Investigate making the wiki accessible for pull requests to allow direct modification or make the wiki part of the repository Vcpkg does this, albeit for their website)
  • Review all changes
  • First alpha build
  • Stress test the three campaigns and fix issues that show up
  • First beta build
Work expected to be needed in the future (in no particular order):
  • Networking system (transfer contents of replacement files, precache lists, possibly other immutable data): work in progress
  • New UI (including HUD image replacement for Opposing Force): being researched
  • Versions of Opposing Force-only HUD images that have the scanline effect removed: some preliminary research done, needs more investigation
  • A way to start all three campaigns from the main menu. Probably the same solution as used in Condition Zero Deleted Scenes (menu button that starts a map that opens an SDK-level menu that enables campaign and training selection). Depends on the new UI
  • Unknown unknowns
There has also been more work done on the Updated projects with bug fixes and improvements which i'll list when i release the next set of beta builds. I need to investigate a couple more things before i can do that.

Many thanks to a1batross, hammermaps, Shepard, BryanHaley, Ronin4862 and malortie for helping to get this project much closer to its first release. I couldn't have done this without you.
Posted 1 year ago2022-10-09 18:10:33 UTC Post #346955

Half-Life Updated betas released

Half-Life Updated, Half-Life: Opposing Force Updated and Half-Life: Blue Shift Updated betas have been released:
Half-Life Updated: https://github.com/SamVanheer/halflife-updated/releases/tag/HLU-V1.0.0-beta13
Half-Life: Opposing Force Updated: https://github.com/SamVanheer/halflife-op4-updated/releases/tag/HLOP4U-V1.0.0-beta010
Half-Life: Blue Shift Updated: https://github.com/SamVanheer/halflife-bs-updated/releases/tag/HLBSU-V1.0.0-beta010

Notable changes

  • Fixed Room DSP effects not always activating
  • Try to unstuck player after level transition if stuck in the world (fixes getting stuck in vent at start of Gene Worm's Lair when playing with high fps)
  • Fixed MP5 not creating bullet decals half the time
  • Fixed crash while parsing command menu with unsupported custom button
  • Fixed tripmines blowing up after loading save game in c1a3d
  • Fix game crashing when triggering certain entities on an empty dedicated server with maxplayers 1
    • This change made it so you should access the world using the CWorld::Instance global instead of the SDK's various ways
  • Fixed Shock Trooper health not matching original game
  • Fixed MP5 shooting animations sometimes playing a deploy animation
  • Fixed Shock Trooper weapon body being set incorrectly, removed some unused constants
  • Fixed Engineer not shutting off his blow torch properly
  • Fixed NPC fired 556 and 762 bullets not creating bullet decals half the time

Project changes

  • Cleaned up the Linux makefiles to remove obsolete logic and simplify the compilation process
  • Fixed the Linux makefiles not working when using Clang++ due to missing -mno-sse compiler flag
  • Added particle man sources to the object list in Linux client Makefile
  • Added game icons for program icon and Steam library icon

Important change listed separately

Use post build event to copy dlls instead of using output directory.
Visual Studio's Output Directory now uses $(SolutionDir)$(Configuration)\$(ProjectName)\ as generally expected.
You will need to edit the file filecopy.bat to change where mod dlls are placed. The tutorial on the TWHL wiki has been updated with this information.
Note: you will need to clean solution and rebuild to remove remaining references to the old output directory.

Git repository changes

  • Added Github Actions CI configuration file based on Half-Life Unified SDK's version. Continuous Integration provides pre-built binaries for Windows and Linux in case you want to test out the latest builds. Available for up to 90 days after the build has successfully completed.

Github wiki changes

  • Explicitly specified the scope of the projects and added instructions on where to go for help with making mods
  • Added links to TWHL website, Discord server, SDK setup tutorial and Half-Life Updated status thread
Thanks to Shepard, Ronin4862, a1batross, FreeSlave and λλλλλλ for help with these changes.
Special thanks to malortie for greatly improving and standardizing the models used by all games.
Posted 1 year ago2022-10-09 18:10:55 UTC Post #346956

Progress on Unified SDK

SDK code changes

  • Conditional evaluation code has been separated from the game configuration system to allow its use in other areas without coupling it with that feature
  • Conditional evaluation exposes constants using property accessors instead of using global variables, this eliminates the need to manually update them
  • Consolidated most of the game configuration system code GameConfig.h and GameConfig.cpp, redesigned it to use a type-safe API to prevent exceptions from being thrown at runtime due to failed casting from std::any
  • Disabled AVX instruction set in libnyquist to ensure that users using a CPU that doesn't have that instruction set can play the mod without crashing
  • Disabled modernize-use-nullptr clang-tidy check due to a bug in clang-tidy causing code analysis to fail
  • Merged the server and player movement material systems, this reduces the memory usage by 1/3rd in total (1 version on the client, 1 on the server, 1 in entity code which has been merged)
  • Reworked nearly all file access to use IFileSystem, with the exception of code that needs to access files directly (IFileSystem isn't thread safe and doesn't provide an API to cleanly load a file using absolute paths)
  • Fixed memory leaks in old file loading code (as a consequence of the IFileSystem refactor work)
  • Merged func_tank_of and related entity classes into func_tank and its related entity classes. This effectively halves the amount of code used because they were nearly identical
  • Use local vcpkg port for json schema validator and libnyquist. This removes both libraries from the Visual Studio solution and exempts them from full rebuilds, allowing for faster rebuilding (~30 seconds on my machine). Continuous integration builds cache these libraries which allows for faster building there as well
  • Updated json schema validator to latest commit
  • Fixed some vcpkg warnings and configuration issues
  • Removed obsolete trigger_secret check in trigger code (there is no such entity making this dead code)
  • Identify players by using IsPlayer() everywhere instead of relying on classname or classification values (allows using classes that derive from CBasePlayer to track different data, use separate classes for different gamerules, etc)
  • Made string_t strongly typed to prevent accidental conversions between integers and strings, enforce use of correct constants
  • Improved Visual Studio natvis support for string_t
  • Changed client-side ALERT messages to print as debug output to match server (print when developer > 0)
  • Improved sentences.txt warning logging to ignore sentences that are clearly not part of groups to reduce the amount of log spam

C# code changes

  • player_loadsaved messages are now also updated to use the game-specific message name
  • Reworked Packager to list excluded and ignored files
  • Fixed alien aircraft sounds restarting when loading saved game in ba_xen3
  • Fixed Installer creating empty files when running with dry run enabled (game crashes when loading empty BSP files so this was a pretty big problem)

Asset changes

  • Added game icon source files
  • Added Op4CTF stats format string files
  • Updated fgd to merge func_tank_of into func_tank
  • Added Opposing Force VGUI images
  • Added missing left and right arrow VGUI images (created from Op4 images)
  • Added Op4CTF class description text files

Improved Visual Studio natvis support for string_t

Since string_t is now a struct Visual Studio recognizes it as a distinct type so it can have proper natvis support added for it. You can now view the contents of any string_t variable directly in the debugger without any additional work:
User posted image
If a string is null (i.e. default constructed or assigned to using a default constructed string_t) then it will read Null string_t.
Otherwise it will show the contents of the string as "contents here".

This can be incredibly useful when you're debugging code and you need to know what an entity's classname or targetname is.

Packager output changes

The Packager tool can now listed excluded and ignored files:
User posted image
Excluded files were excluded explicitly using the file exclusion pattern list, ignored files weren't included by the file inclusion pattern list and weren't excluded either. This makes it easy to tell if a file is being incorrectly skipped.

Remaining work to be done

  • Rework the client command registry to eliminate use of shared pointers (done)
  • Rework game configuration system to no longer rely on std::any to harden the API against incorrect use (done)
  • Update Packager tool to provide list of files not included in the package to spot missing files more easily (done)
  • Update changelog to include all changes
  • Write documentation for all new features (partially complete)
  • Investigate making the wiki accessible for pull requests to allow direct modification or make the wiki part of the repository Vcpkg does this, albeit for their website)
  • Review all changes
  • First alpha build
  • Stress test the three campaigns and fix issues that show up
  • First beta build
Work expected to be needed in the future (in no particular order):
  • Networking system (transfer contents of replacement files, precache lists, possibly other immutable data): work in progress
  • New UI (including HUD image replacement for Opposing Force): being researched
  • Versions of Opposing Force-only HUD images that have the scanline effect removed: some preliminary research done, needs more investigation
  • A way to start all three campaigns from the main menu. Probably the same solution as used in Condition Zero Deleted Scenes (menu button that starts a map that opens an SDK-level menu that enables campaign and training selection). Depends on the new UI
Posted 1 year ago2022-10-09 18:11:21 UTC Post #346957

Research done on implementing scripting system

Last year i outlined how a scripting system could be implemented: https://twhl.info/thread/view/20055?page=2#post-346080

I wanted to do some preliminary work on this to see if any of the scripting code used by the conditional evaluation system needed changing, but it grew quite a bit bigger.

I've spent some time researching everything i outlined to determine how feasible it would be, and i built a prototype to test some things.
Note: This is a prototype, which means it's not even a pre-alpha design. It's just an experiment, it has not been added to the project and won't be for quite a while.
List of topics:
  • Plugins and map scripts: what differences are there between them, what are they, how do they work
  • Are shared script entities allowed
  • How are multiple scripts dealt with
  • How does the scheduler work
  • How does trigger_script work
  • How are hooks handled
  • Are custom entities supported, and how do they work
  • Analysis

Plugins and map scripts: what differences are there between them, what are they, how do they work

What differences are there between them

None. Well, not none, but they behave functionally the same. Plugins are loaded from a configuration file that looks like this:
[
{
    "Name": "TestPlugin",
    "Scripts": [
        "maps/test.as",
        "maps/another.as",
        "plugins/plugin.as"
    ]
}
]
Plugins are loaded on server startup and stay loaded. There is a command to reload them but it's for debugging purposes, i haven't implemented proper lifetime management yet.

Map scripts are loaded from the map configuration file:
{
    "Includes": [
        "cfg/HalfLifeConfig.json"
    ],
    "Sections": [
        {
            "Name": "Scripts",
            "Scripts": [
                "maps/test.as",
                "maps/duplicate.as"
            ]
        }
    ]
}
Map scripts are discarded on map change.

When printed in the console the type is printed, but this only affects names and not behavior.

Beyond that they are identical in every way. Plugins can do everything a map script can and vice versa. Map scripts are just plugins with special treatment. They even use the same configuration file parsing code, so i will be referring to both as plugins.

What are they

Plugins are a set of Angelscript modules. Each module represents a script file listed in the configuration file. A script may include other files, including files that are themselves listed in the file but this will result in the same code being included in two separate modules.

Non-shared script entities may use the same names in multiple modules.

How do they work

When a plugin is loaded all of its scripts are loaded into modules. Each module is then initialized by calling ScriptInit.

When a module is about to be discarded ScriptShutdown is called. This can happen if an error occurred during initialization for any module in a plugin, if the plugin is being reloaded or if the game is shutting down.

These are the only functions called directly by the game and are also the only ones that cannot be marked shared.

All other script code is executed in response to game events which can come from various sources.

Are shared script entities allowed

Yes. This mechanism is used to share the custom entity base classes (see below for more information).

How are multiple scripts dealt with

The scripts listed in the config file are all loaded into their own module so if a script just needs to be loaded but is not interacted with directly by other script code this makes it easy to use. This solves the problem of scripts clashing with each-other.

For example a script could define a custom entity and register it in its ScriptInit function. ScriptInit cannot be shared so multiple custom entities can be added to a plugin just by including the scripts in the config file.

Note that you should still avoid doing that when possible since it makes using the script code directly much more difficult.

How does the scheduler work

I've redesigned the scheduler to be safer and easier to work with:
void ScriptInit()
{
    State state;
    state.Data = "Hello World!";
    Scheduler.Schedule(ScheduledCallback(@state.Callback), 1.5, 3);

    //Will trigger an Angelscript exception (stops script execution and logs to the console)
    //Scheduler.Schedule(null, 1.5, 3);
}

class State
{
    string Data;

    int Count = 1;

    void Callback()
    {
        log::info("Data is: " + Data + ", " + Count);
        ++Count;
    }
}
The scheduler no longer uses function names and no longer captures parameters. It takes a single funcdef void ScheduledCallback() that functions must match to be used. If a function isn't compatible your script won't compile.

Stateful callbacks are possible using delegates as shown above.

Scheduler::Schedule takes 3 parameters: the callback, interval between executions and total number of executions (default 1 for single execution, can be Scheduler::REPEAT_INFINITE_TIMES for unlimited executions).

It returns a handle type that can be used to clear the callback.

The scheduler is now so simple that you could almost implement it yourself in a script. The only thing that's missing is a way to call a function every frame (which the scheduler provides).

How does trigger_script work

It searches for the first occurrence of the specified function with the right signature: void functionName(CBaseEntity@ activator, CBaseEntity@ caller, USE_TYPE useType, float value)

The first module to provide it wins. And other instances are logged as warnings.
This supports namespaces so it should be easy enough to avoid conflicts.

No think function support has been added since you can do that with the scheduler.

How are hooks handled

Hooks are now events and are handled using a uniform syntax:
void ScriptInit()
{
    Events::Subscribe(@Callback);
    Events::Subscribe(@MapInit);
}

void MapInit(MapInitEventArgs@ args)
{

}

void Callback(SayTextEventArgs@ foo)
{
    log::info("Callback executed");
    log::info(foo.AllText);
    log::info(foo.Command);
}
All events have a reference type of name <EventName>EventArgs and a funcdef void <EventName>Handler(<EventName>EventArgs@ args).

Subscribing is done by calling Events::Subscribe with your callback, unsubscribing works the same way with Events::Unsubscribe. If you're using an object method you'll need to cast it using the funcdef.

Events are completely type-safe and have compile-time error checking so you don't have to worry about getting the function signature wrong or having a typo in the name.

There is no PLUGIN_HANDLED functionality. Event handlers are always invoked in the order that they are subscribed, regardless of whether the handler is in a plugin or map script. The order that plugins are listed in the config file has no effect on behavior.

Events may provide a way to suppress behavior in C++ code, that's provided on a case-by-case basis and does not affect event handler execution.

Defining events in C++:
/**
 *    @brief Base class for script event argument types.
 */
class EventArgs : public as::RefCountedClass
{
public:
    EventArgs() = default;

    EventArgs(const EventArgs&) = delete;
    EventArgs& operator=(const EventArgs&) = delete;
};

class MapInitEventArgs : public EventArgs
{
public:
    MapInitEventArgs() = default;
};

class SayTextEventArgs : public EventArgs
{
public:
    SayTextEventArgs(std::string&& allText, std::string&& command)
        : AllText(std::move(allText)), Command(std::move(command))
    {
    }

    const std::string AllText;
    const std::string Command;

    bool Suppress = false;
};
Registering events in C++:
static void RegisterMapInitEventArgs(asIScriptEngine& engine, EventSystem& eventSystem)
{
    const char* const name = "MapInitEventArgs";

    eventSystem.RegisterEvent<MapInitEventArgs>(name);
}

static void RegisterSayTextEventArgs(asIScriptEngine& engine, EventSystem& eventSystem)
{
    const char* const name = "SayTextEventArgs";

    eventSystem.RegisterEvent<SayTextEventArgs>(name);

    engine.RegisterObjectProperty(name, "const string AllText", asOFFSET(SayTextEventArgs, AllText));
    engine.RegisterObjectProperty(name, "const string Command", asOFFSET(SayTextEventArgs, Command));
    engine.RegisterObjectProperty(name, "bool Suppress", asOFFSET(SayTextEventArgs, Suppress));
}

void RegisterEventTypes(asIScriptEngine& engine, EventSystem& eventSystem)
{
    const std::string previousNamespace = engine.GetDefaultNamespace();
    engine.SetDefaultNamespace("");

    RegisterMapInitEventArgs(engine, eventSystem);
    RegisterSayTextEventArgs(engine, eventSystem);

    engine.SetDefaultNamespace(previousNamespace.c_str());
}
Publishing an event is also simple:
// Inform plugins, see if any of them want to handle it.
{
    const auto event = scripting::g_Scripting.GetEventSystem()->Publish<scripting::SayTextEventArgs>(p, pcmd);

    if (event->Suppress)
    {
        return;
    }
}
It's all very simple.
Posted 1 year ago2022-10-09 18:11:37 UTC Post #346958

Are custom entities supported, and how do they work

Yes, but only in multiplayer. Unfortunately the save game system doesn't fully support custom entities so workarounds would be needed. It is possible to implement but rather complex, so i'm not sure if it's worth the effort.

They work a lot like they do in Sven Co-op:
void ScriptInit()
{
    CustomEntities::RegisterType("foo", "Bar::Test");
}

namespace Bar
{
class Test : ScriptBaseEntity
{
    bool KeyValue(const string& in key, const string& in value)
    {
        log::info("Keyvalue is " + key + " " + value);
        return true;
    }
}
}
ScriptBaseEntity is the base class for entities that inherit from CBaseEntity. It has a self property representing its C++ object and a BaseClass property representing the set of virtual functions you can override, specifically allowing you to call the base class implementations.

It's included in scripts by adding this code to the start of each root script file:
external shared class ScriptBaseClass;
external shared class ScriptBaseEntity;
The class implementations are in a special Angelscript module. This approach reduces memory usage and speeds up compile times for all modules by not having these class implementations included in each module.

ScriptBaseClass is something new. All script classes have this as their base class and it looks like this:
shared abstract class ScriptBaseClass : ScriptBaseClassInterface
{
    private CustomEntityCallbackHandler@ m_CallbackHandler;

    void SetCallbackHandler(CustomEntityCallbackHandler@ value) final
    {
        if(m_CallbackHandler is null)
             @m_CallbackHandler = cast<CustomEntityCallbackHandler>(@value);
    }
}
It provides a place to put anything that's common to all of these classes to avoid duplication.

ScriptBaseClassInterface exists to mark the class as an implementation of the interface defined on the C++ side, to enforce correctness.

I've only implemented the framework to glue all of this together and test it, you can't make functional custom entities yet but that's the easy part.

With some effort this can be extended to support custom NPCs and weapons as well.

Analysis

As i said above this is only a prototype. Nothing is set in stone. The work i've done has already uncovered edge cases and problems that need solving.

For those interested all of the scripting code (excluding conditional evaluation code) is 3126 lines of code right now.

The current design would work well if it were multiplayer-only, like Sven Co-op is. Not having to worry about saving state makes things quite a bit easier.

The problem with having to save state is that there is a lot of it. You have map scripts, plugins, the global variables in both, any callbacks registered in several places and script objects that integrate with C++ code like custom entities.

Such a complex set of integrations makes saving all of that state difficult to do. On top of that the save system wasn't designed to allow saving of discrete sets of data like Source is. While it can be implemented manually it depends on how the engine saves its state. While that's probably never going to change it does mean custom engines could break that code.

If a custom engine doesn't support save games made by Steam Half-Life then such a workaround might won't work either.

Additionally save games have a maximum size of 512KB to write the contents of entities and all engine headers except the main save header and entity headers (Source has 3096KB max and is configurable in SDK code). Compiled script code could get pretty big once you add in plugins and entire custom entities, and globals could contain any data including dictionaries that point to themselves, large strings, lists of strings, etc.

This makes saving state a lot harder.

Source VScript

Main article: VScript

I've looked into how Source's scripting system works to see what kind of design decisions they made. Theirs is much simpler and is abstracted to allow multiple scripting languages (though any given game and tool sticks to the same language for everything).

It doesn't support fully custom entities but does allow running functions using specific entity inputs. Each entity can have a think function and it's possible to directly add outputs that call functions.

This makes sense because Source I/O system closely matches the concept of function calls, just like Goldsource's trigger system is a rougher emulation of the same thing and Quake 1's even rougher version does this within the limitations of QuakeC.

It also leverages the networking prop metadata to allow looking up and setting any entity variable listed in the server side network send table which cuts down on a lot of API registration code. It has a function EntFireByHandle that lets you fire inputs which effectively replaces function registration code.

Although this doesn't let you call other functions like IsAlive() that's still pretty powerful.

A design like this is far simpler and more restricted; it has no plugin support, and nothing complex like creating text based menus and getting callbacks for selected options.

It does make saving state much easier since it only requires serializing the compiled scripts, global variables (of which there are going to be fewer than in Angelscript plugins) and metadata tying entities to their scripts.
Posted 1 year ago2022-10-09 18:14:04 UTC Post #346959

Conclusion

I think for now scripting should not support saving and loading in the design. Excluding plugins from saving and only saving map scripts makes things easier but can easily cause bugs if server plugins are not blocked from doing things that would cause inconsistencies when saving and loading.

I think custom entities should not be saved at all since that's where the complexity lies. Simpler scripting interaction like trigger_script is doable but still requires the compiled code to be serialized.

It may be better to first overhaul the SDK to use a design more like Source: save data is more generalized (done before in HLEnhanced) allowing its use in querying and setting values, maybe redesigning the triggering system to use an I/O system under the hood to allow name-based input triggering and hooking into outputs.

For the purposes of interacting with entities through simple scripting that's more than enough. Custom entities would require more work to function since they have to be able to call functions but they can also use property lookup and input firing.

The current rule of "only ScriptInit and ScriptShutdown are explicitly called" is too strict since the alternative is using events for per-frame calls. Entity-specific calls like Precache in VScript can't be handled as well using events since events are a global occurrence; if any entity wants to use events to precache something for itself it'll be called for every callback that registers itself for that event.

That'd result in N entities calling Precache for M callbacks, which is a lot of useless callbacks slowing things down.

There is also the issue of functions with the same name conflicting which is still a problem when including other scripts. A possible solution could be to parse the script and renaming functions that are called globally to use unique identifiers, and chaining them to occur in a specific order (Source does this): includes first in the order they are included (also the order they occur in the resulting script) and then the script that included it.

All that said, scripting support is something that's pretty low priority right now. It requires a bunch of code cleanup to be done first to avoid having breaking changes in APIs later on and is pretty much a major version change all by itself.

The prototype's code isn't available yet since it's a work in progress, i'll push it to a separate branch once a bunch of stuff has been sorted out in both the prototype and the master branch, and once V1.0.0 is out and major remaining work has been sorted. This is mostly to avoid people trying to use an early prototype to implement scripting support in their mods without understanding that it's nowhere near ready for that. It would blow up in your face if you tried that right now.

That about wraps things up for now, the todo list above shows there's still quite a bit of work left for V1.0.0. Hopefully i can get through all of that quickly so this gets finished sooner rather than later.

I will leave you with this log output from the Angelscript test scripts to see just how much diagnostics this thing pumps out:
[sv] [angelscript] [trace] Logger initialized
[sv] [angelscript] [trace] Trying to create precompiled module
[sv] [angelscript] [debug] Creating precompiled module "Precompiled:ScriptBaseClasses"
[sv] [angelscript] [debug] Successfully built precompiled module "Precompiled:ScriptBaseClasses"
[sv] [angelscript] [trace] Creating plugin "TestPlugin" with 3 scripts
[sv] [angelscript] [trace] Creating plugin script module with script "maps/test.as"
[sv] [angelscript] [trace] Trying to create module
[sv] [angelscript] [debug] Creating module "Plugin:TestPlugin:maps/test.as"
[sv] [angelscript] [debug] Adding generated code
[sv] [angelscript] [debug] Adding script "c:\program files (x86)\steam\steamapps\common\half-life\hlu\scripts\maps\test.as" (root)
[sv] [angelscript] [trace] Encountered #include "maps/other.as" in "c:/program files (x86)/steam/steamapps/common/half-life/hlu/scripts/maps/test.as"
[sv] [angelscript] [debug] Adding script "c:\program files (x86)\steam\steamapps\common\half-life\hlu_downloads\scripts\maps\other.as" (depth 1)
[sv] [angelscript] [trace] Encountered #include "maps/another.as" in "c:/program files (x86)/steam/steamapps/common/half-life/hlu_downloads/scripts/maps/other.as"
[sv] [angelscript] [debug] Adding script "c:\program files (x86)\steam\steamapps\common\half-life\hlu\scripts\maps\another.as" (depth 2)
[sv] [angelscript] [debug] Successfully built module "Plugin:TestPlugin:maps/test.as"
[sv] [angelscript] [trace] Creating plugin script module with script "maps/another.as"
[sv] [angelscript] [trace] Trying to create module
[sv] [angelscript] [debug] Creating module "Plugin:TestPlugin:maps/another.as"
[sv] [angelscript] [debug] Adding generated code
[sv] [angelscript] [debug] Adding script "c:\program files (x86)\steam\steamapps\common\half-life\hlu\scripts\maps\another.as" (root)
[sv] [angelscript] [debug] Successfully built module "Plugin:TestPlugin:maps/another.as"
[sv] [angelscript] [trace] Creating plugin script module with script "plugins/plugin.as"
[sv] [angelscript] [trace] Trying to create module
[sv] [angelscript] [debug] Creating module "Plugin:TestPlugin:plugins/plugin.as"
[sv] [angelscript] [debug] Adding generated code
[sv] [angelscript] [debug] Adding script "c:\program files (x86)\steam\steamapps\common\half-life\hlu\scripts\plugins\plugin.as" (root)
[sv] [angelscript] [debug] Successfully built module "Plugin:TestPlugin:plugins/plugin.as"
[sv] [angelscript] [trace] Executing function "void ScriptInit()" (Plugin:TestPlugin:maps/test.as)
[sv] [angelscript] [info] [Plugin:TestPlugin:maps/test.as] Hello World!
[sv] [angelscript] [trace] Module "Plugin:TestPlugin:maps/test.as": subscribing callback "void Callback(SayTextEventArgs@)" to event "SayTextEventArgs"
[sv] [angelscript] [trace] Registering custom entity classname "foo" with type "Bar::Test"
[sv] [angelscript] [trace] Checking class "ScriptBaseEntity" for a C++ wrapper
[sv] [angelscript] [debug] Registered custom entity classname "foo" with type "Bar::Test"
[sv] [angelscript] [trace] Registering custom entity classname "foo" with type "Bar::Test2"
[sv] [angelscript] [trace] Checking class "ScriptBaseEntity" for a C++ wrapper
[sv] [angelscript] [error] Can't register custom class: the classname "foo" is already in use with type "Bar::Test" in module "Plugin:TestPlugin:maps/test.as"
[sv] [angelscript] [error] Stack trace (1 out of 1 levels):
[sv] [angelscript] [error] 0: void ScriptInit() at c:/program files (x86)/steam/steamapps/common/half-life/hlu/scripts/maps/test.as:10,2
[sv] [angelscript] [trace] Module "Plugin:TestPlugin:maps/test.as": subscribing callback "void MapInit(MapInitEventArgs@)" to event "MapInitEventArgs"
[sv] [angelscript] [trace] Scheduler::Schedule: Scheduled callback "void State::Callback()" from module "Plugin:TestPlugin:maps/test.as" at 0 seconds to start at 1.5 seconds with interval 1.5 and repeat count 3
[sv] [angelscript] [trace] Executing function "void ScriptInit()" (Plugin:TestPlugin:plugins/plugin.as)
[sv] [angelscript] [info] [Plugin:TestPlugin:plugins/plugin.as] Plugin initializing
[sv] [angelscript] [debug] Created plugin "TestPlugin" with 3 scripts
[sv] [angelscript] [debug] Removing all map plugins
[sv] [angelscript] [trace] Creating plugin "MapPlugin" with 2 scripts
[sv] [angelscript] [trace] Creating plugin script module with script "maps/test.as"
[sv] [angelscript] [trace] Trying to create module
[sv] [angelscript] [debug] Creating module "Map:MapPlugin:maps/test.as"
[sv] [angelscript] [debug] Adding generated code
[sv] [angelscript] [debug] Adding script "c:\program files (x86)\steam\steamapps\common\half-life\hlu\scripts\maps\test.as" (root)
[sv] [angelscript] [trace] Encountered #include "maps/other.as" in "c:/program files (x86)/steam/steamapps/common/half-life/hlu/scripts/maps/test.as"
[sv] [angelscript] [debug] Adding script "c:\program files (x86)\steam\steamapps\common\half-life\hlu_downloads\scripts\maps\other.as" (depth 1)
[sv] [angelscript] [trace] Encountered #include "maps/another.as" in "c:/program files (x86)/steam/steamapps/common/half-life/hlu_downloads/scripts/maps/other.as"
[sv] [angelscript] [debug] Adding script "c:\program files (x86)\steam\steamapps\common\half-life\hlu\scripts\maps\another.as" (depth 2)
[sv] [angelscript] [debug] Successfully built module "Map:MapPlugin:maps/test.as"
[sv] [angelscript] [debug] Created plugin "MapPlugin" with 2 scripts
[sv] [angelscript] [trace] Publishing event "MapInitEventArgs"
[sv] [angelscript] [trace] Executing function "void MapInit(MapInitEventArgs@)" (Plugin:TestPlugin:maps/test.as)
[sv] [angelscript] [trace] Executing function "void MapInit(MapInitEventArgs@)" (Map:MapPlugin:maps/test.as)
[sv] [angelscript] [trace] trigger_script(trigger_me): Found function "void Bar::TriggerMe(CBaseEntity@ activator, CBaseEntity@ caller, USE_TYPE useType, float value)" in module "Map:MapPlugin:maps/test.as"
[sv] [angelscript] [warning] trigger_script(trigger_me): Encountered multiple functions with signature "void Bar::TriggerMe(CBaseEntity@ activator, CBaseEntity@ caller, USE_TYPE useType, float value)"
[sv] [angelscript] [warning] trigger_script(trigger_me): Next function encountered in module "Map:MapPlugin:maps/duplicate.as"
Posted 1 year ago2022-10-22 17:44:05 UTC Post #346990

Progress on Unified SDK

I made some big changes this past two weeks so i've decided to put this in its own update post.

I've done a run through all three campaigns and all issues related to new features have been fixed. Another run is needed to double check everything but it should be smoother since everything has been shaken down.

SDK code changes

  • Added fmtlib formatter specializations for Vector, Vector2D, MinuteSecondTime
    • This makes it possible to print vectors by passing the vector itself instead of the individual components. Supports floating point format specifications like {:.0f} to trim fractional parts
  • Reworked GameLibrary interface to use virtual functions for simpler design (backported from multiple WIP feature branches)
  • Forcefully reset monster ideal activity when released from Barnacle (previously depended on task-specific behavior)
  • Re-added activity changes to talk monster schedules using ACT_IDLE instead of ACT_SIGNAL3 (not used by talk monsters, but is used by allied human grunts which do have that activity)
  • Fixed repel entities not setting correct use function (Male assassin repel entities tried to spawn Human grunts)
  • Fixed satchel charge not using model replacement (caused a fatal error)
  • Print list of loggers in alphabetical order
  • Added error logging for spdlog errors (these are internal errors that developers should fix)
  • Changed all uses of pfnGetPlayerWONId (obsolete, always returns UINT32_MAX) to pfnGetPlayerAuthId (Steam Id)
  • Added PlayerLogInfo struct to select a fmtlib formatter that prints player information in the form "nickname<userid><steamid><teamname>"
  • Added log_setalllevels console command to set the log level for all loggers at once
  • Added log_setentlevels console command to set the log level for all loggers that replaced ALERT macro uses (See documentation for a list)
  • Added UTIL_ConsolePrint as safe version of CLIENT_PRINTF (uses fmtlib to format the message eliminating the need to use UTIL_VarArgs)
  • Enabled the use of ASSERT and ASSERTSZ in the client dll
  • Use ASSERTSZ in OFFSET functions instead of a preprocessor condition
  • Converted all uses of the ALERT macro to use spdlog loggers
    • Assert logging now uses the assert logger
    • Node graph logging now uses the nodegraph logger
    • Save and restore logging now uses the saverestore logger
    • Gamerules logging now uses the gamerules logger (this includes log output previously routed to the server log file)
    • Server voice logging now uses the voice logger
      • Removed voice_serverdebug cvar (obsolete since spdlog handles that now)
    • General entity logging now uses the ent logger
      • Entity IO logging (entities being fired, multisource being unlocked, master entity checks) now uses the ent.io logger
      • NPC logging now uses the ent.ai logger
      • NPC scripted behavior logging now uses the ent.ai.script logger
  • Removed ALERT macro

C# code changes

  • Fixed shotguns not being properly aligned in ba_security2
  • Fixed Barney in ba_security2 armory greeting player as Freeman
  • Reworked MapSpecificUpgradeAction to allow specifying multiple maps
  • Blue Shift Hazard Course sentences are now renamed in existing maps
  • Added KeyValueMatcher tool

Asset changes

  • Fixed Blue Shift sentence replacement using the wrong group names for some groups (caused grunts to call you Freeman)
  • Renamed Blue Shift-specific Hazard Course sentences to use distinct names to allow them to be used side by side with the original ones
  • Added missing Blue Shift sentence replacement (scientist pre-disaster dialogue)

ALERT logging changes

The ALERT macro has been removed because it was a pretty poor way to handle the logging of information. There was no way to tell where a message comes from and your only option for verbose logging is at_aiconsole which doesn't leave you with many options. Since this was also used by the node graph certain maps could cause node graph generation to take several minutes instead of a few seconds, so having that on its own logger really helps.

Assert logging uses its own logger using the critical level to make them visible by default. Asserts are only enabled in debug builds so you won't see them at all in a release build.

The ability to selectively enable logging for subsystems makes it easier to debug specific map setups. You can debug entity triggers using the ent.io logger and scripted sequences using ent.ai.script without getting all of the other log output at the same time.

There is one remaining use of pfnAlertMessage: Con_VDPrintf and Con_DPrintf, which uses the engine function to print developer-only output in its own color.

Those functions are only used by filesystem utility functions, where it will eventually be replaced with another spdlog logger.

Client commands should always use UTIL_ConsolePrint to print console responses. This sends log output to the given player's console and uses fmtlib formatting making it easier to use than CLIENT_PRINTF and other alternatives.

The long term goal is for all logging code to use spdlog for greater consistency. Since much of the remaining logging code is in the UI it's easier to leave this be and wait until any large scale UI work has been done to cut down on the amount of work that's needed.

PlayerLogInfo logging

An important change to gamerules logging is how player info is logged. Previously the logging code involved manually checking game modes and getting each part of the player log info to print it:
https://github.com/SamVanheer/halflife-unified-sdk/blob/dda7b26cdf06572aa12cc4f04865433aa47b17c0/src/game/server/gamerules/multiplay_gamerules.cpp#L743-L851

(snippet too big to fit in this post)

This was the most verbose log message in the entire codebase. The change to use this special struct reduces that to:
if (pVictim->pev == pKiller)
{
    // killed self
    Logger->trace("{} committed suicide with \"{}\"", PlayerLogInfo{*pVictim}, killer_weapon_name);
}
else if ((pKiller->flags & FL_CLIENT) != 0)
{
    Logger->trace("{} killed {} with \"{}\"", PlayerLogInfo{*Killer}, PlayerLogInfo{*pVictim}, killer_weapon_name);
}
else
{
    // killed by the world
    Logger->trace("{} committed suicide with committed suicide with \"{}\" (world)", PlayerLogInfo{*pVictim}, killer_weapon_name);
}
Even in the old codebase it was possible to dramatically reduce the complexity by using GetTeamName (exists only in Opposing Force), but that wasn't done in the original codebase, presumably due to time constraints.

This is a great example of how fmtlib's type-safe printing and custom formatting functionalities can lead to simpler and more consistent code.

KeyValueMatcher

The KeyValueMatcher tool has been added. A pre-built version doesn't exist yet since there hasn't been a new pre-alpha build yet.

This tool is really simple: it lets you search through a directory, checking all maps to see if they contain a certain classname and/or keyvalue.

Its intended purpose is to find occurrences of specific keyvalues that are obsolete to determine whether they can be removed.

An example of keyvalues that need checking are these: https://github.com/SamVanheer/halflife-unified-sdk/blob/c64a9ca9896c3f63e1f2759e67e043a36593929a/src/game/server/entities/buttons.cpp#L153-L158

To find these you'd use this command line:
cd path/to/tools
HalfLife.UnifiedSdk.KeyValueMatcher.exe --maps-directory ../maps --classname "^multisource$" --key "^style$"
This particular one prints only map names with no results, since there are no multisource entities with this keyvalue, so that check can be safely removed.

You can filter by classname, key and/or value using regular expressions to match on anything.

I haven't written the documentation for it yet but that will be taken care of soon.

Remaining work to be done

  • Change entities to use sound paths instead of indices (e.g. allow specifying the move sound for doors directly). This should probably include separate sounds for opening and closing. This is a breaking change if left for a later update
  • Update changelog to include all changes
  • Write documentation for all new features (partially complete)
  • Move wiki documentation to repository files like CONTRIBUTING, BUILDING, etc
  • Investigate making the wiki accessible for pull requests to allow direct modification or make the wiki part of the repository (Vcpkg does this, albeit for their website)
  • Review all changes
  • First alpha build
  • Stress test the three campaigns and fix issues that show up (first test done)
  • First beta build
Work expected to be needed in the future (in no particular order):
  • Networking system (transfer contents of replacement files, precache lists, possibly other immutable data): work in progress
  • New UI (including HUD image replacement for Opposing Force): being researched
  • Versions of Opposing Force-only HUD images that have the scanline effect removed: some preliminary research done, needs more investigation
  • A way to start all three campaigns from the main menu. Probably the same solution as used in Condition Zero Deleted Scenes (menu button that starts a map that opens an SDK-level menu that enables campaign and training selection). Depends on the new UI
There are other changes that need doing that aren't on this list because they need to be done in the Updated projects, but those are small changes.

Thanks to everybody for the feedback and help you've been giving, it's been very helpful.
Posted 1 year ago2022-10-29 15:04:23 UTC Post #347028

Progress on Half-Life Updated, Updated CMake and Unified SDK

I've moved all of the wiki documentation for Half-Life Updated, Half-Life: Opposing Force Updated, Half-Life: Blue Shift Updated, Half-Life Updated CMake and Half-Life Unified SDK to the Git repositories themselves. The wikis have been disabled, links to the wiki will redirect to the repository automatically.

The README.md file for each project is the starting point for modders now. It provides information about the projects and provides links to other files.

Build instructions are in BUILDING.md. Installation instructions are in INSTALL.md. The changelog is in CHANGELOG.md. The full changelog is in FULL_CHANGELOG.md.

The Unified SDK additionally has instructions on getting started with development in DEVELOPING.md.

The Unified SDK's documentation is located in the docs directory. The README.md file in that directory contains a list of all documentation, categorized by type.

The links to files are set up to take advantage of Github's relative link feature so you can navigate around the codebase. This also means that links to other Markdown files will work even when the repository is cloned. Unfortunately the same is not true for links to other pages like the issue tracker and releases, but this simplifies the process of cloning the project.

Half-Life Updated CMake archived

The Half-Life Updated CMake project has been archived. This means the project is read-only. You can't make pull requests or issues but you can still clone it. No further updates will be made to it.

This completes the planned archiving of that project.

Converting Half-Life Updated to CMake

I've been thinking about converting the three Updated projects to CMake to deal with several problems:
  • Eliminate the hard-coded path to the mod directory used by the post-build dll copying step
  • Eliminate the need to keep makefiles in sync
  • Allow using Visual Studio 2017 and newer by taking advantage of CMake's generator option (currently users have to open the projects in VS2017 which may require changing the version numbers in the file)
Using CMake also makes it easier to synchronize changes between Updated projects and the Unified SDK. This change would also be paired with a change in directory structure to match the Unified SDK.

Downsides:
  • Users will have to learn to work with CMake. Beginners will have trouble with this unless they are guided in the right direction
  • Existing projects will have trouble pulling changes using Git since the changes will cause merge conflicts
  • Existing tutorials will no longer work with this project. Past experience shows that users will rely on video tutorials which cannot be easily updated and will not be able to adapt to the changes
Before i make a decision i want to ask for feedback from users to see which problems and challenges they may have with CMake so i can figure out an adequate solution. Anybody that wants to discuss this should let me know, preferably either on the TWHL Discord's #unified-sdk channel or in this thread.

Merging Updated projects

I'm considering merging the Updated projects to simplify the development process for them. Blue Shift Updated has very few changes, only two of which can cause problems:
  • Rosenberg needs a separate model. It exists in the Unified SDK, but it will need to be distributed as part of the mod installation and users will need to use those files
  • The hud color is different. This is relatively easy to deal with by making it configurable by cvar and setting it in autoexec.cfg
Opposing Force has a lot more assets that are needed and there has been an indication that people don't want all of it by default. Virtually all of the code unique to Opposing Force require assets so merging it in means mods will have to include it to avoid potential fatal errors.

Merging these projects simplifies development and would effectively turn Half-Life Updated into the Half-Life Unified SDK without new features.

Shepard has created a fork of the Unified SDK that also does this: https://github.com/JoelTroch/halflife-unified-sdk/tree/vanilla

So i'm not so sure if it's useful to do. This process is largely to make it easier to work on the projects since having more projects means making changes in the right project and keeping changes in sync. Ideally there'd only be one project that people can use but due to how the engine works it's difficult to make asset requirements optional.

Changing the projects to use CMake would make this easier since it's easy to add CMake options to conditionally include certain files. This is how it was done in HLEnhanced.

I'd like to hear what people have to say about such a change before i make any decisions.

Other changes

I'm going to add trace logging to precache calls as well as diagnostics logging to help identify which game assets are precached during map startup, as well as which ones are required regardless of maps (i.e. assets precached by weapons).

This is to help identify which files can be removed without breaking the mod installation, as well as to identify which files to put into .res files.

It is still required to include most of the assets that are in the provided mod installation for things to work properly.
Posted 1 year ago2022-10-30 13:04:58 UTC Post #347030
Hm few years ago I tried myself out on VirtualStudio. Trying to compile a modified engine code for hl1. I failed horribly and received countless compiler errors till I gave up on it. If CMake behaves similar or is even identical I'd prefer the easier method of having something behaving similar than Spirit of Half-Life something that largely pre configured. Something that allows the mapper to start mapping right away without having to worry to much about config files, project files. Merging and syncing files online even.

I suppose it all comes down to the general accessibility. Now I do realize that this won't be similar to Spirit at all but you know what I meant by drawing this comparison. That being said I prefer only official releases done by you and your fellow helping hands such as Shepard.
Posted 1 year ago2022-11-01 12:30:18 UTC Post #347035
Hm few years ago I tried myself out on VirtualStudio. Trying to compile a modified engine code for hl1. I failed horribly and received countless compiler errors till I gave up on it. If CMake behaves similar or is even identical I'd prefer the easier method of having something behaving similar than Spirit of Half-Life something that largely pre configured. Something that allows the mapper to start mapping right away without having to worry to much about config files, project files. Merging and syncing files online even.

I suppose it all comes down to the general accessibility. Now I do realize that this won't be similar to Spirit at all but you know what I meant by drawing this comparison. That being said I prefer only official releases done by you and your fellow helping hands such as Shepard.
Half-Life Updated should be easy enough to get going. The problems that people have most of the time have to do with the Windows 10 SDK not being (properly) installed or Visual Studio using a 64 bit dependency instead of a 32 bit one due to the order that paths are added to the PATH environment variable.

That depends on the order that you install stuff in and which version, it's not something that can be accounted for at the HL SDK level. Visual Studio only just became a 64 bit program itself in the latest version and it has caused some problems. We'll definitely need to explain the cause and solution of the most common problems somewhere.

The tutorials we've got on the TWHL wiki cover the important stuff but there is still more information needed in some places like what liblist.gam actually is. The mod creation tutorial tells you to make one and what it looks like, but it doesn't quite explain why it's needed. Unfortunately the contents are only explained in detail on the VDC and not here, so we've got the problem of having to pick which wiki to put the information on (or duplicate it).

The list of liblist.gam keyvalues doesn't say which ones are still in use so that's needed as well. Unfortunately that requires digging into both the engine's various libraries as well as Steam since it also reads that file.

Some tutorials are out of date, like this one since Valve changed the way icons are loaded in an update. It would be nice if they documented these changes, i suppose we could ask through the mailing lists but i doubt that'll get results. It's still worth a try though.

I don't think there's any documentation explaining exactly what every part of the engine does, how it's designed, where things are, etc. Usually we explain this when people ask about it but it really needs to be written down somewhere.

Converting Half-Life Updated to CMake

I've gotten feedback about this and the general consensus is that switching to CMake would cause more problems than it solves. For absolute beginners the added difficulty can be too much and breaking tutorials can put a swift end to aspiring modders so the Updated projects will be left as-is.

The plan for the Updated projects is to get a full V1.0.0 released, then they will be considered feature complete with further development continuing with the Half-Life Unified SDK. The Updated projects will be archived (makes them read-only like Half-Life Updated CMake).

For further development it's always possible for somebody to fork the existing projects and continue their development, so worst case scenario you'll be directed to a set of forks.

Of course the Unified SDK does use CMake so there is still the barrier to entry there. The documentation in the repository does a pretty good job explaining how to set things up:
https://github.com/SamVanheer/halflife-unified-sdk/blob/master/DEVELOPING.md
https://github.com/SamVanheer/halflife-unified-sdk/blob/master/BUILDING.md

But when you don't have any experience with these things it can be a bit much. I'd like to add a tutorial to explain how to do basic things like adding new source files and preprocessor definitions, as well as linking to the documentation for all the third party dependencies and programs used. A video tutorial showing the entire setup process will probably help but that could get out of date quite quickly.

Streamlining the CMake files to reduce visual noise and adding comments to indicate where to make changes will help there.

The README.md for the Unified SDK will be updated to include the list of major features needed to get full seamless support for the three campaigns so people can find it more easily, but i've noticed that people tend to skip over reading the documentation.

If anybody has ideas on how to improve the documentation to catch people's attention and explain things more concisely that'd be great. Note that this has to fit within the limitations of Github's Markdown support, so things like colors aren't always practical (colors used in images are possible though).

I'll also add a note in the Updated project's README.md explaining why the music issues can't be fixed there. The gist of it is that it requires adding a third party dependency which would make setting up the mod way harder (too hard for beginners). Solving this problem without the use of tools like CMake is too complicated for a project that aims to be easy to get started with.

The requirement to use the mod installation as a base for making your own mods needs to be made clearer as well.

Merging Updated projects

This too would complicate things since it involves adding a lot of required content to the mod installation. Updated currently requires at minimum the updated skill.cfg. Merging the three projects would add dozens of models and many more sounds, and the sentences.txt file would still need changes since there are too many sentences when they are combined.

It's best to leave that as-is.

A new map decompiler

I recently learned that the source code for WinBSPC and BSP2Map's map decompilation logic came from an official Quake 3 tool called BSPC: https://github.com/id-Software/Quake-III-Arena/blob/master/code/bspc/map_hl.c

The output is nearly identical to WinBSPC, differing seemingly only in the comment header and the maximum brush range (65535 instead of 8192, Quake 3 vs GoldSource map size from the looks of it).

It looks like making a new decompiler is fairly straightforward using this as a base, but i'd like to get some feedback before i look into this further to see what people have to say about such a thing.

Decompilers can be very useful for learning how certain tricks were done, but on the other hand they can be used to steal other people's work. A better decompiler would allow for both. I'm a bit worried about the ethics of making a decompiler that could be good enough to decompile a map and then being able to immediately compile it again without requiring changes.

What are people's thoughts on this? This isn't a yes or no question, all opinions are welcome.
Posted 1 year ago2022-11-01 20:06:45 UTC Post #347036
Personally, I in your shoes would release the SDK's for Hl1, BS, Op4 standalone in a version 1.0 where you add everything as was it was originally for their respected games just with the additions of your countless fixes and optimizations included. Then I'd focus solely on your unified SDK enhancing it and improving upon it till a version 3.0 or till you're absolutely satisfied with it yourself.
On a mappers perspective I would love to get some sort of repository containing everything that the community ever came up with in 1 huge package for the mappers to choose from and play around with in essence to create the ultimate mod and no I'm not talking about having 100 different weapons at your disposal.

Be it a Rosenberg Entity or some Grey Aliens from Sweet Half-Life (Assuming permission is given) maybe any cut Hl1 monsters with working AI etc. that and of course many of the useful features that Spirit first introduced to the mappers and that you yourself came up with. Be it simple fog or an "entity follows entity ability" which for example was shown and used in Point of View where fire and explosion entities followed the downing Osprey in the Op4 Intro Sequence Remake or model and sound swapping.

That combined with your texture size enhancements, your improved and enhanced general game play performance should be all a Hl1 mappers Heart desires. It's a bit saddening seeing how you couldn't improve upon the sentence.txt text size limitations but that was to be expected.

In regards of decompilers. That's how I actually learned to conduct my mapping or rather entity scripting. I became quite adapt at it thanks to the decompiler tools. In my case I rebuilt scripting wise a Test Chamber from the very map that won the Test Chamber Content award here on TWHL. In another map I rebuilt an entire corridor even replicated it's textures but added my own personal touches and visions into it. It helped me understanding how to properly carve brushes and how general brush work should look like in my early mapper days for Hl1. That said corridor was this one: https://media.moddb.com/images/mods/1/14/13305/Evac.jpg In the original (another Twhl content map) there used to be a Tram line down there. In my vision I added a cave instead with a helipad inside it. Nowadays I naturally built all my maps entirely on my own (Not counting donated maps). Still - I believe taking inspirations of other artists can never hurt 'cause that's what it usually is - Art. That's how humanity competes with each other and advances technologically. There's always somebody who thinks they can do things better than the predecessor.

Those who want to steal work of others always will find their way and they won't care either. So I don't think that it should be of any concern to any us. All you could do in the hopes of preventing it is to mention explicitly to respect the work of others by not taking their stuff. At the very least use it only with their proper consent and credits given as it always should be in such a case. Personally I feel that the Half-Life Community was always a rather respecting community when it comes to Single player content. Lets not talk about the MP branch 'cause that's a very different subject. Anyway in my opinion it would be just fine.

Both the Hl Community and Armed Assault Community in my personal experiences were both 2 of the best communities where people always helped each other so much that the end results often resulted in created content which even surpassed the quality of that of their original respected games. That's something to be very proud of as it is unique and also keeps any game alive (@All Companies: Make this Truth to your own. :D) So I'm in favor of an enhanced decompiler tool.
Posted 1 year ago2022-11-06 18:21:13 UTC Post #347049
Personally, I in your shoes would release the SDK's for Hl1, BS, Op4 standalone in a version 1.0 where you add everything as was it was originally for their respected games just with the additions of your countless fixes and optimizations included. Then I'd focus solely on your unified SDK enhancing it and improving upon it till a version 3.0 or till you're absolutely satisfied with it yourself.
Yes that is pretty much the plan. The three Updated projects are slated for a full release, after that they'll be archived. It's always possible for others to continue supporting them by forking the project or by being given contributor access, but i'd like to focus on the Unified SDK after this.
On a mappers perspective I would love to get some sort of repository containing everything that the community ever came up with in 1 huge package for the mappers to choose from and play around with in essence to create the ultimate mod and no I'm not talking about having 100 different weapons at your disposal.

Be it a Rosenberg Entity or some Grey Aliens from Sweet Half-Life (Assuming permission is given) maybe any cut Hl1 monsters with working AI etc. that and of course many of the useful features that Spirit first introduced to the mappers and that you yourself came up with. Be it simple fog or an "entity follows entity ability" which for example was shown and used in Point of View where fire and explosion entities followed the downing Osprey in the Op4 Intro Sequence Remake or model and sound swapping.
I prefer to focus on the limited scope that these projects have because it takes a lot of effort to add so much functionality. Adding stuff from other projects requires the code to be updated to work with the existing modifications and requires the project license to be compatible with what these projects will have (MIT license).

Entities can already follow other entities using the existing MOVETYPE_FOLLOW, it's how Alien Controller sprites stay attached to their hands for instance. It should be possible to expose that functionality for mappers to use though.
That combined with your texture size enhancements, your improved and enhanced general game play performance should be all a Hl1 mappers Heart desires. It's a bit saddening seeing how you couldn't improve upon the sentence.txt text size limitations but that was to be expected.
I did improve on the size limitations, it just requires the custom sound system. The new limit is 65535 sentences. It's a bit hard to spot since i tend to post such long posts. I'll need to write the documentation for that feature to make it clearer.
In regards of decompilers. That's how I actually learned to conduct my mapping or rather entity scripting. I became quite adapt at it thanks to the decompiler tools. In my case I rebuilt scripting wise a Test Chamber from the very map that won the Test Chamber Content award here on TWHL. In another map I rebuilt an entire corridor even replicated it's textures but added my own personal touches and visions into it. It helped me understanding how to properly carve brushes and how general brush work should look like in my early mapper days for Hl1. That said corridor was this one: https://media.moddb.com/images/mods/1/14/13305/Evac.jpg In the original (another Twhl content map) there used to be a Tram line down there. In my vision I added a cave instead with a helipad inside it. Nowadays I naturally built all my maps entirely on my own (Not counting donated maps). Still - I believe taking inspirations of other artists can never hurt 'cause that's what it usually is - Art. That's how humanity competes with each other and advances technologically. There's always somebody who thinks they can do things better than the predecessor.

Those who want to steal work of others always will find their way and they won't care either. So I don't think that it should be of any concern to any us. All you could do in the hopes of preventing it is to mention explicitly to respect the work of others by not taking their stuff. At the very least use it only with their proper consent and credits given as it always should be in such a case. Personally I feel that the Half-Life Community was always a rather respecting community when it comes to Single player content. Lets not talk about the MP branch 'cause that's a very different subject. Anyway in my opinion it would be just fine.

Both the Hl Community and Armed Assault Community in my personal experiences were both 2 of the best communities where people always helped each other so much that the end results often resulted in created content which even surpassed the quality of that of their original respected games. That's something to be very proud of as it is unique and also keeps any game alive (@All Companies: Make this Truth to your own. :D) So I'm in favor of an enhanced decompiler tool.
That seems to be what everybody's opinion on the matter is. Decompilers are very useful tools, i use them all the time when i'm changing existing functionality to debug original campaign maps, but the limitations of those tools make things harder.

Fortunately i've managed to build a new one.
Posted 1 year ago2022-11-06 18:21:19 UTC Post #347050

New Map Decompiler

I've built a new map decompiler based on the original decompiler source code taken from Quake 3's bspc tool:
Just like all decompilers this tool is intended for educational purposes only. Stealing maps is frowned upon, but learning how certain tricks were done will ultimately help people to get better at making maps. My intention with this new tool is to produce more accurate map source files to lower the learning curve for level designers.

Features:
  • Improved map decompiler based on bspc's decompiler code (same as WinBSPC): referred to as the Tree decompiler because it uses the BSP tree to reconstruct brushes
  • Improved map decompiler based on BSP2Map's decompiler logic (all new code): referred to as the Face-To-Brush decompiler since it converts each face to a brush
  • Fixed crash issues
  • Fixed entity data parsing issues with some maps
  • Fixed not being able to decompile maps compiled with tools that optimize out unused BSP planes
  • Fixed some cases of perpendicular texture axis (needs more work)
  • Embedded textures are extracted to <filename>_generated.wad WAD file
  • Fixed some brushes being extremely large due to rounding errors
It's not quite ready yet, as you can see in the video some textures aren't aligned correctly. But the logic for both decompilers is there and works. It'll never be perfect since there isn't enough information in a BSP file to recreate everything, and some brush faces in a compiled map have an illegal shape.

For instance several faces in c1a0 alone are defined by points that all lie on the same line. I suspect this can be remedied to an extent by converting everything to double precision floating point values but if the problem is in the original data then it won't be possible to convert those brush faces. The Brush-To-Face decompiler skips those since it can't get any usable data from them.

Work that needs doing to be feature complete (barring unforeseen crash issues or decompilation failures that can be fixed):
  • Fix all cases of texture axis perpendicular to face if possible
  • Fix cases of textures not using their original X and Y alignments
  • Refactor logic used by both decompilers (i.e. code to calculate texture properties)
  • Add About dialog
This tool is written entirely in C#. This makes it easier to maintain and upgrade and also allows me to use cross-platform UI frameworks without also having to compile everything on the target platform. I haven't tested it on other platforms yet but it should work as well as any Avalonia application. I doubt it'll work well on mobile devices since the UI is geared for desktop.

It's GPLv2-licensed since the original code was released under that license. This is why its code has to be kept separate from other tool code since that's all MIT licensed.

The minimum requirements for the GUI are listed here: https://github.com/AvaloniaUI/Avalonia/wiki/Runtime-Requirements

For users on Windows 7 the command line version can everything the GUI does:
HalfLife.UnifiedSdk.MapDecompiler.CmdLine.exe --help
Description:
  Half-Life Unified SDK Map Decompiler

Usage:
  HalfLife.UnifiedSdk.MapDecompiler.CmdLine [<files>...] [options]

Arguments:
  <files>  List of files to decompile

Options:
  --strategy <FaceToBrush|Tree>                          Which decompiler algorithm to use [default: Tree]
  --destination <destination>                            Directory to save decompiled maps to. Leave empty to use current working directory []
  --merge-brushes                                        Whether to merge brushes [default: True]
  --include-liquids                                      Whether to include brushes with liquid content types [default: True]
  --brush-optimization <BestTextureMatch|FewestBrushes>  What to optimize brushes for [default: BestTextureMatch]
  --version                                              Show version information
  -?, -h, --help                                         Show help and usage information
For those interested, here's how i ported it to C#:
  1. Clone the source code from https://github.com/id-Software/Quake-III-Arena
  2. Remove all files not referenced by the bspc project
  3. Remove all functionality not related to map decompilation
  4. Remove all decompiler paths for BSP formats other than Half-Life's
  5. Remove support for extracting maps from pak and zip files
  6. Use CppCheck to find unused code in remaining files and remove that code
  7. Remaining code is around 4507 lines of C code
  8. Port decompiler logic starting from its main entry point: https://github.com/id-Software/Quake-III-Arena/blob/dbe4ddb10315479fc00086f08e25d968b4b43c49/code/bspc/map_hl.c#L1067
    • Use Penguinboy's excellent Sledge.Formats libraries to handle BSP loading and MAP and WAD writing
    • Convert code to use C# features like classes, containers and LINQ to streamline implementation
  9. Test implementation to catch typos and translation errors
  10. Test implementation against various maps to find problems that the original tool didn't
  11. Test implementation against maps known to crash the original tool and fix issues
  12. Add command line and GUI programs to interface with the decompiler
    • Use Avalonia to build cross-platform UI
    • Parallel-process maps to speed up decompilation and keep the UI responsive
    • Thread-safe logging requires separation of log output for each job
  13. Implement BSP2Map decompiler
    • BSP2Map works by turning each brush face into a brush of its own. The original face is duplicated, inverted and offset by 1 unit in the direction opposite of what the original face was facing, new brush faces are added to connect the two
Posted 1 year ago2022-11-07 15:53:55 UTC Post #347054

Map Decompiler beta build

I've released a first beta build for the new map decompiler: https://github.com/SamVanheer/HalfLife.UnifiedSdk.MapDecompiler/releases/tag/V0.1.0.0-beta001

Features:
  • Improved map decompiler based on bspc's decompiler code (same as WinBSPC): referred to as the Tree decompiler because it uses the BSP tree to reconstruct brushes
  • Improved map decompiler based on BSP2Map's decompiler logic (all new code): referred to as the Face-To-Brush decompiler since it converts each face to a brush
  • Fixed crash issues
  • Fixed entity data parsing issues with some maps
  • Fixed not being able to decompile maps compiled with tools that optimize out unused BSP planes
  • Fixed perpendicular texture axis errors
  • Embedded textures are extracted to <filename>_generated.wad WAD file
  • Fixed some brushes being extremely large due to rounding errors
  • Fixed texture coordinates being incorrect for brush entities with an origin brush
This is a beta release intended for testing. All feedback is welcome, but please keep in mind that this decompiler is not going to get major changes. The process used to decompile maps doesn't allow for radical changes like reshaping brushes so beyond minor issues there isn't much that can be done.
Posted 1 year ago2022-11-10 18:41:30 UTC Post #347074

Map Decompiler beta 2

I've released the second beta build for the new map decompiler: https://github.com/SamVanheer/HalfLife.UnifiedSdk.MapDecompiler/releases/tag/V0.2.0.0-beta002

Changes:
  • Sledge.Formats.Bsp:
    • Textures with missing data are now added as dummy textures (matches engine behavior)
  • Sledge.Formats.Texture:
    • Added default constructors to all supported lump types
  • Project-wide changes:
    • Updated Sledge.Formats.Bsp and Sledge.Formats.Texture dependencies to their latest versions
    • Updated Avalonia to v11.0.0-preview3 to fix DataGrid theme not changing along with the rest of the application
    • Added README.md and license files to published projects
    • Raise property change event on UI thread to prevent problems
    • Use double precision maths to process maps. This increases the accuracy of calculations and solves problems with complex brushwork ending up deformed, and also solves problems with some maps (e.g. cz_lostcause) getting stuck in an infinite loop due to lack of precision
    • Save and load application settings. Settings are saved in AppData/Roaming/Half-Life Unified SDK/MapDecompilerSettings.json
    • Limit job selection to one job at a time (selecting multiple had no effect and is not intended behavior)
    • Reworked log output to use AvaloniaEdit TextArea to eliminate performance issues caused by poor large string support in TextBox controls. This change required a change in scrolling behavior: previously the user could scroll up and freely scroll and scrolling all the way down enabled automatic scrolling, now automatic scrolling is always enabled
    • Miscellaneous refactoring and cleanup
  • Tree Decompiler:
    • Fixed empty texture names causing out of range access
    • Removed obsolete map file limits (decompiler used to use fixed size arrays to store converted data, now uses dynamically allocated arrays)
    • Added more checks for job cancellation to improve responsiveness when cancelling Tree-based decompilation jobs
    • Fixed cleared map bounds having arbitrary maximum size (some BSP format variants exceed the old size, but there are other places where low limits are in place)
  • Face-To-Brush Decompiler:
    • Faces are now merged as much as possible before converting them to brushes (Before, After)
    • Original plane normal is used instead of generating it from face vertices (more accurate this way)
    • Use more accurate collinear detection logic to eliminate false positives
    • Skip faces that are tiny or huge
    • Remove collinear vertices from face before checking face for validity
This beta should work with just about any map but if any maps fail to decompile let me know.

Note that more complex maps will produce invalid geometry, that's a limitation of the decompilation process.

Also note that this only works with Half-Life 1 version 30 BSP files. Alpha, beta and early Source engine maps and maps made for engine offshoots like Svengine and Paranoia 2 are not supported.
Posted 1 year ago2022-11-12 16:27:20 UTC Post #347079

Map Decompiler beta 3

I've released the third beta build for the new map decompiler: https://github.com/SamVanheer/HalfLife.UnifiedSdk.MapDecompiler/releases/tag/V0.3.0.0-beta003

Changes:
  • Project-wide changes:
    • Updated Avalonia to 11.0.0-preview4
    • Create output directory before writing to it
    • Properly handle exceptions thrown during job processing (processing never stopped on its own if an uncaught exception occurred)
    • Use SelectableTextBlock with LineBreak to fix main window and About dialog text layout. This also makes both sets of text selectable
    • Added support for Half-Life Alpha BSP format (this format is Half-Life's BSP format, but with version set to 29 instead of 30)
      • Quake 1 maps will fail to decompile when loaded. They are version 29 maps, but lack texture palette information which the BSP reader expects
    • Updated README.md with additional information:
    • Marked job tasks as long running. This should help to limit the negative effects that job tasks have on asynchronous UI actions (thread starvation)
    • Added an indicator that job failed in program log (adds (Failed) in front of job in the log)
    • Miscellaneous refactoring
  • Both decompilers:
    • Added checks to skip invalid models (e.g. HL Alpha c2a4b has ambient_generic entities that reference brush models that don't exist)
    • Fixed collinear point detection not working across end of array boundary
  • Tree decompiler:
    • Fixed decompiler checking brush faces not part of the current model when matching textures (e.g. c1a0 the doors that open at the start had the wrong texture)
  • Face-To-Brush decompiler:
    • Fixed decompiler leaving duplicate faces after face merging (e.g. c1a0 floor in lobby)
    • Correct normals and vertices that are slightly off the axis normal
Posted 1 year ago2022-11-15 15:48:59 UTC Post #347090

Map Decompiler beta 004

I've released the fourth beta for the new map decompiler: https://github.com/SamVanheer/HalfLife.UnifiedSdk.MapDecompiler/releases/tag/V0.4.0.0-beta004

Changes:
  • Project-wide changes
    • Replaced fix for Face-To-Brush decompiler normal correction with a generalized fix that works for both decompilers (fixes some bad brushes in cz_recoil and likely other maps)
    • Added Quit menu button
    • Added additional log output to the program log to indicate that jobs are being cancelled
    • Marked cancelled jobs with "(Cancelled)" in the program log
    • Reworked some asynchronous code to be more correct
    • Added option to control WAD file generation
    • Added diagnostic output to show what the Face-To-Brush decompiler is doing
    • Elaborated some more on decompiler accuracy in the readme file
    • Miscellaneous refactoring
This will be the last beta build for a while. Once Avalonia 11 has been released i'll update the program and deal with any breaking changes, then a full release can be considered.
Posted 1 year ago2022-11-18 15:43:13 UTC Post #347098

Map Decompiler beta 005

I've released the fifth beta for the new map decompiler: https://github.com/SamVanheer/HalfLife.UnifiedSdk.MapDecompiler/releases/tag/V0.5.0.0-beta005

Changes:
  • Project-wide changes
    • Increased maximum map size to allow decompilation of maps that use maximum engine limits. Some maps used the absolute maximum limit of [-16384, 16384] allowed in the original engine which didn't decompile correctly. Was originally [-4096, 4096], is now [-1,048,576, 1,048,576], larger than any known Half-Life 1 engine variant
    • Ignore duplicate worldspawn entities if they exist in the map (was causing world brushes to be repeatedly decompiled and placed at the entity's origin)
  • Tree decompiler
    • Fixed liquid content texture lookup possibly accessing null reference
Testing shows that the Tree decompiler has trouble with maps that include terrain shaped with prisms as described by this tutorial: https://twhl.info/wiki/page/Tutorial%3A_Advanced_Terrain_Creation

This is a limitation of this decompiler's approach to constructing brushes. Brushes whose edges aren't aligned to the X, Y and Z axes tend to produce brush faces that can't be accurately represented:
User posted image
This particular map (Jungle Bridge by Hons.Elle) seems to have a skybox surrounding the playable area so that probably contributes to the results.

I also checked an old review of WinBSPC (Tree-based) and BSP2Map (Face-To-Brush) to check if there are any other issues to fix: https://valvedev.info/guides/goldsrc-map-decompilers-bsptwomap-vs-winbspc/
Test one: Half-Life's c1a0
BSPTwoMap did a rather poor job recreating the brushwork. You'll notice in all BSPTwoMap decompiles that the brushwork has been chopped up into pieces. Brush entities, rather than being one brush, would become six paper-thin brushes merged together into a box. Note the desk; gaping holes in otherwise solid surfaces are a trademark of BSPTwoMap. Texture axis errors and invalid solid structure errors plagued the brushwork of every BSPTwoMap decompile. Needless to say, these maps would require a lot of work before they became usable again.
The Face-to-brush decompiler does produce brushes where BSP2Map leaves holes and handles texture properties correctly.
WinBSPC's defect is that the level is carved from a box the size of the entire map, which may or may not result in usable brushwork. Here, it at least resembles the original c1a0 better, but still with invalid solids and some texture rotation issues. You'll also notice in that screenshot that it got the texture of the blast doors wrong.
The Tree-based decompiler does a better job producing accurate geometry but it still produces invalid geometry sometimes. It also handles texture properties correctly.
Test two: Half-Life's crossfire
BSPTwoMap's brushwork could charitably be described as inaccurate, but is closer to downright unusable. In addition to the split faces, many brushes (including the railings and ceilings) jut out in triangles, completely unlike the original map. Even worse is the sky; a sky brush that did not exist in the original map cut through the top half of the playfield. Note that we had to split that sky brush in half to let you see the ground of the map. BSPTwoMap's razor-thin brushwork is even more apparent here.

WinBSPC's is more accurate and usable, even getting the odd, uneven terrain correct, but note the texture errors on the walkways. Signs, again, were oriented upside down.
None of these issues occur with either decompilers, although some maps do get the occasional huge brush but it's less common than before.
Test three: Half-Life: Opposing Force's op4ctf_gunyard
BSPTwoMap tripped around any sort of thin brushwork; as you can see from the screenshot, there's more than a few invalid faces from when the railings became concave.

WinBSPC gets it closer, aside from some texturing issues and a few broken solids. Amusingly, the health chargers got flipped upside down.
Same story. The Face-to-brush decompiler produces a single bad brush and associated bad texture properties but it's much better.
Test four: Ricochet's rc_arena
The BSPTwoMap decompile is an actual disaster. Not one of the platforms survived the decompile in any sort of a recognizable shape. We could barely navigate the map in the editor well enough to take a screenshot. The WinBSPC decompile didn't come out so hot either; though its shape is more recognizable, most of the platforms were split into 5-6 brushes each, and the jump arrows were similarly splintered and with texture alignment issues.
The Face-to-brush decompiler produces very accurate results with no invalid solids. The Tree-based decompiler still produces split brushes but that's to be expected. Texture alignment issues aren't apparent, but there are a couple brushes with tiny edges.

It should be noted that the Face-to-brush decompiler uses the visual mesh stored in the BSP file to generate brushes. I suspect BSP2Map uses the output produced by the Tree-based decompiler as a base which would explain some of its problems.

Using the visual mesh makes it more accurate but it also means any brush faces not included by the compiler for rendering are excluded. The Tree-based decompiler also has trouble with these brush faces, for instance cz_recoil has a func_tank with no brushes tied to it because the brushes are invisible and are excluded from the result.

It might be possible to generate these invisible brushes by applying NULL to them but some quick experiments yielded no success. Either way the results will be as (in)accurate as the rest so it'll never be a perfect recreation.

This is also why implementing a modern physics engine in this game is unfeasible. Engines like Havok and Bullet require a 1:1 accurate collision mesh to work with, which doesn't exist in the BSP file. The visual mesh is accurate but incomplete and the four collision hulls are stored only as BSP trees which only provide BSP planes to work with, so constructing an accurate collision mesh is impossible. You'd need to store the original brush data somewhere to use instead.

I think that about wraps up all of the work to be done on this project, at least for the time being.
Posted 1 year ago2022-11-23 12:49:02 UTC Post #347126

Map Decompiler beta 006

I've released the sixth beta for the new map decompiler: https://github.com/SamVanheer/HalfLife.UnifiedSdk.MapDecompiler/releases/tag/V0.6.0.0-beta006

Changes:
  • Project-wide changes:
    • Renamed Cancel button to Cancel All
    • Added Decompile All Again button
    • Fixed Vector3 parsing not using invariant culture (i.e. your computer's locale settings could cause it to parse origins as 0 0 0)
Posted 1 year ago2022-11-26 22:18:51 UTC Post #347132

Progress on Unified SDK

I've released a new pre-alpha build for testing: https://github.com/SamVanheer/halflife-unified-sdk/releases/tag/dev-2022-11-26

Note that the tag name no longer includes the time, this is because the tag is displayed on the HUD and it is physically impossible for the tag to include the time when the mod is compiled since it's only determined when the mod is packaged, so i've opted to omit time information. If multiple releases occur on the same day then it's always possible to add a number to disambiguate the releases.

SDK changes

  • Merged Opposing Force and Blue Shift Git repositories into the Unified SDK repository to ensure the Git history is there and future changes can be merged directly
  • Ensured time compression calculations always produce channel-aligned values
  • Added partial changelog containing all changes that were made in the repository. This does not yet include pull request or changes made in other repositories
  • Added documentation for Skill2Json, Bsp2Obj, KeyValueMatcher and sound system
  • Added support for writing to signon buffer when using new sound system (in laymans terms, this allows looping sounds played by ambient_generic to restart when loading saved games or when going through a level transition back to a map that has an active one)
  • Added diagnostic logging for map startup stages (helps to pinpoint when other log output occurs)
  • Added logger for precache calls. This allows you to see which models, sounds and generic file precaches are occurring, but does not include precache calls made by the engine
  • Ensured all worldspawn keyvalues restore correctly on save game load. The WaveHeight keyvalue did not get set correctly on save game load causing water that isn't func_water to use the wave height that was last set
  • Route all entity sound precaches through PrecacheSound (some Opposing Force entities still called the engine functions directly)
  • Use std::numbers::pi instead of custom PI constant
  • Fixed client keeping replacement maps in cache after map change
  • Extended global sound replacement to apply to client side studio events
  • Added some missing common headers to server library (allows you to open them directly from Visual Studio)
  • Defined constants for waterlevel variable and updated all uses to use the WaterLevel enum
  • Fixed momentary_door trying to play null string_t. This caused warnings to be printed in the console
  • Replaced tracktrain event with proper throttled sound updates. This reduces the amount of work that the engine has to do to handle train pitch sound updates and removes some client side code that is now back on the server side like it was in SDK 1.0
  • Fixed momentary_rot_button not stopping its sound
  • Converted entities to use sound/sentence names instead of indices. The following entities now allow you to specify sound filenames or sentence names (for (un)locked sentence) instead of using combo boxes:
    • func_rotating
    • func_button
    • func_rot_button
    • momentary_rot_button
    • func_door
    • func_door_rotating
    • momentary_door
    • func_plat
    • func_platrot
    • func_train
    • func_tracktrain
    • func_trackchange
    • func_trackautochange
  • Restart track train sound if a player joins (looping sounds don't play for players if they started before they joined)
  • Added scripts to format all source files. The script to perform formatting is a C# script, the scripts that format source files specifically are batch and Bash shell scripts for platform-specific use. This relies on clang-format to perform the formatting. Documentation for this script has not been written yet and is needed to explain how the executable is located so you won't be able to run the scripts just yet
  • Formatted all files

C# changes

  • Initialize parsed vector values array before using it
  • Organized projects in solution folders
  • Implemented Bsp2Obj tool
  • Added ImageSharp license
  • Update README to point to general NET SDK download page
  • Point to main repo documentation
  • Added licenses to programs
  • Reworked KeyValueMatcher printing to allow printing only keyvalue that matched (to limit the amount of output printed)
  • Install all maps instead of only campaign and training maps (so multiplayer maps are updated and work correctly)
  • Published HalfLife.UnifiedSdk.Utilities version 0.1.4 on Nuget

Asset changes

  • Added test map with looping sentence to test signon buffer use
  • Converted entities to use sound names instead of indices
  • Added test map for sound names
  • Removed obsolete train.sc event file

Bsp2Obj tool

This tool converts Half-Life 1 BSP files to the Wavefront OBJ format the same way that Nemesis's Crafty tool does, without depending on any particular Steam filesystem architecture to make immune to breaking changes.

Here's an example showing c1a0 imported in Blender:
User posted image
Consult the documentation for more information: https://github.com/SamVanheer/halflife-unified-sdk/blob/master/docs/tools/bsp2obj.md

Custom sound support for entities

Entities that previously used hardcoded sounds selected by an index now let you specify the sound directly. This allows you to easily use custom sounds that are emitted from the entity itself. In the case of doors you can also specify locked and unlocked sentence groups to play.

See this video for some examples:

Remaining work to be done

  • Change entities to use sound paths instead of indices (e.g. allow specifying the move sound for doors directly). This should probably include separate sounds for opening and closing. This is a breaking change if left for a later update (done)
  • Update changelog to include all changes (partially complete)
  • Write documentation for all new features (partially complete)
  • Move wiki documentation to repository files like CONTRIBUTING, BUILDING, etc (done)
  • Investigate making the wiki accessible for pull requests to allow direct modification or make the wiki part of the repository (Vcpkg does this, albeit for their website) (done)
  • Review all changes
  • First alpha build
  • Stress test the three campaigns and fix issues that show up (first test done)
  • First beta build
Work expected to be needed in the future (in no particular order):
  • Networking system (transfer contents of replacement files, precache lists, possibly other immutable data): work in progress
  • New UI (including HUD image replacement for Opposing Force): being researched
  • Versions of Opposing Force-only HUD images that have the scanline effect removed: some preliminary research done, needs more investigation
  • A way to start all three campaigns from the main menu. Probably the same solution as used in Condition Zero Deleted Scenes (menu button that starts a map that opens an SDK-level menu that enables campaign and training selection). Depends on the new UI

Other changes to be done

I'm going to change the version number for the first release from 1.0.0 to 0.1.0 to reflect the fact that the SDK isn't complete yet.

V1.0.0 should include HUD customization to the point that you can switch the sprites (or TGA files, depending on how it's implemented) and campaign selection.

The documentation will be updated to include the list of major feature work that's left to be done to make this more obvious.

The documentation is more complete now but still needs quite a bit more to cover all of the changes that have been made. The changelog provides a high-level overview of what's been done but there's nothing that shows how this SDK is different from vanilla Half-Life so that will be necessary.

Additionally the entity guide on TWHL's wiki needs to be updated to include the sound and sentence names used by the entities that have been updated. This is useful both for vanilla and this SDK and will be needed for people to know which files and sentence groups are used by the original options.

The button sound options also need updating to include undocumented options: func_button can use the options available to momentary_rot_button and vice versa.

For those interested in knowing why entities use indices instead of file paths like some other entities do, this is largely due to Quake 1 legacy code: https://github.com/id-Software/Quake-Tools/blob/c0d1b91c74eb654365ac7755bc837e497caaca73/qcc/v101qc/buttons.qc#L108-L127

Newer code tends to be more flexible.

func_door 's documentation also needs information about what linking does. I believe this is an often-overlooked feature since i've seen maps implement glass windows as doors with unique names which can cause them to get out of sync, which linking should prevent.

Documentation for entities in the repository will be moved to its own directory with a README.md listing entities by these categories:
  • New
  • Modified
  • Removed
This is to have a central point to use as a reference. Pages related to entities will link to the specific files and vice versa to establish the relationship.

I think it's useful to have a table of contents file that links to every single "hub" Markdown file in the repository. Since the number of files will continue to increase this'll come in handy.

Most of the work needed for V0.1.0 is done now. Once the changelog and documentation is done the first alpha build can be released.

That's about it for now. Thanks to everybody for giving feedback and helping with development, until next time!
Posted 1 year ago2022-11-28 15:34:23 UTC Post #347137

Map decompiler beta 7

I've released the seventh beta for the new map decompiler: https://github.com/SamVanheer/HalfLife.UnifiedSdk.MapDecompiler/releases/tag/v0.7.0.0-beta007

Changes:
  • Project-wide changes:
    • Updated Sledge.Formats.Bsp to version 1.0.8
    • Fixed lightmap size calculation issues (Counter-Strike's de_airstrip couldn't be decompiled because of this)
    • Added option to apply NULL to generated faces. Note that due to inherent limitations in decompiler accuracy some faces that appear valid will be textured with NULL
  • Tree decompiler:
    • Fixed some Day of Defeat maps causing decompilation failure due to Null contents (the compiler is supposed to convert Null contents to Solid contents, which the compiler used for those maps evidently did not do)
    • Fixed cases where faces are incorrectly thought to lie on a winding that it is parallel to. This caused some faces to get a seemingly random texture, actually a texture used on a face that happens to be on the same plane as the one created by the root BSP tree node
    • Fixed exterior faces (faces that touch the void outside the map) generated by compiler being considered for texturing. They will still receive textures but are not checked for potential matches with existing faces since there aren't any
I tried to decompile all of the official maps for all games and they all successfully decompiled. This means all internal decompiler errors are fixed, but the results will never be perfect. I've also tried maps made with modern compilers and maps with complex geometry, which produce the expected results (very complex and off-grid geometry has gaps).
Posted 1 year ago2023-02-04 15:39:31 UTC Post #347297

Half-Life Updated betas released

Half-Life Updated, Half-Life: Opposing Force Updated and Half-Life: Blue Shift Updated betas have been released:
Half-Life Updated: https://github.com/SamVanheer/halflife-updated/releases/tag/HLU-V1.0.0-beta014
Half-Life: Opposing Force Updated: https://github.com/SamVanheer/halflife-op4-updated/releases/tag/HLOP4U-V1.0.0-beta011
Half-Life: Blue Shift Updated: https://github.com/SamVanheer/halflife-bs-updated/releases/tag/HLBSU-V1.0.0-beta011

Bug fixes

  • Correctly save and restore controller and blending variables
  • Set activity before SetYawSpeed so it has the right activity
  • Fixed animation code accessing invalid sequence descriptor
  • Fixed weapon bits being incorrectly set and checked in HasAnyWeapons
  • Fixed projects with spaces in paths failing to execute post build step
  • Fixed NPCs not being able to speak scripted sentences while in scripted death
  • Removed unnecessary semicolons
  • Updated source file encoding to UTF-8
  • Renamed CWorld::Instance to CWorld::World to avoid conflicting with CBaseEntity::Instance function name
  • Added -flifetime-dse=1 flag to Linux Makefile to disable compiler optimization that removed entity memory zero-initialization, resulting in the game crashing when any entity touches the world
  • Fixed game_player_equip crashing when given a null activator
  • Fixed Hornet gun recharging to full ammo after loading a save game
  • Fixed explosives that impact the underside of a brush dealing damage to entities on the other side of that brush
  • Fixed entities with an index greater than 2047 corrupting the client's heap if sent over the network (these entities are never sent over the network, so they will be invisible and won't be checked in client side physics calculations)

New features

  • Save and restore game_player_equip (this allows the entity to be used in singleplayer)
  • Moved IsFacing function from barney.cpp to h_ai.cpp to help prevent linker errors when copy pasting source file
  • When using impulse 107 to get the name of a texture the texture type (as used in materials.txt) will also be printed
  • Made PM_FindTextureType const correct
  • Added WRITE_FLOAT function corresponding to the client's READ_FLOAT function
  • Set maximum edicts to 2048 in liblist.gam (note: the actual maximum edicts value is 2048 + (15 * (maxplayers - 1)). It is possible to create more than 2048 entities as long as entities with an index exceeding this don't have any physical presence in the world (i.e. no model, non-solid, no movement) but since the entity index cannot be chosen when creating entities this is impractical. The game will crash in the server-side player physics code if solid entities are created, presumably because that system is hard-coded to a maximum of 900 solid/visible entities with no bounds checking

Project changes

  • Added delta.lst to the archive again (was accidentally removed in the previous beta)
  • Added game icons to the archive
I also investigated increasing the maximum map size that game code allows for. I concluded that since some parts of the engine hard-code the limit to 4096 units in all directions it would result in inconsistencies so the limit will remain as-is in the SDK source code. Modders can change this limit if they want to, provided that they also implement custom network messages to bypass the engine's hard-coded limit. See this issue for more information.

Thanks to vasiavasiavasia95, FreeSlave, jay!, Shepard, λλλλλλ, anchurcn and Hezus for helping with this update.
Posted 1 year ago2023-02-04 17:32:09 UTC Post #347298

Progress on the Unified SDK

It's been a while since i've posted an update about the Unified SDK. This is mainly because i've been focusing on Half-Life Asset Manager 2.0.0 which is in beta and is expected to reach a full release soon.

SDK Changes

  • Added more documentation covering new feature and changes to existing features
  • Added instructions on how to view Markdown documentation locally using Visual Studio Code
  • Streamlined tool and script documentation
  • removed test_effect entity (not used and only partially implemented, did not function)
  • Return CBasePlayer* from UTIL_PlayerByIndex
  • Removed virtual functions in CBaseEntity that are only implemented for players and called by code that knows it's CBasePlayer
  • Added ToBasePlayer function to convert a (possibly null) entity pointer to a player pointer, with debug check to verify that it is actually a player to catch incorrect results returned by CBaseEntity::IsPlayer
  • Applied a bunch of code cleanup that was originally done in HLEnhanced. This reduces the complexity of type declarations, visual noise, the amount of types suggested by Intellisense, generally making the codebase easier to navigate through
  • Replaced some macros with STL functions
  • Fixed scripted_sequence searching for targets incorrectly
  • Use CBaseEntity find functions instead of edict_t versions
  • Added entity dictionary to create entities with
  • Don't try to play sentences through engine if index exceeds engine limit
  • Use script logger for scripted_sentence and scripted_sequence
  • Changed existing functions to use CBaseEntity for entity parameters instead of edict_t or entvars_t
  • Use local port for openal-soft, modified library name to use one that doesn't conflict with one the engine uses
  • Install OpenAL on Linux, create symlink, locate library using $ORIGIN so the library is located next to client.so

C# Changes

  • Improved command line option and argument passing for existing programs
  • Added missing index-to-filename conversions for func_water and func_train sounds
  • Implemented support for copying assets to hd and lv directories to the AssetSynchronizer

Asset changes

  • Changed MapCfgGenerator destination directory argument from Option to Argument
  • Updated AssetManifest.json to use new structure
  • Added asset source files for many models made by malortie

Entity dictionary

The new entity dictionary has allowed for improvements to be made to existing code.

This change required cbase.h to be split up to ensure correct header include order.

The CREATE_NAMED_ENTITY engine function has been rendered obsolete by this change, the game now looks up entities by name locally using this dictionary. string_t no longer implicitly converts to string_t_value because it was only needed to support CREATE_NAMED_ENTITY.

GetClassPtr no longer exists. Creating entities programmatically with specific types is now done in this manner:
CBeam* pBeam = g_EntityDictionary->Create<CBeam>("beam");
If the type doesn't match what you've requested (e.g. g_EntityDictionary->Create<CBasePlayerWeapon>("beam")) then the game will assert in debug builds to warn you.

It is no longer required for the classname string to continue existing since it's automatically allocated from the string pool. Manual classname assignments are rarely needed since all entity creation is done through the dictionary which guarantees classname assignment. Only entities with multiple classnames and those created on the client side still need it (basically just weapons).

In addition to the entity dictionary there is also a second dictionary for weapons only: g_WeaponDictionary

This dictionary only tracks entities that inherit from CBasePlayerWeapon and is currently used by the weapon precache code. In the future this dictionary will be used for more things when HLEnhanced's updated weapon prediction code and the Better Weapons SDK are merged in. It renders the Better Weapons macro used for weapon registration obsolete.

Additional dictionaries can be added for specific sets of classes:
template <typename TEntity>
void RegisterEntityDescriptor(EntityDescriptor<TEntity>* descriptor)
{
    assert(descriptor);

    // This is where each dictionary is initially constructed and built to add all entity classes.
    // Ideally some form of reflection would be used to build these dictionaries after the initial dictionary has been created,
    // but since we don't have reflection available and existing libraries require loads of refactoring this will have to do for now.
    detail::RegisterEntityDescriptorToDictionaries(descriptor,
        EntityDictionaryLocator<CBaseEntity>::Get(),
        EntityDictionaryLocator<CBasePlayerWeapon>::Get());
}
Simply adding another specialization here creates the dictionary. The global variable used to access it is declared just above this function:
// Used at runtime to look up entities of specific types.
inline EntityDictionary<CBaseEntity>* g_EntityDictionary = EntityDictionaryLocator<CBaseEntity>::Get();
inline EntityDictionary<CBasePlayerWeapon>* g_WeaponDictionary = EntityDictionaryLocator<CBasePlayerWeapon>::Get();
The goal is to eventually merge in Enhanced Half-Life's item class refactoring work, at which point a CBaseItem dictionary will be used to handle the giving of items to the player (e.g. using the give console command) to improve the behavior of existing functionality.

Using CBaseEntity instead of edict_t or entvars_t

These changes are intended to standardize the SDK to use CBaseEntity when passing entities around. This reduces the amount of conversions between types and makes it easier to tell which entities are being accessed.

Third party tools that intercept function calls (e.g. AMXMod) will not work properly with these kind of changes, which is why the README states they're not supported. More changes like these are going to be made to improve the quality of the code and to make it easier to work with.

This also means that trying to make a mod with this SDK right now is a bad idea because you'll have to merge in a lot of changes. Once again i must stress that this SDK is not ready to be used for the creation of mods right now. If you do so and you end up with many merge conflicts it'll be up to you to resolve them.

OpenAL-Soft changes

The OpenAL-Soft library is now renamed and installed on both Windows and Linux. This ensures that both platforms are running the same version with the exact same build settings. This also allows the use of extensions added to newer versions.

There are some things i'd like to try using said extensions to improve the accuracy of the new sound system when compared to the original.

---> Continued in next post
Posted 1 year ago2023-02-04 17:32:31 UTC Post #347299
<--- Continued from previous post

Networking system

I've done some more work and research on the networking system and the more i think about it, the more complicated this gets. I've already invested quite a bit of time into this and it's looking like a lot more work is required for this to work as intended.

Experience tells me that continuing to work on this is a waste of time. It would take far too much effort and will never work quite right due to the problems that have to be worked around (needing to add a second loading screen, having to treat the player as unconnected while transferring data, accounting for blocked ports) so i've instead opted for plan B:

Sending the data using a precached file generated on-demand.

The concept is simple: the server generates a file containing all of the data that is to be sent to the client. The file is precached so the server will tell the client it needs the file. The client deletes old files when it initiates a connection (also occurs on map change) ensuring that it will redownload it. The server deletes old files right before generating a new file to avoid wasting disk space.

Both the client and server's download cvars are forced on to ensure that the file is downloaded. For listen server hosts the server's file is used, in all other cases the downloaded file is used.

I've already implemented this but it's not quite ready to be merged since i need to stress test it more. So far i've only tested sending the list of sentences (to test large amounts of data being transferred), sending a bit of structured data (list of objects with text and binary data), and sending the list of precached sounds and playing them using the new sound system (works, but not every sound is played directly by server side code).

The data is generated as a JSON object, with each data set being a keyvalue in that object:
{
"SoundList": [
        "filename1.wav",
        "filename2.wav"
    ]
}
The resulting JSON formatted in minified form (no whitespace, newlines or comments) is compressed using Bzip2 to about 1/5th the original size.

This is a great example of Perfect is the enemy of good. It took me about 6 hours to implement the new system, including a basic prototype based on Source's network string table system before being redesigned to eliminate problems with that design (waste of memory, forcing the data to follow a specific structure which didn't always compress well). I've spent quite a bit more time than that on the other system.

Of course it would've been a lot easier to implement the system as intended if it could've been integrated into the engine (i replaced Quake 1's networking system with GameNetworkingSockets in a roughly equal amount of time) but that's not an option here.

Once this system is finished i can use it to send over existing data and use it to finish implementing work-in-progress features. There is some data that i'd like to send over using this system to eliminate some other problems but that remains to be seen.

New UI

Like i said above continuing to focus on perfection can be a problem. The goal with the UI updates was to focus on picking a UI framework first and then fixing things, but that will probably take a lot of time. So instead the immediate problems will be solved first, and the much larger task of implementing a new UI will come after.

In practice this means having a way to replace HUD images on a per-map basis. The simplest solution is to remap one image name to another and requiring the HUD to refresh its cached image indices at least at the start of the map.

The server only needs to know the name of the replacement file to precache it and send the name to the client. The name can be sent over using the networking system to ensure that it's there before anything needs it.

There's also the need to handle starting the hazard course/boot camp and campaigns using a custom UI. That can be done the same way as Condition Zero, just loading the campaign and map names from a JSON file. It won't look very pretty but it will work.

Project merging status

Current status:
  • Half-Life Updated (up-to-date)
  • Half-Life: Opposing Force Updated (up-to-date)
  • Half-Life: Blue Shift Updated (up-to-date)
  • Half-Life Updated CMake (complete, project has been archived)
  • HLEnhanced (in progress)
  • Enhanced Half-Life (in progress)
  • Half-Life Better Weapons (in progress)
  • Condition Zero: Deleted Scenes SDK (not started)
Some of the work from HLEnhanced, Enhanced Half-Life and Better Weapons has already been merged in now. I've been going through my old notes for HLEnhanced and checking the source code for anything that's useful to merge in.

Enhanced Half-Life doesn't have as much useful stuff, it's mostly experimental and fragile changes not suited for long-term use. The CBaseItem changes and related changes are the biggest set of changes that need merging, and portions of those are already covered by existing changes (e.g. sound replacement).

Better Weapons isn't a lot of work, just using the new weapons dictionary to create the weapons on the client side automatically. The same functionality exists in HLEnhanced so this will complete work for both projects.

Condition Zero Deleted Scenes has only two changes to offer: trigger_changekeyvalue and trigger_sequence. The former is easy to add, the latter relies on sequence files which aren't useful since they still use the old sentences system.

It would be better to focus on implementing scripting support using Angelscript or some other language (e.g. Squirrel) since that's more powerful and avoids relying on a feature that is partially in the engine, so only one feature from that SDK needs merging.

All in all the merging process is almost complete. Several of the projects can be merged in with only a small amount of work.

Remaining work to be done

I know this looks like a constantly changing target, but it's getting closer and closer to completion now. Some goals have changed based on new information and the knowledge that whatever the first version is, it's going to be what people start with so it should be stable and realistic.

For now i need to focus on open pull requests. I know at least one of them involves adding a new feature to the sentence replacement system that involves automatically adding replacements for individual sentences when the entire sentence group is marked as replaced since that's been a source of bugs a couple times.
  • Update changelog to include all changes (partially complete)
  • Write documentation for all new features (partially complete)
  • Networking system (transfer contents of replacement files, precache lists, possibly other immutable data): work in progress
  • HUD image replacement for Opposing Force
  • Versions of Opposing Force-only HUD images that have the scanline effect removed: some preliminary research done, needs more investigation
  • A way to start all three campaigns from the main menu. Probably the same solution as used in Condition Zero Deleted Scenes (menu button that starts a map that opens an SDK-level menu that enables campaign and training selection)
  • Merge in remaining useful functionality from other projects
  • Implement as much of the work scheduled on the issue tracker as possible
  • Review all changes
  • First alpha build
  • Stress test the three campaigns and fix issues that show up (first test done)
  • First beta build
It's mostly a matter of getting the work done now, just implementing stuff, not doing research. It's too soon to test anything at this stage because a lot of stuff isn't finished yet, so please don't report stuff as broken because that's expected at this stage.

I think that about covers it for now, the next status update shouldn't take 2 months but we'll see.
Posted 1 year ago2023-02-09 14:00:19 UTC Post #347317

Map decompiler beta 8

Beta 8 has been released: https://github.com/SamVanheer/HalfLife.UnifiedSdk.MapDecompiler/releases/tag/V0.8.0.0-beta008

Changes:
  • Updated Sledge.Formats to fix Wally being unable to correctly open generated wad files
  • Added --apply-null option to command line
  • Added option to always generate origin brushes for brush entities even if the origin is 0 0 0
  • Added wildcard matching to apply AAATRIGGER to brushes lacking any textures (referred to as "trigger entity classname wildcards")
Posted 1 year ago2023-03-04 16:32:51 UTC Post #347373

Progress on the Unified SDK

A fair amount of progress has been made in the past few weeks.

A new pre-alpha build is available: https://github.com/SamVanheer/halflife-unified-sdk/releases/tag/dev-2023-03-04
Note: pre-alpha builds are not intended for general testing. As the wiki article on software life cycles explains the first versions intended for this kind of testing are alpha builds. Pre-alpha builds are for testing specific features by request to verify that they work as expected based on bug reports and feature requests.

Since this project is still in the midst of development (essentially this is the game engine development part of the project) it's expected that there are bugs and features not working as expected (or at all). Once all required features have been implemented the project can move to the alpha testing stage where verifying the proper behavior is the main goal.

SDK Changes

  • Implemented console print buffering during client startup. This is necessary to allow regular console output to appear since the engine ignores it until the engine has finished initializing itself. Debug output is not ignored which is why that still shows up. The output does appear slightly out-of-order as a result but that's not a problem in most cases.
  • Print OpenAL info during startup (Vendor name, version and renderer [OpenAL Soft]). Note that this information is equivalent to OpenGL info printed in the console. OpenAL uses an API design based on OpenGL so the concepts are the same. That's why it uses the term "renderer" instead of "driver".
  • Set max edicts to 2048 in liblist.gam, log actual max edicts on map start (max edicts is calculated as max_edicts + ((maxplayers - 1) * 15))
  • Included string_view in Platform.h, enabled string view literals everywhere.
  • Added support for custom hull sizes to all entities (probably won't work properly for brush entities). This involved replacing the non-member function UTIL_SetSize with a CBaseEntity::SetSize member function and overriding the entity's bounds during SetModel and SetSize calls. This setting should be used only with entities that use Studio models (files with .mdl extension).
  • Removed unnecessary workaround for custom hull size in monster_generic, now relies on mapper-defined hull sizes for these.
  • Use bit_cast to convert float to int for writing in WRITE_FLOAT.
  • Project info is now always shown in alpha builds, and the meaning of the Work-In-Progress nature and testing expectations is stated more clearly in the on-screen text.
  • Reworked materials.txt parsing to make it easier to change the data source, simplified material storage, fixed parser bugs involving comment handling and correctly parsing the last material entry.
  • Implemented client user messages dispatcher (see below).
  • Initialize all CHud members, store hud elements & sprites in vector (see below).
  • Removed commented out RecCl* function calls. These were part of an old anti-cheat system once used by official Valve games but no longer does anything now other than add visual noise to code.
  • Implemented network data transfer system using files (see below).
    • Some cvars are forced on to make this work. Server operators are warned if a cvar is forced on to alert them to the change.
  • Send sentences list from server to client, don't load sentences.txt on client (see below).
  • Send material list to client, don't load materials.txt on client (see below).
  • Implemented trigger_changekeyvalue.
  • Fixed Barney playing post-disaster use/unuse sentences in training.
  • Studiomdl compiler change: Mark bones that are referenced by attachment as used. This allows bones that have no vertices attached to it to remain in the model if an attachment references it. v_shock.mdl requires this to fix a visual glitch experienced by some players where polygons meant to be invisible were being rendered due to an OpenGL glitch.
  • Fixed grunt not playing MP5 sounds during scripted sequence. Additionally the Male Assassin and allied grunt received the same fix.
  • Fixed func_tank clamping max range to 2048 (affected the Opposing Force Boot Camp live fire exercise).
  • Forced Pitworm animations to interpolate to fix stuttering movement.
  • Added qfont.h back but only for utils use, use header instead of redefining qfont_t type.
  • Fixed SDK mdlviewer program not compiling due to changes made to vector functions.
  • Fixed tripmines not detonating if placed on breakable and a save game is loaded.
  • Reworked game config system to turn sections into separate values with section-specific types, move conditions to section group (see below).
  • Added Git branch nonfunctional-prototype-networking containing the prototype GameNetworkingSockets-based networking system. As the branch name implies this does not work, it has been added to show people how far its development got and which portions were finished and how. There is some useful information to be gained from it, but there are no plans to continue its development since an alternative has been implemented.

C# Changes

  • Fixed c2a5 barrels not flying as high as they are supposed to (see below).
  • Set custom hull size for monster_generic models that use a hard-coded hull.
  • Use Blue Shift grunt alert sentences in ba_power1.
  • Fixed Rosenberg models in Blue Shift maps.
  • Fixed incorrect rendercolor values in some original HL maps. This happened because some original maps incorrectly include commas in rendercolor values; the parsing code was rewritten to allow the use of string_view which made parsing stricter.
  • Fixed grunts using wrong body value in of2a2.
  • Changed Gina model in ba_security2 to use hologram pushcart model.

Asset Changes

  • Added custom hull size keyvalues to fgd.
  • Added trigger_changekeyvalue to fgd.
  • Removed unnecessary polygons from v_shock.
  • Added HD grunt stretcher sequence.
  • Added shared Hologram pushcart model.
  • Updated game configuration files to use new syntax.

Changes in detail

Client user messages dispatcher

This feature reworks client-side user message handling to allow object member functions to be used without requiring a non-member function to forward it using a global variable.

It changes this code:
// In the global scope somewhere
DECLARE_MESSAGE(m_Health, Health)

// In hud element Init function
HOOK_MESSAGE(Health);
To:
g_ClientUserMessages.RegisterHandler("Health", &CHudHealth::MsgFunc_Health, this);
It's probably easier to show how this change affected the codebase: https://github.com/SamVanheer/halflife-unified-sdk/commit/f32cdda028bef544685c16eecf38a861ba329f42

A lot of boilerplate code has been removed. The hooking of message handlers in VGUI1 code has been moved to a better location.

This feature is a re-implementation of a feature that was added in HLEnhanced for the same purpose. That version also supported multiple message handlers for the same handler in order to support multiple huds, which is a feature that will not be added to this project. That is because multiple huds are not useful; only the visual appearance of the hud should be configurable and that's best done another way.

Additionally the return type has been changed from bool to void since it was never checked anyway and was being used inconsistently.

This feature was added now because it allows the client to detect when a user message is being handled so that the networking system can load its data before it is used anywhere.

Hud class changes

All CHud member variables are now initialized to ensure that variables have valid values from the start. This wasn't a problem but helps to avoid problems in the future.

The list of hud elements is now stored in a std::vector and unnecessary null checks have been removed. Enumeration of hud elements is now much simpler:
for (auto hudElement : m_HudList)
{
    hudElement->VidInit();
}
Additionally the VidInit call is now done using the list instead of manually calling each element as is done in the original SDK.

The list of hud sprites is also stored in a std::vector. Previously all three variables representing hud sprites were in separate dynamically allocated arrays:
// the memory for these arrays are allocated in the first call to CHud::VidInit(), when the hud.txt and associated sprites are loaded.
// freed in ~CHud()
HSPRITE* m_rghSprites; /*[HUD_SPRITE_COUNT]*/ // the sprites loaded from hud.txt
Rect* m_rgrcRects;                              /*[HUD_SPRITE_COUNT]*/
char* m_rgszSpriteNames;                      /*[HUD_SPRITE_COUNT][MAX_SPRITE_NAME_LENGTH]*/
These three variables as well as the actual sprite model name are now stored in a structure:
struct HudSprite
{
    eastl::fixed_string<char, MAX_SPRITE_NAME_LENGTH> Name;
    eastl::fixed_string<char, 64> SpriteName;
    HSPRITE Handle = 0;
    Rect Rectangle{0, 0, 0, 0};
};
The sprite list loaded by the engine (m_pSpriteList) is now freed once all sprites have been loaded into the hud sprite list. This fixes a memory leak on Linux that occurs when the engine does a soft restart (due to video setting changes or changes to filesystem configuration like toggling HD models).

There are still memory leaks in the weapon hud since it uses the same engine function but those will be fixed when the hud sprite text files are converted to JSON.

--> Continued in next post
Posted 1 year ago2023-03-04 16:33:07 UTC Post #347374
<-- Continued from previous post

Network data system

The network data system that i mentioned in a previous post has been implemented. It uses JSON files generated on map start downloaded to clients to transfer large sets of immutable data to allow features to reference said data.

That's a fancy way of saying that the contents of materials.txt and sentences.txt are now sent to clients by copying the contents into a JSON file and making the client download and read the file.

Servers can now modify both files without requiring the same changes on the client side. I still wouldn't recommend doing that, and it won't be necessary once other features have been implemented, but this change was needed to provide the necessary foundation for said other features to be built on.

To make this system work 4 cvars are forced to the value 1:
  • sv_allowdownload
  • sv_send_resources
  • sv_allow_dlfile
  • cl_allowdownload
If the server cvars are detected to be turned off they will be forced on along with a warning to make sure server operators are aware of why it happened (because these cvars are marked to not log changes normally).

The file is always called networkdata/data.json. Using the same name simplifies things and takes advantage of the engine's automatic change detection to regenerate the BZip2 compressed version networkdata/data.json.ztmp which helps to avoid polluting dedicated servers with many old compressed versions. The average compression ratio is about 79% meaning a file with size 100KiB is compressed to a file that's about 21KiB.

The file is deleted by the server prior to generating a new file. The client will also delete both local and downloaded copies to ensure that it can receive a fresh copy. The client will not delete the local file if it is connected to a local server (i.e. singleplayer or listen server).

When running a local server no download is necessary so players are unlikely to notice much impact other than time spent generating and parsing the file.

Two limitations currently exist:
1. You cannot connect to a dedicated server running from the same game installation as the client. This should never be a problem because the dedicated server tool distributed with the client does not work anymore, and dedicated servers should always be installed separately through SteamCMD.
2. If multiple servers share a FastDL server and transfer the generated file to it they will conflict and use the wrong file. If this does become a problem then a possible solution is to store the file in a subdirectory named after the server IP and port: 1.2.3.4_port. This would make the filename unique for each server. The client has the server's IP address so this should work, but whether this will actually work or not remains to be seen.

The file contents are minified JSON and includes metadata to detect when clients connect to servers running a different version.

Average file size is currently about 99-100 KiB. Since features depending on this system are in development this number will change in both directions.

The engine allows a maximum uncompressed file size of 10 MiB, controlled by the sv_filetransfermaxsize cvar. File size shouldn't be a problem but if it does the first thing to do is investigate replacing JSON with a message format like Protobuf.

My estimates show that for the current data, which are lists of strings, using Protobuf will reduce file size by somewhere around 3000 bytes. Once large amounts of structured data are sent over Protobuf will become the more efficient method because of how it works.

Basically JSON contains both data and the message format in text form. Protobuf contains both data and the message format in binary form, using a much more efficient representation. Where a JSON key can be several bytes a Protobuf identifier can be as small as 1 byte and pretty much never gets bigger than that due to lack of complexity in the data being sent.

It's a good first step to optimizing file size, but file size isn't really an issue here. The biggest problem with this system is the download speed. GoldSource's networking system is primitive and slow and can't be made faster. Its default settings send 512 bytes of file data at a time with a minimum of 256 bytes and maximum of 1024 bytes, controlled by the cl_dlmax cvar.

Reducing file size does speed up the download but having to download the file at all will always make it slow enough to be annoying. Unfortunately the file has to be downloaded every time because the data within can change every time it's generated.

The GameNetworkingSockets-based networking system i discussed previously aimed to avoid this problem, but unfortunately it has its own problems that are harder to solve.

That implementation will not be developed further, but its code can be found in this branch: https://github.com/SamVanheer/halflife-unified-sdk/tree/nonfunctional-prototype-networking

Note that Github Actions failed to build this because that branch references older vcpkg dependencies. A limitation inherent in vcpkg's design is responsible for this.

materials.txt and sentences.txt changes

The materials.txt parser now properly handles comments. Previously a line starting with / was treated as a comment instead of any occurence of // as expected.
It also handles the last line properly. Previously if the last material did not end with a newline it was ignored, which is why the last material in HL's file is listed twice.

Last but not least the material limit of 512 has been changed to unlimited.

Both materials.txt and sentences.txt are now sent to the client. This ensures that the client works with the same data as the client.

This is the first step to allowing map-specific versions of both files which will provide `trigger_sequence`'s map-specific sentences support. Other changes are planned to make the most of this new functionality, as well as apply it to other configuration files.

Game configuration system changes

The game configuration system has been reworked to simplify the structure and avoid potential problems when they are updated due to some overly verbose syntax.

Previously a configuration file contained a list of sections like this:
{
    "Sections": [
        {
            "Name": "GlobalModelReplacement",
            "FileName": "cfg/Op4ModelReplacement.json"
        },
        {
            "Name": "GlobalSentenceReplacement",
            "FileName": "cfg/Op4SentenceReplacement.json"
        },
        {
            "Name": "GlobalSoundReplacement",
            "FileName": "cfg/Op4SoundReplacement.json"
        },
        {
            "Name": "HudColor",
            "Color": "0 160 0"
        },
        {
            "Name": "SuitLightType",
            "Type": "nightvision"
        }
    ]
}
This has been changed to this:
{
    "SectionGroups": [
        {
            "Sections": {
                "GlobalModelReplacement": {
                    "FileName": "cfg/Op4ModelReplacement.json"
                },
                "GlobalSentenceReplacement": {
                    "FileName": "cfg/Op4SentenceReplacement.json"
                },
                "GlobalSoundReplacement": {
                    "FileName": "cfg/Op4SoundReplacement.json"
                },
                "HudColor": "0 160 0",
                "SuitLightType": "nightvision"
            }
        }
    ]
}
There is now a list of section groups. A section group pairs a set of sections with an optional Condition key, which previously was available on every section. The section list has been replaced with an object; section names are the key. Section values can now be any JSON object type: object, array, string, etc. This simplifies the trivial sections quite a bit.

Overall these changes reduce the size of config files and make more complex files easier to work with. Since conditionally included sections are now managed by the group condition having multiple sections conditionally included is easier to reason about.

These changes also make it easier to use the JSON Schema for config files in schema-based JSON editors:
User posted image
User posted image
The previous approach confused these editors too much.

--> Continued in next post
Posted 1 year ago2023-03-04 16:33:15 UTC Post #347375
<-- Continued from previous post

c2a5 barrels fix

This has been requested for some time now.

The cause of this problem was the use of trigger_push to add velocity in a way that was framerate-dependent. Instead of applying the set velocity in one go it applied it over the course of about a second. The higher the framerate is the lower the applied velocity becomes.

Thanks Uncle Mike for pointing to the cause and fix, Streit and rbar1um43 for providing a link to this information.

In development: hud sprite replacement

This is a simple test to see if replacing hud sprites at runtime is possible. This works on the same principle as the other replacement systems:
{
    "number_0": "number_0_hl",
    "number_1": "number_1_hl",
    "number_2": "number_2_hl",
    "number_3": "number_3_hl",
    "number_4": "number_4_hl",
    "number_5": "number_5_hl",
    "number_6": "number_6_hl",
    "number_7": "number_7_hl",
    "number_8": "number_8_hl",
    "number_9": "number_9_hl"
}
The default sprite is substituted for its replacement. In this case the default hud uses Opposing Force sprites so it was substituted with the Half-Life versions.

This is only an initial test to see if it can be done. The actual feature will be developed to be more flexible than this. It isn't available in the current dev build.

Project merging status

Current status:
  • Half-Life Updated (up-to-date)
  • Half-Life: Opposing Force Updated (up-to-date)
  • Half-Life: Blue Shift Updated (up-to-date)
  • Half-Life Updated CMake (complete, project has been archived)
  • HLEnhanced (in progress)
  • Enhanced Half-Life (in progress)
  • Half-Life Better Weapons (in progress)
  • Condition Zero: Deleted Scenes SDK (done)
Some HLEnhanced features have been merged by way of equivalent features being implemented.

Condition Zero Deleted Scenes merging work is done. trigger_changekeyvalue has been implemented.

trigger_sequence 's features will be implemented in another manner. This largely involves map-specific sentences.txt files, the other features are more of a nice-to-have thing and are not as important.

Remaining work to be done

  • Merge pull requests (done)
  • Update changelog to include all changes (partially complete)
  • Write documentation for all new features (partially complete)
  • Networking system (transfer contents of replacement files, precache lists, possibly other immutable data): Networking system is implemented, new and existing features are being updated to use it
  • HUD image replacement for Opposing Force: basic hud sprite name replacement test done and functional; more complete implementation will be added to implement this feature
  • Versions of Opposing Force-only HUD images that have the scanline effect removed: some preliminary research done, needs more investigation
  • A way to start all three campaigns from the main menu. Probably the same solution as used in Condition Zero Deleted Scenes (menu button that starts a map that opens an SDK-level menu that enables campaign and training selection)
  • Merge in remaining useful functionality from other projects
  • Implement as much of the work scheduled on the issue tracker as possible
  • Review all changes
  • First alpha build
  • Stress test the three campaigns and fix issues that show up (first test done)
  • First beta build
I am currently working to update features to take advantage of the networking system, so the next progress report will go into more details on that.

Much of this work was done by malortie, whose help cannot be understated. Many thanks to him for his continued work on this project.

Until next time.
Posted 1 year ago2023-03-21 14:02:23 UTC Post #347406

Progress on the Unified SDK

A lot of progress has been made since a lot of smaller things have been done.

A new pre-alpha build is available:
Note: pre-alpha builds are not intended for general testing. As the wiki article on software life cycles explains the first versions intended for this kind of testing are alpha builds. Pre-alpha builds are for testing specific features by request to verify that they work as expected based on bug reports and feature requests.

Since this project is still in the midst of development (essentially this is the game engine development part of the project) it's expected that there are bugs and features not working as expected (or at all). Once all required features have been implemented the project can move to the alpha testing stage where verifying the proper behavior is the main goal.

SDK Changes

  • The global sound replacement dictionary is now sent to the client
  • Reworked global model, sound and sentence replacement to allow multiple files and to allow removing files listed in config files that were checked prior to the current one
  • materials.txt, sentences.txt and skill.json are now specified through the server config file instead of using hard-coded filenames. Note that this may be changed to solve problems related to servers running custom versions of these files since it messes up custom content when downloaded and loaded locally
  • Removed skill sk_reload command (restart the map to see changes)
  • Changed maximum material texture name length to match wad and bsp limit (13->16)
  • Added snow footstep and impact sounds (from Opposing Force Updated, was missing)
  • Converted the material system and hud sprite config files to JSON
  • Removed obsolete alternate entity classnames for weapons
  • Changed weapon precaching to use weapon dictionary, client side code now creates all weapons using this dictionary instead of requiring globals to be added manually
  • Moved weapon class declarations out of weapons.h since they should not be accessed directly
  • Reworked RPG state networking to eliminate use of special case. Nearly all references to WEAPON_* constants are contained in their individual weapons now
  • Disabled MSVC warnings in CMakeLists.txt instead of Platform.h to ensure they are disabled project-wide
  • Removed unused variables and C-style casts
  • Don't assume hud sprites have sequential indices when loaded from hud configs
  • Fixed Penguin grenade not clearing weapon bit correctly
  • Improved user feedback when using JSON console commands
  • Reworked precache diagnostics logging to show the number of precaches known to the server dll when server activation is about to complete
  • Send all project info using network data system, use it to verify mod compatibility, rename cl_showprojectinfo to cl_projectinfo_show
  • Don't rely on cached max entity count on client side (fixes edge case where sounds played on first frame don't play because entity count is 0)
  • Link user messages on server startup instead of after map spawn (fixes edge case where messages are not registered yet when used)
  • Moved print buffering setup to before client init
  • Use separate loggers for sound cache & sentence playback
  • Use engine sound system rules for channel reuse before allocating new channel
  • Set OpenAL reference distance to 0 to match the engine's calculations
  • Disabled OpenAL Doppler effect feature
  • Play regular sounds using OpenAL instead of engine sound system
  • Implemented A3D functionality using HRTF (note: experimental)
  • Make sounds with attenuation 0 play at player's position (should help deal with ambient_generic Play Everywhere sounds still using spatialization; needs more testing)
  • Store sound filenames as lowercase to ensure extension-based lookup works properly (some maps use uppercase extensions)
  • Removed unnecessary global variables in changelevel code
  • Removed unnecessary macros used to mark variables as global and const
  • Fixed typo in spectator overview code
  • Removed redundant weapon slot index data specified by iItemSlot() method, removed duplicate constants for max weapon slots and ammo types
  • Converted as much code as possible to use Vector instead of float arrays/pointers, replaced older vector math functions with Vector operators and helpers
  • Consolidated math functions in mathlib.h/.cpp
  • Use local AngleVectors functions instead of engine function (more efficient, compiler can optimize the calls since it can see the implementation)
  • Merged server and client versions of weapons code when possible
  • Merged CBasePlayerItem and CBasePlayerWeapon (items only work properly when they are weapons anyway, some code assumes all items are weapons and no classes inherit from CBasePlayerItem directly)
  • Moved CWeaponBox to its own file (server only, client doesn't need it, only player accesses the type directly)
  • Reworked ammo type definitions so the types are explicitly defined instead of implicitly through weapons referencing them. The ammo type definition now includes the maximum capacity, code that required the capacity to be passed in now queries the ammo type system for this information instead. The list of ammo types is sent to the client as well
  • Converted functions used to play sounds to be CBaseEntity class members so the edict_t instance can be inferred from the entity, pass CBaseEntity into the sound system and convert to edict only at the very end
  • Implemented per-entity model, sound and sentence replacement (Work In Progress, entities created through code need to inherit these)
  • Reworked the body list to use CBaseEntity and moved it to its own file (body list is used for multiplayer corpses strewn about)
  • Removed EOFFSET type, updated remaining uses to make use of better alternatives
  • Moved spawnpoint logic to its own file and reworked it to use CBaseEntity
  • Replaced FClassnameIs with class member function
  • Reworked code to minimize use of older helper functions and to prefer the use of CBaseEntity when possible
  • Reworked weapon info to be networked to client through the new networking system, WeaponList network message removed
  • HUD now uses weapon and ammo info received from the client
  • Split up gamerules.h so singleplayer & multiplayer gamerules class declarations aren't included (should never be accessed directly)
  • Renamed gamerules filenames to match their class names
  • Moved saverestore implementation to its own file
  • Grouped CTF classes together in the same file (small classes, cuts down on compile time a bit)
  • Consolidate entities related to level changes in the same file, removed wrapper function
  • Use per-entity sentence replacement to handle Otis, Drill Sergeant, Recruit and Rosenberg sentence changes, removed code duplication in these classes
  • Fixed Rosenberg voice pitch being too high
  • Converted much of the entity and gamerules documentation to use Doxygen, remove redundant, obsolete and incorrect documentation

C# Changes

  • Reworked Skill2Json to preserve comments when possible
  • Added Materials2Json tool (with special case to ignore a bad material entry in the HL materials.txt file)
  • Weapons are renamed to use their primary classname if they use an old pre-release name (e.g. weapon_mp5 => weapon_9mmAR)
  • Added Hud2Json
  • Fixed typo in music filename map (Prospero06.mp3 => Prospero05.mp3)

Asset Changes

  • Updated game config files to use lists of replacement files
  • Added cfg file that adds default game data files
  • Changed material texture names to use full names instead of first 12 characters
  • Fixed material texture names that used misspelled names or that included ignored prefixes
  • Removed Op4 snow step sound replacements in sound replacement file (unnecessary, used for all games anyway)
  • Removed duplicate material entries
  • Converted materials.txt to JSON
  • Converted HUD sprite configs to JSON
  • Added script to convert all HUD sprite configs in a directory to JSON
  • Added options to control HRTF to Multiplayer Advanced dialog
  • Added missing Start On spawnflag to func_guntarget
  • Added replacement file keyvalues to fgd, added test map with test files

Replacement file lists

Game configurations now accept a list of files for replacements. The syntax is as follows:
"Sentences": {
    "ResetList": false,
    "FileNames": ["sound/sentences.txt"]
}
If ResetList is true then any files specified by config files loaded before this one are ignored.

Global sentence replacement is unfinished; it requires conversion to JSON and a more explicit way to define sentence groups to solve problems with how sentences and groups are overridden. That can't be done until the sound system is finished.

Materials files now use the full texture name (with prefixes stripped). Along with previous changes this allows for an unlimited number of materials matching specific textures.

HUD sprite configs don't include the resolution since it isn't used anymore.

Sound system changes

All sounds are now played using the new sound system. This works pretty well overall aside from certain sounds that are pitch shifted. The engine handles pitch shifting by stretching or chopping up the samples, whereas OpenAL uses frequency modulation. I'll have to implement the same pitch shifting algorithm to make it sound good.

I've also implemented Head-related transfer function (HRTF) to emulate the original engine's A3D functionality. This is experimental and has some problems, largely performance issues that occur when a lot of sounds are played.

This video shows the new sound system and some of the problems with it:
--> Continued in next post
Posted 1 year ago2023-03-21 14:02:47 UTC Post #347407
<-- Continued from previous post

Per-entity model, sound and sentence replacement

It is now possible to use model, sound and sentence replacement config files on a per-entity basis. While it doesn't yet apply to entities created programmatically it does work with editor-placed ones:
The scientist is using sentence replacement to play different sentence groups and uses the custom model keyvalue to look like a zombie. The zombie is using model replacement to look like a scientist and uses sound replacement to sound like a scientist.

Sentence replacement is now also used for Otis, the Drill Sergeant, Recruit and Rosenberg to eliminate code duplication that was needed to play the right sentence before.

It is possible to replace these files using trigger_changekeyvalue, though i wouldn't recommend it because the models and sounds need to be precached to work.

Weapon and ammo changes

Weapon and ammo info is now sent to the client using the new networking system, ammo is defined separately to avoid cases where multiple weapons can define the same ammo type with different properties, in which case the properties used depend on precache order.

Overall these changes simplify the weapons and ammo code.

Project merging status

Current status:
  • Half-Life Updated (up-to-date)
  • Half-Life: Opposing Force Updated (up-to-date)
  • Half-Life: Blue Shift Updated (up-to-date)
  • Half-Life Updated CMake (done, project has been archived)
  • HLEnhanced (in progress)
  • Enhanced Half-Life (in progress)
  • Half-Life Better Weapons (done)
  • Condition Zero: Deleted Scenes SDK (done)
The changes made to the weapons code allowed the Better Weapons project to be merged in. The client gets its list of weapon classnames from the weapon dictionary so as long as you include the weapon source file in the client project (done for you since you include these in game_shared.cmake) it'll set it up.

You no longer need to manually precache each weapon, the dictionary is also used to do that automatically.

A lot of these changes involve the merging of features from HLEnhanced and Enhanced Half-Life so both of those projects are merging in quite quickly. I'm working to merge in EHL's features first now which shouldn't take much longer, then i'll start checking HLE.

Several issues on the issue tracker were closed as well because of this merging work.

Remaining work to be done

  • Update changelog to include all changes (partially complete)
  • Write documentation for all new features (partially complete)
  • Networking system (other immutable data): Networking system is implemented, new and existing features are being updated to use it
  • HUD image replacement for Opposing Force: basic hud sprite name replacement test done and functional; more complete implementation will be added to implement this feature
  • Versions of Opposing Force-only HUD images that have the scanline effect removed: some preliminary research done, needs more investigation
  • A way to start all three campaigns from the main menu. Probably the same solution as used in Condition Zero Deleted Scenes (menu button that starts a map that opens an SDK-level menu that enables campaign and training selection)
  • Merge in remaining useful functionality from other projects
  • Implement as much of the work scheduled on the issue tracker as possible
  • Review all changes
  • First alpha build
  • Stress test the three campaigns and fix issues that show up (first test done)
  • First beta build
I expect a lot more of these smaller changes to be done now so the progress updates will be long but full of small stuff.

Thanks to hammermaps for contributing code cleanup improvements, that's very useful.

Until next time.
Posted 1 year ago2023-03-21 15:20:23 UTC Post #347408

Unified SDK Pre-alpha build

The previous post didn't include the link to the new pre-alpha build: https://github.com/SamVanheer/halflife-unified-sdk/releases/tag/dev-2023-03-21
Note: pre-alpha builds are not intended for general testing. As the wiki article on software life cycles explains the first versions intended for this kind of testing are alpha builds. Pre-alpha builds are for testing specific features by request to verify that they work as expected based on bug reports and feature requests.

Since this project is still in the midst of development (essentially this is the game engine development part of the project) it's expected that there are bugs and features not working as expected (or at all). Once all required features have been implemented the project can move to the alpha testing stage where verifying the proper behavior is the main goal.
Posted 11 months ago2023-04-06 19:54:08 UTC Post #347427

Progress on the Unified SDK

SDK Changes

  • Cleaned up some redundant constants, added missing constants
  • Fixed spray logo using the wrong decal after a save game load when not using a custom spray (i.e. if you use the default small Lambda spray, not the custom one defined in the Multiplayer options page)
  • Fixed ammo pickup sound playing when picking up a weapon for the first time (bug was introduced in a HL Updated change)
  • Fixed cycler_weapon trying to reload
  • Replaced V_min and V_max with std::min, std::max & std::clamp
  • Centralize server dll functions used by engine to access server dll
  • Added SV_SaveGameComment function: used by the engine to fill in the text shown in the save game description in the Load Game dialog (game uses localized text for this)
  • sv_spamdelay is now ignored in singleplayer (it controls the time before players are allowed to chat again, to limit the amount of chat text allowed at any given time)
  • Reworked the HUD weapon list to allow an unlimited number of weapons in each weapon category (previously limited to 7 weapons total in Opposing Force, 5 in HL)
  • Weapon categories above 5 are only drawn if there are weapons in that category or a category with a greater number (makes HUD look like HL when no Op4 weapons are equipped)
  • Added diagnostic output warning if there are too many multi_manager targets
  • Renamed TakeHealth to GiveHealth because positive amounts give health
  • Refactored game logic to use CBaseEntity wherever possible to make it more consistent and eliminate some older helper functionality
  • Fixed monstermaker with infinite children not spawning an infinite number (will spawn 4,294,967,293 entities and then stops. Ridiculous, i know)
  • Added a way to pass arbitrary keyvalues to monstermaker children (see below)
  • Made sure entities notify their owner if they are killtargeted so they can keep track of how many active children there are
  • Moved chat input position to 2 lines above the chat text area (see below)
  • Fixed entities not removing child entities created by them (see below)
  • Fixed Alien Slave beams staying forever if they exist during a level change
  • Fixed cycler_wreckage storing time value in int instead of float
  • Replaced ´DECLARE_COMMAND´ and HOOK_COMMAND with console command system
  • Install singleplayer gamerules earlier when loading save games so entity initialization and precaching logic can use it
  • Made trigger_changelevel work in multiplayer (non-persistent level changes only)
    • Added the option to use non-persistent level changes in singleplayer
  • Removed monster_vortigaunt classname (never used, breaks Alien Slave code since it searches by classname)
  • Removed game.cfg execution, added safety checks to prevent server state corruption if multiple map change commands occur in the same frame
  • Only allow NPCs to drop items, never respawn dropped items
  • Fixed Gauss gun sometimes setting uranium ammo to -1
  • Fixed Glock not playing empty sound when using secondary attack
  • Fixed player weapons still receiving input when starting to use a func_tank
  • Fixed weapons marked as "limit in world" (e.g. Hand Grenade) respawning at the wrong time if the server is near the entity limit
  • Removed duplicated ammo variables used in prediction code
  • Moved weapon-specific variables from CBaseEntity to their respective classes
  • Store player suit flag in separate variable, removed unused WEAPON_CHAINGUN constant (effectively frees up 2 weapon slots for use)
  • Reworked gib spawning to avoid use of stackalloc
  • Prevent entity classnames from being changed by level designers using entities that can change arbitrary keyvalues
  • Added assert to warn about classnames with uppercase letters, renamed all entities to use lowercase letters (weapon_9mmAR, ammo_9mmAR, ammo_ARgrenades)
  • Changed schedule debug logic (spark effect above NPC's head) to be controlled by sv_schedule_debug cvar
  • Added diagnostic logging to show time required to load server config files
  • Reset sky name to desert to ensure sky name is consistent even after loading other maps that have other sky names (e.g. crossfire does not specify a sky name and uses the last used sky)
  • Implemented strongly typed entity handles
    • Converted existing uses of EHANDLE to use new handle features
    • Reworked func_train and env_spritetrain to use EHANDLE instead of entvars_t* to track the next stop target
    • Use EHANDLE instead of edict_t* to store tripmine owner
  • Reduced project info HUD size
  • Added debug info HUD (see below)
  • Fixed user interface coordinates and sizes being incorrectly adjusted for resolution (caused spectator interface buttons to overlap)
  • Refactored weapons, ammo and pickup items (excluding Op4 CTF items) to use a shared base class
    • Added new features to items (see below)
    • The give and impulse 101 commands now use the item dictionary
  • Reworked func_healthcharger and func_recharge (HEV charger) to use shared base class
    • Added new features to chargers (see below)
  • Added the option to strip the HEV suit and long jump module from players using player_weaponstrip
  • Reworked item_generic to avoid the possibility of the wrong sequence being used if the game saves right between the entity spawning and finishing its initialization
  • Disabled HEV sounds for item_helmet & item_armorvest
  • Removed world_items (obsolete, replaced by individual entities)
  • Removed item_security and unused item types not used by game code
  • Removed trip_beam (debug only, never used)
  • Implemented entities:
    • player_sethealth
    • player_setsuitlighttype
    • player_hassuit
    • logic_setcvar
    • ammo_generic: Gives ammo of a specified ammo type (combine with trigger_changekeyvalue to dynamically change ammo type)
    • ammo_all: Gives all ammo types. Configurable amount
  • Implemented cheats:
    • ent_remove: Remove the first entity matching the given targetname or classname
    • ent_remove_all: Remove all entities matching the given targetname or classname
    • ent_setname: Sets the name of the entity you're looking at or the entity with the given targetname or classname to the given targetname
    • ent_show_origin: Shows the origin of the first entity matching the given targetname or classname by creating a temporary laser dot sprite at its location (note: brush entity origins may be 0 0 0 if they do not have an origin brush)
    • ent_show_center: Shows the center as defined by the first entity matching the given targetname or classname by creating a temporary laser dot sprite at its location
    • ent_show_bbox: Shows the bounding box of the first entity matching the given targetname or classname by drawing blue particle lines (note: the game expands the bounding box by 1 unit in all directions, so the box appears to be larger than the entity)
    • cheat_god: Toggles god mode (even in multiplayer)
    • cheat_notarget: Toggles notarget (even in multiplayer)
    • cheat_noclip: Toggles noclip (even in multiplayer)
    • cheat_givemagazine: Gives a single magazine's worth of ammo for the current weapon to the player. An optional parameter indicates the weapon's attack mode index to allow giving secondary ammo (i.e. MP5 grenades are given with cheat_givemagazine 1)
    • ent_create: Creates an entity at the position you're looking at, passing along additional keyvalues if provided
    • npc_create: Creates an entity at the position you're looking for and adjusts its height to place its bounding box on the ground. If the entity is stuck in an object or can't fall to the ground it is removed
  • Cheats with the cheat_ prefix are autocompleted by the console now
  • Reworked save/restore system to use a datamap to store data
    • Decoupled engine save/restore data from the game's data to allow the game's version to be freely changed
    • Reworked save/restore system to use serializers for each type
    • Fixed arrays of function pointers not saving and restoring correctly
    • Added function table to datamap to store the list of entity functions used as Think/Use/Touch/Blocked in the server dll instead of relying on the engine to look them up (eliminates use of platform-specific functionality)
  • Merged variables used to store the name of an entity's master
  • Moved m_hActivator to CBaseEntity and removed a hack added post-release
  • Reworked Op4 CTF stats menu to use safe format strings and to use a fallback string if the file providing it is missing or contains a bad format strings

Asset Changes

  • Matched changes to the game's code to the fgd
  • Cleaned up master keyvalue definition and made it use the correct data type so editors can auto-complete entity names
  • Removed .wav extension from a couple Op4 sentences
  • Added maps to test new features
  • Added zoo_items map to enable easy testing of all items
  • Added configuration files defining default keyboard bindings & descriptions, changed description for slot 10
  • Removed non-existent PVS flag from monstermaker
  • Updated func_train & env_spritetrain to use new sound name functionality

C# Changes

  • Removed dmdelay keyvalue from charger entities (was always ignored, removed to avoid confusion)
  • Reworked KeyValueMatcher to take maps directory as argument instead of as option, allow multiple directories
  • Reworked KeyValueMatcher to only print a map name if at least one match is found by default and print the number of matches
--> Continued in next post
Posted 11 months ago2023-04-06 19:54:33 UTC Post #347428
<-- Continued from previous post

Debug HUD

A new HUD element has been added that shows some debug info:
User posted image
The current map, player origin and angles are shown. This makes it easier to diagnose issues by including this information in screenshots and helps to note down specific locations in maps.

Enabled with the cl_debuginfo_show cvar.

Chat input position

Chat input is now shown directly above the chat text area instead of in the top-left corner of the screen:
User posted image

New item and charger features

Weapons, ammo and all items other than Opposing Force CTF items now have the following features:
  • Custom respawn delay (works in singleplayer as well)
  • Option to stay visible during respawn
  • Flash on respawn (uses muzzleflash effect)
  • Can disable the pickup sound
  • Can set item to float instead of falling to the ground (items also no longer fall through entities in some cases, though they can still phase through entities if they clip into them)
  • Can trigger entities on spawn and despawn
Ammo entities additionally have these features:
  • Can specify the amount of ammo to give (-1 == maximum)
  • ammo_generic: Can change the ammo type given using trigger_changekeyvalue
  • ammo_all: Give specified amount of all ammo types
Weapon entities additionally have these features:
  • Can specify the amount of ammo in newly spawned weapons
Health kits, battery and guard helmet and vest allow specifying custom capacities as well.

The Health and HEV chargers now have the following features:
  • Can trigger entities on recharge and empty
  • Can change the total capacity (-1 == unlimited)
  • Custom respawn delay
  • Amount of juice to charge per us
This video shows many of these new features:

Monstermaker keyvalue passthrough and proper entity cleanup

monstermaker now passes up to 64 keyvalues to the entities it spawns. This is done by prefixing keyvalues with #:
#TriggerCondition 4
#TriggerTarget death
Killtargeting entities now also correctly cleans up entities spawned by them (if the entity is intended to be killtargeted, not all entities do this). This fixes monstermaker breaking if the spawned entities are killtargeted and certain NPCs and entities leaving effects behind.

Project merging status

Current status:
  • Half-Life Updated (up-to-date)
  • Half-Life: Opposing Force Updated (up-to-date)
  • Half-Life: Blue Shift Updated (up-to-date)
  • Half-Life Updated CMake (done, project has been archived)
  • HLEnhanced (in progress)
  • Enhanced Half-Life (almost done)
  • Half-Life Better Weapons (done)
  • Condition Zero: Deleted Scenes SDK (done)
Enhanced Half-Life's features are almost all merged in. There are a handful of things left to check and then i'll consider that completed.

HLEnhanced features are also being merged in quite quickly and should soon be complete as well.

Many of the changes listed came from these two projects which is why it's going so quickly.

Remaining work to be done

  • Update changelog to include all changes (partially complete)
  • Write documentation for all new features (partially complete)
  • Networking system (other immutable data): Networking system is implemented, new and existing features are being updated to use it
  • HUD image replacement for Opposing Force: basic hud sprite name replacement test done and functional; more complete implementation will be added to implement this feature
  • Versions of Opposing Force-only HUD images that have the scanline effect removed: in progress
  • A way to start all three campaigns from the main menu. Probably the same solution as used in Condition Zero Deleted Scenes (menu button that starts a map that opens an SDK-level menu that enables campaign and training selection)
  • Merge in remaining useful functionality from other projects (in progress)
  • Implement as much of the work scheduled on the issue tracker as possible
  • Review all changes
  • First alpha build
  • Stress test the three campaigns and fix issues that show up (first test done)
  • First beta build
There's a fair amount of work left to be done that's not listed publicly, but i expect to get through that pretty quickly. I've been going through the Github issues, the Git commit histories for EHL and HLE, my old notes and todo lists to see what's left and i'm quickly running out of all of that stuff.

There are some stability issues resulting from changes made that are being fixed as i encounter them. Unfortunately it's not always possible to fix them completely, for example a bug in the engine allows multiple maps to be loaded at the same time and it corrupts the engine and the game's state. I've added code to detect this and kick the player back to the main menu (using another hack in the process) but i'd prefer a proper fix in the engine for that kind of thing.

Until all of the planned features are in place some temporary hacks are needed. I'm working to get this all done ASAP but i can't say how long it'll take.

I can say that Opposing Force and Blue Shift require only 2 more features to allow them to be experienced just like the original: HUD sprite replacement and setting the MP5's default ammo to 50 (is 25 in HL) is all that's left. There is still the need to enable campaign selection but for now the console allows direct control.

I'm leaving all sound system issues until after i've taken care of all of the merging and feature implementation work since those issues are a bit more complicated, and strictly speaking only sentence playback is needed which doesn't require the features that are a bit buggy at the moment. I don't think it'll be a big problem, but i'd prefer to get a bunch of easy stuff out of the way first.

The next update will probably be similar to this one in that it will have loads of smaller changes.

That's all for now.
Posted 11 months ago2023-04-22 16:29:06 UTC Post #347472

Progress on the Unified SDK

SDK Changes

  • Optimized build times a bit (possibly up to half the build time depending on the machine)
  • Fixed game_text messages not resetting time value correctly causing messages that reuse channels to use the wrong start time
  • Fixed sounds set to play everywhere (e.g. scripted_sentence) still setting an origin and having directional audio (a previous change intended to fix this bug as it occurred in the original engine, this accounts for an edge case that was missed)
  • Fixed sounds that have bad loop length data passing this on to OpenAL (now works like the original sound system does)
  • Fixed Apache and Osprey not playing rotor sounds correctly in multiplayer (they use singleplayer-only pitch modulation logic that depends on the local player entity)
  • Reworked func_breakable and func_pushable to require level designers to specify the classname of the entity to spawn on break. This allows both entities to spawn any item in the game aside from CTF items without needing code changes to support newly added items
  • Added natvis definition for EntityHandle to make it easier to see which entity a handle points to
  • Overhauled NPC schedule list code to use a simpler solution
  • Reworked client side message parsing to use a class instead of global variables (saves a little memory and eliminates edge cases from leftover parsing state and missing parse initialization)
  • Marked engine functions as deprecated if they do nothing, nothing useful or if a local function does the same (will produce compiler warning if you try to use such a function)
  • Streamlined usages of edict_t*, entvars_t* and CBaseEntity* to make code more consistent (ongoing effort)
  • Updated most uses of UTIL_GetLocalPlayer to work in multiplayer (ongoing effort)
  • Use more consistent method to check if the game is multiplayer or singleplayer and whether it's a teamplay gamemode
  • Implemented entities:
    • player_hasweapon
    • logic_isskill: fires a target matching the current skill level
    • logic_setskill: Changes the skill level setting. Due to how the skill system works some changes will only take effect after a map change. This entity is the equivalent of Quake 1's trigger_setskill but is a point entity that needs to be triggered. Its intended purpose is to be used in a campaign selection map
    • logic_setskillvar: Sets a specific skill variable to a value. Intended for multiplayer only because skill variables are not saved and restored, and are reset on map load based on specified skill config files
  • Fixed M249 using wrong spread in singleplayer when standing still and ducking (values were swapped)
  • Fixed shotgun starting idle animations too quickly after exhausting all ammo using primary attack, causing the shoot animation to cut off
  • Fixed RPG not playing empty sound when attempting to fire with no ammo left
  • Reworked how the player's reserve ammo and each weapon's magazine is accessed to centralize the logic
  • Fixed Human Grunts dropping weapons again if the game is saved and loaded while the grunt is dying
  • Fixed Health Charger recharge time not using the correct value in Co-op when using mp_coopweprespawn 1
  • Removed cycler_weapon (not very useful, tends to cause problems when weapons code is changed)
  • Made impulse 101 give all weapons using the weapons dictionary, give the suit without playing the login sound and give max ammo without creating ammo entities
  • Disabled the ability to pick up items when in observer/spectator mode
  • Fixed Capture The Flag items crashing on respawn due to faulty respawn point detection logic (error in recreation, not in Opposing Force itself)
  • Removed conditional evaluation from skill config files. This is now handled by map configuration files only to centralize the configuration logic
  • Implemented support for constraining (treat as integer, clamp to minimum and/or maximum value) and networking skill variables to clients
  • Reworked the weapons code to use skill variables to control multiplayer behavior (allows alternate weapon behavior to be controlled in any game mode on a case-by-case basis)
  • Reworked gamerules to rely more on configurable behavior instead of hard-coding gamemode-specific rules (e.g. item respawn rules are now configured in skill files, not in the gamemode-specific gamerules class)
  • Removed mapchange cfg file (superfluous), restricted the server configuration file to the Echo and Command sections, moved default config file names to hardcoded list (see below)
  • Added default map config file used if the map doesn't have a file of its own
  • Made the Echo config section log as info instead of debug/trace
  • Reworked game mode selection (see below)
  • Reworked the map cycle to use JSON (see below)
  • Implemented Spawn Inventory feature (see below)
  • Implemented Persistent Inventory feature in co-op (see below)
  • Disabled the use of landmarks in multiplayer (was recently added, only caused landmarks to act as spawnpoints which causes players to get stuck in level geometry)
  • Fixed ammo updates not being resent after a hud reset has occurred
  • Fixed game_playeractivate target being triggered multiple times in some game modes
  • The gamemode name is sent to clients using the serverinfo buffer and is displayed when using cl_debuginfo_show 1
  • The current in-game time is now displayed when using cl_debuginfo_show 1
  • Mark player as spawning when choosing a class (hides weapon pickup HUD icons for weapons given on spawn)
  • Removed sv_oldgrapple cvar (leftover from an old Opposing Force update)
  • Made the Knife backstab attack work against NPCs
  • Added master support to trigger_relay
  • ent_create and npc_create now only inherit the yaw angle from the player that is executing the command
  • Added new skill variables:
    • allow_use_while_busy: Allows players to +USE entities while their character is busy with a weapon (e.g. reloading)
    • allow_flashlight: Whether to allow the flashlight (replaces mp_flashlight cvar)
    • allow_monsters: Whether to allow monsters to spawn (replaces mp_allowmonsters cvar)
    • falldamagemode: Controls the fall damage mode (0: fixed at 10 damage, 1: progressive: longer falls deal more damage) (replaces mp_falldamage cvar)
    • healthcharger_recharge_time and hevcharger_recharge_time: Controls how long it takes to recharge both charger types (-1 == never recharge) (Replace hard-coded values)
    • weapon_respawn_time, ammo_respawn_time and pickupitem_respawn_time: Controls how long it takes to respawn the three item types (-1 == never respawn) (Replaces hard-coded values)
    • weapon_instant_respawn: If set to 1, weapons not flagged to be limited in world (i.e. hand grenades, satchels, etc) will respawn immediately (Replaces mp_weaponstay cvar)
    • allow_weapon_dropping: Controls whether players can drop weapons. Does not affect singleplayer (Also replaces mp_weaponstay cvar)
    • infinite_ammo: All players have infinite ammo. The game acts as though players have maximum reserve ammo
    • bottomless_magazines: Player reserve ammo is used before the weapon's magazine is used. When used in combination with infinite_ammo the player can fire forever
    • coop_persistent_inventory_grace_period: Amount of time in seconds after joining the server that players are able to spawn with their persisted inventory
    • crowbar_full_damage and pipewrench_full_damage: Whether the crowbar/pipe wrench should deal full damage on each swing. In singleplayer only the first swing deals full damage
    • revolver_laser_sight: Whether to enable the revolver's laser sight (zoom)
    • smg_wide_spread: Whether to use a wider spread
    • shotgun_single_tight_spread: Whether to tighten the single shot spread
    • shotgun_double_wide_spread: Whether to widen the double shot spread
    • crossbow_sniper_bolt: Whether zoomed in shots use sniper bolts (hitscan instead of physical projectile)
    • crossbow_explosive_bolt: Whether bolts explode if they don't hit an object that can be damaged (sniper bolts will never explode)
    • gauss_charge_time: Controls the Gauss gun's charge time. Clamped to the range [0.1, 10]. The Gauss gun overcharges at 10 seconds
    • gauss_fast_ammo_use: Whether to consume ammo quickly
    • gauss_vertical_force: Whether to apply vertical knockback when using secondary attack (Gauss jumping)
    • gauss_damage_radius: Damage radius multiplayer for secondary attack pierced shots. Due to how radius damage is applied this does not turn such attacks into nuclear blasts when the radius is increased
    • egon_narrow_ammo_per_second: Ammo usage per second for narrow mode (minimum 0) (note: narrow mode is never used)
    • egon_wide_ammo_per_second: Ammo usage per second for wide mode (minimum 0)
    • knife_allow_backstab: Whether to treat attacks from behind as a backstab (backstabs deal 100 times normal damage but will never gib its target)
    • grapple_fast: Whether to allow the Barnacle grapple to launch again quickly
    • m249_wide_spread: Whether to use wide spread for M249 shots
    • shockrifle_fast: Whether to allow the Shock Rifle to fire and regenerate ammo faster

C# Changes

  • Fixed ba_outro player freeze breaking if the game is saved and loaded while frozen
  • Reworked skill converter to emit simplified format
  • Converted MapCfgGenerator script to an executable
  • Reworked map cfg generation to use JsonTextWriter
  • Removed Op4 gamemode settings from worldspawn, rely on map cfg to specify the gamemode instead
  • Updated renamed entity classnames in game_player_equip as well
  • Configure maps that use game_player_equip to not give default spawn inventory
  • Added Spawn Inventory for ba_tram1 to give players the HEV suit instead of relying on buggy game behavior to make players pick it up
--> Continued in next post
Posted 11 months ago2023-04-22 16:29:17 UTC Post #347473
<-- Continued from previous post

Asset Changes

  • Updated fgd to account for code changes
  • Added test maps for new features
  • Removed DefaultGameData.json (now defined in game code)
  • Updated Create Server config to account for code changes
  • Added dummy mapcycle.txt and default mapcycle.json
  • Added default Spawn Inventory for multiplayer and CTF (was previously hard-coded)

Map configuration file changes

The map change config file has been removed. It had been added as a counterpart to the engine's mapchange cfg file but in practice it doesn't have any value since it can only change cvars after the game has already started which has the potential to break things.

The server config can now only echo text to the console and execute commands. Default configurations are now hard-coded again to help avoid the case where a server operator modifies the default config, players download a custom map and are then unable to play it due to those changes being missing.

If a map doesn't have a configuration file the game will use the default map configuration file cfg/DefaultMapConfig.json instead.

Game mode selection

Game mode selection has been completely reworked. Previously the game mode used in multiplayer depended on whether the map enables co-op and the value of mp_teamplay. Now maps can specify the default game mode and lock it to that in the map cfg:
{
    "Includes": [
        "cfg/OpposingForceConfig.json"
    ],
    "GameMode": "ctf",
    "LockGameMode": true
}
The server can choose a game mode using the mp_gamemode cvar which contains the name of the game mode to use (deathmatch, teamplay, ctf, coop).

The Create Server dialog also lets you choose one, unfortunately due to engine limitations this had to be done using a separate cvar mp_createserver_gamemode. That cvar overrides the value of mp_gamemode so the method used is slightly different when starting a server that way.

Since engine bugs cause cvars to be updated too late when starting a second local server through the main menu the game now brute forces the engine to execute remaining cvar changes. Due to engine bugs this can cause multiple map changes to occur which the game now responds to by returning you to the main menu. This should never happen since it requires a specially crafted set of console commands to be executed.

The co-op gamemode is now mostly playable. Some entities still need changes to work correctly in a multiplayer environment but for the most part they now behave as they should. Some game logic and features need updating to handle co-op specific requirements as well.

Mapcycle changes

The map cycle is now JSON instead of the original custom format. I don't think many people are aware that the original map cycle format has optional attributes:
// Example/Default mapcycle for Deathmatch Maps
// Copy to mapcycle.txt to use :)
//
op4_bootcamp   "\minplayers\0\maxplayers\32\mp_weaponstay\1\"
op4_datacore   "\minplayers\0\maxplayers\32\mp_weaponstay\1\"
op4_demise     "\minplayers\0\maxplayers\32\mp_weaponstay\1\"
op4_disposal   "\minplayers\0\maxplayers\32\mp_weaponstay\1\"
(from WON Opposing Force)

The original version allows maps to be restricted to certain player counts and also to set cvars. Since the Unified SDK has map configs the ability to change cvars is rather useless and the syntax is pretty confusing, so it's now JSON:
[
    "undertow",
    {
        "Name": "snark_pit",
        "MinPlayers": 0,
        "MaxPlayers": 32
    },
    "boot_camp",
    "lambda_bunker",
    "datacore",
    "stalkyard"
]
It works the same way but it's a lot easier to understand.

The map cycle code is also a lot simpler so it's easier to modify if you wanted to say, modify it for co-op to handle map packs that are chained together using trigger_changelevel. You wouldn't include all maps in the map cycle file so you'd have to keep track of the last map that was in the file so you can skip to the next one.

In most cases you'll only have a list of map names so the syntax barely changes.

Spawn Inventory and Persistent Inventory

Two new features have been added to remove hard-coded functionality, fix bugs and improve co-op.

The Spawn Inventory is a configuration feature that lets you define what the player's inventory is like when they spawn in the game. In singleplayer this is the inventory they start with when a map is loaded using the map command, so when playing through a campaign you'll only get it in the first map, never in subsequent maps.

In multiplayer this is the inventory each player starts with when they respawn. Gamemode-specific inventories are handled through conditional sections in the map config. A new config file called cfg/BaseGameConfig.json has been added that defines the defaults:
{
    "SectionGroups": [
        {
            "Condition": "Multiplayer",
            "Sections": {
                "SpawnInventory": {
                    "HasSuit": true,
                    "Weapons": {
                        "weapon_crowbar": {},
                        "weapon_9mmhandgun": {}
                    },
                    "Ammo": {
                        "9mm": 68
                    },
                    "WeaponToSelect": "weapon_9mmhandgun"
                }
            }
        },
        {
            // Override default multiplayer inventory for CTF.
            "Condition": "Multiplayer && GameMode == \"ctf\"",
            "Sections": {
                "SpawnInventory": {
                    "Reset": true,
                    "HasSuit": true,
                    "Weapons": {
                        "weapon_pipewrench": {},
                        "weapon_eagle": {},
                        "weapon_grapple": {}
                    },
                    "Ammo": {
                        "357": 21
                    },
                    "WeaponToSelect": "weapon_grapple"
                }
            }
        }
    ]
}
You can control whether the player is given the following:
  • HEV suit
  • Long jump module
  • weapons
  • ammo
And set health and armor to any valid value.

And you can choose which weapon is selected by default.

You can also specify the default ammo in a weapon:
"Weapons": {
    "weapon_crowbar": {},
    "weapon_9mmhandgun": {
        "DefaultAmmo": 34 // Two magazines by default
    }
}
So you can give players empty weapons, make them injured by default, or give them a full loadout.

This feature is also used in Blue Shift to replace the original method used to give the HEV suit since it no longer works (it depended on the player touching the suit before touching a teleport trigger). This also allows players to get the HEV suit if they join a co-op game after it has started on that map, though they'll spawn in the wrong location (campaign maps need a lot of adjustments for co-op, which is out of scope for this project).

Building on the backend functionality used by this feature is Persistent Inventory, a feature exclusive to co-op that allows players to carry their inventory state to the next map. All of the above is carried over along with the weapon's current magazine count.

A skill variable coop_persistent_inventory_grace_period controls how long this persistent inventory is given when respawning. A trigger_changelevel keyvalue controls whether the inventory is persisted or not.

Here's a video showing co-op and persistent inventory:
Note that some sounds aren't playing due to an engine bug. I am working on a solution for that.

Project merging status

Current status:
  • Half-Life Updated (up-to-date)
  • Half-Life: Opposing Force Updated (up-to-date)
  • Half-Life: Blue Shift Updated (up-to-date)
  • Half-Life Updated CMake (done, project has been archived)
  • HLEnhanced (in progress)
  • Enhanced Half-Life (almost done)
  • Half-Life Better Weapons (done)
  • Condition Zero: Deleted Scenes SDK (done)
Merging continues along with planned features being finished. This shouldn't take much longer.

Remaining work to be done

  • Update changelog to include all changes (partially complete)
  • Write documentation for all new features (partially complete)
  • Networking system (other immutable data): Networking system is implemented, new and existing features are being updated to use it
  • HUD image replacement for Opposing Force: basic hud sprite name replacement test done and functional; more complete implementation will be added to implement this feature
  • Versions of Opposing Force-only HUD images that have the scanline effect removed: in progress
  • A way to start all three campaigns from the main menu. Probably the same solution as used in Condition Zero Deleted Scenes (menu button that starts a map that opens an SDK-level menu that enables campaign and training selection)
  • Merge in remaining useful functionality from other projects (in progress)
  • Implement as much of the work scheduled on the issue tracker as possible (in progress)
  • Review all changes
  • First alpha build
  • Stress test the three campaigns and fix issues that show up (first test done)
  • First beta build
Most of the work done the past few weeks has been completing work that was listed on the issue tracker. That's quickly getting done and some of those also involve merging other projects (e.g. HLEnhanced weather effects code).

One by one major features are getting implemented, co-op is mostly finished now (you can actually make map packs now that level changes work) so it's going pretty smoothly.

I will end this progress update with a couple videos showing new features:
Posted 10 months ago2023-04-30 14:04:50 UTC Post #347490

Progress on the Unified SDK

SDK Changes

  • Made config system code server only to avoid spending time building this code for the client dll
  • Reworked entity creation and destruction to allow additional logic to be added in around the OnCreate and OnDestroy function calls
  • Implemented entity templates (see below)
  • Reworked weapon_rpg and ammo_rpgclip to use entity templates for multiplayer-only ammo doubling
  • Sync client with server gauss charge time value so spin sound pitch is correct after level change
  • Fixed sounds played on current view entity lagging behind in multiplayer (they now always sound like they originate at the current view position)
  • Reworked casts to CBasePlayer to use ToBasePlayer (extra safety checks and runtime type validation in debug builds)
  • Reworked utility functions involving players to take CBasePlayer* (avoids passing non-player entities to these functions and simplifies function calls)
  • Don't use entvars_t::enemy if it can be avoided (the engine uses this variable in NPC movement code but it doesn't seem to actually be used, needs more investigation)
  • Use UTIL_FindEntityByTargetname when searching by targetname instead of UTIL_FindEntityByString
  • Disable trigger_teleport when target is an empty string (prevents teleportation to seemingly random locations)
  • Added missing monster state name to ReportAIState function (HL Updated change, already done in Unified before)
  • Implemented HUD replacement system (see below)
  • Fixed VGUI1 CListBox class setting scrollbar range to incorrect maximum (off by one issue, last line was always empty as a result)
  • Implemented Campaign selection menu (replaces New Game dialog) (see below)
  • Reworked CTF item detection to avoid use of entity classification (uses MyCTFItemPointer() function now)
  • Reworked how the game determines which kind of gibs to spawn (human or alien gibs, relied on the entity classification before which isn't always accurate)
  • Fixed entities notifying their owner of death multiple times in some cases (this broke monstermaker)

Asset Changes

  • Added entity template for MP5 to set default ammo amount to full magazine (50 rounds) for Opposing Force
  • Set weapon_rpg and ammo_rpgclip ammo amounts to 2 for multiplayer (was previously hard-coded)
  • Added GameMenu.res and gameui_english.txt
  • Disabled New Game menu button, added new Start Campaign... menu button that opens the campaign selection menu
  • Added campaign configs for Half-Life, Opposing Force, Blue Shift and the Uplink demo (Uplink has not been updated to account for code changes, needs more work)
  • Added campaign selection map
  • Updated the localization files

Entity Templates

Entity templates is a feature added to allow the MP5 to have a full magazine by default in Opposing Force (50 rounds instead of 25 as in all other HL games).

Entity templates work by allowing level designers to change the default value of a keyvalue for a specific type of entity. Here's how the MP5's is done:

cfg/entitytemplates/op4/weapon_9mmar.json:
{
    "default_ammo": "50" // Full magazine
}
cfg/OpposingForceConfig.json:
// Rest of file omitted
{
    "SectionGroups": [
        {
            "Sections": {
                "EntityTemplates": {
                    "weapon_9mmar": "cfg/entitytemplates/op4/weapon_9mmar.json"
                }
            }
        }
    ]
}
The map config specifies which template to use; the template itself is a collection of keyvalues to apply.

The template is applied after the entity is created (after OnCreate has finished) but before level designer-set keyvalues are applied. All entities with the specified classname are affected, even if created by a monstermaker or when transitioned from another map.

This is only a simple use of a very powerful feature. You can use this to do things like changing the health of all security guards, change the default model of all grunts to that of the Male Assassin, set the sentence/sound/model replacement file for all zombies, pretty much anything you'd want to do to every instance of a specific entity whose keyvalues aren't always set by designer-placed versions (i.e. if the fgd sets a default value that will always take precedence over the template).

HUD replacement system

It's now possible to replace the HUD on a per-map basis by changing the HUD config files used for the HUD itself and for each weapon.
// Rest of file omitted
{
    "SectionGroups": [
        {
            "Sections": {
                "HudReplacement": {
                    "HudReplacementFile": "sprites/op4/hud.json",
                    "Weapons": {
                        "weapon_9mmar": "sprites/op4/weapon_9mmar.json"
                    }
                }
            }
        }
    ]
}
This overrides the default files used (sprites/hud.json and sprites/weapon_9mmar.json respectively) and causes the client to use the sprites specified therein instead.

This feature does not take effect yet because the art assets have not been completed. malortie is working on those and has been making good progress.

Campaign selection menu

The New Game dialog has been replaced with the Campaign selection menu. This menu allows you to select a campaign from a list and start either its campaign or training map.
This menu works by loading a map that opens a menu. That menu loads the list of campaigns by scanning the campaigns directory for JSON files.

JSON file structure:
{
    "Label": "Half-Life",
    "Description": "The Half-Life campaign.\n\nRelease date: 1998.",
    "CampaignMap": "c0a0",
    "TrainingMap": "t0a0"
}
Either the campaign or training map name can be omitted, in which case the option is disabled. If both are omitted or the file doesn't contain the expected data the file is ignored.

Campaigns are ordered based on their filename. The 4 campaigns shown have these names:
  • 001HalfLife.json
  • 002OpposingForce.json
  • 003BlueShift.json
  • 004Uplink.json
All search paths are scanned so these files can be in <moddir>/campaigns, <moddir_addon>/campaigns, <moddir_downloads>/campaigns or other directories usually checked by the filesystem.

There's no extra work needed to list them, no manifest or anything. This means the same mod can be used as a base for unrelated mods/map packs to allow them to launch through the same menu.

This menu is for singleplayer campaigns only, not multiplayer. Though it can be modified to allow the launching of multiplayer games it isn't recommended since this menu works by loading a singleplayer map.

It's not meant to be anything fancy, it's just a quick and easy way to start a campaign map.

Project merging status

Current status:
  • Half-Life Updated (up-to-date)
  • Half-Life: Opposing Force Updated (up-to-date)
  • Half-Life: Blue Shift Updated (up-to-date)
  • Half-Life Updated CMake (done, project has been archived)
  • HLEnhanced (in progress)
  • Enhanced Half-Life (almost done)
  • Half-Life Better Weapons (done)
  • Condition Zero: Deleted Scenes SDK (done)
No changes since the last progress update, but there are work in progress branches that cover the merging of some of the last features that need to be merged. Some of the work listed above was done in preparation for this work.

Remaining work to be done

  • Update changelog to include all changes (partially complete)
  • Write documentation for all new features (partially complete)
  • Networking system (other immutable data): Networking system is implemented, new and existing features are being updated to use it
  • HUD image replacement for Opposing Force: HUD replacement system has been implemented (complete)
  • Versions of Opposing Force-only HUD images that have the scanline effect removed: in progress
  • A way to start all three campaigns from the main menu: Done, plus ability to start Half-Life: Uplink and add more campaigns with minimal work (complete)
  • Merge in remaining useful functionality from other projects (in progress)
  • Implement as much of the work scheduled on the issue tracker as possible (in progress)
  • Review all changes
  • First alpha build
  • Stress test the four campaigns and fix issues that show up: now includes Uplink as well, once it has been converted (first test done)
  • First beta build
A fair amount of work got done in just a week, but that's because these features build on work that was done before. Entity templates required the item overhaul to be merged in from EHL first since that added the keyvalues to control weapon and ammo amounts; HUD replacement required the switch to JSON files to be done first.

I'd have preferred to overhaul the UI for campaign selection to make it look a little nicer but it would take a lot of work to do that, and this project has been in development for long enough. The focus now is on completing remaining work that's small and useful.

In practice this means most of the open issues on the issue tracker as well as a lot of small things to check that i've got on my TODO list. I'm holding off on going into detail about these because i want to sure about the viability of the remaining work before discussing it.

Once more of the ingoing work is complete i will make another dev build available. That may be as soon as next week but we'll see.
Posted 10 months ago2023-04-30 20:43:11 UTC Post #347491
Would be cool if you could re-add the lovely old Animation Clip of the Op4 WON version where we've seen Ospreys and soldiers running around. While it serves no purpose it might be fantastic for nostalgic reasons. Naturally something like that has 0 priority but eventually at very end as final touch.

Your current progress looks brilliant.
Posted 10 months ago2023-04-30 22:21:59 UTC Post #347492
That's an avi file. The engine doesn't have any support for video playback, and that was done on the main menu. This menu is done using VGUI1, it's an in-game menu. Rendering a video in that is impractical at best and a lot of work either way.
Posted 10 months ago2023-05-21 15:44:01 UTC Post #347535
And did you learn about modern delegate unmanagable pointers and you would like to port whole Half-Life into C# if Valve Software would like to be happy if many coders write in C# and develop own game better then Xash.
No, i stopped working on SharpLife because that project is way too big to be completed in a reasonable amount of time and recreating game behavior is too difficult to do with available resources.

Like i said on Discord SDL3 is not backwards compatible with SDL2. If you intend to interface with existing mods then you will have to use SDL2 for it to work.

Since native libraries have platform-specific behavior you may run into problems if you try to load multiple versions of the same library into memory. In my experience the Linux runtime linker will try to merge identical symbols even if they exist in different dynamic libraries so it may try to merge SDL2 and SDL3 APIs into a single one.

This happened when i converted some globals to use C++17 inline declarations so i had to change the default symbol visibility to hidden, which you can't do with exported symbols like SDL2's API since those must have default (public) visibility to be directly accessible from other binaries. I also had to make a custom SDL2 build to load it side-by-side since it creates process-global objects managed at the operating system level. That was on Windows but Linux likely has something similar.
Posted 10 months ago2023-05-21 17:26:12 UTC Post #347536

Progress on the Unified SDK

SDK Changes

  • Fixed mouse movement during map load affecting initial view angles (caused the player to look to the side instead of straight ahead)
  • Set monster_rosenberg model to correct default (was still using scientist.mdl)
  • Overhauled entity classification system (see below)
    • Implemented is_player_ally keyvalue
  • Fire monstermaker target after NPC spawning is done instead of after NPC creation and before spawning
  • Re-implemented UTIL_FindEntityBy* functions to avoid calling engine function (see below)
    • Implemented wildcard matching for entity classname, targetname & target searches
  • Implemented point_teleport
  • Added initial_capacity keyvalue to Osprey NPCs to allow specifying how many grunts/assassins to deploy instead of relying on existing NPCs in the map to influence calculations (max 24)
  • Implemented targeting laser in all func_tank entities
  • Fixed func_tank entities not returning player weapon control when killtargeted
  • Prevent engine precache limit from being reached (game will no longer tell engine to precache a resource if doing so would cause the limit to be exceeded)
  • Implemented target selector support for target, killtarget & point_teleport (see below)
  • Always store activator on delay trigger to allow access to initial activator entity in delayed trigger setups
  • Ensure turret NPCs finish dying so they don't keep running dying logic forever if killed during first activation
  • Fire Death trigger condition when turret NPCs are destroyed
  • Install additional 32 bit Linux dependencies when running Linux CI (needed because Github Actions has updated its default Linux environment)
  • Implemented Counter-Strike style env_fog
  • Play hud & geiger sounds on unique channels to avoid preempting game sounds (geiger used to cause sounds to cut out)

Asset changes

  • Add default entity classifications config file
  • Added new keyvalues to fgd
  • Added point_teleport and env_fog entities to fgd
  • Cleaned up fgd a bit to remove non-existent functionaltiy and properly hint to level editor how to use certain keyvalues
  • Added source files for class_signs.wad (contains textures stating NPC names)
  • Added zoo_npcs map
  • Added Half-Life Uplink demo sentences & titles entries

C# Changes

  • Fixed flare sprites in c4a3 (Nihilanth boss battle death script) using wrong render mode
  • Fixed grunt on stretcher holding gun in of1a1 (Opposing Force "Welcome To Black Mesa" map)
  • Clear model keyvalue for monster_rosenberg (defaults to correct model in code now)
  • Copy Uplink maps from valve_uplink directory

Entity classification changes

The entity classification system has been redesigned for comfort and utility. Previously this was a hard-coded table defining the relationship between entity classifications.

You can think of a classification as a faction. The human_passive classification (Black Mesa scientists) fears human_military (Human Grunts) and will run away from them. human_military dislikes human_passive and will attack them.

This is now defined in a configuration file:
{
    // Class none is always added and should only have relationship "none" (default) to all classes.
    "none": {},
    "machine": {
        "Relationships": {
            "player": "dislike",
            "human_passive": "dislike",
            "human_military": "none",
            "alien_military": "dislike",
            "alien_passive": "dislike",
            "alien_monster": "dislike",
            "alien_prey": "dislike",
            "alien_predator": "dislike",
            "player_ally": "dislike",
            "player_bioweapon": "dislike",
            "alien_bioweapon": "dislike",
            "human_military_ally": "dislike",
            "race_x": "dislike"
        }
    },
    "player": {
        "Relationships": {
            "machine": "dislike",
            "human_military": "dislike",
            "alien_military": "dislike",
            "alien_passive": "dislike",
            "alien_monster": "dislike",
            "alien_prey": "dislike",
            "alien_predator": "dislike",
            "player_bioweapon": "dislike",
            "alien_bioweapon": "dislike",
            "race_x": "dislike"
        }
    },
    "human_passive": {
        "Relationships": {
            "player": "ally",
            "human_passive": "ally",
            "human_military": "hate",
            "alien_military": "hate",
            "alien_monster": "hate",
            "alien_prey": "dislike",
            "alien_predator": "dislike",
            "player_ally": "ally",
            "human_military_ally": "dislike",
            "race_x": "hate"
        }
    },
    // More classifications here
}
As usual it can be overridden on a per-map basis, though it only supports a single file per map.

The default classification used by NPCs now uses these names instead of a hard-coded index.

Notable changes to existing classifications:
  • added alien_flora classification for Xen trees and large spores (replaces CLASS_BARNACLE which wasn't used by barnacles and was never used because it would cause the class table to be accessed out of bounds)
  • Added IsMachine method to determine whether an entity is a machine of some kind (e.g. Osprey, turrets)
  • Added IsBioWeapon method to determine whether an entity is a bioweapon of some kind (e.g. Snarks, Penguins, Hornets)
  • Changed RadiusDamage and FlameDamage methods to take ignore class as last parameter to allow it to be defaulted to "none"
  • Changed squad monster code to use CanRecruit function to determine whether a candidate entity can be recruited into the current squad monster's squad
  • Changed human grunts, allied human grunts and shock trooper squad dialogue to only identify enemies as monsters if they have alien gibs (previously entities that are not players, player allies, human passive or machines were considered to be monsters)
  • Changed Snarks and Penguins to change their classification when they update their enemy instead of doing so when their classification is requested (avoids possible recursion). They will not do so if they are using a custom classification.
Two new keyvalues have been added:
  • classification: If defined, changes the NPC's classification to the specified class (must be one named in the map's classification config file). The new entity templates feature can be used to apply this to every instance of a specific type of NPC in a map. The new target selector feature (see below) can be used to change the player's classification.
  • is_player_ally: Has three possible values: Default, No and Yes. Default uses the relationship from the NPC's class to the player's class defined in the map's classification config file. No equals dislike (will attack), Yes equals ally.
The classification system changes are not done yet. More work is needed before it works as intended but this covers the majority of the work required.

Entity lookup changes

The functions used to look up entities by classname, targetname and target have been re-implemented in game code. Previously they asked the engine to find entities.

Doing so makes these functions more efficient, not only by eliminating a function pointer call but also because the engine uses an inefficient lookup method. Every time the engine is asked it first looks up the address of the variable used to perform comparisons which slows things down.

The new implementation uses a callback to indicate which variable to use. Analysis of compiled release builds shows that the callback does not exist at all so there is no overhead involved with selecting the variable anymore.

In addition these functions now accept wildcards just like source. So you can for instance trigger all entities that start with a specific prefix:
"target" "class_sprite_*"

Just like Source it only accepts wildcards at the end.

It is now also possible to use target selectors in some cases. For example you can kill the activator of a trigger like this:
"killtarget" "!activator"

Unfortunately some entities don't pass activators along properly, like trigger_relay which passes itself instead.

Two target selectors are supported:
  • !activator: The entity that started the current trigger execution
  • !caller: The last entity in the trigger execution chain

Teleport entities changes

trigger_teleport now has a fire_on_teleport target fired whenever it teleports an entity. Useful for triggering effects and whatnot.

The new point_teleport entity can be used to teleport a targeted entity to the point_teleport entity's position. To teleport the entity that activated it (like a player pressing a button that triggers the teleport entity) the !activator target selector can be used.

It also has a fire_on_teleport keyvalue.

func_tank changes

All tank entities now correctly restore the player's weapon if they are killtargeted. Previously this left the player in an invalid state where the weapon HUD was disabled.

Tanks now also have a target laser visible only to the player that is currently using it.

This laser is enabled only if turned on with the enable_target_laser keyvalue and can use a designer-configured sprite, width and color.

env_fog

Counter-Strike's env_fog entity has been implemented here as well. This entity allows the addition of fog with configurable density, start and stop distances, color and the option to also affect the skybox.

The TWHL and VDC wikis have been updated to include missing keyvalues and spawnflags for this entity.

Note that fog only renders when using the OpenGL renderer. This uses the engine's fog rendering code, not custom OpenGL code.

Hud & geiger sound changes

HUD sounds (weapon selection HUD & chat text) now play on a dedicated sound channel, as do geiger sounds. Previously these played on the player's item channel which is also used for things like weapon reloading. Since the geiger plays very often when near a radiation source it kept cutting off the sounds. This is no longer the case.
--> Continued in next post
Posted 10 months ago2023-05-21 17:26:16 UTC Post #347537
<-- Continued from previous post

zoo_npcs test map

The zoo_npcs test map has been added and includes functionality to test most of the non-scripted NPCs as well as weapons, items, ammo and other new features.
As you can see there is still work needed for the classification changes to work properly. Additional features are needed to help reduce the number of entities created since it exceeds the server-side maximum visible entities limit at times (255).

Note that the turrets float up when the fuel and vent buttons are enabled because both apply trigger_push (fuel to force entities to move and touch trigger_hurt) to all entities in the arena. Since the turrets aren't meant to move they aren't set up to handle this properly.

New features used in the map (not an exhaustive list):
  • trigger_changekeyvalue: used to configure NPCs after spawning to set the classification and is_player_ally keyvalues
  • point_teleport: Used to teleport the player at the push of a button
  • Health & HEV chargers: infinite capacity, recharge 5 units per tick instead of 1
  • All item entities: float item to retain designer-configured angles, stay visible during respawn, sound replacement file to silence respawn sound, respawn in singleplayer, give max ammo on pickup
  • ammo_all: Resupply point outside Displacement field lab
  • env_warpball: Teleport target for Displacement field lab teleporters, creates teleport effect
  • trigger_teleport: fire_on_teleport triggers warpball entity on teleport
  • ´!activator´: Used to kill destroyed turrets (a func_button in a room under the map is used for this, since trigger_relay doesn't preserve the activator)
  • monstermaker: Pass initial path_corner target name to Apache and Osprey NPCs
  • func_tank: target lasers

Project merging status

Current status:
  • Half-Life Updated (up-to-date)
  • Half-Life: Opposing Force Updated (up-to-date)
  • Half-Life: Blue Shift Updated (up-to-date)
  • Half-Life Updated CMake (done)
  • HLEnhanced (in progress)
  • Enhanced Half-Life (almost done)
  • Half-Life Better Weapons (done)
  • Condition Zero: Deleted Scenes SDK (done)
Some of the changes listed above involved integrating features that exist in HLEnhanced, the list of features left to merge is getting very small now.

Remaining work to be done

  • Update changelog to include all changes (partially complete)
  • Write documentation for all new features (partially complete)
  • Networking system (other immutable data): Networking system is implemented, new and existing features are being updated to use it
  • Versions of Opposing Force-only HUD images that have the scanline effect removed: in progress
  • Merge in remaining useful functionality from other projects (in progress)
  • Implement as much of the work scheduled on the issue tracker as possible (in progress)
  • Review all changes
  • First alpha build
  • Stress test the four campaigns and fix issues that show up (first test done)
  • First beta build
The Half-Life Uplink demo maps are included with the game installation now since it's a free demo, and setting it up to copy the maps is non-trivial (Uplink uses the same mod directory name so it has to be renamed, and the maps have to be extracted from the pak0.pak file). The demo works fine aside from a couple scripted events that glitch out, but that also seems to happen in the original so i don't consider those to be worth fixing.

The remaining work required for classifications isn't too much so that should be done soon. After that there's a bunch of smaller stuff i need to check out.

The list of remaining work is getting smaller. There are only a few remaining issues on the issue tracker, not all of which will be implemented.
After that i have to go through my TODO list which includes remaining project merging work.

With a bit of effort the implementation work should be done soon.
Posted 9 months ago2023-06-03 14:21:35 UTC Post #347576

Progress on the Unified SDK

A new pre-alpha build is available: https://github.com/SamVanheer/halflife-unified-sdk/releases/tag/dev-2023-06-03
Note: pre-alpha builds are not intended for general testing. As the wiki article on software life cycles explains the first versions intended for this kind of testing are alpha builds. Pre-alpha builds are for testing specific features by request to verify that they work as expected based on bug reports and feature requests.

Since this project is still in the midst of development (essentially this is the game engine development part of the project) it's expected that there are bugs and features not working as expected (or at all). Once all required features have been implemented the project can move to the alpha testing stage where verifying the proper behavior is the main goal.

SDK Changes

  • The name of the renderer (Software or OpenGL) is now shown in the debug overlay. It also recognizes Direct3D but the engine does not support it anymore and does not include the code for it to work
  • The weapon deploy animation is now always sent to clients when calling EquipWeapon to ensure it plays when the player stops using func_tank entities
  • Renamed allow_weapon_dropping skill var to allow_player_weapon_dropping, added allow_npc_item_dropping to globally control whether NPCs can drop items on death
  • Added null check to scientist fear task to prevent potential crashes
  • Stop func_train move sound when triggered off (fixes sound looping issue in c1a0c (first Unforeseen Consequences map))
  • Fixed Desert Eagle laser position not updating when holding down reload button
  • Fixed Voltigore beams created on death connecting to the wrong entity, causing them to appear to connect to the world origin
  • Fixed being able to break scripted_sequence by +using friendly NPCs to make them follow player
  • Fixed potential incorrect facing in scripted_sequence
  • Fixed female assassin footsteps always playing every 0.2 seconds when walking and running (now uses the same intervals as player, but may differ somewhat because NPCs only think 10 times a second)
  • Fixed female assassins not resetting their movetype if they jump and land while not in combat (resulted in stuttering movement because they were considered to still be in the air)
  • Added ent.io trace logging for multi_manager start
  • Precache monstermaker entities using provided keyvalues
  • Added diagnostics to entity templates for classname key
  • Simplified trigger_changekeyvalue logic
  • Increased maximum number of multi_manager targets from 16 to 64
  • Fixed env_blowercannon crashing if target does not exist, simplified spore creation
  • Implemented logic_random: triggers a randomly selected target out of up to 16 targets
  • Don't draw the beam sprite for the night vision HUD icon
  • Reworked armor HUD to avoid depending on relative positions of the full and empty icons in the sprite sheet
  • Increased maximum number of weapon slots to 10
  • Reworked train control & pain sprites to use hud.json entry for name mapping to enable an Opposing Force-specific version to be used
  • Added barebones bot system for multiplayer testing (see below)
  • Implemented new temporary entity list; increased maximum number from 500 to 2048 (see below)
  • Limited Overflow 2048 temporary ents! message to only print once per frame (excessive logging caused framerate drops to < 30 FPS)
  • Reworked NPC-spawned gibs to be created as temporary entities to eliminate problems with ED_Alloc: out of edicts errors, crashes, entity rendering issues (see below)

C# Changes

  • Fixed c3a2b (Lambda Core map 3) valves soft) locking if turned too quickly in succession. The second valve now locks until it is safe to use it
  • Fixed game over text being cut off in c3a2 (killing retinal scanner scientist in Lambda Core map 1)
  • Fixed some sounds restarting on save load
  • Fixed Nihilanth dialogue on c4a2 (Gonarch's Lair map 1) not playing ("Win, you cannot win")
  • Reworked upgrade tool classes to improve consistency in API design
  • Renamed Installer to ContentInstaller
  • Renamed executables to remove the HalfLife.UnifiedSdk prefix (the Map Decompiler executables have also been renamed)
  • Added flag matching to KeyValueMatcher to filter results by spawnflags or other flag keyvalues
  • Fixed c2a5 (Surface Tension map 1) barrel not flying up if destroyed using radius damage attack
  • Updated Sledge libraries to latest version
  • Reworked Entity class to store keyvalues as lists instead of dictionaries (matches map compile tools behavior)
  • Prune excess multi_manager keyvalues (affects retinal scanner scripts that had too many keys)
  • Reworked Asset Synchronizer to minimize time spent in event handlers to avoid Too many changes at once in directory errors

Asset Changes

  • Various updates to zoo_npcs to improve map performance, stability and behavior
  • Added test maps for features and bug fixes
  • FGD changes:
    • Added Contents choices to func_train (allows trains to be made out of water, slime or lava)
    • Removed redundant and unused keyvalues
    • Fixed incorrect uses of target_source keyvalue type
    • Added missing master keyvalue to game_zone_player
    • Added ReplacementFiles base class to entities that use sounds or sentences
    • Added logic_random
  • Added HUD sprites for Half-Life & Opposing Force HUDs, updated HUD config files to use the new sprites
  • Updated hud.json entries used by entities whose names have changed
  • Added hud.json entries for the Blue Shift helmet & armor vest pickup items
  • Removed hud.json entries for unused train icons, added train_controls & pain_directions entries to allow the HUD to locate the right files at runtime
  • Hide Change Game button when launching the game directly (as opposed to launching through Steam)
  • Updated OpenAL library name in the package manifest so the right file is packaged

Half-Life and Opposing Force HUDs

The HUD is now capable of being switched between the Half-Life and Opposing Force icons:
This was made possible by the creation of new HUD sprites by malortie. Many thanks for the great work you've done!

Bots

A barebones bot system has been added to enable testing multiplayer. These bots are capable only of spawning and respawning when killed and do not know how to select teams and classes so they can't be used in the Capture The Flag gamemode.

New temporary entity list

The client-side temporary entity list has been replaced with a new one that has a higher limit. The engine's list has a maximum of 500 entities, whereas this one has 2048.

This limit is pretty arbitrary. Technically it can even be made unlimited, but it won't increase the number of visible temporary entities because the engine can only render 512 entities in a single frame. Without raising that limit first increasing the temporary entity limit will not have much of an effect. This would require a custom renderer which is out of scope for this project.

A performance issue in the printing of Overflow 2048 temporary ents! has also been addressed.

Client-side gibs

Gibs spawned by NPCs are now handled on the client side. Previously gibs were server side, causing them to count towards the server-side entity limit. Spawning a lot of them could cause the game to shut down or crash, and often caused other entities to disappear because they also count towards the server-side visible entity limit (max 255).

Gibs are now temporary entities and can be spawned en masse without causing errors or crashes:
The cvars cl_gibcount, cl_giblife and cl_gibvelscale control how many are spawned, how long they exist once they stop moving and how much their velocity is affected (the settings in the video are 100, 1 and -10000 respectively).

Some NPCs override the gib count since they want to spawn a specific number of gibs but for most NPCs the cvar is taken into account.

The gibshooter entity still uses server-side gibs but this can be changed if necessary.

Project merging status

Project merging is complete. While there are still some useful changes that could be merged none of them are user-facing and are mostly about fixing minor bugs and making things a bit easier for programmers to use.

Remaining work to be done

  • Update changelog to include all changes (partially complete)
  • Write documentation for all new features (partially complete)
  • Networking system (other immutable data) (done)
  • Versions of Opposing Force-only HUD images that have the scanline effect removed (done)
  • Merge in remaining useful functionality from other projects (done)
  • Implement as much of the work scheduled on the issue tracker as possible (in progress)
  • Review all changes
  • First alpha build
  • Stress test the four campaigns and fix issues that show up (first test done)
  • First beta build
Quite a bit of work is completed now, most of what's left are open issues on the issue tracker and things on my TODO list.

Many thanks to malortie for making the new HUD sprites. With the completion of that feature this SDK can now be used to play all 4 campaigns with the look and feel of each game, which is the main goal for the project.

Thanks to Ronin4862, LambdaLuke87, Oxofemple, FreeSlave and Shepard for helping with bug reporting and fixing.

Special thanks to Penguinboy for updating the Sledge libraries to provide keyvalues as lists to allow for more accurate modification of maps using the map upgrade tool.

This project is in the home stretch now, now it's just a matter of wrapping up remaining work, testing everything and documenting it.
Posted 9 months ago2023-06-11 15:22:56 UTC Post #347600

Progress on the Unified SDK

A new pre-alpha build is available: https://github.com/SamVanheer/halflife-unified-sdk/releases/tag/dev-2023-06-11
Note: pre-alpha builds are not intended for general testing. As the wiki article on software life cycles explains the first versions intended for this kind of testing are alpha builds. Pre-alpha builds are for testing specific features by request to verify that they work as expected based on bug reports and feature requests.

Since this project is still in the midst of development (essentially this is the game engine development part of the project) it's expected that there are bugs and features not working as expected (or at all). Once all required features have been implemented the project can move to the alpha testing stage where verifying the proper behavior is the main goal.

SDK Changes

  • Simplified talk monster friendly check and use/unuse sentence configuration. Friendly NPCs like scientists and security guards will no longer follow players if their classification treats players as anything other than allies (i.e. as if the player has shot them while not in combat)
  • Made it possible to for any NPC to follow players if they are friendly to the player (barebones support only)
    • Disabled following for some NPCs
  • Removed some obsolete HUD sprite functionality
  • Removed unused CHealthPanel class (non-functional VGUI1 version of the health HUD)
  • Reworked UI code to no longer use resolution size specifier
  • Reworked some string buffers & operations to avoid buffer overflows or truncations (fixes GCC compiler warnings)
  • Removed option to print list of all entities with ent_list client command to avoid network buffer overflows
  • Reworked weapons functions that start animations to use pev->body instead of a separate body parameter
  • Fixed Egon gun not stopping its attack animation if the attack button is held down and ammo runs out
  • Implemented chainsaw_melee skill variable: enables buggy melee attack behavior causing corpses to be damaged much faster
  • Changed client-side fog code to update fog parameters before the game frame is drawn to avoid edge cases that cause black screens
  • Clear global state when game_end is used to end multiplayer game (allows Co-op maps to clear state that may affect other maps)
  • Fixed various compiler warnings that occur at warning level 4. 3 warnings remain involving lambdas that have unreachable return statements. This is probably a code analysis bug, not incorrect user code
  • Fixed a bug in the text-based scoreboard that causes it to search for empty teams incorrectly. This code is not used and the VGUI1-based scoreboard does not have this bug
  • Optimized sound system to avoid a potentially expensive call when starting sounds
  • Reworked HUD message drawing to allow for unlimited-length lines
  • Prevent paths passed to filesystem functions from containing .. (parent directory)
  • Fixed Doxygen comments that contain bad syntax
  • Added child_classification keyvalue to allow NPCs that spawn NPCs (e.g. Big Momma, Osprey) to pass on classifications
  • Added unkillable keyvalue & cheat_unkillable: Makes NPCs and players unkillable, respectively. Not all NPCs support this setting
  • Mark monster_*_dead NPCs that should use human gibs as such so they spawn gibs
  • Play predicated sounds through OpenAL sound system as well (footsteps, weapon empty sounds)
  • Added sv_load_all_maps command to load all maps and generate node graph for them (allows leaving the game running on its own, can also be used to generate a log containing any errors logged by the game)
  • Don't switch weapons when using impulse 101 if the player has a weapon equipped already
  • Merged armor HUD into health HUD, draw armor next to health regardless of resolution (matches original game behavior at 640x480 resolution)
  • Fixed bug in sound loading code that caused it to calculate cue point positions incorrectly for stereo sounds
  • Fixed turrets and spore ammo entities not playing sounds in multiplayer in some cases
  • Reworked Nihilanth and Gene Worm death logic to not teleport players in multiplayer (used to trigger the end-of-map script)
  • Reworked Medic Grunt follower logic to follow player that used it instead of the local player
  • Removed invalid cast in Torch Grunt death logic

Asset Changes

  • Cleaned up fgd some more
  • Added keyvalues for new features to the fgd
  • Added test maps for some features
  • Added information about how to compile, package and copy models from source to a game installation using provided scripts
  • Changed hitgroup values for NPC models back to 0 to ensure game balance (i.e. damage required to kill) is identical to the original games
  • Fixed Rat NPC jump and death animations so they don't loop and end on the right frame
  • Fixed MP5 model so the arm vertices aren't attached to the weapon

NPC following behavior

Barebones support allowing NPCs to follow the player has been added:
NPCs don't have default start and stop sentences yet and may not always follow players as expected.

I implemented the essentials needed for this to work so others can expand on it as needed. This covers the AI schedule aspect of things and moving logic from a more specialized C++ class to the general NPC class.

Chainsaw melee

Chainsaw melee is a bug that causes melee weapons to attack corpses very quickly. It was fixed in this project, but i've added the option to enable it again:

Unkillable characters

NPCs can be set to be unkillable. Not all NPCs allow this since some have their own damage logic, but support can be added there as needed.

Players can also enable this for themselves using a cheat.
This feature is based on Source's buddha cheat.

Sounds in multiplayer

Some sounds didn't play in multiplayer. This happens with entities whose origins is in a brush or on the plane of a brush face. The calculations used to determine if a sound emitted at that position can be heard can produce false negatives causing the entity to be silent.

This was fixed by allowing entities to provide an offset to push the sound origin out of brushes. This fix can only be used with the new sound system since the original sound system has this code in the engine.

Remaining work to be done

  • Update changelog to include all changes (partially complete)
  • Write documentation for all new features (partially complete)
  • Implement as much of the work scheduled on the issue tracker as possible (in progress)
  • Review all changes
  • First alpha build
  • Stress test the four campaigns and fix issues that show up (first test done)
  • First beta build
This completes more of what's becoming a small TODO list. There are only a few issues left on the issue tracker that will be handled for the first release.

I've tested the sound system with these high quality sound packs: Stereo sounds play without 3D spatialization, essentially they sound as if they're coming from the player entity. Mono sounds play just fine.
There was an issue with cue points not loading correctly but that was fixed. No other issues were found.

The previous dev build included outdated tools that would fail to run due to relying on an older version of the bundled Utilities library. All tools have been updated so that shouldn't be an issue now.

I'm writing documentation for some engine functions to make it easier to work with them. Note that Doxygen's UI program currently freezes when processing this codebase due to a problem with the doxywizard program. The problem has been dealt with so the next release should work fine.

That's about it for now, expect the next progress update to be smaller than this one.
Posted 9 months ago2023-06-16 18:38:32 UTC Post #347628

Progress on the Unified SDK

A new pre-alpha build is available: https://github.com/SamVanheer/halflife-unified-sdk/releases/tag/dev-2023-06-16
Note: pre-alpha builds are not intended for general testing. As the wiki article on software life cycles explains the first versions intended for this kind of testing are alpha builds. Pre-alpha builds are for testing specific features by request to verify that they work as expected based on bug reports and feature requests.

Since this project is still in the midst of development (essentially this is the game engine development part of the project) it's expected that there are bugs and features not working as expected (or at all). Once all required features have been implemented the project can move to the alpha testing stage where verifying the proper behavior is the main goal.

SDK Changes

  • Added cl_custom_message_text cvar to display a custom message on-screen, along with cl_custom_message_x and cl_custom_message_y to control the position on-screen (works like game_text)
  • Fixed ropes breaking at high framerates
  • Added documentation to the engine's server interface
  • Marked a few more engine functions as deprecated
  • Fixed code analysis warnings
  • Reworked the music system to use a separate OpenAL device to prevent the HRTF setting from affecting music playback
  • Added option to use Link-time optimizations (C++ compiler setting)
  • Reworked HEV suit sentence playback to no longer store group and sentence indices in save games (indices are not guaranteed to be stable)
  • Reworked sentence groups to allow sentences to be non-sequential, removed bad sentence group index diagnostic message
  • Redesigned sentences system to use JSON instead
  • Removed sv_snd_openal and cl_snd_openal cvars (sound is played through the OpenAL sound system only now)
  • Disabled follower logic for (ai)scripted_sequence to prevent players from activating them with +USE (scripts share code with NPCs, including follower code)
  • Converted documentation for server and client interfaces to Doxygen format, fixed swapped documentation for HUD_Init and HUD_VidInit

C# Changes

  • Added Sentences2Json: tool to convert sentences.txt to sentences.json

Asset Changes

  • Replaced sentences.txt with sentences.json
  • Removed sentence groups that aren't actual groups
  • Fixed bad sentence replacements used in the Opposing Force & Blue Shift replacement files
  • Reworked sentences so game-specific sentences replace originals

Framerate-independent rope physics

Ropes in Opposing Force don't account for the framerate so higher framerates cause players to apply more force to ropes, and ropes will eventually stop moving if the framerate gets high enough.

This has been fixed. Rope physics has been reworked to behave as it did at 30 FPS.

Sentences changes

The HEV suit's playback list used to store group and sentence names using their index which can change between maps in this mod because it supports custom sentences files. The list now stores the names instead. This also simplifies the API used to play sounds since it can deduce what is being played using the name. It previously required you to tell it whether it was a sentence or a group.

The switch to JSON means the engine can no longer load the mod's sentences files so support for using the engine's sound system has been removed. The OpenAL sound system handles everything just fine with the sole exception of the bell sound in the Hazard Course map. That single case can be fixed by manually adjusting the sound to play as it does under the original sound system.

The new file syntax looks like this:
{
    "Sentences": [
        // Grenade Warning
        "HG_GREN0 hgrunt/clik(p120) grenade! clik",
        "HG_GREN1 hgrunt/(t30) clik take!(e75) cover!(s5) clik",
        "HG_GREN2 hgrunt/clik clik oh! shit! clik",
        "HG_GREN3 hgrunt/(p110 t40) clik(p120) get!(e78) down!(t30) clik",
        "HG_GREN4 hgrunt/clik(p110) (t40) of!(e75) god! clik(p110)",
        "HG_GREN5 hgrunt/clik no! clik",
        "HG_GREN6 hgrunt/clik move! clik(p120)"
    ],
    "Groups": {
        "HG_GREN": [
            "HG_GREN0",
            "HG_GREN1",
            "HG_GREN2",
            "HG_GREN3",
            "HG_GREN4",
            "HG_GREN5",
            "HG_GREN6"
        ]
    }
}
Sentences is a list of strings containing the sentences, just like before.
Groups is an object of group names to lists of sentences that are in that group.

It is no longer required for sentences to start with the group name and it doesn't have to end with a digit either.
This allows another sentences file to replace groups and arbitrarily combine sentences.

A tool called Sentences2Json has been added that can convert the old format to the new. It also prints warnings if invalid or duplicate sentences are encountered. The default file has a few of those.

Here's an example of the BA_POK group being replaced, which is played when you +USE a pre-disaster Barney, as well as the scripted sentence BA_SEC2_NOPASS which plays when trying to continue in the Insecurity chapter before you've got your armor:
{
    "Sentences": [
        "BA_POK0 hgrunt/clik(p110) (t40) freeman you are lay!(e90) team!(e20) clik(p120) clik",
        "BA_SEC2_NOPASS barney/c2a4_ba_longnite"
    ],
    "Groups": {
        "BA_POK": [
            "BA_POK0"
        ]
    }
}
This becomes:
Note that you can reload the map to test changes as opposed to the original game which requires a full restart.

Remaining work to be done

  • Update changelog to include all changes (partially complete)
  • Write documentation for all new features (partially complete)
  • Implement as much of the work scheduled on the issue tracker as possible (done)
  • Review all changes
  • First alpha build
  • Stress test the four campaigns and fix issues that show up (first test done)
  • First beta build
These changes represent the last programming work that had to be completed for V1.0.0. Code-wise this project is now feature complete, which means it's now time to test it and finish the documentation.

This also includes testing the 3 Updated projects independently to make sure they work on their own. That means 7 full campaign playthroughs are required to verify that they work as intended: once in each Updated project and once in the Unified SDK.

New beta builds for the Updated projects will be made available once i've done these tests.

That's about it for now, until next time.
Posted 9 months ago2023-06-25 13:38:05 UTC Post #347650

Progress on the Unified SDK

SDK Changes

  • Fixed scientists crashing when speaking fear dialogue when enemy has been removed (e.g. scientist screaming in fear at human grunt that was just gibbed)
  • Fixed serverctrl tool not compiling when built as 64 bit (note: tool is not compatible with Steam Half-Life due to removed engine functionality)
  • Removed procinfo library (obsolete, the Half-Life SDK uses SSE2 by default which is only available with CPUs newer than what this library tests for)
  • Tools now obtain SDL2 using vcpkg instead of relying on the SDL2 library included with the SDK (allows 64 bit builds to be made)
  • Fixed qcsg & qrad crashing due to pointer-to-int downconversion in 64 bit builds
  • Disabled fall think function for weapons when the player picks it up to prevent possible double-pickup which removes the weapon and crashes the game
  • Implemented cheat_jetpack: When enabled, players can fly by holding down the jump button and accelerate forward when holding the forward movement button
  • Give maximum ammo when giving weapons with impulse 101 (MP5 gets full magazine)
  • Fixed Egon gun resetting animation when another weapon has just been deployed
  • Added checks to ensure talk monsters (scientists, security guards, friendly grunts) only talk to other talk monsters when idle chatting or getting shot by friends
  • Added missing call to PostRestore in weapons code (caused weapons dropped in the world to change models when going through level changes)
  • Allow CBaseMonster::DropItem to drop any entity (fixes Shock Troopers not dropping roaches)
  • Disabled jump sounds while player is frozen
  • Fixed node graph code incorrectly flagging node graphs as out of date if an outdated graph exists in a search path other than the mod directory
  • Ensure auto weapon switch setting is always initialized (fixes not switching to picked up weapons in singleplayer)

C# Changes

  • Removed globalname from crates next to the Dam in c2a5 to stop them from disappearing when entering through level change
  • Removed unnecessary sentence replacements (now handled by bs_sentences.json)
  • Removed dialogue in ba_tram2 that references Freeman and HEV suit in error

Asset Changes

  • Fixed allied grunt sentence using wrong filename ("Ok, check in!" dialogue)
  • Stop looping turretrot1.wav sound played by loader model

cheat_jetpack

This cheat was added to make it easier to traverse maps more quickly and to allow players to reach ledges that are just out of reach when testing maps:

Remaining work to be done

  • Update changelog to include all changes (partially complete)
  • Write documentation for all new features (partially complete)
  • Review all changes
  • Stress test the four campaigns and fix issues that show up (second test done)
  • First release candidate build
I've tested all campaigns and training courses. All of the above changes are fixes for bugs encountered during these tests.

The next step now is to write the changelog and documentation. Features will be tested to verify that documented behavior works as expected. Any bugs found along the way will be fixed.

Once this process is complete the first release candidate builds for all projects will be released.
Posted 9 months ago2023-06-26 19:31:14 UTC Post #347651
@solo
would it be difficult to make a simple menu with different skills, e.g. pistols, medicine, hacking? surely it would be easy to create experience points that the player receives for killing a monster. And a menu with skills? Can you write me a tip in a nutshell? I'll try to figure it out myself, if I have a problem I promise to flood your inbox with spam! :P
but seriously i would like to make a mini rpg like this out of half-life
You must be logged in to post a response.