ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.379
Committed: Thu Oct 4 23:59:07 2007 UTC (16 years, 7 months ago) by root
Branch: MAIN
Changes since 1.378: +2 -0 lines
Log Message:
*** empty log message ***

File Contents

# User Rev Content
1 root 1.1 package cf;
2    
3 root 1.96 use utf8;
4     use strict;
5    
6 root 1.1 use Symbol;
7     use List::Util;
8 root 1.250 use Socket;
9 root 1.6 use Storable;
10 root 1.224 use Event;
11 root 1.23 use Opcode;
12     use Safe;
13     use Safe::Hole;
14 root 1.19
15 root 1.332 use Coro 3.64 ();
16 root 1.224 use Coro::State;
17 root 1.250 use Coro::Handle;
18 root 1.96 use Coro::Event;
19     use Coro::Timer;
20     use Coro::Signal;
21     use Coro::Semaphore;
22 root 1.105 use Coro::AIO;
23 root 1.237 use Coro::Storable;
24 root 1.332 use Coro::Util ();
25 root 1.96
26 root 1.344 use JSON::XS ();
27 root 1.206 use BDB ();
28 root 1.154 use Data::Dumper;
29 root 1.108 use Digest::MD5;
30 root 1.105 use Fcntl;
31 root 1.227 use YAML::Syck ();
32 root 1.145 use IO::AIO 2.32 ();
33 root 1.32 use Time::HiRes;
34 root 1.208 use Compress::LZF;
35 root 1.302 use Digest::MD5 ();
36 root 1.208
37 root 1.227 # configure various modules to our taste
38     #
39 root 1.237 $Storable::canonical = 1; # reduce rsync transfers
40 root 1.224 Coro::State::cctx_stacksize 256000; # 1-2MB stack, for deep recursions in maze generator
41 root 1.208 Compress::LZF::sfreeze_cr { }; # prime Compress::LZF so it does not use require later
42 root 1.227
43 root 1.224 $Event::Eval = 1; # no idea why this is required, but it is
44 root 1.1
45 root 1.72 # work around bug in YAML::Syck - bad news for perl6, will it be as broken wrt. unicode?
46     $YAML::Syck::ImplicitUnicode = 1;
47    
48 root 1.139 $Coro::main->prio (Coro::PRIO_MAX); # run main coroutine ("the server") with very high priority
49 root 1.1
50 root 1.227 sub WF_AUTOCANCEL () { 1 } # automatically cancel this watcher on reload
51    
52 root 1.85 our %COMMAND = ();
53     our %COMMAND_TIME = ();
54 root 1.159
55     our @EXTS = (); # list of extension package names
56 root 1.85 our %EXTCMD = ();
57 root 1.287 our %EXTICMD = ();
58 root 1.159 our %EXT_CORO = (); # coroutines bound to extensions
59 root 1.161 our %EXT_MAP = (); # pluggable maps
60 root 1.85
61 root 1.223 our $RELOAD; # number of reloads so far
62 root 1.1 our @EVENT;
63 root 1.253
64     our $CONFDIR = confdir;
65     our $DATADIR = datadir;
66     our $LIBDIR = "$DATADIR/ext";
67     our $PODDIR = "$DATADIR/pod";
68     our $MAPDIR = "$DATADIR/" . mapdir;
69     our $LOCALDIR = localdir;
70     our $TMPDIR = "$LOCALDIR/" . tmpdir;
71     our $UNIQUEDIR = "$LOCALDIR/" . uniquedir;
72     our $PLAYERDIR = "$LOCALDIR/" . playerdir;
73     our $RANDOMDIR = "$LOCALDIR/random";
74     our $BDBDIR = "$LOCALDIR/db";
75 root 1.1
76 root 1.245 our $TICK = MAX_TIME * 1e-6; # this is a CONSTANT(!)
77 root 1.35 our $TICK_WATCHER;
78 root 1.168 our $AIO_POLL_WATCHER;
79 root 1.214 our $NEXT_RUNTIME_WRITE; # when should the runtime file be written
80 root 1.35 our $NEXT_TICK;
81 root 1.103 our $NOW;
82 root 1.205 our $USE_FSYNC = 1; # use fsync to write maps - default off
83 root 1.35
84 root 1.206 our $BDB_POLL_WATCHER;
85 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     # we do not use wait_For_tick() as it returns immediately when tick is inactive
379     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.379 LOG llevError | logBacktrace, Carp::longmess "sync job";#d#
433    
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     local $Storable::canonical = 1;
977     my $fob1 = Storable::freeze $ob1;
978     my $fob2 = Storable::freeze $ob2;
979    
980     $fob1 eq $fob2
981     }
982 root 1.45
983 root 1.102 sub reattach {
984     # basically do the same as instantiate, without calling instantiate
985     my ($obj) = @_;
986    
987 root 1.169 bless $obj, ref $obj; # re-bless in case extensions have been reloaded
988    
989 root 1.102 my $registry = $obj->registry;
990    
991     @$registry = ();
992    
993     delete $obj->{_attachment} unless scalar keys %{ $obj->{_attachment} || {} };
994    
995     for my $name (keys %{ $obj->{_attachment} || {} }) {
996     if (my $attach = $attachment{$name}) {
997     for (@$attach) {
998     my ($klass, @attach) = @$_;
999     _attach $registry, $klass, @attach;
1000     }
1001     } else {
1002     warn "object uses attachment '$name' that is not available, postponing.\n";
1003     }
1004     }
1005     }
1006    
1007 root 1.100 cf::attachable->attach (
1008     prio => -1000000,
1009     on_instantiate => sub {
1010     my ($obj, $data) = @_;
1011 root 1.45
1012 root 1.100 $data = from_json $data;
1013 root 1.45
1014 root 1.100 for (@$data) {
1015     my ($name, $args) = @$_;
1016 root 1.49
1017 root 1.100 $obj->attach ($name, %{$args || {} });
1018     }
1019     },
1020 root 1.102 on_reattach => \&reattach,
1021 root 1.100 on_clone => sub {
1022     my ($src, $dst) = @_;
1023    
1024     @{$dst->registry} = @{$src->registry};
1025    
1026     %$dst = %$src;
1027    
1028     %{$dst->{_attachment}} = %{$src->{_attachment}}
1029     if exists $src->{_attachment};
1030     },
1031     );
1032 root 1.45
1033 root 1.46 sub object_freezer_save {
1034 root 1.59 my ($filename, $rdata, $objs) = @_;
1035 root 1.46
1036 root 1.105 sync_job {
1037     if (length $$rdata) {
1038 root 1.362 utf8::decode (my $decname = $filename);
1039 root 1.105 warn sprintf "saving %s (%d,%d)\n",
1040 root 1.362 $decname, length $$rdata, scalar @$objs;
1041 root 1.60
1042 root 1.105 if (my $fh = aio_open "$filename~", O_WRONLY | O_CREAT, 0600) {
1043 root 1.60 chmod SAVE_MODE, $fh;
1044 root 1.105 aio_write $fh, 0, (length $$rdata), $$rdata, 0;
1045 root 1.204 aio_fsync $fh if $cf::USE_FSYNC;
1046 root 1.60 close $fh;
1047 root 1.105
1048     if (@$objs) {
1049     if (my $fh = aio_open "$filename.pst~", O_WRONLY | O_CREAT, 0600) {
1050     chmod SAVE_MODE, $fh;
1051     my $data = Storable::nfreeze { version => 1, objs => $objs };
1052     aio_write $fh, 0, (length $data), $data, 0;
1053 root 1.204 aio_fsync $fh if $cf::USE_FSYNC;
1054 root 1.105 close $fh;
1055     aio_rename "$filename.pst~", "$filename.pst";
1056     }
1057     } else {
1058     aio_unlink "$filename.pst";
1059     }
1060    
1061     aio_rename "$filename~", $filename;
1062 root 1.60 } else {
1063 root 1.105 warn "FATAL: $filename~: $!\n";
1064 root 1.60 }
1065 root 1.59 } else {
1066 root 1.105 aio_unlink $filename;
1067     aio_unlink "$filename.pst";
1068 root 1.59 }
1069 root 1.356 };
1070 root 1.45 }
1071    
1072 root 1.80 sub object_freezer_as_string {
1073     my ($rdata, $objs) = @_;
1074    
1075     use Data::Dumper;
1076    
1077 root 1.81 $$rdata . Dumper $objs
1078 root 1.80 }
1079    
1080 root 1.46 sub object_thawer_load {
1081     my ($filename) = @_;
1082    
1083 root 1.105 my ($data, $av);
1084 root 1.61
1085 root 1.105 (aio_load $filename, $data) >= 0
1086     or return;
1087 root 1.61
1088 root 1.105 unless (aio_stat "$filename.pst") {
1089     (aio_load "$filename.pst", $av) >= 0
1090     or return;
1091 root 1.356
1092 root 1.113 $av = eval { (Storable::thaw $av)->{objs} };
1093 root 1.61 }
1094 root 1.45
1095 root 1.362 utf8::decode (my $decname = $filename);
1096     warn sprintf "loading %s (%d,%d)\n",
1097     $decname, length $data, scalar @{$av || []};
1098 root 1.356
1099     ($data, $av)
1100 root 1.45 }
1101    
1102 root 1.281 =head2 COMMAND CALLBACKS
1103    
1104     =over 4
1105    
1106     =cut
1107    
1108 root 1.45 #############################################################################
1109 root 1.85 # command handling &c
1110 root 1.39
1111 root 1.85 =item cf::register_command $name => \&callback($ob,$args);
1112 root 1.1
1113 root 1.85 Register a callback for execution when the client sends the user command
1114     $name.
1115 root 1.5
1116 root 1.85 =cut
1117 root 1.5
1118 root 1.85 sub register_command {
1119     my ($name, $cb) = @_;
1120 root 1.5
1121 root 1.85 my $caller = caller;
1122     #warn "registering command '$name/$time' to '$caller'";
1123 root 1.1
1124 root 1.85 push @{ $COMMAND{$name} }, [$caller, $cb];
1125 root 1.1 }
1126    
1127 root 1.85 =item cf::register_extcmd $name => \&callback($pl,$packet);
1128 root 1.1
1129 root 1.287 Register a callback for execution when the client sends an (synchronous)
1130     extcmd packet. Ext commands will be processed in the order they are
1131     received by the server, like other user commands. The first argument is
1132     the logged-in player. Ext commands can only be processed after a player
1133     has logged in successfully.
1134    
1135     If the callback returns something, it is sent back as if reply was being
1136     called.
1137    
1138     =item cf::register_exticmd $name => \&callback($ns,$packet);
1139    
1140     Register a callback for execution when the client sends an (asynchronous)
1141     exticmd packet. Exti commands are processed by the server as soon as they
1142     are received, i.e. out of order w.r.t. other commands. The first argument
1143     is a client socket. Exti commands can be received anytime, even before
1144     log-in.
1145 root 1.1
1146 root 1.85 If the callback returns something, it is sent back as if reply was being
1147     called.
1148 root 1.1
1149 root 1.85 =cut
1150 root 1.1
1151 root 1.16 sub register_extcmd {
1152     my ($name, $cb) = @_;
1153    
1154 root 1.159 $EXTCMD{$name} = $cb;
1155 root 1.16 }
1156    
1157 root 1.287 sub register_exticmd {
1158     my ($name, $cb) = @_;
1159    
1160     $EXTICMD{$name} = $cb;
1161     }
1162    
1163 root 1.93 cf::player->attach (
1164 root 1.85 on_command => sub {
1165     my ($pl, $name, $params) = @_;
1166    
1167     my $cb = $COMMAND{$name}
1168     or return;
1169    
1170     for my $cmd (@$cb) {
1171     $cmd->[1]->($pl->ob, $params);
1172     }
1173    
1174     cf::override;
1175     },
1176     on_extcmd => sub {
1177     my ($pl, $buf) = @_;
1178    
1179 root 1.290 my $msg = eval { $pl->ns->{json_coder}->decode ($buf) };
1180 root 1.85
1181     if (ref $msg) {
1182 root 1.316 my ($type, $reply, @payload) =
1183     "ARRAY" eq ref $msg
1184     ? @$msg
1185     : ($msg->{msgtype}, $msg->{msgid}, %$msg); # TODO: version 1, remove
1186    
1187 root 1.338 my @reply;
1188    
1189 root 1.316 if (my $cb = $EXTCMD{$type}) {
1190 root 1.338 @reply = $cb->($pl, @payload);
1191     }
1192    
1193     $pl->ext_reply ($reply, @reply)
1194     if $reply;
1195 root 1.316
1196 root 1.85 } else {
1197     warn "player " . ($pl->ob->name) . " sent unparseable ext message: <$buf>\n";
1198     }
1199    
1200     cf::override;
1201     },
1202 root 1.93 );
1203 root 1.85
1204 root 1.278 sub load_extensions {
1205     cf::sync_job {
1206     my %todo;
1207    
1208     for my $path (<$LIBDIR/*.ext>) {
1209     next unless -r $path;
1210    
1211     $path =~ /([^\/\\]+)\.ext$/ or die "$path";
1212     my $base = $1;
1213     my $pkg = $1;
1214     $pkg =~ s/[^[:word:]]/_/g;
1215     $pkg = "ext::$pkg";
1216    
1217     open my $fh, "<:utf8", $path
1218     or die "$path: $!";
1219    
1220     my $source = do { local $/; <$fh> };
1221 root 1.1
1222 root 1.278 my %ext = (
1223     path => $path,
1224     base => $base,
1225     pkg => $pkg,
1226     );
1227 root 1.1
1228 root 1.279 $ext{meta} = { map { (split /=/, $_, 2)[0, 1] } split /\s+/, $1 }
1229     if $source =~ /\A#!.*?perl.*?#\s*(.*)$/m;
1230 root 1.1
1231 root 1.278 $ext{source} =
1232     "package $pkg; use strict; use utf8;\n"
1233     . "#line 1 \"$path\"\n{\n"
1234     . $source
1235     . "\n};\n1";
1236 root 1.1
1237 root 1.278 $todo{$base} = \%ext;
1238 root 1.166 }
1239 root 1.1
1240 root 1.278 my %done;
1241     while (%todo) {
1242     my $progress;
1243    
1244     while (my ($k, $v) = each %todo) {
1245 root 1.279 for (split /,\s*/, $v->{meta}{depends}) {
1246 root 1.278 goto skip
1247     unless exists $done{$_};
1248     }
1249    
1250     warn "... loading '$k' into '$v->{pkg}'\n";
1251    
1252     unless (eval $v->{source}) {
1253     my $msg = $@ ? "$v->{path}: $@\n"
1254 root 1.279 : "$v->{base}: extension inactive.\n";
1255 root 1.278
1256     if (exists $v->{meta}{mandatory}) {
1257     warn $msg;
1258     warn "mandatory extension failed to load, exiting.\n";
1259     exit 1;
1260     }
1261    
1262 root 1.279 warn $msg;
1263 root 1.278 }
1264    
1265     $done{$k} = delete $todo{$k};
1266     push @EXTS, $v->{pkg};
1267 root 1.279 $progress = 1;
1268 root 1.278 }
1269 root 1.1
1270 root 1.278 skip:
1271     die "cannot load " . (join ", ", keys %todo) . ": unable to resolve dependencies\n"
1272     unless $progress;
1273     }
1274     };
1275 root 1.1 }
1276    
1277 root 1.8 #############################################################################
1278 root 1.70
1279 root 1.281 =back
1280    
1281 root 1.70 =head2 CORE EXTENSIONS
1282    
1283     Functions and methods that extend core crossfire objects.
1284    
1285 root 1.143 =cut
1286    
1287     package cf::player;
1288    
1289 root 1.154 use Coro::AIO;
1290    
1291 root 1.95 =head3 cf::player
1292    
1293 root 1.70 =over 4
1294 root 1.22
1295 root 1.361 =item cf::player::num_playing
1296    
1297     Returns the official number of playing players, as per the Crossfire metaserver rules.
1298    
1299     =cut
1300    
1301     sub num_playing {
1302     scalar grep
1303     $_->ob->map
1304     && !$_->hidden
1305     && !$_->ob->flag (cf::FLAG_WIZ),
1306     cf::player::list
1307     }
1308    
1309 root 1.143 =item cf::player::find $login
1310 root 1.23
1311 root 1.143 Returns the given player object, loading it if necessary (might block).
1312 root 1.23
1313     =cut
1314    
1315 root 1.145 sub playerdir($) {
1316 root 1.253 "$PLAYERDIR/"
1317 root 1.145 . (ref $_[0] ? $_[0]->ob->name : $_[0])
1318     }
1319    
1320 root 1.143 sub path($) {
1321 root 1.145 my $login = ref $_[0] ? $_[0]->ob->name : $_[0];
1322    
1323 root 1.234 (playerdir $login) . "/playerdata"
1324 root 1.143 }
1325    
1326     sub find_active($) {
1327     $cf::PLAYER{$_[0]}
1328     and $cf::PLAYER{$_[0]}->active
1329     and $cf::PLAYER{$_[0]}
1330     }
1331    
1332     sub exists($) {
1333     my ($login) = @_;
1334    
1335     $cf::PLAYER{$login}
1336 root 1.180 or cf::sync_job { !aio_stat path $login }
1337 root 1.143 }
1338    
1339     sub find($) {
1340     return $cf::PLAYER{$_[0]} || do {
1341     my $login = $_[0];
1342    
1343     my $guard = cf::lock_acquire "user_find:$login";
1344    
1345 root 1.151 $cf::PLAYER{$_[0]} || do {
1346 root 1.234 # rename old playerfiles to new ones
1347     #TODO: remove when no longer required
1348     aio_link +(playerdir $login) . "/$login.pl.pst", (playerdir $login) . "/playerdata.pst";
1349     aio_link +(playerdir $login) . "/$login.pl" , (playerdir $login) . "/playerdata";
1350     aio_unlink +(playerdir $login) . "/$login.pl.pst";
1351     aio_unlink +(playerdir $login) . "/$login.pl";
1352    
1353 root 1.356 my $f = new_from_file cf::object::thawer path $login
1354 root 1.151 or return;
1355 root 1.356
1356     my $pl = cf::player::load_pl $f
1357     or return;
1358     local $cf::PLAYER_LOADING{$login} = $pl;
1359     $f->resolve_delayed_derefs;
1360 root 1.151 $cf::PLAYER{$login} = $pl
1361     }
1362     }
1363 root 1.143 }
1364    
1365     sub save($) {
1366     my ($pl) = @_;
1367    
1368     return if $pl->{deny_save};
1369    
1370     my $path = path $pl;
1371     my $guard = cf::lock_acquire "user_save:$path";
1372    
1373     return if $pl->{deny_save};
1374 root 1.146
1375 root 1.154 aio_mkdir playerdir $pl, 0770;
1376 root 1.143 $pl->{last_save} = $cf::RUNTIME;
1377    
1378     $pl->save_pl ($path);
1379 root 1.346 cf::cede_to_tick;
1380 root 1.143 }
1381    
1382     sub new($) {
1383     my ($login) = @_;
1384    
1385     my $self = create;
1386    
1387     $self->ob->name ($login);
1388     $self->{deny_save} = 1;
1389    
1390     $cf::PLAYER{$login} = $self;
1391    
1392     $self
1393 root 1.23 }
1394    
1395 root 1.329 =item $player->send_msg ($channel, $msg, $color, [extra...])
1396    
1397     =cut
1398    
1399     sub send_msg {
1400     my $ns = shift->ns
1401     or return;
1402     $ns->send_msg (@_);
1403     }
1404    
1405 root 1.154 =item $pl->quit_character
1406    
1407     Nukes the player without looking back. If logged in, the connection will
1408     be destroyed. May block for a long time.
1409    
1410     =cut
1411    
1412 root 1.145 sub quit_character {
1413     my ($pl) = @_;
1414    
1415 root 1.220 my $name = $pl->ob->name;
1416    
1417 root 1.145 $pl->{deny_save} = 1;
1418     $pl->password ("*"); # this should lock out the player until we nuked the dir
1419    
1420     $pl->invoke (cf::EVENT_PLAYER_LOGOUT, 1) if $pl->active;
1421     $pl->deactivate;
1422     $pl->invoke (cf::EVENT_PLAYER_QUIT);
1423     $pl->ns->destroy if $pl->ns;
1424    
1425     my $path = playerdir $pl;
1426     my $temp = "$path~$cf::RUNTIME~deleting~";
1427 root 1.154 aio_rename $path, $temp;
1428 root 1.150 delete $cf::PLAYER{$pl->ob->name};
1429     $pl->destroy;
1430 root 1.220
1431     my $prefix = qr<^~\Q$name\E/>;
1432    
1433     # nuke player maps
1434     $cf::MAP{$_}->nuke for grep /$prefix/, keys %cf::MAP;
1435    
1436 root 1.150 IO::AIO::aio_rmtree $temp;
1437 root 1.145 }
1438    
1439 pippijn 1.221 =item $pl->kick
1440    
1441     Kicks a player out of the game. This destroys the connection.
1442    
1443     =cut
1444    
1445     sub kick {
1446     my ($pl, $kicker) = @_;
1447    
1448     $pl->invoke (cf::EVENT_PLAYER_KICK, $kicker);
1449     $pl->killer ("kicked");
1450     $pl->ns->destroy;
1451     }
1452    
1453 root 1.154 =item cf::player::list_logins
1454    
1455     Returns am arrayref of all valid playernames in the system, can take a
1456     while and may block, so not sync_job-capable, ever.
1457    
1458     =cut
1459    
1460     sub list_logins {
1461 root 1.253 my $dirs = aio_readdir $PLAYERDIR
1462 root 1.154 or return [];
1463    
1464     my @logins;
1465    
1466     for my $login (@$dirs) {
1467 root 1.354 my $path = path $login;
1468    
1469     # a .pst is a dead give-away for a valid player
1470     unless (-e "$path.pst") {
1471     my $fh = aio_open $path, Fcntl::O_RDONLY, 0 or next;
1472     aio_read $fh, 0, 512, my $buf, 0 or next;
1473     $buf !~ /^password -------------$/m or next; # official not-valid tag
1474     }
1475 root 1.154
1476     utf8::decode $login;
1477     push @logins, $login;
1478     }
1479    
1480     \@logins
1481     }
1482    
1483     =item $player->maps
1484    
1485 root 1.166 Returns an arrayref of map paths that are private for this
1486 root 1.154 player. May block.
1487    
1488     =cut
1489    
1490     sub maps($) {
1491     my ($pl) = @_;
1492    
1493 root 1.201 $pl = ref $pl ? $pl->ob->name : $pl;
1494    
1495 root 1.154 my $files = aio_readdir playerdir $pl
1496     or return;
1497    
1498     my @paths;
1499    
1500     for (@$files) {
1501     utf8::decode $_;
1502     next if /\.(?:pl|pst)$/;
1503 root 1.158 next unless /^$PATH_SEP/o;
1504 root 1.154
1505 root 1.201 push @paths, cf::map::normalise "~$pl/$_";
1506 root 1.154 }
1507    
1508     \@paths
1509     }
1510    
1511 root 1.283 =item $protocol_xml = $player->expand_cfpod ($crossfire_pod)
1512    
1513     Expand crossfire pod fragments into protocol xml.
1514    
1515     =cut
1516    
1517     sub expand_cfpod {
1518     ((my $self), (local $_)) = @_;
1519    
1520     # escape & and <
1521     s/&/&amp;/g;
1522 root 1.352 s/(?<![BIUGHT])</&lt;/g;
1523 root 1.283
1524     # this is buggy, it needs to properly take care of nested <'s
1525    
1526     1 while
1527     # replace B<>, I<>, U<> etc.
1528     s/B<([^\>]*)>/<b>$1<\/b>/
1529     || s/I<([^\>]*)>/<i>$1<\/i>/
1530     || s/U<([^\>]*)>/<u>$1<\/u>/
1531 root 1.352 || s/T<([^\>]*)>/<big><b>$1<\/b><\/big>/
1532 root 1.283 # replace G<male|female> tags
1533     || s{G<([^>|]*)\|([^>]*)>}{
1534     $self->gender ? $2 : $1
1535     }ge
1536     # replace H<hint text>
1537 root 1.291 || s{H<([^\>]*)>}
1538     {
1539     ("<fg name=\"lightblue\">[$1 (Use hintmode to suppress hints)]</fg>",
1540     "<fg name=\"lightblue\">[Hint suppressed, see hintmode]</fg>",
1541     "")
1542     [$self->{hintmode}]
1543     }ge;
1544 root 1.283
1545     # create single paragraphs (very hackish)
1546     s/(?<=\S)\n(?=\w)/ /g;
1547    
1548 root 1.291 # compress some whitespace
1549 root 1.295 s/\s+\n/\n/g; # ws line-ends
1550     s/\n\n+/\n/g; # double lines
1551     s/^\n+//; # beginning lines
1552     s/\n+$//; # ending lines
1553 root 1.293
1554 root 1.283 $_
1555     }
1556    
1557 root 1.291 sub hintmode {
1558     $_[0]{hintmode} = $_[1] if @_ > 1;
1559     $_[0]{hintmode}
1560     }
1561    
1562 root 1.316 =item $player->ext_reply ($msgid, @msg)
1563 root 1.95
1564     Sends an ext reply to the player.
1565    
1566     =cut
1567    
1568 root 1.316 sub ext_reply($$@) {
1569     my ($self, $id, @msg) = @_;
1570 root 1.95
1571 root 1.336 $self->ns->ext_reply ($id, @msg)
1572 root 1.95 }
1573    
1574 root 1.316 =item $player->ext_msg ($type, @msg)
1575 root 1.231
1576     Sends an ext event to the client.
1577    
1578     =cut
1579    
1580 root 1.316 sub ext_msg($$@) {
1581     my ($self, $type, @msg) = @_;
1582 root 1.231
1583 root 1.316 $self->ns->ext_msg ($type, @msg);
1584 root 1.231 }
1585    
1586 root 1.238 =head3 cf::region
1587    
1588     =over 4
1589    
1590     =cut
1591    
1592     package cf::region;
1593    
1594     =item cf::region::find_by_path $path
1595    
1596 root 1.281 Tries to decuce the likely region for a map knowing only its path.
1597 root 1.238
1598     =cut
1599    
1600     sub find_by_path($) {
1601     my ($path) = @_;
1602    
1603     my ($match, $specificity);
1604    
1605     for my $region (list) {
1606 root 1.304 if ($region->{match} && $path =~ $region->{match}) {
1607 root 1.238 ($match, $specificity) = ($region, $region->specificity)
1608     if $region->specificity > $specificity;
1609     }
1610     }
1611    
1612     $match
1613     }
1614 root 1.143
1615 root 1.95 =back
1616    
1617 root 1.110 =head3 cf::map
1618    
1619     =over 4
1620    
1621     =cut
1622    
1623     package cf::map;
1624    
1625     use Fcntl;
1626     use Coro::AIO;
1627    
1628 root 1.166 use overload
1629 root 1.173 '""' => \&as_string,
1630     fallback => 1;
1631 root 1.166
1632 root 1.133 our $MAX_RESET = 3600;
1633     our $DEFAULT_RESET = 3000;
1634 root 1.110
1635     sub generate_random_map {
1636 root 1.166 my ($self, $rmp) = @_;
1637 root 1.110 # mit "rum" bekleckern, nicht
1638 root 1.166 $self->_create_random_map (
1639 root 1.110 $rmp->{wallstyle}, $rmp->{wall_name}, $rmp->{floorstyle}, $rmp->{monsterstyle},
1640     $rmp->{treasurestyle}, $rmp->{layoutstyle}, $rmp->{doorstyle}, $rmp->{decorstyle},
1641     $rmp->{origin_map}, $rmp->{final_map}, $rmp->{exitstyle}, $rmp->{this_map},
1642     $rmp->{exit_on_final_map},
1643     $rmp->{xsize}, $rmp->{ysize},
1644     $rmp->{expand2x}, $rmp->{layoutoptions1}, $rmp->{layoutoptions2}, $rmp->{layoutoptions3},
1645     $rmp->{symmetry}, $rmp->{difficulty}, $rmp->{difficulty_given}, $rmp->{difficulty_increase},
1646     $rmp->{dungeon_level}, $rmp->{dungeon_depth}, $rmp->{decoroptions}, $rmp->{orientation},
1647     $rmp->{origin_y}, $rmp->{origin_x}, $rmp->{random_seed}, $rmp->{total_map_hp},
1648     $rmp->{map_layout_style}, $rmp->{treasureoptions}, $rmp->{symmetry_used},
1649 root 1.162 (cf::region::find $rmp->{region}), $rmp->{custom}
1650     )
1651 root 1.110 }
1652    
1653 root 1.187 =item cf::map->register ($regex, $prio)
1654    
1655     Register a handler for the map path matching the given regex at the
1656     givne priority (higher is better, built-in handlers have priority 0, the
1657     default).
1658    
1659     =cut
1660    
1661 root 1.166 sub register {
1662 root 1.187 my (undef, $regex, $prio) = @_;
1663 root 1.166 my $pkg = caller;
1664    
1665     no strict;
1666     push @{"$pkg\::ISA"}, __PACKAGE__;
1667    
1668 root 1.187 $EXT_MAP{$pkg} = [$prio, qr<$regex>];
1669 root 1.166 }
1670    
1671     # also paths starting with '/'
1672 root 1.187 $EXT_MAP{"cf::map"} = [0, qr{^(?=/)}];
1673 root 1.166
1674 root 1.170 sub thawer_merge {
1675 root 1.172 my ($self, $merge) = @_;
1676    
1677 root 1.170 # we have to keep some variables in memory intact
1678 root 1.172 local $self->{path};
1679     local $self->{load_path};
1680 root 1.170
1681 root 1.172 $self->SUPER::thawer_merge ($merge);
1682 root 1.170 }
1683    
1684 root 1.166 sub normalise {
1685     my ($path, $base) = @_;
1686    
1687 root 1.192 $path = "$path"; # make sure its a string
1688    
1689 root 1.199 $path =~ s/\.map$//;
1690    
1691 root 1.166 # map plan:
1692     #
1693     # /! non-realised random map exit (special hack!)
1694     # {... are special paths that are not being touched
1695     # ?xxx/... are special absolute paths
1696     # ?random/... random maps
1697     # /... normal maps
1698     # ~user/... per-player map of a specific user
1699    
1700     $path =~ s/$PATH_SEP/\//go;
1701    
1702     # treat it as relative path if it starts with
1703     # something that looks reasonable
1704     if ($path =~ m{^(?:\./|\.\./|\w)}) {
1705     $base or Carp::carp "normalise called with relative path and no base: '$path'";
1706    
1707     $base =~ s{[^/]+/?$}{};
1708     $path = "$base/$path";
1709     }
1710    
1711     for ($path) {
1712     redo if s{//}{/};
1713     redo if s{/\.?/}{/};
1714     redo if s{/[^/]+/\.\./}{/};
1715     }
1716    
1717     $path
1718     }
1719    
1720     sub new_from_path {
1721     my (undef, $path, $base) = @_;
1722    
1723     return $path if UNIVERSAL::isa $path, "cf::map"; # already a map object
1724    
1725     $path = normalise $path, $base;
1726    
1727 root 1.187 for my $pkg (sort { $EXT_MAP{$b}[0] <=> $EXT_MAP{$a}[0] } keys %EXT_MAP) {
1728     if ($path =~ $EXT_MAP{$pkg}[1]) {
1729 root 1.166 my $self = bless cf::map::new, $pkg;
1730     $self->{path} = $path; $self->path ($path);
1731     $self->init; # pass $1 etc.
1732     return $self;
1733     }
1734     }
1735    
1736 root 1.308 Carp::cluck "unable to resolve path '$path' (base '$base').";
1737 root 1.166 ()
1738     }
1739    
1740     sub init {
1741     my ($self) = @_;
1742    
1743     $self
1744     }
1745    
1746     sub as_string {
1747     my ($self) = @_;
1748    
1749     "$self->{path}"
1750     }
1751    
1752     # the displayed name, this is a one way mapping
1753     sub visible_name {
1754     &as_string
1755     }
1756    
1757     # the original (read-only) location
1758     sub load_path {
1759     my ($self) = @_;
1760    
1761 root 1.254 "$MAPDIR/$self->{path}.map"
1762 root 1.166 }
1763    
1764     # the temporary/swap location
1765     sub save_path {
1766     my ($self) = @_;
1767    
1768 root 1.167 (my $path = $_[0]{path}) =~ s/\//$PATH_SEP/g;
1769 root 1.254 "$TMPDIR/$path.map"
1770 root 1.166 }
1771    
1772     # the unique path, undef == no special unique path
1773     sub uniq_path {
1774     my ($self) = @_;
1775    
1776 root 1.167 (my $path = $_[0]{path}) =~ s/\//$PATH_SEP/g;
1777 root 1.253 "$UNIQUEDIR/$path"
1778 root 1.166 }
1779    
1780 root 1.110 # and all this just because we cannot iterate over
1781     # all maps in C++...
1782     sub change_all_map_light {
1783     my ($change) = @_;
1784    
1785 root 1.122 $_->change_map_light ($change)
1786     for grep $_->outdoor, values %cf::MAP;
1787 root 1.110 }
1788    
1789 root 1.275 sub decay_objects {
1790     my ($self) = @_;
1791    
1792     return if $self->{deny_reset};
1793    
1794     $self->do_decay_objects;
1795     }
1796    
1797 root 1.166 sub unlink_save {
1798     my ($self) = @_;
1799    
1800     utf8::encode (my $save = $self->save_path);
1801 root 1.170 IO::AIO::aioreq_pri 4; Coro::AIO::aio_unlink $save;
1802     IO::AIO::aioreq_pri 4; Coro::AIO::aio_unlink "$save.pst";
1803 root 1.166 }
1804    
1805     sub load_header_from($) {
1806     my ($self, $path) = @_;
1807 root 1.110
1808     utf8::encode $path;
1809 root 1.356 my $f = new_from_file cf::object::thawer $path
1810     or return;
1811 root 1.110
1812 root 1.356 $self->_load_header ($f)
1813 root 1.110 or return;
1814    
1815 root 1.356 local $MAP_LOADING{$self->{path}} = $self;
1816     $f->resolve_delayed_derefs;
1817    
1818 root 1.166 $self->{load_path} = $path;
1819 root 1.135
1820 root 1.166 1
1821     }
1822 root 1.110
1823 root 1.188 sub load_header_orig {
1824 root 1.166 my ($self) = @_;
1825 root 1.110
1826 root 1.166 $self->load_header_from ($self->load_path)
1827 root 1.110 }
1828    
1829 root 1.188 sub load_header_temp {
1830 root 1.166 my ($self) = @_;
1831 root 1.110
1832 root 1.166 $self->load_header_from ($self->save_path)
1833     }
1834 root 1.110
1835 root 1.188 sub prepare_temp {
1836     my ($self) = @_;
1837    
1838     $self->last_access ((delete $self->{last_access})
1839     || $cf::RUNTIME); #d#
1840     # safety
1841     $self->{instantiate_time} = $cf::RUNTIME
1842     if $self->{instantiate_time} > $cf::RUNTIME;
1843     }
1844    
1845     sub prepare_orig {
1846     my ($self) = @_;
1847    
1848     $self->{load_original} = 1;
1849     $self->{instantiate_time} = $cf::RUNTIME;
1850     $self->last_access ($cf::RUNTIME);
1851     $self->instantiate;
1852     }
1853    
1854 root 1.166 sub load_header {
1855     my ($self) = @_;
1856 root 1.110
1857 root 1.188 if ($self->load_header_temp) {
1858     $self->prepare_temp;
1859 root 1.166 } else {
1860 root 1.188 $self->load_header_orig
1861 root 1.166 or return;
1862 root 1.188 $self->prepare_orig;
1863 root 1.166 }
1864 root 1.120
1865 root 1.275 $self->{deny_reset} = 1
1866     if $self->no_reset;
1867    
1868 root 1.238 $self->default_region (cf::region::find_by_path $self->{path})
1869     unless $self->default_region;
1870    
1871 root 1.166 1
1872     }
1873 root 1.110
1874 root 1.166 sub find;
1875     sub find {
1876     my ($path, $origin) = @_;
1877 root 1.134
1878 root 1.166 $path = normalise $path, $origin && $origin->path;
1879 root 1.110
1880 root 1.358 cf::lock_wait "map_data:$path";#d#remove
1881 root 1.166 cf::lock_wait "map_find:$path";
1882 root 1.110
1883 root 1.166 $cf::MAP{$path} || do {
1884 root 1.358 my $guard1 = cf::lock_acquire "map_find:$path";
1885     my $guard2 = cf::lock_acquire "map_data:$path"; # just for the fun of it
1886    
1887 root 1.166 my $map = new_from_path cf::map $path
1888     or return;
1889 root 1.110
1890 root 1.116 $map->{last_save} = $cf::RUNTIME;
1891 root 1.110
1892 root 1.166 $map->load_header
1893     or return;
1894 root 1.134
1895 root 1.275 if ($map->should_reset) {#d#TODO# disabled, crashy (locking issue?)
1896 root 1.185 # doing this can freeze the server in a sync job, obviously
1897     #$cf::WAIT_FOR_TICK->wait;
1898 root 1.358 undef $guard1;
1899     undef $guard2;
1900 root 1.112 $map->reset;
1901 root 1.192 return find $path;
1902 root 1.112 }
1903 root 1.110
1904 root 1.166 $cf::MAP{$path} = $map
1905 root 1.110 }
1906     }
1907    
1908 root 1.188 sub pre_load { }
1909     sub post_load { }
1910    
1911 root 1.110 sub load {
1912     my ($self) = @_;
1913    
1914 root 1.196 local $self->{deny_reset} = 1; # loading can take a long time
1915    
1916 root 1.120 my $path = $self->{path};
1917    
1918 root 1.256 {
1919 root 1.357 my $guard = cf::lock_acquire "map_data:$path";
1920 root 1.256
1921 root 1.357 return unless $self->valid;
1922 root 1.360 return unless $self->in_memory == cf::MAP_SWAPPED;
1923 root 1.110
1924 root 1.256 $self->in_memory (cf::MAP_LOADING);
1925 root 1.110
1926 root 1.256 $self->alloc;
1927 root 1.188
1928 root 1.256 $self->pre_load;
1929 root 1.346 cf::cede_to_tick;
1930 root 1.188
1931 root 1.356 my $f = new_from_file cf::object::thawer $self->{load_path};
1932     $f->skip_block;
1933     $self->_load_objects ($f)
1934 root 1.256 or return;
1935 root 1.110
1936 root 1.256 $self->set_object_flag (cf::FLAG_OBJ_ORIGINAL, 1)
1937     if delete $self->{load_original};
1938 root 1.111
1939 root 1.256 if (my $uniq = $self->uniq_path) {
1940     utf8::encode $uniq;
1941 root 1.356 unless (aio_stat $uniq) {
1942     if (my $f = new_from_file cf::object::thawer $uniq) {
1943     $self->clear_unique_items;
1944     $self->_load_objects ($f);
1945     $f->resolve_delayed_derefs;
1946     }
1947 root 1.256 }
1948 root 1.110 }
1949    
1950 root 1.356 $f->resolve_delayed_derefs;
1951    
1952 root 1.346 cf::cede_to_tick;
1953 root 1.256 # now do the right thing for maps
1954     $self->link_multipart_objects;
1955 root 1.110 $self->difficulty ($self->estimate_difficulty)
1956     unless $self->difficulty;
1957 root 1.346 cf::cede_to_tick;
1958 root 1.256
1959     unless ($self->{deny_activate}) {
1960     $self->decay_objects;
1961     $self->fix_auto_apply;
1962     $self->update_buttons;
1963 root 1.346 cf::cede_to_tick;
1964 root 1.256 $self->set_darkness_map;
1965 root 1.346 cf::cede_to_tick;
1966 root 1.256 $self->activate;
1967     }
1968    
1969 root 1.325 $self->{last_save} = $cf::RUNTIME;
1970     $self->last_access ($cf::RUNTIME);
1971 root 1.324
1972 root 1.256 $self->in_memory (cf::MAP_IN_MEMORY);
1973 root 1.110 }
1974    
1975 root 1.188 $self->post_load;
1976 root 1.166 }
1977    
1978     sub customise_for {
1979     my ($self, $ob) = @_;
1980    
1981     return find "~" . $ob->name . "/" . $self->{path}
1982     if $self->per_player;
1983 root 1.134
1984 root 1.275 # return find "?party/" . $ob->name . "/" . $self->{path}
1985     # if $self->per_party;
1986    
1987 root 1.166 $self
1988 root 1.110 }
1989    
1990 root 1.157 # find and load all maps in the 3x3 area around a map
1991 root 1.333 sub load_neighbours {
1992 root 1.157 my ($map) = @_;
1993    
1994 root 1.333 my @neigh; # diagonal neighbours
1995 root 1.157
1996     for (0 .. 3) {
1997     my $neigh = $map->tile_path ($_)
1998     or next;
1999     $neigh = find $neigh, $map
2000     or next;
2001     $neigh->load;
2002    
2003 root 1.333 push @neigh,
2004     [$neigh->tile_path (($_ + 3) % 4), $neigh],
2005     [$neigh->tile_path (($_ + 1) % 4), $neigh];
2006 root 1.157 }
2007    
2008 root 1.333 for (grep defined $_->[0], @neigh) {
2009     my ($path, $origin) = @$_;
2010     my $neigh = find $path, $origin
2011 root 1.157 or next;
2012     $neigh->load;
2013     }
2014     }
2015    
2016 root 1.133 sub find_sync {
2017 root 1.110 my ($path, $origin) = @_;
2018    
2019 root 1.157 cf::sync_job { find $path, $origin }
2020 root 1.133 }
2021    
2022     sub do_load_sync {
2023     my ($map) = @_;
2024 root 1.110
2025 root 1.339 cf::LOG cf::llevDebug | cf::logBacktrace, "do_load_sync"
2026 root 1.342 if $Coro::current == $Coro::main;
2027 root 1.339
2028 root 1.133 cf::sync_job { $map->load };
2029 root 1.110 }
2030    
2031 root 1.157 our %MAP_PREFETCH;
2032 root 1.183 our $MAP_PREFETCHER = undef;
2033 root 1.157
2034     sub find_async {
2035 root 1.339 my ($path, $origin, $load) = @_;
2036 root 1.157
2037 root 1.166 $path = normalise $path, $origin && $origin->{path};
2038 root 1.157
2039 root 1.166 if (my $map = $cf::MAP{$path}) {
2040 root 1.340 return $map if !$load || $map->in_memory == cf::MAP_IN_MEMORY;
2041 root 1.157 }
2042    
2043 root 1.339 $MAP_PREFETCH{$path} |= $load;
2044    
2045 root 1.183 $MAP_PREFETCHER ||= cf::async {
2046 root 1.374 $Coro::current->{desc} = "map prefetcher";
2047    
2048 root 1.183 while (%MAP_PREFETCH) {
2049 root 1.339 while (my ($k, $v) = each %MAP_PREFETCH) {
2050     if (my $map = find $k) {
2051     $map->load if $v;
2052 root 1.308 }
2053 root 1.183
2054 root 1.339 delete $MAP_PREFETCH{$k};
2055 root 1.183 }
2056     }
2057     undef $MAP_PREFETCHER;
2058     };
2059 root 1.189 $MAP_PREFETCHER->prio (6);
2060 root 1.157
2061     ()
2062     }
2063    
2064 root 1.110 sub save {
2065     my ($self) = @_;
2066    
2067 root 1.345 my $lock = cf::lock_acquire "map_data:$self->{path}";
2068 root 1.137
2069 root 1.110 $self->{last_save} = $cf::RUNTIME;
2070    
2071     return unless $self->dirty;
2072    
2073 root 1.166 my $save = $self->save_path; utf8::encode $save;
2074     my $uniq = $self->uniq_path; utf8::encode $uniq;
2075 root 1.117
2076 root 1.110 $self->{load_path} = $save;
2077    
2078     return if $self->{deny_save};
2079    
2080 root 1.132 local $self->{last_access} = $self->last_access;#d#
2081    
2082 root 1.143 cf::async {
2083 root 1.374 $Coro::current->{desc} = "map player save";
2084 root 1.143 $_->contr->save for $self->players;
2085     };
2086    
2087 root 1.110 if ($uniq) {
2088 root 1.166 $self->_save_objects ($save, cf::IO_HEADER | cf::IO_OBJECTS);
2089     $self->_save_objects ($uniq, cf::IO_UNIQUES);
2090 root 1.110 } else {
2091 root 1.166 $self->_save_objects ($save, cf::IO_HEADER | cf::IO_OBJECTS | cf::IO_UNIQUES);
2092 root 1.110 }
2093     }
2094    
2095     sub swap_out {
2096     my ($self) = @_;
2097    
2098 root 1.130 # save first because save cedes
2099     $self->save;
2100    
2101 root 1.345 my $lock = cf::lock_acquire "map_data:$self->{path}";
2102 root 1.137
2103 root 1.110 return if $self->players;
2104     return if $self->in_memory != cf::MAP_IN_MEMORY;
2105     return if $self->{deny_save};
2106    
2107 root 1.359 $self->in_memory (cf::MAP_SWAPPED);
2108    
2109 root 1.358 $self->deactivate;
2110 root 1.359 $_->clear_links_to ($self) for values %cf::MAP;
2111 root 1.110 $self->clear;
2112     }
2113    
2114 root 1.112 sub reset_at {
2115     my ($self) = @_;
2116 root 1.110
2117     # TODO: safety, remove and allow resettable per-player maps
2118 root 1.114 return 1e99 if $self->{deny_reset};
2119 root 1.110
2120 root 1.112 my $time = $self->fixed_resettime ? $self->{instantiate_time} : $self->last_access;
2121 root 1.114 my $to = List::Util::min $MAX_RESET, $self->reset_timeout || $DEFAULT_RESET;
2122 root 1.110
2123 root 1.112 $time + $to
2124     }
2125    
2126     sub should_reset {
2127     my ($self) = @_;
2128    
2129     $self->reset_at <= $cf::RUNTIME
2130 root 1.111 }
2131    
2132 root 1.110 sub reset {
2133     my ($self) = @_;
2134    
2135 root 1.167 my $lock = cf::lock_acquire "map_data:$self->{path}";
2136 root 1.137
2137 root 1.110 return if $self->players;
2138    
2139 root 1.274 warn "resetting map ", $self->path;
2140 root 1.110
2141 root 1.210 $self->in_memory (cf::MAP_SWAPPED);
2142    
2143     # need to save uniques path
2144     unless ($self->{deny_save}) {
2145     my $uniq = $self->uniq_path; utf8::encode $uniq;
2146    
2147     $self->_save_objects ($uniq, cf::IO_UNIQUES)
2148     if $uniq;
2149     }
2150    
2151 root 1.111 delete $cf::MAP{$self->path};
2152 root 1.110
2153 root 1.358 $self->deactivate;
2154 root 1.359 $_->clear_links_to ($self) for values %cf::MAP;
2155 root 1.167 $self->clear;
2156    
2157 root 1.166 $self->unlink_save;
2158 root 1.111 $self->destroy;
2159 root 1.110 }
2160    
2161 root 1.114 my $nuke_counter = "aaaa";
2162    
2163     sub nuke {
2164     my ($self) = @_;
2165    
2166 root 1.349 {
2167     my $lock = cf::lock_acquire "map_data:$self->{path}";
2168    
2169     delete $cf::MAP{$self->path};
2170 root 1.174
2171 root 1.351 $self->unlink_save;
2172    
2173 root 1.349 bless $self, "cf::map";
2174     delete $self->{deny_reset};
2175     $self->{deny_save} = 1;
2176     $self->reset_timeout (1);
2177     $self->path ($self->{path} = "{nuke}/" . ($nuke_counter++));
2178 root 1.174
2179 root 1.349 $cf::MAP{$self->path} = $self;
2180     }
2181 root 1.174
2182 root 1.114 $self->reset; # polite request, might not happen
2183     }
2184    
2185 root 1.276 =item $maps = cf::map::tmp_maps
2186    
2187     Returns an arrayref with all map paths of currently instantiated and saved
2188 root 1.277 maps. May block.
2189 root 1.276
2190     =cut
2191    
2192     sub tmp_maps() {
2193     [
2194     map {
2195     utf8::decode $_;
2196 root 1.277 /\.map$/
2197 root 1.276 ? normalise $_
2198     : ()
2199     } @{ aio_readdir $TMPDIR or [] }
2200     ]
2201     }
2202    
2203 root 1.277 =item $maps = cf::map::random_maps
2204    
2205     Returns an arrayref with all map paths of currently instantiated and saved
2206     random maps. May block.
2207    
2208     =cut
2209    
2210     sub random_maps() {
2211     [
2212     map {
2213     utf8::decode $_;
2214     /\.map$/
2215     ? normalise "?random/$_"
2216     : ()
2217     } @{ aio_readdir $RANDOMDIR or [] }
2218     ]
2219     }
2220    
2221 root 1.158 =item cf::map::unique_maps
2222    
2223 root 1.166 Returns an arrayref of paths of all shared maps that have
2224 root 1.158 instantiated unique items. May block.
2225    
2226     =cut
2227    
2228     sub unique_maps() {
2229 root 1.276 [
2230     map {
2231     utf8::decode $_;
2232 root 1.277 /\.map$/
2233 root 1.276 ? normalise $_
2234     : ()
2235     } @{ aio_readdir $UNIQUEDIR or [] }
2236     ]
2237 root 1.158 }
2238    
2239 root 1.155 package cf;
2240    
2241     =back
2242    
2243     =head3 cf::object
2244    
2245     =cut
2246    
2247     package cf::object;
2248    
2249     =over 4
2250    
2251     =item $ob->inv_recursive
2252 root 1.110
2253 root 1.155 Returns the inventory of the object _and_ their inventories, recursively.
2254 root 1.110
2255 root 1.155 =cut
2256 root 1.144
2257 root 1.155 sub inv_recursive_;
2258     sub inv_recursive_ {
2259     map { $_, inv_recursive_ $_->inv } @_
2260     }
2261 root 1.110
2262 root 1.155 sub inv_recursive {
2263     inv_recursive_ inv $_[0]
2264 root 1.110 }
2265    
2266 root 1.356 =item $ref = $ob->ref
2267    
2268     creates and returns a persistent reference to an objetc that can be stored as a string.
2269    
2270     =item $ob = cf::object::deref ($refstring)
2271    
2272     returns the objetc referenced by refstring. may return undef when it cnanot find the object,
2273     even if the object actually exists. May block.
2274    
2275     =cut
2276    
2277     sub deref {
2278     my ($ref) = @_;
2279    
2280 root 1.377 if ($ref =~ m{^player\/(<1\.[0-9a-f]+>)/(.*)$}) {
2281 root 1.356 my ($uuid, $name) = ($1, $2);
2282     my $pl = $cf::PLAYER_LOADING{$name} || cf::player::find $name
2283     or return;
2284     $pl->ob->uuid eq $uuid
2285     or return;
2286    
2287     $pl->ob
2288     } else {
2289     warn "$ref: cannot resolve object reference\n";
2290     undef
2291     }
2292     }
2293    
2294 root 1.110 package cf;
2295    
2296     =back
2297    
2298 root 1.95 =head3 cf::object::player
2299    
2300     =over 4
2301    
2302 root 1.79 =item $player_object->reply ($npc, $msg[, $flags])
2303 root 1.28
2304     Sends a message to the player, as if the npc C<$npc> replied. C<$npc>
2305     can be C<undef>. Does the right thing when the player is currently in a
2306     dialogue with the given NPC character.
2307    
2308     =cut
2309    
2310 root 1.22 # rough implementation of a future "reply" method that works
2311     # with dialog boxes.
2312 root 1.95 #TODO: the first argument must go, split into a $npc->reply_to ( method
2313 root 1.23 sub cf::object::player::reply($$$;$) {
2314     my ($self, $npc, $msg, $flags) = @_;
2315    
2316     $flags = cf::NDI_BROWN | cf::NDI_UNIQUE unless @_ >= 4;
2317 root 1.22
2318 root 1.24 if ($self->{record_replies}) {
2319     push @{ $self->{record_replies} }, [$npc, $msg, $flags];
2320 elmex 1.282
2321 root 1.24 } else {
2322 elmex 1.282 my $pl = $self->contr;
2323    
2324     if ($pl->{npc_dialog} && $pl->{npc_dialog}->{id}) {
2325 root 1.316 my $dialog = $pl->{npc_dialog};
2326     $dialog->{pl}->ext_msg ($dialog->{id}, update => msg => $dialog->{pl}->expand_cfpod ($msg));
2327 elmex 1.282
2328     } else {
2329     $msg = $npc->name . " says: $msg" if $npc;
2330     $self->message ($msg, $flags);
2331     }
2332 root 1.24 }
2333 root 1.22 }
2334    
2335 root 1.329 =item $object->send_msg ($channel, $msg, $color, [extra...])
2336    
2337     =cut
2338    
2339     sub cf::object::send_msg {
2340     my $pl = shift->contr
2341     or return;
2342     $pl->send_msg (@_);
2343     }
2344    
2345 root 1.79 =item $player_object->may ("access")
2346    
2347     Returns wether the given player is authorized to access resource "access"
2348     (e.g. "command_wizcast").
2349    
2350     =cut
2351    
2352     sub cf::object::player::may {
2353     my ($self, $access) = @_;
2354    
2355     $self->flag (cf::FLAG_WIZ) ||
2356     (ref $cf::CFG{"may_$access"}
2357     ? scalar grep $self->name eq $_, @{$cf::CFG{"may_$access"}}
2358     : $cf::CFG{"may_$access"})
2359     }
2360 root 1.70
2361 root 1.115 =item $player_object->enter_link
2362    
2363     Freezes the player and moves him/her to a special map (C<{link}>).
2364    
2365 root 1.166 The player should be reasonably safe there for short amounts of time. You
2366 root 1.115 I<MUST> call C<leave_link> as soon as possible, though.
2367    
2368 root 1.166 Will never block.
2369    
2370 root 1.115 =item $player_object->leave_link ($map, $x, $y)
2371    
2372 root 1.166 Moves the player out of the special C<{link}> map onto the specified
2373     map. If the map is not valid (or omitted), the player will be moved back
2374     to the location he/she was before the call to C<enter_link>, or, if that
2375     fails, to the emergency map position.
2376 root 1.115
2377     Might block.
2378    
2379     =cut
2380    
2381 root 1.166 sub link_map {
2382     unless ($LINK_MAP) {
2383     $LINK_MAP = cf::map::find "{link}"
2384 root 1.196 or cf::cleanup "FATAL: unable to provide {link} map, exiting.";
2385 root 1.166 $LINK_MAP->load;
2386     }
2387    
2388     $LINK_MAP
2389     }
2390    
2391 root 1.110 sub cf::object::player::enter_link {
2392     my ($self) = @_;
2393    
2394 root 1.259 $self->deactivate_recursive;
2395 root 1.258
2396 root 1.166 return if UNIVERSAL::isa $self->map, "ext::map_link";
2397 root 1.110
2398 root 1.120 $self->{_link_pos} ||= [$self->map->{path}, $self->x, $self->y]
2399 root 1.270 if $self->map && $self->map->{path} ne "{link}";
2400 root 1.110
2401 root 1.194 $self->enter_map ($LINK_MAP || link_map, 10, 10);
2402 root 1.110 }
2403    
2404     sub cf::object::player::leave_link {
2405     my ($self, $map, $x, $y) = @_;
2406    
2407 root 1.270 return unless $self->contr->active;
2408    
2409 root 1.110 my $link_pos = delete $self->{_link_pos};
2410    
2411     unless ($map) {
2412     # restore original map position
2413     ($map, $x, $y) = @{ $link_pos || [] };
2414 root 1.133 $map = cf::map::find $map;
2415 root 1.110
2416     unless ($map) {
2417     ($map, $x, $y) = @$EMERGENCY_POSITION;
2418 root 1.133 $map = cf::map::find $map
2419 root 1.110 or die "FATAL: cannot load emergency map\n";
2420     }
2421     }
2422    
2423     ($x, $y) = (-1, -1)
2424     unless (defined $x) && (defined $y);
2425    
2426     # use -1 or undef as default coordinates, not 0, 0
2427     ($x, $y) = ($map->enter_x, $map->enter_y)
2428     if $x <=0 && $y <= 0;
2429    
2430     $map->load;
2431 root 1.333 $map->load_neighbours;
2432 root 1.110
2433 root 1.143 return unless $self->contr->active;
2434 root 1.110 $self->activate_recursive;
2435 root 1.215
2436     local $self->{_prev_pos} = $link_pos; # ugly hack for rent.ext
2437 root 1.110 $self->enter_map ($map, $x, $y);
2438     }
2439    
2440 root 1.306 =item $player_object->goto ($path, $x, $y[, $check->($map)[, $done->()]])
2441 root 1.268
2442     Moves the player to the given map-path and coordinates by first freezing
2443     her, loading and preparing them map, calling the provided $check callback
2444     that has to return the map if sucecssful, and then unfreezes the player on
2445 root 1.306 the new (success) or old (failed) map position. In either case, $done will
2446     be called at the end of this process.
2447 root 1.110
2448     =cut
2449    
2450 root 1.270 our $GOTOGEN;
2451    
2452 root 1.136 sub cf::object::player::goto {
2453 root 1.306 my ($self, $path, $x, $y, $check, $done) = @_;
2454 root 1.268
2455 root 1.270 # do generation counting so two concurrent goto's will be executed in-order
2456     my $gen = $self->{_goto_generation} = ++$GOTOGEN;
2457    
2458 root 1.110 $self->enter_link;
2459    
2460 root 1.140 (async {
2461 root 1.374 $Coro::current->{desc} = "player::goto $path $x $y";
2462    
2463 root 1.365 # *tag paths override both path and x|y
2464     if ($path =~ /^\*(.*)$/) {
2465     if (my @obs = grep $_->map, ext::map_tags::find $1) {
2466     my $ob = $obs[rand @obs];
2467 root 1.366
2468 root 1.367 # see if we actually can go there
2469 root 1.368 if (@obs = grep !$self->blocked ($_->map, $_->x, $_->y), $ob, $ob->tail) {
2470     $ob = $obs[rand @obs];
2471 root 1.369 } else {
2472     $self->message ("Wow, it's pretty crowded in there.", cf::NDI_UNIQUE | cf::NDI_RED);
2473 root 1.368 }
2474 root 1.369 # else put us there anyways for now #d#
2475 root 1.366
2476 root 1.365 ($path, $x, $y) = ($ob->map, $ob->x, $ob->y);
2477 root 1.369 } else {
2478     ($path, $x, $y) = (undef, undef, undef);
2479 root 1.365 }
2480     }
2481    
2482 root 1.197 my $map = eval {
2483 root 1.369 my $map = defined $path ? cf::map::find $path : undef;
2484 root 1.268
2485     if ($map) {
2486     $map = $map->customise_for ($self);
2487     $map = $check->($map) if $check && $map;
2488     } else {
2489 root 1.369 $self->message ("The exit to '$path' is closed.", cf::NDI_UNIQUE | cf::NDI_RED);
2490 root 1.268 }
2491    
2492 root 1.197 $map
2493 root 1.268 };
2494    
2495     if ($@) {
2496     $self->message ("Something went wrong within the server, please report this incident!", cf::NDI_UNIQUE | cf::NDI_RED);
2497     LOG llevError | logBacktrace, Carp::longmess $@;
2498     }
2499 root 1.115
2500 root 1.270 if ($gen == $self->{_goto_generation}) {
2501     delete $self->{_goto_generation};
2502     $self->leave_link ($map, $x, $y);
2503     }
2504 root 1.306
2505     $done->() if $done;
2506 root 1.110 })->prio (1);
2507     }
2508    
2509     =item $player_object->enter_exit ($exit_object)
2510    
2511     =cut
2512    
2513     sub parse_random_map_params {
2514     my ($spec) = @_;
2515    
2516     my $rmp = { # defaults
2517 root 1.181 xsize => (cf::rndm 15, 40),
2518     ysize => (cf::rndm 15, 40),
2519     symmetry => (cf::rndm 1, cf::SYMMETRY_XY),
2520 root 1.182 #layout => string,
2521 root 1.110 };
2522    
2523     for (split /\n/, $spec) {
2524     my ($k, $v) = split /\s+/, $_, 2;
2525    
2526     $rmp->{lc $k} = $v if (length $k) && (length $v);
2527     }
2528    
2529     $rmp
2530     }
2531    
2532     sub prepare_random_map {
2533     my ($exit) = @_;
2534    
2535 root 1.179 my $guard = cf::lock_acquire "exit_prepare:$exit";
2536    
2537 root 1.110 # all this does is basically replace the /! path by
2538     # a new random map path (?random/...) with a seed
2539     # that depends on the exit object
2540    
2541     my $rmp = parse_random_map_params $exit->msg;
2542    
2543     if ($exit->map) {
2544 root 1.198 $rmp->{region} = $exit->region->name;
2545 root 1.110 $rmp->{origin_map} = $exit->map->path;
2546     $rmp->{origin_x} = $exit->x;
2547     $rmp->{origin_y} = $exit->y;
2548     }
2549    
2550     $rmp->{random_seed} ||= $exit->random_seed;
2551    
2552     my $data = cf::to_json $rmp;
2553     my $md5 = Digest::MD5::md5_hex $data;
2554 root 1.253 my $meta = "$RANDOMDIR/$md5.meta";
2555 root 1.110
2556 root 1.177 if (my $fh = aio_open "$meta~", O_WRONLY | O_CREAT, 0666) {
2557 root 1.110 aio_write $fh, 0, (length $data), $data, 0;
2558 root 1.177 undef $fh;
2559     aio_rename "$meta~", $meta;
2560 root 1.110
2561     $exit->slaying ("?random/$md5");
2562     $exit->msg (undef);
2563     }
2564     }
2565    
2566     sub cf::object::player::enter_exit {
2567     my ($self, $exit) = @_;
2568    
2569     return unless $self->type == cf::PLAYER;
2570    
2571 root 1.195 if ($exit->slaying eq "/!") {
2572     #TODO: this should de-fi-ni-te-ly not be a sync-job
2573 root 1.233 # the problem is that $exit might not survive long enough
2574     # so it needs to be done right now, right here
2575 root 1.195 cf::sync_job { prepare_random_map $exit };
2576     }
2577    
2578     my $slaying = cf::map::normalise $exit->slaying, $exit->map && $exit->map->path;
2579     my $hp = $exit->stats->hp;
2580     my $sp = $exit->stats->sp;
2581    
2582 root 1.110 $self->enter_link;
2583    
2584 root 1.296 # if exit is damned, update players death & WoR home-position
2585     $self->contr->savebed ($slaying, $hp, $sp)
2586     if $exit->flag (FLAG_DAMNED);
2587    
2588 root 1.140 (async {
2589 root 1.374 $Coro::current->{desc} = "enter_exit $slaying $hp $sp";
2590    
2591 root 1.133 $self->deactivate_recursive; # just to be sure
2592 root 1.110 unless (eval {
2593 root 1.195 $self->goto ($slaying, $hp, $sp);
2594 root 1.110
2595     1;
2596     }) {
2597     $self->message ("Something went wrong deep within the crossfire server. "
2598 root 1.233 . "I'll try to bring you back to the map you were before. "
2599     . "Please report this to the dungeon master!",
2600     cf::NDI_UNIQUE | cf::NDI_RED);
2601 root 1.110
2602     warn "ERROR in enter_exit: $@";
2603     $self->leave_link;
2604     }
2605     })->prio (1);
2606     }
2607    
2608 root 1.95 =head3 cf::client
2609    
2610     =over 4
2611    
2612     =item $client->send_drawinfo ($text, $flags)
2613    
2614     Sends a drawinfo packet to the client. Circumvents output buffering so
2615     should not be used under normal circumstances.
2616    
2617 root 1.70 =cut
2618    
2619 root 1.95 sub cf::client::send_drawinfo {
2620     my ($self, $text, $flags) = @_;
2621    
2622     utf8::encode $text;
2623 root 1.225 $self->send_packet (sprintf "drawinfo %d %s", $flags || cf::NDI_BLACK, $text);
2624 root 1.95 }
2625    
2626 root 1.311 =item $client->send_msg ($channel, $msg, $color, [extra...])
2627 root 1.283
2628     Send a drawinfo or msg packet to the client, formatting the msg for the
2629     client if neccessary. C<$type> should be a string identifying the type of
2630     the message, with C<log> being the default. If C<$color> is negative, suppress
2631     the message unless the client supports the msg packet.
2632    
2633     =cut
2634    
2635 root 1.350 our %CHANNEL = (
2636     "c/identify" => {
2637 root 1.375 id => "infobox",
2638 root 1.350 title => "Identify",
2639     reply => undef,
2640     tooltip => "Items recently identified",
2641     },
2642 root 1.352 "c/examine" => {
2643 root 1.375 id => "infobox",
2644 root 1.352 title => "Examine",
2645     reply => undef,
2646     tooltip => "Signs and other items you examined",
2647     },
2648 root 1.375 "c/lookat" => {
2649     id => "infobox",
2650     title => "Look",
2651     reply => undef,
2652     tooltip => "What you saw there",
2653     },
2654 root 1.350 );
2655    
2656 root 1.283 sub cf::client::send_msg {
2657 root 1.311 my ($self, $channel, $msg, $color, @extra) = @_;
2658 root 1.283
2659     $msg = $self->pl->expand_cfpod ($msg);
2660    
2661 root 1.323 $color &= cf::NDI_CLIENT_MASK; # just in case...
2662 root 1.311
2663 root 1.350 # check predefined channels, for the benefit of C
2664 root 1.375 if ($CHANNEL{$channel}) {
2665     $channel = $CHANNEL{$channel};
2666    
2667     $self->ext_msg (channel_info => $channel)
2668     if $self->can_msg;
2669    
2670     $channel = $channel->{id};
2671 root 1.350
2672 root 1.375 } elsif (ref $channel) {
2673 root 1.311 # send meta info to client, if not yet sent
2674     unless (exists $self->{channel}{$channel->{id}}) {
2675     $self->{channel}{$channel->{id}} = $channel;
2676 root 1.353 $self->ext_msg (channel_info => $channel)
2677     if $self->can_msg;
2678 root 1.311 }
2679    
2680     $channel = $channel->{id};
2681     }
2682    
2683 root 1.313 return unless @extra || length $msg;
2684    
2685 root 1.283 if ($self->can_msg) {
2686 root 1.323 # default colour, mask it out
2687     $color &= ~(cf::NDI_COLOR_MASK | cf::NDI_DEF)
2688     if $color & cf::NDI_DEF;
2689    
2690     $self->send_packet ("msg " . $self->{json_coder}->encode (
2691     [$color & cf::NDI_CLIENT_MASK, $channel, $msg, @extra]));
2692 root 1.283 } else {
2693 root 1.323 if ($color >= 0) {
2694     # replace some tags by gcfclient-compatible ones
2695     for ($msg) {
2696     1 while
2697     s/<b>([^<]*)<\/b>/[b]${1}[\/b]/
2698     || s/<i>([^<]*)<\/i>/[i]${1}[\/i]/
2699     || s/<u>([^<]*)<\/u>/[ul]${1}[\/ul]/
2700     || s/<tt>([^<]*)<\/tt>/[fixed]${1}[\/fixed]/
2701     || s/<fg name=\"([^"]+)\">([^<]*)<\/fg>/[color=$1]${2}[\/color]/;
2702     }
2703    
2704     $color &= cf::NDI_COLOR_MASK;
2705 root 1.283
2706 root 1.327 utf8::encode $msg;
2707    
2708 root 1.284 if (0 && $msg =~ /\[/) {
2709 root 1.331 # COMMAND/INFO
2710     $self->send_packet ("drawextinfo $color 10 8 $msg")
2711 root 1.283 } else {
2712 root 1.286 $msg =~ s/\[\/?(?:b|i|u|fixed|color)[^\]]*\]//g;
2713 root 1.283 $self->send_packet ("drawinfo $color $msg")
2714     }
2715     }
2716     }
2717     }
2718    
2719 root 1.316 =item $client->ext_msg ($type, @msg)
2720 root 1.232
2721 root 1.287 Sends an ext event to the client.
2722 root 1.232
2723     =cut
2724    
2725 root 1.316 sub cf::client::ext_msg($$@) {
2726     my ($self, $type, @msg) = @_;
2727 root 1.232
2728 root 1.343 if ($self->extcmd == 2) {
2729 root 1.316 $self->send_packet ("ext " . $self->{json_coder}->encode ([$type, @msg]));
2730 root 1.343 } elsif ($self->extcmd == 1) { # TODO: remove
2731 root 1.316 push @msg, msgtype => "event_$type";
2732     $self->send_packet ("ext " . $self->{json_coder}->encode ({@msg}));
2733     }
2734 root 1.232 }
2735 root 1.95
2736 root 1.336 =item $client->ext_reply ($msgid, @msg)
2737    
2738     Sends an ext reply to the client.
2739    
2740     =cut
2741    
2742     sub cf::client::ext_reply($$@) {
2743     my ($self, $id, @msg) = @_;
2744    
2745     if ($self->extcmd == 2) {
2746     $self->send_packet ("ext " . $self->{json_coder}->encode (["reply-$id", @msg]));
2747 root 1.343 } elsif ($self->extcmd == 1) {
2748 root 1.336 #TODO: version 1, remove
2749     unshift @msg, msgtype => "reply", msgid => $id;
2750     $self->send_packet ("ext " . $self->{json_coder}->encode ({@msg}));
2751     }
2752     }
2753    
2754 root 1.95 =item $success = $client->query ($flags, "text", \&cb)
2755    
2756     Queues a query to the client, calling the given callback with
2757     the reply text on a reply. flags can be C<cf::CS_QUERY_YESNO>,
2758     C<cf::CS_QUERY_SINGLECHAR> or C<cf::CS_QUERY_HIDEINPUT> or C<0>.
2759    
2760 root 1.287 Queries can fail, so check the return code. Or don't, as queries will
2761     become reliable at some point in the future.
2762 root 1.95
2763     =cut
2764    
2765     sub cf::client::query {
2766     my ($self, $flags, $text, $cb) = @_;
2767    
2768     return unless $self->state == ST_PLAYING
2769     || $self->state == ST_SETUP
2770     || $self->state == ST_CUSTOM;
2771    
2772     $self->state (ST_CUSTOM);
2773    
2774     utf8::encode $text;
2775     push @{ $self->{query_queue} }, [(sprintf "query %d %s", $flags, $text), $cb];
2776    
2777     $self->send_packet ($self->{query_queue}[0][0])
2778     if @{ $self->{query_queue} } == 1;
2779 root 1.287
2780     1
2781 root 1.95 }
2782    
2783     cf::client->attach (
2784 root 1.290 on_connect => sub {
2785     my ($ns) = @_;
2786    
2787     $ns->{json_coder} = JSON::XS->new->utf8->max_size (1e6)->convert_blessed;
2788     },
2789 root 1.95 on_reply => sub {
2790     my ($ns, $msg) = @_;
2791    
2792     # this weird shuffling is so that direct followup queries
2793     # get handled first
2794 root 1.128 my $queue = delete $ns->{query_queue}
2795 root 1.129 or return; # be conservative, not sure how that can happen, but we saw a crash here
2796 root 1.95
2797     (shift @$queue)->[1]->($msg);
2798 root 1.202 return unless $ns->valid; # temporary(?) workaround for callback destroying socket
2799 root 1.95
2800     push @{ $ns->{query_queue} }, @$queue;
2801    
2802     if (@{ $ns->{query_queue} } == @$queue) {
2803     if (@$queue) {
2804     $ns->send_packet ($ns->{query_queue}[0][0]);
2805     } else {
2806 root 1.98 $ns->state (ST_PLAYING) if $ns->state == ST_CUSTOM;
2807 root 1.95 }
2808     }
2809     },
2810 root 1.287 on_exticmd => sub {
2811     my ($ns, $buf) = @_;
2812    
2813 root 1.290 my $msg = eval { $ns->{json_coder}->decode ($buf) };
2814 root 1.287
2815     if (ref $msg) {
2816 root 1.316 my ($type, $reply, @payload) =
2817     "ARRAY" eq ref $msg
2818     ? @$msg
2819     : ($msg->{msgtype}, $msg->{msgid}, %$msg); # TODO: version 1, remove
2820    
2821 root 1.338 my @reply;
2822    
2823 root 1.316 if (my $cb = $EXTICMD{$type}) {
2824 root 1.338 @reply = $cb->($ns, @payload);
2825     }
2826    
2827     $ns->ext_reply ($reply, @reply)
2828     if $reply;
2829 root 1.316
2830 root 1.287 } else {
2831     warn "client " . ($ns->pl ? $ns->pl->ob->name : $ns->host) . " sent unparseable exti message: <$buf>\n";
2832     }
2833    
2834     cf::override;
2835     },
2836 root 1.95 );
2837    
2838 root 1.140 =item $client->async (\&cb)
2839 root 1.96
2840     Create a new coroutine, running the specified callback. The coroutine will
2841     be automatically cancelled when the client gets destroyed (e.g. on logout,
2842     or loss of connection).
2843    
2844     =cut
2845    
2846 root 1.140 sub cf::client::async {
2847 root 1.96 my ($self, $cb) = @_;
2848    
2849 root 1.140 my $coro = &Coro::async ($cb);
2850 root 1.103
2851     $coro->on_destroy (sub {
2852 root 1.96 delete $self->{_coro}{$coro+0};
2853 root 1.103 });
2854 root 1.96
2855     $self->{_coro}{$coro+0} = $coro;
2856 root 1.103
2857     $coro
2858 root 1.96 }
2859    
2860     cf::client->attach (
2861     on_destroy => sub {
2862     my ($ns) = @_;
2863    
2864 root 1.97 $_->cancel for values %{ (delete $ns->{_coro}) || {} };
2865 root 1.96 },
2866     );
2867    
2868 root 1.95 =back
2869    
2870 root 1.70
2871     =head2 SAFE SCRIPTING
2872    
2873     Functions that provide a safe environment to compile and execute
2874     snippets of perl code without them endangering the safety of the server
2875     itself. Looping constructs, I/O operators and other built-in functionality
2876     is not available in the safe scripting environment, and the number of
2877 root 1.79 functions and methods that can be called is greatly reduced.
2878 root 1.70
2879     =cut
2880 root 1.23
2881 root 1.42 our $safe = new Safe "safe";
2882 root 1.23 our $safe_hole = new Safe::Hole;
2883    
2884     $SIG{FPE} = 'IGNORE';
2885    
2886 root 1.328 $safe->permit_only (Opcode::opset qw(
2887     :base_core :base_mem :base_orig :base_math
2888     grepstart grepwhile mapstart mapwhile
2889     sort time
2890     ));
2891 root 1.23
2892 root 1.25 # here we export the classes and methods available to script code
2893    
2894 root 1.70 =pod
2895    
2896 root 1.228 The following functions and methods are available within a safe environment:
2897 root 1.70
2898 root 1.297 cf::object
2899     contr pay_amount pay_player map x y force_find force_add
2900 elmex 1.341 insert remove name archname title slaying race decrease_ob_nr
2901 root 1.297
2902     cf::object::player
2903     player
2904    
2905     cf::player
2906     peaceful
2907    
2908     cf::map
2909     trigger
2910 root 1.70
2911     =cut
2912    
2913 root 1.25 for (
2914 root 1.297 ["cf::object" => qw(contr pay_amount pay_player map force_find force_add x y
2915 elmex 1.341 insert remove inv name archname title slaying race
2916     decrease_ob_nr)],
2917 root 1.25 ["cf::object::player" => qw(player)],
2918     ["cf::player" => qw(peaceful)],
2919 elmex 1.91 ["cf::map" => qw(trigger)],
2920 root 1.25 ) {
2921     no strict 'refs';
2922     my ($pkg, @funs) = @$_;
2923 root 1.41 *{"safe::$pkg\::$_"} = $safe_hole->wrap (\&{"$pkg\::$_"})
2924 root 1.25 for @funs;
2925     }
2926 root 1.23
2927 root 1.70 =over 4
2928    
2929     =item @retval = safe_eval $code, [var => value, ...]
2930    
2931     Compiled and executes the given perl code snippet. additional var/value
2932     pairs result in temporary local (my) scalar variables of the given name
2933     that are available in the code snippet. Example:
2934    
2935     my $five = safe_eval '$first + $second', first => 1, second => 4;
2936    
2937     =cut
2938    
2939 root 1.23 sub safe_eval($;@) {
2940     my ($code, %vars) = @_;
2941    
2942     my $qcode = $code;
2943     $qcode =~ s/"/‟/g; # not allowed in #line filenames
2944     $qcode =~ s/\n/\\n/g;
2945    
2946     local $_;
2947 root 1.41 local @safe::cf::_safe_eval_args = values %vars;
2948 root 1.23
2949 root 1.42 my $eval =
2950 root 1.23 "do {\n"
2951     . "my (" . (join ",", map "\$$_", keys %vars) . ") = \@cf::_safe_eval_args;\n"
2952     . "#line 0 \"{$qcode}\"\n"
2953     . $code
2954     . "\n}"
2955 root 1.25 ;
2956    
2957     sub_generation_inc;
2958 root 1.42 my @res = wantarray ? $safe->reval ($eval) : scalar $safe->reval ($eval);
2959 root 1.25 sub_generation_inc;
2960    
2961 root 1.42 if ($@) {
2962     warn "$@";
2963     warn "while executing safe code '$code'\n";
2964     warn "with arguments " . (join " ", %vars) . "\n";
2965     }
2966    
2967 root 1.25 wantarray ? @res : $res[0]
2968 root 1.23 }
2969    
2970 root 1.69 =item cf::register_script_function $function => $cb
2971    
2972     Register a function that can be called from within map/npc scripts. The
2973     function should be reasonably secure and should be put into a package name
2974     like the extension.
2975    
2976     Example: register a function that gets called whenever a map script calls
2977     C<rent::overview>, as used by the C<rent> extension.
2978    
2979     cf::register_script_function "rent::overview" => sub {
2980     ...
2981     };
2982    
2983     =cut
2984    
2985 root 1.23 sub register_script_function {
2986     my ($fun, $cb) = @_;
2987    
2988     no strict 'refs';
2989 root 1.41 *{"safe::$fun"} = $safe_hole->wrap ($cb);
2990 root 1.23 }
2991    
2992 root 1.70 =back
2993    
2994 root 1.71 =cut
2995    
2996 root 1.23 #############################################################################
2997 root 1.203 # the server's init and main functions
2998    
2999 root 1.246 sub load_facedata($) {
3000     my ($path) = @_;
3001 root 1.223
3002 root 1.348 # HACK to clear player env face cache, we need some signal framework
3003     # for this (global event?)
3004     %ext::player_env::MUSIC_FACE_CACHE = ();
3005    
3006 root 1.344 my $enc = JSON::XS->new->utf8->canonical->relaxed;
3007 root 1.334
3008 root 1.229 warn "loading facedata from $path\n";
3009 root 1.223
3010 root 1.236 my $facedata;
3011     0 < aio_load $path, $facedata
3012 root 1.223 or die "$path: $!";
3013    
3014 root 1.237 $facedata = Coro::Storable::thaw $facedata;
3015 root 1.223
3016 root 1.236 $facedata->{version} == 2
3017 root 1.226 or cf::cleanup "$path: version mismatch, cannot proceed.";
3018    
3019 root 1.334 # patch in the exptable
3020     $facedata->{resource}{"res/exp_table"} = {
3021     type => FT_RSRC,
3022 root 1.337 data => $enc->encode ([map cf::level_to_min_exp $_, 1 .. cf::settings->max_level]),
3023 root 1.334 };
3024     cf::cede_to_tick;
3025    
3026 root 1.236 {
3027     my $faces = $facedata->{faceinfo};
3028    
3029     while (my ($face, $info) = each %$faces) {
3030     my $idx = (cf::face::find $face) || cf::face::alloc $face;
3031 root 1.302 cf::face::set_visibility $idx, $info->{visibility};
3032     cf::face::set_magicmap $idx, $info->{magicmap};
3033 root 1.334 cf::face::set_data $idx, 0, $info->{data32}, Digest::MD5::md5 $info->{data32};
3034     cf::face::set_data $idx, 1, $info->{data64}, Digest::MD5::md5 $info->{data64};
3035 root 1.302
3036     cf::cede_to_tick;
3037 root 1.236 }
3038    
3039     while (my ($face, $info) = each %$faces) {
3040     next unless $info->{smooth};
3041     my $idx = cf::face::find $face
3042     or next;
3043     if (my $smooth = cf::face::find $info->{smooth}) {
3044 root 1.302 cf::face::set_smooth $idx, $smooth;
3045     cf::face::set_smoothlevel $idx, $info->{smoothlevel};
3046 root 1.236 } else {
3047     warn "smooth face '$info->{smooth}' not found for face '$face'";
3048     }
3049 root 1.302
3050     cf::cede_to_tick;
3051 root 1.236 }
3052 root 1.223 }
3053    
3054 root 1.236 {
3055     my $anims = $facedata->{animinfo};
3056    
3057     while (my ($anim, $info) = each %$anims) {
3058     cf::anim::set $anim, $info->{frames}, $info->{facings};
3059 root 1.302 cf::cede_to_tick;
3060 root 1.225 }
3061 root 1.236
3062     cf::anim::invalidate_all; # d'oh
3063 root 1.225 }
3064    
3065 root 1.302 {
3066     # TODO: for gcfclient pleasure, we should give resources
3067     # that gcfclient doesn't grok a >10000 face index.
3068     my $res = $facedata->{resource};
3069    
3070 root 1.321 my $soundconf = delete $res->{"res/sound.conf"};
3071 root 1.320
3072 root 1.302 while (my ($name, $info) = each %$res) {
3073     my $idx = (cf::face::find $name) || cf::face::alloc $name;
3074 root 1.334 my $data;
3075 root 1.307
3076 root 1.318 if ($info->{type} & 1) {
3077     # prepend meta info
3078    
3079 root 1.334 my $meta = $enc->encode ({
3080     name => $name,
3081     %{ $info->{meta} || {} },
3082     });
3083 root 1.307
3084 root 1.334 $data = pack "(w/a*)*", $meta, $info->{data};
3085 root 1.337 } else {
3086     $data = $info->{data};
3087 root 1.307 }
3088 root 1.302
3089 root 1.334 cf::face::set_data $idx, 0, $data, Digest::MD5::md5 $data;
3090 root 1.318 cf::face::set_type $idx, $info->{type};
3091    
3092 root 1.302 cf::cede_to_tick;
3093     }
3094 root 1.321
3095     if ($soundconf) {
3096     $soundconf = $enc->decode (delete $soundconf->{data});
3097    
3098     for (0 .. SOUND_CAST_SPELL_0 - 1) {
3099     my $sound = $soundconf->{compat}[$_]
3100     or next;
3101    
3102     my $face = cf::face::find "sound/$sound->[1]";
3103     cf::sound::set $sound->[0] => $face;
3104     cf::sound::old_sound_index $_, $face; # gcfclient-compat
3105     }
3106    
3107 root 1.326 while (my ($k, $v) = each %{$soundconf->{event}}) {
3108     my $face = cf::face::find "sound/$v";
3109     cf::sound::set $k => $face;
3110     }
3111 root 1.321 }
3112 root 1.302 }
3113    
3114 root 1.223 1
3115     }
3116    
3117 root 1.318 register_exticmd fx_want => sub {
3118     my ($ns, $want) = @_;
3119    
3120     while (my ($k, $v) = each %$want) {
3121     $ns->fx_want ($k, $v);
3122     }
3123     };
3124    
3125 root 1.253 sub reload_regions {
3126 root 1.348 # HACK to clear player env face cache, we need some signal framework
3127     # for this (global event?)
3128     %ext::player_env::MUSIC_FACE_CACHE = ();
3129    
3130 root 1.253 load_resource_file "$MAPDIR/regions"
3131     or die "unable to load regions file\n";
3132 root 1.304
3133     for (cf::region::list) {
3134     $_->{match} = qr/$_->{match}/
3135     if exists $_->{match};
3136     }
3137 root 1.253 }
3138    
3139 root 1.246 sub reload_facedata {
3140 root 1.253 load_facedata "$DATADIR/facedata"
3141 root 1.246 or die "unable to load facedata\n";
3142     }
3143    
3144     sub reload_archetypes {
3145 root 1.253 load_resource_file "$DATADIR/archetypes"
3146 root 1.246 or die "unable to load archetypes\n";
3147 root 1.289 #d# NEED to laod twice to resolve forward references
3148     # this really needs to be done in an extra post-pass
3149     # (which needs to be synchronous, so solve it differently)
3150     load_resource_file "$DATADIR/archetypes"
3151     or die "unable to load archetypes\n";
3152 root 1.241 }
3153    
3154 root 1.246 sub reload_treasures {
3155 root 1.253 load_resource_file "$DATADIR/treasures"
3156 root 1.246 or die "unable to load treasurelists\n";
3157 root 1.241 }
3158    
3159 root 1.223 sub reload_resources {
3160 root 1.245 warn "reloading resource files...\n";
3161    
3162 root 1.246 reload_regions;
3163     reload_facedata;
3164 root 1.274 #reload_archetypes;#d#
3165 root 1.246 reload_archetypes;
3166     reload_treasures;
3167 root 1.245
3168     warn "finished reloading resource files\n";
3169 root 1.223 }
3170    
3171     sub init {
3172     reload_resources;
3173 root 1.203 }
3174 root 1.34
3175 root 1.345 sub reload_config {
3176 root 1.253 open my $fh, "<:utf8", "$CONFDIR/config"
3177 root 1.72 or return;
3178    
3179     local $/;
3180     *CFG = YAML::Syck::Load <$fh>;
3181 root 1.131
3182     $EMERGENCY_POSITION = $CFG{emergency_position} || ["/world/world_105_115", 5, 37];
3183    
3184 root 1.139 $cf::map::MAX_RESET = $CFG{map_max_reset} if exists $CFG{map_max_reset};
3185     $cf::map::DEFAULT_RESET = $CFG{map_default_reset} if exists $CFG{map_default_reset};
3186    
3187 root 1.131 if (exists $CFG{mlockall}) {
3188     eval {
3189 root 1.147 $CFG{mlockall} ? eval "mlockall()" : eval "munlockall()"
3190 root 1.131 and die "WARNING: m(un)lockall failed: $!\n";
3191     };
3192     warn $@ if $@;
3193     }
3194 root 1.72 }
3195    
3196 root 1.39 sub main {
3197 root 1.108 # we must not ever block the main coroutine
3198     local $Coro::idle = sub {
3199 root 1.115 Carp::cluck "FATAL: Coro::idle was called, major BUG, use cf::sync_job!\n";#d#
3200 root 1.175 (async {
3201 root 1.374 $Coro::current->{desc} = "IDLE BUG HANDLER";
3202 root 1.175 Event::one_event;
3203     })->prio (Coro::PRIO_MAX);
3204 root 1.108 };
3205    
3206 root 1.345 reload_config;
3207 root 1.210 db_init;
3208 root 1.61 load_extensions;
3209 root 1.183
3210     $TICK_WATCHER->start;
3211 root 1.34 Event::loop;
3212     }
3213    
3214     #############################################################################
3215 root 1.155 # initialisation and cleanup
3216    
3217     # install some emergency cleanup handlers
3218     BEGIN {
3219     for my $signal (qw(INT HUP TERM)) {
3220     Event->signal (
3221 root 1.189 reentrant => 0,
3222     data => WF_AUTOCANCEL,
3223     signal => $signal,
3224 root 1.191 prio => 0,
3225 root 1.189 cb => sub {
3226 root 1.155 cf::cleanup "SIG$signal";
3227     },
3228     );
3229     }
3230     }
3231    
3232 root 1.281 sub write_runtime {
3233     my $runtime = "$LOCALDIR/runtime";
3234    
3235     # first touch the runtime file to show we are still running:
3236     # the fsync below can take a very very long time.
3237    
3238     IO::AIO::aio_utime $runtime, undef, undef;
3239    
3240     my $guard = cf::lock_acquire "write_runtime";
3241    
3242     my $fh = aio_open "$runtime~", O_WRONLY | O_CREAT, 0644
3243     or return;
3244    
3245     my $value = $cf::RUNTIME + 90 + 10;
3246     # 10 is the runtime save interval, for a monotonic clock
3247     # 60 allows for the watchdog to kill the server.
3248    
3249     (aio_write $fh, 0, (length $value), $value, 0) <= 0
3250     and return;
3251    
3252     # always fsync - this file is important
3253     aio_fsync $fh
3254     and return;
3255    
3256     # touch it again to show we are up-to-date
3257     aio_utime $fh, undef, undef;
3258    
3259     close $fh
3260     or return;
3261    
3262     aio_rename "$runtime~", $runtime
3263     and return;
3264    
3265     warn "runtime file written.\n";
3266    
3267     1
3268     }
3269    
3270 root 1.156 sub emergency_save() {
3271 root 1.155 my $freeze_guard = cf::freeze_mainloop;
3272    
3273     warn "enter emergency perl save\n";
3274    
3275     cf::sync_job {
3276     # use a peculiar iteration method to avoid tripping on perl
3277     # refcount bugs in for. also avoids problems with players
3278 root 1.167 # and maps saved/destroyed asynchronously.
3279 root 1.155 warn "begin emergency player save\n";
3280     for my $login (keys %cf::PLAYER) {
3281     my $pl = $cf::PLAYER{$login} or next;
3282     $pl->valid or next;
3283     $pl->save;
3284     }
3285     warn "end emergency player save\n";
3286    
3287     warn "begin emergency map save\n";
3288     for my $path (keys %cf::MAP) {
3289     my $map = $cf::MAP{$path} or next;
3290     $map->valid or next;
3291     $map->save;
3292     }
3293     warn "end emergency map save\n";
3294 root 1.208
3295     warn "begin emergency database checkpoint\n";
3296     BDB::db_env_txn_checkpoint $DB_ENV;
3297     warn "end emergency database checkpoint\n";
3298 root 1.155 };
3299    
3300     warn "leave emergency perl save\n";
3301     }
3302 root 1.22
3303 root 1.211 sub post_cleanup {
3304     my ($make_core) = @_;
3305    
3306     warn Carp::longmess "post_cleanup backtrace"
3307     if $make_core;
3308     }
3309    
3310 root 1.246 sub do_reload_perl() {
3311 root 1.106 # can/must only be called in main
3312     if ($Coro::current != $Coro::main) {
3313 root 1.183 warn "can only reload from main coroutine";
3314 root 1.106 return;
3315     }
3316    
3317 root 1.103 warn "reloading...";
3318    
3319 root 1.212 warn "entering sync_job";
3320    
3321 root 1.213 cf::sync_job {
3322 root 1.214 cf::write_runtime; # external watchdog should not bark
3323 root 1.212 cf::emergency_save;
3324 root 1.214 cf::write_runtime; # external watchdog should not bark
3325 root 1.183
3326 root 1.212 warn "syncing database to disk";
3327     BDB::db_env_txn_checkpoint $DB_ENV;
3328 root 1.106
3329     # if anything goes wrong in here, we should simply crash as we already saved
3330 root 1.65
3331 root 1.183 warn "cancelling all WF_AUTOCANCEL watchers";
3332 root 1.87 for (Event::all_watchers) {
3333     $_->cancel if $_->data & WF_AUTOCANCEL;
3334     }
3335 root 1.65
3336 root 1.183 warn "flushing outstanding aio requests";
3337     for (;;) {
3338 root 1.208 BDB::flush;
3339 root 1.183 IO::AIO::flush;
3340     Coro::cede;
3341 root 1.208 last unless IO::AIO::nreqs || BDB::nreqs;
3342 root 1.183 warn "iterate...";
3343     }
3344    
3345 root 1.223 ++$RELOAD;
3346    
3347 root 1.183 warn "cancelling all extension coros";
3348 root 1.103 $_->cancel for values %EXT_CORO;
3349     %EXT_CORO = ();
3350    
3351 root 1.183 warn "removing commands";
3352 root 1.159 %COMMAND = ();
3353    
3354 root 1.287 warn "removing ext/exti commands";
3355     %EXTCMD = ();
3356     %EXTICMD = ();
3357 root 1.159
3358 root 1.183 warn "unloading/nuking all extensions";
3359 root 1.159 for my $pkg (@EXTS) {
3360 root 1.160 warn "... unloading $pkg";
3361 root 1.159
3362     if (my $cb = $pkg->can ("unload")) {
3363     eval {
3364     $cb->($pkg);
3365     1
3366     } or warn "$pkg unloaded, but with errors: $@";
3367     }
3368    
3369 root 1.160 warn "... nuking $pkg";
3370 root 1.159 Symbol::delete_package $pkg;
3371 root 1.65 }
3372    
3373 root 1.183 warn "unloading all perl modules loaded from $LIBDIR";
3374 root 1.65 while (my ($k, $v) = each %INC) {
3375     next unless $v =~ /^\Q$LIBDIR\E\/.*\.pm$/;
3376    
3377 root 1.183 warn "... unloading $k";
3378 root 1.65 delete $INC{$k};
3379    
3380     $k =~ s/\.pm$//;
3381     $k =~ s/\//::/g;
3382    
3383     if (my $cb = $k->can ("unload_module")) {
3384     $cb->();
3385     }
3386    
3387     Symbol::delete_package $k;
3388     }
3389    
3390 root 1.183 warn "getting rid of safe::, as good as possible";
3391 root 1.65 Symbol::delete_package "safe::$_"
3392 root 1.103 for qw(cf::attachable cf::object cf::object::player cf::client cf::player cf::map cf::party cf::region);
3393 root 1.65
3394 root 1.183 warn "unloading cf.pm \"a bit\"";
3395 root 1.65 delete $INC{"cf.pm"};
3396 root 1.252 delete $INC{"cf/pod.pm"};
3397 root 1.65
3398     # don't, removes xs symbols, too,
3399     # and global variables created in xs
3400     #Symbol::delete_package __PACKAGE__;
3401    
3402 root 1.183 warn "unload completed, starting to reload now";
3403    
3404 root 1.103 warn "reloading cf.pm";
3405 root 1.65 require cf;
3406 root 1.100 cf::_connect_to_perl; # nominally unnecessary, but cannot hurt
3407    
3408 root 1.183 warn "loading config and database again";
3409 root 1.345 cf::reload_config;
3410 root 1.65
3411 root 1.183 warn "loading extensions";
3412 root 1.65 cf::load_extensions;
3413    
3414 root 1.183 warn "reattaching attachments to objects/players";
3415 root 1.222 _global_reattach; # objects, sockets
3416 root 1.183 warn "reattaching attachments to maps";
3417 root 1.144 reattach $_ for values %MAP;
3418 root 1.222 warn "reattaching attachments to players";
3419     reattach $_ for values %PLAYER;
3420 root 1.183
3421 root 1.212 warn "leaving sync_job";
3422 root 1.183
3423 root 1.212 1
3424     } or do {
3425 root 1.106 warn $@;
3426     warn "error while reloading, exiting.";
3427     exit 1;
3428 root 1.212 };
3429 root 1.106
3430 root 1.159 warn "reloaded";
3431 root 1.65 };
3432    
3433 root 1.175 our $RELOAD_WATCHER; # used only during reload
3434    
3435 root 1.246 sub reload_perl() {
3436     # doing reload synchronously and two reloads happen back-to-back,
3437     # coro crashes during coro_state_free->destroy here.
3438    
3439     $RELOAD_WATCHER ||= Event->timer (
3440     reentrant => 0,
3441     after => 0,
3442     data => WF_AUTOCANCEL,
3443     cb => sub {
3444     do_reload_perl;
3445     undef $RELOAD_WATCHER;
3446     },
3447     );
3448     }
3449    
3450 root 1.111 register_command "reload" => sub {
3451 root 1.65 my ($who, $arg) = @_;
3452    
3453     if ($who->flag (FLAG_WIZ)) {
3454 root 1.175 $who->message ("reloading server.");
3455 root 1.374 async {
3456     $Coro::current->{desc} = "perl_reload";
3457     reload_perl;
3458     };
3459 root 1.65 }
3460     };
3461    
3462 root 1.27 unshift @INC, $LIBDIR;
3463 root 1.17
3464 root 1.183 my $bug_warning = 0;
3465    
3466 root 1.239 our @WAIT_FOR_TICK;
3467     our @WAIT_FOR_TICK_BEGIN;
3468    
3469     sub wait_for_tick {
3470 root 1.240 return unless $TICK_WATCHER->is_active;
3471 root 1.241 return if $Coro::current == $Coro::main;
3472    
3473 root 1.239 my $signal = new Coro::Signal;
3474     push @WAIT_FOR_TICK, $signal;
3475     $signal->wait;
3476     }
3477    
3478     sub wait_for_tick_begin {
3479 root 1.240 return unless $TICK_WATCHER->is_active;
3480 root 1.241 return if $Coro::current == $Coro::main;
3481    
3482 root 1.239 my $signal = new Coro::Signal;
3483     push @WAIT_FOR_TICK_BEGIN, $signal;
3484     $signal->wait;
3485     }
3486    
3487 root 1.35 $TICK_WATCHER = Event->timer (
3488 root 1.104 reentrant => 0,
3489 root 1.183 parked => 1,
3490 root 1.191 prio => 0,
3491 root 1.104 at => $NEXT_TICK || $TICK,
3492     data => WF_AUTOCANCEL,
3493     cb => sub {
3494 root 1.183 if ($Coro::current != $Coro::main) {
3495     Carp::cluck "major BUG: server tick called outside of main coro, skipping it"
3496     unless ++$bug_warning > 10;
3497     return;
3498     }
3499    
3500 root 1.265 $NOW = $tick_start = Event::time;
3501 root 1.163
3502 root 1.133 cf::server_tick; # one server iteration
3503 root 1.245
3504 root 1.133 $RUNTIME += $TICK;
3505 root 1.35 $NEXT_TICK += $TICK;
3506    
3507 root 1.214 if ($NOW >= $NEXT_RUNTIME_WRITE) {
3508     $NEXT_RUNTIME_WRITE = $NOW + 10;
3509     Coro::async_pool {
3510 root 1.374 $Coro::current->{desc} = "runtime saver";
3511 root 1.214 write_runtime
3512     or warn "ERROR: unable to write runtime file: $!";
3513     };
3514     }
3515    
3516 root 1.245 if (my $sig = shift @WAIT_FOR_TICK_BEGIN) {
3517     $sig->send;
3518     }
3519     while (my $sig = shift @WAIT_FOR_TICK) {
3520     $sig->send;
3521     }
3522    
3523 root 1.265 $NOW = Event::time;
3524    
3525     # if we are delayed by four ticks or more, skip them all
3526     $NEXT_TICK = $NOW if $NOW >= $NEXT_TICK + $TICK * 4;
3527    
3528     $TICK_WATCHER->at ($NEXT_TICK);
3529     $TICK_WATCHER->start;
3530    
3531     $LOAD = ($NOW - $tick_start) / $TICK;
3532     $LOADAVG = $LOADAVG * 0.75 + $LOAD * 0.25;
3533    
3534 root 1.245 _post_tick;
3535 root 1.35 },
3536     );
3537    
3538 root 1.206 {
3539 root 1.363 BDB::min_parallel 8;
3540 root 1.206 BDB::max_poll_time $TICK * 0.1;
3541     $BDB_POLL_WATCHER = Event->io (
3542     reentrant => 0,
3543     fd => BDB::poll_fileno,
3544     poll => 'r',
3545     prio => 0,
3546     data => WF_AUTOCANCEL,
3547     cb => \&BDB::poll_cb,
3548     );
3549    
3550     BDB::set_sync_prepare {
3551     my $status;
3552     my $current = $Coro::current;
3553     (
3554     sub {
3555     $status = $!;
3556     $current->ready; undef $current;
3557     },
3558     sub {
3559     Coro::schedule while defined $current;
3560     $! = $status;
3561     },
3562     )
3563     };
3564 root 1.77
3565 root 1.206 unless ($DB_ENV) {
3566     $DB_ENV = BDB::db_env_create;
3567 root 1.371 $DB_ENV->set_flags (BDB::AUTO_COMMIT | BDB::REGION_INIT | BDB::TXN_NOSYNC
3568     | BDB::LOG_AUTOREMOVE, 1);
3569     $DB_ENV->set_timeout (30, BDB::SET_TXN_TIMEOUT);
3570     $DB_ENV->set_timeout (30, BDB::SET_LOCK_TIMEOUT);
3571 root 1.206
3572     cf::sync_job {
3573 root 1.208 eval {
3574     BDB::db_env_open
3575     $DB_ENV,
3576 root 1.253 $BDBDIR,
3577 root 1.208 BDB::INIT_LOCK | BDB::INIT_LOG | BDB::INIT_MPOOL | BDB::INIT_TXN
3578     | BDB::RECOVER | BDB::REGISTER | BDB::USE_ENVIRON | BDB::CREATE,
3579     0666;
3580    
3581 root 1.253 cf::cleanup "db_env_open($BDBDIR): $!" if $!;
3582 root 1.208 };
3583    
3584     cf::cleanup "db_env_open(db): $@" if $@;
3585 root 1.206 };
3586     }
3587 root 1.363
3588 root 1.371 $BDB_DEADLOCK_WATCHER = Event->timer (
3589     after => 3,
3590     interval => 1,
3591     hard => 1,
3592     prio => 0,
3593     data => WF_AUTOCANCEL,
3594     cb => sub {
3595     BDB::db_env_lock_detect $DB_ENV, 0, BDB::LOCK_DEFAULT, 0, sub { };
3596     },
3597     );
3598 root 1.363 $BDB_CHECKPOINT_WATCHER = Event->timer (
3599     after => 11,
3600     interval => 60,
3601     hard => 1,
3602     prio => 0,
3603     data => WF_AUTOCANCEL,
3604     cb => sub {
3605     BDB::db_env_txn_checkpoint $DB_ENV, 0, 0, 0, sub { };
3606     },
3607     );
3608     $BDB_TRICKLE_WATCHER = Event->timer (
3609     after => 5,
3610     interval => 10,
3611     hard => 1,
3612     prio => 0,
3613     data => WF_AUTOCANCEL,
3614     cb => sub {
3615     BDB::db_env_memp_trickle $DB_ENV, 20, 0, sub { };
3616     },
3617     );
3618 root 1.206 }
3619    
3620     {
3621     IO::AIO::min_parallel 8;
3622    
3623     undef $Coro::AIO::WATCHER;
3624     IO::AIO::max_poll_time $TICK * 0.1;
3625     $AIO_POLL_WATCHER = Event->io (
3626     reentrant => 0,
3627 root 1.214 data => WF_AUTOCANCEL,
3628 root 1.206 fd => IO::AIO::poll_fileno,
3629     poll => 'r',
3630 root 1.376 prio => 0,
3631 root 1.206 cb => \&IO::AIO::poll_cb,
3632     );
3633     }
3634 root 1.108
3635 root 1.262 my $_log_backtrace;
3636    
3637 root 1.260 sub _log_backtrace {
3638     my ($msg, @addr) = @_;
3639    
3640 root 1.262 $msg =~ s/\n//;
3641 root 1.260
3642 root 1.262 # limit the # of concurrent backtraces
3643     if ($_log_backtrace < 2) {
3644     ++$_log_backtrace;
3645     async {
3646 root 1.374 $Coro::current->{desc} = "abt $msg";
3647    
3648 root 1.262 my @bt = fork_call {
3649     @addr = map { sprintf "%x", $_ } @addr;
3650     my $self = (-f "/proc/$$/exe") ? "/proc/$$/exe" : $^X;
3651     open my $fh, "exec addr2line -C -f -i -e \Q$self\E @addr 2>&1 |"
3652     or die "addr2line: $!";
3653    
3654     my @funcs;
3655     my @res = <$fh>;
3656     chomp for @res;
3657     while (@res) {
3658     my ($func, $line) = splice @res, 0, 2, ();
3659     push @funcs, "[$func] $line";
3660     }
3661 root 1.260
3662 root 1.262 @funcs
3663     };
3664 root 1.260
3665 root 1.262 LOG llevInfo, "[ABT] $msg\n";
3666     LOG llevInfo, "[ABT] $_\n" for @bt;
3667     --$_log_backtrace;
3668     };
3669     } else {
3670 root 1.260 LOG llevInfo, "[ABT] $msg\n";
3671 root 1.262 LOG llevInfo, "[ABT] [suppressed]\n";
3672     }
3673 root 1.260 }
3674    
3675 root 1.249 # load additional modules
3676     use cf::pod;
3677    
3678 root 1.125 END { cf::emergency_save }
3679    
3680 root 1.1 1
3681