우선 기존코드의 문제점을 해결
class GameSession : public PacketSession
{
public:
~GameSession()
{
cout << "~GameSession" << endl;
}
virtual void OnConnected() override;
virtual void OnDisconnected() override;
virtual void OnRecvPacket(BYTE* buffer, int32 len) override;
virtual void OnSend(int32 len) override;
public:
// Player정보를 shared_ptr로 관리중인데
Vector<PlayerRef> _players;
class Player
{
public:
uint64 playerId = 0;
string name;
Protocol::PlayerType type = Protocol::PLAYER_TYPE_NONE;
// Player자체에서도 GameSession을 shared_ptr로 관리중
GameSessionRef ownerSession; // Cycle
};
void GameSession::OnDisconnected()
{
GSessionManager.Remove(static_pointer_cast<GameSession>(shared_from_this()));
if (_currentPlayer)
{
if (auto room = _room.lock())
room->DoAsync(&Room::Leave, _currentPlayer);
}
_currentPlayer = nullptr;
_players.clear();
}
JobTimer? -> 예를들어 설명하겠다. 어떤 캐릭터가 스킬을 써서 5초후 발동된다고 가정해보자 그럼 그 캐릭터의 오브젝트에서 5초를 카운트해야할까? 만약 캐릭터가 1000개라면 카운터가 1000개가 있어야하나??
JobTimer를 두어서 카운터를 전역으로 관리해보자
int main()
{
GRoom->DoTimer(1000, [] { cout << "Hello 1000" << endl; });
GRoom->DoTimer(2000, [] { cout << "Hello 2000" << endl; });
GRoom->DoTimer(3000, [] { cout << "Hello 3000" << endl; });
class JobQueue : public enable_shared_from_this<JobQueue>
{
public:
// ...
void DoTimer(uint64 tickAfter, CallbackType&& callback)
{
JobRef job = ObjectPool<Job>::MakeShared(std::move(callback));
GJobTimer->Reserve(tickAfter, shared_from_this(), job);
}
void JobTimer::Reserve(uint64 tickAfter, weak_ptr<JobQueue> owner, JobRef job)
{
const uint64 executeTick = ::GetTickCount64() + tickAfter;
JobData* jobData = ObjectPool<JobData>::Pop(owner, job);
WRITE_LOCK;
_items.push(TimerItem{ executeTick, jobData });
}
void JobTimer::Distribute(uint64 now)
{
// 한 번에 1 쓰레드만 통과
if (_distributing.exchange(true) == true)
return;
Vector<TimerItem> items;
{
WRITE_LOCK;
while (_items.empty() == false)
{
const TimerItem& timerItem = _items.top();
if (now < timerItem.executeTick)
break;
items.push_back(timerItem);
_items.pop();
}
}
for (TimerItem& item : items)
{
if (JobQueueRef owner = item.jobData->owner.lock())
owner->Push(item.jobData->job);
ObjectPool<JobData>::Push(item.jobData);
}
// 끝났으면 풀어준다
_distributing.store(false);
}
void JobTimer::Clear()
{
WRITE_LOCK;
while (_items.empty() == false)
{
const TimerItem& timerItem = _items.top();
ObjectPool<JobData>::Push(timerItem.jobData);
_items.pop();
}
}