ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.157
Committed: Wed Jan 10 01:16:54 2007 UTC (17 years, 4 months ago) by root
Branch: MAIN
Changes since 1.156: +58 -1 lines
Log Message:
aggressively prefetch tiled maps around the player
- prefetch maps on every tick
- prefetch maps and block the player when entering maps

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