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 
78 void look_at(int x, int y) {
79  cs_print_string(csocket.fd, "lookat %d %d", x, y);
80 }
81 
82 void client_send_apply(int tag) {
83  cs_print_string(csocket.fd, "apply %d", tag);
84 }
85 
86 void client_send_examine(int tag) {
87  cs_print_string(csocket.fd, "examine %d", tag);
88 }
89 
93 void client_send_move(int loc, int tag, int nrof) {
94  cs_print_string(csocket.fd, "move %d %d %d", loc, tag, nrof);
95 }
96 
97 /* Fire & Run code. The server handles repeating of these actions, so
98  * we only need to send a run or fire command for a particular direction
99  * once - we use the drun and dfire to keep track if we need to send
100  * the full command.
101  */
102 static int drun=-1, dfire=-1;
103 
104 void stop_fire() {
105  if (cpl.input_state != Playing) {
106  return;
107  }
108  dfire |= 0x100;
109 }
110 
111 void clear_fire() {
112  if (dfire != -1) {
113  send_command("fire_stop", -1, SC_FIRERUN);
114  dfire = -1;
115  }
116 }
117 
118 void clear_run() {
119  if (drun != -1) {
120  send_command("run_stop", -1, SC_FIRERUN);
121  drun = -1;
122  }
123 }
124 
125 void fire_dir(int dir) {
126  if (cpl.input_state != Playing) {
127  return;
128  }
129  if (dir != dfire) {
130  char buf[MAX_BUF];
131  snprintf(buf, sizeof(buf), "fire %d", dir);
132  if (send_command(buf, cpl.count, SC_NORMAL)) {
133  dfire = dir;
134  cpl.count = 0;
135  }
136  } else {
137  dfire &= 0xff; /* Mark it so that we need a stop_fire */
138  }
139 }
140 
141 void stop_run() {
142  send_command("run_stop", -1, SC_FIRERUN);
143  drun |= 0x100;
144 }
145 
146 void run_dir(int dir) {
147  if (dir != drun) {
148  char buf[MAX_BUF];
149  snprintf(buf, sizeof(buf), "run %d", dir);
150  if (send_command(buf, -1, SC_NORMAL)) {
151  drun = dir;
152  }
153  } else {
154  drun &= 0xff;
155  }
156 }
157 
158 extern const char *const directions[];
159 
160 int command_to_direction(const char *dir) {
161  for (int i = 0; i < 9; i++) {
162  if (!strcmp(dir, directions[i])) {
163  return i;
164  }
165  }
166  return -1;
167 }
168 
169 const char* dir_to_command(int dir) {
170  return directions[dir];
171 }
172 
173 void walk_dir(int dir) {
175 }
176 
181 void predict_scroll(int dir) {
182  switch (dir%8) {
183  case 0:
184  want_offset_x += 1;
185  want_offset_y += 1;
186  break;
187  case 1:
188  want_offset_x += 0;
189  want_offset_y += 1;
190  break;
191  case 2:
192  want_offset_x += -1;
193  want_offset_y += 1;
194  break;
195  case 3:
196  want_offset_x += -1;
197  want_offset_y += 0;
198  break;
199  case 4:
200  want_offset_x += -1;
201  want_offset_y += -1;
202  break;
203  case 5:
204  want_offset_x += 0;
205  want_offset_y += -1;
206  break;
207  case 6:
208  want_offset_x += 1;
209  want_offset_y += -1;
210  break;
211  case 7:
212  want_offset_x += 1;
213  want_offset_y += 0;
214  break;
215  }
216 }
217 
218 static bool starts_with(const char *prefix, const char *str) {
219  return strncmp(prefix, str, strlen(prefix)) == 0;
220 }
221 
222 /* This should be used for all 'command' processing. Other functions should
223  * call this so that proper windowing will be done.
224  * command is the text command, repeat is a count value, or -1 if none
225  * is desired and we don't want to reset the current count.
226  * must_send means we must send this command no matter what (ie, it is
227  * an administrative type of command like fire_stop, and failure to send
228  * it will cause definate problems
229  * return 1 if command was sent, 0 if not sent.
230  */
231 int send_command(const char *command, int repeat, int must_send) {
232  static char last_command[MAX_BUF]="";
233 
234  script_monitor(command,repeat,must_send);
235  if (cpl.input_state==Reply_One) {
236  LOG(LOG_ERROR,"common::send_command","Wont send command '%s' - since in reply mode!",
237  command);
238  cpl.count=0;
239  return 0;
240  }
241 
242  if (use_config[CONFIG_ECHO]) {
244  }
245 
246  if (starts_with("afk", command)) {
247  is_afk = !is_afk;
248  }
249  last_command_sent = time(NULL);
250 
251  /* Does the server understand 'ncom'? If so, special code */
252  if (csocket.cs_version >= 1021) {
254 
255  if (commdiff<0) {
256  commdiff +=256;
257  }
258 
259  /* if too many unanswered commands, not a must send, and command is
260  * the same, drop it
261  */
262  if (commdiff>use_config[CONFIG_CWINDOW] && !must_send && !strcmp(command, last_command)) {
263  if (repeat!=-1) {
264  cpl.count=0;
265  }
266  return 0;
267 #if 0 /* Obnoxious warning message we don't need */
268  fprintf(stderr,"Wont send command %s - window oversized %d %d\n",
270 #endif
271  } else {
272  SockList sl;
273  guint8 buf[MAX_BUF];
274 
275  /* Don't want to copy in administrative commands */
276  if (!must_send) {
277  strcpy(last_command, command);
278  }
281 
282  SockList_Init(&sl, buf);
283  SockList_AddString(&sl, "ncom ");
285  SockList_AddInt(&sl, repeat);
286  SockList_AddString(&sl, command);
287  SockList_Send(&sl, csocket.fd);
288  if (profile_latency) {
289  if (profile_time == NULL) {
290  profile_time = calloc(256, sizeof(int64_t));
291  }
292  profile_time[csocket.command_sent] = g_get_monotonic_time();
293  printf("profile/com\t%d\t%s\n", csocket.command_sent, command);
294  }
295 
296  int dir = command_to_direction(command);
298  if (drun == -1 && dir != -1) {
299  // If movement command, predict scroll.
300  predict_scroll(dir);
301  if (must_send != SC_MOVETO) {
302  clear_move_to();
303  }
304  }
305  }
306  } else {
307  cs_print_string(csocket.fd, "command %d %s", repeat,command);
308  }
309  if (repeat!=-1) {
310  cpl.count=0;
311  }
312  return 1;
313 }
314 
315 void CompleteCmd(unsigned char *data, int len) {
316  if (len !=6) {
317  LOG(LOG_ERROR,"common::CompleteCmd","Invalid length %d - ignoring", len);
318  return;
319  }
322  const int in_flight = csocket.command_sent - csocket.command_received;
323  if (profile_latency) {
324  gint64 now = g_get_monotonic_time();
325  if (profile_time != NULL) {
326  printf("profile/comc\t%d\t%" G_GINT64_FORMAT "\t%d\t%d\n",
328  (now - profile_time[csocket.command_received])/1000,
329  csocket.command_time, in_flight);
330  }
331  }
332  int dir = csocket.dir[csocket.command_received];
333  if (drun == -1 && dir != -1) {
334  // Revert prediction when command is acknowledged. Note that we undo
335  // the prediction the same regardless of whether the command succeeded
336  // or not, because the player always ends up in the center of the map.
337  predict_scroll(dir+4);
338  }
339  script_sync(in_flight);
340 }
341 
342 /* This does special processing on the 'take' command. If the
343  * player has a container open, we want to specifiy what object
344  * to move from that since we've sorted it. command is
345  * the command as tped, cpnext is any optional params.
346  */
347 void command_take(const char *command, const char *cpnext) {
348  /* If the player has specified optional data, or the player
349  * does not have a container open, just issue the command
350  * as normal
351  */
352  if (cpnext || cpl.container == NULL) {
353  send_command(command, cpl.count, 0);
354  } else {
355  if (cpl.container->inv == NULL)
357  "There is nothing in the container to move");
358  else
359  cs_print_string(csocket.fd,"move %d %d %d", cpl.ob->tag,
361  }
362 }
run_dir
void run_dir(int dir)
Definition: player.c:146
MSG_TYPE_CLIENT
#define MSG_TYPE_CLIENT
Client originated Messages.
Definition: newclient.h:390
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:180
client_send_move
void client_send_move(int loc, int tag, int nrof)
Request to move 'nrof' objects with 'tag' to 'loc'.
Definition: player.c:93
Playing
@ Playing
Definition: client.h:145
SockList_Init
void SockList_Init(SockList *sl, guint8 *buf)
Definition: newsocket.c:33
ClientSocket::command_received
guint16 command_received
These are used for the newer 'windowing' method of commands - number of last command sent,...
Definition: client.h:127
keybindings_init
void keybindings_init(const char *character_name)
Reads in the keybindings, and initializes special values.
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
What the input state is.
Definition: client.h:341
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
Largest ncom command number to send before wrapping.
Definition: client.h:115
SockList_Send
int SockList_Send(SockList *sl, GSocketConnection *c)
Send data from a socklist to the socket.
Definition: newsocket.c:113
Reply_One
@ Reply_One
Definition: client.h:146
SockList_AddShort
void SockList_AddShort(SockList *sl, guint16 data)
Definition: newsocket.c:65
is_afk
bool is_afk
Best guess whether or not we are currently AFK or not.
Definition: player.c:46
ClientSocket::command_time
int command_time
Time (in ms) players commands currently take to execute.
Definition: client.h:132
stop_fire
void stop_fire()
Definition: player.c:104
new_player
void new_player(long tag, char *name, long weight, long face)
Initialize player object using information from the server.
Definition: player.c:54
GetInt_String
int GetInt_String(const unsigned char *data)
The reverse of SockList_AddInt, but on strings instead.
Definition: newsocket.c:150
item_struct::nrof
guint32 nrof
Definition: item.h:60
clear_fire
void clear_fire()
Definition: player.c:111
SockList_AddInt
void SockList_AddInt(SockList *sl, guint32 data)
Definition: newsocket.c:81
Spell_struct
Definition: client.h:294
Spell_struct::next
struct Spell_struct * next
Definition: client.h:295
mapdata.h
Player_Struct::container
item * container
open container
Definition: client.h:339
MAX_BUF
#define MAX_BUF
Definition: client.h:40
directions
const char *const directions[]
256-length array to keep track of when commands were sent to the server
Definition: player.c:41
drun
static int drun
Definition: player.c:102
command_take
void command_take(const char *command, const char *cpnext)
Definition: player.c:347
item_struct::face
gint16 face
Definition: item.h:62
Player_Struct::ob
item * ob
Player object.
Definition: client.h:336
SockList_AddString
void SockList_AddString(SockList *sl, const char *str)
Definition: newsocket.c:99
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,...)
Log messages of a certain importance to stderr.
Definition: misc.c:111
cs_print_string
int cs_print_string(GSocketConnection *fd, const char *str,...)
Send a printf-formatted packet to the socket.
Definition: newsocket.c:253
walk_dir
void walk_dir(int dir)
Definition: player.c:173
dfire
static int dfire
Definition: player.c:102
clear_run
void clear_run()
Definition: player.c:118
draw_ext_info
void draw_ext_info(int orig_color, int type, int subtype, const char *message)
A message processor that accepts messages along with meta information color and type.
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:218
NDI_BLACK
#define NDI_BLACK
Definition: newclient.h:224
stop_run
void stop_run()
Definition: player.c:141
command_to_direction
int command_to_direction(const char *dir)
Definition: player.c:160
send_command
int send_command(const char *command, int repeat, int must_send)
Definition: player.c:231
item_struct::tag
gint32 tag
Definition: item.h:59
LOG_ERROR
@ LOG_ERROR
Warning that something definitely didn't work.
Definition: client.h:438
predict_scroll
void predict_scroll(int dir)
Change map drawing offset to provide local movement prediction based on player's movement commands to...
Definition: player.c:181
client_send_apply
void client_send_apply(int tag)
Definition: player.c:82
dir_to_command
const char * dir_to_command(int dir)
Definition: player.c:169
cpl
Client_Player cpl
Player object.
Definition: client.c:69
clear_move_to
void clear_move_to()
Definition: mapdata.c:1497
ClientSocket::cs_version
int cs_version
Definition: client.h:125
MSG_TYPE_CLIENT_NOTICE
#define MSG_TYPE_CLIENT_NOTICE
Non-critical note to player.
Definition: newclient.h:638
fire_dir
void fire_dir(int dir)
Definition: player.c:125
Player_Struct::count
guint32 count
Repeat count on command.
Definition: client.h:359
script.h
use_config
gint16 use_config[CONFIG_NUMS]
Definition: client.h:244
profile_time
int64_t * profile_time
Definition: player.c:37
look_at
void look_at(int x, int y)
Definition: player.c:78
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
List of spells known.
Definition: client.h:350
profile_latency
bool profile_latency
Definition: player.c:36
SockList
Contains the base information we use to make up a packet we want to send.
Definition: newclient.h:654
SC_NORMAL
#define SC_NORMAL
Definition: client.h:93
last_command_sent
time_t last_command_sent
Time when last command was sent.
Definition: player.c:49
client.h
client_send_examine
void client_send_examine(int tag)
Definition: player.c:86
CompleteCmd
void CompleteCmd(unsigned char *data, int len)
Definition: player.c:315