openmohaa/code/gamespy/Voice2/Voice2BuddyMFC/Voice2BuddyMFCDlg.cpp
2023-02-04 21:00:01 +01:00

600 lines
16 KiB
C++

// Voice2BuddyMFCDlg.cpp : implementation file
//
#include "stdafx.h"
#include "Voice2BuddyMFC.h"
#include "Voice2BuddyMFCDlg.h"
#include "LoginDlg.h"
#include "SetupDlg.h"
#include "VoiceSessionDlg.h"
#include "../../common/gsAvailable.h"
#include "../../voice2/gv.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
enum V2BuddyMessage
{
V2B_MSG_DOSETUP,
V2B_MSG_DOLOGIN,
V2B_MSG_DOVOICECHAT,
};
#define THINK_TIMER_ID 100
#define THINK_TIMER_DELAY 10
#define GAME_NAME "gmtest"
#define NAMESPACE_GAMESPY_SHARED 0
#define PRODUCTID_GMTEST 1 // can't use 0!
#define V2B_GP_LOCATION "Voice2BuddyMFC"
#define V2B_GP_STATUS_IDLE "Idle" // free to invite
#define V2B_GP_STATUS_CHATTING "Chatting" // actively chatting
#define V2B_GP_INVITE_DECLINED "Voice invitation declined."
#define V2B_GP_INVITE_ACCEPTED "Voice invitation accepted."
// Utility to check buddy status and make sure they're running V2B
BOOL IsBuddyUsingV2B(GPConnection* theConnection, GPProfile theProfileId, GPBuddyStatus* theStatus);
/////////////////////////////////////////////////////////////////////////////
// CAboutDlg dialog used for App About
class CAboutDlg : public CDialog
{
public:
CAboutDlg();
// Dialog Data
//{{AFX_DATA(CAboutDlg)
enum { IDD = IDD_ABOUTBOX };
//}}AFX_DATA
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CAboutDlg)
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
//}}AFX_VIRTUAL
// Implementation
protected:
//{{AFX_MSG(CAboutDlg)
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
//{{AFX_DATA_INIT(CAboutDlg)
//}}AFX_DATA_INIT
}
void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CAboutDlg)
//}}AFX_DATA_MAP
}
BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
//{{AFX_MSG_MAP(CAboutDlg)
// No message handlers
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CVoice2BuddyMFCDlg dialog
CVoice2BuddyMFCDlg::CVoice2BuddyMFCDlg(CWnd* pParent /*=NULL*/)
: CDialog(CVoice2BuddyMFCDlg::IDD, pParent)
{
//{{AFX_DATA_INIT(CVoice2BuddyMFCDlg)
// NOTE: the ClassWizard will add member initialization here
//}}AFX_DATA_INIT
// Note that LoadIcon does not require a subsequent DestroyIcon in Win32
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
void CVoice2BuddyMFCDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CVoice2BuddyMFCDlg)
DDX_Control(pDX, ID_SETUP, m_SetupButton);
DDX_Control(pDX, ID_VOICECHAT, m_VoiceChatButton);
DDX_Control(pDX, IDC_BUDDYLIST, m_BuddyList);
//}}AFX_DATA_MAP
}
BEGIN_MESSAGE_MAP(CVoice2BuddyMFCDlg, CDialog)
//{{AFX_MSG_MAP(CVoice2BuddyMFCDlg)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_BN_CLICKED(ID_SETUP, OnSetup)
ON_BN_CLICKED(ID_VOICECHAT, OnVoiceChat)
ON_BN_CLICKED(IDCANCEL, OnExit)
ON_WM_TIMER()
ON_WM_DESTROY()
ON_LBN_SELCHANGE(IDC_BUDDYLIST, OnSelchangeBuddylist)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CVoice2BuddyMFCDlg message handlers
BOOL CVoice2BuddyMFCDlg::OnInitDialog()
{
CDialog::OnInitDialog();
// Add "About..." menu item to system menu.
// IDM_ABOUTBOX must be in the system command range.
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);
CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != NULL)
{
CString strAboutMenu;
strAboutMenu.LoadString(IDS_ABOUTBOX);
if (!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}
}
// Set the icon for this dialog. The framework does this automatically
// when the application's main window is not a dialog
SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon
// We're not connected yet
m_GP = NULL;
m_Initialized = FALSE;
m_Connected = FALSE;
m_InvitedPlayer = 0;
m_MyProfileId = 0;
// Start network
SocketStartUp();
// Init voice SDK (pre-init, do not start devices yet)
GVBool aResult = gvStartup(m_hWnd);
if (aResult != GVTrue)
{
MessageBox("Failed on gvStartup!");
PostQuitMessage(0);
}
// Set the codec (this sample hard codes it)
gvSetSampleRate(GVRate_16KHz);
aResult = gvSetCodec(GVCodecSuperHighQuality);
if (aResult != GVTrue)
{
MessageBox("Failed on gvSetCodec!");
PostQuitMessage(0);
}
// Disable the "voice chat" button until a buddy is selected
m_VoiceChatButton.EnableWindow(FALSE);
// Clear the setup info
memset(&m_SetupInfo, 0, sizeof(m_SetupInfo));
// Show the setup dialog
PostMessage(WM_USER+1, V2B_MSG_DOSETUP, 0);
return TRUE; // return TRUE unless you set the focus to a control
}
void CVoice2BuddyMFCDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
if ((nID & 0xFFF0) == IDM_ABOUTBOX)
{
CAboutDlg dlgAbout;
dlgAbout.DoModal();
}
else
{
CDialog::OnSysCommand(nID, lParam);
}
}
// If you add a minimize button to your dialog, you will need the code below
// to draw the icon. For MFC applications using the document/view model,
// this is automatically done for you by the framework.
void CVoice2BuddyMFCDlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // device context for painting
SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);
// Center icon in client rectangle
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
// Draw the icon
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialog::OnPaint();
}
}
// The system calls this to obtain the cursor to display while the user drags
// the minimized window.
HCURSOR CVoice2BuddyMFCDlg::OnQueryDragIcon()
{
return (HCURSOR) m_hIcon;
}
void CVoice2BuddyMFCDlg::OnSetup()
{
// Setup and calibrate the voice hardware
CSetupDlg aSetupDlg;
aSetupDlg.m_SetupInfo = &m_SetupInfo;
aSetupDlg.DoModal();
}
void CVoice2BuddyMFCDlg::OnVoiceChat()
{
// Get the selected buddy
int selIndex = m_BuddyList.GetCurSel();
if (selIndex == -1)
return;
// Get the buddy profile
GPProfile aProfile = m_BuddyList.GetItemData(selIndex);
// Make sure the buddy is using V2B
GPBuddyStatus aStatus;
if (FALSE == IsBuddyUsingV2B(&m_GP, aProfile, &aStatus))
{
MessageBox("Buddy is not running V2B.");
return;
}
// Make sure this buddy isn't chatting already
if (strcmp(V2B_GP_STATUS_CHATTING, aStatus.locationString) == 0)
{
MessageBox("Buddy is currently involved in a voice chat.");
return;
}
// Invite the buddy to voice chat
gpInvitePlayer(&m_GP, aProfile, PRODUCTID_GMTEST, NULL);
m_InvitedPlayer = aProfile; // remember who we invited
}
void CVoice2BuddyMFCDlg::OnExit()
{
// TODO: Add your control notification handler code here
CDialog::OnCancel();
}
LRESULT CVoice2BuddyMFCDlg::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
if (message == (WM_USER+1))
{
// Process custom message
switch(wParam)
{
case V2B_MSG_DOSETUP:
{
// Show the setup dialog
OnSetup();
// Pop up the login dialog
PostMessage(WM_USER+1, V2B_MSG_DOLOGIN, 0);
// Start the app timer
SetTimer(THINK_TIMER_ID, THINK_TIMER_DELAY, NULL);
return 0;
}
case V2B_MSG_DOLOGIN:
{
// Show the login dialog
CLoginDlg aDlg;
int result = aDlg.DoModal();
if (result == IDCANCEL)
PostQuitMessage(0);
else
DoLogin(aDlg.m_Email, aDlg.m_Nickname, aDlg.m_Password);
return 0;
}
case V2B_MSG_DOVOICECHAT:
{
// Create the voice session dialog
CVoiceSessionDlg aVoiceSessionDlg;
aVoiceSessionDlg.m_NNCookie = lParam;
aVoiceSessionDlg.m_SetupInfo = m_SetupInfo;
// We use the profileId of the host as the NNCookie
// now, check to see if we're the host
if (m_MyProfileId == aVoiceSessionDlg.m_NNCookie)
aVoiceSessionDlg.m_IsHost = TRUE;
else
aVoiceSessionDlg.m_IsHost = FALSE;
// Run the dialog
aVoiceSessionDlg.DoModal();
return 0;
}
};
} // end custom messages
return CDialog::WindowProc(message, wParam, lParam);
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Utility to check if a buddy is using this app
BOOL IsBuddyUsingV2B(GPConnection* theConnection, GPProfile theProfileId, GPBuddyStatus* theStatus)
{
GPBuddyStatus aTempStatus;
GPBuddyStatus* aStatus;
if (theStatus != NULL)
aStatus = theStatus; // store location in return value
else
aStatus = &aTempStatus; // store location in temp
// get the internal buddy index
int aBuddyIndex;
GPResult aResult = gpGetBuddyIndex(theConnection, theProfileId, &aBuddyIndex);
if (aResult != GP_NO_ERROR)
return FALSE; // not a buddy
// Get the buddy status
aResult = gpGetBuddyStatus(theConnection, aBuddyIndex, aStatus);
if (aResult != GP_NO_ERROR)
return FALSE; // couldn't get status
// Is the buddy using V2B?
if (strcmp(V2B_GP_LOCATION, aStatus->locationString) != 0)
return FALSE; // no using V2B
return TRUE;
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
static void OnGPError(GPConnection* theConnection, GPErrorArg* theArg, void* theParam)
{
CVoice2BuddyMFCDlg* aDialog = (CVoice2BuddyMFCDlg*)theParam;
char buf[1024];
sprintf(buf, "GP ERROR: %s", theArg->errorString);
aDialog->MessageBox(buf);
if (theArg->fatal == GP_FATAL)
aDialog->m_Connected = FALSE;
GSI_UNUSED(theConnection);
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
static void OnGPBuddyInfo(GPConnection* theConnection, GPGetInfoResponseArg* theArg, void* theParam)
{
CVoice2BuddyMFCDlg* aDialog = (CVoice2BuddyMFCDlg*)theParam;
// Make sure this name is in the list
int anIndex = aDialog->m_BuddyList.FindString(0, theArg->nick);
if (anIndex == -1)
{
int anIndex = aDialog->m_BuddyList.AddString(theArg->nick);
if (anIndex != -1)
aDialog->m_BuddyList.SetItemData(anIndex, theArg->profile);
}
GSI_UNUSED(theConnection);
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
static void OnGPBuddyStatus(GPConnection* theConnection, GPRecvBuddyStatusArg* theArg, void* theParam)
{
CVoice2BuddyMFCDlg* aDialog = (CVoice2BuddyMFCDlg*)theParam;
// Retrieve info for the buddy (nickname etc.)
// will return with file cache information, if available
gpGetInfo(theConnection, theArg->profile, GP_CHECK_CACHE, GP_NON_BLOCKING, (GPCallback)OnGPBuddyInfo, theParam);
GSI_UNUSED(aDialog);
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
static void OnGPBuddyMessage(GPConnection* theConnection, GPRecvBuddyMessageArg* theArg, void* theParam)
{
CVoice2BuddyMFCDlg* aDialog = (CVoice2BuddyMFCDlg*)theParam;
// Ignore messages that are not from V2B
if (FALSE == IsBuddyUsingV2B(theConnection, theArg->profile, NULL))
return;
// If this wasn't the guy we invited, ignore it
if (theArg->profile != aDialog->m_InvitedPlayer)
return;
// Is this an invite accept?
if (strcmp(V2B_GP_INVITE_ACCEPTED, theArg->message)==0)
aDialog->PostMessage(WM_USER+1, V2B_MSG_DOVOICECHAT, aDialog->m_MyProfileId);
// Is this an invite decline?
if (strcmp(V2B_GP_INVITE_DECLINED, theArg->message)==0)
aDialog->MessageBox(theArg->message);
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
static void OnGPInvite(GPConnection* theConnection, GPRecvGameInviteArg* theArg, void* theParam)
{
CVoice2BuddyMFCDlg* aDialog = (CVoice2BuddyMFCDlg*)theParam;
// Make sure the invite is from V2B
if (FALSE == IsBuddyUsingV2B(theConnection, theArg->profile, NULL))
return;
// Find the nickname of the buddy, for display
// (may not be the selected buddy)
CString aBuddyName = "";
int anIndex = 0;
while(anIndex < aDialog->m_BuddyList.GetCount())
{
// If this is the buddy, store off the name
if (aDialog->m_BuddyList.GetItemData(anIndex) == (unsigned int)theArg->profile)
{
aDialog->m_BuddyList.GetText(anIndex, aBuddyName);
break;
}
anIndex++;
}
if (aBuddyName.IsEmpty())
return; // buddy not found, ignore the invite
// Ask the user if they want to accept
char buf[255];
sprintf(buf, "%s has invited you to voice chat. Accept?", (LPCSTR)aBuddyName);
int aDialogResult = aDialog->MessageBox(buf, "Voice Chat", MB_YESNO);
if (aDialogResult == IDYES)
{
// begin voice session
gpSendBuddyMessage(&aDialog->m_GP, theArg->profile, V2B_GP_INVITE_ACCEPTED);
aDialog->PostMessage(WM_USER+1, V2B_MSG_DOVOICECHAT, theArg->profile);
}
else
{
// send rejection
gpSendBuddyMessage(&aDialog->m_GP, theArg->profile, V2B_GP_INVITE_DECLINED);
}
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
static void OnGPConnect(GPConnection* theConnection, GPConnectResponseArg* theArg, void* theParam)
{
CVoice2BuddyMFCDlg* aDialog = (CVoice2BuddyMFCDlg*)theParam;
if (theArg->result == GP_NO_ERROR)
{
aDialog->m_Connected = TRUE;
aDialog->m_SetupButton.EnableWindow(TRUE);
aDialog->m_MyProfileId = theArg->profile;
}
else
{
aDialog->MessageBox("Failed to connect!");
PostQuitMessage(0);
}
GSI_UNUSED(theConnection);
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
void CVoice2BuddyMFCDlg::DoLogin(const CString& theEmail, const CString& theNickname, const CString& thePassword)
{
// Begin the availability check
GSIACResult aResult = GSIACWaiting;
GSIStartAvailableCheck(GAME_NAME);
while(aResult == GSIACWaiting)
{
aResult = GSIAvailableCheckThink();
Sleep(20);
}
// Initialize the Presence SDK
GPResult result = gpInitialize(&m_GP, PRODUCTID_GMTEST, NAMESPACE_GAMESPY_SHARED, GP_PARTNERID_GAMESPY);
if (result != GP_NO_ERROR)
{
MessageBox("Failed on gpInitialize\r\n");
PostQuitMessage(0);
}
gpSetCallback(&m_GP, GP_ERROR, (GPCallback)OnGPError, this);
gpSetCallback(&m_GP, GP_RECV_BUDDY_STATUS, (GPCallback)OnGPBuddyStatus, this);
gpSetCallback(&m_GP, GP_RECV_GAME_INVITE, (GPCallback)OnGPInvite, this);
gpSetCallback(&m_GP, GP_RECV_BUDDY_MESSAGE, (GPCallback)OnGPBuddyMessage,this);
m_Initialized = TRUE;
// Connect to the presence server (goes to callback even though it's blocking)
gpConnect(&m_GP, theNickname, theEmail, thePassword, GP_NO_FIREWALL, GP_BLOCKING, (GPCallback)OnGPConnect, this);
// Set the think timer if we connected
// (m_Connected is set from the callback function above)
if (m_Connected)
SetTimer(THINK_TIMER_ID, THINK_TIMER_DELAY, NULL);
// Set our location
gpSetStatus(&m_GP, GP_ONLINE, V2B_GP_STATUS_IDLE, V2B_GP_LOCATION);
}
void CVoice2BuddyMFCDlg::OnTimer(UINT nIDEvent)
{
// Win32 timers can be called concurrently by the OS.
// (you can get a second timer callback before the first finishes!)
// CRITICAL_SECTION will not prevent this as Win32 allows concurrent access
static bool inTimer = false;
if (inTimer)
return;
// Prevent windows from entering the timer again
inTimer = true;
if (m_Connected)
gpProcess(&m_GP);
// Allow windows to enter the timer again
inTimer = false;
CDialog::OnTimer(nIDEvent);
}
void CVoice2BuddyMFCDlg::OnDestroy()
{
CDialog::OnDestroy();
// Kill the think timer
KillTimer(THINK_TIMER_ID);
// Disconnect from GP
if (m_Connected)
gpDisconnect(&m_GP);
// Destruct GP
if (m_Initialized)
gpDestroy(&m_GP);
gvCleanup();
}
void CVoice2BuddyMFCDlg::OnSelchangeBuddylist()
{
int aSel = m_BuddyList.GetCurSel();
if (aSel == -1)
m_VoiceChatButton.EnableWindow(FALSE);
else
m_VoiceChatButton.EnableWindow(TRUE);
}