/*
** 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 .
*/
/******************************************************************************
*
* NAME
* $Archive: /Commando/Code/WWOnline/WOLSession.cpp $
*
* DESCRIPTION
* WOLSession is the entryway to Westwood Online. An object of this type
* must exist in order to do anything WOL related. There should only be one
* instance of this; retrieve it via GetInstance.
*
* PROGRAMMER
* Steve Clinard & Denzil E. Long, Jr.
* $Author: Steve_t $
*
* VERSION INFO
* $Revision: 79 $
* $Modtime: 2/08/02 11:31a $
*
******************************************************************************/
#include
#include "WOLSession.h"
#include "WOLChatObserver.h"
#include "WOLNetUtilObserver.h"
#include "WOLProduct.h"
#include "WOLConnect.h"
#include "WOLServer.h"
#include "WaitCondition.h"
#include "WOLErrorUtil.h"
#include
#include
#ifdef _MSC_VER
#pragma warning (push,3)
#endif
#include "systimer.h"
#ifdef _MSC_VER
#pragma warning (pop)
#endif
namespace WWOnline {
RefPtr Session::_mInstance;
/******************************************************************************
*
* NAME
* Session::GetInstance
*
* DESCRIPTION
* Retrieve instance to Westwood Online session
*
* INPUTS
* Create - True if session should be created.
*
* RESULT
* Session - Session instance
*
******************************************************************************/
RefPtr Session::GetInstance(bool okToCreate)
{
if (okToCreate && !_mInstance.IsValid())
{
_mInstance = new Session;
if (_mInstance->FinalizeCreate() == false)
{
_mInstance.Release();
}
}
return _mInstance;
}
/******************************************************************************
*
* NAME
* Session::Session
*
* DESCRIPTION
* Default constructor
*
* INPUTS
* NONE
*
* RESULT
* NONE
*
******************************************************************************/
Session::Session() :
mChatCookie(0),
mNetUtilCookie(0),
mCurrentConnectionStatus(ConnectionDisconnected),
mAutoRequestFlags(0),
mLadderPending(0),
mPingsPending(0),
mPingEnable(1),
mLastUserDataRequestTime(0),
mRequestedChannelType(-1),
mRequestingServerList(false),
mIgnoreServerLists(false),
mIsInsider(false),
mServerTime(0)
{
WWDEBUG_SAY(("WOL: Session instantiated\n"));
// Initailize COM
HRESULT hr = CoInitialize(NULL);
if (FAILED(hr))
{
WWDEBUG_SAY(("WOLERROR: CoInitialize() failed!\n"));
}
WWASSERT(SUCCEEDED(hr) && "CoInitialize() failed!");
}
/******************************************************************************
*
* NAME
* Session::FinalizeCreate
*
* DESCRIPTION
* Finalize session creation
*
* INPUTS
* NONE
*
* RESULT
* Success - True if successful.
*
******************************************************************************/
bool Session::FinalizeCreate(void)
{
WWDEBUG_SAY(("WOL: Session Create\n"));
Reset();
//---------------------------------------------------------------------------
// Create chat object
//---------------------------------------------------------------------------
WWDEBUG_SAY(("WOL: Creating IID_IChat object\n"));
WOL::IChat* chatObject = NULL;
HRESULT hr = CoCreateInstance(WOL::CLSID_Chat, NULL, CLSCTX_INPROC_SERVER,
WOL::IID_IChat, (void**)&chatObject);
if (FAILED(hr))
{
WWDEBUG_SAY(("WOLERROR: Failed to create IID_IChat\n"));
return false;
}
if (chatObject)
{
mChat = chatObject;
// Create chat events observer
WWDEBUG_SAY(("WOL: Creating chat events observer\n"));
CComPtr chatEvents;
chatEvents.Attach(new ChatObserver);
if (chatEvents == NULL)
{
WWDEBUG_SAY(("WOLERROR: Failed to create IChatEvents observer\n"));
return false;
}
chatEvents->Init(*this);
hr = mChat.Advise(chatEvents, WOL::IID_IChatEvent, &mChatCookie);
if (FAILED(hr))
{
WWDEBUG_SAY(("WOLERROR: Failed to advise IChatEvents observer\n"));
return false;
}
mChatEvents = chatEvents;
}
//---------------------------------------------------------------------------
// Create netutil object
//---------------------------------------------------------------------------
WWDEBUG_SAY(("WOL: Creating IID_INetUtil object\n"));
WOL::INetUtil* utilObject = NULL;
hr = CoCreateInstance(WOL::CLSID_NetUtil, NULL, CLSCTX_INPROC_SERVER,
WOL::IID_INetUtil, (void **)&utilObject);
if (FAILED(hr))
{
WWDEBUG_SAY(("WOLERROR: Failed to create IID_INetUtil\n"));
return false;
}
if (utilObject)
{
mNetUtil = utilObject;
// Create net utility events observer
WWDEBUG_SAY(("WOL: Creating netutil events observer\n"));
CComPtr utilEvents;
utilEvents.Attach(new NetUtilObserver);
if (utilEvents == NULL)
{
WWDEBUG_SAY(("WOLERROR: Failed to create INetUtilEvents observer\n"));
return false;
}
utilEvents->Init(*this);
hr = mNetUtil.Advise(utilEvents, WOL::IID_INetUtilEvent, &mNetUtilCookie);
if (FAILED(hr))
{
WWDEBUG_SAY(("WOLERROR: Failed to advise INetUtilEvents observer\n"));
return false;
}
mNetUtilEvents = utilEvents;
}
//---------------------------------------------------------------------------
// Setup for current product
//---------------------------------------------------------------------------
WWDEBUG_SAY(("WOL: Setting product SKU\n"));
RefPtrConst product = Product::Current();
if (!product.IsValid())
{
WWDEBUG_SAY(("WOLERROR: WOLProduct not initialized\n"));
return false;
}
const char* regPath = product->GetRegistryPath();
hr = mChat->SetAttributeValue("RegPath", regPath);
if (FAILED(hr))
{
WWDEBUG_SAY(("WOLERROR: SetAttributeValue(RegPath) HRESULT = %s\n", GetChatErrorString(hr)));
return false;
}
unsigned int sku = product->GetSKU();
hr = mChat->SetProductSKU(sku);
if (FAILED(hr))
{
WWDEBUG_SAY(("WOLERROR: SetProductSKU() HRESULT = %s\n", GetChatErrorString(hr)));
return false;
}
// Enable OnChannelTopic() to be called after OnChannelJoin()
hr = mChat->SetAttributeValue("AutoTopic", "true");
if (FAILED(hr))
{
WWDEBUG_SAY(("WOLERROR: SetAttributeValue(AutoTopic) HRESULT = %s\n", GetChatErrorString(hr)));
return false;
}
mPingRequests.reserve(8);
mUsers.reserve(32);
return true;
}
/******************************************************************************
*
* NAME
* Session::~Session
*
* DESCRIPTION
* Destructor
*
* INPUTS
* NONE
*
* RESULT
* NONE
*
******************************************************************************/
Session::~Session()
{
WWDEBUG_SAY(("WOL: Session destructing\n"));
Reset();
if (mIGRObject)
{
WWDEBUG_SAY(("WOL: Releasing IID_IIGROptions object\n"));
mIGRObject.Release();
}
if (mNetUtil)
{
WWDEBUG_SAY(("WOL: Releasing IID_INetUtil object\n"));
AtlUnadvise(mNetUtil, WOL::IID_INetUtilEvent, mNetUtilCookie);
mNetUtil.Release();
}
if (mChat)
{
WWDEBUG_SAY(("WOL: Releasing IID_IChat object\n"));
AtlUnadvise(mChat, WOL::IID_IChatEvent, mChatCookie);
mChat.Release();
}
CoUninitialize();
}
/******************************************************************************
*
* NAME
* Session::ReleaseReference
*
* DESCRIPTION
* Release reference count
*
* INPUTS
* NONE
*
* RESULT
* NONE
*
******************************************************************************/
void Session::ReleaseReference(void)
{
RefCounted::ReleaseReference();
// If there is only the singleton reference then release the object.
if (ReferenceCount() == 1)
{
_mInstance.Release();
}
}
/******************************************************************************
*
* NAME
* Session::Reset
*
* DESCRIPTION
* Reset the WWOnline session
*
* INPUTS
*
* RESULT
* NONE
*
******************************************************************************/
void Session::Reset(void)
{
ClearServers();
SquadData::Reset();
mChatChannels.clear();
mGameChannels.clear();
mPatchFiles.clear();
mUsers.clear();
mBuddies.clear();
mLocatePendingUsers.clear();
mLocatingUser.Release();
}
/******************************************************************************
*
* NAME
* Session::Process
*
* DESCRIPTION
* Perform periodic processing
*
* INPUTS
* NONE
*
* RESULT
*
******************************************************************************/
bool Session::Process(void)
{
if (mCurrentConnectionStatus == ConnectionConnected)
{
MakeLocateUserRequests();
}
DWORD theTime = TIMEGETTIME();
if (theTime < mLastUserDataRequestTime)
{
mLastUserDataRequestTime = theTime;
}
if (theTime >= (mLastUserDataRequestTime + 1000))
{
mLastUserDataRequestTime = theTime;
MakeSquadRequests();
MakeLocaleRequests();
MakeTeamRequests();
MakeLadderRequests();
}
MakePingRequests();
mChat->PumpMessages();
mNetUtil->PumpMessages();
return false;
}
/******************************************************************************
*
* NAME
* Session::GetNewServerList
*
* DESCRIPTION
* Get a new server list.
*
* INPUTS
* NONE
*
* RESULT
* Wait - Wait condition to process for serverlist.
*
******************************************************************************/
RefPtr Session::GetNewServerList(void)
{
return ServerListWait::Create(this);
}
/******************************************************************************
*
* NAME
* Session::RequestServerList
*
* DESCRIPTION
* Submit a request for server list.
*
* INPUTS
* NONE
*
* RESULT
* Success - True if request successful.
*
******************************************************************************/
bool Session::RequestServerList(bool ignore)
{
RefPtrConst product(Product::Current());
if (!product.IsValid())
{
WWASSERT(product.IsValid() && "WOLERROR: Product not initialized.");
return false;
}
mIgnoreServerLists = ignore;
mRequestingServerList = !ignore;
unsigned int sku = product->GetLanguageSKU();
unsigned int version = product->GetVersion();
HRESULT hr = mChat->RequestServerList(sku, version, "NoUser", "NoPass", 30000);
if (FAILED(hr))
{
WWDEBUG_SAY(("WOLERROR: RequestServerList() HRESULT = %s\n", GetChatErrorString(hr)));
}
return SUCCEEDED(hr);
}
/******************************************************************************
*
* NAME
* Session::LoginServer
*
* DESCRIPTION
* Log user onto server.
*
* INPUTS
* Server - Server to logon to.
* Login - Login information
*
* RESULT
* Wait - Wait condition to process for logging in.
*
******************************************************************************/
RefPtr Session::LoginServer(const RefPtr& server,
const RefPtr& login)
{
if (server.IsValid())
{
WWDEBUG_SAY(("WOL: LoginServer connecting to '%s'\n", server->GetName()));
}
else
{
WWDEBUG_SAY(("WOL: LoginServer disconnecting\n"));
}
//---------------------------------------------------------------------------
// If this is the current server, check state of connection
//---------------------------------------------------------------------------
if (mCurrentServer == server)
{
if (server.IsValid())
{
// If already connected to this server, just return with success.
// If connecting then wait for connection to finish.
if (ConnectionConnected == mCurrentConnectionStatus)
{
WWDEBUG_SAY(("WOL: LoginServer already connected.\n"));
RefPtr wait = SingleWait::Create(WOLSTRING("WOL_CONNECTING"));
wait->EndWait(WaitCondition::ConditionMet, WOLSTRING("WOL_CONNECTED"));
return wait;
}
else if (ConnectionConnecting == mCurrentConnectionStatus)
{
WWDEBUG_SAY(("WOL: LoginServer already connecting.\n"));
return EventValueWait::CreateAndObserve(*this, ConnectionConnected, WOLSTRING("WOL_CONNECTING"));
}
}
else
{
// If already disconnected then just return with success. If disconnecting
// then wait for disconnect to finish.
if (ConnectionDisconnected == mCurrentConnectionStatus)
{
WWDEBUG_SAY(("WOL: LoginServer already disconnected.\n"));
RefPtr wait = SingleWait::Create(WOLSTRING("WOL_DISCONNECTING"));
wait->EndWait(WaitCondition::ConditionMet, WOLSTRING("WOL_DISCONNECTED"));
return wait;
}
else if (ConnectionDisconnecting == mCurrentConnectionStatus)
{
WWDEBUG_SAY(("WOL: LoginServer already disconnecting.\n"));
return EventValueWait::CreateAndObserve(*this, ConnectionDisconnected, WOLSTRING("WOL_DISCONNECTING"));
}
}
}
// Clean up on server switch or disconnect
if (ConnectionConnected == mCurrentConnectionStatus)
{
WWDEBUG_SAY(("WOL: LoginServer disconnect cleanup.\n"));
mCurrentChannelStatus = ChannelLeft;
ChannelEvent chanEvent(mCurrentChannelStatus, mCurrentChannel);
NotifyObservers(chanEvent);
mCurrentChannel.Release();
mPendingChannel.Release();
mChatChannels.clear();
ChannelListEvent chatListEvent(ChannelListEvent::NewList, mChatChannels, 0);
NotifyObservers(chatListEvent);
RefPtrConst product = Product::Current();
WWASSERT(product.IsValid());
mGameChannels.clear();
ChannelListEvent gameListEvent(ChannelListEvent::NewList, mGameChannels, product->GetGameCode());
NotifyObservers(gameListEvent);
}
//---------------------------------------------------------------------------
// Process change of server connection
//---------------------------------------------------------------------------
RefPtr serverWait = SerialWait::Create();
// If connecting or disconnecting, let that finish
if (ConnectionDisconnecting == mCurrentConnectionStatus)
{
WWDEBUG_SAY(("WOL: LoginServer wait for disconnect to finish.\n"));
RefPtr< EventValueWait > finish = EventValueWait::CreateAndObserve(*this, ConnectionDisconnected, WOLSTRING("WOL_DISCONNECTING"));
serverWait->Add(finish);
}
else if (ConnectionConnecting == mCurrentConnectionStatus)
{
WWDEBUG_SAY(("WOL: LoginServer wait for connect to finish.\n"));
RefPtr< EventValueWait > finish = EventValueWait::CreateAndObserve(*this, ConnectionConnected, WOLSTRING("WOL_DISCONNECTING"));
serverWait->Add(finish);
}
// If connected or connecting to another server then disconnect from that server
// before connecting to the new one.
if (mCurrentServer.IsValid() || (mPendingServer.IsValid() && (mPendingServer != server)))
{
WWDEBUG_SAY(("WOL: LoginServer changing servers.\n"));
if ((ConnectionConnected == mCurrentConnectionStatus) || (ConnectionConnecting == mCurrentConnectionStatus))
{
WWDEBUG_SAY(("WOL: LoginServer disconnect from current server.\n"));
RefPtr disconnect = DisconnectWait::Create(this);
serverWait->Add(disconnect);
}
}
mPendingServer = server;
mPendingLogin = login;
// Server == NULL is just a logout
if (!server.IsValid())
{
return serverWait;
}
if (!login.IsValid())
{
WWASSERT(login.IsValid());
return serverWait;
}
// Finally, connect to desired server. Request to connect is embedded in Wait_Beginning
WWDEBUG_SAY(("WOL: LoginServer connect to new server.\n"));
RefPtr connect = ConnectWait::Create(this, server, login);
serverWait->Add(connect);
return serverWait;
}
/******************************************************************************
*
* NAME
* Session::Logout
*
* DESCRIPTION
* Log current user off.
*
* INPUTS
* NONE
*
* RESULT
* Wait - Wait condition to process for logout.
*
******************************************************************************/
RefPtr Session::Logout(void)
{
WWDEBUG_SAY(("WOL: Logout\n"));
return LoginServer(RefPtr(), RefPtr());
}
/******************************************************************************
*
* NAME
* Session::ClearServers
*
* DESCRIPTION
* Clear internal server lists
*
* INPUTS
* NONE
*
* RESULT
* NONE
*
******************************************************************************/
void Session::ClearServers(void)
{
mRequestingServerList = false;
mIgnoreServerLists = false;
mIRCServers.clear();
mLadderServer.Release();
mGameResultsServer.Release();
mWDTServer.Release();
mMGLServers.clear();
mPingServers.clear();
}
/******************************************************************************
*
* NAME
* Session::EnableProgressiveChannelList
*
* DESCRIPTION
* Enable / Disable the receipt of progressive channel lists.
*
* INPUTS
* Enable - True to enable; False to disable.
*
* RESULT
* Success - True if operation is successful.
*
******************************************************************************/
bool Session::EnableProgressiveChannelList(bool enable)
{
const char* onoff = ((enable == true) ? "true" : "false");
HRESULT hr = mChat->SetAttributeValue("IncrementalChannelLists", onoff);
return SUCCEEDED(hr);
}
/******************************************************************************
*
* NAME
* Session::RequestChannelList
*
* DESCRIPTION
*
* INPUTS
* Type - Channel type to request.
* AutoPing -
*
* RESULT
* Success - True if request successful.
*
******************************************************************************/
bool Session::RequestChannelList(int channelType, bool autoPing)
{
WWDEBUG_SAY(("WOL: RequestChannelList %d\n", channelType));
if (mRequestedChannelType != -1)
{
WWDEBUG_SAY(("WOLERROR: RequestChannelList already pending\n"));
return false;
}
HRESULT hr = mChat->RequestChannelList(channelType, autoPing);
if (SUCCEEDED(hr))
{
mRequestedChannelType = channelType;
return true;
}
WWDEBUG_SAY(("WOLERROR: RequestChannelList() HRESULT = %s\n", GetChatErrorString(hr)));
return false;
}
/******************************************************************************
*
* NAME
* Session::FindChannel
*
* DESCRIPTION
* Find a channel by name.
*
* INPUTS
* Name - Name of channel to look for.
*
* RESULT
* Channel -
*
******************************************************************************/
RefPtr Session::FindChannel(const wchar_t* channelName)
{
if (channelName == NULL)
{
return NULL;
}
if (mCurrentChannel.IsValid())
{
const WideStringClass& name = mCurrentChannel->GetName();
if (name.Compare_No_Case(channelName) == 0)
{
return mCurrentChannel;
}
}
RefPtr channel = FindChatChannel(channelName);
if (!channel.IsValid())
{
channel = FindGameChannel(channelName);
}
return channel;
}
RefPtr Session::FindChannel(const char* channelName)
{
if (channelName == NULL)
{
return NULL;
}
if (mCurrentChannel.IsValid())
{
WOL::Channel& wolChannel = mCurrentChannel->GetData();
if (stricmp(channelName, (const char*)wolChannel.name) == 0)
{
return mCurrentChannel;
}
}
RefPtr channel = FindChatChannel(channelName);
if (!channel.IsValid())
{
channel = FindGameChannel(channelName);
}
return channel;
}
/******************************************************************************
*
* NAME
* Session::JoinChannel
*
* DESCRIPTION
* Join the specified channel.
*
* INPUTS
* Channel - Channel to join.
*
* RESULT
* Wait - Wait condition to process for joining channel
*
******************************************************************************/
RefPtr Session::JoinChannel(const RefPtr& channel,
const wchar_t* password)
{
// If we are not connected then we cannot join a channel.
if (mCurrentConnectionStatus != ConnectionConnected)
{
WWASSERT(mCurrentConnectionStatus == ConnectionConnected);
RefPtr wait = SingleWait::Create(WOLSTRING("WOL_CHANNELJOIN"));
wait->EndWait(WaitCondition::Error, WOLSTRING("WOL_NOTCONNECTED"));
return wait;
}
// If we are already connected to the requested channel then done.
if (mCurrentChannel == channel)
{
RefPtr wait = SingleWait::Create(WOLSTRING("WOL_CHANNELJOIN"));
wait->EndWait(WaitCondition::ConditionMet, WOLSTRING("WOL_CONNECTED"));
return wait;
}
// Use default product password if one is not provided
if (password == NULL)
{
RefPtrConst product = Product::Current();
WWASSERT(product.IsValid());
password = product->GetChannelPassword();
}
// Set up wait condition for joining channel
mPendingChannel = channel;
RefPtr wait = SerialWait::Create();
if (wait.IsValid())
{
// Leave current channel
if (mCurrentChannel.IsValid())
{
RefPtr leaveWait = LeaveChannelWait::Create(this);
wait->Add(leaveWait);
}
// Join new channel
if (channel.IsValid())
{
RefPtr joinWait = JoinChannelWait::Create(this, channel, password);
wait->Add(joinWait);
}
}
return wait;
}
RefPtr Session::JoinChannel(const wchar_t* chanName, const wchar_t* password, int type)
{
// If we are not connected then we cannot join a channel.
if (mCurrentConnectionStatus != ConnectionConnected)
{
WWASSERT(mCurrentConnectionStatus == ConnectionConnected);
RefPtr wait = SingleWait::Create(WOLSTRING("WOL_CHANNELJOIN"));
wait->EndWait(WaitCondition::Error, WOLSTRING("WOL_NOTCONNECTED"));
return wait;
}
// If we are already connected to the requested channel then done.
if (mCurrentChannel.IsValid())
{
const WideStringClass& curChanName = mCurrentChannel->GetName();
if (curChanName.Compare_No_Case(chanName) == 0)
{
RefPtr wait = SingleWait::Create(WOLSTRING("WOL_CHANNELJOIN"));
wait->EndWait(WaitCondition::ConditionMet, WOLSTRING("WOL_CONNECTED"));
return wait;
}
}
// Use default product password if one is not provided
if (password == NULL)
{
RefPtrConst product = Product::Current();
WWASSERT(product.IsValid());
password = product->GetChannelPassword();
}
// Set up wait condition for joining channel
RefPtr wait = SerialWait::Create();
if (wait.IsValid())
{
// Leave current channel
if (mCurrentChannel.IsValid())
{
RefPtr leaveWait = LeaveChannelWait::Create(this);
wait->Add(leaveWait);
}
if (chanName)
{
// Join new channel
RefPtr joinWait = JoinChannelWait::Create(this, chanName, password, type);
wait->Add(joinWait);
}
}
return wait;
}
/******************************************************************************
*
* NAME
* Session::RequestChannelJoin
*
* DESCRIPTION
*
* INPUTS
* NONE
*
* RESULT
* Success - True if request successful.
*
******************************************************************************/
bool Session::RequestChannelJoin(const RefPtr& channel, const wchar_t* password)
{
if (channel.IsValid() && password)
{
WWDEBUG_SAY(("WOL: RequestChannelJoin C:%S P:%S\n", (const WCHAR*)channel->GetName(), password));
mPendingChannel = channel;
WOL::Channel& wolChannel = channel->GetData();
wcstombs((char*)wolChannel.key, password, sizeof(wolChannel.key));
// If the channel is not in the channel list then mark it as hidden.
RefPtr inList = FindChannel(channel->GetName());
wolChannel.hidden = (inList.IsValid() ? 0 : 1);
HRESULT hr = mChat->RequestChannelJoin(&wolChannel);
if (FAILED(hr))
{
WWDEBUG_SAY(("WOLERROR: RequestChannelJoin() HRESULT = %s\n", GetChatErrorString(hr)));
}
return SUCCEEDED(hr);
}
return false;
}
/******************************************************************************
*
* NAME
* Session::LeaveChannel
*
* DESCRIPTION
*
* INPUTS
* NONE
*
* RESULT
* Wait - Wait condition to process for leaving channel
*
******************************************************************************/
RefPtr Session::LeaveChannel(void)
{
if (!mCurrentChannel.IsValid() || (ChannelJoined != mCurrentChannelStatus))
{
mCurrentChannel.Release();
mCurrentChannelStatus = ChannelLeft;
RefPtr wait = SingleWait::Create(WOLSTRING("WOL_CHANNELLEAVE"));
wait->EndWait(WaitCondition::ConditionMet, WOLSTRING("WOL_CHANNELLEFT"));
return wait;
}
return LeaveChannelWait::Create(this);
}
bool Session::RequestLeaveChannel(void)
{
if (ChannelJoined == mCurrentChannelStatus)
{
HRESULT hr = mChat->RequestChannelLeave();
if (FAILED(hr))
{
WWDEBUG_SAY(("WOLERROR: RequestChannelLeave() failed HRESULT = %s\n", GetChatErrorString(hr)));
return false;
}
mCurrentChannelStatus = ChannelLeaving;
}
return true;
}
/******************************************************************************
*
* NAME
* Session::GetNewChatChannelList
*
* DESCRIPTION
* Request a new chat channel list.
*
* INPUTS
* NONE
*
* RESULT
* Wait - Wait condition to process for chat channel list.
*
******************************************************************************/
RefPtr Session::GetNewChatChannelList(void)
{
return ChannelListWait::Create(this, 0);
}
/******************************************************************************
*
* NAME
* Session::FindChatChannel
*
* DESCRIPTION
* Find a chat channel.
*
* INPUTS
* ChannelName - Name of channel to search for.
*
* RESULT
* Channel -
*
******************************************************************************/
RefPtr Session::FindChatChannel(const wchar_t* channelName)
{
return FindChannelInList(mChatChannels, channelName);
}
RefPtr Session::FindChatChannel(const char* channelName)
{
return FindChannelInList(mChatChannels, channelName);
}
/******************************************************************************
*
* NAME
* Session::GetNewGameChannelList
*
* DESCRIPTION
* Get new game channel list.
*
* INPUTS
* NONE
*
* RESULT
* Wait - Wait condition to process for obtaining new channel list.
*
******************************************************************************/
RefPtr Session::GetNewGameChannelList(void)
{
RefPtrConst product = Product::Current();
if (product.IsValid())
{
int channelType = product->GetGameCode();
return ChannelListWait::Create(this, channelType);
}
return NULL;
}
/******************************************************************************
*
* NAME
* Session::RequestGameChannelList
*
* DESCRIPTION
* Submit request for game channel list.
*
* INPUTS
* NONE
*
* RESULT
* Success - True if request successful.
*
******************************************************************************/
bool Session::RequestGameChannelList(void)
{
RefPtrConst product = Product::Current();
if (!product.IsValid())
{
WWASSERT(product.IsValid());
return false;
}
int channelType = product->GetGameCode();
return RequestChannelList(channelType, false);
}
/******************************************************************************
*
* NAME
* Session::FindGameChannel
*
* DESCRIPTION
* Find a game channel.
*
* INPUTS
* ChannelName - Name of channel to search for.
*
* RESULT
* Channel -
*
******************************************************************************/
RefPtr Session::FindGameChannel(const wchar_t* channelName)
{
return FindChannelInList(mGameChannels, channelName);
}
RefPtr Session::FindGameChannel(const char* channelName)
{
return FindChannelInList(mGameChannels, channelName);
}
/******************************************************************************
*
* NAME
* Session::CreateChannel
*
* DESCRIPTION
* Create a new channel.
*
* INPUTS
* Name - Name of new channel.
* Password - Password for channel.
*
* RESULT
* Wait - Wait condition to process for channel creation.
*
******************************************************************************/
RefPtr Session::CreateChannel(const wchar_t* name, const wchar_t* password, int type)
{
WWDEBUG_SAY(("WOL: CreateChannel '%S' Type %ld\n", name, type));
if (mCurrentConnectionStatus != ConnectionConnected)
{
WWASSERT(mCurrentConnectionStatus == ConnectionConnected);
RefPtr wait = SingleWait::Create(WOLSTRING("WOL_CHANNELCREATE"));
wait->EndWait(WaitCondition::Error, WOLSTRING("WOL_NOTCONNECTED"));
return wait;
}
// If we are already connected to the requested channel then done.
if (mCurrentChannel.IsValid())
{
const WideStringClass& curChanName = mCurrentChannel->GetName();
if (curChanName.Compare_No_Case(name) == 0)
{
RefPtr wait = SingleWait::Create(WOLSTRING("WOL_CHANNELCREATE"));
wait->EndWait(WaitCondition::Error, WOLSTRING("WOL_CONNECTED"));
return wait;
}
}
RefPtr channel = ChannelData::Create(name, password, type);
// Set up wait condition for creating channel
mPendingChannel = channel;
RefPtr wait = SerialWait::Create();
// Leave current channel
if (mCurrentChannel.IsValid())
{
RefPtr leave = LeaveChannelWait::Create(this);
wait->Add(leave);
}
// Create & Join channel
RefPtr create = CreateChannelWait::Create(this, channel, password);
wait->Add(create);
return wait;
}
/******************************************************************************
*
* NAME
* Session::CreateChannel
*
* DESCRIPTION
* Create a new channel
*
* INPUTS
* Channel - Channel data to create from.
* Password - Password for joining channel
*
* RESULT
* Wait - Wait condition to process for creating channel
*
******************************************************************************/
RefPtr Session::CreateChannel(const RefPtr& channel,
const wchar_t* password)
{
WWASSERT(channel.IsValid());
WWDEBUG_SAY(("WOL: CreateChannel '%S'\n", (const WCHAR*)channel->GetName()));
if (mCurrentConnectionStatus != ConnectionConnected)
{
WWASSERT(mCurrentConnectionStatus == ConnectionConnected);
RefPtr wait = SingleWait::Create(WOLSTRING("WOL_CHANNELCREATE"));
wait->EndWait(WaitCondition::Error, WOLSTRING("WOL_NOTCONNECTED"));
return wait;
}
// If we are already connected to the requested channel then done.
if (mCurrentChannel.IsValid())
{
const WideStringClass& curChanName = mCurrentChannel->GetName();
if (curChanName.Compare_No_Case(channel->GetName()) == 0)
{
RefPtr wait = SingleWait::Create(WOLSTRING("WOL_CHANNELCREATE"));
wait->EndWait(WaitCondition::Error, WOLSTRING("WOL_CONNECTED"));
return wait;
}
}
mPendingChannel = channel;
// Set up wait condition for creating channel
RefPtr wait = SerialWait::Create();
// Leave current channel
if (mCurrentChannel.IsValid())
{
RefPtr leave = LeaveChannelWait::Create(this);
wait->Add(leave);
}
// Create & join channel
RefPtr create = CreateChannelWait::Create(this, channel, password);
wait->Add(create);
return wait;
}
/******************************************************************************
*
* NAME
* Session::GetChannelTopic
*
* DESCRIPTION
* Get the topic of the current channel
*
* INPUTS
* NONE
*
* RESULT
* Topic - Channel topic or NULL if no topic or not in channel.
*
******************************************************************************/
const char* Session::GetChannelTopic(void) const
{
const RefPtr& channel = GetCurrentChannel();
if (channel.IsValid())
{
return channel->GetTopic();
}
return NULL;
}
/******************************************************************************
*
* NAME
* Session::SendChannelTopic
*
* DESCRIPTION
* Send the topic of the current channel.
*
* INPUTS
* NONE
*
* RESULT
* True if request successful.
*
******************************************************************************/
bool Session::SendChannelTopic(void)
{
const RefPtr& channel = GetCurrentChannel();
if (channel.IsValid())
{
const char* topic = channel->GetTopic();
if (topic)
{
HRESULT hr = mChat->RequestChannelTopic(topic);
if (SUCCEEDED(hr))
{
return true;
}
WWDEBUG_SAY(("WOLERROR: SendChannelTopic() HRESULT = %s\n", GetChatErrorString(hr)));
}
}
return false;
}
/******************************************************************************
*
* NAME
* Session::SendChannelExtraInfo
*
* DESCRIPTION
* Send the extra info for the current channel.
*
* INPUTS
* NONE
*
* RESULT
* True if request successful.
*
******************************************************************************/
bool Session::SendChannelExtraInfo(void)
{
const RefPtr& channel = GetCurrentChannel();
if (channel.IsValid())
{
const char* exInfo = channel->GetExtraInfo();
if (exInfo)
{
HRESULT hr = mChat->SetChannelExInfo(exInfo);
if (SUCCEEDED(hr))
{
return true;
}
WWDEBUG_SAY(("WOLERROR: ChangeChannelExtraInfo() HRESULT = %s\n", GetChatErrorString(hr)));
}
}
return false;
}
/******************************************************************************
*
* NAME
* Session::RequestUserList
*
* DESCRIPTION
* Request a new user list for the current channel from the IRC server.
*
* INPUTS
* NONE
*
* RESULT
* Success - True if request successful.
*
******************************************************************************/
bool Session::RequestUserList(void)
{
HRESULT hr = mChat->RequestUserList();
if (FAILED(hr))
{
WWDEBUG_SAY(("WOLERROR: RequestUserList() HRESULT = %s\n", GetChatErrorString(hr)));
}
return SUCCEEDED(hr);
}
/******************************************************************************
*
* NAME
* Session::FindUser
*
* DESCRIPTION
* Search for a user in the user list by name.
*
* INPUTS
* Nickname - Name of user to look for.
*
* RESULT
* User - User, if found.
*
******************************************************************************/
RefPtr Session::FindUser(const wchar_t* nickname)
{
// Check the current user first.
if (mCurrentUser.IsValid())
{
const WideStringClass& username = mCurrentUser->GetName();
if (username.Compare_No_Case(nickname) == 0)
{
return mCurrentUser;
}
}
// Check in channels user list
return FindUserInList(nickname, mUsers);
}
/******************************************************************************
*
* NAME
* Session::GetUserOrBuddy
*
* DESCRIPTION
* Search for a user in the user list by name.
*
* INPUTS
* Nickname - Name of user to look for.
*
* RESULT
* User - User, if found.
*
******************************************************************************/
RefPtr Session::GetUserOrBuddy(const wchar_t* name)
{
RefPtr user = FindUser(name);
if (!user.IsValid())
{
user = FindBuddy(name);
}
return user;
}
/******************************************************************************
*
* NAME
* Session::IsCurrentUser
*
* DESCRIPTION
* Check if the specified user is the one logged in.
*
* INPUTS
* User - User to check.
*
* RESULT
* True if specified user is the current one.
*
******************************************************************************/
bool Session::IsCurrentUser(const RefPtr& user) const
{
return user.IsValid() && mCurrentUser.IsValid() &&
user->GetName() == mCurrentUser->GetName();
}
bool Session::IsCurrentUser(const wchar_t* username) const
{
if (username && mCurrentUser.IsValid())
{
const WideStringClass& name = mCurrentUser->GetName();
return (name.Compare_No_Case(username) == 0);
}
return false;
}
/******************************************************************************
*
* NAME
* Session::ChangeCurrentUserLocale
*
* DESCRIPTION
* Change the locale for the user currently logged in.
*
* INPUTS
* Locale - New locale
*
* RESULT
* True if successful.
*
******************************************************************************/
bool Session::ChangeCurrentUserLocale(WOL::Locale locale)
{
if (mCurrentUser.IsValid() && (mCurrentUser->GetLocale() != locale))
{
HRESULT hr = mChat->RequestSetLocale(locale);
if (SUCCEEDED(hr))
{
return true;
}
WWDEBUG_SAY(("WOLERROR: RequestSetLocale() HRESULT = %s\n", GetChatErrorString(hr)));
}
return false;
}
/******************************************************************************
*
* NAME
* Session::SquelchUser
*
* DESCRIPTION
* Enable / disable squelching (ignoring) of other users.
*
* INPUTS
* User - User to squelch / unsquelch
* OnOff - True to squelch; False unsquelch
*
* RESULT
* True if operation successful.
*
******************************************************************************/
bool Session::SquelchUser(const RefPtr& user, bool onoff)
{
if (user.IsValid())
{
WWDEBUG_SAY(("WOL: SquelchUser '%S'\n", user->GetName()));
HRESULT hr = mChat->SetSquelch(&user->GetData(), onoff);
if (SUCCEEDED(hr))
{
user->Squelch(onoff);
return true;
}
WWDEBUG_SAY(("WOLERROR: SetSquelch() HRESULT = %s\n", GetChatErrorString(hr)));
}
return false;
}
/******************************************************************************
*
* NAME
* Session::KickUser
*
* DESCRIPTION
* Make a request to kick a user from the current channel. Kicking is only
* allowed for channel owners.
*
* INPUTS
* User - Name of user to kick from the channel
*
* RESULT
* True if operation successful.
*
******************************************************************************/
bool Session::KickUser(const wchar_t* username)
{
if (username)
{
RefPtr user = FindUserInList(username, mUsers);
if (user.IsValid())
{
WWDEBUG_SAY(("WOL: KickUser '%S'\n", user->GetName()));
HRESULT hr = mChat->RequestUserKick(&user->GetData());
if (SUCCEEDED(hr))
{
return true;
}
WWDEBUG_SAY(("WOLERROR: RequestUserKick() HRESULT = %s\n", GetChatErrorString(hr)));
}
}
return false;
}
/******************************************************************************
*
* NAME
* Session::BanUser
*
* DESCRIPTION
* Make a request to ban / unban a user from the current channel.
* Banning is only allowed for channel owners.
*
* INPUTS
* User - Name of user to ban / unban from the channel
* Ban - True to ban; False to remove ban
*
* RESULT
* True if operation successful.
*
******************************************************************************/
bool Session::BanUser(const wchar_t* username, bool banned)
{
if (username)
{
WWDEBUG_SAY(("WOL: BanUser '%S'\n", username));
char ansiName[64];
wcstombs(ansiName, username, sizeof(ansiName));
HRESULT hr = mChat->RequestChannelBan(ansiName, banned);
if (SUCCEEDED(hr))
{
return true;
}
WWDEBUG_SAY(("WOLERROR: RequestChannelBan() HRESULT = %s\n", GetChatErrorString(hr)));
}
return false;
}
/******************************************************************************
*
* NAME
* Session::PageUser
*
* DESCRIPTION
* Send page message to the specified user.
*
* INPUTS
* User - User to page.
* Message - Message to send.
*
* RESULT
* True if operation successful.
*
******************************************************************************/
bool Session::PageUser(const wchar_t* username, const wchar_t* message)
{
if ((username && wcslen(username)) && (message && wcslen(message)))
{
WOL::User wolUser;
memset((void*)&wolUser, 0, sizeof(wolUser));
wcstombs((char*)&wolUser.name, username, sizeof(wolUser.name));
wolUser.name[sizeof(wolUser.name) - 1] = 0;
if (IsAnsiText(message))
{
char ansiMessage[1024];
wcstombs(ansiMessage, message, sizeof(ansiMessage));
HRESULT hr = mChat->RequestPage(&wolUser, ansiMessage);
if (FAILED(hr))
{
WWDEBUG_SAY(("WOLERROR: RequestPage() HRESULT = %s\n", GetChatErrorString(hr)));
}
return SUCCEEDED(hr);
}
else
{
HRESULT hr = mChat->RequestUnicodePage(&wolUser, message);
if (FAILED(hr))
{
WWDEBUG_SAY(("WOLERROR: RequestUnicodePage() HRESULT = %s\n", GetChatErrorString(hr)));
}
return SUCCEEDED(hr);
}
}
return false;
}
/******************************************************************************
*
* NAME
* Session::RequestLocateUser
*
* DESCRIPTION
* Locate the user on Westwood Online.
*
* INPUTS
* Name - Login name of the user to locate.
*
* RESULT
* True if operation successful.
*
******************************************************************************/
void Session::RequestLocateUser(const wchar_t* name)
{
if (name && wcslen(name))
{
// If we already have the user then submit it, otherwise create a new user entry.
RefPtr user = GetUserOrBuddy(name);
if (!user.IsValid())
{
user = UserData::Create(name);
}
RequestLocateUser(user);
}
}
void Session::RequestLocateUser(const RefPtr& user)
{
if (user.IsValid())
{
// If the user is already pending then do not add him again.
RefPtr pending = FindUserInList(user->GetName(), mLocatePendingUsers);
if (pending.IsValid())
{
return;
}
// Is user currently being located then done
if (mLocatingUser.IsValid())
{
const WideStringClass& userName = user->GetName();
if (userName.Compare_No_Case(mLocatingUser->GetName()) == 0)
{
return;
}
}
// Add the user to the pending locate list
mLocatePendingUsers.push_back(user);
}
}
/******************************************************************************
*
* NAME
* Session::MakeLocateUserRequests
*
* DESCRIPTION
* Make a request to the server to locate users.
*
* INPUTS
* NONE
*
* RESULT
* NONE
*
******************************************************************************/
void Session::MakeLocateUserRequests(void)
{
if (!mLocatePendingUsers.empty() && !mLocatingUser.IsValid())
{
mLocatingUser = mLocatePendingUsers[0];
mLocatePendingUsers.erase(mLocatePendingUsers.begin());
HRESULT hr = mChat->RequestFind(&mLocatingUser->GetData());
if (FAILED(hr))
{
WWDEBUG_SAY(("WOLERROR: RequestFind() HRESULT = %s\n", GetChatErrorString(hr)));
}
}
}
/******************************************************************************
*
* NAME
* Session::RequestUserLocale
*
* DESCRIPTION
* Make a request to query a users locale. (IE: US, Germany, Korea)
*
* INPUTS
* Name - Name of user to query locale for.
*
* RESULT
* NONE
*
******************************************************************************/
void Session::RequestUserLocale(const wchar_t* username)
{
if (username && (wcslen(username) > 0))
{
// Make sure the user is not already in the list.
const unsigned int count = mLocaleRequests.size();
for (unsigned int index = 0; index < count; ++index)
{
if (mLocaleRequests[index].Compare_No_Case(username) == 0)
{
return;
}
}
mLocaleRequests.push_back(username);
}
}
/******************************************************************************
*
* NAME
* Session::MakeLocaleRequests
*
* DESCRIPTION
* Make request to server to query user locales.
*
* INPUTS
* NONE
*
* RESULT
* NONE
*
******************************************************************************/
void Session::MakeLocaleRequests(void)
{
if (!mLocaleRequests.empty())
{
const unsigned int count = min(10, mLocaleRequests.size());
WOL::User* users = new WOL::User[count];
WWASSERT(users && "Failed to create temporary users array");
if (users)
{
for (unsigned int index = 0; index < count; ++index)
{
WideStringClass& username = mLocaleRequests[index];
WWDEBUG_SAY(("WOL: Requesting locale for '%S'\n", (const WCHAR*)username));
WOL::User& user = users[index];
wcstombs((char*)user.name, username, sizeof(user.name));
user.name[sizeof(user.name) - 1] = 0;
if (index == (count - 1))
{
user.next = NULL;
}
else
{
user.next = &users[index + 1];
}
}
HRESULT hr = mChat->RequestUserLocale(users);
// If request was successful then remove from the pending list.
if (SUCCEEDED(hr))
{
LocaleRequestColl::iterator first = mLocaleRequests.begin();
mLocaleRequests.erase(first, first + count);
}
else
{
WWDEBUG_SAY(("WOLERROR: RequestUserLocale() HRESULT = %s\n", GetChatErrorString(hr)));
}
delete []users;
}
}
}
/******************************************************************************
*
* NAME
* Session::RequestSquadInfoByID
*
* DESCRIPTION
* Request information about a squad by Squad ID.
*
* INPUTS
* SquadID - Identifier of squad
*
* RESULT
* NONE
*
******************************************************************************/
void Session::RequestSquadInfoByID(unsigned long squadID)
{
if (squadID != 0)
{
// MAGICK NUMBER -- itoa handles up to 33 digit numbers
wchar_t idString[34];
// MAGICK NUMBER - 10 - squadID is base 10.
_itow(squadID, idString, 10);
// Only add a request that is not already pending.
const unsigned int count = mSquadRequests.size();
for (unsigned int index = 0; index < count; ++index)
{
if (mSquadRequests[index] == idString)
{
return;
}
}
mSquadRequests.push_back(idString);
}
}
/******************************************************************************
*
* NAME
* Session::RequestSquadInfoByMemberName
*
* DESCRIPTION
* Request information about a squad by Member Name.
*
* INPUTS
* memberName - user name of a member
*
* RESULT
* NONE
*
******************************************************************************/
void Session::RequestSquadInfoByMemberName(const wchar_t* memberName)
{
if (memberName && (wcslen(memberName) > 0))
{
// Only add a request that is not already pending.
for (unsigned int index = 0; index < mSquadRequests.size(); index++)
{
if (mSquadRequests[index].Compare_No_Case(memberName) == 0)
{
return;
}
}
mSquadRequests.push_back(memberName);
}
}
/******************************************************************************
*
* NAME
* Session::MakeSquadRequests
*
* DESCRIPTION
* Make request to server to query squad information.
*
* INPUTS
* NONE
*
* RESULT
* NONE
*
******************************************************************************/
void Session::MakeSquadRequests(void)
{
if (!mSquadRequests.empty() && mSquadPending.empty())
{
// Send up to ten requests at a time.
unsigned int count = min(10, mSquadRequests.size());
// Send each request in turn,
for (unsigned int index = 0; index < count; ++index)
{
const WideStringClass& request = mSquadRequests[index];
HRESULT hr = E_FAIL;
// Check to see if this is an ID or a name
// - names can't have the first character be a number so this works.
wchar_t firstChar = request[0];
if (iswdigit(firstChar))
{
unsigned int squadID = _wtoi(request);
WWDEBUG_SAY(("WOL: SquadInfo requested for ID %ld\n", squadID));
hr = mChat->RequestSquadInfo(squadID);
}
else
{
StringClass name(0, true);
request.Convert_To(name);
WWDEBUG_SAY(("WOL: SquadInfo requested for '%s'\n", (const char*)name));
hr = mChat->RequestSquadByName(name);
}
if (FAILED(hr))
{
WWDEBUG_SAY(("WOLERROR: RequestSquadInfo() HRESULT = %s\n", GetChatErrorString(hr)));
break;
}
mSquadPending.push_back(request);
}
SquadRequestColl::iterator first = mSquadRequests.begin();
mSquadRequests.erase(first, first + index);
}
}
/******************************************************************************
*
* NAME
* Session::RequestTeamInfo
*
* DESCRIPTION
* Make a request to query a users team information.
*
* INPUTS
* Name - Name of user to request team information for.
*
* RESULT
* NONE
*
******************************************************************************/
void Session::RequestTeamInfo(const wchar_t* username)
{
if (username && (wcslen(username) > 0))
{
for (unsigned int index = 0; index < mTeamRequests.size(); index++)
{
if (mTeamRequests[index].Compare_No_Case(username) == 0)
{
return;
}
}
mTeamRequests.push_back(username);
}
}
/******************************************************************************
*
* NAME
* Session::MakeTeamRequests
*
* DESCRIPTION
* Send team requests to the server.
*
* INPUTS
* NONE
*
* RESULT
* NONE
*
******************************************************************************/
void Session::MakeTeamRequests(void)
{
if (!mTeamRequests.empty())
{
WWDEBUG_SAY(("WOL: Requesting team information\n"));
unsigned int count = min(10, mTeamRequests.size());
WOL::User* users = new WOL::User[count];
WWASSERT(users && "Failed to create temporary users array");
if (users)
{
for (unsigned int index = 0; index < count; index++)
{
WOL::User& user = users[index];
WideStringClass& username = mTeamRequests[index];
wcstombs((char*)user.name, username.Peek_Buffer(), sizeof(user.name));
user.name[sizeof(user.name) - 1] = 0;
if (index == count - 1)
{
user.next = NULL;
}
else
{
user.next = users + index + 1;
}
}
HRESULT hr = mChat->RequestUserTeam(users);
// If request was successful then remove from the pending list.
if (SUCCEEDED(hr))
{
TeamRequestColl::iterator first = mTeamRequests.begin();
mTeamRequests.erase(first, first + count);
}
else
{
WWDEBUG_SAY(("WOL: HRESULT = %s\n", GetChatErrorString(hr)));
}
delete []users;
}
}
}
/******************************************************************************
*
* NAME
* Session::RequestLadderInfo
*
* DESCRIPTION
* Request a users ladder information.
*
* INPUTS
* User - Name of user to request information for.
* Type - Type of ladder requesting
*
* RESULT
* NONE
*
******************************************************************************/
void Session::RequestLadderInfo(const wchar_t* name, unsigned long type)
{
if (name && (wcslen(name) > 0) && (type & LADDERTYPE_MASK))
{
WideStringClass request(64, true);
request = L"itc:";
// Clan ladder requests are mutually exclusive to other ladder requests
// because it requires the name of the clan, while the other requests use
// the users login name.
if (type & LadderType_Clan)
{
WWASSERT((type & (LadderType_Individual | LadderType_Team)) == 0);
request[2] = L'C';
}
else
{
if (type & LadderType_Individual)
{
request[0] = L'I';
}
if (type & LadderType_Team)
{
request[1] = L'T';
}
}
// Append the name to the request
request += name;
// Check if the request is already in the list.
LadderRequestList::iterator pending = mLadderRequests.begin();
while (pending != mLadderRequests.end())
{
if (request.Compare_No_Case(*pending) == 0)
{
return;
}
pending++;
}
WWDEBUG_SAY(("WOL: LadderInfo request added '%S'.\n", request));
mLadderRequests.push_back(request);
}
}
/******************************************************************************
*
* NAME
* Session::MakeLadderRequests
*
* DESCRIPTION
* Send ladder requests to the server.
*
* INPUTS
* NONE
*
* RESULT
* NONE
*
******************************************************************************/
void Session::MakeLadderRequests(void)
{
// If there are requests and nothing is pending then send the next request
if (!mLadderRequests.empty() && mLadderPending == 0)
{
RefPtrConst product = Product::Current();
if (mLadderServer.IsValid() && product.IsValid())
{
// Build a single key from up to 10 requests with the same request type.
char keys[256];
keys[0] = 0;
unsigned int count = 0;
LadderRequestList::iterator request = mLadderRequests.begin();
const WideStringClass& firstRequest = *request;
while ((count < 10) && (request != mLadderRequests.end()))
{
// Stop appending requests as soon as we encounter a different type.
if (wcsncmp(firstRequest, *request, 3) != 0)
{
break;
}
// Seperate key entries with a colon
if (count > 0)
{
strcat(keys, ":");
}
// The request name follows the type
WCHAR* widename = wcschr(*request, L':');
WWASSERT(widename != NULL && "Invalid Ladder Request");
widename++;
// Add the request
char name[64];
wcstombs(name, widename, 64);
strcat(keys, name);
// Next request
count++;
request++;
}
WWDEBUG_SAY(("WOL: LadderInfo requested for '%s'.\n", keys));
mLadderPending = count;
const char* hostAddr = mLadderServer->GetHostAddress();
unsigned int port = mLadderServer->GetPort();
unsigned long sku = product->GetLadderSKU();
// Request individual ladder
if (firstRequest[0] == L'I')
{
HRESULT hr = mNetUtil->RequestLadderList(hostAddr, port, keys, sku, -1, 0, 0);
if (FAILED(hr))
{
WWDEBUG_SAY(("WOLERROR: RequestLadderList (Individual) HRESULT = %s\n", GetNetUtilErrorString(hr)));
return;
}
mLadderPending |= LadderType_Individual;
}
// Request team ladder
if (firstRequest[1] == L'T')
{
HRESULT hr = mNetUtil->RequestLadderList(hostAddr, port, keys, (sku | LadderType_Team), -1, 0, 0);
if (FAILED(hr))
{
WWDEBUG_SAY(("WOLERROR: RequestLadderList (Team) HRESULT = %s\n", GetNetUtilErrorString(hr)));
return;
}
mLadderPending |= LadderType_Team;
}
// Request clan ladder
if (firstRequest[2] == L'C')
{
HRESULT hr = mNetUtil->RequestLadderList(hostAddr, port, keys, (sku | LadderType_Clan), -1, 0, 0);
if (FAILED(hr))
{
WWDEBUG_SAY(("WOLERROR: RequestLadderList (Clan) HRESULT = %s\n", GetNetUtilErrorString(hr)));
return;
}
mLadderPending |= LadderType_Clan;
}
}
}
}
/******************************************************************************
*
* NAME
* Session::RequestUserDetails
*
* DESCRIPTION
* Request detailed information about the user (Ladder, Clan, Team, etc...)
*
* INPUTS
* User - User to request details for.
* RequestFlags - Flags indicating the type of information to request.
*
* RESULT
* NONE
*
******************************************************************************/
void Session::RequestUserDetails(const RefPtr& user, unsigned long requestFlags)
{
if (user.IsValid())
{
if (requestFlags & REQUEST_SQUADINFO)
{
// Request squad information for the user.
unsigned int squadID = user->GetSquadID();
RefPtr squad = user->GetSquad();
bool requestSquad = (!squad.IsValid() && (squadID != 0));
bool changedSquad = (squad.IsValid() && (squadID != squad->GetID()));
if (requestSquad || changedSquad)
{
// Look for the squad in the local cache
squad = SquadData::FindByID(squadID);
user->SetSquad(squad);
// Request squad information if it is not in the local cache.
if (!squad.IsValid())
{
RequestSquadInfoByID(squadID);
}
else
{
UserEvent event(UserEvent::SquadInfo, user);
NotifyObservers(event);
}
}
}
// Request locale information for this user.
if ((requestFlags & REQUEST_LOCALE) && (user->GetLocale() == WOL::LOC_UNKNOWN))
{
RequestUserLocale(user->GetName());
}
// Request ladder information for this user.
if ((requestFlags & REQUEST_LADDERINFO) && !user->GetLadder().IsValid())
{
RequestLadderInfo(user->GetName(), LadderType_Team);
}
// Request team information for this user.
if ((requestFlags & REQUEST_TEAMINFO) && (user->GetTeam() == 0))
{
RequestTeamInfo(user->GetName());
}
}
}
/******************************************************************************
*
* NAME
* Session::AutoRequestUserDetails
*
* DESCRIPTION
*
* INPUTS
*
* RESULT
* NONE
*
******************************************************************************/
void Session::AutoRequestUserDetails(const RefPtr& user)
{
RequestUserDetails(user, mAutoRequestFlags);
}
/******************************************************************************
*
* NAME
* Session::RequestBuddyList
*
* DESCRIPTION
* Send a request to the server for our buddy list.
*
* INPUTS
* NONE
*
* RESULT
* Success - True if request successful
*
******************************************************************************/
bool Session::RequestBuddyList(void)
{
HRESULT hr = mChat->RequestBuddyList();
if (FAILED(hr))
{
WWDEBUG_SAY(("WOLERROR: RequestBuddyList() failed HRESULT = %s\n", GetChatErrorString(hr)));
}
return SUCCEEDED(hr);
}
/******************************************************************************
*
* NAME
* Session::AddBuddy
*
* DESCRIPTION
* Send a request to the server to add a buddy to our buddy list.
*
* INPUTS
* Buddy - Name of buddy to add.
*
* RESULT
* True if request sent successfully
*
******************************************************************************/
bool Session::AddBuddy(const wchar_t* buddyName)
{
WOL::User wolUser;
memset((void*)&wolUser, 0, sizeof(wolUser));
wcstombs((char*)&wolUser.name, buddyName, sizeof(wolUser.name));
wolUser.name[sizeof(wolUser.name) - 1] = 0;
HRESULT hr = mChat->RequestBuddyAdd(&wolUser);
if (FAILED(hr))
{
WWDEBUG_SAY(("WOLERROR: RequestBuddyAdd() failed HRESULT = %s\n", GetChatErrorString(hr)));
}
return SUCCEEDED(hr);
}
/******************************************************************************
*
* NAME
* Session::RemoveBuddy
*
* DESCRIPTION
* Send a request to the server to remove a buddy from our buddy list.
*
* INPUTS
* Buddy - Name of buddy to remove.
*
* RESULT
* True if request sent successfully
*
******************************************************************************/
bool Session::RemoveBuddy(const wchar_t* buddyName)
{
WOL::User wolUser;
memset((void*)&wolUser, 0, sizeof(wolUser));
wcstombs((char*)&wolUser.name, buddyName, sizeof(wolUser.name));
wolUser.name[sizeof(wolUser.name) - 1] = 0;
HRESULT hr = mChat->RequestBuddyDelete(&wolUser);
if (FAILED(hr))
{
WWDEBUG_SAY(("WOLERROR: RequestBuddyDelete() failed HRESULT = %s\n", GetChatErrorString(hr)));
}
return SUCCEEDED(hr);
}
/******************************************************************************
*
* NAME
* Session::AllowFindPage
*
* DESCRIPTION
* Set flags to allow / disallow user from being found or paged.
*
* INPUTS
* AllowFind - True to allow user to be found; False to disallow.
* AllowPage - True to allow user to be paged; False to disallow.
*
* RESULT
* True if request successful
*
******************************************************************************/
bool Session::AllowFindPage(bool allowFind, bool allowPage)
{
HRESULT hr = mChat->SetFindPage(allowFind, allowPage);
if (FAILED(hr))
{
WWDEBUG_SAY(("WOLERROR: SetFindPage() failed HRESULT = %s\n", GetChatErrorString(hr)));
}
return SUCCEEDED(hr);
}
/******************************************************************************
*
* NAME
* Session::SetBadLanguageFilter
*
* DESCRIPTION
* Enable / Disable filtering of bad language.
*
* INPUTS
* Filter - True to enable filtering, false to disable.
*
* RESULT
* True if request successful
*
******************************************************************************/
bool Session::SetBadLanguageFilter(bool enabled)
{
HRESULT hr = mChat->SetLangFilter(enabled);
if (FAILED(hr))
{
WWDEBUG_SAY(("WOLERROR: SetLangFilter() failed HRESULT = %s\n", GetChatErrorString(hr)));
}
return SUCCEEDED(hr);
}
/******************************************************************************
*
* NAME
* Session::SendPublicMessage
*
* DESCRIPTION
* Send a public message to all users in our channel.
*
* INPUTS
* Message - Message to send
*
* RESULT
* True if request successful
*
******************************************************************************/
bool Session::SendPublicMessage(const char* message)
{
if (message)
{
return SUCCEEDED(mChat->RequestPublicMessage(message));
}
return false;
}
bool Session::SendPublicMessage(const wchar_t* message)
{
if (message)
{
if (IsAnsiText(message))
{
char ansiMessage[1024];
wcstombs(ansiMessage, message, sizeof(ansiMessage));
return SUCCEEDED(mChat->RequestPublicMessage(ansiMessage));
}
else
{
return SUCCEEDED(mChat->RequestPublicUnicodeMessage(message));
}
}
return false;
}
/******************************************************************************
*
* NAME
* Session::SendPublicAction
*
* DESCRIPTION
* Send an actions to all users in our channel.
*
* INPUTS
* Action - Action message
*
* RESULT
* True if request successful
*
******************************************************************************/
bool Session::SendPublicAction(const char* action)
{
if (action)
{
return SUCCEEDED(mChat->RequestPublicAction(action));
}
return false;
}
bool Session::SendPublicAction(const wchar_t* action)
{
if (action)
{
if (IsAnsiText(action))
{
char ansiAction[1024];
wcstombs(ansiAction, action, sizeof(ansiAction));
return SUCCEEDED(mChat->RequestPublicAction(ansiAction));
}
else
{
return SUCCEEDED(mChat->RequestPublicUnicodeAction(action));
}
}
return false;
}
/******************************************************************************
*
* NAME
* Session::SendPrivateMessage
*
* DESCRIPTION
* Send a private message to a single user.
*
* INPUTS
* Username - Name of user to send private message to.
* Message - Message to send.
*
* RESULT
* True if request successful
*
******************************************************************************/
bool Session::SendPrivateMessage(const wchar_t* username, const wchar_t* message)
{
if (username && message)
{
WOL::User user;
memset(&user, 0, sizeof(user));
wcstombs((char*)user.name, username, sizeof(user.name));
user.name[sizeof(user.name) - 1] = 0;
if (IsAnsiText(message))
{
char ansiMessage[512];
wcstombs(ansiMessage, message, sizeof(ansiMessage));
return SUCCEEDED(mChat->RequestPrivateMessage(&user, ansiMessage));
}
return SUCCEEDED(mChat->RequestPrivateUnicodeMessage(&user, message));
}
return false;
}
/******************************************************************************
*
* NAME
* Session::SendPrivateMessage
*
* DESCRIPTION
* Send a private message to a set of users.
*
* INPUTS
* Users - List of users to send message
* Message - Message to send.
*
* RESULT
* True if request successful
*
******************************************************************************/
bool Session::SendPrivateMessage(const UserList& users, const char* message)
{
if (!users.empty() && message)
{
NativeWOLUserList wolUsers(users);
return SUCCEEDED(mChat->RequestPrivateMessage(wolUsers, message));
}
return false;
}
bool Session::SendPrivateMessage(const UserList& users, const wchar_t* message)
{
if (!users.empty() && message)
{
NativeWOLUserList wolUsers(users);
if (IsAnsiText(message))
{
char ansiMessage[512];
wcstombs(ansiMessage, message, sizeof(ansiMessage));
return SUCCEEDED(mChat->RequestPrivateMessage(wolUsers, ansiMessage));
}
return SUCCEEDED(mChat->RequestPrivateUnicodeMessage(wolUsers, message));
}
return false;
}
/******************************************************************************
*
* NAME
* Session::SendPrivateAction
*
* DESCRIPTION
* Send a private action message to a set of users.
*
* INPUTS
* Users - List of users to send message to.
* Action - Action message to send.
*
* RESULT
* True if request successful
*
******************************************************************************/
bool Session::SendPrivateAction(const UserList& users, const char* action)
{
if (!users.empty() && action)
{
NativeWOLUserList wolUsers(users);
return SUCCEEDED(mChat->RequestPrivateAction(wolUsers, action));
}
return false;
}
bool Session::SendPrivateAction(const UserList& users, const wchar_t* action)
{
if (!users.empty() && action)
{
NativeWOLUserList wolUsers(users);
if (IsAnsiText(action))
{
char ansiAction[1024];
wcstombs(ansiAction, action, sizeof(ansiAction));
return SUCCEEDED(mChat->RequestPrivateAction(wolUsers, ansiAction));
}
return SUCCEEDED(mChat->RequestPrivateUnicodeAction(wolUsers, action));
}
return false;
}
/******************************************************************************
*
* NAME
* Session::SendPublicGameOptions
*
* DESCRIPTION
* Send public game options
*
* INPUTS
* Options - Options to send to all users in the channel.
*
* RESULT
* True if request successful
*
******************************************************************************/
bool Session::SendPublicGameOptions(const char* options)
{
if (options)
{
return SUCCEEDED(mChat->RequestPublicGameOptions(options));
}
return false;
}
/******************************************************************************
*
* NAME
* Session::SendPrivateGameOptions
*
* DESCRIPTION
*
* INPUTS
* Options - Options to send to specific users.
*
* RESULT
* True if request successful
*
******************************************************************************/
bool Session::SendPrivateGameOptions(const UserList& users, const char* options)
{
if (options)
{
for (unsigned int index = 0; index < users.size(); index++)
{
NativeWOLUserList wolUsers(users);
HRESULT hr = mChat->RequestPrivateGameOptions(wolUsers, options);
if (FAILED(hr))
{
return false;
}
}
return true;
}
return false;
}
bool Session::SendPrivateGameOptions(const wchar_t* user, const char* options)
{
if (user && options)
{
WOL::User wolUser;
memset(&wolUser, 0, sizeof(WOL::User));
wcstombs((char*)wolUser.name, user, sizeof(wolUser.name));
wolUser.name[sizeof(wolUser.name) - 1] = 0;
return SUCCEEDED(mChat->RequestPrivateGameOptions(&wolUser, options));
}
return false;
}
/******************************************************************************
*
* NAME
* Session::GetLocaleStrings
*
* DESCRIPTION
* Get a list of the WWOnline locale strings.
*
* INPUTS
* Strings - List to fill with locale name strings.
*
* RESULT
* NONE
*
******************************************************************************/
void Session::GetLocaleStrings(std::vector& localeStrings)
{
localeStrings.clear();
int numLocales = 0;
mChat->GetLocaleCount(&numLocales);
for (int index = 0; index < numLocales; index++)
{
const char* name = NULL;
mChat->GetLocaleString(&name, (WOL::Locale)index);
localeStrings.push_back(name);
}
}
/******************************************************************************
*
* NAME
* Session::SendGameResults
*
* DESCRIPTION
*
* INPUTS
* Packet - Game results packet to send.
* Length - Length of packet in bytes.
*
* RESULT
* True if request sent successfully
*
******************************************************************************/
bool Session::SendGameResults(unsigned char* packet, unsigned long length)
{
if (packet && mGameResultsServer.IsValid())
{
mNetUtil->SetGameResMD5(true);
const char* host = mGameResultsServer->GetHostAddress();
unsigned int port = mGameResultsServer->GetPort();
HRESULT hr = mNetUtil->RequestLargeGameresSend(host, port, packet, length);
if (FAILED(hr))
{
WWDEBUG_SAY(("WOLERROR: RequestLargeGameresSend() failed HRESULT = %s\n", GetChatErrorString(hr)));
}
return SUCCEEDED(hr);
}
return false;
}
/******************************************************************************
*
* NAME
* Session::EnablePinging
*
* DESCRIPTION
*
* INPUTS
*
* RESULT
* NONE
*
******************************************************************************/
void Session::EnablePinging(bool enable)
{
mPingEnable += ((enable == true) ? 1 : -1);
}
/******************************************************************************
*
* NAME
* Session::RequestPing
*
* DESCRIPTION
* Request a ping to the specified address.
*
* INPUTS
* Address - Address to ping (IE: irc.westwood.com)
* Timeout - Maximum time to allow for response.
*
* RESULT
* NONE
*
******************************************************************************/
void Session::RequestPing(const char* address, int timeout)
{
if (address)
{
// If the address is already pending then do not add it again.
std::vector::iterator iter = mPingRequests.begin();
while (iter != mPingRequests.end())
{
if (stricmp(iter->GetHostAddress(), address) == 0)
{
// WWDEBUG_SAY(("WOLWARNING: Ping request already pending %s\n", address));
return;
}
iter++;
}
WWDEBUG_SAY(("WOL: Adding ping request %s\n", address));
RawPing ping(address, 0, timeout);
mPingRequests.push_back(ping);
}
}
/******************************************************************************
*
* NAME
* Session::MakePingRequests
*
* DESCRIPTION
* Send out ping requests
*
* INPUTS
* NONE
*
* RESULT
* NONE
*
******************************************************************************/
void Session::MakePingRequests(void)
{
if (mPingEnable && (mPingRequests.size() > mPingsPending))
{
for (unsigned int index = 0; index < mPingRequests.size(); index++)
{
RawPing* ping = &mPingRequests[index];
if (ping->GetHandle() == -1)
{
int handle = 0;
HRESULT hr = mNetUtil->RequestPing(ping->GetHostAddress(), ping->GetTime(), &handle);
if (SUCCEEDED(hr))
{
WWDEBUG_SAY(("WOL: Pinging '%s' [%d]\n", ping->GetHostAddress(), handle));
ping->SetHandle(handle);
mPingsPending++;
}
}
}
}
}
/******************************************************************************
*
* NAME
* Session::UpdatePingServerTime
*
* DESCRIPTION
* Update the ping time of a ping server.
*
* INPUTS
* Name - Host adddress of server to update.
* Time - Ping time.
*
* RESULT
* NONE
*
******************************************************************************/
void Session::UpdatePingServerTime(const char* name, int time)
{
if (name)
{
// Automatically update ping server times.
const PingServerList& pingers = GetPingServerList();
for (unsigned int index = 0; index < pingers.size(); index++)
{
const char* pinger = pingers[index]->GetHostAddress();
if (stricmp(name, pinger) == 0)
{
if (time < 0)
{
time = INT_MAX;
}
pingers[index]->SetPingTime(time);
break;
}
}
}
}
/******************************************************************************
*
* NAME
* Session::RequestUserIP
*
* DESCRIPTION
* Ask the chat server for a users IP address
*
* INPUTS
* Name of user
*
* RESULT
* NONE
*
******************************************************************************/
bool Session::RequestUserIP(char *user_name)
{
if (user_name)
{
WOL::User wolUser;
memset(&wolUser, 0, sizeof(WOL::User));
strncpy((char*)wolUser.name, user_name, sizeof(wolUser.name));
wolUser.name[sizeof(wolUser.name) - 1] = 0;
return SUCCEEDED(mChat->RequestUserIP(&wolUser));
}
return false;
}
/******************************************************************************
*
* NAME
* Session::RequestInsiderStatus
*
* DESCRIPTION
* Used to ask the chat server for if the currently logged in user is
* an insider or not.
*
* INPUTS
* NONE
*
* RESULT
* NONE
*
******************************************************************************/
void Session::RequestInsiderStatus(void)
{
if (mChat != NULL && mCurrentUser.IsValid ())
{
WOL::User &user = mCurrentUser->GetData();
mChat->RequestInsiderStatus(&user);
}
}
/******************************************************************************
*
* NAME
* Session::RequestServerTime
*
* DESCRIPTION
* Used to ask the chat server for its current time.
*
* INPUTS
* NONE
*
* RESULT
* NONE
*
******************************************************************************/
void Session::RequestServerTime(void)
{
if (mChat != NULL)
{
mChat->RequestServerTime();
}
}
/******************************************************************************
*
* NAME
* Session::GetIGRObject
*
* DESCRIPTION
*
* INPUTS
* NONE
*
* RESULT
*
******************************************************************************/
const CComPtr& Session::GetIGRObject(void)
{
if (mIGRObject == NULL)
{
WWDEBUG_SAY(("WOL: Creating IID_IIGROptions object\n"));
WOL::IIGROptions* igrObject = NULL;
HRESULT hr = CoCreateInstance(WOL::CLSID_IGROptions, NULL, CLSCTX_INPROC_SERVER,
WOL::IID_IIGROptions, (void**)&igrObject);
if (SUCCEEDED(hr))
{
HRESULT hr = igrObject->Init();
if (S_FALSE == hr)
{
WWDEBUG_SAY(("WOLWARNING: IGR settings not found\n"));
}
}
else
{
WWDEBUG_SAY(("WOLERROR: Failed to create IID_IIGROptions\n"));
}
mIGRObject = igrObject;
}
return mIGRObject;
}
/******************************************************************************
*
* NAME
* Session::IsStoreLoginAllowed
*
* DESCRIPTION
* Check if it is okay to save users login information.
*
* INPUTS
* NONE
*
* RESULT
* True if allowed to save login information.
*
******************************************************************************/
bool Session::IsStoreLoginAllowed(void)
{
const CComPtr& igr = GetIGRObject();
if (igr)
{
HRESULT hr = igr->Is_Storing_Nicks_Allowed();
return (hr == S_OK);
}
return true;
}
/******************************************************************************
*
* NAME
* Session::IsAutoLoginAllowed
*
* DESCRIPTION
* Check if it is okay to automatically login users.
*
* INPUTS
* NONE
*
* RESULT
* True if allowed to auto login.
*
******************************************************************************/
bool Session::IsAutoLoginAllowed(void)
{
const CComPtr& igr = GetIGRObject();
if (igr)
{
HRESULT hr = igr->Is_Auto_Login_Allowed();
return (hr == S_OK);
}
return true;
}
/******************************************************************************
*
* NAME
* Session::IsRunRegAppAllowed
*
* DESCRIPTION
*
* INPUTS
* NONE
*
* RESULT
* True if allowed to run registration applet.
*
******************************************************************************/
bool Session::IsRunRegAppAllowed(void)
{
const CComPtr& igr = GetIGRObject();
if (igr)
{
HRESULT hr = igr->Is_Running_Reg_App_Allowed();
return (hr == S_OK);
}
return true;
}
/******************************************************************************
*
* NAME
* ChatAdvisement::Create
*
* DESCRIPTION
* Create a IChatEvents advisement
*
* INPUTS
* Chat - IChat object
* Sink - IChatEvents sink to advise.
*
* RESULT
* Advisement - Instance of advisement
*
******************************************************************************/
RefPtr ChatAdvisement::Create(const CComPtr& chat,
const CComPtr& sink)
{
return new ChatAdvisement(chat, sink);
}
/******************************************************************************
*
* NAME
* ChatAdvisement::ChatAdvisement
*
* DESCRIPTION
* Constructor
*
* INPUTS
* Chat - IChat object
* Sink - IChatEvents sink to advise.
*
* RESULT
* NONE
*
******************************************************************************/
ChatAdvisement::ChatAdvisement(const CComPtr& chat, const CComPtr& sink) :
mChat(chat),
mChatCookie(0)
{
if (mChat && sink)
{
AtlAdvise(mChat, sink, WOL::IID_IChatEvent, &mChatCookie);
}
}
/******************************************************************************
*
* NAME
* ChatAdvisement::~ChatAdvisement
*
* DESCRIPTION
* Destructor
*
* INPUTS
* NONE
*
* RESULT
* NONE
*
******************************************************************************/
ChatAdvisement::~ChatAdvisement()
{
if (mChat)
{
AtlUnadvise(mChat, WOL::IID_IChatEvent, mChatCookie);
}
}
} // namespace WWOnline