Securing Lightyear: Server-Side Input Validation
Hey guys, let's dive into something super important when we're building multiplayer games with Lightyear: how we handle player inputs. Right now, Lightyear's input system, in my opinion, leans a bit too much on the client side, and that opens the door to potential problems. We want to build games that are secure, where the server is in charge and can't be easily tricked or exploited. I'm going to explain why server-side validation is crucial, and how we can achieve it, making our games safer and more reliable. Let's get to it!
The Problem: Client-Side Input Authority
So, what's the deal with Lightyear's current input system? Well, when a client wants to control an entity, it simply slaps an InputMarker component onto it and starts sending input messages. The server, in response, adds an InputBuffer component. This process happens without any real checks to see if the client should be controlling that entity or if the entity can even handle the input the client is sending. Think about it: This system means that any client could potentially send inputs for any entity. That's a recipe for chaos, right? It's like letting anyone drive any car without checking if they have a license or if the car is even roadworthy. This lack of validation makes it easier for cheaters to exploit the game, leading to unfair advantages or even breaking the game entirely.
Now, I'm not saying the current system is bad. It's simple and gets things moving quickly. But for anything more than a small prototype, we really need more server-side control. We're aiming for a server-authoritative model, where the server dictates who controls what and what actions are allowed. This ensures that the server is the source of truth, and the clients are just following its orders. This will make your game much more resistant to cheating and other security issues.
Solution 1: Leveraging the ControlledBy Component
Here's where we can start to tighten things up. Lightyear already has a component called ControlledBy. It seems like a no-brainer that this component should play a key role in input handling. The ControlledBy component, in its current form, specifies which client owns or controls a specific entity. The idea is simple: when the server receives an input message, it should first check if the target entity has a ControlledBy component and whether the client sending the message matches the owner specified in the component. If they match, then the input is valid. If not, the input should be rejected. This is the first line of defense! It prevents clients from sending inputs for entities they aren't supposed to control.
Implementing this check in the receive_input_message system would be relatively easy, and the impact would be huge. It would mean that only the client who owns a particular entity is allowed to send input commands to it. The rest of the inputs can be safely ignored. It will be the single most important step in securing the game input. If the purpose of ControlledBy is not to control, it could be renamed to OwnedBy, as mentioned in the original context.
Think of it this way: the ControlledBy component is like the keys to a car. Only the person with the keys (the correct owner) can start the engine and control the car. Without the keys, all input commands are rejected, preventing unauthorized access.
Solution 2: Introducing ActionState for Input Capability
The second problem we need to tackle is what inputs an entity can actually handle. Not every entity should be able to do everything. Some entities might be able to jump, others might be able to shoot, and some might not be able to do anything at all. In Lightyear, there isn't an explicit way for the server to declare which inputs an entity should be capable of handling. This means that if a client sends an input message that an entity can't process, the server might still attempt to handle it, leading to errors or unexpected behavior. To solve this, we should make the server responsible for determining an entity's input capabilities.
I suggest we can solve this by adding an ActionState component or similar component that tells the game what an entity is able to do when it is spawned. This component would list all of the actions the entity can perform, such as moving, shooting, or using special abilities. When an entity is spawned, the server would add this ActionState component, defining which inputs are valid for that entity. By doing this, the server takes control over what an entity can do, ensuring that clients can only send input actions that the entity is actually designed to handle. Now, before processing an input message, the server would first check if the requested action is listed in the ActionState of the target entity. If the action is allowed, the server would process the input. If not, the input would be rejected. It ensures that only valid actions are performed. This also allows the server to automatically replicate the InputMarker to the client. The client will only have the ability to send inputs that the server already allows, making sure there are no surprises.
This would also help with cheating. If a client attempts to send an input for an action that isn't in the ActionState, it would simply be ignored, preventing the cheater from taking unauthorized actions.
Alternatives and Why They Might Not Be Enough
There's been a suggestion for a hook to manually verify incoming input messages. While this could solve the problem, it puts the burden on the game developer. Every single input would need to be manually validated. This approach has a couple of drawbacks. First, it requires extra code from developers. Second, it still leaves Lightyear as