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.116 by root, Mon Jan 1 17:30:34 2007 UTC vs.
Revision 1.138 by root, Fri Jan 5 17:07:17 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;
49our $UPTIME; $UPTIME ||= time; 49our $UPTIME; $UPTIME ||= time;
50our $RUNTIME; 50our $RUNTIME;
51 51
52our %MAP; # all maps 52our %MAP; # all maps
53our $LINK_MAP; # the special {link} map 53our $LINK_MAP; # the special {link} map
54our $FREEZE;
55our $RANDOM_MAPS = cf::localdir . "/random"; 54our $RANDOM_MAPS = cf::localdir . "/random";
56our %EXT_CORO; 55our %EXT_CORO;
57 56
58binmode STDOUT; 57binmode STDOUT;
59binmode STDERR; 58binmode STDERR;
71mkdir cf::localdir . "/" . cf::uniquedir; 70mkdir cf::localdir . "/" . cf::uniquedir;
72mkdir $RANDOM_MAPS; 71mkdir $RANDOM_MAPS;
73 72
74# a special map that is always available 73# a special map that is always available
75our $LINK_MAP; 74our $LINK_MAP;
76 75our $EMERGENCY_POSITION;
77our $EMERGENCY_POSITION = $cf::CFG{emergency_position} || ["/world/world_105_115", 5, 37];
78 76
79############################################################################# 77#############################################################################
80 78
81=head2 GLOBAL VARIABLES 79=head2 GLOBAL VARIABLES
82 80
181sub to_json($) { 179sub to_json($) {
182 $JSON::Syck::ImplicitUnicode = 0; # work around JSON::Syck bugs 180 $JSON::Syck::ImplicitUnicode = 0; # work around JSON::Syck bugs
183 JSON::Syck::Dump $_[0] 181 JSON::Syck::Dump $_[0]
184} 182}
185 183
184=item cf::lock_wait $string
185
186Wait until the given lock is available. See cf::lock_acquire.
187
188=item my $lock = cf::lock_acquire $string
189
190Wait until the given lock is available and then acquires it and returns
191a Coro::guard object. If the guard object gets destroyed (goes out of scope,
192for example when the coroutine gets canceled), the lock is automatically
193returned.
194
195Lock names should begin with a unique identifier (for example, cf::map::find
196uses map_find and cf::map::load uses map_load).
197
198=cut
199
200our %LOCK;
201
202sub lock_wait($) {
203 my ($key) = @_;
204
205 # wait for lock, if any
206 while ($LOCK{$key}) {
207 push @{ $LOCK{$key} }, $Coro::current;
208 Coro::schedule;
209 }
210}
211
212sub lock_acquire($) {
213 my ($key) = @_;
214
215 # wait, to be sure we are not locked
216 lock_wait $key;
217
218 $LOCK{$key} = [];
219
220 Coro::guard {
221 # wake up all waiters, to be on the safe side
222 $_->ready for @{ delete $LOCK{$key} };
223 }
224}
225
226sub freeze_mainloop {
227 return unless $TICK_WATCHER->is_active;
228
229 my $guard = Coro::guard { $TICK_WATCHER->start };
230 $TICK_WATCHER->stop;
231 $guard
232}
233
186=item cf::sync_job { BLOCK } 234=item cf::sync_job { BLOCK }
187 235
188The design of crossfire+ requires that the main coro ($Coro::main) is 236The design of crossfire+ requires that the main coro ($Coro::main) is
189always able to handle events or runnable, as crossfire+ is only partly 237always able to handle events or runnable, as crossfire+ is only partly
190reentrant. Thus "blocking" it by e.g. waiting for I/O is not acceptable. 238reentrant. Thus "blocking" it by e.g. waiting for I/O is not acceptable.
203 # this is the main coro, too bad, we have to block 251 # this is the main coro, too bad, we have to block
204 # till the operation succeeds, freezing the server :/ 252 # till the operation succeeds, freezing the server :/
205 253
206 # TODO: use suspend/resume instead 254 # TODO: use suspend/resume instead
207 # (but this is cancel-safe) 255 # (but this is cancel-safe)
208 local $FREEZE = 1; 256 my $freeze_guard = freeze_mainloop;
209 257
210 my $busy = 1; 258 my $busy = 1;
211 my @res; 259 my @res;
212 260
213 (Coro::async { 261 (Coro::async_pool {
214 @res = eval { $job->() }; 262 @res = eval { $job->() };
215 warn $@ if $@; 263 warn $@ if $@;
216 undef $busy; 264 undef $busy;
217 })->prio (Coro::PRIO_MAX); 265 })->prio (Coro::PRIO_MAX);
218 266
229 } 277 }
230} 278}
231 279
232=item $coro = cf::coro { BLOCK } 280=item $coro = cf::coro { BLOCK }
233 281
234Creates and returns a new coro. This coro is automcatially being canceled 282Creates (and readies) and returns a new coro. This coro is automcatially
235when the extension calling this is being unloaded. 283being canceled when the extension calling this is being unloaded.
236 284
237=cut 285=cut
238 286
239sub coro(&) { 287sub coro(&) {
240 my $cb = shift; 288 my $cb = shift;
241 289
242 my $coro; $coro = async { 290 my $coro = &Coro::async_pool ($cb);
243 eval {
244 $cb->();
245 };
246 warn $@ if $@;
247 };
248 291
249 $coro->on_destroy (sub { 292 $coro->on_destroy (sub {
250 delete $EXT_CORO{$coro+0}; 293 delete $EXT_CORO{$coro+0};
251 }); 294 });
252 $EXT_CORO{$coro+0} = $coro; 295 $EXT_CORO{$coro+0} = $coro;
817 (aio_load "$filename.pst", $av) >= 0 860 (aio_load "$filename.pst", $av) >= 0
818 or return; 861 or return;
819 $av = eval { (Storable::thaw $av)->{objs} }; 862 $av = eval { (Storable::thaw $av)->{objs} };
820 } 863 }
821 864
865 warn sprintf "loading %s (%d)\n",
866 $filename, length $data, scalar @{$av || []};#d#
822 return ($data, $av); 867 return ($data, $av);
823} 868}
824 869
825############################################################################# 870#############################################################################
826# command handling &c 871# command handling &c
1060package cf::map; 1105package cf::map;
1061 1106
1062use Fcntl; 1107use Fcntl;
1063use Coro::AIO; 1108use Coro::AIO;
1064 1109
1065our $MAX_RESET = 7200; 1110our $MAX_RESET = 3600;
1066our $DEFAULT_RESET = 3600; 1111our $DEFAULT_RESET = 3000;
1067 1112
1068sub generate_random_map { 1113sub generate_random_map {
1069 my ($path, $rmp) = @_; 1114 my ($path, $rmp) = @_;
1070 1115
1071 # mit "rum" bekleckern, nicht 1116 # mit "rum" bekleckern, nicht
1087# and all this just because we cannot iterate over 1132# and all this just because we cannot iterate over
1088# all maps in C++... 1133# all maps in C++...
1089sub change_all_map_light { 1134sub change_all_map_light {
1090 my ($change) = @_; 1135 my ($change) = @_;
1091 1136
1092 $_->change_map_light ($change) for values %cf::MAP; 1137 $_->change_map_light ($change)
1138 for grep $_->outdoor, values %cf::MAP;
1093} 1139}
1094 1140
1095sub try_load_header($) { 1141sub try_load_header($) {
1096 my ($path) = @_; 1142 my ($path) = @_;
1097 1143
1100 or return; 1146 or return;
1101 1147
1102 my $map = cf::map::new 1148 my $map = cf::map::new
1103 or return; 1149 or return;
1104 1150
1151 # for better error messages only, will be overwritten
1152 $map->path ($path);
1153
1105 $map->load_header ($path) 1154 $map->load_header ($path)
1106 or return; 1155 or return;
1107 1156
1108 $map->{load_path} = $path; 1157 $map->{load_path} = $path;
1109 1158
1110 $map 1159 $map
1111} 1160}
1112 1161
1162sub find;
1113sub find_map { 1163sub find {
1114 my ($path, $origin) = @_; 1164 my ($path, $origin) = @_;
1115 1165
1116 #warn "find_map<$path,$origin>\n";#d# 1166 #warn "find<$path,$origin>\n";#d#
1117 1167
1118 $path = new cf::path $path, $origin && $origin->path; 1168 $path = new cf::path $path, $origin && $origin->path;
1119 my $key = $path->as_string; 1169 my $key = $path->as_string;
1120 1170
1171 cf::lock_wait "map_find:$key";
1172
1121 $cf::MAP{$key} || do { 1173 $cf::MAP{$key} || do {
1174 my $guard = cf::lock_acquire "map_find:$key";
1175
1122 # do it the slow way 1176 # do it the slow way
1123 my $map = try_load_header $path->save_path; 1177 my $map = try_load_header $path->save_path;
1124 1178
1179 Coro::cede;
1180
1125 if ($map) { 1181 if ($map) {
1182 $map->last_access ((delete $map->{last_access})
1183 || $cf::RUNTIME); #d#
1126 # safety 1184 # safety
1127 $map->{instantiate_time} = $cf::RUNTIME 1185 $map->{instantiate_time} = $cf::RUNTIME
1128 if $map->{instantiate_time} > $cf::RUNTIME; 1186 if $map->{instantiate_time} > $cf::RUNTIME;
1129 } else { 1187 } else {
1130 if (my $rmp = $path->random_map_params) { 1188 if (my $rmp = $path->random_map_params) {
1135 1193
1136 $map or return; 1194 $map or return;
1137 1195
1138 $map->{load_original} = 1; 1196 $map->{load_original} = 1;
1139 $map->{instantiate_time} = $cf::RUNTIME; 1197 $map->{instantiate_time} = $cf::RUNTIME;
1198 $map->last_access ($cf::RUNTIME);
1140 $map->instantiate; 1199 $map->instantiate;
1141 1200
1142 # per-player maps become, after loading, normal maps 1201 # per-player maps become, after loading, normal maps
1143 $map->per_player (0) if $path->{user_rel}; 1202 $map->per_player (0) if $path->{user_rel};
1144 } 1203 }
1145 1204
1146 $map->path ($key); 1205 $map->path ($key);
1147 $map->{path} = $path; 1206 $map->{path} = $path;
1148 $map->{last_save} = $cf::RUNTIME; 1207 $map->{last_save} = $cf::RUNTIME;
1149 $map->last_access ($cf::RUNTIME); 1208
1209 Coro::cede;
1150 1210
1151 if ($map->should_reset) { 1211 if ($map->should_reset) {
1152 $map->reset; 1212 $map->reset;
1213 undef $guard;
1153 $map = find_map $path; 1214 $map = find $path
1215 or return;
1154 } 1216 }
1155 1217
1156 $cf::MAP{$key} = $map 1218 $cf::MAP{$key} = $map
1157 } 1219 }
1158} 1220}
1159 1221
1160sub load { 1222sub load {
1161 my ($self) = @_; 1223 my ($self) = @_;
1162 1224
1225 my $path = $self->{path};
1226 my $guard = cf::lock_acquire "map_load:" . $path->as_string;
1227
1163 return if $self->in_memory != cf::MAP_SWAPPED; 1228 return if $self->in_memory != cf::MAP_SWAPPED;
1164 1229
1165 $self->in_memory (cf::MAP_LOADING); 1230 $self->in_memory (cf::MAP_LOADING);
1166
1167 my $path = $self->{path};
1168 1231
1169 $self->alloc; 1232 $self->alloc;
1170 $self->load_objects ($self->{load_path}, 1) 1233 $self->load_objects ($self->{load_path}, 1)
1171 or return; 1234 or return;
1172 1235
1178 if (aio_open $uniq, O_RDONLY, 0) { 1241 if (aio_open $uniq, O_RDONLY, 0) {
1179 $self->clear_unique_items; 1242 $self->clear_unique_items;
1180 $self->load_objects ($uniq, 0); 1243 $self->load_objects ($uniq, 0);
1181 } 1244 }
1182 } 1245 }
1246
1247 Coro::cede;
1183 1248
1184 # now do the right thing for maps 1249 # now do the right thing for maps
1185 $self->link_multipart_objects; 1250 $self->link_multipart_objects;
1186 1251
1187 if ($self->{path}->is_style_map) { 1252 if ($self->{path}->is_style_map) {
1195 $self->difficulty ($self->estimate_difficulty) 1260 $self->difficulty ($self->estimate_difficulty)
1196 unless $self->difficulty; 1261 unless $self->difficulty;
1197 $self->activate; 1262 $self->activate;
1198 } 1263 }
1199 1264
1265 Coro::cede;
1266
1200 $self->in_memory (cf::MAP_IN_MEMORY); 1267 $self->in_memory (cf::MAP_IN_MEMORY);
1201} 1268}
1202 1269
1203sub load_map_sync { 1270sub find_sync {
1204 my ($path, $origin) = @_; 1271 my ($path, $origin) = @_;
1205 1272
1206 #warn "load_map_sync<$path, $origin>\n";#d# 1273 cf::sync_job { cf::map::find $path, $origin }
1274}
1207 1275
1208 cf::sync_job { 1276sub do_load_sync {
1209 my $map = cf::map::find_map $path, $origin 1277 my ($map) = @_;
1210 or return; 1278
1211 $map->load; 1279 cf::sync_job { $map->load };
1212 $map
1213 }
1214} 1280}
1215 1281
1216sub save { 1282sub save {
1217 my ($self) = @_; 1283 my ($self) = @_;
1218 1284
1285 my $lock = cf::lock_acquire "map_data:" . $self->path;
1286
1287 $self->{last_save} = $cf::RUNTIME;
1288
1289 return unless $self->dirty;
1290
1219 my $save = $self->{path}->save_path; utf8::encode $save; 1291 my $save = $self->{path}->save_path; utf8::encode $save;
1220 my $uniq = $self->{path}->uniq_path; utf8::encode $uniq; 1292 my $uniq = $self->{path}->uniq_path; utf8::encode $uniq;
1221 1293
1222 $self->{last_save} = $cf::RUNTIME;
1223
1224 return unless $self->dirty;
1225
1226 $self->{load_path} = $save; 1294 $self->{load_path} = $save;
1227 1295
1228 return if $self->{deny_save}; 1296 return if $self->{deny_save};
1297
1298 local $self->{last_access} = $self->last_access;#d#
1229 1299
1230 if ($uniq) { 1300 if ($uniq) {
1231 $self->save_objects ($save, cf::IO_HEADER | cf::IO_OBJECTS); 1301 $self->save_objects ($save, cf::IO_HEADER | cf::IO_OBJECTS);
1232 $self->save_objects ($uniq, cf::IO_UNIQUES); 1302 $self->save_objects ($uniq, cf::IO_UNIQUES);
1233 } else { 1303 } else {
1236} 1306}
1237 1307
1238sub swap_out { 1308sub swap_out {
1239 my ($self) = @_; 1309 my ($self) = @_;
1240 1310
1311 # save first because save cedes
1312 $self->save;
1313
1314 my $lock = cf::lock_acquire "map_data:" . $self->path;
1315
1241 return if $self->players; 1316 return if $self->players;
1242 return if $self->in_memory != cf::MAP_IN_MEMORY; 1317 return if $self->in_memory != cf::MAP_IN_MEMORY;
1243 return if $self->{deny_save}; 1318 return if $self->{deny_save};
1244 1319
1245 $self->save;
1246 $self->clear; 1320 $self->clear;
1247 $self->in_memory (cf::MAP_SWAPPED); 1321 $self->in_memory (cf::MAP_SWAPPED);
1248} 1322}
1249 1323
1250sub reset_at { 1324sub reset_at {
1288} 1362}
1289 1363
1290sub reset { 1364sub reset {
1291 my ($self) = @_; 1365 my ($self) = @_;
1292 1366
1367 my $lock = cf::lock_acquire "map_data:" . $self->path;
1368
1293 return if $self->players; 1369 return if $self->players;
1294 return if $self->{path}{user_rel};#d# 1370 return if $self->{path}{user_rel};#d#
1295 1371
1296 warn "resetting map ", $self->path;#d# 1372 warn "resetting map ", $self->path;#d#
1297 1373
1316 1392
1317sub customise_for { 1393sub customise_for {
1318 my ($map, $ob) = @_; 1394 my ($map, $ob) = @_;
1319 1395
1320 if ($map->per_player) { 1396 if ($map->per_player) {
1321 return cf::map::find_map "~" . $ob->name . "/" . $map->{path}{path}; 1397 return cf::map::find "~" . $ob->name . "/" . $map->{path}{path};
1322 } 1398 }
1323 1399
1324 $map 1400 $map
1325} 1401}
1326 1402
1327sub emergency_save { 1403sub emergency_save {
1328 local $cf::FREEZE = 1; 1404 my $freeze_guard = cf::freeze_mainloop;
1329 1405
1330 warn "enter emergency map save\n"; 1406 warn "enter emergency map save\n";
1331 1407
1332 cf::sync_job { 1408 cf::sync_job {
1333 warn "begin emergency map save\n"; 1409 warn "begin emergency map save\n";
1405=cut 1481=cut
1406 1482
1407sub cf::object::player::enter_link { 1483sub cf::object::player::enter_link {
1408 my ($self) = @_; 1484 my ($self) = @_;
1409 1485
1486 $self->deactivate_recursive;
1487
1410 return if $self->map == $LINK_MAP; 1488 return if $self->map == $LINK_MAP;
1411 1489
1412 $self->{_link_pos} = [$self->map->{path}, $self->x, $self->y] 1490 $self->{_link_pos} ||= [$self->map->{path}, $self->x, $self->y]
1413 if $self->map; 1491 if $self->map;
1414 1492
1415 $self->enter_map ($LINK_MAP, 20, 20); 1493 $self->enter_map ($LINK_MAP, 20, 20);
1416 $self->deactivate_recursive;
1417} 1494}
1418 1495
1419sub cf::object::player::leave_link { 1496sub cf::object::player::leave_link {
1420 my ($self, $map, $x, $y) = @_; 1497 my ($self, $map, $x, $y) = @_;
1421 1498
1422 my $link_pos = delete $self->{_link_pos}; 1499 my $link_pos = delete $self->{_link_pos};
1423 1500
1424 unless ($map) { 1501 unless ($map) {
1425 # restore original map position 1502 # restore original map position
1426 ($map, $x, $y) = @{ $link_pos || [] }; 1503 ($map, $x, $y) = @{ $link_pos || [] };
1427 $map = cf::map::find_map $map; 1504 $map = cf::map::find $map;
1428 1505
1429 unless ($map) { 1506 unless ($map) {
1430 ($map, $x, $y) = @$EMERGENCY_POSITION; 1507 ($map, $x, $y) = @$EMERGENCY_POSITION;
1431 $map = cf::map::find_map $map 1508 $map = cf::map::find $map
1432 or die "FATAL: cannot load emergency map\n"; 1509 or die "FATAL: cannot load emergency map\n";
1433 } 1510 }
1434 } 1511 }
1435 1512
1436 ($x, $y) = (-1, -1) 1513 ($x, $y) = (-1, -1)
1444 1521
1445 $self->activate_recursive; 1522 $self->activate_recursive;
1446 $self->enter_map ($map, $x, $y); 1523 $self->enter_map ($map, $x, $y);
1447} 1524}
1448 1525
1526cf::player->attach (
1527 on_logout => sub {
1528 my ($pl) = @_;
1529
1530 # abort map switching before logout
1531 if ($pl->ob->{_link_pos}) {
1532 cf::sync_job {
1533 $pl->ob->leave_link
1534 };
1535 }
1536 },
1537 on_login => sub {
1538 my ($pl) = @_;
1539
1540 # try to abort aborted map switching on player login :)
1541 # should happen only on crashes
1542 if ($pl->ob->{_link_pos}) {
1543 $pl->ob->enter_link;
1544 Coro::async_pool {
1545 # we need this sleep as the login has a concurrent enter_exit running
1546 # and this sleep increases chances of the player not ending up in scorn
1547 Coro::Timer::sleep 1;
1548 $pl->ob->leave_link;
1549 };
1550 }
1551 },
1552);
1553
1449=item $player_object->goto_map ($map, $x, $y) 1554=item $player_object->goto ($path, $x, $y)
1450 1555
1451=cut 1556=cut
1452 1557
1453sub cf::object::player::goto_map { 1558sub cf::object::player::goto {
1454 my ($self, $path, $x, $y) = @_; 1559 my ($self, $path, $x, $y) = @_;
1455 1560
1456 $self->enter_link; 1561 $self->enter_link;
1457 1562
1458 (Coro::async { 1563 (Coro::async_pool {
1459 $path = new cf::path $path; 1564 $path = new cf::path $path;
1460 1565
1461 my $map = cf::map::find_map $path->as_string; 1566 my $map = cf::map::find $path->as_string;
1462 $map = $map->customise_for ($self) if $map; 1567 $map = $map->customise_for ($self) if $map;
1463 1568
1464 warn "entering ", $map->path, " at ($x, $y)\n" 1569# warn "entering ", $map->path, " at ($x, $y)\n"
1465 if $map; 1570# if $map;
1466 1571
1467 $map or $self->message ("The exit is closed", cf::NDI_UNIQUE | cf::NDI_RED); 1572 $map or $self->message ("The exit is closed", cf::NDI_UNIQUE | cf::NDI_RED);
1468 1573
1469 $self->leave_link ($map, $x, $y); 1574 $self->leave_link ($map, $x, $y);
1470 })->prio (1); 1575 })->prio (1);
1525 1630
1526 return unless $self->type == cf::PLAYER; 1631 return unless $self->type == cf::PLAYER;
1527 1632
1528 $self->enter_link; 1633 $self->enter_link;
1529 1634
1530 (Coro::async { 1635 (Coro::async_pool {
1636 $self->deactivate_recursive; # just to be sure
1531 unless (eval { 1637 unless (eval {
1532
1533 prepare_random_map $exit 1638 prepare_random_map $exit
1534 if $exit->slaying eq "/!"; 1639 if $exit->slaying eq "/!";
1535 1640
1536 my $path = new cf::path $exit->slaying, $exit->map && $exit->map->path; 1641 my $path = new cf::path $exit->slaying, $exit->map && $exit->map->path;
1537 $self->goto_map ($path, $exit->stats->hp, $exit->stats->sp); 1642 $self->goto ($path, $exit->stats->hp, $exit->stats->sp);
1538 1643
1539 1; 1644 1;
1540 }) { 1645 }) {
1541 $self->message ("Something went wrong deep within the crossfire server. " 1646 $self->message ("Something went wrong deep within the crossfire server. "
1542 . "I'll try to bring you back to the map you were before. " 1647 . "I'll try to bring you back to the map you were before. "
1599 on_reply => sub { 1704 on_reply => sub {
1600 my ($ns, $msg) = @_; 1705 my ($ns, $msg) = @_;
1601 1706
1602 # this weird shuffling is so that direct followup queries 1707 # this weird shuffling is so that direct followup queries
1603 # get handled first 1708 # get handled first
1604 my $queue = delete $ns->{query_queue}; 1709 my $queue = delete $ns->{query_queue}
1710 or return; # be conservative, not sure how that can happen, but we saw a crash here
1605 1711
1606 (shift @$queue)->[1]->($msg); 1712 (shift @$queue)->[1]->($msg);
1607 1713
1608 push @{ $ns->{query_queue} }, @$queue; 1714 push @{ $ns->{query_queue} }, @$queue;
1609 1715
1626=cut 1732=cut
1627 1733
1628sub cf::client::coro { 1734sub cf::client::coro {
1629 my ($self, $cb) = @_; 1735 my ($self, $cb) = @_;
1630 1736
1631 my $coro; $coro = async { 1737 my $coro = &Coro::async_pool ($cb);
1632 eval {
1633 $cb->();
1634 };
1635 warn $@ if $@;
1636 };
1637 1738
1638 $coro->on_destroy (sub { 1739 $coro->on_destroy (sub {
1639 delete $self->{_coro}{$coro+0}; 1740 delete $self->{_coro}{$coro+0};
1640 }); 1741 });
1641 1742
1813 1914
1814{ 1915{
1815 my $path = cf::localdir . "/database.pst"; 1916 my $path = cf::localdir . "/database.pst";
1816 1917
1817 sub db_load() { 1918 sub db_load() {
1818 warn "loading database $path\n";#d# remove later
1819 $DB = stat $path ? Storable::retrieve $path : { }; 1919 $DB = stat $path ? Storable::retrieve $path : { };
1820 } 1920 }
1821 1921
1822 my $pid; 1922 my $pid;
1823 1923
1824 sub db_save() { 1924 sub db_save() {
1825 warn "saving database $path\n";#d# remove later
1826 waitpid $pid, 0 if $pid; 1925 waitpid $pid, 0 if $pid;
1827 if (0 == ($pid = fork)) { 1926 if (0 == ($pid = fork)) {
1828 $DB->{_meta}{version} = 1; 1927 $DB->{_meta}{version} = 1;
1829 Storable::nstore $DB, "$path~"; 1928 Storable::nstore $DB, "$path~";
1830 rename "$path~", $path; 1929 rename "$path~", $path;
1878 open my $fh, "<:utf8", cf::confdir . "/config" 1977 open my $fh, "<:utf8", cf::confdir . "/config"
1879 or return; 1978 or return;
1880 1979
1881 local $/; 1980 local $/;
1882 *CFG = YAML::Syck::Load <$fh>; 1981 *CFG = YAML::Syck::Load <$fh>;
1982
1983 $EMERGENCY_POSITION = $CFG{emergency_position} || ["/world/world_105_115", 5, 37];
1984
1985 if (exists $CFG{mlockall}) {
1986 eval {
1987 $CFG{mlockall} ? &mlockall : &munlockall
1988 and die "WARNING: m(un)lockall failed: $!\n";
1989 };
1990 warn $@ if $@;
1991 }
1883} 1992}
1884 1993
1885sub main { 1994sub main {
1886 # we must not ever block the main coroutine 1995 # we must not ever block the main coroutine
1887 local $Coro::idle = sub { 1996 local $Coro::idle = sub {
1907 return; 2016 return;
1908 } 2017 }
1909 2018
1910 warn "reloading..."; 2019 warn "reloading...";
1911 2020
1912 local $FREEZE = 1; 2021 my $guard = freeze_mainloop;
1913 cf::emergency_save; 2022 cf::emergency_save;
1914 2023
1915 eval { 2024 eval {
1916 # if anything goes wrong in here, we should simply crash as we already saved 2025 # if anything goes wrong in here, we should simply crash as we already saved
1917 2026
2053 reentrant => 0, 2162 reentrant => 0,
2054 prio => 0, 2163 prio => 0,
2055 at => $NEXT_TICK || $TICK, 2164 at => $NEXT_TICK || $TICK,
2056 data => WF_AUTOCANCEL, 2165 data => WF_AUTOCANCEL,
2057 cb => sub { 2166 cb => sub {
2058 unless ($FREEZE) {
2059 cf::server_tick; # one server iteration 2167 cf::server_tick; # one server iteration
2060 $RUNTIME += $TICK; 2168 $RUNTIME += $TICK;
2061 }
2062
2063 $NEXT_TICK += $TICK; 2169 $NEXT_TICK += $TICK;
2064 2170
2065 # if we are delayed by four ticks or more, skip them all 2171 # if we are delayed by four ticks or more, skip them all
2066 $NEXT_TICK = Event::time if Event::time >= $NEXT_TICK + $TICK * 4; 2172 $NEXT_TICK = Event::time if Event::time >= $NEXT_TICK + $TICK * 4;
2067 2173
2090 or warn "ERROR: unable to write runtime file: $!"; 2196 or warn "ERROR: unable to write runtime file: $!";
2091 })->(); 2197 })->();
2092 }, 2198 },
2093); 2199);
2094 2200
2201END { cf::emergency_save }
2202
20951 22031
2096 2204

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines