ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.61
Committed: Sun Sep 3 22:45:56 2006 UTC (17 years, 8 months ago) by root
Branch: MAIN
Changes since 1.60: +17 -5 lines
Log Message:
string scanning (e.g. for patch) is not implemented ATM but should be easy
to add with an alternative constructor for object_thawer.

Rewrote flex scanner to be simpler, faster and more modularised.

Initial speedup: 16%

(ah well)

File Contents

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