My family and I have been playing (and creating our own factions) for the past number of months. Since v3.2.1 (we use the linux version in Ubuntu 8.10) we noticed when playing on our LAN with 3 players that we get numerous crash scenarios or corrupt game starts. After spending a long time debugging I have found a few items that the authors should possibly look at in order to fix these:
1. Firstly there seems to be a random timing issue when opening a socket (not sure if this is linux specific). I added some retry code to allow glest to not crash but simply wait until the socket is available:
void ClientSocket::connect(const Ip &ip, int port){
sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family= AF_INET;
addr.sin_addr.s_addr= inet_addr(ip.getString().c_str());
addr.sin_port= htons(port);
int err= ::connect(sock, reinterpret_cast<const sockaddr*>(&addr), sizeof(addr));
if(err < 0) {
char szBuf[1024]="";
sprintf(szBuf,"#2 Error connecting socket for IP: %s for Port: %d err = %d errno = %d [%s]",ip.getString().c_str(),port,err,errno,strerror(errno));
fprintf(stderr,szBuf);
if (errno == EINPROGRESS) {
fd_set myset;
struct timeval tv;
int valopt;
socklen_t lon;
fprintf(stderr, "EINPROGRESS in connect() - selecting\n");
do {
tv.tv_sec = 10;
tv.tv_usec = 0;
FD_ZERO(&myset);
FD_SET(sock, &myset);
err = select(sock+1, NULL, &myset, NULL, &tv);
if (err < 0 && errno != EINTR) {
sprintf(szBuf, "Error connecting %d - %s\n", errno, strerror(errno));
throwException(szBuf);
}
else if (err > 0) {
// Socket selected for write
lon = sizeof(int);
if (getsockopt(sock, SOL_SOCKET, SO_ERROR, (void*)(&valopt), &lon) < 0) {
sprintf(szBuf, "Error in getsockopt() %d - %s\n", errno, strerror(errno));
throwException(szBuf);
}
// Check the value returned...
if (valopt) {
sprintf(szBuf, "Error in delayed connection() %d - %s\n", valopt, strerror(valopt));
throwException(szBuf);
}
errno = 0;
fprintf(stderr, "Apparent recovery for connection sock = %d, err = %d, errno = %d\n",sock,err,errno);
break;
}
else {
sprintf(szBuf, "Timeout in select() - Cancelling!\n");
throwException(szBuf);
}
} while (1);
}
if(err < 0)
{
throwException(szBuf);
}
fprintf(stderr, "Valid recovery for connection sock = %d, err = %d, errno = %d\n",sock,err,errno);
}
}
2. The random corrupt startups occurs due to an incomplete NetworkMessageLaunch message sturcture. This structure does not contain 3 additional booleans that exist in the gamesettings class and therefore some network players start with 0 resources and/or units. I corrected that by modifiying the NetworkMessageLaunch class:
Added these 3 missing booleans:
int8 defaultResources;
int8 defaultUnits;
int8 defaultVictoryConditions;
like this:
class NetworkMessageLaunch: public NetworkMessage{
private:
static const int maxStringSize= 256;
private:
struct Data{
int8 messageType;
NetworkString<maxStringSize> description;
NetworkString<maxStringSize> map;
NetworkString<maxStringSize> tileset;
NetworkString<maxStringSize> tech;
NetworkString<maxStringSize> factionTypeNames[GameConstants::maxPlayers]; //faction names
int8 factionControls[GameConstants::maxPlayers];
int8 thisFactionIndex;
int8 factionCount;
int8 teams[GameConstants::maxPlayers];
int8 startLocationIndex[GameConstants::maxPlayers];
int8 defaultResources;
int8 defaultUnits;
int8 defaultVictoryConditions;
};
private:
Data data;
public:
NetworkMessageLaunch();
NetworkMessageLaunch(const GameSettings *gameSettings);
void buildGameSettings(GameSettings *gameSettings) const;
virtual bool receive(Socket* socket);
virtual void send(Socket* socket) const;
};
NetworkMessageLaunch::NetworkMessageLaunch(const GameSettings *gameSettings){
data.messageType=nmtLaunch;
data.description= gameSettings->getDescription();
data.map= gameSettings->getMap();
data.tileset= gameSettings->getTileset();
data.tech= gameSettings->getTech();
data.factionCount= gameSettings->getFactionCount();
data.thisFactionIndex= gameSettings->getThisFactionIndex();
data.defaultResources= gameSettings->getDefaultResources();
data.defaultUnits= gameSettings->getDefaultUnits();
data.defaultVictoryConditions= gameSettings->getDefaultVictoryConditions();
for(int i= 0; i<data.factionCount; ++i){
data.factionTypeNames[i]= gameSettings->getFactionTypeName(i);
data.factionControls[i]= gameSettings->getFactionControl(i);
data.teams[i]= gameSettings->getTeam(i);
data.startLocationIndex[i]= gameSettings->getStartLocationIndex(i);
}
}
void NetworkMessageLaunch::buildGameSettings(GameSettings *gameSettings) const{
gameSettings->setDescription(data.description.getString());
gameSettings->setMap(data.map.getString());
gameSettings->setTileset(data.tileset.getString());
gameSettings->setTech(data.tech.getString());
gameSettings->setFactionCount(data.factionCount);
gameSettings->setThisFactionIndex(data.thisFactionIndex);
gameSettings->setDefaultResources(data.defaultResources);
gameSettings->setDefaultUnits(data.defaultUnits);
gameSettings->setDefaultVictoryConditions(data.defaultVictoryConditions);
for(int i= 0; i<data.factionCount; ++i){
gameSettings->setFactionTypeName(i, data.factionTypeNames[i].getString());
gameSettings->setFactionControl(i, static_cast<ControlType>(data.factionControls[i]));
gameSettings->setTeam(i, data.teams[i]);
gameSettings->setStartLocationIndex(i, data.startLocationIndex[i]);
}
}