/*------------------------------------------------------------------------- * fedsrv\AdminSession.CPP * * This houses the implementation of CAdminSession Class. This class * exposes log-on methods allowing an Admin Session (using COM) with AllSrv. * * Owner: , * * Copyright 1986-1998 Microsoft Corporation, All Rights Reserved *-----------------------------------------------------------------------*/ #include "pch.h" #include "AdminSessionEventSink.h" #include "zreg.h" ///////////////////////////////////////////////////////////////////////////// // CAdminSession TC_OBJECT_EXTERN_IMPL(CAdminSession) ///////////////////////////////////////////////////////////////////////////// // Static Initialization /*static*/ TCAutoCriticalSection CAdminSession::s_cs; /*static*/ CAdminSession::XSessions CAdminSession::s_vecSessions; // this is temporary until launcher obj gets done /*static*/ CAdminSession* CAdminSession::s_pWhoStartedServer = NULL; // This is NULL unless a session started the server ///////////////////////////////////////////////////////////////////////////// // Construction / Destruction /*------------------------------------------------------------------------- * CAdminSession() *------------------------------------------------------------------------- */ CAdminSession::CAdminSession() : m_pEventSink(NULL) { XLockStatic lock(&s_cs); if (_Module.WasCOMStarted() && !s_pWhoStartedServer) s_pWhoStartedServer = this; // Add ourself to the static collection s_vecSessions.push_back(this); } /*------------------------------------------------------------------------- * ~CAdminSession() *------------------------------------------------------------------------- */ CAdminSession::~CAdminSession() { XLockStatic lock(&s_cs); if (this == s_pWhoStartedServer) s_pWhoStartedServer = NULL; // Remove ourself from the static collection XSessionsIt it = std::find(s_vecSessions.begin(), s_vecSessions.end(), this); if (it != s_vecSessions.end()) s_vecSessions.erase(it); lock.Unlock(); #if defined(ALLSRV_STANDALONE) // Possibly shutdown the standalone server if no more sessions if (0 == GetSessionCount()) { // Get the Server property IAdminServerPtr spServer; if (SUCCEEDED(get_Server(&spServer))) { IAdminGamesPtr spGames; if (SUCCEEDED(spServer->get_Games(&spGames))) { // Shutdown the server if no games exist long cGames = 0; spGames->get_Count(&cGames); if (0 == cGames) PostThreadMessage(g.idReceiveThread, WM_QUIT, 0, 0); } } } #endif // defined(ALLSRV_STANDALONE) } HRESULT CAdminSession::FinalConstruct() { // #define CAdminSession_TRACE_CONSTRUCTION #ifdef CAdminSession_TRACE_CONSTRUCTION _TRACE_BEGIN DWORD id = GetCurrentThreadId(); _TRACE_PART2("CAdminSession::FinalConstruct(): ThreadId = %d (0x%X)\n", id, id); _TRACE_PART1("\tRaw pointer = 0x%08X\n", this); _TRACE_END #endif // CAdminSession_TRACE_CONSTRUCTION // Create the event sink object CComObject* pEventSink = NULL; RETURN_FAILED(pEventSink->CreateInstance(&pEventSink)); pEventSink->AddRef(); pEventSink->Init(this); m_pEventSink = pEventSink; // Indicate success return S_OK; } void CAdminSession::FinalRelease() { // Terminate the event sink object if (m_pEventSink) { m_pEventSink->Term(); m_pEventSink->Release(); m_pEventSink = NULL; } } ///////////////////////////////////////////////////////////////////////////// // Overrides IUnknown* CAdminSession::OnGetUnknown() { return NULL; } /*------------------------------------------------------------------------- * DestroyAllSessions() *------------------------------------------------------------------------- * Purpose: * Kill all active (and hosed) sessions * */ /* static*/void CAdminSession::DestroyAllSessions() { int cSessions; while (cSessions = GetSessionCount()) // Intentional assignment { // Keep a reference while disconnecting object CAdminSession* pSession = GetLastSession(); IUnknownPtr spUnk(pSession); // Disconnect all external references if (NULL != spUnk) ::CoDisconnectObject(spUnk, 0); // Release the IUnknown spUnk = NULL; // That /should/ have forced a deletion. If not, remove from list XLockStatic lock(&s_cs); if (GetSessionCount() == cSessions) { XSessionsIt it = std::find(s_vecSessions.begin(), s_vecSessions.end(), pSession); assert(it != s_vecSessions.end()); s_vecSessions.erase(it); } } } /*------------------------------------------------------------------------- * put_SessionInfo() *------------------------------------------------------------------------- * Purpose: * * */ STDMETHODIMP CAdminSession::put_SessionInfo(ITCSessionInfo* pSessionInfo) { // GetAGCGlobal()->TriggerEvent(ppListeners, EventID_NewSession, 0); return S_OK; } /*------------------------------------------------------------------------- * get_SessionInfo() *------------------------------------------------------------------------- * Purpose: * * */ STDMETHODIMP CAdminSession::get_SessionInfo(ITCSessionInfo** ppSessionInfo) { return S_OK; } /*------------------------------------------------------------------------- * get_SessionInfos() *------------------------------------------------------------------------- * Purpose: * * */ STDMETHODIMP CAdminSession::get_SessionInfos(ITCSessionInfos** ppSessionInfos) { return S_OK; } /*------------------------------------------------------------------------- * get_SessionInfos() *------------------------------------------------------------------------- * Purpose: * Use this to find out of an event of a certain type is firing * * A uniqueID of AGC_Any_Objects causes the check to be done for global event firing, * otherwise this id is used to check for specialized event firing for the AGC * object with this id. * */ STDMETHODIMP CAdminSession::get_IsEventActivated(AGCEventID event, AGCUniqueID uniqueID, BOOL *pVal) { if (uniqueID == -1) uniqueID = AGC_Any_Objects; assert(m_pEventSink); *pVal = GetAGCGlobal()->IsRegistered(event, uniqueID, reinterpret_cast(m_pEventSink)); return S_OK; } /*------------------------------------------------------------------------- * get_Server() *------------------------------------------------------------------------- * Purpose: * * */ STDMETHODIMP CAdminSession::get_Server(IAdminServer** ppAdminServer) { // Get the CAdminServer instance from the GIT HRESULT hr; assert(g.dwServerGITCookie); ZSucceeded(hr = GetAGCGlobal()->GetInterfaceFromGlobal( g.dwServerGITCookie, IID_IAdminServer, (void**)ppAdminServer)); return hr; } /*------------------------------------------------------------------------- * ActivateEvents() *------------------------------------------------------------------------- * Purpose: * Use this to activate event firing for events of a certain type * * Parameters: * event: id of the event to be deactivated * uniqueID: if not AGC_Any_Objects, then this is AGC unqiue object id telling who the event * should be fired for. AGC_Any_Objects means for all applicable objects */ STDMETHODIMP CAdminSession::ActivateEvents(AGCEventID event, AGCUniqueID uniqueID) { if (-1 == uniqueID) uniqueID = AGC_Any_Objects; assert(m_pEventSink); GetAGCGlobal()->RegisterEvent(event, uniqueID, reinterpret_cast(m_pEventSink)); return S_OK; } /*------------------------------------------------------------------------- * DeactivateEvents() *------------------------------------------------------------------------- * Purpose: * Use this to activate event firing for events of a certain type * * Parameters: * event: id of the event to be deactivated * uniqueID: if not AGC_Any_Objects, then this is AGC unqiue object id telling who the event * should be fired for. AGC_Any_Objects means for all applicable objects * * Remarks: * There should be a DeactivateEvents() call for each ActivateEvents() call. * A single DeactivateEvents() call with AGC_Any_Objects as the unique ID does not turn off * all event firing for the AGCEventID event. */ STDMETHODIMP CAdminSession::DeactivateEvents(AGCEventID event, AGCUniqueID uniqueID) { if (-1 == uniqueID) uniqueID = AGC_Any_Objects; assert(m_pEventSink); GetAGCGlobal()->RevokeEvent(event, uniqueID, reinterpret_cast(m_pEventSink)); return S_OK; } /*------------------------------------------------------------------------- * ActivateAllEvents() *------------------------------------------------------------------------- * Purpose: * Use this to activate event firing all events for this session * */ STDMETHODIMP CAdminSession::ActivateAllEvents() { assert(m_pEventSink); GetAGCGlobal()->RegisterAllEvents( reinterpret_cast(m_pEventSink)); return S_OK; } /*------------------------------------------------------------------------- * DeactivateAllEvents() *------------------------------------------------------------------------- * Purpose: * Use this to deactivate event firing all events for this session * */ STDMETHODIMP CAdminSession::DeactivateAllEvents() { assert(m_pEventSink); GetAGCGlobal()->RevokeAllEvents( reinterpret_cast(m_pEventSink)); return S_OK; } /*------------------------------------------------------------------------- * get_ProcessID() *------------------------------------------------------------------------- * Purpose: * Determine the process identifier of this AllSrv process. * */ STDMETHODIMP CAdminSession::get_ProcessID(long* pdwProcessID) { CLEAROUT(pdwProcessID, long(GetCurrentProcessId())); return S_OK; } /*------------------------------------------------------------------------- * get_ProductVersion() *------------------------------------------------------------------------- * Purpose: * Determine the version number of AllSrv * */ STDMETHODIMP CAdminSession::get_Version(IAGCVersionInfo** ppVersion) { // Initialize the [out] parameter CLEAROUT(ppVersion, (IAGCVersionInfo*)NULL); // Create an instance of the version object IAGCVersionInfoPtr spVersion; RETURN_FAILED(spVersion.CreateInstance(CLSID_AGCVersionInfo)); // Initialize the version object TCHAR szModule[_MAX_PATH]; GetModuleFileName(_Module.GetModuleInstance(), szModule, sizeofArray(szModule)); RETURN_FAILED(spVersion->put_FileName(CComBSTR(szModule))); // Detach the object to the [out] parameter *ppVersion = spVersion.Detach(); // Indicate success return S_OK; } /*------------------------------------------------------------------------- * CAdminSession::Stop() *------------------------------------------------------------------------- * Purpose: * Use this to kill the server. * */ STDMETHODIMP CAdminSession::Stop() { OutputDebugString("\nSomeone is shutting down the server (possibly remotely) using the Admin Object Model.\n"); _Module.StopAllsrv(); return S_OK; } /*------------------------------------------------------------------------- * CAdminSession::Pause() *------------------------------------------------------------------------- * Purpose: * Use this to pause the server. When paused, no new users may join * and once everyone logs off, the server shuts down. * * This method does nothing if the server is already paused. * */ STDMETHODIMP CAdminSession::Pause() { FedSrv_Pause(); return S_OK; } /*------------------------------------------------------------------------- * get_WhoStartedServer() *------------------------------------------------------------------------- * Purpose: * Use this to find out who started the server * */ STDMETHODIMP CAdminSession::get_WhoStartedServer(IAdminSession** pIAdminSession) { XLockStatic lock(&s_cs); if (s_pWhoStartedServer == NULL) { *pIAdminSession = NULL; return S_OK; } // TODO: NOT THREAD-SAFE!!! return s_pWhoStartedServer->QueryInterface(IID_IAdminSession, (void**)pIAdminSession); } /*------------------------------------------------------------------------- * CAdminSession::get_EventLog *------------------------------------------------------------------------- * Purpose: * Gets the previously created event logger object. * */ STDMETHODIMP CAdminSession::get_EventLog(IAGCEventLogger** ppEventLogger) { // Delegate to the module object return _Module.get_EventLog(ppEventLogger); } ///////////////////////////////////////////////////////////////////////////// // STDMETHODIMP CAdminSession::get_PerfCounters(IAGCEvent** ppPerfCounters) { // Create an AGCEvent object to store the named performance counters IAGCEventCreatePtr spEvent; RETURN_FAILED(spEvent.CreateInstance(CLSID_AGCEvent)); RETURN_FAILED(spEvent->Init()); // Populate the event with the named performance counters CComVariant var(0L); V_I4(&var) = g.pServerCounters->cPacketsIn; RETURN_FAILED(spEvent->AddProperty(CComBSTR(L"PacketsIn"), &var)); V_I4(&var) = g.pServerCounters->cPlayersOnline; RETURN_FAILED(spEvent->AddProperty(CComBSTR(L"PlayersOnline"), &var)); V_I4(&var) = g.pServerCounters->timeBetweenInnerLoops; RETURN_FAILED(spEvent->AddProperty(CComBSTR(L"TimeInnerLoop"), &var)); // Return the new object return spEvent->QueryInterface(IID_IAGCEvent, (void**)ppPerfCounters); } ///////////////////////////////////////////////////////////////////////////// // STDMETHODIMP CAdminSession::SendAdminChat(BSTR bstrText, long nUserID, DATE dateOriginal) { // This just triggers an event _AGCModule.TriggerEvent(NULL, EventID_AdminChat, "", nUserID, -1, -1, 2, "Message" , VT_BSTR , bstrText, "OriginalTime" , VT_DATE , dateOriginal); // Indicate success return S_OK; } /*------------------------------------------------------------------------- * CAdminSession::Continue() *------------------------------------------------------------------------- * Purpose: * Use this to continue the server from a paused state. * * This method does nothing if the server is not paused. * */ STDMETHODIMP CAdminSession::Continue() { FedSrv_Continue(); return S_OK; }