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.
 
 
 
 
 

2538 lines
72 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/>.
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()
{
#if defined(HAVE_CYPYTHONIDE) && !defined(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();
}