Validating Client Transform and Property Updates
Summary
Shows how to use validators to do server-side validation of transform and property updates from clients for their owned entities. Server-side validators are used to prevent clients from cheating. An example script shows how to register a transform validator to restrict what transform values the client can set, and a property validor to restrict which properties ids, types, and value the client can set. A second example shows how to use a transform validator to limit the player's movement speed, and send corrective position deltas to the client in and RPC for the client to correct its position.
Description
Validators can be used to validate the transform and property updates that players send for entities they own. See Tutorial 12 for more information on entity ownership, and Tutorial 13 for validating a shared entity whose ownership transfers between players.
The following script shows how to use register a transform validator and a property validator. The transform validator prevents the client from setting the y-position below zero and the scale from being modified. It checks if the update will modify the scale by checking transform.IsDirty(ksReadOnlyTransformState.DirtyFlag.SCALE). You can check for position or rotation changes by changing the SCALE flag to POSITION or ROTATION.
The property validator prevents the client from setting properties outside a specific id range and validates the property type. Property validators are called once for each property. When you need to validate multiple properties together, it is best to do that in an Update handler when the properties have changed. This script sets a bool when property 100 or 101 change, and in an update handler if the bool is set, checks if the difference between property 100 and 101 is greater than 10 and if it is, changes property 101 to be 10 away from property 100.
using KS.Reactor.Server;
using KS.Reactor;
public class seValidator : ksServerEntityScript
{
private bool m_checkProperties;
// Called after all other scripts on all entities are attached.
public override void Initialize()
{
Room.OnUpdate[0] += Update;
Entity.OnValidateOwnerTransform += ValidateTransform;
Entity.OnValidateOwnerProperty += ValidateProperty;
}
// Called when the script is detached.
public override void Detached()
{
Room.OnUpdate[0] -= Update;
Entity.OnValidateOwnerTransform -= ValidateTransform;
Entity.OnValidateOwnerProperty -= ValidateProperty;
}
private void Update()
{
if (m_checkProperties)
{
m_checkProperties = false;
// If the difference between property 100 and property 101 is greater than 10, set property 100 to be 10
// away from property 100.
float prop100 = Properties[100];
float prop101 = Properties[101];
if (ksMath.Abs(prop101 - prop100) > 10f)
{
Properties[101] = prop100 + (prop101 > prop100 ? 10 : -10);
}
}
}
private ksValidationResult ValidateTransform(ksReadOnlyTransformState transform, float deltaTime)
{
// Don't allow the y-position to go below zero, and don't allow the scale to be modified.
if (transform.Position.Y >= 0f && !transform.IsDirty(ksReadOnlyTransformState.DirtyFlag.SCALE))
{
return ksValidationResult.PASS;
}
// If the y-position is below zero or the scale is modified, apply the transform changes manually with the
// y-position set to zero, and fail validation.
Transform.Position = new ksVector3(transform.Position.X, ksMath.Max(0f, transform.Position.Y),
transform.Position.Z);
Transform.Rotation = transform.Rotation;
return ksValidationResult.FAIL;
}
private ksValidationResult ValidateProperty(uint propertyId, ksMultiType value, float deltaTime)
{
// Don't allow the owner the set properties outside the range 100-200.
if (propertyId < 100 || propertyId >= 200)
{
return ksValidationResult.FAIL;
}
// Properties 100 and 101 must be floats, and property 100 must be within the range -100 to 100.
if (propertyId == 100 || propertyId == 101)
{
if (value.Type != ksMultiType.Types.FLOAT || (propertyId == 100 && (value < -100f || value > 100f)))
{
return ksValidationResult.FAIL;
}
// When property 100 or 101 changes, set a bool to do additional validation in the next Update.
m_checkProperties = true;
}
return ksValidationResult.PASS;
}
}Returning ksValidationResult.FAIL or ksValidationResult.ABORT from a validator will prevent the value from being applied. You can then manually set the transform or property to somtehing else from within the validator. If there are multiple validator functions registered, the value will not be applied if any return ksValidationResult.FAIL or ksValidationResult.ABORT. ksValidationResult.ABORT prevents further validator functions from being called, whereas ksValidationResult.FAIL does not.
The deltaTime parameter is the amount of time in seconds over which the property was updated on the client that sent the update. This can be used in calculations for limiting movement speed.
If the server rejects an update, the owner client is not notified. If you need to inform the client and take corrective action, you should do so using an RPC.
The following server scripts show how to limit the player's movement speed and send corrective position deltas to the client for the client to correct its position.
The following client script applies the correction deltas received from the server and acknowledges them by sending them back in an RPC. This script applies the entire correction delta at once. To make it smoother, you could apply the delta over multiple frames, in which case you would need to send only the amount of the delta you applied in an acknowledgent RPC each frame that you apply part of the delta.
Last updated

