ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.204
Committed: Fri Feb 2 19:59:22 2007 UTC (17 years, 3 months ago) by root
Branch: MAIN
Changes since 1.203: +4 -2 lines
Log Message:
disable fsync for normal map writes - problems with that should be rare

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