# I spent a year building a multiplayer shooter template, and the hardest part was letting players build the map

**URL:** <https://forums.unrealengine.com/t/i-spent-a-year-building-a-multiplayer-shooter-template-and-the-hardest-part-was-letting-players-build-the-map/2841998>\
**Category:** Community & Industry Discussion\
**Tags:** unreal-engine\
**Created:** [October 17, 2026, 11:08pm UTC](https://forums.unrealengine.com/t/i-spent-a-year-building-a-multiplayer-shooter-template-and-the-hardest-part-was-letting-players-build-the-map/2841998 "2026-10-17T23:08:13Z")\
**Posts on this page:** 3\
**Page:** 1

<div class="post-metadata">

**Author:** ![Sami\_GameDev](https://d1ap1mz92jnks1.cloudfront.net/user_avatar/forums.unrealengine.com/sami_gamedev/32/1212915_2.png) [@Sami\_GameDev](https://forums.unrealengine.com/u/Sami_GameDev)\
**Post date:** [October 17, 2026, 11:08pm UTC](https://forums.unrealengine.com/t/i-spent-a-year-building-a-multiplayer-shooter-template-and-the-hardest-part-was-letting-players-build-the-map/2841998/1 "2026-10-17T23:08:13Z")

</div>

I have been working on a third person shooter framework for UE5 for about a year. It started as a netcode exercise and turned into something I did not plan: a map editor that runs inside the game, where a map you build is sent to your friend over the network when they join.

This is a write-up of how the two halves fit together, and the parts that were harder than I expected. It is a commercial template - the link is at the bottom - but the interesting content here is the problems, not the sales pitch.

## The shooter half

The combat is C++, server-authoritative, with the client predicting what it can.

**Server-Side Rewind.** The server keeps a short history of every character’s hitboxes. When a shot arrives it rewinds those boxes to the moment the client actually fired, traces against that older state, and puts everything back. Hitscan, projectiles and shotgun pellets all go through it, with headshot detection.

The part I did not anticipate: rewind becomes unfair past a certain ping. It does not delete the latency, it just decides the target absorbs it instead of the shooter. At 400 ms you are killing people who already reached cover on their own screen. So there is a ceiling - above a threshold rewind turns off, the shot falls back to plain server-authoritative detection, and a warning icon tells the player why the feel changed.

**Weapons.** Pistol, SMG, assault rifle, shotgun, sniper, grenade launcher, rocket launcher. Hitscan with surface-based impact FX, projectiles with their own flight and radial damage, shell-by-shell shotgun reloads, graduated scope zoom, predicted reloads and ammo.

**Locomotion** is Blueprint, built on Motion Matching - real starts, stops, pivots and turn-in-place, replicated for simulated proxies so other players animate without a per-tick network cost.

**Match flow.** Warmup, Match, Cooldown, team deathmatch and free-for-all, team spawns, scoring, leaderboard, winner announcement and restart.

## The Creative Mode half

A map editor inside the game. No Unreal Editor, no cooking, no asset work - your players can use it.

You fly, you point, you click. A categorised palette of pieces, real move and rotate gizmos with grid and angle snapping, multi-select, 128-step undo where one action is one step whether it moved one piece or ten. Weapon, ammo and buff spawners you place yourself. Player starts for neutral or per team, so the same map works for both modes.

**The sky is part of the map, not a backdrop.** Five weather presets with blending, a day/night cycle you can set by hand or let run. Rain leaves splash ripples. Snow accumulates on every surface, changes the footstep sound, and keeps the prints players leave in it. Weather stops at a roof instead of falling through it - that one is a GPU depth-buffer collision module plus a shelter probe that moves the spawn band.

## The part that was actually hard

Getting a player-built map onto the other machines.

The map only exists as data at runtime. There is no cooked level to travel to, so the usual answer - ServerTravel to a map name - has nothing to point at.

Replicating the geometry does not work either. A map of any real size blows straight past the replicated array size ceiling, and even if it fit you would be sending the same meshes everyone already has on disk.

**What worked was not replicating the map at all.** Each piece is stored as an id plus a transform. The host hands that list over on join, and every client rebuilds the level locally out of its own cooked assets. Nothing large crosses the wire and both machines end up with identical geometry.

Two things I would warn anyone about. Pieces have to become static once the match starts - leaving them movable looks fine in a single-player test and falls apart the moment two clients disagree about physics. And save files must store ids, never asset paths: the moment a path is baked into a save, moving an asset breaks every map anyone ever built.

## What I would do differently

I hardcoded asset paths in C++ early on. A `ConstructorHelpers` `FObjectFinder` with a literal /Game/ path is invisible to the editor’s reference fixer, so when I reorganised content the game broke silently at runtime, and the editor’s own move tool reported success while doing nothing - because the loaded module was holding those assets. Everything that can be is exposed in Project Settings now, and the data assets are registered with the Asset Manager by class rather than by folder, so nothing depends on where a file sits.

Happy to answer anything about the netcode or the builder. The rewind ceiling in particular is a design decision more than a technical one, and I am curious what thresholds other people landed on.

Ultimate TPS on Fab: [Ultimate Third Person Shooter — Multiplayer + Creative Mode + Lag Compensation | Fab](https://www.fab.com/listings/2e4c8751-9978-40c5-82f6-5ab7b09ade24)

---

<div class="post-metadata">

**Author:** ![jessicaw3344](https://d1ap1mz92jnks1.cloudfront.net/user_avatar/forums.unrealengine.com/jessicaw3344/32/882603_2.png) [@jessicaw3344](https://forums.unrealengine.com/u/jessicaw3344)\
**Post date:** [October 22, 2026, 10:09am UTC](https://forums.unrealengine.com/t/i-spent-a-year-building-a-multiplayer-shooter-template-and-the-hardest-part-was-letting-players-build-the-map/2841998/2 "2026-10-22T10:09:47Z")

</div>

Thank you for the detailed write-up. The point that rewind moves the latency onto the target rather than removing it is a useful way to explain the trade-off. A middle option between full rewind and switching it off is to clamp the rewind time to a maximum value, so a high-ping player gets partial compensation instead of none, and the target never has to absorb more than a fixed window. Logging the rewind distance and the resulting hit or miss for each shot would also make it possible to tune that threshold from real match data instead of guesswork. Storing ids rather than asset paths in the save files is a sound decision as well, since it avoids the broken reference problem many projects run into after a content reorganisation.

---

<div class="post-metadata">

**Author:** ![Sami\_GameDev](https://d1ap1mz92jnks1.cloudfront.net/user_avatar/forums.unrealengine.com/sami_gamedev/32/1212915_2.png) [@Sami\_GameDev](https://forums.unrealengine.com/u/Sami_GameDev)\
**Post date:** [October 26, 2026, 3:09am UTC](https://forums.unrealengine.com/t/i-spent-a-year-building-a-multiplayer-shooter-template-and-the-hardest-part-was-letting-players-build-the-map/2841998/3 "2026-10-26T03:09:35Z")

</div>

The clamp is a good suggestion and I think it ends up better than what I shipped, but for a different reason than the one you gave.

The binary cutoff was deliberate. What I was protecting was not the compensation, it was the player knowing which rules they are under: either what you see is what you get, or it is not, and the warning icon says so. A clamp on its own removes the cliff but makes the failure quiet. At 400 ms with a 150 ms clamp you are still aiming at what you see, you just miss sometimes, and nothing tells you that you are now expected to lead.

So I am taking the clamp and keeping the announcement, and it is going into the next update: clamp the window so compensation degrades instead of falling off a ledge, and raise the same warning whenever the clamp is actually biting. That gets your continuity without losing the part the cutoff was there for.

On the data, the template ships a per shot debug view that draws the live hitboxes, the rewound ones and the box the trace hit, which is what you need to confirm the system is doing what it claims. Aggregating rewind distance and outcome across a session is a fair point though, and it is the right way to settle the threshold rather than argue about it.

What number did you land on, and was it from match data or from feel?
