Crossfire Server, Trunk
lowlevel.cpp
Go to the documentation of this file.
1 /*
2  * Crossfire -- cooperative multi-player graphical RPG and adventure game
3  *
4  * Copyright (c) 1999-2014 Mark Wedel and the Crossfire Development Team
5  * Copyright (c) 1992 Frank Tore Johansen
6  *
7  * Crossfire is free software and comes with ABSOLUTELY NO WARRANTY. You are
8  * welcome to redistribute it under certain conditions. For details, please
9  * see COPYING and LICENSE.
10  *
11  * The authors can be reached via e-mail at <crossfire@metalforge.org>.
12  */
13 
26 #include "global.h"
27 
28 #include <assert.h>
29 #include <errno.h>
30 #include <stdarg.h>
31 #include <stdlib.h>
32 #include <string.h>
33 
34 #include "shared/newclient.h"
35 #include "sproto.h"
36 
37 #ifdef WIN32
38 #include <winsock2.h>
39 #endif
40 
41 /***********************************************************************
42  *
43  * SockList functions/utilities
44  *
45  **********************************************************************/
46 
53  SockList_Reset(sl);
54 }
55 
63  (void)sl;
64 }
65 
72  sl->len = 2;
73 }
74 
81  sl->len = 0;
82 }
83 
92 static void SockList_Ensure(const SockList *sl, size_t size) {
93  if (sl->len+size > sizeof(sl->buf)) {
95  }
96 }
97 
103 void SockList_AddChar(SockList *sl, unsigned char data) {
104  SockList_Ensure(sl, 1);
105  sl->buf[sl->len++] = data;
106 }
107 
113 void SockList_AddShort(SockList *sl, uint16_t data) {
114  SockList_Ensure(sl, 2);
115  sl->buf[sl->len++] = (data>>8)&0xff;
116  sl->buf[sl->len++] = data&0xff;
117 }
118 
124 void SockList_AddInt(SockList *sl, uint32_t data) {
125  SockList_Ensure(sl, 4);
126  sl->buf[sl->len++] = (data>>24)&0xff;
127  sl->buf[sl->len++] = (data>>16)&0xff;
128  sl->buf[sl->len++] = (data>>8)&0xff;
129  sl->buf[sl->len++] = data&0xff;
130 }
131 
137 void SockList_AddInt64(SockList *sl, uint64_t data) {
138  SockList_Ensure(sl, 8);
139  sl->buf[sl->len++] = (char)((data>>56)&0xff);
140  sl->buf[sl->len++] = (char)((data>>48)&0xff);
141  sl->buf[sl->len++] = (char)((data>>40)&0xff);
142  sl->buf[sl->len++] = (char)((data>>32)&0xff);
143  sl->buf[sl->len++] = (char)((data>>24)&0xff);
144  sl->buf[sl->len++] = (char)((data>>16)&0xff);
145  sl->buf[sl->len++] = (char)((data>>8)&0xff);
146  sl->buf[sl->len++] = (char)(data&0xff);
147 }
148 
154 void SockList_AddString(SockList *sl, const char *data) {
155  SockList_AddData(sl, data, strlen(data));
156 }
157 
164 void SockList_AddData(SockList *sl, const void *data, size_t len) {
165  SockList_Ensure(sl, len);
166  memcpy(sl->buf+sl->len, data, len);
167  sl->len += len;
168 }
169 
176 void SockList_AddLen8Data(SockList *sl, const void *data, size_t len) {
177  assert(len <= 255);
178  SockList_AddChar(sl, len);
179  SockList_AddData(sl, data, len);
180 }
181 
188 void SockList_AddLen16Data(SockList *sl, const void *data, size_t len) {
189  assert(len <= 65535);
190  SockList_AddShort(sl, len);
191  SockList_AddData(sl, data, len);
192 }
193 
199 void SockList_AddPrintf(SockList *sl, const char *format, ...) {
200  size_t size;
201  int n;
202  va_list arg;
203 
204  size = sizeof(sl->buf)-sl->len;
205 
206  va_start(arg, format);
207  n = vsnprintf((char *)sl->buf+sl->len, size, format, arg);
208  va_end(arg);
209 
210  if (n <= -1 || (size_t)n >= size) {
212  }
213  sl->len += (size_t)n;
214 }
215 
223  char *p;
224 
225  p = stringbuffer_finish(sb);
226  SockList_AddString(sl, p);
227  free(p);
228 }
229 
235  SockList_Ensure(sl, 1);
236  sl->buf[sl->len] = '\0';
237 }
238 
243 size_t SockList_Avail(const SockList *sl) {
244  return sizeof(sl->buf)-sl->len;
245 }
246 
251 int GetInt_String(const unsigned char *data) {
252  return ((data[0]<<24)+(data[1]<<16)+(data[2]<<8)+data[3]);
253 }
254 
255 short GetShort_String(const unsigned char *data) {
256  return ((data[0]<<8)+data[1]);
257 }
258 
259 /******************************************************************************
260  *
261  * Start of read routines.
262  *
263  ******************************************************************************/
264 
272 int SockList_ReadPacket(int fd, SockList *sl, int len) {
273  int stat, toread;
274 
275  /* We already have a partial packet */
276  if (sl->len < 2) {
277 #ifdef WIN32 /* ***WIN32 SockList_ReadPacket: change read() to recv() */
278 
279  stat = recv(fd, reinterpret_cast<char *>(sl->buf+sl->len), 2-sl->len, 0);
280 
281 #else
282  do {
283  stat = read(fd, sl->buf+sl->len, 2-sl->len);
284  } while ((stat == -1) && (errno == EINTR));
285 #endif
286  if (stat < 0) {
287  /* In non blocking mode, EAGAIN is set when there is no
288  * data available.
289  */
290 #ifdef WIN32 /* ***WIN32 SockList_ReadPacket: error handling for win32 */
291  if ((stat == -1) && WSAGetLastError() != WSAEWOULDBLOCK) {
292  if (WSAGetLastError() == WSAECONNRESET)
293  LOG(llevDebug, "Connection closed by client\n");
294  else {
295  LOG(llevDebug, "ReadPacket got error %d, returning -1\n", WSAGetLastError());
296  }
297  return -1; /* kick this user! */
298  }
299 #else
300  if (errno == ECONNRESET) {
301  LOG(llevDebug, "ReadPacket got error %s, returning -1\n", strerror(errno));
302  return -1;
303  }
304  if (errno != EAGAIN && errno != EWOULDBLOCK) {
305  LOG(llevDebug, "ReadPacket got error %s, returning 0\n", strerror(errno));
306  }
307 #endif
308  return 0; /*Error */
309  }
310  if (stat == 0)
311  return -1;
312  sl->len += stat;
313 #ifdef CS_LOGSTATS
314  cst_tot.ibytes += stat;
315  cst_lst.ibytes += stat;
316 #endif
317  if (stat < 2)
318  return 0; /* Still don't have a full packet */
319  }
320  /* Figure out how much more data we need to read. Add 2 from the
321  * end of this - size header information is not included.
322  */
323  toread = 2+(sl->buf[0]<<8)+sl->buf[1]-sl->len;
324  if ((toread+(int)sl->len) >= len) {
325  LOG(llevError, "SockList_ReadPacket: Want to read more bytes than will fit in buffer (%lu>=%lu).\n", (unsigned long)toread+sl->len, (unsigned long)len);
326  /* Quick hack in case for 'oldsocketmode' input. If we are
327  * closing the socket anyways, then reading this extra 100 bytes
328  * shouldn't hurt.
329  */
330 #ifdef WIN32 /* ***win32 SockList_ReadPacket: change read() to recv() */
331  stat = recv(fd, reinterpret_cast<char *>(sl->buf+2), 100, 0);
332 #else
333  stat = read(fd, sl->buf+2, 100);
334 #endif /* end win32 */
335  (void) stat; // Don't care how much we read; avoid complier warnings
336 
337  /* return error so the socket is closed */
338  return -1;
339  }
340  do {
341 #ifdef WIN32 /* ***win32 SockList_ReadPacket: change read() to recv() */
342  stat = recv(fd, reinterpret_cast<char *>(sl->buf+sl->len), toread, 0);
343 #else
344  do {
345  stat = read(fd, sl->buf+sl->len, toread);
346  } while ((stat < 0) && (errno == EINTR));
347 #endif
348  if (stat < 0) {
349 #ifdef WIN32 /* ***win32 SockList_ReadPacket: change error handling for win32 */
350  if ((stat == -1) && WSAGetLastError() != WSAEWOULDBLOCK) {
351  if (WSAGetLastError() == WSAECONNRESET)
352  LOG(llevDebug, "Connection closed by client\n");
353  else {
354  LOG(llevDebug, "ReadPacket got error %d, returning -1\n", WSAGetLastError());
355  }
356  return -1; /* kick this user! */
357  }
358 #else
359  if (errno != EAGAIN && errno != EWOULDBLOCK) {
360  LOG(llevDebug, "ReadPacket got error %s, returning 0\n", strerror(errno));
361  }
362 #endif
363  return 0; /*Error */
364  }
365  if (stat == 0)
366  return -1;
367  sl->len += stat;
368 #ifdef CS_LOGSTATS
369  cst_tot.ibytes += stat;
370  cst_lst.ibytes += stat;
371 #endif
372  toread -= stat;
373  if (toread == 0)
374  return 1;
375  if (toread < 0) {
376  LOG(llevError, "SockList_ReadPacket: Read more bytes than desired.\n");
377  return 1;
378  }
379  } while (toread > 0);
380  return 0;
381 }
382 
383 /*******************************************************************************
384  *
385  * Start of write related routines.
386  *
387  ******************************************************************************/
388 
396 static void Write_To_Socket(socket_struct* ns, const unsigned char* buf, const int len) {
397  if (ns->status == Ns_Dead || !buf) {
398  LOG(llevDebug, "Write_To_Socket called with dead socket\n");
399  return;
400  }
401 
402 #ifdef WIN32
403  const int amt = send(ns->fd, reinterpret_cast<const char *>(buf), len, 0);
404 #else
405  const int amt = send(ns->fd, buf, len, 0);
406 #endif
407  if (amt < 0) { /* We got an error */
408 #ifdef WIN32 /* ***win32 Write_To_Socket: change error handling */
409  if (amt == -1 && WSAGetLastError() != WSAEWOULDBLOCK) {
410  LOG(llevInfo, "New socket write failed WTS (%d).\n",
411  WSAGetLastError());
412 #else
413  if (errno != EWOULDBLOCK) {
414  LOG(llevInfo, "New socket write failed WTS: %s\n",
415  strerror(errno));
416 #endif
417  ns->status = Ns_Dead;
418  return;
419  } else { /* EWOULDBLOCK */
420  LOG(llevError,
421  "Write_To_Socket: write would block; disconnecting. Try "
422  "increasing SOCKETBUFSIZE.\n");
423  ns->status = Ns_Dead;
424  return;
425  }
426  } else if (amt != len) {
427  LOG(llevError, "Write_To_Socket: write wrote less than requested; "
428  "disconnecting. Try increasing SOCKETBUFSIZE.\n");
429  ns->status = Ns_Dead;
430  return;
431  }
432 #ifdef CS_LOGSTATS
433  cst_tot.obytes += amt;
434  cst_lst.obytes += amt;
435 #endif
436 }
437 
445  if (ns->status == Ns_Dead || sl == NULL)
446  return;
447 
448  sl->buf[0] = ((sl->len-2)>>8)&0xFF;
449  sl->buf[1] = (sl->len-2)&0xFF;
450  Write_To_Socket(ns, sl->buf, sl->len);
451 }
452 
453 /******************************************************************************
454  *
455  * statistics logging functions.
456  *
457  ******************************************************************************/
458 
459 #ifdef CS_LOGSTATS
460 
461 static int count_all_players() {
462  int players = 0;
463  player *pl;
464  for (pl = first_player, players = 0; pl != NULL; pl = pl->next, players++);
465  return players;
466 }
467 
468 /* cst_tot is for the life of the server, cst_last is for the last series of
469  * stats
470  */
472 
476 void write_cs_stats(void) {
477  time_t now = time(NULL);
478 
479  /* If no connections recently, don't bother to log anything */
480  if (cst_lst.ibytes == 0 && cst_lst.obytes == 0)
481  return;
482 
483  LOG(llevInfo, "STAT: players: %d active, %d total\n", count_players(), count_all_players());
484  /* CSSTAT is put in so scripts can easily find the line */
485  LOG(llevInfo, "CSSTAT: %.16s tot %d %d %d %ld inc %d %d %d %ld\n",
489  cst_lst.ibytes = 0;
490  cst_lst.obytes = 0;
493 }
494 #endif
495 
global.h
first_player
player * first_player
Definition: init.cpp:106
SockList_AddShort
void SockList_AddShort(SockList *sl, uint16_t data)
Definition: lowlevel.cpp:113
CS_Stats::ibytes
int ibytes
Definition: newclient.h:684
Send_With_Handling
void Send_With_Handling(socket_struct *ns, SockList *sl)
Definition: lowlevel.cpp:444
llevError
@ llevError
Definition: logger.h:11
SockList_NullTerminate
void SockList_NullTerminate(SockList *sl)
Definition: lowlevel.cpp:234
LOG
void LOG(LogLevel logLevel, const char *format,...)
Definition: logger.cpp:51
CS_Stats
Definition: newclient.h:683
player
Definition: player.h:105
n
based on the size of the this randomly makes land number of spaces randomly lower or higher The default is Note that this is run also based on the the altitude amount will likely be less So if you do something like l and n
Definition: land.6.txt:25
socket_struct
Definition: newserver.h:89
SockList::len
size_t len
Definition: newclient.h:675
socket_info
Socket_Info socket_info
Definition: init.cpp:49
GetShort_String
short GetShort_String(const unsigned char *data)
Definition: lowlevel.cpp:255
SockList_AddData
void SockList_AddData(SockList *sl, const void *data, size_t len)
Definition: lowlevel.cpp:164
time
non standard information is not specified or uptime this means how long since the executable has been started A particular host may have been running a server for quite a long time
Definition: arch-handbook.txt:206
SockList_Reset
void SockList_Reset(SockList *sl)
Definition: lowlevel.cpp:71
CS_Stats::obytes
int obytes
Definition: newclient.h:685
SockList_Ensure
static void SockList_Ensure(const SockList *sl, size_t size)
Definition: lowlevel.cpp:92
buf
StringBuffer * buf
Definition: readable.cpp:1552
SockList
Definition: newclient.h:670
Ns_Dead
@ Ns_Dead
Definition: newserver.h:67
SockList_Term
void SockList_Term(SockList *sl)
Definition: lowlevel.cpp:62
stringbuffer_finish
char * stringbuffer_finish(StringBuffer *sb)
Definition: stringbuffer.cpp:76
SockList::buf
unsigned char buf[MAXSOCKBUF]
Definition: newclient.h:676
CS_Stats::max_conn
short max_conn
Definition: newclient.h:686
navar-midane_time.data
data
Definition: navar-midane_time.py:11
SockList_Init
void SockList_Init(SockList *sl)
Definition: lowlevel.cpp:52
sproto.h
SockList_AddLen8Data
void SockList_AddLen8Data(SockList *sl, const void *data, size_t len)
Definition: lowlevel.cpp:176
SockList_AddChar
void SockList_AddChar(SockList *sl, unsigned char data)
Definition: lowlevel.cpp:103
nlohmann::detail::void
j template void())
Definition: json.hpp:4099
cst_tot
CS_Stats cst_tot
llevInfo
@ llevInfo
Definition: logger.h:12
fatal
void fatal(enum fatal_error err)
Definition: utils.cpp:570
SockList_AddInt
void SockList_AddInt(SockList *sl, uint32_t data)
Definition: lowlevel.cpp:124
players
std::vector< archetype * > players
Definition: player.cpp:493
write_cs_stats
void write_cs_stats(void)
socket_struct::status
enum Sock_Status status
Definition: newserver.h:90
SockList_AddStringBuffer
void SockList_AddStringBuffer(SockList *sl, StringBuffer *sb)
Definition: lowlevel.cpp:222
SockList_ReadPacket
int SockList_ReadPacket(int fd, SockList *sl, int len)
Definition: lowlevel.cpp:272
SockList_Avail
size_t SockList_Avail(const SockList *sl)
Definition: lowlevel.cpp:243
send
Definition: send.py:1
format
Python Guilds Quick outline Add a guild(mapmakers) this is still a problem *after dropping the token to gain access to the stove a woodfloor now appears which is Toolshed Token(found in Guild_HQ) *Note also have multiple gates in place to protect players and items from the mana explosion drop x for Jewelers room *Jewelers room works just need to determine what x is drop x for Thaumaturgy room *Thaumaturgy room works just need to determine what x is drop gold dropping the Firestar named fearless allows access to but I suspect that the drop location of the chest is not as intended because the player is in the way once you enter the chest the exit back to the basement is things such as the message et al reside on teleporters which then transport items to the map as they are when the map is already purchased items reappear in that area From my this does not cause any problems at the moment But this should be corrected fixed Major it s now possible to buy guilds Ryo Update Uploaded guild package to CVS Changes the cauldrons and the charging room I spent a while agonizing over They were natural guild enhancements but much too much value for any reasonable expense to buy them Then I thought that they should be pay access but at a greatly reduced rate SO when you buy a forge or whatever for your guild it is available on a perplayer daily rate but it will be accessable for testing and to DMs to play with Like I said lots still to do with the especially comingt up with quest items for buying things like the new workshops and stuff One of the things I would like some input on would be proposals for additional fields for either the guildhouses or guild datafiles to play with Currently the Guildhouse but there is no reason we can t have more than one measure of a guild perhaps have dues relate to Dues and use points for some other suspended or inactive or when a guild is founded inactive active Guilds have the format
Definition: README.txt:140
Write_To_Socket
static void Write_To_Socket(socket_struct *ns, const unsigned char *buf, const int len)
Definition: lowlevel.cpp:396
SockList_AddInt64
void SockList_AddInt64(SockList *sl, uint64_t data)
Definition: lowlevel.cpp:137
Socket_Info::allocated_sockets
int allocated_sockets
Definition: newserver.h:144
SockList_ResetRead
void SockList_ResetRead(SockList *sl)
Definition: lowlevel.cpp:80
newclient.h
CS_Stats::time_start
time_t time_start
Definition: newclient.h:687
socket_struct::fd
int fd
Definition: newserver.h:91
now
Crossfire Protocol most of the time after the actual code was already omit certain important and possibly make life miserable any new developer or curious player should be able to find most of the relevant information here If inconsistencies are found or this documentation proves to be consider the latest server side protocol code in the public source code repository as the authoritative reference Introduction If you were ever curious enough to telnet or netcat to a Crossfire chances are you were sorely disappointed While the protocol may seem to use plain text at it actually uses a mix of ASCII and binary data This handbook attempts to document various aspects of the Crossfire protocol As consult the README file to find out how to get in touch with helpful people via mailing and more History the communications plan was set to be a text based system It was up to the server and client to parse these messages and determine what to do These messages were assumed to be line per message At a reasonably early stage of Eric Anderson wrote a then the data itself you could send many data and after the other end could decode these commands This works fairly but I think the creation of numerous sub packets has some performance hit the eutl was not especially well so writing a client for a different platform became more Eric left to work on other products shortly after writing his which didn t really leave anyone with a full understanding of the socket code 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 Packet Format the outside packet method the byte size for the size information is not included here Eutl originally used bytes for the size to bytes seems it makes a least some sense The actual data is something of the nature of the commands listed below It is a text 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 remember that you must take into account the size of the packet There is no termination of other than knowing how long it should be For now
Definition: protocol.txt:71
count_players
int count_players(void)
Definition: metaserver.cpp:48
SockList_AddLen16Data
void SockList_AddLen16Data(SockList *sl, const void *data, size_t len)
Definition: lowlevel.cpp:188
StringBuffer
Definition: stringbuffer.cpp:25
cst_lst
CS_Stats cst_lst
Definition: newclient.h:690
OUT_OF_MEMORY
@ OUT_OF_MEMORY
Definition: define.h:48
altar_valkyrie.pl
pl
Definition: altar_valkyrie.py:28
SockList_AddPrintf
void SockList_AddPrintf(SockList *sl, const char *format,...)
Definition: lowlevel.cpp:199
if
if(!(yy_init))
Definition: loader.c:2626
SockList_AddString
void SockList_AddString(SockList *sl, const char *data)
Definition: lowlevel.cpp:154
llevDebug
@ llevDebug
Definition: logger.h:13
GetInt_String
int GetInt_String(const unsigned char *data)
Definition: lowlevel.cpp:251