ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.406
Committed: Mon Dec 17 08:27:44 2007 UTC (16 years, 5 months ago) by root
Branch: MAIN
Changes since 1.405: +22 -18 lines
Log Message:
- emit sa global resource_update event after facedata is loaded
- cleanly attach to the resource_update signal to reload sound conf

File Contents

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