ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.388
Committed: Thu Oct 18 02:56:13 2007 UTC (16 years, 7 months ago) by root
Branch: MAIN
Changes since 1.387: +2 -3 lines
Log Message:
remove storable compatibility mode

File Contents

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