Forum posts

Posted 1 day ago2023-03-23 17:54:41 UTC
in Need help with modding Post #347415
Decompilers can't produce accurate results because there isn't enough information in a HL1 BSP file to produce something that can be compiled back into a map. You'll always need to touch it up after decompiling so you'll need a good understanding of level design to work with those maps.

I recently made an improved decompiler which should be more accurate:

It still requires work to touch up the maps after the fact. This is not something you can skip, no matter which decompiler you use.
There are guides for 2013, you just need to dig a little deeper:
The Valve Developer Community has many articles that cover Source modding:
Posted 3 days ago2023-03-21 15:20:23 UTC
in Half-Life Updated (custom SDK) Post #347408

Unified SDK Pre-alpha build

The previous post didn't include the link to the new pre-alpha build:
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 3 days ago2023-03-21 14:02:47 UTC
in Half-Life Updated (custom SDK) 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 3 days ago2023-03-21 14:02:23 UTC
in Half-Life Updated (custom SDK) 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 2 weeks ago2023-03-04 16:33:15 UTC
in Half-Life Updated (custom SDK) 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 2 weeks ago2023-03-04 16:33:07 UTC
in Half-Life Updated (custom SDK) 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: 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:

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 2 weeks ago2023-03-04 16:32:51 UTC
in Half-Life Updated (custom SDK) 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:
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
g_ClientUserMessages.RegisterHandler("Health", &CHudHealth::MsgFunc_Health, this);
It's probably easier to show how this change affected the codebase:

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)
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 month ago2023-02-09 14:00:19 UTC
in Half-Life Updated (custom SDK) Post #347317

Map decompiler beta 8

Beta 8 has been released:

  • 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 month ago2023-02-09 13:50:41 UTC
in Half-Life Asset Manager Post #347316

Half-Life Asset Manager V2 Beta 8 released

Half-Life Asset Manager V2 Beta 8 has been released:

  • Added explicit conversion from UTF8-encoded std::string to std::filesystem::path to fix sequence files failing to load
  • Fixed arc ball camera distance spinner being limited to 0-99.99 range
  • When launching a default program to open a file, if the file does not exist this will be more clearly indicated (previously file existence and default program checks were reported in a single error message)
Posted 1 month ago2023-02-04 17:32:31 UTC
in Half-Life Updated (custom SDK) 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": [
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 month ago2023-02-04 17:32:09 UTC
in Half-Life Updated (custom SDK) 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

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)

    // 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.
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 month ago2023-02-04 15:39:31 UTC
in Half-Life Updated (custom SDK) 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:
Half-Life: Opposing Force Updated:
Half-Life: Blue Shift Updated:

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 month ago2023-01-28 19:30:22 UTC
in Half-Life Asset Manager Post #347263

Half-Life Asset Manager V2.0.0 Beta 007 released

Half-Life Asset Manager V2.0.0 Beta 7 has been released:

  • Removed the "Use single instance" option. The default behavior is now to always use a single instance. This option was provided to emulate the behavior of older model viewers which could only load one model at a time. Asset Manager can load any number of models and should always use the same instance to share resources
  • Ensured that file logging is initialized after the single instance check to prevent multiple instances from logging to the same file at the same time
  • Updated the installer to use the correct icon for Shell Execute (the icon shown when a file opened through Asset Manager is shown in Windows Explorer)
Posted 1 month ago2023-01-26 17:07:05 UTC
in Half-Life Asset Manager Post #347260

Half-Life Asset Manager V2.0.0 Beta 006 released

Beta 6 has been released:

Note: betas 3, 4 and 5 were faulty and have been removed.

  • Always show progress dialog when loading 10 or more assets, wait only a second to start showing the dialog otherwise
  • Added support for forwarding Counter-Strike Nexon models to another model viewer (no known model viewers exist for this format)
  • Fixed liblist.gam reader not checking if the file was successfully opened
  • When loading multiple assets, if any require loading in an external program the user will be prompted with a dialog after all other models have finished loading. If multiple assets require an external program the user will be given the option to load each file individually or all at once
  • The log file is always created. The log-to-file command line argument has been removed
  • Newlines are removed from sequence names
  • Fixed viewmodel with capital V in prefix not opening in first person view
  • Replaced Studiomodel compiler and decompiler frontends with Crowbar shortcut
  • Allow user to specify additional command line arguments to launch external programs with
  • Optimized asset tab switching
  • Update asset filesystem when the filename changes or when settings are changed
  • Executable files are now placed in the root directory of the installation instead of in the bin directory
  • The Options dialog now remembers which page you were on during a session
  • Added support for texture remapping with a mid value of -01 (see manual for explanation)
  • Updated the manual to better explain color remapping
The new external programs dialog:
Real-time filesystem changes and options page being remembered:
Posted 2 months ago2023-01-17 15:52:20 UTC
in Half-Life Asset Manager Post #347234

