Crossfire Client, Trunk
player.c
Go to the documentation of this file.
1 /*
2  * Crossfire -- cooperative multi-player graphical RPG and adventure game
3  *
4  * Copyright (c) 1999-2013 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 
29 #include <stdint.h>
30 
31 #include "client.h"
32 #include "external.h"
33 #include "script.h"
34 #include "mapdata.h"
35 
36 bool profile_latency = false;
37 int64_t *profile_time = NULL;
41 const char *const directions[] = {"stay", "north", "northeast",
42  "east", "southeast", "south",
43  "southwest", "west", "northwest"};
44 
46 bool is_afk;
47 
50 
54 void new_player(long tag, char *name, long weight, long face) {
55  Spell *spell, *spnext;
56 
57  cpl.ob->tag = tag;
58  cpl.ob->nrof = 1;
59  copy_name(cpl.ob->d_name, name);
60 
61  /* Right after player exit server will send this with empty name. */
62  if (strlen(name) != 0) {
63  keybindings_init(name);
64  }
65 
66  cpl.ob->weight = (float)weight / 1000;
67  cpl.ob->face = face;
68 
69  if (cpl.spelldata) {
70  for (spell = cpl.spelldata; spell; spell = spnext) {
71  spnext = spell->next;
72  free(spell);
73  }
74  cpl.spelldata = NULL;
75  }
76 
77  // Reset auto-AFK data.
78  is_afk = false;
79  last_command_sent = time(NULL);
80 }
81 
82 void look_at(int x, int y) {
83  cs_print_string(csocket.fd, "lookat %d %d", x, y);
84 }
85 
86 void client_send_apply(int tag) {
87  cs_print_string(csocket.fd, "apply %d", tag);
88 }
89 
90 void client_send_examine(int tag) {
91  cs_print_string(csocket.fd, "examine %d", tag);
92 }
93 
97 void client_send_move(int loc, int tag, int nrof) {
98  cs_print_string(csocket.fd, "move %d %d %d", loc, tag, nrof);
99 }
100 
101 /* Fire & Run code. The server handles repeating of these actions, so
102  * we only need to send a run or fire command for a particular direction
103  * once - we use the drun and dfire to keep track if we need to send
104  * the full command.
105  */
106 static int drun=-1, dfire=-1;
107 
108 void stop_fire() {
109  if (cpl.input_state != Playing) {
110  return;
111  }
112  dfire |= 0x100;
113 }
114 
115 void clear_fire() {
116  if (dfire != -1) {
117  send_command("fire_stop", -1, SC_FIRERUN);
118  dfire = -1;
119  }
120 }
121 
122 void clear_run() {
123  if (drun != -1) {
124  send_command("run_stop", -1, SC_FIRERUN);
125  drun = -1;
126  }
127 }
128 
129 void fire_dir(int dir) {
130  if (cpl.input_state != Playing) {
131  return;
132  }
133  if (dir != dfire) {
134  char buf[MAX_BUF];
135  snprintf(buf, sizeof(buf), "fire %d", dir);
136  if (send_command(buf, cpl.count, SC_NORMAL)) {
137  dfire = dir;
138  cpl.count = 0;
139  }
140  } else {
141  dfire &= 0xff; /* Mark it so that we need a stop_fire */
142  }
143 }
144 
145 void stop_run() {
146  send_command("run_stop", -1, SC_FIRERUN);
147  drun |= 0x100;
148 }
149 
150 void run_dir(int dir) {
151  if (dir != drun) {
152  char buf[MAX_BUF];
153  snprintf(buf, sizeof(buf), "run %d", dir);
154  if (send_command(buf, -1, SC_NORMAL)) {
155  drun = dir;
156  }
157  } else {
158  drun &= 0xff;
159  }
160 }
161 
162 extern const char *const directions[];
163 
164 int command_to_direction(const char *dir) {
165  for (int i = 0; i < 9; i++) {
166  if (!strcmp(dir, directions[i])) {
167  return i;
168  }
169  }
170  return -1;
171 }
172 
173 const char* dir_to_command(int dir) {
174  return directions[dir];
175 }
176 
177 void walk_dir(int dir) {
179 }
180 
185 void predict_scroll(int dir) {
186  switch (dir%8) {
187  case 0:
188  want_offset_x += 1;
189  want_offset_y += 1;
190  break;
191  case 1:
192  want_offset_x += 0;
193  want_offset_y += 1;
194  break;
195  case 2:
196  want_offset_x += -1;
197  want_offset_y += 1;
198  break;
199  case 3:
200  want_offset_x += -1;
201  want_offset_y += 0;
202  break;
203  case 4:
204  want_offset_x += -1;
205  want_offset_y += -1;
206  break;
207  case 5:
208  want_offset_x += 0;
209  want_offset_y += -1;
210  break;
211  case 6:
212  want_offset_x += 1;
213  want_offset_y += -1;
214  break;
215  case 7:
216  want_offset_x += 1;
217  want_offset_y += 0;
218  break;
219  }
220 }
221 
222 static bool starts_with(const char *prefix, const char *str) {
223  return strncmp(prefix, str, strlen(prefix)) == 0;
224 }
225 
226 /* This should be used for all 'command' processing. Other functions should
227  * call this so that proper windowing will be done.
228  * command is the text command, repeat is a count value, or -1 if none
229  * is desired and we don't want to reset the current count.
230  * must_send means we must send this command no matter what (ie, it is
231  * an administrative type of command like fire_stop, and failure to send
232  * it will cause definate problems
233  * return 1 if command was sent, 0 if not sent.
234  */
235 int send_command(const char *command, int repeat, int must_send) {
236  static char last_command[MAX_BUF]="";
237 
238  script_monitor(command,repeat,must_send);
239  if (cpl.input_state==Reply_One) {
240  LOG(LOG_ERROR,"common::send_command","Wont send command '%s' - since in reply mode!",
241  command);
242  cpl.count=0;
243  return 0;
244  }
245 
246  if (use_config[CONFIG_ECHO]) {
248  }
249 
250  if (starts_with("afk", command)) {
251  is_afk = !is_afk;
252  }
253  last_command_sent = time(NULL);
254 
255  /* Does the server understand 'ncom'? If so, special code */
256  if (csocket.cs_version >= 1021) {
258 
259  if (commdiff<0) {
260  commdiff +=256;
261  }
262 
263  /* if too many unanswered commands, not a must send, and command is
264  * the same, drop it
265  */
266  if (commdiff>use_config[CONFIG_CWINDOW] && !must_send && !strcmp(command, last_command)) {
267  if (repeat!=-1) {
268  cpl.count=0;
269  }
270  return 0;
271 #if 0 /* Obnoxious warning message we don't need */
272  fprintf(stderr,"Wont send command %s - window oversized %d %d\n",
274 #endif
275  } else {
276  SockList sl;
277  guint8 buf[MAX_BUF];
278 
279  /* Don't want to copy in administrative commands */
280  if (!must_send) {
281  strcpy(last_command, command);
282  }
285 
286  SockList_Init(&sl, buf);
287  SockList_AddString(&sl, "ncom ");
289  SockList_AddInt(&sl, repeat);
290  SockList_AddString(&sl, command);
291  SockList_Send(&sl, csocket.fd);
292  if (profile_latency) {
293  if (profile_time == NULL) {
294  profile_time = calloc(256, sizeof(int64_t));
295  }
296  profile_time[csocket.command_sent] = g_get_monotonic_time();
297  printf("profile/com\t%d\t%s\n", csocket.command_sent, command);
298  }
299 
300  int dir = command_to_direction(command);
302  if (drun == -1 && dir != -1) {
303  // If movement command, predict scroll.
304  predict_scroll(dir);
305  if (must_send != SC_MOVETO) {
306  clear_move_to();
307  }
308  }
309  }
310  } else {
311  cs_print_string(csocket.fd, "command %d %s", repeat,command);
312  }
313  if (repeat!=-1) {
314  cpl.count=0;
315  }
316  return 1;
317 }
318 
319 void CompleteCmd(unsigned char *data, int len) {
320  if (len !=6) {
321  LOG(LOG_ERROR,"common::CompleteCmd","Invalid length %d - ignoring", len);
322  return;
323  }
326  const int in_flight = csocket.command_sent - csocket.command_received;
327  if (profile_latency) {
328  gint64 now = g_get_monotonic_time();
329  if (profile_time != NULL) {
330  printf("profile/comc\t%d\t%" G_GINT64_FORMAT "\t%d\t%d\n",
332  (now - profile_time[csocket.command_received])/1000,
333  csocket.command_time, in_flight);
334  }
335  }
336  int dir = csocket.dir[csocket.command_received];
337  if (drun == -1 && dir != -1) {
338  // Revert prediction when command is acknowledged. Note that we undo
339  // the prediction the same regardless of whether the command succeeded
340  // or not, because the player always ends up in the center of the map.
341  predict_scroll(dir+4);
342  }
343  script_sync(in_flight);
344 }
345 
346 /* This does special processing on the 'take' command. If the
347  * player has a container open, we want to specifiy what object
348  * to move from that since we've sorted it. command is
349  * the command as tped, cpnext is any optional params.
350  */
351 void command_take(const char *command, const char *cpnext) {
352  /* If the player has specified optional data, or the player
353  * does not have a container open, just issue the command
354  * as normal
355  */
356  if (cpnext || cpl.container == NULL) {
357  send_command(command, cpl.count, 0);
358  } else {
359  if (cpl.container->inv == NULL)
361  "There is nothing in the container to move");
362  else
363  cs_print_string(csocket.fd,"move %d %d %d", cpl.ob->tag,
365  }
366 }
run_dir
void run_dir(int dir)
Definition: player.c:150
MSG_TYPE_CLIENT
#define MSG_TYPE_CLIENT
Definition: newclient.h:387
CONFIG_CWINDOW
#define CONFIG_CWINDOW
Definition: client.h:186
item_struct::inv
struct item_struct * inv
Definition: item.h:54
GetShort_String
short GetShort_String(const unsigned char *data)
Definition: newsocket.c:179
client_send_move
void client_send_move(int loc, int tag, int nrof)
Definition: player.c:97
Playing
@ Playing
Definition: client.h:145
SockList_Init
void SockList_Init(SockList *sl, guint8 *buf)
Definition: newsocket.c:32
ClientSocket::command_received
guint16 command_received
Definition: client.h:127
keybindings_init
void keybindings_init(const char *character_name)
Definition: keys.c:532
script_sync
void script_sync(int commdiff)
Definition: script.c:475
SC_FIRERUN
#define SC_FIRERUN
Definition: client.h:94
Player_Struct::input_state
Input_State input_state
Definition: client.h:339
ClientSocket::fd
GSocketConnection * fd
Definition: client.h:124
want_offset_x
int want_offset_x
Definition: mapdata.c:92
external.h
COMMAND_MAX
#define COMMAND_MAX
Definition: client.h:115
SockList_Send
int SockList_Send(SockList *sl, GSocketConnection *c)
Definition: newsocket.c:112
Reply_One
@ Reply_One
Definition: client.h:146
SockList_AddShort
void SockList_AddShort(SockList *sl, guint16 data)
Definition: newsocket.c:64
is_afk
bool is_afk
Definition: player.c:46
ClientSocket::command_time
int command_time
Definition: client.h:132
stop_fire
void stop_fire()
Definition: player.c:108
new_player
void new_player(long tag, char *name, long weight, long face)
Definition: player.c:54
GetInt_String
int GetInt_String(const unsigned char *data)
Definition: newsocket.c:149
item_struct::nrof
guint32 nrof
Definition: item.h:60
clear_fire
void clear_fire()
Definition: player.c:115
SockList_AddInt
void SockList_AddInt(SockList *sl, guint32 data)
Definition: newsocket.c:80
Spell_struct
Definition: client.h:292
Spell_struct::next
struct Spell_struct * next
Definition: client.h:293
mapdata.h
Player_Struct::container
item * container
Definition: client.h:337
MAX_BUF
#define MAX_BUF
Definition: client.h:40
directions
const char *const directions[]
Definition: player.c:41
drun
static int drun
Definition: player.c:106
command_take
void command_take(const char *command, const char *cpnext)
Definition: player.c:351
item_struct::face
gint16 face
Definition: item.h:62
Player_Struct::ob
item * ob
Definition: client.h:334
SockList_AddString
void SockList_AddString(SockList *sl, const char *str)
Definition: newsocket.c:98
SC_MOVETO
#define SC_MOVETO
Definition: client.h:96
want_offset_y
int want_offset_y
Definition: mapdata.c:93
script_monitor
void script_monitor(const char *command, int repeat, int must_send)
Definition: script.c:927
LOG
void LOG(LogLevel level, const char *origin, const char *format,...)
Definition: misc.c:111
cs_print_string
int cs_print_string(GSocketConnection *fd, const char *str,...)
Definition: newsocket.c:252
walk_dir
void walk_dir(int dir)
Definition: player.c:177
dfire
static int dfire
Definition: player.c:106
clear_run
void clear_run()
Definition: player.c:122
draw_ext_info
void draw_ext_info(int orig_color, int type, int subtype, const char *message)
Definition: info.c:915
csocket
ClientSocket csocket
Definition: client.c:70
starts_with
static bool starts_with(const char *prefix, const char *str)
Definition: player.c:222
NDI_BLACK
#define NDI_BLACK
Definition: newclient.h:221
stop_run
void stop_run()
Definition: player.c:145
command_to_direction
int command_to_direction(const char *dir)
Definition: player.c:164
send_command
int send_command(const char *command, int repeat, int must_send)
Definition: player.c:235
item_struct::tag
gint32 tag
Definition: item.h:59
LOG_ERROR
@ LOG_ERROR
Warning that something definitely didn't work.
Definition: client.h:436
predict_scroll
void predict_scroll(int dir)
Definition: player.c:185
client_send_apply
void client_send_apply(int tag)
Definition: player.c:86
dir_to_command
const char * dir_to_command(int dir)
Definition: player.c:173
cpl
Client_Player cpl
Definition: client.c:69
clear_move_to
void clear_move_to()
Definition: mapdata.c:1478
ClientSocket::cs_version
int cs_version
Definition: client.h:125
MSG_TYPE_CLIENT_NOTICE
#define MSG_TYPE_CLIENT_NOTICE
Definition: newclient.h:635
fire_dir
void fire_dir(int dir)
Definition: player.c:129
Player_Struct::count
guint32 count
Definition: client.h:357
script.h
use_config
gint16 use_config[CONFIG_NUMS]
Definition: client.h:242
profile_time
int64_t * profile_time
Definition: player.c:37
look_at
void look_at(int x, int y)
Definition: player.c:82
ClientSocket::dir
gint8 dir[COMMAND_MAX]
Definition: client.h:136
item_struct::d_name
char d_name[NAME_LEN]
Definition: item.h:55
ClientSocket::command_sent
guint16 command_sent
Definition: client.h:127
item_struct::weight
float weight
Definition: item.h:61
CONFIG_ECHO
#define CONFIG_ECHO
Definition: client.h:184
copy_name
#define copy_name(t, f)
Definition: item.h:43
Player_Struct::spelldata
Spell * spelldata
Definition: client.h:348
profile_latency
bool profile_latency
Definition: player.c:36
SockList
Definition: newclient.h:651
SC_NORMAL
#define SC_NORMAL
Definition: client.h:93
last_command_sent
time_t last_command_sent
Definition: player.c:49
client.h
client_send_examine
void client_send_examine(int tag)
Definition: player.c:90
CompleteCmd
void CompleteCmd(unsigned char *data, int len)
Definition: player.c:319