31 March 2021

March 2021

The headliners this month are prediction and addon downloading, but we did a ton of stuff.
Cursor Input
Layla is playing with something that needed the old GMod style cursor input, so I made that possible.

Join A Friend's Game
Being able to play with friends should be easy. This month I wrote a bunch of server lobby and rich presence stuff to make it really easy to join a friend's game.

I've mentioned this in previous blogs, but I think it's an important point. If you make a game then people will be able to join. You won't need to play with firewall stuff, it'll just work.
I got prediction all working. I had been putting it off for a couple of months because I thought it was going to be a pain in the ass, in the end it took 2 days and wasn't that bad.

What is prediction

So a quick explanation of how prediction works in Source. The client makes a user command which is a struct that describes the inputs of the player. Basic stuff like buttons and aim angles. It sends this user command to the server and the server moves the player, shoots the gun etc. 

While it's sending to the server it also runs it locally, so anything you do happens instantly. When the server runs its version and we get the results we compare them to the results our prediction got. If it all worked out the player should be in the same place, weapons should have the same amount of ammo etc.


Any variables you use during prediction need to be marked as predicted. If variables aren't marked as predicted, it could lead to further prediction errors because the client won't be able to get a good starting point where its in sync with the server.
There's nothing complicated here, it's like any other networked variable. 

First Time

If there's a prediction error the client might go back and re-predict a few frames back to get everything back where it should be. When it's doing that you don't want to do things like play sounds again.

To guard against this we have Predict.FirstTime. It's always true on the server, but on the client it's only true if it's the first time predicting that frame.

Turning Off

When you shoot a gun you play a sound. The sound is predicted locally, but when the frame is run on the server the sound is always played. We do some magic in the background to stop that sound playing again if it was predicted by the local client.

But sometimes you're only running some code on the server. Like for example, if you shoot a bullet and then someone dies, you might want to play that sound to everyone - the local player included - because they couldn't predict that sound.

In these instances we can use Prediction.Off() to turn off prediction. It creates an IDisposable so we can easily sope it. I don't love this syntax because we still have to check whether it's the server, so it might change.

Updated Glow Effect
Garry hated the L4D-looking glow effect that we had with the engine, so I rewrote it inspired by our Facepunch.Highlight mesh outline effect, it outputs to HDR so the glow itself comes naturally from the intensity of the outline really helping it pop out.
The whole effect is highly configurable, you can set if it's additive, opaque, and it's depth aware now, both visible and occluded parts of the glow can be changed, so for example, items that are being held by the physgun are partially visible when occluded.

To have the previous behaviour you only need to make both visible and occluded parts share the same parameters.

You can get really creative with this effect, playing with it allows creators to make something unexpected, if you are doing a Predator/Zombie-style gamemode you can easily use it to have a sort of wallhack vision for example.
ModelDoc Improvements
I made some more improvmenets to ModelDoc this month. ModelDoc is the tool used to create/import models into Source 2.

Multiple Bindposes

By default ModelDoc fbx import only supports one bindpose. And that's probably fine if you're making all your models bespoke.. but if you're like us and importing models from other places, you're going to run into problems.

The rust crossbow viewmodel was broken because of this. That's now fixed.

Viewmodel View Mode

There's now a viewmodel camera mode, which just locks the view to zero, this is useful for seeing how your viewmodel looks like in game and if you'll need to offset it. There's probably some other useful camera modes I can add in the future.

Import Source Models

I added support for importing Source1 models. It's debatable if we should even include this because we want to encourage new assets to be made, not just for old assets to be ported over. The way I see it though, people are gonna do it anyway and I'd rather people not download old janky MDL decompilers.

Physics Hull From Renderer Fixes

This was broken. It didn't visualize or calculate the mass info so I fixed that up.
Anyone that has gotten quite deep into mapping in Hammer probably knows about the Entity IO stuff. It's a system that lets you hook up outputs on entites to inputs on other entities.
For example, you can trigger an output on a button called "OnPressed" to an input in a light called "Toggle", and the button will turn the light on and off.

This month I re-implemented it in C#. To create an input on your entity you just mark it with an InputAttribute.
And to fire outputs you just call FireOutput
There's some finessing left to do here, but it works exactly how you'd expect.
File Transfers
There wasn't any code to transfer files from the server to the client on join, so I added some. This method of getting files from the server over the game network socket isn't ideal, but it works fine as a last resort.

The ideal scenario in production is that the files are downloaded in an addon, or from a website. That way we can support multiple downloads at once and it's a ton faster. This is how things should work 99% of the time and will totally be supported.

But I feel downloading from the server is important when developing an addon. If I'm making a gamemode and people are on my server I don't want to have to upload an addon for them to get my latest changes - they should get them live.

