ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.362
Committed: Mon Sep 10 12:44:06 2007 UTC (16 years, 8 months ago) by root
Branch: MAIN
Changes since 1.361: +5 -4 lines
Log Message:
- implement tag keyword but do not use it yet
- skip_block now skips known types of sub-blocks
- print the decoded filename if possible

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