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.56 by root, Wed Aug 30 08:28:33 2006 UTC vs.
Revision 1.71 by root, Sun Oct 1 10:59:30 2006 UTC

11use Event; 11use Event;
12$Event::Eval = 1; # no idea why this is required, but it is 12$Event::Eval = 1; # no idea why this is required, but it is
13 13
14use strict; 14use strict;
15 15
16_reload_1; 16_init_vars;
17 17
18our %COMMAND = (); 18our %COMMAND = ();
19our @EVENT; 19our @EVENT;
20our %PROP_TYPE;
21our %PROP_IDX;
22our $LIBDIR = maps_directory "perl"; 20our $LIBDIR = maps_directory "perl";
23 21
24our $TICK = MAX_TIME * 1e-6; 22our $TICK = MAX_TIME * 1e-6;
25our $TICK_WATCHER; 23our $TICK_WATCHER;
26our $NEXT_TICK; 24our $NEXT_TICK;
25
26our %CFG;
27
28#############################################################################
29
30=head2 GLOBAL VARIABLES
31
32=over 4
33
34=item $cf::LIBDIR
35
36The perl library directory, where extensions and cf-specific modules can
37be found. It will be added to C<@INC> automatically.
38
39=item $cf::TICK
40
41The interval between server ticks, in seconds.
42
43=item %cf::CFG
44
45Configuration for the server, loaded from C</etc/crossfire/config>, or
46from wherever your confdir points to.
47
48=back
49
50=cut
27 51
28BEGIN { 52BEGIN {
29 *CORE::GLOBAL::warn = sub { 53 *CORE::GLOBAL::warn = sub {
30 my $msg = join "", @_; 54 my $msg = join "", @_;
31 $msg .= "\n" 55 $msg .= "\n"
34 print STDERR "cfperl: $msg"; 58 print STDERR "cfperl: $msg";
35 LOG llevError, "cfperl: $msg"; 59 LOG llevError, "cfperl: $msg";
36 }; 60 };
37} 61}
38 62
39my %ignore_set = (MAP_PROP_PATH => 1); # I hate the plug-in api. Deeply!
40
41# generate property mutators
42sub 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 } unless $ignore_set{$prop};
61 }
62}
63
64# auto-generate most of the API
65
66prop_gen OBJECT_PROP => "cf::object";
67# CFAPI_OBJECT_ANIMATION?
68prop_gen PLAYER_PROP => "cf::object::player";
69
70prop_gen MAP_PROP => "cf::map";
71prop_gen ARCH_PROP => "cf::arch";
72
73@safe::cf::object::player::ISA = @cf::object::player::ISA = 'cf::object'; 63@safe::cf::object::player::ISA = @cf::object::player::ISA = 'cf::object';
74 64
75# we bless all objects into (empty) derived classes to force a method lookup 65# we bless all objects into (empty) derived classes to force a method lookup
76# within the Safe compartment. 66# within the Safe compartment.
77for my $pkg (qw(cf::object cf::object::player cf::player cf::map cf::party cf::region cf::arch cf::living)) { 67for my $pkg (qw(cf::object cf::object::player cf::player cf::map cf::party cf::region cf::arch cf::living)) {
87my @exts; 77my @exts;
88my @hook; 78my @hook;
89my %command; 79my %command;
90my %extcmd; 80my %extcmd;
91 81
92############################################################################# 82=head2 UTILITY FUNCTIONS
93# utility functions 83
84=over 4
85
86=cut
94 87
95use JSON::Syck (); # TODO# replace by JSON::PC once working 88use JSON::Syck (); # TODO# replace by JSON::PC once working
89
90=item $ref = cf::from_json $json
91
92Converts a JSON string into the corresponding perl data structure.
93
94=cut
96 95
97sub from_json($) { 96sub from_json($) {
98 $JSON::Syck::ImplicitUnicode = 1; # work around JSON::Syck bugs 97 $JSON::Syck::ImplicitUnicode = 1; # work around JSON::Syck bugs
99 JSON::Syck::Load $_[0] 98 JSON::Syck::Load $_[0]
100} 99}
101 100
101=item $json = cf::to_json $ref
102
103Converts a perl data structure into its JSON representation.
104
105=cut
106
102sub to_json($) { 107sub to_json($) {
103 $JSON::Syck::ImplicitUnicode = 0; # work around JSON::Syck bugs 108 $JSON::Syck::ImplicitUnicode = 0; # work around JSON::Syck bugs
104 JSON::Syck::Dump $_[0] 109 JSON::Syck::Dump $_[0]
105} 110}
106 111
107############################################################################# 112=back
108# "new" plug-in system
109 113
114=cut
115
116#############################################################################
117
110=head3 EVENTS AND OBJECT ATTACHMENTS 118=head2 EVENTS AND OBJECT ATTACHMENTS
111 119
112=over 4 120=over 4
113 121
114=item $object->attach ($attachment, key => $value...) 122=item $object->attach ($attachment, key => $value...)
115 123
389 for (@$callbacks) { 397 for (@$callbacks) {
390 eval { &{$_->[1]} }; 398 eval { &{$_->[1]} };
391 399
392 if ($@) { 400 if ($@) {
393 warn "$@"; 401 warn "$@";
394 warn "... while processing $EVENT[$event][0] event, skipping processing altogether.\n"; 402 warn "... while processing $EVENT[$event][0](@_) event, skipping processing altogether.\n";
395 override; 403 override;
396 } 404 }
397 405
398 return 1 if $override; 406 return 1 if $override;
399 } 407 }
415removed in future versions), and there is no public API to access override 423removed in future versions), and there is no public API to access override
416results (if you must, access C<@cf::invoke_results> directly). 424results (if you must, access C<@cf::invoke_results> directly).
417 425
418=back 426=back
419 427
420=head2 methods valid for all pointers 428=cut
429
430#############################################################################
431
432=head2 METHODS VALID FOR ALL CORE OBJECTS
421 433
422=over 4 434=over 4
423 435
424=item $object->valid 436=item $object->valid, $player->valid, $map->valid
425
426=item $player->valid
427
428=item $map->valid
429 437
430Just because you have a perl object does not mean that the corresponding 438Just because you have a perl object does not mean that the corresponding
431C-level object still exists. If you try to access an object that has no 439C-level object still exists. If you try to access an object that has no
432valid C counterpart anymore you get an exception at runtime. This method 440valid C counterpart anymore you get an exception at runtime. This method
433can be used to test for existence of the C object part without causing an 441can be used to test for existence of the C object part without causing an
476 } 484 }
477 } 485 }
478} 486}
479 487
480sub object_freezer_save { 488sub object_freezer_save {
481 my ($filename, $objs) = @_; 489 my ($filename, $rdata, $objs) = @_;
482 490
483 if (@$objs) { 491 if (length $$rdata) {
492 warn sprintf "saving %s (%d,%d)\n",
493 $filename, length $$rdata, scalar @$objs;
494
484 open my $fh, ">:raw", "$filename.pst~"; 495 if (open my $fh, ">:raw", "$filename~") {
496 chmod SAVE_MODE, $fh;
497 syswrite $fh, $$rdata;
498 close $fh;
499
500 if (@$objs && open my $fh, ">:raw", "$filename.pst~") {
501 chmod SAVE_MODE, $fh;
485 syswrite $fh, Storable::nfreeze { version => 1, objs => $objs }; 502 syswrite $fh, Storable::nfreeze { version => 1, objs => $objs };
486 close $fh; 503 close $fh;
487 chmod SAVE_MODE, "$filename.pst~";
488 rename "$filename.pst~", "$filename.pst"; 504 rename "$filename.pst~", "$filename.pst";
505 } else {
506 unlink "$filename.pst";
507 }
508
509 rename "$filename~", $filename;
510 } else {
511 warn "FATAL: $filename~: $!\n";
512 }
489 } else { 513 } else {
514 unlink $filename;
490 unlink "$filename.pst"; 515 unlink "$filename.pst";
491 } 516 }
492
493 chmod SAVE_MODE, "$filename~";
494 rename "$filename~", $filename;
495} 517}
496 518
497sub object_thawer_load { 519sub object_thawer_load {
498 my ($filename) = @_; 520 my ($filename) = @_;
499 521
522 local $/;
523
524 my $av;
525
526 #TODO: use sysread etc.
527 if (open my $data, "<:raw:perlio", $filename) {
528 $data = <$data>;
500 open my $fh, "<:raw:perlio", "$filename.pst" 529 if (open my $pst, "<:raw:perlio", "$filename.pst") {
501 or return; 530 $av = eval { (Storable::thaw <$pst>)->{objs} };
531 }
532 return ($data, $av);
533 }
502 534
503 eval { local $/; (Storable::thaw <$fh>)->{objs} } 535 ()
504} 536}
505 537
506attach_to_objects 538attach_to_objects
507 prio => -1000000, 539 prio => -1000000,
508 on_clone => sub { 540 on_clone => sub {
654 load_extension $ext; 686 load_extension $ext;
655 1 687 1
656 } or warn "$ext not loaded: $@"; 688 } or warn "$ext not loaded: $@";
657 } 689 }
658} 690}
659
660sub _perl_reload(&) {
661 my ($msg) = @_;
662
663 $msg->("reloading...");
664
665 eval {
666 # 1. cancel all watchers
667 $_->cancel for Event::all_watchers;
668
669 # 2. unload all extensions
670 for (@exts) {
671 $msg->("unloading <$_>");
672 unload_extension $_;
673 }
674
675 # 3. unload all modules loaded from $LIBDIR
676 while (my ($k, $v) = each %INC) {
677 next unless $v =~ /^\Q$LIBDIR\E\/.*\.pm$/;
678
679 $msg->("removing <$k>");
680 delete $INC{$k};
681
682 $k =~ s/\.pm$//;
683 $k =~ s/\//::/g;
684
685 if (my $cb = $k->can ("unload_module")) {
686 $cb->();
687 }
688
689 Symbol::delete_package $k;
690 }
691
692 # 4. get rid of safe::, as good as possible
693 Symbol::delete_package "safe::$_"
694 for qw(cf::object cf::object::player cf::player cf::map cf::party cf::region);
695
696 # 5. remove register_script_function callbacks
697 # TODO
698
699 # 6. unload cf.pm "a bit"
700 delete $INC{"cf.pm"};
701
702 # don't, removes xs symbols, too,
703 # and global variables created in xs
704 #Symbol::delete_package __PACKAGE__;
705
706 # 7. reload cf.pm
707 $msg->("reloading cf.pm");
708 require cf;
709 };
710 $msg->($@) if $@;
711
712 $msg->("reloaded");
713};
714
715sub perl_reload() {
716 _perl_reload {
717 warn $_[0];
718 print "$_[0]\n";
719 };
720}
721
722register_command "perl-reload", 0, sub {
723 my ($who, $arg) = @_;
724
725 if ($who->flag (FLAG_WIZ)) {
726 _perl_reload {
727 warn $_[0];
728 $who->message ($_[0]);
729 };
730 }
731};
732 691
733############################################################################# 692#############################################################################
734# extcmd framework, basically convert ext <msg> 693# extcmd framework, basically convert ext <msg>
735# into pkg::->on_extcmd_arg1 (...) while shortcutting a few 694# into pkg::->on_extcmd_arg1 (...) while shortcutting a few
736 695
764 defined $path or return; 723 defined $path or return;
765 724
766 unlink "$path.pst"; 725 unlink "$path.pst";
767}; 726};
768 727
769# old style persistent data, TODO: remove #d#
770*cf::mapsupport::on_swapin = sub {
771 my ($map) = @_;
772
773 my $path = $map->tmpname;
774 $path = $map->path unless defined $path;
775
776 warn "$path.cfperl\n";#d#
777
778 open my $fh, "<:raw", "$path.cfperl"
779 or return; # no perl data
780
781 my $data = Storable::thaw do { local $/; <$fh> };
782
783 $data->{version} <= 1
784 or return; # too new
785
786 $map->_set_obs ($data->{obs});
787 $map->invoke (EVENT_MAP_UPGRADE);
788};
789
790attach_to_maps prio => -10000, package => cf::mapsupport::; 728attach_to_maps prio => -10000, package => cf::mapsupport::;
791 729
792############################################################################# 730#############################################################################
793# load/save perl data associated with player->ob objects 731# load/save perl data associated with player->ob objects
794 732
795sub all_objects(@) { 733sub all_objects(@) {
796 @_, map all_objects ($_->inv), @_ 734 @_, map all_objects ($_->inv), @_
797} 735}
798 736
737# TODO: compatibility cruft, remove when no longer needed
799attach_to_players 738attach_to_players
800 on_load => sub { 739 on_load => sub {
801 my ($pl, $path) = @_; 740 my ($pl, $path) = @_;
802 741
803 for my $o (all_objects $pl->ob) { 742 for my $o (all_objects $pl->ob) {
809 } 748 }
810 }, 749 },
811; 750;
812 751
813############################################################################# 752#############################################################################
814# core extensions - in perl 753
754=head2 CORE EXTENSIONS
755
756Functions and methods that extend core crossfire objects.
757
758=over 4
815 759
816=item cf::player::exists $login 760=item cf::player::exists $login
817 761
818Returns true when the given account exists. 762Returns true when the given account exists.
819 763
859 $msg{msgid} = $id; 803 $msg{msgid} = $id;
860 804
861 $self->send ("ext " . to_json \%msg); 805 $self->send ("ext " . to_json \%msg);
862} 806}
863 807
808=back
809
810=cut
811
864############################################################################# 812#############################################################################
865# map scripting support 813
814=head2 SAFE SCRIPTING
815
816Functions that provide a safe environment to compile and execute
817snippets of perl code without them endangering the safety of the server
818itself. Looping constructs, I/O operators and other built-in functionality
819is not available in the safe scripting environment, and the number of
820functions and methods that cna be called is greatly reduced.
821
822=cut
866 823
867our $safe = new Safe "safe"; 824our $safe = new Safe "safe";
868our $safe_hole = new Safe::Hole; 825our $safe_hole = new Safe::Hole;
869 826
870$SIG{FPE} = 'IGNORE'; 827$SIG{FPE} = 'IGNORE';
871 828
872$safe->permit_only (Opcode::opset qw(:base_core :base_mem :base_orig :base_math sort time)); 829$safe->permit_only (Opcode::opset qw(:base_core :base_mem :base_orig :base_math sort time));
873 830
874# here we export the classes and methods available to script code 831# here we export the classes and methods available to script code
832
833=pod
834
835The following fucntions and emthods are available within a safe environment:
836
837 cf::object contr pay_amount pay_player
838 cf::object::player player
839 cf::player peaceful
840
841=cut
875 842
876for ( 843for (
877 ["cf::object" => qw(contr pay_amount pay_player)], 844 ["cf::object" => qw(contr pay_amount pay_player)],
878 ["cf::object::player" => qw(player)], 845 ["cf::object::player" => qw(player)],
879 ["cf::player" => qw(peaceful)], 846 ["cf::player" => qw(peaceful)],
882 my ($pkg, @funs) = @$_; 849 my ($pkg, @funs) = @$_;
883 *{"safe::$pkg\::$_"} = $safe_hole->wrap (\&{"$pkg\::$_"}) 850 *{"safe::$pkg\::$_"} = $safe_hole->wrap (\&{"$pkg\::$_"})
884 for @funs; 851 for @funs;
885} 852}
886 853
854=over 4
855
856=item @retval = safe_eval $code, [var => value, ...]
857
858Compiled and executes the given perl code snippet. additional var/value
859pairs result in temporary local (my) scalar variables of the given name
860that are available in the code snippet. Example:
861
862 my $five = safe_eval '$first + $second', first => 1, second => 4;
863
864=cut
865
887sub safe_eval($;@) { 866sub safe_eval($;@) {
888 my ($code, %vars) = @_; 867 my ($code, %vars) = @_;
889 868
890 my $qcode = $code; 869 my $qcode = $code;
891 $qcode =~ s/"/‟/g; # not allowed in #line filenames 870 $qcode =~ s/"/‟/g; # not allowed in #line filenames
913 } 892 }
914 893
915 wantarray ? @res : $res[0] 894 wantarray ? @res : $res[0]
916} 895}
917 896
897=item cf::register_script_function $function => $cb
898
899Register a function that can be called from within map/npc scripts. The
900function should be reasonably secure and should be put into a package name
901like the extension.
902
903Example: register a function that gets called whenever a map script calls
904C<rent::overview>, as used by the C<rent> extension.
905
906 cf::register_script_function "rent::overview" => sub {
907 ...
908 };
909
910=cut
911
918sub register_script_function { 912sub register_script_function {
919 my ($fun, $cb) = @_; 913 my ($fun, $cb) = @_;
920 914
921 no strict 'refs'; 915 no strict 'refs';
922 *{"safe::$fun"} = $safe_hole->wrap ($cb); 916 *{"safe::$fun"} = $safe_hole->wrap ($cb);
923} 917}
924 918
919=back
920
921=cut
922
923#############################################################################
924
925=head2 EXTENSION DATABASE SUPPORT
926
927Crossfire maintains a very simple database for extension use. It can
928currently store anything that can be serialised using Storable, which
929excludes objects.
930
931The parameter C<$family> should best start with the name of the extension
932using it, it should be unique.
933
934=over 4
935
936=item $hashref = cf::db_get $family
937
938Return a hashref for use by the extension C<$family>, which can be
939modified. After modifications, you have to call C<cf::db_dirty> or
940C<cf::db_sync>.
941
942=item $value = cf::db_get $family => $key
943
944Returns a single value from the database
945
946=item cf::db_put $family => $hashref
947
948Stores the given family hashref into the database. Updates are delayed, if
949you want the data to be synced to disk immediately, use C<cf::db_sync>.
950
951=item cf::db_put $family => $key => $value
952
953Stores the given C<$value> in the family hash. Updates are delayed, if you
954want the data to be synced to disk immediately, use C<cf::db_sync>.
955
956=item cf::db_dirty
957
958Marks the database as dirty, to be updated at a later time.
959
960=item cf::db_sync
961
962Immediately write the database to disk I<if it is dirty>.
963
964=cut
965
966{
967 my $db;
968 my $path = cf::localdir . "/database.pst";
969
970 sub db_load() {
971 warn "loading database $path\n";#d# remove later
972 $db = stat $path ? Storable::retrieve $path : { };
973 }
974
975 my $pid;
976
977 sub db_save() {
978 warn "saving database $path\n";#d# remove later
979 waitpid $pid, 0 if $pid;
980 if (0 == ($pid = fork)) {
981 $db->{_meta}{version} = 1;
982 Storable::nstore $db, "$path~";
983 rename "$path~", $path;
984 cf::_exit 0 if defined $pid;
985 }
986 }
987
988 my $dirty;
989
990 sub db_sync() {
991 db_save if $dirty;
992 undef $dirty;
993 }
994
995 my $idle = Event->idle (min => $TICK * 2.8, max => 10, repeat => 0, cb => sub {
996 db_sync;
997 });
998
999 sub db_dirty() {
1000 $dirty = 1;
1001 $idle->start;
1002 }
1003
1004 sub db_get($;$) {
1005 @_ >= 2
1006 ? $db->{$_[0]}{$_[1]}
1007 : ($db->{$_[0]} ||= { })
1008 }
1009
1010 sub db_put($$;$) {
1011 if (@_ >= 3) {
1012 $db->{$_[0]}{$_[1]} = $_[2];
1013 } else {
1014 $db->{$_[0]} = $_[1];
1015 }
1016 db_dirty;
1017 }
1018
1019 attach_global
1020 prio => 10000,
1021 on_cleanup => sub {
1022 db_sync;
1023 },
1024 ;
1025}
1026
925############################################################################# 1027#############################################################################
926# the server's main() 1028# the server's main()
927 1029
928sub main { 1030sub main {
1031 db_load;
1032 load_extensions;
929 Event::loop; 1033 Event::loop;
930} 1034}
931 1035
932############################################################################# 1036#############################################################################
933# initialisation 1037# initialisation
934 1038
1039sub _perl_reload(&) {
1040 my ($msg) = @_;
1041
1042 $msg->("reloading...");
1043
1044 eval {
1045 # cancel all watchers
1046 $_->cancel for Event::all_watchers;
1047
1048 # unload all extensions
1049 for (@exts) {
1050 $msg->("unloading <$_>");
1051 unload_extension $_;
1052 }
1053
1054 # unload all modules loaded from $LIBDIR
1055 while (my ($k, $v) = each %INC) {
1056 next unless $v =~ /^\Q$LIBDIR\E\/.*\.pm$/;
1057
1058 $msg->("removing <$k>");
1059 delete $INC{$k};
1060
1061 $k =~ s/\.pm$//;
1062 $k =~ s/\//::/g;
1063
1064 if (my $cb = $k->can ("unload_module")) {
1065 $cb->();
1066 }
1067
1068 Symbol::delete_package $k;
1069 }
1070
1071 # sync database to disk
1072 cf::db_sync;
1073
1074 # get rid of safe::, as good as possible
1075 Symbol::delete_package "safe::$_"
1076 for qw(cf::object cf::object::player cf::player cf::map cf::party cf::region);
1077
1078 # remove register_script_function callbacks
1079 # TODO
1080
1081 # unload cf.pm "a bit"
1082 delete $INC{"cf.pm"};
1083
1084 # don't, removes xs symbols, too,
1085 # and global variables created in xs
1086 #Symbol::delete_package __PACKAGE__;
1087
1088 # reload cf.pm
1089 $msg->("reloading cf.pm");
1090 require cf;
1091
1092 # load database again
1093 cf::db_load;
1094
1095 # load extensions
1096 $msg->("load extensions");
1097 cf::load_extensions;
1098
1099 # reattach attachments to objects
1100 $msg->("reattach");
1101 _global_reattach;
1102 };
1103 $msg->($@) if $@;
1104
1105 $msg->("reloaded");
1106};
1107
1108sub perl_reload() {
1109 _perl_reload {
1110 warn $_[0];
1111 print "$_[0]\n";
1112 };
1113}
1114
1115register_command "perl-reload", 0, sub {
1116 my ($who, $arg) = @_;
1117
1118 if ($who->flag (FLAG_WIZ)) {
1119 _perl_reload {
1120 warn $_[0];
1121 $who->message ($_[0]);
1122 };
1123 }
1124};
1125
935register "<global>", __PACKAGE__; 1126register "<global>", __PACKAGE__;
936 1127
937unshift @INC, $LIBDIR; 1128unshift @INC, $LIBDIR;
938
939load_extensions;
940 1129
941$TICK_WATCHER = Event->timer ( 1130$TICK_WATCHER = Event->timer (
942 prio => 1, 1131 prio => 1,
943 at => $NEXT_TICK || 1, 1132 at => $NEXT_TICK || 1,
944 cb => sub { 1133 cb => sub {
953 $TICK_WATCHER->at ($NEXT_TICK); 1142 $TICK_WATCHER->at ($NEXT_TICK);
954 $TICK_WATCHER->start; 1143 $TICK_WATCHER->start;
955 }, 1144 },
956); 1145);
957 1146
958_reload_2;
959
9601 11471
961 1148

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines