ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.164
Committed: Thu Jan 11 01:24:25 2007 UTC (17 years, 4 months ago) by root
Branch: MAIN
Changes since 1.163: +11 -11 lines
Log Message:
slightly more abstraction, much more to come

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 root 1.164 sub unlink_save {
522     my ($self) = @_;
523    
524     utf8::encode (my $save = $self->save_path);
525     IO::AIO::aioreq_pri 4; IO::AIO::aio_unlink $save;
526     IO::AIO::aioreq_pri 4; IO::AIO::aio_unlink "$save.pst";
527     }
528    
529 root 1.108 package cf;
530    
531     #############################################################################
532    
533 root 1.93 =head2 ATTACHABLE OBJECTS
534    
535 root 1.94 Many objects in crossfire are so-called attachable objects. That means you can
536     attach callbacks/event handlers (a collection of which is called an "attachment")
537     to it. All such attachable objects support the following methods.
538    
539     In the following description, CLASS can be any of C<global>, C<object>
540     C<player>, C<client> or C<map> (i.e. the attachable objects in
541     crossfire+).
542 root 1.55
543     =over 4
544    
545 root 1.94 =item $attachable->attach ($attachment, key => $value...)
546    
547     =item $attachable->detach ($attachment)
548    
549     Attach/detach a pre-registered attachment to a specific object and give it
550     the specified key/value pairs as arguments.
551    
552     Example, attach a minesweeper attachment to the given object, making it a
553     10x10 minesweeper game:
554 root 1.46
555 root 1.94 $obj->attach (minesweeper => width => 10, height => 10);
556 root 1.53
557 root 1.93 =item $bool = $attachable->attached ($name)
558 root 1.46
559 root 1.93 Checks wether the named attachment is currently attached to the object.
560 root 1.46
561 root 1.94 =item cf::CLASS->attach ...
562 root 1.46
563 root 1.94 =item cf::CLASS->detach ...
564 root 1.92
565 root 1.94 Define an anonymous attachment and attach it to all objects of the given
566     CLASS. See the next function for an explanation of its arguments.
567 root 1.92
568 root 1.93 You can attach to global events by using the C<cf::global> class.
569 root 1.92
570 root 1.94 Example, log all player logins:
571    
572     cf::player->attach (
573     on_login => sub {
574     my ($pl) = @_;
575     ...
576     },
577     );
578    
579     Example, attach to the jeweler skill:
580    
581     cf::object->attach (
582     type => cf::SKILL,
583     subtype => cf::SK_JEWELER,
584     on_use_skill => sub {
585     my ($sk, $ob, $part, $dir, $msg) = @_;
586     ...
587     },
588     );
589    
590     =item cf::CLASS::attachment $name, ...
591    
592     Register an attachment by C<$name> through which attachable objects of the
593     given CLASS can refer to this attachment.
594    
595     Some classes such as crossfire maps and objects can specify attachments
596     that are attached at load/instantiate time, thus the need for a name.
597    
598     These calls expect any number of the following handler/hook descriptions:
599 root 1.46
600     =over 4
601    
602     =item prio => $number
603    
604     Set the priority for all following handlers/hooks (unless overwritten
605     by another C<prio> setting). Lower priority handlers get executed
606     earlier. The default priority is C<0>, and many built-in handlers are
607     registered at priority C<-1000>, so lower priorities should not be used
608     unless you know what you are doing.
609    
610 root 1.93 =item type => $type
611    
612     (Only for C<< cf::object->attach >> calls), limits the attachment to the
613     given type of objects only (the additional parameter C<subtype> can be
614     used to further limit to the given subtype).
615    
616 root 1.46 =item on_I<event> => \&cb
617    
618     Call the given code reference whenever the named event happens (event is
619     something like C<instantiate>, C<apply>, C<use_skill> and so on, and which
620     handlers are recognised generally depends on the type of object these
621     handlers attach to).
622    
623     See F<include/eventinc.h> for the full list of events supported, and their
624     class.
625    
626     =item package => package::
627    
628     Look for sub functions of the name C<< on_I<event> >> in the given
629     package and register them. Only handlers for eevents supported by the
630     object/class are recognised.
631    
632     =back
633    
634 root 1.94 Example, define an attachment called "sockpuppet" that calls the given
635     event handler when a monster attacks:
636    
637     cf::object::attachment sockpuppet =>
638     on_skill_attack => sub {
639     my ($self, $victim) = @_;
640     ...
641     }
642     }
643    
644 root 1.96 =item $attachable->valid
645    
646     Just because you have a perl object does not mean that the corresponding
647     C-level object still exists. If you try to access an object that has no
648     valid C counterpart anymore you get an exception at runtime. This method
649     can be used to test for existence of the C object part without causing an
650     exception.
651    
652 root 1.39 =cut
653    
654 root 1.40 # the following variables are defined in .xs and must not be re-created
655 root 1.100 our @CB_GLOBAL = (); # registry for all global events
656     our @CB_ATTACHABLE = (); # registry for all attachables
657     our @CB_OBJECT = (); # all objects (should not be used except in emergency)
658     our @CB_PLAYER = ();
659     our @CB_CLIENT = ();
660     our @CB_TYPE = (); # registry for type (cf-object class) based events
661     our @CB_MAP = ();
662 root 1.39
663 root 1.45 my %attachment;
664    
665 root 1.93 sub _attach_cb($$$$) {
666     my ($registry, $event, $prio, $cb) = @_;
667 root 1.39
668     use sort 'stable';
669    
670     $cb = [$prio, $cb];
671    
672     @{$registry->[$event]} = sort
673     { $a->[0] cmp $b->[0] }
674     @{$registry->[$event] || []}, $cb;
675     }
676    
677 root 1.100 # hack
678     my %attachable_klass = map +($_ => 1), KLASS_OBJECT, KLASS_CLIENT, KLASS_PLAYER, KLASS_MAP;
679    
680 root 1.39 # attach handles attaching event callbacks
681     # the only thing the caller has to do is pass the correct
682     # registry (== where the callback attaches to).
683 root 1.93 sub _attach {
684 root 1.45 my ($registry, $klass, @arg) = @_;
685 root 1.39
686 root 1.93 my $object_type;
687 root 1.39 my $prio = 0;
688     my %cb_id = map +("on_" . lc $EVENT[$_][0], $_) , grep $EVENT[$_][1] == $klass, 0 .. $#EVENT;
689    
690 root 1.100 #TODO: get rid of this hack
691     if ($attachable_klass{$klass}) {
692     %cb_id = (%cb_id, map +("on_" . lc $EVENT[$_][0], $_) , grep $EVENT[$_][1] == KLASS_ATTACHABLE, 0 .. $#EVENT);
693     }
694    
695 root 1.45 while (@arg) {
696     my $type = shift @arg;
697 root 1.39
698     if ($type eq "prio") {
699 root 1.45 $prio = shift @arg;
700 root 1.39
701 root 1.93 } elsif ($type eq "type") {
702     $object_type = shift @arg;
703     $registry = $CB_TYPE[$object_type] ||= [];
704    
705     } elsif ($type eq "subtype") {
706     defined $object_type or Carp::croak "subtype specified without type";
707     my $object_subtype = shift @arg;
708     $registry = $CB_TYPE[$object_type + $object_subtype * NUM_SUBTYPES] ||= [];
709    
710 root 1.39 } elsif ($type eq "package") {
711 root 1.45 my $pkg = shift @arg;
712 root 1.39
713     while (my ($name, $id) = each %cb_id) {
714     if (my $cb = $pkg->can ($name)) {
715 root 1.93 _attach_cb $registry, $id, $prio, $cb;
716 root 1.39 }
717     }
718    
719     } elsif (exists $cb_id{$type}) {
720 root 1.93 _attach_cb $registry, $cb_id{$type}, $prio, shift @arg;
721 root 1.39
722     } elsif (ref $type) {
723     warn "attaching objects not supported, ignoring.\n";
724    
725     } else {
726 root 1.45 shift @arg;
727 root 1.39 warn "attach argument '$type' not supported, ignoring.\n";
728     }
729     }
730     }
731    
732 root 1.93 sub _object_attach {
733 root 1.48 my ($obj, $name, %arg) = @_;
734 root 1.46
735 root 1.55 return if exists $obj->{_attachment}{$name};
736    
737 root 1.46 if (my $attach = $attachment{$name}) {
738     my $registry = $obj->registry;
739    
740 root 1.47 for (@$attach) {
741     my ($klass, @attach) = @$_;
742 root 1.93 _attach $registry, $klass, @attach;
743 root 1.47 }
744 root 1.46
745 root 1.48 $obj->{$name} = \%arg;
746 root 1.46 } else {
747     warn "object uses attachment '$name' that is not available, postponing.\n";
748     }
749    
750 root 1.50 $obj->{_attachment}{$name} = undef;
751 root 1.46 }
752    
753 root 1.93 sub cf::attachable::attach {
754     if (ref $_[0]) {
755     _object_attach @_;
756     } else {
757     _attach shift->_attach_registry, @_;
758     }
759 root 1.55 };
760 root 1.46
761 root 1.54 # all those should be optimised
762 root 1.93 sub cf::attachable::detach {
763 root 1.54 my ($obj, $name) = @_;
764 root 1.46
765 root 1.93 if (ref $obj) {
766     delete $obj->{_attachment}{$name};
767     reattach ($obj);
768     } else {
769     Carp::croak "cannot, currently, detach class attachments";
770     }
771 root 1.55 };
772    
773 root 1.93 sub cf::attachable::attached {
774 root 1.55 my ($obj, $name) = @_;
775    
776     exists $obj->{_attachment}{$name}
777 root 1.39 }
778    
779 root 1.100 for my $klass (qw(ATTACHABLE GLOBAL OBJECT PLAYER CLIENT MAP)) {
780 root 1.93 eval "#line " . __LINE__ . " 'cf.pm'
781     sub cf::\L$klass\E::_attach_registry {
782     (\\\@CB_$klass, KLASS_$klass)
783     }
784 root 1.45
785 root 1.93 sub cf::\L$klass\E::attachment {
786     my \$name = shift;
787 root 1.39
788 root 1.93 \$attachment{\$name} = [[KLASS_$klass, \@_]];
789     }
790     ";
791     die if $@;
792 root 1.52 }
793    
794 root 1.39 our $override;
795 root 1.45 our @invoke_results = (); # referenced from .xs code. TODO: play tricks with reify and mortals?
796 root 1.39
797 root 1.45 sub override {
798     $override = 1;
799     @invoke_results = ();
800 root 1.39 }
801    
802 root 1.45 sub do_invoke {
803 root 1.39 my $event = shift;
804 root 1.40 my $callbacks = shift;
805 root 1.39
806 root 1.45 @invoke_results = ();
807    
808 root 1.39 local $override;
809    
810 root 1.40 for (@$callbacks) {
811 root 1.39 eval { &{$_->[1]} };
812    
813     if ($@) {
814     warn "$@";
815 root 1.64 warn "... while processing $EVENT[$event][0](@_) event, skipping processing altogether.\n";
816 root 1.39 override;
817     }
818    
819     return 1 if $override;
820     }
821    
822     0
823     }
824    
825 root 1.96 =item $bool = cf::global::invoke (EVENT_CLASS_XXX, ...)
826 root 1.55
827 root 1.96 =item $bool = $attachable->invoke (EVENT_CLASS_XXX, ...)
828 root 1.55
829 root 1.96 Generate an object-specific event with the given arguments.
830 root 1.55
831 root 1.96 This API is preliminary (most likely, the EVENT_CLASS_xxx prefix will be
832 root 1.55 removed in future versions), and there is no public API to access override
833     results (if you must, access C<@cf::invoke_results> directly).
834    
835     =back
836    
837 root 1.71 =cut
838    
839 root 1.70 #############################################################################
840 root 1.45 # object support
841    
842 root 1.102 sub reattach {
843     # basically do the same as instantiate, without calling instantiate
844     my ($obj) = @_;
845    
846     my $registry = $obj->registry;
847    
848     @$registry = ();
849    
850     delete $obj->{_attachment} unless scalar keys %{ $obj->{_attachment} || {} };
851    
852     for my $name (keys %{ $obj->{_attachment} || {} }) {
853     if (my $attach = $attachment{$name}) {
854     for (@$attach) {
855     my ($klass, @attach) = @$_;
856     _attach $registry, $klass, @attach;
857     }
858     } else {
859     warn "object uses attachment '$name' that is not available, postponing.\n";
860     }
861     }
862     }
863    
864 root 1.100 cf::attachable->attach (
865     prio => -1000000,
866     on_instantiate => sub {
867     my ($obj, $data) = @_;
868 root 1.45
869 root 1.100 $data = from_json $data;
870 root 1.45
871 root 1.100 for (@$data) {
872     my ($name, $args) = @$_;
873 root 1.49
874 root 1.100 $obj->attach ($name, %{$args || {} });
875     }
876     },
877 root 1.102 on_reattach => \&reattach,
878 root 1.100 on_clone => sub {
879     my ($src, $dst) = @_;
880    
881     @{$dst->registry} = @{$src->registry};
882    
883     %$dst = %$src;
884    
885     %{$dst->{_attachment}} = %{$src->{_attachment}}
886     if exists $src->{_attachment};
887     },
888     );
889 root 1.45
890 root 1.46 sub object_freezer_save {
891 root 1.59 my ($filename, $rdata, $objs) = @_;
892 root 1.46
893 root 1.105 sync_job {
894     if (length $$rdata) {
895     warn sprintf "saving %s (%d,%d)\n",
896     $filename, length $$rdata, scalar @$objs;
897 root 1.60
898 root 1.105 if (my $fh = aio_open "$filename~", O_WRONLY | O_CREAT, 0600) {
899 root 1.60 chmod SAVE_MODE, $fh;
900 root 1.105 aio_write $fh, 0, (length $$rdata), $$rdata, 0;
901     aio_fsync $fh;
902 root 1.60 close $fh;
903 root 1.105
904     if (@$objs) {
905     if (my $fh = aio_open "$filename.pst~", O_WRONLY | O_CREAT, 0600) {
906     chmod SAVE_MODE, $fh;
907     my $data = Storable::nfreeze { version => 1, objs => $objs };
908     aio_write $fh, 0, (length $data), $data, 0;
909     aio_fsync $fh;
910     close $fh;
911     aio_rename "$filename.pst~", "$filename.pst";
912     }
913     } else {
914     aio_unlink "$filename.pst";
915     }
916    
917     aio_rename "$filename~", $filename;
918 root 1.60 } else {
919 root 1.105 warn "FATAL: $filename~: $!\n";
920 root 1.60 }
921 root 1.59 } else {
922 root 1.105 aio_unlink $filename;
923     aio_unlink "$filename.pst";
924 root 1.59 }
925 root 1.45 }
926     }
927    
928 root 1.80 sub object_freezer_as_string {
929     my ($rdata, $objs) = @_;
930    
931     use Data::Dumper;
932    
933 root 1.81 $$rdata . Dumper $objs
934 root 1.80 }
935    
936 root 1.46 sub object_thawer_load {
937     my ($filename) = @_;
938    
939 root 1.105 my ($data, $av);
940 root 1.61
941 root 1.105 (aio_load $filename, $data) >= 0
942     or return;
943 root 1.61
944 root 1.105 unless (aio_stat "$filename.pst") {
945     (aio_load "$filename.pst", $av) >= 0
946     or return;
947 root 1.113 $av = eval { (Storable::thaw $av)->{objs} };
948 root 1.61 }
949 root 1.45
950 root 1.118 warn sprintf "loading %s (%d)\n",
951     $filename, length $data, scalar @{$av || []};#d#
952 root 1.105 return ($data, $av);
953 root 1.45 }
954    
955     #############################################################################
956 root 1.85 # command handling &c
957 root 1.39
958 root 1.85 =item cf::register_command $name => \&callback($ob,$args);
959 root 1.1
960 root 1.85 Register a callback for execution when the client sends the user command
961     $name.
962 root 1.5
963 root 1.85 =cut
964 root 1.5
965 root 1.85 sub register_command {
966     my ($name, $cb) = @_;
967 root 1.5
968 root 1.85 my $caller = caller;
969     #warn "registering command '$name/$time' to '$caller'";
970 root 1.1
971 root 1.85 push @{ $COMMAND{$name} }, [$caller, $cb];
972 root 1.1 }
973    
974 root 1.85 =item cf::register_extcmd $name => \&callback($pl,$packet);
975 root 1.1
976 root 1.85 Register a callbackf ro execution when the client sends an extcmd packet.
977 root 1.1
978 root 1.85 If the callback returns something, it is sent back as if reply was being
979     called.
980 root 1.1
981 root 1.85 =cut
982 root 1.1
983 root 1.16 sub register_extcmd {
984     my ($name, $cb) = @_;
985    
986 root 1.159 $EXTCMD{$name} = $cb;
987 root 1.16 }
988    
989 root 1.93 cf::player->attach (
990 root 1.85 on_command => sub {
991     my ($pl, $name, $params) = @_;
992    
993     my $cb = $COMMAND{$name}
994     or return;
995    
996     for my $cmd (@$cb) {
997     $cmd->[1]->($pl->ob, $params);
998     }
999    
1000     cf::override;
1001     },
1002     on_extcmd => sub {
1003     my ($pl, $buf) = @_;
1004    
1005     my $msg = eval { from_json $buf };
1006    
1007     if (ref $msg) {
1008     if (my $cb = $EXTCMD{$msg->{msgtype}}) {
1009 root 1.159 if (my %reply = $cb->($pl, $msg)) {
1010 root 1.85 $pl->ext_reply ($msg->{msgid}, %reply);
1011     }
1012     }
1013     } else {
1014     warn "player " . ($pl->ob->name) . " sent unparseable ext message: <$buf>\n";
1015     }
1016    
1017     cf::override;
1018     },
1019 root 1.93 );
1020 root 1.85
1021 root 1.1 sub load_extension {
1022     my ($path) = @_;
1023    
1024     $path =~ /([^\/\\]+)\.ext$/ or die "$path";
1025 root 1.5 my $base = $1;
1026 root 1.1 my $pkg = $1;
1027     $pkg =~ s/[^[:word:]]/_/g;
1028 root 1.41 $pkg = "ext::$pkg";
1029 root 1.1
1030 root 1.160 warn "... loading '$path' into '$pkg'\n";
1031 root 1.1
1032     open my $fh, "<:utf8", $path
1033     or die "$path: $!";
1034    
1035     my $source =
1036     "package $pkg; use strict; use utf8;\n"
1037     . "#line 1 \"$path\"\n{\n"
1038     . (do { local $/; <$fh> })
1039     . "\n};\n1";
1040    
1041     eval $source
1042 root 1.82 or die $@ ? "$path: $@\n"
1043     : "extension disabled.\n";
1044 root 1.1
1045 root 1.159 push @EXTS, $pkg;
1046 root 1.1 }
1047    
1048     sub load_extensions {
1049     for my $ext (<$LIBDIR/*.ext>) {
1050 root 1.3 next unless -r $ext;
1051 root 1.2 eval {
1052     load_extension $ext;
1053     1
1054     } or warn "$ext not loaded: $@";
1055 root 1.1 }
1056     }
1057    
1058 root 1.8 #############################################################################
1059     # load/save/clean perl data associated with a map
1060    
1061 root 1.39 *cf::mapsupport::on_clean = sub {
1062 root 1.13 my ($map) = @_;
1063 root 1.7
1064     my $path = $map->tmpname;
1065     defined $path or return;
1066    
1067 root 1.46 unlink "$path.pst";
1068 root 1.7 };
1069    
1070 root 1.93 cf::map->attach (prio => -10000, package => cf::mapsupport::);
1071 root 1.39
1072 root 1.8 #############################################################################
1073 root 1.70
1074     =head2 CORE EXTENSIONS
1075    
1076     Functions and methods that extend core crossfire objects.
1077    
1078 root 1.143 =cut
1079    
1080     package cf::player;
1081    
1082 root 1.154 use Coro::AIO;
1083    
1084 root 1.95 =head3 cf::player
1085    
1086 root 1.70 =over 4
1087 root 1.22
1088 root 1.143 =item cf::player::find $login
1089 root 1.23
1090 root 1.143 Returns the given player object, loading it if necessary (might block).
1091 root 1.23
1092     =cut
1093    
1094 root 1.145 sub playerdir($) {
1095     cf::localdir
1096     . "/"
1097     . cf::playerdir
1098     . "/"
1099     . (ref $_[0] ? $_[0]->ob->name : $_[0])
1100     }
1101    
1102 root 1.143 sub path($) {
1103 root 1.145 my $login = ref $_[0] ? $_[0]->ob->name : $_[0];
1104    
1105     (playerdir $login) . "/$login.pl"
1106 root 1.143 }
1107    
1108     sub find_active($) {
1109     $cf::PLAYER{$_[0]}
1110     and $cf::PLAYER{$_[0]}->active
1111     and $cf::PLAYER{$_[0]}
1112     }
1113    
1114     sub exists($) {
1115     my ($login) = @_;
1116    
1117     $cf::PLAYER{$login}
1118     or cf::sync_job { !aio_stat $login }
1119     }
1120    
1121     sub find($) {
1122     return $cf::PLAYER{$_[0]} || do {
1123     my $login = $_[0];
1124    
1125     my $guard = cf::lock_acquire "user_find:$login";
1126    
1127 root 1.151 $cf::PLAYER{$_[0]} || do {
1128     my $pl = load_pl path $login
1129     or return;
1130     $cf::PLAYER{$login} = $pl
1131     }
1132     }
1133 root 1.143 }
1134    
1135     sub save($) {
1136     my ($pl) = @_;
1137    
1138     return if $pl->{deny_save};
1139    
1140     my $path = path $pl;
1141     my $guard = cf::lock_acquire "user_save:$path";
1142    
1143     return if $pl->{deny_save};
1144 root 1.146
1145 root 1.154 aio_mkdir playerdir $pl, 0770;
1146 root 1.143 $pl->{last_save} = $cf::RUNTIME;
1147    
1148     $pl->save_pl ($path);
1149     Coro::cede;
1150     }
1151    
1152     sub new($) {
1153     my ($login) = @_;
1154    
1155     my $self = create;
1156    
1157     $self->ob->name ($login);
1158     $self->{deny_save} = 1;
1159    
1160     $cf::PLAYER{$login} = $self;
1161    
1162     $self
1163 root 1.23 }
1164    
1165 root 1.154 =item $pl->quit_character
1166    
1167     Nukes the player without looking back. If logged in, the connection will
1168     be destroyed. May block for a long time.
1169    
1170     =cut
1171    
1172 root 1.145 sub quit_character {
1173     my ($pl) = @_;
1174    
1175     $pl->{deny_save} = 1;
1176     $pl->password ("*"); # this should lock out the player until we nuked the dir
1177    
1178     $pl->invoke (cf::EVENT_PLAYER_LOGOUT, 1) if $pl->active;
1179     $pl->deactivate;
1180     $pl->invoke (cf::EVENT_PLAYER_QUIT);
1181     $pl->ns->destroy if $pl->ns;
1182    
1183     my $path = playerdir $pl;
1184     my $temp = "$path~$cf::RUNTIME~deleting~";
1185 root 1.154 aio_rename $path, $temp;
1186 root 1.150 delete $cf::PLAYER{$pl->ob->name};
1187     $pl->destroy;
1188     IO::AIO::aio_rmtree $temp;
1189 root 1.145 }
1190    
1191 root 1.154 =item cf::player::list_logins
1192    
1193     Returns am arrayref of all valid playernames in the system, can take a
1194     while and may block, so not sync_job-capable, ever.
1195    
1196     =cut
1197    
1198     sub list_logins {
1199     my $dirs = aio_readdir cf::localdir . "/" . cf::playerdir
1200     or return [];
1201    
1202     my @logins;
1203    
1204     for my $login (@$dirs) {
1205     my $fh = aio_open path $login, Fcntl::O_RDONLY, 0 or next;
1206     aio_read $fh, 0, 512, my $buf, 0 or next;
1207 root 1.155 $buf !~ /^password -------------$/m or next; # official not-valid tag
1208 root 1.154
1209     utf8::decode $login;
1210     push @logins, $login;
1211     }
1212    
1213     \@logins
1214     }
1215    
1216     =item $player->maps
1217    
1218     Returns an arrayref of cf::path's of all maps that are private for this
1219     player. May block.
1220    
1221     =cut
1222    
1223     sub maps($) {
1224     my ($pl) = @_;
1225    
1226     my $files = aio_readdir playerdir $pl
1227     or return;
1228    
1229     my @paths;
1230    
1231     for (@$files) {
1232     utf8::decode $_;
1233     next if /\.(?:pl|pst)$/;
1234 root 1.158 next unless /^$PATH_SEP/o;
1235 root 1.154
1236     push @paths, new cf::path "~" . $pl->ob->name . "/" . $_;
1237     }
1238    
1239     \@paths
1240     }
1241    
1242 root 1.95 =item $player->ext_reply ($msgid, $msgtype, %msg)
1243    
1244     Sends an ext reply to the player.
1245    
1246     =cut
1247    
1248 root 1.143 sub ext_reply($$$%) {
1249 root 1.95 my ($self, $id, %msg) = @_;
1250    
1251     $msg{msgid} = $id;
1252    
1253 root 1.143 $self->send ("ext " . cf::to_json \%msg);
1254 root 1.95 }
1255    
1256 root 1.143 package cf;
1257    
1258 root 1.95 =back
1259    
1260 root 1.110
1261     =head3 cf::map
1262    
1263     =over 4
1264    
1265     =cut
1266    
1267     package cf::map;
1268    
1269     use Fcntl;
1270     use Coro::AIO;
1271    
1272 root 1.133 our $MAX_RESET = 3600;
1273     our $DEFAULT_RESET = 3000;
1274 root 1.110
1275     sub generate_random_map {
1276     my ($path, $rmp) = @_;
1277    
1278     # mit "rum" bekleckern, nicht
1279 root 1.162 cf::map::_create_random_map (
1280 root 1.110 $path,
1281     $rmp->{wallstyle}, $rmp->{wall_name}, $rmp->{floorstyle}, $rmp->{monsterstyle},
1282     $rmp->{treasurestyle}, $rmp->{layoutstyle}, $rmp->{doorstyle}, $rmp->{decorstyle},
1283     $rmp->{origin_map}, $rmp->{final_map}, $rmp->{exitstyle}, $rmp->{this_map},
1284     $rmp->{exit_on_final_map},
1285     $rmp->{xsize}, $rmp->{ysize},
1286     $rmp->{expand2x}, $rmp->{layoutoptions1}, $rmp->{layoutoptions2}, $rmp->{layoutoptions3},
1287     $rmp->{symmetry}, $rmp->{difficulty}, $rmp->{difficulty_given}, $rmp->{difficulty_increase},
1288     $rmp->{dungeon_level}, $rmp->{dungeon_depth}, $rmp->{decoroptions}, $rmp->{orientation},
1289     $rmp->{origin_y}, $rmp->{origin_x}, $rmp->{random_seed}, $rmp->{total_map_hp},
1290     $rmp->{map_layout_style}, $rmp->{treasureoptions}, $rmp->{symmetry_used},
1291 root 1.162 (cf::region::find $rmp->{region}), $rmp->{custom}
1292     )
1293 root 1.110 }
1294    
1295     # and all this just because we cannot iterate over
1296     # all maps in C++...
1297     sub change_all_map_light {
1298     my ($change) = @_;
1299    
1300 root 1.122 $_->change_map_light ($change)
1301     for grep $_->outdoor, values %cf::MAP;
1302 root 1.110 }
1303    
1304 root 1.161 sub load_map_header($) {
1305 root 1.110 my ($path) = @_;
1306    
1307     utf8::encode $path;
1308     aio_open $path, O_RDONLY, 0
1309     or return;
1310    
1311     my $map = cf::map::new
1312     or return;
1313    
1314 root 1.161 # for better error messages only, will be overwritten later
1315 root 1.135 $map->path ($path);
1316    
1317 root 1.110 $map->load_header ($path)
1318     or return;
1319    
1320     $map->{load_path} = $path;
1321    
1322     $map
1323     }
1324    
1325 root 1.133 sub find;
1326     sub find {
1327 root 1.110 my ($path, $origin) = @_;
1328    
1329 root 1.133 #warn "find<$path,$origin>\n";#d#
1330 root 1.110
1331 root 1.112 $path = new cf::path $path, $origin && $origin->path;
1332 root 1.110 my $key = $path->as_string;
1333    
1334 root 1.120 cf::lock_wait "map_find:$key";
1335    
1336 root 1.110 $cf::MAP{$key} || do {
1337 root 1.120 my $guard = cf::lock_acquire "map_find:$key";
1338    
1339 root 1.110 # do it the slow way
1340 root 1.161 my $map = $path->load_temp;
1341 root 1.110
1342 root 1.134 Coro::cede;
1343    
1344 root 1.110 if ($map) {
1345 root 1.132 $map->last_access ((delete $map->{last_access})
1346     || $cf::RUNTIME); #d#
1347 root 1.110 # safety
1348     $map->{instantiate_time} = $cf::RUNTIME
1349     if $map->{instantiate_time} > $cf::RUNTIME;
1350     } else {
1351 root 1.161 $map = $path->load_orig
1352     or return;
1353 root 1.110
1354 root 1.111 $map->{load_original} = 1;
1355 root 1.110 $map->{instantiate_time} = $cf::RUNTIME;
1356 root 1.132 $map->last_access ($cf::RUNTIME);
1357 root 1.110 $map->instantiate;
1358    
1359     # per-player maps become, after loading, normal maps
1360     $map->per_player (0) if $path->{user_rel};
1361     }
1362    
1363     $map->path ($key);
1364     $map->{path} = $path;
1365 root 1.116 $map->{last_save} = $cf::RUNTIME;
1366 root 1.110
1367 root 1.134 Coro::cede;
1368    
1369 root 1.112 if ($map->should_reset) {
1370     $map->reset;
1371 root 1.123 undef $guard;
1372 root 1.133 $map = find $path
1373 root 1.124 or return;
1374 root 1.112 }
1375 root 1.110
1376     $cf::MAP{$key} = $map
1377     }
1378     }
1379    
1380     sub load {
1381     my ($self) = @_;
1382    
1383 root 1.120 my $path = $self->{path};
1384     my $guard = cf::lock_acquire "map_load:" . $path->as_string;
1385    
1386 root 1.110 return if $self->in_memory != cf::MAP_SWAPPED;
1387    
1388     $self->in_memory (cf::MAP_LOADING);
1389    
1390     $self->alloc;
1391     $self->load_objects ($self->{load_path}, 1)
1392     or return;
1393    
1394 root 1.112 $self->set_object_flag (cf::FLAG_OBJ_ORIGINAL, 1)
1395     if delete $self->{load_original};
1396 root 1.111
1397 root 1.110 if (my $uniq = $path->uniq_path) {
1398     utf8::encode $uniq;
1399     if (aio_open $uniq, O_RDONLY, 0) {
1400     $self->clear_unique_items;
1401     $self->load_objects ($uniq, 0);
1402     }
1403     }
1404    
1405 root 1.134 Coro::cede;
1406    
1407 root 1.110 # now do the right thing for maps
1408     $self->link_multipart_objects;
1409    
1410     if ($self->{path}->is_style_map) {
1411     $self->{deny_save} = 1;
1412     $self->{deny_reset} = 1;
1413     } else {
1414 root 1.164 $self->decay_objects;
1415 root 1.110 $self->fix_auto_apply;
1416     $self->update_buttons;
1417     $self->set_darkness_map;
1418     $self->difficulty ($self->estimate_difficulty)
1419     unless $self->difficulty;
1420     $self->activate;
1421     }
1422    
1423 root 1.134 Coro::cede;
1424    
1425 root 1.110 $self->in_memory (cf::MAP_IN_MEMORY);
1426     }
1427    
1428 root 1.157 # find and load all maps in the 3x3 area around a map
1429     sub load_diag {
1430     my ($map) = @_;
1431    
1432     my @diag; # diagonal neighbours
1433    
1434     for (0 .. 3) {
1435     my $neigh = $map->tile_path ($_)
1436     or next;
1437     $neigh = find $neigh, $map
1438     or next;
1439     $neigh->load;
1440    
1441     push @diag, [$neigh->tile_path (($_ + 3) % 4), $neigh],
1442     [$neigh->tile_path (($_ + 1) % 4), $neigh];
1443     }
1444    
1445     for (@diag) {
1446     my $neigh = find @$_
1447     or next;
1448     $neigh->load;
1449     }
1450     }
1451    
1452 root 1.133 sub find_sync {
1453 root 1.110 my ($path, $origin) = @_;
1454    
1455 root 1.157 cf::sync_job { find $path, $origin }
1456 root 1.133 }
1457    
1458     sub do_load_sync {
1459     my ($map) = @_;
1460 root 1.110
1461 root 1.133 cf::sync_job { $map->load };
1462 root 1.110 }
1463    
1464 root 1.157 our %MAP_PREFETCH;
1465     our $MAP_PREFETCHER = Coro::async {
1466     while () {
1467     while (%MAP_PREFETCH) {
1468     my $key = each %MAP_PREFETCH
1469     or next;
1470     my $path = delete $MAP_PREFETCH{$key};
1471    
1472     my $map = find $path
1473     or next;
1474     $map->load;
1475     }
1476     Coro::schedule;
1477     }
1478     };
1479    
1480     sub find_async {
1481     my ($path, $origin) = @_;
1482    
1483     $path = new cf::path $path, $origin && $origin->path;
1484     my $key = $path->as_string;
1485    
1486     if (my $map = $cf::MAP{$key}) {
1487     return $map if $map->in_memory == cf::MAP_IN_MEMORY;
1488     }
1489    
1490     $MAP_PREFETCH{$key} = $path;
1491     $MAP_PREFETCHER->ready;
1492    
1493     ()
1494     }
1495    
1496 root 1.110 sub save {
1497     my ($self) = @_;
1498    
1499 root 1.137 my $lock = cf::lock_acquire "map_data:" . $self->path;
1500    
1501 root 1.110 $self->{last_save} = $cf::RUNTIME;
1502    
1503     return unless $self->dirty;
1504    
1505 root 1.117 my $save = $self->{path}->save_path; utf8::encode $save;
1506     my $uniq = $self->{path}->uniq_path; utf8::encode $uniq;
1507    
1508 root 1.110 $self->{load_path} = $save;
1509    
1510     return if $self->{deny_save};
1511    
1512 root 1.132 local $self->{last_access} = $self->last_access;#d#
1513    
1514 root 1.143 cf::async {
1515     $_->contr->save for $self->players;
1516     };
1517    
1518 root 1.110 if ($uniq) {
1519     $self->save_objects ($save, cf::IO_HEADER | cf::IO_OBJECTS);
1520     $self->save_objects ($uniq, cf::IO_UNIQUES);
1521     } else {
1522     $self->save_objects ($save, cf::IO_HEADER | cf::IO_OBJECTS | cf::IO_UNIQUES);
1523     }
1524     }
1525    
1526     sub swap_out {
1527     my ($self) = @_;
1528    
1529 root 1.130 # save first because save cedes
1530     $self->save;
1531    
1532 root 1.137 my $lock = cf::lock_acquire "map_data:" . $self->path;
1533    
1534 root 1.110 return if $self->players;
1535     return if $self->in_memory != cf::MAP_IN_MEMORY;
1536     return if $self->{deny_save};
1537    
1538     $self->clear;
1539     $self->in_memory (cf::MAP_SWAPPED);
1540     }
1541    
1542 root 1.112 sub reset_at {
1543     my ($self) = @_;
1544 root 1.110
1545     # TODO: safety, remove and allow resettable per-player maps
1546 root 1.114 return 1e99 if $self->{path}{user_rel};
1547     return 1e99 if $self->{deny_reset};
1548 root 1.110
1549 root 1.112 my $time = $self->fixed_resettime ? $self->{instantiate_time} : $self->last_access;
1550 root 1.114 my $to = List::Util::min $MAX_RESET, $self->reset_timeout || $DEFAULT_RESET;
1551 root 1.110
1552 root 1.112 $time + $to
1553     }
1554    
1555     sub should_reset {
1556     my ($self) = @_;
1557    
1558     $self->reset_at <= $cf::RUNTIME
1559 root 1.111 }
1560    
1561 root 1.113 sub rename {
1562     my ($self, $new_path) = @_;
1563    
1564 root 1.164 $self->{path}->unlink_save;
1565 root 1.113
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.164 $self->{path}->unlink_save;
1589 root 1.111 $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