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.110 by root, Mon Jan 1 11:21:55 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->{instantiate_time} = $cf::RUNTIME;
1126 $map->instantiate;
1127
1128 # per-player maps become, after loading, normal maps
1129 $map->per_player (0) if $path->{user_rel};
1130 }
1131
1132 $map->path ($key);
1133 $map->{path} = $path;
1134 $map->last_access ($cf::RUNTIME);
1135
1136 $map->reset if $map->should_reset;
1137
1138 $cf::MAP{$key} = $map
1139 }
1140}
1141
1142sub load {
1143 my ($self) = @_;
1144
1145 return if $self->in_memory != cf::MAP_SWAPPED;
1146
1147 $self->in_memory (cf::MAP_LOADING);
1148
1149 my $path = $self->{path};
1150
1151 $self->alloc;
1152 $self->load_objects ($self->{load_path}, 1)
1153 or return;
1154
1155 if (my $uniq = $path->uniq_path) {
1156 utf8::encode $uniq;
1157 if (aio_open $uniq, O_RDONLY, 0) {
1158 $self->clear_unique_items;
1159 $self->load_objects ($uniq, 0);
1160 }
1161 }
1162
1163 # now do the right thing for maps
1164 $self->link_multipart_objects;
1165
1166 if ($self->{path}->is_style_map) {
1167 $self->{deny_save} = 1;
1168 $self->{deny_reset} = 1;
1169 } else {
1170 $self->fix_auto_apply;
1171 $self->decay_objects;
1172 $self->update_buttons;
1173 $self->set_darkness_map;
1174 $self->difficulty ($self->estimate_difficulty)
1175 unless $self->difficulty;
1176 $self->activate;
1177 }
1178
1179 $self->in_memory (cf::MAP_IN_MEMORY);
1180}
1181
1182sub load_map_sync {
1183 my ($path, $origin) = @_;
1184
1185 #warn "load_map_sync<$path, $origin>\n";#d#
1186
1187 cf::sync_job {
1188 my $map = cf::map::find_map $path, $origin
1189 or return;
1190 $map->load;
1191 $map
1192 }
1193}
1194
1195sub save {
1196 my ($self) = @_;
1197
1198 my $save = $self->{path}->save_path; utf8::encode $save;
1199 my $uniq = $self->{path}->uniq_path; utf8::encode $uniq;
1200
1201 $self->{last_save} = $cf::RUNTIME;
1202
1203 return unless $self->dirty;
1204
1205 $self->{load_path} = $save;
1206
1207 return if $self->{deny_save};
1208
1209 warn "saving map ", $self->path;
1210
1211 if ($uniq) {
1212 $self->save_objects ($save, cf::IO_HEADER | cf::IO_OBJECTS);
1213 $self->save_objects ($uniq, cf::IO_UNIQUES);
1214 } else {
1215 $self->save_objects ($save, cf::IO_HEADER | cf::IO_OBJECTS | cf::IO_UNIQUES);
1216 }
1217}
1218
1219sub swap_out {
1220 my ($self) = @_;
1221
1222 return if $self->players;
1223 return if $self->in_memory != cf::MAP_IN_MEMORY;
1224 return if $self->{deny_save};
1225
1226 $self->save;
1227 $self->clear;
1228 $self->in_memory (cf::MAP_SWAPPED);
1229}
1230
1231sub should_reset {
1232 my ($map) = @_;
1233
1234 # TODO: safety, remove and allow resettable per-player maps
1235 return if $map->{path}{user_rel};#d#
1236 return if $map->{deny_reset};
1237 #return unless $map->reset_timeout;
1238
1239 my $time = $map->fixed_resettime ? $map->{instantiate_time} : $map->last_access;
1240
1241 $time + ($map->reset_timeout || $DEFAULT_RESET) < $cf::RUNTIME
1242}
1243
1244sub reset {
1245 my ($self) = @_;
1246
1247 return if $self->players;
1248 return if $self->{path}{user_rel};#d#
1249
1250 warn "resetting map ", $self->path;#d#
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 $_->clear_links_to ($self) for values %cf::MAP;
1257
1258 $self->clear;
1259 $self->in_memory (cf::MAP_SWAPPED);
1260 utf8::encode ($self->{load_path} = $self->{path}->load_path);
1261}
1262
1263sub customise_for {
1264 my ($map, $ob) = @_;
1265
1266 if ($map->per_player) {
1267 return cf::map::find_map "~" . $ob->name . "/" . $map->{path}{path};
1268 }
1269
1270 $map
1271}
1272
1273sub emergency_save {
1274 local $cf::FREEZE = 1;
1275
1276 warn "enter emergency map save\n";
1277
1278 cf::sync_job {
1279 warn "begin emergency map save\n";
1280 $_->save for values %cf::MAP;
1281 };
1282
1283 warn "end emergency map save\n";
1284}
1285
1286package cf;
1287
1288=back
1289
1290
888=head3 cf::object::player 1291=head3 cf::object::player
889 1292
890=over 4 1293=over 4
891 1294
892=item $player_object->reply ($npc, $msg[, $flags]) 1295=item $player_object->reply ($npc, $msg[, $flags])
925 1328
926 $self->flag (cf::FLAG_WIZ) || 1329 $self->flag (cf::FLAG_WIZ) ||
927 (ref $cf::CFG{"may_$access"} 1330 (ref $cf::CFG{"may_$access"}
928 ? scalar grep $self->name eq $_, @{$cf::CFG{"may_$access"}} 1331 ? scalar grep $self->name eq $_, @{$cf::CFG{"may_$access"}}
929 : $cf::CFG{"may_$access"}) 1332 : $cf::CFG{"may_$access"})
1333}
1334
1335sub cf::object::player::enter_link {
1336 my ($self) = @_;
1337
1338 return if $self->map == $LINK_MAP;
1339
1340 $self->{_link_pos} = [$self->map->{path}, $self->x, $self->y]
1341 if $self->map;
1342
1343 $self->enter_map ($LINK_MAP, 20, 20);
1344 $self->deactivate_recursive;
1345}
1346
1347sub cf::object::player::leave_link {
1348 my ($self, $map, $x, $y) = @_;
1349
1350 my $link_pos = delete $self->{_link_pos};
1351
1352 unless ($map) {
1353 $self->message ("The exit is closed", cf::NDI_UNIQUE | cf::NDI_RED);
1354
1355 # restore original map position
1356 ($map, $x, $y) = @{ $link_pos || [] };
1357 $map = cf::map::find_map $map;
1358
1359 unless ($map) {
1360 ($map, $x, $y) = @$EMERGENCY_POSITION;
1361 $map = cf::map::find_map $map
1362 or die "FATAL: cannot load emergency map\n";
1363 }
1364 }
1365
1366 ($x, $y) = (-1, -1)
1367 unless (defined $x) && (defined $y);
1368
1369 # use -1 or undef as default coordinates, not 0, 0
1370 ($x, $y) = ($map->enter_x, $map->enter_y)
1371 if $x <=0 && $y <= 0;
1372
1373 $map->load;
1374
1375 $self->activate_recursive;
1376 $self->enter_map ($map, $x, $y);
1377}
1378
1379=item $player_object->goto_map ($map, $x, $y)
1380
1381=cut
1382
1383sub cf::object::player::goto_map {
1384 my ($self, $path, $x, $y) = @_;
1385
1386 $self->enter_link;
1387
1388 (Coro::async {
1389 $path = new cf::path $path;
1390
1391 my $map = cf::map::find_map $path->as_string;
1392 $map = $map->customise_for ($self) if $map;
1393
1394 warn "entering ", $map->path, " at ($x, $y)\n"
1395 if $map;
1396
1397 $self->leave_link ($map, $x, $y);
1398 })->prio (1);
1399}
1400
1401=item $player_object->enter_exit ($exit_object)
1402
1403=cut
1404
1405sub parse_random_map_params {
1406 my ($spec) = @_;
1407
1408 my $rmp = { # defaults
1409 xsize => 10,
1410 ysize => 10,
1411 };
1412
1413 for (split /\n/, $spec) {
1414 my ($k, $v) = split /\s+/, $_, 2;
1415
1416 $rmp->{lc $k} = $v if (length $k) && (length $v);
1417 }
1418
1419 $rmp
1420}
1421
1422sub prepare_random_map {
1423 my ($exit) = @_;
1424
1425 # all this does is basically replace the /! path by
1426 # a new random map path (?random/...) with a seed
1427 # that depends on the exit object
1428
1429 my $rmp = parse_random_map_params $exit->msg;
1430
1431 if ($exit->map) {
1432 $rmp->{region} = $exit->map->region_name;
1433 $rmp->{origin_map} = $exit->map->path;
1434 $rmp->{origin_x} = $exit->x;
1435 $rmp->{origin_y} = $exit->y;
1436 }
1437
1438 $rmp->{random_seed} ||= $exit->random_seed;
1439
1440 my $data = cf::to_json $rmp;
1441 my $md5 = Digest::MD5::md5_hex $data;
1442
1443 if (my $fh = aio_open "$cf::RANDOM_MAPS/$md5.meta", O_WRONLY | O_CREAT, 0666) {
1444 aio_write $fh, 0, (length $data), $data, 0;
1445
1446 $exit->slaying ("?random/$md5");
1447 $exit->msg (undef);
1448 }
1449}
1450
1451sub cf::object::player::enter_exit {
1452 my ($self, $exit) = @_;
1453
1454 return unless $self->type == cf::PLAYER;
1455
1456 $self->enter_link;
1457
1458 (Coro::async {
1459 unless (eval {
1460
1461 prepare_random_map $exit
1462 if $exit->slaying eq "/!";
1463
1464 my $path = new cf::path $exit->slaying, $exit->map && $exit->map->path;
1465 $self->goto_map ($path, $exit->stats->hp, $exit->stats->sp);
1466
1467 1;
1468 }) {
1469 $self->message ("Something went wrong deep within the crossfire server. "
1470 . "I'll try to bring you back to the map you were before. "
1471 . "Please report this to the dungeon master",
1472 cf::NDI_UNIQUE | cf::NDI_RED);
1473
1474 warn "ERROR in enter_exit: $@";
1475 $self->leave_link;
1476 }
1477 })->prio (1);
930} 1478}
931 1479
932=head3 cf::client 1480=head3 cf::client
933 1481
934=over 4 1482=over 4
1261 local $/; 1809 local $/;
1262 *CFG = YAML::Syck::Load <$fh>; 1810 *CFG = YAML::Syck::Load <$fh>;
1263} 1811}
1264 1812
1265sub main { 1813sub main {
1814 # we must not ever block the main coroutine
1815 local $Coro::idle = sub {
1816 Carp::cluck "FATAL: Coro::idle was called, major BUG\n";#d#
1817 (Coro::unblock_sub {
1818 Event::one_event;
1819 })->();
1820 };
1821
1266 cfg_load; 1822 cfg_load;
1267 db_load; 1823 db_load;
1268 load_extensions; 1824 load_extensions;
1269 Event::loop; 1825 Event::loop;
1270} 1826}
1271 1827
1272############################################################################# 1828#############################################################################
1273# initialisation 1829# initialisation
1274 1830
1275sub _perl_reload() { 1831sub perl_reload() {
1832 # can/must only be called in main
1833 if ($Coro::current != $Coro::main) {
1834 warn "can only reload from main coroutine\n";
1835 return;
1836 }
1837
1276 warn "reloading..."; 1838 warn "reloading...";
1277 1839
1840 local $FREEZE = 1;
1841 cf::emergency_save;
1842
1278 eval { 1843 eval {
1279 local $FREEZE = 1; 1844 # if anything goes wrong in here, we should simply crash as we already saved
1280
1281 cf::emergency_save;
1282 1845
1283 # cancel all watchers 1846 # cancel all watchers
1284 for (Event::all_watchers) { 1847 for (Event::all_watchers) {
1285 $_->cancel if $_->data & WF_AUTOCANCEL; 1848 $_->cancel if $_->data & WF_AUTOCANCEL;
1286 } 1849 }
1345 1908
1346 # reattach attachments to objects 1909 # reattach attachments to objects
1347 warn "reattach"; 1910 warn "reattach";
1348 _global_reattach; 1911 _global_reattach;
1349 }; 1912 };
1350 warn $@ if $@;
1351 1913
1352 warn "reloaded"; 1914 if ($@) {
1915 warn $@;
1916 warn "error while reloading, exiting.";
1917 exit 1;
1918 }
1919
1920 warn "reloaded successfully";
1353}; 1921};
1354 1922
1355sub perl_reload() { 1923#############################################################################
1356 _perl_reload; 1924
1925unless ($LINK_MAP) {
1926 $LINK_MAP = cf::map::new;
1927
1928 $LINK_MAP->width (41);
1929 $LINK_MAP->height (41);
1930 $LINK_MAP->alloc;
1931 $LINK_MAP->path ("{link}");
1932 $LINK_MAP->{path} = bless { path => "{link}" }, "cf::path";
1933 $LINK_MAP->in_memory (MAP_IN_MEMORY);
1934
1935 # dirty hack because... archetypes are not yet loaded
1936 Event->timer (
1937 after => 2,
1938 cb => sub {
1939 $_[0]->w->cancel;
1940
1941 # provide some exits "home"
1942 my $exit = cf::object::new "exit";
1943
1944 $exit->slaying ($EMERGENCY_POSITION->[0]);
1945 $exit->stats->hp ($EMERGENCY_POSITION->[1]);
1946 $exit->stats->sp ($EMERGENCY_POSITION->[2]);
1947
1948 $LINK_MAP->insert ($exit->clone, 19, 19);
1949 $LINK_MAP->insert ($exit->clone, 19, 20);
1950 $LINK_MAP->insert ($exit->clone, 19, 21);
1951 $LINK_MAP->insert ($exit->clone, 20, 19);
1952 $LINK_MAP->insert ($exit->clone, 20, 21);
1953 $LINK_MAP->insert ($exit->clone, 21, 19);
1954 $LINK_MAP->insert ($exit->clone, 21, 20);
1955 $LINK_MAP->insert ($exit->clone, 21, 21);
1956
1957 $exit->destroy;
1958 });
1959
1960 $LINK_MAP->{deny_save} = 1;
1961 $LINK_MAP->{deny_reset} = 1;
1962
1963 $cf::MAP{$LINK_MAP->path} = $LINK_MAP;
1357} 1964}
1358 1965
1359register "<global>", __PACKAGE__; 1966register "<global>", __PACKAGE__;
1360 1967
1361register_command "perl-reload" => sub { 1968register_command "perl-reload" => sub {
1362 my ($who, $arg) = @_; 1969 my ($who, $arg) = @_;
1363 1970
1364 if ($who->flag (FLAG_WIZ)) { 1971 if ($who->flag (FLAG_WIZ)) {
1365 $who->message ("reloading..."); 1972 $who->message ("start of reload.");
1366 _perl_reload; 1973 perl_reload;
1974 $who->message ("end of reload.");
1367 } 1975 }
1368}; 1976};
1369 1977
1370unshift @INC, $LIBDIR; 1978unshift @INC, $LIBDIR;
1371 1979
1390 }, 1998 },
1391); 1999);
1392 2000
1393IO::AIO::max_poll_time $TICK * 0.2; 2001IO::AIO::max_poll_time $TICK * 0.2;
1394 2002
2003Event->io (
1395Event->io (fd => IO::AIO::poll_fileno, 2004 fd => IO::AIO::poll_fileno,
1396 poll => 'r', 2005 poll => 'r',
1397 prio => 5, 2006 prio => 5,
1398 data => WF_AUTOCANCEL, 2007 data => WF_AUTOCANCEL,
1399 cb => \&IO::AIO::poll_cb); 2008 cb => \&IO::AIO::poll_cb,
2009);
1400 2010
1401# we must not ever block the main coroutine 2011Event->timer (
1402$Coro::idle = sub { 2012 data => WF_AUTOCANCEL,
1403 #Carp::cluck "FATAL: Coro::idle was called, major BUG\n";#d# 2013 after => 0,
1404 warn "FATAL: Coro::idle was called, major BUG\n"; 2014 interval => 10,
2015 cb => sub {
1405 (Coro::unblock_sub { 2016 (Coro::unblock_sub {
1406 Event::one_event; 2017 write_runtime
2018 or warn "ERROR: unable to write runtime file: $!";
1407 })->(); 2019 })->();
1408}; 2020 },
2021);
1409 2022
14101 20231
1411 2024

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines