void GameLoop() { ProcessAsyncSaveFinish(); /* autosave game? */ if (_do_autosave) { _do_autosave = false; DoAutosave(); SetWindowDirty(WC_STATUS_BAR, 0); } /* switch game mode? */ if (_switch_mode != SM_NONE) { SwitchToMode(_switch_mode); _switch_mode = SM_NONE; } IncreaseSpriteLRU(); InteractiveRandom(); extern int _caret_timer; _caret_timer += 3; CursorTick(); #ifdef ENABLE_NETWORK /* Check for UDP stuff */ if (_network_available) NetworkUDPGameLoop(); if (_networking && !IsGeneratingWorld()) { /* Multiplayer */ NetworkGameLoop(); } else { if (_network_reconnect > 0 && --_network_reconnect == 0) { /* This means that we want to reconnect to the last host * We do this here, because it means that the network is really closed */ NetworkClientConnectGame(NetworkAddress(_settings_client.network.last_host, _settings_client.network.last_port), COMPANY_SPECTATOR); } /* Singleplayer */ StateGameLoop(); } /* Check chat messages roughly once a second. */ static uint check_message = 0; if (++check_message > 1000 / MILLISECONDS_PER_TICK) { check_message = 0; NetworkChatMessageLoop(); } #else StateGameLoop(); #endif /* ENABLE_NETWORK */ if (!_pause_mode && HasBit(_display_opt, DO_FULL_ANIMATION)) DoPaletteAnimations(); if (!_pause_mode || _game_mode == GM_EDITOR || _settings_game.construction.command_pause_level > CMDPL_NO_CONSTRUCTION) MoveAllTextEffects(); InputLoop(); _sound_driver->MainLoop(); MusicLoop(); }
/** * State controlling game loop. * The state must not be changed from anywhere but here. * That check is enforced in DoCommand. */ void StateGameLoop() { /* dont execute the state loop during pause */ if (_pause_mode != PM_UNPAUSED) { UpdateLandscapingLimits(); CallWindowTickEvent(); return; } if (IsGeneratingWorld()) return; ClearStorageChanges(false); if (_game_mode == GM_EDITOR) { RunTileLoop(); CallVehicleTicks(); CallLandscapeTick(); ClearStorageChanges(true); UpdateLandscapingLimits(); CallWindowTickEvent(); NewsLoop(); } else { if (_debug_desync_level > 2 && _date_fract == 0 && (_date & 0x1F) == 0) { /* Save the desync savegame if needed. */ char name[MAX_PATH]; snprintf(name, lengthof(name), "dmp_cmds_%08x_%08x.sav", _settings_game.game_creation.generation_seed, _date); SaveOrLoad(name, SL_SAVE, AUTOSAVE_DIR, false); } CheckCaches(); /* All these actions has to be done from OWNER_NONE * for multiplayer compatibility */ Backup<CompanyByte> cur_company(_current_company, OWNER_NONE, FILE_LINE); AnimateAnimatedTiles(); IncreaseDate(); RunTileLoop(); CallVehicleTicks(); CallLandscapeTick(); ClearStorageChanges(true); AI::GameLoop(); UpdateLandscapingLimits(); CallWindowTickEvent(); NewsLoop(); cur_company.Restore(); } assert(IsLocalCompany()); }
bool HasRoadTypesAvail(const CompanyID company, const RoadTypes rts) { RoadTypes avail_roadtypes; if (company == OWNER_TOWN || _game_mode == GM_EDITOR || IsGeneratingWorld()) { avail_roadtypes = ROADTYPES_ROAD; } else { Company *c = Company::GetIfValid(company); if (c == NULL) return false; avail_roadtypes = (RoadTypes)c->avail_roadtypes | ROADTYPES_ROAD; // road is available for always for everybody } return (rts & ~avail_roadtypes) == 0; }
/*! * Toplevel network safe docommand function for the current company. Must not be called recursively. * The callback is called when the command succeeded or failed. The parameters * tile, p1 and p2 are from the #CommandProc function. The paramater cmd is the command to execute. * The parameter my_cmd is used to indicate if the command is from a company or the server. * * @param tile The tile to perform a command on (see #CommandProc) * @param p1 Additional data for the command (see #CommandProc) * @param p2 Additional data for the command (see #CommandProc) * @param cmd The command to execute (a CMD_* value) * @param callback A callback function to call after the command is finished * @param text The text to pass * @param my_cmd indicator if the command is from a company or server (to display error messages for a user) * @return true if the command succeeded, else false */ bool DoCommandP(TileIndex tile, uint32 p1, uint32 p2, uint32 cmd, CommandCallback *callback, const char *text, bool my_cmd) { assert(_docommand_recursive == 0); CommandCost res, res2; int x = TileX(tile) * TILE_SIZE; int y = TileY(tile) * TILE_SIZE; _error_message = INVALID_STRING_ID; StringID error_part1 = GB(cmd, 16, 16); _additional_cash_required = 0; /* get pointer to command handler */ byte cmd_id = cmd & CMD_ID_MASK; assert(cmd_id < lengthof(_command_proc_table)); CommandProc *proc = _command_proc_table[cmd_id].proc; if (proc == NULL) return false; /* Command flags are used internally */ uint cmd_flags = GetCommandFlags(cmd); /* Flags get send to the DoCommand */ DoCommandFlag flags = CommandFlagsToDCFlags(cmd_flags); /* Do not even think about executing out-of-bounds tile-commands */ if (tile != 0 && (tile >= MapSize() || (!IsValidTile(tile) && (cmd_flags & CMD_ALL_TILES) == 0))) return false; /* Always execute server and spectator commands as spectator */ if (cmd_flags & (CMD_SPECTATOR | CMD_SERVER)) _current_company = COMPANY_SPECTATOR; CompanyID old_company = _current_company; /* If the company isn't valid it may only do server command or start a new company! * The server will ditch any server commands a client sends to it, so effectively * this guards the server from executing functions for an invalid company. */ if (_game_mode == GM_NORMAL && (cmd_flags & (CMD_SPECTATOR | CMD_SERVER)) == 0 && !Company::IsValidID(_current_company)) { if (my_cmd) ShowErrorMessage(error_part1, _error_message, x, y); return false; } bool notest = (cmd_flags & CMD_NO_TEST) != 0; _docommand_recursive = 1; /* cost estimation only? */ if (!IsGeneratingWorld() && _shift_pressed && IsLocalCompany() && !(cmd & CMD_NETWORK_COMMAND) && cmd_id != CMD_PAUSE) { /* estimate the cost. */ SetTownRatingTestMode(true); res = proc(tile, flags, p1, p2, text); SetTownRatingTestMode(false); if (CmdFailed(res)) { res.SetGlobalErrorMessage(); ShowErrorMessage(error_part1, _error_message, x, y); } else { ShowEstimatedCostOrIncome(res.GetCost(), x, y); } _docommand_recursive = 0; ClearStorageChanges(false); return false; } if (!((cmd & CMD_NO_TEST_IF_IN_NETWORK) && _networking)) { /* first test if the command can be executed. */ SetTownRatingTestMode(true); res = proc(tile, flags, p1, p2, text); SetTownRatingTestMode(false); assert(cmd_id == CMD_COMPANY_CTRL || old_company == _current_company); if (CmdFailed(res)) { res.SetGlobalErrorMessage(); goto show_error; } /* no money? Only check if notest is off */ if (!notest && res.GetCost() != 0 && !CheckCompanyHasMoney(res)) goto show_error; } #ifdef ENABLE_NETWORK /* * If we are in network, and the command is not from the network * send it to the command-queue and abort execution */ if (_networking && !(cmd & CMD_NETWORK_COMMAND)) { NetworkSend_Command(tile, p1, p2, cmd & ~CMD_FLAGS_MASK, callback, text); _docommand_recursive = 0; ClearStorageChanges(false); return true; } #endif /* ENABLE_NETWORK */ DEBUG(desync, 1, "cmd: %08x; %08x; %1x; %06x; %08x; %08x; %04x; %s\n", _date, _date_fract, (int)_current_company, tile, p1, p2, cmd & ~CMD_NETWORK_COMMAND, text); /* update last build coordinate of company. */ if (tile != 0) { Company *c = Company::GetIfValid(_current_company); if (c != NULL) c->last_build_coordinate = tile; } /* Actually try and execute the command. If no cost-type is given * use the construction one */ res2 = proc(tile, flags | DC_EXEC, p1, p2, text); assert(cmd_id == CMD_COMPANY_CTRL || old_company == _current_company); /* If notest is on, it means the result of the test can be different than * the real command.. so ignore the test */ if (!notest && !((cmd & CMD_NO_TEST_IF_IN_NETWORK) && _networking)) { assert(res.GetCost() == res2.GetCost() && CmdFailed(res) == CmdFailed(res2)); // sanity check } else { if (CmdFailed(res2)) { res2.SetGlobalErrorMessage(); goto show_error; } } SubtractMoneyFromCompany(res2); /* update signals if needed */ UpdateSignalsInBuffer(); if (IsLocalCompany() && _game_mode != GM_EDITOR) { if (res2.GetCost() != 0 && tile != 0) ShowCostOrIncomeAnimation(x, y, GetSlopeZ(x, y), res2.GetCost()); if (_additional_cash_required != 0) { SetDParam(0, _additional_cash_required); if (my_cmd) ShowErrorMessage(error_part1, STR_ERROR_NOT_ENOUGH_CASH_REQUIRES_CURRENCY, x, y); if (res2.GetCost() == 0) goto callb_err; } } _docommand_recursive = 0; if (callback) callback(true, tile, p1, p2); ClearStorageChanges(true); return true; show_error: /* show error message if the command fails? */ if (IsLocalCompany() && error_part1 != 0 && my_cmd) { ShowErrorMessage(error_part1, _error_message, x, y); } callb_err: _docommand_recursive = 0; if (callback) callback(false, tile, p1, p2); ClearStorageChanges(false); return false; }
void VideoDriver_SDL::MainLoop() { uint32 cur_ticks = SDL_CALL SDL_GetTicks(); uint32 last_cur_ticks = cur_ticks; uint32 next_tick = cur_ticks + MILLISECONDS_PER_TICK; uint32 pal_tick = 0; uint32 mod; int numkeys; Uint8 *keys; if (_draw_threaded) { /* Initialise the mutex first, because that's the thing we *need* * directly in the newly created thread. */ _draw_mutex = ThreadMutex::New(); if (_draw_mutex == NULL) { _draw_threaded = false; } else { _draw_mutex->BeginCritical(); _draw_continue = true; _draw_threaded = ThreadObject::New(&DrawSurfaceToScreenThread, NULL, &_draw_thread); /* Free the mutex if we won't be able to use it. */ if (!_draw_threaded) { _draw_mutex->EndCritical(); delete _draw_mutex; } else { /* Wait till the draw mutex has started itself. */ _draw_mutex->WaitForSignal(); } } } DEBUG(driver, 1, "SDL: using %sthreads", _draw_threaded ? "" : "no "); for (;;) { uint32 prev_cur_ticks = cur_ticks; // to check for wrapping InteractiveRandom(); // randomness while (PollEvent() == -1) {} if (_exit_game) break; mod = SDL_CALL SDL_GetModState(); keys = SDL_CALL SDL_GetKeyState(&numkeys); #if defined(_DEBUG) if (_shift_pressed) #else /* Speedup when pressing tab, except when using ALT+TAB * to switch to another application */ if (keys[SDLK_TAB] && (mod & KMOD_ALT) == 0) #endif { if (!_networking && _game_mode != GM_MENU) _fast_forward |= 2; } else if (_fast_forward & 2) { _fast_forward = 0; } cur_ticks = SDL_CALL SDL_GetTicks(); if (cur_ticks >= next_tick || (_fast_forward && !_pause_mode) || cur_ticks < prev_cur_ticks) { _realtime_tick += cur_ticks - last_cur_ticks; last_cur_ticks = cur_ticks; next_tick = cur_ticks + MILLISECONDS_PER_TICK; bool old_ctrl_pressed = _ctrl_pressed; _ctrl_pressed = !!(mod & KMOD_CTRL); _shift_pressed = !!(mod & KMOD_SHIFT); /* determine which directional keys are down */ _dirkeys = (keys[SDLK_LEFT] ? 1 : 0) | (keys[SDLK_UP] ? 2 : 0) | (keys[SDLK_RIGHT] ? 4 : 0) | (keys[SDLK_DOWN] ? 8 : 0); if (old_ctrl_pressed != _ctrl_pressed) HandleCtrlChanged(); /* The gameloop is the part that can run asynchroniously. The rest * except sleeping can't. */ if (_draw_threaded) _draw_mutex->EndCritical(); GameLoop(); if (_draw_threaded) _draw_mutex->BeginCritical(); UpdateWindows(); if (++pal_tick > 4) { CheckPaletteAnim(); pal_tick = 1; } } else { /* Release the thread while sleeping */ if (_draw_threaded) _draw_mutex->EndCritical(); CSleep(1); if (_draw_threaded) _draw_mutex->BeginCritical(); NetworkDrawChatMessage(); DrawMouseCursor(); } /* End of the critical part. */ if (_draw_threaded && !IsGeneratingWorld()) { _draw_mutex->SendSignal(); } else { /* Oh, we didn't have threads, then just draw unthreaded */ DrawSurfaceToScreen(); } } if (_draw_threaded) { _draw_continue = false; /* Sending signal if there is no thread blocked * is very valid and results in noop */ _draw_mutex->SendSignal(); _draw_mutex->EndCritical(); _draw_thread->Join(); delete _draw_mutex; delete _draw_thread; } }