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.105 by root, Sun Dec 31 17:17:23 2006 UTC vs.
Revision 1.111 by root, Mon Jan 1 12:28:47 2007 UTC

15use Coro::Timer; 15use Coro::Timer;
16use Coro::Signal; 16use Coro::Signal;
17use Coro::Semaphore; 17use Coro::Semaphore;
18use Coro::AIO; 18use Coro::AIO;
19 19
20use Digest::MD5;
20use Fcntl; 21use Fcntl;
21use IO::AIO 2.31 (); 22use IO::AIO 2.31 ();
22use YAML::Syck (); 23use YAML::Syck ();
23use Time::HiRes; 24use Time::HiRes;
24 25
49our $RUNTIME; 50our $RUNTIME;
50 51
51our %MAP; # all maps 52our %MAP; # all maps
52our $LINK_MAP; # the special {link} map 53our $LINK_MAP; # the special {link} map
53our $FREEZE; 54our $FREEZE;
55our $RANDOM_MAPS = cf::localdir . "/random";
56our %EXT_CORO;
54 57
55binmode STDOUT; 58binmode STDOUT;
56binmode STDERR; 59binmode STDERR;
57 60
58# read virtual server time, if available 61# read virtual server time, if available
64 67
65mkdir cf::localdir; 68mkdir cf::localdir;
66mkdir cf::localdir . "/" . cf::playerdir; 69mkdir cf::localdir . "/" . cf::playerdir;
67mkdir cf::localdir . "/" . cf::tmpdir; 70mkdir cf::localdir . "/" . cf::tmpdir;
68mkdir cf::localdir . "/" . cf::uniquedir; 71mkdir cf::localdir . "/" . cf::uniquedir;
72mkdir $RANDOM_MAPS;
69 73
70our %EXT_CORO; 74# a special map that is always available
75our $LINK_MAP;
76
77our $EMERGENCY_POSITION = $cf::CFG{emergency_position} || ["/world/world_105_115", 5, 37];
71 78
72############################################################################# 79#############################################################################
73 80
74=head2 GLOBAL VARIABLES 81=head2 GLOBAL VARIABLES
75 82
174sub to_json($) { 181sub to_json($) {
175 $JSON::Syck::ImplicitUnicode = 0; # work around JSON::Syck bugs 182 $JSON::Syck::ImplicitUnicode = 0; # work around JSON::Syck bugs
176 JSON::Syck::Dump $_[0] 183 JSON::Syck::Dump $_[0]
177} 184}
178 185
179# main coro must never ever "block" except in Event 186=item cf::sync_job { BLOCK }
180# sync_job ensures this by running the job in a coroutine 187
181# and waiting in Event while the server is otherwise frozen 188The design of crossfire+ requires that the main coro ($Coro::main) is
189always 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.
191
192If it must be done, put the blocking parts into C<sync_job>. This will run
193the given BLOCK in another coroutine while waiting for the result. The
194server will be frozen during this time, so the block should either finish
195fast or be very important.
196
197=cut
198
182sub sync_job(&) { 199sub sync_job(&) {
183 my ($job) = @_; 200 my ($job) = @_;
184 201
185 my $busy = 1; 202 my $busy = 1;
186 my @res; 203 my @res;
187
188 local $FREEZE = 1;
189 204
190 my $coro = Coro::async { 205 my $coro = Coro::async {
191 @res = eval { $job->() }; 206 @res = eval { $job->() };
192 warn $@ if $@; 207 warn $@ if $@;
193 undef $busy; 208 undef $busy;
194 }; 209 };
195 210
196 if ($Coro::current == $Coro::main) { 211 if ($Coro::current == $Coro::main) {
212 # TODO: use suspend/resume instead
213 local $FREEZE = 1;
197 $coro->prio (Coro::PRIO_MAX); 214 $coro->prio (Coro::PRIO_MAX);
198 while ($busy) { 215 while ($busy) {
199 Coro::cede_notself; 216 Coro::cede_notself;
200 Event::one_event unless Coro::nready; 217 Event::one_event unless Coro::nready;
201 } 218 }
229 $EXT_CORO{$coro+0} = $coro; 246 $EXT_CORO{$coro+0} = $coro;
230 247
231 $coro 248 $coro
232} 249}
233 250
251sub write_runtime {
252 my $runtime = cf::localdir . "/runtime";
253
254 my $fh = aio_open "$runtime~", O_WRONLY | O_CREAT, 0644
255 or return;
256
257 my $value = $cf::RUNTIME;
258 (aio_write $fh, 0, (length $value), $value, 0) <= 0
259 and return;
260
261 aio_fsync $fh
262 and return;
263
264 close $fh
265 or return;
266
267 aio_rename "$runtime~", $runtime
268 and return;
269
270 1
271}
272
234=back 273=back
235 274
236=cut 275=cut
276
277#############################################################################
278
279package cf::path;
280
281sub new {
282 my ($class, $path, $base) = @_;
283
284 $path = $path->as_string if ref $path;
285
286 my $self = bless { }, $class;
287
288 if ($path =~ s{^\?random/}{}) {
289 Coro::AIO::aio_load "$cf::RANDOM_MAPS/$path.meta", my $data;
290 $self->{random} = cf::from_json $data;
291 } else {
292 if ($path =~ s{^~([^/]+)?}{}) {
293 $self->{user_rel} = 1;
294
295 if (defined $1) {
296 $self->{user} = $1;
297 } elsif ($base =~ m{^~([^/]+)/}) {
298 $self->{user} = $1;
299 } else {
300 warn "cannot resolve user-relative path without user <$path,$base>\n";
301 }
302 } elsif ($path =~ /^\//) {
303 # already absolute
304 } else {
305 $base =~ s{[^/]+/?$}{};
306 return $class->new ("$base/$path");
307 }
308
309 for ($path) {
310 redo if s{/\.?/}{/};
311 redo if s{/[^/]+/\.\./}{/};
312 }
313 }
314
315 $self->{path} = $path;
316
317 $self
318}
319
320# the name / primary key / in-game path
321sub as_string {
322 my ($self) = @_;
323
324 $self->{user_rel} ? "~$self->{user}$self->{path}"
325 : $self->{random} ? "?random/$self->{path}"
326 : $self->{path}
327}
328
329# the displayed name, this is a one way mapping
330sub visible_name {
331 my ($self) = @_;
332
333# if (my $rmp = $self->{random}) {
334# # todo: be more intelligent about this
335# "?random/$rmp->{origin_map}+$rmp->{origin_x}+$rmp->{origin_y}/$rmp->{dungeon_level}"
336# } else {
337 $self->as_string
338# }
339}
340
341# escape the /'s in the path
342sub _escaped_path {
343 # ∕ is U+2215
344 (my $path = $_[0]{path}) =~ s/\//∕/g;
345 $path
346}
347
348# the original (read-only) location
349sub load_path {
350 my ($self) = @_;
351
352 sprintf "%s/%s/%s", cf::datadir, cf::mapdir, $self->{path}
353}
354
355# the temporary/swap location
356sub save_path {
357 my ($self) = @_;
358
359 $self->{user_rel} ? sprintf "%s/%s/%s/%s", cf::localdir, cf::playerdir, $self->{user}, $self->_escaped_path
360 : $self->{random} ? sprintf "%s/%s", $RANDOM_MAPS, $self->{path}
361 : sprintf "%s/%s/%s", cf::localdir, cf::tmpdir, $self->_escaped_path
362}
363
364# the unique path, might be eq to save_path
365sub uniq_path {
366 my ($self) = @_;
367
368 $self->{user_rel} || $self->{random}
369 ? undef
370 : sprintf "%s/%s/%s", cf::localdir, cf::uniquedir, $self->_escaped_path
371}
372
373# return random map parameters, or undef
374sub random_map_params {
375 my ($self) = @_;
376
377 $self->{random}
378}
379
380# this is somewhat ugly, but style maps do need special treatment
381sub is_style_map {
382 $_[0]{path} =~ m{^/styles/}
383}
384
385package cf;
237 386
238############################################################################# 387#############################################################################
239 388
240=head2 ATTACHABLE OBJECTS 389=head2 ATTACHABLE OBJECTS
241 390
883 $self->send ("ext " . to_json \%msg); 1032 $self->send ("ext " . to_json \%msg);
884} 1033}
885 1034
886=back 1035=back
887 1036
1037
1038=head3 cf::map
1039
1040=over 4
1041
1042=cut
1043
1044package cf::map;
1045
1046use Fcntl;
1047use Coro::AIO;
1048
1049our $MAX_RESET = 7200;
1050our $DEFAULT_RESET = 3600;
1051$MAX_RESET = 10;#d#
1052$DEFAULT_RESET = 10;#d#
1053
1054sub generate_random_map {
1055 my ($path, $rmp) = @_;
1056
1057 # mit "rum" bekleckern, nicht
1058 cf::map::_create_random_map
1059 $path,
1060 $rmp->{wallstyle}, $rmp->{wall_name}, $rmp->{floorstyle}, $rmp->{monsterstyle},
1061 $rmp->{treasurestyle}, $rmp->{layoutstyle}, $rmp->{doorstyle}, $rmp->{decorstyle},
1062 $rmp->{origin_map}, $rmp->{final_map}, $rmp->{exitstyle}, $rmp->{this_map},
1063 $rmp->{exit_on_final_map},
1064 $rmp->{xsize}, $rmp->{ysize},
1065 $rmp->{expand2x}, $rmp->{layoutoptions1}, $rmp->{layoutoptions2}, $rmp->{layoutoptions3},
1066 $rmp->{symmetry}, $rmp->{difficulty}, $rmp->{difficulty_given}, $rmp->{difficulty_increase},
1067 $rmp->{dungeon_level}, $rmp->{dungeon_depth}, $rmp->{decoroptions}, $rmp->{orientation},
1068 $rmp->{origin_y}, $rmp->{origin_x}, $rmp->{random_seed}, $rmp->{total_map_hp},
1069 $rmp->{map_layout_style}, $rmp->{treasureoptions}, $rmp->{symmetry_used},
1070 (cf::region::find $rmp->{region})
1071}
1072
1073# and all this just because we cannot iterate over
1074# all maps in C++...
1075sub change_all_map_light {
1076 my ($change) = @_;
1077
1078 $_->change_map_light ($change) for values %cf::MAP;
1079}
1080
1081sub try_load_header($) {
1082 my ($path) = @_;
1083
1084 utf8::encode $path;
1085 aio_open $path, O_RDONLY, 0
1086 or return;
1087
1088 my $map = cf::map::new
1089 or return;
1090
1091 $map->load_header ($path)
1092 or return;
1093
1094 $map->{load_path} = $path;
1095 use Data::Dumper; warn Dumper $map;#d#
1096
1097 $map
1098}
1099
1100sub find_map {
1101 my ($path, $origin) = @_;
1102
1103 #warn "find_map<$path,$origin>\n";#d#
1104
1105 $path = ref $path ? $path : new cf::path $path, $origin && $origin->path;
1106 my $key = $path->as_string;
1107
1108 $cf::MAP{$key} || do {
1109 # do it the slow way
1110 my $map = try_load_header $path->save_path;
1111
1112 if ($map) {
1113 # safety
1114 $map->{instantiate_time} = $cf::RUNTIME
1115 if $map->{instantiate_time} > $cf::RUNTIME;
1116 } else {
1117 if (my $rmp = $path->random_map_params) {
1118 $map = generate_random_map $key, $rmp;
1119 } else {
1120 $map = try_load_header $path->load_path;
1121 }
1122
1123 $map or return;
1124
1125 $map->{load_original} = 1;
1126 $map->{instantiate_time} = $cf::RUNTIME;
1127 $map->instantiate;
1128
1129 # per-player maps become, after loading, normal maps
1130 $map->per_player (0) if $path->{user_rel};
1131 }
1132
1133 $map->path ($key);
1134 $map->{path} = $path;
1135 $map->last_access ($cf::RUNTIME);
1136
1137 $map->reset if $map->should_reset;
1138
1139 $cf::MAP{$key} = $map
1140 }
1141}
1142
1143sub load {
1144 my ($self) = @_;
1145
1146 return if $self->in_memory != cf::MAP_SWAPPED;
1147
1148 $self->in_memory (cf::MAP_LOADING);
1149
1150 my $path = $self->{path};
1151
1152 $self->alloc;
1153 $self->load_objects ($self->{load_path}, 1)
1154 or return;
1155
1156 $self->set_object_flag (cf::FLAG_OBJ_ORIGINAL, 1) if delete $self->{load_original};
1157
1158 if (my $uniq = $path->uniq_path) {
1159 utf8::encode $uniq;
1160 if (aio_open $uniq, O_RDONLY, 0) {
1161 $self->clear_unique_items;
1162 $self->load_objects ($uniq, 0);
1163 }
1164 }
1165
1166 # now do the right thing for maps
1167 $self->link_multipart_objects;
1168
1169 if ($self->{path}->is_style_map) {
1170 $self->{deny_save} = 1;
1171 $self->{deny_reset} = 1;
1172 } else {
1173 $self->fix_auto_apply;
1174 $self->decay_objects;
1175 $self->update_buttons;
1176 $self->set_darkness_map;
1177 $self->difficulty ($self->estimate_difficulty)
1178 unless $self->difficulty;
1179 $self->activate;
1180 }
1181
1182 $self->in_memory (cf::MAP_IN_MEMORY);
1183}
1184
1185sub load_map_sync {
1186 my ($path, $origin) = @_;
1187
1188 #warn "load_map_sync<$path, $origin>\n";#d#
1189
1190 cf::sync_job {
1191 my $map = cf::map::find_map $path, $origin
1192 or return;
1193 $map->load;
1194 $map
1195 }
1196}
1197
1198sub save {
1199 my ($self) = @_;
1200
1201 my $save = $self->{path}->save_path; utf8::encode $save;
1202 my $uniq = $self->{path}->uniq_path; utf8::encode $uniq;
1203
1204 $self->{last_save} = $cf::RUNTIME;
1205
1206 return unless $self->dirty;
1207
1208 $self->{load_path} = $save;
1209
1210 return if $self->{deny_save};
1211
1212 warn "saving map ", $self->path;
1213
1214 if ($uniq) {
1215 $self->save_objects ($save, cf::IO_HEADER | cf::IO_OBJECTS);
1216 $self->save_objects ($uniq, cf::IO_UNIQUES);
1217 } else {
1218 $self->save_objects ($save, cf::IO_HEADER | cf::IO_OBJECTS | cf::IO_UNIQUES);
1219 }
1220}
1221
1222sub swap_out {
1223 my ($self) = @_;
1224
1225 return if $self->players;
1226 return if $self->in_memory != cf::MAP_IN_MEMORY;
1227 return if $self->{deny_save};
1228
1229 $self->save;
1230 $self->clear;
1231 $self->in_memory (cf::MAP_SWAPPED);
1232}
1233
1234sub should_reset {
1235 my ($map) = @_;
1236
1237 # TODO: safety, remove and allow resettable per-player maps
1238 return if $map->{path}{user_rel};#d#
1239 return if $map->{deny_reset};
1240 #return unless $map->reset_timeout;
1241
1242 my $time = $map->fixed_resettime ? $map->{instantiate_time} : $map->last_access;
1243 my $to = $map->reset_timeout || $DEFAULT_RESET;
1244 $to = $MAX_RESET if $to > $MAX_RESET;
1245
1246 $time + $to < $cf::RUNTIME
1247}
1248
1249sub unlink_save {
1250 my ($self) = @_;
1251
1252 utf8::encode (my $save = $self->{path}->save_path);
1253 aioreq_pri 3; IO::AIO::aio_unlink $save;
1254 aioreq_pri 3; IO::AIO::aio_unlink "$save.pst";
1255}
1256
1257sub reset {
1258 my ($self) = @_;
1259
1260 return if $self->players;
1261 return if $self->{path}{user_rel};#d#
1262
1263 warn "resetting map ", $self->path;#d#
1264
1265 delete $cf::MAP{$self->path};
1266
1267 $_->clear_links_to ($self) for values %cf::MAP;
1268
1269 $self->unlink_save;
1270 $self->destroy;
1271}
1272
1273sub customise_for {
1274 my ($map, $ob) = @_;
1275
1276 if ($map->per_player) {
1277 return cf::map::find_map "~" . $ob->name . "/" . $map->{path}{path};
1278 }
1279
1280 $map
1281}
1282
1283sub emergency_save {
1284 local $cf::FREEZE = 1;
1285
1286 warn "enter emergency map save\n";
1287
1288 cf::sync_job {
1289 warn "begin emergency map save\n";
1290 $_->save for values %cf::MAP;
1291 };
1292
1293 warn "end emergency map save\n";
1294}
1295
1296package cf;
1297
1298=back
1299
1300
888=head3 cf::object::player 1301=head3 cf::object::player
889 1302
890=over 4 1303=over 4
891 1304
892=item $player_object->reply ($npc, $msg[, $flags]) 1305=item $player_object->reply ($npc, $msg[, $flags])
925 1338
926 $self->flag (cf::FLAG_WIZ) || 1339 $self->flag (cf::FLAG_WIZ) ||
927 (ref $cf::CFG{"may_$access"} 1340 (ref $cf::CFG{"may_$access"}
928 ? scalar grep $self->name eq $_, @{$cf::CFG{"may_$access"}} 1341 ? scalar grep $self->name eq $_, @{$cf::CFG{"may_$access"}}
929 : $cf::CFG{"may_$access"}) 1342 : $cf::CFG{"may_$access"})
1343}
1344
1345sub cf::object::player::enter_link {
1346 my ($self) = @_;
1347
1348 return if $self->map == $LINK_MAP;
1349
1350 $self->{_link_pos} = [$self->map->{path}, $self->x, $self->y]
1351 if $self->map;
1352
1353 $self->enter_map ($LINK_MAP, 20, 20);
1354 $self->deactivate_recursive;
1355}
1356
1357sub cf::object::player::leave_link {
1358 my ($self, $map, $x, $y) = @_;
1359
1360 my $link_pos = delete $self->{_link_pos};
1361
1362 unless ($map) {
1363 $self->message ("The exit is closed", cf::NDI_UNIQUE | cf::NDI_RED);
1364
1365 # restore original map position
1366 ($map, $x, $y) = @{ $link_pos || [] };
1367 $map = cf::map::find_map $map;
1368
1369 unless ($map) {
1370 ($map, $x, $y) = @$EMERGENCY_POSITION;
1371 $map = cf::map::find_map $map
1372 or die "FATAL: cannot load emergency map\n";
1373 }
1374 }
1375
1376 ($x, $y) = (-1, -1)
1377 unless (defined $x) && (defined $y);
1378
1379 # use -1 or undef as default coordinates, not 0, 0
1380 ($x, $y) = ($map->enter_x, $map->enter_y)
1381 if $x <=0 && $y <= 0;
1382
1383 $map->load;
1384
1385 $self->activate_recursive;
1386 $self->enter_map ($map, $x, $y);
1387}
1388
1389=item $player_object->goto_map ($map, $x, $y)
1390
1391=cut
1392
1393sub cf::object::player::goto_map {
1394 my ($self, $path, $x, $y) = @_;
1395
1396 $self->enter_link;
1397
1398 (Coro::async {
1399 $path = new cf::path $path;
1400
1401 my $map = cf::map::find_map $path->as_string;
1402 $map = $map->customise_for ($self) if $map;
1403
1404 warn "entering ", $map->path, " at ($x, $y)\n"
1405 if $map;
1406
1407 $self->leave_link ($map, $x, $y);
1408 })->prio (1);
1409}
1410
1411=item $player_object->enter_exit ($exit_object)
1412
1413=cut
1414
1415sub parse_random_map_params {
1416 my ($spec) = @_;
1417
1418 my $rmp = { # defaults
1419 xsize => 10,
1420 ysize => 10,
1421 };
1422
1423 for (split /\n/, $spec) {
1424 my ($k, $v) = split /\s+/, $_, 2;
1425
1426 $rmp->{lc $k} = $v if (length $k) && (length $v);
1427 }
1428
1429 $rmp
1430}
1431
1432sub prepare_random_map {
1433 my ($exit) = @_;
1434
1435 # all this does is basically replace the /! path by
1436 # a new random map path (?random/...) with a seed
1437 # that depends on the exit object
1438
1439 my $rmp = parse_random_map_params $exit->msg;
1440
1441 if ($exit->map) {
1442 $rmp->{region} = $exit->map->region_name;
1443 $rmp->{origin_map} = $exit->map->path;
1444 $rmp->{origin_x} = $exit->x;
1445 $rmp->{origin_y} = $exit->y;
1446 }
1447
1448 $rmp->{random_seed} ||= $exit->random_seed;
1449
1450 my $data = cf::to_json $rmp;
1451 my $md5 = Digest::MD5::md5_hex $data;
1452
1453 if (my $fh = aio_open "$cf::RANDOM_MAPS/$md5.meta", O_WRONLY | O_CREAT, 0666) {
1454 aio_write $fh, 0, (length $data), $data, 0;
1455
1456 $exit->slaying ("?random/$md5");
1457 $exit->msg (undef);
1458 }
1459}
1460
1461sub cf::object::player::enter_exit {
1462 my ($self, $exit) = @_;
1463
1464 return unless $self->type == cf::PLAYER;
1465
1466 $self->enter_link;
1467
1468 (Coro::async {
1469 unless (eval {
1470
1471 prepare_random_map $exit
1472 if $exit->slaying eq "/!";
1473
1474 my $path = new cf::path $exit->slaying, $exit->map && $exit->map->path;
1475 $self->goto_map ($path, $exit->stats->hp, $exit->stats->sp);
1476
1477 1;
1478 }) {
1479 $self->message ("Something went wrong deep within the crossfire server. "
1480 . "I'll try to bring you back to the map you were before. "
1481 . "Please report this to the dungeon master",
1482 cf::NDI_UNIQUE | cf::NDI_RED);
1483
1484 warn "ERROR in enter_exit: $@";
1485 $self->leave_link;
1486 }
1487 })->prio (1);
930} 1488}
931 1489
932=head3 cf::client 1490=head3 cf::client
933 1491
934=over 4 1492=over 4
1261 local $/; 1819 local $/;
1262 *CFG = YAML::Syck::Load <$fh>; 1820 *CFG = YAML::Syck::Load <$fh>;
1263} 1821}
1264 1822
1265sub main { 1823sub main {
1824 # we must not ever block the main coroutine
1825 local $Coro::idle = sub {
1826 Carp::cluck "FATAL: Coro::idle was called, major BUG\n";#d#
1827 (Coro::unblock_sub {
1828 Event::one_event;
1829 })->();
1830 };
1831
1266 cfg_load; 1832 cfg_load;
1267 db_load; 1833 db_load;
1268 load_extensions; 1834 load_extensions;
1269 Event::loop; 1835 Event::loop;
1270} 1836}
1271 1837
1272############################################################################# 1838#############################################################################
1273# initialisation 1839# initialisation
1274 1840
1275sub _perl_reload() { 1841sub reload() {
1842 # can/must only be called in main
1843 if ($Coro::current != $Coro::main) {
1844 warn "can only reload from main coroutine\n";
1845 return;
1846 }
1847
1276 warn "reloading..."; 1848 warn "reloading...";
1277 1849
1850 local $FREEZE = 1;
1851 cf::emergency_save;
1852
1278 eval { 1853 eval {
1279 local $FREEZE = 1; 1854 # if anything goes wrong in here, we should simply crash as we already saved
1280
1281 cf::emergency_save;
1282 1855
1283 # cancel all watchers 1856 # cancel all watchers
1284 for (Event::all_watchers) { 1857 for (Event::all_watchers) {
1285 $_->cancel if $_->data & WF_AUTOCANCEL; 1858 $_->cancel if $_->data & WF_AUTOCANCEL;
1286 } 1859 }
1345 1918
1346 # reattach attachments to objects 1919 # reattach attachments to objects
1347 warn "reattach"; 1920 warn "reattach";
1348 _global_reattach; 1921 _global_reattach;
1349 }; 1922 };
1350 warn $@ if $@;
1351 1923
1352 warn "reloaded"; 1924 if ($@) {
1925 warn $@;
1926 warn "error while reloading, exiting.";
1927 exit 1;
1928 }
1929
1930 warn "reloaded successfully";
1353}; 1931};
1354 1932
1355sub perl_reload() { 1933#############################################################################
1356 _perl_reload; 1934
1935unless ($LINK_MAP) {
1936 $LINK_MAP = cf::map::new;
1937
1938 $LINK_MAP->width (41);
1939 $LINK_MAP->height (41);
1940 $LINK_MAP->alloc;
1941 $LINK_MAP->path ("{link}");
1942 $LINK_MAP->{path} = bless { path => "{link}" }, "cf::path";
1943 $LINK_MAP->in_memory (MAP_IN_MEMORY);
1944
1945 # dirty hack because... archetypes are not yet loaded
1946 Event->timer (
1947 after => 2,
1948 cb => sub {
1949 $_[0]->w->cancel;
1950
1951 # provide some exits "home"
1952 my $exit = cf::object::new "exit";
1953
1954 $exit->slaying ($EMERGENCY_POSITION->[0]);
1955 $exit->stats->hp ($EMERGENCY_POSITION->[1]);
1956 $exit->stats->sp ($EMERGENCY_POSITION->[2]);
1957
1958 $LINK_MAP->insert ($exit->clone, 19, 19);
1959 $LINK_MAP->insert ($exit->clone, 19, 20);
1960 $LINK_MAP->insert ($exit->clone, 19, 21);
1961 $LINK_MAP->insert ($exit->clone, 20, 19);
1962 $LINK_MAP->insert ($exit->clone, 20, 21);
1963 $LINK_MAP->insert ($exit->clone, 21, 19);
1964 $LINK_MAP->insert ($exit->clone, 21, 20);
1965 $LINK_MAP->insert ($exit->clone, 21, 21);
1966
1967 $exit->destroy;
1968 });
1969
1970 $LINK_MAP->{deny_save} = 1;
1971 $LINK_MAP->{deny_reset} = 1;
1972
1973 $cf::MAP{$LINK_MAP->path} = $LINK_MAP;
1357} 1974}
1358 1975
1359register "<global>", __PACKAGE__; 1976register "<global>", __PACKAGE__;
1360 1977
1361register_command "perl-reload" => sub { 1978register_command "reload" => sub {
1362 my ($who, $arg) = @_; 1979 my ($who, $arg) = @_;
1363 1980
1364 if ($who->flag (FLAG_WIZ)) { 1981 if ($who->flag (FLAG_WIZ)) {
1982 $who->message ("start of reload.");
1983 reload;
1365 $who->message ("reloading..."); 1984 $who->message ("end of reload.");
1366 _perl_reload;
1367 } 1985 }
1368}; 1986};
1369 1987
1370unshift @INC, $LIBDIR; 1988unshift @INC, $LIBDIR;
1371 1989
1390 }, 2008 },
1391); 2009);
1392 2010
1393IO::AIO::max_poll_time $TICK * 0.2; 2011IO::AIO::max_poll_time $TICK * 0.2;
1394 2012
2013Event->io (
1395Event->io (fd => IO::AIO::poll_fileno, 2014 fd => IO::AIO::poll_fileno,
1396 poll => 'r', 2015 poll => 'r',
1397 prio => 5, 2016 prio => 5,
1398 data => WF_AUTOCANCEL, 2017 data => WF_AUTOCANCEL,
1399 cb => \&IO::AIO::poll_cb); 2018 cb => \&IO::AIO::poll_cb,
2019);
1400 2020
1401# we must not ever block the main coroutine 2021Event->timer (
1402$Coro::idle = sub { 2022 data => WF_AUTOCANCEL,
1403 #Carp::cluck "FATAL: Coro::idle was called, major BUG\n";#d# 2023 after => 0,
1404 warn "FATAL: Coro::idle was called, major BUG\n"; 2024 interval => 10,
2025 cb => sub {
1405 (Coro::unblock_sub { 2026 (Coro::unblock_sub {
1406 Event::one_event; 2027 write_runtime
2028 or warn "ERROR: unable to write runtime file: $!";
1407 })->(); 2029 })->();
1408}; 2030 },
2031);
1409 2032
14101 20331
1411 2034

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines