Skip to content

Black Mesa support - #78

Open
MyGamepedia wants to merge 10 commits into
TF2-DMB:masterfrom
MyGamepedia:bms
Open

Black Mesa support#78
MyGamepedia wants to merge 10 commits into
TF2-DMB:masterfrom
MyGamepedia:bms

Conversation

@MyGamepedia

@MyGamepedia MyGamepedia commented Jul 23, 2026

Copy link
Copy Markdown

Full Black Mesa support, a couple of bug fixes, added Black Mesa nav mesh/area features. Compiles under SourceMod 1.12.
Everything was tested on Windows build, the Linux build needs some tests (cuz i was unable):

  • Does it compile ?
  • Does it work without errors ?

It's also better to check if TF2 build is working without errors.
Includes cbasenpc_bmsnavtest, so you can test bms nav features pretty quickly.
I expect this needs a review, I'm not too familiar with how extensions are coded, so I guess some things could be done better, but at very least, the Windows build is working pretty well. Didn't test absolutely everything, but overall I'm happy with how it works. Here is demo I used for the extension abilities showcase in Crowbar/HECU Collective discord servers: https://youtu.be/zxkjcR4G7MQ

Full changes log:
cbasenpc.txt:

  • added bms signs, offsets, etc.

AMBuildScript:

  • now compiles black mesa.

configure.py:

  • should work now

AMBuilder:

  • split bms and tf2 things, tf2 gameruls and tf2 nav/area to tf2, bms nav/area to bms
  • included cbasenpc_bmsnavtest.sp

cbasenpc_internal.cpp:

  • tf2 gamerules stuff is now under macro

extension.cpp:

  • tf2 stuff under tf2 macro
  • proper unload so if it doesn't load it doesn't crash the game

helpers.cpp:

  • iirc the changes for 1.12 compile

natives.cpp:

  • removed double basecombatcharacter::setup

natives\bms:

  • black mesa has custom classes for CNavMesh/CNavArea with CBlackMesa prefix.

entityfactory.cpp, behavior.cpp:

  • changed context->GetContext() to context so it can compile

nav.cpp:

  • split bms and tf2 nav.cpp version via macro

takedamageinfo.cpp:

  • throw error for black mesa if used GettDamageForForceCalc/SetDamageForForceCalc

serialrefresher.cpp:

  • used different algorithm to get framesnapshotmanager in bms

baseanimating.cpp, baseanimating.h:

  • bms wil use offset for CBaseAnimating::mSequenceDuration
  • used different algorithm to get GetAnimationEvent in bms

baseentity.cpp, baseentity.h:

  • new code to get CBaseEntity::PhysicsMarkEntitiesAsTouching and g_TouchTrace.
  • black mesa will use trigger_auto_crouch for BEGIN_VAR instead of trigger_stun.
  • added CBaseEntity::SDK_OnUnload() for proper unload.

bms_nav_area.h:

  • CBlackMesaNavArea class.

nav_mesh.cpp:

  • will give more info when unloaded for CNavMesh::Load reasons.
  • used symbols for bms linux build.

takedamageinfo.cpp:

  • m_flDamageForForce moved as tf2 only.

toolsnav_mesh.h:

  • added "vstdlib/random.h" to compile.

cbasenpc.txt:

  • new data + fixes to prev commit

PackageScript:

  • bms folder included

bms\nav.inc:

  • methodmap for CBlackMesaNavArea

pluginentityfactory.h:

  • added sensitive classname compare fix, need for newlights and gonarch spawn.

NextBotEventResponderInterface.h:

  • added OnThreatChanged(CBaseEntity*) for bms

nav_area.h:

  • bms crash fixes

NextBotGroundLocomotion.h:

  • bms crash fix

bms/activity.inc:

  • bms activity enum version, taked from SourceCoop

cbasenpc.inc:

  • added CBASENPC_BLACKMESA and CBASENPC_TF2 macro, this allows you to compile the same plugin for different games.

baseanimating.inc:

  • split tf2 and bms activity versions

takedamageinfo.inc:

  • split so crit features only in tf2 plugins

