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