ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.165
Committed: Fri Jan 12 22:09:22 2007 UTC (17 years, 4 months ago) by root
Branch: MAIN
Changes since 1.164: +14 -12 lines
Log Message:
fix obvious bugs, i am still unhappy with the design

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