Files
Sun1602/Server/AgentServer/Zone/SSQ/SSQUpdateCtrl.cpp
T
2022-10-26 12:25:11 +08:00

1179 lines
40 KiB
C++

#include "stdafx.h"
#include <PacketStruct_AG.h>
#include <PacketStruct_AW.h>
#include <UserSessions/User.h>
#include <UserSessions/UserManager.h>
#include <ServerSessions/ServerSessionManager.h>
#include <ServerSessions/BattleServerSession.h>
#include <ServerSessions/FieldServerSession.h>
#include <Zone/DungeonSSQ.h>
#include <Zone/ZoneManager.h>
#include <ssq/SSQInfoParser.h>
//==================================================================================================
// for debugging control
#define __WAVERIX_LOG_FORMAT_0S(msg) \
{ SUNLOG(eCRITICAL_LOG, "[Func:%s][Line:%3u] %s", __FUNCTION__, __LINE__, msg); }
#define __WAVERIX_LOG_FORMAT_0S1N(msg, value) \
{ SUNLOG(eCRITICAL_LOG, "[Func:%s][Line:%3u] %s (%u)", __FUNCTION__, __LINE__, msg, value); }
#ifdef _DEBUG
#define WAVERIX_DEBUG_LOG_0S(msg) __WAVERIX_LOG_FORMAT_0S(msg)
#define WAVERIX_DEBUG_LOG_0S1N(msg, value) __WAVERIX_LOG_FORMAT_0S1N(msg, value)
#else
#define WAVERIX_DEBUG_LOG_0S(msg)
#define WAVERIX_DEBUG_LOG_0S1N(msg, value)
#endif
//
//==================================================================================================
//
//
#include <Zone/WarControlManager.h>
#include "ISSQHandler.h"
#include "SSQControlInfos.hxx"
#include "SSQUpdateCtrl.hxx"
#include "SSQPacketProcess.hxx"
#include "SSQControlManagerImpl.hxx"
//==================================================================================================
// <SSQUpdateCtrl_xxx><Implementation>
//
VOID
SSQUpdateCtrl::MigrationDefault(const SSQCODE CodeOfSSQ,
SSQControlManagerImpl* const pMgrImpl,
SSQMsgRouterInAgent* const pMsgParser,
SSQInfo* const pSSQInfo)
{
m_CodeOfSSQ = CodeOfSSQ;
m_CurType = SSQControlInfo::TT_MAX;
m_Timer.Stop();
m_pManager = pMgrImpl;
m_pMsgRouter= pMsgParser;
m_pSSQInfo = pSSQInfo;
m_pSSQField = 0;
m_ControlField = RCS_NONE;
m_ProcedIndex = 0;
m_IsFastTrack = 0;
if(m_pMsgRouter)
m_pMsgRouter->SetUpdateCtrl(this);
};
VOID
SSQUpdateCtrl::RegisterUpdateCtrl(SSQUpdateCtrl* const pTarget)
{
SSQControlManagerImpl* const pImpl = pTarget->m_pManager;
pImpl->_RegisterUpdateCtrl(pTarget->m_CodeOfSSQ, pTarget);
}
BOOL
SSQUpdateCtrl::UpdateMsg(const curtime_info& tmInfo)
{
__UNUSED(tmInfo);
if(m_pMsgRouter->GetUpdateCtrl())
m_pMsgRouter->Update(m_CodeOfSSQ);
return TRUE;
}
VOID
SSQUpdateCtrl::RecvedMsgProcess(const WAR_MSG_HDR* IN pStream)
{
if(m_pMsgRouter->GetUpdateCtrl())
m_pMsgRouter->OnRecv(pStream);
}
VOID
SSQUpdateCtrl::RecvedMsgProcessSpecial(const WAR_MSG_HDR* IN pStream)
{
if(m_pMsgRouter->GetUpdateCtrl())
m_pMsgRouter->OnRecvSpecial(pStream);
}
VOID
SSQUpdateCtrl::UserEnter(User* const pUser)
{
// 특수 조건 설정할 것... enter enabled state
const DWORD playerKey = pUser->GetPlayerKey();
//
ISSQHandler* const pHandler = m_pMsgRouter->FindSlot(playerKey);
//_NA_000000_20151109_BUG_SSQ_HANDLER_NULL
if (pHandler != NULL)
{
// ASSERT (pHandler) ... ?
pHandler->ChangeStatus(ISSQHandler::State<ISSQHandler::USR_ENTERED_SSQ_FIELD>());
//WAVERIX_SSQ_PORTAL_TRANSACTION_MODIFY
//{
// pUser->SSQHandler = ISSQHandlerD(pUser, pHandler); // Link
// m_pMsgRouter->RemoveSlot(playerKey);
//}
}
else
{
SUNLOG(eCRITICAL_LOG, "["__FUNCTION__"][CriticalBug] Cannot Find ISSQHandler [PlayerKey:%d]",
playerKey);
}
}
VOID
SSQUpdateCtrl::UserLeave(User* const pUser)
{
// UserLeave시 Zone::users->Slot(insert) 처리가 이뤄지도록 하자.??
//WAVERIX_SSQ_PORTAL_TRANSACTION_MODIFY
const DWORD playerKey = pUser->GetPlayerKey();
ISSQHandler* const pHandler = m_pMsgRouter->FindSlot(playerKey);
pHandler->ChangeStatus(ISSQHandler::State<ISSQHandler::USR_LEAVED_SSQ_FIELD>());
//m_pMsgRouter->SlotClear(pUser);
}
//==================================================================================================
// <SSQUpdateCtrl><None State>
//
void
SSQUpdateCtrl_None::Init(const curtime_info& tmInfo)
{
__UNUSED(tmInfo);
m_CurType = value;
m_Timer.SetTimer(interval);
m_ControlField = RCS_NONE;
m_IsFastTrack = 0;
}
BOOL
SSQUpdateCtrl_None::Update(const curtime_info& tmInfo)
{
if(m_Timer.IsExpired())
{
DWORD findedIndex = 0;
if(m_pSSQInfo->IsNoticeTime(&tmInfo.SysTime, &findedIndex))
{
__WAVERIX_LOG_FORMAT_0S1N("Notice Time!", m_CodeOfSSQ);
m_ProcedIndex = (BYTE)findedIndex; // 현재 활성화된 탭
SSQUpdateCtrl_Notice* const pCtrl = TAllocNew(SSQUpdateCtrl_Notice_Info::value_type);
pCtrl->Migration(pCtrl, this);
pCtrl->Init(tmInfo);
pCtrl->RegisterUpdateCtrl(pCtrl);
m_pManager->SSQSingletonNoticeCtrl.EnterSingletonNotice(m_pSSQInfo, findedIndex);
TAllocDelete(updater_info::value_type, this);
return TRUE;
}
}
return TRUE;
}
//==================================================================================================
// <SSQUpdateCtrl><Notice State>
//
VOID
SSQUpdateCtrl_Notice::Init(const curtime_info& tmInfo)
{
m_CurType = value;
m_Timer.SetTimer(interval);
// 초기화 되는 시점 broadcast 처리 시간
m_StartTick = m_NextTimeout = GetTickCount();
SSQInfo::TimeRange range = m_pSSQInfo->m_CTimeNotice[m_ProcedIndex];
_ConvertWzTmFmt2AcmTime(range);
// Init시키는 주체가 SSQUpdateCtrl_None이고, 공지시간으로 평가하므로 항상 0<=x
ASSERT(tmInfo.AcmTime >= range.Start);
m_NextAcmTime = tmInfo.AcmTime; // 처음에는 바로 enter
const AcmTmFmt tmFmt = tmInfo.AcmTime - range.Start; // tmFmt = [0, 5)
m_StartCountdown = (BYTE)(broadcast_countdown_init-tmFmt);
//m_StartCountdown = (BYTE)(tmFmt); // (BYTE)(broadcast_countdown_init-tmFmt);
m_Countdown = m_StartCountdown;
}
BOOL
SSQUpdateCtrl_Notice::Update(const curtime_info& tmInfo)
{
if(!m_Timer.IsExpired())
return TRUE;
if(tmInfo.AcmTime >= m_NextAcmTime)
{
__WAVERIX_LOG_FORMAT_0S1N("Post 1 minute", m_CodeOfSSQ);
if(m_IsFastTrack)
m_Countdown = 0;
// 5회 전송 이후 대기시간으로 전환 - (WARNING) SSQ Field생성 여부 확인할 것
if(broadcast_changeto_waiting_countdown_value /*0*/ == m_Countdown)
{
Process_CreateSSQField(); // 연관 이벤트를 통해 성공시 m_pSSQField이 설정된다.
SSQUpdateCtrl* pCtrl = NULL;
if(!m_pSSQField)
{ // 더 이상 어찌할 수 없는 상황이다. 이번 SSQ Dungeon open은 스킵하기로 한다.
__WAVERIX_LOG_FORMAT_0S1N("Fail change to NextState by BattleServer Problem", m_CodeOfSSQ);
pCtrl = TAllocNew(SSQUpdateCtrl_None_Info::value_type);
}
else
{ // Success Processing
WAVERIX_DEBUG_LOG_0S1N("Change to Waiting", m_CodeOfSSQ);
pCtrl = TAllocNew(SSQUpdateCtrl_Waiting_Info::value_type);
}
pCtrl->Migration(pCtrl, this);
pCtrl->Init(tmInfo);
pCtrl->RegisterUpdateCtrl(pCtrl);
TAllocDelete(updater_info::value_type, this);
return TRUE;
}
// 5,4,3,2,1 분 전일 경우 여기에 도달하게 될 것이다.
SSQInfo::TimeRange range = m_pSSQInfo->m_CTimeNotice[m_ProcedIndex];
_ConvertWzTmFmt2AcmTime(range);
const BYTE remain_countdown = range.End >= tmInfo.AcmTime
? BYTE(range.End - tmInfo.AcmTime)
: 0;
if(FlowControl::FCAssert((remain_countdown <= broadcast_countdown_init) &&
"어떻게 처리해야할 지를 고민하라..."))
{
// 조건에 만족하는 다음 next timeout을 구하라.
BYTE offset = m_StartCountdown - remain_countdown;__TOUCHED(offset);
//m_NextTimeout = m_StartTick + interval_broadcast;// * (offset));
m_Countdown = remain_countdown;
m_NextAcmTime += 1; // 1'
}
else
{
__WAVERIX_LOG_FORMAT_0S1N("Proc Error", remain_countdown);
// 에러 처리를 해야할 것이다.
m_NextTimeout = 0;
m_Countdown = broadcast_changeto_waiting_countdown_value;
}
switch(m_Countdown)
{
case broadcast_countdown_init - 0: // (5-0)분 전
case broadcast_countdown_init - 1: // (5-1)분 전
case broadcast_countdown_init - 2: // (5-2)분 전
case broadcast_countdown_init - 3: // (5-3)분 전
case broadcast_countdown_init - 4: // (5-4)분 전
{
// 와중에 BattleServer가 죽었거나 나중에 뜨는 경우를 감안한다.
Process_CreateSSQField();
SendNoticeBroadcast(m_Countdown);
--m_Countdown;
}
break;
}
}
return TRUE;
}
VOID
SSQUpdateCtrl_Notice::SendNoticeBroadcast(const SSQInfo::COUNT_T countdown_value)
{
// Send Broadcasting
m_pManager->SSQSingletonNoticeCtrl.Broadcast_Notice(this,
SSQSingletonNotice::eNotice_OpenCountdown,
countdown_value);
}
VOID
SSQUpdateCtrl_Notice::Process_CreateSSQField()
{
WAVERIX_DEBUG_LOG_0S1N("Enter", m_CodeOfSSQ);
const DWORD RESULT_FAIL = 0;
//////////////////////////////////////////////////////////////////////////
// <LOGIC CONTROL>
FlowControl flow;
FLOWCONTROL_START()
{
if(GetSSQField())
return;
// 먼저 찾도록 하자.... CodeOfSSQ로...
// - Key의 유무, Zone IO 등등을 종합적으로 평가해서
InstanceDungeon* pInstanceDungeon = NULL;
const RC::eROOM_RESULT rt = g_ZoneManager.CreateStaticDungeon(m_pSSQInfo->m_MapCode,
pInstanceDungeon,
INDUN_KIND_SS_QUEST,
FALSE);
// 이것이 성공한 뒤에 this->GetSSQField는 자동 설정된다.
IfFailThenBreak(RC::RC_ROOM_SUCCESS == rt, RESULT_FAIL);
// 해당 정보에 따라 object정보가 할당되어야 한다.
WAVERIX_DEBUG_LOG_0S1N("SUCCESS", m_CodeOfSSQ);
return;
}
FLOWCONTROL_END;
//////////////////////////////////////////////////////////////////////////
// <ERROR CONTROL>
__WAVERIX_LOG_FORMAT_0S1N("FAIL", m_CodeOfSSQ);
return;
}
//==================================================================================================
// <SSQUpdateCtrl><Waiting State>
//
inline VOID
SSQUpdateCtrl_Waiting::Clear_WaitingState_Vars()
{
m_EventType = SWET_UPPERBOUND;
m_EventArg0 = 0;
m_EventArg1 = 0;
}
VOID
SSQUpdateCtrl_Waiting::Init(const curtime_info& tmInfo)
{
m_CurType = value;
m_Timer.SetTimer(interval);
m_ControlField = RCS_ENABLE_ENTER | RCS_ENABLE_CREATE_PORTAL;
// 대기 상태의 시작은 입장 가능
// < 이후 스크립트에서 제한사항이 변경되었을 경우 여기에 설정하는 방식을 사용해야 할 것이다.
{ // event: 입장 가능 공지(instance)
State_Data data = {
SWET_START_WAITING_NODE,
0, 2 * minute, //, 0, enterable_limit_enter_notice
SWET_NOTICE_COUNTDOWN
};
m_StateData[SWET_START_WAITING_NODE ] = data;
};
{ // event: 입장 종료 countdown 공지(2x)
State_Data data = {
SWET_NOTICE_COUNTDOWN,
2 * minute, 5 * minute, //, enterable_limit_enter_notice, enterable_limit_countdown_notice
SWET_ENTERENCE_CLOSED
};
m_StateData[SWET_NOTICE_COUNTDOWN ] = data;
};
{ // event: 입장 종료 공지(instance), 입장 요청 블록 처리 <- skip process <-
State_Data data = {
SWET_ENTERENCE_CLOSED,
4 * minute + 5 * second, 5 * minute, //, enterable_limit_countdown_notice, enterable_limit_with_portal_delay_tick
SWET_FINAL_ENTERENCE_TIMEOUT
};
m_StateData[SWET_ENTERENCE_CLOSED ] = data;
};
{ // event: wait for client enterable period
State_Data data = {
SWET_FINAL_ENTERENCE_TIMEOUT,
5 * minute, 6 * minute, //, enterable_limit_with_portal_delay_tick, enter_ack_timeout_limit
SWET_GAMBLE_PROCESS
};
m_StateData[SWET_FINAL_ENTERENCE_TIMEOUT ] = data;
};
{ // event: gamble process
State_Data data = {
SWET_GAMBLE_PROCESS,
6 * minute, 6 * minute + 15 * second, //, enter_ack_timeout_limit, gameble_timeout_limit
SWET_BATTLE_GRACE_PERIOD
};
m_StateData[SWET_GAMBLE_PROCESS ] = data;
};
{ // event: grace period
State_Data data = {
SWET_BATTLE_GRACE_PERIOD,
6 * minute + 15 * second, 6 * minute + 50 * second, //, gameble_timeout_limit, battle_grace_period
SWET_BATTLE_START_JUST_BEFORE
};
m_StateData[SWET_BATTLE_GRACE_PERIOD ] = data;
};
{
State_Data data = {
SWET_BATTLE_START_JUST_BEFORE,
6 * minute + 50 * second, 7 * minute, //, battle_grace_period, battle_countdown_timeout
SWET_BATTLE_COUNTDOWN
};
m_StateData[SWET_BATTLE_START_JUST_BEFORE ] = data;
};
if(m_IsFastTrack)
{
m_StateData[SWET_START_WAITING_NODE ].m_Begin = 0;
m_StateData[SWET_START_WAITING_NODE ].m_End = 5 * second;
m_StateData[SWET_NOTICE_COUNTDOWN ].m_Begin = 5 * second;
m_StateData[SWET_NOTICE_COUNTDOWN ].m_End = 1 * minute;
m_StateData[SWET_ENTERENCE_CLOSED ].m_Begin = 50 * second;
m_StateData[SWET_ENTERENCE_CLOSED ].m_End = 1 * minute;
m_StateData[SWET_FINAL_ENTERENCE_TIMEOUT ].m_Begin = 1 * minute;
m_StateData[SWET_FINAL_ENTERENCE_TIMEOUT ].m_End = 1 * minute + 1 * second;
m_StateData[SWET_GAMBLE_PROCESS ].m_Begin = 1 * minute + 1 * second;
m_StateData[SWET_GAMBLE_PROCESS ].m_End = 1 * minute + 18 * second;
m_StateData[SWET_BATTLE_GRACE_PERIOD ].m_Begin = 1 * minute + 18 * second;
m_StateData[SWET_BATTLE_GRACE_PERIOD ].m_End = 1 * minute + 20 * second;
m_StateData[SWET_BATTLE_START_JUST_BEFORE ].m_Begin = 1 * minute + 20 * second;
m_StateData[SWET_BATTLE_START_JUST_BEFORE ].m_End = 1 * minute + 25 * second;
}
{
State_Data data = {
SWET_BATTLE_COUNTDOWN,
0, 0,
SWET_UPPERBOUND
};
m_StateData[SWET_BATTLE_COUNTDOWN ] = data;
};
//─────────────────────────────────────────
m_StartTick = GetTickCount();
m_CCUReport.Init(tmInfo, this);
// SWET_START_WAITING_NODE : Initial Values
Clear_WaitingState_Vars();
m_EventType = SWET_START_WAITING_NODE; // first enter state
//
ZeroMemory(&m_GambleResult, sizeof(m_GambleResult));
}
WAITING_STATE_PROCNODE(SWET_START_WAITING_NODE, SWET_NOTICE_COUNTDOWN)
{
const State_Data& rData = m_StateData[m_EventType];
const DWORD NOT_PROCESSED = 0;
const DWORD NOTICED = !NOT_PROCESSED;
// m_EventArg 0:FALSE, 1:TRUE(noticed)
if(NOTICED == m_EventArg0) // processed
{
const BOOLEAN inSection =
State_Data::IsAcceptSection(tmInfo, m_StartTick, rData.m_Begin, rData.m_End);
if(!inSection)
{ // goto next_state
Clear_WaitingState_Vars();
m_EventType = rData.m_Next;
return;
}
return;
}
WAVERIX_DEBUG_LOG_0S1N("SWET_START_WAITING_NODE", m_CodeOfSSQ);
ASSERT(NOT_PROCESSED == m_EventArg0);
// first process -or- long term skip
m_EventArg0 = NOTICED;
m_pManager->SSQSingletonNoticeCtrl.Broadcast_Notice(this, SSQSingletonNotice::eNotice_Open);
}
WAITING_STATE_PROCNODE(SWET_NOTICE_COUNTDOWN, SWET_ENTERENCE_CLOSED)
{
const State_Data& rData = m_StateData[m_EventType];
const DWORD NOT_PROCESSED = 0;
const DWORD NOTICE_0_2 = 1;
const DWORD NOTICE_2_3 = 2; // 입장 종료 2분 전
const DWORD NOTICE_3_4 = 3; // 입장 종료 1분 전
const DWORD NOTICE_4__ = 4; // 입장 종료
const DWORD GOTO_NEXT = 5;
if(NOT_PROCESSED != m_EventArg0)
{
const BOOLEAN inSection =
State_Data::IsAcceptSection(tmInfo, m_StartTick, rData.m_Begin, rData.m_End);
if(!inSection)
{ // goto next_state - 뭔가의 이유로 관련 프로세스를 껑충 뛸 경우를 감안하자.
// 그럴 경우라면 바로 아래의 상태로 넘어가는 편이 나을 지도 모르겠다.
WAVERIX_DEBUG_LOG_0S1N("Change to NextState - 이후 입장 불가", m_CodeOfSSQ);
Clear_WaitingState_Vars();
m_EventType = rData.m_Next;
return;
}
//SSQ_PROTOCOL_NOTICE_ENABLE_ENTER_COUNTDOWN
if(tmInfo.CurTick >= m_EventArg1)
{
switch(const DWORD current_mode = m_EventArg0)
{
case NOTICE_0_2:
{ // 2분전 공지 발송
WAVERIX_DEBUG_LOG_0S1N("(입장 종료 공지) 2분 전", m_CodeOfSSQ);
m_EventArg0 = NOTICE_2_3;
m_EventArg1 = m_StartTick + 3 * minute;
if(m_IsFastTrack)
m_EventArg1 = m_StartTick + 10*second;
m_pManager->SSQSingletonNoticeCtrl.Broadcast_Notice(
this, SSQSingletonNotice::eNotice_CloseCountdown, 2);
}
break;
case NOTICE_2_3:
{ // 1분전 공지 발송
WAVERIX_DEBUG_LOG_0S1N("(입장 종료 공지) 1분 전", m_CodeOfSSQ);
m_EventArg0 = NOTICE_3_4;
m_EventArg1 = m_StartTick + 4 * minute;
if(m_IsFastTrack)
m_EventArg1 = m_StartTick + 15*second;
m_pManager->SSQSingletonNoticeCtrl.Broadcast_Notice(
this, SSQSingletonNotice::eNotice_CloseCountdown, 1);
}
break;
case NOTICE_3_4:
{ // 입장 종료까지의 1분 처리 (50')
__WAVERIX_LOG_FORMAT_0S1N("(입장 종료 공지) 포탈 생성 가능시간 끝", m_CodeOfSSQ);
// <- Update : disable create portal
m_EventArg0 = NOTICE_4__;
m_EventArg1 = m_StartTick + 4 * minute + 50 * second;
if(m_IsFastTrack)
m_EventArg1 = m_StartTick + 20*second;
m_pManager->SSQSingletonNoticeCtrl.Broadcast_Notice(
this, SSQSingletonNotice::eNotice_ClosePortal);
m_ControlField &= ~RCS_ENABLE_CREATE_PORTAL;
}
break;
case NOTICE_4__:
{
WAVERIX_DEBUG_LOG_0S1N("(입장 종료) 10초 전", m_CodeOfSSQ);
m_EventArg0 = GOTO_NEXT;
m_EventArg1 = m_StartTick + 4 * minute + 59 * second;
if(m_IsFastTrack)
m_EventArg1 = m_StartTick + 25*second;
return;
}
break;
case GOTO_NEXT:
{
WAVERIX_DEBUG_LOG_0S1N("(입장 종료 공지) 입장 종료", m_CodeOfSSQ);
Clear_WaitingState_Vars();
m_EventType = rData.m_Next;
m_pManager->SSQSingletonNoticeCtrl.Broadcast_Notice(
this, SSQSingletonNotice::eNotice_Close);
m_ControlField &= ~RCS_ENABLE_ENTER;
return; // notice!
}
break;
}
}
return;
}
__WAVERIX_LOG_FORMAT_0S1N("SWET_NOTICE_COUNTDOWN", m_CodeOfSSQ);
ASSERT(NOT_PROCESSED == m_EventArg0);
// first process -or- long term skip
// GNM_SSQ_START_COUNTDOWN
m_EventArg0 = NOTICE_0_2; // 0~2분 사이
m_EventArg1 = m_StartTick + 2 * minute;
if(m_IsFastTrack)
m_EventArg1 = m_StartTick;
}
WAITING_STATE_PROCNODE(SWET_ENTERENCE_CLOSED, SWET_FINAL_ENTERENCE_TIMEOUT)
{
const State_Data& rData = m_StateData[m_EventType];
// 처리 기간 [4'5", 5']
const DWORD NOT_PROCESSED = 0;
const DWORD WAIT_FOR_CLIENT_GRACE_PERIOD = !NOT_PROCESSED;
if(WAIT_FOR_CLIENT_GRACE_PERIOD == m_EventArg0)
{
const BOOLEAN inSection =
State_Data::IsAcceptSection(tmInfo, m_StartTick, rData.m_Begin, rData.m_End);
if(!inSection)
{ // goto next_state
Clear_WaitingState_Vars();
m_EventType = rData.m_Next;
return;
}
return;
}
WAVERIX_DEBUG_LOG_0S1N("SWET_ENTERENCE_CLOSED", m_CodeOfSSQ);
ASSERT(NOT_PROCESSED == m_EventArg0);
// first process -or- long term skip
m_EventArg0 = WAIT_FOR_CLIENT_GRACE_PERIOD;
}
WAITING_STATE_PROCNODE(SWET_FINAL_ENTERENCE_TIMEOUT, SWET_GAMBLE_PROCESS)
{
const State_Data& rData = m_StateData[m_EventType];
// 처리 기간 [5', 6']
const DWORD NOT_PROCESSED = 0;
const DWORD WAIT_FOR_CLIENT_GRACE_PERIOD = !NOT_PROCESSED;
if(WAIT_FOR_CLIENT_GRACE_PERIOD == m_EventArg0)
{
const BOOLEAN inSection =
State_Data::IsAcceptSection(tmInfo, m_StartTick, rData.m_Begin, rData.m_End);
if(!inSection)
{ // goto next_state
Clear_WaitingState_Vars();
m_EventType = rData.m_Next;
return;
}
return;
}
WAVERIX_DEBUG_LOG_0S1N("SWET_FINAL_ENTERENCE_TIMEOUT", m_CodeOfSSQ);
ASSERT(NOT_PROCESSED == m_EventArg0);
// first process -or- long term skip
m_EventArg0 = WAIT_FOR_CLIENT_GRACE_PERIOD;
}
WAITING_STATE_PROCNODE(SWET_GAMBLE_PROCESS, SWET_BATTLE_GRACE_PERIOD)
{
const State_Data& rData = m_StateData[m_EventType];
// 최초 진입 상태 <- 대기 시간 시작한 지 6분 경과
// 처리 기간 [6'00", 6'15"]
const DWORD NOT_PROCESSED = 0;
const DWORD GAMBLE_PERIOD = 1;
const DWORD GAMBLE_RESULT = 2;
SSQInfoParser* const pSSQInfoParser = SSQInfoParser::Instance();
const SSQInfo* pSSQInfo = pSSQInfoParser->FindSSQInfo(m_CodeOfSSQ);
if (pSSQInfo == NULL)
{
SUNLOG(
eCRITICAL_LOG,
__FUNCTION__" : Not found SSQ Info : SSQCode : %d",
m_CodeOfSSQ
);
return;
}
if (pSSQInfo->m_Cost == 0)
{
//배틀존 리뉴얼 이후 타락한 사원 입장 지불 조건이 사라졌고 스크립트 상
//지불 금액을 0으로 세팅하게 되면서 겜블 이벤트자체의 유효성이 사라졌다.
//스크립트 상에서 지불 금액을 0이상 값으로 설정하면 겜블 이벤트는 정상 동작.
Clear_WaitingState_Vars();
m_EventType = rData.m_Next;
return;
}
if(NOT_PROCESSED != m_EventArg0)
{
const BOOLEAN inSection =
State_Data::IsAcceptSection(tmInfo, m_StartTick, rData.m_Begin, rData.m_End);
if(!inSection)
{ // goto next_state
Clear_WaitingState_Vars();
m_EventType = rData.m_Next;
return;
}
if(tmInfo.CurTick >= m_EventArg1)
{
BROADCASTER_MSG_CG_ZONE_EX_SSQ_ROOM_CTRL_CMD& rMsgCMD = m_pMsgRouter->GetCMD_CG();
WAR_MSG_HDR* const pHeader = &rMsgCMD.MSG_HEADER;
switch(const DWORD current_mode = m_EventArg0)
{
case GAMBLE_PERIOD:
{
m_EventArg0 = GAMBLE_RESULT;
m_EventArg1 = m_StartTick + 6 * minute + 14 * second;
WAVERIX_DEBUG_LOG_0S1N("A->G Gameble StartCmd & Notice G->C", m_CodeOfSSQ);
{
BROADCASTER_MSG_AG_ZONE_SSQ_ROOM_CTRL_CMD& rMsgCMD = m_pMsgRouter->GetCMD_AG();
WAR_MSG_HDR* const pHeader = &rMsgCMD.MSG_HEADER;
MSGSUB_SSQ_S2S_GAMBLE_START msg;
msg.CodeOfSSQ = m_CodeOfSSQ;
msg.SerializeTo(pHeader);
};
{
MSGSUB_SSQ_PROTOCOL_NOTICE_GAMBLE_START msg;
msg.SerializeTo(pHeader);
};
}
break;
case GAMBLE_RESULT:
{ // Gamble결과 확인 여부가 중요하다.
__WAVERIX_LOG_FORMAT_0S1N("Notice Gamble Result", m_CodeOfSSQ);
Clear_WaitingState_Vars();
m_EventType = rData.m_Next;
// (THINKING)
// - Agnet에서? GameServer에서? 지연시켜서 보낼까?, 클라 내장할까?
if(FlowControl::FCAssert(SSQ_S2S_GAMBLE_RESULT == m_GambleResult._TAG))
{
MSGSUB_SSQ_PROTOCOL_NOTICE_GAMBLE_RESULT msg;
msg.RewardHeim = m_GambleResult.RewardHeim;
msg.CharName = m_GambleResult.CharName;
msg.SerializeTo(pHeader);
__WAVERIX_LOG_FORMAT_0S((TCHAR*)m_GambleResult.CharName);
}
return;
}
break;
}
}
return;
}
WAVERIX_DEBUG_LOG_0S1N("SWET_GAMBLE_PROCESS", m_CodeOfSSQ);
ASSERT(NOT_PROCESSED == m_EventArg0);
// first process -or- long term skip
// A->G : start gamble process
// A->C : gamble notice
// A->C : gamble result notice
m_EventArg0 = GAMBLE_PERIOD;
if(m_IsFastTrack == 0)
m_EventArg1 = m_StartTick + 6 * minute; // 바로 시작 명령으로
else
m_EventArg1 = m_StartTick;
}
WAITING_STATE_PROCNODE(SWET_BATTLE_GRACE_PERIOD, SWET_BATTLE_START_JUST_BEFORE)
{
const State_Data& rData = m_StateData[m_EventType];
// 최초 진입 상태 <- 갬블 종료후 6'15" -> 6'50" 까지...
// 처리 기간 [6'15", 6'50"]
const DWORD NOT_PROCESSED = 0;
const DWORD PREPARE_BATTLE = !NOT_PROCESSED;
if(PREPARE_BATTLE == m_EventArg0)
{
const BOOLEAN inSection =
State_Data::IsAcceptSection(tmInfo, m_StartTick, rData.m_Begin, rData.m_End);
if(!inSection)
{ // goto next_state
Clear_WaitingState_Vars();
m_EventType = rData.m_Next;
return;
}
return;
}
WAVERIX_DEBUG_LOG_0S1N("SWET_BATTLE_GRACE_PERIOD", m_CodeOfSSQ);
ASSERT(NOT_PROCESSED == m_EventArg0);
// first process -or- long term skip
//WAVERIX_DEBUGGING_OUTPUTn("A<->G 전투를 위한 준비 : NPC 생성 등등");
m_EventArg0 = PREPARE_BATTLE;
}
WAITING_STATE_PROCNODE(SWET_BATTLE_START_JUST_BEFORE, SWET_BATTLE_COUNTDOWN)
{
const State_Data& rData = m_StateData[m_EventType];
// 최초 진입 상태 <- 대기 시간 시작한 지 6'50" 경과
// 처리 기간 [6'55", 7'00"]
const DWORD NOT_PROCESSED = 0;
const DWORD BATTLE_COUNTDOWN = !NOT_PROCESSED;
if(BATTLE_COUNTDOWN == m_EventArg0)
{
const BOOLEAN inSection =
State_Data::IsAcceptSection(tmInfo, m_StartTick, rData.m_Begin, rData.m_End);
if(!inSection)
{ // goto next_state
Clear_WaitingState_Vars();
m_EventType = rData.m_Next;
return;
}
return;
}
WAVERIX_DEBUG_LOG_0S1N("SWET_BATTLE_START_JUST_BEFORE", m_CodeOfSSQ);
ASSERT(NOT_PROCESSED == m_EventArg0);
// first process -or- long term skip
WAVERIX_DEBUG_LOG_0S("A<->G Battle Preparation : Create NPCs, etc...");
m_EventArg0 = BATTLE_COUNTDOWN;
{
BROADCASTER_MSG_AG_ZONE_SSQ_ROOM_CTRL_CMD& rMsgCMD = m_pMsgRouter->GetCMD_AG();
WAR_MSG_HDR* const pHeader = &rMsgCMD.MSG_HEADER;
{
MSGSUB_SSQ_S2S_BATTLE_COUNTDOWN msg;
msg.CodeOfSSQ = m_CodeOfSSQ;
msg.SerializeTo(pHeader);
}
{
MSGSUB_SSQ_S2S_BATTLE_PREPARATION msg;
msg.CodeOfSSQ = m_CodeOfSSQ;
msg.SerializeTo(pHeader);
}
};
{
BROADCASTER_MSG_AW_OPERATION_ROOM_CTRL_CMD& rMsgCMD = m_pMsgRouter->GetCMD_AW();
WAR_MSG_HDR* const pHeader = &rMsgCMD.MSG_HEADER;
MSGSUB_SSQ_S2S_SET_CHAT_BLOCK_ZONE msg;
msg.ZoneKey = m_pSSQField->GetKey();
msg.Enabled = FALSE;
msg.SerializeTo(pHeader);
};
{
BROADCASTER_MSG_CG_ZONE_EX_SSQ_ROOM_CTRL_CMD& rMsgCMD = m_pMsgRouter->GetCMD_CG();
WAR_MSG_HDR* const pHeader = &rMsgCMD.MSG_HEADER;
MSGSUB_SSQ_PROTOCOL_NOTICE_BATTLE_COUNTDOWN msg;
msg.SerializeTo(pHeader);
};
}
BOOL
SSQUpdateCtrl_Waiting::Update(const curtime_info& tmInfo)
{
if(m_Timer.IsExpired())
{
switch(m_EventType)
{
case SWET_START_WAITING_NODE :
WAITING_STATE_PROCNODE_CALL(SWET_START_WAITING_NODE, SWET_NOTICE_COUNTDOWN, tmInfo);
break;
case SWET_NOTICE_COUNTDOWN :
WAITING_STATE_PROCNODE_CALL(SWET_NOTICE_COUNTDOWN, SWET_ENTERENCE_CLOSED, tmInfo);
break;
case SWET_ENTERENCE_CLOSED :
WAITING_STATE_PROCNODE_CALL(SWET_ENTERENCE_CLOSED, SWET_FINAL_ENTERENCE_TIMEOUT, tmInfo);
break;
case SWET_FINAL_ENTERENCE_TIMEOUT :
{
// 1) 전투 시작 조건 판단
const BOOLEAN isCancelStart =
(m_pSSQField->GetUserNum() <
(DWORD)m_pSSQInfo->m_PlayerCountInfo[SSQInfo::PCL_MIN]);
WAVERIX_DEBUG_CODE(
const CHAR* const pResultMsg
= isCancelStart
? "踏狗역迦離훙逃掘齡,SSQ뇹잿呵겨"
: "찮璃踏狗역迦離훙鑒掘齡";
WAVERIX_DEBUG_LOG_0S1N(pResultMsg, m_CodeOfSSQ);
);
if(isCancelStart && m_IsFastTrack == 0)
{
Process_Change_ClearState(tmInfo);
return TRUE;
}
WAITING_STATE_PROCNODE_CALL(SWET_FINAL_ENTERENCE_TIMEOUT, SWET_GAMBLE_PROCESS, tmInfo);
}
break;
case SWET_GAMBLE_PROCESS :
{
WAITING_STATE_PROCNODE_CALL(SWET_GAMBLE_PROCESS, SWET_BATTLE_GRACE_PERIOD, tmInfo);
}
break;
case SWET_BATTLE_GRACE_PERIOD :
WAITING_STATE_PROCNODE_CALL(SWET_BATTLE_GRACE_PERIOD, SWET_BATTLE_START_JUST_BEFORE, tmInfo);
break;
case SWET_BATTLE_START_JUST_BEFORE :
WAITING_STATE_PROCNODE_CALL(SWET_BATTLE_START_JUST_BEFORE, SWET_BATTLE_COUNTDOWN, tmInfo);
break;
case SWET_BATTLE_COUNTDOWN :
{
WAVERIX_DEBUG_LOG_0S1N("SWET_BATTLE_COUNTDOWN", m_CodeOfSSQ);
// change updater - battle process
Process_Change_BattleState(tmInfo);
}
break;
default:
ASSERT(!"Impossible Control Logic");
break;
};
}
m_CCUReport.Update(tmInfo, this);
return TRUE;
}
VOID
SSQUpdateCtrl_Waiting::Process_Change_BattleState(const curtime_info& tmInfo)
{
__WAVERIX_LOG_FORMAT_0S1N("Change to BattleState", m_CodeOfSSQ);
SSQUpdateCtrl_Battle* const pCtrl = TAllocNew(SSQUpdateCtrl_Battle_Info::value_type);
pCtrl->Migration(pCtrl, this);
pCtrl->Init(tmInfo);
pCtrl->RegisterUpdateCtrl(pCtrl);
TAllocDelete(updater_info::value_type, this);
}
VOID
SSQUpdateCtrl_Waiting::Process_Change_ClearState(const curtime_info& tmInfo)
{
__WAVERIX_LOG_FORMAT_0S1N("Change to ClearState", m_CodeOfSSQ);
m_ControlField &= ~RCS_BATTLE_STARTED;
{
BROADCASTER_MSG_AG_ZONE_SSQ_ROOM_CTRL_CMD& rMsgCMD = m_pMsgRouter->GetCMD_AG();
WAR_MSG_HDR* const pHeader = &rMsgCMD.MSG_HEADER;
MSGSUB_SSQ_S2S_BATTLE_CANCEL msg;
msg.CodeOfSSQ = m_CodeOfSSQ;
msg.SerializeTo(pHeader);
};
{
BROADCASTER_MSG_AW_OPERATION_ROOM_CTRL_CMD& rMsgCMD = m_pMsgRouter->GetCMD_AW();
WAR_MSG_HDR* const pHeader = &rMsgCMD.MSG_HEADER;
MSGSUB_SSQ_S2S_SET_CHAT_BLOCK_ZONE msg;
msg.ZoneKey = m_pSSQField->GetKey();
msg.Enabled = TRUE;
msg.SerializeTo(pHeader);
};
{
BROADCASTER_MSG_CG_ZONE_EX_SSQ_ROOM_CTRL_CMD& rMsgCMD = m_pMsgRouter->GetCMD_CG();
WAR_MSG_HDR* const pHeader = &rMsgCMD.MSG_HEADER;
MSGSUB_SSQ_PROTOCOL_NOTICE_BATTLE_CANCLED msg;
msg.SerializeTo(pHeader);
};
SSQUpdateCtrl_Clear* const pCtrl = TAllocNew(SSQUpdateCtrl_Clear_Info::value_type);
pCtrl->Migration(pCtrl, this);
pCtrl->Init(tmInfo);
pCtrl->RegisterUpdateCtrl(pCtrl);
TAllocDelete(updater_info::value_type, this);
}
//==================================================================================================
//
VOID
SSQCCUReporter::Init(const curtime_info& tmInfo, SSQUpdateCtrl* const pUpdateCtrl)
{
const eTIME_TYPE time_type = pUpdateCtrl->GetTimeType();
ASSERT(time_type == TT_BATTLE || time_type == TT_WAITING);
const BOOLEAN isBattleCountdown = time_type == TT_BATTLE;
const SSQInfo* const pSSQInfo = pUpdateCtrl->GetSSQInfo();
const DWORD total_second = isBattleCountdown ? pSSQInfo->m_QuestPlayTime * 60
: pSSQInfo->m_WatingTime * 60;
m_EndTick = tmInfo.CurTick + (total_second*second);
m_CCUReportTimer.Instance();
}
bool
SSQCCUReporter::Update(const curtime_info& tmInfo, SSQUpdateCtrl* const pUpdateCtrl)
{
if(!m_CCUReportTimer.IsExpired())
return true;
m_CCUReportTimer.SetNextTimeoutByCurTickBased(5*second);
const BOOLEAN bOverThan = !(tmInfo.CurTick < m_EndTick);
BROADCASTER_MSG_CG_ZONE_EX_SSQ_ROOM_CTRL_CMD& rMsgCMD = pUpdateCtrl->GetMsgRouter()->GetCMD_CG();
WAR_MSG_HDR* const pHeader = &rMsgCMD.MSG_HEADER;
MSGSUB_SSQ_PROTOCOL_INFORMATION_REMAINED_TIME msg;
if(bOverThan)
{
msg.Minute = 0;
msg.Second = 0;
msg.SerializeTo(pHeader);
return false;
}
else
{
const DWORD diff = (DWORD)((m_EndTick - tmInfo.CurTick) / 1000);
const SSQInfo::MINUTE_T remain_min = static_cast<SSQInfo::MINUTE_T>(diff / 60);
const SSQInfo::SECOND_T remain_sec = static_cast<SSQInfo::SECOND_T>(diff % 60);
msg.Minute = remain_min;
msg.Second = remain_sec;
msg.SerializeTo(pHeader);
}
return true;
}
//==================================================================================================
// <SSQUpdateCtrl><Battle State>
//
VOID
SSQUpdateCtrl_Battle::Init(const curtime_info& tmInfo)
{
WAVERIX_DEBUG_LOG_0S1N("Init", m_CodeOfSSQ);
m_CurType = value;
m_Timer.SetTimer(interval);
SSQInfo::TimeRange range = m_pSSQInfo->m_CTimeBattle[m_ProcedIndex];
_ConvertWzTmFmt2AcmTime(range);
m_BattleCloseTimeFlag = range.End;
m_BattleCloseCountdown = m_pSSQInfo->m_QuestPlayTime + 1; // (+1) 최초 Broadcasting처리 목적
m_CCUReport.Init(tmInfo, this);
m_ControlField = RCS_BATTLE_STARTED;
{
BROADCASTER_MSG_AG_ZONE_SSQ_ROOM_CTRL_CMD& rMsgCMD = m_pMsgRouter->GetCMD_AG();
WAR_MSG_HDR* const pHeader = &rMsgCMD.MSG_HEADER;
MSGSUB_SSQ_S2S_BATTLE_START msg;
msg.CodeOfSSQ = m_CodeOfSSQ;
msg.SerializeTo(pHeader);
};
{
BROADCASTER_MSG_CG_ZONE_EX_SSQ_ROOM_CTRL_CMD& rMsgCMD = m_pMsgRouter->GetCMD_CG();
WAR_MSG_HDR* const pHeader = &rMsgCMD.MSG_HEADER;
MSGSUB_SSQ_PROTOCOL_NOTICE_BATTLE_START msg;
msg.SerializeTo(pHeader);
};
}
BOOL
SSQUpdateCtrl_Battle::Update(const curtime_info& tmInfo)
{
// send collected data to players
// - 전투가 빨리 끝날 수도 있다.
// (WAVERIX) (090720) (CHANGES) void->bool (true: continued, false: overthan endtick)
//const BOOLEAN isTimeout = tmInfo.AcmTime > m_BattleCloseTimeFlag;
const BOOLEAN isTimeout = !m_CCUReport.Update(tmInfo, this);
const BOOLEAN isNeedClear = !!(m_ControlField & RCS_BATTLE_END_REQUESTED);
if(isTimeout || isNeedClear)
{
__WAVERIX_LOG_FORMAT_0S1N("On Battle End Condition", m_CodeOfSSQ);
{
BROADCASTER_MSG_AG_ZONE_SSQ_ROOM_CTRL_CMD& rMsgCMD = m_pMsgRouter->GetCMD_AG();
WAR_MSG_HDR* const pHeader = &rMsgCMD.MSG_HEADER;
MSGSUB_SSQ_S2S_BATTLE_END msg;
msg.CodeOfSSQ = m_CodeOfSSQ;
msg.SerializeTo(pHeader);
};
{
BROADCASTER_MSG_CG_ZONE_EX_SSQ_ROOM_CTRL_CMD& rMsgCMD = m_pMsgRouter->GetCMD_CG();
WAR_MSG_HDR* const pHeader = &rMsgCMD.MSG_HEADER;
MSGSUB_SSQ_PROTOCOL_NOTICE_BATTLE_END msg;
msg.SerializeTo(pHeader);
};
SSQUpdateCtrl_Clear* const pCtrl = TAllocNew(SSQUpdateCtrl_Clear_Info::value_type);
pCtrl->Migration(pCtrl, this);
pCtrl->Init(tmInfo);
pCtrl->RegisterUpdateCtrl(pCtrl);
TAllocDelete(updater_info::value_type, this);
return TRUE;
}
//
const WzTmFmt countdown = m_BattleCloseTimeFlag - tmInfo.AcmTime;
if(countdown != m_BattleCloseCountdown)
{
--m_BattleCloseCountdown;
//{
// BROADCASTER_MSG_CG_ZONE_EX_SSQ_ROOM_CTRL_CMD& rMsgCMD = m_pMsgRouter->GetCMD_CG();
// WAR_MSG_HDR* const pHeader = &rMsgCMD.MSG_HEADER;
// MSGSUB_SSQ_PROTOCOL_INFORMATION_REMAINED_TIME msg;
// msg.Remains = (SSQInfo::MINUTE_T)countdown;
// msg.SerializeTo(pHeader);
//};
}
// (WAVERIX) (090720) (CHANGES) void->bool (true: continued, false: overthan endtick)
//m_CCUReport.Update(tmInfo, this);
return TRUE;
}
//==================================================================================================
// <SSQUpdateCtrl><Clear State>
// - 전투 후 정리 단계...
VOID
SSQUpdateCtrl_Clear::Init(const curtime_info& tmInfo)
{
__UNUSED(tmInfo);
m_CurType = value;
m_Timer.SetTimer(interval);
m_ClearStep = eStep_Init;
// m_ControlField <- 이것은 Waiting & Battle에서 설정된 값으로 입력되므로, 이것에 따른 처리를 수행해야 한다.
// Waiting에서 전투 시작 취소가 발생했을 경우, 복귀시키고, 복귀 이후의 (입장료) 복구 처리를 수행한다.
// Battle에서의 전투 시작 취소가 발생했을 경우, 관련 정보 처리, 복귀 처리
// (공통)
// - 적절한 타임아웃 이후 강제 방 파괴... 안 나가고 버팅기는 상황도 충분히 존재할 수 있다.
WAVERIX_DEBUG_LOG_0S("");
if(m_ControlField & RCS_BATTLE_STARTED)
{
__WAVERIX_LOG_FORMAT_0S1N("전투 종료후 보상 처리", m_CodeOfSSQ);
}
else
{
__WAVERIX_LOG_FORMAT_0S1N("전투 취소후 복구 처리", m_CodeOfSSQ);
}
}
BOOL
SSQUpdateCtrl_Clear::Update(const curtime_info& tmInfo)
{
// restore all changed condition
// winner reward process
// expel players from SSQField
// close SSQField
switch(m_ClearStep)
{
case eStep_Init:
m_ClearStep = (eStep)(m_ClearStep+1);
break;
case eStep_Wait_Cleared:
_Step_Wait_Cleared(tmInfo);
break;
case eStep_Next:
_Step_Next(tmInfo);
break;
};
return TRUE;
}
VOID
SSQUpdateCtrl_Clear::_Step_Wait_Cleared(const curtime_info& tmInfo)
{
__UNUSED(tmInfo);
if(m_ControlField & RCS_CLEARED)
{
__WAVERIX_LOG_FORMAT_0S1N("Received Zone Clear Msg from GameServer", m_CodeOfSSQ);
m_ClearStep = (eStep)(m_ClearStep+1);
}
else if(!(m_ControlField & RCS_CLEARING))
{
BROADCASTER_MSG_AG_ZONE_SSQ_ROOM_CTRL_CMD& rMsgCMD = m_pMsgRouter->GetCMD_AG();
WAR_MSG_HDR* const pHeader = &rMsgCMD.MSG_HEADER;
{
MSGSUB_SSQ_S2S_FIELD_BLOCK msg;
msg.CodeOfSSQ = m_CodeOfSSQ;
msg.SerializeTo(pHeader);
};
{
MSGSUB_SSQ_S2S_BATTLE_CLEAR msg;
msg.CodeOfSSQ = m_CodeOfSSQ;
msg.SerializeTo(pHeader);
};
m_ControlField |= RCS_CLEARING;
}
}
VOID
SSQUpdateCtrl_Clear::_Step_Next(const curtime_info& tmInfo)
{
m_pSSQField->SetClearProcess();
//////////////////////////////////////////////////////////////////////////
SSQUpdateCtrl_None* const pCtrl = TAllocNew(SSQUpdateCtrl_None_Info::value_type);
pCtrl->Migration(pCtrl, this);
pCtrl->Init(tmInfo);
pCtrl->RegisterUpdateCtrl(pCtrl);
TAllocDelete(updater_info::value_type, this);
}