389 lines
10 KiB
C++
389 lines
10 KiB
C++
#include "StdAfx.h"
|
||
#include "ScriptTimeCall.h"
|
||
#include "../../base/Container.hpp"
|
||
//定义BootCall文件头标志
|
||
const FileHeaders::FILEIDENT CScriptTimeCallManager::ScriptBootCallFileHeader::FileIdent =
|
||
{ MAKEFOURCC('S', 'B', 'C', 0) };
|
||
//定义BootCall文件版本号
|
||
const FileHeaders::FILEVERSION CScriptTimeCallManager::ScriptBootCallFileHeader::FileVersion =
|
||
{ MAKEFOURCC(1, 11, 3, 8) };
|
||
|
||
|
||
using namespace wylib::stream;
|
||
using namespace FDOP;
|
||
|
||
CScriptTimeCallManager::CScriptTimeCallManager()
|
||
:Inherited()
|
||
{
|
||
m_nMarkedRemoveCount = 0;
|
||
}
|
||
|
||
CScriptTimeCallManager::~CScriptTimeCallManager()
|
||
{
|
||
RemoveAll(FALSE);
|
||
ClearBootCallList();
|
||
}
|
||
|
||
VOID STDCALL CScriptTimeCallManager::ScriptTimeCallDispatcher(CScriptTimeCallManager *lpManager, CList<ScriptCallInfo>::NodeType *pCallInfoNode, INT_PTR nCount)
|
||
{
|
||
ScriptCallInfo &sc = *pCallInfoNode;
|
||
sc.pNPC->GetScript().Call(sc.sFn, sc.args, lpManager->m_SRetList, 0);
|
||
}
|
||
|
||
static ULONGLONG GetNextRunTime(LPCSTR sNextRunDateTime, UINT dwSecInterval)
|
||
{
|
||
INT y, m, d, h, n, s;
|
||
y = m = d = h = n = s = 0;
|
||
|
||
sscanf(sNextRunDateTime, "%d-%d-%d %d:%d:%d", &y, &m, &d, &h, &n, &s);
|
||
CMiniDateTime tRun, tNow = CMiniDateTime::now();
|
||
|
||
tRun.encode(y, m, d, h, n, s);
|
||
while (tNow.tv > tRun.tv)
|
||
{
|
||
tRun += dwSecInterval;
|
||
}
|
||
return tRun;
|
||
}
|
||
|
||
HANDLE CScriptTimeCallManager::RegisterTimeCall(CNpc *pNPC, LPCSTR sFnName, LPCSTR sNextCall,
|
||
UINT dwSecInterval, bool boCallOnNextBoot,const CScriptValueList &args)
|
||
{
|
||
//如果函数已经被注册,则返回空
|
||
if (GetTimeCall(pNPC, sFnName) || !dwSecInterval)
|
||
return NULL;
|
||
|
||
//添加注册信息到自身记录列表
|
||
ScriptCallInfo sc1;
|
||
CList<ScriptCallInfo>::NodeType *pNode = m_CallList.linkAtLast(sc1);
|
||
|
||
//为定时调用记录对象赋值
|
||
ScriptCallInfo &sc = *pNode;
|
||
ZeroMemory(&sc, sizeof(sc));
|
||
sc.pNPC = pNPC;
|
||
_asncpytA(sc.sFn, sFnName);
|
||
_asncpytA(sc.sNPCName, pNPC->GetEntityName());
|
||
_asncpytA(sc.sSceneName, pNPC->GetScene()->GetSceneName());
|
||
sc.boBootCall = boCallOnNextBoot;
|
||
sc.boMarkedRemove = FALSE;
|
||
sc.args = args;
|
||
ULONGLONG lNextCall = GetNextRunTime(sNextCall, dwSecInterval);
|
||
|
||
//向定时调用父类中注册调用
|
||
sc.hCall = AddTimeCall(this, (CTimeCaller::TimedCallBack)ScriptTimeCallDispatcher, pNode, dwSecInterval, lNextCall);
|
||
if (!sc.hCall)
|
||
{
|
||
sc.~ScriptCallInfo();
|
||
m_CallList.remove(pNode);
|
||
pNode = NULL;
|
||
}
|
||
return pNode;
|
||
}
|
||
|
||
bool CScriptTimeCallManager::UnregisterTimeCall(HANDLE hTimeCall)
|
||
{
|
||
CList<ScriptCallInfo>::Iterator it(m_CallList);
|
||
CList<ScriptCallInfo>::NodeType *pNode;
|
||
|
||
for (pNode = it.first(); pNode; pNode = it.next())
|
||
{
|
||
if (pNode == hTimeCall)
|
||
{
|
||
ScriptCallInfo &sc = *pNode;
|
||
//如果需要在下次启动的时候检查调用则存储此数据
|
||
if (sc.boBootCall)
|
||
AddToBootCall(sc);
|
||
//移除定时调用对象
|
||
RemoveTimeCall(sc.hCall);
|
||
//标记为移除
|
||
sc.boMarkedRemove = TRUE;
|
||
//增加移除标记数
|
||
m_nMarkedRemoveCount++;
|
||
return true;
|
||
}
|
||
}
|
||
return false;
|
||
}
|
||
|
||
HANDLE CScriptTimeCallManager::GetTimeCall(CNpc *pNPC, LPCSTR sFnName)
|
||
{
|
||
CList<ScriptCallInfo>::Iterator it(m_CallList);
|
||
CList<ScriptCallInfo>::NodeType *pNode;
|
||
|
||
for (pNode = it.first(); pNode; pNode = it.next())
|
||
{
|
||
ScriptCallInfo &sc = *pNode;
|
||
if (!sc.boMarkedRemove)
|
||
{
|
||
if (sc.pNPC == pNPC || !pNPC)
|
||
{
|
||
if (strcmp(sc.sFn, sFnName) == 0)
|
||
{
|
||
return pNode;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
return NULL;
|
||
}
|
||
|
||
INT_PTR CScriptTimeCallManager::Run(ULONGLONG lRunTickLimit /* = 0 */)
|
||
{
|
||
SF_TIME_CHECK();
|
||
DECLARE_TIME_PROF("CScriptTimeCallManager::Run");
|
||
INT_PTR nRet = Inherited::Run(lRunTickLimit);
|
||
|
||
if (m_nMarkedRemoveCount > 0)
|
||
{
|
||
RemoveAll(TRUE);
|
||
}
|
||
return nRet;
|
||
}
|
||
|
||
VOID CScriptTimeCallManager::RemoveAll(BOOL boJustRemoveMarked)
|
||
{
|
||
CList<ScriptCallInfo>::Iterator it(m_CallList);
|
||
CList<ScriptCallInfo>::NodeType *pNode;
|
||
|
||
for (pNode = it.first(); pNode; pNode = it.next())
|
||
{
|
||
ScriptCallInfo &sc = *pNode;
|
||
if (boJustRemoveMarked && sc.boMarkedRemove)
|
||
{
|
||
sc.~ScriptCallInfo();
|
||
it.remove(pNode);
|
||
}
|
||
}
|
||
m_nMarkedRemoveCount = 0;
|
||
}
|
||
|
||
VOID CScriptTimeCallManager::AddToBootCall(const ScriptCallInfo& callInfo)
|
||
{
|
||
ScriptBootCallData bc1;
|
||
CList<ScriptBootCallData>::NodeType *pNode = m_BootCallList.linkAtLast(bc1);
|
||
|
||
ScriptBootCallData &bc = *pNode; //拷贝场景名称、NPC名称、函数名称信息
|
||
ZeroMemory(&bc, sizeof(bc));
|
||
_asncpytA(bc.sSceneName, callInfo.sSceneName);
|
||
_asncpytA(bc.sNPCName, callInfo.sNPCName);
|
||
_asncpytA(bc.sFn, callInfo.sFn);
|
||
|
||
//拷贝参数表
|
||
bc.args = callInfo.args;
|
||
|
||
//计算下次调用的CMiniDateTime类型的时间以及调用周期
|
||
ULONGLONG lInterval, lNextCallTick;
|
||
lInterval = GetCallInterval(callInfo.hCall, &lNextCallTick);
|
||
bc.dwSecInterval = (DWORD)lInterval;
|
||
bc.nNextCall = (int)lNextCallTick;
|
||
}
|
||
|
||
VOID CScriptTimeCallManager::ClearBootCallList()
|
||
{
|
||
/*CList<ScriptBootCallData>::NodeType *pNode;
|
||
CList<ScriptBootCallData>::Iterator it(m_BootCallList);*/
|
||
|
||
// 此地方会造成多次析构。m_BootCallList.clear()自身会析构对象
|
||
////循环调用析构函数
|
||
//for (pNode = it.first(); pNode; pNode = it.next())
|
||
//{
|
||
// ScriptBootCallData &bc = *pNode;
|
||
// bc.~ScriptBootCallData();
|
||
//}
|
||
|
||
m_BootCallList.clear();
|
||
}
|
||
|
||
INT_PTR CScriptTimeCallManager::RunBootCalls()
|
||
{
|
||
CNpc *pNpc;
|
||
CScene *pScene;
|
||
CFuBen *pFb;
|
||
INT_PTR Result = 0;
|
||
CMiniDateTime tNextCall;
|
||
CMiniDateTime tNow;
|
||
CFuBenManager *pFbMgr = GetLogicServer()->GetLogicEngine()->GetFuBenMgr();
|
||
CList<ScriptBootCallData>::NodeType *pNode;
|
||
CList<ScriptBootCallData>::Iterator it(m_BootCallList);
|
||
|
||
tNow = CMiniDateTime::now();
|
||
//必须按顺序检查和调用BootCall,否则可能违背脚本中带有先后关系的调用逻辑顺序
|
||
for (pNode = it.first(); pNode; pNode = it.next())
|
||
{
|
||
ScriptBootCallData &bc = *pNode;
|
||
if (tNow.tv >= bc.nNextCall.tv)
|
||
{
|
||
//查找NPC所在场景对象
|
||
if (!pFbMgr->GetFbPtrBySceneName(bc.sSceneName, pFb, pScene))
|
||
{
|
||
OutputMsg(rmWaning, _T("can not call BootCall[%s-%s:%s] scene was removed"),
|
||
bc.sSceneName, bc.sNPCName, bc.sFn);
|
||
continue;
|
||
}
|
||
//在场景中查找NPC对象
|
||
if ( !(pNpc = pScene->GetNpc(bc.sNPCName)) )
|
||
{
|
||
OutputMsg(rmWaning, _T("can not call BootCall[%s-%s:%s] NPC was removed"),
|
||
bc.sSceneName, bc.sNPCName, bc.sFn);
|
||
continue;
|
||
}
|
||
//检查NPC脚本中是否还存在此函数
|
||
if ( !pNpc->GetScript().FunctionExists(bc.sFn) )
|
||
{
|
||
OutputMsg(rmWaning, _T("can not call BootCall[%s-%s:%s] function does not exists any more"),
|
||
bc.sSceneName, bc.sNPCName, bc.sFn);
|
||
continue;
|
||
}
|
||
//循环调用脚本
|
||
tNextCall = bc.nNextCall;
|
||
do
|
||
{
|
||
tNextCall.tv += bc.dwSecInterval;
|
||
pNpc->GetScript().Call(bc.sFn, bc.args, m_SRetList, 0);
|
||
}
|
||
while (tNow.tv >= tNextCall.tv);
|
||
Result++;
|
||
}
|
||
}
|
||
ClearBootCallList();//清空列表以防止重复调用
|
||
return Result;
|
||
}
|
||
|
||
INT_PTR CScriptTimeCallManager::LoadBootCalls(LPCTSTR sFilePath)
|
||
{
|
||
if (!FileExists(sFilePath))
|
||
return 0;
|
||
|
||
ScriptBootCallFileHeader* pHdr;
|
||
CMemoryStream ms;
|
||
if (ms.loadFromFile(sFilePath) < sizeof(pHdr))
|
||
return -1;
|
||
|
||
pHdr = (ScriptBootCallFileHeader*)ms.getMemory();
|
||
if(!pHdr)
|
||
{
|
||
return 0;
|
||
}
|
||
//验证文件是否有效
|
||
if (pHdr->ident.uIdent != ScriptBootCallFileHeader::FileIdent.uIdent)
|
||
{
|
||
OutputMsg(rmError, _T("invalid BootCall file"));
|
||
return -2;
|
||
}
|
||
if (pHdr->version.uVersion != ScriptBootCallFileHeader::FileVersion.uVersion)
|
||
{
|
||
OutputMsg(rmError, _T("invalid BootCall file vesion %d.%d.%d.%d"),
|
||
pHdr->version.p.v, pHdr->version.p.y, pHdr->version.p.m, pHdr->version.p.d);
|
||
return -3;
|
||
}
|
||
if (pHdr->dwSizeData != sizeof(ScriptBootCallData))
|
||
{
|
||
OutputMsg(rmError, _T("BootCall data structure has been modified"));
|
||
return -4;
|
||
}
|
||
if (pHdr->dwDataCRC32 != ~CRC32Update(0xFFFFFFFF, pHdr + 1, pHdr->dwDataSize))
|
||
{
|
||
OutputMsg(rmError, _T("BootCall data CRC check failure"));
|
||
return -5;
|
||
}
|
||
|
||
ClearBootCallList();
|
||
|
||
//读取BootCall数据
|
||
if (pHdr->dwNumCalls > 0)
|
||
{
|
||
ScriptBootCallData bcd;
|
||
|
||
ms.setPosition(sizeof(*pHdr));
|
||
for (INT_PTR i=pHdr->dwNumCalls-1; i>-1; --i)
|
||
{
|
||
CLinkedNode<ScriptBootCallData> *pNode = m_BootCallList.linkAtLast(bcd);
|
||
ScriptBootCallData& bc = *pNode;
|
||
ZeroMemory(&bc, sizeof(bc));
|
||
bc.loadFromStream(ms);
|
||
}
|
||
}
|
||
|
||
return pHdr->dwNumCalls;
|
||
}
|
||
|
||
INT_PTR CScriptTimeCallManager::SaveBootCalls(LPCTSTR sFilePath)
|
||
{
|
||
TCHAR sSavePath[1024];
|
||
|
||
//获取保存文件的目录路径,如果目录路径字符长度超出缓存长度则报错
|
||
if ( ExtractFileDirectory(sFilePath, sSavePath, ArrayCount(sSavePath)) >= ArrayCount(sSavePath) )
|
||
{
|
||
OutputMsg(rmError, _T("unable to save BootCall Data to %s, path to long"), sFilePath);
|
||
return -1;
|
||
}
|
||
//逐层判断目录是否存在,如果不存在则创建
|
||
if ( !DeepCreateDirectory(sSavePath) )
|
||
{
|
||
OutputError(GetLastError(), _T("unable to create BootCall directory %s "), sSavePath);
|
||
return -2;
|
||
}
|
||
|
||
ScriptBootCallFileHeader hdr;
|
||
CMemoryStream ms;
|
||
|
||
ZeroMemory(&hdr, sizeof(hdr));
|
||
hdr.ident = ScriptBootCallFileHeader::FileIdent;
|
||
hdr.version = ScriptBootCallFileHeader::FileVersion;
|
||
hdr.dwNumCalls = (DWORD)m_BootCallList.count();
|
||
hdr.dwSizeData = sizeof(ScriptBootCallData);
|
||
ms.setSize(sizeof(hdr));
|
||
ms.setPosition(sizeof(hdr));
|
||
|
||
//写入数据
|
||
CList<ScriptBootCallData>::NodeType *pNode;
|
||
CList<ScriptBootCallData>::Iterator it(m_BootCallList);
|
||
for (pNode = it.first(); pNode; pNode = it.next())
|
||
{
|
||
ScriptBootCallData &bc = *pNode;
|
||
bc.saveToStream(ms);
|
||
}
|
||
//计算数据断CRC
|
||
hdr.dwDataSize = (DWORD)ms.getSize() - sizeof(hdr);
|
||
if (hdr.dwDataSize == 0)
|
||
{
|
||
hdr.dwDataSize = 0;
|
||
}
|
||
else
|
||
{
|
||
hdr.dwDataCRC32 = ~CRC32Update(0xFFFFFFFF, (LPCSTR)ms.getMemory() + sizeof(hdr), hdr.dwDataSize);
|
||
}
|
||
//写入文件头
|
||
memcpy(ms.getMemory(), &hdr, sizeof(hdr));
|
||
|
||
ms.saveToFile(sFilePath);
|
||
return hdr.dwNumCalls;
|
||
}
|
||
|
||
ULONGLONG CScriptTimeCallManager::GetCurrentTick()
|
||
{
|
||
return GetGlobalLogicEngine()->getMiniDateTime();
|
||
}
|
||
|
||
bool CScriptTimeCallManager::ScriptBootCallData::loadFromStream(wylib::stream::CBaseStream &stm)
|
||
{
|
||
stm.read(sSceneName, sizeof(sSceneName));
|
||
stm.read(sNPCName, sizeof(sNPCName));
|
||
stm.read(sFn, sizeof(sFn));
|
||
stm.read(&nNextCall, sizeof(nNextCall));
|
||
stm.read(&dwSecInterval, sizeof(dwSecInterval));
|
||
args.loadFromStream(stm);
|
||
return true;
|
||
}
|
||
|
||
void CScriptTimeCallManager::ScriptBootCallData::saveToStream(wylib::stream::CBaseStream &stm)
|
||
{
|
||
stm.write(sSceneName, sizeof(sSceneName));
|
||
stm.write(sNPCName, sizeof(sNPCName));
|
||
stm.write(sFn, sizeof(sFn));
|
||
stm.write(&nNextCall, sizeof(nNextCall));
|
||
stm.write(&dwSecInterval, sizeof(dwSecInterval));
|
||
args.saveToStream(stm);
|
||
}
|
||
|