ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.412
Committed: Wed Apr 2 11:13:55 2008 UTC (16 years, 1 month ago) by root
Branch: MAIN
Changes since 1.411: +40 -28 lines
Log Message:
*** empty log message ***

File Contents

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