ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.229
Committed: Sun Apr 1 00:36:34 2007 UTC (17 years, 1 month ago) by root
Branch: MAIN
Changes since 1.228: +6 -5 lines
Log Message:
- add cfhq2xa to the installed binaries
- cfutil: autogenerate 64x64 versions out of existing 32x32 versions
  if required (--cache is highly recommended).
- rename faces to facedata to avoid clashes with the old file.
- add the 64x64 tiles to faceset 1 in the server (unused)

TODO: protocol to split faces (they are too large)
TODO: devise a faceset protocol incompatible enough with gcfclient so it
      doesn't puke.

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