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.120 by root, Tue Jan 2 11:08:36 2007 UTC vs.
Revision 1.131 by root, Thu Jan 4 00:53:54 2007 UTC

8use Storable; 8use Storable;
9use Opcode; 9use Opcode;
10use Safe; 10use Safe;
11use Safe::Hole; 11use Safe::Hole;
12 12
13use Coro 3.3; 13use Coro 3.3 ();
14use Coro::Event; 14use Coro::Event;
15use Coro::Timer; 15use Coro::Timer;
16use Coro::Signal; 16use Coro::Signal;
17use Coro::Semaphore; 17use Coro::Semaphore;
18use Coro::AIO; 18use Coro::AIO;
71mkdir cf::localdir . "/" . cf::uniquedir; 71mkdir cf::localdir . "/" . cf::uniquedir;
72mkdir $RANDOM_MAPS; 72mkdir $RANDOM_MAPS;
73 73
74# a special map that is always available 74# a special map that is always available
75our $LINK_MAP; 75our $LINK_MAP;
76 76our $EMERGENCY_POSITION;
77our $EMERGENCY_POSITION = $cf::CFG{emergency_position} || ["/world/world_105_115", 5, 37];
78 77
79############################################################################# 78#############################################################################
80 79
81=head2 GLOBAL VARIABLES 80=head2 GLOBAL VARIABLES
82 81
245 # wake up all waiters, to be on the safe side 244 # wake up all waiters, to be on the safe side
246 $_->ready for @{ delete $LOCK{$key} }; 245 $_->ready for @{ delete $LOCK{$key} };
247 } 246 }
248} 247}
249 248
249=item cf::async { BLOCK }
250
251Like C<Coro::async>, but runs the given BLOCK in an eval and only logs the
252error instead of exiting the server in case of a problem.
253
254=cut
255
256sub async(&) {
257 my ($cb) = @_;
258
259 Coro::async {
260 eval { $cb->() };
261 warn $@ if $@;
262 }
263}
264
250=item cf::sync_job { BLOCK } 265=item cf::sync_job { BLOCK }
251 266
252The design of crossfire+ requires that the main coro ($Coro::main) is 267The design of crossfire+ requires that the main coro ($Coro::main) is
253always able to handle events or runnable, as crossfire+ is only partly 268always able to handle events or runnable, as crossfire+ is only partly
254reentrant. Thus "blocking" it by e.g. waiting for I/O is not acceptable. 269reentrant. Thus "blocking" it by e.g. waiting for I/O is not acceptable.
301=cut 316=cut
302 317
303sub coro(&) { 318sub coro(&) {
304 my $cb = shift; 319 my $cb = shift;
305 320
306 my $coro; $coro = async { 321 my $coro = &cf::async ($cb);
307 eval {
308 $cb->();
309 };
310 warn $@ if $@;
311 };
312 322
313 $coro->on_destroy (sub { 323 $coro->on_destroy (sub {
314 delete $EXT_CORO{$coro+0}; 324 delete $EXT_CORO{$coro+0};
315 }); 325 });
316 $EXT_CORO{$coro+0} = $coro; 326 $EXT_CORO{$coro+0} = $coro;
1153# and all this just because we cannot iterate over 1163# and all this just because we cannot iterate over
1154# all maps in C++... 1164# all maps in C++...
1155sub change_all_map_light { 1165sub change_all_map_light {
1156 my ($change) = @_; 1166 my ($change) = @_;
1157 1167
1158 $_->change_map_light ($change) for values %cf::MAP; 1168 $_->change_map_light ($change)
1169 for grep $_->outdoor, values %cf::MAP;
1159} 1170}
1160 1171
1161sub try_load_header($) { 1172sub try_load_header($) {
1162 my ($path) = @_; 1173 my ($path) = @_;
1163 1174
1174 $map->{load_path} = $path; 1185 $map->{load_path} = $path;
1175 1186
1176 $map 1187 $map
1177} 1188}
1178 1189
1190sub find_map;
1179sub find_map { 1191sub find_map {
1180 my ($path, $origin) = @_; 1192 my ($path, $origin) = @_;
1181 1193
1182 #warn "find_map<$path,$origin>\n";#d# 1194 #warn "find_map<$path,$origin>\n";#d#
1183 1195
1210 $map->instantiate; 1222 $map->instantiate;
1211 1223
1212 # per-player maps become, after loading, normal maps 1224 # per-player maps become, after loading, normal maps
1213 $map->per_player (0) if $path->{user_rel}; 1225 $map->per_player (0) if $path->{user_rel};
1214 } 1226 }
1215 #Coro::Timer::sleep 1;#d#
1216 1227
1217 $map->path ($key); 1228 $map->path ($key);
1218 $map->{path} = $path; 1229 $map->{path} = $path;
1219 $map->{last_save} = $cf::RUNTIME; 1230 $map->{last_save} = $cf::RUNTIME;
1220 $map->last_access ($cf::RUNTIME); 1231 $map->last_access ($cf::RUNTIME);
1221 1232
1222 if ($map->should_reset) { 1233 if ($map->should_reset) {
1223 $map->reset; 1234 $map->reset;
1235 undef $guard;
1224 $map = find_map $path; 1236 $map = find_map $path
1237 or return;
1225 } 1238 }
1226 1239
1227 $cf::MAP{$key} = $map 1240 $cf::MAP{$key} = $map
1228 } 1241 }
1229} 1242}
1308} 1321}
1309 1322
1310sub swap_out { 1323sub swap_out {
1311 my ($self) = @_; 1324 my ($self) = @_;
1312 1325
1326 # save first because save cedes
1327 $self->save;
1328
1313 return if $self->players; 1329 return if $self->players;
1314 return if $self->in_memory != cf::MAP_IN_MEMORY; 1330 return if $self->in_memory != cf::MAP_IN_MEMORY;
1315 return if $self->{deny_save}; 1331 return if $self->{deny_save};
1316 1332
1317 $self->save;
1318 $self->clear; 1333 $self->clear;
1319 $self->in_memory (cf::MAP_SWAPPED); 1334 $self->in_memory (cf::MAP_SWAPPED);
1320} 1335}
1321 1336
1322sub reset_at { 1337sub reset_at {
1535 1550
1536 # try to abort aborted map switching on player login :) 1551 # try to abort aborted map switching on player login :)
1537 # should happen only on crashes 1552 # should happen only on crashes
1538 if ($pl->ob->{_link_pos}) { 1553 if ($pl->ob->{_link_pos}) {
1539 $pl->ob->enter_link; 1554 $pl->ob->enter_link;
1540 Coro::async { 1555 cf::async {
1541 # we need this sleep as the login has a concurrent enter_exit running 1556 # we need this sleep as the login has a concurrent enter_exit running
1542 # and this sleep increases chances of the player not ending up in scorn 1557 # and this sleep increases chances of the player not ending up in scorn
1543 Coro::Timer::sleep 1; 1558 Coro::Timer::sleep 1;
1544 $pl->ob->leave_link; 1559 $pl->ob->leave_link;
1545 }; 1560 };
1554sub cf::object::player::goto_map { 1569sub cf::object::player::goto_map {
1555 my ($self, $path, $x, $y) = @_; 1570 my ($self, $path, $x, $y) = @_;
1556 1571
1557 $self->enter_link; 1572 $self->enter_link;
1558 1573
1559 (Coro::async { 1574 (cf::async {
1560 $path = new cf::path $path; 1575 $path = new cf::path $path;
1561 1576
1562 my $map = cf::map::find_map $path->as_string; 1577 my $map = cf::map::find_map $path->as_string;
1563 $map = $map->customise_for ($self) if $map; 1578 $map = $map->customise_for ($self) if $map;
1564 1579
1626 1641
1627 return unless $self->type == cf::PLAYER; 1642 return unless $self->type == cf::PLAYER;
1628 1643
1629 $self->enter_link; 1644 $self->enter_link;
1630 1645
1631 (Coro::async { 1646 (cf::async {
1632 unless (eval { 1647 unless (eval {
1633
1634 prepare_random_map $exit 1648 prepare_random_map $exit
1635 if $exit->slaying eq "/!"; 1649 if $exit->slaying eq "/!";
1636 1650
1637 my $path = new cf::path $exit->slaying, $exit->map && $exit->map->path; 1651 my $path = new cf::path $exit->slaying, $exit->map && $exit->map->path;
1638 $self->goto_map ($path, $exit->stats->hp, $exit->stats->sp); 1652 $self->goto_map ($path, $exit->stats->hp, $exit->stats->sp);
1700 on_reply => sub { 1714 on_reply => sub {
1701 my ($ns, $msg) = @_; 1715 my ($ns, $msg) = @_;
1702 1716
1703 # this weird shuffling is so that direct followup queries 1717 # this weird shuffling is so that direct followup queries
1704 # get handled first 1718 # get handled first
1705 my $queue = delete $ns->{query_queue}; 1719 my $queue = delete $ns->{query_queue}
1720 or return; # be conservative, not sure how that can happen, but we saw a crash here
1706 1721
1707 (shift @$queue)->[1]->($msg); 1722 (shift @$queue)->[1]->($msg);
1708 1723
1709 push @{ $ns->{query_queue} }, @$queue; 1724 push @{ $ns->{query_queue} }, @$queue;
1710 1725
1727=cut 1742=cut
1728 1743
1729sub cf::client::coro { 1744sub cf::client::coro {
1730 my ($self, $cb) = @_; 1745 my ($self, $cb) = @_;
1731 1746
1732 my $coro; $coro = async { 1747 my $coro = &cf::async ($cb);
1733 eval {
1734 $cb->();
1735 };
1736 warn $@ if $@;
1737 };
1738 1748
1739 $coro->on_destroy (sub { 1749 $coro->on_destroy (sub {
1740 delete $self->{_coro}{$coro+0}; 1750 delete $self->{_coro}{$coro+0};
1741 }); 1751 });
1742 1752
1914 1924
1915{ 1925{
1916 my $path = cf::localdir . "/database.pst"; 1926 my $path = cf::localdir . "/database.pst";
1917 1927
1918 sub db_load() { 1928 sub db_load() {
1919 warn "loading database $path\n";#d# remove later
1920 $DB = stat $path ? Storable::retrieve $path : { }; 1929 $DB = stat $path ? Storable::retrieve $path : { };
1921 } 1930 }
1922 1931
1923 my $pid; 1932 my $pid;
1924 1933
1925 sub db_save() { 1934 sub db_save() {
1926 warn "saving database $path\n";#d# remove later
1927 waitpid $pid, 0 if $pid; 1935 waitpid $pid, 0 if $pid;
1928 if (0 == ($pid = fork)) { 1936 if (0 == ($pid = fork)) {
1929 $DB->{_meta}{version} = 1; 1937 $DB->{_meta}{version} = 1;
1930 Storable::nstore $DB, "$path~"; 1938 Storable::nstore $DB, "$path~";
1931 rename "$path~", $path; 1939 rename "$path~", $path;
1979 open my $fh, "<:utf8", cf::confdir . "/config" 1987 open my $fh, "<:utf8", cf::confdir . "/config"
1980 or return; 1988 or return;
1981 1989
1982 local $/; 1990 local $/;
1983 *CFG = YAML::Syck::Load <$fh>; 1991 *CFG = YAML::Syck::Load <$fh>;
1992
1993 $EMERGENCY_POSITION = $CFG{emergency_position} || ["/world/world_105_115", 5, 37];
1994
1995 if (exists $CFG{mlockall}) {
1996 eval {
1997 $CFG{mlockall} ? &mlockall : &munlockall
1998 and die "WARNING: m(un)lockall failed: $!\n";
1999 };
2000 warn $@ if $@;
2001 }
1984} 2002}
1985 2003
1986sub main { 2004sub main {
1987 # we must not ever block the main coroutine 2005 # we must not ever block the main coroutine
1988 local $Coro::idle = sub { 2006 local $Coro::idle = sub {
2191 or warn "ERROR: unable to write runtime file: $!"; 2209 or warn "ERROR: unable to write runtime file: $!";
2192 })->(); 2210 })->();
2193 }, 2211 },
2194); 2212);
2195 2213
2214END { cf::emergency_save }
2215
21961 22161
2197 2217

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines