ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
(Generate patch)

Comparing deliantra/server/lib/cf.pm (file contents):
Revision 1.46 by root, Sun Aug 27 16:15:12 2006 UTC vs.
Revision 1.83 by root, Mon Dec 11 01:30:41 2006 UTC

5use Storable; 5use Storable;
6use Opcode; 6use Opcode;
7use Safe; 7use Safe;
8use Safe::Hole; 8use Safe::Hole;
9 9
10use IO::AIO ();
11use YAML::Syck ();
10use Time::HiRes; 12use Time::HiRes;
11use Event; 13use Event;
12$Event::Eval = 1; # no idea why this is required, but it is 14$Event::Eval = 1; # no idea why this is required, but it is
13 15
16# work around bug in YAML::Syck - bad news for perl6, will it be as broken wrt. unicode?
17$YAML::Syck::ImplicitUnicode = 1;
18
14use strict; 19use strict;
20
21_init_vars;
15 22
16our %COMMAND = (); 23our %COMMAND = ();
17our @EVENT; 24our @EVENT;
18our %PROP_TYPE;
19our %PROP_IDX;
20our $LIBDIR = maps_directory "perl"; 25our $LIBDIR = maps_directory "perl";
21 26
22our $TICK = MAX_TIME * 1e-6; 27our $TICK = MAX_TIME * 1e-6;
23our $TICK_WATCHER; 28our $TICK_WATCHER;
24our $NEXT_TICK; 29our $NEXT_TICK;
30
31our %CFG;
32
33our $uptime;#d#
34our $UPTIME;
35$UPTIME ||= $uptime;#d#
36$UPTIME ||= time;
37
38#############################################################################
39
40=head2 GLOBAL VARIABLES
41
42=over 4
43
44=item $cf::UPTIME
45
46The timestamp of the server start (so not actually an uptime).
47
48=item $cf::LIBDIR
49
50The perl library directory, where extensions and cf-specific modules can
51be found. It will be added to C<@INC> automatically.
52
53=item $cf::TICK
54
55The interval between server ticks, in seconds.
56
57=item %cf::CFG
58
59Configuration for the server, loaded from C</etc/crossfire/config>, or
60from wherever your confdir points to.
61
62=back
63
64=cut
25 65
26BEGIN { 66BEGIN {
27 *CORE::GLOBAL::warn = sub { 67 *CORE::GLOBAL::warn = sub {
28 my $msg = join "", @_; 68 my $msg = join "", @_;
29 $msg .= "\n" 69 $msg .= "\n"
32 print STDERR "cfperl: $msg"; 72 print STDERR "cfperl: $msg";
33 LOG llevError, "cfperl: $msg"; 73 LOG llevError, "cfperl: $msg";
34 }; 74 };
35} 75}
36 76
37my %ignore_set = (MAP_PROP_PATH => 1); # I hate the plug-in api. Deeply!
38
39# generate property mutators
40sub 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 } unless $ignore_set{$prop};
59 }
60}
61
62# auto-generate most of the API
63
64prop_gen OBJECT_PROP => "cf::object";
65# CFAPI_OBJECT_ANIMATION?
66prop_gen PLAYER_PROP => "cf::object::player";
67
68prop_gen MAP_PROP => "cf::map";
69prop_gen ARCH_PROP => "cf::arch";
70
71@safe::cf::object::player::ISA = @cf::object::player::ISA = 'cf::object'; 77@safe::cf::object::player::ISA = @cf::object::player::ISA = 'cf::object';
72 78
73# we bless all objects into (empty) derived classes to force a method lookup 79# we bless all objects into (empty) derived classes to force a method lookup
74# within the Safe compartment. 80# within the Safe compartment.
75for my $pkg (qw(cf::object cf::object::player cf::player cf::map cf::party cf::region cf::arch)) { 81for my $pkg (qw(cf::object cf::object::player cf::player cf::map cf::party cf::region cf::arch cf::living)) {
76 no strict 'refs'; 82 no strict 'refs';
77 @{"safe::$pkg\::wrap::ISA"} = @{"$pkg\::wrap::ISA"} = $pkg; 83 @{"safe::$pkg\::wrap::ISA"} = @{"$pkg\::wrap::ISA"} = $pkg;
78} 84}
79 85
80$Event::DIED = sub { 86$Event::DIED = sub {
85my @exts; 91my @exts;
86my @hook; 92my @hook;
87my %command; 93my %command;
88my %extcmd; 94my %extcmd;
89 95
90############################################################################# 96=head2 UTILITY FUNCTIONS
91# utility functions 97
98=over 4
99
100=cut
92 101
93use JSON::Syck (); # TODO# replace by JSON::PC once working 102use JSON::Syck (); # TODO# replace by JSON::PC once working
103
104=item $ref = cf::from_json $json
105
106Converts a JSON string into the corresponding perl data structure.
107
108=cut
94 109
95sub from_json($) { 110sub from_json($) {
96 $JSON::Syck::ImplicitUnicode = 1; # work around JSON::Syck bugs 111 $JSON::Syck::ImplicitUnicode = 1; # work around JSON::Syck bugs
97 JSON::Syck::Load $_[0] 112 JSON::Syck::Load $_[0]
98} 113}
99 114
115=item $json = cf::to_json $ref
116
117Converts a perl data structure into its JSON representation.
118
119=cut
120
100sub to_json($) { 121sub to_json($) {
101 $JSON::Syck::ImplicitUnicode = 0; # work around JSON::Syck bugs 122 $JSON::Syck::ImplicitUnicode = 0; # work around JSON::Syck bugs
102 JSON::Syck::Dump $_[0] 123 JSON::Syck::Dump $_[0]
103} 124}
104 125
126=back
127
128=cut
129
105############################################################################# 130#############################################################################
106# "new" plug-in system
107 131
132=head2 EVENTS AND OBJECT ATTACHMENTS
133
134=over 4
135
136=item $object->attach ($attachment, key => $value...)
137
108=item $object->attach ($attachment, ...) 138=item $object->detach ($attachment)
109 139
110Attach a pre-registered attachment to an object. 140Attach/detach a pre-registered attachment to an object.
111 141
142=item $player->attach ($attachment, key => $value...)
143
112=item $player->attach ($attachment, ...) 144=item $player->detach ($attachment)
113 145
114Attach a pre-registered attachment to a player. 146Attach/detach a pre-registered attachment to a player.
115 147
116=item $map->attach ($attachment, ...) # not yet persistent 148=item $map->attach ($attachment, key => $value...)
117 149
150=item $map->detach ($attachment)
151
118Attach a pre-registered attachment to a map. 152Attach/detach a pre-registered attachment to a map.
153
154=item $bool = $object->attached ($name)
155
156=item $bool = $player->attached ($name)
157
158=item $bool = $map->attached ($name)
159
160Checks wether the named attachment is currently attached to the object.
119 161
120=item cf::attach_global ... 162=item cf::attach_global ...
121 163
122Attach handlers for global events. 164Attach handlers for global events.
123 165
150package and register them. Only handlers for eevents supported by the 192package and register them. Only handlers for eevents supported by the
151object/class are recognised. 193object/class are recognised.
152 194
153=back 195=back
154 196
155=item cf::attach_to_type $object_type, ... 197=item cf::attach_to_type $object_type, $subtype, ...
156 198
157Attach handlers for a specific object type (e.g. TRANSPORT). 199Attach handlers for a specific object type (e.g. TRANSPORT) and
200subtype. If C<$subtype> is zero or undef, matches all objects of the given
201type.
158 202
159=item cf::attach_to_objects ... 203=item cf::attach_to_objects ...
160 204
161Attach handlers to all objects. Do not use this except for debugging or 205Attach handlers to all objects. Do not use this except for debugging or
162very rare events, as handlers are (obviously) called for I<all> objects in 206very rare events, as handlers are (obviously) called for I<all> objects in
169=item cf::attach_to_maps ... 213=item cf::attach_to_maps ...
170 214
171Attach handlers to all maps. 215Attach handlers to all maps.
172 216
173=item cf:register_attachment $name, ... 217=item cf:register_attachment $name, ...
218
219Register an attachment by name through which objects can refer to this
220attachment.
221
222=item cf:register_player_attachment $name, ...
223
224Register an attachment by name through which players can refer to this
225attachment.
226
227=item cf:register_map_attachment $name, ...
228
229Register an attachment by name through which maps can refer to this
230attachment.
174 231
175=cut 232=cut
176 233
177# the following variables are defined in .xs and must not be re-created 234# the following variables are defined in .xs and must not be re-created
178our @CB_GLOBAL = (); # registry for all global events 235our @CB_GLOBAL = (); # registry for all global events
241 298
242 \%undo 299 \%undo
243} 300}
244 301
245sub _attach_attachment { 302sub _attach_attachment {
246 my ($klass, $obj, $name, @args) = q_; 303 my ($obj, $name, %arg) = @_;
304
305 return if exists $obj->{_attachment}{$name};
247 306
248 my $res; 307 my $res;
249 308
250 if (my $attach = $attachment{$name}) { 309 if (my $attach = $attachment{$name}) {
251 my $registry = $obj->registry; 310 my $registry = $obj->registry;
252 311
312 for (@$attach) {
313 my ($klass, @attach) = @$_;
253 $res = _attach @$registry, $klass, @$attach; 314 $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 } 315 }
316
317 $obj->{$name} = \%arg;
264 } else { 318 } else {
265 warn "object uses attachment '$name' that is not available, postponing.\n"; 319 warn "object uses attachment '$name' that is not available, postponing.\n";
266 } 320 }
267 321
268 push @{$obj->{_attachment}}, $name; 322 $obj->{_attachment}{$name} = undef;
269 323
270 $res->{attachment} = $name; 324 $res->{attachment} = $name;
271 $res 325 $res
272} 326}
273 327
274sub cf::object::attach { 328*cf::object::attach =
329*cf::player::attach =
330*cf::map::attach = sub {
275 my ($obj, $name, @args) = @_; 331 my ($obj, $name, %arg) = @_;
276 332
277 _attach_attachment KLASS_OBJECT, $obj, $name, @args; 333 _attach_attachment $obj, $name, %arg;
278} 334};
279 335
336# all those should be optimised
337*cf::object::detach =
280sub cf::player::attach { 338*cf::player::detach =
339*cf::map::detach = sub {
281 my ($obj, $name, @args) = @_; 340 my ($obj, $name) = @_;
282 341
283 _attach_attachment KLASS_PLAYER, $obj, $name, @args; 342 delete $obj->{_attachment}{$name};
284} 343 reattach ($obj);
344};
285 345
286sub cf::map::attach { 346*cf::object::attached =
347*cf::player::attached =
348*cf::map::attached = sub {
287 my ($obj, $name, @args) = @_; 349 my ($obj, $name) = @_;
288 350
289 _attach_attachment KLASS_MAP, $obj, $name, @args; 351 exists $obj->{_attachment}{$name}
290} 352};
291 353
292sub attach_global { 354sub attach_global {
293 _attach @CB_GLOBAL, KLASS_GLOBAL, @_ 355 _attach @CB_GLOBAL, KLASS_GLOBAL, @_
294} 356}
295 357
296sub attach_to_type { 358sub attach_to_type {
297 my $type = shift; 359 my $type = shift;
360 my $subtype = shift;
298 361
299 _attach @{$CB_TYPE[$type]}, KLASS_OBJECT, @_ 362 _attach @{$CB_TYPE[$type + $subtype * NUM_SUBTYPES]}, KLASS_OBJECT, @_
300} 363}
301 364
302sub attach_to_objects { 365sub attach_to_objects {
303 _attach @CB_OBJECT, KLASS_OBJECT, @_ 366 _attach @CB_OBJECT, KLASS_OBJECT, @_
304} 367}
312} 375}
313 376
314sub register_attachment { 377sub register_attachment {
315 my $name = shift; 378 my $name = shift;
316 379
380 $attachment{$name} = [[KLASS_OBJECT, @_]];
381}
382
383sub register_player_attachment {
384 my $name = shift;
385
386 $attachment{$name} = [[KLASS_PLAYER, @_]];
387}
388
389sub register_map_attachment {
390 my $name = shift;
391
317 $attachment{$name} = [@_]; 392 $attachment{$name} = [[KLASS_MAP, @_]];
318} 393}
319 394
320our $override; 395our $override;
321our @invoke_results = (); # referenced from .xs code. TODO: play tricks with reify and mortals? 396our @invoke_results = (); # referenced from .xs code. TODO: play tricks with reify and mortals?
322 397
336 for (@$callbacks) { 411 for (@$callbacks) {
337 eval { &{$_->[1]} }; 412 eval { &{$_->[1]} };
338 413
339 if ($@) { 414 if ($@) {
340 warn "$@"; 415 warn "$@";
341 warn "... while processing $EVENT[$event][0] event, skipping processing altogether.\n"; 416 warn "... while processing $EVENT[$event][0](@_) event, skipping processing altogether.\n";
342 override; 417 override;
343 } 418 }
344 419
345 return 1 if $override; 420 return 1 if $override;
346 } 421 }
347 422
348 0 423 0
349} 424}
350 425
426=item $bool = cf::invoke EVENT_GLOBAL_XXX, ...
427
428=item $bool = $object->invoke (EVENT_OBJECT_XXX, ...)
429
430=item $bool = $player->invoke (EVENT_PLAYER_XXX, ...)
431
432=item $bool = $map->invoke (EVENT_MAP_XXX, ...)
433
434Generate a global/object/player/map-specific event with the given arguments.
435
436This API is preliminary (most likely, the EVENT_KLASS_xxx prefix will be
437removed in future versions), and there is no public API to access override
438results (if you must, access C<@cf::invoke_results> directly).
439
440=back
441
442=cut
443
444#############################################################################
445
446=head2 METHODS VALID FOR ALL CORE OBJECTS
447
448=over 4
449
450=item $object->valid, $player->valid, $map->valid
451
452Just because you have a perl object does not mean that the corresponding
453C-level object still exists. If you try to access an object that has no
454valid C counterpart anymore you get an exception at runtime. This method
455can be used to test for existence of the C object part without causing an
456exception.
457
458=back
459
460=cut
461
462*cf::object::valid =
463*cf::player::valid =
464*cf::map::valid = \&cf::_valid;
465
351############################################################################# 466#############################################################################
352# object support 467# object support
353 468
354sub instantiate { 469sub instantiate {
355 my ($obj, $data) = @_; 470 my ($obj, $data) = @_;
356 471
357 $data = from_json $data; 472 $data = from_json $data;
358 473
359 for (@$data) { 474 for (@$data) {
360 my ($name, $args) = @$_; 475 my ($name, $args) = @$_;
361 attach $obj, $name, @{$args || [] }; 476
477 $obj->attach ($name, %{$args || {} });
362 } 478 }
363} 479}
364 480
365# basically do the same as instantiate, without calling instantiate 481# basically do the same as instantiate, without calling instantiate
366sub reattach { 482sub reattach {
367 warn "reattach<@_>\n";#d#
368 my ($obj) = @_; 483 my ($obj) = @_;
369 my $registry = $obj->registry; 484 my $registry = $obj->registry;
370 485
486 @$registry = ();
487
488 delete $obj->{_attachment} unless scalar keys %{ $obj->{_attachment} || {} };
489
371 for my $name (@{ $obj->{_attachment} }) { 490 for my $name (keys %{ $obj->{_attachment} || {} }) {
372 if (my $attach = $attachment{$name}) { 491 if (my $attach = $attachment{$name}) {
492 for (@$attach) {
493 my ($klass, @attach) = @$_;
373 _attach @$registry, KLASS_OBJECT, @$attach; 494 _attach @$registry, $klass, @attach;
495 }
374 } else { 496 } else {
375 warn "object uses attachment '$name' that is not available, postponing.\n"; 497 warn "object uses attachment '$name' that is not available, postponing.\n";
376 } 498 }
377 } 499 }
378
379 warn "reattach<@_, $_>\n";
380} 500}
381 501
382sub object_freezer_save { 502sub object_freezer_save {
383 my ($filename, $objs) = @_; 503 my ($filename, $rdata, $objs) = @_;
384 warn "freeze $filename\n";#d#
385 use Data::Dumper; print Dumper $objs;
386 504
387 $filename .= ".pst"; 505 if (length $$rdata) {
506 warn sprintf "saving %s (%d,%d)\n",
507 $filename, length $$rdata, scalar @$objs;
388 508
389 if (@$objs) {
390 open my $fh, ">:raw", "$filename~"; 509 if (open my $fh, ">:raw", "$filename~") {
391 chmod $fh, SAVE_MODE; 510 chmod SAVE_MODE, $fh;
511 syswrite $fh, $$rdata;
512 close $fh;
513
514 if (@$objs && open my $fh, ">:raw", "$filename.pst~") {
515 chmod SAVE_MODE, $fh;
392 syswrite $fh, Storable::nfreeze { version => 1, objs => $objs }; 516 syswrite $fh, Storable::nfreeze { version => 1, objs => $objs };
393 close $fh; 517 close $fh;
518 rename "$filename.pst~", "$filename.pst";
519 } else {
520 unlink "$filename.pst";
521 }
522
394 rename "$filename~", $filename; 523 rename "$filename~", $filename;
524 } else {
525 warn "FATAL: $filename~: $!\n";
526 }
395 } else { 527 } else {
396 unlink $filename; 528 unlink $filename;
529 unlink "$filename.pst";
397 } 530 }
531}
532
533sub object_freezer_as_string {
534 my ($rdata, $objs) = @_;
535
536 use Data::Dumper;
537
538 $$rdata . Dumper $objs
398} 539}
399 540
400sub object_thawer_load { 541sub object_thawer_load {
401 my ($filename) = @_; 542 my ($filename) = @_;
402 543
403 warn "thaw $filename\n";#d# 544 local $/;
404 545
546 my $av;
547
548 #TODO: use sysread etc.
549 if (open my $data, "<:raw:perlio", $filename) {
550 $data = <$data>;
405 open my $fh, "<:raw:perlio", "$filename.pst" 551 if (open my $pst, "<:raw:perlio", "$filename.pst") {
406 or return; 552 $av = eval { (Storable::thaw <$pst>)->{objs} };
553 }
554 return ($data, $av);
555 }
407 556
408 eval { local $/; (Storable::thaw <$fh>)->{objs} } 557 ()
409} 558}
410 559
411attach_to_objects 560attach_to_objects
412 prio => -1000000, 561 prio => -1000000,
413 on_clone => sub { 562 on_clone => sub {
414 my ($src, $dst) = @_; 563 my ($src, $dst) = @_;
415 564
416 @{$dst->registry} = @{$src->registry}; 565 @{$dst->registry} = @{$src->registry};
417 warn "registry clone ", join ":", @{$src->registry};#d#
418 566
419 %$dst = %$src; 567 %$dst = %$src;
420 568
421 $dst->{_attachment} = [@{ $src->{_attachment} }] 569 %{$dst->{_attachment}} = %{$src->{_attachment}}
422 if exists $src->{_attachment}; 570 if exists $src->{_attachment};
423
424 warn "clone<@_>\n";#d#
425 }, 571 },
426; 572;
427 573
428############################################################################# 574#############################################################################
429# old plug-in events 575# old plug-in events
501 . "#line 1 \"$path\"\n{\n" 647 . "#line 1 \"$path\"\n{\n"
502 . (do { local $/; <$fh> }) 648 . (do { local $/; <$fh> })
503 . "\n};\n1"; 649 . "\n};\n1";
504 650
505 eval $source 651 eval $source
506 or die "$path: $@"; 652 or die $@ ? "$path: $@\n"
653 : "extension disabled.\n";
507 654
508 push @exts, $pkg; 655 push @exts, $pkg;
509 $ext_pkg{$base} = $pkg; 656 $ext_pkg{$base} = $pkg;
510 657
511# no strict 'refs'; 658# no strict 'refs';
562 load_extension $ext; 709 load_extension $ext;
563 1 710 1
564 } or warn "$ext not loaded: $@"; 711 } or warn "$ext not loaded: $@";
565 } 712 }
566} 713}
567
568sub _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
590 $k =~ s/\.pm$//;
591 $k =~ s/\//::/g;
592
593 if (my $cb = $k->can ("unload_module")) {
594 $cb->();
595 }
596
597 Symbol::delete_package $k;
598 }
599
600 # 4. get rid of safe::, as good as possible
601 Symbol::delete_package "safe::$_"
602 for qw(cf::object cf::object::player cf::player cf::map cf::party cf::region);
603
604 # 5. remove register_script_function callbacks
605 # TODO
606
607 # 6. unload cf.pm "a bit"
608 delete $INC{"cf.pm"};
609
610 # don't, removes xs symbols, too,
611 # and global variables created in xs
612 #Symbol::delete_package __PACKAGE__;
613
614 # 7. reload cf.pm
615 $msg->("reloading cf.pm");
616 require cf;
617 };
618 $msg->($@) if $@;
619
620 $msg->("reloaded");
621};
622
623sub perl_reload() {
624 _perl_reload {
625 warn $_[0];
626 print "$_[0]\n";
627 };
628}
629
630register_command "perl-reload", 0, sub {
631 my ($who, $arg) = @_;
632
633 if ($who->flag (FLAG_WIZ)) {
634 _perl_reload {
635 warn $_[0];
636 $who->message ($_[0]);
637 };
638 }
639};
640 714
641############################################################################# 715#############################################################################
642# extcmd framework, basically convert ext <msg> 716# extcmd framework, basically convert ext <msg>
643# into pkg::->on_extcmd_arg1 (...) while shortcutting a few 717# into pkg::->on_extcmd_arg1 (...) while shortcutting a few
644 718
669 my ($map) = @_; 743 my ($map) = @_;
670 744
671 my $path = $map->tmpname; 745 my $path = $map->tmpname;
672 defined $path or return; 746 defined $path or return;
673 747
674 unlink "$path.cfperl";
675 unlink "$path.pst"; 748 unlink "$path.pst";
676}; 749};
677 750
678*cf::mapsupport::on_swapin =
679*cf::mapsupport::on_load = sub {
680 my ($map) = @_;
681
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
696attach_to_maps prio => -10000, package => cf::mapsupport::; 751attach_to_maps prio => -10000, package => cf::mapsupport::;
697 752
698############################################################################# 753#############################################################################
699# load/save perl data associated with player->ob objects 754# load/save perl data associated with player->ob objects
700 755
701sub all_objects(@) { 756sub all_objects(@) {
702 @_, map all_objects ($_->inv), @_ 757 @_, map all_objects ($_->inv), @_
703} 758}
704 759
760# TODO: compatibility cruft, remove when no longer needed
705attach_to_players 761attach_to_players
706 on_load => sub { 762 on_load => sub {
707 my ($pl, $path) = @_; 763 my ($pl, $path) = @_;
708 764
709 for my $o (all_objects $pl->ob) { 765 for my $o (all_objects $pl->ob) {
715 } 771 }
716 }, 772 },
717; 773;
718 774
719############################################################################# 775#############################################################################
720# core extensions - in perl 776
777=head2 CORE EXTENSIONS
778
779Functions and methods that extend core crossfire objects.
780
781=over 4
721 782
722=item cf::player::exists $login 783=item cf::player::exists $login
723 784
724Returns true when the given account exists. 785Returns true when the given account exists.
725 786
728sub cf::player::exists($) { 789sub cf::player::exists($) {
729 cf::player::find $_[0] 790 cf::player::find $_[0]
730 or -f sprintf "%s/%s/%s/%s.pl", cf::localdir, cf::playerdir, ($_[0]) x 2; 791 or -f sprintf "%s/%s/%s/%s.pl", cf::localdir, cf::playerdir, ($_[0]) x 2;
731} 792}
732 793
733=item $player->reply ($npc, $msg[, $flags]) 794=item $player_object->reply ($npc, $msg[, $flags])
734 795
735Sends a message to the player, as if the npc C<$npc> replied. C<$npc> 796Sends a message to the player, as if the npc C<$npc> replied. C<$npc>
736can be C<undef>. Does the right thing when the player is currently in a 797can be C<undef>. Does the right thing when the player is currently in a
737dialogue with the given NPC character. 798dialogue with the given NPC character.
738 799
765 $msg{msgid} = $id; 826 $msg{msgid} = $id;
766 827
767 $self->send ("ext " . to_json \%msg); 828 $self->send ("ext " . to_json \%msg);
768} 829}
769 830
831=item $player_object->may ("access")
832
833Returns wether the given player is authorized to access resource "access"
834(e.g. "command_wizcast").
835
836=cut
837
838sub cf::object::player::may {
839 my ($self, $access) = @_;
840
841 $self->flag (cf::FLAG_WIZ) ||
842 (ref $cf::CFG{"may_$access"}
843 ? scalar grep $self->name eq $_, @{$cf::CFG{"may_$access"}}
844 : $cf::CFG{"may_$access"})
845}
846
847=cut
848
770############################################################################# 849#############################################################################
771# map scripting support 850
851=head2 SAFE SCRIPTING
852
853Functions that provide a safe environment to compile and execute
854snippets of perl code without them endangering the safety of the server
855itself. Looping constructs, I/O operators and other built-in functionality
856is not available in the safe scripting environment, and the number of
857functions and methods that can be called is greatly reduced.
858
859=cut
772 860
773our $safe = new Safe "safe"; 861our $safe = new Safe "safe";
774our $safe_hole = new Safe::Hole; 862our $safe_hole = new Safe::Hole;
775 863
776$SIG{FPE} = 'IGNORE'; 864$SIG{FPE} = 'IGNORE';
777 865
778$safe->permit_only (Opcode::opset qw(:base_core :base_mem :base_orig :base_math sort time)); 866$safe->permit_only (Opcode::opset qw(:base_core :base_mem :base_orig :base_math sort time));
779 867
780# here we export the classes and methods available to script code 868# here we export the classes and methods available to script code
869
870=pod
871
872The following fucntions and emthods are available within a safe environment:
873
874 cf::object contr pay_amount pay_player
875 cf::object::player player
876 cf::player peaceful
877
878=cut
781 879
782for ( 880for (
783 ["cf::object" => qw(contr pay_amount pay_player)], 881 ["cf::object" => qw(contr pay_amount pay_player)],
784 ["cf::object::player" => qw(player)], 882 ["cf::object::player" => qw(player)],
785 ["cf::player" => qw(peaceful)], 883 ["cf::player" => qw(peaceful)],
788 my ($pkg, @funs) = @$_; 886 my ($pkg, @funs) = @$_;
789 *{"safe::$pkg\::$_"} = $safe_hole->wrap (\&{"$pkg\::$_"}) 887 *{"safe::$pkg\::$_"} = $safe_hole->wrap (\&{"$pkg\::$_"})
790 for @funs; 888 for @funs;
791} 889}
792 890
891=over 4
892
893=item @retval = safe_eval $code, [var => value, ...]
894
895Compiled and executes the given perl code snippet. additional var/value
896pairs result in temporary local (my) scalar variables of the given name
897that are available in the code snippet. Example:
898
899 my $five = safe_eval '$first + $second', first => 1, second => 4;
900
901=cut
902
793sub safe_eval($;@) { 903sub safe_eval($;@) {
794 my ($code, %vars) = @_; 904 my ($code, %vars) = @_;
795 905
796 my $qcode = $code; 906 my $qcode = $code;
797 $qcode =~ s/"/‟/g; # not allowed in #line filenames 907 $qcode =~ s/"/‟/g; # not allowed in #line filenames
819 } 929 }
820 930
821 wantarray ? @res : $res[0] 931 wantarray ? @res : $res[0]
822} 932}
823 933
934=item cf::register_script_function $function => $cb
935
936Register a function that can be called from within map/npc scripts. The
937function should be reasonably secure and should be put into a package name
938like the extension.
939
940Example: register a function that gets called whenever a map script calls
941C<rent::overview>, as used by the C<rent> extension.
942
943 cf::register_script_function "rent::overview" => sub {
944 ...
945 };
946
947=cut
948
824sub register_script_function { 949sub register_script_function {
825 my ($fun, $cb) = @_; 950 my ($fun, $cb) = @_;
826 951
827 no strict 'refs'; 952 no strict 'refs';
828 *{"safe::$fun"} = $safe_hole->wrap ($cb); 953 *{"safe::$fun"} = $safe_hole->wrap ($cb);
829} 954}
830 955
956=back
957
958=cut
959
960#############################################################################
961
962=head2 EXTENSION DATABASE SUPPORT
963
964Crossfire maintains a very simple database for extension use. It can
965currently store anything that can be serialised using Storable, which
966excludes objects.
967
968The parameter C<$family> should best start with the name of the extension
969using it, it should be unique.
970
971=over 4
972
973=item $hashref = cf::db_get $family
974
975Return a hashref for use by the extension C<$family>, which can be
976modified. After modifications, you have to call C<cf::db_dirty> or
977C<cf::db_sync>.
978
979=item $value = cf::db_get $family => $key
980
981Returns a single value from the database
982
983=item cf::db_put $family => $hashref
984
985Stores the given family hashref into the database. Updates are delayed, if
986you want the data to be synced to disk immediately, use C<cf::db_sync>.
987
988=item cf::db_put $family => $key => $value
989
990Stores the given C<$value> in the family hash. Updates are delayed, if you
991want the data to be synced to disk immediately, use C<cf::db_sync>.
992
993=item cf::db_dirty
994
995Marks the database as dirty, to be updated at a later time.
996
997=item cf::db_sync
998
999Immediately write the database to disk I<if it is dirty>.
1000
1001=cut
1002
1003our $DB;
1004
1005{
1006 my $path = cf::localdir . "/database.pst";
1007
1008 sub db_load() {
1009 warn "loading database $path\n";#d# remove later
1010 $DB = stat $path ? Storable::retrieve $path : { };
1011 }
1012
1013 my $pid;
1014
1015 sub db_save() {
1016 warn "saving database $path\n";#d# remove later
1017 waitpid $pid, 0 if $pid;
1018 if (0 == ($pid = fork)) {
1019 $DB->{_meta}{version} = 1;
1020 Storable::nstore $DB, "$path~";
1021 rename "$path~", $path;
1022 cf::_exit 0 if defined $pid;
1023 }
1024 }
1025
1026 my $dirty;
1027
1028 sub db_sync() {
1029 db_save if $dirty;
1030 undef $dirty;
1031 }
1032
1033 my $idle = Event->idle (min => $TICK * 2.8, max => 10, repeat => 0, cb => sub {
1034 db_sync;
1035 });
1036
1037 sub db_dirty() {
1038 $dirty = 1;
1039 $idle->start;
1040 }
1041
1042 sub db_get($;$) {
1043 @_ >= 2
1044 ? $DB->{$_[0]}{$_[1]}
1045 : ($DB->{$_[0]} ||= { })
1046 }
1047
1048 sub db_put($$;$) {
1049 if (@_ >= 3) {
1050 $DB->{$_[0]}{$_[1]} = $_[2];
1051 } else {
1052 $DB->{$_[0]} = $_[1];
1053 }
1054 db_dirty;
1055 }
1056
1057 attach_global
1058 prio => 10000,
1059 on_cleanup => sub {
1060 db_sync;
1061 },
1062 ;
1063}
1064
831############################################################################# 1065#############################################################################
832# the server's main() 1066# the server's main()
833 1067
1068sub cfg_load {
1069 open my $fh, "<:utf8", cf::confdir . "/config"
1070 or return;
1071
1072 local $/;
1073 *CFG = YAML::Syck::Load <$fh>;
1074}
1075
834sub main { 1076sub main {
1077 cfg_load;
1078 db_load;
1079 load_extensions;
835 Event::loop; 1080 Event::loop;
836} 1081}
837 1082
838############################################################################# 1083#############################################################################
839# initialisation 1084# initialisation
840 1085
1086sub _perl_reload(&) {
1087 my ($msg) = @_;
1088
1089 $msg->("reloading...");
1090
1091 eval {
1092 # cancel all watchers
1093 $_->cancel for Event::all_watchers;
1094
1095 # unload all extensions
1096 for (@exts) {
1097 $msg->("unloading <$_>");
1098 unload_extension $_;
1099 }
1100
1101 # unload all modules loaded from $LIBDIR
1102 while (my ($k, $v) = each %INC) {
1103 next unless $v =~ /^\Q$LIBDIR\E\/.*\.pm$/;
1104
1105 $msg->("removing <$k>");
1106 delete $INC{$k};
1107
1108 $k =~ s/\.pm$//;
1109 $k =~ s/\//::/g;
1110
1111 if (my $cb = $k->can ("unload_module")) {
1112 $cb->();
1113 }
1114
1115 Symbol::delete_package $k;
1116 }
1117
1118 # sync database to disk
1119 cf::db_sync;
1120
1121 # get rid of safe::, as good as possible
1122 Symbol::delete_package "safe::$_"
1123 for qw(cf::object cf::object::player cf::player cf::map cf::party cf::region);
1124
1125 # remove register_script_function callbacks
1126 # TODO
1127
1128 # unload cf.pm "a bit"
1129 delete $INC{"cf.pm"};
1130
1131 # don't, removes xs symbols, too,
1132 # and global variables created in xs
1133 #Symbol::delete_package __PACKAGE__;
1134
1135 # reload cf.pm
1136 $msg->("reloading cf.pm");
1137 require cf;
1138
1139 # load config and database again
1140 cf::cfg_load;
1141 cf::db_load;
1142
1143 # load extensions
1144 $msg->("load extensions");
1145 cf::load_extensions;
1146
1147 # reattach attachments to objects
1148 $msg->("reattach");
1149 _global_reattach;
1150 };
1151 $msg->($@) if $@;
1152
1153 $msg->("reloaded");
1154};
1155
1156sub perl_reload() {
1157 _perl_reload {
1158 warn $_[0];
1159 print "$_[0]\n";
1160 };
1161}
1162
1163register_command "perl-reload", 0, sub {
1164 my ($who, $arg) = @_;
1165
1166 if ($who->flag (FLAG_WIZ)) {
1167 _perl_reload {
1168 warn $_[0];
1169 $who->message ($_[0]);
1170 };
1171 }
1172};
1173
841register "<global>", __PACKAGE__; 1174register "<global>", __PACKAGE__;
842 1175
843unshift @INC, $LIBDIR; 1176unshift @INC, $LIBDIR;
844 1177
845load_extensions;
846
847$TICK_WATCHER = Event->timer ( 1178$TICK_WATCHER = Event->timer (
848 prio => 1, 1179 prio => 1,
1180 async => 1,
849 at => $NEXT_TICK || 1, 1181 at => $NEXT_TICK || 1,
850 cb => sub { 1182 cb => sub {
851 cf::server_tick; # one server iteration 1183 cf::server_tick; # one server iteration
852 1184
853 my $NOW = Event::time; 1185 my $NOW = Event::time;
854 $NEXT_TICK += $TICK; 1186 $NEXT_TICK += $TICK;
855 1187
856 # if we are delayed by four ticks, skip them all 1188 # if we are delayed by four ticks or more, skip them all
857 $NEXT_TICK = $NOW if $NOW >= $NEXT_TICK + $TICK * 4; 1189 $NEXT_TICK = $NOW if $NOW >= $NEXT_TICK + $TICK * 4;
858 1190
859 $TICK_WATCHER->at ($NEXT_TICK); 1191 $TICK_WATCHER->at ($NEXT_TICK);
860 $TICK_WATCHER->start; 1192 $TICK_WATCHER->start;
861 }, 1193 },
862); 1194);
863 1195
1196IO::AIO::max_poll_time $TICK * 0.2;
1197
1198Event->io (fd => IO::AIO::poll_fileno,
1199 poll => 'r',
1200 prio => 5,
1201 cb => \&IO::AIO::poll_cb);
1202
8641 12031
865 1204

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines