Go to the documentation of this file.
54 #include <sys/types.h>
55 #include <netinet/in.h>
56 #include <netinet/tcp.h>
71 #define VALIDCHAR_MSG "The first character must be alphanumeric and the last cannot be a space. None of these characters are allowed: :;/\\["
124 if (len <= 0 || !
buf) {
144 for (;
buf[s] &&
buf[s] !=
' '; s++)
150 while (
buf[s] ==
' ')
156 for (;
buf[s] &&
buf[s] !=
' '; s++)
160 while (s < len &&
buf[s] ==
' ')
165 if (!strcmp(cmd,
"sound2")) {
168 }
else if (!strcmp(cmd,
"spellmon")) {
171 monitor_spells = atoi(
param);
172 if (monitor_spells < 0 || monitor_spells > 2) {
178 }
else if (!strcmp(cmd,
"darkness")) {
181 darkness = atoi(
param);
182 if (darkness != 0 && darkness != 1) {
188 }
else if (!strcmp(cmd,
"map2cmd")) {
191 map2cmd = atoi(
param);
197 }
else if (!strcmp(cmd,
"facecache")) {
200 facecache = atoi(
param);
201 if (facecache != 0 && facecache != 1) {
207 }
else if (!strcmp(cmd,
"faceset")) {
213 }
else if (!strcmp(cmd,
"mapsize")) {
216 if (sscanf(
param,
"%dx%d%n", &
x, &
y, &
n) != 2 ||
n != (
int)strlen(
param)) {
225 if ((
x <= 0) && (
y <= 0)) {
258 }
else if (!strcmp(cmd,
"tick")) {
262 if (tick != 0 && tick != 1) {
268 }
else if (!strcmp(cmd,
"bot")) {
271 is_bot = atoi(
param);
272 if (is_bot != 0 && is_bot != 1) {
278 }
else if (!strcmp(cmd,
"want_pickup")) {
281 want_pickup = atoi(
param);
282 if (want_pickup != 0 && want_pickup != 1) {
288 }
else if (!strcmp(cmd,
"num_look_objects")) {
306 }
else if (!strcmp(cmd,
"extended_stats")) {
309 extended_stats = atoi(
param);
310 if (extended_stats != 0 && extended_stats != 1) {
316 }
else if (!strcmp(cmd,
"beat")) {
317 int use_beat = atoi(
param);
319 if (use_beat != 0 && use_beat != 1) {
327 }
else if (!strcmp(cmd,
"loginmethod")) {
330 loginmethod = atoi(
param);
333 if (loginmethod > 2) loginmethod=2;
338 }
else if (!strcmp(cmd,
"notifications")) {
341 notifications = atoi(
param);
346 }
else if (!strcmp(cmd,
"newmapcmd")) {
354 }
else if (!strcmp(cmd,
"extendedTextInfos")) {
360 }
else if (!strcmp(cmd,
"itemcmd")) {
366 }
else if (!strcmp(cmd,
"exp64")) {
419 "Warning: Your client is too old to receive map data. Please update to a new client at: "
420 "https://sourceforge.net/projects/crossfire/");
436 const Face *smoothface;
455 "Could not smooth face %s. "
456 "Check that this face has a smoothing pixmap, or remove its smoothlevel.\n",
482 if (len <= 0 || !
buf) {
483 LOG(
llevDebug,
"IP '%s' sent bogus ask_smooth_cmd information\n", ns->
host);
510 LOG(
llevDebug,
"Corrupt ncom command - not long enough - discarding\n");
533 "You can not issue commands - state is not ST_PLAYING (%s)",
539 if (
pl->ob->speed_left < 0) {
540 LOG(
llevError,
"Player %s (%s) has negative time - shouldn't do command.\n",
541 pl->ob->name ?
pl->ob->name :
"(unnamed)",
pl->socket->account_name ?
pl->socket->account_name :
"(no account)");
544 if (setsockopt(
pl->socket->fd, IPPROTO_TCP, TCP_NODELAY, &
tmp,
sizeof(
tmp)))
545 LOG(
llevError,
"send_tick: Unable to turn off TCP_NODELAY\n");
558 if (
FABS(
pl->ob->speed) < 0.001)
566 if (setsockopt(
pl->socket->fd, IPPROTO_TCP, TCP_NODELAY, &
tmp,
sizeof(
tmp)))
567 LOG(
llevError,
"send_tick: Unable to turn on TCP_NODELAY\n");
573 if (len <= 0 || !
buf) {
574 LOG(
llevDebug,
"Player '%s' sent bogus reply_cmd information\n",
pl->ob->name);
582 LOG(
llevError,
"Got reply message with ST_PLAYING input state\n");
635 char *cs_str = strtok_r(
buf,
" ", &
rest);
636 char *sc_str = strtok_r(NULL,
" ", &
rest);
639 if (cs_str == NULL || sc_str == NULL) {
658 LOG(
llevInfo,
"Connection from %s (%s), CS %d, SC %d\n",
693 if (len <= 0 || !
buf) {
694 LOG(
llevDebug,
"Player '%s' sent bogus move_cmd information\n",
pl->ob->name);
703 for (i = 0; i < 2; i++) {
705 if (!(
buf = strchr(
buf,
' '))) {
737 #define AddIfInt64(Old, New, sl, Type) \
740 SockList_AddChar(sl, Type); \
741 SockList_AddInt64(sl, New); \
744 #define AddIfInt(Old, New, sl, Type) \
747 SockList_AddChar(sl, Type); \
748 SockList_AddInt(sl, New); \
751 #define AddIfShort(Old, New, sl, Type) \
754 SockList_AddChar(sl, Type); \
755 SockList_AddShort(sl, New); \
758 #define AddIfFloat(Old, New, sl, Type) \
761 SockList_AddChar(sl, Type); \
762 SockList_AddInt(sl, (long)(New*FLOAT_MULTI));\
765 #define AddIfString(Old, New, sl, Type) \
766 if (Old == NULL || strcmp(Old, New)) { \
768 Old = strdup_local(New); \
769 SockList_AddChar(sl, Type); \
770 SockList_AddLen8Data(sl, New, strlen(New)); \
779 const int16_t until =
MIN(11,
pl->ob->level);
780 for (int16_t i = 1; i < until; i++) {
781 if (
pl->levhp[i] < 9 ||
pl->levsp[i] < 6 ||
pl->levgrace[i] < 3) {
795 const char *god =
"none";
797 if (
pl->socket->notifications < 3) {
809 if (QUERY_FLAG(pl->ob, F)) { \
822 if (strcmp(
item->arch->name,
"poisoning") == 0) {
825 if (strcmp(
item->arch->name,
"blindness") == 0) {
828 if (
item->type ==
FORCE && strcmp(
item->name,
"confusion") == 0) {
858 if (
pl->ob != NULL) {
873 if (
pl->socket->extended_stats) {
874 int16_t golem_hp, golem_maxhp;
882 if (
pl->ob != NULL) {
917 if (
pl->last_skill_ob[s]) {
921 LOG(
llevDebug,
"pruning removed object from last_skill_ob\n");
922 pl->last_skill_ob[s] = NULL;
926 if (
pl->last_skill_exp[s] !=
pl->last_skill_ob[s]->stats.exp) {
933 pl->last_skill_exp[s] =
pl->last_skill_ob[s]->stats.exp;
953 if (
pl->socket->sc_version < 1025) {
965 if (
pl->socket->monitor_spells) {
1037 for (i = 0; i <
anim->num_animations; i++) {
1057 memset(cell->
faces, face,
sizeof(cell->
faces));
1060 #define MAX_HEAD_POS MAX(MAX_CLIENT_X, MAX_CLIENT_Y)
1097 uint8_t nlayer, smoothlevel = 0;
1103 const Face *face =
ob->face;
1127 if (bx < ax || by < ay) {
1128 LOG(
llevError,
"map2_add_ob: bx (%d) or by (%d) is less than ax (%d) or ay (%d)\n", bx, by, ax, ay);
1135 for (
l = layer-1;
l <= layer+1;
l++) {
1138 if (
heads[by][bx][
l] == head)
1145 if (!
heads[by][bx][layer])
1146 heads[by][bx][layer] = head;
1148 heads[by][bx][layer+1] = head;
1153 uint16_t face_num = face ? face->
number : 0;
1157 face_num = (
ob->animation ?
ob->animation->num : 0)|(1<<15);
1169 uint8_t len, anim_speed = 0, i;
1181 smoothlevel =
ob->smoothlevel;
1192 anim_speed =
ob->anim_speed;
1193 else if (
FABS(
ob->speed) < 0.004)
1195 else if (
FABS(
ob->speed) >= 1.0)
1198 anim_speed = (
int)(1.0/
FABS(
ob->speed));
1246 nlayer = 0x10+layer+(2<<5);
1267 int got_one = 0, poisoned = 0, diseased = 0;
1269 int value, granularity;
1270 const object *
probe;
1292 granularity = (
probe->level - 14) / 3;
1293 if (granularity <= 0)
1295 else if (granularity > 30)
1299 if (
ob->stats.maxhp > 0) {
1300 value = (
ob->stats.hp * granularity) / (
ob->stats.maxhp);
1304 else if (
value > granularity)
1305 value = granularity;
1319 snprintf(
name,
sizeof(
name),
"hpbar%s%s%s_%d",
1320 poisoned ?
"_poisoned" :
"",
1321 diseased ?
"_diseased" :
"",
1322 (!poisoned && !diseased) ?
"_standard" :
"",
1325 if (dummy != NULL) {
1335 int got_one =
check_probe(ax, ay,
ob, sl, ns, has_obj, alive_layer);
1339 if (dummy != NULL) {
1364 int layer, got_one = 0, del_one = 0, oldlen, has_obj = 0;
1371 for (layer = 0; layer <
MAP_LAYERS; layer++) {
1374 head =
heads[ay][ax][layer];
1379 got_one +=
map2_add_ob(ax, ay, layer, head, sl, ns, &has_obj, 1);
1392 if (!del_one && !got_one) {
1394 }
else if (del_one && !has_obj) {
1411 int x,
y, ax, ay, darkness, min_x, max_x, min_y, max_y, oldlen, layer;
1447 for (
y = min_y;
y < max_y;
y++, ay++) {
1449 for (
x = min_x;
x < max_x;
x++, ax++) {
1499 int have_darkness = 0, has_obj = 0, got_one = 0, del_one = 0, g1, alive_layer = -1, old_got;
1535 for (layer = 0; layer <
MAP_LAYERS; layer++) {
1542 && (
pl->invisible&(
pl->invisible < 50 ? 4 : 1))
1552 if (got_one != old_got || (
ob->head == NULL &&
ob->more))
1560 if (g1 == has_obj) {
1562 }
else if (
ob->head == NULL) {
1567 if (layer != alive_layer)
1574 if (!del_one && !got_one && !have_darkness) {
1576 }
else if (del_one && !has_obj) {
1598 if (sl.
len > startlen) {
1612 int min_x, min_y, max_x, max_y;
1615 LOG(
llevError,
"draw_client_map called with non player/non eric-server\n");
1619 if (
pl->contr->transport) {
1620 pm =
pl->contr->transport->map;
1635 min_x =
pl->x-
pl->contr->socket->mapx/2;
1636 min_y =
pl->y-
pl->contr->socket->mapy/2;
1637 max_x =
pl->x+(
pl->contr->socket->mapx+1)/2;
1638 max_y =
pl->y+(
pl->contr->socket->mapy+1)/2;
1639 for (j = min_y; j < max_y; j++) {
1640 for (i = min_x; i < max_x; i++) {
1660 if (
pl->contr->do_los) {
1662 pl->contr->do_los = 0;
1684 for (
x = 0;
x < mx;
x++) {
1685 for (
y = 0;
y < my;
y++) {
1689 }
else if (
x+dx < 0 || x+dx >= ns->
mapx ||
y+dy < 0 || y+dy >= ns->
mapy) {
1725 if (!
pl->socket || !
pl->socket->monitor_spells)
1734 if (!
pl->spell_state)
1779 if (!
pl->socket->monitor_spells)
1798 if (!
pl->socket->want_pickup)
1816 switch (
spell->type)
1822 if (!
spell->other_arch)
1829 if (
spell->randomitems != NULL)
1841 int len, i,
skill = 0;
1881 len = strlen(
spell->msg);
1888 if (
pl->socket->monitor_spells >= 2) {
1915 LOG(
llevError,
"esrv_add_spells, tried to add a spell to a NULL player\n");
1919 if (!
pl->socket->monitor_spells)
1939 if (
pl->socket->monitor_spells >= 2) {
1952 LOG(
llevError,
"Asked to send a non-spell object as a spell\n");
1976 if (setsockopt(
pl->socket->fd, IPPROTO_TCP, TCP_NODELAY, &
tmp,
sizeof(
tmp)))
1977 LOG(
llevError,
"send_tick: Unable to turn on TCP_NODELAY\n");
1981 if (setsockopt(
pl->socket->fd, IPPROTO_TCP, TCP_NODELAY, &
tmp,
sizeof(
tmp)))
1982 LOG(
llevError,
"send_tick: Unable to turn off TCP_NODELAY\n");
2053 uint16_t faceno = 0;
2064 if (acn->face[0] != 0 ) {
2131 nlen = (
unsigned char)
buf[0];
2132 if (nlen >=
MAX_BUF || nlen > *len-2) {
2138 plen = (
unsigned char)
buf[nlen+1];
2139 if (plen >=
MAX_BUF || plen > *len-2-nlen) {
2142 memcpy(password,
buf+2+nlen, plen);
2164 if (len <= 0 || !
buf) {
2165 LOG(
llevDebug,
"IP '%s' sent bogus add_player_cmd information\n", ns->
host);
2187 SockList_AddString(&sl,
"failure accountlogin No such account name exists on this server");
2263 if (len <= 0 || !
buf) {
2264 LOG(
llevDebug,
"IP '%s' sent bogus add_player_cmd information\n", ns->
host);
2302 if (strlen(password)<2) {
2310 SockList_AddString(&sl,
"failure accountnew That account already exists on this server");
2319 "failure accountnew Choose a different account name. " VALIDCHAR_MSG);
2327 "failure accountnew That account name is too long");
2336 "failure accountnew Choose a different password. " VALIDCHAR_MSG);
2344 "failure accountnew That password is too long");
2378 if (len <= 0 || !
buf) {
2379 LOG(
llevDebug,
"IP '%s' sent bogus add_player_cmd information\n", ns->
host);
2431 SockList_AddString(&sl,
"failure accountaddplayer 0 That character is already connected to this account.");
2437 SockList_AddString(&sl,
"failure accountaddplayer 1 That character is already connected to a different account.");
2449 SockList_AddString(&sl,
"failure accountaddplayer 0 That character is already connected to a different account which is currently logged in.");
2471 }
else if (
status == 2) {
2472 SockList_AddString(&sl,
"failure accountaddplayer 0 You have reached the maximum number of characters allowed per account.");
2508 if (len <= 0 || !
buf) {
2509 LOG(
llevDebug,
"IP '%s' sent bogus account_play_cmd information\n", ns->
host);
2539 for (i=0; chars[i]; i++) {
2540 if (strcmp(chars[i],
buf) == 0)
2545 "failure accountplay Character %s is not associated with account %s",
2555 "failure accountplay Character %s is already playing",
2570 if (
pl->socket == ns) {
2599 #define MAX_CHOICES 100
2609 int status, nlen, choice_num=0, i;
2615 if (len <= 0 || !
buf) {
2616 LOG(
llevDebug,
"IP '%s' sent bogus create_player_cmd information\n", ns->
host);
2647 SockList_AddString(&sl,
"failure createplayer Player name contains invalid characters");
2654 if (strlen(password)<2) {
2677 if (strlen(password)>17)
2710 if (
pl->socket == ns) {
2712 if (!strcmp(
pl->ob->name,
name)) {
2730 if (
pl->ob->contr ==
pl) {
2748 int i, j, stat_total=0;
2749 char *
key, *
value, *race=NULL, *class_name=NULL;
2754 memset(&new_stats, 0,
sizeof(
living));
2756 while (nlen < len) {
2765 if ((i == 0) || (nlen + i > len))
break;
2785 "failure createplayer Invalid starting map");
2799 "Number of choices receive exceed max value: %d>%d\n",
2802 choices[choice_num] =
value;
2810 int val = atoi(
value);
2838 "failure createplayer Stat value is out of range - %d must be between %d and %d",
2847 "failure createplayer Total allocated statistics is higher than allowed (%d>%d)",
2863 if (!race_a || race_a->clone.type !=
PLAYER || !class_a || class_a->clone.type !=
CLASS) {
2865 "failure createplayer Invalid or unknown race or class");
2876 "failure createplayer Unable to apply race or class - statistic is out of bounds");
2921 pl->name_changed = 1;
2938 if (
pl->ob->map == NULL) {
2939 LOG(
llevError,
"Couldn't put player %s on start map %s!",
pl->ob->name,
map->name);
2945 pl->bed_x =
pl->ob->x;
2946 pl->bed_y =
pl->ob->y;
2958 for (i=0; i < choice_num; i++) {
2960 const char *
value, *cp;
2964 choiceval = strchr(choices[i],
' ');
2966 LOG(
llevError,
"Choice does not specify value: %s\n", choices[i]);
2976 LOG(
llevError,
"Choice not found in archetype: %s\n", choices[i]);
2979 cp = strstr(
value, choiceval);
2981 LOG(
llevError,
"Choice value not found in archetype: %s %s\n",
2982 choices[i], choiceval);
2992 if ((cp[strlen(choiceval)] !=
' ') && (cp[strlen(choiceval)] != 0) &&
2993 (cp !=
value) && (*(cp-1) !=
' ')) {
2995 LOG(
llevError,
"Choice value matches substring but not entire word: %s substring %s\n",
3001 LOG(
llevError,
"Choice value can not find archetype %s\n", choiceval);
3010 LOG(
llevInfo,
"new character %s from %s\n",
pl->ob->name,
pl->ob->contr->socket->host);
3013 "%s has entered the game.",
pl->ob->name);
3031 if (len <= 0 || !
buf) {
3032 LOG(
llevDebug,
"IP '%s' sent bogus account_password_cmd information\n", ns->
host);
3062 if (strlen(change)<2) {
3072 "failure accountpw Choose a different password. " VALIDCHAR_MSG);
3080 "failure accountpw That password is too long");
3091 error =
"failure accountpw Illegal characters present";
3092 }
else if (
status == 2) {
3093 error =
"failure accountpw Invalid account";
3095 error =
"failure accountpw Incorrect current password";
player * find_player_socket(const socket_struct *ns)
#define ADD_PLAYER_NO_STATS_ROLL
void account_add_player_cmd(char *buf, int len, socket_struct *ns)
#define CS_STAT_RES_DEPLETE
static const short atnr_cs_stat[NROFATTACKS]
#define MAP_CLIENT_X_MINIMUM
void SockList_AddInt(SockList *sl, uint32_t data)
#define ST_CHANGE_PASSWORD_OLD
void print_ext_msg(socket_struct *ns, int color, uint8_t type, uint8_t subtype, const char *message)
int find_smooth(const Face *face, const Face **smoothed)
static int annotate_ob(int ax, int ay, const object *ob, SockList *sl, socket_struct *ns, int *has_obj, int *alive_layer)
void SockList_AddInt64(SockList *sl, uint64_t data)
static bool CAN_PROBE(const object *ob)
#define FLAG_CLIENT_ANIM_RANDOM
char * account_trusted_host
void send_account_players(socket_struct *ns)
#define CS_STAT_APPLIED_WIS
#define MSG_TYPE_ADMIN_PLAYER
void LOG(LogLevel logLevel, const char *format,...)
void send_query(socket_struct *ns, uint8_t flags, const char *text)
void account_play_cmd(char *buf, int len, socket_struct *ns)
void new_player_cmd(uint8_t *buf, int len, player *pl)
#define MAP2_COORD_OFFSET
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
void ask_smooth_cmd(char *buf, int len, socket_struct *ns)
#define QUERY_FLAG(xyz, p)
char const * newhash(char const *password)
arch
DIALOGCHECK MINARGS 1 MAXARGS 1
linked_char * account_get_additional_chars(const char *account_name, const Account_Chars *chars, int *count)
#define CS_STAT_APPLIED_POW
int get_skill_client_code(const char *skill_name)
static int map2_delete_layer(int ax, int ay, int layer, SockList *sl, socket_struct *ns)
void update_position(mapstruct *m, int x, int y)
#define MAX_NUM_LOOK_OBJECTS
void command_execute(object *pl, char *command)
#define AddIfInt64(Old, New, sl, Type)
void SockList_AddString(SockList *sl, const char *data)
#define CS_STAT_SPELL_DENY
char ** account_get_players_for_account(const char *account_name)
void reply_cmd(char *buf, int len, player *pl)
static void append_spell(player *pl, SockList *sl, object *spell)
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 command
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
uint8_t starting_stat_max
void draw_ext_info_format(int flags, int pri, const object *pl, uint8_t type, uint8_t subtype, const char *format,...) PRINTF_ARGS(6
player * get_player(player *p)
static void send_extra_stats(SockList *sl, player *pl)
static const flag_definition flags[]
#define CS_STAT_RES_HOLYWORD
int account_link(const char *account_name, const char *player_name)
#define CS_STAT_TURN_UNDEAD
#define SP_CREATE_MISSILE
void receive_play_again(object *op, char key)
void set_player_socket(player *p, socket_struct *ns)
#define MSG_TYPE_COMMAND_ERROR
static void map_clearcell(struct map_cell_struct *cell, int face, int darkness)
static bool map2_coord_valid(int x)
void send_plugin_custom_message(object *pl, char *buf)
void SockList_Reset(SockList *sl)
#define CS_STAT_CHARACTER_FLAGS
const char * object_get_value(const object *op, const char *const key)
void set_up_cmd(char *buf, int len, socket_struct *ns)
static const object * heads[MAX_HEAD_POS][MAX_HEAD_POS][MAP_LAYERS]
void key_confirm_quit(object *op, char key)
#define CS_STAT_APPLIED_DEX
player * add_player(socket_struct *ns, int flags)
object * object_insert_in_ob(object *op, object *where)
#define CS_STAT_GOLEM_MAXHP
Plugin animator file specs[Config] name
size_t SockList_Avail(const SockList *sl)
short GetShort_String(const unsigned char *data)
const char *const short_stat_name[NUM_STATS]
#define SP_SUMMON_MONSTER
static event_registration m
#define AddIfShort(Old, New, sl, Type)
Account_Chars * account_chars
void esrv_send_animation(socket_struct *ns, const Animations *anim)
void set_title(const object *pl, char *buf, size_t len)
#define CS_STAT_SPELL_REPEL
void account_new_cmd(char *buf, int len, socket_struct *ns)
void account_login_cmd(char *buf, int len, socket_struct *ns)
int playername_ok(const char *cp)
void esrv_send_face(socket_struct *ns, const Face *face, int nocache)
#define CS_STAT_APPLIED_CHA
#define ST_CHANGE_PASSWORD_CONFIRM
int check_race_and_class(living *stats, archetype *race, archetype *opclass)
int8_t blocked_los[MAP_CLIENT_X][MAP_CLIENT_Y]
void check_login(object *op, const char *password)
const char * account_get_account_for_char(const char *charname)
#define ADD_PLAYER_NO_MAP
void account_char_free(Account_Chars *chars)
static void send_smooth(socket_struct *ns, const Face *face)
sstring add_string(const char *str)
void draw_client_map(object *pl)
int account_remove_player(const char *account_name, const char *player_name)
void SockList_AddShort(SockList *sl, uint16_t data)
object * object_find_by_type_and_name(const object *who, int type, const char *name)
void SockList_AddChar(SockList *sl, unsigned char c)
void set_attr_value(living *stats, int attr, int8_t value)
#define CS_STAT_APPLIED_STR
void account_password(char *buf, int len, socket_struct *ns)
void esrv_map_scroll(socket_struct *ns, int dx, int dy)
#define GET_MAP_FACE_OBJ(M, X, Y, L)
#define FREE_AND_COPY(sv, nv)
static void check_space_for_heads(int ax, int ay, SockList *sl, socket_struct *ns)
#define CS_STAT_APPLIED_CON
#define MAP_LAYER_LIVING1
void esrv_update_spells(player *pl)
const char * skill_names[MAX_SKILLS]
uint16_t faces[MAP_LAYERS]
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
#define CS_STAT_RES_GHOSTHIT
struct linked_char * next
#define CS_STAT_SKILLINFO
void free_charlinks(linked_char *lc)
int account_new(const char *account_name, const char *account_password)
#define ST_CHANGE_PASSWORD_NEW
#define CS_STAT_RES_BLIND
#define FLAG_UNAGGRESSIVE
#define MAP2_TYPE_DARKNESS
static uint16_t MAP2_COORD_ENCODE(int x, int y, int flags)
mapstruct * get_map_from_coord(mapstruct *m, int16_t *x, int16_t *y)
const Face * get_face_by_id(uint16_t id)
uint8_t account_block_create
int16_t SP_level_spellpoint_cost(object *caster, object *spell, int flags)
int account_is_logged_in(const char *name)
void SockList_Init(SockList *sl)
static uint8_t is_perfect(const player *pl)
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
#define CS_STAT_WEIGHT_LIM
#define CS_STAT_ITEM_POWER
#define CS_STAT_RES_POISON
void receive_player_name(object *op, const char *name)
int is_valid_faceset(int fsn)
object * object_present_in_ob(uint8_t type, const object *op)
uint32_t get_weight_limit(int stat)
void SockList_Term(SockList *sl)
#define AddIfInt(Old, New, sl, Type)
int probe(object *op, object *caster, object *spell_ob, int dir, int level)
void key_roll_stat(object *op, char key)
#define FLAG_CLIENT_ANIM_SYNC
void create_player_cmd(char *buf, int len, socket_struct *ns)
int apply_race_and_class(object *op, archetype *race, archetype *opclass, living *stats)
#define CS_STAT_SPELL_ATTUNE
void SockList_AddData(SockList *sl, const void *data, size_t len)
void esrv_move_object(object *pl, tag_t to, tag_t tag, long nrof)
client_spell * get_client_spell_state(player *pl, object *spell)
int SP_level_dam_adjust(const object *caster, const object *spob)
object * object_find_by_arch_name(const object *who, const char *name)
#define AddIfFloat(Old, New, sl, Type)
#define ST_CONFIRM_PASSWORD
void send_tick(player *pl)
uint8_t starting_stat_min
static int map2_add_ob(int ax, int ay, int layer, const object *ob, SockList *sl, socket_struct *ns, int *has_obj, int is_head)
int get_map_flags(mapstruct *oldmap, mapstruct **newmap, int16_t x, int16_t y, int16_t *nx, int16_t *ny)
static int account_block_create(const socket_struct *ns)
void enter_exit(object *op, object *exit_ob)
void map_newmap_cmd(socket_struct *ns)
void esrv_add_spells(player *pl, object *spell)
const typedef char * sstring
#define MAP_CLIENT_Y_MINIMUM
void esrv_remove_spell(player *pl, object *spell)
std::vector< Account_Char * > chars
void SockList_AddLen8Data(SockList *sl, const void *data, size_t len)
void receive_player_password(object *op, const char *password)
struct map_cell_struct cells[MAX_CLIENT_X][MAX_CLIENT_Y]
void add_me_cmd(char *buf, int len, socket_struct *ns)
void esrv_draw_look(object *pl)
void version_cmd(char *buf, int len, socket_struct *ns)
method_ret ob_apply(object *op, object *applier, int aflags)
#define NUM_ANIMATIONS(ob)
void SockList_ResetRead(SockList *sl)
uint8_t anims_sent[MAXANIMNUM]
int account_change_password(const char *account_name, const char *current_password, const char *new_password)
#define CS_STAT_RES_DEATH
object * arch_to_object(archetype *at)
int GetInt_String(const unsigned char *data)
static void draw_client_map2(object *pl)
int strcasecmp(const char *s1, const char *s2)
int save_player(object *op, int flag)
#define ST_GET_PARTY_PASSWORD
archetype * get_archetype_by_type_subtype(int type, int subtype)
void esrv_send_pickup(player *pl)
void object_remove(object *op)
void player_set_state(player *pl, uint8_t state)
archetype * try_find_archetype(const char *name)
#define AddIfString(Old, New, sl, Type)
const Face * try_find_face(const char *name, const Face *error)
#define MIN_NUM_LOOK_OBJECTS
#define MAP_LAYER_LIVING2
static int check_probe(int ax, int ay, const object *ob, SockList *sl, socket_struct *ns, int *has_obj, int *alive_layer)
void move_cmd(char *buf, int len, player *pl)
Account_Chars * account_char_load(const char *account_name)
void key_change_class(object *op, char key)
void receive_party_password(object *op, const char *password)
static int spell_client_use(const object *spell)
int account_check_string(const char *str)
void esrv_update_stats(player *pl)
No space after the left no space before the right paren Comma right after the formal param
#define MSG_TYPE_ADMIN_VERSION
int8_t get_attr_value(const living *stats, int attr)
void update_los(object *op)
static void add_char_field(SockList *sl, int type, const char *data)
int account_login(const char *account_name, const char *account_password)
int verify_player(const char *name, char *password)
void esrv_new_player(player *pl, uint32_t weight)
static int decode_name_password(const char *buf, int *len, char *name, char *password)
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 most everything that is sent is text This is more or less how things worked under except it packed the ints into bytes in a known order In some we handle ints as in they are sent as binary information How any command handles it is detailed below in the command description The S and C represent the direction of the we use MSB as well as any ints or shorts that get sent inside the packets All packets are defined to have at least one word of text
void account_char_save(Account_Chars *chars)
#define CS_STAT_RES_DRAIN
uint8_t starting_stat_points
void Send_With_Handling(socket_struct *ns, SockList *sl)
#define FOR_INV_PREPARE(op_, it_)
void account_char_remove(Account_Chars *chars, const char *pl_name)
#define CS_STAT_APPLIED_INT
void rangetostring(const object *pl, char *obuf, size_t len)
#define NS_FACESENT_SMOOTH
void SockList_AddPrintf(SockList *sl, const char *format,...)
const char * account_exists(const char *account_name)