cbasenpc_bmsnavtest.sp:

  • simple plugin to test bms nav.

AMBuildScript:
- now compiles black mesa.
configure.py
- should work now
AMBuilder:
- split bms and tf2 things, tf2 gameruls and tf2 nav/area to tf2, bms nav/area to bms
cbasenpc_internal.cpp:
- tf2 gamerules stuff is now under macro
extension.cpp:
- tf2 stuff under tf2 macro
- proper unload so if it doesn't load it doesn't crash the game
helpers.cpp:
- iirc the changes for 1.12 compile
natives.cpp:
- removed double basecombatcharacter::setup
natives\bms:
- black mesa has custom classes for CNavMesh/CNavArea with CBlackMesa prefix.
entityfactory.cpp, behavior.cpp:
- changed context->GetContext() to context so it can compile
nav.cpp:
- split bms and tf2 nav.cpp version via macro
takedamageinfo.cpp:
- throw error for black mesa if used GettDamageForForceCalc/SetDamageForForceCalc
serialrefresher.cpp:
- used different algorithm to get framesnapshotmanager in bms
baseanimating.cpp, baseanimating.h:
- bms wil use offset for CBaseAnimating::mSequenceDuration
- used different algorithm to get GetAnimationEvent in bms
baseentity.cpp, baseentity.h:
- new code to get CBaseEntity::PhysicsMarkEntitiesAsTouching and g_TouchTrace.
- black mesa will use trigger_auto_crouch for BEGIN_VAR instead of trigger_stun.
- added CBaseEntity::SDK_OnUnload() for proper unload.
bms_nav_area.h:
- CBlackMesaNavArea class.
nav_mesh.cpp:
- will give more info when unloaded for CNavMesh::Load reasons.
takedamageinfo.cpp:
- m_flDamageForForce moved as tf2 only.
toolsnav_mesh.h:
- added "vstdlib/random.h" to compile.
cbasenpc.txt:
- new data + fixes to prev commit
PackageScript:
- bms folder included
bms\nav.inc:
- methodmap for CBlackMesaNavArea
black mesa doesn't always use lowercase only classnames, so newlights and gonarch doesn't appear, newlight_point will appear but newLight_Point won't (the second case used by maps). this fixes the issue.
nav area map crash fix, fixed offsets for UpdateLastKnownArea and OnNavAreaChanged, nextbot spawn crashes
- fixed "Host_Error: DLL_SetView: not a client" error, the reason is silly... the sign was for OnRestore and not for LookupPoseParameter, i wasted my entire day on this...
- cbasenpc_example.sp now will compile.
- added bmsnavtest.sp for black mesa nav mesh/area test goals.
- Now the compiler will split Black Mesa and TF2 features, this is taked from SourceCoop. By default, TF2 version compiled. Currently this affect activity.inc file include and damage crit type natives.
- Moved TF2 activity.inc version to tf subfolder.
- moved bmsnavtest.sp.
- added cbasenpc_bmsnavtest.sp to AMBuilder from scripting folder.
@Kenzzer

Kenzzer commented Jul 24, 2026

Copy link
Copy Markdown
Collaborator

Everything was tested on Windows build, the Linux build needs some tests

Even if it's half broken/no linux support, PR like these are always a welcomed addition.

I'm quite busy currently, so I'm going to review the PR over several days. Still thanks for your work!

@Kenzzer Kenzzer left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And here's the review, thank you for your work.

Comment on lines +145 to +164
#if SOURCE_ENGINE == SE_BMS
/*
* The timer classes in the actual Black Mesa binary contain an
* additional 4-byte vtable pointer which is absent from the SDK
* declarations used to build the extension.
*
* ILocomotion:
* 2 IntervalTimer = 8 missing bytes
* 1 CountdownTimer = 4 missing bytes
*
* NextBotGroundLocomotion:
* 3 CountdownTimer = 12 missing bytes
*
* Total: 24 bytes, or 0x18.
*
* This storage must not be initialized or cleared. The game's
* NextBotGroundLocomotion constructor uses this memory.
*/
unsigned char m_bmsTimerLayoutPadding[0x18];
#endif

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, you're in luck I can edit the hl2sdks. So feel free to open an issue or PR over there for the bms branch, and we can fix this.

