{Proposal} Game Protocol: Client Capabilities

2 views
Skip to first unread message

Sharvil Nanavati

unread,
Jul 5, 2008, 8:28:29 PM7/5/08
to SubSpace Development
*NOTE*
This proposal differs slightly from the original forum post (http://
www.ssforum.net/index.php?showtopic=20841).

- A new capability, "*sendto supported", was added
- The capabilities are assumed to be the same as Continuum's unless
the client sends a reply to "Query Client Version" or "Query Client
Capabilities"

Summary:

This proposal contains two client-to-server and two server-to-client
packets. These packets will allow the server to determine which client
is connected and whether that client supports some pre-defined
capabilities. In addition, the client has the option to notify the
server if its capabilities change at runtime (e.g. due to user
interaction).

Rationale:

As 3rd party development flourishes, we see an increasing number of
non-Continuum clients connecting to game servers. Since some clients
implement a subset of the full Continuum feature-set, it becomes
necessary for a server to take a different action if some feature is
missing.

With ASSS, it is also possible to write extensions that are not
compatible with the Continuum client. However, not all clients are
expected to be able to support all extensions. As before, if the
server can query the client's capabilities, it can fall-back to a
supported action in case a capability is not available.

Packets:

Direction: Server to Client
Packet Name: Query Client Version
Packet: 0x80

Direction: Client to Server
Packet Name: Client Version
Packet: 0x80 <client (4)> <clientVersion (4)> <displayName (asciiZ)>
client:
0 - Reserved
1 - Starlight
clientVersion: an application-specific version number. This number
must increase with every public client release.
displayName: a string that can be used by the server to display the
client name. Example: "Starlight 1.0"

Direction: Server to Client
Packet Name: Query Client Capabilities
Packet: 0x81

Direction: Client to Server
Packet Name: Client Capabilities
Packet: 0x81 <capFlags1 (1)> [capFlags2 (1) [capFlags3 (1) ... ]]
capFlags1:
bit 0 - Extended Continuum Chat Types
bit 1 - Supports *sendto
bit 2..7 - must be zero
Note: this is a variable-length packet with a maximum of 256 capFlags
bytes. The server must accept all packets smaller than 258 bytes and
will interpret only the flags it understands.

Usage:

The following rules determine when the above packets can be sent:
1) The server may send "Query Client Version" or "Query Client
Capabilities" at any time after a connection is established (i.e.
after 0x00 0x02 packet)
2) The client must send "Client Version" only in response to a "Query
Client Version" packet.
3) The client may only send "Client Capabilities" after receiving at
least one "Query Client Capabilities" packet.
4) The client may send "Client Capabilities" at any time after
receiving "Query Client Capabilities".

If the server sends a "Client Version" packet and receives no reply
AND the client does not reply to "Query Client Capabilities", the
server must assume the same capabilities as a full, Continuum-
compatible client. If the server receives a valid reply to the "Client
Version" packet OR the "Query Client Capabilities" packet, the
following rules apply:

If the server does not receive a single reply to "Query Client
Capabilities", it should assume no capabilities. If the server
receives at least one reply but no subsequent replies, it should
assume the capabilities of the last reply.

Chris "Cerium" Rog

