frontport r8667

"Rewrite the stupid file transfer routines.
* It will now only send the file to the person that needs it.

* Adds abort code to the file routines as well, and also tries to detect when the Host is using a malformed filename to host a map.

* Make sure user can't hit the 'ready' button when the map transfer is ongoing.

* Get rid of the silly delays (Much faster transfers)

NOTE: we are still capped by fps. 
fixes ticket:1128
fixes ticket:1127
refs ticket:215 (still can't transfer 'map packs')"

git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/trunk@8734 4a71c877-e1ca-e34f-864e-861f7616d084
master
Buginator 2009-12-19 00:23:37 +00:00 committed by Git SVN Gateway
parent 87802afa73
commit 38f66c5f9e
7 changed files with 211 additions and 136 deletions

View File

@ -202,8 +202,6 @@ typedef struct
Socket** fds;
} SocketSet;
#define PLAYER_HOST 1
// ////////////////////////////////////////////////////////////////////////
// Variables
@ -2309,6 +2307,7 @@ BOOL NETbcast(NETMSG *msg)
tcp_socket = NULL;
NetPlay.players[NetPlay.hostPlayer].heartbeat = false; // mark host as dead
//Game is pretty much over --should just end everything when HOST dies.
setLobbyError(ERROR_HOSTDROPPED);
return false;
}
}
@ -2720,78 +2719,52 @@ BOOL NETsetupTCPIP(const char *machine)
// ////////////////////////////////////////////////////////////////////////
// File Transfer programs.
/** Send file. It returns % of file sent when 100 it's complete. Call until it returns 100.
* @TODO Needs to be rewritten. See issue #215. */
* @TODO: more error checking (?) different file types (?)
* Maybe should close file handle, and seek each time?
*
* @NOTE: MAX_FILE_TRANSFER_PACKET is set to 2k per packet since 7*2 = 14K which is pretty
* much our limit. Don't screw with that without having a bigger buffer!
* NET_BUFFER_SIZE is at 16k. (also remember text chat, plus all the other cruff)
*/
#define MAX_FILE_TRANSFER_PACKET 2048
UBYTE NETsendFile(BOOL newFile, char *fileName, UDWORD player)
UBYTE NETsendFile(char *fileName, UDWORD player)
{
static int32_t currPos;
static PHYSFS_sint64 fileSize_64;
static PHYSFS_sint32 fileSize_32; // we don't support 64bit nettypes yet.
static PHYSFS_file *pFileHandle;
int32_t bytesRead;
char inBuff[MAX_FILE_TRANSFER_PACKET];
uint8_t sendto = 0;
int32_t bytesRead = 0;
uint8_t sendto = 0;
char inBuff[MAX_FILE_TRANSFER_PACKET];
// We are not the host, so we don't care. (in fact, this would be a error)
if(!NetPlay.isHost)
{
return true;
}
memset(inBuff, 0x0, sizeof(inBuff));
if (newFile)
{
// open the file.
pFileHandle = PHYSFS_openRead(fileName); // check file exists
debug(LOG_NET, "Sending [directory: %s] %s to client", PHYSFS_getRealDir(fileName), fileName);
if (pFileHandle == NULL)
{
debug(LOG_ERROR, "Failed to open %s for reading: %s", fileName, PHYSFS_getLastError());
return 0; // failed
}
// get the file's size.
fileSize_64 = PHYSFS_fileLength(pFileHandle);
fileSize_32 = (int32_t) fileSize_64; // we don't support 64bit int nettypes.
currPos = 0;
}
// read some bytes.
if (!pFileHandle)
{
debug(LOG_ERROR, "No filehandle");
return 0; // failed
}
bytesRead = PHYSFS_read(pFileHandle, inBuff,1, MAX_FILE_TRANSFER_PACKET);
bytesRead = PHYSFS_read(NetPlay.players[player].wzFile.pFileHandle, inBuff,1, MAX_FILE_TRANSFER_PACKET);
sendto = (uint8_t) player;
if (player == 0)
{ // FIXME: why would you send (map) file to everyone ??
// even if they already have it? multiplay.c 1529 & 1550 are both
// NETsendFile(true,mapStr,0); & NETsendFile(false,game.map,0);
// so we ALWAYS send it, it seems?
// NOTE: we send to everyone since there is no way to signal the host
// to stop sending the map. Which means that everytime a player joins
// that doesn't have the map, it will send to all players again.
// We also are limited by fps(!) to the amount of time the loop runs.
NETbeginEncode(FILEMSG, NET_ALL_PLAYERS); // send it.
}
else
{
sendto = (uint8_t) player;
NETbeginEncode(FILEMSG,sendto);
}
// form a message
NETint32_t(&fileSize_32); // total bytes in this file. (we don't support 64bit yet)
NETint32_t(&bytesRead); // bytes in this packet
NETint32_t(&currPos); // start byte
NETstring(fileName, 256); //256 = max filename size
NETbin(inBuff, bytesRead);
NETbeginEncode(NET_FILE_PAYLOAD, sendto);
NETint32_t(&NetPlay.players[player].wzFile.fileSize_32); // total bytes in this file. (we don't support 64bit yet)
NETint32_t(&bytesRead); // bytes in this packet
NETint32_t(&NetPlay.players[player].wzFile.currPos); // start byte
NETstring(fileName, 256); //256 = max filename size
NETbin(inBuff, bytesRead);
NETend();
currPos += bytesRead; // update position!
if(currPos == fileSize_64)
NetPlay.players[player].wzFile.currPos += bytesRead; // update position!
if(NetPlay.players[player].wzFile.currPos == NetPlay.players[player].wzFile.fileSize_32)
{
PHYSFS_close(pFileHandle);
PHYSFS_close(NetPlay.players[player].wzFile.pFileHandle);
NetPlay.players[player].wzFile.isSending = false; // we are done sending to this client.
NetPlay.players[player].needFile = false;
}
return (currPos * 100) / fileSize_64;
return (NetPlay.players[player].wzFile.currPos * 100) / NetPlay.players[player].wzFile.fileSize_32;
}
/* @TODO Needs to be rewritten. See issue #215. */
/* @TODO more error checking (?) different file types (?) */
// recv file. it returns % of the file so far recvd.
UBYTE NETrecvFile(void)
{
@ -2799,18 +2772,17 @@ UBYTE NETrecvFile(void)
char fileName[256];
char outBuff[MAX_FILE_TRANSFER_PACKET];
static PHYSFS_file *pFileHandle;
static bool isLoop = false;
memset(fileName, 0x0, sizeof(fileName));
memset(outBuff, 0x0, sizeof(outBuff));
//read incoming bytes.
NETbeginDecode(FILEMSG);
NETbeginDecode(NET_FILE_PAYLOAD);
NETint32_t(&fileSize); // total bytes in this file.
NETint32_t(&bytesRead); // bytes in this packet
NETint32_t(&currPos); // start byte
// read filename
NETstring(fileName, 256); // Ugh. 256 = max array size
NETstring(fileName, 256); // read filename (only valid on 1st packet)
debug(LOG_NET, "Creating new file %s, position is %d", fileName, currPos);
if (currPos == 0) // first packet!
@ -2823,21 +2795,38 @@ UBYTE NETrecvFile(void)
fsize = PHYSFS_fileLength(fin);
if ((int32_t) fsize == fileSize)
{
// NOTE: we would send a abort message to host, but since we can't,
// we won't.
#ifdef DEBUG
debug(LOG_NET, "We already have the file %s.", fileName);
#endif
uint32_t reason = ALREADY_HAVE_FILE;
debug(LOG_NET, "We already have the file %s! ", fileName);
PHYSFS_close(fin);
NETend();
// NOTE: we can't abort out, since the host ALWAYS sends the data until done
// PHYSFS_close(fin);
// NETend();
// return 100;
NETbeginEncode(NET_FILE_CANCELLED, NET_HOST_ONLY);
NETuint32_t(&selectedPlayer);
NETuint32_t(&reason);
NETend();
if (!isLoop)
{
isLoop = true;
}
else
{
uint32_t reason = STUCK_IN_FILE_LOOP;
NETend();
// we should never get here, it means, that the game can't detect the level, but we have the file.
// so we kick this player out.
NETbeginEncode(NET_FILE_CANCELLED, NET_HOST_ONLY);
NETuint32_t(&selectedPlayer);
NETuint32_t(&reason);
NETend();
debug(LOG_FATAL, "Something is really wrong with the file's (%s) data, game can't detect it?", fileName);
return 100;
}
}
PHYSFS_close(fin);
#ifdef DEBUG
debug(LOG_NET, "We have the same named file, but different size. Redownloading %s", fileName);
#endif
debug(LOG_NET, "We already have the file %s, but different size %d vs %d. Redownloading", fileName, (int32_t) fsize, fileSize);
}
pFileHandle = PHYSFS_openWrite(fileName); // create a new file.
}

View File

@ -27,6 +27,7 @@
#define _netplay_h
#include "nettypes.h"
#include <physfs.h>
// Lobby Connection errors
@ -40,7 +41,8 @@ typedef enum
ERROR_WRONGVERSION,
ERROR_WRONGPASSWORD, // NOTE WRONG_PASSWORD results in conflict
ERROR_HOSTDROPPED,
ERROR_WRONGDATA
ERROR_WRONGDATA,
ERROR_UNKNOWNFILEISSUE
} LOBBY_ERROR_TYPES;
@ -92,7 +94,7 @@ typedef enum
NET_DROIDDISEMBARK, //43 droid disembarked from a Transporter
NET_RESEARCHSTATUS, //44 research state.
NET_LASSAT, //45 lassat firing.
NET_REQUESTMAP, //46 dont have map, please send it.
NET_UNUSED_46, //46 old map request, now unused.
NET_AITEXTMSG, //47 chat between AIs
NET_TEAMS_ON, //48 locked teams mode
NET_BEACONMSG, //49 place beacon
@ -114,6 +116,12 @@ typedef enum
NET_POSITIONREQUEST, //65 position in GUI player list
NET_DATA_CHECK, //66 Data integrity check
NET_HOST_DROPPED, //67 Host has dropped
NET_FUTURE1, //68 future use
NET_FUTURE2, //69 "
NET_FUTURE3, //70 "
NET_FILE_REQUESTED, //71 Player has requested a file (map/mod/?)
NET_FILE_CANCELLED, //72 Player cancelled a file request
NET_FILE_PAYLOAD, //73 sending file to the player that needs it
NUM_GAME_PACKETS // *MUST* be last.
} MESSAGE_TYPES;
@ -182,8 +190,29 @@ typedef struct {
BOOL status; // If the packet compiled or not (this is _not_ sent!)
} NETMSG;
#define FILEMSG 254 // a file packet
typedef struct
{
PHYSFS_file *pFileHandle; // handle
PHYSFS_sint32 fileSize_32; // size
int32_t currPos; // current position
BOOL isSending; // sending to this player
BOOL isCancelled; // player cancelled
int32_t filetype; // future use (1=map 2=mod 3=...)
} WZFile;
typedef struct
{
int32_t player; // the client we sent data to
int32_t done; // how far done we are (100= finished)
int32_t byteCount; // current byte count
} wzFileStatus;
typedef enum
{
WZ_FILE_OK,
ALREADY_HAVE_FILE,
STUCK_IN_FILE_LOOP
} wzFileEnum;
// ////////////////////////////////////////////////////////////////////////
// Player information. Filled when players join, never re-ordered. selectedPlayer global points to
// currently controlled player. This array is indexed by GUI slots in pregame.
@ -201,6 +230,8 @@ typedef struct
BOOL ready; ///< player ready to start?
uint32_t versionCheckTime; ///< Time when check sent. Uses 0xffffffff for nothing sent yet
BOOL playerVersionFlag; ///< We kick on false
BOOL needFile; ///< if We need a file sent to us
WZFile wzFile; ///< for each player, we keep track of map progress
} PLAYER;
// ////////////////////////////////////////////////////////////////////////
@ -238,7 +269,7 @@ extern BOOL NETsend(NETMSG *msg, UDWORD player); // send to player
extern BOOL NETbcast(NETMSG *msg); // broadcast to everyone
extern BOOL NETrecv(uint8_t *type); // recv a message if possible
extern UBYTE NETsendFile(BOOL newFile, char *fileName, UDWORD player); // send file chunk.
extern UBYTE NETsendFile(char *fileName, UDWORD player); // send file chunk.
extern UBYTE NETrecvFile(void); // recv file chunk
extern int NETclose(void); // close current game

