ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.51
Committed: Mon Aug 28 14:05:24 2006 UTC (17 years, 9 months ago) by root
Branch: MAIN
Changes since 1.50: +8 -7 lines
Log Message:
improved,more automatic freezer

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.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 root 1.50 $obj->{_attachment}{$name} = undef;
268 root 1.46
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.50 delete $obj->{_attachment} unless scalar keys %{ $obj->{_attachment} || {} };
374 root 1.49
375 root 1.50 for my $name (keys %{ $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     if (@$objs) {
391 root 1.51 open my $fh, ">:raw", "$filename.pst~";
392 root 1.46 syswrite $fh, Storable::nfreeze { version => 1, objs => $objs };
393     close $fh;
394 root 1.51 chmod SAVE_MODE, "$filename.pst~";
395     rename "$filename.pst~", "$filename.pst";
396 root 1.46 } else {
397 root 1.51 unlink "$filename.pst";
398 root 1.45 }
399 root 1.51
400     chmod SAVE_MODE, "$filename~";
401     rename "$filename~", $filename;
402 root 1.45 }
403    
404 root 1.46 sub object_thawer_load {
405     my ($filename) = @_;
406    
407     open my $fh, "<:raw:perlio", "$filename.pst"
408     or return;
409 root 1.45
410 root 1.46 eval { local $/; (Storable::thaw <$fh>)->{objs} }
411 root 1.45 }
412    
413     attach_to_objects
414     prio => -1000000,
415     on_clone => sub {
416     my ($src, $dst) = @_;
417    
418     @{$dst->registry} = @{$src->registry};
419    
420     %$dst = %$src;
421    
422 root 1.50 %{$dst->{_attachment}} = %{$src->{_attachment}}
423 root 1.45 if exists $src->{_attachment};
424     },
425     ;
426    
427     #############################################################################
428 root 1.39 # old plug-in events
429    
430 root 1.1 sub inject_event {
431 root 1.14 my $extension = shift;
432     my $event_code = shift;
433 root 1.1
434 root 1.14 my $cb = $hook[$event_code]{$extension}
435 root 1.5 or return;
436    
437 root 1.14 &$cb
438 root 1.5 }
439    
440     sub inject_global_event {
441 root 1.12 my $event = shift;
442 root 1.5
443 root 1.12 my $cb = $hook[$event]
444 root 1.1 or return;
445    
446 root 1.12 List::Util::max map &$_, values %$cb
447 root 1.1 }
448    
449     sub inject_command {
450     my ($name, $obj, $params) = @_;
451    
452     for my $cmd (@{ $command{$name} }) {
453     $cmd->[1]->($obj, $params);
454     }
455    
456     -1
457     }
458    
459     sub register_command {
460     my ($name, $time, $cb) = @_;
461    
462     my $caller = caller;
463 root 1.16 #warn "registering command '$name/$time' to '$caller'";
464 root 1.4
465 root 1.1 push @{ $command{$name} }, [$time, $cb, $caller];
466     $COMMAND{"$name\000"} = List::Util::max map $_->[0], @{ $command{$name} };
467     }
468    
469 root 1.16 sub register_extcmd {
470     my ($name, $cb) = @_;
471    
472     my $caller = caller;
473     #warn "registering extcmd '$name' to '$caller'";
474    
475     $extcmd{$name} = [$cb, $caller];
476     }
477    
478 root 1.6 sub register {
479     my ($base, $pkg) = @_;
480    
481 root 1.45 #TODO
482 root 1.6 }
483    
484 root 1.1 sub load_extension {
485     my ($path) = @_;
486    
487     $path =~ /([^\/\\]+)\.ext$/ or die "$path";
488 root 1.5 my $base = $1;
489 root 1.1 my $pkg = $1;
490     $pkg =~ s/[^[:word:]]/_/g;
491 root 1.41 $pkg = "ext::$pkg";
492 root 1.1
493     warn "loading '$path' into '$pkg'\n";
494    
495     open my $fh, "<:utf8", $path
496     or die "$path: $!";
497    
498     my $source =
499     "package $pkg; use strict; use utf8;\n"
500     . "#line 1 \"$path\"\n{\n"
501     . (do { local $/; <$fh> })
502     . "\n};\n1";
503    
504     eval $source
505     or die "$path: $@";
506    
507     push @exts, $pkg;
508 root 1.5 $ext_pkg{$base} = $pkg;
509 root 1.1
510 root 1.6 # no strict 'refs';
511 root 1.23 # @{"$pkg\::ISA"} = ext::;
512 root 1.1
513 root 1.6 register $base, $pkg;
514 root 1.1 }
515    
516     sub unload_extension {
517     my ($pkg) = @_;
518    
519     warn "removing extension $pkg\n";
520    
521     # remove hooks
522 root 1.45 #TODO
523     # for my $idx (0 .. $#PLUGIN_EVENT) {
524     # delete $hook[$idx]{$pkg};
525     # }
526 root 1.1
527     # remove commands
528     for my $name (keys %command) {
529     my @cb = grep $_->[2] ne $pkg, @{ $command{$name} };
530    
531     if (@cb) {
532     $command{$name} = \@cb;
533     $COMMAND{"$name\000"} = List::Util::max map $_->[0], @cb;
534     } else {
535     delete $command{$name};
536     delete $COMMAND{"$name\000"};
537     }
538     }
539    
540 root 1.15 # remove extcmds
541 root 1.16 for my $name (grep $extcmd{$_}[1] eq $pkg, keys %extcmd) {
542     delete $extcmd{$name};
543 root 1.15 }
544    
545 root 1.43 if (my $cb = $pkg->can ("unload")) {
546 elmex 1.31 eval {
547     $cb->($pkg);
548     1
549     } or warn "$pkg unloaded, but with errors: $@";
550     }
551    
552 root 1.1 Symbol::delete_package $pkg;
553     }
554    
555     sub load_extensions {
556     my $LIBDIR = maps_directory "perl";
557    
558     for my $ext (<$LIBDIR/*.ext>) {
559 root 1.3 next unless -r $ext;
560 root 1.2 eval {
561     load_extension $ext;
562     1
563     } or warn "$ext not loaded: $@";
564 root 1.1 }
565     }
566    
567 root 1.36 sub _perl_reload(&) {
568     my ($msg) = @_;
569    
570     $msg->("reloading...");
571    
572     eval {
573     # 1. cancel all watchers
574     $_->cancel for Event::all_watchers;
575    
576     # 2. unload all extensions
577     for (@exts) {
578     $msg->("unloading <$_>");
579     unload_extension $_;
580     }
581    
582     # 3. unload all modules loaded from $LIBDIR
583     while (my ($k, $v) = each %INC) {
584     next unless $v =~ /^\Q$LIBDIR\E\/.*\.pm$/;
585    
586     $msg->("removing <$k>");
587     delete $INC{$k};
588 root 1.1
589 root 1.36 $k =~ s/\.pm$//;
590     $k =~ s/\//::/g;
591 root 1.3
592 root 1.36 if (my $cb = $k->can ("unload_module")) {
593     $cb->();
594 root 1.27 }
595    
596 root 1.36 Symbol::delete_package $k;
597     }
598 root 1.27
599 root 1.41 # 4. get rid of safe::, as good as possible
600     Symbol::delete_package "safe::$_"
601 root 1.45 for qw(cf::object cf::object::player cf::player cf::map cf::party cf::region);
602 root 1.36
603     # 5. remove register_script_function callbacks
604     # TODO
605    
606     # 6. unload cf.pm "a bit"
607     delete $INC{"cf.pm"};
608    
609 root 1.41 # don't, removes xs symbols, too,
610     # and global variables created in xs
611 root 1.36 #Symbol::delete_package __PACKAGE__;
612    
613     # 7. reload cf.pm
614     $msg->("reloading cf.pm");
615     require cf;
616     };
617     $msg->($@) if $@;
618 root 1.27
619 root 1.36 $msg->("reloaded");
620     };
621 root 1.27
622 root 1.36 sub perl_reload() {
623     _perl_reload {
624     warn $_[0];
625     print "$_[0]\n";
626     };
627     }
628 root 1.27
629 root 1.36 register_command "perl-reload", 0, sub {
630     my ($who, $arg) = @_;
631 root 1.27
632 root 1.36 if ($who->flag (FLAG_WIZ)) {
633     _perl_reload {
634     warn $_[0];
635     $who->message ($_[0]);
636 root 1.4 };
637 root 1.1 }
638     };
639    
640 root 1.8 #############################################################################
641 root 1.28 # extcmd framework, basically convert ext <msg>
642 root 1.15 # into pkg::->on_extcmd_arg1 (...) while shortcutting a few
643    
644 root 1.44 attach_to_players
645 root 1.43 on_extcmd => sub {
646     my ($pl, $buf) = @_;
647    
648     my $msg = eval { from_json $buf };
649    
650     if (ref $msg) {
651     if (my $cb = $extcmd{$msg->{msgtype}}) {
652     if (my %reply = $cb->[0]->($pl, $msg)) {
653     $pl->ext_reply ($msg->{msgid}, %reply);
654     }
655 root 1.28 }
656 root 1.43 } else {
657     warn "player " . ($pl->ob->name) . " sent unparseable ext message: <$buf>\n";
658 root 1.28 }
659 root 1.15
660 root 1.43 cf::override;
661     },
662     ;
663 root 1.15
664     #############################################################################
665 root 1.8 # load/save/clean perl data associated with a map
666    
667 root 1.39 *cf::mapsupport::on_clean = sub {
668 root 1.13 my ($map) = @_;
669 root 1.7
670     my $path = $map->tmpname;
671     defined $path or return;
672    
673 root 1.46 unlink "$path.pst";
674 root 1.7 };
675    
676 root 1.51 # old style persistent data, TODO: remove #d#
677 root 1.39 *cf::mapsupport::on_swapin =
678     *cf::mapsupport::on_load = sub {
679 root 1.13 my ($map) = @_;
680 root 1.6
681     my $path = $map->tmpname;
682     $path = $map->path unless defined $path;
683    
684     open my $fh, "<:raw", "$path.cfperl"
685     or return; # no perl data
686    
687     my $data = Storable::thaw do { local $/; <$fh> };
688    
689     $data->{version} <= 1
690     or return; # too new
691    
692     $map->_set_obs ($data->{obs});
693 root 1.50 $map->invoke (EVENT_MAP_UPGRADE);
694 root 1.6 };
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.47 _reload_2;
865    
866 root 1.1 1
867