unread,
Jul 6, 2008, 1:05:02 AM7/6/08
to SubSpace Development
Most of what I had to say about this was posted on the forums (which
I'm too lazy to get at the moment), but...

Isn't *sendto a protocol feature? I thought it just sends the client a
new "assign player id/entering arena" packet set and lets them figure
out the rest. As long as a client properly implements the protocol,
shouldn't they just end up where the server wants them to be?

D1st0rt

unread,
Jul 6, 2008, 1:28:00 AM7/6/08
to SubSpace Development
> Direction: Client to Server
> Packet Name: Client Capabilities
> Packet: 0x81 <capFlags1 (1)> [capFlags2 (1) [capFlags3 (1) ... ]]
> capFlags1:
>     bit 0 - Extended Continuum Chat Types
>     bit 1 - Supports *sendto
>     bit 2..7 - must be zero
> Note: this is a variable-length packet with a maximum of 256 capFlags
> bytes. The server must accept all packets smaller than 258 bytes and
> will interpret only the flags it understands.
Is there a reason we need 256 bytes of bitfields? I would think it
would be more practical to have
capFlagsX:
byte 0 - identifier
byte 1 - previous bitfield (such as your capFlags1 definition)

This gives the advantages of making order not matter and also you only
have to include the bitfields relevant to the client.
Downside is that we half the number of flags but by the time we run
out of space in the packet, we should have come up with a solution.

Sharvil Nanavati

unread,
Jul 6, 2008, 1:35:28 AM7/6/08
to SubSpace Development
I believe that's an arena push that you're referring to. There's also
a Continuum-only *sendto that allows you to push a client to another
zone entirely and is sent via packet 0x3B. Not all clients support
that packet (since it's an undocumented but official addition to the
protocol) so I added it to the bitfield.

Sharvil Nanavati

unread,
Jul 6, 2008, 2:31:13 AM7/6/08
to SubSpace Development
There are up to 256 bytes of bitfields (but since only the first byte
is specified, the subsequent fields don't have to be bitfields) so if
all bits in subsequent bitfields are 0, only the first byte gets sent.

Your approach gets the same results but I think our expectations are
slightly different. You're trying to optimize for 'holes' in the
bitfields, expecting a sparse distribution. I expect that there will
be few holes and when there are 0-bytes, they will mostly be to the
right (which the client won't have to send at all).

In the short run, the dense assumption holds because there's only one
byte so there can't be any holes. :) In the long run, there will
always be cases where one approach is more optimal than the other but
I still think that the distribution will be dense and left-skewed.

Chris "Cerium" Rog

unread,
Jul 6, 2008, 3:36:38 AM7/6/08
to SubSpace Development
On Jul 6, 12:35 am, Sharvil Nanavati <Sharvil.Nanav...@gmail.com>
wrote:
> I believe that's an arena push that you're referring to. There's also
> a Continuum-only *sendto that allows you to push a client to another
> zone entirely and is sent via packet 0x3B. Not all clients support
> that packet (since it's an undocumented but official addition to the
> protocol) so I added it to the bitfield.

Ahhh, that.

Do you happen to have the packet structure handy? I'm curious what
info the client is given when being sent the request. I seem to recall
there being a security issue since either the client or the server was
either buffering the password or assuming the client is still ok to
login. Couldn't someone use this as a way to login as someone else
without their password if they send this packet to a client which
doesn't support it, then uses their own client to login as the target?

D1st0rt

unread,
Jul 6, 2008, 1:57:36 PM7/6/08
to SubSpace Development
I do agree with you that is more likely but obviously we won't know
until we get there. Another benefit of the id approach is that if for
whatever unforeseen reason we decide to revise or remove a bitfield
(to add more granular options or if it becomes obsolete) we could
essentially "defragment" the packet with less breaking of existing
handlers. Also if we wanted to burn another id, the same client would
be able to communicate with servers running different protocol
versions.

On Jul 6, 2:31 am, Sharvil Nanavati <Sharvil.Nanav...@gmail.com>
wrote:

Chris "Cerium" Rog

unread,
Jul 7, 2008, 12:10:26 AM7/7/08
to SubSpace Development
Plus, it gives us the ability to also use one whole byte for a value
rather than just 8 boolean values.

D1st0rt

unread,
Jul 7, 2008, 12:16:23 AM7/7/08
to SubSpace Development
Well actually in the current setup there is no limitation because they
aren't grouped at all. It just so happens that the first flags to get
allocated didn't take up a whole byte so filling out that one is the
next logical step.

Sharvil Nanavati

unread,
Jul 7, 2008, 3:36:08 PM7/7/08
to SubSpace Development
> I do agree with you that is more likely but obviously we won't know
> until we get there.

Excellent, it sounds like we agree on the expected behaviour of
clients. If, in practice, our predictions are wrong, we can certainly
come back and construct a new packet optimized for holes. Let's move
forward with the existing packet.

> Another benefit of the id approach is that if for
> whatever unforeseen reason we decide to revise or remove a bitfield
> (to add more granular options or if it becomes obsolete) we could
> essentially "defragment" the packet with less breaking of existing
> handlers. Also if we wanted to burn another id, the same client would
> be able to communicate with servers running different protocol
> versions.

Revising an existing packet (where the updated field in question has
already been defined) is terrible for maintaining application
compatibility. Even if all applications are rebuilt with the revision
taken into account, people will still be running older software and we
should not force them to upgrade.

I don't quite understand what you mean by defragmenting a packet -
could you please clarify?

The last point - "burning another id" - is perfectly acceptable and
should be the route we take if we need to make major revisions that
can't be integrated into this packet. There are plenty of packet IDs
available - an endless supply, even. Packet ID 0xFF is an escape
sequence in the official Continuum protocol to extend IDs beyond a
single byte. :-)

Sharvil Nanavati

unread,
Jul 15, 2008, 4:30:11 PM7/15/08
to SubSpace Development
Things have been quiet on this front too - are we ready to approve
this proposal? Let's get some feedback until Thursday and finalize by
end-of-day.

On Jul 7, 12:36 pm, Sharvil Nanavati <Sharvil.Nanav...@gmail.com>
wrote:

Sharvil Nanavati

unread,
Jul 19, 2008, 12:04:10 AM7/19/08
to SubSpace Development
I'll wait until Monday for more feedback on this and if there's no
major objection (or no feedback at all) it will be approved.

On Jul 15, 1:30 pm, Sharvil Nanavati <Sharvil.Nanav...@gmail.com>

Chris "Cerium" Rog

unread,
Jul 19, 2008, 2:48:01 AM7/19/08
to SubSpace Development
I still think that we should use something a bit more defined than
just a (potentially huge) set of n-bit fields. When we go and define
things that use 1 bit here and 4 bytes there, it makes parsing the
data hellish. I'd much rather see something a bit closer to what d1
suggested in that we have the actual packet data wrapped with some
kind of sub-id. This way we can choose not to include things, add
things in any order, etc.

From a development standpoint, this would be far easier to implement,
in my opinion. Sure, it's easy enough right now considering we're
looking at exactly 1 bit which is relevant, but if we add any
functionality to the client or (even better), bak or someone else
finishes a new client, this packet will become flooded with capability
flags and info. I'd much rather setup for the worst case scenario now
then have to deal with doing something that I feel will have to be
done eventually anyway. And hell, even if we don't ever have to deal
with anymore than what we've defined here, then we've simply added
another byte to the overhead and an additional line or two of code to
the implementation in exchange for a very clean and extendable client
caps packet.

Sharvil Nanavati

unread,
Jul 24, 2008, 7:56:47 PM7/24/08
to SubSpace Development
To be honest, I still don't agree with that approach - if we have to
selective about what information we're sending (which is the whole
point of these 'subtype' fields), we should be using different packets
entirely since they're not really capability flags.

Having said that, I don't think we're going to see an explosion in the
fields of this packet - I just wanted to have something well-defined
so that we know what we're getting ourselves into. I can live with
some overhead for a small, rarely-used packet if it means we can all
agree to something. Let's do what D1st0rt suggested:

Direction: Client to Server
Packet Name: Client Capabilities
Packet: 0x81 <subtype1 (1)> <data1 (?)> [<subtype2 (1)> <data2 (?)>
[<subtype3 (1)> <data3 (?)> ...]]
subtype:
0 - capFlags1
capFlags1: uint8
bit 0 - Extended Continuum Chat Types
bit 1 - Supports *sendto
bit 2..7 - must be zero

Where the subtype determines the type of the following data field. The
subtypes can be sent in any order but may not repeat in a single
packet.

Can we agree to this? Let's try to finish this off by end-of-day
Friday.

Cerium

unread,
Jul 24, 2008, 10:04:46 PM7/24/08
to subspace-d...@googlegroups.com
I can agree to that. I'd just make one difference in the implementation:

Rather than not allowing the subtypes to be duplicated, simply use the last
read copy of that subtype (probably closest to the end) for the final
values -- or basically, do no "have we seen this subtype" checking in the
code and just process everything as it's read.

But I suppose it's a "best practice" type thing anyway, so the actual
implementation won't matter a great deal.

Sharvil Nanavati

unread,
Jul 25, 2008, 1:32:37 AM7/25/08
to SubSpace Development
Actually, that's a good point - I forgot to specify what "subtypes ...
may not repeat in a single packet" actually means. The recipient isn't
responsible for validating the packet; rather, the sender must ensure
that the packet is well-formed (i.e. no subtype is repeated). If the
sender sends a packet that is not well-formed, the recipient is free
to take any action in response (use first subtype, use last subtype,
use random subtype, try to validate and throw error, terminate
connection, etc.). Besides, depending on the language and programming
style you use, it might be more natural to use the first subtype than
the last (consider inserting items into C++'s std::map where the key
is the subtype and the value is the data for the subtype).

I think it's fair to say that the response of the recipient is
implementation-specific in case a malformed packet is received - the
developer then has the freedom to choose whichever action is most
appropriate given the nature of the application. In short, we're
talking GIGO (http://en.wikipedia.org/wiki/Garbage_in,_garbage_out). I
would like to leave it open-ended so that:

1) Applications that send this packet really take the "don't duplicate
subtypes" statement seriously
2) Applications that receive this packet can do as much/little as
required to handle errors
Reply all
Reply to author
Forward
0 new messages