diff --git a/src/ai/api/ai_tunnel.cpp b/src/ai/api/ai_tunnel.cpp index 2b1fb1e319..53a38073a3 100644 --- a/src/ai/api/ai_tunnel.cpp +++ b/src/ai/api/ai_tunnel.cpp @@ -23,14 +23,26 @@ /* If it's a tunnel alread, take the easy way out! */ if (IsTunnelTile(tile)) return ::GetOtherTunnelEnd(tile); - ::DoCommand(tile, 0, 0, DC_AUTO, CMD_BUILD_TUNNEL); - return _build_tunnel_endtile == 0 ? INVALID_TILE : _build_tunnel_endtile; + uint start_z; + Slope start_tileh = ::GetTileSlope(tile, &start_z); + DiagDirection direction = ::GetInclinedSlopeDirection(start_tileh); + if (direction == INVALID_DIAGDIR) return INVALID_TILE; + + TileIndexDiff delta = ::TileOffsByDiagDir(direction); + uint end_z; + do { + tile += delta; + if (!::IsValidTile(tile)) return INVALID_TILE; + + ::GetTileSlope(tile, &end_z); + } while (start_z != end_z); + + return tile; } static void _DoCommandReturnBuildTunnel2(class AIInstance *instance) { if (!AITunnel::_BuildTunnelRoad2()) { - AIObject::SetLastCommandRes(false); AIInstance::DoCommandReturn(instance); return; } @@ -43,7 +55,6 @@ static void _DoCommandReturnBuildTunnel2(class AIInstance *instance) static void _DoCommandReturnBuildTunnel1(class AIInstance *instance) { if (!AITunnel::_BuildTunnelRoad1()) { - AIObject::SetLastCommandRes(false); AIInstance::DoCommandReturn(instance); return; } @@ -62,7 +73,7 @@ static void _DoCommandReturnBuildTunnel1(class AIInstance *instance) uint type = 0; if (vehicle_type == AIVehicle::VT_ROAD) { type |= (TRANSPORT_ROAD << 9); - type |= RoadTypeToRoadTypes((::RoadType)AIObject::GetRoadType()); + type |= ::RoadTypeToRoadTypes((::RoadType)AIObject::GetRoadType()); } else { type |= (TRANSPORT_RAIL << 9); type |= AIRail::GetCurrentRailType(); @@ -74,10 +85,7 @@ static void _DoCommandReturnBuildTunnel1(class AIInstance *instance) } AIObject::SetCallbackVariable(0, start); - if (!AIObject::DoCommand(start, type, 0, CMD_BUILD_TUNNEL, NULL, &_DoCommandReturnBuildTunnel1)) return false; - - /* In case of test-mode, test if we can build both road pieces */ - return _BuildTunnelRoad1(); + return AIObject::DoCommand(start, type, 0, CMD_BUILD_TUNNEL, NULL, &_DoCommandReturnBuildTunnel1); } /* static */ bool AITunnel::_BuildTunnelRoad1() @@ -89,10 +97,7 @@ static void _DoCommandReturnBuildTunnel1(class AIInstance *instance) DiagDirection dir_1 = (DiagDirection)((::TileX(start) == ::TileX(end)) ? (::TileY(start) < ::TileY(end) ? DIAGDIR_NW : DIAGDIR_SE) : (::TileX(start) < ::TileX(end) ? DIAGDIR_NE : DIAGDIR_SW)); DiagDirection dir_2 = ::ReverseDiagDir(dir_1); - if (!AIObject::DoCommand(start + ::TileOffsByDiagDir(dir_1), ::DiagDirToRoadBits(dir_2) | (AIObject::GetRoadType() << 4), 0, CMD_BUILD_ROAD, NULL, &_DoCommandReturnBuildTunnel2)) return false; - - /* In case of test-mode, test the other road piece too */ - return _BuildTunnelRoad2(); + return AIObject::DoCommand(start + ::TileOffsByDiagDir(dir_1), ::DiagDirToRoadBits(dir_2) | (AIObject::GetRoadType() << 4), 0, CMD_BUILD_ROAD, NULL, &_DoCommandReturnBuildTunnel2); } /* static */ bool AITunnel::_BuildTunnelRoad2() diff --git a/src/ai/api/ai_tunnel.hpp b/src/ai/api/ai_tunnel.hpp index 51d3404298..c6d7b3d106 100644 --- a/src/ai/api/ai_tunnel.hpp +++ b/src/ai/api/ai_tunnel.hpp @@ -46,11 +46,15 @@ public: /** * Get the tile that exits on the other end of a (would be) tunnel starting - * at tile. + * at tile. If there is no 'simple' inclined slope at the start tile, + * this function will return AIMap::TILE_INVALID. * @param tile The tile that is an entrance to a tunnel or the tile where you may want to build a tunnel. * @pre AIMap::IsValidTile(tile). * @return The TileIndex that is the other end of the (would be) tunnel, or * AIMap::TILE_INVALID if no other end was found (can't build tunnel). + * @note Even if this function returns a valid tile, that is no guarantee + * that building a tunnel will succeed. Use BuildTunnel in AITestMode to + * check whether a tunnel can actually be build. */ static TileIndex GetOtherTunnelEnd(TileIndex tile); diff --git a/src/genworld.cpp b/src/genworld.cpp index dfae4822e0..70f3905019 100644 --- a/src/genworld.cpp +++ b/src/genworld.cpp @@ -47,32 +47,10 @@ void InitializeGame(uint size_x, uint size_y, bool reset_date); * in the genworld.h and genworld.c! -- TrueLight */ gw_info _gw; -/** - * Set the status of the Paint flag. - * If it is true, the thread will hold with any futher generating till - * the drawing of the screen is done. This is handled by - * SetGeneratingWorldProgress(), so calling that function will stall - * from time to time. - */ -void SetGeneratingWorldPaintStatus(bool status) -{ - _gw.wait_for_draw = status; -} - -/** - * Returns true if the thread wants the main program to do a (full) paint. - * If this returns false, please do not update the screen. Because we are - * writing in a thread, it can cause damaged data (reading and writing the - * same tile at the same time). - */ -bool IsGeneratingWorldReadyForPaint() -{ - /* If we are in quit_thread mode, ignore this and always return false. This - * forces the screen to not be drawn, and the GUI not to wait for a draw. */ - if (!_gw.active || _gw.quit_thread || !_gw.threaded) return false; - - return _gw.wait_for_draw; -} +/** Rights for the map generation */ +ThreadMutex *_genworld_mapgen_mutex = ThreadMutex::New(); +/** Rights for the painting */ +ThreadMutex *_genworld_paint_mutex = ThreadMutex::New(); /** * Tells if the world generation is done in a thread or not. @@ -100,6 +78,7 @@ static void CleanupGeneration() DeleteWindowById(WC_GENERATE_PROGRESS_WINDOW, 0); MarkWholeScreenDirty(); + _genworld_mapgen_mutex->EndCritical(); } /** @@ -109,6 +88,7 @@ static void _GenerateWorld(void *arg) { try { _generating_world = true; + _genworld_mapgen_mutex->BeginCritical(); if (_network_dedicated) DEBUG(net, 0, "Generating map, please wait..."); /* Set the Random() seed to generation_seed so we produce the same map with the same seed */ if (_settings_game.game_creation.generation_seed == GENERATE_NEW_SEED) _settings_game.game_creation.generation_seed = _settings_newgame.game_creation.generation_seed = InteractiveRandom(); @@ -194,6 +174,7 @@ static void _GenerateWorld(void *arg) } } catch (...) { _generating_world = false; + _genworld_mapgen_mutex->EndCritical(); throw; } } @@ -223,11 +204,16 @@ void GenerateWorldSetAbortCallback(gw_abort_proc *proc) void WaitTillGeneratedWorld() { if (_gw.thread == NULL) return; + + _genworld_mapgen_mutex->EndCritical(); + _genworld_paint_mutex->EndCritical(); _gw.quit_thread = true; _gw.thread->Join(); delete _gw.thread; _gw.thread = NULL; _gw.threaded = false; + _genworld_mapgen_mutex->BeginCritical(); + _genworld_paint_mutex->BeginCritical(); } /** @@ -280,7 +266,6 @@ void GenerateWorld(GenerateWorldMode mode, uint size_x, uint size_y) _gw.abort = false; _gw.abortp = NULL; _gw.lc = _local_company; - _gw.wait_for_draw = false; _gw.quit_thread = false; _gw.threaded = true; @@ -315,7 +300,9 @@ void GenerateWorld(GenerateWorldMode mode, uint size_x, uint size_y) !ThreadObject::New(&_GenerateWorld, NULL, &_gw.thread)) { DEBUG(misc, 1, "Cannot create genworld thread, reverting to single-threaded mode"); _gw.threaded = false; + _genworld_mapgen_mutex->EndCritical(); _GenerateWorld(NULL); + _genworld_mapgen_mutex->BeginCritical(); return; } diff --git a/src/genworld.h b/src/genworld.h index d46ee731b4..522d93f469 100644 --- a/src/genworld.h +++ b/src/genworld.h @@ -16,6 +16,8 @@ enum { LG_TERRAGENESIS = 1, ///< TerraGenesis Perlin landscape generator GENERATE_NEW_SEED = UINT_MAX, ///< Create a new random seed + + GENWORLD_REDRAW_TIMEOUT = 200, ///< Timeout between redraws }; /* Modes for GenerateWorld */ @@ -32,7 +34,6 @@ typedef void gw_abort_proc(); struct gw_info { bool active; ///< Is generating world active bool abort; ///< Whether to abort the thread ASAP - bool wait_for_draw; ///< Are we waiting on a draw event bool quit_thread; ///< Do we want to quit the active thread bool threaded; ///< Whether we run _GenerateWorld threaded GenerateWorldMode mode;///< What mode are we making a world in @@ -69,8 +70,6 @@ static inline bool IsGeneratingWorld() } /* genworld.cpp */ -void SetGeneratingWorldPaintStatus(bool status); -bool IsGeneratingWorldReadyForPaint(); bool IsGenerateWorldThreaded(); void GenerateWorldSetCallback(gw_done_proc *proc); void GenerateWorldSetAbortCallback(gw_abort_proc *proc); @@ -89,4 +88,7 @@ void StartNewGameWithoutGUI(uint seed); void ShowCreateScenario(); void StartScenarioEditor(); +extern class ThreadMutex *_genworld_mapgen_mutex; +extern class ThreadMutex *_genworld_paint_mutex; + #endif /* GENWORLD_H */ diff --git a/src/genworld_gui.cpp b/src/genworld_gui.cpp index 8fc28caf25..2dbd636086 100644 --- a/src/genworld_gui.cpp +++ b/src/genworld_gui.cpp @@ -26,6 +26,7 @@ #include "landscape_type.h" #include "querystring_gui.h" #include "town.h" +#include "thread.h" #include "table/strings.h" #include "table/sprites.h" @@ -1037,8 +1038,8 @@ static void _SetGeneratingWorldProgress(gwp_class cls, uint progress, uint total _tp.percent = percent_table[cls]; } - /* Don't update the screen too often. So update it once in every 200ms */ - if (!_network_dedicated && _tp.timer != 0 && _realtime_tick - _tp.timer < 200) return; + /* Don't update the screen too often. So update it once in every once in a while... */ + if (!_network_dedicated && _tp.timer != 0 && _realtime_tick - _tp.timer < GENWORLD_REDRAW_TIMEOUT) return; /* Percentage is about the number of completed tasks, so 'current - 1' */ _tp.percent = percent_table[cls] + (percent_table[cls + 1] - percent_table[cls]) * (_tp.current == 0 ? 0 : _tp.current - 1) / _tp.total; @@ -1064,12 +1065,15 @@ static void _SetGeneratingWorldProgress(gwp_class cls, uint progress, uint total InvalidateWindow(WC_GENERATE_PROGRESS_WINDOW, 0); MarkWholeScreenDirty(); - SetGeneratingWorldPaintStatus(true); - /* We wait here till the paint is done, so we don't read and write - * on the same tile at the same moment. Nasty hack, but that happens - * if you implement threading afterwards */ - while (IsGeneratingWorldReadyForPaint()) { CSleep(10); } + /* Release the rights to the map generator, and acquire the rights to the + * paint thread. The 'other' thread already has the paint thread rights so + * this ensures us that we are waiting until the paint thread is done + * before we reacquire the mapgen rights */ + _genworld_mapgen_mutex->EndCritical(); + _genworld_paint_mutex->BeginCritical(); + _genworld_mapgen_mutex->BeginCritical(); + _genworld_paint_mutex->EndCritical(); _tp.timer = _realtime_tick; } diff --git a/src/gfx.cpp b/src/gfx.cpp index aeac0db1d0..700c64d618 100644 --- a/src/gfx.cpp +++ b/src/gfx.cpp @@ -18,6 +18,7 @@ #include "core/sort_func.hpp" #include "landscape_type.h" #include "network/network_func.h" +#include "thread.h" #include "table/palettes.h" #include "table/sprites.h" @@ -1392,7 +1393,18 @@ void DrawDirtyBlocks() int x; int y; - if (IsGeneratingWorld() && !IsGeneratingWorldReadyForPaint()) return; + if (IsGeneratingWorld()) { + /* We are generating the world, so release our rights to the map and + * painting while we are waiting a bit. */ + _genworld_paint_mutex->EndCritical(); + _genworld_mapgen_mutex->EndCritical(); + + /* Wait a while and update _realtime_tick so we are given the rights */ + CSleep(GENWORLD_REDRAW_TIMEOUT); + _realtime_tick += GENWORLD_REDRAW_TIMEOUT; + _genworld_paint_mutex->BeginCritical(); + _genworld_mapgen_mutex->BeginCritical(); + } y = 0; do { @@ -1460,12 +1472,6 @@ void DrawDirtyBlocks() _invalid_rect.top = h; _invalid_rect.right = 0; _invalid_rect.bottom = 0; - - /* If we are generating a world, and waiting for a paint run, mark it here - * as done painting, so we can continue generating. */ - if (IsGeneratingWorld() && IsGeneratingWorldReadyForPaint()) { - SetGeneratingWorldPaintStatus(false); - } } /*! diff --git a/src/newgrf.cpp b/src/newgrf.cpp index 42a54a15e2..32c8691212 100644 --- a/src/newgrf.cpp +++ b/src/newgrf.cpp @@ -4380,7 +4380,8 @@ static void GRFLoadError(byte *buf, size_t len) /* Only two parameter numbers can be used in the string. */ uint i = 0; for (; i < 2 && len > 0; i++) { - error->param_number[i] = grf_load_byte(&buf); + uint param_number = grf_load_byte(&buf); + error->param_value[i] = (param_number < _cur_grffile->param_end ? _cur_grffile->param[param_number] : 0); len--; } error->num_params = i; diff --git a/src/newgrf_config.h b/src/newgrf_config.h index aba8dc330d..9f0e7efd54 100644 --- a/src/newgrf_config.h +++ b/src/newgrf_config.h @@ -53,8 +53,8 @@ struct GRFError { char *data; ///< Additional data for message and custom_message StringID message; ///< Default message StringID severity; ///< Info / Warning / Error / Fatal - uint8 num_params; ///< Number of additinal parameters for custom_message (0, 1 or 2) - uint8 param_number[2]; ///< GRF parameters to show for custom_message + uint8 num_params; ///< Number of additinal parameters for message and custom_message (0, 1 or 2) + uint32 param_value[2]; ///< Values of GRF parameters to show for message and custom_message }; /** Information about GRF, used in the game and (part of it) in savegames */ diff --git a/src/newgrf_gui.cpp b/src/newgrf_gui.cpp index 1b265f4a8c..3b54e1aaf7 100644 --- a/src/newgrf_gui.cpp +++ b/src/newgrf_gui.cpp @@ -57,12 +57,7 @@ static void ShowNewGRFInfo(const GRFConfig *c, uint x, uint y, uint w, uint bott SetDParam (3, STR_JUST_RAW_STRING); SetDParamStr(4, c->error->data); for (uint i = 0; i < c->error->num_params; i++) { - uint32 param = 0; - byte param_number = c->error->param_number[i]; - - if (param_number < c->num_params) param = c->param[param_number]; - - SetDParam(5 + i, param); + SetDParam(5 + i, c->error->param_value[i]); } GetString(message, c->error->custom_message == NULL ? c->error->message : STR_JUST_RAW_STRING, lastof(message)); diff --git a/src/openttd.cpp b/src/openttd.cpp index 8f61574fa7..3552af646b 100644 --- a/src/openttd.cpp +++ b/src/openttd.cpp @@ -50,6 +50,7 @@ #include "elrail_func.h" #include "rev.h" #include "highscore.h" +#include "thread.h" #include "newgrf_commons.h" @@ -652,6 +653,10 @@ int ttd_main(int argc, char *argv[]) InitializeGUI(); IConsoleCmdExec("exec scripts/autoexec.scr 0"); + /* Take our initial lock on whatever we might want to do! */ + _genworld_paint_mutex->BeginCritical(); + _genworld_mapgen_mutex->BeginCritical(); + GenerateWorld(GW_EMPTY, 64, 64); // Make the viewport initialization happy WaitTillGeneratedWorld(); diff --git a/src/saveload/vehicle_sl.cpp b/src/saveload/vehicle_sl.cpp index 4d55a56a68..a05987a27e 100644 --- a/src/saveload/vehicle_sl.cpp +++ b/src/saveload/vehicle_sl.cpp @@ -298,6 +298,18 @@ void AfterLoadVehicles(bool part_of_load) } } + if (CheckSavegameVersion(105)) { + /* Before 105 there was no order for shared orders, thus it messed up horribly */ + FOR_ALL_VEHICLES(v) { + if (v->First() != v || v->orders.list != NULL || v->previous_shared != NULL || v->next_shared == NULL) continue; + + v->orders.list = new OrderList(NULL, v); + for (Vehicle *u = v; u != NULL; u = u->next_shared) { + u->orders.list = v->orders.list; + } + } + } + CheckValidVehicles(); FOR_ALL_VEHICLES(v) {