Forum posts

Posted 1 year 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: https://github.com/SamVanheer/HalfLifeAssetManager/releases/tag/HLAM-V2.0.0-beta007

Changes:
  • 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 year 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: https://github.com/SamVanheer/HalfLifeAssetManager/releases/tag/HLAM-V2.0.0-beta005

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

Changes:
  • 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 1 year 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: https://github.com/SamVanheer/HalfLifeAssetManager/releases/tag/HLAM-V2.0.0-beta002

Changes:
  • 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 1 year 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.

Download: https://github.com/SamVanheer/HalfLifeAssetManager/releases/tag/HLAM-V2.0.0-beta001
Posted 1 year 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 1 year 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:
https://twhl.info/wiki/page/Leak
https://twhl.info/wiki/page/RUST%3A_Ways_to_Prevent_Leaks
https://twhl.info/wiki/page/RUST%3A_Big_Block_Method_of_Error_Finding
https://twhl.info/wiki/page/Tutorial%3A_Diagnosing_Problems
https://twhl.info/wiki/page/Tutorial%3A_How_to_fix_those_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: https://developer.valvesoftware.com/wiki/SDK_Docs
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: https://twhl.info/wiki/page/category%3AEntity_Guides%2BReview_Required

I've already reviewed some entity pages, you can look at this one for an example on how to update those: https://twhl.info/wiki/page/func_button

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: https://developer.valvesoftware.com/wiki/Func_button

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: https://developer.valvesoftware.com/wiki/List_of_entities

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 1 year 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. site:learn.microsoft.com 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 1 year 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: https://en.wikipedia.org/wiki/Quake_engine#Engine_design_and_milestones

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 1 year 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: https://github.com/ValveSoftware/halflife/blob/c7240b965743a53a29491dd49320c88eecf6257b/engine/eiface.h#L262

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");
        return;
    }

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

    CBaseEntity* player = UTIL_PlayerByIndex(index);

    if (!player)
    {
        return;
    }

    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 1 year 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: https://github.com/SamVanheer/HalfLife.UnifiedSdk.MapDecompiler/releases/tag/v0.7.0.0-beta007

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

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

SDK changes

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

C# changes

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

Asset changes

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

Bsp2Obj tool

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

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

Custom sound support for entities

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

See this video for some examples:

Remaining work to be done

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

Other changes to be done

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

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

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

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

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

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

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

Newer code tends to be more flexible.

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

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

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

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

That's about it for now. Thanks to everybody for giving feedback and helping with development, until next time!
Posted 1 year ago2022-11-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: https://github.com/SamVanheer/HalfLife.UnifiedSdk.MapDecompiler/releases/tag/V0.6.0.0-beta006

Changes:
  • Project-wide changes:
    • Renamed Cancel button to Cancel All
    • Added Decompile All Again button
    • Fixed Vector3 parsing not using invariant culture (i.e. your computer's locale settings could cause it to parse origins as 0 0 0)
Posted 2 years 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: https://github.com/SamVanheer/HalfLife.UnifiedSdk.MapDecompiler/releases/tag/V0.5.0.0-beta005

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

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

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

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

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

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

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

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

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

I think that about wraps up all of the work to be done on this project, at least for the time being.
Posted 2 years 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: https://github.com/SamVanheer/HalfLife.UnifiedSdk.MapDecompiler/releases/tag/V0.4.0.0-beta004

Changes:
  • Project-wide changes
    • Replaced fix for Face-To-Brush decompiler normal correction with a generalized fix that works for both decompilers (fixes some bad brushes in cz_recoil and likely other maps)
    • Added Quit menu button
    • Added additional log output to the program log to indicate that jobs are being cancelled
    • Marked cancelled jobs with "(Cancelled)" in the program log
    • Reworked some asynchronous code to be more correct
    • Added option to control WAD file generation
    • Added diagnostic output to show what the Face-To-Brush decompiler is doing
    • Elaborated some more on decompiler accuracy in the readme file
    • Miscellaneous refactoring
This will be the last beta build for a while. Once Avalonia 11 has been released i'll update the program and deal with any breaking changes, then a full release can be considered.
Posted 2 years 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: https://github.com/SamVanheer/HalfLife.UnifiedSdk.MapDecompiler/releases/tag/V0.3.0.0-beta003

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

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

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

Also note that this only works with Half-Life 1 version 30 BSP files. Alpha, beta and early Source engine maps and maps made for engine offshoots like Svengine and Paranoia 2 are not supported.
Posted 2 years 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: https://github.com/SamVanheer/HalfLife.UnifiedSdk.MapDecompiler/releases/tag/V0.1.0.0-beta001

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

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

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

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

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

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

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

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

Arguments:
  <files>  List of files to decompile

Options:
  --strategy <FaceToBrush|Tree>                          Which decompiler algorithm to use [default: Tree]
  --destination <destination>                            Directory to save decompiled maps to. Leave empty to use current working directory []
  --merge-brushes                                        Whether to merge brushes [default: True]
  --include-liquids                                      Whether to include brushes with liquid content types [default: True]
  --brush-optimization <BestTextureMatch|FewestBrushes>  What to optimize brushes for [default: BestTextureMatch]
  --version                                              Show version information
  -?, -h, --help                                         Show help and usage information
For those interested, here's how i ported it to C#:
  1. Clone the source code from https://github.com/id-Software/Quake-III-Arena
  2. Remove all files not referenced by the bspc project
  3. Remove all functionality not related to map decompilation
  4. Remove all decompiler paths for BSP formats other than Half-Life's
  5. Remove support for extracting maps from pak and zip files
  6. Use CppCheck to find unused code in remaining files and remove that code
  7. Remaining code is around 4507 lines of C code
  8. Port decompiler logic starting from its main entry point: https://github.com/id-Software/Quake-III-Arena/blob/dbe4ddb10315479fc00086f08e25d968b4b43c49/code/bspc/map_hl.c#L1067
    • Use Penguinboy's excellent Sledge.Formats libraries to handle BSP loading and MAP and WAD writing
    • Convert code to use C# features like classes, containers and LINQ to streamline implementation
  9. Test implementation to catch typos and translation errors
  10. Test implementation against various maps to find problems that the original tool didn't
  11. Test implementation against maps known to crash the original tool and fix issues
  12. Add command line and GUI programs to interface with the decompiler
    • Use Avalonia to build cross-platform UI
    • Parallel-process maps to speed up decompilation and keep the UI responsive
    • Thread-safe logging requires separation of log output for each job
  13. Implement BSP2Map decompiler
    • BSP2Map works by turning each brush face into a brush of its own. The original face is duplicated, inverted and offset by 1 unit in the direction opposite of what the original face was facing, new brush faces are added to connect the two
Posted 2 years 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: https://media.moddb.com/images/mods/1/14/13305/Evac.jpg In the original (another Twhl content map) there used to be a Tram line down there. In my vision I added a cave instead with a helipad inside it. Nowadays I naturally built all my maps entirely on my own (Not counting donated maps). Still - I believe taking inspirations of other artists can never hurt 'cause that's what it usually is - Art. That's how humanity competes with each other and advances technologically. There's always somebody who thinks they can do things better than the predecessor.

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

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

Fortunately i've managed to build a new one.
Posted 2 years 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:
https://github.com/SamVanheer/halflife-unified-sdk/blob/master/DEVELOPING.md
https://github.com/SamVanheer/halflife-unified-sdk/blob/master/BUILDING.md

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

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

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

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

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

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

Merging Updated projects

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

It's best to leave that as-is.

A new map decompiler

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

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

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

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

What are people's thoughts on this? This isn't a yes or no question, all opinions are welcome.
Posted 2 years 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 README.md file for each project is the starting point for modders now. It provides information about the projects and provides links to other files.

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

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

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

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

Half-Life Updated CMake archived

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

This completes the planned archiving of that project.

Converting Half-Life Updated to CMake

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

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

Merging Updated projects

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

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

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

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

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

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

Other changes

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

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

It is still required to include most of the assets that are in the provided mod installation for things to work properly.
Posted 2 years 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 ent.io logger
      • NPC logging now uses the ent.ai logger
      • NPC scripted behavior logging now uses the ent.ai.script logger
  • Removed ALERT macro

C# code changes

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

Asset changes

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

ALERT logging changes

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

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

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

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

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

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

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

PlayerLogInfo logging

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

(snippet too big to fit in this post)

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

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

KeyValueMatcher

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

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

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

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

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

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

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

Remaining work to be done

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

Thanks to everybody for the feedback and help you've been giving, it's been very helpful.
Posted 2 years ago2022-10-09 18:14:04 UTC
in Half-Life Updated (custom SDK) Post #346959

Conclusion

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

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

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

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

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

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

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

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

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

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

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

Analysis

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

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

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

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

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

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

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

This makes saving state a lot harder.

Source VScript

Main article: VScript

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

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

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

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

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

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

It does make saving state much easier since it only requires serializing the compiled scripts, global variables (of which there are going to be fewer than in Angelscript plugins) and metadata tying entities to their scripts.
Posted 2 years 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: https://twhl.info/thread/view/20055?page=2#post-346080

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

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

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

What differences are there between them

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

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

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

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

What are they

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

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

How do they work

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

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

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

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

Are shared script entities allowed

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

How are multiple scripts dealt with

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

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

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

How does the scheduler work

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

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

class State
{
    string Data;

    int Count = 1;

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

Stateful callbacks are possible using delegates as shown above.

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

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

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

How does trigger_script work

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

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

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

How are hooks handled

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

void MapInit(MapInitEventArgs@ args)
{

}

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

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

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

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

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

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

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

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

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

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

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

    eventSystem.RegisterEvent<MapInitEventArgs>(name);
}

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

    eventSystem.RegisterEvent<SayTextEventArgs>(name);

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

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

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

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

    if (event->Suppress)
    {
        return;
    }
}
It's all very simple.
Posted 2 years 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 2 years 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: https://github.com/SamVanheer/halflife-updated/releases/tag/HLU-V1.0.0-beta13
Half-Life: Opposing Force Updated: https://github.com/SamVanheer/halflife-op4-updated/releases/tag/HLOP4U-V1.0.0-beta010
Half-Life: Blue Shift Updated: https://github.com/SamVanheer/halflife-bs-updated/releases/tag/HLBSU-V1.0.0-beta010

Notable changes

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

Project changes

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

Important change listed separately

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

Git repository changes

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

Github wiki changes

  • Explicitly specified the scope of the projects and added instructions on where to go for help with making mods
  • Added links to TWHL website, Discord server, SDK setup tutorial and Half-Life Updated status thread
Thanks to Shepard, Ronin4862, a1batross, FreeSlave and λλλλλλ for help with these changes.
Special thanks to malortie for greatly improving and standardizing the models used by all games.
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 2 years 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 2 years 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: https://github.com/SamVanheer/halflife-unified-sdk/wiki/Hud-Entity-Info

New game icon

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

Diagnostics output in Installer

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

Continued in next post ==>
Posted 2 years 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 2 years 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: https://github.com/SamVanheer/halflife-updated/releases/tag/HLU-V1.0.0-beta12
Half-Life: Opposing Force Updated: https://github.com/SamVanheer/halflife-op4-updated/releases/tag/HLOP4U-V1.0.0-beta009
Half-Life: Blue Shift Updated: https://github.com/SamVanheer/halflife-bs-updated/releases/tag/HLBSU-V1.0.0-beta009

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

Progress on Half-Life Unified SDK

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

EASTL library

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

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

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

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

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

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

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

Progress on new sentences system

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

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

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

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

Remaining work to be done

  • Merge pull requests into repository (in progress, all pull requests in Updated repos are merged)
  • Implement new sentence playback system, add all sentences (90% complete)
  • Review malortie's work and provide feedback
  • Update changelog to include all changes
  • Review all changes
  • First alpha build
  • Stress test the three campaigns and fix issues that show up
  • First beta build
Many thanks to a1batross, FreeSlave, Ronin4862 and malortie for their continued contributions to these projects. I really appreciate it.
Posted 2 years 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 2 years 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 2 years 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: https://en.wikipedia.org/wiki/List_of_Intel_graphics_processing_units#Gen7

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: https://en.wikipedia.org/wiki/Voodoo3#Models

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: https://bugreports.qt.io/browse/QTBUG-91255
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: https://gamebanana.com/tools/6828

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.
Posted 2 years ago2022-07-17 12:45:17 UTC
in Half-Life Asset Manager Post #346730

Status update on Half-Life Asset Manager

I've decided to write this update to address a few things now since development on the Unified SDK is taking a bit longer than i expected.

TL;DR: Windows 7 will remain as the minimum requirement for the foreseeable future, but this will eventually change to Windows 10. The minimum OpenGL version will eventually change to 3.3, and the Android version has been cancelled.

Ongoing development

I've reworked the project's dependencies to use vcpkg or Git submodules to acquire them all. Only Qt5 requires a separate installation now, handled using Qt's maintenance tool. Vcpkg downloads, builds and installs dependencies during the CMake configure stage which is about the best way to handle C++ dependencies these days (though far from ideal).

Continuous integration has been configured for Windows and produces a usable installation. This is intended to be used for testing only, not for actual use. Since these are dev builds they tend to be unstable and buggy.

This work gets us closer to a working Linux version, but i want to wait to make a Linux version until i've sorted out some other things. I don't know if there's anything else needed to get it working, but since Qt uses OpenGL 3 internally and the program itself uses OpenGL 1.1 there may be graphical issues i'm not aware of. Waiting to make a Linux version until after the switch to shaders might make it easier.

Minimum requirements

I've been looking into the best way to handle changes that involve changing system requirements. Specifically, the minimum version of Windows and OpenGL required to run the program.

Currently you need Windows 7 or newer (32 or 64 bit) to run this program. This requirement is imposed by the Graphical User Interface (GUI) framework used: Qt5.

Since Qt5.7 Windows 7 is the minimum supported version. Qt5.6 was the last version to support Windows XP and Vista. 5.6 was a Long Term Supported (LTS) release which means it received support (bug fixes) for 3 years after release. It was released in Q1 2016 which means support ended in Q1 2019. Backporting HLAM to 5.6 proved too complicated since the program uses features added in newer versions.

HLAM currently uses Qt5.15.2 which is also an LTS release. It released in Q2 2020 so it will be supported until Q2 2023. Support is available only for commercial licensees (HLAM uses the free license). Starting with version 5.15.3 you need a commercial license to use patch versions for this release.

To get support HLAM would need to upgrade to Qt6. Any bugs that prevent the program's intended operation will require that. So far there haven't been any of those (and i doubt there will be), but it is possible that a game-breaker bug will show up in the future.

Qt6 dropped support for Windows 7 and 8.1. There also aren't any 32 bit Qt binaries available although they can be manually built if needed. If an upgrade to Qt6 is required then the program will also be upgraded to 64 bit at the same time if it hasn't already been done by that time.

I'm holding off on switching to 64 bit only because i don't know if there are users that need a 32 bit version. There is no real advantage to switching to 64 bit at this time so supporting 2 versions is unnecessary. If nobody needs a 32 bit version then i can switch to 64 bit at any time.

Additionally, Microsoft has been dropping support across the board for Windows XP and Vista the past few years:

With Visual Studio 2015 they introduced the XP toolset, required to make programs that can run on XP.
Visual Studio 2017 was the last version to provide this toolset.
The Visual C++ re-distributable no longer provides an XP-compatible runtime. Support was removed last year because the certificate used to sign the redist was not renewed. The re-distributable will still install but programs that use it will fail to launch.

Visual Studio 2022 dropped support for targeting Windows Vista. Accordingly, Microsoft's standard library removed support for Vista just a few weeks ago. These changes will make their way to the re-distributable soon, so i'd expect it to stop working on Vista as well.

VS 2022 also can't be installed on Windows 7 and 8.1 anymore. This is a prelude to targeting support being dropped, but that's probably half a decade away.

It is still possible to target Vista by using the 2019 toolset and XP by using the 2017 toolset, but these toolsets are no longer supported and don't support newer versions of C++. In time it will become necessary to choose between using newer language and library features or supporting older platforms.

This is already a problem for XP: the C++17 filesystem API is not fully supported on XP. Half-Life Model Viewer 2 broke as a result. I had to remove some code and disable a feature on XP to get it working again.

Many C++ libraries already dropped support for XP and Vista well before Microsoft ended support themselves. Qt5.7 released in mid 2016, Chromium removed support at around the same time.

Any programs that use these libraries had to drop support to upgrade. This is why Steam dropped support for XP and Vista a few years back. Electron based programs use Chromium under the hood so they have to do this as well. Due to how often exploits are found in Chromium not updating can be a real danger to users.

Although there are places where you can find versions of these libraries that work on XP and Vista they are largely unsupported, closed source hacks. The risk of unofficial binaries containing malware is serious enough to discourage the use of them across the board.

The Dolphin emulator project recently dropped support for Windows 7 and 8.1. They explained their reasons for doing so quite well, and the same reasons apply to XP and Vista support. You can read their blog post for more information: https://dolphin-emu.org/blog/2022/06/09/leaving-a-legend/

I don't plan to drop support for Windows 7 and 8.1 at this time, but support for those platforms will eventually end. Newer versions of HLAM will not support it, while older versions will remain available to users on those platforms.

This covers Windows version support. OpenGL is a bit different.

Continued in next post ==>
Posted 2 years ago2022-07-17 12:44:48 UTC
in Half-Life Updated (custom SDK) Post #346729
<== Continued from previous post

Assets repository

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

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

Remaining work to be done

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

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

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

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

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

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

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

Until next time.
Posted 2 years ago2022-07-17 12:44:43 UTC
in Half-Life Updated (custom SDK) Post #346728

Progress on Half-Life Unified SDK

Make sure to read the wiki for more information.

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

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

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

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

Major changes

New music system

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

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

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

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

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

This video shows the music system in action:

Project configuration changes

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

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

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

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

Project info overlay

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

More information here.

Removal of support for low resolutions

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

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

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

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

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

Changes in HalfLife.UnifiedSdk-CSharp

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

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

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

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

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

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

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

Continued in next post ==>
Posted 2 years ago2022-06-13 10:15:32 UTC
in How to remove line from right of health hud? Post #346631
You can't modify any data structure that's shared with the engine. weapon_data_t is one of those, along with every other structure referenced in delta.lst and others.

If you need to send data to the client you need to use the existing variables. If you run out then you'll need to send data using user messages. If that's not a solution for your problem then there's nothing more you can do.

Also note that data in weapon_data_t is only sent to clients if weapon prediction is enabled. Given that you're trying to send data related to the use key i don't think sending it as part of weapon data is the correct solution. The server is responsible for processing use inputs anyway, so it may not even be necessary to send this data to the client.
Posted 2 years ago2022-05-26 10:40:30 UTC
in Submodels change when monster performs action Post #346553
Barney's one of the oldest NPCs and doesn't use the game's body group APIs, instead it directly modifies the pev->body variable so it doesn't respect the state of body groups.

To fix this in code you should use GetBodygroup and SetBodygroup instead.

You can see how that's done here: https://github.com/SamVanheer/halflife-unified-sdk/commit/2f27727b42a9f4ab7e104d178771b006df572f71#diff-c961f346030c270f9dc49f497b12c437c8a08940336ddad837d025428dc6b526R326-R338

Note that there are other places in the code where these changes are needed.

I'd suggest checking the latest version of that code to see what needs to be changed: https://github.com/SamVanheer/halflife-unified-sdk/blob/master/src/game/server/entities/NPCs/blackmesa/barney.cpp
Posted 2 years ago2022-05-14 16:23:12 UTC
in Half-Life Updated (custom SDK) Post #346520

