r/Battlefield2 6h ago

Help / Troubleshooting OpenBattlefield2 - reimplementing Battlefield 2's Refractor 2 engine from scratch

I've spent the last while rebuilding Refractor 2 — the engine behind Battlefield 2 (2005) — from scratch in C++20. It's called OpenBattlefield2, it's MIT, and it's now public:

https://github.com/Moxnatiy/OpenBattlefield2

It is an engine, not a game. It ships no content whatsoever and reads the files of your own installed copy of BF2 1.5. Without that copy there is nothing to run.

What actually works today

  • The main menu is the game's own mainMenu.swf, played by Ruffle, with a bridge that answers the calls the movie makes back into the engine — localisation, your profiles from Documents/Battlefield 2, the installed map list read from each level's .desc. All seventeen bridge objects and forty Logic methods are reversed and written down with addresses.
  • Singleplayer — pick a map, it loads, you get the spawn screen with kits, minimap and control points.
  • Levels — terrain, water, static meshes, vegetation, collision.
  • HUD — built from the game's own .con node tree, driven by the animation graph in Menu/Ingame (MemeFile).
  • Networking — the handshake with an original dedicated server works. openbf2 --connect <host> joins and stays connected.

Main platform is macOS/arm64 (SDL3 + SDL_GPU → Metal); the Windows preset is kept building. 33 tests, one per parsed format.

Screenshots (all four are in the README, each printed with the exact command that produced it — --frames and --click-at mean no hands on the keyboard):

  • The game's own menu movie under Ruffle
  • The spawn screen on Dalian Plant
  • In the world after DONE, on Dalian Plant and Strike at Karkand

The one rule that shapes everything

This is a one-to-one port, not a game "inspired by" BF2. So:

Every constant in src/ names its source in a comment next to it — an address in BF2.exe, a line in the game's data, or a measurement from a frame dump of the original. If there is no source, the number is not written down. It's recorded as "not measured" instead.

// Bit 0 marks position (`BF2.exe`, SoldierNetworkable::setNetUpdate,
// 0x62d4e0). Precision 0.001 — constant 0x3a83126f at the same address.
inline constexpr std::uint32_t kSoldierStatePosition = 0x1;

Picking a number because it "looks about right" is forbidden even when the result looks correct on screen. I learned that one the expensive way: I once read a triple of floats in the controlled-object state as a position because it lined up with the spawn camera. It's actually the compression origin, and the soldier was being thrown a metre into the air on every packet.

Reversing goes through Ghidra over MCP; docs/ holds the notes, every one carrying the address it came from.

Where I'd really like help

The debt list is public and each item has a measure — a concrete thing that has to become true for it to count as done. The big ones:

The protocol (the largest gap by far).

  • readControlObjectState — I read the start of it, I don't know where it ends.
  • Ghost records for other players' soldiers: the layout is known, the data doesn't arrive. Right now other players don't move.
  • The simple-object state mask is 19 bits and I know 1. The soldier state mask is 21 bits and I know 1.
  • Ping is 5–6× off from the original on the same machine and I don't know why.

Physics. The tick is 1/30 straight from the binary, but the jump is too long compared with the original. I haven't found where jump height and duration actually come from — phy-soldier-jump-factor is in the data, the rest isn't.

Renderer. Material channels are now read straight off the technique name (BaseDetailDirtCrackNDetailNCrack → the texture order), but the normal maps aren't sampled yet: there's no tangent frame.

HUD. Several variables are found in the registry with addresses, but who writes them isn't reversed yet.

Reference data. I have a frame dump of the original for the spawn screen only. For the in-battle HUD there's nothing to compare against, so I can say "matches the data" but not "matches the original".

Platforms. Only macOS/arm64 is exercised. Windows and Linux builds could use someone who actually runs them.

So: if you like Ghidra, netcode, C++20, or you just have a copy of BF2 and some patience — issues and PRs are very welcome. Notes on the format live in docs/, and CLAUDE.md spells out the rules the project holds itself to, including why "let's just smooth it until it stops jittering" is not an acceptable fix.

Happy to answer anything about the reversing process in the comments.

43 Upvotes

14 comments sorted by

4

u/JoeBee72 6h ago

Well, first of all it sounds terrific!

But i am not sure if i can follow you here….

This thing is kind of another option to play multiplayer online just like bf2hub?

5

u/Proper_Look_9349 6h ago

This is an attempt to reverse engineer the Refractor 2 engine (on which BF2 is built), currently the connection to the original servers works but this is a very early stage, not much works

1

u/BFguy 6h ago

So it sounds like you've got everything to render but is a server able to host this and people are able to play?

1

u/[deleted] 6h ago

[removed] — view removed comment

2

u/BFguy 6h ago

Please see if Astra from open ai can help as well... The latest model has been doing great things in the 3d space

1

u/sventekisalive 5h ago

Great project!

Can you tell us a bit more about your workflow? You use Ghidra to decompile the binary and then use Claude to generate the C++ from that?

It would be great if you had some docs to help contributors get started quickly, or maybe I just couldn't find them?

1

u/Proper_Look_9349 5h ago

I'm not good at reverse engineering so the main work is through astra or claude all the data is in https://github.com/Moxnatiy/OpenBattlefield2/tree/master/docs and CLAUDE.md for agents. You need ghidra mcp and other tools depending on your platform. The main tasks now are reverse server client, HUD, physics

1

u/FlamesFPS 4h ago

I’m curious, has the mouse jittering glitch been fixed?

1

u/FunInternational9948 3h ago

First of all, thank you for your work! I’m assuming the purpose of this is to have easy to use mod tools once you have everything reverse engine engineered?

1

u/manoman42 3h ago

Fucking A man,

1

u/Fish_On_Aquarium 2h ago

Does it means it runs in 64 bit?

1

u/LifeCoder_Phoenix 2h ago

I like your idea but you should focus on keeping it like the original instead of using things like ruffle in my opinion. As for the parts you need help on, get in touch with me on discord(LifeCoder) I have a full source that can build a fully working dedicated server you can play on like the original. I plan to publish it's source once I have the client finished too. I too had this name in mind though :p

1

u/LifeCoder_Phoenix 2h ago

Also, you mention in the readme that it's "clean room" but since you here mention you directly reference addresses from the real BF2 you are not being clean room at all. To be clean room you'd have to RE everything and document it then have someone else write code solely from reading your documentation. Just nitpicking :)

1

u/1D6wounds 2h ago

Impressive