Merge branch 'main' into ly-as-sdk/LYN-2948
This commit is contained in:
@@ -1044,11 +1044,9 @@ void CSystem::AutoDetectSpec(const bool detectResolution)
|
||||
unsigned int numSysCores(1), numProcCores(1);
|
||||
Win32SysInspect::GetNumCPUCores(numSysCores, numProcCores);
|
||||
CryLogAlways("--- Number of available cores: %d (out of %d)", numProcCores, numSysCores);
|
||||
const int numLogicalProcs = gEnv->pi.numLogicalProcessors;
|
||||
CryLogAlways("--- Number of logical processors: %d", numLogicalProcs);
|
||||
|
||||
// get CPU rating
|
||||
const int cpuRating = numLogicalProcs >= 8 ? 3 : (numLogicalProcs >= 6 ? 2 : 1);
|
||||
const int cpuRating = numProcCores >= 4 ? 3 : (numProcCores >= 3 ? 2 : 1);
|
||||
|
||||
// get GPU info
|
||||
unsigned int gpuVendorId(0), gpuDeviceId(0), totVidMem(0);
|
||||
|
||||
@@ -89,7 +89,6 @@ ly_add_target(
|
||||
Legacy::CrySystem.Static
|
||||
AZ::AzCore
|
||||
Legacy::CryCommon
|
||||
Legacy::CryCommon.EngineSettings.Static
|
||||
)
|
||||
|
||||
################################################################################
|
||||
@@ -109,7 +108,6 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED)
|
||||
PRIVATE
|
||||
AZ::AzTest
|
||||
Legacy::CryCommon
|
||||
Legacy::CryCommon.EngineSettings.Static
|
||||
Legacy::CrySystem.Static
|
||||
AZ::AzFramework
|
||||
)
|
||||
|
||||
@@ -87,14 +87,6 @@ bool CConsoleBatchFile::ExecuteConfigFile(const char* sFilename)
|
||||
filename = PathUtil::ReplaceExtension(filename, "cfg");
|
||||
}
|
||||
|
||||
#if defined(CVARS_WHITELIST)
|
||||
bool ignoreWhitelist = true;
|
||||
if (_stricmp(sFilename, "autoexec.cfg") == 0)
|
||||
{
|
||||
ignoreWhitelist = false;
|
||||
}
|
||||
#endif // defined(CVARS_WHITELIST)
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
CCryFile file;
|
||||
|
||||
@@ -179,18 +171,9 @@ bool CConsoleBatchFile::ExecuteConfigFile(const char* sFilename)
|
||||
continue;
|
||||
}
|
||||
|
||||
#if defined(CVARS_WHITELIST)
|
||||
if (ignoreWhitelist || (gEnv->pSystem->GetCVarsWhiteList() && gEnv->pSystem->GetCVarsWhiteList()->IsWhiteListed(strLine, false)))
|
||||
#endif // defined(CVARS_WHITELIST)
|
||||
{
|
||||
m_pConsole->ExecuteString(strLine);
|
||||
}
|
||||
#if defined(CVARS_WHITELIST)
|
||||
else if (gEnv->IsDedicated())
|
||||
{
|
||||
gEnv->pSystem->GetILog()->LogError("Failed to execute command: '%s' as it is not whitelisted\n", strLine.c_str());
|
||||
}
|
||||
#endif // defined(CVARS_WHITELIST)
|
||||
}
|
||||
// See above
|
||||
// ((CXConsole*)m_pConsole)->SetStatus(bConsoleStatus);
|
||||
|
||||
@@ -117,7 +117,6 @@ struct IRenderer;
|
||||
struct ISystem;
|
||||
struct ITimer;
|
||||
struct IFFont;
|
||||
struct IKeyboard;
|
||||
struct ICVar;
|
||||
struct IConsole;
|
||||
struct IProcess;
|
||||
|
||||
@@ -1,259 +0,0 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
// Original file Copyright Crytek GMBH or its affiliates, used under license.
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// NOTE: INTERNAL HEADER NOT FOR PUBLIC USE
|
||||
// This header should only be include by SystemThreading.cpp only
|
||||
// It provides an interface for PThread intrinsics
|
||||
// It's only client should be CThreadManager which should manage all thread interaction
|
||||
#if !defined(INCLUDED_FROM_SYSTEM_THREADING_CPP)
|
||||
# error "CRYTEK INTERNAL HEADER. ONLY INCLUDE FROM SYSTEMTHRADING.CPP."
|
||||
#endif
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#define DEFAULT_THREAD_STACK_SIZE_KB 0
|
||||
#define CRY_PTHREAD_THREAD_NAME_MAX 16
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// THREAD CREATION AND MANAGMENT
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
namespace CryThreadUtil
|
||||
{
|
||||
// Define type for platform specific thread handle
|
||||
typedef pthread_t TThreadHandle;
|
||||
|
||||
struct SThreadCreationDesc
|
||||
{
|
||||
// Define platform specific thread entry function functor type
|
||||
typedef void* (* EntryFunc)(void*);
|
||||
|
||||
const char* szThreadName;
|
||||
EntryFunc fpEntryFunc;
|
||||
void* pArgList;
|
||||
uint32 nStackSizeInBytes;
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
TThreadHandle CryGetCurrentThreadHandle()
|
||||
{
|
||||
return (TThreadHandle)pthread_self();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Note: Handle must be closed lated via CryCloseThreadHandle()
|
||||
TThreadHandle CryDuplicateThreadHandle(const TThreadHandle& hThreadHandle)
|
||||
{
|
||||
// Do not do anything
|
||||
// If you add a new platform which duplicates handles make sure to mirror the change in CryCloseThreadHandle(..)
|
||||
return hThreadHandle;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void CryCloseThreadHandle(TThreadHandle& hThreadHandle)
|
||||
{
|
||||
pthread_detach(hThreadHandle);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
threadID CryGetCurrentThreadId()
|
||||
{
|
||||
return threadID(pthread_self());
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
threadID CryGetThreadId(TThreadHandle hThreadHandle)
|
||||
{
|
||||
return threadID(hThreadHandle);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Note: On OSX the thread name can only be set by the thread itself.
|
||||
void CrySetThreadName(TThreadHandle pThreadHandle, const char* sThreadName)
|
||||
{
|
||||
char threadName[CRY_PTHREAD_THREAD_NAME_MAX];
|
||||
if (!cry_strcpy(threadName, sThreadName))
|
||||
{
|
||||
CryLog("<ThreadInfo> CrySetThreadName: input thread name '%s' truncated to '%s'", sThreadName, threadName);
|
||||
}
|
||||
#if AZ_TRAIT_OS_PLATFORM_APPLE
|
||||
// On OSX the thread name can only be set by the thread itself.
|
||||
assert(pthread_equal(pthread_self(), (pthread_t )pThreadHandle));
|
||||
|
||||
if (pthread_setname_np(threadName) != 0)
|
||||
#else
|
||||
if (pthread_setname_np(pThreadHandle, threadName) != 0)
|
||||
#endif
|
||||
{
|
||||
switch (errno)
|
||||
{
|
||||
case ERANGE:
|
||||
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "<ThreadInfo> CrySetThreadName: Unable to rename thread \"%s\". Error Msg: \"Name to long. Exceeds %d bytes.\"", sThreadName, CRY_PTHREAD_THREAD_NAME_MAX);
|
||||
break;
|
||||
default:
|
||||
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "<ThreadInfo> CrySetThreadName: Unsupported error code: %i", errno);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void CrySetThreadAffinityMask(TThreadHandle pThreadHandle, DWORD dwAffinityMask)
|
||||
{
|
||||
#if defined(AZ_PLATFORM_ANDROID)
|
||||
// Not supported on ANDROID
|
||||
// Alternative solution
|
||||
// Watch out that android will clear the mask after a core has been switched off hence loosing the affinity mask setting!
|
||||
// http://stackoverflow.com/questions/16319725/android-set-thread-affinity
|
||||
#elif AZ_TRAIT_OS_PLATFORM_APPLE
|
||||
# pragma message "Warning: <ThreadInfo> CrySetThreadAffinityMask not implemented for platform"
|
||||
// Implementation details can be found here
|
||||
// https://developer.apple.com/library/mac/releasenotes/Performance/RN-AffinityAPI/
|
||||
#else
|
||||
cpu_set_t cpu_mask;
|
||||
CPU_ZERO(&cpu_mask);
|
||||
for (int cpu = 0; cpu < sizeof(cpu_mask) * 8; ++cpu)
|
||||
{
|
||||
if (dwAffinityMask & (1 << cpu))
|
||||
{
|
||||
CPU_SET(cpu, &cpu_mask);
|
||||
}
|
||||
}
|
||||
|
||||
if (sched_setaffinity(0, sizeof(cpu_mask), &cpu_mask) != 0)
|
||||
{
|
||||
switch (errno)
|
||||
{
|
||||
case EFAULT:
|
||||
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "<ThreadInfo> CrySetThreadAffinityMask: Supplied memory address was invalid.");
|
||||
break;
|
||||
case EINVAL:
|
||||
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "<ThreadInfo> CrySetThreadAffinityMask: The affinity bit mask [%u] contains no processors that are currently physically on the system and permitted to the process .", dwAffinityMask);
|
||||
break;
|
||||
case EPERM:
|
||||
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "<ThreadInfo> CrySetThreadAffinityMask: The calling process does not have appropriate privileges. Mask [%u].", dwAffinityMask);
|
||||
break;
|
||||
case ESRCH:
|
||||
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "<ThreadInfo> CrySetThreadAffinityMask: The process whose ID is pid could not be found.");
|
||||
break;
|
||||
default:
|
||||
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "<ThreadInfo> CrySetThreadAffinityMask: Unsupported error code: %i", errno);
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void CrySetThreadPriority(TThreadHandle pThreadHandle, DWORD dwPriority)
|
||||
{
|
||||
int policy;
|
||||
struct sched_param param;
|
||||
|
||||
pthread_getschedparam(pThreadHandle, &policy, ¶m);
|
||||
param.sched_priority = sched_get_priority_max(dwPriority);
|
||||
pthread_setschedparam(pThreadHandle, policy, ¶m);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void CrySetThreadPriorityBoost(TThreadHandle pThreadHandle, bool bEnabled)
|
||||
{
|
||||
// Not supported
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool CryCreateThread(TThreadHandle* pThreadHandle, const SThreadCreationDesc& threadDesc)
|
||||
{
|
||||
uint32 nStackSize = threadDesc.nStackSizeInBytes != 0 ? threadDesc.nStackSizeInBytes : DEFAULT_THREAD_STACK_SIZE_KB * 1024;
|
||||
|
||||
assert(pThreadHandle != reinterpret_cast<TThreadHandle*>(THREADID_NULL));
|
||||
pthread_attr_t threadAttr;
|
||||
sched_param schedParam;
|
||||
pthread_attr_init(&threadAttr);
|
||||
pthread_attr_setdetachstate(&threadAttr, PTHREAD_CREATE_JOINABLE);
|
||||
pthread_attr_setstacksize(&threadAttr, nStackSize);
|
||||
|
||||
const int err = pthread_create(
|
||||
pThreadHandle,
|
||||
&threadAttr,
|
||||
threadDesc.fpEntryFunc,
|
||||
threadDesc.pArgList);
|
||||
|
||||
// Handle error on thread creation
|
||||
switch (err)
|
||||
{
|
||||
case 0:
|
||||
// No error
|
||||
break;
|
||||
case EAGAIN:
|
||||
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "<ThreadInfo> Unable to create thread \"%s\". Error Msg: \"Insufficient resources to create another thread, or a system-imposed limit on the number of threads was encountered.\"", threadDesc.szThreadName);
|
||||
return false;
|
||||
case EINVAL:
|
||||
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "<ThreadInfo> Unable to create thread \"%s\". Error Msg: \"Invalid attribute setting for thread creation.\"", threadDesc.szThreadName);
|
||||
return false;
|
||||
case EPERM:
|
||||
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "<ThreadInfo> Unable to create thread \"%s\". Error Msg: \"No permission to set the scheduling policy and parameters specified in attribute setting\"", threadDesc.szThreadName);
|
||||
return false;
|
||||
default:
|
||||
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "<ThreadInfo> Unable to create thread \"%s\". Unknown error message. Error code %i", threadDesc.szThreadName, err);
|
||||
break;
|
||||
}
|
||||
|
||||
// Print info to log
|
||||
CryComment("<ThreadInfo>: New thread \"%s\" | StackSize: %u(KB)", threadDesc.szThreadName, threadDesc.nStackSizeInBytes / 1024);
|
||||
return true;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void CryThreadExitCall()
|
||||
{
|
||||
// Notes on: pthread_exit
|
||||
// A thread that was create with pthread_create implicitly calls pthread_exit when the thread returns from its start routine (the function that was first called after a thread was created).
|
||||
// pthread_exit(NULL);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// FLOATING POINT EXCEPTIONS
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
namespace CryThreadUtil
|
||||
{
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
void EnableFloatExceptions(EFPE_Severity eFPESeverity)
|
||||
{
|
||||
// TODO:
|
||||
// Not implemented
|
||||
// for potential implementation see http://linux.die.net/man/3/feenableexcept
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void EnableFloatExceptions(threadID nThreadId, EFPE_Severity eFPESeverity)
|
||||
{
|
||||
// TODO:
|
||||
// Not implemented
|
||||
// for potential implementation see http://linux.die.net/man/3/feenableexcept
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
uint GetFloatingPointExceptionMask()
|
||||
{
|
||||
// Not implemented
|
||||
return ~0;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void SetFloatingPointExceptionMask(uint nMask)
|
||||
{
|
||||
// Not implemented
|
||||
}
|
||||
}
|
||||
@@ -1,432 +0,0 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
// Original file Copyright Crytek GMBH or its affiliates, used under license.
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// NOTE: INTERNAL HEADER NOT FOR PUBLIC USE
|
||||
// This header should only be include by SystemThreading.cpp only
|
||||
// It provides an interface for WinApi intrinsics
|
||||
// It's only client should be CThreadManager which should manage all thread interaction
|
||||
|
||||
#pragma once
|
||||
|
||||
#if defined(AZ_RESTRICTED_PLATFORM)
|
||||
#undef AZ_RESTRICTED_SECTION
|
||||
#define CRYTHREADUTIL_WIN32_THREAD_H_SECTION_1 1
|
||||
#define CRYTHREADUTIL_WIN32_THREAD_H_SECTION_2 2
|
||||
#endif
|
||||
|
||||
#if !defined(INCLUDED_FROM_SYSTEM_THREADING_CPP)
|
||||
# error "CRYTEK INTERNAL HEADER. ONLY INCLUDE FROM SYSTEMTHRADING.CPP."
|
||||
#endif
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#define DEFAULT_THREAD_STACK_SIZE_KB 0
|
||||
|
||||
// Returns the last Win32 error, in string format. Returns an empty string if there is no error.
|
||||
static string GetLastErrorAsString()
|
||||
{
|
||||
// Get the error message, if any.
|
||||
DWORD errorMessageID = GetLastError();
|
||||
if (errorMessageID == 0)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
#if defined(AZ_RESTRICTED_PLATFORM)
|
||||
#define AZ_RESTRICTED_SECTION CRYTHREADUTIL_WIN32_THREAD_H_SECTION_1
|
||||
#include AZ_RESTRICTED_FILE(CryThreadUtil_win32_thread_h)
|
||||
#endif
|
||||
#if defined(AZ_RESTRICTED_SECTION_IMPLEMENTED)
|
||||
#undef AZ_RESTRICTED_SECTION_IMPLEMENTED
|
||||
#else
|
||||
LPSTR messageBuffer = nullptr;
|
||||
size_t size = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, errorMessageID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), messageBuffer, 0, NULL);
|
||||
|
||||
string message(messageBuffer, size);
|
||||
|
||||
// Free the buffer.
|
||||
LocalFree(messageBuffer);
|
||||
|
||||
return message;
|
||||
#endif
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// THREAD CREATION AND MANAGMENT
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
namespace CryThreadUtil
|
||||
{
|
||||
// Define type for platform specific thread handle
|
||||
typedef THREAD_HANDLE TThreadHandle;
|
||||
|
||||
struct SThreadCreationDesc
|
||||
{
|
||||
// Define platform specific thread entry function functor type
|
||||
typedef unsigned int(_stdcall * EntryFunc)(void*);
|
||||
|
||||
const char* szThreadName;
|
||||
EntryFunc fpEntryFunc;
|
||||
void* pArgList;
|
||||
uint32 nStackSizeInBytes;
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
TThreadHandle CryGetCurrentThreadHandle()
|
||||
{
|
||||
return GetCurrentThread(); // most likely returns pseudo handle (0xfffffffe)
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Note: Handle must be closed lated via CryCloseThreadHandle()
|
||||
TThreadHandle CryDuplicateThreadHandle(const TThreadHandle& hThreadHandle)
|
||||
{
|
||||
// NOTES:
|
||||
// GetCurrentThread() may return a psydo handle to the current thread
|
||||
// to avoid going into the slower kernel mode.
|
||||
// Hence the handle is useless when being used from an other thread.
|
||||
// - GetCurrentThread() -> 0xfffffffe
|
||||
// - GetCurrentProcess() -> 0xffffffff
|
||||
|
||||
HANDLE hRealHandle = 0;
|
||||
DuplicateHandle(GetCurrentProcess(), // Source Process Handle.
|
||||
hThreadHandle, // Source Handle to dup.
|
||||
GetCurrentProcess(), // Target Process Handle.
|
||||
&hRealHandle, // Target Handle pointer.
|
||||
0, // Options flag.
|
||||
TRUE, // Inheritable flag
|
||||
DUPLICATE_SAME_ACCESS); // Options
|
||||
|
||||
return (TThreadHandle)hRealHandle;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void CryCloseThreadHandle(TThreadHandle& hThreadHandle)
|
||||
{
|
||||
if (hThreadHandle)
|
||||
{
|
||||
CloseHandle(hThreadHandle);
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
threadID CryGetCurrentThreadId()
|
||||
{
|
||||
return GetCurrentThreadId();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
threadID CryGetThreadId(TThreadHandle hThreadHandle)
|
||||
{
|
||||
return GetThreadId(hThreadHandle);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void CrySetThreadName(TThreadHandle pThreadHandle, const char* sThreadName)
|
||||
{
|
||||
const DWORD MS_VC_EXCEPTION = 0x406D1388;
|
||||
|
||||
struct SThreadNameDesc
|
||||
{
|
||||
DWORD dwType; // Must be 0x1000.
|
||||
LPCSTR szName; // Pointer to name (in user addr space).
|
||||
DWORD dwThreadID; // Thread ID (-1=caller thread).
|
||||
DWORD dwFlags; // Reserved for future use, must be zero.
|
||||
};
|
||||
|
||||
SThreadNameDesc info;
|
||||
info.dwType = 0x1000;
|
||||
info.szName = sThreadName;
|
||||
info.dwThreadID = GetThreadId(pThreadHandle);
|
||||
info.dwFlags = 0;
|
||||
AZ_PUSH_DISABLE_WARNING(6312 6322, "-Wunknown-warning-option")
|
||||
// warning C6312: Possible infinite loop: use of the constant EXCEPTION_CONTINUE_EXECUTION in the exception-filter expression of a try-except
|
||||
// warning C6322: empty _except block
|
||||
__try
|
||||
{
|
||||
// Raise exception to set thread name for attached debugger
|
||||
RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(DWORD), (ULONG_PTR*)&info);
|
||||
}
|
||||
__except (GetExceptionCode() == MS_VC_EXCEPTION ? EXCEPTION_CONTINUE_EXECUTION : EXCEPTION_EXECUTE_HANDLER)
|
||||
{
|
||||
}
|
||||
AZ_POP_DISABLE_WARNING
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void CrySetThreadAffinityMask(TThreadHandle pThreadHandle, DWORD dwAffinityMask)
|
||||
{
|
||||
SetThreadAffinityMask(pThreadHandle, dwAffinityMask);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void CrySetThreadPriority(TThreadHandle pThreadHandle, DWORD dwPriority)
|
||||
{
|
||||
if (!SetThreadPriority(pThreadHandle, dwPriority))
|
||||
{
|
||||
string errMsg = GetLastErrorAsString();
|
||||
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "<ThreadInfo> Unable to set thread priority. System Error Msg: \"%s\"", errMsg.c_str());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void CrySetThreadPriorityBoost(TThreadHandle pThreadHandle, bool bEnabled)
|
||||
{
|
||||
SetThreadPriorityBoost(pThreadHandle, !bEnabled);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool CryCreateThread(TThreadHandle* pThreadHandle, const SThreadCreationDesc& threadDesc)
|
||||
{
|
||||
const uint32 nStackSize = threadDesc.nStackSizeInBytes != 0 ? threadDesc.nStackSizeInBytes : DEFAULT_THREAD_STACK_SIZE_KB * 1024;
|
||||
|
||||
// Create thread
|
||||
unsigned int threadId = 0;
|
||||
#if defined(AZ_RESTRICTED_PLATFORM)
|
||||
#define AZ_RESTRICTED_SECTION CRYTHREADUTIL_WIN32_THREAD_H_SECTION_2
|
||||
#include AZ_RESTRICTED_FILE(CryThreadUtil_win32_thread_h)
|
||||
#endif
|
||||
#if defined(AZ_RESTRICTED_SECTION_IMPLEMENTED)
|
||||
#undef AZ_RESTRICTED_SECTION_IMPLEMENTED
|
||||
#else
|
||||
*pThreadHandle = (void*)_beginthreadex(NULL, nStackSize, threadDesc.fpEntryFunc, threadDesc.pArgList, CREATE_SUSPENDED, &threadId);
|
||||
#endif
|
||||
|
||||
if (!(*pThreadHandle))
|
||||
{
|
||||
string errMsg = GetLastErrorAsString();
|
||||
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "<ThreadInfo> Unable to create thread \"%s\". System Error Msg: \"%s\"", threadDesc.szThreadName, errMsg.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Start thread
|
||||
ResumeThread(*pThreadHandle);
|
||||
|
||||
// Print info to log
|
||||
CryComment("<ThreadInfo>: New thread \"%s\" | StackSize: %u(KB)", threadDesc.szThreadName, threadDesc.nStackSizeInBytes / 1024);
|
||||
return true;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void CryThreadExitCall()
|
||||
{
|
||||
// Note on: ExitThread() (from MSDN)
|
||||
// ExitThread is the preferred method of exiting a thread in C code.
|
||||
// However, in C++ code, the thread is exited before any destructor can be called or any other automatic cleanup can be performed.
|
||||
// Therefore, in C++ code, you should return from your thread function.
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// FLOATING POINT EXCEPTIONS
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
namespace CryThreadUtil
|
||||
{
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
void EnableFloatExceptions([[maybe_unused]] EFPE_Severity eFPESeverity)
|
||||
{
|
||||
AZ_PUSH_DISABLE_WARNING(4996, "-Wunknown-warning-option")
|
||||
|
||||
// Optimization
|
||||
// Enable DAZ/FZ
|
||||
// Denormals Are Zeros
|
||||
// Flush-to-Zero
|
||||
_controlfp(_DN_FLUSH, _MCW_DN);
|
||||
_mm_setcsr(_mm_getcsr() | _MM_FLUSH_ZERO_ON);
|
||||
|
||||
#ifndef _RELEASE
|
||||
if (eFPESeverity == eFPE_None)
|
||||
{
|
||||
// mask all floating exceptions off.
|
||||
_controlfp(_MCW_EM, _MCW_EM);
|
||||
_mm_setcsr(_mm_getcsr() | _MM_MASK_MASK);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Clear pending exceptions
|
||||
_fpreset();
|
||||
|
||||
if (eFPESeverity == eFPE_Basic)
|
||||
{
|
||||
// Enable:
|
||||
// - _EM_ZERODIVIDE
|
||||
// - _EM_INVALID
|
||||
//
|
||||
// Disable:
|
||||
// - _EM_DENORMAL
|
||||
// - _EM_OVERFLOW
|
||||
// - _EM_UNDERFLOW
|
||||
// - _EM_INEXACT
|
||||
|
||||
_controlfp(_EM_INEXACT | _EM_DENORMAL | _EM_UNDERFLOW | _EM_OVERFLOW, _MCW_EM);
|
||||
_mm_setcsr((_mm_getcsr() & ~_MM_MASK_MASK) | (_MM_MASK_DENORM | _MM_MASK_INEXACT | _MM_MASK_UNDERFLOW | _MM_MASK_OVERFLOW));
|
||||
|
||||
//_mm_setcsr(_mm_getcsr() & ~0x280);
|
||||
}
|
||||
|
||||
if (eFPESeverity == eFPE_All)
|
||||
{
|
||||
// Enable:
|
||||
// - _EM_ZERODIVIDE
|
||||
// - _EM_INVALID
|
||||
// - _EM_UNDERFLOW
|
||||
// - _EM_OVERFLOW
|
||||
//
|
||||
// Disable:
|
||||
// - _EM_INEXACT
|
||||
// - _EM_DENORMAL
|
||||
|
||||
_controlfp(_EM_INEXACT | _EM_DENORMAL, _MCW_EM);
|
||||
_mm_setcsr((_mm_getcsr() & ~_MM_MASK_MASK) | (_MM_MASK_INEXACT | _MM_MASK_DENORM));
|
||||
}
|
||||
}
|
||||
#endif // _RELEASE
|
||||
|
||||
AZ_POP_DISABLE_WARNING
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void EnableFloatExceptions(threadID nThreadId, EFPE_Severity eFPESeverity)
|
||||
{
|
||||
if (eFPESeverity >= eFPE_LastEntry)
|
||||
{
|
||||
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_ERROR, "Floating Point Exception (FPE) severity is out of range. (%i)", eFPESeverity);
|
||||
}
|
||||
|
||||
// Check if the thread ID matches the current thread
|
||||
if (nThreadId == 0 || nThreadId == CryGetCurrentThreadId())
|
||||
{
|
||||
EnableFloatExceptions(eFPESeverity);
|
||||
return;
|
||||
}
|
||||
|
||||
HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, true, nThreadId);
|
||||
|
||||
if (hThread == 0)
|
||||
{
|
||||
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_ERROR, "Unable to open thread. %p", hThread);
|
||||
return;
|
||||
}
|
||||
|
||||
SuspendThread(hThread);
|
||||
|
||||
CONTEXT ctx;
|
||||
memset(&ctx, 0, sizeof(ctx));
|
||||
ctx.ContextFlags = CONTEXT_ALL;
|
||||
if (GetThreadContext(hThread, &ctx) == 0)
|
||||
{
|
||||
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_ERROR, "Unable to get thread context");
|
||||
ResumeThread(hThread);
|
||||
CloseHandle(hThread);
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef PLATFORM_64BIT
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Note:
|
||||
// DO NOT USE ctx.FltSave.MxCsr ... SetThreadContext() will copy the value of ctx.MxCsr into it
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
DWORD& floatMxCsr = ctx.MxCsr; // Hold FPE Mask and Status for MMX (SSE) floating point registers
|
||||
WORD& floatControlWord = ctx.FltSave.ControlWord; // Hold FPE Mask for floating point registers
|
||||
#ifndef _RELEASE
|
||||
WORD& floatStatuslWord = ctx.FltSave.StatusWord; // Holds FPE Status for floating point registers
|
||||
#endif
|
||||
#else
|
||||
DWORD& floatMxCsr = *(DWORD*)(&ctx.ExtendedRegisters[24]); // Hold FPE Mask and Status for MMX (SSE) floating point registers
|
||||
DWORD& floatControlWord = ctx.FloatSave.ControlWord; // Hold FPE Mask for floating point registers
|
||||
DWORD& floatStatuslWord = ctx.FloatSave.StatusWord; // Holds FPE Status for floating point registers
|
||||
#endif
|
||||
|
||||
// Flush-To-Zero Mode
|
||||
// Two conditions must be met for FTZ processing to occur:
|
||||
// - The FTZ bit (bit 15) in the MXCSR register must be masked (value = 1).
|
||||
// - The underflow exception (bit 11) needs to be masked (value = 1).
|
||||
|
||||
// Set flush mode to zero mode
|
||||
floatControlWord = (floatControlWord & ~_MCW_DN) | _DN_FLUSH;
|
||||
floatMxCsr = (floatMxCsr & ~_MM_FLUSH_ZERO_MASK) | (_MM_FLUSH_ZERO_ON);
|
||||
|
||||
#ifndef _RELEASE
|
||||
|
||||
// Reset FPE bits
|
||||
floatControlWord = floatControlWord | _MCW_EM;
|
||||
floatMxCsr = floatMxCsr | _MM_MASK_MASK;
|
||||
|
||||
// Clear pending exceptions
|
||||
floatStatuslWord = floatStatuslWord & ~(_SW_INEXACT | _SW_UNDERFLOW | _SW_OVERFLOW | _SW_ZERODIVIDE | _SW_INVALID | _SW_DENORMAL);
|
||||
floatMxCsr = floatMxCsr & ~(_MM_EXCEPT_INEXACT | _MM_EXCEPT_UNDERFLOW | _MM_EXCEPT_OVERFLOW | _MM_EXCEPT_DIV_ZERO | _MM_EXCEPT_INVALID | _MM_EXCEPT_DENORM);
|
||||
|
||||
if (eFPESeverity == eFPE_Basic)
|
||||
{
|
||||
// Enable:
|
||||
// - _EM_ZERODIVIDE
|
||||
// - _EM_INVALID
|
||||
//
|
||||
// Disable:
|
||||
// - _EM_DENORMAL
|
||||
// - _EM_OVERFLOW
|
||||
// - _EM_UNDERFLOW
|
||||
// - _EM_INEXACT
|
||||
|
||||
floatControlWord = (floatControlWord & ~_MCW_EM) | (_EM_DENORMAL | _EM_INEXACT | EM_UNDERFLOW | _EM_OVERFLOW);
|
||||
floatMxCsr = (floatMxCsr & ~_MM_MASK_MASK) | (_MM_MASK_DENORM | _MM_MASK_INEXACT | _MM_MASK_UNDERFLOW | _MM_MASK_OVERFLOW);
|
||||
}
|
||||
|
||||
if (eFPESeverity == eFPE_All)
|
||||
{
|
||||
// Enable:
|
||||
// - _EM_ZERODIVIDE
|
||||
// - _EM_INVALID
|
||||
// - _EM_UNDERFLOW
|
||||
// - _EM_OVERFLOW
|
||||
//
|
||||
// Disable:
|
||||
// - _EM_INEXACT
|
||||
// - _EM_DENORMAL
|
||||
|
||||
floatControlWord = (floatControlWord & ~_MCW_EM) | (_EM_INEXACT | _EM_DENORMAL);
|
||||
floatMxCsr = (floatMxCsr & ~_MM_MASK_MASK) | (_MM_MASK_INEXACT | _MM_MASK_DENORM);
|
||||
}
|
||||
#endif
|
||||
|
||||
ctx.ContextFlags = CONTEXT_ALL;
|
||||
if (SetThreadContext(hThread, &ctx) == 0)
|
||||
{
|
||||
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_ERROR, "Error setting ThreadContext for ThreadID: %u", nThreadId);
|
||||
ResumeThread(hThread);
|
||||
CloseHandle(hThread);
|
||||
return;
|
||||
}
|
||||
|
||||
ResumeThread(hThread);
|
||||
CloseHandle(hThread);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
uint GetFloatingPointExceptionMask()
|
||||
{
|
||||
uint nMask = 0;
|
||||
_clearfp();
|
||||
_controlfp_s(&nMask, 0, 0);
|
||||
return nMask;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void SetFloatingPointExceptionMask(uint nMask)
|
||||
{
|
||||
uint temp = 0;
|
||||
_clearfp();
|
||||
const unsigned int kAllowedBits = _MCW_DN | _MCW_EM | _MCW_RC;
|
||||
_controlfp_s(&temp, nMask, kAllowedBits);
|
||||
}
|
||||
}
|
||||
@@ -1,926 +0,0 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
// Original file Copyright Crytek GMBH or its affiliates, used under license.
|
||||
|
||||
#include "CrySystem_precompiled.h"
|
||||
#include "DebugCallStack.h"
|
||||
|
||||
#if defined(WIN32) || defined(WIN64)
|
||||
|
||||
#include <IConsole.h>
|
||||
#include <CryPath.h>
|
||||
#include <Pak/CryPakUtils.h>
|
||||
#include "System.h"
|
||||
|
||||
#include <AzCore/Debug/StackTracer.h>
|
||||
#include <AzCore/Debug/EventTraceDrillerBus.h>
|
||||
|
||||
#include "resource.h"
|
||||
__pragma(comment(lib, "version.lib"))
|
||||
|
||||
//! Needs one external of DLL handle.
|
||||
extern HMODULE gDLLHandle;
|
||||
|
||||
#include <DbgHelp.h>
|
||||
|
||||
#define MAX_PATH_LENGTH 1024
|
||||
#define MAX_SYMBOL_LENGTH 512
|
||||
|
||||
static HWND hwndException = 0;
|
||||
static bool g_bUserDialog = true; // true=on crash show dialog box, false=supress user interaction
|
||||
|
||||
static int PrintException(EXCEPTION_POINTERS* pex);
|
||||
|
||||
static bool IsFloatingPointException(EXCEPTION_POINTERS* pex);
|
||||
|
||||
extern LONG WINAPI CryEngineExceptionFilterWER(struct _EXCEPTION_POINTERS* pExceptionPointers);
|
||||
extern LONG WINAPI CryEngineExceptionFilterMiniDump(struct _EXCEPTION_POINTERS* pExceptionPointers, const char* szDumpPath, MINIDUMP_TYPE mdumpValue);
|
||||
|
||||
//=============================================================================
|
||||
CONTEXT CaptureCurrentContext()
|
||||
{
|
||||
CONTEXT context;
|
||||
memset(&context, 0, sizeof(context));
|
||||
context.ContextFlags = CONTEXT_FULL;
|
||||
RtlCaptureContext(&context);
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
LONG __stdcall CryUnhandledExceptionHandler(EXCEPTION_POINTERS* pex)
|
||||
{
|
||||
return DebugCallStack::instance()->handleException(pex);
|
||||
}
|
||||
|
||||
|
||||
BOOL CALLBACK EnumModules(
|
||||
PCSTR ModuleName,
|
||||
DWORD64 BaseOfDll,
|
||||
PVOID UserContext)
|
||||
{
|
||||
DebugCallStack::TModules& modules = *static_cast<DebugCallStack::TModules*>(UserContext);
|
||||
modules[(void*)BaseOfDll] = ModuleName;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
//=============================================================================
|
||||
// Class Statics
|
||||
//=============================================================================
|
||||
|
||||
// Return single instance of class.
|
||||
IDebugCallStack* IDebugCallStack::instance()
|
||||
{
|
||||
static DebugCallStack sInstance;
|
||||
return &sInstance;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------------------------------------------
|
||||
// Sets up the symbols for functions in the debug file.
|
||||
//------------------------------------------------------------------------------------------------------------------------
|
||||
DebugCallStack::DebugCallStack()
|
||||
: prevExceptionHandler(0)
|
||||
, m_pSystem(0)
|
||||
, m_nSkipNumFunctions(0)
|
||||
, m_bCrash(false)
|
||||
, m_szBugMessage(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
DebugCallStack::~DebugCallStack()
|
||||
{
|
||||
}
|
||||
|
||||
void DebugCallStack::RemoveOldFiles()
|
||||
{
|
||||
RemoveFile("error.log");
|
||||
RemoveFile("error.bmp");
|
||||
RemoveFile("error.dmp");
|
||||
}
|
||||
|
||||
void DebugCallStack::RemoveFile(const char* szFileName)
|
||||
{
|
||||
FILE* pFile = nullptr;
|
||||
azfopen(&pFile, szFileName, "r");
|
||||
const bool bFileExists = (pFile != NULL);
|
||||
|
||||
if (bFileExists)
|
||||
{
|
||||
fclose(pFile);
|
||||
|
||||
WriteLineToLog("Removing file \"%s\"...", szFileName);
|
||||
if (remove(szFileName) == 0)
|
||||
{
|
||||
WriteLineToLog("File successfully removed.");
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteLineToLog("Couldn't remove file!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DebugCallStack::installErrorHandler(ISystem* pSystem)
|
||||
{
|
||||
m_pSystem = pSystem;
|
||||
prevExceptionHandler = (void*)SetUnhandledExceptionFilter(CryUnhandledExceptionHandler);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void DebugCallStack::SetUserDialogEnable(const bool bUserDialogEnable)
|
||||
{
|
||||
g_bUserDialog = bUserDialogEnable;
|
||||
}
|
||||
|
||||
|
||||
DWORD g_idDebugThreads[10];
|
||||
const char* g_nameDebugThreads[10];
|
||||
int g_nDebugThreads = 0;
|
||||
volatile int g_lockThreadDumpList = 0;
|
||||
|
||||
void MarkThisThreadForDebugging(const char* name)
|
||||
{
|
||||
EBUS_EVENT(AZ::Debug::EventTraceDrillerSetupBus, SetThreadName, AZStd::this_thread::get_id(), name);
|
||||
|
||||
WriteLock lock(g_lockThreadDumpList);
|
||||
DWORD id = GetCurrentThreadId();
|
||||
if (g_nDebugThreads == sizeof(g_idDebugThreads) / sizeof(g_idDebugThreads[0]))
|
||||
{
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < g_nDebugThreads; i++)
|
||||
{
|
||||
if (g_idDebugThreads[i] == id)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
g_nameDebugThreads[g_nDebugThreads] = name;
|
||||
g_idDebugThreads[g_nDebugThreads++] = id;
|
||||
((CSystem*)gEnv->pSystem)->EnableFloatExceptions(g_cvars.sys_float_exceptions);
|
||||
}
|
||||
|
||||
void UnmarkThisThreadFromDebugging()
|
||||
{
|
||||
WriteLock lock(g_lockThreadDumpList);
|
||||
DWORD id = GetCurrentThreadId();
|
||||
for (int i = g_nDebugThreads - 1; i >= 0; i--)
|
||||
{
|
||||
if (g_idDebugThreads[i] == id)
|
||||
{
|
||||
memmove(g_idDebugThreads + i, g_idDebugThreads + i + 1, (g_nDebugThreads - 1 - i) * sizeof(g_idDebugThreads[0]));
|
||||
memmove(g_nameDebugThreads + i, g_nameDebugThreads + i + 1, (g_nDebugThreads - 1 - i) * sizeof(g_nameDebugThreads[0]));
|
||||
--g_nDebugThreads;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern int prev_sys_float_exceptions;
|
||||
void UpdateFPExceptionsMaskForThreads()
|
||||
{
|
||||
int mask = -iszero(g_cvars.sys_float_exceptions);
|
||||
CONTEXT ctx;
|
||||
for (int i = 0; i < g_nDebugThreads; i++)
|
||||
{
|
||||
if (g_idDebugThreads[i] != GetCurrentThreadId())
|
||||
{
|
||||
HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, TRUE, g_idDebugThreads[i]);
|
||||
ctx.ContextFlags = CONTEXT_ALL;
|
||||
SuspendThread(hThread);
|
||||
GetThreadContext(hThread, &ctx);
|
||||
#ifndef WIN64
|
||||
(ctx.FloatSave.ControlWord |= 7) &= ~5 | mask;
|
||||
(*(WORD*)(ctx.ExtendedRegisters + 24) |= 0x280) &= ~0x280 | mask;
|
||||
#else
|
||||
(ctx.FltSave.ControlWord |= 7) &= ~5 | mask;
|
||||
(ctx.FltSave.MxCsr |= 0x280) &= ~0x280 | mask;
|
||||
#endif
|
||||
SetThreadContext(hThread, &ctx);
|
||||
ResumeThread(hThread);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
int DebugCallStack::handleException(EXCEPTION_POINTERS* exception_pointer)
|
||||
{
|
||||
if (gEnv == NULL)
|
||||
{
|
||||
return EXCEPTION_EXECUTE_HANDLER;
|
||||
}
|
||||
|
||||
ResetFPU(exception_pointer);
|
||||
|
||||
prev_sys_float_exceptions = 0;
|
||||
const int cached_sys_float_exceptions = g_cvars.sys_float_exceptions;
|
||||
|
||||
((CSystem*)gEnv->pSystem)->EnableFloatExceptions(0);
|
||||
|
||||
if (g_cvars.sys_WER)
|
||||
{
|
||||
gEnv->pLog->FlushAndClose();
|
||||
return CryEngineExceptionFilterWER(exception_pointer);
|
||||
}
|
||||
|
||||
if (g_cvars.sys_no_crash_dialog)
|
||||
{
|
||||
DWORD dwMode = SetErrorMode(SEM_NOGPFAULTERRORBOX);
|
||||
SetErrorMode(dwMode | SEM_NOGPFAULTERRORBOX);
|
||||
}
|
||||
|
||||
m_bCrash = true;
|
||||
|
||||
if (g_cvars.sys_no_crash_dialog)
|
||||
{
|
||||
DWORD dwMode = SetErrorMode(SEM_NOGPFAULTERRORBOX);
|
||||
SetErrorMode(dwMode | SEM_NOGPFAULTERRORBOX);
|
||||
}
|
||||
|
||||
static bool firstTime = true;
|
||||
|
||||
if (g_cvars.sys_dump_aux_threads)
|
||||
{
|
||||
for (int i = 0; i < g_nDebugThreads; i++)
|
||||
{
|
||||
if (g_idDebugThreads[i] != GetCurrentThreadId())
|
||||
{
|
||||
SuspendThread(OpenThread(THREAD_ALL_ACCESS, TRUE, g_idDebugThreads[i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// uninstall our exception handler.
|
||||
SetUnhandledExceptionFilter((LPTOP_LEVEL_EXCEPTION_FILTER)prevExceptionHandler);
|
||||
|
||||
if (!firstTime)
|
||||
{
|
||||
WriteLineToLog("Critical Exception! Called Multiple Times!");
|
||||
gEnv->pLog->FlushAndClose();
|
||||
// Exception called more then once.
|
||||
return EXCEPTION_EXECUTE_HANDLER;
|
||||
}
|
||||
|
||||
// Print exception info:
|
||||
{
|
||||
char excCode[80];
|
||||
char excAddr[80];
|
||||
WriteLineToLog("<CRITICAL EXCEPTION>");
|
||||
sprintf_s(excAddr, "0x%04X:0x%p", exception_pointer->ContextRecord->SegCs, exception_pointer->ExceptionRecord->ExceptionAddress);
|
||||
sprintf_s(excCode, "0x%08X", exception_pointer->ExceptionRecord->ExceptionCode);
|
||||
WriteLineToLog("Exception: %s, at Address: %s", excCode, excAddr);
|
||||
|
||||
{
|
||||
IMemoryManager::SProcessMemInfo memInfo;
|
||||
if (gEnv->pSystem->GetIMemoryManager()->GetProcessMemInfo(memInfo))
|
||||
{
|
||||
uint32 nMemUsage = (uint32)(memInfo.PagefileUsage / (1024 * 1024));
|
||||
WriteLineToLog("Virtual memory usage: %dMb", nMemUsage);
|
||||
}
|
||||
gEnv->szDebugStatus[SSystemGlobalEnvironment::MAX_DEBUG_STRING_LENGTH - 1] = '\0';
|
||||
WriteLineToLog("Debug Status: %s", gEnv->szDebugStatus);
|
||||
}
|
||||
}
|
||||
|
||||
firstTime = false;
|
||||
|
||||
const int ret = SubmitBug(exception_pointer);
|
||||
|
||||
if (ret != IDB_IGNORE)
|
||||
{
|
||||
CryEngineExceptionFilterWER(exception_pointer);
|
||||
}
|
||||
|
||||
gEnv->pLog->FlushAndClose();
|
||||
|
||||
if (exception_pointer->ExceptionRecord->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
|
||||
{
|
||||
// This is non continuable exception. abort application now.
|
||||
exit(exception_pointer->ExceptionRecord->ExceptionCode);
|
||||
}
|
||||
|
||||
//typedef long (__stdcall *ExceptionFunc)(EXCEPTION_POINTERS*);
|
||||
//ExceptionFunc prevFunc = (ExceptionFunc)prevExceptionHandler;
|
||||
//return prevFunc( (EXCEPTION_POINTERS*)exception_pointer );
|
||||
if (ret == IDB_EXIT)
|
||||
{
|
||||
// Immediate exit.
|
||||
// on windows, exit() and _exit() do all sorts of things, unfortuantely
|
||||
// TerminateProcess is the only way to die.
|
||||
TerminateProcess(GetCurrentProcess(), exception_pointer->ExceptionRecord->ExceptionCode); // we crashed, so don't return a zero exit code!
|
||||
// on linux based systems, _exit will not call ATEXIT and other things, which makes it more suitable for termination in an emergency such
|
||||
// as an unhandled exception.
|
||||
// however, this function is a windows exception handler.
|
||||
}
|
||||
else if (ret == IDB_IGNORE)
|
||||
{
|
||||
#ifndef WIN64
|
||||
exception_pointer->ContextRecord->FloatSave.StatusWord &= ~31;
|
||||
exception_pointer->ContextRecord->FloatSave.ControlWord |= 7;
|
||||
(*(WORD*)(exception_pointer->ContextRecord->ExtendedRegisters + 24) &= 31) |= 0x1F80;
|
||||
#else
|
||||
exception_pointer->ContextRecord->FltSave.StatusWord &= ~31;
|
||||
exception_pointer->ContextRecord->FltSave.ControlWord |= 7;
|
||||
(exception_pointer->ContextRecord->FltSave.MxCsr &= 31) |= 0x1F80;
|
||||
#endif
|
||||
firstTime = true;
|
||||
prevExceptionHandler = (void*)SetUnhandledExceptionFilter(CryUnhandledExceptionHandler);
|
||||
g_cvars.sys_float_exceptions = cached_sys_float_exceptions;
|
||||
((CSystem*)gEnv->pSystem)->EnableFloatExceptions(g_cvars.sys_float_exceptions);
|
||||
return EXCEPTION_CONTINUE_EXECUTION;
|
||||
}
|
||||
|
||||
// Continue;
|
||||
return EXCEPTION_EXECUTE_HANDLER;
|
||||
}
|
||||
|
||||
void DebugCallStack::ReportBug(const char* szErrorMessage)
|
||||
{
|
||||
WriteLineToLog("Reporting bug: %s", szErrorMessage);
|
||||
|
||||
m_szBugMessage = szErrorMessage;
|
||||
m_context = CaptureCurrentContext();
|
||||
SubmitBug(NULL);
|
||||
m_szBugMessage = NULL;
|
||||
}
|
||||
|
||||
void DebugCallStack::dumpCallStack(std::vector<string>& funcs)
|
||||
{
|
||||
WriteLineToLog("=============================================================================");
|
||||
int len = (int)funcs.size();
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
const char* str = funcs[i].c_str();
|
||||
WriteLineToLog("%2d) %s", len - i, str);
|
||||
}
|
||||
WriteLineToLog("=============================================================================");
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void DebugCallStack::LogExceptionInfo(EXCEPTION_POINTERS* pex)
|
||||
{
|
||||
string path("");
|
||||
if ((gEnv) && (gEnv->pFileIO))
|
||||
{
|
||||
const char* logAlias = gEnv->pFileIO->GetAlias("@log@");
|
||||
if (!logAlias)
|
||||
{
|
||||
logAlias = gEnv->pFileIO->GetAlias("@root@");
|
||||
}
|
||||
if (logAlias)
|
||||
{
|
||||
path = logAlias;
|
||||
path += "/";
|
||||
}
|
||||
}
|
||||
|
||||
string fileName = path;
|
||||
fileName += "error.log";
|
||||
|
||||
struct stat fileInfo;
|
||||
string timeStamp;
|
||||
string backupPath;
|
||||
if (gEnv->IsDedicated())
|
||||
{
|
||||
backupPath = PathUtil::ToUnixPath(PathUtil::AddSlash(path + "DumpBackups"));
|
||||
gEnv->pFileIO->CreatePath(backupPath.c_str());
|
||||
|
||||
if (stat(fileName.c_str(), &fileInfo) == 0)
|
||||
{
|
||||
// Backup log
|
||||
tm creationTime;
|
||||
localtime_s(&creationTime, &fileInfo.st_mtime);
|
||||
char tempBuffer[32];
|
||||
strftime(tempBuffer, sizeof(tempBuffer), "%d %b %Y (%H %M %S)", &creationTime);
|
||||
timeStamp = tempBuffer;
|
||||
|
||||
string backupFileName = backupPath + timeStamp + " error.log";
|
||||
CopyFile(fileName.c_str(), backupFileName.c_str(), true);
|
||||
}
|
||||
}
|
||||
|
||||
FILE* f = nullptr;
|
||||
azfopen(&f, fileName.c_str(), "wt");
|
||||
|
||||
CDebugAllowFileAccess ignoreInvalidFileAccess;
|
||||
|
||||
static char errorString[s_iCallStackSize];
|
||||
errorString[0] = 0;
|
||||
|
||||
// Time and Version.
|
||||
char versionbuf[1024];
|
||||
azstrcpy(versionbuf, AZ_ARRAY_SIZE(versionbuf), "");
|
||||
PutVersion(versionbuf, AZ_ARRAY_SIZE(versionbuf));
|
||||
cry_strcat(errorString, versionbuf);
|
||||
cry_strcat(errorString, "\n");
|
||||
|
||||
char excCode[MAX_WARNING_LENGTH];
|
||||
char excAddr[80];
|
||||
char desc[1024];
|
||||
char excDesc[MAX_WARNING_LENGTH];
|
||||
|
||||
// make sure the mouse cursor is visible
|
||||
ShowCursor(TRUE);
|
||||
|
||||
const char* excName;
|
||||
if (m_bIsFatalError || !pex)
|
||||
{
|
||||
const char* const szMessage = m_bIsFatalError ? s_szFatalErrorCode : m_szBugMessage;
|
||||
excName = szMessage;
|
||||
cry_strcpy(excCode, szMessage);
|
||||
cry_strcpy(excAddr, "");
|
||||
cry_strcpy(desc, "");
|
||||
cry_strcpy(m_excModule, "");
|
||||
cry_strcpy(excDesc, szMessage);
|
||||
}
|
||||
else
|
||||
{
|
||||
sprintf_s(excAddr, "0x%04X:0x%p", pex->ContextRecord->SegCs, pex->ExceptionRecord->ExceptionAddress);
|
||||
sprintf_s(excCode, "0x%08X", pex->ExceptionRecord->ExceptionCode);
|
||||
excName = TranslateExceptionCode(pex->ExceptionRecord->ExceptionCode);
|
||||
cry_strcpy(desc, "");
|
||||
sprintf_s(excDesc, "%s\r\n%s", excName, desc);
|
||||
|
||||
|
||||
if (pex->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION)
|
||||
{
|
||||
if (pex->ExceptionRecord->NumberParameters > 1)
|
||||
{
|
||||
ULONG_PTR iswrite = pex->ExceptionRecord->ExceptionInformation[0];
|
||||
DWORD64 accessAddr = pex->ExceptionRecord->ExceptionInformation[1];
|
||||
if (iswrite)
|
||||
{
|
||||
sprintf_s(desc, "Attempt to write data to address 0x%08llu\r\nThe memory could not be \"written\"", accessAddr);
|
||||
}
|
||||
else
|
||||
{
|
||||
sprintf_s(desc, "Attempt to read from address 0x%08llu\r\nThe memory could not be \"read\"", accessAddr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
WriteLineToLog("Exception Code: %s", excCode);
|
||||
WriteLineToLog("Exception Addr: %s", excAddr);
|
||||
WriteLineToLog("Exception Module: %s", m_excModule);
|
||||
WriteLineToLog("Exception Name : %s", excName);
|
||||
WriteLineToLog("Exception Description: %s", desc);
|
||||
|
||||
|
||||
cry_strcpy(m_excDesc, excDesc);
|
||||
cry_strcpy(m_excAddr, excAddr);
|
||||
cry_strcpy(m_excCode, excCode);
|
||||
|
||||
|
||||
char errs[32768];
|
||||
sprintf_s(errs, "Exception Code: %s\nException Addr: %s\nException Module: %s\nException Description: %s, %s\n",
|
||||
excCode, excAddr, m_excModule, excName, desc);
|
||||
|
||||
|
||||
IMemoryManager::SProcessMemInfo memInfo;
|
||||
if (gEnv->pSystem->GetIMemoryManager()->GetProcessMemInfo(memInfo))
|
||||
{
|
||||
char memoryString[256];
|
||||
double MB = 1024 * 1024;
|
||||
sprintf_s(memoryString, "Memory in use: %3.1fMB\n", (double)(memInfo.PagefileUsage) / MB);
|
||||
cry_strcat(errs, memoryString);
|
||||
}
|
||||
{
|
||||
const int tempStringSize = 256;
|
||||
char tempString[tempStringSize];
|
||||
|
||||
gEnv->szDebugStatus[SSystemGlobalEnvironment::MAX_DEBUG_STRING_LENGTH - 1] = '\0';
|
||||
sprintf_s(tempString, tempStringSize, "Debug Status: %s\n", gEnv->szDebugStatus);
|
||||
cry_strcat(errs, tempString);
|
||||
|
||||
sprintf_s(tempString, tempStringSize, "Out of Memory: %d\n", gEnv->bIsOutOfMemory);
|
||||
cry_strcat(errs, tempString);
|
||||
}
|
||||
cry_strcat(errs, "\nCall Stack Trace:\n");
|
||||
|
||||
std::vector<string> funcs;
|
||||
if (gEnv->bIsOutOfMemory)
|
||||
{
|
||||
cry_strcat(errs, "1) OUT_OF_MEMORY()\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ::Debug::StackFrame frames[25];
|
||||
AZ::Debug::SymbolStorage::StackLine lines[AZ_ARRAY_SIZE(frames)];
|
||||
unsigned int numFrames = AZ::Debug::StackRecorder::Record(frames, AZ_ARRAY_SIZE(frames), 3);
|
||||
if (numFrames)
|
||||
{
|
||||
AZ::Debug::SymbolStorage::DecodeFrames(frames, numFrames, lines);
|
||||
for (unsigned int i = 0; i < numFrames; i++)
|
||||
{
|
||||
funcs.push_back(lines[i]);
|
||||
}
|
||||
}
|
||||
dumpCallStack(funcs);
|
||||
// Fill call stack.
|
||||
char str[s_iCallStackSize];
|
||||
cry_strcpy(str, "");
|
||||
for (unsigned int i = 0; i < funcs.size(); i++)
|
||||
{
|
||||
char temp[s_iCallStackSize];
|
||||
sprintf_s(temp, "%2zd) %s", funcs.size() - i, (const char*)funcs[i].c_str());
|
||||
cry_strcat(str, temp);
|
||||
cry_strcat(str, "\r\n");
|
||||
cry_strcat(errs, temp);
|
||||
cry_strcat(errs, "\n");
|
||||
}
|
||||
cry_strcpy(m_excCallstack, str);
|
||||
}
|
||||
|
||||
cry_strcat(errorString, errs);
|
||||
|
||||
if (f)
|
||||
{
|
||||
fwrite(errorString, strlen(errorString), 1, f);
|
||||
if (!gEnv->bIsOutOfMemory)
|
||||
{
|
||||
if (g_cvars.sys_dump_aux_threads)
|
||||
{
|
||||
for (int i = 0; i < g_nDebugThreads; i++)
|
||||
{
|
||||
if (g_idDebugThreads[i] != GetCurrentThreadId())
|
||||
{
|
||||
fprintf(f, "\n\nSuspended thread (%s):\n", g_nameDebugThreads[i]);
|
||||
HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, TRUE, g_idDebugThreads[i]);
|
||||
|
||||
// mirrors the AZ::Debug::Trace::PrintCallstack() functionality, but prints to a file
|
||||
{
|
||||
AZ::Debug::StackFrame frames[10];
|
||||
|
||||
// Without StackFrame explicit alignment frames array is aligned to 4 bytes
|
||||
// which causes the stack tracing to fail.
|
||||
AZ::Debug::SymbolStorage::StackLine lines[AZ_ARRAY_SIZE(frames)];
|
||||
|
||||
unsigned int numFrames = AZ::Debug::StackRecorder::Record(frames, AZ_ARRAY_SIZE(frames), 0, hThread);
|
||||
if (numFrames)
|
||||
{
|
||||
AZ::Debug::SymbolStorage::DecodeFrames(frames, numFrames, lines);
|
||||
for (unsigned int i2 = 0; i2 < numFrames; ++i2)
|
||||
{
|
||||
fprintf(f, "%2d) %s\n", numFrames - i2, lines[i2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ResumeThread(hThread);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
fflush(f);
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
if (pex)
|
||||
{
|
||||
MINIDUMP_TYPE mdumpValue;
|
||||
bool bDump = true;
|
||||
switch (g_cvars.sys_dump_type)
|
||||
{
|
||||
case 0:
|
||||
bDump = false;
|
||||
break;
|
||||
case 1:
|
||||
mdumpValue = MiniDumpNormal;
|
||||
break;
|
||||
case 2:
|
||||
mdumpValue = (MINIDUMP_TYPE)(MiniDumpWithIndirectlyReferencedMemory | MiniDumpWithDataSegs);
|
||||
break;
|
||||
case 3:
|
||||
mdumpValue = MiniDumpWithFullMemory;
|
||||
break;
|
||||
default:
|
||||
mdumpValue = (MINIDUMP_TYPE)g_cvars.sys_dump_type;
|
||||
break;
|
||||
}
|
||||
if (bDump)
|
||||
{
|
||||
fileName = path + "error.dmp";
|
||||
|
||||
if (gEnv->IsDedicated() && stat(fileName.c_str(), &fileInfo) == 0)
|
||||
{
|
||||
// Backup dump (use timestamp from error.log if available)
|
||||
if (timeStamp.empty())
|
||||
{
|
||||
tm creationTime;
|
||||
localtime_s(&creationTime, &fileInfo.st_mtime);
|
||||
char tempBuffer[32];
|
||||
strftime(tempBuffer, sizeof(tempBuffer), "%d %b %Y (%H %M %S)", &creationTime);
|
||||
timeStamp = tempBuffer;
|
||||
}
|
||||
|
||||
string backupFileName = backupPath + timeStamp + " error.dmp";
|
||||
CopyFile(fileName.c_str(), backupFileName.c_str(), true);
|
||||
}
|
||||
|
||||
CryEngineExceptionFilterMiniDump(pex, fileName.c_str(), mdumpValue);
|
||||
}
|
||||
}
|
||||
|
||||
//if no crash dialog don't even submit the bug
|
||||
if (m_postBackupProcess && g_cvars.sys_no_crash_dialog == 0 && g_bUserDialog)
|
||||
{
|
||||
m_postBackupProcess();
|
||||
}
|
||||
else
|
||||
{
|
||||
// lawsonn: Disabling the JIRA-based crash reporter for now
|
||||
// we'll need to deal with it our own way, pending QA.
|
||||
// if you're customizing the engine this is also your opportunity to deal with it.
|
||||
if (g_cvars.sys_no_crash_dialog != 0 || !g_bUserDialog)
|
||||
{
|
||||
// ------------ place custom crash handler here ---------------------
|
||||
// it should launch an executable!
|
||||
/// by this time, error.bmp will be in the engine root folder
|
||||
// error.log and error.dmp will also be present in the engine root folder
|
||||
// if your error dumper wants those, it should zip them up and send them or offer to do so.
|
||||
// ------------------------------------------------------------------
|
||||
}
|
||||
}
|
||||
const bool bQuitting = !gEnv || !gEnv->pSystem || gEnv->pSystem->IsQuitting();
|
||||
|
||||
//[AlexMcC|16.04.10] When the engine is shutting down, MessageBox doesn't display a box
|
||||
// and immediately returns IDYES. Avoid this by just not trying to save if we're quitting.
|
||||
// Don't ask to save if this isn't a real crash (a real crash has exception pointers)
|
||||
if (g_cvars.sys_no_crash_dialog == 0 && g_bUserDialog && gEnv->IsEditor() && !bQuitting && pex)
|
||||
{
|
||||
BackupCurrentLevel();
|
||||
|
||||
const INT_PTR res = DialogBoxParam(gDLLHandle, MAKEINTRESOURCE(IDD_CONFIRM_SAVE_LEVEL), NULL, DebugCallStack::ConfirmSaveDialogProc, NULL);
|
||||
if (res == IDB_CONFIRM_SAVE)
|
||||
{
|
||||
if (SaveCurrentLevel())
|
||||
{
|
||||
MessageBox(NULL, "Level has been successfully saved!\r\nPress Ok to terminate Editor.", "Save", MB_OK);
|
||||
}
|
||||
else
|
||||
{
|
||||
MessageBox(NULL, "Error saving level.\r\nPress Ok to terminate Editor.", "Save", MB_OK | MB_ICONWARNING);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (g_cvars.sys_no_crash_dialog != 0 || !g_bUserDialog)
|
||||
{
|
||||
// terminate immediately - since we're in a crash, there is no point unwinding stack, we've already done access violation or worse.
|
||||
// calling exit will only cause further death down the line...
|
||||
TerminateProcess(GetCurrentProcess(), pex->ExceptionRecord->ExceptionCode);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
INT_PTR CALLBACK DebugCallStack::ExceptionDialogProc(HWND hwndDlg, UINT message, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
static EXCEPTION_POINTERS* pex;
|
||||
|
||||
static char errorString[32768] = "";
|
||||
|
||||
switch (message)
|
||||
{
|
||||
case WM_INITDIALOG:
|
||||
{
|
||||
pex = (EXCEPTION_POINTERS*)lParam;
|
||||
HWND h;
|
||||
|
||||
if (pex->ExceptionRecord->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
|
||||
{
|
||||
// Disable continue button for non continuable exceptions.
|
||||
//h = GetDlgItem( hwndDlg,IDB_CONTINUE );
|
||||
//if (h) EnableWindow( h,FALSE );
|
||||
}
|
||||
|
||||
DebugCallStack* pDCS = static_cast<DebugCallStack*>(DebugCallStack::instance());
|
||||
|
||||
h = GetDlgItem(hwndDlg, IDC_EXCEPTION_DESC);
|
||||
if (h)
|
||||
{
|
||||
SendMessage(h, EM_REPLACESEL, FALSE, (LONG_PTR)pDCS->m_excDesc);
|
||||
}
|
||||
|
||||
h = GetDlgItem(hwndDlg, IDC_EXCEPTION_CODE);
|
||||
if (h)
|
||||
{
|
||||
SendMessage(h, EM_REPLACESEL, FALSE, (LONG_PTR)pDCS->m_excCode);
|
||||
}
|
||||
|
||||
h = GetDlgItem(hwndDlg, IDC_EXCEPTION_MODULE);
|
||||
if (h)
|
||||
{
|
||||
SendMessage(h, EM_REPLACESEL, FALSE, (LONG_PTR)pDCS->m_excModule);
|
||||
}
|
||||
|
||||
h = GetDlgItem(hwndDlg, IDC_EXCEPTION_ADDRESS);
|
||||
if (h)
|
||||
{
|
||||
SendMessage(h, EM_REPLACESEL, FALSE, (LONG_PTR)pDCS->m_excAddr);
|
||||
}
|
||||
|
||||
// Fill call stack.
|
||||
HWND callStack = GetDlgItem(hwndDlg, IDC_CALLSTACK);
|
||||
if (callStack)
|
||||
{
|
||||
SendMessage(callStack, WM_SETTEXT, FALSE, (LPARAM)pDCS->m_excCallstack);
|
||||
}
|
||||
|
||||
if (hwndException)
|
||||
{
|
||||
DestroyWindow(hwndException);
|
||||
hwndException = 0;
|
||||
}
|
||||
|
||||
if (IsFloatingPointException(pex))
|
||||
{
|
||||
EnableWindow(GetDlgItem(hwndDlg, IDB_IGNORE), TRUE);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case WM_COMMAND:
|
||||
switch (LOWORD(wParam))
|
||||
{
|
||||
case IDB_EXIT:
|
||||
case IDB_IGNORE:
|
||||
// Fall through.
|
||||
|
||||
EndDialog(hwndDlg, wParam);
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
INT_PTR CALLBACK DebugCallStack::ConfirmSaveDialogProc(HWND hwndDlg, UINT message, WPARAM wParam, [[maybe_unused]] LPARAM lParam)
|
||||
{
|
||||
switch (message)
|
||||
{
|
||||
case WM_INITDIALOG:
|
||||
{
|
||||
// The user might be holding down the spacebar while the engine crashes.
|
||||
// If we don't remove keyboard focus from this dialog, the keypress will
|
||||
// press the default button before the dialog actually appears, even if
|
||||
// the user has already released the key, which is bad.
|
||||
SetFocus(NULL);
|
||||
} break;
|
||||
case WM_COMMAND:
|
||||
{
|
||||
switch (LOWORD(wParam))
|
||||
{
|
||||
case IDB_CONFIRM_SAVE: // Fall through
|
||||
case IDB_DONT_SAVE:
|
||||
{
|
||||
EndDialog(hwndDlg, wParam);
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
} break;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
bool DebugCallStack::BackupCurrentLevel()
|
||||
{
|
||||
CSystem* pSystem = static_cast<CSystem*>(m_pSystem);
|
||||
if (pSystem && pSystem->GetUserCallback())
|
||||
{
|
||||
return pSystem->GetUserCallback()->OnBackupDocument();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DebugCallStack::SaveCurrentLevel()
|
||||
{
|
||||
CSystem* pSystem = static_cast<CSystem*>(m_pSystem);
|
||||
if (pSystem && pSystem->GetUserCallback())
|
||||
{
|
||||
return pSystem->GetUserCallback()->OnSaveDocument();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int DebugCallStack::SubmitBug(EXCEPTION_POINTERS* exception_pointer)
|
||||
{
|
||||
int ret = IDB_EXIT;
|
||||
|
||||
assert(!hwndException);
|
||||
|
||||
RemoveOldFiles();
|
||||
|
||||
AZ::Debug::Trace::PrintCallstack("", 2);
|
||||
|
||||
LogExceptionInfo(exception_pointer);
|
||||
|
||||
if (IsFloatingPointException(exception_pointer))
|
||||
{
|
||||
//! Print exception dialog.
|
||||
ret = PrintException(exception_pointer);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void DebugCallStack::ResetFPU(EXCEPTION_POINTERS* pex)
|
||||
{
|
||||
if (IsFloatingPointException(pex))
|
||||
{
|
||||
// How to reset FPU: http://www.experts-exchange.com/Programming/System/Windows__Programming/Q_10310953.html
|
||||
_clearfp();
|
||||
#ifndef WIN64
|
||||
pex->ContextRecord->FloatSave.ControlWord |= 0x2F;
|
||||
pex->ContextRecord->FloatSave.StatusWord &= ~0x8080;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
string DebugCallStack::GetModuleNameForAddr(void* addr)
|
||||
{
|
||||
if (m_modules.empty())
|
||||
{
|
||||
return "[unknown]";
|
||||
}
|
||||
|
||||
if (addr < m_modules.begin()->first)
|
||||
{
|
||||
return "[unknown]";
|
||||
}
|
||||
|
||||
TModules::const_iterator it = m_modules.begin();
|
||||
TModules::const_iterator end = m_modules.end();
|
||||
for (; ++it != end; )
|
||||
{
|
||||
if (addr < it->first)
|
||||
{
|
||||
return (--it)->second;
|
||||
}
|
||||
}
|
||||
|
||||
//if address is higher than the last module, we simply assume it is in the last module.
|
||||
return m_modules.rbegin()->second;
|
||||
}
|
||||
|
||||
void DebugCallStack::GetProcNameForAddr(void* addr, string& procName, void*& baseAddr, string& filename, int& line)
|
||||
{
|
||||
AZ::Debug::SymbolStorage::StackLine func, file, module;
|
||||
AZ::Debug::SymbolStorage::FindFunctionFromIP(addr, &func, &file, &module, line, baseAddr);
|
||||
procName = func;
|
||||
filename = file;
|
||||
}
|
||||
|
||||
string DebugCallStack::GetCurrentFilename()
|
||||
{
|
||||
char fullpath[MAX_PATH_LENGTH + 1];
|
||||
GetModuleFileName(NULL, fullpath, MAX_PATH_LENGTH);
|
||||
return fullpath;
|
||||
}
|
||||
|
||||
static bool IsFloatingPointException(EXCEPTION_POINTERS* pex)
|
||||
{
|
||||
if (!pex)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
DWORD exceptionCode = pex->ExceptionRecord->ExceptionCode;
|
||||
switch (exceptionCode)
|
||||
{
|
||||
case EXCEPTION_FLT_DENORMAL_OPERAND:
|
||||
case EXCEPTION_FLT_DIVIDE_BY_ZERO:
|
||||
case EXCEPTION_FLT_INEXACT_RESULT:
|
||||
case EXCEPTION_FLT_INVALID_OPERATION:
|
||||
case EXCEPTION_FLT_OVERFLOW:
|
||||
case EXCEPTION_FLT_UNDERFLOW:
|
||||
case STATUS_FLOAT_MULTIPLE_FAULTS:
|
||||
case STATUS_FLOAT_MULTIPLE_TRAPS:
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
int DebugCallStack::PrintException(EXCEPTION_POINTERS* exception_pointer)
|
||||
{
|
||||
return (int)DialogBoxParam(gDLLHandle, MAKEINTRESOURCE(IDD_CRITICAL_ERROR), NULL, DebugCallStack::ExceptionDialogProc, (LPARAM)exception_pointer);
|
||||
}
|
||||
|
||||
#else
|
||||
void MarkThisThreadForDebugging(const char*) {}
|
||||
void UnmarkThisThreadFromDebugging() {}
|
||||
void UpdateFPExceptionsMaskForThreads() {}
|
||||
#endif //WIN32
|
||||
@@ -1,95 +0,0 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
// Original file Copyright Crytek GMBH or its affiliates, used under license.
|
||||
|
||||
#ifndef CRYINCLUDE_CRYSYSTEM_DEBUGCALLSTACK_H
|
||||
#define CRYINCLUDE_CRYSYSTEM_DEBUGCALLSTACK_H
|
||||
#pragma once
|
||||
|
||||
|
||||
#include "IDebugCallStack.h"
|
||||
|
||||
#if defined (WIN32) || defined (WIN64)
|
||||
|
||||
//! Limits the maximal number of functions in call stack.
|
||||
const int MAX_DEBUG_STACK_ENTRIES_FILE_DUMP = 12;
|
||||
|
||||
struct ISystem;
|
||||
|
||||
//!============================================================================
|
||||
//!
|
||||
//! DebugCallStack class, capture call stack information from symbol files.
|
||||
//!
|
||||
//!============================================================================
|
||||
class DebugCallStack
|
||||
: public IDebugCallStack
|
||||
{
|
||||
public:
|
||||
DebugCallStack();
|
||||
virtual ~DebugCallStack();
|
||||
|
||||
ISystem* GetSystem() { return m_pSystem; };
|
||||
|
||||
virtual string GetModuleNameForAddr(void* addr);
|
||||
virtual void GetProcNameForAddr(void* addr, string& procName, void*& baseAddr, string& filename, int& line);
|
||||
virtual string GetCurrentFilename();
|
||||
|
||||
void installErrorHandler(ISystem* pSystem);
|
||||
virtual int handleException(EXCEPTION_POINTERS* exception_pointer);
|
||||
|
||||
virtual void ReportBug(const char*);
|
||||
|
||||
void dumpCallStack(std::vector<string>& functions);
|
||||
|
||||
void SetUserDialogEnable(const bool bUserDialogEnable);
|
||||
|
||||
typedef std::map<void*, string> TModules;
|
||||
protected:
|
||||
static void RemoveOldFiles();
|
||||
static void RemoveFile(const char* szFileName);
|
||||
|
||||
static int PrintException(EXCEPTION_POINTERS* exception_pointer);
|
||||
static INT_PTR CALLBACK ExceptionDialogProc(HWND hwndDlg, UINT message, WPARAM wParam, LPARAM lParam);
|
||||
static INT_PTR CALLBACK ConfirmSaveDialogProc(HWND hwndDlg, UINT message, WPARAM wParam, LPARAM lParam);
|
||||
|
||||
void LogExceptionInfo(EXCEPTION_POINTERS* exception_pointer);
|
||||
bool BackupCurrentLevel();
|
||||
bool SaveCurrentLevel();
|
||||
int SubmitBug(EXCEPTION_POINTERS* exception_pointer);
|
||||
void ResetFPU(EXCEPTION_POINTERS* pex);
|
||||
|
||||
static const int s_iCallStackSize = 32768;
|
||||
|
||||
char m_excLine[256];
|
||||
char m_excModule[128];
|
||||
|
||||
char m_excDesc[MAX_WARNING_LENGTH];
|
||||
char m_excCode[MAX_WARNING_LENGTH];
|
||||
char m_excAddr[80];
|
||||
char m_excCallstack[s_iCallStackSize];
|
||||
|
||||
void* prevExceptionHandler;
|
||||
|
||||
bool m_bCrash;
|
||||
const char* m_szBugMessage;
|
||||
|
||||
ISystem* m_pSystem;
|
||||
|
||||
int m_nSkipNumFunctions;
|
||||
CONTEXT m_context;
|
||||
|
||||
TModules m_modules;
|
||||
};
|
||||
|
||||
#endif //WIN32
|
||||
|
||||
#endif // CRYINCLUDE_CRYSYSTEM_DEBUGCALLSTACK_H
|
||||
@@ -14,11 +14,6 @@
|
||||
#include "CrySystem_precompiled.h"
|
||||
#include "System.h"
|
||||
#include <AZCrySystemInitLogSink.h>
|
||||
#include "DebugCallStack.h"
|
||||
#if defined(AZ_MONOLITHIC_BUILD)
|
||||
#include <CryCommon/CryExtension/Impl/ICryFactoryRegistryImpl.h>
|
||||
#include <CryCommon/CryExtension/Impl/RegFactoryNode.h>
|
||||
#endif
|
||||
|
||||
#if defined(AZ_RESTRICTED_PLATFORM)
|
||||
#undef AZ_RESTRICTED_SECTION
|
||||
@@ -76,10 +71,6 @@ public:
|
||||
{
|
||||
switch (event)
|
||||
{
|
||||
case ESYSTEM_EVENT_LEVEL_UNLOAD:
|
||||
gEnv->pSystem->SetThreadState(ESubsys_Physics, false);
|
||||
break;
|
||||
|
||||
case ESYSTEM_EVENT_LEVEL_LOAD_START:
|
||||
case ESYSTEM_EVENT_LEVEL_LOAD_END:
|
||||
{
|
||||
@@ -90,7 +81,6 @@ public:
|
||||
case ESYSTEM_EVENT_LEVEL_POST_UNLOAD:
|
||||
{
|
||||
CryCleanup();
|
||||
gEnv->pSystem->SetThreadState(ESubsys_Physics, true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -131,31 +121,13 @@ CRYSYSTEM_API ISystem* CreateSystemInterface(const SSystemInitParams& startupPar
|
||||
#define AZ_RESTRICTED_SECTION DLLMAIN_CPP_SECTION_2
|
||||
#include AZ_RESTRICTED_FILE(DllMain_cpp)
|
||||
#endif
|
||||
#if defined(AZ_MONOLITHIC_BUILD)
|
||||
ICryFactoryRegistryImpl* pCryFactoryImpl = static_cast<ICryFactoryRegistryImpl*>(pSystem->GetCryFactoryRegistry());
|
||||
pCryFactoryImpl->RegisterFactories(g_pHeadToRegFactories);
|
||||
#endif // AZ_MONOLITHIC_BUILD
|
||||
|
||||
// the earliest point the system exists - w2e tell the callback
|
||||
if (startupParams.pUserCallback)
|
||||
{
|
||||
startupParams.pUserCallback->OnSystemConnect(pSystem);
|
||||
}
|
||||
|
||||
// Environment Variable to signal we don't want to override our exception handler - our crash report system will set this
|
||||
auto envVar = AZ::Environment::FindVariable<bool>("ExceptionHandlerIsSet");
|
||||
bool handlerIsSet = (envVar && *envVar);
|
||||
|
||||
if (!startupParams.bMinimal && !handlerIsSet) // in minimal mode, we want to crash when we crash!
|
||||
{
|
||||
#if defined(WIN32)
|
||||
// Install exception handler in Release modes.
|
||||
((DebugCallStack*)IDebugCallStack::instance())->installErrorHandler(pSystem);
|
||||
#elif defined(AZ_RESTRICTED_PLATFORM)
|
||||
#define AZ_RESTRICTED_SECTION DLLMAIN_CPP_SECTION_3
|
||||
#include AZ_RESTRICTED_FILE(DllMain_cpp)
|
||||
#endif
|
||||
}
|
||||
|
||||
bool retVal = false;
|
||||
{
|
||||
AZ::Debug::StartupLogSinkReporter<AZ::Debug::CrySystemInitLogSink> initLogSink;
|
||||
@@ -177,20 +149,5 @@ CRYSYSTEM_API ISystem* CreateSystemInterface(const SSystemInitParams& startupPar
|
||||
|
||||
return pSystem;
|
||||
}
|
||||
|
||||
CRYSYSTEM_API void WINAPI CryInstallUnhandledExceptionHandler()
|
||||
{
|
||||
#if defined(AZ_RESTRICTED_PLATFORM)
|
||||
#define AZ_RESTRICTED_SECTION DLLMAIN_CPP_SECTION_4
|
||||
#include AZ_RESTRICTED_FILE(DllMain_cpp)
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(ENABLE_PROFILING_CODE) && !defined(LINUX) && !defined(APPLE)
|
||||
CRYSYSTEM_API void CryInstallPostExceptionHandler(void (* PostExceptionHandlerCallback)())
|
||||
{
|
||||
return IDebugCallStack::instance()->FileCreationCallback(PostExceptionHandlerCallback);
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
@@ -1,359 +0,0 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
// Original file Copyright Crytek GMBH or its affiliates, used under license.
|
||||
|
||||
// Description : Part of CryEngine's extension framework.
|
||||
|
||||
|
||||
#include "CrySystem_precompiled.h"
|
||||
#include "CryFactoryRegistryImpl.h"
|
||||
#include "../System.h"
|
||||
|
||||
#include <CryExtension/ICryUnknown.h>
|
||||
#include <CryExtension/Impl/RegFactoryNode.h>
|
||||
#include <CryExtension/Impl/CryGUIDHelper.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
|
||||
CCryFactoryRegistryImpl::CCryFactoryRegistryImpl()
|
||||
: m_guard()
|
||||
, m_byCName()
|
||||
, m_byCID()
|
||||
, m_byIID()
|
||||
, m_callbacks()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
CCryFactoryRegistryImpl::~CCryFactoryRegistryImpl()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
CCryFactoryRegistryImpl& CCryFactoryRegistryImpl::Access()
|
||||
{
|
||||
static StaticInstance<CCryFactoryRegistryImpl, AZStd::no_destruct<CCryFactoryRegistryImpl>> s_registry;
|
||||
return s_registry;
|
||||
}
|
||||
|
||||
|
||||
ICryFactory* CCryFactoryRegistryImpl::GetFactory(const char* cname) const
|
||||
{
|
||||
AUTO_READLOCK(m_guard);
|
||||
|
||||
if (!cname)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
const FactoryByCName search(cname);
|
||||
FactoriesByCNameConstIt it = std::lower_bound(m_byCName.begin(), m_byCName.end(), search);
|
||||
return it != m_byCName.end() && !(search < *it) ? (*it).m_ptr : 0;
|
||||
}
|
||||
|
||||
|
||||
ICryFactory* CCryFactoryRegistryImpl::GetFactory(const CryClassID& cid) const
|
||||
{
|
||||
AUTO_READLOCK(m_guard);
|
||||
|
||||
const FactoryByCID search(cid);
|
||||
FactoriesByCIDConstIt it = std::lower_bound(m_byCID.begin(), m_byCID.end(), search);
|
||||
return it != m_byCID.end() && !(search < *it) ? (*it).m_ptr : 0;
|
||||
}
|
||||
|
||||
|
||||
void CCryFactoryRegistryImpl::IterateFactories(const CryInterfaceID& iid, ICryFactory** pFactories, size_t& numFactories) const
|
||||
{
|
||||
AUTO_READLOCK(m_guard);
|
||||
|
||||
typedef std::pair<FactoriesByIIDConstIt, FactoriesByIIDConstIt> SearchResult;
|
||||
SearchResult res = std::equal_range(m_byIID.begin(), m_byIID.end(), FactoryByIID(iid, 0), LessPredFactoryByIIDOnly());
|
||||
|
||||
const size_t numFactoriesFound = std::distance(res.first, res.second);
|
||||
if (pFactories)
|
||||
{
|
||||
numFactories = min(numFactories, numFactoriesFound);
|
||||
FactoriesByIIDConstIt it = res.first;
|
||||
for (size_t i = 0; i < numFactories; ++i, ++it)
|
||||
{
|
||||
pFactories[i] = (*it).m_ptr;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
numFactories = numFactoriesFound;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CCryFactoryRegistryImpl::RegisterCallback(ICryFactoryRegistryCallback* pCallback)
|
||||
{
|
||||
if (!pCallback)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
AUTO_MODIFYLOCK(m_guard);
|
||||
|
||||
Callbacks::iterator it = std::lower_bound(m_callbacks.begin(), m_callbacks.end(), pCallback);
|
||||
if (it == m_callbacks.end() || pCallback < *it)
|
||||
{
|
||||
m_callbacks.insert(it, pCallback);
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(0 && "CCryFactoryRegistryImpl::RegisterCallback() -- pCallback already registered!");
|
||||
}
|
||||
}
|
||||
{
|
||||
AUTO_READLOCK(m_guard);
|
||||
|
||||
typedef std::pair<FactoriesByIIDConstIt, FactoriesByIIDConstIt> SearchResult;
|
||||
SearchResult res = std::equal_range(m_byIID.begin(), m_byIID.end(), FactoryByIID(cryiidof<ICryUnknown>(), 0), LessPredFactoryByIIDOnly());
|
||||
|
||||
for (; res.first != res.second; ++res.first)
|
||||
{
|
||||
pCallback->OnNotifyFactoryRegistered((*res.first).m_ptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CCryFactoryRegistryImpl::UnregisterCallback(ICryFactoryRegistryCallback* pCallback)
|
||||
{
|
||||
if (!pCallback)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AUTO_MODIFYLOCK(m_guard);
|
||||
|
||||
Callbacks::iterator it = std::lower_bound(m_callbacks.begin(), m_callbacks.end(), pCallback);
|
||||
if (it != m_callbacks.end() && !(pCallback < *it))
|
||||
{
|
||||
m_callbacks.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool CCryFactoryRegistryImpl::GetInsertionPos(ICryFactory* pFactory, FactoriesByCNameIt& itPosForCName, FactoriesByCIDIt& itPosForCID)
|
||||
{
|
||||
assert(pFactory);
|
||||
|
||||
struct FatalError
|
||||
{
|
||||
static void Report(ICryFactory* pKnownFactory, ICryFactory* pNewFactory)
|
||||
{
|
||||
char err[1024];
|
||||
sprintf_s(err, sizeof(err), "Conflicting factories...\n"
|
||||
"Factory (0x%p): ClassID = %s, ClassName = \"%s\"\n"
|
||||
"Factory (0x%p): ClassID = %s, ClassName = \"%s\"",
|
||||
pKnownFactory, pKnownFactory ? CryGUIDHelper::Print(pKnownFactory->GetClassID()).c_str() : "$unknown$", pKnownFactory ? pKnownFactory->GetName() : "$unknown$",
|
||||
pNewFactory, pNewFactory ? CryGUIDHelper::Print(pNewFactory->GetClassID()).c_str() : "$unknown$", pNewFactory ? pNewFactory->GetName() : "$unknown$");
|
||||
|
||||
#if AZ_LEGACY_CRYSYSTEM_TRAIT_FACTORY_REGISTRY_USE_PRINTF_FOR_FATAL
|
||||
printf("\n!!! Fatal error !!!\n");
|
||||
printf(err);
|
||||
printf("\n");
|
||||
#elif defined(WIN32) || defined(WIN64)
|
||||
OutputDebugStringA("\n!!! Fatal error !!!\n");
|
||||
OutputDebugStringA(err);
|
||||
OutputDebugStringA("\n");
|
||||
MessageBoxA(0, err, "!!! Fatal error !!!", MB_OK | MB_ICONERROR);
|
||||
#endif
|
||||
|
||||
assert(0);
|
||||
exit(0);
|
||||
}
|
||||
};
|
||||
|
||||
FactoryByCName searchByCName(pFactory);
|
||||
FactoriesByCNameIt itForCName = std::lower_bound(m_byCName.begin(), m_byCName.end(), searchByCName);
|
||||
if (itForCName != m_byCName.end())
|
||||
{
|
||||
// If the addresses match, then this factory is already registered. It's not really worth error-ing about,
|
||||
// as double registration will not cause any harm.
|
||||
if (itForCName->m_ptr == pFactory)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(searchByCName < *itForCName))
|
||||
{
|
||||
FatalError::Report((*itForCName).m_ptr, pFactory);
|
||||
}
|
||||
}
|
||||
|
||||
FactoryByCID searchByCID(pFactory);
|
||||
FactoriesByCIDIt itForCID = std::lower_bound(m_byCID.begin(), m_byCID.end(), searchByCID);
|
||||
if (itForCID != m_byCID.end() && !(searchByCID < *itForCID))
|
||||
{
|
||||
FatalError::Report((*itForCID).m_ptr, pFactory);
|
||||
}
|
||||
|
||||
itPosForCName = itForCName;
|
||||
itPosForCID = itForCID;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void CCryFactoryRegistryImpl::RegisterFactories(const SRegFactoryNode* pFactories)
|
||||
{
|
||||
size_t numFactoriesToAdd = 0;
|
||||
size_t numInterfacesSupported = 0;
|
||||
{
|
||||
const SRegFactoryNode* p = pFactories;
|
||||
while (p)
|
||||
{
|
||||
ICryFactory* pFactory = p->m_pFactory;
|
||||
assert(pFactory);
|
||||
if (pFactory)
|
||||
{
|
||||
const CryInterfaceID* pIIDs = 0;
|
||||
size_t numIIDs = 0;
|
||||
pFactory->ClassSupports(pIIDs, numIIDs);
|
||||
|
||||
numInterfacesSupported += numIIDs;
|
||||
++numFactoriesToAdd;
|
||||
}
|
||||
|
||||
p = p->m_pNext;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
AUTO_MODIFYLOCK(m_guard);
|
||||
|
||||
m_byCName.reserve(m_byCName.size() + numFactoriesToAdd);
|
||||
m_byCID.reserve(m_byCID.size() + numFactoriesToAdd);
|
||||
m_byIID.reserve(m_byIID.size() + numInterfacesSupported);
|
||||
|
||||
size_t numFactoriesAdded = 0;
|
||||
const SRegFactoryNode* p = pFactories;
|
||||
while (p)
|
||||
{
|
||||
ICryFactory* pFactory = p->m_pFactory;
|
||||
if (pFactory)
|
||||
{
|
||||
FactoriesByCNameIt itPosForCName;
|
||||
FactoriesByCIDIt itPosForCID;
|
||||
if (GetInsertionPos(pFactory, itPosForCName, itPosForCID))
|
||||
{
|
||||
m_byCName.insert(itPosForCName, FactoryByCName(pFactory));
|
||||
m_byCID.insert(itPosForCID, FactoryByCID(pFactory));
|
||||
|
||||
const CryInterfaceID* pIIDs = 0;
|
||||
size_t numIIDs = 0;
|
||||
pFactory->ClassSupports(pIIDs, numIIDs);
|
||||
|
||||
for (size_t i = 0; i < numIIDs; ++i)
|
||||
{
|
||||
const FactoryByIID newFactory(pIIDs[i], pFactory);
|
||||
m_byIID.push_back(newFactory);
|
||||
}
|
||||
|
||||
for (size_t i = 0, s = m_callbacks.size(); i < s; ++i)
|
||||
{
|
||||
m_callbacks[i]->OnNotifyFactoryRegistered(pFactory);
|
||||
}
|
||||
|
||||
++numFactoriesAdded;
|
||||
}
|
||||
}
|
||||
|
||||
p = p->m_pNext;
|
||||
}
|
||||
|
||||
if (numFactoriesAdded)
|
||||
{
|
||||
std::sort(m_byIID.begin(), m_byIID.end());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CCryFactoryRegistryImpl::UnregisterFactories(const SRegFactoryNode* pFactories)
|
||||
{
|
||||
AUTO_MODIFYLOCK(m_guard);
|
||||
|
||||
const SRegFactoryNode* p = pFactories;
|
||||
while (p)
|
||||
{
|
||||
ICryFactory* pFactory = p->m_pFactory;
|
||||
UnregisterFactoryInternal(pFactory);
|
||||
p = p->m_pNext;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CCryFactoryRegistryImpl::UnregisterFactory(ICryFactory* const pFactory)
|
||||
{
|
||||
AUTO_MODIFYLOCK(m_guard);
|
||||
|
||||
UnregisterFactoryInternal(pFactory);
|
||||
}
|
||||
|
||||
|
||||
void CCryFactoryRegistryImpl::UnregisterFactoryInternal(ICryFactory* const pFactory)
|
||||
{
|
||||
if (pFactory)
|
||||
{
|
||||
FactoryByCName searchByCName(pFactory);
|
||||
FactoriesByCNameIt itForCName = std::lower_bound(m_byCName.begin(), m_byCName.end(), searchByCName);
|
||||
if (itForCName != m_byCName.end() && !(searchByCName < *itForCName))
|
||||
{
|
||||
assert((*itForCName).m_ptr == pFactory);
|
||||
if ((*itForCName).m_ptr == pFactory)
|
||||
{
|
||||
m_byCName.erase(itForCName);
|
||||
}
|
||||
}
|
||||
|
||||
FactoryByCID searchByCID(pFactory);
|
||||
FactoriesByCIDIt itForCID = std::lower_bound(m_byCID.begin(), m_byCID.end(), searchByCID);
|
||||
if (itForCID != m_byCID.end() && !(searchByCID < *itForCID))
|
||||
{
|
||||
assert((*itForCID).m_ptr == pFactory);
|
||||
if ((*itForCID).m_ptr == pFactory)
|
||||
{
|
||||
m_byCID.erase(itForCID);
|
||||
}
|
||||
}
|
||||
|
||||
const CryInterfaceID* pIIDs = 0;
|
||||
size_t numIIDs = 0;
|
||||
pFactory->ClassSupports(pIIDs, numIIDs);
|
||||
|
||||
for (size_t i = 0; i < numIIDs; ++i)
|
||||
{
|
||||
FactoryByIID searchByIID(pIIDs[i], pFactory);
|
||||
FactoriesByIIDIt itForIID = std::lower_bound(m_byIID.begin(), m_byIID.end(), searchByIID);
|
||||
if (itForIID != m_byIID.end() && !(searchByIID < *itForIID))
|
||||
{
|
||||
m_byIID.erase(itForIID);
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0, s = m_callbacks.size(); i < s; ++i)
|
||||
{
|
||||
m_callbacks[i]->OnNotifyFactoryUnregistered(pFactory);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ICryFactoryRegistry* CSystem::GetCryFactoryRegistry() const
|
||||
{
|
||||
return &CCryFactoryRegistryImpl::Access();
|
||||
}
|
||||
@@ -1,128 +0,0 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
// Original file Copyright Crytek GMBH or its affiliates, used under license.
|
||||
|
||||
// Description : Part of CryEngine's extension framework.
|
||||
|
||||
|
||||
#ifndef CRYINCLUDE_CRYSYSTEM_EXTENSIONSYSTEM_CRYFACTORYREGISTRYIMPL_H
|
||||
#define CRYINCLUDE_CRYSYSTEM_EXTENSIONSYSTEM_CRYFACTORYREGISTRYIMPL_H
|
||||
#pragma once
|
||||
|
||||
|
||||
|
||||
#include <CryExtension/Impl/ICryFactoryRegistryImpl.h>
|
||||
#include <CryExtension/ICryFactory.h>
|
||||
|
||||
#include <AzCore/std/containers/vector.h>
|
||||
|
||||
|
||||
class CCryFactoryRegistryImpl
|
||||
: public ICryFactoryRegistryImpl
|
||||
{
|
||||
public:
|
||||
virtual ICryFactory* GetFactory(const char* cname) const;
|
||||
virtual ICryFactory* GetFactory(const CryClassID& cid) const;
|
||||
virtual void IterateFactories(const CryInterfaceID& iid, ICryFactory** pFactories, size_t& numFactories) const;
|
||||
|
||||
virtual void RegisterCallback(ICryFactoryRegistryCallback* pCallback);
|
||||
virtual void UnregisterCallback(ICryFactoryRegistryCallback* pCallback);
|
||||
|
||||
virtual void RegisterFactories(const SRegFactoryNode* pFactories);
|
||||
virtual void UnregisterFactories(const SRegFactoryNode* pFactories);
|
||||
|
||||
virtual void UnregisterFactory(ICryFactory* const pFactory);
|
||||
|
||||
public:
|
||||
static CCryFactoryRegistryImpl& Access();
|
||||
CCryFactoryRegistryImpl();
|
||||
~CCryFactoryRegistryImpl();
|
||||
|
||||
private:
|
||||
struct FactoryByCName
|
||||
{
|
||||
const char* m_cname;
|
||||
ICryFactory* m_ptr;
|
||||
|
||||
FactoryByCName(const char* cname)
|
||||
: m_cname(cname)
|
||||
, m_ptr(0) {assert(m_cname); }
|
||||
FactoryByCName(ICryFactory* ptr)
|
||||
: m_cname(ptr ? ptr->GetName() : 0)
|
||||
, m_ptr(ptr) {assert(m_cname && m_ptr); }
|
||||
bool operator <(const FactoryByCName& rhs) const {return strcmp(m_cname, rhs.m_cname) < 0; }
|
||||
};
|
||||
typedef std::vector<FactoryByCName> FactoriesByCName;
|
||||
typedef FactoriesByCName::iterator FactoriesByCNameIt;
|
||||
typedef FactoriesByCName::const_iterator FactoriesByCNameConstIt;
|
||||
|
||||
struct FactoryByCID
|
||||
{
|
||||
CryClassID m_cid;
|
||||
ICryFactory* m_ptr;
|
||||
|
||||
FactoryByCID(const CryClassID& cid)
|
||||
: m_cid(cid)
|
||||
, m_ptr(0) {}
|
||||
FactoryByCID(ICryFactory* ptr)
|
||||
: m_cid(ptr ? ptr->GetClassID() : MAKE_CRYGUID(0, 0))
|
||||
, m_ptr(ptr) {assert(m_ptr); }
|
||||
bool operator <(const FactoryByCID& rhs) const {return m_cid < rhs.m_cid; }
|
||||
};
|
||||
typedef std::vector<FactoryByCID> FactoriesByCID;
|
||||
typedef FactoriesByCID::iterator FactoriesByCIDIt;
|
||||
typedef FactoriesByCID::const_iterator FactoriesByCIDConstIt;
|
||||
|
||||
struct FactoryByIID
|
||||
{
|
||||
CryInterfaceID m_iid;
|
||||
ICryFactory* m_ptr;
|
||||
|
||||
FactoryByIID(CryInterfaceID iid, ICryFactory* pFactory)
|
||||
: m_iid(iid)
|
||||
, m_ptr(pFactory) {}
|
||||
bool operator <(const FactoryByIID& rhs) const
|
||||
{
|
||||
if (m_iid != rhs.m_iid)
|
||||
{
|
||||
return m_iid < rhs.m_iid;
|
||||
}
|
||||
return m_ptr < rhs.m_ptr;
|
||||
}
|
||||
};
|
||||
typedef std::vector<FactoryByIID> FactoriesByIID;
|
||||
typedef FactoriesByIID::iterator FactoriesByIIDIt;
|
||||
typedef FactoriesByIID::const_iterator FactoriesByIIDConstIt;
|
||||
struct LessPredFactoryByIIDOnly
|
||||
{
|
||||
bool operator ()(const FactoryByIID& lhs, const FactoryByIID& rhs) const {return lhs.m_iid < rhs.m_iid; }
|
||||
};
|
||||
|
||||
typedef std::vector<ICryFactoryRegistryCallback*> Callbacks;
|
||||
typedef Callbacks::iterator CallbacksIt;
|
||||
typedef Callbacks::const_iterator CallbacksConstIt;
|
||||
|
||||
private:
|
||||
bool GetInsertionPos(ICryFactory* pFactory, FactoriesByCNameIt& itPosForCName, FactoriesByCIDIt& itPosForCID);
|
||||
void UnregisterFactoryInternal(ICryFactory* const pFactory);
|
||||
|
||||
private:
|
||||
mutable CryReadModifyLock m_guard;
|
||||
|
||||
FactoriesByCName m_byCName;
|
||||
FactoriesByCID m_byCID;
|
||||
FactoriesByIID m_byIID;
|
||||
|
||||
Callbacks m_callbacks;
|
||||
};
|
||||
|
||||
#endif // CRYINCLUDE_CRYSYSTEM_EXTENSIONSYSTEM_CRYFACTORYREGISTRYIMPL_H
|
||||
@@ -1,955 +0,0 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
// Original file Copyright Crytek GMBH or its affiliates, used under license.
|
||||
|
||||
// Description : Part of CryEngine's extension framework.
|
||||
|
||||
|
||||
#include "CrySystem_precompiled.h"
|
||||
#include "TestExtensions.h"
|
||||
|
||||
#ifdef EXTENSION_SYSTEM_INCLUDE_TESTCASES
|
||||
|
||||
#include <CryExtension/Impl/ClassWeaver.h>
|
||||
#include <CryExtension/Impl/ICryFactoryRegistryImpl.h>
|
||||
#include <CryExtension/CryCreateClassInstance.h>
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
namespace TestComposition
|
||||
{
|
||||
struct ITestExt1
|
||||
: public ICryUnknown
|
||||
{
|
||||
CRYINTERFACE_DECLARE(ITestExt1, 0x9d9e0dcfa5764cb0, 0xa73701595f75bd32)
|
||||
|
||||
virtual void Call1() const = 0;
|
||||
};
|
||||
|
||||
DECLARE_SMART_POINTERS(ITestExt1);
|
||||
|
||||
|
||||
class CTestExt1
|
||||
: public ITestExt1
|
||||
{
|
||||
CRYINTERFACE_BEGIN()
|
||||
CRYINTERFACE_ADD(ITestExt1)
|
||||
CRYINTERFACE_END()
|
||||
|
||||
CRYGENERATE_CLASS(CTestExt1, "TestExt1", 0x43b04e7cc1be45ca, 0x9df6ccb1c0dc1ad8)
|
||||
|
||||
public:
|
||||
virtual void Call1() const;
|
||||
|
||||
private:
|
||||
int i;
|
||||
};
|
||||
|
||||
CRYREGISTER_CLASS(CTestExt1)
|
||||
|
||||
CTestExt1::CTestExt1()
|
||||
{
|
||||
i = 1;
|
||||
}
|
||||
|
||||
CTestExt1::~CTestExt1()
|
||||
{
|
||||
printf("Inside CTestExt1 dtor\n");
|
||||
}
|
||||
|
||||
void CTestExt1::Call1() const
|
||||
{
|
||||
printf("Inside CTestExt1::Call1()\n");
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct ITestExt2
|
||||
: public ICryUnknown
|
||||
{
|
||||
CRYINTERFACE_DECLARE(ITestExt2, 0x8eb7a4b399874b9c, 0xb96bd6da7a8c72f9)
|
||||
|
||||
virtual void Call2() = 0;
|
||||
};
|
||||
|
||||
DECLARE_SMART_POINTERS(ITestExt2);
|
||||
|
||||
|
||||
class CTestExt2
|
||||
: public ITestExt2
|
||||
{
|
||||
CRYINTERFACE_BEGIN()
|
||||
CRYINTERFACE_ADD(ITestExt2)
|
||||
CRYINTERFACE_END()
|
||||
|
||||
CRYGENERATE_CLASS(CTestExt2, "TestExt2", 0x25b3ebf8f1754b9a, 0xb5494e3da7cdd80f)
|
||||
|
||||
public:
|
||||
virtual void Call2();
|
||||
|
||||
private:
|
||||
int i;
|
||||
};
|
||||
|
||||
CRYREGISTER_CLASS(CTestExt2)
|
||||
|
||||
CTestExt2::CTestExt2()
|
||||
{
|
||||
i = 2;
|
||||
}
|
||||
|
||||
CTestExt2::~CTestExt2()
|
||||
{
|
||||
printf("Inside CTestExt2 dtor\n");
|
||||
}
|
||||
|
||||
void CTestExt2::Call2()
|
||||
{
|
||||
printf("Inside CTestExt2::Call2()\n");
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class CComposed
|
||||
: public ICryUnknown
|
||||
{
|
||||
CRYGENERATE_CLASS(CComposed, "Composed", 0x0439d74b8dcd4b7f, 0x9287dcdf7e26a3a5)
|
||||
|
||||
CRYCOMPOSITE_BEGIN()
|
||||
CRYCOMPOSITE_ADD(m_pTestExt1, "Ext1")
|
||||
CRYCOMPOSITE_ADD(m_pTestExt2, "Ext2")
|
||||
CRYCOMPOSITE_END(CComposed)
|
||||
|
||||
CRYINTERFACE_BEGIN()
|
||||
CRYINTERFACE_END()
|
||||
|
||||
private:
|
||||
ITestExt1Ptr m_pTestExt1;
|
||||
ITestExt2Ptr m_pTestExt2;
|
||||
};
|
||||
|
||||
CRYREGISTER_CLASS(CComposed)
|
||||
|
||||
CComposed::CComposed()
|
||||
: m_pTestExt1()
|
||||
, m_pTestExt2()
|
||||
{
|
||||
CryCreateClassInstance("TestExt1", m_pTestExt1);
|
||||
CryCreateClassInstance("TestExt2", m_pTestExt2);
|
||||
}
|
||||
|
||||
CComposed::~CComposed()
|
||||
{
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct ITestExt3
|
||||
: public ICryUnknown
|
||||
{
|
||||
CRYINTERFACE_DECLARE(ITestExt3, 0xdd017935a2134898, 0xbd2fffa145551876)
|
||||
|
||||
virtual void Call3() = 0;
|
||||
};
|
||||
|
||||
DECLARE_SMART_POINTERS(ITestExt3);
|
||||
|
||||
class CTestExt3
|
||||
: public ITestExt3
|
||||
{
|
||||
CRYGENERATE_CLASS(CTestExt3, "TestExt3", 0xeceab40bc4bb4988, 0xa9f63c1db85a69b1)
|
||||
|
||||
CRYINTERFACE_BEGIN()
|
||||
CRYINTERFACE_ADD(ITestExt3)
|
||||
CRYINTERFACE_END()
|
||||
|
||||
public:
|
||||
virtual void Call3();
|
||||
|
||||
private:
|
||||
int i;
|
||||
};
|
||||
|
||||
CRYREGISTER_CLASS(CTestExt3)
|
||||
|
||||
CTestExt3::CTestExt3()
|
||||
{
|
||||
i = 3;
|
||||
}
|
||||
|
||||
CTestExt3::~CTestExt3()
|
||||
{
|
||||
printf("Inside CTestExt3 dtor\n");
|
||||
}
|
||||
|
||||
void CTestExt3::Call3()
|
||||
{
|
||||
printf("Inside CTestExt3::Call3()\n");
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class CComposed2
|
||||
: public ICryUnknown
|
||||
{
|
||||
CRYGENERATE_CLASS(CComposed2, "Composed2", 0x0439d74b8dcd4b7e, 0x9287dcdf7e26a3a6)
|
||||
|
||||
CRYCOMPOSITE_BEGIN()
|
||||
CRYCOMPOSITE_ADD(m_pTestExt3, "Ext3")
|
||||
CRYCOMPOSITE_END(CComposed2)
|
||||
|
||||
CRYINTERFACE_BEGIN()
|
||||
CRYINTERFACE_END()
|
||||
|
||||
private:
|
||||
ITestExt3Ptr m_pTestExt3;
|
||||
};
|
||||
|
||||
CRYREGISTER_CLASS(CComposed2)
|
||||
|
||||
CComposed2::CComposed2()
|
||||
: m_pTestExt3()
|
||||
{
|
||||
CryCreateClassInstance("TestExt3", m_pTestExt3);
|
||||
}
|
||||
|
||||
CComposed2::~CComposed2()
|
||||
{
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class CTestExt4
|
||||
: public ITestExt1
|
||||
, public ITestExt2
|
||||
, public ITestExt3
|
||||
{
|
||||
CRYINTERFACE_BEGIN()
|
||||
CRYINTERFACE_ADD(ITestExt1)
|
||||
CRYINTERFACE_ADD(ITestExt2)
|
||||
CRYINTERFACE_ADD(ITestExt3)
|
||||
CRYINTERFACE_END()
|
||||
|
||||
CRYGENERATE_CLASS(CTestExt4, "TestExt4", 0x43204e7cc1be45ca, 0x9df4ccb1c0dc1ad8)
|
||||
|
||||
public:
|
||||
virtual void Call1() const;
|
||||
virtual void Call2();
|
||||
virtual void Call3();
|
||||
|
||||
private:
|
||||
int i;
|
||||
};
|
||||
|
||||
CRYREGISTER_CLASS(CTestExt4)
|
||||
|
||||
CTestExt4::CTestExt4()
|
||||
{
|
||||
i = 4;
|
||||
}
|
||||
|
||||
CTestExt4::~CTestExt4()
|
||||
{
|
||||
printf("Inside CTestExt4 dtor\n");
|
||||
}
|
||||
|
||||
void CTestExt4::Call1() const
|
||||
{
|
||||
printf("Inside CTestExt4::Call1()\n");
|
||||
}
|
||||
|
||||
void CTestExt4::Call2()
|
||||
{
|
||||
printf("Inside CTestExt4::Call2()\n");
|
||||
}
|
||||
|
||||
void CTestExt4::Call3()
|
||||
{
|
||||
printf("Inside CTestExt4::Call3()\n");
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class CMegaComposed
|
||||
: public CComposed
|
||||
, public CComposed2
|
||||
{
|
||||
CRYGENERATE_CLASS(CMegaComposed, "MegaComposed", 0x512787559f84503, 0x421ac1af66f2fb6f)
|
||||
|
||||
CRYCOMPOSITE_BEGIN()
|
||||
CRYCOMPOSITE_ADD(m_pTestExt4, "Ext4")
|
||||
CRYCOMPOSITE_ENDWITHBASE2(CMegaComposed, CComposed, CComposed2)
|
||||
|
||||
CRYINTERFACE_BEGIN()
|
||||
CRYINTERFACE_END()
|
||||
|
||||
private:
|
||||
AZStd::shared_ptr<CTestExt4> m_pTestExt4;
|
||||
};
|
||||
|
||||
CRYREGISTER_CLASS(CMegaComposed)
|
||||
|
||||
CMegaComposed::CMegaComposed()
|
||||
: m_pTestExt4()
|
||||
{
|
||||
printf("Inside CMegaComposed ctor\n");
|
||||
m_pTestExt4 = CTestExt4::CreateClassInstance();
|
||||
}
|
||||
|
||||
CMegaComposed::~CMegaComposed()
|
||||
{
|
||||
printf("Inside CMegaComposed dtor\n");
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static void TestComposition()
|
||||
{
|
||||
printf("\nTest composition:\n");
|
||||
|
||||
ICryUnknownPtr p;
|
||||
if (CryCreateClassInstance("MegaComposed", p))
|
||||
{
|
||||
ITestExt1Ptr p1 = cryinterface_cast<ITestExt1>(crycomposite_query(p, "Ext1"));
|
||||
if (p1)
|
||||
{
|
||||
p1->Call1(); // calls CTestExt1::Call1()
|
||||
}
|
||||
ITestExt2Ptr p2 = cryinterface_cast<ITestExt2>(crycomposite_query(p, "Ext2"));
|
||||
if (p2)
|
||||
{
|
||||
p2->Call2(); // calls CTestExt2::Call2()
|
||||
}
|
||||
ITestExt3Ptr p3 = cryinterface_cast<ITestExt3>(crycomposite_query(p, "Ext3"));
|
||||
if (p3)
|
||||
{
|
||||
p3->Call3(); // calls CTestExt3::Call3()
|
||||
}
|
||||
p3 = cryinterface_cast<ITestExt3>(crycomposite_query(p, "Ext4"));
|
||||
if (p3)
|
||||
{
|
||||
p3->Call3(); // calls CTestExt4::Call3()
|
||||
}
|
||||
p1 = cryinterface_cast<ITestExt1>(crycomposite_query(p.get(), "Ext4"));
|
||||
p2 = cryinterface_cast<ITestExt2>(crycomposite_query(p.get(), "Ext4"));
|
||||
|
||||
bool b = CryIsSameClassInstance(p1, p2); // true
|
||||
}
|
||||
|
||||
{
|
||||
ICryUnknownConstPtr pCUnk = p;
|
||||
ICryUnknownConstPtr pComp1 = crycomposite_query(pCUnk.get(), "Ext1");
|
||||
//ICryUnknownPtr pComp1 = crycomposite_query(pCUnk, "Ext1"); // must fail to compile due to const rules
|
||||
|
||||
ITestExt1ConstPtr p1 = cryinterface_cast<const ITestExt1>(pComp1);
|
||||
if (p1)
|
||||
{
|
||||
p1->Call1();
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace TestComposition
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
namespace TestExtension
|
||||
{
|
||||
class CFoobar
|
||||
: public IFoobar
|
||||
{
|
||||
CRYINTERFACE_BEGIN()
|
||||
CRYINTERFACE_ADD(IFoobar)
|
||||
CRYINTERFACE_END()
|
||||
|
||||
CRYGENERATE_CLASS(CFoobar, "Foobar", 0x76c8dd6d16634531, 0x95d3b1cfabcf7ef4)
|
||||
|
||||
public:
|
||||
virtual void Foo();
|
||||
};
|
||||
|
||||
CRYREGISTER_CLASS(CFoobar)
|
||||
|
||||
CFoobar::CFoobar()
|
||||
{
|
||||
}
|
||||
|
||||
CFoobar::~CFoobar()
|
||||
{
|
||||
}
|
||||
|
||||
void CFoobar::Foo()
|
||||
{
|
||||
printf("Inside CFoobar::Foo()\n");
|
||||
}
|
||||
|
||||
static void TestFoobar()
|
||||
{
|
||||
AZStd::shared_ptr<CFoobar> p = CFoobar::CreateClassInstance();
|
||||
{
|
||||
CryInterfaceID iid = cryiidof<IFoobar>();
|
||||
CryClassID clsid = p->GetFactory()->GetClassID();
|
||||
int t = 0;
|
||||
}
|
||||
|
||||
{
|
||||
IAPtr sp_ = cryinterface_cast<IA>(p); // sp_ == NULL
|
||||
|
||||
ICryUnknownPtr sp1 = cryinterface_cast<ICryUnknown>(p);
|
||||
IFoobarPtr sp = cryinterface_cast<IFoobar>(sp1);
|
||||
sp->Foo();
|
||||
}
|
||||
|
||||
{
|
||||
CFoobar* pF = p.get();
|
||||
pF->Foo();
|
||||
ICryUnknown* p1 = cryinterface_cast<ICryUnknown>(pF);
|
||||
}
|
||||
|
||||
IFoobar* pFoo = cryinterface_cast<IFoobar>(p.get());
|
||||
ICryFactory* pF1 = pFoo->GetFactory();
|
||||
pFoo->Foo();
|
||||
|
||||
int t = 0;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class CRaboof
|
||||
: public IRaboof
|
||||
{
|
||||
CRYINTERFACE_BEGIN()
|
||||
CRYINTERFACE_ADD(IRaboof)
|
||||
CRYINTERFACE_END()
|
||||
|
||||
CRYGENERATE_SINGLETONCLASS(CRaboof, "Raabof", 0xba482ce12b2e4309, 0x8238ed8b52cb1f1e)
|
||||
|
||||
public:
|
||||
virtual void Rab();
|
||||
};
|
||||
|
||||
CRYREGISTER_SINGLETON_CLASS(CRaboof)
|
||||
|
||||
CRaboof::CRaboof()
|
||||
{
|
||||
}
|
||||
|
||||
CRaboof::~CRaboof()
|
||||
{
|
||||
}
|
||||
|
||||
void CRaboof::Rab()
|
||||
{
|
||||
printf("Inside CRaboof::Rab()\n");
|
||||
}
|
||||
|
||||
static void TestRaboof()
|
||||
{
|
||||
AZStd::shared_ptr<CRaboof> pFoo0_ = CRaboof::CreateClassInstance();
|
||||
IRaboofPtr pFoo0 = cryinterface_cast<IRaboof>(pFoo0_);
|
||||
ICryUnknownPtr p0 = cryinterface_cast<ICryUnknown>(pFoo0);
|
||||
|
||||
CryInterfaceID iid = cryiidof<IRaboof>();
|
||||
CryClassID clsid = p0->GetFactory()->GetClassID();
|
||||
|
||||
AZStd::shared_ptr<CRaboof> pFoo1 = CRaboof::CreateClassInstance();
|
||||
|
||||
pFoo0->Rab();
|
||||
pFoo1->Rab();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class CAB
|
||||
: public IA
|
||||
, public IB
|
||||
{
|
||||
CRYINTERFACE_BEGIN()
|
||||
CRYINTERFACE_ADD(IA)
|
||||
CRYINTERFACE_ADD(IB)
|
||||
CRYINTERFACE_END()
|
||||
|
||||
CRYGENERATE_CLASS(CAB, "AB", 0xb9e54711a64448c0, 0xa4819b4ed3024d04)
|
||||
|
||||
public:
|
||||
virtual void A();
|
||||
virtual void B();
|
||||
|
||||
private:
|
||||
int i;
|
||||
};
|
||||
|
||||
CRYREGISTER_CLASS(CAB)
|
||||
|
||||
CAB::CAB()
|
||||
{
|
||||
i = 0x12345678;
|
||||
}
|
||||
|
||||
CAB::~CAB()
|
||||
{
|
||||
}
|
||||
|
||||
void CAB::A()
|
||||
{
|
||||
printf("Inside CAB::A()\n");
|
||||
}
|
||||
|
||||
void CAB::B()
|
||||
{
|
||||
printf("Inside CAB::B()\n");
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class CABC
|
||||
: public CAB
|
||||
, public IC
|
||||
{
|
||||
CRYINTERFACE_BEGIN()
|
||||
CRYINTERFACE_ADD(IC)
|
||||
CRYINTERFACE_ENDWITHBASE(CAB)
|
||||
|
||||
CRYGENERATE_CLASS(CABC, "ABC", 0x4e61feae11854be7, 0xa16157c5f8baadd9)
|
||||
|
||||
public:
|
||||
virtual void C();
|
||||
|
||||
private:
|
||||
int a;
|
||||
};
|
||||
|
||||
CRYREGISTER_CLASS(CABC)
|
||||
|
||||
CABC::CABC()
|
||||
//: CAB()
|
||||
{
|
||||
a = 0x87654321;
|
||||
}
|
||||
|
||||
CABC::~CABC()
|
||||
{
|
||||
}
|
||||
|
||||
void CABC::C()
|
||||
{
|
||||
printf("Inside CABC::C()\n");
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class CCustomC
|
||||
: public ICustomC
|
||||
{
|
||||
CRYINTERFACE_BEGIN()
|
||||
CRYINTERFACE_ADD(IC)
|
||||
CRYINTERFACE_ADD(ICustomC)
|
||||
CRYINTERFACE_END()
|
||||
|
||||
CRYGENERATE_CLASS(CCustomC, "CustomC", 0xee61760b98a44b71, 0xa05e7372b44bd0fd)
|
||||
|
||||
public:
|
||||
virtual void C();
|
||||
virtual void C1();
|
||||
|
||||
private:
|
||||
int a;
|
||||
};
|
||||
|
||||
CRYREGISTER_CLASS(CCustomC)
|
||||
|
||||
CCustomC::CCustomC()
|
||||
{
|
||||
a = 0x87654321;
|
||||
}
|
||||
|
||||
CCustomC::~CCustomC()
|
||||
{
|
||||
}
|
||||
|
||||
void CCustomC::C()
|
||||
{
|
||||
printf("Inside CCustomC::C()\n");
|
||||
}
|
||||
|
||||
void CCustomC::C1()
|
||||
{
|
||||
printf("Inside CCustomC::C1()\n");
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class CMultiBase
|
||||
: public CAB
|
||||
, public CCustomC
|
||||
{
|
||||
CRYINTERFACE_BEGIN()
|
||||
CRYINTERFACE_ENDWITHBASE2(CAB, CCustomC)
|
||||
|
||||
CRYGENERATE_CLASS(CMultiBase, "MultiBase", 0x75966b8f98644d42, 0x8fbdd489e94cc29e)
|
||||
|
||||
public:
|
||||
virtual void A();
|
||||
virtual void C1();
|
||||
|
||||
int i;
|
||||
};
|
||||
|
||||
CRYREGISTER_CLASS(CMultiBase)
|
||||
|
||||
CMultiBase::CMultiBase()
|
||||
{
|
||||
i = 0x87654321;
|
||||
}
|
||||
|
||||
CMultiBase::~CMultiBase()
|
||||
{
|
||||
}
|
||||
|
||||
void CMultiBase::C1()
|
||||
{
|
||||
printf("Inside CMultiBase::C1()\n");
|
||||
}
|
||||
|
||||
void CMultiBase::A()
|
||||
{
|
||||
printf("Inside CMultiBase::A()\n");
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static void TestComplex()
|
||||
{
|
||||
{
|
||||
ICPtr p;
|
||||
if (CryCreateClassInstance(MAKE_CRYGUID(0x75966b8f98644d42, 0x8fbdd489e94cc29e), p))
|
||||
{
|
||||
p->C();
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
ICustomCPtr p;
|
||||
if (CryCreateClassInstance("MultiBase", p))
|
||||
{
|
||||
p->C();
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
IFoobarPtr p;
|
||||
if (CryCreateClassInstance(MAKE_CRYGUID(0x75966b8f98644d42, 0x8fbdd489e94cc29e), p))
|
||||
{
|
||||
p->Foo();
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
AZStd::shared_ptr<CMultiBase> p = CMultiBase::CreateClassInstance();
|
||||
AZStd::shared_ptr<const CMultiBase> pc = p;
|
||||
|
||||
{
|
||||
ICryUnknownPtr pUnk = cryinterface_cast<ICryUnknown>(p);
|
||||
ICryUnknownConstPtr pCUnk0 = cryinterface_cast<const ICryUnknown>(p);
|
||||
ICryUnknownConstPtr pCUnk1 = cryinterface_cast<const ICryUnknown>(pc);
|
||||
//ICryUnknownPtr pUnkF = cryinterface_cast<ICryUnknown>(pc); // must fail to compile due to const rules
|
||||
|
||||
ICryFactory* pF = pUnk->GetFactory();
|
||||
|
||||
int t = 0;
|
||||
}
|
||||
|
||||
ICPtr pC = cryinterface_cast<IC>(p);
|
||||
ICustomCPtr pCC = cryinterface_cast<ICustomC>(pC);
|
||||
|
||||
p->C();
|
||||
p->C1();
|
||||
|
||||
pC->C();
|
||||
pCC->C1();
|
||||
|
||||
IAPtr pA = cryinterface_cast<IA>(p);
|
||||
pA->A();
|
||||
p->A();
|
||||
}
|
||||
|
||||
{
|
||||
AZStd::shared_ptr<CCustomC> p = CCustomC::CreateClassInstance();
|
||||
|
||||
ICPtr pC = cryinterface_cast<IC>(p);
|
||||
ICustomCPtr pCC = cryinterface_cast<ICustomC>(pC);
|
||||
|
||||
p->C();
|
||||
p->C1();
|
||||
|
||||
pC->C();
|
||||
pCC->C1();
|
||||
}
|
||||
{
|
||||
CryInterfaceID ia = cryiidof<IA>();
|
||||
CryInterfaceID ib = cryiidof<IB>();
|
||||
CryInterfaceID ic = cryiidof<IC>();
|
||||
CryInterfaceID ico = cryiidof<ICryUnknown>();
|
||||
}
|
||||
|
||||
{
|
||||
AZStd::shared_ptr<CAB> p = CAB::CreateClassInstance();
|
||||
CryClassID clsid = p->GetFactory()->GetClassID();
|
||||
|
||||
IAPtr pA = cryinterface_cast<IA>(p);
|
||||
IBPtr pB = cryinterface_cast<IB>(p);
|
||||
|
||||
IBPtr pB1 = cryinterface_cast<IB>(pA);
|
||||
IAPtr pA1 = cryinterface_cast<IA>(pB);
|
||||
|
||||
pA->A();
|
||||
pB->B();
|
||||
|
||||
ICryUnknownPtr p1 = cryinterface_cast<ICryUnknown>(pA);
|
||||
ICryUnknownPtr p2 = cryinterface_cast<ICryUnknown>(pB);
|
||||
const ICryUnknown* p3 = cryinterface_cast<const ICryUnknown>(pB.get());
|
||||
|
||||
int t = 0;
|
||||
}
|
||||
|
||||
{
|
||||
AZStd::shared_ptr<CABC> pABC = CABC::CreateClassInstance();
|
||||
CryClassID clsid = pABC->GetFactory()->GetClassID();
|
||||
|
||||
ICryFactory* pFac = pABC->GetFactory();
|
||||
pFac->ClassSupports(cryiidof<IA>());
|
||||
pFac->ClassSupports(cryiidof<IRaboof>());
|
||||
|
||||
IAPtr pABC0 = cryinterface_cast<IA>(pABC);
|
||||
IBPtr pABC1 = cryinterface_cast<IB>(pABC0);
|
||||
ICPtr pABC2 = cryinterface_cast<IC>(pABC1);
|
||||
|
||||
pABC2->C();
|
||||
pABC1->B();
|
||||
|
||||
pABC2->GetFactory();
|
||||
|
||||
const IC* pCconst = pABC2.get();
|
||||
const ICryUnknown* pOconst = cryinterface_cast<const ICryUnknown>(pCconst);
|
||||
const IA* pAconst = cryinterface_cast<const IA>(pOconst);
|
||||
const IB* pBconst = cryinterface_cast<const IB>(pAconst);
|
||||
|
||||
//const IA* pA11 = cryinterface_cast<IA>(pOconst);
|
||||
|
||||
pCconst = cryinterface_cast<const IC>(pBconst);
|
||||
|
||||
IC* pC = static_cast<IC*>(static_cast<void*>(pABC1.get()));
|
||||
pC->C(); // calls IB::B()
|
||||
|
||||
int t = 0;
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// use of extension system without any of the helper macros/templates
|
||||
|
||||
class CDontLikeMacrosFactory
|
||||
: public ICryFactory
|
||||
{
|
||||
// ICryFactory
|
||||
public:
|
||||
virtual const char* GetClassName() const
|
||||
{
|
||||
return "DontLikeMacros";
|
||||
}
|
||||
virtual const CryClassID& GetClassID() const
|
||||
{
|
||||
static const CryClassID cid = {0x73c3ab0042e6488aull, 0x89ca1a3763365565ull};
|
||||
return cid;
|
||||
}
|
||||
virtual bool ClassSupports(const CryInterfaceID& iid) const
|
||||
{
|
||||
return iid == cryiidof<ICryUnknown>() || iid == cryiidof<IDontLikeMacros>();
|
||||
}
|
||||
virtual void ClassSupports(const CryInterfaceID*& pIIDs, size_t& numIIDs) const
|
||||
{
|
||||
static const CryInterfaceID iids[2] = {cryiidof<ICryUnknown>(), cryiidof<IDontLikeMacros>()};
|
||||
pIIDs = iids;
|
||||
numIIDs = 2;
|
||||
}
|
||||
virtual ICryUnknownPtr CreateClassInstance() const;
|
||||
|
||||
public:
|
||||
static CDontLikeMacrosFactory& Access()
|
||||
{
|
||||
return s_factory;
|
||||
}
|
||||
|
||||
private:
|
||||
CDontLikeMacrosFactory() {}
|
||||
~CDontLikeMacrosFactory() {}
|
||||
|
||||
private:
|
||||
static CDontLikeMacrosFactory s_factory;
|
||||
};
|
||||
|
||||
CDontLikeMacrosFactory CDontLikeMacrosFactory::s_factory;
|
||||
|
||||
class CDontLikeMacros
|
||||
: public IDontLikeMacros
|
||||
{
|
||||
// ICryUnknown
|
||||
public:
|
||||
virtual ICryFactory* GetFactory() const
|
||||
{
|
||||
return &CDontLikeMacrosFactory::Access();
|
||||
};
|
||||
|
||||
// only needed to be able to create initial shared_ptr<CDontLikeMacros> so we don't lose type info for debugging (i.e. inspecting shared_ptr<>)
|
||||
template <class T>
|
||||
friend void AZStd::Internal::sp_ms_deleter<T>::destroy();
|
||||
template <class T>
|
||||
friend AZStd::shared_ptr<T> AZStd::make_shared<T>();
|
||||
|
||||
protected:
|
||||
virtual void* QueryInterface(const CryInterfaceID& iid) const
|
||||
{
|
||||
if (iid == cryiidof<ICryUnknown>())
|
||||
{
|
||||
return (void*) (ICryUnknown*) this;
|
||||
}
|
||||
else if (iid == cryiidof<IDontLikeMacros>())
|
||||
{
|
||||
return (void*) (IDontLikeMacros*) this;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void* QueryComposite(const char*) const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// IDontLikeMacros
|
||||
public:
|
||||
virtual void CallMe()
|
||||
{
|
||||
printf("Yey, no macros...\n");
|
||||
}
|
||||
|
||||
CDontLikeMacros() {}
|
||||
|
||||
protected:
|
||||
virtual ~CDontLikeMacros() {}
|
||||
};
|
||||
|
||||
ICryUnknownPtr CDontLikeMacrosFactory::CreateClassInstance() const
|
||||
{
|
||||
AZStd::shared_ptr<CDontLikeMacros> p = AZStd::make_shared<CDontLikeMacros>();
|
||||
return ICryUnknownPtr(*static_cast<AZStd::shared_ptr<ICryUnknown>*>(static_cast<void*>(&p)));
|
||||
}
|
||||
|
||||
static SRegFactoryNode g_dontLikeMacrosFactory(&CDontLikeMacrosFactory::Access());
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static void TestDontLikeMacros()
|
||||
{
|
||||
ICryFactory* f = &CDontLikeMacrosFactory::Access();
|
||||
|
||||
f->ClassSupports(cryiidof<ICryUnknown>());
|
||||
f->ClassSupports(cryiidof<IDontLikeMacros>());
|
||||
|
||||
const CryInterfaceID* pIIDs = 0;
|
||||
size_t numIIDs = 0;
|
||||
f->ClassSupports(pIIDs, numIIDs);
|
||||
|
||||
ICryUnknownPtr p = f->CreateClassInstance();
|
||||
IDontLikeMacrosPtr pp = cryinterface_cast<IDontLikeMacros>(p);
|
||||
|
||||
ICryUnknownPtr pq = crycomposite_query(p, "blah");
|
||||
|
||||
pp->CallMe();
|
||||
}
|
||||
} // namespace TestExtension
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
void TestExtensions(ICryFactoryRegistryImpl* pReg)
|
||||
{
|
||||
printf("Test extensions:\n");
|
||||
|
||||
struct MyCallback
|
||||
: public ICryFactoryRegistryCallback
|
||||
{
|
||||
virtual void OnNotifyFactoryRegistered(ICryFactory* pFactory)
|
||||
{
|
||||
int test = 0;
|
||||
}
|
||||
virtual void OnNotifyFactoryUnregistered(ICryFactory* pFactory)
|
||||
{
|
||||
int test = 0;
|
||||
}
|
||||
};
|
||||
|
||||
//pReg->RegisterCallback((ICryFactoryRegistryCallback*) 0x4);
|
||||
//pReg->RegisterCallback((ICryFactoryRegistryCallback*) 0x1);
|
||||
//pReg->RegisterCallback((ICryFactoryRegistryCallback*) 0x3);
|
||||
//pReg->RegisterCallback((ICryFactoryRegistryCallback*) 0x3);
|
||||
//pReg->RegisterCallback((ICryFactoryRegistryCallback*) 0x2);
|
||||
|
||||
//pReg->UnregisterCallback((ICryFactoryRegistryCallback*) 0x2);
|
||||
//pReg->UnregisterCallback((ICryFactoryRegistryCallback*) 0x2);
|
||||
//pReg->UnregisterCallback((ICryFactoryRegistryCallback*) 0x4);
|
||||
//pReg->UnregisterCallback((ICryFactoryRegistryCallback*) 0x3);
|
||||
//pReg->UnregisterCallback((ICryFactoryRegistryCallback*) 0x1);
|
||||
|
||||
//MyCallback callback0;
|
||||
//pReg->RegisterCallback(&callback0);
|
||||
//pReg->RegisterFactories(g_pHeadToRegFactories);
|
||||
|
||||
//pReg->RegisterFactories(g_pHeadToRegFactories);
|
||||
//pReg->UnregisterFactories(g_pHeadToRegFactories);
|
||||
|
||||
ICryFactory* pF[4];
|
||||
size_t numFactories = 4;
|
||||
pReg->IterateFactories(cryiidof<IA>(), pF, numFactories);
|
||||
pReg->IterateFactories(MAKE_CRYGUID(-1, -1), pF, numFactories);
|
||||
|
||||
numFactories = (size_t) -1;
|
||||
pReg->IterateFactories(cryiidof<ICryUnknown>(), 0, numFactories);
|
||||
|
||||
MyCallback callback1;
|
||||
pReg->RegisterCallback(&callback1);
|
||||
pReg->UnregisterCallback(&callback1);
|
||||
|
||||
ICryFactory* p;
|
||||
p = pReg->GetFactory(MAKE_CRYGUID(0xee61760b98a44b71, 0xa05e7372b44bd0fd));
|
||||
p = pReg->GetFactory("CustomC");
|
||||
p = pReg->GetFactory("ABC");
|
||||
p = pReg->GetFactory((const char*)0);
|
||||
|
||||
p = pReg->GetFactory("DontLikeMacros");
|
||||
p = pReg->GetFactory(MAKE_CRYGUID(0x73c3ab0042e6488a, 0x89ca1a3763365565));
|
||||
|
||||
TestExtension::TestFoobar();
|
||||
TestExtension::TestRaboof();
|
||||
TestExtension::TestComplex();
|
||||
TestExtension::TestDontLikeMacros();
|
||||
|
||||
TestComposition::TestComposition();
|
||||
}
|
||||
|
||||
#endif // #ifdef EXTENSION_SYSTEM_INCLUDE_TESTCASES
|
||||
@@ -1,126 +0,0 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
// Original file Copyright Crytek GMBH or its affiliates, used under license.
|
||||
|
||||
// Description : Part of CryEngine's extension framework.
|
||||
|
||||
|
||||
#ifndef CRYINCLUDE_CRYSYSTEM_EXTENSIONSYSTEM_TESTCASES_TESTEXTENSIONS_H
|
||||
#define CRYINCLUDE_CRYSYSTEM_EXTENSIONSYSTEM_TESTCASES_TESTEXTENSIONS_H
|
||||
#pragma once
|
||||
|
||||
|
||||
//#define EXTENSION_SYSTEM_INCLUDE_TESTCASES
|
||||
|
||||
#ifdef EXTENSION_SYSTEM_INCLUDE_TESTCASES
|
||||
|
||||
#include <CryExtension/ICryUnknown.h>
|
||||
|
||||
struct ICryFactoryRegistryImpl;
|
||||
|
||||
void TestExtensions(ICryFactoryRegistryImpl* pReg);
|
||||
|
||||
struct IFoobar
|
||||
: public ICryUnknown
|
||||
{
|
||||
CRYINTERFACE_DECLARE(IFoobar, 0x539e9c672cad4a03, 0x9ecd8069c99a846b)
|
||||
|
||||
virtual void Foo() = 0;
|
||||
};
|
||||
|
||||
DECLARE_SMART_POINTERS(IFoobar);
|
||||
|
||||
struct IRaboof
|
||||
: public ICryUnknown
|
||||
{
|
||||
CRYINTERFACE_DECLARE(IRaboof, 0x135ca25e634b4d13, 0x9e4467968a708822)
|
||||
|
||||
virtual void Rab() = 0;
|
||||
};
|
||||
|
||||
DECLARE_SMART_POINTERS(IRaboof);
|
||||
|
||||
struct IA
|
||||
: public ICryUnknown
|
||||
{
|
||||
CRYINTERFACE_DECLARE(IA, 0xd93aaceb35ec427e, 0xb64bf8dec4997e67)
|
||||
|
||||
virtual void A() = 0;
|
||||
};
|
||||
|
||||
DECLARE_SMART_POINTERS(IA);
|
||||
|
||||
struct IB
|
||||
: public ICryUnknown
|
||||
{
|
||||
CRYINTERFACE_DECLARE(IB, 0xe0d830c826424e11, 0x9eacfa19eaf31ffb)
|
||||
|
||||
virtual void B() = 0;
|
||||
};
|
||||
|
||||
DECLARE_SMART_POINTERS(IB);
|
||||
|
||||
struct IC
|
||||
: public ICryUnknown
|
||||
{
|
||||
CRYINTERFACE_DECLARE(IC, 0x577509a20fc5477c, 0x893757c9ca88b27b)
|
||||
|
||||
virtual void C() = 0;
|
||||
};
|
||||
|
||||
DECLARE_SMART_POINTERS(IC);
|
||||
|
||||
struct ICustomC
|
||||
: public IC
|
||||
{
|
||||
CRYINTERFACE_DECLARE(ICustomC, 0x2ac769da4c7443bf, 0x80911033e21dfbcf)
|
||||
|
||||
virtual void C1() = 0;
|
||||
};
|
||||
|
||||
DECLARE_SMART_POINTERS(ICustomC);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// use of extension system without any of the helper macros/templates
|
||||
|
||||
struct IDontLikeMacros
|
||||
: public ICryUnknown
|
||||
{
|
||||
template <class T>
|
||||
friend const CryInterfaceID& InterfaceCastSemantics::cryiidof();
|
||||
template <class T>
|
||||
friend void AZStd::Internal::sp_ms_deleter<T>::destroy();
|
||||
template <class T>
|
||||
friend AZStd::shared_ptr<T> AZStd::make_shared<T>();
|
||||
protected:
|
||||
virtual ~IDontLikeMacros() {}
|
||||
|
||||
private:
|
||||
// It's very important that this static function is implemented for each interface!
|
||||
// Otherwise the consistency of cryinterface_cast<T>() is compromised because
|
||||
// cryiidof<T>() = cryiidof<baseof<T>>() {baseof<T> = ICryUnknown in most cases}
|
||||
static const CryInterfaceID& IID()
|
||||
{
|
||||
static const CryInterfaceID iid = {0x0f43b7e3f1364af0ull, 0xb4a16a975bea3ec4ull};
|
||||
return iid;
|
||||
}
|
||||
|
||||
public:
|
||||
virtual void CallMe() = 0;
|
||||
};
|
||||
|
||||
DECLARE_SMART_POINTERS(IDontLikeMacros);
|
||||
|
||||
|
||||
#endif // #ifdef EXTENSION_SYSTEM_INCLUDE_TESTCASES
|
||||
|
||||
#endif // CRYINCLUDE_CRYSYSTEM_EXTENSIONSYSTEM_TESTCASES_TESTEXTENSIONS_H
|
||||
@@ -1,278 +0,0 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
// Original file Copyright Crytek GMBH or its affiliates, used under license.
|
||||
|
||||
// Description : A multiplatform base class for handling errors and collecting call stacks
|
||||
|
||||
|
||||
#include "CrySystem_precompiled.h"
|
||||
#include "IDebugCallStack.h"
|
||||
#include <Pak/CryPakUtils.h>
|
||||
#include "System.h"
|
||||
#include <AzFramework/IO/FileOperations.h>
|
||||
#include <AzCore/NativeUI/NativeUIRequests.h>
|
||||
#include <AzCore/StringFunc/StringFunc.h>
|
||||
#include <AzCore/Utils/Utils.h>
|
||||
//#if !defined(LINUX)
|
||||
|
||||
#include <ISystem.h>
|
||||
|
||||
const char* const IDebugCallStack::s_szFatalErrorCode = "FATAL_ERROR";
|
||||
|
||||
IDebugCallStack::IDebugCallStack()
|
||||
: m_bIsFatalError(false)
|
||||
, m_postBackupProcess(0)
|
||||
, m_memAllocFileHandle(AZ::IO::InvalidHandle)
|
||||
{
|
||||
}
|
||||
|
||||
IDebugCallStack::~IDebugCallStack()
|
||||
{
|
||||
StopMemLog();
|
||||
}
|
||||
|
||||
#if AZ_LEGACY_CRYSYSTEM_TRAIT_DEBUGCALLSTACK_SINGLETON
|
||||
IDebugCallStack* IDebugCallStack::instance()
|
||||
{
|
||||
static IDebugCallStack sInstance;
|
||||
return &sInstance;
|
||||
}
|
||||
#endif
|
||||
|
||||
void IDebugCallStack::FileCreationCallback(void (* postBackupProcess)())
|
||||
{
|
||||
m_postBackupProcess = postBackupProcess;
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void IDebugCallStack::LogCallstack()
|
||||
{
|
||||
AZ::Debug::Trace::PrintCallstack("", 2);
|
||||
}
|
||||
|
||||
const char* IDebugCallStack::TranslateExceptionCode(DWORD dwExcept)
|
||||
{
|
||||
switch (dwExcept)
|
||||
{
|
||||
#if AZ_LEGACY_CRYSYSTEM_TRAIT_DEBUGCALLSTACK_TRANSLATE
|
||||
case EXCEPTION_ACCESS_VIOLATION:
|
||||
return "EXCEPTION_ACCESS_VIOLATION";
|
||||
break;
|
||||
case EXCEPTION_DATATYPE_MISALIGNMENT:
|
||||
return "EXCEPTION_DATATYPE_MISALIGNMENT";
|
||||
break;
|
||||
case EXCEPTION_BREAKPOINT:
|
||||
return "EXCEPTION_BREAKPOINT";
|
||||
break;
|
||||
case EXCEPTION_SINGLE_STEP:
|
||||
return "EXCEPTION_SINGLE_STEP";
|
||||
break;
|
||||
case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
|
||||
return "EXCEPTION_ARRAY_BOUNDS_EXCEEDED";
|
||||
break;
|
||||
case EXCEPTION_FLT_DENORMAL_OPERAND:
|
||||
return "EXCEPTION_FLT_DENORMAL_OPERAND";
|
||||
break;
|
||||
case EXCEPTION_FLT_DIVIDE_BY_ZERO:
|
||||
return "EXCEPTION_FLT_DIVIDE_BY_ZERO";
|
||||
break;
|
||||
case EXCEPTION_FLT_INEXACT_RESULT:
|
||||
return "EXCEPTION_FLT_INEXACT_RESULT";
|
||||
break;
|
||||
case EXCEPTION_FLT_INVALID_OPERATION:
|
||||
return "EXCEPTION_FLT_INVALID_OPERATION";
|
||||
break;
|
||||
case EXCEPTION_FLT_OVERFLOW:
|
||||
return "EXCEPTION_FLT_OVERFLOW";
|
||||
break;
|
||||
case EXCEPTION_FLT_STACK_CHECK:
|
||||
return "EXCEPTION_FLT_STACK_CHECK";
|
||||
break;
|
||||
case EXCEPTION_FLT_UNDERFLOW:
|
||||
return "EXCEPTION_FLT_UNDERFLOW";
|
||||
break;
|
||||
case EXCEPTION_INT_DIVIDE_BY_ZERO:
|
||||
return "EXCEPTION_INT_DIVIDE_BY_ZERO";
|
||||
break;
|
||||
case EXCEPTION_INT_OVERFLOW:
|
||||
return "EXCEPTION_INT_OVERFLOW";
|
||||
break;
|
||||
case EXCEPTION_PRIV_INSTRUCTION:
|
||||
return "EXCEPTION_PRIV_INSTRUCTION";
|
||||
break;
|
||||
case EXCEPTION_IN_PAGE_ERROR:
|
||||
return "EXCEPTION_IN_PAGE_ERROR";
|
||||
break;
|
||||
case EXCEPTION_ILLEGAL_INSTRUCTION:
|
||||
return "EXCEPTION_ILLEGAL_INSTRUCTION";
|
||||
break;
|
||||
case EXCEPTION_NONCONTINUABLE_EXCEPTION:
|
||||
return "EXCEPTION_NONCONTINUABLE_EXCEPTION";
|
||||
break;
|
||||
case EXCEPTION_STACK_OVERFLOW:
|
||||
return "EXCEPTION_STACK_OVERFLOW";
|
||||
break;
|
||||
case EXCEPTION_INVALID_DISPOSITION:
|
||||
return "EXCEPTION_INVALID_DISPOSITION";
|
||||
break;
|
||||
case EXCEPTION_GUARD_PAGE:
|
||||
return "EXCEPTION_GUARD_PAGE";
|
||||
break;
|
||||
case EXCEPTION_INVALID_HANDLE:
|
||||
return "EXCEPTION_INVALID_HANDLE";
|
||||
break;
|
||||
//case EXCEPTION_POSSIBLE_DEADLOCK: return "EXCEPTION_POSSIBLE_DEADLOCK"; break ;
|
||||
|
||||
case STATUS_FLOAT_MULTIPLE_FAULTS:
|
||||
return "STATUS_FLOAT_MULTIPLE_FAULTS";
|
||||
break;
|
||||
case STATUS_FLOAT_MULTIPLE_TRAPS:
|
||||
return "STATUS_FLOAT_MULTIPLE_TRAPS";
|
||||
break;
|
||||
|
||||
|
||||
#endif
|
||||
default:
|
||||
return "Unknown";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void IDebugCallStack::PutVersion(char* str, size_t length)
|
||||
{
|
||||
AZ_PUSH_DISABLE_WARNING(4996, "-Wunknown-warning-option")
|
||||
|
||||
if (!gEnv || !gEnv->pSystem)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
char sFileVersion[128];
|
||||
gEnv->pSystem->GetFileVersion().ToString(sFileVersion, sizeof(sFileVersion));
|
||||
|
||||
char sProductVersion[128];
|
||||
gEnv->pSystem->GetProductVersion().ToString(sProductVersion, sizeof(sFileVersion));
|
||||
|
||||
|
||||
//! Get time.
|
||||
time_t ltime;
|
||||
time(<ime);
|
||||
tm* today = localtime(<ime);
|
||||
|
||||
char s[1024];
|
||||
//! Use strftime to build a customized time string.
|
||||
strftime(s, 128, "Logged at %#c\n", today);
|
||||
azstrcat(str, length, s);
|
||||
sprintf_s(s, "FileVersion: %s\n", sFileVersion);
|
||||
azstrcat(str, length, s);
|
||||
sprintf_s(s, "ProductVersion: %s\n", sProductVersion);
|
||||
azstrcat(str, length, s);
|
||||
|
||||
if (gEnv->pLog)
|
||||
{
|
||||
const char* logfile = gEnv->pLog->GetFileName();
|
||||
if (logfile)
|
||||
{
|
||||
sprintf (s, "LogFile: %s\n", logfile);
|
||||
azstrcat(str, length, s);
|
||||
}
|
||||
}
|
||||
|
||||
AZ::IO::FixedMaxPathString projectPath = AZ::Utils::GetProjectPath();
|
||||
azstrcat(str, length, "ProjectDir: ");
|
||||
azstrcat(str, length, projectPath.c_str());
|
||||
azstrcat(str, length, "\n");
|
||||
|
||||
#if AZ_LEGACY_CRYSYSTEM_TRAIT_DEBUGCALLSTACK_APPEND_MODULENAME
|
||||
GetModuleFileNameA(NULL, s, sizeof(s));
|
||||
|
||||
// Log EXE filename only if possible (not full EXE path which could contain sensitive info)
|
||||
AZStd::string exeName;
|
||||
if (AZ::StringFunc::Path::GetFullFileName(s, exeName))
|
||||
{
|
||||
azstrcat(str, length, "Executable: ");
|
||||
azstrcat(str, length, exeName.c_str());
|
||||
|
||||
# ifdef AZ_DEBUG_BUILD
|
||||
azstrcat(str, length, " (debug: yes");
|
||||
# else
|
||||
azstrcat(str, length, " (debug: no");
|
||||
# endif
|
||||
}
|
||||
#endif
|
||||
AZ_POP_DISABLE_WARNING
|
||||
}
|
||||
|
||||
|
||||
//Crash the application, in this way the debug callstack routine will be called and it will create all the necessary files (error.log, dump, and eventually screenshot)
|
||||
void IDebugCallStack::FatalError(const char* description)
|
||||
{
|
||||
m_bIsFatalError = true;
|
||||
WriteLineToLog(description);
|
||||
|
||||
#ifndef _RELEASE
|
||||
bool bShowDebugScreen = g_cvars.sys_no_crash_dialog == 0;
|
||||
// showing the debug screen is not safe when not called from mainthread
|
||||
// it normally leads to a infinity recursion followed by a stack overflow, preventing
|
||||
// useful call stacks, thus they are disabled
|
||||
bShowDebugScreen = bShowDebugScreen && gEnv->mMainThreadId == CryGetCurrentThreadId();
|
||||
if (bShowDebugScreen)
|
||||
{
|
||||
EBUS_EVENT(AZ::NativeUI::NativeUIRequestBus, DisplayOkDialog, "Open 3D Engine Fatal Error", description, false);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(WIN32) || !defined(_RELEASE)
|
||||
int* p = 0x0;
|
||||
PREFAST_SUPPRESS_WARNING(6011) * p = 1; // we're intentionally crashing here
|
||||
#endif
|
||||
}
|
||||
|
||||
void IDebugCallStack::WriteLineToLog(const char* format, ...)
|
||||
{
|
||||
CDebugAllowFileAccess allowFileAccess;
|
||||
|
||||
va_list ArgList;
|
||||
char szBuffer[MAX_WARNING_LENGTH];
|
||||
va_start(ArgList, format);
|
||||
vsnprintf_s(szBuffer, sizeof(szBuffer), sizeof(szBuffer) - 1, format, ArgList);
|
||||
cry_strcat(szBuffer, "\n");
|
||||
szBuffer[sizeof(szBuffer) - 1] = '\0';
|
||||
va_end(ArgList);
|
||||
|
||||
AZ::IO::HandleType fileHandle = AZ::IO::InvalidHandle;
|
||||
AZ::IO::FileIOBase::GetDirectInstance()->Open("@Log@\\error.log", AZ::IO::GetOpenModeFromStringMode("a+t"), fileHandle);
|
||||
if (fileHandle != AZ::IO::InvalidHandle)
|
||||
{
|
||||
AZ::IO::FileIOBase::GetDirectInstance()->Write(fileHandle, szBuffer, strlen(szBuffer));
|
||||
AZ::IO::FileIOBase::GetDirectInstance()->Flush(fileHandle);
|
||||
AZ::IO::FileIOBase::GetDirectInstance()->Close(fileHandle);
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void IDebugCallStack::StartMemLog()
|
||||
{
|
||||
AZ::IO::FileIOBase::GetDirectInstance()->Open("@Log@\\memallocfile.log", AZ::IO::OpenMode::ModeWrite, m_memAllocFileHandle);
|
||||
|
||||
assert(m_memAllocFileHandle != AZ::IO::InvalidHandle);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void IDebugCallStack::StopMemLog()
|
||||
{
|
||||
if (m_memAllocFileHandle != AZ::IO::InvalidHandle)
|
||||
{
|
||||
AZ::IO::FileIOBase::GetDirectInstance()->Close(m_memAllocFileHandle);
|
||||
m_memAllocFileHandle = AZ::IO::InvalidHandle;
|
||||
}
|
||||
}
|
||||
//#endif //!defined(LINUX)
|
||||
@@ -1,90 +0,0 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
// Original file Copyright Crytek GMBH or its affiliates, used under license.
|
||||
|
||||
// Description : A multiplatform base class for handling errors and collecting call stacks
|
||||
|
||||
#ifndef CRYINCLUDE_CRYSYSTEM_IDEBUGCALLSTACK_H
|
||||
#define CRYINCLUDE_CRYSYSTEM_IDEBUGCALLSTACK_H
|
||||
#pragma once
|
||||
|
||||
#include "System.h"
|
||||
|
||||
#if AZ_LEGACY_CRYSYSTEM_TRAIT_FORWARD_EXCEPTION_POINTERS
|
||||
struct EXCEPTION_POINTERS;
|
||||
#endif
|
||||
//! Limits the maximal number of functions in call stack.
|
||||
enum
|
||||
{
|
||||
MAX_DEBUG_STACK_ENTRIES = 80
|
||||
};
|
||||
|
||||
class IDebugCallStack
|
||||
{
|
||||
public:
|
||||
// Returns single instance of DebugStack
|
||||
static IDebugCallStack* instance();
|
||||
|
||||
virtual int handleException([[maybe_unused]] EXCEPTION_POINTERS* exception_pointer){return 0; }
|
||||
|
||||
// returns the module name of a given address
|
||||
virtual string GetModuleNameForAddr([[maybe_unused]] void* addr) { return "[unknown]"; }
|
||||
|
||||
// returns the function name of a given address together with source file and line number (if available) of a given address
|
||||
virtual void GetProcNameForAddr(void* addr, string& procName, void*& baseAddr, string& filename, int& line)
|
||||
{
|
||||
filename = "[unknown]";
|
||||
line = 0;
|
||||
baseAddr = addr;
|
||||
#if defined(PLATFORM_64BIT)
|
||||
procName.Format("[%016llX]", addr);
|
||||
#else
|
||||
procName.Format("[%08X]", addr);
|
||||
#endif
|
||||
}
|
||||
|
||||
// returns current filename
|
||||
virtual string GetCurrentFilename() { return "[unknown]"; }
|
||||
|
||||
//! Dumps Current Call Stack to log.
|
||||
virtual void LogCallstack();
|
||||
//triggers a fatal error, so the DebugCallstack can create the error.log and terminate the application
|
||||
void FatalError(const char*);
|
||||
|
||||
//Reports a bug and continues execution
|
||||
virtual void ReportBug(const char*) {}
|
||||
|
||||
virtual void FileCreationCallback(void (* postBackupProcess)());
|
||||
|
||||
static void WriteLineToLog(const char* format, ...);
|
||||
|
||||
virtual void StartMemLog();
|
||||
virtual void StopMemLog();
|
||||
|
||||
protected:
|
||||
IDebugCallStack();
|
||||
virtual ~IDebugCallStack();
|
||||
|
||||
static const char* TranslateExceptionCode(DWORD dwExcept);
|
||||
static void PutVersion(char* str, size_t length);
|
||||
|
||||
bool m_bIsFatalError;
|
||||
static const char* const s_szFatalErrorCode;
|
||||
|
||||
void (* m_postBackupProcess)();
|
||||
|
||||
AZ::IO::HandleType m_memAllocFileHandle;
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif // CRYINCLUDE_CRYSYSTEM_IDEBUGCALLSTACK_H
|
||||
@@ -1,59 +0,0 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
// Original file Copyright Crytek GMBH or its affiliates, used under license.
|
||||
|
||||
#pragma once
|
||||
|
||||
struct SThreadConfig
|
||||
{
|
||||
enum eThreadParamFlag
|
||||
{
|
||||
eThreadParamFlag_ThreadName = BIT(0),
|
||||
eThreadParamFlag_StackSize = BIT(1),
|
||||
eThreadParamFlag_Affinity = BIT(2),
|
||||
eThreadParamFlag_Priority = BIT(3),
|
||||
eThreadParamFlag_PriorityBoost = BIT(4),
|
||||
};
|
||||
|
||||
typedef uint32 TThreadParamFlag;
|
||||
|
||||
const char* szThreadName;
|
||||
uint32 stackSizeBytes;
|
||||
uint32 affinityFlag;
|
||||
int32 priority;
|
||||
bool bDisablePriorityBoost;
|
||||
|
||||
TThreadParamFlag paramActivityFlag;
|
||||
};
|
||||
|
||||
class IThreadConfigManager
|
||||
{
|
||||
public:
|
||||
virtual ~IThreadConfigManager()
|
||||
{
|
||||
}
|
||||
|
||||
//! Called once during System startup.
|
||||
//! Loads the thread configuration for the executing platform from file.
|
||||
virtual bool LoadConfig(const char* pcPath) = 0;
|
||||
|
||||
//! Returns true if a config has been loaded.
|
||||
virtual bool ConfigLoaded() const = 0;
|
||||
|
||||
//! Gets the thread configuration for the specified thread on the active platform.
|
||||
//! If no matching config is found a default configuration is returned (which does not have the same name as the search string).
|
||||
virtual const SThreadConfig* GetThreadConfig(const char* sThreadName, ...) = 0;
|
||||
virtual const SThreadConfig* GetDefaultThreadConfig() const = 0;
|
||||
|
||||
//! Dump a detailed description of the thread startup configurations for this platform to the log file.
|
||||
virtual void DumpThreadConfigurationsToLog() = 0;
|
||||
};
|
||||
@@ -17,7 +17,6 @@
|
||||
#include "LevelSystem.h"
|
||||
#include <IAudioSystem.h>
|
||||
#include "IMovieSystem.h"
|
||||
#include "IMaterialEffects.h"
|
||||
#include <IResourceManager.h>
|
||||
#include <ILocalizationManager.h>
|
||||
#include "CryPath.h"
|
||||
@@ -648,20 +647,6 @@ ILevel* CLevelSystem::LoadLevelInternal(const char* _levelName)
|
||||
|
||||
AZStd::string levelPath(pLevelInfo->GetPath());
|
||||
|
||||
/*
|
||||
ICVar *pFileCache = gEnv->pConsole->GetCVar("sys_FileCache"); CRY_ASSERT(pFileCache);
|
||||
|
||||
if(pFileCache->GetIVal())
|
||||
{
|
||||
if(pPak->OpenPack("",pLevelInfo->GetPath()+string("/FileCache.dat")))
|
||||
gEnv->pLog->Log("FileCache.dat loaded");
|
||||
else
|
||||
gEnv->pLog->Log("FileCache.dat not loaded");
|
||||
}
|
||||
*/
|
||||
|
||||
m_pSystem->SetThreadState(ESubsys_Physics, false);
|
||||
|
||||
ICVar* pSpamDelay = gEnv->pConsole->GetCVar("log_SpamDelay");
|
||||
float spamDelay = 0.0f;
|
||||
if (pSpamDelay)
|
||||
@@ -768,8 +753,6 @@ ILevel* CLevelSystem::LoadLevelInternal(const char* _levelName)
|
||||
|
||||
gEnv->pSystem->GetISystemEventDispatcher()->OnSystemEvent(ESYSTEM_EVENT_LEVEL_PRECACHE_START, 0, 0);
|
||||
|
||||
m_pSystem->SetThreadState(ESubsys_Physics, true);
|
||||
|
||||
return m_pCurrentLevel;
|
||||
}
|
||||
|
||||
|
||||
@@ -247,8 +247,6 @@ namespace LegacyLevelSystem
|
||||
|
||||
auto pPak = gEnv->pCryPak;
|
||||
|
||||
m_pSystem->SetThreadState(ESubsys_Physics, false);
|
||||
|
||||
ICVar* pSpamDelay = gEnv->pConsole->GetCVar("log_SpamDelay");
|
||||
float spamDelay = 0.0f;
|
||||
if (pSpamDelay)
|
||||
@@ -343,8 +341,6 @@ namespace LegacyLevelSystem
|
||||
|
||||
gEnv->pSystem->GetISystemEventDispatcher()->OnSystemEvent(ESYSTEM_EVENT_LEVEL_PRECACHE_START, 0, 0);
|
||||
|
||||
m_pSystem->SetThreadState(ESubsys_Physics, true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -920,13 +920,7 @@ bool CLog::LogToMainThread(const char* szString, ELogType logType, bool bAdd, SL
|
||||
msg.bAdd = bAdd;
|
||||
msg.destination = destination;
|
||||
msg.logType = logType;
|
||||
// don't try to store the log message for later in case of out of memory, since then its very likely that this allocation
|
||||
// also fails and results in a stack overflow. This way we should at least get a out of memory on-screen message instead of
|
||||
// a not obvious crash
|
||||
if ((gEnv) && (gEnv->bIsOutOfMemory == false))
|
||||
{
|
||||
m_threadSafeMsgQueue.push(msg);
|
||||
}
|
||||
m_threadSafeMsgQueue.push(msg);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -1448,8 +1442,6 @@ void CLog::UpdateLoadingScreen(const char* szFormat, ...)
|
||||
|
||||
if (CryGetCurrentThreadId() == m_nMainThreadId)
|
||||
{
|
||||
((CSystem*)m_pSystem)->UpdateLoadingScreen();
|
||||
|
||||
#ifndef LINUX
|
||||
// Take this opportunity to update streaming engine.
|
||||
if (IStreamEngine* pStreamEngine = GetISystem()->GetStreamEngine())
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,293 +0,0 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
// Original file Copyright Crytek GMBH or its affiliates, used under license.
|
||||
|
||||
#ifndef CRYINCLUDE_CRYSYSTEM_NOTIFICATIONNETWORK_H
|
||||
#define CRYINCLUDE_CRYSYSTEM_NOTIFICATIONNETWORK_H
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <IConsole.h>
|
||||
#include <INotificationNetwork.h>
|
||||
#include <CryThread.h>
|
||||
|
||||
#include <AzCore/Socket/AzSocket_fwd.h>
|
||||
|
||||
class CNotificationNetwork;
|
||||
namespace NotificationNetwork {
|
||||
// Constants
|
||||
|
||||
static const uint32 NN_PACKET_HEADER_LENGTH = 2 * sizeof(uint32) + NN_CHANNEL_NAME_LENGTH_MAX;
|
||||
|
||||
static const uint32 NN_PACKET_HEADER_OFFSET_MESSAGE = 0;
|
||||
static const uint32 NN_PACKET_HEADER_OFFSET_DATA_LENGTH = sizeof(uint32);
|
||||
static const uint32 NN_PACKET_HEADER_OFFSET_CHANNEL = sizeof(uint32) + sizeof(uint32);
|
||||
|
||||
static const char* NN_THREAD_NAME = "NotificationNetwork";
|
||||
|
||||
enum EMessage
|
||||
{
|
||||
eMessage_DataTransfer = 0xbada2217,
|
||||
|
||||
eMessage_ChannelRegister = 0xab4eda30,
|
||||
eMessage_ChannelUnregister = 0xfa4e3423,
|
||||
};
|
||||
|
||||
// Classes
|
||||
|
||||
struct CChannel
|
||||
{
|
||||
public:
|
||||
static bool IsNameValid(const char* name);
|
||||
|
||||
public:
|
||||
CChannel();
|
||||
CChannel(const char* name);
|
||||
~CChannel();
|
||||
|
||||
public:
|
||||
void WriteToPacketHeader(void* pPacket) const;
|
||||
void ReadFromPacketHeader(void* pPacket);
|
||||
|
||||
public:
|
||||
bool operator ==(const CChannel& channel) const;
|
||||
bool operator !=(const CChannel& channel) const;
|
||||
|
||||
private:
|
||||
char m_name[NN_CHANNEL_NAME_LENGTH_MAX];
|
||||
};
|
||||
|
||||
// TEMP
|
||||
struct SBuffer
|
||||
{
|
||||
uint8* pData;
|
||||
uint32 length;
|
||||
CChannel channel;
|
||||
};
|
||||
|
||||
class CListeners
|
||||
{
|
||||
public:
|
||||
CListeners();
|
||||
~CListeners();
|
||||
|
||||
public:
|
||||
size_t Count() { return m_listeners.size(); }
|
||||
size_t Count(const CChannel& channel);
|
||||
|
||||
CChannel& Channel(size_t index) { return m_listeners[index].second; }
|
||||
CChannel* Channel(INotificationNetworkListener* pListener);
|
||||
|
||||
bool Bind(const CChannel& channel, INotificationNetworkListener* pListener);
|
||||
bool Remove(INotificationNetworkListener* pListener);
|
||||
|
||||
void NotificationPush(const SBuffer& buffer);
|
||||
void NotificationsProcess();
|
||||
|
||||
private:
|
||||
std::vector< std::pair<INotificationNetworkListener*, CChannel> > m_listeners;
|
||||
|
||||
std::queue<SBuffer> m_notifications[2];
|
||||
std::queue<SBuffer>* m_pNotificationWrite;
|
||||
std::queue<SBuffer>* m_pNotificationRead;
|
||||
CryCriticalSection m_notificationCriticalSection;
|
||||
};
|
||||
|
||||
class CConnectionBase
|
||||
{
|
||||
public:
|
||||
CConnectionBase(CNotificationNetwork* pNotificationNetwork);
|
||||
virtual ~CConnectionBase();
|
||||
|
||||
public:
|
||||
AZSOCKET CreateSocket();
|
||||
|
||||
bool Connect(const char* address, uint16 port);
|
||||
|
||||
AZSOCKET GetSocket() { return m_socket; }
|
||||
|
||||
bool Validate();
|
||||
|
||||
bool SendNotification(const CChannel& channel, const void* pBuffer, size_t length);
|
||||
|
||||
bool Receive(CListeners& listeners);
|
||||
|
||||
bool GetIsConnectedFlag();
|
||||
bool GetIsFailedToConnectFlag() const;
|
||||
|
||||
protected:
|
||||
CNotificationNetwork* GetNotificationNetwork() { return m_pNotificationNetwork; }
|
||||
|
||||
void SetAddress(const char* address, uint16 port);
|
||||
void SetSocket(AZSOCKET sock) { m_socket = sock; }
|
||||
|
||||
bool Send(const void* pBuffer, size_t length);
|
||||
bool SendMessage(EMessage eMessage, const CChannel& channel, uint32 data);
|
||||
|
||||
bool Select_Internal();
|
||||
void CloseSocket_Internal();
|
||||
|
||||
virtual bool OnConnect([[maybe_unused]] bool bConnectionResult) { return true; }
|
||||
virtual bool OnDisconnect() {return true; }
|
||||
virtual bool OnMessage([[maybe_unused]] EMessage eMessage, [[maybe_unused]] const CChannel& channel) { return false; }
|
||||
|
||||
private:
|
||||
bool ReceiveMessage(CListeners& listeners);
|
||||
bool ReceiveNotification(CListeners& listeners);
|
||||
|
||||
protected:
|
||||
CNotificationNetwork* m_pNotificationNetwork;
|
||||
|
||||
char m_address[16];
|
||||
uint16 m_port;
|
||||
|
||||
AZSOCKET m_socket;
|
||||
|
||||
uint8 m_bufferHeader[NN_PACKET_HEADER_LENGTH];
|
||||
SBuffer m_buffer;
|
||||
uint32 m_dataLeft;
|
||||
|
||||
volatile bool m_boIsConnected;
|
||||
volatile bool m_boIsFailedToConnect;
|
||||
};
|
||||
|
||||
class CClient
|
||||
: public CConnectionBase
|
||||
, public INotificationNetworkClient
|
||||
{
|
||||
public:
|
||||
typedef std::vector<INotificationNetworkConnectionCallback*> TDNotificationNetworkConnectionCallbacks;
|
||||
|
||||
static CClient* Create(CNotificationNetwork* pNotificationNetwork, const char* address, uint16 port);
|
||||
static CClient* Create(CNotificationNetwork* pNotificationNetwork);
|
||||
|
||||
private:
|
||||
CClient(CNotificationNetwork* pNotificationNetwork);
|
||||
~CClient();
|
||||
|
||||
public:
|
||||
bool Receive() { return CConnectionBase::Receive(m_listeners); }
|
||||
|
||||
void Update();
|
||||
|
||||
// CConnectionBase
|
||||
public:
|
||||
virtual bool OnConnect(bool bConnectionResult);
|
||||
virtual bool OnDisconnect();
|
||||
virtual bool OnMessage(EMessage eMessage, const CChannel& channel);
|
||||
|
||||
// INotificationNetworkClient
|
||||
public:
|
||||
bool Connect(const char* address, uint16 port);
|
||||
|
||||
void Release() { delete this; }
|
||||
|
||||
virtual bool ListenerBind(const char* channelName, INotificationNetworkListener* pListener);
|
||||
virtual bool ListenerRemove(INotificationNetworkListener* pListener);
|
||||
|
||||
virtual bool Send(const char* channelName, const void* pBuffer, size_t length);
|
||||
|
||||
virtual bool IsConnected() {return CConnectionBase::GetIsConnectedFlag(); }
|
||||
virtual bool IsFailedToConnect() const{return CConnectionBase::GetIsFailedToConnectFlag(); }
|
||||
|
||||
virtual bool RegisterCallbackListener(INotificationNetworkConnectionCallback* pConnectionCallback);
|
||||
virtual bool UnregisterCallbackListener(INotificationNetworkConnectionCallback* pConnectionCallback);
|
||||
private:
|
||||
CListeners m_listeners;
|
||||
|
||||
TDNotificationNetworkConnectionCallbacks m_cNotificationNetworkConnectionCallbacks;
|
||||
CryCriticalSection m_stConnectionCallbacksLock;
|
||||
};
|
||||
} // namespace NotificationNetwork
|
||||
class CNotificationNetwork
|
||||
: public INotificationNetwork
|
||||
{
|
||||
private:
|
||||
class CConnection
|
||||
: public NotificationNetwork::CConnectionBase
|
||||
{
|
||||
public:
|
||||
CConnection(CNotificationNetwork* pNotificationNetwork, AZSOCKET sock);
|
||||
virtual ~CConnection();
|
||||
|
||||
public:
|
||||
bool IsListening(const NotificationNetwork::CChannel& channel);
|
||||
|
||||
// CConnectionBase
|
||||
protected:
|
||||
virtual bool OnMessage(NotificationNetwork::EMessage eMessage, const NotificationNetwork::CChannel& channel);
|
||||
|
||||
private:
|
||||
std::vector<NotificationNetwork::CChannel> m_listeningChannels;
|
||||
};
|
||||
|
||||
class CThread
|
||||
: public CryThread<CThread>
|
||||
{
|
||||
public:
|
||||
CThread();
|
||||
~CThread();
|
||||
|
||||
public:
|
||||
bool Begin(CNotificationNetwork* pNotificationNetwork);
|
||||
void End();
|
||||
|
||||
// CryRunnable
|
||||
public:
|
||||
virtual void Run();
|
||||
|
||||
private:
|
||||
CNotificationNetwork* m_pNotificationNetwork;
|
||||
bool m_bRun;
|
||||
} m_thread;
|
||||
|
||||
public:
|
||||
static CNotificationNetwork* Create();
|
||||
|
||||
public:
|
||||
CNotificationNetwork();
|
||||
~CNotificationNetwork();
|
||||
|
||||
public:
|
||||
void ReleaseClients(NotificationNetwork::CClient* pClient);
|
||||
|
||||
private:
|
||||
void ProcessSockets();
|
||||
|
||||
// INotificationNetwork
|
||||
public:
|
||||
virtual void Release() { delete this; }
|
||||
|
||||
virtual INotificationNetworkClient* CreateClient();
|
||||
|
||||
virtual INotificationNetworkClient* Connect(const char* address, uint16 port);
|
||||
|
||||
virtual size_t GetConnectionCount(const char* channelName);
|
||||
|
||||
virtual void Update();
|
||||
|
||||
virtual bool ListenerBind(const char* channelName, INotificationNetworkListener* pListener);
|
||||
virtual bool ListenerRemove(INotificationNetworkListener* pListener);
|
||||
|
||||
virtual uint32 Send(const char* channelName, const void* pBuffer, size_t length);
|
||||
|
||||
private:
|
||||
AZSOCKET m_socket;
|
||||
|
||||
std::vector<CConnection*> m_connections;
|
||||
std::vector<NotificationNetwork::CClient*> m_clients;
|
||||
NotificationNetwork::CListeners m_listeners;
|
||||
|
||||
CryCriticalSection m_clientsCriticalSection;
|
||||
};
|
||||
|
||||
#endif // CRYINCLUDE_CRYSYSTEM_NOTIFICATIONNETWORK_H
|
||||
@@ -1,134 +0,0 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
// Original file Copyright Crytek GMBH or its affiliates, used under license.
|
||||
|
||||
#include "CrySystem_precompiled.h"
|
||||
#include "ProfileLogSystem.h"
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// class CLogElement
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
CLogElement::CLogElement()
|
||||
: m_pParent (NULL)
|
||||
, m_time (0)
|
||||
{
|
||||
}
|
||||
|
||||
CLogElement::CLogElement(CLogElement* pParent)
|
||||
: m_pParent (pParent)
|
||||
, m_time (0)
|
||||
{
|
||||
}
|
||||
|
||||
CLogElement::CLogElement(CLogElement* pParent, const char* name, const char* message)
|
||||
: m_pParent (pParent)
|
||||
, m_strName (name)
|
||||
, m_strMessage(message)
|
||||
, m_time (0)
|
||||
{
|
||||
}
|
||||
|
||||
void CLogElement::Flush(stack_string& indent)
|
||||
{
|
||||
if (m_logElements.empty())
|
||||
{
|
||||
CryLog("%s%s [%.3f ms] %s", indent.c_str(), m_strName.c_str(), m_time, m_strMessage.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
CryLog("%s+%s [%.3f ms] %s", indent.c_str(), m_strName.c_str(), m_time, m_strMessage.c_str());
|
||||
|
||||
indent += " ";
|
||||
for (std::list<CLogElement>::iterator it = m_logElements.begin(); it != m_logElements.end(); ++it)
|
||||
{
|
||||
(*it).Flush(indent);
|
||||
}
|
||||
indent.erase(0, 2);
|
||||
|
||||
CryLog("%s-%s", indent.c_str(), m_strName.c_str());
|
||||
}
|
||||
|
||||
ILogElement* CLogElement::Log(const char* name, const char* message)
|
||||
{
|
||||
m_logElements.push_back(CLogElement(this));
|
||||
m_logElements.back().m_strName = name;
|
||||
m_logElements.back().m_strMessage = message;
|
||||
|
||||
return &m_logElements.back();
|
||||
}
|
||||
|
||||
ILogElement* CLogElement::SetTime(float time)
|
||||
{
|
||||
m_time = time;
|
||||
|
||||
return m_pParent;
|
||||
}
|
||||
|
||||
void CLogElement::Clear()
|
||||
{
|
||||
m_logElements.resize(0);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// class CProfileLogSystem
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
CProfileLogSystem::CProfileLogSystem()
|
||||
: m_rootElelent(NULL)
|
||||
, m_pLastElelent(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
CProfileLogSystem::~CProfileLogSystem()
|
||||
{
|
||||
}
|
||||
|
||||
ILogElement* CProfileLogSystem::Log(const char* name, const char* message)
|
||||
{
|
||||
if (m_pLastElelent)
|
||||
{
|
||||
m_pLastElelent = m_pLastElelent->Log(name, message);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_rootElelent.Clear();
|
||||
m_rootElelent.SetName(name);
|
||||
m_rootElelent.SetMessage(message);
|
||||
m_pLastElelent = &m_rootElelent;
|
||||
}
|
||||
|
||||
return m_pLastElelent;
|
||||
}
|
||||
|
||||
void CProfileLogSystem::SetTime(ILogElement* pElement, float time)
|
||||
{
|
||||
if (pElement == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_pLastElelent = pElement->SetTime(time);
|
||||
if (m_pLastElelent)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
stack_string indent;
|
||||
m_rootElelent.Flush(indent);
|
||||
m_rootElelent.Clear();
|
||||
}
|
||||
|
||||
void CProfileLogSystem::Release()
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
// Original file Copyright Crytek GMBH or its affiliates, used under license.
|
||||
|
||||
// Description : Implementation of the IProfileLogSystem interface, which is used to
|
||||
// save hierarchical log with SHierProfileLogItem
|
||||
|
||||
|
||||
#ifndef CRYINCLUDE_CRYSYSTEM_PROFILELOGSYSTEM_H
|
||||
#define CRYINCLUDE_CRYSYSTEM_PROFILELOGSYSTEM_H
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ProfileLog.h"
|
||||
|
||||
class CLogElement
|
||||
: public ILogElement
|
||||
{
|
||||
public:
|
||||
CLogElement();
|
||||
CLogElement(CLogElement* pParent);
|
||||
CLogElement(CLogElement* pParent, const char* name, const char* message);
|
||||
|
||||
virtual ILogElement* Log (const char* name, const char* message);
|
||||
virtual ILogElement* SetTime (float time);
|
||||
virtual void Flush (stack_string& indent);
|
||||
|
||||
void Clear ();
|
||||
|
||||
inline void SetName(const char* name)
|
||||
{
|
||||
m_strName = name;
|
||||
}
|
||||
|
||||
inline void SetMessage(const char* message)
|
||||
{
|
||||
m_strMessage = message;
|
||||
}
|
||||
|
||||
private:
|
||||
string m_strName;
|
||||
string m_strMessage;
|
||||
float m_time; // milliSeconds
|
||||
|
||||
CLogElement* m_pParent;
|
||||
std::list<CLogElement> m_logElements;
|
||||
};
|
||||
|
||||
class CProfileLogSystem
|
||||
: public IProfileLogSystem
|
||||
{
|
||||
public:
|
||||
CProfileLogSystem();
|
||||
~CProfileLogSystem();
|
||||
|
||||
virtual ILogElement* Log (const char* name, const char* message);
|
||||
virtual void SetTime (ILogElement* pElement, float time);
|
||||
virtual void Release ();
|
||||
|
||||
private:
|
||||
CLogElement m_rootElelent;
|
||||
ILogElement* m_pLastElelent;
|
||||
};
|
||||
|
||||
#endif // CRYINCLUDE_CRYSYSTEM_PROFILELOGSYSTEM_H
|
||||
@@ -1,191 +0,0 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
// Original file Copyright Crytek GMBH or its affiliates, used under license.
|
||||
|
||||
// Description : Remote command system implementation
|
||||
|
||||
#include "CrySystem_precompiled.h"
|
||||
#include "IServiceNetwork.h"
|
||||
#include "RemoteCommand.h"
|
||||
#include "RemoteCommandHelpers.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// remote system internal logging
|
||||
#ifdef RELEASE
|
||||
#define LOG_VERBOSE(level, txt, ...)
|
||||
#else
|
||||
#define LOG_VERBOSE(level, txt, ...) if (GetManager()->CheckVerbose(level)) { GetManager()->Log(txt, __VA_ARGS__); }
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
CRemoteCommandManager::CRemoteCommandManager()
|
||||
{
|
||||
// Create the CVAR
|
||||
m_pVerboseLevel = gEnv->pConsole->RegisterInt("rc_debugVerboseLevel", 0, VF_DEV_ONLY);
|
||||
}
|
||||
|
||||
CRemoteCommandManager::~CRemoteCommandManager()
|
||||
{
|
||||
// Release the CVar
|
||||
if (NULL != m_pVerboseLevel)
|
||||
{
|
||||
m_pVerboseLevel->Release();
|
||||
m_pVerboseLevel = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
IRemoteCommandServer* CRemoteCommandManager::CreateServer(uint16 localPort)
|
||||
{
|
||||
// Create the listener
|
||||
IServiceNetworkListener* listener = gEnv->pServiceNetwork->CreateListener(localPort);
|
||||
if (NULL == listener)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Create the wrapper
|
||||
return new CRemoteCommandServer(this, listener);
|
||||
}
|
||||
|
||||
IRemoteCommandClient* CRemoteCommandManager::CreateClient()
|
||||
{
|
||||
// Create the wrapper
|
||||
return new CRemoteCommandClient(this);
|
||||
}
|
||||
|
||||
void CRemoteCommandManager::RegisterCommandClass(IRemoteCommandClass& commandClass)
|
||||
{
|
||||
// Make sure command class is not already registered
|
||||
const string& className(commandClass.GetName());
|
||||
TClassMap::const_iterator it = m_pClasses.find(className);
|
||||
if (it != m_pClasses.end())
|
||||
{
|
||||
LOG_VERBOSE(1, "Class '%s' is already registered",
|
||||
className.c_str());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const uint32 classID = m_pClassesByID.size();
|
||||
m_pClassesByID.push_back(&commandClass);
|
||||
m_pClassesMap[ className ] = classID;
|
||||
m_pClasses[ className ] = &commandClass;
|
||||
|
||||
// Verbose
|
||||
LOG_VERBOSE(1, "Registered command class '%s' with id %d",
|
||||
className.c_str(),
|
||||
classID);
|
||||
}
|
||||
|
||||
#ifndef RELEASE
|
||||
bool CRemoteCommandManager::CheckVerbose(const uint32 level) const
|
||||
{
|
||||
const int verboseLevel = m_pVerboseLevel->GetIVal();
|
||||
return (int)level < verboseLevel;
|
||||
}
|
||||
|
||||
void CRemoteCommandManager::Log(const char* txt, ...) const
|
||||
{
|
||||
// format the print buffer
|
||||
char buffer[512];
|
||||
va_list ap;
|
||||
va_start(ap, txt);
|
||||
vsprintf_s(buffer, sizeof(buffer), txt, ap);
|
||||
va_end(ap);
|
||||
|
||||
// pass to log
|
||||
gEnv->pLog->LogAlways(buffer);
|
||||
}
|
||||
#endif
|
||||
|
||||
void CRemoteCommandManager::BuildClassMapping(const std::vector<string>& classNames, std::vector< IRemoteCommandClass* >& outClasses)
|
||||
{
|
||||
LOG_VERBOSE(3, "Building class mapping for %d classes",
|
||||
classNames.size());
|
||||
|
||||
// Output list size has the same size as class names array
|
||||
const uint32 numClasses = classNames.size();
|
||||
outClasses.resize(numClasses);
|
||||
|
||||
// Match the classes
|
||||
for (size_t i = 0; i < numClasses; ++i)
|
||||
{
|
||||
// Find the matching class
|
||||
const string& className = classNames[i];
|
||||
TClassMap::const_iterator it = m_pClasses.find(className);
|
||||
if (it != m_pClasses.end())
|
||||
{
|
||||
CRY_ASSERT(className == it->second->GetName());
|
||||
CRY_ASSERT(it->second != NULL);
|
||||
outClasses[i] = it->second;
|
||||
|
||||
// Report class mapping in heavy verbose mode
|
||||
LOG_VERBOSE(3, "Class[%d] = %s",
|
||||
i,
|
||||
className.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
outClasses[i] = NULL;
|
||||
|
||||
// Class not mapped (this can cause errors)
|
||||
LOG_VERBOSE(0, "Remote command class '%s' not found on this machine",
|
||||
className.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CRemoteCommandManager::SetVerbosityLevel(const uint32 level)
|
||||
{
|
||||
// propagate the value to CVar (so it is consistent across the engine)
|
||||
if (NULL != m_pVerboseLevel)
|
||||
{
|
||||
m_pVerboseLevel->Set((int)level);
|
||||
}
|
||||
}
|
||||
|
||||
void CRemoteCommandManager::GetClassList(std::vector<string>& outClassNames) const
|
||||
{
|
||||
const uint32 numClasses = m_pClassesByID.size();
|
||||
outClassNames.resize(numClasses);
|
||||
for (size_t id = 0; id < numClasses; ++id)
|
||||
{
|
||||
IRemoteCommandClass* theClass = m_pClassesByID[id];
|
||||
if (NULL != theClass)
|
||||
{
|
||||
outClassNames[id] = theClass->GetName();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool CRemoteCommandManager::FindClassId(IRemoteCommandClass* commandClass, uint32& outClassId) const
|
||||
{
|
||||
// Local search (linear, slower)
|
||||
TClassIDMap::const_iterator it = m_pClassesMap.find(commandClass->GetName());
|
||||
if (it != m_pClassesMap.end())
|
||||
{
|
||||
outClassId = it->second;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Not found
|
||||
return false;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// Do not remove (can mess up the uber file builds)
|
||||
#undef LOG_VERBOSE
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@@ -1,459 +0,0 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
// Original file Copyright Crytek GMBH or its affiliates, used under license.
|
||||
|
||||
// Description : Remote command system implementation
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "IServiceNetwork.h"
|
||||
#include "IRemoteCommand.h"
|
||||
#include "CryThread.h"
|
||||
|
||||
class CRemoteCommandManager;
|
||||
|
||||
// Remote command client implementation
|
||||
class CRemoteCommandClient
|
||||
: public IRemoteCommandClient
|
||||
, public CryRunnable
|
||||
{
|
||||
protected:
|
||||
//-------------------------------------------------------------
|
||||
|
||||
class Command
|
||||
{
|
||||
public:
|
||||
ILINE IServiceNetworkMessage* GetMessage() const
|
||||
{
|
||||
return m_pMessage;
|
||||
}
|
||||
|
||||
ILINE uint32 GetCommandId() const
|
||||
{
|
||||
return m_id;
|
||||
}
|
||||
|
||||
public:
|
||||
// Create command data from serializing a remote command object
|
||||
static Command* Compile(const IRemoteCommand& cmd, const uint32 commandId, const uint32 classId);
|
||||
|
||||
void AddRef();
|
||||
void Release();
|
||||
|
||||
private:
|
||||
Command();
|
||||
~Command();
|
||||
|
||||
volatile int m_refCount;
|
||||
uint32 m_id;
|
||||
const char* m_szClassName; // debug only
|
||||
IServiceNetworkMessage* m_pMessage;
|
||||
};
|
||||
|
||||
//-------------------------------------------------------------
|
||||
|
||||
// Local connection reference to command
|
||||
// NOTE: pCommand is reference counted from the calling code
|
||||
struct CommandRef
|
||||
{
|
||||
Command* m_pCommand;
|
||||
uint64 m_lastSentTime;
|
||||
|
||||
ILINE CommandRef()
|
||||
: m_pCommand(NULL)
|
||||
, m_lastSentTime(0)
|
||||
{}
|
||||
|
||||
ILINE CommandRef(Command* pCommand)
|
||||
: m_pCommand(pCommand)
|
||||
, m_lastSentTime(0)
|
||||
{}
|
||||
|
||||
// Order function for set container (we want to keep the commands sorted by ID)
|
||||
static ILINE bool CompareCommandRefs(CommandRef* const& a, CommandRef* const& b)
|
||||
{
|
||||
return a->m_pCommand->GetCommandId() < b->m_pCommand->GetCommandId();
|
||||
}
|
||||
};
|
||||
|
||||
//-------------------------------------------------------------
|
||||
|
||||
// Remote server connection wrapper
|
||||
class Connection
|
||||
: public IRemoteCommandConnection
|
||||
{
|
||||
// How many commands we can send upfront before waiting for an ACK
|
||||
static const uint32 kCommandSendLead = 50;
|
||||
|
||||
// How much command data can be merged into a single packet (KB)
|
||||
static const uint32 kCommandMaxMergePacketSize = 1024;
|
||||
|
||||
// Time after which we start resending commands (ms)
|
||||
static const uint32 kCommandResendTime = 2000;
|
||||
|
||||
protected:
|
||||
CRemoteCommandManager* m_pManager;
|
||||
volatile int m_refCount;
|
||||
|
||||
// Connection (from service network layer)
|
||||
IServiceNetworkConnection* m_pConnection;
|
||||
|
||||
// Cached address of the remote endpoint
|
||||
ServiceNetworkAddress m_remoteAddress;
|
||||
|
||||
// Pending commands, they are kept ed here until they are ACKed as executed by server
|
||||
typedef std::vector<CommandRef*> TCommands;
|
||||
TCommands m_pCommands;
|
||||
CryMutex m_commandAccessMutex;
|
||||
|
||||
// A queue of raw messages
|
||||
typedef CryMT::CLocklessPointerQueue<IServiceNetworkMessage> TRawMessageQueue;
|
||||
TRawMessageQueue m_pRawMessages;
|
||||
CryMutex m_rawMessagesMutex;
|
||||
|
||||
// Last command that was ACKed as received by server
|
||||
// This is used to synchronize the both ends of the pipeline
|
||||
uint32 m_lastReceivedCommand;
|
||||
|
||||
// Last command that was ACKed as executed by server
|
||||
// This is used to synchronize the both ends of the pipeline
|
||||
uint32 m_lastExecutedCommand;
|
||||
|
||||
public:
|
||||
ILINE CRemoteCommandManager* GetManager() const
|
||||
{
|
||||
return m_pManager;
|
||||
}
|
||||
|
||||
public:
|
||||
Connection(CRemoteCommandManager* pManager, IServiceNetworkConnection* pConnection, uint32 currentCommandId);
|
||||
|
||||
// Add command to sending queue in this connection
|
||||
void AddToSendQueue(Command* pCommand);
|
||||
|
||||
// Process the communication, returns false if connection should be deleted
|
||||
bool Update();
|
||||
|
||||
// Send the "disconnect" message to the remote side therefore gracefully closing the connection.
|
||||
void SendDisconnectMessage();
|
||||
|
||||
public:
|
||||
// IRemoteCommandConnection interface implementation
|
||||
virtual bool IsAlive() const;
|
||||
virtual const ServiceNetworkAddress& GetRemoteAddress() const;
|
||||
virtual bool SendRawMessage(IServiceNetworkMessage* pMessage);
|
||||
virtual IServiceNetworkMessage* ReceiveRawMessage();
|
||||
virtual void Close(bool bFlushQueueBeforeClosing = false);
|
||||
virtual void AddRef();
|
||||
virtual void Release();
|
||||
|
||||
private:
|
||||
~Connection();
|
||||
};
|
||||
|
||||
protected:
|
||||
CRemoteCommandManager* m_pManager;
|
||||
|
||||
typedef std::vector<Connection*> TConnections;
|
||||
TConnections m_pConnections;
|
||||
TConnections m_pConnectionsToDelete;
|
||||
CryMutex m_accessMutex;
|
||||
|
||||
// Local command ID counter, incremented atomically using CryInterlockedIncrement
|
||||
volatile uint32 m_commandId;
|
||||
|
||||
typedef CryThread<CRemoteCommandClient> TRemoteClientThread;
|
||||
TRemoteClientThread* m_pThread;
|
||||
CryEvent m_threadEvent;
|
||||
bool m_bCloseThread;
|
||||
|
||||
public:
|
||||
ILINE CRemoteCommandManager* GetManager() const
|
||||
{
|
||||
return m_pManager;
|
||||
}
|
||||
|
||||
public:
|
||||
CRemoteCommandClient(CRemoteCommandManager* pManager);
|
||||
virtual ~CRemoteCommandClient();
|
||||
|
||||
// IRemoteCommandClient interface
|
||||
virtual void Delete();
|
||||
virtual bool Schedule(const IRemoteCommand& command);
|
||||
virtual IRemoteCommandConnection* ConnectToServer(const class ServiceNetworkAddress& serverAddress);
|
||||
|
||||
// CryRunnable interface implementation
|
||||
virtual void Run();
|
||||
virtual void Cancel();
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// Remote command server implementation
|
||||
class CRemoteCommandServer
|
||||
: public IRemoteCommandServer
|
||||
, public CryRunnable
|
||||
{
|
||||
#if defined(AZ_RESTRICTED_PLATFORM)
|
||||
#include AZ_RESTRICTED_FILE(RemoteCommand_h)
|
||||
#endif
|
||||
|
||||
protected:
|
||||
// Wrapped commands
|
||||
class WrappedCommand
|
||||
{
|
||||
private:
|
||||
IRemoteCommand* m_pCommand;
|
||||
volatile int m_refCount;
|
||||
uint32 m_commandID;
|
||||
|
||||
public:
|
||||
ILINE const uint32 GetId() const
|
||||
{
|
||||
return m_commandID;
|
||||
}
|
||||
|
||||
ILINE IRemoteCommand* GetCommand() const
|
||||
{
|
||||
return m_pCommand;
|
||||
}
|
||||
|
||||
public:
|
||||
WrappedCommand(IRemoteCommand* pCommand, const uint32 commandId);
|
||||
void AddRef();
|
||||
void Release();
|
||||
|
||||
private:
|
||||
~WrappedCommand();
|
||||
};
|
||||
|
||||
// Local endpoint
|
||||
class Endpoint
|
||||
{
|
||||
private:
|
||||
IServiceNetworkConnection* m_pConnection;
|
||||
class CRemoteCommandServer* m_pServer;
|
||||
CRemoteCommandManager* m_pManager;
|
||||
|
||||
// ACK counters for synchronization
|
||||
uint32 m_lastReceivedCommand;
|
||||
uint32 m_lastExecutedCommand;
|
||||
uint32 m_lastReceivedCommandACKed;
|
||||
uint32 m_lastExecutedCommandACKed;
|
||||
CryMutex m_accessLock;
|
||||
|
||||
// We have received class list (it's a valid RC connection)
|
||||
bool m_bHasReceivedClassList;
|
||||
|
||||
// Locally mapped class id (because IDs on remote side can be different than here)
|
||||
typedef std::vector< IRemoteCommandClass* > TLocalClassFactoryList;
|
||||
TLocalClassFactoryList m_pLocalClassFactories;
|
||||
|
||||
// Commands that were received and should be executed
|
||||
typedef CryMT::CLocklessPointerQueue< WrappedCommand > TCommandQueue;
|
||||
TCommandQueue m_pCommandsToExecute;
|
||||
CryMutex m_commandListLock;
|
||||
|
||||
public:
|
||||
ILINE CRemoteCommandManager* GetManager() const
|
||||
{
|
||||
return m_pManager;
|
||||
}
|
||||
|
||||
// Get the endpoint connection
|
||||
ILINE IServiceNetworkConnection* GetConnection() const
|
||||
{
|
||||
return m_pConnection;
|
||||
}
|
||||
|
||||
// Have we received a class list from the client
|
||||
ILINE bool HasReceivedClassList() const
|
||||
{
|
||||
return m_bHasReceivedClassList;
|
||||
}
|
||||
|
||||
public:
|
||||
Endpoint(CRemoteCommandManager* pManager, class CRemoteCommandServer* pServer, IServiceNetworkConnection* pConnection);
|
||||
~Endpoint();
|
||||
|
||||
// Execute pending commands (called from main thread)
|
||||
void Execute();
|
||||
|
||||
// Update (send/receive, etc) Returns false if endpoint died.
|
||||
bool Update();
|
||||
|
||||
// Get the class name as translated by this endpoint (by ID)
|
||||
const char* GetClassName(const uint32 classId) const;
|
||||
|
||||
// Create command object by class ID
|
||||
IRemoteCommand* CreateObject(const uint32 classId) const;
|
||||
};
|
||||
|
||||
// Received raw message
|
||||
// Beware to use always via pointer to this type since propper reference counting is not implemented for copy and assigment
|
||||
struct RawMessage
|
||||
{
|
||||
// We keep a reference to connection so we know where to send the response
|
||||
IServiceNetworkConnection* m_pConnection;
|
||||
IServiceNetworkMessage* m_pMessage;
|
||||
|
||||
ILINE RawMessage(IServiceNetworkConnection* pConnection, IServiceNetworkMessage* pMessage)
|
||||
: m_pConnection(pConnection)
|
||||
, m_pMessage(pMessage)
|
||||
{
|
||||
m_pMessage->AddRef();
|
||||
m_pConnection->AddRef();
|
||||
}
|
||||
|
||||
ILINE ~RawMessage()
|
||||
{
|
||||
m_pMessage->Release();
|
||||
m_pConnection->Release();
|
||||
}
|
||||
|
||||
private:
|
||||
ILINE RawMessage([[maybe_unused]] const RawMessage& other) {};
|
||||
ILINE RawMessage& operator==([[maybe_unused]] const RawMessage& other) { return *this; }
|
||||
};
|
||||
|
||||
protected:
|
||||
CRemoteCommandManager* m_pManager;
|
||||
|
||||
// Network listening socket
|
||||
IServiceNetworkListener* m_pListener;
|
||||
|
||||
// Live endpoints
|
||||
typedef std::vector<Endpoint*> TEndpoints;
|
||||
TEndpoints m_pEndpoints;
|
||||
TEndpoints m_pUpdateEndpoints;
|
||||
CryMutex m_accessLock;
|
||||
|
||||
// Endpoints that were discarded and should be deleted
|
||||
// We can delete endpoints only from the update thread
|
||||
TEndpoints m_pEndpointToDelete;
|
||||
|
||||
// Received raw messages
|
||||
typedef CryMT::CLocklessPointerQueue<RawMessage> TRawMessagesQueue;
|
||||
TRawMessagesQueue m_pRawMessages;
|
||||
CryMutex m_rawMessagesLock;
|
||||
|
||||
// Listeners for raw messages that require synchronous processing
|
||||
typedef std::vector<IRemoteCommandListenerSync*> TRawMessageListenersSync;
|
||||
TRawMessageListenersSync m_pRawListenersSync;
|
||||
|
||||
// Listeners for raw messages that can be processed asynchronously (faster path)
|
||||
typedef std::vector<IRemoteCommandListenerAsync*> TRawMessageListenersAsync;
|
||||
TRawMessageListenersAsync m_pRawListenersAsync;
|
||||
|
||||
// Command communication and deserialization is done on thread
|
||||
typedef CryThread<CRemoteCommandServer> TRemoteServerThread;
|
||||
TRemoteServerThread* m_pThread;
|
||||
|
||||
// Suppression counter (execution of commands is suppressed when>0)
|
||||
// This is updated using CryInterlocked* functions
|
||||
volatile int m_suppressionCounter;
|
||||
bool m_bIsSuppressed;
|
||||
|
||||
// Request to close the network thread
|
||||
bool m_bCloseThread;
|
||||
|
||||
public:
|
||||
ILINE CRemoteCommandManager* GetManager() const
|
||||
{
|
||||
return m_pManager;
|
||||
}
|
||||
|
||||
public:
|
||||
CRemoteCommandServer(CRemoteCommandManager* pManager, IServiceNetworkListener* pListener);
|
||||
virtual ~CRemoteCommandServer();
|
||||
|
||||
// IRemoteCommandServer interface implementation
|
||||
virtual void Delete();
|
||||
virtual void FlushCommandQueue();
|
||||
virtual void SuppressCommands();
|
||||
virtual void ResumeCommands();
|
||||
virtual void RegisterSyncMessageListener(IRemoteCommandListenerSync* pListener);
|
||||
virtual void UnregisterSyncMessageListener(IRemoteCommandListenerSync* pListener);
|
||||
virtual void RegisterAsyncMessageListener(IRemoteCommandListenerAsync* pListener);
|
||||
virtual void UnregisterAsyncMessageListener(IRemoteCommandListenerAsync* pListener);
|
||||
virtual void Broadcast(IServiceNetworkMessage* pMessage);
|
||||
virtual bool HasConnectedClients() const;
|
||||
|
||||
// CryRunnable
|
||||
virtual void Run();
|
||||
virtual void Cancel();
|
||||
|
||||
protected:
|
||||
void ProcessRawMessageAsync(IServiceNetworkMessage* pMessage, IServiceNetworkConnection* pConnection);
|
||||
void ProcessRawMessagesSync();
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// Remote command manager implementation
|
||||
class CRemoteCommandManager
|
||||
: public IRemoteCommandManager
|
||||
{
|
||||
public:
|
||||
CRemoteCommandManager();
|
||||
virtual ~CRemoteCommandManager();
|
||||
|
||||
// IRemoteCommandManager interface implementation
|
||||
virtual void SetVerbosityLevel(const uint32 level);
|
||||
virtual IRemoteCommandServer* CreateServer(uint16 localPort);
|
||||
virtual IRemoteCommandClient* CreateClient();
|
||||
virtual void RegisterCommandClass(IRemoteCommandClass& commandClass);
|
||||
|
||||
// Debug print
|
||||
#ifdef RELEASE
|
||||
void Log([[maybe_unused]] const char* txt, ...) const {};
|
||||
bool CheckVerbose([[maybe_unused]] const uint32 level) const { return false; }
|
||||
#else
|
||||
void Log(const char* txt, ...) const;
|
||||
bool CheckVerbose(const uint32 level) const;
|
||||
#endif
|
||||
|
||||
// Build ID->Class Factory mapping given the class name list, will report errors to the log.
|
||||
void BuildClassMapping(const std::vector<string>& classNames, std::vector< IRemoteCommandClass* >& outClasses);
|
||||
|
||||
// Get list of class names (in order of their IDs)
|
||||
void GetClassList(std::vector<string>& outClassNames) const;
|
||||
|
||||
// Find class ID for given class, returns false if not found
|
||||
bool FindClassId(IRemoteCommandClass* commandClass, uint32& outClassId) const;
|
||||
|
||||
public:
|
||||
ILINE CRemoteCommandManager* GetManager()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
private:
|
||||
// Class name mapping
|
||||
typedef std::map< string, IRemoteCommandClass* > TClassMap;
|
||||
TClassMap m_pClasses;
|
||||
|
||||
// Class ID lookup
|
||||
typedef std::vector< IRemoteCommandClass* > TClassIDList;
|
||||
TClassIDList m_pClassesByID;
|
||||
|
||||
// Class ID mapping
|
||||
typedef std::map< string, int > TClassIDMap;
|
||||
TClassIDMap m_pClassesMap;
|
||||
|
||||
// Verbose level
|
||||
ICVar* m_pVerboseLevel;
|
||||
};
|
||||
@@ -1,756 +0,0 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
// Original file Copyright Crytek GMBH or its affiliates, used under license.
|
||||
|
||||
// Description : Remote command system implementation
|
||||
|
||||
#include "CrySystem_precompiled.h"
|
||||
#include "IServiceNetwork.h"
|
||||
#include "RemoteCommand.h"
|
||||
#include "RemoteCommandHelpers.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// remote system internal logging
|
||||
#ifdef RELEASE
|
||||
#define LOG_VERBOSE(level, txt, ...)
|
||||
#else
|
||||
#define LOG_VERBOSE(level, txt, ...) if (GetManager()->CheckVerbose(level)) { GetManager()->Log(txt, __VA_ARGS__); }
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
CRemoteCommandClient::Command::Command()
|
||||
: m_refCount(1)
|
||||
, m_szClassName(NULL)
|
||||
, m_id(0)
|
||||
{
|
||||
}
|
||||
|
||||
CRemoteCommandClient::Command::~Command()
|
||||
{
|
||||
// Release message buffer with compiled command data
|
||||
if (m_pMessage != NULL)
|
||||
{
|
||||
m_pMessage->Release();
|
||||
m_pMessage = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
CRemoteCommandClient::Command* CRemoteCommandClient::Command::Compile(const IRemoteCommand& cmd, const uint32 commandId, const uint32 classId)
|
||||
{
|
||||
// Build command header
|
||||
CommandHeader header;
|
||||
header.classId = classId;
|
||||
header.commandId = commandId;
|
||||
header.size = 0; // not known yet
|
||||
|
||||
// Output stream builder
|
||||
CDataWriteStreamBuffer writer;
|
||||
|
||||
// Start the packet with a command header (it will be later overwritten)
|
||||
writer << header;
|
||||
|
||||
// Serialize command header and data
|
||||
const uint32 commandDataStart = writer.GetSize();
|
||||
cmd.SaveToStream(writer);
|
||||
const uint32 commandDataEnd = writer.GetSize();
|
||||
|
||||
// Extract a message from the stream
|
||||
IServiceNetworkMessage* pMessage = writer.BuildMessage();
|
||||
if (NULL == pMessage)
|
||||
{
|
||||
// No message was generated (for some reason)
|
||||
// Do not allow this command to compile
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Rewrite header with the proper command size
|
||||
// This is a little bit over-the-top because it uses another serializer created
|
||||
// on top of the message buffer. The advantage is that we have the endianess problem abstracted away.
|
||||
// TODO: consider writing the size directly
|
||||
{
|
||||
// update header with popper data size
|
||||
const uint32 dataSize = commandDataEnd - commandDataStart;
|
||||
header.size = dataSize;
|
||||
|
||||
// rewrite the header in existing message
|
||||
CDataWriteStreamToMessage inPlaceWriter(pMessage);
|
||||
inPlaceWriter << header;
|
||||
}
|
||||
|
||||
// Create command wrapper
|
||||
Command* pCommand = new Command();
|
||||
pCommand->m_id = commandId;
|
||||
pCommand->m_szClassName = cmd.GetClass()->GetName();
|
||||
pCommand->m_pMessage = pMessage;
|
||||
return pCommand;
|
||||
}
|
||||
|
||||
void CRemoteCommandClient::Command::AddRef()
|
||||
{
|
||||
CryInterlockedIncrement(&m_refCount);
|
||||
}
|
||||
|
||||
void CRemoteCommandClient::Command::Release()
|
||||
{
|
||||
if (0 == CryInterlockedDecrement(&m_refCount))
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
CRemoteCommandClient::Connection::Connection(CRemoteCommandManager* pManager, IServiceNetworkConnection* pConnection, uint32 currentCommandId)
|
||||
: m_pConnection(pConnection)
|
||||
, m_pManager(pManager)
|
||||
, m_lastReceivedCommand(currentCommandId)
|
||||
, m_lastExecutedCommand(currentCommandId)
|
||||
, m_remoteAddress(pConnection->GetRemoteAddress())
|
||||
, m_refCount(1)
|
||||
{
|
||||
// The first thing to do after the connection is initialized is to
|
||||
// send the message with list of classes supported by this side.
|
||||
{
|
||||
// Write the header
|
||||
PackedHeader header;
|
||||
header.magic = PackedHeader::kMagic;
|
||||
header.msgType = PackedHeader::eCommand_ClassList;
|
||||
header.count = currentCommandId; // send the intial command ID so we can be in sync
|
||||
|
||||
// Get the class list for our local remote command manager
|
||||
std::vector< string > classList;
|
||||
GetManager()->GetClassList(classList);
|
||||
|
||||
// Write the message
|
||||
CDataWriteStreamBuffer writer;
|
||||
writer << header;
|
||||
writer << classList;
|
||||
|
||||
// Send the message to the remote side
|
||||
IServiceNetworkMessage* pMsg = writer.BuildMessage();
|
||||
if (NULL != pMsg)
|
||||
{
|
||||
LOG_VERBOSE(1, "Sent class list message (%d classes, size=%d) to '%s'",
|
||||
classList.size(),
|
||||
pMsg->GetSize(),
|
||||
m_pConnection->GetRemoteAddress().ToString().c_str());
|
||||
|
||||
// TODO: well, there is no reason this can fail since the connection is brand new, but...
|
||||
// We still relay on the service network to deliver this message unharmed.
|
||||
m_pConnection->SendMsg(pMsg);
|
||||
|
||||
// cleanup
|
||||
pMsg->Release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CRemoteCommandClient::Connection::~Connection()
|
||||
{
|
||||
// Close the connection
|
||||
const bool bFlushBeforeClosing = false;
|
||||
Close(bFlushBeforeClosing);
|
||||
|
||||
// Release any commands left over on the list
|
||||
for (TCommands::const_iterator it = m_pCommands.begin();
|
||||
it != m_pCommands.end(); ++it)
|
||||
{
|
||||
(*it)->m_pCommand->Release();
|
||||
delete (*it);
|
||||
}
|
||||
m_pCommands.clear();
|
||||
|
||||
// Release all of the raw messages that were not picked up
|
||||
while (!m_pRawMessages.empty())
|
||||
{
|
||||
IServiceNetworkMessage* pMessage = m_pRawMessages.pop();
|
||||
pMessage->Release();
|
||||
}
|
||||
|
||||
// Release the connection object
|
||||
SAFE_RELEASE(m_pConnection);
|
||||
}
|
||||
|
||||
void CRemoteCommandClient::Connection::SendDisconnectMessage()
|
||||
{
|
||||
if (NULL != m_pConnection && m_pConnection->IsAlive())
|
||||
{
|
||||
IDataWriteStream* pWriter = gEnv->pServiceNetwork->CreateMessageWriter();
|
||||
if (NULL != pWriter)
|
||||
{
|
||||
// write header to message
|
||||
PackedHeader header;
|
||||
header.magic = PackedHeader::kMagic;
|
||||
header.count = 0;
|
||||
header.msgType = PackedHeader::eCommand_Disconnect;
|
||||
*pWriter << header;
|
||||
|
||||
// Send the disconnect signal
|
||||
IServiceNetworkMessage* pMessage = pWriter->BuildMessage();
|
||||
if (NULL != pMessage)
|
||||
{
|
||||
m_pConnection->SendMsg(pMessage);
|
||||
pMessage->Release();
|
||||
}
|
||||
|
||||
pWriter->Delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CRemoteCommandClient::Connection::AddToSendQueue(Command* pCommand)
|
||||
{
|
||||
// Do not add commands if the connection is closed
|
||||
if (m_pConnection == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Add command to local list
|
||||
// NOTE: this list always needs to be sorted in increasing command ID for various optimization reason.
|
||||
// This is achieved by resorting after pushing each element. Usually the cost of this is close to nothing
|
||||
// because incoming commands tend to be added with increasing command IDs.
|
||||
// The only case when something else can happen is when commands are added from different threads
|
||||
// and the one that was lower CommandID took longer to serialize and therefore is added later.
|
||||
// Anyway, this case is handled here.
|
||||
{
|
||||
CryAutoLock<CryMutex> lock(m_commandAccessMutex);
|
||||
|
||||
// Always add to the end (don't try to guess position)
|
||||
// TODO: consider binary search
|
||||
m_pCommands.push_back(new CommandRef(pCommand));
|
||||
|
||||
// Resort, NODE: This usually does not sort anything because the vector is already sorted
|
||||
std::sort(m_pCommands.begin(), m_pCommands.end(), CommandRef::CompareCommandRefs);
|
||||
}
|
||||
|
||||
// Keep local reference to command (since we added it to our array)
|
||||
pCommand->AddRef();
|
||||
}
|
||||
|
||||
bool CRemoteCommandClient::Connection::Update()
|
||||
{
|
||||
// If the network connection got dead we should close this one to
|
||||
if ((NULL == m_pConnection) || !m_pConnection->IsAlive())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Receive ACKs first so we have better view of what to send
|
||||
uint32 newLastExecutedCommand = m_lastExecutedCommand;
|
||||
uint32 newLastReceivedCommand = m_lastReceivedCommand;
|
||||
IServiceNetworkMessage* pMsg = m_pConnection->ReceiveMsg();
|
||||
while (pMsg != NULL)
|
||||
{
|
||||
// Deserialize the message
|
||||
{
|
||||
CDataReadStreamFormMessage reader(pMsg);
|
||||
ResponseHeader response;
|
||||
reader << response;
|
||||
|
||||
// is this proper command system message ?
|
||||
if (response.magic == PackedHeader::kMagic)
|
||||
{
|
||||
if (response.msgType == PackedHeader::eCommand_ACK)
|
||||
{
|
||||
// Update internal ACK values
|
||||
// This code supports getting the ACK messages out of order.
|
||||
newLastExecutedCommand = max<uint32>(newLastExecutedCommand, response.lastCommandExecuted);
|
||||
newLastReceivedCommand = max<uint32>(newLastReceivedCommand, response.lastCommandReceived);
|
||||
|
||||
LOG_VERBOSE(3, "ACK (rcv=%d, exe=%d) received from '%s'",
|
||||
response.lastCommandReceived,
|
||||
response.lastCommandExecuted,
|
||||
m_pConnection->GetRemoteAddress().ToString().c_str());
|
||||
}
|
||||
else if (response.msgType == PackedHeader::eCommand_Disconnect)
|
||||
{
|
||||
// Disconnect request was received
|
||||
LOG_VERBOSE(3, "DISCONNECT (rcv=%d, exe=%d) received from '%s'",
|
||||
response.lastCommandReceived,
|
||||
response.lastCommandExecuted,
|
||||
m_pConnection->GetRemoteAddress().ToString().c_str());
|
||||
|
||||
// Close connection
|
||||
m_pConnection->Close();
|
||||
m_pConnection->Release();
|
||||
m_pConnection = NULL;
|
||||
|
||||
// release the message
|
||||
pMsg->Release();
|
||||
|
||||
// Signal manager to delete this object
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Keep an extra reference for the message in the raw message list
|
||||
pMsg->AddRef();
|
||||
|
||||
// Assume it's a raw message, add it to the raw list
|
||||
m_pRawMessages.push(pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
// Release message data
|
||||
pMsg->Release();
|
||||
|
||||
// Get next message from the network
|
||||
pMsg = m_pConnection->ReceiveMsg();
|
||||
}
|
||||
|
||||
// ACK was updated
|
||||
if ((newLastExecutedCommand != m_lastExecutedCommand) ||
|
||||
(newLastReceivedCommand != m_lastReceivedCommand))
|
||||
{
|
||||
m_lastExecutedCommand = newLastExecutedCommand;
|
||||
m_lastReceivedCommand = newLastReceivedCommand;
|
||||
|
||||
// Drop commands that were ACKed as received (server has them and they will be executed soon)
|
||||
{
|
||||
CryAutoLock<CryMutex> lock(m_commandAccessMutex);
|
||||
|
||||
// we use this to count how many elements we need to remove later from the command vector
|
||||
uint32 numCommandsToDelete = 0;
|
||||
|
||||
for (TCommands::const_iterator it = m_pCommands.begin();
|
||||
it != m_pCommands.end(); ++it)
|
||||
{
|
||||
CommandRef* cmdRef = *it;
|
||||
|
||||
// Command is still needed because it was not yet received by the remote part
|
||||
if (cmdRef->m_pCommand->GetCommandId() > newLastReceivedCommand)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// Drop the command data
|
||||
cmdRef->m_pCommand->Release();
|
||||
delete cmdRef;
|
||||
|
||||
++numCommandsToDelete;
|
||||
}
|
||||
|
||||
// Erase the command slots in the vector (in one batch)
|
||||
if (numCommandsToDelete > 0)
|
||||
{
|
||||
m_pCommands.erase(m_pCommands.begin(), m_pCommands.begin() + numCommandsToDelete);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// (Re)Send the commands
|
||||
{
|
||||
// Calculate the maximum command ID we can send, this depends on
|
||||
// the last command that was ACKed as executed on the remote side.
|
||||
// This effectively throttles the communication and prevents the
|
||||
// situation when remote side is flooded with unprocessed commands.
|
||||
// NOTE: the time when command is executed is different to the
|
||||
// time that command is received. Sometimes if the server is suppressed (level loading)
|
||||
// it can take a long time before commands begin to execute.
|
||||
const uint32 maxCommandIdToSend = m_lastExecutedCommand + kCommandSendLead;
|
||||
|
||||
// Calculate the cutoff time for sending (all commands that were not send before this time will be sent again)
|
||||
// This assumes that the last sent time for new commands is 0 (so they will always got sent the first time)
|
||||
// This situation can only happen due to the network failure since RemoteCommand layer does not require the commands to be resent.
|
||||
const uint64 currentTime = gEnv->pTimer->GetAsyncTime().GetMilliSecondsAsInt64();
|
||||
const uint64 cutoffTime = currentTime - kCommandResendTime;
|
||||
|
||||
std::vector< CommandRef* > commandsInPacket; // temp array
|
||||
|
||||
// Process until we send all that there is to send
|
||||
for (;; )
|
||||
{
|
||||
// When sending connections try to merge them in larger packets.
|
||||
// NOTE: this should not impact delivery time since we are not waiting
|
||||
// for pending commands to accumulate before sending them, it's just an optimization
|
||||
// to prevent may small messages from being sent.
|
||||
uint32 packetDataSizeSoFar = 0;
|
||||
{
|
||||
CryAutoLock<CryMutex> lock(m_commandAccessMutex);
|
||||
|
||||
// fast local clear
|
||||
// TODO: do we have a good template alternative to temporary array on stack?
|
||||
packetDataSizeSoFar = 0;
|
||||
commandsInPacket.resize(0);
|
||||
|
||||
for (TCommands::iterator it = m_pCommands.begin();
|
||||
it != m_pCommands.end(); ++it)
|
||||
{
|
||||
CommandRef* commandRef = *it;
|
||||
|
||||
// this command is to new, don't send it
|
||||
if (commandRef->m_pCommand->GetCommandId() >= maxCommandIdToSend)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// should we send this command ?
|
||||
if (commandRef->m_lastSentTime < cutoffTime)
|
||||
{
|
||||
// will it fit into current packet ?
|
||||
const uint32 commandDataSize = commandRef->m_pCommand->GetMessage()->GetSize();
|
||||
if (packetDataSizeSoFar == 0 || // always add at least one command to the packet (no splitting)
|
||||
(packetDataSizeSoFar + commandDataSize < kCommandMaxMergePacketSize))
|
||||
{
|
||||
if (commandRef->m_lastSentTime == 0)
|
||||
{
|
||||
LOG_VERBOSE(3, "Command ID=%d is sent FIRST TIME to '%s'",
|
||||
commandRef->m_pCommand->GetCommandId(),
|
||||
m_pConnection->GetRemoteAddress().ToString().c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_VERBOSE(3, "Command ID=%d is resent to '%s'",
|
||||
commandRef->m_pCommand->GetCommandId(),
|
||||
m_pConnection->GetRemoteAddress().ToString().c_str());
|
||||
}
|
||||
|
||||
// will be sent
|
||||
commandsInPacket.push_back(commandRef);
|
||||
packetDataSizeSoFar += commandDataSize;
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_VERBOSE(3, "Command ID=%d is to big (%d) to fit packet size limit (%d)",
|
||||
commandRef->m_pCommand->GetCommandId(),
|
||||
commandDataSize,
|
||||
kCommandMaxMergePacketSize);
|
||||
|
||||
// no more commands will fit current packet
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// No new commands to be send
|
||||
if (commandsInPacket.empty())
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// Stats
|
||||
LOG_VERBOSE(3, "Sending %d commands in packet, total size=%d, maxID=%d, dest: %s",
|
||||
commandsInPacket.size(),
|
||||
packetDataSizeSoFar,
|
||||
maxCommandIdToSend,
|
||||
m_pConnection->GetRemoteAddress().ToString().c_str());
|
||||
|
||||
// Estimate the size of the network packet
|
||||
const uint32 messageDataSize = packetDataSizeSoFar + PackedHeader::kSerializationSize;
|
||||
|
||||
// Allocate and fill the message buffer
|
||||
IServiceNetworkMessage* pSendMsg = gEnv->pServiceNetwork->AllocMessageBuffer(messageDataSize);
|
||||
if (NULL != pSendMsg)
|
||||
{
|
||||
CDataWriteStreamToMessage writer(pSendMsg);
|
||||
|
||||
// Packet header
|
||||
PackedHeader header;
|
||||
header.magic = PackedHeader::kMagic;
|
||||
header.msgType = PackedHeader::eCommand_Command;
|
||||
header.count = commandsInPacket.size(); // number commands to send in this packet
|
||||
writer << header;
|
||||
|
||||
// Merge data of single commands
|
||||
for (size_t i = 0; i < commandsInPacket.size(); ++i)
|
||||
{
|
||||
const IServiceNetworkMessage* pCommandMsg = commandsInPacket[i]->m_pCommand->GetMessage();
|
||||
writer.Write(pCommandMsg->GetPointer(), pCommandMsg->GetSize());
|
||||
}
|
||||
|
||||
// Schedule the packet for sending via our network connection
|
||||
if (m_pConnection->SendMsg(pSendMsg))
|
||||
{
|
||||
// Only after the network layer has accepted our message we can assume that the commands were sent
|
||||
for (size_t i = 0; i < commandsInPacket.size(); ++i)
|
||||
{
|
||||
CommandRef* cmdRef = commandsInPacket[i];
|
||||
cmdRef->m_lastSentTime = currentTime;
|
||||
}
|
||||
|
||||
// Release temporary message memory
|
||||
pSendMsg->Release();
|
||||
}
|
||||
else
|
||||
{
|
||||
// We failed to send the message (possibly the send queue is full)
|
||||
pSendMsg->Release();
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// No message was created, stop sending
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Keep the connection alive
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CRemoteCommandClient::Connection::IsAlive() const
|
||||
{
|
||||
return (NULL != m_pConnection) && (m_pConnection->IsAlive());
|
||||
}
|
||||
|
||||
const ServiceNetworkAddress& CRemoteCommandClient::Connection::GetRemoteAddress() const
|
||||
{
|
||||
return m_remoteAddress;
|
||||
}
|
||||
|
||||
void CRemoteCommandClient::Connection::Close(bool bFlushQueueBeforeClosing /*= false*/)
|
||||
{
|
||||
// Close the connection
|
||||
if (NULL != m_pConnection)
|
||||
{
|
||||
if (m_pConnection->IsAlive() && bFlushQueueBeforeClosing)
|
||||
{
|
||||
// We have a chance to send a graceful disconnect message, so send it
|
||||
SendDisconnectMessage();
|
||||
|
||||
// Send all the messages from the send queue before closing this connection.
|
||||
// This does not block current thread.
|
||||
m_pConnection->FlushAndClose(IServiceNetworkConnection::kDefaultFlushTime);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Just close the connection (hasher way)
|
||||
m_pConnection->Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool CRemoteCommandClient::Connection::SendRawMessage(IServiceNetworkMessage* pMessage)
|
||||
{
|
||||
// We can send the raw messages right away
|
||||
if (NULL != m_pConnection && m_pConnection->IsAlive())
|
||||
{
|
||||
return m_pConnection->SendMsg(pMessage);
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
IServiceNetworkMessage* CRemoteCommandClient::Connection::ReceiveRawMessage()
|
||||
{
|
||||
return m_pRawMessages.pop();
|
||||
}
|
||||
|
||||
void CRemoteCommandClient::Connection::AddRef()
|
||||
{
|
||||
CryInterlockedIncrement(&m_refCount);
|
||||
}
|
||||
|
||||
void CRemoteCommandClient::Connection::Release()
|
||||
{
|
||||
if (0 == CryInterlockedDecrement(&m_refCount))
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
CRemoteCommandClient::CRemoteCommandClient(CRemoteCommandManager* pManager)
|
||||
: m_pManager(pManager)
|
||||
, m_commandId(0)
|
||||
, m_bCloseThread(false)
|
||||
{
|
||||
// Start processing thread (sending, etc)
|
||||
m_pThread = new TRemoteClientThread();
|
||||
m_pThread->Start(*this);
|
||||
}
|
||||
|
||||
CRemoteCommandClient::~CRemoteCommandClient()
|
||||
{
|
||||
// Stop the thread
|
||||
if (NULL != m_pThread)
|
||||
{
|
||||
m_pThread->Cancel();
|
||||
m_pThread->Stop();
|
||||
m_pThread->WaitForThread();
|
||||
delete m_pThread;
|
||||
}
|
||||
|
||||
// Delete connections
|
||||
for (size_t i = 0; i < m_pConnections.size(); ++i)
|
||||
{
|
||||
m_pConnections[i]->Release();
|
||||
}
|
||||
m_pConnections.clear();
|
||||
}
|
||||
|
||||
void CRemoteCommandClient::Delete()
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
IRemoteCommandConnection* CRemoteCommandClient::ConnectToServer(const class ServiceNetworkAddress& serverAddress)
|
||||
{
|
||||
CryAutoLock< CryMutex > lock(m_accessMutex);
|
||||
|
||||
// Do not connect twice to the same server
|
||||
for (TConnections::const_iterator it = m_pConnections.begin();
|
||||
it != m_pConnections.end(); ++it)
|
||||
{
|
||||
if (ServiceNetworkAddress::CompareBaseAddress((*it)->GetRemoteAddress(), serverAddress))
|
||||
{
|
||||
LOG_VERBOSE(0, "Failed to connect to server '%s': already connected",
|
||||
serverAddress.ToString().c_str());
|
||||
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// Open a network connection
|
||||
IServiceNetworkConnection* pNetConnection = gEnv->pServiceNetwork->Connect(serverAddress);
|
||||
if (NULL == pNetConnection)
|
||||
{
|
||||
LOG_VERBOSE(0, "Failed to connect to server '%s': server is not responding",
|
||||
serverAddress.ToString().c_str());
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Get current command ID (only commands after this one will be sent)
|
||||
const uint32 firstCommandId = m_commandId;
|
||||
|
||||
// Create a wrapping class and add it to the connection list
|
||||
Connection* pConnection = new Connection(GetManager(), pNetConnection, firstCommandId);
|
||||
m_pConnections.push_back(pConnection);
|
||||
|
||||
// Keep internal reference
|
||||
pConnection->AddRef();
|
||||
|
||||
LOG_VERBOSE(0, "Connected to remote command server '%s', first command ID=%d",
|
||||
serverAddress.ToString().c_str(),
|
||||
firstCommandId);
|
||||
|
||||
return pConnection;
|
||||
}
|
||||
|
||||
bool CRemoteCommandClient::Schedule(const IRemoteCommand& command)
|
||||
{
|
||||
// No connections
|
||||
if (m_pConnections.empty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Find ClassID for command
|
||||
uint32 classId = 0;
|
||||
if (!GetManager()->FindClassId(command.GetClass(), classId))
|
||||
{
|
||||
LOG_VERBOSE(0, "Class '%s' not recognized. Did you call RegisterClass() ?",
|
||||
command.GetClass()->GetName());
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Alloc new command ID and compile command data
|
||||
// TODO: consider moving the compilation to thread (this may be unsafe).
|
||||
const uint32 commandId = CryInterlockedIncrement((volatile int*) &m_commandId);
|
||||
Command* pCommand = Command::Compile(command, commandId, classId);
|
||||
|
||||
// Register new command in all of the existing server connections
|
||||
if (NULL != pCommand)
|
||||
{
|
||||
CryAutoLock<CryMutex> lock(m_accessMutex);
|
||||
|
||||
for (TConnections::const_iterator it = m_pConnections.begin();
|
||||
it != m_pConnections.end(); ++it)
|
||||
{
|
||||
(*it)->AddToSendQueue(pCommand);
|
||||
}
|
||||
|
||||
// We are done with our reference
|
||||
pCommand->Release();
|
||||
}
|
||||
|
||||
// Signal the thread to process data
|
||||
m_threadEvent.Set();
|
||||
return true;
|
||||
}
|
||||
|
||||
void CRemoteCommandClient::Run()
|
||||
{
|
||||
TConnections pUpdateList;
|
||||
|
||||
CryThreadSetName(-1, "RemoteCommandThread");
|
||||
|
||||
while (!m_bCloseThread)
|
||||
{
|
||||
// copy to local list for updating
|
||||
{
|
||||
CryAutoLock<CryMutex> lock(m_accessMutex);
|
||||
pUpdateList = m_pConnections;
|
||||
}
|
||||
|
||||
// update current connection list
|
||||
for (TConnections::const_iterator it = pUpdateList.begin();
|
||||
it != pUpdateList.end(); ++it)
|
||||
{
|
||||
if (!(*it)->Update())
|
||||
{
|
||||
CryAutoLock<CryMutex> lock(m_accessMutex);
|
||||
m_pConnectionsToDelete.push_back(*it);
|
||||
}
|
||||
}
|
||||
|
||||
// delete pending connections
|
||||
{
|
||||
CryAutoLock<CryMutex> lock(m_accessMutex);
|
||||
for (TConnections::iterator it = m_pConnectionsToDelete.begin();
|
||||
it != m_pConnectionsToDelete.end(); ++it)
|
||||
{
|
||||
// delete the object
|
||||
(*it)->Release();
|
||||
(*it)->Close(true);
|
||||
|
||||
// remove from connection list
|
||||
TConnections::iterator jt = std::find(m_pConnections.begin(), m_pConnections.end(), *it);
|
||||
if (jt != m_pConnections.end())
|
||||
{
|
||||
m_pConnections.erase(jt);
|
||||
}
|
||||
}
|
||||
|
||||
// reset the array
|
||||
m_pConnectionsToDelete.clear();
|
||||
}
|
||||
|
||||
// Limit the CPU usage
|
||||
const uint32 maxWaitTime = 100;
|
||||
m_threadEvent.Wait(maxWaitTime);
|
||||
}
|
||||
}
|
||||
|
||||
void CRemoteCommandClient::Cancel()
|
||||
{
|
||||
m_bCloseThread = true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// Do not remove (can mess up the uber file builds)
|
||||
#undef LOG_VERBOSE
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@@ -1,361 +0,0 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
// Original file Copyright Crytek GMBH or its affiliates, used under license.
|
||||
|
||||
// Description : Helper classes for remote command system
|
||||
|
||||
|
||||
#include "CrySystem_precompiled.h"
|
||||
#include "IServiceNetwork.h"
|
||||
#include "RemoteCommandHelpers.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
CDataReadStreamFormMessage::CDataReadStreamFormMessage(const IServiceNetworkMessage* message)
|
||||
: m_pMessage(message)
|
||||
, m_size(message->GetSize())
|
||||
, m_pData(static_cast<const char*>(message->GetPointer()))
|
||||
, m_offset(0)
|
||||
{
|
||||
// AddRef() is not const unfortunatelly
|
||||
const_cast<IServiceNetworkMessage*>(m_pMessage)->AddRef();
|
||||
}
|
||||
|
||||
CDataReadStreamFormMessage::~CDataReadStreamFormMessage()
|
||||
{
|
||||
// Release() is not const unfortunatelly
|
||||
const_cast<IServiceNetworkMessage*>(m_pMessage)->Release();
|
||||
}
|
||||
|
||||
void CDataReadStreamFormMessage::Delete()
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
void CDataReadStreamFormMessage::Skip(const uint32 size)
|
||||
{
|
||||
CRY_ASSERT(m_offset + size < m_size);
|
||||
m_offset += size;
|
||||
}
|
||||
|
||||
void CDataReadStreamFormMessage::Read(void* pData, const uint32 size)
|
||||
{
|
||||
CRY_ASSERT(m_offset + size < m_size);
|
||||
const char* pReadPtr = m_pData + m_offset;
|
||||
memcpy(pData, pReadPtr, size);
|
||||
m_offset += size;
|
||||
}
|
||||
|
||||
void CDataReadStreamFormMessage::Read8(void* pData)
|
||||
{
|
||||
// it does not actually matter if its uint64, int64 or double so use any
|
||||
ReadType<uint64>(pData);
|
||||
}
|
||||
|
||||
void CDataReadStreamFormMessage::Read4(void* pData)
|
||||
{
|
||||
// it does not actually matter if its uint32, int32 or float so use any
|
||||
ReadType<uint32>(pData);
|
||||
}
|
||||
|
||||
void CDataReadStreamFormMessage::Read2(void* pData)
|
||||
{
|
||||
// it does not actually matter if its uint16, int16 so use any
|
||||
ReadType<uint16>(pData);
|
||||
}
|
||||
|
||||
void CDataReadStreamFormMessage::Read1(void* pData)
|
||||
{
|
||||
// it does not actually matter if its uint8, int8 so use any
|
||||
ReadType<uint8>(pData);
|
||||
}
|
||||
|
||||
const void* CDataReadStreamFormMessage::GetPointer()
|
||||
{
|
||||
const char* pReadPtr = m_pData + m_offset;
|
||||
return pReadPtr;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
CDataWriteStreamToMessage::CDataWriteStreamToMessage(IServiceNetworkMessage* pMessage)
|
||||
: m_pMessage(pMessage)
|
||||
, m_size(pMessage->GetSize())
|
||||
, m_pData(static_cast<char*>(pMessage->GetPointer()))
|
||||
, m_offset(0)
|
||||
{
|
||||
m_pMessage->AddRef();
|
||||
}
|
||||
|
||||
CDataWriteStreamToMessage::~CDataWriteStreamToMessage()
|
||||
{
|
||||
m_pMessage->Release();
|
||||
}
|
||||
|
||||
void CDataWriteStreamToMessage::Delete()
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
const uint32 CDataWriteStreamToMessage::GetSize() const
|
||||
{
|
||||
return m_size;
|
||||
}
|
||||
|
||||
void CDataWriteStreamToMessage::CopyToBuffer(void* pData) const
|
||||
{
|
||||
memcpy(pData, m_pData, m_size);
|
||||
}
|
||||
|
||||
IServiceNetworkMessage* CDataWriteStreamToMessage::BuildMessage() const
|
||||
{
|
||||
m_pMessage->AddRef();
|
||||
return m_pMessage;
|
||||
}
|
||||
|
||||
void CDataWriteStreamToMessage::Write(const void* pData, const uint32 size)
|
||||
{
|
||||
CRY_ASSERT(m_offset + size < m_size);
|
||||
memcpy((char*)m_pData + m_offset, pData, size);
|
||||
m_offset += size;
|
||||
}
|
||||
|
||||
void CDataWriteStreamToMessage::Write8(const void* pData)
|
||||
{
|
||||
// it does not actually matter if its uint64, int64 or double so use any
|
||||
WriteType<uint64>(pData);
|
||||
}
|
||||
|
||||
void CDataWriteStreamToMessage::Write4(const void* pData)
|
||||
{
|
||||
// it does not actually matter if its uint32, int32 or float so use any
|
||||
WriteType<uint32>(pData);
|
||||
}
|
||||
|
||||
void CDataWriteStreamToMessage::Write2(const void* pData)
|
||||
{
|
||||
// it does not actually matter if its uint16, int16 so use any
|
||||
WriteType<uint16>(pData);
|
||||
}
|
||||
|
||||
void CDataWriteStreamToMessage::Write1(const void* pData)
|
||||
{
|
||||
// it does not actually matter if its uint8, int8 so use any
|
||||
WriteType<uint8>(pData);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
CDataReadStreamMemoryBuffer::CDataReadStreamMemoryBuffer(const void* pData, const uint32 size)
|
||||
: m_size(size)
|
||||
, m_offset(0)
|
||||
{
|
||||
m_pData = new uint8 [size];
|
||||
memcpy(m_pData, pData, size);
|
||||
}
|
||||
|
||||
CDataReadStreamMemoryBuffer::~CDataReadStreamMemoryBuffer()
|
||||
{
|
||||
delete [] m_pData;
|
||||
m_pData = NULL;
|
||||
}
|
||||
|
||||
void CDataReadStreamMemoryBuffer::Delete()
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
void CDataReadStreamMemoryBuffer::Skip(const uint32 size)
|
||||
{
|
||||
CRY_ASSERT(m_offset + size <= m_size);
|
||||
m_offset += size;
|
||||
}
|
||||
|
||||
void CDataReadStreamMemoryBuffer::Read8(void* pData)
|
||||
{
|
||||
Read(pData, 8);
|
||||
SwapEndian(*reinterpret_cast<uint64*>(pData));
|
||||
}
|
||||
|
||||
void CDataReadStreamMemoryBuffer::Read4(void* pData)
|
||||
{
|
||||
Read(pData, 4);
|
||||
SwapEndian(*reinterpret_cast<uint32*>(pData));
|
||||
}
|
||||
|
||||
void CDataReadStreamMemoryBuffer::Read2(void* pData)
|
||||
{
|
||||
Read(pData, 2);
|
||||
SwapEndian(*reinterpret_cast<uint16*>(pData));
|
||||
}
|
||||
|
||||
void CDataReadStreamMemoryBuffer::Read1(void* pData)
|
||||
{
|
||||
return Read(pData, 1);
|
||||
}
|
||||
|
||||
const void* CDataReadStreamMemoryBuffer::GetPointer()
|
||||
{
|
||||
return m_pData + m_offset;
|
||||
};
|
||||
|
||||
void CDataReadStreamMemoryBuffer::Read(void* pData, const uint32 size)
|
||||
{
|
||||
CRY_ASSERT(m_offset + size <= m_size);
|
||||
memcpy(pData, m_pData + m_offset, size);
|
||||
m_offset += size;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
CDataWriteStreamBuffer::CDataWriteStreamBuffer()
|
||||
: m_size(0)
|
||||
{
|
||||
// Start with the initial (preallocated) partition
|
||||
// This optimization assumes that initial size of most of the messages will be small.
|
||||
// NOTE: default partition is not added to the partition table (that would require push_backs to vector)
|
||||
char* partitionMemory = &m_defaultPartition[0];
|
||||
m_pCurrentPointer = partitionMemory;
|
||||
m_leftInPartition = sizeof(m_defaultPartition);
|
||||
}
|
||||
|
||||
CDataWriteStreamBuffer::~CDataWriteStreamBuffer()
|
||||
{
|
||||
// Free all memory partitions that were allocated dynamically
|
||||
for (size_t i = 0; i < m_pPartitions.size(); ++i)
|
||||
{
|
||||
CryModuleFree(m_pPartitions[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void CDataWriteStreamBuffer::Delete()
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
const uint32 CDataWriteStreamBuffer::GetSize() const
|
||||
{
|
||||
return m_size;
|
||||
}
|
||||
|
||||
void CDataWriteStreamBuffer::CopyToBuffer(void* pData) const
|
||||
{
|
||||
uint32 dataLeft = m_size;
|
||||
char* pWritePtr = (char*)pData;
|
||||
|
||||
// Copy data from default (preallocated) partition
|
||||
{
|
||||
const uint32 partitionSize = sizeof(m_defaultPartition);
|
||||
const uint32 dataToCopy = min<uint32>(partitionSize, dataLeft);
|
||||
memcpy(pWritePtr, &m_defaultPartition[0], dataToCopy);
|
||||
|
||||
// advance
|
||||
pWritePtr += dataToCopy;
|
||||
dataLeft -= dataToCopy;
|
||||
}
|
||||
|
||||
// Copy data from dynamic partitions
|
||||
for (uint32 i = 0; i < m_pPartitions.size(); ++i)
|
||||
{
|
||||
// get size of data to copy
|
||||
const uint32 partitionSize = m_partitionSizes[i];
|
||||
const uint32 dataToCopy = min<uint32>(partitionSize, dataLeft);
|
||||
memcpy(pWritePtr, m_pPartitions[i], dataToCopy);
|
||||
|
||||
// advance
|
||||
pWritePtr += dataToCopy;
|
||||
dataLeft -= dataToCopy;
|
||||
}
|
||||
|
||||
// Make sure all data was written
|
||||
CRY_ASSERT(dataLeft == 0);
|
||||
}
|
||||
|
||||
IServiceNetworkMessage* CDataWriteStreamBuffer::BuildMessage() const
|
||||
{
|
||||
// No data written, no message created
|
||||
if (0 == m_size)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Create message to hold all the data
|
||||
IServiceNetworkMessage* pMessage = gEnv->pServiceNetwork->AllocMessageBuffer(m_size);
|
||||
if (NULL == pMessage)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Copy data to messages
|
||||
CopyToBuffer(pMessage->GetPointer());
|
||||
return pMessage;
|
||||
}
|
||||
|
||||
void CDataWriteStreamBuffer::Write(const void* pData, const uint32 size)
|
||||
{
|
||||
static const uint32 kAdditionalPartitionSize = 65536;
|
||||
|
||||
uint32 dataLeft = size;
|
||||
while (dataLeft > 0)
|
||||
{
|
||||
// new partition needed
|
||||
if (m_leftInPartition == 0)
|
||||
{
|
||||
// Allocate new partition data
|
||||
char* partitionMemory = (char*)CryModuleMalloc(kAdditionalPartitionSize);
|
||||
CRY_ASSERT(partitionMemory != NULL);
|
||||
|
||||
// add new partition to list
|
||||
m_partitionSizes.push_back(kAdditionalPartitionSize);
|
||||
m_pPartitions.push_back(partitionMemory);
|
||||
m_pCurrentPointer = partitionMemory;
|
||||
m_leftInPartition = kAdditionalPartitionSize;
|
||||
}
|
||||
|
||||
// how many bytes can we write to current partition ?
|
||||
const uint32 maxToWrite = min<uint32>(m_leftInPartition, dataLeft);
|
||||
memcpy(m_pCurrentPointer, pData, maxToWrite);
|
||||
|
||||
// advance
|
||||
m_size += maxToWrite;
|
||||
dataLeft -= maxToWrite;
|
||||
pData = (const char*)pData + maxToWrite;
|
||||
m_pCurrentPointer += maxToWrite;
|
||||
m_leftInPartition -= maxToWrite;
|
||||
}
|
||||
}
|
||||
|
||||
void CDataWriteStreamBuffer::Write8(const void* pData)
|
||||
{
|
||||
// it does not actually matter if its uint64, int64 or double so use any
|
||||
WriteType<uint64>(pData);
|
||||
}
|
||||
|
||||
void CDataWriteStreamBuffer::Write4(const void* pData)
|
||||
{
|
||||
// it does not actually matter if its uint32, int32 or float so use any
|
||||
WriteType<uint32>(pData);
|
||||
}
|
||||
|
||||
void CDataWriteStreamBuffer::Write2(const void* pData)
|
||||
{
|
||||
// it does not actually matter if its uint16, int16 so use any
|
||||
WriteType<uint16>(pData);
|
||||
}
|
||||
|
||||
void CDataWriteStreamBuffer::Write1(const void* pData)
|
||||
{
|
||||
// it does not actually matter if its uint8, int8 so use any
|
||||
WriteType<uint8>(pData);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@@ -1,307 +0,0 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
// Original file Copyright Crytek GMBH or its affiliates, used under license.
|
||||
|
||||
// Description : Remote command system helper classes
|
||||
|
||||
|
||||
#ifndef CRYINCLUDE_CRYSYSTEM_REMOTECOMMANDHELPERS_H
|
||||
#define CRYINCLUDE_CRYSYSTEM_REMOTECOMMANDHELPERS_H
|
||||
#pragma once
|
||||
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
|
||||
#include "IRemoteCommand.h"
|
||||
|
||||
struct IServiceNetworkMessage;
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
|
||||
// Stream reader for service network message
|
||||
// Implements automatic byte swapping
|
||||
class CDataReadStreamFormMessage
|
||||
: public IDataReadStream
|
||||
{
|
||||
private:
|
||||
const IServiceNetworkMessage* m_pMessage;
|
||||
const char* m_pData;
|
||||
uint32 m_offset;
|
||||
uint32 m_size;
|
||||
|
||||
private:
|
||||
template<typename T>
|
||||
ILINE void ReadType(void* pData)
|
||||
{
|
||||
CRY_ASSERT(m_offset + sizeof(T) < m_size);
|
||||
const T& readPos = *reinterpret_cast<const T*>(m_pData + m_offset);
|
||||
*reinterpret_cast<T*>(pData) = readPos;
|
||||
SwapEndian(*reinterpret_cast<T*>(pData));
|
||||
m_offset += sizeof(T);
|
||||
}
|
||||
|
||||
public:
|
||||
CDataReadStreamFormMessage(const IServiceNetworkMessage* message);
|
||||
virtual ~CDataReadStreamFormMessage();
|
||||
|
||||
const uint32 GetOffset() const
|
||||
{
|
||||
return m_offset;
|
||||
}
|
||||
|
||||
void SetPosition(uint32 offset)
|
||||
{
|
||||
m_offset = offset;
|
||||
}
|
||||
|
||||
public:
|
||||
// IDataReadStream interface
|
||||
virtual void Delete();
|
||||
virtual void Skip(const uint32 size);
|
||||
virtual void Read(void* pData, const uint32 size);
|
||||
virtual void Read8(void* pData);
|
||||
virtual void Read4(void* pData);
|
||||
virtual void Read2(void* pData);
|
||||
virtual void Read1(void* pData);
|
||||
virtual const void* GetPointer();
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
|
||||
// Stream writer that writes into the service network message
|
||||
class CDataWriteStreamToMessage
|
||||
: public IDataWriteStream
|
||||
{
|
||||
private:
|
||||
IServiceNetworkMessage* m_pMessage;
|
||||
char* m_pData;
|
||||
uint32 m_offset;
|
||||
uint32 m_size;
|
||||
|
||||
private:
|
||||
template<typename T>
|
||||
ILINE void WriteType(const void* pData)
|
||||
{
|
||||
CRY_ASSERT(m_offset + sizeof(T) < m_size);
|
||||
T& writePos = *reinterpret_cast<T*>(m_pData + m_offset);
|
||||
writePos = *reinterpret_cast<const T*>(pData);
|
||||
SwapEndian(writePos);
|
||||
m_offset += sizeof(T);
|
||||
}
|
||||
|
||||
public:
|
||||
CDataWriteStreamToMessage(IServiceNetworkMessage* pMessage);
|
||||
virtual ~CDataWriteStreamToMessage();
|
||||
|
||||
// IDataWriteStream interface implementation
|
||||
virtual void Delete();
|
||||
virtual const uint32 GetSize() const;
|
||||
virtual struct IServiceNetworkMessage* BuildMessage() const;
|
||||
virtual void CopyToBuffer(void* pData) const;
|
||||
virtual void Write(const void* pData, const uint32 size);
|
||||
virtual void Write8(const void* pData);
|
||||
virtual void Write4(const void* pData);
|
||||
virtual void Write2(const void* pData);
|
||||
virtual void Write1(const void* pData);
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
|
||||
/// Stream reader reading from owner memory buffer
|
||||
class CDataReadStreamMemoryBuffer
|
||||
: public IDataReadStream
|
||||
{
|
||||
private:
|
||||
const uint32 m_size;
|
||||
uint8* m_pData;
|
||||
uint32 m_offset;
|
||||
|
||||
public:
|
||||
// memory is copied!
|
||||
CDataReadStreamMemoryBuffer(const void* pData, const uint32 size);
|
||||
virtual ~CDataReadStreamMemoryBuffer();
|
||||
|
||||
virtual void Delete();
|
||||
virtual void Skip(const uint32 size);
|
||||
virtual void Read8(void* pData);
|
||||
virtual void Read4(void* pData);
|
||||
virtual void Read2(void* pData);
|
||||
virtual void Read1(void* pData);
|
||||
virtual const void* GetPointer();
|
||||
virtual void Read(void* pData, const uint32 size);
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
|
||||
// Stream writer that writes into the internal memory buffer
|
||||
class CDataWriteStreamBuffer
|
||||
: public IDataWriteStream
|
||||
{
|
||||
static const uint32 kStaticPartitionSize = 4096;
|
||||
|
||||
private:
|
||||
// Default (preallocated) partition
|
||||
char m_defaultPartition[ kStaticPartitionSize ];
|
||||
|
||||
// Allocated dynamic partitions
|
||||
std::vector<char*> m_pPartitions;
|
||||
|
||||
// Size of the dynamic message partitions
|
||||
std::vector<uint32> m_partitionSizes;
|
||||
|
||||
// Pointer to current writing position in the current partition
|
||||
char* m_pCurrentPointer;
|
||||
|
||||
// Space left in current partition
|
||||
uint32 m_leftInPartition;
|
||||
|
||||
// Total message size so far
|
||||
uint32 m_size;
|
||||
|
||||
private:
|
||||
// Directly write typed data into the stream
|
||||
template<typename T>
|
||||
ILINE void WriteType(const void* pData)
|
||||
{
|
||||
// try to use the faster path if we are not crossing the partition boundary
|
||||
if (m_leftInPartition >= sizeof(T))
|
||||
{
|
||||
// faster case
|
||||
T& writePos = *reinterpret_cast<T*>(m_pCurrentPointer);
|
||||
writePos = *reinterpret_cast<const T*>(pData);
|
||||
SwapEndian(writePos);
|
||||
m_pCurrentPointer += sizeof(T);
|
||||
m_leftInPartition -= sizeof(T);
|
||||
m_size += sizeof(T);
|
||||
}
|
||||
else
|
||||
{
|
||||
// slower case (more generic)
|
||||
T tempVal(*reinterpret_cast<const T*>(pData));
|
||||
SwapEndian(tempVal);
|
||||
Write(&tempVal, sizeof(tempVal));
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
CDataWriteStreamBuffer();
|
||||
virtual ~CDataWriteStreamBuffer();
|
||||
|
||||
// IDataWriteStream interface implementation
|
||||
virtual void Delete();
|
||||
virtual const uint32 GetSize() const;
|
||||
virtual IServiceNetworkMessage* BuildMessage() const;
|
||||
virtual void CopyToBuffer(void* pData) const;
|
||||
virtual void Write(const void* pData, const uint32 size);
|
||||
virtual void Write8(const void* pData);
|
||||
virtual void Write4(const void* pData);
|
||||
virtual void Write2(const void* pData);
|
||||
virtual void Write1(const void* pData);
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// Packet header
|
||||
struct PackedHeader
|
||||
{
|
||||
// Estimation (or better yet, exact value) of how much data this header will take when written.
|
||||
// Please make sure that actual size after serialization is not bigger than this value.
|
||||
static const uint32 kSerializationSize = sizeof(uint8) + sizeof(uint32) + sizeof(uint32);
|
||||
|
||||
// Magic value that identifies command messages vs raw messages
|
||||
static const uint32 kMagic = 0xABBAF00D;
|
||||
|
||||
// Command type
|
||||
// Keep the values unchanged as this may break the protocol
|
||||
enum ECommand
|
||||
{
|
||||
// Server class list mapping
|
||||
eCommand_ClassList = 0,
|
||||
|
||||
// Command data
|
||||
eCommand_Command = 1,
|
||||
|
||||
// Disconnect signal
|
||||
eCommand_Disconnect = 2,
|
||||
|
||||
// ACK packet
|
||||
eCommand_ACK = 3,
|
||||
};
|
||||
|
||||
uint32 magic;
|
||||
uint8 msgType;
|
||||
uint32 count;
|
||||
|
||||
// serialization operator
|
||||
template< class T >
|
||||
friend T& operator<<(T& stream, PackedHeader& header)
|
||||
{
|
||||
stream << header.magic;
|
||||
stream << header.msgType;
|
||||
stream << header.count;
|
||||
return stream;
|
||||
}
|
||||
};
|
||||
|
||||
// Header sent with every command
|
||||
struct CommandHeader
|
||||
{
|
||||
uint32 commandId;
|
||||
uint32 classId;
|
||||
uint32 size;
|
||||
|
||||
CommandHeader()
|
||||
: commandId(0)
|
||||
, classId(0)
|
||||
, size(0)
|
||||
{}
|
||||
|
||||
// serialization operator
|
||||
template< class T >
|
||||
friend T& operator<<(T& stream, CommandHeader& header)
|
||||
{
|
||||
stream << header.commandId;
|
||||
stream << header.classId;
|
||||
stream << header.size;
|
||||
return stream;
|
||||
}
|
||||
};
|
||||
|
||||
// General Response/ACK header
|
||||
struct ResponseHeader
|
||||
{
|
||||
uint32 magic;
|
||||
uint8 msgType;
|
||||
uint32 lastCommandReceived;
|
||||
uint32 lastCommandExecuted;
|
||||
|
||||
ResponseHeader()
|
||||
: lastCommandReceived(0)
|
||||
, lastCommandExecuted(0)
|
||||
, msgType(PackedHeader::eCommand_ACK)
|
||||
{}
|
||||
|
||||
// serialization operator
|
||||
template< class T >
|
||||
friend T& operator<<(T& stream, ResponseHeader& header)
|
||||
{
|
||||
stream << header.magic;
|
||||
stream << header.msgType;
|
||||
stream << header.lastCommandReceived;
|
||||
stream << header.lastCommandExecuted;
|
||||
return stream;
|
||||
}
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#endif // CRYINCLUDE_CRYSYSTEM_REMOTECOMMANDHELPERS_H
|
||||
@@ -1,832 +0,0 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
// Original file Copyright Crytek GMBH or its affiliates, used under license.
|
||||
|
||||
// Description : Remote command system implementation (server)
|
||||
|
||||
#include "CrySystem_precompiled.h"
|
||||
#include "IServiceNetwork.h"
|
||||
#include "RemoteCommand.h"
|
||||
#include "RemoteCommandHelpers.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// remote system internal logging
|
||||
#ifdef RELEASE
|
||||
#define LOG_VERBOSE(level, txt, ...)
|
||||
#else
|
||||
#define LOG_VERBOSE(level, txt, ...) if (GetManager()->CheckVerbose(level)) { GetManager()->Log(txt, __VA_ARGS__); }
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
CRemoteCommandServer::WrappedCommand::WrappedCommand(IRemoteCommand* pCommand, const uint32 commandId)
|
||||
: m_pCommand(pCommand)
|
||||
, m_refCount(1)
|
||||
, m_commandID(commandId)
|
||||
{
|
||||
}
|
||||
|
||||
CRemoteCommandServer::WrappedCommand::~WrappedCommand()
|
||||
{
|
||||
CRY_ASSERT(m_refCount == 0);
|
||||
m_pCommand->Delete();
|
||||
}
|
||||
|
||||
void CRemoteCommandServer::WrappedCommand::AddRef()
|
||||
{
|
||||
CryInterlockedIncrement(&m_refCount);
|
||||
}
|
||||
|
||||
void CRemoteCommandServer::WrappedCommand::Release()
|
||||
{
|
||||
if (0 == CryInterlockedDecrement(&m_refCount))
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
CRemoteCommandServer::Endpoint::Endpoint(CRemoteCommandManager* pManager, class CRemoteCommandServer* pServer, IServiceNetworkConnection* pConnection)
|
||||
: m_pConnection(pConnection)
|
||||
, m_pManager(pManager)
|
||||
, m_pServer(pServer)
|
||||
, m_lastReceivedCommand(0)
|
||||
, m_lastExecutedCommand(0)
|
||||
, m_lastReceivedCommandACKed(0)
|
||||
, m_lastExecutedCommandACKed(0)
|
||||
, m_bHasReceivedClassList(false)
|
||||
{
|
||||
}
|
||||
|
||||
CRemoteCommandServer::Endpoint::~Endpoint()
|
||||
{
|
||||
// release commands that were not yet executed
|
||||
// this will release the command memory buffers (if they are not referenced elsewhere)
|
||||
while (!m_pCommandsToExecute.empty())
|
||||
{
|
||||
WrappedCommand* pCommand = m_pCommandsToExecute.pop();
|
||||
pCommand->Release();
|
||||
}
|
||||
|
||||
// make sure the network connection is closed
|
||||
if (NULL != m_pConnection)
|
||||
{
|
||||
// send the disconnect message
|
||||
{
|
||||
CDataWriteStreamBuffer writer;
|
||||
|
||||
// format messages
|
||||
PackedHeader header;
|
||||
header.magic = PackedHeader::kMagic;
|
||||
header.msgType = PackedHeader::eCommand_Disconnect;
|
||||
header.count = 0;
|
||||
writer << header;
|
||||
|
||||
// Send the message
|
||||
IServiceNetworkMessage* pMessage = writer.BuildMessage();
|
||||
if (NULL != pMessage)
|
||||
{
|
||||
m_pConnection->SendMsg(pMessage);
|
||||
pMessage->Release();
|
||||
}
|
||||
}
|
||||
|
||||
// close the connection (but try to send messages out)
|
||||
m_pConnection->FlushAndClose(IServiceNetworkConnection::kDefaultFlushTime);
|
||||
m_pConnection->Release();
|
||||
m_pConnection = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
const char* CRemoteCommandServer::Endpoint::GetClassName(const uint32 classId) const
|
||||
{
|
||||
// class index is out of bounds
|
||||
if (classId >= m_pLocalClassFactories.size())
|
||||
{
|
||||
return "InvalidClassID";
|
||||
}
|
||||
|
||||
// get class factory for the class ID
|
||||
IRemoteCommandClass* theClass = m_pLocalClassFactories[ classId ];
|
||||
if (NULL == theClass)
|
||||
{
|
||||
// ID is valid but we do not support this class
|
||||
// Can happen, usually due to version mismatch between client and server binaries
|
||||
return "UnsupportedClassID";
|
||||
}
|
||||
|
||||
return theClass->GetName();
|
||||
}
|
||||
|
||||
IRemoteCommand* CRemoteCommandServer::Endpoint::CreateObject(const uint32 classId) const
|
||||
{
|
||||
// class index is out of bounds
|
||||
if (classId >= m_pLocalClassFactories.size())
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// get class factory for given class index
|
||||
IRemoteCommandClass* theClass = m_pLocalClassFactories[ classId ];
|
||||
if (NULL == theClass)
|
||||
{
|
||||
// ID is valid but we do not support this class
|
||||
// Can happen, usually due to version mismatch between client and server binaries
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// use the class definition to create the instance of the remote command object
|
||||
return theClass->CreateObject();
|
||||
}
|
||||
|
||||
void CRemoteCommandServer::Endpoint::Execute()
|
||||
{
|
||||
uint32 idOfLastExecutedCommand = 0;
|
||||
|
||||
// Process the commands on the execution list
|
||||
while (!m_pCommandsToExecute.empty())
|
||||
{
|
||||
// Pop the command from the stack
|
||||
WrappedCommand* pCommand = m_pCommandsToExecute.pop();
|
||||
|
||||
LOG_VERBOSE(3, "Executing command '%s', ID %d",
|
||||
pCommand->GetCommand()->GetClass()->GetName(),
|
||||
pCommand->GetId());
|
||||
|
||||
// Here is where the magic happens
|
||||
{
|
||||
pCommand->GetCommand()->Execute();
|
||||
}
|
||||
|
||||
// Keep track of the command ID executed so far (so we can update the ACK later)
|
||||
CRY_ASSERT(pCommand->GetId() > idOfLastExecutedCommand);
|
||||
idOfLastExecutedCommand = pCommand->GetId();
|
||||
|
||||
// Command was executed, we can release it
|
||||
pCommand->Release();
|
||||
}
|
||||
|
||||
// Update the ACK data (if it's needed)
|
||||
if (idOfLastExecutedCommand != 0)
|
||||
{
|
||||
CryAutoLock<CryMutex> lock(m_accessLock);
|
||||
|
||||
LOG_VERBOSE(3, "Updating LastExecutedCommandID %d->%d",
|
||||
m_lastExecutedCommand,
|
||||
idOfLastExecutedCommand);
|
||||
|
||||
// Well, it only makes sens if the current command ID is greater that the last one executed
|
||||
CRY_ASSERT(idOfLastExecutedCommand > m_lastExecutedCommand);
|
||||
if (idOfLastExecutedCommand > m_lastExecutedCommand)
|
||||
{
|
||||
m_lastExecutedCommand = idOfLastExecutedCommand;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool CRemoteCommandServer::Endpoint::Update()
|
||||
{
|
||||
// Check connection status
|
||||
if (!m_pConnection->IsAlive())
|
||||
{
|
||||
// Signal the owner that this endpoint should be deleted
|
||||
return false;
|
||||
}
|
||||
|
||||
// Receive and deserialize the commands
|
||||
// Note that this is done asynchronously so commands can be decoded even if the main thread is busy
|
||||
// Note that execution is DEFERRED to the main thread (I wouldn't risk doing it from this thread ;-))
|
||||
bool bDisconnectReceived = false;
|
||||
IServiceNetworkMessage* pMsg = m_pConnection->ReceiveMsg();
|
||||
while (NULL != pMsg && !bDisconnectReceived)
|
||||
{
|
||||
CDataReadStreamFormMessage reader(pMsg);
|
||||
|
||||
// read back the packet header
|
||||
PackedHeader packetHeader;
|
||||
reader << packetHeader;
|
||||
|
||||
// Is this a command system messages ?
|
||||
if (packetHeader.magic == PackedHeader::kMagic)
|
||||
{
|
||||
switch (packetHeader.msgType)
|
||||
{
|
||||
// Class list, usually sent as first thing after connection
|
||||
case PackedHeader::eCommand_ClassList:
|
||||
{
|
||||
// deserialize class names
|
||||
std::vector< string > classNames;
|
||||
reader << classNames;
|
||||
|
||||
// sync the command ID to the current value on the client
|
||||
const uint32 firstCommandID = packetHeader.count;
|
||||
m_lastExecutedCommand = firstCommandID;
|
||||
m_lastExecutedCommandACKed = firstCommandID;
|
||||
m_lastReceivedCommand = firstCommandID;
|
||||
m_lastReceivedCommandACKed = firstCommandID;
|
||||
m_bHasReceivedClassList = true;
|
||||
|
||||
LOG_VERBOSE(3, "Received class list packet, count=%d, first message=%d from '%s'",
|
||||
classNames.size(),
|
||||
packetHeader.count,
|
||||
m_pConnection->GetRemoteAddress().ToString().c_str());
|
||||
|
||||
// create class mapping between remote client and this server
|
||||
GetManager()->BuildClassMapping(classNames, m_pLocalClassFactories);
|
||||
break;
|
||||
}
|
||||
|
||||
// Actual command packets
|
||||
case PackedHeader::eCommand_Command:
|
||||
{
|
||||
LOG_VERBOSE(3, "Received packet, count=%d from '%s'",
|
||||
packetHeader.count,
|
||||
m_pConnection->GetRemoteAddress().ToString().c_str());
|
||||
|
||||
// load the serialized commands
|
||||
const uint32 numCommands = packetHeader.count;
|
||||
for (uint32 i = 0; i < numCommands; ++i)
|
||||
{
|
||||
// Each command is prefixed with header
|
||||
CommandHeader header;
|
||||
reader << header;
|
||||
|
||||
// We must be able to skip to the end of the command data because sometimes
|
||||
// some data can be omitted - either by dropping the command altogether or
|
||||
// by faulty deserialization. Don't trust the user.
|
||||
const uint32 offset = reader.GetOffset();
|
||||
const uint32 endOffset = offset + header.size; // here is where we can skip
|
||||
|
||||
LOG_VERBOSE(3, "Received command ID=%d (class id=%d, size=%d) from '%s'",
|
||||
header.commandId,
|
||||
header.classId,
|
||||
header.size,
|
||||
m_pConnection->GetRemoteAddress().ToString().c_str());
|
||||
|
||||
// Do not process commands out of order.
|
||||
// This should not happen if network is in good health, but we cannot assume that, never-ever.
|
||||
// This code will cause our side to stop executing new commands until the remote side to resend the missing ones.
|
||||
// Typically it is better than executing commands out of order.
|
||||
const uint32 expectedNextCommand = m_lastReceivedCommand + 1;
|
||||
if (header.commandId > expectedNextCommand)
|
||||
{
|
||||
LOG_VERBOSE(0, "Out of order command ID (%d > %d) received from '%s'",
|
||||
header.commandId,
|
||||
expectedNextCommand,
|
||||
m_pConnection->GetRemoteAddress().ToString().c_str());
|
||||
|
||||
// next commands will be even older, no need to check them
|
||||
break;
|
||||
}
|
||||
|
||||
// Do not process the old commands
|
||||
// This may happen pretty often when command is resent while the ACK is "in-flight"
|
||||
// Just drop the data and go on.
|
||||
if (header.commandId <= m_lastReceivedCommand)
|
||||
{
|
||||
// getting old command is not an error, it just means that we have large enough lag
|
||||
// that the client started resending old commands.
|
||||
LOG_VERBOSE(1, "Old command (%d <= %d) received from '%s'",
|
||||
header.commandId,
|
||||
m_lastReceivedCommand,
|
||||
m_pConnection->GetRemoteAddress().ToString().c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
// next command received, we are very strict about matchig the command IDs here
|
||||
CRY_ASSERT(header.commandId == expectedNextCommand);
|
||||
m_lastReceivedCommand = expectedNextCommand;
|
||||
|
||||
// create the command
|
||||
IRemoteCommand* pCommand = CreateObject(header.classId);
|
||||
if (NULL != pCommand)
|
||||
{
|
||||
// Fine-grain logging
|
||||
LOG_VERBOSE(3, "Received command '%s', classId=%d, commandId=%d from '%s'",
|
||||
pCommand->GetClass()->GetName(),
|
||||
header.classId,
|
||||
header.commandId,
|
||||
m_pConnection->GetRemoteAddress().ToString().c_str());
|
||||
|
||||
// Deserialize the command data from network message
|
||||
pCommand->LoadFromStream(reader);
|
||||
|
||||
// Add to list of commands to execute
|
||||
{
|
||||
m_pCommandsToExecute.push(new WrappedCommand(pCommand, header.commandId));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_VERBOSE(0, "ClassId %d not recognized. Skipping command ID%d from '%s'",
|
||||
header.classId,
|
||||
header.commandId,
|
||||
m_pConnection->GetRemoteAddress().ToString().c_str());
|
||||
}
|
||||
|
||||
// Update the last command ID
|
||||
m_lastReceivedCommand = header.commandId;
|
||||
}
|
||||
|
||||
// Sync the message stream to popper position
|
||||
CRY_ASSERT(reader.GetOffset() <= endOffset);
|
||||
reader.SetPosition(endOffset);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// request to disconnect (graceful)
|
||||
case PackedHeader::eCommand_Disconnect:
|
||||
{
|
||||
LOG_VERBOSE(3, "Received disconnect request from '%s'",
|
||||
m_pConnection->GetRemoteAddress().ToString().c_str());
|
||||
|
||||
m_pConnection->Close();
|
||||
bDisconnectReceived = true;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// should not happen
|
||||
default:
|
||||
{
|
||||
LOG_VERBOSE(0, "Invalid message type '%s' received from '%s'",
|
||||
packetHeader.msgType,
|
||||
m_pConnection->GetRemoteAddress().ToString().c_str());
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// This is a raw message, try to process immediately using async listeners.
|
||||
// If it fails, add to the queue for processing on the main thread by sync listeners.
|
||||
m_pServer->ProcessRawMessageAsync(pMsg, m_pConnection);
|
||||
}
|
||||
|
||||
// Release the message data
|
||||
pMsg->Release();
|
||||
|
||||
// Get the next message from network
|
||||
if (!bDisconnectReceived)
|
||||
{
|
||||
pMsg = m_pConnection->ReceiveMsg();
|
||||
}
|
||||
}
|
||||
|
||||
// The value of lastExecutedCommand can change outside this thread,
|
||||
// so capture it one and keep it constant for the duration of the logic in this function.
|
||||
const uint32 snapshotLastExecutedCommand = m_lastExecutedCommand;
|
||||
|
||||
// Determine if we should send generate the ACK signal
|
||||
if ((snapshotLastExecutedCommand != m_lastExecutedCommandACKed) ||
|
||||
(m_lastReceivedCommand != m_lastReceivedCommandACKed)) // this can
|
||||
{
|
||||
ResponseHeader header;
|
||||
header.magic = PackedHeader::kMagic;
|
||||
header.msgType = PackedHeader::eCommand_ACK;
|
||||
header.lastCommandReceived = m_lastReceivedCommand;
|
||||
header.lastCommandExecuted = snapshotLastExecutedCommand; // note that we use the captured values
|
||||
|
||||
LOG_VERBOSE(3, "Sending ACK to '%s' with LastReceived=%d, LastExecuted=%d",
|
||||
m_pConnection->GetRemoteAddress().ToString().c_str(),
|
||||
header.lastCommandReceived,
|
||||
header.lastCommandExecuted);
|
||||
|
||||
// Write header into the message
|
||||
CDataWriteStreamBuffer writer;
|
||||
writer << header;
|
||||
|
||||
// Extract the message
|
||||
IServiceNetworkMessage* pMessage = writer.BuildMessage();
|
||||
if (NULL != pMessage)
|
||||
{
|
||||
// Send it back over the connection (works as ACK)
|
||||
if (m_pConnection->SendMsg(pMessage))
|
||||
{
|
||||
// Only after the message is accepted by the network we can assume that we have ACKed it properly
|
||||
// This can still leave a possibility that this message gets eaten in the network but we will resend newer ACK
|
||||
// soon enough that we don't need to bother with this.
|
||||
m_lastExecutedCommandACKed = header.lastCommandExecuted;
|
||||
m_lastReceivedCommandACKed = header.lastCommandReceived;
|
||||
}
|
||||
|
||||
pMessage->Release();
|
||||
}
|
||||
}
|
||||
|
||||
// Continue
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
CRemoteCommandServer::CRemoteCommandServer(CRemoteCommandManager* pManager, IServiceNetworkListener* pListener)
|
||||
: m_pManager(pManager)
|
||||
, m_pListener(pListener)
|
||||
, m_bCloseThread(false)
|
||||
, m_suppressionCounter(0)
|
||||
, m_bIsSuppressed(false)
|
||||
{
|
||||
// Start processing thread (receiving from network, deserialization, etc)
|
||||
m_pThread = new TRemoteServerThread();
|
||||
m_pThread->Start(*this);
|
||||
}
|
||||
|
||||
CRemoteCommandServer::~CRemoteCommandServer()
|
||||
{
|
||||
// Stop the thread, assumes that thread is responsive
|
||||
if (NULL != m_pThread)
|
||||
{
|
||||
m_pThread->Cancel();
|
||||
m_pThread->Stop();
|
||||
m_pThread->WaitForThread();
|
||||
delete m_pThread;
|
||||
}
|
||||
|
||||
// Cleanup the clients endpoints
|
||||
for (TEndpoints::const_iterator it = m_pEndpoints.begin();
|
||||
it != m_pEndpoints.end(); ++it)
|
||||
{
|
||||
delete (*it);
|
||||
}
|
||||
m_pEndpoints.clear();
|
||||
|
||||
// Cleanup the clients that were not yet deleted but are dead
|
||||
for (TEndpoints::const_iterator it = m_pEndpointToDelete.begin();
|
||||
it != m_pEndpointToDelete.end(); ++it)
|
||||
{
|
||||
delete (*it);
|
||||
}
|
||||
m_pEndpointToDelete.clear();
|
||||
|
||||
// Cleanup the raw messages
|
||||
while (!m_pRawMessages.empty())
|
||||
{
|
||||
delete m_pRawMessages.pop();
|
||||
}
|
||||
|
||||
// Properly close the listening socket
|
||||
if (m_pListener != NULL)
|
||||
{
|
||||
m_pListener->Close();
|
||||
m_pListener->Release();
|
||||
m_pListener = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void CRemoteCommandServer::ProcessRawMessageAsync(IServiceNetworkMessage* pMessage, IServiceNetworkConnection* pConnection)
|
||||
{
|
||||
// we lock for the whole duration of the function - I think that's the safest.
|
||||
// this function is being called from remote command server thread and even if it locks for a moment that's not a tragic situation.
|
||||
CryAutoLock<CryMutex> lock(m_rawMessagesLock);
|
||||
|
||||
// Process the message using async listeners
|
||||
bool bWasProcessed = false;
|
||||
for (TRawMessageListenersAsync::const_iterator it = m_pRawListenersAsync.begin();
|
||||
it != m_pRawListenersAsync.end(); ++it)
|
||||
{
|
||||
CDataReadStreamFormMessage reader(pMessage);
|
||||
CDataWriteStreamBuffer writer;
|
||||
|
||||
// Request the listener to process this message
|
||||
if ((*it)->OnRawMessageAsync(pConnection->GetRemoteAddress(), reader, writer))
|
||||
{
|
||||
// Send response back using the source connection
|
||||
if (writer.GetSize() > 0)
|
||||
{
|
||||
IServiceNetworkMessage* pNewMessage = writer.BuildMessage();
|
||||
if (NULL != pNewMessage)
|
||||
{
|
||||
pConnection->SendMsg(pNewMessage);
|
||||
pNewMessage->Release();
|
||||
}
|
||||
}
|
||||
|
||||
// mark as processed
|
||||
bWasProcessed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Stats
|
||||
if (bWasProcessed)
|
||||
{
|
||||
LOG_VERBOSE(3, "Raw message from '%s', size %d ASYNC, PROCESSED",
|
||||
pConnection->GetRemoteAddress().ToString().c_str(),
|
||||
pMessage->GetSize());
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_VERBOSE(3, "Raw message from '%s', size %d ASYNC, NOT PROCESSED",
|
||||
pConnection->GetRemoteAddress().ToString().c_str(),
|
||||
pMessage->GetSize());
|
||||
}
|
||||
|
||||
// If we have sync listeners add the raw message for processing on the main thread
|
||||
if (!bWasProcessed && !m_pRawListenersSync.empty())
|
||||
{
|
||||
m_pRawMessages.push(new RawMessage(pConnection, pMessage));
|
||||
}
|
||||
}
|
||||
|
||||
void CRemoteCommandServer::ProcessRawMessagesSync()
|
||||
{
|
||||
// get messages
|
||||
TRawMessageListenersSync listeners;
|
||||
{
|
||||
CryAutoLock<CryMutex> lock(m_rawMessagesLock);
|
||||
listeners = m_pRawListenersSync;
|
||||
}
|
||||
|
||||
// process each message
|
||||
while (!m_pRawMessages.empty())
|
||||
{
|
||||
RawMessage* pMsg = m_pRawMessages.pop();
|
||||
|
||||
// Process messages only from alive connection (they could die before we got a chance to process the message)
|
||||
if (pMsg && pMsg->m_pConnection->IsAlive())
|
||||
{
|
||||
// Try to process by on of the listeners
|
||||
bool bWasProcessed = false;
|
||||
for (TRawMessageListenersSync::const_iterator jt = listeners.begin();
|
||||
jt != listeners.end(); ++jt)
|
||||
{
|
||||
CDataReadStreamFormMessage reader(pMsg->m_pMessage);
|
||||
CDataWriteStreamBuffer writer;
|
||||
|
||||
// Request the listener to process this message
|
||||
if ((*jt)->OnRawMessageSync(pMsg->m_pConnection->GetRemoteAddress(), reader, writer))
|
||||
{
|
||||
// Send response back using the source connection
|
||||
if (writer.GetSize() > 0)
|
||||
{
|
||||
IServiceNetworkMessage* pMessage = writer.BuildMessage();
|
||||
if (NULL != pMessage)
|
||||
{
|
||||
pMsg->m_pConnection->SendMsg(pMessage);
|
||||
pMessage->Release();
|
||||
}
|
||||
}
|
||||
|
||||
// mark as processed
|
||||
bWasProcessed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Stats
|
||||
if (bWasProcessed)
|
||||
{
|
||||
LOG_VERBOSE(3, "Raw message from '%s', size %d SYNC PROCESSED",
|
||||
pMsg->m_pConnection->GetRemoteAddress().ToString().c_str(),
|
||||
pMsg->m_pMessage->GetSize());
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_VERBOSE(3, "Raw message from '%s', size %d SYNC NOT PROCESSED",
|
||||
pMsg->m_pConnection->GetRemoteAddress().ToString().c_str(),
|
||||
pMsg->m_pMessage->GetSize());
|
||||
}
|
||||
}
|
||||
|
||||
/// Cleanup
|
||||
delete pMsg;
|
||||
}
|
||||
}
|
||||
|
||||
void CRemoteCommandServer::Delete()
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
void CRemoteCommandServer::FlushCommandQueue()
|
||||
{
|
||||
// Always process raw messages, even if commands are suspended
|
||||
ProcessRawMessagesSync();
|
||||
|
||||
// When the command server is suppressed externally, well, then don't execute any commands
|
||||
// This is usually used when the main thread is doing some heavy stuff.
|
||||
// TODO: Consider signaling the clients about this condition.
|
||||
if (m_bIsSuppressed)
|
||||
{
|
||||
LOG_VERBOSE(4, "FlushCommandQueue: suppressed (counter=%d)",
|
||||
m_suppressionCounter);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Update the endpoints from a copy of the list
|
||||
{
|
||||
CryAutoLock<CryMutex> lock(m_accessLock);
|
||||
m_pUpdateEndpoints = m_pEndpoints;
|
||||
}
|
||||
|
||||
// Execute the commands for each endpoint
|
||||
for (TEndpoints::const_iterator it = m_pUpdateEndpoints.begin();
|
||||
it != m_pUpdateEndpoints.end(); ++it)
|
||||
{
|
||||
(*it)->Execute();
|
||||
}
|
||||
|
||||
// Delete endpoints that were discarded within the thread (due to network errors)
|
||||
// We couldn't do that there because we would need to lock to much inside the mutex (bad idea)
|
||||
if (!m_pEndpointToDelete.empty())
|
||||
{
|
||||
// TODO: consider using different CS for pDeletedEnpoints array
|
||||
CryAutoLock<CryMutex> lock(m_accessLock);
|
||||
|
||||
// delete the endpoint structured (deferred)
|
||||
for (size_t i = 0; i < m_pEndpointToDelete.size(); ++i)
|
||||
{
|
||||
delete m_pEndpointToDelete[i];
|
||||
}
|
||||
|
||||
m_pEndpointToDelete.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void CRemoteCommandServer::SuppressCommands()
|
||||
{
|
||||
if (CryInterlockedIncrement(&m_suppressionCounter) > 0)
|
||||
{
|
||||
m_bIsSuppressed = true;
|
||||
}
|
||||
}
|
||||
|
||||
void CRemoteCommandServer::ResumeCommands()
|
||||
{
|
||||
if (CryInterlockedDecrement(&m_suppressionCounter) == 0)
|
||||
{
|
||||
m_bIsSuppressed = false;
|
||||
}
|
||||
}
|
||||
|
||||
void CRemoteCommandServer::Run()
|
||||
{
|
||||
TEndpoints updateList;
|
||||
|
||||
CryThreadSetName(-1, "RemoteCommandThread");
|
||||
|
||||
#if defined(AZ_RESTRICTED_PLATFORM)
|
||||
#include AZ_RESTRICTED_FILE(RemoteCommandServer_cpp)
|
||||
#endif
|
||||
|
||||
while (!m_bCloseThread)
|
||||
{
|
||||
// Accept new connections
|
||||
{
|
||||
IServiceNetworkConnection* pNewConnection = m_pListener->Accept();
|
||||
if (NULL != pNewConnection)
|
||||
{
|
||||
LOG_VERBOSE(2, "New endpoint created with connection '%s'",
|
||||
pNewConnection->GetRemoteAddress().ToString().c_str());
|
||||
|
||||
// Create endpoint wrapper
|
||||
Endpoint* pEndPoint = new Endpoint(GetManager(), this, pNewConnection);
|
||||
|
||||
// Add to endpoint list
|
||||
{
|
||||
CryAutoLock<CryMutex> lock(m_accessLock);
|
||||
m_pEndpoints.push_back(pEndPoint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get the current endpoint table (for update)
|
||||
{
|
||||
CryAutoLock<CryMutex> lock(m_accessLock);
|
||||
updateList = m_pEndpoints;
|
||||
}
|
||||
|
||||
// Update endpoints
|
||||
for (TEndpoints::iterator it = updateList.begin();
|
||||
it != updateList.end(); ++it)
|
||||
{
|
||||
Endpoint* ep = (*it);
|
||||
if (!ep->Update())
|
||||
{
|
||||
LOG_VERBOSE(2, "RemoteCommand endpoint '%s' closed",
|
||||
ep->GetConnection()->GetRemoteAddress().ToString().c_str());
|
||||
|
||||
// remove the endpoint from the original list
|
||||
{
|
||||
CryAutoLock<CryMutex> lock(m_accessLock);
|
||||
|
||||
// it's safe to remove from the endpoints list - we are iterating over a copy
|
||||
m_pEndpoints.erase(std::find(m_pEndpoints.begin(), m_pEndpoints.end(), ep));
|
||||
|
||||
// don't delete the endpoint structure now (it may still be executed on main thread)
|
||||
// instead add it to a list that will be processed at the end of execution so this endpoint can get deleted
|
||||
m_pEndpointToDelete.push_back(ep);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Limit the CPU usage
|
||||
// TODO: consider using some event based mechanism since the only source of
|
||||
// work for this thread is the network we can esily be triggered by that.
|
||||
Sleep(5);
|
||||
}
|
||||
}
|
||||
|
||||
void CRemoteCommandServer::Cancel()
|
||||
{
|
||||
m_bCloseThread = true;
|
||||
}
|
||||
|
||||
void CRemoteCommandServer::RegisterSyncMessageListener(IRemoteCommandListenerSync* pListener)
|
||||
{
|
||||
CryAutoLock<CryMutex> lock(m_rawMessagesLock);
|
||||
m_pRawListenersSync.push_back(pListener);
|
||||
}
|
||||
|
||||
void CRemoteCommandServer::UnregisterSyncMessageListener(IRemoteCommandListenerSync* pListener)
|
||||
{
|
||||
CryAutoLock<CryMutex> lock(m_rawMessagesLock);
|
||||
|
||||
TRawMessageListenersSync::iterator it = std::find(m_pRawListenersSync.begin(), m_pRawListenersSync.end(), pListener);
|
||||
if (it != m_pRawListenersSync.end())
|
||||
{
|
||||
m_pRawListenersSync.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
void CRemoteCommandServer::RegisterAsyncMessageListener(IRemoteCommandListenerAsync* pListener)
|
||||
{
|
||||
CryAutoLock<CryMutex> lock(m_rawMessagesLock);
|
||||
m_pRawListenersAsync.push_back(pListener);
|
||||
}
|
||||
|
||||
void CRemoteCommandServer::UnregisterAsyncMessageListener(IRemoteCommandListenerAsync* pListener)
|
||||
{
|
||||
CryAutoLock<CryMutex> lock(m_rawMessagesLock);
|
||||
|
||||
TRawMessageListenersAsync::iterator it = std::find(m_pRawListenersAsync.begin(), m_pRawListenersAsync.end(), pListener);
|
||||
if (it != m_pRawListenersAsync.end())
|
||||
{
|
||||
m_pRawListenersAsync.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
void CRemoteCommandServer::Broadcast(IServiceNetworkMessage* pMessage)
|
||||
{
|
||||
if (NULL != pMessage && pMessage->GetSize() > 0)
|
||||
{
|
||||
CryAutoLock<CryMutex> lock(m_rawMessagesLock);
|
||||
for (TEndpoints::const_iterator jt = m_pEndpoints.begin();
|
||||
jt != m_pEndpoints.end(); ++jt)
|
||||
{
|
||||
Endpoint* pEndpoint = (*jt);
|
||||
if (pEndpoint->HasReceivedClassList())
|
||||
{
|
||||
IServiceNetworkConnection* pConnection = pEndpoint->GetConnection();
|
||||
if (NULL != pConnection)
|
||||
{
|
||||
pConnection->SendMsg(pMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool CRemoteCommandServer::HasConnectedClients() const
|
||||
{
|
||||
CryAutoLock<CryMutex> lock(m_rawMessagesLock);
|
||||
|
||||
for (TEndpoints::const_iterator jt = m_pEndpoints.begin();
|
||||
jt != m_pEndpoints.end(); ++jt)
|
||||
{
|
||||
Endpoint* pEndpoint = (*jt);
|
||||
if (pEndpoint->HasReceivedClassList())
|
||||
{
|
||||
IServiceNetworkConnection* pConnection = pEndpoint->GetConnection();
|
||||
if (pConnection->IsAlive())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// Do not remove (can mess up the uber file builds)
|
||||
#undef LOG_VERBOSE
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@@ -699,7 +699,7 @@ void CResourceManager::OnSystemEvent(ESystemEvent event, [[maybe_unused]] UINT_P
|
||||
if (g_cvars.archiveVars.nLoadCache)
|
||||
{
|
||||
//Load the frontend common mode switch pak, this can considerably reduce the time spent switching especially from disc
|
||||
if (!gEnv->bMultiplayer && LoadMenuCommonPak(FRONTEND_COMMON_PAK_FILENAME_SP) == false)
|
||||
if (LoadMenuCommonPak(FRONTEND_COMMON_PAK_FILENAME_SP) == false)
|
||||
{
|
||||
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "Could not load %s during init. This file can significantly reduce frontend loading times.\n", FRONTEND_COMMON_PAK_FILENAME_SP);
|
||||
}
|
||||
@@ -710,14 +710,7 @@ void CResourceManager::OnSystemEvent(ESystemEvent event, [[maybe_unused]] UINT_P
|
||||
|
||||
case ESYSTEM_EVENT_LEVEL_LOAD_PREPARE:
|
||||
{
|
||||
if (!gEnv->bMultiplayer)
|
||||
{
|
||||
UnloadMenuCommonPak(FRONTEND_COMMON_PAK_FILENAME_SP, FRONTEND_COMMON_LIST_FILENAME "_sp");
|
||||
}
|
||||
else
|
||||
{
|
||||
UnloadMenuCommonPak(FRONTEND_COMMON_PAK_FILENAME_MP, FRONTEND_COMMON_LIST_FILENAME "_mp");
|
||||
}
|
||||
UnloadMenuCommonPak(FRONTEND_COMMON_PAK_FILENAME_SP, FRONTEND_COMMON_LIST_FILENAME "_sp");
|
||||
|
||||
m_bLevelTransitioning = !m_sLevelName.empty();
|
||||
|
||||
|
||||
@@ -1,239 +0,0 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
// Original file Copyright Crytek GMBH or its affiliates, used under license.
|
||||
|
||||
#include "CrySystem_precompiled.h"
|
||||
#include <Serialization/IArchiveHost.h>
|
||||
#include "JSONIArchive.h"
|
||||
#include "JSONOArchive.h"
|
||||
#include "BinArchive.h"
|
||||
#include "XmlIArchive.h"
|
||||
#include "XmlOArchive.h"
|
||||
#include <Serialization/ClassFactoryImpl.h>
|
||||
|
||||
namespace Serialization
|
||||
{
|
||||
bool LoadFile(std::vector<char>& content, const char* filename)
|
||||
{
|
||||
AZ::IO::HandleType fileHandle = gEnv->pCryPak->FOpen(filename, "rb");
|
||||
if (!fileHandle)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
gEnv->pCryPak->FSeek(fileHandle, 0, SEEK_END);
|
||||
size_t size = gEnv->pCryPak->FTell(fileHandle);
|
||||
gEnv->pCryPak->FSeek(fileHandle, 0, SEEK_SET);
|
||||
|
||||
content.resize(size);
|
||||
bool result = true;
|
||||
if (size != 0)
|
||||
{
|
||||
result = gEnv->pCryPak->FRead(&content[0], size, fileHandle) == size;
|
||||
}
|
||||
gEnv->pCryPak->FClose(fileHandle);
|
||||
return result;
|
||||
}
|
||||
|
||||
class CArchiveHost
|
||||
: public IArchiveHost
|
||||
{
|
||||
public:
|
||||
bool LoadJsonFile(const SStruct& obj, const char* filename) override
|
||||
{
|
||||
std::vector<char> content;
|
||||
if (!LoadFile(content, filename))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
JSONIArchive ia;
|
||||
if (!ia.open(content.data(), content.size()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return ia(obj);
|
||||
}
|
||||
|
||||
bool SaveJsonFile(const char* gameFilename, const SStruct& obj) override
|
||||
{
|
||||
char buffer[AZ::IO::IArchive::MaxPath];
|
||||
const char* filename = gEnv->pCryPak->AdjustFileName(gameFilename, buffer, AZ_ARRAY_SIZE(buffer), AZ::IO::IArchive::FLAGS_FOR_WRITING);
|
||||
JSONOArchive oa;
|
||||
if (!oa(obj))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return oa.save(filename);
|
||||
}
|
||||
|
||||
bool LoadJsonBuffer(const SStruct& obj, const char* buffer, size_t bufferLength) override
|
||||
{
|
||||
if (bufferLength == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
JSONIArchive ia;
|
||||
if (!ia.open(buffer, bufferLength))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return ia(obj);
|
||||
}
|
||||
|
||||
bool SaveJsonBuffer(DynArray<char>& buffer, const SStruct& obj) override
|
||||
{
|
||||
JSONOArchive oa;
|
||||
if (!oa(obj))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
buffer.assign(oa.buffer(), oa.buffer() + oa.length());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool LoadBinaryFile(const SStruct& obj, const char* filename) override
|
||||
{
|
||||
std::vector<char> content;
|
||||
if (!LoadFile(content, filename))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
BinIArchive ia;
|
||||
if (!ia.open(content.data(), content.size()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return ia(obj);
|
||||
}
|
||||
|
||||
bool SaveBinaryFile(const char* gameFilename, const SStruct& obj) override
|
||||
{
|
||||
char buffer[AZ::IO::IArchive::MaxPath];
|
||||
const char* filename = gEnv->pCryPak->AdjustFileName(gameFilename, buffer, AZ_ARRAY_SIZE(buffer), AZ::IO::IArchive::FLAGS_FOR_WRITING);
|
||||
BinOArchive oa;
|
||||
obj(oa);
|
||||
return oa.save(filename);
|
||||
}
|
||||
|
||||
bool LoadBinaryBuffer(const SStruct& obj, const char* buffer, size_t bufferLength) override
|
||||
{
|
||||
if (bufferLength == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
BinIArchive ia;
|
||||
if (!ia.open(buffer, bufferLength))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return ia(obj);
|
||||
}
|
||||
|
||||
bool SaveBinaryBuffer(DynArray<char>& buffer, const SStruct& obj) override
|
||||
{
|
||||
BinOArchive oa;
|
||||
obj(oa);
|
||||
buffer.assign(oa.buffer(), oa.buffer() + oa.length());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CloneBinary(const SStruct& dest, const SStruct& src) override
|
||||
{
|
||||
BinOArchive oa;
|
||||
src(oa);
|
||||
BinIArchive ia;
|
||||
if (!ia.open(oa.buffer(), oa.length()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
dest(ia);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CompareBinary(const SStruct& lhs, const SStruct& rhs) override
|
||||
{
|
||||
BinOArchive oa1;
|
||||
lhs(oa1);
|
||||
BinOArchive oa2;
|
||||
rhs(oa2);
|
||||
if (oa1.length() != oa2.length())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return memcmp(oa1.buffer(), oa2.buffer(), oa1.length()) == 0;
|
||||
}
|
||||
|
||||
bool SaveXmlFile(const char* filename, const SStruct& obj, const char* rootNodeName) override
|
||||
{
|
||||
XmlNodeRef node = SaveXmlNode(obj, rootNodeName);
|
||||
if (!node)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return node->saveToFile(filename);
|
||||
}
|
||||
|
||||
bool LoadXmlFile(const SStruct& obj, const char* filename) override
|
||||
{
|
||||
XmlNodeRef node = gEnv->pSystem->LoadXmlFromFile(filename);
|
||||
if (!node)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return LoadXmlNode(obj, node);
|
||||
}
|
||||
|
||||
XmlNodeRef SaveXmlNode(const SStruct& obj, const char* nodeName) override
|
||||
{
|
||||
CXmlOArchive oa;
|
||||
XmlNodeRef node = gEnv->pSystem->CreateXmlNode(nodeName);
|
||||
if (!node)
|
||||
{
|
||||
return XmlNodeRef();
|
||||
}
|
||||
oa.SetXmlNode(node);
|
||||
if (!obj(oa))
|
||||
{
|
||||
return XmlNodeRef();
|
||||
}
|
||||
return oa.GetXmlNode();
|
||||
}
|
||||
|
||||
bool SaveXmlNode(XmlNodeRef& node, const SStruct& obj) override
|
||||
{
|
||||
if (!node)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
CXmlOArchive oa;
|
||||
oa.SetXmlNode(node);
|
||||
return obj(oa);
|
||||
}
|
||||
|
||||
bool LoadXmlNode(const SStruct& obj, const XmlNodeRef& node) override
|
||||
{
|
||||
CXmlIArchive ia;
|
||||
ia.SetXmlNode(node);
|
||||
if (!obj(ia))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
IArchiveHost* CreateArchiveHost()
|
||||
{
|
||||
return new CArchiveHost;
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
// Original file Copyright Crytek GMBH or its affiliates, used under license.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Serialization/IArchiveHost.h>
|
||||
|
||||
namespace Serialization
|
||||
{
|
||||
IArchiveHost* CreateArchiveHost();
|
||||
}
|
||||
@@ -1,839 +0,0 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
// Original file Copyright Crytek GMBH or its affiliates, used under license.
|
||||
|
||||
#include "CrySystem_precompiled.h"
|
||||
#include "BinArchive.h"
|
||||
#include <map>
|
||||
#include "Serialization/ClassFactory.h"
|
||||
|
||||
namespace Serialization {
|
||||
static const unsigned char SIZE16 = 254;
|
||||
static const unsigned char SIZE32 = 255;
|
||||
|
||||
static const unsigned int BIN_MAGIC = 0xb1a4c17f;
|
||||
|
||||
//#ifdef _DEBUG
|
||||
//typedef std::map<unsigned short, string> HashMap;
|
||||
//static HashMap hashMap;
|
||||
//#endif
|
||||
|
||||
BinOArchive::BinOArchive()
|
||||
: IArchive(OUTPUT | BINARY)
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
void BinOArchive::clear()
|
||||
{
|
||||
stream_.clear();
|
||||
stream_.write((const char*)&BIN_MAGIC, sizeof(BIN_MAGIC));
|
||||
}
|
||||
|
||||
size_t BinOArchive::length() const
|
||||
{
|
||||
return stream_.position();
|
||||
}
|
||||
|
||||
bool BinOArchive::save(const char* filename)
|
||||
{
|
||||
FILE* f = nullptr;
|
||||
azfopen(&f, filename, "wb");
|
||||
if (!f)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (fwrite(buffer(), 1, length(), f) != length())
|
||||
{
|
||||
fclose(f);
|
||||
return false;
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
return true;
|
||||
}
|
||||
|
||||
inline void BinOArchive::openNode(const char* name, bool size8)
|
||||
{
|
||||
if (!strlen(name))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned short hash = calcHash(name);
|
||||
stream_.write(hash);
|
||||
|
||||
blockSizeOffsets_.push_back(int(stream_.position()));
|
||||
stream_.write((unsigned char)0);
|
||||
if (!size8)
|
||||
{
|
||||
stream_.write((unsigned short)0);
|
||||
}
|
||||
|
||||
#ifdef _DEBUG
|
||||
// HashMap::iterator i = hashMap.find(hash);
|
||||
// if(i != hashMap.end() && i->second != name)
|
||||
// ASSERT_STR(0, name);
|
||||
// hashMap[hash] = name;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline void BinOArchive::closeNode(const char* name, bool size8)
|
||||
{
|
||||
if (!strlen(name))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned int offset = blockSizeOffsets_.back();
|
||||
unsigned int size = (unsigned int)(stream_.position() - offset - sizeof(unsigned char) - (size8 ? 0 : sizeof(unsigned short)));
|
||||
blockSizeOffsets_.pop_back();
|
||||
unsigned char* sizePtr = (unsigned char*)(stream_.buffer() + offset);
|
||||
|
||||
if (size < SIZE16)
|
||||
{
|
||||
*sizePtr = size;
|
||||
if (!size8)
|
||||
{
|
||||
unsigned char* buffer = sizePtr + 3;
|
||||
memmove(buffer - 2, buffer, size);
|
||||
stream_.setPosition(stream_.position() - 2);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
YASLI_ASSERT(!size8);
|
||||
if (size < 0x10000)
|
||||
{
|
||||
*sizePtr = SIZE16;
|
||||
*((unsigned short*)(sizePtr + 1)) = size;
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned char* buffer = sizePtr + 3;
|
||||
stream_.write((unsigned short)0);
|
||||
*sizePtr = SIZE32;
|
||||
memmove(buffer + 2, buffer, size);
|
||||
*((unsigned int*)(sizePtr + 1)) = size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool BinOArchive::operator()(bool& value, const char* name, [[maybe_unused]] const char* label)
|
||||
{
|
||||
openNode(name);
|
||||
stream_.write(value);
|
||||
closeNode(name);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BinOArchive::operator()(IString& value, const char* name, [[maybe_unused]] const char* label)
|
||||
{
|
||||
bool size8 = strlen(value.get()) + 1 < SIZE16;
|
||||
openNode(name, size8);
|
||||
stream_ << value.get();
|
||||
stream_.write(char(0));
|
||||
closeNode(name, size8);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BinOArchive::operator()(IWString& value, const char* name, [[maybe_unused]] const char* label)
|
||||
{
|
||||
bool size8 = (wcslen(value.get()) + 1) * 2 < SIZE16;
|
||||
openNode(name, size8);
|
||||
stream_ << value.get();
|
||||
stream_.write(short(0));
|
||||
closeNode(name, size8);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BinOArchive::operator()(float& value, const char* name, [[maybe_unused]] const char* label)
|
||||
{
|
||||
openNode(name);
|
||||
stream_.write(value);
|
||||
closeNode(name);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BinOArchive::operator()(double& value, const char* name, [[maybe_unused]] const char* label)
|
||||
{
|
||||
openNode(name);
|
||||
stream_.write(value);
|
||||
closeNode(name);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BinOArchive::operator()(int16& value, const char* name, [[maybe_unused]] const char* label)
|
||||
{
|
||||
openNode(name);
|
||||
stream_.write(value);
|
||||
closeNode(name);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BinOArchive::operator()(int8& value, const char* name, [[maybe_unused]] const char* label)
|
||||
{
|
||||
openNode(name);
|
||||
stream_.write(value);
|
||||
closeNode(name);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BinOArchive::operator()(uint8& value, const char* name, [[maybe_unused]] const char* label)
|
||||
{
|
||||
openNode(name);
|
||||
stream_.write(value);
|
||||
closeNode(name);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BinOArchive::operator()(char& value, const char* name, [[maybe_unused]] const char* label)
|
||||
{
|
||||
openNode(name);
|
||||
stream_.write(value);
|
||||
closeNode(name);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BinOArchive::operator()(uint16& value, const char* name, [[maybe_unused]] const char* label)
|
||||
{
|
||||
openNode(name);
|
||||
stream_.write(value);
|
||||
closeNode(name);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BinOArchive::operator()(int32& value, const char* name, [[maybe_unused]] const char* label)
|
||||
{
|
||||
openNode(name);
|
||||
stream_.write(value);
|
||||
closeNode(name);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BinOArchive::operator()(uint32& value, const char* name, [[maybe_unused]] const char* label)
|
||||
{
|
||||
openNode(name);
|
||||
stream_.write(value);
|
||||
closeNode(name);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BinOArchive::operator()(int64& value, const char* name, [[maybe_unused]] const char* label)
|
||||
{
|
||||
openNode(name);
|
||||
stream_.write(value);
|
||||
closeNode(name);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BinOArchive::operator()(uint64& value, const char* name, [[maybe_unused]] const char* label)
|
||||
{
|
||||
openNode(name);
|
||||
stream_.write(value);
|
||||
closeNode(name);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BinOArchive::operator()(const SStruct& ser, const char* name, [[maybe_unused]] const char* label)
|
||||
{
|
||||
openNode(name, false);
|
||||
ser(*this);
|
||||
closeNode(name, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BinOArchive::operator()(IContainer& ser, const char* name, [[maybe_unused]] const char* label)
|
||||
{
|
||||
openNode(name, false);
|
||||
|
||||
unsigned int size = (unsigned int)ser.size();
|
||||
if (size < SIZE16)
|
||||
{
|
||||
stream_.write((unsigned char)size);
|
||||
}
|
||||
else if (size < 0x10000)
|
||||
{
|
||||
stream_.write(SIZE16);
|
||||
stream_.write((unsigned short)size);
|
||||
}
|
||||
else
|
||||
{
|
||||
stream_.write(SIZE32);
|
||||
stream_.write(size);
|
||||
}
|
||||
|
||||
if (strlen(name))
|
||||
{
|
||||
if (size > 0)
|
||||
{
|
||||
int i = 0;
|
||||
do
|
||||
{
|
||||
char elementName[16];
|
||||
azitoa(i++, elementName, AZ_ARRAY_SIZE(elementName), 10);
|
||||
ser(*this, elementName, "");
|
||||
} while (ser.next());
|
||||
}
|
||||
|
||||
closeNode(name, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (size > 0)
|
||||
{
|
||||
do
|
||||
{
|
||||
ser(*this, "", "");
|
||||
}
|
||||
while (ser.next());
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BinOArchive::operator()(IPointer& ptr, const char* name, [[maybe_unused]] const char* label)
|
||||
{
|
||||
openNode(name, false);
|
||||
|
||||
const char* typeName = ptr.registeredTypeName();
|
||||
if (!typeName)
|
||||
{
|
||||
typeName = "";
|
||||
}
|
||||
if (typeName[0] == '\0' && ptr.get())
|
||||
{
|
||||
CRY_ASSERT_MESSAGE(0, "Writing unregistered class. Use SERIALIZATION_CLASS_NAME macro for registration.");
|
||||
}
|
||||
|
||||
TypeID baseType = ptr.baseType();
|
||||
|
||||
if (ptr.get())
|
||||
{
|
||||
stream_ << typeName;
|
||||
stream_.write(char(0));
|
||||
ptr.serializer()(*this);
|
||||
}
|
||||
else
|
||||
{
|
||||
stream_.write(char(0));
|
||||
}
|
||||
|
||||
closeNode(name, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
BinIArchive::BinIArchive()
|
||||
: IArchive(INPUT | BINARY)
|
||||
, loadedData_(0)
|
||||
{
|
||||
}
|
||||
|
||||
BinIArchive::~BinIArchive()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
bool BinIArchive::load(const char* filename)
|
||||
{
|
||||
close();
|
||||
|
||||
FILE* f = nullptr;
|
||||
azfopen(&f, filename, "rb");
|
||||
|
||||
if (!f)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
fseek(f, 0, SEEK_END);
|
||||
size_t length = ftell(f);
|
||||
fseek(f, 0, SEEK_SET);
|
||||
if (length == 0)
|
||||
{
|
||||
fclose(f);
|
||||
return false;
|
||||
}
|
||||
loadedData_ = new char[length];
|
||||
if (fread((void*)loadedData_, 1, length, f) != length || !open(loadedData_, length))
|
||||
{
|
||||
close();
|
||||
fclose(f);
|
||||
return false;
|
||||
}
|
||||
fclose(f);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BinIArchive::open(const char* buffer, size_t size)
|
||||
{
|
||||
if (size < sizeof(int))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (*(unsigned*)(buffer) != BIN_MAGIC)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
buffer += sizeof(unsigned int);
|
||||
size -= sizeof(unsigned int);
|
||||
|
||||
blocks_.push_back(Block(buffer, (unsigned int)size));
|
||||
return true;
|
||||
}
|
||||
|
||||
void BinIArchive::close()
|
||||
{
|
||||
if (loadedData_)
|
||||
{
|
||||
delete[] loadedData_;
|
||||
}
|
||||
loadedData_ = 0;
|
||||
}
|
||||
|
||||
bool BinIArchive::openNode(const char* name)
|
||||
{
|
||||
Block block(0, 0);
|
||||
if (currentBlock().get(name, block))
|
||||
{
|
||||
blocks_.push_back(block);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void BinIArchive::closeNode([[maybe_unused]] const char* name, [[maybe_unused]] bool check)
|
||||
{
|
||||
YASLI_ASSERT(!check || currentBlock().validToClose());
|
||||
blocks_.pop_back();
|
||||
}
|
||||
|
||||
bool BinIArchive::operator()(bool& value, const char* name, [[maybe_unused]] const char* label)
|
||||
{
|
||||
if (!strlen(name))
|
||||
{
|
||||
read(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!openNode(name))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
read(value);
|
||||
closeNode(name);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BinIArchive::operator()(IString& value, const char* name, [[maybe_unused]] const char* label)
|
||||
{
|
||||
if (!strlen(name))
|
||||
{
|
||||
string str;
|
||||
read(str);
|
||||
value.set(str.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!openNode(name))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
string str;
|
||||
read(str);
|
||||
value.set(str.c_str());
|
||||
closeNode(name);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BinIArchive::operator()(IWString& value, const char* name, [[maybe_unused]] const char* label)
|
||||
{
|
||||
if (!strlen(name))
|
||||
{
|
||||
wstring str;
|
||||
read(str);
|
||||
value.set(str.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!openNode(name))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
wstring str;
|
||||
read(str);
|
||||
value.set(str.c_str());
|
||||
closeNode(name);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BinIArchive::operator()(float& value, const char* name, [[maybe_unused]] const char* label)
|
||||
{
|
||||
if (!strlen(name))
|
||||
{
|
||||
read(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!openNode(name))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
read(value);
|
||||
closeNode(name);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BinIArchive::operator()(double& value, const char* name, [[maybe_unused]] const char* label)
|
||||
{
|
||||
if (!strlen(name))
|
||||
{
|
||||
read(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!openNode(name))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
read(value);
|
||||
closeNode(name);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BinIArchive::operator()(int16& value, const char* name, [[maybe_unused]] const char* label)
|
||||
{
|
||||
if (!strlen(name))
|
||||
{
|
||||
read(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!openNode(name))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
read(value);
|
||||
closeNode(name);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BinIArchive::operator()(uint16& value, const char* name, [[maybe_unused]] const char* label)
|
||||
{
|
||||
if (!strlen(name))
|
||||
{
|
||||
read(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!openNode(name))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
read(value);
|
||||
closeNode(name);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool BinIArchive::operator()(int32& value, const char* name, [[maybe_unused]] const char* label)
|
||||
{
|
||||
if (!strlen(name))
|
||||
{
|
||||
read(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!openNode(name))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
read(value);
|
||||
closeNode(name);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BinIArchive::operator()(uint32& value, const char* name, [[maybe_unused]] const char* label)
|
||||
{
|
||||
if (!strlen(name))
|
||||
{
|
||||
read(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!openNode(name))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
read(value);
|
||||
closeNode(name);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BinIArchive::operator()(int64& value, const char* name, [[maybe_unused]] const char* label)
|
||||
{
|
||||
if (!strlen(name))
|
||||
{
|
||||
read(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!openNode(name))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
read(value);
|
||||
closeNode(name);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BinIArchive::operator()(uint64& value, const char* name, [[maybe_unused]] const char* label)
|
||||
{
|
||||
if (!strlen(name))
|
||||
{
|
||||
read(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!openNode(name))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
read(value);
|
||||
closeNode(name);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BinIArchive::operator()(int8& value, const char* name, [[maybe_unused]] const char* label)
|
||||
{
|
||||
if (!strlen(name))
|
||||
{
|
||||
read(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!openNode(name))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
read(value);
|
||||
closeNode(name);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BinIArchive::operator()(uint8& value, const char* name, [[maybe_unused]] const char* label)
|
||||
{
|
||||
if (!strlen(name))
|
||||
{
|
||||
read(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!openNode(name))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
read(value);
|
||||
closeNode(name);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BinIArchive::operator()(char& value, const char* name, [[maybe_unused]] const char* label)
|
||||
{
|
||||
if (!strlen(name))
|
||||
{
|
||||
read(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!openNode(name))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
read(value);
|
||||
closeNode(name);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BinIArchive::operator()(const SStruct& ser, const char* name, [[maybe_unused]] const char* label)
|
||||
{
|
||||
if (!strlen(name))
|
||||
{
|
||||
ser(*this);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!openNode(name))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
ser(*this);
|
||||
closeNode(name, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BinIArchive::operator()(IContainer& ser, const char* name, [[maybe_unused]] const char* label)
|
||||
{
|
||||
if (strlen(name))
|
||||
{
|
||||
if (!openNode(name))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t size = currentBlock().readPackedSize();
|
||||
ser.resize(size);
|
||||
|
||||
if (size > 0)
|
||||
{
|
||||
int i = 0;
|
||||
do
|
||||
{
|
||||
char elementName[16];
|
||||
azitoa(i++, elementName, AZ_ARRAY_SIZE(elementName), 10);
|
||||
ser(*this, elementName, "");
|
||||
}
|
||||
while (ser.next());
|
||||
}
|
||||
closeNode(name);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
size_t size = currentBlock().readPackedSize();
|
||||
ser.resize(size);
|
||||
if (size > 0)
|
||||
{
|
||||
do
|
||||
{
|
||||
ser(*this, "", "");
|
||||
}
|
||||
while (ser.next());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool BinIArchive::operator()(IPointer& ptr, const char* name, [[maybe_unused]] const char* label)
|
||||
{
|
||||
if (strlen(name) && !openNode(name))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
string typeName;
|
||||
read(typeName);
|
||||
if (ptr.get() && (typeName.empty() || strcmp(typeName.c_str(), ptr.registeredTypeName()) != 0))
|
||||
{
|
||||
ptr.create(""); // 0
|
||||
}
|
||||
if (!typeName.empty() && !ptr.get())
|
||||
{
|
||||
ptr.create(typeName.c_str());
|
||||
}
|
||||
|
||||
if (SStruct ser = ptr.serializer())
|
||||
{
|
||||
ser(*this);
|
||||
}
|
||||
|
||||
if (strlen(name))
|
||||
{
|
||||
closeNode(name);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned int BinIArchive::Block::readPackedSize()
|
||||
{
|
||||
unsigned char size8;
|
||||
read(size8);
|
||||
if (size8 < SIZE16)
|
||||
{
|
||||
return size8;
|
||||
}
|
||||
if (size8 == SIZE16)
|
||||
{
|
||||
unsigned short size16;
|
||||
read(size16);
|
||||
return size16;
|
||||
}
|
||||
unsigned int size32;
|
||||
read(size32);
|
||||
return size32;
|
||||
}
|
||||
|
||||
bool BinIArchive::Block::get(const char* name, Block& block)
|
||||
{
|
||||
if (begin_ == end_)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
complex_ = true;
|
||||
unsigned short hashName = calcHash(name);
|
||||
const char* currInitial = curr_;
|
||||
bool restarted = false;
|
||||
for (;; )
|
||||
{
|
||||
if (curr_ >= end_)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned short hash;
|
||||
read(hash);
|
||||
unsigned int size = readPackedSize();
|
||||
|
||||
const char* currPrev = curr_;
|
||||
if ((curr_ += size) == end_)
|
||||
{
|
||||
if (restarted)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
curr_ = begin_;
|
||||
restarted = true;
|
||||
}
|
||||
|
||||
//ASSERT(curr_ < end_);
|
||||
|
||||
if (hash == hashName)
|
||||
{
|
||||
block = Block(currPrev, size);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (curr_ == currInitial)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,180 +0,0 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
// Original file Copyright Crytek GMBH or its affiliates, used under license.
|
||||
|
||||
#pragma once
|
||||
|
||||
// For tags 16-bit xor-hash is used, with check for uniquness in debug
|
||||
// Block size is automatic: 8, 16 or 32 bits
|
||||
|
||||
#include "Serialization/IArchive.h"
|
||||
#include "MemoryWriter.h"
|
||||
|
||||
namespace Serialization {
|
||||
inline unsigned short calcHash(const char* str)
|
||||
{
|
||||
unsigned short hash = 0;
|
||||
const unsigned short* p = (const unsigned short*)(str);
|
||||
for (;; )
|
||||
{
|
||||
unsigned short w = *p++;
|
||||
if (!(w & 0xff))
|
||||
{
|
||||
break;
|
||||
}
|
||||
hash ^= w;
|
||||
if (!(w & 0xff00))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
class BinOArchive
|
||||
: public IArchive
|
||||
{
|
||||
public:
|
||||
|
||||
BinOArchive();
|
||||
~BinOArchive() {}
|
||||
|
||||
void clear();
|
||||
size_t length() const;
|
||||
const char* buffer() const { return stream_.buffer(); }
|
||||
bool save(const char* fileName);
|
||||
|
||||
bool operator()(bool& value, const char* name, const char* label);
|
||||
bool operator()(IString& value, const char* name, const char* label);
|
||||
bool operator()(IWString& value, const char* name, const char* label);
|
||||
bool operator()(float& value, const char* name, const char* label);
|
||||
bool operator()(double& value, const char* name, const char* label);
|
||||
bool operator()(int32& value, const char* name, const char* label);
|
||||
bool operator()(uint32& value, const char* name, const char* label);
|
||||
bool operator()(int16& value, const char* name, const char* label);
|
||||
bool operator()(uint16& value, const char* name, const char* label);
|
||||
bool operator()(int64& value, const char* name, const char* label);
|
||||
bool operator()(uint64& value, const char* name, const char* label);
|
||||
|
||||
bool operator()(int8& value, const char* name, const char* label);
|
||||
bool operator()(uint8& value, const char* name, const char* label);
|
||||
bool operator()(char& value, const char* name, const char* label);
|
||||
|
||||
bool operator()(const SStruct& ser, const char* name, const char* label);
|
||||
bool operator()(IContainer& ser, const char* name, const char* label);
|
||||
bool operator()(IPointer& ptr, const char* name, const char* label);
|
||||
|
||||
using IArchive::operator();
|
||||
|
||||
private:
|
||||
void openContainer(const char* name, int size, const char* typeName);
|
||||
void openNode(const char* name, bool size8 = true);
|
||||
void closeNode(const char* name, bool size8 = true);
|
||||
|
||||
std::vector<unsigned int> blockSizeOffsets_;
|
||||
MemoryWriter stream_;
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class BinIArchive
|
||||
: public IArchive
|
||||
{
|
||||
public:
|
||||
|
||||
BinIArchive();
|
||||
~BinIArchive();
|
||||
|
||||
bool load(const char* fileName);
|
||||
bool open(const char* buffer, size_t length); // doesn't copy the buffer
|
||||
bool open(const BinOArchive& ar) { return open(ar.buffer(), ar.length()); }
|
||||
void close();
|
||||
|
||||
bool operator()(bool& value, const char* name, const char* label);
|
||||
bool operator()(IString& value, const char* name, const char* label);
|
||||
bool operator()(IWString& value, const char* name, const char* label);
|
||||
bool operator()(float& value, const char* name, const char* label);
|
||||
bool operator()(double& value, const char* name, const char* label);
|
||||
bool operator()(int16& value, const char* name, const char* label);
|
||||
bool operator()(uint16& value, const char* name, const char* label);
|
||||
bool operator()(int32& value, const char* name, const char* label);
|
||||
bool operator()(uint32& value, const char* name, const char* label);
|
||||
bool operator()(int64& value, const char* name, const char* label);
|
||||
bool operator()(uint64& value, const char* name, const char* label);
|
||||
|
||||
bool operator()(int8& value, const char* name, const char* label);
|
||||
bool operator()(uint8& value, const char* name, const char* label);
|
||||
bool operator()(char& value, const char* name, const char* label);
|
||||
|
||||
bool operator()(const SStruct& ser, const char* name, const char* label);
|
||||
bool operator()(IContainer& ser, const char* name, const char* label);
|
||||
bool operator()(IPointer& ptr, const char* name, const char* label);
|
||||
|
||||
using IArchive::operator();
|
||||
|
||||
private:
|
||||
class Block
|
||||
{
|
||||
public:
|
||||
Block(const char* data, int size)
|
||||
: begin_(data)
|
||||
, end_(data + size)
|
||||
, curr_(data)
|
||||
, complex_(false) {}
|
||||
|
||||
bool get(const char* name, Block& block);
|
||||
|
||||
void read(void* data, int size)
|
||||
{
|
||||
YASLI_ASSERT(curr_ + size <= end_);
|
||||
memcpy(data, curr_, size);
|
||||
curr_ += size;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void read(T& x){ read(&x, sizeof(x)); }
|
||||
|
||||
void read(string& s)
|
||||
{
|
||||
YASLI_ASSERT(curr_ + strlen(curr_) < end_);
|
||||
s = curr_;
|
||||
curr_ += strlen(curr_) + 1;
|
||||
}
|
||||
void read(wstring& s)
|
||||
{
|
||||
YASLI_ASSERT(curr_ + sizeof(wchar_t) * wcslen((wchar_t*)curr_) < end_);
|
||||
s = (wchar_t*)curr_;
|
||||
curr_ += (wcslen((wchar_t*)curr_) + 1) * sizeof(wchar_t);
|
||||
}
|
||||
|
||||
unsigned int readPackedSize();
|
||||
|
||||
bool validToClose() const { return complex_ || curr_ == end_; }
|
||||
|
||||
private:
|
||||
const char* begin_;
|
||||
const char* end_;
|
||||
const char* curr_;
|
||||
bool complex_;
|
||||
};
|
||||
|
||||
typedef std::vector<Block> Blocks;
|
||||
Blocks blocks_;
|
||||
const char* loadedData_;
|
||||
|
||||
bool openNode(const char* name);
|
||||
void closeNode(const char* name, bool check = true);
|
||||
Block& currentBlock() { return blocks_.back(); }
|
||||
template<class T>
|
||||
void read(T& t) { currentBlock().read(t); }
|
||||
};
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,95 +0,0 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
// Original file Copyright Crytek GMBH or its affiliates, used under license.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Serialization/IArchive.h"
|
||||
#include "MemoryReader.h"
|
||||
#include "Token.h"
|
||||
#include <memory>
|
||||
|
||||
namespace Serialization {
|
||||
class MemoryReader;
|
||||
|
||||
class JSONIArchive
|
||||
: public IArchive
|
||||
{
|
||||
public:
|
||||
JSONIArchive();
|
||||
~JSONIArchive();
|
||||
|
||||
bool load(const char* filename);
|
||||
bool open(const char* buffer, size_t length, bool free = false);
|
||||
|
||||
// virtuals:
|
||||
bool operator()(bool& value, const char* name = "", const char* label = 0);
|
||||
bool operator()(IString& value, const char* name = "", const char* label = 0);
|
||||
bool operator()(IWString& value, const char* name = "", const char* label = 0);
|
||||
bool operator()(float& value, const char* name = "", const char* label = 0);
|
||||
bool operator()(double& value, const char* name = "", const char* label = 0);
|
||||
bool operator()(int16& value, const char* name = "", const char* label = 0);
|
||||
bool operator()(uint16& value, const char* name = "", const char* label = 0);
|
||||
bool operator()(int32& value, const char* name = "", const char* label = 0);
|
||||
bool operator()(uint32& value, const char* name = "", const char* label = 0);
|
||||
bool operator()(int64& value, const char* name = "", const char* label = 0);
|
||||
bool operator()(uint64& value, const char* name = "", const char* label = 0);
|
||||
|
||||
bool operator()(int8& value, const char* name = "", const char* label = 0);
|
||||
bool operator()(uint8& value, const char* name = "", const char* label = 0);
|
||||
bool operator()(char& value, const char* name = "", const char* label = 0);
|
||||
|
||||
bool operator()(const SStruct& ser, const char* name = "", const char* label = 0);
|
||||
bool operator()(const SBlackBox& ser, const char* name = "", const char* label = 0);
|
||||
bool operator()(IContainer& ser, const char* name = "", const char* label = 0);
|
||||
bool operator()(IKeyValue& ser, const char* name = "", const char* label = 0);
|
||||
bool operator()(IPointer& ser, const char* name = "", const char* label = 0);
|
||||
|
||||
using IArchive::operator();
|
||||
private:
|
||||
bool findName(const char* name, Token* outName = 0);
|
||||
bool openBracket();
|
||||
bool closeBracket();
|
||||
|
||||
bool openContainerBracket();
|
||||
bool closeContainerBracket();
|
||||
|
||||
void checkValueToken();
|
||||
bool checkStringValueToken();
|
||||
void readToken();
|
||||
void putToken();
|
||||
int line(const char* position) const;
|
||||
bool isName(Token token) const;
|
||||
|
||||
bool expect(char token);
|
||||
void skipBlock();
|
||||
|
||||
struct Level
|
||||
{
|
||||
const char* start;
|
||||
const char* firstToken;
|
||||
bool isContainer;
|
||||
bool isKeyValue;
|
||||
Level()
|
||||
: isContainer(false)
|
||||
, isKeyValue(false) {}
|
||||
};
|
||||
typedef std::vector<Level> Stack;
|
||||
Stack stack_;
|
||||
|
||||
std::unique_ptr<MemoryReader> reader_;
|
||||
Token token_;
|
||||
std::vector<char> unescapeBuffer_;
|
||||
string filename_;
|
||||
void* buffer_;
|
||||
};
|
||||
}
|
||||
@@ -1,828 +0,0 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
// Original file Copyright Crytek GMBH or its affiliates, used under license.
|
||||
|
||||
#include "CrySystem_precompiled.h"
|
||||
#include "JSONOArchive.h"
|
||||
#include "MemoryWriter.h"
|
||||
#include "Serialization/KeyValue.h"
|
||||
#include "Serialization/ClassFactory.h"
|
||||
#include "Serialization/BlackBox.h"
|
||||
#include <float.h>
|
||||
|
||||
namespace Serialization {
|
||||
// Some of non-latin1 characters here are not escaped to
|
||||
// keep compatibility with 8-bit local encoding (e.g. windows-1251)
|
||||
static const char* escapeTable[256] = {
|
||||
"\\0" /* 0x00: */,
|
||||
"\\x01" /* 0x01: */,
|
||||
"\\x02" /* 0x02: */,
|
||||
"\\x03" /* 0x03: */,
|
||||
"\\x04" /* 0x04: */,
|
||||
"\\x05" /* 0x05: */,
|
||||
"\\x06" /* 0x06: */,
|
||||
"\\x07" /* 0x07: */,
|
||||
"\\x08" /* 0x08: */,
|
||||
"\\t" /* 0x09: \t */,
|
||||
"\\n" /* 0x0A: \n */,
|
||||
"\\x0B" /* 0x0B: */,
|
||||
"\\x0C" /* 0x0C: */,
|
||||
"\\r" /* 0x0D: */,
|
||||
"\\x0E" /* 0x0E: */,
|
||||
"\\x0F" /* 0x0F: */,
|
||||
|
||||
|
||||
"\\x10" /* 0x10: */,
|
||||
"\\x11" /* 0x11: */,
|
||||
"\\x12" /* 0x12: */,
|
||||
"\\x13" /* 0x13: */,
|
||||
"\\x14" /* 0x14: */,
|
||||
"\\x15" /* 0x15: */,
|
||||
"\\x16" /* 0x16: */,
|
||||
"\\x17" /* 0x17: */,
|
||||
"\\x18" /* 0x18: */,
|
||||
"\\x19" /* 0x19: */,
|
||||
"\\x1A" /* 0x1A: */,
|
||||
"\\x1B" /* 0x1B: */,
|
||||
"\\x1C" /* 0x1C: */,
|
||||
"\\x1D" /* 0x1D: */,
|
||||
"\\x1E" /* 0x1E: */,
|
||||
"\\x1F" /* 0x1F: */,
|
||||
|
||||
|
||||
" " /* 0x20: */,
|
||||
"!" /* 0x21: ! */,
|
||||
"\\\"" /* 0x22: " */,
|
||||
"#" /* 0x23: # */,
|
||||
"$" /* 0x24: $ */,
|
||||
"%" /* 0x25: % */,
|
||||
"&" /* 0x26: & */,
|
||||
"'" /* 0x27: ' */,
|
||||
"(" /* 0x28: ( */,
|
||||
")" /* 0x29: ) */,
|
||||
"*" /* 0x2A: * */,
|
||||
"+" /* 0x2B: + */,
|
||||
"," /* 0x2C: , */,
|
||||
"-" /* 0x2D: - */,
|
||||
"." /* 0x2E: . */,
|
||||
"/" /* 0x2F: / */,
|
||||
|
||||
|
||||
"0" /* 0x30: 0 */,
|
||||
"1" /* 0x31: 1 */,
|
||||
"2" /* 0x32: 2 */,
|
||||
"3" /* 0x33: 3 */,
|
||||
"4" /* 0x34: 4 */,
|
||||
"5" /* 0x35: 5 */,
|
||||
"6" /* 0x36: 6 */,
|
||||
"7" /* 0x37: 7 */,
|
||||
"8" /* 0x38: 8 */,
|
||||
"9" /* 0x39: 9 */,
|
||||
":" /* 0x3A: : */,
|
||||
";" /* 0x3B: ; */,
|
||||
"<" /* 0x3C: < */,
|
||||
"=" /* 0x3D: = */,
|
||||
">" /* 0x3E: > */,
|
||||
"?" /* 0x3F: ? */,
|
||||
|
||||
|
||||
"@" /* 0x40: @ */,
|
||||
"A" /* 0x41: A */,
|
||||
"B" /* 0x42: B */,
|
||||
"C" /* 0x43: C */,
|
||||
"D" /* 0x44: D */,
|
||||
"E" /* 0x45: E */,
|
||||
"F" /* 0x46: F */,
|
||||
"G" /* 0x47: G */,
|
||||
"H" /* 0x48: H */,
|
||||
"I" /* 0x49: I */,
|
||||
"J" /* 0x4A: J */,
|
||||
"K" /* 0x4B: K */,
|
||||
"L" /* 0x4C: L */,
|
||||
"M" /* 0x4D: M */,
|
||||
"N" /* 0x4E: N */,
|
||||
"O" /* 0x4F: O */,
|
||||
|
||||
|
||||
"P" /* 0x50: P */,
|
||||
"Q" /* 0x51: Q */,
|
||||
"R" /* 0x52: R */,
|
||||
"S" /* 0x53: S */,
|
||||
"T" /* 0x54: T */,
|
||||
"U" /* 0x55: U */,
|
||||
"V" /* 0x56: V */,
|
||||
"W" /* 0x57: W */,
|
||||
"X" /* 0x58: X */,
|
||||
"Y" /* 0x59: Y */,
|
||||
"Z" /* 0x5A: Z */,
|
||||
"[" /* 0x5B: [ */,
|
||||
"\\\\" /* 0x5C: \ */,
|
||||
"]" /* 0x5D: ] */,
|
||||
"^" /* 0x5E: ^ */,
|
||||
"_" /* 0x5F: _ */,
|
||||
|
||||
|
||||
"`" /* 0x60: ` */,
|
||||
"a" /* 0x61: a */,
|
||||
"b" /* 0x62: b */,
|
||||
"c" /* 0x63: c */,
|
||||
"d" /* 0x64: d */,
|
||||
"e" /* 0x65: e */,
|
||||
"f" /* 0x66: f */,
|
||||
"g" /* 0x67: g */,
|
||||
"h" /* 0x68: h */,
|
||||
"i" /* 0x69: i */,
|
||||
"j" /* 0x6A: j */,
|
||||
"k" /* 0x6B: k */,
|
||||
"l" /* 0x6C: l */,
|
||||
"m" /* 0x6D: m */,
|
||||
"n" /* 0x6E: n */,
|
||||
"o" /* 0x6F: o */,
|
||||
|
||||
|
||||
"p" /* 0x70: p */,
|
||||
"q" /* 0x71: q */,
|
||||
"r" /* 0x72: r */,
|
||||
"s" /* 0x73: s */,
|
||||
"t" /* 0x74: t */,
|
||||
"u" /* 0x75: u */,
|
||||
"v" /* 0x76: v */,
|
||||
"w" /* 0x77: w */,
|
||||
"x" /* 0x78: x */,
|
||||
"y" /* 0x79: y */,
|
||||
"z" /* 0x7A: z */,
|
||||
"{" /* 0x7B: { */,
|
||||
"|" /* 0x7C: | */,
|
||||
"}" /* 0x7D: } */,
|
||||
"~" /* 0x7E: ~ */,
|
||||
"\x7F" /* 0x7F: */, // for utf-8
|
||||
|
||||
|
||||
"\x80" /* 0x80: */,
|
||||
"\x81" /* 0x81: */,
|
||||
"\x82" /* 0x82: */,
|
||||
"\x83" /* 0x83: */,
|
||||
"\x84" /* 0x84: */,
|
||||
"\x85" /* 0x85: */,
|
||||
"\x86" /* 0x86: */,
|
||||
"\x87" /* 0x87: */,
|
||||
"\x88" /* 0x88: */,
|
||||
"\x89" /* 0x89: */,
|
||||
"\x8A" /* 0x8A: */,
|
||||
"\x8B" /* 0x8B: */,
|
||||
"\x8C" /* 0x8C: */,
|
||||
"\x8D" /* 0x8D: */,
|
||||
"\x8E" /* 0x8E: */,
|
||||
"\x8F" /* 0x8F: */,
|
||||
|
||||
|
||||
"\x90" /* 0x90: */,
|
||||
"\x91" /* 0x91: */,
|
||||
"\x92" /* 0x92: */,
|
||||
"\x93" /* 0x93: */,
|
||||
"\x94" /* 0x94: */,
|
||||
"\x95" /* 0x95: */,
|
||||
"\x96" /* 0x96: */,
|
||||
"\x97" /* 0x97: */,
|
||||
"\x98" /* 0x98: */,
|
||||
"\x99" /* 0x99: */,
|
||||
"\x9A" /* 0x9A: */,
|
||||
"\x9B" /* 0x9B: */,
|
||||
"\x9C" /* 0x9C: */,
|
||||
"\x9D" /* 0x9D: */,
|
||||
"\x9E" /* 0x9E: */,
|
||||
"\x9F" /* 0x9F: */,
|
||||
|
||||
|
||||
"\xA0" /* 0xA0: */,
|
||||
"\xA1" /* 0xA1: */,
|
||||
"\xA2" /* 0xA2: */,
|
||||
"\xA3" /* 0xA3: */,
|
||||
"\xA4" /* 0xA4: */,
|
||||
"\xA5" /* 0xA5: */,
|
||||
"\xA6" /* 0xA6: */,
|
||||
"\xA7" /* 0xA7: */,
|
||||
"\xA8" /* 0xA8: */,
|
||||
"\xA9" /* 0xA9: */,
|
||||
"\xAA" /* 0xAA: */,
|
||||
"\xAB" /* 0xAB: */,
|
||||
"\xAC" /* 0xAC: */,
|
||||
"\xAD" /* 0xAD: */,
|
||||
"\xAE" /* 0xAE: */,
|
||||
"\xAF" /* 0xAF: */,
|
||||
|
||||
|
||||
"\xB0" /* 0xB0: */,
|
||||
"\xB1" /* 0xB1: */,
|
||||
"\xB2" /* 0xB2: */,
|
||||
"\xB3" /* 0xB3: */,
|
||||
"\xB4" /* 0xB4: */,
|
||||
"\xB5" /* 0xB5: */,
|
||||
"\xB6" /* 0xB6: */,
|
||||
"\xB7" /* 0xB7: */,
|
||||
"\xB8" /* 0xB8: */,
|
||||
"\xB9" /* 0xB9: */,
|
||||
"\xBA" /* 0xBA: */,
|
||||
"\xBB" /* 0xBB: */,
|
||||
"\xBC" /* 0xBC: */,
|
||||
"\xBD" /* 0xBD: */,
|
||||
"\xBE" /* 0xBE: */,
|
||||
"\xBF" /* 0xBF: */,
|
||||
|
||||
|
||||
"\xC0" /* 0xC0: */,
|
||||
"\xC1" /* 0xC1: */,
|
||||
"\xC2" /* 0xC2: */,
|
||||
"\xC3" /* 0xC3: */,
|
||||
"\xC4" /* 0xC4: */,
|
||||
"\xC5" /* 0xC5: */,
|
||||
"\xC6" /* 0xC6: */,
|
||||
"\xC7" /* 0xC7: */,
|
||||
"\xC8" /* 0xC8: */,
|
||||
"\xC9" /* 0xC9: */,
|
||||
"\xCA" /* 0xCA: */,
|
||||
"\xCB" /* 0xCB: */,
|
||||
"\xCC" /* 0xCC: */,
|
||||
"\xCD" /* 0xCD: */,
|
||||
"\xCE" /* 0xCE: */,
|
||||
"\xCF" /* 0xCF: */,
|
||||
|
||||
|
||||
"\xD0" /* 0xD0: */,
|
||||
"\xD1" /* 0xD1: */,
|
||||
"\xD2" /* 0xD2: */,
|
||||
"\xD3" /* 0xD3: */,
|
||||
"\xD4" /* 0xD4: */,
|
||||
"\xD5" /* 0xD5: */,
|
||||
"\xD6" /* 0xD6: */,
|
||||
"\xD7" /* 0xD7: */,
|
||||
"\xD8" /* 0xD8: */,
|
||||
"\xD9" /* 0xD9: */,
|
||||
"\xDA" /* 0xDA: */,
|
||||
"\xDB" /* 0xDB: */,
|
||||
"\xDC" /* 0xDC: */,
|
||||
"\xDD" /* 0xDD: */,
|
||||
"\xDE" /* 0xDE: */,
|
||||
"\xDF" /* 0xDF: */,
|
||||
|
||||
|
||||
"\xE0" /* 0xE0: */,
|
||||
"\xE1" /* 0xE1: */,
|
||||
"\xE2" /* 0xE2: */,
|
||||
"\xE3" /* 0xE3: */,
|
||||
"\xE4" /* 0xE4: */,
|
||||
"\xE5" /* 0xE5: */,
|
||||
"\xE6" /* 0xE6: */,
|
||||
"\xE7" /* 0xE7: */,
|
||||
"\xE8" /* 0xE8: */,
|
||||
"\xE9" /* 0xE9: */,
|
||||
"\xEA" /* 0xEA: */,
|
||||
"\xEB" /* 0xEB: */,
|
||||
"\xEC" /* 0xEC: */,
|
||||
"\xED" /* 0xED: */,
|
||||
"\xEE" /* 0xEE: */,
|
||||
"\xEF" /* 0xEF: */,
|
||||
|
||||
|
||||
"\xF0" /* 0xF0: */,
|
||||
"\xF1" /* 0xF1: */,
|
||||
"\xF2" /* 0xF2: */,
|
||||
"\xF3" /* 0xF3: */,
|
||||
"\xF4" /* 0xF4: */,
|
||||
"\xF5" /* 0xF5: */,
|
||||
"\xF6" /* 0xF6: */,
|
||||
"\xF7" /* 0xF7: */,
|
||||
"\xF8" /* 0xF8: */,
|
||||
"\xF9" /* 0xF9: */,
|
||||
"\xFA" /* 0xFA: */,
|
||||
"\xFB" /* 0xFB: */,
|
||||
"\xFC" /* 0xFC: */,
|
||||
"\xFD" /* 0xFD: */,
|
||||
"\xFE" /* 0xFE: */,
|
||||
"\xFF" /* 0xFF: */
|
||||
};
|
||||
|
||||
static void escapeString(MemoryWriter& dest, const char* begin, const char* end)
|
||||
{
|
||||
while (begin != end)
|
||||
{
|
||||
const char* str = escapeTable[(unsigned char)(*begin)];
|
||||
dest.write(str);
|
||||
++begin;
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
static const int TAB_WIDTH = 2;
|
||||
|
||||
JSONOArchive::JSONOArchive(int textWidth, const char* header)
|
||||
: IArchive(OUTPUT | TEXT)
|
||||
, header_(header)
|
||||
, textWidth_(textWidth)
|
||||
, compactOffset_(0)
|
||||
{
|
||||
buffer_.reset(new MemoryWriter(1024, true));
|
||||
if (header_)
|
||||
{
|
||||
(*buffer_) << header_;
|
||||
}
|
||||
|
||||
YASLI_ASSERT(stack_.empty());
|
||||
stack_.push_back(Level(false, 0, 0));
|
||||
}
|
||||
|
||||
JSONOArchive::~JSONOArchive()
|
||||
{
|
||||
}
|
||||
|
||||
bool JSONOArchive::save(const char* fileName)
|
||||
{
|
||||
YASLI_ESCAPE(fileName && strlen(fileName) > 0, return false);
|
||||
YASLI_ESCAPE(stack_.size() == 1, return false);
|
||||
YASLI_ESCAPE(buffer_.get() != 0, return false);
|
||||
YASLI_ESCAPE(buffer_->position() <= buffer_->size(), return false);
|
||||
stack_.pop_back();
|
||||
FILE* file = nullptr;
|
||||
azfopen(&file, fileName, "wb");
|
||||
if (file)
|
||||
{
|
||||
if (fwrite(buffer_->c_str(), 1, buffer_->position(), file) != buffer_->position())
|
||||
{
|
||||
fclose(file);
|
||||
return false;
|
||||
}
|
||||
fclose(file);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const char* JSONOArchive::c_str() const
|
||||
{
|
||||
return buffer_->c_str();
|
||||
}
|
||||
|
||||
size_t JSONOArchive::length() const
|
||||
{
|
||||
return buffer_->position();
|
||||
}
|
||||
|
||||
void JSONOArchive::openBracket()
|
||||
{
|
||||
*buffer_ << "{";
|
||||
}
|
||||
|
||||
void JSONOArchive::closeBracket()
|
||||
{
|
||||
*buffer_ << "}";
|
||||
}
|
||||
|
||||
void JSONOArchive::openContainerBracket()
|
||||
{
|
||||
*buffer_ << "[";
|
||||
}
|
||||
|
||||
void JSONOArchive::closeContainerBracket()
|
||||
{
|
||||
*buffer_ << "]";
|
||||
}
|
||||
|
||||
void JSONOArchive::placeName(const char* name)
|
||||
{
|
||||
if (stack_.back().isKeyValue)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if ((name[0] != '\0' || !stack_.back().isContainer) && stack_.size() > 1)
|
||||
{
|
||||
*buffer_ << "\"";
|
||||
*buffer_ << name;
|
||||
*buffer_ << "\": ";
|
||||
stack_.back().nameIndex += 1;
|
||||
}
|
||||
}
|
||||
|
||||
void JSONOArchive::placeIndent(bool putComma)
|
||||
{
|
||||
if (stack_.back().isKeyValue)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (putComma && stack_.back().elementIndex > 0)
|
||||
{
|
||||
*buffer_ << ",";
|
||||
}
|
||||
if (buffer_->position() > 0)
|
||||
{
|
||||
*buffer_ << "\n";
|
||||
}
|
||||
int count = int(stack_.size() - 1);
|
||||
stack_.back().indentCount += count;
|
||||
stack_.back().elementIndex += 1;
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
*buffer_ << "\t";
|
||||
}
|
||||
compactOffset_ = 0;
|
||||
}
|
||||
|
||||
void JSONOArchive::placeIndentCompact(bool putComma)
|
||||
{
|
||||
if (stack_.back().isKeyValue)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (putComma && stack_.back().elementIndex > 0)
|
||||
{
|
||||
*buffer_ << ",";
|
||||
}
|
||||
if ((compactOffset_ % 32) != 0 && stack_.back().isContainer)
|
||||
{
|
||||
*buffer_ << " ";
|
||||
compactOffset_ += 1;
|
||||
stack_.back().elementIndex += 1;
|
||||
}
|
||||
else if (buffer_->size())
|
||||
{
|
||||
*buffer_ << "\n";
|
||||
int count = int(stack_.size() - 1);
|
||||
stack_.back().indentCount += count /* * TAB_WIDTH*/;
|
||||
stack_.back().elementIndex += 1;
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
*buffer_ << "\t";
|
||||
}
|
||||
compactOffset_ = 1;
|
||||
}
|
||||
}
|
||||
|
||||
bool JSONOArchive::operator()(bool& value, const char* name, [[maybe_unused]] const char* label)
|
||||
{
|
||||
placeIndent();
|
||||
placeName(name);
|
||||
*buffer_ << (value ? "true" : "false");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool JSONOArchive::operator()(IString& value, const char* name, [[maybe_unused]] const char* label)
|
||||
{
|
||||
placeIndent();
|
||||
placeName(name);
|
||||
(*buffer_) << "\"";
|
||||
const char* str = value.get();
|
||||
escapeString(*buffer_, str, str + strlen(value.get()));
|
||||
(*buffer_) << "\"";
|
||||
return true;
|
||||
}
|
||||
|
||||
inline char* writeUtf16ToUtf8(char* s, unsigned int ch)
|
||||
{
|
||||
const unsigned char byteMark = 0x80;
|
||||
const unsigned char byteMask = 0xBF;
|
||||
|
||||
size_t len;
|
||||
|
||||
if (ch < 0x80)
|
||||
{
|
||||
len = 1;
|
||||
}
|
||||
else if (ch < 0x800)
|
||||
{
|
||||
len = 2;
|
||||
}
|
||||
else if (ch < 0x10000)
|
||||
{
|
||||
len = 3;
|
||||
}
|
||||
else if (ch < 0x200000)
|
||||
{
|
||||
len = 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
return s;
|
||||
}
|
||||
|
||||
s += len;
|
||||
|
||||
const unsigned char firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
|
||||
switch (len)
|
||||
{
|
||||
case 4:
|
||||
*--s = (char)((ch | byteMark) & byteMask);
|
||||
ch >>= 6;
|
||||
case 3:
|
||||
*--s = (char)((ch | byteMark) & byteMask);
|
||||
ch >>= 6;
|
||||
case 2:
|
||||
*--s = (char)((ch | byteMark) & byteMask);
|
||||
ch >>= 6;
|
||||
case 1:
|
||||
*--s = (char)(ch | firstByteMark[len]);
|
||||
}
|
||||
|
||||
return s + len;
|
||||
}
|
||||
|
||||
bool JSONOArchive::operator()(IWString& value, const char* name, [[maybe_unused]] const char* label)
|
||||
{
|
||||
placeIndent();
|
||||
placeName(name);
|
||||
(*buffer_) << "\"";
|
||||
|
||||
const wchar_t* in = value.get();
|
||||
for (; *in; ++in)
|
||||
{
|
||||
char buf[6];
|
||||
escapeString(*buffer_, buf, writeUtf16ToUtf8(buf, *in));
|
||||
}
|
||||
|
||||
(*buffer_) << "\"";
|
||||
return true;
|
||||
}
|
||||
|
||||
bool JSONOArchive::operator()(float& value, const char* name, [[maybe_unused]] const char* label)
|
||||
{
|
||||
placeIndentCompact();
|
||||
placeName(name);
|
||||
(*buffer_) << value;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool JSONOArchive::operator()(double& value, const char* name, [[maybe_unused]] const char* label)
|
||||
{
|
||||
placeIndentCompact();
|
||||
placeName(name);
|
||||
(*buffer_) << value;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool JSONOArchive::operator()(int32& value, const char* name, [[maybe_unused]] const char* label)
|
||||
{
|
||||
placeIndentCompact();
|
||||
placeName(name);
|
||||
(*buffer_) << value;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool JSONOArchive::operator()(uint32& value, const char* name, [[maybe_unused]] const char* label)
|
||||
{
|
||||
placeIndentCompact();
|
||||
placeName(name);
|
||||
(*buffer_) << value;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool JSONOArchive::operator()(int16& value, const char* name, [[maybe_unused]] const char* label)
|
||||
{
|
||||
placeIndentCompact();
|
||||
placeName(name);
|
||||
(*buffer_) << value;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool JSONOArchive::operator()(uint16& value, const char* name, [[maybe_unused]] const char* label)
|
||||
{
|
||||
placeIndentCompact();
|
||||
placeName(name);
|
||||
(*buffer_) << value;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool JSONOArchive::operator()(int64& value, const char* name, [[maybe_unused]] const char* label)
|
||||
{
|
||||
placeIndentCompact();
|
||||
placeName(name);
|
||||
(*buffer_) << value;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool JSONOArchive::operator()(uint64& value, const char* name, [[maybe_unused]] const char* label)
|
||||
{
|
||||
placeIndentCompact();
|
||||
placeName(name);
|
||||
(*buffer_) << value;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool JSONOArchive::operator()(uint8& value, const char* name, [[maybe_unused]] const char* label)
|
||||
{
|
||||
placeIndentCompact();
|
||||
placeName(name);
|
||||
(*buffer_) << value;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool JSONOArchive::operator()(int8& value, const char* name, [[maybe_unused]] const char* label)
|
||||
{
|
||||
placeIndentCompact();
|
||||
placeName(name);
|
||||
(*buffer_) << value;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool JSONOArchive::operator()(char& value, const char* name, [[maybe_unused]] const char* label)
|
||||
{
|
||||
placeIndentCompact();
|
||||
placeName(name);
|
||||
(*buffer_) << value;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool JSONOArchive::operator()(const SStruct& ser, const char* name, [[maybe_unused]] const char* label)
|
||||
{
|
||||
placeIndent();
|
||||
placeName(name);
|
||||
std::size_t position = buffer_->position();
|
||||
openBracket();
|
||||
stack_.push_back(Level(false, position, int(strlen(name) + 2 * (name[0] & 1) + (stack_.size() - 1) * TAB_WIDTH + 2)));
|
||||
|
||||
YASLI_ASSERT(ser);
|
||||
ser(*this);
|
||||
|
||||
bool joined = joinLinesIfPossible();
|
||||
bool noNames = stack_.back().nameIndex == 0;
|
||||
if (noNames)
|
||||
{
|
||||
if (stack_.size() != 2)
|
||||
{
|
||||
buffer_->buffer()[stack_.back().startPosition] = '[';
|
||||
}
|
||||
}
|
||||
stack_.pop_back();
|
||||
if (!joined)
|
||||
{
|
||||
placeIndent(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
*buffer_ << " ";
|
||||
}
|
||||
if (noNames)
|
||||
{
|
||||
closeContainerBracket();
|
||||
}
|
||||
else
|
||||
{
|
||||
closeBracket();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool JSONOArchive::operator()(const SBlackBox& box, const char* name, [[maybe_unused]] const char* label)
|
||||
{
|
||||
if (strcmp(box.format, "json") != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (box.size == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
placeIndent();
|
||||
placeName(name);
|
||||
return buffer_->write(box.data, box.size);
|
||||
}
|
||||
|
||||
bool JSONOArchive::operator()(IKeyValue& keyValue, [[maybe_unused]] const char* name, [[maybe_unused]] const char* label)
|
||||
{
|
||||
placeIndent();
|
||||
|
||||
*buffer_ << "\"";
|
||||
*buffer_ << keyValue.get();
|
||||
*buffer_ << "\": ";
|
||||
stack_.back().nameIndex += 1;
|
||||
|
||||
stack_.back().isKeyValue = true;
|
||||
keyValue.serializeValue(*this, "", 0);
|
||||
stack_.back().isKeyValue = false;
|
||||
if (stack_.back().isContainer)
|
||||
{
|
||||
stack_.back().isDictionary = true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool JSONOArchive::operator()(IPointer& ser, const char* name, [[maybe_unused]] const char* label)
|
||||
{
|
||||
placeIndent();
|
||||
placeName(name);
|
||||
openBracket();
|
||||
const char* registeredTypeName = ser.registeredTypeName();
|
||||
if (registeredTypeName && registeredTypeName[0] != '\0')
|
||||
{
|
||||
*buffer_ << " ";
|
||||
placeName(registeredTypeName);
|
||||
stack_.back().isKeyValue = true;
|
||||
operator()(ser.serializer(), "");
|
||||
stack_.back().isKeyValue = false;
|
||||
*buffer_ << " ";
|
||||
}
|
||||
closeBracket();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool JSONOArchive::operator()(IContainer& ser, const char* name, [[maybe_unused]] const char* label)
|
||||
{
|
||||
placeIndent();
|
||||
placeName(name);
|
||||
std::size_t position = buffer_->position();
|
||||
openContainerBracket();
|
||||
stack_.push_back(Level(true, position, int(strlen(name) + 2 * (name[0] & 1) + stack_.size() - 1 * TAB_WIDTH + 2)));
|
||||
|
||||
std::size_t size = ser.size();
|
||||
if (size > 0)
|
||||
{
|
||||
do
|
||||
{
|
||||
ser(*this, "", "");
|
||||
} while (ser.next());
|
||||
}
|
||||
|
||||
bool joined = joinLinesIfPossible();
|
||||
bool isDictionary = stack_.back().isDictionary;
|
||||
if (isDictionary)
|
||||
{
|
||||
buffer_->buffer()[stack_.back().startPosition] = '{';
|
||||
}
|
||||
stack_.pop_back();
|
||||
if (!joined)
|
||||
{
|
||||
placeIndent(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
*buffer_ << " ";
|
||||
}
|
||||
|
||||
if (isDictionary)
|
||||
{
|
||||
closeBracket();
|
||||
}
|
||||
else
|
||||
{
|
||||
closeContainerBracket();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static char* joinLines(char* start, char* end)
|
||||
{
|
||||
YASLI_ASSERT(start <= end);
|
||||
char* next = start;
|
||||
while (next != end)
|
||||
{
|
||||
if (*next != '\t' && *next != '\r')
|
||||
{
|
||||
if (*next != '\n')
|
||||
{
|
||||
*start = *next;
|
||||
}
|
||||
else
|
||||
{
|
||||
*start = ' ';
|
||||
}
|
||||
++start;
|
||||
}
|
||||
++next;
|
||||
}
|
||||
return start;
|
||||
}
|
||||
|
||||
bool JSONOArchive::joinLinesIfPossible()
|
||||
{
|
||||
YASLI_ASSERT(!stack_.empty());
|
||||
std::size_t startPosition = stack_.back().startPosition;
|
||||
YASLI_ASSERT(startPosition < buffer_->size());
|
||||
int indentCount = stack_.back().indentCount;
|
||||
//YASLI_ASSERT(startPosition >= indentCount);
|
||||
if (buffer_->position() - startPosition - indentCount < std::size_t(textWidth_))
|
||||
{
|
||||
char* buffer = buffer_->buffer();
|
||||
char* start = buffer + startPosition;
|
||||
char* end = buffer + buffer_->position();
|
||||
end = joinLines(start, end);
|
||||
std::size_t newPosition = end - buffer;
|
||||
YASLI_ASSERT(newPosition <= buffer_->position());
|
||||
buffer_->setPosition(newPosition);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// vim:ts=4 sw=4:
|
||||
@@ -1,102 +0,0 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
// Original file Copyright Crytek GMBH or its affiliates, used under license.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include "Serialization/IArchive.h"
|
||||
#include "Serialization/MemoryWriter.h"
|
||||
|
||||
namespace Serialization {
|
||||
class MemoryWriter;
|
||||
|
||||
class JSONOArchive
|
||||
: public IArchive
|
||||
{
|
||||
public:
|
||||
// header = 0 - default header, use "" to omit
|
||||
JSONOArchive(int textWidth = 80, const char* header = 0);
|
||||
~JSONOArchive();
|
||||
|
||||
bool save(const char* fileName);
|
||||
|
||||
const char* c_str() const;
|
||||
const char* buffer() const { return c_str(); }
|
||||
size_t length() const;
|
||||
|
||||
// from Archive:
|
||||
bool operator()(bool& value, const char* name = "", const char* label = 0);
|
||||
bool operator()(IString& value, const char* name = "", const char* label = 0);
|
||||
bool operator()(IWString& value, const char* name = "", const char* label = 0);
|
||||
bool operator()(float& value, const char* name = "", const char* label = 0);
|
||||
bool operator()(double& value, const char* name = "", const char* label = 0);
|
||||
bool operator()(int16& value, const char* name = "", const char* label = 0);
|
||||
bool operator()(uint16& value, const char* name = "", const char* label = 0);
|
||||
bool operator()(int32& value, const char* name = "", const char* label = 0);
|
||||
bool operator()(uint32& value, const char* name = "", const char* label = 0);
|
||||
bool operator()(int64& value, const char* name = "", const char* label = 0);
|
||||
bool operator()(uint64& value, const char* name = "", const char* label = 0);
|
||||
|
||||
bool operator()(char& value, const char* name = "", const char* label = 0);
|
||||
bool operator()(int8& value, const char* name = "", const char* label = 0);
|
||||
bool operator()(uint8& value, const char* name = "", const char* label = 0);
|
||||
|
||||
bool operator()(const SStruct& ser, const char* name = "", const char* label = 0);
|
||||
bool operator()(const SBlackBox& box, const char* name = "", const char* label = 0);
|
||||
bool operator()(IContainer& ser, const char* name = "", const char* label = 0);
|
||||
bool operator()(IKeyValue& keyValue, const char* name = "", const char* label = 0);
|
||||
bool operator()(IPointer& ser, const char* name = "", const char* label = 0);
|
||||
// ^^^
|
||||
|
||||
using IArchive::operator();
|
||||
private:
|
||||
void openBracket();
|
||||
void closeBracket();
|
||||
void openContainerBracket();
|
||||
void closeContainerBracket();
|
||||
void placeName(const char* name);
|
||||
void placeIndent(bool putComma = true);
|
||||
void placeIndentCompact(bool putComma = true);
|
||||
|
||||
bool joinLinesIfPossible();
|
||||
|
||||
struct Level
|
||||
{
|
||||
Level(bool _isContainer, std::size_t position, int column)
|
||||
: isKeyValue(false)
|
||||
, isContainer(_isContainer)
|
||||
, isDictionary(false)
|
||||
, startPosition(position)
|
||||
, nameIndex(0)
|
||||
, elementIndex(0)
|
||||
, indentCount(-column)
|
||||
{}
|
||||
bool isKeyValue;
|
||||
bool isContainer;
|
||||
bool isDictionary;
|
||||
std::size_t startPosition;
|
||||
int nameIndex;
|
||||
int elementIndex;
|
||||
int indentCount;
|
||||
};
|
||||
|
||||
typedef std::vector<Level> Stack;
|
||||
Stack stack_;
|
||||
std::unique_ptr<MemoryWriter> buffer_;
|
||||
const char* header_;
|
||||
int textWidth_;
|
||||
string fileName_;
|
||||
int compactOffset_;
|
||||
bool isKeyValue_;
|
||||
};
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
// Original file Copyright Crytek GMBH or its affiliates, used under license.
|
||||
|
||||
#include "CrySystem_precompiled.h"
|
||||
#include <platform.h>
|
||||
#include "Serialization/Assert.h"
|
||||
#include "MemoryReader.h"
|
||||
#include <stdlib.h>
|
||||
#include <memory.h>
|
||||
|
||||
namespace Serialization {
|
||||
MemoryReader::MemoryReader()
|
||||
: size_(0)
|
||||
, position_(0)
|
||||
, memory_(0)
|
||||
, ownedMemory_(false)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
MemoryReader::MemoryReader(const void* memory, std::size_t size, bool ownAndFree)
|
||||
: size_(size)
|
||||
, position_((const char*)(memory))
|
||||
, memory_((const char*)(memory))
|
||||
, ownedMemory_(ownAndFree)
|
||||
{
|
||||
}
|
||||
|
||||
MemoryReader::~MemoryReader()
|
||||
{
|
||||
if (ownedMemory_)
|
||||
{
|
||||
free(const_cast<char*>(memory_));
|
||||
memory_ = 0;
|
||||
size_ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void MemoryReader::setPosition(const char* position)
|
||||
{
|
||||
position_ = position;
|
||||
}
|
||||
|
||||
void MemoryReader::read(void* data, std::size_t size)
|
||||
{
|
||||
YASLI_ASSERT(memory_ && position_);
|
||||
YASLI_ASSERT(position_ - memory_ + size <= size_);
|
||||
memcpy(data, position_, size);
|
||||
position_ += size;
|
||||
}
|
||||
|
||||
bool MemoryReader::checkedRead(void* data, std::size_t size)
|
||||
{
|
||||
if (!memory_ || !position_)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (position_ - memory_ + size > size_)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy(data, position_, size);
|
||||
position_ += size;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MemoryReader::checkedSkip(std::size_t size)
|
||||
{
|
||||
if (!memory_ || !position_)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (position_ - memory_ + size > size_)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
position_ += size;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
// Original file Copyright Crytek GMBH or its affiliates, used under license.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
namespace Serialization {
|
||||
class MemoryReader
|
||||
{
|
||||
public:
|
||||
|
||||
MemoryReader();
|
||||
MemoryReader(const void* memory, size_t size, bool ownAndFree = false);
|
||||
~MemoryReader();
|
||||
|
||||
void setPosition(const char* position);
|
||||
const char* position(){ return position_; }
|
||||
|
||||
template<class T>
|
||||
void read(T& value)
|
||||
{
|
||||
read(reinterpret_cast<void*>(&value), sizoef(value));
|
||||
}
|
||||
void read(void* data, size_t size);
|
||||
bool checkedSkip(size_t size);
|
||||
bool checkedRead(void* data, size_t size);
|
||||
template<class T>
|
||||
bool checkedRead(T& t)
|
||||
{
|
||||
return checkedRead((void*)&t, sizeof(t));
|
||||
}
|
||||
|
||||
const char* buffer() const{ return memory_; }
|
||||
size_t size() const{ return size_; }
|
||||
|
||||
const char* begin() const{ return memory_; }
|
||||
const char* end() const{ return memory_ + size_; }
|
||||
private:
|
||||
size_t size_;
|
||||
const char* position_;
|
||||
const char* memory_;
|
||||
bool ownedMemory_;
|
||||
};
|
||||
}
|
||||
// vim:ts=4 sw=4:
|
||||
@@ -1,236 +0,0 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
// Original file Copyright Crytek GMBH or its affiliates, used under license.
|
||||
|
||||
#include "CrySystem_precompiled.h"
|
||||
#include <platform.h>
|
||||
#include "Serialization/Assert.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <cstring>
|
||||
#include <math.h>
|
||||
#ifdef _MSC_VER
|
||||
# include <float.h>
|
||||
# define isnan _isnan
|
||||
#endif
|
||||
|
||||
#include "MemoryWriter.h"
|
||||
|
||||
#undef YASLI_ASSERT
|
||||
#define YASLI_ASSERT(x)
|
||||
|
||||
namespace Serialization {
|
||||
MemoryWriter::MemoryWriter(std::size_t size, bool reallocate)
|
||||
: size_(size)
|
||||
, reallocate_(reallocate)
|
||||
, digits_(5)
|
||||
{
|
||||
allocate(size);
|
||||
}
|
||||
|
||||
MemoryWriter::~MemoryWriter()
|
||||
{
|
||||
position_ = 0;
|
||||
CryModuleFree(memory_);
|
||||
}
|
||||
|
||||
void MemoryWriter::allocate(std::size_t initialSize)
|
||||
{
|
||||
memory_ = (char*)CryModuleMalloc(initialSize + 1);
|
||||
position_ = memory_;
|
||||
}
|
||||
|
||||
void MemoryWriter::reallocate(std::size_t newSize)
|
||||
{
|
||||
YASLI_ASSERT(newSize > size_);
|
||||
std::size_t pos = position();
|
||||
// Supressing the warning as we generally don't handle malloc errors.
|
||||
// cppcheck-suppress memleakOnRealloc
|
||||
memory_ = (char*)CryModuleRealloc(memory_, newSize + 1);
|
||||
YASLI_ASSERT(memory_ != 0);
|
||||
position_ = memory_ + pos;
|
||||
size_ = newSize;
|
||||
}
|
||||
|
||||
MemoryWriter& MemoryWriter::operator<<(int value)
|
||||
{
|
||||
// TODO: optimize
|
||||
char buffer[12];
|
||||
sprintf_s(buffer, "%i", value);
|
||||
return operator<<((const char*)buffer);
|
||||
}
|
||||
|
||||
MemoryWriter& MemoryWriter::operator<<(long value)
|
||||
{
|
||||
// TODO: optimize
|
||||
char buffer[12];
|
||||
#ifdef _MSC_VER
|
||||
sprintf_s(buffer, "%i", value);
|
||||
#else
|
||||
sprintf_s(buffer, "%li", value);
|
||||
#endif
|
||||
return operator<<((const char*)buffer);
|
||||
}
|
||||
|
||||
MemoryWriter& MemoryWriter::operator<<(unsigned long value)
|
||||
{
|
||||
// TODO: optimize
|
||||
char buffer[12];
|
||||
#ifdef _MSC_VER
|
||||
sprintf_s(buffer, "%u", value);
|
||||
#else
|
||||
sprintf_s(buffer, "%lu", value);
|
||||
#endif
|
||||
return operator<<((const char*)buffer);
|
||||
}
|
||||
|
||||
MemoryWriter& MemoryWriter::operator<<(long long value)
|
||||
{
|
||||
// TODO: optimize
|
||||
char buffer[24];
|
||||
#ifdef _MSC_VER
|
||||
sprintf_s(buffer, "%I64i", value);
|
||||
#else
|
||||
sprintf_s(buffer, "%lli", value);
|
||||
#endif
|
||||
return operator<<((const char*)buffer);
|
||||
}
|
||||
|
||||
MemoryWriter& MemoryWriter::operator<<(unsigned long long value)
|
||||
{
|
||||
// TODO: optimize
|
||||
char buffer[24];
|
||||
sprintf_s(buffer, "%llu", value);
|
||||
return operator<<((const char*)buffer);
|
||||
}
|
||||
|
||||
MemoryWriter& MemoryWriter::operator<<(unsigned int value)
|
||||
{
|
||||
// TODO: optimize
|
||||
char buffer[12];
|
||||
sprintf_s(buffer, "%u", value);
|
||||
return operator<<((const char*)buffer);
|
||||
}
|
||||
|
||||
MemoryWriter& MemoryWriter::operator<<(char value)
|
||||
{
|
||||
char buffer[12];
|
||||
sprintf_s(buffer, "%i", int(value));
|
||||
return operator<<((const char*)buffer);
|
||||
}
|
||||
|
||||
MemoryWriter& MemoryWriter::operator<<(unsigned char value)
|
||||
{
|
||||
char buffer[12];
|
||||
sprintf_s(buffer, "%i", int(value));
|
||||
return operator<<((const char*)buffer);
|
||||
}
|
||||
|
||||
MemoryWriter& MemoryWriter::operator<<(signed char value)
|
||||
{
|
||||
char buffer[12];
|
||||
sprintf_s(buffer, "%i", int(value));
|
||||
return operator<<((const char*)buffer);
|
||||
}
|
||||
|
||||
inline void cutRightZeros(const char* str)
|
||||
{
|
||||
for (char* p = (char*)str + strlen(str) - 1; p >= str; --p)
|
||||
{
|
||||
if (*p == '0')
|
||||
{
|
||||
*p = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MemoryWriter& MemoryWriter::operator<<(double value)
|
||||
{
|
||||
YASLI_ASSERT(!isnan(value));
|
||||
|
||||
char buf[64] = { 0 };
|
||||
sprintf_s(buf, "%f", value);
|
||||
operator<<(buf);
|
||||
return *this;
|
||||
}
|
||||
|
||||
MemoryWriter& MemoryWriter::operator<<(const char* value)
|
||||
{
|
||||
write((void*)value, strlen(value));
|
||||
YASLI_ASSERT(position() < size());
|
||||
*position_ = '\0';
|
||||
return *this;
|
||||
}
|
||||
|
||||
MemoryWriter& MemoryWriter::operator<<(const wchar_t* value)
|
||||
{
|
||||
write((void*)value, wcslen(value) * sizeof(wchar_t));
|
||||
YASLI_ASSERT(position() < size());
|
||||
*position_ = '\0';
|
||||
return *this;
|
||||
}
|
||||
|
||||
void MemoryWriter::setPosition(std::size_t pos)
|
||||
{
|
||||
YASLI_ASSERT(pos < size_);
|
||||
YASLI_ASSERT(memory_ + pos <= position_);
|
||||
position_ = memory_ + pos;
|
||||
}
|
||||
|
||||
void MemoryWriter::write(const char* value)
|
||||
{
|
||||
write((void*)value, strlen(value));
|
||||
}
|
||||
|
||||
bool MemoryWriter::write(const void* data, std::size_t size)
|
||||
{
|
||||
YASLI_ASSERT(memory_ <= position_);
|
||||
YASLI_ASSERT(position() < this->size());
|
||||
if (size_ - position() > size)
|
||||
{
|
||||
memcpy(position_, data, size);
|
||||
position_ += size;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!reallocate_)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
reallocate(size_ * 2);
|
||||
write(data, size);
|
||||
}
|
||||
YASLI_ASSERT(position() < this->size());
|
||||
return true;
|
||||
}
|
||||
|
||||
void MemoryWriter::write(char c)
|
||||
{
|
||||
if (size_ - position() > 1)
|
||||
{
|
||||
*(char*)(position_) = c;
|
||||
++position_;
|
||||
}
|
||||
else
|
||||
{
|
||||
YASLI_ESCAPE(reallocate_, return );
|
||||
reallocate(size_ * 2);
|
||||
write(c);
|
||||
}
|
||||
YASLI_ASSERT(position() < this->size());
|
||||
}
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
// Original file Copyright Crytek GMBH or its affiliates, used under license.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
namespace Serialization {
|
||||
class MemoryWriter
|
||||
{
|
||||
public:
|
||||
MemoryWriter(std::size_t size = 128, bool reallocate = true);
|
||||
~MemoryWriter();
|
||||
|
||||
const char* c_str() { return memory_; };
|
||||
const wchar_t* w_str() { return (wchar_t*)memory_; };
|
||||
char* buffer() { return memory_; }
|
||||
const char* buffer() const { return memory_; }
|
||||
std::size_t size() const{ return size_; }
|
||||
void clear() { position_ = memory_; }
|
||||
|
||||
// String interface (after this calls '\0' is always written)
|
||||
MemoryWriter& operator<<(int value);
|
||||
MemoryWriter& operator<<(long value);
|
||||
MemoryWriter& operator<<(unsigned long value);
|
||||
MemoryWriter& operator<<(unsigned int value);
|
||||
MemoryWriter& operator<<(long long value);
|
||||
MemoryWriter& operator<<(unsigned long long value);
|
||||
MemoryWriter& operator<<(float value) { return (*this) << double(value); }
|
||||
MemoryWriter& operator<<(double value);
|
||||
MemoryWriter& operator<<(signed char value);
|
||||
MemoryWriter& operator<<(unsigned char value);
|
||||
MemoryWriter& operator<<(char value);
|
||||
MemoryWriter& operator<<(const char* value);
|
||||
MemoryWriter& operator<<(const wchar_t* value);
|
||||
|
||||
// Binary interface (does not writes trailing '\0')
|
||||
template<class T>
|
||||
void write(const T& value)
|
||||
{
|
||||
write(reinterpret_cast<const T*>(&value), sizeof(value));
|
||||
}
|
||||
void write(char c);
|
||||
void write(const char* str);
|
||||
bool write(const void* data, std::size_t size);
|
||||
|
||||
std::size_t position() const{ return position_ - memory_; }
|
||||
void setPosition(std::size_t pos);
|
||||
|
||||
MemoryWriter& setDigits(int digits) { digits_ = (unsigned char)digits; return *this; }
|
||||
|
||||
private:
|
||||
void allocate(std::size_t initialSize);
|
||||
void reallocate(std::size_t newSize);
|
||||
|
||||
std::size_t size_;
|
||||
char* position_;
|
||||
char* memory_;
|
||||
bool reallocate_;
|
||||
unsigned char digits_;
|
||||
};
|
||||
}
|
||||
@@ -1,492 +0,0 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
// Original file Copyright Crytek GMBH or its affiliates, used under license.
|
||||
|
||||
#include "CrySystem_precompiled.h"
|
||||
#include <AzTest/AzTest.h>
|
||||
|
||||
#include <AzCore/UnitTest/TestTypes.h>
|
||||
|
||||
#include "ArchiveHost.h"
|
||||
#include <Serialization/STL.h>
|
||||
#include <Serialization/IArchive.h>
|
||||
#include <Serialization/StringList.h>
|
||||
#include <Serialization/SmartPtr.h>
|
||||
#include <memory>
|
||||
|
||||
namespace Serialization
|
||||
{
|
||||
struct SMember
|
||||
{
|
||||
string name;
|
||||
float weight;
|
||||
|
||||
SMember()
|
||||
: weight(0.0f)
|
||||
{}
|
||||
|
||||
void CheckEquality(const SMember& copy) const
|
||||
{
|
||||
EXPECT_TRUE(name == copy.name);
|
||||
EXPECT_TRUE(weight == copy.weight);
|
||||
}
|
||||
|
||||
void Change(int index)
|
||||
{
|
||||
name = "Changed name ";
|
||||
name += (index % 10) + '0';
|
||||
weight = float(index);
|
||||
}
|
||||
|
||||
void Serialize(IArchive& ar)
|
||||
{
|
||||
ar(name, "name");
|
||||
ar(weight, "weight");
|
||||
}
|
||||
};
|
||||
|
||||
class CPolyBase
|
||||
: public _i_reference_target_t
|
||||
{
|
||||
public:
|
||||
CPolyBase()
|
||||
{
|
||||
baseMember = "Regular base member";
|
||||
}
|
||||
|
||||
virtual void Change()
|
||||
{
|
||||
baseMember = "Changed base member";
|
||||
}
|
||||
|
||||
virtual void Serialize(IArchive& ar)
|
||||
{
|
||||
ar(baseMember, "baseMember");
|
||||
}
|
||||
|
||||
virtual void CheckEquality(const CPolyBase* copy) const
|
||||
{
|
||||
EXPECT_TRUE(baseMember == copy->baseMember);
|
||||
}
|
||||
|
||||
virtual bool IsDerivedA() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
virtual bool IsDerivedB() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
protected:
|
||||
string baseMember;
|
||||
};
|
||||
|
||||
class CPolyDerivedA
|
||||
: public CPolyBase
|
||||
{
|
||||
public:
|
||||
void Serialize(IArchive& ar)
|
||||
{
|
||||
CPolyBase::Serialize(ar);
|
||||
ar(derivedMember, "derivedMember");
|
||||
}
|
||||
|
||||
bool IsDerivedA() const override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void CheckEquality(const CPolyBase* copyBase) const
|
||||
{
|
||||
EXPECT_TRUE(copyBase->IsDerivedA());
|
||||
const CPolyDerivedA* copy = (CPolyDerivedA*)copyBase;
|
||||
EXPECT_TRUE(derivedMember == copy->derivedMember);
|
||||
|
||||
CPolyBase::CheckEquality(copyBase);
|
||||
}
|
||||
protected:
|
||||
string derivedMember;
|
||||
};
|
||||
|
||||
class CPolyDerivedB
|
||||
: public CPolyBase
|
||||
{
|
||||
public:
|
||||
CPolyDerivedB()
|
||||
: derivedMember("B Derived")
|
||||
{}
|
||||
|
||||
bool IsDerivedB() const override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void Serialize(IArchive& ar)
|
||||
{
|
||||
CPolyBase::Serialize(ar);
|
||||
ar(derivedMember, "derivedMember");
|
||||
}
|
||||
|
||||
void CheckEquality(const CPolyBase* copyBase) const
|
||||
{
|
||||
EXPECT_TRUE(copyBase->IsDerivedB());
|
||||
const CPolyDerivedB* copy = (const CPolyDerivedB*)copyBase;
|
||||
EXPECT_TRUE(derivedMember == copy->derivedMember);
|
||||
|
||||
CPolyBase::CheckEquality(copyBase);
|
||||
}
|
||||
protected:
|
||||
string derivedMember;
|
||||
};
|
||||
|
||||
struct SNumericTypes
|
||||
{
|
||||
SNumericTypes()
|
||||
: m_bool(false)
|
||||
, m_char(0)
|
||||
, m_int8(0)
|
||||
, m_uint8(0)
|
||||
, m_int16(0)
|
||||
, m_uint16(0)
|
||||
, m_int32(0)
|
||||
, m_uint32(0)
|
||||
, m_int64(0)
|
||||
, m_uint64(0)
|
||||
, m_float(0.0f)
|
||||
, m_double(0.0)
|
||||
{}
|
||||
|
||||
void Change()
|
||||
{
|
||||
m_bool = true;
|
||||
m_char = -1;
|
||||
m_int8 = -2;
|
||||
m_uint8 = 0xff - 3;
|
||||
m_int16 = -6;
|
||||
m_uint16 = 0xff - 7;
|
||||
m_int32 = -4;
|
||||
m_uint32 = -5;
|
||||
m_int64 = -8ll;
|
||||
m_uint64 = 9ull;
|
||||
m_float = -10.0f;
|
||||
m_double = -11.0;
|
||||
}
|
||||
|
||||
void Serialize(IArchive& ar)
|
||||
{
|
||||
ar(m_bool, "bool");
|
||||
ar(m_char, "char");
|
||||
ar(m_int8, "int8");
|
||||
ar(m_uint8, "uint8");
|
||||
ar(m_int16, "int16");
|
||||
ar(m_uint16, "uint16");
|
||||
ar(m_int32, "int32");
|
||||
ar(m_uint32, "uint32");
|
||||
ar(m_int64, "int64");
|
||||
ar(m_uint64, "uint64");
|
||||
ar(m_float, "float");
|
||||
ar(m_double, "double");
|
||||
}
|
||||
|
||||
void CheckEquality(const SNumericTypes& rhs) const
|
||||
{
|
||||
EXPECT_TRUE(m_bool == rhs.m_bool);
|
||||
EXPECT_TRUE(m_char == rhs.m_char);
|
||||
EXPECT_TRUE(m_int8 == rhs.m_int8);
|
||||
EXPECT_TRUE(m_uint8 == rhs.m_uint8);
|
||||
EXPECT_TRUE(m_int16 == rhs.m_int16);
|
||||
EXPECT_TRUE(m_uint16 == rhs.m_uint16);
|
||||
EXPECT_TRUE(m_int32 == rhs.m_int32);
|
||||
EXPECT_TRUE(m_uint32 == rhs.m_uint32);
|
||||
EXPECT_TRUE(m_int64 == rhs.m_int64);
|
||||
EXPECT_TRUE(m_uint64 == rhs.m_uint64);
|
||||
EXPECT_TRUE(m_float == rhs.m_float);
|
||||
EXPECT_TRUE(m_double == rhs.m_double);
|
||||
}
|
||||
|
||||
bool m_bool;
|
||||
|
||||
char m_char;
|
||||
int8 m_int8;
|
||||
uint8 m_uint8;
|
||||
|
||||
int16 m_int16;
|
||||
uint16 m_uint16;
|
||||
|
||||
int32 m_int32;
|
||||
uint32 m_uint32;
|
||||
|
||||
int64 m_int64;
|
||||
uint64 m_uint64;
|
||||
|
||||
float m_float;
|
||||
double m_double;
|
||||
};
|
||||
|
||||
class CComplexClass
|
||||
{
|
||||
public:
|
||||
CComplexClass()
|
||||
: index(0)
|
||||
{
|
||||
name = "Foo";
|
||||
stringList.push_back("Choice 1");
|
||||
stringList.push_back("Choice 2");
|
||||
stringList.push_back("Choice 3");
|
||||
|
||||
polyPtr.reset(new CPolyDerivedA());
|
||||
|
||||
polyVector.push_back(new CPolyDerivedB);
|
||||
polyVector.push_back(new CPolyBase);
|
||||
|
||||
SMember& a = stringToStructMap["a"];
|
||||
a.name = "A";
|
||||
SMember& b = stringToStructMap["b"];
|
||||
b.name = "B";
|
||||
|
||||
members.resize(13);
|
||||
|
||||
intToString.push_back(std::make_pair(1, "one"));
|
||||
intToString.push_back(std::make_pair(2, "two"));
|
||||
intToString.push_back(std::make_pair(3, "three"));
|
||||
stringToInt.push_back(std::make_pair("one", 1));
|
||||
stringToInt.push_back(std::make_pair("two", 2));
|
||||
stringToInt.push_back(std::make_pair("three", 3));
|
||||
}
|
||||
|
||||
void Change()
|
||||
{
|
||||
name = "Slightly changed name";
|
||||
index = 2;
|
||||
polyPtr.reset(new CPolyDerivedB());
|
||||
polyPtr->Change();
|
||||
|
||||
for (size_t i = 0; i < members.size(); ++i)
|
||||
{
|
||||
members[i].Change(int(i));
|
||||
}
|
||||
|
||||
members.erase(members.begin());
|
||||
|
||||
for (size_t i = 0; i < polyVector.size(); ++i)
|
||||
{
|
||||
polyVector[i]->Change();
|
||||
}
|
||||
|
||||
polyVector.resize(4);
|
||||
polyVector.push_back(new CPolyBase());
|
||||
polyVector[4]->Change();
|
||||
|
||||
const size_t arrayLen = sizeof(array) / sizeof(array[0]);
|
||||
for (size_t i = 0; i < arrayLen; ++i)
|
||||
{
|
||||
array[i].Change(int(arrayLen - i));
|
||||
}
|
||||
|
||||
numericTypes.Change();
|
||||
|
||||
vectorOfStrings.push_back("str1");
|
||||
vectorOfStrings.push_back("2str");
|
||||
vectorOfStrings.push_back("thirdstr");
|
||||
|
||||
stringToStructMap.erase("a");
|
||||
SMember& c = stringToStructMap["c"];
|
||||
c.name = "C";
|
||||
|
||||
intToString.push_back(std::make_pair(4, "four"));
|
||||
stringToInt.push_back(std::make_pair("four", 4));
|
||||
}
|
||||
|
||||
void Serialize(IArchive& ar)
|
||||
{
|
||||
ar(name, "name");
|
||||
ar(polyPtr, "polyPtr");
|
||||
ar(polyVector, "polyVector");
|
||||
ar(members, "members");
|
||||
{
|
||||
StringListValue value(stringList, stringList[index]);
|
||||
ar(value, "stringList");
|
||||
index = value.index();
|
||||
if (index == -1)
|
||||
{
|
||||
index = 0;
|
||||
}
|
||||
}
|
||||
ar(array, "array");
|
||||
ar(numericTypes, "numericTypes");
|
||||
ar(vectorOfStrings, "vectorOfStrings");
|
||||
ar(stringToInt, "stringToInt");
|
||||
}
|
||||
|
||||
void CheckEquality(const CComplexClass& copy) const
|
||||
{
|
||||
EXPECT_TRUE(name == copy.name);
|
||||
EXPECT_TRUE(index == copy.index);
|
||||
|
||||
EXPECT_TRUE(polyPtr != 0);
|
||||
EXPECT_TRUE(copy.polyPtr != 0);
|
||||
polyPtr->CheckEquality(copy.polyPtr);
|
||||
|
||||
EXPECT_TRUE(members.size() == copy.members.size());
|
||||
for (size_t i = 0; i < members.size(); ++i)
|
||||
{
|
||||
members[i].CheckEquality(copy.members[i]);
|
||||
}
|
||||
|
||||
EXPECT_TRUE(polyVector.size() == copy.polyVector.size());
|
||||
for (size_t i = 0; i < polyVector.size(); ++i)
|
||||
{
|
||||
if (polyVector[i] == 0)
|
||||
{
|
||||
EXPECT_TRUE(copy.polyVector[i] == 0);
|
||||
continue;
|
||||
}
|
||||
EXPECT_TRUE(copy.polyVector[i] != 0);
|
||||
polyVector[i]->CheckEquality(copy.polyVector[i]);
|
||||
}
|
||||
|
||||
const size_t arrayLen = sizeof(array) / sizeof(array[0]);
|
||||
for (size_t i = 0; i < arrayLen; ++i)
|
||||
{
|
||||
array[i].CheckEquality(copy.array[i]);
|
||||
}
|
||||
|
||||
numericTypes.CheckEquality(copy.numericTypes);
|
||||
|
||||
EXPECT_TRUE(stringToInt.size() == copy.stringToInt.size());
|
||||
for (size_t i = 0; i < stringToInt.size(); ++i)
|
||||
{
|
||||
EXPECT_TRUE(stringToInt[i] == copy.stringToInt[i]);
|
||||
}
|
||||
}
|
||||
protected:
|
||||
string name;
|
||||
typedef std::vector<SMember> Members;
|
||||
std::vector<string> vectorOfStrings;
|
||||
std::vector<std::pair<int, string> > intToString;
|
||||
std::vector<std::pair<string, int> > stringToInt;
|
||||
Members members;
|
||||
int32 index;
|
||||
SNumericTypes numericTypes;
|
||||
|
||||
StringListStatic stringList;
|
||||
std::vector< _smart_ptr<CPolyBase> > polyVector;
|
||||
_smart_ptr<CPolyBase> polyPtr;
|
||||
|
||||
std::map<string, SMember> stringToStructMap;
|
||||
|
||||
SMember array[5];
|
||||
};
|
||||
|
||||
struct ArchiveHostTests
|
||||
: ::testing::Test
|
||||
{
|
||||
public:
|
||||
void SetUp() override
|
||||
{
|
||||
AZ::AllocatorInstance<AZ::LegacyAllocator>::Create();
|
||||
AZ::AllocatorInstance<CryStringAllocator>::Create();
|
||||
|
||||
m_classFactoryRTTI = AZStd::make_unique<ClassFactoryRTTI>();
|
||||
}
|
||||
|
||||
void TearDown()
|
||||
{
|
||||
m_classFactoryRTTI.reset();
|
||||
|
||||
AZ::AllocatorInstance<CryStringAllocator>::Destroy();
|
||||
AZ::AllocatorInstance<AZ::LegacyAllocator>::Destroy();
|
||||
}
|
||||
|
||||
struct ClassFactoryRTTI
|
||||
{
|
||||
ClassFactoryRTTI()
|
||||
: CPolyBaseCPolyBase_DerivedDescription("base", "Base")
|
||||
, CPolyBaseCPolyBase_Creator(&CPolyBaseCPolyBase_DerivedDescription)
|
||||
, TypeCPolyBase_DerivedDescription("derived_a", "Derived A")
|
||||
, TypeCPolyBase_Creator(&TypeCPolyBase_DerivedDescription)
|
||||
, CPolyDerivedBCPolyBase_DerivedDescription("derived_b", "Derived B")
|
||||
, CPolyDerivedBCPolyBase_Creator(&CPolyDerivedBCPolyBase_DerivedDescription)
|
||||
{}
|
||||
|
||||
~ClassFactoryRTTI()
|
||||
{
|
||||
Serialization::ClassFactory<CPolyBase>::destroy();
|
||||
}
|
||||
|
||||
const Serialization::TypeDescription CPolyBaseCPolyBase_DerivedDescription;
|
||||
Serialization::ClassFactory<CPolyBase>::Creator<CPolyBase> CPolyBaseCPolyBase_Creator;
|
||||
|
||||
const Serialization::TypeDescription TypeCPolyBase_DerivedDescription;
|
||||
Serialization::ClassFactory<CPolyBase>::Creator<CPolyDerivedA> TypeCPolyBase_Creator;
|
||||
|
||||
const Serialization::TypeDescription CPolyDerivedBCPolyBase_DerivedDescription;
|
||||
Serialization::ClassFactory<CPolyBase>::Creator<CPolyDerivedB> CPolyDerivedBCPolyBase_Creator;
|
||||
};
|
||||
AZStd::unique_ptr<ClassFactoryRTTI> m_classFactoryRTTI;
|
||||
};
|
||||
|
||||
TEST_F(ArchiveHostTests, JsonBasicTypes)
|
||||
{
|
||||
std::unique_ptr<IArchiveHost> host(CreateArchiveHost());
|
||||
|
||||
DynArray<char> bufChanged;
|
||||
CComplexClass objChanged;
|
||||
objChanged.Change();
|
||||
host->SaveJsonBuffer(bufChanged, SStruct(objChanged));
|
||||
EXPECT_TRUE(!bufChanged.empty());
|
||||
|
||||
DynArray<char> bufResaved;
|
||||
{
|
||||
CComplexClass obj;
|
||||
|
||||
EXPECT_TRUE(host->LoadJsonBuffer(SStruct(obj), bufChanged.data(), bufChanged.size()));
|
||||
EXPECT_TRUE(host->SaveJsonBuffer(bufResaved, SStruct(obj)));
|
||||
EXPECT_TRUE(!bufResaved.empty());
|
||||
|
||||
obj.CheckEquality(objChanged);
|
||||
}
|
||||
EXPECT_TRUE(bufChanged.size() == bufResaved.size());
|
||||
for (size_t i = 0; i < bufChanged.size(); ++i)
|
||||
{
|
||||
EXPECT_TRUE(bufChanged[i] == bufResaved[i]);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(ArchiveHostTests, BinBasicTypes)
|
||||
{
|
||||
std::unique_ptr<IArchiveHost> host(CreateArchiveHost());
|
||||
|
||||
DynArray<char> bufChanged;
|
||||
CComplexClass objChanged;
|
||||
objChanged.Change();
|
||||
host->SaveBinaryBuffer(bufChanged, SStruct(objChanged));
|
||||
EXPECT_TRUE(!bufChanged.empty());
|
||||
|
||||
DynArray<char> bufResaved;
|
||||
{
|
||||
CComplexClass obj;
|
||||
|
||||
EXPECT_TRUE(host->LoadBinaryBuffer(SStruct(obj), bufChanged.data(), bufChanged.size()));
|
||||
EXPECT_TRUE(host->SaveBinaryBuffer(bufResaved, SStruct(obj)));
|
||||
EXPECT_TRUE(!bufResaved.empty());
|
||||
|
||||
obj.CheckEquality(objChanged);
|
||||
}
|
||||
EXPECT_TRUE(bufChanged.size() == bufResaved.size());
|
||||
for (size_t i = 0; i < bufChanged.size(); ++i)
|
||||
{
|
||||
EXPECT_TRUE(bufChanged[i] == bufResaved[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,89 +0,0 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
// Original file Copyright Crytek GMBH or its affiliates, used under license.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "Serialization/Strings.h"
|
||||
|
||||
namespace Serialization {
|
||||
struct Token
|
||||
{
|
||||
Token(const char* _str = 0)
|
||||
: start(_str)
|
||||
, end(_str ? _str + strlen(_str) : 0)
|
||||
{
|
||||
}
|
||||
|
||||
Token(const char* _str, size_t _len)
|
||||
: start(_str)
|
||||
, end(_str + _len) {}
|
||||
Token(const char* _start, const char* _end)
|
||||
: start(_start)
|
||||
, end(_end) {}
|
||||
|
||||
void set(const char* _start, const char* _end) { start = _start; end = _end; }
|
||||
std::size_t length() const{ return end - start; }
|
||||
|
||||
bool operator==(const Token& rhs) const
|
||||
{
|
||||
if (length() != rhs.length())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return memcmp(start, rhs.start, length()) == 0;
|
||||
}
|
||||
bool operator==(const string& rhs) const
|
||||
{
|
||||
if (length() != rhs.size())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return memcmp(start, rhs.c_str(), length()) == 0;
|
||||
}
|
||||
|
||||
bool operator==(const char* text) const
|
||||
{
|
||||
if (strncmp(text, start, length()) == 0)
|
||||
{
|
||||
return text[length()] == '\0';
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool operator!=(const char* text) const
|
||||
{
|
||||
if (strncmp(text, start, length()) == 0)
|
||||
{
|
||||
return text[length()] != '\0';
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bool operator==(char c) const
|
||||
{
|
||||
return length() == 1 && *start == c;
|
||||
}
|
||||
bool operator!=(char c) const
|
||||
{
|
||||
return length() != 1 || *start != c;
|
||||
}
|
||||
|
||||
operator bool() const{
|
||||
return start != end;
|
||||
}
|
||||
string str() const{ return string(start, end); }
|
||||
|
||||
const char* start;
|
||||
const char* end;
|
||||
};
|
||||
}
|
||||
@@ -1,297 +0,0 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
// Original file Copyright Crytek GMBH or its affiliates, used under license.
|
||||
|
||||
#include "CrySystem_precompiled.h"
|
||||
#include "CryExtension/Impl/ClassWeaver.h"
|
||||
|
||||
#include <Serialization/STL.h>
|
||||
#include <Serialization/ClassFactory.h>
|
||||
|
||||
#include "XmlIArchive.h"
|
||||
|
||||
#include <Serialization/STLImpl.h>
|
||||
#include <Serialization/ClassFactoryImpl.h>
|
||||
|
||||
namespace XmlUtil
|
||||
{
|
||||
int g_hintSuccess = 0;
|
||||
int g_hintFail = 0;
|
||||
|
||||
|
||||
XmlNodeRef FindChildNode(XmlNodeRef pParent, const int childIndexOverride, int& childIndexHint, const char* const name)
|
||||
{
|
||||
CRY_ASSERT(pParent);
|
||||
|
||||
if (0 <= childIndexOverride)
|
||||
{
|
||||
CRY_ASSERT(childIndexOverride < pParent->getChildCount());
|
||||
return pParent->getChild(childIndexOverride);
|
||||
}
|
||||
else
|
||||
{
|
||||
CRY_ASSERT(name);
|
||||
CRY_ASSERT(name[ 0 ]);
|
||||
CRY_ASSERT(0 <= childIndexHint);
|
||||
|
||||
const int childCount = pParent->getChildCount();
|
||||
const bool hasValidChildHint = (childIndexHint < childCount);
|
||||
if (hasValidChildHint)
|
||||
{
|
||||
XmlNodeRef pChildNode = pParent->getChild(childIndexHint);
|
||||
if (pChildNode->isTag(name))
|
||||
{
|
||||
g_hintSuccess++;
|
||||
const int nextChildIndexHint = childIndexHint + 1;
|
||||
childIndexHint = (nextChildIndexHint < childCount) ? nextChildIndexHint : 0;
|
||||
return pChildNode;
|
||||
}
|
||||
else
|
||||
{
|
||||
g_hintFail++;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < childCount; ++i)
|
||||
{
|
||||
XmlNodeRef pChildNode = pParent->getChild(i);
|
||||
if (pChildNode->isTag(name))
|
||||
{
|
||||
const int nextChildIndexHint = i + 1;
|
||||
childIndexHint = (nextChildIndexHint < childCount) ? nextChildIndexHint : 0;
|
||||
return pChildNode;
|
||||
}
|
||||
}
|
||||
}
|
||||
return XmlNodeRef();
|
||||
}
|
||||
|
||||
|
||||
template< typename T, typename TOut >
|
||||
bool ReadChildNodeAs(XmlNodeRef pParent, const int childIndexOverride, int& childIndexHint, const char* const name, TOut& valueOut)
|
||||
{
|
||||
XmlNodeRef pChild = FindChildNode(pParent, childIndexOverride, childIndexHint, name);
|
||||
if (pChild)
|
||||
{
|
||||
T tmp;
|
||||
const bool readValueSuccess = pChild->getAttr("value", tmp);
|
||||
if (readValueSuccess)
|
||||
{
|
||||
valueOut = tmp;
|
||||
}
|
||||
return readValueSuccess;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
template< typename T >
|
||||
bool ReadChildNode(XmlNodeRef pParent, const int childIndexOverride, int& childIndexHint, const char* const name, T& valueOut)
|
||||
{
|
||||
return ReadChildNodeAs< T >(pParent, childIndexOverride, childIndexHint, name, valueOut);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Serialization::CXmlIArchive::CXmlIArchive()
|
||||
: IArchive(INPUT | NO_EMPTY_NAMES)
|
||||
, m_childIndexOverride(-1)
|
||||
, m_childIndexHint(0)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
Serialization::CXmlIArchive::CXmlIArchive(XmlNodeRef pRootNode)
|
||||
: IArchive(INPUT | NO_EMPTY_NAMES)
|
||||
, m_pRootNode(pRootNode)
|
||||
, m_childIndexOverride(-1)
|
||||
, m_childIndexHint(0)
|
||||
{
|
||||
CRY_ASSERT(m_pRootNode);
|
||||
}
|
||||
|
||||
|
||||
Serialization::CXmlIArchive::~CXmlIArchive()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void Serialization::CXmlIArchive::SetXmlNode(XmlNodeRef pNode)
|
||||
{
|
||||
m_pRootNode = pNode;
|
||||
}
|
||||
|
||||
|
||||
XmlNodeRef Serialization::CXmlIArchive::GetXmlNode() const
|
||||
{
|
||||
return m_pRootNode;
|
||||
}
|
||||
|
||||
|
||||
bool Serialization::CXmlIArchive::operator()(bool& value, const char* name, [[maybe_unused]] const char* label)
|
||||
{
|
||||
XmlNodeRef pChild = XmlUtil::FindChildNode(m_pRootNode, m_childIndexOverride, m_childIndexHint, name);
|
||||
if (pChild)
|
||||
{
|
||||
const char* const stringValue = pChild->getAttr("value");
|
||||
if (stringValue)
|
||||
{
|
||||
value = (strcmp("true", stringValue) == 0);
|
||||
value = value || (strcmp("1", stringValue) == 0);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool Serialization::CXmlIArchive::operator()(IString& value, const char* name, [[maybe_unused]] const char* label)
|
||||
{
|
||||
XmlNodeRef pChild = XmlUtil::FindChildNode(m_pRootNode, m_childIndexOverride, m_childIndexHint, name);
|
||||
if (pChild)
|
||||
{
|
||||
const char* const stringValue = pChild->getAttr("value");
|
||||
if (stringValue)
|
||||
{
|
||||
value.set(stringValue);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool Serialization::CXmlIArchive::operator()([[maybe_unused]] IWString& value, [[maybe_unused]] const char* name, [[maybe_unused]] const char* label)
|
||||
{
|
||||
CryFatalError("CXmlIArchive::operator() with IWString is not implemented");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool Serialization::CXmlIArchive::operator()(float& value, const char* name, [[maybe_unused]] const char* label)
|
||||
{
|
||||
return XmlUtil::ReadChildNode(m_pRootNode, m_childIndexOverride, m_childIndexHint, name, value);
|
||||
}
|
||||
|
||||
|
||||
bool Serialization::CXmlIArchive::operator()(double& value, const char* name, [[maybe_unused]] const char* label)
|
||||
{
|
||||
return XmlUtil::ReadChildNode(m_pRootNode, m_childIndexOverride, m_childIndexHint, name, value);
|
||||
}
|
||||
|
||||
|
||||
bool Serialization::CXmlIArchive::operator()(int16& value, const char* name, [[maybe_unused]] const char* label)
|
||||
{
|
||||
return XmlUtil::ReadChildNodeAs< int >(m_pRootNode, m_childIndexOverride, m_childIndexHint, name, value);
|
||||
}
|
||||
|
||||
|
||||
bool Serialization::CXmlIArchive::operator()(uint16& value, const char* name, [[maybe_unused]] const char* label)
|
||||
{
|
||||
return XmlUtil::ReadChildNodeAs< uint >(m_pRootNode, m_childIndexOverride, m_childIndexHint, name, value);
|
||||
}
|
||||
|
||||
|
||||
bool Serialization::CXmlIArchive::operator()(int32& value, const char* name, [[maybe_unused]] const char* label)
|
||||
{
|
||||
return XmlUtil::ReadChildNode(m_pRootNode, m_childIndexOverride, m_childIndexHint, name, value);
|
||||
}
|
||||
|
||||
|
||||
bool Serialization::CXmlIArchive::operator()(uint32& value, const char* name, [[maybe_unused]] const char* label)
|
||||
{
|
||||
return XmlUtil::ReadChildNode(m_pRootNode, m_childIndexOverride, m_childIndexHint, name, value);
|
||||
}
|
||||
|
||||
|
||||
bool Serialization::CXmlIArchive::operator()(int64& value, const char* name, [[maybe_unused]] const char* label)
|
||||
{
|
||||
return XmlUtil::ReadChildNode(m_pRootNode, m_childIndexOverride, m_childIndexHint, name, value);
|
||||
}
|
||||
|
||||
|
||||
bool Serialization::CXmlIArchive::operator()(uint64& value, const char* name, [[maybe_unused]] const char* label)
|
||||
{
|
||||
return XmlUtil::ReadChildNode(m_pRootNode, m_childIndexOverride, m_childIndexHint, name, value);
|
||||
}
|
||||
|
||||
|
||||
bool Serialization::CXmlIArchive::operator()(int8& value, const char* name, [[maybe_unused]] const char* label)
|
||||
{
|
||||
return XmlUtil::ReadChildNodeAs< int >(m_pRootNode, m_childIndexOverride, m_childIndexHint, name, value);
|
||||
}
|
||||
|
||||
|
||||
bool Serialization::CXmlIArchive::operator()(uint8& value, const char* name, [[maybe_unused]] const char* label)
|
||||
{
|
||||
return XmlUtil::ReadChildNodeAs< uint >(m_pRootNode, m_childIndexOverride, m_childIndexHint, name, value);
|
||||
}
|
||||
|
||||
|
||||
bool Serialization::CXmlIArchive::operator()(char& value, const char* name, [[maybe_unused]] const char* label)
|
||||
{
|
||||
return XmlUtil::ReadChildNodeAs< int >(m_pRootNode, m_childIndexOverride, m_childIndexHint, name, value);
|
||||
}
|
||||
|
||||
|
||||
bool Serialization::CXmlIArchive::operator()(const SStruct& ser, const char* name, [[maybe_unused]] const char* label)
|
||||
{
|
||||
CRY_ASSERT(name);
|
||||
CRY_ASSERT(name[ 0 ]);
|
||||
|
||||
XmlNodeRef pChild = XmlUtil::FindChildNode(m_pRootNode, m_childIndexOverride, m_childIndexHint, name);
|
||||
if (pChild)
|
||||
{
|
||||
CXmlIArchive childArchive(pChild);
|
||||
childArchive.SetFilter(GetFilter());
|
||||
childArchive.SetInnerContext(GetInnerContext());
|
||||
|
||||
const bool serializeSuccess = ser(childArchive);
|
||||
return serializeSuccess;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool Serialization::CXmlIArchive::operator()(IContainer& ser, const char* name, [[maybe_unused]] const char* label)
|
||||
{
|
||||
CRY_ASSERT(name);
|
||||
CRY_ASSERT(name[ 0 ]);
|
||||
|
||||
bool serializeSuccess = true;
|
||||
|
||||
XmlNodeRef pChild = XmlUtil::FindChildNode(m_pRootNode, m_childIndexOverride, m_childIndexHint, name);
|
||||
if (pChild)
|
||||
{
|
||||
const int elementCount = pChild->getChildCount();
|
||||
ser.resize(elementCount);
|
||||
|
||||
if (0 < elementCount)
|
||||
{
|
||||
CXmlIArchive childArchive(pChild);
|
||||
childArchive.SetFilter(GetFilter());
|
||||
childArchive.SetInnerContext(GetInnerContext());
|
||||
|
||||
for (int i = 0; i < elementCount; ++i)
|
||||
{
|
||||
childArchive.m_childIndexOverride = i;
|
||||
|
||||
serializeSuccess &= ser(childArchive, "Element", "Element");
|
||||
ser.next();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return serializeSuccess;
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
// Original file Copyright Crytek GMBH or its affiliates, used under license.
|
||||
|
||||
#ifndef __XML_I_ARCHIVE__H__
|
||||
#define __XML_I_ARCHIVE__H__
|
||||
|
||||
#include <Serialization/IArchive.h>
|
||||
|
||||
namespace Serialization
|
||||
{
|
||||
class CXmlIArchive
|
||||
: public IArchive
|
||||
{
|
||||
public:
|
||||
CXmlIArchive();
|
||||
CXmlIArchive(XmlNodeRef pRootNode);
|
||||
~CXmlIArchive();
|
||||
|
||||
void SetXmlNode(XmlNodeRef pNode);
|
||||
XmlNodeRef GetXmlNode() const;
|
||||
|
||||
// IArchive
|
||||
bool operator()(bool& value, const char* name = "", const char* label = 0) override;
|
||||
bool operator()(IString& value, const char* name = "", const char* label = 0) override;
|
||||
bool operator()(IWString& value, const char* name = "", const char* label = 0) override;
|
||||
bool operator()(float& value, const char* name = "", const char* label = 0) override;
|
||||
bool operator()(double& value, const char* name = "", const char* label = 0) override;
|
||||
bool operator()(int16& value, const char* name = "", const char* label = 0) override;
|
||||
bool operator()(uint16& value, const char* name = "", const char* label = 0) override;
|
||||
bool operator()(int32& value, const char* name = "", const char* label = 0) override;
|
||||
bool operator()(uint32& value, const char* name = "", const char* label = 0) override;
|
||||
bool operator()(int64& value, const char* name = "", const char* label = 0) override;
|
||||
bool operator()(uint64& value, const char* name = "", const char* label = 0) override;
|
||||
|
||||
bool operator()(int8& value, const char* name = "", const char* label = 0) override;
|
||||
bool operator()(uint8& value, const char* name = "", const char* label = 0) override;
|
||||
bool operator()(char& value, const char* name = "", const char* label = 0) override;
|
||||
|
||||
bool operator()(const SStruct& ser, const char* name = "", const char* label = 0) override;
|
||||
bool operator()(IContainer& ser, const char* name = "", const char* label = 0) override;
|
||||
// ~IArchive
|
||||
|
||||
using IArchive::operator();
|
||||
|
||||
private:
|
||||
XmlNodeRef m_pRootNode;
|
||||
int m_childIndexOverride;
|
||||
int m_childIndexHint;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,213 +0,0 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
// Original file Copyright Crytek GMBH or its affiliates, used under license.
|
||||
|
||||
#include "CrySystem_precompiled.h"
|
||||
#include "CryExtension/Impl/ClassWeaver.h"
|
||||
|
||||
#include <Serialization/STL.h>
|
||||
#include <Serialization/IClassFactory.h>
|
||||
|
||||
#include "XmlOArchive.h"
|
||||
|
||||
#include <Serialization/STLImpl.h>
|
||||
#include <Serialization/ClassFactory.h>
|
||||
|
||||
namespace XmlUtil
|
||||
{
|
||||
XmlNodeRef CreateChildNode(XmlNodeRef pParent, const char* const name)
|
||||
{
|
||||
CRY_ASSERT(pParent);
|
||||
CRY_ASSERT(name);
|
||||
CRY_ASSERT(name[ 0 ]);
|
||||
|
||||
XmlNodeRef pChild = pParent->createNode(name);
|
||||
CRY_ASSERT(pChild);
|
||||
|
||||
pParent->addChild(pChild);
|
||||
return pChild;
|
||||
}
|
||||
|
||||
template < typename T, typename TIn >
|
||||
bool WriteChildNodeAs(XmlNodeRef pParent, const char* const name, const TIn& value)
|
||||
{
|
||||
XmlNodeRef pChild = XmlUtil::CreateChildNode(pParent, name);
|
||||
CRY_ASSERT(pChild);
|
||||
|
||||
pChild->setAttr("value", static_cast< T >(value));
|
||||
return true;
|
||||
}
|
||||
|
||||
template < typename T >
|
||||
bool WriteChildNode(XmlNodeRef pParent, const char* const name, const T& value)
|
||||
{
|
||||
return WriteChildNodeAs< T >(pParent, name, value);
|
||||
}
|
||||
}
|
||||
|
||||
Serialization::CXmlOArchive::CXmlOArchive()
|
||||
: IArchive(OUTPUT | NO_EMPTY_NAMES)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
Serialization::CXmlOArchive::CXmlOArchive(XmlNodeRef pRootNode)
|
||||
: IArchive(OUTPUT | NO_EMPTY_NAMES)
|
||||
, m_pRootNode(pRootNode)
|
||||
{
|
||||
CRY_ASSERT(m_pRootNode);
|
||||
}
|
||||
|
||||
|
||||
Serialization::CXmlOArchive::~CXmlOArchive()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void Serialization::CXmlOArchive::SetXmlNode(XmlNodeRef pNode)
|
||||
{
|
||||
m_pRootNode = pNode;
|
||||
}
|
||||
|
||||
|
||||
XmlNodeRef Serialization::CXmlOArchive::GetXmlNode() const
|
||||
{
|
||||
return m_pRootNode;
|
||||
}
|
||||
|
||||
|
||||
bool Serialization::CXmlOArchive::operator()(bool& value, const char* name, [[maybe_unused]] const char* label)
|
||||
{
|
||||
const char* const stringValue = value ? "true" : "false";
|
||||
return XmlUtil::WriteChildNode(m_pRootNode, name, stringValue);
|
||||
}
|
||||
|
||||
|
||||
bool Serialization::CXmlOArchive::operator()(IString& value, const char* name, [[maybe_unused]] const char* label)
|
||||
{
|
||||
const char* const stringValue = value.get();
|
||||
return XmlUtil::WriteChildNode(m_pRootNode, name, stringValue);
|
||||
}
|
||||
|
||||
|
||||
bool Serialization::CXmlOArchive::operator()([[maybe_unused]] IWString& value, [[maybe_unused]] const char* name, [[maybe_unused]] const char* label)
|
||||
{
|
||||
CryFatalError("CXmlOArchive::operator() with IWString is not implemented");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool Serialization::CXmlOArchive::operator()(float& value, const char* name, [[maybe_unused]] const char* label)
|
||||
{
|
||||
return XmlUtil::WriteChildNode(m_pRootNode, name, value);
|
||||
}
|
||||
|
||||
|
||||
bool Serialization::CXmlOArchive::operator()(double& value, const char* name, [[maybe_unused]] const char* label)
|
||||
{
|
||||
return XmlUtil::WriteChildNode(m_pRootNode, name, value);
|
||||
}
|
||||
|
||||
|
||||
bool Serialization::CXmlOArchive::operator()(int16& value, const char* name, [[maybe_unused]] const char* label)
|
||||
{
|
||||
return XmlUtil::WriteChildNodeAs< int >(m_pRootNode, name, value);
|
||||
}
|
||||
|
||||
|
||||
bool Serialization::CXmlOArchive::operator()(uint16& value, const char* name, [[maybe_unused]] const char* label)
|
||||
{
|
||||
return XmlUtil::WriteChildNodeAs< uint >(m_pRootNode, name, value);
|
||||
}
|
||||
|
||||
|
||||
bool Serialization::CXmlOArchive::operator()(int32& value, const char* name, [[maybe_unused]] const char* label)
|
||||
{
|
||||
return XmlUtil::WriteChildNode(m_pRootNode, name, value);
|
||||
}
|
||||
|
||||
|
||||
bool Serialization::CXmlOArchive::operator()(uint32& value, const char* name, [[maybe_unused]] const char* label)
|
||||
{
|
||||
return XmlUtil::WriteChildNode(m_pRootNode, name, value);
|
||||
}
|
||||
|
||||
|
||||
bool Serialization::CXmlOArchive::operator()(int64& value, const char* name, [[maybe_unused]] const char* label)
|
||||
{
|
||||
return XmlUtil::WriteChildNode(m_pRootNode, name, value);
|
||||
}
|
||||
|
||||
|
||||
bool Serialization::CXmlOArchive::operator()(uint64& value, const char* name, [[maybe_unused]] const char* label)
|
||||
{
|
||||
return XmlUtil::WriteChildNode(m_pRootNode, name, value);
|
||||
}
|
||||
|
||||
|
||||
bool Serialization::CXmlOArchive::operator()(int8& value, const char* name, [[maybe_unused]] const char* label)
|
||||
{
|
||||
return XmlUtil::WriteChildNodeAs< int >(m_pRootNode, name, value);
|
||||
}
|
||||
|
||||
|
||||
bool Serialization::CXmlOArchive::operator()(uint8& value, const char* name, [[maybe_unused]] const char* label)
|
||||
{
|
||||
return XmlUtil::WriteChildNodeAs< uint >(m_pRootNode, name, value);
|
||||
}
|
||||
|
||||
|
||||
bool Serialization::CXmlOArchive::operator()(char& value, const char* name, [[maybe_unused]] const char* label)
|
||||
{
|
||||
return XmlUtil::WriteChildNodeAs< int >(m_pRootNode, name, value);
|
||||
}
|
||||
|
||||
|
||||
bool Serialization::CXmlOArchive::operator()(const SStruct& ser, const char* name, [[maybe_unused]] const char* label)
|
||||
{
|
||||
CRY_ASSERT(name);
|
||||
CRY_ASSERT(name[ 0 ]);
|
||||
|
||||
XmlNodeRef pChild = XmlUtil::CreateChildNode(m_pRootNode, name);
|
||||
CXmlOArchive childArchive(pChild);
|
||||
childArchive.SetFilter(GetFilter());
|
||||
childArchive.SetInnerContext(GetInnerContext());
|
||||
|
||||
const bool serializeSuccess = ser(childArchive);
|
||||
|
||||
return serializeSuccess;
|
||||
}
|
||||
|
||||
|
||||
bool Serialization::CXmlOArchive::operator()(IContainer& ser, const char* name, [[maybe_unused]] const char* label)
|
||||
{
|
||||
CRY_ASSERT(name);
|
||||
CRY_ASSERT(name[ 0 ]);
|
||||
|
||||
bool serializeSuccess = true;
|
||||
|
||||
XmlNodeRef pChild = XmlUtil::CreateChildNode(m_pRootNode, name);
|
||||
CXmlOArchive childArchive(pChild);
|
||||
childArchive.SetFilter(GetFilter());
|
||||
childArchive.SetInnerContext(GetInnerContext());
|
||||
|
||||
const size_t containerSize = ser.size();
|
||||
if (0 < containerSize)
|
||||
{
|
||||
do
|
||||
{
|
||||
serializeSuccess &= ser(childArchive, "Element", "Element");
|
||||
} while (ser.next());
|
||||
}
|
||||
|
||||
return serializeSuccess;
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
// Original file Copyright Crytek GMBH or its affiliates, used under license.
|
||||
|
||||
#ifndef __XML_O_ARCHIVE__H__
|
||||
#define __XML_O_ARCHIVE__H__
|
||||
|
||||
#include <Serialization/IArchive.h>
|
||||
|
||||
namespace Serialization
|
||||
{
|
||||
class CXmlOArchive
|
||||
: public IArchive
|
||||
{
|
||||
public:
|
||||
CXmlOArchive();
|
||||
CXmlOArchive(XmlNodeRef pRootNode);
|
||||
~CXmlOArchive();
|
||||
|
||||
void SetXmlNode(XmlNodeRef pNode);
|
||||
XmlNodeRef GetXmlNode() const;
|
||||
|
||||
// IArchive
|
||||
bool operator()(bool& value, const char* name = "", const char* label = 0) override;
|
||||
bool operator()(IString& value, const char* name = "", const char* label = 0) override;
|
||||
bool operator()(IWString& value, const char* name = "", const char* label = 0) override;
|
||||
bool operator()(float& value, const char* name = "", const char* label = 0) override;
|
||||
bool operator()(double& value, const char* name = "", const char* label = 0) override;
|
||||
bool operator()(int16& value, const char* name = "", const char* label = 0) override;
|
||||
bool operator()(uint16& value, const char* name = "", const char* label = 0) override;
|
||||
bool operator()(int32& value, const char* name = "", const char* label = 0) override;
|
||||
bool operator()(uint32& value, const char* name = "", const char* label = 0) override;
|
||||
bool operator()(int64& value, const char* name = "", const char* label = 0) override;
|
||||
bool operator()(uint64& value, const char* name = "", const char* label = 0) override;
|
||||
|
||||
bool operator()(int8& value, const char* name = "", const char* label = 0) override;
|
||||
bool operator()(uint8& value, const char* name = "", const char* label = 0) override;
|
||||
bool operator()(char& value, const char* name = "", const char* label = 0) override;
|
||||
|
||||
bool operator()(const SStruct& ser, const char* name = "", const char* label = 0) override;
|
||||
bool operator()(IContainer& ser, const char* name = "", const char* label = 0) override;
|
||||
// ~IArchive
|
||||
|
||||
using IArchive::operator();
|
||||
|
||||
private:
|
||||
XmlNodeRef m_pRootNode;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,475 +0,0 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
// Original file Copyright Crytek GMBH or its affiliates, used under license.
|
||||
|
||||
// Description : Service network implementation
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "IServiceNetwork.h"
|
||||
#include <AzCore/Socket/AzSocket_fwd.h>
|
||||
|
||||
class CServiceNetwork;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// General message buffer
|
||||
class CServiceNetworkMessage
|
||||
: public IServiceNetworkMessage
|
||||
{
|
||||
private:
|
||||
void* m_pData;
|
||||
uint32 m_id;
|
||||
uint32 m_size;
|
||||
int volatile m_refCount;
|
||||
|
||||
public:
|
||||
CServiceNetworkMessage(const uint32 id, const uint32 size);
|
||||
virtual ~CServiceNetworkMessage();
|
||||
|
||||
// IServiceNetworMessage interface
|
||||
virtual uint32 GetId() const;
|
||||
virtual uint32 GetSize() const;
|
||||
virtual void* GetPointer();
|
||||
virtual const void* GetPointer() const;
|
||||
virtual struct IDataReadStream* CreateReader() const;
|
||||
virtual void AddRef();
|
||||
virtual void Release();
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// General network TCP/IP connection
|
||||
class CServiceNetworkConnection
|
||||
: public IServiceNetworkConnection
|
||||
{
|
||||
public:
|
||||
friend class CServiceNetworkListener;
|
||||
|
||||
// maximum size of a single message (0.5MB by default)
|
||||
static const uint32 kMaximumMessageSize = 5 << 19;
|
||||
|
||||
// initialization message send period (ms)
|
||||
static const uint64 kInitializationPerior = 1000;
|
||||
|
||||
// keep alive period (ms), by default every 2s
|
||||
static const uint64 kKeepAlivePeriod = 2000;
|
||||
|
||||
// reconnection retries period (ms)
|
||||
static const uint64 kReconnectTryPerior = 1000;
|
||||
|
||||
// timeout for assuming server side connection dead (reconnection timeout)
|
||||
static const uint64 hReconnectTimeOut = 30 * 1000;
|
||||
|
||||
// communication time out (ms)
|
||||
static const uint64 kTimeout = 5000;
|
||||
|
||||
// Type of endpoint
|
||||
enum EEndpoint
|
||||
{
|
||||
// This is the server side of the connection (on the side of the listening socket)
|
||||
eEndpoint_Server,
|
||||
|
||||
// This is the client side of the connection (we connected to the listening socket)
|
||||
eEndpoint_Client,
|
||||
};
|
||||
|
||||
// Internal state machine
|
||||
enum EState
|
||||
{
|
||||
// Connection is initializing
|
||||
eState_Initializing,
|
||||
|
||||
// Connection is valid
|
||||
eState_Valid,
|
||||
|
||||
// Operation on the socket failed (we may need to reconnect)
|
||||
eState_Lost,
|
||||
|
||||
// Connection is closed
|
||||
eState_Closed,
|
||||
};
|
||||
|
||||
// Command IDs, do not change the numerical values
|
||||
enum ECommand
|
||||
{
|
||||
// Data block command
|
||||
eCommand_Data = 1,
|
||||
|
||||
// Keep alive command
|
||||
eCommand_KeepAlive = 2,
|
||||
|
||||
// Initialize communication channel (sent only once)
|
||||
eCommand_Initialize = 3,
|
||||
};
|
||||
|
||||
#pragma pack(push)
|
||||
#pragma pack(1)
|
||||
|
||||
struct Header
|
||||
{
|
||||
uint8 m_cmd;
|
||||
uint32 m_size;
|
||||
|
||||
void Swap();
|
||||
};
|
||||
|
||||
struct InitHeader
|
||||
{
|
||||
uint8 m_cmd;
|
||||
uint8 m_pad0;
|
||||
uint8 m_pad1;
|
||||
uint8 m_pad2;
|
||||
uint32 m_tryCount;
|
||||
uint64 m_guid0;
|
||||
uint64 m_guid1;
|
||||
|
||||
void Swap();
|
||||
};
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
private:
|
||||
CServiceNetwork* m_pManager;
|
||||
|
||||
// Type of endpoint (client/server)
|
||||
EEndpoint m_endpointType;
|
||||
|
||||
// Connection state (internal)
|
||||
EState m_state;
|
||||
|
||||
// Reference count (updated using CryInterlocked* functions)
|
||||
int volatile m_refCount;
|
||||
|
||||
// Internal socket data
|
||||
AZSOCKET m_socket;
|
||||
|
||||
// Local address
|
||||
ServiceNetworkAddress m_localAddress;
|
||||
|
||||
// Remote connection address
|
||||
ServiceNetworkAddress m_remoteAddress;
|
||||
|
||||
// Internal connection ID (unique)
|
||||
CryGUID m_connectionID;
|
||||
|
||||
// Internal time counters
|
||||
uint64 m_lastReconnectTime;
|
||||
uint64 m_lastKeepAliveSendTime;
|
||||
uint64 m_lastMessageReceivedTime;
|
||||
uint64 m_lastInitializationSendTime;
|
||||
uint32 m_reconnectTryCount;
|
||||
|
||||
// Statistics (updated from threads using CryIntelocked* functions)
|
||||
volatile uint32 m_statsNumPacketsSend;
|
||||
volatile uint32 m_statsNumPacketsReceived;
|
||||
volatile uint32 m_statsNumDataSend;
|
||||
volatile uint32 m_statsNumDataReceived;
|
||||
|
||||
// Queue of messages to send (thread access possible)
|
||||
typedef CryMT::CLocklessPointerQueue< CServiceNetworkMessage > TSendQueue;
|
||||
CServiceNetworkMessage* m_pSendedMessages;
|
||||
TSendQueue m_pSendQueue;
|
||||
uint32 m_messageDataSentSoFar;
|
||||
volatile int m_sendQueueDataSize;
|
||||
|
||||
// Queue of received message
|
||||
typedef CryMT::CLocklessPointerQueue< CServiceNetworkMessage > TReceiveQueue;
|
||||
TReceiveQueue m_pReceiveQueue;
|
||||
uint32 m_receiveQueueDataSize;
|
||||
uint32 m_messageDataReceivedSoFar;
|
||||
uint32 m_messageReceiveLength;
|
||||
|
||||
// Message being received "right now"
|
||||
CServiceNetworkMessage* m_pCurrentReceiveMessage;
|
||||
uint32 m_messageDummyReadLength;
|
||||
|
||||
// External request to close this connection was issued
|
||||
bool m_bCloseRequested;
|
||||
|
||||
// Do not accept any new data for sending or receiving
|
||||
bool m_bDisableCommunication;
|
||||
|
||||
public:
|
||||
ILINE bool IsInitialized() const
|
||||
{
|
||||
return m_state != eState_Initializing;
|
||||
}
|
||||
|
||||
ILINE bool IsSendingQueueEmpty() const
|
||||
{
|
||||
return m_pSendQueue.empty();
|
||||
}
|
||||
|
||||
ILINE CServiceNetwork* GetManager() const
|
||||
{
|
||||
return m_pManager;
|
||||
}
|
||||
|
||||
public:
|
||||
CServiceNetworkConnection(
|
||||
class CServiceNetwork* manager,
|
||||
EEndpoint endpointType,
|
||||
AZSOCKET socket,
|
||||
const CryGUID& connectionID,
|
||||
const ServiceNetworkAddress& localAddress,
|
||||
const ServiceNetworkAddress& remoteAddress);
|
||||
|
||||
virtual ~CServiceNetworkConnection();
|
||||
|
||||
// IServiceNetworkConnection interface implementation
|
||||
virtual const ServiceNetworkAddress& GetRemoteAddress() const;
|
||||
virtual const ServiceNetworkAddress& GetLocalAddress() const;
|
||||
virtual const CryGUID& GetGUID() const;
|
||||
virtual bool IsAlive() const;
|
||||
virtual uint32 GetMessageSendCount() const;
|
||||
virtual uint32 GetMessageReceivedCount() const;
|
||||
virtual uint64 GetMessageSendDataSize() const;
|
||||
virtual uint64 GetMessageReceivedDataSize() const;
|
||||
virtual bool SendMsg(IServiceNetworkMessage* message);
|
||||
virtual IServiceNetworkMessage* ReceiveMsg();
|
||||
virtual void FlushAndClose(const uint32 timeout);
|
||||
virtual void FlushAndWait();
|
||||
virtual void Close();
|
||||
virtual void AddRef();
|
||||
virtual void Release();
|
||||
|
||||
// All remote connections are updated on the client side
|
||||
// This is called from service network update thread, try not to call by hand :)
|
||||
void Update();
|
||||
|
||||
private:
|
||||
void ProcessSendingQueue();
|
||||
void ProcessReceivingQueue();
|
||||
|
||||
// Keep alive message handling
|
||||
void ProcessKeepAlive();
|
||||
void SendKeepAlive(const uint64 currentNetworkTime);
|
||||
bool HandleTimeout(const uint64 currentNetworkTime);
|
||||
|
||||
// Handle the reconnection request
|
||||
bool HandleReconnect(AZSOCKET socket, const uint32 tryCount);
|
||||
|
||||
// General send/receive functions with error handling.
|
||||
// If socket error occurs the connection will be put in the lost state.
|
||||
uint32 TrySend(const void* dataBuffer, uint32 dataSize, bool autoHandleErrors);
|
||||
|
||||
// Internal receive function with error handling
|
||||
uint32 TryReceive(void* dataBuffer, uint32 dataSize, bool autoHandleErrors);
|
||||
|
||||
// Try to reconnect to the remote address
|
||||
bool TryReconnect();
|
||||
|
||||
// Try to send the initialization header
|
||||
bool TryInitialize();
|
||||
|
||||
// Low-level socket shutdown (hash way)
|
||||
void Shutdown();
|
||||
|
||||
// Reset the connection (put in the lost state and reconnect)
|
||||
void Reset();
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// TCP/IP listener
|
||||
class CServiceNetworkListener
|
||||
: public IServiceNetworkListener
|
||||
{
|
||||
typedef CServiceNetworkConnection::InitHeader TInitHeader;
|
||||
|
||||
struct PendingConnection
|
||||
{
|
||||
// Connection socket
|
||||
AZSOCKET m_socket;
|
||||
|
||||
// Initialization of initialization header received so far
|
||||
uint32 m_dataReceivedSoFar;
|
||||
|
||||
// Initialization header
|
||||
TInitHeader m_initHeader;
|
||||
|
||||
// Remote address (as returned from accept)
|
||||
ServiceNetworkAddress m_remoteAddress;
|
||||
};
|
||||
|
||||
protected:
|
||||
// Owner (the manager)
|
||||
CServiceNetwork* m_pManager;
|
||||
|
||||
// Reference count, updated using CryInterlocked* functions
|
||||
int volatile m_refCount;
|
||||
|
||||
// Listening socket
|
||||
AZSOCKET m_socket;
|
||||
|
||||
// Local address (usually has the IP in 127.0.0.1:port form)
|
||||
ServiceNetworkAddress m_localAddress;
|
||||
|
||||
// Request to close this listener was received
|
||||
bool m_closeRequestReceived;
|
||||
|
||||
// Pending connections (but not yet initialized)
|
||||
typedef std::vector< PendingConnection* > TPendingConnectionList;
|
||||
TPendingConnectionList m_pPendingConnections;
|
||||
|
||||
// All active connections spawned from this listener
|
||||
typedef std::vector< CServiceNetworkConnection* > TConnectionList;
|
||||
TConnectionList m_pLocalConnections;
|
||||
|
||||
// Access lock for the class members (thread safe)
|
||||
CryMutex m_accessLock;
|
||||
|
||||
public:
|
||||
ILINE CServiceNetwork* GetManager() const
|
||||
{
|
||||
return m_pManager;
|
||||
}
|
||||
|
||||
public:
|
||||
CServiceNetworkListener(CServiceNetwork* pManager, AZSOCKET socket, const ServiceNetworkAddress& address);
|
||||
virtual ~CServiceNetworkListener();
|
||||
|
||||
void Update();
|
||||
|
||||
// IServiceNetworkListener interface implementation
|
||||
virtual const ServiceNetworkAddress& GetLocalAddress() const;
|
||||
virtual uint32 GetConnectionCount() const;
|
||||
virtual IServiceNetworkConnection* Accept();
|
||||
virtual bool IsAlive() const;
|
||||
virtual void AddRef();
|
||||
virtual void Release();
|
||||
virtual void Close();
|
||||
|
||||
private:
|
||||
void ProcessIncomingConnections();
|
||||
void ProcessPendingConnections();
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// TCP/IP manager for service connection channels
|
||||
class CServiceNetwork
|
||||
: public IServiceNetwork
|
||||
, public CryRunnable
|
||||
{
|
||||
#if defined(AZ_RESTRICTED_PLATFORM)
|
||||
#include AZ_RESTRICTED_FILE(ServiceNetwork_h)
|
||||
#endif
|
||||
|
||||
protected:
|
||||
struct ConnectionToClose
|
||||
{
|
||||
CServiceNetworkConnection* pConnection;
|
||||
|
||||
// timeout for forded close
|
||||
uint64 maxWaitTime;
|
||||
};
|
||||
|
||||
protected:
|
||||
// Local listeners
|
||||
typedef std::vector< CServiceNetworkListener* > TListenerArray;
|
||||
TListenerArray m_pListeners;
|
||||
|
||||
// Local connections
|
||||
typedef std::vector< CServiceNetworkConnection* > TConnectionArray;
|
||||
TConnectionArray m_pConnections;
|
||||
|
||||
// Connections that are waiting for all of their data to be sent before closing
|
||||
typedef std::vector< ConnectionToClose > TConnectionsToCloseArray;
|
||||
TConnectionsToCloseArray m_connectionsToClose;
|
||||
|
||||
// We are running on threads, needed to sync the access to arrays
|
||||
CryMutex m_accessMutex;
|
||||
|
||||
// Current network time (ms)
|
||||
uint64 m_networkTime;
|
||||
|
||||
// Exit was requested
|
||||
bool m_bExitRequested;
|
||||
|
||||
// Message verbose level
|
||||
ICVar* m_pVerboseLevel;
|
||||
|
||||
// Thread
|
||||
typedef CryThread< CServiceNetwork > TServiceNetworkThread;
|
||||
TServiceNetworkThread* m_pThread;
|
||||
|
||||
// Buffer ID allocator (unique, incremented atomically using CryInterlockedIncrement)
|
||||
volatile int m_bufferID;
|
||||
|
||||
// Random number generator for GUID creation
|
||||
CRndGen m_guidGenerator;
|
||||
|
||||
// Send/Receive queue size limit
|
||||
ICVar* m_pReceiveDataQueueLimit;
|
||||
ICVar* m_pSendDataQueueLimit;
|
||||
|
||||
public:
|
||||
ILINE const uint64 GetNetworkTime() const
|
||||
{
|
||||
return m_networkTime;
|
||||
}
|
||||
|
||||
ILINE const CServiceNetwork* GetManager() const
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
ILINE const uint32 GetReceivedDataQueueLimit() const
|
||||
{
|
||||
return m_pReceiveDataQueueLimit->GetIVal();
|
||||
}
|
||||
|
||||
ILINE const uint32 GetSendDataQueueLimit() const
|
||||
{
|
||||
return m_pSendDataQueueLimit->GetIVal();
|
||||
}
|
||||
|
||||
public:
|
||||
CServiceNetwork();
|
||||
virtual ~CServiceNetwork();
|
||||
|
||||
// IServiceNetwork interface implementation
|
||||
virtual void SetVerbosityLevel(const uint32 level);
|
||||
virtual IServiceNetworkMessage* AllocMessageBuffer(const uint32 size);
|
||||
virtual struct IDataWriteStream* CreateMessageWriter();
|
||||
virtual struct IDataReadStream* CreateMessageReader(const void* pData, const uint32 dataSize);
|
||||
virtual ServiceNetworkAddress GetHostAddress(const string& addressString, uint16 optionalPort = 0) const;
|
||||
virtual IServiceNetworkListener* CreateListener(uint16 localPort);
|
||||
virtual IServiceNetworkConnection* Connect(const ServiceNetworkAddress& remoteAddress);
|
||||
|
||||
// CryRunnable
|
||||
virtual void Run();
|
||||
virtual void Cancel();
|
||||
|
||||
// Register connection in the connection list (thread safe)
|
||||
void RegisterConnection(CServiceNetworkConnection& con);
|
||||
|
||||
// Register connection for closing one all of the outgoing messages are sent
|
||||
void RegisterForDeferredClose(CServiceNetworkConnection& con, const uint32 timeout);
|
||||
|
||||
// Debug print
|
||||
#ifdef RELEASE
|
||||
void Log([[maybe_unused]] const char* txt, ...) const {};
|
||||
bool CheckVerbose([[maybe_unused]] const uint32 level) const { return false; }
|
||||
#else
|
||||
void Log(const char* txt, ...) const;
|
||||
bool CheckVerbose(const uint32 level) const;
|
||||
#endif
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@@ -1,787 +0,0 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
// Original file Copyright Crytek GMBH or its affiliates, used under license.
|
||||
|
||||
#include "CrySystem_precompiled.h"
|
||||
|
||||
#ifdef SOFTCODE_SYSTEM_ENABLED
|
||||
|
||||
#ifndef SOFTCODE_ENABLED
|
||||
// Even if this module isn't built with SC enabled, if the SC system is enabled we define
|
||||
// it for this compilation unit to ensure we use the correct versions of the IType* interfaces.
|
||||
#define SOFTCODE_ENABLED
|
||||
#endif
|
||||
|
||||
#include "SoftCodeMgr.h"
|
||||
#include <IConsole.h>
|
||||
#include <CryLibrary.h>
|
||||
#include <CryPath.h>
|
||||
#include <AzCore/std/functional.h> // for function<> in find files
|
||||
|
||||
// This should resolve to "GetTypeLibrary" but we export by ordinal to avoid overheads on 360
|
||||
// and keep everything consistent.
|
||||
static const char* DLL_GETTYPELIBRARY = (LPCSTR)1;
|
||||
|
||||
struct CInstanceData
|
||||
{
|
||||
CInstanceData(void* pInstance, size_t memberCount)
|
||||
: m_pOldInstance(pInstance)
|
||||
, m_pNewInstance()
|
||||
{
|
||||
m_members.resize(memberCount);
|
||||
}
|
||||
|
||||
~CInstanceData()
|
||||
{
|
||||
// Delete all members
|
||||
for (TMemberVec::iterator iter(m_members.begin());
|
||||
iter != m_members.end();
|
||||
++iter)
|
||||
{
|
||||
// TODO: Safe cross module? Same allocator? Use a Destroy() method?
|
||||
delete *iter;
|
||||
}
|
||||
}
|
||||
|
||||
void* Instance() { return m_pOldInstance; }
|
||||
|
||||
void AddMember(size_t index, IExchangeValue& value)
|
||||
{
|
||||
// TODO: Add support for members with same name at different hierarchy levels
|
||||
assert(m_members[index] == NULL);
|
||||
assert(index != ~0);
|
||||
|
||||
// Support expansion of m_members during while resolving members
|
||||
if (index >= m_members.size())
|
||||
{
|
||||
m_members.resize(index + 1);
|
||||
}
|
||||
|
||||
m_members[index] = value.Clone();
|
||||
}
|
||||
|
||||
IExchangeValue* GetMember(size_t index) const
|
||||
{
|
||||
assert(index < m_members.size());
|
||||
return m_members[index];
|
||||
}
|
||||
|
||||
void SetNewInstance(void* pNewInstance) { m_pNewInstance = pNewInstance; }
|
||||
|
||||
void* m_pOldInstance;
|
||||
void* m_pNewInstance;
|
||||
|
||||
typedef std::vector<IExchangeValue*> TMemberVec;
|
||||
TMemberVec m_members;
|
||||
};
|
||||
|
||||
|
||||
class CExchanger
|
||||
: public IExchanger
|
||||
{
|
||||
public:
|
||||
CExchanger()
|
||||
: m_pInstanceData()
|
||||
, m_instanceIndex(~0)
|
||||
, m_state(eState_ResolvingMembers)
|
||||
{}
|
||||
|
||||
virtual ~CExchanger()
|
||||
{
|
||||
DestroyInstanceData();
|
||||
}
|
||||
|
||||
virtual bool IsLoading() const { return m_state >= eState_WritingNewMembers; }
|
||||
virtual size_t InstanceCount() const { return m_instances.size(); }
|
||||
|
||||
virtual bool BeginInstance(void* pInstance)
|
||||
{
|
||||
if (IsLoading())
|
||||
{
|
||||
if (++m_instanceIndex < m_instances.size())
|
||||
{
|
||||
m_pInstanceData = m_instances[m_instanceIndex];
|
||||
m_pInstanceData->SetNewInstance(pInstance);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_pInstanceData = NULL;
|
||||
}
|
||||
}
|
||||
else // Reading/resolving members
|
||||
{
|
||||
m_instanceIndex = m_instances.size();
|
||||
m_pInstanceData = new CInstanceData(pInstance, m_memberMap.size());
|
||||
m_instances.push_back(m_pInstanceData);
|
||||
}
|
||||
|
||||
return m_pInstanceData != NULL;
|
||||
}
|
||||
|
||||
virtual bool SetValue(const char* name, IExchangeValue& value)
|
||||
{
|
||||
assert(!IsLoading());
|
||||
|
||||
const size_t index = FindMemberIndex(name);
|
||||
const bool consumingValue = index != ~0;
|
||||
|
||||
if (consumingValue)
|
||||
{
|
||||
m_pInstanceData->AddMember(index, value);
|
||||
}
|
||||
|
||||
return consumingValue;
|
||||
}
|
||||
|
||||
virtual IExchangeValue* GetValue(const char* name, void* pTarget, size_t targetSize)
|
||||
{
|
||||
assert(IsLoading());
|
||||
|
||||
const size_t index = FindMemberIndex(name);
|
||||
|
||||
// If member resolved (may not be if restoring to old instances)
|
||||
if (index != ~0)
|
||||
{
|
||||
// If member data available (may not be if member is new)
|
||||
if (IExchangeValue* pValue = m_pInstanceData->GetMember(index))
|
||||
{
|
||||
if (pValue->GetSizeOf() == targetSize)
|
||||
{
|
||||
return pValue;
|
||||
}
|
||||
else // Member size mismatch
|
||||
{
|
||||
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING,
|
||||
"SoftCode: Member %s of instance %p has changed size (old: %d new: %d), setting to default value.",
|
||||
name, m_pInstanceData->Instance(), (int)pValue->GetSizeOf(), (int)targetSize);
|
||||
}
|
||||
}
|
||||
else // Member unknown
|
||||
{
|
||||
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING,
|
||||
"SoftCode: Member %s (of instance %p) appears to be new.",
|
||||
name, m_pInstanceData->Instance());
|
||||
|
||||
// TODO: Could attempt to validate against a known wipe pattern ie. 0xfefefefe
|
||||
// This could catch most uninitialized variables...
|
||||
|
||||
if (targetSize <= sizeof(void*))
|
||||
{
|
||||
switch (targetSize)
|
||||
{
|
||||
case 1:
|
||||
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "\tLeaving as: %d", *reinterpret_cast<char*>(pTarget));
|
||||
break;
|
||||
case 2:
|
||||
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "\tLeaving as: %04x", *reinterpret_cast<short*>(pTarget));
|
||||
break;
|
||||
case 4:
|
||||
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "\tLeaving as: %08x", *reinterpret_cast<int*>(pTarget));
|
||||
break;
|
||||
case 8:
|
||||
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "\tLeaving as: %llx", *reinterpret_cast<long long*>(pTarget));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Indicate value should be default constructed
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Used once required members have been established, members not already
|
||||
// encountered will be ignored.
|
||||
void LockMemberSet()
|
||||
{
|
||||
assert(m_state == eState_ResolvingMembers);
|
||||
|
||||
DestroyInstanceData();
|
||||
m_state = eState_ReadingOldMembers;
|
||||
}
|
||||
|
||||
// Rewinds instance data and prepare for loading
|
||||
void RewindForLoading()
|
||||
{
|
||||
assert(m_state == eState_ReadingOldMembers);
|
||||
|
||||
m_pInstanceData = NULL;
|
||||
m_instanceIndex = ~0;
|
||||
m_state = eState_WritingNewMembers;
|
||||
}
|
||||
|
||||
// Rewinds instance data to prepare to restore old members (UNDO)
|
||||
void RewindForRestore()
|
||||
{
|
||||
assert(m_state == eState_WritingNewMembers);
|
||||
|
||||
m_pInstanceData = NULL;
|
||||
m_instanceIndex = ~0;
|
||||
m_state = eState_RestoringOldMembers;
|
||||
}
|
||||
|
||||
void NotifyListenerOfReplacements(ISoftCodeListener* pListener)
|
||||
{
|
||||
for (TInstanceVec::const_iterator iter(m_instances.begin()); iter != m_instances.end(); ++iter)
|
||||
{
|
||||
CInstanceData* pInstanceData = *iter;
|
||||
pListener->InstanceReplaced(pInstanceData->m_pOldInstance, pInstanceData->m_pNewInstance);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void DestroyInstanceData()
|
||||
{
|
||||
m_pInstanceData = NULL;
|
||||
m_instanceIndex = ~0;
|
||||
|
||||
for (TInstanceVec::iterator iter(m_instances.begin()); iter != m_instances.end(); ++iter)
|
||||
{
|
||||
delete *iter;
|
||||
}
|
||||
|
||||
m_instances.resize(0);
|
||||
}
|
||||
|
||||
inline size_t FindMemberIndex(const string& memberName)
|
||||
{
|
||||
size_t index = ~0;
|
||||
|
||||
// If needed members have been resolved
|
||||
if (m_state != eState_ResolvingMembers)
|
||||
{
|
||||
TMemberMap::const_iterator iter(m_memberMap.find(memberName));
|
||||
if (iter != m_memberMap.end())
|
||||
{
|
||||
index = iter->second;
|
||||
}
|
||||
}
|
||||
else // Add this member to the map with a new index
|
||||
{
|
||||
// Ensure there's no member name duplicates
|
||||
assert(m_memberMap.find(memberName) == m_memberMap.end());
|
||||
|
||||
// A new entry
|
||||
index = m_memberMap.size();
|
||||
size_t& newIndex = m_memberMap[memberName];
|
||||
newIndex = index;
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
private:
|
||||
CInstanceData* m_pInstanceData;
|
||||
size_t m_instanceIndex;
|
||||
|
||||
typedef std::vector<CInstanceData*> TInstanceVec;
|
||||
TInstanceVec m_instances;
|
||||
|
||||
// Maps instance members to offsets in instance member vectors
|
||||
typedef std::map<string, size_t> TMemberMap;
|
||||
TMemberMap m_memberMap;
|
||||
|
||||
enum EState
|
||||
{
|
||||
eState_ResolvingMembers = 0, // Record new member names as found
|
||||
eState_ReadingOldMembers, // Scrape requested member data from old instances
|
||||
eState_WritingNewMembers, // Write old member data to new instances
|
||||
eState_RestoringOldMembers, // Restore scraped values to old instances (UNDO)
|
||||
};
|
||||
|
||||
EState m_state;
|
||||
};
|
||||
|
||||
|
||||
// ----
|
||||
|
||||
DynamicTypeLibrary::DynamicTypeLibrary(const char* name)
|
||||
: m_name(name)
|
||||
, m_listeners(1)
|
||||
{}
|
||||
|
||||
const char* DynamicTypeLibrary::GetName()
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
|
||||
void* DynamicTypeLibrary::CreateInstanceVoid(const char* typeName)
|
||||
{
|
||||
TTypeMap::const_iterator typeIter(m_types.find(typeName));
|
||||
if (typeIter != m_types.end())
|
||||
{
|
||||
ITypeRegistrar* pRegistrar = typeIter->second;
|
||||
return pRegistrar->CreateInstance();
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void DynamicTypeLibrary::SetOverride(ITypeLibrary* /*pOverrideLib*/)
|
||||
{
|
||||
CryFatalError("Unsupported: Attempting to SetOverride on a DynamicTypeLibrary!");
|
||||
}
|
||||
|
||||
size_t DynamicTypeLibrary::GetTypes([[maybe_unused]] ITypeRegistrar** ppRegistrar, [[maybe_unused]] size_t& count) const
|
||||
{
|
||||
CryFatalError("Unsupported: Attempting to GetTypes on a DynamicTypeLibrary!");
|
||||
return 0;
|
||||
}
|
||||
|
||||
void DynamicTypeLibrary::AddListener(const char* libraryName, ISoftCodeListener* pListener, const char* listenerName)
|
||||
{
|
||||
// This DynamicTypeLibrary could have been created by this listener request
|
||||
// So ensure we have a name...!
|
||||
if (!m_name)
|
||||
{
|
||||
m_name = libraryName;
|
||||
}
|
||||
|
||||
m_listeners.Add(pListener, listenerName);
|
||||
}
|
||||
|
||||
void DynamicTypeLibrary::RemoveListener(ISoftCodeListener* pListener)
|
||||
{
|
||||
m_listeners.Remove(pListener);
|
||||
}
|
||||
|
||||
void DynamicTypeLibrary::IntegrateLibrary(ITypeLibrary* pLib, bool isDefault)
|
||||
{
|
||||
typedef std::vector<ITypeRegistrar*> TTypeVec;
|
||||
|
||||
// Resolve our name if we haven't already
|
||||
if (!m_name)
|
||||
{
|
||||
m_name = pLib->GetName();
|
||||
}
|
||||
|
||||
// Override the new lib immediately
|
||||
pLib->SetOverride(this);
|
||||
|
||||
// Query the new library for its types
|
||||
size_t typeCount = 0;
|
||||
pLib->GetTypes(NULL, typeCount);
|
||||
if (typeCount > 0)
|
||||
{
|
||||
TTypeVec typeVec;
|
||||
typeVec.resize(typeCount);
|
||||
pLib->GetTypes(&(typeVec.front()), typeCount);
|
||||
|
||||
if (!isDefault)
|
||||
{
|
||||
CryLogAlways("SoftCode: Integrating %d new types defined in %s...", (int)typeCount, m_name);
|
||||
}
|
||||
|
||||
// Attempt to integrate each type found
|
||||
for (TTypeVec::iterator typeIter(typeVec.begin()); typeIter != typeVec.end(); ++typeIter)
|
||||
{
|
||||
IntegrateType(*typeIter, isDefault);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "SoftCode: New %s library has no registered types. Nothing to integrate.", pLib->GetName());
|
||||
}
|
||||
}
|
||||
|
||||
ITypeRegistrar* DynamicTypeLibrary::FindTypeForInstance(void* pInstance) const
|
||||
{
|
||||
for (TTypeMap::const_iterator iter(m_types.begin()); iter != m_types.end(); ++iter)
|
||||
{
|
||||
ITypeRegistrar* pType = iter->second;
|
||||
if (pType->HasInstance(pInstance))
|
||||
{
|
||||
return pType;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void DynamicTypeLibrary::IntegrateType(ITypeRegistrar* pType, bool isDefault)
|
||||
{
|
||||
const char* typeName = pType->GetName();
|
||||
|
||||
// If there's an existing registrar
|
||||
ITypeRegistrar* pExistingType = m_types[typeName];
|
||||
assert(pExistingType != pType); // Sanity check
|
||||
|
||||
// If the new type is the default (built-in) type but it's already been overridden
|
||||
if (isDefault && pExistingType)
|
||||
{
|
||||
return; // Nothing to do
|
||||
}
|
||||
// TODO: Inform listeners that there's a new library available
|
||||
// and ask if we should use it immediately or defer
|
||||
|
||||
CExchanger exchanger;
|
||||
|
||||
// If the type can be safely created, visited and destroyed
|
||||
if (EvaluateType(pType, exchanger))
|
||||
{
|
||||
// Override the type
|
||||
m_types[typeName] = pType;
|
||||
|
||||
if (!isDefault)
|
||||
{
|
||||
CryLogAlways("SoftCode: Overridden %s in library %s", typeName, m_name);
|
||||
}
|
||||
|
||||
const size_t instanceCount = (pExistingType) ? pExistingType->InstanceCount() : 0;
|
||||
|
||||
// If there are any existing instances
|
||||
if (instanceCount > 0)
|
||||
{
|
||||
CryLogAlways("SoftCode: Attempting to exchange %d %s instances to the new version...", (int)instanceCount, typeName);
|
||||
|
||||
// Read instance members for type (removes data for resolved members)
|
||||
if (pExistingType->ExchangeInstances(exchanger))
|
||||
{
|
||||
exchanger.RewindForLoading();
|
||||
|
||||
// Write instance members for type
|
||||
if (pType->ExchangeInstances(exchanger))
|
||||
{
|
||||
// Success! Tell the listeners to fix up their pointers
|
||||
for (TListeners::Notifier notifier(m_listeners); notifier.IsValid(); notifier.Next())
|
||||
{
|
||||
exchanger.NotifyListenerOfReplacements(*notifier);
|
||||
}
|
||||
|
||||
CryLogAlways("SoftCode: %d %s instances successfully overridden to latest!", (int)instanceCount, typeName);
|
||||
|
||||
// Clean up old instances
|
||||
if (!pExistingType->DestroyInstances())
|
||||
{
|
||||
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "SoftCode: Failed to destroy old instances of type %s - leak probable.", typeName);
|
||||
}
|
||||
}
|
||||
else // Failed to create & write into new instances
|
||||
{
|
||||
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_ERROR, "SoftCode: Failed to create and write into new instances of %s. Attempting restore of old instances...", typeName);
|
||||
if (!pType->DestroyInstances())
|
||||
{
|
||||
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "SoftCode: Failed to destroy new instances of type %s - leak probable.", typeName);
|
||||
}
|
||||
|
||||
// Restore the original type library as the active one
|
||||
m_types[typeName] = pExistingType;
|
||||
|
||||
// Attempt to restore the old instances with their original data
|
||||
exchanger.RewindForRestore();
|
||||
if (pExistingType->ExchangeInstances(exchanger))
|
||||
{
|
||||
CryLogAlways("SoftCode: Type %s in library %s successfully restored to previous revision!", typeName, m_name);
|
||||
}
|
||||
else
|
||||
{
|
||||
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_ERROR, "SoftCode: Restore of old %s instances failed. State now undefined!", typeName);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_ERROR, "SoftCode: Failed to read members on %s", typeName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool DynamicTypeLibrary::EvaluateType(ITypeRegistrar* pType, CExchanger& exchanger)
|
||||
{
|
||||
// Try a full object life-time with a single instance of the
|
||||
// new type before attempting a member exchange. This also allow the
|
||||
// exchanger to determine the required members to be removed from the
|
||||
// old instances.
|
||||
bool testPassed = false;
|
||||
|
||||
// Create a single test instance of the type
|
||||
if (pType->CreateInstance())
|
||||
{
|
||||
// Read the instance members (also prepares the exchanger member set)
|
||||
if (pType->ExchangeInstances(exchanger))
|
||||
{
|
||||
// Destroy the old instance
|
||||
if (pType->DestroyInstances())
|
||||
{
|
||||
// Indicate required members are now resolved
|
||||
exchanger.LockMemberSet();
|
||||
testPassed = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_ERROR, "SoftCode: Failed to destroy test instance of type: %s. New type will be skipped.", pType->GetName());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_ERROR, "SoftCode: Failed to read members in test instance of type: %s. New type will be skipped.", pType->GetName());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_ERROR, "SoftCode: Failed to create test instance of type: %s. New type will be skipped.", pType->GetName());
|
||||
}
|
||||
|
||||
return testPassed;
|
||||
}
|
||||
|
||||
// ----
|
||||
|
||||
// The export we expect to find in the SoftCode modules
|
||||
typedef ITypeLibrary* (__stdcall * TGetTypeLibraryFcn)();
|
||||
|
||||
static void SoftCode_UpdateCmd([[maybe_unused]] IConsoleCmdArgs* pArgs)
|
||||
{
|
||||
gEnv->pSoftCodeMgr->LoadNewModules();
|
||||
}
|
||||
|
||||
static int g_autoUpdatePeriod = 0;
|
||||
|
||||
SoftCodeMgr::SoftCodeMgr()
|
||||
{
|
||||
REGISTER_CVAR2("sc_autoupdate", &g_autoUpdatePeriod, 5, VF_CHEAT, "Set the auto-update poll period for new SoftCode modules. Set to zero to disable");
|
||||
REGISTER_COMMAND("sc_update", reinterpret_cast<ConsoleCommandFunc>(&SoftCode_UpdateCmd), VF_CHEAT, "Loads any new SoftCode modules");
|
||||
|
||||
// Clear out any old modules
|
||||
{
|
||||
typedef std::vector<string> TStringVec;
|
||||
TStringVec filePaths;
|
||||
|
||||
if (FindSoftCodeFiles("*", filePaths) > 0)
|
||||
{
|
||||
for (TStringVec::const_iterator iter(filePaths.begin()); iter != filePaths.end(); ++iter)
|
||||
{
|
||||
if (!DeleteFile(iter->c_str()))
|
||||
{
|
||||
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_ERROR, "SoftCode: Failed to clean %s", iter->c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SoftCodeMgr::~SoftCodeMgr()
|
||||
{
|
||||
if (gEnv->pConsole)
|
||||
{
|
||||
gEnv->pConsole->RemoveCommand("sc_update");
|
||||
gEnv->pConsole->UnregisterVariable("sc_autoupdate");
|
||||
}
|
||||
}
|
||||
|
||||
// Used to register built-in libraries on first use
|
||||
void SoftCodeMgr::RegisterLibrary(ITypeLibrary* pDefaultLib)
|
||||
{
|
||||
DynamicTypeLibrary& typeLib = m_libraryMap[pDefaultLib->GetName()];
|
||||
typeLib.IntegrateLibrary(pDefaultLib, true);
|
||||
}
|
||||
|
||||
// Look for new SoftCode modules and load them, adding their types to the registry
|
||||
void SoftCodeMgr::LoadNewModules()
|
||||
{
|
||||
typedef std::vector<string> TStringVec;
|
||||
typedef TStringVec::const_iterator TModuleIter;
|
||||
TStringVec modulePaths;
|
||||
|
||||
// Find modules
|
||||
FindSoftCodeFiles("*.dll", modulePaths);
|
||||
|
||||
for (TModuleIter libIter(modulePaths.begin()); libIter != modulePaths.end(); ++libIter)
|
||||
{
|
||||
const char* moduleName = libIter->c_str();
|
||||
LoadModule(moduleName);
|
||||
}
|
||||
}
|
||||
|
||||
void SoftCodeMgr::AddListener(const char* libraryName, ISoftCodeListener* pListener, const char* listenerName)
|
||||
{
|
||||
// Find an existing lib or create a new one to add the listener to
|
||||
DynamicTypeLibrary& lib = m_libraryMap[libraryName];
|
||||
lib.AddListener(libraryName, pListener, listenerName);
|
||||
}
|
||||
|
||||
void SoftCodeMgr::RemoveListener(const char* libraryName, ISoftCodeListener* pListener)
|
||||
{
|
||||
TLibMap::iterator iter(m_libraryMap.find(libraryName));
|
||||
|
||||
if (iter != m_libraryMap.end())
|
||||
{
|
||||
iter->second.RemoveListener(pListener);
|
||||
}
|
||||
}
|
||||
|
||||
// To be called regularly to poll for library updates
|
||||
void SoftCodeMgr::PollForNewModules()
|
||||
{
|
||||
if (g_autoUpdatePeriod > 0)
|
||||
{
|
||||
const CTimeValue frameStartTime(gEnv->pTimer->GetFrameStartTime(ITimer::ETIMER_UI));
|
||||
if (m_nextAutoCheckTime <= frameStartTime)
|
||||
{
|
||||
m_nextAutoCheckTime.SetSeconds((int64)g_autoUpdatePeriod);
|
||||
m_nextAutoCheckTime += frameStartTime;
|
||||
|
||||
// Attempt to find and load any new modules
|
||||
LoadNewModules();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
// Util
|
||||
class InstanceFixup
|
||||
: public ISoftCodeListener
|
||||
{
|
||||
public:
|
||||
InstanceFixup(void* pOldInstance)
|
||||
: m_pOldInstance(pOldInstance)
|
||||
, m_pNewInstance() {}
|
||||
|
||||
virtual void InstanceReplaced(void* pOldInstance, void* pNewInstance)
|
||||
{
|
||||
if (m_pOldInstance == pOldInstance)
|
||||
{
|
||||
m_pNewInstance = pNewInstance;
|
||||
}
|
||||
}
|
||||
|
||||
void* NewInstance() const { return m_pNewInstance; }
|
||||
|
||||
private:
|
||||
void* m_pOldInstance;
|
||||
void* m_pNewInstance;
|
||||
};
|
||||
}
|
||||
|
||||
// Stops thread execution until a new SoftCode module is available
|
||||
void* SoftCodeMgr::WaitForUpdate(void* pInstance)
|
||||
{
|
||||
DynamicTypeLibrary* pOwningLib = NULL;
|
||||
ITypeRegistrar* pOldType = NULL;
|
||||
|
||||
// Find existing instance
|
||||
for (TLibMap::iterator libIter(m_libraryMap.begin()); libIter != m_libraryMap.end(); ++libIter)
|
||||
{
|
||||
DynamicTypeLibrary& lib = libIter->second;
|
||||
if (ITypeRegistrar* pType = lib.FindTypeForInstance(pInstance))
|
||||
{
|
||||
pOwningLib = &lib;
|
||||
pOldType = pType;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!pOwningLib)
|
||||
{
|
||||
CryFatalError("SoftCode: Attempting to wait for update on an unknown instance!");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
InstanceFixup instanceFixup(pInstance);
|
||||
pOwningLib->AddListener(pOwningLib->GetName(), &instanceFixup, "InstanceFixup");
|
||||
|
||||
while (true)
|
||||
{
|
||||
// Find and load new modules
|
||||
LoadNewModules();
|
||||
|
||||
// Got a new instance?
|
||||
if (instanceFixup.NewInstance())
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// Wait for a new module
|
||||
CryLogAlways("SoftCode: Pausing execution until class %s in %s library is updated...", pOldType->GetName(), pOwningLib->GetName());
|
||||
__debugbreak(); // Stopped here? Check your log!
|
||||
}
|
||||
|
||||
pOwningLib->RemoveListener(&instanceFixup);
|
||||
|
||||
return instanceFixup.NewInstance();
|
||||
}
|
||||
|
||||
bool SoftCodeMgr::LoadModule(const char* moduleName)
|
||||
{
|
||||
bool success = false;
|
||||
|
||||
// If module not yet loaded
|
||||
if (m_loadedSet.find(moduleName) == m_loadedSet.end())
|
||||
{
|
||||
m_loadedSet.insert(moduleName);
|
||||
|
||||
CryLogAlways("SoftCode: Found new module %s, attempting to load...", moduleName);
|
||||
|
||||
HMODULE hModule = CryLoadLibrary(moduleName);
|
||||
|
||||
if (hModule)
|
||||
{
|
||||
TGetTypeLibraryFcn pGetTypeLibraryFcn = reinterpret_cast<TGetTypeLibraryFcn>(GetProcAddress(hModule, DLL_GETTYPELIBRARY));
|
||||
if (pGetTypeLibraryFcn)
|
||||
{
|
||||
// Add to list of loaded libs & override any earlier TypeLibraries already registered
|
||||
ITypeLibrary* pTypeLibrary = pGetTypeLibraryFcn();
|
||||
if (pTypeLibrary)
|
||||
{
|
||||
const char* libraryName = pTypeLibrary->GetName();
|
||||
m_libraryMap[libraryName].IntegrateLibrary(pTypeLibrary, false);
|
||||
|
||||
CryLogAlways("SoftCode: Loaded new type library \"%s\" from module %s.", libraryName, moduleName);
|
||||
success = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_ERROR, "Failed to resolve GetTypeLibrary() export in: %s (error: %x)", moduleName, GetLastError());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_ERROR, "Failed to load: %s (error: %x)", moduleName, GetLastError());
|
||||
}
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
size_t SoftCodeMgr::FindSoftCodeFiles(const string& searchName, std::vector<string>& foundPaths) const
|
||||
{
|
||||
foundPaths.clear();
|
||||
|
||||
stack_string scSoftCodeDir;
|
||||
|
||||
TCHAR modulePath[MAX_PATH];
|
||||
GetModuleFileName(NULL, modulePath, sizeof(modulePath));
|
||||
scSoftCodeDir = PathUtil::GetParentDirectory(modulePath);
|
||||
scSoftCodeDir += "\\SoftCode\\";
|
||||
|
||||
|
||||
gEnv->pFileIO->FindFiles(scSoftCodeDir.c_str(), searchName, [&](const char* filePath) -> bool
|
||||
{
|
||||
if (!gEnv->pFileIO->IsDirectory(filePath) && !gEnv->pFileIO->IsReadOnly(filePath))
|
||||
{
|
||||
foundPaths.push_back(filePath);
|
||||
}
|
||||
|
||||
// Keep asking for more files, no early out
|
||||
return true;
|
||||
});
|
||||
|
||||
// Sort the paths into name order
|
||||
std::sort(foundPaths.begin(), foundPaths.end());
|
||||
|
||||
return foundPaths.size();
|
||||
}
|
||||
|
||||
#endif // SOFTCODE_ENABLED
|
||||
@@ -1,111 +0,0 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
// Original file Copyright Crytek GMBH or its affiliates, used under license.
|
||||
|
||||
#ifndef CRYINCLUDE_CRYSYSTEM_SOFTCODE_SOFTCODEMGR_H
|
||||
#define CRYINCLUDE_CRYSYSTEM_SOFTCODE_SOFTCODEMGR_H
|
||||
#pragma once
|
||||
|
||||
#include <CryListenerSet.h>
|
||||
|
||||
#include "ISoftCodeMgr.h"
|
||||
|
||||
struct ITypeLibrary;
|
||||
struct ITypeRegistrar;
|
||||
class CExchanger;
|
||||
|
||||
// Internal: Performs the dynamic type management needed for SoftCoding
|
||||
class DynamicTypeLibrary
|
||||
: public ITypeLibrary
|
||||
{
|
||||
public:
|
||||
DynamicTypeLibrary(const char* name = NULL);
|
||||
|
||||
// ITypeLibrary impl.
|
||||
virtual const char* GetName();
|
||||
virtual void* CreateInstanceVoid(const char* typeName);
|
||||
virtual void SetOverride(ITypeLibrary* pOverrideLib);
|
||||
virtual size_t GetTypes(ITypeRegistrar** ppRegistrar, size_t& count) const;
|
||||
|
||||
void AddListener(const char* libraryName, ISoftCodeListener* pListener, const char* listenerName);
|
||||
void RemoveListener(ISoftCodeListener* pListener);
|
||||
|
||||
// Attempts to add the library types the active set
|
||||
void IntegrateLibrary(ITypeLibrary* pLib, bool isDefault);
|
||||
|
||||
ITypeRegistrar* FindTypeForInstance(void* pInstance) const;
|
||||
|
||||
private:
|
||||
// Attempts to add the type the active set
|
||||
void IntegrateType(ITypeRegistrar* pType, bool isDefault);
|
||||
// Ensure the type can be safely created, visited, destroyed and prep exchanger
|
||||
bool EvaluateType(ITypeRegistrar* pType, CExchanger& exchanger);
|
||||
|
||||
private:
|
||||
typedef std::vector<ITypeLibrary*> TLibVec;
|
||||
typedef std::map<string, ITypeRegistrar*> TTypeMap;
|
||||
typedef CListenerSet<ISoftCodeListener*> TListeners;
|
||||
|
||||
// The current set of active types
|
||||
std::map<string, ITypeRegistrar*> m_types;
|
||||
// Current set of loaded libraries
|
||||
TLibVec m_history;
|
||||
// Set of listeners to SC changes
|
||||
TListeners m_listeners;
|
||||
|
||||
const char* m_name; // Supplied by the first real library that registers
|
||||
};
|
||||
|
||||
|
||||
// Implements the global singleton responsible for SoftCode management
|
||||
class SoftCodeMgr
|
||||
: public ISoftCodeMgr
|
||||
{
|
||||
public:
|
||||
SoftCodeMgr();
|
||||
virtual ~SoftCodeMgr();
|
||||
|
||||
// Used to register built-in libraries on first use
|
||||
virtual void RegisterLibrary(ITypeLibrary* pLib);
|
||||
|
||||
// Look for new SoftCode modules and load them, adding their types to the registry
|
||||
virtual void LoadNewModules();
|
||||
|
||||
virtual void AddListener(const char* libraryName, ISoftCodeListener* pListener, const char* listenerName);
|
||||
virtual void RemoveListener(const char* libraryName, ISoftCodeListener* pListener);
|
||||
|
||||
// To be called regularly to poll for library updates
|
||||
virtual void PollForNewModules();
|
||||
|
||||
// Stops thread execution until a new SoftCode module is available
|
||||
virtual void* WaitForUpdate(void* pInstance);
|
||||
|
||||
private:
|
||||
bool LoadModule(const char* moduleName);
|
||||
size_t FindSoftCodeFiles(const string& searchName, std::vector<string>& foundPaths) const;
|
||||
|
||||
private:
|
||||
typedef std::map<string, DynamicTypeLibrary> TLibMap;
|
||||
typedef std::set<string> TLoadedLibSet;
|
||||
|
||||
// Records the history for each TypeLibrary keyed by library name
|
||||
TLibMap m_libraryMap;
|
||||
|
||||
// Records the library files already loaded
|
||||
TLoadedLibSet m_loadedSet;
|
||||
|
||||
// Used to determine when the next auto-update will occur
|
||||
CTimeValue m_nextAutoCheckTime;
|
||||
};
|
||||
|
||||
|
||||
#endif // CRYINCLUDE_CRYSYSTEM_SOFTCODE_SOFTCODEMGR_H
|
||||
@@ -121,12 +121,9 @@ LRESULT WINAPI WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
||||
|
||||
#include <IRenderer.h>
|
||||
#include <IMovieSystem.h>
|
||||
#include <ServiceNetwork.h>
|
||||
#include <ILog.h>
|
||||
#include <IAudioSystem.h>
|
||||
#include <IProcess.h>
|
||||
#include <INotificationNetwork.h>
|
||||
#include <ISoftCodeMgr.h>
|
||||
#include <LyShine/ILyShine.h>
|
||||
|
||||
#include <LoadScreenBus.h>
|
||||
@@ -134,8 +131,6 @@ LRESULT WINAPI WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
||||
#include <AzFramework/Archive/Archive.h>
|
||||
#include "XConsole.h"
|
||||
#include "Log.h"
|
||||
#include "NotificationNetwork.h"
|
||||
#include "ProfileLog.h"
|
||||
|
||||
#include "XML/xml.h"
|
||||
#include "XML/ReadWriteXMLSink.h"
|
||||
@@ -145,12 +140,10 @@ LRESULT WINAPI WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
||||
|
||||
#include "LocalizedStringManager.h"
|
||||
#include "XML/XmlUtils.h"
|
||||
#include "Serialization/ArchiveHost.h"
|
||||
#include "SystemEventDispatcher.h"
|
||||
#include "ServerThrottle.h"
|
||||
#include "ResourceManager.h"
|
||||
#include "HMDBus.h"
|
||||
#include <IThreadManager.h>
|
||||
|
||||
#include "IZLibCompressor.h"
|
||||
#include "IZlibDecompressor.h"
|
||||
@@ -269,22 +262,6 @@ namespace
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(CVARS_WHITELIST)
|
||||
struct SCVarsWhitelistConfigSink
|
||||
: public ILoadConfigurationEntrySink
|
||||
{
|
||||
virtual void OnLoadConfigurationEntry(const char* szKey, const char* szValue, const char* szGroup)
|
||||
{
|
||||
ICVarsWhitelist* pCVarsWhitelist = gEnv->pSystem->GetCVarsWhiteList();
|
||||
bool whitelisted = (pCVarsWhitelist) ? pCVarsWhitelist->IsWhiteListed(szKey, false) : true;
|
||||
if (whitelisted)
|
||||
{
|
||||
gEnv->pConsole->LoadConfigVar(szKey, szValue);
|
||||
}
|
||||
}
|
||||
} g_CVarsWhitelistConfigSink;
|
||||
#endif // defined(CVARS_WHITELIST)
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
// System Implementation.
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
@@ -324,27 +301,13 @@ CSystem::CSystem(SharedEnvironmentInstance* pSharedEnvironment)
|
||||
m_env.pSystem = this;
|
||||
m_env.pTimer = &m_Time;
|
||||
m_env.pNameTable = &m_nameTable;
|
||||
m_env.bServer = false;
|
||||
m_env.bMultiplayer = false;
|
||||
m_env.bHostMigrating = false;
|
||||
m_env.bIgnoreAllAsserts = false;
|
||||
m_env.bNoAssertDialog = false;
|
||||
m_env.bTesting = false;
|
||||
|
||||
m_env.pSharedEnvironment = pSharedEnvironment;
|
||||
|
||||
m_env.SetFMVIsPlaying(false);
|
||||
m_env.SetCutsceneIsPlaying(false);
|
||||
|
||||
m_env.szDebugStatus[0] = '\0';
|
||||
|
||||
#if !defined(CONSOLE)
|
||||
m_env.SetIsClient(false);
|
||||
#endif
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
m_pStreamEngine = NULL;
|
||||
m_PhysThread = 0;
|
||||
|
||||
m_pIFont = NULL;
|
||||
m_pIFontUi = NULL;
|
||||
@@ -372,7 +335,6 @@ CSystem::CSystem(SharedEnvironmentInstance* pSharedEnvironment)
|
||||
m_pILZ4Decompressor = NULL;
|
||||
m_pIZStdDecompressor = nullptr;
|
||||
m_pLocalizationManager = NULL;
|
||||
m_sys_physics_CPU = 0;
|
||||
#if defined(AZ_RESTRICTED_PLATFORM)
|
||||
#define AZ_RESTRICTED_SECTION SYSTEM_CPP_SECTION_2
|
||||
#include AZ_RESTRICTED_FILE(System_cpp)
|
||||
@@ -380,15 +342,9 @@ CSystem::CSystem(SharedEnvironmentInstance* pSharedEnvironment)
|
||||
m_sys_min_step = 0;
|
||||
m_sys_max_step = 0;
|
||||
|
||||
m_pNotificationNetwork = NULL;
|
||||
|
||||
m_cvAIUpdate = NULL;
|
||||
|
||||
m_pUserCallback = NULL;
|
||||
#if defined(CVARS_WHITELIST)
|
||||
m_pCVarsWhitelist = NULL;
|
||||
m_pCVarsWhitelistConfigSink = &g_CVarsWhitelistConfigSink;
|
||||
#endif // defined(CVARS_WHITELIST)
|
||||
m_sys_memory_debug = NULL;
|
||||
m_sysWarnings = NULL;
|
||||
m_sysKeyboard = NULL;
|
||||
@@ -411,13 +367,6 @@ CSystem::CSystem(SharedEnvironmentInstance* pSharedEnvironment)
|
||||
m_bNoCrashDialog = false;
|
||||
m_bNoErrorReportWindow = false;
|
||||
|
||||
#ifndef _RELEASE
|
||||
m_checkpointLoadCount = 0;
|
||||
m_loadOrigin = eLLO_Unknown;
|
||||
m_hasJustResumed = false;
|
||||
m_expectingMapCommand = false;
|
||||
#endif
|
||||
|
||||
m_pCVarQuit = NULL;
|
||||
|
||||
m_bForceNonDevMode = false;
|
||||
@@ -431,13 +380,6 @@ CSystem::CSystem(SharedEnvironmentInstance* pSharedEnvironment)
|
||||
m_nServerConfigSpec = CONFIG_VERYHIGH_SPEC;
|
||||
m_nMaxConfigSpec = CONFIG_VERYHIGH_SPEC;
|
||||
|
||||
//m_hPhysicsThread = INVALID_HANDLE_VALUE;
|
||||
//m_hPhysicsActive = INVALID_HANDLE_VALUE;
|
||||
//m_bStopPhysics = 0;
|
||||
//m_bPhysicsActive = 0;
|
||||
|
||||
m_pProgressListener = 0;
|
||||
|
||||
m_bPaused = false;
|
||||
m_bNoUpdate = false;
|
||||
m_nUpdateCounter = 0;
|
||||
@@ -445,14 +387,10 @@ CSystem::CSystem(SharedEnvironmentInstance* pSharedEnvironment)
|
||||
|
||||
|
||||
m_pXMLUtils = new CXmlUtils(this);
|
||||
m_pArchiveHost = Serialization::CreateArchiveHost();
|
||||
m_pMemoryManager = CryGetIMemoryManager();
|
||||
m_pThreadTaskManager = new CThreadTaskManager;
|
||||
m_pResourceManager = new CResourceManager;
|
||||
m_pTextModeConsole = NULL;
|
||||
|
||||
InitThreadSystem();
|
||||
|
||||
g_pPakHeap = new CMTSafeHeap;
|
||||
|
||||
if (!AZ::AllocatorInstance<AZ::OSAllocator>::IsReady())
|
||||
@@ -467,7 +405,6 @@ CSystem::CSystem(SharedEnvironmentInstance* pSharedEnvironment)
|
||||
AZ::Debug::Trace::Instance().Init();
|
||||
}
|
||||
|
||||
m_UpdateTimesIdx = 0U;
|
||||
m_bNeedDoWorkDuringOcclusionChecks = false;
|
||||
|
||||
m_eRuntimeState = ESYSTEM_EVENT_LEVEL_UNLOAD;
|
||||
@@ -481,15 +418,12 @@ CSystem::CSystem(SharedEnvironmentInstance* pSharedEnvironment)
|
||||
#endif
|
||||
|
||||
m_ConfigPlatform = CONFIG_INVALID_PLATFORM;
|
||||
|
||||
AzFramework::Terrain::TerrainDataNotificationBus::Handler::BusConnect();
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
CSystem::~CSystem()
|
||||
{
|
||||
AzFramework::Terrain::TerrainDataNotificationBus::Handler::BusDisconnect();
|
||||
ShutDown();
|
||||
|
||||
#if AZ_LEGACY_CRYSYSTEM_TRAIT_USE_MESSAGE_HANDLER
|
||||
@@ -499,17 +433,8 @@ CSystem::~CSystem()
|
||||
CRY_ASSERT(m_windowMessageHandlers.empty() && "There exists a dangling window message handler somewhere");
|
||||
|
||||
SAFE_DELETE(m_pXMLUtils);
|
||||
SAFE_DELETE(m_pArchiveHost);
|
||||
SAFE_DELETE(m_pThreadTaskManager);
|
||||
SAFE_DELETE(m_pResourceManager);
|
||||
SAFE_DELETE(m_pSystemEventDispatcher);
|
||||
// SAFE_DELETE(m_pMemoryManager);
|
||||
|
||||
if (gEnv && gEnv->pThreadManager)
|
||||
{
|
||||
gEnv->pThreadManager->UnRegisterThirdPartyThread("Main");
|
||||
}
|
||||
ShutDownThreadSystem();
|
||||
|
||||
SAFE_DELETE(g_pPakHeap);
|
||||
|
||||
@@ -621,8 +546,6 @@ void CSystem::ShutDown()
|
||||
|
||||
SAFE_DELETE(m_pTextModeConsole);
|
||||
|
||||
KillPhysicsThread();
|
||||
|
||||
if (m_sys_firstlaunch)
|
||||
{
|
||||
m_sys_firstlaunch->Set("0");
|
||||
@@ -668,10 +591,7 @@ void CSystem::ShutDown()
|
||||
gEnv->pLyShine = nullptr;
|
||||
}
|
||||
|
||||
SAFE_DELETE(m_env.pResourceCompilerHelper);
|
||||
|
||||
SAFE_RELEASE(m_env.pMovieSystem);
|
||||
SAFE_DELETE(m_env.pServiceNetwork);
|
||||
SAFE_RELEASE(m_env.pLyShine);
|
||||
SAFE_RELEASE(m_env.pCryFont);
|
||||
if (m_env.pConsole)
|
||||
@@ -711,7 +631,6 @@ void CSystem::ShutDown()
|
||||
SAFE_RELEASE(m_sys_GraphicsQuality);
|
||||
SAFE_RELEASE(m_sys_firstlaunch);
|
||||
SAFE_RELEASE(m_sys_enable_budgetmonitoring);
|
||||
SAFE_RELEASE(m_sys_physics_CPU);
|
||||
|
||||
#if defined(AZ_RESTRICTED_PLATFORM)
|
||||
#define AZ_RESTRICTED_SECTION SYSTEM_CPP_SECTION_3
|
||||
@@ -721,9 +640,6 @@ void CSystem::ShutDown()
|
||||
SAFE_RELEASE(m_sys_min_step);
|
||||
SAFE_RELEASE(m_sys_max_step);
|
||||
|
||||
SAFE_RELEASE(m_pNotificationNetwork);
|
||||
|
||||
SAFE_DELETE(m_env.pSoftCodeMgr);
|
||||
SAFE_DELETE(m_pDefaultValidator);
|
||||
m_pValidator = nullptr;
|
||||
|
||||
@@ -748,7 +664,6 @@ void CSystem::ShutDown()
|
||||
SAFE_RELEASE(m_env.pConsole);
|
||||
|
||||
// Log must be last thing released.
|
||||
SAFE_RELEASE(m_env.pProfileLogSystem);
|
||||
if (m_env.pLog)
|
||||
{
|
||||
m_env.pLog->FlushAndClose();
|
||||
@@ -757,10 +672,6 @@ void CSystem::ShutDown()
|
||||
|
||||
ShutdownFileSystem();
|
||||
|
||||
#if defined(MAP_LOADING_SLICING)
|
||||
delete gEnv->pSystemScheduler;
|
||||
#endif // defined(MAP_LOADING_SLICING)
|
||||
|
||||
ShutdownModuleLibraries();
|
||||
|
||||
EBUS_EVENT(CrySystemEventBus, OnCrySystemPostShutdown);
|
||||
@@ -846,273 +757,6 @@ ISystem* CSystem::GetCrySystem()
|
||||
return this;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Physics thread task
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
class CPhysicsThreadTask
|
||||
: public IThreadTask
|
||||
{
|
||||
public:
|
||||
|
||||
CPhysicsThreadTask()
|
||||
{
|
||||
m_bStopRequested = 0;
|
||||
m_bIsActive = 0;
|
||||
m_stepRequested = 0;
|
||||
m_bProcessing = 0;
|
||||
m_doZeroStep = 0;
|
||||
m_lastStepTimeTaken = 0U;
|
||||
m_lastWaitTimeTaken = 0U;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// IThreadTask implementation.
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
virtual void OnUpdate()
|
||||
{
|
||||
Run();
|
||||
// At the end.. delete the task
|
||||
delete this;
|
||||
}
|
||||
virtual void Stop()
|
||||
{
|
||||
Cancel();
|
||||
}
|
||||
virtual SThreadTaskInfo* GetTaskInfo() { return &m_TaskInfo; }
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
virtual void Run()
|
||||
{
|
||||
m_bStopRequested = 0;
|
||||
m_bIsActive = 1;
|
||||
|
||||
float step, timeTaken, kSlowdown = 1.0f;
|
||||
int nSlowFrames = 0;
|
||||
int64 timeStart;
|
||||
#ifdef ENABLE_LW_PROFILERS
|
||||
LARGE_INTEGER stepStart, stepEnd;
|
||||
#endif
|
||||
LARGE_INTEGER waitStart, waitEnd;
|
||||
MarkThisThreadForDebugging("Physics");
|
||||
|
||||
#if defined(AZ_RESTRICTED_PLATFORM)
|
||||
#define AZ_RESTRICTED_SECTION SYSTEM_CPP_SECTION_5
|
||||
#include AZ_RESTRICTED_FILE(System_cpp)
|
||||
#endif
|
||||
while (true)
|
||||
{
|
||||
QueryPerformanceCounter(&waitStart);
|
||||
m_FrameEvent.Wait(); // Wait untill new frame
|
||||
QueryPerformanceCounter(&waitEnd);
|
||||
m_lastWaitTimeTaken = waitEnd.QuadPart - waitStart.QuadPart;
|
||||
|
||||
if (m_bStopRequested)
|
||||
{
|
||||
UnmarkThisThreadFromDebugging();
|
||||
return;
|
||||
}
|
||||
bool stepped = false;
|
||||
#ifdef ENABLE_LW_PROFILERS
|
||||
QueryPerformanceCounter(&stepStart);
|
||||
#endif
|
||||
while ((step = m_stepRequested) > 0 || m_doZeroStep)
|
||||
{
|
||||
stepped = true;
|
||||
m_stepRequested = 0;
|
||||
m_bProcessing = 1;
|
||||
m_doZeroStep = 0;
|
||||
|
||||
if (kSlowdown != 1.0f)
|
||||
{
|
||||
step = max(1, FtoI(step * kSlowdown * 50 - 0.5f)) * 0.02f;
|
||||
}
|
||||
timeStart = CryGetTicks();
|
||||
timeTaken = gEnv->pTimer->TicksToSeconds(CryGetTicks() - timeStart);
|
||||
if (timeTaken > step * 0.9f)
|
||||
{
|
||||
if (++nSlowFrames > 5)
|
||||
{
|
||||
kSlowdown = step * 0.9f / timeTaken;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
kSlowdown = 1.0f, nSlowFrames = 0;
|
||||
}
|
||||
m_bProcessing = 0;
|
||||
//int timeSleep = (int)((m_timeTarget-gEnv->pTimer->GetAsyncTime()).GetMilliSeconds()*0.9f);
|
||||
//Sleep(max(0,timeSleep));
|
||||
}
|
||||
if (!stepped)
|
||||
{
|
||||
Sleep(0);
|
||||
}
|
||||
m_FrameDone.Set();
|
||||
#ifdef ENABLE_LW_PROFILERS
|
||||
QueryPerformanceCounter(&stepEnd);
|
||||
m_lastStepTimeTaken = stepEnd.QuadPart - stepStart.QuadPart;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
virtual void Cancel()
|
||||
{
|
||||
Pause();
|
||||
m_bStopRequested = 1;
|
||||
m_FrameEvent.Set();
|
||||
m_bIsActive = 0;
|
||||
}
|
||||
|
||||
int Pause()
|
||||
{
|
||||
if (m_bIsActive)
|
||||
{
|
||||
AZ_PROFILE_FUNCTION_STALL(AZ::Debug::ProfileCategory::System);
|
||||
m_bIsActive = 0;
|
||||
while (m_bProcessing)
|
||||
{
|
||||
;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
int Resume()
|
||||
{
|
||||
if (!m_bIsActive)
|
||||
{
|
||||
m_bIsActive = 1;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
int IsActive() { return m_bIsActive; }
|
||||
int RequestStep(float dt)
|
||||
{
|
||||
if (m_bIsActive && dt > FLT_EPSILON)
|
||||
{
|
||||
m_stepRequested += dt;
|
||||
if (dt <= 0.0f)
|
||||
{
|
||||
m_doZeroStep = 1;
|
||||
}
|
||||
m_FrameEvent.Set();
|
||||
}
|
||||
|
||||
return m_bProcessing;
|
||||
}
|
||||
float GetRequestedStep() { return m_stepRequested; }
|
||||
|
||||
uint64 LastStepTaken() const
|
||||
{
|
||||
return m_lastStepTimeTaken;
|
||||
}
|
||||
|
||||
uint64 LastWaitTime() const
|
||||
{
|
||||
return m_lastWaitTimeTaken;
|
||||
}
|
||||
|
||||
void EnsureStepDone()
|
||||
{
|
||||
FRAME_PROFILER("SysUpdate:PhysicsEnsureDone", gEnv->pSystem, PROFILE_SYSTEM);
|
||||
if (m_bIsActive)
|
||||
{
|
||||
while (m_stepRequested > 0.0f || m_bProcessing)
|
||||
{
|
||||
m_FrameDone.Wait();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
volatile int m_bStopRequested;
|
||||
volatile int m_bIsActive;
|
||||
volatile float m_stepRequested;
|
||||
volatile int m_bProcessing;
|
||||
volatile int m_doZeroStep;
|
||||
volatile uint64 m_lastStepTimeTaken;
|
||||
volatile uint64 m_lastWaitTimeTaken;
|
||||
|
||||
CryEvent m_FrameEvent;
|
||||
CryEvent m_FrameDone;
|
||||
|
||||
SThreadTaskInfo m_TaskInfo;
|
||||
};
|
||||
|
||||
void CSystem::CreatePhysicsThread()
|
||||
{
|
||||
if (!m_PhysThread)
|
||||
{
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
SThreadTaskParams threadParams;
|
||||
threadParams.name = "Physics";
|
||||
threadParams.nFlags = THREAD_TASK_BLOCKING;
|
||||
threadParams.nStackSizeKB = PHYSICS_STACK_SIZE >> 10;
|
||||
#if defined(AZ_RESTRICTED_PLATFORM)
|
||||
#define AZ_RESTRICTED_SECTION SYSTEM_CPP_SECTION_6
|
||||
#include AZ_RESTRICTED_FILE(System_cpp)
|
||||
#endif
|
||||
|
||||
{
|
||||
m_PhysThread = new CPhysicsThreadTask;
|
||||
GetIThreadTaskManager()->RegisterTask(m_PhysThread, threadParams);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void CSystem::KillPhysicsThread()
|
||||
{
|
||||
if (m_PhysThread)
|
||||
{
|
||||
GetIThreadTaskManager()->UnregisterTask(m_PhysThread);
|
||||
m_PhysThread = 0;
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// AzFramework::Terrain::TerrainDataNotificationBus START
|
||||
void CSystem::OnTerrainDataCreateBegin()
|
||||
{
|
||||
KillPhysicsThread();
|
||||
}
|
||||
|
||||
void CSystem::OnTerrainDataDestroyBegin()
|
||||
{
|
||||
OnTerrainDataCreateBegin();
|
||||
}
|
||||
|
||||
// AzFramework::Terrain::TerrainDataNotificationBus END
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
int CSystem::SetThreadState(ESubsystem subsys, bool bActive)
|
||||
{
|
||||
switch (subsys)
|
||||
{
|
||||
case ESubsys_Physics:
|
||||
{
|
||||
if (m_PhysThread)
|
||||
{
|
||||
return bActive ? ((CPhysicsThreadTask*)m_PhysThread)->Resume() : ((CPhysicsThreadTask*)m_PhysThread)->Pause();
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void CSystem::SleepIfInactive()
|
||||
{
|
||||
// ProcessSleep()
|
||||
if (m_bDedicatedServer || m_bEditor || gEnv->bMultiplayer)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void CSystem::SleepIfNeeded()
|
||||
{
|
||||
@@ -1185,11 +829,7 @@ bool CSystem::UpdatePreTickBus(int updateFlags, int nPauseMode)
|
||||
// do the dedicated sleep earlier than the frame profiler to avoid having it counted
|
||||
if (gEnv->IsDedicated())
|
||||
{
|
||||
#if defined(MAP_LOADING_SLICING)
|
||||
gEnv->pSystemScheduler->SchedulingSleepIfNeeded();
|
||||
#else
|
||||
SleepIfNeeded();
|
||||
#endif // defined(MAP_LOADING_SLICING)
|
||||
}
|
||||
#endif //EXCLUDE_UPDATE_ON_CONSOLE
|
||||
|
||||
@@ -1204,9 +844,6 @@ bool CSystem::UpdatePreTickBus(int updateFlags, int nPauseMode)
|
||||
|
||||
m_nUpdateCounter++;
|
||||
#ifndef EXCLUDE_UPDATE_ON_CONSOLE
|
||||
// Check if game needs to be sleeping when not active.
|
||||
SleepIfInactive();
|
||||
|
||||
if (m_pUserCallback)
|
||||
{
|
||||
m_pUserCallback->OnUpdate();
|
||||
@@ -1221,7 +858,6 @@ bool CSystem::UpdatePreTickBus(int updateFlags, int nPauseMode)
|
||||
prev_sys_float_exceptions = g_cvars.sys_float_exceptions;
|
||||
|
||||
EnableFloatExceptions(g_cvars.sys_float_exceptions);
|
||||
UpdateFPExceptionsMaskForThreads();
|
||||
}
|
||||
#endif //EXCLUDE_UPDATE_ON_CONSOLE
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
@@ -1268,13 +904,6 @@ bool CSystem::UpdatePreTickBus(int updateFlags, int nPauseMode)
|
||||
}
|
||||
#endif //PROFILE_WITH_VTUNE
|
||||
|
||||
#ifdef SOFTCODE_SYSTEM_ENABLED
|
||||
if (m_env.pSoftCodeMgr)
|
||||
{
|
||||
m_env.pSoftCodeMgr->PollForNewModules();
|
||||
}
|
||||
#endif
|
||||
|
||||
if (m_pStreamEngine)
|
||||
{
|
||||
FRAME_PROFILER("StreamEngine::Update()", this, PROFILE_SYSTEM);
|
||||
@@ -1295,7 +924,6 @@ bool CSystem::UpdatePreTickBus(int updateFlags, int nPauseMode)
|
||||
if (m_sysNoUpdate && m_sysNoUpdate->GetIVal())
|
||||
{
|
||||
bNoUpdate = true;
|
||||
updateFlags = ESYSUPDATE_IGNORE_PHYSICS;
|
||||
}
|
||||
|
||||
m_bNoUpdate = bNoUpdate;
|
||||
@@ -1378,16 +1006,6 @@ bool CSystem::UpdatePreTickBus(int updateFlags, int nPauseMode)
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifndef EXCLUDE_UPDATE_ON_CONSOLE
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
//update notification network system
|
||||
if (m_pNotificationNetwork)
|
||||
{
|
||||
FRAME_PROFILER("SysUpdate:NotificationNetwork", this, PROFILE_SYSTEM);
|
||||
m_pNotificationNetwork->Update();
|
||||
}
|
||||
#endif //EXCLUDE_UPDATE_ON_CONSOLE
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
//update sound system Part 1 if in Editor / in Game Mode Viewsystem updates the Listeners
|
||||
if (!m_env.IsEditorGameMode())
|
||||
@@ -1406,15 +1024,6 @@ bool CSystem::UpdatePreTickBus(int updateFlags, int nPauseMode)
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Update Threads Task Manager.
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
if (m_pThreadTaskManager)
|
||||
{
|
||||
FRAME_PROFILER("SysUpdate:ThreadTaskManager", this, PROFILE_SYSTEM);
|
||||
m_pThreadTaskManager->OnUpdate();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Update Resource Manager.
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
@@ -1423,77 +1032,6 @@ bool CSystem::UpdatePreTickBus(int updateFlags, int nPauseMode)
|
||||
m_pResourceManager->Update();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// update physic system
|
||||
//static float time_zero = 0;
|
||||
if (m_sys_physics_CPU->GetIVal() > 0 && !gEnv->IsDedicated())
|
||||
{
|
||||
CreatePhysicsThread();
|
||||
}
|
||||
else
|
||||
{
|
||||
KillPhysicsThread();
|
||||
}
|
||||
|
||||
static int g_iPausedPhys = 0;
|
||||
|
||||
CPhysicsThreadTask* pPhysicsThreadTask = ((CPhysicsThreadTask*)m_PhysThread);
|
||||
if (!pPhysicsThreadTask)
|
||||
{
|
||||
FRAME_PROFILER_LEGACYONLY("SysUpdate:AllAIAndPhysics", this, PROFILE_SYSTEM);
|
||||
AZ_TRACE_METHOD_NAME("SysUpdate::AllAIAndPhysics");
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// update entity system (a little bit) before physics
|
||||
if (nPauseMode != 1)
|
||||
{
|
||||
if (!bNoUpdate)
|
||||
{
|
||||
EBUS_EVENT(CrySystemEventBus, OnCrySystemPrePhysicsUpdate);
|
||||
}
|
||||
}
|
||||
|
||||
// intermingle physics/AI updates so that if we get a big timestep (frame rate glitch etc) the
|
||||
// AI gets to steer entities before they travel over cliffs etc.
|
||||
const float maxTimeStep = 0.25f;
|
||||
int maxSteps = 1;
|
||||
//float fCurTime = m_Time.GetCurrTime();
|
||||
float timeToDo = m_Time.GetFrameTime();//fCurTime - fPrevTime;
|
||||
if (m_env.bMultiplayer)
|
||||
{
|
||||
timeToDo = m_Time.GetRealFrameTime();
|
||||
}
|
||||
|
||||
|
||||
|
||||
while (timeToDo > 0.0001f && maxSteps-- > 0)
|
||||
{
|
||||
float thisStep = min(maxTimeStep, timeToDo);
|
||||
timeToDo -= thisStep;
|
||||
|
||||
|
||||
EBUS_EVENT(CrySystemEventBus, OnCrySystemPostPhysicsUpdate);
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
// In multithreaded physics mode, post physics fires after physics events are dispatched on the main thread.
|
||||
EBUS_EVENT(CrySystemEventBus, OnCrySystemPostPhysicsUpdate);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// update entity system (a little bit) before physics
|
||||
if (nPauseMode != 1)
|
||||
{
|
||||
if (!bNoUpdate)
|
||||
{
|
||||
EBUS_EVENT(CrySystemEventBus, OnCrySystemPrePhysicsUpdate);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Use UI timer for CryMovie, because it should not be affected by pausing game time
|
||||
const float fMovieFrameTime = m_Time.GetFrameTime(ITimer::ETIMER_UI);
|
||||
|
||||
@@ -1555,8 +1093,6 @@ bool CSystem::UpdatePostTickBus(int updateFlags, int /*nPauseMode*/)
|
||||
m_updateTimes.push_back(std::make_pair(cur_time, updateTime));
|
||||
}
|
||||
|
||||
UpdateUpdateTimes();
|
||||
|
||||
{
|
||||
FRAME_PROFILER("SysUpdate - SystemEventDispatcher::Update", this, PROFILE_SYSTEM);
|
||||
m_pSystemEventDispatcher->Update();
|
||||
@@ -1895,12 +1431,6 @@ ILocalizationManager* CSystem::GetLocalizationManager()
|
||||
return m_pLocalizationManager;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
IThreadTaskManager* CSystem::GetIThreadTaskManager()
|
||||
{
|
||||
return m_pThreadTaskManager;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
IResourceManager* CSystem::GetIResourceManager()
|
||||
{
|
||||
@@ -1965,10 +1495,6 @@ void CSystem::ExecuteCommandLine(bool deferred)
|
||||
if (pCmd->GetType() == eCLAT_Post)
|
||||
{
|
||||
string sLine = pCmd->GetName();
|
||||
|
||||
#if defined(CVARS_WHITELIST)
|
||||
if (!GetCVarsWhiteList() || GetCVarsWhiteList()->IsWhiteListed(sLine, false))
|
||||
#endif
|
||||
{
|
||||
if (pCmd->GetValue())
|
||||
{
|
||||
@@ -1978,12 +1504,6 @@ void CSystem::ExecuteCommandLine(bool deferred)
|
||||
GetILog()->Log("Executing command from command line: \n%s\n", sLine.c_str()); // - the actual command might be executed much later (e.g. level load pause)
|
||||
GetIConsole()->ExecuteString(sLine.c_str(), false, deferred);
|
||||
}
|
||||
#if defined(CVARS_WHITELIST)
|
||||
else if (gEnv->IsDedicated())
|
||||
{
|
||||
GetILog()->LogError("Failed to execute command: '%s' as it is not whitelisted\n", sLine.c_str());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2083,93 +1603,6 @@ void CProfilingSystem::VTunePause()
|
||||
#endif
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
sUpdateTimes& CSystem::GetCurrentUpdateTimeStats()
|
||||
{
|
||||
return m_UpdateTimes[m_UpdateTimesIdx];
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
const sUpdateTimes* CSystem::GetUpdateTimeStats(uint32& index, uint32& num)
|
||||
{
|
||||
index = m_UpdateTimesIdx;
|
||||
num = NUM_UPDATE_TIMES;
|
||||
return m_UpdateTimes;
|
||||
}
|
||||
|
||||
void CSystem::UpdateUpdateTimes()
|
||||
{
|
||||
sUpdateTimes& sample = m_UpdateTimes[m_UpdateTimesIdx];
|
||||
if (m_PhysThread)
|
||||
{
|
||||
static uint64 lastPhysTime = 0U;
|
||||
static uint64 lastMainTime = 0U;
|
||||
static uint64 lastYields = 0U;
|
||||
static uint64 lastPhysWait = 0U;
|
||||
uint64 physTime = 0, mainTime = 0;
|
||||
uint32 yields = 0;
|
||||
physTime = ((CPhysicsThreadTask*)m_PhysThread)->LastStepTaken();
|
||||
mainTime = CryGetTicks() - lastMainTime;
|
||||
lastMainTime = mainTime;
|
||||
lastPhysWait = ((CPhysicsThreadTask*)m_PhysThread)->LastWaitTime();
|
||||
sample.PhysStepTime = physTime;
|
||||
sample.SysUpdateTime = mainTime;
|
||||
sample.PhysYields = yields;
|
||||
sample.physWaitTime = lastPhysWait;
|
||||
}
|
||||
++m_UpdateTimesIdx;
|
||||
if (m_UpdateTimesIdx >= NUM_UPDATE_TIMES)
|
||||
{
|
||||
m_UpdateTimesIdx = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#ifndef _RELEASE
|
||||
void CSystem::GetCheckpointData(ICheckpointData& data)
|
||||
{
|
||||
data.m_totalLoads = m_checkpointLoadCount;
|
||||
data.m_loadOrigin = m_loadOrigin;
|
||||
}
|
||||
|
||||
void CSystem::IncreaseCheckpointLoadCount()
|
||||
{
|
||||
if (!m_hasJustResumed)
|
||||
{
|
||||
++m_checkpointLoadCount;
|
||||
}
|
||||
|
||||
m_hasJustResumed = false;
|
||||
}
|
||||
|
||||
void CSystem::SetLoadOrigin(LevelLoadOrigin origin)
|
||||
{
|
||||
switch (origin)
|
||||
{
|
||||
case eLLO_NewLevel: // Intentional fall through
|
||||
case eLLO_Level2Level:
|
||||
m_expectingMapCommand = true;
|
||||
break;
|
||||
|
||||
case eLLO_Resumed:
|
||||
m_hasJustResumed = true;
|
||||
break;
|
||||
|
||||
case eLLO_MapCmd:
|
||||
if (m_expectingMapCommand)
|
||||
{
|
||||
// We knew a map command was coming, so don't process this.
|
||||
m_expectingMapCommand = false;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
m_loadOrigin = origin;
|
||||
m_checkpointLoadCount = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool CSystem::SteamInit()
|
||||
{
|
||||
#if USE_STEAM
|
||||
|
||||
@@ -26,14 +26,12 @@
|
||||
#include "MTSafeAllocator.h"
|
||||
#include "CPUDetect.h"
|
||||
#include <AzFramework/Archive/ArchiveVars.h>
|
||||
#include "ThreadTask.h"
|
||||
#include "RenderBus.h"
|
||||
|
||||
#include <LoadScreenBus.h>
|
||||
#include <ThermalInfo.h>
|
||||
|
||||
#include <AzCore/Module/DynamicModuleHandle.h>
|
||||
#include <AzFramework/Terrain/TerrainDataRequestBus.h>
|
||||
|
||||
namespace AzFramework
|
||||
{
|
||||
@@ -42,10 +40,8 @@ namespace AzFramework
|
||||
|
||||
struct IConsoleCmdArgs;
|
||||
class CServerThrottle;
|
||||
struct ICryFactoryRegistryImpl;
|
||||
struct IZLibCompressor;
|
||||
class CWatchdogThread;
|
||||
class CThreadManager;
|
||||
|
||||
#if defined(AZ_RESTRICTED_PLATFORM)
|
||||
#undef AZ_RESTRICTED_SECTION
|
||||
@@ -184,8 +180,6 @@ class CThreadManager;
|
||||
#include "CryLibrary.h"
|
||||
#endif
|
||||
|
||||
#define NUM_UPDATE_TIMES (128U)
|
||||
|
||||
#ifdef WIN32
|
||||
typedef void* WIN_HMODULE;
|
||||
#else
|
||||
@@ -242,7 +236,6 @@ struct SSystemCVars
|
||||
int sys_WER;
|
||||
int sys_dump_type;
|
||||
int sys_ai;
|
||||
int sys_physics;
|
||||
int sys_entitysystem;
|
||||
int sys_trackview;
|
||||
int sys_vtune;
|
||||
@@ -343,7 +336,6 @@ class CSystem
|
||||
, public IWindowMessageHandler
|
||||
, public AZ::RenderNotificationsBus::Handler
|
||||
, public CrySystemRequestBus::Handler
|
||||
, private AzFramework::Terrain::TerrainDataNotificationBus::Handler
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -398,20 +390,8 @@ public:
|
||||
ISystem* GetCrySystem() override;
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//! Update screen during loading.
|
||||
void UpdateLoadingScreen();
|
||||
|
||||
//! Update screen and call some important tick functions during loading.
|
||||
void SynchronousLoadingTick(const char* pFunc, int line);
|
||||
|
||||
uint32 GetUsedMemory();
|
||||
|
||||
#ifndef _RELEASE
|
||||
virtual void GetCheckpointData(ICheckpointData& data);
|
||||
virtual void IncreaseCheckpointLoadCount();
|
||||
virtual void SetLoadOrigin(LevelLoadOrigin origin);
|
||||
#endif
|
||||
|
||||
virtual bool SteamInit();
|
||||
|
||||
void Relaunch(bool bRelaunch);
|
||||
@@ -426,8 +406,6 @@ public:
|
||||
virtual const char* GetUserName();
|
||||
virtual int GetApplicationInstance();
|
||||
int GetApplicationLogInstance(const char* logFilePath) override;
|
||||
virtual sUpdateTimes& GetCurrentUpdateTimeStats();
|
||||
virtual const sUpdateTimes* GetUpdateTimeStats(uint32&, uint32&);
|
||||
|
||||
ITimer* GetITimer(){ return m_env.pTimer; }
|
||||
AZ::IO::IArchive* GetIPak() { return m_env.pCryPak; };
|
||||
@@ -435,7 +413,6 @@ public:
|
||||
IRemoteConsole* GetIRemoteConsole();
|
||||
IMovieSystem* GetIMovieSystem() { return m_env.pMovieSystem; };
|
||||
IMemoryManager* GetIMemoryManager(){ return m_pMemoryManager; }
|
||||
IThreadManager* GetIThreadManager() override {return m_env.pThreadManager; }
|
||||
ICryFont* GetICryFont(){ return m_env.pCryFont; }
|
||||
ILog* GetILog(){ return m_env.pLog; }
|
||||
ICmdLine* GetICmdLine(){ return m_pCmdLine; }
|
||||
@@ -445,11 +422,8 @@ public:
|
||||
IViewSystem* GetIViewSystem();
|
||||
ILevelSystem* GetILevelSystem();
|
||||
ISystemEventDispatcher* GetISystemEventDispatcher() { return m_pSystemEventDispatcher; }
|
||||
IThreadTaskManager* GetIThreadTaskManager();
|
||||
IResourceManager* GetIResourceManager();
|
||||
ITextModeConsole* GetITextModeConsole();
|
||||
IVisualLog* GetIVisualLog() { return m_env.pVisualLog; }
|
||||
INotificationNetwork* GetINotificationNetwork() { return m_pNotificationNetwork; }
|
||||
IProfilingSystem* GetIProfilingSystem() { return &m_ProfilingSystem; }
|
||||
IZLibCompressor* GetIZLibCompressor() { return m_pIZLibCompressor; }
|
||||
IZLibDecompressor* GetIZLibDecompressor() { return m_pIZLibDecompressor; }
|
||||
@@ -460,19 +434,6 @@ public:
|
||||
CPNoise3* GetNoiseGen();
|
||||
virtual uint64 GetUpdateCounter() { return m_nUpdateCounter; };
|
||||
|
||||
virtual void SetLoadingProgressListener(ILoadingProgressListener* pLoadingProgressListener)
|
||||
{
|
||||
m_pProgressListener = pLoadingProgressListener;
|
||||
};
|
||||
|
||||
virtual ILoadingProgressListener* GetLoadingProgressListener() const
|
||||
{
|
||||
return m_pProgressListener;
|
||||
};
|
||||
|
||||
void SetIMaterialEffects(IMaterialEffects* pMaterialEffects) { m_env.pMaterialEffects = pMaterialEffects; }
|
||||
void SetIOpticsManager(IOpticsManager* pOpticsManager) { m_env.pOpticsManager = pOpticsManager; }
|
||||
void SetIVisualLog(IVisualLog* pVisualLog) { m_env.pVisualLog = pVisualLog; }
|
||||
void DetectGameFolderAccessRights();
|
||||
|
||||
virtual void ExecuteCommandLine(bool deferred=true);
|
||||
@@ -486,8 +447,6 @@ public:
|
||||
virtual IXmlUtils* GetXmlUtils();
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
virtual Serialization::IArchiveHost* GetArchiveHost() const { return m_pArchiveHost; }
|
||||
|
||||
void SetViewCamera(CCamera& Camera){ m_ViewCamera = Camera; }
|
||||
CCamera& GetViewCamera() { return m_ViewCamera; }
|
||||
|
||||
@@ -557,12 +516,6 @@ public:
|
||||
|
||||
//! Return pointer to user defined callback.
|
||||
ISystemUserCallback* GetUserCallback() const { return m_pUserCallback; };
|
||||
#if defined(CVARS_WHITELIST)
|
||||
virtual ICVarsWhitelist* GetCVarsWhiteList() const { return m_pCVarsWhitelist; };
|
||||
virtual ILoadConfigurationEntrySink* GetCVarsWhiteListConfigSink() const { return m_pCVarsWhitelistConfigSink; }
|
||||
#else
|
||||
virtual ILoadConfigurationEntrySink* GetCVarsWhiteListConfigSink() const { return nullptr; }
|
||||
#endif // defined(CVARS_WHITELIST)
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
virtual void SaveConfiguration();
|
||||
@@ -574,7 +527,6 @@ public:
|
||||
virtual void SetConfigPlatform(ESystemConfigPlatform platform);
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
virtual int SetThreadState(ESubsystem subsys, bool bActive);
|
||||
virtual bool IsPaused() const { return m_bPaused; };
|
||||
|
||||
virtual ILocalizationManager* GetLocalizationManager();
|
||||
@@ -584,15 +536,11 @@ public:
|
||||
// static as memReplay needs it before CSystem has been setup - expose a ISystem interface to this function if you need it outside CrySystem
|
||||
static void debug_GetCallStackRaw(void** callstack, uint32& callstackLength);
|
||||
|
||||
virtual ICryFactoryRegistry* GetCryFactoryRegistry() const;
|
||||
|
||||
public:
|
||||
#if !defined(RELEASE)
|
||||
void SetVersionInfo(const char* const szVersion);
|
||||
#endif
|
||||
|
||||
virtual bool InitializeEngineModule(const char* dllName, const char* moduleClassName, const SSystemInitParams& initParams) override;
|
||||
virtual bool UnloadEngineModule(const char* dllName, const char* moduleClassName);
|
||||
virtual const IImageHandler* GetImageHandler() const override { return m_imageHandler.get(); }
|
||||
|
||||
void ShutdownModuleLibraries();
|
||||
@@ -615,8 +563,6 @@ private:
|
||||
// Release all resources.
|
||||
void ShutDown();
|
||||
|
||||
void SleepIfInactive();
|
||||
|
||||
bool LoadEngineDLLs();
|
||||
|
||||
//! @name Initialization routines
|
||||
@@ -630,12 +576,6 @@ private:
|
||||
|
||||
//@}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Threading functions.
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void InitThreadSystem();
|
||||
void ShutDownThreadSystem();
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Helper functions.
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
@@ -652,9 +592,6 @@ private:
|
||||
void LogBuildInfo();
|
||||
void SetDevMode(bool bEnable);
|
||||
|
||||
void CreatePhysicsThread();
|
||||
void KillPhysicsThread();
|
||||
|
||||
#ifndef _RELEASE
|
||||
static void SystemVersionChanged(ICVar* pCVar);
|
||||
#endif // #ifndef _RELEASE
|
||||
@@ -683,7 +620,6 @@ public:
|
||||
virtual bool GetForceNonDevMode() const;
|
||||
virtual bool WasInDevMode() const { return m_bWasInDevMode; };
|
||||
virtual bool IsDevMode() const { return m_bInDevMode && !GetForceNonDevMode(); }
|
||||
virtual bool IsMinimalMode() const { return m_bMinimal; }
|
||||
virtual bool IsMODValid(const char* szMODName) const
|
||||
{
|
||||
if (!szMODName || strstr(szMODName, ".") || strstr(szMODName, "\\"))
|
||||
@@ -734,7 +670,6 @@ private: // ------------------------------------------------------
|
||||
bool m_bRelaunch; //!< relaunching the app or not (true beforerelaunch)
|
||||
int m_iLoadingMode; //!< Game is loading w/o changing context (0 not, 1 quickloading, 2 full loading)
|
||||
bool m_bTestMode; //!< If running in testing mode.
|
||||
bool m_bMinimal; //!< If running in 'minimal mode'.
|
||||
bool m_bEditor; //!< If running in Editor.
|
||||
bool m_bNoCrashDialog;
|
||||
bool m_bNoErrorReportWindow;
|
||||
@@ -749,16 +684,6 @@ private: // ------------------------------------------------------
|
||||
SDefaultValidator* m_pDefaultValidator; //!<
|
||||
CCpuFeatures* m_pCpu; //!< CPU features
|
||||
int m_ttMemStatSS; //!< Time to memstat screenshot
|
||||
string m_szCmdLine;
|
||||
|
||||
int m_iTraceAllocations;
|
||||
|
||||
#ifndef _RELEASE
|
||||
int m_checkpointLoadCount;// Total times game has loaded from a checkpoint
|
||||
LevelLoadOrigin m_loadOrigin; // Where the load was initiated from
|
||||
bool m_hasJustResumed; // Has resume game just been called
|
||||
bool m_expectingMapCommand;
|
||||
#endif
|
||||
bool m_bDrawConsole; //!< Set to true if OK to draw the console.
|
||||
bool m_bDrawUI; //!< Set to true if OK to draw UI.
|
||||
|
||||
@@ -809,8 +734,6 @@ private: // ------------------------------------------------------
|
||||
// XML Utils interface.
|
||||
class CXmlUtils* m_pXMLUtils;
|
||||
|
||||
Serialization::IArchiveHost* m_pArchiveHost;
|
||||
|
||||
int m_iApplicationInstance;
|
||||
|
||||
//! to hold the values stored in system.cfg
|
||||
@@ -876,8 +799,6 @@ private: // ------------------------------------------------------
|
||||
ICVar* m_sys_asset_processor;
|
||||
ICVar* m_sys_load_files_to_memory;
|
||||
|
||||
ICVar* m_sys_physics_CPU;
|
||||
|
||||
#if defined(AZ_RESTRICTED_PLATFORM)
|
||||
#define AZ_RESTRICTED_SECTION SYSTEM_H_SECTION_4
|
||||
#include AZ_RESTRICTED_FILE(System_h)
|
||||
@@ -900,15 +821,6 @@ private: // ------------------------------------------------------
|
||||
//! User define callback for system events.
|
||||
ISystemUserCallback* m_pUserCallback;
|
||||
|
||||
#if defined(CVARS_WHITELIST)
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
//! User define callback for whitelisting cvars
|
||||
ICVarsWhitelist* m_pCVarsWhitelist;
|
||||
ILoadConfigurationEntrySink* m_pCVarsWhitelistConfigSink;
|
||||
#endif // defined(CVARS_WHITELIST)
|
||||
|
||||
//int m_nCurrentLogVerbosity;
|
||||
|
||||
SFileVersion m_fileVersion;
|
||||
SFileVersion m_productVersion;
|
||||
SFileVersion m_buildVersion;
|
||||
@@ -919,8 +831,6 @@ private: // ------------------------------------------------------
|
||||
// Name table.
|
||||
CNameTable m_nameTable;
|
||||
|
||||
IThreadTask* m_PhysThread;
|
||||
|
||||
ESystemConfigSpec m_nServerConfigSpec;
|
||||
ESystemConfigSpec m_nMaxConfigSpec;
|
||||
ESystemConfigPlatform m_ConfigPlatform;
|
||||
@@ -928,8 +838,6 @@ private: // ------------------------------------------------------
|
||||
std::unique_ptr<CServerThrottle> m_pServerThrottle;
|
||||
|
||||
CProfilingSystem m_ProfilingSystem;
|
||||
sUpdateTimes m_UpdateTimes[NUM_UPDATE_TIMES];
|
||||
uint32 m_UpdateTimesIdx;
|
||||
|
||||
// Pause mode.
|
||||
bool m_bPaused;
|
||||
@@ -1000,26 +908,14 @@ private:
|
||||
ESystemGlobalState m_systemGlobalState;
|
||||
static const char* GetSystemGlobalStateName(const ESystemGlobalState systemGlobalState);
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// AzFramework::Terrain::TerrainDataNotificationBus START
|
||||
void OnTerrainDataCreateBegin() override;
|
||||
void OnTerrainDataDestroyBegin() override;
|
||||
// AzFramework::Terrain::TerrainDataNotificationBus END
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public:
|
||||
void InitLocalization();
|
||||
void UpdateUpdateTimes();
|
||||
|
||||
protected: // -------------------------------------------------------------
|
||||
|
||||
ILoadingProgressListener* m_pProgressListener;
|
||||
CCmdLine* m_pCmdLine;
|
||||
CThreadManager* m_pThreadManager;
|
||||
CThreadTaskManager* m_pThreadTaskManager;
|
||||
class CResourceManager* m_pResourceManager;
|
||||
ITextModeConsole* m_pTextModeConsole;
|
||||
INotificationNetwork* m_pNotificationNetwork;
|
||||
|
||||
string m_currentLanguageAudio;
|
||||
string m_systemConfigName; // computed from system_(hardwareplatform)_(assetsPlatform) - eg, system_android_es3.cfg or system_android_opengl.cfg or system_windows_pc.cfg
|
||||
|
||||
@@ -35,19 +35,13 @@
|
||||
#define SYSTEMINIT_CPP_SECTION_17 17
|
||||
#endif
|
||||
|
||||
#if defined(MAP_LOADING_SLICING)
|
||||
#include "SystemScheduler.h"
|
||||
#endif // defined(MAP_LOADING_SLICING)
|
||||
#include "CryLibrary.h"
|
||||
#include "CryPath.h"
|
||||
#include <StringUtils.h>
|
||||
#include <IThreadManager.h>
|
||||
|
||||
#include <AzFramework/Input/Devices/Mouse/InputDeviceMouse.h>
|
||||
#include <AzFramework/IO/LocalFileIO.h>
|
||||
|
||||
#include <IEngineModule.h>
|
||||
#include <CryExtension/CryCreateClassInstance.h>
|
||||
#include <AzCore/IO/SystemFile.h> // for AZ_MAX_PATH_LEN
|
||||
#include <AzCore/IO/Streamer/Streamer.h>
|
||||
#include <AzCore/IO/Streamer/StreamerComponent.h>
|
||||
@@ -105,24 +99,16 @@
|
||||
#include "PhysRenderer.h"
|
||||
#include "LocalizedStringManager.h"
|
||||
#include "SystemEventDispatcher.h"
|
||||
#include "ThreadConfigManager.h"
|
||||
#include "Validator.h"
|
||||
#include "ServerThrottle.h"
|
||||
#include "SystemCFG.h"
|
||||
#include "AutoDetectSpec.h"
|
||||
#include "ResourceManager.h"
|
||||
#include "MTSafeAllocator.h"
|
||||
#include "NotificationNetwork.h"
|
||||
#include "ExtensionSystem/CryFactoryRegistryImpl.h"
|
||||
#include "ExtensionSystem/TestCases/TestExtensions.h"
|
||||
#include "ProfileLogSystem.h"
|
||||
#include "SoftCode/SoftCodeMgr.h"
|
||||
#include "ZLibCompressor.h"
|
||||
#include "ZLibDecompressor.h"
|
||||
#include "ZStdDecompressor.h"
|
||||
#include "LZ4Decompressor.h"
|
||||
#include "ServiceNetwork.h"
|
||||
#include "RemoteCommand.h"
|
||||
#include "LevelSystem/LevelSystem.h"
|
||||
#include "LevelSystem/SpawnableLevelSystem.h"
|
||||
#include "ViewSystem/ViewSystem.h"
|
||||
@@ -152,8 +138,6 @@
|
||||
#include "MobileDetectSpec.h"
|
||||
#endif
|
||||
|
||||
#include "IDebugCallStack.h"
|
||||
|
||||
#include "WindowsConsole.h"
|
||||
|
||||
#if defined(EXTERNAL_CRASH_REPORTING)
|
||||
@@ -169,11 +153,6 @@
|
||||
# include <AzFramework/Network/AssetProcessorConnection.h>
|
||||
#endif
|
||||
|
||||
// if we enable the built-in local version instead of remote:
|
||||
#if defined(CRY_ENABLE_RC_HELPER)
|
||||
#include "ResourceCompilerHelper.h"
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
extern LONG WINAPI CryEngineExceptionFilterWER(struct _EXCEPTION_POINTERS* pExceptionPointers);
|
||||
#endif
|
||||
@@ -406,26 +385,20 @@ struct SysSpecOverrideSink
|
||||
}
|
||||
else
|
||||
{
|
||||
// This could bypass the restricted/whitelisted cvar checks that exist elsewhere depending on
|
||||
// This could bypass the restricted cvar checks that exist elsewhere depending on
|
||||
// the calling code so we also need check here before setting.
|
||||
bool isConst = pCvar->IsConstCVar();
|
||||
bool isCheat = ((pCvar->GetFlags() & (VF_CHEAT | VF_CHEAT_NOCHECK | VF_CHEAT_ALWAYS_CHECK)) != 0);
|
||||
bool isReadOnly = ((pCvar->GetFlags() & VF_READONLY) != 0);
|
||||
bool isDeprecated = ((pCvar->GetFlags() & VF_DEPRECATED) != 0);
|
||||
bool allowApplyCvar = true;
|
||||
bool whitelisted = true;
|
||||
|
||||
#if defined CVARS_WHITELIST
|
||||
ICVarsWhitelist* cvarWhitelist = gEnv->pSystem->GetCVarsWhiteList();
|
||||
whitelisted = cvarWhitelist ? cvarWhitelist->IsWhiteListed(szKey, true) : true;
|
||||
#endif
|
||||
|
||||
if ((isConst || isCheat || isReadOnly) || isDeprecated)
|
||||
{
|
||||
allowApplyCvar = !isDeprecated && (gEnv->pSystem->IsDevMode()) || (gEnv->IsEditor());
|
||||
}
|
||||
|
||||
if ((allowApplyCvar && whitelisted) || ALLOW_CONST_CVAR_MODIFICATIONS)
|
||||
if ((allowApplyCvar) || ALLOW_CONST_CVAR_MODIFICATIONS)
|
||||
{
|
||||
applyCvar = true;
|
||||
}
|
||||
@@ -856,148 +829,6 @@ bool CSystem::UnloadDLL(const char* dllName)
|
||||
return isSuccess;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool CSystem::InitializeEngineModule(const char* dllName, const char* moduleClassName, const SSystemInitParams& initParams)
|
||||
{
|
||||
bool bResult = false;
|
||||
|
||||
stack_string msg;
|
||||
msg = "Initializing ";
|
||||
AZStd::string dll = dllName;
|
||||
|
||||
// Strip off Cry if the dllname is Cry<something>
|
||||
if (dll.find("Cry") == 0)
|
||||
{
|
||||
msg += dll.substr(3).c_str();
|
||||
}
|
||||
else
|
||||
{
|
||||
msg += dllName;
|
||||
}
|
||||
msg += "...";
|
||||
|
||||
if (m_pUserCallback)
|
||||
{
|
||||
m_pUserCallback->OnInitProgress(msg.c_str());
|
||||
}
|
||||
AZ_TracePrintf(moduleClassName, "%s", msg.c_str());
|
||||
|
||||
IMemoryManager::SProcessMemInfo memStart, memEnd;
|
||||
if (GetIMemoryManager())
|
||||
{
|
||||
GetIMemoryManager()->GetProcessMemInfo(memStart);
|
||||
}
|
||||
else
|
||||
{
|
||||
ZeroStruct(memStart);
|
||||
}
|
||||
|
||||
stack_string dllfile = "";
|
||||
|
||||
|
||||
#if defined(AZ_RESTRICTED_PLATFORM)
|
||||
#define AZ_RESTRICTED_SECTION SYSTEMINIT_CPP_SECTION_16
|
||||
#include AZ_RESTRICTED_FILE(SystemInit_cpp)
|
||||
#endif
|
||||
#if defined(AZ_RESTRICTED_SECTION_IMPLEMENTED)
|
||||
#undef AZ_RESTRICTED_SECTION_IMPLEMENTED
|
||||
#else
|
||||
|
||||
dllfile.append(dllName);
|
||||
|
||||
#if defined(LINUX)
|
||||
dllfile = "lib" + PathUtil::ReplaceExtension(dllfile, "so");
|
||||
#ifndef LINUX
|
||||
dllfile.MakeLower();
|
||||
#endif
|
||||
#elif defined(AZ_PLATFORM_MAC)
|
||||
dllfile = "lib" + PathUtil::ReplaceExtension(dllfile, "dylib");
|
||||
#elif defined(AZ_PLATFORM_IOS)
|
||||
PathUtil::RemoveExtension(dllfile);
|
||||
#else
|
||||
dllfile = PathUtil::ReplaceExtension(dllfile, "dll");
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#if !defined(AZ_MONOLITHIC_BUILD)
|
||||
|
||||
m_moduleDLLHandles.insert(std::make_pair(dllfile.c_str(), LoadDLL(dllfile.c_str())));
|
||||
if (!m_moduleDLLHandles[dllfile.c_str()])
|
||||
{
|
||||
return bResult;
|
||||
}
|
||||
|
||||
#endif // #if !defined(AZ_MONOLITHIC_BUILD)
|
||||
|
||||
AZStd::shared_ptr<IEngineModule> pModule;
|
||||
if (CryCreateClassInstance(moduleClassName, pModule))
|
||||
{
|
||||
bResult = pModule->Initialize(m_env, initParams);
|
||||
|
||||
// After initializing the module, give it a chance to register any AZ console vars
|
||||
// declared within the module.
|
||||
pModule->RegisterConsoleVars();
|
||||
}
|
||||
|
||||
if (GetIMemoryManager())
|
||||
{
|
||||
GetIMemoryManager()->GetProcessMemInfo(memEnd);
|
||||
|
||||
#if defined(AZ_ENABLE_TRACING)
|
||||
uint64 memUsed = memEnd.WorkingSetSize - memStart.WorkingSetSize;
|
||||
#endif
|
||||
AZ_TracePrintf(AZ_TRACE_SYSTEM_WINDOW, "Initializing %s %s, MemUsage=%uKb", dllName, pModule ? "done" : "failed", uint32(memUsed / 1024));
|
||||
}
|
||||
|
||||
return bResult;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool CSystem::UnloadEngineModule(const char* dllName, const char* moduleClassName)
|
||||
{
|
||||
bool isSuccess = false;
|
||||
|
||||
// Remove the factory.
|
||||
ICryFactoryRegistryImpl* const pReg = static_cast<ICryFactoryRegistryImpl*>(GetCryFactoryRegistry());
|
||||
|
||||
if (pReg != nullptr)
|
||||
{
|
||||
ICryFactory* pICryFactory = pReg->GetFactory(moduleClassName);
|
||||
|
||||
if (pICryFactory != nullptr)
|
||||
{
|
||||
pReg->UnregisterFactory(pICryFactory);
|
||||
}
|
||||
}
|
||||
|
||||
stack_string msg;
|
||||
msg = "Unloading ";
|
||||
msg += dllName;
|
||||
msg += "...";
|
||||
|
||||
AZ_TracePrintf(AZ_TRACE_SYSTEM_WINDOW, "%s", msg.c_str());
|
||||
|
||||
stack_string dllfile = dllName;
|
||||
|
||||
#if defined(LINUX)
|
||||
dllfile = "lib" + PathUtil::ReplaceExtension(dllfile, "so");
|
||||
#ifndef LINUX
|
||||
dllfile.MakeLower();
|
||||
#endif
|
||||
#elif defined(APPLE)
|
||||
dllfile = "lib" + PathUtil::ReplaceExtension(dllfile, "dylib");
|
||||
#else
|
||||
dllfile = PathUtil::ReplaceExtension(dllfile, "dll");
|
||||
#endif
|
||||
|
||||
#if !defined(AZ_MONOLITHIC_BUILD)
|
||||
isSuccess = UnloadDLL(dllfile.c_str());
|
||||
#endif // #if !defined(AZ_MONOLITHIC_BUILD)
|
||||
|
||||
return isSuccess;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void CSystem::ShutdownModuleLibraries()
|
||||
{
|
||||
@@ -1176,7 +1007,6 @@ bool CSystem::InitFileSystem()
|
||||
|
||||
// get the DirectInstance FileIOBase which should be the AZ::LocalFileIO
|
||||
m_env.pFileIO = AZ::IO::FileIOBase::GetDirectInstance();
|
||||
m_env.pResourceCompilerHelper = nullptr;
|
||||
|
||||
m_env.pCryPak = AZ::Interface<AZ::IO::IArchive>::Get();
|
||||
m_env.pFileIO = AZ::IO::FileIOBase::GetInstance();
|
||||
@@ -1255,8 +1085,7 @@ bool CSystem::InitFileSystem_LoadEngineFolders(const SSystemInitParams&)
|
||||
{
|
||||
LOADING_TIME_PROFILE_SECTION;
|
||||
{
|
||||
ILoadConfigurationEntrySink* pCVarsWhiteListConfigSink = GetCVarsWhiteListConfigSink();
|
||||
LoadConfiguration(m_systemConfigName.c_str(), pCVarsWhiteListConfigSink);
|
||||
LoadConfiguration(m_systemConfigName.c_str());
|
||||
AZ_Printf(AZ_TRACE_SYSTEM_WINDOW, "Loading system configuration from %s...", m_systemConfigName.c_str());
|
||||
}
|
||||
|
||||
@@ -1266,13 +1095,6 @@ bool CSystem::InitFileSystem_LoadEngineFolders(const SSystemInitParams&)
|
||||
|
||||
GetISystem()->SetConfigPlatform(GetDevicePlatform());
|
||||
|
||||
#if defined(CRY_ENABLE_RC_HELPER)
|
||||
if (!m_env.pResourceCompilerHelper)
|
||||
{
|
||||
m_env.pResourceCompilerHelper = new CResourceCompilerHelper();
|
||||
}
|
||||
#endif
|
||||
|
||||
auto projectPath = AZ::Utils::GetProjectPath();
|
||||
AZ_Printf(AZ_TRACE_SYSTEM_WINDOW, "Project Path: %s\n", projectPath.empty() ? "None specified" : projectPath.c_str());
|
||||
|
||||
@@ -1327,7 +1149,6 @@ bool CSystem::InitAudioSystem(const SSystemInitParams& initParams)
|
||||
|
||||
bool useRealAudioSystem = false;
|
||||
if (!initParams.bPreview
|
||||
&& !initParams.bMinimal
|
||||
&& !m_bDedicatedServer
|
||||
&& m_sys_audio_disable->GetIVal() == 0)
|
||||
{
|
||||
@@ -1815,12 +1636,8 @@ bool CSystem::Init(const SSystemInitParams& startupParams)
|
||||
gEnv->mMainThreadId = GetCurrentThreadId(); //Set this ASAP on startup
|
||||
|
||||
InlineInitializationProcessing("CSystem::Init start");
|
||||
m_szCmdLine = startupParams.szSystemCmdLine;
|
||||
|
||||
m_env.szCmdLine = m_szCmdLine.c_str();
|
||||
m_env.bTesting = startupParams.bTesting;
|
||||
m_env.bNoAssertDialog = startupParams.bTesting;
|
||||
m_env.bNoRandomSeed = startupParams.bNoRandom;
|
||||
m_env.bNoAssertDialog = false;
|
||||
|
||||
m_bNoCrashDialog = gEnv->IsDedicated();
|
||||
|
||||
@@ -1867,12 +1684,6 @@ bool CSystem::Init(const SSystemInitParams& startupParams)
|
||||
|
||||
AZ_Assert(CryMemory::IsHeapValid(), "Memory heap must be valid before continuing SystemInit.");
|
||||
|
||||
#ifdef EXTENSION_SYSTEM_INCLUDE_TESTCASES
|
||||
TestExtensions(&CCryFactoryRegistryImpl::Access());
|
||||
#endif
|
||||
|
||||
//_controlfp(0, _EM_INVALID|_EM_ZERODIVIDE | _PC_64 );
|
||||
|
||||
#if defined(WIN32) || defined(WIN64)
|
||||
// check OS version - we only want to run on XP or higher - talk to Martin Mittring if you want to change this
|
||||
{
|
||||
@@ -1903,16 +1714,10 @@ AZ_POP_DISABLE_WARNING
|
||||
m_bPreviewMode = startupParams.bPreview;
|
||||
m_bTestMode = startupParams.bTestMode;
|
||||
m_pUserCallback = startupParams.pUserCallback;
|
||||
m_bMinimal = startupParams.bMinimal;
|
||||
|
||||
#if defined(CVARS_WHITELIST)
|
||||
m_pCVarsWhitelist = startupParams.pCVarsWhitelist;
|
||||
#endif // defined(CVARS_WHITELIST)
|
||||
m_bDedicatedServer = startupParams.bDedicatedServer;
|
||||
m_currentLanguageAudio = "";
|
||||
|
||||
memcpy(gEnv->pProtectedFunctions, startupParams.pProtectedFunctions, sizeof(startupParams.pProtectedFunctions));
|
||||
|
||||
#if !defined(CONSOLE)
|
||||
m_env.SetIsEditor(m_bEditor);
|
||||
m_env.SetIsEditorGameMode(false);
|
||||
@@ -1920,7 +1725,6 @@ AZ_POP_DISABLE_WARNING
|
||||
#endif
|
||||
|
||||
m_env.SetToolMode(startupParams.bToolMode);
|
||||
m_env.bIsOutOfMemory = false;
|
||||
|
||||
if (m_bEditor)
|
||||
{
|
||||
@@ -2084,23 +1888,6 @@ AZ_POP_DISABLE_WARNING
|
||||
// so we log this immediately after setting the log filename
|
||||
LogVersion();
|
||||
|
||||
//here we should be good to ask Crypak to do something
|
||||
|
||||
// Initialise after pLog and CPU feature initialization
|
||||
// AND after console creation (Editor only)
|
||||
// May need access to engine folder .pak files
|
||||
gEnv->pThreadManager->GetThreadConfigManager()->LoadConfig("config/engine_core.thread_config");
|
||||
|
||||
if (m_bEditor)
|
||||
{
|
||||
gEnv->pThreadManager->GetThreadConfigManager()->LoadConfig("config/engine_sandbox.thread_config");
|
||||
}
|
||||
|
||||
// Setup main thread
|
||||
void* pThreadHandle = 0; // Let system figure out thread handle
|
||||
gEnv->pThreadManager->RegisterThirdPartyThread(pThreadHandle, "Main");
|
||||
m_env.pProfileLogSystem = new CProfileLogSystem();
|
||||
|
||||
bool devModeEnable = true;
|
||||
|
||||
#if defined(_RELEASE)
|
||||
@@ -2116,22 +1903,6 @@ AZ_POP_DISABLE_WARNING
|
||||
|
||||
SetDevMode(devModeEnable);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// CREATE NOTIFICATION NETWORK
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
m_pNotificationNetwork = nullptr;
|
||||
#ifndef _RELEASE
|
||||
#ifndef LINUX
|
||||
|
||||
if (!startupParams.bMinimal)
|
||||
{
|
||||
m_pNotificationNetwork = CNotificationNetwork::Create();
|
||||
}
|
||||
#endif//LINUX
|
||||
#endif // _RELEASE
|
||||
|
||||
InlineInitializationProcessing("CSystem::Init NotificationNetwork");
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// CREATE CONSOLE
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
@@ -2200,8 +1971,6 @@ AZ_POP_DISABLE_WARNING
|
||||
// CPU features detection.
|
||||
m_pCpu = new CCpuFeatures;
|
||||
m_pCpu->Detect();
|
||||
m_env.pi.numCoresAvailableToProcess = m_pCpu->GetCPUCount();
|
||||
m_env.pi.numLogicalProcessors = m_pCpu->GetLogicalCPUCount();
|
||||
|
||||
// Check hard minimum CPU requirements
|
||||
if (!CheckCPURequirements(m_pCpu, this))
|
||||
@@ -2251,17 +2020,15 @@ AZ_POP_DISABLE_WARNING
|
||||
}
|
||||
|
||||
{
|
||||
ILoadConfigurationEntrySink* pCVarsWhiteListConfigSink = GetCVarsWhiteListConfigSink();
|
||||
|
||||
// We have to load this file again since first time we did it without devmode
|
||||
LoadConfiguration(m_systemConfigName.c_str(), pCVarsWhiteListConfigSink);
|
||||
LoadConfiguration(m_systemConfigName.c_str());
|
||||
// Optional user defined overrides
|
||||
LoadConfiguration("user.cfg", pCVarsWhiteListConfigSink);
|
||||
LoadConfiguration("user.cfg");
|
||||
|
||||
#if defined(ENABLE_STATS_AGENT)
|
||||
if (m_pCmdLine->FindArg(eCLAT_Pre, "useamblecfg"))
|
||||
{
|
||||
LoadConfiguration("amble.cfg", pCVarsWhiteListConfigSink);
|
||||
LoadConfiguration("amble.cfg");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -2308,7 +2075,7 @@ AZ_POP_DISABLE_WARNING
|
||||
InlineInitializationProcessing("CSystem::Init LoadConfigurations");
|
||||
|
||||
#ifdef WIN32
|
||||
if ((g_cvars.sys_WER) && (!startupParams.bMinimal))
|
||||
if ((g_cvars.sys_WER))
|
||||
{
|
||||
SetUnhandledExceptionFilter(CryEngineExceptionFilterWER);
|
||||
}
|
||||
@@ -2318,7 +2085,6 @@ AZ_POP_DISABLE_WARNING
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Localization
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
if (!startupParams.bMinimal)
|
||||
{
|
||||
InitLocalization();
|
||||
}
|
||||
@@ -2336,7 +2102,6 @@ AZ_POP_DISABLE_WARNING
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// AUDIO
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
if (!startupParams.bMinimal)
|
||||
{
|
||||
if (InitAudioSystem(startupParams))
|
||||
{
|
||||
@@ -2359,12 +2124,6 @@ AZ_POP_DISABLE_WARNING
|
||||
m_pUserCallback->OnInitProgress("First time asset processing - may take a minute...");
|
||||
}
|
||||
|
||||
#ifdef SOFTCODE_SYSTEM_ENABLED
|
||||
m_env.pSoftCodeMgr = new SoftCodeMgr();
|
||||
#else
|
||||
m_env.pSoftCodeMgr = nullptr;
|
||||
#endif
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// System cursor
|
||||
@@ -2374,8 +2133,7 @@ AZ_POP_DISABLE_WARNING
|
||||
// - System cursor has to be enabled manually by the Game if needed; the custom UiCursor will typically be used instead
|
||||
|
||||
if (!gEnv->IsDedicated() &&
|
||||
!gEnv->IsEditor() &&
|
||||
!startupParams.bTesting)
|
||||
!gEnv->IsEditor())
|
||||
{
|
||||
AzFramework::InputSystemCursorRequestBus::Event(AzFramework::InputDeviceMouse::Id,
|
||||
&AzFramework::InputSystemCursorRequests::SetSystemCursorState,
|
||||
@@ -2407,8 +2165,6 @@ AZ_POP_DISABLE_WARNING
|
||||
}
|
||||
|
||||
InlineInitializationProcessing("CSystem::Init InitShine");
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// CONSOLE
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
if (!InitConsole())
|
||||
@@ -2416,22 +2172,6 @@ AZ_POP_DISABLE_WARNING
|
||||
return false;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// SERVICE NETWORK
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
if (!startupParams.bSkipNetwork && !startupParams.bMinimal)
|
||||
{
|
||||
m_env.pServiceNetwork = new CServiceNetwork();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// REMOTE COMMAND SYTSTEM
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
if (!startupParams.bSkipNetwork && !startupParams.bMinimal)
|
||||
{
|
||||
m_env.pRemoteCommandManager = new CRemoteCommandManager();
|
||||
}
|
||||
|
||||
if (m_pUserCallback)
|
||||
{
|
||||
m_pUserCallback->OnInitProgress("Initializing additional systems...");
|
||||
@@ -2489,27 +2229,6 @@ AZ_POP_DISABLE_WARNING
|
||||
|
||||
InlineInitializationProcessing("CSystem::Init ZStdDecompressor");
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Initialize task threads.
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
{
|
||||
m_pThreadTaskManager->InitThreads();
|
||||
|
||||
SetAffinity();
|
||||
AZ_Assert(CryMemory::IsHeapValid(), "CryMemory heap must be valid before initializing VTune.");
|
||||
|
||||
|
||||
if (strstr(startupParams.szSystemCmdLine, "-VTUNE") != 0 || g_cvars.sys_vtune != 0)
|
||||
{
|
||||
if (!InitVTuneProfiler())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
InlineInitializationProcessing("CSystem::Init InitTaskThreads");
|
||||
|
||||
if (m_env.pLyShine)
|
||||
{
|
||||
m_env.pLyShine->PostInit();
|
||||
@@ -2533,8 +2252,6 @@ AZ_POP_DISABLE_WARNING
|
||||
}
|
||||
}
|
||||
EnableFloatExceptions(g_cvars.sys_float_exceptions);
|
||||
|
||||
MarkThisThreadForDebugging("Main");
|
||||
}
|
||||
|
||||
InlineInitializationProcessing("CSystem::Init End");
|
||||
@@ -2589,8 +2306,7 @@ static void LoadConfigurationCmd(IConsoleCmdArgs* pParams)
|
||||
return;
|
||||
}
|
||||
|
||||
ILoadConfigurationEntrySink* pCVarsWhiteListConfigSink = GetISystem()->GetCVarsWhiteListConfigSink();
|
||||
GetISystem()->LoadConfiguration(string("Config/") + pParams->GetArg(1), pCVarsWhiteListConfigSink);
|
||||
GetISystem()->LoadConfiguration(string("Config/") + pParams->GetArg(1));
|
||||
}
|
||||
|
||||
|
||||
@@ -2815,20 +2531,6 @@ void CmdDrillToFile(IConsoleCmdArgs* pArgs)
|
||||
}
|
||||
}
|
||||
|
||||
void ChangeLogAllocations(ICVar* pVal)
|
||||
{
|
||||
g_iTraceAllocations = pVal->GetIVal();
|
||||
|
||||
if (g_iTraceAllocations == 2)
|
||||
{
|
||||
IDebugCallStack::instance()->StartMemLog();
|
||||
}
|
||||
else
|
||||
{
|
||||
IDebugCallStack::instance()->StopMemLog();
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void CSystem::CreateSystemVars()
|
||||
{
|
||||
@@ -2886,9 +2588,6 @@ void CSystem::CreateSystemVars()
|
||||
|
||||
m_cvAIUpdate = REGISTER_INT("ai_NoUpdate", 0, VF_CHEAT, "Disables AI system update when 1");
|
||||
|
||||
m_iTraceAllocations = g_iTraceAllocations;
|
||||
REGISTER_CVAR2_CB("sys_logallocations", &m_iTraceAllocations, m_iTraceAllocations, VF_DUMPTODISK, "Save allocation call stack", ChangeLogAllocations);
|
||||
|
||||
m_cvMemStats = REGISTER_INT("MemStats", 0, 0,
|
||||
"0/x=refresh rate in milliseconds\n"
|
||||
"Use 1000 to switch on and 0 to switch off\n"
|
||||
@@ -3027,16 +2726,6 @@ void CSystem::CreateSystemVars()
|
||||
m_sys_TaskThread_CPU[5] = REGISTER_INT("sys_TaskThread5_CPU", 1, 0,
|
||||
"Specifies the physical CPU index taskthread5 will run on");
|
||||
|
||||
//if physics thread is excluded all locks inside are mapped to NO_LOCK
|
||||
//var must be not visible to accidentally get enabled
|
||||
#if defined(EXCLUDE_PHYSICS_THREAD)
|
||||
m_sys_physics_CPU = REGISTER_INT("sys_physics_CPU_disabled", 0, 0,
|
||||
"Specifies the physical CPU index physics will run on");
|
||||
#else
|
||||
m_sys_physics_CPU = REGISTER_INT("sys_physics_CPU", 1, 0,
|
||||
"Specifies the physical CPU index physics will run on");
|
||||
#endif
|
||||
|
||||
#if defined(AZ_RESTRICTED_PLATFORM)
|
||||
#define AZ_RESTRICTED_SECTION SYSTEMINIT_CPP_SECTION_12
|
||||
#include AZ_RESTRICTED_FILE(SystemInit_cpp)
|
||||
@@ -3200,7 +2889,6 @@ void CSystem::CreateSystemVars()
|
||||
"0=off / 1=enabled");
|
||||
*/
|
||||
REGISTER_CVAR2("sys_AI", &g_cvars.sys_ai, 1, 0, "Enables AI Update");
|
||||
REGISTER_CVAR2("sys_physics", &g_cvars.sys_physics, 1, 0, "Enables Physics Update");
|
||||
REGISTER_CVAR2("sys_entities", &g_cvars.sys_entitysystem, 1, 0, "Enables Entities Update");
|
||||
REGISTER_CVAR2("sys_trackview", &g_cvars.sys_trackview, 1, 0, "Enables TrackView Update");
|
||||
|
||||
@@ -3235,10 +2923,6 @@ void CSystem::CreateSystemVars()
|
||||
|
||||
REGISTER_STRING("dlc_directory", "", 0, "Holds the path to the directory where DLC should be installed to and read from");
|
||||
|
||||
#if defined(MAP_LOADING_SLICING)
|
||||
CreateSystemScheduler(this);
|
||||
#endif // defined(MAP_LOADING_SLICING)
|
||||
|
||||
#if defined(WIN32) || defined(WIN64)
|
||||
REGISTER_INT("sys_screensaver_allowed", 0, VF_NULL, "Specifies if screen saver is allowed to start up while the game is running.");
|
||||
#endif
|
||||
|
||||
@@ -39,8 +39,6 @@
|
||||
#include <ILevelSystem.h>
|
||||
#include <LyShine/ILyShine.h>
|
||||
|
||||
#include "ThreadInfo.h"
|
||||
|
||||
#include <LoadScreenBus.h>
|
||||
|
||||
#if defined(AZ_RESTRICTED_PLATFORM)
|
||||
@@ -90,52 +88,6 @@ void CSystem::OnScene3DEnd()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//! Update screen and call some important tick functions during loading.
|
||||
void CSystem::SynchronousLoadingTick([[maybe_unused]] const char* pFunc, [[maybe_unused]] int line)
|
||||
{
|
||||
LOADING_TIME_PROFILE_SECTION;
|
||||
if (gEnv && gEnv->bMultiplayer && !gEnv->IsEditor())
|
||||
{
|
||||
//UpdateLoadingScreen currently contains a couple of tick functions that need to be called regularly during the synchronous level loading,
|
||||
//when the usual engine and game ticks are suspended.
|
||||
UpdateLoadingScreen();
|
||||
|
||||
#if defined(MAP_LOADING_SLICING)
|
||||
GetISystemScheduler()->SliceAndSleep(pFunc, line);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void CSystem::UpdateLoadingScreen()
|
||||
{
|
||||
// Do not update the network thread from here - it will cause context corruption - use the NetworkStallTicker thread system
|
||||
|
||||
if (GetCurrentThreadId() != gEnv->mMainThreadId)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
#if defined(AZ_RESTRICTED_PLATFORM)
|
||||
#define AZ_RESTRICTED_SECTION SYSTEMRENDERER_CPP_SECTION_2
|
||||
#include AZ_RESTRICTED_FILE(SystemRender_cpp)
|
||||
#endif
|
||||
|
||||
#if AZ_LOADSCREENCOMPONENT_ENABLED
|
||||
EBUS_EVENT(LoadScreenBus, UpdateAndRender);
|
||||
#endif // if AZ_LOADSCREENCOMPONENT_ENABLED
|
||||
|
||||
if (!m_bEditor && !IsQuitting())
|
||||
{
|
||||
if (m_pProgressListener)
|
||||
{
|
||||
m_pProgressListener->OnLoadingProgress(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void CSystem::DisplayErrorMessage(const char* acMessage,
|
||||
|
||||
@@ -1,211 +0,0 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
// Original file Copyright Crytek GMBH or its affiliates, used under license.
|
||||
|
||||
// Description : Implementation of the CSystemScheduler class
|
||||
|
||||
|
||||
#include "CrySystem_precompiled.h"
|
||||
|
||||
#include "ProjectDefines.h"
|
||||
#if defined(MAP_LOADING_SLICING)
|
||||
|
||||
#include "SystemScheduler.h"
|
||||
|
||||
#include "MiniQueue.h"
|
||||
#include "ClientHandler.h"
|
||||
#include "ServerHandler.h"
|
||||
|
||||
void CreateSystemScheduler(CSystem* pSystem)
|
||||
{
|
||||
gEnv->pSystemScheduler = new CSystemScheduler(pSystem);
|
||||
}
|
||||
|
||||
CSystemScheduler::CSystemScheduler(CSystem* pSystem)
|
||||
: m_pSystem(pSystem)
|
||||
, m_lastSliceCheckTime(0.0f)
|
||||
, m_sliceLoadingRef(0)
|
||||
{
|
||||
int defaultSchedulingMode = 0;
|
||||
if (gEnv->IsDedicated())
|
||||
{
|
||||
defaultSchedulingMode = 2;
|
||||
}
|
||||
|
||||
m_svSchedulingMode = REGISTER_INT("sv_scheduling", defaultSchedulingMode, 0, "Scheduling mode\n"
|
||||
" 0: Normal mode\n"
|
||||
" 1: Client\n"
|
||||
" 2: Server\n");
|
||||
|
||||
m_svSchedulingBucket = REGISTER_INT("sv_schedulingBucket", 0, 0, "Scheduling bucket\n");
|
||||
|
||||
m_svSchedulingAffinity = REGISTER_INT("sv_SchedulingAffinity", 0, 0, "Scheduling affinity\n");
|
||||
|
||||
m_svSchedulingClientTimeout = REGISTER_INT("sv_schedulingClientTimeout", 1000, 0, "Client wait server\n");
|
||||
m_svSchedulingServerTimeout = REGISTER_INT("sv_schedulingServerTimeout", 100, 0, "Server wait server\n");
|
||||
|
||||
#if defined(MAP_LOADING_SLICING)
|
||||
m_svSliceLoadEnable = REGISTER_INT("sv_sliceLoadEnable", 1, 0, "Enable/disable slice loading logic\n");
|
||||
;
|
||||
m_svSliceLoadBudget = REGISTER_INT("sv_sliceLoadBudget", 10, 0, "Slice budget\n");
|
||||
;
|
||||
m_svSliceLoadLogging = REGISTER_INT("sv_sliceLoadLogging", 0, 0, "Enable/disable slice loading logging\n");
|
||||
#endif
|
||||
|
||||
m_pLastSliceName = "INACTIVE";
|
||||
m_lastSliceLine = 0;
|
||||
}
|
||||
|
||||
CSystemScheduler::~CSystemScheduler(void)
|
||||
{
|
||||
}
|
||||
|
||||
void CSystemScheduler::SliceLoadingBegin()
|
||||
{
|
||||
m_lastSliceCheckTime = gEnv->pTimer->GetAsyncTime();
|
||||
m_sliceLoadingRef++;
|
||||
m_pLastSliceName = "START";
|
||||
m_lastSliceLine = 0;
|
||||
}
|
||||
|
||||
void CSystemScheduler::SliceLoadingEnd()
|
||||
{
|
||||
m_sliceLoadingRef--;
|
||||
m_pLastSliceName = "INACTIVE";
|
||||
m_lastSliceLine = 0;
|
||||
}
|
||||
|
||||
void CSystemScheduler::SliceAndSleep(const char* sliceName, int line)
|
||||
{
|
||||
#if defined(MAP_LOADING_SLICING)
|
||||
if (!gEnv->IsDedicated())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!m_sliceLoadingRef)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!m_svSliceLoadEnable->GetIVal())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SchedulingModeUpdate();
|
||||
|
||||
CTimeValue currTime = gEnv->pTimer->GetAsyncTime();
|
||||
|
||||
float sliceBudget = CLAMP(m_svSliceLoadBudget->GetFVal(), 0, 1000.0f / m_pSystem->GetDedicatedMaxRate()->GetFVal());
|
||||
bool doSleep = true;
|
||||
if ((currTime - m_pSystem->GetLastTickTime()).GetMilliSeconds() < sliceBudget)
|
||||
{
|
||||
m_lastSliceCheckTime = currTime;
|
||||
doSleep = false;
|
||||
}
|
||||
|
||||
if (doSleep)
|
||||
{
|
||||
if (m_svSliceLoadLogging->GetIVal())
|
||||
{
|
||||
float diff = (currTime - m_lastSliceCheckTime).GetMilliSeconds();
|
||||
if (diff > sliceBudget)
|
||||
{
|
||||
CryLogAlways("[SliceAndSleep]: Interval between slice [%s:%i] and [%s:%i] was [%f] out of budget [%f]", m_pLastSliceName, m_lastSliceLine, sliceName, line, diff, sliceBudget);
|
||||
}
|
||||
}
|
||||
|
||||
m_pSystem->SleepIfNeeded();
|
||||
}
|
||||
|
||||
m_pLastSliceName = sliceName;
|
||||
m_lastSliceLine = line;
|
||||
#endif
|
||||
}
|
||||
|
||||
void CSystemScheduler::SchedulingSleepIfNeeded()
|
||||
{
|
||||
if (!gEnv->IsDedicated())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SchedulingModeUpdate();
|
||||
m_pSystem->SleepIfNeeded();
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Scheduling mode routines
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Scheduling mode update logic
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void CSystemScheduler::SchedulingModeUpdate()
|
||||
{
|
||||
static std::unique_ptr<ClientHandler> m_client;
|
||||
static std::unique_ptr<ServerHandler> m_server;
|
||||
|
||||
if (int scheduling = m_svSchedulingMode->GetIVal())
|
||||
{
|
||||
if (scheduling == 1) //client
|
||||
{
|
||||
if (!m_client.get())
|
||||
{
|
||||
m_server.reset();
|
||||
m_client.reset(new ClientHandler(m_svSchedulingBucket->GetString(), m_svSchedulingAffinity->GetIVal(), m_svSchedulingClientTimeout->GetIVal()));
|
||||
}
|
||||
if (m_client->Sync())
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (scheduling == 2) //server
|
||||
{
|
||||
if (!m_server.get())
|
||||
{
|
||||
m_client.reset();
|
||||
m_server.reset(new ServerHandler(m_svSchedulingBucket->GetString(), m_svSchedulingAffinity->GetIVal(), m_svSchedulingServerTimeout->GetIVal()));
|
||||
}
|
||||
if (m_server->Sync())
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_client.reset();
|
||||
m_server.reset();
|
||||
}
|
||||
}
|
||||
|
||||
#endif // defined(MAP_LOADING_SLICING)
|
||||
|
||||
extern "C" void SliceAndSleep(const char* pFunc, int line)
|
||||
{
|
||||
if (GetISystemScheduler())
|
||||
{
|
||||
GetISystemScheduler()->SliceAndSleep(pFunc, line);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
// Original file Copyright Crytek GMBH or its affiliates, used under license.
|
||||
|
||||
#ifndef CRYINCLUDE_CRYSYSTEM_SYSTEMSCHEDULER_H
|
||||
#define CRYINCLUDE_CRYSYSTEM_SYSTEMSCHEDULER_H
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "System.h"
|
||||
#include <ISystemScheduler.h>
|
||||
|
||||
class CSystemScheduler
|
||||
: public ISystemScheduler
|
||||
{
|
||||
public:
|
||||
CSystemScheduler(CSystem* pSystem);
|
||||
virtual ~CSystemScheduler(void);
|
||||
|
||||
// ISystemScheduler
|
||||
virtual void SliceAndSleep(const char* sliceName, int line);
|
||||
virtual void SliceLoadingBegin();
|
||||
virtual void SliceLoadingEnd();
|
||||
|
||||
virtual void SchedulingSleepIfNeeded(void);
|
||||
// ~ISystemScheduler
|
||||
|
||||
protected:
|
||||
void SchedulingModeUpdate(void);
|
||||
|
||||
private:
|
||||
CSystem* m_pSystem;
|
||||
ICVar* m_svSchedulingAffinity;
|
||||
ICVar* m_svSchedulingClientTimeout;
|
||||
ICVar* m_svSchedulingServerTimeout;
|
||||
ICVar* m_svSchedulingBucket;
|
||||
ICVar* m_svSchedulingMode;
|
||||
ICVar* m_svSliceLoadEnable;
|
||||
ICVar* m_svSliceLoadBudget;
|
||||
ICVar* m_svSliceLoadLogging;
|
||||
|
||||
CTimeValue m_lastSliceCheckTime;
|
||||
|
||||
int m_sliceLoadingRef;
|
||||
|
||||
const char* m_pLastSliceName;
|
||||
int m_lastSliceLine;
|
||||
};
|
||||
|
||||
// Summary:
|
||||
// Creates the system scheduler interface.
|
||||
void CreateSystemScheduler(CSystem* pSystem);
|
||||
|
||||
#endif // CRYINCLUDE_CRYSYSTEM_SYSTEMSCHEDULER_H
|
||||
@@ -1,697 +0,0 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
// Original file Copyright Crytek GMBH or its affiliates, used under license.
|
||||
|
||||
#include "CrySystem_precompiled.h"
|
||||
#include "System.h"
|
||||
#include "ThreadConfigManager.h"
|
||||
#include "IThreadManager.h"
|
||||
#include <CryCustomTypes.h>
|
||||
#include "CryUtils.h"
|
||||
|
||||
#define INCLUDED_FROM_SYSTEM_THREADING_CPP
|
||||
|
||||
#if defined(AZ_RESTRICTED_PLATFORM)
|
||||
#undef AZ_RESTRICTED_SECTION
|
||||
#define SYSTEMTHREADING_CPP_SECTION_1 1
|
||||
#define SYSTEMTHREADING_CPP_SECTION_2 2
|
||||
#define SYSTEMTHREADING_CPP_SECTION_3 3
|
||||
#endif
|
||||
|
||||
#if defined(WIN32) || defined(WIN64)
|
||||
#include "CryThreadUtil_win32_thread.h"
|
||||
#define AZ_RESTRICTED_SECTION_IMPLEMENTED
|
||||
#elif defined(AZ_RESTRICTED_PLATFORM)
|
||||
#define AZ_RESTRICTED_SECTION SYSTEMTHREADING_CPP_SECTION_1
|
||||
#include AZ_RESTRICTED_FILE(SystemThreading_cpp)
|
||||
#endif
|
||||
#if defined(AZ_RESTRICTED_SECTION_IMPLEMENTED)
|
||||
#undef AZ_RESTRICTED_SECTION_IMPLEMENTED
|
||||
#else
|
||||
#include "CryThreadUtil_pthread.h"
|
||||
#endif
|
||||
#undef INCLUDED_FROM_SYSTEM_THREADING_CPP
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
static void ApplyThreadConfig(CryThreadUtil::TThreadHandle pThreadHandle, const SThreadConfig& rThreadDesc)
|
||||
{
|
||||
// Apply config
|
||||
if (rThreadDesc.paramActivityFlag & SThreadConfig::eThreadParamFlag_ThreadName)
|
||||
{
|
||||
CryThreadUtil::CrySetThreadName(pThreadHandle, rThreadDesc.szThreadName);
|
||||
}
|
||||
if (rThreadDesc.paramActivityFlag & SThreadConfig::eThreadParamFlag_Affinity)
|
||||
{
|
||||
CryThreadUtil::CrySetThreadAffinityMask(pThreadHandle, rThreadDesc.affinityFlag);
|
||||
}
|
||||
if (rThreadDesc.paramActivityFlag & SThreadConfig::eThreadParamFlag_Priority)
|
||||
{
|
||||
CryThreadUtil::CrySetThreadPriority(pThreadHandle, rThreadDesc.priority);
|
||||
}
|
||||
if (rThreadDesc.paramActivityFlag & SThreadConfig::eThreadParamFlag_PriorityBoost)
|
||||
{
|
||||
CryThreadUtil::CrySetThreadPriorityBoost(pThreadHandle, !rThreadDesc.bDisablePriorityBoost);
|
||||
}
|
||||
|
||||
CryComment("<ThreadInfo> Configured thread \"%s\" %s | AffinityMask: %u %s | Priority: %i %s | PriorityBoost: %s %s",
|
||||
rThreadDesc.szThreadName, (rThreadDesc.paramActivityFlag & SThreadConfig::eThreadParamFlag_ThreadName) ? "" : "(ignored)",
|
||||
rThreadDesc.affinityFlag, (rThreadDesc.paramActivityFlag & SThreadConfig::eThreadParamFlag_Affinity) ? "" : "(ignored)",
|
||||
rThreadDesc.priority, (rThreadDesc.paramActivityFlag & SThreadConfig::eThreadParamFlag_Priority) ? "" : "(ignored)",
|
||||
!rThreadDesc.bDisablePriorityBoost ? "enabled" : "disabled", (rThreadDesc.paramActivityFlag & SThreadConfig::eThreadParamFlag_PriorityBoost) ? "" : "(ignored)");
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
struct SThreadMetaData
|
||||
: public CMultiThreadRefCount
|
||||
{
|
||||
SThreadMetaData()
|
||||
: m_pThreadTask(0)
|
||||
, m_threadHandle(0)
|
||||
, m_threadId(0)
|
||||
, m_threadName("Cry_UnnamedThread")
|
||||
, m_isRunning(false)
|
||||
{
|
||||
}
|
||||
|
||||
IThread* m_pThreadTask; // Pointer to thread task to be executed
|
||||
CThreadManager* m_pThreadMngr; // Pointer to thread manager
|
||||
|
||||
CryThreadUtil::TThreadHandle m_threadHandle; // Thread handle
|
||||
threadID m_threadId; // The active threadId, 0 = Invalid Id
|
||||
|
||||
CryMutex m_threadExitMutex; // Mutex used to safeguard thread exit condition signaling
|
||||
CryConditionVariable m_threadExitCondition; // Signaled when the thread is about to exit
|
||||
|
||||
CryFixedStringT<THREAD_NAME_LENGTH_MAX> m_threadName; // Thread name
|
||||
volatile bool m_isRunning; // Indicates the thread is not ready to exit yet
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
class CThreadManager
|
||||
: public IThreadManager
|
||||
{
|
||||
public:
|
||||
// <interfuscator:shuffle>
|
||||
virtual ~CThreadManager()
|
||||
{
|
||||
}
|
||||
|
||||
virtual bool SpawnThread(IThread* pThread, const char* sThreadName, ...) override;
|
||||
virtual bool JoinThread(IThread* pThreadTask, EJoinMode eJoinMode) override;
|
||||
|
||||
virtual bool RegisterThirdPartyThread(void* pThreadHandle, const char* sThreadName, ...) override;
|
||||
virtual bool UnRegisterThirdPartyThread(const char* sThreadName, ...) override;
|
||||
|
||||
virtual const char* GetThreadName(threadID nThreadId) override;
|
||||
virtual threadID GetThreadId(const char* sThreadName, ...) override;
|
||||
|
||||
virtual void ForEachOtherThread(IThreadManager::ThreadModifFunction fpThreadModiFunction, void* pFuncData = 0) override;
|
||||
|
||||
virtual void EnableFloatExceptions(EFPE_Severity eFPESeverity, threadID nThreadId = 0) override;
|
||||
virtual void EnableFloatExceptionsForEachOtherThread(EFPE_Severity eFPESeverity) override;
|
||||
|
||||
virtual uint GetFloatingPointExceptionMask() override;
|
||||
virtual void SetFloatingPointExceptionMask(uint nMask) override;
|
||||
|
||||
IThreadConfigManager* GetThreadConfigManager() override
|
||||
{
|
||||
return &m_threadConfigManager;
|
||||
}
|
||||
// </interfuscator:shuffle>
|
||||
private:
|
||||
#if defined(WIN32) || defined(WIN64)
|
||||
static unsigned __stdcall RunThread(void* thisPtr);
|
||||
#define AZ_RESTRICTED_SECTION_IMPLEMENTED
|
||||
#elif defined(AZ_RESTRICTED_PLATFORM)
|
||||
#define AZ_RESTRICTED_SECTION SYSTEMTHREADING_CPP_SECTION_2
|
||||
#include AZ_RESTRICTED_FILE(SystemThreading_cpp)
|
||||
#endif
|
||||
#if defined(AZ_RESTRICTED_SECTION_IMPLEMENTED)
|
||||
#undef AZ_RESTRICTED_SECTION_IMPLEMENTED
|
||||
#else
|
||||
static void* RunThread(void* thisPtr);
|
||||
#endif
|
||||
|
||||
private:
|
||||
bool UnregisterThread(IThread* pThreadTask);
|
||||
|
||||
bool SpawnThreadImpl(IThread* pThread, const char* sThreadName);
|
||||
|
||||
bool RegisterThirdPartyThreadImpl(CryThreadUtil::TThreadHandle pThreadHandle, const char* sThreadName);
|
||||
bool UnRegisterThirdPartyThreadImpl(const char* sThreadName);
|
||||
|
||||
threadID GetThreadIdImpl(const char* sThreadName);
|
||||
|
||||
private:
|
||||
// Note: Guard SThreadMetaData with a _smart_ptr and lock to ensure that a thread waiting to be signaled by another still
|
||||
// has access to valid SThreadMetaData even though the other thread terminated and as a result unregistered itself from the CThreadManager.
|
||||
// An example would be the join method. Where one thread waits on a signal from an other thread to terminate and release its SThreadMetaData,
|
||||
// sharing the same SThreadMetaData condition variable.
|
||||
typedef std::map<IThread*, _smart_ptr<SThreadMetaData> > SpawnedThreadMap;
|
||||
typedef std::map<IThread*, _smart_ptr<SThreadMetaData> >::iterator SpawnedThreadMapIter;
|
||||
typedef std::map<IThread*, _smart_ptr<SThreadMetaData> >::const_iterator SpawnedThreadMapConstIter;
|
||||
typedef std::pair<IThread*, _smart_ptr<SThreadMetaData> > ThreadMapPair;
|
||||
|
||||
typedef std::map<CryFixedStringT<THREAD_NAME_LENGTH_MAX>, _smart_ptr<SThreadMetaData> > SpawnedThirdPartyThreadMap;
|
||||
typedef std::map<CryFixedStringT<THREAD_NAME_LENGTH_MAX>, _smart_ptr<SThreadMetaData> >::iterator SpawnedThirdPartyThreadMapIter;
|
||||
typedef std::map<CryFixedStringT<THREAD_NAME_LENGTH_MAX>, _smart_ptr<SThreadMetaData> >::const_iterator SpawnedThirdPartyThreadMapConstIter;
|
||||
typedef std::pair<CryFixedStringT<THREAD_NAME_LENGTH_MAX>, _smart_ptr<SThreadMetaData> > ThirdPartyThreadMapPair;
|
||||
|
||||
CryCriticalSection m_spawnedThreadsLock; // Use lock for the rare occasion a thread is created/destroyed
|
||||
SpawnedThreadMap m_spawnedThreads; // Holds information of all spawned threads (through this system)
|
||||
|
||||
CryCriticalSection m_spawnedThirdPartyThreadsLock; // Use lock for the rare occasion a thread is created/destroyed
|
||||
SpawnedThirdPartyThreadMap m_spawnedThirdPartyThread; // Holds information of all registered 3rd party threads (through this system)
|
||||
|
||||
CThreadConfigManager m_threadConfigManager;
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
#if defined(WIN32) || defined(WIN64)
|
||||
unsigned __stdcall CThreadManager::RunThread(void* thisPtr)
|
||||
#define AZ_RESTRICTED_SECTION_IMPLEMENTED
|
||||
#elif defined(AZ_RESTRICTED_PLATFORM)
|
||||
#define AZ_RESTRICTED_SECTION SYSTEMTHREADING_CPP_SECTION_3
|
||||
#include AZ_RESTRICTED_FILE(SystemThreading_cpp)
|
||||
#endif
|
||||
#if defined(AZ_RESTRICTED_SECTION_IMPLEMENTED)
|
||||
#undef AZ_RESTRICTED_SECTION_IMPLEMENTED
|
||||
#else
|
||||
void* CThreadManager::RunThread(void* thisPtr)
|
||||
#endif
|
||||
{
|
||||
// Check that we are not spawning a thread before gEnv->pSystem has been set
|
||||
// Otherwise we cannot enable floating point exceptions
|
||||
if (!gEnv || !gEnv->pSystem)
|
||||
{
|
||||
CryFatalError("[Error]: CThreadManager::RunThread requires gEnv->pSystem to be initialized.");
|
||||
}
|
||||
|
||||
IThreadConfigManager* pThreadConfigMngr = gEnv->pThreadManager->GetThreadConfigManager();
|
||||
|
||||
SThreadMetaData* pThreadData = reinterpret_cast<SThreadMetaData*>(thisPtr);
|
||||
pThreadData->m_threadId = CryThreadUtil::CryGetCurrentThreadId();
|
||||
|
||||
// Apply config
|
||||
const SThreadConfig* pThreadConfig = pThreadConfigMngr->GetThreadConfig(pThreadData->m_threadName.c_str());
|
||||
ApplyThreadConfig(pThreadData->m_threadHandle, *pThreadConfig);
|
||||
|
||||
// Config not found, append thread name with no config tag
|
||||
if (pThreadConfig == pThreadConfigMngr->GetDefaultThreadConfig())
|
||||
{
|
||||
CryFixedStringT<THREAD_NAME_LENGTH_MAX> tmpString(pThreadData->m_threadName);
|
||||
const char* cNoConfigAppendix = "(NoCfgFound)";
|
||||
int nNumCharsToReplace = strlen(cNoConfigAppendix);
|
||||
|
||||
// Replace thread name ending
|
||||
if (pThreadData->m_threadName.size() > THREAD_NAME_LENGTH_MAX - nNumCharsToReplace)
|
||||
{
|
||||
tmpString.replace(THREAD_NAME_LENGTH_MAX - nNumCharsToReplace, nNumCharsToReplace, cNoConfigAppendix, nNumCharsToReplace);
|
||||
}
|
||||
else
|
||||
{
|
||||
tmpString.append(cNoConfigAppendix);
|
||||
}
|
||||
|
||||
// Print to log
|
||||
if (pThreadConfigMngr->ConfigLoaded())
|
||||
{
|
||||
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "<ThreadInfo> No Thread config found for thread %s using ... default config.", pThreadData->m_threadName.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "<ThreadInfo> Thread config not loaded yet. Hence no thread config was found for thread %s ... using default config.", pThreadData->m_threadName.c_str());
|
||||
}
|
||||
|
||||
// Rename Thread
|
||||
CryThreadUtil::CrySetThreadName(pThreadData->m_threadHandle, tmpString.c_str());
|
||||
}
|
||||
|
||||
// Enable FPEs
|
||||
gEnv->pThreadManager->EnableFloatExceptions((EFPE_Severity)g_cvars.sys_float_exceptions);
|
||||
|
||||
// Execute thread code
|
||||
pThreadData->m_pThreadTask->ThreadEntry();
|
||||
|
||||
// Disable FPEs
|
||||
gEnv->pThreadManager->EnableFloatExceptions(eFPE_None);
|
||||
|
||||
// Signal imminent thread end
|
||||
pThreadData->m_threadExitMutex.Lock();
|
||||
pThreadData->m_isRunning = false;
|
||||
pThreadData->m_threadExitCondition.Notify();
|
||||
pThreadData->m_threadExitMutex.Unlock();
|
||||
|
||||
// Unregister thread
|
||||
// Note: Unregister after m_threadExitCondition.Notify() to ensure pThreadData is still valid
|
||||
pThreadData->m_pThreadMngr->UnregisterThread(pThreadData->m_pThreadTask);
|
||||
|
||||
CryThreadUtil::CryThreadExitCall();
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool CThreadManager::JoinThread(IThread* pThreadTask, EJoinMode eJoinMode)
|
||||
{
|
||||
// Get thread object
|
||||
_smart_ptr<SThreadMetaData> pThreadImpl = 0;
|
||||
{
|
||||
AUTO_LOCK(m_spawnedThreadsLock);
|
||||
|
||||
SpawnedThreadMapIter res = m_spawnedThreads.find(pThreadTask);
|
||||
if (res == m_spawnedThreads.end())
|
||||
{
|
||||
// Thread has already finished and unregistered itself.
|
||||
// As it is complete we cannot wait for it.
|
||||
// Hence return true.
|
||||
return true;
|
||||
}
|
||||
|
||||
pThreadImpl = res->second; // Keep object alive
|
||||
}
|
||||
|
||||
// On try join, exit if the thread is not in a state to exit
|
||||
if (eJoinMode == eJM_TryJoin && pThreadImpl->m_isRunning)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Wait for completion of the target thread exit condition
|
||||
pThreadImpl->m_threadExitMutex.Lock();
|
||||
while (pThreadImpl->m_isRunning)
|
||||
{
|
||||
pThreadImpl->m_threadExitCondition.Wait(pThreadImpl->m_threadExitMutex);
|
||||
}
|
||||
pThreadImpl->m_threadExitMutex.Unlock();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool CThreadManager::UnregisterThread(IThread* pThreadTask)
|
||||
{
|
||||
AUTO_LOCK(m_spawnedThreadsLock);
|
||||
|
||||
SpawnedThreadMapIter res = m_spawnedThreads.find(pThreadTask);
|
||||
if (res == m_spawnedThreads.end())
|
||||
{
|
||||
// Duplicate thread deletion
|
||||
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "<ThreadInfo>: UnregisterThread: Unable to unregister thread. Thread name could not be found. Double deletion? IThread pointer: %p", pThreadTask);
|
||||
return false;
|
||||
}
|
||||
|
||||
m_spawnedThreads.erase(res);
|
||||
return true;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
const char* CThreadManager::GetThreadName(threadID nThreadId)
|
||||
{
|
||||
// Loop over internally spawned threads
|
||||
{
|
||||
AUTO_LOCK(m_spawnedThreadsLock);
|
||||
|
||||
SpawnedThreadMapConstIter iter = m_spawnedThreads.begin();
|
||||
SpawnedThreadMapConstIter iterEnd = m_spawnedThreads.end();
|
||||
|
||||
for (; iter != iterEnd; ++iter)
|
||||
{
|
||||
if (iter->second->m_threadId == nThreadId)
|
||||
{
|
||||
return iter->second->m_threadName.c_str();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Loop over third party threads
|
||||
{
|
||||
AUTO_LOCK(m_spawnedThirdPartyThreadsLock);
|
||||
|
||||
SpawnedThirdPartyThreadMapConstIter iter = m_spawnedThirdPartyThread.begin();
|
||||
SpawnedThirdPartyThreadMapConstIter iterEnd = m_spawnedThirdPartyThread.end();
|
||||
|
||||
for (; iter != iterEnd; ++iter)
|
||||
{
|
||||
if (iter->second->m_threadId == nThreadId)
|
||||
{
|
||||
return iter->second->m_threadName.c_str();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void CThreadManager::ForEachOtherThread(IThreadManager::ThreadModifFunction fpThreadModiFunction, void* pFuncData)
|
||||
{
|
||||
threadID nCurThreadId = CryThreadUtil::CryGetCurrentThreadId();
|
||||
|
||||
// Loop over internally spawned threads
|
||||
{
|
||||
AUTO_LOCK(m_spawnedThreadsLock);
|
||||
|
||||
SpawnedThreadMapConstIter iter = m_spawnedThreads.begin();
|
||||
SpawnedThreadMapConstIter iterEnd = m_spawnedThreads.end();
|
||||
|
||||
for (; iter != iterEnd; ++iter)
|
||||
{
|
||||
if (iter->second->m_threadId != nCurThreadId)
|
||||
{
|
||||
fpThreadModiFunction(iter->second->m_threadId, pFuncData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Loop over third party threads
|
||||
{
|
||||
AUTO_LOCK(m_spawnedThirdPartyThreadsLock);
|
||||
|
||||
SpawnedThirdPartyThreadMapConstIter iter = m_spawnedThirdPartyThread.begin();
|
||||
SpawnedThirdPartyThreadMapConstIter iterEnd = m_spawnedThirdPartyThread.end();
|
||||
|
||||
for (; iter != iterEnd; ++iter)
|
||||
{
|
||||
if (iter->second->m_threadId != nCurThreadId)
|
||||
{
|
||||
fpThreadModiFunction(iter->second->m_threadId, pFuncData);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool CThreadManager::SpawnThread(IThread* pThreadTask, const char* sThreadName, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, sThreadName);
|
||||
|
||||
// Format thread name
|
||||
char strThreadName[THREAD_NAME_LENGTH_MAX];
|
||||
const int cNumCharsNeeded = azvsnprintf(strThreadName, CRY_ARRAY_COUNT(strThreadName), sThreadName, args);
|
||||
if (cNumCharsNeeded > THREAD_NAME_LENGTH_MAX - 1 || cNumCharsNeeded < 0)
|
||||
{
|
||||
strThreadName[THREAD_NAME_LENGTH_MAX - 1] = '\0'; // The WinApi only null terminates if strLen < bufSize
|
||||
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "<ThreadInfo>: ThreadName \"%s\" has been truncated. Max characters allowed: %i. ", strThreadName, THREAD_NAME_LENGTH_MAX - 1);
|
||||
}
|
||||
|
||||
// Spawn thread
|
||||
bool ret = SpawnThreadImpl(pThreadTask, strThreadName);
|
||||
|
||||
if (!ret)
|
||||
{
|
||||
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "<ThreadInfo>: CSystem::SpawnThread error spawning thread: \"%s\" ", strThreadName);
|
||||
}
|
||||
|
||||
va_end(args);
|
||||
return ret;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool CThreadManager::SpawnThreadImpl(IThread* pThreadTask, const char* sThreadName)
|
||||
{
|
||||
if (pThreadTask == NULL)
|
||||
{
|
||||
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_ERROR, "<ThreadInfo>: SpawnThread '%s' ThreadTask is NULL : ignoring", sThreadName);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Init thread meta data
|
||||
SThreadMetaData* pThreadMetaData = new SThreadMetaData();
|
||||
pThreadMetaData->m_pThreadTask = pThreadTask;
|
||||
pThreadMetaData->m_pThreadMngr = this;
|
||||
pThreadMetaData->m_threadName = sThreadName;
|
||||
|
||||
// Add thread to map
|
||||
{
|
||||
AUTO_LOCK(m_spawnedThreadsLock);
|
||||
SpawnedThreadMapIter res = m_spawnedThreads.find(pThreadTask);
|
||||
if (res != m_spawnedThreads.end())
|
||||
{
|
||||
// Thread with same name already spawned
|
||||
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "<ThreadInfo>: SpawnThread: Thread \"%s\" already exists.", sThreadName);
|
||||
delete pThreadMetaData;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Insert thread data
|
||||
m_spawnedThreads.insert(ThreadMapPair(pThreadTask, pThreadMetaData));
|
||||
}
|
||||
|
||||
// Load config if we can and if no config has been defined to be loaded
|
||||
const SThreadConfig* pThreadConfig = gEnv->pThreadManager->GetThreadConfigManager()->GetThreadConfig(sThreadName);
|
||||
|
||||
// Create thread description
|
||||
CryThreadUtil::SThreadCreationDesc desc = {sThreadName, RunThread, pThreadMetaData, pThreadConfig->paramActivityFlag & SThreadConfig::eThreadParamFlag_StackSize ? pThreadConfig->stackSizeBytes : 0};
|
||||
|
||||
// Spawn new thread
|
||||
pThreadMetaData->m_isRunning = CryThreadUtil::CryCreateThread(&(pThreadMetaData->m_threadHandle), desc);
|
||||
|
||||
// Validate thread creation
|
||||
if (!pThreadMetaData->m_isRunning)
|
||||
{
|
||||
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "<ThreadInfo>: SpawnThread: Could not spawn thread \"%s\" .", sThreadName);
|
||||
|
||||
// Remove thread from map (also releases SThreadMetaData _smart_ptr)
|
||||
m_spawnedThreads.erase(m_spawnedThreads.find(pThreadTask));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool CThreadManager::RegisterThirdPartyThread(void* pThreadHandle, const char* sThreadName, ...)
|
||||
{
|
||||
if (!pThreadHandle)
|
||||
{
|
||||
pThreadHandle = reinterpret_cast<void*>(CryThreadUtil::CryGetCurrentThreadHandle());
|
||||
}
|
||||
|
||||
va_list args;
|
||||
va_start(args, sThreadName);
|
||||
|
||||
// Format thread name
|
||||
char strThreadName[THREAD_NAME_LENGTH_MAX];
|
||||
const int cNumCharsNeeded = azvsnprintf(strThreadName, CRY_ARRAY_COUNT(strThreadName), sThreadName, args);
|
||||
if (cNumCharsNeeded > THREAD_NAME_LENGTH_MAX - 1)
|
||||
{
|
||||
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "<ThreadInfo>: ThreadName \"%s\" has been truncated. Max characters allowed: %i. ", strThreadName, THREAD_NAME_LENGTH_MAX - 1);
|
||||
}
|
||||
|
||||
// Register 3rd party thread
|
||||
bool ret = RegisterThirdPartyThreadImpl(reinterpret_cast<CryThreadUtil::TThreadHandle>(pThreadHandle), strThreadName);
|
||||
|
||||
va_end(args);
|
||||
return ret;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool CThreadManager::RegisterThirdPartyThreadImpl(CryThreadUtil::TThreadHandle threadHandle, const char* sThreadName)
|
||||
{
|
||||
if (strcmp(sThreadName, "") == 0)
|
||||
{
|
||||
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "<ThreadInfo>: CThreadManager::RegisterThirdPartyThread error registering third party thread. No name provided.");
|
||||
return false;
|
||||
}
|
||||
// Init thread meta data
|
||||
SThreadMetaData* pThreadMetaData = new SThreadMetaData();
|
||||
pThreadMetaData->m_pThreadTask = 0;
|
||||
pThreadMetaData->m_pThreadMngr = this;
|
||||
pThreadMetaData->m_threadName = sThreadName;
|
||||
pThreadMetaData->m_threadHandle = CryThreadUtil::CryDuplicateThreadHandle(threadHandle); // Ensure that we are not storing a pseudo handle
|
||||
pThreadMetaData->m_threadId = CryThreadUtil::CryGetThreadId(pThreadMetaData->m_threadHandle);
|
||||
|
||||
{
|
||||
AUTO_LOCK(m_spawnedThirdPartyThreadsLock);
|
||||
|
||||
// Check for duplicate
|
||||
SpawnedThirdPartyThreadMapConstIter res = m_spawnedThirdPartyThread.find(sThreadName);
|
||||
if (res != m_spawnedThirdPartyThread.end())
|
||||
{
|
||||
CryFatalError("CThreadManager::RegisterThirdPartyThread - Unable to register thread \"%s\""
|
||||
"because another third party thread with the same name \"%s\" has already been registered with ThreadHandle: %p",
|
||||
sThreadName, res->second->m_threadName.c_str(), reinterpret_cast<void*>(threadHandle));
|
||||
|
||||
delete pThreadMetaData;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Insert thread data
|
||||
m_spawnedThirdPartyThread.insert(ThirdPartyThreadMapPair(pThreadMetaData->m_threadName.c_str(), pThreadMetaData));
|
||||
}
|
||||
|
||||
// Get thread config
|
||||
const SThreadConfig* pThreadConfig = gEnv->pThreadManager->GetThreadConfigManager()->GetThreadConfig(sThreadName);
|
||||
|
||||
// Apply config (if not default config)
|
||||
if (strcmp(pThreadConfig->szThreadName, sThreadName) == 0)
|
||||
{
|
||||
ApplyThreadConfig(threadHandle, *pThreadConfig);
|
||||
}
|
||||
|
||||
// Update FP exception mask for 3rd party thread
|
||||
if (pThreadMetaData->m_threadId)
|
||||
{
|
||||
CryThreadUtil::EnableFloatExceptions(pThreadMetaData->m_threadId, (EFPE_Severity)g_cvars.sys_float_exceptions);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool CThreadManager::UnRegisterThirdPartyThread(const char* sThreadName, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, sThreadName);
|
||||
|
||||
// Format thread name
|
||||
char strThreadName[THREAD_NAME_LENGTH_MAX];
|
||||
const int cNumCharsNeeded = azvsnprintf(strThreadName, CRY_ARRAY_COUNT(strThreadName), sThreadName, args);
|
||||
if (cNumCharsNeeded > THREAD_NAME_LENGTH_MAX - 1)
|
||||
{
|
||||
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "<ThreadInfo>: ThreadName \"%s\" has been truncated. Max characters allowed: %i. ", strThreadName, THREAD_NAME_LENGTH_MAX - 1);
|
||||
}
|
||||
|
||||
// Unregister 3rd party thread
|
||||
bool ret = UnRegisterThirdPartyThreadImpl(strThreadName);
|
||||
|
||||
va_end(args);
|
||||
return ret;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool CThreadManager::UnRegisterThirdPartyThreadImpl(const char* sThreadName)
|
||||
{
|
||||
AUTO_LOCK(m_spawnedThirdPartyThreadsLock);
|
||||
|
||||
SpawnedThirdPartyThreadMapIter res = m_spawnedThirdPartyThread.find(sThreadName);
|
||||
if (res == m_spawnedThirdPartyThread.end())
|
||||
{
|
||||
// Duplicate thread deletion
|
||||
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "<ThreadInfo>: UnRegisterThirdPartyThread: Unable to unregister thread. Thread name \"%s\" could not be found. Double deletion? ", sThreadName);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Close thread handle
|
||||
CryThreadUtil::CryCloseThreadHandle(res->second->m_threadHandle);
|
||||
|
||||
// Delete reference from container
|
||||
m_spawnedThirdPartyThread.erase(res);
|
||||
return true;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
threadID CThreadManager::GetThreadId(const char* sThreadName, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, sThreadName);
|
||||
|
||||
// Format thread name
|
||||
char strThreadName[THREAD_NAME_LENGTH_MAX];
|
||||
const int cNumCharsNeeded = azvsnprintf(strThreadName, CRY_ARRAY_COUNT(strThreadName), sThreadName, args);
|
||||
if (cNumCharsNeeded > THREAD_NAME_LENGTH_MAX - 1)
|
||||
{
|
||||
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "<ThreadInfo>: ThreadName \"%s\" has been truncated. Max characters allowed: %i. ", strThreadName, THREAD_NAME_LENGTH_MAX - 1);
|
||||
}
|
||||
|
||||
// Get thread name
|
||||
threadID ret = GetThreadIdImpl(strThreadName);
|
||||
|
||||
va_end(args);
|
||||
return ret;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
threadID CThreadManager::GetThreadIdImpl(const char* sThreadName)
|
||||
{
|
||||
// Loop over internally spawned threads
|
||||
{
|
||||
AUTO_LOCK(m_spawnedThreadsLock);
|
||||
|
||||
SpawnedThreadMapConstIter iter = m_spawnedThreads.begin();
|
||||
SpawnedThreadMapConstIter iterEnd = m_spawnedThreads.end();
|
||||
|
||||
for (; iter != iterEnd; ++iter)
|
||||
{
|
||||
if (iter->second->m_threadName.compare(sThreadName) == 0)
|
||||
{
|
||||
return iter->second->m_threadId;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Loop over third party threads
|
||||
{
|
||||
AUTO_LOCK(m_spawnedThirdPartyThreadsLock);
|
||||
|
||||
SpawnedThirdPartyThreadMapConstIter iter = m_spawnedThirdPartyThread.begin();
|
||||
SpawnedThirdPartyThreadMapConstIter iterEnd = m_spawnedThirdPartyThread.end();
|
||||
|
||||
for (; iter != iterEnd; ++iter)
|
||||
{
|
||||
if (iter->second->m_threadName.compare(sThreadName) == 0)
|
||||
{
|
||||
return iter->second->m_threadId;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
static void EnableFPExceptionsForThread(threadID nThreadId, void* pData)
|
||||
{
|
||||
EFPE_Severity eFPESeverity = *(EFPE_Severity*)pData;
|
||||
CryThreadUtil::EnableFloatExceptions(nThreadId, eFPESeverity);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void CThreadManager::EnableFloatExceptions(EFPE_Severity eFPESeverity, threadID nThreadId /*=0*/)
|
||||
{
|
||||
CryThreadUtil::EnableFloatExceptions(nThreadId, eFPESeverity);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void CThreadManager::EnableFloatExceptionsForEachOtherThread(EFPE_Severity eFPESeverity)
|
||||
{
|
||||
ForEachOtherThread(EnableFPExceptionsForThread, &eFPESeverity);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
uint CThreadManager::GetFloatingPointExceptionMask()
|
||||
{
|
||||
return CryThreadUtil::GetFloatingPointExceptionMask();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void CThreadManager::SetFloatingPointExceptionMask(uint nMask)
|
||||
{
|
||||
CryThreadUtil::SetFloatingPointExceptionMask(nMask);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void CSystem::InitThreadSystem()
|
||||
{
|
||||
m_pThreadManager = new CThreadManager();
|
||||
m_env.pThreadManager = m_pThreadManager;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void CSystem::ShutDownThreadSystem()
|
||||
{
|
||||
SAFE_DELETE(m_pThreadManager);
|
||||
}
|
||||
@@ -46,8 +46,6 @@
|
||||
#include <shlobj.h>
|
||||
#endif
|
||||
|
||||
#include "IDebugCallStack.h"
|
||||
|
||||
#if defined(APPLE) || defined(LINUX)
|
||||
#include <pwd.h>
|
||||
#endif
|
||||
@@ -752,13 +750,6 @@ void CSystem::FatalError(const char* format, ...)
|
||||
}
|
||||
|
||||
// Dump callstack.
|
||||
#endif
|
||||
#if defined (WIN32)
|
||||
//Triggers a fatal error, so the DebugCallstack can create the error.log and terminate the application
|
||||
IDebugCallStack::instance()->FatalError(szBuffer);
|
||||
#elif defined(AZ_RESTRICTED_PLATFORM)
|
||||
#define AZ_RESTRICTED_SECTION SYSTEMWIN32_CPP_SECTION_1
|
||||
#include AZ_RESTRICTED_FILE(SystemWin32_cpp)
|
||||
#endif
|
||||
|
||||
CryDebugBreak();
|
||||
@@ -800,8 +791,6 @@ void CSystem::ReportBug([[maybe_unused]] const char* format, ...)
|
||||
va_start(ArgList, format);
|
||||
azvsnprintf(szBuffer + strlen(sPrefix), MAX_WARNING_LENGTH - strlen(sPrefix), format, ArgList);
|
||||
va_end(ArgList);
|
||||
|
||||
IDebugCallStack::instance()->ReportBug(szBuffer);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -910,10 +899,6 @@ void CSystem::LogSystemInfo()
|
||||
OSVERSIONINFO OSVerInfo;
|
||||
OSVerInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
|
||||
|
||||
// log Windows type
|
||||
Win32SysInspect::GetOS(m_env.pi.winVer, m_env.pi.win64Bit, szBuffer, sizeof(szBuffer));
|
||||
CryLogAlways(szBuffer);
|
||||
|
||||
// log system language
|
||||
GetLocaleInfo(LOCALE_SYSTEM_DEFAULT, LOCALE_SENGLANGUAGE, szLanguageBuffer, sizeof(szLanguageBuffer));
|
||||
azsprintf(szBuffer, "System language: %s", szLanguageBuffer);
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
#include "CrySystem_precompiled.h"
|
||||
#include <AzTest/AzTest.h>
|
||||
#include <AzCore/Memory/AllocatorScope.h>
|
||||
#include <CryCommon/stl/STLAlignedAlloc.h>
|
||||
|
||||
TEST(StringTests, CUT_Strings)
|
||||
{
|
||||
@@ -424,21 +423,6 @@ TEST_F(CryPrimitives, CUT_FixedString)
|
||||
EXPECT_EQ("0123", str5);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Unit Testing of aligned_vector
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
TEST_F(CryPrimitives, CUT_AlignedVector)
|
||||
{
|
||||
stl::aligned_vector<int, 16> vec;
|
||||
|
||||
vec.push_back(1);
|
||||
vec.push_back(2);
|
||||
vec.push_back(3);
|
||||
|
||||
EXPECT_TRUE(vec.size() == 3);
|
||||
EXPECT_TRUE(((INT_PTR)(&vec[0]) % 16) == 0);
|
||||
}
|
||||
|
||||
TEST_F(CryPrimitives, CUT_DynArray)
|
||||
{
|
||||
LegacyDynArray<int> a;
|
||||
|
||||
@@ -1,577 +0,0 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
// Original file Copyright Crytek GMBH or its affiliates, used under license.
|
||||
|
||||
#include "CrySystem_precompiled.h"
|
||||
#include "ThreadConfigManager.h"
|
||||
#include "IConsole.h"
|
||||
#include "System.h"
|
||||
#include <StringUtils.h>
|
||||
#include <CryCustomTypes.h>
|
||||
#include "CryUtils.h"
|
||||
namespace
|
||||
{
|
||||
const char* sCurThreadConfigFilename = "";
|
||||
const uint32 sPlausibleStackSizeLimitKB = (1024 * 100); // 100mb
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
CThreadConfigManager::CThreadConfigManager()
|
||||
{
|
||||
m_defaultConfig.szThreadName = "CryThread_Unnamed";
|
||||
m_defaultConfig.stackSizeBytes = 0;
|
||||
m_defaultConfig.affinityFlag = -1;
|
||||
m_defaultConfig.priority = THREAD_PRIORITY_NORMAL;
|
||||
m_defaultConfig.bDisablePriorityBoost = false;
|
||||
m_defaultConfig.paramActivityFlag = (SThreadConfig::TThreadParamFlag)~0;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
const SThreadConfig* CThreadConfigManager::GetThreadConfig(const char* szThreadName, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, szThreadName);
|
||||
|
||||
// Format thread name
|
||||
char strThreadName[THREAD_NAME_LENGTH_MAX];
|
||||
const int cNumCharsNeeded = azvsnprintf(strThreadName, CRY_ARRAY_COUNT(strThreadName), szThreadName, args);
|
||||
if (cNumCharsNeeded > THREAD_NAME_LENGTH_MAX - 1)
|
||||
{
|
||||
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "<ThreadInfo>: ThreadName \"%s\" has been truncated. Max characters allowed: %i. ", strThreadName, THREAD_NAME_LENGTH_MAX - 1);
|
||||
}
|
||||
|
||||
// Get Thread Config
|
||||
const SThreadConfig* retThreasdConfig = GetThreadConfigImpl(strThreadName);
|
||||
|
||||
va_end(args);
|
||||
return retThreasdConfig;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
const SThreadConfig* CThreadConfigManager::GetThreadConfigImpl(const char* szThreadName)
|
||||
{
|
||||
// Get thread config for platform
|
||||
ThreadConfigMapConstIter threatRet = m_threadConfig.find(CryFixedStringT<THREAD_NAME_LENGTH_MAX>(szThreadName));
|
||||
if (threatRet == m_threadConfig.end())
|
||||
{
|
||||
// Search in wildcard setups
|
||||
ThreadConfigMapConstIter wildCardIter = m_wildcardThreadConfig.begin();
|
||||
ThreadConfigMapConstIter wildCardIterEnd = m_wildcardThreadConfig.end();
|
||||
for (; wildCardIter != wildCardIterEnd; ++wildCardIter)
|
||||
{
|
||||
if (CryStringUtils::MatchWildcard(szThreadName, wildCardIter->second.szThreadName))
|
||||
{
|
||||
// Store new thread config
|
||||
SThreadConfig threadConfig = wildCardIter->second;
|
||||
std::pair<ThreadConfigMapIter, bool> res;
|
||||
res = m_threadConfig.insert(ThreadConfigMapPair(CryFixedStringT<THREAD_NAME_LENGTH_MAX>(szThreadName), threadConfig));
|
||||
|
||||
// Store name (ref to key)
|
||||
SThreadConfig& rMapThreadConfig = res.first->second;
|
||||
rMapThreadConfig.szThreadName = res.first->first.c_str();
|
||||
|
||||
// Return new thread config
|
||||
return &res.first->second;
|
||||
}
|
||||
}
|
||||
|
||||
// Failure case, no match found
|
||||
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "<ThreadInfo>: Unable to find config for thread:%s", szThreadName);
|
||||
return &m_defaultConfig;
|
||||
}
|
||||
|
||||
// Return thread config
|
||||
return &threatRet->second;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
const SThreadConfig* CThreadConfigManager::GetDefaultThreadConfig() const
|
||||
{
|
||||
return &m_defaultConfig;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool CThreadConfigManager::LoadConfig(const char* pcPath)
|
||||
{
|
||||
// Adjust filename for OnDisk or in .pak file loading
|
||||
char szFullPathBuf[AZ::IO::IArchive::MaxPath];
|
||||
gEnv->pCryPak->AdjustFileName(pcPath, szFullPathBuf, AZ_ARRAY_SIZE(szFullPathBuf), 0);
|
||||
|
||||
// Open file
|
||||
XmlNodeRef xmlRoot = GetISystem()->LoadXmlFromFile(szFullPathBuf);
|
||||
if (!xmlRoot)
|
||||
{
|
||||
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "<ThreadConfigInfo>: File \"%s\" not found!", pcPath);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Load config for active platform
|
||||
sCurThreadConfigFilename = pcPath;
|
||||
const char* strPlatformId = IdentifyPlatform();
|
||||
CryFixedStringT<32> tmpPlatformStr;
|
||||
bool retValue = false;
|
||||
|
||||
// Try load common platform settings
|
||||
tmpPlatformStr.Format("%s_Common", strPlatformId);
|
||||
LoadPlatformConfig(xmlRoot, tmpPlatformStr.c_str());
|
||||
|
||||
#if defined(CRY_PLATFORM_DESKTOP)
|
||||
// Handle PC specifically as we do not know the core setup of the executing machine.
|
||||
// Try and find the next power of 2 core setup. Otherwise fallback to a lower power of 2 core setup spec
|
||||
|
||||
// Try and load next pow of 2 setup for active pc core configuration
|
||||
const unsigned int numCPUs = ((CSystem*)GetISystem())->GetCPUFeatures()->GetLogicalCPUCount();
|
||||
uint32 i = numCPUs;
|
||||
for (; i > 0; --i)
|
||||
{
|
||||
tmpPlatformStr.Format("%s_%i", strPlatformId, i);
|
||||
retValue = LoadPlatformConfig(xmlRoot, tmpPlatformStr.c_str());
|
||||
if (retValue)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (retValue && i != numCPUs)
|
||||
{
|
||||
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "<ThreadConfigInfo>: (%s: %u core) Unable to find platform config \"%s\". Next valid config found was %s_%u.",
|
||||
strPlatformId, numCPUs, tmpPlatformStr.c_str(), strPlatformId, i);
|
||||
}
|
||||
|
||||
#else
|
||||
tmpPlatformStr.Format("%s", strPlatformId);
|
||||
retValue = LoadPlatformConfig(xmlRoot, strPlatformId);
|
||||
#endif
|
||||
|
||||
// Print out info
|
||||
if (retValue)
|
||||
{
|
||||
CryLogAlways("<ThreadConfigInfo>: Thread profile loaded: \"%s\" (%s) ", tmpPlatformStr.c_str(), pcPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Could not find any matching platform
|
||||
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "<ThreadConfigInfo>: Active platform identifier string \"%s\" not found in config \"%s\".", strPlatformId, sCurThreadConfigFilename);
|
||||
}
|
||||
|
||||
sCurThreadConfigFilename = "";
|
||||
return retValue;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool CThreadConfigManager::ConfigLoaded() const
|
||||
{
|
||||
return !m_threadConfig.empty();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool CThreadConfigManager::LoadPlatformConfig(const XmlNodeRef& rXmlRoot, const char* sPlatformId)
|
||||
{
|
||||
// Validate node
|
||||
if (!rXmlRoot->isTag("ThreadConfig"))
|
||||
{
|
||||
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "<ThreadConfigInfo>: Unable to find root xml node \"ThreadConfig\"");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Find active platform
|
||||
const uint32 numPlatforms = rXmlRoot->getChildCount();
|
||||
for (uint32 i = 0; i < numPlatforms; ++i)
|
||||
{
|
||||
const XmlNodeRef xmlPlatformNode = rXmlRoot->getChild(i);
|
||||
|
||||
// Is platform node
|
||||
if (!xmlPlatformNode->isTag("Platform"))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Is has Name attribute
|
||||
if (!xmlPlatformNode->haveAttr("Name"))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Is platform of interest
|
||||
const char* platformName = xmlPlatformNode->getAttr("Name");
|
||||
if (_stricmp(sPlatformId, platformName) == 0)
|
||||
{
|
||||
// Load platform
|
||||
LoadThreadDefaultConfig(xmlPlatformNode);
|
||||
LoadPlatformThreadConfigs(xmlPlatformNode);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void CThreadConfigManager::LoadPlatformThreadConfigs(const XmlNodeRef& rXmlPlatformRef)
|
||||
{
|
||||
// Get thread configurations for active platform
|
||||
const uint32 numThreads = rXmlPlatformRef->getChildCount();
|
||||
for (uint32 j = 0; j < numThreads; ++j)
|
||||
{
|
||||
const XmlNodeRef xmlThreadNode = rXmlPlatformRef->getChild(j);
|
||||
|
||||
if (!xmlThreadNode->isTag("Thread"))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Ensure thread config has name
|
||||
if (!xmlThreadNode->haveAttr("Name"))
|
||||
{
|
||||
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "<ThreadConfigInfo>: [XML Parsing] Thread node without \"name\" attribute encountered.");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Load thread config
|
||||
SThreadConfig loadedThreadConfig = SThreadConfig(m_defaultConfig);
|
||||
LoadThreadConfig(xmlThreadNode, loadedThreadConfig);
|
||||
|
||||
// Get thread name and check if it contains wildcard characters
|
||||
const char* szThreadName = xmlThreadNode->getAttr("Name");
|
||||
bool bWildCard = strchr(szThreadName, '*') ? true : false;
|
||||
ThreadConfigMap& threadConfig = bWildCard ? m_wildcardThreadConfig : m_threadConfig;
|
||||
|
||||
// Check for duplicate and override it with new config if found
|
||||
if (threadConfig.find(szThreadName) != threadConfig.end())
|
||||
{
|
||||
CryLogAlways("<ThreadConfigInfo>: [XML Parsing] Thread with name \"%s\" already loaded. Overriding with new configuration", szThreadName);
|
||||
threadConfig[szThreadName] = loadedThreadConfig;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Store new thread config
|
||||
std::pair<ThreadConfigMapIter, bool> res;
|
||||
res = threadConfig.insert(ThreadConfigMapPair(CryFixedStringT<THREAD_NAME_LENGTH_MAX>(szThreadName), loadedThreadConfig));
|
||||
|
||||
// Store name (ref to key)
|
||||
SThreadConfig& rMapThreadConfig = res.first->second;
|
||||
rMapThreadConfig.szThreadName = res.first->first.c_str();
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool CThreadConfigManager::LoadThreadDefaultConfig(const XmlNodeRef& rXmlPlatformRef)
|
||||
{
|
||||
// Find default thread config node
|
||||
const uint32 numNodes = rXmlPlatformRef->getChildCount();
|
||||
for (uint32 j = 0; j < numNodes; ++j)
|
||||
{
|
||||
const XmlNodeRef xmlNode = rXmlPlatformRef->getChild(j);
|
||||
|
||||
// Load default config
|
||||
if (xmlNode->isTag("ThreadDefault"))
|
||||
{
|
||||
LoadThreadConfig(xmlNode, m_defaultConfig);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void CThreadConfigManager::LoadAffinity(const XmlNodeRef& rXmlThreadRef, uint32& rAffinity, SThreadConfig::TThreadParamFlag& rParamActivityFlag)
|
||||
{
|
||||
const char* szValidCharacters = "-,0123456789";
|
||||
uint32 affinity = 0;
|
||||
|
||||
// Validate node
|
||||
if (!rXmlThreadRef->haveAttr("Affinity"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate token
|
||||
CryFixedStringT<32> affinityRawStr(rXmlThreadRef->getAttr("Affinity"));
|
||||
if (affinityRawStr.empty())
|
||||
{
|
||||
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "<ThreadConfigInfo>: [XML Parsing] Empty attribute \"Affinity\" encountered");
|
||||
return;
|
||||
}
|
||||
|
||||
if (affinityRawStr.compareNoCase("ignore") == 0)
|
||||
{
|
||||
// Param is inactive, clear bit
|
||||
rParamActivityFlag &= ~SThreadConfig::eThreadParamFlag_Affinity;
|
||||
return;
|
||||
}
|
||||
|
||||
CryFixedStringT<32>::size_type nPos = affinityRawStr.find_first_not_of(" -,0123456789");
|
||||
if (nPos != CryFixedStringT<32>::npos)
|
||||
{
|
||||
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING,
|
||||
"<ThreadConfigInfo>: [XML Parsing] Invalid character \"%c\" encountered in \"Affinity\" attribute. Valid characters:\"%s\" Offending token:\"%s\"", affinityRawStr.at(nPos),
|
||||
szValidCharacters, affinityRawStr.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
// Tokenize comma separated string
|
||||
int pos = 0;
|
||||
CryFixedStringT<32> affnityTokStr = affinityRawStr.Tokenize(",", pos);
|
||||
while (!affnityTokStr.empty())
|
||||
{
|
||||
affnityTokStr.Trim();
|
||||
|
||||
long affinityId = strtol(affnityTokStr.c_str(), NULL, 10);
|
||||
if (affinityId == LONG_MAX || affinityId == LONG_MIN)
|
||||
{
|
||||
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "<ThreadConfigInfo>: [XML Parsing] Unknown value \"%s\" encountered for attribute \"Affinity\"", affnityTokStr.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
// Allow scheduler to pick thread
|
||||
if (affinityId == -1)
|
||||
{
|
||||
affinity = ~0;
|
||||
break;
|
||||
}
|
||||
|
||||
// Set affinity bit
|
||||
affinity |= BIT(affinityId);
|
||||
|
||||
// Move to next token
|
||||
affnityTokStr = affinityRawStr.Tokenize(",", pos);
|
||||
}
|
||||
|
||||
// Set affinity reference
|
||||
rAffinity = affinity;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void CThreadConfigManager::LoadPriority(const XmlNodeRef& rXmlThreadRef, int32& rPriority, SThreadConfig::TThreadParamFlag& rParamActivityFlag)
|
||||
{
|
||||
const char* szValidCharacters = "-,0123456789";
|
||||
|
||||
// Validate node
|
||||
if (!rXmlThreadRef->haveAttr("Priority"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate token
|
||||
CryFixedStringT<32> threadPrioStr(rXmlThreadRef->getAttr("Priority"));
|
||||
threadPrioStr.Trim();
|
||||
if (threadPrioStr.empty())
|
||||
{
|
||||
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "<ThreadConfigInfo>: [XML Parsing] Empty attribute \"Priority\" encountered");
|
||||
return;
|
||||
}
|
||||
|
||||
if (threadPrioStr.compareNoCase("ignore") == 0)
|
||||
{
|
||||
// Param is inactive, clear bit
|
||||
rParamActivityFlag &= ~SThreadConfig::eThreadParamFlag_Priority;
|
||||
return;
|
||||
}
|
||||
|
||||
// Test for character string (no numbers allowed)
|
||||
if (threadPrioStr.find_first_of(szValidCharacters) == CryFixedStringT<32>::npos)
|
||||
{
|
||||
threadPrioStr.MakeLower();
|
||||
|
||||
// Set priority
|
||||
if (threadPrioStr.compare("below_normal") == 0)
|
||||
{
|
||||
rPriority = THREAD_PRIORITY_BELOW_NORMAL;
|
||||
}
|
||||
else if (threadPrioStr.compare("normal") == 0)
|
||||
{
|
||||
rPriority = THREAD_PRIORITY_NORMAL;
|
||||
}
|
||||
else if (threadPrioStr.compare("above_normal") == 0)
|
||||
{
|
||||
rPriority = THREAD_PRIORITY_ABOVE_NORMAL;
|
||||
}
|
||||
else if (threadPrioStr.compare("idle") == 0)
|
||||
{
|
||||
rPriority = THREAD_PRIORITY_IDLE;
|
||||
}
|
||||
else if (threadPrioStr.compare("lowest") == 0)
|
||||
{
|
||||
rPriority = THREAD_PRIORITY_LOWEST;
|
||||
}
|
||||
else if (threadPrioStr.compare("highest") == 0)
|
||||
{
|
||||
rPriority = THREAD_PRIORITY_HIGHEST;
|
||||
}
|
||||
else if (threadPrioStr.compare("time_critical") == 0)
|
||||
{
|
||||
rPriority = THREAD_PRIORITY_TIME_CRITICAL;
|
||||
}
|
||||
else
|
||||
{
|
||||
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "<ThreadConfigInfo>: [XML Parsing] Platform unsupported value \"%s\" encountered for attribute \"Priority\"", threadPrioStr.c_str());
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Test for number string (no alphabetical characters allowed)
|
||||
else if (threadPrioStr.find_first_not_of(szValidCharacters) == CryFixedStringT<32>::npos)
|
||||
{
|
||||
long numValue = strtol(threadPrioStr.c_str(), NULL, 10);
|
||||
if (numValue == LONG_MAX || numValue == LONG_MIN)
|
||||
{
|
||||
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "<ThreadConfigInfo>: [XML Parsing] Unsupported number type \"%s\" for for attribute \"Priority\"", threadPrioStr.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
// Set priority
|
||||
rPriority = numValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
// String contains characters and numbers
|
||||
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "<ThreadConfigInfo>: [XML Parsing] Unsupported type \"%s\" encountered for attribute \"Priority\". Token containers numbers and characters", threadPrioStr.c_str());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void CThreadConfigManager::LoadDisablePriorityBoost(const XmlNodeRef& rXmlThreadRef, bool& rPriorityBoost, SThreadConfig::TThreadParamFlag& rParamActivityFlag)
|
||||
{
|
||||
// Validate node
|
||||
if (!rXmlThreadRef->haveAttr("DisablePriorityBoost"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Extract bool info
|
||||
CryFixedStringT<16> sAttribToken(rXmlThreadRef->getAttr("DisablePriorityBoost"));
|
||||
sAttribToken.Trim();
|
||||
sAttribToken.MakeLower();
|
||||
|
||||
if (sAttribToken.compare("ignore") == 0)
|
||||
{
|
||||
// Param is inactive, clear bit
|
||||
rParamActivityFlag &= ~SThreadConfig::eThreadParamFlag_PriorityBoost;
|
||||
return;
|
||||
}
|
||||
else if (sAttribToken.compare("true") == 0 || sAttribToken.compare("1") == 0)
|
||||
{
|
||||
rPriorityBoost = true;
|
||||
}
|
||||
else if (sAttribToken.compare("false") == 0 || sAttribToken.compare("0") == 0)
|
||||
{
|
||||
rPriorityBoost = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "<ThreadConfigInfo>: [XML Parsing] Unsupported bool type \"%s\" encountered for attribute \"DisablePriorityBoost\"",
|
||||
rXmlThreadRef->getAttr("DisablePriorityBoost"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void CThreadConfigManager::LoadStackSize(const XmlNodeRef& rXmlThreadRef, uint32& rStackSize, SThreadConfig::TThreadParamFlag& rParamActivityFlag)
|
||||
{
|
||||
const char* sValidCharacters = "0123456789";
|
||||
|
||||
if (rXmlThreadRef->haveAttr("StackSizeKB"))
|
||||
{
|
||||
// Read stack size
|
||||
CryFixedStringT<32> stackSize(rXmlThreadRef->getAttr("StackSizeKB"));
|
||||
|
||||
// Validate stack size
|
||||
if (stackSize.empty())
|
||||
{
|
||||
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "<ThreadConfigInfo>: [XML Parsing] Empty attribute \"StackSize\" encountered");
|
||||
return;
|
||||
}
|
||||
else if (stackSize.compareNoCase("ignore") == 0)
|
||||
{
|
||||
// Param is inactive, clear bit
|
||||
rParamActivityFlag &= ~SThreadConfig::eThreadParamFlag_StackSize;
|
||||
return;
|
||||
}
|
||||
else if (stackSize.find_first_not_of(sValidCharacters) == CryFixedStringT<32>::npos)
|
||||
{
|
||||
// Convert string to long
|
||||
long stackSizeVal = strtol(stackSize.c_str(), NULL, 10);
|
||||
if (stackSizeVal == LONG_MAX || stackSizeVal == LONG_MIN)
|
||||
{
|
||||
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "<ThreadConfigInfo>: [XML Parsing] Invalid number for \"StackSize\" encountered. \"%s\"", stackSize.c_str());
|
||||
return;
|
||||
}
|
||||
else if (stackSizeVal <= 0 || stackSizeVal > sPlausibleStackSizeLimitKB)
|
||||
{
|
||||
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "<ThreadConfigInfo>: [XML Parsing] \"StackSize\" value not plausible \"%" PRId64 "KB\"", (int64)stackSizeVal);
|
||||
return;
|
||||
}
|
||||
|
||||
// Set stack size
|
||||
rStackSize = stackSizeVal * 1024; // Convert to bytes
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void CThreadConfigManager::LoadThreadConfig(const XmlNodeRef& rXmlThreadRef, SThreadConfig& rThreadConfig)
|
||||
{
|
||||
LoadAffinity(rXmlThreadRef, rThreadConfig.affinityFlag, rThreadConfig.paramActivityFlag);
|
||||
LoadPriority(rXmlThreadRef, rThreadConfig.priority, rThreadConfig.paramActivityFlag);
|
||||
LoadDisablePriorityBoost(rXmlThreadRef, rThreadConfig.bDisablePriorityBoost, rThreadConfig.paramActivityFlag);
|
||||
LoadStackSize(rXmlThreadRef, rThreadConfig.stackSizeBytes, rThreadConfig.paramActivityFlag);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
const char* CThreadConfigManager::IdentifyPlatform()
|
||||
{
|
||||
#if defined(AZ_RESTRICTED_PLATFORM)
|
||||
#include AZ_RESTRICTED_FILE(ThreadConfigManager_cpp)
|
||||
#endif
|
||||
#if defined(AZ_RESTRICTED_SECTION_IMPLEMENTED)
|
||||
#undef AZ_RESTRICTED_SECTION_IMPLEMENTED
|
||||
#elif defined(ANDROID)
|
||||
return "android";
|
||||
#elif defined(LINUX)
|
||||
return "linux";
|
||||
#elif defined(APPLE)
|
||||
return "mac";
|
||||
#elif defined(WIN32) || defined(WIN64)
|
||||
return "pc";
|
||||
#else
|
||||
#error "Undefined platform"
|
||||
#endif
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void CThreadConfigManager::DumpThreadConfigurationsToLog()
|
||||
{
|
||||
#if !defined(RELEASE)
|
||||
|
||||
// Print header
|
||||
CryLogAlways("== Thread Startup Config List (\"%s\") ==", IdentifyPlatform());
|
||||
|
||||
// Print loaded default config
|
||||
CryLogAlways(" (Default) 1. \"%s\" (StackSize:%uKB | Affinity:%u | Priority:%i | PriorityBoost:\"%s\")", m_defaultConfig.szThreadName, m_defaultConfig.stackSizeBytes / 1024,
|
||||
m_defaultConfig.affinityFlag, m_defaultConfig.priority, m_defaultConfig.bDisablePriorityBoost ? "disabled" : "enabled");
|
||||
|
||||
// Print loaded thread configs
|
||||
int listItemCounter = 1;
|
||||
ThreadConfigMapConstIter iter = m_threadConfig.begin();
|
||||
ThreadConfigMapConstIter iterEnd = m_threadConfig.end();
|
||||
for (; iter != iterEnd; ++iter)
|
||||
{
|
||||
const SThreadConfig& threadConfig = iter->second;
|
||||
CryLogAlways("%3d.\"%s\" %s (StackSize:%uKB %s | Affinity:%u %s | Priority:%i %s | PriorityBoost:\"%s\" %s)", ++listItemCounter,
|
||||
threadConfig.szThreadName, (threadConfig.paramActivityFlag & SThreadConfig::eThreadParamFlag_ThreadName) ? "" : "(ignored)",
|
||||
threadConfig.stackSizeBytes / 1024u, (threadConfig.paramActivityFlag & SThreadConfig::eThreadParamFlag_StackSize) ? "" : "(ignored)",
|
||||
threadConfig.affinityFlag, (threadConfig.paramActivityFlag & SThreadConfig::eThreadParamFlag_Affinity) ? "" : "(ignored)",
|
||||
threadConfig.priority, (threadConfig.paramActivityFlag & SThreadConfig::eThreadParamFlag_Priority) ? "" : "(ignored)",
|
||||
!threadConfig.bDisablePriorityBoost ? "enabled" : "disabled", (threadConfig.paramActivityFlag & SThreadConfig::eThreadParamFlag_PriorityBoost) ? "" : "(ignored)");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -1,137 +0,0 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
// Original file Copyright Crytek GMBH or its affiliates, used under license.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include "IThreadConfigManager.h"
|
||||
|
||||
/*
|
||||
ThreadConfigManager:
|
||||
Loads a thread configuration from an xml file and stores them.
|
||||
|
||||
== XML File Layout and Rules: ===
|
||||
|
||||
= Platform names =
|
||||
(case insensitive)
|
||||
"ANDROID"
|
||||
"PC"
|
||||
"MAC"
|
||||
etc.
|
||||
|
||||
= Basic Layout =
|
||||
<ThreadConfig>
|
||||
<Platform name="XXX">
|
||||
<ThreadDefault Affinity="XX" Priority="XX" StackSizeKB="XX">
|
||||
<Thread name ="A" Affinity="XX" Priority="XX" StackSizeKB="XX">
|
||||
<Thread name ="B" Affinity="XX" >
|
||||
...
|
||||
</Platform>
|
||||
|
||||
<Platform name="YYY">
|
||||
...
|
||||
</Platform>
|
||||
</ThreadConfig>
|
||||
|
||||
= Parser Order for Platform =
|
||||
1. PlatformName_Common (valid for all potential platform configurations. Can be overridden by concert platform configuration)
|
||||
2. PlatformName or PlatformName_X (for platforms with unknown CPU count where X is the number of potential cores. The equal or next lower matching configuration for the identified core count at runtime will be taken)
|
||||
|
||||
Note: Overriding of thread configuration by later parsed configuration allowed.
|
||||
|
||||
= <ThreadDefault> and <Thread> XML attributes =
|
||||
|
||||
!!!
|
||||
Note: Use "ignore" as value if you do not want the thread system to set the value specifically!
|
||||
If a value is not defines the <ThreadDefault> value of the parameter will be used.
|
||||
This is useful when dealing with 3rdParty threads where you are not in control of the parameter setup.
|
||||
!!!
|
||||
|
||||
Name:
|
||||
"x" (string) : Name of thread
|
||||
"x*y" (string) : Name of thread with wildcard character
|
||||
|
||||
Affinity:
|
||||
"-1" : Put SW thread affinity in the hands of the scheduler - (default) -
|
||||
"x" : Run thread on specified core
|
||||
"x, y, ..." : Run thread on specified cores
|
||||
|
||||
Priority:
|
||||
"idle" : Hint to CryEngine to run thread with pre-set priority
|
||||
"below_normal" : Hint to CryEngine to run thread with pre-set priority
|
||||
"normal" : Hint to CryEngine to run thread with pre-set priority - (default) -
|
||||
"above_normal" : Hint to CryEngine to run thread with pre-set priority
|
||||
"highest" : Hint to CryEngine to run thread with pre-set priority
|
||||
"time_critical" : Hint to CryEngine to run thread with pre-set priority
|
||||
"x" (number) : User defined thread priority number
|
||||
|
||||
StackSizeKB:
|
||||
"0" : Let platform decide on the stack size - (default) -
|
||||
"x" : Create thread with "x" KB of stack size
|
||||
|
||||
DisablePriorityBoost:
|
||||
"true" : Disable priority boosting - (default) -
|
||||
"false" : Enable priority boosting
|
||||
*/
|
||||
|
||||
class CThreadConfigManager
|
||||
: public IThreadConfigManager
|
||||
{
|
||||
public:
|
||||
typedef std::map<CryFixedStringT<THREAD_NAME_LENGTH_MAX>, SThreadConfig> ThreadConfigMap;
|
||||
typedef std::pair<CryFixedStringT<THREAD_NAME_LENGTH_MAX>, SThreadConfig> ThreadConfigMapPair;
|
||||
typedef std::map<CryFixedStringT<THREAD_NAME_LENGTH_MAX>, SThreadConfig>::iterator ThreadConfigMapIter;
|
||||
typedef std::map<CryFixedStringT<THREAD_NAME_LENGTH_MAX>, SThreadConfig>::const_iterator ThreadConfigMapConstIter;
|
||||
|
||||
public:
|
||||
CThreadConfigManager();
|
||||
~CThreadConfigManager()
|
||||
{
|
||||
}
|
||||
|
||||
// Called once during System startup.
|
||||
// Loads the thread configuration for the executing platform from file.
|
||||
virtual bool LoadConfig(const char* pcPath) override;
|
||||
|
||||
// Returns true if a config has been loaded
|
||||
virtual bool ConfigLoaded() const override;
|
||||
|
||||
// Gets the thread configuration for the specified thread on the active platform.
|
||||
// If no matching config is found a default configuration is returned
|
||||
// (which does not have the same name as the search string).
|
||||
virtual const SThreadConfig* GetThreadConfig(const char* sThreadName, ...) override;
|
||||
virtual const SThreadConfig* GetDefaultThreadConfig() const override;
|
||||
|
||||
virtual void DumpThreadConfigurationsToLog() override;
|
||||
|
||||
private:
|
||||
const char* IdentifyPlatform();
|
||||
|
||||
const SThreadConfig* GetThreadConfigImpl(const char* cThreadName);
|
||||
|
||||
bool LoadPlatformConfig(const XmlNodeRef& rXmlRoot, const char* sPlatformId);
|
||||
|
||||
void LoadPlatformThreadConfigs(const XmlNodeRef& rXmlPlatformRef);
|
||||
bool LoadThreadDefaultConfig(const XmlNodeRef& rXmlPlatformRef);
|
||||
void LoadThreadConfig(const XmlNodeRef& rXmlThreadRef, SThreadConfig& rThreadConfig);
|
||||
|
||||
void LoadAffinity(const XmlNodeRef& rXmlThreadRef, uint32& rAffinity, SThreadConfig::TThreadParamFlag& rParamActivityFlag);
|
||||
void LoadPriority(const XmlNodeRef& rXmlThreadRef, int32& rPriority, SThreadConfig::TThreadParamFlag& rParamActivityFlag);
|
||||
void LoadDisablePriorityBoost(const XmlNodeRef& rXmlThreadRef, bool& rPriorityBoost, SThreadConfig::TThreadParamFlag& rParamActivityFlag);
|
||||
void LoadStackSize(const XmlNodeRef& rXmlThreadRef, uint32& rStackSize, SThreadConfig::TThreadParamFlag& rParamActivityFlag);
|
||||
|
||||
private:
|
||||
ThreadConfigMap m_threadConfig; // Note: The map key is referenced by as const char* by the value's storage class. Other containers may not support this behaviour as they will re-allocate memory as they grow/shrink.
|
||||
ThreadConfigMap m_wildcardThreadConfig;
|
||||
SThreadConfig m_defaultConfig;
|
||||
};
|
||||
@@ -1,123 +0,0 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
// Original file Copyright Crytek GMBH or its affiliates, used under license.
|
||||
|
||||
#include "CrySystem_precompiled.h"
|
||||
#include "ThreadInfo.h"
|
||||
#include "System.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
#if defined(AZ_RESTRICTED_PLATFORM)
|
||||
#undef AZ_RESTRICTED_SECTION
|
||||
#define THREADINFO_CPP_SECTION_1 1
|
||||
#define THREADINFO_CPP_SECTION_2 2
|
||||
#endif
|
||||
|
||||
#if defined(AZ_RESTRICTED_PLATFORM)
|
||||
#define AZ_RESTRICTED_SECTION THREADINFO_CPP_SECTION_1
|
||||
#include AZ_RESTRICTED_FILE(ThreadInfo_cpp)
|
||||
#endif
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
#if AZ_LEGACY_CRYSYSTEM_TRAIT_THREADINFO_WINDOWS_STYLE
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
void SThreadInfo::GetCurrentThreads(TThreadInfo& threadsOut)
|
||||
{
|
||||
HANDLE h = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
|
||||
DWORD currProcessId = GetCurrentProcessId();
|
||||
if (h != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
THREADENTRY32 te;
|
||||
te.dwSize = sizeof(te);
|
||||
if (Thread32First(h, &te))
|
||||
{
|
||||
do
|
||||
{
|
||||
if (te.dwSize >= FIELD_OFFSET(THREADENTRY32, th32OwnerProcessID) + sizeof(te.th32OwnerProcessID))
|
||||
{
|
||||
if (te.th32OwnerProcessID == currProcessId)
|
||||
{
|
||||
threadsOut[te.th32ThreadID] = CryThreadGetName(te.th32ThreadID);
|
||||
}
|
||||
}
|
||||
te.dwSize = sizeof(te);
|
||||
} while (Thread32Next(h, &te));
|
||||
}
|
||||
CloseHandle(h);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
void SThreadInfo::OpenThreadHandles(TThreads& threadsOut, const TThreadIds& threadIds /* = TThreadIds()*/, bool ignoreCurrThread /* = true*/)
|
||||
{
|
||||
TThreadIds threadids = threadIds;
|
||||
if (threadids.empty())
|
||||
{
|
||||
TThreadInfo threads;
|
||||
GetCurrentThreads(threads);
|
||||
DWORD currThreadId = GetCurrentThreadId();
|
||||
for (TThreadInfo::iterator it = threads.begin(), end = threads.end(); it != end; ++it)
|
||||
{
|
||||
if (!ignoreCurrThread || it->first != currThreadId)
|
||||
{
|
||||
threadids.push_back(it->first);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (TThreadIds::iterator it = threadids.begin(), end = threadids.end(); it != end; ++it)
|
||||
{
|
||||
SThreadHandle thread;
|
||||
thread.Id = *it;
|
||||
thread.Handle = OpenThread(THREAD_ALL_ACCESS, FALSE, *it);
|
||||
threadsOut.push_back(thread);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
void SThreadInfo::CloseThreadHandles(const TThreads& threads)
|
||||
{
|
||||
for (TThreads::const_iterator it = threads.begin(), end = threads.end(); it != end; ++it)
|
||||
{
|
||||
CloseHandle(it->Handle);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
#elif defined(AZ_RESTRICTED_PLATFORM)
|
||||
#define AZ_RESTRICTED_SECTION THREADINFO_CPP_SECTION_2
|
||||
#include AZ_RESTRICTED_FILE(ThreadInfo_cpp)
|
||||
#elif defined(LINUX) || defined(APPLE)
|
||||
void SThreadInfo::GetCurrentThreads(TThreadInfo& threadsOut)
|
||||
{
|
||||
assert(false); // not implemented!
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
void SThreadInfo::OpenThreadHandles(TThreads& threadsOut, const TThreadIds& threadIds /* = TThreadIds()*/, bool ignoreCurrThread /* = true*/)
|
||||
{
|
||||
assert(false); // not implemented!
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
void SThreadInfo::CloseThreadHandles(const TThreads& threads)
|
||||
{
|
||||
assert(false); // not implemented!
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
#endif
|
||||
@@ -1,43 +0,0 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
// Original file Copyright Crytek GMBH or its affiliates, used under license.
|
||||
|
||||
#ifndef CRYINCLUDE_CRYSYSTEM_THREADINFO_H
|
||||
#define CRYINCLUDE_CRYSYSTEM_THREADINFO_H
|
||||
#pragma once
|
||||
|
||||
|
||||
struct SThreadInfo
|
||||
{
|
||||
public:
|
||||
struct SThreadHandle
|
||||
{
|
||||
HANDLE Handle;
|
||||
uint32 Id;
|
||||
};
|
||||
|
||||
typedef std::vector<uint32> TThreadIds;
|
||||
typedef std::vector<SThreadHandle> TThreads;
|
||||
typedef std::map<uint32, string> TThreadInfo;
|
||||
|
||||
// returns thread info
|
||||
static void GetCurrentThreads(TThreadInfo& threadsOut);
|
||||
|
||||
// fills threadsOut vector with thread handles of given thread ids; if threadIds vector is emtpy it fills all running threads
|
||||
// if ignoreCurrThread is true it will not return the current thread
|
||||
static void OpenThreadHandles(TThreads& threadsOut, const TThreadIds& threadIds = TThreadIds(), bool ignoreCurrThread = true);
|
||||
|
||||
// closes thread handles; should be called whenever GetCurrentThreads was called!
|
||||
static void CloseThreadHandles(const TThreads& threads);
|
||||
};
|
||||
|
||||
#endif // CRYINCLUDE_CRYSYSTEM_THREADINFO_H
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,181 +0,0 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
// Original file Copyright Crytek GMBH or its affiliates, used under license.
|
||||
|
||||
#ifndef CRYINCLUDE_CRYSYSTEM_THREADTASK_H
|
||||
#define CRYINCLUDE_CRYSYSTEM_THREADTASK_H
|
||||
#pragma once
|
||||
|
||||
|
||||
#include <IThreadTask.h>
|
||||
#include <CryThread.h>
|
||||
#include <MultiThread_Containers.h>
|
||||
|
||||
#define MAIN_THREAD_INDEX 0
|
||||
|
||||
class CThreadTask_Thread;
|
||||
|
||||
|
||||
void MarkThisThreadForDebugging(const char* name);
|
||||
void UnmarkThisThreadFromDebugging();
|
||||
void UpdateFPExceptionsMaskForThreads();
|
||||
|
||||
|
||||
class CThreadTaskManager;
|
||||
///
|
||||
struct IThreadTaskRunnable
|
||||
{
|
||||
virtual ~IThreadTaskRunnable(){}
|
||||
virtual void Run() = 0;
|
||||
virtual void Cancel() = 0;
|
||||
};
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
class CThreadTask_Thread
|
||||
: public CryThread<IThreadTaskRunnable>
|
||||
, public IThreadTask_Thread
|
||||
{
|
||||
protected:
|
||||
void Init();
|
||||
public:
|
||||
CThreadTask_Thread(CThreadTaskManager* pTaskMgr, const char* sName, int nThreadIndex, int nProcessor, int nThreadPriority, ThreadPoolHandle poolHandle = -1);
|
||||
~CThreadTask_Thread();
|
||||
|
||||
// see IThreadTaskRunnable, CryThread<>
|
||||
void Run() override;
|
||||
void Cancel() override;
|
||||
|
||||
// see CryThread<>
|
||||
void Terminate() override;
|
||||
|
||||
// IThreadTask_Thread
|
||||
void AddTask(SThreadTaskInfo* pTaskInfo) override;
|
||||
void RemoveTask(SThreadTaskInfo* pTaskInfo) override;
|
||||
void RemoveAllTasks() override;
|
||||
void SingleUpdate() override;
|
||||
|
||||
void ChangeProcessor(int nProcessor);
|
||||
public:
|
||||
CThreadTaskManager* m_pTaskManager;
|
||||
string m_sThreadName;
|
||||
int m_nThreadIndex; // -1 means the thread is blocking
|
||||
int m_nProcessor;
|
||||
int m_nThreadPriority;
|
||||
|
||||
THREAD_HANDLE m_hThreadHandle;
|
||||
|
||||
// Tasks running on this thread.
|
||||
typedef CryMT::CLocklessPointerQueue<SThreadTaskInfo, stl::STLGlobalAllocator<SThreadTaskInfo> > Tasks;
|
||||
Tasks tasks;
|
||||
|
||||
// The task is being processing now
|
||||
SThreadTaskInfo* m_pProcessingTask;
|
||||
|
||||
CryEvent m_waitForTasks;
|
||||
|
||||
// Set to true when thread must stop.
|
||||
volatile bool bStopThread;
|
||||
volatile bool bRunning;
|
||||
|
||||
// handle of threads pool which this thread belongs to(if any)
|
||||
ThreadPoolHandle m_poolHandle;
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
class CThreadTaskManager
|
||||
: public IThreadTaskManager
|
||||
{
|
||||
private:
|
||||
typedef std::vector<CThreadTask_Thread*, stl::STLGlobalAllocator<CThreadTask_Thread*> > Threads;
|
||||
// note: this struct is auxilary and NOT thread-safe
|
||||
// it is only for internal use inside the task manager
|
||||
struct CThreadsPool
|
||||
{
|
||||
ThreadPoolHandle m_hHandle;
|
||||
Threads m_Threads;
|
||||
ThreadPoolDesc m_pDescription;
|
||||
const bool SetAffinity(const ThreadPoolAffinityMask AffinityMask);
|
||||
const bool operator < (const CThreadsPool& p) const { return m_hHandle < p.m_hHandle; }
|
||||
const bool operator == (const CThreadsPool& p) const { return m_hHandle == p.m_hHandle; }
|
||||
};
|
||||
|
||||
typedef std::vector<CThreadsPool> ThreadsPools;
|
||||
|
||||
public:
|
||||
CThreadTaskManager();
|
||||
~CThreadTaskManager();
|
||||
|
||||
void InitThreads();
|
||||
void CloseThreads();
|
||||
void StopAllThreads();
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// IThreadTaskManager
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
virtual void RegisterTask(IThreadTask* pTask, const SThreadTaskParams& options);
|
||||
virtual void UnregisterTask(IThreadTask* pTask);
|
||||
virtual void SetMaxThreadCount(int nMaxThreads);
|
||||
virtual void SetThreadName(threadID dwThreadId, const char* sThreadName);
|
||||
virtual const char* GetThreadName(threadID dwThreadId);
|
||||
virtual threadID GetThreadByName(const char* sThreadName);
|
||||
|
||||
// Thread pool framework
|
||||
virtual ThreadPoolHandle CreateThreadsPool(const ThreadPoolDesc& desc);
|
||||
virtual const bool DestroyThreadsPool(const ThreadPoolHandle& handle);
|
||||
virtual const bool GetThreadsPoolDesc(const ThreadPoolHandle handle, ThreadPoolDesc* pDesc) const;
|
||||
virtual const bool SetThreadsPoolAffinity(const ThreadPoolHandle handle, const ThreadPoolAffinityMask AffinityMask);
|
||||
|
||||
virtual void MarkThisThreadForDebugging(const char* name, bool bDump);
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// This is on update function of the main thread.
|
||||
void OnUpdate();
|
||||
|
||||
void AddSystemThread(threadID nThreadId);
|
||||
void RemoveSystemThread(threadID nThreadId);
|
||||
|
||||
// Balancing tasks in the pool between threads
|
||||
void BalanceThreadsPool(const ThreadPoolHandle& handle);
|
||||
void BalanceThreadInPool(CThreadTask_Thread* pFreeThread, Threads* pThreads = NULL);
|
||||
|
||||
private:
|
||||
void ScheduleTask(SThreadTaskInfo* pTaskInfo);
|
||||
void RescheduleTasks();
|
||||
private:
|
||||
|
||||
// User created threads pools
|
||||
mutable CryReadModifyLock m_threadsPoolsLock;
|
||||
ThreadsPools m_threadsPools;
|
||||
|
||||
// Physical threads available to system.
|
||||
Threads m_threads;
|
||||
|
||||
// Threads with single blocking task attached.
|
||||
Threads m_blockingThreads;
|
||||
|
||||
typedef CryMT::CLocklessPointerQueue<SThreadTaskInfo> Tasks;
|
||||
|
||||
Tasks m_unassignedTasks;
|
||||
|
||||
mutable CryCriticalSection m_threadNameLock;
|
||||
mutable CryCriticalSection m_threadRemove;
|
||||
typedef std::map<threadID, string> ThreadNames;
|
||||
ThreadNames m_threadNames;
|
||||
|
||||
mutable CryCriticalSection m_systemThreadsLock;
|
||||
std::vector<threadID> m_systemThreads;
|
||||
|
||||
// Max threads that can be executed at same time.
|
||||
int m_nMaxThreads;
|
||||
};
|
||||
|
||||
|
||||
#endif // CRYINCLUDE_CRYSYSTEM_THREADTASK_H
|
||||
@@ -1,46 +0,0 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "CrySystem_precompiled.h"
|
||||
#include <AzTest/AzTest.h>
|
||||
#include <Cry_Math.h>
|
||||
|
||||
namespace UnitTest
|
||||
{
|
||||
class CryMathTestFixture
|
||||
: public ::testing::Test
|
||||
{};
|
||||
|
||||
#if AZ_TRAIT_DISABLE_FAILED_MATH_TESTS
|
||||
TEST_F(CryMathTestFixture, DISABLED_InverserSqrt_HasAtLeast22BitsOfAccuracy)
|
||||
#else
|
||||
TEST_F(CryMathTestFixture, InverserSqrt_HasAtLeast22BitsOfAccuracy)
|
||||
#endif
|
||||
{
|
||||
float testFloat(0.336950600);
|
||||
const float result = isqrt_safe_tpl(testFloat * testFloat);
|
||||
const float epsilon = 0.00001f;
|
||||
EXPECT_NEAR(2.96779, result, epsilon);
|
||||
}
|
||||
|
||||
#if AZ_TRAIT_DISABLE_FAILED_MATH_TESTS
|
||||
TEST_F(CryMathTestFixture, DISABLED_SimdSqrt_HasAtLeast23BitsOfAccuracy)
|
||||
#else
|
||||
TEST_F(CryMathTestFixture, SimdSqrt_HasAtLeast23BitsOfAccuracy)
|
||||
#endif
|
||||
{
|
||||
float testFloat(3434.34839439);
|
||||
const float result = sqrt_tpl(testFloat);
|
||||
const float epsilon = 0.00001f;
|
||||
EXPECT_NEAR(58.60331, result, epsilon);
|
||||
}
|
||||
}
|
||||
@@ -1,141 +0,0 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
#include "CrySystem_precompiled.h"
|
||||
|
||||
#include <AzTest/AzTest.h>
|
||||
#include <AzCore/UnitTest/UnitTest.h>
|
||||
|
||||
#include <AzCore/Component/ComponentApplication.h>
|
||||
#include <AzCore/IO/SystemFile.h> // for max path decl
|
||||
#include <AzCore/std/parallel/thread.h>
|
||||
#include <AzCore/std/parallel/semaphore.h>
|
||||
#include <AzCore/std/functional.h> // for function<> in the find files callback.
|
||||
#include <AzFramework/IO/LocalFileIO.h>
|
||||
#include <AzFramework/Archive/ArchiveFileIO.h>
|
||||
#include <AzFramework/Archive/Archive.h>
|
||||
#include <AzFramework/Archive/INestedArchive.h>
|
||||
#include <ILevelSystem.h>
|
||||
|
||||
namespace CryPakUnitTests
|
||||
{
|
||||
|
||||
#if defined(AZ_PLATFORM_WINDOWS)
|
||||
|
||||
// Note: none of the below is really a unit test, its all basic feature tests
|
||||
// for critical functionality
|
||||
|
||||
class Integ_CryPakUnitTests
|
||||
: public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
bool IsPackValid(const char* path)
|
||||
{
|
||||
AZ::IO::IArchive* pak = gEnv->pCryPak;
|
||||
if (!pak)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!pak->OpenPack(path, AZ::IO::IArchive::FLAGS_PATH_REAL))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
pak->ClosePack(path);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(Integ_CryPakUnitTests, TestCryPakModTime)
|
||||
{
|
||||
AZ::IO::FileIOBase* fileIo = AZ::IO::FileIOBase::GetInstance();
|
||||
ASSERT_NE(nullptr, fileIo);
|
||||
|
||||
AZ::IO::IArchive* pak = gEnv->pCryPak;
|
||||
// repeat the following test multiple times, since timing (seconds) can affect it and it involves time!
|
||||
for (int iteration = 0; iteration < 10; ++iteration)
|
||||
{
|
||||
AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds{ 100 });
|
||||
|
||||
// helper paths and strings
|
||||
AZStd::string gameFolder = fileIo->GetAlias("@usercache@");
|
||||
|
||||
AZStd::string testFile = "unittest.bin";
|
||||
AZStd::string testFilePath = gameFolder + "\\" + testFile;
|
||||
AZStd::string testPak = "unittest.pak";
|
||||
AZStd::string testPakPath = gameFolder + "\\" + testPak;
|
||||
AZStd::string zipCmd = "-zip=" + testPakPath;
|
||||
|
||||
// delete test files in case they already exist
|
||||
fileIo->Remove(testFilePath.c_str());
|
||||
pak->ClosePack(testPakPath);
|
||||
fileIo->Remove(testPakPath.c_str());
|
||||
|
||||
// create a test file
|
||||
char data[] = "unittest";
|
||||
FILE* f = nullptr;
|
||||
azfopen(&f, testFilePath.c_str(), "wb");
|
||||
EXPECT_TRUE(f != nullptr); // file successfully opened for writing
|
||||
EXPECT_TRUE(fwrite(data, sizeof(char), sizeof(data), f) == sizeof(data)); // file written to successfully
|
||||
EXPECT_TRUE(fclose(f) == 0); // file closed successfully
|
||||
|
||||
AZ::IO::HandleType fDisk = pak->FOpen(testFilePath.c_str(), "rb");
|
||||
EXPECT_TRUE(fDisk > 0); // opened file on disk successfully
|
||||
uint64_t modTimeDisk = pak->GetModificationTime(fDisk); // high res mod time extracted from file on disk
|
||||
EXPECT_TRUE(pak->FClose(fDisk) == 0); // file closed successfully
|
||||
|
||||
// create a low res copy of disk file's mod time
|
||||
uint64_t absDiff, maxDiff = 20000000ul;
|
||||
uint16_t dosDate, dosTime;
|
||||
FILETIME ft;
|
||||
LARGE_INTEGER lt;
|
||||
|
||||
ft.dwHighDateTime = modTimeDisk >> 32;
|
||||
ft.dwLowDateTime = modTimeDisk & 0xFFFFFFFF;
|
||||
EXPECT_TRUE(FileTimeToDosDateTime(&ft, &dosDate, &dosTime) != FALSE); // converted to DOSTIME successfully
|
||||
ft.dwHighDateTime = 0;
|
||||
ft.dwLowDateTime = 0;
|
||||
EXPECT_TRUE(DosDateTimeToFileTime(dosDate, dosTime, &ft) != FALSE); // converted to FILETIME successfully
|
||||
lt.HighPart = ft.dwHighDateTime;
|
||||
lt.LowPart = ft.dwLowDateTime;
|
||||
uint64_t modTimeDiskLowRes = lt.QuadPart;
|
||||
|
||||
absDiff = modTimeDiskLowRes >= modTimeDisk ? modTimeDiskLowRes - modTimeDisk : modTimeDisk - modTimeDiskLowRes;
|
||||
EXPECT_LE(absDiff, maxDiff); // FILETIME (high res) and DOSTIME (low res) should be at most 2 seconds apart
|
||||
|
||||
gEnv->pResourceCompilerHelper->CallResourceCompiler(testFilePath.c_str(), zipCmd.c_str());
|
||||
EXPECT_EQ(AZ::IO::ResultCode::Success, fileIo->Remove(testFilePath.c_str())); // test file on disk deleted successfully
|
||||
|
||||
EXPECT_TRUE(pak->OpenPack(testPakPath)); // opened pak successfully
|
||||
|
||||
AZ::IO::HandleType fPak = pak->FOpen(testFilePath.c_str(), "rb");
|
||||
EXPECT_GT(fPak, 0); // file (in pak) opened correctly
|
||||
uint64_t modTimePak = pak->GetModificationTime(fPak); // low res mod time extracted from file in pak
|
||||
EXPECT_EQ(0, pak->FClose(fPak)); // file closed successfully
|
||||
|
||||
EXPECT_TRUE(pak->ClosePack(testPakPath)); // closed pak successfully
|
||||
EXPECT_EQ(AZ::IO::ResultCode::Success, fileIo->Remove(testPakPath.c_str())); // test pak file deleted successfully
|
||||
|
||||
absDiff = modTimePak >= modTimeDisk ? modTimePak - modTimeDisk : modTimeDisk - modTimePak;
|
||||
// compare mod times. They are allowed to be up to 2 seconds apart but no more
|
||||
EXPECT_LE(absDiff, maxDiff); // FILETIME (disk) and DOSTIME (pak) should be at most 2 seconds apart
|
||||
// note: Do not directly compare the disk time and pack time, the resolution drops the last digit off in some cases in pak
|
||||
// it only has a 2 second resolution. you may compare to make sure that the pak time is WITHIN 2 seconds (as above) but not equal.
|
||||
|
||||
// we depend on the fact that crypak is rounding up, instead of down
|
||||
EXPECT_GE(modTimePak, modTimeDisk);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
}
|
||||
@@ -783,12 +783,6 @@ void CUNIXConsole::KeyEnter()
|
||||
|
||||
if (pushCommand)
|
||||
{
|
||||
CSystem* pSystem = static_cast<CSystem*>(gEnv->pSystem);
|
||||
#if defined(CVARS_WHITELIST)
|
||||
ICVarsWhitelist* pCVarsWhitelist = pSystem->GetCVarsWhiteList();
|
||||
bool execute = (pCVarsWhitelist) ? pCVarsWhitelist->IsWhiteListed(m_InputLine, false) : true;
|
||||
if (execute)
|
||||
#endif // defined(CVARS_WHITELIST)
|
||||
{
|
||||
m_CommandQueue.push_back(m_InputLine);
|
||||
}
|
||||
|
||||
@@ -28,7 +28,6 @@
|
||||
#include <ISystem.h>
|
||||
#include <ILog.h>
|
||||
#include <IProcess.h>
|
||||
#include <IRemoteCommand.h>
|
||||
#include <IRenderAuxGeom.h>
|
||||
#include "ConsoleHelpGen.h" // CConsoleHelpGen
|
||||
|
||||
@@ -141,147 +140,6 @@ void Command_SetWaitFrames(IConsoleCmdArgs* pCmd)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
CNotificationNetworkConsole
|
||||
|
||||
*/
|
||||
|
||||
#include <INotificationNetwork.h>
|
||||
class CNotificationNetworkConsole
|
||||
: public INotificationNetworkListener
|
||||
{
|
||||
private:
|
||||
static const uint32 LENGTH_MAX = 256;
|
||||
static CNotificationNetworkConsole* s_pInstance;
|
||||
|
||||
public:
|
||||
static bool Initialize()
|
||||
{
|
||||
if (s_pInstance)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
INotificationNetwork* pNotificationNetwork = gEnv->pSystem->GetINotificationNetwork();
|
||||
if (!pNotificationNetwork)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
s_pInstance = new CNotificationNetworkConsole();
|
||||
pNotificationNetwork->ListenerBind("Command", s_pInstance);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void Shutdown()
|
||||
{
|
||||
if (!s_pInstance)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
delete s_pInstance;
|
||||
s_pInstance = NULL;
|
||||
}
|
||||
|
||||
static void Update()
|
||||
{
|
||||
if (s_pInstance)
|
||||
{
|
||||
s_pInstance->ProcessCommand();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
CNotificationNetworkConsole()
|
||||
{
|
||||
m_pConsole = NULL;
|
||||
|
||||
m_commandBuffer[0][0] = '\0';
|
||||
m_commandBuffer[1][0] = '\0';
|
||||
m_commandBufferIndex = 0;
|
||||
m_commandCriticalSection = ::CryCreateCriticalSection();
|
||||
}
|
||||
|
||||
~CNotificationNetworkConsole()
|
||||
{
|
||||
if (m_commandCriticalSection)
|
||||
{
|
||||
::CryDeleteCriticalSection(m_commandCriticalSection);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void ProcessCommand()
|
||||
{
|
||||
if (!ValidateConsole())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
char* command = NULL;
|
||||
::CryEnterCriticalSection(m_commandCriticalSection);
|
||||
if (*m_commandBuffer[m_commandBufferIndex])
|
||||
{
|
||||
command = m_commandBuffer[m_commandBufferIndex];
|
||||
}
|
||||
++m_commandBufferIndex &= 1;
|
||||
::CryLeaveCriticalSection(m_commandCriticalSection);
|
||||
|
||||
if (command)
|
||||
{
|
||||
m_pConsole->ExecuteString(command);
|
||||
*command = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
bool ValidateConsole()
|
||||
{
|
||||
if (m_pConsole)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!gEnv->pConsole)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
m_pConsole = gEnv->pConsole;
|
||||
return true;
|
||||
}
|
||||
|
||||
// INotificationNetworkListener
|
||||
public:
|
||||
void OnNotificationNetworkReceive(const void* pBuffer, size_t length)
|
||||
{
|
||||
if (!ValidateConsole())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (length > LENGTH_MAX)
|
||||
{
|
||||
length = LENGTH_MAX;
|
||||
}
|
||||
|
||||
::CryEnterCriticalSection(m_commandCriticalSection);
|
||||
::memcpy(m_commandBuffer[m_commandBufferIndex], pBuffer, length);
|
||||
m_commandBuffer[m_commandBufferIndex][LENGTH_MAX - 1] = '\0';
|
||||
::CryLeaveCriticalSection(m_commandCriticalSection);
|
||||
}
|
||||
|
||||
private:
|
||||
IConsole* m_pConsole;
|
||||
|
||||
char m_commandBuffer[2][LENGTH_MAX];
|
||||
size_t m_commandBufferIndex;
|
||||
void* m_commandCriticalSection;
|
||||
};
|
||||
|
||||
CNotificationNetworkConsole* CNotificationNetworkConsole::s_pInstance = NULL;
|
||||
|
||||
void ConsoleShow(IConsoleCmdArgs*)
|
||||
{
|
||||
gEnv->pConsole->ShowConsole(true);
|
||||
@@ -360,8 +218,6 @@ CXConsole::CXConsole()
|
||||
m_waitSeconds = 0.0f;
|
||||
m_blockCounter = 0;
|
||||
|
||||
CNotificationNetworkConsole::Initialize();
|
||||
|
||||
AzFramework::ConsoleRequestBus::Handler::BusConnect();
|
||||
AzFramework::CommandRegistrationBus::Handler::BusConnect();
|
||||
|
||||
@@ -380,8 +236,6 @@ CXConsole::~CXConsole()
|
||||
gEnv->pSystem->GetIRemoteConsole()->UnregisterListener(this);
|
||||
}
|
||||
|
||||
CNotificationNetworkConsole::Shutdown();
|
||||
|
||||
if (!m_mapVariables.empty())
|
||||
{
|
||||
while (!m_mapVariables.empty())
|
||||
@@ -1206,8 +1060,6 @@ void CXConsole::Update()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CNotificationNetworkConsole::Update();
|
||||
}
|
||||
|
||||
//enable this for now, we need it for profiling etc
|
||||
@@ -1750,11 +1602,6 @@ void CXConsole::AuditCVars(IConsoleCmdArgs* pArg)
|
||||
int devOnlyMask = VF_DEV_ONLY;
|
||||
int dediOnlyMask = VF_DEDI_ONLY;
|
||||
int excludeMask = cheatMask | constMask | readOnlyMask | devOnlyMask | dediOnlyMask;
|
||||
#if defined(CVARS_WHITELIST)
|
||||
CSystem* pSystem = static_cast<CSystem*>(gEnv->pSystem);
|
||||
ICVarsWhitelist* pCVarsWhitelist = pSystem->GetCVarsWhiteList();
|
||||
bool excludeWhitelist = true;
|
||||
#endif // defined(CVARS_WHITELIST)
|
||||
|
||||
if (numArgs > 1)
|
||||
{
|
||||
@@ -1787,13 +1634,6 @@ void CXConsole::AuditCVars(IConsoleCmdArgs* pArg)
|
||||
excludeMask &= ~dediOnlyMask;
|
||||
}
|
||||
|
||||
#if defined(CVARS_WHITELIST)
|
||||
if (azstricmp(arg, "whitelist") == 0)
|
||||
{
|
||||
excludeWhitelist = false;
|
||||
}
|
||||
#endif // defined(CVARS_WHITELIST)
|
||||
|
||||
--numArgs;
|
||||
}
|
||||
}
|
||||
@@ -1811,11 +1651,6 @@ void CXConsole::AuditCVars(IConsoleCmdArgs* pArg)
|
||||
int devOnlyFlags = (command.m_nFlags & devOnlyMask);
|
||||
int dediOnlyFlags = (command.m_nFlags & dediOnlyMask);
|
||||
bool shouldLog = ((cheatFlags | devOnlyFlags | dediOnlyFlags) == 0) || (((cheatFlags | devOnlyFlags | dediOnlyFlags) & ~excludeMask) != 0);
|
||||
#if defined(CVARS_WHITELIST)
|
||||
bool whitelisted = (pCVarsWhitelist) ? pCVarsWhitelist->IsWhiteListed(command.m_sName, true) : true;
|
||||
shouldLog &= (!whitelisted || (whitelisted & !excludeWhitelist));
|
||||
#endif // defined(CVARS_WHITELIST)
|
||||
|
||||
if (shouldLog)
|
||||
{
|
||||
CryLogAlways("[CVARS]: [COMMAND] %s%s%s%s%s",
|
||||
@@ -1823,11 +1658,7 @@ void CXConsole::AuditCVars(IConsoleCmdArgs* pArg)
|
||||
(cheatFlags != 0) ? " [VF_CHEAT]" : "",
|
||||
(devOnlyFlags != 0) ? " [VF_DEV_ONLY]" : "",
|
||||
(dediOnlyFlags != 0) ? " [VF_DEDI_ONLY]" : "",
|
||||
#if defined(CVARS_WHITELIST)
|
||||
(whitelisted == true) ? " [WHITELIST]" : ""
|
||||
#else
|
||||
""
|
||||
#endif // defined(CVARS_WHITELIST)
|
||||
);
|
||||
++commandCount;
|
||||
}
|
||||
@@ -1844,11 +1675,6 @@ void CXConsole::AuditCVars(IConsoleCmdArgs* pArg)
|
||||
int devOnlyFlags = (flags & devOnlyMask);
|
||||
int dediOnlyFlags = (flags & dediOnlyMask);
|
||||
bool shouldLog = ((cheatFlags | constFlags | readOnlyFlags | devOnlyFlags | dediOnlyFlags) == 0) || (((cheatFlags | constFlags | readOnlyFlags | devOnlyFlags | dediOnlyFlags) & ~excludeMask) != 0);
|
||||
#if defined(CVARS_WHITELIST)
|
||||
bool whitelisted = (pCVarsWhitelist) ? pCVarsWhitelist->IsWhiteListed(pVariable->GetName(), true) : true;
|
||||
shouldLog &= (!whitelisted || (whitelisted & !excludeWhitelist));
|
||||
#endif // defined(CVARS_WHITELIST)
|
||||
|
||||
if (shouldLog)
|
||||
{
|
||||
CryLogAlways("[CVARS]: [VARIABLE] %s%s%s%s%s%s%s",
|
||||
@@ -1858,11 +1684,7 @@ void CXConsole::AuditCVars(IConsoleCmdArgs* pArg)
|
||||
(readOnlyFlags != 0) ? " [VF_READONLY]" : "",
|
||||
(devOnlyFlags != 0) ? " [VF_DEV_ONLY]" : "",
|
||||
(dediOnlyFlags != 0) ? " [VF_DEDI_ONLY]" : "",
|
||||
#if defined(CVARS_WHITELIST)
|
||||
(whitelisted == true) ? " [WHITELIST]" : ""
|
||||
#else
|
||||
""
|
||||
#endif // defined(CVARS_WHITELIST)
|
||||
);
|
||||
++cvarCount;
|
||||
}
|
||||
@@ -2521,11 +2343,6 @@ const char* CXConsole::ProcessCompletion(const char* szInputBuffer)
|
||||
}
|
||||
}
|
||||
//try to search in command list
|
||||
|
||||
#if defined(CVARS_WHITELIST)
|
||||
CSystem* pSystem = static_cast<CSystem*>(gEnv->pSystem);
|
||||
ICVarsWhitelist* pCVarsWhitelist = pSystem->GetCVarsWhiteList();
|
||||
#endif // defined(CVARS_WHITELIST)
|
||||
bool bArgumentAutoComplete = false;
|
||||
std::vector<string> matches;
|
||||
|
||||
@@ -2566,10 +2383,6 @@ const char* CXConsole::ProcessCompletion(const char* szInputBuffer)
|
||||
string cmd = string(sVar) + " " + pArgumentAutoComplete->GetValue(i);
|
||||
if (_strnicmp(m_sPrevTab.c_str(), cmd.c_str(), m_sPrevTab.length()) == 0)
|
||||
{
|
||||
#if defined(CVARS_WHITELIST)
|
||||
bool whitelisted = (pCVarsWhitelist) ? pCVarsWhitelist->IsWhiteListed(cmd, true) : true;
|
||||
if (whitelisted)
|
||||
#endif // defined(CVARS_WHITELIST)
|
||||
{
|
||||
bArgumentAutoComplete = true;
|
||||
matches.push_back(cmd);
|
||||
@@ -2591,10 +2404,6 @@ const char* CXConsole::ProcessCompletion(const char* szInputBuffer)
|
||||
{
|
||||
if (_strnicmp(m_sPrevTab.c_str(), itrCmds->first.c_str(), m_sPrevTab.length()) == 0)
|
||||
{
|
||||
#if defined(CVARS_WHITELIST)
|
||||
bool whitelisted = (pCVarsWhitelist) ? pCVarsWhitelist->IsWhiteListed(itrCmds->first, true) : true;
|
||||
if (whitelisted)
|
||||
#endif // defined(CVARS_WHITELIST)
|
||||
{
|
||||
matches.push_back((char* const)itrCmds->first.c_str());
|
||||
}
|
||||
@@ -2614,10 +2423,6 @@ const char* CXConsole::ProcessCompletion(const char* szInputBuffer)
|
||||
{//if(itrVars->first.compare(0,m_sPrevTab.length(),m_sPrevTab)==0)
|
||||
if (_strnicmp(m_sPrevTab.c_str(), itrVars->first, m_sPrevTab.length()) == 0)
|
||||
{
|
||||
#if defined(CVARS_WHITELIST)
|
||||
bool whitelisted = (pCVarsWhitelist) ? pCVarsWhitelist->IsWhiteListed(itrVars->first, true) : true;
|
||||
if (whitelisted)
|
||||
#endif // defined(CVARS_WHITELIST)
|
||||
{
|
||||
matches.push_back((char* const)itrVars->first);
|
||||
}
|
||||
@@ -2991,12 +2796,6 @@ void CXConsole::ExecuteInputBuffer()
|
||||
|
||||
AddCommandToHistory(sTemp.c_str());
|
||||
|
||||
#if defined(CVARS_WHITELIST)
|
||||
CSystem* pSystem = static_cast<CSystem*>(gEnv->pSystem);
|
||||
ICVarsWhitelist* pCVarsWhitelist = pSystem->GetCVarsWhiteList();
|
||||
bool execute = (pCVarsWhitelist) ? pCVarsWhitelist->IsWhiteListed(sTemp, false) : true;
|
||||
if (execute)
|
||||
#endif // defined(CVARS_WHITELIST)
|
||||
{
|
||||
ExecuteStringInternal(sTemp.c_str(), true); // from console
|
||||
}
|
||||
|
||||
@@ -1809,8 +1809,6 @@ XmlNodeRef XmlParserImp::ParseFile(const char* filename, XmlString& errorString,
|
||||
ParseEnd();
|
||||
}
|
||||
|
||||
SYNCHRONOUS_LOADING_TICK();
|
||||
|
||||
delete [] pFileContents;
|
||||
|
||||
return root;
|
||||
|
||||
@@ -19,14 +19,11 @@ set(FILES
|
||||
ConsoleBatchFile.cpp
|
||||
ConsoleHelpGen.cpp
|
||||
CryAsyncMemcpy.cpp
|
||||
DebugCallStack.cpp
|
||||
GeneralMemoryHeap.cpp
|
||||
HandlerBase.cpp
|
||||
IDebugCallStack.cpp
|
||||
AsyncPakManager.cpp
|
||||
Log.cpp
|
||||
SystemRender.cpp
|
||||
NotificationNetwork.cpp
|
||||
PhysRenderer.cpp
|
||||
ResourceManager.cpp
|
||||
ServerHandler.cpp
|
||||
@@ -36,7 +33,6 @@ set(FILES
|
||||
SystemCFG.cpp
|
||||
SystemEventDispatcher.cpp
|
||||
SystemInit.cpp
|
||||
SystemScheduler.cpp
|
||||
SystemWin32.cpp
|
||||
Timer.cpp
|
||||
UnixConsole.cpp
|
||||
@@ -52,16 +48,9 @@ set(FILES
|
||||
ServerHandler.h
|
||||
ServerThrottle.h
|
||||
SyncLock.h
|
||||
SystemScheduler.h
|
||||
UnixConsole.h
|
||||
SystemInit.h
|
||||
Serialization/MemoryReader.h
|
||||
XML/ReadWriteXMLSink.h
|
||||
Serialization/ArchiveHost.h
|
||||
Serialization/MemoryWriter.h
|
||||
Serialization/JSONIArchive.h
|
||||
Serialization/JSONOArchive.h
|
||||
Serialization/BinArchive.h
|
||||
AZCrySystemInitLogSink.h
|
||||
AZCoreLogSink.h
|
||||
CmdLine.h
|
||||
@@ -69,12 +58,8 @@ set(FILES
|
||||
ConsoleBatchFile.h
|
||||
ConsoleHelpGen.h
|
||||
CryWaterMark.h
|
||||
DebugCallStack.h
|
||||
GeneralMemoryHeap.h
|
||||
IDebugCallStack.h
|
||||
IThreadConfigManager.h
|
||||
Log.h
|
||||
NotificationNetwork.h
|
||||
resource.h
|
||||
SimpleStringPool.h
|
||||
CrySystem_precompiled.h
|
||||
@@ -113,42 +98,18 @@ set(FILES
|
||||
XML/WriteXMLSource.cpp
|
||||
ZipFile.h
|
||||
ZipFileFormat_info.h
|
||||
ProfileLogSystem.cpp
|
||||
Sampler.cpp
|
||||
ProfileLogSystem.h
|
||||
Sampler.h
|
||||
LocalizedStringManager.cpp
|
||||
LocalizedStringManager.h
|
||||
CryThreadUtil_win32_thread.h
|
||||
ThreadInfo.cpp
|
||||
ThreadInfo.h
|
||||
ThreadTask.h
|
||||
ThreadTask.cpp
|
||||
ThreadConfigManager.h
|
||||
ThreadConfigManager.cpp
|
||||
SystemThreading.cpp
|
||||
ExtensionSystem/CryFactoryRegistryImpl.cpp
|
||||
ExtensionSystem/CryFactoryRegistryImpl.h
|
||||
ExtensionSystem/TestCases/TestExtensions.cpp
|
||||
ExtensionSystem/TestCases/TestExtensions.h
|
||||
ZLibCompressor.cpp
|
||||
ZLibCompressor.h
|
||||
SoftCode/SoftCodeMgr.cpp
|
||||
SoftCode/SoftCodeMgr.h
|
||||
Huffman.cpp
|
||||
Huffman.h
|
||||
RemoteConsole/RemoteConsole.cpp
|
||||
RemoteConsole/RemoteConsole.h
|
||||
RemoteConsole/RemoteConsole_impl.inl
|
||||
RemoteConsole/RemoteConsole_none.inl
|
||||
ServiceNetwork.cpp
|
||||
ServiceNetwork.h
|
||||
RemoteCommand.cpp
|
||||
RemoteCommand.h
|
||||
RemoteCommandHelpers.cpp
|
||||
RemoteCommandHelpers.h
|
||||
RemoteCommandServer.cpp
|
||||
RemoteCommandClient.cpp
|
||||
ZLibDecompressor.h
|
||||
ZLibDecompressor.cpp
|
||||
LZ4Decompressor.h
|
||||
@@ -165,17 +126,6 @@ set(FILES
|
||||
ViewSystem/ViewSystem.h
|
||||
ZStdDecompressor.h
|
||||
ZStdDecompressor.cpp
|
||||
Serialization/ArchiveHost.cpp
|
||||
Serialization/BinArchive.cpp
|
||||
Serialization/JSONIArchive.cpp
|
||||
Serialization/JSONOArchive.cpp
|
||||
Serialization/MemoryReader.cpp
|
||||
Serialization/MemoryWriter.cpp
|
||||
Serialization/Token.h
|
||||
Serialization/XmlIArchive.cpp
|
||||
Serialization/XmlIArchive.h
|
||||
Serialization/XmlOArchive.cpp
|
||||
Serialization/XmlOArchive.h
|
||||
StreamEngine/StreamAsyncFileRequest.cpp
|
||||
StreamEngine/StreamAsyncFileRequest_Jobs.cpp
|
||||
StreamEngine/StreamEngine.cpp
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
|
||||
set(FILES
|
||||
Components/MathConversionTests.cpp
|
||||
Serialization/Test_ArchiveHost.cpp
|
||||
Tests/Test_CLog.cpp
|
||||
Tests/Test_CommandRegistration.cpp
|
||||
Tests/Test_CryPrimitives.cpp
|
||||
@@ -19,7 +18,5 @@ set(FILES
|
||||
Tests/Test_Localization.cpp
|
||||
Tests/test_Main.cpp
|
||||
Tests/test_MaterialUtils.cpp
|
||||
UnitTests/CryMathTests.cpp
|
||||
UnitTests/CryPakUnitTests.cpp
|
||||
DllMain.cpp
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user