28#include <ghttp/ghttp.h>
75 buf[0] =
'0' + val%10;
94 buf[0] =
'0' + val%10;
104 if (a.size() != b.size())
107 for (
int i=0; i<(int)a.size(); ++i)
109 c.push_back(a[i] && b[i]);
118 for (
int i=0; i<(int)a.size(); ++i)
147static const int MaxPingValue = 255*255*2;
154 int bestPing = MaxPingValue;
155 for (
int i=0; i<(int)a->
pseudoPing.size(); ++i)
161 if (p1 * p1 + p2 * p2 < bestPing)
162 bestPing = p1 * p1 + p2 * p2;
165 return (
int)sqrt(bestPing);
177 Global.config.getInt(
"NOECHO", quietTMP);
184 Global.config.getInt(
"MATCH_WEIGHT_LOWPING", weightLowPing,
"GENERALS");
185 Global.config.getInt(
"MATCH_WEIGHT_AVGPOINTS", weightAvgPoints,
"GENERALS");
186 totalWeight = weightLowPing + weightAvgPoints;
188 INFMSG(
"weightLowPing = " << weightLowPing);
189 INFMSG(
"weightAvgPoints = " << weightAvgPoints);
190 INFMSG(
"totalWeight = " << totalWeight);
192 Global.config.getInt(
"SecondsBetweenPoolSizeAnnouncements", m_secondsBetweenPoolSizeAnnouncements,
NULL);
193 if (m_secondsBetweenPoolSizeAnnouncements < 10)
195 m_secondsBetweenPoolSizeAnnouncements = 10;
197 m_nextPoolSizeAnnouncement = time(
NULL);
204#define W(x) setw(x) <<
205void GeneralsMatcher::dumpUsers(
void)
210void GeneralsMatcher::sendMatchInfo(std::string name1, std::string name2, std::string name3, std::string name4,
211 std::string name5, std::string name6, std::string name7, std::string name8,
214 int numPlayers,
int ladderID)
277 whichMap = whichMap%numMaps;
281 DBGMSG(
"Random map #" << whichMap <<
"/" << numMaps);
284 for (i=0; i<(int)user1->
maps.size(); ++i)
291 DBGMSG(
"Playing on map in pos " << i);
431 peerMessagePlayer(
m_peer, n.c_str(), s.c_str(), NormalMessage);
436 bool showPoolSize =
false;
438 if (now > m_nextPoolSizeAnnouncement)
440 m_nextPoolSizeAnnouncement = now + m_secondsBetweenPoolSizeAnnouncements;
443 checkMatchesInUserMap(m_nonLadderUsers1v1, 0, 2, showPoolSize);
444 checkMatchesInUserMap(m_nonLadderUsers2v2, 0, 4, showPoolSize);
445 checkMatchesInUserMap(m_nonLadderUsers3v3, 0, 6, showPoolSize);
446 checkMatchesInUserMap(m_nonLadderUsers4v4, 0, 8, showPoolSize);
448 for (LadderMap::iterator it = m_ladders.begin(); it != m_ladders.end(); ++it)
450 checkMatchesInUserMap(it->second, it->first, 2, showPoolSize);
454double GeneralsMatcher::computeMatchFitness(
const std::string& i1,
const GeneralsUser *u1,
const std::string& i2,
const GeneralsUser *u2)
466 double p1percent = (double)p2/(
double)p1;
467 double p2percent = (double)p1/(
double)p2;
469 if (!u1->
widened && ( p1percent < u1->minPoints || p1percent > u1->
maxPoints ))
472 if (!u2->
widened && ( p2percent < u2->minPoints || p2percent > u2->
maxPoints ))
476 int minP =
min(p1, p2);
477 int maxP =
max(p1, p2);
478 double pointPercent = (double)minP/(
double)maxP;
504 double matchFitness = ( weightAvgPoints * (1-pointPercent) +
505 weightLowPing * (MaxPingValue - pingDelta)/MaxPingValue ) / (
double)totalWeight;
522void GeneralsMatcher::checkMatchesInUserMap(
UserMap& userMap,
int ladderID,
int numPlayers,
bool showPoolSize)
524 UserMap::iterator i1, i2, i3, i4, i5, i6, i7, i8;
525 GeneralsUser *u1, *u2, *u3, *u4, *u5, *u6, *u7, *u8;
526 static const double fitnessThreshold = 0.3;
532 s =
"MBOT:POOLSIZE ";
537 for (i1 = userMap.begin(); i1 != userMap.end(); ++i1)
541 peerMessagePlayer(
m_peer, i1->first.c_str(), s.c_str(), NormalMessage);
549 for (
int m=0; m<(int)u1->
maps.size(); ++m)
551 DBGMSG(
"Widening search for " << i1->first);
552 peerMessagePlayer(
m_peer, i1->first.c_str(),
"MBOT:WIDENINGSEARCH", NormalMessage);
557 for (i1 = userMap.begin(); i1 != userMap.end(); ++i1)
563 GeneralsUser *bestUser =
NULL;
564 double bestMatchFitness = 0.0;
565 std::string bestName =
"";
569 for (++i2; i2 != userMap.end(); ++i2)
575 double matchFitness = computeMatchFitness(i1->first, u1, i2->first, u2);
576 if (matchFitness > fitnessThreshold)
580 if (matchFitness > bestMatchFitness)
583 bestMatchFitness = matchFitness;
585 bestName = i2->first;
591 for (++i3; i3 != userMap.end(); ++i3)
597 double matchFitness1 = computeMatchFitness(i1->first, u1, i3->first, u3);
598 double matchFitness2 = computeMatchFitness(i2->first, u2, i3->first, u3);
601 if (
MapSetCount(tmp) && matchFitness1 > fitnessThreshold && matchFitness2 > fitnessThreshold)
604 for (++i4; i4 != userMap.end(); ++i4)
610 double matchFitness1 = computeMatchFitness(i1->first, u1, i4->first, u4);
611 double matchFitness2 = computeMatchFitness(i2->first, u2, i4->first, u4);
612 double matchFitness3 = computeMatchFitness(i3->first, u3, i4->first, u4);
616 if (
MapSetCount(tmp) && matchFitness1 > fitnessThreshold && matchFitness2 > fitnessThreshold && matchFitness3 > fitnessThreshold)
621 sendMatchInfo(i1->first, i2->first, i3->first, i4->first,
"",
"",
"",
"",
629 for (++i5; i5 != userMap.end(); ++i3)
635 double matchFitness1 = computeMatchFitness(i1->first, u1, i5->first, u5);
636 double matchFitness2 = computeMatchFitness(i2->first, u2, i5->first, u5);
637 double matchFitness3 = computeMatchFitness(i3->first, u3, i5->first, u5);
638 double matchFitness4 = computeMatchFitness(i4->first, u4, i5->first, u5);
643 if (
MapSetCount(tmp) && matchFitness1 > fitnessThreshold && matchFitness2 > fitnessThreshold && matchFitness3 > fitnessThreshold && matchFitness4 > fitnessThreshold)
646 for (++i6; i6 != userMap.end(); ++i6)
652 double matchFitness1 = computeMatchFitness(i1->first, u1, i6->first, u6);
653 double matchFitness2 = computeMatchFitness(i2->first, u2, i6->first, u6);
654 double matchFitness3 = computeMatchFitness(i3->first, u3, i6->first, u6);
655 double matchFitness4 = computeMatchFitness(i4->first, u4, i6->first, u6);
656 double matchFitness5 = computeMatchFitness(i5->first, u5, i6->first, u6);
662 if (
MapSetCount(tmp) && matchFitness1 > fitnessThreshold && matchFitness2 > fitnessThreshold && matchFitness3 > fitnessThreshold && matchFitness4 > fitnessThreshold && matchFitness5 > fitnessThreshold)
667 sendMatchInfo(i1->first, i2->first, i3->first, i4->first, i5->first, i6->first,
"",
"",
668 u1, u2, u3, u4, u5, u6,
NULL,
NULL, 6, ladderID);
675 for (++i7; i7 != userMap.end(); ++i7)
681 double matchFitness1 = computeMatchFitness(i1->first, u1, i7->first, u7);
682 double matchFitness2 = computeMatchFitness(i2->first, u2, i7->first, u7);
683 double matchFitness3 = computeMatchFitness(i3->first, u3, i7->first, u7);
684 double matchFitness4 = computeMatchFitness(i4->first, u4, i7->first, u7);
685 double matchFitness5 = computeMatchFitness(i5->first, u5, i7->first, u7);
686 double matchFitness6 = computeMatchFitness(i6->first, u6, i7->first, u7);
693 if (
MapSetCount(tmp) && matchFitness1 > fitnessThreshold && matchFitness2 > fitnessThreshold && matchFitness3 > fitnessThreshold && matchFitness4 > fitnessThreshold && matchFitness5 > fitnessThreshold && matchFitness6 > fitnessThreshold)
696 for (++i8; i8 != userMap.end(); ++i8)
702 double matchFitness1 = computeMatchFitness(i1->first, u1, i8->first, u8);
703 double matchFitness2 = computeMatchFitness(i2->first, u2, i8->first, u8);
704 double matchFitness3 = computeMatchFitness(i3->first, u3, i8->first, u8);
705 double matchFitness4 = computeMatchFitness(i4->first, u4, i8->first, u8);
706 double matchFitness5 = computeMatchFitness(i5->first, u5, i8->first, u8);
707 double matchFitness6 = computeMatchFitness(i6->first, u6, i8->first, u8);
708 double matchFitness7 = computeMatchFitness(i7->first, u7, i8->first, u8);
716 if (
MapSetCount(tmp) && matchFitness1 > fitnessThreshold && matchFitness2 > fitnessThreshold && matchFitness3 > fitnessThreshold && matchFitness4 > fitnessThreshold && matchFitness5 > fitnessThreshold && matchFitness6 > fitnessThreshold && matchFitness7 > fitnessThreshold)
719 sendMatchInfo(i1->first, i2->first, i3->first, i4->first, i5->first, i6->first, i7->first, i8->first,
720 u1, u2, u3, u4, u5, u6, u7, u8, 8, ladderID);
742 if (bestUser && numPlayers == 2)
745 DBGMSG(
"Matching " << i1->first <<
" with " << bestName <<
":\n"
746 "\tmatch fitness: " << bestMatchFitness <<
"\n"
747 "\tpoint percentage: " << (1-bestUser->
points/(
double)u1->
points)*100 <<
"\n"
748 "\tpoints: " << u1->
points <<
", " << u2->
points <<
"\n"
749 "\tping in ms: " << sqrt(1000000 *
calcPingDelta(u1, bestUser) / (255*255*2)) <<
"\n"
750 "\tprevious attempts: " << u1->
widened <<
", " << bestUser->
widened);
751 sendMatchInfo(i1->first, bestName,
"",
"",
"",
"",
"",
"",
761bool GeneralsMatcher::handleUserWiden(
const char *nick)
763 GeneralsUser *userInfo = findUserInAnyLadder(nick);
766 userInfo = findNonLadderUser(nick);
771 DBGMSG(
"Got Widen from nick not needing one!");
772 peerMessagePlayer(
m_peer, nick,
"MBOT:CANTSENDWIDENNOW", NormalMessage);
775 DBGMSG(
"Widening search for " << nick);
776 peerMessagePlayer(
m_peer, nick,
"MBOT:WIDENINGSEARCH", NormalMessage);
782bool GeneralsMatcher::handleUserInfo(
const char *nick,
const std::string&
msg)
784 DBGMSG(
"Got user info [" <<
msg <<
"] from " << nick);
785 GeneralsUser *userInfo = removeNonMatchingUser(nick);
788 DBGMSG(
"Got UserInfo from nick not needing one!");
789 peerMessagePlayer(
m_peer, nick,
"MBOT:CANTSENDCINFONOW", NormalMessage);
792 DBGMSG(
"Looking at " << nick <<
" with user info [" <<
msg <<
"]");
795 unsigned int ladderPassCRC = 0;
800 int firstMarker =
msg.find_first_of(
'\\', offset);
803 int secondMarker =
msg.find_first_of(
'\\', firstMarker + 1);
804 if (secondMarker < 0)
806 int thirdMarker =
msg.find_first_of(
'\\', secondMarker + 1);
809 std::string k =
msg.substr(firstMarker + 1, secondMarker - firstMarker - 1);
810 std::string v =
msg.substr(secondMarker + 1, thirdMarker - secondMarker - 1);
811 offset = thirdMarker - 1;
815 int val = atoi(v.c_str());
821 else if (k ==
"LadID")
823 ladderID = atoi(v.c_str());
825 else if (k ==
"LadPass")
827 ladderPassCRC = atoi(v.c_str());
829 else if (k ==
"PointsMin")
833 else if (k ==
"PointsMax")
837 else if (k ==
"PingMax")
839 userInfo->
maxPing = atoi(v.c_str());
841 else if (k ==
"DisconMax")
845 else if (k ==
"Maps")
852 userInfo->
maps.clear();
855 INFMSG(
"Bad maps from " << nick <<
": [" << v <<
"]");
856 peerMessagePlayer(
m_peer, nick,
"MBOT:BADMAPS", NormalMessage);
859 const char *buf = v.c_str();
863 bool hasMap = (*buf !=
'0');
865 userInfo->
maps.push_back( hasMap );
870 else if (k ==
"NumPlayers")
876 INFMSG(
"Bad numPlayers from " << nick <<
": [" << userInfo->
numPlayers <<
"]");
877 peerMessagePlayer(
m_peer, nick,
"MBOT:BADCINFO", NormalMessage);
883 userInfo->
IP = atoi(v.c_str());
887 userInfo->
NAT = atoi(v.c_str());
889 else if (k ==
"Side")
891 userInfo->
country = atoi(v.c_str());
893 else if (k ==
"Color")
895 userInfo->
color = atoi(v.c_str());
897 else if (k ==
"Pings")
899 if (!v.length() || (v.length() % 2))
901 INFMSG(
"Bad pings from " << nick <<
": [" << v <<
"]");
902 peerMessagePlayer(
m_peer, nick,
"MBOT:BADPINGS", NormalMessage);
906 const char *buf = v.c_str();
914 ping = (int)strtol(buf2,
NULL, 16);
918 else if (k ==
"Points")
920 userInfo->
points =
max(1, atoi(v.c_str()));
922 else if (k ==
"Discons")
924 userInfo->
discons = atoi(v.c_str());
928 INFMSG(
"Unknown key/value pair in user info [" << k <<
"]/[" << v <<
"]");
929 peerMessagePlayer(
m_peer, nick,
"MBOT:BADCINFO", NormalMessage);
934 std::string s =
"MBOT:WORKING ";
938 addUserInLadder(nick, ladderID, userInfo);
943 addNonLadderUser(nick, userInfo);
963 peerMessagePlayer(
m_peer, nick, s.c_str(), NormalMessage);
965 DBGMSG(
"Player " << nick <<
" is matching now, ack was [" << s <<
"]");
969GeneralsUser* GeneralsMatcher::findUser(
const std::string& who)
972 user = findNonLadderUser(who);
975 user = findNonMatchingUser(who);
978 user = findUserInAnyLadder(who);
985GeneralsUser* GeneralsMatcher::findUserInAnyLadder(
const std::string& who)
987 for (LadderMap::iterator lIt = m_ladders.begin(); lIt != m_ladders.end(); ++lIt)
989 UserMap::iterator uIt = lIt->second.find(who);
990 if (uIt != lIt->second.end())
996GeneralsUser* GeneralsMatcher::findUserInLadder(
const std::string& who,
int ladderID)
998 LadderMap::iterator lIt = m_ladders.find(ladderID);
999 if (lIt == m_ladders.end())
1002 UserMap::iterator uIt = lIt->second.find(who);
1003 if (uIt == lIt->second.end())
1009GeneralsUser* GeneralsMatcher::findNonLadderUser(
const std::string& who)
1011 UserMap::iterator it = m_nonLadderUsers1v1.find(who);
1012 if (it != m_nonLadderUsers1v1.end())
1015 it = m_nonLadderUsers2v2.find(who);
1016 if (it != m_nonLadderUsers2v2.end())
1019 it = m_nonLadderUsers3v3.find(who);
1020 if (it != m_nonLadderUsers3v3.end())
1023 it = m_nonLadderUsers4v4.find(who);
1024 if (it != m_nonLadderUsers4v4.end())
1030GeneralsUser* GeneralsMatcher::findNonMatchingUser(
const std::string& who)
1032 UserMap::iterator it = m_nonMatchingUsers.find(who);
1033 if (it == m_nonMatchingUsers.end())
1039void GeneralsMatcher::addUser(
const std::string& who)
1043 ERRMSG(
"Re-adding " << who);
1047 addNonMatchingUser(who,
new GeneralsUser);
1050void GeneralsMatcher::addUserInLadder(
const std::string& who,
int ladderID,
GeneralsUser *user)
1052 m_ladders[ladderID][who] = user;
1055void GeneralsMatcher::addNonLadderUser(
const std::string& who,
GeneralsUser *user)
1060 m_nonLadderUsers1v1[who] = user;
1063 m_nonLadderUsers2v2[who] = user;
1066 m_nonLadderUsers3v3[who] = user;
1069 m_nonLadderUsers4v4[who] = user;
1074void GeneralsMatcher::addNonMatchingUser(
const std::string& who,
GeneralsUser *user)
1076 m_nonMatchingUsers[who] = user;
1080bool GeneralsMatcher::removeUser(
const std::string& who)
1083 user = removeUserInAnyLadder(who);
1089 user = removeNonLadderUser(who);
1095 user = removeNonMatchingUser(who);
1105GeneralsUser* GeneralsMatcher::removeUserInLadder(
const std::string& who,
int ladderID)
1107 LadderMap::iterator lIt = m_ladders.find(ladderID);
1108 if (lIt == m_ladders.end())
1111 UserMap::iterator uIt = lIt->second.find(who);
1112 if (uIt == lIt->second.end())
1115 GeneralsUser *user = uIt->second;
1116 lIt->second.erase(uIt);
1120GeneralsUser* GeneralsMatcher::removeUserInAnyLadder(
const std::string& who)
1122 for (LadderMap::iterator lIt = m_ladders.begin(); lIt != m_ladders.end(); ++lIt)
1124 UserMap::iterator uIt = lIt->second.find(who);
1125 if (uIt != lIt->second.end())
1127 GeneralsUser *user = uIt->second;
1128 lIt->second.erase(uIt);
1135GeneralsUser* GeneralsMatcher::removeNonLadderUser(
const std::string& who)
1137 UserMap::iterator it = m_nonLadderUsers1v1.find(who);
1138 if (it != m_nonLadderUsers1v1.end())
1140 GeneralsUser *user = it->second;
1141 m_nonLadderUsers1v1.erase(it);
1145 it = m_nonLadderUsers2v2.find(who);
1146 if (it != m_nonLadderUsers2v2.end())
1148 GeneralsUser *user = it->second;
1149 m_nonLadderUsers2v2.erase(it);
1153 it = m_nonLadderUsers3v3.find(who);
1154 if (it != m_nonLadderUsers3v3.end())
1156 GeneralsUser *user = it->second;
1157 m_nonLadderUsers3v3.erase(it);
1161 it = m_nonLadderUsers4v4.find(who);
1162 if (it != m_nonLadderUsers4v4.end())
1164 GeneralsUser *user = it->second;
1165 m_nonLadderUsers4v4.erase(it);
1172GeneralsUser* GeneralsMatcher::removeNonMatchingUser(
const std::string& who)
1174 UserMap::iterator it = m_nonMatchingUsers.find(who);
1175 if (it == m_nonMatchingUsers.end())
1178 GeneralsUser *user = it->second;
1179 m_nonMatchingUsers.erase(it);
1193 if (messageType == ActionMessage)
1205 if (messageType == ActionMessage)
1207 DBGMSG(nick <<
" " << message);
1211 DBGMSG(
"[" << nick <<
"] " << message);
1214 if (messageType != NormalMessage)
1217 std::string line = message;
1221 int firstMarker = line.find_first_of(
'\\', offset);
1222 int secondMarker = line.find_first_of(
'\\', firstMarker + 1);
1223 if (firstMarker >= 0 && secondMarker >= 0)
1225 std::string marker = line.substr(firstMarker + 1, secondMarker - firstMarker - 1);
1226 if (marker ==
"CINFO")
1228 handleUserInfo(nick, line.substr(secondMarker));
1230 else if (marker ==
"WIDEN")
1232 handleUserWiden(nick);
1236 INFMSG(
"Unknown marker [" << marker <<
"] in line [" << line <<
"] from " << nick);
1241 INFMSG(
"Failed to parse line [" << line <<
"] from " << nick);
1247 DBGMSG(
"Player " << nick <<
" joined");
1253 DBGMSG(
"Player " << nick <<
" left");
1260 DBGMSG(
"Player " << oldNick <<
" changed nick to " << newNick <<
" - resetting to non-matching state");
1261 removeUser(oldNick);
1269 DBGMSG(
"PlayerEnum: success=" << success <<
" index=" << gameSpyIndex <<
", nick=" << nick <<
", flags=" << flags);
1271 if (success && gameSpyIndex >= 0 &&
m_nick != nick)
1286 Global.config.getInt(
"NOECHO", quietTMP);
std::string intToString(int val)
virtual void handlePlayerEnum(bool success, int gameSpyIndex, const char *nick, int flags)
virtual void handlePlayerChangedNick(const char *oldNick, const char *newNick)
virtual void checkMatches(void)
virtual void handlePlayerJoined(const char *nick)
virtual void handlePlayerLeft(const char *nick)
virtual void handleDisconnect(const char *reason)
virtual void handleRoomMessage(const char *nick, const char *message, MessageType messageType)
virtual void handlePlayerMessage(const char *nick, const char *message, MessageType messageType)
virtual void handleDisconnect(const char *reason)
virtual void handlePlayerLeft(const char *nick)
virtual void handlePlayerMessage(const char *nick, const char *message, MessageType messageType)
virtual void handlePlayerJoined(const char *nick)
virtual void checkMatches(void)
virtual void handlePlayerEnum(bool success, int gameSpyIndex, const char *nick, int flags)
virtual void handlePlayerChangedNick(const char *oldNick, const char *newNick)
virtual void handleRoomMessage(const char *nick, const char *message, MessageType messageType)
std::vector< int > pseudoPing
MapBitSet MapSetUnion(const MapBitSet &a, const MapBitSet &b)
std::string uintToString(unsigned int val)
int calcPingDelta(const GeneralsUser *a, const GeneralsUser *b)
int MapSetCount(const MapBitSet &a)
std::string intToString(int val)
std::map< std::string, GeneralsUser * > UserMap
std::vector< bool > MapBitSet