ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.386
Committed: Mon Oct 15 17:50:27 2007 UTC (16 years, 7 months ago) by root
Branch: MAIN
Changes since 1.385: +36 -10 lines
Log Message:
implement merge checking by slow and careful recursion on the objects, slow, but hopefully very rare even in the future

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