ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.410
Committed: Tue Jan 15 12:02:24 2008 UTC (16 years, 4 months ago) by root
Branch: MAIN
CVS Tags: rel-2_41
Changes since 1.409: +11 -1 lines
Log Message:
*** empty log message ***

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