ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.382
Committed: Thu Oct 11 00:34:31 2007 UTC (16 years, 7 months ago) by root
Branch: MAIN
Changes since 1.381: +1 -1 lines
Log Message:
- rewrite follow to use a coroutine and make it generally safer.
- the map scheduler, under duress, tried to swap out maps fast, but when
  a sync job was entered it also entered and andless loop causing a freeze.
  hack around this temporairly by always sleeping 50ms.

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