ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.154
Committed: Tue Jan 9 15:36:19 2007 UTC (17 years, 4 months ago) by root
Branch: MAIN
Changes since 1.153: +89 -4 lines
Log Message:
- better dm shell
- minor cleanups
- added cf::dumpval, cf::player::list_logins and $player->maps

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