ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.378
Committed: Thu Oct 4 11:36:54 2007 UTC (16 years, 7 months ago) by root
Branch: MAIN
Changes since 1.377: +0 -3 lines
Log Message:
should no longer be necessary

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