ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.272
Committed: Sun Jun 3 15:32:50 2007 UTC (16 years, 11 months ago) by root
Branch: MAIN
Changes since 1.271: +1 -0 lines
Log Message:
fix magicmap

File Contents

# User Rev Content
1 root 1.1 package cf;
2    
3 root 1.96 use utf8;
4     use strict;
5    
6 root 1.1 use Symbol;
7     use List::Util;
8 root 1.250 use Socket;
9 root 1.6 use Storable;
10 root 1.224 use Event;
11 root 1.23 use Opcode;
12     use Safe;
13     use Safe::Hole;
14 root 1.19
15 root 1.244 use Coro 3.61 ();
16 root 1.224 use Coro::State;
17 root 1.250 use Coro::Handle;
18 root 1.96 use Coro::Event;
19     use Coro::Timer;
20     use Coro::Signal;
21     use Coro::Semaphore;
22 root 1.105 use Coro::AIO;
23 root 1.237 use Coro::Storable;
24 root 1.96
25 root 1.206 use BDB ();
26 root 1.154 use Data::Dumper;
27 root 1.108 use Digest::MD5;
28 root 1.105 use Fcntl;
29 root 1.227 use YAML::Syck ();
30 root 1.145 use IO::AIO 2.32 ();
31 root 1.32 use Time::HiRes;
32 root 1.208 use Compress::LZF;
33    
34 root 1.227 # configure various modules to our taste
35     #
36 root 1.237 $Storable::canonical = 1; # reduce rsync transfers
37 root 1.224 Coro::State::cctx_stacksize 256000; # 1-2MB stack, for deep recursions in maze generator
38 root 1.208 Compress::LZF::sfreeze_cr { }; # prime Compress::LZF so it does not use require later
39 root 1.227
40 root 1.224 $Event::Eval = 1; # no idea why this is required, but it is
41 root 1.1
42 root 1.72 # work around bug in YAML::Syck - bad news for perl6, will it be as broken wrt. unicode?
43     $YAML::Syck::ImplicitUnicode = 1;
44    
45 root 1.139 $Coro::main->prio (Coro::PRIO_MAX); # run main coroutine ("the server") with very high priority
46 root 1.1
47 root 1.227 sub WF_AUTOCANCEL () { 1 } # automatically cancel this watcher on reload
48    
49 root 1.85 our %COMMAND = ();
50     our %COMMAND_TIME = ();
51 root 1.159
52     our @EXTS = (); # list of extension package names
53 root 1.85 our %EXTCMD = ();
54 root 1.159 our %EXT_CORO = (); # coroutines bound to extensions
55 root 1.161 our %EXT_MAP = (); # pluggable maps
56 root 1.85
57 root 1.223 our $RELOAD; # number of reloads so far
58 root 1.1 our @EVENT;
59 root 1.253
60     our $CONFDIR = confdir;
61     our $DATADIR = datadir;
62     our $LIBDIR = "$DATADIR/ext";
63     our $PODDIR = "$DATADIR/pod";
64     our $MAPDIR = "$DATADIR/" . mapdir;
65     our $LOCALDIR = localdir;
66     our $TMPDIR = "$LOCALDIR/" . tmpdir;
67     our $UNIQUEDIR = "$LOCALDIR/" . uniquedir;
68     our $PLAYERDIR = "$LOCALDIR/" . playerdir;
69     our $RANDOMDIR = "$LOCALDIR/random";
70     our $BDBDIR = "$LOCALDIR/db";
71 root 1.1
72 root 1.245 our $TICK = MAX_TIME * 1e-6; # this is a CONSTANT(!)
73 root 1.35 our $TICK_WATCHER;
74 root 1.168 our $AIO_POLL_WATCHER;
75 root 1.214 our $NEXT_RUNTIME_WRITE; # when should the runtime file be written
76 root 1.35 our $NEXT_TICK;
77 root 1.103 our $NOW;
78 root 1.205 our $USE_FSYNC = 1; # use fsync to write maps - default off
79 root 1.35
80 root 1.206 our $BDB_POLL_WATCHER;
81     our $DB_ENV;
82    
83 root 1.70 our %CFG;
84    
85 root 1.84 our $UPTIME; $UPTIME ||= time;
86 root 1.103 our $RUNTIME;
87    
88 root 1.143 our %PLAYER; # all users
89     our %MAP; # all maps
90 root 1.166 our $LINK_MAP; # the special {link} map, which is always available
91 root 1.103
92 root 1.166 # used to convert map paths into valid unix filenames by replacing / by ∕
93     our $PATH_SEP = "∕"; # U+2215, chosen purely for visual reasons
94    
95 root 1.265 our $LOAD; # a number between 0 (idle) and 1 (too many objects)
96     our $LOADAVG; # same thing, but with alpha-smoothing
97     our $tick_start; # for load detecting purposes
98    
99 root 1.103 binmode STDOUT;
100     binmode STDERR;
101    
102     # read virtual server time, if available
103 root 1.253 unless ($RUNTIME || !-e "$LOCALDIR/runtime") {
104     open my $fh, "<", "$LOCALDIR/runtime"
105 root 1.103 or die "unable to read runtime file: $!";
106     $RUNTIME = <$fh> + 0.;
107     }
108    
109 root 1.253 mkdir $_
110     for $LOCALDIR, $TMPDIR, $UNIQUEDIR, $PLAYERDIR, $RANDOMDIR, $BDBDIR;
111 root 1.103
112 root 1.131 our $EMERGENCY_POSITION;
113 root 1.110
114 root 1.199 sub cf::map::normalise;
115    
116 root 1.70 #############################################################################
117    
118     =head2 GLOBAL VARIABLES
119    
120     =over 4
121    
122 root 1.83 =item $cf::UPTIME
123    
124     The timestamp of the server start (so not actually an uptime).
125    
126 root 1.103 =item $cf::RUNTIME
127    
128     The time this server has run, starts at 0 and is increased by $cf::TICK on
129     every server tick.
130    
131 root 1.253 =item $cf::CONFDIR $cf::DATADIR $cf::LIBDIR $cf::PODDIR
132     $cf::MAPDIR $cf::LOCALDIR $cf::TMPDIR $cf::UNIQUEDIR
133     $cf::PLAYERDIR $cf::RANDOMDIR $cf::BDBDIR
134    
135     Various directories - "/etc", read-only install directory, perl-library
136     directory, pod-directory, read-only maps directory, "/var", "/var/tmp",
137     unique-items directory, player file directory, random maps directory and
138     database environment.
139 root 1.70
140 root 1.103 =item $cf::NOW
141    
142     The time of the last (current) server tick.
143    
144 root 1.70 =item $cf::TICK
145    
146     The interval between server ticks, in seconds.
147    
148 root 1.265 =item $cf::LOADAVG
149    
150     The current CPU load on the server (alpha-smoothed), as a value between 0
151     (none) and 1 (overloaded), indicating how much time is spent on processing
152     objects per tick. Healthy values are < 0.5.
153    
154     =item $cf::LOAD
155    
156     The raw value load value from the last tick.
157    
158 root 1.70 =item %cf::CFG
159    
160     Configuration for the server, loaded from C</etc/crossfire/config>, or
161     from wherever your confdir points to.
162    
163 root 1.239 =item cf::wait_for_tick, cf::wait_for_tick_begin
164 root 1.155
165 root 1.239 These are functions that inhibit the current coroutine one tick. cf::wait_for_tick_begin only
166     returns directly I<after> the tick processing (and consequently, can only wake one process
167     per tick), while cf::wait_for_tick wakes up all waiters after tick processing.
168 root 1.155
169 root 1.70 =back
170    
171     =cut
172    
173 root 1.1 BEGIN {
174     *CORE::GLOBAL::warn = sub {
175     my $msg = join "", @_;
176 root 1.103
177 root 1.1 $msg .= "\n"
178     unless $msg =~ /\n$/;
179    
180 root 1.255 $msg =~ s/([\x00-\x08\x0b-\x1f])/sprintf "\\x%02x", ord $1/ge;
181 root 1.250
182     utf8::encode $msg;
183 root 1.146 LOG llevError, $msg;
184 root 1.1 };
185     }
186    
187 root 1.93 @safe::cf::global::ISA = @cf::global::ISA = 'cf::attachable';
188     @safe::cf::object::ISA = @cf::object::ISA = 'cf::attachable';
189     @safe::cf::player::ISA = @cf::player::ISA = 'cf::attachable';
190     @safe::cf::client::ISA = @cf::client::ISA = 'cf::attachable';
191     @safe::cf::map::ISA = @cf::map::ISA = 'cf::attachable';
192 root 1.41 @safe::cf::object::player::ISA = @cf::object::player::ISA = 'cf::object';
193 root 1.25
194 root 1.41 # we bless all objects into (empty) derived classes to force a method lookup
195 root 1.25 # within the Safe compartment.
196 root 1.86 for my $pkg (qw(
197 root 1.100 cf::global cf::attachable
198 root 1.86 cf::object cf::object::player
199 root 1.89 cf::client cf::player
200 root 1.86 cf::arch cf::living
201     cf::map cf::party cf::region
202     )) {
203 root 1.25 no strict 'refs';
204 root 1.41 @{"safe::$pkg\::wrap::ISA"} = @{"$pkg\::wrap::ISA"} = $pkg;
205 root 1.25 }
206 root 1.1
207 root 1.18 $Event::DIED = sub {
208     warn "error in event callback: @_";
209     };
210    
211 root 1.70 =head2 UTILITY FUNCTIONS
212    
213     =over 4
214    
215 root 1.154 =item dumpval $ref
216    
217 root 1.70 =cut
218 root 1.44
219 root 1.154 sub dumpval {
220     eval {
221     local $SIG{__DIE__};
222     my $d;
223     if (1) {
224     $d = new Data::Dumper([$_[0]], ["*var"]);
225     $d->Terse(1);
226     $d->Indent(2);
227     $d->Quotekeys(0);
228     $d->Useqq(1);
229     #$d->Bless(...);
230     $d->Seen($_[1]) if @_ > 1;
231     $d = $d->Dump();
232     }
233     $d =~ s/([\x00-\x07\x09\x0b\x0c\x0e-\x1f])/sprintf "\\x%02x", ord($1)/ge;
234     $d
235     } || "[unable to dump $_[0]: '$@']";
236     }
237    
238 root 1.227 use JSON::XS qw(to_json from_json); # TODO# replace by JSON::PC once working
239 root 1.44
240 root 1.70 =item $ref = cf::from_json $json
241    
242     Converts a JSON string into the corresponding perl data structure.
243    
244     =item $json = cf::to_json $ref
245    
246     Converts a perl data structure into its JSON representation.
247    
248 root 1.120 =item cf::lock_wait $string
249    
250     Wait until the given lock is available. See cf::lock_acquire.
251    
252     =item my $lock = cf::lock_acquire $string
253    
254     Wait until the given lock is available and then acquires it and returns
255 root 1.135 a Coro::guard object. If the guard object gets destroyed (goes out of scope,
256 root 1.120 for example when the coroutine gets canceled), the lock is automatically
257     returned.
258    
259 root 1.133 Lock names should begin with a unique identifier (for example, cf::map::find
260     uses map_find and cf::map::load uses map_load).
261 root 1.120
262 root 1.253 =item $locked = cf::lock_active $string
263    
264     Return true if the lock is currently active, i.e. somebody has locked it.
265    
266 root 1.120 =cut
267    
268     our %LOCK;
269    
270     sub lock_wait($) {
271     my ($key) = @_;
272    
273     # wait for lock, if any
274     while ($LOCK{$key}) {
275     push @{ $LOCK{$key} }, $Coro::current;
276     Coro::schedule;
277     }
278     }
279    
280     sub lock_acquire($) {
281     my ($key) = @_;
282    
283     # wait, to be sure we are not locked
284     lock_wait $key;
285    
286     $LOCK{$key} = [];
287    
288 root 1.135 Coro::guard {
289 root 1.120 # wake up all waiters, to be on the safe side
290     $_->ready for @{ delete $LOCK{$key} };
291     }
292     }
293    
294 root 1.253 sub lock_active($) {
295     my ($key) = @_;
296    
297     ! ! $LOCK{$key}
298     }
299    
300 root 1.133 sub freeze_mainloop {
301     return unless $TICK_WATCHER->is_active;
302    
303 root 1.168 my $guard = Coro::guard {
304     $TICK_WATCHER->start;
305     };
306 root 1.133 $TICK_WATCHER->stop;
307     $guard
308     }
309    
310 root 1.140 =item cf::async { BLOCK }
311    
312     Currently the same as Coro::async_pool, meaning you cannot use
313     C<on_destroy>, C<join> or other gimmicks on these coroutines. The only
314     thing you are allowed to do is call C<prio> on it.
315    
316     =cut
317    
318     BEGIN { *async = \&Coro::async_pool }
319    
320 root 1.106 =item cf::sync_job { BLOCK }
321    
322     The design of crossfire+ requires that the main coro ($Coro::main) is
323     always able to handle events or runnable, as crossfire+ is only partly
324     reentrant. Thus "blocking" it by e.g. waiting for I/O is not acceptable.
325    
326     If it must be done, put the blocking parts into C<sync_job>. This will run
327     the given BLOCK in another coroutine while waiting for the result. The
328     server will be frozen during this time, so the block should either finish
329     fast or be very important.
330    
331     =cut
332    
333 root 1.105 sub sync_job(&) {
334     my ($job) = @_;
335    
336     if ($Coro::current == $Coro::main) {
337 root 1.265 my $time = Event::time;
338    
339 root 1.112 # this is the main coro, too bad, we have to block
340     # till the operation succeeds, freezing the server :/
341    
342 root 1.110 # TODO: use suspend/resume instead
343 root 1.112 # (but this is cancel-safe)
344 root 1.133 my $freeze_guard = freeze_mainloop;
345 root 1.112
346     my $busy = 1;
347     my @res;
348    
349 root 1.140 (async {
350 root 1.112 @res = eval { $job->() };
351     warn $@ if $@;
352     undef $busy;
353     })->prio (Coro::PRIO_MAX);
354    
355 root 1.105 while ($busy) {
356 root 1.141 Coro::cede or Event::one_event;
357 root 1.105 }
358 root 1.112
359 root 1.265 $time = Event::time - $time;
360    
361 root 1.266 LOG llevError | logBacktrace, Carp::longmess "long sync job"
362     if $time > $TICK * 0.5 && $TICK_WATCHER->is_active;
363 root 1.265
364     $tick_start += $time; # do not account sync jobs to server load
365    
366 root 1.112 wantarray ? @res : $res[0]
367 root 1.105 } else {
368 root 1.112 # we are in another coroutine, how wonderful, everything just works
369    
370     $job->()
371 root 1.105 }
372     }
373    
374 root 1.140 =item $coro = cf::async_ext { BLOCK }
375 root 1.103
376 root 1.159 Like async, but this coro is automatically being canceled when the
377 root 1.140 extension calling this is being unloaded.
378 root 1.103
379     =cut
380    
381 root 1.140 sub async_ext(&) {
382 root 1.103 my $cb = shift;
383    
384 root 1.140 my $coro = &Coro::async ($cb);
385 root 1.103
386     $coro->on_destroy (sub {
387     delete $EXT_CORO{$coro+0};
388     });
389     $EXT_CORO{$coro+0} = $coro;
390    
391     $coro
392     }
393    
394 root 1.108 sub write_runtime {
395 root 1.264 my $runtime = "$LOCALDIR/runtime";
396    
397     # first touch the runtime file to show we are still running:
398     # the fsync below can take a very very long time.
399    
400 root 1.271 IO::AIO::aio_utime $runtime, undef, undef;
401 root 1.264
402 root 1.219 my $guard = cf::lock_acquire "write_runtime";
403    
404 root 1.108 my $fh = aio_open "$runtime~", O_WRONLY | O_CREAT, 0644
405     or return;
406    
407 root 1.186 my $value = $cf::RUNTIME + 90 + 10;
408     # 10 is the runtime save interval, for a monotonic clock
409     # 60 allows for the watchdog to kill the server.
410    
411 root 1.108 (aio_write $fh, 0, (length $value), $value, 0) <= 0
412     and return;
413    
414 root 1.204 # always fsync - this file is important
415 root 1.108 aio_fsync $fh
416     and return;
417    
418 root 1.264 # touch it again to show we are up-to-date
419 root 1.271 aio_utime $fh, undef, undef;
420 root 1.264
421 root 1.108 close $fh
422     or return;
423    
424     aio_rename "$runtime~", $runtime
425     and return;
426    
427 root 1.265 warn "runtime file written.\n";#d#
428 root 1.263
429 root 1.108 1
430     }
431    
432 root 1.230 =item cf::datalog type => key => value, ...
433    
434     Log a datalog packet of the given type with the given key-value pairs.
435    
436     =cut
437    
438     sub datalog($@) {
439     my ($type, %kv) = @_;
440     warn "DATALOG ", JSON::XS->new->ascii->encode ({ %kv, type => $type });
441     }
442    
443 root 1.70 =back
444    
445 root 1.71 =cut
446    
447 root 1.44 #############################################################################
448 root 1.39
449 root 1.93 =head2 ATTACHABLE OBJECTS
450    
451 root 1.94 Many objects in crossfire are so-called attachable objects. That means you can
452     attach callbacks/event handlers (a collection of which is called an "attachment")
453     to it. All such attachable objects support the following methods.
454    
455     In the following description, CLASS can be any of C<global>, C<object>
456     C<player>, C<client> or C<map> (i.e. the attachable objects in
457     crossfire+).
458 root 1.55
459     =over 4
460    
461 root 1.94 =item $attachable->attach ($attachment, key => $value...)
462    
463     =item $attachable->detach ($attachment)
464    
465     Attach/detach a pre-registered attachment to a specific object and give it
466     the specified key/value pairs as arguments.
467    
468     Example, attach a minesweeper attachment to the given object, making it a
469     10x10 minesweeper game:
470 root 1.46
471 root 1.94 $obj->attach (minesweeper => width => 10, height => 10);
472 root 1.53
473 root 1.93 =item $bool = $attachable->attached ($name)
474 root 1.46
475 root 1.93 Checks wether the named attachment is currently attached to the object.
476 root 1.46
477 root 1.94 =item cf::CLASS->attach ...
478 root 1.46
479 root 1.94 =item cf::CLASS->detach ...
480 root 1.92
481 root 1.94 Define an anonymous attachment and attach it to all objects of the given
482     CLASS. See the next function for an explanation of its arguments.
483 root 1.92
484 root 1.93 You can attach to global events by using the C<cf::global> class.
485 root 1.92
486 root 1.94 Example, log all player logins:
487    
488     cf::player->attach (
489     on_login => sub {
490     my ($pl) = @_;
491     ...
492     },
493     );
494    
495     Example, attach to the jeweler skill:
496    
497     cf::object->attach (
498     type => cf::SKILL,
499     subtype => cf::SK_JEWELER,
500     on_use_skill => sub {
501     my ($sk, $ob, $part, $dir, $msg) = @_;
502     ...
503     },
504     );
505    
506     =item cf::CLASS::attachment $name, ...
507    
508     Register an attachment by C<$name> through which attachable objects of the
509     given CLASS can refer to this attachment.
510    
511     Some classes such as crossfire maps and objects can specify attachments
512     that are attached at load/instantiate time, thus the need for a name.
513    
514     These calls expect any number of the following handler/hook descriptions:
515 root 1.46
516     =over 4
517    
518     =item prio => $number
519    
520     Set the priority for all following handlers/hooks (unless overwritten
521     by another C<prio> setting). Lower priority handlers get executed
522     earlier. The default priority is C<0>, and many built-in handlers are
523     registered at priority C<-1000>, so lower priorities should not be used
524     unless you know what you are doing.
525    
526 root 1.93 =item type => $type
527    
528     (Only for C<< cf::object->attach >> calls), limits the attachment to the
529     given type of objects only (the additional parameter C<subtype> can be
530     used to further limit to the given subtype).
531    
532 root 1.46 =item on_I<event> => \&cb
533    
534     Call the given code reference whenever the named event happens (event is
535     something like C<instantiate>, C<apply>, C<use_skill> and so on, and which
536     handlers are recognised generally depends on the type of object these
537     handlers attach to).
538    
539     See F<include/eventinc.h> for the full list of events supported, and their
540     class.
541    
542     =item package => package::
543    
544     Look for sub functions of the name C<< on_I<event> >> in the given
545     package and register them. Only handlers for eevents supported by the
546     object/class are recognised.
547    
548     =back
549    
550 root 1.94 Example, define an attachment called "sockpuppet" that calls the given
551     event handler when a monster attacks:
552    
553     cf::object::attachment sockpuppet =>
554     on_skill_attack => sub {
555     my ($self, $victim) = @_;
556     ...
557     }
558     }
559    
560 root 1.96 =item $attachable->valid
561    
562     Just because you have a perl object does not mean that the corresponding
563     C-level object still exists. If you try to access an object that has no
564     valid C counterpart anymore you get an exception at runtime. This method
565     can be used to test for existence of the C object part without causing an
566     exception.
567    
568 root 1.39 =cut
569    
570 root 1.40 # the following variables are defined in .xs and must not be re-created
571 root 1.100 our @CB_GLOBAL = (); # registry for all global events
572     our @CB_ATTACHABLE = (); # registry for all attachables
573     our @CB_OBJECT = (); # all objects (should not be used except in emergency)
574     our @CB_PLAYER = ();
575     our @CB_CLIENT = ();
576     our @CB_TYPE = (); # registry for type (cf-object class) based events
577     our @CB_MAP = ();
578 root 1.39
579 root 1.45 my %attachment;
580    
581 root 1.170 sub cf::attachable::thawer_merge {
582     # simply override everything except _meta
583     local $_[0]{_meta};
584     %{$_[0]} = %{$_[1]};
585     }
586    
587 root 1.93 sub _attach_cb($$$$) {
588     my ($registry, $event, $prio, $cb) = @_;
589 root 1.39
590     use sort 'stable';
591    
592     $cb = [$prio, $cb];
593    
594     @{$registry->[$event]} = sort
595     { $a->[0] cmp $b->[0] }
596     @{$registry->[$event] || []}, $cb;
597     }
598    
599 root 1.100 # hack
600     my %attachable_klass = map +($_ => 1), KLASS_OBJECT, KLASS_CLIENT, KLASS_PLAYER, KLASS_MAP;
601    
602 root 1.39 # attach handles attaching event callbacks
603     # the only thing the caller has to do is pass the correct
604     # registry (== where the callback attaches to).
605 root 1.93 sub _attach {
606 root 1.45 my ($registry, $klass, @arg) = @_;
607 root 1.39
608 root 1.93 my $object_type;
609 root 1.39 my $prio = 0;
610     my %cb_id = map +("on_" . lc $EVENT[$_][0], $_) , grep $EVENT[$_][1] == $klass, 0 .. $#EVENT;
611    
612 root 1.100 #TODO: get rid of this hack
613     if ($attachable_klass{$klass}) {
614     %cb_id = (%cb_id, map +("on_" . lc $EVENT[$_][0], $_) , grep $EVENT[$_][1] == KLASS_ATTACHABLE, 0 .. $#EVENT);
615     }
616    
617 root 1.45 while (@arg) {
618     my $type = shift @arg;
619 root 1.39
620     if ($type eq "prio") {
621 root 1.45 $prio = shift @arg;
622 root 1.39
623 root 1.93 } elsif ($type eq "type") {
624     $object_type = shift @arg;
625     $registry = $CB_TYPE[$object_type] ||= [];
626    
627     } elsif ($type eq "subtype") {
628     defined $object_type or Carp::croak "subtype specified without type";
629     my $object_subtype = shift @arg;
630 root 1.267 $registry = $CB_TYPE[$object_type + $object_subtype * NUM_TYPES] ||= [];
631 root 1.93
632 root 1.39 } elsif ($type eq "package") {
633 root 1.45 my $pkg = shift @arg;
634 root 1.39
635     while (my ($name, $id) = each %cb_id) {
636     if (my $cb = $pkg->can ($name)) {
637 root 1.93 _attach_cb $registry, $id, $prio, $cb;
638 root 1.39 }
639     }
640    
641     } elsif (exists $cb_id{$type}) {
642 root 1.93 _attach_cb $registry, $cb_id{$type}, $prio, shift @arg;
643 root 1.39
644     } elsif (ref $type) {
645     warn "attaching objects not supported, ignoring.\n";
646    
647     } else {
648 root 1.45 shift @arg;
649 root 1.39 warn "attach argument '$type' not supported, ignoring.\n";
650     }
651     }
652     }
653    
654 root 1.93 sub _object_attach {
655 root 1.48 my ($obj, $name, %arg) = @_;
656 root 1.46
657 root 1.55 return if exists $obj->{_attachment}{$name};
658    
659 root 1.46 if (my $attach = $attachment{$name}) {
660     my $registry = $obj->registry;
661    
662 root 1.47 for (@$attach) {
663     my ($klass, @attach) = @$_;
664 root 1.93 _attach $registry, $klass, @attach;
665 root 1.47 }
666 root 1.46
667 root 1.48 $obj->{$name} = \%arg;
668 root 1.46 } else {
669     warn "object uses attachment '$name' that is not available, postponing.\n";
670     }
671    
672 root 1.50 $obj->{_attachment}{$name} = undef;
673 root 1.46 }
674    
675 root 1.93 sub cf::attachable::attach {
676     if (ref $_[0]) {
677     _object_attach @_;
678     } else {
679     _attach shift->_attach_registry, @_;
680     }
681 root 1.267 _recalc_want;
682 root 1.55 };
683 root 1.46
684 root 1.54 # all those should be optimised
685 root 1.93 sub cf::attachable::detach {
686 root 1.54 my ($obj, $name) = @_;
687 root 1.46
688 root 1.93 if (ref $obj) {
689     delete $obj->{_attachment}{$name};
690     reattach ($obj);
691     } else {
692     Carp::croak "cannot, currently, detach class attachments";
693     }
694 root 1.267 _recalc_want;
695 root 1.55 };
696    
697 root 1.93 sub cf::attachable::attached {
698 root 1.55 my ($obj, $name) = @_;
699    
700     exists $obj->{_attachment}{$name}
701 root 1.39 }
702    
703 root 1.100 for my $klass (qw(ATTACHABLE GLOBAL OBJECT PLAYER CLIENT MAP)) {
704 root 1.93 eval "#line " . __LINE__ . " 'cf.pm'
705     sub cf::\L$klass\E::_attach_registry {
706     (\\\@CB_$klass, KLASS_$klass)
707     }
708 root 1.45
709 root 1.93 sub cf::\L$klass\E::attachment {
710     my \$name = shift;
711 root 1.39
712 root 1.93 \$attachment{\$name} = [[KLASS_$klass, \@_]];
713     }
714     ";
715     die if $@;
716 root 1.52 }
717    
718 root 1.39 our $override;
719 root 1.45 our @invoke_results = (); # referenced from .xs code. TODO: play tricks with reify and mortals?
720 root 1.39
721 root 1.45 sub override {
722     $override = 1;
723     @invoke_results = ();
724 root 1.39 }
725    
726 root 1.45 sub do_invoke {
727 root 1.39 my $event = shift;
728 root 1.40 my $callbacks = shift;
729 root 1.39
730 root 1.45 @invoke_results = ();
731    
732 root 1.39 local $override;
733    
734 root 1.40 for (@$callbacks) {
735 root 1.39 eval { &{$_->[1]} };
736    
737     if ($@) {
738     warn "$@";
739 root 1.64 warn "... while processing $EVENT[$event][0](@_) event, skipping processing altogether.\n";
740 root 1.39 override;
741     }
742    
743     return 1 if $override;
744     }
745    
746     0
747     }
748    
749 root 1.96 =item $bool = cf::global::invoke (EVENT_CLASS_XXX, ...)
750 root 1.55
751 root 1.96 =item $bool = $attachable->invoke (EVENT_CLASS_XXX, ...)
752 root 1.55
753 root 1.96 Generate an object-specific event with the given arguments.
754 root 1.55
755 root 1.96 This API is preliminary (most likely, the EVENT_CLASS_xxx prefix will be
756 root 1.55 removed in future versions), and there is no public API to access override
757     results (if you must, access C<@cf::invoke_results> directly).
758    
759     =back
760    
761 root 1.71 =cut
762    
763 root 1.70 #############################################################################
764 root 1.45 # object support
765    
766 root 1.102 sub reattach {
767     # basically do the same as instantiate, without calling instantiate
768     my ($obj) = @_;
769    
770 root 1.169 bless $obj, ref $obj; # re-bless in case extensions have been reloaded
771    
772 root 1.102 my $registry = $obj->registry;
773    
774     @$registry = ();
775    
776     delete $obj->{_attachment} unless scalar keys %{ $obj->{_attachment} || {} };
777    
778     for my $name (keys %{ $obj->{_attachment} || {} }) {
779     if (my $attach = $attachment{$name}) {
780     for (@$attach) {
781     my ($klass, @attach) = @$_;
782     _attach $registry, $klass, @attach;
783     }
784     } else {
785     warn "object uses attachment '$name' that is not available, postponing.\n";
786     }
787     }
788     }
789    
790 root 1.100 cf::attachable->attach (
791     prio => -1000000,
792     on_instantiate => sub {
793     my ($obj, $data) = @_;
794 root 1.45
795 root 1.100 $data = from_json $data;
796 root 1.45
797 root 1.100 for (@$data) {
798     my ($name, $args) = @$_;
799 root 1.49
800 root 1.100 $obj->attach ($name, %{$args || {} });
801     }
802     },
803 root 1.102 on_reattach => \&reattach,
804 root 1.100 on_clone => sub {
805     my ($src, $dst) = @_;
806    
807     @{$dst->registry} = @{$src->registry};
808    
809     %$dst = %$src;
810    
811     %{$dst->{_attachment}} = %{$src->{_attachment}}
812     if exists $src->{_attachment};
813     },
814     );
815 root 1.45
816 root 1.46 sub object_freezer_save {
817 root 1.59 my ($filename, $rdata, $objs) = @_;
818 root 1.46
819 root 1.105 sync_job {
820     if (length $$rdata) {
821     warn sprintf "saving %s (%d,%d)\n",
822     $filename, length $$rdata, scalar @$objs;
823 root 1.60
824 root 1.105 if (my $fh = aio_open "$filename~", O_WRONLY | O_CREAT, 0600) {
825 root 1.60 chmod SAVE_MODE, $fh;
826 root 1.105 aio_write $fh, 0, (length $$rdata), $$rdata, 0;
827 root 1.204 aio_fsync $fh if $cf::USE_FSYNC;
828 root 1.60 close $fh;
829 root 1.105
830     if (@$objs) {
831     if (my $fh = aio_open "$filename.pst~", O_WRONLY | O_CREAT, 0600) {
832     chmod SAVE_MODE, $fh;
833     my $data = Storable::nfreeze { version => 1, objs => $objs };
834     aio_write $fh, 0, (length $data), $data, 0;
835 root 1.204 aio_fsync $fh if $cf::USE_FSYNC;
836 root 1.105 close $fh;
837     aio_rename "$filename.pst~", "$filename.pst";
838     }
839     } else {
840     aio_unlink "$filename.pst";
841     }
842    
843     aio_rename "$filename~", $filename;
844 root 1.60 } else {
845 root 1.105 warn "FATAL: $filename~: $!\n";
846 root 1.60 }
847 root 1.59 } else {
848 root 1.105 aio_unlink $filename;
849     aio_unlink "$filename.pst";
850 root 1.59 }
851 root 1.45 }
852     }
853    
854 root 1.80 sub object_freezer_as_string {
855     my ($rdata, $objs) = @_;
856    
857     use Data::Dumper;
858    
859 root 1.81 $$rdata . Dumper $objs
860 root 1.80 }
861    
862 root 1.46 sub object_thawer_load {
863     my ($filename) = @_;
864    
865 root 1.105 my ($data, $av);
866 root 1.61
867 root 1.105 (aio_load $filename, $data) >= 0
868     or return;
869 root 1.61
870 root 1.105 unless (aio_stat "$filename.pst") {
871     (aio_load "$filename.pst", $av) >= 0
872     or return;
873 root 1.113 $av = eval { (Storable::thaw $av)->{objs} };
874 root 1.61 }
875 root 1.45
876 root 1.118 warn sprintf "loading %s (%d)\n",
877     $filename, length $data, scalar @{$av || []};#d#
878 root 1.105 return ($data, $av);
879 root 1.45 }
880    
881     #############################################################################
882 root 1.85 # command handling &c
883 root 1.39
884 root 1.85 =item cf::register_command $name => \&callback($ob,$args);
885 root 1.1
886 root 1.85 Register a callback for execution when the client sends the user command
887     $name.
888 root 1.5
889 root 1.85 =cut
890 root 1.5
891 root 1.85 sub register_command {
892     my ($name, $cb) = @_;
893 root 1.5
894 root 1.85 my $caller = caller;
895     #warn "registering command '$name/$time' to '$caller'";
896 root 1.1
897 root 1.85 push @{ $COMMAND{$name} }, [$caller, $cb];
898 root 1.1 }
899    
900 root 1.85 =item cf::register_extcmd $name => \&callback($pl,$packet);
901 root 1.1
902 root 1.85 Register a callbackf ro execution when the client sends an extcmd packet.
903 root 1.1
904 root 1.85 If the callback returns something, it is sent back as if reply was being
905     called.
906 root 1.1
907 root 1.85 =cut
908 root 1.1
909 root 1.16 sub register_extcmd {
910     my ($name, $cb) = @_;
911    
912 root 1.159 $EXTCMD{$name} = $cb;
913 root 1.16 }
914    
915 root 1.93 cf::player->attach (
916 root 1.85 on_command => sub {
917     my ($pl, $name, $params) = @_;
918    
919     my $cb = $COMMAND{$name}
920     or return;
921    
922     for my $cmd (@$cb) {
923     $cmd->[1]->($pl->ob, $params);
924     }
925    
926     cf::override;
927     },
928     on_extcmd => sub {
929     my ($pl, $buf) = @_;
930    
931     my $msg = eval { from_json $buf };
932    
933     if (ref $msg) {
934     if (my $cb = $EXTCMD{$msg->{msgtype}}) {
935 root 1.159 if (my %reply = $cb->($pl, $msg)) {
936 root 1.85 $pl->ext_reply ($msg->{msgid}, %reply);
937     }
938     }
939     } else {
940     warn "player " . ($pl->ob->name) . " sent unparseable ext message: <$buf>\n";
941     }
942    
943     cf::override;
944     },
945 root 1.93 );
946 root 1.85
947 root 1.1 sub load_extension {
948     my ($path) = @_;
949    
950     $path =~ /([^\/\\]+)\.ext$/ or die "$path";
951 root 1.5 my $base = $1;
952 root 1.1 my $pkg = $1;
953     $pkg =~ s/[^[:word:]]/_/g;
954 root 1.41 $pkg = "ext::$pkg";
955 root 1.1
956 root 1.160 warn "... loading '$path' into '$pkg'\n";
957 root 1.1
958     open my $fh, "<:utf8", $path
959     or die "$path: $!";
960    
961     my $source =
962     "package $pkg; use strict; use utf8;\n"
963     . "#line 1 \"$path\"\n{\n"
964     . (do { local $/; <$fh> })
965     . "\n};\n1";
966    
967 root 1.166 unless (eval $source) {
968     my $msg = $@ ? "$path: $@\n"
969     : "extension disabled.\n";
970     if ($source =~ /^#!.*perl.*#.*MANDATORY/m) { # ugly match
971     warn $@;
972     warn "mandatory extension failed to load, exiting.\n";
973     exit 1;
974     }
975     die $@;
976     }
977 root 1.1
978 root 1.159 push @EXTS, $pkg;
979 root 1.1 }
980    
981     sub load_extensions {
982     for my $ext (<$LIBDIR/*.ext>) {
983 root 1.3 next unless -r $ext;
984 root 1.2 eval {
985     load_extension $ext;
986     1
987     } or warn "$ext not loaded: $@";
988 root 1.1 }
989     }
990    
991 root 1.8 #############################################################################
992 root 1.70
993     =head2 CORE EXTENSIONS
994    
995     Functions and methods that extend core crossfire objects.
996    
997 root 1.143 =cut
998    
999     package cf::player;
1000    
1001 root 1.154 use Coro::AIO;
1002    
1003 root 1.95 =head3 cf::player
1004    
1005 root 1.70 =over 4
1006 root 1.22
1007 root 1.143 =item cf::player::find $login
1008 root 1.23
1009 root 1.143 Returns the given player object, loading it if necessary (might block).
1010 root 1.23
1011     =cut
1012    
1013 root 1.145 sub playerdir($) {
1014 root 1.253 "$PLAYERDIR/"
1015 root 1.145 . (ref $_[0] ? $_[0]->ob->name : $_[0])
1016     }
1017    
1018 root 1.143 sub path($) {
1019 root 1.145 my $login = ref $_[0] ? $_[0]->ob->name : $_[0];
1020    
1021 root 1.234 (playerdir $login) . "/playerdata"
1022 root 1.143 }
1023    
1024     sub find_active($) {
1025     $cf::PLAYER{$_[0]}
1026     and $cf::PLAYER{$_[0]}->active
1027     and $cf::PLAYER{$_[0]}
1028     }
1029    
1030     sub exists($) {
1031     my ($login) = @_;
1032    
1033     $cf::PLAYER{$login}
1034 root 1.180 or cf::sync_job { !aio_stat path $login }
1035 root 1.143 }
1036    
1037     sub find($) {
1038     return $cf::PLAYER{$_[0]} || do {
1039     my $login = $_[0];
1040    
1041     my $guard = cf::lock_acquire "user_find:$login";
1042    
1043 root 1.151 $cf::PLAYER{$_[0]} || do {
1044 root 1.234 # rename old playerfiles to new ones
1045     #TODO: remove when no longer required
1046     aio_link +(playerdir $login) . "/$login.pl.pst", (playerdir $login) . "/playerdata.pst";
1047     aio_link +(playerdir $login) . "/$login.pl" , (playerdir $login) . "/playerdata";
1048     aio_unlink +(playerdir $login) . "/$login.pl.pst";
1049     aio_unlink +(playerdir $login) . "/$login.pl";
1050    
1051 root 1.151 my $pl = load_pl path $login
1052     or return;
1053     $cf::PLAYER{$login} = $pl
1054     }
1055     }
1056 root 1.143 }
1057    
1058     sub save($) {
1059     my ($pl) = @_;
1060    
1061     return if $pl->{deny_save};
1062    
1063     my $path = path $pl;
1064     my $guard = cf::lock_acquire "user_save:$path";
1065    
1066     return if $pl->{deny_save};
1067 root 1.146
1068 root 1.154 aio_mkdir playerdir $pl, 0770;
1069 root 1.143 $pl->{last_save} = $cf::RUNTIME;
1070    
1071     $pl->save_pl ($path);
1072     Coro::cede;
1073     }
1074    
1075     sub new($) {
1076     my ($login) = @_;
1077    
1078     my $self = create;
1079    
1080     $self->ob->name ($login);
1081     $self->{deny_save} = 1;
1082    
1083     $cf::PLAYER{$login} = $self;
1084    
1085     $self
1086 root 1.23 }
1087    
1088 root 1.154 =item $pl->quit_character
1089    
1090     Nukes the player without looking back. If logged in, the connection will
1091     be destroyed. May block for a long time.
1092    
1093     =cut
1094    
1095 root 1.145 sub quit_character {
1096     my ($pl) = @_;
1097    
1098 root 1.220 my $name = $pl->ob->name;
1099    
1100 root 1.145 $pl->{deny_save} = 1;
1101     $pl->password ("*"); # this should lock out the player until we nuked the dir
1102    
1103     $pl->invoke (cf::EVENT_PLAYER_LOGOUT, 1) if $pl->active;
1104     $pl->deactivate;
1105     $pl->invoke (cf::EVENT_PLAYER_QUIT);
1106     $pl->ns->destroy if $pl->ns;
1107    
1108     my $path = playerdir $pl;
1109     my $temp = "$path~$cf::RUNTIME~deleting~";
1110 root 1.154 aio_rename $path, $temp;
1111 root 1.150 delete $cf::PLAYER{$pl->ob->name};
1112     $pl->destroy;
1113 root 1.220
1114     my $prefix = qr<^~\Q$name\E/>;
1115    
1116     # nuke player maps
1117     $cf::MAP{$_}->nuke for grep /$prefix/, keys %cf::MAP;
1118    
1119 root 1.150 IO::AIO::aio_rmtree $temp;
1120 root 1.145 }
1121    
1122 pippijn 1.221 =item $pl->kick
1123    
1124     Kicks a player out of the game. This destroys the connection.
1125    
1126     =cut
1127    
1128     sub kick {
1129     my ($pl, $kicker) = @_;
1130    
1131     $pl->invoke (cf::EVENT_PLAYER_KICK, $kicker);
1132     $pl->killer ("kicked");
1133     $pl->ns->destroy;
1134     }
1135    
1136 root 1.154 =item cf::player::list_logins
1137    
1138     Returns am arrayref of all valid playernames in the system, can take a
1139     while and may block, so not sync_job-capable, ever.
1140    
1141     =cut
1142    
1143     sub list_logins {
1144 root 1.253 my $dirs = aio_readdir $PLAYERDIR
1145 root 1.154 or return [];
1146    
1147     my @logins;
1148    
1149     for my $login (@$dirs) {
1150     my $fh = aio_open path $login, Fcntl::O_RDONLY, 0 or next;
1151     aio_read $fh, 0, 512, my $buf, 0 or next;
1152 root 1.155 $buf !~ /^password -------------$/m or next; # official not-valid tag
1153 root 1.154
1154     utf8::decode $login;
1155     push @logins, $login;
1156     }
1157    
1158     \@logins
1159     }
1160    
1161     =item $player->maps
1162    
1163 root 1.166 Returns an arrayref of map paths that are private for this
1164 root 1.154 player. May block.
1165    
1166     =cut
1167    
1168     sub maps($) {
1169     my ($pl) = @_;
1170    
1171 root 1.201 $pl = ref $pl ? $pl->ob->name : $pl;
1172    
1173 root 1.154 my $files = aio_readdir playerdir $pl
1174     or return;
1175    
1176     my @paths;
1177    
1178     for (@$files) {
1179     utf8::decode $_;
1180     next if /\.(?:pl|pst)$/;
1181 root 1.158 next unless /^$PATH_SEP/o;
1182 root 1.154
1183 root 1.201 push @paths, cf::map::normalise "~$pl/$_";
1184 root 1.154 }
1185    
1186     \@paths
1187     }
1188    
1189 root 1.231 =item $player->ext_reply ($msgid, %msg)
1190 root 1.95
1191     Sends an ext reply to the player.
1192    
1193     =cut
1194    
1195 root 1.231 sub ext_reply($$%) {
1196 root 1.95 my ($self, $id, %msg) = @_;
1197    
1198     $msg{msgid} = $id;
1199    
1200 root 1.143 $self->send ("ext " . cf::to_json \%msg);
1201 root 1.95 }
1202    
1203 root 1.231 =item $player->ext_event ($type, %msg)
1204    
1205     Sends an ext event to the client.
1206    
1207     =cut
1208    
1209     sub ext_event($$%) {
1210     my ($self, $type, %msg) = @_;
1211    
1212 root 1.232 $self->ns->ext_event ($type, %msg);
1213 root 1.231 }
1214    
1215 root 1.238 =head3 cf::region
1216    
1217     =over 4
1218    
1219     =cut
1220    
1221     package cf::region;
1222    
1223     =item cf::region::find_by_path $path
1224    
1225     Tries to decuce the probable region for a map knowing only its path.
1226    
1227     =cut
1228    
1229     sub find_by_path($) {
1230     my ($path) = @_;
1231    
1232     my ($match, $specificity);
1233    
1234     for my $region (list) {
1235     if ($region->match && $path =~ $region->match) {
1236     ($match, $specificity) = ($region, $region->specificity)
1237     if $region->specificity > $specificity;
1238     }
1239     }
1240    
1241     $match
1242     }
1243 root 1.143
1244 root 1.95 =back
1245    
1246 root 1.110 =head3 cf::map
1247    
1248     =over 4
1249    
1250     =cut
1251    
1252     package cf::map;
1253    
1254     use Fcntl;
1255     use Coro::AIO;
1256    
1257 root 1.166 use overload
1258 root 1.173 '""' => \&as_string,
1259     fallback => 1;
1260 root 1.166
1261 root 1.133 our $MAX_RESET = 3600;
1262     our $DEFAULT_RESET = 3000;
1263 root 1.110
1264     sub generate_random_map {
1265 root 1.166 my ($self, $rmp) = @_;
1266 root 1.110 # mit "rum" bekleckern, nicht
1267 root 1.166 $self->_create_random_map (
1268 root 1.110 $rmp->{wallstyle}, $rmp->{wall_name}, $rmp->{floorstyle}, $rmp->{monsterstyle},
1269     $rmp->{treasurestyle}, $rmp->{layoutstyle}, $rmp->{doorstyle}, $rmp->{decorstyle},
1270     $rmp->{origin_map}, $rmp->{final_map}, $rmp->{exitstyle}, $rmp->{this_map},
1271     $rmp->{exit_on_final_map},
1272     $rmp->{xsize}, $rmp->{ysize},
1273     $rmp->{expand2x}, $rmp->{layoutoptions1}, $rmp->{layoutoptions2}, $rmp->{layoutoptions3},
1274     $rmp->{symmetry}, $rmp->{difficulty}, $rmp->{difficulty_given}, $rmp->{difficulty_increase},
1275     $rmp->{dungeon_level}, $rmp->{dungeon_depth}, $rmp->{decoroptions}, $rmp->{orientation},
1276     $rmp->{origin_y}, $rmp->{origin_x}, $rmp->{random_seed}, $rmp->{total_map_hp},
1277     $rmp->{map_layout_style}, $rmp->{treasureoptions}, $rmp->{symmetry_used},
1278 root 1.162 (cf::region::find $rmp->{region}), $rmp->{custom}
1279     )
1280 root 1.110 }
1281    
1282 root 1.187 =item cf::map->register ($regex, $prio)
1283    
1284     Register a handler for the map path matching the given regex at the
1285     givne priority (higher is better, built-in handlers have priority 0, the
1286     default).
1287    
1288     =cut
1289    
1290 root 1.166 sub register {
1291 root 1.187 my (undef, $regex, $prio) = @_;
1292 root 1.166 my $pkg = caller;
1293    
1294     no strict;
1295     push @{"$pkg\::ISA"}, __PACKAGE__;
1296    
1297 root 1.187 $EXT_MAP{$pkg} = [$prio, qr<$regex>];
1298 root 1.166 }
1299    
1300     # also paths starting with '/'
1301 root 1.187 $EXT_MAP{"cf::map"} = [0, qr{^(?=/)}];
1302 root 1.166
1303 root 1.170 sub thawer_merge {
1304 root 1.172 my ($self, $merge) = @_;
1305    
1306 root 1.170 # we have to keep some variables in memory intact
1307 root 1.172 local $self->{path};
1308     local $self->{load_path};
1309     local $self->{deny_save};
1310     local $self->{deny_reset};
1311 root 1.170
1312 root 1.172 $self->SUPER::thawer_merge ($merge);
1313 root 1.170 }
1314    
1315 root 1.166 sub normalise {
1316     my ($path, $base) = @_;
1317    
1318 root 1.192 $path = "$path"; # make sure its a string
1319    
1320 root 1.199 $path =~ s/\.map$//;
1321    
1322 root 1.166 # map plan:
1323     #
1324     # /! non-realised random map exit (special hack!)
1325     # {... are special paths that are not being touched
1326     # ?xxx/... are special absolute paths
1327     # ?random/... random maps
1328     # /... normal maps
1329     # ~user/... per-player map of a specific user
1330    
1331     $path =~ s/$PATH_SEP/\//go;
1332    
1333     # treat it as relative path if it starts with
1334     # something that looks reasonable
1335     if ($path =~ m{^(?:\./|\.\./|\w)}) {
1336     $base or Carp::carp "normalise called with relative path and no base: '$path'";
1337    
1338     $base =~ s{[^/]+/?$}{};
1339     $path = "$base/$path";
1340     }
1341    
1342     for ($path) {
1343     redo if s{//}{/};
1344     redo if s{/\.?/}{/};
1345     redo if s{/[^/]+/\.\./}{/};
1346     }
1347    
1348     $path
1349     }
1350    
1351     sub new_from_path {
1352     my (undef, $path, $base) = @_;
1353    
1354     return $path if UNIVERSAL::isa $path, "cf::map"; # already a map object
1355    
1356     $path = normalise $path, $base;
1357    
1358 root 1.187 for my $pkg (sort { $EXT_MAP{$b}[0] <=> $EXT_MAP{$a}[0] } keys %EXT_MAP) {
1359     if ($path =~ $EXT_MAP{$pkg}[1]) {
1360 root 1.166 my $self = bless cf::map::new, $pkg;
1361     $self->{path} = $path; $self->path ($path);
1362     $self->init; # pass $1 etc.
1363     return $self;
1364     }
1365     }
1366    
1367 root 1.192 Carp::carp "unable to resolve path '$path' (base '$base').";
1368 root 1.166 ()
1369     }
1370    
1371     sub init {
1372     my ($self) = @_;
1373    
1374     $self
1375     }
1376    
1377     sub as_string {
1378     my ($self) = @_;
1379    
1380     "$self->{path}"
1381     }
1382    
1383     # the displayed name, this is a one way mapping
1384     sub visible_name {
1385     &as_string
1386     }
1387    
1388     # the original (read-only) location
1389     sub load_path {
1390     my ($self) = @_;
1391    
1392 root 1.254 "$MAPDIR/$self->{path}.map"
1393 root 1.166 }
1394    
1395     # the temporary/swap location
1396     sub save_path {
1397     my ($self) = @_;
1398    
1399 root 1.167 (my $path = $_[0]{path}) =~ s/\//$PATH_SEP/g;
1400 root 1.254 "$TMPDIR/$path.map"
1401 root 1.166 }
1402    
1403     # the unique path, undef == no special unique path
1404     sub uniq_path {
1405     my ($self) = @_;
1406    
1407 root 1.167 (my $path = $_[0]{path}) =~ s/\//$PATH_SEP/g;
1408 root 1.253 "$UNIQUEDIR/$path"
1409 root 1.166 }
1410    
1411 root 1.110 # and all this just because we cannot iterate over
1412     # all maps in C++...
1413     sub change_all_map_light {
1414     my ($change) = @_;
1415    
1416 root 1.122 $_->change_map_light ($change)
1417     for grep $_->outdoor, values %cf::MAP;
1418 root 1.110 }
1419    
1420 root 1.166 sub unlink_save {
1421     my ($self) = @_;
1422    
1423     utf8::encode (my $save = $self->save_path);
1424 root 1.170 IO::AIO::aioreq_pri 4; Coro::AIO::aio_unlink $save;
1425     IO::AIO::aioreq_pri 4; Coro::AIO::aio_unlink "$save.pst";
1426 root 1.166 }
1427    
1428     sub load_header_from($) {
1429     my ($self, $path) = @_;
1430 root 1.110
1431     utf8::encode $path;
1432 root 1.200 #aio_open $path, O_RDONLY, 0
1433     # or return;
1434 root 1.110
1435 root 1.166 $self->_load_header ($path)
1436 root 1.110 or return;
1437    
1438 root 1.166 $self->{load_path} = $path;
1439 root 1.135
1440 root 1.166 1
1441     }
1442 root 1.110
1443 root 1.188 sub load_header_orig {
1444 root 1.166 my ($self) = @_;
1445 root 1.110
1446 root 1.166 $self->load_header_from ($self->load_path)
1447 root 1.110 }
1448    
1449 root 1.188 sub load_header_temp {
1450 root 1.166 my ($self) = @_;
1451 root 1.110
1452 root 1.166 $self->load_header_from ($self->save_path)
1453     }
1454 root 1.110
1455 root 1.188 sub prepare_temp {
1456     my ($self) = @_;
1457    
1458     $self->last_access ((delete $self->{last_access})
1459     || $cf::RUNTIME); #d#
1460     # safety
1461     $self->{instantiate_time} = $cf::RUNTIME
1462     if $self->{instantiate_time} > $cf::RUNTIME;
1463     }
1464    
1465     sub prepare_orig {
1466     my ($self) = @_;
1467    
1468     $self->{load_original} = 1;
1469     $self->{instantiate_time} = $cf::RUNTIME;
1470     $self->last_access ($cf::RUNTIME);
1471     $self->instantiate;
1472     }
1473    
1474 root 1.166 sub load_header {
1475     my ($self) = @_;
1476 root 1.110
1477 root 1.188 if ($self->load_header_temp) {
1478     $self->prepare_temp;
1479 root 1.166 } else {
1480 root 1.188 $self->load_header_orig
1481 root 1.166 or return;
1482 root 1.188 $self->prepare_orig;
1483 root 1.166 }
1484 root 1.120
1485 root 1.238 $self->default_region (cf::region::find_by_path $self->{path})
1486     unless $self->default_region;
1487    
1488 root 1.166 1
1489     }
1490 root 1.110
1491 root 1.166 sub find;
1492     sub find {
1493     my ($path, $origin) = @_;
1494 root 1.134
1495 root 1.166 $path = normalise $path, $origin && $origin->path;
1496 root 1.110
1497 root 1.166 cf::lock_wait "map_find:$path";
1498 root 1.110
1499 root 1.166 $cf::MAP{$path} || do {
1500     my $guard = cf::lock_acquire "map_find:$path";
1501     my $map = new_from_path cf::map $path
1502     or return;
1503 root 1.110
1504 root 1.116 $map->{last_save} = $cf::RUNTIME;
1505 root 1.110
1506 root 1.166 $map->load_header
1507     or return;
1508 root 1.134
1509 root 1.195 if ($map->should_reset && 0) {#d#TODO# disabled, crashy (locking issue?)
1510 root 1.185 # doing this can freeze the server in a sync job, obviously
1511     #$cf::WAIT_FOR_TICK->wait;
1512 root 1.112 $map->reset;
1513 root 1.123 undef $guard;
1514 root 1.192 return find $path;
1515 root 1.112 }
1516 root 1.110
1517 root 1.166 $cf::MAP{$path} = $map
1518 root 1.110 }
1519     }
1520    
1521 root 1.188 sub pre_load { }
1522     sub post_load { }
1523    
1524 root 1.110 sub load {
1525     my ($self) = @_;
1526    
1527 root 1.196 local $self->{deny_reset} = 1; # loading can take a long time
1528    
1529 root 1.120 my $path = $self->{path};
1530    
1531 root 1.256 {
1532     my $guard = cf::lock_acquire "map_load:$path";
1533    
1534     return if $self->in_memory != cf::MAP_SWAPPED;
1535 root 1.110
1536 root 1.256 $self->in_memory (cf::MAP_LOADING);
1537 root 1.110
1538 root 1.256 $self->alloc;
1539 root 1.188
1540 root 1.256 $self->pre_load;
1541     Coro::cede;
1542 root 1.188
1543 root 1.256 $self->_load_objects ($self->{load_path}, 1)
1544     or return;
1545 root 1.110
1546 root 1.256 $self->set_object_flag (cf::FLAG_OBJ_ORIGINAL, 1)
1547     if delete $self->{load_original};
1548 root 1.111
1549 root 1.256 if (my $uniq = $self->uniq_path) {
1550     utf8::encode $uniq;
1551     if (aio_open $uniq, O_RDONLY, 0) {
1552     $self->clear_unique_items;
1553     $self->_load_objects ($uniq, 0);
1554     }
1555 root 1.110 }
1556    
1557 root 1.166 Coro::cede;
1558 root 1.256 # now do the right thing for maps
1559     $self->link_multipart_objects;
1560 root 1.110 $self->difficulty ($self->estimate_difficulty)
1561     unless $self->difficulty;
1562 root 1.166 Coro::cede;
1563 root 1.256
1564     unless ($self->{deny_activate}) {
1565     $self->decay_objects;
1566     $self->fix_auto_apply;
1567     $self->update_buttons;
1568     Coro::cede;
1569     $self->set_darkness_map;
1570     Coro::cede;
1571     $self->activate;
1572     }
1573    
1574     $self->in_memory (cf::MAP_IN_MEMORY);
1575 root 1.110 }
1576    
1577 root 1.188 $self->post_load;
1578 root 1.166 }
1579    
1580     sub customise_for {
1581     my ($self, $ob) = @_;
1582    
1583     return find "~" . $ob->name . "/" . $self->{path}
1584     if $self->per_player;
1585 root 1.134
1586 root 1.166 $self
1587 root 1.110 }
1588    
1589 root 1.157 # find and load all maps in the 3x3 area around a map
1590     sub load_diag {
1591     my ($map) = @_;
1592    
1593     my @diag; # diagonal neighbours
1594    
1595     for (0 .. 3) {
1596     my $neigh = $map->tile_path ($_)
1597     or next;
1598     $neigh = find $neigh, $map
1599     or next;
1600     $neigh->load;
1601    
1602     push @diag, [$neigh->tile_path (($_ + 3) % 4), $neigh],
1603     [$neigh->tile_path (($_ + 1) % 4), $neigh];
1604     }
1605    
1606     for (@diag) {
1607     my $neigh = find @$_
1608     or next;
1609     $neigh->load;
1610     }
1611     }
1612    
1613 root 1.133 sub find_sync {
1614 root 1.110 my ($path, $origin) = @_;
1615    
1616 root 1.157 cf::sync_job { find $path, $origin }
1617 root 1.133 }
1618    
1619     sub do_load_sync {
1620     my ($map) = @_;
1621 root 1.110
1622 root 1.133 cf::sync_job { $map->load };
1623 root 1.110 }
1624    
1625 root 1.157 our %MAP_PREFETCH;
1626 root 1.183 our $MAP_PREFETCHER = undef;
1627 root 1.157
1628     sub find_async {
1629     my ($path, $origin) = @_;
1630    
1631 root 1.166 $path = normalise $path, $origin && $origin->{path};
1632 root 1.157
1633 root 1.166 if (my $map = $cf::MAP{$path}) {
1634 root 1.157 return $map if $map->in_memory == cf::MAP_IN_MEMORY;
1635     }
1636    
1637 root 1.183 undef $MAP_PREFETCH{$path};
1638     $MAP_PREFETCHER ||= cf::async {
1639     while (%MAP_PREFETCH) {
1640     for my $path (keys %MAP_PREFETCH) {
1641     my $map = find $path
1642     or next;
1643     $map->load;
1644    
1645     delete $MAP_PREFETCH{$path};
1646     }
1647     }
1648     undef $MAP_PREFETCHER;
1649     };
1650 root 1.189 $MAP_PREFETCHER->prio (6);
1651 root 1.157
1652     ()
1653     }
1654    
1655 root 1.110 sub save {
1656     my ($self) = @_;
1657    
1658 root 1.137 my $lock = cf::lock_acquire "map_data:" . $self->path;
1659    
1660 root 1.110 $self->{last_save} = $cf::RUNTIME;
1661    
1662     return unless $self->dirty;
1663    
1664 root 1.166 my $save = $self->save_path; utf8::encode $save;
1665     my $uniq = $self->uniq_path; utf8::encode $uniq;
1666 root 1.117
1667 root 1.110 $self->{load_path} = $save;
1668    
1669     return if $self->{deny_save};
1670    
1671 root 1.132 local $self->{last_access} = $self->last_access;#d#
1672    
1673 root 1.143 cf::async {
1674     $_->contr->save for $self->players;
1675     };
1676    
1677 root 1.110 if ($uniq) {
1678 root 1.166 $self->_save_objects ($save, cf::IO_HEADER | cf::IO_OBJECTS);
1679     $self->_save_objects ($uniq, cf::IO_UNIQUES);
1680 root 1.110 } else {
1681 root 1.166 $self->_save_objects ($save, cf::IO_HEADER | cf::IO_OBJECTS | cf::IO_UNIQUES);
1682 root 1.110 }
1683     }
1684    
1685     sub swap_out {
1686     my ($self) = @_;
1687    
1688 root 1.130 # save first because save cedes
1689     $self->save;
1690    
1691 root 1.137 my $lock = cf::lock_acquire "map_data:" . $self->path;
1692    
1693 root 1.110 return if $self->players;
1694     return if $self->in_memory != cf::MAP_IN_MEMORY;
1695     return if $self->{deny_save};
1696    
1697     $self->clear;
1698     $self->in_memory (cf::MAP_SWAPPED);
1699     }
1700    
1701 root 1.112 sub reset_at {
1702     my ($self) = @_;
1703 root 1.110
1704     # TODO: safety, remove and allow resettable per-player maps
1705 root 1.169 return 1e99 if $self->isa ("ext::map_per_player");#d#
1706 root 1.114 return 1e99 if $self->{deny_reset};
1707 root 1.110
1708 root 1.112 my $time = $self->fixed_resettime ? $self->{instantiate_time} : $self->last_access;
1709 root 1.114 my $to = List::Util::min $MAX_RESET, $self->reset_timeout || $DEFAULT_RESET;
1710 root 1.110
1711 root 1.112 $time + $to
1712     }
1713    
1714     sub should_reset {
1715     my ($self) = @_;
1716    
1717     $self->reset_at <= $cf::RUNTIME
1718 root 1.111 }
1719    
1720 root 1.110 sub reset {
1721     my ($self) = @_;
1722    
1723 root 1.167 my $lock = cf::lock_acquire "map_data:$self->{path}";
1724 root 1.137
1725 root 1.110 return if $self->players;
1726 root 1.166 return if $self->isa ("ext::map_per_player");#d#
1727 root 1.110
1728     warn "resetting map ", $self->path;#d#
1729    
1730 root 1.210 $self->in_memory (cf::MAP_SWAPPED);
1731    
1732     # need to save uniques path
1733     unless ($self->{deny_save}) {
1734     my $uniq = $self->uniq_path; utf8::encode $uniq;
1735    
1736     $self->_save_objects ($uniq, cf::IO_UNIQUES)
1737     if $uniq;
1738     }
1739    
1740 root 1.111 delete $cf::MAP{$self->path};
1741 root 1.110
1742 root 1.167 $self->clear;
1743    
1744 root 1.110 $_->clear_links_to ($self) for values %cf::MAP;
1745    
1746 root 1.166 $self->unlink_save;
1747 root 1.111 $self->destroy;
1748 root 1.110 }
1749    
1750 root 1.114 my $nuke_counter = "aaaa";
1751    
1752     sub nuke {
1753     my ($self) = @_;
1754    
1755 root 1.174 delete $cf::MAP{$self->path};
1756    
1757     $self->unlink_save;
1758    
1759     bless $self, "cf::map";
1760     delete $self->{deny_reset};
1761 root 1.114 $self->{deny_save} = 1;
1762     $self->reset_timeout (1);
1763 root 1.174 $self->path ($self->{path} = "{nuke}/" . ($nuke_counter++));
1764    
1765     $cf::MAP{$self->path} = $self;
1766    
1767 root 1.114 $self->reset; # polite request, might not happen
1768     }
1769    
1770 root 1.158 =item cf::map::unique_maps
1771    
1772 root 1.166 Returns an arrayref of paths of all shared maps that have
1773 root 1.158 instantiated unique items. May block.
1774    
1775     =cut
1776    
1777     sub unique_maps() {
1778 root 1.253 my $files = aio_readdir $UNIQUEDIR
1779 root 1.158 or return;
1780    
1781     my @paths;
1782    
1783     for (@$files) {
1784     utf8::decode $_;
1785     next if /\.pst$/;
1786     next unless /^$PATH_SEP/o;
1787    
1788 root 1.199 push @paths, cf::map::normalise $_;
1789 root 1.158 }
1790    
1791     \@paths
1792     }
1793    
1794 root 1.155 package cf;
1795    
1796     =back
1797    
1798     =head3 cf::object
1799    
1800     =cut
1801    
1802     package cf::object;
1803    
1804     =over 4
1805    
1806     =item $ob->inv_recursive
1807 root 1.110
1808 root 1.155 Returns the inventory of the object _and_ their inventories, recursively.
1809 root 1.110
1810 root 1.155 =cut
1811 root 1.144
1812 root 1.155 sub inv_recursive_;
1813     sub inv_recursive_ {
1814     map { $_, inv_recursive_ $_->inv } @_
1815     }
1816 root 1.110
1817 root 1.155 sub inv_recursive {
1818     inv_recursive_ inv $_[0]
1819 root 1.110 }
1820    
1821     package cf;
1822    
1823     =back
1824    
1825 root 1.95 =head3 cf::object::player
1826    
1827     =over 4
1828    
1829 root 1.79 =item $player_object->reply ($npc, $msg[, $flags])
1830 root 1.28
1831     Sends a message to the player, as if the npc C<$npc> replied. C<$npc>
1832     can be C<undef>. Does the right thing when the player is currently in a
1833     dialogue with the given NPC character.
1834    
1835     =cut
1836    
1837 root 1.22 # rough implementation of a future "reply" method that works
1838     # with dialog boxes.
1839 root 1.95 #TODO: the first argument must go, split into a $npc->reply_to ( method
1840 root 1.23 sub cf::object::player::reply($$$;$) {
1841     my ($self, $npc, $msg, $flags) = @_;
1842    
1843     $flags = cf::NDI_BROWN | cf::NDI_UNIQUE unless @_ >= 4;
1844 root 1.22
1845 root 1.24 if ($self->{record_replies}) {
1846     push @{ $self->{record_replies} }, [$npc, $msg, $flags];
1847     } else {
1848     $msg = $npc->name . " says: $msg" if $npc;
1849     $self->message ($msg, $flags);
1850     }
1851 root 1.22 }
1852    
1853 root 1.79 =item $player_object->may ("access")
1854    
1855     Returns wether the given player is authorized to access resource "access"
1856     (e.g. "command_wizcast").
1857    
1858     =cut
1859    
1860     sub cf::object::player::may {
1861     my ($self, $access) = @_;
1862    
1863     $self->flag (cf::FLAG_WIZ) ||
1864     (ref $cf::CFG{"may_$access"}
1865     ? scalar grep $self->name eq $_, @{$cf::CFG{"may_$access"}}
1866     : $cf::CFG{"may_$access"})
1867     }
1868 root 1.70
1869 root 1.115 =item $player_object->enter_link
1870    
1871     Freezes the player and moves him/her to a special map (C<{link}>).
1872    
1873 root 1.166 The player should be reasonably safe there for short amounts of time. You
1874 root 1.115 I<MUST> call C<leave_link> as soon as possible, though.
1875    
1876 root 1.166 Will never block.
1877    
1878 root 1.115 =item $player_object->leave_link ($map, $x, $y)
1879    
1880 root 1.166 Moves the player out of the special C<{link}> map onto the specified
1881     map. If the map is not valid (or omitted), the player will be moved back
1882     to the location he/she was before the call to C<enter_link>, or, if that
1883     fails, to the emergency map position.
1884 root 1.115
1885     Might block.
1886    
1887     =cut
1888    
1889 root 1.166 sub link_map {
1890     unless ($LINK_MAP) {
1891     $LINK_MAP = cf::map::find "{link}"
1892 root 1.196 or cf::cleanup "FATAL: unable to provide {link} map, exiting.";
1893 root 1.166 $LINK_MAP->load;
1894     }
1895    
1896     $LINK_MAP
1897     }
1898    
1899 root 1.110 sub cf::object::player::enter_link {
1900     my ($self) = @_;
1901    
1902 root 1.259 $self->deactivate_recursive;
1903 root 1.258
1904 root 1.166 return if UNIVERSAL::isa $self->map, "ext::map_link";
1905 root 1.110
1906 root 1.120 $self->{_link_pos} ||= [$self->map->{path}, $self->x, $self->y]
1907 root 1.270 if $self->map && $self->map->{path} ne "{link}";
1908 root 1.110
1909 root 1.194 $self->enter_map ($LINK_MAP || link_map, 10, 10);
1910 root 1.110 }
1911    
1912     sub cf::object::player::leave_link {
1913     my ($self, $map, $x, $y) = @_;
1914    
1915 root 1.270 return unless $self->contr->active;
1916    
1917 root 1.110 my $link_pos = delete $self->{_link_pos};
1918    
1919     unless ($map) {
1920     # restore original map position
1921     ($map, $x, $y) = @{ $link_pos || [] };
1922 root 1.133 $map = cf::map::find $map;
1923 root 1.110
1924     unless ($map) {
1925     ($map, $x, $y) = @$EMERGENCY_POSITION;
1926 root 1.133 $map = cf::map::find $map
1927 root 1.110 or die "FATAL: cannot load emergency map\n";
1928     }
1929     }
1930    
1931     ($x, $y) = (-1, -1)
1932     unless (defined $x) && (defined $y);
1933    
1934     # use -1 or undef as default coordinates, not 0, 0
1935     ($x, $y) = ($map->enter_x, $map->enter_y)
1936     if $x <=0 && $y <= 0;
1937    
1938     $map->load;
1939 root 1.157 $map->load_diag;
1940 root 1.110
1941 root 1.143 return unless $self->contr->active;
1942 root 1.110 $self->activate_recursive;
1943 root 1.215
1944     local $self->{_prev_pos} = $link_pos; # ugly hack for rent.ext
1945 root 1.110 $self->enter_map ($map, $x, $y);
1946     }
1947    
1948 root 1.268 =item $player_object->goto ($path, $x, $y[, $check->($map)])
1949    
1950     Moves the player to the given map-path and coordinates by first freezing
1951     her, loading and preparing them map, calling the provided $check callback
1952     that has to return the map if sucecssful, and then unfreezes the player on
1953     the new (success) or old (failed) map position.
1954 root 1.110
1955     =cut
1956    
1957 root 1.270 our $GOTOGEN;
1958    
1959 root 1.136 sub cf::object::player::goto {
1960 root 1.268 my ($self, $path, $x, $y, $check) = @_;
1961    
1962 root 1.270 # do generation counting so two concurrent goto's will be executed in-order
1963     my $gen = $self->{_goto_generation} = ++$GOTOGEN;
1964    
1965 root 1.110 $self->enter_link;
1966    
1967 root 1.140 (async {
1968 root 1.197 my $map = eval {
1969     my $map = cf::map::find $path;
1970 root 1.268
1971     if ($map) {
1972     $map = $map->customise_for ($self);
1973     $map = $check->($map) if $check && $map;
1974     } else {
1975     $self->message ("The exit to '$path' is closed", cf::NDI_UNIQUE | cf::NDI_RED);
1976     }
1977    
1978 root 1.197 $map
1979 root 1.268 };
1980    
1981     if ($@) {
1982     $self->message ("Something went wrong within the server, please report this incident!", cf::NDI_UNIQUE | cf::NDI_RED);
1983     LOG llevError | logBacktrace, Carp::longmess $@;
1984     }
1985 root 1.115
1986 root 1.270 if ($gen == $self->{_goto_generation}) {
1987     delete $self->{_goto_generation};
1988     $self->leave_link ($map, $x, $y);
1989     }
1990 root 1.110 })->prio (1);
1991     }
1992    
1993     =item $player_object->enter_exit ($exit_object)
1994    
1995     =cut
1996    
1997     sub parse_random_map_params {
1998     my ($spec) = @_;
1999    
2000     my $rmp = { # defaults
2001 root 1.181 xsize => (cf::rndm 15, 40),
2002     ysize => (cf::rndm 15, 40),
2003     symmetry => (cf::rndm 1, cf::SYMMETRY_XY),
2004 root 1.182 #layout => string,
2005 root 1.110 };
2006    
2007     for (split /\n/, $spec) {
2008     my ($k, $v) = split /\s+/, $_, 2;
2009    
2010     $rmp->{lc $k} = $v if (length $k) && (length $v);
2011     }
2012    
2013     $rmp
2014     }
2015    
2016     sub prepare_random_map {
2017     my ($exit) = @_;
2018    
2019 root 1.179 my $guard = cf::lock_acquire "exit_prepare:$exit";
2020    
2021 root 1.110 # all this does is basically replace the /! path by
2022     # a new random map path (?random/...) with a seed
2023     # that depends on the exit object
2024    
2025     my $rmp = parse_random_map_params $exit->msg;
2026    
2027     if ($exit->map) {
2028 root 1.198 $rmp->{region} = $exit->region->name;
2029 root 1.110 $rmp->{origin_map} = $exit->map->path;
2030     $rmp->{origin_x} = $exit->x;
2031     $rmp->{origin_y} = $exit->y;
2032     }
2033    
2034     $rmp->{random_seed} ||= $exit->random_seed;
2035    
2036     my $data = cf::to_json $rmp;
2037     my $md5 = Digest::MD5::md5_hex $data;
2038 root 1.253 my $meta = "$RANDOMDIR/$md5.meta";
2039 root 1.110
2040 root 1.177 if (my $fh = aio_open "$meta~", O_WRONLY | O_CREAT, 0666) {
2041 root 1.110 aio_write $fh, 0, (length $data), $data, 0;
2042 root 1.177 undef $fh;
2043     aio_rename "$meta~", $meta;
2044 root 1.110
2045     $exit->slaying ("?random/$md5");
2046     $exit->msg (undef);
2047     }
2048     }
2049    
2050     sub cf::object::player::enter_exit {
2051     my ($self, $exit) = @_;
2052    
2053     return unless $self->type == cf::PLAYER;
2054    
2055 root 1.195 if ($exit->slaying eq "/!") {
2056     #TODO: this should de-fi-ni-te-ly not be a sync-job
2057 root 1.233 # the problem is that $exit might not survive long enough
2058     # so it needs to be done right now, right here
2059 root 1.195 cf::sync_job { prepare_random_map $exit };
2060     }
2061    
2062     my $slaying = cf::map::normalise $exit->slaying, $exit->map && $exit->map->path;
2063     my $hp = $exit->stats->hp;
2064     my $sp = $exit->stats->sp;
2065    
2066 root 1.110 $self->enter_link;
2067    
2068 root 1.140 (async {
2069 root 1.133 $self->deactivate_recursive; # just to be sure
2070 root 1.110 unless (eval {
2071 root 1.195 $self->goto ($slaying, $hp, $sp);
2072 root 1.110
2073     1;
2074     }) {
2075     $self->message ("Something went wrong deep within the crossfire server. "
2076 root 1.233 . "I'll try to bring you back to the map you were before. "
2077     . "Please report this to the dungeon master!",
2078     cf::NDI_UNIQUE | cf::NDI_RED);
2079 root 1.110
2080     warn "ERROR in enter_exit: $@";
2081     $self->leave_link;
2082     }
2083     })->prio (1);
2084     }
2085    
2086 root 1.95 =head3 cf::client
2087    
2088     =over 4
2089    
2090     =item $client->send_drawinfo ($text, $flags)
2091    
2092     Sends a drawinfo packet to the client. Circumvents output buffering so
2093     should not be used under normal circumstances.
2094    
2095 root 1.70 =cut
2096    
2097 root 1.95 sub cf::client::send_drawinfo {
2098     my ($self, $text, $flags) = @_;
2099    
2100     utf8::encode $text;
2101 root 1.225 $self->send_packet (sprintf "drawinfo %d %s", $flags || cf::NDI_BLACK, $text);
2102 root 1.95 }
2103    
2104 root 1.232 =item $client->ext_event ($type, %msg)
2105    
2106     Sends an exti event to the client.
2107    
2108     =cut
2109    
2110     sub cf::client::ext_event($$%) {
2111     my ($self, $type, %msg) = @_;
2112    
2113     $msg{msgtype} = "event_$type";
2114     $self->send_packet ("ext " . cf::to_json \%msg);
2115     }
2116 root 1.95
2117     =item $success = $client->query ($flags, "text", \&cb)
2118    
2119     Queues a query to the client, calling the given callback with
2120     the reply text on a reply. flags can be C<cf::CS_QUERY_YESNO>,
2121     C<cf::CS_QUERY_SINGLECHAR> or C<cf::CS_QUERY_HIDEINPUT> or C<0>.
2122    
2123     Queries can fail, so check the return code. Or don't, as queries will become
2124     reliable at some point in the future.
2125    
2126     =cut
2127    
2128     sub cf::client::query {
2129     my ($self, $flags, $text, $cb) = @_;
2130    
2131     return unless $self->state == ST_PLAYING
2132     || $self->state == ST_SETUP
2133     || $self->state == ST_CUSTOM;
2134    
2135     $self->state (ST_CUSTOM);
2136    
2137     utf8::encode $text;
2138     push @{ $self->{query_queue} }, [(sprintf "query %d %s", $flags, $text), $cb];
2139    
2140     $self->send_packet ($self->{query_queue}[0][0])
2141     if @{ $self->{query_queue} } == 1;
2142     }
2143    
2144     cf::client->attach (
2145     on_reply => sub {
2146     my ($ns, $msg) = @_;
2147    
2148     # this weird shuffling is so that direct followup queries
2149     # get handled first
2150 root 1.128 my $queue = delete $ns->{query_queue}
2151 root 1.129 or return; # be conservative, not sure how that can happen, but we saw a crash here
2152 root 1.95
2153     (shift @$queue)->[1]->($msg);
2154 root 1.202 return unless $ns->valid; # temporary(?) workaround for callback destroying socket
2155 root 1.95
2156     push @{ $ns->{query_queue} }, @$queue;
2157    
2158     if (@{ $ns->{query_queue} } == @$queue) {
2159     if (@$queue) {
2160     $ns->send_packet ($ns->{query_queue}[0][0]);
2161     } else {
2162 root 1.98 $ns->state (ST_PLAYING) if $ns->state == ST_CUSTOM;
2163 root 1.95 }
2164     }
2165     },
2166     );
2167    
2168 root 1.140 =item $client->async (\&cb)
2169 root 1.96
2170     Create a new coroutine, running the specified callback. The coroutine will
2171     be automatically cancelled when the client gets destroyed (e.g. on logout,
2172     or loss of connection).
2173    
2174     =cut
2175    
2176 root 1.140 sub cf::client::async {
2177 root 1.96 my ($self, $cb) = @_;
2178    
2179 root 1.140 my $coro = &Coro::async ($cb);
2180 root 1.103
2181     $coro->on_destroy (sub {
2182 root 1.96 delete $self->{_coro}{$coro+0};
2183 root 1.103 });
2184 root 1.96
2185     $self->{_coro}{$coro+0} = $coro;
2186 root 1.103
2187     $coro
2188 root 1.96 }
2189    
2190     cf::client->attach (
2191     on_destroy => sub {
2192     my ($ns) = @_;
2193    
2194 root 1.97 $_->cancel for values %{ (delete $ns->{_coro}) || {} };
2195 root 1.96 },
2196     );
2197    
2198 root 1.95 =back
2199    
2200 root 1.70
2201     =head2 SAFE SCRIPTING
2202    
2203     Functions that provide a safe environment to compile and execute
2204     snippets of perl code without them endangering the safety of the server
2205     itself. Looping constructs, I/O operators and other built-in functionality
2206     is not available in the safe scripting environment, and the number of
2207 root 1.79 functions and methods that can be called is greatly reduced.
2208 root 1.70
2209     =cut
2210 root 1.23
2211 root 1.42 our $safe = new Safe "safe";
2212 root 1.23 our $safe_hole = new Safe::Hole;
2213    
2214     $SIG{FPE} = 'IGNORE';
2215    
2216     $safe->permit_only (Opcode::opset qw(:base_core :base_mem :base_orig :base_math sort time));
2217    
2218 root 1.25 # here we export the classes and methods available to script code
2219    
2220 root 1.70 =pod
2221    
2222 root 1.228 The following functions and methods are available within a safe environment:
2223 root 1.70
2224 elmex 1.91 cf::object contr pay_amount pay_player map
2225 root 1.70 cf::object::player player
2226     cf::player peaceful
2227 elmex 1.91 cf::map trigger
2228 root 1.70
2229     =cut
2230    
2231 root 1.25 for (
2232 elmex 1.91 ["cf::object" => qw(contr pay_amount pay_player map)],
2233 root 1.25 ["cf::object::player" => qw(player)],
2234     ["cf::player" => qw(peaceful)],
2235 elmex 1.91 ["cf::map" => qw(trigger)],
2236 root 1.25 ) {
2237     no strict 'refs';
2238     my ($pkg, @funs) = @$_;
2239 root 1.41 *{"safe::$pkg\::$_"} = $safe_hole->wrap (\&{"$pkg\::$_"})
2240 root 1.25 for @funs;
2241     }
2242 root 1.23
2243 root 1.70 =over 4
2244    
2245     =item @retval = safe_eval $code, [var => value, ...]
2246    
2247     Compiled and executes the given perl code snippet. additional var/value
2248     pairs result in temporary local (my) scalar variables of the given name
2249     that are available in the code snippet. Example:
2250    
2251     my $five = safe_eval '$first + $second', first => 1, second => 4;
2252    
2253     =cut
2254    
2255 root 1.23 sub safe_eval($;@) {
2256     my ($code, %vars) = @_;
2257    
2258     my $qcode = $code;
2259     $qcode =~ s/"/‟/g; # not allowed in #line filenames
2260     $qcode =~ s/\n/\\n/g;
2261    
2262     local $_;
2263 root 1.41 local @safe::cf::_safe_eval_args = values %vars;
2264 root 1.23
2265 root 1.42 my $eval =
2266 root 1.23 "do {\n"
2267     . "my (" . (join ",", map "\$$_", keys %vars) . ") = \@cf::_safe_eval_args;\n"
2268     . "#line 0 \"{$qcode}\"\n"
2269     . $code
2270     . "\n}"
2271 root 1.25 ;
2272    
2273     sub_generation_inc;
2274 root 1.42 my @res = wantarray ? $safe->reval ($eval) : scalar $safe->reval ($eval);
2275 root 1.25 sub_generation_inc;
2276    
2277 root 1.42 if ($@) {
2278     warn "$@";
2279     warn "while executing safe code '$code'\n";
2280     warn "with arguments " . (join " ", %vars) . "\n";
2281     }
2282    
2283 root 1.25 wantarray ? @res : $res[0]
2284 root 1.23 }
2285    
2286 root 1.69 =item cf::register_script_function $function => $cb
2287    
2288     Register a function that can be called from within map/npc scripts. The
2289     function should be reasonably secure and should be put into a package name
2290     like the extension.
2291    
2292     Example: register a function that gets called whenever a map script calls
2293     C<rent::overview>, as used by the C<rent> extension.
2294    
2295     cf::register_script_function "rent::overview" => sub {
2296     ...
2297     };
2298    
2299     =cut
2300    
2301 root 1.23 sub register_script_function {
2302     my ($fun, $cb) = @_;
2303    
2304     no strict 'refs';
2305 root 1.41 *{"safe::$fun"} = $safe_hole->wrap ($cb);
2306 root 1.23 }
2307    
2308 root 1.70 =back
2309    
2310 root 1.71 =cut
2311    
2312 root 1.23 #############################################################################
2313 root 1.65
2314     =head2 EXTENSION DATABASE SUPPORT
2315    
2316     Crossfire maintains a very simple database for extension use. It can
2317 root 1.250 currently store binary data only (use Compress::LZF::sfreeze_cr/sthaw to
2318     convert to/from binary).
2319 root 1.65
2320     The parameter C<$family> should best start with the name of the extension
2321     using it, it should be unique.
2322    
2323     =over 4
2324    
2325     =item $value = cf::db_get $family => $key
2326    
2327 root 1.208 Returns a single value from the database.
2328 root 1.65
2329     =item cf::db_put $family => $key => $value
2330    
2331 root 1.208 Stores the given C<$value> in the family.
2332 root 1.65
2333     =cut
2334    
2335 root 1.78 our $DB;
2336    
2337 root 1.210 sub db_init {
2338     unless ($DB) {
2339     $DB = BDB::db_create $DB_ENV;
2340 root 1.65
2341 root 1.210 cf::sync_job {
2342     eval {
2343     $DB->set_flags (BDB::CHKSUM);
2344 root 1.65
2345 root 1.210 BDB::db_open $DB, undef, "db", undef, BDB::BTREE,
2346     BDB::CREATE | BDB::AUTO_COMMIT, 0666;
2347     cf::cleanup "db_open(db): $!" if $!;
2348     };
2349     cf::cleanup "db_open(db): $@" if $@;
2350 root 1.208 };
2351 root 1.65 }
2352 root 1.208 }
2353 root 1.65
2354 root 1.208 sub db_get($$) {
2355     my $key = "$_[0]/$_[1]";
2356 root 1.65
2357 root 1.208 cf::sync_job {
2358     BDB::db_get $DB, undef, $key, my $data;
2359 root 1.65
2360 root 1.208 $! ? ()
2361 root 1.250 : $data
2362 root 1.65 }
2363 root 1.208 }
2364 root 1.65
2365 root 1.208 sub db_put($$$) {
2366     BDB::dbreq_pri 4;
2367 root 1.250 BDB::db_put $DB, undef, "$_[0]/$_[1]", $_[2], 0, sub { };
2368 root 1.65 }
2369    
2370 root 1.250 =item cf::cache $id => [$paths...], $processversion => $process
2371 root 1.249
2372     Generic caching function that returns the value of the resource $id,
2373     caching and regenerating as required.
2374    
2375     This function can block.
2376    
2377     =cut
2378    
2379     sub cache {
2380 root 1.250 my ($id, $src, $processversion, $process) = @_;
2381 root 1.249
2382 root 1.250 my $meta =
2383     join "\x00",
2384     $processversion,
2385     map {
2386     aio_stat $_
2387     and Carp::croak "$_: $!";
2388    
2389     ($_, (stat _)[7,9])
2390     } @$src;
2391    
2392     my $dbmeta = db_get cache => "$id/meta";
2393     if ($dbmeta ne $meta) {
2394     # changed, we may need to process
2395    
2396     my @data;
2397     my $md5;
2398    
2399     for (0 .. $#$src) {
2400     0 <= aio_load $src->[$_], $data[$_]
2401     or Carp::croak "$src->[$_]: $!";
2402     }
2403    
2404     # if processing is expensive, check
2405     # checksum first
2406     if (1) {
2407     $md5 =
2408     join "\x00",
2409     $processversion,
2410     map {
2411     Coro::cede;
2412     ($src->[$_], Digest::MD5::md5_hex $data[$_])
2413     } 0.. $#$src;
2414    
2415    
2416     my $dbmd5 = db_get cache => "$id/md5";
2417     if ($dbmd5 eq $md5) {
2418     db_put cache => "$id/meta", $meta;
2419    
2420     return db_get cache => "$id/data";
2421     }
2422     }
2423 root 1.249
2424 root 1.253 my $t1 = Time::HiRes::time;
2425 root 1.250 my $data = $process->(\@data);
2426 root 1.253 my $t2 = Time::HiRes::time;
2427    
2428     warn "cache: '$id' processed in ", $t2 - $t1, "s\n";
2429 root 1.249
2430 root 1.250 db_put cache => "$id/data", $data;
2431     db_put cache => "$id/md5" , $md5;
2432     db_put cache => "$id/meta", $meta;
2433 root 1.249
2434 root 1.250 return $data;
2435 root 1.249 }
2436    
2437 root 1.250 db_get cache => "$id/data"
2438     }
2439    
2440     =item fork_call { }, $args
2441    
2442     Executes the given code block with the given arguments in a seperate
2443     process, returning the results. Everything must be serialisable with
2444     Coro::Storable. May, of course, block. Note that the executed sub may
2445     never block itself or use any form of Event handling.
2446    
2447     =cut
2448    
2449     sub fork_call(&@) {
2450     my ($cb, @args) = @_;
2451    
2452     # socketpair my $fh1, my $fh2, Socket::AF_UNIX, Socket::SOCK_STREAM, Socket::PF_UNSPEC
2453     # or die "socketpair: $!";
2454     pipe my $fh1, my $fh2
2455     or die "pipe: $!";
2456    
2457     if (my $pid = fork) {
2458     close $fh2;
2459    
2460     my $res = (Coro::Handle::unblock $fh1)->readline (undef);
2461     $res = Coro::Storable::thaw $res;
2462    
2463     waitpid $pid, 0; # should not block anymore, we expect the child to simply behave
2464    
2465     die $$res unless "ARRAY" eq ref $res;
2466    
2467     return wantarray ? @$res : $res->[-1];
2468 root 1.249 } else {
2469 root 1.262 reset_signals;
2470 root 1.251 local $SIG{__WARN__};
2471 root 1.262 local $SIG{__DIE__};
2472 root 1.250 eval {
2473     close $fh1;
2474    
2475     my @res = eval { $cb->(@args) };
2476     syswrite $fh2, Coro::Storable::freeze +($@ ? \"$@" : \@res);
2477     };
2478    
2479 root 1.251 warn $@ if $@;
2480 root 1.250 _exit 0;
2481 root 1.249 }
2482 root 1.250 }
2483 root 1.249
2484 root 1.65 #############################################################################
2485 root 1.203 # the server's init and main functions
2486    
2487 root 1.246 sub load_facedata($) {
2488     my ($path) = @_;
2489 root 1.223
2490 root 1.229 warn "loading facedata from $path\n";
2491 root 1.223
2492 root 1.236 my $facedata;
2493     0 < aio_load $path, $facedata
2494 root 1.223 or die "$path: $!";
2495    
2496 root 1.237 $facedata = Coro::Storable::thaw $facedata;
2497 root 1.223
2498 root 1.236 $facedata->{version} == 2
2499 root 1.226 or cf::cleanup "$path: version mismatch, cannot proceed.";
2500    
2501 root 1.236 {
2502     my $faces = $facedata->{faceinfo};
2503    
2504     while (my ($face, $info) = each %$faces) {
2505     my $idx = (cf::face::find $face) || cf::face::alloc $face;
2506 root 1.272 warn "$face $info->{magicmap}\n" if $face =~ /obol/;#d#
2507 root 1.236 cf::face::set $idx, $info->{visibility}, $info->{magicmap};
2508     cf::face::set_data $idx, 0, $info->{data32}, $info->{chksum32};
2509     cf::face::set_data $idx, 1, $info->{data64}, $info->{chksum64};
2510     Coro::cede;
2511     }
2512    
2513     while (my ($face, $info) = each %$faces) {
2514     next unless $info->{smooth};
2515     my $idx = cf::face::find $face
2516     or next;
2517     if (my $smooth = cf::face::find $info->{smooth}) {
2518     cf::face::set_smooth $idx, $smooth, $info->{smoothlevel};
2519     } else {
2520     warn "smooth face '$info->{smooth}' not found for face '$face'";
2521     }
2522     Coro::cede;
2523     }
2524 root 1.223 }
2525    
2526 root 1.236 {
2527     my $anims = $facedata->{animinfo};
2528    
2529     while (my ($anim, $info) = each %$anims) {
2530     cf::anim::set $anim, $info->{frames}, $info->{facings};
2531     Coro::cede;
2532 root 1.225 }
2533 root 1.236
2534     cf::anim::invalidate_all; # d'oh
2535 root 1.225 }
2536    
2537 root 1.223 1
2538     }
2539    
2540 root 1.253 sub reload_regions {
2541     load_resource_file "$MAPDIR/regions"
2542     or die "unable to load regions file\n";
2543     }
2544    
2545 root 1.246 sub reload_facedata {
2546 root 1.253 load_facedata "$DATADIR/facedata"
2547 root 1.246 or die "unable to load facedata\n";
2548     }
2549    
2550     sub reload_archetypes {
2551 root 1.253 load_resource_file "$DATADIR/archetypes"
2552 root 1.246 or die "unable to load archetypes\n";
2553 root 1.241 }
2554    
2555 root 1.246 sub reload_treasures {
2556 root 1.253 load_resource_file "$DATADIR/treasures"
2557 root 1.246 or die "unable to load treasurelists\n";
2558 root 1.241 }
2559    
2560 root 1.223 sub reload_resources {
2561 root 1.245 warn "reloading resource files...\n";
2562    
2563 root 1.246 reload_regions;
2564     reload_facedata;
2565     reload_archetypes;
2566     reload_treasures;
2567 root 1.245
2568     warn "finished reloading resource files\n";
2569 root 1.223 }
2570    
2571     sub init {
2572     reload_resources;
2573 root 1.203 }
2574 root 1.34
2575 root 1.73 sub cfg_load {
2576 root 1.253 open my $fh, "<:utf8", "$CONFDIR/config"
2577 root 1.72 or return;
2578    
2579     local $/;
2580     *CFG = YAML::Syck::Load <$fh>;
2581 root 1.131
2582     $EMERGENCY_POSITION = $CFG{emergency_position} || ["/world/world_105_115", 5, 37];
2583    
2584 root 1.139 $cf::map::MAX_RESET = $CFG{map_max_reset} if exists $CFG{map_max_reset};
2585     $cf::map::DEFAULT_RESET = $CFG{map_default_reset} if exists $CFG{map_default_reset};
2586    
2587 root 1.131 if (exists $CFG{mlockall}) {
2588     eval {
2589 root 1.147 $CFG{mlockall} ? eval "mlockall()" : eval "munlockall()"
2590 root 1.131 and die "WARNING: m(un)lockall failed: $!\n";
2591     };
2592     warn $@ if $@;
2593     }
2594 root 1.72 }
2595    
2596 root 1.39 sub main {
2597 root 1.108 # we must not ever block the main coroutine
2598     local $Coro::idle = sub {
2599 root 1.115 Carp::cluck "FATAL: Coro::idle was called, major BUG, use cf::sync_job!\n";#d#
2600 root 1.175 (async {
2601     Event::one_event;
2602     })->prio (Coro::PRIO_MAX);
2603 root 1.108 };
2604    
2605 root 1.73 cfg_load;
2606 root 1.210 db_init;
2607 root 1.61 load_extensions;
2608 root 1.183
2609     $TICK_WATCHER->start;
2610 root 1.34 Event::loop;
2611     }
2612    
2613     #############################################################################
2614 root 1.155 # initialisation and cleanup
2615    
2616     # install some emergency cleanup handlers
2617     BEGIN {
2618     for my $signal (qw(INT HUP TERM)) {
2619     Event->signal (
2620 root 1.189 reentrant => 0,
2621     data => WF_AUTOCANCEL,
2622     signal => $signal,
2623 root 1.191 prio => 0,
2624 root 1.189 cb => sub {
2625 root 1.155 cf::cleanup "SIG$signal";
2626     },
2627     );
2628     }
2629     }
2630    
2631 root 1.156 sub emergency_save() {
2632 root 1.155 my $freeze_guard = cf::freeze_mainloop;
2633    
2634     warn "enter emergency perl save\n";
2635    
2636     cf::sync_job {
2637     # use a peculiar iteration method to avoid tripping on perl
2638     # refcount bugs in for. also avoids problems with players
2639 root 1.167 # and maps saved/destroyed asynchronously.
2640 root 1.155 warn "begin emergency player save\n";
2641     for my $login (keys %cf::PLAYER) {
2642     my $pl = $cf::PLAYER{$login} or next;
2643     $pl->valid or next;
2644     $pl->save;
2645     }
2646     warn "end emergency player save\n";
2647    
2648     warn "begin emergency map save\n";
2649     for my $path (keys %cf::MAP) {
2650     my $map = $cf::MAP{$path} or next;
2651     $map->valid or next;
2652     $map->save;
2653     }
2654     warn "end emergency map save\n";
2655 root 1.208
2656     warn "begin emergency database checkpoint\n";
2657     BDB::db_env_txn_checkpoint $DB_ENV;
2658     warn "end emergency database checkpoint\n";
2659 root 1.155 };
2660    
2661     warn "leave emergency perl save\n";
2662     }
2663 root 1.22
2664 root 1.211 sub post_cleanup {
2665     my ($make_core) = @_;
2666    
2667     warn Carp::longmess "post_cleanup backtrace"
2668     if $make_core;
2669     }
2670    
2671 root 1.246 sub do_reload_perl() {
2672 root 1.106 # can/must only be called in main
2673     if ($Coro::current != $Coro::main) {
2674 root 1.183 warn "can only reload from main coroutine";
2675 root 1.106 return;
2676     }
2677    
2678 root 1.103 warn "reloading...";
2679    
2680 root 1.212 warn "entering sync_job";
2681    
2682 root 1.213 cf::sync_job {
2683 root 1.214 cf::write_runtime; # external watchdog should not bark
2684 root 1.212 cf::emergency_save;
2685 root 1.214 cf::write_runtime; # external watchdog should not bark
2686 root 1.183
2687 root 1.212 warn "syncing database to disk";
2688     BDB::db_env_txn_checkpoint $DB_ENV;
2689 root 1.106
2690     # if anything goes wrong in here, we should simply crash as we already saved
2691 root 1.65
2692 root 1.183 warn "cancelling all WF_AUTOCANCEL watchers";
2693 root 1.87 for (Event::all_watchers) {
2694     $_->cancel if $_->data & WF_AUTOCANCEL;
2695     }
2696 root 1.65
2697 root 1.183 warn "flushing outstanding aio requests";
2698     for (;;) {
2699 root 1.208 BDB::flush;
2700 root 1.183 IO::AIO::flush;
2701     Coro::cede;
2702 root 1.208 last unless IO::AIO::nreqs || BDB::nreqs;
2703 root 1.183 warn "iterate...";
2704     }
2705    
2706 root 1.223 ++$RELOAD;
2707    
2708 root 1.183 warn "cancelling all extension coros";
2709 root 1.103 $_->cancel for values %EXT_CORO;
2710     %EXT_CORO = ();
2711    
2712 root 1.183 warn "removing commands";
2713 root 1.159 %COMMAND = ();
2714    
2715 root 1.183 warn "removing ext commands";
2716 root 1.159 %EXTCMD = ();
2717    
2718 root 1.183 warn "unloading/nuking all extensions";
2719 root 1.159 for my $pkg (@EXTS) {
2720 root 1.160 warn "... unloading $pkg";
2721 root 1.159
2722     if (my $cb = $pkg->can ("unload")) {
2723     eval {
2724     $cb->($pkg);
2725     1
2726     } or warn "$pkg unloaded, but with errors: $@";
2727     }
2728    
2729 root 1.160 warn "... nuking $pkg";
2730 root 1.159 Symbol::delete_package $pkg;
2731 root 1.65 }
2732    
2733 root 1.183 warn "unloading all perl modules loaded from $LIBDIR";
2734 root 1.65 while (my ($k, $v) = each %INC) {
2735     next unless $v =~ /^\Q$LIBDIR\E\/.*\.pm$/;
2736    
2737 root 1.183 warn "... unloading $k";
2738 root 1.65 delete $INC{$k};
2739    
2740     $k =~ s/\.pm$//;
2741     $k =~ s/\//::/g;
2742    
2743     if (my $cb = $k->can ("unload_module")) {
2744     $cb->();
2745     }
2746    
2747     Symbol::delete_package $k;
2748     }
2749    
2750 root 1.183 warn "getting rid of safe::, as good as possible";
2751 root 1.65 Symbol::delete_package "safe::$_"
2752 root 1.103 for qw(cf::attachable cf::object cf::object::player cf::client cf::player cf::map cf::party cf::region);
2753 root 1.65
2754 root 1.183 warn "unloading cf.pm \"a bit\"";
2755 root 1.65 delete $INC{"cf.pm"};
2756 root 1.252 delete $INC{"cf/pod.pm"};
2757 root 1.65
2758     # don't, removes xs symbols, too,
2759     # and global variables created in xs
2760     #Symbol::delete_package __PACKAGE__;
2761    
2762 root 1.183 warn "unload completed, starting to reload now";
2763    
2764 root 1.103 warn "reloading cf.pm";
2765 root 1.65 require cf;
2766 root 1.100 cf::_connect_to_perl; # nominally unnecessary, but cannot hurt
2767    
2768 root 1.183 warn "loading config and database again";
2769 root 1.73 cf::cfg_load;
2770 root 1.65
2771 root 1.183 warn "loading extensions";
2772 root 1.65 cf::load_extensions;
2773    
2774 root 1.183 warn "reattaching attachments to objects/players";
2775 root 1.222 _global_reattach; # objects, sockets
2776 root 1.183 warn "reattaching attachments to maps";
2777 root 1.144 reattach $_ for values %MAP;
2778 root 1.222 warn "reattaching attachments to players";
2779     reattach $_ for values %PLAYER;
2780 root 1.183
2781 root 1.212 warn "leaving sync_job";
2782 root 1.183
2783 root 1.212 1
2784     } or do {
2785 root 1.106 warn $@;
2786     warn "error while reloading, exiting.";
2787     exit 1;
2788 root 1.212 };
2789 root 1.106
2790 root 1.159 warn "reloaded";
2791 root 1.65 };
2792    
2793 root 1.175 our $RELOAD_WATCHER; # used only during reload
2794    
2795 root 1.246 sub reload_perl() {
2796     # doing reload synchronously and two reloads happen back-to-back,
2797     # coro crashes during coro_state_free->destroy here.
2798    
2799     $RELOAD_WATCHER ||= Event->timer (
2800     reentrant => 0,
2801     after => 0,
2802     data => WF_AUTOCANCEL,
2803     cb => sub {
2804     do_reload_perl;
2805     undef $RELOAD_WATCHER;
2806     },
2807     );
2808     }
2809    
2810 root 1.111 register_command "reload" => sub {
2811 root 1.65 my ($who, $arg) = @_;
2812    
2813     if ($who->flag (FLAG_WIZ)) {
2814 root 1.175 $who->message ("reloading server.");
2815 root 1.246 async { reload_perl };
2816 root 1.65 }
2817     };
2818    
2819 root 1.27 unshift @INC, $LIBDIR;
2820 root 1.17
2821 root 1.183 my $bug_warning = 0;
2822    
2823 root 1.239 our @WAIT_FOR_TICK;
2824     our @WAIT_FOR_TICK_BEGIN;
2825    
2826     sub wait_for_tick {
2827 root 1.240 return unless $TICK_WATCHER->is_active;
2828 root 1.241 return if $Coro::current == $Coro::main;
2829    
2830 root 1.239 my $signal = new Coro::Signal;
2831     push @WAIT_FOR_TICK, $signal;
2832     $signal->wait;
2833     }
2834    
2835     sub wait_for_tick_begin {
2836 root 1.240 return unless $TICK_WATCHER->is_active;
2837 root 1.241 return if $Coro::current == $Coro::main;
2838    
2839 root 1.239 my $signal = new Coro::Signal;
2840     push @WAIT_FOR_TICK_BEGIN, $signal;
2841     $signal->wait;
2842     }
2843    
2844 root 1.268 my $min = 1e6;#d#
2845     my $avg = 10;
2846 root 1.35 $TICK_WATCHER = Event->timer (
2847 root 1.104 reentrant => 0,
2848 root 1.183 parked => 1,
2849 root 1.191 prio => 0,
2850 root 1.104 at => $NEXT_TICK || $TICK,
2851     data => WF_AUTOCANCEL,
2852     cb => sub {
2853 root 1.183 if ($Coro::current != $Coro::main) {
2854     Carp::cluck "major BUG: server tick called outside of main coro, skipping it"
2855     unless ++$bug_warning > 10;
2856     return;
2857     }
2858    
2859 root 1.265 $NOW = $tick_start = Event::time;
2860 root 1.163
2861 root 1.133 cf::server_tick; # one server iteration
2862 root 1.245
2863 root 1.268 0 && sync_job {#d#
2864     for(1..10) {
2865     my $t = Event::time;
2866     my $map = my $map = new_from_path cf::map "/tmp/x.map"
2867     or die;
2868    
2869     $map->width (50);
2870     $map->height (50);
2871     $map->alloc;
2872     $map->_load_objects ("/tmp/x.map", 1);
2873     my $t = Event::time - $t;
2874    
2875     #next unless $t < 0.0013;#d#
2876     if ($t < $min) {
2877     $min = $t;
2878     }
2879     $avg = $avg * 0.99 + $t * 0.01;
2880     }
2881     warn "XXXXXXXXXXXXXXXXXX min $min avg $avg\n";#d#
2882     exit 0;
2883     # 2007-05-22 02:33:04.569 min 0.00112509727478027 avg 0.0012259249572477
2884     };
2885    
2886 root 1.133 $RUNTIME += $TICK;
2887 root 1.35 $NEXT_TICK += $TICK;
2888    
2889 root 1.214 if ($NOW >= $NEXT_RUNTIME_WRITE) {
2890     $NEXT_RUNTIME_WRITE = $NOW + 10;
2891     Coro::async_pool {
2892     write_runtime
2893     or warn "ERROR: unable to write runtime file: $!";
2894     };
2895     }
2896    
2897 root 1.191 # my $AFTER = Event::time;
2898     # warn $AFTER - $NOW;#d#
2899 root 1.190
2900 root 1.245 if (my $sig = shift @WAIT_FOR_TICK_BEGIN) {
2901     $sig->send;
2902     }
2903     while (my $sig = shift @WAIT_FOR_TICK) {
2904     $sig->send;
2905     }
2906    
2907 root 1.265 $NOW = Event::time;
2908    
2909     # if we are delayed by four ticks or more, skip them all
2910     $NEXT_TICK = $NOW if $NOW >= $NEXT_TICK + $TICK * 4;
2911    
2912     $TICK_WATCHER->at ($NEXT_TICK);
2913     $TICK_WATCHER->start;
2914    
2915     $LOAD = ($NOW - $tick_start) / $TICK;
2916     $LOADAVG = $LOADAVG * 0.75 + $LOAD * 0.25;
2917    
2918 root 1.245 _post_tick;
2919 root 1.265
2920    
2921 root 1.35 },
2922     );
2923    
2924 root 1.206 {
2925     BDB::max_poll_time $TICK * 0.1;
2926     $BDB_POLL_WATCHER = Event->io (
2927     reentrant => 0,
2928     fd => BDB::poll_fileno,
2929     poll => 'r',
2930     prio => 0,
2931     data => WF_AUTOCANCEL,
2932     cb => \&BDB::poll_cb,
2933     );
2934     BDB::min_parallel 8;
2935    
2936     BDB::set_sync_prepare {
2937     my $status;
2938     my $current = $Coro::current;
2939     (
2940     sub {
2941     $status = $!;
2942     $current->ready; undef $current;
2943     },
2944     sub {
2945     Coro::schedule while defined $current;
2946     $! = $status;
2947     },
2948     )
2949     };
2950 root 1.77
2951 root 1.206 unless ($DB_ENV) {
2952     $DB_ENV = BDB::db_env_create;
2953    
2954     cf::sync_job {
2955 root 1.208 eval {
2956     BDB::db_env_open
2957     $DB_ENV,
2958 root 1.253 $BDBDIR,
2959 root 1.208 BDB::INIT_LOCK | BDB::INIT_LOG | BDB::INIT_MPOOL | BDB::INIT_TXN
2960     | BDB::RECOVER | BDB::REGISTER | BDB::USE_ENVIRON | BDB::CREATE,
2961     0666;
2962    
2963 root 1.253 cf::cleanup "db_env_open($BDBDIR): $!" if $!;
2964 root 1.208
2965     $DB_ENV->set_flags (BDB::AUTO_COMMIT | BDB::REGION_INIT | BDB::TXN_NOSYNC, 1);
2966     $DB_ENV->set_lk_detect;
2967     };
2968    
2969     cf::cleanup "db_env_open(db): $@" if $@;
2970 root 1.206 };
2971     }
2972     }
2973    
2974     {
2975     IO::AIO::min_parallel 8;
2976    
2977     undef $Coro::AIO::WATCHER;
2978     IO::AIO::max_poll_time $TICK * 0.1;
2979     $AIO_POLL_WATCHER = Event->io (
2980     reentrant => 0,
2981 root 1.214 data => WF_AUTOCANCEL,
2982 root 1.206 fd => IO::AIO::poll_fileno,
2983     poll => 'r',
2984     prio => 6,
2985     cb => \&IO::AIO::poll_cb,
2986     );
2987     }
2988 root 1.108
2989 root 1.262 my $_log_backtrace;
2990    
2991 root 1.260 sub _log_backtrace {
2992     my ($msg, @addr) = @_;
2993    
2994 root 1.262 $msg =~ s/\n//;
2995 root 1.260
2996 root 1.262 # limit the # of concurrent backtraces
2997     if ($_log_backtrace < 2) {
2998     ++$_log_backtrace;
2999     async {
3000     my @bt = fork_call {
3001     @addr = map { sprintf "%x", $_ } @addr;
3002     my $self = (-f "/proc/$$/exe") ? "/proc/$$/exe" : $^X;
3003     open my $fh, "exec addr2line -C -f -i -e \Q$self\E @addr 2>&1 |"
3004     or die "addr2line: $!";
3005    
3006     my @funcs;
3007     my @res = <$fh>;
3008     chomp for @res;
3009     while (@res) {
3010     my ($func, $line) = splice @res, 0, 2, ();
3011     push @funcs, "[$func] $line";
3012     }
3013 root 1.260
3014 root 1.262 @funcs
3015     };
3016 root 1.260
3017 root 1.262 LOG llevInfo, "[ABT] $msg\n";
3018     LOG llevInfo, "[ABT] $_\n" for @bt;
3019     --$_log_backtrace;
3020     };
3021     } else {
3022 root 1.260 LOG llevInfo, "[ABT] $msg\n";
3023 root 1.262 LOG llevInfo, "[ABT] [suppressed]\n";
3024     }
3025 root 1.260 }
3026    
3027 root 1.249 # load additional modules
3028     use cf::pod;
3029    
3030 root 1.125 END { cf::emergency_save }
3031    
3032 root 1.1 1
3033