 |
Crossfire Server, Trunk
1.75.0
|
Go to the documentation of this file.
35 #include <arpa/inet.h>
36 #include <sys/types.h>
38 #include <netinet/in.h>
137 char *params = NULL, *cp;
141 if (len <= 0 || !
buf) {
142 LOG(
llevDebug,
"IP '%s' sent bogus request_info_cmd information\n", ns->
host);
156 for (cp =
buf; *cp !=
'\0'; cp++)
162 if (!strcmp(
buf,
"image_info"))
164 else if (!strcmp(
buf,
"image_sums"))
166 else if (!strcmp(
buf,
"skill_info"))
168 else if (!strcmp(
buf,
"skill_extra"))
170 else if (!strcmp(
buf,
"spell_paths"))
172 else if (!strcmp(
buf,
"exp_table"))
174 else if (!strcmp(
buf,
"race_list"))
176 else if (!strcmp(
buf,
"race_info"))
178 else if (!strcmp(
buf,
"class_list"))
180 else if (!strcmp(
buf,
"class_info"))
182 else if (!strcmp(
buf,
"rules"))
184 else if (!strcmp(
buf,
"motd"))
186 else if (!strcmp(
buf,
"news"))
188 else if (!strcmp(
buf,
"newcharinfo"))
190 else if (!strcmp(
buf,
"startingmap"))
192 else if (!strcmp(
buf,
"knowledge_info"))
268 int command_count = 0;
269 while (command_count < 5 || (pl && command_count < 25)) {
289 char *cmd = strtok_r((
char *)ns->
inbuf.
buf + 2,
" ", &
data);
296 got_player_cmd =
handle_cmd(ns, pl, cmd, NULL, 0);
300 if (got_player_cmd) {
335 static struct sockaddr_in insock;
338 struct protoent *protoent;
340 if ((protoent = getprotobyname(
"udp")) == NULL
341 || (fd = socket(PF_INET, SOCK_DGRAM, protoent->p_proto)) == -1) {
344 insock.sin_family = AF_INET;
345 insock.sin_port = htons((
unsigned short)13325);
346 insock.sin_addr.s_addr = inet_addr(
"127.0.0.1");
350 sendto(fd,
buf, strlen(
buf), 0, (
struct sockaddr *)&insock,
sizeof(insock));
364 return fcntl(fd, F_GETFL) != -1 || errno != EBADF;
375 int newsocknum = -1, j;
376 #ifdef HAVE_GETNAMEINFO
377 struct sockaddr_storage addr;
379 struct sockaddr_in addr;
393 if (newsocknum == -1) {
409 if (newsocknum < 0) {
414 init_sockets[newsocknum].
fd = accept(listen_fd, (
struct sockaddr *)&addr, &addrlen);
419 #ifndef HAVE_GETNAMEINFO
426 #ifdef HAVE_GETNAMEINFO
427 getnameinfo((
struct sockaddr *) &addr, addrlen,
buf,
sizeof(
buf), NULL, 0, NI_NUMERICHOST);
429 ip = ntohl(addr.sin_addr.s_addr);
430 snprintf(
buf,
sizeof(
buf),
"%ld.%ld.%ld.%ld", (ip>>24)&255, (ip>>16)&255, (ip>>8)&255, ip&255);
473 LOG(
llevError,
"esrv_update_item(UPD_WEIGHT) did not set player weight: is %lu, should be %lu\n", (
unsigned long)pl->
last_weight, (
unsigned long)
WEIGHT(pl->
ob));
525 fd_set tmp_read, tmp_exceptions;
527 FD_ZERO(&tmp_exceptions);
557 FD_SET((uint32_t)pl->
socket->
fd, &tmp_read);
558 FD_SET((uint32_t)pl->
socket->
fd, &tmp_exceptions);
564 if (sleep_time < 0) {
565 LOG(
llevInfo,
"skipping time (over by %ld ms)\n", -sleep_time/1000);
584 while (sleep_time > 0) {
590 if (errno == EINTR) {
593 }
else if (errno == EBADF) {
598 }
else if (!pollret) {
627 if (FD_ISSET(pl->
socket->
fd, &tmp_exceptions)) {
633 if (!keep_processing) {
void request_info_cmd(char *buf, int len, socket_struct *ns)
request_info_cmd is sort of a meta command.
uint32_t tick
Client wishes to get tick commands.
void send_image_info(socket_struct *ns)
Sends the number of images, checksum of the face file, and the image_info file information.
player * next
Pointer to next player, NULL if this is last.
const char * cmdname
Command name.
void send_image_sums(socket_struct *ns, char *params)
Sends requested face information.
player * first_player
First player.
void reply_cmd(char *buf, int len, player *pl)
This is a reply to a previous query.
bool heartbeat
Client will send hearbeats.
unsigned long total_ticktime
@ llevError
Error, serious thing.
void init_connection(socket_struct *ns, const char *from_ip)
Initializes a connection.
void LOG(LogLevel logLevel, const char *format,...)
Logs a message to stderr, or to file.
socket_struct * init_sockets
Established connections for clients not yet playing.
static const struct client_cmd_mapping client_commands[]
Commands sent directly by client, when connecting or when needed.
Socket structure, represents a client-server connection.
int allocated_sockets
Number of allocated items in init_sockets.
void esrv_send_inventory(object *pl, object *op)
Sends inventory of a container.
void SockList_AddString(SockList *sl, const char *data)
Adds a string without length.
object * ob
The object representing the player.
float speed_left
How much speed is left to spend this round.
Socket_Info socket_info
Socket information.
uint32_t update_inventory
If true, we need to send the inventory list.
void mark_item_cmd(uint8_t *data, int len, player *pl)
Client wants to mark some object.
static void send_updates(player *pl)
Send updated stats, map, look, and inventory to the player.
int max_filedescriptor
max filedescriptor on the system.
void send_new_char_info(socket_struct *ns)
Sends information related to creating a new character to the client.
void send_map_info(socket_struct *ns)
Send information on available start maps.
void send_exp_table(socket_struct *ns)
This sends the experience table the sever is using.
struct listen_info * listen
static int is_fd_valid(int fd)
Checks if file descriptor is valid.
SockList inbuf
If we get an incomplete packet, this is used to hold the data.
uint32_t pticks
Number of ticks since time reset.
uint32_t update_look
If true, we need to send the look window.
void account_password(char *buf, int len, socket_struct *ns)
Handles the account password change.
void send_skill_info(socket_struct *ns, char *params)
This sends the skill number to name mapping.
void account_login_cmd(char *buf, int len, socket_struct *ns)
Handles the account login.
void look_at_cmd(char *buf, int len, player *pl)
Client wants to look at some object.
void knowledge_send_info(socket_struct *ns)
Send the reply_info for 'knowledge_info'.
void final_free_player(player *pl)
Sends the 'goodbye' command to the player, and closes connection.
const uint8_t flag
If set, the player must be in the ST_PLAYING state for this command to be available.
void update_players()
Send updates to players.
void(* func_uint8_int_ns)(char *, int, socket_struct *)
Prototype for functions the client sends without player interaction.
void esrv_update_stats(player *pl)
Sends a statistics update.
void check_all_fds()
Check all file descriptors and mark sockets with invalid ones as dead.
char * host
Which host it is connected from (ip address).
Definition of a function the client sends without player interaction.
void send_tick(player *pl)
void leave(player *pl, int draw_exit)
Player logs out, or was disconnected.
void ask_smooth_cmd(char *buf, int len, socket_struct *ns)
Tells client the picture it has to use to smooth a picture number given as argument.
void account_add_player_cmd(char *buf, int len, socket_struct *ns)
Handle accountaddplayer from server (add a character to this account).
void create_player_cmd(char *buf, int len, socket_struct *ns)
We have received a createplayer command.
void new_player_cmd(uint8_t *buf, int len, player *pl)
This handles the commands issued by the player (ie, north, fire, cast, etc.).
long get_sleep_remaining()
void account_play_cmd(char *buf, int len, socket_struct *ns)
We have received an accountplay command.
void SockList_Init(SockList *sl)
Initializes the SockList instance.
void do_server(void)
This checks the sockets for input and exceptions, does the right thing.
const char * cmdname
Command name.
void move_cmd(char *buf, int len, player *pl)
Moves an object (typically, container to inventory).
void fatal(enum fatal_error err)
fatal() is meant to be called whenever a fatal signal is intercepted.
void send_class_info(socket_struct *ns, char *params)
Send information on the specified class.
#define MAX_BUF
Used for all kinds of things.
int32_t last_weight
Last weight as sent to client; -1 means do not send weight.
int checkbanned(const char *login, const char *host)
Check if a player and/or host is banned.
void version_cmd(char *buf, int len, socket_struct *ns)
Client tells its version.
unsigned int tick_length(float seconds)
Calculate the number of ticks that correspond to real time.
static void new_connection(int listen_fd)
Handle a new connection from a client.
void SockList_Term(SockList *sl)
Frees all resources allocated by a SockList instance.
void send_race_list(socket_struct *ns)
Send the list of player races to the client.
bool handle_client(socket_struct *ns, player *pl)
Handle commands from a client.
#define ST_PLAYING
Usual state.
void free_newsocket(socket_struct *ns)
Frees a socket.
void inscribe_scroll_cmd(char *buf, int len, player *pl)
unsigned long max_ticktime
void send_class_list(socket_struct *ns)
Sends the list of classes to the client.
#define FREE_AND_CLEAR(xyz)
Free the pointer and then set it to NULL.
uint32_t last_tick
Number of ticks since last communication.
struct timeval timeout
Timeout for select.
#define BEAT_INTERVAL
Interval between heartbeat requests.
uint8_t state
Input state of the player (name, password, etc).
void esrv_update_item(int flags, object *pl, object *op)
Updates object *op for player *pl.
void esrv_draw_look(object *pl)
Send the look window.
void send_file(socket_struct *ns, const char *file)
Sends the desired file to the client.
void examine_cmd(char *buf, int len, player *pl)
Client wants to examine some object.
size_t faces_sent_len
This is the number of elements allocated in faces_sent[].
#define WEIGHT(op)
Returns the weight of the given object.
static int handle_cmd(socket_struct *ns, player *pl, char *cmd, char *data, int len)
Handle a command, either directly sent by the client, or sent for a player's action.
void SockList_ResetRead(SockList *sl)
Resets the length of the stored data for reading.
void send_skill_extra(socket_struct *ns, char *params)
Send extra skill information.
void send_spell_paths(socket_struct *ns)
This sends the spell path to name mapping.
void account_new_cmd(char *buf, int len, socket_struct *ns)
Handles the account creation This function shares a fair amount of the same logic as account_login_cm...
uint8_t * faces_sent
This is a bitmap on sent face status.
====Textual A command containing textual data has data fields separated by one ASCII space character. word::A sequence of ASCII characters that does not contain the space or nul character. This is to distinguish it from the _string_, which may contain space characters. Not to be confused with a machine word. int::A _word_ containing the textual representation of an integer. Not to be confused with any of the binary integers in the following section. Otherwise known as the "string value of integer data". Must be parsed, e.g. using `atoi()` to get the actual integer value. string::A sequence of ASCII characters. This must only appear at the end of a command, since spaces are used to separate fields of a textual message.=====Binary All multi-byte integers are transmitted in network byte order(MSB first). int8::1-byte(8-bit) integer int16::2-byte(16-bit) integer int32::4-byte(32-bit) integer lstring::A length-prefixed string, which consists of an `int8` followed by that many bytes of the actual string. This is used to transmit a string(that may contain spaces) in the middle of binary data. l2string::Like _lstring_, but is prefixed with an `int16` to support longer strings Implementation Notes ~~~~~~~~~~~~~~~~~~~~ - Typical implementations read two bytes to determine the length of the subsequent read for the actual message, then read and parse the data from each message according to the commands described below. To send a message, the sender builds the message in a buffer, counts the length of the message, sends the length, and finally sends the actual message. TIP:Incorrectly transmitting or receiving the `length` field can lead to apparent "no response" issues as the client or server blocks to read the entire length of the message. - Since the protocol is highly interactive, it may be useful to set `TCP_NODELAY` on both the client and server. - If you are using a language with a buffered output stream, remember to flush the stream after a complete message. - If the connection is lost(which will also happen if the output buffer overflowing), the player is saved and the server cleans up. This does open up some abuses, but there is no perfect solution here. - 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. Commands -------- In the documentation below, `S->C` represents a message to the client from the server, and `C->S` represents a message to the server from the client. Commands are documented in a brief format like:C->S:version< csval >[scval[vinfo]] Fields are enclosed like `< this >`. Optional fields are denoted like `[this]`. Spaces that appear in the command are literal, i.e. the<< _version > > command above uses spaces to separate its fields, but the command below does not:C->S:accountlogin< name >< password > As described in<< _messages > >, if a command contains data, then the command is separated from the data by a literal space. 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. The protocol commands are broken into various sections which based somewhat on what the commands are for(ie, item related commands, map commands, image commands, etc.) In this way, all the commands related to similar functionality is in the same place. Initialization ~~~~~~~~~~~~~~ version ^^^^^^^ C->S:version< csval >[scval[vinfo]] S->C:version< csval >[scval[vinfo]] Used by the client and server to exchange which version of the Crossfire protocol they understand. Neither send this in response to the other - they should both send this shortly after a connection is established. csval::int, version level of C->S communications scval::int, version level of S->C communications vinfo::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.=====Version ID 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. 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.=====Handling 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 mismatch(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. 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.=====History _scval_ and _vinfo_ were added in version 1020. Before then, there was only one version sent in the version command. NOTE: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. setup ^^^^^ C->S, S->C:setup< option1 >< value1 >< option2 >< value2 > ... Sent by the client to request protocol option changes. This can be at any point during the life of a connection, but usually sent at least once right after the<< _version > > command. The server responds with a message in the same format confirming what configuration options were set. The server only sends a setup command in response to one from the client. The sc_version should be updated in the server if commands have been obsoleted such that old clients may not be able to play. option::word, name of configuration option value::word, value of configuration option. May need further parsing according to the setup options below=====Setup Options There are really 2 set of setup commands here:. Those that control preferences of the client(how big is the map, what faceset to use, etc). . Those that describe capabilities of the client(client supports this protocol command or that) .Setup Options[options="autowidth,header"]|===========================|Command|Description|beat|Ask the server to enable heartbeat support. When heartbeat is enabled, the client must send the server a command every three seconds. If no commands need to be sent, use the `beat` no-op command. Clients that do not contact the server within the interval are assumed to have a temporary connection failure.|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.|extended_stats(0/1 value)|If set to 1, the server will send the CS_STAT_RACE_xxx and CS_STAT_BASE_xxx values too, so the client can display various status related to statistics. Default is 0.|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).|loginmethod(8 bit)|Client sends this to server to note login support. This is basically used as a subset of the csversion/scversion to find out what level of login support the server and client support. Current defined values:0:no advanced support - only legacy login method 1:account based login(described more below) 2:new character creation support This list may grow - for example, advanced character creation could become a feature.|map2cmd:(1)|This indicates client support for the map2 protocol command. See the map2 protocol details above for the main differences. Obsolete:This is the only supported mode now, but many clients use it as a sanity check for protocol versions, so the server still replies. It doesn 't do anything with the data|mapsize(int x) X(int 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 an invalid mapsize command or a mapsize of 0x0, the server will respond with a mapsize that is the maximum size the server supports. Thus, if the client wants to know the maximum map size, it can just do a 'mapsize 0x0' or 'mapsize' and it will get the maximum size back. The server will constrain the provided mapsize x &y to the configured minumum and maximums. For example, if the maximum map size is 25x25, the minimum map size is 9x9, and the client sends a 31x7 mapsize request, the mapsize will be set to 25x9 and the server will send back a mapsize 25x9 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. The server will send an updated map view when this command is sent.|notifications(int value)|Value indicating what notifications the client accepts. It is incremental, a value means "all notifications till this level". The following levels are supported:1:quest-related notifications("addquest" and "updquest") 2:knowledge-related notifications("addknowledge") 3:character status flags(overloaded, blind,...)|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 data
void lock_item_cmd(uint8_t *data, int len, player *pl)
Client wants to apply some object.
int save_player(object *op, int flag)
Saves a player to disk.
object * container
Current container being used.
Definition of a function called in reaction to player's action.
socket_struct * socket
Socket information for this player.
static const struct player_cmd_mapping player_commands[]
Dispatch tables for the server.
unsigned long ticks_overtime
int SockList_ReadPacket(int fd, SockList *sl, int len)
This reads from fd and puts the data in sl.
void set_up_cmd(char *buf, int len, socket_struct *ns)
This is the Setup cmd - easy first implementation.
unsigned char buf[MAXSOCKBUF]
void send_race_info(socket_struct *ns, char *params)
Sends information on specified race to the client.
const func_uint8_int_ns cmdproc
Function to call.
const func_uint8_int_pl cmdproc
Function to call.
void add_me_cmd(char *buf, int len, socket_struct *ns)
The client has requested to be added to the game.
void draw_client_map(object *pl)
Draws client map.
void Send_With_Handling(socket_struct *ns, SockList *sl)
Calls Write_To_Socket to send data to the client.
Contains the base information we use to make up a packet we want to send.
void apply_cmd(char *buf, int len, player *pl)
Client wants to apply some object.
void(* func_uint8_int_pl)(char *, int, player *)
Prototype for functions used to handle player actions.
void SockList_NullTerminate(SockList *sl)
Adds a NUL byte without changing the length.
void send_face_cmd(char *buff, int len, socket_struct *ns)
Client has requested pixmap that it somehow missed getting.
@ llevDebug
Only for debugging purposes.
void init_listening_socket(socket_struct *ns)
This opens *ns for listening to connections.
bool connection_alive(const socket_struct *socket)
Check whether the given socket's connection is alive or not.