And that's what happens now. If I change a stylesheet on my listen-server the clients will be notified, they'll realize they don't have the new file and then download it from the server. When they download it it'll be hotloaded and everything will be in sync again.

Being able to live edit like this makes a huge difference. It changes the way you develop. The players on your server become lab rats, reacting to every change with disappointment or joy. It turns it into a game.
Async Strategy
I've been skirting around how async stuff works for a while but started to nail it down a bit this week. Here's a FAQ.

What is this Async stuff

This lets you run functions asynchronously. 
So if you call the function above, it'll run up until the await, then <seconds> seconds later it'll run Toggle. This is maybe unremarkable on its own, but you can chain these things.
They can also return values, so you can await a value from a function - which is really useful for things like calling APIs.

Is this multithreading?

By default it's coded so it is always run on the main thread, but it's possible to run tasks on other threads.

What's the strategy?

When thinking about Tasks for addons we need to think of a couple of things.

First of all if you're using a Task on an entity and the entity is deleted, what happens. I want to handle that in a nice and automatic way. I don't want to fuck about with CancellationTokens and end up making using async unbearably long winded.

Also, if you're running a task and then you disconnect from the server, the task should cancel. We shouldn't end up with a bunch of tasks running in the menu after the game is disconnected.

The default Task.Delay type functions are in realtime, we probably want to delay in game time by default.

And finally we want to be able to resume at specific times. Like maybe you want to wait until the next frame starts, or ends, or the next tick, or the next physics frame.

What's the status?

I believe we've got a decent, balanced solution to all of these things.
Player Using
I implemented the player's use button this month. I say player's use button, but again it's generic, so technically NPCs should be able to use stuff too.

To make an entity usable you just implement this interface.

I added autocomplete to the default TextEntry box. It can also have a cookie based history - so the history persists between game sessions. 

I did this for the console but it's there for everyone to use..
Entity Implementations
Instead of using the native entities we're  best off re-implementing them in c#. 

It feels like we have an opportunity here, starting fresh, to delete and clean everything up, to make things make a bit more sense.

I don't think Valve have that luxury, I'm guessing they have to support demos, saves and maps from 10 years ago, they can't just fuck it all off.

So this month as a first step I converted doors, buttons and a light entity to c#. 

This uncovered more stuff that I'd like to reimplement in c#. The native code has a MOVETYPE_PUSH system where entities (like doors) move and but push entities. They can kill entities that block their way etc.

It's a system we need because we don't really want to make things like the player/npc controllers purely physics based. But I also think it's a system that we're best off implementing in c# so we can manipulate it a bit easier.

Something else that came up was things like platforms and elevators. These things move in a very predictable fashion, but they're not predicted. I think we can fix that to make them silky smooth.
Voice Chat

Voice chat is something Source2 already had but like with most things, if something is gameplay related, we ideally want to reimplement it in C# so we can start from scratch and have complete control over the feature.

The player can record mic input, which gets sent to the server, these two functions below are virtual functions on the gamemode.
There's a server side function for incoming voice data, your gamemode can then decide who gets to hear that voice data.

Then there's a client side function for voice data that the server has sent. You have the opportunity here to do anything with the voice data, right now it's just a basic audio output but when we dig deeper into the audio side of the engine, I'm sure there will be lots of cool filters we can use to play the audio.

Scene Render Capture
We wanted a way to render 3D objects onto the UI. There's two parts to this, scenes you want to draw onto the UI ideally shouldn't exist in the main scene, it would suck to have to spawn a entity into the main world and hide it just to draw it onto the UI.

Instead, new scene worlds can be created and scene objects added to them.
Scene Objects are pretty much what the whole game uses to render the world so we can just use them without the overhead of creating new entities.

The second part is being able to render a scene world to a render target. Once you have the render target, that texture can just be drawn to the UI.
Because we have access to scene objects, they can also be placed into the main scene world. Imagine a pool table, instead of creating an entity per ball, you can just add balls as scene objects and they will render a model the same way a model entity would, without all the extra bullshit you don't need.

These model scene objects can be animated but right now the API just supports setting of bones. Next month I'll get it animating from the animgraph.


I got this working sooner than expected so here ya go
Gamemode Downloads
Installing stuff sucks. Imagine if you had to install a website before you could visit it. That's my attitude with gamemodes and maps in s&box.

I got the meaty bit of gamemode downloading working this month. The game will now automatically download from GitHub. This is a fun milestone for me because it means we can start making stuff and trying to impress each other.

This is mostly invisible to the end user, to the point where you can do "gamemode dm98" in the console and then "map testmap" and it'll automatically download dm98 and you'll be playing within 5 seconds.

Don't believe me? Check this out (dm98 isn't downloaded)
Obviously this is ridiculously quick because the gamemode is only 500kb, but keep in mind in this small loadtime it's querying github for the latest version, downloading it, unzipping it, compiling it and then finally running it.


