Crossfire Server, Trunk  1.75.0
item.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 
25 #include "global.h"
26 
27 #include <stdlib.h>
28 #include <string.h>
29 
30 #include "newserver.h"
31 #include "object.h"
32 #include "shared/newclient.h"
33 #include "sproto.h"
34 
36 #define MAXITEMLEN 300
37 
38 /*************************************************************************
39  *
40  * Functions related to sending object data to the client.
41  *
42  *************************************************************************
43  */
44 
49 static unsigned int query_flags(const object *op) {
50  unsigned int flags = 0;
51 
52  if (QUERY_FLAG(op, FLAG_APPLIED)) {
53  switch (op->type) {
54  case BOW:
55  case WAND:
56  case ROD:
57  flags = a_readied;
58  break;
59 
60  case WEAPON:
61  flags = a_wielded;
62  break;
63 
64  case SKILL:
65  case ARMOUR:
66  case HELMET:
67  case SHIELD:
68  case RING:
69  case BOOTS:
70  case GLOVES:
71  case AMULET:
72  case GIRDLE:
73  case BRACERS:
74  case CLOAK:
75  flags = a_worn;
76  break;
77 
78  case CONTAINER:
79  flags = a_active;
80  break;
81 
82  default:
83  flags = a_applied;
84  break;
85  }
86  }
87  if (op->type == CONTAINER
88  && ((op->env && op->env->container == op) || (!op->env && QUERY_FLAG(op, FLAG_APPLIED))))
89  flags |= F_OPEN;
90 
91  if (!is_identified(op))
93 
94  if (QUERY_FLAG(op, FLAG_KNOWN_CURSED)) {
95  if (QUERY_FLAG(op, FLAG_DAMNED))
96  flags |= F_DAMNED;
97  else if (QUERY_FLAG(op, FLAG_CURSED))
98  flags |= F_CURSED;
99  }
101  flags |= F_MAGIC;
102  if (QUERY_FLAG(op, FLAG_UNPAID))
103  flags |= F_UNPAID;
104  if (QUERY_FLAG(op, FLAG_INV_LOCKED))
105  flags |= F_LOCKED;
107  flags |= F_BLESSED;
108  // Denote when a book has been read. This gives GUI feedback in the inventory window if the client handles the flag.
109  // NO_SKILL_IDENT is set when identification fails or when the book is read. So the book is read only when it is
110  // both identified and NO_SKILL_IDENT.
112  flags |= F_READ;
113 
114  return flags;
115 }
116 
122 static void add_object_to_socklist(socket_struct *ns, SockList *sl, object *head) {
123  int flags, len, anim_speed;
124  char item_n[MAX_BUF], item_p[MAX_BUF];
125  sstring custom_name = object_get_value(head, CUSTOM_NAME_FIELD);
126 
127  flags = query_flags(head);
128  if (QUERY_FLAG(head, FLAG_NO_PICK))
129  flags |= F_NOPICK;
130 
131  if (!(ns->faces_sent[head->face->number]&NS_FACESENT_FACE))
132  esrv_send_face(ns, head->face, 0);
133 
134  if (QUERY_FLAG(head, FLAG_ANIMATE)) {
135  if (head->animation == NULL) {
136  LOG(llevError, "Item %s in %s (%d,%d) has FLAG_ANIMATE but animation_id 0\n", head->name, (head->env ? head->env->name : (head->map ? head->map->path : "???")), head->x, head->y);
137  CLEAR_FLAG(head, FLAG_ANIMATE);
138  } else if (!ns->anims_sent[head->animation->num])
139  esrv_send_animation(ns, head->animation);
140  }
141 
142  SockList_AddInt(sl, head->count);
143  SockList_AddInt(sl, flags);
144  SockList_AddInt(sl, QUERY_FLAG(head, FLAG_NO_PICK) ? -1 : WEIGHT(head));
145  SockList_AddInt(sl, head->face->number);
146 
147  if (!custom_name) {
148  query_base_name(head, 0, item_n, 126);
149  item_n[127] = 0;
150  len = strlen(item_n);
151  query_base_name(head, 1, item_p, MAX_BUF);
152  } else {
153  safe_strncpy(item_n, custom_name, 127);
154  item_n[127] = 0;
155  len = strlen(item_n);
156  safe_strncpy(item_p, custom_name, sizeof(item_p));
157  }
158  strncpy(item_n+len+1, item_p, 127);
159  /* This is needed because strncpy may not add a ending \0 if the string is long enough. */
160  item_n[len+1+127] = 0;
161  len += strlen(item_n+1+len)+1;
162  SockList_AddLen8Data(sl, item_n, len);
163 
164  SockList_AddShort(sl, (QUERY_FLAG(head, FLAG_ANIMATE) && head->animation) ? head->animation->num : 0);
165  anim_speed = 0;
166  if (QUERY_FLAG(head, FLAG_ANIMATE)) {
167  if (head->anim_speed)
168  anim_speed = head->anim_speed;
169  else {
170  if (FABS(head->speed) < 0.001)
171  anim_speed = 255;
172  else if (FABS(head->speed) >= 1.0)
173  anim_speed = 1;
174  else
175  anim_speed = (int)(1.0/FABS(head->speed));
176  }
177  if (anim_speed > 255)
178  anim_speed = 255;
179  }
180  SockList_AddChar(sl, (char)anim_speed);
181  SockList_AddInt(sl, head->nrof);
182 
183  SockList_AddShort(sl, head->client_type);
184 
185  SET_FLAG(head, FLAG_CLIENT_SENT);
186 }
187 
193 void esrv_draw_look(object *pl) {
194  object *tmp, *last;
195  int got_one = 0, start_look = 0, end_look = 0, objects_sent = 0;
196  SockList sl;
197  char buf[MAX_BUF];
198 
199  if (!pl->contr->socket->update_look) {
200  LOG(llevDebug, "esrv_draw_look called when update_look was not set\n");
201  return;
202  } else {
203  pl->contr->socket->update_look = 0;
204  }
205 
206  if (QUERY_FLAG(pl, FLAG_REMOVED)
207  || pl->map == NULL
208  || pl->map->in_memory != MAP_IN_MEMORY
209  || out_of_map(pl->map, pl->x, pl->y))
210  return;
211 
212  if (pl->contr->transport)
213  for (tmp = pl->contr->transport->inv; tmp && tmp->above; tmp = tmp->above)
214  ;
215  else
216  for (tmp = GET_MAP_OB(pl->map, pl->x, pl->y); tmp && tmp->above; tmp = tmp->above)
217  ;
218 
219  SockList_Init(&sl);
220  SockList_AddString(&sl, "delinv 0");
221  Send_With_Handling(pl->contr->socket, &sl);
222 
223  SockList_Reset(&sl);
224  SockList_AddPrintf(&sl, "item2 ");
225  SockList_AddInt(&sl, 0);
226 
229 
230  if (pl->contr->socket->look_position) {
231  int overhead = 1+(pl->contr->transport != NULL);
232  int prev_len = pl->contr->socket->num_look_objects-overhead-(pl->contr->socket->look_position > pl->contr->socket->num_look_objects-overhead);
233  SockList_AddInt(&sl, 0x80000000|MAX(0, pl->contr->socket->look_position-prev_len));
234  SockList_AddInt(&sl, 0);
235  SockList_AddInt(&sl, -1);
237  snprintf(buf, sizeof(buf), "Click here to see previous group of items");
238  SockList_AddLen8Data(&sl, buf, MIN(strlen(buf), 255));
239  SockList_AddShort(&sl, 0);
240  SockList_AddChar(&sl, 0);
241  SockList_AddInt(&sl, 0);
242  SockList_AddShort(&sl, 0);
243  objects_sent++;
244  got_one++;
245  }
246 
247  if (pl->contr->transport) {
249  objects_sent++;
250  got_one++;
251  }
252 
253  last = NULL;
255  object *head;
256 
257  if (tmp == last) {
258  break;
259  }
260 
261  if (!QUERY_FLAG(pl, FLAG_WIZ)) {
262  // Unless DM, stop at the first floor or two consecutive floor objects.
263  if (QUERY_FLAG(tmp, FLAG_IS_FLOOR) && !last) {
264  last = tmp->below; /* assumes double floor mode */
265  if (last && QUERY_FLAG(last, FLAG_IS_FLOOR))
266  last = last->below;
267  }
268  }
269  if (QUERY_FLAG(pl, FLAG_WIZ) || LOOK_OBJ(tmp)) {
270  if (start_look++ < pl->contr->socket->look_position)
271  continue;
272  end_look++;
273  objects_sent++;
274  if (objects_sent >= pl->contr->socket->num_look_objects) {
275  /* What we basically do is make a 'fake' object -
276  * when the user applies it, we notice the special
277  * tag the object has, and act accordingly.
278  */
279  SockList_AddInt(&sl, 0x80000000|(pl->contr->socket->look_position+end_look-1));
280  SockList_AddInt(&sl, 0);
281  SockList_AddInt(&sl, -1);
283  snprintf(buf, sizeof(buf), "Click here to see next group of items");
284  SockList_AddLen8Data(&sl, buf, MIN(strlen(buf), 255));
285  SockList_AddShort(&sl, 0);
286  SockList_AddChar(&sl, 0);
287  SockList_AddInt(&sl, 0);
288  SockList_AddShort(&sl, 0);
289  break;
290  }
291  head = HEAD(tmp);
292  add_object_to_socklist(pl->contr->socket, &sl, head);
293  got_one++;
294 
295  if (SockList_Avail(&sl) < MAXITEMLEN) {
296  Send_With_Handling(pl->contr->socket, &sl);
297  SockList_Reset(&sl);
298  SockList_AddPrintf(&sl, "item2 ");
299  SockList_AddInt(&sl, 0);
300  got_one = 0;
301  }
302  } /* If LOOK_OBJ() */
304  if (got_one)
305  Send_With_Handling(pl->contr->socket, &sl);
306 
307  SockList_Term(&sl);
308 }
309 
316 void esrv_send_inventory(object *pl, object *op) {
317  int got_one = 0, start_look = 0, end_look = 0, objects_sent = 0;
318  SockList sl;
319  char buf[MAX_BUF];
320  int prev_len = pl->contr->socket->num_look_objects - 2 - (((pl->contr->socket->container_position > pl->contr->socket->num_look_objects - 1)) ? 1 : 0);
321 
322  SockList_Init(&sl);
323  SockList_AddPrintf(&sl, "delinv %u", op->count);
324  Send_With_Handling(pl->contr->socket, &sl);
325 
326  SockList_Reset(&sl);
327  SockList_AddString(&sl, "item2 ");
328  SockList_AddInt(&sl, op->count);
329  objects_sent++;
330 
331  if (pl != op && pl->contr->socket->container_position) {
332  SockList_AddInt(&sl, 0x80000000|MAX(0, pl->contr->socket->container_position-prev_len));
333  SockList_AddInt(&sl, 0);
334  SockList_AddInt(&sl, -1);
336  snprintf(buf, sizeof(buf), "Click here to see previous group of items");
337  SockList_AddLen8Data(&sl, buf, MIN(strlen(buf), 255));
338  SockList_AddShort(&sl, 0);
339  SockList_AddChar(&sl, 0);
340  SockList_AddInt(&sl, 0);
341  SockList_AddShort(&sl, 0);
342  objects_sent++;
343  got_one++;
344  }
345 
346  FOR_INV_PREPARE(op, tmp) {
347  object *head;
348 
349  head = HEAD(tmp);
350  if (LOOK_OBJ(head)) {
351  if (start_look++ < pl->contr->socket->container_position && pl != op)
352  continue;
353  end_look++;
354  objects_sent++;
355  if (pl != op && objects_sent >= pl->contr->socket->num_look_objects) {
356  /* What we basically do is make a 'fake' object -
357  * when the user applies it, we notice the special
358  * tag the object has, and act accordingly.
359  */
360  SockList_AddInt(&sl, 0x80000000|(pl->contr->socket->container_position + end_look - 1));
361  SockList_AddInt(&sl, 0);
362  SockList_AddInt(&sl, -1);
364  snprintf(buf, sizeof(buf), "Click here to see next group of items");
365  SockList_AddLen8Data(&sl, buf, MIN(strlen(buf), 255));
366  SockList_AddShort(&sl, 0);
367  SockList_AddChar(&sl, 0);
368  SockList_AddInt(&sl, 0);
369  SockList_AddShort(&sl, 0);
370  break;
371  }
372 
373  add_object_to_socklist(pl->contr->socket, &sl, head);
374 
375  got_one++;
376 
377  /* It is possible for players to accumulate a huge amount of
378  * items (especially with some of the bags out there) to
379  * overflow the buffer. IF so, send multiple item commands.
380  */
381  if (SockList_Avail(&sl) < MAXITEMLEN) {
382  Send_With_Handling(pl->contr->socket, &sl);
383  SockList_Reset(&sl);
384  SockList_AddString(&sl, "item2 ");
385  SockList_AddInt(&sl, op->count);
386  got_one = 0;
387  }
388  } /* If LOOK_OBJ() */
389  } FOR_INV_FINISH();
390  if (got_one) {
391  /* special case: only one item, the "prev group" arrow */
392  if (pl != op && pl->contr->socket->container_position) {
393  if (got_one > 1)
394  Send_With_Handling(pl->contr->socket, &sl);
395  else {
396  /* view shifted, get to previous page and resend */
397  pl->contr->socket->container_position = MAX(0, pl->contr->socket->container_position - prev_len);
398  esrv_send_inventory(pl, op);
399  }
400  } else
401  Send_With_Handling(pl->contr->socket, &sl);
402  }
403  SockList_Term(&sl);
404 }
405 
414 void esrv_update_item(int flags, object *pl, object *op) {
415  SockList sl;
416 
417  if (!pl->contr)
418  return;
419 
420  /* If we have a request to send the player item, skip a few checks. */
421  if (op != pl) {
422  if (!LOOK_OBJ(op))
423  return;
424  /* we remove the check for op->env, because in theory, the object
425  * is hopefully in the same place, so the client should preserve
426  * order.
427  */
428  }
429  if (!QUERY_FLAG(op, FLAG_CLIENT_SENT)) {
430  // Sometimes, we try to update an item that we haven't sent to the
431  // client. Don't! This can happen, for example, when a button under the
432  // floor gets toggled, but objects under floor tiles are generally not
433  // sent. There are some other places where this happens that we haven't
434  // tracked down, but in general, just don't.
435  //LOG(llevDebug, "We have not sent item %s (%d)\n", op->name, op->count);
436  return;
437  }
438 
439  SockList_Init(&sl);
440  SockList_AddString(&sl, "upditem ");
441  SockList_AddChar(&sl, (char)flags);
442 
443  op = HEAD(op);
444  SockList_AddInt(&sl, op->count);
445  sstring custom_name = object_get_value(op, CUSTOM_NAME_FIELD);
446 
447  if (flags&UPD_LOCATION)
448  SockList_AddInt(&sl, op->env ? op->env->count : 0);
449 
450  if (flags&UPD_FLAGS)
451  SockList_AddInt(&sl, query_flags(op));
452 
453  if (flags&UPD_WEIGHT) {
454  int32_t weight = WEIGHT(op);
455 
456  /* TRANSPORTS are odd - they sort of look like containers,
457  * yet can't be picked up. So we don't to send the weight,
458  * as it is odd that you see weight sometimes and not other
459  * (the draw_look won't send it for example.
460  */
461  SockList_AddInt(&sl, QUERY_FLAG(op, FLAG_NO_PICK) ? -1 : weight);
462  if (pl == op) {
463  op->contr->last_weight = weight;
464  }
465  }
466 
467  if (flags&UPD_FACE) {
468  if (!(pl->contr->socket->faces_sent[op->face->number]&NS_FACESENT_FACE))
469  esrv_send_face(pl->contr->socket, op->face, 0);
470  SockList_AddInt(&sl, op->face->number);
471  }
472  if (flags&UPD_NAME) {
473  int len;
474  char item_p[MAX_BUF];
475  char item_n[MAX_BUF];
476 
477  if (!custom_name) {
478  query_base_name(op, 0, item_n, sizeof(item_n)-1);
479  query_base_name(op, 1, item_p, sizeof(item_p));
480  } else {
481  strlcpy(item_n, custom_name, sizeof(item_n)-1);
482  strlcpy(item_p, custom_name, sizeof(item_p));
483  }
484 
485  len = strlen(item_n)+1;
486  snprintf(item_n+len, sizeof(item_n)-len, "%s", item_p);
487  len += strlen(item_n+len);
488  SockList_AddLen8Data(&sl, item_n, len);
489  }
490  if (flags&UPD_ANIM)
491  SockList_AddShort(&sl, op->animation ? op->animation->num : 0);
492 
493  if (flags&UPD_ANIMSPEED) {
494  int anim_speed = 0;
495 
496  if (QUERY_FLAG(op, FLAG_ANIMATE)) {
497  if (op->anim_speed)
498  anim_speed = op->anim_speed;
499  else {
500  if (FABS(op->speed) < 0.001)
501  anim_speed = 255;
502  else if (FABS(op->speed) >= 1.0)
503  anim_speed = 1;
504  else
505  anim_speed = (int)(1.0/FABS(op->speed));
506  }
507  if (anim_speed > 255)
508  anim_speed = 255;
509  }
510  SockList_AddChar(&sl, (char)anim_speed);
511  }
512  if (flags&UPD_NROF)
513  SockList_AddInt(&sl, op->nrof);
514 
515  Send_With_Handling(pl->contr->socket, &sl);
516  SockList_Term(&sl);
517 }
518 
522 void esrv_send_item(object *pl, object*op) {
523  SockList sl;
524 
525  /* If this is not the player object, do some more checks */
526  if (op != pl) {
527  /* We only send 'visibile' objects to the client */
528  if (!LOOK_OBJ(op))
529  return;
530  /* if the item is on the ground, mark that the look needs to
531  * be updated.
532  */
533  if (!op->env) {
534  pl->contr->socket->update_look = 1;
535  return;
536  }
537  }
538 
539  SockList_Init(&sl);
540  SockList_AddString(&sl, "item2 ");
541 
542  op = HEAD(op);
543  SockList_AddInt(&sl, op->env ? op->env->count : 0);
544 
545  add_object_to_socklist(pl->contr->socket, &sl, op);
546 
547  Send_With_Handling(pl->contr->socket, &sl);
549  SockList_Term(&sl);
550 
551  /* if the object is in an opened container, then it may shift the contents,
552  * so resend everything */
553  if (pl->contr != NULL && pl->container != NULL && op->env == pl->container)
554  pl->contr->socket->update_inventory = 1;
555 }
556 
562 void esrv_del_item(player *pl, object *ob) {
563  SockList sl;
564 
565  if (!QUERY_FLAG(ob, FLAG_CLIENT_SENT))
566  return;
567 
568  SockList_Init(&sl);
569  SockList_AddString(&sl, "delitem ");
570  SockList_AddInt(&sl, ob->count);
571  Send_With_Handling(pl->socket, &sl);
572  SockList_Term(&sl);
573  /* if the object is in an opened container, then it may shift the contents,
574  * so resend everything */
575  if (pl->ob->container != NULL && ob->env == pl->ob->container)
576  pl->socket->update_inventory = 1;
577 }
578 
579 /**************************************************************************
580  *
581  * Client has requested us to do something with an object.
582  *
583  **************************************************************************
584  */
585 
590 bool player_can_find(object *op, object *ob) {
591  return QUERY_FLAG(op, FLAG_WIZ) || !ob->invisible;
592 }
593 
597 static object *ob_if_can_find(object *op, object *ob) {
598  if (player_can_find(op, ob)) {
599  return ob;
600  } else {
601  return NULL;
602  }
603 }
604 
610 static object *esrv_get_ob_from_count(object *pl, tag_t count) {
611  if (pl->count == count)
612  return pl;
613 
614  FOR_INV_PREPARE(pl, op)
615  if (op->count == count)
616  return ob_if_can_find(pl, op);
617  else if (op->type == CONTAINER && pl->container == op) {
618  FOR_INV_PREPARE(op, tmp)
619  if (tmp->count == count)
620  return ob_if_can_find(pl, tmp);
621  FOR_INV_FINISH();
622  }
623  FOR_INV_FINISH();
624 
625  FOR_MAP_PREPARE(pl->map, pl->x, pl->y, op)
626  if (HEAD(op)->count == count)
627  return ob_if_can_find(pl, op);
628  else if (op->type == CONTAINER && pl->container == op) {
629  FOR_INV_PREPARE(op, tmp)
630  if (tmp->count == count)
631  return ob_if_can_find(pl, tmp);
632  FOR_INV_FINISH();
633  }
634  FOR_MAP_FINISH();
635 
636  if (pl->contr->transport) {
637  FOR_INV_PREPARE(pl->contr->transport, tmp)
638  if (tmp->count == count)
639  return ob_if_can_find(pl, tmp);
640  FOR_INV_FINISH();
641  }
642  return NULL;
643 }
644 
646 void examine_cmd(char *buf, int len, player *pl) {
647  long tag;
648  object *op;
649 
650  if (len <= 0 || !buf) {
651  LOG(llevDebug, "Player '%s' sent bogus examine_cmd information\n", pl->ob->name);
652  return;
653  }
654 
655  tag = atoi(buf);
656  op = esrv_get_ob_from_count(pl->ob, tag);
657  if (!op) {
658  LOG(llevDebug, "Player '%s' tried to examine the unknown object (%ld)\n", pl->ob->name, tag);
659  return;
660  }
661  examine(pl->ob, op);
662  if (QUERY_FLAG(pl->ob, FLAG_WIZ)) {
663  do_dump(pl->ob, op);
664  }
665 }
666 
668 void apply_cmd(char *buf, int len, player *pl) {
669  uint32_t tag;
670  object *op;
671 
672  if (!buf || len <= 0) {
673  LOG(llevDebug, "Player '%s' sent bogus apply_cmd information\n", pl->ob->name);
674  return;
675  }
676 
677  tag = atoi(buf);
678  op = esrv_get_ob_from_count(pl->ob, tag);
679 
680  /* sort of a hack, but if the player saves and the player then
681  * manually applies a savebed (or otherwise tries to do stuff),
682  * we run into trouble.
683  */
684  if (QUERY_FLAG(pl->ob, FLAG_REMOVED))
685  return;
686 
687  /* If the high bit is set, player applied a pseudo object. */
688  if (tag&0x80000000) {
689  if (pl->ob->container != NULL) {
690  pl->socket->container_position = tag&0x7fffffff;
691  esrv_send_inventory(pl->ob, pl->ob->container);
692  pl->socket->update_inventory = 0;
693  } else {
694  pl->socket->look_position = tag&0x7fffffff;
695  pl->socket->update_look = 1;
696  }
697  return;
698  }
699 
700  if (!op) {
701  LOG(llevDebug, "Player '%s' tried to apply the unknown object (%d)\n", pl->ob->name, tag);
702  return;
703  }
704  apply_by_living(pl->ob, op, 0, 0);
705 }
706 
708 void lock_item_cmd(uint8_t *data, int len, player *pl) {
709  int flag, tag;
710  object *op;
711  object *tmp;
712  char name[HUGE_BUF];
713 
714  if (len != 5) {
715  LOG(llevDebug, "Player '%s' sent bogus lock_item_cmd information\n", pl->ob->name);
716  return;
717  }
718  flag = data[0];
719  tag = GetInt_String(data+1);
720  op = esrv_get_ob_from_count(pl->ob, tag);
721 
722  if (!op) {
724  "Could not find object to lock/unlock");
725  return;
726  }
727 
728  if (op->map) {
730  "Can't lock/unlock an item on the ground");
731  return;
732  }
733  if (op->env != pl->ob) {
735  "Can't lock/unlock an item not directly in your inventory");
736  return;
737  }
738 
740  if (!flag) {
743  "Unlocked %s.", name);
744  } else {
747  "Locked %s.", name);
748  }
749 
750  tmp = object_merge(op, NULL);
751  if (tmp == NULL) {
752  /* object was not merged - if it was, object_merge() sent updates for us. */
753  esrv_update_item(UPD_FLAGS, pl->ob, op);
754  }
755 }
756 
767 void mark_item_cmd(uint8_t *data, int len, player *pl) {
768  int tag;
769  object *op;
770  char name[MAX_BUF];
771 
772  if (len != 4) {
773  LOG(llevDebug, "Player '%s' sent bogus mark_item_cmd information\n", pl->ob->name);
774  return;
775  }
776 
777  tag = GetInt_String(data);
778  op = esrv_get_ob_from_count(pl->ob, tag);
779  if (!op) {
781  "Could not find object to mark");
782  return;
783  }
784  pl->mark = op;
785  pl->mark_count = op->count;
786  query_name(op, name, MAX_BUF);
788  "Marked item %s",
789  name);
790 }
791 
798 void look_at(object *op, int dx, int dy) {
799  object *tmp;
800  int flag = 0;
801  int16_t x, y;
802  mapstruct *m;
803  char name[MAX_BUF];
804 
805  if (out_of_map(op->map, op->x+dx, op->y+dy))
806  return;
807 
808  x = op->x+dx;
809  y = op->y+dy;
810 
811  m = get_map_from_coord(op->map, &x, &y);
812  if (!m)
813  return;
814 
815  for (tmp = GET_MAP_OB(m, x, y); tmp != NULL && tmp->above != NULL; tmp = tmp->above)
816  ;
817 
819  if (tmp->invisible && !QUERY_FLAG(op, FLAG_WIZ))
820  continue;
821 
822  if (!flag) {
823  if (dx || dy)
825  "There you see:");
826  else {
828  "You see:");
829  }
830  flag = 1;
831  }
832 
833  query_name(tmp, name, MAX_BUF);
834  if (QUERY_FLAG(op, FLAG_WIZ))
836  "- %s (%d).",
837  name, tmp->count);
838  else
840  "- %s.",
841  name);
842 
843  if ((HEAD(tmp)->inv != NULL && (tmp->type != CONTAINER && tmp->type != FLESH))
844  || QUERY_FLAG(op, FLAG_WIZ))
845  inventory(op, HEAD(tmp));
846 
847  /* don't continue under the floor */
848  if (QUERY_FLAG(tmp, FLAG_IS_FLOOR) && !QUERY_FLAG(op, FLAG_WIZ))
849  break;
851 
852  if (!flag) {
853  if (dx || dy)
855  "You see nothing there.");
856  else
858  "You see nothing.");
859  }
860 }
861 
862 static bool player_can_see(player *pl, int dx, int dy) {
863  // TODO: de-duplicate this with darkness check in draw_client_map2()
864  const int darkness = pl->blocked_los[dx+(pl->socket->mapx/2)][dy+(pl->socket->mapy/2)];
865  return darkness < MAX_LIGHT_RADII;
866 }
867 
869 void look_at_cmd(char *buf, int len, player *pl) {
870  int dx, dy;
871  char *cp;
872 
873  if (len <= 0 || !buf) {
874  LOG(llevDebug, "Player '%s' sent bogus look_at_cmd information\n", pl->ob->name);
875  return;
876  }
877 
878  dx = atoi(buf);
879  if (!(cp = strchr(buf, ' '))) {
880  return;
881  }
882  dy = atoi(cp);
883 
884  if (FABS(dx) > MAP_CLIENT_X/2 || FABS(dy) > MAP_CLIENT_Y/2) {
886  "You can't see there from where you're standing.");
887  return;
888  }
889 
890  if (!player_can_see(pl, dx, dy)) {
892  "You can't see there from where you're standing.");
893  return;
894  }
895  look_at(pl->ob, dx, dy);
896 }
897 
899 void esrv_move_object(object *pl, tag_t to, tag_t tag, long nrof) {
900  object *op, *env;
901 
902  op = esrv_get_ob_from_count(pl, tag);
903  if (!op) {
904  LOG(llevDebug, "Player '%s' tried to move an unknown object (%lu)\n", pl->name, (unsigned long)tag);
905  return;
906  }
907 
908  /* If on a transport, you don't drop to the ground - you drop to the
909  * transport.
910  */
911  if (!to && !pl->contr->transport) { /* drop it to the ground */
912  /* LOG(llevDebug, "Drop it on the ground.\n");*/
913 
914  if (op->map && !op->env) {
915 /* LOG(llevDebug, "Dropping object to ground that is already on ground\n");*/
916  return;
917  }
918  /* If it is an active container, then we should drop all objects
919  * in the container and not the container itself.
920  */
921  if (op->inv && QUERY_FLAG(op, FLAG_APPLIED)) {
922  FOR_INV_PREPARE(op, current)
923  drop_object(pl, current, 0);
924  FOR_INV_FINISH();
925  esrv_update_item(UPD_WEIGHT, pl, op);
926  } else {
927  drop_object(pl, op, nrof);
928  }
929  return;
930  } else if (to == pl->count) { /* pick it up to the inventory */
931  /* return if player has already picked it up */
932  if (op->env == pl)
933  return;
934 
935  pl->contr->count = nrof;
936  pick_up(pl, op);
937  return;
938  }
939  /* If not dropped or picked up, we are putting it into a sack */
940  if (pl->contr->transport) {
941  if (object_can_pick(pl, op)
942  && transport_can_hold(pl->contr->transport, op, nrof)) {
943  put_object_in_sack(pl, pl->contr->transport, op, nrof);
944  }
945  } else {
947  if (!env) {
948  LOG(llevDebug, "Player '%s' tried to move object to the unknown location (%d)\n", pl->name, to);
949  return;
950  }
951  /* put_object_in_sack presumes that necessary sanity checking
952  * has already been done (eg, it can be picked up and fits in
953  * in a sack, so check for those things. We should also check
954  * an make sure env is in fact a container for that matter.
955  */
956  if (env->type == CONTAINER
957  && object_can_pick(pl, op)
958  && sack_can_hold(pl, env, op, nrof)) {
959  put_object_in_sack(pl, env, op, nrof);
960  }
961  }
962 }
963 
964 void inscribe_scroll_cmd(char *buf, int len, player *pl) {
965  object *scroll, *spell, *marked, *inscription, *currentspell;
966  tag_t tscroll, tspell, tmarked;
967  char type;
968 
969  if (len < 1) {
970  LOG(llevDebug, "Player %s sent an invalid inscribe command.\n", pl->ob->name);
971  return;
972  }
973 
974  type = buf[0];
975 
976  inscription = find_skill_by_name(pl->ob, "inscription");
977  if (!inscription) {
978  draw_ext_info(NDI_UNIQUE, 0, pl->ob, MSG_TYPE_SKILL, MSG_TYPE_SKILL_FAILURE, "You don't know how to write!");
979  return;
980  }
981 
982  if (type == 0) {
983  if (len != 9) {
984  LOG(llevDebug, "Player %s sent an invalid inscribe command.\n", pl->ob->name);
985  return;
986  }
987  tscroll = GetInt_String((uint8_t *)buf+1);
988  tspell = GetInt_String((uint8_t *)buf+5);
989 
990  scroll = esrv_get_ob_from_count(pl->ob, tscroll);
991  if (!scroll) {
992  LOG(llevDebug, "Player %s sent an invalid scroll for inscribe command.\n", pl->ob->name);
993  return;
994  }
995 
996  spell = esrv_get_ob_from_count(pl->ob, tspell);
997  if (!spell) {
998  LOG(llevDebug, "Player %s sent an invalid spell for inscribe command.\n", pl->ob->name);
999  return;
1000  }
1001 
1002  tmarked = pl->mark_count;
1003  marked = pl->mark;
1004  currentspell = pl->ranges[range_magic];
1005 
1006  pl->mark_count = tscroll;
1007  pl->mark = scroll;
1008  pl->ranges[range_magic] = spell;
1009 
1010  write_on_item(pl->ob, "", inscription);
1011 
1012  pl->mark_count = tmarked;
1013  pl->mark = marked;
1014  pl->ranges[range_magic] = currentspell;
1015  } else {
1016  }
1017 }
GET_MAP_OB
#define GET_MAP_OB(M, X, Y)
Gets the bottom object on a map.
Definition: map.h:170
MAP_CLIENT_X
#define MAP_CLIENT_X
This determines the maximum map size the client can request (and thus what the server will send to th...
Definition: config.h:237
SockList_AddInt
void SockList_AddInt(SockList *sl, uint32_t data)
Adds a 32 bit value.
Definition: lowlevel.cpp:127
global.h
UPD_FACE
#define UPD_FACE
Definition: newclient.h:321
NS_FACESENT_FACE
#define NS_FACESENT_FACE
Bitmask for the faces_sent[] array - what portion of the face have we sent?
Definition: newserver.h:141
safe_strncpy
#define safe_strncpy
Definition: compat.h:27
FOR_MAP_FINISH
#define FOR_MAP_FINISH()
Finishes FOR_MAP_PREPARE().
Definition: define.h:724
esrv_move_object
void esrv_move_object(object *pl, tag_t to, tag_t tag, long nrof)
Move an object to a new location.
Definition: item.cpp:899
MSG_TYPE_COMMAND_SUCCESS
#define MSG_TYPE_COMMAND_SUCCESS
Successful result from command.
Definition: newclient.h:534
BOW
@ BOW
Definition: object.h:123
BRACERS
@ BRACERS
Definition: object.h:222
F_OPEN
#define F_OPEN
Definition: newclient.h:293
llevError
@ llevError
Error, serious thing.
Definition: logger.h:11
FABS
#define FABS(x)
Decstations have trouble with fabs()...
Definition: define.h:22
WAND
@ WAND
Definition: object.h:225
LOG
void LOG(LogLevel logLevel, const char *format,...)
Logs a message to stderr, or to file.
Definition: logger.cpp:58
SET_FLAG
#define SET_FLAG(xyz, p)
Definition: define.h:224
mark_item_cmd
void mark_item_cmd(uint8_t *data, int len, player *pl)
Client wants to mark some object.
Definition: item.cpp:767
FLESH
@ FLESH
animal 'body parts' -b.t.
Definition: object.h:192
socket_struct::container_position
uint16_t container_position
Start of container contents to send to client.
Definition: newserver.h:119
player
One player.
Definition: player.h:105
object::client_type
uint16_t client_type
Public type information.
Definition: object.h:350
GLOVES
@ GLOVES
Definition: object.h:218
object::inv
object * inv
Pointer to the first object in the inventory.
Definition: object.h:298
GIRDLE
@ GIRDLE
Definition: object.h:228
socket_struct::look_position
uint16_t look_position
Start of drawing of look window.
Definition: newserver.h:118
F_UNPAID
#define F_UNPAID
Definition: newclient.h:289
MSG_TYPE_SKILL
#define MSG_TYPE_SKILL
Messages related to skill use.
Definition: newclient.h:411
QUERY_FLAG
#define QUERY_FLAG(xyz, p)
Definition: define.h:226
socket_struct
Socket structure, represents a client-server connection.
Definition: newserver.h:93
socket_struct::mapx
uint8_t mapx
Definition: newserver.h:120
object_merge
object * object_merge(object *op, object *top)
This function goes through all objects below and including top, and merges op to the first matching o...
Definition: object.cpp:2036
player::mark_count
uint32_t mark_count
Count of marked object.
Definition: player.h:212
pick_up
bool pick_up(object *op, object *alt)
Try to pick up an item.
Definition: c_object.cpp:520
socket_struct::num_look_objects
uint8_t num_look_objects
The maximum number of objects to show on the ground view; this number includes the prev/next group fa...
Definition: newserver.h:126
UPD_WEIGHT
#define UPD_WEIGHT
Definition: newclient.h:320
SockList_AddString
void SockList_AddString(SockList *sl, const char *data)
Adds a string without length.
Definition: lowlevel.cpp:157
object::speed
float speed
Frequency of object 'moves' relative to server tick rate.
Definition: object.h:337
object::invisible
int16_t invisible
How much longer the object will be invis.
Definition: object.h:370
object::x
int16_t x
Definition: object.h:335
player::ob
object * ob
The object representing the player.
Definition: player.h:177
ARMOUR
@ ARMOUR
Definition: object.h:125
player::transport
object * transport
Transport the player is in.
Definition: player.h:214
object::map
struct mapstruct * map
Pointer to the map in which this object is present.
Definition: object.h:305
UPD_LOCATION
#define UPD_LOCATION
Definition: newclient.h:318
do_dump
void do_dump(object *who, object *what)
Definition: c_wiz.cpp:2865
WEAPON
@ WEAPON
Definition: object.h:124
write_on_item
int write_on_item(object *pl, const char *params, object *skill)
Implement the 'inscription' skill, which checks for the required skills and marked items before runni...
Definition: skills.cpp:1762
socket_struct::update_inventory
uint32_t update_inventory
If true, we need to send the inventory list.
Definition: newserver.h:109
AMULET
@ AMULET
Definition: object.h:144
draw_ext_info_format
void draw_ext_info_format(int flags, int pri, const object *pl, uint8_t type, uint8_t subtype, const char *format,...) PRINTF_ARGS(6
MIN
#define MIN(x, y)
Definition: compat.h:21
SKILL
@ SKILL
Also see SKILL_TOOL (74) below.
Definition: object.h:148
examine_cmd
void examine_cmd(char *buf, int len, player *pl)
Client wants to examine some object.
Definition: item.cpp:646
object::count
tag_t count
Unique object number for this object.
Definition: object.h:307
flags
static const flag_definition flags[]
Flag mapping.
Definition: gridarta-types-convert.cpp:101
MSG_TYPE_COMMAND_EXAMINE
#define MSG_TYPE_COMMAND_EXAMINE
Player examining something.
Definition: newclient.h:536
esrv_update_item
void esrv_update_item(int flags, object *pl, object *op)
Updates object *op for player *pl.
Definition: item.cpp:414
FLAG_BLESSED
#define FLAG_BLESSED
Item has a blessing, opposite of cursed/damned.
Definition: define.h:369
UPD_NROF
#define UPD_NROF
Definition: newclient.h:325
MSG_TYPE_COMMAND_ERROR
#define MSG_TYPE_COMMAND_ERROR
Bad syntax/can't use command.
Definition: newclient.h:533
FLAG_INV_LOCKED
#define FLAG_INV_LOCKED
Item will not be dropped from inventory.
Definition: define.h:329
SockList_Reset
void SockList_Reset(SockList *sl)
Resets the length of the stored data for writing.
Definition: lowlevel.cpp:74
mapstruct::path
char path[HUGE_BUF]
Filename of the map.
Definition: map.h:355
FLAG_APPLIED
#define FLAG_APPLIED
Object is ready for use by living.
Definition: define.h:235
a_active
@ a_active
Definition: newclient.h:285
buf
StringBuffer * buf
Definition: readable.cpp:1565
object::above
object * above
Pointer to the object stacked above this one.
Definition: object.h:296
HUGE_BUF
#define HUGE_BUF
Used for messages - some can be quite long.
Definition: define.h:37
MSG_TYPE_COMMAND
#define MSG_TYPE_COMMAND
Responses to commands, eg, who.
Definition: newclient.h:408
MAX
#define MAX(x, y)
Definition: compat.h:24
F_READ
#define F_READ
Definition: newclient.h:297
FLAG_NO_PICK
#define FLAG_NO_PICK
Object can't be picked up.
Definition: define.h:239
name
Plugin animator file specs[Config] name
Definition: animfiles.txt:4
FLAG_IS_FLOOR
#define FLAG_IS_FLOOR
Can't see what's underneath this object.
Definition: define.h:302
draw_ext_info
vs only yadda is in because all tags get reset on the next draw_ext_info In the second since it is all in one draw_ext_info
Definition: media-tags.txt:61
socket_struct::update_look
uint32_t update_look
If true, we need to send the look window.
Definition: newserver.h:108
SockList_Avail
size_t SockList_Avail(const SockList *sl)
Returns the available bytes in a SockList instance.
Definition: lowlevel.cpp:246
LOOK_OBJ
#define LOOK_OBJ(ob)
This returns TRUE if the object is something that should be displayed in the look window.
Definition: object.h:521
object::y
int16_t y
Position in the map for this object.
Definition: object.h:335
m
static event_registration m
Definition: citylife.cpp:424
CLOAK
@ CLOAK
Definition: object.h:209
socket_struct::mapy
uint8_t mapy
How large a map the client wants.
Definition: newserver.h:120
MAP_IN_MEMORY
#define MAP_IN_MEMORY
Map is fully loaded.
Definition: map.h:126
a_readied
@ a_readied
Definition: newclient.h:285
object::contr
struct player * contr
Pointer to the player which control this object.
Definition: object.h:284
lock_item_cmd
void lock_item_cmd(uint8_t *data, int len, player *pl)
Client wants to apply some object.
Definition: item.cpp:708
MAP_CLIENT_Y
#define MAP_CLIENT_Y
Definition: config.h:238
HELMET
@ HELMET
Definition: object.h:141
add_object_to_socklist
static void add_object_to_socklist(socket_struct *ns, SockList *sl, object *head)
Used in the send_look to put object head into SockList sl for socket ns.
Definition: item.cpp:122
F_DAMNED
#define F_DAMNED
Definition: newclient.h:292
object::anim_speed
uint8_t anim_speed
Ticks between animation-frames.
Definition: object.h:429
esrv_send_face
void esrv_send_face(socket_struct *ns, const Face *face, int nocache)
Sends a face to a client if they are in pixmap mode, nothing gets sent in bitmap mode.
Definition: image.cpp:72
query_name
void query_name(const object *op, char *buf, size_t size)
Describes an item.
Definition: item.cpp:594
player::blocked_los
int8_t blocked_los[MAP_CLIENT_X][MAP_CLIENT_Y]
Array showing what spaces the player can see.
Definition: player.h:178
FLAG_KNOWN_BLESSED
#define FLAG_KNOWN_BLESSED
Item is known to be blessed.
Definition: define.h:370
FLAG_KNOWN_CURSED
#define FLAG_KNOWN_CURSED
The object is known to be cursed.
Definition: define.h:320
Face::number
uint16_t number
This is the image unique identifier.
Definition: face.h:15
player::ranges
object * ranges[range_size]
Object for each range.
Definition: player.h:116
a_applied
@ a_applied
Definition: newclient.h:285
FOR_OB_AND_BELOW_FINISH
#define FOR_OB_AND_BELOW_FINISH()
Finishes FOR_OB_AND_BELOW_PREPARE().
Definition: define.h:748
HEAD
#define HEAD(op)
Returns the head part of an object.
Definition: object.h:607
range_magic
@ range_magic
Spells.
Definition: player.h:32
SockList_AddShort
void SockList_AddShort(SockList *sl, uint16_t data)
Adds a 16 bit value.
Definition: lowlevel.cpp:116
transport_can_hold
int transport_can_hold(const object *transport, const object *op, int nrof)
Can transport hold object op? This is a pretty trivial function, but in the future,...
Definition: apply.cpp:54
ROD
@ ROD
Definition: object.h:114
CONTAINER
@ CONTAINER
Definition: object.h:236
object::below
object * below
Pointer to the object stacked below this one.
Definition: object.h:295
query_short_name
void query_short_name(const object *op, char *buf, size_t size)
query_short_name(object) is similar to query_name(), but doesn't contain any information about object...
Definition: item.cpp:518
SockList_AddChar
void SockList_AddChar(SockList *sl, unsigned char c)
Adds an 8 bit value.
Definition: lowlevel.cpp:106
object::face
const Face * face
Face with colors.
Definition: object.h:341
out_of_map
int out_of_map(mapstruct *m, int x, int y)
Return 1 if coordinates X and Y are out of the map M, taking into account tiling.
Definition: map.cpp:2324
player_can_see
static bool player_can_see(player *pl, int dx, int dy)
Definition: item.cpp:862
is_identified
int is_identified(const object *op)
Return true if the item is identified, either because it is of a type that doesn't ever need identifi...
Definition: item.cpp:1360
F_CURSED
#define F_CURSED
Definition: newclient.h:291
object::type
uint8_t type
PLAYER, BULLET, etc.
Definition: object.h:348
esrv_send_inventory
void esrv_send_inventory(object *pl, object *op)
Sends inventory of a container.
Definition: item.cpp:316
FLAG_DAMNED
#define FLAG_DAMNED
The object is very cursed.
Definition: define.h:317
UPD_FLAGS
#define UPD_FLAGS
Definition: newclient.h:319
spell
with a maximum of six This is not so if you are wearing plate you receive no benefit Armour is additive with all the supplementry forms of which means that it lasts until the next semi permanent spell effect is cast upon the character spell
Definition: tome-of-magic.txt:44
FOR_INV_FINISH
#define FOR_INV_FINISH()
Finishes FOR_INV_PREPARE().
Definition: define.h:671
F_UNIDENTIFIED
#define F_UNIDENTIFIED
Definition: newclient.h:288
esrv_get_ob_from_count
static object * esrv_get_ob_from_count(object *pl, tag_t count)
Takes a player and object count (tag) and returns the actual object pointer, or null if it can't be f...
Definition: item.cpp:610
tag_t
uint32_t tag_t
Object tag, unique during the whole game.
Definition: object.h:14
sproto.h
get_map_from_coord
mapstruct * get_map_from_coord(mapstruct *m, int16_t *x, int16_t *y)
This is basically the same as out_of_map above(), but instead we return NULL if no map is valid (coor...
Definition: map.cpp:2352
FOR_OB_AND_BELOW_PREPARE
#define FOR_OB_AND_BELOW_PREPARE(op_)
Constructs a loop iterating over an object and all objects below it in the same pile.
Definition: define.h:744
BOOK
@ BOOK
Definition: object.h:119
object::animation
const Animations * animation
Animation of this item, NULL if not animated.
Definition: object.h:428
RING
@ RING
Definition: object.h:190
SockList_Init
void SockList_Init(SockList *sl)
Initializes the SockList instance.
Definition: lowlevel.cpp:55
weight
TIPS on SURVIVING Crossfire is populated with a wealth of different monsters These monsters can have varying immunities and attack types In some of them can be quite a bit smarter than others It will be important for new players to learn the abilities of different monsters and learn just how much it will take to kill them This section discusses how monsters can interact with players Most monsters in the game are out to mindlessly kill and destroy the players These monsters will help boost a player s after he kills them When fighting a large amount of monsters in a single attempt to find a narrower hallway so that you are not being attacked from all sides Charging into a room full of Beholders for instance would not be open the door and fight them one at a time For there are several maps designed for them Find these areas and clear them out All throughout these a player can find signs and books which they can read by stepping onto them and hitting A to apply the book sign These messages will help the player to learn the system One more always keep an eye on your food If your food drops to your character will soon so BE CAREFUL ! NPCs Non Player Character are special monsters which have intelligence Players may be able to interact with these monsters to help solve puzzles and find items of interest To speak with a monster you suspect to be a simply move to an adjacent square to them and push the double ie Enter your and press< Return > You can also use say if you feel like typing a little extra Other NPCs may not speak to but display intelligence with their movement Some monsters can be and may attack the nearest of your enemies Others can be in that they follow you around and help you in your quest to kill enemies and find treasure SPECIAL ITEMS There are many special items which can be found in of these the most important may be the signs all a player must do is apply the handle In the case of the player must move items over the button to hold it down Some of the larger buttons may need very large items to be moved onto before they can be activated Gates and locked but be for you could fall down into a pit full of ghosts or dragons and not be able to get back out Break away sometimes it may be worth a player s time to test the walls of a map for secret doors Fire such as missile weapons and spells you will notice them going up in smoke ! So be careful not to destroy valuable items Spellbooks sometimes a player can learn the other times they cannot There are many different types of books and scrolls out there Improve item have lower weight
Definition: survival-guide.txt:100
F_NOPICK
#define F_NOPICK
Definition: newclient.h:294
player_can_find
bool player_can_find(object *op, object *ob)
Return true if player 'op' can see object 'op' for purpose of locating items for partial item matchin...
Definition: item.cpp:590
find_skill_by_name
object * find_skill_by_name(object *who, const char *name)
This returns the skill pointer of the given name (the one that accumulates exp, has the level,...
Definition: skill_util.cpp:211
env
static std::shared_ptr< inja::Environment > env
Rendering environment.
Definition: mapper.cpp:2222
MAX_BUF
#define MAX_BUF
Used for all kinds of things.
Definition: define.h:35
drop_object
object * drop_object(object *op, object *tmp, uint32_t nrof)
Try to drop an object on the floor.
Definition: c_object.cpp:1075
player::last_weight
int32_t last_weight
Last weight as sent to client; -1 means do not send weight.
Definition: player.h:158
strlcpy
size_t strlcpy(char *dst, const char *src, size_t size)
Portable implementation of strlcpy(3).
Definition: porting.cpp:222
SockList_Term
void SockList_Term(SockList *sl)
Frees all resources allocated by a SockList instance.
Definition: lowlevel.cpp:65
empty_face
const Face * empty_face
Definition: image.cpp:36
FLAG_CLIENT_SENT
#define FLAG_CLIENT_SENT
We use it to detect cases were the server is trying to send an upditem when we have not actually sent...
Definition: define.h:346
F_BLESSED
#define F_BLESSED
Definition: newclient.h:296
to
**Media tags please refer to the protocol file in doc Developers protocol Quick for your pleasure an example[/b][i] This is an old full of dirt and partially destroyed[hand] My dear as you two years i had to leave quickly Words have come to me of powerful magic scrolls discovered in an old temple by my uncle I have moved to study them I not forgot your knowledge in ancient languages I need your help for[print][b] Some parts of document are to damaged to be readable[/b][arcane] Arghis[color=Red] k h[color=dark slate blue] ark[color=#004000] fido[/color][hand] please come as fast as possible my friend[print][b] The bottom of letter seems deliberatly shredded What is but not limited to
Definition: media-tags.txt:30
UPD_ANIMSPEED
#define UPD_ANIMSPEED
Definition: newclient.h:324
MSG_TYPE_COMMAND_FAILURE
#define MSG_TYPE_COMMAND_FAILURE
Failed result from command.
Definition: newclient.h:535
FOR_MAP_PREPARE
#define FOR_MAP_PREPARE(map_, mx_, my_, it_)
Constructs a loop iterating over all objects of a map tile.
Definition: define.h:717
FLAG_KNOWN_MAGICAL
#define FLAG_KNOWN_MAGICAL
The object is known to be magical.
Definition: define.h:319
player::mark
object * mark
Marked object.
Definition: player.h:213
FLAG_REMOVED
#define FLAG_REMOVED
Object is not in any map or invenory.
Definition: define.h:232
FLAG_WIZ
#define FLAG_WIZ
Object has special privilegies.
Definition: define.h:231
esrv_send_animation
void esrv_send_animation(socket_struct *ns, const Animations *anim)
Need to send an animation sequence to the client.
Definition: request.cpp:1038
NDI_UNIQUE
#define NDI_UNIQUE
Print immediately, don't buffer.
Definition: newclient.h:266
object::name
sstring name
The name of the object, obviously...
Definition: object.h:319
MSG_TYPE_SKILL_FAILURE
#define MSG_TYPE_SKILL_FAILURE
Failure in using skill.
Definition: newclient.h:594
esrv_send_item
void esrv_send_item(object *pl, object *op)
Sends item's info to player.
Definition: item.cpp:522
esrv_del_item
void esrv_del_item(player *pl, object *ob)
Tells the client to delete an item.
Definition: item.cpp:562
object_can_pick
int object_can_pick(const object *who, const object *item)
Finds out if an object can be picked up.
Definition: object.cpp:3852
esrv_draw_look
void esrv_draw_look(object *pl)
Send the look window.
Definition: item.cpp:193
newserver.h
mapstruct
This is a game-map.
Definition: map.h:315
object::env
object * env
Pointer to the object which is the environment.
Definition: object.h:301
sstring
const typedef char * sstring
Definition: sstring.h:2
ob_if_can_find
static object * ob_if_can_find(object *op, object *ob)
Return object 'ob' if player 'op' can find it, otherwise return NULL.
Definition: item.cpp:597
SockList_AddLen8Data
void SockList_AddLen8Data(SockList *sl, const void *data, size_t len)
Adds a data block prepended with an 8 bit length field.
Definition: lowlevel.cpp:179
mapstruct::in_memory
uint32_t in_memory
Combination of IN_MEMORY_xxx flags.
Definition: map.h:335
apply_by_living
int apply_by_living(object *pl, object *op, int aflag, int quiet)
Living thing is applying an object.
Definition: apply.cpp:637
object_get_value
sstring object_get_value(const object *op, const char *const key)
Get an extra value by key.
Definition: object.cpp:4331
look_at
void look_at(object *op, int dx, int dy)
Prints items on the specified square.
Definition: item.cpp:798
a_wielded
@ a_wielded
Definition: newclient.h:285
CLEAR_FLAG
#define CLEAR_FLAG(xyz, p)
Definition: define.h:225
WEIGHT
#define WEIGHT(op)
Returns the weight of the given object.
Definition: define.h:645
Animations::num
uint16_t num
Where we are in the array.
Definition: face.h:29
socket_struct::anims_sent
uint8_t anims_sent[MAXANIMNUM]
What animations we sent.
Definition: newserver.h:101
socket_struct::faces_sent
uint8_t * faces_sent
This is a bitmap on sent face status.
Definition: newserver.h:100
apply_cmd
void apply_cmd(char *buf, int len, player *pl)
Client wants to apply some object.
Definition: item.cpp:668
MAXITEMLEN
#define MAXITEMLEN
This is the maximum number of bytes we expect any one item to take up.
Definition: item.cpp:36
data
====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
Definition: protocol.txt:379
GetInt_String
int GetInt_String(const unsigned char *data)
Basically does the reverse of SockList_AddInt, but on strings instead.
Definition: lowlevel.cpp:254
player::count
uint32_t count
Any numbers typed before a command.
Definition: player.h:122
F_MAGIC
#define F_MAGIC
Definition: newclient.h:290
FLAG_ANIMATE
#define FLAG_ANIMATE
The object looks at archetype for faces.
Definition: define.h:242
UPD_NAME
#define UPD_NAME
Definition: newclient.h:322
newclient.h
object::container
object * container
Current container being used.
Definition: object.h:299
look_at_cmd
void look_at_cmd(char *buf, int len, player *pl)
Client wants to look at some object.
Definition: item.cpp:869
inscribe_scroll_cmd
void inscribe_scroll_cmd(char *buf, int len, player *pl)
Definition: item.cpp:964
F_LOCKED
#define F_LOCKED
Definition: newclient.h:295
inventory
void inventory(object *op, object *inv)
Prints object's inventory.
Definition: c_object.cpp:2175
FLAG_UNPAID
#define FLAG_UNPAID
Object hasn't been paid for yet.
Definition: define.h:236
UPD_ANIM
#define UPD_ANIM
Definition: newclient.h:323
object::nrof
uint32_t nrof
Number of objects.
Definition: object.h:342
player::socket
socket_struct * socket
Socket information for this player.
Definition: player.h:107
query_base_name
void query_base_name(const object *op, int plural, char *buf, size_t size)
Query a short name for the item.
Definition: item.cpp:695
query_flags
static unsigned int query_flags(const object *op)
This is a similar to query_name, but returns flags to be sended to client.
Definition: item.cpp:49
BOOTS
@ BOOTS
Definition: object.h:217
SHIELD
@ SHIELD
Definition: object.h:140
a_worn
@ a_worn
Definition: newclient.h:285
FLAG_NO_SKILL_IDENT
#define FLAG_NO_SKILL_IDENT
If set, item cannot be identified w/ a skill.
Definition: define.h:335
FLAG_CURSED
#define FLAG_CURSED
The object is cursed.
Definition: define.h:316
examine
void examine(object *op, object *tmp)
Player examines some object.
Definition: c_object.cpp:1987
sack_can_hold
int sack_can_hold(const object *pl, const object *sack, const object *op, uint32_t nrof)
Check if an item op can be put into a sack.
Definition: c_object.cpp:317
Send_With_Handling
void Send_With_Handling(socket_struct *ns, SockList *sl)
Calls Write_To_Socket to send data to the client.
Definition: lowlevel.cpp:447
SockList
Contains the base information we use to make up a packet we want to send.
Definition: newclient.h:685
CUSTOM_NAME_FIELD
#define CUSTOM_NAME_FIELD
Key in an object for the player-assigned custom name.
Definition: object.h:98
FOR_INV_PREPARE
#define FOR_INV_PREPARE(op_, it_)
Constructs a loop iterating over the inventory of an object.
Definition: define.h:664
MAX_LIGHT_RADII
#define MAX_LIGHT_RADII
Max radii for 'light' object, really large values allow objects that can slow down the game.
Definition: define.h:444
object.h
llevDebug
@ llevDebug
Only for debugging purposes.
Definition: logger.h:13
is_valid_types_gen.type
list type
Definition: is_valid_types_gen.py:25
SockList_AddPrintf
void SockList_AddPrintf(SockList *sl, const char *format,...)
Adds a printf like formatted string.
Definition: lowlevel.cpp:202
FLAG_IDENTIFIED
#define FLAG_IDENTIFIED
Item is identifiable (e.g.
Definition: define.h:261
put_object_in_sack
void put_object_in_sack(object *op, object *sack, object *tmp, uint32_t nrof)
Something tries to put an object into another.
Definition: c_object.cpp:939