Half-Life Asset Manager V2.0.0 Beta 002 released

Beta 2 has been released:

  • Added dropdown menu showing list of all loaded assets for quick navigation
  • Added support for opening multiple files in File Browser
  • Added support for navigating to other directories in File Browser
  • Fixed color settings not loading properly
  • Draw UV map using OpenGL (not in Export UV Map dialog)
  • Optimized UI update logic a bit
  • Fixed enabling player hitbox and disabling show off-screen areas drawing wireframe boxes
  • Log info about merging sequence group & external texture files into main file
  • Reorganized UV Mesh controls
  • Moved Steam Language setting to File System page, added options to control game directory inclusion (e.g. _hd directory)
  • Added game configuration unique id to options page
  • Show drawn polygons count for current scene
  • Fixed mirror model not always working
  • Fixed textures reuploading when switching between assets
Here's an example of many files being loaded:
Note that loading so many of them isn't the recommended way to use this program, it impacts performance. The progress bar should show up more quickly, it currently has a 4 second delay in case loading is done too fast to show it.
Posted 2 months ago2023-01-12 18:44:06 UTC
in Half-Life Asset Manager Post #347231

Half-Life Asset Manager V2.0.0 Beta 001

Half-Life Asset Manager V2.0.0 is now available for beta testing.

This version fixes numerous bugs, adds new features, streamlines and optimizes existing features and provides a better experience than V1.3.0.

Notable changes:
  • Re-implemented hardware accelerated texture view. This fixes bugs with the texture not updating when imported or when the colormap is changed
  • Fixed texture not updating when the texture name is changed to one matching a color remapping name
  • Filenames passed on the command line are now reformed if passed without quotes. This matches the behavior of other model viewers and allows models to be opened through JACK
  • Wave sound files are no longer converted from 32 bit float to 8 or 16 bit PCM. This should improve the audio quality for high quality sounds
  • Fixed sequence FPS using scientific notation if the value is too large
  • Fixed exported textures becoming corrupted due to use-after-free of image pixel data
  • Configuration file data has been changed to use consistent case style. This means existing settings will not be recognized. Many changes have been made that render existing settings obsolete or unusable so it will be necessary to reconfigure the program on first use
  • Multiple assets can be loaded at the same time using the Load dialog. Using CTRL+A it is possible to load all models in a directory at the same time. Note that this may take a few seconds
  • Opening an asset that is already opened will now refresh the existing asset tab
  • Added the option to limited to only one asset open at a time (matches original model viewer behavior)
  • Assets pause when they are not the active asset to save CPU time (improves performance)
  • Added progress dialogs to asset load and close to give the user the opportunity to cancel if the process takes too long
  • Optimized asset load and close to dramatically reduce the time required. This also speeds up program shutdown with many assets opened
  • Redesigned the File List panel to be a more useful File Browser. The File Browser can quickly switch directories based on the available game configurations and can filter files based not on their extension but by checking their contents. Opening a directory is a bit slower as a result but provides more accurate results and allows fast navigation between files. Combining the Limit to one asset at a time feature with the arrow and Enter keys allows switching between models just like the Previous and Next actions in Half-Life Model Viewer 2
  • Added the Messages panel. This panel shows messages from the application. When an error occurs the panel is automatically opened to show the error. It is also possible to enable debug message output to get more information about the program's behavior
  • The Play Sounds and Pitch *= Framerate (renamed to Framerate affects Pitch) checkboxes are now global settings in the new Audio menu
  • Moved Power Of 2 Textures and filter settings to the new Video menu, removed settings from Options dialog and Textures panel
  • Added Multisample Anti-Aliasing option in the Video menu. Changing this setting will re-create the 3D window which may cause a slight flicker
  • Added the option to take transparent screenshots. This can be enabled by checking the Transparent Screenshots option in the Video menu. Note that the background color will still affect transparent objects. To counter this set the background color to black before taking screenshots
  • The StudioModel dock widgets are now saved and loaded to retain their docking position, visible and active states. The Reset Dock Widgets action in the Asset menu resets these to their original state
  • Added option to hide the Controls Bar, Timeline and all Edit Controls (switches to maximized 3D window). These settings are remembered and restored
  • The Save View and Restore View actions now operate globally across all assets, allowing the current camera and camera state to be saved in one asset and restored in another
  • Reworked the Model Info panel into the Show QC Data dialog. This dialog shows the model's data as (pseudo-) QC data
  • Added Half-Life Asset Manager manual. The manual explains what each UI element does and how they behave. There is also information on features like color remapping
  • Added vertical sync option
  • Added the option to specify the filename for Quake 1 Model Viewer, Source 1 Model Viewer and Xash Model Viewer to delegate the loading of models from these engines to
  • Added the event Ids 1011, 1012 and 1013 from Opposing Force as valid sound events
  • Added the option to specify which event Ids correspond to sound events in addition to built-in event Ids
  • Reworked game configurations to use Hammer-like configuration
  • Added game configurations auto-detect wizard to automatically find and add all games and mods in a game installation
  • Each asset now uses its own filesystem configured for it based on the asset file location to automatically locate related files correctly (e.g. sounds referenced by events)
    • This works even if the asset isn't located in a game directory; if the file is located in a game asset directory like models any directories related to it will be used to locate files. A common case is a downloaded model with related sounds which will play correctly.
    • This also supports the use of fallback directories, so a Condition Zero model can reference a sound file from Counter-Strike and play it as it would in-game
    • This also detects and uses language-specific directories
  • The StudioModel renderer is now shared between assets, dramatically reducing memory usage (nearly 3MB per asset)
  • Reworked user interface to reduce overall size to allow the window to be made smaller
  • Reworked the 3D window to use a single shared instance for all 3D rendering. This reduces the amount of graphics resources allocated for use with windows. Note that this stops models from rendering in the normal window while the fullscreen window is open
  • Added option to switch between Perspective and Orthographic projection in Scene view for the Arc Ball camera. Orthographic mode tries to maintain a good zoom to match the equivalent Perspective zoom, but this behavior is limited and does not work well with unusual window aspect ratios
  • Camera field of view settings are now remembered
  • Added the Enable Texture option to the Scene->Ground panel. Enabling this without a texture loaded now uses an auto-generated grid texture
  • Added the option to choose the aspect ratio used by the Show Guidelines option
  • Added Show Off-screen Areas option to mask portions of the 3D window that are invisible in-game when using the selected aspect ratio
  • It is now possible to edit a sequence's looping flag framerate, activity, activity weight and linear movement vector. Changing the linear movement vector also updates the linear movement flags (LX, LY and LZ)
  • Added support for custom activities using the ACT_<number> syntax supported by the studiomdl compiler. This fixes tentacle2.mdl not showing the right activities
