ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.7
Committed: Tue Mar 7 13:44:43 2006 UTC (18 years, 2 months ago) by root
Branch: MAIN
Changes since 1.6: +11 -0 lines
Log Message:
*** empty log message ***

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.1
7     use strict;
8    
9     our %COMMAND;
10     our @EVENT;
11     our %PROP_TYPE;
12     our %PROP_IDX;
13    
14     BEGIN {
15     @EVENT = map lc, @EVENT;
16    
17     *CORE::GLOBAL::warn = sub {
18     my $msg = join "", @_;
19     $msg .= "\n"
20     unless $msg =~ /\n$/;
21    
22     print STDERR "cfperl: $msg";
23     LOG llevError, "cfperl: $msg";
24     };
25     }
26    
27     # generate property mutators
28     sub prop_gen {
29     my ($prefix, $class) = @_;
30    
31     no strict 'refs';
32    
33     for my $prop (keys %PROP_TYPE) {
34     $prop =~ /^\Q$prefix\E_(.*$)/ or next;
35     my $sub = lc $1;
36    
37     my $type = $PROP_TYPE{$prop};
38     my $idx = $PROP_IDX {$prop};
39    
40     *{"$class\::get_$sub"} = *{"$class\::$sub"} = sub {
41     $_[0]->get_property ($type, $idx)
42     };
43    
44     *{"$class\::set_$sub"} = sub {
45     $_[0]->set_property ($type, $idx, $_[1]);
46     };
47     }
48     }
49    
50     # auto-generate most of the API
51    
52     prop_gen OBJECT_PROP => "cf::object";
53     # CFAPI_OBJECT_ANIMATION?
54     prop_gen PLAYER_PROP => "cf::object::player";
55    
56     prop_gen MAP_PROP => "cf::map";
57     prop_gen ARCH_PROP => "cf::arch";
58    
59     # guessed hierarchies
60    
61     @cf::object::player::ISA = 'cf::object';
62     @cf::object::map::ISA = 'cf::object';
63    
64 root 1.5 my %ext_pkg;
65 root 1.1 my @exts;
66     my @hook;
67     my %command;
68    
69     sub inject_event {
70     my ($data) = @_;
71    
72 root 1.5 my $cb = $hook[$data->{event_code}]{$data->{extension}}
73     or return;
74    
75     $cb->($data)
76     }
77    
78     sub inject_global_event {
79     my ($data) = @_;
80    
81 root 1.1 my $cb = $hook[$data->{event_code}]
82     or return;
83    
84     $_->($data) for values %$cb;
85    
86     ()
87     }
88    
89     sub inject_command {
90     my ($name, $obj, $params) = @_;
91    
92     for my $cmd (@{ $command{$name} }) {
93     $cmd->[1]->($obj, $params);
94     }
95    
96     -1
97     }
98    
99     sub register_command {
100     my ($name, $time, $cb) = @_;
101    
102     my $caller = caller;
103    
104 root 1.4 warn "registering command '$name/$time' to '$caller'";
105    
106 root 1.1 push @{ $command{$name} }, [$time, $cb, $caller];
107     $COMMAND{"$name\000"} = List::Util::max map $_->[0], @{ $command{$name} };
108     }
109    
110 root 1.6 sub register {
111     my ($base, $pkg) = @_;
112    
113     for my $idx (0 .. $#EVENT) {
114     if (my $ref = $pkg->can ("on_$EVENT[$idx]")) {
115     warn "registering $EVENT[$idx] hook to '$pkg'\n";
116     $hook[$idx]{$base} = $ref;
117     }
118     }
119     }
120    
121 root 1.1 sub load_extension {
122     my ($path) = @_;
123    
124     $path =~ /([^\/\\]+)\.ext$/ or die "$path";
125 root 1.5 my $base = $1;
126 root 1.1 my $pkg = $1;
127     $pkg =~ s/[^[:word:]]/_/g;
128     $pkg = "cf::ext::$pkg";
129    
130     warn "loading '$path' into '$pkg'\n";
131    
132     open my $fh, "<:utf8", $path
133     or die "$path: $!";
134    
135     my $source =
136     "package $pkg; use strict; use utf8;\n"
137     . "#line 1 \"$path\"\n{\n"
138     . (do { local $/; <$fh> })
139     . "\n};\n1";
140    
141     eval $source
142     or die "$path: $@";
143    
144     push @exts, $pkg;
145 root 1.5 $ext_pkg{$base} = $pkg;
146 root 1.1
147 root 1.6 # no strict 'refs';
148 root 1.1 # @{"$pkg\::ISA"} = cf::ext::;
149    
150 root 1.6 register $base, $pkg;
151 root 1.1 }
152    
153     sub unload_extension {
154     my ($pkg) = @_;
155    
156     warn "removing extension $pkg\n";
157    
158     # remove hooks
159     for my $idx (0 .. $#EVENT) {
160     delete $hook[$idx]{$pkg};
161     }
162    
163     # remove commands
164     for my $name (keys %command) {
165     my @cb = grep $_->[2] ne $pkg, @{ $command{$name} };
166    
167     if (@cb) {
168     $command{$name} = \@cb;
169     $COMMAND{"$name\000"} = List::Util::max map $_->[0], @cb;
170     } else {
171     delete $command{$name};
172     delete $COMMAND{"$name\000"};
173     }
174     }
175    
176     Symbol::delete_package $pkg;
177     }
178    
179     sub load_extensions {
180     my $LIBDIR = maps_directory "perl";
181    
182     for my $ext (<$LIBDIR/*.ext>) {
183 root 1.3 next unless -r $ext;
184 root 1.2 eval {
185     load_extension $ext;
186     1
187     } or warn "$ext not loaded: $@";
188 root 1.1 }
189     }
190    
191     register_command "perl-reload", 0, sub {
192     my ($who, $arg) = @_;
193    
194     if ($who->flag (FLAG_WIZ)) {
195 root 1.3 $who->message ("reloading...");
196    
197 root 1.1 warn "reloading...\n";
198 root 1.4 eval {
199     unload_extension $_ for @exts;
200     delete $INC{"cf.pm"};
201    
202     # don't, removes xs symbols, too
203     #Symbol::delete_package $pkg;
204    
205     require cf;
206     };
207     warn $@ if $@;
208     $who->message ($@) if $@;
209 root 1.1 warn "reloaded\n";
210 root 1.3
211     $who->message ("reloaded");
212     } else {
213     $who->message ("Intruder Alert!");
214 root 1.1 }
215     };
216    
217 root 1.7 *on_mapclean = sub {
218     my $map = shift->{map};
219    
220     my $path = $map->tmpname;
221     defined $path or return;
222    
223     unlink "$path.cfperl";
224     };
225    
226 root 1.6 *on_mapin =
227     *on_mapload = sub {
228     my $map = shift->{map};
229    
230     my $path = $map->tmpname;
231     $path = $map->path unless defined $path;
232    
233     open my $fh, "<:raw", "$path.cfperl"
234     or return; # no perl data
235    
236     my $data = Storable::thaw do { local $/; <$fh> };
237    
238     $data->{version} <= 1
239     or return; # too new
240    
241     $map->_set_obs ($data->{obs});
242     };
243    
244     *on_mapout = sub {
245     my $map = shift->{map};
246    
247     my $path = $map->tmpname;
248     $path = $map->path unless defined $path;
249    
250     my $obs = $map->_get_obs;
251    
252     if (defined $obs) {
253     open my $fh, ">:raw", "$path.cfperl"
254     or die "$path.cfperl: $!";
255    
256     print $fh Storable::nfreeze {
257     version => 1,
258     obs => $obs,
259     };
260 root 1.7
261     chmod SAVE_MODE, "$path.cfperl"; # very racy, but cf-compatible *g*
262 root 1.6 } else {
263     unlink "$path.cfperl";
264     }
265     };
266    
267     register "<global>", __PACKAGE__;
268    
269 root 1.1 load_extensions;
270    
271     1
272