Validation¶
Every value that arrives is checked before your code sees it. A value that fails is dropped.
Two checks¶
| Check | Covers | Runs |
|---|---|---|
| Codec bounds | Types, ranges, lengths, counts, enum names, an instance's class | When sending, and on arrival |
:validate |
Any rule of your own | On arrival only |
A value that fails its bounds when sending throws at the call. On arrival, either check failing drops the value.
The trust boundary¶
The server is the trust boundary.
- On the server, values come from clients, who may be exploiters. A drop is logged as a warning.
- On a client, values come from the server, so a drop means the two disagree. It is logged as an error.
Checks of your own¶
Bounds stop malformed values. They cannot tell whether a player is allowed to do something, which
is what :validate is for. A check returns nil to accept, or a string to drop, which becomes the
reason in the log.
A check on one field, refusing a sender who fires more than ten times a second:
Damage = Lync.packet(Lync.int(0, 500):validate(function(amount, context)
if context.last ~= nil and context.now - context.last < 0.1 then
return "faster than 10 Hz"
end
return nil
end)),
A check on a whole value, for a rule between fields:
Trade = Lync.packet(Lync.struct({
give = Lync.int(0, 1000),
take = Lync.int(0, 1000),
}):validate(function(offer)
if offer.give == 0 and offer.take == 0 then
return "empty trade"
end
return nil
end)),
context field |
Holds |
|---|---|
player |
The sender. Nil on a client, and in Lync.decode. |
now |
When the value arrived. 0 in Lync.decode. |
last |
When this sender's previous value for this definition arrived. Nil in Lync.decode. |
lastadvances on every payload, including rejected ones and each payload of a batch. A sender cannot reset it or slip a burst past a rate check.- The context table is reused, so copy anything you keep from it.
- Checks run in the order you chained them, and the first reason wins.
What a drop does¶
| Result | |
|---|---|
| Your listener | Never runs for that value. |
| The log | One record with the reason, the definition, where it was declared, and the sender. |
| A request | Gets no reply, so the asker ends with "timeout". |
| The rest of the frame | Still read after a :validate rejection. Bytes that don't decode drop the rest of that frame. |
One client cannot flood your log: each gets 8 drop records per code every 10 seconds in each namespace. The rest are counted and logged as one summary. See Errors and logging.