Some of the most drastic UI changes can be seen here:
User posted image
The full changelog can be found here.

Check the manual for more information about how program features work.

All feedback is welcome.

Posted 2 months ago2022-12-31 17:09:28 UTC
in define new BITS_COND for monsters Post #347216
That is the definition for it. The condition is set in a couple places using: SetConditions(bits_COND_LIGHT_DAMAGE);

The actual conditions under which it is set can vary from NPC to NPC.
Posted 2 months ago2022-12-31 16:59:59 UTC
in Wiki enhancment thread Post #347215
The tutorials on this site are hosted on the wiki. Wikis are collaborative, so tutorials aren't going to be written entirely by a single person.

A lot of the articles we've got are old and outdated and need reviewing and improving. They also need to be condensed and combined, like for example we have too many articles on leaks:

Not all articles are purely about leaks but there is redundant information here. Ideally only one page would cover the subject, linked from anywhere else that it's mentioned. The page format should be clear and straightforward:

What are leaks, what causes them

Compile tools require maps to be sealed because...

How to find the cause of a leak

Point files, any notes about loading them in various map editors

How to prevent leaks from occurring

Best practices

Examples of leaks

General case: map is visibly not sealed
Less general case: a brush entity is used to seal the map, special mention to func_detail not sealing maps
Off-grid brushwork
Complex geometry causing leaks due to precision errors converting data

More generally we should add more information on the main wiki page to explain that there are Half-Life 1 and Half-Life 2 modding tutorials, what the differences are and what to expect.

The main page for each engine should then have a list of each modding category along with a short description. Currently these pages rely on wiki categories to provide this information and it's confusing to newcomers.

The Valve Developer Community does this:
User posted image
Each of those categories should then have a page that covers the subject in more detail, along with a list of tutorials. Ideally tutorials would be split into 2 lists: ones that are known to be up-to-date and relevant and archived ones that are outdated, possibly referencing obsolete tools or older methods.

This keeps the older tutorials around without confusing people as to whether they're applicable or not.

Each category should also have an "introduction to <category>" tutorial to explain the basics.

Some information that definitely needs to be there is:
  • Warning against the use of older tools like Jed's Model Viewer, Milkshape 3D, Hammer. People still use these even though they're old and obsolete. We should find out where they're getting these from to ensure that they're not getting bad advice right out of the gate.
  • Making sure that people understand what it means to make a mod using the source code. C++ is one of the hardest languages out there, copy pasting stuff isn't going to work for anything beyond the simplest of changes. Unfortunately people seem to think that this is like copying configuration files or scripts that are loaded in isolation by the game, which don't have to deal with the many rules that C++ has. A basic understanding of computer programming is essential to learn to modify the source code, at the very least knowing variables, functions, types and the concept of function calls, as well as understanding how header and source files work is needed to do common tasks like making a modified version of an NPC or weapon. We should find good tutorials that explain these things to make that easier to do.
  • Link to existing documentation like JACK's manual.
  • Properly explain how to set up JACK for mapping, ensure things like getting up-to-date compile tools and setting them up correctly is covered. We've seen some people using outdated compilers as well as running tools in the wrong order.
  • See my previous post for a lot more of this.
