ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/server/main.C
Revision: 1.100
Committed: Mon Apr 30 04:25:30 2007 UTC (17 years ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.99: +1 -1 lines
Log Message:
This is the first rough cut of the skill use system (use the STABLE tag).

Details will likely change, and combat skills do not work very well, but
it works quite well.

Players no longer have a shoottype or range slots, instead, each player
has these members:

   combat_skill/combat_ob  the currently selected skill (and weapon)
                           for direct attacks.
   ranged_skill/ranged_ob  the currently selected ranged skill (and
                           bow/spell/item)
   golem                   the currently-controlled golem, if any.

File Contents

# Content
1 /*
2 * CrossFire, A Multiplayer game
3 *
4 * Copyright (C) 2005, 2006, 2007 Marc Lehmann & Crossfire+ Development Team
5 * Copyright (C) 2001-2003 Mark Wedel & Crossfire Development Team
6 * Copyright (C) 1992 Frank Tore Johansen
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 *
22 * The authors can be reached via e-mail at <crossfire@schmorp.de>
23 */
24
25 #include <global.h>
26 #include <object.h>
27 #include <tod.h>
28
29 #include <sproto.h>
30 #include <time.h>
31
32 #include <../random_maps/random_map.h>
33 #include <../random_maps/rproto.h>
34 #include "path.h"
35
36 static char days[7][4] = {
37 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
38 };
39
40 void
41 version (object *op)
42 {
43 if (op)
44 clear_win_info (op);
45
46 new_draw_info_format (NDI_UNIQUE, 0, op, "This is Crossfire+ v%s", VERSION);
47
48 new_draw_info (NDI_UNIQUE, 0, op, "Authors and contributors to this program:");
49 new_draw_info (NDI_UNIQUE, 0, op, "Marc A. Lehmann <pcg@goof.com>");
50 new_draw_info (NDI_UNIQUE, 0, op, "Robin Redeker <elmex@x-paste.de>");
51 new_draw_info (NDI_UNIQUE, 0, op, "Pippijn van Steenhoven <pip88nl@gmail.com>");
52 new_draw_info (NDI_UNIQUE, 0, op, "");
53 new_draw_info (NDI_UNIQUE, 0, op, "Authors and contributors to the original Crossfire:");
54 new_draw_info (NDI_UNIQUE, 0, op, "Mark Wedel <mwedel@sonic.net>");
55 new_draw_info (NDI_UNIQUE, 0, op, "Frank Tore Johansen <frankj@ifi.uio.no>");
56 new_draw_info (NDI_UNIQUE, 0, op, "Kjetil Torgrim Homme <kjetilho@ifi.uio.no>");
57 new_draw_info (NDI_UNIQUE, 0, op, "Tyler Van Gorder <tvangod@ecst.csuchico.edu>");
58 new_draw_info (NDI_UNIQUE, 0, op, "Tony Elmroth <elmroth@cd.chalmers.se>");
59 new_draw_info (NDI_UNIQUE, 0, op, "Dougal Scott <dougal.scott@fcit.monasu.edu.au>");
60 new_draw_info (NDI_UNIQUE, 0, op, "William <wchuang@athena.mit.edu>");
61 new_draw_info (NDI_UNIQUE, 0, op, "Geoff Bailey <ftww@cs.su.oz.au>");
62 new_draw_info (NDI_UNIQUE, 0, op, "Kjetil Wiekhorst Jørgensen <jorgens@flipper.pvv.unit.no>");
63 new_draw_info (NDI_UNIQUE, 0, op, "Cameron Blackwood <c.blackwood@rdt.monash.edu.au>");
64 new_draw_info (NDI_UNIQUE, 0, op, "Joseph L. Traub <jtraub+@cmu.edu>");
65 new_draw_info (NDI_UNIQUE, 0, op, "Rupert G. Goldie <rgg@aaii.oz.au>");
66 new_draw_info (NDI_UNIQUE, 0, op, "Eric A. Anderson <eanders+@cmu.edu>");
67 new_draw_info (NDI_UNIQUE, 0, op, "Rickard Eneqvist <eneq@Prag.DoCS.UU.SE>");
68 new_draw_info (NDI_UNIQUE, 0, op, "Jarkko Sonninen <Jarkko.Sonninen@lut.fi>");
69 new_draw_info (NDI_UNIQUE, 0, op, "Karl Holland <kholland@sunlab.cit.cornell.du>");
70 new_draw_info (NDI_UNIQUE, 0, op, "Mikael Lundgren <vick@bern.docs.uu.se>");
71 new_draw_info (NDI_UNIQUE, 0, op, "Mikael Olsson <mol@meryl.csd.uu.se>");
72 new_draw_info (NDI_UNIQUE, 0, op, "Tero Haatanen <Tero.Haatanen@lut.fi>");
73 new_draw_info (NDI_UNIQUE, 0, op, "Lasse Ylitalo <ylitalo@student.docs.uu.se>");
74 new_draw_info (NDI_UNIQUE, 0, op, "Niilo Neuvo <anipa@guru.magic.fi>");
75 new_draw_info (NDI_UNIQUE, 0, op, "Markku J{rvinen <mta@modeemi.cs.tut.fi>");
76 new_draw_info (NDI_UNIQUE, 0, op, "Sylvain Meunier <meunier@inf.enst.fr>");
77 new_draw_info (NDI_UNIQUE, 0, op, "Jason Fosback <jfosback@darmok.uoregon.edu>");
78 new_draw_info (NDI_UNIQUE, 0, op, "Carl Edman <cedman@capitalist.princeton.edu>");
79 new_draw_info (NDI_UNIQUE, 0, op, "Charles Henrich <henrich@crh.cl.msu.edu>");
80 new_draw_info (NDI_UNIQUE, 0, op, "Gregor Schmid <schmid@fb3-s7.math.tu-berlin.de>");
81 new_draw_info (NDI_UNIQUE, 0, op, "Raphael Quinet <quinet@montefiore.ulg.ac.be>");
82 new_draw_info (NDI_UNIQUE, 0, op, "Jari Vanhala <jam@modeemi.cs.tut.fi>");
83 new_draw_info (NDI_UNIQUE, 0, op, "Tero Kivinen <kivinen@joker.cs.hut.fi>");
84 new_draw_info (NDI_UNIQUE, 0, op, "Peter Mardahl <peterm@soda.berkeley.edu>");
85 new_draw_info (NDI_UNIQUE, 0, op, "Matthew Zeher <matt@cs.odu.edu>");
86 new_draw_info (NDI_UNIQUE, 0, op, "Scott R. Turner <srt@sun-dimas.aero.org>");
87 new_draw_info (NDI_UNIQUE, 0, op, "Ben Fennema <huma@netcom.com>");
88 new_draw_info (NDI_UNIQUE, 0, op, "Nick Williams <njw@cs.city.ac.uk>");
89 new_draw_info (NDI_UNIQUE, 0, op, "Laurent Wacrenier <Wacren@Gin.ObsPM.Fr>");
90 new_draw_info (NDI_UNIQUE, 0, op, "Brian Thomas <thomas@astro.psu.edu>");
91 new_draw_info (NDI_UNIQUE, 0, op, "John Steven Moerk <jsm@axon.ksc.nasa.gov>");
92 new_draw_info (NDI_UNIQUE, 0, op, "Delbecq David <david.delbecq@mailandnews.com>");
93 new_draw_info (NDI_UNIQUE, 0, op, "Chachkoff Yann <yann.chachkoff@mailandnews.com>\n");
94 new_draw_info (NDI_UNIQUE, 0, op, "");
95 new_draw_info (NDI_UNIQUE, 0, op, "Images and art:");
96 new_draw_info (NDI_UNIQUE, 0, op, "Peter Gardner");
97 new_draw_info (NDI_UNIQUE, 0, op, "David Gervais <david_eg@mail.com>");
98 new_draw_info (NDI_UNIQUE, 0, op, "Mitsuhiro Itakura <ita@gold.koma.jaeri.go.jp>");
99 new_draw_info (NDI_UNIQUE, 0, op, "Hansjoerg Malthaner <hansjoerg.malthaner@danet.de>");
100 new_draw_info (NDI_UNIQUE, 0, op, "Mårten Woxberg <maxmc@telia.com>");
101 new_draw_info (NDI_UNIQUE, 0, op, "");
102 new_draw_info (NDI_UNIQUE, 0, op, "And many more!");
103 }
104
105 /* This is a basic little function to put the player back to his
106 * savebed. We do some error checking - its possible that the
107 * savebed map may no longer exist, so we make sure the player
108 * goes someplace.
109 */
110 void
111 enter_player_savebed (object *op)
112 {
113 object *tmp = object::create ();
114 EXIT_PATH (tmp) = op->contr->savebed_map;
115 EXIT_X (tmp) = op->contr->bed_x;
116 EXIT_Y (tmp) = op->contr->bed_y;
117 op->enter_exit (tmp);
118 tmp->destroy ();
119 }
120
121 /*
122 * enter_map(): Moves the player and pets from current map (if any) to
123 * new map. map, x, y must be set. map is the map we are moving the
124 * player to - it could be the map he just came from if the load failed for
125 * whatever reason. If default map coordinates are to be used, then
126 * the function that calls this should figure them out.
127 */
128 void
129 object::enter_map (maptile *newmap, int x, int y)
130 {
131 if (destroyed () || !newmap || newmap->in_memory != MAP_IN_MEMORY)
132 return;
133
134 if (out_of_map (newmap, x, y))
135 {
136 LOG (llevError, "enter_map: supplied coordinates are not within the map! (%s: %d, %d)\n", &newmap->path, x, y);
137 x = newmap->enter_x;
138 y = newmap->enter_y;
139 if (out_of_map (newmap, x, y))
140 {
141 LOG (llevError, "enter_map: map %s provides invalid default enter location (%d, %d) > (%d, %d)\n",
142 &newmap->path, x, y, newmap->width, newmap->height);
143 new_draw_info (NDI_UNIQUE, 0, this, "The exit is closed");
144 return;
145 }
146 }
147
148 if (contr && map != newmap && map)
149 if (INVOKE_MAP (LEAVE, map, ARG_PLAYER (contr)))
150 return;
151
152 // remove, so stupid ob_locked does not trigger a failure
153 remove ();
154
155 /* try to find a spot for the player */
156 if (ob_blocked (this, newmap, x, y))
157 { /* First choice blocked */
158 /* We try to find a spot for the player, starting closest in.
159 * We could use find_first_free_spot, but that doesn't randomize it at all,
160 * So for example, if the north space is free, you would always end up there even
161 * if other spaces around are available.
162 * Note that for the second and third calls, we could start at a position other
163 * than one, but then we could end up on the other side of walls and so forth.
164 */
165 int i = find_free_spot (this, newmap, x, y, 1, SIZEOFFREE1 + 1);
166
167 if (i == -1)
168 {
169 i = find_free_spot (this, newmap, x, y, 1, SIZEOFFREE2 + 1);
170 if (i == -1)
171 i = find_free_spot (this, newmap, x, y, 1, SIZEOFFREE);
172 }
173
174 if (i != -1)
175 {
176 x += freearr_x[i];
177 y += freearr_y[i];
178 }
179 else
180 /* not much we can do in this case. */
181 LOG (llevInfo, "enter_map: Could not find free spot for player - will dump on top of object (%s: %d, %d)\n", &newmap->path, x, y);
182 }
183
184 if (contr && map != newmap)
185 {
186 if (INVOKE_PLAYER (MAP_CHANGE, contr, ARG_MAP (newmap), ARG_INT (x), ARG_INT (y)))
187 return;
188
189 if (INVOKE_MAP (ENTER, newmap, ARG_PLAYER (contr), ARG_INT (x), ARG_INT (y)))
190 return;
191 }
192
193 this->x = x;
194 this->y = y;
195 map = newmap;
196
197 insert_ob_in_map (this, map, 0, INS_NO_WALK_ON);
198
199 enemy = 0;
200
201 if (contr)
202 {
203 contr->maplevel = newmap->path;
204 contr->count = 0;
205 }
206
207 /* Update any golems */
208 if (type == PLAYER)
209 if (object *golem = contr->golem)
210 {
211 int i = find_free_spot (golem, newmap, x, y, 1, SIZEOFFREE);
212
213 if (i < 0)
214 golem->destroy ();
215 else
216 {
217 newmap->insert (golem, x + freearr_x[i], y + freearr_y[i]);
218 golem->direction = find_dir_2 (x - golem->x, y - golem->y);
219 }
220 }
221
222 /* since the players map is already loaded, we don't need to worry
223 * about pending objects.
224 */
225 remove_all_pets (newmap);
226 }
227
228 /* process_players1 and process_players2 do all the player related stuff.
229 * I moved it out of process events and process_map. This was to some
230 * extent for debugging as well as to get a better idea of the time used
231 * by the various functions. process_players1() does the processing before
232 * objects have been updated, process_players2() does the processing that
233 * is needed after the players have been updated.
234 */
235 static void
236 process_players1 ()
237 {
238 /* Basically, we keep looping until all the players have done their actions. */
239 for (int flag = 1; flag != 0;)
240 {
241 flag = 0;
242 for_all_players (pl)
243 {
244 pl->refcnt_chk ();
245
246 if (!pl->ob || !pl->ns || !pl->ob->active)
247 continue;
248
249 if (pl->ob->speed_left > 0 && pl->ns)
250 if (handle_newcs_player (pl->ob))
251 flag = 1;
252
253 /* If the player is not actively playing, don't make a
254 * backup save - nothing to save anyway. Plus, the
255 * map may not longer be valid. This can happen when the
256 * player quits - they exist for purposes of tracking on the map,
257 * but don't actually reside on any actual map.
258 */
259 if (QUERY_FLAG (pl->ob, FLAG_REMOVED))
260 continue;
261 } /* end of for loop for all the players */
262 } /* for flag */
263
264 for_all_players (pl)
265 {
266 if (!pl->ob || !pl->ns || !pl->ob->active)
267 continue;
268
269 if (settings.casting_time)
270 {
271 if (pl->ob->casting_time > 0)
272 {
273 pl->ob->casting_time--;
274 pl->ob->start_holding = 1;
275 }
276
277 /* set spell_state so we can update the range in stats field */
278 if ((pl->ob->casting_time == 0) && (pl->ob->start_holding == 1))
279 pl->ob->start_holding = 0;
280 }
281
282 do_some_living (pl->ob);
283 }
284 }
285
286 static void
287 process_players2 ()
288 {
289 /* Then check if any players should use weapon-speed instead of speed */
290 for_all_players (pl)
291 {
292 /* The code that did weapon_sp handling here was out of place -
293 * this isn't called until after the player has finished there
294 * actions, and is thus out of place. All we do here is bounds
295 * checking.
296 */
297 if (pl->has_hit)
298 {
299 if (pl->ob->speed_left > pl->weapon_sp)
300 pl->ob->speed_left = pl->weapon_sp;
301
302 /* This needs to be here - if the player is running, we need to
303 * clear this each tick, but new commands are not being received
304 * so execute_newserver_command() is never called
305 */
306 pl->has_hit = 0;
307 }
308 else if (pl->ob->speed_left > pl->ob->speed)
309 pl->ob->speed_left = pl->ob->speed;
310 }
311 }
312
313 void
314 process_events ()
315 {
316 process_players1 ();
317
318 for_all_actives (op)
319 {
320 /* Now process op */
321 if (QUERY_FLAG (op, FLAG_FREED))
322 {
323 LOG (llevError, "BUG: process_events(): Free object on list\n");
324 op->set_speed (0);
325 continue;
326 }
327
328 if (!op->has_active_speed ())
329 {
330 LOG (llevError, "BUG: process_events(): Object %s has no speed (%f), "
331 "but is on active list\n", op->debug_desc (), op->speed);
332 op->set_speed (0);
333 continue;
334 }
335
336 if (op->flag [FLAG_REMOVED])
337 {
338 LOG (llevError, "BUG: process_events(): removed object is on active list: %s\n",
339 op->debug_desc ());
340 op->set_speed (0);
341 continue;
342 }
343
344 /* Animate the object. Bug or feature that anim_speed
345 * is based on ticks, and not the creatures speed?
346 */
347 if (op->anim_speed && op->last_anim >= op->anim_speed)
348 {
349 animate_object (op, op->type == PLAYER ? op->facing : op->direction);
350
351 op->last_anim = 1;
352 }
353 else
354 op->last_anim++;
355
356 if (op->speed_left > 0)
357 {
358 #if 0
359 /* I've seen occasional crashes in move_symptom() with it
360 * crashing because op is removed - add some debugging to
361 * track if it is removed at this point.
362 * This unfortunately is a bit too verbose it seems - not sure
363 * why - I think what happens is a map is freed or something and
364 * some objects get 'lost' - removed never to be reclaimed.
365 * removed objects generally shouldn't exist.
366 */
367 if (QUERY_FLAG (op, FLAG_REMOVED))
368 {
369 LOG (llevDebug, "process_events: calling process_object with removed object %s\n", op->name ? op->name : "null");
370 }
371 #endif
372 --op->speed_left;
373 process_object (op);
374
375 if (op->destroyed ())
376 continue;
377 }
378
379 if (settings.casting_time == TRUE && op->casting_time > 0)
380 op->casting_time--;
381
382 if (op->speed_left <= 0)
383 op->speed_left += FABS (op->speed);
384 }
385
386 process_players2 ();
387 }
388
389 /* clean up everything before exiting */
390 void
391 emergency_save ()
392 {
393 LOG (llevDebug, "emergency save begin.\n");
394
395 cfperl_emergency_save ();
396
397 LOG (llevDebug, "saving book archive.\n");
398 write_book_archive ();
399
400 LOG (llevDebug, "emergency save done.\n");
401 }
402
403 // send all clients some informational text
404 static void
405 cleanup_inform (const char *cause, bool make_core)
406 {
407 int flags = NDI_UNIQUE | NDI_ALL | (make_core ? NDI_RED : NDI_GREEN);
408
409 new_draw_info_format (flags, 0, 0, "The server will now shutdown.\n");
410 new_draw_info_format (flags, 0, 0, "Cause for this shutdown: %s\n", cause);
411
412 if (make_core)
413 new_draw_info_format (flags, 0, 0, "This is considered a crash, but all maps and players have been saved.\n");
414 else
415 new_draw_info_format (flags, 0, 0, "This is considered to be a clean shutdown, and all maps and players will be saved now.\n");
416
417 new_draw_info_format (flags, 0, 0, "%s\n", CLEANUP_MESSAGE);
418
419 flush_sockets ();
420 }
421
422 /* clean up everything before exiting */
423 void
424 cleanup (const char *cause, bool make_core)
425 {
426 if (make_core)
427 fork_abort (cause);
428
429 LOG (llevError, "cleanup cause: %s\n", cause);
430
431 if (!make_core)
432 cleanup_inform (cause, make_core);
433
434 LOG (llevDebug, "cleanup begin.\n");
435
436 if (init_done && !in_cleanup)
437 {
438 in_cleanup = true;
439 emergency_save ();
440 }
441 else
442 in_cleanup = true;
443
444 LOG (llevDebug, "running cleanup handlers.\n");
445 INVOKE_GLOBAL (CLEANUP);
446
447 LOG (llevDebug, "cleanup done.\n");
448
449 if (make_core)
450 {
451 cleanup_inform (cause, make_core);
452 cfperl_cleanup (make_core);
453 _exit (1);
454 }
455 else
456 {
457 new_draw_info_format (NDI_UNIQUE | NDI_ALL | NDI_GREEN, 0, 0, "Maps and players successfully saved, exiting.\n");
458 new_draw_info_format (NDI_UNIQUE | NDI_ALL | NDI_GREEN, 0, 0, "And again: " CLEANUP_MESSAGE "\n");
459 flush_sockets ();
460 cfperl_cleanup (make_core);
461 _exit (0);
462 }
463 }
464
465 int
466 forbid_play (void)
467 {
468 #if !defined(_IBMR2) && !defined(___IBMR2) && defined(PERM_FILE)
469 char buf[MAX_BUF], day[MAX_BUF];
470 FILE *fp;
471 time_t clock;
472 struct tm *tm;
473 int i, start, stop, forbit = 0, comp;
474
475 clock = time (NULL);
476 tm = (struct tm *) localtime (&clock);
477
478 sprintf (buf, "%s/%s", settings.confdir, PERM_FILE);
479 if ((fp = open_and_uncompress (buf, 0, &comp)) == NULL)
480 return 0;
481
482 while (fgets (buf, MAX_BUF, fp))
483 {
484 if (buf[0] == '#')
485 continue;
486
487 if (!strncmp (buf, "msg", 3))
488 {
489 if (forbit)
490 while (fgets (buf, MAX_BUF, fp)) /* print message */
491 fputs (buf, logfile);
492 break;
493 }
494 else if (sscanf (buf, "%s %d%*c%d\n", day, &start, &stop) != 3)
495 {
496 LOG (llevDebug, "Warning: Incomplete line in permission file ignored.\n");
497 continue;
498 }
499
500 for (i = 0; i < 7; i++)
501 {
502 if (!strncmp (buf, days[i], 3) && (tm->tm_wday == i) && (tm->tm_hour >= start) && (tm->tm_hour < stop))
503 forbit = 1;
504 }
505 }
506
507 close_and_delete (fp, comp);
508
509 return forbit;
510 #else
511 return 0;
512 #endif
513 }
514
515 /*
516 * do_specials() is a collection of functions to call from time to time.
517 * Modified 2000-1-14 MSW to use the global pticks count to determine how
518 * often to do things. This will allow us to spred them out more often.
519 * I use prime numbers for the factor count - in that way, it is less likely
520 * these actions will fall on the same tick (compared to say using 500/2500/15000
521 * which would mean on that 15,000 tick count a whole bunch of stuff gets
522 * done). Of course, there can still be times where multiple specials are
523 * done on the same tick, but that will happen very infrequently
524 *
525 * I also think this code makes it easier to see how often we really are
526 * doing the various things.
527 */
528 void
529 do_specials (void)
530 {
531 if (!(pticks % TICKS_PER_HOUR))
532 adjust_daylight ();
533
534 if (!(pticks % 7))
535 shstr::gc ();
536
537 if (!(pticks % 2503))
538 fix_weight (); /* Hack to fix weightproblems caused by bugs */
539
540 if (!(pticks % 5003))
541 write_book_archive ();
542
543 if (!(pticks % 5009))
544 clean_friendly_list ();
545
546 if (!(pticks % 5011))
547 obsolete_parties ();
548
549 if (!(pticks % 12503))
550 fix_luck ();
551 }
552
553 void
554 server_tick ()
555 {
556 // first do the user visible stuff
557 doeric_server ();
558 INVOKE_GLOBAL (CLOCK);
559 process_events (); /* "do" something with objects with speed */
560 flush_sockets ();
561
562 // then do some bookkeeping, should not really be here
563 do_specials (); /* Routines called from time to time. */
564 attachable::check_mortals ();
565
566 ++pticks;
567 }
568
569 #if 0
570 // used fro benchmarking (x86/amd64-specific)
571 typedef unsigned long tval;
572 typedef unsigned long long stamp64;
573
574 extern inline tval
575 stamp (void)
576 {
577 tval tsc;
578 asm volatile ("rdtsc":"=a" (tsc)::"edx");
579
580 return tsc;
581 }
582
583 extern inline tval
584 measure (tval t)
585 {
586 tval tsc;
587 asm volatile ("rdtsc":"=a" (tsc)::"edx");
588
589 if (tsc > t)
590 return tsc - t;
591 else
592 return t - tsc;
593 }
594
595 int
596 main (int argc, char **argv)
597 {
598 rand_gen rg(0);
599 tval fastest = 0x7fffffff;
600 for (int loop = 10000; loop--; )
601 {
602 tval s = stamp ();
603 volatile int i = rg.get_int(25);
604 fastest = min (fastest, measure (s));
605 }
606
607 //printf ("fastest %d\n", fastest);
608 for (int i = 0; i < 1024*1024*3; ++i)
609 {
610 char c = rg.get_int (256);
611 write (2, &c, 1);
612 }
613 }
614
615 #else
616
617 // normal main
618 int
619 main (int argc, char **argv)
620 {
621 settings.argc = argc;
622 settings.argv = argv;
623
624 init (argc, argv);
625
626 initPlugins ();
627
628 for (;;)
629 cfperl_main ();
630 }
631 #endif
632