1 |
elmex |
1.1 |
/* |
2 |
|
|
CrossFire, A Multiplayer game for X-windows |
3 |
|
|
|
4 |
|
|
Copyright (C) 2002 Mark Wedel & Crossfire Development Team |
5 |
|
|
Copyright (C) 1992 Frank Tore Johansen |
6 |
|
|
|
7 |
|
|
This program is free software; you can redistribute it and/or modify |
8 |
|
|
it under the terms of the GNU General Public License as published by |
9 |
|
|
the Free Software Foundation; either version 2 of the License, or |
10 |
|
|
(at your option) any later version. |
11 |
|
|
|
12 |
|
|
This program is distributed in the hope that it will be useful, |
13 |
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 |
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
15 |
|
|
GNU General Public License for more details. |
16 |
|
|
|
17 |
|
|
You should have received a copy of the GNU General Public License |
18 |
|
|
along with this program; if not, write to the Free Software |
19 |
|
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
20 |
|
|
|
21 |
root |
1.18 |
The authors can be reached via e-mail at <crossfire@schmorp.de> |
22 |
elmex |
1.1 |
*/ |
23 |
|
|
|
24 |
|
|
#include <global.h> |
25 |
|
|
#ifndef __CEXTRACT__ |
26 |
root |
1.15 |
# include <sproto.h> |
27 |
elmex |
1.1 |
#endif |
28 |
|
|
#include <spells.h> |
29 |
|
|
#include <loader.h> |
30 |
|
|
#include <define.h> |
31 |
|
|
|
32 |
|
|
extern void sub_weight (object *, signed long); |
33 |
|
|
extern void add_weight (object *, signed long); |
34 |
|
|
extern long pticks; |
35 |
|
|
|
36 |
|
|
/* If flag is non zero, it means that we want to try and save everyone, but |
37 |
|
|
* keep the game running. Thus, we don't want to free any information. |
38 |
|
|
*/ |
39 |
root |
1.15 |
void |
40 |
|
|
emergency_save (int flag) |
41 |
|
|
{ |
42 |
elmex |
1.1 |
player *pl; |
43 |
root |
1.15 |
|
44 |
elmex |
1.1 |
#ifndef NO_EMERGENCY_SAVE |
45 |
|
|
trying_emergency_save = 1; |
46 |
root |
1.15 |
if (editor) |
47 |
elmex |
1.1 |
return; |
48 |
root |
1.15 |
LOG (llevError, "Emergency save: "); |
49 |
|
|
for (pl = first_player; pl != NULL; pl = pl->next) |
50 |
|
|
{ |
51 |
|
|
if (!pl->ob) |
52 |
|
|
{ |
53 |
|
|
LOG (llevError, "No name, ignoring this.\n"); |
54 |
|
|
continue; |
55 |
|
|
} |
56 |
|
|
LOG (llevError, "%s ", pl->ob->name); |
57 |
|
|
new_draw_info (NDI_UNIQUE, 0, pl->ob, "Emergency save..."); |
58 |
elmex |
1.1 |
|
59 |
|
|
/* If we are not exiting the game (ie, this is sort of a backup save), then |
60 |
|
|
* don't change the location back to the village. Note that there are other |
61 |
|
|
* options to have backup saves be done at the starting village |
62 |
|
|
*/ |
63 |
root |
1.15 |
if (!flag) |
64 |
|
|
{ |
65 |
|
|
strcpy (pl->maplevel, first_map_path); |
66 |
|
|
if (pl->ob->map != NULL) |
67 |
root |
1.6 |
pl->ob->map = NULL; |
68 |
root |
1.15 |
pl->ob->x = -1; |
69 |
|
|
pl->ob->y = -1; |
70 |
|
|
} |
71 |
|
|
if (!save_player (pl->ob, flag)) |
72 |
|
|
{ |
73 |
|
|
LOG (llevError, "(failed) "); |
74 |
|
|
new_draw_info (NDI_UNIQUE, 0, pl->ob, "Emergency save failed, checking score..."); |
75 |
|
|
} |
76 |
|
|
check_score (pl->ob); |
77 |
elmex |
1.1 |
} |
78 |
root |
1.15 |
LOG (llevError, "\n"); |
79 |
elmex |
1.1 |
#else |
80 |
root |
1.15 |
LOG (llevInfo, "Emergency saves disabled, no save attempted\n"); |
81 |
elmex |
1.1 |
#endif |
82 |
|
|
/* If the game is exiting, remove the player locks */ |
83 |
root |
1.15 |
if (!flag) |
84 |
|
|
{ |
85 |
|
|
for (pl = first_player; pl != NULL; pl = pl->next) |
86 |
|
|
{ |
87 |
|
|
if (pl->ob) |
88 |
|
|
{ |
89 |
|
|
} |
90 |
|
|
} |
91 |
elmex |
1.1 |
} |
92 |
|
|
} |
93 |
|
|
|
94 |
|
|
/* Delete character with name. if new is set, also delete the new |
95 |
|
|
* style directory, otherwise, just delete the old style playfile |
96 |
|
|
* (needed for transition) |
97 |
|
|
*/ |
98 |
root |
1.15 |
void |
99 |
|
|
delete_character (const char *name, int newchar) |
100 |
|
|
{ |
101 |
|
|
char buf[MAX_BUF]; |
102 |
elmex |
1.1 |
|
103 |
root |
1.15 |
sprintf (buf, "%s/%s/%s.pl", settings.localdir, settings.playerdir, name); |
104 |
|
|
if (unlink (buf) == -1) |
105 |
|
|
LOG (llevDebug, "Cannot delete character file %s: %s\n", buf, strerror (errno)); |
106 |
|
|
if (newchar) |
107 |
|
|
{ |
108 |
|
|
sprintf (buf, "%s/%s/%s", settings.localdir, settings.playerdir, name); |
109 |
|
|
/* this effectively does an rm -rf on the directory */ |
110 |
|
|
remove_directory (buf); |
111 |
elmex |
1.1 |
} |
112 |
|
|
} |
113 |
|
|
|
114 |
|
|
/* This verify that a character of name exits, and that it matches |
115 |
|
|
* password. It return 0 if there is match, 1 if no such player, |
116 |
|
|
* 2 if incorrect password. |
117 |
|
|
*/ |
118 |
|
|
|
119 |
root |
1.15 |
int |
120 |
|
|
verify_player (const char *name, char *password) |
121 |
elmex |
1.1 |
{ |
122 |
root |
1.15 |
char buf[MAX_BUF]; |
123 |
|
|
int comp; |
124 |
|
|
FILE *fp; |
125 |
|
|
|
126 |
|
|
if (strpbrk (name, "/.\\") != NULL) |
127 |
|
|
{ |
128 |
|
|
LOG (llevError, "Username contains illegal characters: %s\n", name); |
129 |
|
|
return 1; |
130 |
|
|
} |
131 |
|
|
|
132 |
|
|
snprintf (buf, sizeof (buf), "%s/%s/%s/%s.pl", settings.localdir, settings.playerdir, name, name); |
133 |
|
|
if (strlen (buf) >= sizeof (buf) - 1) |
134 |
|
|
{ |
135 |
|
|
LOG (llevError, "Username too long: %s\n", name); |
136 |
|
|
return 1; |
137 |
|
|
} |
138 |
|
|
|
139 |
|
|
if ((fp = open_and_uncompress (buf, 0, &comp)) == NULL) |
140 |
|
|
return 1; |
141 |
|
|
|
142 |
|
|
/* Read in the file until we find the password line. Our logic could |
143 |
|
|
* be a bit better on cleaning up the password from the file, but since |
144 |
|
|
* it is written by the program, I think it is fair to assume that the |
145 |
|
|
* syntax should be pretty standard. |
146 |
|
|
*/ |
147 |
|
|
while (fgets (buf, MAX_BUF - 1, fp) != NULL) |
148 |
|
|
{ |
149 |
|
|
if (!strncmp (buf, "password ", 9)) |
150 |
|
|
{ |
151 |
|
|
buf[strlen (buf) - 1] = 0; /* remove newline */ |
152 |
|
|
if (check_password (password, buf + 9)) |
153 |
|
|
{ |
154 |
|
|
close_and_delete (fp, comp); |
155 |
|
|
return 0; |
156 |
root |
1.6 |
} |
157 |
root |
1.15 |
else |
158 |
|
|
{ |
159 |
|
|
close_and_delete (fp, comp); |
160 |
|
|
return 2; |
161 |
root |
1.6 |
} |
162 |
|
|
} |
163 |
elmex |
1.1 |
} |
164 |
root |
1.15 |
LOG (llevDebug, "Could not find a password line in player %s\n", name); |
165 |
|
|
close_and_delete (fp, comp); |
166 |
|
|
return 1; |
167 |
elmex |
1.1 |
} |
168 |
|
|
|
169 |
|
|
/* Checks to see if anyone else by 'name' is currently playing. |
170 |
|
|
* If we find that file or another character of some name is already in the |
171 |
|
|
* game, we don't let this person join (we should really let the new player |
172 |
|
|
* enter the password, and if correct, disconnect that socket and attach it to |
173 |
|
|
* the players current session. |
174 |
|
|
* If no one by that name is currently playing, we then make sure the name |
175 |
|
|
* doesn't include any bogus characters. |
176 |
|
|
* We return 0 if the name is in use/bad, 1 if it is OK to use this name. |
177 |
|
|
*/ |
178 |
|
|
|
179 |
root |
1.15 |
int |
180 |
|
|
check_name (player *me, const char *name) |
181 |
|
|
{ |
182 |
|
|
player *pl; |
183 |
elmex |
1.1 |
|
184 |
root |
1.15 |
for (pl = first_player; pl != NULL; pl = pl->next) |
185 |
|
|
if (pl != me && pl->ob->name != NULL && !strcmp (pl->ob->name, name)) |
186 |
|
|
{ |
187 |
|
|
new_draw_info (NDI_UNIQUE, 0, me->ob, "That name is already in use."); |
188 |
|
|
return 0; |
189 |
|
|
} |
190 |
elmex |
1.1 |
|
191 |
root |
1.15 |
if (*name == '\0') |
192 |
|
|
{ |
193 |
|
|
new_draw_info (NDI_UNIQUE, 0, me->ob, "Null names are not allowed."); |
194 |
|
|
return 0; |
195 |
elmex |
1.1 |
} |
196 |
|
|
|
197 |
root |
1.15 |
if (!playername_ok (name)) |
198 |
|
|
{ |
199 |
|
|
new_draw_info (NDI_UNIQUE, 0, me->ob, "That name contains illegal characters."); |
200 |
|
|
return 0; |
201 |
elmex |
1.1 |
} |
202 |
root |
1.15 |
if (strlen (name) >= MAX_NAME) |
203 |
|
|
{ |
204 |
|
|
new_draw_info (NDI_UNIQUE, 0, me->ob, "That name is too long."); |
205 |
|
|
return 0; |
206 |
elmex |
1.1 |
} |
207 |
|
|
|
208 |
root |
1.15 |
return 1; |
209 |
elmex |
1.1 |
} |
210 |
|
|
|
211 |
root |
1.15 |
int |
212 |
|
|
create_savedir_if_needed (char *savedir) |
213 |
elmex |
1.1 |
{ |
214 |
|
|
struct stat *buf; |
215 |
|
|
|
216 |
root |
1.15 |
if ((buf = (struct stat *) malloc (sizeof (struct stat))) == NULL) |
217 |
|
|
{ |
218 |
|
|
LOG (llevError, "Unable to save playerfile... out of memory.\n"); |
219 |
|
|
return 0; |
220 |
|
|
} |
221 |
|
|
else |
222 |
|
|
{ |
223 |
|
|
stat (savedir, buf); |
224 |
|
|
if (!S_ISDIR (buf->st_mode)) |
225 |
|
|
if (mkdir (savedir, SAVE_DIR_MODE)) |
226 |
|
|
{ |
227 |
|
|
LOG (llevError, "Unable to create player savedir %s: %s\n", savedir, strerror (errno)); |
228 |
|
|
return 0; |
229 |
|
|
} |
230 |
|
|
free (buf); |
231 |
|
|
} |
232 |
|
|
return 1; |
233 |
elmex |
1.1 |
} |
234 |
|
|
|
235 |
|
|
/* |
236 |
|
|
* If flag is set, it's only backup, ie dont remove objects from inventory |
237 |
|
|
* If BACKUP_SAVE_AT_HOME is set, and the flag is set, then the player |
238 |
|
|
* will be saved at the emergency save location. |
239 |
|
|
* Returns non zero if successful. |
240 |
|
|
*/ |
241 |
|
|
|
242 |
root |
1.15 |
int |
243 |
|
|
save_player (object *op, int flag) |
244 |
|
|
{ |
245 |
|
|
object *tmp, *container = NULL; |
246 |
elmex |
1.1 |
player *pl = op->contr; |
247 |
root |
1.15 |
int i, wiz = QUERY_FLAG (op, FLAG_WIZ); |
248 |
|
|
|
249 |
elmex |
1.1 |
#ifdef BACKUP_SAVE_AT_HOME |
250 |
|
|
sint16 backup_x, backup_y; |
251 |
|
|
#endif |
252 |
|
|
|
253 |
root |
1.15 |
if (!op->stats.exp) |
254 |
|
|
return 0; /* no experience, no save */ |
255 |
elmex |
1.1 |
|
256 |
root |
1.15 |
flag &= 1; |
257 |
elmex |
1.1 |
|
258 |
root |
1.15 |
if (!pl->name_changed || (!flag && !op->stats.exp)) |
259 |
|
|
{ |
260 |
|
|
if (!flag) |
261 |
|
|
{ |
262 |
|
|
new_draw_info (NDI_UNIQUE, 0, op, "Your game is not valid,"); |
263 |
|
|
new_draw_info (NDI_UNIQUE, 0, op, "Game not saved."); |
264 |
|
|
} |
265 |
|
|
return 0; |
266 |
elmex |
1.1 |
} |
267 |
root |
1.15 |
|
268 |
|
|
/* Sanity check - some stuff changes this when player is exiting */ |
269 |
|
|
if (op->type != PLAYER) |
270 |
elmex |
1.1 |
return 0; |
271 |
|
|
|
272 |
root |
1.15 |
/* Prevent accidental saves if connection is reset after player has |
273 |
|
|
* mostly exited. |
274 |
|
|
*/ |
275 |
|
|
if (pl->state != ST_PLAYING && pl->state != ST_GET_PARTY_PASSWORD) |
276 |
|
|
return 0; |
277 |
elmex |
1.1 |
|
278 |
root |
1.9 |
INVOKE_PLAYER (SAVE, op->contr); |
279 |
root |
1.5 |
|
280 |
elmex |
1.1 |
if (flag == 0) |
281 |
root |
1.15 |
terminate_all_pets (op); |
282 |
elmex |
1.1 |
|
283 |
root |
1.9 |
object_freezer freezer; |
284 |
root |
1.3 |
|
285 |
root |
1.9 |
/* Eneq(@csd.uu.se): If we have an open container hide it. */ |
286 |
|
|
if (op->container) |
287 |
root |
1.5 |
{ |
288 |
root |
1.9 |
container = op->container; |
289 |
|
|
op->container = NULL; |
290 |
root |
1.5 |
} |
291 |
elmex |
1.1 |
|
292 |
root |
1.15 |
fprintf (freezer, "password %s\n", pl->password); |
293 |
root |
1.3 |
|
294 |
elmex |
1.1 |
if (settings.set_title == TRUE) |
295 |
root |
1.15 |
if (pl->own_title[0] != '\0') |
296 |
|
|
fprintf (freezer, "title %s\n", pl->own_title); |
297 |
elmex |
1.1 |
|
298 |
root |
1.15 |
fprintf (freezer, "explore %d\n", pl->explore); |
299 |
|
|
fprintf (freezer, "gen_hp %d\n", pl->gen_hp); |
300 |
|
|
fprintf (freezer, "gen_sp %d\n", pl->gen_sp); |
301 |
|
|
fprintf (freezer, "gen_grace %d\n", pl->gen_grace); |
302 |
|
|
fprintf (freezer, "listening %d\n", pl->listening); |
303 |
|
|
fprintf (freezer, "shoottype %d\n", pl->shoottype); |
304 |
|
|
fprintf (freezer, "bowtype %d\n", pl->bowtype); |
305 |
|
|
fprintf (freezer, "petmode %d\n", pl->petmode); |
306 |
|
|
fprintf (freezer, "peaceful %d\n", pl->peaceful); |
307 |
|
|
fprintf (freezer, "digestion %d\n", pl->digestion); |
308 |
|
|
fprintf (freezer, "pickup %d\n", pl->mode); |
309 |
|
|
fprintf (freezer, "outputs_sync %d\n", pl->outputs_sync); |
310 |
|
|
fprintf (freezer, "outputs_count %d\n", pl->outputs_count); |
311 |
elmex |
1.1 |
/* Match the enumerations but in string form */ |
312 |
root |
1.15 |
fprintf (freezer, "usekeys %s\n", pl->usekeys == key_inventory ? "key_inventory" : (pl->usekeys == keyrings ? "keyrings" : "containers")); |
313 |
elmex |
1.1 |
/* Match the enumerations but in string form */ |
314 |
root |
1.15 |
fprintf (freezer, "unapply %s\n", pl->unapply == unapply_nochoice ? "unapply_nochoice" : |
315 |
|
|
(pl->unapply == unapply_never ? "unapply_never" : "unapply_always")); |
316 |
elmex |
1.1 |
|
317 |
|
|
#ifdef BACKUP_SAVE_AT_HOME |
318 |
root |
1.26 |
if (op->map && flag == 0) |
319 |
elmex |
1.1 |
#else |
320 |
root |
1.26 |
if (op->map) |
321 |
elmex |
1.1 |
#endif |
322 |
root |
1.15 |
fprintf (freezer, "map %s\n", op->map->path); |
323 |
elmex |
1.1 |
else |
324 |
root |
1.15 |
fprintf (freezer, "map %s\n", settings.emergency_mapname); |
325 |
|
|
|
326 |
|
|
fprintf (freezer, "savebed_map %s\n", pl->savebed_map); |
327 |
|
|
fprintf (freezer, "bed_x %d\nbed_y %d\n", pl->bed_x, pl->bed_y); |
328 |
|
|
fprintf (freezer, "weapon_sp %f\n", pl->weapon_sp); |
329 |
|
|
fprintf (freezer, "Str %d\n", pl->orig_stats.Str); |
330 |
|
|
fprintf (freezer, "Dex %d\n", pl->orig_stats.Dex); |
331 |
|
|
fprintf (freezer, "Con %d\n", pl->orig_stats.Con); |
332 |
|
|
fprintf (freezer, "Int %d\n", pl->orig_stats.Int); |
333 |
|
|
fprintf (freezer, "Pow %d\n", pl->orig_stats.Pow); |
334 |
|
|
fprintf (freezer, "Wis %d\n", pl->orig_stats.Wis); |
335 |
|
|
fprintf (freezer, "Cha %d\n", pl->orig_stats.Cha); |
336 |
|
|
|
337 |
|
|
fprintf (freezer, "lev_array %d\n", op->level > 10 ? 10 : op->level); |
338 |
|
|
for (i = 1; i <= pl->last_level && i <= 10; i++) |
339 |
|
|
{ |
340 |
|
|
fprintf (freezer, "%d\n", pl->levhp[i]); |
341 |
|
|
fprintf (freezer, "%d\n", pl->levsp[i]); |
342 |
|
|
fprintf (freezer, "%d\n", pl->levgrace[i]); |
343 |
|
|
} |
344 |
root |
1.4 |
|
345 |
root |
1.9 |
freezer.put (op->contr); |
346 |
root |
1.4 |
|
347 |
root |
1.15 |
fprintf (freezer, "endplst\n"); |
348 |
elmex |
1.1 |
|
349 |
root |
1.15 |
SET_FLAG (op, FLAG_NO_FIX_PLAYER); |
350 |
|
|
CLEAR_FLAG (op, FLAG_WIZ); |
351 |
elmex |
1.1 |
#ifdef BACKUP_SAVE_AT_HOME |
352 |
root |
1.15 |
if (flag) |
353 |
|
|
{ |
354 |
|
|
backup_x = op->x; |
355 |
|
|
backup_y = op->y; |
356 |
|
|
op->x = -1; |
357 |
|
|
op->y = -1; |
358 |
|
|
} |
359 |
elmex |
1.1 |
/* Save objects, but not unpaid objects. Don't remove objects from |
360 |
|
|
* inventory. |
361 |
|
|
*/ |
362 |
root |
1.15 |
save_object (freezer, op, 2); |
363 |
|
|
if (flag) |
364 |
|
|
{ |
365 |
|
|
op->x = backup_x; |
366 |
|
|
op->y = backup_y; |
367 |
|
|
} |
368 |
elmex |
1.1 |
#else |
369 |
root |
1.15 |
save_object (freezer, op, 3); /* don't check and don't remove */ |
370 |
elmex |
1.1 |
#endif |
371 |
|
|
|
372 |
root |
1.9 |
char filename[MAX_BUF]; |
373 |
root |
1.15 |
|
374 |
|
|
sprintf (filename, "%s/%s/%s/%s.pl", settings.localdir, settings.playerdir, &op->name, &op->name); |
375 |
|
|
make_path_to_file (filename); |
376 |
root |
1.9 |
freezer.save (filename); |
377 |
|
|
|
378 |
root |
1.15 |
CLEAR_FLAG (op, FLAG_NO_FIX_PLAYER); |
379 |
elmex |
1.1 |
|
380 |
root |
1.15 |
if (!flag) |
381 |
|
|
while ((tmp = op->inv)) |
382 |
root |
1.22 |
tmp->destroy (1); |
383 |
elmex |
1.1 |
|
384 |
|
|
/* Eneq(@csd.uu.se): Reveal the container if we have one. */ |
385 |
root |
1.15 |
if (flag && container != NULL) |
386 |
elmex |
1.1 |
op->container = container; |
387 |
|
|
|
388 |
root |
1.15 |
if (wiz) |
389 |
|
|
SET_FLAG (op, FLAG_WIZ); |
390 |
root |
1.5 |
|
391 |
root |
1.15 |
if (!flag) |
392 |
|
|
esrv_send_inventory (op, op); |
393 |
elmex |
1.1 |
|
394 |
|
|
return 1; |
395 |
|
|
} |
396 |
|
|
|
397 |
root |
1.15 |
void |
398 |
|
|
check_login (object *op) |
399 |
|
|
{ |
400 |
|
|
char filename[MAX_BUF]; |
401 |
|
|
char buf[MAX_BUF], bufall[MAX_BUF]; |
402 |
|
|
int i, value; |
403 |
|
|
player *pl = op->contr; |
404 |
|
|
int correct = 0; |
405 |
|
|
|
406 |
|
|
strcpy (pl->maplevel, first_map_path); |
407 |
|
|
sprintf (filename, "%s/%s/%s/%s.pl", settings.localdir, settings.playerdir, &op->name, &op->name); |
408 |
|
|
|
409 |
|
|
object_thawer thawer (filename); |
410 |
|
|
|
411 |
|
|
/* If no file, must be a new player, so lets get confirmation of |
412 |
|
|
* the password. Return control to the higher level dispatch, |
413 |
|
|
* since the rest of this just deals with loading of the file. |
414 |
|
|
*/ |
415 |
|
|
if (!thawer) |
416 |
|
|
{ |
417 |
|
|
confirm_password (op); |
418 |
|
|
return; |
419 |
elmex |
1.1 |
} |
420 |
root |
1.3 |
|
421 |
root |
1.15 |
if (fgets (bufall, MAX_BUF, thawer) != NULL) |
422 |
|
|
{ |
423 |
|
|
if (sscanf (bufall, "password %s\n", buf)) |
424 |
|
|
{ |
425 |
|
|
/* New password scheme: */ |
426 |
|
|
correct = check_password (pl->write_buf + 1, buf); |
427 |
root |
1.6 |
} |
428 |
root |
1.15 |
} |
429 |
root |
1.16 |
|
430 |
root |
1.15 |
if (!correct) |
431 |
|
|
{ |
432 |
|
|
new_draw_info (NDI_UNIQUE, 0, op, " "); |
433 |
|
|
new_draw_info (NDI_UNIQUE, 0, op, "Wrong Password!"); |
434 |
|
|
new_draw_info (NDI_UNIQUE, 0, op, " "); |
435 |
|
|
op->name = op->name_pl = "noname"; |
436 |
root |
1.24 |
op->contr->socket->password_fails++; |
437 |
|
|
if (op->contr->socket->password_fails >= MAX_PASSWORD_FAILURES) |
438 |
root |
1.15 |
{ |
439 |
|
|
new_draw_info (NDI_UNIQUE, 0, op, "You gave an incorrect password too many times, you will now be dropped from the server."); |
440 |
root |
1.24 |
LOG (llevInfo, "A player connecting from %s has been dropped for password failure\n", op->contr->socket->host); |
441 |
root |
1.28 |
op->contr->socket->destroy (); /* the socket loop should handle the rest for us */ |
442 |
root |
1.15 |
} |
443 |
|
|
else |
444 |
|
|
get_name (op); |
445 |
root |
1.19 |
|
446 |
root |
1.15 |
return; /* Once again, rest of code just loads the char */ |
447 |
elmex |
1.1 |
} |
448 |
|
|
|
449 |
|
|
#ifdef SAVE_INTERVAL |
450 |
root |
1.15 |
pl->last_save_time = time (NULL); |
451 |
elmex |
1.1 |
#endif /* SAVE_INTERVAL */ |
452 |
root |
1.28 |
pl->party = 0; |
453 |
|
|
|
454 |
root |
1.15 |
if (settings.search_items == TRUE) |
455 |
|
|
pl->search_str[0] = '\0'; |
456 |
root |
1.28 |
|
457 |
root |
1.15 |
pl->name_changed = 1; |
458 |
|
|
pl->orig_stats.Str = 0; |
459 |
|
|
pl->orig_stats.Dex = 0; |
460 |
|
|
pl->orig_stats.Con = 0; |
461 |
|
|
pl->orig_stats.Int = 0; |
462 |
|
|
pl->orig_stats.Pow = 0; |
463 |
|
|
pl->orig_stats.Wis = 0; |
464 |
|
|
pl->orig_stats.Cha = 0; |
465 |
|
|
strcpy (pl->savebed_map, first_map_path); |
466 |
|
|
pl->bed_x = 0, pl->bed_y = 0; |
467 |
|
|
pl->spellparam[0] = '\0'; |
468 |
|
|
|
469 |
|
|
/* Loop through the file, loading the rest of the values */ |
470 |
|
|
while (fgets (bufall, MAX_BUF, thawer) != NULL) |
471 |
|
|
{ |
472 |
|
|
sscanf (bufall, "%s %d\n", buf, &value); |
473 |
|
|
if (!strcmp (buf, "endplst")) |
474 |
|
|
break; |
475 |
|
|
else if (!strcmp (buf, "oid")) |
476 |
|
|
thawer.get (pl, value); |
477 |
|
|
else if (!strcmp (buf, "title") && settings.set_title == TRUE) |
478 |
|
|
sscanf (bufall, "title %[^\n]", pl->own_title); |
479 |
|
|
else if (!strcmp (buf, "explore")) |
480 |
|
|
pl->explore = value; |
481 |
|
|
else if (!strcmp (buf, "gen_hp")) |
482 |
|
|
pl->gen_hp = value; |
483 |
|
|
else if (!strcmp (buf, "shoottype")) |
484 |
|
|
pl->shoottype = (rangetype) value; |
485 |
|
|
else if (!strcmp (buf, "bowtype")) |
486 |
|
|
pl->bowtype = (bowtype_t) value; |
487 |
|
|
else if (!strcmp (buf, "petmode")) |
488 |
|
|
pl->petmode = (petmode_t) value; |
489 |
|
|
else if (!strcmp (buf, "gen_sp")) |
490 |
|
|
pl->gen_sp = value; |
491 |
|
|
else if (!strcmp (buf, "gen_grace")) |
492 |
|
|
pl->gen_grace = value; |
493 |
|
|
else if (!strcmp (buf, "listening")) |
494 |
|
|
pl->listening = value; |
495 |
|
|
else if (!strcmp (buf, "peaceful")) |
496 |
|
|
pl->peaceful = value; |
497 |
|
|
else if (!strcmp (buf, "digestion")) |
498 |
|
|
pl->digestion = value; |
499 |
|
|
else if (!strcmp (buf, "pickup")) |
500 |
|
|
pl->mode = value; |
501 |
|
|
else if (!strcmp (buf, "outputs_sync")) |
502 |
|
|
pl->outputs_sync = value; |
503 |
|
|
else if (!strcmp (buf, "outputs_count")) |
504 |
|
|
pl->outputs_count = value; |
505 |
|
|
else if (!strcmp (buf, "map")) |
506 |
|
|
sscanf (bufall, "map %s", pl->maplevel); |
507 |
|
|
else if (!strcmp (buf, "savebed_map")) |
508 |
|
|
sscanf (bufall, "savebed_map %s", pl->savebed_map); |
509 |
|
|
else if (!strcmp (buf, "bed_x")) |
510 |
|
|
pl->bed_x = value; |
511 |
|
|
else if (!strcmp (buf, "bed_y")) |
512 |
|
|
pl->bed_y = value; |
513 |
|
|
else if (!strcmp (buf, "weapon_sp")) |
514 |
|
|
sscanf (buf, "weapon_sp %f", &pl->weapon_sp); |
515 |
|
|
else if (!strcmp (buf, "Str")) |
516 |
|
|
pl->orig_stats.Str = value; |
517 |
|
|
else if (!strcmp (buf, "Dex")) |
518 |
|
|
pl->orig_stats.Dex = value; |
519 |
|
|
else if (!strcmp (buf, "Con")) |
520 |
|
|
pl->orig_stats.Con = value; |
521 |
|
|
else if (!strcmp (buf, "Int")) |
522 |
|
|
pl->orig_stats.Int = value; |
523 |
|
|
else if (!strcmp (buf, "Pow")) |
524 |
|
|
pl->orig_stats.Pow = value; |
525 |
|
|
else if (!strcmp (buf, "Wis")) |
526 |
|
|
pl->orig_stats.Wis = value; |
527 |
|
|
else if (!strcmp (buf, "Cha")) |
528 |
|
|
pl->orig_stats.Cha = value; |
529 |
|
|
else if (!strcmp (buf, "usekeys")) |
530 |
|
|
{ |
531 |
|
|
if (!strcmp (bufall + 8, "key_inventory\n")) |
532 |
|
|
pl->usekeys = key_inventory; |
533 |
|
|
else if (!strcmp (bufall + 8, "keyrings\n")) |
534 |
|
|
pl->usekeys = keyrings; |
535 |
|
|
else if (!strcmp (bufall + 8, "containers\n")) |
536 |
|
|
pl->usekeys = containers; |
537 |
|
|
else |
538 |
|
|
LOG (llevDebug, "load_player: got unknown usekeys type: %s\n", bufall + 8); |
539 |
root |
1.6 |
} |
540 |
root |
1.15 |
else if (!strcmp (buf, "unapply")) |
541 |
|
|
{ |
542 |
|
|
if (!strcmp (bufall + 8, "unapply_nochoice\n")) |
543 |
|
|
pl->unapply = unapply_nochoice; |
544 |
|
|
else if (!strcmp (bufall + 8, "unapply_never\n")) |
545 |
|
|
pl->unapply = unapply_never; |
546 |
|
|
else if (!strcmp (bufall + 8, "unapply_always\n")) |
547 |
|
|
pl->unapply = unapply_always; |
548 |
|
|
else |
549 |
|
|
LOG (llevDebug, "load_player: got unknown unapply type: %s\n", bufall + 8); |
550 |
|
|
} |
551 |
|
|
else if (!strcmp (buf, "lev_array")) |
552 |
|
|
{ |
553 |
|
|
for (i = 1; i <= value; i++) |
554 |
|
|
{ |
555 |
|
|
char line[128]; |
556 |
|
|
|
557 |
|
|
fgets (line, 128, thawer); |
558 |
|
|
pl->levhp[i] = atoi (line); |
559 |
|
|
fgets (line, 128, thawer); |
560 |
|
|
pl->levsp[i] = atoi (line); |
561 |
|
|
fgets (line, 128, thawer); |
562 |
|
|
pl->levgrace[i] = atoi (line); |
563 |
|
|
} |
564 |
|
|
/* spell_array code removed - don't know when that was last used. |
565 |
|
|
* Even the load code below will someday be replaced by spells being |
566 |
|
|
* objects. |
567 |
|
|
*/ |
568 |
|
|
} |
569 |
|
|
else if (!strcmp (buf, "known_spell")) |
570 |
|
|
{ |
571 |
|
|
#if 0 |
572 |
|
|
/* Logic is left here in case someone wants to try |
573 |
|
|
* and write code to update to spell objects. |
574 |
|
|
*/ |
575 |
|
|
char *cp = strchr (bufall, '\n'); |
576 |
|
|
|
577 |
|
|
*cp = '\0'; |
578 |
|
|
cp = strchr (bufall, ' '); |
579 |
|
|
cp++; |
580 |
|
|
for (i = 0; i < NROFREALSPELLS; i++) |
581 |
|
|
if (!strcmp (spells[i].name, cp)) |
582 |
root |
1.11 |
{ |
583 |
root |
1.15 |
pl->known_spells[pl->nrofknownspells++] = i; |
584 |
|
|
break; |
585 |
root |
1.11 |
} |
586 |
root |
1.15 |
if (i == NROFREALSPELLS) |
587 |
|
|
LOG (llevDebug, "Error: unknown spell (%s)\n", cp); |
588 |
elmex |
1.1 |
#endif |
589 |
root |
1.6 |
} |
590 |
root |
1.15 |
} /* End of loop loading the character file */ |
591 |
root |
1.19 |
|
592 |
root |
1.15 |
leave_map (op); |
593 |
|
|
op->speed = 0; |
594 |
|
|
update_ob_speed (op); |
595 |
|
|
|
596 |
root |
1.23 |
op->clear (); |
597 |
root |
1.15 |
|
598 |
|
|
op->contr = pl; |
599 |
|
|
pl->ob = op; |
600 |
|
|
|
601 |
|
|
/* this loads the standard objects values. */ |
602 |
|
|
load_object (thawer, op, 0); |
603 |
|
|
|
604 |
|
|
CLEAR_FLAG (op, FLAG_NO_FIX_PLAYER); |
605 |
|
|
|
606 |
root |
1.17 |
assign (pl->title, op->arch->clone.name); |
607 |
root |
1.15 |
|
608 |
|
|
/* If the map where the person was last saved does not exist, |
609 |
|
|
* restart them on their home-savebed. This is good for when |
610 |
|
|
* maps change between versions |
611 |
|
|
* First, we check for partial path, then check to see if the full |
612 |
|
|
* path (for unique player maps) |
613 |
|
|
*/ |
614 |
root |
1.26 |
if (!has_been_loaded (pl->maplevel) |
615 |
|
|
&& check_path (pl->maplevel, 1) == -1 |
616 |
|
|
&& check_path (pl->maplevel, 0) == -1) |
617 |
root |
1.15 |
{ |
618 |
root |
1.26 |
strcpy (pl->maplevel, pl->savebed_map); |
619 |
|
|
op->x = pl->bed_x, op->y = pl->bed_y; |
620 |
elmex |
1.1 |
} |
621 |
|
|
|
622 |
root |
1.15 |
/* make sure he's a player--needed because of class change. */ |
623 |
|
|
op->type = PLAYER; |
624 |
elmex |
1.1 |
|
625 |
root |
1.15 |
enter_exit (op, NULL); |
626 |
elmex |
1.1 |
|
627 |
root |
1.15 |
pl->name_changed = 1; |
628 |
|
|
pl->state = ST_PLAYING; |
629 |
elmex |
1.1 |
#ifdef AUTOSAVE |
630 |
root |
1.15 |
pl->last_save_tick = pticks; |
631 |
elmex |
1.1 |
#endif |
632 |
root |
1.15 |
op->carrying = sum_weight (op); |
633 |
|
|
/* Need to call fix_player now - program modified so that it is not |
634 |
|
|
* called during the load process (FLAG_NO_FIX_PLAYER set when |
635 |
|
|
* saved) |
636 |
|
|
* Moved ahead of the esrv functions, so proper weights will be |
637 |
|
|
* sent to the client. |
638 |
|
|
*/ |
639 |
|
|
link_player_skills (op); |
640 |
|
|
|
641 |
|
|
if (!legal_range (op, op->contr->shoottype)) |
642 |
|
|
op->contr->shoottype = range_none; |
643 |
|
|
|
644 |
|
|
fix_player (op); |
645 |
|
|
|
646 |
|
|
/* if it's a dragon player, set the correct title here */ |
647 |
|
|
if (is_dragon_pl (op) && op->inv != NULL) |
648 |
|
|
{ |
649 |
|
|
object *tmp, *abil = NULL, *skin = NULL; |
650 |
|
|
|
651 |
|
|
for (tmp = op->inv; tmp != NULL; tmp = tmp->below) |
652 |
|
|
{ |
653 |
|
|
if (tmp->type == FORCE) |
654 |
|
|
{ |
655 |
|
|
if (strcmp (tmp->arch->name, "dragon_ability_force") == 0) |
656 |
|
|
abil = tmp; |
657 |
|
|
else if (strcmp (tmp->arch->name, "dragon_skin_force") == 0) |
658 |
|
|
skin = tmp; |
659 |
root |
1.6 |
} |
660 |
|
|
} |
661 |
root |
1.15 |
set_dragon_name (op, abil, skin); |
662 |
|
|
} |
663 |
|
|
|
664 |
|
|
new_draw_info (NDI_UNIQUE, 0, op, "Welcome Back!"); |
665 |
|
|
new_draw_info_format (NDI_UNIQUE | NDI_ALL | NDI_DK_ORANGE, 5, NULL, "%s has entered the game.", &pl->ob->name); |
666 |
|
|
|
667 |
|
|
INVOKE_PLAYER (LOAD, pl, ARG_STRING (filename)); |
668 |
|
|
INVOKE_PLAYER (LOGIN, pl); |
669 |
|
|
|
670 |
root |
1.27 |
op->contr->socket->floorbox_update (); |
671 |
root |
1.24 |
|
672 |
root |
1.15 |
/* If the player should be dead, call kill_player for them |
673 |
|
|
* Only check for hp - if player lacks food, let the normal |
674 |
|
|
* logic for that to take place. If player is permanently |
675 |
|
|
* dead, and not using permadeath mode, the kill_player will |
676 |
|
|
* set the play_again flag, so return. |
677 |
|
|
*/ |
678 |
|
|
if (op->stats.hp < 0) |
679 |
|
|
{ |
680 |
|
|
new_draw_info (NDI_UNIQUE, 0, op, "Your character was dead last your played."); |
681 |
|
|
kill_player (op); |
682 |
|
|
if (pl->state != ST_PLAYING) |
683 |
|
|
return; |
684 |
elmex |
1.1 |
} |
685 |
root |
1.24 |
|
686 |
|
|
LOG (llevInfo, "LOGIN: Player named %s from ip %s\n", &op->name, op->contr->socket->host); |
687 |
root |
1.15 |
|
688 |
|
|
/* Do this after checking for death - no reason sucking up bandwidth if |
689 |
|
|
* the data isn't needed. |
690 |
|
|
*/ |
691 |
|
|
esrv_new_player (op->contr, op->weight + op->carrying); |
692 |
|
|
esrv_send_inventory (op, op); |
693 |
|
|
esrv_add_spells (op->contr, NULL); |
694 |
|
|
|
695 |
|
|
CLEAR_FLAG (op, FLAG_FRIENDLY); |
696 |
|
|
|
697 |
|
|
/* can_use_shield is a new flag. However, the can_use.. seems to largely come |
698 |
|
|
* from the class, and not race. I don't see any way to get the class information |
699 |
|
|
* to then update this. I don't think this will actually break anything - anyone |
700 |
|
|
* that can use armour should be able to use a shield. What this may 'break' |
701 |
|
|
* are features new characters get, eg, if someone starts up with a Q, they |
702 |
|
|
* should be able to use a shield. However, old Q's won't get that advantage. |
703 |
|
|
*/ |
704 |
|
|
if (QUERY_FLAG (op, FLAG_USE_ARMOUR)) |
705 |
|
|
SET_FLAG (op, FLAG_USE_SHIELD); |
706 |
root |
1.20 |
|
707 |
root |
1.15 |
return; |
708 |
elmex |
1.1 |
} |