bool 
MemCacheClient::AddServer(
    const char *    aServerAddress,
    const char *    aServerName,
    unsigned        aServices
    )
{
    if (!aServerName) {
        aServerName = aServerAddress;
    }

    // if we the server address is valid then we allow the server 
    // to be added. All servers being added are assumed to be available
    // or to be soon made available. 
    Server * pServer = new Server(mTrace);
    if (!pServer->Set(aServerAddress)) {
        mTrace.Trace(CLERROR, "Ignoring invalid server: %s (%s)", 
            aServerAddress, aServerName);
        delete pServer;
        return false;
    }
    for (size_t n = 0; n < mServer.size(); ++n) {
        if (*pServer == *mServer[n]) {
            mTrace.Trace(CLERROR, "Ignoring duplicate server: %s (%s)", 
                aServerAddress, aServerName);
            return true; // already have it
        }
    }
    mServer.push_back(pServer);

    // for each salt we generate a string hash for the consistent hash 
    // table. To ensure stability of the hashing for multiple servers, 
    // we want to have a number of entries for each server. 
    static const char * rgpSalt[] = {
        "{DEA60AAB-CFF9-4a20-A799-4E5E93369656}",
        "{C05167CC-57DA-40f2-9EB8-18F65E56FD21}",
        "{57939537-0966-49e7-B675-ACE63246BFA5}",
        "{F0C8BE5C-A0F1-478f-BC45-28D42AF0CA1E}"
    };

    string_t sKey;
    ConsistentHash entry(0, pServer, aServices, 0);
    for (size_t n = 0; n < sizeof(rgpSalt)/sizeof(rgpSalt[0]); ++n) {
        sKey  = pServer->GetAddress();
        sKey += rgpSalt[n];
        entry.mEntry++;
        entry.mHash = CreateKeyHash(sKey.data());
        mServerHash.push_back(entry);
    }

    // sort the vector so that we can binary search it
    std::sort(mServerHash.begin(), mServerHash.end());

    mTrace.Trace(CLINFO, "Adding server: %s (%s:%u), services: 0x%x",
        aServerAddress, aServerName, pServer->GetPort(), aServices);
    return true;
}
bool 
MemCacheClient::AddServer(
    const char * a_pszServer
    )
{
    // if we the server address is valid then we allow the server 
    // to be added. All servers being added are assumed to be available
    // or to be soon made available. Uncontactable servers will cause
    // extra load on the database because the caching will not be available.
    Server * pServer = new Server;
    if (!pServer->Set(a_pszServer)) {
        delete pServer;
        return false;
    }

    for (size_t n = 0; n < m_rgpServer.size(); ++n) {
        if (*pServer == *m_rgpServer[n]) return true; // already have it
    }
    m_rgpServer.push_back(pServer);

    // for each salt we generate a string hash for the consistent hash 
    // table. To ensure stability of the hashing for multiple servers, 
    // we want to have a number of entries for each server. 
    static const char * rgpSalt[] = {
        "{DD4C855D-7548-4804-8F1A-166CDBACEFE7}",
        "{9BF02198-1D29-4aa3-9466-A4AF4372D5B1}",
        "{0F20CD2F-ACF2-44bc-8CE3-54529D7B738D}",
        "{DEA60AAB-CFF9-4a20-A799-4E5E93369656}",
        "{C05167CC-57DA-40f2-9EB8-18F65E56FD21}",
        "{57939537-0966-49e7-B675-ACE63246BFA5}",
        "{F0C8BE5C-A0F1-478f-BC45-28D42AF0CA1E}"
    };

    string_t sKey;
    ConsistentHash entry(0, pServer);
    for (size_t n = 0; n < sizeof(rgpSalt)/sizeof(rgpSalt[0]); ++n) {
        sKey  = pServer->GetAddress();
        sKey += rgpSalt[n];
        entry.mHash = CreateKeyHash(sKey.data());
        m_rgServerHash.push_back(entry);
    }

    // sort the vector so that we can binary search it
    std::sort(m_rgServerHash.begin(), m_rgServerHash.end());

#if 0
    printf("\nSERVER RING (%d servers):\n", m_rgpServer.size());
    for (size_t n = 0; n < m_rgServerHash.size(); ++n) {
        printf("%08x = %s\n", m_rgServerHash[n].mHash,
            m_rgServerHash[n].mServer->GetAddress());
    }
#endif

    return true;
}
MCResult 
MemCacheClient::IncDec(
    const char *    aType, 
    unsigned        aService,
    const char *    aKey, 
    uint64_t *      aNewValue,
    uint64_t        aDiff,
    bool            aWantReply
    )
{
    string_t key(aKey);
    Server * pServer = FindServer(key, aService);
    if (!pServer) return MCERR_NOSERVER;

    char szBuf[50];
    string_t sRequest(aType);
    sRequest += ' ';
    sRequest += aKey;
    snprintf(szBuf, sizeof(szBuf), " %" PRIu64, aDiff);
    sRequest += szBuf;
    if (!aWantReply) {
        sRequest += " noreply";
    }
    sRequest += "\r\n";

    try {
        pServer->SendBytes(sRequest.data(), sRequest.length());

        if (!aWantReply) {
            return MCERR_NOREPLY;
        }

        string_t sValue;
        sValue = pServer->GetByte();
        while (sValue[sValue.length()-1] != '\n') {
            sValue += pServer->GetByte();
        }

        if (sValue == "NOT_FOUND\r\n") {
            return MCERR_NOTFOUND;
        }

        if (aNewValue) {
            *aNewValue = strtoull(sValue.data(), NULL, 10);
        }
        return MCERR_OK;
    }
    catch (const Socket::Exception & e) {
        mTrace.Trace(CLINFO, "IncDec: error '%s' at %s, marking request as NOSERVER",
            e.mDetail, pServer->GetAddress());
        pServer->Disconnect();
        return MCERR_NOSERVER;
    }
}
Exemple #4
0
/*
    Takes a server structure and adds it to the list control
    if insert is 1, then add an item to the list, otherwise it will
    update the current item with new data
*/
void LstOdaServerList::AddServerToList(const Server &s, 
                                        wxInt32 index, 
                                        bool insert)
{
    wxFileConfig ConfigInfo;
    wxInt32 PQGood, PQPlayable, PQLaggy;
    
    wxInt32 i = 0;
    wxListItem li;
    
    wxUint64 Ping = 0;
    wxString GameType = wxT("");
    size_t WadCount = 0;
    
    li.m_mask = wxLIST_MASK_TEXT;

    if (insert)
    {
        li.m_itemId = ALCInsertItem();
    }
    else
    {
        // All cells must be cleared on a "replace" operation, otherwise if the 
        // server failed to respond a second time 'round, we would end up with 
        // some stale data (which is highly dependent on the response check 
        // below)
        ClearItemCells(index);
        
        li.m_itemId = index;
    }
       
    // Address column
    li.m_col = serverlist_field_address;    
    li.m_text = stdstr_towxstr(s.GetAddress());

    SetItem(li);

    // break here so atleast we have an ip address to go by
    if (s.GotResponse() == false)
        return;

    // Server name column
    li.m_col = serverlist_field_name;
    li.m_text = stdstr_towxstr(s.Info.Name);
       
    SetItem(li);
      
    // Ping column
    Ping = s.GetPing();

    li.m_col = serverlist_field_ping;
    li.m_text = wxString::Format(_T("%llu"), Ping);

    SetItem(li);

    // Number of players, Maximum players column
    // TODO: acquire max players, max clients and spectators from these 2 and
    // create some kind of graphical column maybe
    li.m_col = serverlist_field_players;
    li.m_text = wxString::Format(_T("%d/%d"),s.Info.Players.size(),s.Info.MaxClients);
    
    // Colour the entire text column (wx/windows bug - exploited) if there are
    // players
    // TODO: Allow the user to select prefered colours
    if (s.Info.Players.size())
        li.SetTextColour(wxColor(0,192,0));

    SetItem(li); 
    
    // WAD files column
    WadCount = s.Info.Wads.size();
    
    // build a list of pwads
    if (WadCount)
    {
        // pwad list
        std::string wadlist;
        std::string pwad;
            
        for (i = 2; i < WadCount; ++i)
        {
            pwad = s.Info.Wads[i].Name.substr(0, s.Info.Wads[i].Name.find('.'));
            
            wadlist.append(pwad);
            wadlist.append(" ");
        }
            
        li.m_col = serverlist_field_wads;
        li.m_text = stdstr_towxstr(wadlist);
    
        SetItem(li);
    }

    // Map name column
    li.m_col = serverlist_field_map;
    li.m_text = stdstr_towxstr(s.Info.CurrentMap).Upper();
    
    SetItem(li);
       
    // Game type column
    switch (s.Info.GameType)
    {
        case GT_Cooperative:
        {
            // Detect a single player server
            if (s.Info.MaxPlayers > 1)
                GameType = wxT("Cooperative");
            else
                GameType = wxT("Single Player");
        }
        break;
        
        case GT_Deathmatch:
        {
            GameType = wxT("Deathmatch");
        }
        break;
        
        case GT_TeamDeathmatch:
        {
            GameType = wxT("Team Deathmatch");
        }
        break;
        
        case GT_CaptureTheFlag:
        {
            GameType = wxT("Capture The Flag");
        }
        break;
        
        default:
        {
            GameType = wxT("Unknown");
        }
        break;
    }
    
    li.m_col = serverlist_field_type;
    li.m_text = GameType;
    
    SetItem(li);

    // IWAD column
    if (WadCount)
    {
        std::string iwad;
        iwad = s.Info.Wads[1].Name.substr(0, s.Info.Wads[1].Name.find('.'));
        
        li.m_col = serverlist_field_iwad;
        li.m_text = stdstr_towxstr(iwad);
    }
    
    SetItem(li);
    
    // Icons
    // -----
    
    // Padlock icon for passworded servers
    SetItemColumnImage(li.m_itemId, serverlist_field_name, 
        (s.Info.PasswordHash.length() ? ImageList_Padlock : -1));
    
    ConfigInfo.Read(wxT("IconPingQualityGood"), &PQGood, 150);
    ConfigInfo.Read(wxT("IconPingQualityPlayable"), &PQPlayable, 300);
    ConfigInfo.Read(wxT("IconPingQualityLaggy"), &PQLaggy, 350);
    
    // Coloured bullets for ping quality
    if (Ping < PQGood)
    {
        SetItemColumnImage(li.m_itemId, serverlist_field_ping, 
            ImageList_PingGreen);
    }
    else if (Ping < PQPlayable)
    {
        SetItemColumnImage(li.m_itemId, serverlist_field_ping, 
            ImageList_PingOrange);
    }
    else if (Ping < PQLaggy)
    {
        SetItemColumnImage(li.m_itemId, serverlist_field_ping, 
            ImageList_PingRed);
    }
    else
    {
        SetItemColumnImage(li.m_itemId, serverlist_field_ping, 
            ImageList_PingGray);
    }
}