ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.371
Committed: Thu Sep 13 16:16:01 2007 UTC (16 years, 8 months ago) by root
Branch: MAIN
Changes since 1.370: +15 -4 lines
Log Message:
- do better transaction handling
- require BDB 1.1

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