1
0
mirror of https://foundry.openuru.org/gitblit/r/CWE-ou-minkata.git synced 2025-07-20 04:09:16 +00:00

Initial Commit of CyanWorlds.com Engine Open Source Client/Plugin

This commit is contained in:
JWPlatt
2011-03-12 12:34:52 -05:00
commit a20a222fc2
3976 changed files with 1301355 additions and 0 deletions

View File

@ -0,0 +1,383 @@
/*==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==*/
#include "plAutoProfile.h"
#include "plgDispatch.h"
#include "../plNetClient/plNetClientMgr.h"
#include "../plNetClient/plNetLinkingMgr.h"
#include "hsStream.h"
#include "hsTimer.h"
#include "../plMessage/plAgeLoadedMsg.h"
#include "../pnTimer/plTimerCallbackManager.h"
#include "../plMessage/plTimerCallbackMsg.h"
#include "../plAvatar/plAvatarMgr.h"
#include "../plAvatar/plArmatureMod.h"
#include "../plModifier/plSpawnModifier.h"
#include "../plMessage/plConsoleMsg.h"
#include "../pnMessage/plClientMsg.h"
#include "../plAgeLoader/plAgeLoader.h"
#include "plProfileManagerFull.h"
#include "../plFile/plFileUtils.h"
#include "../plPipeline/plDebugText.h"
#include "../pnMessage/plTimeMsg.h"
#include "../plStatusLog/plStatusLog.h"
#include "../plVault/plVault.h"
#include "../plContainer/plConfigInfo.h" // for plStringList
// For taking screenshots
#include "../plGImage/plMipmap.h"
#include "../Apps/plClient/plClient.h"
#include "../plJPEG/plJPEG.h"
#include "plPipeline.h"
#include <algorithm>
class plAutoProfileImp : public plAutoProfile
{
protected:
plStringList fAges;
int fNextAge;
int fNextSpawnPoint;
const char* fLastSpawnPointName;
// For profiling a single age
std::string fAgeName;
bool fLinkedToSingleAge;
bool fJustLinkToAges;
UInt64 fLinkTime;
std::string fStatusMessage;
void INextProfile();
bool INextAge();
bool INextSpawnPoint();
void IInit();
void IShutdown();
public:
plAutoProfileImp();
virtual void StartProfile(const char* ageName);
virtual void LinkToAllAges();
virtual hsBool MsgReceive(plMessage* msg);
};
plAutoProfile* plAutoProfile::Instance()
{
static plAutoProfileImp theInstance;
return &theInstance;
}
////////////////////////////////////////////////////////////////////////////////
plAutoProfileImp::plAutoProfileImp() : fNextAge(0), fNextSpawnPoint(0), fLastSpawnPointName(nil), fLinkedToSingleAge(false), fJustLinkToAges(false)
{
}
void plAutoProfileImp::StartProfile(const char* ageName)
{
if (ageName)
fAgeName = ageName;
else
fAgeName = "";
IInit();
plProfileManagerFull::Instance().ActivateAllStats();
}
void plAutoProfileImp::LinkToAllAges()
{
fJustLinkToAges = true;
IInit();
}
void plAutoProfileImp::IInit()
{
// TODO: Find a better way to grab a list of age names, since the old data server
// no longer exists
/*plIDataServer* dataServer = plNetClientMgr::GetInstance()->GetDataServer();
if (!dataServer)
return;
dataServer->GetDatasetAges(fAges);*/
// The first age we link into is AvatarCustomization, since we have a new avatar.
// Coincidentally, the first age in our list is AvatarCustomization. However,
// sometimes linking from ACA to ACA causes a crash. To get around that, we just
// reverse the list, so it's last.
std::reverse(fAges.begin(), fAges.end());
fNextAge = 0;
RegisterAs(kAutoProfile_KEY);
plgDispatch::Dispatch()->RegisterForExactType(plAgeBeginLoadingMsg::Index(), GetKey());
plgDispatch::Dispatch()->RegisterForExactType(plAgeLoadedMsg::Index(), GetKey());
plgDispatch::Dispatch()->RegisterForExactType(plInitialAgeStateLoadedMsg::Index(), GetKey());
plgDispatch::Dispatch()->RegisterForExactType(plEvalMsg::Index(), GetKey());
plConsoleMsg* consoleMsg = TRACKED_NEW plConsoleMsg(plConsoleMsg::kExecuteLine, "Camera.AlwaysCut true");
consoleMsg->Send();
}
#ifdef HS_BUILD_FOR_WIN32
#include "hsWindows.h"
#include <shellapi.h>
#endif
void plAutoProfileImp::IShutdown()
{
// KLUDGE - Copy the load timing log, in case we used it
#define kTimingLog L"readtimings.0.log"
#define kAgeTimingLog L"agetimings.0.log"
wchar destPath[MAX_PATH];
wchar sourcePath[MAX_PATH];
PathAddFilename(destPath, plProfileManagerFull::Instance().GetProfilePath(), kTimingLog, arrsize(destPath));
PathGetLogDirectory(sourcePath, arrsize(sourcePath));
PathAddFilename(sourcePath, sourcePath, kTimingLog, arrsize(sourcePath));
plFileUtils::FileCopy(sourcePath, destPath);
PathAddFilename(destPath, plProfileManagerFull::Instance().GetProfilePath(), kAgeTimingLog, arrsize(destPath));
PathGetLogDirectory(sourcePath, arrsize(sourcePath));
PathAddFilename(sourcePath, sourcePath, kAgeTimingLog, arrsize(sourcePath));
plFileUtils::FileCopy(sourcePath, destPath);
#ifdef HS_BUILD_FOR_WIN32
ShellExecute(nil, nil, "PostRun.bat", nil, nil, SW_SHOWNORMAL);
#endif
plgDispatch::Dispatch()->UnRegisterForExactType(plEvalMsg::Index(), GetKey());
plgDispatch::Dispatch()->UnRegisterForExactType(plInitialAgeStateLoadedMsg::Index(), GetKey());
plgDispatch::Dispatch()->UnRegisterForExactType(plAgeLoadedMsg::Index(), GetKey());
UnRegisterAs(kAutoProfile_KEY);
// Pump the queue so we get fully unregistered
plgDispatch::Dispatch()->MsgQueueProcess();
plClientMsg* clientMsg = TRACKED_NEW plClientMsg(plClientMsg::kQuit);
clientMsg->Send(hsgResMgr::ResMgr()->FindKey(kClient_KEY));
}
void plAutoProfileImp::INextProfile()
{
// Haven't linked to our first age yet, do that before we start profiling
if (fNextAge == 0)
{
if (!INextAge())
IShutdown();
}
else
{
// Log the stats for this spawn point
if (fLastSpawnPointName)
{
const char * ageName = NetCommGetAge()->ageDatasetName;
plProfileManagerFull::Instance().LogStats(ageName, fLastSpawnPointName);
plMipmap mipmap;
if (plClient::GetInstance()->GetPipeline()->CaptureScreen(&mipmap))
{
char fileName[256];
sprintf(fileName, "%s%s_%s.jpg",
plProfileManagerFull::Instance().GetProfilePath(),
ageName, fLastSpawnPointName);
plJPEG::Instance().SetWriteQuality(100);
plJPEG::Instance().WriteToFile(fileName, &mipmap);
}
fLastSpawnPointName = nil;
}
// Try to go to the next spawn point
if (!INextSpawnPoint())
{
// Link to the next age
if (!INextAge())
{
// We've done all the ages, shut down
IShutdown();
}
}
}
}
bool plAutoProfileImp::INextAge()
{
const char* ageName = nil;
if (fAgeName.length() > 0)
{
if (fLinkedToSingleAge)
return false;
fLinkedToSingleAge = true;
ageName = fAgeName.c_str();
}
else
{
if (fNextAge >= fAges.size())
return false;
ageName = fAges[fNextAge].c_str();
}
fNextAge++;
fNextSpawnPoint = 0;
plAgeLinkStruct link;
link.GetAgeInfo()->SetAgeFilename(ageName);
link.SetLinkingRules(plNetCommon::LinkingRules::kBasicLink);
plNetLinkingMgr::GetInstance()->LinkToAge(&link);
fStatusMessage = "Linking to age ";
fStatusMessage += ageName;
return true;
}
bool plAutoProfileImp::INextSpawnPoint()
{
if (fJustLinkToAges)
return false;
const char* kPerfSpawnPrefix = "cPerf-";
int kPerfSpawnLen = strlen(kPerfSpawnPrefix);
// Find the next perf spawn point
bool foundGood = false;
while (fNextSpawnPoint < plAvatarMgr::GetInstance()->NumSpawnPoints())
{
const plSpawnModifier* spawnMod = plAvatarMgr::GetInstance()->GetSpawnPoint(fNextSpawnPoint);
fLastSpawnPointName = spawnMod->GetKeyName();
if (strncmp(fLastSpawnPointName, kPerfSpawnPrefix, kPerfSpawnLen) == 0)
{
fStatusMessage = "Profiling spawn point ";
fStatusMessage += fLastSpawnPointName;
foundGood = true;
break;
}
else
fNextSpawnPoint++;
}
if (!foundGood)
{
fLastSpawnPointName = nil;
fStatusMessage = "No profile spawn point found";
return false;
}
plArmatureMod *avatar = plAvatarMgr::GetInstance()->GetLocalAvatar();
if (avatar)
{
double fakeTime = 0.0f;
avatar->SpawnAt(fNextSpawnPoint, fakeTime);
}
fNextSpawnPoint++;
plTimerCallbackMsg* timerMsg = TRACKED_NEW plTimerCallbackMsg(GetKey());
plgTimerCallbackMgr::NewTimer(30, timerMsg);
return true;
}
hsBool plAutoProfileImp::MsgReceive(plMessage* msg)
{
plEvalMsg* evalMsg = plEvalMsg::ConvertNoRef(msg);
if (evalMsg)
{
if (fStatusMessage.length() > 0)
plDebugText::Instance().DrawString(10, 10, fStatusMessage.c_str());
}
plAgeLoadedMsg* ageLoaded = plAgeLoadedMsg::ConvertNoRef(msg);
if (ageLoaded)
{
if (!ageLoaded->fLoaded)
{
fLinkTime = hsTimer::GetFullTickCount();
hsStatusMessage("Age unloaded");
}
return true;
}
plInitialAgeStateLoadedMsg* ageStateLoaded = plInitialAgeStateLoadedMsg::ConvertNoRef(msg);
if (ageStateLoaded)
{
if (fNextAge > 0)
{
fLinkTime = hsTimer::GetFullTickCount() - fLinkTime;
float ms = hsTimer::FullTicksToMs(fLinkTime);
hsStatusMessageF("Age %s finished load, took %.1f ms",
fAges[fNextAge-1].c_str(),
ms);
plStatusLog::AddLineS("agetimings.log", "Age %s took %.1f ms",
fAges[fNextAge-1].c_str(),
ms);
}
fStatusMessage = "Age loaded. Preparing to profile.";
// Age is loaded, start profiling in 5 seconds (to make sure the avatar is linked in all the way)
plTimerCallbackMsg* timerMsg = TRACKED_NEW plTimerCallbackMsg(GetKey());
plgTimerCallbackMgr::NewTimer(5, timerMsg);
return true;
}
plTimerCallbackMsg* timerMsg = plTimerCallbackMsg::ConvertNoRef(msg);
if (timerMsg)
{
INextProfile();
return true;
}
// When the first age starts to load, register the stupid avatar customization variable
// so the calibration screen won't pop up and ruin everything. I'm sure I could try
// and do this earlier, but I'm not sure when that player folder is set so screw it, it works here.
plAgeBeginLoadingMsg* ageBeginLoadingMsg = plAgeBeginLoadingMsg::ConvertNoRef(msg);
if (ageBeginLoadingMsg)
{
plgDispatch::Dispatch()->UnRegisterForExactType(plAgeBeginLoadingMsg::Index(), GetKey());
VaultAddChronicleEntryAndWait(L"InitialAvCursomizationsDone", 0, L"1");
return true;
}
return false;
}

View File

@ -0,0 +1,46 @@
/*==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==*/
#ifndef plAutoProfile_h_inc
#define plAutoProfile_h_inc
#include "../pnKeyedObject/hsKeyedObject.h"
class plAutoProfile : public hsKeyedObject
{
public:
CLASSNAME_REGISTER(plAutoProfile);
GETINTERFACE_ANY(plAutoProfile, hsKeyedObject);
static plAutoProfile* Instance();
// If ageName is nil, do all ages
virtual void StartProfile(const char* ageName = nil)=0;
// For when we just want to link to each age, for other reasons (profiling load times)
virtual void LinkToAllAges()=0;
};
#endif // plAutoProfile_h_inc

View File

@ -0,0 +1,304 @@
/*==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==*/
#include "plProfile.h"
#include "plProfileManager.h"
#include "../plNetClient/plNetClientMgr.h"
#include "hsTimer.h"
plProfile_CreateCounter("Age Upload BitsPerSec", "Network", UploadAgeBitsPerSec);
plProfile_CreateMemCounter("Upload Bytes", "Network", UploadBW);
plProfile_CreateMemCounter("Upload Avg Packet Size", "Network", UploadAPS);
plProfile_CreateCounter("Upload Num Packets", "Network", UploadPPS);
plProfile_CreateCounter("Upload Avg Packets Queued", "Network", UploadPQ);
plProfile_CreateCounter("Recvd Multiple Acks", "Network", RMultAcksPQ);
plProfile_CreateCounter("Age Download BitsPerSec", "Network", DownloadAgeBitsPerSec);
plProfile_CreateMemCounter("Download Bytes", "Network", DownloadBW);
plProfile_CreateMemCounter("Download Avg Packet Size", "Network", DownloadAPS);
plProfile_CreateCounter("Download Num Packets", "Network", DownloadPPS);
plProfile_CreateCounter("Download Avg Packets Queued", "Network", DownloadPQ);
plProfile_CreateCounter("Download Dropped Pkts", "Network", DownloadDP);
plProfile_CreateCounter("Remote Players", "Network", RemotePlayers);
plProfile_CreateCounter("Peers", "Network", Peers);
plProfile_CreateCounter("Player ID", "Network", PlayerID);
plProfile_CreateCounter("Account ID", "Network", AccountID);
plProfile_CreateTimer("Avg Receipt Time", "Network", AvgReceiptTime);
plProfile_CreateTimer("Peak Receipt Time", "Network", PeakReceiptTime);
#ifdef HS_FIND_MEM_LEAKS
plProfile_CreateMemCounter("Allocated", "Memory", MemAllocated);
plProfile_CreateMemCounter("Peak Alloc", "Memory", MemPeakAlloc);
#endif
static plProfileVar gVarRFPS("RFPS", "General", plProfileVar::kDisplayTime | plProfileVar::kDisplayFPS);
plProfile_Extern(DrawTriangles);
plProfile_Extern(MatChange);
plProfile_CreateCounter("Polys Per Material", "General", PolysPerMat);
#ifdef PL_PROFILE_ENABLED
#define plProfile_GetValue(varName) gProfileVar##varName.GetValue()
#else
#define plProfile_GetValue(varName) 0
#endif
void CalculateProfiles()
{
// KLUDGE - do timing that overlaps the beginframe / endframe (where timing is normally reset)
static UInt32 lastTicks = plProfileManager::GetTime();
UInt32 curTicks = plProfileManager::GetTime();
gVarRFPS.Set(curTicks - lastTicks);
lastTicks = curTicks;
// KLUDGE - calulate the polys per material
if (plProfile_GetValue(MatChange) == 0)
plProfile_Set(PolysPerMat, 0);
else
plProfile_Set(PolysPerMat, plProfile_GetValue(DrawTriangles) / plProfile_GetValue(MatChange));
#ifdef HS_FIND_MEM_LEAKS
// plProfile_Set(MemAllocated, MemGetAllocated());
// plProfile_Set(MemPeakAlloc, MemGetPeakAllocated());
#endif
// Network stats
plNetClientMgr* nc = plNetClientMgr::GetInstance();
if (!nc->GetFlagsBit(plNetClientMgr::kDisabled))
{
#if 0
hsAssert(nc->GetNetCore(), "nil net core in stats?");
plNetCoreStats* ns = nc->GetNetCore()->GetStats();
plProfile_Set(UploadAgeBitsPerSec, (UInt32)nc->GetNetClientStats().GetAgeStatsULBitsPerSec());
plProfile_Set(UploadBW, ns->GetULBits()/8);
plProfile_Set(UploadPPS, ns->GetULPackets());
plProfile_Set(UploadAPS, (UInt32)ns->GetULAvgPacketBytes());
plProfile_Set(UploadPQ, (UInt32)ns->GetULAvgNumPacketsQueued());
plProfile_Set(RMultAcksPQ, nc->GetNetClientStats().GetRecvdMultipleAcks());
plProfile_Set(DownloadAgeBitsPerSec, (UInt32)nc->GetNetClientStats().GetAgeStatsDLBitsPerSec());
plProfile_Set(DownloadBW, ns->GetDLBits()/8);
plProfile_Set(DownloadPPS, ns->GetDLPackets());
plProfile_Set(DownloadAPS, (UInt32)ns->GetDLAvgPacketBytes());
plProfile_Set(DownloadPQ, (UInt32)ns->GetDLAvgNumPacketsQueued());
plProfile_Set(DownloadDP, ns->GetDLDroppedPackets());
plProfile_Set(RemotePlayers, nc->RemotePlayerKeys().size());
plProfile_Set(Peers, ns->GetNumPeers());
plProfile_Set(PlayerID, nc->GetPlayerID());
plProfile_Set(AccountID, nc->GetAccountID());
plProfile_Set(AvgReceiptTime, hsTimer::PrecSecsToTicks(ns->GetAvgReceiptTime()));
plProfile_Set(PeakReceiptTime, hsTimer::PrecSecsToTicks(ns->GetPeakReceiptTime()));
#endif
}
}
#include "../plPipeline/plPlates.h"
static plGraphPlate* fFPSPlate = nil;
static plGraphPlate* fNetBWPlate = nil;
static plGraphPlate* fNetPPSPlate = nil;
static plGraphPlate* fNetQueuesPlate = nil;
static plGraphPlate* fNetAvgBWPlate = nil;
static plGraphPlate* fNetAvgPPSPlate = nil;
static plGraphPlate* fNetAvgQueuesPlate = nil;
static int ICreateStdPlate(plGraphPlate** graph)
{
if (plPlateManager::InstanceValid())
{
plPlateManager::Instance().CreateGraphPlate(graph);
(*graph)->SetSize(0.25, 0.25);
(*graph)->SetDataRange(0, 100, 100);
return hsOK;
}
return hsFail;
}
void CreateStandardGraphs(const char* groupName, bool create)
{
if (strcmp(groupName, "General") == 0)
{
if (create)
{
if (ICreateStdPlate(&fFPSPlate) == hsOK)
{
fFPSPlate->SetTitle("mSecs");
fFPSPlate->SetLabelText("Tot", "Draw", "Upd");
}
}
else
{
plPlateManager::Instance().DestroyPlate(fFPSPlate);
fFPSPlate = nil;
}
}
else if (strcmp(groupName, "Network") == 0)
{
if (create)
{
if (ICreateStdPlate(&fNetBWPlate) == hsOK)
{
fNetBWPlate->SetDataLabels(0, 2000);
fNetBWPlate->SetTitle("Bytes");
fNetBWPlate->SetLabelText("UL", "DL");
ICreateStdPlate(&fNetPPSPlate);
fNetPPSPlate->SetDataLabels(0, 20);
fNetPPSPlate->SetTitle("Packets");
fNetPPSPlate->SetLabelText("UL", "DL");
ICreateStdPlate(&fNetQueuesPlate);
fNetQueuesPlate->SetDataLabels(0, 20);
fNetQueuesPlate->SetTitle("Queue Counts");
fNetQueuesPlate->SetLabelText("UL", "DL");
ICreateStdPlate(&fNetQueuesPlate);
fNetQueuesPlate->SetDataLabels(0, 20);
fNetQueuesPlate->SetTitle("Queue Counts");
fNetQueuesPlate->SetLabelText("UL", "DL");
ICreateStdPlate(&fNetQueuesPlate);
fNetQueuesPlate->SetDataLabels(0, 20);
fNetQueuesPlate->SetTitle("Queue Counts");
fNetQueuesPlate->SetLabelText("UL", "DL");
ICreateStdPlate(&fNetAvgBWPlate);
fNetAvgBWPlate->SetDataLabels(0, 5000);
fNetAvgBWPlate->SetTitle("Avg BytesPS");
fNetAvgBWPlate->SetLabelText("UL", "DL");
ICreateStdPlate(&fNetAvgPPSPlate);
fNetAvgPPSPlate->SetDataLabels(0, 40);
fNetAvgPPSPlate->SetTitle("Avg PacketsPS");
fNetAvgPPSPlate->SetLabelText("UL", "DL");
ICreateStdPlate(&fNetAvgQueuesPlate);
fNetAvgQueuesPlate->SetDataLabels(0, 40);
fNetAvgQueuesPlate->SetTitle("Avg Queue CountsPS");
fNetAvgQueuesPlate->SetLabelText("UL", "DL");
}
}
else
{
plPlateManager::Instance().DestroyPlate(fNetBWPlate);
plPlateManager::Instance().DestroyPlate(fNetPPSPlate);
plPlateManager::Instance().DestroyPlate(fNetQueuesPlate);
plPlateManager::Instance().DestroyPlate(fNetAvgBWPlate);
plPlateManager::Instance().DestroyPlate(fNetAvgPPSPlate);
plPlateManager::Instance().DestroyPlate(fNetAvgQueuesPlate);
fNetBWPlate = nil;
fNetPPSPlate = nil;
fNetQueuesPlate = nil;
fNetAvgBWPlate = nil;
fNetAvgPPSPlate = nil;
fNetAvgQueuesPlate = nil;
}
}
}
plProfile_CreateTimer("Draw", "General", DrawTime);
plProfile_CreateTimer("Update", "General", UpdateTime);
void UpdateStandardGraphs(float xPos, float yPos)
{
#define PositionPlate(plate) \
plate->SetPosition(xPos, yPos); \
yPos += 0.25; \
plate->SetVisible(true);
if (fFPSPlate)
{
fFPSPlate->AddData(
gVarRFPS.GetValue(),
plProfile_GetValue(DrawTime),
plProfile_GetValue(UpdateTime));
PositionPlate(fFPSPlate);
}
plNetClientMgr* nc = plNetClientMgr::GetInstance();
#if 0
plNetCoreStats* ns = nc ? nc->GetNetCore()->GetStats() : nil;
if (!nc || !ns)
return;
if (fNetBWPlate)
{
fNetBWPlate->AddData(
(UInt32)(ns->GetULBits()/8.f),
(UInt32)(ns->GetDLBits()/8.f));
PositionPlate(fNetBWPlate);
}
if (fNetPPSPlate)
{
fNetPPSPlate->AddData(
ns->GetULPackets(),
ns->GetDLPackets());
PositionPlate(fNetPPSPlate);
}
if (fNetQueuesPlate)
{
unsigned int ul, dl;
nc->GetNetCore()->GetOutQueueMsgCount(plNetCore::kPeerAll,ul);
nc->GetNetCore()->GetInQueueMsgCount(plNetCore::kPeerAll,dl);
fNetQueuesPlate->AddData(ul,dl);
PositionPlate(fNetQueuesPlate);
}
if (fNetAvgBWPlate)
{
fNetAvgBWPlate->AddData(
(UInt32)(ns->GetULBitsPS()/8.f),
(UInt32)(ns->GetDLBitsPS()/8.f));
PositionPlate(fNetAvgBWPlate);
}
if (fNetAvgPPSPlate)
{
fNetAvgPPSPlate->AddData(
(Int32)ns->GetULNumPacketsPS(),
(Int32)ns->GetDLNumPacketsPS());
PositionPlate(fNetAvgPPSPlate);
}
if (fNetAvgQueuesPlate)
{
fNetAvgQueuesPlate->AddData(
(Int32)ns->GetULAvgNumPacketsQueued(),
(Int32)ns->GetDLAvgNumPacketsQueued());
PositionPlate(fNetAvgQueuesPlate);
}
#endif
}

