Skip to content

Ai/reconstruction - #68

Open
kr4uzi wants to merge 8 commits into
B2BF:mainfrom
kr4uzi:ai/reconstruction
Open

kr4uzi wants to merge 8 commits into
B2BF:mainfrom
kr4uzi:ai/reconstruction

Conversation

@kr4uzi

@kr4uzi kr4uzi commented Nov 3, 2026

Copy link
Copy Markdown

fully reconstructed ai support

Markus Kraus and others added 6 commits November 3, 2026 19:02
Math, Core, IO, Scene, Physics and Platform units the AI library needs, plus the GameLogic/GameEvents AI call
sites (collision-handler and radio-courier reports, CombatArea AI queries, PortalEvent, DropVehicleEvent
producer). Minimal hunks only; Polynom.h keeps its libstdc++ licence notice.

goUpInPath restores the leading slash it strips, as the ELF does; on Windows only when the path had one,
because isPathAbsolute also accepts drive paths there. The Linux plugin loader calls deinitDll before dlclose
so a map reload cannot reuse stale AI singletons ([CHOSEN, not recovered], a native-port design). The
RendDX9 and bf2_runtime_pair manifests link the units the recovered Math code now needs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: OpenAI Codex <noreply@openai.com>
All 10,961 in-scope libaidll.so functions of BF2 1.50, recovered from the ia-32 ELF with amd-64 and PE
cross-checks; evidence vocabulary as in AGENTS.md ([INFERRED], [UNRESOLVED], [PLATFORM], [CHOSEN, not
recovered]). Chosen declarations exist only for never-implemented pure slots and null-only member pointers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: OpenAI Codex <noreply@openai.com>
…rary

One AIDLL source manifest, Code/Build/aidll_sources.txt, feeds both CMake and aidll.cmd. It lists the units
in the retail link order, which is also the static-initialisation order, read from the .ctors table of
libaidll.so. The engine image excludes every AIDLL-only unit by a directory rule (AI/AIDLL minus the shared
CommonSingletonServer, plus three units outside it), so bf2_w32ded.exe never links them. syntaxcheck.cmd
covers every AI unit. The linux-x64 preset builds a native libaidll.so with an export map (AidllAbi); this
native library is a native-port design, not retail. AIDLL test fixtures are updated for the retired stops.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: OpenAI Codex <noreply@openai.com>
The engine's instance (bf2 ds:0x8ABBCB0) was declared under the [CHOSEN] name g_random and defined in
ParticleTemplate.cpp; the AI DLL's (libaidll BSS 0x4DD454) as Rand in Random.cpp. Both retail images name it
dice::hfe::Rand and initialise it in Random.cpp's static init (bf2 0x83CB610, libaidll 0x48A850: iostream
Init, then the default seed). Random.h now declares one extern Random Rand; Random.cpp defines it for each
image; every g_random use is renamed (mechanical, no behaviour change).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: OpenAI Codex <noreply@openai.com>
The getter returned a fixed 2 ahead of its own logic, and the console
setter raised every value below 2 to 2 in release builds, so a server
could never start a round with a single player: the client kept showing
"The game will start when 1 more player(s) join" even with
sv.numPlayersNeededToStart 1 and bots on the map.

Retail does neither. The bf2 ELF getter (080BC1F0) returns 6, or 8 from
33 slots up, on ranked servers and the configured value otherwise; the
console SET is a raw field write. Both functions now match that.

Validated on the Windows x86 dedicated server with the retail 1.50
client: with sv.numPlayersNeededToStart 1 the round starts on the first
human spawn and the bots join the round.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The x86 server gets CBF2VoipServer from the separate BF2VoipServer_w32ded.dll
(bf2_voipserver, defined only for x86, .def with x86 decorated names). The x64
dedicated server had no definitions, so its strict link failed. Add the
voice-server sources to it, as the Linux image already does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kr4uzi
kr4uzi force-pushed the ai/reconstruction branch from 5ddf524 to 86d73bf Compare November 3, 2026 19:37
@AndreasLauvhjell

Copy link
Copy Markdown
Contributor

Hell yeah, nice work! Seems like you've had your hands full! Did you have aidll_r.dll as well to look into? It may seem like the most detailed ai-related dll to get insights. I ran the PR through Claude which did some spot checks

Notes from the AIDLL_r debug build

Summary

No behavioural errors found. About 15 functions of upstream PR #68 were spot-checked
against the retail binaries in Ghidra; logic and constants match. The debug build AIDLL_r.dll
(DICE Canada BoosterPack2 branch, equivalent to 1.50, with asserts) lets us replace several of the
PR's guesses with DICE's own names:

  • Name weapon type 4 WTC4 instead of WTSticky.
  • Name the object-type flag bits the PR marks [UNRESOLVED] (table below).
  • Move 8 source files to their real translation-unit paths, and split the folded ones.

These are naming and documentation improvements; none changes behaviour.

Names

Weapon type 4 is WTC4. AIDLL_r asserts wInfo->getWeaponType() == WTC4 ("Expected C4 weapon
type") in BBC4.cpp. The PR uses WTSticky in AIInterface/BFKnowledge/WeaponTemplate.h:32 and
WeaponObject.cpp:125.

Object-type flags. isOfType(n) tests bit 1 << n. Values come from the enum-to-string switch
in AIDLL_r (FUN_100d3010, jump table at 0x100D32EC). isOfType(0x15) = ITSoldier = 0x200000
confirms the mapping. Files with [UNRESOLVED] flag notes include BBChange.cpp, BBIdle.cpp,
BFInformationEnvironment.cpp, BAPConLineOfFire.h, BAPAMoveToObjectDirect.h, AISettings.cpp.

Bit Name Bit Name
0x1 ITProduction 0x4000 ITCover
0x2 ITAmmunition 0x8000 ITMobile
0x4 ITRepair 0x10000 ITUnmanned
0x8 ITUnit 0x20000 ITLowPriority
0x10 ITAir 0x40000 ITStructure
0x20 ITHelicopter 0x80000 ITBiological
0x40 ITGround 0x100000 ITVegetation
0x80 ITNaval 0x200000 ITSoldier
0x100 ITControlpoint 0x400000 ITObstructedView
0x200 ITRadar 0x800000 ITNoTemperature
0x400 ITAirfield 0x1000000 ITHasWreck
0x800 ITTransportation 0x2000000 ITObstacle
0x1000 ITArtillery 0x4000000 ITHasCarHorn
0x2000 ITFixed

Example: SquadManager::friendliesCloseTo skips (flags & 0x20) == 0 && (flags & 0x10) != 0,
i.e. planes only (ITAir without ITHelicopter).

Kit types (matches the code's spawnKit tests 1, 3, 4, 5): 0 KTSpecops, 1 KTSniper,
2 KTAssault, 3 KTSupport, 4 KTEngineer, 5 KTMedic, then KTExtended1..6. Anti-tank = KTExtended1
is inferred, not confirmed.

Source placement

AIDLL_r carries 210 real .cpp paths under Code\BF2\AI\AIDLL\AI\. Eight PR files sit in another
folder than DICE's, and some classes the PR folds into other files have their own file in retail.
Paths are relative to Code/BF2/AI/AIDLL/AI/.

Class / file Real path (AIDLL_r) PR #68 path
Aimer AIBotActionPlan/Aimer.cpp AIBot/Aimer.cpp
UrgencyMerger AIBot/UrgencyMerger.cpp AIBotBehaviour/UrgencyMerger.cpp
AIMineManager AIKnowledge/AIMineManager.cpp Main/AIMineManager.cpp
WeaponInfoManager AIUtils/WeaponInfoManager.cpp AIKnowledge/WeaponInfoManager.cpp
AerialHeightMap AIPathFinding/AerialHeightMap/AerialHeightMap.cpp AIStrategicMap/AerialHeightMap.cpp
UserOrderManager AIStrategicEntity/UserOrders/UserOrderManager.cpp Main/UserOrderManager.cpp
AILandingZoneManager Main/AILandingZoneManager.cpp AIStrategicEntity/AILandingZoneManager.cpp
SensingAgent Soldier/Turret/MouseFreeLook patterns 3 files: AIBot/SensingAgentSoldierPattern.cpp etc. 1 file: AIBot/SensingAgentPatterns.cpp
SCurve AIUtils/SCurve.cpp inline in BBCallForMedic.cpp / BBMedicAssist.cpp
boxTools AIUtils/boxTools.cpp not present
IAIMain, IAIPathfindingSetup, IBotManagerModifier Main/IAIMain.cpp etc. folded into AIMain, AIEventCourier, BotManager

The PR marks these placements [INFERRED], so the fix is a move plus dropping the marker.

Verified against retail

What this table is: each row is one claim the PR makes about how retail behaves. I opened the
matching function in the retail binary in Ghidra and checked the claim against the compiled code.
Every row matched, so these parts of the PR can be trusted as retail-accurate.

Address note: Ghidra loads libaidll.so ia-32 at base +0x10000, so Ghidra address = the PR's
"ELF 0x..." address + 0x10000 (e.g. PR 0x1F8F40 = Ghidra 0x208F40).

What the PR claims Retail value found Where checked (libaidll ia-32 unless noted)
Aim point offset, soldier shooting soldier -0.2 m when the weapon's maxRange < 10, else -0.3 m when the target stands 0x208F40 BAPWrapperAiming::execute
Aim point centre for a bot target lying -0.7 m, standing/crouching 0 0x1A7650 (copy 0x18A2E0) soldierHack_getSoldierCentre
Aim point for a human target the soldier's first camera (eye) bf2 ia-32 0x8403010 / 0x8403100
Skill adjusted per weapon class 1-(1-s) x 0.5 (bullets) / 0.8 / 0.4, thrown or guided 1.0, scale 3.0 0x2D6900 EntryTrigger::execute
Unused sniper special case see "Sniper branch" below 0x2D6900 EntryTrigger::execute
AI deviation formula (1-s) x deviation x scale x ((1-t) x 0.5 + 0.5) bf2 ia-32 0x84231B0 WeaponObject::setBotSkill
AI weapon template defaults deviation 14, correction time 15 s, allowedDeviation 0.5 bf2 ia-32 0x8423570 WeaponTemplate ctor
When to pull the trigger (precision gate) 0.2 x precision^2 vertical band, negativeYLimitFactor 0.1 0x1FBD80 BAPCConPrecision::evaluate
C4 urgency place 500, blow 100, no friendlies within 15 m, no weight parameter 0x20F290 BBC4::calculateUrgency
Friendlies-near check counts the asking bot itself 0x1C07F0 SquadManager::friendliesCloseTo
Unarmed fire range 250 m (the PR file is a skeleton, but this constant is right) 0x2239E0 BBFireUnarmed::calculateUrgency
Behaviours that ignore setBehaviourModifier C4, Random, Suicide have no weight parameter at all 0x20F290, 0x22A780, 0x22D680

Sniper branch

In EntryTrigger::execute (the code that presses fire and sets the bot's skill-based spread),
retail contains:

if (weapon->getWeaponZoom() != PINone && bot->getSpawnKit() == 1)   // zoom weapon, sniper kit
    weapon->getMaxRange();                                          // result thrown away

The compiler kept the call but nothing uses its result. It looks like a sniper-specific skill or
spread adjustment that DICE started or removed, leaving only this empty shell. The PR reproduces
it correctly ("retail: the result is dropped"). It matters as a hint that a sniper rule was once
intended, and as a natural place for a [CHOSEN] sniper fix later.

About AIDLL_r

AIDLL_r.dll comes from D:\DiceCanada\BoosterPack2, dated 2006-04-05, and already contains every
1.50-only feature (co-op squad fill, Random, Triggerable, flashbang flag), so it is a valid
naming reference for 1.50.

AIDLL_r.dll is a DICE Canada BoosterPack2 debug build of the AI DLL
(2006-05-04, asserts and __FILE__ paths, no PDB). It is older than 1.50 but
already has the 1.50 AI features. It is used as evidence for names, enum
values and source-file placement only; behaviour stays matched to retail 1.50
(libaidll.so / AIDLL.dll). Follows the review notes on B2BF#68.

Names
- WTSticky -> WTC4 (assert "wInfo->getWeaponType() == WTC4", BBC4.cpp).
- Information::isOfType(InformationType), DICE's name (assert
  "!targetPrimaryInfo->isOfType(ITSoldier)"). The single-bit object-type flag
  tests use it with the IT* names (enum-to-string switch 0x100D3010), resolving
  the [UNRESOLVED] flag notes. BotMain::event_airborn tests ITAir, then
  ITHelicopter, as retail compiles it (ELF 0x18D740).
- Kit types documented: 6 is KTAT, 7..12 KTExtended1..6.
- 89 member and parameter renames from AIDLL_r assert expressions, each
  checked by the field offset the assert reads and by its use in the tree
  (BotNavigation, BotMain, SquadManager/Formation, SAI, AIStrategicObject*,
  AISettings, BotManager, QuadTree/ClusterGraph, ...). Evidence addresses are
  in the comments.

Source placement (AIDLL_r __FILE__ paths)
- Moved: Aimer, BotScript.h -> AIBotActionPlan/; UrgencyMerger -> AIBot/;
  AIMineManager -> AIKnowledge/; WeaponInfoManager, CustomCurve -> AIUtils/;
  AerialHeightMap -> AIPathFinding/AerialHeightMap/; UserOrderManager ->
  AIStrategicEntity/UserOrders/; AILandingZoneManager, InformationManager ->
  Main/.
- Split, functions moved unchanged: SensingAgentPatterns into the Soldier,
  Turret, MouseFreeLook and Null pattern TUs; SCurve out of CustomCurve;
  IAIMain, IAIPathfindingSetup and IBotManagerModifier into their own TUs.
- Code/Build/aidll_sources.txt, Code/Tests/syntaxcheck.cmd and the
  Code/Tests/aidll fixtures follow the moves and renames.

No behaviour change intended: where AIDLL_r and the tree were compared, they
agree with 1.50 apart from asserts. Built and smoke-tested on macOS
(bf2_aidll, vtable audit unchanged, 16-bot round); the Windows fixtures were
updated as text only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kr4uzi

kr4uzi commented Nov 5, 2026

Copy link
Copy Markdown
Author

Did you have aidll_r.dll as well to look into? It may seem like the most detailed ai-related dll to get insights.

Indeed I didn’t - the latest commit contains now some additional improvements based on the _r version (mostly member names and some slight code improvements).

Btw 64/32 windows and a macOS build is also almost done.

…ainst retail)

Recovers the retail Memory.dll (Ver 1.01, 2005/01/10 "Final") into Code/BF2/Memory as its own
image, bf2_memory. Every function cites its PE address; placement follows the shipped function
and data order. The Final build is mostly stubs: only the chunk pools, the sanity limit,
getStatus and the resource counters do work; the counters, trace and histogram exports return
constants, as in retail.

- Platform seams behind #if, [CHOSEN, not recovered]: constructor/destructor instead of DllMain
  on POSIX, stderr instead of the message box, pthreads, and a 16-byte header slot on 64-bit.
- BF2_MEMORY_PASSTHROUGH (AUTO/ON/OFF) forwards to malloc/free under AddressSanitizer.
- Built on request only (EXCLUDE_FROM_ALL) and linked into no game image: the game keeps
  allocating through the shared CRT (issue #7). Code/BF2/Memory is skipped by the engine source
  discovery in CMakeLists.txt and Code/Build/build.cmd.
- Windows: Code/Build/memory.cmd. Tests: Code/Tests/memory (unit tests, internal tests, and
  memory_diff, which runs one allocation script against the retail and the rebuilt DLL; the
  outputs were identical with MSVC x86).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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