|
|
/*==========================================================================
* * Copyright (C) 1999 Microsoft Corporation. All Rights Reserved. * * File: dvclientengine.h * Content: Definition of class for DirectXVoice Client * * History: * Date By Reason * ==== == ====== * 07/06/99 rodtoll Created it * 07/21/99 rodtoll Added flags field to entity data * 07/26/99 rodtoll Updated to support IDirectXVoiceNotify interface * 08/25/99 rodtoll General Cleanup/Modifications to support new * compression sub-system. * 08/30/99 rodtoll Added additional members to handle connect/disconnect timeout * 08/31/99 rodtoll Added new handle for new disconnect procedure * 09/03/99 rodtoll Re-work of playback core to support mixing to multiple buffers * for 3d support. * Re-worked playback to use position instead of notifications * allows for simpler handling of high CPU and 3d support * Implemented CreateUserBuffer/DeleteUserBuffer * 09/14/99 rodtoll Added members to support new notify masks * rodtoll Added CheckShouldSendMessage * rodtoll Added SendPlayerLevels * rodtoll Updated initialize for new parameters * rodtoll Added SetNotifyMask function * 09/18/99 rodtoll Added HandleThreadError to be called when an internal thread dies. * 09/27/99 rodtoll Added playback volume control * rodtoll Added reduction of playback volume when transmitting * 09/28/99 rodtoll Double notifications of local client when host migrates fixed. * rodtoll Added queue for notifications, notifications are added to the queue and * then signalled by the notify thread. (Prevents problems caused by notify * handlers taking a long time to return). * 09/29/99 pnewson Major AGC overhaul * 10/19/99 rodtoll Fix: Bug #113904 Shutdown Issues * Added handler for SESSIONLOST messages * 10/29/99 rodtoll Bug #113726 - Integrate Voxware Codecs, updating to use new * pluggable codec architecture. * 11/12/99 rodtoll Updated to use new playback and record classes and remove * old playback/record system. (Includes new waveIn/waveOut support) * rodtoll Updated to support new recording thread * rodtoll Added new echo suppression code * 11/17/99 rodtoll Fix: Bug #117177 - Calling Connect w/o voice session never returns * 11/23/99 rodtoll Updated Initialize/SetNotifyMask so error checking behaviour is consistant * 12/16/99 rodtoll Bug #117405 - 3D Sound APIs misleading - 3d sound apis renamed * The Delete3DSoundBuffer was re-worked to match the create * rodtoll Bug #122629 - Host migration broken in unusual configurations * Implemented new host migration scheme * 01/10/00 pnewson AGC and VA tuning * 01/14/2000 rodtoll Updated for new Set/GetTransmit target parameters * rodtoll Updated to support multiple targets * rodtoll Updated for new notification queueing * rodtoll Added FPM for handling notification memory * 01/21/00 rodtoll Bug #128897 - Disconnect timeout broken * 01/24/2000 rodtoll Bug #129427: Calling Destroy3DSoundBuffer for player who has * already disconnected resulted in an incorrect DVERR_NOTBUFFERED * error code. * 01/27/2000 rodtoll Bug #129934 - Update Create3DSoundBuffer to take DSBUFFERDESC * 02/17/2000 rodtoll Bug #133691 - Choppy audio - queue was not adapting * Added instrumentation * 03/28/2000 rodtoll Re-wrote nametable handling and locking -- more scalable * 03/29/2000 rodtoll Bug #30957 - Made conversion quality setting optional * rodtoll Bug #30753 - Added volatile to the class definition * 04/07/2000 rodtoll Bug #32179 - Prevent registration of > 1 interface * rodtoll Updated to use no copy sends, so handles pooling frames to be sent, proper * pulling of frames from pools and returns. * 05/17/2000 rodtoll Bug #35110 Simultaneous playback of 2 voices results in distorted playback * 06/21/2000 rodtoll Bug #35767 - Implement ability for Dsound effects processing if dpvoice buffers * Updated Connect and Create3DSoundBuffer to take buffers instead of descriptions * rodtoll Bug #36820 - Host migrates to wrong client when server is shut down before host's client disconnects * Caused because client attempts to register new server when there is one already * rodtoll Bug #37045 - Race conditions prevent acknowledgement of new host * Added send when new host is elected of settingsconfirm message * 06/27/2000 rodtoll Fixed window where outstanding sends being returned after we have deregistered * Voice now waits for all outstanding voice sends to complete before shutting down * rodtoll Added COM abstraction * 07/09/2000 rodtoll Added signature bytes * 07/12/2000 rodtoll Bug #39117 - Access violation while running VoicePosition. Several issues: * - Allow Destroy3DBuffer during disconnect * - Move nametable cleanup to before freesoundbufferlist * - Fixed code so always remove from list on DeleteSoundTarget * - Removed unneeded logic * 07/22/20000 rodtoll Bug #40296, 38858 - Crashes due to shutdown race condition * Now ensures that all threads from transport have left and that * all notificatinos have been processed before shutdown is complete. * 11/16/2000 rodtoll Bug #40587 - DPVOICE: Mixing server needs to use multi-processors * 01/26/2001 rodtoll WINBUG #293197 - DPVOICE: [STRESS} Stress applications cannot tell difference between out of memory and internal errors. * Remap DSERR_OUTOFMEMORY to DVERR_OUTOFMEMORY instead of DVERR_SOUNDINITFAILURE. * Remap DSERR_ALLOCATED to DVERR_PLAYBACKSYSTEMERROR instead of DVERR_SOUNDINITFAILURE. * 04/06/2001 kareemc Added Voice Defense * 04/11/2001 rodtoll WINBUG #221494 DPVOICE: Capped the # of queued recording events to prevent multiple wakeups resulting in false lockup * detection * 02/25/2002 rodtoll WINBUG #550009 - SECURITY: DPVOICE: Potential corruption of voice client state * - Harden receive pathways: i.e. confirm message sources from server, confirm message makes * sense w.r.t session settings, tighten validation, prevent duplicate disconnects from executing. * rodtoll WINBUG #550124 - SECURITY: DPVOICE: Shared memory region with NULL DACL * - Pull statistics exporting code * 06/13/2002 simonpow BUG #59944 Switched over to using Threadpool based timers rather than multimedia ***************************************************************************/
#ifndef __DVCLIENTENGINE_H
#define __DVCLIENTENGINE_H
#define DVCSTATE_NOTINITIALIZED 0x00000000
#define DVCSTATE_IDLE 0x00000001
#define DVCSTATE_CONNECTING 0x00000002
#define DVCSTATE_CONNECTED 0x00000003
#define DVCSTATE_DISCONNECTING 0x00000004
#define DVCECHOSTATE_IDLE 0x00000000
#define DVCECHOSTATE_RECORDING 0x00000001
#define DVCECHOSTATE_PLAYBACK 0x00000002
struct DIRECTVOICECLIENTOBJECT;
// Size in bytes of the fixed size elements
#define DV_CLIENT_NOTIFY_ELEMENT_SIZE 16
// Wakeup multiplier -- how many times / time we're supposed
// to wakeup should we actually wake up
#define DV_CLIENT_WAKEUP_MULTIPLER 4
#if defined(DEBUG) || defined(DBG)
#define CHECKLISTINTEGRITY CheckListIntegrity
#else
#define CHECKLISTINTEGRITY()
#endif
// CDirectVoiceClientEngine
//
// This class represents the IDirectXVoiceClient interface.
//
// The class is thread safe except for construction and
// destruction. The class is protected with a Multiple-Reader
// Single-Write lock.
//
#define VSIG_CLIENTENGINE 'ELCV'
#define VSIG_CLIENTENGINE_FREE 'ELC_'
//
volatile class CDirectVoiceClientEngine: public CDirectVoiceEngine { protected:
typedef enum { NOTIFY_FIXED, // Structure stored in fixed
NOTIFY_DYNAMIC // Memory allocated in dynamic
} ElementType;
struct CNotifyElement { typedef VOID (*PNOTIFY_COMPLETE)(PVOID pvContext, CNotifyElement *pElement); DWORD m_dwType; union _Element { struct { LPVOID m_lpData; } dynamic; struct { BYTE m_bFixedHolder[DV_CLIENT_NOTIFY_ELEMENT_SIZE]; } fixed; } m_element; DWORD m_dwDataSize; ElementType m_etElementType; PVOID pvContext; PNOTIFY_COMPLETE pNotifyFunc; CNotifyElement *m_lpNext; };
struct TimerHandlerParam { HANDLE hPlaybackTimerEvent; volatile LONG lPlaybackCount; HANDLE hRecordTimerEvent; DNCRITICAL_SECTION csPlayCount; };
public: CDirectVoiceClientEngine( DIRECTVOICECLIENTOBJECT *lpObject ); ~CDirectVoiceClientEngine();
public: // IDirectXVoiceClient Interface
HRESULT Connect( LPDVSOUNDDEVICECONFIG lpSoundDeviceConfig, LPDVCLIENTCONFIG lpClientConfig, DWORD dwFlags ); HRESULT Disconnect( DWORD dwFlags ); HRESULT GetSessionDesc( LPDVSESSIONDESC lpSessionDescBuffer ); HRESULT GetClientConfig( LPDVCLIENTCONFIG lpClientConfig ); HRESULT SetClientConfig( LPDVCLIENTCONFIG lpClientConfig ); HRESULT GetCaps( LPDVCAPS lpCaps ); HRESULT GetCompressionTypes( LPVOID lpBuffer, LPDWORD lpdwSize, LPDWORD lpdwNumElements, DWORD dwFlags ); HRESULT SetTransmitTarget( PDVID dvidTarget, DWORD dwNumTargets, DWORD dwFlags ); HRESULT GetTransmitTarget( LPDVID lpdvidTargets, PDWORD pdwNumElements, DWORD dwFlags ); HRESULT Create3DSoundBuffer( DVID dvidID, LPDIRECTSOUNDBUFFER lpdsBufferDesc, DWORD dwPriority, DWORD dwFlags, LPDIRECTSOUND3DBUFFER *lpBuffer ); HRESULT Delete3DSoundBuffer( DVID dvidID, LPDIRECTSOUND3DBUFFER *lpBuffer ); HRESULT SetNotifyMask( LPDWORD lpdwMessages, DWORD dwNumElements ); HRESULT GetSoundDeviceConfig( PDVSOUNDDEVICECONFIG pSoundDeviceConfig, PDWORD pdwBufferSize );
public: // CDirectVoiceEngine Members
HRESULT Initialize( CDirectVoiceTransport *lpTransport, LPDVMESSAGEHANDLER lpdvHandler, LPVOID lpUserContext, LPDWORD lpdwMessages, DWORD dwNumElements ); BOOL ReceiveSpeechMessage( DVID dvidSource, LPVOID lpMessage, DWORD dwSize ); HRESULT StartTransportSession(); HRESULT StopTransportSession(); HRESULT AddPlayer( DVID dvID ); HRESULT RemovePlayer( DVID dvID ); HRESULT CreateGroup( DVID dvID ); HRESULT DeleteGroup( DVID dvID ); HRESULT AddPlayerToGroup( DVID dvidGroup, DVID dvidPlayer ); HRESULT RemovePlayerFromGroup( DVID dvidGroup, DVID dvidPlayer ); HRESULT MigrateHost( DVID dvidNewHost, LPDIRECTPLAYVOICESERVER lpdvServer );
HRESULT MigrateHost_RunElection();
inline DWORD GetCurrentState() const { return m_dwCurrentState; };
BOOL InitClass();
public: // packet validation
inline static BOOL ValidateSessionType( DWORD dwSessionType ); inline static BOOL ValidateSessionFlags( DWORD dwFlags ); inline static BOOL ValidatePlayerFlags( DWORD dwFlags ); inline static BOOL ValidatePlayerDVID( DVID dvid ); inline BOOL ValidatePacketType( const DVPROTOCOLMSG_FULLMESSAGE* lpdvFullMessage ) const; protected: // Message handlers
HRESULT InternalSetNotifyMask( LPDWORD lpdwMessages, DWORD dwNumElements );
BOOL InternalCreateVoicePlayer( DVID dvidSource, PDVPROTOCOLMSG_PLAYERJOIN lpdvCreatePlayer, DWORD dwSize );
BOOL QueueSpeech( DVID dvidSource, PDVPROTOCOLMSG_SPEECHHEADER pdvSpeechHeader, PBYTE pbData, DWORD dwSize );
BOOL HandleConnectRefuse( DVID dvidSource, PDVPROTOCOLMSG_CONNECTREFUSE lpdvConnectRefuse, DWORD dwSize ); BOOL HandleCreateVoicePlayer( DVID dvidSource, PDVPROTOCOLMSG_PLAYERJOIN lpdvCreatePlayer, DWORD dwSize ); BOOL HandleDeleteVoicePlayer( DVID dvidSource, PDVPROTOCOLMSG_PLAYERQUIT lpdvDeletePlayer, DWORD dwSize ); BOOL HandleSpeech( DVID dvidSource, PDVPROTOCOLMSG_SPEECHHEADER lpdvSpeech, DWORD dwSize ); BOOL HandleSpeechWithFrom( DVID dvidSource, PDVPROTOCOLMSG_SPEECHWITHFROM lpdvSpeech, DWORD dwSize ); BOOL HandleSpeechBounce( DVID dvidSource, PDVPROTOCOLMSG_SPEECHHEADER lpdvSpeech, DWORD dwSize ); BOOL HandleConnectAccept( DVID dvidSource, PDVPROTOCOLMSG_CONNECTACCEPT lpdvConnectAccept, DWORD dwSize ); BOOL HandleDisconnectConfirm( DVID dvidSource, PDVPROTOCOLMSG_DISCONNECT lpdvDisconnect, DWORD dwSize ); BOOL HandleSetTarget( DVID dvidSource, PDVPROTOCOLMSG_SETTARGET lpdvSetTarget, DWORD dwSize ); BOOL HandleSessionLost( DVID dvidSource, PDVPROTOCOLMSG_SESSIONLOST lpdvSessionLost, DWORD dwSize ); BOOL HandlePlayerList( DVID dvidSource, PDVPROTOCOLMSG_PLAYERLIST lpdvPlayerList, DWORD dwSize ); BOOL HandleHostMigrated( DVID dvidSource, PDVPROTOCOLMSG_HOSTMIGRATED lpdvHostMigrated, DWORD dwSize ); BOOL HandleHostMigrateLeave( DVID dvidSource, PDVPROTOCOLMSG_HOSTMIGRATELEAVE lpdvHostMigrateLeave, DWORD dwSize );
friend class CClientRecordSubSystem;
protected:
void CheckListIntegrity();
void DoSessionLost(HRESULT hrReason); void DoSignalDisconnect(HRESULT hrDisconnectReason);
void HandleThreadError( HRESULT hrResult );
// Actually send the message to the client app
void TransmitMessage( DWORD dwMessageType, LPVOID lpData, DWORD dwSize );
void Cleanup(); void DoDisconnect(); void DoConnectResponse(); void WaitForBufferReturns();
void SetCurrentState( DWORD dwState ); HRESULT InitializeSoundSystem(); HRESULT ShutdownSoundSystem(); HRESULT CheckForDuplicateObjects(); HRESULT HandleLocalHostMigrateCreate();
void SetConnectResult( HRESULT hrOriginalResult ); HRESULT GetConnectResult() const;
void SetupPlaybackBufferDesc( LPDSBUFFERDESC lpdsBufferDesc, LPDSBUFFERDESC lpdsBufferSource );
HRESULT InitializeClientServer(); void DeInitializeClientServer();
void CheckForUserTimeout( DWORD dwCurTime ); void SendPlayerLevels();
BOOL CheckShouldSendMessage( DWORD dwMessageType );
HRESULT CreateGeneralBuffer( ); void UpdateActivePlayPendingList(); void UpdateActiveNotifyPendingList(); void CleanupNotifyLists(); void CleanupPlaybackLists();
static void __cdecl RecordThread( void *lpParam ); static void __cdecl PlaybackThread( void *lpParam ); static void __cdecl NotifyThread( void *lpParam ); static void MixingWakeupProc(void * pvUserData ); static PVOID ClientBufferAlloc( void *const pv, const DWORD dwSize ); static void ClientBufferFree( void *const pv, void *const pvBuffer );
PDVTRANSPORT_BUFFERDESC GetTransmitBuffer( DWORD dwSize, LPVOID *ppvContext ); HRESULT SendComplete( PDVEVENTMSG_SENDCOMPLETE pSendComplete ); void ReturnTransmitBuffer( PVOID pvContext );
HRESULT Send_ConnectRequest(); HRESULT Send_DisconnectRequest(); HRESULT Send_SessionLost(); HRESULT Send_SettingsConfirm();
HRESULT SetPlaybackVolume( LONG lVolume );
DWORD m_dwSignature;
CFramePool *m_lpFramePool; // Pool of frames
CDirectVoiceTransport *m_lpSessionTransport; // Transport for the session
DVSOUNDDEVICECONFIG m_dvSoundDeviceConfig; // Sound device config
DVCLIENTCONFIG m_dvClientConfig; // Sound general config
DVSESSIONDESC m_dvSessionDesc; // Session configuration
DVCAPS m_dvCaps; // Caps
LPDVMESSAGEHANDLER m_lpMessageHandler; // User message handler
LPVOID m_lpUserContext; // User context for message handler
DVID m_dvidServer; // DVID of the server
PDVID m_pdvidTargets; // DVID of the current target(s) (Protected by m_csTargetLock)
DWORD m_dwNumTargets; // # of targets (Protected by m_csTargetLock)
DWORD m_dwTargetVersion; // Increment each time targets are changed
DNCRITICAL_SECTION m_csTargetLock; // If you need write/read lock, always take it before this lock
HRESULT InternalSetTransmitTarget( PDVID pdvidTargets, DWORD dwNumTargets ); HRESULT CheckForAndRemoveTarget( DVID dvidID ); volatile DWORD m_dwCurrentState; // Current engine state
CFrame *m_tmpFrame; // Tmp frame for receiving
LPDIRECTPLAYVOICESERVER m_lpdvServerMigrated; // Stores reference to migrated host
DWORD m_dwActiveCount; DWORD m_dwEchoState; DNCRITICAL_SECTION m_lockPlaybackMode;
DNCRITICAL_SECTION m_csCleanupProtect; // Used to ensure only only cleanup instance is active
protected: // Buffer maintenance
HRESULT AddSoundTarget( CSoundTarget *lpcsTarget ); HRESULT DeleteSoundTarget( DVID dvidID ); HRESULT FindSoundTarget( DVID dvidID, CSoundTarget **lpcsTarget ); HRESULT InitSoundTargetList(); HRESULT FreeSoundTargetList();
protected: // Notification queue
// Queue up a notification for the user
HRESULT NotifyQueue_Add( DWORD dwMessageType, LPVOID lpData, DWORD dwDataSize, PVOID pvContext = NULL, CNotifyElement::PNOTIFY_COMPLETE pNotifyFunc = NULL ); HRESULT NotifyQueue_Get( CNotifyElement **pneElement ); HRESULT NotifyQueue_Init(); HRESULT NotifyQueue_Free(); HRESULT NotifyQueue_ElementFree( CNotifyElement *lpElement ); HRESULT NotifyQueue_IndicateNext(); void NotifyQueue_Disable(); void NotifyQueue_Enable(); void NotifyQueue_Flush();
static void NotifyComplete_SyncWait( PVOID pvContext, CNotifyElement *pElement ); static void NotifyComplete_LocalPlayer( PVOID pvContext, CNotifyElement *pElement ); static void NotifyComplete_RemotePlayer( PVOID pvContext, CNotifyElement *pElement );
DNCRITICAL_SECTION m_csNotifyQueueLock; // Notification queue
BOOL m_fNotifyQueueEnabled; CNotifyElement *m_lpNotifyList; // List of notifications
HANDLE m_hNewNotifyElement; // Semaphore signalled when new event is queued
HRESULT SendConnectResult(); HRESULT SendDisconnectResult();
protected:
HRESULT SetupInitialBuffers(); HRESULT SetupSpeechBuffer(); HRESULT FreeBuffers();
protected: // Statistics
void ClientStats_Reset(); void ClientStats_Begin(); void ClientStats_End(); void ClientStats_Dump(); void ClientStats_Dump_Record(); void ClientStats_Dump_Playback(); void ClientStats_Dump_Receive(); static void ClientStats_Dump_Transmit(); ClientStatistics m_stats; ClientStatistics *m_pStatsBlob; protected: // Sound System Information
CAudioPlaybackBuffer *m_audioPlaybackBuffer; // Audio playback buffer
CAudioRecordDevice *m_audioRecordDevice; // Audio record device
CAudioPlaybackDevice *m_audioPlaybackDevice; // Audio Playback Device
CAudioRecordBuffer *m_audioRecordBuffer; // Audio Record Buffer;
HANDLE m_hNotifyDone; // Signalled when notify thread dies
HANDLE m_hNotifyTerminate; // Signalled to terminate notify thread
HANDLE m_hNotifyChange; // Signalled when notification interval changes
HANDLE m_hRecordDone; // Record thread done signal
HANDLE m_hRecordTerminate; // Record thread stop signal
HANDLE m_hPlaybackDone; // Playback thread done signal
HANDLE m_hPlaybackTerminate; // Playback thread terminate signal
HANDLE m_hConnectAck; // Signalled when connect complete
HANDLE m_hDisconnectAck; // Signalled when disconnect complete
HRESULT m_hrDisconnectResult; // Result of the disconnect result
HRESULT m_hrConnectResult; // Result of the connection request
HRESULT m_hrOriginalConnectResult; // Original Connect Result (Untranslated)
DVID m_dvidLocal; // Local DVID
DNCRITICAL_SECTION m_csClassLock;
BOOL m_fCritSecInited;
protected: // Compression Control Data
LPDVFULLCOMPRESSIONINFO m_lpdvfCompressionInfo; // Information about current compression type
// Memory pointed to is owned by dvcdb.cpp
DWORD m_dwCompressedFrameSize; // Size of a compressed frame
DWORD m_dwUnCompressedFrameSize; // Size of an uncompressed frame
DWORD m_dwNumPerBuffer; // # of frames / directsound buffer
CFramePool *m_pFramePool; // Frame pool
DIRECTVOICECLIENTOBJECT *m_lpObject; // Cached pointer to the COM interface
DWORD m_dwSynchBegin; // GetTickCount at Connect/Disconnect start
HANDLE m_hNotifySynch; // Notified when connect/disconnect completes
HANDLE m_hNotifyDisconnect; // Signalled
HANDLE m_hNotifyConnect; // Connect
HANDLE m_hRecordThreadHandle; HANDLE m_hPlaybackThreadHandle;
DNCRITICAL_SECTION m_csBufferLock; // Lock to protect playback mixing buffers
CSoundTarget *m_lpstBufferList; // Other Playback Mixing Buffers
CSoundTarget *m_lpstGeneralBuffer; // General Playback Mixing Buffer
LPDWORD m_lpdwMessageElements; // Buffer with notifiers
DWORD m_dwNumMessageElements; // Number of notifiers
DNCRITICAL_SECTION m_csNotifyLock; // Lock on list of notifiers
DSBUFFERDESC m_dsBufferDesc;
TimerHandlerParam m_thTimerInfo; DvTimer * m_pTimer; DWORD m_dwLastConnectSent; DWORD m_dwHostOrderID; DWORD m_dwMigrateHostOrderID; // Host Order ID of current host
PVOID m_pvLocalPlayerContext;
DIRECTSOUNDMIXER_SRCQUALITY m_dwOriginalRecordQuality; DIRECTSOUNDMIXER_SRCQUALITY m_dwOriginalPlayQuality;
CFixedPool m_fpNotifications; // Frame pool for notifications
BYTE m_bLastPeak; // Last frame peak
BYTE m_bLastPlaybackPeak; // Last peak on playback
BYTE m_bMsgNum; // Last msg # transmitted
BYTE m_bSeqNum; // Last sequence # transmitted
BOOL m_bLastTransmitted; // Was last frame sent?
BOOL m_fSessionLost; // Flag indicating session was lost
BOOL m_fLocalPlayerNotify; // Has notification been sent for local player
BOOL m_fLocalPlayerAvailable; BOOL m_fConnectAsync; // Are we connecting async?
BOOL m_fDisconnectAsync; // Are we disconnecting async?
CVoiceNameTable m_voiceNameTable;
DWORD m_dwPlayActiveCount; CBilink m_blPlayActivePlayers; CBilink m_blPlayAddPlayers; DNCRITICAL_SECTION m_csPlayAddList;
CBilink m_blNotifyActivePlayers; CBilink m_blNotifyAddPlayers; DNCRITICAL_SECTION m_csNotifyAddList;
CFixedPool m_fpPlayers;
DNCRITICAL_SECTION m_csTransmitBufferLock; CFixedPool m_BufferDescPool; CFixedPool* m_pBufferPools; DWORD *m_pdwBufferPoolSizes; DWORD m_dwNumPools;
LONG m_iPlayerListReceived; // Have we received a playerlist message for this session?
// RESET this before host migration processing!!
BOOL m_fDisconnecting; // Flag used to guard against multiple, simultaneous disconnects running
};
#endif
|