ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.385
Committed: Fri Oct 12 19:13:26 2007 UTC (16 years, 7 months ago) by root
Branch: MAIN
Changes since 1.384: +11 -6 lines
Log Message:
slightly better can_merge

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