View File

@ -0,0 +1,29 @@
/*==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==*/
void CalculateProfiles();
void CreateStandardGraphs(const char* groupName, bool create);
void UpdateStandardGraphs(float xPos, float yPos);

View File

@ -0,0 +1,676 @@
/*==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==*/
#include "plProfileManagerFull.h"
#include "plProfileManager.h"
#include "../plPipeline/plDebugText.h"
#include "../plPipeline/plPlates.h"
#include "plCalculatedProfiles.h"
#include "hsStream.h"
#include "../pnUtils/pnUtils.h"
#include "../plUnifiedTime/plUnifiedTime.h"
#include "../plFile/plFileUtils.h"
plProfileManagerFull::plProfileManagerFull() :
fVars(plProfileManager::Instance().fVars),
fLogStats(false),
fShowLaps(nil),
fMinLap(0),
fDetailGraph(nil)
{
}
plProfileManagerFull& plProfileManagerFull::Instance()
{
static plProfileManagerFull theInstance;
return theInstance;
}
void plProfileManagerFull::GetGroups(GroupSet& groups)
{
groups.clear();
for (int i = 0; i < fVars.size(); i++)
groups.insert(fVars[i]->GetGroup());
}
void plProfileManagerFull::ShowGroup(const char* groupName)
{
if (!groupName)
groupName = "General";
// If we're already showing this group, stop
if (fShowGroups.find(groupName) != fShowGroups.end())
{
CreateStandardGraphs(groupName, false);
fShowGroups.erase(groupName);
ISetActive(groupName, false);
}
else
{
const char* shareGroupName = nil;
for (int i = 0; i < fVars.size(); i++)
{
if (stricmp(fVars[i]->GetGroup(), groupName) == 0)
{
shareGroupName = fVars[i]->GetGroup();
}
}
// We do have a group with this name, so insert one of the variable's
// pointers to the name into our list (we can hang on to those pointers)
if (shareGroupName)
{
ISetActive(shareGroupName, true);
CreateStandardGraphs(shareGroupName, true);
fShowGroups.insert(shareGroupName);
}
}
}
void plProfileManagerFull::ShowNextGroup()
{
const char* curGroup = nil;
if (fShowGroups.begin() != fShowGroups.end())
curGroup = *(fShowGroups.begin());
GroupSet groups;
GetGroups(groups);
const char* nextGroup = nil;
if (curGroup)
{
CreateStandardGraphs(curGroup, false);
GroupSet::iterator it = groups.find(curGroup);
it++;
if (it != groups.end())
{
nextGroup = *it;
}
ISetActive(curGroup,false);
}
else
{
nextGroup = *(groups.begin());
}
fShowGroups.clear();
if (nextGroup)
{
ISetActive(nextGroup, true);
CreateStandardGraphs(nextGroup, true);
fShowGroups.insert(nextGroup);
}
}
plProfileVar* plProfileManagerFull::IFindTimer(const char *name)
{
for (int i = 0; i < fVars.size(); i++)
{
if (stricmp(fVars[i]->GetName(), name) == 0)
return fVars[i];
}
return nil;
}
void plProfileManagerFull::GetLaps(LapNames& lapNames)
{
for (int i = 0; i < fVars.size(); i++)
{
plProfileVar* var = fVars[i];
if (var->GetLaps())
{
LapPair lapPair;
lapPair.group = var->GetGroup();
lapPair.varName = var->GetName();
lapNames.push_back(lapPair);
}
}
}
enum
{
kColName,
kColValue,
kColAvg,
kColMax,
kColIndex,
};
typedef std::vector<plProfileBase*> ProfileGroup;
static void PrintColumn(ProfileGroup& group, const char* groupName, int column, int x, int y, int& width, int& height, int off =0)
{
plDebugText& txt = plDebugText::Instance();
int yInc = txt.GetFontHeight() + 2;
height = 0;
width = 0;
width = hsMaximum(width, txt.CalcStringWidth(groupName) + 1);
txt.DrawString(x, y+height, groupName, 255, 255, 255, 255, plDebugText::kStyleBold);
height += yInc;
UInt32 samplesWidth = txt.CalcStringWidth("[000]");
for (int i = 0; i < group.size(); i++)
{
char str[1024];
switch (column)
{
case kColName:
strcpy(str, group[i]->GetName());
// Since we don't draw the samples text for stats that only have 1 sample,
// if the stat with the longest name is fluctuating between 1 and more than
// 1 sample the width of the column will jump around. So we calculate the
// width based on the stat name plus the width of the widest sample we should
// get
width = hsMaximum(width, txt.CalcStringWidth(str) + samplesWidth + 1);
// Now add on the samples text, if we have any
if (group[i]->GetTimerSamples())
{
char cnt[20];
sprintf(cnt, "[%d]", group[i]->GetTimerSamples());
strcat(str, cnt);
}
break;
case kColValue:
group[i]->PrintValue(str);
break;
case kColAvg:
group[i]->PrintAvg(str);
break;
case kColMax:
group[i]->PrintMax(str);
break;
case kColIndex:
sprintf(str,"[%3d]",i+off);
break;
}
txt.DrawString(x, y+height, str);
if (column != kColName)
width = hsMaximum(width, txt.CalcStringWidth(str) + 1);
height += yInc;
}
// So the columns don't jump around as much as values change, pad them out to a certain width
width = hsMaximum(width, txt.CalcStringWidth("000.0 ms") + 1);
}
static void PrintGroup(ProfileGroup& group, const char* groupName, int& x, int& y)
{
int width, height;
PrintColumn(group, groupName, kColName, x, y, width, height);
x += width + 10;
PrintColumn(group, "Avg", kColAvg, x, y, width, height);
x += width + 10;
PrintColumn(group, "Cur", kColValue, x, y, width, height);
x += width + 10;
PrintColumn(group, "Max", kColMax, x, y, width, height);
x += width + 10;
y += height;
}
static void PrintLapGroup(ProfileGroup& group, const char* groupName, int& x, int& y, int min)
{
int width, height;
if(min > 0)
{
PrintColumn(group, "Index", kColIndex, x, y, width, height, min);
x += width + 10;
}
PrintColumn(group, "Avg", kColAvg, x, y, width, height);
x += width + 10;
PrintColumn(group, groupName, kColName, x, y, width, height);
x += width + 10;
PrintColumn(group, "Cur", kColValue, x, y, width, height);
x += width + 10;
y += height;
}
void plProfileManagerFull::EndFrame()
{
CalculateProfiles();
}
void plProfileManagerFull::Update()
{
if (fLogStats)
ILogStats();
//
// Print the groups we're showing
//
int maxX = 0;
int y = 10;
GroupSet::iterator it;
for (it = fShowGroups.begin(); it != fShowGroups.end(); it++)
{
const char* groupName = *it;
std::vector<plProfileBase*> group;
for (int i = 0; i < fVars.size(); i++)
if (hsStrEQ(fVars[i]->GetGroup(), groupName))
group.push_back(fVars[i]);
int x = 10;
PrintGroup(group, groupName, x, y);
maxX = hsMaximum(maxX, x);
y += 10;
}
//
// Print the laps we're showing
//
if (fShowLaps && fShowLaps->GetLaps())
{
plProfileLaps* laps = fShowLaps->GetLaps();
std::vector<plProfileBase*> group;
int numLaps = laps->GetNumLaps();
if(numLaps < fMinLap)
fMinLap = 0;
for (int i = 0; i < numLaps; i++)
{
if(i >= fMinLap && i < (fMinLap + 40))
group.push_back(laps->GetLap(i));
}
y = 10;
char buf[256];
sprintf(buf, "%s - %s", fShowLaps->GetGroup(), fShowLaps->GetName());
PrintLapGroup(group, buf, maxX, y, fMinLap);
}
//
// Update the graphs
//
float size = 0.25;
float xPos = 1 - size / 2;
float yPos = -1 + size / 2;
for (int i = 0; i < fGraphs.size(); i++)
{
plGraphPlate* graph = fGraphs[i];
plProfileVar* var = IFindTimer(graph->GetTitle());
if (var)
{
graph->SetPosition(xPos, yPos);
graph->AddData(var->GetValue());
graph->SetVisible(true);
yPos += size;
}
}
UpdateStandardGraphs(xPos, yPos);
float detailSize = 0.9;
float detailX = 1 - detailSize / 2;
float detailY = 1 - detailSize / 2;
if (fDetailGraph)
{
fDetailGraph->SetPosition(detailX,detailY);
double value;
double scale;
int i;
std::vector<Int32> values;
for (i=0; i<fDetailVars.size(); i++)
{
value = (double)fDetailVars[i].var->GetValue();
scale = 100.0/((double)(fDetailVars[i].max-fDetailVars[i].min));
value = scale*value-fDetailVars[i].min;
values.push_back((Int32)value);
}
fDetailGraph->AddData(values);
fDetailGraph->SetVisible(true);
}
}
void plProfileManagerFull::ActivateAllStats()
{
for (int i = 0; i < fVars.size(); i++)
{
fVars[i]->SetActive(true);
fVars[i]->Start();
}
}
void plProfileManagerFull::IPrintGroup(hsStream* s, const char* groupName, bool printTitle)
{
char buf[256];
for (int i = 0; i < fVars.size(); i++)
{
plProfileVar* var = fVars[i];
if (hsStrEQ(var->GetGroup(), groupName))
{
if (printTitle)
sprintf(buf, "%s:%s", var->GetGroup(), var->GetName());
else
var->PrintAvg(buf, false);
s->Write(strlen(buf), buf);
s->WriteByte(',');
}
}
}
void plProfileManagerFull::LogStats(const char* ageName, const char* spawnName)
{
fLogStats = true;
wchar* temp = hsStringToWString(ageName);
fLogAgeName = temp;
delete [] temp;
fLogSpawnName = spawnName;
}
const wchar* plProfileManagerFull::GetProfilePath()
{
static wchar profilePath[MAX_PATH];
static bool initialized = false;
if (!initialized)
{
initialized = true;
plUnifiedTime curTime = plUnifiedTime::GetCurrentTime(plUnifiedTime::kLocal);
PathGetUserDirectory(profilePath, arrsize(profilePath));
PathAddFilename(profilePath, profilePath, L"Profile", arrsize(profilePath));
plFileUtils::CreateDir(profilePath);
wchar buff[256];
swprintf(buff, L"%02d-%02d-%04d_%02d-%02d//",
curTime.GetMonth(),
curTime.GetDay(),
curTime.GetYear(),
curTime.GetHour(),
curTime.GetMinute());
PathAddFilename(profilePath, profilePath, buff, arrsize(profilePath));
plFileUtils::CreateDir(profilePath);
}
return profilePath;
}
void plProfileManagerFull::ILogStats()
{
wchar statFilename[256];
swprintf(statFilename, L"%s%s.csv", GetProfilePath(), fLogAgeName.c_str());
bool exists = plFileUtils::FileExists(statFilename);
hsUNIXStream s;
if (s.Open(statFilename, L"ab"))
{
GroupSet groups;
GetGroups(groups);
GroupSet::iterator it;
if (!exists)
{
const char* kSpawn = "Spawn";
s.Write(strlen(kSpawn), kSpawn);
s.WriteByte(',');
for (it = groups.begin(); it != groups.end(); it++)
{
const char* groupName = *it;
IPrintGroup(&s, groupName, true);
}
s.WriteByte('\r');
s.WriteByte('\n');
}
s.Write(fLogSpawnName.length(), fLogSpawnName.c_str());
s.WriteByte(',');
for (it = groups.begin(); it != groups.end(); it++)
{
const char* groupName = *it;
IPrintGroup(&s, groupName);
}
s.WriteByte('\r');
s.WriteByte('\n');
s.Close();
}
fLogStats = false;
fLogAgeName = L"";
fLogSpawnName = "";
}
void plProfileManagerFull::ShowLaps(const char* groupName, const char* varName)
{
plProfileVar* var = nil;
if(fShowLaps)
fShowLaps->SetLapsActive(false);
for (int i = 0; i < fVars.size(); i++)
{
int j = 0;
while(fVars[i]->GetName()[j++] == ' ') {}
if (stricmp(&(fVars[i]->GetName()[j-1]), varName) == 0 &&
stricmp(fVars[i]->GetGroup(), groupName) == 0)
{
var = fVars[i];
break;
}
}
if (var)
{
if (var == fShowLaps)
{
fShowLaps = nil;
}
else
{
fShowLaps = var;
}
}
if(fShowLaps)
fShowLaps->SetLapsActive(true);
}
void plProfileManagerFull::CreateGraph(const char* varName, UInt32 min, UInt32 max)
{
// If the graph is already created, destroy it
for (int i = 0; i < fGraphs.size(); i++)
{
if (strcmp(fGraphs[i]->GetTitle(), varName) == 0)
{
plPlateManager::Instance().DestroyPlate(fGraphs[i]);
fGraphs.erase(fGraphs.begin()+i);
return;
}
}
plProfileVar* var = IFindTimer(varName);
if (var)
{
plGraphPlate* graph = nil;
plPlateManager::Instance().CreateGraphPlate(&graph);
graph->SetSize(0.25, 0.25);
graph->SetDataRange(min, max, 100);
graph->SetTitle(var->GetName());
fGraphs.push_back(graph);
}
}
void plProfileManagerFull::ResetDefaultDetailVars()
{
fDetailVars.clear();
AddDetailVar("ApplyAnimation",0,50);
AddDetailVar("AnimatingPhysicals",0,50);
AddDetailVar("StoppedAnimPhysicals",0,50);
AddDetailVar("DrawableTime",0,50);
AddDetailVar("Polys",0,150000);
AddDetailVar("Step",0,50);
AddDetailVar("LineOfSight",0,50);
AddDetailVar(" PhysicsUpdates",0,50);
AddDetailVar("Stream Shove Time",0,50);
AddDetailVar("RenderSetup",0,50);
}
void plProfileManagerFull::ShowDetailGraph()
{
// if graph is already created, kill it
if (fDetailGraph)
HideDetailGraph();
if (fDetailVars.size() == 0)
ResetDefaultDetailVars();
plPlateManager::Instance().CreateGraphPlate(&fDetailGraph);
fDetailGraph->SetSize(0.9,0.9);
fDetailGraph->SetDataRange(0,500,500);
fDetailGraph->SetDataLabels(0,100); // should be relatively simple to cast everything to a 0-100 range
fDetailGraph->SetTitle("Detail");
UpdateDetailLabels();
}
void plProfileManagerFull::HideDetailGraph()
{
if (fDetailGraph)
{
plPlateManager::Instance().DestroyPlate(fDetailGraph);
fDetailGraph = nil;
}
}
void plProfileManagerFull::AddDetailVar(const char* varName, UInt32 min, UInt32 max)
{
int i=0;
for (i=0; i<fDetailVars.size(); i++)
{
if (stricmp(fDetailVars[i].var->GetName(), varName) == 0)
return; // don't add it again
}
plProfileVar* var = IFindTimer(varName);
if (!var)
return;
var->SetActive(true);
if (fDetailVars.size() == 10)
fDetailVars.erase(fDetailVars.begin()); // we don't want any more then 10 at this point, so drop the oldest one
detailVar temp;
temp.var = var;
temp.min = min;
temp.max = max;
fDetailVars.push_back(temp);
UpdateDetailLabels();
}
void plProfileManagerFull::RemoveDetailVar(const char* varName)
{
int i=0;
for (i=0; i<fDetailVars.size(); i++)
{
if (stricmp(fDetailVars[i].var->GetName(), varName) == 0)
{
fDetailVars.erase(fDetailVars.begin()+i);
}
}
UpdateDetailLabels();
}
void plProfileManagerFull::UpdateDetailLabels()
{
if (fDetailGraph)
{
int i;
std::vector<std::string> labels;
for (i=0; i<fDetailVars.size(); i++)
labels.push_back(fDetailVars[i].var->GetName());
fDetailGraph->SetLabelText(labels);
// update the colors as well, just in case
std::vector<UInt32> colors;
colors.push_back(0xff00ff00); // green
colors.push_back(0xff0000ff); // blue
colors.push_back(0xffffff00); // yellow
colors.push_back(0xffff00ff); // pink
colors.push_back(0xffffffff); // white
colors.push_back(0xff00ffff); // cyan
colors.push_back(0xffff8000); // orange
colors.push_back(0xff8000ff); // purple
colors.push_back(0xffff0080); // fuscha
colors.push_back(0xff808080); // grey
fDetailGraph->SetDataColors(colors);
}
}
void plProfileManagerFull::ResetMax()
{
for (int i = 0; i < fVars.size(); i++)
fVars[i]->ResetMax();
}
void plProfileManagerFull::ISetActive(const char* groupName, bool active)
{
for (int i = 0; i < fVars.size(); i++)
{
if (stricmp(fVars[i]->GetGroup(), groupName) == 0)
{
fVars[i]->SetActive(active);
}
}
}

