You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

2554 lines
73 KiB

/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, 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 <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
#pragma warning(disable: 4284)
#include "HeadSpin.h"
#include "hsTypes.h"
#include "hsWindowHndl.h"
#include "plClient.h"
#include "hsStream.h"
#include "../plResMgr/plResManager.h"
#include "../plResMgr/plKeyFinder.h"
#include "../pnKeyedObject/plKey.h"
#include "../pnKeyedObject/plFixedKey.h"
#include "../pnMessage/plRefMsg.h"
#include "../pnSceneObject/plSceneObject.h"
#include "../pnSceneObject/plCoordinateInterface.h"
#include "../plScene/plSceneNode.h"
#include "../pnMessage/plTimeMsg.h"
#include "../pnMessage/plClientMsg.h"
#include "../pfCamera/plVirtualCamNeu.h"
#include "hsTimer.h"
#include "../plPipeline/hsG3DDeviceSelector.h"
#include "../plFile/plEncryptedStream.h"
#include "../plFile/plFileUtils.h"
#include "../plInputCore/plInputManager.h"
#include "../plInputCore/plInputInterfaceMgr.h"
#include "../plInputCore/plInputDevice.h"
#include "../plPhysX/plSimulationMgr.h"
#include "../plNetClient/plNetClientMgr.h"
#include "../plAvatar/plAvatarMgr.h"
#include "../plScene/plRelevanceMgr.h"
#include "../pnTimer/plTimerCallbackManager.h"
#include "../pfAudio/plListener.h"
#include "../pnMessage/plCmdIfaceModMsg.h"
#include "../plMessage/plRoomLoadNotifyMsg.h"
#include "../pnMessage/plPlayerPageMsg.h"
#include "../pnMessage/plCameraMsg.h"
#include "../plMessage/plTransitionMsg.h"
#include "../plMessage/plLinkToAgeMsg.h"
#include "../plMessage/plPreloaderMsg.h"
#include "../plMessage/plNetCommMsgs.h"
#include "../plMessage/plAgeLoadedMsg.h"
#include "../pfConsole/pfConsoleEngine.h"
#include "../pfConsole/pfConsole.h"
#include "../pfConsole/pfConsoleDirSrc.h"
#include "../plScene/plPageTreeMgr.h"
#include "../plScene/plVisMgr.h"
#include "../plFile/hsFiles.h"
#include "../pfKI/pfKI.h"
#include "../plAudio/plAudioSystem.h"
#include "../plAudio/plAudioCaps.h"
#include "../plStatGather/plProfileManagerFull.h"
#include "plPipeline.h"
#include "../plPipeline/plPipelineCreate.h"
#include "../plPipeline/plPipeDebugFlags.h"
#include "../plPipeline/plTransitionMgr.h"
#include "../plPipeline/plCaptureRender.h"
#include "../plPipeline/plDynamicEnvMap.h"
#include "../plNetClient/plLinkEffectsMgr.h"
#include "../plAvatar/plAvatarClothing.h"
#include "../plAvatar/plArmatureMod.h"
#include "../pnMessage/plProxyDrawMsg.h"
#include "../plScene/plRenderRequest.h"
#include "../plDrawable/plAccessGeometry.h"
#include "plPipeResReq.h"
#include "../plDrawable/plVisLOSMgr.h"
#include "../plGImage/plBitmap.h"
#include "../plStatusLog/plStatusLog.h"
#include "../plProgressMgr/plProgressMgr.h"
#include "../plPipeline/plDTProgressMgr.h"
#include "../plPipeline/plBinkPlayer.h"
#include "../plMessage/plMovieMsg.h"
#include "../plSDL/plSDL.h"
#include "../pnDispatch/plDispatch.h"
#include "../pnDispatch/plDispatchLogBase.h"
#include "../pfGameGUIMgr/pfGameGUIMgr.h"
#include "../pfPython/cyMisc.h"
#include "../plMessage/plInputEventMsg.h"
#include "../plMessage/plRenderRequestMsg.h"
#include "../pnMessage/plEventCallbackMsg.h"
#include "../plModifier/plSimpleModifier.h"
#include "plAudible.h"
#include "../plMessage/plAnimCmdMsg.h"
#include "../pnMessage/plSoundMsg.h"
#include "../pnMessage/plAudioSysMsg.h"
#include "../plMessage/plRenderMsg.h"
#include "../plAgeLoader/plResPatcher.h"
#include "../pfPython/cyPythonInterface.h"
#include "../plUnifiedTime/plClientUnifiedTime.h"
#include "../pfAnimation/plAnimDebugList.h"
#include "../pfGameGUIMgr/pfGUICtrlGenerator.h"
#include "../plGImage/plWinFontCache.h"
#include "../plGImage/plFontCache.h"
#include "../pfJournalBook/pfJournalBook.h"
#include "../plAvatar/plAGAnimInstance.h"
#include "../plAgeLoader/plAgeLoader.h"
#include "../plClientKey/plClientKey.h"
#include "../CoreLib/plQuality.h"
#include "../plGLight/plShadowCaster.h"
#include "../plNetClient/plNetLinkingMgr.h"
#include "../plNetCommon/plNetCommonConstants.h"
#include "../plNetGameLib/plNetGameLib.h"
#include "../pfSecurePreloader/pfSecurePreloader.h"
#include "../pfLocalizationMgr/pfLocalizationMgr.h"
#include "../pfCsrSrv/pfCsrSrv.h"
#include "plTweak.h"
#define MSG_LOADING_BAR
// static hsVector3 gAbsDown(0,0,-hsScalar1);
static plDispatchBase* gDisp = nil;
static plTimerCallbackManager* gTimerMgr = nil;
static plAudioSystem* gAudio = nil;
hsBool plClient::fDelayMS = false;
plClient* plClient::fInstance=nil;
static hsTArray<HMODULE> fLoadedDLLs;
plClient::plClient()
: fPipeline(nil),
fDone(false),
fQuitIntro(false),
fWindowHndl(nil),
fInputManager(nil),
fConsole(nil),
fCurrentNode(nil),
fNewCamera(nil),
fpAuxInitDir(nil),
fTransitionMgr(nil),
fLinkEffectsMgr(nil),
fProgressBar(nil),
fGameGUIMgr(nil),
fKIGUIGlue(nil),
fWindowActive(false),
fAnimDebugList(nil),
fClampCap(-1),
fQuality(0),
fPageMgr(nil),
fFontCache(nil),
fHoldLoadRequests(false),
fNumLoadingRooms(0),
fNumPostLoadMsgs(0),
fPostLoadMsgInc(0.f),
fPatchGlobalAges(false)
{
#ifndef PLASMA_EXTERNAL_RELEASE
bPythonDebugConnected = false;
#endif
hsStatusMessage("Constructing client\n");
plClient::SetInstance(this);
// gNextRoom[0] = '\0';
// Setup the timer. These can be overriden with console commands.
hsTimer::SetRealTime(true);
#ifdef HS_DEBUGGING
// hsTimer::SetRealTime(false);
hsTimer::SetTimeClamp(0.1f);
#else // HS_DEBUGGING
// hsTimer::SetRealTime(true);
hsTimer::SetTimeClamp(0);
#endif // HS_DEBUGGING
IDetectAudioVideoSettings(); // need to do this before the console is created
/// allow console commands to start working early
// Create the console engine
fConsoleEngine = TRACKED_NEW pfConsoleEngine();
// create network mgr before console runs
plNetClientMgr::SetInstance(TRACKED_NEW plNetClientMgr);
plAgeLoader::SetInstance(TRACKED_NEW plAgeLoader);
// Use it to parse the init directory
wchar initFolder[MAX_PATH];
PathGetInitDirectory(initFolder, arrsize(initFolder));
pfConsoleDirSrc dirSrc( fConsoleEngine, initFolder, L"*.ini" );
#ifndef PLASMA_EXTERNAL_RELEASE
// internal builds also parse the local init folder
dirSrc.ParseDirectory( L"init", L"*.ini" );
#endif
/// End of console stuff
}
plClient::~plClient()
{
hsStatusMessage("Destructing client\n");
plClient::SetInstance( nil );
delete fPageMgr;
delete [] fpAuxInitDir;
}
#include "../plGImage/plAVIWriter.h"
#include "../pfCharacter/pfMarkerMgr.h"
hsBool plClient::Shutdown()
{
plSynchEnabler ps(false); // disable dirty state tracking during shutdown
delete fProgressBar;
CsrSrvShutdown();
// Just in case, clear this out (trying to fix a crash bug where this is still active at shutdown)
plDispatch::SetMsgRecieveCallback(nil);
// Let the resmanager know we're going to be shutting down.
hsgResMgr::ResMgr()->BeginShutdown();
// Must kill off all movies before shutting down audio.
IKillMovies();
plgAudioSys::Activate(false);
plBinkPlayer::DeInit();
//
// Get any proxies to commit suicide.
plProxyDrawMsg* nuke = TRACKED_NEW plProxyDrawMsg(plProxyDrawMsg::kAllTypes
| plProxyDrawMsg::kDestroy);
plgDispatch::MsgSend(nuke);
if (plAVIWriter::IsInitialized())
plAVIWriter::Instance().Shutdown();
hsStatusMessage( "Shutting down client...\n" );
// First, before anybody else goes away, write out our key mappings
if( plInputInterfaceMgr::GetInstance() )
plInputInterfaceMgr::GetInstance()->WriteKeyMap();
// tell Python that its ok to shutdown
PythonInterface::WeAreInShutdown();
// Shutdown the journalBook API
pfJournalBook::SingletonShutdown();
/// Take down the KI
pfGameGUIMgr *mgr = pfGameGUIMgr::GetInstance();
if( mgr )
mgr->UnloadDialog( "KIBlackBar" ); // unload the blackbar which will bootstrap in the rest of the KI dialogs
// Take down our GUI control generator
pfGUICtrlGenerator::Instance().Shutdown();
if (plNetClientMgr::GetInstance())
{
plNetClientMgr::GetInstance()->Shutdown();
plNetClientMgr::GetInstance()->UnRegisterAs(kNetClientMgr_KEY); // deletes NetClientMgr instance
plNetClientMgr::SetInstance(nil);
}
if (plAgeLoader::GetInstance())
{
plAgeLoader::GetInstance()->Shutdown();
plAgeLoader::GetInstance()->UnRegisterAs(kAgeLoader_KEY); // deletes instance
plAgeLoader::SetInstance(nil);
}
if (pfSecurePreloader::IsInstanced())
{
pfSecurePreloader::GetInstance()->Shutdown();
// pfSecurePreloader handles its own fixed key unregistration
}
if (fInputManager)
{
fInputManager->UnRegisterAs(kInput_KEY);
fInputManager = nil;
}
if( fGameGUIMgr != nil )
{
fGameGUIMgr->UnRegisterAs( kGameGUIMgr_KEY );
fGameGUIMgr = nil;
}
for (int i = 0; i < fRooms.Count(); i++)
{
plSceneNode *sn = fRooms[i].fNode;
GetKey()->Release(sn->GetKey());
}
fRooms.Reset();
fRoomsLoading.clear();
// Shutdown plNetClientMgr
plAccessGeometry::DeInit();
delete fPipeline;
fPipeline = nil;
if (plSimulationMgr::GetInstance())
plSimulationMgr::Shutdown();
plAvatarMgr::ShutDown();
plRelevanceMgr::DeInit();
if (fPageMgr)
fPageMgr->Reset();
if( fKIGUIGlue != nil )
{
fKIGUIGlue->UnRegisterAs( kKIGUIGlue_KEY );
fKIGUIGlue = nil;
}
if( fTransitionMgr != nil )
{
fTransitionMgr->UnRegisterAs( kTransitionMgr_KEY );
fTransitionMgr = nil;
}
delete fConsoleEngine;
fConsoleEngine = nil;
if (fLinkEffectsMgr)
{
fLinkEffectsMgr->UnRegisterAs( kLinkEffectsMgr_KEY);
fLinkEffectsMgr=nil;
}
plClothingMgr::DeInit();
if( fFontCache != nil )
{
fFontCache->UnRegisterAs( kFontCache_KEY );
fFontCache = nil;
}
pfMarkerMgr::Shutdown();
delete fAnimDebugList;
//#ifndef PLASMA_EXTERNAL_RELEASE
if( fConsole != nil )
{
// UnRegisterAs destroys the object for us
fConsole->UnRegisterAs( kConsoleObject_KEY );
fConsole = nil;
}
//#endif
PythonInterface::finiPython();
if (fNewCamera)
fNewCamera->UnRegisterAs( kVirtualCamera1_KEY );
// mark the listener for death.
// there's no need to keep this around...
plUoid lu(kListenerMod_KEY);
plKey pLKey = hsgResMgr::ResMgr()->FindKey(lu);
if (pLKey)
{
plListener* pLMod = plListener::ConvertNoRef(pLKey->GetObjectPtr());
if (pLMod)
pLMod->UnRegisterAs(kListenerMod_KEY);
}
plgAudioSys::Shutdown();
if (pfLocalizationMgr::InstanceValid())
pfLocalizationMgr::Shutdown();
ShutdownDLLs();
plVisLOSMgr::DeInit();
delete fPageMgr;
fPageMgr = nil;
plGlobalVisMgr::DeInit();
#ifdef TRACK_AG_ALLOCS
DumpAGAllocs();
#endif // TRACK_AG_ALLOCS
// This will destruct the client. Do it last.
UnRegisterAs(kClient_KEY);
return false;
}
void plClient::InitAuxInits()
{
// Use another init directory specified in Command line Arg -i
if (fpAuxInitDir)
pfConsoleDirSrc dirSrc( fConsoleEngine, fpAuxInitDir, "*.ini" );
}
void plClient::InitInputs()
{
hsStatusMessage("InitInputs client\n");
fInputManager = TRACKED_NEW plInputManager( fWindowHndl );
fInputManager->CreateInterfaceMod(fPipeline);
fInputManager->RegisterAs( kInput_KEY );
plgDispatch::Dispatch()->RegisterForExactType(plIMouseXEventMsg::Index(), fInputManager->GetKey());
plgDispatch::Dispatch()->RegisterForExactType(plIMouseYEventMsg::Index(), fInputManager->GetKey());
plgDispatch::Dispatch()->RegisterForExactType(plIMouseBEventMsg::Index(), fInputManager->GetKey());
plgDispatch::Dispatch()->RegisterForExactType(plEvalMsg::Index(), fInputManager->GetKey());
plInputDevice* pKeyboard = TRACKED_NEW plKeyboardDevice();
fInputManager->AddInputDevice(pKeyboard);
plInputDevice* pMouse = TRACKED_NEW plMouseDevice();
fInputManager->AddInputDevice(pMouse);
if( fWindowActive )
fInputManager->Activate( true );
}
void plClient::ISetGraphicsDefaults()
{
// couldn't find display mode set defaults write to ini file
wchar graphicsIniFile[MAX_PATH];
PathGetInitDirectory(graphicsIniFile, arrsize(graphicsIniFile));
PathAddFilename(graphicsIniFile, graphicsIniFile, L"graphics.ini", arrsize(graphicsIniFile));
IWriteDefaultGraphicsSettings(graphicsIniFile);
plPipeline::fInitialPipeParams.Windowed = plPipeline::fDefaultPipeParams.Windowed;
plPipeline::fInitialPipeParams.AntiAliasingAmount = plPipeline::fDefaultPipeParams.AntiAliasingAmount;
plPipeline::fInitialPipeParams.AnisotropicLevel = plPipeline::fDefaultPipeParams.AnisotropicLevel;
plPipeline::fInitialPipeParams.TextureQuality = plPipeline::fDefaultPipeParams.TextureQuality;
plPipeline::fInitialPipeParams.VSync = plPipeline::fDefaultPipeParams.VSync;
plShadowCaster::EnableShadowCast(plPipeline::fDefaultPipeParams.Shadows ? true : false);
plQuality::SetQuality(plPipeline::fDefaultPipeParams.VideoQuality);
if( (fClampCap >= 0) && (fClampCap < plQuality::GetCapability()) )
plQuality::SetCapability(fClampCap);
plDynamicCamMap::SetEnabled(plPipeline::fDefaultPipeParams.PlanarReflections ? true : false);
}
hsBool plClient::InitPipeline()
{
hsStatusMessage("InitPipeline client\n");
HWND hWnd = fWindowHndl;
hsG3DDeviceModeRecord dmr;
hsG3DDeviceSelector devSel;
devSel.Enumerate(hWnd);
devSel.RemoveUnusableDevModes(true);
if (!devSel.GetDefault(&dmr))
{
hsMessageBox("No suitable rendering devices found.","Plasma", hsMessageBoxNormal, hsMessageBoxIconError);
return true;
}
hsG3DDeviceRecord *rec = (hsG3DDeviceRecord *)dmr.GetDevice();
int res = -1;
if(!plPipeline::fInitialPipeParams.Windowed)
{
// find our resolution if we're not in windowed mode
for ( int i = 0; i < rec->GetModes().GetCount(); i++ )
{
hsG3DDeviceMode *mode = rec->GetMode(i);
if ((mode->GetWidth() == plPipeline::fInitialPipeParams.Width) &&
(mode->GetHeight() == plPipeline::fInitialPipeParams.Height) &&
(mode->GetColorDepth() == plPipeline::fInitialPipeParams.ColorDepth))
{
res = i;
break;
}
}
if(res != -1)
{
// found it set it as the current mode.
dmr = hsG3DDeviceModeRecord(*rec, *rec->GetMode(res));
}
else
{
ISetGraphicsDefaults();
}
}
if(plPipeline::fInitialPipeParams.TextureQuality == -1)
{
plPipeline::fInitialPipeParams.TextureQuality = dmr.GetDevice()->GetCap(hsG3DDeviceSelector::kCapsPixelShader) ? 2 : 1;
}
else
{
// clamp value to range
if(plPipeline::fInitialPipeParams.TextureQuality > 2) plPipeline::fInitialPipeParams.TextureQuality = 2;
if(plPipeline::fInitialPipeParams.TextureQuality < 0) plPipeline::fInitialPipeParams.TextureQuality = 0;
plBitmap::SetGlobalLevelChopCount(2 - plPipeline::fInitialPipeParams.TextureQuality);
}
plPipeline *pipe = plPipelineCreate::CreatePipeline( hWnd, &dmr );
if( pipe->GetErrorString() != nil )
{
ISetGraphicsDefaults();
#ifdef PLASMA_EXTERNAL_RELEASE
hsMessageBox("There was an error initializing the video card.\nSetting defaults.", "Error", hsMessageBoxNormal);
#else
hsMessageBox( pipe->GetErrorString(), "Error creating pipeline", hsMessageBoxNormal );
#endif
delete pipe;
devSel.GetDefault(&dmr);
pipe = plPipelineCreate::CreatePipeline( hWnd, &dmr );
if(pipe->GetErrorString() != nil)
{
// not much else we can do
return true;
}
}
fPipeline = pipe;
hsVector3 up;
hsPoint3 from, at;
from.Set(0, 0, 10.f);
at.Set(0, 20.f, 10.f);
up.Set(0,0,-1.f);
hsMatrix44 cam;
cam.MakeCamera(&from,&at,&up);
float yon = 500.0f;
pipe->SetFOV( 60.f, hsIntToScalar( 60.f * pipe->Height() / pipe->Width() ) );
pipe->SetDepth( 0.3f, yon );
hsMatrix44 id;
id.Reset();
pipe->SetWorldToCamera( cam, id );
pipe->RefreshMatrices();
// Do this so we're still black before we show progress bars, but the correct color coming out of 'em
fClearColor.Set( 0.f, 0.f, 0.f, 1.f );
pipe->SetClear(&fClearColor);
pipe->ClearRenderTarget();
plAccessGeometry::Init(pipe);
if( fPipeline )
fPipeline->LoadResources();
return false;
}
//============================================================================
void plClient::SetClearColor( hsColorRGBA &color )
{
fClearColor = color;
if( fPipeline != nil )
{
fPipeline->SetClear(&fClearColor, nil);
}
}
//============================================================================
void plClient::IDispatchMsgReceiveCallback()
{
if (fInstance->fProgressBar)
fInstance->fProgressBar->Increment(1);
static char buf[30];
sprintf(buf, "Msg %d", fInstance->fNumPostLoadMsgs);
fInstance->IIncProgress(fInstance->fPostLoadMsgInc, buf);
fInstance->fNumPostLoadMsgs++;
}
//============================================================================
hsBool plClient::MsgReceive(plMessage* msg)
{
if (plGenRefMsg * genRefMsg = plGenRefMsg::ConvertNoRef(msg)) {
// do nothing, we just use the client's key to ref vault image nodes.
return true;
}
plClientRefMsg* pRefMsg = plClientRefMsg::ConvertNoRef(msg);
if (pRefMsg)
{
switch(pRefMsg->fType)
{
case plClientRefMsg::kLoadRoom :
#ifndef PLASMA_EXTERNAL_RELEASE
plStatusLog::AddLineS( "pageouts.log", ".. ClientRefMsg received for room %s", pRefMsg->GetRef() != nil ? pRefMsg->GetRef()->GetKey()->GetUoid().GetObjectName() : "nilref" );
#endif
// was it that the room was loaded?
if (hsCheckBits(pRefMsg->GetContext(), plRefMsg::kOnCreate))
IRoomLoaded(plSceneNode::Convert(pRefMsg->GetRef()), false);
// or was it that the room was unloaded?
else if (hsCheckBits(pRefMsg->GetContext(), plRefMsg::kOnDestroy))
IRoomUnloaded(plSceneNode::Convert(pRefMsg->GetRef()));
#ifndef PLASMA_EXTERNAL_RELEASE
else
plStatusLog::AddLineS("pageouts.log", ".. refMsg is UNHANDLED");
#endif
break;
case plClientRefMsg::kLoadRoomHold:
if (hsCheckBits(pRefMsg->GetContext(), plRefMsg::kOnCreate))
IRoomLoaded(plSceneNode::Convert(pRefMsg->GetRef()), true);
break;
//
// Manually add room.
// Add to pageMgr, but don't load the entire room.
//
case plClientRefMsg::kManualRoom:
{
if (pRefMsg->GetContext() & plRefMsg::kOnCreate ||
pRefMsg->GetContext() & plRefMsg::kOnRequest)
{
hsBool found=false;
plSceneNode *pNode = plSceneNode::ConvertNoRef(pRefMsg->GetRef());
int i;
for (i = 0; i < fRooms.Count(); i++)
{
if (fRooms[i].fNode->GetKey() == pRefMsg->GetSender())
{
found=true;
break;
}
}
if (!found)
{
if (pNode)
{
fRooms.Append( plRoomRec( pNode, 0 ) );
fPageMgr->AddNode(pNode);
}
}
}
else
{
plSceneNode* node = plSceneNode::ConvertNoRef(pRefMsg->GetRef());
if(node)
{
int i;
for (i = 0; i < fRooms.Count(); i++)
{
if (fRooms[i].fNode->GetKey() == node->GetKey())
{
fRooms.Remove(i);
break;
}
}
fPageMgr->RemoveNode(node);
}
}
}
break;
}
}
plClientMsg* pMsg = plClientMsg::ConvertNoRef(msg);
if (pMsg)
{
switch(pMsg->GetClientMsgFlag())
{
case plClientMsg::kQuit:
SetDone(true);
break;
case plClientMsg::kLoadRoom:
case plClientMsg::kLoadRoomHold:
{
IQueueRoomLoad(pMsg->GetRoomLocs(), (pMsg->GetClientMsgFlag() == plClientMsg::kLoadRoomHold));
if (!fHoldLoadRequests)
ILoadNextRoom();
}
break;
case plClientMsg::kUnloadRoom:
IUnloadRooms(pMsg->GetRoomLocs());
break;
case plClientMsg::kLoadNextRoom:
ILoadNextRoom();
break;
// Load optimizations: messages to pre-load and un-load all the keys in a given age
case plClientMsg::kLoadAgeKeys:
{
plResManager *mgr = (plResManager *)hsgResMgr::ResMgr();
mgr->LoadAgeKeys( pMsg->GetAgeName() );
}
break;
case plClientMsg::kReleaseAgeKeys:
{
plResManager *mgr = (plResManager *)hsgResMgr::ResMgr();
mgr->DropAgeKeys( pMsg->GetAgeName() );
}
break;
case plClientMsg::kDisableRenderScene:
{
plClient::GetInstance()->SetFlag( plClient::kFlagDBGDisableRender, true );
}
break;
case plClientMsg::kEnableRenderScene:
{
plClient::GetInstance()->SetFlag( plClient::kFlagDBGDisableRender, false );
}
break;
case plClientMsg::kResetGraphicsDevice:
{
ResetDisplayDevice(pMsg->fGraphicsSettings.fWidth, pMsg->fGraphicsSettings.fHeight, pMsg->fGraphicsSettings.fColorDepth, pMsg->fGraphicsSettings.fWindowed, pMsg->fGraphicsSettings.fNumAASamples, pMsg->fGraphicsSettings.fMaxAnisoSamples, pMsg->fGraphicsSettings.fVSync);
}
break;
case plClientMsg::kSetGraphicsDefaults:
{
ISetGraphicsDefaults();
ResetDisplayDevice(plPipeline::fDefaultPipeParams.Width, plPipeline::fDefaultPipeParams.Height, plPipeline::fDefaultPipeParams.ColorDepth, plPipeline::fDefaultPipeParams.Windowed,
plPipeline::fDefaultPipeParams.AntiAliasingAmount, plPipeline::fDefaultPipeParams.AnisotropicLevel, plPipeline::fDefaultPipeParams.VSync, true);
}
break;
}
return true;
}
plRenderRequestMsg* rendReq = plRenderRequestMsg::ConvertNoRef(msg);
if( rendReq )
{
IAddRenderRequest(rendReq->Request());
return true;
}
plEventCallbackMsg* callback = plEventCallbackMsg::ConvertNoRef(msg);
if( callback )
{
char str[256];
sprintf(str, "Callback event from %s\n", callback->GetSender()
? callback->GetSender()->GetName()
: "Unknown");
hsStatusMessage(str);
static int gotten = 0;
if( ++gotten > 5 )
{
plSimpleModifier* simpMod = plSimpleModifier::ConvertNoRef(callback->GetSender()->ObjectIsLoaded());
plAudible* aud = plAudible::ConvertNoRef(callback->GetSender()->ObjectIsLoaded());
if( simpMod )
{
plAnimCmdMsg* cmd = TRACKED_NEW plAnimCmdMsg;
cmd->AddReceiver(simpMod->GetKey());
cmd->SetCmd(plAnimCmdMsg::kRemoveCallbacks);
cmd->AddCallback(callback);
plgDispatch::MsgSend(cmd);
hsRefCnt_SafeUnRef(callback);
}
else if( aud )
{
plSoundMsg* cmd = TRACKED_NEW plSoundMsg;
cmd->AddReceiver(aud->GetKey());
cmd->SetCmd(plSoundMsg::kRemoveCallbacks);
cmd->AddCallback(callback);
plgDispatch::MsgSend(cmd);
hsRefCnt_SafeUnRef(callback);
}
hsStatusMessage("Removed\n");
gotten = 0;
}
return true;
}
plMovieMsg* mov = plMovieMsg::ConvertNoRef(msg);
if( mov )
{
return IHandleMovieMsg(mov);
}
plLinkEffectsTriggerMsg* linkFX = plLinkEffectsTriggerMsg::ConvertNoRef(msg);
if (linkFX)
{
if (!linkFX->IsLeavingAge())
{
#ifdef MSG_LOADING_BAR
// Temporary stat gathering stuff
#if 0//ndef PLASMA_EXTERNAL_RELEASE
hsUNIXStream s;
s.Open("Messages.txt", "at");
static bool firstLog = true;
if (firstLog)
{
firstLog = false;
s.WriteString("------------------------------------\n");
}
char buf[256];
sprintf(buf, "%s %d\n", plAgeLoader::GetInstance()->GetCurrAgeFilename(), fNumPostLoadMsgs);
s.WriteString(buf);
s.Close();
#endif
#endif
}
return true;
}
//============================================================================
// plNetCommAuthMsg
//============================================================================
if (plNetCommAuthMsg * authCommMsg = plNetCommAuthMsg::ConvertNoRef(msg)) {
IHandleNetCommAuthMsg(authCommMsg);
return true;
}
//============================================================================
// plPreloaderMsg
//============================================================================
if (plPreloaderMsg * preloaderMsg = plPreloaderMsg::ConvertNoRef(msg)) {
IHandlePreloaderMsg(preloaderMsg);
return true;
}
return hsKeyedObject::MsgReceive(msg);
}
//============================================================================
hsBool plClient::IHandleMovieMsg(plMovieMsg* mov)
{
if( !(mov->GetFileName() && *mov->GetFileName()) )
return true;
int i;
i = fMovies.GetCount();
if( !(mov->GetCmd() & plMovieMsg::kMake) )
{
for( i = 0; i < fMovies.GetCount(); i++ )
{
if( !stricmp(mov->GetFileName(), fMovies[i]->GetFileName()) )
break;
}
}
if( i == fMovies.GetCount() )
{
fMovies.Append(TRACKED_NEW plBinkPlayer);
fMovies[i]->SetFileName(mov->GetFileName());
}
if( mov->GetCmd() & plMovieMsg::kAddCallbacks )
{
int j;
for( j = 0; j < mov->GetNumCallbacks(); j++ )
fMovies[i]->AddCallback(mov->GetCallback(j));
}
if( mov->GetCmd() & plMovieMsg::kMove )
fMovies[i]->SetPosition(mov->GetCenter());
if( mov->GetCmd() & plMovieMsg::kScale )
fMovies[i]->SetScale(mov->GetScale());
if( mov->GetCmd() & plMovieMsg::kColorAndOpacity )
fMovies[i]->SetColor(mov->GetColor());
if( mov->GetCmd() & plMovieMsg::kColor )
{
hsColorRGBA c = fMovies[i]->GetColor();
c.Set(mov->GetColor().r, mov->GetColor().g, mov->GetColor().b, c.a);
fMovies[i]->SetColor(c);
}
if( mov->GetCmd() & plMovieMsg::kOpacity )
{
hsColorRGBA c = fMovies[i]->GetColor();
c.a = mov->GetColor().a;
fMovies[i]->SetColor(c);
}
if( mov->GetCmd() & plMovieMsg::kFadeIn )
{
fMovies[i]->SetFadeFromColor(mov->GetFadeInColor());
fMovies[i]->SetFadeFromTime(mov->GetFadeInSecs());
}
if( mov->GetCmd() & plMovieMsg::kFadeOut )
{
fMovies[i]->SetFadeToColor(mov->GetFadeOutColor());
fMovies[i]->SetFadeToTime(mov->GetFadeOutSecs());
}
if( mov->GetCmd() & plMovieMsg::kVolume )
fMovies[i]->SetVolume(mov->GetVolume());
if( mov->GetCmd() & plMovieMsg::kStart )
fMovies[i]->Start(fPipeline, fWindowHndl);
if( mov->GetCmd() & plMovieMsg::kPause )
fMovies[i]->Pause(true);
if( mov->GetCmd() & plMovieMsg::kResume )
fMovies[i]->Pause(false);
if( mov->GetCmd() & plMovieMsg::kStop )
fMovies[i]->Stop();
// If a movie has lost its filename, it means something went horribly wrong
// with playing it and it has shutdown. Or we just stopped it. Either way,
// we need to clear it out of our list.
if( !(fMovies[i]->GetFileName() && *fMovies[i]->GetFileName()) )
{
delete fMovies[i];
fMovies.Remove(i);
}
return true;
}
int plClient::IFindRoomByLoc(const plLocation& loc)
{
for (int i = 0; i < fRooms.Count(); i++)
{
if (fRooms[i].fNode->GetKey()->GetUoid().GetLocation() == loc)
return i;
}
return -1;
}
bool plClient::IIsRoomLoading(const plLocation& loc)
{
for (int i = 0; i < fRoomsLoading.size(); i++)
{
if (fRoomsLoading[i] == loc)
return true;
}
return false;
}
void plClient::SetHoldLoadRequests(bool hold)
{
fHoldLoadRequests = hold;
if (!fHoldLoadRequests)
ILoadNextRoom();
}
#include "../plResMgr/plPageInfo.h"
void plClient::IQueueRoomLoad(const std::vector<plLocation>& locs, bool hold)
{
bool allSameAge = true;
const char* lastAgeName = nil;
UInt32 numRooms = 0;
for (int i = 0; i < locs.size(); i++)
{
const plLocation& loc = locs[i];
const plPageInfo* info = plKeyFinder::Instance().GetLocationInfo(loc);
bool alreadyLoaded = (IFindRoomByLoc(loc) != -1);
bool isLoading = IIsRoomLoading(loc);
if (!info || alreadyLoaded || isLoading)
{
#ifdef HS_DEBUGGING
if (!info)
hsStatusMessageF("Ignoring LoadRoom request for location 0x%x because we can't find the location", loc.GetSequenceNumber());
else if (alreadyLoaded)
hsStatusMessageF("Ignoring LoadRoom request for %s-%s, since room is already loaded", info->GetAge(), info->GetPage());
else if (isLoading)
hsStatusMessageF("Ignoring LoadRoom request for %s-%s, since room is currently loading", info->GetAge(), info->GetPage());
#endif
continue;
}
fLoadRooms.push_back(TRACKED_NEW LoadRequest(loc, hold));
if (!lastAgeName || hsStrEQ(info->GetAge(), lastAgeName))
lastAgeName = info->GetAge();
else
allSameAge = false;
// hsStatusMessageF("+++ Loading room %s-%s", info.GetAge(), info.GetPage());
numRooms++;
}
if (numRooms == 0)
return;
fNumLoadingRooms += numRooms;
}
void plClient::ILoadNextRoom()
{
LoadRequest* req = nil;
while (!fLoadRooms.empty())
{
req = fLoadRooms.front();
fLoadRooms.pop_front();
bool alreadyLoaded = (IFindRoomByLoc(req->loc) != -1);
hsBool isLoading = IIsRoomLoading(req->loc);
if (alreadyLoaded || isLoading)
{
delete req;
req = nil;
fNumLoadingRooms--;
}
else
break;
}
if (req)
{
plClientRefMsg* pRefMsg = TRACKED_NEW plClientRefMsg(GetKey(),
plRefMsg::kOnCreate, -1,
req->hold ? plClientRefMsg::kLoadRoomHold : plClientRefMsg::kLoadRoom);
fRoomsLoading.push_back(req->loc); // flag the location as currently loading
// PageInPage is not guaranteed to finish synchronously, just FYI
plResManager *mgr = (plResManager *)hsgResMgr::ResMgr();
mgr->PageInRoom(req->loc, plSceneNode::Index(), pRefMsg);
delete req;
plClientMsg* nextRoom = TRACKED_NEW plClientMsg(plClientMsg::kLoadNextRoom);
nextRoom->Send(GetKey());
}
}
void plClient::IUnloadRooms(const std::vector<plLocation>& locs)
{
for (int i = 0; i < locs.size(); i++)
{
const plLocation& loc = locs[i];
if (!loc.IsValid())
continue;
plKey nodeKey = nil;
// First, look in our room list. It *should* be there, which allows us to avoid a
// potential nasty reload-find in the resMgr.
int roomIdx = IFindRoomByLoc(loc);
if (roomIdx != -1)
nodeKey = fRooms[roomIdx].fNode->GetKey();
if (nodeKey == nil)
{
nodeKey = plKeyFinder::Instance().FindSceneNodeKey(loc);
}
if (nodeKey != nil)
{
plSceneNode* node = plSceneNode::ConvertNoRef(nodeKey->ObjectIsLoaded());
if (node)
{
#ifndef PLASMA_EXTERNAL_RELEASE
plStatusLog::AddLineS("pageouts.log", "SceneNode for %s loaded; Removing node", node->GetKey()->GetUoid().GetObjectName());
#endif
fPageMgr->RemoveNode(node);
}
else
{
#ifndef PLASMA_EXTERNAL_RELEASE
plStatusLog::AddLineS("pageouts.log", "SceneNode for %s NOT loaded", nodeKey->GetUoid().GetObjectName());
#endif
}
GetKey()->Release(nodeKey); // release notify interest in scene node
UInt32 recFlags = 0;
if (roomIdx != -1)
{
recFlags = fRooms[roomIdx].fFlags;
fRooms.Remove(roomIdx);
}
if (node == fCurrentNode)
fCurrentNode = nil;
#ifndef PLASMA_EXTERNAL_RELEASE
plStatusLog::AddLineS("pageouts.log", "Telling netClientMgr about paging out %s", nodeKey->GetUoid().GetObjectName());
#endif
if (plNetClientMgr::GetInstance() != nil)
{
// Don't care really about the message that just came in, we care whether it was really held or not
if (!hsCheckBits(recFlags, plRoomRec::kHeld))
plAgeLoader::GetInstance()->StartPagingOutRoom(&nodeKey, 1);
// Tell NetClientManager not to expect any pageout info on this guy, since he was held
else
plAgeLoader::GetInstance()->IgnorePagingOutRoom(&nodeKey, 1);
}
}
else
{
#ifndef PLASMA_EXTERNAL_RELEASE
// plStatusLog::AddLineS("pageouts.log", "++ Can't find node key for paging out room %s, loc 0x%x",
// pMsg->GetRoomName() != nil ? pMsg->GetRoomName() : "",
// loc.GetSequenceNumber());
#endif
}
}
}
void plClient::IRoomLoaded(plSceneNode* node, bool hold)
{
fCurrentNode = node;
// make sure we don't already have this room in the list:
hsBool bAppend = true;
for (int i = 0; i < fRooms.Count(); i++)
{
if (fRooms[i].fNode == fCurrentNode)
{
bAppend = false;
break;
}
}
if (bAppend)
{
if (hold)
{
fRooms.Append(plRoomRec(fCurrentNode, plRoomRec::kHeld));
}
else
{
fRooms.Append(plRoomRec(fCurrentNode, 0));
fPageMgr->AddNode(fCurrentNode);
}
}
fNumLoadingRooms--;
// Shut down the progress bar if that was the last room
if (fProgressBar != nil && fNumLoadingRooms <= 0)
{
#ifdef MSG_LOADING_BAR
if (!hold)
{
struct AgeMsgCount { const char* AgeName; int NumMsgs; };
static AgeMsgCount ageMsgCount[] =
{
{ "BahroCave", 2600 },
{ "BaronCityOffice", 670 },
{ "city", 269000 },
{ "Cleft", 11000 },
{ "Garden", 19700 },
{ "Garrison", 28800 },
{ "Gira", 3300 },
{ "Kadish", 19700 },
{ "Neighborhood", 19900 },
{ "Nexus", 1400 },
{ "Personal", 20300 },
{ "Teledahn", 48000 }
};
char name[256];
strcpy(name, &fProgressBar->GetTitle()[strlen("Loading ")]);
name[strlen(name)-3] = '\0';
// Get the precalculated value for how many messages will be
// sent out before the screen actually fades in
int numMsgs = 0;
for (int i = 0; i < sizeof(ageMsgCount)/sizeof(AgeMsgCount); i++)
{
if (hsStrEQ(ageMsgCount[i].AgeName, name))
{
numMsgs = ageMsgCount[i].NumMsgs;
break;
}
}
fNumPostLoadMsgs = 0;
// The last 10% of the age loading bar is for messages, so adjust
// our progress bar increment to fill the bar fully when all
// messages have been sent
float max = fProgressBar->GetMax();
float amtLeft = max - (max * 0.9f);
fPostLoadMsgInc = (numMsgs != 0) ? amtLeft / numMsgs : 0;
#ifndef PLASMA_EXTERNAL_RELEASE
if (plDispatchLogBase::IsLogging())
plDispatchLogBase::GetInstance()->LogStatusBarChange(fProgressBar->GetTitle(), "displaying messages");
#endif // PLASMA_EXTERNAL_RELEASE
#endif
}
}
hsRefCnt_SafeUnRef(fCurrentNode);
plKey pRmKey = fCurrentNode->GetKey();
plAgeLoader::GetInstance()->FinishedPagingInRoom(&pRmKey, 1);
// *** this used to call "ActivateNode" (in physics) which wasn't implemented.
// *** we should make this "turn on" physics for the selected node
// *** depending on what guarantees we can make about the load state -- anything useful?
// now tell all those who are interested that a room was loaded
if (!hold)
{
plRoomLoadNotifyMsg* loadmsg = TRACKED_NEW plRoomLoadNotifyMsg;
loadmsg->SetRoom(pRmKey);
loadmsg->SetWhatHappen(plRoomLoadNotifyMsg::kLoaded);
plgDispatch::MsgSend(loadmsg);
}
else
hsStatusMessageF("Done loading hold room %s, t=%f\n", pRmKey->GetName(), hsTimer::GetSeconds());
plLocation loc = pRmKey->GetUoid().GetLocation();
for (int i = 0; i < fRoomsLoading.size(); i++)
{
if (fRoomsLoading[i] == loc)
{
fRoomsLoading.erase(fRoomsLoading.begin() + i);
break;
}
}
if (!fNumLoadingRooms)
IStopProgress();
}
//============================================================================
void plClient::IRoomUnloaded(plSceneNode* node)
{
#ifndef PLASMA_EXTERNAL_RELEASE
plStatusLog::AddLineS("pageouts.log", ".. refMsg is onDestroy");
#endif
fCurrentNode = node;
hsRefCnt_SafeUnRef(fCurrentNode);
plKey pRmKey = fCurrentNode->GetKey();
if (plAgeLoader::GetInstance())
plAgeLoader::GetInstance()->FinishedPagingOutRoom(&pRmKey, 1);
// tell all those who are interested that a room was unloaded
plRoomLoadNotifyMsg* loadmsg = TRACKED_NEW plRoomLoadNotifyMsg;
loadmsg->SetRoom(pRmKey);
loadmsg->SetWhatHappen(plRoomLoadNotifyMsg::kUnloaded);
plgDispatch::MsgSend(loadmsg);
}
void plClient::IReadKeyedObjCallback(plKey key)
{
fInstance->IIncProgress(1, key->GetName());
}
//============================================================================
void plClient::IProgressMgrCallbackProc(plOperationProgress * progress)
{
if(!fInstance)
return;
fInstance->fMessagePumpProc();
fInstance->IDraw();
}
//============================================================================
void plClient::IIncProgress (hsScalar byHowMuch, const char * text)
{
if (fProgressBar) {
#ifndef PLASMA_EXTERNAL_RELEASE
fProgressBar->SetStatusText( text );
#endif
fProgressBar->Increment( byHowMuch );
}
}
//============================================================================
void plClient::IStartProgress( const char *title, hsScalar len )
{
if (fProgressBar)
{
fProgressBar->SetLength(fProgressBar->GetMax()+len);
}
else
{
fProgressBar = plProgressMgr::GetInstance()->RegisterOperation(len, title, plProgressMgr::kNone, false, true);
#ifndef PLASMA_EXTERNAL_RELEASE
if (plDispatchLogBase::IsLogging())
plDispatchLogBase::GetInstance()->LogStatusBarChange(fProgressBar->GetTitle(), "starting");
#endif // PLASMA_EXTERNAL_RELEASE
((plResManager*)hsgResMgr::ResMgr())->SetProgressBarProc(IReadKeyedObjCallback);
plDispatch::SetMsgRecieveCallback(IDispatchMsgReceiveCallback);
fLastProgressUpdate = 0.f;
}
// Workaround for NVidia driver bug, showing up as BCO not there first time.
// See Mantis bug 0014590.
if( fPipeline )
fPipeline->LoadResources();
}
//============================================================================
void plClient::IStopProgress( void )
{
if (fProgressBar)
{
#ifndef PLASMA_EXTERNAL_RELEASE
if (plDispatchLogBase::IsLogging())
plDispatchLogBase::GetInstance()->LogStatusBarChange(fProgressBar->GetTitle(), "done");
#endif // PLASMA_EXTERNAL_RELEASE
plDispatch::SetMsgRecieveCallback(nil);
((plResManager*)hsgResMgr::ResMgr())->SetProgressBarProc(IReadKeyedObjCallback);
delete fProgressBar;
fProgressBar = nil;
plPipeResReq::Request();
fFlags.SetBit(kFlagGlobalDataLoaded);
if (fFlags.IsBitSet(kFlagAsyncInitComplete))
ICompleteInit();
}
}
/*****************************************************************************
*
*
*
***/
extern hsBool gDataServerLocal;
#include "plQuality.h"
#include "plLoadMask.h"
#if 0
class LoginNetClientCommCallback : public plNetClientComm::Callback
{
public:
enum Op {kAuth, kCreatePlayer, kGetPlayerList, kLeave, kDeletePlayer};
LoginNetClientCommCallback() : plNetClientComm::Callback(), fNumCurrentOps(0)
{}
virtual void OperationStarted( UInt32 context )
{
fNumCurrentOps++;
}
virtual void OperationComplete( UInt32 context, int resultCode )
{
if (context == kAuth)
{
if (hsSucceeded(resultCode))
{
plClient::GetInstance()->fAuthPassed = true;
}
}
else if (context == kGetPlayerList)
{
if ( hsSucceeded( resultCode ) )
{
UInt32 numPlayers = fCbArgs.GetInt(0);
UInt32 pId = -1;
std::string pName;
for (UInt32 i = 0; i < numPlayers; i++)
{
pId = fCbArgs.GetInt((UInt16)(i*3+1));
pName = fCbArgs.GetString((UInt16)(i*3+2));
if (pName == plClient::GetInstance()->fUsername)
{
plClient::GetInstance()->fPlayerID = pId;
break;
}
}
}
}
else if (context == kCreatePlayer)
{
if (hsSucceeded(resultCode))
plClient::GetInstance()->fPlayerID = fCbArgs.GetInt(0);
}
else if (context == kDeletePlayer)
{
if (hsSucceeded(resultCode))
plClient::GetInstance()->fPlayerID = -1;
}
fNumCurrentOps--;
}
bool IsActive()
{
return fNumCurrentOps > 0;
}
private:
int fNumCurrentOps;
};
#endif
//============================================================================
hsBool plClient::StartInit()
{
hsStatusMessage("Init client\n");
fFlags.SetBit( kFlagIniting );
pfLocalizationMgr::Initialize("dat");
plQuality::SetQuality(fQuality);
if( (GetClampCap() >= 0) && (GetClampCap() < plQuality::GetCapability()) )
plQuality::SetCapability(GetClampCap());
/// 2.16.2001 mcn - Moved console engine init to constructor,
/// so we could use console commands even before the pipeline init
plDTProgressMgr::DeclareThyself();
// Set our callback for the progress manager so everybody else can use it
fLastProgressUpdate = 0.f;
plProgressMgr::GetInstance()->SetCallbackProc( IProgressMgrCallbackProc );
// Check the registry, which deletes data files that are either corrupt or
// have old version numbers. If the file still exists on the file server
// then it will be patched on-the-fly as needed (unless you're running with
// local data of course).
((plResManager *)hsgResMgr::ResMgr())->VerifyPages();
// the dx8 audio system MUST be initialized
// before the database is loaded
HWND hWnd = fWindowHndl;
SetForegroundWindow(fWindowHndl);
plgAudioSys::Init(hWnd);
gAudio = plgAudioSys::Sys();
RegisterAs( kClient_KEY );
InitDLLs();
plGlobalVisMgr::Init();
fPageMgr = TRACKED_NEW plPageTreeMgr;
plVisLOSMgr::Init(fPipeline, fPageMgr);
// init globals
plAvatarMgr::GetInstance();
plRelevanceMgr::Init();
gDisp = plgDispatch::Dispatch();
gTimerMgr = plgTimerCallbackMgr::Mgr();
//
// initialize input system
//
InitInputs();
/// Init the console object
/// Note: this can be done last because the console engine was inited first, and
/// everything in code that works with the console does so through the console engine
fConsole = TRACKED_NEW pfConsole();
pfConsole::SetPipeline( fPipeline );
fConsole->RegisterAs( kConsoleObject_KEY ); // fixedKey from plFixedKey.h
fConsole->Init( fConsoleEngine );
/// Init the font cache
fFontCache = TRACKED_NEW plFontCache();
/// Init the transition manager
fTransitionMgr = TRACKED_NEW plTransitionMgr();
fTransitionMgr->RegisterAs( kTransitionMgr_KEY ); // fixedKey from plFixedKey.h
fTransitionMgr->Init();
// Init the Age Linking effects manager
fLinkEffectsMgr = TRACKED_NEW plLinkEffectsMgr();
fLinkEffectsMgr->RegisterAs( kLinkEffectsMgr_KEY ); // fixedKey from plFixedKey.h
fLinkEffectsMgr->Init();
/// Init the in-game GUI manager
fGameGUIMgr = TRACKED_NEW pfGameGUIMgr();
fGameGUIMgr->RegisterAs( kGameGUIMgr_KEY );
fGameGUIMgr->Init();
plgAudioSys::Activate(true);
plConst(hsScalar) delay(2.f);
//commenting out publisher splash for MORE
//IPlayIntroBink("avi/intro0.bik", delay, 0.f, 0.f, 1.f, 1.f, 0.75);
//if( GetDone() ) return false;
IPlayIntroBink("avi/intro1.bik", 0.f, 0.f, 0.f, 1.f, 1.f, 0.75);
if( GetDone() ) return false;
plgDispatch::Dispatch()->RegisterForExactType(plMovieMsg::Index(), GetKey());
//
// Init Net before loading things
//
plgDispatch::Dispatch()->RegisterForExactType(plNetCommAuthMsg::Index(), GetKey());
plNetClientMgr::GetInstance()->Init();
plAgeLoader::GetInstance()->Init();
pfSecurePreloader::GetInstance()->Init();
plCmdIfaceModMsg* pModMsg2 = TRACKED_NEW plCmdIfaceModMsg;
pModMsg2->SetBCastFlag(plMessage::kBCastByExactType);
pModMsg2->SetSender(fConsole->GetKey());
pModMsg2->SetCmd(plCmdIfaceModMsg::kAdd);
plgDispatch::MsgSend(pModMsg2);
// create new the virtual camera
fNewCamera = TRACKED_NEW plVirtualCam1;
fNewCamera->RegisterAs( kVirtualCamera1_KEY );
fNewCamera->Init();
fNewCamera->SetPipeline( GetPipeline() );
float aspectratio = (float)fPipeline->Width() / (float)fPipeline->Height();
plVirtualCam1::SetAspectRatio(aspectratio);
plVirtualCam1::SetFOV(plVirtualCam1::GetFOVw(), plVirtualCam1::GetFOVh());
pfGameGUIMgr::GetInstance()->SetAspectRatio( aspectratio );
plMouseDevice::Instance()->SetDisplayResolution((float)fPipeline->Width(), (float)fPipeline->Height());
// create the listener for the audio system:
plListener* pLMod = TRACKED_NEW plListener;
pLMod->RegisterAs(kListenerMod_KEY );
plgDispatch::Dispatch()->RegisterForExactType(plEvalMsg::Index(), pLMod->GetKey());
plgDispatch::Dispatch()->RegisterForExactType(plAudioSysMsg::Index(), pLMod->GetKey());
plSynchedObject::PushSynchDisabled(false); // enable dirty tracking
if (StrCmp(NetCommGetStartupAge()->ageDatasetName, "StartUp") == 0)
{
plNetCommAuthMsg * msg = NEW(plNetCommAuthMsg);
msg->result = kNetSuccess;
msg->param = nil;
msg->Send();
}
// 2nd half of plClient initialization occurs after
// all network events have completed. Async events:
//
// 1) Download secure files
//
// Continue plClient init via IOnAsyncInitComplete().
return true;
}
//============================================================================
void plClient::IPatchGlobalAgeFiles( void )
{
const char * ageFiles[] = {
"GlobalAnimations",
"GlobalAvatars",
"GlobalClothing",
"GlobalMarkers",
"GUI",
"CustomAvatars"
};
for (unsigned i = 0; i < arrsize(ageFiles); ++i) {
plResPatcher myPatcher(ageFiles[i], true);
if (gDataServerLocal)
break;
if (!myPatcher.Update()) {
SetDone(true);
break;
}
}
}
void plClient::InitDLLs()
{
hsStatusMessage("Init dlls client\n");
char str[255];
typedef void (*PInitGlobalsFunc) (hsResMgr *, plFactory *, plTimerCallbackManager *, plTimerShare*,
plNetClientApp*);
hsFolderIterator modDllFolder("ModDLL\\");
while (modDllFolder.NextFileSuffix(".dll"))
{
modDllFolder.GetPathAndName(str);
HMODULE hMod = LoadLibrary(str);
if (hMod)
{
PInitGlobalsFunc initGlobals = (PInitGlobalsFunc)GetProcAddress(hMod, "InitGlobals");
initGlobals(hsgResMgr::ResMgr(), plFactory::GetTheFactory(), plgTimerCallbackMgr::Mgr(),
hsTimer::GetTheTimer(), plNetClientApp::GetInstance());
fLoadedDLLs.Append(hMod);
}
}
}
void plClient::ShutdownDLLs()
{
int j;
for( j = 0; j < fLoadedDLLs.GetCount(); j++ )
{
BOOL ret = FreeLibrary(fLoadedDLLs[j]);
if( !ret )
hsStatusMessage("Failed to free lib\n");
}
fLoadedDLLs.Reset();
}
hsBool plClient::MainLoop()
{
#ifndef PLASMA_EXTERNAL_RELEASE
if (PythonInterface::UsePythonDebugger())
{
PythonInterface::PythonDebugger()->Update();
if (PythonInterface::PythonDebugger()->IsConnected())
{
bPythonDebugConnected = true;
if (PythonInterface::DebuggerRequestedExit() && PythonInterface::PythonDebugger()->ExitOnStop())
SetDone(true); // debugger requested that we stop running, so exit nicely
}
else
bPythonDebugConnected = false;
}
#endif
#ifdef PLASMA_EXTERNAL_RELEASE
if (DebugIsDebuggerPresent())
{
NetCliAuthLogClientDebuggerConnect();
SetDone(true);
}
#endif
if(plClient::fDelayMS)
Sleep(5);
// Reset our stats
plProfileManager::Instance().BeginFrame();
if (IUpdate())
return true;
if (IDraw())
return true;
plProfileManagerFull::Instance().EndFrame();
plProfileManager::Instance().EndFrame();
// Draw the stats
plProfileManagerFull::Instance().Update();
return false;
}
#include "plProfile.h"
plProfile_Extern(DrawTime);
plProfile_Extern(UpdateTime);
plProfile_CreateTimer("ResMgr", "Update", ResMgr);
plProfile_CreateTimer("DispatchQueue", "Update", DispatchQueue);
plProfile_CreateTimer("RenderSetup", "Update", RenderMsg);
plProfile_CreateTimer("Simulation", "Update", Simulation);
plProfile_CreateTimer("NetTime", "Update", UpdateNetTime);
plProfile_Extern(TimeMsg);
plProfile_Extern(EvalMsg);
plProfile_Extern(TransformMsg);
plProfile_Extern(CameraMsg);
plProfile_Extern(AnimatingPhysicals);
plProfile_Extern(StoppedAnimPhysicals);
plProfile_CreateTimer("BeginRender", "Render", BeginRender);
plProfile_CreateTimer("ClearRender", "Render", ClearRender);
plProfile_CreateTimer("PreRender", "Render", PreRender);
plProfile_CreateTimer("MainRender", "Render", MainRender);
plProfile_CreateTimer("PostRender", "Render", PostRender);
plProfile_CreateTimer("Movies", "Render", Movies);
plProfile_CreateTimer("Console", "Render", Console);
plProfile_CreateTimer("StatusLog", "Render", StatusLog);
plProfile_CreateTimer("ProgressMgr", "Render", ProgressMgr);
plProfile_CreateTimer("ScreenElem", "Render", ScreenElem);
plProfile_CreateTimer("EndRender", "Render", EndRender);
hsBool plClient::IUpdate()
{
plProfile_BeginTiming(UpdateTime);
// reset timer on first frame if realtime and not clamping, to avoid initial large delta
if (hsTimer::GetSysSeconds()==0 && hsTimer::IsRealTime() && hsTimer::GetTimeClamp()==0)
hsTimer::SetRealTime(true);
plProfile_BeginTiming(DispatchQueue);
plgDispatch::Dispatch()->MsgQueueProcess();
plProfile_EndTiming(DispatchQueue);
const char *inputUpdate = "Update";
if (fInputManager) // Is this used anymore? Seems to always be nil.
fInputManager->Update();
hsTimer::IncSysSeconds();
plClientUnifiedTime::SetSysTime(); // keep a unified time, based on sysSeconds
// Time may have been clamped in IncSysSeconds, depending on hsTimer's current mode.
double currTime = hsTimer::GetSysSeconds();
hsScalar delSecs = hsTimer::GetDelSysSeconds();
// do not change this ordering
plProfile_BeginTiming(UpdateNetTime);
plNetClientMgr::GetInstance()->Update(currTime);
plProfile_EndTiming(UpdateNetTime);
// update python
//plCaptureRender::Update(fPipeline);
plCaptureRender::Update();
cyMisc::Update(currTime);
// This TimeMsg doesn't really do much, except somehow it flushes the dispatch
// after the NetClientMgr updates, delivering any SelfDestruct messages in the
// queue. This is important to prevent objects that are about to go away from
// starting trouble during their update. So to get rid of this message, some
// other way of flushing the dispatch after NegClientMgr's update is needed. mf
plProfile_BeginTiming(TimeMsg);
plTimeMsg* msg = TRACKED_NEW plTimeMsg(nil, nil, nil, nil);
plgDispatch::MsgSend(msg);
plProfile_EndTiming(TimeMsg);
plProfile_BeginTiming(EvalMsg);
plEvalMsg* eval = TRACKED_NEW plEvalMsg(nil, nil, nil, nil);
plgDispatch::MsgSend(eval);
plProfile_EndTiming(EvalMsg);
char *xFormLap1 = "Main";
plProfile_BeginLap(TransformMsg, xFormLap1);
plTransformMsg* xform = TRACKED_NEW plTransformMsg(nil, nil, nil, nil);
plgDispatch::MsgSend(xform);
plProfile_EndLap(TransformMsg, xFormLap1);
plCoordinateInterface::SetTransformPhase(plCoordinateInterface::kTransformPhaseDelayed);
if (fAnimDebugList)
fAnimDebugList->ShowReport();
plProfile_BeginTiming(Simulation);
plSimulationMgr::GetInstance()->Advance(delSecs);
plProfile_EndTiming(Simulation);
// At this point, we just register for a plDelayedTransformMsg when dirtied.
if (!plCoordinateInterface::GetDelayedTransformsEnabled())
{
char *xFormLap2 = "Simulation";
plProfile_BeginLap(TransformMsg, xFormLap2);
xform = TRACKED_NEW plTransformMsg(nil, nil, nil, nil);
plgDispatch::MsgSend(xform);
plProfile_EndLap(TransformMsg, xFormLap2);
}
else
{
char *xFormLap3 = "Delayed";
plProfile_BeginLap(TransformMsg, xFormLap3);
xform = TRACKED_NEW plDelayedTransformMsg(nil, nil, nil, nil);
plgDispatch::MsgSend(xform);
plProfile_EndLap(TransformMsg, xFormLap3);
}
plCoordinateInterface::SetTransformPhase(plCoordinateInterface::kTransformPhaseNormal);
plProfile_BeginTiming(CameraMsg);
plCameraMsg* cameras = TRACKED_NEW plCameraMsg;
cameras->SetCmd(plCameraMsg::kUpdateCameras);
cameras->SetBCastFlag(plMessage::kBCastByExactType);
plgDispatch::MsgSend(cameras);
plProfile_EndTiming(CameraMsg);
if (fPatchGlobalAges)
{
// Download or patch our global ages, if necessary
IPatchGlobalAgeFiles();
IOnAsyncInitComplete();
fPatchGlobalAges = false;
}
return false;
}
hsBool plClient::IDrawProgress() {
// HACK: Don't draw while we're caching some room loads, otherwise the
// progress bar will jump around while we're calculating the size
if (fHoldLoadRequests)
return false;
// Reset our stats
plProfileManager::Instance().BeginFrame();
plProfile_BeginTiming(DrawTime);
if( fPipeline->BeginRender() )
{
return IFlushRenderRequests();
}
// Override the clear color to black.
fPipeline->ClearRenderTarget(&hsColorRGBA().Set(0.f, 0.f, 0.f, 1.f));
#ifndef PLASMA_EXTERNAL_RELEASE
fConsole->Draw( fPipeline );
#endif
plStatusLogMgr::GetInstance().Draw();
plProgressMgr::GetInstance()->Draw( fPipeline );
fPipeline->RenderScreenElements();
fPipeline->EndRender();
plProfile_EndTiming(DrawTime);
plProfileManager::Instance().EndFrame();
return false;
}
hsBool plClient::IDraw()
{
// Limit framerate
static float lastDrawTime;
static const float kMaxFrameRate = 1.f/30.f;
float currTime = (float) hsTimer::GetSeconds();
if (!fPipeline->IsDebugFlagSet(plPipeDbg::kFlagNVPerfHUD))
{
// If we're using NVPerfHUD to step through draw calls,
// We're going to have a frame delta of zero. In that
// case we need to draw no matter what, and we don't
// care as much about starving other threads because
// we're presumably just debugging a graphics glitch.
if ((currTime - lastDrawTime) < kMaxFrameRate)
return true;
}
lastDrawTime = currTime;
// If we're shutting down, don't attempt to draw. Doing so
// tends to cause a device reload each frame.
if (fDone)
return true;
if (plProgressMgr::GetInstance()->IsActive())
return IDrawProgress();
plProfile_Extern(VisEval);
plProfile_BeginTiming(VisEval);
plGlobalVisMgr::Instance()->Eval(fPipeline->GetViewPositionWorld());
plProfile_EndTiming(VisEval);
plProfile_BeginTiming(RenderMsg);
plRenderMsg* rendMsg = TRACKED_NEW plRenderMsg(fPipeline);
plgDispatch::MsgSend(rendMsg);
plProfile_EndTiming(RenderMsg);
plPreResourceMsg* preMsg = TRACKED_NEW plPreResourceMsg(fPipeline);
plgDispatch::MsgSend(preMsg);
// This might not be the ideal place for this, but it
// needs to be AFTER the plRenderMsg is sent, and
// BEFORE BeginRender. (plRenderMsg causes construction of
// Dynamic objects (e.g. RT's), BeginRender uses them (e.g. shadows).
if( plPipeResReq::Check() || fPipeline->CheckResources() )
{
fPipeline->LoadResources();
}
plProfile_EndTiming(UpdateTime);
plProfile_BeginTiming(DrawTime);
plProfile_BeginTiming(BeginRender);
if( fPipeline->BeginRender() )
{
plProfile_EndTiming(BeginRender);
return IFlushRenderRequests();
}
plProfile_EndTiming(BeginRender);
plProfile_BeginTiming(ClearRender);
fPipeline->ClearRenderTarget();
plProfile_EndTiming(ClearRender);
plProfile_BeginTiming(PreRender);
if( !fFlags.IsBitSet( kFlagDBGDisableRRequests ) )
IProcessPreRenderRequests();
plProfile_EndTiming(PreRender);
plProfile_BeginTiming(MainRender);
if( !fFlags.IsBitSet( kFlagDBGDisableRender ) )
fPageMgr->Render(fPipeline);
plProfile_EndTiming(MainRender);
plProfile_BeginTiming(PostRender);
if( !fFlags.IsBitSet( kFlagDBGDisableRRequests ) )
IProcessPostRenderRequests();
plProfile_EndTiming(PostRender);
plProfile_BeginTiming(Movies);
IServiceMovies();
plProfile_EndTiming(Movies);
#ifndef PLASMA_EXTERNAL_RELEASE
plProfile_BeginTiming(Console);
fConsole->Draw( fPipeline );
plProfile_EndTiming(Console);
#endif
plProfile_BeginTiming(StatusLog);
plStatusLogMgr::GetInstance().Draw();
plProfile_EndTiming(StatusLog);
plProfile_BeginTiming(ProgressMgr);
plProgressMgr::GetInstance()->Draw( fPipeline );
plProfile_EndTiming(ProgressMgr);
fLastProgressUpdate = hsTimer::GetSeconds();
plProfile_BeginTiming(ScreenElem);
fPipeline->RenderScreenElements();
plProfile_EndTiming(ScreenElem);
plProfile_BeginTiming(EndRender);
fPipeline->EndRender();
plProfile_EndTiming(EndRender);
plProfile_EndTiming(DrawTime);
return false;
}
void plClient::IServiceMovies()
{
int i;
for( i = 0; i < fMovies.GetCount(); i++ )
{
hsAssert(fMovies[i]->GetFileName() && *fMovies[i]->GetFileName(), "Lost our movie");
if( !fMovies[i]->NextFrame() )
{
delete fMovies[i];
fMovies.Remove(i);
i--;
}
}
}
void plClient::IKillMovies()
{
int i;
for( i = 0; i < fMovies.GetCount(); i++ )
delete fMovies[i];
fMovies.Reset();
}
hsBool plClient::IPlayIntroBink(const char* movieName, hsScalar endDelay, hsScalar posX, hsScalar posY, hsScalar scaleX, hsScalar scaleY, hsScalar volume /* = 1.0 */)
{
SetQuitIntro(false);
plBinkPlayer player;
player.SetPosition(posX, posY);
player.SetScale(scaleX, scaleY);
player.SetFileName(movieName);
player.SetFadeToTime(endDelay);
player.SetFadeToColor(hsColorRGBA().Set(0, 0, 0, 1.f));
player.SetVolume(volume);
bool firstTry = true; // flag to make sure that we don't quit before we even start
if( player.Start(fPipeline, fWindowHndl) )
{
while( true )
{
if( fInstance )
fInstance->fMessagePumpProc();
if( GetDone() )
return true;
if (firstTry)
{
firstTry = false;
SetQuitIntro(false);
}
else
{
if( GetQuitIntro() )
return true;
}
hsBool done = false;
if( !fPipeline->BeginRender() )
{
fPipeline->ClearRenderTarget();
done = !player.NextFrame();
fPipeline->EndRender();
}
if( done )
return true;
}
return true;
}
return false;
}
hsBool plClient::IFlushRenderRequests()
{
// For those requesting ack's, we could go through and send them
// mail telling them their request was ill-timed. But hopefully,
// the lack of an acknowledgement will serve as notice.
int i;
for( i = 0; i < fPreRenderRequests.GetCount(); i++ )
hsRefCnt_SafeUnRef(fPreRenderRequests[i]);
fPreRenderRequests.Reset();
for( i = 0; i < fPostRenderRequests.GetCount(); i++ )
hsRefCnt_SafeUnRef(fPostRenderRequests[i]);
fPostRenderRequests.Reset();
return false;
}
void plClient::IProcessRenderRequests(hsTArray<plRenderRequest*>& reqs)
{
int i;
for( i = 0; i < reqs.GetCount(); i++ )
{
reqs[i]->Render(fPipeline, fPageMgr);
hsRefCnt_SafeUnRef(reqs[i]);
}
reqs.SetCount(0);
}
void plClient::IProcessPreRenderRequests()
{
IProcessRenderRequests(fPreRenderRequests);
}
void plClient::IProcessPostRenderRequests()
{
IProcessRenderRequests(fPostRenderRequests);
}
void plClient::IAddRenderRequest(plRenderRequest* req)
{
if( req->GetPriority() < 0 )
{
int i;
for( i = 0; i < fPreRenderRequests.GetCount(); i++ )
{
if( req->GetPriority() < fPreRenderRequests[i]->GetPriority() )
break;
}
fPreRenderRequests.Insert(i, req);
hsRefCnt_SafeRef(req);
}
else
{
int i;
for( i = 0; i < fPostRenderRequests.GetCount(); i++ )
{
if( req->GetPriority() < fPostRenderRequests[i]->GetPriority() )
break;
}
fPostRenderRequests.Insert(i, req);
hsRefCnt_SafeRef(req);
}
}
hsG3DDeviceModeRecord plClient::ILoadDevMode(const char* devModeFile)
{
hsStatusMessage("Load DevMode client\n");
HWND hWnd = fWindowHndl;
hsUNIXStream stream;
hsBool gottaCreate = false;
// If DevModeFind is specified, use the old method
// if ((GetGameFlags() & kDevModeFind))
// FindAndSaveDevMode(hWnd, devModeFile);
// Otherwise, use the new method
hsG3DDeviceModeRecord dmr;
if (stream.Open(devModeFile, "rb"))
{
/// It's there, but is the device record valid?
hsG3DDeviceRecord selRec;
hsG3DDeviceMode selMode;
selRec.Read(&stream);
if( selRec.IsInvalid() )
{
hsStatusMessage( "WARNING: Old DeviceRecord found on file. Setting defaults..." );
gottaCreate = true;
}
else
{
/// Read the rest in
selMode.Read(&stream);
UInt16 performance = stream.ReadSwap16();
if( performance < 25 )
plBitmap::SetGlobalLevelChopCount( 2 );
else if( performance < 75 )
plBitmap::SetGlobalLevelChopCount( 1 );
else
plBitmap::SetGlobalLevelChopCount( 0 );
}
stream.Close();
dmr = hsG3DDeviceModeRecord(selRec, selMode);
}
else
gottaCreate = true;
if( gottaCreate )
{
hsG3DDeviceSelector devSel;
devSel.Enumerate(hWnd);
devSel.RemoveUnusableDevModes(true);
if (!devSel.GetDefault(&dmr))
{
//hsAssert(0, "plGame::LoadDevMode - No acceptable hardware found");
hsMessageBox("No suitable rendering devices found.","realMYST",hsMessageBoxNormal);
return dmr;
}
if (stream.Open(devModeFile, "wb"))
{
dmr.GetDevice()->Write(&stream);
dmr.GetMode()->Write(&stream);
stream.WriteSwap16((UInt16)(0*100));
stream.Close();
}
}
return dmr;
}
void plClient::ResetDisplayDevice(int Width, int Height, int ColorDepth, hsBool Windowed, int NumAASamples, int MaxAnisotropicSamples, hsBool VSync, hsBool windowOnly)
{
if(!fPipeline) return;
int BorderWidth = 0, BorderHeight = 0, CaptionHeight = 0;
WindowActivate(false);
int ActualWidth;
int ActualHeight;
if( Windowed )
{
BorderWidth = GetSystemMetrics( SM_CXSIZEFRAME );
BorderHeight = GetSystemMetrics( SM_CYSIZEFRAME );
CaptionHeight = GetSystemMetrics( SM_CYCAPTION );
ActualWidth = Width + BorderWidth * 2;
ActualHeight = Height + BorderHeight * 2 + CaptionHeight;
SetWindowLong( fWindowHndl, GWL_STYLE,
WS_POPUP | WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_VISIBLE);
SetWindowLong(fWindowHndl, GWL_EXSTYLE, 0);
}
else
{
SetWindowLong(fWindowHndl, GWL_STYLE,
WS_POPUP | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_MAXIMIZE | WS_VISIBLE);
SetWindowLong(fWindowHndl, GWL_EXSTYLE, WS_EX_APPWINDOW);
}
if(!windowOnly)
fPipeline->ResetDisplayDevice(Width, Height, ColorDepth, Windowed, NumAASamples, MaxAnisotropicSamples, VSync);
float aspectratio = (float)Width / (float)Height;
plMouseDevice::Instance()->SetDisplayResolution((float)Width, (float)Height);
pfGameGUIMgr::GetInstance()->SetAspectRatio( aspectratio );
UINT flags = SWP_NOCOPYBITS | SWP_NOMOVE | SWP_SHOWWINDOW;
if(Windowed)
{
SetWindowPos( fWindowHndl, HWND_NOTOPMOST, 0, 0, ActualWidth, ActualHeight, flags );
}
else
{
SetWindowPos( fWindowHndl, HWND_TOP, 0, 0, Width, Height, flags );
::ClipCursor(nil);
}
WindowActivate(true);
}
void WriteBool(hsStream *stream, char *name, hsBool on )
{
char command[256];
sprintf(command, "%s %s\r\n", name, on ? "true" : "false");
stream->WriteString(command);
}
void WriteInt(hsStream *stream, char *name, int val )
{
char command[256];
sprintf(command, "%s %d\r\n", name, val);
stream->WriteString(command);
}
void WriteString(hsStream *stream, const char *name, const char *val)
{
char command[256];
sprintf(command, "%s %s\r\n", name, val);
stream->WriteString(command);
}
// Detect audio/video settings and save them to their respective ini file, if ini files don't exist
void plClient::IDetectAudioVideoSettings()
{
// Setup default pipeline settings
hsBool devmode = true;
hsG3DDeviceModeRecord dmr;
hsG3DDeviceSelector devSel;
devSel.Enumerate(fWindowHndl);
devSel.RemoveUnusableDevModes(true);
if (!devSel.GetDefault(&dmr))
devmode = false;
hsG3DDeviceRecord *rec = (hsG3DDeviceRecord *)dmr.GetDevice();
const hsG3DDeviceMode *mode = dmr.GetMode();
hsBool pixelshaders = rec->GetCap(hsG3DDeviceSelector::kCapsPixelShader);
int psMajor = 0, psMinor = 0;
rec->GetPixelShaderVersion(psMajor, psMinor);
hsBool refDevice = false;
if(rec->GetG3DHALorHEL() == hsG3DDeviceSelector::kHHD3DRefDev)
refDevice = true;
plPipeline::fDefaultPipeParams.Width = hsG3DDeviceSelector::kDefaultWidth;
plPipeline::fDefaultPipeParams.Height = hsG3DDeviceSelector::kDefaultHeight;
plPipeline::fDefaultPipeParams.ColorDepth = hsG3DDeviceSelector::kDefaultDepth;
#if defined(HS_DEBUGGING) || defined(DEBUG)
plPipeline::fDefaultPipeParams.Windowed = true;
#else
plPipeline::fDefaultPipeParams.Windowed = false;
#endif
plPipeline::fDefaultPipeParams.Shadows = 0;
// enable shadows if TnL is available, meaning not an intel extreme.
if(rec->GetG3DHALorHEL() == hsG3DDeviceSelector::kHHD3DTnLHalDev)
plPipeline::fDefaultPipeParams.Shadows = 1;
// enable planar reflections if pixelshaders are available
if(pixelshaders && !refDevice)
{
plPipeline::fDefaultPipeParams.PlanarReflections = 1;
}
else
{
plPipeline::fDefaultPipeParams.PlanarReflections = 0;
}
// enable 2x antialiasing and anisotropic to 2 samples if pixelshader version is greater that 2.0
if(psMajor >= 2 && !refDevice)
{
plPipeline::fDefaultPipeParams.AntiAliasingAmount = rec->GetMaxAnisotropicSamples() ? 2 : 0;
plPipeline::fDefaultPipeParams.AnisotropicLevel = mode->GetNumFSAATypes() ? 2 : 0;
}
else
{
plPipeline::fDefaultPipeParams.AntiAliasingAmount = 0;
plPipeline::fDefaultPipeParams.AnisotropicLevel = 0;
}
if(refDevice)
{
plPipeline::fDefaultPipeParams.TextureQuality = 0;
plPipeline::fDefaultPipeParams.VideoQuality = 0;
}
else
{
plPipeline::fDefaultPipeParams.TextureQuality = psMajor >= 2 ? 2 : 1;
plPipeline::fDefaultPipeParams.VideoQuality = pixelshaders ? 2 : 1;
}
plPipeline::fDefaultPipeParams.VSync = false;
// card specific overrides
if(strstr(rec->GetDriverDesc(), "FX 5200"))
{
plPipeline::fDefaultPipeParams.AntiAliasingAmount = 0;
}
int val = 0;
hsStream *stream = nil;
hsUNIXStream s;
wchar audioIniFile[MAX_PATH], graphicsIniFile[MAX_PATH];
PathGetInitDirectory(audioIniFile, arrsize(audioIniFile));
StrCopy(graphicsIniFile, audioIniFile, arrsize(audioIniFile));
PathAddFilename(audioIniFile, audioIniFile, L"audio.ini", arrsize(audioIniFile));
PathAddFilename(graphicsIniFile, graphicsIniFile, L"graphics.ini", arrsize(graphicsIniFile));
#ifndef PLASMA_EXTERNAL_RELEASE
// internal builds can use the local dir
if (PathDoesFileExist(L"init//audio.ini"))
StrCopy(audioIniFile, L"init//audio.ini", arrsize(audioIniFile));
if (PathDoesFileExist(L"init//graphics.ini"))
StrCopy(graphicsIniFile, L"init//graphics.ini", arrsize(audioIniFile));
#endif
//check to see if audio.ini exists
if (s.Open(audioIniFile))
{
s.Close();
}
else
{
stream = plEncryptedStream::OpenEncryptedFileWrite(audioIniFile);
plAudioCaps caps = plAudioCapsDetector::Detect(false, true);
val = 6;
if( (hsPhysicalMemory() < 256) || plProfileManager::Instance().GetProcessorSpeed() < 1350000000)
{
val = 3;
}
char deviceName[256];
sprintf(deviceName, "\"%s\"", DEFAULT_AUDIO_DEVICE_NAME);
WriteBool(stream, "Audio.Initialize", caps.IsAvailable());
WriteBool(stream, "Audio.UseEAX", false);
WriteInt(stream, "Audio.SetPriorityCutoff", val);
WriteInt(stream, "Audio.MuteAll", false);
WriteInt(stream, "Audio.SetChannelVolume SoundFX", 1);
WriteInt(stream, "Audio.SetChannelVolume BgndMusic", 1);
WriteInt(stream, "Audio.SetChannelVolume Ambience", 1);
WriteInt(stream, "Audio.SetChannelVolume NPCVoice", 1);
WriteInt(stream, "Audio.EnableVoiceRecording", 1);
WriteString(stream, "Audio.SetDeviceName", deviceName );
stream->Close();
delete stream;
stream = nil;
}
// check to see if graphics.ini exists
if (s.Open(graphicsIniFile))
{
s.Close();
}
else
{
IWriteDefaultGraphicsSettings(graphicsIniFile);
}
}
void plClient::IWriteDefaultGraphicsSettings(const wchar* destFile)
{
hsStream *stream = plEncryptedStream::OpenEncryptedFileWrite(destFile);
WriteInt(stream, "Graphics.Width", plPipeline::fDefaultPipeParams.Width);
WriteInt(stream, "Graphics.Height", plPipeline::fDefaultPipeParams.Height);
WriteInt(stream, "Graphics.ColorDepth", plPipeline::fDefaultPipeParams.ColorDepth);
WriteBool(stream, "Graphics.Windowed", plPipeline::fDefaultPipeParams.Windowed);
WriteInt(stream, "Graphics.AntiAliasAmount", plPipeline::fDefaultPipeParams.AntiAliasingAmount);
WriteInt(stream, "Graphics.AnisotropicLevel", plPipeline::fDefaultPipeParams.AnisotropicLevel );
WriteInt(stream, "Graphics.TextureQuality",plPipeline::fDefaultPipeParams.TextureQuality);
WriteInt(stream, "Quality.Level", plPipeline::fDefaultPipeParams.VideoQuality);
WriteInt(stream, "Graphics.Shadow.Enable", plPipeline::fDefaultPipeParams.Shadows);
WriteInt(stream, "Graphics.EnablePlanarReflections", plPipeline::fDefaultPipeParams.PlanarReflections);
WriteBool(stream, "Graphics.EnableVSync", plPipeline::fDefaultPipeParams.VSync);
stream->Close();
delete stream;
stream = nil;
}
void plClient::WindowActivate(bool active)
{
if (GetDone())
return;
if( !fWindowActive != !active )
{
if( fInputManager != nil )
fInputManager->Activate( active );
plArmatureMod::WindowActivate( active );
}
fWindowActive = active;
}
//============================================================================
void plClient::IOnAsyncInitComplete () {
// Init State Desc Language (files should now be downloaded and in place)
plSDLMgr::GetInstance()->SetNetApp(plNetClientMgr::GetInstance());
plSDLMgr::GetInstance()->Init( plSDL::kDisallowTimeStamping );
PythonInterface::initPython();
// set the pipeline for the python cyMisc module so that it can do a screen capture
cyMisc::SetPipeline( fPipeline );
// Load our custom fonts from our current dat directory
fFontCache->LoadCustomFonts("dat");
plWinFontCache::GetInstance().LoadCustomFonts("dat");
// We'd like to do a SetHoldLoadRequests here, but the GUI stuff doesn't draw right
// if you try to delay the loading for it. To work around that, we allocate a
// global loading bar in advance and set it to a big enough range that when the GUI's
// are done loading about the right amount of it is filled.
fNumLoadingRooms++;
IStartProgress("Loading Global...", 0);
/// Init the KI
pfGameGUIMgr *mgr = pfGameGUIMgr::GetInstance();
mgr->LoadDialog( "KIBlackBar" ); // load the blackbar which will bootstrap in the rest of the KI dialogs
// Init the journal book API
pfJournalBook::SingletonInit();
SetHoldLoadRequests(true);
fProgressBar->SetLength(fProgressBar->GetProgress());
plClothingMgr::Init();
// Load in any clothing data
((plResManager*)hsgResMgr::ResMgr())->PageInAge("GlobalClothing");
pfMarkerMgr::Instance();
fAnimDebugList = TRACKED_NEW plAnimDebugList();
/// Now parse final init files (*.fni). These are files just like ini files, only to be run
/// after all hell has broken loose in the client.
wchar initFolder[MAX_PATH];
PathGetInitDirectory(initFolder, arrsize(initFolder));
pfConsoleDirSrc dirSrc( fConsoleEngine, initFolder, L"net*.fni" ); // connect to net first
#ifndef PLASMA_EXTERNAL_RELEASE
// internal builds also parse the local init folder
dirSrc.ParseDirectory( L"init", L"net*.fni" );
#endif
dirSrc.ParseDirectory( initFolder, L"*.fni" );
#ifndef PLASMA_EXTERNAL_RELEASE
// internal builds also parse the local init folder
dirSrc.ParseDirectory( L"init", L"*.fni" );
#endif
// run fni in the Aux Init dir
if (fpAuxInitDir)
{
dirSrc.ParseDirectory(fpAuxInitDir, "net*.fni" ); // connect to net first
dirSrc.ParseDirectory(fpAuxInitDir, "*.fni" );
}
fNumLoadingRooms--;
((plResManager*)hsgResMgr::ResMgr())->PageInAge("GlobalAnimations");
SetHoldLoadRequests(false);
// Tell the transition manager to start faded out. This is so we don't
// get a frame or two of non-faded drawing before we do our initial fade in
(void)(TRACKED_NEW plTransitionMsg( plTransitionMsg::kFadeOut, 0.0f, true ))->Send();
fFlags.SetBit(kFlagAsyncInitComplete);
if (fFlags.IsBitSet(kFlagGlobalDataLoaded))
ICompleteInit();
}
//============================================================================
void plClient::ICompleteInit () {
// Reset clear color on the pipeline
// fPipeline->ClearRenderTarget( &fClearColor, &depth );
plSimulationMgr::GetInstance()->Resume(); // start the sim at the last possible minute
fFlags.SetBit( kFlagIniting, false );
hsStatusMessage("Client init complete.");
// Tell everyone we're ready to rock.
plClientMsg* clientMsg = TRACKED_NEW plClientMsg(plClientMsg::kInitComplete);
clientMsg->SetBCastFlag(plMessage::kBCastByType);
clientMsg->Send();
CsrSrvInitialize();
}
//============================================================================
void plClient::IHandlePreloaderMsg (plPreloaderMsg * msg) {
plgDispatch::Dispatch()->UnRegisterForExactType(plPreloaderMsg::Index(), GetKey());
if (!msg->fSuccess) {
char str[1024];
StrPrintf(
str,
arrsize(str),
"Secure file preloader failed"
);
plNetClientApp::GetInstance()->QueueDisableNet(true, str);
return;
}
fPatchGlobalAges = true;
}
//============================================================================
void plClient::IHandleNetCommAuthMsg (plNetCommAuthMsg * msg) {
plgDispatch::Dispatch()->UnRegisterForExactType(plNetCommAuthMsg::Index(), GetKey());
if (IS_NET_ERROR(msg->result)) {
char str[1024];
StrPrintf(
str,
arrsize(str),
// fmt
"Authentication failed: NetError %u, %S.\n"
,// values
msg->result,
NetErrorToString(msg->result)
);
plNetClientApp::GetInstance()->QueueDisableNet(true, str);
return;
}
plgDispatch::Dispatch()->RegisterForExactType(plPreloaderMsg::Index(), GetKey());
// Precache our secure files
pfSecurePreloader::GetInstance()->RequestFileGroup(L"Python", L"pak");
pfSecurePreloader::GetInstance()->RequestFileGroup(L"SDL", L"sdl");
pfSecurePreloader::GetInstance()->Start();
}