ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.373
Committed: Sat Sep 15 15:58:06 2007 UTC (16 years, 8 months ago) by root
Branch: MAIN
Changes since 1.372: +0 -7 lines
Log Message:
- quick work around for gate-move-bug. the real fix is probably
  to either give everything a movetype OR to treat movetype 0
  as special (can move anywhere if there is at leats one movetype not blocked).
  the latter was too big a change for me to do it, thus just the workaround.
- do not do locking on i/o anymore.

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