ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.49
Committed: Sun Aug 27 17:59:26 2006 UTC (17 years, 9 months ago) by root
Branch: MAIN
Changes since 1.48: +5 -2 lines
Log Message:
fixes, objects on maps get instantiated properly

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.49
362     $obj->attach ($name, %{$args || {} });
363 root 1.46 }
364     }
365    
366     # basically do the same as instantiate, without calling instantiate
367     sub reattach {
368     my ($obj) = @_;
369     my $registry = $obj->registry;
370 root 1.45
371 root 1.47 @$registry = ();
372    
373 root 1.49 delete $obj->{_attachment} unless @{ $obj->{_attachment} || [] };
374    
375     for my $name (@{ $obj->{_attachment} || [] }) {
376 root 1.45 if (my $attach = $attachment{$name}) {
377 root 1.47 for (@$attach) {
378     my ($klass, @attach) = @$_;
379     _attach @$registry, $klass, @attach;
380     }
381 root 1.45 } else {
382 root 1.46 warn "object uses attachment '$name' that is not available, postponing.\n";
383 root 1.45 }
384 root 1.46 }
385     }
386 root 1.45
387 root 1.46 sub object_freezer_save {
388     my ($filename, $objs) = @_;
389    
390     $filename .= ".pst";
391    
392     if (@$objs) {
393     open my $fh, ">:raw", "$filename~";
394     chmod $fh, SAVE_MODE;
395     syswrite $fh, Storable::nfreeze { version => 1, objs => $objs };
396     close $fh;
397     rename "$filename~", $filename;
398     } else {
399     unlink $filename;
400 root 1.45 }
401     }
402    
403 root 1.46 sub object_thawer_load {
404     my ($filename) = @_;
405    
406     open my $fh, "<:raw:perlio", "$filename.pst"
407     or return;
408 root 1.45
409 root 1.46 eval { local $/; (Storable::thaw <$fh>)->{objs} }
410 root 1.45 }
411    
412     attach_to_objects
413     prio => -1000000,
414     on_clone => sub {
415     my ($src, $dst) = @_;
416    
417     @{$dst->registry} = @{$src->registry};
418    
419     %$dst = %$src;
420    
421     $dst->{_attachment} = [@{ $src->{_attachment} }]
422     if exists $src->{_attachment};
423     },
424     ;
425    
426     #############################################################################
427 root 1.39 # old plug-in events
428    
429 root 1.1 sub inject_event {
430 root 1.14 my $extension = shift;
431     my $event_code = shift;
432 root 1.1
433 root 1.14 my $cb = $hook[$event_code]{$extension}
434 root 1.5 or return;
435    
436 root 1.14 &$cb
437 root 1.5 }
438    
439     sub inject_global_event {
440 root 1.12 my $event = shift;
441 root 1.5
442 root 1.12 my $cb = $hook[$event]
443 root 1.1 or return;
444    
445 root 1.12 List::Util::max map &$_, values %$cb
446 root 1.1 }
447    
448     sub inject_command {
449     my ($name, $obj, $params) = @_;
450    
451     for my $cmd (@{ $command{$name} }) {
452     $cmd->[1]->($obj, $params);
453     }
454    
455     -1
456     }
457    
458     sub register_command {
459     my ($name, $time, $cb) = @_;
460    
461     my $caller = caller;
462 root 1.16 #warn "registering command '$name/$time' to '$caller'";
463 root 1.4
464 root 1.1 push @{ $command{$name} }, [$time, $cb, $caller];
465     $COMMAND{"$name\000"} = List::Util::max map $_->[0], @{ $command{$name} };
466     }
467    
468 root 1.16 sub register_extcmd {
469     my ($name, $cb) = @_;
470    
471     my $caller = caller;
472     #warn "registering extcmd '$name' to '$caller'";
473    
474     $extcmd{$name} = [$cb, $caller];
475     }
476    
477 root 1.6 sub register {
478     my ($base, $pkg) = @_;
479    
480 root 1.45 #TODO
481 root 1.6 }
482    
483 root 1.1 sub load_extension {
484     my ($path) = @_;
485    
486     $path =~ /([^\/\\]+)\.ext$/ or die "$path";
487 root 1.5 my $base = $1;
488 root 1.1 my $pkg = $1;
489     $pkg =~ s/[^[:word:]]/_/g;
490 root 1.41 $pkg = "ext::$pkg";
491 root 1.1
492     warn "loading '$path' into '$pkg'\n";
493    
494     open my $fh, "<:utf8", $path
495     or die "$path: $!";
496    
497     my $source =
498     "package $pkg; use strict; use utf8;\n"
499     . "#line 1 \"$path\"\n{\n"
500     . (do { local $/; <$fh> })
501     . "\n};\n1";
502    
503     eval $source
504     or die "$path: $@";
505    
506     push @exts, $pkg;
507 root 1.5 $ext_pkg{$base} = $pkg;
508 root 1.1
509 root 1.6 # no strict 'refs';
510 root 1.23 # @{"$pkg\::ISA"} = ext::;
511 root 1.1
512 root 1.6 register $base, $pkg;
513 root 1.1 }
514    
515     sub unload_extension {
516     my ($pkg) = @_;
517    
518     warn "removing extension $pkg\n";
519    
520     # remove hooks
521 root 1.45 #TODO
522     # for my $idx (0 .. $#PLUGIN_EVENT) {
523     # delete $hook[$idx]{$pkg};
524     # }
525 root 1.1
526     # remove commands
527     for my $name (keys %command) {
528     my @cb = grep $_->[2] ne $pkg, @{ $command{$name} };
529    
530     if (@cb) {
531     $command{$name} = \@cb;
532     $COMMAND{"$name\000"} = List::Util::max map $_->[0], @cb;
533     } else {
534     delete $command{$name};
535     delete $COMMAND{"$name\000"};
536     }
537     }
538    
539 root 1.15 # remove extcmds
540 root 1.16 for my $name (grep $extcmd{$_}[1] eq $pkg, keys %extcmd) {
541     delete $extcmd{$name};
542 root 1.15 }
543    
544 root 1.43 if (my $cb = $pkg->can ("unload")) {
545 elmex 1.31 eval {
546     $cb->($pkg);
547     1
548     } or warn "$pkg unloaded, but with errors: $@";
549     }
550    
551 root 1.1 Symbol::delete_package $pkg;
552     }
553    
554     sub load_extensions {
555     my $LIBDIR = maps_directory "perl";
556    
557     for my $ext (<$LIBDIR/*.ext>) {
558 root 1.3 next unless -r $ext;
559 root 1.2 eval {
560     load_extension $ext;
561     1
562     } or warn "$ext not loaded: $@";
563 root 1.1 }
564     }
565    
566 root 1.36 sub _perl_reload(&) {
567     my ($msg) = @_;
568    
569     $msg->("reloading...");
570    
571     eval {
572     # 1. cancel all watchers
573     $_->cancel for Event::all_watchers;
574    
575     # 2. unload all extensions
576     for (@exts) {
577     $msg->("unloading <$_>");
578     unload_extension $_;
579     }
580    
581     # 3. unload all modules loaded from $LIBDIR
582     while (my ($k, $v) = each %INC) {
583     next unless $v =~ /^\Q$LIBDIR\E\/.*\.pm$/;
584    
585     $msg->("removing <$k>");
586     delete $INC{$k};
587 root 1.1
588 root 1.36 $k =~ s/\.pm$//;
589     $k =~ s/\//::/g;
590 root 1.3
591 root 1.36 if (my $cb = $k->can ("unload_module")) {
592     $cb->();
593 root 1.27 }
594    
595 root 1.36 Symbol::delete_package $k;
596     }
597 root 1.27
598 root 1.41 # 4. get rid of safe::, as good as possible
599     Symbol::delete_package "safe::$_"
600 root 1.45 for qw(cf::object cf::object::player cf::player cf::map cf::party cf::region);
601 root 1.36
602     # 5. remove register_script_function callbacks
603     # TODO
604    
605     # 6. unload cf.pm "a bit"
606     delete $INC{"cf.pm"};
607    
608 root 1.41 # don't, removes xs symbols, too,
609     # and global variables created in xs
610 root 1.36 #Symbol::delete_package __PACKAGE__;
611    
612     # 7. reload cf.pm
613     $msg->("reloading cf.pm");
614     require cf;
615     };
616     $msg->($@) if $@;
617 root 1.27
618 root 1.36 $msg->("reloaded");
619     };
620 root 1.27
621 root 1.36 sub perl_reload() {
622     _perl_reload {
623     warn $_[0];
624     print "$_[0]\n";
625     };
626     }
627 root 1.27
628 root 1.36 register_command "perl-reload", 0, sub {
629     my ($who, $arg) = @_;
630 root 1.27
631 root 1.36 if ($who->flag (FLAG_WIZ)) {
632     _perl_reload {
633     warn $_[0];
634     $who->message ($_[0]);
635 root 1.4 };
636 root 1.1 }
637     };
638    
639 root 1.8 #############################################################################
640 root 1.28 # extcmd framework, basically convert ext <msg>
641 root 1.15 # into pkg::->on_extcmd_arg1 (...) while shortcutting a few
642    
643 root 1.44 attach_to_players
644 root 1.43 on_extcmd => sub {
645     my ($pl, $buf) = @_;
646    
647     my $msg = eval { from_json $buf };
648    
649     if (ref $msg) {
650     if (my $cb = $extcmd{$msg->{msgtype}}) {
651     if (my %reply = $cb->[0]->($pl, $msg)) {
652     $pl->ext_reply ($msg->{msgid}, %reply);
653     }
654 root 1.28 }
655 root 1.43 } else {
656     warn "player " . ($pl->ob->name) . " sent unparseable ext message: <$buf>\n";
657 root 1.28 }
658 root 1.15
659 root 1.43 cf::override;
660     },
661     ;
662 root 1.15
663     #############################################################################
664 root 1.8 # load/save/clean perl data associated with a map
665    
666 root 1.39 *cf::mapsupport::on_clean = sub {
667 root 1.13 my ($map) = @_;
668 root 1.7
669     my $path = $map->tmpname;
670     defined $path or return;
671    
672     unlink "$path.cfperl";
673 root 1.46 unlink "$path.pst";
674 root 1.7 };
675    
676 root 1.39 *cf::mapsupport::on_swapin =
677     *cf::mapsupport::on_load = sub {
678 root 1.13 my ($map) = @_;
679 root 1.6
680     my $path = $map->tmpname;
681     $path = $map->path unless defined $path;
682    
683     open my $fh, "<:raw", "$path.cfperl"
684     or return; # no perl data
685    
686     my $data = Storable::thaw do { local $/; <$fh> };
687    
688     $data->{version} <= 1
689     or return; # too new
690    
691     $map->_set_obs ($data->{obs});
692     };
693    
694 root 1.39 attach_to_maps prio => -10000, package => cf::mapsupport::;
695    
696 root 1.8 #############################################################################
697     # load/save perl data associated with player->ob objects
698    
699 root 1.33 sub all_objects(@) {
700     @_, map all_objects ($_->inv), @_
701     }
702    
703 root 1.39 attach_to_players
704     on_load => sub {
705     my ($pl, $path) = @_;
706    
707     for my $o (all_objects $pl->ob) {
708     if (my $value = $o->get_ob_key_value ("_perl_data")) {
709     $o->set_ob_key_value ("_perl_data");
710 root 1.8
711 root 1.39 %$o = %{ Storable::thaw pack "H*", $value };
712     }
713 root 1.11 }
714 root 1.39 },
715     ;
716 root 1.6
717 root 1.22 #############################################################################
718     # core extensions - in perl
719    
720 root 1.23 =item cf::player::exists $login
721    
722     Returns true when the given account exists.
723    
724     =cut
725    
726     sub cf::player::exists($) {
727     cf::player::find $_[0]
728     or -f sprintf "%s/%s/%s/%s.pl", cf::localdir, cf::playerdir, ($_[0]) x 2;
729     }
730    
731 root 1.28 =item $player->reply ($npc, $msg[, $flags])
732    
733     Sends a message to the player, as if the npc C<$npc> replied. C<$npc>
734     can be C<undef>. Does the right thing when the player is currently in a
735     dialogue with the given NPC character.
736    
737     =cut
738    
739 root 1.22 # rough implementation of a future "reply" method that works
740     # with dialog boxes.
741 root 1.23 sub cf::object::player::reply($$$;$) {
742     my ($self, $npc, $msg, $flags) = @_;
743    
744     $flags = cf::NDI_BROWN | cf::NDI_UNIQUE unless @_ >= 4;
745 root 1.22
746 root 1.24 if ($self->{record_replies}) {
747     push @{ $self->{record_replies} }, [$npc, $msg, $flags];
748     } else {
749     $msg = $npc->name . " says: $msg" if $npc;
750     $self->message ($msg, $flags);
751     }
752 root 1.22 }
753    
754 root 1.28 =item $player->ext_reply ($msgid, $msgtype, %msg)
755    
756     Sends an ext reply to the player.
757    
758     =cut
759    
760     sub cf::player::ext_reply($$$%) {
761     my ($self, $id, %msg) = @_;
762    
763     $msg{msgid} = $id;
764    
765     $self->send ("ext " . to_json \%msg);
766     }
767    
768 root 1.22 #############################################################################
769 root 1.23 # map scripting support
770    
771 root 1.42 our $safe = new Safe "safe";
772 root 1.23 our $safe_hole = new Safe::Hole;
773    
774     $SIG{FPE} = 'IGNORE';
775    
776     $safe->permit_only (Opcode::opset qw(:base_core :base_mem :base_orig :base_math sort time));
777    
778 root 1.25 # here we export the classes and methods available to script code
779    
780     for (
781 root 1.27 ["cf::object" => qw(contr pay_amount pay_player)],
782 root 1.25 ["cf::object::player" => qw(player)],
783     ["cf::player" => qw(peaceful)],
784     ) {
785     no strict 'refs';
786     my ($pkg, @funs) = @$_;
787 root 1.41 *{"safe::$pkg\::$_"} = $safe_hole->wrap (\&{"$pkg\::$_"})
788 root 1.25 for @funs;
789     }
790 root 1.23
791     sub safe_eval($;@) {
792     my ($code, %vars) = @_;
793    
794     my $qcode = $code;
795     $qcode =~ s/"/‟/g; # not allowed in #line filenames
796     $qcode =~ s/\n/\\n/g;
797    
798     local $_;
799 root 1.41 local @safe::cf::_safe_eval_args = values %vars;
800 root 1.23
801 root 1.42 my $eval =
802 root 1.23 "do {\n"
803     . "my (" . (join ",", map "\$$_", keys %vars) . ") = \@cf::_safe_eval_args;\n"
804     . "#line 0 \"{$qcode}\"\n"
805     . $code
806     . "\n}"
807 root 1.25 ;
808    
809     sub_generation_inc;
810 root 1.42 my @res = wantarray ? $safe->reval ($eval) : scalar $safe->reval ($eval);
811 root 1.25 sub_generation_inc;
812    
813 root 1.42 if ($@) {
814     warn "$@";
815     warn "while executing safe code '$code'\n";
816     warn "with arguments " . (join " ", %vars) . "\n";
817     }
818    
819 root 1.25 wantarray ? @res : $res[0]
820 root 1.23 }
821    
822     sub register_script_function {
823     my ($fun, $cb) = @_;
824    
825     no strict 'refs';
826 root 1.41 *{"safe::$fun"} = $safe_hole->wrap ($cb);
827 root 1.23 }
828    
829     #############################################################################
830 root 1.34 # the server's main()
831    
832 root 1.39 sub main {
833 root 1.34 Event::loop;
834     }
835    
836     #############################################################################
837 root 1.22 # initialisation
838    
839 root 1.6 register "<global>", __PACKAGE__;
840    
841 root 1.27 unshift @INC, $LIBDIR;
842 root 1.17
843 root 1.1 load_extensions;
844    
845 root 1.35 $TICK_WATCHER = Event->timer (
846     prio => 1,
847     at => $NEXT_TICK || 1,
848     cb => sub {
849     cf::server_tick; # one server iteration
850    
851     my $NOW = Event::time;
852     $NEXT_TICK += $TICK;
853    
854 root 1.37 # if we are delayed by four ticks, skip them all
855     $NEXT_TICK = $NOW if $NOW >= $NEXT_TICK + $TICK * 4;
856 root 1.35
857     $TICK_WATCHER->at ($NEXT_TICK);
858     $TICK_WATCHER->start;
859     },
860     );
861    
862 root 1.47 _reload_2;
863    
864 root 1.1 1
865