The talk of GitHub might get people worried that we're not using workshop anymore. We're still going to use it, but as one of many options. 

Whether you want to upload your gamemode to workshop, GitHub, a zip on your website, it makes no difference. As long as we can download it.
Playtest Bugs
Early in the month, after I got prediction working, we did a quick playtest to make sure multiplayer was working. It was technically working for the most part but there were a few obvious bugs.

Error Models

A lot of the models were errors because the client couldn't find the real models. This is because the models are networked as ResourceID's - which are hashes of the filename and filetype. The client only has the ResourceID if the resource is loaded.

I found a bunch of network precache code in the engine but it wasn't hooked up to anything, so I deleted it all and implemented it in C#.

This should be nothing that a programmer has to worry about. When you set an entity's model it'll add it to the precache network string table automatically, which'll obviously get networked to the client, allowing them to work out what model it is.


People were able to use cheats. Specifically Layla was noclipping about and spawning bots. I tried to fix this a couple of times and failed, so ended up rewriting most of the console command system.

We really had two console systems. When we were running under Unreal Engine I'd made our own console system which was the only console system we used. It pretty much mirrored the way the source console system worked.

Now we're on Source 2 it makes less sense. We still want to be able to make console commands and console variables, but the only actual console system should be the native one. So that's how it works now.

Entity Offset

We've had a bug for a while where the client entity is offset from the server entity. You can see this in the above image.. the green wireframes the server physics. The silver bin is all the way down the end of the map on the server, but the client version is right there.

This is unsolved as of right now, pending investigation.
CSS Rendering Improvements
I took some good time to revisit UI elements; refactoring, optimizing and finishing up some loose ends, it's starting to feel almost as if you're able to design with the full power of a browser CSS engine now, without the gargantuan bulk of actually carrying a browser.

For every element we are doing a bunch of tests to make sure that everything conforms as expected.


The CSSBox container was almost entirely rewritten, both borders and the box itself has a very nice, smooth subpixel analytical AA, it looks specially nice when it's animating or moving dynamically like in a HUD element where you have a dynamic crosshair. 

Text rendering

Text, including emoji rendering is now super crispy, previously we were using some hacks to try to sharpen it
And you can see all tests we have for it:


I've also implemented 9-slice scaling, you can use an image as a border to your container and decorate it however you like, MMOs and RPGs type of games make great use of it, you're able to design it the same way you would with the border-image CSS parameter.
For reference to help visualize, the image used as the 9-slice texture for the border testing above looks something like this:


Even as we add more supported elements, all these elements should be conforming to the W3C standards to the best as possible, most of the knowledge people already have should translate seamlessly even when compositing complex effects when designing their UI on S&box, with the advantage we are not carrying an entire browser stack on our back.
Deathmatch Maps
I started off at Facepunch by experimenting with deathmatch maps - which should hopefully end up being versatile enough to be used in multiple different gamemodes.

Scale Map
We started running into a problem with some of the Rust assets because they use inconsistent scales. A lot of the feel of Source is the consistent scale for everything.

So to help us through this I made us a map to use as a scale bible when creating maps and models.

Unit Test Maps
Garry wanted some maps to test the functionality of buttons, doors, moving platforms and push triggers. These maps were created before the entities were implemented and were used to test them during development.
 They'll remain in place after the entities are all done as a way to easily test functionality.
CodeGen Optimizations
When compiling addons we run codegen over it. This is a modified version of Source Generators and enables a bunch of tricks we do with RPCs and network variables. This was taking about 4 seconds on our base addon (which has a lot of files).

I rewrote it and now it takes 0.1 seconds.
Virtual Scrolling
When you have huge lists of things in UI you often run into a problem of there being way too many panels. It doesn't matter how fast your UI system is, there's always a number of panels that is going to bring it down.

Virtual scrolling solves that by only showing the panels that are visible. As you scroll panels that go out of view are destroyed and panels that come into view are created.

So instead of adding panels to it, you add data to a list. Then you just have to tell it which type of child panel to create.

Job Listing: Animator
We're still looking for animator(s). If you're interested please check this out.
Another good month of progress, good velocity. It's really starting to shape into where I want it to start,

We've got a backlog of features and bugs that is expanding every month, but the biggest remaining task is a backend to register gamemodes/maps. I think I could probably knock that out in a day or two.

I say this every devblog but I'm a bit split on giving out a dev preview this month. Part of me wants to hold off and mess about making different gamemodes for a month. If we open up I don't think I'm going to be able to do that because there'll be documentation/features requests/bug reports. But on the other hand there'll be more people to play and share with.

I think I just talked myself into it. We'll give some keys out in April.