ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.399
Committed: Sun Dec 16 02:50:33 2007 UTC (16 years, 5 months ago) by root
Branch: MAIN
Changes since 1.398: +1 -1 lines
Log Message:
*** empty log message ***

File Contents

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