ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.223
Committed: Sun Mar 11 02:12:44 2007 UTC (17 years, 2 months ago) by root
Branch: MAIN
Changes since 1.222: +34 -6 lines
Log Message:
- MAJOR CHANGE
- you now need to use cfutil to install arches.
- former bigfaces are broken in the server
- bigfaces are no longer supported. at all.
- use face numbers instead of pointers
  * saves lotsa space
  * saves lotsa indirections
  * saves lots(?) cpu cycles
- completely rewrote face handling
- faces can now be added at runtime
- reload will add new faces
- this does not apply to animations
- use a hastable instead of binary search (faster) for faces
- face caching is broken
- facesets are gone
- server always reports MAX_FACES to any client who asks

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