ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.387
Committed: Mon Oct 15 23:49:10 2007 UTC (16 years, 7 months ago) by root
Branch: MAIN
Changes since 1.386: +6 -2 lines
Log Message:
cede no longer returns a status

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