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.119 by root, Tue Jan 2 08:26:42 2007 UTC vs.
Revision 1.139 by root, Fri Jan 5 19:12:03 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;
26use Event; $Event::Eval = 1; # no idea why this is required, but it is 26use Event; $Event::Eval = 1; # no idea why this is required, but it is
27 27
28# work around bug in YAML::Syck - bad news for perl6, will it be as broken wrt. unicode? 28# work around bug in YAML::Syck - bad news for perl6, will it be as broken wrt. unicode?
29$YAML::Syck::ImplicitUnicode = 1; 29$YAML::Syck::ImplicitUnicode = 1;
30 30
31$Coro::main->prio (2); # run main coroutine ("the server") with very high priority 31$Coro::main->prio (Coro::PRIO_MAX); # run main coroutine ("the server") with very high priority
32 32
33sub WF_AUTOCANCEL () { 1 } # automatically cancel this watcher on reload 33sub WF_AUTOCANCEL () { 1 } # automatically cancel this watcher on reload
34 34
35our %COMMAND = (); 35our %COMMAND = ();
36our %COMMAND_TIME = (); 36our %COMMAND_TIME = ();
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
219 while ($busy) { 267 while ($busy) {
220 Coro::cede_notself; 268 unless (Coro::cede) {
221 Event::one_event unless Coro::nready; 269 Coro::nready ? Event::one_event 0 : Event::one_event;
270 Coro::cede_notself unless Coro::cede;
271 }
222 } 272 }
223 273
224 wantarray ? @res : $res[0] 274 wantarray ? @res : $res[0]
225 } else { 275 } else {
226 # we are in another coroutine, how wonderful, everything just works 276 # we are in another coroutine, how wonderful, everything just works
229 } 279 }
230} 280}
231 281
232=item $coro = cf::coro { BLOCK } 282=item $coro = cf::coro { BLOCK }
233 283
234Creates and returns a new coro. This coro is automcatially being canceled 284Creates (and readies) and returns a new coro. This coro is automcatially
235when the extension calling this is being unloaded. 285being canceled when the extension calling this is being unloaded.
236 286
237=cut 287=cut
238 288
239sub coro(&) { 289sub coro(&) {
240 my $cb = shift; 290 my $cb = shift;
241 291
242 my $coro; $coro = async { 292 my $coro = &Coro::async_pool ($cb);
243 eval {
244 $cb->();
245 };
246 warn $@ if $@;
247 };
248 293
249 $coro->on_destroy (sub { 294 $coro->on_destroy (sub {
250 delete $EXT_CORO{$coro+0}; 295 delete $EXT_CORO{$coro+0};
251 }); 296 });
252 $EXT_CORO{$coro+0} = $coro; 297 $EXT_CORO{$coro+0} = $coro;
1062package cf::map; 1107package cf::map;
1063 1108
1064use Fcntl; 1109use Fcntl;
1065use Coro::AIO; 1110use Coro::AIO;
1066 1111
1067our $MAX_RESET = 7200; 1112our $MAX_RESET = 3600;
1068our $DEFAULT_RESET = 3600; 1113our $DEFAULT_RESET = 3000;
1069 1114
1070sub generate_random_map { 1115sub generate_random_map {
1071 my ($path, $rmp) = @_; 1116 my ($path, $rmp) = @_;
1072 1117
1073 # mit "rum" bekleckern, nicht 1118 # mit "rum" bekleckern, nicht
1089# and all this just because we cannot iterate over 1134# and all this just because we cannot iterate over
1090# all maps in C++... 1135# all maps in C++...
1091sub change_all_map_light { 1136sub change_all_map_light {
1092 my ($change) = @_; 1137 my ($change) = @_;
1093 1138
1094 $_->change_map_light ($change) for values %cf::MAP; 1139 $_->change_map_light ($change)
1140 for grep $_->outdoor, values %cf::MAP;
1095} 1141}
1096 1142
1097sub try_load_header($) { 1143sub try_load_header($) {
1098 my ($path) = @_; 1144 my ($path) = @_;
1099 1145
1102 or return; 1148 or return;
1103 1149
1104 my $map = cf::map::new 1150 my $map = cf::map::new
1105 or return; 1151 or return;
1106 1152
1153 # for better error messages only, will be overwritten
1154 $map->path ($path);
1155
1107 $map->load_header ($path) 1156 $map->load_header ($path)
1108 or return; 1157 or return;
1109 1158
1110 $map->{load_path} = $path; 1159 $map->{load_path} = $path;
1111 1160
1112 $map 1161 $map
1113} 1162}
1114 1163
1164sub find;
1115sub find_map { 1165sub find {
1116 my ($path, $origin) = @_; 1166 my ($path, $origin) = @_;
1117 1167
1118 #warn "find_map<$path,$origin>\n";#d# 1168 #warn "find<$path,$origin>\n";#d#
1119 1169
1120 $path = new cf::path $path, $origin && $origin->path; 1170 $path = new cf::path $path, $origin && $origin->path;
1121 my $key = $path->as_string; 1171 my $key = $path->as_string;
1122 1172
1173 cf::lock_wait "map_find:$key";
1174
1123 $cf::MAP{$key} || do { 1175 $cf::MAP{$key} || do {
1176 my $guard = cf::lock_acquire "map_find:$key";
1177
1124 # do it the slow way 1178 # do it the slow way
1125 my $map = try_load_header $path->save_path; 1179 my $map = try_load_header $path->save_path;
1126 1180
1181 Coro::cede;
1182
1127 if ($map) { 1183 if ($map) {
1184 $map->last_access ((delete $map->{last_access})
1185 || $cf::RUNTIME); #d#
1128 # safety 1186 # safety
1129 $map->{instantiate_time} = $cf::RUNTIME 1187 $map->{instantiate_time} = $cf::RUNTIME
1130 if $map->{instantiate_time} > $cf::RUNTIME; 1188 if $map->{instantiate_time} > $cf::RUNTIME;
1131 } else { 1189 } else {
1132 if (my $rmp = $path->random_map_params) { 1190 if (my $rmp = $path->random_map_params) {
1137 1195
1138 $map or return; 1196 $map or return;
1139 1197
1140 $map->{load_original} = 1; 1198 $map->{load_original} = 1;
1141 $map->{instantiate_time} = $cf::RUNTIME; 1199 $map->{instantiate_time} = $cf::RUNTIME;
1200 $map->last_access ($cf::RUNTIME);
1142 $map->instantiate; 1201 $map->instantiate;
1143 1202
1144 # per-player maps become, after loading, normal maps 1203 # per-player maps become, after loading, normal maps
1145 $map->per_player (0) if $path->{user_rel}; 1204 $map->per_player (0) if $path->{user_rel};
1146 } 1205 }
1147 #Coro::Timer::sleep 1;#d#
1148 1206
1149 $map->path ($key); 1207 $map->path ($key);
1150 $map->{path} = $path; 1208 $map->{path} = $path;
1151 $map->{last_save} = $cf::RUNTIME; 1209 $map->{last_save} = $cf::RUNTIME;
1152 $map->last_access ($cf::RUNTIME); 1210
1211 Coro::cede;
1153 1212
1154 if ($map->should_reset) { 1213 if ($map->should_reset) {
1155 $map->reset; 1214 $map->reset;
1215 undef $guard;
1156 $map = find_map $path; 1216 $map = find $path
1217 or return;
1157 } 1218 }
1158 1219
1159 $cf::MAP{$key} = $map 1220 $cf::MAP{$key} = $map
1160 } 1221 }
1161} 1222}
1162 1223
1163sub load { 1224sub load {
1164 my ($self) = @_; 1225 my ($self) = @_;
1165 1226
1227 my $path = $self->{path};
1228 my $guard = cf::lock_acquire "map_load:" . $path->as_string;
1229
1166 return if $self->in_memory != cf::MAP_SWAPPED; 1230 return if $self->in_memory != cf::MAP_SWAPPED;
1167 1231
1168 $self->in_memory (cf::MAP_LOADING); 1232 $self->in_memory (cf::MAP_LOADING);
1169
1170 my $path = $self->{path};
1171 1233
1172 $self->alloc; 1234 $self->alloc;
1173 $self->load_objects ($self->{load_path}, 1) 1235 $self->load_objects ($self->{load_path}, 1)
1174 or return; 1236 or return;
1175 1237
1181 if (aio_open $uniq, O_RDONLY, 0) { 1243 if (aio_open $uniq, O_RDONLY, 0) {
1182 $self->clear_unique_items; 1244 $self->clear_unique_items;
1183 $self->load_objects ($uniq, 0); 1245 $self->load_objects ($uniq, 0);
1184 } 1246 }
1185 } 1247 }
1248
1249 Coro::cede;
1186 1250
1187 # now do the right thing for maps 1251 # now do the right thing for maps
1188 $self->link_multipart_objects; 1252 $self->link_multipart_objects;
1189 1253
1190 if ($self->{path}->is_style_map) { 1254 if ($self->{path}->is_style_map) {
1198 $self->difficulty ($self->estimate_difficulty) 1262 $self->difficulty ($self->estimate_difficulty)
1199 unless $self->difficulty; 1263 unless $self->difficulty;
1200 $self->activate; 1264 $self->activate;
1201 } 1265 }
1202 1266
1267 Coro::cede;
1268
1203 $self->in_memory (cf::MAP_IN_MEMORY); 1269 $self->in_memory (cf::MAP_IN_MEMORY);
1204} 1270}
1205 1271
1206sub load_map_sync { 1272sub find_sync {
1207 my ($path, $origin) = @_; 1273 my ($path, $origin) = @_;
1208 1274
1209 #warn "load_map_sync<$path, $origin>\n";#d#
1210
1211 cf::sync_job { 1275 cf::sync_job {
1212 my $map = cf::map::find_map $path, $origin 1276 my $map = cf::map::find $path, $origin;
1213 or return;
1214 $map->load;
1215 $map 1277 $map
1216 } 1278 }
1279}
1280
1281sub do_load_sync {
1282 my ($map) = @_;
1283
1284 cf::sync_job { $map->load };
1217} 1285}
1218 1286
1219sub save { 1287sub save {
1220 my ($self) = @_; 1288 my ($self) = @_;
1221 1289
1290 my $lock = cf::lock_acquire "map_data:" . $self->path;
1291
1222 $self->{last_save} = $cf::RUNTIME; 1292 $self->{last_save} = $cf::RUNTIME;
1223 1293
1224 return unless $self->dirty; 1294 return unless $self->dirty;
1225 1295
1226 my $save = $self->{path}->save_path; utf8::encode $save; 1296 my $save = $self->{path}->save_path; utf8::encode $save;
1227 my $uniq = $self->{path}->uniq_path; utf8::encode $uniq; 1297 my $uniq = $self->{path}->uniq_path; utf8::encode $uniq;
1228 1298
1229 $self->{load_path} = $save; 1299 $self->{load_path} = $save;
1230 1300
1231 return if $self->{deny_save}; 1301 return if $self->{deny_save};
1302
1303 local $self->{last_access} = $self->last_access;#d#
1232 1304
1233 if ($uniq) { 1305 if ($uniq) {
1234 $self->save_objects ($save, cf::IO_HEADER | cf::IO_OBJECTS); 1306 $self->save_objects ($save, cf::IO_HEADER | cf::IO_OBJECTS);
1235 $self->save_objects ($uniq, cf::IO_UNIQUES); 1307 $self->save_objects ($uniq, cf::IO_UNIQUES);
1236 } else { 1308 } else {
1239} 1311}
1240 1312
1241sub swap_out { 1313sub swap_out {
1242 my ($self) = @_; 1314 my ($self) = @_;
1243 1315
1316 # save first because save cedes
1317 $self->save;
1318
1319 my $lock = cf::lock_acquire "map_data:" . $self->path;
1320
1244 return if $self->players; 1321 return if $self->players;
1245 return if $self->in_memory != cf::MAP_IN_MEMORY; 1322 return if $self->in_memory != cf::MAP_IN_MEMORY;
1246 return if $self->{deny_save}; 1323 return if $self->{deny_save};
1247 1324
1248 $self->save;
1249 $self->clear; 1325 $self->clear;
1250 $self->in_memory (cf::MAP_SWAPPED); 1326 $self->in_memory (cf::MAP_SWAPPED);
1251} 1327}
1252 1328
1253sub reset_at { 1329sub reset_at {
1291} 1367}
1292 1368
1293sub reset { 1369sub reset {
1294 my ($self) = @_; 1370 my ($self) = @_;
1295 1371
1372 my $lock = cf::lock_acquire "map_data:" . $self->path;
1373
1296 return if $self->players; 1374 return if $self->players;
1297 return if $self->{path}{user_rel};#d# 1375 return if $self->{path}{user_rel};#d#
1298 1376
1299 warn "resetting map ", $self->path;#d# 1377 warn "resetting map ", $self->path;#d#
1300 1378
1319 1397
1320sub customise_for { 1398sub customise_for {
1321 my ($map, $ob) = @_; 1399 my ($map, $ob) = @_;
1322 1400
1323 if ($map->per_player) { 1401 if ($map->per_player) {
1324 return cf::map::find_map "~" . $ob->name . "/" . $map->{path}{path}; 1402 return cf::map::find "~" . $ob->name . "/" . $map->{path}{path};
1325 } 1403 }
1326 1404
1327 $map 1405 $map
1328} 1406}
1329 1407
1330sub emergency_save { 1408sub emergency_save {
1331 local $cf::FREEZE = 1; 1409 my $freeze_guard = cf::freeze_mainloop;
1332 1410
1333 warn "enter emergency map save\n"; 1411 warn "enter emergency map save\n";
1334 1412
1335 cf::sync_job { 1413 cf::sync_job {
1336 warn "begin emergency map save\n"; 1414 warn "begin emergency map save\n";
1408=cut 1486=cut
1409 1487
1410sub cf::object::player::enter_link { 1488sub cf::object::player::enter_link {
1411 my ($self) = @_; 1489 my ($self) = @_;
1412 1490
1491 $self->deactivate_recursive;
1492
1413 return if $self->map == $LINK_MAP; 1493 return if $self->map == $LINK_MAP;
1414 1494
1415 $self->{_link_pos} = [$self->map->{path}, $self->x, $self->y] 1495 $self->{_link_pos} ||= [$self->map->{path}, $self->x, $self->y]
1416 if $self->map; 1496 if $self->map;
1417 1497
1418 $self->enter_map ($LINK_MAP, 20, 20); 1498 $self->enter_map ($LINK_MAP, 20, 20);
1419 $self->deactivate_recursive;
1420} 1499}
1421 1500
1422sub cf::object::player::leave_link { 1501sub cf::object::player::leave_link {
1423 my ($self, $map, $x, $y) = @_; 1502 my ($self, $map, $x, $y) = @_;
1424 1503
1425 my $link_pos = delete $self->{_link_pos}; 1504 my $link_pos = delete $self->{_link_pos};
1426 1505
1427 unless ($map) { 1506 unless ($map) {
1428 # restore original map position 1507 # restore original map position
1429 ($map, $x, $y) = @{ $link_pos || [] }; 1508 ($map, $x, $y) = @{ $link_pos || [] };
1430 $map = cf::map::find_map $map; 1509 $map = cf::map::find $map;
1431 1510
1432 unless ($map) { 1511 unless ($map) {
1433 ($map, $x, $y) = @$EMERGENCY_POSITION; 1512 ($map, $x, $y) = @$EMERGENCY_POSITION;
1434 $map = cf::map::find_map $map 1513 $map = cf::map::find $map
1435 or die "FATAL: cannot load emergency map\n"; 1514 or die "FATAL: cannot load emergency map\n";
1436 } 1515 }
1437 } 1516 }
1438 1517
1439 ($x, $y) = (-1, -1) 1518 ($x, $y) = (-1, -1)
1447 1526
1448 $self->activate_recursive; 1527 $self->activate_recursive;
1449 $self->enter_map ($map, $x, $y); 1528 $self->enter_map ($map, $x, $y);
1450} 1529}
1451 1530
1531cf::player->attach (
1532 on_logout => sub {
1533 my ($pl) = @_;
1534
1535 # abort map switching before logout
1536 if ($pl->ob->{_link_pos}) {
1537 cf::sync_job {
1538 $pl->ob->leave_link
1539 };
1540 }
1541 },
1542 on_login => sub {
1543 my ($pl) = @_;
1544
1545 # try to abort aborted map switching on player login :)
1546 # should happen only on crashes
1547 if ($pl->ob->{_link_pos}) {
1548
1549 $pl->ob->enter_link;
1550 (Coro::async_pool {
1551 # we need this sleep as the login has a concurrent enter_exit running
1552 # and this sleep increases chances of the player not ending up in scorn
1553 Coro::Timer::sleep 1;
1554 $pl->ob->leave_link;
1555 })->prio (2);
1556 }
1557 },
1558);
1559
1452=item $player_object->goto_map ($path, $x, $y) 1560=item $player_object->goto ($path, $x, $y)
1453 1561
1454=cut 1562=cut
1455 1563
1456sub cf::object::player::goto_map { 1564sub cf::object::player::goto {
1457 my ($self, $path, $x, $y) = @_; 1565 my ($self, $path, $x, $y) = @_;
1458 1566
1459 $self->enter_link; 1567 $self->enter_link;
1460 1568
1461 (Coro::async { 1569 (Coro::async_pool {
1462 $path = new cf::path $path; 1570 $path = new cf::path $path;
1463 1571
1464 my $map = cf::map::find_map $path->as_string; 1572 my $map = cf::map::find $path->as_string;
1465 $map = $map->customise_for ($self) if $map; 1573 $map = $map->customise_for ($self) if $map;
1466 1574
1467# warn "entering ", $map->path, " at ($x, $y)\n" 1575# warn "entering ", $map->path, " at ($x, $y)\n"
1468# if $map; 1576# if $map;
1469 1577
1528 1636
1529 return unless $self->type == cf::PLAYER; 1637 return unless $self->type == cf::PLAYER;
1530 1638
1531 $self->enter_link; 1639 $self->enter_link;
1532 1640
1533 (Coro::async { 1641 (Coro::async_pool {
1642 $self->deactivate_recursive; # just to be sure
1534 unless (eval { 1643 unless (eval {
1535
1536 prepare_random_map $exit 1644 prepare_random_map $exit
1537 if $exit->slaying eq "/!"; 1645 if $exit->slaying eq "/!";
1538 1646
1539 my $path = new cf::path $exit->slaying, $exit->map && $exit->map->path; 1647 my $path = new cf::path $exit->slaying, $exit->map && $exit->map->path;
1540 $self->goto_map ($path, $exit->stats->hp, $exit->stats->sp); 1648 $self->goto ($path, $exit->stats->hp, $exit->stats->sp);
1541 1649
1542 1; 1650 1;
1543 }) { 1651 }) {
1544 $self->message ("Something went wrong deep within the crossfire server. " 1652 $self->message ("Something went wrong deep within the crossfire server. "
1545 . "I'll try to bring you back to the map you were before. " 1653 . "I'll try to bring you back to the map you were before. "
1602 on_reply => sub { 1710 on_reply => sub {
1603 my ($ns, $msg) = @_; 1711 my ($ns, $msg) = @_;
1604 1712
1605 # this weird shuffling is so that direct followup queries 1713 # this weird shuffling is so that direct followup queries
1606 # get handled first 1714 # get handled first
1607 my $queue = delete $ns->{query_queue}; 1715 my $queue = delete $ns->{query_queue}
1716 or return; # be conservative, not sure how that can happen, but we saw a crash here
1608 1717
1609 (shift @$queue)->[1]->($msg); 1718 (shift @$queue)->[1]->($msg);
1610 1719
1611 push @{ $ns->{query_queue} }, @$queue; 1720 push @{ $ns->{query_queue} }, @$queue;
1612 1721
1629=cut 1738=cut
1630 1739
1631sub cf::client::coro { 1740sub cf::client::coro {
1632 my ($self, $cb) = @_; 1741 my ($self, $cb) = @_;
1633 1742
1634 my $coro; $coro = async { 1743 my $coro = &Coro::async_pool ($cb);
1635 eval {
1636 $cb->();
1637 };
1638 warn $@ if $@;
1639 };
1640 1744
1641 $coro->on_destroy (sub { 1745 $coro->on_destroy (sub {
1642 delete $self->{_coro}{$coro+0}; 1746 delete $self->{_coro}{$coro+0};
1643 }); 1747 });
1644 1748
1816 1920
1817{ 1921{
1818 my $path = cf::localdir . "/database.pst"; 1922 my $path = cf::localdir . "/database.pst";
1819 1923
1820 sub db_load() { 1924 sub db_load() {
1821 warn "loading database $path\n";#d# remove later
1822 $DB = stat $path ? Storable::retrieve $path : { }; 1925 $DB = stat $path ? Storable::retrieve $path : { };
1823 } 1926 }
1824 1927
1825 my $pid; 1928 my $pid;
1826 1929
1827 sub db_save() { 1930 sub db_save() {
1828 warn "saving database $path\n";#d# remove later
1829 waitpid $pid, 0 if $pid; 1931 waitpid $pid, 0 if $pid;
1830 if (0 == ($pid = fork)) { 1932 if (0 == ($pid = fork)) {
1831 $DB->{_meta}{version} = 1; 1933 $DB->{_meta}{version} = 1;
1832 Storable::nstore $DB, "$path~"; 1934 Storable::nstore $DB, "$path~";
1833 rename "$path~", $path; 1935 rename "$path~", $path;
1881 open my $fh, "<:utf8", cf::confdir . "/config" 1983 open my $fh, "<:utf8", cf::confdir . "/config"
1882 or return; 1984 or return;
1883 1985
1884 local $/; 1986 local $/;
1885 *CFG = YAML::Syck::Load <$fh>; 1987 *CFG = YAML::Syck::Load <$fh>;
1988
1989 $EMERGENCY_POSITION = $CFG{emergency_position} || ["/world/world_105_115", 5, 37];
1990
1991 $cf::map::MAX_RESET = $CFG{map_max_reset} if exists $CFG{map_max_reset};
1992 $cf::map::DEFAULT_RESET = $CFG{map_default_reset} if exists $CFG{map_default_reset};
1993
1994 if (exists $CFG{mlockall}) {
1995 eval {
1996 $CFG{mlockall} ? &mlockall : &munlockall
1997 and die "WARNING: m(un)lockall failed: $!\n";
1998 };
1999 warn $@ if $@;
2000 }
1886} 2001}
1887 2002
1888sub main { 2003sub main {
1889 # we must not ever block the main coroutine 2004 # we must not ever block the main coroutine
1890 local $Coro::idle = sub { 2005 local $Coro::idle = sub {
1891 Carp::cluck "FATAL: Coro::idle was called, major BUG, use cf::sync_job!\n";#d# 2006 Carp::cluck "FATAL: Coro::idle was called, major BUG, use cf::sync_job!\n";#d#
1892 (Coro::unblock_sub { 2007 Coro::async_pool { Event::one_event };
1893 Event::one_event;
1894 })->();
1895 }; 2008 };
1896 2009
1897 cfg_load; 2010 cfg_load;
1898 db_load; 2011 db_load;
1899 load_extensions; 2012 load_extensions;
1910 return; 2023 return;
1911 } 2024 }
1912 2025
1913 warn "reloading..."; 2026 warn "reloading...";
1914 2027
1915 local $FREEZE = 1; 2028 my $guard = freeze_mainloop;
1916 cf::emergency_save; 2029 cf::emergency_save;
1917 2030
1918 eval { 2031 eval {
1919 # if anything goes wrong in here, we should simply crash as we already saved 2032 # if anything goes wrong in here, we should simply crash as we already saved
1920 2033
2056 reentrant => 0, 2169 reentrant => 0,
2057 prio => 0, 2170 prio => 0,
2058 at => $NEXT_TICK || $TICK, 2171 at => $NEXT_TICK || $TICK,
2059 data => WF_AUTOCANCEL, 2172 data => WF_AUTOCANCEL,
2060 cb => sub { 2173 cb => sub {
2061 unless ($FREEZE) {
2062 cf::server_tick; # one server iteration 2174 cf::server_tick; # one server iteration
2063 $RUNTIME += $TICK; 2175 $RUNTIME += $TICK;
2064 }
2065
2066 $NEXT_TICK += $TICK; 2176 $NEXT_TICK += $TICK;
2067 2177
2068 # if we are delayed by four ticks or more, skip them all 2178 # if we are delayed by four ticks or more, skip them all
2069 $NEXT_TICK = Event::time if Event::time >= $NEXT_TICK + $TICK * 4; 2179 $NEXT_TICK = Event::time if Event::time >= $NEXT_TICK + $TICK * 4;
2070 2180
2093 or warn "ERROR: unable to write runtime file: $!"; 2203 or warn "ERROR: unable to write runtime file: $!";
2094 })->(); 2204 })->();
2095 }, 2205 },
2096); 2206);
2097 2207
2208END { cf::emergency_save }
2209
20981 22101
2099 2211

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines