ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.57
Committed: Wed Aug 30 11:21:24 2006 UTC (17 years, 9 months ago) by root
Branch: MAIN
Changes since 1.56: +1 -2 lines
Log Message:
*** empty log message ***

File Contents

# User Rev Content
1 root 1.1 package cf;
2    
3     use Symbol;
4     use List::Util;
5 root 1.6 use Storable;
6 root 1.23 use Opcode;
7     use Safe;
8     use Safe::Hole;
9 root 1.19
10 root 1.32 use Time::HiRes;
11 root 1.18 use Event;
12 root 1.19 $Event::Eval = 1; # no idea why this is required, but it is
13 root 1.1
14     use strict;
15    
16 root 1.47 _reload_1;
17    
18 root 1.39 our %COMMAND = ();
19 root 1.1 our @EVENT;
20     our %PROP_TYPE;
21     our %PROP_IDX;
22 root 1.27 our $LIBDIR = maps_directory "perl";
23 root 1.1
24 root 1.35 our $TICK = MAX_TIME * 1e-6;
25     our $TICK_WATCHER;
26     our $NEXT_TICK;
27    
28 root 1.1 BEGIN {
29     *CORE::GLOBAL::warn = sub {
30     my $msg = join "", @_;
31     $msg .= "\n"
32     unless $msg =~ /\n$/;
33    
34     print STDERR "cfperl: $msg";
35     LOG llevError, "cfperl: $msg";
36     };
37     }
38    
39 root 1.9 my %ignore_set = (MAP_PROP_PATH => 1); # I hate the plug-in api. Deeply!
40    
41 root 1.1 # generate property mutators
42     sub prop_gen {
43     my ($prefix, $class) = @_;
44    
45     no strict 'refs';
46    
47     for my $prop (keys %PROP_TYPE) {
48     $prop =~ /^\Q$prefix\E_(.*$)/ or next;
49     my $sub = lc $1;
50    
51     my $type = $PROP_TYPE{$prop};
52     my $idx = $PROP_IDX {$prop};
53    
54     *{"$class\::get_$sub"} = *{"$class\::$sub"} = sub {
55     $_[0]->get_property ($type, $idx)
56     };
57    
58     *{"$class\::set_$sub"} = sub {
59     $_[0]->set_property ($type, $idx, $_[1]);
60 root 1.9 } unless $ignore_set{$prop};
61 root 1.1 }
62     }
63    
64     # auto-generate most of the API
65    
66     prop_gen OBJECT_PROP => "cf::object";
67     # CFAPI_OBJECT_ANIMATION?
68     prop_gen PLAYER_PROP => "cf::object::player";
69    
70     prop_gen MAP_PROP => "cf::map";
71     prop_gen ARCH_PROP => "cf::arch";
72    
73 root 1.41 @safe::cf::object::player::ISA = @cf::object::player::ISA = 'cf::object';
74 root 1.25
75 root 1.41 # we bless all objects into (empty) derived classes to force a method lookup
76 root 1.25 # within the Safe compartment.
77 root 1.50 for my $pkg (qw(cf::object cf::object::player cf::player cf::map cf::party cf::region cf::arch cf::living)) {
78 root 1.25 no strict 'refs';
79 root 1.41 @{"safe::$pkg\::wrap::ISA"} = @{"$pkg\::wrap::ISA"} = $pkg;
80 root 1.25 }
81 root 1.1
82 root 1.18 $Event::DIED = sub {
83     warn "error in event callback: @_";
84     };
85    
86 root 1.5 my %ext_pkg;
87 root 1.1 my @exts;
88     my @hook;
89     my %command;
90 root 1.15 my %extcmd;
91 root 1.1
92 root 1.39 #############################################################################
93 root 1.45 # utility functions
94 root 1.44
95 root 1.45 use JSON::Syck (); # TODO# replace by JSON::PC once working
96 root 1.44
97 root 1.45 sub from_json($) {
98     $JSON::Syck::ImplicitUnicode = 1; # work around JSON::Syck bugs
99     JSON::Syck::Load $_[0]
100 root 1.44 }
101    
102 root 1.45 sub to_json($) {
103     $JSON::Syck::ImplicitUnicode = 0; # work around JSON::Syck bugs
104     JSON::Syck::Dump $_[0]
105 root 1.44 }
106    
107     #############################################################################
108 root 1.39 # "new" plug-in system
109    
110 root 1.55 =head3 EVENTS AND OBJECT ATTACHMENTS
111    
112     =over 4
113    
114 root 1.48 =item $object->attach ($attachment, key => $value...)
115 root 1.46
116 root 1.53 =item $object->detach ($attachment)
117    
118     Attach/detach a pre-registered attachment to an object.
119 root 1.46
120 root 1.48 =item $player->attach ($attachment, key => $value...)
121 root 1.46
122 root 1.53 =item $player->detach ($attachment)
123    
124     Attach/detach a pre-registered attachment to a player.
125    
126     =item $map->attach ($attachment, key => $value...)
127 root 1.46
128 root 1.53 =item $map->detach ($attachment)
129 root 1.46
130 root 1.53 Attach/detach a pre-registered attachment to a map.
131 root 1.39
132 root 1.55 =item $bool = $object->attached ($name)
133    
134     =item $bool = $player->attached ($name)
135    
136     =item $bool = $map->attached ($name)
137    
138     Checks wether the named attachment is currently attached to the object.
139    
140 root 1.40 =item cf::attach_global ...
141 root 1.39
142 root 1.46 Attach handlers for global events.
143    
144     This and all following C<attach_*>-functions expect any number of the
145     following handler/hook descriptions:
146    
147     =over 4
148    
149     =item prio => $number
150    
151     Set the priority for all following handlers/hooks (unless overwritten
152     by another C<prio> setting). Lower priority handlers get executed
153     earlier. The default priority is C<0>, and many built-in handlers are
154     registered at priority C<-1000>, so lower priorities should not be used
155     unless you know what you are doing.
156    
157     =item on_I<event> => \&cb
158    
159     Call the given code reference whenever the named event happens (event is
160     something like C<instantiate>, C<apply>, C<use_skill> and so on, and which
161     handlers are recognised generally depends on the type of object these
162     handlers attach to).
163    
164     See F<include/eventinc.h> for the full list of events supported, and their
165     class.
166    
167     =item package => package::
168    
169     Look for sub functions of the name C<< on_I<event> >> in the given
170     package and register them. Only handlers for eevents supported by the
171     object/class are recognised.
172    
173     =back
174    
175 root 1.47 =item cf::attach_to_type $object_type, $subtype, ...
176 root 1.39
177 root 1.47 Attach handlers for a specific object type (e.g. TRANSPORT) and
178     subtype. If C<$subtype> is zero or undef, matches all objects of the given
179     type.
180 root 1.46
181 root 1.40 =item cf::attach_to_objects ...
182 root 1.39
183 root 1.46 Attach handlers to all objects. Do not use this except for debugging or
184     very rare events, as handlers are (obviously) called for I<all> objects in
185     the game.
186    
187 root 1.40 =item cf::attach_to_players ...
188 root 1.39
189 root 1.46 Attach handlers to all players.
190    
191 root 1.40 =item cf::attach_to_maps ...
192 root 1.39
193 root 1.46 Attach handlers to all maps.
194    
195 root 1.45 =item cf:register_attachment $name, ...
196    
197 root 1.52 Register an attachment by name through which objects can refer to this
198     attachment.
199    
200 root 1.55 =item cf:register_player_attachment $name, ...
201    
202     Register an attachment by name through which players can refer to this
203     attachment.
204    
205 root 1.52 =item cf:register_map_attachment $name, ...
206    
207     Register an attachment by name through which maps can refer to this
208     attachment.
209    
210 root 1.39 =cut
211    
212 root 1.40 # the following variables are defined in .xs and must not be re-created
213 root 1.39 our @CB_GLOBAL = (); # registry for all global events
214 root 1.45 our @CB_OBJECT = (); # all objects (should not be used except in emergency)
215 root 1.40 our @CB_PLAYER = ();
216 root 1.39 our @CB_TYPE = (); # registry for type (cf-object class) based events
217 root 1.40 our @CB_MAP = ();
218 root 1.39
219 root 1.45 my %attachment;
220    
221 root 1.39 sub _attach_cb($\%$$$) {
222     my ($registry, $undo, $event, $prio, $cb) = @_;
223    
224     use sort 'stable';
225    
226     $cb = [$prio, $cb];
227    
228     @{$registry->[$event]} = sort
229     { $a->[0] cmp $b->[0] }
230     @{$registry->[$event] || []}, $cb;
231    
232     push @{$undo->{cb}}, [$event, $cb];
233     }
234    
235     # attach handles attaching event callbacks
236     # the only thing the caller has to do is pass the correct
237     # registry (== where the callback attaches to).
238 root 1.45 sub _attach(\@$@) {
239     my ($registry, $klass, @arg) = @_;
240 root 1.39
241     my $prio = 0;
242    
243     my %undo = (
244     registry => $registry,
245     cb => [],
246     );
247    
248     my %cb_id = map +("on_" . lc $EVENT[$_][0], $_) , grep $EVENT[$_][1] == $klass, 0 .. $#EVENT;
249    
250 root 1.45 while (@arg) {
251     my $type = shift @arg;
252 root 1.39
253     if ($type eq "prio") {
254 root 1.45 $prio = shift @arg;
255 root 1.39
256     } elsif ($type eq "package") {
257 root 1.45 my $pkg = shift @arg;
258 root 1.39
259     while (my ($name, $id) = each %cb_id) {
260     if (my $cb = $pkg->can ($name)) {
261     _attach_cb $registry, %undo, $id, $prio, $cb;
262     }
263     }
264    
265     } elsif (exists $cb_id{$type}) {
266 root 1.45 _attach_cb $registry, %undo, $cb_id{$type}, $prio, shift @arg;
267 root 1.39
268     } elsif (ref $type) {
269     warn "attaching objects not supported, ignoring.\n";
270    
271     } else {
272 root 1.45 shift @arg;
273 root 1.39 warn "attach argument '$type' not supported, ignoring.\n";
274     }
275     }
276    
277     \%undo
278     }
279    
280 root 1.46 sub _attach_attachment {
281 root 1.48 my ($obj, $name, %arg) = @_;
282 root 1.46
283 root 1.55 return if exists $obj->{_attachment}{$name};
284    
285 root 1.46 my $res;
286    
287     if (my $attach = $attachment{$name}) {
288     my $registry = $obj->registry;
289    
290 root 1.47 for (@$attach) {
291     my ($klass, @attach) = @$_;
292     $res = _attach @$registry, $klass, @attach;
293     }
294 root 1.46
295 root 1.48 $obj->{$name} = \%arg;
296 root 1.46 } else {
297     warn "object uses attachment '$name' that is not available, postponing.\n";
298     }
299    
300 root 1.50 $obj->{_attachment}{$name} = undef;
301 root 1.46
302     $res->{attachment} = $name;
303     $res
304     }
305    
306 root 1.54 *cf::object::attach =
307     *cf::player::attach =
308     *cf::map::attach = sub {
309 root 1.48 my ($obj, $name, %arg) = @_;
310 root 1.46
311 root 1.48 _attach_attachment $obj, $name, %arg;
312 root 1.55 };
313 root 1.46
314 root 1.54 # all those should be optimised
315     *cf::object::detach =
316     *cf::player::detach =
317     *cf::map::detach = sub {
318     my ($obj, $name) = @_;
319 root 1.46
320 root 1.54 delete $obj->{_attachment}{$name};
321 root 1.55 reattach ($obj);
322     };
323    
324     *cf::object::attached =
325     *cf::player::attached =
326     *cf::map::attached = sub {
327     my ($obj, $name) = @_;
328    
329     exists $obj->{_attachment}{$name}
330 root 1.54 };
331 root 1.53
332 root 1.39 sub attach_global {
333     _attach @CB_GLOBAL, KLASS_GLOBAL, @_
334     }
335    
336 root 1.40 sub attach_to_type {
337 root 1.39 my $type = shift;
338 root 1.47 my $subtype = shift;
339 root 1.45
340 root 1.47 _attach @{$CB_TYPE[$type + $subtype * NUM_SUBTYPES]}, KLASS_OBJECT, @_
341 root 1.39 }
342    
343     sub attach_to_objects {
344 root 1.40 _attach @CB_OBJECT, KLASS_OBJECT, @_
345 root 1.39 }
346    
347     sub attach_to_players {
348 root 1.40 _attach @CB_PLAYER, KLASS_PLAYER, @_
349 root 1.39 }
350    
351     sub attach_to_maps {
352 root 1.40 _attach @CB_MAP, KLASS_MAP, @_
353 root 1.39 }
354    
355 root 1.45 sub register_attachment {
356     my $name = shift;
357    
358 root 1.47 $attachment{$name} = [[KLASS_OBJECT, @_]];
359 root 1.45 }
360    
361 root 1.55 sub register_player_attachment {
362     my $name = shift;
363    
364     $attachment{$name} = [[KLASS_PLAYER, @_]];
365     }
366    
367 root 1.52 sub register_map_attachment {
368     my $name = shift;
369    
370     $attachment{$name} = [[KLASS_MAP, @_]];
371     }
372    
373 root 1.39 our $override;
374 root 1.45 our @invoke_results = (); # referenced from .xs code. TODO: play tricks with reify and mortals?
375 root 1.39
376 root 1.45 sub override {
377     $override = 1;
378     @invoke_results = ();
379 root 1.39 }
380    
381 root 1.45 sub do_invoke {
382 root 1.39 my $event = shift;
383 root 1.40 my $callbacks = shift;
384 root 1.39
385 root 1.45 @invoke_results = ();
386    
387 root 1.39 local $override;
388    
389 root 1.40 for (@$callbacks) {
390 root 1.39 eval { &{$_->[1]} };
391    
392     if ($@) {
393     warn "$@";
394     warn "... while processing $EVENT[$event][0] event, skipping processing altogether.\n";
395     override;
396     }
397    
398     return 1 if $override;
399     }
400    
401     0
402     }
403    
404 root 1.55 =item $bool = cf::invoke EVENT_GLOBAL_XXX, ...
405    
406     =item $bool = $object->invoke (EVENT_OBJECT_XXX, ...)
407    
408     =item $bool = $player->invoke (EVENT_PLAYER_XXX, ...)
409    
410     =item $bool = $map->invoke (EVENT_MAP_XXX, ...)
411    
412     Generate a global/object/player/map-specific event with the given arguments.
413    
414     This API is preliminary (most likely, the EVENT_KLASS_xxx prefix will be
415     removed in future versions), and there is no public API to access override
416     results (if you must, access C<@cf::invoke_results> directly).
417    
418     =back
419    
420     =head2 methods valid for all pointers
421    
422     =over 4
423    
424     =item $object->valid
425    
426     =item $player->valid
427    
428     =item $map->valid
429    
430     Just because you have a perl object does not mean that the corresponding
431     C-level object still exists. If you try to access an object that has no
432     valid C counterpart anymore you get an exception at runtime. This method
433     can be used to test for existence of the C object part without causing an
434     exception.
435    
436     =back
437    
438     =cut
439    
440     *cf::object::valid =
441     *cf::player::valid =
442     *cf::map::valid = \&cf::_valid;
443    
444 root 1.39 #############################################################################
445 root 1.45 # object support
446    
447     sub instantiate {
448     my ($obj, $data) = @_;
449    
450     $data = from_json $data;
451    
452     for (@$data) {
453 root 1.46 my ($name, $args) = @$_;
454 root 1.49
455     $obj->attach ($name, %{$args || {} });
456 root 1.46 }
457     }
458    
459     # basically do the same as instantiate, without calling instantiate
460     sub reattach {
461     my ($obj) = @_;
462     my $registry = $obj->registry;
463 root 1.45
464 root 1.47 @$registry = ();
465    
466 root 1.50 delete $obj->{_attachment} unless scalar keys %{ $obj->{_attachment} || {} };
467 root 1.49
468 root 1.50 for my $name (keys %{ $obj->{_attachment} || {} }) {
469 root 1.45 if (my $attach = $attachment{$name}) {
470 root 1.47 for (@$attach) {
471     my ($klass, @attach) = @$_;
472     _attach @$registry, $klass, @attach;
473     }
474 root 1.45 } else {
475 root 1.46 warn "object uses attachment '$name' that is not available, postponing.\n";
476 root 1.45 }
477 root 1.46 }
478     }
479 root 1.45
480 root 1.46 sub object_freezer_save {
481     my ($filename, $objs) = @_;
482    
483     if (@$objs) {
484 root 1.51 open my $fh, ">:raw", "$filename.pst~";
485 root 1.46 syswrite $fh, Storable::nfreeze { version => 1, objs => $objs };
486     close $fh;
487 root 1.57 unlink "$filename.cfperl";
488 root 1.51 chmod SAVE_MODE, "$filename.pst~";
489     rename "$filename.pst~", "$filename.pst";
490 root 1.46 } else {
491 root 1.51 unlink "$filename.pst";
492 root 1.45 }
493 root 1.51
494     chmod SAVE_MODE, "$filename~";
495     rename "$filename~", $filename;
496 root 1.45 }
497    
498 root 1.46 sub object_thawer_load {
499     my ($filename) = @_;
500    
501     open my $fh, "<:raw:perlio", "$filename.pst"
502     or return;
503 root 1.45
504 root 1.46 eval { local $/; (Storable::thaw <$fh>)->{objs} }
505 root 1.45 }
506    
507     attach_to_objects
508     prio => -1000000,
509     on_clone => sub {
510     my ($src, $dst) = @_;
511    
512     @{$dst->registry} = @{$src->registry};
513    
514     %$dst = %$src;
515    
516 root 1.50 %{$dst->{_attachment}} = %{$src->{_attachment}}
517 root 1.45 if exists $src->{_attachment};
518     },
519     ;
520    
521     #############################################################################
522 root 1.39 # old plug-in events
523    
524 root 1.1 sub inject_event {
525 root 1.14 my $extension = shift;
526     my $event_code = shift;
527 root 1.1
528 root 1.14 my $cb = $hook[$event_code]{$extension}
529 root 1.5 or return;
530    
531 root 1.14 &$cb
532 root 1.5 }
533    
534     sub inject_global_event {
535 root 1.12 my $event = shift;
536 root 1.5
537 root 1.12 my $cb = $hook[$event]
538 root 1.1 or return;
539    
540 root 1.12 List::Util::max map &$_, values %$cb
541 root 1.1 }
542    
543     sub inject_command {
544     my ($name, $obj, $params) = @_;
545    
546     for my $cmd (@{ $command{$name} }) {
547     $cmd->[1]->($obj, $params);
548     }
549    
550     -1
551     }
552    
553     sub register_command {
554     my ($name, $time, $cb) = @_;
555    
556     my $caller = caller;
557 root 1.16 #warn "registering command '$name/$time' to '$caller'";
558 root 1.4
559 root 1.1 push @{ $command{$name} }, [$time, $cb, $caller];
560     $COMMAND{"$name\000"} = List::Util::max map $_->[0], @{ $command{$name} };
561     }
562    
563 root 1.16 sub register_extcmd {
564     my ($name, $cb) = @_;
565    
566     my $caller = caller;
567     #warn "registering extcmd '$name' to '$caller'";
568    
569     $extcmd{$name} = [$cb, $caller];
570     }
571    
572 root 1.6 sub register {
573     my ($base, $pkg) = @_;
574    
575 root 1.45 #TODO
576 root 1.6 }
577    
578 root 1.1 sub load_extension {
579     my ($path) = @_;
580    
581     $path =~ /([^\/\\]+)\.ext$/ or die "$path";
582 root 1.5 my $base = $1;
583 root 1.1 my $pkg = $1;
584     $pkg =~ s/[^[:word:]]/_/g;
585 root 1.41 $pkg = "ext::$pkg";
586 root 1.1
587     warn "loading '$path' into '$pkg'\n";
588    
589     open my $fh, "<:utf8", $path
590     or die "$path: $!";
591    
592     my $source =
593     "package $pkg; use strict; use utf8;\n"
594     . "#line 1 \"$path\"\n{\n"
595     . (do { local $/; <$fh> })
596     . "\n};\n1";
597    
598     eval $source
599     or die "$path: $@";
600    
601     push @exts, $pkg;
602 root 1.5 $ext_pkg{$base} = $pkg;
603 root 1.1
604 root 1.6 # no strict 'refs';
605 root 1.23 # @{"$pkg\::ISA"} = ext::;
606 root 1.1
607 root 1.6 register $base, $pkg;
608 root 1.1 }
609    
610     sub unload_extension {
611     my ($pkg) = @_;
612    
613     warn "removing extension $pkg\n";
614    
615     # remove hooks
616 root 1.45 #TODO
617     # for my $idx (0 .. $#PLUGIN_EVENT) {
618     # delete $hook[$idx]{$pkg};
619     # }
620 root 1.1
621     # remove commands
622     for my $name (keys %command) {
623     my @cb = grep $_->[2] ne $pkg, @{ $command{$name} };
624    
625     if (@cb) {
626     $command{$name} = \@cb;
627     $COMMAND{"$name\000"} = List::Util::max map $_->[0], @cb;
628     } else {
629     delete $command{$name};
630     delete $COMMAND{"$name\000"};
631     }
632     }
633    
634 root 1.15 # remove extcmds
635 root 1.16 for my $name (grep $extcmd{$_}[1] eq $pkg, keys %extcmd) {
636     delete $extcmd{$name};
637 root 1.15 }
638    
639 root 1.43 if (my $cb = $pkg->can ("unload")) {
640 elmex 1.31 eval {
641     $cb->($pkg);
642     1
643     } or warn "$pkg unloaded, but with errors: $@";
644     }
645    
646 root 1.1 Symbol::delete_package $pkg;
647     }
648    
649     sub load_extensions {
650     my $LIBDIR = maps_directory "perl";
651    
652     for my $ext (<$LIBDIR/*.ext>) {
653 root 1.3 next unless -r $ext;
654 root 1.2 eval {
655     load_extension $ext;
656     1
657     } or warn "$ext not loaded: $@";
658 root 1.1 }
659     }
660    
661 root 1.36 sub _perl_reload(&) {
662     my ($msg) = @_;
663    
664     $msg->("reloading...");
665    
666     eval {
667     # 1. cancel all watchers
668     $_->cancel for Event::all_watchers;
669    
670     # 2. unload all extensions
671     for (@exts) {
672     $msg->("unloading <$_>");
673     unload_extension $_;
674     }
675    
676     # 3. unload all modules loaded from $LIBDIR
677     while (my ($k, $v) = each %INC) {
678     next unless $v =~ /^\Q$LIBDIR\E\/.*\.pm$/;
679    
680     $msg->("removing <$k>");
681     delete $INC{$k};
682 root 1.1
683 root 1.36 $k =~ s/\.pm$//;
684     $k =~ s/\//::/g;
685 root 1.3
686 root 1.36 if (my $cb = $k->can ("unload_module")) {
687     $cb->();
688 root 1.27 }
689    
690 root 1.36 Symbol::delete_package $k;
691     }
692 root 1.27
693 root 1.41 # 4. get rid of safe::, as good as possible
694     Symbol::delete_package "safe::$_"
695 root 1.45 for qw(cf::object cf::object::player cf::player cf::map cf::party cf::region);
696 root 1.36
697     # 5. remove register_script_function callbacks
698     # TODO
699    
700     # 6. unload cf.pm "a bit"
701     delete $INC{"cf.pm"};
702    
703 root 1.41 # don't, removes xs symbols, too,
704     # and global variables created in xs
705 root 1.36 #Symbol::delete_package __PACKAGE__;
706    
707     # 7. reload cf.pm
708     $msg->("reloading cf.pm");
709     require cf;
710     };
711     $msg->($@) if $@;
712 root 1.27
713 root 1.36 $msg->("reloaded");
714     };
715 root 1.27
716 root 1.36 sub perl_reload() {
717     _perl_reload {
718     warn $_[0];
719     print "$_[0]\n";
720     };
721     }
722 root 1.27
723 root 1.36 register_command "perl-reload", 0, sub {
724     my ($who, $arg) = @_;
725 root 1.27
726 root 1.36 if ($who->flag (FLAG_WIZ)) {
727     _perl_reload {
728     warn $_[0];
729     $who->message ($_[0]);
730 root 1.4 };
731 root 1.1 }
732     };
733    
734 root 1.8 #############################################################################
735 root 1.28 # extcmd framework, basically convert ext <msg>
736 root 1.15 # into pkg::->on_extcmd_arg1 (...) while shortcutting a few
737    
738 root 1.44 attach_to_players
739 root 1.43 on_extcmd => sub {
740     my ($pl, $buf) = @_;
741    
742     my $msg = eval { from_json $buf };
743    
744     if (ref $msg) {
745     if (my $cb = $extcmd{$msg->{msgtype}}) {
746     if (my %reply = $cb->[0]->($pl, $msg)) {
747     $pl->ext_reply ($msg->{msgid}, %reply);
748     }
749 root 1.28 }
750 root 1.43 } else {
751     warn "player " . ($pl->ob->name) . " sent unparseable ext message: <$buf>\n";
752 root 1.28 }
753 root 1.15
754 root 1.43 cf::override;
755     },
756     ;
757 root 1.15
758     #############################################################################
759 root 1.8 # load/save/clean perl data associated with a map
760    
761 root 1.39 *cf::mapsupport::on_clean = sub {
762 root 1.13 my ($map) = @_;
763 root 1.7
764     my $path = $map->tmpname;
765     defined $path or return;
766    
767 root 1.46 unlink "$path.pst";
768 root 1.7 };
769    
770 root 1.51 # old style persistent data, TODO: remove #d#
771 root 1.56 *cf::mapsupport::on_swapin = sub {
772 root 1.13 my ($map) = @_;
773 root 1.6
774     my $path = $map->tmpname;
775     $path = $map->path unless defined $path;
776    
777     open my $fh, "<:raw", "$path.cfperl"
778     or return; # no perl data
779    
780     my $data = Storable::thaw do { local $/; <$fh> };
781    
782     $data->{version} <= 1
783     or return; # too new
784    
785     $map->_set_obs ($data->{obs});
786 root 1.50 $map->invoke (EVENT_MAP_UPGRADE);
787 root 1.6 };
788    
789 root 1.39 attach_to_maps prio => -10000, package => cf::mapsupport::;
790    
791 root 1.8 #############################################################################
792     # load/save perl data associated with player->ob objects
793    
794 root 1.33 sub all_objects(@) {
795     @_, map all_objects ($_->inv), @_
796     }
797    
798 root 1.39 attach_to_players
799     on_load => sub {
800     my ($pl, $path) = @_;
801    
802     for my $o (all_objects $pl->ob) {
803     if (my $value = $o->get_ob_key_value ("_perl_data")) {
804     $o->set_ob_key_value ("_perl_data");
805 root 1.8
806 root 1.39 %$o = %{ Storable::thaw pack "H*", $value };
807     }
808 root 1.11 }
809 root 1.39 },
810     ;
811 root 1.6
812 root 1.22 #############################################################################
813     # core extensions - in perl
814    
815 root 1.23 =item cf::player::exists $login
816    
817     Returns true when the given account exists.
818    
819     =cut
820    
821     sub cf::player::exists($) {
822     cf::player::find $_[0]
823     or -f sprintf "%s/%s/%s/%s.pl", cf::localdir, cf::playerdir, ($_[0]) x 2;
824     }
825    
826 root 1.28 =item $player->reply ($npc, $msg[, $flags])
827    
828     Sends a message to the player, as if the npc C<$npc> replied. C<$npc>
829     can be C<undef>. Does the right thing when the player is currently in a
830     dialogue with the given NPC character.
831    
832     =cut
833    
834 root 1.22 # rough implementation of a future "reply" method that works
835     # with dialog boxes.
836 root 1.23 sub cf::object::player::reply($$$;$) {
837     my ($self, $npc, $msg, $flags) = @_;
838    
839     $flags = cf::NDI_BROWN | cf::NDI_UNIQUE unless @_ >= 4;
840 root 1.22
841 root 1.24 if ($self->{record_replies}) {
842     push @{ $self->{record_replies} }, [$npc, $msg, $flags];
843     } else {
844     $msg = $npc->name . " says: $msg" if $npc;
845     $self->message ($msg, $flags);
846     }
847 root 1.22 }
848    
849 root 1.28 =item $player->ext_reply ($msgid, $msgtype, %msg)
850    
851     Sends an ext reply to the player.
852    
853     =cut
854    
855     sub cf::player::ext_reply($$$%) {
856     my ($self, $id, %msg) = @_;
857    
858     $msg{msgid} = $id;
859    
860     $self->send ("ext " . to_json \%msg);
861     }
862    
863 root 1.22 #############################################################################
864 root 1.23 # map scripting support
865    
866 root 1.42 our $safe = new Safe "safe";
867 root 1.23 our $safe_hole = new Safe::Hole;
868    
869     $SIG{FPE} = 'IGNORE';
870    
871     $safe->permit_only (Opcode::opset qw(:base_core :base_mem :base_orig :base_math sort time));
872    
873 root 1.25 # here we export the classes and methods available to script code
874    
875     for (
876 root 1.27 ["cf::object" => qw(contr pay_amount pay_player)],
877 root 1.25 ["cf::object::player" => qw(player)],
878     ["cf::player" => qw(peaceful)],
879     ) {
880     no strict 'refs';
881     my ($pkg, @funs) = @$_;
882 root 1.41 *{"safe::$pkg\::$_"} = $safe_hole->wrap (\&{"$pkg\::$_"})
883 root 1.25 for @funs;
884     }
885 root 1.23
886     sub safe_eval($;@) {
887     my ($code, %vars) = @_;
888    
889     my $qcode = $code;
890     $qcode =~ s/"/‟/g; # not allowed in #line filenames
891     $qcode =~ s/\n/\\n/g;
892    
893     local $_;
894 root 1.41 local @safe::cf::_safe_eval_args = values %vars;
895 root 1.23
896 root 1.42 my $eval =
897 root 1.23 "do {\n"
898     . "my (" . (join ",", map "\$$_", keys %vars) . ") = \@cf::_safe_eval_args;\n"
899     . "#line 0 \"{$qcode}\"\n"
900     . $code
901     . "\n}"
902 root 1.25 ;
903    
904     sub_generation_inc;
905 root 1.42 my @res = wantarray ? $safe->reval ($eval) : scalar $safe->reval ($eval);
906 root 1.25 sub_generation_inc;
907    
908 root 1.42 if ($@) {
909     warn "$@";
910     warn "while executing safe code '$code'\n";
911     warn "with arguments " . (join " ", %vars) . "\n";
912     }
913    
914 root 1.25 wantarray ? @res : $res[0]
915 root 1.23 }
916    
917     sub register_script_function {
918     my ($fun, $cb) = @_;
919    
920     no strict 'refs';
921 root 1.41 *{"safe::$fun"} = $safe_hole->wrap ($cb);
922 root 1.23 }
923    
924     #############################################################################
925 root 1.34 # the server's main()
926    
927 root 1.39 sub main {
928 root 1.34 Event::loop;
929     }
930    
931     #############################################################################
932 root 1.22 # initialisation
933    
934 root 1.6 register "<global>", __PACKAGE__;
935    
936 root 1.27 unshift @INC, $LIBDIR;
937 root 1.17
938 root 1.1 load_extensions;
939    
940 root 1.35 $TICK_WATCHER = Event->timer (
941     prio => 1,
942     at => $NEXT_TICK || 1,
943     cb => sub {
944     cf::server_tick; # one server iteration
945    
946     my $NOW = Event::time;
947     $NEXT_TICK += $TICK;
948    
949 root 1.37 # if we are delayed by four ticks, skip them all
950     $NEXT_TICK = $NOW if $NOW >= $NEXT_TICK + $TICK * 4;
951 root 1.35
952     $TICK_WATCHER->at ($NEXT_TICK);
953     $TICK_WATCHER->start;
954     },
955     );
956    
957 root 1.47 _reload_2;
958    
959 root 1.1 1
960