ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.359
Committed: Sat Sep 8 18:15:55 2007 UTC (16 years, 8 months ago) by root
Branch: MAIN
Changes since 1.358: +4 -3 lines
Log Message:
clear links on swap-out, to avoid obvious causes for crashes

File Contents

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