1
0
Fork 0

(svn r23795) [1.1] -Backport from trunk:

- Fix: Make default timeouts for certain network states lower and configurable [FS#4955] (r23764)
- Fix: Check whether a water tile is really empty when overbuilding it with an object [FS#4956] (r23763)
- Fix: Missing locking causing crash in extreme case when being in the MP lobby [FS#4938] (r23752)
- Fix: Clear the backed up orders of a removed station as well, otherwise one could create orders to a station that was never in the original backupped orders. For example a road vehicle trying to go to a buoy [FS#4876] (r23464)
- Fix: Do not assume all industries that cut trees have tile (0,0) and wait until all tiles of an industry are completed before starting to cut trees (r23458)
release/1.1
rubidium 2012-01-13 21:54:59 +00:00
parent 15a9cae86e
commit 7faba28c31
20 changed files with 177 additions and 64 deletions

View File

@ -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);
}

View File

@ -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

View File

@ -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.
}

View File

@ -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

View File

@ -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

View File

@ -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;
}

View File

@ -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);

View File

@ -1717,9 +1717,11 @@ 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) {
uint lag = NetworkCalculateLag(cs);
switch (cs->status) {
case NetworkClientSocket::STATUS_ACTIVE:
/* 1 lag-point per day */
uint lag = NetworkCalculateLag(cs) / DAY_TICKS;
lag /= DAY_TICKS;
if (lag > 0) {
if (lag > 3) {
/* Client did still not report in after 4 game-day, drop him
@ -1730,7 +1732,7 @@ void NetworkServer_Tick(bool send_frame)
/* 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);
cs->SendError(NETWORK_ERROR_TIMEOUT_COMPUTER);
continue;
}
@ -1749,23 +1751,60 @@ void NetworkServer_Tick(bool send_frame)
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);
cs->SendError(NETWORK_ERROR_TIMEOUT_COMPUTER);
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);
break;
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->CloseConnection(NETWORK_RECV_STATUS_SERVER_ERROR);
cs->SendError(NETWORK_ERROR_TIMEOUT_COMPUTER);
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) {

View File

@ -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 */

View File

@ -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 */

View File

@ -23,6 +23,7 @@ void NetworkUDPQueryServer(NetworkAddress address, bool manually = false);
void NetworkUDPAdvertise();
void NetworkUDPRemoveAdvertise(bool blocking);
void NetworkUDPClose();
void NetworkBackgroundUDPLoop();
#endif /* ENABLE_NETWORK */

View File

@ -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;

View File

@ -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);
}
}

View File

@ -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);

View File

@ -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 */

View File

@ -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;
}
}
}
}

View File

@ -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)

View File

@ -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);
}
/**

View File

@ -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

View File

@ -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),