We should also add a page explaining how the engine works, its architecture, where it looks for stuff, engine limits and which ones can be changed (and why others can't be).

I'd like to take a crack at this but i'm preoccupied with other projects. If anybody wants to write these i can put together a cliff notes version of these, maybe add barebones pages without formatting but otherwise if i can find the time i'll do it myself.

Some easy work that can be done is to deal with the pages that require review:

I've already reviewed some entity pages, you can look at this one for an example on how to update those:

Specifically the header is what needed updating in most cases. Some entities also listed keyvalues with options that didn't apply to them, specifically sound options where the user was told to figure out which ones were relevant. I removed the unused ones and updated the others with sound and sentence names.

I don't know what the plan is with regards to the Team Fortress Classic versions which are listed on the same page. Those could be separated out or merged using the same approach as what's done on the VDC. Here's an example:

This is a fair amount of work, best done in stages (fix header formatting and irrelevant content issues first, then reformat). Figuring out how to structure the information and indicate game-specific features should be done before making any changes.

It would probably help to make a dedicated page for the entity list, just like the VDC does it:

This is more organized and easier to search through, and we can add information here that a category won't allow for.

Does that sound good?
Posted 2 months ago2022-12-25 17:57:40 UTC
in Wiki enhancment thread Post #347200
We added a new section on the website's main page to make the wiki more visible. It should help people find it and get started.

I also cleaned up some entity guide pages for entities but there is a lot more work needed there.

I've got some notes on what needs to be improved:

In general: prefer existing documentation (e.g. Microsoft website resources that explain a feature are sufficient)
  • Make the wiki more visible. The recent addition to the main page helps but users should be drawn to it as much as possible.
  • Update tutorials for modern tools, try to condense information that is spread across multiple pages. E.g. there are several wiki pages that cover leaks, merging them and streamlining the information will go a lot further to help.
  • Rewrite tutorials to use third person perspective (i.e. no "i" or personal anecdotes).
  • A page that covers the essentials of using a PC. Things like using Windows Explorer, configuring it to always show file extensions, using the command line, using Windows Search, using Google Search with filters. Web browsers plus recommended add-ons (UBlock Origin at minimum). This page should bring users up to an expected base knowledge level for other tutorials. Some of this stuff is likely overkill for most people but it's better to establish the expected skill level up front.
  • A list of programs that are recommended for use:
    • Notepad++ for general purpose text editing
    • Visual Studio Code for various programming and scripting languages
    • Visual Studio Community for C++ use and Half-Life modding
    • WinMerge for finding differences between text files and directories
    • Irfanview and GIMP for viewing and editing images
    • GoldWave for audio editing (cue points specifically)
    • Audacity for general audio editing
    • Hex editor for viewing and editing binary files (personally use HxD, may be outdated, need suggestions)
    • OBS for video recording
    • Kdenlive for video editing
    • Blender for modeling
    • SourceTree for Git GUI (alternatively Github Desktop)
    • JACK for mapping (Trenchbroom HL support is getting good as well)
    • Half-Life Asset Manager for model viewing and editing
    • Paranoia 2 Model Viewer for Xash models and as an alternate model viewer
    • Virtualbox for running virtual machines (useful for Linux development on Windows)
    • Putty for connecting to servers over SSH
  • List of programs that are NOT recommended for use:
    • Hammer (outdated, buggy)
    • Milkshape 3D (outdated, hard to use and lacking in modern tool support)
    • Jed's Model Viewer (buggy, limited, replaced by better alternatives)
  • A page that explains how to use the search function in various commonly used programs (cover regular expressions in particular):
    • Notepad++: CTRL+F
    • Visual Studio:
    • Github: search in repository, explain filters including negation with particular emphasis on path filtering (e.g. -path:tests filters out results in the tests directory)
    • Explain how to search for compiler errors (e.g. C<error code> or LNK<error code>)
  • A page that explains how to use Git effectively:
    • Cloning
    • Creating local repository
    • Checking out branches
    • Merging, pulling, pushing, fetching, rebasing, stashing
    • Importance of committing changes, descriptive commit messages, how and when to use soft and hard reset
    • Prefer use of a GUI like SourceTree over the command line
  • Instill a proactive mentality: search first using the various search tools available, check tutorials and documentation (e.g. TWHL wiki), try to solve a problem before asking for help. Explain importance of trying yourself (experience gained is more valuable than anything, allows for improved problem solving over time)
  • Explain importance of learning C++ before trying to make a mod. Many beginners underestimate the difficulty in making a mod without having requisite knowledge of the language and assume copy pasting code is sufficient. The average beginner needs several months of experience before they can realistically make a mod. Though copy pasting may work for some cases it will fail for anything that involves copy pasting externally visible symbols like non-static free functions. Explain how differences between mods can make copy pasting difficult to impossible and requires knowledge of the language to modify code.
  • List of common compiler errors categorized by compiler (MSVC, GCC, Clang) and by compiler step (compiler itself, then linker, many commonly reported errors are linker errors)
  • List of errors shown by the game categorized by type (host error, sys (fatal) error, errors logged in console), include error message template and example as well as common errors if relevant
  • Explain how to debug errors using qconsole.log
  • Explain how to debug crashes (using qconsole.log, debuggers)
  • Explain the concept of remote procedure calls and show how events and user messages are primitive implementations of the concept
Posted 3 months ago2022-12-17 14:12:11 UTC
in Ok, here goes: question, why is a LEAK bad? Post #347178
When a map has a leak the compiler can't tell which parts of the map are inside and which are outside. The visibility and radiosity compilers won't run so the game can't determine which parts of the map are visible and which aren't, so the entire map (including the normally discarded exterior) is rendered at the same time. The map is also fullbright since there is no lighting data.

This will also affect networking since entities are checked for visibility using the data that won't exist so it will always be deemed visible, so if you have a lot of visible entities you'll end up with invisible entities.

The compile tools need the map to be sealed so the outside can be discarded. Otherwise you'd have calculations that expect to hit a brush face at some point that just keep going off into the void forever (or until they hit the maximum map size).

The wiki article on Quake 1 explains this stuff as well:

You don't really notice how important this is until you run a map on an older computer (very early 2000s or older). A modern computer can brute force render the entire map, unless it has poor immediate mode OpenGL support and the map has dynamic lights which causes graphics performance to drop significantly.
Posted 3 months ago2022-12-05 17:16:30 UTC
in Create server commands? Post #347155
There are no examples of server commands in the SDK but it's pretty straightforward.

To add a command you need to call this function:

This registers the command with the given name.

Here's an example:
void PrintPlayerName()
    if (CMD_ARGC() < 1)
        g_engfuncs.pfnServerPrint("Usage: print_player_name <player_index>\n");

    const int index = atoi(CMD_ARGV(1));

    CBaseEntity* player = UTIL_PlayerByIndex(index);

    if (!player)

    g_engfuncs.pfnServerPrint(UTIL_VarArgs("Player name: %s\n"), STRING(player->pev->netname));

// Somewhere in startup code.
g_engfuncs.pfnAddServerCommand("print_player_name", &PrintPlayerName);
This command prints the name of the player at the given index, or nothing if the player slot isn't in use.
You can print to console using:
UTIL_ClientPrintAll(HUD_PRINTCONSOLE, "your message here\n");
You can also loop over all players and use:
CLIENT_PRINTF(client, print_console, "your message here\n");
The first method goes through the client dll first, the second goes through the engine.
Posted 3 months ago2022-11-28 15:34:23 UTC
in Half-Life Updated (custom SDK) Post #347137

Map decompiler beta 7

I've released the seventh beta for the new map decompiler:

  • 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 3 months ago2022-11-26 22:18:51 UTC
in Half-Life Updated (custom SDK) Post #347132

Progress on Unified SDK

I've released a new pre-alpha build for testing:

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 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:

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:

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 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 4 months ago2022-11-23 12:49:02 UTC
in Half-Life Updated (custom SDK) Post #347126

Map Decompiler beta 006

I've released the sixth beta for the new map decompiler:

  • 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 4 months ago2022-11-18 15:43:13 UTC
in Half-Life Updated (custom SDK) Post #347098

Map Decompiler beta 005

I've released the fifth beta for the new map decompiler:

  • 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:

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:
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 4 months ago2022-11-15 15:48:59 UTC
in Half-Life Updated (custom SDK) Post #347090

Map Decompiler beta 004

I've released the fourth beta for the new map decompiler:

  • 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 4 months ago2022-11-12 16:27:20 UTC
in Half-Life Updated (custom SDK) Post #347079

Map Decompiler beta 3

I've released the third beta build for the new map decompiler:

  • 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 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 4 months ago2022-11-10 18:41:30 UTC
in Half-Life Updated (custom SDK) Post #347074

Map Decompiler beta 2

I've released the second beta build for the new map decompiler:

  • 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 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 4 months ago2022-11-07 15:53:55 UTC
in Half-Life Updated (custom SDK) Post #347054

Map Decompiler beta build

I've released a first beta build for the new map decompiler:

  • 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 4 months ago2022-11-06 18:21:19 UTC
in Half-Life Updated (custom SDK) 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.

  • 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:

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

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

  <files>  List of files to decompile

  --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
  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:
    • 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 4 months ago2022-11-06 18:21:13 UTC
in Half-Life Updated (custom SDK) 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: 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 4 months ago2022-11-01 12:30:18 UTC
in Half-Life Updated (custom SDK) 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:

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 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 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:

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 4 months ago2022-10-29 15:04:23 UTC
in Half-Life Updated (custom SDK) 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 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 Installation instructions are in The changelog is in The full changelog is in

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

The Unified SDK's documentation is located in the docs directory. The 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.

  • 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:

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 5 months ago2022-10-22 17:44:05 UTC
in Half-Life Updated (custom SDK) 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 logger
      • NPC logging now uses the logger
      • NPC scripted behavior logging now uses the 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 logger and scripted sequences using 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:

(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);
    // 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.


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:

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 5 months ago2022-10-09 18:14:04 UTC
in Half-Life Updated (custom SDK) Post #346959


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/"
[sv] [angelscript] [trace] Trying to create module
[sv] [angelscript] [debug] Creating module "Plugin:TestPlugin:maps/"
[sv] [angelscript] [debug] Adding generated code
[sv] [angelscript] [debug] Adding script "c:\program files (x86)\steam\steamapps\common\half-life\hlu\scripts\maps\" (root)
[sv] [angelscript] [trace] Encountered #include "maps/" in "c:/program files (x86)/steam/steamapps/common/half-life/hlu/scripts/maps/"
[sv] [angelscript] [debug] Adding script "c:\program files (x86)\steam\steamapps\common\half-life\hlu_downloads\scripts\maps\" (depth 1)
[sv] [angelscript] [trace] Encountered #include "maps/" in "c:/program files (x86)/steam/steamapps/common/half-life/hlu_downloads/scripts/maps/"
[sv] [angelscript] [debug] Adding script "c:\program files (x86)\steam\steamapps\common\half-life\hlu\scripts\maps\" (depth 2)
[sv] [angelscript] [debug] Successfully built module "Plugin:TestPlugin:maps/"
[sv] [angelscript] [trace] Creating plugin script module with script "maps/"
[sv] [angelscript] [trace] Trying to create module
[sv] [angelscript] [debug] Creating module "Plugin:TestPlugin:maps/"
[sv] [angelscript] [debug] Adding generated code
[sv] [angelscript] [debug] Adding script "c:\program files (x86)\steam\steamapps\common\half-life\hlu\scripts\maps\" (root)
[sv] [angelscript] [debug] Successfully built module "Plugin:TestPlugin:maps/"
[sv] [angelscript] [trace] Creating plugin script module with script "plugins/"
[sv] [angelscript] [trace] Trying to create module
[sv] [angelscript] [debug] Creating module "Plugin:TestPlugin:plugins/"
[sv] [angelscript] [debug] Adding generated code
[sv] [angelscript] [debug] Adding script "c:\program files (x86)\steam\steamapps\common\half-life\hlu\scripts\plugins\" (root)
[sv] [angelscript] [debug] Successfully built module "Plugin:TestPlugin:plugins/"
[sv] [angelscript] [trace] Executing function "void ScriptInit()" (Plugin:TestPlugin:maps/
[sv] [angelscript] [info] [Plugin:TestPlugin:maps/] Hello World!
[sv] [angelscript] [trace] Module "Plugin:TestPlugin:maps/": 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/"
[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/,2
[sv] [angelscript] [trace] Module "Plugin:TestPlugin:maps/": subscribing callback "void MapInit(MapInitEventArgs@)" to event "MapInitEventArgs"
[sv] [angelscript] [trace] Scheduler::Schedule: Scheduled callback "void State::Callback()" from module "Plugin:TestPlugin:maps/" 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/
[sv] [angelscript] [info] [Plugin:TestPlugin:plugins/] 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/"
[sv] [angelscript] [trace] Trying to create module
[sv] [angelscript] [debug] Creating module "Map:MapPlugin:maps/"
[sv] [angelscript] [debug] Adding generated code
[sv] [angelscript] [debug] Adding script "c:\program files (x86)\steam\steamapps\common\half-life\hlu\scripts\maps\" (root)
[sv] [angelscript] [trace] Encountered #include "maps/" in "c:/program files (x86)/steam/steamapps/common/half-life/hlu/scripts/maps/"
[sv] [angelscript] [debug] Adding script "c:\program files (x86)\steam\steamapps\common\half-life\hlu_downloads\scripts\maps\" (depth 1)
[sv] [angelscript] [trace] Encountered #include "maps/" in "c:/program files (x86)/steam/steamapps/common/half-life/hlu_downloads/scripts/maps/"
[sv] [angelscript] [debug] Adding script "c:\program files (x86)\steam\steamapps\common\half-life\hlu\scripts\maps\" (depth 2)
[sv] [angelscript] [debug] Successfully built module "Map:MapPlugin:maps/"
[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/
[sv] [angelscript] [trace] Executing function "void MapInit(MapInitEventArgs@)" (Map:MapPlugin:maps/
[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/"
[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/"
Posted 5 months ago2022-10-09 18:11:37 UTC
in Half-Life Updated (custom SDK) 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.


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 5 months ago2022-10-09 18:11:21 UTC
in Half-Life Updated (custom SDK) Post #346957

Research done on implementing scripting system

Last year i outlined how a scripting system could be implemented:

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": [
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": [
    "Sections": [
            "Name": "Scripts",
            "Scripts": [
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);
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()

void MapInit(MapInitEventArgs@ args)


void Callback(SayTextEventArgs@ foo)
    log::info("Callback executed");
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
    EventArgs() = default;

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

class MapInitEventArgs : public EventArgs
    MapInitEventArgs() = default;

class SayTextEventArgs : public EventArgs
    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";


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


    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();

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

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)
It's all very simple.
Posted 5 months ago2022-10-09 18:10:55 UTC
in Half-Life Updated (custom SDK) 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 5 months ago2022-10-09 18:10:33 UTC
in Half-Life Updated (custom SDK) 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:
Half-Life: Opposing Force Updated:
Half-Life: Blue Shift Updated:

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.
The game does have trouble with that code, the log spam can seriously slow down node graph generation. Having developer mode enabled causes it to take much longer due to having to format and print the error message.

But the game does eventually continue, you just have to wait it out.
Posted 6 months ago2022-09-17 16:35:37 UTC
in Half-Life Updated (custom SDK) 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 6 months ago2022-09-17 16:35:01 UTC
in Half-Life Updated (custom SDK) 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:

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 6 months ago2022-09-16 21:29:24 UTC
in HL1 Mod HUD suddenly disappeared? Post #346881
It's caused by the larger resolution. The armor hud is positioned at a fraction of the resolution so the greater the resolution the further right it moves.
Posted 7 months ago2022-08-06 14:01:17 UTC
in Half-Life Updated (custom SDK) 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:
Half-Life: Opposing Force Updated:
Half-Life: Blue Shift Updated:

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 8 months ago2022-07-17 17:31:38 UTC
in "Host_Error: Couldn't get DLL API from !" Post #346736
It looks like it's trying to load the server dll on startup, but that should only happen if it's a dedicated server.

You are trying to use the Steam version right? Not Xash or some old version?
Posted 8 months ago2022-07-17 16:35:32 UTC
in "Host_Error: Couldn't get DLL API from !" Post #346734
That error means the server dll doesn't export the API used by the game, but if it's not printing the dll name then something else is going wrong.

Run the game with the command line arguments -condebug + developer 1.

Run the game until you get the error, then check the contents of Half-Life/qconsole.log. It should have logged some information.
Posted 8 months ago2022-07-17 12:45:25 UTC
in Half-Life Asset Manager Post #346731
<== Continued from previous post

OpenGL requirements

HLAM's user code currently uses OpenGL 1.1, typically referred to as immediate mode. Pretty much anyone using a graphics card made this century can run it, though performance may vary. Qt5 uses OpenGL 3 features but they are optional.

Qt itself uses OpenGL through an abstraction layer that exposes the OpenGL ES (OpenGL for Embedded Systems, aka mobile devices) 2 and 3 APIs.

This abstraction has multiple implementations. By default Qt tries to pick an OpenGL implementation that provides the most complete support. If your driver supports OpenGL 3 then it will use desktop OpenGL. Otherwise it will fall back to using Google's ANGLE library to provide support, which means it may use Direct3D under the hood. It's also possible to use a Software implementation.

HLAM's user code (model rendering code) doesn't use Qt's API for OpenGL rendering. It instead uses a library called GLEW (OpenGL Extension Wrangler). This library works only with desktop OpenGL, which means HLAM requires the use of desktop OpenGL.

The use of this library caused some problems because some users have a graphics card that doesn't support OpenGL well enough, causing Qt to use ANGLE instead of desktop OpenGL which broke things.

HLAM currently forces the use of desktop OpenGL. I plan to remove GLEW and rely on Qt's API for OpenGL to allow the use of ANGLE which should ensure maximum compatibility.

Independently of this change i will also be rewriting the graphics code to use modern OpenGL (using shaders, retained mode). This means the minimum requirement will be OpenGL 3.3.

Although OpenGL 3.3 was released in 2010 the first graphics cards to support it were released in 2007: Intel integrated cards lagged behind, the first card to support 3.3 and newer on Windows was released in 2012:

Requiring OpenGL 3.3 means you'll need a graphics card made in the last 15 years. To put that into perspective, it's like requiring a Voodoo 3 card in 2014:

Because there are some users whose systems don't support OpenGL 3.3 i will hold off on making that change until i've done a bunch of other work first. There is a lot of refactoring, user interface and bug fixing work that needs doing so i'll do that first. I'll leave the switch to shaders until after that point.

Unfortunately it is not possible to support both versions of OpenGL. Immediate mode and retained mode are very different in how they work. I've also looked into supporting OpenGL 2.0 as a fallback, but the differences between that and 3.3 are significant enough that supporting both at once is a very complicated task.

As with Windows i don't want to raise the system requirements, but the use of immediate mode makes adding features that require graphics support more difficult. Performance issues on newer cards that don't support immediate mode well are also an issue, and the use of such an old API can cause the driver to switch to less efficient ways of handling things behind the scenes (which is one of the reasons why Vulkan exists).

OpenGL 3.2 added the concept of core and compatibility contexts. Compatibility contexts allow the use of immediate mode while core contexts do not. I expect that immediate mode and everything that interacts with it will suffer from performance issues, now or in the future. OpenGL 2.0 shaders were built on top of immediate mode, so i expect that to run into problems as well at some point.

I want to make sure this program continues to function correctly and efficiently for a long time, so using OpenGL 3.3 is the best option here, at least for the foreseeable future.

Qt6 introduced a new API called QRhi (in preview in Qt5, and some of the tools are restrictive in license) that allows you to switch between OpenGL 3, OpenGL ES 2, D3D11 and Vulkan. If and when HLAM upgrades to Qt6 this API could be used to allow users to pick the backend that performs best. Hopefully this would also allow users to sidestep issues with APIs no longer being fully supported.

I'm not planning to do this at this time since it requires Qt6. The licensing seems to have changed, but only for Qt6.3 onwards. It's something to keep in mind for the future. I expect that the amount of work won't be anywhere near the amount required for the move from immediate mode to shaders since QRhi supports OpenGL 3 and uses cross-compiling to convert shaders.

Android version

There have been a few requests for an Android version. I've said in the past that an Android version would require at minimum a working Linux version and shaders. I've looked into this further to determine whether is it viable to provide an Android version.

Making a native program work on Android requires the use of a Java wrapper. This is mostly automated for Qt CMake projects from what i've found so it shouldn't be a problem, but it does add some complexity.

HLAM depends on third party libraries. These have to be available on Android for the program to work. Qt5 supports Android and OpenAL-Soft provides Android support, though it appears it has to be built manually.

A recent issue seems to have popped up that makes targeting newer versions of Android (Android 11, released almost 2 years ago) more difficult when using Qt:
Since Qt5.15 is no longer supported for non-commercial licensees this is possibly a deal-breaker. Qt6 would be required for HLAM to target newer Android versions.

HLAM is designed for use in desktop environments. Android requires a completely new user interface to rely on touch-screen input. I doubt it will be possible to provide the entire interface.

I suspect all users requesting an Android version are doing so because they are playing Half-Life using Xash for Android. Xash uses a slightly modified version of the Studiomodel format that makes it incompatible with HLAM. HLAM loads models and converts them to allow them to be edited. Since Xash re-purposes unused data this causes the program to crash.

My solution for this will be to add a means of detecting this format and forwarding users to Paranoia 2 Model Viewer if they have it installed and HLAM is configured to know of its location. In the long term, if and when a plugin system is added it may be possible to load these models in HLAM directly but that's not possible at this time because Xash is GPL-licensed, which would require HLAM to also be GPL-licensed.

HLAM still uses Half-Life SDK code (slowly getting refactored out, almost all of the remaining code will be replaced with the shader update) and is intended to be MIT licensed (which i need to sort out as well), so Xash's code can't be used here. I'm not a lawyer, so i don't know under what conditions it is possible to use that code here. The research i've done is inconclusive about this.

Although Xash for Android requires the original game to be installed along with models that don't include the new data, there is no way to know just how many models use the unmodified format and how many don't. An Android version could be completely useless if a significant number of models can't be loaded.

I also don't have an Android tablet to work with, only a phone which limits my ability to develop and test such a version.

Lastly, there have been only a handful of these requests. Developing an Android version is a lot of work, and justifying it considering the small number of requests is difficult. There is a lot of work needed elsewhere such as adding support for other model formats and other asset types (hence the name) so an Android version would always keep getting pushed back.

I don't want to promise a feature that will never be made, so i've decided to cancel this version. I should've done this sooner but i expected to finish the Unified SDK a lot sooner. The delays ended up pushing this further back, which is why i've investigated it now to make a decision.

For Android users that want to view models on Android i'd recommend directing your requests to the Paranoia 2 Model Viewer developers. The GameBanana page has links relevant to this:

Near future

I'm still working on the Unified SDK so until V1.0.0 is done i won't be working on HLAM. It is getting closer to being finished so hopefully i can get things going soon.

See the Half-Life Updated thread for more information about that.