Uses Protocol V4 - BSON.
parent
54fca60476
commit
754e6c15c7
|
@ -14,6 +14,7 @@ AM_CXXFLAGS = $(WZ_CXXFLAGS) $(QT4_CFLAGS)
|
||||||
|
|
||||||
noinst_LIBRARIES = libnetplay.a
|
noinst_LIBRARIES = libnetplay.a
|
||||||
noinst_HEADERS = \
|
noinst_HEADERS = \
|
||||||
|
netlobby.h \
|
||||||
netlog.h \
|
netlog.h \
|
||||||
netplay.h \
|
netplay.h \
|
||||||
netqueue.h \
|
netqueue.h \
|
||||||
|
@ -21,9 +22,9 @@ noinst_HEADERS = \
|
||||||
nettypes.h
|
nettypes.h
|
||||||
|
|
||||||
libnetplay_a_SOURCES = \
|
libnetplay_a_SOURCES = \
|
||||||
netjoin_stub.cpp \
|
netsocket.cpp \
|
||||||
|
netlobby.cpp \
|
||||||
netlog.cpp \
|
netlog.cpp \
|
||||||
netplay.cpp \
|
netplay.cpp \
|
||||||
netqueue.cpp \
|
netqueue.cpp \
|
||||||
netsocket.cpp \
|
|
||||||
nettypes.cpp
|
nettypes.cpp
|
||||||
|
|
|
@ -1,34 +0,0 @@
|
||||||
/*
|
|
||||||
This file is part of Warzone 2100.
|
|
||||||
Copyright (C) 1999-2004 Eidos Interactive
|
|
||||||
Copyright (C) 2005-2011 Warzone 2100 Project
|
|
||||||
|
|
||||||
Warzone 2100 is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
Warzone 2100 is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with Warzone 2100; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
*/
|
|
||||||
/*
|
|
||||||
* Net join.
|
|
||||||
* join related stuff
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "lib/framework/frame.h"
|
|
||||||
#include "netplay.h"
|
|
||||||
|
|
||||||
int32_t NETgetGameFlagsUnjoined(unsigned int gameid, unsigned int flag)
|
|
||||||
{
|
|
||||||
ASSERT(gameid < ARRAY_SIZE(NetPlay.games), "Out of range gameid: %u", gameid);
|
|
||||||
ASSERT(flag < ARRAY_SIZE(NetPlay.games[gameid].desc.dwUserFlags), "Out of range flag number: %u", flag);
|
|
||||||
|
|
||||||
return NetPlay.games[gameid].desc.dwUserFlags[flag];
|
|
||||||
}
|
|
|
@ -0,0 +1,534 @@
|
||||||
|
/*
|
||||||
|
This file is part of Warzone 2100.
|
||||||
|
Copyright (C) 1999-2004 Eidos Interactive
|
||||||
|
Copyright (C) 2005-2011 Warzone 2100 Project
|
||||||
|
|
||||||
|
Warzone 2100 is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
Warzone 2100 is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with Warzone 2100; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "netlobby.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Variables
|
||||||
|
*/
|
||||||
|
LobbyClient lobbyclient;
|
||||||
|
|
||||||
|
LobbyClient::LobbyClient()
|
||||||
|
{
|
||||||
|
// Set defaults
|
||||||
|
callId_ = 1;
|
||||||
|
|
||||||
|
gameId_ = 0;
|
||||||
|
socket_ = NULL;
|
||||||
|
|
||||||
|
lastError_.code = LOBBY_NO_ERROR;
|
||||||
|
lastError_.message = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LobbyClient::stop()
|
||||||
|
{
|
||||||
|
// remove the game from the masterserver.
|
||||||
|
lobbyclient.delGame();
|
||||||
|
lobbyclient.freeError();
|
||||||
|
|
||||||
|
// Clear/free up games.
|
||||||
|
games.clear();
|
||||||
|
|
||||||
|
lobbyclient.disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
LOBBY_ERROR LobbyClient::addGame(char** result, uint32_t port, uint32_t maxPlayers,
|
||||||
|
const char* description, const char* versionstring,
|
||||||
|
uint32_t game_version_major, uint32_t game_version_minor,
|
||||||
|
bool isPrivate, const char* modlist,
|
||||||
|
const char* mapname, const char* hostplayer)
|
||||||
|
{
|
||||||
|
bson kwargs[1];
|
||||||
|
bson_buffer buffer[1];
|
||||||
|
|
||||||
|
if (gameId_ != 0)
|
||||||
|
return LOBBY_NO_ERROR;
|
||||||
|
|
||||||
|
bson_buffer_init(buffer);
|
||||||
|
bson_append_int(buffer, "port", port);
|
||||||
|
bson_append_string(buffer, "description", description);
|
||||||
|
bson_append_int(buffer, "currentPlayers", 1);
|
||||||
|
bson_append_int(buffer, "maxPlayers", maxPlayers);
|
||||||
|
bson_append_string(buffer, "multiVer", versionstring);
|
||||||
|
bson_append_int(buffer, "wzVerMajor", game_version_major);
|
||||||
|
bson_append_int(buffer, "wzVerMinor", game_version_minor);
|
||||||
|
bson_append_bool(buffer, "isPrivate", isPrivate);
|
||||||
|
bson_append_string(buffer, "modlist", modlist);
|
||||||
|
bson_append_string(buffer, "mapname", mapname);
|
||||||
|
bson_append_string(buffer, "hostplayer", hostplayer);
|
||||||
|
|
||||||
|
bson_from_buffer(kwargs, buffer);
|
||||||
|
|
||||||
|
if (call_("addGame", NULL, kwargs) != LOBBY_NO_ERROR)
|
||||||
|
{
|
||||||
|
// Prevents "addGame" until "delGame" gets called.
|
||||||
|
gameId_ = -1;
|
||||||
|
return lastError_.code;
|
||||||
|
}
|
||||||
|
bson_destroy(kwargs);
|
||||||
|
|
||||||
|
if (callResult_.code != LOBBY_NO_ERROR)
|
||||||
|
{
|
||||||
|
debug(LOG_ERROR, "Received: server error %d: %s", callResult_.code, callResult_.result);
|
||||||
|
|
||||||
|
// Prevents "addGame" until "delGame" gets called.
|
||||||
|
gameId_ = -1;
|
||||||
|
|
||||||
|
setError_(callResult_.code, _("Got server error %d!"), callResult_.code);
|
||||||
|
freeCallResult_();
|
||||||
|
return lastError_.code;
|
||||||
|
}
|
||||||
|
|
||||||
|
bson_iterator it;
|
||||||
|
bson_iterator_init(&it, callResult_.result);
|
||||||
|
|
||||||
|
bson_iterator_next(&it);
|
||||||
|
if (bson_iterator_type(&it) != bson_int)
|
||||||
|
{
|
||||||
|
freeCallResult_();
|
||||||
|
return setError_(LOBBY_INVALID_DATA, _("Received invalid addGame data."));
|
||||||
|
}
|
||||||
|
gameId_ = bson_iterator_int(&it);
|
||||||
|
|
||||||
|
bson_iterator_next(&it);
|
||||||
|
if (bson_iterator_type(&it) != bson_string)
|
||||||
|
{
|
||||||
|
freeCallResult_();
|
||||||
|
return setError_(LOBBY_INVALID_DATA, _("Received invalid addGame data."));
|
||||||
|
}
|
||||||
|
asprintf(result, bson_iterator_string(&it));
|
||||||
|
|
||||||
|
freeCallResult_();
|
||||||
|
return LOBBY_NO_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOBBY_ERROR LobbyClient::delGame()
|
||||||
|
{
|
||||||
|
bson kwargs[1];
|
||||||
|
bson_buffer buffer[1];
|
||||||
|
|
||||||
|
if (gameId_ == 0)
|
||||||
|
{
|
||||||
|
return LOBBY_NO_ERROR;
|
||||||
|
}
|
||||||
|
else if (gameId_ == -1)
|
||||||
|
{
|
||||||
|
// Clear an error.
|
||||||
|
gameId_ = 0;
|
||||||
|
return LOBBY_NO_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
bson_buffer_init(buffer);
|
||||||
|
bson_append_int(buffer, "gameId", gameId_);
|
||||||
|
bson_from_buffer(kwargs, buffer);
|
||||||
|
|
||||||
|
if (call_("delGame", NULL, kwargs) != LOBBY_NO_ERROR)
|
||||||
|
return lastError_.code;
|
||||||
|
bson_destroy(kwargs);
|
||||||
|
|
||||||
|
if (callResult_.code != LOBBY_NO_ERROR)
|
||||||
|
{
|
||||||
|
debug(LOG_ERROR, "Received: server error %d: %s.", callResult_.code, callResult_.result);
|
||||||
|
setError_(callResult_.code, _("Got server error (%d)."), callResult_.code);
|
||||||
|
freeCallResult_();
|
||||||
|
return lastError_.code;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
gameId_ = 0;
|
||||||
|
|
||||||
|
freeCallResult_();
|
||||||
|
return LOBBY_NO_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOBBY_ERROR LobbyClient::addPlayer(unsigned int index, const char* name, const char* ipaddress)
|
||||||
|
{
|
||||||
|
bson kwargs[1];
|
||||||
|
bson_buffer buffer[1];
|
||||||
|
|
||||||
|
if (gameId_ == 0 || gameId_ == -1)
|
||||||
|
return setError_(LOBBY_NO_GAME, _("Create a game first!"));
|
||||||
|
|
||||||
|
bson_buffer_init(buffer);
|
||||||
|
bson_append_int(buffer, "gameId", gameId_);
|
||||||
|
bson_append_int(buffer, "slot", index);
|
||||||
|
bson_append_string(buffer, "name", name);
|
||||||
|
bson_append_string(buffer, "ipaddress", ipaddress);
|
||||||
|
bson_from_buffer(kwargs, buffer);
|
||||||
|
|
||||||
|
if (call_("addPlayer", NULL, kwargs) != LOBBY_NO_ERROR)
|
||||||
|
return lastError_.code;
|
||||||
|
bson_destroy(kwargs);
|
||||||
|
|
||||||
|
if (callResult_.code != LOBBY_NO_ERROR)
|
||||||
|
{
|
||||||
|
debug(LOG_ERROR, "Received: server error %d: %s.", callResult_.code, callResult_.result);
|
||||||
|
setError_(callResult_.code, _("Got server error (%d)."), callResult_.code);
|
||||||
|
freeCallResult_();
|
||||||
|
return lastError_.code;
|
||||||
|
}
|
||||||
|
|
||||||
|
freeCallResult_();
|
||||||
|
return LOBBY_NO_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOBBY_ERROR LobbyClient::delPlayer(unsigned int index)
|
||||||
|
{
|
||||||
|
bson kwargs[1];
|
||||||
|
bson_buffer buffer[1];
|
||||||
|
|
||||||
|
if (gameId_ == 0 || gameId_ == -1)
|
||||||
|
return setError_(LOBBY_NO_GAME, _("Create a game first!"));
|
||||||
|
|
||||||
|
bson_buffer_init(buffer);
|
||||||
|
bson_append_int(buffer, "gameId", gameId_);
|
||||||
|
bson_append_int(buffer, "slot", index);
|
||||||
|
bson_from_buffer(kwargs, buffer);
|
||||||
|
|
||||||
|
if (call_("delPlayer", NULL, kwargs) != LOBBY_NO_ERROR)
|
||||||
|
return lastError_.code;
|
||||||
|
bson_destroy(kwargs);
|
||||||
|
|
||||||
|
if (callResult_.code != LOBBY_NO_ERROR)
|
||||||
|
{
|
||||||
|
debug(LOG_ERROR, "Received: server error %d: %s.", callResult_.code, callResult_.result);
|
||||||
|
setError_(callResult_.code, _("Got server error (%d)."), callResult_.code);
|
||||||
|
freeCallResult_();
|
||||||
|
return lastError_.code;
|
||||||
|
}
|
||||||
|
|
||||||
|
freeCallResult_();
|
||||||
|
return LOBBY_NO_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOBBY_ERROR LobbyClient::updatePlayer(unsigned int index, const char* name)
|
||||||
|
{
|
||||||
|
bson kwargs[1];
|
||||||
|
bson_buffer buffer[1];
|
||||||
|
|
||||||
|
if (gameId_ == 0 || gameId_ == -1)
|
||||||
|
return setError_(LOBBY_NO_GAME, _("Create a game first!"));
|
||||||
|
|
||||||
|
bson_buffer_init(buffer);
|
||||||
|
bson_append_int(buffer, "gameId", gameId_);
|
||||||
|
bson_append_int(buffer, "slot", index);
|
||||||
|
bson_append_string(buffer, "name", name);
|
||||||
|
bson_from_buffer(kwargs, buffer);
|
||||||
|
|
||||||
|
if (call_("updatePlayer", NULL, kwargs) != LOBBY_NO_ERROR)
|
||||||
|
return lastError_.code;
|
||||||
|
bson_destroy(kwargs);
|
||||||
|
|
||||||
|
if (callResult_.code != LOBBY_NO_ERROR)
|
||||||
|
{
|
||||||
|
debug(LOG_ERROR, "Received: server error %d: %s.", callResult_.code, callResult_.result);
|
||||||
|
setError_(callResult_.code, _("Got server error (%d)."), callResult_.code);
|
||||||
|
freeCallResult_();
|
||||||
|
return lastError_.code;
|
||||||
|
}
|
||||||
|
|
||||||
|
freeCallResult_();
|
||||||
|
return LOBBY_NO_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOBBY_ERROR LobbyClient::listGames(int maxGames)
|
||||||
|
{
|
||||||
|
bson_iterator it, gameIt;
|
||||||
|
const char * key;
|
||||||
|
uint32_t gameCount = 0;
|
||||||
|
LOBBY_GAME game;
|
||||||
|
|
||||||
|
// Clear old games.
|
||||||
|
games.clear();
|
||||||
|
|
||||||
|
// Run "list" and retrieve its result
|
||||||
|
if (call_("list", NULL, NULL) != LOBBY_NO_ERROR)
|
||||||
|
return lastError_.code;
|
||||||
|
|
||||||
|
//Print the result to stdout.
|
||||||
|
//bson_print_raw(callResult_.result, 0);
|
||||||
|
|
||||||
|
// Translate the result into LOBBY_GAMEs
|
||||||
|
bson_iterator_init(&it, callResult_.result);
|
||||||
|
while (bson_iterator_next(&it) && gameCount <= maxGames)
|
||||||
|
{
|
||||||
|
// new Game
|
||||||
|
if (bson_iterator_type(&it) == bson_object)
|
||||||
|
{
|
||||||
|
bson_iterator_init(&gameIt, bson_iterator_value(&it));
|
||||||
|
while (bson_iterator_next(&gameIt))
|
||||||
|
{
|
||||||
|
key = bson_iterator_key(&gameIt);
|
||||||
|
|
||||||
|
if (strcmp(key, "port") == 0)
|
||||||
|
{
|
||||||
|
game.port = (uint32_t)bson_iterator_int(&gameIt);
|
||||||
|
}
|
||||||
|
else if (strcmp(key, "host") == 0) // Generated at server side.
|
||||||
|
{
|
||||||
|
game.host = bson_iterator_string(&gameIt);
|
||||||
|
}
|
||||||
|
if (strcmp(key, "description") == 0)
|
||||||
|
{
|
||||||
|
game.description = bson_iterator_string(&gameIt);
|
||||||
|
}
|
||||||
|
else if (strcmp(key, "currentPlayers") == 0)
|
||||||
|
{
|
||||||
|
game.currentPlayers = bson_iterator_int(&gameIt);
|
||||||
|
}
|
||||||
|
else if (strcmp(key, "maxPlayers") == 0)
|
||||||
|
{
|
||||||
|
game.maxPlayers = bson_iterator_int(&gameIt);
|
||||||
|
}
|
||||||
|
else if (strcmp(key, "multiVer") == 0)
|
||||||
|
{
|
||||||
|
game.versionstring = bson_iterator_string(&gameIt);
|
||||||
|
}
|
||||||
|
else if (strcmp(key, "wzVerMajor") == 0)
|
||||||
|
{
|
||||||
|
game.game_version_major = (uint32_t)bson_iterator_int(&gameIt);
|
||||||
|
}
|
||||||
|
else if (strcmp(key, "wzVerMinor") == 0)
|
||||||
|
{
|
||||||
|
game.game_version_minor = (uint32_t)bson_iterator_int(&gameIt);
|
||||||
|
}
|
||||||
|
else if (strcmp(key, "isPrivate") == 0)
|
||||||
|
{
|
||||||
|
game.isPrivate = bson_iterator_bool(&gameIt);
|
||||||
|
}
|
||||||
|
else if (strcmp(key, "modlist") == 0)
|
||||||
|
{
|
||||||
|
game.modlist = bson_iterator_string(&gameIt);
|
||||||
|
}
|
||||||
|
else if (strcmp(key, "mapname") == 0)
|
||||||
|
{
|
||||||
|
game.mapname = bson_iterator_string(&gameIt);
|
||||||
|
}
|
||||||
|
else if (strcmp(key, "hostplayer") == 0)
|
||||||
|
{
|
||||||
|
game.hostplayer = bson_iterator_string(&gameIt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
games.push_back(game);
|
||||||
|
gameCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
freeCallResult_();
|
||||||
|
return LOBBY_NO_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool LobbyClient::isConnected()
|
||||||
|
{
|
||||||
|
return (socket_ != NULL && !socketReadDisconnected(socket_));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LobbyClient::connect()
|
||||||
|
{
|
||||||
|
char version[sizeof("version") + sizeof(uint32_t)];
|
||||||
|
char* p_version = version;
|
||||||
|
|
||||||
|
if (isConnected())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// Build the initial version command.
|
||||||
|
strlcpy(p_version, "version", sizeof("version"));
|
||||||
|
p_version+= sizeof("version");
|
||||||
|
*(uint32_t*)p_version = htonl(LOBBY_VERSION);
|
||||||
|
|
||||||
|
callId_ = 0;
|
||||||
|
|
||||||
|
// Resolve the hostname
|
||||||
|
SocketAddress *const hosts = resolveHost(host_.c_str(), port_);
|
||||||
|
|
||||||
|
// Resolve failed?
|
||||||
|
if (hosts == NULL)
|
||||||
|
{
|
||||||
|
debug(LOG_ERROR, "Cannot resolve masterserver \"%s\": %s.", host_.c_str(), strSockError(getSockErr()));
|
||||||
|
return setError_(LOBBY_TRANSPORT_ERROR, _("Could not resolve masterserver name (%s)!"), host_.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
// try each address from resolveHost until we successfully connect.
|
||||||
|
socket_ = socketOpenAny(hosts, 1500);
|
||||||
|
deleteSocketAddress(hosts);
|
||||||
|
|
||||||
|
// No address succeeded or couldn't send version data
|
||||||
|
if (socket_ == NULL || writeAll(socket_, version, sizeof(version)) == SOCKET_ERROR)
|
||||||
|
{
|
||||||
|
debug(LOG_ERROR, "Cannot connect to masterserver \"%s:%d\": %s", host_.c_str(), port_, strSockError(getSockErr()));
|
||||||
|
return setError_(LOBBY_TRANSPORT_ERROR, _("Could not communicate with lobby server! Is TCP port %u open for outgoing traffic?"), port_);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LobbyClient::disconnect()
|
||||||
|
{
|
||||||
|
if (!isConnected())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
socketClose(socket_);
|
||||||
|
socket_ = NULL;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOBBY_ERROR LobbyClient::call_(const char* command, bson* args, bson* kwargs)
|
||||||
|
{
|
||||||
|
bson bson[1];
|
||||||
|
bson_buffer buffer[1];
|
||||||
|
int bsize;
|
||||||
|
char *call;
|
||||||
|
uint32_t resSize;
|
||||||
|
|
||||||
|
// Connect to the masterserver
|
||||||
|
if (connect() != true)
|
||||||
|
return lastError_.code;
|
||||||
|
|
||||||
|
callId_ += 1;
|
||||||
|
|
||||||
|
debug(LOG_NET, "Calling \"%s\" on the mastserver", command);
|
||||||
|
|
||||||
|
bson_buffer_init(buffer);
|
||||||
|
bson_append_start_array(buffer, "call");
|
||||||
|
{
|
||||||
|
// Command
|
||||||
|
bson_append_string(buffer, "0", command);
|
||||||
|
|
||||||
|
// Arguments
|
||||||
|
if (args == NULL)
|
||||||
|
{
|
||||||
|
// Add a empty "array"/"list"
|
||||||
|
bson_append_start_array(buffer, "1");
|
||||||
|
bson_append_finish_object(buffer);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
bson_append_bson(buffer, "1", args);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keyword Arguments
|
||||||
|
if (kwargs == NULL)
|
||||||
|
{
|
||||||
|
// Add a empty "object"/"dict"
|
||||||
|
bson_append_start_object(buffer, "2");
|
||||||
|
bson_append_finish_object(buffer);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Keyword arguments
|
||||||
|
bson_append_bson(buffer, "2", kwargs);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call id
|
||||||
|
bson_append_int(buffer, "3", callId_);
|
||||||
|
}
|
||||||
|
bson_append_finish_object(buffer);
|
||||||
|
bson_from_buffer(bson, buffer);
|
||||||
|
|
||||||
|
bsize = bson_size(bson);
|
||||||
|
|
||||||
|
call = (char*) malloc(sizeof(uint32_t) + bsize);
|
||||||
|
*(uint32_t*)call = htonl(bsize);
|
||||||
|
memcpy(call + sizeof(uint32_t), bson->data, bsize);
|
||||||
|
|
||||||
|
if (writeAll(socket_, call, sizeof(uint32_t) + bsize) == SOCKET_ERROR
|
||||||
|
|| readAll(socket_, &resSize, sizeof(resSize), 300) != sizeof(resSize))
|
||||||
|
{
|
||||||
|
connect(); // FIXME: Should check why readAll failed.
|
||||||
|
|
||||||
|
if (writeAll(socket_, call, sizeof(uint32_t) + bsize) == SOCKET_ERROR
|
||||||
|
|| readAll(socket_, &resSize, sizeof(resSize), 300) != sizeof(resSize))
|
||||||
|
{
|
||||||
|
debug(LOG_ERROR, "Failed sending command \"%s\" to the masterserver: %s.", command, strSockError(getSockErr()));
|
||||||
|
return setError_(LOBBY_TRANSPORT_ERROR, _("Failed to communicate with the masterserver."));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
free(call);
|
||||||
|
bson_destroy(bson);
|
||||||
|
|
||||||
|
resSize = ntohl(resSize);
|
||||||
|
|
||||||
|
callResult_.buffer = (char*) malloc(resSize);
|
||||||
|
if (readAll(socket_, callResult_.buffer, resSize, 900) != resSize)
|
||||||
|
{
|
||||||
|
debug(LOG_ERROR, "Failed reading the result for \"%s\" from the masterserver: %s.", command, strSockError(getSockErr()));
|
||||||
|
|
||||||
|
free(callResult_.buffer);
|
||||||
|
return setError_(LOBBY_TRANSPORT_ERROR, _("Failed to communicate with the masterserver."));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the first object (bson_array)
|
||||||
|
bson_iterator it;
|
||||||
|
bson_iterator_init(&it, callResult_.buffer);
|
||||||
|
bson_iterator_next(&it);
|
||||||
|
if (bson_iterator_type(&it) != bson_array)
|
||||||
|
{
|
||||||
|
debug(LOG_ERROR, "Received invalid bson data (no array first): %d.", bson_iterator_type(&it));
|
||||||
|
|
||||||
|
free(callResult_.buffer);
|
||||||
|
return setError_(LOBBY_INVALID_DATA, _("Failed to communicate with the masterserver."));
|
||||||
|
}
|
||||||
|
bson_iterator_init(&it, bson_iterator_value(&it));
|
||||||
|
bson_iterator_next(&it);
|
||||||
|
|
||||||
|
if (bson_iterator_type(&it) != bson_int)
|
||||||
|
{
|
||||||
|
debug(LOG_ERROR, "Received invalid bson data (no int first): %d.", bson_iterator_type(&it));
|
||||||
|
|
||||||
|
free(callResult_.buffer);
|
||||||
|
return setError_(LOBBY_INVALID_DATA, _("Failed to communicate with the masterserver."));
|
||||||
|
}
|
||||||
|
|
||||||
|
callResult_.code = (LOBBY_ERROR)bson_iterator_int(&it);
|
||||||
|
bson_iterator_next(&it);
|
||||||
|
|
||||||
|
bson_type type = bson_iterator_type(&it);
|
||||||
|
if (type == bson_string)
|
||||||
|
{
|
||||||
|
callResult_.result = bson_iterator_string(&it);
|
||||||
|
}
|
||||||
|
else if (type == bson_object || type == bson_array)
|
||||||
|
{
|
||||||
|
callResult_.result = bson_iterator_value(&it);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return LOBBY_NO_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOBBY_ERROR LobbyClient::setError_(const LOBBY_ERROR code, char * message, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
int count;
|
||||||
|
|
||||||
|
lastError_.code = code;
|
||||||
|
|
||||||
|
va_start(ap, message);
|
||||||
|
count = vasprintf(&lastError_.message, message, ap);
|
||||||
|
va_end(ap);
|
||||||
|
|
||||||
|
if (count == -1)
|
||||||
|
lastError_.message = NULL;
|
||||||
|
|
||||||
|
return code;
|
||||||
|
}
|
|
@ -0,0 +1,157 @@
|
||||||
|
/*
|
||||||
|
This file is part of Warzone 2100.
|
||||||
|
Copyright (C) 1999-2004 Eidos Interactive
|
||||||
|
Copyright (C) 2005-2011 Warzone 2100 Project
|
||||||
|
|
||||||
|
Warzone 2100 is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
Warzone 2100 is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with Warzone 2100; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _netlobby_h
|
||||||
|
#define _netlobby_h
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "lib/framework/frame.h"
|
||||||
|
#include "lib/framework/string_ext.h"
|
||||||
|
#include "netsocket.h"
|
||||||
|
#include "bson/bson.h"
|
||||||
|
|
||||||
|
#define LOBBY_VERSION 4
|
||||||
|
|
||||||
|
enum LOBBY_ERROR
|
||||||
|
{
|
||||||
|
LOBBY_NO_ERROR = 0,
|
||||||
|
|
||||||
|
// Copied from XMLRPC for socketrpc
|
||||||
|
LOBBY_PARSE_ERROR = -32700,
|
||||||
|
LOBBY_SERVER_ERROR = -32600,
|
||||||
|
LOBBY_APPLICATION_ERROR = -32500,
|
||||||
|
LOBBY_TRANSPORT_ERROR = -32300,
|
||||||
|
|
||||||
|
// Specific errors.
|
||||||
|
LOBBY_UNSUPPORTED_ENCODING = -32701,
|
||||||
|
LOBBY_METHOD_NOT_FOUND = -32601,
|
||||||
|
|
||||||
|
// Custom error codes.
|
||||||
|
LOBBY_INVALID_DATA = -500,
|
||||||
|
LOBBY_WRONG_LOGIN = -404,
|
||||||
|
LOBBY_NOT_ACCEPTABLE = -403,
|
||||||
|
LOBBY_NO_GAME = -402,
|
||||||
|
};
|
||||||
|
|
||||||
|
// FIXME: Not sure if std::string is a good idear here,
|
||||||
|
// as multitint is passing them direct to iV_DrawText.
|
||||||
|
struct LOBBY_GAME
|
||||||
|
{
|
||||||
|
uint32_t port; ///< Port hosting on.
|
||||||
|
std::string host; ///< IPv4, IPv6 or DNS Name of the host.
|
||||||
|
std::string description; ///< Game Description.
|
||||||
|
uint32_t currentPlayers; ///< Number of joined players.
|
||||||
|
uint32_t maxPlayers; ///< Maximum number of players.
|
||||||
|
std::string versionstring; ///< Version string.
|
||||||
|
uint32_t game_version_major; ///< Minor NETCODE version.
|
||||||
|
uint32_t game_version_minor; ///< Major NETCODE version.
|
||||||
|
bool isPrivate; ///< Password protected?
|
||||||
|
std::string modlist; ///< display string for mods.
|
||||||
|
std::string mapname; ///< name of map hosted.
|
||||||
|
std::string hostplayer; ///< hosts playername.
|
||||||
|
};
|
||||||
|
|
||||||
|
struct lobbyError
|
||||||
|
{
|
||||||
|
LOBBY_ERROR code;
|
||||||
|
char* message;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct lobbyCallResult
|
||||||
|
{
|
||||||
|
LOBBY_ERROR code;
|
||||||
|
char* buffer;
|
||||||
|
const char* result;
|
||||||
|
};
|
||||||
|
|
||||||
|
class LobbyClient {
|
||||||
|
public:
|
||||||
|
std::vector<LOBBY_GAME> games;
|
||||||
|
|
||||||
|
LobbyClient();
|
||||||
|
void stop();
|
||||||
|
|
||||||
|
bool connect();
|
||||||
|
bool disconnect();
|
||||||
|
bool isConnected();
|
||||||
|
|
||||||
|
LOBBY_ERROR addGame(char** result, uint32_t port, uint32_t maxPlayers,
|
||||||
|
const char* description, const char* versionstring,
|
||||||
|
uint32_t gameversion__major, uint32_t gameversion__minor,
|
||||||
|
bool isPassworded, const char* modlist,
|
||||||
|
const char* mapname, const char* hostplayer);
|
||||||
|
|
||||||
|
LOBBY_ERROR delGame();
|
||||||
|
LOBBY_ERROR addPlayer(unsigned int index, const char* name, const char* ipaddress);
|
||||||
|
LOBBY_ERROR delPlayer(unsigned int index);
|
||||||
|
LOBBY_ERROR updatePlayer(unsigned int index, const char* name);
|
||||||
|
LOBBY_ERROR listGames(int maxGames);
|
||||||
|
|
||||||
|
LobbyClient& setHost(const std::string& host) { host_ = host; return *this; }
|
||||||
|
std::string getHost() const { return host_; }
|
||||||
|
|
||||||
|
LobbyClient& setPort(const uint32_t& port) { port_ = port; return *this; }
|
||||||
|
uint32_t getPort() { return port_; }
|
||||||
|
|
||||||
|
lobbyError* getError() { return &lastError_; }
|
||||||
|
|
||||||
|
void freeError()
|
||||||
|
{
|
||||||
|
if (lastError_.code == LOBBY_NO_ERROR)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
lastError_.code = LOBBY_NO_ERROR;
|
||||||
|
free(lastError_.message);
|
||||||
|
lastError_.message = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
int32_t gameId_;
|
||||||
|
|
||||||
|
uint32_t callId_;
|
||||||
|
|
||||||
|
std::string host_;
|
||||||
|
uint32_t port_;
|
||||||
|
|
||||||
|
Socket* socket_;
|
||||||
|
|
||||||
|
lobbyError lastError_;
|
||||||
|
lobbyCallResult callResult_;
|
||||||
|
|
||||||
|
LOBBY_ERROR call_(const char* command, bson* args, bson* kwargs);
|
||||||
|
|
||||||
|
void freeCallResult_()
|
||||||
|
{
|
||||||
|
callResult_.code = LOBBY_NO_ERROR;
|
||||||
|
callResult_.result = NULL;
|
||||||
|
free(callResult_.buffer);
|
||||||
|
callResult_.buffer = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOBBY_ERROR setError_(const LOBBY_ERROR code, char * message, ...);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // #ifndef _netlobby_h
|
||||||
|
|
||||||
|
extern LobbyClient lobbyclient;
|
|
@ -56,8 +56,7 @@
|
||||||
#endif //WZ_OS_LINUX
|
#endif //WZ_OS_LINUX
|
||||||
|
|
||||||
// WARNING !!! This is initialised via configuration.c !!!
|
// WARNING !!! This is initialised via configuration.c !!!
|
||||||
char masterserver_name[255] = {'\0'};
|
static unsigned int gameserver_port = 0;
|
||||||
static unsigned int masterserver_port = 0, gameserver_port = 0;
|
|
||||||
|
|
||||||
#define NET_TIMEOUT_DELAY 2500 // we wait this amount of time for socket activity
|
#define NET_TIMEOUT_DELAY 2500 // we wait this amount of time for socket activity
|
||||||
#define NET_READ_TIMEOUT 0
|
#define NET_READ_TIMEOUT 0
|
||||||
|
@ -75,7 +74,6 @@ static unsigned int masterserver_port = 0, gameserver_port = 0;
|
||||||
// Function prototypes
|
// Function prototypes
|
||||||
static void NETplayerLeaving(UDWORD player); // Cleanup sockets on player leaving (nicely)
|
static void NETplayerLeaving(UDWORD player); // Cleanup sockets on player leaving (nicely)
|
||||||
static void NETplayerDropped(UDWORD player); // Broadcast NET_PLAYER_DROPPED & cleanup
|
static void NETplayerDropped(UDWORD player); // Broadcast NET_PLAYER_DROPPED & cleanup
|
||||||
static void NETregisterServer(int state);
|
|
||||||
static void NETallowJoining(void);
|
static void NETallowJoining(void);
|
||||||
static void recvDebugSync(NETQUEUE queue);
|
static void recvDebugSync(NETQUEUE queue);
|
||||||
static bool onBanList(const char *ip);
|
static bool onBanList(const char *ip);
|
||||||
|
@ -109,8 +107,6 @@ struct NET_PLAYER_DATA
|
||||||
NETPLAY NetPlay;
|
NETPLAY NetPlay;
|
||||||
PLAYER_IP *IPlist = NULL;
|
PLAYER_IP *IPlist = NULL;
|
||||||
static bool allow_joining = false;
|
static bool allow_joining = false;
|
||||||
static bool server_not_there = false;
|
|
||||||
static GAMESTRUCT gamestruct;
|
|
||||||
|
|
||||||
// update flags
|
// update flags
|
||||||
bool netPlayersUpdated;
|
bool netPlayersUpdated;
|
||||||
|
@ -119,10 +115,9 @@ int mapDownloadProgress;
|
||||||
/**
|
/**
|
||||||
* Socket used for these purposes:
|
* Socket used for these purposes:
|
||||||
* * Host a game, be a server.
|
* * Host a game, be a server.
|
||||||
* * Connect to the lobby server.
|
|
||||||
* * Join a server for a game.
|
* * Join a server for a game.
|
||||||
*/
|
*/
|
||||||
static Socket* tcp_socket = NULL; //socket used to talk to lobbyserver/ host machine
|
static Socket* tcp_socket = NULL; //socket used to talk to host machine
|
||||||
|
|
||||||
static Socket *bsocket = NULL; //buffered socket (holds tcp_socket) (clients only?)
|
static Socket *bsocket = NULL; //buffered socket (holds tcp_socket) (clients only?)
|
||||||
static Socket *connected_bsocket[MAX_CONNECTED_PLAYERS] = { NULL };
|
static Socket *connected_bsocket[MAX_CONNECTED_PLAYERS] = { NULL };
|
||||||
|
@ -169,7 +164,6 @@ bool NETisCorrectVersion(uint32_t game_version_major, uint32_t game_version_mino
|
||||||
void NETGameLocked( bool flag)
|
void NETGameLocked( bool flag)
|
||||||
{
|
{
|
||||||
NetPlay.GamePassworded = flag;
|
NetPlay.GamePassworded = flag;
|
||||||
gamestruct.privateGame = flag;
|
|
||||||
NETlogEntry("Password is", SYNC_FLAG, NetPlay.GamePassworded);
|
NETlogEntry("Password is", SYNC_FLAG, NetPlay.GamePassworded);
|
||||||
debug(LOG_NET, "Passworded game is %s", NetPlay.GamePassworded ? "TRUE" : "FALSE" );
|
debug(LOG_NET, "Passworded game is %s", NetPlay.GamePassworded ? "TRUE" : "FALSE" );
|
||||||
}
|
}
|
||||||
|
@ -390,12 +384,12 @@ static void NET_DestroyPlayer(unsigned int index)
|
||||||
{
|
{
|
||||||
NetPlay.players[index].allocated = false;
|
NetPlay.players[index].allocated = false;
|
||||||
NetPlay.playercount--;
|
NetPlay.playercount--;
|
||||||
gamestruct.desc.dwCurrentPlayers = NetPlay.playercount;
|
|
||||||
|
// Inform the masterserver.
|
||||||
if (allow_joining && NetPlay.isHost)
|
if (allow_joining && NetPlay.isHost)
|
||||||
{
|
{
|
||||||
// Update player count in the lobby by disconnecting
|
lobbyclient.delPlayer(index);
|
||||||
// and reconnecting
|
lobbyclient.freeError();
|
||||||
NETregisterServer(2);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
NET_InitPlayer(index, false); // reinitialize
|
NET_InitPlayer(index, false); // reinitialize
|
||||||
|
@ -498,6 +492,13 @@ bool NETchangePlayerName(UDWORD index, char *newName)
|
||||||
sstrcpy(NetPlay.players[0].name, newName);
|
sstrcpy(NetPlay.players[0].name, newName);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (NetPlay.isHost && allow_joining)
|
||||||
|
{
|
||||||
|
lobbyclient.updatePlayer(index, newName);
|
||||||
|
lobbyclient.freeError();
|
||||||
|
}
|
||||||
|
|
||||||
debug(LOG_NET, "Requesting a change of player name for pid=%u to %s", index, newName);
|
debug(LOG_NET, "Requesting a change of player name for pid=%u to %s", index, newName);
|
||||||
NETlogEntry("Player wants a name change.", SYNC_FLAG, index);
|
NETlogEntry("Player wants a name change.", SYNC_FLAG, index);
|
||||||
sstrcpy(NetPlay.players[index].name, newName);
|
sstrcpy(NetPlay.players[index].name, newName);
|
||||||
|
@ -596,285 +597,6 @@ bool NETsetGameFlags(UDWORD flag, SDWORD value)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @note \c game is being sent to the master server (if hosting)
|
|
||||||
* The implementation of NETsendGAMESTRUCT <em>must</em> guarantee to
|
|
||||||
* pack it in network byte order (big-endian).
|
|
||||||
*
|
|
||||||
* @return true on success, false when a socket error has occurred
|
|
||||||
*
|
|
||||||
* @see GAMESTRUCT,NETrecvGAMESTRUCT
|
|
||||||
*/
|
|
||||||
static bool NETsendGAMESTRUCT(Socket* sock, const GAMESTRUCT* ourgamestruct)
|
|
||||||
{
|
|
||||||
// A buffer that's guaranteed to have the correct size (i.e. it
|
|
||||||
// circumvents struct padding, which could pose a problem). Initialise
|
|
||||||
// to zero so that we can be sure we're not sending any (undefined)
|
|
||||||
// memory content across the network.
|
|
||||||
char buf[sizeof(ourgamestruct->GAMESTRUCT_VERSION) + sizeof(ourgamestruct->name) + sizeof(ourgamestruct->desc.host) + (sizeof(int32_t) * 8) +
|
|
||||||
sizeof(ourgamestruct->secondaryHosts) + sizeof(ourgamestruct->extra) + sizeof(ourgamestruct->mapname) + sizeof(ourgamestruct->hostname) + sizeof(ourgamestruct->versionstring) +
|
|
||||||
sizeof(ourgamestruct->modlist) + (sizeof(uint32_t) * 9) ] = { 0 };
|
|
||||||
char *buffer = buf;
|
|
||||||
unsigned int i;
|
|
||||||
ssize_t result;
|
|
||||||
|
|
||||||
// Now dump the data into the buffer
|
|
||||||
// Copy 32bit large big endian numbers
|
|
||||||
*(uint32_t*)buffer = htonl(ourgamestruct->GAMESTRUCT_VERSION);
|
|
||||||
buffer += sizeof(uint32_t);
|
|
||||||
|
|
||||||
// Copy a string
|
|
||||||
strlcpy(buffer, ourgamestruct->name, sizeof(ourgamestruct->name));
|
|
||||||
buffer += sizeof(ourgamestruct->name);
|
|
||||||
|
|
||||||
// Copy 32bit large big endian numbers
|
|
||||||
*(int32_t*)buffer = htonl(ourgamestruct->desc.dwSize);
|
|
||||||
buffer += sizeof(int32_t);
|
|
||||||
*(int32_t*)buffer = htonl(ourgamestruct->desc.dwFlags);
|
|
||||||
buffer += sizeof(int32_t);
|
|
||||||
|
|
||||||
// Copy yet another string
|
|
||||||
strlcpy(buffer, ourgamestruct->desc.host, sizeof(ourgamestruct->desc.host));
|
|
||||||
buffer += sizeof(ourgamestruct->desc.host);
|
|
||||||
|
|
||||||
// Copy 32bit large big endian numbers
|
|
||||||
*(int32_t*)buffer = htonl(ourgamestruct->desc.dwMaxPlayers);
|
|
||||||
buffer += sizeof(int32_t);
|
|
||||||
*(int32_t*)buffer = htonl(ourgamestruct->desc.dwCurrentPlayers);
|
|
||||||
buffer += sizeof(int32_t);
|
|
||||||
for (i = 0; i < ARRAY_SIZE(ourgamestruct->desc.dwUserFlags); ++i)
|
|
||||||
{
|
|
||||||
*(int32_t*)buffer = htonl(ourgamestruct->desc.dwUserFlags[i]);
|
|
||||||
buffer += sizeof(int32_t);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy a string
|
|
||||||
for (i = 0; i <ARRAY_SIZE(ourgamestruct->secondaryHosts); ++i)
|
|
||||||
{
|
|
||||||
strlcpy(buffer, ourgamestruct->secondaryHosts[i], sizeof(ourgamestruct->secondaryHosts[i]));
|
|
||||||
buffer += sizeof(ourgamestruct->secondaryHosts[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy a string
|
|
||||||
strlcpy(buffer, ourgamestruct->extra, sizeof(ourgamestruct->extra));
|
|
||||||
buffer += sizeof(ourgamestruct->extra);
|
|
||||||
|
|
||||||
// Copy a string
|
|
||||||
strlcpy(buffer, ourgamestruct->mapname, sizeof(ourgamestruct->mapname));
|
|
||||||
buffer += sizeof(ourgamestruct->mapname);
|
|
||||||
|
|
||||||
// Copy a string
|
|
||||||
strlcpy(buffer, ourgamestruct->hostname, sizeof(ourgamestruct->hostname));
|
|
||||||
buffer += sizeof(ourgamestruct->hostname);
|
|
||||||
|
|
||||||
// Copy a string
|
|
||||||
strlcpy(buffer, ourgamestruct->versionstring, sizeof(ourgamestruct->versionstring));
|
|
||||||
buffer += sizeof(ourgamestruct->versionstring);
|
|
||||||
|
|
||||||
// Copy a string
|
|
||||||
strlcpy(buffer, ourgamestruct->modlist, sizeof(ourgamestruct->modlist));
|
|
||||||
buffer += sizeof(ourgamestruct->modlist);
|
|
||||||
|
|
||||||
// Copy 32bit large big endian numbers
|
|
||||||
*(uint32_t*)buffer = htonl(ourgamestruct->game_version_major);
|
|
||||||
buffer += sizeof(uint32_t);
|
|
||||||
|
|
||||||
// Copy 32bit large big endian numbers
|
|
||||||
*(uint32_t*)buffer = htonl(ourgamestruct->game_version_minor);
|
|
||||||
buffer += sizeof(uint32_t);
|
|
||||||
|
|
||||||
// Copy 32bit large big endian numbers
|
|
||||||
*(uint32_t*)buffer = htonl(ourgamestruct->privateGame);
|
|
||||||
buffer += sizeof(uint32_t);
|
|
||||||
|
|
||||||
// Copy 32bit large big endian numbers
|
|
||||||
*(uint32_t*)buffer = htonl(ourgamestruct->pureGame);
|
|
||||||
buffer += sizeof(uint32_t);
|
|
||||||
|
|
||||||
// Copy 32bit large big endian numbers
|
|
||||||
*(uint32_t*)buffer = htonl(ourgamestruct->Mods);
|
|
||||||
buffer += sizeof(uint32_t);
|
|
||||||
|
|
||||||
// Copy 32bit large big endian numbers
|
|
||||||
*(uint32_t*)buffer = htonl(ourgamestruct->gameId);
|
|
||||||
buffer += sizeof(uint32_t);
|
|
||||||
|
|
||||||
// Copy 32bit large big endian numbers
|
|
||||||
*(uint32_t*)buffer = htonl(ourgamestruct->future2);
|
|
||||||
buffer += sizeof(uint32_t);
|
|
||||||
|
|
||||||
// Copy 32bit large big endian numbers
|
|
||||||
*(uint32_t*)buffer = htonl(ourgamestruct->future3);
|
|
||||||
buffer += sizeof(uint32_t);
|
|
||||||
|
|
||||||
// Copy 32bit large big endian numbers
|
|
||||||
*(uint32_t*)buffer = htonl(ourgamestruct->future4);
|
|
||||||
buffer += sizeof(uint32_t);
|
|
||||||
|
|
||||||
debug(LOG_NET, "sending GAMESTRUCT, size: %u", (unsigned int)sizeof(buf));
|
|
||||||
|
|
||||||
// Send over the GAMESTRUCT
|
|
||||||
result = writeAll(sock, buf, sizeof(buf));
|
|
||||||
if (result == SOCKET_ERROR)
|
|
||||||
{
|
|
||||||
const int err = getSockErr();
|
|
||||||
|
|
||||||
// If packet could not be sent, we should inform user of the error.
|
|
||||||
debug(LOG_ERROR, "Failed to send GAMESTRUCT. Reason: %s", strSockError(getSockErr()));
|
|
||||||
debug(LOG_ERROR, "Please make sure TCP ports %u & %u are open!", masterserver_port, gameserver_port);
|
|
||||||
|
|
||||||
setSockErr(err);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @note \c game is being retrieved from the master server (if browsing the
|
|
||||||
* lobby). The implementation of NETrecvGAMESTRUCT should assume the data
|
|
||||||
* to be packed in network byte order (big-endian).
|
|
||||||
*
|
|
||||||
* @see GAMESTRUCT,NETsendGAMESTRUCT
|
|
||||||
*/
|
|
||||||
static bool NETrecvGAMESTRUCT(GAMESTRUCT* ourgamestruct)
|
|
||||||
{
|
|
||||||
// A buffer that's guaranteed to have the correct size (i.e. it
|
|
||||||
// circumvents struct padding, which could pose a problem).
|
|
||||||
char buf[sizeof(ourgamestruct->GAMESTRUCT_VERSION) + sizeof(ourgamestruct->name) + sizeof(ourgamestruct->desc.host) + (sizeof(int32_t) * 8) +
|
|
||||||
sizeof(ourgamestruct->secondaryHosts) + sizeof(ourgamestruct->extra) + sizeof(ourgamestruct->mapname) + sizeof(ourgamestruct->hostname) + sizeof(ourgamestruct->versionstring) +
|
|
||||||
sizeof(ourgamestruct->modlist) + (sizeof(uint32_t) * 9) ] = { 0 };
|
|
||||||
char* buffer = buf;
|
|
||||||
unsigned int i;
|
|
||||||
ssize_t result = 0;
|
|
||||||
|
|
||||||
// Read a GAMESTRUCT from the connection
|
|
||||||
if (tcp_socket == NULL
|
|
||||||
|| socket_set == NULL
|
|
||||||
|| checkSockets(socket_set, NET_TIMEOUT_DELAY) <= 0
|
|
||||||
|| !socketReadReady(tcp_socket)
|
|
||||||
|| (result = readNoInt(tcp_socket, buf, sizeof(buf))) != sizeof(buf))
|
|
||||||
{
|
|
||||||
unsigned int time = wzGetTicks();
|
|
||||||
if (result == SOCKET_ERROR)
|
|
||||||
{
|
|
||||||
debug(LOG_ERROR, "Server socket (%p) ecountered error: %s", tcp_socket, strSockError(getSockErr()));
|
|
||||||
SocketSet_DelSocket(socket_set, tcp_socket); // mark it invalid
|
|
||||||
socketClose(tcp_socket);
|
|
||||||
tcp_socket = NULL;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
i = result;
|
|
||||||
while (i < sizeof(buf) && wzGetTicks() < time + 2500)
|
|
||||||
{
|
|
||||||
result = readNoInt(tcp_socket, buf+i, sizeof(buf)-i);
|
|
||||||
if (result == SOCKET_ERROR
|
|
||||||
|| (result == 0 && socketReadDisconnected(tcp_socket)))
|
|
||||||
{
|
|
||||||
debug(LOG_ERROR, "Server socket (%p) ecountered error: %s", tcp_socket, strSockError(getSockErr()));
|
|
||||||
debug(LOG_ERROR, "GAMESTRUCT recv failed; received %u bytes out of %d", i, (int)sizeof(buf));
|
|
||||||
SocketSet_DelSocket(socket_set, tcp_socket); // mark it invalid
|
|
||||||
socketClose(tcp_socket);
|
|
||||||
tcp_socket = NULL;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
i += result;
|
|
||||||
}
|
|
||||||
if (i != sizeof(buf))
|
|
||||||
{
|
|
||||||
debug(LOG_ERROR, "GAMESTRUCT recv size mismatch; received %u bytes; expecting %d", i, (int)sizeof(buf));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now dump the data into the game struct
|
|
||||||
// Copy 32bit large big endian numbers
|
|
||||||
ourgamestruct->GAMESTRUCT_VERSION = ntohl(*(uint32_t*)buffer);
|
|
||||||
buffer += sizeof(uint32_t);
|
|
||||||
// Copy a string
|
|
||||||
sstrcpy(ourgamestruct->name, buffer);
|
|
||||||
buffer += sizeof(ourgamestruct->name);
|
|
||||||
|
|
||||||
// Copy 32bit large big endian numbers
|
|
||||||
ourgamestruct->desc.dwSize = ntohl(*(int32_t*)buffer);
|
|
||||||
buffer += sizeof(int32_t);
|
|
||||||
ourgamestruct->desc.dwFlags = ntohl(*(int32_t*)buffer);
|
|
||||||
buffer += sizeof(int32_t);
|
|
||||||
|
|
||||||
// Copy yet another string
|
|
||||||
sstrcpy(ourgamestruct->desc.host, buffer);
|
|
||||||
buffer += sizeof(ourgamestruct->desc.host);
|
|
||||||
|
|
||||||
// Copy 32bit large big endian numbers
|
|
||||||
ourgamestruct->desc.dwMaxPlayers = ntohl(*(int32_t*)buffer);
|
|
||||||
buffer += sizeof(int32_t);
|
|
||||||
ourgamestruct->desc.dwCurrentPlayers = ntohl(*(int32_t*)buffer);
|
|
||||||
buffer += sizeof(int32_t);
|
|
||||||
for (i = 0; i < ARRAY_SIZE(ourgamestruct->desc.dwUserFlags); ++i)
|
|
||||||
{
|
|
||||||
ourgamestruct->desc.dwUserFlags[i] = ntohl(*(int32_t*)buffer);
|
|
||||||
buffer += sizeof(int32_t);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy a string
|
|
||||||
for (i = 0; i < ARRAY_SIZE(ourgamestruct->secondaryHosts); ++i)
|
|
||||||
{
|
|
||||||
sstrcpy(ourgamestruct->secondaryHosts[i], buffer);
|
|
||||||
buffer += sizeof(ourgamestruct->secondaryHosts[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy a string
|
|
||||||
sstrcpy(ourgamestruct->extra, buffer);
|
|
||||||
buffer += sizeof(ourgamestruct->extra);
|
|
||||||
|
|
||||||
// Copy a string
|
|
||||||
sstrcpy(ourgamestruct->mapname, buffer);
|
|
||||||
buffer += sizeof(ourgamestruct->mapname);
|
|
||||||
|
|
||||||
// Copy a string
|
|
||||||
sstrcpy(ourgamestruct->hostname, buffer);
|
|
||||||
buffer += sizeof(ourgamestruct->hostname);
|
|
||||||
|
|
||||||
// Copy a string
|
|
||||||
sstrcpy(ourgamestruct->versionstring, buffer);
|
|
||||||
buffer += sizeof(ourgamestruct->versionstring);
|
|
||||||
|
|
||||||
// Copy a string
|
|
||||||
sstrcpy(ourgamestruct->modlist, buffer);
|
|
||||||
buffer += sizeof(ourgamestruct->modlist);
|
|
||||||
|
|
||||||
// Copy 32bit large big endian numbers
|
|
||||||
ourgamestruct->game_version_major = ntohl(*(uint32_t*)buffer);
|
|
||||||
buffer += sizeof(uint32_t);
|
|
||||||
ourgamestruct->game_version_minor = ntohl(*(uint32_t*)buffer);
|
|
||||||
buffer += sizeof(uint32_t);
|
|
||||||
ourgamestruct->privateGame = ntohl(*(uint32_t*)buffer);
|
|
||||||
buffer += sizeof(uint32_t);
|
|
||||||
ourgamestruct->pureGame = ntohl(*(uint32_t*)buffer);
|
|
||||||
buffer += sizeof(uint32_t);
|
|
||||||
ourgamestruct->Mods = ntohl(*(uint32_t*)buffer);
|
|
||||||
buffer += sizeof(uint32_t);
|
|
||||||
ourgamestruct->gameId = ntohl(*(uint32_t*)buffer);
|
|
||||||
buffer += sizeof(uint32_t);
|
|
||||||
ourgamestruct->future2 = ntohl(*(uint32_t*)buffer);
|
|
||||||
buffer += sizeof(uint32_t);
|
|
||||||
ourgamestruct->future3 = ntohl(*(uint32_t*)buffer);
|
|
||||||
buffer += sizeof(uint32_t);
|
|
||||||
ourgamestruct->future4 = ntohl(*(uint32_t*)buffer);
|
|
||||||
buffer += sizeof(uint32_t);
|
|
||||||
|
|
||||||
// cat the modstring (if there is one) to the version string to display it for the end-user
|
|
||||||
if (ourgamestruct->modlist[0] != '\0')
|
|
||||||
{
|
|
||||||
ssprintf(ourgamestruct->versionstring, "%s, Mods:%s", ourgamestruct->versionstring, ourgamestruct->modlist);
|
|
||||||
}
|
|
||||||
debug(LOG_NET, "received GAMESTRUCT");
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static int upnp_init(void *asdf)
|
static int upnp_init(void *asdf)
|
||||||
{
|
{
|
||||||
struct UPNPDev *devlist;
|
struct UPNPDev *devlist;
|
||||||
|
@ -1011,19 +733,15 @@ int NETinit(bool bFirstCall)
|
||||||
{
|
{
|
||||||
debug(LOG_NET, "NETPLAY: Init called, MORNIN'");
|
debug(LOG_NET, "NETPLAY: Init called, MORNIN'");
|
||||||
|
|
||||||
memset(&NetPlay.games, 0, sizeof(NetPlay.games));
|
|
||||||
// NOTE NetPlay.isUPNP is already set in configuration.c!
|
// NOTE NetPlay.isUPNP is already set in configuration.c!
|
||||||
NetPlay.bComms = true;
|
NetPlay.bComms = true;
|
||||||
NetPlay.GamePassworded = false;
|
NetPlay.GamePassworded = false;
|
||||||
NetPlay.ShowedMOTD = false;
|
|
||||||
NetPlay.isHostAlive = false;
|
NetPlay.isHostAlive = false;
|
||||||
NetPlay.gamePassword[0] = '\0';
|
NetPlay.gamePassword[0] = '\0';
|
||||||
NetPlay.MOTD = strdup("");
|
|
||||||
sstrcpy(NetPlay.gamePassword,_("Enter password here"));
|
sstrcpy(NetPlay.gamePassword,_("Enter password here"));
|
||||||
NETstartLogging();
|
NETstartLogging();
|
||||||
}
|
}
|
||||||
|
|
||||||
NetPlay.ShowedMOTD = false;
|
|
||||||
NetPlay.GamePassworded = false;
|
NetPlay.GamePassworded = false;
|
||||||
memset(&sync_counter, 0x0, sizeof(sync_counter)); //clear counters
|
memset(&sync_counter, 0x0, sizeof(sync_counter)); //clear counters
|
||||||
|
|
||||||
|
@ -1037,6 +755,10 @@ int NETshutdown(void)
|
||||||
{
|
{
|
||||||
debug( LOG_NET, "NETshutdown" );
|
debug( LOG_NET, "NETshutdown" );
|
||||||
NETlogEntry("NETshutdown", SYNC_FLAG, selectedPlayer);
|
NETlogEntry("NETshutdown", SYNC_FLAG, selectedPlayer);
|
||||||
|
|
||||||
|
// Stop the lobbyclient.
|
||||||
|
lobbyclient.stop();
|
||||||
|
|
||||||
NETstopLogging();
|
NETstopLogging();
|
||||||
if (IPlist)
|
if (IPlist)
|
||||||
free(IPlist);
|
free(IPlist);
|
||||||
|
@ -1058,13 +780,11 @@ int NETclose(void)
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
|
|
||||||
// reset flag
|
// reset flag
|
||||||
NetPlay.ShowedMOTD = false;
|
|
||||||
NEThaltJoining();
|
NEThaltJoining();
|
||||||
|
|
||||||
debug(LOG_NET, "Terminating sockets.");
|
debug(LOG_NET, "Terminating sockets.");
|
||||||
|
|
||||||
NetPlay.isHost = false;
|
NetPlay.isHost = false;
|
||||||
server_not_there = false;
|
|
||||||
allow_joining = false;
|
allow_joining = false;
|
||||||
|
|
||||||
if(bsocket)
|
if(bsocket)
|
||||||
|
@ -1925,183 +1645,6 @@ UBYTE NETrecvFile(NETQUEUE queue)
|
||||||
return ((currPos + bytesToRead) * 100) / fileSize;
|
return ((currPos + bytesToRead) * 100) / fileSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
static ssize_t readLobbyResponse(Socket* sock, unsigned int timeout)
|
|
||||||
{
|
|
||||||
uint32_t lobbyStatusCode;
|
|
||||||
uint32_t MOTDLength;
|
|
||||||
uint32_t buffer[2];
|
|
||||||
ssize_t result, received = 0;
|
|
||||||
|
|
||||||
// Get status and message length
|
|
||||||
result = readAll(sock, &buffer, sizeof(buffer), timeout);
|
|
||||||
if (result != sizeof(buffer))
|
|
||||||
goto error;
|
|
||||||
received += result;
|
|
||||||
lobbyStatusCode = ntohl(buffer[0]);
|
|
||||||
MOTDLength = ntohl(buffer[1]);
|
|
||||||
|
|
||||||
// Get status message
|
|
||||||
free(NetPlay.MOTD);
|
|
||||||
NetPlay.MOTD = (char *)malloc(MOTDLength + 1);
|
|
||||||
result = readAll(sock, NetPlay.MOTD, MOTDLength, timeout);
|
|
||||||
if (result != MOTDLength)
|
|
||||||
goto error;
|
|
||||||
received += result;
|
|
||||||
// NUL terminate string
|
|
||||||
NetPlay.MOTD[MOTDLength] = '\0';
|
|
||||||
|
|
||||||
if (lobbyStatusCode / 100 != 2) // Check whether status code is 2xx (success)
|
|
||||||
{
|
|
||||||
debug(LOG_ERROR, "Lobby error (%u): %s", (unsigned int)lobbyStatusCode, NetPlay.MOTD);
|
|
||||||
return received;
|
|
||||||
}
|
|
||||||
|
|
||||||
debug(LOG_NET, "Lobby success (%u): %s", (unsigned int)lobbyStatusCode, NetPlay.MOTD);
|
|
||||||
return received;
|
|
||||||
|
|
||||||
error:
|
|
||||||
if (result == SOCKET_ERROR)
|
|
||||||
{
|
|
||||||
free(NetPlay.MOTD);
|
|
||||||
if (asprintf(&NetPlay.MOTD, "Error while connecting to the lobby server: %s\nMake sure port %d can receive incoming connections.", strSockError(getSockErr()), gameserver_port) == -1)
|
|
||||||
NetPlay.MOTD = NULL;
|
|
||||||
debug(LOG_ERROR, "%s", NetPlay.MOTD);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
free(NetPlay.MOTD);
|
|
||||||
if (asprintf(&NetPlay.MOTD, "Disconnected from lobby server. Failed to register game.") == -1)
|
|
||||||
NetPlay.MOTD = NULL;
|
|
||||||
debug(LOG_ERROR, "%s", NetPlay.MOTD);
|
|
||||||
}
|
|
||||||
|
|
||||||
return SOCKET_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void NETregisterServer(int state)
|
|
||||||
{
|
|
||||||
static Socket* rs_socket = NULL;
|
|
||||||
static int registered = 0;
|
|
||||||
|
|
||||||
if (server_not_there)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state != registered)
|
|
||||||
{
|
|
||||||
switch(state)
|
|
||||||
{
|
|
||||||
// Update player counts
|
|
||||||
case 2:
|
|
||||||
{
|
|
||||||
if (!NETsendGAMESTRUCT(rs_socket, &gamestruct))
|
|
||||||
{
|
|
||||||
socketClose(rs_socket);
|
|
||||||
rs_socket = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
// Register a game with the lobby
|
|
||||||
case 1:
|
|
||||||
{
|
|
||||||
uint32_t gameId = 0;
|
|
||||||
SocketAddress *const hosts = resolveHost(masterserver_name, masterserver_port);
|
|
||||||
|
|
||||||
if (hosts == NULL)
|
|
||||||
{
|
|
||||||
debug(LOG_ERROR, "Cannot resolve masterserver \"%s\": %s", masterserver_name, strSockError(getSockErr()));
|
|
||||||
free(NetPlay.MOTD);
|
|
||||||
if (asprintf(&NetPlay.MOTD, _("Could not resolve masterserver name (%s)!"), masterserver_name) == -1)
|
|
||||||
NetPlay.MOTD = NULL;
|
|
||||||
server_not_there = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close an existing socket.
|
|
||||||
if (rs_socket != NULL)
|
|
||||||
{
|
|
||||||
socketClose(rs_socket);
|
|
||||||
rs_socket = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// try each address from resolveHost until we successfully connect.
|
|
||||||
rs_socket = socketOpenAny(hosts, 1500);
|
|
||||||
deleteSocketAddress(hosts);
|
|
||||||
|
|
||||||
// No address succeeded.
|
|
||||||
if (rs_socket == NULL)
|
|
||||||
{
|
|
||||||
debug(LOG_ERROR, "Cannot connect to masterserver \"%s:%d\": %s", masterserver_name, masterserver_port, strSockError(getSockErr()));
|
|
||||||
free(NetPlay.MOTD);
|
|
||||||
if (asprintf(&NetPlay.MOTD, _("Could not communicate with lobby server! Is TCP port %u open for outgoing traffic?"), masterserver_port) == -1)
|
|
||||||
NetPlay.MOTD = NULL;
|
|
||||||
server_not_there = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get a game ID
|
|
||||||
if (writeAll(rs_socket, "gaId", sizeof("gaId")) == SOCKET_ERROR
|
|
||||||
|| readAll(rs_socket, &gameId, sizeof(gameId), 10000) != sizeof(gameId))
|
|
||||||
{
|
|
||||||
free(NetPlay.MOTD);
|
|
||||||
if (asprintf(&NetPlay.MOTD, "Failed to retrieve a game ID: %s", strSockError(getSockErr())) == -1)
|
|
||||||
NetPlay.MOTD = NULL;
|
|
||||||
debug(LOG_ERROR, "%s", NetPlay.MOTD);
|
|
||||||
|
|
||||||
// The socket has been invalidated, so get rid of it. (using them now may cause SIGPIPE).
|
|
||||||
socketClose(rs_socket);
|
|
||||||
rs_socket = NULL;
|
|
||||||
server_not_there = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
gamestruct.gameId = ntohl(gameId);
|
|
||||||
debug(LOG_NET, "Using game ID: %u", (unsigned int)gamestruct.gameId);
|
|
||||||
|
|
||||||
// Register our game with the server
|
|
||||||
if (writeAll(rs_socket, "addg", sizeof("addg")) == SOCKET_ERROR
|
|
||||||
// and now send what the server wants
|
|
||||||
|| !NETsendGAMESTRUCT(rs_socket, &gamestruct))
|
|
||||||
{
|
|
||||||
debug(LOG_ERROR, "Failed to register game with server: %s", strSockError(getSockErr()));
|
|
||||||
socketClose(rs_socket);
|
|
||||||
rs_socket = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (readLobbyResponse(rs_socket, NET_TIMEOUT_DELAY) == SOCKET_ERROR)
|
|
||||||
{
|
|
||||||
socketClose(rs_socket);
|
|
||||||
rs_socket = NULL;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Preserves another register
|
|
||||||
registered=state;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
// Unregister the game (close the socket)
|
|
||||||
case 0:
|
|
||||||
{
|
|
||||||
if (rs_socket != NULL)
|
|
||||||
{
|
|
||||||
// we don't need this anymore, so clean up
|
|
||||||
socketClose(rs_socket);
|
|
||||||
rs_socket = NULL;
|
|
||||||
server_not_there = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Preserves another unregister
|
|
||||||
registered=state;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// ////////////////////////////////////////////////////////////////////////
|
// ////////////////////////////////////////////////////////////////////////
|
||||||
// Host a game with a given name and player name. & 4 user game flags
|
// Host a game with a given name and player name. & 4 user game flags
|
||||||
static void NETallowJoining(void)
|
static void NETallowJoining(void)
|
||||||
|
@ -2116,16 +1659,6 @@ static void NETallowJoining(void)
|
||||||
if (allow_joining == false) return;
|
if (allow_joining == false) return;
|
||||||
ASSERT(NetPlay.isHost, "Cannot receive joins if not host!");
|
ASSERT(NetPlay.isHost, "Cannot receive joins if not host!");
|
||||||
|
|
||||||
NETregisterServer(1);
|
|
||||||
|
|
||||||
// This is here since we need to get the status, before we can show the info.
|
|
||||||
// FIXME: find better location to stick this?
|
|
||||||
if (!NetPlay.ShowedMOTD)
|
|
||||||
{
|
|
||||||
ShowMOTD();
|
|
||||||
NetPlay.ShowedMOTD = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tmp_socket_set == NULL)
|
if (tmp_socket_set == NULL)
|
||||||
{
|
{
|
||||||
// initialize server socket set
|
// initialize server socket set
|
||||||
|
@ -2201,7 +1734,7 @@ static void NETallowJoining(void)
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Commented out as each masterserver check creates an error.
|
// Commented out as each masterserver check creates an error.
|
||||||
debug(LOG_ERROR, "Received an invalid version \"%d.%d\".", major, minor);
|
// debug(LOG_ERROR, "Received an invalid version \"%d.%d\".", major, minor);
|
||||||
result = htonl(ERROR_WRONGVERSION);
|
result = htonl(ERROR_WRONGVERSION);
|
||||||
memcpy(&buffer, &result, sizeof(result));
|
memcpy(&buffer, &result, sizeof(result));
|
||||||
writeAll(tmp_socket[i], &buffer, sizeof(result));
|
writeAll(tmp_socket[i], &buffer, sizeof(result));
|
||||||
|
@ -2319,7 +1852,7 @@ static void NETallowJoining(void)
|
||||||
// Wrong password. Reject.
|
// Wrong password. Reject.
|
||||||
rejected = (uint8_t)ERROR_WRONGPASSWORD;
|
rejected = (uint8_t)ERROR_WRONGPASSWORD;
|
||||||
}
|
}
|
||||||
else if ((int)NetPlay.playercount > gamestruct.desc.dwMaxPlayers)
|
else if ((int)NetPlay.playercount > NetPlay.maxPlayers)
|
||||||
{
|
{
|
||||||
// Game full. Reject.
|
// Game full. Reject.
|
||||||
rejected = (uint8_t)ERROR_FULL;
|
rejected = (uint8_t)ERROR_FULL;
|
||||||
|
@ -2330,6 +1863,13 @@ static void NETallowJoining(void)
|
||||||
rejected = (uint8_t)ERROR_WRONGDATA;
|
rejected = (uint8_t)ERROR_WRONGDATA;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Now add the player to the lobbyserver if he isn't rejected.
|
||||||
|
if (rejected == 0)
|
||||||
|
{
|
||||||
|
lobbyclient.addPlayer(index, name, NetPlay.players[index].IPtextAddress);
|
||||||
|
lobbyclient.freeError();
|
||||||
|
}
|
||||||
|
|
||||||
if (rejected)
|
if (rejected)
|
||||||
{
|
{
|
||||||
debug(LOG_INFO, "Rejecting new player, reason (%u).", (unsigned int) rejected);
|
debug(LOG_INFO, "Rejecting new player, reason (%u).", (unsigned int) rejected);
|
||||||
|
@ -2365,9 +1905,6 @@ static void NETallowJoining(void)
|
||||||
|
|
||||||
debug(LOG_NET, "Player, %s, with index of %u has joined using socket %p", name, (unsigned int)index, connected_bsocket[index]);
|
debug(LOG_NET, "Player, %s, with index of %u has joined using socket %p", name, (unsigned int)index, connected_bsocket[index]);
|
||||||
|
|
||||||
// Increment player count
|
|
||||||
gamestruct.desc.dwCurrentPlayers++;
|
|
||||||
|
|
||||||
MultiPlayerJoin(index);
|
MultiPlayerJoin(index);
|
||||||
|
|
||||||
// Narrowcast to new player that everyone has joined.
|
// Narrowcast to new player that everyone has joined.
|
||||||
|
@ -2395,10 +1932,6 @@ static void NETallowJoining(void)
|
||||||
}
|
}
|
||||||
NETfixDuplicatePlayerNames();
|
NETfixDuplicatePlayerNames();
|
||||||
|
|
||||||
// Send the updated GAMESTRUCT to the masterserver
|
|
||||||
NETregisterServer(2);
|
|
||||||
|
|
||||||
|
|
||||||
// reset flags for new players
|
// reset flags for new players
|
||||||
NetPlay.players[index].wzFile.isCancelled = false;
|
NetPlay.players[index].wzFile.isCancelled = false;
|
||||||
NetPlay.players[index].wzFile.isSending = false;
|
NetPlay.players[index].wzFile.isSending = false;
|
||||||
|
@ -2414,6 +1947,8 @@ bool NEThostGame(const char* SessionName, const char* PlayerName,
|
||||||
UDWORD plyrs) // # of players.
|
UDWORD plyrs) // # of players.
|
||||||
{
|
{
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
|
char* motd;
|
||||||
|
char* modlist;
|
||||||
|
|
||||||
debug(LOG_NET, "NEThostGame(%s, %s, %d, %d, %d, %d, %u)", SessionName, PlayerName,
|
debug(LOG_NET, "NEThostGame(%s, %s, %d, %d, %d, %d, %u)", SessionName, PlayerName,
|
||||||
one, two, three, four, plyrs);
|
one, two, three, four, plyrs);
|
||||||
|
@ -2466,40 +2001,14 @@ bool NEThostGame(const char* SessionName, const char* PlayerName,
|
||||||
}
|
}
|
||||||
|
|
||||||
NetPlay.isHost = true;
|
NetPlay.isHost = true;
|
||||||
|
// FIXME: Is this the right place for this?
|
||||||
|
NetPlay.maxPlayers = plyrs;
|
||||||
NETlogEntry("Hosting game, resetting ban list.", SYNC_FLAG, 0);
|
NETlogEntry("Hosting game, resetting ban list.", SYNC_FLAG, 0);
|
||||||
if (IPlist)
|
if (IPlist)
|
||||||
{
|
{
|
||||||
free(IPlist);
|
free(IPlist);
|
||||||
IPlist = NULL;
|
IPlist = NULL;
|
||||||
}
|
}
|
||||||
sstrcpy(gamestruct.name, SessionName);
|
|
||||||
memset(&gamestruct.desc, 0, sizeof(gamestruct.desc));
|
|
||||||
gamestruct.desc.dwSize = sizeof(gamestruct.desc);
|
|
||||||
//gamestruct.desc.guidApplication = GAME_GUID;
|
|
||||||
memset(gamestruct.desc.host, 0, sizeof(gamestruct.desc.host));
|
|
||||||
gamestruct.desc.dwCurrentPlayers = 1;
|
|
||||||
gamestruct.desc.dwMaxPlayers = plyrs;
|
|
||||||
gamestruct.desc.dwFlags = 0;
|
|
||||||
gamestruct.desc.dwUserFlags[0] = one;
|
|
||||||
gamestruct.desc.dwUserFlags[1] = two;
|
|
||||||
gamestruct.desc.dwUserFlags[2] = three;
|
|
||||||
gamestruct.desc.dwUserFlags[3] = four;
|
|
||||||
memset(gamestruct.secondaryHosts, 0, sizeof(gamestruct.secondaryHosts));
|
|
||||||
sstrcpy(gamestruct.extra, "Extra"); // extra string (future use)
|
|
||||||
sstrcpy(gamestruct.mapname, game.map); // map we are hosting
|
|
||||||
sstrcpy(gamestruct.hostname, PlayerName);
|
|
||||||
sstrcpy(gamestruct.versionstring, VersionString); // version (string)
|
|
||||||
sstrcpy(gamestruct.modlist, getModList()); // List of mods
|
|
||||||
gamestruct.GAMESTRUCT_VERSION = 3; // version of this structure
|
|
||||||
gamestruct.game_version_major = NETCODE_VERSION_MAJOR; // Netcode Major version
|
|
||||||
gamestruct.game_version_minor = NETCODE_VERSION_MINOR; // NetCode Minor version
|
|
||||||
// gamestruct.privateGame = 0; // if true, it is a private game
|
|
||||||
gamestruct.pureGame = 0; // NO mods allowed if true
|
|
||||||
gamestruct.Mods = 0; // number of concatenated mods?
|
|
||||||
gamestruct.gameId = 0;
|
|
||||||
gamestruct.future2 = 0xBAD02; // for future use
|
|
||||||
gamestruct.future3 = 0xBAD03; // for future use
|
|
||||||
gamestruct.future4 = 0xBAD04; // for future use
|
|
||||||
|
|
||||||
selectedPlayer= NET_CreatePlayer(PlayerName);
|
selectedPlayer= NET_CreatePlayer(PlayerName);
|
||||||
realSelectedPlayer = selectedPlayer;
|
realSelectedPlayer = selectedPlayer;
|
||||||
|
@ -2516,9 +2025,29 @@ bool NEThostGame(const char* SessionName, const char* PlayerName,
|
||||||
changeColour(NET_HOST_ONLY, war_GetSPcolor());
|
changeColour(NET_HOST_ONLY, war_GetSPcolor());
|
||||||
}
|
}
|
||||||
|
|
||||||
allow_joining = true;
|
// remove an existing game from the masterserver.
|
||||||
|
lobbyclient.delGame();
|
||||||
|
lobbyclient.freeError();
|
||||||
|
|
||||||
NETregisterServer(0);
|
modlist = getModList();
|
||||||
|
|
||||||
|
// Register the game on the masterserver
|
||||||
|
if (lobbyclient.addGame(&motd, (uint32_t)gameserver_port, (uint32_t)NetPlay.maxPlayers,
|
||||||
|
SessionName, VersionString, NETCODE_VERSION_MAJOR, NETCODE_VERSION_MINOR,
|
||||||
|
NetPlay.GamePassworded, modlist, game.map, PlayerName) != LOBBY_NO_ERROR)
|
||||||
|
{
|
||||||
|
lobbyError* error = lobbyclient.getError();
|
||||||
|
if (asprintf(&motd, error->message) == -1)
|
||||||
|
motd = NULL;
|
||||||
|
|
||||||
|
lobbyclient.freeError();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show the MOTD
|
||||||
|
showMOTD(motd);
|
||||||
|
free(motd);
|
||||||
|
|
||||||
|
allow_joining = true;
|
||||||
|
|
||||||
debug(LOG_NET, "Hosting a server. We are player %d.", selectedPlayer);
|
debug(LOG_NET, "Hosting a server. We are player %d.", selectedPlayer);
|
||||||
|
|
||||||
|
@ -2532,8 +2061,8 @@ bool NEThaltJoining(void)
|
||||||
debug(LOG_NET, "temporarily locking game to prevent more players");
|
debug(LOG_NET, "temporarily locking game to prevent more players");
|
||||||
|
|
||||||
allow_joining = false;
|
allow_joining = false;
|
||||||
// disconnect from the master server
|
// disconnect from the lobby server
|
||||||
NETregisterServer(0);
|
lobbyclient.stop();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2541,121 +2070,19 @@ bool NEThaltJoining(void)
|
||||||
// find games on open connection
|
// find games on open connection
|
||||||
bool NETfindGame(void)
|
bool NETfindGame(void)
|
||||||
{
|
{
|
||||||
SocketAddress* hosts;
|
if (getLobbyError() == ERROR_CHEAT || getLobbyError() == ERROR_KICKED)
|
||||||
unsigned int gamecount = 0;
|
{
|
||||||
uint32_t gamesavailable;
|
return false;
|
||||||
int result = 0;
|
}
|
||||||
debug(LOG_NET, "Looking for games...");
|
setLobbyError(ERROR_NOERROR);
|
||||||
|
|
||||||
if (getLobbyError() == ERROR_CHEAT || getLobbyError() == ERROR_KICKED)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
setLobbyError(ERROR_NOERROR);
|
|
||||||
|
|
||||||
NetPlay.games[0].desc.dwSize = 0;
|
if (lobbyclient.listGames(MaxGames) != LOBBY_NO_ERROR)
|
||||||
NetPlay.games[0].desc.dwCurrentPlayers = 0;
|
{
|
||||||
NetPlay.games[0].desc.dwMaxPlayers = 0;
|
debug(LOG_ERROR, lobbyclient.getError()->message);
|
||||||
|
lobbyclient.freeError();
|
||||||
if(!NetPlay.bComms)
|
setLobbyError(ERROR_CONNECTION);
|
||||||
{
|
return false;
|
||||||
selectedPlayer = NET_HOST_ONLY; // Host is always 0
|
}
|
||||||
NetPlay.isHost = true;
|
|
||||||
NetPlay.hostPlayer = NET_HOST_ONLY;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if ((hosts = resolveHost(masterserver_name, masterserver_port)) == NULL)
|
|
||||||
{
|
|
||||||
debug(LOG_ERROR, "Cannot resolve hostname \"%s\": %s", masterserver_name, strSockError(getSockErr()));
|
|
||||||
setLobbyError(ERROR_CONNECTION);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tcp_socket != NULL)
|
|
||||||
{
|
|
||||||
debug(LOG_NET, "Deleting tcp_socket %p", tcp_socket);
|
|
||||||
if (socket_set)
|
|
||||||
{
|
|
||||||
SocketSet_DelSocket(socket_set, tcp_socket);
|
|
||||||
}
|
|
||||||
socketClose(tcp_socket);
|
|
||||||
tcp_socket = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
tcp_socket = socketOpenAny(hosts, 15000);
|
|
||||||
|
|
||||||
deleteSocketAddress(hosts);
|
|
||||||
hosts = NULL;
|
|
||||||
|
|
||||||
if (tcp_socket == NULL)
|
|
||||||
{
|
|
||||||
debug(LOG_ERROR, "Cannot connect to \"%s:%d\": %s", masterserver_name, masterserver_port, strSockError(getSockErr()));
|
|
||||||
setLobbyError(ERROR_CONNECTION);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
debug(LOG_NET, "New tcp_socket = %p", tcp_socket);
|
|
||||||
// client machines only need 1 socket set
|
|
||||||
socket_set = allocSocketSet();
|
|
||||||
if (socket_set == NULL)
|
|
||||||
{
|
|
||||||
debug(LOG_ERROR, "Cannot create socket set: %s", strSockError(getSockErr()));
|
|
||||||
setLobbyError(ERROR_CONNECTION);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
debug(LOG_NET, "Created socket_set %p", socket_set);
|
|
||||||
|
|
||||||
SocketSet_AddSocket(socket_set, tcp_socket);
|
|
||||||
|
|
||||||
debug(LOG_NET, "Sending list cmd");
|
|
||||||
|
|
||||||
if (writeAll(tcp_socket, "list", sizeof("list")) != SOCKET_ERROR
|
|
||||||
&& checkSockets(socket_set, NET_TIMEOUT_DELAY) > 0
|
|
||||||
&& socketReadReady(tcp_socket)
|
|
||||||
&& (result = readNoInt(tcp_socket, &gamesavailable, sizeof(gamesavailable))))
|
|
||||||
{
|
|
||||||
gamesavailable = MIN(ntohl(gamesavailable), ARRAY_SIZE(NetPlay.games));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (result == SOCKET_ERROR)
|
|
||||||
{
|
|
||||||
debug(LOG_NET, "Server socket ecountered error: %s", strSockError(getSockErr()));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
debug(LOG_NET, "Server didn't respond (timeout)");
|
|
||||||
}
|
|
||||||
SocketSet_DelSocket(socket_set, tcp_socket); // mark it invalid
|
|
||||||
socketClose(tcp_socket);
|
|
||||||
tcp_socket = NULL;
|
|
||||||
|
|
||||||
// when we fail to receive a game count, bail out
|
|
||||||
setLobbyError(ERROR_CONNECTION);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
debug(LOG_NET, "receiving info on %u game(s)", (unsigned int)gamesavailable);
|
|
||||||
|
|
||||||
// Clear old games from list.
|
|
||||||
memset(NetPlay.games, 0x00, sizeof(NetPlay.games));
|
|
||||||
|
|
||||||
while (gamecount < gamesavailable)
|
|
||||||
{
|
|
||||||
// Attempt to receive a game description structure
|
|
||||||
if (!NETrecvGAMESTRUCT(&NetPlay.games[gamecount]))
|
|
||||||
{
|
|
||||||
debug(LOG_NET, "only %u game(s) received", (unsigned int)gamecount);
|
|
||||||
// If we fail, success depends on the amount of games that we've read already
|
|
||||||
return gamecount;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (NetPlay.games[gamecount].desc.host[0] == '\0')
|
|
||||||
{
|
|
||||||
strncpy(NetPlay.games[gamecount].desc.host, getSocketTextAddress(tcp_socket), sizeof(NetPlay.games[gamecount].desc.host));
|
|
||||||
}
|
|
||||||
|
|
||||||
++gamecount;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -2663,6 +2090,7 @@ bool NETfindGame(void)
|
||||||
// ////////////////////////////////////////////////////////////////////////
|
// ////////////////////////////////////////////////////////////////////////
|
||||||
// ////////////////////////////////////////////////////////////////////////
|
// ////////////////////////////////////////////////////////////////////////
|
||||||
// Functions used to setup and join games.
|
// Functions used to setup and join games.
|
||||||
|
// does setLobbyError on errors.
|
||||||
bool NETjoinGame(const char* host, uint32_t port, const char* playername)
|
bool NETjoinGame(const char* host, uint32_t port, const char* playername)
|
||||||
{
|
{
|
||||||
SocketAddress *hosts = NULL;
|
SocketAddress *hosts = NULL;
|
||||||
|
@ -2836,7 +2264,7 @@ bool NETjoinGame(const char* host, uint32_t port, const char* playername)
|
||||||
*/
|
*/
|
||||||
void NETsetMasterserverName(const char* hostname)
|
void NETsetMasterserverName(const char* hostname)
|
||||||
{
|
{
|
||||||
sstrcpy(masterserver_name, hostname);
|
lobbyclient.setHost(hostname);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -2844,7 +2272,7 @@ void NETsetMasterserverName(const char* hostname)
|
||||||
*/
|
*/
|
||||||
const char* NETgetMasterserverName()
|
const char* NETgetMasterserverName()
|
||||||
{
|
{
|
||||||
return masterserver_name;
|
return lobbyclient.getHost().c_str();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
@ -2853,7 +2281,7 @@ const char* NETgetMasterserverName()
|
||||||
*/
|
*/
|
||||||
void NETsetMasterserverPort(unsigned int port)
|
void NETsetMasterserverPort(unsigned int port)
|
||||||
{
|
{
|
||||||
masterserver_port = port;
|
lobbyclient.setPort(port);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -2861,7 +2289,7 @@ void NETsetMasterserverPort(unsigned int port)
|
||||||
*/
|
*/
|
||||||
unsigned int NETgetMasterserverPort()
|
unsigned int NETgetMasterserverPort()
|
||||||
{
|
{
|
||||||
return masterserver_port;
|
return lobbyclient.getPort();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
#define _netplay_h
|
#define _netplay_h
|
||||||
|
|
||||||
#include "nettypes.h"
|
#include "nettypes.h"
|
||||||
|
#include "netlobby.h"
|
||||||
#include <physfs.h>
|
#include <physfs.h>
|
||||||
|
|
||||||
// Lobby Connection errors
|
// Lobby Connection errors
|
||||||
|
@ -265,6 +266,7 @@ struct NETPLAY
|
||||||
{
|
{
|
||||||
GAMESTRUCT games[MaxGames]; ///< The collection of games
|
GAMESTRUCT games[MaxGames]; ///< The collection of games
|
||||||
PLAYER players[MAX_PLAYERS]; ///< The array of players.
|
PLAYER players[MAX_PLAYERS]; ///< The array of players.
|
||||||
|
uint32_t maxPlayers; ///< Maximum number of players.
|
||||||
uint32_t playercount; ///< Number of players in game.
|
uint32_t playercount; ///< Number of players in game.
|
||||||
uint32_t hostPlayer; ///< Index of host in player array
|
uint32_t hostPlayer; ///< Index of host in player array
|
||||||
uint32_t bComms; ///< Actually do the comms?
|
uint32_t bComms; ///< Actually do the comms?
|
||||||
|
@ -274,9 +276,6 @@ struct NETPLAY
|
||||||
PHYSFS_file *pMapFileHandle;
|
PHYSFS_file *pMapFileHandle;
|
||||||
char gamePassword[password_string_size]; //
|
char gamePassword[password_string_size]; //
|
||||||
bool GamePassworded; // if we have a password or not.
|
bool GamePassworded; // if we have a password or not.
|
||||||
bool ShowedMOTD; // only want to show this once
|
|
||||||
char MOTDbuffer[255]; // buffer for MOTD
|
|
||||||
char* MOTD;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct PLAYER_IP
|
struct PLAYER_IP
|
||||||
|
@ -330,7 +329,7 @@ extern int32_t NETgetGameFlagsUnjoined(unsigned int gameid, unsigned int flag);
|
||||||
extern bool NETsetGameFlags(UDWORD flag, SDWORD value); // set game flag(1-4) to value.
|
extern bool NETsetGameFlags(UDWORD flag, SDWORD value); // set game flag(1-4) to value.
|
||||||
extern bool NEThaltJoining(void); // stop new players joining this game
|
extern bool NEThaltJoining(void); // stop new players joining this game
|
||||||
extern bool NETfindGame(void); // find games being played(uses GAME_GUID);
|
extern bool NETfindGame(void); // find games being played(uses GAME_GUID);
|
||||||
extern bool NETjoinGame(const char* host, uint32_t port, const char* playername); // join game given with playername
|
extern bool NETjoinGame(const char* host, uint32_t port, const char* playername); // join game given with playername
|
||||||
extern bool NEThostGame(const char* SessionName, const char* PlayerName,// host a game
|
extern bool NEThostGame(const char* SessionName, const char* PlayerName,// host a game
|
||||||
SDWORD one, SDWORD two, SDWORD three, SDWORD four, UDWORD plyrs);
|
SDWORD one, SDWORD two, SDWORD three, SDWORD four, UDWORD plyrs);
|
||||||
extern bool NETchangePlayerName(UDWORD player, char *newName);// change a players name.
|
extern bool NETchangePlayerName(UDWORD player, char *newName);// change a players name.
|
||||||
|
|
164
src/multiint.cpp
164
src/multiint.cpp
|
@ -820,27 +820,21 @@ bool joinGame(const char* host, uint32_t port)
|
||||||
|
|
||||||
// ////////////////////////////////////////////////////////////////////////////
|
// ////////////////////////////////////////////////////////////////////////////
|
||||||
// Game Chooser Screen.
|
// Game Chooser Screen.
|
||||||
|
|
||||||
static void addGames(void)
|
static void addGames(void)
|
||||||
{
|
{
|
||||||
UDWORD i,gcount=0;
|
UDWORD i,gcount=0;
|
||||||
static const char *wrongVersionTip = "Your version of Warzone is incompatible with this game.";
|
static const char *wrongVersionTip = "Your version of Warzone is incompatible with this game.";
|
||||||
static const char *badModTip = "Your loaded mods are incompatible with this game. (Check mods/autoload/?)";
|
static const char *badModTip = "Your loaded mods are incompatible with this game. (Check mods/autoload/?)";
|
||||||
|
|
||||||
//count games to see if need two columns.
|
|
||||||
for(i=0;i<MaxGames;i++) // draw games
|
|
||||||
{
|
|
||||||
if( NetPlay.games[i].desc.dwSize !=0)
|
|
||||||
{
|
|
||||||
gcount++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
W_BUTINIT sButInit;
|
W_BUTINIT sButInit;
|
||||||
sButInit.formID = FRONTEND_BOTFORM;
|
sButInit.formID = FRONTEND_BOTFORM;
|
||||||
sButInit.width = GAMES_GAMEWIDTH;
|
sButInit.width = GAMES_GAMEWIDTH;
|
||||||
sButInit.height = GAMES_GAMEHEIGHT;
|
sButInit.height = GAMES_GAMEHEIGHT;
|
||||||
sButInit.pDisplay = displayRemoteGame;
|
sButInit.pDisplay = displayRemoteGame;
|
||||||
|
|
||||||
|
//count games to see if need two columns.
|
||||||
|
gcount = lobbyclient.games.size();
|
||||||
|
|
||||||
// we want the old games deleted, and only list games when we should
|
// we want the old games deleted, and only list games when we should
|
||||||
if (getLobbyError() || !gcount)
|
if (getLobbyError() || !gcount)
|
||||||
{
|
{
|
||||||
|
@ -856,56 +850,53 @@ static void addGames(void)
|
||||||
// only have to do this if we have any games available.
|
// only have to do this if we have any games available.
|
||||||
if (!getLobbyError() && gcount)
|
if (!getLobbyError() && gcount)
|
||||||
{
|
{
|
||||||
for (i=0; i<MaxGames; i++) // draw games
|
for (i=0; i < gcount; i++) // draw games
|
||||||
{
|
{
|
||||||
widgDelete(psWScreen, GAMES_GAMESTART+i); // remove old icon.
|
widgDelete(psWScreen, GAMES_GAMESTART+i); // remove old icon.
|
||||||
if (NetPlay.games[i].desc.dwSize !=0)
|
|
||||||
|
sButInit.id = GAMES_GAMESTART+i;
|
||||||
|
|
||||||
|
if (gcount < 9) // only center column needed.
|
||||||
{
|
{
|
||||||
|
sButInit.x = 165;
|
||||||
sButInit.id = GAMES_GAMESTART+i;
|
sButInit.y = (UWORD)(30+((5+GAMES_GAMEHEIGHT)*i) );
|
||||||
|
}
|
||||||
if (gcount < 9) // only center column needed.
|
else
|
||||||
|
{
|
||||||
|
if (i<9) //column 1
|
||||||
{
|
{
|
||||||
sButInit.x = 165;
|
sButInit.x = 50;
|
||||||
sButInit.y = (UWORD)(30+((5+GAMES_GAMEHEIGHT)*i) );
|
sButInit.y = (UWORD)(30+((5+GAMES_GAMEHEIGHT)*i) );
|
||||||
}
|
}
|
||||||
else
|
else //column 2
|
||||||
{
|
{
|
||||||
if (i<9) //column 1
|
sButInit.x = 60+GAMES_GAMEWIDTH;
|
||||||
{
|
sButInit.y = (UWORD)(30+((5+GAMES_GAMEHEIGHT)*(i-9) ) );
|
||||||
sButInit.x = 50;
|
|
||||||
sButInit.y = (UWORD)(30+((5+GAMES_GAMEHEIGHT)*i) );
|
|
||||||
}
|
|
||||||
else //column 2
|
|
||||||
{
|
|
||||||
sButInit.x = 60+GAMES_GAMEWIDTH;
|
|
||||||
sButInit.y = (UWORD)(30+((5+GAMES_GAMEHEIGHT)*(i-9) ) );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// display the correct tooltip message.
|
|
||||||
if (!NETisCorrectVersion(NetPlay.games[i].game_version_minor, NetPlay.games[i].game_version_major))
|
|
||||||
{
|
|
||||||
sButInit.pTip = wrongVersionTip;
|
|
||||||
}
|
|
||||||
else if (strcmp(NetPlay.games[i].modlist,getModList()) != 0)
|
|
||||||
{
|
|
||||||
sButInit.pTip = badModTip;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ssprintf(tooltipbuffer, "Map:%s, Game:%s, Hosted by %s ", NetPlay.games[i].mapname, NetPlay.games[i].name, NetPlay.games[i].hostname);
|
|
||||||
sButInit.pTip = tooltipbuffer;
|
|
||||||
}
|
|
||||||
sButInit.UserData = i;
|
|
||||||
|
|
||||||
widgAddButton(psWScreen, &sButInit);
|
|
||||||
}
|
}
|
||||||
|
// display the correct tooltip message.
|
||||||
|
if (!NETisCorrectVersion(lobbyclient.games[i].game_version_major, lobbyclient.games[i].game_version_minor))
|
||||||
|
{
|
||||||
|
sButInit.pTip = wrongVersionTip;
|
||||||
|
}
|
||||||
|
else if (strcmp(lobbyclient.games[i].modlist.c_str(), getModList()) != 0)
|
||||||
|
{
|
||||||
|
sButInit.pTip = badModTip;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ssprintf(tooltipbuffer, "Map:%s, Game:%s, Hosted by %s ", lobbyclient.games[i].mapname.c_str(), lobbyclient.games[i].description.c_str(), lobbyclient.games[i].hostplayer.c_str());
|
||||||
|
sButInit.pTip = tooltipbuffer;
|
||||||
|
}
|
||||||
|
sButInit.UserData = i;
|
||||||
|
|
||||||
|
widgAddButton(psWScreen, &sButInit);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// display lobby message based on results.
|
// display lobby message based on results.
|
||||||
// This is a 'button', not text so it can be hilighted/centered.
|
// This is a 'button', not text so it can be hilighted/centered.
|
||||||
const char *txt;
|
const char *txt;
|
||||||
W_BUTINIT sButInit;
|
W_BUTINIT sButInit;
|
||||||
|
|
||||||
|
@ -979,6 +970,7 @@ void runGameFind(void )
|
||||||
UDWORD id;
|
UDWORD id;
|
||||||
static UDWORD lastupdate=0;
|
static UDWORD lastupdate=0;
|
||||||
static char game_password[64]; // check if StringSize is available
|
static char game_password[64]; // check if StringSize is available
|
||||||
|
LOBBY_GAME* lGame;
|
||||||
|
|
||||||
if (lastupdate > gameTime) lastupdate = 0;
|
if (lastupdate > gameTime) lastupdate = 0;
|
||||||
if (gameTime-lastupdate > 6000 && !EnablePasswordPrompt)
|
if (gameTime-lastupdate > 6000 && !EnablePasswordPrompt)
|
||||||
|
@ -1015,29 +1007,28 @@ void runGameFind(void )
|
||||||
if (id >= GAMES_GAMESTART && id<=GAMES_GAMEEND)
|
if (id >= GAMES_GAMESTART && id<=GAMES_GAMEEND)
|
||||||
{
|
{
|
||||||
gameNumber = id-GAMES_GAMESTART;
|
gameNumber = id-GAMES_GAMESTART;
|
||||||
|
lGame = &lobbyclient.games[gameNumber];
|
||||||
|
|
||||||
if (!(NetPlay.games[gameNumber].desc.dwFlags & SESSION_JOINDISABLED)) // if still joinable
|
if (lGame->isPrivate)
|
||||||
{
|
{
|
||||||
if (NetPlay.games[gameNumber].privateGame)
|
showPasswordForm();
|
||||||
{
|
|
||||||
showPasswordForm();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ingame.localOptionsReceived = false; // note we are awaiting options
|
|
||||||
sstrcpy(game.name, NetPlay.games[gameNumber].name); // store name
|
|
||||||
|
|
||||||
joinGame(NetPlay.games[gameNumber].desc.host, 0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ingame.localOptionsReceived = false; // note we are awaiting options
|
||||||
|
sstrcpy(game.name, lGame->description.c_str()); // store name
|
||||||
|
|
||||||
|
joinGame(lGame->host.c_str(), lGame->port);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (id == CON_PASSWORDYES)
|
else if (id == CON_PASSWORDYES)
|
||||||
{
|
{
|
||||||
ingame.localOptionsReceived = false; // note we are awaiting options
|
lGame = &lobbyclient.games[gameNumber];
|
||||||
sstrcpy(game.name, NetPlay.games[gameNumber].name); // store name
|
|
||||||
|
|
||||||
joinGame(NetPlay.games[gameNumber].desc.host, 0);
|
ingame.localOptionsReceived = false; // note we are awaiting options
|
||||||
|
sstrcpy(game.name, lGame->description.c_str()); // store name
|
||||||
|
|
||||||
|
joinGame(lGame->host.c_str(), lGame->port);
|
||||||
}
|
}
|
||||||
else if (id == CON_PASSWORDNO)
|
else if (id == CON_PASSWORDNO)
|
||||||
{
|
{
|
||||||
|
@ -3623,15 +3614,16 @@ void displayRemoteGame(WIDGET *psWidget, UDWORD xOffset, UDWORD yOffset, PIELIGH
|
||||||
{
|
{
|
||||||
UDWORD x = xOffset+psWidget->x;
|
UDWORD x = xOffset+psWidget->x;
|
||||||
UDWORD y = yOffset+psWidget->y;
|
UDWORD y = yOffset+psWidget->y;
|
||||||
UDWORD i = psWidget->UserData;
|
UDWORD gameNumber = psWidget->UserData;
|
||||||
char tmp[80], gamename[StringSize];
|
char tmp[80], gamename[StringSize];
|
||||||
unsigned int ping;
|
|
||||||
|
|
||||||
if (LobbyError != ERROR_NOERROR)
|
if (getLobbyError() != ERROR_NOERROR)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LOBBY_GAME* lGame = &lobbyclient.games[gameNumber];
|
||||||
|
|
||||||
// Draw blue boxes.
|
// Draw blue boxes.
|
||||||
drawBlueBox(x,y,psWidget->width,psWidget->height);
|
drawBlueBox(x,y,psWidget->width,psWidget->height);
|
||||||
drawBlueBox(x,y,94,psWidget->height);
|
drawBlueBox(x,y,94,psWidget->height);
|
||||||
|
@ -3640,23 +3632,9 @@ void displayRemoteGame(WIDGET *psWidget, UDWORD xOffset, UDWORD yOffset, PIELIGH
|
||||||
//draw game info
|
//draw game info
|
||||||
iV_SetFont(font_regular); // font
|
iV_SetFont(font_regular); // font
|
||||||
|
|
||||||
// get game info.
|
if (NETisCorrectVersion(lGame->game_version_major, lGame->game_version_minor))
|
||||||
// TODO: Check whether this code is used at all in skirmish games, if not, remove it.
|
|
||||||
if ((NetPlay.games[i].desc.dwFlags & SESSION_JOINDISABLED)
|
|
||||||
|| strcmp(NetPlay.games[i].modlist,getModList()) != 0
|
|
||||||
|| (bMultiPlayer
|
|
||||||
&& !NetPlay.bComms
|
|
||||||
&& NETgetGameFlagsUnjoined(gameNumber,1) == SKIRMISH // the LAST bug...
|
|
||||||
&& NetPlay.games[gameNumber].desc.dwCurrentPlayers >= NetPlay.games[gameNumber].desc.dwMaxPlayers - 1))
|
|
||||||
{
|
{
|
||||||
iV_SetTextColour(WZCOL_TEXT_MEDIUM);
|
if (lGame->currentPlayers >= lGame->maxPlayers)
|
||||||
// FIXME: We should really use another way to indicate that the game is full than our current big fat cross.
|
|
||||||
// need some sort of closed thing here!
|
|
||||||
iV_DrawImage(FrontImages,IMAGE_NOJOIN,x+18,y+11);
|
|
||||||
}
|
|
||||||
else if (!NETisCorrectVersion(NetPlay.games[i].game_version_minor, NetPlay.games[i].game_version_major))
|
|
||||||
{
|
|
||||||
if (NetPlay.games[gameNumber].desc.dwCurrentPlayers >= NetPlay.games[gameNumber].desc.dwMaxPlayers)
|
|
||||||
{
|
{
|
||||||
iV_SetTextColour(WZCOL_TEXT_MEDIUM);
|
iV_SetTextColour(WZCOL_TEXT_MEDIUM);
|
||||||
}
|
}
|
||||||
|
@ -3665,7 +3643,7 @@ void displayRemoteGame(WIDGET *psWidget, UDWORD xOffset, UDWORD yOffset, PIELIGH
|
||||||
iV_SetTextColour(WZCOL_FORM_TEXT);
|
iV_SetTextColour(WZCOL_FORM_TEXT);
|
||||||
}
|
}
|
||||||
iV_DrawText(_("Players"), x + 5, y + 18);
|
iV_DrawText(_("Players"), x + 5, y + 18);
|
||||||
ssprintf(tmp, "%d/%d", NetPlay.games[i].desc.dwCurrentPlayers, NetPlay.games[i].desc.dwMaxPlayers);
|
ssprintf(tmp, "%d/%d", lGame->currentPlayers, lGame->maxPlayers);
|
||||||
iV_DrawText(tmp, x + 17, y + 33);
|
iV_DrawText(tmp, x + 17, y + 33);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -3676,11 +3654,7 @@ void displayRemoteGame(WIDGET *psWidget, UDWORD xOffset, UDWORD yOffset, PIELIGH
|
||||||
}
|
}
|
||||||
|
|
||||||
//draw type overlay.
|
//draw type overlay.
|
||||||
if( NETgetGameFlagsUnjoined(i,1) == CAMPAIGN)
|
if (lGame->isPrivate) // check to see if it is a private game
|
||||||
{
|
|
||||||
iV_DrawImage(FrontImages, IMAGE_ARENA_OVER, x + 59, y + 3);
|
|
||||||
}
|
|
||||||
else if (NetPlay.games[i].privateGame) // check to see if it is a private game
|
|
||||||
{
|
{
|
||||||
iV_DrawImage(FrontImages, IMAGE_LOCKED_NOBG, x+62, y+3); // lock icon
|
iV_DrawImage(FrontImages, IMAGE_LOCKED_NOBG, x+62, y+3); // lock icon
|
||||||
}
|
}
|
||||||
|
@ -3690,22 +3664,10 @@ void displayRemoteGame(WIDGET *psWidget, UDWORD xOffset, UDWORD yOffset, PIELIGH
|
||||||
}
|
}
|
||||||
|
|
||||||
// ping rating
|
// ping rating
|
||||||
ping = NETgetGameFlagsUnjoined(i, 2);
|
iV_DrawImage(FrontImages,IMAGE_LAMP_GREEN,x+70,y+26);
|
||||||
if (ping < PING_MED)
|
|
||||||
{
|
|
||||||
iV_DrawImage(FrontImages,IMAGE_LAMP_GREEN,x+70,y+26);
|
|
||||||
}
|
|
||||||
else if (ping >= PING_MED && ping < PING_HI)
|
|
||||||
{
|
|
||||||
iV_DrawImage(FrontImages,IMAGE_LAMP_AMBER,x+70,y+26);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
iV_DrawImage(FrontImages,IMAGE_LAMP_RED,x+70,y+26);
|
|
||||||
}
|
|
||||||
|
|
||||||
//draw game name
|
//draw game name
|
||||||
sstrcpy(gamename, NetPlay.games[i].name);
|
sstrcpy(gamename, lGame->description.c_str());
|
||||||
while(iV_GetTextWidth(gamename) > (psWidget->width-110) )
|
while(iV_GetTextWidth(gamename) > (psWidget->width-110) )
|
||||||
{
|
{
|
||||||
gamename[strlen(gamename)-1]='\0';
|
gamename[strlen(gamename)-1]='\0';
|
||||||
|
@ -3713,7 +3675,7 @@ void displayRemoteGame(WIDGET *psWidget, UDWORD xOffset, UDWORD yOffset, PIELIGH
|
||||||
iV_DrawText(gamename, x + 100, y + 18); // name
|
iV_DrawText(gamename, x + 100, y + 18); // name
|
||||||
|
|
||||||
iV_SetFont(font_small); // font
|
iV_SetFont(font_small); // font
|
||||||
iV_DrawText(NetPlay.games[i].versionstring, x + 100, y + 32); // version
|
iV_DrawText(lGame->versionstring.c_str(), x + 100, y + 32); // version
|
||||||
}
|
}
|
||||||
|
|
||||||
void displayTeamChooser(WIDGET *psWidget, UDWORD xOffset, UDWORD yOffset, PIELIGHT *pColours)
|
void displayTeamChooser(WIDGET *psWidget, UDWORD xOffset, UDWORD yOffset, PIELIGHT *pColours)
|
||||||
|
|
|
@ -416,9 +416,9 @@ void setupNewPlayer(UDWORD player)
|
||||||
|
|
||||||
// While not the perfect place for this, it has to do when a HOST joins (hosts) game
|
// While not the perfect place for this, it has to do when a HOST joins (hosts) game
|
||||||
// unfortunatly, we don't get the message until after the setup is done.
|
// unfortunatly, we don't get the message until after the setup is done.
|
||||||
void ShowMOTD(void)
|
void showMOTD(const char* motd)
|
||||||
{
|
{
|
||||||
// when HOST joins the game, show server MOTD message first
|
// when HOST joins the game, show server MOTD message first
|
||||||
addConsoleMessage(_("System message:"), DEFAULT_JUSTIFY, NOTIFY_MESSAGE);
|
addConsoleMessage(_("System message:"), DEFAULT_JUSTIFY, NOTIFY_MESSAGE);
|
||||||
addConsoleMessage(NetPlay.MOTD, DEFAULT_JUSTIFY, NOTIFY_MESSAGE);
|
addConsoleMessage(motd, DEFAULT_JUSTIFY, NOTIFY_MESSAGE);
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,7 +35,7 @@ extern void clearPlayer (UDWORD player, bool quietly);// wipe a player off t
|
||||||
//extern bool ProcessDroidOrders (void);
|
//extern bool ProcessDroidOrders (void);
|
||||||
//extern UDWORD arenaPlayersReceived;
|
//extern UDWORD arenaPlayersReceived;
|
||||||
|
|
||||||
extern void ShowMOTD(void);
|
extern void showMOTD(const char* motd);
|
||||||
extern bool recvDataCheck(NETQUEUE queue);
|
extern bool recvDataCheck(NETQUEUE queue);
|
||||||
extern bool sendDataCheck(void);
|
extern bool sendDataCheck(void);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue