ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.207
Committed: Sun Feb 11 22:42:09 2007 UTC (17 years, 3 months ago) by root
Branch: MAIN
Changes since 1.206: +1 -0 lines
Log Message:
folks, we have a database environment to our disposal now

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