ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.250
Committed: Wed Apr 18 14:24:10 2007 UTC (17 years, 1 month ago) by root
Branch: MAIN
Changes since 1.249: +99 -38 lines
Log Message:
- implement two new helper functions:
  cf::cache => load and process a file, caching the result in the db
  cf::fork_call => execute a given sub asynchronously (e.g. for cpu jobs)
- make use of it in ext/map-world.ext, greatly speeding up worldmap
  loading.
- preliminary garbage added to cf::pod.

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