/* ** Command & Conquer Renegade(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 . */ /****************************************************************************** * * FILE * $Archive: /Commando/Code/WWOnline/WOLNetUtilObserver.cpp $ * * DESCRIPTION * * PROGRAMMER * $Author: Denzil_l $ * * VERSION INFO * $Revision: 25 $ * $Modtime: 2/21/02 5:36p $ * ******************************************************************************/ #include #include "WOLNetUtilObserver.h" #include "WOLSession.h" #include "WOLLadder.h" #include "WOLServer.h" #include "WOLErrorUtil.h" namespace WWOnline { /****************************************************************************** * * NAME * NetUtilObserver::NetUtilObserver * * DESCRIPTION * Constructor * * INPUTS * NONE * * RESULT * NONE * ******************************************************************************/ NetUtilObserver::NetUtilObserver() : mRefCount(1), mOuter(NULL) { WWDEBUG_SAY(("WOL: NetUtilObserver Instantiated\n")); } /****************************************************************************** * * NAME * NetUtilObserver::~NetUtilObserver * * DESCRIPTION * Destructor * * INPUTS * NONE * * RESULT * NONE * ******************************************************************************/ NetUtilObserver::~NetUtilObserver() { WWDEBUG_SAY(("WOL: NetUtilObserver Destroyed\n")); } /****************************************************************************** * * NAME * NetUtilObserver::Init * * DESCRIPTION * Initialize Net utility observer * * INPUTS * WOLSession - Outer session * * RESULT * NONE * ******************************************************************************/ void NetUtilObserver::Init(Session& outer) { mOuter = &outer; } /**************************************************************************** * * NAME * IUnknown::QueryInterface * * DESCRIPTION * * INPUTS * * RESULT * ****************************************************************************/ STDMETHODIMP NetUtilObserver::QueryInterface(const IID& iid, void** ppv) { if ((iid == IID_IUnknown) || (iid == WOL::IID_INetUtilEvent)) { *ppv = static_cast(this); } else { *ppv = NULL; return E_NOINTERFACE; } static_cast(*ppv)->AddRef(); return S_OK; } /**************************************************************************** * * NAME * IUnknown::AddRef * * DESCRIPTION * * INPUTS * NONE * * RESULT * ****************************************************************************/ ULONG STDMETHODCALLTYPE NetUtilObserver::AddRef(void) { InterlockedIncrement((LPLONG)&mRefCount); return mRefCount; } /**************************************************************************** * * NAME * IUnknown::Release * * DESCRIPTION * * INPUTS * NONE * * RESULT * ****************************************************************************/ ULONG STDMETHODCALLTYPE NetUtilObserver::Release(void) { InterlockedDecrement((LPLONG)&mRefCount); if (mRefCount == 0) { delete this; return 0; } return mRefCount; } /****************************************************************************** * * NAME * NetUtilObserver::OnPing * * DESCRIPTION * Handle ping result response to a ping request * * INPUTS * Result - Error / Status code * Time - Ping time * IP - IP Address * Handle - Ping request handle * * RESULT * ******************************************************************************/ STDMETHODIMP NetUtilObserver::OnPing(HRESULT result, int time, unsigned long ip, int handle) { if (mOuter == NULL) { WWDEBUG_SAY(("WOLERROR: Session not initialized\n")); WWASSERT(mOuter && "Session not initialized"); return S_OK; } if (FAILED(result)) { WWDEBUG_SAY(("WOLERROR: OnPing '%s'\n", GetNetUtilErrorString(result))); return S_OK; } // Find the ping request int pingIndex = -1; for (unsigned int index = 0; index < mOuter->mPingRequests.size(); index++) { if (mOuter->mPingRequests[index].GetHandle() == handle) { pingIndex = index; break; } } if ((pingIndex >= 0) && ((unsigned)pingIndex < mOuter->mPingRequests.size())) { RawPing* ping = &mOuter->mPingRequests[pingIndex]; ping->SetTime(time); ping->SetIPAddress(ip); WWDEBUG_SAY(("WOL: OnPing %s = %d\n", ping->GetHostAddress(), time)); // Automatically update ping time for ping servers. mOuter->UpdatePingServerTime(ping->GetHostAddress(), time); // Notify others about the ping result. mOuter->NotifyObservers(*ping); // Remove ping from request list. std::vector::iterator iter = mOuter->mPingRequests.begin(); while (iter != mOuter->mPingRequests.end()) { if (iter == ping) { mOuter->mPingRequests.erase(iter); mOuter->mPingsPending--; break; } iter++; } } return S_OK; } /****************************************************************************** * * NAME * NetUtilObserver::OnLadderList * * DESCRIPTION * * INPUTS * * RESULT * ******************************************************************************/ STDMETHODIMP NetUtilObserver::OnLadderList(HRESULT result, WOL::Ladder* list, int rungCount, long timeStamp, int keyRung) { if (mOuter == NULL) { WWDEBUG_SAY(("WOLERROR: Session not initialized\n")); WWASSERT(mOuter && "Session not initialized"); return S_OK; } if (FAILED(result) || (list == NULL)) { WWDEBUG_SAY(("WOLERROR: OnLadderList '%s'\n", GetNetUtilErrorString(result))); // Clear the ladder type we requested (in order) if (mOuter->mLadderPending & LadderType_Individual) { mOuter->mLadderPending &= ~LadderType_Individual; } else if (mOuter->mLadderPending & LadderType_Team) { mOuter->mLadderPending &= ~LadderType_Team; } else if (mOuter->mLadderPending & LadderType_Clan) { mOuter->mLadderPending &= ~LadderType_Clan; } else { mOuter->mLadderPending &= ~LADDERTYPE_MASK; } // If there are no pending ladders then remove the satisfied requests if ((mOuter->mLadderPending & LADDERTYPE_MASK) == 0) { for (unsigned int count = 0; count < mOuter->mLadderPending; count++) { mOuter->mLadderRequests.pop_front(); } mOuter->mLadderPending = 0; } return S_OK; } //--------------------------------------------------------------------------- // Handle ladder information for individual users. //--------------------------------------------------------------------------- if (keyRung == -1) { ProcessLadderListResults(list, timeStamp); return S_OK; } //--------------------------------------------------------------------------- // Handle ladder search results //--------------------------------------------------------------------------- LadderList ladders; WOL::Ladder* wolLadder = list; while (wolLadder) { RefPtr data = LadderData::Create(*wolLadder, timeStamp); if (data.IsValid()) { ladders.push_back(data); } wolLadder = wolLadder->next; } mOuter->NotifyObservers(ladders); return S_OK; } /****************************************************************************** * * NAME * NetUtilObserver::ProcessLadderListResults * * DESCRIPTION * * INPUTS * * RESULT * NONE * ******************************************************************************/ void NetUtilObserver::ProcessLadderListResults(WOL::Ladder* list, long timeStamp) { if (mOuter->mLadderPending) { // Within a given request, ladder results are guaranteed to be in order. WOL::Ladder* wolLadder = list; // Extract the ladder request type from the hiword of the SKU LadderType type = (LadderType)(wolLadder->sku & LADDERTYPE_MASK); if (type == 0) { type = LadderType_Individual; } Session::LadderRequestList::iterator request = mOuter->mLadderRequests.begin(); unsigned int ladderCount = 0; while (wolLadder) { // Get the name of the user we requested information for. const WCHAR* requestName = wcschr(*request, L':'); WWASSERT(requestName != NULL && "Invalid ladder request"); requestName++; wchar_t ladderName[64]; mbstowcs(ladderName, (const char*)wolLadder->login_name, sizeof(wolLadder->login_name)); WWDEBUG_SAY(("WOL: LadderInfo [%08lX] Requested '%S', Received '%S'\n", type, requestName, ladderName)); // If the ladder name matches the requested name then there is ladder info available. bool hasLadderData = (wcsicmp(requestName, ladderName) == 0); if (type == LadderType_Clan) { RefPtr squad = SquadData::FindByAbbr(requestName); if (squad.IsValid()) { // Update / create ladder information for this clan RefPtr ladderData = squad->GetLadder(); if (ladderData.IsValid()) { ladderData->UpdateData(*wolLadder, timeStamp); } else if (hasLadderData) { ladderData = LadderData::Create(*wolLadder, timeStamp); WWASSERT(ladderData.IsValid()); squad->SetLadder(ladderData); } // Notify others about updated clan ladder information NotifyClanLadderUpdate(mOuter->mUsers, squad); NotifyClanLadderUpdate(mOuter->mBuddies, squad); } } else { RefPtr user = mOuter->GetUserOrBuddy(requestName); // Update users with new ladder information. if (user.IsValid()) { RefPtr ladderData = user->GetLadderFromType(type); if (ladderData.IsValid()) { ladderData->UpdateData(*wolLadder, timeStamp); } else if (hasLadderData) { ladderData = LadderData::Create(*wolLadder, timeStamp); WWASSERT(ladderData.IsValid()); if (ladderData.IsValid()) { user->SetLadderFromType(ladderData, type); } } // Notify others that the ladder information for this user has changed. UserEvent event(UserEvent::LadderInfo, user); mOuter->NotifyObservers(event); } } // Notify others about the raw ladder information LadderInfoEvent ladderEvent(ladderName, *wolLadder, timeStamp); mOuter->NotifyObservers(ladderEvent); request++; ladderCount++; wolLadder = wolLadder->next; } // Clear the ladder type we received mOuter->mLadderPending &= ~type; // If there are no pending ladders then remove the satisfied requests if ((mOuter->mLadderPending & LADDERTYPE_MASK) == 0) { WWASSERT(ladderCount == mOuter->mLadderPending && "LadderInfo received doesn't match number requested"); for (unsigned int count = 0; count < mOuter->mLadderPending; ++count) { mOuter->mLadderRequests.pop_front(); } mOuter->mLadderPending = 0; } } } void NetUtilObserver::NotifyClanLadderUpdate(const UserList& users, const RefPtr& squad) { const unsigned int userCount = users.size(); for (unsigned int index = 0; index < userCount; ++index) { const RefPtr& user = users[index]; if (user.IsValid() && (user->GetSquadID() == squad->GetID())) { // Notify others that this user received squad information UserEvent event(UserEvent::LadderInfo, user); mOuter->NotifyObservers(event); } } } /****************************************************************************** * * NAME * NetUtilObserver::OnGameresSent * * DESCRIPTION * * INPUTS * * RESULT * ******************************************************************************/ STDMETHODIMP NetUtilObserver::OnGameresSent(HRESULT) { WWDEBUG_SAY(("WOLWARNING: OnGameresSent() not implemented\n")); return S_OK; } /****************************************************************************** * * NAME * NetUtilObserver::OnNewNick * * DESCRIPTION * * INPUTS * * RESULT * ******************************************************************************/ STDMETHODIMP NetUtilObserver::OnNewNick(HRESULT result, LPCSTR message, LPCSTR nickname, LPCSTR password) { if (mOuter == NULL) { WWDEBUG_SAY(("WOLERROR: Session not initialized\n")); WWASSERT(mOuter && "Session not initialized"); return S_OK; } if (FAILED(result)) { WWDEBUG_SAY(("WOLERROR: OnNewNick '%s'\n", GetNetUtilErrorString(result))); return S_OK; } RefPtr login; if (result == S_OK) { login = LoginInfo::Create(nickname, password, true); } NewLoginInfoEvent event(login, message); mOuter->NotifyObservers(event); return S_OK; } /****************************************************************************** * * NAME * NetUtilObserver::OnAgeCheck * * DESCRIPTION * * INPUTS * * RESULT * ******************************************************************************/ STDMETHODIMP NetUtilObserver::OnAgeCheck(HRESULT result, int years, int consent) { if (mOuter == NULL) { WWDEBUG_SAY(("WOLERROR: Session not initialized\n")); WWASSERT(mOuter && "Session not initialized"); return S_OK; } if (FAILED(result)) { WWDEBUG_SAY(("WOLERROR: OnAgeCheck '%s'\n", GetNetUtilErrorString(result))); return S_OK; } AgeCheckEvent event(years, consent != 0); mOuter->NotifyObservers(event); return S_OK; } /****************************************************************************** * * NAME * NetUtilObserver::OnWDTState * * DESCRIPTION * * INPUTS * * RESULT * ******************************************************************************/ STDMETHODIMP NetUtilObserver::OnWDTState(HRESULT result, unsigned char* , int ) { WWDEBUG_SAY(("WOLWARNING: OnWDTState not implemented\n")); return S_OK; } /****************************************************************************** * * NAME * NetUtilObserver::OnHighscore * * DESCRIPTION * * INPUTS * * RESULT * ******************************************************************************/ STDMETHODIMP NetUtilObserver::OnHighscore(HRESULT result, WOL::Highscore* list, int count, long time, int keyRung) { WWDEBUG_SAY(("WOLWARNING: OnHighscore not implemented\n")); return S_OK; } } // namespace WWOnline