mirror of
https://github.com/openmoh/openmohaa.git
synced 2025-04-28 21:57:57 +03:00
1113 lines
27 KiB
C
1113 lines
27 KiB
C
![]() |
/*
|
||
|
GameSpy Peer SDK
|
||
|
Dan "Mr. Pants" Schoenblum
|
||
|
dan@gamespy.com
|
||
|
|
||
|
Copyright 1999-2007 GameSpy Industries, Inc
|
||
|
|
||
|
devsupport@gamespy.com
|
||
|
*/
|
||
|
|
||
|
/*************
|
||
|
** INCLUDES **
|
||
|
*************/
|
||
|
#include <string.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <stdio.h>
|
||
|
#include "peerMain.h"
|
||
|
#include "peerOperations.h"
|
||
|
#include "peerSB.h"
|
||
|
#include "peerCallbacks.h"
|
||
|
#include "peerMangle.h"
|
||
|
#include "peerRooms.h"
|
||
|
#include "peerAutoMatch.h"
|
||
|
#include "peerQR.h"
|
||
|
|
||
|
/************
|
||
|
** GLOBALS **
|
||
|
************/
|
||
|
int piSBQueryVersion = QVERSION_QR2;
|
||
|
|
||
|
/**********
|
||
|
** GAMES **
|
||
|
**********/
|
||
|
static void piSBGamesListCallback
|
||
|
(
|
||
|
SBServerListPtr serverlist,
|
||
|
SBListCallbackReason reason,
|
||
|
SBServer server,
|
||
|
void * instance
|
||
|
)
|
||
|
{
|
||
|
PEER peer = (PEER)instance;
|
||
|
PEER_CONNECTION;
|
||
|
|
||
|
#if 0
|
||
|
{
|
||
|
static const char reasonStrings[][32] =
|
||
|
{
|
||
|
"serveradded",
|
||
|
"serverupdated",
|
||
|
"serverdeleted",
|
||
|
"initiallistcomplete",
|
||
|
"disconnected",
|
||
|
"queryerror",
|
||
|
"publicipdetermined"
|
||
|
};
|
||
|
char buffer[256];
|
||
|
sprintf(buffer, "GAMES | LIST | %s", reasonStrings[reason]);
|
||
|
if(server)
|
||
|
sprintf(buffer + strlen(buffer), " | %s:%d", SBServerGetPublicAddress(server), SBServerGetPublicQueryPort(server));
|
||
|
strcat(buffer, "\n");
|
||
|
OutputDebugString(buffer);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
// handle based on the callback reason
|
||
|
switch(reason)
|
||
|
{
|
||
|
case slc_serveradded:
|
||
|
// if it's added call the callback and get it updated
|
||
|
piAddListingGamesCallback(peer, PEERTrue, server, PEER_ADD);
|
||
|
if(!SBServerHasBasicKeys(server))
|
||
|
{
|
||
|
SBBool usequerychallenge = SBFalse;
|
||
|
if (serverlist->backendgameflags & QR2_USE_QUERY_CHALLENGE)
|
||
|
usequerychallenge = SBTrue;
|
||
|
SBQueryEngineUpdateServer(&connection->gameEngine, server, 0, QTYPE_BASIC, usequerychallenge);
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case slc_serverupdated:
|
||
|
// if it's updated let the app know
|
||
|
piAddListingGamesCallback(peer, PEERTrue, server, PEER_UPDATE);
|
||
|
break;
|
||
|
|
||
|
case slc_serverdeleted:
|
||
|
// if it's deleted, call the callback
|
||
|
if ((server->state & (STATE_PENDINGBASICQUERY|STATE_PENDINGFULLQUERY|STATE_PENDINGICMPQUERY)) != 0)
|
||
|
SBQueryEngineRemoveServerFromFIFOs(&connection->gameEngine, server);
|
||
|
piAddListingGamesCallback(peer, PEERTrue, server, PEER_REMOVE);
|
||
|
break;
|
||
|
|
||
|
case slc_initiallistcomplete:
|
||
|
// done with the initial list
|
||
|
connection->initialGameList = PEERFalse;
|
||
|
|
||
|
// let the app know
|
||
|
piAddListingGamesCallback(peer, PEERTrue, NULL, PEER_COMPLETE);
|
||
|
|
||
|
break;
|
||
|
|
||
|
case slc_queryerror:
|
||
|
// call the failed callback
|
||
|
piAddListingGamesCallback(peer, PEERFalse, NULL, 0);
|
||
|
break;
|
||
|
|
||
|
case slc_publicipdetermined:
|
||
|
// let the engine know the IP
|
||
|
connection->publicIP = serverlist->mypublicip;
|
||
|
SBQueryEngineSetPublicIP(&connection->gameEngine, serverlist->mypublicip);
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void piSBGamesEngineCallback
|
||
|
(
|
||
|
SBQueryEnginePtr engine,
|
||
|
SBQueryEngineCallbackReason reason,
|
||
|
SBServer server,
|
||
|
void * instance
|
||
|
)
|
||
|
{
|
||
|
PEER peer = (PEER)instance;
|
||
|
//PEER_CONNECTION;
|
||
|
|
||
|
#if 0
|
||
|
{
|
||
|
static const char reasonStrings[][32] =
|
||
|
{
|
||
|
"updatesuccess",
|
||
|
"updatefailed",
|
||
|
"engineidle"
|
||
|
};
|
||
|
char buffer[256];
|
||
|
sprintf(buffer, "GAMES | ENGINE | %s", reasonStrings[reason]);
|
||
|
if(server)
|
||
|
sprintf(buffer + strlen(buffer), " | %s:%d", SBServerGetPublicAddress(server), SBServerGetPublicQueryPort(server));
|
||
|
strcat(buffer, "\n");
|
||
|
OutputDebugString(buffer);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
// if this is an updated server, call the callback
|
||
|
if(reason == qe_updatesuccess)
|
||
|
piAddListingGamesCallback(peer, PEERTrue, server, PEER_UPDATE);
|
||
|
|
||
|
GSI_UNUSED(engine);
|
||
|
}
|
||
|
|
||
|
PEERBool piSBStartListingGames
|
||
|
(
|
||
|
PEER peer,
|
||
|
const unsigned char * fields,
|
||
|
int numFields,
|
||
|
const char * filter
|
||
|
)
|
||
|
{
|
||
|
char groupFilter[32];
|
||
|
char smartSpyFilter[MAX_FILTER_LEN];
|
||
|
char fullFields[MAX_FIELD_LIST_LEN];
|
||
|
int listLen;
|
||
|
int keyLen;
|
||
|
int i;
|
||
|
|
||
|
PEER_CONNECTION;
|
||
|
|
||
|
// check that we're initialized.
|
||
|
assert(connection->sbInitialized);
|
||
|
if(!connection->sbInitialized)
|
||
|
return PEERFalse;
|
||
|
|
||
|
// Stop it if it's going.
|
||
|
/////////////////////////
|
||
|
piSBStopListingGames(peer);
|
||
|
|
||
|
// Clear the list.
|
||
|
//////////////////
|
||
|
SBServerListClear(&connection->gameList);
|
||
|
|
||
|
// Filter by group if in one.
|
||
|
/////////////////////////////
|
||
|
if(connection->groupID)
|
||
|
sprintf(groupFilter, "groupid=%d", connection->groupID);
|
||
|
else
|
||
|
strcpy(groupFilter, "groupid is null");
|
||
|
|
||
|
// Setup the actual filter.
|
||
|
///////////////////////////
|
||
|
if(filter)
|
||
|
{
|
||
|
sprintf(smartSpyFilter, "(%s) AND (", groupFilter);
|
||
|
strzcat(smartSpyFilter, filter, sizeof(smartSpyFilter) - 1);
|
||
|
strcat(smartSpyFilter, ")");
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
strcpy(smartSpyFilter, groupFilter);
|
||
|
}
|
||
|
|
||
|
// Clear this in case it was set.
|
||
|
/////////////////////////////////
|
||
|
connection->gameEngine.numserverkeys = 0;
|
||
|
|
||
|
// We always set these keys.
|
||
|
////////////////////////////
|
||
|
strcpy(fullFields, "\\hostname\\gamemode");
|
||
|
listLen = (int)strlen(fullFields);
|
||
|
SBQueryEngineAddQueryKey(&connection->gameEngine, HOSTNAME_KEY);
|
||
|
SBQueryEngineAddQueryKey(&connection->gameEngine, GAMEMODE_KEY);
|
||
|
|
||
|
// Build the key list.
|
||
|
//////////////////////
|
||
|
for(i = 0 ; i < numFields ; i++)
|
||
|
{
|
||
|
// Check that we have the space to add the key.
|
||
|
///////////////////////////////////////////////
|
||
|
keyLen = (int)strlen(qr2_registered_key_list[fields[i]]);
|
||
|
if((listLen + keyLen + 1) >= MAX_FIELD_LIST_LEN)
|
||
|
break;
|
||
|
|
||
|
// Add the key.
|
||
|
///////////////
|
||
|
listLen += sprintf(fullFields + listLen, "\\%s", qr2_registered_key_list[fields[i]]);
|
||
|
|
||
|
// Add to the engine query list.
|
||
|
////////////////////////////////
|
||
|
SBQueryEngineAddQueryKey(&connection->gameEngine, fields[i]);
|
||
|
}
|
||
|
|
||
|
// Start updating the list.
|
||
|
///////////////////////////
|
||
|
if(SBServerListConnectAndQuery(&connection->gameList, fullFields, smartSpyFilter, PUSH_UPDATES, 0) != sbe_noerror)
|
||
|
{
|
||
|
piSBStopListingGames(peer);
|
||
|
return PEERFalse;
|
||
|
}
|
||
|
|
||
|
// Doing the initial listing.
|
||
|
/////////////////////////////
|
||
|
connection->initialGameList = PEERTrue;
|
||
|
|
||
|
// Clear the list before they start getting servers.
|
||
|
////////////////////////////////////////////////////
|
||
|
piAddListingGamesCallback(peer, PEERTrue, NULL, PEER_CLEAR);
|
||
|
|
||
|
return PEERTrue;
|
||
|
}
|
||
|
|
||
|
void piSBStopListingGames
|
||
|
(
|
||
|
PEER peer
|
||
|
)
|
||
|
{
|
||
|
PEER_CONNECTION;
|
||
|
|
||
|
// check that we're initialized.
|
||
|
assert(connection->sbInitialized);
|
||
|
if(!connection->sbInitialized)
|
||
|
return;
|
||
|
|
||
|
// Stop the listing.
|
||
|
////////////////////
|
||
|
SBServerListDisconnect(&connection->gameList);
|
||
|
|
||
|
// Stop the engine.
|
||
|
///////////////////
|
||
|
SBEngineHaltUpdates(&connection->gameEngine);
|
||
|
|
||
|
// Remove all pending game list callbacks.
|
||
|
//////////////////////////////////////////
|
||
|
piClearCallbacks(peer, PI_LISTING_GAMES_CALLBACK);
|
||
|
}
|
||
|
|
||
|
/***********
|
||
|
** GROUPS **
|
||
|
***********/
|
||
|
static void piSBGroupsListCallback
|
||
|
(
|
||
|
SBServerListPtr serverlist,
|
||
|
SBListCallbackReason reason,
|
||
|
SBServer server,
|
||
|
void * instance
|
||
|
)
|
||
|
{
|
||
|
piOperation * operation;
|
||
|
PEER peer = (PEER)instance;
|
||
|
PEER_CONNECTION;
|
||
|
|
||
|
operation = connection->listingGroupsOperation;
|
||
|
|
||
|
#if 0
|
||
|
{
|
||
|
static const char reasonStrings[][32] =
|
||
|
{
|
||
|
"serveradded",
|
||
|
"serverupdated",
|
||
|
"serverdeleted",
|
||
|
"initiallistcomplete",
|
||
|
"disconnected",
|
||
|
"queryerror",
|
||
|
"publicipdetermined"
|
||
|
};
|
||
|
char buffer[256];
|
||
|
sprintf(buffer, "GROUPS | LIST | %s", reasonStrings[reason]);
|
||
|
if(server)
|
||
|
sprintf(buffer + strlen(buffer), " | %d", ntohl(SBServerGetPublicInetAddress(server)));
|
||
|
strcat(buffer, "\n");
|
||
|
OutputDebugString(buffer);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
// handle based on the callback reason
|
||
|
switch(reason)
|
||
|
{
|
||
|
// if it's added call the callback
|
||
|
case slc_serveradded:
|
||
|
{
|
||
|
int groupID = (int)ntohl(SBServerGetPublicInetAddress(server));
|
||
|
//#ifndef GSI_PEER_UNICODE
|
||
|
const char * name = SBServerGetStringValueA(server, "hostname", "(No Name)");
|
||
|
int numWaiting = SBServerGetIntValueA(server, "numwaiting", 0);
|
||
|
int maxWaiting = SBServerGetIntValueA(server, "maxwaiting", 0);
|
||
|
int numGames = SBServerGetIntValueA(server, "numservers", 0);
|
||
|
int numPlaying = SBServerGetIntValueA(server, "numplayers", 0);
|
||
|
//#else
|
||
|
// const unsigned short * name = SBServerGetStringValue(server, L"hostname", L"(No Name)");
|
||
|
// int numWaiting = SBServerGetIntValue(server, L"numwaiting", 0);
|
||
|
// int maxWaiting = SBServerGetIntValue(server, L"maxwaiting", 0);
|
||
|
// int numGames = SBServerGetIntValue(server, L"numservers", 0);
|
||
|
// int numPlaying = SBServerGetIntValue(server, L"numplayers", 0);
|
||
|
//#endif
|
||
|
piAddListGroupRoomsCallback(peer, PEERTrue, groupID, server, name, numWaiting, maxWaiting, numGames, numPlaying, (peerListGroupRoomsCallback)operation->callback, operation->callbackParam, operation->ID);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
// initial list completion
|
||
|
case slc_initiallistcomplete:
|
||
|
// calling the terminating callback
|
||
|
piAddListGroupRoomsCallback(peer, PEERTrue, 0, NULL, NULL, 0, 0, 0, 0, (peerListGroupRoomsCallback)operation->callback, operation->callbackParam, operation->ID);
|
||
|
piRemoveOperation(peer, operation);
|
||
|
connection->listingGroupsOperation = NULL;
|
||
|
break;
|
||
|
|
||
|
// check for a query error
|
||
|
case slc_queryerror:
|
||
|
// call the failed callback
|
||
|
piAddListGroupRoomsCallback(peer, PEERFalse, 0, NULL, NULL, 0, 0, 0, 0, (peerListGroupRoomsCallback)operation->callback, operation->callbackParam, operation->ID);
|
||
|
piRemoveOperation(peer, operation);
|
||
|
connection->listingGroupsOperation = NULL;
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
GSI_UNUSED(serverlist);
|
||
|
}
|
||
|
|
||
|
PEERBool piSBStartListingGroups
|
||
|
(
|
||
|
PEER peer,
|
||
|
const char * fields
|
||
|
)
|
||
|
{
|
||
|
char fullFields[MAX_FIELD_LIST_LEN + 1];
|
||
|
int len;
|
||
|
|
||
|
PEER_CONNECTION;
|
||
|
|
||
|
// check that we're initialized.
|
||
|
assert(connection->sbInitialized);
|
||
|
if(!connection->sbInitialized)
|
||
|
return PEERFalse;
|
||
|
|
||
|
// Stop it if it's going.
|
||
|
/////////////////////////
|
||
|
piSBStopListingGroups(peer);
|
||
|
|
||
|
// Clear the list.
|
||
|
//////////////////
|
||
|
SBServerListClear(&connection->groupList);
|
||
|
|
||
|
// Setup the fields list.
|
||
|
/////////////////////////
|
||
|
strcpy(fullFields, "\\hostname\\numwaiting\\maxwaiting\\numservers\\numplayers");
|
||
|
len = (int)strlen(fullFields);
|
||
|
strzcpy(fullFields + len, fields, (unsigned int)MAX_FIELD_LIST_LEN - len);
|
||
|
|
||
|
// Start updating the list.
|
||
|
///////////////////////////
|
||
|
if(SBServerListConnectAndQuery(&connection->groupList, fullFields, "", SEND_GROUPS, 0) != sbe_noerror)
|
||
|
{
|
||
|
piSBStopListingGroups(peer);
|
||
|
return PEERFalse;
|
||
|
}
|
||
|
|
||
|
return PEERTrue;
|
||
|
}
|
||
|
|
||
|
void piSBStopListingGroups
|
||
|
(
|
||
|
PEER peer
|
||
|
)
|
||
|
{
|
||
|
PEER_CONNECTION;
|
||
|
|
||
|
// check that we're initialized.
|
||
|
assert(connection->sbInitialized);
|
||
|
if(!connection->sbInitialized)
|
||
|
return;
|
||
|
|
||
|
// Stop the listing.
|
||
|
////////////////////
|
||
|
SBServerListDisconnect(&connection->groupList);
|
||
|
}
|
||
|
|
||
|
/**************
|
||
|
** AUTOMATCH **
|
||
|
**************/
|
||
|
static PEERBool piIsLocalServer(PEER peer, SBServer server)
|
||
|
{
|
||
|
PEER_CONNECTION;
|
||
|
|
||
|
// check the public ip
|
||
|
if(SBServerGetPublicInetAddress(server) != connection->publicIP)
|
||
|
return PEERFalse;
|
||
|
|
||
|
// check the private ip and port
|
||
|
if(SBServerHasPrivateAddress(server))
|
||
|
{
|
||
|
if(SBServerGetPrivateInetAddress(server) != connection->privateIP)
|
||
|
return PEERFalse;
|
||
|
|
||
|
if(SBServerGetPrivateQueryPort(server) != connection->autoMatchOperation->port)
|
||
|
return PEERFalse;
|
||
|
}
|
||
|
// check the public port
|
||
|
else
|
||
|
{
|
||
|
if(SBServerGetPublicQueryPort(server) != connection->autoMatchOperation->port)
|
||
|
return PEERFalse;
|
||
|
}
|
||
|
|
||
|
return PEERTrue;
|
||
|
}
|
||
|
|
||
|
static int piSBAutoMatchGetServerRating(PEER peer, SBServer server)
|
||
|
{
|
||
|
PEER_CONNECTION;
|
||
|
|
||
|
// ignore the local machine
|
||
|
if(connection->autoMatchReporting && piIsLocalServer(peer, server))
|
||
|
return 0;
|
||
|
|
||
|
// check if it got the keys.
|
||
|
if(!SBServerHasFullKeys(server))
|
||
|
return 0;
|
||
|
|
||
|
// ignore full servers
|
||
|
#ifndef GSI_UNICODE
|
||
|
if(SBServerGetIntValue(server, "numplayers", 0) >= SBServerGetIntValue(server, "maxplayers", 0))
|
||
|
return 0;
|
||
|
#else
|
||
|
if(SBServerGetIntValue(server, L"numplayers", 0) >= SBServerGetIntValue(server, L"maxplayers", 0))
|
||
|
return 0;
|
||
|
#endif
|
||
|
|
||
|
// return the rating for this server
|
||
|
return piCallAutoMatchRateCallback(peer, server);
|
||
|
}
|
||
|
|
||
|
static void piSBAutoMatchCheckUpdatedServer(PEER peer, SBServer server)
|
||
|
{
|
||
|
int rating;
|
||
|
|
||
|
PEER_CONNECTION;
|
||
|
|
||
|
// only do this if we're waiting
|
||
|
if(connection->autoMatchStatus != PEERWaiting)
|
||
|
return;
|
||
|
|
||
|
// if we're already joining a staging room, ignore this
|
||
|
if(connection->enteringRoom[StagingRoom])
|
||
|
return;
|
||
|
|
||
|
// get the rating for this server
|
||
|
rating = piSBAutoMatchGetServerRating(peer, server);
|
||
|
|
||
|
// if it's not acceptable, ignore
|
||
|
if(rating <= 0)
|
||
|
return;
|
||
|
|
||
|
// stop reporting and leave the room
|
||
|
piStopAutoMatchReporting(peer);
|
||
|
piLeaveRoom(peer, StagingRoom, "");
|
||
|
|
||
|
// join the match
|
||
|
if(!piJoinAutoMatchRoom(peer, server))
|
||
|
{
|
||
|
// it's bad news if it failed to start the op
|
||
|
piSetAutoMatchStatus(peer, PEERFailed);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void piSBAutoMatchCheckPushedServer(PEER peer, SBServer server)
|
||
|
{
|
||
|
SortInfo info;
|
||
|
int aRating;
|
||
|
PEER_CONNECTION;
|
||
|
|
||
|
// We must be in the waiting or searching phase to rate a server this way
|
||
|
if(connection->autoMatchStatus != PEERWaiting && connection->autoMatchStatus != PEERSearching)
|
||
|
return;
|
||
|
|
||
|
// if we're already joining a staging room, ignore this
|
||
|
if(connection->enteringRoom[StagingRoom])
|
||
|
return;
|
||
|
|
||
|
// Check if this is the only server in our list
|
||
|
if (SBServerListCount(&connection->autoMatchList) == 1)
|
||
|
{
|
||
|
// get the rating for this server
|
||
|
aRating = piSBAutoMatchGetServerRating(peer, server);
|
||
|
|
||
|
// if it's not acceptable, ignore
|
||
|
if(aRating <= 0)
|
||
|
return;
|
||
|
|
||
|
// stop reporting and leave the room
|
||
|
piStopAutoMatchReporting(peer);
|
||
|
piLeaveRoom(peer, StagingRoom, "");
|
||
|
|
||
|
// join the match
|
||
|
if(!piJoinAutoMatchRoom(peer, server))
|
||
|
{
|
||
|
// it's bad news if it failed to start the op
|
||
|
piSetAutoMatchStatus(peer, PEERFailed);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// otherwise we'll need to run through all the servers
|
||
|
// rate each of them and join the one with the highest rating.
|
||
|
else if (SBServerListCount(&connection->autoMatchList) > 1)
|
||
|
{
|
||
|
int count, i;
|
||
|
SBServer aServer;
|
||
|
|
||
|
// loop through all the servers.
|
||
|
count = SBServerListCount(&connection->autoMatchList);
|
||
|
for(i = (count - 1) ; i >= 0 ; i--)
|
||
|
{
|
||
|
// get the server.
|
||
|
aServer = SBServerListNth(&connection->autoMatchList, i);
|
||
|
|
||
|
// get the rating for this server
|
||
|
aRating = piSBAutoMatchGetServerRating(peer, aServer);
|
||
|
if(aRating <= 0)
|
||
|
{
|
||
|
SBServerListRemoveAt(&connection->autoMatchList, i);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
// store this server's rating
|
||
|
SBServerAddIntKeyValue(aServer, PI_AUTOMATCH_RATING_KEY, aRating);
|
||
|
}
|
||
|
|
||
|
// recount
|
||
|
count = SBServerListCount(&connection->autoMatchList);
|
||
|
|
||
|
// no matches?
|
||
|
if(!count)
|
||
|
{
|
||
|
piSetAutoMatchStatus(peer, PEERWaiting);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// sort by rating
|
||
|
#ifdef GSI_UNICODE
|
||
|
_tcscpy(info.sortkey, (const unsigned short *)PI_AUTOMATCH_RATING_KEY);
|
||
|
#else
|
||
|
_tcscpy(info.sortkey, PI_AUTOMATCH_RATING_KEY);
|
||
|
#endif
|
||
|
info.comparemode = sbcm_int;
|
||
|
SBServerListSort(&connection->autoMatchList, SBTrue, info);
|
||
|
|
||
|
piStopAutoMatchReporting(peer);
|
||
|
piLeaveRoom(peer, StagingRoom, "");
|
||
|
|
||
|
// join the top rated match
|
||
|
if(!piJoinAutoMatchRoom(peer, SBServerListNth(&connection->autoMatchList, 0)))
|
||
|
{
|
||
|
// it's bad news if it failed to start the op
|
||
|
piSetAutoMatchStatus(peer, PEERFailed);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
static void piSBAutoMatchListCallback
|
||
|
(
|
||
|
SBServerListPtr serverlist,
|
||
|
SBListCallbackReason reason,
|
||
|
SBServer server,
|
||
|
void * instance
|
||
|
)
|
||
|
{
|
||
|
PEER peer = (PEER)instance;
|
||
|
PEER_CONNECTION;
|
||
|
|
||
|
#if 0
|
||
|
{
|
||
|
static const char reasonStrings[][32] =
|
||
|
{
|
||
|
"serveradded",
|
||
|
"serverupdated",
|
||
|
"serverdeleted",
|
||
|
"initiallistcomplete",
|
||
|
"disconnected",
|
||
|
"queryerror",
|
||
|
"publicipdetermined"
|
||
|
};
|
||
|
char buffer[256];
|
||
|
sprintf(buffer, "AUTOMATCH | LIST | %s", reasonStrings[reason]);
|
||
|
if(server)
|
||
|
sprintf(buffer + strlen(buffer), " | %s:%d", SBServerGetPublicAddress(server), SBServerGetPublicQueryPort(server));
|
||
|
strcat(buffer, "\n");
|
||
|
OutputDebugString(buffer);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
// handle based on the callback reason
|
||
|
switch(reason)
|
||
|
{
|
||
|
case slc_serveradded:
|
||
|
// If we have the server info, or we already requested it
|
||
|
// just bail
|
||
|
if(server->state & (STATE_FULLKEYS|STATE_PENDINGFULLQUERY))
|
||
|
break;
|
||
|
|
||
|
// If it's behind a firewall, get the keys from the master
|
||
|
if (!SBServerDirectConnect(server))
|
||
|
SBGetServerRulesFromMaster(&connection->autoMatchList, SBServerGetPublicInetAddress(server), SBServerGetPublicQueryPortNBO(server));
|
||
|
else // send normal update
|
||
|
{
|
||
|
SBBool usequerychallenge = SBFalse;
|
||
|
if (serverlist->backendgameflags & QR2_USE_QUERY_CHALLENGE)
|
||
|
usequerychallenge = SBTrue;
|
||
|
SBQueryEngineUpdateServer(&connection->autoMatchEngine, server, 0, QTYPE_FULL, usequerychallenge);
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case slc_serverupdated:
|
||
|
// If we don't have the full keys, request them
|
||
|
if(!SBServerHasFullKeys(server) && SBServerDirectConnect(server) && !(server->state & STATE_PENDINGFULLQUERY))
|
||
|
{
|
||
|
// Send a normal query, if the server is firewalled we wouldn't be here
|
||
|
// assert(SBServerDirectConnect(server))
|
||
|
SBBool usequerychallenge = SBFalse;
|
||
|
if (serverlist->backendgameflags & QR2_USE_QUERY_CHALLENGE)
|
||
|
usequerychallenge = SBTrue;
|
||
|
SBQueryEngineUpdateServer(&connection->autoMatchEngine, server, 0, QTYPE_FULL, usequerychallenge);
|
||
|
}
|
||
|
else if(!SBServerHasFullKeys(server) && !SBServerDirectConnect(server))
|
||
|
{
|
||
|
SBGetServerRulesFromMaster(&connection->autoMatchList, SBServerGetPublicInetAddress(server), SBServerGetPublicQueryPortNBO(server));
|
||
|
}
|
||
|
else
|
||
|
piSBAutoMatchCheckPushedServer(peer, server);
|
||
|
break;
|
||
|
|
||
|
case slc_serverdeleted:
|
||
|
// make sure it's not in the update queue
|
||
|
if ((server->state & (STATE_PENDINGBASICQUERY|STATE_PENDINGFULLQUERY)) != 0)
|
||
|
SBQueryEngineRemoveServerFromFIFOs(&connection->autoMatchEngine, server);
|
||
|
break;
|
||
|
|
||
|
case slc_initiallistcomplete:
|
||
|
// if no servers were found, start Waiting
|
||
|
// or if servers were found but all were behind a firewall, start Waiting
|
||
|
if(!SBServerListCount(&connection->autoMatchList) ||
|
||
|
(0==connection->autoMatchEngine.querylist.count))
|
||
|
{
|
||
|
piSetAutoMatchStatus(peer, PEERWaiting);
|
||
|
}
|
||
|
break;
|
||
|
case slc_queryerror:
|
||
|
// browsing failed
|
||
|
connection->autoMatchSBFailed = PEERTrue;
|
||
|
if(connection->autoMatchStatus == PEERSearching)
|
||
|
piSetAutoMatchStatus(peer, connection->autoMatchQRFailed?PEERFailed:PEERWaiting);
|
||
|
break;
|
||
|
|
||
|
case slc_publicipdetermined:
|
||
|
// let the engine know the IP
|
||
|
connection->publicIP = serverlist->mypublicip;
|
||
|
SBQueryEngineSetPublicIP(&connection->gameEngine, serverlist->mypublicip);
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void piSBAutoMatchEngineCallback
|
||
|
(
|
||
|
SBQueryEnginePtr engine,
|
||
|
SBQueryEngineCallbackReason reason,
|
||
|
SBServer server,
|
||
|
void * instance
|
||
|
)
|
||
|
{
|
||
|
PEER peer = (PEER)instance;
|
||
|
int i;
|
||
|
int count;
|
||
|
int rating;
|
||
|
SortInfo info;
|
||
|
|
||
|
PEER_CONNECTION;
|
||
|
|
||
|
#if 0
|
||
|
{
|
||
|
static const char reasonStrings[][32] =
|
||
|
{
|
||
|
"updatesuccess",
|
||
|
"updatefailed",
|
||
|
"engineidle"
|
||
|
};
|
||
|
char buffer[256];
|
||
|
sprintf(buffer, "AUTOMATCH | ENGINE | %s", reasonStrings[reason]);
|
||
|
if(server)
|
||
|
sprintf(buffer + strlen(buffer), " | %s:%d", SBServerGetPublicAddress(server), SBServerGetPublicQueryPort(server));
|
||
|
strcat(buffer, "\n");
|
||
|
OutputDebugString(buffer);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
// handle based on the callback reason
|
||
|
switch(reason)
|
||
|
{
|
||
|
case qe_updatesuccess:
|
||
|
// check the server
|
||
|
piSBAutoMatchCheckUpdatedServer(peer, server);
|
||
|
break;
|
||
|
|
||
|
case qe_updatefailed:
|
||
|
if(!SBServerListCount(&connection->autoMatchList))
|
||
|
piSetAutoMatchStatus(peer, PEERWaiting);
|
||
|
break;
|
||
|
|
||
|
// check if the querying is complete.
|
||
|
case qe_engineidle:
|
||
|
// make sure we're searching
|
||
|
if(connection->autoMatchStatus != PEERSearching)
|
||
|
return;
|
||
|
|
||
|
// if we're already in or joining a staging room, ignore this
|
||
|
if(connection->inRoom[StagingRoom] || connection->enteringRoom[StagingRoom])
|
||
|
return;
|
||
|
|
||
|
// loop through all the servers.
|
||
|
count = SBServerListCount(&connection->autoMatchList);
|
||
|
for(i = (count - 1) ; i >= 0 ; i--)
|
||
|
{
|
||
|
// get the server.
|
||
|
server = SBServerListNth(&connection->autoMatchList, i);
|
||
|
|
||
|
// get the rating for this server
|
||
|
rating = piSBAutoMatchGetServerRating(peer, server);
|
||
|
if(rating <= 0)
|
||
|
{
|
||
|
SBServerListRemoveAt(&connection->autoMatchList, i);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
// store this server's rating
|
||
|
SBServerAddIntKeyValue(server, PI_AUTOMATCH_RATING_KEY, rating);
|
||
|
}
|
||
|
|
||
|
// recount
|
||
|
count = SBServerListCount(&connection->autoMatchList);
|
||
|
|
||
|
// no matches?
|
||
|
if(!count)
|
||
|
{
|
||
|
piSetAutoMatchStatus(peer, PEERWaiting);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// sort by rating
|
||
|
#ifdef GSI_UNICODE
|
||
|
_tcscpy(info.sortkey, (const unsigned short *)PI_AUTOMATCH_RATING_KEY);
|
||
|
#else
|
||
|
_tcscpy(info.sortkey, PI_AUTOMATCH_RATING_KEY);
|
||
|
#endif
|
||
|
|
||
|
info.comparemode = sbcm_int;
|
||
|
SBServerListSort(&connection->autoMatchList, SBTrue, info);
|
||
|
|
||
|
// join the top rated match
|
||
|
if(!piJoinAutoMatchRoom(peer, SBServerListNth(&connection->autoMatchList, 0)))
|
||
|
{
|
||
|
// it's bad news if it failed to start the op
|
||
|
piSetAutoMatchStatus(peer, PEERFailed);
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
GSI_UNUSED(engine);
|
||
|
}
|
||
|
|
||
|
PEERBool piSBStartListingAutoMatches
|
||
|
(
|
||
|
PEER peer
|
||
|
)
|
||
|
{
|
||
|
PEER_CONNECTION;
|
||
|
|
||
|
// check that we're initialized.
|
||
|
assert(connection->sbInitialized);
|
||
|
if(!connection->sbInitialized)
|
||
|
{
|
||
|
connection->autoMatchSBFailed = PEERTrue;
|
||
|
return PEERFalse;
|
||
|
}
|
||
|
|
||
|
// stop it if it's going.
|
||
|
piSBStopListingAutoMatches(peer);
|
||
|
|
||
|
// clear the list.
|
||
|
SBServerListClear(&connection->autoMatchList);
|
||
|
|
||
|
// start updating the list.
|
||
|
if(SBServerListConnectAndQuery(&connection->autoMatchList, NULL, connection->autoMatchFilter, PUSH_UPDATES, 0) != sbe_noerror)
|
||
|
{
|
||
|
piSBStopListingAutoMatches(peer);
|
||
|
connection->autoMatchSBFailed = PEERTrue;
|
||
|
return PEERFalse;
|
||
|
}
|
||
|
|
||
|
// clear the error flag
|
||
|
connection->autoMatchSBFailed = PEERFalse;
|
||
|
|
||
|
// we're browsing
|
||
|
connection->autoMatchBrowsing = PEERTrue;
|
||
|
|
||
|
return PEERTrue;
|
||
|
}
|
||
|
|
||
|
void piSBStopListingAutoMatches
|
||
|
(
|
||
|
PEER peer
|
||
|
)
|
||
|
{
|
||
|
PEER_CONNECTION;
|
||
|
|
||
|
// check that we're initialized.
|
||
|
assert(connection->sbInitialized);
|
||
|
if(!connection->sbInitialized)
|
||
|
return;
|
||
|
|
||
|
// we're done browsing
|
||
|
connection->autoMatchBrowsing = PEERFalse;
|
||
|
|
||
|
// stop the listing.
|
||
|
SBServerListDisconnect(&connection->autoMatchList);
|
||
|
|
||
|
// stop the engine.
|
||
|
SBEngineHaltUpdates(&connection->autoMatchEngine);
|
||
|
}
|
||
|
|
||
|
/**************
|
||
|
** FUNCTIONS **
|
||
|
**************/
|
||
|
PEERBool piSBInit
|
||
|
(
|
||
|
PEER peer
|
||
|
)
|
||
|
{
|
||
|
char autoMatchTitle[PI_TITLE_MAX_LEN];
|
||
|
|
||
|
PEER_CONNECTION;
|
||
|
|
||
|
// init the game list and engine
|
||
|
memset(&connection->gameList, 0, sizeof(connection->gameList));
|
||
|
SBServerListInit(&connection->gameList, connection->title, connection->sbName, connection->sbSecretKey, connection->sbGameVersion, SBFalse, piSBGamesListCallback, peer);
|
||
|
SBQueryEngineInit(&connection->gameEngine, connection->sbMaxUpdates, piSBQueryVersion, SBFalse, piSBGamesEngineCallback, peer);
|
||
|
|
||
|
// init the group list
|
||
|
memset(&connection->groupList, 0, sizeof(connection->groupList));
|
||
|
SBServerListInit(&connection->groupList, connection->title, connection->sbName, connection->sbSecretKey, connection->sbGameVersion, SBFalse, piSBGroupsListCallback, peer);
|
||
|
|
||
|
// init the AutoMatch list and engine
|
||
|
strzcpy(autoMatchTitle, connection->title, sizeof(autoMatchTitle));
|
||
|
strzcat(autoMatchTitle, "am", sizeof(autoMatchTitle));
|
||
|
memset(&connection->autoMatchList, 0, sizeof(connection->autoMatchList));
|
||
|
SBServerListInit(&connection->autoMatchList, autoMatchTitle, connection->sbName, connection->sbSecretKey, connection->sbGameVersion, SBFalse, piSBAutoMatchListCallback, peer);
|
||
|
SBQueryEngineInit(&connection->autoMatchEngine, connection->sbMaxUpdates, piSBQueryVersion, SBFalse, piSBAutoMatchEngineCallback, peer);
|
||
|
|
||
|
// no host server yet
|
||
|
connection->hostServer = NULL;
|
||
|
|
||
|
// initialized
|
||
|
connection->sbInitialized = PEERTrue;
|
||
|
|
||
|
return PEERTrue;
|
||
|
}
|
||
|
|
||
|
void piSBCleanup
|
||
|
(
|
||
|
PEER peer
|
||
|
)
|
||
|
{
|
||
|
PEER_CONNECTION;
|
||
|
|
||
|
// stop listing
|
||
|
piSBStopListingGames(peer);
|
||
|
piSBStopListingGroups(peer);
|
||
|
|
||
|
// cleanup the SB
|
||
|
if(connection->sbInitialized)
|
||
|
{
|
||
|
SBServerListCleanup(&connection->gameList);
|
||
|
SBEngineCleanup(&connection->gameEngine);
|
||
|
SBServerListCleanup(&connection->groupList);
|
||
|
SBServerListCleanup(&connection->autoMatchList);
|
||
|
SBEngineCleanup(&connection->autoMatchEngine);
|
||
|
}
|
||
|
|
||
|
// AutoMatch cleanup
|
||
|
connection->autoMatchBrowsing = PEERFalse;
|
||
|
}
|
||
|
|
||
|
|
||
|
void piSBUpdateGame
|
||
|
(
|
||
|
PEER peer,
|
||
|
SBServer server,
|
||
|
PEERBool fullUpdate,
|
||
|
PEERBool forceUpdateByMaster,
|
||
|
PEERBool icmpEcho
|
||
|
)
|
||
|
{
|
||
|
PEER_CONNECTION;
|
||
|
|
||
|
// check that we're initialized.
|
||
|
assert(connection->sbInitialized);
|
||
|
|
||
|
if(!connection->sbInitialized)
|
||
|
return;
|
||
|
|
||
|
// Changed 08-26-2004
|
||
|
// Saad Nader
|
||
|
// Added force update by master server parameter
|
||
|
// for internal update function
|
||
|
/////////////////////
|
||
|
if (forceUpdateByMaster)
|
||
|
{
|
||
|
// remove from the existing update lists if present
|
||
|
if(server->flags & UNSOLICITED_UDP_FLAG)
|
||
|
SBQueryEngineRemoveServerFromFIFOs(&connection->gameEngine, server);
|
||
|
|
||
|
// Obtain the information via the master server
|
||
|
SBGetServerRulesFromMaster(&connection->gameList, server->publicip, server->publicport);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (icmpEcho && (server->flags & ICMP_IP_FLAG))
|
||
|
{
|
||
|
SBBool usequerychallenge = SBFalse;
|
||
|
if (connection->gameList.backendgameflags & QR2_USE_QUERY_CHALLENGE)
|
||
|
usequerychallenge = SBTrue;
|
||
|
|
||
|
if (server->flags & UNSOLICITED_UDP_FLAG)
|
||
|
SBQueryEngineRemoveServerFromFIFOs(&connection->gameEngine, server);
|
||
|
|
||
|
SBQueryEngineUpdateServer(&connection->gameEngine, server, 1, QTYPE_ICMP, usequerychallenge);
|
||
|
return;
|
||
|
}
|
||
|
// if the server supports unsolicited UDP, query it directly
|
||
|
if(server->flags & UNSOLICITED_UDP_FLAG)
|
||
|
{
|
||
|
SBBool usequerychallenge = SBFalse;
|
||
|
if (connection->gameList.backendgameflags & QR2_USE_QUERY_CHALLENGE)
|
||
|
usequerychallenge = SBTrue;
|
||
|
|
||
|
// remove from the existing update lists if present
|
||
|
SBQueryEngineRemoveServerFromFIFOs(&connection->gameEngine, server);
|
||
|
|
||
|
// query the server
|
||
|
SBQueryEngineUpdateServer(&connection->gameEngine, server, 1, fullUpdate?QTYPE_FULL:QTYPE_BASIC, usequerychallenge);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// get the values from the master server
|
||
|
SBGetServerRulesFromMaster(&connection->gameList, server->publicip, server->publicport);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void piSBThink
|
||
|
(
|
||
|
PEER peer
|
||
|
)
|
||
|
{
|
||
|
PEER_CONNECTION;
|
||
|
|
||
|
// check that we're initialized.
|
||
|
if(!connection->sbInitialized)
|
||
|
return;
|
||
|
|
||
|
SBListThink(&connection->gameList);
|
||
|
SBListThink(&connection->groupList);
|
||
|
SBQueryEngineThink(&connection->gameEngine);
|
||
|
SBListThink(&connection->autoMatchList);
|
||
|
SBQueryEngineThink(&connection->autoMatchEngine);
|
||
|
}
|
||
|
|
||
|
void piSendNatNegotiateCookie
|
||
|
(
|
||
|
PEER peer,
|
||
|
unsigned int ip,
|
||
|
unsigned short port,
|
||
|
int cookie
|
||
|
)
|
||
|
{
|
||
|
PEER_CONNECTION;
|
||
|
|
||
|
// check that we're initialized.
|
||
|
if(!connection->sbInitialized)
|
||
|
return;
|
||
|
|
||
|
// send the cookie
|
||
|
if (peerIsAutoMatching(peer))
|
||
|
{
|
||
|
SBSendNatNegotiateCookieToServer(&connection->autoMatchList, ip, htons(port), cookie);
|
||
|
}
|
||
|
else
|
||
|
SBSendNatNegotiateCookieToServer(&connection->gameList, ip, htons(port), cookie);
|
||
|
}
|
||
|
|
||
|
void piSendMessageToServer
|
||
|
(
|
||
|
PEER peer,
|
||
|
unsigned int ip,
|
||
|
unsigned short port,
|
||
|
const char * data,
|
||
|
int len
|
||
|
)
|
||
|
{
|
||
|
PEER_CONNECTION;
|
||
|
|
||
|
// check that we're initialized.
|
||
|
if(!connection->sbInitialized)
|
||
|
return;
|
||
|
|
||
|
// send the message
|
||
|
if(peerIsAutoMatching(peer))
|
||
|
{
|
||
|
SBSendMessageToServer(&connection->autoMatchList, ip, port, data, len);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
SBSendMessageToServer(&connection->gameList, ip, port, data, len);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void piSBCloneServerTableMap(void * elem, void * clientData)
|
||
|
{
|
||
|
SBKeyValuePair * kvPair = (SBKeyValuePair *)elem;
|
||
|
SBServer clone = (SBServer)clientData;
|
||
|
|
||
|
SBServerAddKeyValue(clone, kvPair->key, kvPair->value);
|
||
|
}
|
||
|
|
||
|
SBServer piSBCloneServer(SBServer server)
|
||
|
{
|
||
|
SBServer clone;
|
||
|
HashTable table;
|
||
|
|
||
|
// allocate the clone
|
||
|
clone = SBAllocServer(NULL, server->publicip, server->publicport);
|
||
|
|
||
|
// save off the table
|
||
|
table = clone->keyvals;
|
||
|
|
||
|
// copy the source server
|
||
|
memcpy(clone, server, sizeof(struct _SBServer));
|
||
|
clone->keyvals = table;
|
||
|
clone->next = NULL;
|
||
|
|
||
|
// copy all the values from the original table
|
||
|
TableMap(server->keyvals, piSBCloneServerTableMap, clone);
|
||
|
|
||
|
return clone;
|
||
|
}
|
||
|
|
||
|
void piSBFreeHostServer(PEER peer)
|
||
|
{
|
||
|
PEER_CONNECTION;
|
||
|
|
||
|
if(!connection->hostServer)
|
||
|
return;
|
||
|
|
||
|
SBServerFree(&connection->hostServer);
|
||
|
connection->hostServer = NULL;
|
||
|
}
|