return sp_ftoc(info->GetDamageForForceCalc());
#else
return context->ThrowNativeError(
"CTakeDamageInfo.GetDamageForForceCalc is only supported in TF2."

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's rephrase this as "Not supported for this mod." so we're in line with what SourceMod does as well when a base scripting native it has doesn't function on a certain game.

return 0;
#else
return context->ThrowNativeError(
"CTakeDamageInfo.SetDamageForForceCalc is only supported in TF2."

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto.

Comment on lines +17 to +21
#if SOURCE_ENGINE == SE_BMS
VCall<float, CStudioHdr*, int> CBaseAnimating::mSequenceDuration;
#else
MCall<float, CStudioHdr*, int> CBaseAnimating::mSequenceDuration;
#endif

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need some explanation on this. I understand you prefer to call the virtual function, because it exists. But then why having added a signature for it in the gamedata ?

Comment on lines +193 to +197
#if SOURCE_ENGINE == SE_BMS
BEGIN_VAR("trigger_auto_crouch");
#else
BEGIN_VAR("trigger_stun");
#endif

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of having an if/else here, let's see if we can't just pick a shared entity between the two games. prop_dynamic should work, even if it isn't directly CBaseEntity

#endif
#define _CBASENPC_TAKEDAMAGEINFO_INC_

#if defined CBASENPC_TF2

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be removed.

*/
public native void SetDamagedOtherPlayers(int value);

#if defined CBASENPC_TF2

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be removed.

Comment on lines +6 to +10
#if defined CBASENPC_BLACKMESA
#include "bms/activity.inc"
#elseif defined CBASENPC_TF2
#include "tf2/activity.inc"
#endif

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As is, I don't like that. But it's my fault this is necessary in the first place. This can be kept.

Comment on lines +42 to +63
#if SOURCE_ENGINE == SE_BMS && defined(__linux__)
void* navMeshAddress = nullptr;
if (!config->GetMemSig("TheNavMesh_Linux", &navMeshAddress) || !navMeshAddress)
{
snprintf(error, maxlength, "Couldn't resolve TheNavMesh_Linux symbol!");
return false;
}
else

void* navAreasAddress = nullptr;
if (!config->GetMemSig("TheNavAreas_Linux", &navAreasAddress) || !navAreasAddress)
{
snprintf(error, maxlength, "Failed to retrieve TheNavMesh ptr!");
snprintf(error, maxlength, "Couldn't resolve TheNavAreas_Linux symbol!");
return false;
}

g_pNavMeshLoad = DETOUR_CREATE_MEMBER(CNavMesh_Load, "CNavMesh::Load");
if (g_pNavMeshLoad != nullptr)
// TheNavMesh is a global CNavMesh* variable.
CNavMesh** navMeshGlobal = reinterpret_cast<CNavMesh**>(navMeshAddress);
resolvedNavMesh = *navMeshGlobal;

// TheNavAreas is the global NavAreaVector object itself.
resolvedNavAreas = reinterpret_cast<NavAreaVector*>(navAreasAddress);
#else

@Kenzzer Kenzzer Jul 27, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is probably going to be a big blocking span of code in this review. Ideally I want the lookup method to be shared between all games, if it needs to be adapted I can make a concession. But TheNavAreas & TheNavMesh should be both possible to resolve in assembly with CNavMesh::Load signature, yes it does mean a little more gamedata but this keeps everything clear.

I can shoulder that part of the work if you can provide me the server.dll/srv.so for bms.

@@ -84,35 +84,103 @@ void Tools_RefreshEntity(CBaseEntity* entity, int player, bool toggle) {

bool Tools_Refresh_Init(SourceMod::IGameConfig* config, char* error, size_t maxlength)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This whole thing was an experiment that didn't turn out as well as I hoped. So we can delete everything here if you're not using on bms.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants