ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.48
Committed: Sun Aug 27 15:24:22 2006 UTC (17 years, 9 months ago) by root
Branch: MAIN
Changes since 1.47: +12 -20 lines
Log Message:
further refinements

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.45 for my $pkg (qw(cf::object cf::object::player cf::player cf::map cf::party cf::region cf::arch)) {
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.48 =item $object->attach ($attachment, key => $value...)
111 root 1.46
112     Attach a pre-registered attachment to an object.
113    
114 root 1.48 =item $player->attach ($attachment, key => $value...)
115 root 1.46
116     Attach a pre-registered attachment to a player.
117    
118 root 1.48 =item $map->attach ($attachment, key => $value...) # not yet persistent
119 root 1.46
120     Attach a pre-registered attachment to a map.
121 root 1.39
122 root 1.40 =item cf::attach_global ...
123 root 1.39
124 root 1.46 Attach handlers for global events.
125    
126     This and all following C<attach_*>-functions expect any number of the
127     following handler/hook descriptions:
128    
129     =over 4
130    
131     =item prio => $number
132    
133     Set the priority for all following handlers/hooks (unless overwritten
134     by another C<prio> setting). Lower priority handlers get executed
135     earlier. The default priority is C<0>, and many built-in handlers are
136     registered at priority C<-1000>, so lower priorities should not be used
137     unless you know what you are doing.
138    
139     =item on_I<event> => \&cb
140    
141     Call the given code reference whenever the named event happens (event is
142     something like C<instantiate>, C<apply>, C<use_skill> and so on, and which
143     handlers are recognised generally depends on the type of object these
144     handlers attach to).
145    
146     See F<include/eventinc.h> for the full list of events supported, and their
147     class.
148    
149     =item package => package::
150    
151     Look for sub functions of the name C<< on_I<event> >> in the given
152     package and register them. Only handlers for eevents supported by the
153     object/class are recognised.
154    
155     =back
156    
157 root 1.47 =item cf::attach_to_type $object_type, $subtype, ...
158 root 1.39
159 root 1.47 Attach handlers for a specific object type (e.g. TRANSPORT) and
160     subtype. If C<$subtype> is zero or undef, matches all objects of the given
161     type.
162 root 1.46
163 root 1.40 =item cf::attach_to_objects ...
164 root 1.39
165 root 1.46 Attach handlers to all objects. Do not use this except for debugging or
166     very rare events, as handlers are (obviously) called for I<all> objects in
167     the game.
168    
169 root 1.40 =item cf::attach_to_players ...
170 root 1.39
171 root 1.46 Attach handlers to all players.
172    
173 root 1.40 =item cf::attach_to_maps ...
174 root 1.39
175 root 1.46 Attach handlers to all maps.
176    
177 root 1.45 =item cf:register_attachment $name, ...
178    
179 root 1.39 =cut
180    
181 root 1.40 # the following variables are defined in .xs and must not be re-created
182 root 1.39 our @CB_GLOBAL = (); # registry for all global events
183 root 1.45 our @CB_OBJECT = (); # all objects (should not be used except in emergency)
184 root 1.40 our @CB_PLAYER = ();
185 root 1.39 our @CB_TYPE = (); # registry for type (cf-object class) based events
186 root 1.40 our @CB_MAP = ();
187 root 1.39
188 root 1.45 my %attachment;
189    
190 root 1.39 sub _attach_cb($\%$$$) {
191     my ($registry, $undo, $event, $prio, $cb) = @_;
192    
193     use sort 'stable';
194    
195     $cb = [$prio, $cb];
196    
197     @{$registry->[$event]} = sort
198     { $a->[0] cmp $b->[0] }
199     @{$registry->[$event] || []}, $cb;
200    
201     push @{$undo->{cb}}, [$event, $cb];
202     }
203    
204     # attach handles attaching event callbacks
205     # the only thing the caller has to do is pass the correct
206     # registry (== where the callback attaches to).
207 root 1.45 sub _attach(\@$@) {
208     my ($registry, $klass, @arg) = @_;
209 root 1.39
210     my $prio = 0;
211    
212     my %undo = (
213     registry => $registry,
214     cb => [],
215     );
216    
217     my %cb_id = map +("on_" . lc $EVENT[$_][0], $_) , grep $EVENT[$_][1] == $klass, 0 .. $#EVENT;
218    
219 root 1.45 while (@arg) {
220     my $type = shift @arg;
221 root 1.39
222     if ($type eq "prio") {
223 root 1.45 $prio = shift @arg;
224 root 1.39
225     } elsif ($type eq "package") {
226 root 1.45 my $pkg = shift @arg;
227 root 1.39
228     while (my ($name, $id) = each %cb_id) {
229     if (my $cb = $pkg->can ($name)) {
230     _attach_cb $registry, %undo, $id, $prio, $cb;
231     }
232     }
233    
234     } elsif (exists $cb_id{$type}) {
235 root 1.45 _attach_cb $registry, %undo, $cb_id{$type}, $prio, shift @arg;
236 root 1.39
237     } elsif (ref $type) {
238     warn "attaching objects not supported, ignoring.\n";
239    
240     } else {
241 root 1.45 shift @arg;
242 root 1.39 warn "attach argument '$type' not supported, ignoring.\n";
243     }
244     }
245    
246     \%undo
247     }
248    
249 root 1.46 sub _attach_attachment {
250 root 1.48 my ($obj, $name, %arg) = @_;
251 root 1.46
252     my $res;
253    
254     if (my $attach = $attachment{$name}) {
255     my $registry = $obj->registry;
256    
257 root 1.47 for (@$attach) {
258     my ($klass, @attach) = @$_;
259     $res = _attach @$registry, $klass, @attach;
260     }
261 root 1.46
262 root 1.48 $obj->{$name} = \%arg;
263 root 1.46 } else {
264     warn "object uses attachment '$name' that is not available, postponing.\n";
265     }
266    
267     push @{$obj->{_attachment}}, $name;
268    
269     $res->{attachment} = $name;
270     $res
271     }
272    
273 root 1.39 sub cf::object::attach {
274 root 1.48 my ($obj, $name, %arg) = @_;
275 root 1.46
276 root 1.48 _attach_attachment $obj, $name, %arg;
277 root 1.46 }
278    
279     sub cf::player::attach {
280 root 1.48 my ($obj, $name, %arg) = @_;
281 root 1.46
282 root 1.48 _attach_attachment KLASS_PLAYER, $obj, $name, %arg;
283 root 1.46 }
284    
285     sub cf::map::attach {
286 root 1.48 my ($obj, $name, %arg) = @_;
287 root 1.46
288 root 1.48 _attach_attachment KLASS_MAP, $obj, $name, %arg;
289 root 1.39 }
290    
291     sub attach_global {
292     _attach @CB_GLOBAL, KLASS_GLOBAL, @_
293     }
294    
295 root 1.40 sub attach_to_type {
296 root 1.39 my $type = shift;
297 root 1.47 my $subtype = shift;
298 root 1.45
299 root 1.47 _attach @{$CB_TYPE[$type + $subtype * NUM_SUBTYPES]}, KLASS_OBJECT, @_
300 root 1.39 }
301    
302     sub attach_to_objects {
303 root 1.40 _attach @CB_OBJECT, KLASS_OBJECT, @_
304 root 1.39 }
305    
306     sub attach_to_players {
307 root 1.40 _attach @CB_PLAYER, KLASS_PLAYER, @_
308 root 1.39 }
309    
310     sub attach_to_maps {
311 root 1.40 _attach @CB_MAP, KLASS_MAP, @_
312 root 1.39 }
313    
314 root 1.45 sub register_attachment {
315     my $name = shift;
316    
317 root 1.47 $attachment{$name} = [[KLASS_OBJECT, @_]];
318 root 1.45 }
319    
320 root 1.39 our $override;
321 root 1.45 our @invoke_results = (); # referenced from .xs code. TODO: play tricks with reify and mortals?
322 root 1.39
323 root 1.45 sub override {
324     $override = 1;
325     @invoke_results = ();
326 root 1.39 }
327    
328 root 1.45 sub do_invoke {
329 root 1.39 my $event = shift;
330 root 1.40 my $callbacks = shift;
331 root 1.39
332 root 1.45 @invoke_results = ();
333    
334 root 1.39 local $override;
335    
336 root 1.40 for (@$callbacks) {
337 root 1.39 eval { &{$_->[1]} };
338    
339     if ($@) {
340     warn "$@";
341     warn "... while processing $EVENT[$event][0] event, skipping processing altogether.\n";
342     override;
343     }
344    
345     return 1 if $override;
346     }
347    
348     0
349     }
350    
351     #############################################################################
352 root 1.45 # object support
353    
354     sub instantiate {
355     my ($obj, $data) = @_;
356    
357     $data = from_json $data;
358    
359     for (@$data) {
360 root 1.46 my ($name, $args) = @$_;
361 root 1.48 attach $obj, $name, %{$args || {} };
362 root 1.46 }
363     }
364    
365     # basically do the same as instantiate, without calling instantiate
366     sub reattach {
367     my ($obj) = @_;
368     my $registry = $obj->registry;
369 root 1.45
370 root 1.47 @$registry = ();
371    
372 root 1.46 for my $name (@{ $obj->{_attachment} }) {
373 root 1.45 if (my $attach = $attachment{$name}) {
374 root 1.47 for (@$attach) {
375     my ($klass, @attach) = @$_;
376     _attach @$registry, $klass, @attach;
377     }
378 root 1.45 } else {
379 root 1.46 warn "object uses attachment '$name' that is not available, postponing.\n";
380 root 1.45 }
381 root 1.46 }
382     }
383 root 1.45
384 root 1.46 sub object_freezer_save {
385     my ($filename, $objs) = @_;
386    
387     $filename .= ".pst";
388    
389     if (@$objs) {
390     open my $fh, ">:raw", "$filename~";
391     chmod $fh, SAVE_MODE;
392     syswrite $fh, Storable::nfreeze { version => 1, objs => $objs };
393     close $fh;
394     rename "$filename~", $filename;
395     } else {
396     unlink $filename;
397 root 1.45 }
398     }
399    
400 root 1.46 sub object_thawer_load {
401     my ($filename) = @_;
402    
403     open my $fh, "<:raw:perlio", "$filename.pst"
404     or return;
405 root 1.45
406 root 1.46 eval { local $/; (Storable::thaw <$fh>)->{objs} }
407 root 1.45 }
408    
409     attach_to_objects
410     prio => -1000000,
411     on_clone => sub {
412     my ($src, $dst) = @_;
413    
414     @{$dst->registry} = @{$src->registry};
415    
416     %$dst = %$src;
417    
418     $dst->{_attachment} = [@{ $src->{_attachment} }]
419     if exists $src->{_attachment};
420     },
421     ;
422    
423     #############################################################################
424 root 1.39 # old plug-in events
425    
426 root 1.1 sub inject_event {
427 root 1.14 my $extension = shift;
428     my $event_code = shift;
429 root 1.1
430 root 1.14 my $cb = $hook[$event_code]{$extension}
431 root 1.5 or return;
432    
433 root 1.14 &$cb
434 root 1.5 }
435    
436     sub inject_global_event {
437 root 1.12 my $event = shift;
438 root 1.5
439 root 1.12 my $cb = $hook[$event]
440 root 1.1 or return;
441    
442 root 1.12 List::Util::max map &$_, values %$cb
443 root 1.1 }
444    
445     sub inject_command {
446     my ($name, $obj, $params) = @_;
447    
448     for my $cmd (@{ $command{$name} }) {
449     $cmd->[1]->($obj, $params);
450     }
451    
452     -1
453     }
454    
455     sub register_command {
456     my ($name, $time, $cb) = @_;
457    
458     my $caller = caller;
459 root 1.16 #warn "registering command '$name/$time' to '$caller'";
460 root 1.4
461 root 1.1 push @{ $command{$name} }, [$time, $cb, $caller];
462     $COMMAND{"$name\000"} = List::Util::max map $_->[0], @{ $command{$name} };
463     }
464    
465 root 1.16 sub register_extcmd {
466     my ($name, $cb) = @_;
467    
468     my $caller = caller;
469     #warn "registering extcmd '$name' to '$caller'";
470    
471     $extcmd{$name} = [$cb, $caller];
472     }
473    
474 root 1.6 sub register {
475     my ($base, $pkg) = @_;
476    
477 root 1.45 #TODO
478 root 1.6 }
479    
480 root 1.1 sub load_extension {
481     my ($path) = @_;
482    
483     $path =~ /([^\/\\]+)\.ext$/ or die "$path";
484 root 1.5 my $base = $1;
485 root 1.1 my $pkg = $1;
486     $pkg =~ s/[^[:word:]]/_/g;
487 root 1.41 $pkg = "ext::$pkg";
488 root 1.1
489     warn "loading '$path' into '$pkg'\n";
490    
491     open my $fh, "<:utf8", $path
492     or die "$path: $!";
493    
494     my $source =
495     "package $pkg; use strict; use utf8;\n"
496     . "#line 1 \"$path\"\n{\n"
497     . (do { local $/; <$fh> })
498     . "\n};\n1";
499    
500     eval $source
501     or die "$path: $@";
502    
503     push @exts, $pkg;
504 root 1.5 $ext_pkg{$base} = $pkg;
505 root 1.1
506 root 1.6 # no strict 'refs';
507 root 1.23 # @{"$pkg\::ISA"} = ext::;
508 root 1.1
509 root 1.6 register $base, $pkg;
510 root 1.1 }
511    
512     sub unload_extension {
513     my ($pkg) = @_;
514    
515     warn "removing extension $pkg\n";
516    
517     # remove hooks
518 root 1.45 #TODO
519     # for my $idx (0 .. $#PLUGIN_EVENT) {
520     # delete $hook[$idx]{$pkg};
521     # }
522 root 1.1
523     # remove commands
524     for my $name (keys %command) {
525     my @cb = grep $_->[2] ne $pkg, @{ $command{$name} };
526    
527     if (@cb) {
528     $command{$name} = \@cb;
529     $COMMAND{"$name\000"} = List::Util::max map $_->[0], @cb;
530     } else {
531     delete $command{$name};
532     delete $COMMAND{"$name\000"};
533     }
534     }
535    
536 root 1.15 # remove extcmds
537 root 1.16 for my $name (grep $extcmd{$_}[1] eq $pkg, keys %extcmd) {
538     delete $extcmd{$name};
539 root 1.15 }
540    
541 root 1.43 if (my $cb = $pkg->can ("unload")) {
542 elmex 1.31 eval {
543     $cb->($pkg);
544     1
545     } or warn "$pkg unloaded, but with errors: $@";
546     }
547    
548 root 1.1 Symbol::delete_package $pkg;
549     }
550    
551     sub load_extensions {
552     my $LIBDIR = maps_directory "perl";
553    
554     for my $ext (<$LIBDIR/*.ext>) {
555 root 1.3 next unless -r $ext;
556 root 1.2 eval {
557     load_extension $ext;
558     1
559     } or warn "$ext not loaded: $@";
560 root 1.1 }
561     }
562    
563 root 1.36 sub _perl_reload(&) {
564     my ($msg) = @_;
565    
566     $msg->("reloading...");
567    
568     eval {
569     # 1. cancel all watchers
570     $_->cancel for Event::all_watchers;
571    
572     # 2. unload all extensions
573     for (@exts) {
574     $msg->("unloading <$_>");
575     unload_extension $_;
576     }
577    
578     # 3. unload all modules loaded from $LIBDIR
579     while (my ($k, $v) = each %INC) {
580     next unless $v =~ /^\Q$LIBDIR\E\/.*\.pm$/;
581    
582     $msg->("removing <$k>");
583     delete $INC{$k};
584 root 1.1
585 root 1.36 $k =~ s/\.pm$//;
586     $k =~ s/\//::/g;
587 root 1.3
588 root 1.36 if (my $cb = $k->can ("unload_module")) {
589     $cb->();
590 root 1.27 }
591    
592 root 1.36 Symbol::delete_package $k;
593     }
594 root 1.27
595 root 1.41 # 4. get rid of safe::, as good as possible
596     Symbol::delete_package "safe::$_"
597 root 1.45 for qw(cf::object cf::object::player cf::player cf::map cf::party cf::region);
598 root 1.36
599     # 5. remove register_script_function callbacks
600     # TODO
601    
602     # 6. unload cf.pm "a bit"
603     delete $INC{"cf.pm"};
604    
605 root 1.41 # don't, removes xs symbols, too,
606     # and global variables created in xs
607 root 1.36 #Symbol::delete_package __PACKAGE__;
608    
609     # 7. reload cf.pm
610     $msg->("reloading cf.pm");
611     require cf;
612     };
613     $msg->($@) if $@;
614 root 1.27
615 root 1.36 $msg->("reloaded");
616     };
617 root 1.27
618 root 1.36 sub perl_reload() {
619     _perl_reload {
620     warn $_[0];
621     print "$_[0]\n";
622     };
623     }
624 root 1.27
625 root 1.36 register_command "perl-reload", 0, sub {
626     my ($who, $arg) = @_;
627 root 1.27
628 root 1.36 if ($who->flag (FLAG_WIZ)) {
629     _perl_reload {
630     warn $_[0];
631     $who->message ($_[0]);
632 root 1.4 };
633 root 1.1 }
634     };
635    
636 root 1.8 #############################################################################
637 root 1.28 # extcmd framework, basically convert ext <msg>
638 root 1.15 # into pkg::->on_extcmd_arg1 (...) while shortcutting a few
639    
640 root 1.44 attach_to_players
641 root 1.43 on_extcmd => sub {
642     my ($pl, $buf) = @_;
643    
644     my $msg = eval { from_json $buf };
645    
646     if (ref $msg) {
647     if (my $cb = $extcmd{$msg->{msgtype}}) {
648     if (my %reply = $cb->[0]->($pl, $msg)) {
649     $pl->ext_reply ($msg->{msgid}, %reply);
650     }
651 root 1.28 }
652 root 1.43 } else {
653     warn "player " . ($pl->ob->name) . " sent unparseable ext message: <$buf>\n";
654 root 1.28 }
655 root 1.15
656 root 1.43 cf::override;
657     },
658     ;
659 root 1.15
660     #############################################################################
661 root 1.8 # load/save/clean perl data associated with a map
662    
663 root 1.39 *cf::mapsupport::on_clean = sub {
664 root 1.13 my ($map) = @_;
665 root 1.7
666     my $path = $map->tmpname;
667     defined $path or return;
668    
669     unlink "$path.cfperl";
670 root 1.46 unlink "$path.pst";
671 root 1.7 };
672    
673 root 1.39 *cf::mapsupport::on_swapin =
674     *cf::mapsupport::on_load = sub {
675 root 1.13 my ($map) = @_;
676 root 1.6
677     my $path = $map->tmpname;
678     $path = $map->path unless defined $path;
679    
680     open my $fh, "<:raw", "$path.cfperl"
681     or return; # no perl data
682    
683     my $data = Storable::thaw do { local $/; <$fh> };
684    
685     $data->{version} <= 1
686     or return; # too new
687    
688     $map->_set_obs ($data->{obs});
689     };
690    
691 root 1.39 attach_to_maps prio => -10000, package => cf::mapsupport::;
692    
693 root 1.8 #############################################################################
694     # load/save perl data associated with player->ob objects
695    
696 root 1.33 sub all_objects(@) {
697     @_, map all_objects ($_->inv), @_
698     }
699    
700 root 1.39 attach_to_players
701     on_load => sub {
702     my ($pl, $path) = @_;
703    
704     for my $o (all_objects $pl->ob) {
705     if (my $value = $o->get_ob_key_value ("_perl_data")) {
706     $o->set_ob_key_value ("_perl_data");
707 root 1.8
708 root 1.39 %$o = %{ Storable::thaw pack "H*", $value };
709     }
710 root 1.11 }
711 root 1.39 },
712     ;
713 root 1.6
714 root 1.22 #############################################################################
715     # core extensions - in perl
716    
717 root 1.23 =item cf::player::exists $login
718    
719     Returns true when the given account exists.
720    
721     =cut
722    
723     sub cf::player::exists($) {
724     cf::player::find $_[0]
725     or -f sprintf "%s/%s/%s/%s.pl", cf::localdir, cf::playerdir, ($_[0]) x 2;
726     }
727    
728 root 1.28 =item $player->reply ($npc, $msg[, $flags])
729    
730     Sends a message to the player, as if the npc C<$npc> replied. C<$npc>
731     can be C<undef>. Does the right thing when the player is currently in a
732     dialogue with the given NPC character.
733    
734     =cut
735    
736 root 1.22 # rough implementation of a future "reply" method that works
737     # with dialog boxes.
738 root 1.23 sub cf::object::player::reply($$$;$) {
739     my ($self, $npc, $msg, $flags) = @_;
740    
741     $flags = cf::NDI_BROWN | cf::NDI_UNIQUE unless @_ >= 4;
742 root 1.22
743 root 1.24 if ($self->{record_replies}) {
744     push @{ $self->{record_replies} }, [$npc, $msg, $flags];
745     } else {
746     $msg = $npc->name . " says: $msg" if $npc;
747     $self->message ($msg, $flags);
748     }
749 root 1.22 }
750    
751 root 1.28 =item $player->ext_reply ($msgid, $msgtype, %msg)
752    
753     Sends an ext reply to the player.
754    
755     =cut
756    
757     sub cf::player::ext_reply($$$%) {
758     my ($self, $id, %msg) = @_;
759    
760     $msg{msgid} = $id;
761    
762     $self->send ("ext " . to_json \%msg);
763     }
764    
765 root 1.22 #############################################################################
766 root 1.23 # map scripting support
767    
768 root 1.42 our $safe = new Safe "safe";
769 root 1.23 our $safe_hole = new Safe::Hole;
770    
771     $SIG{FPE} = 'IGNORE';
772    
773     $safe->permit_only (Opcode::opset qw(:base_core :base_mem :base_orig :base_math sort time));
774    
775 root 1.25 # here we export the classes and methods available to script code
776    
777     for (
778 root 1.27 ["cf::object" => qw(contr pay_amount pay_player)],
779 root 1.25 ["cf::object::player" => qw(player)],
780     ["cf::player" => qw(peaceful)],
781     ) {
782     no strict 'refs';
783     my ($pkg, @funs) = @$_;
784 root 1.41 *{"safe::$pkg\::$_"} = $safe_hole->wrap (\&{"$pkg\::$_"})
785 root 1.25 for @funs;
786     }
787 root 1.23
788     sub safe_eval($;@) {
789     my ($code, %vars) = @_;
790    
791     my $qcode = $code;
792     $qcode =~ s/"/‟/g; # not allowed in #line filenames
793     $qcode =~ s/\n/\\n/g;
794    
795     local $_;
796 root 1.41 local @safe::cf::_safe_eval_args = values %vars;
797 root 1.23
798 root 1.42 my $eval =
799 root 1.23 "do {\n"
800     . "my (" . (join ",", map "\$$_", keys %vars) . ") = \@cf::_safe_eval_args;\n"
801     . "#line 0 \"{$qcode}\"\n"
802     . $code
803     . "\n}"
804 root 1.25 ;
805    
806     sub_generation_inc;
807 root 1.42 my @res = wantarray ? $safe->reval ($eval) : scalar $safe->reval ($eval);
808 root 1.25 sub_generation_inc;
809    
810 root 1.42 if ($@) {
811     warn "$@";
812     warn "while executing safe code '$code'\n";
813     warn "with arguments " . (join " ", %vars) . "\n";
814     }
815    
816 root 1.25 wantarray ? @res : $res[0]
817 root 1.23 }
818    
819     sub register_script_function {
820     my ($fun, $cb) = @_;
821    
822     no strict 'refs';
823 root 1.41 *{"safe::$fun"} = $safe_hole->wrap ($cb);
824 root 1.23 }
825    
826     #############################################################################
827 root 1.34 # the server's main()
828    
829 root 1.39 sub main {
830 root 1.34 Event::loop;
831     }
832    
833     #############################################################################
834 root 1.22 # initialisation
835    
836 root 1.6 register "<global>", __PACKAGE__;
837    
838 root 1.27 unshift @INC, $LIBDIR;
839 root 1.17
840 root 1.1 load_extensions;
841    
842 root 1.35 $TICK_WATCHER = Event->timer (
843     prio => 1,
844     at => $NEXT_TICK || 1,
845     cb => sub {
846     cf::server_tick; # one server iteration
847    
848     my $NOW = Event::time;
849     $NEXT_TICK += $TICK;
850    
851 root 1.37 # if we are delayed by four ticks, skip them all
852     $NEXT_TICK = $NOW if $NOW >= $NEXT_TICK + $TICK * 4;
853 root 1.35
854     $TICK_WATCHER->at ($NEXT_TICK);
855     $TICK_WATCHER->start;
856     },
857     );
858    
859 root 1.47 _reload_2;
860    
861 root 1.1 1
862