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