Remaining work for Half-Life Unified SDK V1.0.0

  • Replacing sounds (for footstep sounds which differ in Opposing Force), requires client side support which is difficult to provide since it involves the client loading replacement map files
  • Replacing HUD sprites (for Opposing Force which uses a digital-like effect on sprites), requires client side support, could hit engine HUD sprite limit. Might need to be delayed until the UI can be reworked to no longer depend on sprites
  • Replacing the engine's music playback system with one that won't stop playing after level changes. Could be complicated to implement properly since there's no easy way to tell when the client disconnects from a server
I'm considering leaving these features for V2.0.0 so as to focus on stabilizing V1.0.0 and getting it released. V2.0.0 would then be released as soon as it's done so there might be a small amount of time in-between, it's impossible to predict.

Other work that's needed for V1.0.0:
  • Get the assets repository up and running, include all versions of the install scripts in it (add, commit, remove so it's in the history). This repo will contain all of the map, model, etc sources as well as any script code used in the game. A script or tool to copy the scripts from the assets repo to the game installation will be needed to streamline the development process (possibly a filesystem watcher that auto-copies files)
  • Integrate malortie's work into the game installation
  • Update packing script to use a timestamp-based naming scheme (PackageName-YYYY-MM-DD-Revision.zip where Revision is the N'th package that's been made) to make it easier to distribute
I've considered putting the game installation in version control but the available options are bad at storing binary data. For example HLEnhanced has 133 commits and has a size of over 1 GB. The Unified SDK's binaries are twice the size of HLEnhanced so the repository would quickly grow in size to eat up a lot of disk space. Git wasn't made for this, and you can't really bend it to work that way without using risky Git commands. And even if you do that you won't be able to pull changes if you wipe the history (Git will see it as essentially an unrelated branch).

Git LFS isn't an option either. Github's data limits for free accounts is 1 GB storage and 1 GB download bandwidth per month. This project alone would quickly use up the storage space and a single multiplayer playtest could use up that bandwidth as well.

Perforce is stated as being able to do this, but as i understand it it works pretty much like Git LFS and there is no free hosting option for any kind of version control that will host files of this size.

So instead i've opted to use a strategy similar to what i've done with the Half-Life Updated projects: package the installation and upload it on the releases page. Old dev releases will be pruned regularly, full releases will stay. It might be possible to automate this so that executing a script packages the installation and auto-uploads it to Github but i haven't looked into that yet.

I would also like to reiterate that this SDK is not ready for development use yet. Once V1.0.0 has been released i will be switching the repository's setup a bit so the master branch tracks full releases so that it always represents the latest stable build, with an unstable dev branch and feature branches that branch off from the dev branch. This will make it easier to make mods with this SDK without accidentally cloning an unstable version.

Note that because some work involves making breaking changes future updates are unlikely to be easy to merge in. It's unlikely that you'll be able to pull V2.0.0 on top of a modified V1.0.0 and expect everything to work. Unfortunately this is the difficulty in making an SDK that is available early while also solving big problems. The alternative is keeping it private or labeling it unstable the entire time which only complicates things.

I'd like to thank malortie for updating all of the models to work with this SDK. He's been doing a lot of good work and this SDK would not be complete without him.

I'd also like to thank Penguinboy for fixing a performance issue with the skill name regular expression which dramatically slowed down map loading. This fix really helped.
Posted 2 years ago2022-05-14 16:23:06 UTC
in Half-Life Updated (custom SDK) Post #346519

Progress on Half-Life Unified SDK

A fair amount of work has been done in recent weeks. These are the highlights:
  • Simplified the logging system to remove the need to create loggers in PostInitialize in some game systems
  • Added file logging with configuration support in logging.json
  • Added IGameSystem interface to help simplify how various systems are initialized and shut down
  • Bunch of code cleanup including removal of redundant forward declarations and extern keyword, removing duplicated code and more
  • Added IMPLEMENT_CUSTOM_SCHEDULES and DECLARE_COMMAND macros to Visual Studio hint file so Intellisense stops showing green squiggles under usages
  • Reworked how client commands are handled on the server side to use a map of commands. Commands can be registered and unregistered at any time, this is done for gamemode-specific commands to simplify command handling
  • Changed how weapon selection is handled to use a proper named client command (previously any weapon_* command was interpreted as a weapon selection command)
  • Added virtual destructor to CGameRules so the destructor runs properly
  • Re-implemented the spectator client command to function outside of the CTF gamemode. This command was made unavailable due to Opposing Force using an older version of the SDK as a base which didn't support spectator mode in all game modes. Opposing Force Updated does this for consistency with the original game, the Unified SDK allows the use of this mode in all game modes just like the Half-Life SDK does (even in singleplayer)
  • The hud color is now saved and restored, allowing it to persist between levels
  • Fixed some Opposing Force code sending ScoreInfo messages with incorrect contents causing a fatal error (Opposing Force doesn't send the player class and team index values because it uses a different method of team-based gameplay)
  • Reworked how the health and model values are set to allow mappers to set custom values for both. Entities now initialize these in OnCreate, if a mapper-provided value exists it will be overridden in DispatchKeyValue. All code uses pev->model now to support custom models, although some edge cases may exist
  • Removed NPC voice pitch restore hack (initial pitch value is set in OnCreate now)
  • Added global model replacement support. This feature lets you globally change references to a model to use another model instead. This does not apply to models used by the engine since those references cannot be changed
  • The PRECACHE_SOUND macro has been removed, either CBaseEntity::PrecacheSound (for entities) or UTIL_PrecacheSound (for global precaching) should be used instead, or g_engfuncs.PfnPrecacheSound if you need to precache a sound unaffected by global model replacement
  • The SET_MODEL macro has been removed, CBaseEntity::SetModel should be used instead
  • Weapons store off the world, view and player models they use to properly precache and restore these models as well as to adjust the active weapon model when changing levels (not intended for use outside of these use cases, but can be made more robust if necessary)
  • Fixed weapon_eagle saving and restoring booleans as integers
  • The player's hud color and suit light type (flashlight or nightvision) can be configured from a configuration file now as well as through an entity and the console
  • Simplified how game configurations are loaded by merging the data apply step into the parse step (more efficient, easier to use, less confusing)
The work that's been done brings us much closer to allowing one to play all 3 games without requiring either code changes or swapping out files.

I've recorded a short video that shows the Half-Life => Opposing Force => Blue Shift transition when changing maps:
The first map has this configuration file:
{
    "Sections": [
        {
            "Name": "HudColor",
            "Color": "255 160 0"
        },
        {
            "Name": "SuitLightType",
            "Type": "flashlight"
        }
    ]
}
These are the default values as well.

The second map has this:
{
    "Sections": [
        {
            "Name": "HudColor",
            "Color": "0 160 0"
        },
        {
            "Name": "SuitLightType",
            "Type": "nightvision"
        },
        {
            "Name": "GlobalModelReplacement",
            "FileName": "cfg/maps/Op4ModelReplacement.json"
        }
    ]
}
This configures the HUD color to Opposing Force green, the suit light type to night vision and it uses the Opposing Force model replacement file, which looks like this:
{
    "models/v_9mmar.mdl": "models/op4/v_9mmar.mdl",
    "models/v_9mmhandgun.mdl": "models/op4/v_9mmhandgun.mdl",
    "models/v_357.mdl": "models/op4/v_357.mdl",
    "models/v_chub.mdl": "models/op4/v_chub.mdl",
    "models/v_crossbow.mdl": "models/op4/v_crossbow.mdl",
    "models/v_crowbar.mdl": "models/op4/v_crowbar.mdl",
    "models/v_desert_eagle.mdl": "models/op4/v_desert_eagle.mdl",
    "models/v_displacer.mdl": "models/op4/v_displacer.mdl",
    "models/v_egon.mdl": "models/op4/v_egon.mdl",
    "models/v_gauss.mdl": "models/op4/v_gauss.mdl",
    "models/v_grenade.mdl": "models/op4/v_grenade.mdl",
    "models/v_hgun.mdl": "models/op4/v_hgun.mdl",
    "models/v_knife.mdl": "models/op4/v_knife.mdl",
    "models/v_m40a1.mdl": "models/op4/v_m40a1.mdl",
    "models/v_penguin.mdl": "models/op4/v_penguin.mdl",
    "models/v_pipe_wrench.mdl": "models/op4/v_pipe_wrench.mdl",
    "models/v_rpg.mdl": "models/op4/v_rpg.mdl",
    "models/v_satchel.mdl": "models/op4/v_satchel.mdl",
    "models/v_satchel_radio.mdl": "models/op4/v_satchel_radio.mdl",
    "models/v_saw.mdl": "models/op4/v_saw.mdl",
    "models/v_shock.mdl": "models/op4/v_shock.mdl",
    "models/v_shotgun.mdl": "models/op4/v_shotgun.mdl",
    "models/v_spore_launcher.mdl": "models/op4/v_spore_launcher.mdl",
    "models/v_squeak.mdl": "models/op4/v_squeak.mdl",
    "models/v_tripmine.mdl": "models/op4/v_tripmine.mdl"
}
The third map uses this:
{
    "Sections": [
        {
            "Name": "HudColor",
            "Color": "95 95 255"
        },
        {
            "Name": "SuitLightType",
            "Type": "flashlight"
        },
        {
            "Name": "GlobalModelReplacement",
            "FileName": "cfg/maps/BlueShiftModelReplacement.json"
        }
    ]
}
Same thing as Opposing Force.

Config files can be included in other config files so a single file can be used to configure entire map packs pretty easily. The server config file can also do all of this so you can use that to configure a mod's default values.

The CTF game mode is the big exception since that still uses hard-coded values. Ideally those will also be configurable at some point.
Posted 2 years ago2022-05-14 16:22:25 UTC
in Half-Life Updated (custom SDK) Post #346518

Half-Life: Opposing Force Updated beta released

A Half-Life: Opposing Force Updated beta has been released: https://github.com/SamVanheer/halflife-op4-updated/releases/tag/HLOP4U-V1.0.0-beta008

Changes:
  • Fixed typo in shephard player model name
  • Fixed clients not getting ScoreInfo message for new players immediately in CTF
  • Fixed crashes when GetClientColor returns null in CTF
Thanks to Shepard for helping test this and uncovering these issues.
This post was made on a thread that has been deleted.
Posted 2 years ago2022-05-05 14:58:43 UTC
in Half-Life Updated (custom SDK) Post #346494

Half-Life Updated betas released

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

Changes for all games:
  • Fixed node graphs being loaded from search paths other than GAMECONFIG path (properly this time)
  • Fixed node graphs being loaded with a null byte appended
  • Fixed "fullupdate" call making a HUD disappear
  • Fixed STL Algorithm Header Errors When Included with Platform.h
Changes for Opposing Force:
  • Fixed not being able to open team menu after a full CTF round has been played
  • Fixed npc_gonome trying to play non-existent sounds
  • Fixed monster_tentacle missing spawnflag 64 (different model option)
I've tested Opposing Force CTF using multiple clients on a LAN. Everything seems to be working fine, so i'm fairly certain that this will be the last beta release before the full release.
Posted 2 years ago2022-04-25 13:12:19 UTC
in Half-Life Asset Manager Post #346472
That error means your computer doesn't support the version of OpenGL that's needed to run the program.

The planned changes for 2.0.0 might solve this problem for you and a few others that have had this error, but i can't guarantee that. I can't give an ETA on when the next version will be out, but i'll try to get this done soon.
This post was made on a thread that has been deleted.
Posted 2 years ago2022-04-10 11:34:22 UTC
in Half-Life Updated (custom SDK) Post #346420

Skill2Json tool release

I've released a beta version of a new tool called Skill2Json. This tool converts original Half-Life skill.cfg files to the Unified SDK's skill.json format.

Download here: https://github.com/SamVanheer/HalfLife.UnifiedSdk-CSharp/releases/tag/Tools-v0.1.0

See the readme for more information: https://github.com/SamVanheer/HalfLife.UnifiedSdk-CSharp#halflife-unified-sdk-csharp-tools

HalfLife.UnifiedSdk.Utilities

This library now has a Tokenizer struct that can extract tokens out of text just like the game's COM_Parse function. I haven't published a new version on Nuget yet since i'm probably adding more to the library soon.