1323 lines
34 KiB
C++
1323 lines
34 KiB
C++
![]() |
/*
|
||
|
** Command & Conquer Generals Zero Hour(tm)
|
||
|
** Copyright 2025 Electronic Arts Inc.
|
||
|
**
|
||
|
** This program 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 3 of the License, or
|
||
|
** (at your option) any later version.
|
||
|
**
|
||
|
** This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||
|
*/
|
||
|
|
||
|
#ifdef _WIN32
|
||
|
#include <process.h>
|
||
|
#endif
|
||
|
|
||
|
#include <wstring.h>
|
||
|
#include <tcp.h>
|
||
|
#include <wdebug.h>
|
||
|
#include "mydebug.h"
|
||
|
|
||
|
#include <ghttp/ghttp.h>
|
||
|
|
||
|
#include <cmath>
|
||
|
#include <cstdlib>
|
||
|
#include <algorithm>
|
||
|
#include <strstream>
|
||
|
|
||
|
#ifdef _UNIX
|
||
|
using namespace std;
|
||
|
#endif
|
||
|
|
||
|
/*
|
||
|
#ifdef IN
|
||
|
#undef IN
|
||
|
#endif
|
||
|
#define IN const
|
||
|
*/
|
||
|
#include "generals.h"
|
||
|
/*
|
||
|
#ifdef IN
|
||
|
#undef IN
|
||
|
#endif
|
||
|
#define IN const
|
||
|
*/
|
||
|
#include "global.h"
|
||
|
/*
|
||
|
#ifdef IN
|
||
|
#undef IN
|
||
|
#endif
|
||
|
#define IN const
|
||
|
*/
|
||
|
|
||
|
std::string intToString(int val)
|
||
|
{
|
||
|
std::string s = "";
|
||
|
bool neg = (val < 0);
|
||
|
if (val < 0)
|
||
|
{
|
||
|
val = -val;
|
||
|
}
|
||
|
if (val == 0)
|
||
|
return "0";
|
||
|
|
||
|
char buf[2];
|
||
|
buf[1] = 0;
|
||
|
while (val)
|
||
|
{
|
||
|
buf[0] = '0' + val%10;
|
||
|
val /= 10;
|
||
|
s.insert(0, buf);
|
||
|
}
|
||
|
if (neg)
|
||
|
s.insert(0, "-");
|
||
|
return s;
|
||
|
}
|
||
|
|
||
|
std::string uintToString(unsigned int val)
|
||
|
{
|
||
|
std::string s = "";
|
||
|
if (val == 0)
|
||
|
return "0";
|
||
|
|
||
|
char buf[2];
|
||
|
buf[1] = 0;
|
||
|
while (val)
|
||
|
{
|
||
|
buf[0] = '0' + val%10;
|
||
|
val /= 10;
|
||
|
s.insert(0, buf);
|
||
|
}
|
||
|
return s;
|
||
|
}
|
||
|
|
||
|
MapBitSet MapSetUnion(const MapBitSet& a, const MapBitSet& b)
|
||
|
{
|
||
|
MapBitSet c;
|
||
|
if (a.size() != b.size())
|
||
|
return c;
|
||
|
|
||
|
for (int i=0; i<(int)a.size(); ++i)
|
||
|
{
|
||
|
c.push_back(a[i] && b[i]);
|
||
|
}
|
||
|
|
||
|
return c;
|
||
|
}
|
||
|
|
||
|
int MapSetCount(const MapBitSet& a)
|
||
|
{
|
||
|
int count=0;
|
||
|
for (int i=0; i<(int)a.size(); ++i)
|
||
|
{
|
||
|
//DBGMSG(a[i]);
|
||
|
if (a[i])
|
||
|
++count;
|
||
|
}
|
||
|
return count;
|
||
|
}
|
||
|
|
||
|
// =====================================================================
|
||
|
// Users
|
||
|
// =====================================================================
|
||
|
|
||
|
GeneralsUser::GeneralsUser(void)
|
||
|
{
|
||
|
status = STATUS_INCHANNEL;
|
||
|
points = 1;
|
||
|
minPoints = maxPoints = 100;
|
||
|
country = color = -1;
|
||
|
pseudoPing.clear();
|
||
|
matchStart = time(NULL);
|
||
|
timeToWiden = 0;
|
||
|
widened = false;
|
||
|
numPlayers = 2;
|
||
|
discons = maxDiscons = 2;
|
||
|
maps.clear();
|
||
|
maxPing = 1000;
|
||
|
}
|
||
|
|
||
|
static const int MaxPingValue = 255*255*2;
|
||
|
|
||
|
int calcPingDelta(const GeneralsUser *a, const GeneralsUser *b)
|
||
|
{
|
||
|
if (!a || !b || a->pseudoPing.size() != b->pseudoPing.size())
|
||
|
return MaxPingValue; // Max ping
|
||
|
|
||
|
int bestPing = MaxPingValue;
|
||
|
for (int i=0; i<(int)a->pseudoPing.size(); ++i)
|
||
|
{
|
||
|
int p1, p2;
|
||
|
p1 = a->pseudoPing[i];
|
||
|
p2 = b->pseudoPing[i];
|
||
|
|
||
|
if (p1 * p1 + p2 * p2 < bestPing)
|
||
|
bestPing = p1 * p1 + p2 * p2;
|
||
|
}
|
||
|
|
||
|
return (int)sqrt(bestPing);
|
||
|
}
|
||
|
|
||
|
|
||
|
// =====================================================================
|
||
|
// Matcher thread
|
||
|
// =====================================================================
|
||
|
|
||
|
GeneralsMatcher::GeneralsMatcher()
|
||
|
{
|
||
|
// Read some values from the config file
|
||
|
int quietTMP = 0;
|
||
|
Global.config.getInt("NOECHO", quietTMP);
|
||
|
if (quietTMP)
|
||
|
quiet = true;
|
||
|
else
|
||
|
quiet = false;
|
||
|
|
||
|
// Grab the weights for different parameters
|
||
|
Global.config.getInt("MATCH_WEIGHT_LOWPING", weightLowPing, "GENERALS");
|
||
|
Global.config.getInt("MATCH_WEIGHT_AVGPOINTS", weightAvgPoints, "GENERALS");
|
||
|
totalWeight = weightLowPing + weightAvgPoints;
|
||
|
|
||
|
INFMSG("weightLowPing = " << weightLowPing);
|
||
|
INFMSG("weightAvgPoints = " << weightAvgPoints);
|
||
|
INFMSG("totalWeight = " << totalWeight);
|
||
|
|
||
|
Global.config.getInt("SecondsBetweenPoolSizeAnnouncements", m_secondsBetweenPoolSizeAnnouncements, NULL);
|
||
|
if (m_secondsBetweenPoolSizeAnnouncements < 10)
|
||
|
{
|
||
|
m_secondsBetweenPoolSizeAnnouncements = 10;
|
||
|
}
|
||
|
m_nextPoolSizeAnnouncement = time(NULL);
|
||
|
}
|
||
|
|
||
|
void GeneralsMatcher::init(void)
|
||
|
{}
|
||
|
|
||
|
|
||
|
#define W(x) setw(x) <<
|
||
|
void GeneralsMatcher::dumpUsers(void)
|
||
|
{}
|
||
|
|
||
|
|
||
|
|
||
|
void GeneralsMatcher::sendMatchInfo(std::string name1, std::string name2, std::string name3, std::string name4,
|
||
|
std::string name5, std::string name6, std::string name7, std::string name8,
|
||
|
GeneralsUser *user1, GeneralsUser *user2, GeneralsUser *user3, GeneralsUser *user4,
|
||
|
GeneralsUser *user5, GeneralsUser *user6, GeneralsUser *user7, GeneralsUser *user8,
|
||
|
int numPlayers, int ladderID)
|
||
|
{
|
||
|
MapBitSet tmp = MapSetUnion(user1->maps, user2->maps);
|
||
|
if (numPlayers > 2)
|
||
|
{
|
||
|
tmp = MapSetUnion(tmp, user3->maps);
|
||
|
tmp = MapSetUnion(tmp, user4->maps);
|
||
|
}
|
||
|
if (numPlayers > 4)
|
||
|
{
|
||
|
tmp = MapSetUnion(tmp, user5->maps);
|
||
|
tmp = MapSetUnion(tmp, user6->maps);
|
||
|
}
|
||
|
if (numPlayers > 6)
|
||
|
{
|
||
|
tmp = MapSetUnion(tmp, user7->maps);
|
||
|
tmp = MapSetUnion(tmp, user8->maps);
|
||
|
}
|
||
|
|
||
|
int numMaps = MapSetCount(tmp);
|
||
|
|
||
|
if (!numMaps)
|
||
|
{
|
||
|
DBGMSG("No maps!");
|
||
|
user1->status = STATUS_WORKING;
|
||
|
user2->status = STATUS_WORKING;
|
||
|
if (numPlayers > 2)
|
||
|
{
|
||
|
user3->status = STATUS_WORKING;
|
||
|
user4->status = STATUS_WORKING;
|
||
|
}
|
||
|
if (numPlayers > 4)
|
||
|
{
|
||
|
user5->status = STATUS_WORKING;
|
||
|
user6->status = STATUS_WORKING;
|
||
|
}
|
||
|
if (numPlayers > 6)
|
||
|
{
|
||
|
user7->status = STATUS_WORKING;
|
||
|
user8->status = STATUS_WORKING;
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
user1->status = STATUS_MATCHED;
|
||
|
user2->status = STATUS_MATCHED;
|
||
|
if (numPlayers > 2)
|
||
|
{
|
||
|
user3->status = STATUS_MATCHED;
|
||
|
user4->status = STATUS_MATCHED;
|
||
|
}
|
||
|
if (numPlayers > 4)
|
||
|
{
|
||
|
user5->status = STATUS_MATCHED;
|
||
|
user6->status = STATUS_MATCHED;
|
||
|
}
|
||
|
if (numPlayers > 6)
|
||
|
{
|
||
|
user7->status = STATUS_MATCHED;
|
||
|
user8->status = STATUS_MATCHED;
|
||
|
}
|
||
|
|
||
|
int whichMap = Global.rnd.Int(0, RAND_MAX-1);
|
||
|
DBGMSG(whichMap);
|
||
|
whichMap = whichMap%numMaps;
|
||
|
DBGMSG(whichMap);
|
||
|
++whichMap;
|
||
|
DBGMSG(whichMap);
|
||
|
DBGMSG("Random map #" << whichMap << "/" << numMaps);
|
||
|
|
||
|
int i;
|
||
|
for (i=0; i<(int)user1->maps.size(); ++i)
|
||
|
{
|
||
|
if (tmp[i])
|
||
|
--whichMap;
|
||
|
if (whichMap == 0)
|
||
|
break;
|
||
|
}
|
||
|
DBGMSG("Playing on map in pos " << i);
|
||
|
|
||
|
std::string s;
|
||
|
s = "MBOT:MATCHED ";
|
||
|
s.append(intToString(i));
|
||
|
s.append(" ");
|
||
|
s.append(intToString( Global.rnd.Int(0, RAND_MAX-1) ));
|
||
|
s.append(" ");
|
||
|
s.append(name1);
|
||
|
s.append(" ");
|
||
|
s.append(uintToString(user1->IP));
|
||
|
s.append(" ");
|
||
|
s.append(intToString(user1->country));
|
||
|
s.append(" ");
|
||
|
s.append(intToString(user1->color));
|
||
|
s.append(" ");
|
||
|
s.append(intToString(user1->NAT));
|
||
|
s.append(" ");
|
||
|
s.append(name2);
|
||
|
s.append(" ");
|
||
|
s.append(uintToString(user2->IP));
|
||
|
s.append(" ");
|
||
|
s.append(intToString(user2->country));
|
||
|
s.append(" ");
|
||
|
s.append(intToString(user2->color));
|
||
|
s.append(" ");
|
||
|
s.append(intToString(user2->NAT));
|
||
|
if (user3)
|
||
|
{
|
||
|
s.append(" ");
|
||
|
s.append(name3);
|
||
|
s.append(" ");
|
||
|
s.append(uintToString(user3->IP));
|
||
|
s.append(" ");
|
||
|
s.append(intToString(user3->country));
|
||
|
s.append(" ");
|
||
|
s.append(intToString(user3->color));
|
||
|
s.append(" ");
|
||
|
s.append(intToString(user3->NAT));
|
||
|
}
|
||
|
if (user4)
|
||
|
{
|
||
|
s.append(" ");
|
||
|
s.append(name4);
|
||
|
s.append(" ");
|
||
|
s.append(uintToString(user4->IP));
|
||
|
s.append(" ");
|
||
|
s.append(intToString(user4->country));
|
||
|
s.append(" ");
|
||
|
s.append(intToString(user4->color));
|
||
|
s.append(" ");
|
||
|
s.append(intToString(user4->NAT));
|
||
|
}
|
||
|
if (user5)
|
||
|
{
|
||
|
s.append(" ");
|
||
|
s.append(name5);
|
||
|
s.append(" ");
|
||
|
s.append(uintToString(user5->IP));
|
||
|
s.append(" ");
|
||
|
s.append(intToString(user5->country));
|
||
|
s.append(" ");
|
||
|
s.append(intToString(user5->color));
|
||
|
s.append(" ");
|
||
|
s.append(intToString(user5->NAT));
|
||
|
}
|
||
|
if (user6)
|
||
|
{
|
||
|
s.append(" ");
|
||
|
s.append(name6);
|
||
|
s.append(" ");
|
||
|
s.append(uintToString(user6->IP));
|
||
|
s.append(" ");
|
||
|
s.append(intToString(user6->country));
|
||
|
s.append(" ");
|
||
|
s.append(intToString(user6->color));
|
||
|
s.append(" ");
|
||
|
s.append(intToString(user6->NAT));
|
||
|
}
|
||
|
if (user7)
|
||
|
{
|
||
|
s.append(" ");
|
||
|
s.append(name7);
|
||
|
s.append(" ");
|
||
|
s.append(uintToString(user7->IP));
|
||
|
s.append(" ");
|
||
|
s.append(intToString(user7->country));
|
||
|
s.append(" ");
|
||
|
s.append(intToString(user7->color));
|
||
|
s.append(" ");
|
||
|
s.append(intToString(user7->NAT));
|
||
|
}
|
||
|
if (user8)
|
||
|
{
|
||
|
s.append(" ");
|
||
|
s.append(name8);
|
||
|
s.append(" ");
|
||
|
s.append(uintToString(user8->IP));
|
||
|
s.append(" ");
|
||
|
s.append(intToString(user8->country));
|
||
|
s.append(" ");
|
||
|
s.append(intToString(user8->color));
|
||
|
s.append(" ");
|
||
|
s.append(intToString(user8->NAT));
|
||
|
}
|
||
|
|
||
|
std::string n;
|
||
|
n = name1;
|
||
|
n.append(",");
|
||
|
n.append(name2);
|
||
|
if (user3)
|
||
|
{
|
||
|
n.append(",");
|
||
|
n.append(name3);
|
||
|
}
|
||
|
if (user4)
|
||
|
{
|
||
|
n.append(",");
|
||
|
n.append(name4);
|
||
|
}
|
||
|
if (user5)
|
||
|
{
|
||
|
n.append(",");
|
||
|
n.append(name5);
|
||
|
}
|
||
|
if (user6)
|
||
|
{
|
||
|
n.append(",");
|
||
|
n.append(name6);
|
||
|
}
|
||
|
if (user7)
|
||
|
{
|
||
|
n.append(",");
|
||
|
n.append(name7);
|
||
|
}
|
||
|
if (user8)
|
||
|
{
|
||
|
n.append(",");
|
||
|
n.append(name8);
|
||
|
}
|
||
|
peerMessagePlayer(m_peer, n.c_str(), s.c_str(), NormalMessage);
|
||
|
}
|
||
|
|
||
|
void GeneralsMatcher::checkMatches(void)
|
||
|
{
|
||
|
bool showPoolSize = false;
|
||
|
time_t now = time(NULL);
|
||
|
if (now > m_nextPoolSizeAnnouncement)
|
||
|
{
|
||
|
m_nextPoolSizeAnnouncement = now + m_secondsBetweenPoolSizeAnnouncements;
|
||
|
showPoolSize = true;
|
||
|
}
|
||
|
checkMatchesInUserMap(m_nonLadderUsers1v1, 0, 2, showPoolSize);
|
||
|
checkMatchesInUserMap(m_nonLadderUsers2v2, 0, 4, showPoolSize);
|
||
|
checkMatchesInUserMap(m_nonLadderUsers3v3, 0, 6, showPoolSize);
|
||
|
checkMatchesInUserMap(m_nonLadderUsers4v4, 0, 8, showPoolSize);
|
||
|
|
||
|
for (LadderMap::iterator it = m_ladders.begin(); it != m_ladders.end(); ++it)
|
||
|
{
|
||
|
checkMatchesInUserMap(it->second, it->first, 2, showPoolSize);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
double GeneralsMatcher::computeMatchFitness(const std::string& i1, const GeneralsUser *u1, const std::string& i2, const GeneralsUser *u2)
|
||
|
{
|
||
|
//DBGMSG("matching "<<i1<< " vs "<<i2);
|
||
|
if (u1->status != STATUS_WORKING || u2->status != STATUS_WORKING)
|
||
|
return 0.0;
|
||
|
|
||
|
// see if they pinged the same # of servers (sanity).
|
||
|
if (u1->pseudoPing.size() != u2->pseudoPing.size())
|
||
|
return 0.0;
|
||
|
|
||
|
// check point percentage ranges
|
||
|
int p1 = max(1,u1->points), p2 = max(1,u2->points);
|
||
|
double p1percent = (double)p2/(double)p1;
|
||
|
double p2percent = (double)p1/(double)p2;
|
||
|
//DBGMSG("points: " << p1 << "," << p2 << " - " << p1percent << "," << p2percent);
|
||
|
if (!u1->widened && ( p1percent < u1->minPoints || p1percent > u1->maxPoints ))
|
||
|
return 0.0;
|
||
|
|
||
|
if (!u2->widened && ( p2percent < u2->minPoints || p2percent > u2->maxPoints ))
|
||
|
return 0.0;
|
||
|
|
||
|
|
||
|
int minP = min(p1, p2);
|
||
|
int maxP = max(p1, p2);
|
||
|
double pointPercent = (double)minP/(double)maxP;
|
||
|
//DBGMSG("\tpointPercent = "<<pointPercent);
|
||
|
|
||
|
// check pings
|
||
|
int pingDelta = calcPingDelta(u1, u2);
|
||
|
if (!u1->widened && pingDelta > u1->maxPing)
|
||
|
return 0.0;
|
||
|
if (!u2->widened && pingDelta > u2->maxPing)
|
||
|
return 0.0;
|
||
|
//DBGMSG("pingDelta="<<pingDelta);
|
||
|
|
||
|
//DBGMSG(u1->discons << "," << u1->maxDiscons << " " << u2->discons << "," << u2->maxDiscons);
|
||
|
// check discons
|
||
|
if (u1->maxDiscons && (!u1->widened && u2->discons > u1->maxDiscons))
|
||
|
return 0.0;
|
||
|
if (u2->maxDiscons && (!u2->widened && u1->discons > u2->maxDiscons))
|
||
|
return 0.0;
|
||
|
//DBGMSG("Made it through discons");
|
||
|
|
||
|
{
|
||
|
MapBitSet tmp = MapSetUnion(u1->maps, u2->maps);
|
||
|
if (!MapSetCount(tmp))
|
||
|
return 0.0;
|
||
|
}
|
||
|
|
||
|
// they have something in common. calculate match fitness.
|
||
|
double matchFitness = ( weightAvgPoints * (1-pointPercent) +
|
||
|
weightLowPing * (MaxPingValue - pingDelta)/MaxPingValue ) / (double)totalWeight;
|
||
|
//DBGMSG("Match fitness: "<<matchFitness);
|
||
|
|
||
|
/*
|
||
|
DBGMSG(i1->first << " vs " << i2->first << " has fitness " << matchFitness << "\n"
|
||
|
"\tpointPercent: " << pointPercent << "\n"
|
||
|
"\tpingDelta: " << pingDelta << "\n"
|
||
|
"\twidened: " << u1->widened << u2->widened << "\n"
|
||
|
"\tweightAvgPoints: " << weightAvgPoints << "\n"
|
||
|
"\tweightLowPing: " << weightLowPing << "\n"
|
||
|
"\ttotalWeight: " << totalWeight
|
||
|
);
|
||
|
*/
|
||
|
|
||
|
return matchFitness;
|
||
|
}
|
||
|
|
||
|
void GeneralsMatcher::checkMatchesInUserMap(UserMap& userMap, int ladderID, int numPlayers, bool showPoolSize)
|
||
|
{
|
||
|
UserMap::iterator i1, i2, i3, i4, i5, i6, i7, i8;
|
||
|
GeneralsUser *u1, *u2, *u3, *u4, *u5, *u6, *u7, *u8;
|
||
|
static const double fitnessThreshold = 0.3;
|
||
|
time_t now = time(NULL);
|
||
|
|
||
|
std::string s;
|
||
|
if (showPoolSize)
|
||
|
{
|
||
|
s = "MBOT:POOLSIZE ";
|
||
|
s.append(intToString(userMap.size()));
|
||
|
}
|
||
|
|
||
|
// iterate through users, timing them out as neccessary
|
||
|
for (i1 = userMap.begin(); i1 != userMap.end(); ++i1)
|
||
|
{
|
||
|
if (showPoolSize)
|
||
|
{
|
||
|
peerMessagePlayer(m_peer, i1->first.c_str(), s.c_str(), NormalMessage);
|
||
|
}
|
||
|
|
||
|
u1 = i1->second;
|
||
|
if (u1->timeToWiden && u1->timeToWiden < now)
|
||
|
{
|
||
|
u1->timeToWiden = 0;
|
||
|
u1->widened = true;
|
||
|
for (int m=0; m<(int)u1->maps.size(); ++m)
|
||
|
u1->maps[m] = 1;
|
||
|
DBGMSG("Widening search for " << i1->first);
|
||
|
peerMessagePlayer(m_peer, i1->first.c_str(), "MBOT:WIDENINGSEARCH", NormalMessage);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// iterate through all users, looking for a match
|
||
|
for (i1 = userMap.begin(); i1 != userMap.end(); ++i1)
|
||
|
{
|
||
|
u1 = i1->second;
|
||
|
if (u1->status != STATUS_WORKING)
|
||
|
continue;
|
||
|
|
||
|
GeneralsUser *bestUser = NULL;
|
||
|
double bestMatchFitness = 0.0;
|
||
|
std::string bestName = "";
|
||
|
|
||
|
// look at everybody left
|
||
|
i2 = i1;
|
||
|
for (++i2; i2 != userMap.end(); ++i2)
|
||
|
{
|
||
|
u2 = i2->second;
|
||
|
if (u2->status != STATUS_WORKING)
|
||
|
continue;
|
||
|
|
||
|
double matchFitness = computeMatchFitness(i1->first, u1, i2->first, u2);
|
||
|
if (matchFitness > fitnessThreshold)
|
||
|
{
|
||
|
if (numPlayers == 2)
|
||
|
{
|
||
|
if (matchFitness > bestMatchFitness)
|
||
|
{
|
||
|
// possibly match 2 players
|
||
|
bestMatchFitness = matchFitness;
|
||
|
bestUser = u2;
|
||
|
bestName = i2->first;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
i3 = i2;
|
||
|
for (++i3; i3 != userMap.end(); ++i3)
|
||
|
{
|
||
|
u3 = i3->second;
|
||
|
if (u3->status != STATUS_WORKING)
|
||
|
continue;
|
||
|
|
||
|
double matchFitness1 = computeMatchFitness(i1->first, u1, i3->first, u3);
|
||
|
double matchFitness2 = computeMatchFitness(i2->first, u2, i3->first, u3);
|
||
|
MapBitSet tmp = MapSetUnion(u1->maps, u2->maps);
|
||
|
tmp = MapSetUnion(tmp, u3->maps);
|
||
|
if (MapSetCount(tmp) && matchFitness1 > fitnessThreshold && matchFitness2 > fitnessThreshold)
|
||
|
{
|
||
|
i4 = i3;
|
||
|
for (++i4; i4 != userMap.end(); ++i4)
|
||
|
{
|
||
|
u4 = i4->second;
|
||
|
if (u4->status != STATUS_WORKING)
|
||
|
continue;
|
||
|
|
||
|
double matchFitness1 = computeMatchFitness(i1->first, u1, i4->first, u4);
|
||
|
double matchFitness2 = computeMatchFitness(i2->first, u2, i4->first, u4);
|
||
|
double matchFitness3 = computeMatchFitness(i3->first, u3, i4->first, u4);
|
||
|
MapBitSet tmp = MapSetUnion(u1->maps, u2->maps);
|
||
|
tmp = MapSetUnion(tmp, u3->maps);
|
||
|
tmp = MapSetUnion(tmp, u4->maps);
|
||
|
if (MapSetCount(tmp) && matchFitness1 > fitnessThreshold && matchFitness2 > fitnessThreshold && matchFitness3 > fitnessThreshold)
|
||
|
{
|
||
|
if (numPlayers == 4)
|
||
|
{
|
||
|
// match 4 players
|
||
|
sendMatchInfo(i1->first, i2->first, i3->first, i4->first, "", "", "", "",
|
||
|
u1, u2, u3, u4, NULL, NULL, NULL, NULL, 4, ladderID);
|
||
|
break;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
|
||
|
i5 = i4;
|
||
|
for (++i5; i5 != userMap.end(); ++i3)
|
||
|
{
|
||
|
u5 = i5->second;
|
||
|
if (u5->status != STATUS_WORKING)
|
||
|
continue;
|
||
|
|
||
|
double matchFitness1 = computeMatchFitness(i1->first, u1, i5->first, u5);
|
||
|
double matchFitness2 = computeMatchFitness(i2->first, u2, i5->first, u5);
|
||
|
double matchFitness3 = computeMatchFitness(i3->first, u3, i5->first, u5);
|
||
|
double matchFitness4 = computeMatchFitness(i4->first, u4, i5->first, u5);
|
||
|
MapBitSet tmp = MapSetUnion(u1->maps, u2->maps);
|
||
|
tmp = MapSetUnion(tmp, u3->maps);
|
||
|
tmp = MapSetUnion(tmp, u4->maps);
|
||
|
tmp = MapSetUnion(tmp, u5->maps);
|
||
|
if (MapSetCount(tmp) && matchFitness1 > fitnessThreshold && matchFitness2 > fitnessThreshold && matchFitness3 > fitnessThreshold && matchFitness4 > fitnessThreshold)
|
||
|
{
|
||
|
i6 = i5;
|
||
|
for (++i6; i6 != userMap.end(); ++i6)
|
||
|
{
|
||
|
u6 = i6->second;
|
||
|
if (u6->status != STATUS_WORKING)
|
||
|
continue;
|
||
|
|
||
|
double matchFitness1 = computeMatchFitness(i1->first, u1, i6->first, u6);
|
||
|
double matchFitness2 = computeMatchFitness(i2->first, u2, i6->first, u6);
|
||
|
double matchFitness3 = computeMatchFitness(i3->first, u3, i6->first, u6);
|
||
|
double matchFitness4 = computeMatchFitness(i4->first, u4, i6->first, u6);
|
||
|
double matchFitness5 = computeMatchFitness(i5->first, u5, i6->first, u6);
|
||
|
MapBitSet tmp = MapSetUnion(u1->maps, u2->maps);
|
||
|
tmp = MapSetUnion(tmp, u3->maps);
|
||
|
tmp = MapSetUnion(tmp, u4->maps);
|
||
|
tmp = MapSetUnion(tmp, u5->maps);
|
||
|
tmp = MapSetUnion(tmp, u6->maps);
|
||
|
if (MapSetCount(tmp) && matchFitness1 > fitnessThreshold && matchFitness2 > fitnessThreshold && matchFitness3 > fitnessThreshold && matchFitness4 > fitnessThreshold && matchFitness5 > fitnessThreshold)
|
||
|
{
|
||
|
if (numPlayers == 6)
|
||
|
{
|
||
|
// match 6 players
|
||
|
sendMatchInfo(i1->first, i2->first, i3->first, i4->first, i5->first, i6->first, "", "",
|
||
|
u1, u2, u3, u4, u5, u6, NULL, NULL, 6, ladderID);
|
||
|
break;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
|
||
|
i7 = i6;
|
||
|
for (++i7; i7 != userMap.end(); ++i7)
|
||
|
{
|
||
|
u7 = i7->second;
|
||
|
if (u7->status != STATUS_WORKING)
|
||
|
continue;
|
||
|
|
||
|
double matchFitness1 = computeMatchFitness(i1->first, u1, i7->first, u7);
|
||
|
double matchFitness2 = computeMatchFitness(i2->first, u2, i7->first, u7);
|
||
|
double matchFitness3 = computeMatchFitness(i3->first, u3, i7->first, u7);
|
||
|
double matchFitness4 = computeMatchFitness(i4->first, u4, i7->first, u7);
|
||
|
double matchFitness5 = computeMatchFitness(i5->first, u5, i7->first, u7);
|
||
|
double matchFitness6 = computeMatchFitness(i6->first, u6, i7->first, u7);
|
||
|
MapBitSet tmp = MapSetUnion(u1->maps, u2->maps);
|
||
|
tmp = MapSetUnion(tmp, u3->maps);
|
||
|
tmp = MapSetUnion(tmp, u4->maps);
|
||
|
tmp = MapSetUnion(tmp, u5->maps);
|
||
|
tmp = MapSetUnion(tmp, u6->maps);
|
||
|
tmp = MapSetUnion(tmp, u7->maps);
|
||
|
if (MapSetCount(tmp) && matchFitness1 > fitnessThreshold && matchFitness2 > fitnessThreshold && matchFitness3 > fitnessThreshold && matchFitness4 > fitnessThreshold && matchFitness5 > fitnessThreshold && matchFitness6 > fitnessThreshold)
|
||
|
{
|
||
|
i8 = i7;
|
||
|
for (++i8; i8 != userMap.end(); ++i8)
|
||
|
{
|
||
|
u8 = i8->second;
|
||
|
if (u8->status != STATUS_WORKING)
|
||
|
continue;
|
||
|
|
||
|
double matchFitness1 = computeMatchFitness(i1->first, u1, i8->first, u8);
|
||
|
double matchFitness2 = computeMatchFitness(i2->first, u2, i8->first, u8);
|
||
|
double matchFitness3 = computeMatchFitness(i3->first, u3, i8->first, u8);
|
||
|
double matchFitness4 = computeMatchFitness(i4->first, u4, i8->first, u8);
|
||
|
double matchFitness5 = computeMatchFitness(i5->first, u5, i8->first, u8);
|
||
|
double matchFitness6 = computeMatchFitness(i6->first, u6, i8->first, u8);
|
||
|
double matchFitness7 = computeMatchFitness(i7->first, u7, i8->first, u8);
|
||
|
MapBitSet tmp = MapSetUnion(u1->maps, u2->maps);
|
||
|
tmp = MapSetUnion(tmp, u3->maps);
|
||
|
tmp = MapSetUnion(tmp, u4->maps);
|
||
|
tmp = MapSetUnion(tmp, u5->maps);
|
||
|
tmp = MapSetUnion(tmp, u6->maps);
|
||
|
tmp = MapSetUnion(tmp, u7->maps);
|
||
|
tmp = MapSetUnion(tmp, u8->maps);
|
||
|
if (MapSetCount(tmp) && matchFitness1 > fitnessThreshold && matchFitness2 > fitnessThreshold && matchFitness3 > fitnessThreshold && matchFitness4 > fitnessThreshold && matchFitness5 > fitnessThreshold && matchFitness6 > fitnessThreshold && matchFitness7 > fitnessThreshold)
|
||
|
{
|
||
|
// match 8 players
|
||
|
sendMatchInfo(i1->first, i2->first, i3->first, i4->first, i5->first, i6->first, i7->first, i8->first,
|
||
|
u1, u2, u3, u4, u5, u6, u7, u8, 8, ladderID);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
} // for i2
|
||
|
|
||
|
if (bestUser && numPlayers == 2)
|
||
|
{
|
||
|
// we had a match. send the info.
|
||
|
DBGMSG("Matching " << i1->first << " with " << bestName << ":\n"
|
||
|
"\tmatch fitness: " << bestMatchFitness << "\n"
|
||
|
"\tpoint percentage: " << (1-bestUser->points/(double)u1->points)*100 << "\n"
|
||
|
"\tpoints: " << u1->points << ", " << u2->points << "\n"
|
||
|
"\tping in ms: " << sqrt(1000000 * calcPingDelta(u1, bestUser) / (255*255*2)) << "\n"
|
||
|
"\tprevious attempts: " << u1->widened << ", " << bestUser->widened);
|
||
|
sendMatchInfo(i1->first, bestName, "", "", "", "", "", "",
|
||
|
u1, bestUser, NULL, NULL, NULL, NULL, NULL, NULL, 2, ladderID);
|
||
|
break;
|
||
|
}
|
||
|
} // for i1
|
||
|
|
||
|
dumpUsers();
|
||
|
}
|
||
|
|
||
|
// return false for possible hack attempt
|
||
|
bool GeneralsMatcher::handleUserWiden(const char *nick)
|
||
|
{
|
||
|
GeneralsUser *userInfo = findUserInAnyLadder(nick);
|
||
|
if (!userInfo)
|
||
|
{
|
||
|
userInfo = findNonLadderUser(nick);
|
||
|
}
|
||
|
|
||
|
if (!userInfo)
|
||
|
{
|
||
|
DBGMSG("Got Widen from nick not needing one!");
|
||
|
peerMessagePlayer(m_peer, nick, "MBOT:CANTSENDWIDENNOW", NormalMessage);
|
||
|
return false;
|
||
|
}
|
||
|
DBGMSG("Widening search for " << nick);
|
||
|
peerMessagePlayer(m_peer, nick, "MBOT:WIDENINGSEARCH", NormalMessage);
|
||
|
|
||
|
userInfo->widened = true;
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool GeneralsMatcher::handleUserInfo(const char *nick, const std::string& msg)
|
||
|
{
|
||
|
DBGMSG("Got user info [" << msg << "] from " << nick);
|
||
|
GeneralsUser *userInfo = removeNonMatchingUser(nick);
|
||
|
if (!userInfo)
|
||
|
{
|
||
|
DBGMSG("Got UserInfo from nick not needing one!");
|
||
|
peerMessagePlayer(m_peer, nick, "MBOT:CANTSENDCINFONOW", NormalMessage);
|
||
|
return false;
|
||
|
}
|
||
|
DBGMSG("Looking at " << nick << " with user info [" << msg << "]");
|
||
|
|
||
|
int ladderID = 0;
|
||
|
unsigned int ladderPassCRC = 0;
|
||
|
|
||
|
int offset = 0;
|
||
|
while (1)
|
||
|
{
|
||
|
int firstMarker = msg.find_first_of('\\', offset);
|
||
|
if (firstMarker < 0)
|
||
|
break;
|
||
|
int secondMarker = msg.find_first_of('\\', firstMarker + 1);
|
||
|
if (secondMarker < 0)
|
||
|
break;
|
||
|
int thirdMarker = msg.find_first_of('\\', secondMarker + 1);
|
||
|
if (thirdMarker < 0)
|
||
|
break;
|
||
|
std::string k = msg.substr(firstMarker + 1, secondMarker - firstMarker - 1);
|
||
|
std::string v = msg.substr(secondMarker + 1, thirdMarker - secondMarker - 1);
|
||
|
offset = thirdMarker - 1;
|
||
|
|
||
|
if (k == "Widen")
|
||
|
{
|
||
|
int val = atoi(v.c_str());
|
||
|
if (val > 0)
|
||
|
userInfo->timeToWiden = time(NULL) + val;
|
||
|
else
|
||
|
userInfo->timeToWiden = 0;
|
||
|
}
|
||
|
else if (k == "LadID")
|
||
|
{
|
||
|
ladderID = atoi(v.c_str());
|
||
|
}
|
||
|
else if (k == "LadPass")
|
||
|
{
|
||
|
ladderPassCRC = atoi(v.c_str());
|
||
|
}
|
||
|
else if (k == "PointsMin")
|
||
|
{
|
||
|
userInfo->minPoints = atoi(v.c_str());
|
||
|
}
|
||
|
else if (k == "PointsMax")
|
||
|
{
|
||
|
userInfo->maxPoints = atoi(v.c_str());
|
||
|
}
|
||
|
else if (k == "PingMax")
|
||
|
{
|
||
|
userInfo->maxPing = atoi(v.c_str());
|
||
|
}
|
||
|
else if (k == "DisconMax")
|
||
|
{
|
||
|
userInfo->maxDiscons = atoi(v.c_str());
|
||
|
}
|
||
|
else if (k == "Maps")
|
||
|
{
|
||
|
#ifdef DEBUG
|
||
|
//int curMaps = userInfo->maps.size();
|
||
|
#endif
|
||
|
|
||
|
//DBGMSG("map cur size is " << curMaps);
|
||
|
userInfo->maps.clear();
|
||
|
if (!v.length())
|
||
|
{
|
||
|
INFMSG("Bad maps from " << nick << ": [" << v << "]");
|
||
|
peerMessagePlayer(m_peer, nick, "MBOT:BADMAPS", NormalMessage);
|
||
|
return false;
|
||
|
}
|
||
|
const char *buf = v.c_str();
|
||
|
int pos = 0;
|
||
|
while (*buf)
|
||
|
{
|
||
|
bool hasMap = (*buf != '0');
|
||
|
//DBGMSG("Setting map " << pos << " to " << hasMap);
|
||
|
userInfo->maps.push_back( hasMap );
|
||
|
++pos;
|
||
|
++buf;
|
||
|
}
|
||
|
}
|
||
|
else if (k == "NumPlayers")
|
||
|
{
|
||
|
userInfo->numPlayers = atoi(v.c_str());
|
||
|
if (userInfo->numPlayers != 2 && userInfo->numPlayers != 4 &&
|
||
|
userInfo->numPlayers != 6 && userInfo->numPlayers != 8)
|
||
|
{
|
||
|
INFMSG("Bad numPlayers from " << nick << ": [" << userInfo->numPlayers << "]");
|
||
|
peerMessagePlayer(m_peer, nick, "MBOT:BADCINFO", NormalMessage);
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
else if (k == "IP")
|
||
|
{
|
||
|
userInfo->IP = atoi(v.c_str());
|
||
|
}
|
||
|
else if (k == "NAT")
|
||
|
{
|
||
|
userInfo->NAT = atoi(v.c_str());
|
||
|
}
|
||
|
else if (k == "Side")
|
||
|
{
|
||
|
userInfo->country = atoi(v.c_str());
|
||
|
}
|
||
|
else if (k == "Color")
|
||
|
{
|
||
|
userInfo->color = atoi(v.c_str());
|
||
|
}
|
||
|
else if (k == "Pings")
|
||
|
{
|
||
|
if (!v.length() || (v.length() % 2))
|
||
|
{
|
||
|
INFMSG("Bad pings from " << nick << ": [" << v << "]");
|
||
|
peerMessagePlayer(m_peer, nick, "MBOT:BADPINGS", NormalMessage);
|
||
|
return false;
|
||
|
}
|
||
|
int ping = 0;
|
||
|
const char *buf = v.c_str();
|
||
|
char buf2[3];
|
||
|
buf2[2] = '\0';
|
||
|
// We've already assured that pingStr has non-zero even length.
|
||
|
while (*buf)
|
||
|
{
|
||
|
buf2[0] = *buf++;
|
||
|
buf2[1] = *buf++;
|
||
|
ping = (int)strtol(buf2, NULL, 16);
|
||
|
userInfo->pseudoPing.push_back(ping);
|
||
|
}
|
||
|
}
|
||
|
else if (k == "Points")
|
||
|
{
|
||
|
userInfo->points = max(1, atoi(v.c_str()));
|
||
|
}
|
||
|
else if (k == "Discons")
|
||
|
{
|
||
|
userInfo->discons = atoi(v.c_str());
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
INFMSG("Unknown key/value pair in user info [" << k << "]/[" << v << "]");
|
||
|
peerMessagePlayer(m_peer, nick, "MBOT:BADCINFO", NormalMessage);
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
std::string s = "MBOT:WORKING ";
|
||
|
|
||
|
if (ladderID)
|
||
|
{
|
||
|
addUserInLadder(nick, ladderID, userInfo);
|
||
|
s.append(intToString(m_ladders[ladderID].size()));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
addNonLadderUser(nick, userInfo);
|
||
|
switch (userInfo->numPlayers)
|
||
|
{
|
||
|
case 2:
|
||
|
s.append(intToString(m_nonLadderUsers1v1.size()));
|
||
|
break;
|
||
|
case 4:
|
||
|
s.append(intToString(m_nonLadderUsers2v2.size()));
|
||
|
break;
|
||
|
case 6:
|
||
|
s.append(intToString(m_nonLadderUsers3v3.size()));
|
||
|
break;
|
||
|
case 8:
|
||
|
s.append(intToString(m_nonLadderUsers4v4.size()));
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
userInfo->status = STATUS_WORKING;
|
||
|
userInfo->matchStart = time(NULL);
|
||
|
peerMessagePlayer(m_peer, nick, s.c_str(), NormalMessage);
|
||
|
|
||
|
DBGMSG("Player " << nick << " is matching now, ack was [" << s << "]");
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
GeneralsUser* GeneralsMatcher::findUser(const std::string& who)
|
||
|
{
|
||
|
GeneralsUser *user;
|
||
|
user = findNonLadderUser(who);
|
||
|
if (user)
|
||
|
return user;
|
||
|
user = findNonMatchingUser(who);
|
||
|
if (user)
|
||
|
return user;
|
||
|
user = findUserInAnyLadder(who);
|
||
|
if (user)
|
||
|
return user;
|
||
|
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
GeneralsUser* GeneralsMatcher::findUserInAnyLadder(const std::string& who)
|
||
|
{
|
||
|
for (LadderMap::iterator lIt = m_ladders.begin(); lIt != m_ladders.end(); ++lIt)
|
||
|
{
|
||
|
UserMap::iterator uIt = lIt->second.find(who);
|
||
|
if (uIt != lIt->second.end())
|
||
|
return uIt->second;
|
||
|
}
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
GeneralsUser* GeneralsMatcher::findUserInLadder(const std::string& who, int ladderID)
|
||
|
{
|
||
|
LadderMap::iterator lIt = m_ladders.find(ladderID);
|
||
|
if (lIt == m_ladders.end())
|
||
|
return NULL;
|
||
|
|
||
|
UserMap::iterator uIt = lIt->second.find(who);
|
||
|
if (uIt == lIt->second.end())
|
||
|
return NULL;
|
||
|
|
||
|
return uIt->second;
|
||
|
}
|
||
|
|
||
|
GeneralsUser* GeneralsMatcher::findNonLadderUser(const std::string& who)
|
||
|
{
|
||
|
UserMap::iterator it = m_nonLadderUsers1v1.find(who);
|
||
|
if (it != m_nonLadderUsers1v1.end())
|
||
|
return it->second;
|
||
|
|
||
|
it = m_nonLadderUsers2v2.find(who);
|
||
|
if (it != m_nonLadderUsers2v2.end())
|
||
|
return it->second;
|
||
|
|
||
|
it = m_nonLadderUsers3v3.find(who);
|
||
|
if (it != m_nonLadderUsers3v3.end())
|
||
|
return it->second;
|
||
|
|
||
|
it = m_nonLadderUsers4v4.find(who);
|
||
|
if (it != m_nonLadderUsers4v4.end())
|
||
|
return it->second;
|
||
|
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
GeneralsUser* GeneralsMatcher::findNonMatchingUser(const std::string& who)
|
||
|
{
|
||
|
UserMap::iterator it = m_nonMatchingUsers.find(who);
|
||
|
if (it == m_nonMatchingUsers.end())
|
||
|
return NULL;
|
||
|
|
||
|
return it->second;
|
||
|
}
|
||
|
|
||
|
void GeneralsMatcher::addUser(const std::string& who)
|
||
|
{
|
||
|
if (findUser(who))
|
||
|
{
|
||
|
ERRMSG("Re-adding " << who);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
addNonMatchingUser(who, new GeneralsUser);
|
||
|
}
|
||
|
|
||
|
void GeneralsMatcher::addUserInLadder(const std::string& who, int ladderID, GeneralsUser *user)
|
||
|
{
|
||
|
m_ladders[ladderID][who] = user;
|
||
|
}
|
||
|
|
||
|
void GeneralsMatcher::addNonLadderUser(const std::string& who, GeneralsUser *user)
|
||
|
{
|
||
|
switch (user->numPlayers)
|
||
|
{
|
||
|
case 2:
|
||
|
m_nonLadderUsers1v1[who] = user;
|
||
|
break;
|
||
|
case 4:
|
||
|
m_nonLadderUsers2v2[who] = user;
|
||
|
break;
|
||
|
case 6:
|
||
|
m_nonLadderUsers3v3[who] = user;
|
||
|
break;
|
||
|
case 8:
|
||
|
m_nonLadderUsers4v4[who] = user;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void GeneralsMatcher::addNonMatchingUser(const std::string& who, GeneralsUser *user)
|
||
|
{
|
||
|
m_nonMatchingUsers[who] = user;
|
||
|
}
|
||
|
|
||
|
|
||
|
bool GeneralsMatcher::removeUser(const std::string& who)
|
||
|
{
|
||
|
GeneralsUser *user;
|
||
|
user = removeUserInAnyLadder(who);
|
||
|
if (user)
|
||
|
{
|
||
|
delete user;
|
||
|
return true;
|
||
|
}
|
||
|
user = removeNonLadderUser(who);
|
||
|
if (user)
|
||
|
{
|
||
|
delete user;
|
||
|
return true;
|
||
|
}
|
||
|
user = removeNonMatchingUser(who);
|
||
|
if (user)
|
||
|
{
|
||
|
delete user;
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
GeneralsUser* GeneralsMatcher::removeUserInLadder(const std::string& who, int ladderID)
|
||
|
{
|
||
|
LadderMap::iterator lIt = m_ladders.find(ladderID);
|
||
|
if (lIt == m_ladders.end())
|
||
|
return NULL;
|
||
|
|
||
|
UserMap::iterator uIt = lIt->second.find(who);
|
||
|
if (uIt == lIt->second.end())
|
||
|
return NULL;
|
||
|
|
||
|
GeneralsUser *user = uIt->second;
|
||
|
lIt->second.erase(uIt);
|
||
|
return user;
|
||
|
}
|
||
|
|
||
|
GeneralsUser* GeneralsMatcher::removeUserInAnyLadder(const std::string& who)
|
||
|
{
|
||
|
for (LadderMap::iterator lIt = m_ladders.begin(); lIt != m_ladders.end(); ++lIt)
|
||
|
{
|
||
|
UserMap::iterator uIt = lIt->second.find(who);
|
||
|
if (uIt != lIt->second.end())
|
||
|
{
|
||
|
GeneralsUser *user = uIt->second;
|
||
|
lIt->second.erase(uIt);
|
||
|
return user;
|
||
|
}
|
||
|
}
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
GeneralsUser* GeneralsMatcher::removeNonLadderUser(const std::string& who)
|
||
|
{
|
||
|
UserMap::iterator it = m_nonLadderUsers1v1.find(who);
|
||
|
if (it != m_nonLadderUsers1v1.end())
|
||
|
{
|
||
|
GeneralsUser *user = it->second;
|
||
|
m_nonLadderUsers1v1.erase(it);
|
||
|
return user;
|
||
|
}
|
||
|
|
||
|
it = m_nonLadderUsers2v2.find(who);
|
||
|
if (it != m_nonLadderUsers2v2.end())
|
||
|
{
|
||
|
GeneralsUser *user = it->second;
|
||
|
m_nonLadderUsers2v2.erase(it);
|
||
|
return user;
|
||
|
}
|
||
|
|
||
|
it = m_nonLadderUsers3v3.find(who);
|
||
|
if (it != m_nonLadderUsers3v3.end())
|
||
|
{
|
||
|
GeneralsUser *user = it->second;
|
||
|
m_nonLadderUsers3v3.erase(it);
|
||
|
return user;
|
||
|
}
|
||
|
|
||
|
it = m_nonLadderUsers4v4.find(who);
|
||
|
if (it != m_nonLadderUsers4v4.end())
|
||
|
{
|
||
|
GeneralsUser *user = it->second;
|
||
|
m_nonLadderUsers4v4.erase(it);
|
||
|
return user;
|
||
|
}
|
||
|
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
GeneralsUser* GeneralsMatcher::removeNonMatchingUser(const std::string& who)
|
||
|
{
|
||
|
UserMap::iterator it = m_nonMatchingUsers.find(who);
|
||
|
if (it == m_nonMatchingUsers.end())
|
||
|
return NULL;
|
||
|
|
||
|
GeneralsUser *user = it->second;
|
||
|
m_nonMatchingUsers.erase(it);
|
||
|
return user;
|
||
|
}
|
||
|
|
||
|
|
||
|
void GeneralsMatcher::handleDisconnect( const char *reason )
|
||
|
{
|
||
|
ERRMSG("Disconnected");
|
||
|
done = true;
|
||
|
exit(0);
|
||
|
}
|
||
|
|
||
|
void GeneralsMatcher::handleRoomMessage( const char *nick, const char *message, MessageType messageType )
|
||
|
{
|
||
|
if (messageType == ActionMessage)
|
||
|
{
|
||
|
PARANOIDMSG(nick << " " << message);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
PARANOIDMSG("[" << nick << "] " << message);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void GeneralsMatcher::handlePlayerMessage( const char *nick, const char *message, MessageType messageType )
|
||
|
{
|
||
|
if (messageType == ActionMessage)
|
||
|
{
|
||
|
DBGMSG(nick << " " << message);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
DBGMSG("[" << nick << "] " << message);
|
||
|
}
|
||
|
|
||
|
if (messageType != NormalMessage)
|
||
|
return;
|
||
|
|
||
|
std::string line = message;
|
||
|
line.append("\\");
|
||
|
|
||
|
int offset = 0;
|
||
|
int firstMarker = line.find_first_of('\\', offset);
|
||
|
int secondMarker = line.find_first_of('\\', firstMarker + 1);
|
||
|
if (firstMarker >= 0 && secondMarker >= 0)
|
||
|
{
|
||
|
std::string marker = line.substr(firstMarker + 1, secondMarker - firstMarker - 1);
|
||
|
if (marker == "CINFO")
|
||
|
{
|
||
|
handleUserInfo(nick, line.substr(secondMarker));//, std::string::npos));
|
||
|
}
|
||
|
else if (marker == "WIDEN")
|
||
|
{
|
||
|
handleUserWiden(nick);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
INFMSG("Unknown marker [" << marker << "] in line [" << line << "] from " << nick);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
INFMSG("Failed to parse line [" << line << "] from " << nick);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void GeneralsMatcher::handlePlayerJoined( const char *nick )
|
||
|
{
|
||
|
DBGMSG("Player " << nick << " joined");
|
||
|
addUser(nick);
|
||
|
}
|
||
|
|
||
|
void GeneralsMatcher::handlePlayerLeft( const char *nick )
|
||
|
{
|
||
|
DBGMSG("Player " << nick << " left");
|
||
|
if (m_nick != nick)
|
||
|
removeUser(nick);
|
||
|
}
|
||
|
|
||
|
void GeneralsMatcher::handlePlayerChangedNick( const char *oldNick, const char *newNick )
|
||
|
{
|
||
|
DBGMSG("Player " << oldNick << " changed nick to " << newNick << " - resetting to non-matching state");
|
||
|
removeUser(oldNick);
|
||
|
addUser(newNick);
|
||
|
}
|
||
|
|
||
|
void GeneralsMatcher::handlePlayerEnum( bool success, int gameSpyIndex, const char *nick, int flags )
|
||
|
{
|
||
|
if (!nick)
|
||
|
nick = "";
|
||
|
DBGMSG("PlayerEnum: success=" << success << " index=" << gameSpyIndex << ", nick=" << nick << ", flags=" << flags);
|
||
|
|
||
|
if (success && gameSpyIndex >= 0 && m_nick != nick)
|
||
|
{
|
||
|
addUser(nick);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
// =====================================================================
|
||
|
// TEST Client Matcher class
|
||
|
// =====================================================================
|
||
|
|
||
|
GeneralsClientMatcher::GeneralsClientMatcher()
|
||
|
{
|
||
|
// Read some values from the config file
|
||
|
int quietTMP = 0;
|
||
|
Global.config.getInt("NOECHO", quietTMP);
|
||
|
if (quietTMP)
|
||
|
quiet = true;
|
||
|
else
|
||
|
quiet = false;
|
||
|
}
|
||
|
|
||
|
void GeneralsClientMatcher::init(void)
|
||
|
{
|
||
|
m_baseNick.setFormatted("qmBot%d", time(NULL));
|
||
|
m_profileID = 0;
|
||
|
}
|
||
|
|
||
|
void GeneralsClientMatcher::checkMatches(void)
|
||
|
{}
|
||
|
|
||
|
void GeneralsClientMatcher::handleDisconnect( const char *reason )
|
||
|
{}
|
||
|
void GeneralsClientMatcher::handleRoomMessage( const char *nick, const char *message, MessageType messageType )
|
||
|
{}
|
||
|
void GeneralsClientMatcher::handlePlayerMessage( const char *nick, const char *message, MessageType messageType )
|
||
|
{}
|
||
|
void GeneralsClientMatcher::handlePlayerJoined( const char *nick )
|
||
|
{}
|
||
|
void GeneralsClientMatcher::handlePlayerLeft( const char *nick )
|
||
|
{}
|
||
|
void GeneralsClientMatcher::handlePlayerChangedNick( const char *oldNick, const char *newNick )
|
||
|
{}
|
||
|
void GeneralsClientMatcher::handlePlayerEnum( bool success, int gameSpyIndex, const char *nick, int flags )
|
||
|
{}
|
||
|
|
||
|
|
||
|
// =====================================================================
|
||
|
// End of File
|
||
|
// =====================================================================
|
||
|
|
||
|
|