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.73 by root, Sun Oct 1 11:46:51 2006 UTC vs.
Revision 1.95 by root, Fri Dec 22 02:04:20 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 ();
10use YAML::Syck (); 11use YAML::Syck ();
11use Time::HiRes; 12use Time::HiRes;
12use Event; 13use Event;
13$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
14 15
15# work around bug in YAML::Syck - bad news for perl6, will it be as broken wrt. unicode? 16# work around bug in YAML::Syck - bad news for perl6, will it be as broken wrt. unicode?
16$YAML::Syck::ImplicitUnicode = 1; 17$YAML::Syck::ImplicitUnicode = 1;
17 18
18use strict; 19use strict;
19 20
21sub WF_AUTOCANCEL () { 1 } # automatically cancel this watcher on reload
22
23our %COMMAND = ();
24our %COMMAND_TIME = ();
25our %EXTCMD = ();
26
20_init_vars; 27_init_vars;
21 28
22our %COMMAND = ();
23our @EVENT; 29our @EVENT;
24our $LIBDIR = maps_directory "perl"; 30our $LIBDIR = datadir . "/ext";
25 31
26our $TICK = MAX_TIME * 1e-6; 32our $TICK = MAX_TIME * 1e-6;
27our $TICK_WATCHER; 33our $TICK_WATCHER;
28our $NEXT_TICK; 34our $NEXT_TICK;
29 35
30our %CFG; 36our %CFG;
31 37
38our $UPTIME; $UPTIME ||= time;
39
32############################################################################# 40#############################################################################
33 41
34=head2 GLOBAL VARIABLES 42=head2 GLOBAL VARIABLES
35 43
36=over 4 44=over 4
45
46=item $cf::UPTIME
47
48The timestamp of the server start (so not actually an uptime).
37 49
38=item $cf::LIBDIR 50=item $cf::LIBDIR
39 51
40The perl library directory, where extensions and cf-specific modules can 52The perl library directory, where extensions and cf-specific modules can
41be found. It will be added to C<@INC> automatically. 53be found. It will be added to C<@INC> automatically.
62 print STDERR "cfperl: $msg"; 74 print STDERR "cfperl: $msg";
63 LOG llevError, "cfperl: $msg"; 75 LOG llevError, "cfperl: $msg";
64 }; 76 };
65} 77}
66 78
79@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';
67@safe::cf::object::player::ISA = @cf::object::player::ISA = 'cf::object'; 84@safe::cf::object::player::ISA = @cf::object::player::ISA = 'cf::object';
68 85
69# we bless all objects into (empty) derived classes to force a method lookup 86# we bless all objects into (empty) derived classes to force a method lookup
70# within the Safe compartment. 87# within the Safe compartment.
71for my $pkg (qw(cf::object cf::object::player cf::player cf::map cf::party cf::region cf::arch cf::living)) { 88for my $pkg (qw(
89 cf::global
90 cf::object cf::object::player
91 cf::client cf::player
92 cf::arch cf::living
93 cf::map cf::party cf::region
94)) {
72 no strict 'refs'; 95 no strict 'refs';
73 @{"safe::$pkg\::wrap::ISA"} = @{"$pkg\::wrap::ISA"} = $pkg; 96 @{"safe::$pkg\::wrap::ISA"} = @{"$pkg\::wrap::ISA"} = $pkg;
74} 97}
75 98
76$Event::DIED = sub { 99$Event::DIED = sub {
78}; 101};
79 102
80my %ext_pkg; 103my %ext_pkg;
81my @exts; 104my @exts;
82my @hook; 105my @hook;
83my %command;
84my %extcmd;
85 106
86=head2 UTILITY FUNCTIONS 107=head2 UTILITY FUNCTIONS
87 108
88=over 4 109=over 4
89 110
117 138
118=cut 139=cut
119 140
120############################################################################# 141#############################################################################
121 142
122=head2 EVENTS AND OBJECT ATTACHMENTS 143=head2 ATTACHABLE OBJECTS
144
145Many objects in crossfire are so-called attachable objects. That means you can
146attach callbacks/event handlers (a collection of which is called an "attachment")
147to it. All such attachable objects support the following methods.
148
149In the following description, CLASS can be any of C<global>, C<object>
150C<player>, C<client> or C<map> (i.e. the attachable objects in
151crossfire+).
123 152
124=over 4 153=over 4
125 154
126=item $object->attach ($attachment, key => $value...)
127
128=item $object->detach ($attachment)
129
130Attach/detach a pre-registered attachment to an object.
131
132=item $player->attach ($attachment, key => $value...)
133
134=item $player->detach ($attachment)
135
136Attach/detach a pre-registered attachment to a player.
137
138=item $map->attach ($attachment, key => $value...) 155=item $attachable->attach ($attachment, key => $value...)
139 156
140=item $map->detach ($attachment) 157=item $attachable->detach ($attachment)
141 158
142Attach/detach a pre-registered attachment to a map. 159Attach/detach a pre-registered attachment to a specific object and give it
160the specified key/value pairs as arguments.
143 161
144=item $bool = $object->attached ($name) 162Example, attach a minesweeper attachment to the given object, making it a
16310x10 minesweeper game:
145 164
146=item $bool = $player->attached ($name) 165 $obj->attach (minesweeper => width => 10, height => 10);
147 166
148=item $bool = $map->attached ($name) 167=item $bool = $attachable->attached ($name)
149 168
150Checks wether the named attachment is currently attached to the object. 169Checks wether the named attachment is currently attached to the object.
151 170
152=item cf::attach_global ... 171=item cf::CLASS->attach ...
153 172
154Attach handlers for global events. 173=item cf::CLASS->detach ...
155 174
156This and all following C<attach_*>-functions expect any number of the 175Define an anonymous attachment and attach it to all objects of the given
157following handler/hook descriptions: 176CLASS. See the next function for an explanation of its arguments.
177
178You can attach to global events by using the C<cf::global> class.
179
180Example, log all player logins:
181
182 cf::player->attach (
183 on_login => sub {
184 my ($pl) = @_;
185 ...
186 },
187 );
188
189Example, attach to the jeweler skill:
190
191 cf::object->attach (
192 type => cf::SKILL,
193 subtype => cf::SK_JEWELER,
194 on_use_skill => sub {
195 my ($sk, $ob, $part, $dir, $msg) = @_;
196 ...
197 },
198 );
199
200=item cf::CLASS::attachment $name, ...
201
202Register an attachment by C<$name> through which attachable objects of the
203given CLASS can refer to this attachment.
204
205Some classes such as crossfire maps and objects can specify attachments
206that are attached at load/instantiate time, thus the need for a name.
207
208These calls expect any number of the following handler/hook descriptions:
158 209
159=over 4 210=over 4
160 211
161=item prio => $number 212=item prio => $number
162 213
164by another C<prio> setting). Lower priority handlers get executed 215by another C<prio> setting). Lower priority handlers get executed
165earlier. The default priority is C<0>, and many built-in handlers are 216earlier. The default priority is C<0>, and many built-in handlers are
166registered at priority C<-1000>, so lower priorities should not be used 217registered at priority C<-1000>, so lower priorities should not be used
167unless you know what you are doing. 218unless you know what you are doing.
168 219
220=item type => $type
221
222(Only for C<< cf::object->attach >> calls), limits the attachment to the
223given type of objects only (the additional parameter C<subtype> can be
224used to further limit to the given subtype).
225
169=item on_I<event> => \&cb 226=item on_I<event> => \&cb
170 227
171Call the given code reference whenever the named event happens (event is 228Call the given code reference whenever the named event happens (event is
172something like C<instantiate>, C<apply>, C<use_skill> and so on, and which 229something like C<instantiate>, C<apply>, C<use_skill> and so on, and which
173handlers are recognised generally depends on the type of object these 230handlers are recognised generally depends on the type of object these
182package and register them. Only handlers for eevents supported by the 239package and register them. Only handlers for eevents supported by the
183object/class are recognised. 240object/class are recognised.
184 241
185=back 242=back
186 243
187=item cf::attach_to_type $object_type, $subtype, ... 244Example, define an attachment called "sockpuppet" that calls the given
245event handler when a monster attacks:
188 246
189Attach handlers for a specific object type (e.g. TRANSPORT) and 247 cf::object::attachment sockpuppet =>
190subtype. If C<$subtype> is zero or undef, matches all objects of the given 248 on_skill_attack => sub {
191type. 249 my ($self, $victim) = @_;
192 250 ...
193=item cf::attach_to_objects ... 251 }
194 252 }
195Attach handlers to all objects. Do not use this except for debugging or
196very rare events, as handlers are (obviously) called for I<all> objects in
197the game.
198
199=item cf::attach_to_players ...
200
201Attach handlers to all players.
202
203=item cf::attach_to_maps ...
204
205Attach handlers to all maps.
206
207=item cf:register_attachment $name, ...
208
209Register an attachment by name through which objects can refer to this
210attachment.
211
212=item cf:register_player_attachment $name, ...
213
214Register an attachment by name through which players can refer to this
215attachment.
216
217=item cf:register_map_attachment $name, ...
218
219Register an attachment by name through which maps can refer to this
220attachment.
221 253
222=cut 254=cut
223 255
224# the following variables are defined in .xs and must not be re-created 256# the following variables are defined in .xs and must not be re-created
225our @CB_GLOBAL = (); # registry for all global events 257our @CB_GLOBAL = (); # registry for all global events
226our @CB_OBJECT = (); # all objects (should not be used except in emergency) 258our @CB_OBJECT = (); # all objects (should not be used except in emergency)
227our @CB_PLAYER = (); 259our @CB_PLAYER = ();
260our @CB_CLIENT = ();
228our @CB_TYPE = (); # registry for type (cf-object class) based events 261our @CB_TYPE = (); # registry for type (cf-object class) based events
229our @CB_MAP = (); 262our @CB_MAP = ();
230 263
231my %attachment; 264my %attachment;
232 265
233sub _attach_cb($\%$$$) { 266sub _attach_cb($$$$) {
234 my ($registry, $undo, $event, $prio, $cb) = @_; 267 my ($registry, $event, $prio, $cb) = @_;
235 268
236 use sort 'stable'; 269 use sort 'stable';
237 270
238 $cb = [$prio, $cb]; 271 $cb = [$prio, $cb];
239 272
240 @{$registry->[$event]} = sort 273 @{$registry->[$event]} = sort
241 { $a->[0] cmp $b->[0] } 274 { $a->[0] cmp $b->[0] }
242 @{$registry->[$event] || []}, $cb; 275 @{$registry->[$event] || []}, $cb;
243
244 push @{$undo->{cb}}, [$event, $cb];
245} 276}
246 277
247# attach handles attaching event callbacks 278# attach handles attaching event callbacks
248# the only thing the caller has to do is pass the correct 279# the only thing the caller has to do is pass the correct
249# registry (== where the callback attaches to). 280# registry (== where the callback attaches to).
250sub _attach(\@$@) { 281sub _attach {
251 my ($registry, $klass, @arg) = @_; 282 my ($registry, $klass, @arg) = @_;
252 283
284 my $object_type;
253 my $prio = 0; 285 my $prio = 0;
254
255 my %undo = (
256 registry => $registry,
257 cb => [],
258 );
259
260 my %cb_id = map +("on_" . lc $EVENT[$_][0], $_) , grep $EVENT[$_][1] == $klass, 0 .. $#EVENT; 286 my %cb_id = map +("on_" . lc $EVENT[$_][0], $_) , grep $EVENT[$_][1] == $klass, 0 .. $#EVENT;
261 287
262 while (@arg) { 288 while (@arg) {
263 my $type = shift @arg; 289 my $type = shift @arg;
264 290
265 if ($type eq "prio") { 291 if ($type eq "prio") {
266 $prio = shift @arg; 292 $prio = shift @arg;
267 293
294 } elsif ($type eq "type") {
295 $object_type = shift @arg;
296 $registry = $CB_TYPE[$object_type] ||= [];
297
298 } elsif ($type eq "subtype") {
299 defined $object_type or Carp::croak "subtype specified without type";
300 my $object_subtype = shift @arg;
301 $registry = $CB_TYPE[$object_type + $object_subtype * NUM_SUBTYPES] ||= [];
302
268 } elsif ($type eq "package") { 303 } elsif ($type eq "package") {
269 my $pkg = shift @arg; 304 my $pkg = shift @arg;
270 305
271 while (my ($name, $id) = each %cb_id) { 306 while (my ($name, $id) = each %cb_id) {
272 if (my $cb = $pkg->can ($name)) { 307 if (my $cb = $pkg->can ($name)) {
273 _attach_cb $registry, %undo, $id, $prio, $cb; 308 _attach_cb $registry, $id, $prio, $cb;
274 } 309 }
275 } 310 }
276 311
277 } elsif (exists $cb_id{$type}) { 312 } elsif (exists $cb_id{$type}) {
278 _attach_cb $registry, %undo, $cb_id{$type}, $prio, shift @arg; 313 _attach_cb $registry, $cb_id{$type}, $prio, shift @arg;
279 314
280 } elsif (ref $type) { 315 } elsif (ref $type) {
281 warn "attaching objects not supported, ignoring.\n"; 316 warn "attaching objects not supported, ignoring.\n";
282 317
283 } else { 318 } else {
284 shift @arg; 319 shift @arg;
285 warn "attach argument '$type' not supported, ignoring.\n"; 320 warn "attach argument '$type' not supported, ignoring.\n";
286 } 321 }
287 } 322 }
288
289 \%undo
290} 323}
291 324
292sub _attach_attachment { 325sub _object_attach {
293 my ($obj, $name, %arg) = @_; 326 my ($obj, $name, %arg) = @_;
294 327
295 return if exists $obj->{_attachment}{$name}; 328 return if exists $obj->{_attachment}{$name};
296
297 my $res;
298 329
299 if (my $attach = $attachment{$name}) { 330 if (my $attach = $attachment{$name}) {
300 my $registry = $obj->registry; 331 my $registry = $obj->registry;
301 332
302 for (@$attach) { 333 for (@$attach) {
303 my ($klass, @attach) = @$_; 334 my ($klass, @attach) = @$_;
304 $res = _attach @$registry, $klass, @attach; 335 _attach $registry, $klass, @attach;
305 } 336 }
306 337
307 $obj->{$name} = \%arg; 338 $obj->{$name} = \%arg;
308 } else { 339 } else {
309 warn "object uses attachment '$name' that is not available, postponing.\n"; 340 warn "object uses attachment '$name' that is not available, postponing.\n";
310 } 341 }
311 342
312 $obj->{_attachment}{$name} = undef; 343 $obj->{_attachment}{$name} = undef;
313
314 $res->{attachment} = $name;
315 $res
316} 344}
317 345
318*cf::object::attach = 346sub cf::attachable::attach {
319*cf::player::attach = 347 if (ref $_[0]) {
320*cf::map::attach = sub { 348 _object_attach @_;
321 my ($obj, $name, %arg) = @_; 349 } else {
322 350 _attach shift->_attach_registry, @_;
323 _attach_attachment $obj, $name, %arg; 351 }
324}; 352};
325 353
326# all those should be optimised 354# all those should be optimised
327*cf::object::detach = 355sub cf::attachable::detach {
328*cf::player::detach =
329*cf::map::detach = sub {
330 my ($obj, $name) = @_; 356 my ($obj, $name) = @_;
331 357
358 if (ref $obj) {
332 delete $obj->{_attachment}{$name}; 359 delete $obj->{_attachment}{$name};
333 reattach ($obj); 360 reattach ($obj);
361 } else {
362 Carp::croak "cannot, currently, detach class attachments";
363 }
334}; 364};
335 365
336*cf::object::attached = 366sub cf::attachable::attached {
337*cf::player::attached =
338*cf::map::attached = sub {
339 my ($obj, $name) = @_; 367 my ($obj, $name) = @_;
340 368
341 exists $obj->{_attachment}{$name} 369 exists $obj->{_attachment}{$name}
342};
343
344sub attach_global {
345 _attach @CB_GLOBAL, KLASS_GLOBAL, @_
346} 370}
347 371
348sub attach_to_type { 372for my $klass (qw(GLOBAL OBJECT PLAYER CLIENT MAP)) {
349 my $type = shift; 373 eval "#line " . __LINE__ . " 'cf.pm'
350 my $subtype = shift; 374 sub cf::\L$klass\E::_attach_registry {
375 (\\\@CB_$klass, KLASS_$klass)
376 }
351 377
352 _attach @{$CB_TYPE[$type + $subtype * NUM_SUBTYPES]}, KLASS_OBJECT, @_ 378 sub cf::\L$klass\E::attachment {
353}
354
355sub attach_to_objects {
356 _attach @CB_OBJECT, KLASS_OBJECT, @_
357}
358
359sub attach_to_players {
360 _attach @CB_PLAYER, KLASS_PLAYER, @_
361}
362
363sub attach_to_maps {
364 _attach @CB_MAP, KLASS_MAP, @_
365}
366
367sub register_attachment {
368 my $name = shift; 379 my \$name = shift;
369 380
370 $attachment{$name} = [[KLASS_OBJECT, @_]];
371}
372
373sub register_player_attachment {
374 my $name = shift;
375
376 $attachment{$name} = [[KLASS_PLAYER, @_]];
377}
378
379sub register_map_attachment {
380 my $name = shift;
381
382 $attachment{$name} = [[KLASS_MAP, @_]]; 381 \$attachment{\$name} = [[KLASS_$klass, \@_]];
382 }
383 ";
384 die if $@;
383} 385}
384 386
385our $override; 387our $override;
386our @invoke_results = (); # referenced from .xs code. TODO: play tricks with reify and mortals? 388our @invoke_results = (); # referenced from .xs code. TODO: play tricks with reify and mortals?
387 389
417 419
418=item $bool = $object->invoke (EVENT_OBJECT_XXX, ...) 420=item $bool = $object->invoke (EVENT_OBJECT_XXX, ...)
419 421
420=item $bool = $player->invoke (EVENT_PLAYER_XXX, ...) 422=item $bool = $player->invoke (EVENT_PLAYER_XXX, ...)
421 423
424=item $bool = $client->invoke (EVENT_CLIENT_XXX, ...)
425
422=item $bool = $map->invoke (EVENT_MAP_XXX, ...) 426=item $bool = $map->invoke (EVENT_MAP_XXX, ...)
423 427
424Generate a global/object/player/map-specific event with the given arguments. 428Generate a global/object/player/map-specific event with the given arguments.
425 429
426This API is preliminary (most likely, the EVENT_KLASS_xxx prefix will be 430This API is preliminary (most likely, the EVENT_KLASS_xxx prefix will be
431 435
432=cut 436=cut
433 437
434############################################################################# 438#############################################################################
435 439
436=head2 METHODS VALID FOR ALL CORE OBJECTS 440=head2 METHODS VALID FOR ALL ATTACHABLE OBJECTS
441
442Attachable objects includes objects, players, clients and maps.
437 443
438=over 4 444=over 4
439 445
440=item $object->valid, $player->valid, $map->valid 446=item $object->valid
441 447
442Just because you have a perl object does not mean that the corresponding 448Just because you have a perl object does not mean that the corresponding
443C-level object still exists. If you try to access an object that has no 449C-level object still exists. If you try to access an object that has no
444valid C counterpart anymore you get an exception at runtime. This method 450valid C counterpart anymore you get an exception at runtime. This method
445can be used to test for existence of the C object part without causing an 451can be used to test for existence of the C object part without causing an
447 453
448=back 454=back
449 455
450=cut 456=cut
451 457
452*cf::object::valid =
453*cf::player::valid =
454*cf::map::valid = \&cf::_valid;
455
456############################################################################# 458#############################################################################
457# object support 459# object support
458 460
459sub instantiate { 461sub instantiate {
460 my ($obj, $data) = @_; 462 my ($obj, $data) = @_;
479 481
480 for my $name (keys %{ $obj->{_attachment} || {} }) { 482 for my $name (keys %{ $obj->{_attachment} || {} }) {
481 if (my $attach = $attachment{$name}) { 483 if (my $attach = $attachment{$name}) {
482 for (@$attach) { 484 for (@$attach) {
483 my ($klass, @attach) = @$_; 485 my ($klass, @attach) = @$_;
484 _attach @$registry, $klass, @attach; 486 _attach $registry, $klass, @attach;
485 } 487 }
486 } else { 488 } else {
487 warn "object uses attachment '$name' that is not available, postponing.\n"; 489 warn "object uses attachment '$name' that is not available, postponing.\n";
488 } 490 }
489 } 491 }
518 unlink $filename; 520 unlink $filename;
519 unlink "$filename.pst"; 521 unlink "$filename.pst";
520 } 522 }
521} 523}
522 524
525sub object_freezer_as_string {
526 my ($rdata, $objs) = @_;
527
528 use Data::Dumper;
529
530 $$rdata . Dumper $objs
531}
532
523sub object_thawer_load { 533sub object_thawer_load {
524 my ($filename) = @_; 534 my ($filename) = @_;
525 535
526 local $/; 536 local $/;
527 537
537 } 547 }
538 548
539 () 549 ()
540} 550}
541 551
542attach_to_objects 552cf::object->attach (
543 prio => -1000000, 553 prio => -1000000,
544 on_clone => sub { 554 on_clone => sub {
545 my ($src, $dst) = @_; 555 my ($src, $dst) = @_;
546 556
547 @{$dst->registry} = @{$src->registry}; 557 @{$dst->registry} = @{$src->registry};
549 %$dst = %$src; 559 %$dst = %$src;
550 560
551 %{$dst->{_attachment}} = %{$src->{_attachment}} 561 %{$dst->{_attachment}} = %{$src->{_attachment}}
552 if exists $src->{_attachment}; 562 if exists $src->{_attachment};
553 }, 563 },
554; 564);
555 565
556############################################################################# 566#############################################################################
557# old plug-in events 567# command handling &c
558 568
559sub inject_event { 569=item cf::register_command $name => \&callback($ob,$args);
560 my $extension = shift;
561 my $event_code = shift;
562 570
563 my $cb = $hook[$event_code]{$extension} 571Register a callback for execution when the client sends the user command
564 or return; 572$name.
565 573
566 &$cb 574=cut
567}
568
569sub inject_global_event {
570 my $event = shift;
571
572 my $cb = $hook[$event]
573 or return;
574
575 List::Util::max map &$_, values %$cb
576}
577
578sub inject_command {
579 my ($name, $obj, $params) = @_;
580
581 for my $cmd (@{ $command{$name} }) {
582 $cmd->[1]->($obj, $params);
583 }
584
585 -1
586}
587 575
588sub register_command { 576sub register_command {
589 my ($name, $time, $cb) = @_; 577 my ($name, $cb) = @_;
590 578
591 my $caller = caller; 579 my $caller = caller;
592 #warn "registering command '$name/$time' to '$caller'"; 580 #warn "registering command '$name/$time' to '$caller'";
593 581
594 push @{ $command{$name} }, [$time, $cb, $caller]; 582 push @{ $COMMAND{$name} }, [$caller, $cb];
595 $COMMAND{"$name\000"} = List::Util::max map $_->[0], @{ $command{$name} };
596} 583}
584
585=item cf::register_extcmd $name => \&callback($pl,$packet);
586
587Register a callbackf ro execution when the client sends an extcmd packet.
588
589If the callback returns something, it is sent back as if reply was being
590called.
591
592=cut
597 593
598sub register_extcmd { 594sub register_extcmd {
599 my ($name, $cb) = @_; 595 my ($name, $cb) = @_;
600 596
601 my $caller = caller; 597 my $caller = caller;
602 #warn "registering extcmd '$name' to '$caller'"; 598 #warn "registering extcmd '$name' to '$caller'";
603 599
604 $extcmd{$name} = [$cb, $caller]; 600 $EXTCMD{$name} = [$cb, $caller];
605} 601}
602
603cf::player->attach (
604 on_command => sub {
605 my ($pl, $name, $params) = @_;
606
607 my $cb = $COMMAND{$name}
608 or return;
609
610 for my $cmd (@$cb) {
611 $cmd->[1]->($pl->ob, $params);
612 }
613
614 cf::override;
615 },
616 on_extcmd => sub {
617 my ($pl, $buf) = @_;
618
619 my $msg = eval { from_json $buf };
620
621 if (ref $msg) {
622 if (my $cb = $EXTCMD{$msg->{msgtype}}) {
623 if (my %reply = $cb->[0]->($pl, $msg)) {
624 $pl->ext_reply ($msg->{msgid}, %reply);
625 }
626 }
627 } else {
628 warn "player " . ($pl->ob->name) . " sent unparseable ext message: <$buf>\n";
629 }
630
631 cf::override;
632 },
633);
606 634
607sub register { 635sub register {
608 my ($base, $pkg) = @_; 636 my ($base, $pkg) = @_;
609 637
610 #TODO 638 #TODO
629 . "#line 1 \"$path\"\n{\n" 657 . "#line 1 \"$path\"\n{\n"
630 . (do { local $/; <$fh> }) 658 . (do { local $/; <$fh> })
631 . "\n};\n1"; 659 . "\n};\n1";
632 660
633 eval $source 661 eval $source
634 or die "$path: $@"; 662 or die $@ ? "$path: $@\n"
663 : "extension disabled.\n";
635 664
636 push @exts, $pkg; 665 push @exts, $pkg;
637 $ext_pkg{$base} = $pkg; 666 $ext_pkg{$base} = $pkg;
638 667
639# no strict 'refs'; 668# no strict 'refs';
652# for my $idx (0 .. $#PLUGIN_EVENT) { 681# for my $idx (0 .. $#PLUGIN_EVENT) {
653# delete $hook[$idx]{$pkg}; 682# delete $hook[$idx]{$pkg};
654# } 683# }
655 684
656 # remove commands 685 # remove commands
657 for my $name (keys %command) { 686 for my $name (keys %COMMAND) {
658 my @cb = grep $_->[2] ne $pkg, @{ $command{$name} }; 687 my @cb = grep $_->[0] ne $pkg, @{ $COMMAND{$name} };
659 688
660 if (@cb) { 689 if (@cb) {
661 $command{$name} = \@cb; 690 $COMMAND{$name} = \@cb;
662 $COMMAND{"$name\000"} = List::Util::max map $_->[0], @cb;
663 } else { 691 } else {
664 delete $command{$name};
665 delete $COMMAND{"$name\000"}; 692 delete $COMMAND{$name};
666 } 693 }
667 } 694 }
668 695
669 # remove extcmds 696 # remove extcmds
670 for my $name (grep $extcmd{$_}[1] eq $pkg, keys %extcmd) { 697 for my $name (grep $EXTCMD{$_}[1] eq $pkg, keys %EXTCMD) {
671 delete $extcmd{$name}; 698 delete $EXTCMD{$name};
672 } 699 }
673 700
674 if (my $cb = $pkg->can ("unload")) { 701 if (my $cb = $pkg->can ("unload")) {
675 eval { 702 eval {
676 $cb->($pkg); 703 $cb->($pkg);
680 707
681 Symbol::delete_package $pkg; 708 Symbol::delete_package $pkg;
682} 709}
683 710
684sub load_extensions { 711sub load_extensions {
685 my $LIBDIR = maps_directory "perl";
686
687 for my $ext (<$LIBDIR/*.ext>) { 712 for my $ext (<$LIBDIR/*.ext>) {
688 next unless -r $ext; 713 next unless -r $ext;
689 eval { 714 eval {
690 load_extension $ext; 715 load_extension $ext;
691 1 716 1
692 } or warn "$ext not loaded: $@"; 717 } or warn "$ext not loaded: $@";
693 } 718 }
694} 719}
695 720
696############################################################################# 721#############################################################################
697# extcmd framework, basically convert ext <msg>
698# into pkg::->on_extcmd_arg1 (...) while shortcutting a few
699
700attach_to_players
701 on_extcmd => sub {
702 my ($pl, $buf) = @_;
703
704 my $msg = eval { from_json $buf };
705
706 if (ref $msg) {
707 if (my $cb = $extcmd{$msg->{msgtype}}) {
708 if (my %reply = $cb->[0]->($pl, $msg)) {
709 $pl->ext_reply ($msg->{msgid}, %reply);
710 }
711 }
712 } else {
713 warn "player " . ($pl->ob->name) . " sent unparseable ext message: <$buf>\n";
714 }
715
716 cf::override;
717 },
718;
719
720#############################################################################
721# load/save/clean perl data associated with a map 722# load/save/clean perl data associated with a map
722 723
723*cf::mapsupport::on_clean = sub { 724*cf::mapsupport::on_clean = sub {
724 my ($map) = @_; 725 my ($map) = @_;
725 726
727 defined $path or return; 728 defined $path or return;
728 729
729 unlink "$path.pst"; 730 unlink "$path.pst";
730}; 731};
731 732
732attach_to_maps prio => -10000, package => cf::mapsupport::; 733cf::map->attach (prio => -10000, package => cf::mapsupport::);
733 734
734############################################################################# 735#############################################################################
735# load/save perl data associated with player->ob objects 736# load/save perl data associated with player->ob objects
736 737
737sub all_objects(@) { 738sub all_objects(@) {
738 @_, map all_objects ($_->inv), @_ 739 @_, map all_objects ($_->inv), @_
739} 740}
740 741
741# TODO: compatibility cruft, remove when no longer needed 742# TODO: compatibility cruft, remove when no longer needed
742attach_to_players 743cf::player->attach (
743 on_load => sub { 744 on_load => sub {
744 my ($pl, $path) = @_; 745 my ($pl, $path) = @_;
745 746
746 for my $o (all_objects $pl->ob) { 747 for my $o (all_objects $pl->ob) {
747 if (my $value = $o->get_ob_key_value ("_perl_data")) { 748 if (my $value = $o->get_ob_key_value ("_perl_data")) {
749 750
750 %$o = %{ Storable::thaw pack "H*", $value }; 751 %$o = %{ Storable::thaw pack "H*", $value };
751 } 752 }
752 } 753 }
753 }, 754 },
754; 755);
755 756
756############################################################################# 757#############################################################################
757 758
758=head2 CORE EXTENSIONS 759=head2 CORE EXTENSIONS
759 760
760Functions and methods that extend core crossfire objects. 761Functions and methods that extend core crossfire objects.
762
763=head3 cf::player
761 764
762=over 4 765=over 4
763 766
764=item cf::player::exists $login 767=item cf::player::exists $login
765 768
770sub cf::player::exists($) { 773sub cf::player::exists($) {
771 cf::player::find $_[0] 774 cf::player::find $_[0]
772 or -f sprintf "%s/%s/%s/%s.pl", cf::localdir, cf::playerdir, ($_[0]) x 2; 775 or -f sprintf "%s/%s/%s/%s.pl", cf::localdir, cf::playerdir, ($_[0]) x 2;
773} 776}
774 777
778=item $player->ext_reply ($msgid, $msgtype, %msg)
779
780Sends an ext reply to the player.
781
782=cut
783
784sub cf::player::ext_reply($$$%) {
785 my ($self, $id, %msg) = @_;
786
787 $msg{msgid} = $id;
788
789 $self->send ("ext " . to_json \%msg);
790}
791
792=back
793
794=head3 cf::object::player
795
796=over 4
797
775=item $player->reply ($npc, $msg[, $flags]) 798=item $player_object->reply ($npc, $msg[, $flags])
776 799
777Sends a message to the player, as if the npc C<$npc> replied. C<$npc> 800Sends a message to the player, as if the npc C<$npc> replied. C<$npc>
778can be C<undef>. Does the right thing when the player is currently in a 801can be C<undef>. Does the right thing when the player is currently in a
779dialogue with the given NPC character. 802dialogue with the given NPC character.
780 803
781=cut 804=cut
782 805
783# rough implementation of a future "reply" method that works 806# rough implementation of a future "reply" method that works
784# with dialog boxes. 807# with dialog boxes.
808#TODO: the first argument must go, split into a $npc->reply_to ( method
785sub cf::object::player::reply($$$;$) { 809sub cf::object::player::reply($$$;$) {
786 my ($self, $npc, $msg, $flags) = @_; 810 my ($self, $npc, $msg, $flags) = @_;
787 811
788 $flags = cf::NDI_BROWN | cf::NDI_UNIQUE unless @_ >= 4; 812 $flags = cf::NDI_BROWN | cf::NDI_UNIQUE unless @_ >= 4;
789 813
793 $msg = $npc->name . " says: $msg" if $npc; 817 $msg = $npc->name . " says: $msg" if $npc;
794 $self->message ($msg, $flags); 818 $self->message ($msg, $flags);
795 } 819 }
796} 820}
797 821
798=item $player->ext_reply ($msgid, $msgtype, %msg) 822=item $player_object->may ("access")
799 823
800Sends an ext reply to the player. 824Returns wether the given player is authorized to access resource "access"
825(e.g. "command_wizcast").
801 826
802=cut 827=cut
803 828
804sub cf::player::ext_reply($$$%) { 829sub cf::object::player::may {
805 my ($self, $id, %msg) = @_; 830 my ($self, $access) = @_;
806 831
807 $msg{msgid} = $id; 832 $self->flag (cf::FLAG_WIZ) ||
808 833 (ref $cf::CFG{"may_$access"}
809 $self->send ("ext " . to_json \%msg); 834 ? scalar grep $self->name eq $_, @{$cf::CFG{"may_$access"}}
835 : $cf::CFG{"may_$access"})
810} 836}
837
838=head3 cf::client
839
840=over 4
841
842=item $client->send_drawinfo ($text, $flags)
843
844Sends a drawinfo packet to the client. Circumvents output buffering so
845should not be used under normal circumstances.
846
847=cut
848
849sub cf::client::send_drawinfo {
850 my ($self, $text, $flags) = @_;
851
852 utf8::encode $text;
853 $self->send_packet (sprintf "drawinfo %d %s", $flags, $text);
854}
855
856
857=item $success = $client->query ($flags, "text", \&cb)
858
859Queues a query to the client, calling the given callback with
860the reply text on a reply. flags can be C<cf::CS_QUERY_YESNO>,
861C<cf::CS_QUERY_SINGLECHAR> or C<cf::CS_QUERY_HIDEINPUT> or C<0>.
862
863Queries can fail, so check the return code. Or don't, as queries will become
864reliable at some point in the future.
865
866=cut
867
868sub cf::client::query {
869 my ($self, $flags, $text, $cb) = @_;
870
871 return unless $self->state == ST_PLAYING
872 || $self->state == ST_SETUP
873 || $self->state == ST_CUSTOM;
874
875 $self->state (ST_CUSTOM);
876
877 utf8::encode $text;
878 push @{ $self->{query_queue} }, [(sprintf "query %d %s", $flags, $text), $cb];
879
880 $self->send_packet ($self->{query_queue}[0][0])
881 if @{ $self->{query_queue} } == 1;
882}
883
884cf::client->attach (
885 on_reply => sub {
886 my ($ns, $msg) = @_;
887
888 # this weird shuffling is so that direct followup queries
889 # get handled first
890 my $queue = delete $ns->{query_queue};
891
892 (shift @$queue)->[1]->($msg);
893
894 push @{ $ns->{query_queue} }, @$queue;
895
896 if (@{ $ns->{query_queue} } == @$queue) {
897 if (@$queue) {
898 $ns->send_packet ($ns->{query_queue}[0][0]);
899 } else {
900 $ns->state (ST_PLAYING);
901 }
902 }
903 },
904);
811 905
812=back 906=back
813 907
814=cut
815
816#############################################################################
817 908
818=head2 SAFE SCRIPTING 909=head2 SAFE SCRIPTING
819 910
820Functions that provide a safe environment to compile and execute 911Functions that provide a safe environment to compile and execute
821snippets of perl code without them endangering the safety of the server 912snippets of perl code without them endangering the safety of the server
822itself. Looping constructs, I/O operators and other built-in functionality 913itself. Looping constructs, I/O operators and other built-in functionality
823is not available in the safe scripting environment, and the number of 914is not available in the safe scripting environment, and the number of
824functions and methods that cna be called is greatly reduced. 915functions and methods that can be called is greatly reduced.
825 916
826=cut 917=cut
827 918
828our $safe = new Safe "safe"; 919our $safe = new Safe "safe";
829our $safe_hole = new Safe::Hole; 920our $safe_hole = new Safe::Hole;
836 927
837=pod 928=pod
838 929
839The following fucntions and emthods are available within a safe environment: 930The following fucntions and emthods are available within a safe environment:
840 931
841 cf::object contr pay_amount pay_player 932 cf::object contr pay_amount pay_player map
842 cf::object::player player 933 cf::object::player player
843 cf::player peaceful 934 cf::player peaceful
935 cf::map trigger
844 936
845=cut 937=cut
846 938
847for ( 939for (
848 ["cf::object" => qw(contr pay_amount pay_player)], 940 ["cf::object" => qw(contr pay_amount pay_player map)],
849 ["cf::object::player" => qw(player)], 941 ["cf::object::player" => qw(player)],
850 ["cf::player" => qw(peaceful)], 942 ["cf::player" => qw(peaceful)],
943 ["cf::map" => qw(trigger)],
851) { 944) {
852 no strict 'refs'; 945 no strict 'refs';
853 my ($pkg, @funs) = @$_; 946 my ($pkg, @funs) = @$_;
854 *{"safe::$pkg\::$_"} = $safe_hole->wrap (\&{"$pkg\::$_"}) 947 *{"safe::$pkg\::$_"} = $safe_hole->wrap (\&{"$pkg\::$_"})
855 for @funs; 948 for @funs;
965 1058
966Immediately write the database to disk I<if it is dirty>. 1059Immediately write the database to disk I<if it is dirty>.
967 1060
968=cut 1061=cut
969 1062
1063our $DB;
1064
970{ 1065{
971 my $db;
972 my $path = cf::localdir . "/database.pst"; 1066 my $path = cf::localdir . "/database.pst";
973 1067
974 sub db_load() { 1068 sub db_load() {
975 warn "loading database $path\n";#d# remove later 1069 warn "loading database $path\n";#d# remove later
976 $db = stat $path ? Storable::retrieve $path : { }; 1070 $DB = stat $path ? Storable::retrieve $path : { };
977 } 1071 }
978 1072
979 my $pid; 1073 my $pid;
980 1074
981 sub db_save() { 1075 sub db_save() {
982 warn "saving database $path\n";#d# remove later 1076 warn "saving database $path\n";#d# remove later
983 waitpid $pid, 0 if $pid; 1077 waitpid $pid, 0 if $pid;
984 if (0 == ($pid = fork)) { 1078 if (0 == ($pid = fork)) {
985 $db->{_meta}{version} = 1; 1079 $DB->{_meta}{version} = 1;
986 Storable::nstore $db, "$path~"; 1080 Storable::nstore $DB, "$path~";
987 rename "$path~", $path; 1081 rename "$path~", $path;
988 cf::_exit 0 if defined $pid; 1082 cf::_exit 0 if defined $pid;
989 } 1083 }
990 } 1084 }
991 1085
994 sub db_sync() { 1088 sub db_sync() {
995 db_save if $dirty; 1089 db_save if $dirty;
996 undef $dirty; 1090 undef $dirty;
997 } 1091 }
998 1092
999 my $idle = Event->idle (min => $TICK * 2.8, max => 10, repeat => 0, cb => sub { 1093 my $idle = Event->idle (min => $TICK * 2.8, max => 10, repeat => 0, data => WF_AUTOCANCEL, cb => sub {
1000 db_sync; 1094 db_sync;
1001 }); 1095 });
1002 1096
1003 sub db_dirty() { 1097 sub db_dirty() {
1004 $dirty = 1; 1098 $dirty = 1;
1005 $idle->start; 1099 $idle->start;
1006 } 1100 }
1007 1101
1008 sub db_get($;$) { 1102 sub db_get($;$) {
1009 @_ >= 2 1103 @_ >= 2
1010 ? $db->{$_[0]}{$_[1]} 1104 ? $DB->{$_[0]}{$_[1]}
1011 : ($db->{$_[0]} ||= { }) 1105 : ($DB->{$_[0]} ||= { })
1012 } 1106 }
1013 1107
1014 sub db_put($$;$) { 1108 sub db_put($$;$) {
1015 if (@_ >= 3) { 1109 if (@_ >= 3) {
1016 $db->{$_[0]}{$_[1]} = $_[2]; 1110 $DB->{$_[0]}{$_[1]} = $_[2];
1017 } else { 1111 } else {
1018 $db->{$_[0]} = $_[1]; 1112 $DB->{$_[0]} = $_[1];
1019 } 1113 }
1020 db_dirty; 1114 db_dirty;
1021 } 1115 }
1022 1116
1023 attach_global 1117 cf::global->attach (
1024 prio => 10000, 1118 prio => 10000,
1025 on_cleanup => sub { 1119 on_cleanup => sub {
1026 db_sync; 1120 db_sync;
1027 }, 1121 },
1028 ; 1122 );
1029} 1123}
1030 1124
1031############################################################################# 1125#############################################################################
1032# the server's main() 1126# the server's main()
1033 1127
1035 open my $fh, "<:utf8", cf::confdir . "/config" 1129 open my $fh, "<:utf8", cf::confdir . "/config"
1036 or return; 1130 or return;
1037 1131
1038 local $/; 1132 local $/;
1039 *CFG = YAML::Syck::Load <$fh>; 1133 *CFG = YAML::Syck::Load <$fh>;
1040
1041 use Data::Dumper; warn Dumper \%CFG;
1042} 1134}
1043 1135
1044sub main { 1136sub main {
1045 cfg_load; 1137 cfg_load;
1046 db_load; 1138 db_load;
1056 1148
1057 $msg->("reloading..."); 1149 $msg->("reloading...");
1058 1150
1059 eval { 1151 eval {
1060 # cancel all watchers 1152 # cancel all watchers
1061 $_->cancel for Event::all_watchers; 1153 for (Event::all_watchers) {
1154 $_->cancel if $_->data & WF_AUTOCANCEL;
1155 }
1062 1156
1063 # unload all extensions 1157 # unload all extensions
1064 for (@exts) { 1158 for (@exts) {
1065 $msg->("unloading <$_>"); 1159 $msg->("unloading <$_>");
1066 unload_extension $_; 1160 unload_extension $_;
1126 warn $_[0]; 1220 warn $_[0];
1127 print "$_[0]\n"; 1221 print "$_[0]\n";
1128 }; 1222 };
1129} 1223}
1130 1224
1225register "<global>", __PACKAGE__;
1226
1131register_command "perl-reload", 0, sub { 1227register_command "perl-reload" => sub {
1132 my ($who, $arg) = @_; 1228 my ($who, $arg) = @_;
1133 1229
1134 if ($who->flag (FLAG_WIZ)) { 1230 if ($who->flag (FLAG_WIZ)) {
1135 _perl_reload { 1231 _perl_reload {
1136 warn $_[0]; 1232 warn $_[0];
1137 $who->message ($_[0]); 1233 $who->message ($_[0]);
1138 }; 1234 };
1139 } 1235 }
1140}; 1236};
1141 1237
1142register "<global>", __PACKAGE__;
1143
1144unshift @INC, $LIBDIR; 1238unshift @INC, $LIBDIR;
1145 1239
1146$TICK_WATCHER = Event->timer ( 1240$TICK_WATCHER = Event->timer (
1147 prio => 1, 1241 prio => 0,
1148 at => $NEXT_TICK || 1, 1242 at => $NEXT_TICK || 1,
1243 data => WF_AUTOCANCEL,
1149 cb => sub { 1244 cb => sub {
1150 cf::server_tick; # one server iteration 1245 cf::server_tick; # one server iteration
1151 1246
1152 my $NOW = Event::time; 1247 my $NOW = Event::time;
1153 $NEXT_TICK += $TICK; 1248 $NEXT_TICK += $TICK;
1154 1249
1155 # if we are delayed by four ticks, skip them all 1250 # if we are delayed by four ticks or more, skip them all
1156 $NEXT_TICK = $NOW if $NOW >= $NEXT_TICK + $TICK * 4; 1251 $NEXT_TICK = $NOW if $NOW >= $NEXT_TICK + $TICK * 4;
1157 1252
1158 $TICK_WATCHER->at ($NEXT_TICK); 1253 $TICK_WATCHER->at ($NEXT_TICK);
1159 $TICK_WATCHER->start; 1254 $TICK_WATCHER->start;
1160 }, 1255 },
1161); 1256);
1162 1257
1258IO::AIO::max_poll_time $TICK * 0.2;
1259
1260Event->io (fd => IO::AIO::poll_fileno,
1261 poll => 'r',
1262 prio => 5,
1263 data => WF_AUTOCANCEL,
1264 cb => \&IO::AIO::poll_cb);
1265
11631 12661
1164 1267

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines