ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.372
Committed: Sat Sep 15 13:25:33 2007 UTC (16 years, 8 months ago) by root
Branch: MAIN
Changes since 1.371: +0 -1 lines
Log Message:
do no longer corrupt passwqords, letting everybody to log-in

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