ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.357
Committed: Tue Sep 4 08:42:58 2007 UTC (16 years, 8 months ago) by root
Branch: MAIN
Changes since 1.356: +2 -2 lines
Log Message:
- clean up stuff
- get rid of map_load lock
- improve change_object

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.250 use Socket;
9 root 1.6 use Storable;
10 root 1.224 use Event;
11 root 1.23 use Opcode;
12     use Safe;
13     use Safe::Hole;
14 root 1.19
15 root 1.332 use Coro 3.64 ();
16 root 1.224 use Coro::State;
17 root 1.250 use Coro::Handle;
18 root 1.96 use Coro::Event;
19     use Coro::Timer;
20     use Coro::Signal;
21     use Coro::Semaphore;
22 root 1.105 use Coro::AIO;
23 root 1.237 use Coro::Storable;
24 root 1.332 use Coro::Util ();
25 root 1.96
26 root 1.344 use JSON::XS ();
27 root 1.206 use BDB ();
28 root 1.154 use Data::Dumper;
29 root 1.108 use Digest::MD5;
30 root 1.105 use Fcntl;
31 root 1.227 use YAML::Syck ();
32 root 1.145 use IO::AIO 2.32 ();
33 root 1.32 use Time::HiRes;
34 root 1.208 use Compress::LZF;
35 root 1.302 use Digest::MD5 ();
36 root 1.208
37 root 1.227 # configure various modules to our taste
38     #
39 root 1.237 $Storable::canonical = 1; # reduce rsync transfers
40 root 1.224 Coro::State::cctx_stacksize 256000; # 1-2MB stack, for deep recursions in maze generator
41 root 1.208 Compress::LZF::sfreeze_cr { }; # prime Compress::LZF so it does not use require later
42 root 1.227
43 root 1.224 $Event::Eval = 1; # no idea why this is required, but it is
44 root 1.1
45 root 1.72 # work around bug in YAML::Syck - bad news for perl6, will it be as broken wrt. unicode?
46     $YAML::Syck::ImplicitUnicode = 1;
47    
48 root 1.139 $Coro::main->prio (Coro::PRIO_MAX); # run main coroutine ("the server") with very high priority
49 root 1.1
50 root 1.227 sub WF_AUTOCANCEL () { 1 } # automatically cancel this watcher on reload
51    
52 root 1.85 our %COMMAND = ();
53     our %COMMAND_TIME = ();
54 root 1.159
55     our @EXTS = (); # list of extension package names
56 root 1.85 our %EXTCMD = ();
57 root 1.287 our %EXTICMD = ();
58 root 1.159 our %EXT_CORO = (); # coroutines bound to extensions
59 root 1.161 our %EXT_MAP = (); # pluggable maps
60 root 1.85
61 root 1.223 our $RELOAD; # number of reloads so far
62 root 1.1 our @EVENT;
63 root 1.253
64     our $CONFDIR = confdir;
65     our $DATADIR = datadir;
66     our $LIBDIR = "$DATADIR/ext";
67     our $PODDIR = "$DATADIR/pod";
68     our $MAPDIR = "$DATADIR/" . mapdir;
69     our $LOCALDIR = localdir;
70     our $TMPDIR = "$LOCALDIR/" . tmpdir;
71     our $UNIQUEDIR = "$LOCALDIR/" . uniquedir;
72     our $PLAYERDIR = "$LOCALDIR/" . playerdir;
73     our $RANDOMDIR = "$LOCALDIR/random";
74     our $BDBDIR = "$LOCALDIR/db";
75 root 1.1
76 root 1.245 our $TICK = MAX_TIME * 1e-6; # this is a CONSTANT(!)
77 root 1.35 our $TICK_WATCHER;
78 root 1.168 our $AIO_POLL_WATCHER;
79 root 1.214 our $NEXT_RUNTIME_WRITE; # when should the runtime file be written
80 root 1.35 our $NEXT_TICK;
81 root 1.103 our $NOW;
82 root 1.205 our $USE_FSYNC = 1; # use fsync to write maps - default off
83 root 1.35
84 root 1.206 our $BDB_POLL_WATCHER;
85     our $DB_ENV;
86    
87 root 1.70 our %CFG;
88    
89 root 1.84 our $UPTIME; $UPTIME ||= time;
90 root 1.103 our $RUNTIME;
91    
92 root 1.356 our (%PLAYER, %PLAYER_LOADING); # all users
93     our (%MAP, %MAP_LOADING ); # all maps
94 root 1.166 our $LINK_MAP; # the special {link} map, which is always available
95 root 1.103
96 root 1.166 # used to convert map paths into valid unix filenames by replacing / by ∕
97     our $PATH_SEP = "∕"; # U+2215, chosen purely for visual reasons
98    
99 root 1.265 our $LOAD; # a number between 0 (idle) and 1 (too many objects)
100     our $LOADAVG; # same thing, but with alpha-smoothing
101     our $tick_start; # for load detecting purposes
102    
103 root 1.103 binmode STDOUT;
104     binmode STDERR;
105    
106     # read virtual server time, if available
107 root 1.253 unless ($RUNTIME || !-e "$LOCALDIR/runtime") {
108     open my $fh, "<", "$LOCALDIR/runtime"
109 root 1.103 or die "unable to read runtime file: $!";
110     $RUNTIME = <$fh> + 0.;
111     }
112    
113 root 1.253 mkdir $_
114     for $LOCALDIR, $TMPDIR, $UNIQUEDIR, $PLAYERDIR, $RANDOMDIR, $BDBDIR;
115 root 1.103
116 root 1.131 our $EMERGENCY_POSITION;
117 root 1.110
118 root 1.199 sub cf::map::normalise;
119    
120 root 1.70 #############################################################################
121    
122     =head2 GLOBAL VARIABLES
123    
124     =over 4
125    
126 root 1.83 =item $cf::UPTIME
127    
128     The timestamp of the server start (so not actually an uptime).
129    
130 root 1.103 =item $cf::RUNTIME
131    
132     The time this server has run, starts at 0 and is increased by $cf::TICK on
133     every server tick.
134    
135 root 1.253 =item $cf::CONFDIR $cf::DATADIR $cf::LIBDIR $cf::PODDIR
136     $cf::MAPDIR $cf::LOCALDIR $cf::TMPDIR $cf::UNIQUEDIR
137     $cf::PLAYERDIR $cf::RANDOMDIR $cf::BDBDIR
138    
139     Various directories - "/etc", read-only install directory, perl-library
140     directory, pod-directory, read-only maps directory, "/var", "/var/tmp",
141     unique-items directory, player file directory, random maps directory and
142     database environment.
143 root 1.70
144 root 1.103 =item $cf::NOW
145    
146     The time of the last (current) server tick.
147    
148 root 1.70 =item $cf::TICK
149    
150     The interval between server ticks, in seconds.
151    
152 root 1.265 =item $cf::LOADAVG
153    
154     The current CPU load on the server (alpha-smoothed), as a value between 0
155     (none) and 1 (overloaded), indicating how much time is spent on processing
156     objects per tick. Healthy values are < 0.5.
157    
158     =item $cf::LOAD
159    
160     The raw value load value from the last tick.
161    
162 root 1.70 =item %cf::CFG
163    
164     Configuration for the server, loaded from C</etc/crossfire/config>, or
165     from wherever your confdir points to.
166    
167 root 1.239 =item cf::wait_for_tick, cf::wait_for_tick_begin
168 root 1.155
169 root 1.239 These are functions that inhibit the current coroutine one tick. cf::wait_for_tick_begin only
170     returns directly I<after> the tick processing (and consequently, can only wake one process
171     per tick), while cf::wait_for_tick wakes up all waiters after tick processing.
172 root 1.155
173 elmex 1.310 =item @cf::INVOKE_RESULTS
174    
175     This array contains the results of the last C<invoke ()> call. When
176     C<cf::override> is called C<@cf::INVOKE_RESULTS> is set to the parameters of
177     that call.
178    
179 root 1.70 =back
180    
181     =cut
182    
183 root 1.1 BEGIN {
184     *CORE::GLOBAL::warn = sub {
185     my $msg = join "", @_;
186 root 1.103
187 root 1.1 $msg .= "\n"
188     unless $msg =~ /\n$/;
189    
190 root 1.255 $msg =~ s/([\x00-\x08\x0b-\x1f])/sprintf "\\x%02x", ord $1/ge;
191 root 1.250
192     utf8::encode $msg;
193 root 1.146 LOG llevError, $msg;
194 root 1.1 };
195     }
196    
197 root 1.93 @safe::cf::global::ISA = @cf::global::ISA = 'cf::attachable';
198     @safe::cf::object::ISA = @cf::object::ISA = 'cf::attachable';
199     @safe::cf::player::ISA = @cf::player::ISA = 'cf::attachable';
200     @safe::cf::client::ISA = @cf::client::ISA = 'cf::attachable';
201     @safe::cf::map::ISA = @cf::map::ISA = 'cf::attachable';
202 root 1.273 @safe::cf::arch::ISA = @cf::arch::ISA = 'cf::object';
203     @safe::cf::object::player::ISA = @cf::object::player::ISA = 'cf::object'; # not really true (yet)
204 root 1.25
205 root 1.41 # we bless all objects into (empty) derived classes to force a method lookup
206 root 1.25 # within the Safe compartment.
207 root 1.86 for my $pkg (qw(
208 root 1.100 cf::global cf::attachable
209 root 1.86 cf::object cf::object::player
210 root 1.89 cf::client cf::player
211 root 1.86 cf::arch cf::living
212     cf::map cf::party cf::region
213     )) {
214 root 1.25 no strict 'refs';
215 root 1.41 @{"safe::$pkg\::wrap::ISA"} = @{"$pkg\::wrap::ISA"} = $pkg;
216 root 1.25 }
217 root 1.1
218 root 1.18 $Event::DIED = sub {
219     warn "error in event callback: @_";
220     };
221    
222 root 1.281 #############################################################################
223    
224 root 1.70 =head2 UTILITY FUNCTIONS
225    
226     =over 4
227    
228 root 1.154 =item dumpval $ref
229    
230 root 1.70 =cut
231 root 1.44
232 root 1.154 sub dumpval {
233     eval {
234     local $SIG{__DIE__};
235     my $d;
236     if (1) {
237     $d = new Data::Dumper([$_[0]], ["*var"]);
238     $d->Terse(1);
239     $d->Indent(2);
240     $d->Quotekeys(0);
241     $d->Useqq(1);
242     #$d->Bless(...);
243     $d->Seen($_[1]) if @_ > 1;
244     $d = $d->Dump();
245     }
246     $d =~ s/([\x00-\x07\x09\x0b\x0c\x0e-\x1f])/sprintf "\\x%02x", ord($1)/ge;
247     $d
248     } || "[unable to dump $_[0]: '$@']";
249     }
250    
251 root 1.70 =item $ref = cf::from_json $json
252    
253     Converts a JSON string into the corresponding perl data structure.
254    
255     =item $json = cf::to_json $ref
256    
257     Converts a perl data structure into its JSON representation.
258    
259 root 1.287 =cut
260    
261 root 1.290 our $json_coder = JSON::XS->new->utf8->max_size (1e6); # accept ~1mb max
262 root 1.287
263     sub to_json ($) { $json_coder->encode ($_[0]) }
264     sub from_json ($) { $json_coder->decode ($_[0]) }
265    
266 root 1.120 =item cf::lock_wait $string
267    
268     Wait until the given lock is available. See cf::lock_acquire.
269    
270     =item my $lock = cf::lock_acquire $string
271    
272     Wait until the given lock is available and then acquires it and returns
273 root 1.135 a Coro::guard object. If the guard object gets destroyed (goes out of scope,
274 root 1.120 for example when the coroutine gets canceled), the lock is automatically
275     returned.
276    
277 root 1.347 Locks are *not* recursive, locking from the same coro twice results in a
278     deadlocked coro.
279    
280 root 1.133 Lock names should begin with a unique identifier (for example, cf::map::find
281     uses map_find and cf::map::load uses map_load).
282 root 1.120
283 root 1.253 =item $locked = cf::lock_active $string
284    
285     Return true if the lock is currently active, i.e. somebody has locked it.
286    
287 root 1.120 =cut
288    
289     our %LOCK;
290    
291     sub lock_wait($) {
292     my ($key) = @_;
293    
294     # wait for lock, if any
295     while ($LOCK{$key}) {
296     push @{ $LOCK{$key} }, $Coro::current;
297     Coro::schedule;
298     }
299     }
300    
301     sub lock_acquire($) {
302     my ($key) = @_;
303    
304     # wait, to be sure we are not locked
305     lock_wait $key;
306    
307     $LOCK{$key} = [];
308    
309 root 1.135 Coro::guard {
310 root 1.120 # wake up all waiters, to be on the safe side
311     $_->ready for @{ delete $LOCK{$key} };
312     }
313     }
314    
315 root 1.253 sub lock_active($) {
316     my ($key) = @_;
317    
318     ! ! $LOCK{$key}
319     }
320    
321 root 1.133 sub freeze_mainloop {
322     return unless $TICK_WATCHER->is_active;
323    
324 root 1.168 my $guard = Coro::guard {
325     $TICK_WATCHER->start;
326     };
327 root 1.133 $TICK_WATCHER->stop;
328     $guard
329     }
330    
331 root 1.315 =item cf::get_slot $time[, $priority[, $name]]
332 root 1.314
333     Allocate $time seconds of blocking CPU time at priority C<$priority>:
334     This call blocks and returns only when you have at least C<$time> seconds
335     of cpu time till the next tick. The slot is only valid till the next cede.
336    
337 root 1.315 The optional C<$name> can be used to identify the job to run. It might be
338     used for statistical purposes and should identify the same time-class.
339    
340 root 1.314 Useful for short background jobs.
341    
342     =cut
343    
344     our @SLOT_QUEUE;
345     our $SLOT_QUEUE;
346    
347     $SLOT_QUEUE->cancel if $SLOT_QUEUE;
348     $SLOT_QUEUE = Coro::async {
349     my $signal = new Coro::Signal;
350    
351     while () {
352     next_job:
353     my $avail = cf::till_tick;
354     if ($avail > 0.01) {
355     for (0 .. $#SLOT_QUEUE) {
356     if ($SLOT_QUEUE[$_][0] < $avail) {
357     my $job = splice @SLOT_QUEUE, $_, 1, ();
358     $job->[2]->send;
359     Coro::cede;
360     goto next_job;
361     }
362     }
363     }
364    
365     if (@SLOT_QUEUE) {
366     # we do not use wait_For_tick() as it returns immediately when tick is inactive
367     push @cf::WAIT_FOR_TICK, $signal;
368     $signal->wait;
369     } else {
370     Coro::schedule;
371     }
372     }
373     };
374    
375 root 1.315 sub get_slot($;$$) {
376     my ($time, $pri, $name) = @_;
377    
378     $time = $TICK * .6 if $time > $TICK * .6;
379     my $sig = new Coro::Signal;
380 root 1.314
381 root 1.315 push @SLOT_QUEUE, [$time, $pri, $sig, $name];
382 root 1.314 @SLOT_QUEUE = sort { $b->[1] <=> $a->[1] } @SLOT_QUEUE;
383     $SLOT_QUEUE->ready;
384     $sig->wait;
385     }
386    
387 root 1.140 =item cf::async { BLOCK }
388    
389     Currently the same as Coro::async_pool, meaning you cannot use
390     C<on_destroy>, C<join> or other gimmicks on these coroutines. The only
391     thing you are allowed to do is call C<prio> on it.
392    
393     =cut
394    
395     BEGIN { *async = \&Coro::async_pool }
396    
397 root 1.106 =item cf::sync_job { BLOCK }
398    
399 root 1.281 The design of Crossfire TRT requires that the main coroutine ($Coro::main)
400     is always able to handle events or runnable, as Crossfire TRT is only
401     partly reentrant. Thus "blocking" it by e.g. waiting for I/O is not
402     acceptable.
403 root 1.106
404     If it must be done, put the blocking parts into C<sync_job>. This will run
405     the given BLOCK in another coroutine while waiting for the result. The
406     server will be frozen during this time, so the block should either finish
407     fast or be very important.
408    
409     =cut
410    
411 root 1.105 sub sync_job(&) {
412     my ($job) = @_;
413    
414     if ($Coro::current == $Coro::main) {
415 root 1.265 my $time = Event::time;
416    
417 root 1.112 # this is the main coro, too bad, we have to block
418     # till the operation succeeds, freezing the server :/
419    
420 root 1.110 # TODO: use suspend/resume instead
421 root 1.112 # (but this is cancel-safe)
422 root 1.133 my $freeze_guard = freeze_mainloop;
423 root 1.112
424     my $busy = 1;
425     my @res;
426    
427 root 1.140 (async {
428 root 1.112 @res = eval { $job->() };
429     warn $@ if $@;
430     undef $busy;
431     })->prio (Coro::PRIO_MAX);
432    
433 root 1.105 while ($busy) {
434 root 1.141 Coro::cede or Event::one_event;
435 root 1.105 }
436 root 1.112
437 root 1.265 $time = Event::time - $time;
438    
439 root 1.266 LOG llevError | logBacktrace, Carp::longmess "long sync job"
440     if $time > $TICK * 0.5 && $TICK_WATCHER->is_active;
441 root 1.265
442     $tick_start += $time; # do not account sync jobs to server load
443    
444 root 1.112 wantarray ? @res : $res[0]
445 root 1.105 } else {
446 root 1.112 # we are in another coroutine, how wonderful, everything just works
447    
448     $job->()
449 root 1.105 }
450     }
451    
452 root 1.140 =item $coro = cf::async_ext { BLOCK }
453 root 1.103
454 root 1.159 Like async, but this coro is automatically being canceled when the
455 root 1.140 extension calling this is being unloaded.
456 root 1.103
457     =cut
458    
459 root 1.140 sub async_ext(&) {
460 root 1.103 my $cb = shift;
461    
462 root 1.140 my $coro = &Coro::async ($cb);
463 root 1.103
464     $coro->on_destroy (sub {
465     delete $EXT_CORO{$coro+0};
466     });
467     $EXT_CORO{$coro+0} = $coro;
468    
469     $coro
470     }
471    
472 root 1.281 =item fork_call { }, $args
473    
474     Executes the given code block with the given arguments in a seperate
475     process, returning the results. Everything must be serialisable with
476     Coro::Storable. May, of course, block. Note that the executed sub may
477     never block itself or use any form of Event handling.
478    
479     =cut
480    
481     sub fork_call(&@) {
482     my ($cb, @args) = @_;
483    
484 root 1.332 # we seemingly have to make a local copy of the whole thing,
485     # otherwise perl prematurely frees the stuff :/
486 root 1.355 # TODO: investigate and fix (likely this will be rather laborious)
487 root 1.281
488 root 1.332 my @res = Coro::Util::fork_eval {
489 root 1.281 reset_signals;
490 root 1.332 &$cb
491     }, @args;
492 root 1.298
493 root 1.332 wantarray ? @res : $res[-1]
494 root 1.281 }
495    
496     =item $value = cf::db_get $family => $key
497    
498     Returns a single value from the environment database.
499    
500     =item cf::db_put $family => $key => $value
501    
502     Stores the given C<$value> in the family. It can currently store binary
503     data only (use Compress::LZF::sfreeze_cr/sthaw to convert to/from binary).
504    
505     =cut
506    
507     our $DB;
508    
509     sub db_init {
510     unless ($DB) {
511     $DB = BDB::db_create $DB_ENV;
512    
513     cf::sync_job {
514     eval {
515     $DB->set_flags (BDB::CHKSUM);
516    
517     BDB::db_open $DB, undef, "db", undef, BDB::BTREE,
518     BDB::CREATE | BDB::AUTO_COMMIT, 0666;
519     cf::cleanup "db_open(db): $!" if $!;
520     };
521     cf::cleanup "db_open(db): $@" if $@;
522     };
523     }
524     }
525    
526     sub db_get($$) {
527     my $key = "$_[0]/$_[1]";
528    
529     cf::sync_job {
530     BDB::db_get $DB, undef, $key, my $data;
531    
532     $! ? ()
533     : $data
534     }
535     }
536    
537     sub db_put($$$) {
538     BDB::dbreq_pri 4;
539     BDB::db_put $DB, undef, "$_[0]/$_[1]", $_[2], 0, sub { };
540     }
541    
542     =item cf::cache $id => [$paths...], $processversion => $process
543    
544     Generic caching function that returns the value of the resource $id,
545     caching and regenerating as required.
546    
547     This function can block.
548    
549     =cut
550    
551     sub cache {
552     my ($id, $src, $processversion, $process) = @_;
553    
554     my $meta =
555     join "\x00",
556     $processversion,
557     map {
558     aio_stat $_
559     and Carp::croak "$_: $!";
560 root 1.264
561 root 1.281 ($_, (stat _)[7,9])
562     } @$src;
563 root 1.264
564 root 1.281 my $dbmeta = db_get cache => "$id/meta";
565     if ($dbmeta ne $meta) {
566     # changed, we may need to process
567 root 1.264
568 root 1.281 my @data;
569     my $md5;
570 root 1.219
571 root 1.281 for (0 .. $#$src) {
572     0 <= aio_load $src->[$_], $data[$_]
573     or Carp::croak "$src->[$_]: $!";
574     }
575 root 1.108
576 root 1.281 # if processing is expensive, check
577     # checksum first
578     if (1) {
579     $md5 =
580     join "\x00",
581     $processversion,
582     map {
583 root 1.346 cf::cede_to_tick;
584 root 1.281 ($src->[$_], Digest::MD5::md5_hex $data[$_])
585     } 0.. $#$src;
586    
587 root 1.186
588 root 1.281 my $dbmd5 = db_get cache => "$id/md5";
589     if ($dbmd5 eq $md5) {
590     db_put cache => "$id/meta", $meta;
591 root 1.108
592 root 1.281 return db_get cache => "$id/data";
593     }
594     }
595 root 1.108
596 root 1.281 my $t1 = Time::HiRes::time;
597     my $data = $process->(\@data);
598     my $t2 = Time::HiRes::time;
599 root 1.264
600 root 1.281 warn "cache: '$id' processed in ", $t2 - $t1, "s\n";
601 root 1.108
602 root 1.281 db_put cache => "$id/data", $data;
603     db_put cache => "$id/md5" , $md5;
604     db_put cache => "$id/meta", $meta;
605 root 1.108
606 root 1.281 return $data;
607     }
608 root 1.263
609 root 1.281 db_get cache => "$id/data"
610 root 1.108 }
611    
612 root 1.230 =item cf::datalog type => key => value, ...
613    
614     Log a datalog packet of the given type with the given key-value pairs.
615    
616     =cut
617    
618     sub datalog($@) {
619     my ($type, %kv) = @_;
620     warn "DATALOG ", JSON::XS->new->ascii->encode ({ %kv, type => $type });
621     }
622    
623 root 1.70 =back
624    
625 root 1.71 =cut
626    
627 root 1.44 #############################################################################
628 root 1.39
629 root 1.93 =head2 ATTACHABLE OBJECTS
630    
631 root 1.94 Many objects in crossfire are so-called attachable objects. That means you can
632     attach callbacks/event handlers (a collection of which is called an "attachment")
633     to it. All such attachable objects support the following methods.
634    
635     In the following description, CLASS can be any of C<global>, C<object>
636     C<player>, C<client> or C<map> (i.e. the attachable objects in
637 root 1.281 Crossfire TRT).
638 root 1.55
639     =over 4
640    
641 root 1.94 =item $attachable->attach ($attachment, key => $value...)
642    
643     =item $attachable->detach ($attachment)
644    
645     Attach/detach a pre-registered attachment to a specific object and give it
646     the specified key/value pairs as arguments.
647    
648     Example, attach a minesweeper attachment to the given object, making it a
649     10x10 minesweeper game:
650 root 1.46
651 root 1.94 $obj->attach (minesweeper => width => 10, height => 10);
652 root 1.53
653 root 1.93 =item $bool = $attachable->attached ($name)
654 root 1.46
655 root 1.93 Checks wether the named attachment is currently attached to the object.
656 root 1.46
657 root 1.94 =item cf::CLASS->attach ...
658 root 1.46
659 root 1.94 =item cf::CLASS->detach ...
660 root 1.92
661 root 1.94 Define an anonymous attachment and attach it to all objects of the given
662     CLASS. See the next function for an explanation of its arguments.
663 root 1.92
664 root 1.93 You can attach to global events by using the C<cf::global> class.
665 root 1.92
666 root 1.94 Example, log all player logins:
667    
668     cf::player->attach (
669     on_login => sub {
670     my ($pl) = @_;
671     ...
672     },
673     );
674    
675     Example, attach to the jeweler skill:
676    
677     cf::object->attach (
678     type => cf::SKILL,
679     subtype => cf::SK_JEWELER,
680     on_use_skill => sub {
681     my ($sk, $ob, $part, $dir, $msg) = @_;
682     ...
683     },
684     );
685    
686     =item cf::CLASS::attachment $name, ...
687    
688     Register an attachment by C<$name> through which attachable objects of the
689     given CLASS can refer to this attachment.
690    
691     Some classes such as crossfire maps and objects can specify attachments
692     that are attached at load/instantiate time, thus the need for a name.
693    
694     These calls expect any number of the following handler/hook descriptions:
695 root 1.46
696     =over 4
697    
698     =item prio => $number
699    
700     Set the priority for all following handlers/hooks (unless overwritten
701     by another C<prio> setting). Lower priority handlers get executed
702     earlier. The default priority is C<0>, and many built-in handlers are
703     registered at priority C<-1000>, so lower priorities should not be used
704     unless you know what you are doing.
705    
706 root 1.93 =item type => $type
707    
708     (Only for C<< cf::object->attach >> calls), limits the attachment to the
709     given type of objects only (the additional parameter C<subtype> can be
710     used to further limit to the given subtype).
711    
712 root 1.46 =item on_I<event> => \&cb
713    
714     Call the given code reference whenever the named event happens (event is
715     something like C<instantiate>, C<apply>, C<use_skill> and so on, and which
716     handlers are recognised generally depends on the type of object these
717     handlers attach to).
718    
719     See F<include/eventinc.h> for the full list of events supported, and their
720     class.
721    
722     =item package => package::
723    
724     Look for sub functions of the name C<< on_I<event> >> in the given
725     package and register them. Only handlers for eevents supported by the
726     object/class are recognised.
727    
728     =back
729    
730 root 1.94 Example, define an attachment called "sockpuppet" that calls the given
731     event handler when a monster attacks:
732    
733     cf::object::attachment sockpuppet =>
734     on_skill_attack => sub {
735     my ($self, $victim) = @_;
736     ...
737     }
738     }
739    
740 root 1.96 =item $attachable->valid
741    
742     Just because you have a perl object does not mean that the corresponding
743     C-level object still exists. If you try to access an object that has no
744     valid C counterpart anymore you get an exception at runtime. This method
745     can be used to test for existence of the C object part without causing an
746     exception.
747    
748 root 1.39 =cut
749    
750 root 1.40 # the following variables are defined in .xs and must not be re-created
751 root 1.100 our @CB_GLOBAL = (); # registry for all global events
752     our @CB_ATTACHABLE = (); # registry for all attachables
753     our @CB_OBJECT = (); # all objects (should not be used except in emergency)
754     our @CB_PLAYER = ();
755     our @CB_CLIENT = ();
756     our @CB_TYPE = (); # registry for type (cf-object class) based events
757     our @CB_MAP = ();
758 root 1.39
759 root 1.45 my %attachment;
760    
761 root 1.170 sub cf::attachable::thawer_merge {
762     # simply override everything except _meta
763     local $_[0]{_meta};
764     %{$_[0]} = %{$_[1]};
765     }
766    
767 root 1.93 sub _attach_cb($$$$) {
768     my ($registry, $event, $prio, $cb) = @_;
769 root 1.39
770     use sort 'stable';
771    
772     $cb = [$prio, $cb];
773    
774     @{$registry->[$event]} = sort
775     { $a->[0] cmp $b->[0] }
776     @{$registry->[$event] || []}, $cb;
777     }
778    
779 root 1.100 # hack
780     my %attachable_klass = map +($_ => 1), KLASS_OBJECT, KLASS_CLIENT, KLASS_PLAYER, KLASS_MAP;
781    
782 root 1.39 # attach handles attaching event callbacks
783     # the only thing the caller has to do is pass the correct
784     # registry (== where the callback attaches to).
785 root 1.93 sub _attach {
786 root 1.45 my ($registry, $klass, @arg) = @_;
787 root 1.39
788 root 1.93 my $object_type;
789 root 1.39 my $prio = 0;
790     my %cb_id = map +("on_" . lc $EVENT[$_][0], $_) , grep $EVENT[$_][1] == $klass, 0 .. $#EVENT;
791    
792 root 1.100 #TODO: get rid of this hack
793     if ($attachable_klass{$klass}) {
794     %cb_id = (%cb_id, map +("on_" . lc $EVENT[$_][0], $_) , grep $EVENT[$_][1] == KLASS_ATTACHABLE, 0 .. $#EVENT);
795     }
796    
797 root 1.45 while (@arg) {
798     my $type = shift @arg;
799 root 1.39
800     if ($type eq "prio") {
801 root 1.45 $prio = shift @arg;
802 root 1.39
803 root 1.93 } elsif ($type eq "type") {
804     $object_type = shift @arg;
805     $registry = $CB_TYPE[$object_type] ||= [];
806    
807     } elsif ($type eq "subtype") {
808     defined $object_type or Carp::croak "subtype specified without type";
809     my $object_subtype = shift @arg;
810 root 1.267 $registry = $CB_TYPE[$object_type + $object_subtype * NUM_TYPES] ||= [];
811 root 1.93
812 root 1.39 } elsif ($type eq "package") {
813 root 1.45 my $pkg = shift @arg;
814 root 1.39
815     while (my ($name, $id) = each %cb_id) {
816     if (my $cb = $pkg->can ($name)) {
817 root 1.93 _attach_cb $registry, $id, $prio, $cb;
818 root 1.39 }
819     }
820    
821     } elsif (exists $cb_id{$type}) {
822 root 1.93 _attach_cb $registry, $cb_id{$type}, $prio, shift @arg;
823 root 1.39
824     } elsif (ref $type) {
825     warn "attaching objects not supported, ignoring.\n";
826    
827     } else {
828 root 1.45 shift @arg;
829 root 1.39 warn "attach argument '$type' not supported, ignoring.\n";
830     }
831     }
832     }
833    
834 root 1.93 sub _object_attach {
835 root 1.48 my ($obj, $name, %arg) = @_;
836 root 1.46
837 root 1.55 return if exists $obj->{_attachment}{$name};
838    
839 root 1.46 if (my $attach = $attachment{$name}) {
840     my $registry = $obj->registry;
841    
842 root 1.47 for (@$attach) {
843     my ($klass, @attach) = @$_;
844 root 1.93 _attach $registry, $klass, @attach;
845 root 1.47 }
846 root 1.46
847 root 1.48 $obj->{$name} = \%arg;
848 root 1.46 } else {
849 root 1.280 warn "object uses attachment '$name' which is not available, postponing.\n";
850 root 1.46 }
851    
852 root 1.50 $obj->{_attachment}{$name} = undef;
853 root 1.46 }
854    
855 root 1.93 sub cf::attachable::attach {
856     if (ref $_[0]) {
857     _object_attach @_;
858     } else {
859     _attach shift->_attach_registry, @_;
860     }
861 root 1.267 _recalc_want;
862 root 1.55 };
863 root 1.46
864 root 1.54 # all those should be optimised
865 root 1.93 sub cf::attachable::detach {
866 root 1.54 my ($obj, $name) = @_;
867 root 1.46
868 root 1.93 if (ref $obj) {
869     delete $obj->{_attachment}{$name};
870     reattach ($obj);
871     } else {
872     Carp::croak "cannot, currently, detach class attachments";
873     }
874 root 1.267 _recalc_want;
875 root 1.55 };
876    
877 root 1.93 sub cf::attachable::attached {
878 root 1.55 my ($obj, $name) = @_;
879    
880     exists $obj->{_attachment}{$name}
881 root 1.39 }
882    
883 root 1.100 for my $klass (qw(ATTACHABLE GLOBAL OBJECT PLAYER CLIENT MAP)) {
884 root 1.93 eval "#line " . __LINE__ . " 'cf.pm'
885     sub cf::\L$klass\E::_attach_registry {
886     (\\\@CB_$klass, KLASS_$klass)
887     }
888 root 1.45
889 root 1.93 sub cf::\L$klass\E::attachment {
890     my \$name = shift;
891 root 1.39
892 root 1.93 \$attachment{\$name} = [[KLASS_$klass, \@_]];
893     }
894     ";
895     die if $@;
896 root 1.52 }
897    
898 root 1.39 our $override;
899 elmex 1.310 our @INVOKE_RESULTS = (); # referenced from .xs code. TODO: play tricks with reify and mortals?
900 root 1.39
901 root 1.45 sub override {
902     $override = 1;
903 elmex 1.310 @INVOKE_RESULTS = (@_);
904 root 1.39 }
905    
906 root 1.45 sub do_invoke {
907 root 1.39 my $event = shift;
908 root 1.40 my $callbacks = shift;
909 root 1.39
910 elmex 1.310 @INVOKE_RESULTS = ();
911 root 1.45
912 root 1.39 local $override;
913    
914 root 1.40 for (@$callbacks) {
915 root 1.39 eval { &{$_->[1]} };
916    
917     if ($@) {
918     warn "$@";
919 root 1.64 warn "... while processing $EVENT[$event][0](@_) event, skipping processing altogether.\n";
920 root 1.39 override;
921     }
922    
923     return 1 if $override;
924     }
925    
926     0
927     }
928    
929 root 1.96 =item $bool = cf::global::invoke (EVENT_CLASS_XXX, ...)
930 root 1.55
931 root 1.96 =item $bool = $attachable->invoke (EVENT_CLASS_XXX, ...)
932 root 1.55
933 root 1.96 Generate an object-specific event with the given arguments.
934 root 1.55
935 root 1.96 This API is preliminary (most likely, the EVENT_CLASS_xxx prefix will be
936 root 1.55 removed in future versions), and there is no public API to access override
937 elmex 1.310 results (if you must, access C<@cf::INVOKE_RESULTS> directly).
938 root 1.55
939     =back
940    
941 root 1.71 =cut
942    
943 root 1.70 #############################################################################
944 root 1.45 # object support
945 root 1.312 #
946    
947     sub _can_merge {
948     my ($ob1, $ob2) = @_;
949    
950     local $Storable::canonical = 1;
951     my $fob1 = Storable::freeze $ob1;
952     my $fob2 = Storable::freeze $ob2;
953    
954     $fob1 eq $fob2
955     }
956 root 1.45
957 root 1.102 sub reattach {
958     # basically do the same as instantiate, without calling instantiate
959     my ($obj) = @_;
960    
961 root 1.169 bless $obj, ref $obj; # re-bless in case extensions have been reloaded
962    
963 root 1.102 my $registry = $obj->registry;
964    
965     @$registry = ();
966    
967     delete $obj->{_attachment} unless scalar keys %{ $obj->{_attachment} || {} };
968    
969     for my $name (keys %{ $obj->{_attachment} || {} }) {
970     if (my $attach = $attachment{$name}) {
971     for (@$attach) {
972     my ($klass, @attach) = @$_;
973     _attach $registry, $klass, @attach;
974     }
975     } else {
976     warn "object uses attachment '$name' that is not available, postponing.\n";
977     }
978     }
979     }
980    
981 root 1.100 cf::attachable->attach (
982     prio => -1000000,
983     on_instantiate => sub {
984     my ($obj, $data) = @_;
985 root 1.45
986 root 1.100 $data = from_json $data;
987 root 1.45
988 root 1.100 for (@$data) {
989     my ($name, $args) = @$_;
990 root 1.49
991 root 1.100 $obj->attach ($name, %{$args || {} });
992     }
993     },
994 root 1.102 on_reattach => \&reattach,
995 root 1.100 on_clone => sub {
996     my ($src, $dst) = @_;
997    
998     @{$dst->registry} = @{$src->registry};
999    
1000     %$dst = %$src;
1001    
1002     %{$dst->{_attachment}} = %{$src->{_attachment}}
1003     if exists $src->{_attachment};
1004     },
1005     );
1006 root 1.45
1007 root 1.46 sub object_freezer_save {
1008 root 1.59 my ($filename, $rdata, $objs) = @_;
1009 root 1.46
1010 root 1.356 my $guard = cf::lock_acquire "io";
1011    
1012 root 1.105 sync_job {
1013     if (length $$rdata) {
1014     warn sprintf "saving %s (%d,%d)\n",
1015     $filename, length $$rdata, scalar @$objs;
1016 root 1.60
1017 root 1.105 if (my $fh = aio_open "$filename~", O_WRONLY | O_CREAT, 0600) {
1018 root 1.60 chmod SAVE_MODE, $fh;
1019 root 1.105 aio_write $fh, 0, (length $$rdata), $$rdata, 0;
1020 root 1.204 aio_fsync $fh if $cf::USE_FSYNC;
1021 root 1.60 close $fh;
1022 root 1.105
1023     if (@$objs) {
1024     if (my $fh = aio_open "$filename.pst~", O_WRONLY | O_CREAT, 0600) {
1025     chmod SAVE_MODE, $fh;
1026     my $data = Storable::nfreeze { version => 1, objs => $objs };
1027     aio_write $fh, 0, (length $data), $data, 0;
1028 root 1.204 aio_fsync $fh if $cf::USE_FSYNC;
1029 root 1.105 close $fh;
1030     aio_rename "$filename.pst~", "$filename.pst";
1031     }
1032     } else {
1033     aio_unlink "$filename.pst";
1034     }
1035    
1036     aio_rename "$filename~", $filename;
1037 root 1.60 } else {
1038 root 1.105 warn "FATAL: $filename~: $!\n";
1039 root 1.60 }
1040 root 1.59 } else {
1041 root 1.105 aio_unlink $filename;
1042     aio_unlink "$filename.pst";
1043 root 1.59 }
1044 root 1.356 };
1045    
1046     undef $guard;
1047 root 1.45 }
1048    
1049 root 1.80 sub object_freezer_as_string {
1050     my ($rdata, $objs) = @_;
1051    
1052     use Data::Dumper;
1053    
1054 root 1.81 $$rdata . Dumper $objs
1055 root 1.80 }
1056    
1057 root 1.46 sub object_thawer_load {
1058     my ($filename) = @_;
1059    
1060 root 1.105 my ($data, $av);
1061 root 1.61
1062 root 1.356 my $guard = cf::lock_acquire "io";
1063    
1064 root 1.105 (aio_load $filename, $data) >= 0
1065     or return;
1066 root 1.61
1067 root 1.105 unless (aio_stat "$filename.pst") {
1068     (aio_load "$filename.pst", $av) >= 0
1069     or return;
1070 root 1.356
1071     undef $guard;
1072 root 1.113 $av = eval { (Storable::thaw $av)->{objs} };
1073 root 1.61 }
1074 root 1.45
1075 root 1.118 warn sprintf "loading %s (%d)\n",
1076 root 1.274 $filename, length $data, scalar @{$av || []};
1077 root 1.356
1078     ($data, $av)
1079 root 1.45 }
1080    
1081 root 1.281 =head2 COMMAND CALLBACKS
1082    
1083     =over 4
1084    
1085     =cut
1086    
1087 root 1.45 #############################################################################
1088 root 1.85 # command handling &c
1089 root 1.39
1090 root 1.85 =item cf::register_command $name => \&callback($ob,$args);
1091 root 1.1
1092 root 1.85 Register a callback for execution when the client sends the user command
1093     $name.
1094 root 1.5
1095 root 1.85 =cut
1096 root 1.5
1097 root 1.85 sub register_command {
1098     my ($name, $cb) = @_;
1099 root 1.5
1100 root 1.85 my $caller = caller;
1101     #warn "registering command '$name/$time' to '$caller'";
1102 root 1.1
1103 root 1.85 push @{ $COMMAND{$name} }, [$caller, $cb];
1104 root 1.1 }
1105    
1106 root 1.85 =item cf::register_extcmd $name => \&callback($pl,$packet);
1107 root 1.1
1108 root 1.287 Register a callback for execution when the client sends an (synchronous)
1109     extcmd packet. Ext commands will be processed in the order they are
1110     received by the server, like other user commands. The first argument is
1111     the logged-in player. Ext commands can only be processed after a player
1112     has logged in successfully.
1113    
1114     If the callback returns something, it is sent back as if reply was being
1115     called.
1116    
1117     =item cf::register_exticmd $name => \&callback($ns,$packet);
1118    
1119     Register a callback for execution when the client sends an (asynchronous)
1120     exticmd packet. Exti commands are processed by the server as soon as they
1121     are received, i.e. out of order w.r.t. other commands. The first argument
1122     is a client socket. Exti commands can be received anytime, even before
1123     log-in.
1124 root 1.1
1125 root 1.85 If the callback returns something, it is sent back as if reply was being
1126     called.
1127 root 1.1
1128 root 1.85 =cut
1129 root 1.1
1130 root 1.16 sub register_extcmd {
1131     my ($name, $cb) = @_;
1132    
1133 root 1.159 $EXTCMD{$name} = $cb;
1134 root 1.16 }
1135    
1136 root 1.287 sub register_exticmd {
1137     my ($name, $cb) = @_;
1138    
1139     $EXTICMD{$name} = $cb;
1140     }
1141    
1142 root 1.93 cf::player->attach (
1143 root 1.85 on_command => sub {
1144     my ($pl, $name, $params) = @_;
1145    
1146     my $cb = $COMMAND{$name}
1147     or return;
1148    
1149     for my $cmd (@$cb) {
1150     $cmd->[1]->($pl->ob, $params);
1151     }
1152    
1153     cf::override;
1154     },
1155     on_extcmd => sub {
1156     my ($pl, $buf) = @_;
1157    
1158 root 1.290 my $msg = eval { $pl->ns->{json_coder}->decode ($buf) };
1159 root 1.85
1160     if (ref $msg) {
1161 root 1.316 my ($type, $reply, @payload) =
1162     "ARRAY" eq ref $msg
1163     ? @$msg
1164     : ($msg->{msgtype}, $msg->{msgid}, %$msg); # TODO: version 1, remove
1165    
1166 root 1.338 my @reply;
1167    
1168 root 1.316 if (my $cb = $EXTCMD{$type}) {
1169 root 1.338 @reply = $cb->($pl, @payload);
1170     }
1171    
1172     $pl->ext_reply ($reply, @reply)
1173     if $reply;
1174 root 1.316
1175 root 1.85 } else {
1176     warn "player " . ($pl->ob->name) . " sent unparseable ext message: <$buf>\n";
1177     }
1178    
1179     cf::override;
1180     },
1181 root 1.93 );
1182 root 1.85
1183 root 1.278 sub load_extensions {
1184     cf::sync_job {
1185     my %todo;
1186    
1187     for my $path (<$LIBDIR/*.ext>) {
1188     next unless -r $path;
1189    
1190     $path =~ /([^\/\\]+)\.ext$/ or die "$path";
1191     my $base = $1;
1192     my $pkg = $1;
1193     $pkg =~ s/[^[:word:]]/_/g;
1194     $pkg = "ext::$pkg";
1195    
1196     open my $fh, "<:utf8", $path
1197     or die "$path: $!";
1198    
1199     my $source = do { local $/; <$fh> };
1200 root 1.1
1201 root 1.278 my %ext = (
1202     path => $path,
1203     base => $base,
1204     pkg => $pkg,
1205     );
1206 root 1.1
1207 root 1.279 $ext{meta} = { map { (split /=/, $_, 2)[0, 1] } split /\s+/, $1 }
1208     if $source =~ /\A#!.*?perl.*?#\s*(.*)$/m;
1209 root 1.1
1210 root 1.278 $ext{source} =
1211     "package $pkg; use strict; use utf8;\n"
1212     . "#line 1 \"$path\"\n{\n"
1213     . $source
1214     . "\n};\n1";
1215 root 1.1
1216 root 1.278 $todo{$base} = \%ext;
1217 root 1.166 }
1218 root 1.1
1219 root 1.278 my %done;
1220     while (%todo) {
1221     my $progress;
1222    
1223     while (my ($k, $v) = each %todo) {
1224 root 1.279 for (split /,\s*/, $v->{meta}{depends}) {
1225 root 1.278 goto skip
1226     unless exists $done{$_};
1227     }
1228    
1229     warn "... loading '$k' into '$v->{pkg}'\n";
1230    
1231     unless (eval $v->{source}) {
1232     my $msg = $@ ? "$v->{path}: $@\n"
1233 root 1.279 : "$v->{base}: extension inactive.\n";
1234 root 1.278
1235     if (exists $v->{meta}{mandatory}) {
1236     warn $msg;
1237     warn "mandatory extension failed to load, exiting.\n";
1238     exit 1;
1239     }
1240    
1241 root 1.279 warn $msg;
1242 root 1.278 }
1243    
1244     $done{$k} = delete $todo{$k};
1245     push @EXTS, $v->{pkg};
1246 root 1.279 $progress = 1;
1247 root 1.278 }
1248 root 1.1
1249 root 1.278 skip:
1250     die "cannot load " . (join ", ", keys %todo) . ": unable to resolve dependencies\n"
1251     unless $progress;
1252     }
1253     };
1254 root 1.1 }
1255    
1256 root 1.8 #############################################################################
1257 root 1.70
1258 root 1.281 =back
1259    
1260 root 1.70 =head2 CORE EXTENSIONS
1261    
1262     Functions and methods that extend core crossfire objects.
1263    
1264 root 1.143 =cut
1265    
1266     package cf::player;
1267    
1268 root 1.154 use Coro::AIO;
1269    
1270 root 1.95 =head3 cf::player
1271    
1272 root 1.70 =over 4
1273 root 1.22
1274 root 1.143 =item cf::player::find $login
1275 root 1.23
1276 root 1.143 Returns the given player object, loading it if necessary (might block).
1277 root 1.23
1278     =cut
1279    
1280 root 1.145 sub playerdir($) {
1281 root 1.253 "$PLAYERDIR/"
1282 root 1.145 . (ref $_[0] ? $_[0]->ob->name : $_[0])
1283     }
1284    
1285 root 1.143 sub path($) {
1286 root 1.145 my $login = ref $_[0] ? $_[0]->ob->name : $_[0];
1287    
1288 root 1.234 (playerdir $login) . "/playerdata"
1289 root 1.143 }
1290    
1291     sub find_active($) {
1292     $cf::PLAYER{$_[0]}
1293     and $cf::PLAYER{$_[0]}->active
1294     and $cf::PLAYER{$_[0]}
1295     }
1296    
1297     sub exists($) {
1298     my ($login) = @_;
1299    
1300     $cf::PLAYER{$login}
1301 root 1.180 or cf::sync_job { !aio_stat path $login }
1302 root 1.143 }
1303    
1304     sub find($) {
1305     return $cf::PLAYER{$_[0]} || do {
1306     my $login = $_[0];
1307    
1308     my $guard = cf::lock_acquire "user_find:$login";
1309    
1310 root 1.151 $cf::PLAYER{$_[0]} || do {
1311 root 1.234 # rename old playerfiles to new ones
1312     #TODO: remove when no longer required
1313     aio_link +(playerdir $login) . "/$login.pl.pst", (playerdir $login) . "/playerdata.pst";
1314     aio_link +(playerdir $login) . "/$login.pl" , (playerdir $login) . "/playerdata";
1315     aio_unlink +(playerdir $login) . "/$login.pl.pst";
1316     aio_unlink +(playerdir $login) . "/$login.pl";
1317    
1318 root 1.356 my $f = new_from_file cf::object::thawer path $login
1319 root 1.151 or return;
1320 root 1.356
1321     $f->next;
1322     my $pl = cf::player::load_pl $f
1323     or return;
1324     local $cf::PLAYER_LOADING{$login} = $pl;
1325     $f->resolve_delayed_derefs;
1326 root 1.151 $cf::PLAYER{$login} = $pl
1327     }
1328     }
1329 root 1.143 }
1330    
1331     sub save($) {
1332     my ($pl) = @_;
1333    
1334     return if $pl->{deny_save};
1335    
1336     my $path = path $pl;
1337     my $guard = cf::lock_acquire "user_save:$path";
1338    
1339     return if $pl->{deny_save};
1340 root 1.146
1341 root 1.154 aio_mkdir playerdir $pl, 0770;
1342 root 1.143 $pl->{last_save} = $cf::RUNTIME;
1343    
1344     $pl->save_pl ($path);
1345 root 1.346 cf::cede_to_tick;
1346 root 1.143 }
1347    
1348     sub new($) {
1349     my ($login) = @_;
1350    
1351     my $self = create;
1352    
1353     $self->ob->name ($login);
1354     $self->{deny_save} = 1;
1355    
1356     $cf::PLAYER{$login} = $self;
1357    
1358     $self
1359 root 1.23 }
1360    
1361 root 1.329 =item $player->send_msg ($channel, $msg, $color, [extra...])
1362    
1363     =cut
1364    
1365     sub send_msg {
1366     my $ns = shift->ns
1367     or return;
1368     $ns->send_msg (@_);
1369     }
1370    
1371 root 1.154 =item $pl->quit_character
1372    
1373     Nukes the player without looking back. If logged in, the connection will
1374     be destroyed. May block for a long time.
1375    
1376     =cut
1377    
1378 root 1.145 sub quit_character {
1379     my ($pl) = @_;
1380    
1381 root 1.220 my $name = $pl->ob->name;
1382    
1383 root 1.145 $pl->{deny_save} = 1;
1384     $pl->password ("*"); # this should lock out the player until we nuked the dir
1385    
1386     $pl->invoke (cf::EVENT_PLAYER_LOGOUT, 1) if $pl->active;
1387     $pl->deactivate;
1388     $pl->invoke (cf::EVENT_PLAYER_QUIT);
1389     $pl->ns->destroy if $pl->ns;
1390    
1391     my $path = playerdir $pl;
1392     my $temp = "$path~$cf::RUNTIME~deleting~";
1393 root 1.154 aio_rename $path, $temp;
1394 root 1.150 delete $cf::PLAYER{$pl->ob->name};
1395     $pl->destroy;
1396 root 1.220
1397     my $prefix = qr<^~\Q$name\E/>;
1398    
1399     # nuke player maps
1400     $cf::MAP{$_}->nuke for grep /$prefix/, keys %cf::MAP;
1401    
1402 root 1.150 IO::AIO::aio_rmtree $temp;
1403 root 1.145 }
1404    
1405 pippijn 1.221 =item $pl->kick
1406    
1407     Kicks a player out of the game. This destroys the connection.
1408    
1409     =cut
1410    
1411     sub kick {
1412     my ($pl, $kicker) = @_;
1413    
1414     $pl->invoke (cf::EVENT_PLAYER_KICK, $kicker);
1415     $pl->killer ("kicked");
1416     $pl->ns->destroy;
1417     }
1418    
1419 root 1.154 =item cf::player::list_logins
1420    
1421     Returns am arrayref of all valid playernames in the system, can take a
1422     while and may block, so not sync_job-capable, ever.
1423    
1424     =cut
1425    
1426     sub list_logins {
1427 root 1.253 my $dirs = aio_readdir $PLAYERDIR
1428 root 1.154 or return [];
1429    
1430     my @logins;
1431    
1432     for my $login (@$dirs) {
1433 root 1.354 my $path = path $login;
1434    
1435     # a .pst is a dead give-away for a valid player
1436     unless (-e "$path.pst") {
1437     my $fh = aio_open $path, Fcntl::O_RDONLY, 0 or next;
1438     aio_read $fh, 0, 512, my $buf, 0 or next;
1439     $buf !~ /^password -------------$/m or next; # official not-valid tag
1440     }
1441 root 1.154
1442     utf8::decode $login;
1443     push @logins, $login;
1444     }
1445    
1446     \@logins
1447     }
1448    
1449     =item $player->maps
1450    
1451 root 1.166 Returns an arrayref of map paths that are private for this
1452 root 1.154 player. May block.
1453    
1454     =cut
1455    
1456     sub maps($) {
1457     my ($pl) = @_;
1458    
1459 root 1.201 $pl = ref $pl ? $pl->ob->name : $pl;
1460    
1461 root 1.154 my $files = aio_readdir playerdir $pl
1462     or return;
1463    
1464     my @paths;
1465    
1466     for (@$files) {
1467     utf8::decode $_;
1468     next if /\.(?:pl|pst)$/;
1469 root 1.158 next unless /^$PATH_SEP/o;
1470 root 1.154
1471 root 1.201 push @paths, cf::map::normalise "~$pl/$_";
1472 root 1.154 }
1473    
1474     \@paths
1475     }
1476    
1477 root 1.283 =item $protocol_xml = $player->expand_cfpod ($crossfire_pod)
1478    
1479     Expand crossfire pod fragments into protocol xml.
1480    
1481     =cut
1482    
1483     sub expand_cfpod {
1484     ((my $self), (local $_)) = @_;
1485    
1486     # escape & and <
1487     s/&/&amp;/g;
1488 root 1.352 s/(?<![BIUGHT])</&lt;/g;
1489 root 1.283
1490     # this is buggy, it needs to properly take care of nested <'s
1491    
1492     1 while
1493     # replace B<>, I<>, U<> etc.
1494     s/B<([^\>]*)>/<b>$1<\/b>/
1495     || s/I<([^\>]*)>/<i>$1<\/i>/
1496     || s/U<([^\>]*)>/<u>$1<\/u>/
1497 root 1.352 || s/T<([^\>]*)>/<big><b>$1<\/b><\/big>/
1498 root 1.283 # replace G<male|female> tags
1499     || s{G<([^>|]*)\|([^>]*)>}{
1500     $self->gender ? $2 : $1
1501     }ge
1502     # replace H<hint text>
1503 root 1.291 || s{H<([^\>]*)>}
1504     {
1505     ("<fg name=\"lightblue\">[$1 (Use hintmode to suppress hints)]</fg>",
1506     "<fg name=\"lightblue\">[Hint suppressed, see hintmode]</fg>",
1507     "")
1508     [$self->{hintmode}]
1509     }ge;
1510 root 1.283
1511     # create single paragraphs (very hackish)
1512     s/(?<=\S)\n(?=\w)/ /g;
1513    
1514 root 1.291 # compress some whitespace
1515 root 1.295 s/\s+\n/\n/g; # ws line-ends
1516     s/\n\n+/\n/g; # double lines
1517     s/^\n+//; # beginning lines
1518     s/\n+$//; # ending lines
1519 root 1.293
1520 root 1.283 $_
1521     }
1522    
1523 root 1.291 sub hintmode {
1524     $_[0]{hintmode} = $_[1] if @_ > 1;
1525     $_[0]{hintmode}
1526     }
1527    
1528 root 1.316 =item $player->ext_reply ($msgid, @msg)
1529 root 1.95
1530     Sends an ext reply to the player.
1531    
1532     =cut
1533    
1534 root 1.316 sub ext_reply($$@) {
1535     my ($self, $id, @msg) = @_;
1536 root 1.95
1537 root 1.336 $self->ns->ext_reply ($id, @msg)
1538 root 1.95 }
1539    
1540 root 1.316 =item $player->ext_msg ($type, @msg)
1541 root 1.231
1542     Sends an ext event to the client.
1543    
1544     =cut
1545    
1546 root 1.316 sub ext_msg($$@) {
1547     my ($self, $type, @msg) = @_;
1548 root 1.231
1549 root 1.316 $self->ns->ext_msg ($type, @msg);
1550 root 1.231 }
1551    
1552 root 1.238 =head3 cf::region
1553    
1554     =over 4
1555    
1556     =cut
1557    
1558     package cf::region;
1559    
1560     =item cf::region::find_by_path $path
1561    
1562 root 1.281 Tries to decuce the likely region for a map knowing only its path.
1563 root 1.238
1564     =cut
1565    
1566     sub find_by_path($) {
1567     my ($path) = @_;
1568    
1569     my ($match, $specificity);
1570    
1571     for my $region (list) {
1572 root 1.304 if ($region->{match} && $path =~ $region->{match}) {
1573 root 1.238 ($match, $specificity) = ($region, $region->specificity)
1574     if $region->specificity > $specificity;
1575     }
1576     }
1577    
1578     $match
1579     }
1580 root 1.143
1581 root 1.95 =back
1582    
1583 root 1.110 =head3 cf::map
1584    
1585     =over 4
1586    
1587     =cut
1588    
1589     package cf::map;
1590    
1591     use Fcntl;
1592     use Coro::AIO;
1593    
1594 root 1.166 use overload
1595 root 1.173 '""' => \&as_string,
1596     fallback => 1;
1597 root 1.166
1598 root 1.133 our $MAX_RESET = 3600;
1599     our $DEFAULT_RESET = 3000;
1600 root 1.110
1601     sub generate_random_map {
1602 root 1.166 my ($self, $rmp) = @_;
1603 root 1.110 # mit "rum" bekleckern, nicht
1604 root 1.166 $self->_create_random_map (
1605 root 1.110 $rmp->{wallstyle}, $rmp->{wall_name}, $rmp->{floorstyle}, $rmp->{monsterstyle},
1606     $rmp->{treasurestyle}, $rmp->{layoutstyle}, $rmp->{doorstyle}, $rmp->{decorstyle},
1607     $rmp->{origin_map}, $rmp->{final_map}, $rmp->{exitstyle}, $rmp->{this_map},
1608     $rmp->{exit_on_final_map},
1609     $rmp->{xsize}, $rmp->{ysize},
1610     $rmp->{expand2x}, $rmp->{layoutoptions1}, $rmp->{layoutoptions2}, $rmp->{layoutoptions3},
1611     $rmp->{symmetry}, $rmp->{difficulty}, $rmp->{difficulty_given}, $rmp->{difficulty_increase},
1612     $rmp->{dungeon_level}, $rmp->{dungeon_depth}, $rmp->{decoroptions}, $rmp->{orientation},
1613     $rmp->{origin_y}, $rmp->{origin_x}, $rmp->{random_seed}, $rmp->{total_map_hp},
1614     $rmp->{map_layout_style}, $rmp->{treasureoptions}, $rmp->{symmetry_used},
1615 root 1.162 (cf::region::find $rmp->{region}), $rmp->{custom}
1616     )
1617 root 1.110 }
1618    
1619 root 1.187 =item cf::map->register ($regex, $prio)
1620    
1621     Register a handler for the map path matching the given regex at the
1622     givne priority (higher is better, built-in handlers have priority 0, the
1623     default).
1624    
1625     =cut
1626    
1627 root 1.166 sub register {
1628 root 1.187 my (undef, $regex, $prio) = @_;
1629 root 1.166 my $pkg = caller;
1630    
1631     no strict;
1632     push @{"$pkg\::ISA"}, __PACKAGE__;
1633    
1634 root 1.187 $EXT_MAP{$pkg} = [$prio, qr<$regex>];
1635 root 1.166 }
1636    
1637     # also paths starting with '/'
1638 root 1.187 $EXT_MAP{"cf::map"} = [0, qr{^(?=/)}];
1639 root 1.166
1640 root 1.170 sub thawer_merge {
1641 root 1.172 my ($self, $merge) = @_;
1642    
1643 root 1.170 # we have to keep some variables in memory intact
1644 root 1.172 local $self->{path};
1645     local $self->{load_path};
1646 root 1.170
1647 root 1.172 $self->SUPER::thawer_merge ($merge);
1648 root 1.170 }
1649    
1650 root 1.166 sub normalise {
1651     my ($path, $base) = @_;
1652    
1653 root 1.192 $path = "$path"; # make sure its a string
1654    
1655 root 1.199 $path =~ s/\.map$//;
1656    
1657 root 1.166 # map plan:
1658     #
1659     # /! non-realised random map exit (special hack!)
1660     # {... are special paths that are not being touched
1661     # ?xxx/... are special absolute paths
1662     # ?random/... random maps
1663     # /... normal maps
1664     # ~user/... per-player map of a specific user
1665    
1666     $path =~ s/$PATH_SEP/\//go;
1667    
1668     # treat it as relative path if it starts with
1669     # something that looks reasonable
1670     if ($path =~ m{^(?:\./|\.\./|\w)}) {
1671     $base or Carp::carp "normalise called with relative path and no base: '$path'";
1672    
1673     $base =~ s{[^/]+/?$}{};
1674     $path = "$base/$path";
1675     }
1676    
1677     for ($path) {
1678     redo if s{//}{/};
1679     redo if s{/\.?/}{/};
1680     redo if s{/[^/]+/\.\./}{/};
1681     }
1682    
1683     $path
1684     }
1685    
1686     sub new_from_path {
1687     my (undef, $path, $base) = @_;
1688    
1689     return $path if UNIVERSAL::isa $path, "cf::map"; # already a map object
1690    
1691     $path = normalise $path, $base;
1692    
1693 root 1.187 for my $pkg (sort { $EXT_MAP{$b}[0] <=> $EXT_MAP{$a}[0] } keys %EXT_MAP) {
1694     if ($path =~ $EXT_MAP{$pkg}[1]) {
1695 root 1.166 my $self = bless cf::map::new, $pkg;
1696     $self->{path} = $path; $self->path ($path);
1697     $self->init; # pass $1 etc.
1698     return $self;
1699     }
1700     }
1701    
1702 root 1.308 Carp::cluck "unable to resolve path '$path' (base '$base').";
1703 root 1.166 ()
1704     }
1705    
1706     sub init {
1707     my ($self) = @_;
1708    
1709     $self
1710     }
1711    
1712     sub as_string {
1713     my ($self) = @_;
1714    
1715     "$self->{path}"
1716     }
1717    
1718     # the displayed name, this is a one way mapping
1719     sub visible_name {
1720     &as_string
1721     }
1722    
1723     # the original (read-only) location
1724     sub load_path {
1725     my ($self) = @_;
1726    
1727 root 1.254 "$MAPDIR/$self->{path}.map"
1728 root 1.166 }
1729    
1730     # the temporary/swap location
1731     sub save_path {
1732     my ($self) = @_;
1733    
1734 root 1.167 (my $path = $_[0]{path}) =~ s/\//$PATH_SEP/g;
1735 root 1.254 "$TMPDIR/$path.map"
1736 root 1.166 }
1737    
1738     # the unique path, undef == no special unique path
1739     sub uniq_path {
1740     my ($self) = @_;
1741    
1742 root 1.167 (my $path = $_[0]{path}) =~ s/\//$PATH_SEP/g;
1743 root 1.253 "$UNIQUEDIR/$path"
1744 root 1.166 }
1745    
1746 root 1.110 # and all this just because we cannot iterate over
1747     # all maps in C++...
1748     sub change_all_map_light {
1749     my ($change) = @_;
1750    
1751 root 1.122 $_->change_map_light ($change)
1752     for grep $_->outdoor, values %cf::MAP;
1753 root 1.110 }
1754    
1755 root 1.275 sub decay_objects {
1756     my ($self) = @_;
1757    
1758     return if $self->{deny_reset};
1759    
1760     $self->do_decay_objects;
1761     }
1762    
1763 root 1.166 sub unlink_save {
1764     my ($self) = @_;
1765    
1766     utf8::encode (my $save = $self->save_path);
1767 root 1.170 IO::AIO::aioreq_pri 4; Coro::AIO::aio_unlink $save;
1768     IO::AIO::aioreq_pri 4; Coro::AIO::aio_unlink "$save.pst";
1769 root 1.166 }
1770    
1771     sub load_header_from($) {
1772     my ($self, $path) = @_;
1773 root 1.110
1774     utf8::encode $path;
1775 root 1.356 my $f = new_from_file cf::object::thawer $path
1776     or return;
1777 root 1.110
1778 root 1.356 $self->_load_header ($f)
1779 root 1.110 or return;
1780    
1781 root 1.356 local $MAP_LOADING{$self->{path}} = $self;
1782     $f->resolve_delayed_derefs;
1783    
1784 root 1.166 $self->{load_path} = $path;
1785 root 1.135
1786 root 1.166 1
1787     }
1788 root 1.110
1789 root 1.188 sub load_header_orig {
1790 root 1.166 my ($self) = @_;
1791 root 1.110
1792 root 1.166 $self->load_header_from ($self->load_path)
1793 root 1.110 }
1794    
1795 root 1.188 sub load_header_temp {
1796 root 1.166 my ($self) = @_;
1797 root 1.110
1798 root 1.166 $self->load_header_from ($self->save_path)
1799     }
1800 root 1.110
1801 root 1.188 sub prepare_temp {
1802     my ($self) = @_;
1803    
1804     $self->last_access ((delete $self->{last_access})
1805     || $cf::RUNTIME); #d#
1806     # safety
1807     $self->{instantiate_time} = $cf::RUNTIME
1808     if $self->{instantiate_time} > $cf::RUNTIME;
1809     }
1810    
1811     sub prepare_orig {
1812     my ($self) = @_;
1813    
1814     $self->{load_original} = 1;
1815     $self->{instantiate_time} = $cf::RUNTIME;
1816     $self->last_access ($cf::RUNTIME);
1817     $self->instantiate;
1818     }
1819    
1820 root 1.166 sub load_header {
1821     my ($self) = @_;
1822 root 1.110
1823 root 1.188 if ($self->load_header_temp) {
1824     $self->prepare_temp;
1825 root 1.166 } else {
1826 root 1.188 $self->load_header_orig
1827 root 1.166 or return;
1828 root 1.188 $self->prepare_orig;
1829 root 1.166 }
1830 root 1.120
1831 root 1.275 $self->{deny_reset} = 1
1832     if $self->no_reset;
1833    
1834 root 1.238 $self->default_region (cf::region::find_by_path $self->{path})
1835     unless $self->default_region;
1836    
1837 root 1.166 1
1838     }
1839 root 1.110
1840 root 1.166 sub find;
1841     sub find {
1842     my ($path, $origin) = @_;
1843 root 1.134
1844 root 1.166 $path = normalise $path, $origin && $origin->path;
1845 root 1.110
1846 root 1.166 cf::lock_wait "map_find:$path";
1847 root 1.110
1848 root 1.166 $cf::MAP{$path} || do {
1849     my $guard = cf::lock_acquire "map_find:$path";
1850     my $map = new_from_path cf::map $path
1851     or return;
1852 root 1.110
1853 root 1.116 $map->{last_save} = $cf::RUNTIME;
1854 root 1.110
1855 root 1.166 $map->load_header
1856     or return;
1857 root 1.134
1858 root 1.275 if ($map->should_reset) {#d#TODO# disabled, crashy (locking issue?)
1859 root 1.185 # doing this can freeze the server in a sync job, obviously
1860     #$cf::WAIT_FOR_TICK->wait;
1861 root 1.112 $map->reset;
1862 root 1.123 undef $guard;
1863 root 1.192 return find $path;
1864 root 1.112 }
1865 root 1.110
1866 root 1.166 $cf::MAP{$path} = $map
1867 root 1.110 }
1868     }
1869    
1870 root 1.188 sub pre_load { }
1871     sub post_load { }
1872    
1873 root 1.110 sub load {
1874     my ($self) = @_;
1875    
1876 root 1.196 local $self->{deny_reset} = 1; # loading can take a long time
1877    
1878 root 1.120 my $path = $self->{path};
1879    
1880 root 1.256 {
1881 root 1.357 my $guard = cf::lock_acquire "map_data:$path";
1882 root 1.256
1883 root 1.357 return unless $self->valid;
1884 root 1.256 return if $self->in_memory != cf::MAP_SWAPPED;
1885 root 1.110
1886 root 1.256 $self->in_memory (cf::MAP_LOADING);
1887 root 1.110
1888 root 1.256 $self->alloc;
1889 root 1.188
1890 root 1.256 $self->pre_load;
1891 root 1.346 cf::cede_to_tick;
1892 root 1.188
1893 root 1.356 my $f = new_from_file cf::object::thawer $self->{load_path};
1894     $f->skip_block;
1895     $self->_load_objects ($f)
1896 root 1.256 or return;
1897 root 1.110
1898 root 1.256 $self->set_object_flag (cf::FLAG_OBJ_ORIGINAL, 1)
1899     if delete $self->{load_original};
1900 root 1.111
1901 root 1.256 if (my $uniq = $self->uniq_path) {
1902     utf8::encode $uniq;
1903 root 1.356 unless (aio_stat $uniq) {
1904     if (my $f = new_from_file cf::object::thawer $uniq) {
1905     $self->clear_unique_items;
1906     $self->_load_objects ($f);
1907     $f->resolve_delayed_derefs;
1908     }
1909 root 1.256 }
1910 root 1.110 }
1911    
1912 root 1.356 $f->resolve_delayed_derefs;
1913    
1914 root 1.346 cf::cede_to_tick;
1915 root 1.256 # now do the right thing for maps
1916     $self->link_multipart_objects;
1917 root 1.110 $self->difficulty ($self->estimate_difficulty)
1918     unless $self->difficulty;
1919 root 1.346 cf::cede_to_tick;
1920 root 1.256
1921     unless ($self->{deny_activate}) {
1922     $self->decay_objects;
1923     $self->fix_auto_apply;
1924     $self->update_buttons;
1925 root 1.346 cf::cede_to_tick;
1926 root 1.256 $self->set_darkness_map;
1927 root 1.346 cf::cede_to_tick;
1928 root 1.256 $self->activate;
1929     }
1930    
1931 root 1.325 $self->{last_save} = $cf::RUNTIME;
1932     $self->last_access ($cf::RUNTIME);
1933 root 1.324
1934 root 1.256 $self->in_memory (cf::MAP_IN_MEMORY);
1935 root 1.110 }
1936    
1937 root 1.188 $self->post_load;
1938 root 1.166 }
1939    
1940     sub customise_for {
1941     my ($self, $ob) = @_;
1942    
1943     return find "~" . $ob->name . "/" . $self->{path}
1944     if $self->per_player;
1945 root 1.134
1946 root 1.275 # return find "?party/" . $ob->name . "/" . $self->{path}
1947     # if $self->per_party;
1948    
1949 root 1.166 $self
1950 root 1.110 }
1951    
1952 root 1.157 # find and load all maps in the 3x3 area around a map
1953 root 1.333 sub load_neighbours {
1954 root 1.157 my ($map) = @_;
1955    
1956 root 1.333 my @neigh; # diagonal neighbours
1957 root 1.157
1958     for (0 .. 3) {
1959     my $neigh = $map->tile_path ($_)
1960     or next;
1961     $neigh = find $neigh, $map
1962     or next;
1963     $neigh->load;
1964    
1965 root 1.333 push @neigh,
1966     [$neigh->tile_path (($_ + 3) % 4), $neigh],
1967     [$neigh->tile_path (($_ + 1) % 4), $neigh];
1968 root 1.157 }
1969    
1970 root 1.333 for (grep defined $_->[0], @neigh) {
1971     my ($path, $origin) = @$_;
1972     my $neigh = find $path, $origin
1973 root 1.157 or next;
1974     $neigh->load;
1975     }
1976     }
1977    
1978 root 1.133 sub find_sync {
1979 root 1.110 my ($path, $origin) = @_;
1980    
1981 root 1.157 cf::sync_job { find $path, $origin }
1982 root 1.133 }
1983    
1984     sub do_load_sync {
1985     my ($map) = @_;
1986 root 1.110
1987 root 1.339 cf::LOG cf::llevDebug | cf::logBacktrace, "do_load_sync"
1988 root 1.342 if $Coro::current == $Coro::main;
1989 root 1.339
1990 root 1.133 cf::sync_job { $map->load };
1991 root 1.110 }
1992    
1993 root 1.157 our %MAP_PREFETCH;
1994 root 1.183 our $MAP_PREFETCHER = undef;
1995 root 1.157
1996     sub find_async {
1997 root 1.339 my ($path, $origin, $load) = @_;
1998 root 1.157
1999 root 1.166 $path = normalise $path, $origin && $origin->{path};
2000 root 1.157
2001 root 1.166 if (my $map = $cf::MAP{$path}) {
2002 root 1.340 return $map if !$load || $map->in_memory == cf::MAP_IN_MEMORY;
2003 root 1.157 }
2004    
2005 root 1.339 $MAP_PREFETCH{$path} |= $load;
2006    
2007 root 1.183 $MAP_PREFETCHER ||= cf::async {
2008     while (%MAP_PREFETCH) {
2009 root 1.339 while (my ($k, $v) = each %MAP_PREFETCH) {
2010     if (my $map = find $k) {
2011     $map->load if $v;
2012 root 1.308 }
2013 root 1.183
2014 root 1.339 delete $MAP_PREFETCH{$k};
2015 root 1.183 }
2016     }
2017     undef $MAP_PREFETCHER;
2018     };
2019 root 1.189 $MAP_PREFETCHER->prio (6);
2020 root 1.157
2021     ()
2022     }
2023    
2024 root 1.110 sub save {
2025     my ($self) = @_;
2026    
2027 root 1.345 my $lock = cf::lock_acquire "map_data:$self->{path}";
2028 root 1.137
2029 root 1.110 $self->{last_save} = $cf::RUNTIME;
2030    
2031     return unless $self->dirty;
2032    
2033 root 1.166 my $save = $self->save_path; utf8::encode $save;
2034     my $uniq = $self->uniq_path; utf8::encode $uniq;
2035 root 1.117
2036 root 1.110 $self->{load_path} = $save;
2037    
2038     return if $self->{deny_save};
2039    
2040 root 1.132 local $self->{last_access} = $self->last_access;#d#
2041    
2042 root 1.143 cf::async {
2043     $_->contr->save for $self->players;
2044     };
2045    
2046 root 1.110 if ($uniq) {
2047 root 1.166 $self->_save_objects ($save, cf::IO_HEADER | cf::IO_OBJECTS);
2048     $self->_save_objects ($uniq, cf::IO_UNIQUES);
2049 root 1.110 } else {
2050 root 1.166 $self->_save_objects ($save, cf::IO_HEADER | cf::IO_OBJECTS | cf::IO_UNIQUES);
2051 root 1.110 }
2052     }
2053    
2054     sub swap_out {
2055     my ($self) = @_;
2056    
2057 root 1.130 # save first because save cedes
2058     $self->save;
2059    
2060 root 1.345 my $lock = cf::lock_acquire "map_data:$self->{path}";
2061 root 1.137
2062 root 1.110 return if $self->players;
2063     return if $self->in_memory != cf::MAP_IN_MEMORY;
2064     return if $self->{deny_save};
2065    
2066     $self->clear;
2067     $self->in_memory (cf::MAP_SWAPPED);
2068     }
2069    
2070 root 1.112 sub reset_at {
2071     my ($self) = @_;
2072 root 1.110
2073     # TODO: safety, remove and allow resettable per-player maps
2074 root 1.114 return 1e99 if $self->{deny_reset};
2075 root 1.110
2076 root 1.112 my $time = $self->fixed_resettime ? $self->{instantiate_time} : $self->last_access;
2077 root 1.114 my $to = List::Util::min $MAX_RESET, $self->reset_timeout || $DEFAULT_RESET;
2078 root 1.110
2079 root 1.112 $time + $to
2080     }
2081    
2082     sub should_reset {
2083     my ($self) = @_;
2084    
2085     $self->reset_at <= $cf::RUNTIME
2086 root 1.111 }
2087    
2088 root 1.110 sub reset {
2089     my ($self) = @_;
2090    
2091 root 1.167 my $lock = cf::lock_acquire "map_data:$self->{path}";
2092 root 1.137
2093 root 1.110 return if $self->players;
2094    
2095 root 1.274 warn "resetting map ", $self->path;
2096 root 1.110
2097 root 1.210 $self->in_memory (cf::MAP_SWAPPED);
2098    
2099     # need to save uniques path
2100     unless ($self->{deny_save}) {
2101     my $uniq = $self->uniq_path; utf8::encode $uniq;
2102    
2103     $self->_save_objects ($uniq, cf::IO_UNIQUES)
2104     if $uniq;
2105     }
2106    
2107 root 1.111 delete $cf::MAP{$self->path};
2108 root 1.110
2109 root 1.167 $self->clear;
2110    
2111 root 1.110 $_->clear_links_to ($self) for values %cf::MAP;
2112    
2113 root 1.166 $self->unlink_save;
2114 root 1.111 $self->destroy;
2115 root 1.110 }
2116    
2117 root 1.114 my $nuke_counter = "aaaa";
2118    
2119     sub nuke {
2120     my ($self) = @_;
2121    
2122 root 1.349 {
2123     my $lock = cf::lock_acquire "map_data:$self->{path}";
2124    
2125     delete $cf::MAP{$self->path};
2126 root 1.174
2127 root 1.351 $self->unlink_save;
2128    
2129 root 1.349 bless $self, "cf::map";
2130     delete $self->{deny_reset};
2131     $self->{deny_save} = 1;
2132     $self->reset_timeout (1);
2133     $self->path ($self->{path} = "{nuke}/" . ($nuke_counter++));
2134 root 1.174
2135 root 1.349 $cf::MAP{$self->path} = $self;
2136     }
2137 root 1.174
2138 root 1.114 $self->reset; # polite request, might not happen
2139     }
2140    
2141 root 1.276 =item $maps = cf::map::tmp_maps
2142    
2143     Returns an arrayref with all map paths of currently instantiated and saved
2144 root 1.277 maps. May block.
2145 root 1.276
2146     =cut
2147    
2148     sub tmp_maps() {
2149     [
2150     map {
2151     utf8::decode $_;
2152 root 1.277 /\.map$/
2153 root 1.276 ? normalise $_
2154     : ()
2155     } @{ aio_readdir $TMPDIR or [] }
2156     ]
2157     }
2158    
2159 root 1.277 =item $maps = cf::map::random_maps
2160    
2161     Returns an arrayref with all map paths of currently instantiated and saved
2162     random maps. May block.
2163    
2164     =cut
2165    
2166     sub random_maps() {
2167     [
2168     map {
2169     utf8::decode $_;
2170     /\.map$/
2171     ? normalise "?random/$_"
2172     : ()
2173     } @{ aio_readdir $RANDOMDIR or [] }
2174     ]
2175     }
2176    
2177 root 1.158 =item cf::map::unique_maps
2178    
2179 root 1.166 Returns an arrayref of paths of all shared maps that have
2180 root 1.158 instantiated unique items. May block.
2181    
2182     =cut
2183    
2184     sub unique_maps() {
2185 root 1.276 [
2186     map {
2187     utf8::decode $_;
2188 root 1.277 /\.map$/
2189 root 1.276 ? normalise $_
2190     : ()
2191     } @{ aio_readdir $UNIQUEDIR or [] }
2192     ]
2193 root 1.158 }
2194    
2195 root 1.155 package cf;
2196    
2197     =back
2198    
2199     =head3 cf::object
2200    
2201     =cut
2202    
2203     package cf::object;
2204    
2205     =over 4
2206    
2207     =item $ob->inv_recursive
2208 root 1.110
2209 root 1.155 Returns the inventory of the object _and_ their inventories, recursively.
2210 root 1.110
2211 root 1.155 =cut
2212 root 1.144
2213 root 1.155 sub inv_recursive_;
2214     sub inv_recursive_ {
2215     map { $_, inv_recursive_ $_->inv } @_
2216     }
2217 root 1.110
2218 root 1.155 sub inv_recursive {
2219     inv_recursive_ inv $_[0]
2220 root 1.110 }
2221    
2222 root 1.356 =item $ref = $ob->ref
2223    
2224     creates and returns a persistent reference to an objetc that can be stored as a string.
2225    
2226     =item $ob = cf::object::deref ($refstring)
2227    
2228     returns the objetc referenced by refstring. may return undef when it cnanot find the object,
2229     even if the object actually exists. May block.
2230    
2231     =cut
2232    
2233     sub deref {
2234     my ($ref) = @_;
2235    
2236     # temporary compatibility#TODO#remove
2237     $ref =~ s{^<}{player/<};
2238    
2239     if ($ref =~ m{^player\/(<1\.\d+>)/(.*)$}) {
2240     my ($uuid, $name) = ($1, $2);
2241     my $pl = $cf::PLAYER_LOADING{$name} || cf::player::find $name
2242     or return;
2243     $pl->ob->uuid eq $uuid
2244     or return;
2245    
2246     $pl->ob
2247     } else {
2248     warn "$ref: cannot resolve object reference\n";
2249     undef
2250     }
2251     }
2252    
2253 root 1.110 package cf;
2254    
2255     =back
2256    
2257 root 1.95 =head3 cf::object::player
2258    
2259     =over 4
2260    
2261 root 1.79 =item $player_object->reply ($npc, $msg[, $flags])
2262 root 1.28
2263     Sends a message to the player, as if the npc C<$npc> replied. C<$npc>
2264     can be C<undef>. Does the right thing when the player is currently in a
2265     dialogue with the given NPC character.
2266    
2267     =cut
2268    
2269 root 1.22 # rough implementation of a future "reply" method that works
2270     # with dialog boxes.
2271 root 1.95 #TODO: the first argument must go, split into a $npc->reply_to ( method
2272 root 1.23 sub cf::object::player::reply($$$;$) {
2273     my ($self, $npc, $msg, $flags) = @_;
2274    
2275     $flags = cf::NDI_BROWN | cf::NDI_UNIQUE unless @_ >= 4;
2276 root 1.22
2277 root 1.24 if ($self->{record_replies}) {
2278     push @{ $self->{record_replies} }, [$npc, $msg, $flags];
2279 elmex 1.282
2280 root 1.24 } else {
2281 elmex 1.282 my $pl = $self->contr;
2282    
2283     if ($pl->{npc_dialog} && $pl->{npc_dialog}->{id}) {
2284 root 1.316 my $dialog = $pl->{npc_dialog};
2285     $dialog->{pl}->ext_msg ($dialog->{id}, update => msg => $dialog->{pl}->expand_cfpod ($msg));
2286 elmex 1.282
2287     } else {
2288     $msg = $npc->name . " says: $msg" if $npc;
2289     $self->message ($msg, $flags);
2290     }
2291 root 1.24 }
2292 root 1.22 }
2293    
2294 root 1.329 =item $object->send_msg ($channel, $msg, $color, [extra...])
2295    
2296     =cut
2297    
2298     sub cf::object::send_msg {
2299     my $pl = shift->contr
2300     or return;
2301     $pl->send_msg (@_);
2302     }
2303    
2304 root 1.79 =item $player_object->may ("access")
2305    
2306     Returns wether the given player is authorized to access resource "access"
2307     (e.g. "command_wizcast").
2308    
2309     =cut
2310    
2311     sub cf::object::player::may {
2312     my ($self, $access) = @_;
2313    
2314     $self->flag (cf::FLAG_WIZ) ||
2315     (ref $cf::CFG{"may_$access"}
2316     ? scalar grep $self->name eq $_, @{$cf::CFG{"may_$access"}}
2317     : $cf::CFG{"may_$access"})
2318     }
2319 root 1.70
2320 root 1.115 =item $player_object->enter_link
2321    
2322     Freezes the player and moves him/her to a special map (C<{link}>).
2323    
2324 root 1.166 The player should be reasonably safe there for short amounts of time. You
2325 root 1.115 I<MUST> call C<leave_link> as soon as possible, though.
2326    
2327 root 1.166 Will never block.
2328    
2329 root 1.115 =item $player_object->leave_link ($map, $x, $y)
2330    
2331 root 1.166 Moves the player out of the special C<{link}> map onto the specified
2332     map. If the map is not valid (or omitted), the player will be moved back
2333     to the location he/she was before the call to C<enter_link>, or, if that
2334     fails, to the emergency map position.
2335 root 1.115
2336     Might block.
2337    
2338     =cut
2339    
2340 root 1.166 sub link_map {
2341     unless ($LINK_MAP) {
2342     $LINK_MAP = cf::map::find "{link}"
2343 root 1.196 or cf::cleanup "FATAL: unable to provide {link} map, exiting.";
2344 root 1.166 $LINK_MAP->load;
2345     }
2346    
2347     $LINK_MAP
2348     }
2349    
2350 root 1.110 sub cf::object::player::enter_link {
2351     my ($self) = @_;
2352    
2353 root 1.259 $self->deactivate_recursive;
2354 root 1.258
2355 root 1.166 return if UNIVERSAL::isa $self->map, "ext::map_link";
2356 root 1.110
2357 root 1.120 $self->{_link_pos} ||= [$self->map->{path}, $self->x, $self->y]
2358 root 1.270 if $self->map && $self->map->{path} ne "{link}";
2359 root 1.110
2360 root 1.194 $self->enter_map ($LINK_MAP || link_map, 10, 10);
2361 root 1.110 }
2362    
2363     sub cf::object::player::leave_link {
2364     my ($self, $map, $x, $y) = @_;
2365    
2366 root 1.270 return unless $self->contr->active;
2367    
2368 root 1.110 my $link_pos = delete $self->{_link_pos};
2369    
2370     unless ($map) {
2371     # restore original map position
2372     ($map, $x, $y) = @{ $link_pos || [] };
2373 root 1.133 $map = cf::map::find $map;
2374 root 1.110
2375     unless ($map) {
2376     ($map, $x, $y) = @$EMERGENCY_POSITION;
2377 root 1.133 $map = cf::map::find $map
2378 root 1.110 or die "FATAL: cannot load emergency map\n";
2379     }
2380     }
2381    
2382     ($x, $y) = (-1, -1)
2383     unless (defined $x) && (defined $y);
2384    
2385     # use -1 or undef as default coordinates, not 0, 0
2386     ($x, $y) = ($map->enter_x, $map->enter_y)
2387     if $x <=0 && $y <= 0;
2388    
2389     $map->load;
2390 root 1.333 $map->load_neighbours;
2391 root 1.110
2392 root 1.143 return unless $self->contr->active;
2393 root 1.110 $self->activate_recursive;
2394 root 1.215
2395     local $self->{_prev_pos} = $link_pos; # ugly hack for rent.ext
2396 root 1.110 $self->enter_map ($map, $x, $y);
2397     }
2398    
2399 root 1.306 =item $player_object->goto ($path, $x, $y[, $check->($map)[, $done->()]])
2400 root 1.268
2401     Moves the player to the given map-path and coordinates by first freezing
2402     her, loading and preparing them map, calling the provided $check callback
2403     that has to return the map if sucecssful, and then unfreezes the player on
2404 root 1.306 the new (success) or old (failed) map position. In either case, $done will
2405     be called at the end of this process.
2406 root 1.110
2407     =cut
2408    
2409 root 1.270 our $GOTOGEN;
2410    
2411 root 1.136 sub cf::object::player::goto {
2412 root 1.306 my ($self, $path, $x, $y, $check, $done) = @_;
2413 root 1.268
2414 root 1.270 # do generation counting so two concurrent goto's will be executed in-order
2415     my $gen = $self->{_goto_generation} = ++$GOTOGEN;
2416    
2417 root 1.110 $self->enter_link;
2418    
2419 root 1.140 (async {
2420 root 1.197 my $map = eval {
2421     my $map = cf::map::find $path;
2422 root 1.268
2423     if ($map) {
2424     $map = $map->customise_for ($self);
2425     $map = $check->($map) if $check && $map;
2426     } else {
2427     $self->message ("The exit to '$path' is closed", cf::NDI_UNIQUE | cf::NDI_RED);
2428     }
2429    
2430 root 1.197 $map
2431 root 1.268 };
2432    
2433     if ($@) {
2434     $self->message ("Something went wrong within the server, please report this incident!", cf::NDI_UNIQUE | cf::NDI_RED);
2435     LOG llevError | logBacktrace, Carp::longmess $@;
2436     }
2437 root 1.115
2438 root 1.270 if ($gen == $self->{_goto_generation}) {
2439     delete $self->{_goto_generation};
2440     $self->leave_link ($map, $x, $y);
2441     }
2442 root 1.306
2443     $done->() if $done;
2444 root 1.110 })->prio (1);
2445     }
2446    
2447     =item $player_object->enter_exit ($exit_object)
2448    
2449     =cut
2450    
2451     sub parse_random_map_params {
2452     my ($spec) = @_;
2453    
2454     my $rmp = { # defaults
2455 root 1.181 xsize => (cf::rndm 15, 40),
2456     ysize => (cf::rndm 15, 40),
2457     symmetry => (cf::rndm 1, cf::SYMMETRY_XY),
2458 root 1.182 #layout => string,
2459 root 1.110 };
2460    
2461     for (split /\n/, $spec) {
2462     my ($k, $v) = split /\s+/, $_, 2;
2463    
2464     $rmp->{lc $k} = $v if (length $k) && (length $v);
2465     }
2466    
2467     $rmp
2468     }
2469    
2470     sub prepare_random_map {
2471     my ($exit) = @_;
2472    
2473 root 1.179 my $guard = cf::lock_acquire "exit_prepare:$exit";
2474    
2475 root 1.110 # all this does is basically replace the /! path by
2476     # a new random map path (?random/...) with a seed
2477     # that depends on the exit object
2478    
2479     my $rmp = parse_random_map_params $exit->msg;
2480    
2481     if ($exit->map) {
2482 root 1.198 $rmp->{region} = $exit->region->name;
2483 root 1.110 $rmp->{origin_map} = $exit->map->path;
2484     $rmp->{origin_x} = $exit->x;
2485     $rmp->{origin_y} = $exit->y;
2486     }
2487    
2488     $rmp->{random_seed} ||= $exit->random_seed;
2489    
2490     my $data = cf::to_json $rmp;
2491     my $md5 = Digest::MD5::md5_hex $data;
2492 root 1.253 my $meta = "$RANDOMDIR/$md5.meta";
2493 root 1.110
2494 root 1.177 if (my $fh = aio_open "$meta~", O_WRONLY | O_CREAT, 0666) {
2495 root 1.110 aio_write $fh, 0, (length $data), $data, 0;
2496 root 1.177 undef $fh;
2497     aio_rename "$meta~", $meta;
2498 root 1.110
2499     $exit->slaying ("?random/$md5");
2500     $exit->msg (undef);
2501     }
2502     }
2503    
2504     sub cf::object::player::enter_exit {
2505     my ($self, $exit) = @_;
2506    
2507     return unless $self->type == cf::PLAYER;
2508    
2509 root 1.195 if ($exit->slaying eq "/!") {
2510     #TODO: this should de-fi-ni-te-ly not be a sync-job
2511 root 1.233 # the problem is that $exit might not survive long enough
2512     # so it needs to be done right now, right here
2513 root 1.195 cf::sync_job { prepare_random_map $exit };
2514     }
2515    
2516     my $slaying = cf::map::normalise $exit->slaying, $exit->map && $exit->map->path;
2517     my $hp = $exit->stats->hp;
2518     my $sp = $exit->stats->sp;
2519    
2520 root 1.110 $self->enter_link;
2521    
2522 root 1.296 # if exit is damned, update players death & WoR home-position
2523     $self->contr->savebed ($slaying, $hp, $sp)
2524     if $exit->flag (FLAG_DAMNED);
2525    
2526 root 1.140 (async {
2527 root 1.133 $self->deactivate_recursive; # just to be sure
2528 root 1.110 unless (eval {
2529 root 1.195 $self->goto ($slaying, $hp, $sp);
2530 root 1.110
2531     1;
2532     }) {
2533     $self->message ("Something went wrong deep within the crossfire server. "
2534 root 1.233 . "I'll try to bring you back to the map you were before. "
2535     . "Please report this to the dungeon master!",
2536     cf::NDI_UNIQUE | cf::NDI_RED);
2537 root 1.110
2538     warn "ERROR in enter_exit: $@";
2539     $self->leave_link;
2540     }
2541     })->prio (1);
2542     }
2543    
2544 root 1.95 =head3 cf::client
2545    
2546     =over 4
2547    
2548     =item $client->send_drawinfo ($text, $flags)
2549    
2550     Sends a drawinfo packet to the client. Circumvents output buffering so
2551     should not be used under normal circumstances.
2552    
2553 root 1.70 =cut
2554    
2555 root 1.95 sub cf::client::send_drawinfo {
2556     my ($self, $text, $flags) = @_;
2557    
2558     utf8::encode $text;
2559 root 1.225 $self->send_packet (sprintf "drawinfo %d %s", $flags || cf::NDI_BLACK, $text);
2560 root 1.95 }
2561    
2562 root 1.311 =item $client->send_msg ($channel, $msg, $color, [extra...])
2563 root 1.283
2564     Send a drawinfo or msg packet to the client, formatting the msg for the
2565     client if neccessary. C<$type> should be a string identifying the type of
2566     the message, with C<log> being the default. If C<$color> is negative, suppress
2567     the message unless the client supports the msg packet.
2568    
2569     =cut
2570    
2571 root 1.350 our %CHANNEL = (
2572     "c/identify" => {
2573     id => "identify",
2574     title => "Identify",
2575     reply => undef,
2576     tooltip => "Items recently identified",
2577     },
2578 root 1.352 "c/examine" => {
2579     id => "examine",
2580     title => "Examine",
2581     reply => undef,
2582     tooltip => "Signs and other items you examined",
2583     },
2584 root 1.350 );
2585    
2586 root 1.283 sub cf::client::send_msg {
2587 root 1.311 my ($self, $channel, $msg, $color, @extra) = @_;
2588 root 1.283
2589     $msg = $self->pl->expand_cfpod ($msg);
2590    
2591 root 1.323 $color &= cf::NDI_CLIENT_MASK; # just in case...
2592 root 1.311
2593 root 1.350 # check predefined channels, for the benefit of C
2594     $channel = $CHANNEL{$channel} if $CHANNEL{$channel};
2595    
2596 root 1.311 if (ref $channel) {
2597     # send meta info to client, if not yet sent
2598     unless (exists $self->{channel}{$channel->{id}}) {
2599     $self->{channel}{$channel->{id}} = $channel;
2600 root 1.353 $self->ext_msg (channel_info => $channel)
2601     if $self->can_msg;
2602 root 1.311 }
2603    
2604     $channel = $channel->{id};
2605     }
2606    
2607 root 1.313 return unless @extra || length $msg;
2608    
2609 root 1.283 if ($self->can_msg) {
2610 root 1.323 # default colour, mask it out
2611     $color &= ~(cf::NDI_COLOR_MASK | cf::NDI_DEF)
2612     if $color & cf::NDI_DEF;
2613    
2614     $self->send_packet ("msg " . $self->{json_coder}->encode (
2615     [$color & cf::NDI_CLIENT_MASK, $channel, $msg, @extra]));
2616 root 1.283 } else {
2617 root 1.323 if ($color >= 0) {
2618     # replace some tags by gcfclient-compatible ones
2619     for ($msg) {
2620     1 while
2621     s/<b>([^<]*)<\/b>/[b]${1}[\/b]/
2622     || s/<i>([^<]*)<\/i>/[i]${1}[\/i]/
2623     || s/<u>([^<]*)<\/u>/[ul]${1}[\/ul]/
2624     || s/<tt>([^<]*)<\/tt>/[fixed]${1}[\/fixed]/
2625     || s/<fg name=\"([^"]+)\">([^<]*)<\/fg>/[color=$1]${2}[\/color]/;
2626     }
2627    
2628     $color &= cf::NDI_COLOR_MASK;
2629 root 1.283
2630 root 1.327 utf8::encode $msg;
2631    
2632 root 1.284 if (0 && $msg =~ /\[/) {
2633 root 1.331 # COMMAND/INFO
2634     $self->send_packet ("drawextinfo $color 10 8 $msg")
2635 root 1.283 } else {
2636 root 1.286 $msg =~ s/\[\/?(?:b|i|u|fixed|color)[^\]]*\]//g;
2637 root 1.283 $self->send_packet ("drawinfo $color $msg")
2638     }
2639     }
2640     }
2641     }
2642    
2643 root 1.316 =item $client->ext_msg ($type, @msg)
2644 root 1.232
2645 root 1.287 Sends an ext event to the client.
2646 root 1.232
2647     =cut
2648    
2649 root 1.316 sub cf::client::ext_msg($$@) {
2650     my ($self, $type, @msg) = @_;
2651 root 1.232
2652 root 1.343 if ($self->extcmd == 2) {
2653 root 1.316 $self->send_packet ("ext " . $self->{json_coder}->encode ([$type, @msg]));
2654 root 1.343 } elsif ($self->extcmd == 1) { # TODO: remove
2655 root 1.316 push @msg, msgtype => "event_$type";
2656     $self->send_packet ("ext " . $self->{json_coder}->encode ({@msg}));
2657     }
2658 root 1.232 }
2659 root 1.95
2660 root 1.336 =item $client->ext_reply ($msgid, @msg)
2661    
2662     Sends an ext reply to the client.
2663    
2664     =cut
2665    
2666     sub cf::client::ext_reply($$@) {
2667     my ($self, $id, @msg) = @_;
2668    
2669     if ($self->extcmd == 2) {
2670     $self->send_packet ("ext " . $self->{json_coder}->encode (["reply-$id", @msg]));
2671 root 1.343 } elsif ($self->extcmd == 1) {
2672 root 1.336 #TODO: version 1, remove
2673     unshift @msg, msgtype => "reply", msgid => $id;
2674     $self->send_packet ("ext " . $self->{json_coder}->encode ({@msg}));
2675     }
2676     }
2677    
2678 root 1.95 =item $success = $client->query ($flags, "text", \&cb)
2679    
2680     Queues a query to the client, calling the given callback with
2681     the reply text on a reply. flags can be C<cf::CS_QUERY_YESNO>,
2682     C<cf::CS_QUERY_SINGLECHAR> or C<cf::CS_QUERY_HIDEINPUT> or C<0>.
2683    
2684 root 1.287 Queries can fail, so check the return code. Or don't, as queries will
2685     become reliable at some point in the future.
2686 root 1.95
2687     =cut
2688    
2689     sub cf::client::query {
2690     my ($self, $flags, $text, $cb) = @_;
2691    
2692     return unless $self->state == ST_PLAYING
2693     || $self->state == ST_SETUP
2694     || $self->state == ST_CUSTOM;
2695    
2696     $self->state (ST_CUSTOM);
2697    
2698     utf8::encode $text;
2699     push @{ $self->{query_queue} }, [(sprintf "query %d %s", $flags, $text), $cb];
2700    
2701     $self->send_packet ($self->{query_queue}[0][0])
2702     if @{ $self->{query_queue} } == 1;
2703 root 1.287
2704     1
2705 root 1.95 }
2706    
2707     cf::client->attach (
2708 root 1.290 on_connect => sub {
2709     my ($ns) = @_;
2710    
2711     $ns->{json_coder} = JSON::XS->new->utf8->max_size (1e6)->convert_blessed;
2712     },
2713 root 1.95 on_reply => sub {
2714     my ($ns, $msg) = @_;
2715    
2716     # this weird shuffling is so that direct followup queries
2717     # get handled first
2718 root 1.128 my $queue = delete $ns->{query_queue}
2719 root 1.129 or return; # be conservative, not sure how that can happen, but we saw a crash here
2720 root 1.95
2721     (shift @$queue)->[1]->($msg);
2722 root 1.202 return unless $ns->valid; # temporary(?) workaround for callback destroying socket
2723 root 1.95
2724     push @{ $ns->{query_queue} }, @$queue;
2725    
2726     if (@{ $ns->{query_queue} } == @$queue) {
2727     if (@$queue) {
2728     $ns->send_packet ($ns->{query_queue}[0][0]);
2729     } else {
2730 root 1.98 $ns->state (ST_PLAYING) if $ns->state == ST_CUSTOM;
2731 root 1.95 }
2732     }
2733     },
2734 root 1.287 on_exticmd => sub {
2735     my ($ns, $buf) = @_;
2736    
2737 root 1.290 my $msg = eval { $ns->{json_coder}->decode ($buf) };
2738 root 1.287
2739     if (ref $msg) {
2740 root 1.316 my ($type, $reply, @payload) =
2741     "ARRAY" eq ref $msg
2742     ? @$msg
2743     : ($msg->{msgtype}, $msg->{msgid}, %$msg); # TODO: version 1, remove
2744    
2745 root 1.338 my @reply;
2746    
2747 root 1.316 if (my $cb = $EXTICMD{$type}) {
2748 root 1.338 @reply = $cb->($ns, @payload);
2749     }
2750    
2751     $ns->ext_reply ($reply, @reply)
2752     if $reply;
2753 root 1.316
2754 root 1.287 } else {
2755     warn "client " . ($ns->pl ? $ns->pl->ob->name : $ns->host) . " sent unparseable exti message: <$buf>\n";
2756     }
2757    
2758     cf::override;
2759     },
2760 root 1.95 );
2761    
2762 root 1.140 =item $client->async (\&cb)
2763 root 1.96
2764     Create a new coroutine, running the specified callback. The coroutine will
2765     be automatically cancelled when the client gets destroyed (e.g. on logout,
2766     or loss of connection).
2767    
2768     =cut
2769    
2770 root 1.140 sub cf::client::async {
2771 root 1.96 my ($self, $cb) = @_;
2772    
2773 root 1.140 my $coro = &Coro::async ($cb);
2774 root 1.103
2775     $coro->on_destroy (sub {
2776 root 1.96 delete $self->{_coro}{$coro+0};
2777 root 1.103 });
2778 root 1.96
2779     $self->{_coro}{$coro+0} = $coro;
2780 root 1.103
2781     $coro
2782 root 1.96 }
2783    
2784     cf::client->attach (
2785     on_destroy => sub {
2786     my ($ns) = @_;
2787    
2788 root 1.97 $_->cancel for values %{ (delete $ns->{_coro}) || {} };
2789 root 1.96 },
2790     );
2791    
2792 root 1.95 =back
2793    
2794 root 1.70
2795     =head2 SAFE SCRIPTING
2796    
2797     Functions that provide a safe environment to compile and execute
2798     snippets of perl code without them endangering the safety of the server
2799     itself. Looping constructs, I/O operators and other built-in functionality
2800     is not available in the safe scripting environment, and the number of
2801 root 1.79 functions and methods that can be called is greatly reduced.
2802 root 1.70
2803     =cut
2804 root 1.23
2805 root 1.42 our $safe = new Safe "safe";
2806 root 1.23 our $safe_hole = new Safe::Hole;
2807    
2808     $SIG{FPE} = 'IGNORE';
2809    
2810 root 1.328 $safe->permit_only (Opcode::opset qw(
2811     :base_core :base_mem :base_orig :base_math
2812     grepstart grepwhile mapstart mapwhile
2813     sort time
2814     ));
2815 root 1.23
2816 root 1.25 # here we export the classes and methods available to script code
2817    
2818 root 1.70 =pod
2819    
2820 root 1.228 The following functions and methods are available within a safe environment:
2821 root 1.70
2822 root 1.297 cf::object
2823     contr pay_amount pay_player map x y force_find force_add
2824 elmex 1.341 insert remove name archname title slaying race decrease_ob_nr
2825 root 1.297
2826     cf::object::player
2827     player
2828    
2829     cf::player
2830     peaceful
2831    
2832     cf::map
2833     trigger
2834 root 1.70
2835     =cut
2836    
2837 root 1.25 for (
2838 root 1.297 ["cf::object" => qw(contr pay_amount pay_player map force_find force_add x y
2839 elmex 1.341 insert remove inv name archname title slaying race
2840     decrease_ob_nr)],
2841 root 1.25 ["cf::object::player" => qw(player)],
2842     ["cf::player" => qw(peaceful)],
2843 elmex 1.91 ["cf::map" => qw(trigger)],
2844 root 1.25 ) {
2845     no strict 'refs';
2846     my ($pkg, @funs) = @$_;
2847 root 1.41 *{"safe::$pkg\::$_"} = $safe_hole->wrap (\&{"$pkg\::$_"})
2848 root 1.25 for @funs;
2849     }
2850 root 1.23
2851 root 1.70 =over 4
2852    
2853     =item @retval = safe_eval $code, [var => value, ...]
2854    
2855     Compiled and executes the given perl code snippet. additional var/value
2856     pairs result in temporary local (my) scalar variables of the given name
2857     that are available in the code snippet. Example:
2858    
2859     my $five = safe_eval '$first + $second', first => 1, second => 4;
2860    
2861     =cut
2862    
2863 root 1.23 sub safe_eval($;@) {
2864     my ($code, %vars) = @_;
2865    
2866     my $qcode = $code;
2867     $qcode =~ s/"/‟/g; # not allowed in #line filenames
2868     $qcode =~ s/\n/\\n/g;
2869    
2870     local $_;
2871 root 1.41 local @safe::cf::_safe_eval_args = values %vars;
2872 root 1.23
2873 root 1.42 my $eval =
2874 root 1.23 "do {\n"
2875     . "my (" . (join ",", map "\$$_", keys %vars) . ") = \@cf::_safe_eval_args;\n"
2876     . "#line 0 \"{$qcode}\"\n"
2877     . $code
2878     . "\n}"
2879 root 1.25 ;
2880    
2881     sub_generation_inc;
2882 root 1.42 my @res = wantarray ? $safe->reval ($eval) : scalar $safe->reval ($eval);
2883 root 1.25 sub_generation_inc;
2884    
2885 root 1.42 if ($@) {
2886     warn "$@";
2887     warn "while executing safe code '$code'\n";
2888     warn "with arguments " . (join " ", %vars) . "\n";
2889     }
2890    
2891 root 1.25 wantarray ? @res : $res[0]
2892 root 1.23 }
2893    
2894 root 1.69 =item cf::register_script_function $function => $cb
2895    
2896     Register a function that can be called from within map/npc scripts. The
2897     function should be reasonably secure and should be put into a package name
2898     like the extension.
2899    
2900     Example: register a function that gets called whenever a map script calls
2901     C<rent::overview>, as used by the C<rent> extension.
2902    
2903     cf::register_script_function "rent::overview" => sub {
2904     ...
2905     };
2906    
2907     =cut
2908    
2909 root 1.23 sub register_script_function {
2910     my ($fun, $cb) = @_;
2911    
2912     no strict 'refs';
2913 root 1.41 *{"safe::$fun"} = $safe_hole->wrap ($cb);
2914 root 1.23 }
2915    
2916 root 1.70 =back
2917    
2918 root 1.71 =cut
2919    
2920 root 1.23 #############################################################################
2921 root 1.203 # the server's init and main functions
2922    
2923 root 1.246 sub load_facedata($) {
2924     my ($path) = @_;
2925 root 1.223
2926 root 1.348 # HACK to clear player env face cache, we need some signal framework
2927     # for this (global event?)
2928     %ext::player_env::MUSIC_FACE_CACHE = ();
2929    
2930 root 1.344 my $enc = JSON::XS->new->utf8->canonical->relaxed;
2931 root 1.334
2932 root 1.229 warn "loading facedata from $path\n";
2933 root 1.223
2934 root 1.236 my $facedata;
2935     0 < aio_load $path, $facedata
2936 root 1.223 or die "$path: $!";
2937    
2938 root 1.237 $facedata = Coro::Storable::thaw $facedata;
2939 root 1.223
2940 root 1.236 $facedata->{version} == 2
2941 root 1.226 or cf::cleanup "$path: version mismatch, cannot proceed.";
2942    
2943 root 1.334 # patch in the exptable
2944     $facedata->{resource}{"res/exp_table"} = {
2945     type => FT_RSRC,
2946 root 1.337 data => $enc->encode ([map cf::level_to_min_exp $_, 1 .. cf::settings->max_level]),
2947 root 1.334 };
2948     cf::cede_to_tick;
2949    
2950 root 1.236 {
2951     my $faces = $facedata->{faceinfo};
2952    
2953     while (my ($face, $info) = each %$faces) {
2954     my $idx = (cf::face::find $face) || cf::face::alloc $face;
2955 root 1.302 cf::face::set_visibility $idx, $info->{visibility};
2956     cf::face::set_magicmap $idx, $info->{magicmap};
2957 root 1.334 cf::face::set_data $idx, 0, $info->{data32}, Digest::MD5::md5 $info->{data32};
2958     cf::face::set_data $idx, 1, $info->{data64}, Digest::MD5::md5 $info->{data64};
2959 root 1.302
2960     cf::cede_to_tick;
2961 root 1.236 }
2962    
2963     while (my ($face, $info) = each %$faces) {
2964     next unless $info->{smooth};
2965     my $idx = cf::face::find $face
2966     or next;
2967     if (my $smooth = cf::face::find $info->{smooth}) {
2968 root 1.302 cf::face::set_smooth $idx, $smooth;
2969     cf::face::set_smoothlevel $idx, $info->{smoothlevel};
2970 root 1.236 } else {
2971     warn "smooth face '$info->{smooth}' not found for face '$face'";
2972     }
2973 root 1.302
2974     cf::cede_to_tick;
2975 root 1.236 }
2976 root 1.223 }
2977    
2978 root 1.236 {
2979     my $anims = $facedata->{animinfo};
2980    
2981     while (my ($anim, $info) = each %$anims) {
2982     cf::anim::set $anim, $info->{frames}, $info->{facings};
2983 root 1.302 cf::cede_to_tick;
2984 root 1.225 }
2985 root 1.236
2986     cf::anim::invalidate_all; # d'oh
2987 root 1.225 }
2988    
2989 root 1.302 {
2990     # TODO: for gcfclient pleasure, we should give resources
2991     # that gcfclient doesn't grok a >10000 face index.
2992     my $res = $facedata->{resource};
2993    
2994 root 1.321 my $soundconf = delete $res->{"res/sound.conf"};
2995 root 1.320
2996 root 1.302 while (my ($name, $info) = each %$res) {
2997     my $idx = (cf::face::find $name) || cf::face::alloc $name;
2998 root 1.334 my $data;
2999 root 1.307
3000 root 1.318 if ($info->{type} & 1) {
3001     # prepend meta info
3002    
3003 root 1.334 my $meta = $enc->encode ({
3004     name => $name,
3005     %{ $info->{meta} || {} },
3006     });
3007 root 1.307
3008 root 1.334 $data = pack "(w/a*)*", $meta, $info->{data};
3009 root 1.337 } else {
3010     $data = $info->{data};
3011 root 1.307 }
3012 root 1.302
3013 root 1.334 cf::face::set_data $idx, 0, $data, Digest::MD5::md5 $data;
3014 root 1.318 cf::face::set_type $idx, $info->{type};
3015    
3016 root 1.302 cf::cede_to_tick;
3017     }
3018 root 1.321
3019     if ($soundconf) {
3020     $soundconf = $enc->decode (delete $soundconf->{data});
3021    
3022     for (0 .. SOUND_CAST_SPELL_0 - 1) {
3023     my $sound = $soundconf->{compat}[$_]
3024     or next;
3025    
3026     my $face = cf::face::find "sound/$sound->[1]";
3027     cf::sound::set $sound->[0] => $face;
3028     cf::sound::old_sound_index $_, $face; # gcfclient-compat
3029     }
3030    
3031 root 1.326 while (my ($k, $v) = each %{$soundconf->{event}}) {
3032     my $face = cf::face::find "sound/$v";
3033     cf::sound::set $k => $face;
3034     }
3035 root 1.321 }
3036 root 1.302 }
3037    
3038 root 1.223 1
3039     }
3040    
3041 root 1.318 register_exticmd fx_want => sub {
3042     my ($ns, $want) = @_;
3043    
3044     while (my ($k, $v) = each %$want) {
3045     $ns->fx_want ($k, $v);
3046     }
3047     };
3048    
3049 root 1.253 sub reload_regions {
3050 root 1.348 # HACK to clear player env face cache, we need some signal framework
3051     # for this (global event?)
3052     %ext::player_env::MUSIC_FACE_CACHE = ();
3053    
3054 root 1.253 load_resource_file "$MAPDIR/regions"
3055     or die "unable to load regions file\n";
3056 root 1.304
3057     for (cf::region::list) {
3058     $_->{match} = qr/$_->{match}/
3059     if exists $_->{match};
3060     }
3061 root 1.253 }
3062    
3063 root 1.246 sub reload_facedata {
3064 root 1.253 load_facedata "$DATADIR/facedata"
3065 root 1.246 or die "unable to load facedata\n";
3066     }
3067    
3068     sub reload_archetypes {
3069 root 1.253 load_resource_file "$DATADIR/archetypes"
3070 root 1.246 or die "unable to load archetypes\n";
3071 root 1.289 #d# NEED to laod twice to resolve forward references
3072     # this really needs to be done in an extra post-pass
3073     # (which needs to be synchronous, so solve it differently)
3074     load_resource_file "$DATADIR/archetypes"
3075     or die "unable to load archetypes\n";
3076 root 1.241 }
3077    
3078 root 1.246 sub reload_treasures {
3079 root 1.253 load_resource_file "$DATADIR/treasures"
3080 root 1.246 or die "unable to load treasurelists\n";
3081 root 1.241 }
3082    
3083 root 1.223 sub reload_resources {
3084 root 1.245 warn "reloading resource files...\n";
3085    
3086 root 1.246 reload_regions;
3087     reload_facedata;
3088 root 1.274 #reload_archetypes;#d#
3089 root 1.246 reload_archetypes;
3090     reload_treasures;
3091 root 1.245
3092     warn "finished reloading resource files\n";
3093 root 1.223 }
3094    
3095     sub init {
3096     reload_resources;
3097 root 1.203 }
3098 root 1.34
3099 root 1.345 sub reload_config {
3100 root 1.253 open my $fh, "<:utf8", "$CONFDIR/config"
3101 root 1.72 or return;
3102    
3103     local $/;
3104     *CFG = YAML::Syck::Load <$fh>;
3105 root 1.131
3106     $EMERGENCY_POSITION = $CFG{emergency_position} || ["/world/world_105_115", 5, 37];
3107    
3108 root 1.139 $cf::map::MAX_RESET = $CFG{map_max_reset} if exists $CFG{map_max_reset};
3109     $cf::map::DEFAULT_RESET = $CFG{map_default_reset} if exists $CFG{map_default_reset};
3110    
3111 root 1.131 if (exists $CFG{mlockall}) {
3112     eval {
3113 root 1.147 $CFG{mlockall} ? eval "mlockall()" : eval "munlockall()"
3114 root 1.131 and die "WARNING: m(un)lockall failed: $!\n";
3115     };
3116     warn $@ if $@;
3117     }
3118 root 1.72 }
3119    
3120 root 1.39 sub main {
3121 root 1.108 # we must not ever block the main coroutine
3122     local $Coro::idle = sub {
3123 root 1.115 Carp::cluck "FATAL: Coro::idle was called, major BUG, use cf::sync_job!\n";#d#
3124 root 1.175 (async {
3125     Event::one_event;
3126     })->prio (Coro::PRIO_MAX);
3127 root 1.108 };
3128    
3129 root 1.345 reload_config;
3130 root 1.210 db_init;
3131 root 1.61 load_extensions;
3132 root 1.183
3133     $TICK_WATCHER->start;
3134 root 1.34 Event::loop;
3135     }
3136    
3137     #############################################################################
3138 root 1.155 # initialisation and cleanup
3139    
3140     # install some emergency cleanup handlers
3141     BEGIN {
3142     for my $signal (qw(INT HUP TERM)) {
3143     Event->signal (
3144 root 1.189 reentrant => 0,
3145     data => WF_AUTOCANCEL,
3146     signal => $signal,
3147 root 1.191 prio => 0,
3148 root 1.189 cb => sub {
3149 root 1.155 cf::cleanup "SIG$signal";
3150     },
3151     );
3152     }
3153     }
3154    
3155 root 1.281 sub write_runtime {
3156     my $runtime = "$LOCALDIR/runtime";
3157    
3158     # first touch the runtime file to show we are still running:
3159     # the fsync below can take a very very long time.
3160    
3161     IO::AIO::aio_utime $runtime, undef, undef;
3162    
3163     my $guard = cf::lock_acquire "write_runtime";
3164    
3165     my $fh = aio_open "$runtime~", O_WRONLY | O_CREAT, 0644
3166     or return;
3167    
3168     my $value = $cf::RUNTIME + 90 + 10;
3169     # 10 is the runtime save interval, for a monotonic clock
3170     # 60 allows for the watchdog to kill the server.
3171    
3172     (aio_write $fh, 0, (length $value), $value, 0) <= 0
3173     and return;
3174    
3175     # always fsync - this file is important
3176     aio_fsync $fh
3177     and return;
3178    
3179     # touch it again to show we are up-to-date
3180     aio_utime $fh, undef, undef;
3181    
3182     close $fh
3183     or return;
3184    
3185     aio_rename "$runtime~", $runtime
3186     and return;
3187    
3188     warn "runtime file written.\n";
3189    
3190     1
3191     }
3192    
3193 root 1.156 sub emergency_save() {
3194 root 1.155 my $freeze_guard = cf::freeze_mainloop;
3195    
3196     warn "enter emergency perl save\n";
3197    
3198     cf::sync_job {
3199     # use a peculiar iteration method to avoid tripping on perl
3200     # refcount bugs in for. also avoids problems with players
3201 root 1.167 # and maps saved/destroyed asynchronously.
3202 root 1.155 warn "begin emergency player save\n";
3203     for my $login (keys %cf::PLAYER) {
3204     my $pl = $cf::PLAYER{$login} or next;
3205     $pl->valid or next;
3206     $pl->save;
3207     }
3208     warn "end emergency player save\n";
3209    
3210     warn "begin emergency map save\n";
3211     for my $path (keys %cf::MAP) {
3212     my $map = $cf::MAP{$path} or next;
3213     $map->valid or next;
3214     $map->save;
3215     }
3216     warn "end emergency map save\n";
3217 root 1.208
3218     warn "begin emergency database checkpoint\n";
3219     BDB::db_env_txn_checkpoint $DB_ENV;
3220     warn "end emergency database checkpoint\n";
3221 root 1.155 };
3222    
3223     warn "leave emergency perl save\n";
3224     }
3225 root 1.22
3226 root 1.211 sub post_cleanup {
3227     my ($make_core) = @_;
3228    
3229     warn Carp::longmess "post_cleanup backtrace"
3230     if $make_core;
3231     }
3232    
3233 root 1.246 sub do_reload_perl() {
3234 root 1.106 # can/must only be called in main
3235     if ($Coro::current != $Coro::main) {
3236 root 1.183 warn "can only reload from main coroutine";
3237 root 1.106 return;
3238     }
3239    
3240 root 1.103 warn "reloading...";
3241    
3242 root 1.212 warn "entering sync_job";
3243    
3244 root 1.213 cf::sync_job {
3245 root 1.214 cf::write_runtime; # external watchdog should not bark
3246 root 1.212 cf::emergency_save;
3247 root 1.214 cf::write_runtime; # external watchdog should not bark
3248 root 1.183
3249 root 1.212 warn "syncing database to disk";
3250     BDB::db_env_txn_checkpoint $DB_ENV;
3251 root 1.106
3252     # if anything goes wrong in here, we should simply crash as we already saved
3253 root 1.65
3254 root 1.183 warn "cancelling all WF_AUTOCANCEL watchers";
3255 root 1.87 for (Event::all_watchers) {
3256     $_->cancel if $_->data & WF_AUTOCANCEL;
3257     }
3258 root 1.65
3259 root 1.183 warn "flushing outstanding aio requests";
3260     for (;;) {
3261 root 1.208 BDB::flush;
3262 root 1.183 IO::AIO::flush;
3263     Coro::cede;
3264 root 1.208 last unless IO::AIO::nreqs || BDB::nreqs;
3265 root 1.183 warn "iterate...";
3266     }
3267    
3268 root 1.223 ++$RELOAD;
3269    
3270 root 1.183 warn "cancelling all extension coros";
3271 root 1.103 $_->cancel for values %EXT_CORO;
3272     %EXT_CORO = ();
3273    
3274 root 1.183 warn "removing commands";
3275 root 1.159 %COMMAND = ();
3276    
3277 root 1.287 warn "removing ext/exti commands";
3278     %EXTCMD = ();
3279     %EXTICMD = ();
3280 root 1.159
3281 root 1.183 warn "unloading/nuking all extensions";
3282 root 1.159 for my $pkg (@EXTS) {
3283 root 1.160 warn "... unloading $pkg";
3284 root 1.159
3285     if (my $cb = $pkg->can ("unload")) {
3286     eval {
3287     $cb->($pkg);
3288     1
3289     } or warn "$pkg unloaded, but with errors: $@";
3290     }
3291    
3292 root 1.160 warn "... nuking $pkg";
3293 root 1.159 Symbol::delete_package $pkg;
3294 root 1.65 }
3295    
3296 root 1.183 warn "unloading all perl modules loaded from $LIBDIR";
3297 root 1.65 while (my ($k, $v) = each %INC) {
3298     next unless $v =~ /^\Q$LIBDIR\E\/.*\.pm$/;
3299    
3300 root 1.183 warn "... unloading $k";
3301 root 1.65 delete $INC{$k};
3302    
3303     $k =~ s/\.pm$//;
3304     $k =~ s/\//::/g;
3305    
3306     if (my $cb = $k->can ("unload_module")) {
3307     $cb->();
3308     }
3309    
3310     Symbol::delete_package $k;
3311     }
3312    
3313 root 1.183 warn "getting rid of safe::, as good as possible";
3314 root 1.65 Symbol::delete_package "safe::$_"
3315 root 1.103 for qw(cf::attachable cf::object cf::object::player cf::client cf::player cf::map cf::party cf::region);
3316 root 1.65
3317 root 1.183 warn "unloading cf.pm \"a bit\"";
3318 root 1.65 delete $INC{"cf.pm"};
3319 root 1.252 delete $INC{"cf/pod.pm"};
3320 root 1.65
3321     # don't, removes xs symbols, too,
3322     # and global variables created in xs
3323     #Symbol::delete_package __PACKAGE__;
3324    
3325 root 1.183 warn "unload completed, starting to reload now";
3326    
3327 root 1.103 warn "reloading cf.pm";
3328 root 1.65 require cf;
3329 root 1.100 cf::_connect_to_perl; # nominally unnecessary, but cannot hurt
3330    
3331 root 1.183 warn "loading config and database again";
3332 root 1.345 cf::reload_config;
3333 root 1.65
3334 root 1.183 warn "loading extensions";
3335 root 1.65 cf::load_extensions;
3336    
3337 root 1.183 warn "reattaching attachments to objects/players";
3338 root 1.222 _global_reattach; # objects, sockets
3339 root 1.183 warn "reattaching attachments to maps";
3340 root 1.144 reattach $_ for values %MAP;
3341 root 1.222 warn "reattaching attachments to players";
3342     reattach $_ for values %PLAYER;
3343 root 1.183
3344 root 1.212 warn "leaving sync_job";
3345 root 1.183
3346 root 1.212 1
3347     } or do {
3348 root 1.106 warn $@;
3349     warn "error while reloading, exiting.";
3350     exit 1;
3351 root 1.212 };
3352 root 1.106
3353 root 1.159 warn "reloaded";
3354 root 1.65 };
3355    
3356 root 1.175 our $RELOAD_WATCHER; # used only during reload
3357    
3358 root 1.246 sub reload_perl() {
3359     # doing reload synchronously and two reloads happen back-to-back,
3360     # coro crashes during coro_state_free->destroy here.
3361    
3362     $RELOAD_WATCHER ||= Event->timer (
3363     reentrant => 0,
3364     after => 0,
3365     data => WF_AUTOCANCEL,
3366     cb => sub {
3367     do_reload_perl;
3368     undef $RELOAD_WATCHER;
3369     },
3370     );
3371     }
3372    
3373 root 1.111 register_command "reload" => sub {
3374 root 1.65 my ($who, $arg) = @_;
3375    
3376     if ($who->flag (FLAG_WIZ)) {
3377 root 1.175 $who->message ("reloading server.");
3378 root 1.246 async { reload_perl };
3379 root 1.65 }
3380     };
3381    
3382 root 1.27 unshift @INC, $LIBDIR;
3383 root 1.17
3384 root 1.183 my $bug_warning = 0;
3385    
3386 root 1.239 our @WAIT_FOR_TICK;
3387     our @WAIT_FOR_TICK_BEGIN;
3388    
3389     sub wait_for_tick {
3390 root 1.240 return unless $TICK_WATCHER->is_active;
3391 root 1.241 return if $Coro::current == $Coro::main;
3392    
3393 root 1.239 my $signal = new Coro::Signal;
3394     push @WAIT_FOR_TICK, $signal;
3395     $signal->wait;
3396     }
3397    
3398     sub wait_for_tick_begin {
3399 root 1.240 return unless $TICK_WATCHER->is_active;
3400 root 1.241 return if $Coro::current == $Coro::main;
3401    
3402 root 1.239 my $signal = new Coro::Signal;
3403     push @WAIT_FOR_TICK_BEGIN, $signal;
3404     $signal->wait;
3405     }
3406    
3407 root 1.268 my $min = 1e6;#d#
3408     my $avg = 10;
3409 root 1.35 $TICK_WATCHER = Event->timer (
3410 root 1.104 reentrant => 0,
3411 root 1.183 parked => 1,
3412 root 1.191 prio => 0,
3413 root 1.104 at => $NEXT_TICK || $TICK,
3414     data => WF_AUTOCANCEL,
3415     cb => sub {
3416 root 1.183 if ($Coro::current != $Coro::main) {
3417     Carp::cluck "major BUG: server tick called outside of main coro, skipping it"
3418     unless ++$bug_warning > 10;
3419     return;
3420     }
3421    
3422 root 1.265 $NOW = $tick_start = Event::time;
3423 root 1.163
3424 root 1.133 cf::server_tick; # one server iteration
3425 root 1.245
3426 root 1.268 0 && sync_job {#d#
3427     for(1..10) {
3428     my $t = Event::time;
3429     my $map = my $map = new_from_path cf::map "/tmp/x.map"
3430     or die;
3431    
3432     $map->width (50);
3433     $map->height (50);
3434     $map->alloc;
3435 root 1.356 $map->_load_objects ("/tmp/x.map", 1); #TODO: does not work
3436 root 1.268 my $t = Event::time - $t;
3437    
3438     #next unless $t < 0.0013;#d#
3439     if ($t < $min) {
3440     $min = $t;
3441     }
3442     $avg = $avg * 0.99 + $t * 0.01;
3443     }
3444     warn "XXXXXXXXXXXXXXXXXX min $min avg $avg\n";#d#
3445     exit 0;
3446     # 2007-05-22 02:33:04.569 min 0.00112509727478027 avg 0.0012259249572477
3447     };
3448    
3449 root 1.133 $RUNTIME += $TICK;
3450 root 1.35 $NEXT_TICK += $TICK;
3451    
3452 root 1.214 if ($NOW >= $NEXT_RUNTIME_WRITE) {
3453     $NEXT_RUNTIME_WRITE = $NOW + 10;
3454     Coro::async_pool {
3455     write_runtime
3456     or warn "ERROR: unable to write runtime file: $!";
3457     };
3458     }
3459    
3460 root 1.191 # my $AFTER = Event::time;
3461     # warn $AFTER - $NOW;#d#
3462 root 1.190
3463 root 1.245 if (my $sig = shift @WAIT_FOR_TICK_BEGIN) {
3464     $sig->send;
3465     }
3466     while (my $sig = shift @WAIT_FOR_TICK) {
3467     $sig->send;
3468     }
3469    
3470 root 1.265 $NOW = Event::time;
3471    
3472     # if we are delayed by four ticks or more, skip them all
3473     $NEXT_TICK = $NOW if $NOW >= $NEXT_TICK + $TICK * 4;
3474    
3475     $TICK_WATCHER->at ($NEXT_TICK);
3476     $TICK_WATCHER->start;
3477    
3478     $LOAD = ($NOW - $tick_start) / $TICK;
3479     $LOADAVG = $LOADAVG * 0.75 + $LOAD * 0.25;
3480    
3481 root 1.245 _post_tick;
3482 root 1.265
3483    
3484 root 1.35 },
3485     );
3486    
3487 root 1.206 {
3488     BDB::max_poll_time $TICK * 0.1;
3489     $BDB_POLL_WATCHER = Event->io (
3490     reentrant => 0,
3491     fd => BDB::poll_fileno,
3492     poll => 'r',
3493     prio => 0,
3494     data => WF_AUTOCANCEL,
3495     cb => \&BDB::poll_cb,
3496     );
3497     BDB::min_parallel 8;
3498    
3499     BDB::set_sync_prepare {
3500     my $status;
3501     my $current = $Coro::current;
3502     (
3503     sub {
3504     $status = $!;
3505     $current->ready; undef $current;
3506     },
3507     sub {
3508     Coro::schedule while defined $current;
3509     $! = $status;
3510     },
3511     )
3512     };
3513 root 1.77
3514 root 1.206 unless ($DB_ENV) {
3515     $DB_ENV = BDB::db_env_create;
3516    
3517     cf::sync_job {
3518 root 1.208 eval {
3519     BDB::db_env_open
3520     $DB_ENV,
3521 root 1.253 $BDBDIR,
3522 root 1.208 BDB::INIT_LOCK | BDB::INIT_LOG | BDB::INIT_MPOOL | BDB::INIT_TXN
3523     | BDB::RECOVER | BDB::REGISTER | BDB::USE_ENVIRON | BDB::CREATE,
3524     0666;
3525    
3526 root 1.253 cf::cleanup "db_env_open($BDBDIR): $!" if $!;
3527 root 1.208
3528     $DB_ENV->set_flags (BDB::AUTO_COMMIT | BDB::REGION_INIT | BDB::TXN_NOSYNC, 1);
3529     $DB_ENV->set_lk_detect;
3530     };
3531    
3532     cf::cleanup "db_env_open(db): $@" if $@;
3533 root 1.206 };
3534     }
3535     }
3536    
3537     {
3538     IO::AIO::min_parallel 8;
3539    
3540     undef $Coro::AIO::WATCHER;
3541     IO::AIO::max_poll_time $TICK * 0.1;
3542     $AIO_POLL_WATCHER = Event->io (
3543     reentrant => 0,
3544 root 1.214 data => WF_AUTOCANCEL,
3545 root 1.206 fd => IO::AIO::poll_fileno,
3546     poll => 'r',
3547     prio => 6,
3548     cb => \&IO::AIO::poll_cb,
3549     );
3550     }
3551 root 1.108
3552 root 1.262 my $_log_backtrace;
3553    
3554 root 1.260 sub _log_backtrace {
3555     my ($msg, @addr) = @_;
3556    
3557 root 1.262 $msg =~ s/\n//;
3558 root 1.260
3559 root 1.262 # limit the # of concurrent backtraces
3560     if ($_log_backtrace < 2) {
3561     ++$_log_backtrace;
3562     async {
3563     my @bt = fork_call {
3564     @addr = map { sprintf "%x", $_ } @addr;
3565     my $self = (-f "/proc/$$/exe") ? "/proc/$$/exe" : $^X;
3566     open my $fh, "exec addr2line -C -f -i -e \Q$self\E @addr 2>&1 |"
3567     or die "addr2line: $!";
3568    
3569     my @funcs;
3570     my @res = <$fh>;
3571     chomp for @res;
3572     while (@res) {
3573     my ($func, $line) = splice @res, 0, 2, ();
3574     push @funcs, "[$func] $line";
3575     }
3576 root 1.260
3577 root 1.262 @funcs
3578     };
3579 root 1.260
3580 root 1.262 LOG llevInfo, "[ABT] $msg\n";
3581     LOG llevInfo, "[ABT] $_\n" for @bt;
3582     --$_log_backtrace;
3583     };
3584     } else {
3585 root 1.260 LOG llevInfo, "[ABT] $msg\n";
3586 root 1.262 LOG llevInfo, "[ABT] [suppressed]\n";
3587     }
3588 root 1.260 }
3589    
3590 root 1.249 # load additional modules
3591     use cf::pod;
3592    
3593 root 1.125 END { cf::emergency_save }
3594    
3595 root 1.1 1
3596