ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.140
Committed: Fri Jan 5 20:04:02 2007 UTC (17 years, 4 months ago) by root
Branch: MAIN
Changes since 1.139: +28 -17 lines
Log Message:
fix the bug: on_destroy is obviously not being called on pooled coroutines, aslo use more sensible names than 'coro'

File Contents

# Content
1 package cf;
2
3 use utf8;
4 use strict;
5
6 use Symbol;
7 use List::Util;
8 use Storable;
9 use Opcode;
10 use Safe;
11 use Safe::Hole;
12
13 use Coro 3.3 ();
14 use Coro::Event;
15 use Coro::Timer;
16 use Coro::Signal;
17 use Coro::Semaphore;
18 use Coro::AIO;
19
20 use Digest::MD5;
21 use Fcntl;
22 use IO::AIO 2.31 ();
23 use YAML::Syck ();
24 use Time::HiRes;
25
26 use Event; $Event::Eval = 1; # no idea why this is required, but it is
27
28 # work around bug in YAML::Syck - bad news for perl6, will it be as broken wrt. unicode?
29 $YAML::Syck::ImplicitUnicode = 1;
30
31 $Coro::main->prio (Coro::PRIO_MAX); # run main coroutine ("the server") with very high priority
32
33 sub WF_AUTOCANCEL () { 1 } # automatically cancel this watcher on reload
34
35 our %COMMAND = ();
36 our %COMMAND_TIME = ();
37 our %EXTCMD = ();
38
39 our @EVENT;
40 our $LIBDIR = datadir . "/ext";
41
42 our $TICK = MAX_TIME * 1e-6;
43 our $TICK_WATCHER;
44 our $NEXT_TICK;
45 our $NOW;
46
47 our %CFG;
48
49 our $UPTIME; $UPTIME ||= time;
50 our $RUNTIME;
51
52 our %MAP; # all maps
53 our $LINK_MAP; # the special {link} map
54 our $RANDOM_MAPS = cf::localdir . "/random";
55 our %EXT_CORO;
56
57 binmode STDOUT;
58 binmode STDERR;
59
60 # read virtual server time, if available
61 unless ($RUNTIME || !-e cf::localdir . "/runtime") {
62 open my $fh, "<", cf::localdir . "/runtime"
63 or die "unable to read runtime file: $!";
64 $RUNTIME = <$fh> + 0.;
65 }
66
67 mkdir cf::localdir;
68 mkdir cf::localdir . "/" . cf::playerdir;
69 mkdir cf::localdir . "/" . cf::tmpdir;
70 mkdir cf::localdir . "/" . cf::uniquedir;
71 mkdir $RANDOM_MAPS;
72
73 # a special map that is always available
74 our $LINK_MAP;
75 our $EMERGENCY_POSITION;
76
77 #############################################################################
78
79 =head2 GLOBAL VARIABLES
80
81 =over 4
82
83 =item $cf::UPTIME
84
85 The timestamp of the server start (so not actually an uptime).
86
87 =item $cf::RUNTIME
88
89 The time this server has run, starts at 0 and is increased by $cf::TICK on
90 every server tick.
91
92 =item $cf::LIBDIR
93
94 The perl library directory, where extensions and cf-specific modules can
95 be found. It will be added to C<@INC> automatically.
96
97 =item $cf::NOW
98
99 The time of the last (current) server tick.
100
101 =item $cf::TICK
102
103 The interval between server ticks, in seconds.
104
105 =item %cf::CFG
106
107 Configuration for the server, loaded from C</etc/crossfire/config>, or
108 from wherever your confdir points to.
109
110 =back
111
112 =cut
113
114 BEGIN {
115 *CORE::GLOBAL::warn = sub {
116 my $msg = join "", @_;
117 utf8::encode $msg;
118
119 $msg .= "\n"
120 unless $msg =~ /\n$/;
121
122 LOG llevError, "cfperl: $msg";
123 };
124 }
125
126 @safe::cf::global::ISA = @cf::global::ISA = 'cf::attachable';
127 @safe::cf::object::ISA = @cf::object::ISA = 'cf::attachable';
128 @safe::cf::player::ISA = @cf::player::ISA = 'cf::attachable';
129 @safe::cf::client::ISA = @cf::client::ISA = 'cf::attachable';
130 @safe::cf::map::ISA = @cf::map::ISA = 'cf::attachable';
131 @safe::cf::object::player::ISA = @cf::object::player::ISA = 'cf::object';
132
133 # we bless all objects into (empty) derived classes to force a method lookup
134 # within the Safe compartment.
135 for my $pkg (qw(
136 cf::global cf::attachable
137 cf::object cf::object::player
138 cf::client cf::player
139 cf::arch cf::living
140 cf::map cf::party cf::region
141 )) {
142 no strict 'refs';
143 @{"safe::$pkg\::wrap::ISA"} = @{"$pkg\::wrap::ISA"} = $pkg;
144 }
145
146 $Event::DIED = sub {
147 warn "error in event callback: @_";
148 };
149
150 my %ext_pkg;
151 my @exts;
152 my @hook;
153
154 =head2 UTILITY FUNCTIONS
155
156 =over 4
157
158 =cut
159
160 use JSON::Syck (); # TODO# replace by JSON::PC once working
161
162 =item $ref = cf::from_json $json
163
164 Converts a JSON string into the corresponding perl data structure.
165
166 =cut
167
168 sub from_json($) {
169 $JSON::Syck::ImplicitUnicode = 1; # work around JSON::Syck bugs
170 JSON::Syck::Load $_[0]
171 }
172
173 =item $json = cf::to_json $ref
174
175 Converts a perl data structure into its JSON representation.
176
177 =cut
178
179 sub to_json($) {
180 $JSON::Syck::ImplicitUnicode = 0; # work around JSON::Syck bugs
181 JSON::Syck::Dump $_[0]
182 }
183
184 =item cf::lock_wait $string
185
186 Wait until the given lock is available. See cf::lock_acquire.
187
188 =item my $lock = cf::lock_acquire $string
189
190 Wait until the given lock is available and then acquires it and returns
191 a Coro::guard object. If the guard object gets destroyed (goes out of scope,
192 for example when the coroutine gets canceled), the lock is automatically
193 returned.
194
195 Lock names should begin with a unique identifier (for example, cf::map::find
196 uses map_find and cf::map::load uses map_load).
197
198 =cut
199
200 our %LOCK;
201
202 sub lock_wait($) {
203 my ($key) = @_;
204
205 # wait for lock, if any
206 while ($LOCK{$key}) {
207 push @{ $LOCK{$key} }, $Coro::current;
208 Coro::schedule;
209 }
210 }
211
212 sub lock_acquire($) {
213 my ($key) = @_;
214
215 # wait, to be sure we are not locked
216 lock_wait $key;
217
218 $LOCK{$key} = [];
219
220 Coro::guard {
221 # wake up all waiters, to be on the safe side
222 $_->ready for @{ delete $LOCK{$key} };
223 }
224 }
225
226 sub freeze_mainloop {
227 return unless $TICK_WATCHER->is_active;
228
229 my $guard = Coro::guard { $TICK_WATCHER->start };
230 $TICK_WATCHER->stop;
231 $guard
232 }
233
234 =item cf::async { BLOCK }
235
236 Currently the same as Coro::async_pool, meaning you cannot use
237 C<on_destroy>, C<join> or other gimmicks on these coroutines. The only
238 thing you are allowed to do is call C<prio> on it.
239
240 =cut
241
242 BEGIN { *async = \&Coro::async_pool }
243
244 =item cf::sync_job { BLOCK }
245
246 The design of crossfire+ requires that the main coro ($Coro::main) is
247 always able to handle events or runnable, as crossfire+ is only partly
248 reentrant. Thus "blocking" it by e.g. waiting for I/O is not acceptable.
249
250 If it must be done, put the blocking parts into C<sync_job>. This will run
251 the given BLOCK in another coroutine while waiting for the result. The
252 server will be frozen during this time, so the block should either finish
253 fast or be very important.
254
255 =cut
256
257 sub sync_job(&) {
258 my ($job) = @_;
259
260 if ($Coro::current == $Coro::main) {
261 # this is the main coro, too bad, we have to block
262 # till the operation succeeds, freezing the server :/
263
264 # TODO: use suspend/resume instead
265 # (but this is cancel-safe)
266 my $freeze_guard = freeze_mainloop;
267
268 my $busy = 1;
269 my @res;
270
271 (async {
272 @res = eval { $job->() };
273 warn $@ if $@;
274 undef $busy;
275 })->prio (Coro::PRIO_MAX);
276
277 while ($busy) {
278 unless (Coro::cede) {
279 Coro::nready ? Event::one_event 0 : Event::one_event;
280 Coro::cede_notself unless Coro::cede;
281 }
282 }
283
284 wantarray ? @res : $res[0]
285 } else {
286 # we are in another coroutine, how wonderful, everything just works
287
288 $job->()
289 }
290 }
291
292 =item $coro = cf::async_ext { BLOCK }
293
294 Like async, but this coro is automcatially being canceled when the
295 extension calling this is being unloaded.
296
297 =cut
298
299 sub async_ext(&) {
300 my $cb = shift;
301
302 my $coro = &Coro::async ($cb);
303
304 $coro->on_destroy (sub {
305 delete $EXT_CORO{$coro+0};
306 });
307 $EXT_CORO{$coro+0} = $coro;
308
309 $coro
310 }
311
312 sub write_runtime {
313 my $runtime = cf::localdir . "/runtime";
314
315 my $fh = aio_open "$runtime~", O_WRONLY | O_CREAT, 0644
316 or return;
317
318 my $value = $cf::RUNTIME + 1 + 10; # 10 is the runtime save interval, for a monotonic clock
319 (aio_write $fh, 0, (length $value), $value, 0) <= 0
320 and return;
321
322 aio_fsync $fh
323 and return;
324
325 close $fh
326 or return;
327
328 aio_rename "$runtime~", $runtime
329 and return;
330
331 1
332 }
333
334 =back
335
336 =cut
337
338 #############################################################################
339
340 package cf::path;
341
342 sub new {
343 my ($class, $path, $base) = @_;
344
345 $path = $path->as_string if ref $path;
346
347 my $self = bless { }, $class;
348
349 # {... are special paths that are not touched
350 # ?xxx/... are special absolute paths
351 # ?random/... random maps
352 # /! non-realised random map exit
353 # /... normal maps
354 # ~/... per-player maps without a specific player (DO NOT USE)
355 # ~user/... per-player map of a specific user
356
357 if ($path =~ /^{/) {
358 # fine as it is
359 } elsif ($path =~ s{^\?random/}{}) {
360 Coro::AIO::aio_load "$cf::RANDOM_MAPS/$path.meta", my $data;
361 $self->{random} = cf::from_json $data;
362 } else {
363 if ($path =~ s{^~([^/]+)?}{}) {
364 $self->{user_rel} = 1;
365
366 if (defined $1) {
367 $self->{user} = $1;
368 } elsif ($base =~ m{^~([^/]+)/}) {
369 $self->{user} = $1;
370 } else {
371 warn "cannot resolve user-relative path without user <$path,$base>\n";
372 }
373 } elsif ($path =~ /^\//) {
374 # already absolute
375 } else {
376 $base =~ s{[^/]+/?$}{};
377 return $class->new ("$base/$path");
378 }
379
380 for ($path) {
381 redo if s{/\.?/}{/};
382 redo if s{/[^/]+/\.\./}{/};
383 }
384 }
385
386 $self->{path} = $path;
387
388 $self
389 }
390
391 # the name / primary key / in-game path
392 sub as_string {
393 my ($self) = @_;
394
395 $self->{user_rel} ? "~$self->{user}$self->{path}"
396 : $self->{random} ? "?random/$self->{path}"
397 : $self->{path}
398 }
399
400 # the displayed name, this is a one way mapping
401 sub visible_name {
402 my ($self) = @_;
403
404 # if (my $rmp = $self->{random}) {
405 # # todo: be more intelligent about this
406 # "?random/$rmp->{origin_map}+$rmp->{origin_x}+$rmp->{origin_y}/$rmp->{dungeon_level}"
407 # } else {
408 $self->as_string
409 # }
410 }
411
412 # escape the /'s in the path
413 sub _escaped_path {
414 # ∕ is U+2215
415 (my $path = $_[0]{path}) =~ s/\//∕/g;
416 $path
417 }
418
419 # the original (read-only) location
420 sub load_path {
421 my ($self) = @_;
422
423 sprintf "%s/%s/%s", cf::datadir, cf::mapdir, $self->{path}
424 }
425
426 # the temporary/swap location
427 sub save_path {
428 my ($self) = @_;
429
430 $self->{user_rel} ? sprintf "%s/%s/%s/%s", cf::localdir, cf::playerdir, $self->{user}, $self->_escaped_path
431 : $self->{random} ? sprintf "%s/%s", $RANDOM_MAPS, $self->{path}
432 : sprintf "%s/%s/%s", cf::localdir, cf::tmpdir, $self->_escaped_path
433 }
434
435 # the unique path, might be eq to save_path
436 sub uniq_path {
437 my ($self) = @_;
438
439 $self->{user_rel} || $self->{random}
440 ? undef
441 : sprintf "%s/%s/%s", cf::localdir, cf::uniquedir, $self->_escaped_path
442 }
443
444 # return random map parameters, or undef
445 sub random_map_params {
446 my ($self) = @_;
447
448 $self->{random}
449 }
450
451 # this is somewhat ugly, but style maps do need special treatment
452 sub is_style_map {
453 $_[0]{path} =~ m{^/styles/}
454 }
455
456 package cf;
457
458 #############################################################################
459
460 =head2 ATTACHABLE OBJECTS
461
462 Many objects in crossfire are so-called attachable objects. That means you can
463 attach callbacks/event handlers (a collection of which is called an "attachment")
464 to it. All such attachable objects support the following methods.
465
466 In the following description, CLASS can be any of C<global>, C<object>
467 C<player>, C<client> or C<map> (i.e. the attachable objects in
468 crossfire+).
469
470 =over 4
471
472 =item $attachable->attach ($attachment, key => $value...)
473
474 =item $attachable->detach ($attachment)
475
476 Attach/detach a pre-registered attachment to a specific object and give it
477 the specified key/value pairs as arguments.
478
479 Example, attach a minesweeper attachment to the given object, making it a
480 10x10 minesweeper game:
481
482 $obj->attach (minesweeper => width => 10, height => 10);
483
484 =item $bool = $attachable->attached ($name)
485
486 Checks wether the named attachment is currently attached to the object.
487
488 =item cf::CLASS->attach ...
489
490 =item cf::CLASS->detach ...
491
492 Define an anonymous attachment and attach it to all objects of the given
493 CLASS. See the next function for an explanation of its arguments.
494
495 You can attach to global events by using the C<cf::global> class.
496
497 Example, log all player logins:
498
499 cf::player->attach (
500 on_login => sub {
501 my ($pl) = @_;
502 ...
503 },
504 );
505
506 Example, attach to the jeweler skill:
507
508 cf::object->attach (
509 type => cf::SKILL,
510 subtype => cf::SK_JEWELER,
511 on_use_skill => sub {
512 my ($sk, $ob, $part, $dir, $msg) = @_;
513 ...
514 },
515 );
516
517 =item cf::CLASS::attachment $name, ...
518
519 Register an attachment by C<$name> through which attachable objects of the
520 given CLASS can refer to this attachment.
521
522 Some classes such as crossfire maps and objects can specify attachments
523 that are attached at load/instantiate time, thus the need for a name.
524
525 These calls expect any number of the following handler/hook descriptions:
526
527 =over 4
528
529 =item prio => $number
530
531 Set the priority for all following handlers/hooks (unless overwritten
532 by another C<prio> setting). Lower priority handlers get executed
533 earlier. The default priority is C<0>, and many built-in handlers are
534 registered at priority C<-1000>, so lower priorities should not be used
535 unless you know what you are doing.
536
537 =item type => $type
538
539 (Only for C<< cf::object->attach >> calls), limits the attachment to the
540 given type of objects only (the additional parameter C<subtype> can be
541 used to further limit to the given subtype).
542
543 =item on_I<event> => \&cb
544
545 Call the given code reference whenever the named event happens (event is
546 something like C<instantiate>, C<apply>, C<use_skill> and so on, and which
547 handlers are recognised generally depends on the type of object these
548 handlers attach to).
549
550 See F<include/eventinc.h> for the full list of events supported, and their
551 class.
552
553 =item package => package::
554
555 Look for sub functions of the name C<< on_I<event> >> in the given
556 package and register them. Only handlers for eevents supported by the
557 object/class are recognised.
558
559 =back
560
561 Example, define an attachment called "sockpuppet" that calls the given
562 event handler when a monster attacks:
563
564 cf::object::attachment sockpuppet =>
565 on_skill_attack => sub {
566 my ($self, $victim) = @_;
567 ...
568 }
569 }
570
571 =item $attachable->valid
572
573 Just because you have a perl object does not mean that the corresponding
574 C-level object still exists. If you try to access an object that has no
575 valid C counterpart anymore you get an exception at runtime. This method
576 can be used to test for existence of the C object part without causing an
577 exception.
578
579 =cut
580
581 # the following variables are defined in .xs and must not be re-created
582 our @CB_GLOBAL = (); # registry for all global events
583 our @CB_ATTACHABLE = (); # registry for all attachables
584 our @CB_OBJECT = (); # all objects (should not be used except in emergency)
585 our @CB_PLAYER = ();
586 our @CB_CLIENT = ();
587 our @CB_TYPE = (); # registry for type (cf-object class) based events
588 our @CB_MAP = ();
589
590 my %attachment;
591
592 sub _attach_cb($$$$) {
593 my ($registry, $event, $prio, $cb) = @_;
594
595 use sort 'stable';
596
597 $cb = [$prio, $cb];
598
599 @{$registry->[$event]} = sort
600 { $a->[0] cmp $b->[0] }
601 @{$registry->[$event] || []}, $cb;
602 }
603
604 # hack
605 my %attachable_klass = map +($_ => 1), KLASS_OBJECT, KLASS_CLIENT, KLASS_PLAYER, KLASS_MAP;
606
607 # attach handles attaching event callbacks
608 # the only thing the caller has to do is pass the correct
609 # registry (== where the callback attaches to).
610 sub _attach {
611 my ($registry, $klass, @arg) = @_;
612
613 my $object_type;
614 my $prio = 0;
615 my %cb_id = map +("on_" . lc $EVENT[$_][0], $_) , grep $EVENT[$_][1] == $klass, 0 .. $#EVENT;
616
617 #TODO: get rid of this hack
618 if ($attachable_klass{$klass}) {
619 %cb_id = (%cb_id, map +("on_" . lc $EVENT[$_][0], $_) , grep $EVENT[$_][1] == KLASS_ATTACHABLE, 0 .. $#EVENT);
620 }
621
622 while (@arg) {
623 my $type = shift @arg;
624
625 if ($type eq "prio") {
626 $prio = shift @arg;
627
628 } elsif ($type eq "type") {
629 $object_type = shift @arg;
630 $registry = $CB_TYPE[$object_type] ||= [];
631
632 } elsif ($type eq "subtype") {
633 defined $object_type or Carp::croak "subtype specified without type";
634 my $object_subtype = shift @arg;
635 $registry = $CB_TYPE[$object_type + $object_subtype * NUM_SUBTYPES] ||= [];
636
637 } elsif ($type eq "package") {
638 my $pkg = shift @arg;
639
640 while (my ($name, $id) = each %cb_id) {
641 if (my $cb = $pkg->can ($name)) {
642 _attach_cb $registry, $id, $prio, $cb;
643 }
644 }
645
646 } elsif (exists $cb_id{$type}) {
647 _attach_cb $registry, $cb_id{$type}, $prio, shift @arg;
648
649 } elsif (ref $type) {
650 warn "attaching objects not supported, ignoring.\n";
651
652 } else {
653 shift @arg;
654 warn "attach argument '$type' not supported, ignoring.\n";
655 }
656 }
657 }
658
659 sub _object_attach {
660 my ($obj, $name, %arg) = @_;
661
662 return if exists $obj->{_attachment}{$name};
663
664 if (my $attach = $attachment{$name}) {
665 my $registry = $obj->registry;
666
667 for (@$attach) {
668 my ($klass, @attach) = @$_;
669 _attach $registry, $klass, @attach;
670 }
671
672 $obj->{$name} = \%arg;
673 } else {
674 warn "object uses attachment '$name' that is not available, postponing.\n";
675 }
676
677 $obj->{_attachment}{$name} = undef;
678 }
679
680 sub cf::attachable::attach {
681 if (ref $_[0]) {
682 _object_attach @_;
683 } else {
684 _attach shift->_attach_registry, @_;
685 }
686 };
687
688 # all those should be optimised
689 sub cf::attachable::detach {
690 my ($obj, $name) = @_;
691
692 if (ref $obj) {
693 delete $obj->{_attachment}{$name};
694 reattach ($obj);
695 } else {
696 Carp::croak "cannot, currently, detach class attachments";
697 }
698 };
699
700 sub cf::attachable::attached {
701 my ($obj, $name) = @_;
702
703 exists $obj->{_attachment}{$name}
704 }
705
706 for my $klass (qw(ATTACHABLE GLOBAL OBJECT PLAYER CLIENT MAP)) {
707 eval "#line " . __LINE__ . " 'cf.pm'
708 sub cf::\L$klass\E::_attach_registry {
709 (\\\@CB_$klass, KLASS_$klass)
710 }
711
712 sub cf::\L$klass\E::attachment {
713 my \$name = shift;
714
715 \$attachment{\$name} = [[KLASS_$klass, \@_]];
716 }
717 ";
718 die if $@;
719 }
720
721 our $override;
722 our @invoke_results = (); # referenced from .xs code. TODO: play tricks with reify and mortals?
723
724 sub override {
725 $override = 1;
726 @invoke_results = ();
727 }
728
729 sub do_invoke {
730 my $event = shift;
731 my $callbacks = shift;
732
733 @invoke_results = ();
734
735 local $override;
736
737 for (@$callbacks) {
738 eval { &{$_->[1]} };
739
740 if ($@) {
741 warn "$@";
742 warn "... while processing $EVENT[$event][0](@_) event, skipping processing altogether.\n";
743 override;
744 }
745
746 return 1 if $override;
747 }
748
749 0
750 }
751
752 =item $bool = cf::global::invoke (EVENT_CLASS_XXX, ...)
753
754 =item $bool = $attachable->invoke (EVENT_CLASS_XXX, ...)
755
756 Generate an object-specific event with the given arguments.
757
758 This API is preliminary (most likely, the EVENT_CLASS_xxx prefix will be
759 removed in future versions), and there is no public API to access override
760 results (if you must, access C<@cf::invoke_results> directly).
761
762 =back
763
764 =cut
765
766 #############################################################################
767 # object support
768
769 sub reattach {
770 # basically do the same as instantiate, without calling instantiate
771 my ($obj) = @_;
772
773 my $registry = $obj->registry;
774
775 @$registry = ();
776
777 delete $obj->{_attachment} unless scalar keys %{ $obj->{_attachment} || {} };
778
779 for my $name (keys %{ $obj->{_attachment} || {} }) {
780 if (my $attach = $attachment{$name}) {
781 for (@$attach) {
782 my ($klass, @attach) = @$_;
783 _attach $registry, $klass, @attach;
784 }
785 } else {
786 warn "object uses attachment '$name' that is not available, postponing.\n";
787 }
788 }
789 }
790
791 cf::attachable->attach (
792 prio => -1000000,
793 on_instantiate => sub {
794 my ($obj, $data) = @_;
795
796 $data = from_json $data;
797
798 for (@$data) {
799 my ($name, $args) = @$_;
800
801 $obj->attach ($name, %{$args || {} });
802 }
803 },
804 on_reattach => \&reattach,
805 on_clone => sub {
806 my ($src, $dst) = @_;
807
808 @{$dst->registry} = @{$src->registry};
809
810 %$dst = %$src;
811
812 %{$dst->{_attachment}} = %{$src->{_attachment}}
813 if exists $src->{_attachment};
814 },
815 );
816
817 sub object_freezer_save {
818 my ($filename, $rdata, $objs) = @_;
819
820 sync_job {
821 if (length $$rdata) {
822 warn sprintf "saving %s (%d,%d)\n",
823 $filename, length $$rdata, scalar @$objs;
824
825 if (my $fh = aio_open "$filename~", O_WRONLY | O_CREAT, 0600) {
826 chmod SAVE_MODE, $fh;
827 aio_write $fh, 0, (length $$rdata), $$rdata, 0;
828 aio_fsync $fh;
829 close $fh;
830
831 if (@$objs) {
832 if (my $fh = aio_open "$filename.pst~", O_WRONLY | O_CREAT, 0600) {
833 chmod SAVE_MODE, $fh;
834 my $data = Storable::nfreeze { version => 1, objs => $objs };
835 aio_write $fh, 0, (length $data), $data, 0;
836 aio_fsync $fh;
837 close $fh;
838 aio_rename "$filename.pst~", "$filename.pst";
839 }
840 } else {
841 aio_unlink "$filename.pst";
842 }
843
844 aio_rename "$filename~", $filename;
845 } else {
846 warn "FATAL: $filename~: $!\n";
847 }
848 } else {
849 aio_unlink $filename;
850 aio_unlink "$filename.pst";
851 }
852 }
853 }
854
855 sub object_freezer_as_string {
856 my ($rdata, $objs) = @_;
857
858 use Data::Dumper;
859
860 $$rdata . Dumper $objs
861 }
862
863 sub object_thawer_load {
864 my ($filename) = @_;
865
866 my ($data, $av);
867
868 (aio_load $filename, $data) >= 0
869 or return;
870
871 unless (aio_stat "$filename.pst") {
872 (aio_load "$filename.pst", $av) >= 0
873 or return;
874 $av = eval { (Storable::thaw $av)->{objs} };
875 }
876
877 warn sprintf "loading %s (%d)\n",
878 $filename, length $data, scalar @{$av || []};#d#
879 return ($data, $av);
880 }
881
882 #############################################################################
883 # command handling &c
884
885 =item cf::register_command $name => \&callback($ob,$args);
886
887 Register a callback for execution when the client sends the user command
888 $name.
889
890 =cut
891
892 sub register_command {
893 my ($name, $cb) = @_;
894
895 my $caller = caller;
896 #warn "registering command '$name/$time' to '$caller'";
897
898 push @{ $COMMAND{$name} }, [$caller, $cb];
899 }
900
901 =item cf::register_extcmd $name => \&callback($pl,$packet);
902
903 Register a callbackf ro execution when the client sends an extcmd packet.
904
905 If the callback returns something, it is sent back as if reply was being
906 called.
907
908 =cut
909
910 sub register_extcmd {
911 my ($name, $cb) = @_;
912
913 my $caller = caller;
914 #warn "registering extcmd '$name' to '$caller'";
915
916 $EXTCMD{$name} = [$cb, $caller];
917 }
918
919 cf::player->attach (
920 on_command => sub {
921 my ($pl, $name, $params) = @_;
922
923 my $cb = $COMMAND{$name}
924 or return;
925
926 for my $cmd (@$cb) {
927 $cmd->[1]->($pl->ob, $params);
928 }
929
930 cf::override;
931 },
932 on_extcmd => sub {
933 my ($pl, $buf) = @_;
934
935 my $msg = eval { from_json $buf };
936
937 if (ref $msg) {
938 if (my $cb = $EXTCMD{$msg->{msgtype}}) {
939 if (my %reply = $cb->[0]->($pl, $msg)) {
940 $pl->ext_reply ($msg->{msgid}, %reply);
941 }
942 }
943 } else {
944 warn "player " . ($pl->ob->name) . " sent unparseable ext message: <$buf>\n";
945 }
946
947 cf::override;
948 },
949 );
950
951 sub register {
952 my ($base, $pkg) = @_;
953
954 #TODO
955 }
956
957 sub load_extension {
958 my ($path) = @_;
959
960 $path =~ /([^\/\\]+)\.ext$/ or die "$path";
961 my $base = $1;
962 my $pkg = $1;
963 $pkg =~ s/[^[:word:]]/_/g;
964 $pkg = "ext::$pkg";
965
966 warn "loading '$path' into '$pkg'\n";
967
968 open my $fh, "<:utf8", $path
969 or die "$path: $!";
970
971 my $source =
972 "package $pkg; use strict; use utf8;\n"
973 . "#line 1 \"$path\"\n{\n"
974 . (do { local $/; <$fh> })
975 . "\n};\n1";
976
977 eval $source
978 or die $@ ? "$path: $@\n"
979 : "extension disabled.\n";
980
981 push @exts, $pkg;
982 $ext_pkg{$base} = $pkg;
983
984 # no strict 'refs';
985 # @{"$pkg\::ISA"} = ext::;
986
987 register $base, $pkg;
988 }
989
990 sub unload_extension {
991 my ($pkg) = @_;
992
993 warn "removing extension $pkg\n";
994
995 # remove hooks
996 #TODO
997 # for my $idx (0 .. $#PLUGIN_EVENT) {
998 # delete $hook[$idx]{$pkg};
999 # }
1000
1001 # remove commands
1002 for my $name (keys %COMMAND) {
1003 my @cb = grep $_->[0] ne $pkg, @{ $COMMAND{$name} };
1004
1005 if (@cb) {
1006 $COMMAND{$name} = \@cb;
1007 } else {
1008 delete $COMMAND{$name};
1009 }
1010 }
1011
1012 # remove extcmds
1013 for my $name (grep $EXTCMD{$_}[1] eq $pkg, keys %EXTCMD) {
1014 delete $EXTCMD{$name};
1015 }
1016
1017 if (my $cb = $pkg->can ("unload")) {
1018 eval {
1019 $cb->($pkg);
1020 1
1021 } or warn "$pkg unloaded, but with errors: $@";
1022 }
1023
1024 Symbol::delete_package $pkg;
1025 }
1026
1027 sub load_extensions {
1028 for my $ext (<$LIBDIR/*.ext>) {
1029 next unless -r $ext;
1030 eval {
1031 load_extension $ext;
1032 1
1033 } or warn "$ext not loaded: $@";
1034 }
1035 }
1036
1037 #############################################################################
1038 # load/save/clean perl data associated with a map
1039
1040 *cf::mapsupport::on_clean = sub {
1041 my ($map) = @_;
1042
1043 my $path = $map->tmpname;
1044 defined $path or return;
1045
1046 unlink "$path.pst";
1047 };
1048
1049 cf::map->attach (prio => -10000, package => cf::mapsupport::);
1050
1051 #############################################################################
1052 # load/save perl data associated with player->ob objects
1053
1054 sub all_objects(@) {
1055 @_, map all_objects ($_->inv), @_
1056 }
1057
1058 # TODO: compatibility cruft, remove when no longer needed
1059 cf::player->attach (
1060 on_load => sub {
1061 my ($pl, $path) = @_;
1062
1063 for my $o (all_objects $pl->ob) {
1064 if (my $value = $o->get_ob_key_value ("_perl_data")) {
1065 $o->set_ob_key_value ("_perl_data");
1066
1067 %$o = %{ Storable::thaw pack "H*", $value };
1068 }
1069 }
1070 },
1071 );
1072
1073 #############################################################################
1074
1075 =head2 CORE EXTENSIONS
1076
1077 Functions and methods that extend core crossfire objects.
1078
1079 =head3 cf::player
1080
1081 =over 4
1082
1083 =item cf::player::exists $login
1084
1085 Returns true when the given account exists.
1086
1087 =cut
1088
1089 sub cf::player::exists($) {
1090 cf::player::find $_[0]
1091 or -f sprintf "%s/%s/%s/%s.pl", cf::localdir, cf::playerdir, ($_[0]) x 2;
1092 }
1093
1094 =item $player->ext_reply ($msgid, $msgtype, %msg)
1095
1096 Sends an ext reply to the player.
1097
1098 =cut
1099
1100 sub cf::player::ext_reply($$$%) {
1101 my ($self, $id, %msg) = @_;
1102
1103 $msg{msgid} = $id;
1104
1105 $self->send ("ext " . to_json \%msg);
1106 }
1107
1108 =back
1109
1110
1111 =head3 cf::map
1112
1113 =over 4
1114
1115 =cut
1116
1117 package cf::map;
1118
1119 use Fcntl;
1120 use Coro::AIO;
1121
1122 our $MAX_RESET = 3600;
1123 our $DEFAULT_RESET = 3000;
1124
1125 sub generate_random_map {
1126 my ($path, $rmp) = @_;
1127
1128 # mit "rum" bekleckern, nicht
1129 cf::map::_create_random_map
1130 $path,
1131 $rmp->{wallstyle}, $rmp->{wall_name}, $rmp->{floorstyle}, $rmp->{monsterstyle},
1132 $rmp->{treasurestyle}, $rmp->{layoutstyle}, $rmp->{doorstyle}, $rmp->{decorstyle},
1133 $rmp->{origin_map}, $rmp->{final_map}, $rmp->{exitstyle}, $rmp->{this_map},
1134 $rmp->{exit_on_final_map},
1135 $rmp->{xsize}, $rmp->{ysize},
1136 $rmp->{expand2x}, $rmp->{layoutoptions1}, $rmp->{layoutoptions2}, $rmp->{layoutoptions3},
1137 $rmp->{symmetry}, $rmp->{difficulty}, $rmp->{difficulty_given}, $rmp->{difficulty_increase},
1138 $rmp->{dungeon_level}, $rmp->{dungeon_depth}, $rmp->{decoroptions}, $rmp->{orientation},
1139 $rmp->{origin_y}, $rmp->{origin_x}, $rmp->{random_seed}, $rmp->{total_map_hp},
1140 $rmp->{map_layout_style}, $rmp->{treasureoptions}, $rmp->{symmetry_used},
1141 (cf::region::find $rmp->{region})
1142 }
1143
1144 # and all this just because we cannot iterate over
1145 # all maps in C++...
1146 sub change_all_map_light {
1147 my ($change) = @_;
1148
1149 $_->change_map_light ($change)
1150 for grep $_->outdoor, values %cf::MAP;
1151 }
1152
1153 sub try_load_header($) {
1154 my ($path) = @_;
1155
1156 utf8::encode $path;
1157 aio_open $path, O_RDONLY, 0
1158 or return;
1159
1160 my $map = cf::map::new
1161 or return;
1162
1163 # for better error messages only, will be overwritten
1164 $map->path ($path);
1165
1166 $map->load_header ($path)
1167 or return;
1168
1169 $map->{load_path} = $path;
1170
1171 $map
1172 }
1173
1174 sub find;
1175 sub find {
1176 my ($path, $origin) = @_;
1177
1178 #warn "find<$path,$origin>\n";#d#
1179
1180 $path = new cf::path $path, $origin && $origin->path;
1181 my $key = $path->as_string;
1182
1183 cf::lock_wait "map_find:$key";
1184
1185 $cf::MAP{$key} || do {
1186 my $guard = cf::lock_acquire "map_find:$key";
1187
1188 # do it the slow way
1189 my $map = try_load_header $path->save_path;
1190
1191 Coro::cede;
1192
1193 if ($map) {
1194 $map->last_access ((delete $map->{last_access})
1195 || $cf::RUNTIME); #d#
1196 # safety
1197 $map->{instantiate_time} = $cf::RUNTIME
1198 if $map->{instantiate_time} > $cf::RUNTIME;
1199 } else {
1200 if (my $rmp = $path->random_map_params) {
1201 $map = generate_random_map $key, $rmp;
1202 } else {
1203 $map = try_load_header $path->load_path;
1204 }
1205
1206 $map or return;
1207
1208 $map->{load_original} = 1;
1209 $map->{instantiate_time} = $cf::RUNTIME;
1210 $map->last_access ($cf::RUNTIME);
1211 $map->instantiate;
1212
1213 # per-player maps become, after loading, normal maps
1214 $map->per_player (0) if $path->{user_rel};
1215 }
1216
1217 $map->path ($key);
1218 $map->{path} = $path;
1219 $map->{last_save} = $cf::RUNTIME;
1220
1221 Coro::cede;
1222
1223 if ($map->should_reset) {
1224 $map->reset;
1225 undef $guard;
1226 $map = find $path
1227 or return;
1228 }
1229
1230 $cf::MAP{$key} = $map
1231 }
1232 }
1233
1234 sub load {
1235 my ($self) = @_;
1236
1237 my $path = $self->{path};
1238 my $guard = cf::lock_acquire "map_load:" . $path->as_string;
1239
1240 return if $self->in_memory != cf::MAP_SWAPPED;
1241
1242 $self->in_memory (cf::MAP_LOADING);
1243
1244 $self->alloc;
1245 $self->load_objects ($self->{load_path}, 1)
1246 or return;
1247
1248 $self->set_object_flag (cf::FLAG_OBJ_ORIGINAL, 1)
1249 if delete $self->{load_original};
1250
1251 if (my $uniq = $path->uniq_path) {
1252 utf8::encode $uniq;
1253 if (aio_open $uniq, O_RDONLY, 0) {
1254 $self->clear_unique_items;
1255 $self->load_objects ($uniq, 0);
1256 }
1257 }
1258
1259 Coro::cede;
1260
1261 # now do the right thing for maps
1262 $self->link_multipart_objects;
1263
1264 if ($self->{path}->is_style_map) {
1265 $self->{deny_save} = 1;
1266 $self->{deny_reset} = 1;
1267 } else {
1268 $self->fix_auto_apply;
1269 $self->decay_objects;
1270 $self->update_buttons;
1271 $self->set_darkness_map;
1272 $self->difficulty ($self->estimate_difficulty)
1273 unless $self->difficulty;
1274 $self->activate;
1275 }
1276
1277 Coro::cede;
1278
1279 $self->in_memory (cf::MAP_IN_MEMORY);
1280 }
1281
1282 sub find_sync {
1283 my ($path, $origin) = @_;
1284
1285 cf::sync_job { cf::map::find $path, $origin }
1286 }
1287
1288 sub do_load_sync {
1289 my ($map) = @_;
1290
1291 cf::sync_job { $map->load };
1292 }
1293
1294 sub save {
1295 my ($self) = @_;
1296
1297 my $lock = cf::lock_acquire "map_data:" . $self->path;
1298
1299 $self->{last_save} = $cf::RUNTIME;
1300
1301 return unless $self->dirty;
1302
1303 my $save = $self->{path}->save_path; utf8::encode $save;
1304 my $uniq = $self->{path}->uniq_path; utf8::encode $uniq;
1305
1306 $self->{load_path} = $save;
1307
1308 return if $self->{deny_save};
1309
1310 local $self->{last_access} = $self->last_access;#d#
1311
1312 if ($uniq) {
1313 $self->save_objects ($save, cf::IO_HEADER | cf::IO_OBJECTS);
1314 $self->save_objects ($uniq, cf::IO_UNIQUES);
1315 } else {
1316 $self->save_objects ($save, cf::IO_HEADER | cf::IO_OBJECTS | cf::IO_UNIQUES);
1317 }
1318 }
1319
1320 sub swap_out {
1321 my ($self) = @_;
1322
1323 # save first because save cedes
1324 $self->save;
1325
1326 my $lock = cf::lock_acquire "map_data:" . $self->path;
1327
1328 return if $self->players;
1329 return if $self->in_memory != cf::MAP_IN_MEMORY;
1330 return if $self->{deny_save};
1331
1332 $self->clear;
1333 $self->in_memory (cf::MAP_SWAPPED);
1334 }
1335
1336 sub reset_at {
1337 my ($self) = @_;
1338
1339 # TODO: safety, remove and allow resettable per-player maps
1340 return 1e99 if $self->{path}{user_rel};
1341 return 1e99 if $self->{deny_reset};
1342
1343 my $time = $self->fixed_resettime ? $self->{instantiate_time} : $self->last_access;
1344 my $to = List::Util::min $MAX_RESET, $self->reset_timeout || $DEFAULT_RESET;
1345
1346 $time + $to
1347 }
1348
1349 sub should_reset {
1350 my ($self) = @_;
1351
1352 $self->reset_at <= $cf::RUNTIME
1353 }
1354
1355 sub unlink_save {
1356 my ($self) = @_;
1357
1358 utf8::encode (my $save = $self->{path}->save_path);
1359 aioreq_pri 3; IO::AIO::aio_unlink $save;
1360 aioreq_pri 3; IO::AIO::aio_unlink "$save.pst";
1361 }
1362
1363 sub rename {
1364 my ($self, $new_path) = @_;
1365
1366 $self->unlink_save;
1367
1368 delete $cf::MAP{$self->path};
1369 $self->{path} = new cf::path $new_path;
1370 $self->path ($self->{path}->as_string);
1371 $cf::MAP{$self->path} = $self;
1372
1373 $self->save;
1374 }
1375
1376 sub reset {
1377 my ($self) = @_;
1378
1379 my $lock = cf::lock_acquire "map_data:" . $self->path;
1380
1381 return if $self->players;
1382 return if $self->{path}{user_rel};#d#
1383
1384 warn "resetting map ", $self->path;#d#
1385
1386 delete $cf::MAP{$self->path};
1387
1388 $_->clear_links_to ($self) for values %cf::MAP;
1389
1390 $self->unlink_save;
1391 $self->destroy;
1392 }
1393
1394 my $nuke_counter = "aaaa";
1395
1396 sub nuke {
1397 my ($self) = @_;
1398
1399 $self->{deny_save} = 1;
1400 $self->reset_timeout (1);
1401 $self->rename ("{nuke}/" . ($nuke_counter++));
1402 $self->reset; # polite request, might not happen
1403 }
1404
1405 sub customise_for {
1406 my ($map, $ob) = @_;
1407
1408 if ($map->per_player) {
1409 return cf::map::find "~" . $ob->name . "/" . $map->{path}{path};
1410 }
1411
1412 $map
1413 }
1414
1415 sub emergency_save {
1416 my $freeze_guard = cf::freeze_mainloop;
1417
1418 warn "enter emergency map save\n";
1419
1420 cf::sync_job {
1421 warn "begin emergency map save\n";
1422 $_->save for values %cf::MAP;
1423 };
1424
1425 warn "end emergency map save\n";
1426 }
1427
1428 package cf;
1429
1430 =back
1431
1432
1433 =head3 cf::object::player
1434
1435 =over 4
1436
1437 =item $player_object->reply ($npc, $msg[, $flags])
1438
1439 Sends a message to the player, as if the npc C<$npc> replied. C<$npc>
1440 can be C<undef>. Does the right thing when the player is currently in a
1441 dialogue with the given NPC character.
1442
1443 =cut
1444
1445 # rough implementation of a future "reply" method that works
1446 # with dialog boxes.
1447 #TODO: the first argument must go, split into a $npc->reply_to ( method
1448 sub cf::object::player::reply($$$;$) {
1449 my ($self, $npc, $msg, $flags) = @_;
1450
1451 $flags = cf::NDI_BROWN | cf::NDI_UNIQUE unless @_ >= 4;
1452
1453 if ($self->{record_replies}) {
1454 push @{ $self->{record_replies} }, [$npc, $msg, $flags];
1455 } else {
1456 $msg = $npc->name . " says: $msg" if $npc;
1457 $self->message ($msg, $flags);
1458 }
1459 }
1460
1461 =item $player_object->may ("access")
1462
1463 Returns wether the given player is authorized to access resource "access"
1464 (e.g. "command_wizcast").
1465
1466 =cut
1467
1468 sub cf::object::player::may {
1469 my ($self, $access) = @_;
1470
1471 $self->flag (cf::FLAG_WIZ) ||
1472 (ref $cf::CFG{"may_$access"}
1473 ? scalar grep $self->name eq $_, @{$cf::CFG{"may_$access"}}
1474 : $cf::CFG{"may_$access"})
1475 }
1476
1477 =item $player_object->enter_link
1478
1479 Freezes the player and moves him/her to a special map (C<{link}>).
1480
1481 The player should be reaosnably safe there for short amounts of time. You
1482 I<MUST> call C<leave_link> as soon as possible, though.
1483
1484 =item $player_object->leave_link ($map, $x, $y)
1485
1486 Moves the player out of the specila link map onto the given map. If the
1487 map is not valid (or omitted), the player will be moved back to the
1488 location he/she was before the call to C<enter_link>, or, if that fails,
1489 to the emergency map position.
1490
1491 Might block.
1492
1493 =cut
1494
1495 sub cf::object::player::enter_link {
1496 my ($self) = @_;
1497
1498 $self->deactivate_recursive;
1499
1500 return if $self->map == $LINK_MAP;
1501
1502 $self->{_link_pos} ||= [$self->map->{path}, $self->x, $self->y]
1503 if $self->map;
1504
1505 $self->enter_map ($LINK_MAP, 20, 20);
1506 }
1507
1508 sub cf::object::player::leave_link {
1509 my ($self, $map, $x, $y) = @_;
1510
1511 my $link_pos = delete $self->{_link_pos};
1512
1513 unless ($map) {
1514 # restore original map position
1515 ($map, $x, $y) = @{ $link_pos || [] };
1516 $map = cf::map::find $map;
1517
1518 unless ($map) {
1519 ($map, $x, $y) = @$EMERGENCY_POSITION;
1520 $map = cf::map::find $map
1521 or die "FATAL: cannot load emergency map\n";
1522 }
1523 }
1524
1525 ($x, $y) = (-1, -1)
1526 unless (defined $x) && (defined $y);
1527
1528 # use -1 or undef as default coordinates, not 0, 0
1529 ($x, $y) = ($map->enter_x, $map->enter_y)
1530 if $x <=0 && $y <= 0;
1531
1532 $map->load;
1533
1534 $self->activate_recursive;
1535 $self->enter_map ($map, $x, $y);
1536 }
1537
1538 cf::player->attach (
1539 on_logout => sub {
1540 my ($pl) = @_;
1541
1542 # abort map switching before logout
1543 if ($pl->ob->{_link_pos}) {
1544 cf::sync_job {
1545 $pl->ob->leave_link
1546 };
1547 }
1548 },
1549 on_login => sub {
1550 my ($pl) = @_;
1551
1552 # try to abort aborted map switching on player login :)
1553 # should happen only on crashes
1554 if ($pl->ob->{_link_pos}) {
1555
1556 $pl->ob->enter_link;
1557 (async {
1558 # we need this sleep as the login has a concurrent enter_exit running
1559 # and this sleep increases chances of the player not ending up in scorn
1560 $pl->ob->reply (undef,
1561 "There was an internal problem at your last logout, "
1562 . "the server will try to bring you to your intended destination in a second.",
1563 cf::NDI_RED);
1564 Coro::Timer::sleep 1;
1565 $pl->ob->leave_link;
1566 })->prio (2);
1567 }
1568 },
1569 );
1570
1571 =item $player_object->goto ($path, $x, $y)
1572
1573 =cut
1574
1575 sub cf::object::player::goto {
1576 my ($self, $path, $x, $y) = @_;
1577
1578 $self->enter_link;
1579
1580 (async {
1581 $path = new cf::path $path;
1582
1583 my $map = cf::map::find $path->as_string;
1584 $map = $map->customise_for ($self) if $map;
1585
1586 # warn "entering ", $map->path, " at ($x, $y)\n"
1587 # if $map;
1588
1589 $map or $self->message ("The exit is closed", cf::NDI_UNIQUE | cf::NDI_RED);
1590
1591 $self->leave_link ($map, $x, $y);
1592 })->prio (1);
1593 }
1594
1595 =item $player_object->enter_exit ($exit_object)
1596
1597 =cut
1598
1599 sub parse_random_map_params {
1600 my ($spec) = @_;
1601
1602 my $rmp = { # defaults
1603 xsize => 10,
1604 ysize => 10,
1605 };
1606
1607 for (split /\n/, $spec) {
1608 my ($k, $v) = split /\s+/, $_, 2;
1609
1610 $rmp->{lc $k} = $v if (length $k) && (length $v);
1611 }
1612
1613 $rmp
1614 }
1615
1616 sub prepare_random_map {
1617 my ($exit) = @_;
1618
1619 # all this does is basically replace the /! path by
1620 # a new random map path (?random/...) with a seed
1621 # that depends on the exit object
1622
1623 my $rmp = parse_random_map_params $exit->msg;
1624
1625 if ($exit->map) {
1626 $rmp->{region} = $exit->map->region_name;
1627 $rmp->{origin_map} = $exit->map->path;
1628 $rmp->{origin_x} = $exit->x;
1629 $rmp->{origin_y} = $exit->y;
1630 }
1631
1632 $rmp->{random_seed} ||= $exit->random_seed;
1633
1634 my $data = cf::to_json $rmp;
1635 my $md5 = Digest::MD5::md5_hex $data;
1636
1637 if (my $fh = aio_open "$cf::RANDOM_MAPS/$md5.meta", O_WRONLY | O_CREAT, 0666) {
1638 aio_write $fh, 0, (length $data), $data, 0;
1639
1640 $exit->slaying ("?random/$md5");
1641 $exit->msg (undef);
1642 }
1643 }
1644
1645 sub cf::object::player::enter_exit {
1646 my ($self, $exit) = @_;
1647
1648 return unless $self->type == cf::PLAYER;
1649
1650 $self->enter_link;
1651
1652 (async {
1653 $self->deactivate_recursive; # just to be sure
1654 unless (eval {
1655 prepare_random_map $exit
1656 if $exit->slaying eq "/!";
1657
1658 my $path = new cf::path $exit->slaying, $exit->map && $exit->map->path;
1659 $self->goto ($path, $exit->stats->hp, $exit->stats->sp);
1660
1661 1;
1662 }) {
1663 $self->message ("Something went wrong deep within the crossfire server. "
1664 . "I'll try to bring you back to the map you were before. "
1665 . "Please report this to the dungeon master",
1666 cf::NDI_UNIQUE | cf::NDI_RED);
1667
1668 warn "ERROR in enter_exit: $@";
1669 $self->leave_link;
1670 }
1671 })->prio (1);
1672 }
1673
1674 =head3 cf::client
1675
1676 =over 4
1677
1678 =item $client->send_drawinfo ($text, $flags)
1679
1680 Sends a drawinfo packet to the client. Circumvents output buffering so
1681 should not be used under normal circumstances.
1682
1683 =cut
1684
1685 sub cf::client::send_drawinfo {
1686 my ($self, $text, $flags) = @_;
1687
1688 utf8::encode $text;
1689 $self->send_packet (sprintf "drawinfo %d %s", $flags, $text);
1690 }
1691
1692
1693 =item $success = $client->query ($flags, "text", \&cb)
1694
1695 Queues a query to the client, calling the given callback with
1696 the reply text on a reply. flags can be C<cf::CS_QUERY_YESNO>,
1697 C<cf::CS_QUERY_SINGLECHAR> or C<cf::CS_QUERY_HIDEINPUT> or C<0>.
1698
1699 Queries can fail, so check the return code. Or don't, as queries will become
1700 reliable at some point in the future.
1701
1702 =cut
1703
1704 sub cf::client::query {
1705 my ($self, $flags, $text, $cb) = @_;
1706
1707 return unless $self->state == ST_PLAYING
1708 || $self->state == ST_SETUP
1709 || $self->state == ST_CUSTOM;
1710
1711 $self->state (ST_CUSTOM);
1712
1713 utf8::encode $text;
1714 push @{ $self->{query_queue} }, [(sprintf "query %d %s", $flags, $text), $cb];
1715
1716 $self->send_packet ($self->{query_queue}[0][0])
1717 if @{ $self->{query_queue} } == 1;
1718 }
1719
1720 cf::client->attach (
1721 on_reply => sub {
1722 my ($ns, $msg) = @_;
1723
1724 # this weird shuffling is so that direct followup queries
1725 # get handled first
1726 my $queue = delete $ns->{query_queue}
1727 or return; # be conservative, not sure how that can happen, but we saw a crash here
1728
1729 (shift @$queue)->[1]->($msg);
1730
1731 push @{ $ns->{query_queue} }, @$queue;
1732
1733 if (@{ $ns->{query_queue} } == @$queue) {
1734 if (@$queue) {
1735 $ns->send_packet ($ns->{query_queue}[0][0]);
1736 } else {
1737 $ns->state (ST_PLAYING) if $ns->state == ST_CUSTOM;
1738 }
1739 }
1740 },
1741 );
1742
1743 =item $client->async (\&cb)
1744
1745 Create a new coroutine, running the specified callback. The coroutine will
1746 be automatically cancelled when the client gets destroyed (e.g. on logout,
1747 or loss of connection).
1748
1749 =cut
1750
1751 sub cf::client::async {
1752 my ($self, $cb) = @_;
1753
1754 my $coro = &Coro::async ($cb);
1755
1756 $coro->on_destroy (sub {
1757 delete $self->{_coro}{$coro+0};
1758 });
1759
1760 $self->{_coro}{$coro+0} = $coro;
1761
1762 $coro
1763 }
1764
1765 cf::client->attach (
1766 on_destroy => sub {
1767 my ($ns) = @_;
1768
1769 $_->cancel for values %{ (delete $ns->{_coro}) || {} };
1770 },
1771 );
1772
1773 =back
1774
1775
1776 =head2 SAFE SCRIPTING
1777
1778 Functions that provide a safe environment to compile and execute
1779 snippets of perl code without them endangering the safety of the server
1780 itself. Looping constructs, I/O operators and other built-in functionality
1781 is not available in the safe scripting environment, and the number of
1782 functions and methods that can be called is greatly reduced.
1783
1784 =cut
1785
1786 our $safe = new Safe "safe";
1787 our $safe_hole = new Safe::Hole;
1788
1789 $SIG{FPE} = 'IGNORE';
1790
1791 $safe->permit_only (Opcode::opset qw(:base_core :base_mem :base_orig :base_math sort time));
1792
1793 # here we export the classes and methods available to script code
1794
1795 =pod
1796
1797 The following fucntions and emthods are available within a safe environment:
1798
1799 cf::object contr pay_amount pay_player map
1800 cf::object::player player
1801 cf::player peaceful
1802 cf::map trigger
1803
1804 =cut
1805
1806 for (
1807 ["cf::object" => qw(contr pay_amount pay_player map)],
1808 ["cf::object::player" => qw(player)],
1809 ["cf::player" => qw(peaceful)],
1810 ["cf::map" => qw(trigger)],
1811 ) {
1812 no strict 'refs';
1813 my ($pkg, @funs) = @$_;
1814 *{"safe::$pkg\::$_"} = $safe_hole->wrap (\&{"$pkg\::$_"})
1815 for @funs;
1816 }
1817
1818 =over 4
1819
1820 =item @retval = safe_eval $code, [var => value, ...]
1821
1822 Compiled and executes the given perl code snippet. additional var/value
1823 pairs result in temporary local (my) scalar variables of the given name
1824 that are available in the code snippet. Example:
1825
1826 my $five = safe_eval '$first + $second', first => 1, second => 4;
1827
1828 =cut
1829
1830 sub safe_eval($;@) {
1831 my ($code, %vars) = @_;
1832
1833 my $qcode = $code;
1834 $qcode =~ s/"/‟/g; # not allowed in #line filenames
1835 $qcode =~ s/\n/\\n/g;
1836
1837 local $_;
1838 local @safe::cf::_safe_eval_args = values %vars;
1839
1840 my $eval =
1841 "do {\n"
1842 . "my (" . (join ",", map "\$$_", keys %vars) . ") = \@cf::_safe_eval_args;\n"
1843 . "#line 0 \"{$qcode}\"\n"
1844 . $code
1845 . "\n}"
1846 ;
1847
1848 sub_generation_inc;
1849 my @res = wantarray ? $safe->reval ($eval) : scalar $safe->reval ($eval);
1850 sub_generation_inc;
1851
1852 if ($@) {
1853 warn "$@";
1854 warn "while executing safe code '$code'\n";
1855 warn "with arguments " . (join " ", %vars) . "\n";
1856 }
1857
1858 wantarray ? @res : $res[0]
1859 }
1860
1861 =item cf::register_script_function $function => $cb
1862
1863 Register a function that can be called from within map/npc scripts. The
1864 function should be reasonably secure and should be put into a package name
1865 like the extension.
1866
1867 Example: register a function that gets called whenever a map script calls
1868 C<rent::overview>, as used by the C<rent> extension.
1869
1870 cf::register_script_function "rent::overview" => sub {
1871 ...
1872 };
1873
1874 =cut
1875
1876 sub register_script_function {
1877 my ($fun, $cb) = @_;
1878
1879 no strict 'refs';
1880 *{"safe::$fun"} = $safe_hole->wrap ($cb);
1881 }
1882
1883 =back
1884
1885 =cut
1886
1887 #############################################################################
1888
1889 =head2 EXTENSION DATABASE SUPPORT
1890
1891 Crossfire maintains a very simple database for extension use. It can
1892 currently store anything that can be serialised using Storable, which
1893 excludes objects.
1894
1895 The parameter C<$family> should best start with the name of the extension
1896 using it, it should be unique.
1897
1898 =over 4
1899
1900 =item $hashref = cf::db_get $family
1901
1902 Return a hashref for use by the extension C<$family>, which can be
1903 modified. After modifications, you have to call C<cf::db_dirty> or
1904 C<cf::db_sync>.
1905
1906 =item $value = cf::db_get $family => $key
1907
1908 Returns a single value from the database
1909
1910 =item cf::db_put $family => $hashref
1911
1912 Stores the given family hashref into the database. Updates are delayed, if
1913 you want the data to be synced to disk immediately, use C<cf::db_sync>.
1914
1915 =item cf::db_put $family => $key => $value
1916
1917 Stores the given C<$value> in the family hash. Updates are delayed, if you
1918 want the data to be synced to disk immediately, use C<cf::db_sync>.
1919
1920 =item cf::db_dirty
1921
1922 Marks the database as dirty, to be updated at a later time.
1923
1924 =item cf::db_sync
1925
1926 Immediately write the database to disk I<if it is dirty>.
1927
1928 =cut
1929
1930 our $DB;
1931
1932 {
1933 my $path = cf::localdir . "/database.pst";
1934
1935 sub db_load() {
1936 $DB = stat $path ? Storable::retrieve $path : { };
1937 }
1938
1939 my $pid;
1940
1941 sub db_save() {
1942 waitpid $pid, 0 if $pid;
1943 if (0 == ($pid = fork)) {
1944 $DB->{_meta}{version} = 1;
1945 Storable::nstore $DB, "$path~";
1946 rename "$path~", $path;
1947 cf::_exit 0 if defined $pid;
1948 }
1949 }
1950
1951 my $dirty;
1952
1953 sub db_sync() {
1954 db_save if $dirty;
1955 undef $dirty;
1956 }
1957
1958 my $idle = Event->idle (min => $TICK * 2.8, max => 10, repeat => 0, data => WF_AUTOCANCEL, cb => sub {
1959 db_sync;
1960 });
1961
1962 sub db_dirty() {
1963 $dirty = 1;
1964 $idle->start;
1965 }
1966
1967 sub db_get($;$) {
1968 @_ >= 2
1969 ? $DB->{$_[0]}{$_[1]}
1970 : ($DB->{$_[0]} ||= { })
1971 }
1972
1973 sub db_put($$;$) {
1974 if (@_ >= 3) {
1975 $DB->{$_[0]}{$_[1]} = $_[2];
1976 } else {
1977 $DB->{$_[0]} = $_[1];
1978 }
1979 db_dirty;
1980 }
1981
1982 cf::global->attach (
1983 prio => 10000,
1984 on_cleanup => sub {
1985 db_sync;
1986 },
1987 );
1988 }
1989
1990 #############################################################################
1991 # the server's main()
1992
1993 sub cfg_load {
1994 open my $fh, "<:utf8", cf::confdir . "/config"
1995 or return;
1996
1997 local $/;
1998 *CFG = YAML::Syck::Load <$fh>;
1999
2000 $EMERGENCY_POSITION = $CFG{emergency_position} || ["/world/world_105_115", 5, 37];
2001
2002 $cf::map::MAX_RESET = $CFG{map_max_reset} if exists $CFG{map_max_reset};
2003 $cf::map::DEFAULT_RESET = $CFG{map_default_reset} if exists $CFG{map_default_reset};
2004
2005 if (exists $CFG{mlockall}) {
2006 eval {
2007 $CFG{mlockall} ? &mlockall : &munlockall
2008 and die "WARNING: m(un)lockall failed: $!\n";
2009 };
2010 warn $@ if $@;
2011 }
2012 }
2013
2014 sub main {
2015 # we must not ever block the main coroutine
2016 local $Coro::idle = sub {
2017 Carp::cluck "FATAL: Coro::idle was called, major BUG, use cf::sync_job!\n";#d#
2018 async { Event::one_event };
2019 };
2020
2021 cfg_load;
2022 db_load;
2023 load_extensions;
2024 Event::loop;
2025 }
2026
2027 #############################################################################
2028 # initialisation
2029
2030 sub reload() {
2031 # can/must only be called in main
2032 if ($Coro::current != $Coro::main) {
2033 warn "can only reload from main coroutine\n";
2034 return;
2035 }
2036
2037 warn "reloading...";
2038
2039 my $guard = freeze_mainloop;
2040 cf::emergency_save;
2041
2042 eval {
2043 # if anything goes wrong in here, we should simply crash as we already saved
2044
2045 # cancel all watchers
2046 for (Event::all_watchers) {
2047 $_->cancel if $_->data & WF_AUTOCANCEL;
2048 }
2049
2050 # cancel all extension coros
2051 $_->cancel for values %EXT_CORO;
2052 %EXT_CORO = ();
2053
2054 # unload all extensions
2055 for (@exts) {
2056 warn "unloading <$_>";
2057 unload_extension $_;
2058 }
2059
2060 # unload all modules loaded from $LIBDIR
2061 while (my ($k, $v) = each %INC) {
2062 next unless $v =~ /^\Q$LIBDIR\E\/.*\.pm$/;
2063
2064 warn "removing <$k>";
2065 delete $INC{$k};
2066
2067 $k =~ s/\.pm$//;
2068 $k =~ s/\//::/g;
2069
2070 if (my $cb = $k->can ("unload_module")) {
2071 $cb->();
2072 }
2073
2074 Symbol::delete_package $k;
2075 }
2076
2077 # sync database to disk
2078 cf::db_sync;
2079 IO::AIO::flush;
2080
2081 # get rid of safe::, as good as possible
2082 Symbol::delete_package "safe::$_"
2083 for qw(cf::attachable cf::object cf::object::player cf::client cf::player cf::map cf::party cf::region);
2084
2085 # remove register_script_function callbacks
2086 # TODO
2087
2088 # unload cf.pm "a bit"
2089 delete $INC{"cf.pm"};
2090
2091 # don't, removes xs symbols, too,
2092 # and global variables created in xs
2093 #Symbol::delete_package __PACKAGE__;
2094
2095 # reload cf.pm
2096 warn "reloading cf.pm";
2097 require cf;
2098 cf::_connect_to_perl; # nominally unnecessary, but cannot hurt
2099
2100 # load config and database again
2101 cf::cfg_load;
2102 cf::db_load;
2103
2104 # load extensions
2105 warn "load extensions";
2106 cf::load_extensions;
2107
2108 # reattach attachments to objects
2109 warn "reattach";
2110 _global_reattach;
2111 };
2112
2113 if ($@) {
2114 warn $@;
2115 warn "error while reloading, exiting.";
2116 exit 1;
2117 }
2118
2119 warn "reloaded successfully";
2120 };
2121
2122 #############################################################################
2123
2124 unless ($LINK_MAP) {
2125 $LINK_MAP = cf::map::new;
2126
2127 $LINK_MAP->width (41);
2128 $LINK_MAP->height (41);
2129 $LINK_MAP->alloc;
2130 $LINK_MAP->path ("{link}");
2131 $LINK_MAP->{path} = bless { path => "{link}" }, "cf::path";
2132 $LINK_MAP->in_memory (MAP_IN_MEMORY);
2133
2134 # dirty hack because... archetypes are not yet loaded
2135 Event->timer (
2136 after => 2,
2137 cb => sub {
2138 $_[0]->w->cancel;
2139
2140 # provide some exits "home"
2141 my $exit = cf::object::new "exit";
2142
2143 $exit->slaying ($EMERGENCY_POSITION->[0]);
2144 $exit->stats->hp ($EMERGENCY_POSITION->[1]);
2145 $exit->stats->sp ($EMERGENCY_POSITION->[2]);
2146
2147 $LINK_MAP->insert ($exit->clone, 19, 19);
2148 $LINK_MAP->insert ($exit->clone, 19, 20);
2149 $LINK_MAP->insert ($exit->clone, 19, 21);
2150 $LINK_MAP->insert ($exit->clone, 20, 19);
2151 $LINK_MAP->insert ($exit->clone, 20, 21);
2152 $LINK_MAP->insert ($exit->clone, 21, 19);
2153 $LINK_MAP->insert ($exit->clone, 21, 20);
2154 $LINK_MAP->insert ($exit->clone, 21, 21);
2155
2156 $exit->destroy;
2157 });
2158
2159 $LINK_MAP->{deny_save} = 1;
2160 $LINK_MAP->{deny_reset} = 1;
2161
2162 $cf::MAP{$LINK_MAP->path} = $LINK_MAP;
2163 }
2164
2165 register "<global>", __PACKAGE__;
2166
2167 register_command "reload" => sub {
2168 my ($who, $arg) = @_;
2169
2170 if ($who->flag (FLAG_WIZ)) {
2171 $who->message ("start of reload.");
2172 reload;
2173 $who->message ("end of reload.");
2174 }
2175 };
2176
2177 unshift @INC, $LIBDIR;
2178
2179 $TICK_WATCHER = Event->timer (
2180 reentrant => 0,
2181 prio => 0,
2182 at => $NEXT_TICK || $TICK,
2183 data => WF_AUTOCANCEL,
2184 cb => sub {
2185 cf::server_tick; # one server iteration
2186 $RUNTIME += $TICK;
2187 $NEXT_TICK += $TICK;
2188
2189 # if we are delayed by four ticks or more, skip them all
2190 $NEXT_TICK = Event::time if Event::time >= $NEXT_TICK + $TICK * 4;
2191
2192 $TICK_WATCHER->at ($NEXT_TICK);
2193 $TICK_WATCHER->start;
2194 },
2195 );
2196
2197 IO::AIO::max_poll_time $TICK * 0.2;
2198
2199 Event->io (
2200 fd => IO::AIO::poll_fileno,
2201 poll => 'r',
2202 prio => 5,
2203 data => WF_AUTOCANCEL,
2204 cb => \&IO::AIO::poll_cb,
2205 );
2206
2207 Event->timer (
2208 data => WF_AUTOCANCEL,
2209 after => 0,
2210 interval => 10,
2211 cb => sub {
2212 (Coro::unblock_sub {
2213 write_runtime
2214 or warn "ERROR: unable to write runtime file: $!";
2215 })->();
2216 },
2217 );
2218
2219 END { cf::emergency_save }
2220
2221 1
2222