diff --git a/src/disaster_cmd.cpp b/src/disaster_cmd.cpp index 9c4accc947..4944e9e927 100644 --- a/src/disaster_cmd.cpp +++ b/src/disaster_cmd.cpp @@ -376,7 +376,7 @@ static bool DisasterTick_Ufo(DisasterVehicle *v) static void DestructIndustry(Industry *i) { for (TileIndex tile = 0; tile != MapSize(); tile++) { - if (IsTileType(tile, MP_INDUSTRY) && GetIndustryIndex(tile) == i->index) { + if (i->TileBelongsToIndustry(tile)) { ResetIndustryConstructionStage(tile); MarkTileDirtyByTile(tile); } diff --git a/src/industry.h b/src/industry.h index 221c3ac66e..d05e9359e5 100644 --- a/src/industry.h +++ b/src/industry.h @@ -79,6 +79,16 @@ struct Industry : IndustryPool::PoolItem<&_industry_pool> { void RecomputeProductionMultipliers(); + /** + * Check if a given tile belongs to this industry. + * @param tile The tile to check. + * @return True if the tils is part of this industry. + */ + inline bool TileBelongsToIndustry(TileIndex tile) const + { + return IsTileType(tile, MP_INDUSTRY) && GetIndustryIndex(tile) == this->index; + } + /** * Get the industry of the given tile * @param tile the tile to get the industry from diff --git a/src/industry_cmd.cpp b/src/industry_cmd.cpp index e2bc4c0468..d2b4eed7bd 100644 --- a/src/industry_cmd.cpp +++ b/src/industry_cmd.cpp @@ -1065,10 +1065,14 @@ static bool SearchLumberMillTrees(TileIndex tile, void *user_data) */ static void ChopLumberMillTrees(Industry *i) { + /* We only want to cut trees if all tiles are completed. */ + TILE_AREA_LOOP(tile_cur, i->location) { + if (i->TileBelongsToIndustry(tile_cur)) { + if (!IsIndustryCompleted(tile_cur)) return; + } + } + TileIndex tile = i->location.tile; - - if (!IsIndustryCompleted(tile)) return; // Can't proceed if not completed. - if (CircularTileSearch(&tile, 40, SearchLumberMillTrees, NULL)) { // 40x40 tiles to search. i->produced_cargo_waiting[0] = min(0xffff, i->produced_cargo_waiting[0] + 45); // Found a tree, add according value to waiting cargo. } diff --git a/src/lang/english.txt b/src/lang/english.txt index 3228445460..37c1c39218 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -1776,6 +1776,8 @@ STR_NETWORK_ERROR_SERVER_BANNED :{WHITE}You are STR_NETWORK_ERROR_KICKED :{WHITE}You were kicked out of the game STR_NETWORK_ERROR_CHEATER :{WHITE}Cheating is not allowed on this server STR_NETWORK_ERROR_TOO_MANY_COMMANDS :{WHITE}You were sending too many commands to the server +STR_NETWORK_ERROR_TIMEOUT_PASSWORD :{WHITE}You took too long to enter the password +STR_NETWORK_ERROR_TIMEOUT_COMPUTER :{WHITE}Your computer took too long to join ############ Leave those lines in this order!! STR_NETWORK_ERROR_CLIENT_GENERAL :general error diff --git a/src/network/network.cpp b/src/network/network.cpp index fd8af0be74..8d8fce4e63 100644 --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -819,20 +819,18 @@ static void NetworkSend() } } -/* We have to do some UDP checking */ -void NetworkUDPGameLoop() +/** + * We have to do some (simple) background stuff that runs normally, + * even when we are not in multiplayer. For example stuff needed + * for finding servers or downloading content. + */ +void NetworkBackgroundLoop() { _network_content_client.SendReceive(); TCPConnecter::CheckCallbacks(); NetworkHTTPSocketHandler::HTTPReceive(); - if (_network_udp_server) { - _udp_server_socket->ReceivePackets(); - _udp_master_socket->ReceivePackets(); - } else { - _udp_client_socket->ReceivePackets(); - if (_network_udp_broadcast > 0) _network_udp_broadcast--; - } + NetworkBackgroundUDPLoop(); } /* The main loop called from ttd.c diff --git a/src/network/network_client.cpp b/src/network/network_client.cpp index 35741f09ae..a4b45e9b79 100644 --- a/src/network/network_client.cpp +++ b/src/network/network_client.cpp @@ -633,6 +633,12 @@ DEF_GAME_RECEIVE_COMMAND(Client, PACKET_SERVER_ERROR) case NETWORK_ERROR_TOO_MANY_COMMANDS: _switch_mode_errorstr = STR_NETWORK_ERROR_TOO_MANY_COMMANDS; break; + case NETWORK_ERROR_TIMEOUT_PASSWORD: + ShowErrorMessage(STR_NETWORK_ERROR_TIMEOUT_PASSWORD, INVALID_STRING_ID, WL_CRITICAL); + break; + case NETWORK_ERROR_TIMEOUT_COMPUTER: + ShowErrorMessage(STR_NETWORK_ERROR_TIMEOUT_COMPUTER, INVALID_STRING_ID, WL_CRITICAL); + break; default: _switch_mode_errorstr = STR_NETWORK_ERROR_LOSTCONNECTION; } diff --git a/src/network/network_func.h b/src/network/network_func.h index 015c7dba10..7ce8414f97 100644 --- a/src/network/network_func.h +++ b/src/network/network_func.h @@ -40,7 +40,7 @@ const char *NetworkChangeCompanyPassword(CompanyID company_id, const char *passw void NetworkReboot(); void NetworkDisconnect(bool blocking = false, bool close_admins = true); void NetworkGameLoop(); -void NetworkUDPGameLoop(); +void NetworkBackgroundLoop(); void ParseConnectionString(const char **company, const char **port, char *connection_string); void NetworkStartDebugLog(NetworkAddress address); void NetworkPopulateCompanyStats(NetworkCompanyStats *stats); diff --git a/src/network/network_server.cpp b/src/network/network_server.cpp index 05a06189ad..fc1c7678f9 100644 --- a/src/network/network_server.cpp +++ b/src/network/network_server.cpp @@ -1717,55 +1717,94 @@ void NetworkServer_Tick(bool send_frame) _settings_client.network.bytes_per_frame_burst); /* Check if the speed of the client is what we can expect from a client */ - if (cs->status == NetworkClientSocket::STATUS_ACTIVE) { - /* 1 lag-point per day */ - uint lag = NetworkCalculateLag(cs) / DAY_TICKS; - if (lag > 0) { - if (lag > 3) { - /* Client did still not report in after 4 game-day, drop him - * (that is, the 3 of above, + 1 before any lag is counted) */ - IConsolePrintF(CC_ERROR, cs->last_packet + 3 * DAY_TICKS * MILLISECONDS_PER_TICK > _realtime_tick ? - /* A packet was received in the last three game days, so the client is likely lagging behind. */ - "Client #%d is dropped because the client's game state is more than 4 game-days behind" : - /* No packet was received in the last three game days; sounds like a lost connection. */ - "Client #%d is dropped because the client did not respond for more than 4 game-days", - cs->client_id); - cs->CloseConnection(NETWORK_RECV_STATUS_SERVER_ERROR); + uint lag = NetworkCalculateLag(cs); + switch (cs->status) { + case NetworkClientSocket::STATUS_ACTIVE: + /* 1 lag-point per day */ + lag /= DAY_TICKS; + if (lag > 0) { + if (lag > 3) { + /* Client did still not report in after 4 game-day, drop him + * (that is, the 3 of above, + 1 before any lag is counted) */ + IConsolePrintF(CC_ERROR, cs->last_packet + 3 * DAY_TICKS * MILLISECONDS_PER_TICK > _realtime_tick ? + /* A packet was received in the last three game days, so the client is likely lagging behind. */ + "Client #%d is dropped because the client's game state is more than 4 game-days behind" : + /* No packet was received in the last three game days; sounds like a lost connection. */ + "Client #%d is dropped because the client did not respond for more than 4 game-days", + cs->client_id); + cs->SendError(NETWORK_ERROR_TIMEOUT_COMPUTER); + continue; + } + + /* Report once per time we detect the lag, and only when we + * received a packet in the last 2000 milliseconds. If we + * did not receive a packet, then the client is not just + * slow, but the connection is likely severed. Mentioning + * frame_freq is not useful in this case. */ + if (cs->lag_test == 0 && cs->last_packet + DAY_TICKS * MILLISECONDS_PER_TICK > _realtime_tick) { + IConsolePrintF(CC_WARNING,"[%d] Client #%d is slow, try increasing [network.]frame_freq to a higher value!", _frame_counter, cs->client_id); + cs->lag_test = 1; + } + } else { + cs->lag_test = 0; + } + if (cs->last_frame_server - cs->last_token_frame >= 5 * DAY_TICKS) { + /* This is a bad client! It didn't send the right token back. */ + IConsolePrintF(CC_ERROR, "Client #%d is dropped because it fails to send valid acks", cs->client_id); + cs->SendError(NETWORK_ERROR_TIMEOUT_COMPUTER); continue; } + break; - /* Report once per time we detect the lag, and only when we - * received a packet in the last 2000 milliseconds. If we - * did not receive a packet, then the client is not just - * slow, but the connection is likely severed. Mentioning - * frame_freq is not useful in this case. */ - if (cs->lag_test == 0 && cs->last_packet + DAY_TICKS * MILLISECONDS_PER_TICK > _realtime_tick) { - IConsolePrintF(CC_WARNING,"[%d] Client #%d is slow, try increasing [network.]frame_freq to a higher value!", _frame_counter, cs->client_id); - cs->lag_test = 1; + case NetworkClientSocket::STATUS_INACTIVE: + case NetworkClientSocket::STATUS_NEWGRFS_CHECK: + case NetworkClientSocket::STATUS_AUTHORIZED: + /* NewGRF check and authorized states should be handled almost instantly. + * So give them some lee-way, likewise for the query with inactive. */ + if (lag > 4 * DAY_TICKS) { + IConsolePrintF(CC_ERROR,"Client #%d is dropped because it took longer than %d ticks to start the joining process", cs->client_id, 4 * DAY_TICKS); + cs->SendError(NETWORK_ERROR_TIMEOUT_COMPUTER); + continue; } - } else { - cs->lag_test = 0; - } - if (cs->last_frame_server - cs->last_token_frame >= 5 * DAY_TICKS) { - /* This is a bad client! It didn't send the right token back. */ - IConsolePrintF(CC_ERROR, "Client #%d is dropped because it fails to send valid acks", cs->client_id); - cs->CloseConnection(NETWORK_RECV_STATUS_SERVER_ERROR); - continue; - } - } else if (cs->status == NetworkClientSocket::STATUS_PRE_ACTIVE) { - uint lag = NetworkCalculateLag(cs); - if (lag > _settings_client.network.max_join_time) { - IConsolePrintF(CC_ERROR,"Client #%d is dropped because it took longer than %d ticks for him to join", cs->client_id, _settings_client.network.max_join_time); - cs->CloseConnection(NETWORK_RECV_STATUS_SERVER_ERROR); - continue; - } - } else if (cs->status == NetworkClientSocket::STATUS_INACTIVE) { - uint lag = NetworkCalculateLag(cs); - if (lag > 4 * DAY_TICKS) { - IConsolePrintF(CC_ERROR,"Client #%d is dropped because it took longer than %d ticks to start the joining process", cs->client_id, 4 * DAY_TICKS); - cs->CloseConnection(NETWORK_RECV_STATUS_SERVER_ERROR); - continue; - } + break; + + case NetworkClientSocket::STATUS_MAP: + /* Downloading the map... this is the amount of time since starting the saving. */ + if (lag > _settings_client.network.max_download_time) { + IConsolePrintF(CC_ERROR,"Client #%d is dropped because it took longer than %d ticks for him to download the map", cs->client_id, _settings_client.network.max_download_time); + cs->SendError(NETWORK_ERROR_TIMEOUT_COMPUTER); + continue; + } + break; + + case NetworkClientSocket::STATUS_DONE_MAP: + case NetworkClientSocket::STATUS_PRE_ACTIVE: + /* The map has been sent, so this is for loading the map and syncing up. */ + if (lag > _settings_client.network.max_join_time) { + IConsolePrintF(CC_ERROR,"Client #%d is dropped because it took longer than %d ticks for him to join", cs->client_id, _settings_client.network.max_join_time); + cs->SendError(NETWORK_ERROR_TIMEOUT_COMPUTER); + continue; + } + break; + + case NetworkClientSocket::STATUS_AUTH_GAME: + case NetworkClientSocket::STATUS_AUTH_COMPANY: + /* These don't block? */ + if (lag > _settings_client.network.max_password_time) { + IConsolePrintF(CC_ERROR,"Client #%d is dropped because it took longer than %d to enter the password", cs->client_id, _settings_client.network.max_password_time); + cs->SendError(NETWORK_ERROR_TIMEOUT_PASSWORD); + continue; + } + break; + + case NetworkClientSocket::STATUS_MAP_WAIT: + /* This is an internal state where we do not wait + * on the client to move to a different state. */ + break; + + case NetworkClientSocket::STATUS_END: + /* Bad server/code. */ + NOT_REACHED(); } if (cs->status >= NetworkClientSocket::STATUS_PRE_ACTIVE) { diff --git a/src/network/network_type.h b/src/network/network_type.h index f40a1cdd5f..b9c7f65d95 100644 --- a/src/network/network_type.h +++ b/src/network/network_type.h @@ -119,6 +119,8 @@ enum NetworkErrorCode { NETWORK_ERROR_CHEATER, NETWORK_ERROR_FULL, NETWORK_ERROR_TOO_MANY_COMMANDS, + NETWORK_ERROR_TIMEOUT_PASSWORD, + NETWORK_ERROR_TIMEOUT_COMPUTER, }; #endif /* ENABLE_NETWORK */ diff --git a/src/network/network_udp.cpp b/src/network/network_udp.cpp index 7f602e0831..b4c8012599 100644 --- a/src/network/network_udp.cpp +++ b/src/network/network_udp.cpp @@ -637,4 +637,20 @@ void NetworkUDPClose() DEBUG(net, 1, "[udp] closed listeners"); } +/** Receive the UDP packets. */ +void NetworkBackgroundUDPLoop() +{ + _network_udp_mutex->BeginCritical(); + + if (_network_udp_server) { + _udp_server_socket->ReceivePackets(); + _udp_master_socket->ReceivePackets(); + } else { + _udp_client_socket->ReceivePackets(); + if (_network_udp_broadcast > 0) _network_udp_broadcast--; + } + + _network_udp_mutex->EndCritical(); +} + #endif /* ENABLE_NETWORK */ diff --git a/src/network/network_udp.h b/src/network/network_udp.h index bd315baba1..3dfd076720 100644 --- a/src/network/network_udp.h +++ b/src/network/network_udp.h @@ -23,6 +23,7 @@ void NetworkUDPQueryServer(NetworkAddress address, bool manually = false); void NetworkUDPAdvertise(); void NetworkUDPRemoveAdvertise(bool blocking); void NetworkUDPClose(); +void NetworkBackgroundUDPLoop(); #endif /* ENABLE_NETWORK */ diff --git a/src/newgrf_industries.cpp b/src/newgrf_industries.cpp index c2eb0d0d45..69575aa2d5 100644 --- a/src/newgrf_industries.cpp +++ b/src/newgrf_industries.cpp @@ -242,7 +242,7 @@ uint32 IndustryGetVariable(const ResolverObject *object, byte variable, byte par /* Get random tile bits at offset param */ case 0x61: tile = GetNearbyTile(parameter, tile, false); - return (IsTileType(tile, MP_INDUSTRY) && Industry::GetByTile(tile) == industry) ? GetIndustryRandomBits(tile) : 0; + return industry->TileBelongsToIndustry(tile) ? GetIndustryRandomBits(tile) : 0; /* Land info of nearby tiles */ case 0x62: return GetNearbyIndustryTileInformation(parameter, tile, INVALID_INDUSTRY, false); @@ -250,7 +250,7 @@ uint32 IndustryGetVariable(const ResolverObject *object, byte variable, byte par /* Animation stage of nearby tiles */ case 0x63: tile = GetNearbyTile(parameter, tile, false); - if (IsTileType(tile, MP_INDUSTRY) && Industry::GetByTile(tile) == industry) { + if (industry->TileBelongsToIndustry(tile)) { return GetAnimationFrame(tile); } return 0xFFFFFFFF; diff --git a/src/newgrf_industrytiles.cpp b/src/newgrf_industrytiles.cpp index e0d5ac7cda..2681ce9635 100644 --- a/src/newgrf_industrytiles.cpp +++ b/src/newgrf_industrytiles.cpp @@ -331,7 +331,7 @@ bool StartStopIndustryTileAnimation(const Industry *ind, IndustryAnimationTrigge bool ret = true; uint32 random = Random(); TILE_AREA_LOOP(tile, ind->location) { - if (IsTileType(tile, MP_INDUSTRY) && GetIndustryIndex(tile) == ind->index) { + if (ind->TileBelongsToIndustry(tile)) { if (StartStopIndustryTileAnimation(tile, iat, random)) { SB(random, 0, 16, Random()); } else { @@ -415,7 +415,7 @@ void TriggerIndustry(Industry *ind, IndustryTileTrigger trigger) { uint32 reseed_industry = 0; TILE_AREA_LOOP(tile, ind->location) { - if (IsTileType(tile, MP_INDUSTRY) && GetIndustryIndex(tile) == ind->index) { + if (ind->TileBelongsToIndustry(tile)) { DoTriggerIndustryTile(tile, trigger, ind, reseed_industry); } } diff --git a/src/object_cmd.cpp b/src/object_cmd.cpp index 157d2c834a..5860a7ca2c 100644 --- a/src/object_cmd.cpp +++ b/src/object_cmd.cpp @@ -34,6 +34,7 @@ #include "newgrf_object.h" #include "date_func.h" #include "newgrf_debug.h" +#include "vehicle_func.h" #include "table/strings.h" #include "table/object_land.h" @@ -221,6 +222,9 @@ CommandCost CmdBuildObject(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3 /* Can't build on water owned by another company. */ Owner o = GetTileOwner(t); if (o != OWNER_NONE && o != OWNER_WATER) cost.AddCost(CheckOwnership(o, t)); + + /* However, the tile has to be clear of vehicles. */ + cost.AddCost(EnsureNoVehicleOnGround(t)); } } else { if (!allow_ground) return_cmd_error(STR_ERROR_MUST_BE_BUILT_ON_WATER); diff --git a/src/openttd.cpp b/src/openttd.cpp index 4329f5ee46..09b9dc67bf 100644 --- a/src/openttd.cpp +++ b/src/openttd.cpp @@ -1268,7 +1268,7 @@ void GameLoop() #ifdef ENABLE_NETWORK /* Check for UDP stuff */ - if (_network_available) NetworkUDPGameLoop(); + if (_network_available) NetworkBackgroundLoop(); if (_networking && !IsGeneratingWorld()) { /* Multiplayer */ diff --git a/src/order_backup.cpp b/src/order_backup.cpp index 2a0b7be216..3228f60044 100644 --- a/src/order_backup.cpp +++ b/src/order_backup.cpp @@ -17,6 +17,7 @@ #include "order_backup.h" #include "vehicle_base.h" #include "window_func.h" +#include "station_map.h" OrderBackupPool _order_backup_pool("BackupOrder"); INSTANTIATE_POOL_METHODS(OrderBackup) @@ -262,3 +263,25 @@ void InitializeOrderBackups() { _order_backup_pool.CleanPool(); } + +/** + * Removes an order from all vehicles. Triggers when, say, a station is removed. + * @param type The type of the order (OT_GOTO_[STATION|DEPOT|WAYPOINT]). + * @param destination The destination. Can be a StationID, DepotID or WaypointID. + */ +/* static */ void OrderBackup::RemoveOrder(OrderType type, DestinationID destination) +{ + OrderBackup *ob; + FOR_ALL_ORDER_BACKUPS(ob) { + for (Order *order = ob->orders; order != NULL; order = order->next) { + OrderType ot = order->GetType(); + if (ot == OT_GOTO_DEPOT && (order->GetDepotActionType() & ODATFB_NEAREST_DEPOT) != 0) continue; + if (ot == OT_IMPLICIT || (IsHangarTile(ob->tile) && ot == OT_GOTO_DEPOT)) ot = OT_GOTO_STATION; + if (ot == type && order->GetDestination() == destination) { + /* Remove the order backup! If a station/depot gets removed, we can't/shouldn't restore those broken orders. */ + delete ob; + break; + } + } + } +} diff --git a/src/order_backup.h b/src/order_backup.h index 51281e8608..06320feb6b 100644 --- a/src/order_backup.h +++ b/src/order_backup.h @@ -67,6 +67,7 @@ public: static void ClearGroup(GroupID group); static void ClearVehicle(const Vehicle *v); + static void RemoveOrder(OrderType type, DestinationID destination); }; #define FOR_ALL_ORDER_BACKUPS_FROM(var, start) FOR_ALL_ITEMS_FROM(OrderBackup, order_backup_index, var, start) diff --git a/src/order_cmd.cpp b/src/order_cmd.cpp index 80dc0ce719..cb9691da51 100644 --- a/src/order_cmd.cpp +++ b/src/order_cmd.cpp @@ -27,6 +27,7 @@ #include "station_base.h" #include "waypoint_base.h" #include "company_base.h" +#include "order_backup.h" #include "table/strings.h" @@ -1699,6 +1700,8 @@ restart: } } } + + OrderBackup::RemoveOrder(type, destination); } /** diff --git a/src/settings_type.h b/src/settings_type.h index d6947c75bf..9c17ec728a 100644 --- a/src/settings_type.h +++ b/src/settings_type.h @@ -155,6 +155,8 @@ struct NetworkSettings { uint16 bytes_per_frame; ///< how many bytes may, over a long period, be received per frame? uint16 bytes_per_frame_burst; ///< how many bytes may, over a short period, be received? uint16 max_join_time; ///< maximum amount of time, in game ticks, a client may take to join + uint16 max_download_time; ///< maximum amount of time, in game ticks, a client may take to download the map + uint16 max_password_time; ///< maximum amount of time, in game ticks, a client may take to enter the password bool pause_on_join; ///< pause the game when people join uint16 server_port; ///< port the server listens on uint16 server_admin_port; ///< port the server listens on for the admin network diff --git a/src/table/settings.h b/src/table/settings.h index ccdbf98a10..1be39fbd22 100644 --- a/src/table/settings.h +++ b/src/table/settings.h @@ -645,6 +645,8 @@ const SettingDesc _settings[] = { SDTC_VAR(network.bytes_per_frame, SLE_UINT16, S, NO, 8, 1, 65535, 0, STR_NULL, NULL), SDTC_VAR(network.bytes_per_frame_burst,SLE_UINT16, S, NO, 256, 1, 65535, 0, STR_NULL, NULL), SDTC_VAR(network.max_join_time, SLE_UINT16, S, NO, 500, 0, 32000, 0, STR_NULL, NULL), + SDTC_VAR(network.max_download_time, SLE_UINT16, S, NO, 1000, 0, 32000, 0, STR_NULL, NULL), + SDTC_VAR(network.max_password_time, SLE_UINT16, S, NO, 2000, 0, 32000, 0, STR_NULL, NULL), SDTC_BOOL(network.pause_on_join, S, NO, true, STR_NULL, NULL), SDTC_VAR(network.server_port, SLE_UINT16, S, NO,NETWORK_DEFAULT_PORT,0,65535,0,STR_NULL, NULL), SDTC_VAR(network.server_admin_port, SLE_UINT16, S, NO, NETWORK_ADMIN_PORT,0,65535,0,STR_NULL, NULL),