View File

@ -152,16 +152,8 @@ UDWORD hashBuffer(uint8_t *pData, uint32_t size)
{
val = (uint32_t *)(NewData+pt);
// original:
//hashval = SDL_SwapBE32(hashval ^ SDL_SwapBE32((*val))); // I hope this is correct...can't test since no PPC machine
// Next time, ask someone who does, or at least get some someone to double-check your math. ;)
hashval ^= (*val);
// Here is a solution that makes BE archs binary-compatible with the LE output of the above
hashval = SDL_Swap32(hashval ^ SDL_Swap32(*val));
// Here is the solution that the above was probably intended to do:
//hashval ^= (*val); // no endianness enforcement, since it'll be done by whatever handles its output
// spams a ton--but useful for debugging.
// debug(LOG_NET, "hash %08x pt %08x val is %08x", hashval, pt, *val);
pt += 4;
@ -175,6 +167,8 @@ UDWORD hashBuffer(uint8_t *pData, uint32_t size)
return hashval;
}
// create the hash for that data block.
// Data should be converted to Network byte order
void calcDataHash(uint8_t *pBuffer, uint32_t size, uint32_t index)
{
if (!bMultiPlayer)
@ -182,18 +176,9 @@ void calcDataHash(uint8_t *pBuffer, uint32_t size, uint32_t index)
return;
}
// create the hash for that data block.
DataHash[index] ^= SDL_SwapBE32(hashBuffer(pBuffer, size));
// Here is the original solution:
//DataHash[index] = SDL_SwapBE32(DataHash[index] ^ hashBuffer(pBuffer, size)); // check endian issues?
// Here is a solution that makes BE archs binary-compatible with the LE output of the above
DataHash[index] = SDL_Swap32(DataHash[index] ^ SDL_SwapLE32(hashBuffer(pBuffer, size)));
// Here is the solution that the above was probably intended to do:
//DataHash[index] ^= SDL_SwapBE32(hashBuffer(pBuffer, size));
debug(LOG_NET, "DataHash[%2u] = %08x\n", index, DataHash[index]);
debug(LOG_NET, "DataHash[%2u] = %08x", index, DataHash[index]);
return;
}

View File

@ -837,6 +837,11 @@ static void addGames(void)
case ERROR_WRONGDATA:
txt = _("Wrong data/mod detected by Host.");
break;
// AFAIK, the only way this can really happy is if the Host's file is named wrong, or a client side error.
case ERROR_UNKNOWNFILEISSUE:
txt = _("Host couldn't send file?");
debug(LOG_POPUP, "Warzone couldn't complete a file request.\n\nPossibly, Host's file is incorrect. Check your logs for more details.");
break;
case ERROR_WRONGPASSWORD:
txt = _("Incorrect Password!");
break;
@ -1577,6 +1582,13 @@ BOOL recvReadyRequest()
return false;
}
// do not allow players to select 'ready' if we are sending a map too them!
// TODO: make a new icon to show this state?
if (NetPlay.players[player].wzFile.isSending)
{
return false;
}
return changeReadyStatus((UBYTE)player, bReady);
}
@ -2695,11 +2707,11 @@ void frontendMultiMessages(void)
// Copy the message to the global one used by the new NET API
switch(type)
{
case NET_REQUESTMAP:
case NET_FILE_REQUESTED:
recvMapFileRequested();
break;
case FILEMSG:
case NET_FILE_PAYLOAD:
widgSetButtonState(psWScreen, MULTIOP_MAP_BUT, 1); // turn preview button off
if (recvMapFileData())
{
@ -2707,6 +2719,34 @@ void frontendMultiMessages(void)
}
break;
case NET_FILE_CANCELLED: // host only routine
{
uint32_t reason;
uint32_t victim;
NETbeginDecode(NET_FILE_CANCELLED);
NETuint32_t(&victim);
NETuint32_t(&reason);
NETend();
switch (reason)
{
case STUCK_IN_FILE_LOOP:
debug(LOG_WARNING, "Received file cancel request from player %u, They are stuck in a loop?", victim);
kickPlayer(victim, "couldn't upload file for some reason. ", ERROR_UNKNOWNFILEISSUE);
NetPlay.players[victim].wzFile.isCancelled = true;
NetPlay.players[victim].wzFile.isSending = false;
break;
case ALREADY_HAVE_FILE:
default:
debug(LOG_WARNING, "Received file cancel request from player %u, They already have the file ?", victim);
NetPlay.players[victim].wzFile.isCancelled = true;
NetPlay.players[victim].wzFile.isSending = false;
break;
}
}
break;
case NET_OPTIONS: // incoming options file.
recvOptions();
ingame.localOptionsReceived = true;
@ -2848,8 +2888,8 @@ void frontendMultiMessages(void)
void runMultiOptions(void)
{
static UDWORD lastrefresh=0;
UDWORD id,value;//,i;
static UDWORD lastrefresh = 0;
UDWORD id, value, i;
char sTemp[128];
PLAYERSTATS playerStats;
W_CONTEXT context;
@ -2859,10 +2899,13 @@ void runMultiOptions(void)
frontendMultiMessages();
// keep sending the map if required.
if(bSendingMap)
for (i = 0; i < MAX_PLAYERS; i++)
{
sendMap();
// send it for each player that needs it
if (NetPlay.players[i].wzFile.isSending)
{
sendMap();
}
}
// update boxes?

View File

@ -220,7 +220,7 @@ void recvOptions()
debug(LOG_NET, "Map was not found, requesting map %s from host.", game.map);
// Request the map from the host
NETbeginEncode(NET_REQUESTMAP, NET_HOST_ONLY);
NETbeginEncode(NET_FILE_REQUESTED, NET_HOST_ONLY);
NETuint32_t(&player);
NETend();

View File

@ -80,8 +80,6 @@ UBYTE bDisplayMultiJoiningStatus;
MULTIPLAYERGAME game; //info to describe game.
MULTIPLAYERINGAME ingame;
BOOL bSendingMap = false; // map broadcasting.
char beaconReceiveMsg[MAX_PLAYERS][MAX_CONSOLE_STRING_LENGTH]; //beacon msg for each player
char playerName[MAX_PLAYERS][MAX_STR_LENGTH]; //Array to store all player names (humans and AIs)
BOOL bPlayerReadyGUI[MAX_PLAYERS] = {false};
@ -1558,20 +1556,32 @@ BOOL recvDestroyFeature()
BOOL recvMapFileRequested()
{
char mapStr[256],mapName[256],fixedname[256];
uint32_t player;
// another player is requesting the map
PHYSFS_sint64 fileSize_64;
PHYSFS_file *pFileHandle;
// We are not the host, so we don't care. (in fact, this would be a error)
if(!NetPlay.isHost)
{
return true;
}
// start sending the map to the other players.
if(!bSendingMap)
// Check to see who wants the file
NETbeginDecode(NET_FILE_REQUESTED);
NETuint32_t(&player);
NETend();
if (!NetPlay.players[player].wzFile.isSending)
{
NetPlay.players[player].needFile = true;
NetPlay.players[player].wzFile.isCancelled = false;
NetPlay.players[player].wzFile.isSending = true;
memset(mapStr,0,256);
memset(mapName,0,256);
memset(fixedname,0,256);
bSendingMap = true;
addConsoleMessage("Map was requested: SENDING MAP!",DEFAULT_JUSTIFY, SYSTEM_MESSAGE);
sstrcpy(mapName, game.map);
@ -1592,38 +1602,55 @@ BOOL recvMapFileRequested()
snprintf(mapStr, sizeof(mapStr), "%dc-%s.wz", game.maxPlayers, mapName);
snprintf(fixedname, sizeof(fixedname), "maps/%s", mapStr); //We know maps are in /maps dir...now. fix for linux -Q
sstrcpy(mapStr, fixedname);
debug(LOG_NET, "Map was requested. Sending %s", mapStr);
// NOTE: should we check if file exsists before trying to send it ?
NETsendFile(true,mapStr,0);
debug(LOG_NET, "Map was requested. Looking for %s", mapStr);
// Checking to see if file is available...
pFileHandle = PHYSFS_openRead(mapStr);
if (pFileHandle == NULL)
{
debug(LOG_ERROR, "Failed to open %s for reading: %s", mapStr, PHYSFS_getLastError());
debug(LOG_FATAL, "You have a map (%s) that can't be located.\n\nMake sure it is in the correct directory and or format!", mapStr);
// NOTE: if we get here, then the game is basically over, The host can't send the file for whatever reason...
// Which also means, that we can't continue.
debug(LOG_NET, "***Host has a file issue, and is being forced to quit!***");
NETbeginEncode(NET_HOST_DROPPED, NET_ALL_PLAYERS);
NETend();
abort();
}
// get the file's size.
fileSize_64 = PHYSFS_fileLength(pFileHandle);
debug(LOG_NET, "File is valid, sending [directory: %s] %s to client %u", PHYSFS_getRealDir(mapStr), mapStr, player);
NetPlay.players[player].wzFile.pFileHandle = pFileHandle;
NetPlay.players[player].wzFile.fileSize_32 = (int32_t) fileSize_64; //we don't support 64bit int nettypes.
NetPlay.players[player].wzFile.currPos = 0;
NETsendFile(mapStr, player);
}
return true;
}
// continue sending the map
UBYTE sendMap(void)
void sendMap(void)
{
int i = 0;
UBYTE done;
static UDWORD lastCall;
if(lastCall > gameTime)lastCall= 0;
if ( (gameTime - lastCall) <200)
for (i = 0; i < MAX_PLAYERS; i++)
{
return 0;
}
lastCall = gameTime;
done = NETsendFile(false,game.map,0);
if(done == 100)
if (NetPlay.players[i].wzFile.isSending)
{
done = NETsendFile(game.map, i);
if (done == 100)
{
addConsoleMessage("MAP SENT!",DEFAULT_JUSTIFY, SYSTEM_MESSAGE);
bSendingMap = false;
debug(LOG_NET, "=== File has been sent to player %d ===", i);
NetPlay.players[i].wzFile.isSending = false;
NetPlay.players[i].needFile = false;
}
}
}
return done;
}
// Another player is broadcasting a map, recv a chunk. Returns false if not yet done.
@ -1634,6 +1661,7 @@ BOOL recvMapFileData()
{
addConsoleMessage("MAP DOWNLOADED!",DEFAULT_JUSTIFY, SYSTEM_MESSAGE);
sendTextMessage("MAP DOWNLOADED",true); //send
debug(LOG_NET, "=== File has been received. ===");
// clear out the old level list.
levShutDown();

View File

@ -147,8 +147,7 @@ extern BOOL sendAIMessage (char *pStr, UDWORD player, UDWORD to); //send AI mes
extern BOOL turnOffMultiMsg (BOOL bDoit);
extern UBYTE sendMap (void);
extern void sendMap(void);
extern BOOL multiplayerWinSequence(BOOL firstCall);
/////////////////////////////////////////////////////////