ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.163
Committed: Thu Jan 11 01:01:56 2007 UTC (17 years, 4 months ago) by root
Branch: MAIN
Changes since 1.162: +2 -0 lines
Log Message:
reset random maps regularly

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.162
478 root 1.108 $path
479     }
480    
481     # the original (read-only) location
482     sub load_path {
483     my ($self) = @_;
484    
485     sprintf "%s/%s/%s", cf::datadir, cf::mapdir, $self->{path}
486     }
487    
488     # the temporary/swap location
489     sub save_path {
490     my ($self) = @_;
491    
492 root 1.162 $self->{user_rel}
493     ? sprintf "%s/%s/%s/%s", cf::localdir, cf::playerdir, $self->{user}, $self->_escaped_path
494     : sprintf "%s/%s/%s", cf::localdir, cf::tmpdir, $self->_escaped_path
495 root 1.108 }
496    
497 root 1.161 # the unique path, undef == no special unique path
498 root 1.108 sub uniq_path {
499     my ($self) = @_;
500    
501 root 1.161 sprintf "%s/%s/%s", cf::localdir, cf::uniquedir, $self->_escaped_path
502     }
503    
504     # this is somewhat ugly, but style maps do need special treatment
505     sub is_style_map {
506     $_[0]{path} =~ m{^/styles/}
507 root 1.108 }
508    
509 root 1.161 sub load_orig {
510 root 1.108 my ($self) = @_;
511    
512 root 1.161 &cf::map::load_map_header ($self->load_path)
513 root 1.108 }
514    
515 root 1.161 sub load_temp {
516     my ($self) = @_;
517    
518     &cf::map::load_map_header ($self->save_path)
519 root 1.108 }
520    
521     package cf;
522    
523     #############################################################################
524    
525 root 1.93 =head2 ATTACHABLE OBJECTS
526    
527 root 1.94 Many objects in crossfire are so-called attachable objects. That means you can
528     attach callbacks/event handlers (a collection of which is called an "attachment")
529     to it. All such attachable objects support the following methods.
530    
531     In the following description, CLASS can be any of C<global>, C<object>
532     C<player>, C<client> or C<map> (i.e. the attachable objects in
533     crossfire+).
534 root 1.55
535     =over 4
536    
537 root 1.94 =item $attachable->attach ($attachment, key => $value...)
538    
539     =item $attachable->detach ($attachment)
540    
541     Attach/detach a pre-registered attachment to a specific object and give it
542     the specified key/value pairs as arguments.
543    
544     Example, attach a minesweeper attachment to the given object, making it a
545     10x10 minesweeper game:
546 root 1.46
547 root 1.94 $obj->attach (minesweeper => width => 10, height => 10);
548 root 1.53
549 root 1.93 =item $bool = $attachable->attached ($name)
550 root 1.46
551 root 1.93 Checks wether the named attachment is currently attached to the object.
552 root 1.46
553 root 1.94 =item cf::CLASS->attach ...
554 root 1.46
555 root 1.94 =item cf::CLASS->detach ...
556 root 1.92
557 root 1.94 Define an anonymous attachment and attach it to all objects of the given
558     CLASS. See the next function for an explanation of its arguments.
559 root 1.92
560 root 1.93 You can attach to global events by using the C<cf::global> class.
561 root 1.92
562 root 1.94 Example, log all player logins:
563    
564     cf::player->attach (
565     on_login => sub {
566     my ($pl) = @_;
567     ...
568     },
569     );
570    
571     Example, attach to the jeweler skill:
572    
573     cf::object->attach (
574     type => cf::SKILL,
575     subtype => cf::SK_JEWELER,
576     on_use_skill => sub {
577     my ($sk, $ob, $part, $dir, $msg) = @_;
578     ...
579     },
580     );
581    
582     =item cf::CLASS::attachment $name, ...
583    
584     Register an attachment by C<$name> through which attachable objects of the
585     given CLASS can refer to this attachment.
586    
587     Some classes such as crossfire maps and objects can specify attachments
588     that are attached at load/instantiate time, thus the need for a name.
589    
590     These calls expect any number of the following handler/hook descriptions:
591 root 1.46
592     =over 4
593    
594     =item prio => $number
595    
596     Set the priority for all following handlers/hooks (unless overwritten
597     by another C<prio> setting). Lower priority handlers get executed
598     earlier. The default priority is C<0>, and many built-in handlers are
599     registered at priority C<-1000>, so lower priorities should not be used
600     unless you know what you are doing.
601    
602 root 1.93 =item type => $type
603    
604     (Only for C<< cf::object->attach >> calls), limits the attachment to the
605     given type of objects only (the additional parameter C<subtype> can be
606     used to further limit to the given subtype).
607    
608 root 1.46 =item on_I<event> => \&cb
609    
610     Call the given code reference whenever the named event happens (event is
611     something like C<instantiate>, C<apply>, C<use_skill> and so on, and which
612     handlers are recognised generally depends on the type of object these
613     handlers attach to).
614    
615     See F<include/eventinc.h> for the full list of events supported, and their
616     class.
617    
618     =item package => package::
619    
620     Look for sub functions of the name C<< on_I<event> >> in the given
621     package and register them. Only handlers for eevents supported by the
622     object/class are recognised.
623    
624     =back
625    
626 root 1.94 Example, define an attachment called "sockpuppet" that calls the given
627     event handler when a monster attacks:
628    
629     cf::object::attachment sockpuppet =>
630     on_skill_attack => sub {
631     my ($self, $victim) = @_;
632     ...
633     }
634     }
635    
636 root 1.96 =item $attachable->valid
637    
638     Just because you have a perl object does not mean that the corresponding
639     C-level object still exists. If you try to access an object that has no
640     valid C counterpart anymore you get an exception at runtime. This method
641     can be used to test for existence of the C object part without causing an
642     exception.
643    
644 root 1.39 =cut
645    
646 root 1.40 # the following variables are defined in .xs and must not be re-created
647 root 1.100 our @CB_GLOBAL = (); # registry for all global events
648     our @CB_ATTACHABLE = (); # registry for all attachables
649     our @CB_OBJECT = (); # all objects (should not be used except in emergency)
650     our @CB_PLAYER = ();
651     our @CB_CLIENT = ();
652     our @CB_TYPE = (); # registry for type (cf-object class) based events
653     our @CB_MAP = ();
654 root 1.39
655 root 1.45 my %attachment;
656    
657 root 1.93 sub _attach_cb($$$$) {
658     my ($registry, $event, $prio, $cb) = @_;
659 root 1.39
660     use sort 'stable';
661    
662     $cb = [$prio, $cb];
663    
664     @{$registry->[$event]} = sort
665     { $a->[0] cmp $b->[0] }
666     @{$registry->[$event] || []}, $cb;
667     }
668    
669 root 1.100 # hack
670     my %attachable_klass = map +($_ => 1), KLASS_OBJECT, KLASS_CLIENT, KLASS_PLAYER, KLASS_MAP;
671    
672 root 1.39 # attach handles attaching event callbacks
673     # the only thing the caller has to do is pass the correct
674     # registry (== where the callback attaches to).
675 root 1.93 sub _attach {
676 root 1.45 my ($registry, $klass, @arg) = @_;
677 root 1.39
678 root 1.93 my $object_type;
679 root 1.39 my $prio = 0;
680     my %cb_id = map +("on_" . lc $EVENT[$_][0], $_) , grep $EVENT[$_][1] == $klass, 0 .. $#EVENT;
681    
682 root 1.100 #TODO: get rid of this hack
683     if ($attachable_klass{$klass}) {
684     %cb_id = (%cb_id, map +("on_" . lc $EVENT[$_][0], $_) , grep $EVENT[$_][1] == KLASS_ATTACHABLE, 0 .. $#EVENT);
685     }
686    
687 root 1.45 while (@arg) {
688     my $type = shift @arg;
689 root 1.39
690     if ($type eq "prio") {
691 root 1.45 $prio = shift @arg;
692 root 1.39
693 root 1.93 } elsif ($type eq "type") {
694     $object_type = shift @arg;
695     $registry = $CB_TYPE[$object_type] ||= [];
696    
697     } elsif ($type eq "subtype") {
698     defined $object_type or Carp::croak "subtype specified without type";
699     my $object_subtype = shift @arg;
700     $registry = $CB_TYPE[$object_type + $object_subtype * NUM_SUBTYPES] ||= [];
701    
702 root 1.39 } elsif ($type eq "package") {
703 root 1.45 my $pkg = shift @arg;
704 root 1.39
705     while (my ($name, $id) = each %cb_id) {
706     if (my $cb = $pkg->can ($name)) {
707 root 1.93 _attach_cb $registry, $id, $prio, $cb;
708 root 1.39 }
709     }
710    
711     } elsif (exists $cb_id{$type}) {
712 root 1.93 _attach_cb $registry, $cb_id{$type}, $prio, shift @arg;
713 root 1.39
714     } elsif (ref $type) {
715     warn "attaching objects not supported, ignoring.\n";
716    
717     } else {
718 root 1.45 shift @arg;
719 root 1.39 warn "attach argument '$type' not supported, ignoring.\n";
720     }
721     }
722     }
723    
724 root 1.93 sub _object_attach {
725 root 1.48 my ($obj, $name, %arg) = @_;
726 root 1.46
727 root 1.55 return if exists $obj->{_attachment}{$name};
728    
729 root 1.46 if (my $attach = $attachment{$name}) {
730     my $registry = $obj->registry;
731    
732 root 1.47 for (@$attach) {
733     my ($klass, @attach) = @$_;
734 root 1.93 _attach $registry, $klass, @attach;
735 root 1.47 }
736 root 1.46
737 root 1.48 $obj->{$name} = \%arg;
738 root 1.46 } else {
739     warn "object uses attachment '$name' that is not available, postponing.\n";
740     }
741    
742 root 1.50 $obj->{_attachment}{$name} = undef;
743 root 1.46 }
744    
745 root 1.93 sub cf::attachable::attach {
746     if (ref $_[0]) {
747     _object_attach @_;
748     } else {
749     _attach shift->_attach_registry, @_;
750     }
751 root 1.55 };
752 root 1.46
753 root 1.54 # all those should be optimised
754 root 1.93 sub cf::attachable::detach {
755 root 1.54 my ($obj, $name) = @_;
756 root 1.46
757 root 1.93 if (ref $obj) {
758     delete $obj->{_attachment}{$name};
759     reattach ($obj);
760     } else {
761     Carp::croak "cannot, currently, detach class attachments";
762     }
763 root 1.55 };
764    
765 root 1.93 sub cf::attachable::attached {
766 root 1.55 my ($obj, $name) = @_;
767    
768     exists $obj->{_attachment}{$name}
769 root 1.39 }
770    
771 root 1.100 for my $klass (qw(ATTACHABLE GLOBAL OBJECT PLAYER CLIENT MAP)) {
772 root 1.93 eval "#line " . __LINE__ . " 'cf.pm'
773     sub cf::\L$klass\E::_attach_registry {
774     (\\\@CB_$klass, KLASS_$klass)
775     }
776 root 1.45
777 root 1.93 sub cf::\L$klass\E::attachment {
778     my \$name = shift;
779 root 1.39
780 root 1.93 \$attachment{\$name} = [[KLASS_$klass, \@_]];
781     }
782     ";
783     die if $@;
784 root 1.52 }
785    
786 root 1.39 our $override;
787 root 1.45 our @invoke_results = (); # referenced from .xs code. TODO: play tricks with reify and mortals?
788 root 1.39
789 root 1.45 sub override {
790     $override = 1;
791     @invoke_results = ();
792 root 1.39 }
793    
794 root 1.45 sub do_invoke {
795 root 1.39 my $event = shift;
796 root 1.40 my $callbacks = shift;
797 root 1.39
798 root 1.45 @invoke_results = ();
799    
800 root 1.39 local $override;
801    
802 root 1.40 for (@$callbacks) {
803 root 1.39 eval { &{$_->[1]} };
804    
805     if ($@) {
806     warn "$@";
807 root 1.64 warn "... while processing $EVENT[$event][0](@_) event, skipping processing altogether.\n";
808 root 1.39 override;
809     }
810    
811     return 1 if $override;
812     }
813    
814     0
815     }
816    
817 root 1.96 =item $bool = cf::global::invoke (EVENT_CLASS_XXX, ...)
818 root 1.55
819 root 1.96 =item $bool = $attachable->invoke (EVENT_CLASS_XXX, ...)
820 root 1.55
821 root 1.96 Generate an object-specific event with the given arguments.
822 root 1.55
823 root 1.96 This API is preliminary (most likely, the EVENT_CLASS_xxx prefix will be
824 root 1.55 removed in future versions), and there is no public API to access override
825     results (if you must, access C<@cf::invoke_results> directly).
826    
827     =back
828    
829 root 1.71 =cut
830    
831 root 1.70 #############################################################################
832 root 1.45 # object support
833    
834 root 1.102 sub reattach {
835     # basically do the same as instantiate, without calling instantiate
836     my ($obj) = @_;
837    
838     my $registry = $obj->registry;
839    
840     @$registry = ();
841    
842     delete $obj->{_attachment} unless scalar keys %{ $obj->{_attachment} || {} };
843    
844     for my $name (keys %{ $obj->{_attachment} || {} }) {
845     if (my $attach = $attachment{$name}) {
846     for (@$attach) {
847     my ($klass, @attach) = @$_;
848     _attach $registry, $klass, @attach;
849     }
850     } else {
851     warn "object uses attachment '$name' that is not available, postponing.\n";
852     }
853     }
854     }
855    
856 root 1.100 cf::attachable->attach (
857     prio => -1000000,
858     on_instantiate => sub {
859     my ($obj, $data) = @_;
860 root 1.45
861 root 1.100 $data = from_json $data;
862 root 1.45
863 root 1.100 for (@$data) {
864     my ($name, $args) = @$_;
865 root 1.49
866 root 1.100 $obj->attach ($name, %{$args || {} });
867     }
868     },
869 root 1.102 on_reattach => \&reattach,
870 root 1.100 on_clone => sub {
871     my ($src, $dst) = @_;
872    
873     @{$dst->registry} = @{$src->registry};
874    
875     %$dst = %$src;
876    
877     %{$dst->{_attachment}} = %{$src->{_attachment}}
878     if exists $src->{_attachment};
879     },
880     );
881 root 1.45
882 root 1.46 sub object_freezer_save {
883 root 1.59 my ($filename, $rdata, $objs) = @_;
884 root 1.46
885 root 1.105 sync_job {
886     if (length $$rdata) {
887     warn sprintf "saving %s (%d,%d)\n",
888     $filename, length $$rdata, scalar @$objs;
889 root 1.60
890 root 1.105 if (my $fh = aio_open "$filename~", O_WRONLY | O_CREAT, 0600) {
891 root 1.60 chmod SAVE_MODE, $fh;
892 root 1.105 aio_write $fh, 0, (length $$rdata), $$rdata, 0;
893     aio_fsync $fh;
894 root 1.60 close $fh;
895 root 1.105
896     if (@$objs) {
897     if (my $fh = aio_open "$filename.pst~", O_WRONLY | O_CREAT, 0600) {
898     chmod SAVE_MODE, $fh;
899     my $data = Storable::nfreeze { version => 1, objs => $objs };
900     aio_write $fh, 0, (length $data), $data, 0;
901     aio_fsync $fh;
902     close $fh;
903     aio_rename "$filename.pst~", "$filename.pst";
904     }
905     } else {
906     aio_unlink "$filename.pst";
907     }
908    
909     aio_rename "$filename~", $filename;
910 root 1.60 } else {
911 root 1.105 warn "FATAL: $filename~: $!\n";
912 root 1.60 }
913 root 1.59 } else {
914 root 1.105 aio_unlink $filename;
915     aio_unlink "$filename.pst";
916 root 1.59 }
917 root 1.45 }
918     }
919    
920 root 1.80 sub object_freezer_as_string {
921     my ($rdata, $objs) = @_;
922    
923     use Data::Dumper;
924    
925 root 1.81 $$rdata . Dumper $objs
926 root 1.80 }
927    
928 root 1.46 sub object_thawer_load {
929     my ($filename) = @_;
930    
931 root 1.105 my ($data, $av);
932 root 1.61
933 root 1.105 (aio_load $filename, $data) >= 0
934     or return;
935 root 1.61
936 root 1.105 unless (aio_stat "$filename.pst") {
937     (aio_load "$filename.pst", $av) >= 0
938     or return;
939 root 1.113 $av = eval { (Storable::thaw $av)->{objs} };
940 root 1.61 }
941 root 1.45
942 root 1.118 warn sprintf "loading %s (%d)\n",
943     $filename, length $data, scalar @{$av || []};#d#
944 root 1.105 return ($data, $av);
945 root 1.45 }
946    
947     #############################################################################
948 root 1.85 # command handling &c
949 root 1.39
950 root 1.85 =item cf::register_command $name => \&callback($ob,$args);
951 root 1.1
952 root 1.85 Register a callback for execution when the client sends the user command
953     $name.
954 root 1.5
955 root 1.85 =cut
956 root 1.5
957 root 1.85 sub register_command {
958     my ($name, $cb) = @_;
959 root 1.5
960 root 1.85 my $caller = caller;
961     #warn "registering command '$name/$time' to '$caller'";
962 root 1.1
963 root 1.85 push @{ $COMMAND{$name} }, [$caller, $cb];
964 root 1.1 }
965    
966 root 1.85 =item cf::register_extcmd $name => \&callback($pl,$packet);
967 root 1.1
968 root 1.85 Register a callbackf ro execution when the client sends an extcmd packet.
969 root 1.1
970 root 1.85 If the callback returns something, it is sent back as if reply was being
971     called.
972 root 1.1
973 root 1.85 =cut
974 root 1.1
975 root 1.16 sub register_extcmd {
976     my ($name, $cb) = @_;
977    
978 root 1.159 $EXTCMD{$name} = $cb;
979 root 1.16 }
980    
981 root 1.93 cf::player->attach (
982 root 1.85 on_command => sub {
983     my ($pl, $name, $params) = @_;
984    
985     my $cb = $COMMAND{$name}
986     or return;
987    
988     for my $cmd (@$cb) {
989     $cmd->[1]->($pl->ob, $params);
990     }
991    
992     cf::override;
993     },
994     on_extcmd => sub {
995     my ($pl, $buf) = @_;
996    
997     my $msg = eval { from_json $buf };
998    
999     if (ref $msg) {
1000     if (my $cb = $EXTCMD{$msg->{msgtype}}) {
1001 root 1.159 if (my %reply = $cb->($pl, $msg)) {
1002 root 1.85 $pl->ext_reply ($msg->{msgid}, %reply);
1003     }
1004     }
1005     } else {
1006     warn "player " . ($pl->ob->name) . " sent unparseable ext message: <$buf>\n";
1007     }
1008    
1009     cf::override;
1010     },
1011 root 1.93 );
1012 root 1.85
1013 root 1.1 sub load_extension {
1014     my ($path) = @_;
1015    
1016     $path =~ /([^\/\\]+)\.ext$/ or die "$path";
1017 root 1.5 my $base = $1;
1018 root 1.1 my $pkg = $1;
1019     $pkg =~ s/[^[:word:]]/_/g;
1020 root 1.41 $pkg = "ext::$pkg";
1021 root 1.1
1022 root 1.160 warn "... loading '$path' into '$pkg'\n";
1023 root 1.1
1024     open my $fh, "<:utf8", $path
1025     or die "$path: $!";
1026    
1027     my $source =
1028     "package $pkg; use strict; use utf8;\n"
1029     . "#line 1 \"$path\"\n{\n"
1030     . (do { local $/; <$fh> })
1031     . "\n};\n1";
1032    
1033     eval $source
1034 root 1.82 or die $@ ? "$path: $@\n"
1035     : "extension disabled.\n";
1036 root 1.1
1037 root 1.159 push @EXTS, $pkg;
1038 root 1.1 }
1039    
1040     sub load_extensions {
1041     for my $ext (<$LIBDIR/*.ext>) {
1042 root 1.3 next unless -r $ext;
1043 root 1.2 eval {
1044     load_extension $ext;
1045     1
1046     } or warn "$ext not loaded: $@";
1047 root 1.1 }
1048     }
1049    
1050 root 1.8 #############################################################################
1051     # load/save/clean perl data associated with a map
1052    
1053 root 1.39 *cf::mapsupport::on_clean = sub {
1054 root 1.13 my ($map) = @_;
1055 root 1.7
1056     my $path = $map->tmpname;
1057     defined $path or return;
1058    
1059 root 1.46 unlink "$path.pst";
1060 root 1.7 };
1061    
1062 root 1.93 cf::map->attach (prio => -10000, package => cf::mapsupport::);
1063 root 1.39
1064 root 1.8 #############################################################################
1065 root 1.70
1066     =head2 CORE EXTENSIONS
1067    
1068     Functions and methods that extend core crossfire objects.
1069    
1070 root 1.143 =cut
1071    
1072     package cf::player;
1073    
1074 root 1.154 use Coro::AIO;
1075    
1076 root 1.95 =head3 cf::player
1077    
1078 root 1.70 =over 4
1079 root 1.22
1080 root 1.143 =item cf::player::find $login
1081 root 1.23
1082 root 1.143 Returns the given player object, loading it if necessary (might block).
1083 root 1.23
1084     =cut
1085    
1086 root 1.145 sub playerdir($) {
1087     cf::localdir
1088     . "/"
1089     . cf::playerdir
1090     . "/"
1091     . (ref $_[0] ? $_[0]->ob->name : $_[0])
1092     }
1093    
1094 root 1.143 sub path($) {
1095 root 1.145 my $login = ref $_[0] ? $_[0]->ob->name : $_[0];
1096    
1097     (playerdir $login) . "/$login.pl"
1098 root 1.143 }
1099    
1100     sub find_active($) {
1101     $cf::PLAYER{$_[0]}
1102     and $cf::PLAYER{$_[0]}->active
1103     and $cf::PLAYER{$_[0]}
1104     }
1105    
1106     sub exists($) {
1107     my ($login) = @_;
1108    
1109     $cf::PLAYER{$login}
1110     or cf::sync_job { !aio_stat $login }
1111     }
1112    
1113     sub find($) {
1114     return $cf::PLAYER{$_[0]} || do {
1115     my $login = $_[0];
1116    
1117     my $guard = cf::lock_acquire "user_find:$login";
1118    
1119 root 1.151 $cf::PLAYER{$_[0]} || do {
1120     my $pl = load_pl path $login
1121     or return;
1122     $cf::PLAYER{$login} = $pl
1123     }
1124     }
1125 root 1.143 }
1126    
1127     sub save($) {
1128     my ($pl) = @_;
1129    
1130     return if $pl->{deny_save};
1131    
1132     my $path = path $pl;
1133     my $guard = cf::lock_acquire "user_save:$path";
1134    
1135     return if $pl->{deny_save};
1136 root 1.146
1137 root 1.154 aio_mkdir playerdir $pl, 0770;
1138 root 1.143 $pl->{last_save} = $cf::RUNTIME;
1139    
1140     $pl->save_pl ($path);
1141     Coro::cede;
1142     }
1143    
1144     sub new($) {
1145     my ($login) = @_;
1146    
1147     my $self = create;
1148    
1149     $self->ob->name ($login);
1150     $self->{deny_save} = 1;
1151    
1152     $cf::PLAYER{$login} = $self;
1153    
1154     $self
1155 root 1.23 }
1156    
1157 root 1.154 =item $pl->quit_character
1158    
1159     Nukes the player without looking back. If logged in, the connection will
1160     be destroyed. May block for a long time.
1161    
1162     =cut
1163    
1164 root 1.145 sub quit_character {
1165     my ($pl) = @_;
1166    
1167     $pl->{deny_save} = 1;
1168     $pl->password ("*"); # this should lock out the player until we nuked the dir
1169    
1170     $pl->invoke (cf::EVENT_PLAYER_LOGOUT, 1) if $pl->active;
1171     $pl->deactivate;
1172     $pl->invoke (cf::EVENT_PLAYER_QUIT);
1173     $pl->ns->destroy if $pl->ns;
1174    
1175     my $path = playerdir $pl;
1176     my $temp = "$path~$cf::RUNTIME~deleting~";
1177 root 1.154 aio_rename $path, $temp;
1178 root 1.150 delete $cf::PLAYER{$pl->ob->name};
1179     $pl->destroy;
1180     IO::AIO::aio_rmtree $temp;
1181 root 1.145 }
1182    
1183 root 1.154 =item cf::player::list_logins
1184    
1185     Returns am arrayref of all valid playernames in the system, can take a
1186     while and may block, so not sync_job-capable, ever.
1187    
1188     =cut
1189    
1190     sub list_logins {
1191     my $dirs = aio_readdir cf::localdir . "/" . cf::playerdir
1192     or return [];
1193    
1194     my @logins;
1195    
1196     for my $login (@$dirs) {
1197     my $fh = aio_open path $login, Fcntl::O_RDONLY, 0 or next;
1198     aio_read $fh, 0, 512, my $buf, 0 or next;
1199 root 1.155 $buf !~ /^password -------------$/m or next; # official not-valid tag
1200 root 1.154
1201     utf8::decode $login;
1202     push @logins, $login;
1203     }
1204    
1205     \@logins
1206     }
1207    
1208     =item $player->maps
1209    
1210     Returns an arrayref of cf::path's of all maps that are private for this
1211     player. May block.
1212    
1213     =cut
1214    
1215     sub maps($) {
1216     my ($pl) = @_;
1217    
1218     my $files = aio_readdir playerdir $pl
1219     or return;
1220    
1221     my @paths;
1222    
1223     for (@$files) {
1224     utf8::decode $_;
1225     next if /\.(?:pl|pst)$/;
1226 root 1.158 next unless /^$PATH_SEP/o;
1227 root 1.154
1228     push @paths, new cf::path "~" . $pl->ob->name . "/" . $_;
1229     }
1230    
1231     \@paths
1232     }
1233    
1234 root 1.95 =item $player->ext_reply ($msgid, $msgtype, %msg)
1235    
1236     Sends an ext reply to the player.
1237    
1238     =cut
1239    
1240 root 1.143 sub ext_reply($$$%) {
1241 root 1.95 my ($self, $id, %msg) = @_;
1242    
1243     $msg{msgid} = $id;
1244    
1245 root 1.143 $self->send ("ext " . cf::to_json \%msg);
1246 root 1.95 }
1247    
1248 root 1.143 package cf;
1249    
1250 root 1.95 =back
1251    
1252 root 1.110
1253     =head3 cf::map
1254    
1255     =over 4
1256    
1257     =cut
1258    
1259     package cf::map;
1260    
1261     use Fcntl;
1262     use Coro::AIO;
1263    
1264 root 1.133 our $MAX_RESET = 3600;
1265     our $DEFAULT_RESET = 3000;
1266 root 1.110
1267     sub generate_random_map {
1268     my ($path, $rmp) = @_;
1269    
1270     # mit "rum" bekleckern, nicht
1271 root 1.162 cf::map::_create_random_map (
1272 root 1.110 $path,
1273     $rmp->{wallstyle}, $rmp->{wall_name}, $rmp->{floorstyle}, $rmp->{monsterstyle},
1274     $rmp->{treasurestyle}, $rmp->{layoutstyle}, $rmp->{doorstyle}, $rmp->{decorstyle},
1275     $rmp->{origin_map}, $rmp->{final_map}, $rmp->{exitstyle}, $rmp->{this_map},
1276     $rmp->{exit_on_final_map},
1277     $rmp->{xsize}, $rmp->{ysize},
1278     $rmp->{expand2x}, $rmp->{layoutoptions1}, $rmp->{layoutoptions2}, $rmp->{layoutoptions3},
1279     $rmp->{symmetry}, $rmp->{difficulty}, $rmp->{difficulty_given}, $rmp->{difficulty_increase},
1280     $rmp->{dungeon_level}, $rmp->{dungeon_depth}, $rmp->{decoroptions}, $rmp->{orientation},
1281     $rmp->{origin_y}, $rmp->{origin_x}, $rmp->{random_seed}, $rmp->{total_map_hp},
1282     $rmp->{map_layout_style}, $rmp->{treasureoptions}, $rmp->{symmetry_used},
1283 root 1.162 (cf::region::find $rmp->{region}), $rmp->{custom}
1284     )
1285 root 1.110 }
1286    
1287     # and all this just because we cannot iterate over
1288     # all maps in C++...
1289     sub change_all_map_light {
1290     my ($change) = @_;
1291    
1292 root 1.122 $_->change_map_light ($change)
1293     for grep $_->outdoor, values %cf::MAP;
1294 root 1.110 }
1295    
1296 root 1.161 sub load_map_header($) {
1297 root 1.110 my ($path) = @_;
1298    
1299     utf8::encode $path;
1300     aio_open $path, O_RDONLY, 0
1301     or return;
1302    
1303     my $map = cf::map::new
1304     or return;
1305    
1306 root 1.161 # for better error messages only, will be overwritten later
1307 root 1.135 $map->path ($path);
1308    
1309 root 1.110 $map->load_header ($path)
1310     or return;
1311    
1312     $map->{load_path} = $path;
1313    
1314     $map
1315     }
1316    
1317 root 1.133 sub find;
1318     sub find {
1319 root 1.110 my ($path, $origin) = @_;
1320    
1321 root 1.133 #warn "find<$path,$origin>\n";#d#
1322 root 1.110
1323 root 1.112 $path = new cf::path $path, $origin && $origin->path;
1324 root 1.110 my $key = $path->as_string;
1325    
1326 root 1.120 cf::lock_wait "map_find:$key";
1327    
1328 root 1.110 $cf::MAP{$key} || do {
1329 root 1.120 my $guard = cf::lock_acquire "map_find:$key";
1330    
1331 root 1.110 # do it the slow way
1332 root 1.161 my $map = $path->load_temp;
1333 root 1.110
1334 root 1.134 Coro::cede;
1335    
1336 root 1.110 if ($map) {
1337 root 1.132 $map->last_access ((delete $map->{last_access})
1338     || $cf::RUNTIME); #d#
1339 root 1.110 # safety
1340     $map->{instantiate_time} = $cf::RUNTIME
1341     if $map->{instantiate_time} > $cf::RUNTIME;
1342     } else {
1343 root 1.161 $map = $path->load_orig
1344     or return;
1345 root 1.110
1346 root 1.111 $map->{load_original} = 1;
1347 root 1.110 $map->{instantiate_time} = $cf::RUNTIME;
1348 root 1.132 $map->last_access ($cf::RUNTIME);
1349 root 1.110 $map->instantiate;
1350    
1351     # per-player maps become, after loading, normal maps
1352     $map->per_player (0) if $path->{user_rel};
1353     }
1354    
1355     $map->path ($key);
1356     $map->{path} = $path;
1357 root 1.116 $map->{last_save} = $cf::RUNTIME;
1358 root 1.110
1359 root 1.134 Coro::cede;
1360    
1361 root 1.112 if ($map->should_reset) {
1362     $map->reset;
1363 root 1.123 undef $guard;
1364 root 1.133 $map = find $path
1365 root 1.124 or return;
1366 root 1.112 }
1367 root 1.110
1368     $cf::MAP{$key} = $map
1369     }
1370     }
1371    
1372     sub load {
1373     my ($self) = @_;
1374    
1375 root 1.120 my $path = $self->{path};
1376     my $guard = cf::lock_acquire "map_load:" . $path->as_string;
1377    
1378 root 1.110 return if $self->in_memory != cf::MAP_SWAPPED;
1379    
1380     $self->in_memory (cf::MAP_LOADING);
1381    
1382     $self->alloc;
1383     $self->load_objects ($self->{load_path}, 1)
1384     or return;
1385    
1386 root 1.112 $self->set_object_flag (cf::FLAG_OBJ_ORIGINAL, 1)
1387     if delete $self->{load_original};
1388 root 1.111
1389 root 1.110 if (my $uniq = $path->uniq_path) {
1390     utf8::encode $uniq;
1391     if (aio_open $uniq, O_RDONLY, 0) {
1392     $self->clear_unique_items;
1393     $self->load_objects ($uniq, 0);
1394     }
1395     }
1396    
1397 root 1.134 Coro::cede;
1398    
1399 root 1.110 # now do the right thing for maps
1400     $self->link_multipart_objects;
1401    
1402     if ($self->{path}->is_style_map) {
1403     $self->{deny_save} = 1;
1404     $self->{deny_reset} = 1;
1405     } else {
1406     $self->fix_auto_apply;
1407     $self->decay_objects;
1408     $self->update_buttons;
1409     $self->set_darkness_map;
1410     $self->difficulty ($self->estimate_difficulty)
1411     unless $self->difficulty;
1412     $self->activate;
1413     }
1414    
1415 root 1.134 Coro::cede;
1416    
1417 root 1.110 $self->in_memory (cf::MAP_IN_MEMORY);
1418     }
1419    
1420 root 1.157 # find and load all maps in the 3x3 area around a map
1421     sub load_diag {
1422     my ($map) = @_;
1423    
1424     my @diag; # diagonal neighbours
1425    
1426     for (0 .. 3) {
1427     my $neigh = $map->tile_path ($_)
1428     or next;
1429     $neigh = find $neigh, $map
1430     or next;
1431     $neigh->load;
1432    
1433     push @diag, [$neigh->tile_path (($_ + 3) % 4), $neigh],
1434     [$neigh->tile_path (($_ + 1) % 4), $neigh];
1435     }
1436    
1437     for (@diag) {
1438     my $neigh = find @$_
1439     or next;
1440     $neigh->load;
1441     }
1442     }
1443    
1444 root 1.133 sub find_sync {
1445 root 1.110 my ($path, $origin) = @_;
1446    
1447 root 1.157 cf::sync_job { find $path, $origin }
1448 root 1.133 }
1449    
1450     sub do_load_sync {
1451     my ($map) = @_;
1452 root 1.110
1453 root 1.133 cf::sync_job { $map->load };
1454 root 1.110 }
1455    
1456 root 1.157 our %MAP_PREFETCH;
1457     our $MAP_PREFETCHER = Coro::async {
1458     while () {
1459     while (%MAP_PREFETCH) {
1460     my $key = each %MAP_PREFETCH
1461     or next;
1462     my $path = delete $MAP_PREFETCH{$key};
1463    
1464     my $map = find $path
1465     or next;
1466     $map->load;
1467     }
1468     Coro::schedule;
1469     }
1470     };
1471    
1472     sub find_async {
1473     my ($path, $origin) = @_;
1474    
1475     $path = new cf::path $path, $origin && $origin->path;
1476     my $key = $path->as_string;
1477    
1478     if (my $map = $cf::MAP{$key}) {
1479     return $map if $map->in_memory == cf::MAP_IN_MEMORY;
1480     }
1481    
1482     $MAP_PREFETCH{$key} = $path;
1483     $MAP_PREFETCHER->ready;
1484    
1485     ()
1486     }
1487    
1488 root 1.110 sub save {
1489     my ($self) = @_;
1490    
1491 root 1.137 my $lock = cf::lock_acquire "map_data:" . $self->path;
1492    
1493 root 1.110 $self->{last_save} = $cf::RUNTIME;
1494    
1495     return unless $self->dirty;
1496    
1497 root 1.117 my $save = $self->{path}->save_path; utf8::encode $save;
1498     my $uniq = $self->{path}->uniq_path; utf8::encode $uniq;
1499    
1500 root 1.110 $self->{load_path} = $save;
1501    
1502     return if $self->{deny_save};
1503    
1504 root 1.132 local $self->{last_access} = $self->last_access;#d#
1505    
1506 root 1.143 cf::async {
1507     $_->contr->save for $self->players;
1508     };
1509    
1510 root 1.110 if ($uniq) {
1511     $self->save_objects ($save, cf::IO_HEADER | cf::IO_OBJECTS);
1512     $self->save_objects ($uniq, cf::IO_UNIQUES);
1513     } else {
1514     $self->save_objects ($save, cf::IO_HEADER | cf::IO_OBJECTS | cf::IO_UNIQUES);
1515     }
1516     }
1517    
1518     sub swap_out {
1519     my ($self) = @_;
1520    
1521 root 1.130 # save first because save cedes
1522     $self->save;
1523    
1524 root 1.137 my $lock = cf::lock_acquire "map_data:" . $self->path;
1525    
1526 root 1.110 return if $self->players;
1527     return if $self->in_memory != cf::MAP_IN_MEMORY;
1528     return if $self->{deny_save};
1529    
1530     $self->clear;
1531     $self->in_memory (cf::MAP_SWAPPED);
1532     }
1533    
1534 root 1.112 sub reset_at {
1535     my ($self) = @_;
1536 root 1.110
1537     # TODO: safety, remove and allow resettable per-player maps
1538 root 1.114 return 1e99 if $self->{path}{user_rel};
1539     return 1e99 if $self->{deny_reset};
1540 root 1.110
1541 root 1.112 my $time = $self->fixed_resettime ? $self->{instantiate_time} : $self->last_access;
1542 root 1.114 my $to = List::Util::min $MAX_RESET, $self->reset_timeout || $DEFAULT_RESET;
1543 root 1.110
1544 root 1.112 $time + $to
1545     }
1546    
1547     sub should_reset {
1548     my ($self) = @_;
1549    
1550     $self->reset_at <= $cf::RUNTIME
1551 root 1.111 }
1552    
1553     sub unlink_save {
1554     my ($self) = @_;
1555    
1556     utf8::encode (my $save = $self->{path}->save_path);
1557     aioreq_pri 3; IO::AIO::aio_unlink $save;
1558     aioreq_pri 3; IO::AIO::aio_unlink "$save.pst";
1559 root 1.110 }
1560    
1561 root 1.113 sub rename {
1562     my ($self, $new_path) = @_;
1563    
1564     $self->unlink_save;
1565    
1566     delete $cf::MAP{$self->path};
1567     $self->{path} = new cf::path $new_path;
1568 root 1.114 $self->path ($self->{path}->as_string);
1569 root 1.113 $cf::MAP{$self->path} = $self;
1570    
1571     $self->save;
1572     }
1573    
1574 root 1.110 sub reset {
1575     my ($self) = @_;
1576    
1577 root 1.137 my $lock = cf::lock_acquire "map_data:" . $self->path;
1578    
1579 root 1.110 return if $self->players;
1580     return if $self->{path}{user_rel};#d#
1581    
1582     warn "resetting map ", $self->path;#d#
1583    
1584 root 1.111 delete $cf::MAP{$self->path};
1585 root 1.110
1586     $_->clear_links_to ($self) for values %cf::MAP;
1587    
1588 root 1.111 $self->unlink_save;
1589     $self->destroy;
1590 root 1.110 }
1591    
1592 root 1.114 my $nuke_counter = "aaaa";
1593    
1594     sub nuke {
1595     my ($self) = @_;
1596    
1597     $self->{deny_save} = 1;
1598     $self->reset_timeout (1);
1599     $self->rename ("{nuke}/" . ($nuke_counter++));
1600     $self->reset; # polite request, might not happen
1601     }
1602    
1603 root 1.110 sub customise_for {
1604     my ($map, $ob) = @_;
1605    
1606     if ($map->per_player) {
1607 root 1.133 return cf::map::find "~" . $ob->name . "/" . $map->{path}{path};
1608 root 1.110 }
1609    
1610     $map
1611     }
1612    
1613 root 1.158 =item cf::map::unique_maps
1614    
1615     Returns an arrayref of cf::path's of all shared maps that have
1616     instantiated unique items. May block.
1617    
1618     =cut
1619    
1620     sub unique_maps() {
1621     my $files = aio_readdir cf::localdir . "/" . cf::uniquedir
1622     or return;
1623    
1624     my @paths;
1625    
1626     for (@$files) {
1627     utf8::decode $_;
1628     next if /\.pst$/;
1629     next unless /^$PATH_SEP/o;
1630    
1631     push @paths, new cf::path $_;
1632     }
1633    
1634     \@paths
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    
1816 root 1.110 $self->enter_link;
1817    
1818 root 1.140 (async {
1819 root 1.133 my $map = cf::map::find $path->as_string;
1820 root 1.110 $map = $map->customise_for ($self) if $map;
1821    
1822 root 1.119 # warn "entering ", $map->path, " at ($x, $y)\n"
1823     # if $map;
1824 root 1.110
1825 root 1.149 $map or $self->message ("The exit to '" . ($path->visible_name) . "' is closed", cf::NDI_UNIQUE | cf::NDI_RED);
1826 root 1.115
1827 root 1.110 $self->leave_link ($map, $x, $y);
1828     })->prio (1);
1829     }
1830    
1831     =item $player_object->enter_exit ($exit_object)
1832    
1833     =cut
1834    
1835     sub parse_random_map_params {
1836     my ($spec) = @_;
1837    
1838     my $rmp = { # defaults
1839     xsize => 10,
1840     ysize => 10,
1841     };
1842    
1843     for (split /\n/, $spec) {
1844     my ($k, $v) = split /\s+/, $_, 2;
1845    
1846     $rmp->{lc $k} = $v if (length $k) && (length $v);
1847     }
1848    
1849     $rmp
1850     }
1851    
1852     sub prepare_random_map {
1853     my ($exit) = @_;
1854    
1855     # all this does is basically replace the /! path by
1856     # a new random map path (?random/...) with a seed
1857     # that depends on the exit object
1858    
1859     my $rmp = parse_random_map_params $exit->msg;
1860    
1861     if ($exit->map) {
1862     $rmp->{region} = $exit->map->region_name;
1863     $rmp->{origin_map} = $exit->map->path;
1864     $rmp->{origin_x} = $exit->x;
1865     $rmp->{origin_y} = $exit->y;
1866     }
1867    
1868     $rmp->{random_seed} ||= $exit->random_seed;
1869    
1870     my $data = cf::to_json $rmp;
1871     my $md5 = Digest::MD5::md5_hex $data;
1872    
1873     if (my $fh = aio_open "$cf::RANDOM_MAPS/$md5.meta", O_WRONLY | O_CREAT, 0666) {
1874     aio_write $fh, 0, (length $data), $data, 0;
1875    
1876     $exit->slaying ("?random/$md5");
1877     $exit->msg (undef);
1878     }
1879     }
1880    
1881     sub cf::object::player::enter_exit {
1882     my ($self, $exit) = @_;
1883    
1884     return unless $self->type == cf::PLAYER;
1885    
1886     $self->enter_link;
1887    
1888 root 1.140 (async {
1889 root 1.133 $self->deactivate_recursive; # just to be sure
1890 root 1.110 unless (eval {
1891     prepare_random_map $exit
1892     if $exit->slaying eq "/!";
1893    
1894     my $path = new cf::path $exit->slaying, $exit->map && $exit->map->path;
1895 root 1.136 $self->goto ($path, $exit->stats->hp, $exit->stats->sp);
1896 root 1.110
1897     1;
1898     }) {
1899     $self->message ("Something went wrong deep within the crossfire server. "
1900     . "I'll try to bring you back to the map you were before. "
1901 root 1.158 . "Please report this to the dungeon master!",
1902 root 1.110 cf::NDI_UNIQUE | cf::NDI_RED);
1903    
1904     warn "ERROR in enter_exit: $@";
1905     $self->leave_link;
1906     }
1907     })->prio (1);
1908     }
1909    
1910 root 1.95 =head3 cf::client
1911    
1912     =over 4
1913    
1914     =item $client->send_drawinfo ($text, $flags)
1915    
1916     Sends a drawinfo packet to the client. Circumvents output buffering so
1917     should not be used under normal circumstances.
1918    
1919 root 1.70 =cut
1920    
1921 root 1.95 sub cf::client::send_drawinfo {
1922     my ($self, $text, $flags) = @_;
1923    
1924     utf8::encode $text;
1925     $self->send_packet (sprintf "drawinfo %d %s", $flags, $text);
1926     }
1927    
1928    
1929     =item $success = $client->query ($flags, "text", \&cb)
1930    
1931     Queues a query to the client, calling the given callback with
1932     the reply text on a reply. flags can be C<cf::CS_QUERY_YESNO>,
1933     C<cf::CS_QUERY_SINGLECHAR> or C<cf::CS_QUERY_HIDEINPUT> or C<0>.
1934    
1935     Queries can fail, so check the return code. Or don't, as queries will become
1936     reliable at some point in the future.
1937    
1938     =cut
1939    
1940     sub cf::client::query {
1941     my ($self, $flags, $text, $cb) = @_;
1942    
1943     return unless $self->state == ST_PLAYING
1944     || $self->state == ST_SETUP
1945     || $self->state == ST_CUSTOM;
1946    
1947     $self->state (ST_CUSTOM);
1948    
1949     utf8::encode $text;
1950     push @{ $self->{query_queue} }, [(sprintf "query %d %s", $flags, $text), $cb];
1951    
1952     $self->send_packet ($self->{query_queue}[0][0])
1953     if @{ $self->{query_queue} } == 1;
1954     }
1955    
1956     cf::client->attach (
1957     on_reply => sub {
1958     my ($ns, $msg) = @_;
1959    
1960     # this weird shuffling is so that direct followup queries
1961     # get handled first
1962 root 1.128 my $queue = delete $ns->{query_queue}
1963 root 1.129 or return; # be conservative, not sure how that can happen, but we saw a crash here
1964 root 1.95
1965     (shift @$queue)->[1]->($msg);
1966    
1967     push @{ $ns->{query_queue} }, @$queue;
1968    
1969     if (@{ $ns->{query_queue} } == @$queue) {
1970     if (@$queue) {
1971     $ns->send_packet ($ns->{query_queue}[0][0]);
1972     } else {
1973 root 1.98 $ns->state (ST_PLAYING) if $ns->state == ST_CUSTOM;
1974 root 1.95 }
1975     }
1976     },
1977     );
1978    
1979 root 1.140 =item $client->async (\&cb)
1980 root 1.96
1981     Create a new coroutine, running the specified callback. The coroutine will
1982     be automatically cancelled when the client gets destroyed (e.g. on logout,
1983     or loss of connection).
1984    
1985     =cut
1986    
1987 root 1.140 sub cf::client::async {
1988 root 1.96 my ($self, $cb) = @_;
1989    
1990 root 1.140 my $coro = &Coro::async ($cb);
1991 root 1.103
1992     $coro->on_destroy (sub {
1993 root 1.96 delete $self->{_coro}{$coro+0};
1994 root 1.103 });
1995 root 1.96
1996     $self->{_coro}{$coro+0} = $coro;
1997 root 1.103
1998     $coro
1999 root 1.96 }
2000    
2001     cf::client->attach (
2002     on_destroy => sub {
2003     my ($ns) = @_;
2004    
2005 root 1.97 $_->cancel for values %{ (delete $ns->{_coro}) || {} };
2006 root 1.96 },
2007     );
2008    
2009 root 1.95 =back
2010    
2011 root 1.70
2012     =head2 SAFE SCRIPTING
2013    
2014     Functions that provide a safe environment to compile and execute
2015     snippets of perl code without them endangering the safety of the server
2016     itself. Looping constructs, I/O operators and other built-in functionality
2017     is not available in the safe scripting environment, and the number of
2018 root 1.79 functions and methods that can be called is greatly reduced.
2019 root 1.70
2020     =cut
2021 root 1.23
2022 root 1.42 our $safe = new Safe "safe";
2023 root 1.23 our $safe_hole = new Safe::Hole;
2024    
2025     $SIG{FPE} = 'IGNORE';
2026    
2027     $safe->permit_only (Opcode::opset qw(:base_core :base_mem :base_orig :base_math sort time));
2028    
2029 root 1.25 # here we export the classes and methods available to script code
2030    
2031 root 1.70 =pod
2032    
2033     The following fucntions and emthods are available within a safe environment:
2034    
2035 elmex 1.91 cf::object contr pay_amount pay_player map
2036 root 1.70 cf::object::player player
2037     cf::player peaceful
2038 elmex 1.91 cf::map trigger
2039 root 1.70
2040     =cut
2041    
2042 root 1.25 for (
2043 elmex 1.91 ["cf::object" => qw(contr pay_amount pay_player map)],
2044 root 1.25 ["cf::object::player" => qw(player)],
2045     ["cf::player" => qw(peaceful)],
2046 elmex 1.91 ["cf::map" => qw(trigger)],
2047 root 1.25 ) {
2048     no strict 'refs';
2049     my ($pkg, @funs) = @$_;
2050 root 1.41 *{"safe::$pkg\::$_"} = $safe_hole->wrap (\&{"$pkg\::$_"})
2051 root 1.25 for @funs;
2052     }
2053 root 1.23
2054 root 1.70 =over 4
2055    
2056     =item @retval = safe_eval $code, [var => value, ...]
2057    
2058     Compiled and executes the given perl code snippet. additional var/value
2059     pairs result in temporary local (my) scalar variables of the given name
2060     that are available in the code snippet. Example:
2061    
2062     my $five = safe_eval '$first + $second', first => 1, second => 4;
2063    
2064     =cut
2065    
2066 root 1.23 sub safe_eval($;@) {
2067     my ($code, %vars) = @_;
2068    
2069     my $qcode = $code;
2070     $qcode =~ s/"/‟/g; # not allowed in #line filenames
2071     $qcode =~ s/\n/\\n/g;
2072    
2073     local $_;
2074 root 1.41 local @safe::cf::_safe_eval_args = values %vars;
2075 root 1.23
2076 root 1.42 my $eval =
2077 root 1.23 "do {\n"
2078     . "my (" . (join ",", map "\$$_", keys %vars) . ") = \@cf::_safe_eval_args;\n"
2079     . "#line 0 \"{$qcode}\"\n"
2080     . $code
2081     . "\n}"
2082 root 1.25 ;
2083    
2084     sub_generation_inc;
2085 root 1.42 my @res = wantarray ? $safe->reval ($eval) : scalar $safe->reval ($eval);
2086 root 1.25 sub_generation_inc;
2087    
2088 root 1.42 if ($@) {
2089     warn "$@";
2090     warn "while executing safe code '$code'\n";
2091     warn "with arguments " . (join " ", %vars) . "\n";
2092     }
2093    
2094 root 1.25 wantarray ? @res : $res[0]
2095 root 1.23 }
2096    
2097 root 1.69 =item cf::register_script_function $function => $cb
2098    
2099     Register a function that can be called from within map/npc scripts. The
2100     function should be reasonably secure and should be put into a package name
2101     like the extension.
2102    
2103     Example: register a function that gets called whenever a map script calls
2104     C<rent::overview>, as used by the C<rent> extension.
2105    
2106     cf::register_script_function "rent::overview" => sub {
2107     ...
2108     };
2109    
2110     =cut
2111    
2112 root 1.23 sub register_script_function {
2113     my ($fun, $cb) = @_;
2114    
2115     no strict 'refs';
2116 root 1.41 *{"safe::$fun"} = $safe_hole->wrap ($cb);
2117 root 1.23 }
2118    
2119 root 1.70 =back
2120    
2121 root 1.71 =cut
2122    
2123 root 1.23 #############################################################################
2124 root 1.65
2125     =head2 EXTENSION DATABASE SUPPORT
2126    
2127     Crossfire maintains a very simple database for extension use. It can
2128     currently store anything that can be serialised using Storable, which
2129     excludes objects.
2130    
2131     The parameter C<$family> should best start with the name of the extension
2132     using it, it should be unique.
2133    
2134     =over 4
2135    
2136     =item $hashref = cf::db_get $family
2137    
2138     Return a hashref for use by the extension C<$family>, which can be
2139     modified. After modifications, you have to call C<cf::db_dirty> or
2140     C<cf::db_sync>.
2141    
2142     =item $value = cf::db_get $family => $key
2143    
2144     Returns a single value from the database
2145    
2146     =item cf::db_put $family => $hashref
2147    
2148     Stores the given family hashref into the database. Updates are delayed, if
2149     you want the data to be synced to disk immediately, use C<cf::db_sync>.
2150    
2151     =item cf::db_put $family => $key => $value
2152    
2153     Stores the given C<$value> in the family hash. Updates are delayed, if you
2154     want the data to be synced to disk immediately, use C<cf::db_sync>.
2155    
2156     =item cf::db_dirty
2157    
2158     Marks the database as dirty, to be updated at a later time.
2159    
2160     =item cf::db_sync
2161    
2162     Immediately write the database to disk I<if it is dirty>.
2163    
2164     =cut
2165    
2166 root 1.78 our $DB;
2167    
2168 root 1.65 {
2169 root 1.66 my $path = cf::localdir . "/database.pst";
2170 root 1.65
2171     sub db_load() {
2172 root 1.78 $DB = stat $path ? Storable::retrieve $path : { };
2173 root 1.65 }
2174    
2175     my $pid;
2176    
2177     sub db_save() {
2178     waitpid $pid, 0 if $pid;
2179 root 1.67 if (0 == ($pid = fork)) {
2180 root 1.78 $DB->{_meta}{version} = 1;
2181     Storable::nstore $DB, "$path~";
2182 root 1.65 rename "$path~", $path;
2183     cf::_exit 0 if defined $pid;
2184     }
2185     }
2186    
2187     my $dirty;
2188    
2189     sub db_sync() {
2190     db_save if $dirty;
2191     undef $dirty;
2192     }
2193    
2194 root 1.87 my $idle = Event->idle (min => $TICK * 2.8, max => 10, repeat => 0, data => WF_AUTOCANCEL, cb => sub {
2195 root 1.65 db_sync;
2196     });
2197    
2198     sub db_dirty() {
2199     $dirty = 1;
2200     $idle->start;
2201     }
2202    
2203     sub db_get($;$) {
2204     @_ >= 2
2205 root 1.78 ? $DB->{$_[0]}{$_[1]}
2206     : ($DB->{$_[0]} ||= { })
2207 root 1.65 }
2208    
2209     sub db_put($$;$) {
2210     if (@_ >= 3) {
2211 root 1.78 $DB->{$_[0]}{$_[1]} = $_[2];
2212 root 1.65 } else {
2213 root 1.78 $DB->{$_[0]} = $_[1];
2214 root 1.65 }
2215     db_dirty;
2216     }
2217 root 1.67
2218 root 1.93 cf::global->attach (
2219     prio => 10000,
2220 root 1.67 on_cleanup => sub {
2221     db_sync;
2222     },
2223 root 1.93 );
2224 root 1.65 }
2225    
2226     #############################################################################
2227 root 1.34 # the server's main()
2228    
2229 root 1.73 sub cfg_load {
2230 root 1.72 open my $fh, "<:utf8", cf::confdir . "/config"
2231     or return;
2232    
2233     local $/;
2234     *CFG = YAML::Syck::Load <$fh>;
2235 root 1.131
2236     $EMERGENCY_POSITION = $CFG{emergency_position} || ["/world/world_105_115", 5, 37];
2237    
2238 root 1.139 $cf::map::MAX_RESET = $CFG{map_max_reset} if exists $CFG{map_max_reset};
2239     $cf::map::DEFAULT_RESET = $CFG{map_default_reset} if exists $CFG{map_default_reset};
2240    
2241 root 1.131 if (exists $CFG{mlockall}) {
2242     eval {
2243 root 1.147 $CFG{mlockall} ? eval "mlockall()" : eval "munlockall()"
2244 root 1.131 and die "WARNING: m(un)lockall failed: $!\n";
2245     };
2246     warn $@ if $@;
2247     }
2248 root 1.72 }
2249    
2250 root 1.39 sub main {
2251 root 1.108 # we must not ever block the main coroutine
2252     local $Coro::idle = sub {
2253 root 1.115 Carp::cluck "FATAL: Coro::idle was called, major BUG, use cf::sync_job!\n";#d#
2254 root 1.140 async { Event::one_event };
2255 root 1.108 };
2256    
2257 root 1.73 cfg_load;
2258 root 1.65 db_load;
2259 root 1.61 load_extensions;
2260 root 1.34 Event::loop;
2261     }
2262    
2263     #############################################################################
2264 root 1.155 # initialisation and cleanup
2265    
2266     # install some emergency cleanup handlers
2267     BEGIN {
2268     for my $signal (qw(INT HUP TERM)) {
2269     Event->signal (
2270     data => WF_AUTOCANCEL,
2271     signal => $signal,
2272     cb => sub {
2273     cf::cleanup "SIG$signal";
2274     },
2275     );
2276     }
2277     }
2278    
2279 root 1.156 sub emergency_save() {
2280 root 1.155 my $freeze_guard = cf::freeze_mainloop;
2281    
2282     warn "enter emergency perl save\n";
2283    
2284     cf::sync_job {
2285     # use a peculiar iteration method to avoid tripping on perl
2286     # refcount bugs in for. also avoids problems with players
2287     # and maps saved/Destroyed asynchronously.
2288     warn "begin emergency player save\n";
2289     for my $login (keys %cf::PLAYER) {
2290     my $pl = $cf::PLAYER{$login} or next;
2291     $pl->valid or next;
2292     $pl->save;
2293     }
2294     warn "end emergency player save\n";
2295    
2296     warn "begin emergency map save\n";
2297     for my $path (keys %cf::MAP) {
2298     my $map = $cf::MAP{$path} or next;
2299     $map->valid or next;
2300     $map->save;
2301     }
2302     warn "end emergency map save\n";
2303     };
2304    
2305     warn "leave emergency perl save\n";
2306     }
2307 root 1.22
2308 root 1.111 sub reload() {
2309 root 1.106 # can/must only be called in main
2310     if ($Coro::current != $Coro::main) {
2311     warn "can only reload from main coroutine\n";
2312     return;
2313     }
2314    
2315 root 1.103 warn "reloading...";
2316    
2317 root 1.159 warn "freezing server";
2318 root 1.133 my $guard = freeze_mainloop;
2319 root 1.106 cf::emergency_save;
2320    
2321 root 1.159 warn "sync database to disk";
2322     cf::db_sync;
2323     IO::AIO::flush;
2324    
2325 root 1.103 eval {
2326 root 1.106 # if anything goes wrong in here, we should simply crash as we already saved
2327 root 1.65
2328 root 1.159 warn "cancel all watchers";
2329 root 1.87 for (Event::all_watchers) {
2330     $_->cancel if $_->data & WF_AUTOCANCEL;
2331     }
2332 root 1.65
2333 root 1.159 warn "cancel all extension coros";
2334 root 1.103 $_->cancel for values %EXT_CORO;
2335     %EXT_CORO = ();
2336    
2337 root 1.159 warn "remove commands";
2338     %COMMAND = ();
2339    
2340     warn "remove ext commands";
2341     %EXTCMD = ();
2342    
2343     warn "unload/nuke all extensions";
2344     for my $pkg (@EXTS) {
2345 root 1.160 warn "... unloading $pkg";
2346 root 1.159
2347     if (my $cb = $pkg->can ("unload")) {
2348     eval {
2349     $cb->($pkg);
2350     1
2351     } or warn "$pkg unloaded, but with errors: $@";
2352     }
2353    
2354 root 1.160 warn "... nuking $pkg";
2355 root 1.159 Symbol::delete_package $pkg;
2356 root 1.65 }
2357    
2358 root 1.159 warn "unload all perl modules loaded from $LIBDIR";
2359 root 1.65 while (my ($k, $v) = each %INC) {
2360     next unless $v =~ /^\Q$LIBDIR\E\/.*\.pm$/;
2361    
2362 root 1.103 warn "removing <$k>";
2363 root 1.65 delete $INC{$k};
2364    
2365     $k =~ s/\.pm$//;
2366     $k =~ s/\//::/g;
2367    
2368     if (my $cb = $k->can ("unload_module")) {
2369     $cb->();
2370     }
2371    
2372     Symbol::delete_package $k;
2373     }
2374    
2375 root 1.159 warn "get rid of safe::, as good as possible";
2376 root 1.65 Symbol::delete_package "safe::$_"
2377 root 1.103 for qw(cf::attachable cf::object cf::object::player cf::client cf::player cf::map cf::party cf::region);
2378 root 1.65
2379 root 1.159 warn "unload cf.pm \"a bit\"";
2380 root 1.65 delete $INC{"cf.pm"};
2381    
2382     # don't, removes xs symbols, too,
2383     # and global variables created in xs
2384     #Symbol::delete_package __PACKAGE__;
2385    
2386 root 1.103 warn "reloading cf.pm";
2387 root 1.65 require cf;
2388 root 1.100 cf::_connect_to_perl; # nominally unnecessary, but cannot hurt
2389    
2390 root 1.159 warn "load config and database again";
2391 root 1.73 cf::cfg_load;
2392 root 1.65 cf::db_load;
2393    
2394 root 1.103 warn "load extensions";
2395 root 1.65 cf::load_extensions;
2396    
2397 root 1.159 warn "reattach attachments to objects/players";
2398 root 1.65 _global_reattach;
2399 root 1.159 warn "reattach attachments to maps";
2400 root 1.144 reattach $_ for values %MAP;
2401 root 1.65 };
2402    
2403 root 1.106 if ($@) {
2404     warn $@;
2405     warn "error while reloading, exiting.";
2406     exit 1;
2407     }
2408    
2409 root 1.159 warn "reloaded";
2410 root 1.65 };
2411    
2412 root 1.108 #############################################################################
2413    
2414     unless ($LINK_MAP) {
2415     $LINK_MAP = cf::map::new;
2416    
2417     $LINK_MAP->width (41);
2418     $LINK_MAP->height (41);
2419     $LINK_MAP->alloc;
2420     $LINK_MAP->path ("{link}");
2421     $LINK_MAP->{path} = bless { path => "{link}" }, "cf::path";
2422     $LINK_MAP->in_memory (MAP_IN_MEMORY);
2423 root 1.110
2424     # dirty hack because... archetypes are not yet loaded
2425     Event->timer (
2426 root 1.142 after => 10,
2427 root 1.110 cb => sub {
2428     $_[0]->w->cancel;
2429    
2430     # provide some exits "home"
2431     my $exit = cf::object::new "exit";
2432    
2433     $exit->slaying ($EMERGENCY_POSITION->[0]);
2434     $exit->stats->hp ($EMERGENCY_POSITION->[1]);
2435     $exit->stats->sp ($EMERGENCY_POSITION->[2]);
2436    
2437     $LINK_MAP->insert ($exit->clone, 19, 19);
2438     $LINK_MAP->insert ($exit->clone, 19, 20);
2439     $LINK_MAP->insert ($exit->clone, 19, 21);
2440     $LINK_MAP->insert ($exit->clone, 20, 19);
2441     $LINK_MAP->insert ($exit->clone, 20, 21);
2442     $LINK_MAP->insert ($exit->clone, 21, 19);
2443     $LINK_MAP->insert ($exit->clone, 21, 20);
2444     $LINK_MAP->insert ($exit->clone, 21, 21);
2445    
2446     $exit->destroy;
2447     });
2448    
2449     $LINK_MAP->{deny_save} = 1;
2450     $LINK_MAP->{deny_reset} = 1;
2451    
2452     $cf::MAP{$LINK_MAP->path} = $LINK_MAP;
2453 root 1.108 }
2454    
2455 root 1.111 register_command "reload" => sub {
2456 root 1.65 my ($who, $arg) = @_;
2457    
2458     if ($who->flag (FLAG_WIZ)) {
2459 root 1.107 $who->message ("start of reload.");
2460 root 1.111 reload;
2461 root 1.107 $who->message ("end of reload.");
2462 root 1.65 }
2463     };
2464    
2465 root 1.27 unshift @INC, $LIBDIR;
2466 root 1.17
2467 root 1.35 $TICK_WATCHER = Event->timer (
2468 root 1.104 reentrant => 0,
2469     prio => 0,
2470     at => $NEXT_TICK || $TICK,
2471     data => WF_AUTOCANCEL,
2472     cb => sub {
2473 root 1.163 $NOW = Event::time;
2474    
2475 root 1.133 cf::server_tick; # one server iteration
2476     $RUNTIME += $TICK;
2477 root 1.35 $NEXT_TICK += $TICK;
2478    
2479 root 1.155 $WAIT_FOR_TICK->broadcast;
2480     $WAIT_FOR_TICK_ONE->send if $WAIT_FOR_TICK_ONE->awaited;
2481    
2482 root 1.78 # if we are delayed by four ticks or more, skip them all
2483 root 1.103 $NEXT_TICK = Event::time if Event::time >= $NEXT_TICK + $TICK * 4;
2484 root 1.35
2485     $TICK_WATCHER->at ($NEXT_TICK);
2486     $TICK_WATCHER->start;
2487     },
2488     );
2489    
2490 root 1.80 IO::AIO::max_poll_time $TICK * 0.2;
2491 root 1.77
2492 root 1.108 Event->io (
2493     fd => IO::AIO::poll_fileno,
2494     poll => 'r',
2495     prio => 5,
2496     data => WF_AUTOCANCEL,
2497     cb => \&IO::AIO::poll_cb,
2498     );
2499    
2500     Event->timer (
2501     data => WF_AUTOCANCEL,
2502     after => 0,
2503     interval => 10,
2504     cb => sub {
2505     (Coro::unblock_sub {
2506     write_runtime
2507     or warn "ERROR: unable to write runtime file: $!";
2508     })->();
2509     },
2510     );
2511 root 1.103
2512 root 1.125 END { cf::emergency_save }
2513    
2514 root 1.1 1
2515