ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.160
Committed: Wed Jan 10 22:54:06 2007 UTC (17 years, 4 months ago) by root
Branch: MAIN
Changes since 1.159: +3 -2 lines
Log Message:
beautify

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