ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.153
Committed: Mon Jan 8 23:36:16 2007 UTC (17 years, 4 months ago) by root
Branch: MAIN
Changes since 1.152: +3 -3 lines
Log Message:
major goof in LOG, and defensive programming doesn't help when you use the wrong parameters

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