ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.377
Committed: Thu Oct 4 11:36:38 2007 UTC (16 years, 7 months ago) by root
Branch: MAIN
Changes since 1.376: +1 -1 lines
Log Message:
d'oh

File Contents

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