server-1.12/doc/Developers/protocol

1528 lines
66 KiB
Plaintext

Updated $Date: 2009-01-19 00:07:11 -0500 (Mon, 19 Jan 2009) $ by $Author: lalo $:
Outline:
This section contains a brief outline of the document.
Background: List some of the background for client/server, and current state
of affairs.
General Socket Notes: How sockets are presently used.
Protocol: Commands that are sent back and forth.
Example Session: A brief example of what protocol commands would be sent
back and forth.
Programming notes: A few notes that can be useful for people writing clients
are extending the server.
Todo: Things to do in the future.
Note: each section is separated by a line of dashes. This should make finding
a specific section easier.
In order to make things a little for people doing porting, I have
added SUMMARY comments in some of the sections. These contain a very
brief summary of some aspect that is needed for writing the code. A more
detailed explanation of the SUMMARY can be determined by reading the
section.
------------------------------------------------------------------------------
Background:
Originally, the communications plan was set to be a text based system.
These messages are what is originally detailed below (now removed). It was
up to the server and client to parse these messages and determine what to
do. These messages were assumed to be 1 line per message.
At a reasonably early stage of development, Eric Anderson wrote a fairly
(but not totally) complete client/server that used his eutl package. This
package pretty much set up packets with subpackets - these subpackets would
have a tag for the data type, then the data itself. Thus, you could any many
types, and after transmission, the other end could decode these commands.
This works fairly well, but I think the creation of numerous sub packets has
some performance hit. Also, the eutl was not especially well documented,
so writing a client for a different platform became more difficult (you
needed to first port over eutl.) An example such of this is the Java client
currently in production. Also, Eric left to work on other products shortly
after writing his client, which didn't really leave anyone with a full
understanding.
I have decided to remove the eutl dependency. At least one advantage is
that having this network related code directly in the client and server
makes error handling a bit easier/cleaner.
However, instead of a straight text method, the outside packet method is:
<size (2 bytes)><data (size bytes)> The <size> is the size of the data
packet, the 2 byte size for the size information is not included here.
Eutl originally used 4 bytes for the size - to me, 2 bytes seems plenty (gives
a maximum packet of 32767 - I can't see ever going beyond a few thousand,
simply because in a fast action game, transmission size of such a packet would
probably not make things playable.) While saving 2 bytes might not be much,
it makes a least some sense.
The actual data is something of the nature of the commands listed below. It
is a text command, followed by possible other data. The remaining data can
be binary - it is up to the client and server to decode what it sent.
The commands as described below is just the data portion of the packet. If
writing a new client, remember that you must take into account the size of
the packet. there is no termination of packets, other than knowing how long
it should be.
For now, most everything that is sent is text. This is more or less how
things worked under eutl, except it packed the ints into 4 bytes in a known
order. In some cases, we handle ints as strings, in others, they are
sent as binary information. How any command handles it is detailed
below in the command description.
The S and C represent the direction of the data (S->C represents something
the server sends to the client, C->S represents something the client sends
to the server.)
In terms of all binary values, we use MSB order (same as eutl used). This
includes the initial length information, as well as any ints or shorts that
get sent inside the packets. All packets are defined to have at least one
word of text, followed by a space, then followed by optional data (which can
be binary.)
Side note: Generally, the data the client sends to the server is text,
but a fair amount of data the server sends to the client is binary. This
has somewhat to do with who wrote what code, and also has to do that the
S->C bandwidth is going to more the more serious limitation - the client
generally won't be sending so much data that the its flow is much problem.
Note that all the commands as detailed below are up to date descriptions
I removed a lot of the old notes on this file, because they were
out of date, and while might be good ideas, were not all that relevant to
how things currently work.
Summary: Packets sent back and forth have a 2 byte header (MSB order)
which contains the length of the rest of the packet.
------------------------------------------------------------------------------
General socket notes:
We are using a TCP/IP socket. Other methods could be used, but the
present protocol does not make very good provisions for missing data, so
it needs to be something that corrects errors/does resends automatically
(or just doesn't get errors in the first place.)
For now, we set non blocking output on the server side. This means we don't
have to worry about internal buffering.
If the connection is lost (which will also happen if the output buffer
overflowing), it looks like we just terminate the connection without saving
(meaning last save takes effect.) This can certainly be abused the same way
that currently killing the server (ie, go to treasure chamber, get all the
treasure, kill server, wait for map to reset, play again, with you starting
in the treasure chamber.)
I don't know if there is a really good way to handle it. The other method
would be to save the player back in town. But this then gets the situation
of 'oops, I'm trapped', lose connection, start back in town.
This is probably preferable - all you really gained there is a word of
recall spell/scroll. Also, it doesn't really hurt the honest players who
lost their connection for various reasons (and in fact, could be a disadvantage
if they can't connect again until after the map resets, and they lost all
the loot they were working on..)
The server only reads data from the socket if the player has an action.
This isn't really good, since many of the commands below might not be actual
commands for the player. The alternative is to look at the data, and if it
is a player command and there isn't time, store it away to be processed later.
But this increases complexity, in that the server must start buffering the
commands. Fortunately, for now, there are few such client commands.
If it becomes a case where the client is requesting images/sounds, dual
channels could probably be used, since requesting that data is not related
to the actual playing of the game (or a special daemon that serves those
requests could also be done.)
SUMMARY: TCP/IP sockets are used for exchange data. Server uses non
blocking i/o when writing to the socket, and the server only reads from the
socket when the player actually has time for an action.
------------------------------------------------------------------------------
Protocol:
Object tags: Many of the commands below refer to 'object tags'. Whenever
the server creates an object, it creates a unique tag for that object
(starting at 1 when the server is first run, and ever increasing.) Tags
are unique, but are not consistent between runs. Thus, the client can
not store tags when it exits and hope to re-use them when it joins the
server at a later time - tags are only valid for the current connection.
I have decided to break the protocol into various sections which based
somewhat on what the commands are for (ie, item related commands,
map commands, image commands, etc.)
******************************************************************************
COMMANDS RELATING TO ESTABLISHING THE INITIAL CONNECTION AND CLOSING THE
CONNECTION
C->S: version <csval> [scval [vinfo]]
S->C: version <csval> [scval [vinfo]]
Through the version command, the client and server exchange what
version of the protocol they understand. Neither send this
in response to the other - they should both send this
shortly after a connection is established.
csval is the version level of C->S communications.
scval is the version level of S->C communications.
vinfo is a string that is purely for informative that general
client/server info (ie, javaclient, x11client, winclient, sinix server,
etc). It is purely of interest of server admins who can see what
type of clients people are using.
If a new command is added to the protocol in the C->S direction, then
the version number in csval will get increased. Likewise, the same
is true for the scval.
As far as the client is concerned, its scval must be at least equal
to the server, and its csval should not be newer than the server.
The server does not care about the version command it receives right
now - all it currently does is log mismatches. In theory, the
server should keep track of what the client has, and adjust the
commands it sends respectively in the S->C direction. The server is
resilant enough that it won't crash with a version mistmach
(however, client may end up sending commands that the server just
ignores). It is really up to the client to enforce versioning and
quit if the versions don't match.
scval and vinfo was added starting in 1020. Before that version,
there was only one version sent in the version command.
The version are currently integers, in the form ABCD.
A = 1, and will likely for quite a while. This will only really change
if needed from rollover of B.
B represents major protocol changes - if B mismatches, the clients
will be totally unusable. Such an example would be change of map or
item sending commands (either new commands or new format.)
C represents more minor but still significant changes - clients might
still work together, but some features that used to work may now fail
due to the mismatch. An example may be a change in the meaning
of some field in some command - providing the field is the same size,
it still should be decoded properly, but the meaning won't be processed
properly.
D represents very minor changes or new commands. Things should work no
worse if D does not match, however if they do match, some new features
might be included. An example of the would be the C->S mark command
to mark items. Server not understanding this just means that the
server can not process it, and will ignore it.
Note: Since all 'packets' have the length as the first 2 bytes, all
that either the client or server needs to be able to do is look at
the first string and see if it understands it. If not, it knows how
many bytes it can skip. As such, exact version matches should not
be necessary for proper operation - however, both the client and
server needs to be coded to handle such cases.
Note 2: For the most part, this has been obsoleted by the
setup command which always return status and whether it
understood the command or not. However there are still some cases
where using this versioning is useful - an example it the
addition of the requestinfo/replyinfo commands - the client
wants to wait for acknowledge of all the replyinfo commands
it has issued before sending the addme command. However, if the
server doesn't understand these options, the client will never
get a response. With the versioning, the client can look at
the version and know if it should wait for a response or if
the server will never send back.
C->S: addme
Tells the server that it should add me (the client) to the game.
Generally, the client will always send this command, but I suppose there
can be actions the client wants to do before being added.
S->C: addme_failed
S->C: addme_success
This are responses to the last addme command. I really think these
should be discontinued, as they are one of the few messages which is
just a confirmation of a previous messsage. The addme_failed should
really be replaced with a terminate type of of message (player quits
game, server could inform us nicely and exit out). addme_success is
really of no use - client just throws it away.
S->C: goodbye (Added in SC protocol version 1022)
Informs the client that the server has finished transmitting data
to the client. This is a bit cleaner than the client detecting
a read error. In theory, a C->S of the same type could be done,
but I don't think it would make a big difference for the server (is
going to do the same thing regardless of a clean connection drop
or a bad one).
Also see the setfacemode command below.
******************************************************************************
COMMANDS RELATING TO PLAYER INPUT AND OUTPUT IN THE INFO WINDOW
C->S: ncom <packet> <repeat> <command>
(ncom = new command)
Client sends a command to the server. Ordinary commands (ie, north,
west, apply, maps, etc), might be sent, commands with options may be sent
(ie, 'invoke create food of booze', 'cast medium fireball'). There are a
few special commands that can also be sent.
Packet is a 16 bit arbitrary value the server will send back through comc
to inform the client of execution end.
repeat is a 32 bit value and is the repeat value, or a count value used
for instance for dropping.
command is the actual command data (north, whatever).
'fire' command is a special case. The server will handle repeat firing.
'fire_stop' will be sent to inform the server to stop firing. A
different command name has been chosen to make things easier on the
server ('fire_stop' should be a 0 time command, with the rest of the
fire commands actually taking some time.) In some cases, 'fire_stop'
may be sent almost immediately after the first fire (in cases where the
player only wants to fire once).
S->C: comc <packet> <time>
(comc = completed command)
This is used in the window of the ncom command above. packet is
the command number we just executed (16 bit binary), and time
is a 32 bit value represent how many milliseconds the player is
currently taking to execute commands. This information can be
used by the client to do some throttling.
C->S: reply <text>
Sends <text> as a reply to the last query command sent.
S->C: drawinfo <color> <text>
Tell the client to draw whatever text in color. Color are specified
in newclient.h, and is sent as a string. The client is free to do
whatever it wants with the color information (which may very well
mean ignore it.)
S->C: query <flags> [text]
Asks the client program for input. This is only used in a few places -
mostly in creating a character and login, but in fact anyplace in the
server that changes the input state (pl->contr->state, ST_*), should
send a query.
<flags> are detailed in the <newclient.h> file, which is common to both
the client and server. The flags of relevance to this command are the
CS_QUERY flags. <flags> are sent in plaintext form.
The client is free to ignore the flags. However, if the server just
wants a single character as a reply, that is all it uses, even if the
client sends a long string (Server will use the first character.)
[text] is the question/prompt that should be printed. Right now, it is
typically just a ':'. Client should display it no matter what is is,
however. Text is an optional field. Note - as of 0.94.2, text may
be multi line, delimited by newlines. Client should handle this
appropriately.
C->S: toggleextendedtext <type>...
Ask the server to send extended text information for a given type.
type is a list of decimal integers.
Currently supported/reserved values for types are described in drawextinfo
S->C: ExtendedTextSet <type1> <type2> .... <typen>
Tell client what actually are the extended infos server may
send to the client when this is needed. All those infos will
be related to the map and send through mapextended command.
Each string represent an info which is enabled. Look
at toggleextendedinfos and drawextinfo for details.
S->C: drawextinfo <color> <type> <subtype> message
Tell the client to draw specific text. Color are specified
in newclient.h, and is sent as a string. The client is free to do
whatever it wants with the color information (it may very well ignore it.)
color same as color info from S->C drawinfo
type is an int in decimal representation, giving the type of message
subtype is an int in decimal representation, giving subtype
(flavor) of message.
message is a string representation of textual message. content of message may
very well vary depending on the type.
The server will never send a message to a client with a message type not
requested at setup by a toggleextendedinfo. Client is however encouraged to handle
those case to catch bugs in protocols which may arise in future.
It is possible a client handles a given message type but not a given subtype.
The server does not care about the client subtype (flavors) and, so, client
should have a 'generic representation' for each supported type, which will be
used when a given subtype is not supported. The type are made a way subtypes
can be considered as just visual variations (eg a scroll,a card, a letter
and a book will share the same type)
Values for types and description:
0 reserved
1 books
books contains media tags in their body (see doc/mediaTags)
message has the form <title>\n<body>
subtype represent the style of book.
MSG_TYPE_BOOK_CLASP_1 1
MSG_TYPE_BOOK_CLASP_2 2
MSG_TYPE_BOOK_ELEGANT_1 3
MSG_TYPE_BOOK_ELEGANT_2 4
MSG_TYPE_BOOK_QUARTO_1 5
MSG_TYPE_BOOK_QUARTO_2 6
MSG_TYPE_BOOK_SPELL_EVOKER 8
MSG_TYPE_BOOK_SPELL_PRAYER 9
MSG_TYPE_BOOK_SPELL_PYRO 10
MSG_TYPE_BOOK_SPELL_SORCERER 11
MSG_TYPE_BOOK_SPELL_SUMMONER 12
2 cards
3 papers
4 signs
5 monuments
6 scripted dialogs
7 motd
no subtype, content of message is the media tag enabled motd
8 admin
9 shop
10 command
Responses to commands, eg, who.
11 attribute
Changes to attributes (stats, resistances, etc).
12 skill
Messages related to using skills.
13 apply
Applying objects.
14 attack
Attack related messages.
15 communication
Communication between players.
16 spell
Spell related info.
17 item
Item related information.
18 misc
Messages that don't go anyplace else.
19 victim
Something bad is happening to the player.
******************************************************************************
ITEM MANIPULATION RELATED COMMANDS
Client requests to server:
C->S: move <to> <tag> <nrof>
All parameters are integers sent in string format. <to> is where to move
the object to, tag is the object tag, nrof is how many to move. This
command is used to pickup/drop objects. If <to> or <tag> is zero, the
object is being moved from/to the ground. This command is used to move
items from a container into inventory and vice versa. if nrof
is zero, all objects will be moved.
C->S: examine <val>
Tells the server that I want to examine object <val>, where <val> is a
text string that represents that objects tag. Objects tags are unique
for each object.
C->S: apply <val>
Tells the server that I want to apply the object <val>. Like examine,
<val> is a text representation of an integer tag.
C->S: lock <val><tag>
Tells to server to set the inventory lock flag of item tag to val.
val of 1 means that the item should be locked, 0 means unlocked.
val is 1 byte binary, tag is 4 byte binary. The server should send
and upditem command with new flags to reflect this change.
C->S: mark <tag>
'marks' and item for secondary usage. Some actions in crossfire
require an action that uses another item (improvement scrolls, flint
& steel). In order not to rely on inventory ordering or other
gimmicks, this 'marked' item is used. Only one item can be marked
at a time - server will only keep track of the latest mark sent.
<tag> is a 4 byte binary value. The server will generally send a
drawinfo command informing the player, but there is no especially
easy way for the client to know what the marked item is (although,
client knowing this is not strictly needed)
C->S: inscribe 0 <tag1> <tag2>
Player wants to write spell 'tag1' on scroll 'tag2'. This is roughly equivalent
of manually marking an item, readying the spell, and using the inscription skill.
'0' should be one byte, tags should be 4 byte integers.
Server updates to client:
S->C: item2 <location><tag1><flags1><weight1><face1><name1><anim1>
<animspeed1><nrof1><type1><object2....>
Sends item information to the client. All parameters are sent as 4
byte ints, except for name, anim, animspeed and type.
location is where the object is located (0=ground, any other value
means is the tag of the object (either player or container)). This
value is sent as 4 bytes.
tag is the item tag - unique for each item (however, a tag might
be resend, to tell the client to update some object.)
flags are various flags on the item (curse, applied, etc). They are
detailed in newclient.h It is 4 bytes
weight is the weight of single one of these objects (in grams).
The client will need to figure the total weight on its own.
face is the face number. These are not guaranteed to be the same
across different runs of the game (however, in reality, they will
only change on the one server if they make changes to the archetypes
and rebuild.) Some face information will be sent from the server
to the client before actually sending a face number.
name is the name of the object. The first byte of this field is the
text length of the name. Starting at SC 1024, this name is two
strings, with a null separation. The first byte (length) is the
length for both of these strings. This name information is just
the information of what the object is called. It does not include
how many of the items there are.
anim: This is the animation sequence id to use. It is 2 bytes.
The server will send an 'anim' command for this before sending an
item1 command with this anim command.
animspeed: How often the object should be animated. This is 1
byte, and is the number of ticks that should pass between each
animation (a value of 1 means it should be animated every tick.)
1 byte limits this to once every 255 ticks - I can't see anything
being animated slower than that.
nrof: How many objects comprise this item. This is used for
the name field and calculating total weight. It is 4 bytes.
type: A numeric type id for the item. The only meaning of this
value is really for sorting - all armors will have type values
the same or near that each other. The client is free to ignore
this. This value is 2 bytes.
S->C: upditem <flags><tag><vals>+
This updates some item (of tag) with new values. flags determines
what values are sent and to be updated (for a definition of the flag
values, see the UPD_ flags in newclient.h file.) The order of the
vals is the same as in the item command - however, as additional
values are added (and the <flags> extended), the order will remain
the LSB order of the flags - that is, the value associated with bit
1 set is sent first, then bit 2, etc.
The format of the values is same as the item command above.
Only one item can be updated with the upditem command. An item
command should have been sent by the server before an upditem
command is set.
S->C: delitem <tag1><tag2>...
Tells the client to delete items with the tag values.
S->C delinv <tag>
Tells the client to delete items carried in/by the object <tag>.
<tag> is sent as plaintext numbers. Tag of 0 means to delete
all items on the space tech character is standing on. This command
only affects the inventory of the object. To fully delete a
container object, a delinv followed by a delitem should be issued.
S->C addspell <tag1> <level1> <casting time1> <mana1> <grace1> <damage1> <skill>
<path1> <name1> <display name1> <message1> <spell2 ....>
Tells the client to add the spell(s) listed to the list of spells
the client knows about. This will be sent at login, and again whenever
new spells are sent.
The fields are;
<tag> - (4 bytes - int) The ID number for the spell item. This is
going to be unique, for each spell and will be used to refer
to it henceforth. The string form of this should also be
appended to the cast/invoke commands in order to cast the spell.
<level> (2 bytes, signed int)
The level of the spell.
<casting time> (2 bytes, signed int)
The time it will take to cast the spell, in server ticks.
<mana> (2 bytes, signed int)
The mana cost to cast the spell (may be zero)
<grace> (2 bytes, signed int)
The grace cost to cast the spell (may be zero)
<damage> (2 bytes, signed int)
The current damage done by the spell. Note that the meaning of
this number is to a large part spell dependent, what damage it
actually does will depend on how the spell works.
<skill> (1 byte, unsigned int)
The skill that the spell uses to be cast, if zero, no skill is
used in the casting of this spell.
The numbers are the same as for request_info skill_info
<path> (4 bytes, unsigned integer)
The path that the spell belongs to.
The client should determine the effect of this by comparing
these values to both the spell_paths request_info data and the
stats info concerning attunement/repulsion, etc.
<face> (4 bytes, signed int)
The number of the face that corresponds to the spell, the client
can request this facenumber if they want to show a graphical spell
representation.
<name> (1 (non-zero) length byte, followed by that many bytes of ASCII text)
This is a name to identify the spell, which the client can use
for display purposes, it should /NOT/ be used with the 'cast'
command, whilst it might work, no such guarantee is made by the
server. - Use tag instead.
<message> (2 length bytes (which may be zero) followed by that many
bytes of ASCII text)
The description of the spell. Note that this has an extra length
byte because the messages may well be longer than 256 bytes in
length.
S->C updspell <flags><tag><vals>+
This updates some spell (of tag) with new values. The flags are 1 byte
and determine which values have been updated, and should be re-read.
Not all fields may be updated by this command, only those that can be
changed.
If new fields are added in future, they will extend the flags bitmask
and the order will remain the LSB order of the flags - that is, the
value associated with bit 1 set is sent first, then bit 2, etc.
The format of the values is same as the addspell command above.
Only one spell can be updated with the updspell command. A spell
command should have been sent by the server before an updspell
command is set.
S->C delspell <tag>
Tells the client to remove its information about the spell Tag is a 4
byte value, the same as the one sent when the spell was added.
******************************************************************************
COMMANDS RELATING TO THE PLAYER OBJECT/STATS
S->C: player <tag><weight><face><name>
All fields are the same as described in item above. The only
difference is that player tells the client that this is the central
object it needs to care about.
S->C: stats <stat1><val1><stat2><val2>...
This is a server message that tells the client values of the various
stats. All values are binary. that <stat> values are listed in the
newclient.h file and are sent as 8bits. All the values sent are
16 bits with these exceptions:
-weight limit:
data is 32 bits.
-speed, weapon_sp is converted to an int first by multiply by
FLOAT_MULTI (as defined in newclient.h) and then sent as
32 bits.
-range, title are sent as strings with their length preceded. The
length is 1 byte.
-experience: If the client uses the 'exp64' setup flag, this is sent
as 64 bit data, otherwise, 32 bit data (with appropriate loss of
information. To make tracking of this easier, a different stat
type is used for 64 bit data (CS_STAT_EXP64) compared to 32 bit -
thus, the client can know what to do based on stat type. However,
for the run of the session, either CS_STAT_EXP64 of CS_STAT_EXP will
get used, never both.
-skill experience: this is only set if exp64 is set. This is sent
as the skill level, followed by the skill exp (thus, 9 bytes +
the stat type byte)
-spell paths: if spellmon is set in setup, the bitmask of attuned,
repelled and denied paths is sent. These are all 32bits
******************************************************************************
COMMANDS RELATING TO IMAGE INFORMATION TRANSMISSION
S->C: anim <num><flags><face1><face2>...
Informs the client of an animation sequence. The client is responsible
for animating the objects in the inventory window, and upditem
and other items command will refer to the animation number with
num above. All values are 2 byte binary values.
<num> is the animation number we are defining. The server will only
send the anim command for a particular <num> once per run - the
client needs to keep track what has been sent. On new runs,
anim commands will be resent.
<flags> is currently unused, but is included because I think there
may end up being cases were more about the animation than just the
num and faces are needed.
<face1>... is the various faces that comprise the animation
sequence. The number of faces can be determined by checking the
length of the packet. These values correspond in the same
way as all references to face do.
Note that how fast the object is animated is contained in the
item commands.
S->C: image2 <face><set><len><data>
Sends a png version of an image to the client. <face> is the face
number (4 bytes), <set> the faceset the image is part of,
<len> the length of data (4 bytes), data is the png data used to
the image.
S->C: face2 <num><setnum><checksum><name>
Informs the client that image <num> (binary short) of faceset <setnum>
(A byte) is associated with <name>. This is used when the client is
caching images. In normal
operation, when the server runs across a face that it hasn't sent
the client, it sends a png for that face. If the face
mode is none, the server then sends this command. The client can
then check to see if it might have cached this face, and if not,
should then request it from the server. Note that the num to name
mappings can change between server and different runs of the
server. For this reason, this data needs to be sent each
time run. The client should be able to load/determine what face
to load via the name.
C->S: setfacemode <val>
This tells the server what type of display mode the client is using.
<val> is a plaintext integer. 0=no faces, 1=bitmap, 2=xpm (pixmap).
3=png (added in CS version 1022)
If the 5'th bit is true (ie, 0x10 & val is true), that then informs
the server that client is caching the images, and only send image
names.
This command is depreciated, as the only type of images currently
supported is PNG. The client should instead use the setup command
to request if it wants to cache images or not.
C->S: askface <num>
Requests that the server send the client face <num>. It will rely
on the previous set facemode to determine what facetype to send.
num is a plaintext integer.
S->C: smooth <facenbr><smoothpic>
All parameters are short int in binary form (2 bytes each)
This command informs the client on how to smooth a face, when it will need it.
Following are the facenbr of the picture involved in the
smoothing algorithm. See doc on smoothing on how to use them.
This info may be send automatically from server if client has
smoothing enabled but may also be requested by client using
the asksmooth command below
C->S: asksmooth <facenbr>
Parameters is a plain text integer.
Ask server to send a smooth sequence. Server will respond with a smooth command.
<facenbr> is an integer telling server which face we want smooth information on.
******************************************************************************
MAP UPDATE COMMANDS
S->C: map2 <coord1><len1/type1><data1><len2/type2><data2>...<coord2>
This is an update of the map1 commands above [removed]. It is meant to be
extensible. It is also meant to incorporate the ideas of the
extended map info command.
All data is in standard binary form.
The coord value is 16 bits.
the coord values are length + x + y values.
The data represented looks like this:
first 6 bits: The x coordinate (0-63)
next 6 bits: the y coordinate (0-63)
LSB 0-3: Has the following meaning:
0: Normal coordinate
1: Use this coordinate for scroll information and not
an actual coordinate. This removes the need for the
mapscroll command. Note that coordinate are still
modified with the MAP2_COORD_OFFSET as described below -
as an example, if 14, 14 is passed, that means effective
scroll of -1, -1. This should only be set in the first
coordinate value. Another (real) coordinate pair
will immediately follow this coordinate pair.
2-15: Unused/reserved.
The x & y coordinates are offset by MAP2_COORD_OFFSET (15).
This is necessary because there may be effects noticeable to the
player (light sources) that to the left/above the visible map.
By using this offset, we can use a coordinate like
10, 27 to denote that something is 5 spaces above the top
of the map.
<len/type> This is a single byte of data.
This describes the data that is to follow. If this byte is 255,
then this is a coordinate termination byte - this means a 7+
length field of type 1f is not possible.
The top 3 bits (len) denote the number of bytes that follow - it is
possible that this is zero, to denote all the relevant information is
included in the type. If this is 7 (all bits set) then the following
byte is an additive length value. Currently, nothing has a 7+
bit length.
The bottom 5 bits is the type of data - this allows for 31 different
types of data (0-31/0x0-0x1f). The meaning of the data itself depends
on what the type is. List of various types:
0x0: Denotes this space should be cleared. Length in this case should
also be zero, as there is no data that follows. Clear in this
context means that all data associated with the space should
be purged, eg, images, sound, darkness, whatever.
0x1: Darkness information - a single byte follows on how light/dark
the space is. 0 is completely dark, 255 is full bright.
Note that 0 will never be sent - if the space is completely
dark, players won't be able to see it.
0x2: Sound?
0x3: Light sources?
0x4 - 0xf: Unused
0x10-0x19: Image information. Layer 0x10 is the lowest, 0x19 is the
highest. There are several forms of this data depending on the
length:
2 bytes: This is only the face number.
3 bytes: <face num>[<smooth>|<animspeed>]
4 bytes: <face num><animspeed><smooth>
smooth is a single byte for smoothing information.
if face_num has the high bit set, then this object is
an animation, and the byte after the animation is animation
speed (see below for more details). Smooth information
may still be sent for animations, and that would follow
the animspeed.
If face is 0, that means that the face (or animation)
is no longer visible, and smooth information should also be
cleared.
For animations:
face will have the high bit set to denote it is not a
face but an animation id.
MSB-1, MSB-2: These are used to denote type of animation:
0: Normal animation - start at first phase, etc.
1: Randomize - randomize the animation phase & timing.
2: Synchronize - this animation should be in the same phase
as other animations with the same id. Used for things
like oceans.
Like the itemcmd, the server will send information for
the animation to the client before using the animation
id.
Animations have an extra byte after the animation id -
this is the animation speed. Like the itemcmd, it is
how long, in ticks, between animations. 1 means it should
be animated every tick.
Some notes:
Coordinates outside the viewable map may be sent. In these cases, it
means that a big image that extends onto the viewable map is on that
space. For big images, only the bottom right coordinate is sent -
this is why it may be off the viewable coordinates. For such spaces,
only the actual big image itself will be sent for that space.
Note that all operations are considered updates to the space (eg, new
image, new light level, etc). The exception would be the clear
command, which means clear all data with the space.
Note that while not used now, order of these subpackets is important.
A clear (0x00) followed by other data should be parsed in that order -
clear the data, then process the data. In contrast, sending
data followed by a clear byte makes no sense. This functionality
will likely be used in the future - for example, if 6 layers need
to be cleared and the other 2 remain the same, it will be more
efficient to send that clear byte followed by the 2 layers to
redisplay instead of sending 6 layers with an empty face..
Relative to the map1/map1a commands, this is more bandwidth intensive -
basically, an additional byte is needed for each piece of data sent.
Thus, on a 25x25 map, if we presume 1.5 objects/space, this is
an extra 940 bytes to send. OTOH, typically the entire map
is not being sent - only those bits that change, so this may not
be as costly as that.
If the player is using smoothing, this may actually save bytes,
as the redundant coordinates and type/length information
does not need to be sent. With the map2 command, the mapextend
command is deprecated and is not used.
General design notes: For data types that vary in length because
of optional data, the required data should be sent first, followed
by optional data if appropriate. An example of this is the face
information - we send the 2 face bytes first, then follow that
with optional data (smoothing and/or animation data). This makes
parsing on the client easier - basically, the client should be
able to parse the data a byte (or pairing at a time).
S->C: tick <tickno>
<tickno> is an unsigned 32 bit binary value.
This just tells the client what the current tick is. Right now,
the client only uses this to know when to animate the images that
the client is responsible for animating. This will only be sent
if negotiated with the setup command.
S->C: map_scroll <dx> <dy>
This tells the client to scroll the map dx and dy direction. dx and
dy will typically be -1, 0, or 1, depending on how the player moved.
<dx> and <dy> are sent as plaintext. positive values are down and to
the right respectively, negative values are opposite.
C->S: mapredraw
Requests that the server resend the entire map to the client -
can be useful if the client or client player knows that the map
is out of date/corrupted.
S->C: newmap
This tells the client to clear the map state. This command will be sent
only if negotiated by the newmapcmd option.
S->C: magicmap <width> <height> <px> <py> <data>
This gives the client information from a magic map command.
<width> <height> are ASCII integers of the size. <px> <py> is
the player's location (ASCII), and data is binary data of the
appropriate color. It is 1 byte per space, with the low
nibble containing the color information, and the high nibble
containing extra flags, like the existence of walls and floors.
See the FACE_FLOOR and FACE_WALL values.
The string of data represents the space from left to right,
then up to down.
C->S: toggleextendedinfos <string1> <string2> .... <stringn>
Ask the server to send some additional information about the map.
This command is followed by 1 or more strings. String are separated
with spaces. Each string toggle an info. The server will respond
with the command ExtendedInfoSet telling client what actual
extended infos will be send to the client.
Valid extended infos are as follow:
smooth
send smoothlevel information to the client.
S->C: ExtendedInfoSet <string1> <string2> .... <stringn>
Tell client what actually are the extended infos server may
send to the client when this is needed. All those infos will
be related to the map and send through mapextended command.
Each string represent an info which is enabled. Look
at toggleextendedinfos and mapextended for details.
******************************************************************************
SOUND RELATED COMMANDS:
C->S: setsound <val>
Obsolete command, should not be used, will be removed at some point.
S->C: sound <x><y><num><type>
Obsolete, will never be sent.
S->C: sound2 <x><y><dir><volume><type><len of subtype>subtype<len of name>name
Plays a sound. See the 'sound' document for more information.
'x' and 'y' are bytes, position of the sound relative to the player.
'dir' is a byte from 0 to 8, the direction the sound is moving to.
'volume' is a byte from 1 to 100, arbitrary intensity of the sound.
'type' is a byte, the major sound type.
'len of action' is a byte, length of action.
'action' is a string, the actual sound name to play.
'len of name' is a byte, length of name.
'name' is the name of the sound emitter, that can be used to find a specific sound.
S->C: music <string>
Change background music. Server will send NONE as the string to stop any
music from playing. (Due to someone forgetting to update this file when
he/she implemented the command, it is unknown what exactly should be done
with the string parameter, is it a filename? If yes: relative what?)
******************************************************************************
MISC COMMANDS:
C->S: lookat <dx> <dy>
Client (player) is looking at space dx,dy. dx and dy are delta
offsets from the player (client doesn't know where the player is
on the map, so this is the only real way to deal with it.) dx
and dy plaintext integers. This is only a request to the
server - a response will typically come back in drawinfo commands.
C->S
S->C: setup <option1> <value1> <option2> <value2>
Client first sends a setup command to the server to request a change
in some value (option1).
The following options are supported:
(please keep this alphabetized to make it easier to see different
options)
bot (0/1 value):
If set to 1, the client will not be considered a player when
updating information to the metaserver. This is to avoid having
a server with many bots appear more crowded than others.
darkness (0/1 value):
If set to 1 (default), the server will send darkness information
in the map protocol commands. If 0, the server will not
include darkness, thus saving a minor amount of bandwidth.
Since the client is free to ignore the darkness information,
this does not allow the client to cheat. In the case of the
old 'map' protocol command, turning darkness off will result
in the masking faces not getting sent to the client.
exp64: If true, client can handle the 64 bit exp values that
are now used. Otherwise, values are sent as 32 bit. Setting
this flag also means that skill exp will be sent, and it will
be sent in revised method as described in the stats command.
Value is an integer in string format.
This is always the default on servers with protocol version >=1028.
extendedMapInfos (0/1)
Toggle sending from server of extended map informations.
What lies in this extended info depended on what extended
infos the client asked. See toggleextendedinfos command for
details.
extendedTextInfos (0/1)
Toggle sending from server of extended text informations.
What lies in this extended info depended on what extended
infos the client asked. See toggleextendedtext command for details.
facecache (0/1)
Determines if the client is caching images (1) or wants the
images sent to it without caching them (0). Default is 0. This
replaces the setfacemode command.
faceset (8 bit)
Faceset the client wishes to use. If the faceset is not
valid, the server returns the faceset the client will be
using (default 0).
inscribe (0/1):
Client will probably always send 1. Server will reply with 1 if it
supports the 'inscribe' command, 0 else.
map2cmd: (1)
This indicates client support for the map2 protocol command.
See the map2 protocol details above for the main differences.
Note that if map2 protocol is used, the following setup commands
have no effect, either as being redundant or not applicable:
mapsize x X y
Sets the map size to x X y. Note the spaces here are only for
clarity - there should be no spaces when actually sent (it
should be 11x11 or 25x25). The default map size unless changed
is 11x11. The minimum map size the server will allow is 9x9
(no technical reason this could be smaller, but I don't think
the game would be smaller). The maximum map size supported in the
current protocol is 63x63. However, each server can have its
maximum map size sent to most any value.
If the client sends a mapsize command out of valid range, the
server will respond with a mapsize with the maximum size
the server supports. Thus, if the client wants to know the maximum
map size, it can just do a 'mapsize 0x0' and it will get the
maximum size back.
The server will only set the mapsize for the client if both x & y values
are valid. For example, if the maximum map size is 25x25, and the
client sends a 31x23 mapsize request, the mapsize will remain at
11x11 (default) and the server will send back a mapsize 25x25
setup command.
When the values are valid, the server will send back a mapsize
XxY setup command. Note that this is from its parsed values,
so it may not match stringwise with what the client sent, but will
match 0 wise. For example, the client may send a 'mapsize 025X025'
command, in which case the server will respond with a
'mapsize 25x25' command - the data is functionally the same.
While the server in theory supports non square viewing regions,
this has not be tested.
newmapcmd (0/1)
This tells the server if the client understands the newmap
protocol command. This is used by the client in the fog
of war mode to receive newmap commands from the server each time
the player changes maps.
num_look_objects (int value)
The maximum number of objects shown in the ground view. If more
objects are present, fake objects are created for selecting the
previous/next group of items. Defaults to 50 if not set. The server
may adjust the given value to a suitable one; this applies to both
the lower and the upper limit. Returned is the number of items
actually used; this value can differ from the given one if it has
been adjusted.
Note: the requested int value is the total number of objects
including fake objects (prev/next group and transport icons). The
intention for this option is to enable clients to use a fixed
number of slots for displaying ground view objects.
sound (bitmask)
Obsolete, server will return FALSE.
sound2 (bitmask)
New sound support. Check above for more information.
1=send sound effects
2=send music
64=mute sound; same as "sound" ncom command
spellmon (0/1)
If set to 1 the client has indicated that it wishes to be
sent the spell list and updated when it changes.
tick (0/1)
If set, the client want tick commands so it knows animation
timing.
want_pickup (0/1)
If set, the client wants pickup mode to be sent when player
correctly logs in (to synchronize interface).
Note that this flag only sends at login, not when mode changes.
All data in the setup command is in ASCII text form. options and
values can not have whitepace - the client and server use whitspace
to split the options and values.
Currently (2001-05-28), the server only sends a setup in response to
client first having sent a setup command.
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
This section describes the requestinfo and replyinfo commands.
Because these commands may handle different types of data with different
return formats, this section is formatted a bit differently to make
it easier to read the different structures.
C->S: requestinfo <info_type>[options]
S->C: replyinfo <info_type>[options]<data>
The requestinfo command is a general purpose way for the client to request
some piece of data the server may have. The server still needs to be
coded to respond to the specific info_type, but if the passed info_type is
not supported, the server will still respond with the replyinfo, but with
an empty data list.
This mechanism allows the client to send requests for data and not need to
do complicated checking if the server would understand the specific
request - if the server understands it, the data gets sent back. If the
server doesn't understand it, the client gets no data, but does get the
replyinfo so that it knows that the server does not support that
particular aspect.
Only one info_type is allowed for each requestinfo. If the client
requests many pieces of information (say image sets available, spell
listings, etc), it should send multiple requestinfos.
[options] is specific to the info_type - it could be a range of values, or
empty.
Requestinfo requests will not change any data on the server - the setup
command should be used for that. The requestinfo just requests data.
Note that since the requests can be made before a player logs in, the
requestinfo command will not generally support getting information related
to the player object.
The following info_types are supported:
image_info:
'image_info' (no options):
Request basic image information the server has. The data is sent in
text format - the replyinfo is newline terminated. Since the packet
length is sent in the header, that is used to figure out the length of
returned data.
Line 1: The last image number the server has. Note that there is
no image 0, so this also amounts to the number of images
if you start counting from one.
Line 2: checksum of all the image name information. This can
basically be used to determine if the number to name mapping is
the same, eg, if on server 1 the total is 123456, and the player
goes to server 2 and the total is the same, we can say with a high
degree of confidence that the name to number mappings are the name.
If instead the numbers differ, we know we can't rely on using the
same mappings.
Line 3+:The image set information the client has. The format
is the same as the format in the image_info file, sans comments.
The server will ignore any parameters the client sends.
An examply response:
replyinfo image_info
3512
1169234
0:base:standard:0:32x32:none:The standard image set.
1:clsc:classic:0:32x32:none:Classic and new styling.
image_sums <start> <stop>
Request the image number to name (and checksum) values - in this way,
the client can build all images before play starts and also request
any missing images. The returned data is
image_sums <start> <stop> <imagenum><checksum><faceset><namelength><name>
There is an initial space after the stop value, but no spaces after
that point. The <start> and <stop> values are ASCII text (same
format as it is sent to the server in). The start and stop
values are inclusive - thus, if the start is 0 and the stop
is 100, 101 checksums will be set.
imagenum is 16 bit binary data.
checksum is 32 bit binary data. It contains the checksum for
the image in the current selected set, and will use whatever
fallback logic the imagesets specify.
faceset is 8 bit binary data. It contains the actually selected
faceset.
namelength is 8 bit binary data. It is the length of the
name field below, including the null terminator.
name is character data. It is null terminated to make processing
easier - in this way, the client doesn't need to copy the data
to make it null terminated.
Note that due to possible OS system constraints on the maximum single
write supported to a socket, the complete set can not be requested at
once - instead, the images information should be requested in blocks
of less than 1000. The server will not process a block larger than
1000 at a time. Smaller blocks may be desired if the client wants to
try to reduce the potential lag caused.
Multiple requests for all the information can be sent at once, as the
server will buffer the response data, but constraints prevent the
server from sending the entire data back in one replyinfo (one being
that the data would be beyond 65535 bytes, so the length information
in the packet would not be accurate.)
If the client sends invalid data (stop is less than start, missing
stop paremeter, stop is beyond the number of images, or asking for
more than 1000 at a time), the reply will just be an empty list.
Note that the server will track that it has sent the face
information for the requested images, and thus will not send
it again (unless requested via requestinfo). Thus, this
request should always do the right thing with the
returned information.
exp_table (no parameters)
This requests the experience table (what exp is needed for each
level) from the server. With this data, the client can easily
display how much experience is needed for the different skills
or total exp value for next level. Data format:
<num_levels>: uint16 - max level/how many exp values follow.
<level1> ... <level num_levels>: uint64 - amount of exp needed
for the level.
Note that num_levels and the actual exp values are transmitted
as binary values.
skill_info (no parameters)
This returns the skill number to skill name mappings. In this
way, new skills can be added in the server, and the client
can use this new skill information with no changes to the code.
All data below is in text format. Format is:
stat number:skill name
Where stat number is the number that will be used to send
that skill information. Example:
141:lockpicking
142:hiding
143:smithery
spell_paths (no parameters)
This returns a list of all spell paths in the game, along with the
number associated with them. This should be used to parse spell_path
data in the stats command. The number is a bitmask but is sent as a
decimal value.
All data is sent in text format. Format is:
number:name
eg
16:missiles
race_list (no parameters)
Returns the races players can choose.
The names can be used to request more information with race_info.
Reply format is: replyinfo race_list |race1|race2|...|racen
race_info <race name>
Returns information about specified race.
(work in progress)
class_list (no parameters)
Returns the classes players can choose.
The names can be used to request more information with class_info.
Reply format is: replyinfo class_list |class1|class2|...|classn
class_info <class name>
Returns information about specified class.
(work in progress)
------------------------------------------------------------------------------
Example Session:
The client first opens a connection to the server.
S->C: version 1001
C->S: version 1001
The client/server are exchanging version information, to verify that
they can both properly communicate with each other. If there is
a mismatch, one or both of the sides might close the connection.
C->S: setfacemode 2
The client is informing the server that is wants to use XPM images. Note
that setfacemode is an optional command - if the client wants to live with
the default (XPM) mode, it doesn't need to send this.
C->S: addme
S->C: addme_success
Client is informing the server it wants to be added to the game. Server
is telling client that the command has succeeded, and it will then
be added.
NOTE: I am not sure if this is the exact order of the next few commands,
since a whole bunch of stuff is being done at once.
S->C: pixmap (All that the map command uses will be sent.)
S->C: map (display starting town map.)
S->C: stats (display default character stats)
S->C: drawinfo (display motd)
S->C: query (get player name)
C->S: reply (return player name)
S->C: drawinfo (inform player to enter password)
S->C: query (request password)
C->S: reply (return player password.)
At this point, things could deviate two ways - player could be
starting a new character, in which case, numerous draw infos, query's
(stat rolling), replys, stats (change stats that were just
rolled), map updates (player changing class) could be sent. However,
we will assume that the player actually entered the proper password
and an existing character is rejoining the game.
Once again, I am not positive this is the correct order or not.
S->C: player (send player object.)
S->C: stats (send player stats)
S->C: pixmap (assuming on different map and we haven't sent some of
the images before)
S->C: map (map player was saved on)
S->C: pixmap (assuming we have not sent image for item before)
S->C: item (item in players inventory or where he is standing)
After that is established, a loop is established that typically will result
in these commands being sent at various times:
S->C: stats - to inform the client when stats go up or down.
S->C: map_scroll (when the player moves)
S->C: map (update after map_scroll, or when player changes maps.)
S->C: pixmap/bitmap (with maps commands) to update faces.
S->C: drawinfo (Tell about hitting creatures, applying, etc.)
S->C: item (tell what objects are in players inventory, or space he is standing
on.
C->S: command (general commands, like north, fire, cast, etc.)
C->S: apply (applying and object.)
C->S: move (moving and object)
C->S: examine (examine an object.)
S->C: query (keypress for shop listing, some other areas)
C->S: reply (from last query)
------------------------------------------------------------------------------
Programming Notes:
These are a few quick notes on how things work. Note that they really
only apply to the code in the standard distribution, most of the direct
i/o is handled by functions that are talked about. If writing a client
from scratch, you will need to port this over (or write your own - it
isn't very complicated.)
For the server and the C client, a SockList structure is used for basic
data handling. Basically, this is just a structure that has an unsigned
character buffer and a length field (which contains the length of data in
the buffer, not the actual buffer length.)
As a side note, when sending a packet, you can supply the length of the
data and the sending routines will take care of sending the 2 bytes of
length information.
When getting a packet, these 2 bytes are at the start of the buffer and
not removed.
There is a file called newsocket.c - this file is shared between the`
client and server distribution, but except for the SockList data type,
it could probably be used by itself. The newsocket.c file contains
some routines to pack ints, shorts, and single chars into SockList
structs, as well as functions for the reverse. It also contains a
function to send socklists, as well as read them. The Add??? functions
increase the len field of the socklist, the Get??? functions do not
change the pointer in anyways. Thus, to get an int and move the buffer,
you do something like:
int = GetIntString(data); data+=4
As a side note, if you malloc the data for the buffer, make sure to free
it when done.
There is also the newclient.h file which is shared between the client and
server. This file contains the definition of the SockList, as well as
many defined values for constants of varying means (ie, that in the
stats command, a stat value of 1 is hit points, etc.) When porting to
a new system, you will need to grab these constant values for yourself.
A few other notes:
The item command lists the weight for an individual item of that type. It
thus becomes the responsibility of the client to parse the name to see how
many there are, and then multiply the weight by that nrof for an accurate
value. This should probably be changed, with the item command including
an nrof, with that being removed from the string we send. Also, the client
is responsible for computing the weight of containers, and thus the player
itself.
------------------------------------------------------------------------------
Image caching:
Image caching has been implemented on the client, with necessary server
support to handle it. This section will briefly describe how image
caching works on the protocol level, as well as how the current client does
it.
First, the client checks for an option denoting the image caching
is desired. If so, we initialize all the images to a default value - this
means we don't need to put special checks into the drawing code to see if
we have an image - we just draw the default images (I use a question mark
pixmap, since that makes it very easy to see what stuff is cached.) We
also initialize an array which will hold the number to name mapping so
that when we actually get the image, we know what filename to store it
as.
Second, we request the server to do image caching. This is done
by oring the cache directive to the image mode we want.
C->S: setfacemode 18
Then, when the server finds an image number that it has not send to the
client, it sends us a name command information us the number to name mapping:
S->C: face 65 CSword.115
Note that this is not exactly how the send - the number is actually send
in binary form, and there is no space between that the and the name. Such
formating is difficult here, but the above example illustrates the
data is sent.
The client then checks for the existence of the image locally. Note that it
is up to the client to apply any extensions based on display type (ie,
add .xpm or .gif or whatever.) The current client stores images in
~/.crossfire/images, and then splits them into sub directories based on
the first 2 letters - in the above example, the file would be
~/.crossfire/images/CS/CSword.115
If the client does not have the image or otherwise needs a copy from the
server, it then requests it:
C->S: askface 65
The server will then send the image via the normal bitmap/pixmap
routines.
S->C: pixmap <data>
Because the pixmap/bitmap routines do include the image name,
we must store the name & number mapping someplace before sending the
askface. I just used an array of character pointers, so then in
position 65, we do a strdup of the name, store it, then use it when
the pixmap/bitmap command comes in, and free that data.
Also, the client does occasional redraws of all data if it has received
new images and is running in cached mode. Otherwise, the map can remain
out of date indefinitely (although, once the player moves, things will get
redrawn.)
This has the effect that first time running in cached mode, performance
will actually be a little bit worse for the client (after all, it needs
to still request all the images, but is still doing pretty constant redraws
of the data.) But times after that, performance is greatly improved.
------------------------------------------------------------------------------
Changes:
This area documents changes in the protocol version and what happened between
them. Note that this is not a complete list, as the setup command
is used to control many cases.:
CS version 1022 -> 1023: just to sync version with server
CS version 1021 -> 1022: Client supports sending of verbal image type
questions.
SC version 1022 -> 1023: Server supports sending png images (addition of
image command).
SC version 1023 -> 1024: Server will send two part names (described in
item command) which contains the singular & plural form of the name)
SC version 1024 -> 1025: Support for sending all resistance
values in the stats command.
SC version 1025 -> 1026: Add face1 command that includes the image
checksum.
SC version 1026 -> 1027: Add requestinfo/replyinfo commands - client
can check this to know if it should expect a replyinfo to its requestinfo.
SC version 1027 -> 1028: mostly remove obsolete commands:
image, face, face1, map, map1, map1a, command
------------------------------------------------------------------------------
Todo:
It probably makes more sense to do a more preemptive handling of socket
events. That is, instead of sleeping 120 ms, then checking all the
sockets, we do a select on all the file descriptors with an appropriate
sleep time.
If we get input, we handle it at that time if the player has an action. In
this way, instead of handling all the actions after sleeping for the 120ms,
we can instead spread them out more. Only when the 120ms expire do we
then do all the actions like move the monsters, regenerate HP, etc.
The only potential problem I see with this right now is that select will
return immediately if there is data on the socket (player has used up all
their time, are paralyzed, etc.) This would probably mean that the server
needs to do internal buffering, which complicates things some. The
potential advantage with this is that we could peek at the data, and
if the command is not a player action (ie, maybe requesting an image, or
a misc command like who), we could still execute it. Actually, we can
get around the select problem by only adding the file descriptors from
sockets that actually have time to perform actions.
It probably also makes sense to look at the map at the end of each tick
and determine what needs to be sent (same for the look window.) If a player
is moving really fast (speed > 1), they could in theory move 2 spaces in
1 tick - what is the point then of sending a map and the items for the space
the skip over quickly?
However, what might also make more sense (but becomes a bit more complicated)
is adjust the players speed by these smaller amounts. Thus, if the player
has speed of 2.0, every (half a tick) we add 1 point or something. What
might be smarter is if we do set up the sleep system above, then anytime
we get an event that we see how much time has passed and increase all the
players speed by that amount. Thus, if a player is running, they will move
whenever they have proper speed - this may make things feel a bit snappier.