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.109 by root, Sun Dec 31 22:23:12 2006 UTC vs.
Revision 1.125 by root, Tue Jan 2 23:06:39 2007 UTC

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
77our $EMERGENCY_POSITION = $cf::CFG{emergency_position} || ["/world/world_105_115", 5, 37];
76 78
77############################################################################# 79#############################################################################
78 80
79=head2 GLOBAL VARIABLES 81=head2 GLOBAL VARIABLES
80 82
179sub to_json($) { 181sub to_json($) {
180 $JSON::Syck::ImplicitUnicode = 0; # work around JSON::Syck bugs 182 $JSON::Syck::ImplicitUnicode = 0; # work around JSON::Syck bugs
181 JSON::Syck::Dump $_[0] 183 JSON::Syck::Dump $_[0]
182} 184}
183 185
186=item my $guard = cf::guard { BLOCK }
187
188Run the given callback when the guard object gets destroyed (useful for
189coroutine cancellations).
190
191You can call C<< ->cancel >> on the guard object to stop the block from
192being executed.
193
194=cut
195
196sub guard(&) {
197 bless \(my $cb = $_[0]), cf::guard::;
198}
199
200sub cf::guard::cancel {
201 ${$_[0]} = sub { };
202}
203
204sub cf::guard::DESTROY {
205 ${$_[0]}->();
206}
207
208=item cf::lock_wait $string
209
210Wait until the given lock is available. See cf::lock_acquire.
211
212=item my $lock = cf::lock_acquire $string
213
214Wait until the given lock is available and then acquires it and returns
215a guard object. If the guard object gets destroyed (goes out of scope,
216for example when the coroutine gets canceled), the lock is automatically
217returned.
218
219Lock names should begin with a unique identifier (for example, find_map
220uses map_find and load_map uses map_load).
221
222=cut
223
224our %LOCK;
225
226sub lock_wait($) {
227 my ($key) = @_;
228
229 # wait for lock, if any
230 while ($LOCK{$key}) {
231 push @{ $LOCK{$key} }, $Coro::current;
232 Coro::schedule;
233 }
234}
235
236sub lock_acquire($) {
237 my ($key) = @_;
238
239 # wait, to be sure we are not locked
240 lock_wait $key;
241
242 $LOCK{$key} = [];
243
244 cf::guard {
245 # wake up all waiters, to be on the safe side
246 $_->ready for @{ delete $LOCK{$key} };
247 }
248}
249
184=item cf::sync_job { BLOCK } 250=item cf::sync_job { BLOCK }
185 251
186The design of crossfire+ requires that the main coro ($Coro::main) is 252The design of crossfire+ requires that the main coro ($Coro::main) is
187always able to handle events or runnable, as crossfire+ is only partly 253always able to handle events or runnable, as crossfire+ is only partly
188reentrant. Thus "blocking" it by e.g. waiting for I/O is not acceptable. 254reentrant. Thus "blocking" it by e.g. waiting for I/O is not acceptable.
195=cut 261=cut
196 262
197sub sync_job(&) { 263sub sync_job(&) {
198 my ($job) = @_; 264 my ($job) = @_;
199 265
200 my $busy = 1;
201 my @res;
202
203 # TODO: use suspend/resume instead
204 local $FREEZE = 1;
205
206 my $coro = Coro::async {
207 @res = eval { $job->() };
208 warn $@ if $@;
209 undef $busy;
210 };
211
212 if ($Coro::current == $Coro::main) { 266 if ($Coro::current == $Coro::main) {
267 # this is the main coro, too bad, we have to block
268 # till the operation succeeds, freezing the server :/
269
270 # TODO: use suspend/resume instead
271 # (but this is cancel-safe)
272 local $FREEZE = 1;
273
274 my $busy = 1;
275 my @res;
276
277 (Coro::async {
278 @res = eval { $job->() };
279 warn $@ if $@;
280 undef $busy;
213 $coro->prio (Coro::PRIO_MAX); 281 })->prio (Coro::PRIO_MAX);
282
214 while ($busy) { 283 while ($busy) {
215 Coro::cede_notself; 284 Coro::cede_notself;
216 Event::one_event unless Coro::nready; 285 Event::one_event unless Coro::nready;
217 } 286 }
287
288 wantarray ? @res : $res[0]
218 } else { 289 } else {
219 $coro->join; 290 # we are in another coroutine, how wonderful, everything just works
291
292 $job->()
220 } 293 }
221
222 wantarray ? @res : $res[0]
223} 294}
224 295
225=item $coro = cf::coro { BLOCK } 296=item $coro = cf::coro { BLOCK }
226 297
227Creates and returns a new coro. This coro is automcatially being canceled 298Creates and returns a new coro. This coro is automcatially being canceled
251 my $runtime = cf::localdir . "/runtime"; 322 my $runtime = cf::localdir . "/runtime";
252 323
253 my $fh = aio_open "$runtime~", O_WRONLY | O_CREAT, 0644 324 my $fh = aio_open "$runtime~", O_WRONLY | O_CREAT, 0644
254 or return; 325 or return;
255 326
256 my $value = $cf::RUNTIME; 327 my $value = $cf::RUNTIME + 1 + 10; # 10 is the runtime save interval, for a monotonic clock
257 (aio_write $fh, 0, (length $value), $value, 0) <= 0 328 (aio_write $fh, 0, (length $value), $value, 0) <= 0
258 and return; 329 and return;
259 330
260 aio_fsync $fh 331 aio_fsync $fh
261 and return; 332 and return;
278package cf::path; 349package cf::path;
279 350
280sub new { 351sub new {
281 my ($class, $path, $base) = @_; 352 my ($class, $path, $base) = @_;
282 353
354 $path = $path->as_string if ref $path;
355
283 my $self = bless { }, $class; 356 my $self = bless { }, $class;
284 357
358 # {... are special paths that are not touched
359 # ?xxx/... are special absolute paths
360 # ?random/... random maps
361 # /! non-realised random map exit
362 # /... normal maps
363 # ~/... per-player maps without a specific player (DO NOT USE)
364 # ~user/... per-player map of a specific user
365
366 if ($path =~ /^{/) {
367 # fine as it is
285 if ($path =~ s{^\?random/}{}) { 368 } elsif ($path =~ s{^\?random/}{}) {
286 Coro::AIO::aio_load "$cf::RANDOM_MAPS/$path.meta", my $data; 369 Coro::AIO::aio_load "$cf::RANDOM_MAPS/$path.meta", my $data;
287 $self->{random} = cf::from_json $data; 370 $self->{random} = cf::from_json $data;
288 } else { 371 } else {
289 if ($path =~ s{^~([^/]+)?}{}) { 372 if ($path =~ s{^~([^/]+)?}{}) {
290 $self->{user_rel} = 1; 373 $self->{user_rel} = 1;
795 or return; 878 or return;
796 879
797 unless (aio_stat "$filename.pst") { 880 unless (aio_stat "$filename.pst") {
798 (aio_load "$filename.pst", $av) >= 0 881 (aio_load "$filename.pst", $av) >= 0
799 or return; 882 or return;
800 $av = eval { (Storable::thaw <$av>)->{objs} }; 883 $av = eval { (Storable::thaw $av)->{objs} };
801 } 884 }
802 885
886 warn sprintf "loading %s (%d)\n",
887 $filename, length $data, scalar @{$av || []};#d#
803 return ($data, $av); 888 return ($data, $av);
804} 889}
805 890
806############################################################################# 891#############################################################################
807# command handling &c 892# command handling &c
1029 $self->send ("ext " . to_json \%msg); 1114 $self->send ("ext " . to_json \%msg);
1030} 1115}
1031 1116
1032=back 1117=back
1033 1118
1119
1120=head3 cf::map
1121
1122=over 4
1123
1124=cut
1125
1126package cf::map;
1127
1128use Fcntl;
1129use Coro::AIO;
1130
1131our $MAX_RESET = 7200;
1132our $DEFAULT_RESET = 3600;
1133
1134sub generate_random_map {
1135 my ($path, $rmp) = @_;
1136
1137 # mit "rum" bekleckern, nicht
1138 cf::map::_create_random_map
1139 $path,
1140 $rmp->{wallstyle}, $rmp->{wall_name}, $rmp->{floorstyle}, $rmp->{monsterstyle},
1141 $rmp->{treasurestyle}, $rmp->{layoutstyle}, $rmp->{doorstyle}, $rmp->{decorstyle},
1142 $rmp->{origin_map}, $rmp->{final_map}, $rmp->{exitstyle}, $rmp->{this_map},
1143 $rmp->{exit_on_final_map},
1144 $rmp->{xsize}, $rmp->{ysize},
1145 $rmp->{expand2x}, $rmp->{layoutoptions1}, $rmp->{layoutoptions2}, $rmp->{layoutoptions3},
1146 $rmp->{symmetry}, $rmp->{difficulty}, $rmp->{difficulty_given}, $rmp->{difficulty_increase},
1147 $rmp->{dungeon_level}, $rmp->{dungeon_depth}, $rmp->{decoroptions}, $rmp->{orientation},
1148 $rmp->{origin_y}, $rmp->{origin_x}, $rmp->{random_seed}, $rmp->{total_map_hp},
1149 $rmp->{map_layout_style}, $rmp->{treasureoptions}, $rmp->{symmetry_used},
1150 (cf::region::find $rmp->{region})
1151}
1152
1153# and all this just because we cannot iterate over
1154# all maps in C++...
1155sub change_all_map_light {
1156 my ($change) = @_;
1157
1158 $_->change_map_light ($change)
1159 for grep $_->outdoor, values %cf::MAP;
1160}
1161
1162sub try_load_header($) {
1163 my ($path) = @_;
1164
1165 utf8::encode $path;
1166 aio_open $path, O_RDONLY, 0
1167 or return;
1168
1169 my $map = cf::map::new
1170 or return;
1171
1172 $map->load_header ($path)
1173 or return;
1174
1175 $map->{load_path} = $path;
1176
1177 $map
1178}
1179
1180sub find_map {
1181 my ($path, $origin) = @_;
1182
1183 #warn "find_map<$path,$origin>\n";#d#
1184
1185 $path = new cf::path $path, $origin && $origin->path;
1186 my $key = $path->as_string;
1187
1188 cf::lock_wait "map_find:$key";
1189
1190 $cf::MAP{$key} || do {
1191 my $guard = cf::lock_acquire "map_find:$key";
1192
1193 # do it the slow way
1194 my $map = try_load_header $path->save_path;
1195
1196 if ($map) {
1197 # safety
1198 $map->{instantiate_time} = $cf::RUNTIME
1199 if $map->{instantiate_time} > $cf::RUNTIME;
1200 } else {
1201 if (my $rmp = $path->random_map_params) {
1202 $map = generate_random_map $key, $rmp;
1203 } else {
1204 $map = try_load_header $path->load_path;
1205 }
1206
1207 $map or return;
1208
1209 $map->{load_original} = 1;
1210 $map->{instantiate_time} = $cf::RUNTIME;
1211 $map->instantiate;
1212
1213 # per-player maps become, after loading, normal maps
1214 $map->per_player (0) if $path->{user_rel};
1215 }
1216
1217 $map->path ($key);
1218 $map->{path} = $path;
1219 $map->{last_save} = $cf::RUNTIME;
1220 $map->last_access ($cf::RUNTIME);
1221
1222 if ($map->should_reset) {
1223 $map->reset;
1224 undef $guard;
1225 $map = cf::map::find_map ($path)
1226 or return;
1227 }
1228
1229 $cf::MAP{$key} = $map
1230 }
1231}
1232
1233sub load {
1234 my ($self) = @_;
1235
1236 my $path = $self->{path};
1237 my $guard = cf::lock_acquire "map_load:" . $path->as_string;
1238
1239 return if $self->in_memory != cf::MAP_SWAPPED;
1240
1241 $self->in_memory (cf::MAP_LOADING);
1242
1243 $self->alloc;
1244 $self->load_objects ($self->{load_path}, 1)
1245 or return;
1246
1247 $self->set_object_flag (cf::FLAG_OBJ_ORIGINAL, 1)
1248 if delete $self->{load_original};
1249
1250 if (my $uniq = $path->uniq_path) {
1251 utf8::encode $uniq;
1252 if (aio_open $uniq, O_RDONLY, 0) {
1253 $self->clear_unique_items;
1254 $self->load_objects ($uniq, 0);
1255 }
1256 }
1257
1258 # now do the right thing for maps
1259 $self->link_multipart_objects;
1260
1261 if ($self->{path}->is_style_map) {
1262 $self->{deny_save} = 1;
1263 $self->{deny_reset} = 1;
1264 } else {
1265 $self->fix_auto_apply;
1266 $self->decay_objects;
1267 $self->update_buttons;
1268 $self->set_darkness_map;
1269 $self->difficulty ($self->estimate_difficulty)
1270 unless $self->difficulty;
1271 $self->activate;
1272 }
1273
1274 $self->in_memory (cf::MAP_IN_MEMORY);
1275}
1276
1277sub load_map_sync {
1278 my ($path, $origin) = @_;
1279
1280 #warn "load_map_sync<$path, $origin>\n";#d#
1281
1282 cf::sync_job {
1283 my $map = cf::map::find_map $path, $origin
1284 or return;
1285 $map->load;
1286 $map
1287 }
1288}
1289
1290sub save {
1291 my ($self) = @_;
1292
1293 $self->{last_save} = $cf::RUNTIME;
1294
1295 return unless $self->dirty;
1296
1297 my $save = $self->{path}->save_path; utf8::encode $save;
1298 my $uniq = $self->{path}->uniq_path; utf8::encode $uniq;
1299
1300 $self->{load_path} = $save;
1301
1302 return if $self->{deny_save};
1303
1304 if ($uniq) {
1305 $self->save_objects ($save, cf::IO_HEADER | cf::IO_OBJECTS);
1306 $self->save_objects ($uniq, cf::IO_UNIQUES);
1307 } else {
1308 $self->save_objects ($save, cf::IO_HEADER | cf::IO_OBJECTS | cf::IO_UNIQUES);
1309 }
1310}
1311
1312sub swap_out {
1313 my ($self) = @_;
1314
1315 return if $self->players;
1316 return if $self->in_memory != cf::MAP_IN_MEMORY;
1317 return if $self->{deny_save};
1318
1319 $self->save;
1320 $self->clear;
1321 $self->in_memory (cf::MAP_SWAPPED);
1322}
1323
1324sub reset_at {
1325 my ($self) = @_;
1326
1327 # TODO: safety, remove and allow resettable per-player maps
1328 return 1e99 if $self->{path}{user_rel};
1329 return 1e99 if $self->{deny_reset};
1330
1331 my $time = $self->fixed_resettime ? $self->{instantiate_time} : $self->last_access;
1332 my $to = List::Util::min $MAX_RESET, $self->reset_timeout || $DEFAULT_RESET;
1333
1334 $time + $to
1335}
1336
1337sub should_reset {
1338 my ($self) = @_;
1339
1340 $self->reset_at <= $cf::RUNTIME
1341}
1342
1343sub unlink_save {
1344 my ($self) = @_;
1345
1346 utf8::encode (my $save = $self->{path}->save_path);
1347 aioreq_pri 3; IO::AIO::aio_unlink $save;
1348 aioreq_pri 3; IO::AIO::aio_unlink "$save.pst";
1349}
1350
1351sub rename {
1352 my ($self, $new_path) = @_;
1353
1354 $self->unlink_save;
1355
1356 delete $cf::MAP{$self->path};
1357 $self->{path} = new cf::path $new_path;
1358 $self->path ($self->{path}->as_string);
1359 $cf::MAP{$self->path} = $self;
1360
1361 $self->save;
1362}
1363
1364sub reset {
1365 my ($self) = @_;
1366
1367 return if $self->players;
1368 return if $self->{path}{user_rel};#d#
1369
1370 warn "resetting map ", $self->path;#d#
1371
1372 delete $cf::MAP{$self->path};
1373
1374 $_->clear_links_to ($self) for values %cf::MAP;
1375
1376 $self->unlink_save;
1377 $self->destroy;
1378}
1379
1380my $nuke_counter = "aaaa";
1381
1382sub nuke {
1383 my ($self) = @_;
1384
1385 $self->{deny_save} = 1;
1386 $self->reset_timeout (1);
1387 $self->rename ("{nuke}/" . ($nuke_counter++));
1388 $self->reset; # polite request, might not happen
1389}
1390
1391sub customise_for {
1392 my ($map, $ob) = @_;
1393
1394 if ($map->per_player) {
1395 return cf::map::find_map "~" . $ob->name . "/" . $map->{path}{path};
1396 }
1397
1398 $map
1399}
1400
1401sub emergency_save {
1402 local $cf::FREEZE = 1;
1403
1404 warn "enter emergency map save\n";
1405
1406 cf::sync_job {
1407 warn "begin emergency map save\n";
1408 $_->save for values %cf::MAP;
1409 };
1410
1411 warn "end emergency map save\n";
1412}
1413
1414package cf;
1415
1416=back
1417
1418
1034=head3 cf::object::player 1419=head3 cf::object::player
1035 1420
1036=over 4 1421=over 4
1037 1422
1038=item $player_object->reply ($npc, $msg[, $flags]) 1423=item $player_object->reply ($npc, $msg[, $flags])
1071 1456
1072 $self->flag (cf::FLAG_WIZ) || 1457 $self->flag (cf::FLAG_WIZ) ||
1073 (ref $cf::CFG{"may_$access"} 1458 (ref $cf::CFG{"may_$access"}
1074 ? scalar grep $self->name eq $_, @{$cf::CFG{"may_$access"}} 1459 ? scalar grep $self->name eq $_, @{$cf::CFG{"may_$access"}}
1075 : $cf::CFG{"may_$access"}) 1460 : $cf::CFG{"may_$access"})
1461}
1462
1463=item $player_object->enter_link
1464
1465Freezes the player and moves him/her to a special map (C<{link}>).
1466
1467The player should be reaosnably safe there for short amounts of time. You
1468I<MUST> call C<leave_link> as soon as possible, though.
1469
1470=item $player_object->leave_link ($map, $x, $y)
1471
1472Moves the player out of the specila link map onto the given map. If the
1473map is not valid (or omitted), the player will be moved back to the
1474location he/she was before the call to C<enter_link>, or, if that fails,
1475to the emergency map position.
1476
1477Might block.
1478
1479=cut
1480
1481sub cf::object::player::enter_link {
1482 my ($self) = @_;
1483
1484 $self->deactivate_recursive;
1485
1486 return if $self->map == $LINK_MAP;
1487
1488 $self->{_link_pos} ||= [$self->map->{path}, $self->x, $self->y]
1489 if $self->map;
1490
1491 $self->enter_map ($LINK_MAP, 20, 20);
1492}
1493
1494sub cf::object::player::leave_link {
1495 my ($self, $map, $x, $y) = @_;
1496
1497 my $link_pos = delete $self->{_link_pos};
1498
1499 unless ($map) {
1500 # restore original map position
1501 ($map, $x, $y) = @{ $link_pos || [] };
1502 $map = cf::map::find_map $map;
1503
1504 unless ($map) {
1505 ($map, $x, $y) = @$EMERGENCY_POSITION;
1506 $map = cf::map::find_map $map
1507 or die "FATAL: cannot load emergency map\n";
1508 }
1509 }
1510
1511 ($x, $y) = (-1, -1)
1512 unless (defined $x) && (defined $y);
1513
1514 # use -1 or undef as default coordinates, not 0, 0
1515 ($x, $y) = ($map->enter_x, $map->enter_y)
1516 if $x <=0 && $y <= 0;
1517
1518 $map->load;
1519
1520 $self->activate_recursive;
1521 $self->enter_map ($map, $x, $y);
1522}
1523
1524cf::player->attach (
1525 on_logout => sub {
1526 my ($pl) = @_;
1527
1528 # abort map switching before logout
1529 if ($pl->ob->{_link_pos}) {
1530 cf::sync_job {
1531 $pl->ob->leave_link
1532 };
1533 }
1534 },
1535 on_login => sub {
1536 my ($pl) = @_;
1537
1538 # try to abort aborted map switching on player login :)
1539 # should happen only on crashes
1540 if ($pl->ob->{_link_pos}) {
1541 $pl->ob->enter_link;
1542 Coro::async {
1543 # we need this sleep as the login has a concurrent enter_exit running
1544 # and this sleep increases chances of the player not ending up in scorn
1545 Coro::Timer::sleep 1;
1546 $pl->ob->leave_link;
1547 };
1548 }
1549 },
1550);
1551
1552=item $player_object->goto_map ($path, $x, $y)
1553
1554=cut
1555
1556sub cf::object::player::goto_map {
1557 my ($self, $path, $x, $y) = @_;
1558
1559 $self->enter_link;
1560
1561 (Coro::async {
1562 $path = new cf::path $path;
1563
1564 my $map = cf::map::find_map $path->as_string;
1565 $map = $map->customise_for ($self) if $map;
1566
1567# warn "entering ", $map->path, " at ($x, $y)\n"
1568# if $map;
1569
1570 $map or $self->message ("The exit is closed", cf::NDI_UNIQUE | cf::NDI_RED);
1571
1572 $self->leave_link ($map, $x, $y);
1573 })->prio (1);
1574}
1575
1576=item $player_object->enter_exit ($exit_object)
1577
1578=cut
1579
1580sub parse_random_map_params {
1581 my ($spec) = @_;
1582
1583 my $rmp = { # defaults
1584 xsize => 10,
1585 ysize => 10,
1586 };
1587
1588 for (split /\n/, $spec) {
1589 my ($k, $v) = split /\s+/, $_, 2;
1590
1591 $rmp->{lc $k} = $v if (length $k) && (length $v);
1592 }
1593
1594 $rmp
1595}
1596
1597sub prepare_random_map {
1598 my ($exit) = @_;
1599
1600 # all this does is basically replace the /! path by
1601 # a new random map path (?random/...) with a seed
1602 # that depends on the exit object
1603
1604 my $rmp = parse_random_map_params $exit->msg;
1605
1606 if ($exit->map) {
1607 $rmp->{region} = $exit->map->region_name;
1608 $rmp->{origin_map} = $exit->map->path;
1609 $rmp->{origin_x} = $exit->x;
1610 $rmp->{origin_y} = $exit->y;
1611 }
1612
1613 $rmp->{random_seed} ||= $exit->random_seed;
1614
1615 my $data = cf::to_json $rmp;
1616 my $md5 = Digest::MD5::md5_hex $data;
1617
1618 if (my $fh = aio_open "$cf::RANDOM_MAPS/$md5.meta", O_WRONLY | O_CREAT, 0666) {
1619 aio_write $fh, 0, (length $data), $data, 0;
1620
1621 $exit->slaying ("?random/$md5");
1622 $exit->msg (undef);
1623 }
1624}
1625
1626sub cf::object::player::enter_exit {
1627 my ($self, $exit) = @_;
1628
1629 return unless $self->type == cf::PLAYER;
1630
1631 $self->enter_link;
1632
1633 (Coro::async {
1634 unless (eval {
1635
1636 prepare_random_map $exit
1637 if $exit->slaying eq "/!";
1638
1639 my $path = new cf::path $exit->slaying, $exit->map && $exit->map->path;
1640 $self->goto_map ($path, $exit->stats->hp, $exit->stats->sp);
1641
1642 1;
1643 }) {
1644 $self->message ("Something went wrong deep within the crossfire server. "
1645 . "I'll try to bring you back to the map you were before. "
1646 . "Please report this to the dungeon master",
1647 cf::NDI_UNIQUE | cf::NDI_RED);
1648
1649 warn "ERROR in enter_exit: $@";
1650 $self->leave_link;
1651 }
1652 })->prio (1);
1076} 1653}
1077 1654
1078=head3 cf::client 1655=head3 cf::client
1079 1656
1080=over 4 1657=over 4
1339 1916
1340{ 1917{
1341 my $path = cf::localdir . "/database.pst"; 1918 my $path = cf::localdir . "/database.pst";
1342 1919
1343 sub db_load() { 1920 sub db_load() {
1344 warn "loading database $path\n";#d# remove later
1345 $DB = stat $path ? Storable::retrieve $path : { }; 1921 $DB = stat $path ? Storable::retrieve $path : { };
1346 } 1922 }
1347 1923
1348 my $pid; 1924 my $pid;
1349 1925
1350 sub db_save() { 1926 sub db_save() {
1351 warn "saving database $path\n";#d# remove later
1352 waitpid $pid, 0 if $pid; 1927 waitpid $pid, 0 if $pid;
1353 if (0 == ($pid = fork)) { 1928 if (0 == ($pid = fork)) {
1354 $DB->{_meta}{version} = 1; 1929 $DB->{_meta}{version} = 1;
1355 Storable::nstore $DB, "$path~"; 1930 Storable::nstore $DB, "$path~";
1356 rename "$path~", $path; 1931 rename "$path~", $path;
1409} 1984}
1410 1985
1411sub main { 1986sub main {
1412 # we must not ever block the main coroutine 1987 # we must not ever block the main coroutine
1413 local $Coro::idle = sub { 1988 local $Coro::idle = sub {
1414 Carp::cluck "FATAL: Coro::idle was called, major BUG\n";#d# 1989 Carp::cluck "FATAL: Coro::idle was called, major BUG, use cf::sync_job!\n";#d#
1415 (Coro::unblock_sub { 1990 (Coro::unblock_sub {
1416 Event::one_event; 1991 Event::one_event;
1417 })->(); 1992 })->();
1418 }; 1993 };
1419 1994
1424} 1999}
1425 2000
1426############################################################################# 2001#############################################################################
1427# initialisation 2002# initialisation
1428 2003
1429sub perl_reload() { 2004sub reload() {
1430 # can/must only be called in main 2005 # can/must only be called in main
1431 if ($Coro::current != $Coro::main) { 2006 if ($Coro::current != $Coro::main) {
1432 warn "can only reload from main coroutine\n"; 2007 warn "can only reload from main coroutine\n";
1433 return; 2008 return;
1434 } 2009 }
1527 $LINK_MAP->height (41); 2102 $LINK_MAP->height (41);
1528 $LINK_MAP->alloc; 2103 $LINK_MAP->alloc;
1529 $LINK_MAP->path ("{link}"); 2104 $LINK_MAP->path ("{link}");
1530 $LINK_MAP->{path} = bless { path => "{link}" }, "cf::path"; 2105 $LINK_MAP->{path} = bless { path => "{link}" }, "cf::path";
1531 $LINK_MAP->in_memory (MAP_IN_MEMORY); 2106 $LINK_MAP->in_memory (MAP_IN_MEMORY);
2107
2108 # dirty hack because... archetypes are not yet loaded
2109 Event->timer (
2110 after => 2,
2111 cb => sub {
2112 $_[0]->w->cancel;
2113
2114 # provide some exits "home"
2115 my $exit = cf::object::new "exit";
2116
2117 $exit->slaying ($EMERGENCY_POSITION->[0]);
2118 $exit->stats->hp ($EMERGENCY_POSITION->[1]);
2119 $exit->stats->sp ($EMERGENCY_POSITION->[2]);
2120
2121 $LINK_MAP->insert ($exit->clone, 19, 19);
2122 $LINK_MAP->insert ($exit->clone, 19, 20);
2123 $LINK_MAP->insert ($exit->clone, 19, 21);
2124 $LINK_MAP->insert ($exit->clone, 20, 19);
2125 $LINK_MAP->insert ($exit->clone, 20, 21);
2126 $LINK_MAP->insert ($exit->clone, 21, 19);
2127 $LINK_MAP->insert ($exit->clone, 21, 20);
2128 $LINK_MAP->insert ($exit->clone, 21, 21);
2129
2130 $exit->destroy;
2131 });
2132
2133 $LINK_MAP->{deny_save} = 1;
2134 $LINK_MAP->{deny_reset} = 1;
2135
2136 $cf::MAP{$LINK_MAP->path} = $LINK_MAP;
1532} 2137}
1533 2138
1534register "<global>", __PACKAGE__; 2139register "<global>", __PACKAGE__;
1535 2140
1536register_command "perl-reload" => sub { 2141register_command "reload" => sub {
1537 my ($who, $arg) = @_; 2142 my ($who, $arg) = @_;
1538 2143
1539 if ($who->flag (FLAG_WIZ)) { 2144 if ($who->flag (FLAG_WIZ)) {
1540 $who->message ("start of reload."); 2145 $who->message ("start of reload.");
1541 perl_reload; 2146 reload;
1542 $who->message ("end of reload."); 2147 $who->message ("end of reload.");
1543 } 2148 }
1544}; 2149};
1545 2150
1546unshift @INC, $LIBDIR; 2151unshift @INC, $LIBDIR;
1586 or warn "ERROR: unable to write runtime file: $!"; 2191 or warn "ERROR: unable to write runtime file: $!";
1587 })->(); 2192 })->();
1588 }, 2193 },
1589); 2194);
1590 2195
2196END { cf::emergency_save }
2197
15911 21981
1592 2199

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines