August 2023 Update - Netcode in Roblox
- Riptcage
- Sep 1, 2023
- 5 min read
Updated: Sep 15, 2023
Quake 2 Remastered revealed at QuakeCon!
It's a dream come true for much of the quake community. Would love to see a Q2Remastered mod of weapons factory.
Oh hello Source Code. If I wasn't committed to a Roblox game at this time... I'd probably do it. At any rate, this release is great timing to create hype around Quake 2 for the year, possibly bringing in a younger audience on steam. Although the player base for fast action first person shooters is relatively low on Roblox, I'm hoping to ensure the unique aspects of Q2WF translate well into Roblox for a fun and engaging experience. If it brings in a small community of players that love it and want more at the very least, I'd be happy with that. The Q2 Remaster has given me a great target for further improvements that I will likely push forward into the release of WFR, like a muzzle blast coming from the barrel of weapons for example.
Multiplayer Games and Roblox Netcode
The month of August was a huge learning curve for how to approach Netcode in Roblox as a developer.
And honestly.. I probably should have focused on a pure client based single player game experience first. Or one that has minimal server interaction required for other players. Woops! And here I am. Diving into the deep end of a multiplayer game.

Regardless, WFR at its core is an FPS experience that interacts with other players. Luckily, Roblox uses a client-server model approach to achieve this and covers a lot of the fundamental interaction for developers, like client player movement auto-replicating and physics created on the server with a client as the network owner will also replicate to all clients. Due to the nature of this model, it means there will be lag based on where the server is and where each client is, and other factors I won't get into.
Client Ping = The time it takes to talk to the server and the server to talk back to you in milliseconds.

I remember having a 250-350ms ping playing Q2WF back in the year 2000, depending on the server location I was connected too. Many of us became a "Lead the shot Pro" because we endured the lag to our advantage. I can't imagine this today. So, while doing some research, it seems the average ping on Roblox is around 80 - 150ms, with the occasional slow poke. That'll do.
There are a lot of specifics about Netcode that I won't get into. What I will talk about is how Weapons Factory Roblox will approach solving this problem.
Lag Compensation
Color coding is the best way to show what happens in a multiplayer game as it relates to Netcode.

The most smooth gameplay version of solving this problem is a "favor the player" approach, where the player sees the visuals as intended and likely does the hit registration, with a server-side validation hit to reconcile the difference based on the shooters ping across other players. This approach prevents exploits and attempts to match the player who shot the bullet as close as possible on the server, and the visual replicates to other players.
I am choosing a mostly "Favor the Player" approach where it makes sense to ensure the players experience is as smooth as possible. This is true for Projectiles and Hitscan (or instant) shots. This means I have implemented a custom lag compensation on the server for proper validation of "what should be possible" from the barrel position to the intended shot position. See below video demonstration where the blue ball is what the server sees with and without lag compensation.
What we want is the blue ball to be more accurately matching the rocket and this is shown in the 2nd clip with lag compensation. If a player shot has been exploited, they will be the only one that sees it because the server will not register the hit, meaning other players won't see it either. In the end, if someone has a 150ms ping, that means they take 75ms to the server and 75ms back.
Projectiles
We calculate the latency of the player when a projectile is shot and sent to the server plus knowing the shot origin/velocity of the projectile, we can essentially say latency_offset = shot_origin + (latency * bullet_velocity) to determine how far forward in the vector direction the shot should really be when it starts on the server and other clients. This means it may appear to skip a few studs in front of the barrel when shot if lag is over 300ms, however... lets hope that kind of ping isn't common and with a 150 ping average in the heat of battle.. no one should really notice this gap between shot & projectile.
Hitscan
We treat hitscan slightly different because it entails an instant shot. We let the client do a raycast to get smooth visuals, while sending an event to the server to do the same thing. Once the client hits we show the visuals for the player and we validate that hit position against the position of the server hit. If its within a certain threshold, then it's 98% likely a successful shot and we then fire an event to all other clients to show the hit visual.
So what is the expectation?
For Quake 2 Weapons Factory in Roblox
The player who shot will see the FX visuals smoothly, while triggering an event to the server that validates ammo, the weapon, and runs the same calculation as the client, with no visuals. The server catches up to match where it should hit based on the players latency shown above and is the authority to determine if its a success or fail. Lastly, after the server validates the shot, it fires off an event to all players for the hit visuals to show.
Will the below video occur? This is a likely with a favor the player approach, but hey.. is it better then leading the shot?
What happens if the shot should hit within the gap of latency?
If the client says we should hit point blank, the server double checks with an instant raycast from the location of the shot to the location of where the latency_offset would skip and match up too. If there is a hit then it's likely a true statement from the client and it returns as a successful.
What about physics objects?
For grenades that rely on physics.. there is a subtle lag delay because the grenade object must be cloned by the server first and then passed network ownership to the client to control it. If anyone has a better idea on how to solve lag on server owned physics objects that become client network owned, please ping me and provide advice. I would greatly appreciate it.
That should cover it for now.
What about Devices?
The "Devices" implementation is almost complete, but is not ready for demonstration just yet. It works on the client, I'm just ensuring the server aspect is in mint condition to the new standards going forward as mentioned about netcode.
Therefore, I will showcase the alarm next week. One of the key differences will be the UI shown from q2wf vs wfr.