ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.411
Committed: Fri Feb 1 15:54:07 2008 UTC (16 years, 3 months ago) by root
Branch: MAIN
CVS Tags: rel-2_42
Changes since 1.410: +2 -4 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 root 1.411 cf::cleanup "mandatory extension failed to load, exiting.";
1322 root 1.278 }
1323    
1324 root 1.279 warn $msg;
1325 root 1.278 }
1326    
1327     $done{$k} = delete $todo{$k};
1328     push @EXTS, $v->{pkg};
1329 root 1.279 $progress = 1;
1330 root 1.278 }
1331 root 1.1
1332 root 1.278 skip:
1333     die "cannot load " . (join ", ", keys %todo) . ": unable to resolve dependencies\n"
1334     unless $progress;
1335     }
1336     };
1337 root 1.1 }
1338    
1339 root 1.8 #############################################################################
1340 root 1.70
1341 root 1.281 =back
1342    
1343 root 1.70 =head2 CORE EXTENSIONS
1344    
1345     Functions and methods that extend core crossfire objects.
1346    
1347 root 1.143 =cut
1348    
1349     package cf::player;
1350    
1351 root 1.154 use Coro::AIO;
1352    
1353 root 1.95 =head3 cf::player
1354    
1355 root 1.70 =over 4
1356 root 1.22
1357 root 1.361 =item cf::player::num_playing
1358    
1359     Returns the official number of playing players, as per the Crossfire metaserver rules.
1360    
1361     =cut
1362    
1363     sub num_playing {
1364     scalar grep
1365     $_->ob->map
1366     && !$_->hidden
1367     && !$_->ob->flag (cf::FLAG_WIZ),
1368     cf::player::list
1369     }
1370    
1371 root 1.143 =item cf::player::find $login
1372 root 1.23
1373 root 1.143 Returns the given player object, loading it if necessary (might block).
1374 root 1.23
1375     =cut
1376    
1377 root 1.145 sub playerdir($) {
1378 root 1.253 "$PLAYERDIR/"
1379 root 1.145 . (ref $_[0] ? $_[0]->ob->name : $_[0])
1380     }
1381    
1382 root 1.143 sub path($) {
1383 root 1.145 my $login = ref $_[0] ? $_[0]->ob->name : $_[0];
1384    
1385 root 1.234 (playerdir $login) . "/playerdata"
1386 root 1.143 }
1387    
1388     sub find_active($) {
1389     $cf::PLAYER{$_[0]}
1390     and $cf::PLAYER{$_[0]}->active
1391     and $cf::PLAYER{$_[0]}
1392     }
1393    
1394     sub exists($) {
1395     my ($login) = @_;
1396    
1397     $cf::PLAYER{$login}
1398 root 1.180 or cf::sync_job { !aio_stat path $login }
1399 root 1.143 }
1400    
1401     sub find($) {
1402     return $cf::PLAYER{$_[0]} || do {
1403     my $login = $_[0];
1404    
1405     my $guard = cf::lock_acquire "user_find:$login";
1406    
1407 root 1.151 $cf::PLAYER{$_[0]} || do {
1408 root 1.234 # rename old playerfiles to new ones
1409     #TODO: remove when no longer required
1410     aio_link +(playerdir $login) . "/$login.pl.pst", (playerdir $login) . "/playerdata.pst";
1411     aio_link +(playerdir $login) . "/$login.pl" , (playerdir $login) . "/playerdata";
1412     aio_unlink +(playerdir $login) . "/$login.pl.pst";
1413     aio_unlink +(playerdir $login) . "/$login.pl";
1414    
1415 root 1.356 my $f = new_from_file cf::object::thawer path $login
1416 root 1.151 or return;
1417 root 1.356
1418     my $pl = cf::player::load_pl $f
1419     or return;
1420     local $cf::PLAYER_LOADING{$login} = $pl;
1421     $f->resolve_delayed_derefs;
1422 root 1.151 $cf::PLAYER{$login} = $pl
1423     }
1424     }
1425 root 1.143 }
1426    
1427     sub save($) {
1428     my ($pl) = @_;
1429    
1430     return if $pl->{deny_save};
1431    
1432     my $path = path $pl;
1433     my $guard = cf::lock_acquire "user_save:$path";
1434    
1435     return if $pl->{deny_save};
1436 root 1.146
1437 root 1.154 aio_mkdir playerdir $pl, 0770;
1438 root 1.143 $pl->{last_save} = $cf::RUNTIME;
1439    
1440     $pl->save_pl ($path);
1441 root 1.346 cf::cede_to_tick;
1442 root 1.143 }
1443    
1444     sub new($) {
1445     my ($login) = @_;
1446    
1447     my $self = create;
1448    
1449     $self->ob->name ($login);
1450     $self->{deny_save} = 1;
1451    
1452     $cf::PLAYER{$login} = $self;
1453    
1454     $self
1455 root 1.23 }
1456    
1457 root 1.329 =item $player->send_msg ($channel, $msg, $color, [extra...])
1458    
1459     =cut
1460    
1461     sub send_msg {
1462     my $ns = shift->ns
1463     or return;
1464     $ns->send_msg (@_);
1465     }
1466    
1467 root 1.154 =item $pl->quit_character
1468    
1469     Nukes the player without looking back. If logged in, the connection will
1470     be destroyed. May block for a long time.
1471    
1472     =cut
1473    
1474 root 1.145 sub quit_character {
1475     my ($pl) = @_;
1476    
1477 root 1.220 my $name = $pl->ob->name;
1478    
1479 root 1.145 $pl->{deny_save} = 1;
1480     $pl->password ("*"); # this should lock out the player until we nuked the dir
1481    
1482     $pl->invoke (cf::EVENT_PLAYER_LOGOUT, 1) if $pl->active;
1483     $pl->deactivate;
1484     $pl->invoke (cf::EVENT_PLAYER_QUIT);
1485     $pl->ns->destroy if $pl->ns;
1486    
1487     my $path = playerdir $pl;
1488     my $temp = "$path~$cf::RUNTIME~deleting~";
1489 root 1.154 aio_rename $path, $temp;
1490 root 1.150 delete $cf::PLAYER{$pl->ob->name};
1491     $pl->destroy;
1492 root 1.220
1493     my $prefix = qr<^~\Q$name\E/>;
1494    
1495     # nuke player maps
1496     $cf::MAP{$_}->nuke for grep /$prefix/, keys %cf::MAP;
1497    
1498 root 1.150 IO::AIO::aio_rmtree $temp;
1499 root 1.145 }
1500    
1501 pippijn 1.221 =item $pl->kick
1502    
1503     Kicks a player out of the game. This destroys the connection.
1504    
1505     =cut
1506    
1507     sub kick {
1508     my ($pl, $kicker) = @_;
1509    
1510     $pl->invoke (cf::EVENT_PLAYER_KICK, $kicker);
1511     $pl->killer ("kicked");
1512     $pl->ns->destroy;
1513     }
1514    
1515 root 1.154 =item cf::player::list_logins
1516    
1517     Returns am arrayref of all valid playernames in the system, can take a
1518     while and may block, so not sync_job-capable, ever.
1519    
1520     =cut
1521    
1522     sub list_logins {
1523 root 1.253 my $dirs = aio_readdir $PLAYERDIR
1524 root 1.154 or return [];
1525    
1526     my @logins;
1527    
1528     for my $login (@$dirs) {
1529 root 1.354 my $path = path $login;
1530    
1531     # a .pst is a dead give-away for a valid player
1532     unless (-e "$path.pst") {
1533     my $fh = aio_open $path, Fcntl::O_RDONLY, 0 or next;
1534     aio_read $fh, 0, 512, my $buf, 0 or next;
1535     $buf !~ /^password -------------$/m or next; # official not-valid tag
1536     }
1537 root 1.154
1538     utf8::decode $login;
1539     push @logins, $login;
1540     }
1541    
1542     \@logins
1543     }
1544    
1545     =item $player->maps
1546    
1547 root 1.166 Returns an arrayref of map paths that are private for this
1548 root 1.154 player. May block.
1549    
1550     =cut
1551    
1552     sub maps($) {
1553     my ($pl) = @_;
1554    
1555 root 1.201 $pl = ref $pl ? $pl->ob->name : $pl;
1556    
1557 root 1.154 my $files = aio_readdir playerdir $pl
1558     or return;
1559    
1560     my @paths;
1561    
1562     for (@$files) {
1563     utf8::decode $_;
1564     next if /\.(?:pl|pst)$/;
1565 root 1.158 next unless /^$PATH_SEP/o;
1566 root 1.154
1567 root 1.201 push @paths, cf::map::normalise "~$pl/$_";
1568 root 1.154 }
1569    
1570     \@paths
1571     }
1572    
1573 root 1.283 =item $protocol_xml = $player->expand_cfpod ($crossfire_pod)
1574    
1575     Expand crossfire pod fragments into protocol xml.
1576    
1577     =cut
1578    
1579 root 1.393 use re 'eval';
1580 root 1.391
1581     my $group;
1582     my $interior; $interior = qr{
1583 root 1.393 # match a pod interior sequence sans C<< >>
1584 root 1.391 (?:
1585     \ (.*?)\ (?{ $group = $^N })
1586     | < (??{$interior}) >
1587     )
1588     }x;
1589    
1590 root 1.283 sub expand_cfpod {
1591 root 1.391 my ($self, $pod) = @_;
1592    
1593     my $xml;
1594 root 1.283
1595 root 1.391 while () {
1596 root 1.392 if ($pod =~ /\G( (?: [^BCGHITU]+ | .(?!<) )+ )/xgcs) {
1597 root 1.391 $group = $1;
1598    
1599     $group =~ s/&/&amp;/g;
1600     $group =~ s/</&lt;/g;
1601    
1602     $xml .= $group;
1603     } elsif ($pod =~ m%\G
1604     ([BCGHITU])
1605     <
1606     (?:
1607     ([^<>]*) (?{ $group = $^N })
1608     | < $interior >
1609     )
1610     >
1611     %gcsx
1612     ) {
1613     my ($code, $data) = ($1, $group);
1614    
1615     if ($code eq "B") {
1616     $xml .= "<b>" . expand_cfpod ($self, $data) . "</b>";
1617     } elsif ($code eq "I") {
1618     $xml .= "<i>" . expand_cfpod ($self, $data) . "</i>";
1619     } elsif ($code eq "U") {
1620     $xml .= "<u>" . expand_cfpod ($self, $data) . "</u>";
1621     } elsif ($code eq "C") {
1622     $xml .= "<tt>" . expand_cfpod ($self, $data) . "</tt>";
1623     } elsif ($code eq "T") {
1624     $xml .= "<big><b>" . expand_cfpod ($self, $data) . "</b></big>";
1625     } elsif ($code eq "G") {
1626     my ($male, $female) = split /\|/, $data;
1627     $data = $self->gender ? $female : $male;
1628     $xml .= expand_cfpod ($self, $data);
1629     } elsif ($code eq "H") {
1630     $xml .= ("<fg name=\"lightblue\">[" . expand_cfpod ($self, $data) . " (Use hintmode to suppress hints)]</fg>",
1631     "<fg name=\"lightblue\">[Hint suppressed, see hintmode]</fg>",
1632     "")
1633     [$self->{hintmode}];
1634     } else {
1635     $xml .= "error processing '$code($data)' directive";
1636     }
1637     } else {
1638     if ($pod =~ /\G(.+)/) {
1639     warn "parse error while expanding $pod (at $1)";
1640     }
1641     last;
1642     }
1643     }
1644    
1645     for ($xml) {
1646     # create single paragraphs (very hackish)
1647     s/(?<=\S)\n(?=\w)/ /g;
1648    
1649     # compress some whitespace
1650     s/\s+\n/\n/g; # ws line-ends
1651     s/\n\n+/\n/g; # double lines
1652     s/^\n+//; # beginning lines
1653     s/\n+$//; # ending lines
1654     }
1655 root 1.293
1656 root 1.391 $xml
1657 root 1.283 }
1658    
1659 root 1.393 no re 'eval';
1660    
1661 root 1.291 sub hintmode {
1662     $_[0]{hintmode} = $_[1] if @_ > 1;
1663     $_[0]{hintmode}
1664     }
1665    
1666 root 1.316 =item $player->ext_reply ($msgid, @msg)
1667 root 1.95
1668     Sends an ext reply to the player.
1669    
1670     =cut
1671    
1672 root 1.316 sub ext_reply($$@) {
1673     my ($self, $id, @msg) = @_;
1674 root 1.95
1675 root 1.336 $self->ns->ext_reply ($id, @msg)
1676 root 1.95 }
1677    
1678 root 1.316 =item $player->ext_msg ($type, @msg)
1679 root 1.231
1680     Sends an ext event to the client.
1681    
1682     =cut
1683    
1684 root 1.316 sub ext_msg($$@) {
1685     my ($self, $type, @msg) = @_;
1686 root 1.231
1687 root 1.316 $self->ns->ext_msg ($type, @msg);
1688 root 1.231 }
1689    
1690 root 1.238 =head3 cf::region
1691    
1692     =over 4
1693    
1694     =cut
1695    
1696     package cf::region;
1697    
1698     =item cf::region::find_by_path $path
1699    
1700 root 1.281 Tries to decuce the likely region for a map knowing only its path.
1701 root 1.238
1702     =cut
1703    
1704     sub find_by_path($) {
1705     my ($path) = @_;
1706    
1707     my ($match, $specificity);
1708    
1709     for my $region (list) {
1710 root 1.304 if ($region->{match} && $path =~ $region->{match}) {
1711 root 1.238 ($match, $specificity) = ($region, $region->specificity)
1712     if $region->specificity > $specificity;
1713     }
1714     }
1715    
1716     $match
1717     }
1718 root 1.143
1719 root 1.95 =back
1720    
1721 root 1.110 =head3 cf::map
1722    
1723     =over 4
1724    
1725     =cut
1726    
1727     package cf::map;
1728    
1729     use Fcntl;
1730     use Coro::AIO;
1731    
1732 root 1.166 use overload
1733 root 1.173 '""' => \&as_string,
1734     fallback => 1;
1735 root 1.166
1736 root 1.133 our $MAX_RESET = 3600;
1737     our $DEFAULT_RESET = 3000;
1738 root 1.110
1739     sub generate_random_map {
1740 root 1.166 my ($self, $rmp) = @_;
1741 root 1.110 # mit "rum" bekleckern, nicht
1742 root 1.166 $self->_create_random_map (
1743 root 1.110 $rmp->{wallstyle}, $rmp->{wall_name}, $rmp->{floorstyle}, $rmp->{monsterstyle},
1744     $rmp->{treasurestyle}, $rmp->{layoutstyle}, $rmp->{doorstyle}, $rmp->{decorstyle},
1745     $rmp->{origin_map}, $rmp->{final_map}, $rmp->{exitstyle}, $rmp->{this_map},
1746     $rmp->{exit_on_final_map},
1747     $rmp->{xsize}, $rmp->{ysize},
1748     $rmp->{expand2x}, $rmp->{layoutoptions1}, $rmp->{layoutoptions2}, $rmp->{layoutoptions3},
1749     $rmp->{symmetry}, $rmp->{difficulty}, $rmp->{difficulty_given}, $rmp->{difficulty_increase},
1750     $rmp->{dungeon_level}, $rmp->{dungeon_depth}, $rmp->{decoroptions}, $rmp->{orientation},
1751     $rmp->{origin_y}, $rmp->{origin_x}, $rmp->{random_seed}, $rmp->{total_map_hp},
1752     $rmp->{map_layout_style}, $rmp->{treasureoptions}, $rmp->{symmetry_used},
1753 root 1.162 (cf::region::find $rmp->{region}), $rmp->{custom}
1754     )
1755 root 1.110 }
1756    
1757 root 1.187 =item cf::map->register ($regex, $prio)
1758    
1759     Register a handler for the map path matching the given regex at the
1760     givne priority (higher is better, built-in handlers have priority 0, the
1761     default).
1762    
1763     =cut
1764    
1765 root 1.166 sub register {
1766 root 1.187 my (undef, $regex, $prio) = @_;
1767 root 1.166 my $pkg = caller;
1768    
1769     no strict;
1770     push @{"$pkg\::ISA"}, __PACKAGE__;
1771    
1772 root 1.187 $EXT_MAP{$pkg} = [$prio, qr<$regex>];
1773 root 1.166 }
1774    
1775     # also paths starting with '/'
1776 root 1.187 $EXT_MAP{"cf::map"} = [0, qr{^(?=/)}];
1777 root 1.166
1778 root 1.170 sub thawer_merge {
1779 root 1.172 my ($self, $merge) = @_;
1780    
1781 root 1.170 # we have to keep some variables in memory intact
1782 root 1.172 local $self->{path};
1783     local $self->{load_path};
1784 root 1.170
1785 root 1.172 $self->SUPER::thawer_merge ($merge);
1786 root 1.170 }
1787    
1788 root 1.166 sub normalise {
1789     my ($path, $base) = @_;
1790    
1791 root 1.192 $path = "$path"; # make sure its a string
1792    
1793 root 1.199 $path =~ s/\.map$//;
1794    
1795 root 1.166 # map plan:
1796     #
1797     # /! non-realised random map exit (special hack!)
1798     # {... are special paths that are not being touched
1799     # ?xxx/... are special absolute paths
1800     # ?random/... random maps
1801     # /... normal maps
1802     # ~user/... per-player map of a specific user
1803    
1804     $path =~ s/$PATH_SEP/\//go;
1805    
1806     # treat it as relative path if it starts with
1807     # something that looks reasonable
1808     if ($path =~ m{^(?:\./|\.\./|\w)}) {
1809     $base or Carp::carp "normalise called with relative path and no base: '$path'";
1810    
1811     $base =~ s{[^/]+/?$}{};
1812     $path = "$base/$path";
1813     }
1814    
1815     for ($path) {
1816     redo if s{//}{/};
1817     redo if s{/\.?/}{/};
1818     redo if s{/[^/]+/\.\./}{/};
1819     }
1820    
1821     $path
1822     }
1823    
1824     sub new_from_path {
1825     my (undef, $path, $base) = @_;
1826    
1827     return $path if UNIVERSAL::isa $path, "cf::map"; # already a map object
1828    
1829     $path = normalise $path, $base;
1830    
1831 root 1.187 for my $pkg (sort { $EXT_MAP{$b}[0] <=> $EXT_MAP{$a}[0] } keys %EXT_MAP) {
1832     if ($path =~ $EXT_MAP{$pkg}[1]) {
1833 root 1.166 my $self = bless cf::map::new, $pkg;
1834     $self->{path} = $path; $self->path ($path);
1835     $self->init; # pass $1 etc.
1836     return $self;
1837     }
1838     }
1839    
1840 root 1.308 Carp::cluck "unable to resolve path '$path' (base '$base').";
1841 root 1.166 ()
1842     }
1843    
1844     sub init {
1845     my ($self) = @_;
1846    
1847     $self
1848     }
1849    
1850     sub as_string {
1851     my ($self) = @_;
1852    
1853     "$self->{path}"
1854     }
1855    
1856     # the displayed name, this is a one way mapping
1857     sub visible_name {
1858     &as_string
1859     }
1860    
1861     # the original (read-only) location
1862     sub load_path {
1863     my ($self) = @_;
1864    
1865 root 1.254 "$MAPDIR/$self->{path}.map"
1866 root 1.166 }
1867    
1868     # the temporary/swap location
1869     sub save_path {
1870     my ($self) = @_;
1871    
1872 root 1.167 (my $path = $_[0]{path}) =~ s/\//$PATH_SEP/g;
1873 root 1.254 "$TMPDIR/$path.map"
1874 root 1.166 }
1875    
1876     # the unique path, undef == no special unique path
1877     sub uniq_path {
1878     my ($self) = @_;
1879    
1880 root 1.167 (my $path = $_[0]{path}) =~ s/\//$PATH_SEP/g;
1881 root 1.253 "$UNIQUEDIR/$path"
1882 root 1.166 }
1883    
1884 root 1.110 # and all this just because we cannot iterate over
1885     # all maps in C++...
1886     sub change_all_map_light {
1887     my ($change) = @_;
1888    
1889 root 1.122 $_->change_map_light ($change)
1890     for grep $_->outdoor, values %cf::MAP;
1891 root 1.110 }
1892    
1893 root 1.275 sub decay_objects {
1894     my ($self) = @_;
1895    
1896     return if $self->{deny_reset};
1897    
1898     $self->do_decay_objects;
1899     }
1900    
1901 root 1.166 sub unlink_save {
1902     my ($self) = @_;
1903    
1904     utf8::encode (my $save = $self->save_path);
1905 root 1.170 IO::AIO::aioreq_pri 4; Coro::AIO::aio_unlink $save;
1906     IO::AIO::aioreq_pri 4; Coro::AIO::aio_unlink "$save.pst";
1907 root 1.166 }
1908    
1909     sub load_header_from($) {
1910     my ($self, $path) = @_;
1911 root 1.110
1912     utf8::encode $path;
1913 root 1.356 my $f = new_from_file cf::object::thawer $path
1914     or return;
1915 root 1.110
1916 root 1.356 $self->_load_header ($f)
1917 root 1.110 or return;
1918    
1919 root 1.356 local $MAP_LOADING{$self->{path}} = $self;
1920     $f->resolve_delayed_derefs;
1921    
1922 root 1.166 $self->{load_path} = $path;
1923 root 1.135
1924 root 1.166 1
1925     }
1926 root 1.110
1927 root 1.188 sub load_header_orig {
1928 root 1.166 my ($self) = @_;
1929 root 1.110
1930 root 1.166 $self->load_header_from ($self->load_path)
1931 root 1.110 }
1932    
1933 root 1.188 sub load_header_temp {
1934 root 1.166 my ($self) = @_;
1935 root 1.110
1936 root 1.166 $self->load_header_from ($self->save_path)
1937     }
1938 root 1.110
1939 root 1.188 sub prepare_temp {
1940     my ($self) = @_;
1941    
1942     $self->last_access ((delete $self->{last_access})
1943     || $cf::RUNTIME); #d#
1944     # safety
1945     $self->{instantiate_time} = $cf::RUNTIME
1946     if $self->{instantiate_time} > $cf::RUNTIME;
1947     }
1948    
1949     sub prepare_orig {
1950     my ($self) = @_;
1951    
1952     $self->{load_original} = 1;
1953     $self->{instantiate_time} = $cf::RUNTIME;
1954     $self->last_access ($cf::RUNTIME);
1955     $self->instantiate;
1956     }
1957    
1958 root 1.166 sub load_header {
1959     my ($self) = @_;
1960 root 1.110
1961 root 1.188 if ($self->load_header_temp) {
1962     $self->prepare_temp;
1963 root 1.166 } else {
1964 root 1.188 $self->load_header_orig
1965 root 1.166 or return;
1966 root 1.188 $self->prepare_orig;
1967 root 1.166 }
1968 root 1.120
1969 root 1.275 $self->{deny_reset} = 1
1970     if $self->no_reset;
1971    
1972 root 1.238 $self->default_region (cf::region::find_by_path $self->{path})
1973     unless $self->default_region;
1974    
1975 root 1.166 1
1976     }
1977 root 1.110
1978 root 1.166 sub find;
1979     sub find {
1980     my ($path, $origin) = @_;
1981 root 1.134
1982 root 1.166 $path = normalise $path, $origin && $origin->path;
1983 root 1.110
1984 root 1.358 cf::lock_wait "map_data:$path";#d#remove
1985 root 1.166 cf::lock_wait "map_find:$path";
1986 root 1.110
1987 root 1.166 $cf::MAP{$path} || do {
1988 root 1.358 my $guard1 = cf::lock_acquire "map_find:$path";
1989     my $guard2 = cf::lock_acquire "map_data:$path"; # just for the fun of it
1990    
1991 root 1.166 my $map = new_from_path cf::map $path
1992     or return;
1993 root 1.110
1994 root 1.116 $map->{last_save} = $cf::RUNTIME;
1995 root 1.110
1996 root 1.166 $map->load_header
1997     or return;
1998 root 1.134
1999 root 1.275 if ($map->should_reset) {#d#TODO# disabled, crashy (locking issue?)
2000 root 1.185 # doing this can freeze the server in a sync job, obviously
2001     #$cf::WAIT_FOR_TICK->wait;
2002 root 1.358 undef $guard1;
2003     undef $guard2;
2004 root 1.112 $map->reset;
2005 root 1.192 return find $path;
2006 root 1.112 }
2007 root 1.110
2008 root 1.166 $cf::MAP{$path} = $map
2009 root 1.110 }
2010     }
2011    
2012 root 1.188 sub pre_load { }
2013     sub post_load { }
2014    
2015 root 1.110 sub load {
2016     my ($self) = @_;
2017    
2018 root 1.196 local $self->{deny_reset} = 1; # loading can take a long time
2019    
2020 root 1.120 my $path = $self->{path};
2021    
2022 root 1.256 {
2023 root 1.357 my $guard = cf::lock_acquire "map_data:$path";
2024 root 1.256
2025 root 1.357 return unless $self->valid;
2026 root 1.360 return unless $self->in_memory == cf::MAP_SWAPPED;
2027 root 1.110
2028 root 1.256 $self->in_memory (cf::MAP_LOADING);
2029 root 1.110
2030 root 1.256 $self->alloc;
2031 root 1.188
2032 root 1.256 $self->pre_load;
2033 root 1.346 cf::cede_to_tick;
2034 root 1.188
2035 root 1.356 my $f = new_from_file cf::object::thawer $self->{load_path};
2036     $f->skip_block;
2037     $self->_load_objects ($f)
2038 root 1.256 or return;
2039 root 1.110
2040 root 1.256 $self->set_object_flag (cf::FLAG_OBJ_ORIGINAL, 1)
2041     if delete $self->{load_original};
2042 root 1.111
2043 root 1.256 if (my $uniq = $self->uniq_path) {
2044     utf8::encode $uniq;
2045 root 1.356 unless (aio_stat $uniq) {
2046     if (my $f = new_from_file cf::object::thawer $uniq) {
2047     $self->clear_unique_items;
2048     $self->_load_objects ($f);
2049     $f->resolve_delayed_derefs;
2050     }
2051 root 1.256 }
2052 root 1.110 }
2053    
2054 root 1.356 $f->resolve_delayed_derefs;
2055    
2056 root 1.346 cf::cede_to_tick;
2057 root 1.256 # now do the right thing for maps
2058     $self->link_multipart_objects;
2059 root 1.110 $self->difficulty ($self->estimate_difficulty)
2060     unless $self->difficulty;
2061 root 1.346 cf::cede_to_tick;
2062 root 1.256
2063     unless ($self->{deny_activate}) {
2064     $self->decay_objects;
2065     $self->fix_auto_apply;
2066     $self->update_buttons;
2067 root 1.346 cf::cede_to_tick;
2068 root 1.256 $self->set_darkness_map;
2069 root 1.346 cf::cede_to_tick;
2070 root 1.256 $self->activate;
2071     }
2072    
2073 root 1.325 $self->{last_save} = $cf::RUNTIME;
2074     $self->last_access ($cf::RUNTIME);
2075 root 1.324
2076 root 1.256 $self->in_memory (cf::MAP_IN_MEMORY);
2077 root 1.110 }
2078    
2079 root 1.188 $self->post_load;
2080 root 1.166 }
2081    
2082     sub customise_for {
2083     my ($self, $ob) = @_;
2084    
2085     return find "~" . $ob->name . "/" . $self->{path}
2086     if $self->per_player;
2087 root 1.134
2088 root 1.275 # return find "?party/" . $ob->name . "/" . $self->{path}
2089     # if $self->per_party;
2090    
2091 root 1.166 $self
2092 root 1.110 }
2093    
2094 root 1.157 # find and load all maps in the 3x3 area around a map
2095 root 1.333 sub load_neighbours {
2096 root 1.157 my ($map) = @_;
2097    
2098 root 1.333 my @neigh; # diagonal neighbours
2099 root 1.157
2100     for (0 .. 3) {
2101     my $neigh = $map->tile_path ($_)
2102     or next;
2103     $neigh = find $neigh, $map
2104     or next;
2105     $neigh->load;
2106    
2107 root 1.333 push @neigh,
2108     [$neigh->tile_path (($_ + 3) % 4), $neigh],
2109     [$neigh->tile_path (($_ + 1) % 4), $neigh];
2110 root 1.157 }
2111    
2112 root 1.333 for (grep defined $_->[0], @neigh) {
2113     my ($path, $origin) = @$_;
2114     my $neigh = find $path, $origin
2115 root 1.157 or next;
2116     $neigh->load;
2117     }
2118     }
2119    
2120 root 1.133 sub find_sync {
2121 root 1.110 my ($path, $origin) = @_;
2122    
2123 root 1.157 cf::sync_job { find $path, $origin }
2124 root 1.133 }
2125    
2126     sub do_load_sync {
2127     my ($map) = @_;
2128 root 1.110
2129 root 1.339 cf::LOG cf::llevDebug | cf::logBacktrace, "do_load_sync"
2130 root 1.342 if $Coro::current == $Coro::main;
2131 root 1.339
2132 root 1.133 cf::sync_job { $map->load };
2133 root 1.110 }
2134    
2135 root 1.157 our %MAP_PREFETCH;
2136 root 1.183 our $MAP_PREFETCHER = undef;
2137 root 1.157
2138     sub find_async {
2139 root 1.339 my ($path, $origin, $load) = @_;
2140 root 1.157
2141 root 1.166 $path = normalise $path, $origin && $origin->{path};
2142 root 1.157
2143 root 1.166 if (my $map = $cf::MAP{$path}) {
2144 root 1.340 return $map if !$load || $map->in_memory == cf::MAP_IN_MEMORY;
2145 root 1.157 }
2146    
2147 root 1.339 $MAP_PREFETCH{$path} |= $load;
2148    
2149 root 1.183 $MAP_PREFETCHER ||= cf::async {
2150 root 1.374 $Coro::current->{desc} = "map prefetcher";
2151    
2152 root 1.183 while (%MAP_PREFETCH) {
2153 root 1.339 while (my ($k, $v) = each %MAP_PREFETCH) {
2154     if (my $map = find $k) {
2155     $map->load if $v;
2156 root 1.308 }
2157 root 1.183
2158 root 1.339 delete $MAP_PREFETCH{$k};
2159 root 1.183 }
2160     }
2161     undef $MAP_PREFETCHER;
2162     };
2163 root 1.189 $MAP_PREFETCHER->prio (6);
2164 root 1.157
2165     ()
2166     }
2167    
2168 root 1.110 sub save {
2169     my ($self) = @_;
2170    
2171 root 1.345 my $lock = cf::lock_acquire "map_data:$self->{path}";
2172 root 1.137
2173 root 1.110 $self->{last_save} = $cf::RUNTIME;
2174    
2175     return unless $self->dirty;
2176    
2177 root 1.166 my $save = $self->save_path; utf8::encode $save;
2178     my $uniq = $self->uniq_path; utf8::encode $uniq;
2179 root 1.117
2180 root 1.110 $self->{load_path} = $save;
2181    
2182     return if $self->{deny_save};
2183    
2184 root 1.132 local $self->{last_access} = $self->last_access;#d#
2185    
2186 root 1.143 cf::async {
2187 root 1.374 $Coro::current->{desc} = "map player save";
2188 root 1.143 $_->contr->save for $self->players;
2189     };
2190    
2191 root 1.110 if ($uniq) {
2192 root 1.166 $self->_save_objects ($save, cf::IO_HEADER | cf::IO_OBJECTS);
2193     $self->_save_objects ($uniq, cf::IO_UNIQUES);
2194 root 1.110 } else {
2195 root 1.166 $self->_save_objects ($save, cf::IO_HEADER | cf::IO_OBJECTS | cf::IO_UNIQUES);
2196 root 1.110 }
2197     }
2198    
2199     sub swap_out {
2200     my ($self) = @_;
2201    
2202 root 1.130 # save first because save cedes
2203     $self->save;
2204    
2205 root 1.345 my $lock = cf::lock_acquire "map_data:$self->{path}";
2206 root 1.137
2207 root 1.110 return if $self->players;
2208     return if $self->in_memory != cf::MAP_IN_MEMORY;
2209     return if $self->{deny_save};
2210    
2211 root 1.359 $self->in_memory (cf::MAP_SWAPPED);
2212    
2213 root 1.358 $self->deactivate;
2214 root 1.359 $_->clear_links_to ($self) for values %cf::MAP;
2215 root 1.110 $self->clear;
2216     }
2217    
2218 root 1.112 sub reset_at {
2219     my ($self) = @_;
2220 root 1.110
2221     # TODO: safety, remove and allow resettable per-player maps
2222 root 1.114 return 1e99 if $self->{deny_reset};
2223 root 1.110
2224 root 1.112 my $time = $self->fixed_resettime ? $self->{instantiate_time} : $self->last_access;
2225 root 1.114 my $to = List::Util::min $MAX_RESET, $self->reset_timeout || $DEFAULT_RESET;
2226 root 1.110
2227 root 1.112 $time + $to
2228     }
2229    
2230     sub should_reset {
2231     my ($self) = @_;
2232    
2233     $self->reset_at <= $cf::RUNTIME
2234 root 1.111 }
2235    
2236 root 1.110 sub reset {
2237     my ($self) = @_;
2238    
2239 root 1.167 my $lock = cf::lock_acquire "map_data:$self->{path}";
2240 root 1.137
2241 root 1.110 return if $self->players;
2242    
2243 root 1.274 warn "resetting map ", $self->path;
2244 root 1.110
2245 root 1.210 $self->in_memory (cf::MAP_SWAPPED);
2246    
2247     # need to save uniques path
2248     unless ($self->{deny_save}) {
2249     my $uniq = $self->uniq_path; utf8::encode $uniq;
2250    
2251     $self->_save_objects ($uniq, cf::IO_UNIQUES)
2252     if $uniq;
2253     }
2254    
2255 root 1.111 delete $cf::MAP{$self->path};
2256 root 1.110
2257 root 1.358 $self->deactivate;
2258 root 1.359 $_->clear_links_to ($self) for values %cf::MAP;
2259 root 1.167 $self->clear;
2260    
2261 root 1.166 $self->unlink_save;
2262 root 1.111 $self->destroy;
2263 root 1.110 }
2264    
2265 root 1.114 my $nuke_counter = "aaaa";
2266    
2267     sub nuke {
2268     my ($self) = @_;
2269    
2270 root 1.349 {
2271     my $lock = cf::lock_acquire "map_data:$self->{path}";
2272    
2273     delete $cf::MAP{$self->path};
2274 root 1.174
2275 root 1.351 $self->unlink_save;
2276    
2277 root 1.349 bless $self, "cf::map";
2278     delete $self->{deny_reset};
2279     $self->{deny_save} = 1;
2280     $self->reset_timeout (1);
2281     $self->path ($self->{path} = "{nuke}/" . ($nuke_counter++));
2282 root 1.174
2283 root 1.349 $cf::MAP{$self->path} = $self;
2284     }
2285 root 1.174
2286 root 1.114 $self->reset; # polite request, might not happen
2287     }
2288    
2289 root 1.276 =item $maps = cf::map::tmp_maps
2290    
2291     Returns an arrayref with all map paths of currently instantiated and saved
2292 root 1.277 maps. May block.
2293 root 1.276
2294     =cut
2295    
2296     sub tmp_maps() {
2297     [
2298     map {
2299     utf8::decode $_;
2300 root 1.277 /\.map$/
2301 root 1.276 ? normalise $_
2302     : ()
2303     } @{ aio_readdir $TMPDIR or [] }
2304     ]
2305     }
2306    
2307 root 1.277 =item $maps = cf::map::random_maps
2308    
2309     Returns an arrayref with all map paths of currently instantiated and saved
2310     random maps. May block.
2311    
2312     =cut
2313    
2314     sub random_maps() {
2315     [
2316     map {
2317     utf8::decode $_;
2318     /\.map$/
2319     ? normalise "?random/$_"
2320     : ()
2321     } @{ aio_readdir $RANDOMDIR or [] }
2322     ]
2323     }
2324    
2325 root 1.158 =item cf::map::unique_maps
2326    
2327 root 1.166 Returns an arrayref of paths of all shared maps that have
2328 root 1.158 instantiated unique items. May block.
2329    
2330     =cut
2331    
2332     sub unique_maps() {
2333 root 1.276 [
2334     map {
2335     utf8::decode $_;
2336 root 1.277 /\.map$/
2337 root 1.276 ? normalise $_
2338     : ()
2339     } @{ aio_readdir $UNIQUEDIR or [] }
2340     ]
2341 root 1.158 }
2342    
2343 root 1.155 package cf;
2344    
2345     =back
2346    
2347     =head3 cf::object
2348    
2349     =cut
2350    
2351     package cf::object;
2352    
2353     =over 4
2354    
2355     =item $ob->inv_recursive
2356 root 1.110
2357 root 1.155 Returns the inventory of the object _and_ their inventories, recursively.
2358 root 1.110
2359 root 1.155 =cut
2360 root 1.144
2361 root 1.155 sub inv_recursive_;
2362     sub inv_recursive_ {
2363     map { $_, inv_recursive_ $_->inv } @_
2364     }
2365 root 1.110
2366 root 1.155 sub inv_recursive {
2367     inv_recursive_ inv $_[0]
2368 root 1.110 }
2369    
2370 root 1.356 =item $ref = $ob->ref
2371    
2372     creates and returns a persistent reference to an objetc that can be stored as a string.
2373    
2374     =item $ob = cf::object::deref ($refstring)
2375    
2376     returns the objetc referenced by refstring. may return undef when it cnanot find the object,
2377     even if the object actually exists. May block.
2378    
2379     =cut
2380    
2381     sub deref {
2382     my ($ref) = @_;
2383    
2384 root 1.377 if ($ref =~ m{^player\/(<1\.[0-9a-f]+>)/(.*)$}) {
2385 root 1.356 my ($uuid, $name) = ($1, $2);
2386     my $pl = $cf::PLAYER_LOADING{$name} || cf::player::find $name
2387     or return;
2388     $pl->ob->uuid eq $uuid
2389     or return;
2390    
2391     $pl->ob
2392     } else {
2393     warn "$ref: cannot resolve object reference\n";
2394     undef
2395     }
2396     }
2397    
2398 root 1.110 package cf;
2399    
2400     =back
2401    
2402 root 1.95 =head3 cf::object::player
2403    
2404     =over 4
2405    
2406 root 1.79 =item $player_object->reply ($npc, $msg[, $flags])
2407 root 1.28
2408     Sends a message to the player, as if the npc C<$npc> replied. C<$npc>
2409     can be C<undef>. Does the right thing when the player is currently in a
2410     dialogue with the given NPC character.
2411    
2412     =cut
2413    
2414 root 1.22 # rough implementation of a future "reply" method that works
2415     # with dialog boxes.
2416 root 1.95 #TODO: the first argument must go, split into a $npc->reply_to ( method
2417 root 1.23 sub cf::object::player::reply($$$;$) {
2418     my ($self, $npc, $msg, $flags) = @_;
2419    
2420     $flags = cf::NDI_BROWN | cf::NDI_UNIQUE unless @_ >= 4;
2421 root 1.22
2422 root 1.24 if ($self->{record_replies}) {
2423     push @{ $self->{record_replies} }, [$npc, $msg, $flags];
2424 elmex 1.282
2425 root 1.24 } else {
2426 elmex 1.282 my $pl = $self->contr;
2427    
2428     if ($pl->{npc_dialog} && $pl->{npc_dialog}->{id}) {
2429 root 1.316 my $dialog = $pl->{npc_dialog};
2430     $dialog->{pl}->ext_msg ($dialog->{id}, update => msg => $dialog->{pl}->expand_cfpod ($msg));
2431 elmex 1.282
2432     } else {
2433     $msg = $npc->name . " says: $msg" if $npc;
2434     $self->message ($msg, $flags);
2435     }
2436 root 1.24 }
2437 root 1.22 }
2438    
2439 root 1.329 =item $object->send_msg ($channel, $msg, $color, [extra...])
2440    
2441     =cut
2442    
2443     sub cf::object::send_msg {
2444     my $pl = shift->contr
2445     or return;
2446     $pl->send_msg (@_);
2447     }
2448    
2449 root 1.79 =item $player_object->may ("access")
2450    
2451     Returns wether the given player is authorized to access resource "access"
2452     (e.g. "command_wizcast").
2453    
2454     =cut
2455    
2456     sub cf::object::player::may {
2457     my ($self, $access) = @_;
2458    
2459     $self->flag (cf::FLAG_WIZ) ||
2460     (ref $cf::CFG{"may_$access"}
2461     ? scalar grep $self->name eq $_, @{$cf::CFG{"may_$access"}}
2462     : $cf::CFG{"may_$access"})
2463     }
2464 root 1.70
2465 root 1.115 =item $player_object->enter_link
2466    
2467     Freezes the player and moves him/her to a special map (C<{link}>).
2468    
2469 root 1.166 The player should be reasonably safe there for short amounts of time. You
2470 root 1.115 I<MUST> call C<leave_link> as soon as possible, though.
2471    
2472 root 1.166 Will never block.
2473    
2474 root 1.115 =item $player_object->leave_link ($map, $x, $y)
2475    
2476 root 1.166 Moves the player out of the special C<{link}> map onto the specified
2477     map. If the map is not valid (or omitted), the player will be moved back
2478     to the location he/she was before the call to C<enter_link>, or, if that
2479     fails, to the emergency map position.
2480 root 1.115
2481     Might block.
2482    
2483     =cut
2484    
2485 root 1.166 sub link_map {
2486     unless ($LINK_MAP) {
2487     $LINK_MAP = cf::map::find "{link}"
2488 root 1.196 or cf::cleanup "FATAL: unable to provide {link} map, exiting.";
2489 root 1.166 $LINK_MAP->load;
2490     }
2491    
2492     $LINK_MAP
2493     }
2494    
2495 root 1.110 sub cf::object::player::enter_link {
2496     my ($self) = @_;
2497    
2498 root 1.259 $self->deactivate_recursive;
2499 root 1.258
2500 root 1.166 return if UNIVERSAL::isa $self->map, "ext::map_link";
2501 root 1.110
2502 root 1.120 $self->{_link_pos} ||= [$self->map->{path}, $self->x, $self->y]
2503 root 1.270 if $self->map && $self->map->{path} ne "{link}";
2504 root 1.110
2505 root 1.194 $self->enter_map ($LINK_MAP || link_map, 10, 10);
2506 root 1.110 }
2507    
2508     sub cf::object::player::leave_link {
2509     my ($self, $map, $x, $y) = @_;
2510    
2511 root 1.270 return unless $self->contr->active;
2512    
2513 root 1.110 my $link_pos = delete $self->{_link_pos};
2514    
2515     unless ($map) {
2516     # restore original map position
2517     ($map, $x, $y) = @{ $link_pos || [] };
2518 root 1.133 $map = cf::map::find $map;
2519 root 1.110
2520     unless ($map) {
2521     ($map, $x, $y) = @$EMERGENCY_POSITION;
2522 root 1.133 $map = cf::map::find $map
2523 root 1.110 or die "FATAL: cannot load emergency map\n";
2524     }
2525     }
2526    
2527     ($x, $y) = (-1, -1)
2528     unless (defined $x) && (defined $y);
2529    
2530     # use -1 or undef as default coordinates, not 0, 0
2531     ($x, $y) = ($map->enter_x, $map->enter_y)
2532     if $x <=0 && $y <= 0;
2533    
2534     $map->load;
2535 root 1.333 $map->load_neighbours;
2536 root 1.110
2537 root 1.143 return unless $self->contr->active;
2538 root 1.110 $self->activate_recursive;
2539 root 1.215
2540     local $self->{_prev_pos} = $link_pos; # ugly hack for rent.ext
2541 root 1.110 $self->enter_map ($map, $x, $y);
2542     }
2543    
2544 root 1.306 =item $player_object->goto ($path, $x, $y[, $check->($map)[, $done->()]])
2545 root 1.268
2546     Moves the player to the given map-path and coordinates by first freezing
2547     her, loading and preparing them map, calling the provided $check callback
2548     that has to return the map if sucecssful, and then unfreezes the player on
2549 root 1.306 the new (success) or old (failed) map position. In either case, $done will
2550     be called at the end of this process.
2551 root 1.110
2552     =cut
2553    
2554 root 1.270 our $GOTOGEN;
2555    
2556 root 1.136 sub cf::object::player::goto {
2557 root 1.306 my ($self, $path, $x, $y, $check, $done) = @_;
2558 root 1.268
2559 root 1.270 # do generation counting so two concurrent goto's will be executed in-order
2560     my $gen = $self->{_goto_generation} = ++$GOTOGEN;
2561    
2562 root 1.110 $self->enter_link;
2563    
2564 root 1.140 (async {
2565 root 1.374 $Coro::current->{desc} = "player::goto $path $x $y";
2566    
2567 root 1.365 # *tag paths override both path and x|y
2568     if ($path =~ /^\*(.*)$/) {
2569     if (my @obs = grep $_->map, ext::map_tags::find $1) {
2570     my $ob = $obs[rand @obs];
2571 root 1.366
2572 root 1.367 # see if we actually can go there
2573 root 1.368 if (@obs = grep !$self->blocked ($_->map, $_->x, $_->y), $ob, $ob->tail) {
2574     $ob = $obs[rand @obs];
2575 root 1.369 } else {
2576     $self->message ("Wow, it's pretty crowded in there.", cf::NDI_UNIQUE | cf::NDI_RED);
2577 root 1.368 }
2578 root 1.369 # else put us there anyways for now #d#
2579 root 1.366
2580 root 1.365 ($path, $x, $y) = ($ob->map, $ob->x, $ob->y);
2581 root 1.369 } else {
2582     ($path, $x, $y) = (undef, undef, undef);
2583 root 1.365 }
2584     }
2585    
2586 root 1.197 my $map = eval {
2587 root 1.369 my $map = defined $path ? cf::map::find $path : undef;
2588 root 1.268
2589     if ($map) {
2590     $map = $map->customise_for ($self);
2591     $map = $check->($map) if $check && $map;
2592     } else {
2593 root 1.369 $self->message ("The exit to '$path' is closed.", cf::NDI_UNIQUE | cf::NDI_RED);
2594 root 1.268 }
2595    
2596 root 1.197 $map
2597 root 1.268 };
2598    
2599     if ($@) {
2600     $self->message ("Something went wrong within the server, please report this incident!", cf::NDI_UNIQUE | cf::NDI_RED);
2601     LOG llevError | logBacktrace, Carp::longmess $@;
2602     }
2603 root 1.115
2604 root 1.270 if ($gen == $self->{_goto_generation}) {
2605     delete $self->{_goto_generation};
2606     $self->leave_link ($map, $x, $y);
2607     }
2608 root 1.306
2609     $done->() if $done;
2610 root 1.110 })->prio (1);
2611     }
2612    
2613     =item $player_object->enter_exit ($exit_object)
2614    
2615     =cut
2616    
2617     sub parse_random_map_params {
2618     my ($spec) = @_;
2619    
2620     my $rmp = { # defaults
2621 root 1.181 xsize => (cf::rndm 15, 40),
2622     ysize => (cf::rndm 15, 40),
2623     symmetry => (cf::rndm 1, cf::SYMMETRY_XY),
2624 root 1.182 #layout => string,
2625 root 1.110 };
2626    
2627     for (split /\n/, $spec) {
2628     my ($k, $v) = split /\s+/, $_, 2;
2629    
2630     $rmp->{lc $k} = $v if (length $k) && (length $v);
2631     }
2632    
2633     $rmp
2634     }
2635    
2636     sub prepare_random_map {
2637     my ($exit) = @_;
2638    
2639 root 1.179 my $guard = cf::lock_acquire "exit_prepare:$exit";
2640    
2641 root 1.110 # all this does is basically replace the /! path by
2642     # a new random map path (?random/...) with a seed
2643     # that depends on the exit object
2644    
2645     my $rmp = parse_random_map_params $exit->msg;
2646    
2647     if ($exit->map) {
2648 root 1.198 $rmp->{region} = $exit->region->name;
2649 root 1.110 $rmp->{origin_map} = $exit->map->path;
2650     $rmp->{origin_x} = $exit->x;
2651     $rmp->{origin_y} = $exit->y;
2652     }
2653    
2654     $rmp->{random_seed} ||= $exit->random_seed;
2655    
2656 root 1.398 my $data = cf::encode_json $rmp;
2657 root 1.110 my $md5 = Digest::MD5::md5_hex $data;
2658 root 1.253 my $meta = "$RANDOMDIR/$md5.meta";
2659 root 1.110
2660 root 1.177 if (my $fh = aio_open "$meta~", O_WRONLY | O_CREAT, 0666) {
2661 root 1.110 aio_write $fh, 0, (length $data), $data, 0;
2662 root 1.177 undef $fh;
2663     aio_rename "$meta~", $meta;
2664 root 1.110
2665     $exit->slaying ("?random/$md5");
2666     $exit->msg (undef);
2667     }
2668     }
2669    
2670     sub cf::object::player::enter_exit {
2671     my ($self, $exit) = @_;
2672    
2673     return unless $self->type == cf::PLAYER;
2674    
2675 root 1.195 if ($exit->slaying eq "/!") {
2676     #TODO: this should de-fi-ni-te-ly not be a sync-job
2677 root 1.233 # the problem is that $exit might not survive long enough
2678     # so it needs to be done right now, right here
2679 root 1.195 cf::sync_job { prepare_random_map $exit };
2680     }
2681    
2682     my $slaying = cf::map::normalise $exit->slaying, $exit->map && $exit->map->path;
2683     my $hp = $exit->stats->hp;
2684     my $sp = $exit->stats->sp;
2685    
2686 root 1.110 $self->enter_link;
2687    
2688 root 1.296 # if exit is damned, update players death & WoR home-position
2689     $self->contr->savebed ($slaying, $hp, $sp)
2690     if $exit->flag (FLAG_DAMNED);
2691    
2692 root 1.140 (async {
2693 root 1.374 $Coro::current->{desc} = "enter_exit $slaying $hp $sp";
2694    
2695 root 1.133 $self->deactivate_recursive; # just to be sure
2696 root 1.110 unless (eval {
2697 root 1.195 $self->goto ($slaying, $hp, $sp);
2698 root 1.110
2699     1;
2700     }) {
2701     $self->message ("Something went wrong deep within the crossfire server. "
2702 root 1.233 . "I'll try to bring you back to the map you were before. "
2703     . "Please report this to the dungeon master!",
2704     cf::NDI_UNIQUE | cf::NDI_RED);
2705 root 1.110
2706     warn "ERROR in enter_exit: $@";
2707     $self->leave_link;
2708     }
2709     })->prio (1);
2710     }
2711    
2712 root 1.95 =head3 cf::client
2713    
2714     =over 4
2715    
2716     =item $client->send_drawinfo ($text, $flags)
2717    
2718     Sends a drawinfo packet to the client. Circumvents output buffering so
2719     should not be used under normal circumstances.
2720    
2721 root 1.70 =cut
2722    
2723 root 1.95 sub cf::client::send_drawinfo {
2724     my ($self, $text, $flags) = @_;
2725    
2726     utf8::encode $text;
2727 root 1.225 $self->send_packet (sprintf "drawinfo %d %s", $flags || cf::NDI_BLACK, $text);
2728 root 1.95 }
2729    
2730 root 1.311 =item $client->send_msg ($channel, $msg, $color, [extra...])
2731 root 1.283
2732     Send a drawinfo or msg packet to the client, formatting the msg for the
2733     client if neccessary. C<$type> should be a string identifying the type of
2734     the message, with C<log> being the default. If C<$color> is negative, suppress
2735     the message unless the client supports the msg packet.
2736    
2737     =cut
2738    
2739 root 1.391 # non-persistent channels (usually the info channel)
2740 root 1.350 our %CHANNEL = (
2741     "c/identify" => {
2742 root 1.375 id => "infobox",
2743 root 1.350 title => "Identify",
2744     reply => undef,
2745     tooltip => "Items recently identified",
2746     },
2747 root 1.352 "c/examine" => {
2748 root 1.375 id => "infobox",
2749 root 1.352 title => "Examine",
2750     reply => undef,
2751     tooltip => "Signs and other items you examined",
2752     },
2753 root 1.389 "c/book" => {
2754     id => "infobox",
2755     title => "Book",
2756     reply => undef,
2757     tooltip => "The contents of a note or book",
2758     },
2759 root 1.375 "c/lookat" => {
2760     id => "infobox",
2761     title => "Look",
2762     reply => undef,
2763     tooltip => "What you saw there",
2764     },
2765 root 1.390 "c/who" => {
2766     id => "infobox",
2767     title => "Players",
2768     reply => undef,
2769     tooltip => "Shows players who are currently online",
2770     },
2771     "c/body" => {
2772     id => "infobox",
2773     title => "Body Parts",
2774     reply => undef,
2775     tooltip => "Shows which body parts you posess and are available",
2776     },
2777     "c/uptime" => {
2778     id => "infobox",
2779     title => "Uptime",
2780     reply => undef,
2781 root 1.391 tooltip => "How long the server has been running since last restart",
2782 root 1.390 },
2783     "c/mapinfo" => {
2784     id => "infobox",
2785     title => "Map Info",
2786     reply => undef,
2787     tooltip => "Information related to the maps",
2788     },
2789 root 1.350 );
2790    
2791 root 1.283 sub cf::client::send_msg {
2792 root 1.311 my ($self, $channel, $msg, $color, @extra) = @_;
2793 root 1.283
2794     $msg = $self->pl->expand_cfpod ($msg);
2795    
2796 root 1.323 $color &= cf::NDI_CLIENT_MASK; # just in case...
2797 root 1.311
2798 root 1.350 # check predefined channels, for the benefit of C
2799 root 1.375 if ($CHANNEL{$channel}) {
2800     $channel = $CHANNEL{$channel};
2801    
2802     $self->ext_msg (channel_info => $channel)
2803     if $self->can_msg;
2804    
2805     $channel = $channel->{id};
2806 root 1.350
2807 root 1.375 } elsif (ref $channel) {
2808 root 1.311 # send meta info to client, if not yet sent
2809     unless (exists $self->{channel}{$channel->{id}}) {
2810     $self->{channel}{$channel->{id}} = $channel;
2811 root 1.353 $self->ext_msg (channel_info => $channel)
2812     if $self->can_msg;
2813 root 1.311 }
2814    
2815     $channel = $channel->{id};
2816     }
2817    
2818 root 1.313 return unless @extra || length $msg;
2819    
2820 root 1.283 if ($self->can_msg) {
2821 root 1.323 # default colour, mask it out
2822     $color &= ~(cf::NDI_COLOR_MASK | cf::NDI_DEF)
2823     if $color & cf::NDI_DEF;
2824    
2825     $self->send_packet ("msg " . $self->{json_coder}->encode (
2826     [$color & cf::NDI_CLIENT_MASK, $channel, $msg, @extra]));
2827 root 1.283 } else {
2828 root 1.323 if ($color >= 0) {
2829     # replace some tags by gcfclient-compatible ones
2830     for ($msg) {
2831     1 while
2832     s/<b>([^<]*)<\/b>/[b]${1}[\/b]/
2833     || s/<i>([^<]*)<\/i>/[i]${1}[\/i]/
2834     || s/<u>([^<]*)<\/u>/[ul]${1}[\/ul]/
2835     || s/<tt>([^<]*)<\/tt>/[fixed]${1}[\/fixed]/
2836     || s/<fg name=\"([^"]+)\">([^<]*)<\/fg>/[color=$1]${2}[\/color]/;
2837     }
2838    
2839     $color &= cf::NDI_COLOR_MASK;
2840 root 1.283
2841 root 1.327 utf8::encode $msg;
2842    
2843 root 1.284 if (0 && $msg =~ /\[/) {
2844 root 1.331 # COMMAND/INFO
2845     $self->send_packet ("drawextinfo $color 10 8 $msg")
2846 root 1.283 } else {
2847 root 1.286 $msg =~ s/\[\/?(?:b|i|u|fixed|color)[^\]]*\]//g;
2848 root 1.283 $self->send_packet ("drawinfo $color $msg")
2849     }
2850     }
2851     }
2852     }
2853    
2854 root 1.316 =item $client->ext_msg ($type, @msg)
2855 root 1.232
2856 root 1.287 Sends an ext event to the client.
2857 root 1.232
2858     =cut
2859    
2860 root 1.316 sub cf::client::ext_msg($$@) {
2861     my ($self, $type, @msg) = @_;
2862 root 1.232
2863 root 1.343 if ($self->extcmd == 2) {
2864 root 1.316 $self->send_packet ("ext " . $self->{json_coder}->encode ([$type, @msg]));
2865 root 1.343 } elsif ($self->extcmd == 1) { # TODO: remove
2866 root 1.316 push @msg, msgtype => "event_$type";
2867     $self->send_packet ("ext " . $self->{json_coder}->encode ({@msg}));
2868     }
2869 root 1.232 }
2870 root 1.95
2871 root 1.336 =item $client->ext_reply ($msgid, @msg)
2872    
2873     Sends an ext reply to the client.
2874    
2875     =cut
2876    
2877     sub cf::client::ext_reply($$@) {
2878     my ($self, $id, @msg) = @_;
2879    
2880     if ($self->extcmd == 2) {
2881     $self->send_packet ("ext " . $self->{json_coder}->encode (["reply-$id", @msg]));
2882 root 1.343 } elsif ($self->extcmd == 1) {
2883 root 1.336 #TODO: version 1, remove
2884     unshift @msg, msgtype => "reply", msgid => $id;
2885     $self->send_packet ("ext " . $self->{json_coder}->encode ({@msg}));
2886     }
2887     }
2888    
2889 root 1.95 =item $success = $client->query ($flags, "text", \&cb)
2890    
2891     Queues a query to the client, calling the given callback with
2892     the reply text on a reply. flags can be C<cf::CS_QUERY_YESNO>,
2893     C<cf::CS_QUERY_SINGLECHAR> or C<cf::CS_QUERY_HIDEINPUT> or C<0>.
2894    
2895 root 1.287 Queries can fail, so check the return code. Or don't, as queries will
2896     become reliable at some point in the future.
2897 root 1.95
2898     =cut
2899    
2900     sub cf::client::query {
2901     my ($self, $flags, $text, $cb) = @_;
2902    
2903     return unless $self->state == ST_PLAYING
2904     || $self->state == ST_SETUP
2905     || $self->state == ST_CUSTOM;
2906    
2907     $self->state (ST_CUSTOM);
2908    
2909     utf8::encode $text;
2910     push @{ $self->{query_queue} }, [(sprintf "query %d %s", $flags, $text), $cb];
2911    
2912     $self->send_packet ($self->{query_queue}[0][0])
2913     if @{ $self->{query_queue} } == 1;
2914 root 1.287
2915     1
2916 root 1.95 }
2917    
2918     cf::client->attach (
2919 root 1.290 on_connect => sub {
2920     my ($ns) = @_;
2921    
2922     $ns->{json_coder} = JSON::XS->new->utf8->max_size (1e6)->convert_blessed;
2923     },
2924 root 1.95 on_reply => sub {
2925     my ($ns, $msg) = @_;
2926    
2927     # this weird shuffling is so that direct followup queries
2928     # get handled first
2929 root 1.128 my $queue = delete $ns->{query_queue}
2930 root 1.129 or return; # be conservative, not sure how that can happen, but we saw a crash here
2931 root 1.95
2932     (shift @$queue)->[1]->($msg);
2933 root 1.202 return unless $ns->valid; # temporary(?) workaround for callback destroying socket
2934 root 1.95
2935     push @{ $ns->{query_queue} }, @$queue;
2936    
2937     if (@{ $ns->{query_queue} } == @$queue) {
2938     if (@$queue) {
2939     $ns->send_packet ($ns->{query_queue}[0][0]);
2940     } else {
2941 root 1.98 $ns->state (ST_PLAYING) if $ns->state == ST_CUSTOM;
2942 root 1.95 }
2943     }
2944     },
2945 root 1.287 on_exticmd => sub {
2946     my ($ns, $buf) = @_;
2947    
2948 root 1.290 my $msg = eval { $ns->{json_coder}->decode ($buf) };
2949 root 1.287
2950     if (ref $msg) {
2951 root 1.316 my ($type, $reply, @payload) =
2952     "ARRAY" eq ref $msg
2953     ? @$msg
2954     : ($msg->{msgtype}, $msg->{msgid}, %$msg); # TODO: version 1, remove
2955    
2956 root 1.338 my @reply;
2957    
2958 root 1.316 if (my $cb = $EXTICMD{$type}) {
2959 root 1.338 @reply = $cb->($ns, @payload);
2960     }
2961    
2962     $ns->ext_reply ($reply, @reply)
2963     if $reply;
2964 root 1.316
2965 root 1.287 } else {
2966     warn "client " . ($ns->pl ? $ns->pl->ob->name : $ns->host) . " sent unparseable exti message: <$buf>\n";
2967     }
2968    
2969     cf::override;
2970     },
2971 root 1.95 );
2972    
2973 root 1.140 =item $client->async (\&cb)
2974 root 1.96
2975     Create a new coroutine, running the specified callback. The coroutine will
2976     be automatically cancelled when the client gets destroyed (e.g. on logout,
2977     or loss of connection).
2978    
2979     =cut
2980    
2981 root 1.140 sub cf::client::async {
2982 root 1.96 my ($self, $cb) = @_;
2983    
2984 root 1.140 my $coro = &Coro::async ($cb);
2985 root 1.103
2986     $coro->on_destroy (sub {
2987 root 1.96 delete $self->{_coro}{$coro+0};
2988 root 1.103 });
2989 root 1.96
2990     $self->{_coro}{$coro+0} = $coro;
2991 root 1.103
2992     $coro
2993 root 1.96 }
2994    
2995     cf::client->attach (
2996     on_destroy => sub {
2997     my ($ns) = @_;
2998    
2999 root 1.97 $_->cancel for values %{ (delete $ns->{_coro}) || {} };
3000 root 1.96 },
3001     );
3002    
3003 root 1.95 =back
3004    
3005 root 1.70
3006     =head2 SAFE SCRIPTING
3007    
3008     Functions that provide a safe environment to compile and execute
3009     snippets of perl code without them endangering the safety of the server
3010     itself. Looping constructs, I/O operators and other built-in functionality
3011     is not available in the safe scripting environment, and the number of
3012 root 1.79 functions and methods that can be called is greatly reduced.
3013 root 1.70
3014     =cut
3015 root 1.23
3016 root 1.42 our $safe = new Safe "safe";
3017 root 1.23 our $safe_hole = new Safe::Hole;
3018    
3019     $SIG{FPE} = 'IGNORE';
3020    
3021 root 1.328 $safe->permit_only (Opcode::opset qw(
3022     :base_core :base_mem :base_orig :base_math
3023     grepstart grepwhile mapstart mapwhile
3024     sort time
3025     ));
3026 root 1.23
3027 root 1.25 # here we export the classes and methods available to script code
3028    
3029 root 1.70 =pod
3030    
3031 root 1.228 The following functions and methods are available within a safe environment:
3032 root 1.70
3033 root 1.297 cf::object
3034 root 1.383 contr pay_amount pay_player map x y force_find force_add destroy
3035 elmex 1.341 insert remove name archname title slaying race decrease_ob_nr
3036 root 1.297
3037     cf::object::player
3038     player
3039    
3040     cf::player
3041     peaceful
3042    
3043     cf::map
3044     trigger
3045 root 1.70
3046     =cut
3047    
3048 root 1.25 for (
3049 root 1.297 ["cf::object" => qw(contr pay_amount pay_player map force_find force_add x y
3050 elmex 1.341 insert remove inv name archname title slaying race
3051 root 1.383 decrease_ob_nr destroy)],
3052 root 1.25 ["cf::object::player" => qw(player)],
3053     ["cf::player" => qw(peaceful)],
3054 elmex 1.91 ["cf::map" => qw(trigger)],
3055 root 1.25 ) {
3056     no strict 'refs';
3057     my ($pkg, @funs) = @$_;
3058 root 1.41 *{"safe::$pkg\::$_"} = $safe_hole->wrap (\&{"$pkg\::$_"})
3059 root 1.25 for @funs;
3060     }
3061 root 1.23
3062 root 1.70 =over 4
3063    
3064     =item @retval = safe_eval $code, [var => value, ...]
3065    
3066     Compiled and executes the given perl code snippet. additional var/value
3067     pairs result in temporary local (my) scalar variables of the given name
3068     that are available in the code snippet. Example:
3069    
3070     my $five = safe_eval '$first + $second', first => 1, second => 4;
3071    
3072     =cut
3073    
3074 root 1.23 sub safe_eval($;@) {
3075     my ($code, %vars) = @_;
3076    
3077     my $qcode = $code;
3078     $qcode =~ s/"/‟/g; # not allowed in #line filenames
3079     $qcode =~ s/\n/\\n/g;
3080    
3081     local $_;
3082 root 1.41 local @safe::cf::_safe_eval_args = values %vars;
3083 root 1.23
3084 root 1.42 my $eval =
3085 root 1.23 "do {\n"
3086     . "my (" . (join ",", map "\$$_", keys %vars) . ") = \@cf::_safe_eval_args;\n"
3087     . "#line 0 \"{$qcode}\"\n"
3088     . $code
3089     . "\n}"
3090 root 1.25 ;
3091    
3092     sub_generation_inc;
3093 root 1.42 my @res = wantarray ? $safe->reval ($eval) : scalar $safe->reval ($eval);
3094 root 1.25 sub_generation_inc;
3095    
3096 root 1.42 if ($@) {
3097     warn "$@";
3098     warn "while executing safe code '$code'\n";
3099     warn "with arguments " . (join " ", %vars) . "\n";
3100     }
3101    
3102 root 1.25 wantarray ? @res : $res[0]
3103 root 1.23 }
3104    
3105 root 1.69 =item cf::register_script_function $function => $cb
3106    
3107     Register a function that can be called from within map/npc scripts. The
3108     function should be reasonably secure and should be put into a package name
3109     like the extension.
3110    
3111     Example: register a function that gets called whenever a map script calls
3112     C<rent::overview>, as used by the C<rent> extension.
3113    
3114     cf::register_script_function "rent::overview" => sub {
3115     ...
3116     };
3117    
3118     =cut
3119    
3120 root 1.23 sub register_script_function {
3121     my ($fun, $cb) = @_;
3122    
3123     no strict 'refs';
3124 root 1.41 *{"safe::$fun"} = $safe_hole->wrap ($cb);
3125 root 1.23 }
3126    
3127 root 1.70 =back
3128    
3129 root 1.71 =cut
3130    
3131 root 1.23 #############################################################################
3132 root 1.203 # the server's init and main functions
3133    
3134 root 1.246 sub load_facedata($) {
3135     my ($path) = @_;
3136 root 1.223
3137 root 1.348 # HACK to clear player env face cache, we need some signal framework
3138     # for this (global event?)
3139     %ext::player_env::MUSIC_FACE_CACHE = ();
3140    
3141 root 1.344 my $enc = JSON::XS->new->utf8->canonical->relaxed;
3142 root 1.334
3143 root 1.229 warn "loading facedata from $path\n";
3144 root 1.223
3145 root 1.236 my $facedata;
3146     0 < aio_load $path, $facedata
3147 root 1.223 or die "$path: $!";
3148    
3149 root 1.237 $facedata = Coro::Storable::thaw $facedata;
3150 root 1.223
3151 root 1.236 $facedata->{version} == 2
3152 root 1.226 or cf::cleanup "$path: version mismatch, cannot proceed.";
3153    
3154 root 1.334 # patch in the exptable
3155     $facedata->{resource}{"res/exp_table"} = {
3156     type => FT_RSRC,
3157 root 1.337 data => $enc->encode ([map cf::level_to_min_exp $_, 1 .. cf::settings->max_level]),
3158 root 1.334 };
3159     cf::cede_to_tick;
3160    
3161 root 1.236 {
3162     my $faces = $facedata->{faceinfo};
3163    
3164     while (my ($face, $info) = each %$faces) {
3165     my $idx = (cf::face::find $face) || cf::face::alloc $face;
3166 root 1.405
3167 root 1.302 cf::face::set_visibility $idx, $info->{visibility};
3168     cf::face::set_magicmap $idx, $info->{magicmap};
3169 root 1.334 cf::face::set_data $idx, 0, $info->{data32}, Digest::MD5::md5 $info->{data32};
3170     cf::face::set_data $idx, 1, $info->{data64}, Digest::MD5::md5 $info->{data64};
3171 root 1.302
3172     cf::cede_to_tick;
3173 root 1.236 }
3174    
3175     while (my ($face, $info) = each %$faces) {
3176     next unless $info->{smooth};
3177 root 1.405
3178 root 1.236 my $idx = cf::face::find $face
3179     or next;
3180 root 1.405
3181 root 1.236 if (my $smooth = cf::face::find $info->{smooth}) {
3182 root 1.302 cf::face::set_smooth $idx, $smooth;
3183     cf::face::set_smoothlevel $idx, $info->{smoothlevel};
3184 root 1.236 } else {
3185     warn "smooth face '$info->{smooth}' not found for face '$face'";
3186     }
3187 root 1.302
3188     cf::cede_to_tick;
3189 root 1.236 }
3190 root 1.223 }
3191    
3192 root 1.236 {
3193     my $anims = $facedata->{animinfo};
3194    
3195     while (my ($anim, $info) = each %$anims) {
3196     cf::anim::set $anim, $info->{frames}, $info->{facings};
3197 root 1.302 cf::cede_to_tick;
3198 root 1.225 }
3199 root 1.236
3200     cf::anim::invalidate_all; # d'oh
3201 root 1.225 }
3202    
3203 root 1.302 {
3204     # TODO: for gcfclient pleasure, we should give resources
3205     # that gcfclient doesn't grok a >10000 face index.
3206     my $res = $facedata->{resource};
3207    
3208     while (my ($name, $info) = each %$res) {
3209 root 1.405 if (defined $info->{type}) {
3210     my $idx = (cf::face::find $name) || cf::face::alloc $name;
3211     my $data;
3212    
3213     if ($info->{type} & 1) {
3214     # prepend meta info
3215    
3216     my $meta = $enc->encode ({
3217     name => $name,
3218     %{ $info->{meta} || {} },
3219     });
3220 root 1.307
3221 root 1.405 $data = pack "(w/a*)*", $meta, $info->{data};
3222     } else {
3223     $data = $info->{data};
3224     }
3225 root 1.318
3226 root 1.405 cf::face::set_data $idx, 0, $data, Digest::MD5::md5 $data;
3227     cf::face::set_type $idx, $info->{type};
3228 root 1.337 } else {
3229 root 1.405 $RESOURCE{$name} = $info;
3230 root 1.307 }
3231 root 1.302
3232     cf::cede_to_tick;
3233     }
3234 root 1.406 }
3235    
3236     cf::global->invoke (EVENT_GLOBAL_RESOURCE_UPDATE);
3237 root 1.321
3238 root 1.406 1
3239     }
3240    
3241     cf::global->attach (on_resource_update => sub {
3242     if (my $soundconf = $RESOURCE{"res/sound.conf"}) {
3243     $soundconf = JSON::XS->new->utf8->relaxed->decode ($soundconf->{data});
3244    
3245     for (0 .. SOUND_CAST_SPELL_0 - 1) {
3246     my $sound = $soundconf->{compat}[$_]
3247     or next;
3248 root 1.321
3249 root 1.406 my $face = cf::face::find "sound/$sound->[1]";
3250     cf::sound::set $sound->[0] => $face;
3251     cf::sound::old_sound_index $_, $face; # gcfclient-compat
3252     }
3253 root 1.321
3254 root 1.406 while (my ($k, $v) = each %{$soundconf->{event}}) {
3255     my $face = cf::face::find "sound/$v";
3256     cf::sound::set $k => $face;
3257 root 1.321 }
3258 root 1.302 }
3259 root 1.406 });
3260 root 1.223
3261 root 1.318 register_exticmd fx_want => sub {
3262     my ($ns, $want) = @_;
3263    
3264     while (my ($k, $v) = each %$want) {
3265     $ns->fx_want ($k, $v);
3266     }
3267     };
3268    
3269 root 1.253 sub reload_regions {
3270 root 1.348 # HACK to clear player env face cache, we need some signal framework
3271     # for this (global event?)
3272     %ext::player_env::MUSIC_FACE_CACHE = ();
3273    
3274 root 1.253 load_resource_file "$MAPDIR/regions"
3275     or die "unable to load regions file\n";
3276 root 1.304
3277     for (cf::region::list) {
3278     $_->{match} = qr/$_->{match}/
3279     if exists $_->{match};
3280     }
3281 root 1.253 }
3282    
3283 root 1.246 sub reload_facedata {
3284 root 1.253 load_facedata "$DATADIR/facedata"
3285 root 1.246 or die "unable to load facedata\n";
3286     }
3287    
3288     sub reload_archetypes {
3289 root 1.253 load_resource_file "$DATADIR/archetypes"
3290 root 1.246 or die "unable to load archetypes\n";
3291 root 1.289 #d# NEED to laod twice to resolve forward references
3292     # this really needs to be done in an extra post-pass
3293     # (which needs to be synchronous, so solve it differently)
3294     load_resource_file "$DATADIR/archetypes"
3295     or die "unable to load archetypes\n";
3296 root 1.241 }
3297    
3298 root 1.246 sub reload_treasures {
3299 root 1.253 load_resource_file "$DATADIR/treasures"
3300 root 1.246 or die "unable to load treasurelists\n";
3301 root 1.241 }
3302    
3303 root 1.223 sub reload_resources {
3304 root 1.245 warn "reloading resource files...\n";
3305    
3306 root 1.246 reload_regions;
3307     reload_facedata;
3308 root 1.274 #reload_archetypes;#d#
3309 root 1.246 reload_archetypes;
3310     reload_treasures;
3311 root 1.245
3312     warn "finished reloading resource files\n";
3313 root 1.223 }
3314    
3315     sub init {
3316     reload_resources;
3317 root 1.203 }
3318 root 1.34
3319 root 1.345 sub reload_config {
3320 root 1.253 open my $fh, "<:utf8", "$CONFDIR/config"
3321 root 1.72 or return;
3322    
3323     local $/;
3324 root 1.408 *CFG = YAML::Load <$fh>;
3325 root 1.131
3326     $EMERGENCY_POSITION = $CFG{emergency_position} || ["/world/world_105_115", 5, 37];
3327    
3328 root 1.139 $cf::map::MAX_RESET = $CFG{map_max_reset} if exists $CFG{map_max_reset};
3329     $cf::map::DEFAULT_RESET = $CFG{map_default_reset} if exists $CFG{map_default_reset};
3330    
3331 root 1.131 if (exists $CFG{mlockall}) {
3332     eval {
3333 root 1.147 $CFG{mlockall} ? eval "mlockall()" : eval "munlockall()"
3334 root 1.131 and die "WARNING: m(un)lockall failed: $!\n";
3335     };
3336     warn $@ if $@;
3337     }
3338 root 1.72 }
3339    
3340 root 1.39 sub main {
3341 root 1.108 # we must not ever block the main coroutine
3342     local $Coro::idle = sub {
3343 root 1.115 Carp::cluck "FATAL: Coro::idle was called, major BUG, use cf::sync_job!\n";#d#
3344 root 1.175 (async {
3345 root 1.374 $Coro::current->{desc} = "IDLE BUG HANDLER";
3346 root 1.396 EV::loop EV::LOOP_ONESHOT;
3347 root 1.175 })->prio (Coro::PRIO_MAX);
3348 root 1.108 };
3349    
3350 root 1.345 reload_config;
3351 root 1.210 db_init;
3352 root 1.61 load_extensions;
3353 root 1.183
3354     $TICK_WATCHER->start;
3355 root 1.397 $Coro::current->prio (Coro::PRIO_MAX); # give the main loop max. priority
3356 root 1.396 EV::loop;
3357 root 1.34 }
3358    
3359     #############################################################################
3360 root 1.155 # initialisation and cleanup
3361    
3362     # install some emergency cleanup handlers
3363     BEGIN {
3364 root 1.396 our %SIGWATCHER = ();
3365 root 1.155 for my $signal (qw(INT HUP TERM)) {
3366 root 1.396 $SIGWATCHER{$signal} = EV::signal $signal, sub {
3367     cf::cleanup "SIG$signal";
3368     };
3369 root 1.155 }
3370     }
3371    
3372 root 1.281 sub write_runtime {
3373     my $runtime = "$LOCALDIR/runtime";
3374    
3375     # first touch the runtime file to show we are still running:
3376     # the fsync below can take a very very long time.
3377    
3378     IO::AIO::aio_utime $runtime, undef, undef;
3379    
3380     my $guard = cf::lock_acquire "write_runtime";
3381    
3382     my $fh = aio_open "$runtime~", O_WRONLY | O_CREAT, 0644
3383     or return;
3384    
3385     my $value = $cf::RUNTIME + 90 + 10;
3386     # 10 is the runtime save interval, for a monotonic clock
3387     # 60 allows for the watchdog to kill the server.
3388    
3389     (aio_write $fh, 0, (length $value), $value, 0) <= 0
3390     and return;
3391    
3392     # always fsync - this file is important
3393     aio_fsync $fh
3394     and return;
3395    
3396     # touch it again to show we are up-to-date
3397     aio_utime $fh, undef, undef;
3398    
3399     close $fh
3400     or return;
3401    
3402     aio_rename "$runtime~", $runtime
3403     and return;
3404    
3405     warn "runtime file written.\n";
3406    
3407     1
3408     }
3409    
3410 root 1.156 sub emergency_save() {
3411 root 1.155 my $freeze_guard = cf::freeze_mainloop;
3412    
3413     warn "enter emergency perl save\n";
3414    
3415     cf::sync_job {
3416     # use a peculiar iteration method to avoid tripping on perl
3417     # refcount bugs in for. also avoids problems with players
3418 root 1.167 # and maps saved/destroyed asynchronously.
3419 root 1.155 warn "begin emergency player save\n";
3420     for my $login (keys %cf::PLAYER) {
3421     my $pl = $cf::PLAYER{$login} or next;
3422     $pl->valid or next;
3423 root 1.382 delete $pl->{unclean_save}; # not strictly necessary, but cannot hurt
3424 root 1.155 $pl->save;
3425     }
3426     warn "end emergency player save\n";
3427    
3428     warn "begin emergency map save\n";
3429     for my $path (keys %cf::MAP) {
3430     my $map = $cf::MAP{$path} or next;
3431     $map->valid or next;
3432     $map->save;
3433     }
3434     warn "end emergency map save\n";
3435 root 1.208
3436     warn "begin emergency database checkpoint\n";
3437     BDB::db_env_txn_checkpoint $DB_ENV;
3438     warn "end emergency database checkpoint\n";
3439 root 1.155 };
3440    
3441     warn "leave emergency perl save\n";
3442     }
3443 root 1.22
3444 root 1.211 sub post_cleanup {
3445     my ($make_core) = @_;
3446    
3447     warn Carp::longmess "post_cleanup backtrace"
3448     if $make_core;
3449     }
3450    
3451 root 1.246 sub do_reload_perl() {
3452 root 1.106 # can/must only be called in main
3453     if ($Coro::current != $Coro::main) {
3454 root 1.183 warn "can only reload from main coroutine";
3455 root 1.106 return;
3456     }
3457    
3458 root 1.103 warn "reloading...";
3459    
3460 root 1.212 warn "entering sync_job";
3461    
3462 root 1.213 cf::sync_job {
3463 root 1.214 cf::write_runtime; # external watchdog should not bark
3464 root 1.212 cf::emergency_save;
3465 root 1.214 cf::write_runtime; # external watchdog should not bark
3466 root 1.183
3467 root 1.212 warn "syncing database to disk";
3468     BDB::db_env_txn_checkpoint $DB_ENV;
3469 root 1.106
3470     # if anything goes wrong in here, we should simply crash as we already saved
3471 root 1.65
3472 root 1.183 warn "flushing outstanding aio requests";
3473     for (;;) {
3474 root 1.208 BDB::flush;
3475 root 1.183 IO::AIO::flush;
3476 root 1.387 Coro::cede_notself;
3477 root 1.208 last unless IO::AIO::nreqs || BDB::nreqs;
3478 root 1.183 warn "iterate...";
3479     }
3480    
3481 root 1.223 ++$RELOAD;
3482    
3483 root 1.183 warn "cancelling all extension coros";
3484 root 1.103 $_->cancel for values %EXT_CORO;
3485     %EXT_CORO = ();
3486    
3487 root 1.183 warn "removing commands";
3488 root 1.159 %COMMAND = ();
3489    
3490 root 1.287 warn "removing ext/exti commands";
3491     %EXTCMD = ();
3492     %EXTICMD = ();
3493 root 1.159
3494 root 1.183 warn "unloading/nuking all extensions";
3495 root 1.159 for my $pkg (@EXTS) {
3496 root 1.160 warn "... unloading $pkg";
3497 root 1.159
3498     if (my $cb = $pkg->can ("unload")) {
3499     eval {
3500     $cb->($pkg);
3501     1
3502     } or warn "$pkg unloaded, but with errors: $@";
3503     }
3504    
3505 root 1.160 warn "... nuking $pkg";
3506 root 1.159 Symbol::delete_package $pkg;
3507 root 1.65 }
3508    
3509 root 1.183 warn "unloading all perl modules loaded from $LIBDIR";
3510 root 1.65 while (my ($k, $v) = each %INC) {
3511     next unless $v =~ /^\Q$LIBDIR\E\/.*\.pm$/;
3512    
3513 root 1.183 warn "... unloading $k";
3514 root 1.65 delete $INC{$k};
3515    
3516     $k =~ s/\.pm$//;
3517     $k =~ s/\//::/g;
3518    
3519     if (my $cb = $k->can ("unload_module")) {
3520     $cb->();
3521     }
3522    
3523     Symbol::delete_package $k;
3524     }
3525    
3526 root 1.183 warn "getting rid of safe::, as good as possible";
3527 root 1.65 Symbol::delete_package "safe::$_"
3528 root 1.103 for qw(cf::attachable cf::object cf::object::player cf::client cf::player cf::map cf::party cf::region);
3529 root 1.65
3530 root 1.183 warn "unloading cf.pm \"a bit\"";
3531 root 1.65 delete $INC{"cf.pm"};
3532 root 1.252 delete $INC{"cf/pod.pm"};
3533 root 1.65
3534     # don't, removes xs symbols, too,
3535     # and global variables created in xs
3536     #Symbol::delete_package __PACKAGE__;
3537    
3538 root 1.183 warn "unload completed, starting to reload now";
3539    
3540 root 1.103 warn "reloading cf.pm";
3541 root 1.65 require cf;
3542 root 1.100 cf::_connect_to_perl; # nominally unnecessary, but cannot hurt
3543    
3544 root 1.183 warn "loading config and database again";
3545 root 1.345 cf::reload_config;
3546 root 1.65
3547 root 1.183 warn "loading extensions";
3548 root 1.65 cf::load_extensions;
3549    
3550 root 1.183 warn "reattaching attachments to objects/players";
3551 root 1.222 _global_reattach; # objects, sockets
3552 root 1.183 warn "reattaching attachments to maps";
3553 root 1.144 reattach $_ for values %MAP;
3554 root 1.222 warn "reattaching attachments to players";
3555     reattach $_ for values %PLAYER;
3556 root 1.183
3557 root 1.212 warn "leaving sync_job";
3558 root 1.183
3559 root 1.212 1
3560     } or do {
3561 root 1.106 warn $@;
3562 root 1.411 cf::cleanup "error while reloading, exiting.";
3563 root 1.212 };
3564 root 1.106
3565 root 1.159 warn "reloaded";
3566 root 1.65 };
3567    
3568 root 1.175 our $RELOAD_WATCHER; # used only during reload
3569    
3570 root 1.246 sub reload_perl() {
3571     # doing reload synchronously and two reloads happen back-to-back,
3572     # coro crashes during coro_state_free->destroy here.
3573    
3574 root 1.396 $RELOAD_WATCHER ||= EV::timer 0, 0, sub {
3575 root 1.409 do_reload_perl;
3576 root 1.396 undef $RELOAD_WATCHER;
3577     };
3578 root 1.246 }
3579    
3580 root 1.111 register_command "reload" => sub {
3581 root 1.65 my ($who, $arg) = @_;
3582    
3583     if ($who->flag (FLAG_WIZ)) {
3584 root 1.175 $who->message ("reloading server.");
3585 root 1.374 async {
3586     $Coro::current->{desc} = "perl_reload";
3587     reload_perl;
3588     };
3589 root 1.65 }
3590     };
3591    
3592 root 1.27 unshift @INC, $LIBDIR;
3593 root 1.17
3594 root 1.183 my $bug_warning = 0;
3595    
3596 root 1.239 our @WAIT_FOR_TICK;
3597     our @WAIT_FOR_TICK_BEGIN;
3598    
3599     sub wait_for_tick {
3600 root 1.240 return unless $TICK_WATCHER->is_active;
3601 root 1.241 return if $Coro::current == $Coro::main;
3602    
3603 root 1.239 my $signal = new Coro::Signal;
3604     push @WAIT_FOR_TICK, $signal;
3605     $signal->wait;
3606     }
3607    
3608     sub wait_for_tick_begin {
3609 root 1.240 return unless $TICK_WATCHER->is_active;
3610 root 1.241 return if $Coro::current == $Coro::main;
3611    
3612 root 1.239 my $signal = new Coro::Signal;
3613     push @WAIT_FOR_TICK_BEGIN, $signal;
3614     $signal->wait;
3615     }
3616    
3617 root 1.396 $TICK_WATCHER = EV::periodic_ns 0, $TICK, 0, sub {
3618     if ($Coro::current != $Coro::main) {
3619     Carp::cluck "major BUG: server tick called outside of main coro, skipping it"
3620     unless ++$bug_warning > 10;
3621     return;
3622     }
3623    
3624     $NOW = $tick_start = EV::now;
3625 root 1.214
3626 root 1.396 cf::server_tick; # one server iteration
3627 root 1.245
3628 root 1.404 $RUNTIME += $TICK;
3629     $NEXT_TICK = $_[0]->at;
3630 root 1.265
3631 root 1.396 if ($NOW >= $NEXT_RUNTIME_WRITE) {
3632 root 1.402 $NEXT_RUNTIME_WRITE = List::Util::max $NEXT_RUNTIME_WRITE + 10, $NOW + 5.;
3633 root 1.396 Coro::async_pool {
3634     $Coro::current->{desc} = "runtime saver";
3635     write_runtime
3636     or warn "ERROR: unable to write runtime file: $!";
3637     };
3638     }
3639 root 1.265
3640 root 1.396 if (my $sig = shift @WAIT_FOR_TICK_BEGIN) {
3641     $sig->send;
3642     }
3643     while (my $sig = shift @WAIT_FOR_TICK) {
3644     $sig->send;
3645     }
3646 root 1.265
3647 root 1.396 $LOAD = ($NOW - $tick_start) / $TICK;
3648     $LOADAVG = $LOADAVG * 0.75 + $LOAD * 0.25;
3649 root 1.265
3650 root 1.396 _post_tick;
3651     };
3652     $TICK_WATCHER->priority (EV::MAXPRI);
3653 root 1.35
3654 root 1.206 {
3655 root 1.401 # configure BDB
3656    
3657 root 1.363 BDB::min_parallel 8;
3658 root 1.400 BDB::max_poll_reqs $TICK * 0.1;
3659 root 1.403 $Coro::BDB::WATCHER->priority (1);
3660 root 1.77
3661 root 1.206 unless ($DB_ENV) {
3662     $DB_ENV = BDB::db_env_create;
3663 root 1.371 $DB_ENV->set_flags (BDB::AUTO_COMMIT | BDB::REGION_INIT | BDB::TXN_NOSYNC
3664     | BDB::LOG_AUTOREMOVE, 1);
3665     $DB_ENV->set_timeout (30, BDB::SET_TXN_TIMEOUT);
3666     $DB_ENV->set_timeout (30, BDB::SET_LOCK_TIMEOUT);
3667 root 1.206
3668     cf::sync_job {
3669 root 1.208 eval {
3670     BDB::db_env_open
3671     $DB_ENV,
3672 root 1.253 $BDBDIR,
3673 root 1.208 BDB::INIT_LOCK | BDB::INIT_LOG | BDB::INIT_MPOOL | BDB::INIT_TXN
3674     | BDB::RECOVER | BDB::REGISTER | BDB::USE_ENVIRON | BDB::CREATE,
3675     0666;
3676    
3677 root 1.253 cf::cleanup "db_env_open($BDBDIR): $!" if $!;
3678 root 1.208 };
3679    
3680     cf::cleanup "db_env_open(db): $@" if $@;
3681 root 1.206 };
3682     }
3683 root 1.363
3684 root 1.396 $BDB_DEADLOCK_WATCHER = EV::periodic 0, 3, 0, sub {
3685     BDB::db_env_lock_detect $DB_ENV, 0, BDB::LOCK_DEFAULT, 0, sub { };
3686     };
3687     $BDB_CHECKPOINT_WATCHER = EV::periodic 0, 60, 0, sub {
3688     BDB::db_env_txn_checkpoint $DB_ENV, 0, 0, 0, sub { };
3689     };
3690     $BDB_TRICKLE_WATCHER = EV::periodic 0, 10, 0, sub {
3691     BDB::db_env_memp_trickle $DB_ENV, 20, 0, sub { };
3692     };
3693 root 1.206 }
3694    
3695     {
3696 root 1.401 # configure IO::AIO
3697    
3698 root 1.206 IO::AIO::min_parallel 8;
3699     IO::AIO::max_poll_time $TICK * 0.1;
3700 root 1.403 $Coro::AIO::WATCHER->priority (1);
3701 root 1.206 }
3702 root 1.108
3703 root 1.262 my $_log_backtrace;
3704    
3705 root 1.260 sub _log_backtrace {
3706     my ($msg, @addr) = @_;
3707    
3708 root 1.262 $msg =~ s/\n//;
3709 root 1.260
3710 root 1.262 # limit the # of concurrent backtraces
3711     if ($_log_backtrace < 2) {
3712     ++$_log_backtrace;
3713     async {
3714 root 1.374 $Coro::current->{desc} = "abt $msg";
3715    
3716 root 1.262 my @bt = fork_call {
3717     @addr = map { sprintf "%x", $_ } @addr;
3718     my $self = (-f "/proc/$$/exe") ? "/proc/$$/exe" : $^X;
3719     open my $fh, "exec addr2line -C -f -i -e \Q$self\E @addr 2>&1 |"
3720     or die "addr2line: $!";
3721    
3722     my @funcs;
3723     my @res = <$fh>;
3724     chomp for @res;
3725     while (@res) {
3726     my ($func, $line) = splice @res, 0, 2, ();
3727     push @funcs, "[$func] $line";
3728     }
3729 root 1.260
3730 root 1.262 @funcs
3731     };
3732 root 1.260
3733 root 1.262 LOG llevInfo, "[ABT] $msg\n";
3734     LOG llevInfo, "[ABT] $_\n" for @bt;
3735     --$_log_backtrace;
3736     };
3737     } else {
3738 root 1.260 LOG llevInfo, "[ABT] $msg\n";
3739 root 1.262 LOG llevInfo, "[ABT] [suppressed]\n";
3740     }
3741 root 1.260 }
3742    
3743 root 1.249 # load additional modules
3744     use cf::pod;
3745    
3746 root 1.125 END { cf::emergency_save }
3747    
3748 root 1.1 1
3749