ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.161
Committed: Thu Jan 11 00:16:58 2007 UTC (17 years, 5 months ago) by root
Branch: MAIN
Changes since 1.160: +59 -35 lines
Log Message:
- start pluggable map types framework
- mvoe random map handling out of the core

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