ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.93
Committed: Thu Dec 21 22:41:35 2006 UTC (17 years, 5 months ago) by root
Branch: MAIN
Changes since 1.92: +88 -176 lines
Log Message:
- updated cf.pm to use a more generic and extendable syntax,
  now that it is clear that we will have multiple "attachable" objects.
  maybe bite the bullet in C++ and make attachable virtual?
- completely rework the syntax for attaching and attachments
- update all extensions

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.77 use IO::AIO ();
11 root 1.72 use YAML::Syck ();
12 root 1.32 use Time::HiRes;
13 root 1.18 use Event;
14 root 1.19 $Event::Eval = 1; # no idea why this is required, but it is
15 root 1.1
16 root 1.72 # work around bug in YAML::Syck - bad news for perl6, will it be as broken wrt. unicode?
17     $YAML::Syck::ImplicitUnicode = 1;
18    
19 root 1.1 use strict;
20    
21 root 1.87 sub WF_AUTOCANCEL () { 1 } # automatically cancel this watcher on reload
22    
23 root 1.85 our %COMMAND = ();
24     our %COMMAND_TIME = ();
25     our %EXTCMD = ();
26    
27 root 1.63 _init_vars;
28 root 1.47
29 root 1.1 our @EVENT;
30 root 1.88 our $LIBDIR = datadir . "/ext";
31 root 1.1
32 root 1.35 our $TICK = MAX_TIME * 1e-6;
33     our $TICK_WATCHER;
34     our $NEXT_TICK;
35    
36 root 1.70 our %CFG;
37    
38 root 1.84 our $UPTIME; $UPTIME ||= time;
39 root 1.76
40 root 1.70 #############################################################################
41    
42     =head2 GLOBAL VARIABLES
43    
44     =over 4
45    
46 root 1.83 =item $cf::UPTIME
47    
48     The timestamp of the server start (so not actually an uptime).
49    
50 root 1.70 =item $cf::LIBDIR
51    
52     The perl library directory, where extensions and cf-specific modules can
53     be found. It will be added to C<@INC> automatically.
54    
55     =item $cf::TICK
56    
57     The interval between server ticks, in seconds.
58    
59     =item %cf::CFG
60    
61     Configuration for the server, loaded from C</etc/crossfire/config>, or
62     from wherever your confdir points to.
63    
64     =back
65    
66     =cut
67    
68 root 1.1 BEGIN {
69     *CORE::GLOBAL::warn = sub {
70     my $msg = join "", @_;
71     $msg .= "\n"
72     unless $msg =~ /\n$/;
73    
74     print STDERR "cfperl: $msg";
75     LOG llevError, "cfperl: $msg";
76     };
77     }
78    
79 root 1.93 @safe::cf::global::ISA = @cf::global::ISA = 'cf::attachable';
80     @safe::cf::object::ISA = @cf::object::ISA = 'cf::attachable';
81     @safe::cf::player::ISA = @cf::player::ISA = 'cf::attachable';
82     @safe::cf::client::ISA = @cf::client::ISA = 'cf::attachable';
83     @safe::cf::map::ISA = @cf::map::ISA = 'cf::attachable';
84 root 1.41 @safe::cf::object::player::ISA = @cf::object::player::ISA = 'cf::object';
85 root 1.25
86 root 1.41 # we bless all objects into (empty) derived classes to force a method lookup
87 root 1.25 # within the Safe compartment.
88 root 1.86 for my $pkg (qw(
89 root 1.93 cf::global
90 root 1.86 cf::object cf::object::player
91 root 1.89 cf::client cf::player
92 root 1.86 cf::arch cf::living
93     cf::map cf::party cf::region
94     )) {
95 root 1.25 no strict 'refs';
96 root 1.41 @{"safe::$pkg\::wrap::ISA"} = @{"$pkg\::wrap::ISA"} = $pkg;
97 root 1.25 }
98 root 1.1
99 root 1.18 $Event::DIED = sub {
100     warn "error in event callback: @_";
101     };
102    
103 root 1.5 my %ext_pkg;
104 root 1.1 my @exts;
105     my @hook;
106    
107 root 1.70 =head2 UTILITY FUNCTIONS
108    
109     =over 4
110    
111     =cut
112 root 1.44
113 root 1.45 use JSON::Syck (); # TODO# replace by JSON::PC once working
114 root 1.44
115 root 1.70 =item $ref = cf::from_json $json
116    
117     Converts a JSON string into the corresponding perl data structure.
118    
119     =cut
120    
121 root 1.45 sub from_json($) {
122     $JSON::Syck::ImplicitUnicode = 1; # work around JSON::Syck bugs
123     JSON::Syck::Load $_[0]
124 root 1.44 }
125    
126 root 1.70 =item $json = cf::to_json $ref
127    
128     Converts a perl data structure into its JSON representation.
129    
130     =cut
131    
132 root 1.45 sub to_json($) {
133     $JSON::Syck::ImplicitUnicode = 0; # work around JSON::Syck bugs
134     JSON::Syck::Dump $_[0]
135 root 1.44 }
136    
137 root 1.70 =back
138    
139 root 1.71 =cut
140    
141 root 1.44 #############################################################################
142 root 1.39
143 root 1.93 =head2 ATTACHABLE OBJECTS
144    
145     You can define and attach attachments to each "attachable" object in
146     crossfire+ (objects, players, clients, maps and the special "global"
147     class). In the following description, CLASS can be any of C<global>,
148     C<object> C<player>, C<client> or C<map>.
149 root 1.55
150     =over 4
151    
152 root 1.93 =item cf::CLASS::attachment $name, ...
153 root 1.46
154 root 1.93 Register an attachment by name through which attachable objects can refer
155     to this attachment.
156 root 1.53
157 root 1.93 =item $bool = $attachable->attached ($name)
158 root 1.46
159 root 1.93 Checks wether the named attachment is currently attached to the object.
160 root 1.46
161 root 1.93 =item $attachable->attach ($attachment, key => $value...)
162 root 1.46
163 root 1.93 =item $attachable->detach ($attachment)
164 root 1.92
165 root 1.93 Attach/detach a pre-registered attachment either to a specific object
166     (C<$attachable>) or all objects of the given class (if C<$attachable> is a
167     class in a static method call).
168 root 1.92
169 root 1.93 You can attach to global events by using the C<cf::global> class.
170 root 1.92
171 root 1.93 These method calls expect any number of the following handler/hook
172     descriptions:
173 root 1.46
174     =over 4
175    
176     =item prio => $number
177    
178     Set the priority for all following handlers/hooks (unless overwritten
179     by another C<prio> setting). Lower priority handlers get executed
180     earlier. The default priority is C<0>, and many built-in handlers are
181     registered at priority C<-1000>, so lower priorities should not be used
182     unless you know what you are doing.
183    
184 root 1.93 =item type => $type
185    
186     (Only for C<< cf::object->attach >> calls), limits the attachment to the
187     given type of objects only (the additional parameter C<subtype> can be
188     used to further limit to the given subtype).
189    
190 root 1.46 =item on_I<event> => \&cb
191    
192     Call the given code reference whenever the named event happens (event is
193     something like C<instantiate>, C<apply>, C<use_skill> and so on, and which
194     handlers are recognised generally depends on the type of object these
195     handlers attach to).
196    
197     See F<include/eventinc.h> for the full list of events supported, and their
198     class.
199    
200     =item package => package::
201    
202     Look for sub functions of the name C<< on_I<event> >> in the given
203     package and register them. Only handlers for eevents supported by the
204     object/class are recognised.
205    
206     =back
207    
208 root 1.39 =cut
209    
210 root 1.40 # the following variables are defined in .xs and must not be re-created
211 root 1.39 our @CB_GLOBAL = (); # registry for all global events
212 root 1.45 our @CB_OBJECT = (); # all objects (should not be used except in emergency)
213 root 1.40 our @CB_PLAYER = ();
214 root 1.92 our @CB_CLIENT = ();
215 root 1.39 our @CB_TYPE = (); # registry for type (cf-object class) based events
216 root 1.40 our @CB_MAP = ();
217 root 1.39
218 root 1.45 my %attachment;
219    
220 root 1.93 sub _attach_cb($$$$) {
221     my ($registry, $event, $prio, $cb) = @_;
222 root 1.39
223     use sort 'stable';
224    
225     $cb = [$prio, $cb];
226    
227     @{$registry->[$event]} = sort
228     { $a->[0] cmp $b->[0] }
229     @{$registry->[$event] || []}, $cb;
230     }
231    
232     # attach handles attaching event callbacks
233     # the only thing the caller has to do is pass the correct
234     # registry (== where the callback attaches to).
235 root 1.93 sub _attach {
236 root 1.45 my ($registry, $klass, @arg) = @_;
237 root 1.39
238 root 1.93 my $object_type;
239 root 1.39 my $prio = 0;
240     my %cb_id = map +("on_" . lc $EVENT[$_][0], $_) , grep $EVENT[$_][1] == $klass, 0 .. $#EVENT;
241    
242 root 1.45 while (@arg) {
243     my $type = shift @arg;
244 root 1.39
245     if ($type eq "prio") {
246 root 1.45 $prio = shift @arg;
247 root 1.39
248 root 1.93 } elsif ($type eq "type") {
249     $object_type = shift @arg;
250     $registry = $CB_TYPE[$object_type] ||= [];
251    
252     } elsif ($type eq "subtype") {
253     defined $object_type or Carp::croak "subtype specified without type";
254     my $object_subtype = shift @arg;
255     $registry = $CB_TYPE[$object_type + $object_subtype * NUM_SUBTYPES] ||= [];
256    
257 root 1.39 } elsif ($type eq "package") {
258 root 1.45 my $pkg = shift @arg;
259 root 1.39
260     while (my ($name, $id) = each %cb_id) {
261     if (my $cb = $pkg->can ($name)) {
262 root 1.93 _attach_cb $registry, $id, $prio, $cb;
263 root 1.39 }
264     }
265    
266     } elsif (exists $cb_id{$type}) {
267 root 1.93 _attach_cb $registry, $cb_id{$type}, $prio, shift @arg;
268 root 1.39
269     } elsif (ref $type) {
270     warn "attaching objects not supported, ignoring.\n";
271    
272     } else {
273 root 1.45 shift @arg;
274 root 1.39 warn "attach argument '$type' not supported, ignoring.\n";
275     }
276     }
277     }
278    
279 root 1.93 sub _object_attach {
280 root 1.48 my ($obj, $name, %arg) = @_;
281 root 1.46
282 root 1.55 return if exists $obj->{_attachment}{$name};
283    
284 root 1.46 if (my $attach = $attachment{$name}) {
285     my $registry = $obj->registry;
286    
287 root 1.47 for (@$attach) {
288     my ($klass, @attach) = @$_;
289 root 1.93 _attach $registry, $klass, @attach;
290 root 1.47 }
291 root 1.46
292 root 1.48 $obj->{$name} = \%arg;
293 root 1.46 } else {
294     warn "object uses attachment '$name' that is not available, postponing.\n";
295     }
296    
297 root 1.50 $obj->{_attachment}{$name} = undef;
298 root 1.46 }
299    
300 root 1.93 sub cf::attachable::attach {
301     if (ref $_[0]) {
302     _object_attach @_;
303     } else {
304     _attach shift->_attach_registry, @_;
305     }
306 root 1.55 };
307 root 1.46
308 root 1.54 # all those should be optimised
309 root 1.93 sub cf::attachable::detach {
310 root 1.54 my ($obj, $name) = @_;
311 root 1.46
312 root 1.93 if (ref $obj) {
313     delete $obj->{_attachment}{$name};
314     reattach ($obj);
315     } else {
316     Carp::croak "cannot, currently, detach class attachments";
317     }
318 root 1.55 };
319    
320 root 1.93 sub cf::attachable::attached {
321 root 1.55 my ($obj, $name) = @_;
322    
323     exists $obj->{_attachment}{$name}
324 root 1.39 }
325    
326 root 1.93 for my $klass (qw(GLOBAL OBJECT PLAYER CLIENT MAP)) {
327     eval "#line " . __LINE__ . " 'cf.pm'
328     sub cf::\L$klass\E::_attach_registry {
329     (\\\@CB_$klass, KLASS_$klass)
330     }
331 root 1.45
332 root 1.93 sub cf::\L$klass\E::attachment {
333     my \$name = shift;
334 root 1.39
335 root 1.93 \$attachment{\$name} = [[KLASS_$klass, \@_]];
336     }
337     ";
338     die if $@;
339 root 1.52 }
340    
341 root 1.39 our $override;
342 root 1.45 our @invoke_results = (); # referenced from .xs code. TODO: play tricks with reify and mortals?
343 root 1.39
344 root 1.45 sub override {
345     $override = 1;
346     @invoke_results = ();
347 root 1.39 }
348    
349 root 1.45 sub do_invoke {
350 root 1.39 my $event = shift;
351 root 1.40 my $callbacks = shift;
352 root 1.39
353 root 1.45 @invoke_results = ();
354    
355 root 1.39 local $override;
356    
357 root 1.40 for (@$callbacks) {
358 root 1.39 eval { &{$_->[1]} };
359    
360     if ($@) {
361     warn "$@";
362 root 1.64 warn "... while processing $EVENT[$event][0](@_) event, skipping processing altogether.\n";
363 root 1.39 override;
364     }
365    
366     return 1 if $override;
367     }
368    
369     0
370     }
371    
372 root 1.55 =item $bool = cf::invoke EVENT_GLOBAL_XXX, ...
373    
374     =item $bool = $object->invoke (EVENT_OBJECT_XXX, ...)
375    
376     =item $bool = $player->invoke (EVENT_PLAYER_XXX, ...)
377    
378 root 1.92 =item $bool = $client->invoke (EVENT_CLIENT_XXX, ...)
379    
380 root 1.55 =item $bool = $map->invoke (EVENT_MAP_XXX, ...)
381    
382     Generate a global/object/player/map-specific event with the given arguments.
383    
384     This API is preliminary (most likely, the EVENT_KLASS_xxx prefix will be
385     removed in future versions), and there is no public API to access override
386     results (if you must, access C<@cf::invoke_results> directly).
387    
388     =back
389    
390 root 1.71 =cut
391    
392 root 1.70 #############################################################################
393    
394 root 1.93 =head2 METHODS VALID FOR ALL ATTACHABLE OBJECTS
395    
396     Attachable objects includes objects, players, clients and maps.
397 root 1.55
398     =over 4
399    
400 root 1.93 =item $object->valid
401 root 1.55
402     Just because you have a perl object does not mean that the corresponding
403     C-level object still exists. If you try to access an object that has no
404     valid C counterpart anymore you get an exception at runtime. This method
405     can be used to test for existence of the C object part without causing an
406     exception.
407    
408     =back
409    
410     =cut
411    
412 root 1.39 #############################################################################
413 root 1.45 # object support
414    
415     sub instantiate {
416     my ($obj, $data) = @_;
417    
418     $data = from_json $data;
419    
420     for (@$data) {
421 root 1.46 my ($name, $args) = @$_;
422 root 1.49
423     $obj->attach ($name, %{$args || {} });
424 root 1.46 }
425     }
426    
427     # basically do the same as instantiate, without calling instantiate
428     sub reattach {
429     my ($obj) = @_;
430     my $registry = $obj->registry;
431 root 1.45
432 root 1.47 @$registry = ();
433    
434 root 1.50 delete $obj->{_attachment} unless scalar keys %{ $obj->{_attachment} || {} };
435 root 1.49
436 root 1.50 for my $name (keys %{ $obj->{_attachment} || {} }) {
437 root 1.45 if (my $attach = $attachment{$name}) {
438 root 1.47 for (@$attach) {
439     my ($klass, @attach) = @$_;
440 root 1.93 _attach $registry, $klass, @attach;
441 root 1.47 }
442 root 1.45 } else {
443 root 1.46 warn "object uses attachment '$name' that is not available, postponing.\n";
444 root 1.45 }
445 root 1.46 }
446     }
447 root 1.45
448 root 1.46 sub object_freezer_save {
449 root 1.59 my ($filename, $rdata, $objs) = @_;
450 root 1.46
451 root 1.60 if (length $$rdata) {
452     warn sprintf "saving %s (%d,%d)\n",
453     $filename, length $$rdata, scalar @$objs;
454 root 1.59
455 root 1.60 if (open my $fh, ">:raw", "$filename~") {
456 root 1.59 chmod SAVE_MODE, $fh;
457 root 1.60 syswrite $fh, $$rdata;
458 root 1.59 close $fh;
459 root 1.60
460     if (@$objs && open my $fh, ">:raw", "$filename.pst~") {
461     chmod SAVE_MODE, $fh;
462     syswrite $fh, Storable::nfreeze { version => 1, objs => $objs };
463     close $fh;
464     rename "$filename.pst~", "$filename.pst";
465     } else {
466     unlink "$filename.pst";
467     }
468    
469     rename "$filename~", $filename;
470 root 1.59 } else {
471 root 1.60 warn "FATAL: $filename~: $!\n";
472 root 1.59 }
473 root 1.46 } else {
474 root 1.60 unlink $filename;
475     unlink "$filename.pst";
476 root 1.45 }
477     }
478    
479 root 1.80 sub object_freezer_as_string {
480     my ($rdata, $objs) = @_;
481    
482     use Data::Dumper;
483    
484 root 1.81 $$rdata . Dumper $objs
485 root 1.80 }
486    
487 root 1.46 sub object_thawer_load {
488     my ($filename) = @_;
489    
490 root 1.61 local $/;
491    
492     my $av;
493    
494     #TODO: use sysread etc.
495     if (open my $data, "<:raw:perlio", $filename) {
496     $data = <$data>;
497     if (open my $pst, "<:raw:perlio", "$filename.pst") {
498     $av = eval { (Storable::thaw <$pst>)->{objs} };
499     }
500     return ($data, $av);
501     }
502 root 1.45
503 root 1.61 ()
504 root 1.45 }
505    
506 root 1.93 cf::object->attach (
507 root 1.45 prio => -1000000,
508     on_clone => sub {
509     my ($src, $dst) = @_;
510    
511     @{$dst->registry} = @{$src->registry};
512    
513     %$dst = %$src;
514    
515 root 1.50 %{$dst->{_attachment}} = %{$src->{_attachment}}
516 root 1.45 if exists $src->{_attachment};
517     },
518 root 1.93 );
519 root 1.45
520     #############################################################################
521 root 1.85 # command handling &c
522 root 1.39
523 root 1.85 =item cf::register_command $name => \&callback($ob,$args);
524 root 1.1
525 root 1.85 Register a callback for execution when the client sends the user command
526     $name.
527 root 1.5
528 root 1.85 =cut
529 root 1.5
530 root 1.85 sub register_command {
531     my ($name, $cb) = @_;
532 root 1.5
533 root 1.85 my $caller = caller;
534     #warn "registering command '$name/$time' to '$caller'";
535 root 1.1
536 root 1.85 push @{ $COMMAND{$name} }, [$caller, $cb];
537 root 1.1 }
538    
539 root 1.85 =item cf::register_extcmd $name => \&callback($pl,$packet);
540 root 1.1
541 root 1.85 Register a callbackf ro execution when the client sends an extcmd packet.
542 root 1.1
543 root 1.85 If the callback returns something, it is sent back as if reply was being
544     called.
545 root 1.1
546 root 1.85 =cut
547 root 1.1
548 root 1.16 sub register_extcmd {
549     my ($name, $cb) = @_;
550    
551     my $caller = caller;
552     #warn "registering extcmd '$name' to '$caller'";
553    
554 root 1.85 $EXTCMD{$name} = [$cb, $caller];
555 root 1.16 }
556    
557 root 1.93 cf::player->attach (
558 root 1.85 on_command => sub {
559     my ($pl, $name, $params) = @_;
560    
561     my $cb = $COMMAND{$name}
562     or return;
563    
564     for my $cmd (@$cb) {
565     $cmd->[1]->($pl->ob, $params);
566     }
567    
568     cf::override;
569     },
570     on_extcmd => sub {
571     my ($pl, $buf) = @_;
572    
573     my $msg = eval { from_json $buf };
574    
575     if (ref $msg) {
576     if (my $cb = $EXTCMD{$msg->{msgtype}}) {
577     if (my %reply = $cb->[0]->($pl, $msg)) {
578     $pl->ext_reply ($msg->{msgid}, %reply);
579     }
580     }
581     } else {
582     warn "player " . ($pl->ob->name) . " sent unparseable ext message: <$buf>\n";
583     }
584    
585     cf::override;
586     },
587 root 1.93 );
588 root 1.85
589 root 1.6 sub register {
590     my ($base, $pkg) = @_;
591    
592 root 1.45 #TODO
593 root 1.6 }
594    
595 root 1.1 sub load_extension {
596     my ($path) = @_;
597    
598     $path =~ /([^\/\\]+)\.ext$/ or die "$path";
599 root 1.5 my $base = $1;
600 root 1.1 my $pkg = $1;
601     $pkg =~ s/[^[:word:]]/_/g;
602 root 1.41 $pkg = "ext::$pkg";
603 root 1.1
604     warn "loading '$path' into '$pkg'\n";
605    
606     open my $fh, "<:utf8", $path
607     or die "$path: $!";
608    
609     my $source =
610     "package $pkg; use strict; use utf8;\n"
611     . "#line 1 \"$path\"\n{\n"
612     . (do { local $/; <$fh> })
613     . "\n};\n1";
614    
615     eval $source
616 root 1.82 or die $@ ? "$path: $@\n"
617     : "extension disabled.\n";
618 root 1.1
619     push @exts, $pkg;
620 root 1.5 $ext_pkg{$base} = $pkg;
621 root 1.1
622 root 1.6 # no strict 'refs';
623 root 1.23 # @{"$pkg\::ISA"} = ext::;
624 root 1.1
625 root 1.6 register $base, $pkg;
626 root 1.1 }
627    
628     sub unload_extension {
629     my ($pkg) = @_;
630    
631     warn "removing extension $pkg\n";
632    
633     # remove hooks
634 root 1.45 #TODO
635     # for my $idx (0 .. $#PLUGIN_EVENT) {
636     # delete $hook[$idx]{$pkg};
637     # }
638 root 1.1
639     # remove commands
640 root 1.85 for my $name (keys %COMMAND) {
641     my @cb = grep $_->[0] ne $pkg, @{ $COMMAND{$name} };
642 root 1.1
643     if (@cb) {
644 root 1.85 $COMMAND{$name} = \@cb;
645 root 1.1 } else {
646 root 1.85 delete $COMMAND{$name};
647 root 1.1 }
648     }
649    
650 root 1.15 # remove extcmds
651 root 1.85 for my $name (grep $EXTCMD{$_}[1] eq $pkg, keys %EXTCMD) {
652     delete $EXTCMD{$name};
653 root 1.15 }
654    
655 root 1.43 if (my $cb = $pkg->can ("unload")) {
656 elmex 1.31 eval {
657     $cb->($pkg);
658     1
659     } or warn "$pkg unloaded, but with errors: $@";
660     }
661    
662 root 1.1 Symbol::delete_package $pkg;
663     }
664    
665     sub load_extensions {
666     for my $ext (<$LIBDIR/*.ext>) {
667 root 1.3 next unless -r $ext;
668 root 1.2 eval {
669     load_extension $ext;
670     1
671     } or warn "$ext not loaded: $@";
672 root 1.1 }
673     }
674    
675 root 1.8 #############################################################################
676     # load/save/clean perl data associated with a map
677    
678 root 1.39 *cf::mapsupport::on_clean = sub {
679 root 1.13 my ($map) = @_;
680 root 1.7
681     my $path = $map->tmpname;
682     defined $path or return;
683    
684 root 1.46 unlink "$path.pst";
685 root 1.7 };
686    
687 root 1.93 cf::map->attach (prio => -10000, package => cf::mapsupport::);
688 root 1.39
689 root 1.8 #############################################################################
690     # load/save perl data associated with player->ob objects
691    
692 root 1.33 sub all_objects(@) {
693     @_, map all_objects ($_->inv), @_
694     }
695    
696 root 1.60 # TODO: compatibility cruft, remove when no longer needed
697 root 1.93 cf::player->attach (
698 root 1.39 on_load => sub {
699     my ($pl, $path) = @_;
700    
701     for my $o (all_objects $pl->ob) {
702     if (my $value = $o->get_ob_key_value ("_perl_data")) {
703     $o->set_ob_key_value ("_perl_data");
704 root 1.8
705 root 1.39 %$o = %{ Storable::thaw pack "H*", $value };
706     }
707 root 1.11 }
708 root 1.39 },
709 root 1.93 );
710 root 1.6
711 root 1.22 #############################################################################
712 root 1.70
713     =head2 CORE EXTENSIONS
714    
715     Functions and methods that extend core crossfire objects.
716    
717     =over 4
718 root 1.22
719 root 1.23 =item cf::player::exists $login
720    
721     Returns true when the given account exists.
722    
723     =cut
724    
725     sub cf::player::exists($) {
726     cf::player::find $_[0]
727     or -f sprintf "%s/%s/%s/%s.pl", cf::localdir, cf::playerdir, ($_[0]) x 2;
728     }
729    
730 root 1.79 =item $player_object->reply ($npc, $msg[, $flags])
731 root 1.28
732     Sends a message to the player, as if the npc C<$npc> replied. C<$npc>
733     can be C<undef>. Does the right thing when the player is currently in a
734     dialogue with the given NPC character.
735    
736     =cut
737    
738 root 1.22 # rough implementation of a future "reply" method that works
739     # with dialog boxes.
740 root 1.23 sub cf::object::player::reply($$$;$) {
741     my ($self, $npc, $msg, $flags) = @_;
742    
743     $flags = cf::NDI_BROWN | cf::NDI_UNIQUE unless @_ >= 4;
744 root 1.22
745 root 1.24 if ($self->{record_replies}) {
746     push @{ $self->{record_replies} }, [$npc, $msg, $flags];
747     } else {
748     $msg = $npc->name . " says: $msg" if $npc;
749     $self->message ($msg, $flags);
750     }
751 root 1.22 }
752    
753 root 1.28 =item $player->ext_reply ($msgid, $msgtype, %msg)
754    
755     Sends an ext reply to the player.
756    
757     =cut
758    
759     sub cf::player::ext_reply($$$%) {
760     my ($self, $id, %msg) = @_;
761    
762     $msg{msgid} = $id;
763    
764     $self->send ("ext " . to_json \%msg);
765     }
766    
767 root 1.79 =item $player_object->may ("access")
768    
769     Returns wether the given player is authorized to access resource "access"
770     (e.g. "command_wizcast").
771    
772     =cut
773    
774     sub cf::object::player::may {
775     my ($self, $access) = @_;
776    
777     $self->flag (cf::FLAG_WIZ) ||
778     (ref $cf::CFG{"may_$access"}
779     ? scalar grep $self->name eq $_, @{$cf::CFG{"may_$access"}}
780     : $cf::CFG{"may_$access"})
781     }
782 root 1.70
783     =cut
784    
785 root 1.22 #############################################################################
786 root 1.70
787     =head2 SAFE SCRIPTING
788    
789     Functions that provide a safe environment to compile and execute
790     snippets of perl code without them endangering the safety of the server
791     itself. Looping constructs, I/O operators and other built-in functionality
792     is not available in the safe scripting environment, and the number of
793 root 1.79 functions and methods that can be called is greatly reduced.
794 root 1.70
795     =cut
796 root 1.23
797 root 1.42 our $safe = new Safe "safe";
798 root 1.23 our $safe_hole = new Safe::Hole;
799    
800     $SIG{FPE} = 'IGNORE';
801    
802     $safe->permit_only (Opcode::opset qw(:base_core :base_mem :base_orig :base_math sort time));
803    
804 root 1.25 # here we export the classes and methods available to script code
805    
806 root 1.70 =pod
807    
808     The following fucntions and emthods are available within a safe environment:
809    
810 elmex 1.91 cf::object contr pay_amount pay_player map
811 root 1.70 cf::object::player player
812     cf::player peaceful
813 elmex 1.91 cf::map trigger
814 root 1.70
815     =cut
816    
817 root 1.25 for (
818 elmex 1.91 ["cf::object" => qw(contr pay_amount pay_player map)],
819 root 1.25 ["cf::object::player" => qw(player)],
820     ["cf::player" => qw(peaceful)],
821 elmex 1.91 ["cf::map" => qw(trigger)],
822 root 1.25 ) {
823     no strict 'refs';
824     my ($pkg, @funs) = @$_;
825 root 1.41 *{"safe::$pkg\::$_"} = $safe_hole->wrap (\&{"$pkg\::$_"})
826 root 1.25 for @funs;
827     }
828 root 1.23
829 root 1.70 =over 4
830    
831     =item @retval = safe_eval $code, [var => value, ...]
832    
833     Compiled and executes the given perl code snippet. additional var/value
834     pairs result in temporary local (my) scalar variables of the given name
835     that are available in the code snippet. Example:
836    
837     my $five = safe_eval '$first + $second', first => 1, second => 4;
838    
839     =cut
840    
841 root 1.23 sub safe_eval($;@) {
842     my ($code, %vars) = @_;
843    
844     my $qcode = $code;
845     $qcode =~ s/"/‟/g; # not allowed in #line filenames
846     $qcode =~ s/\n/\\n/g;
847    
848     local $_;
849 root 1.41 local @safe::cf::_safe_eval_args = values %vars;
850 root 1.23
851 root 1.42 my $eval =
852 root 1.23 "do {\n"
853     . "my (" . (join ",", map "\$$_", keys %vars) . ") = \@cf::_safe_eval_args;\n"
854     . "#line 0 \"{$qcode}\"\n"
855     . $code
856     . "\n}"
857 root 1.25 ;
858    
859     sub_generation_inc;
860 root 1.42 my @res = wantarray ? $safe->reval ($eval) : scalar $safe->reval ($eval);
861 root 1.25 sub_generation_inc;
862    
863 root 1.42 if ($@) {
864     warn "$@";
865     warn "while executing safe code '$code'\n";
866     warn "with arguments " . (join " ", %vars) . "\n";
867     }
868    
869 root 1.25 wantarray ? @res : $res[0]
870 root 1.23 }
871    
872 root 1.69 =item cf::register_script_function $function => $cb
873    
874     Register a function that can be called from within map/npc scripts. The
875     function should be reasonably secure and should be put into a package name
876     like the extension.
877    
878     Example: register a function that gets called whenever a map script calls
879     C<rent::overview>, as used by the C<rent> extension.
880    
881     cf::register_script_function "rent::overview" => sub {
882     ...
883     };
884    
885     =cut
886    
887 root 1.23 sub register_script_function {
888     my ($fun, $cb) = @_;
889    
890     no strict 'refs';
891 root 1.41 *{"safe::$fun"} = $safe_hole->wrap ($cb);
892 root 1.23 }
893    
894 root 1.70 =back
895    
896 root 1.71 =cut
897    
898 root 1.23 #############################################################################
899 root 1.65
900     =head2 EXTENSION DATABASE SUPPORT
901    
902     Crossfire maintains a very simple database for extension use. It can
903     currently store anything that can be serialised using Storable, which
904     excludes objects.
905    
906     The parameter C<$family> should best start with the name of the extension
907     using it, it should be unique.
908    
909     =over 4
910    
911     =item $hashref = cf::db_get $family
912    
913     Return a hashref for use by the extension C<$family>, which can be
914     modified. After modifications, you have to call C<cf::db_dirty> or
915     C<cf::db_sync>.
916    
917     =item $value = cf::db_get $family => $key
918    
919     Returns a single value from the database
920    
921     =item cf::db_put $family => $hashref
922    
923     Stores the given family hashref into the database. Updates are delayed, if
924     you want the data to be synced to disk immediately, use C<cf::db_sync>.
925    
926     =item cf::db_put $family => $key => $value
927    
928     Stores the given C<$value> in the family hash. Updates are delayed, if you
929     want the data to be synced to disk immediately, use C<cf::db_sync>.
930    
931     =item cf::db_dirty
932    
933     Marks the database as dirty, to be updated at a later time.
934    
935     =item cf::db_sync
936    
937     Immediately write the database to disk I<if it is dirty>.
938    
939     =cut
940    
941 root 1.78 our $DB;
942    
943 root 1.65 {
944 root 1.66 my $path = cf::localdir . "/database.pst";
945 root 1.65
946     sub db_load() {
947     warn "loading database $path\n";#d# remove later
948 root 1.78 $DB = stat $path ? Storable::retrieve $path : { };
949 root 1.65 }
950    
951     my $pid;
952    
953     sub db_save() {
954     warn "saving database $path\n";#d# remove later
955     waitpid $pid, 0 if $pid;
956 root 1.67 if (0 == ($pid = fork)) {
957 root 1.78 $DB->{_meta}{version} = 1;
958     Storable::nstore $DB, "$path~";
959 root 1.65 rename "$path~", $path;
960     cf::_exit 0 if defined $pid;
961     }
962     }
963    
964     my $dirty;
965    
966     sub db_sync() {
967     db_save if $dirty;
968     undef $dirty;
969     }
970    
971 root 1.87 my $idle = Event->idle (min => $TICK * 2.8, max => 10, repeat => 0, data => WF_AUTOCANCEL, cb => sub {
972 root 1.65 db_sync;
973     });
974    
975     sub db_dirty() {
976     $dirty = 1;
977     $idle->start;
978     }
979    
980     sub db_get($;$) {
981     @_ >= 2
982 root 1.78 ? $DB->{$_[0]}{$_[1]}
983     : ($DB->{$_[0]} ||= { })
984 root 1.65 }
985    
986     sub db_put($$;$) {
987     if (@_ >= 3) {
988 root 1.78 $DB->{$_[0]}{$_[1]} = $_[2];
989 root 1.65 } else {
990 root 1.78 $DB->{$_[0]} = $_[1];
991 root 1.65 }
992     db_dirty;
993     }
994 root 1.67
995 root 1.93 cf::global->attach (
996     prio => 10000,
997 root 1.67 on_cleanup => sub {
998     db_sync;
999     },
1000 root 1.93 );
1001 root 1.65 }
1002    
1003     #############################################################################
1004 root 1.34 # the server's main()
1005    
1006 root 1.73 sub cfg_load {
1007 root 1.72 open my $fh, "<:utf8", cf::confdir . "/config"
1008     or return;
1009    
1010     local $/;
1011     *CFG = YAML::Syck::Load <$fh>;
1012     }
1013    
1014 root 1.39 sub main {
1015 root 1.73 cfg_load;
1016 root 1.65 db_load;
1017 root 1.61 load_extensions;
1018 root 1.34 Event::loop;
1019     }
1020    
1021     #############################################################################
1022 root 1.22 # initialisation
1023    
1024 root 1.65 sub _perl_reload(&) {
1025     my ($msg) = @_;
1026    
1027     $msg->("reloading...");
1028    
1029     eval {
1030     # cancel all watchers
1031 root 1.87 for (Event::all_watchers) {
1032     $_->cancel if $_->data & WF_AUTOCANCEL;
1033     }
1034 root 1.65
1035     # unload all extensions
1036     for (@exts) {
1037     $msg->("unloading <$_>");
1038     unload_extension $_;
1039     }
1040    
1041     # unload all modules loaded from $LIBDIR
1042     while (my ($k, $v) = each %INC) {
1043     next unless $v =~ /^\Q$LIBDIR\E\/.*\.pm$/;
1044    
1045     $msg->("removing <$k>");
1046     delete $INC{$k};
1047    
1048     $k =~ s/\.pm$//;
1049     $k =~ s/\//::/g;
1050    
1051     if (my $cb = $k->can ("unload_module")) {
1052     $cb->();
1053     }
1054    
1055     Symbol::delete_package $k;
1056     }
1057    
1058     # sync database to disk
1059     cf::db_sync;
1060    
1061     # get rid of safe::, as good as possible
1062     Symbol::delete_package "safe::$_"
1063     for qw(cf::object cf::object::player cf::player cf::map cf::party cf::region);
1064    
1065     # remove register_script_function callbacks
1066     # TODO
1067    
1068     # unload cf.pm "a bit"
1069     delete $INC{"cf.pm"};
1070    
1071     # don't, removes xs symbols, too,
1072     # and global variables created in xs
1073     #Symbol::delete_package __PACKAGE__;
1074    
1075     # reload cf.pm
1076     $msg->("reloading cf.pm");
1077     require cf;
1078    
1079 root 1.73 # load config and database again
1080     cf::cfg_load;
1081 root 1.65 cf::db_load;
1082    
1083     # load extensions
1084     $msg->("load extensions");
1085     cf::load_extensions;
1086    
1087     # reattach attachments to objects
1088     $msg->("reattach");
1089     _global_reattach;
1090     };
1091     $msg->($@) if $@;
1092    
1093     $msg->("reloaded");
1094     };
1095    
1096     sub perl_reload() {
1097     _perl_reload {
1098     warn $_[0];
1099     print "$_[0]\n";
1100     };
1101     }
1102    
1103 root 1.85 register "<global>", __PACKAGE__;
1104    
1105     register_command "perl-reload" => sub {
1106 root 1.65 my ($who, $arg) = @_;
1107    
1108     if ($who->flag (FLAG_WIZ)) {
1109     _perl_reload {
1110     warn $_[0];
1111     $who->message ($_[0]);
1112     };
1113     }
1114     };
1115    
1116 root 1.27 unshift @INC, $LIBDIR;
1117 root 1.17
1118 root 1.35 $TICK_WATCHER = Event->timer (
1119 root 1.90 prio => 0,
1120 root 1.77 at => $NEXT_TICK || 1,
1121 root 1.87 data => WF_AUTOCANCEL,
1122 root 1.77 cb => sub {
1123 root 1.35 cf::server_tick; # one server iteration
1124    
1125     my $NOW = Event::time;
1126     $NEXT_TICK += $TICK;
1127    
1128 root 1.78 # if we are delayed by four ticks or more, skip them all
1129 root 1.37 $NEXT_TICK = $NOW if $NOW >= $NEXT_TICK + $TICK * 4;
1130 root 1.35
1131     $TICK_WATCHER->at ($NEXT_TICK);
1132     $TICK_WATCHER->start;
1133     },
1134     );
1135    
1136 root 1.80 IO::AIO::max_poll_time $TICK * 0.2;
1137 root 1.77
1138     Event->io (fd => IO::AIO::poll_fileno,
1139     poll => 'r',
1140     prio => 5,
1141 root 1.87 data => WF_AUTOCANCEL,
1142 root 1.77 cb => \&IO::AIO::poll_cb);
1143    
1144 root 1.1 1
1145