ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.358
Committed: Fri Sep 7 18:10:52 2007 UTC (16 years, 8 months ago) by root
Branch: MAIN
Changes since 1.357: +16 -2 lines
Log Message:
- complain louadly when we acquire a lock we already hold
- deactivate map before clear calls

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