ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.18
Committed: Tue Jul 11 14:24:15 2006 UTC (17 years, 10 months ago) by root
Branch: MAIN
Changes since 1.17: +5 -0 lines
Log Message:
Prepared perl plug-in for event support
removed on_clock (use more efficient Event->timer).

File Contents

# Content
1 package cf;
2
3 use Symbol;
4 use List::Util;
5 use Storable;
6 use Event;
7
8 use strict;
9
10 our %COMMAND;
11 our @EVENT;
12 our %PROP_TYPE;
13 our %PROP_IDX;
14
15 BEGIN {
16 @EVENT = map lc, @EVENT;
17
18 *CORE::GLOBAL::warn = sub {
19 my $msg = join "", @_;
20 $msg .= "\n"
21 unless $msg =~ /\n$/;
22
23 print STDERR "cfperl: $msg";
24 LOG llevError, "cfperl: $msg";
25 };
26 }
27
28 my %ignore_set = (MAP_PROP_PATH => 1); # I hate the plug-in api. Deeply!
29
30 # generate property mutators
31 sub prop_gen {
32 my ($prefix, $class) = @_;
33
34 no strict 'refs';
35
36 for my $prop (keys %PROP_TYPE) {
37 $prop =~ /^\Q$prefix\E_(.*$)/ or next;
38 my $sub = lc $1;
39
40 my $type = $PROP_TYPE{$prop};
41 my $idx = $PROP_IDX {$prop};
42
43 *{"$class\::get_$sub"} = *{"$class\::$sub"} = sub {
44 $_[0]->get_property ($type, $idx)
45 };
46
47 *{"$class\::set_$sub"} = sub {
48 $_[0]->set_property ($type, $idx, $_[1]);
49 } unless $ignore_set{$prop};
50 }
51 }
52
53 # auto-generate most of the API
54
55 prop_gen OBJECT_PROP => "cf::object";
56 # CFAPI_OBJECT_ANIMATION?
57 prop_gen PLAYER_PROP => "cf::object::player";
58
59 prop_gen MAP_PROP => "cf::map";
60 prop_gen ARCH_PROP => "cf::arch";
61
62 # guessed hierarchies
63
64 @cf::object::player::ISA = 'cf::object';
65 @cf::object::map::ISA = 'cf::object';
66
67 $Event::DIED = sub {
68 warn "error in event callback: @_";
69 };
70
71 my %ext_pkg;
72 my @exts;
73 my @hook;
74 my %command;
75 my %extcmd;
76
77 sub inject_event {
78 my $extension = shift;
79 my $event_code = shift;
80
81 my $cb = $hook[$event_code]{$extension}
82 or return;
83
84 &$cb
85 }
86
87 sub inject_global_event {
88 my $event = shift;
89
90 my $cb = $hook[$event]
91 or return;
92
93 List::Util::max map &$_, values %$cb
94 }
95
96 sub inject_command {
97 my ($name, $obj, $params) = @_;
98
99 for my $cmd (@{ $command{$name} }) {
100 $cmd->[1]->($obj, $params);
101 }
102
103 -1
104 }
105
106 sub register_command {
107 my ($name, $time, $cb) = @_;
108
109 my $caller = caller;
110 #warn "registering command '$name/$time' to '$caller'";
111
112 push @{ $command{$name} }, [$time, $cb, $caller];
113 $COMMAND{"$name\000"} = List::Util::max map $_->[0], @{ $command{$name} };
114 }
115
116 sub register_extcmd {
117 my ($name, $cb) = @_;
118
119 my $caller = caller;
120 #warn "registering extcmd '$name' to '$caller'";
121
122 $extcmd{$name} = [$cb, $caller];
123 }
124
125 sub register {
126 my ($base, $pkg) = @_;
127
128 for my $idx (0 .. $#EVENT) {
129 if (my $ref = $pkg->can ("on_$EVENT[$idx]")) {
130 #warn "registering $EVENT[$idx] hook to '$pkg'\n";
131 $hook[$idx]{$base} = $ref;
132 }
133 }
134 }
135
136 sub load_extension {
137 my ($path) = @_;
138
139 $path =~ /([^\/\\]+)\.ext$/ or die "$path";
140 my $base = $1;
141 my $pkg = $1;
142 $pkg =~ s/[^[:word:]]/_/g;
143 $pkg = "cf::ext::$pkg";
144
145 warn "loading '$path' into '$pkg'\n";
146
147 open my $fh, "<:utf8", $path
148 or die "$path: $!";
149
150 my $source =
151 "package $pkg; use strict; use utf8;\n"
152 . "#line 1 \"$path\"\n{\n"
153 . (do { local $/; <$fh> })
154 . "\n};\n1";
155
156 eval $source
157 or die "$path: $@";
158
159 push @exts, $pkg;
160 $ext_pkg{$base} = $pkg;
161
162 # no strict 'refs';
163 # @{"$pkg\::ISA"} = cf::ext::;
164
165 register $base, $pkg;
166 }
167
168 sub unload_extension {
169 my ($pkg) = @_;
170
171 warn "removing extension $pkg\n";
172
173 # remove hooks
174 for my $idx (0 .. $#EVENT) {
175 delete $hook[$idx]{$pkg};
176 }
177
178 # remove commands
179 for my $name (keys %command) {
180 my @cb = grep $_->[2] ne $pkg, @{ $command{$name} };
181
182 if (@cb) {
183 $command{$name} = \@cb;
184 $COMMAND{"$name\000"} = List::Util::max map $_->[0], @cb;
185 } else {
186 delete $command{$name};
187 delete $COMMAND{"$name\000"};
188 }
189 }
190
191 # remove extcmds
192 for my $name (grep $extcmd{$_}[1] eq $pkg, keys %extcmd) {
193 delete $extcmd{$name};
194 }
195
196 Symbol::delete_package $pkg;
197 }
198
199 sub load_extensions {
200 my $LIBDIR = maps_directory "perl";
201
202 for my $ext (<$LIBDIR/*.ext>) {
203 next unless -r $ext;
204 eval {
205 load_extension $ext;
206 1
207 } or warn "$ext not loaded: $@";
208 }
209 }
210
211 register_command "perl-reload", 0, sub {
212 my ($who, $arg) = @_;
213
214 if ($who->flag (FLAG_WIZ)) {
215 $who->message ("reloading...");
216
217 warn "reloading...\n";
218 eval {
219 unload_extension $_ for @exts;
220 delete $INC{"cf.pm"};
221
222 # don't, removes xs symbols, too
223 #Symbol::delete_package $pkg;
224
225 require cf;
226 };
227 warn $@ if $@;
228 $who->message ($@) if $@;
229 warn "reloaded\n";
230
231 $who->message ("reloaded");
232 } else {
233 $who->message ("Intruder Alert!");
234 }
235 };
236
237 #############################################################################
238 # extcmd framework, basically convert ext <id> <pkg> arg1 args
239 # into pkg::->on_extcmd_arg1 (...) while shortcutting a few
240
241 sub on_extcmd {
242 my ($pl, $buf) = @_;
243
244 my ($type) = $buf =~ s/^(\S+) // ? $1 : "";
245
246 $extcmd{$type}[0]->($pl, $buf)
247 if $extcmd{$type};
248 }
249
250 #############################################################################
251 # load/save/clean perl data associated with a map
252
253 *on_mapclean = sub {
254 my ($map) = @_;
255
256 my $path = $map->tmpname;
257 defined $path or return;
258
259 unlink "$path.cfperl";
260 };
261
262 *on_mapin =
263 *on_mapload = sub {
264 my ($map) = @_;
265
266 my $path = $map->tmpname;
267 $path = $map->path unless defined $path;
268
269 open my $fh, "<:raw", "$path.cfperl"
270 or return; # no perl data
271
272 my $data = Storable::thaw do { local $/; <$fh> };
273
274 $data->{version} <= 1
275 or return; # too new
276
277 $map->_set_obs ($data->{obs});
278 };
279
280 *on_mapout = sub {
281 my ($map) = @_;
282
283 my $path = $map->tmpname;
284 $path = $map->path unless defined $path;
285
286 my $obs = $map->_get_obs;
287
288 if (defined $obs) {
289 open my $fh, ">:raw", "$path.cfperl"
290 or die "$path.cfperl: $!";
291
292 stat $path;
293
294 print $fh Storable::nfreeze {
295 size => (stat _)[7],
296 time => (stat _)[9],
297 version => 1,
298 obs => $obs,
299 };
300
301 chmod SAVE_MODE, "$path.cfperl"; # very racy, but cf-compatible *g*
302 } else {
303 unlink "$path.cfperl";
304 }
305 };
306
307 #############################################################################
308 # load/save perl data associated with player->ob objects
309
310 *on_player_load = sub {
311 my ($ob, $path) = @_;
312
313 if (open my $fh, "<:raw", "$path.cfperl") {
314
315 #d##TODO#remove
316
317 my $data = Storable::thaw do { local $/; <$fh> };
318
319 $data->{version} <= 1
320 or return; # too new
321
322 %$ob = %{$data->{ob}};
323 return;
324 }
325
326 for my $o ($ob, $ob->inv) {
327 if (my $value = $o->get_ob_key_value ("_perl_data")) {
328 $o->set_ob_key_value ("_perl_data");
329
330 %$o = %{ Storable::thaw pack "H*", $value };
331 }
332 }
333 };
334
335 *on_player_save = sub {
336 my ($ob, $path) = @_;
337
338 $_->set_ob_key_value (_perl_data => unpack "H*", Storable::nfreeze $_)
339 for grep %$_, $ob, $ob->inv;
340
341 unlink "$path.cfperl";#d##TODO#remove
342 };
343
344 register "<global>", __PACKAGE__;
345
346 unshift @INC, maps_directory "perl";
347
348 load_extensions;
349
350 1
351