ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.166
Committed: Sat Jan 13 23:06:13 2007 UTC (17 years, 4 months ago) by root
Branch: MAIN
Changes since 1.165: +242 -315 lines
Log Message:
WARNING: this release is BROKEN

- rewrote map handling. map types are now completely pluggable, maybe
  *too* pluggable, as everything is a plug-in now.
- mark mandatory extensions as such.
- handle overloaded attachable objects correctly.
- many minor changes.

File Contents

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