A Fortnite game server that runs as a standalone .exe instead of a DLL loaded into the game.
You drop one executable next to FortniteGame and Engine in a build folder, run it, and it launches the
game, turns it into a listen server and drives the whole match from its own process.
Independent community project. Not affiliated with, endorsed by, or supported by Epic Games. This repository contains no Fortnite game files, no launcher and no backend services.
This branch (main) is documentation only. The server itself lives on a branch per Fortnite version, so a
version can be worked on, broken and fixed without disturbing any other.
| Branch | Target | Host | Status |
|---|---|---|---|
main |
Documentation | You are here | |
season-3 |
3.6, Chapter 1 Season 3, CL 4019403, UE 4.19 | Windows | Active |
season-13 |
13.40, Chapter 2 Season 3, CL 14113327, UE 4.25 | Windows and macOS | Active |
season-13 targets the last Fortnite version Epic shipped for macOS, so that branch runs on both hosts.
Bots and bosses exist there too, since Chapter 2 is where the AI is.
To work on the server, check out a version branch:
git clone https://github.com/OGFN-Open-sourceing/FortExternalServer
cd FortExternalServer
git checkout season-3
or
git checkout season-13
New versions branch off an existing one. See Porting to another version.
- How it works
- Requirements
- Building
- Installing
- Configuring
- Running
- Console commands
- Porting to another version
- Troubleshooting
- Credits
Most Fortnite game servers ship a DLL that gets loaded into the game, so the server code and the game share one process. FortExternalServer keeps them apart. All of the match logic lives in your executable and reaches the game from outside.
That split is not total, and it cannot be. A game server has to run code at engine call sites: something must happen when the net driver ticks, when a control message arrives, when the engine asks what net mode it is in. So the server writes small pieces of raw x86-64 machine code into the game process and points those call sites at them. No DLL is produced, nothing extra is written to disk, and nothing appears in the game's module list.
There are two kinds of stub.
Constant return stubs replace a function outright with mov rax, <value> and ret. Used for
GetNetMode (always answer listen server), KickPlayer, CollectGarbage and NetDebug. These involve no
communication at all, which matters because GetNetMode is called thousands of times per frame.
Channel stubs publish their arguments into a shared block and park the game thread in a dispatch pump. While the game thread sits in that pump, the server can run engine functions on it, which is the only thread UE will tolerate them on. The server can also rewrite the stub's arguments before the original function runs, and can tell the stub to skip the original entirely and return a chosen value.
Everything else is ordinary reads and writes into the game's memory, served through a page cache so that walking large structures stays cheap.
Structure offsets are not hardcoded tables. The server walks the running build's own reflection data
(UStruct children, UProperty offsets) to find properties and functions by name, so a lookup is either correct
or reports itself as missing.
To run
- Windows 10 or Windows 11 x64, or macOS on a build whose branch supports it
- A Fortnite build matching the branch you built, including its
FortniteGameandEnginefolders - On Windows, administrator is not required in the usual case, but helps when attaching to a game you started yourself
- On macOS,
task_for_pidneeds privileges. Run the server withsudo, or sign it with thecom.apple.security.cs.debuggerentitlement. The server tells you which is missing if it cannot attach.
To build
- Visual Studio 2026 with the Desktop development with C++ workload, or
- CMake 3.21 or newer with Ninja, or
- Xcode command line tools when building the macOS host, or
- A mingw-w64 toolchain when cross compiling a Windows binary from macOS or Linux
No third party libraries. No package restore. C++20 and the Windows SDK are all it needs.
- Open
FortExternalServer.sln - Pick Release and x64
- Build
The project targets the v145 toolset that ships with Visual Studio 2026. If you are on an older install and
want to build anyway, override the toolset without editing the project:
msbuild FortExternalServer.sln /p:Configuration=Release /p:Platform=x64 /p:FortExternalServerToolset=v143
cmake --preset vs2026
cmake --build --preset vs2026
or, with Ninja and the MSVC toolchain on your path:
cmake --preset ninja-release
cmake --build --preset ninja-release
On a branch that supports macOS, building on a Mac produces a native macOS host that attaches to the macOS Fortnite build:
cmake --preset macos-release
cmake --build --preset macos-release
The output lands in Binaries/Mac/. It builds x86_64 because the game is x86_64.
To produce the Windows executable from a non Windows machine, install mingw-w64 first:
brew install mingw-w64 # macOS
sudo apt install mingw-w64 # Debian or Ubuntu
then:
cmake --preset mingw-release
cmake --build --preset mingw-release
For quick cross compiles without CMake:
make
Windows routes write Binaries/Win64/FortExternalServer.exe. The macOS route writes
Binaries/Mac/FortExternalServer.
Copy the executable into the build folder, the one that has FortniteGame and Engine directly inside it:
YourFortniteBuild/
├── Engine/
├── FortniteGame/
└── FortExternalServer.exe
That is the whole install. The server finds the build root by walking up from its own location until it sees
both FortniteGame and Engine, so a subfolder works too as long as those two are somewhere above it.
One header, Source/Server/Public/Configuration.h. Edit it and rebuild. There is no ini file and no command
line, deliberately, the same way Erbium does it.
struct FConfiguration
{
static inline auto Playlist = "Playlist_DefaultSolo";
static inline auto MapToLoad = "Athena_Terrain";
static inline auto StartingLoadout = "WID_Harvest_Pickaxe_Athena_C_T01";
static inline auto Port = 7777;
static inline auto MaxTickRate = 30;
static inline auto MaxPlayers = 100;
static inline auto TeamSize = 1;
static inline auto MinimumPlayers = 2;
static inline auto WarmupTime = 120;
static inline auto bPlayerBots = false;
static inline auto PlayerBotCount = 0;
static inline auto bBosses = false;
static inline auto bSessions = false;
static inline auto bJoinInProgress = false;
static inline auto bFriendlyFire = false;
static inline auto bHealthRegen = false;
static inline auto bSpectateAfterDeath = true;
static inline auto bAttachToRunningProcess = false;
static inline auto bSkipVersionCheck = false;
static inline constexpr auto bEnableConsole = true;
static inline constexpr auto bVerboseLogs = false;
};| Option | Meaning |
|---|---|
Playlist |
Short name like Playlist_DefaultSolo, or a full object path. Short names are expanded for you. |
MapToLoad |
Map to travel to. |
StartingLoadout |
Comma separated item definition names given to every player on spawn. |
Port |
Port players connect on. |
MaxTickRate |
Server tick rate. |
MaxPlayers |
Session capacity. |
TeamSize |
Players per team. 1 solo, 2 duos, 4 squads. |
MinimumPlayers |
Players needed before the warmup countdown starts. 0 starts with nobody. |
WarmupTime |
Warmup countdown in seconds. 0 skips straight to the bus. |
bPlayerBots |
Spawn AI players. Only on branches whose build has them. |
PlayerBotCount |
How many AI players to spawn when the match starts. |
bBosses |
Spawn the season's bosses with their mythic loadouts. |
bSessions |
Off, the server picks teams itself and sets SquadId from the team index. On, a team already assigned through the game session is kept and SquadId is left alone. |
bJoinInProgress |
Allow players to join after the match has started. |
bFriendlyFire |
Allow teammates to damage each other. |
bHealthRegen |
Leave the health and shield regeneration effects in place. |
bSpectateAfterDeath |
Let eliminated players spectate. |
bAttachToRunningProcess |
Wait for a game you started yourself instead of launching one. |
bSkipVersionCheck |
Run against a build whose changelist does not match this branch. |
bEnableConsole |
Enable the console keys listed below. |
bVerboseLogs |
Verbose logging instead of display level. |
StartingLoadout takes several entries:
static inline auto StartingLoadout = "WID_Harvest_Pickaxe_Athena_C_T01,WID_Shotgun_Standard_Athena_UC_Ore_T03,Athena_Shields";Names resolve directly, then as Name.Name, then load on demand. Anything unresolvable is logged and skipped
rather than failing the spawn.
The resolved configuration is printed at startup under the Config category, so you can always confirm what
the build you are running actually used.
Values that describe the build rather than a preference, storm timings, flight time, starting health and shield, backpack size, live in the build profile instead. See Porting to another version.
Double click the executable, or from a terminal in the build folder:
FortExternalServer.exe
There are no arguments. Everything comes from Configuration.h at build time.
Startup runs through these stages, each named in the log if something goes wrong:
- Resolve the build root
- Launch or attach to the game process
- Read the game module and resolve engine globals
- Install the listen server hooks and confirm the game thread reaches the pump
- Check the build version
- Wait for the front end, then travel to the Athena map
- Prepare the match and start accepting players
From there players connect to your address on the configured port.
Single keypress, in the server console window.
| Key | Action |
|---|---|
H |
List commands |
S |
Stage, player count, alive count, current phase |
P |
List connected players with team and alive state |
B |
Skip the warmup countdown and start the match now |
E |
End the match |
D |
Dump every loaded object to Saved/ObjectDump.txt |
Q |
Shut down |
The object dump is the tool to reach for when a playlist, item or class name is not resolving. Dump, search the file for what you expected, and use the exact name it reports.
Fortnite calls its AI players Phoebe. Spawning the Phoebe pawn is enough on its own, the engine attaches
BP_PhoebePlayerController to it. Around that the server sets up what the game expects: a server bot manager
wired to the bot mutator, and an AI director that is spawned and activated once per match.
Bosses are the same machinery with a name, a location, a mythic loadout and more shield. They live in the build profile:
MakeBoss("Ocean", { "WID_Harvest_Pickaxe_Athena_C_T01", "WID_Assault_Burst_Athena_UC_Ore_T03", "Athena_Bottomless_ChugJug" },
FVector(-90000.0f, -60000.0f, 2000.0f)),The season-13 profile carries Ocean, Jules and Kit. Their coordinates are placeholders. Use the D
console command to dump objects, find the real POI positions for your build, and replace them. A boss with no
real location is spawned at a warmup spawn point and says so in the log.
If a build has no bots, the server says which class was missing rather than failing quietly.
The codebase is built to be branched. Almost everything is version independent: the memory layer, the hook machinery, reflection, the match flow, teams, inventory and the storm all work off names and reflection rather than build specific constants.
To start a new version, branch from the closest existing one:
git checkout season-3
git checkout -b season-4
Then work through these, in this order:
1. Source/FortniteGame/Private/Versioning/Season3BuildProfile.cpp
Rename it for your version and update the values inside. This one file holds the version number and changelist, the UObject and UProperty layout offsets, every asset path, the ability sets, the storm phase table, the known playlists, the world constants and the match tuning values, flight time, storm delay, end of match delay, starting and maximum health and shield, and backpack size. For a nearby version this is often the only file that needs real changes.
2. Object layout, inside that same profile
ObjectLayout holds the engine structure offsets. They are stable within an Unreal version and move between
versions: UStruct and UProperty shift on 4.22, the object array becomes chunked on 4.21, and on 4.25
properties leave Children for ChildProperties and become FField instead of UObject. Set
UStructChildProperties and the reflection walk switches to the FField chain by itself. Compare the 4.19
values on season-3 with the 4.25 values on season-13 to see the shape of a move.
3. Source/Runtime/CoreUObject/Public/UObject/CoreUObjectSignatures.h
Byte patterns for GObjects, StaticFindObject, FName and ProcessEvent. ProcessEvent is resolved by
finding candidates and then confirming one against a real object's virtual table, so it usually survives small
version changes.
4. Source/Runtime/Engine/Public/Engine/EngineSignatures.h
Byte patterns for the networking functions. This is where most of the porting work goes. Anything that fails to resolve is named individually in the startup log, so run it once and the log tells you exactly which patterns need attention rather than making you guess.
Leave bSkipVersionCheck off for a branch once it works. It is what stops someone running a Season 3 build
against a Season 4 game and getting confusing crashes instead of a clear message.
"FortExternalServer.exe must sit in the build folder that contains FortniteGame and Engine"
The executable is not inside a Fortnite build. It walks upward looking for both folders and did not find them.
"The game executable was not found at ..."
[Process] GameExecutable does not match your build. Some builds use a different name or path under
FortniteGame/Binaries/Win64/. Point the setting at the real one.
"Failed to resolve every required engine global"
Byte patterns did not match this build. The log lists which ones. Either you are on a version this branch does not target, or the patterns need updating for it. See Porting to another version.
"The game thread never reached the bridge pump"
The net driver tick hook is not firing, so nothing drives the server. Usually means the NetDriverTickFlush
pattern matched the wrong function, or did not match at all. Check the unresolved function list above it in the
log.
"This build reports changelist X but the server targets Y"
Exactly what it says. Use the branch built for that changelist, or set bSkipVersionCheck in
Configuration.h to try anyway.
Players connect but never spawn
Look for PlayerBootstrap lines in the log. A missing player pawn class or an unresolvable loadout entry is
reported there. Use the D object dump to confirm the real names in your build.
Poor frame rate once the server is running
Lower MaxTickRate. The game thread waits on the server for each match update, so a higher rate costs frame
time.
Built by studying the open source Fortnite server projects that came before it:
- PongooDev/Core — version handling, and the
MapToLoadandbSessionsoptions - plooshi/Erbium — module layout, signature resolution and the single header configuration style
- Ducki67/FN-Gameserver-Center — the archive the byte patterns and match flow were derived from. Raider 3.5 for Season 3, and Forge and HalalGS 19.10 for how bots and bosses are actually spawned
If you use this project, credit it and the projects above.
MIT. See LICENSE.