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

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines