ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.155
Committed: Tue Jan 9 21:32:42 2007 UTC (17 years, 4 months ago) by root
Branch: MAIN
Changes since 1.154: +80 -39 lines
Log Message:
- added $cf::WAIT_FOR_TICK*
- tuned map-scheduler and player-scheduler
- added $op->inv_recursive
- added objects_size/objects($index) and actives_size/actives($index)
  for access to the object and actives vector.
- more robust player loader
- removed _perl_data attribute support
- SIGINT/TERM/HUP are now safe as they are handled synchronously.
- replaced LOOK_OBJ by client_visible
- cleanups

File Contents

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