View File

@ -0,0 +1,115 @@
/*==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==*/
#ifndef plProfileManagerFull_h_inc
#define plProfileManagerFull_h_inc
#include "plProfileManager.h"
#include "hsStlUtils.h"
#include "hsStlSortUtils.h"
class plProfileManager;
class plGraphPlate;
class plStatusLog;
class hsStream;
class plProfileVar;
class plProfileManagerFull
{
public:
typedef std::set<const char*, stringISorter> GroupSet;
protected:
plProfileManager::VarVec& fVars;
bool fLogStats; // If true, log the stats at the end of the frame
std::wstring fLogAgeName;
std::string fLogSpawnName;
std::vector<plGraphPlate*> fGraphs;
plGraphPlate* fDetailGraph;
struct detailVar
{
plProfileVar* var;
Int32 min;
Int32 max;
};
std::vector<detailVar> fDetailVars; // the vars we want to show on the detail graph
GroupSet fShowGroups;
plProfileVar* fShowLaps;
UInt32 fMinLap; // For Display
void IPrintGroup(hsStream* s, const char* groupName, bool printTitle=false);
void ILogStats();
plProfileVar* IFindTimer(const char* name);
void ISetActive(const char* groupName, bool active);
plProfileManagerFull();
public:
static plProfileManagerFull& Instance();
void EndFrame(); // Call end frame on our special timers
void Update();
void GetGroups(GroupSet& groups);
void ShowGroup(const char* groupName);
void ShowNextGroup();
struct LapPair { const char* group; const char* varName; };
typedef std::vector<LapPair> LapNames;
void GetLaps(LapNames& lapNames);
void ShowLaps(const char* groupName, const char* varName);
void SetMinLap(int m) { fMinLap = m; };
void PageDownLaps() { fMinLap += 40; }
void PageUpLaps() { fMinLap = (fMinLap < 40) ? 0 : fMinLap - 40;}
void CreateGraph(const char* varName, UInt32 min, UInt32 max);
void ResetDefaultDetailVars();
void ShowDetailGraph();
void HideDetailGraph();
void AddDetailVar(const char* varName, UInt32 min, UInt32 max);
void RemoveDetailVar(const char* varName);
void UpdateDetailLabels();
void ResetMax();
void LogStats(const char* ageName, const char* spawnName);
const wchar* GetProfilePath();
// If you're going to call LogStats, make sure to call this first so all stats will be evaluated before logging
void ActivateAllStats();
};
#endif // plProfileManagerFull_h_inc

View File

@ -0,0 +1,34 @@
/*==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==*/
#ifndef plStatGatherCreatable_h_inc
#define plStatGatherCreatable_h_inc
#include "../pnFactory/plCreator.h"
#include "plAutoProfile.h"
REGISTER_NONCREATABLE(plAutoProfile);
#endif // plStatGatherCreatable_h_inc