ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.46
Committed: Sun Aug 27 16:15:12 2006 UTC (17 years, 9 months ago) by root
Branch: MAIN
Changes since 1.45: +138 -58 lines
Log Message:
first, untested persistent objetc storage for players and objects, not yte for maps

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