ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.368
Committed: Wed Sep 12 11:18:25 2007 UTC (16 years, 8 months ago) by root
Branch: MAIN
Changes since 1.367: +4 -2 lines
Log Message:
be more intelligent when deciding where to go when hunting for tags

File Contents

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