Files
2022-10-26 12:25:11 +08:00

1068 lines
40 KiB
C++

#include "WorldServerPch.hxx"
#include "./GroupManager.h"
#include <time.h>
// utility
#include <SolarDateTime.h>
// program common
#include <Struct.h>
#include <PacketStruct_DW.h>
#include <PacketStruct_AW.h>
#include "WorldServer.h"
#include "Sessions/ServerSessionInWorld.h"
#include "Objects/WorldUserManager.h"
#include "Objects/WorldUser.h"
//==================================================================================================
ns_world::GroupManager* ns_world::GroupManager::instance_static_ = NULL;
ns_world::GroupManager* ns_world::GroupManager::CreateInstance()
{
// if the old util::Singleton support interface don't exist, the below code need implements
static struct AutoClosure {
AutoClosure(ns_world::GroupManager** static_table) : static_table_(static_table) {
}
~AutoClosure() {
if (*static_table_) {
TAllocDelete(ns_world::GroupManager, *static_table_);
}
*static_table_ = NULL;
}
ns_world::GroupManager** static_table_;
} auto_closure(&instance_static_);
//
if (instance_static_ != NULL) {
return instance_static_;
}
//
ns_world::GroupManager* provider = TAllocNew(ns_world::GroupManager);
// initialization...
instance_static_ = provider;
//
return instance_static_;
}
//==================================================================================================
ns_world::GroupManager::GroupManager()
{
}
ns_world::GroupManager::~GroupManager()
{
;{
GROUP_TABLE::const_iterator it = group_table_.begin();
const GROUP_TABLE::const_iterator end = group_table_.end();
for (; it != end; ++it)
{
GROUP_MEMBER_MAP* member_table = it->second;
TAllocDelete(GROUP_MEMBER_MAP, member_table);
};
};
}
//==================================================================================================
bool ns_world::GroupManager::IsGroupTransaction(ns_world::WorldUser* user)
{
const ns_world::GroupLink& group_link = user->GetGroupInfo();
return group_link.transaction_;
}
bool ns_world::GroupManager::StartGroupTransaction(ns_world::WorldUser* user)
{
ns_world::GroupLink* const editable_group_link = \
const_cast<ns_world::GroupLink*>(&user->GetGroupInfo());
if (editable_group_link->transaction_ != false) {
return false;
};
editable_group_link->transaction_ = true;
return true;
}
bool ns_world::GroupManager::EndGroupTransaction(ns_world::WorldUser* user)
{
ns_world::GroupLink* const editable_group_link = \
const_cast<ns_world::GroupLink*>(&user->GetGroupInfo());
if (editable_group_link->transaction_ == false) {
return false;
};
editable_group_link->transaction_ = false;
if (editable_group_link->transaction_data_.member_addition_data)
{
TAllocDelete(STRUCT_GROUP_MEMBER_INFO, \
editable_group_link->transaction_data_.member_addition_data);
editable_group_link->transaction_data_.member_addition_data = NULL;
};
return true;
}
bool ns_world::GroupManager::GetAddingPreventedFlag(ns_world::WorldUser* user)
{
const ns_world::GroupLink& group_link = user->GetGroupInfo();
return group_link.adding_prevented_;
}
void ns_world::GroupManager::ChangeAddingPreventedFlag(ns_world::WorldUser* user, bool prevent_flag)
{
ns_world::GroupLink* const editable_group_link = \
const_cast<ns_world::GroupLink*>(&user->GetGroupInfo());
editable_group_link->adding_prevented_ = prevent_flag;
}
//==================================================================================================
void ns_world::GroupManager::PrepareGroupServices(ns_world::WorldUser* user)
{
ns_world::GroupLink* const editable_group_link = \
const_cast<ns_world::GroupLink*>(&user->GetGroupInfo());
GROUP_MEMBER_MAP* member_table = editable_group_link->link_;
if (member_table != NULL)
{
member_table->clear();
// TODO: f110818.1L, check other data fields...
return;
};
const USERGUID user_guid = user->GetUserAccountInfo().user_guid;
GROUP_TABLE::const_iterator found = group_table_.find(user_guid);
if (found != group_table_.end())
{
member_table = found->second;
member_table->clear();
editable_group_link->link_ = member_table;
}
else
{
editable_group_link->link_ = TAllocNew(GROUP_MEMBER_MAP);
group_table_.insert(GROUP_TABLE::value_type(user_guid, editable_group_link->link_));
};
}
//==================================================================================================
bool ns_world::GroupManager::CanAddGroupMember(
ns_world::WorldUser* request_user, ns_world::WorldUser* dest_user,
const MSG_CW_GROUP_ADD_SYN* recv_msg)
{
MSG_CW_GROUP_ADD_NAK msg_nak;
msg_nak.m_byResult = msg_nak.NONE;
//
struct FailMessageSender {
FailMessageSender(ns_world::WorldUser* requester, MSG_CW_GROUP_ADD_NAK* msg_nak)
: requester_(requester), msg_nak_(msg_nak) {}
~FailMessageSender()
{
if (msg_nak_->m_byResult == msg_nak_->NONE) {
return;
};
assert(msg_nak_->m_byResult > msg_nak_->ERR_DEFAULT &&
msg_nak_->m_byResult < msg_nak_->MAX_COUNTS);
requester_->SendPacket(msg_nak_, sizeof(*msg_nak_));
};
ns_world::WorldUser* const requester_;
MSG_CW_GROUP_ADD_NAK* const msg_nak_;
private:
FailMessageSender(const FailMessageSender&) : requester_(0), msg_nak_(0) {}
void operator=(const FailMessageSender&) {}
} fail_msg_sender(request_user, &msg_nak);
//
if (request_user == NULL || dest_user == NULL)
{
msg_nak.m_byResult = msg_nak.ERR_DEFAULT;
SUNLOG(eFULL_LOG, "["__FUNCTION__"]Not found user");
return false;
};
if (request_user->GetGroupInfo().IsTransaction() != false)
{
//-- 해당 유저가 다른 유저와 그룹 신청을 시도한 상태
msg_nak.m_byResult = msg_nak.ALREADY_GROUP_REQ;
SUNLOG(eFULL_LOG, "["__FUNCTION__"]%s Transaction error", recv_msg->ptszMemeberName);
return false;
};
if (dest_user->GetGroupInfo().IsAddingPrevented() != false)
{
//-- 그룹 맴버가 그룹 요청 거부 토글을 활성화 시켰다.
msg_nak.m_byResult = msg_nak.ON_TOGGLE_ADDBLOCK;
SUNLOG(eFULL_LOG, "["__FUNCTION__"]%s Ad request block user", recv_msg->ptszMemeberName);
return false;
};
if (dest_user->GetGroupInfo().IsTransaction() != false)
{
//-- 상대방에게 누군가 이미 요청 중이다.
msg_nak.m_byResult = msg_nak.ALREADY_GROUP_REQ;
SUNLOG(eFULL_LOG, "["__FUNCTION__"]%s Already group request", recv_msg->ptszMemeberName);
return false;
};
if (!_tcsnicmp(request_user->GetCharName(), recv_msg->ptszMemeberName, MAX_CHARNAME_LENGTH))
{
//-- 추가하려는 그룹 맴버의 이름과 자신의 이름을 비교
msg_nak.m_byResult = msg_nak.NOT_ADD_ME;
SUNLOG(eFULL_LOG, "["__FUNCTION__"]%s Not request to myself", recv_msg->ptszMemeberName);
return false;
};
if (dest_user->GetConnectedCharInfo().char_level >=
request_user->GetConnectedCharInfo().char_level)
{
//-- 추가 하려는 그룹 맴버의 레벨이 같거나 높은 경우 실패
msg_nak.m_byResult = msg_nak.NOT_MATCHING_LEVEL;
SUNLOG(eFULL_LOG, "["__FUNCTION__"]%s Not matching level", recv_msg->ptszMemeberName);
return false;
};
#if defined(_CHINA)
if (request_user->GetConnectedCharInfo().char_level < HIGH_MEMBER_LEVEL ||
dest_user->GetConnectedCharInfo().char_level < LOW_MEMBER_LEVEL)
{
//-- 중국 요청으로 레벨이 80이상 상위 맴버, 10이상 하위 맴버 가능
msg_nak.m_byResult = msg_nak.NOT_MATCHING_LEVEL;
SUNLOG(eFULL_LOG, "["__FUNCTION__"]%s Not matching level", recv_msg->ptszMemeberName);
return false;
};
#endif
if (GetGroupHighMember(dest_user) == GROUP_HAVE_ONLINE_HIGH ||
GetGroupHighMember(dest_user) == GROUP_HAVE_OFFLINE_HIGH)
{
//-- 추가 하려는 케릭터가 상위 멤버를 가지고 있는가?
msg_nak.m_byResult = msg_nak.ALREADY_GROUP;
SUNLOG(eFULL_LOG, "["__FUNCTION__"]%s Already had high member", recv_msg->ptszMemeberName);
return false;
};
if (FindGroupMember(request_user, recv_msg->ptszMemeberName))
{
//-- 맴버가 그룹에 존재 할때
msg_nak.m_byResult = msg_nak.ALREADY_GROUP;
SUNLOG(eFULL_LOG, "["__FUNCTION__"]%s Already Member", recv_msg->ptszMemeberName);
return false;
};
if (GetLowMemberCount(request_user) >= MAX_GROUP_MEMBER_LOW_MAX)
{
//-- 총 그룹원 6명 하위 맴버는 5명까지 가능하다.
msg_nak.m_byResult = msg_nak.GROUP_OVERFLOW;
SUNLOG(eFULL_LOG, "["__FUNCTION__"]%s OverFlow member count", recv_msg->ptszMemeberName);
return false;
};
return true;
}
//_NA_0_20100527_ADD_CHAR_RELATION_MEMO 인자 변경
void ns_world::GroupManager::SendGroupMemberInfo(
ns_world::WorldUser* user,
const STRUCT_GROUP_MEMBER_INFO& group_member_info,
eGROUP_STAT group_stat)
{
MSG_CW_GROUP_ADD_ACK msg_ack;
if (STRUCT_GROUP_MEMBER_PACKET_INFO* const dest_info = &msg_ack.m_MemberInfo) {
*dest_info = group_member_info.GroupMemberInfo;
};
#ifdef _NA_0_20100527_ADD_CHAR_RELATION_MEMO
msg_ack.charGUID = group_member_info.charGUID;
#endif
msg_ack.m_GroupInfo = group_stat;
user->SendPacket(&msg_ack, sizeof(msg_ack));
}
//그룹 맴버 자료 구조에서 Guid를 기반으로 정보를 가져온다.
const STRUCT_GROUP_MEMBER_INFO*
ns_world::GroupManager::FindGroupMember(ns_world::WorldUser* user, DWORD char_guid) const
{
const ns_world::GroupLink& group_link = user->GetGroupInfo();
if (group_link.link_ == NULL) {
return NULL;
};
const GROUP_MEMBER_MAP& member_table = *group_link.link_;
GROUP_MEMBER_MAP::const_iterator found = member_table.find(char_guid);
if (found == member_table.end()) {
return NULL;
};
const STRUCT_GROUP_MEMBER_INFO* member_info = &found->second;
return member_info;
}
//그룹 맴버 자료 구조에서 이름을 기반으로 해당 정보를 찾는다.
const STRUCT_GROUP_MEMBER_INFO*
ns_world::GroupManager::FindGroupMember(ns_world::WorldUser* user, const TCHAR* member_name) const
{
const ns_world::GroupLink& group_link = user->GetGroupInfo();
if (group_link.link_ == NULL) {
return NULL;
};
// TODO: change search method.
const GROUP_MEMBER_MAP& member_table = *group_link.link_;
FOREACH_CONTAINER(const GROUP_MEMBER_MAP::value_type& node, member_table, GROUP_MEMBER_MAP)
{
const STRUCT_GROUP_MEMBER_INFO& member_info = node.second;
if (_tcsnicmp(member_info.GroupMemberInfo.ptszMemberName,
member_name, MAX_CHARNAME_LENGTH + 1) == 0)
{
return &member_info;
}
};
return NULL;
}
//유저가 가지고 있는 멤버 수 리턴
int ns_world::GroupManager::GetLowMemberCount(ns_world::WorldUser* user) const
{
//MAX값을 리턴 결과적으로 요청이 실패하게 된다.
const ns_world::GroupLink& group_link = user->GetGroupInfo();
if (group_link.link_ == NULL) {
return -1;
};
int number_of_low_group_members = 0;
const GROUP_MEMBER_MAP& member_table = *group_link.link_;
FOREACH_CONTAINER(const GROUP_MEMBER_MAP::value_type& node, member_table, GROUP_MEMBER_MAP)
{
const STRUCT_GROUP_MEMBER_INFO& member_info = node.second;
if (member_info.groupSTAT == GROUP_LEVEL_LOW) {
++number_of_low_group_members;
};
};
return number_of_low_group_members;
}
void ns_world::GroupManager::AddMemberInfo(
ns_world::WorldUser* user, const STRUCT_GROUP_MEMBER_INFO& group_member)
{
if (user == NULL) {
return;
};
if (this->GetGroupMemberCount(user) >= MAX_GROUP_MEMBER)
{
SUNLOG(eCRITICAL_LOG, "["__FUNCTION__"]OverFlow, WorldUser = %s, MemberGuid = %u" ,
user->GetCharName(), group_member.charGUID);
return;
}
if (this->FindGroupMember(user, group_member.charGUID) != NULL)
{
SUNLOG(eCRITICAL_LOG,
"["__FUNCTION__"]can't insert member. already exists. "
"WorldUser = %s, MemberGuid = %u",
user->GetCharName(), group_member.charGUID);
return;
};
GROUP_MEMBER_MAP* const member_table = user->GetGroupInfo().link_;
member_table->operator[](group_member.charGUID) = group_member;
return;
}
void ns_world::GroupManager::SendMemberList(ns_world::WorldUser* user)
{
const int member_count = GetGroupMemberCount(user);
int low_member_count = GetLowMemberCount(user);
//최대 맴버수보다 높을수는 없지만 높다고 해서 서비스를 멈출수는 없다.
//해당 부분파악을 위해 로그를 남기며 많은 그룹원을 가지고 있을 경우
//그룹 혜택을 받지 못하게 하고 메세지를 남기는 방법으로 해결한다.
assert(member_count <= MAX_GROUP_MEMBER && member_count >= 0);
if (member_count > MAX_GROUP_MEMBER || member_count < 0)
{
SUNLOG(eCRITICAL_LOG, "["__FUNCTION__"]WorldUser[%s]Groupmember count is incorrect = %d",
user->GetCharName(), member_count);
};
assert(low_member_count <= MAX_GROUP_MEMBER_LOW_MAX && low_member_count >= 0);
if (low_member_count > MAX_GROUP_MEMBER_LOW_MAX || low_member_count < 0)
{
SUNLOG(eCRITICAL_LOG, "["__FUNCTION__"]WorldUser[%s]Group Low Member Num Over = %d",
user->GetCharName(), low_member_count);
};
// CHANGES: f110811.5L, fixed buffer overrun occurrance possiblity.
// NOTE: 맴버수가 0명이라고 해도 응답 패킷을 클라이언트에게 보낸다.
MSG_CW_GROUP_GROUPLIST_ANS msg_ack;
msg_ack.byGroupMemberNum = 0;
const ns_world::GroupLink& group_link = user->GetGroupInfo();
const GROUP_MEMBER_MAP* member_table = group_link.link_;
if (member_table == NULL) {
user->SendPacket(&msg_ack, msg_ack.GetSize());
return;
};
int counter = 0;
GROUP_MEMBER_MAP::const_iterator it = member_table->begin();
const GROUP_MEMBER_MAP::const_iterator end = member_table->end();
for (; counter < _countof(msg_ack.sGroupMemberInfo) && it != end; ++counter, ++it)
{
const STRUCT_GROUP_MEMBER_INFO& group_info = it->second;
msg_ack.sGroupMemberInfo[counter] = group_info;
};
msg_ack.byGroupMemberNum = static_cast<uint8_t>(counter);
user->SendPacket(&msg_ack, msg_ack.GetSize());
SUNLOG(eFULL_LOG, "["__FUNCTION__"][%s][Guid:%u] Group List Send[Count:%d]",
user->GetCharName(), user->GetUserGuid(), counter);
}
// NOTE: f110818.1L, changes 'InsertMemberInfo' to 'StartGroupMemberAdditionTransaction'.
bool ns_world::GroupManager::StartGroupMemberAdditionTransaction(
ns_world::WorldUser* requester, ns_world::WorldUser* target_user)
{
ns_world::GroupLink* const editable_group_link = \
const_cast<ns_world::GroupLink*>(&requester->GetGroupInfo());
ns_world::GroupLink* const target_editable_group_link = \
const_cast<ns_world::GroupLink*>(&target_user->GetGroupInfo());
if (editable_group_link->transaction_ || target_editable_group_link->transaction_) {
return false;
};
if (FlowControl::FCAssert(editable_group_link->link_ != NULL) == false) {
return false;
};
if (FlowControl::FCAssert(target_editable_group_link->link_ != NULL) == false) {
return false;
};
if (editable_group_link->transaction_data_.member_addition_data == NULL) {
editable_group_link->transaction_data_.member_addition_data = \
TAllocNew(STRUCT_GROUP_MEMBER_INFO);
}
else {
SUNLOG(eCRITICAL_LOG, _T("|["__FUNCTION__"]|Msg=unexpected transaction states,%d"), 1);
};
if (target_editable_group_link->transaction_data_.member_addition_data == NULL) {
target_editable_group_link->transaction_data_.member_addition_data = \
TAllocNew(STRUCT_GROUP_MEMBER_INFO);
}
else {
SUNLOG(eCRITICAL_LOG, _T("|["__FUNCTION__"]|Msg=unexpected transaction states,%d"), 2);
};
STRUCT_GROUP_MEMBER_INFO* const member_info = \
editable_group_link->transaction_data_.member_addition_data;
ZeroMemory(member_info, sizeof(*member_info));
STRUCT_GROUP_MEMBER_INFO* const target_member_info = \
target_editable_group_link->transaction_data_.member_addition_data;
ZeroMemory(target_member_info, sizeof(*target_member_info));
// cross alignment
// (target_member_info <- char_info)
// (member_info <- target_char_info)
const ns_world::ConnectedCharInfo& char_info = requester->GetConnectedCharInfo();
;{
target_member_info->accumEXP = 0;
target_member_info->charGUID = char_info.char_guid;
target_member_info->groupSTAT = GROUP_LEVEL_LOW;
if (STRUCT_GROUP_MEMBER_PACKET_INFO* sub_data = &target_member_info->GroupMemberInfo)
{
sub_data->bOnline = true;
sub_data->byChannel = static_cast<uint8_t>(\
requester->GetUserAccountInfo().channel->GetChannelID());
sub_data->byClass = char_info.char_class;
sub_data->Level = char_info.char_level;
_tcsncpy(sub_data->ptszMemberName, char_info.char_name,
_countof(sub_data->ptszMemberName));
sub_data->ptszMemberName[_countof(sub_data->ptszMemberName) - 1] = _T('\0');
};
target_editable_group_link->transaction_ = true;
};
const ns_world::ConnectedCharInfo& target_char_info = target_user->GetConnectedCharInfo();
;{
member_info->accumEXP = 0;
member_info->charGUID = target_char_info.char_guid;
member_info->groupSTAT = GROUP_LEVEL_HIGH;
if (STRUCT_GROUP_MEMBER_PACKET_INFO* sub_data = &member_info->GroupMemberInfo)
{
sub_data->bOnline = true;
sub_data->byChannel = static_cast<uint8_t>(\
target_user->GetUserAccountInfo().channel->GetChannelID());
sub_data->byClass = target_char_info.char_class;
sub_data->Level = target_char_info.char_level;
_tcsncpy(sub_data->ptszMemberName, target_char_info.char_name,
_countof(sub_data->ptszMemberName));
sub_data->ptszMemberName[_countof(sub_data->ptszMemberName) - 1] = _T('\0');
};
editable_group_link->transaction_ = true;
};
return true;
};
bool ns_world::GroupManager::EndGroupMemberAdditionTransaction(
ns_world::WorldUser* transaction_user, bool succeed,
bool opposite_user_online_state, eGROUP_STAT opposite_user_group_stat)
{
ns_world::GroupLink* const editable_group_link = \
const_cast<ns_world::GroupLink*>(&transaction_user->GetGroupInfo());
if (editable_group_link->link_ == NULL)
{ // clear & realigned state?
assert(!"unexpected state");
return false;
};
if (editable_group_link->transaction_ == false ||
editable_group_link->transaction_data_.member_addition_data == NULL)
{
assert(!"unexpected state");
return false;
};
editable_group_link->transaction_ = false;
STRUCT_GROUP_MEMBER_INFO member_info = \
*editable_group_link->transaction_data_.member_addition_data;
;{
TAllocDelete(STRUCT_GROUP_MEMBER_INFO,
editable_group_link->transaction_data_.member_addition_data);
editable_group_link->transaction_data_.member_addition_data = NULL;
};
if (succeed == false) {
return true;
};
member_info.GroupMemberInfo.bOnline = opposite_user_online_state;
this->AddMemberInfo(transaction_user, member_info);
//
// Synchronization
// Send group member information...
//
this->UpdateGroupInfo(transaction_user);
//_NA_0_20100527_ADD_CHAR_RELATION_MEMO 인자 변경
this->SendGroupMemberInfo(transaction_user, member_info, opposite_user_group_stat);
//
return true;
}
void ns_world::GroupManager::RemoveMemberInfo(ns_world::WorldUser* user, DWORD target_char_guid)
{
ns_world::GroupLink* editable_group_link = \
const_cast<ns_world::GroupLink*>(&user->GetGroupInfo());
if (editable_group_link->link_ == NULL) {
return;
};
GROUP_MEMBER_MAP* const member_table = editable_group_link->link_;
GROUP_MEMBER_MAP::iterator found = member_table->find(target_char_guid);
if (found == member_table->end())
{
SUNLOG(eCRITICAL_LOG, _T("|["__FUNCTION__"]|Msg=can't found target character, ")
_T("actor:target={'%s',%d:%d}|"),
user->GetCharName(), user->GetConnectedCharInfo().char_guid, target_char_guid);
return;
};
member_table->erase(found);
}
//그룹 맴버들에게 로그인한 사실을 알린다. 맴버들의 온/오프라인 데이터도 바꿔준다.
void ns_world::GroupManager::NotifyLoginState(ns_world::WorldUser* user)
{
const ns_world::GroupLink& group_link = user->GetGroupInfo();
const GROUP_MEMBER_MAP* member_table = group_link.link_;
if (member_table == NULL) {
return;
};
MSG_CW_GROUP_LOGIN_BRD msg_brd;
strncpy(msg_brd.ptszMemeberName, user->GetCharName(), _countof(msg_brd.ptszMemeberName));
msg_brd.ptszMemeberName[_countof(msg_brd.ptszMemeberName) - 1] = '\0';
msg_brd.byChannel = static_cast<uint8_t>(user->GetUserAccountInfo().channel->GetChannelID());
//
const DWORD actor_char_guid = user->GetCharGuid();
//int counter = 0;
ns_world::WorldUserManager* const user_manager = ns_world::WorldUserManager::Instance();
GROUP_MEMBER_MAP::const_iterator it = member_table->begin();
const GROUP_MEMBER_MAP::const_iterator end = member_table->end();
for (; /*counter < MAX_GROUP_MEMBER &&*/ it != end; /*++counter,*/ ++it)
{
DWORD char_guid = it->first;
ns_world::WorldUser* member = user_manager->FindUserByCharGuid(char_guid);
if (member == NULL) {
continue;
};
SetMemeberConnectStat(member, actor_char_guid, true);
UpdateGroupInfo(member);
member->SendPacket(&msg_brd, sizeof(msg_brd));
};
}
//그룹 맴버들에게 로그아웃 한 사실을 알린다. 맴버들의 온/오프라인 데이터도 바꿔준다.
void ns_world::GroupManager::NotifyLogoutState(ns_world::WorldUser* user)
{
const ns_world::GroupLink& group_link = user->GetGroupInfo();
const GROUP_MEMBER_MAP* member_table = group_link.link_;
if (member_table == NULL) {
return;
};
MSG_CW_GROUP_LOGOUT_BRD msg_brd;
strncpy(msg_brd.ptszMemeberName, user->GetCharName(), _countof(msg_brd.ptszMemeberName));
msg_brd.ptszMemeberName[_countof(msg_brd.ptszMemeberName) - 1] = '\0';
//
const DWORD actor_char_guid = user->GetCharGuid();
//int counter = 0;
ns_world::WorldUserManager* const user_manager = ns_world::WorldUserManager::Instance();
GROUP_MEMBER_MAP::const_iterator it = member_table->begin();
const GROUP_MEMBER_MAP::const_iterator end = member_table->end();
for (; /*counter < MAX_GROUP_MEMBER &&*/ it != end; /*++counter,*/ ++it)
{
DWORD char_guid = it->first;
ns_world::WorldUser* member = user_manager->FindUserByCharGuid(char_guid);
if (member == NULL) {
continue;
};
SetMemeberConnectStat(member, actor_char_guid, false);
UpdateGroupInfo(member);
member->SendPacket(&msg_brd, sizeof(msg_brd));
};
}
//상위 맴버에게 지정된 경험치를 보너스로 준다.
//상위 맴버가 로그아웃 상태(케릭터 선택창 포함)라면 경험치를 저장하게 된다.
void ns_world::GroupManager::NotifyEXPState(ns_world::WorldUser* user)
{
const ns_world::GroupLink& group_link = user->GetGroupInfo();
const GROUP_MEMBER_MAP* member_table = group_link.link_;
if (member_table == NULL || member_table->empty()) {
return;
};
// WARNING: f110811.5L, ... must be checked whether 'user' is already connected by character
const ns_world::ConnectedCharInfo& actor_char_info = user->GetConnectedCharInfo();
MSG_AW_STATUS_GROUP_ADDEXP_CMD msg_cmd;
;{
//msg_cmd.m_CharGuid = high_group_member_info.charGUID;
msg_cmd.m_Amount = group_link.stacked_bonus_exp_;
_tcsncpy(msg_cmd.m_szCharName, user->GetCharName(),
_countof(msg_cmd.m_szCharName));
msg_cmd.m_szCharName[_countof(msg_cmd.m_szCharName) - 1] = '\0';
};
ns_world::WorldUserManager* const user_manager = ns_world::WorldUserManager::Instance();
GROUP_MEMBER_MAP::const_iterator it = member_table->begin();
const GROUP_MEMBER_MAP::const_iterator end = member_table->end();
for (; it != end; ++it)
{
const STRUCT_GROUP_MEMBER_INFO& high_group_member_info = it->second;
ns_world::WorldUser* high_memeber_user = \
user_manager->FindUserByCharGuid(high_group_member_info.charGUID);
#ifdef _NA_20101026_CHANGE_GROUP_BONUS_APPLY_WAY
if ((high_group_member_info.groupSTAT != GROUP_LEVEL_LOW) ||
(actor_char_info.char_level <= high_group_member_info.GroupMemberInfo.Level))
{
//-- 상위 멤버이거나 레벨이 높으면
continue;
};
#else
if ((high_group_member_info.groupSTAT != GROUP_LEVEL_HIGH) ||
(actor_char_info.char_level >= high_group_member_info.GroupMemberInfo.Level))
{
//-- 상위 멤버이거나 레벨이 높으면
continue;
};
#endif
if ((high_memeber_user == NULL) ||
(high_memeber_user->GetConnectedCharInfo().zone_type == eZONETYPE_CHARSELECT))
{
//-- 멤버가 없거나 케릭터 선택창 이면
SendAccumEXP(actor_char_info.user_guid, high_group_member_info.charGUID,
group_link.stacked_bonus_exp_);
continue;
};
// CHANGES: f110811.5L, ... changes flow control 'return' to 'continue'
if (high_memeber_user->EnabledSendMessage() == false) {
continue;
};
//
;{ // set up 'high_group_member_info' information
// NOTE: waverix said, "I don't understand"
msg_cmd.m_CharGuid = high_group_member_info.charGUID;
};
high_memeber_user->SendPacket(&msg_cmd, sizeof(msg_cmd));
//
};
}
//하위 맴버에게 보너스 금액을 준다.하위 맴버가 오프라인이면 무시한다.
void ns_world::GroupManager::NotifyMoneyState(ns_world::WorldUser* user)
{
const ns_world::GroupLink& group_link = user->GetGroupInfo();
const GROUP_MEMBER_MAP* member_table = group_link.link_;
if (member_table == NULL || member_table->empty()) {
return;
};
// WARNING: f110811.5L, ... must be checked whether 'user' is already connected by character
const ns_world::ConnectedCharInfo& actor_char_info = user->GetConnectedCharInfo();
MSG_AW_STATUS_GROUP_ADDMONEY_CMD msg_cmd;
;{
//send_msg.m_CharGuid = low_group_member_Info.charGUID;
msg_cmd.m_Amount = group_link.stacked_bonus_money_;
_tcsncpy(msg_cmd.m_szCharName, user->GetCharName(),
_countof(msg_cmd.m_szCharName));
msg_cmd.m_szCharName[_countof(msg_cmd.m_szCharName) - 1] = '\0';
};
ns_world::WorldUserManager* const user_manager = ns_world::WorldUserManager::Instance();
GROUP_MEMBER_MAP::const_iterator it = member_table->begin();
const GROUP_MEMBER_MAP::const_iterator end = member_table->end();
for (; it != end; ++it)
{
const STRUCT_GROUP_MEMBER_INFO& low_group_member_info = it->second;
ns_world::WorldUser* low_member_user = \
user_manager->FindUserByCharGuid(low_group_member_info.charGUID);
if (low_member_user == NULL) {
continue;
};
#ifdef _NA_20101026_CHANGE_GROUP_BONUS_APPLY_WAY
if ((low_group_member_info.groupSTAT != GROUP_LEVEL_HIGH) ||
(actor_char_info.char_level >= low_member_user->GetConnectedCharInfo().char_level))
{
continue;
}
#else
if ((low_group_member_info.groupSTAT != GROUP_LEVEL_LOW) ||
(actor_char_info.char_level <= low_member_user->GetConnectedCharInfo().char_level))
{
continue;
}
#endif
if (low_member_user->EnabledSendMessage() == false) {
continue;
};
;{ // set up 'low_group_member_info' information
msg_cmd.m_CharGuid = low_group_member_info.charGUID;
};
low_member_user->SendPacket(&msg_cmd, sizeof(msg_cmd));
};
}
//맴버들에게 레벨업 사실을 알린다. 상위 맴버와 레벨이 같아지면 더이상 그룹 효과가 지속될 수
//없음을 클라이언트 측에서 알려준다.
void ns_world::GroupManager::NotifyLevelUpState(ns_world::WorldUser* user)
{
const ns_world::GroupLink& group_link = user->GetGroupInfo();
const GROUP_MEMBER_MAP* member_table = group_link.link_;
if (member_table == NULL || member_table->empty()) {
return;
};
// WARNING: f110811.5L, ... must be checked whether 'user' is already connected by character
const ns_world::ConnectedCharInfo& actor_char_info = user->GetConnectedCharInfo();
MSG_CW_GROUP_LEVELUP_BRD msg_brd;
;{
_tcsncpy(msg_brd.ptszMemeberName, user->GetCharName(),
_countof(msg_brd.ptszMemeberName));
msg_brd.ptszMemeberName[_countof(msg_brd.ptszMemeberName) - 1] = '\0';
// WARNING: f110811.5L, sizeof(MSG_CW_GROUP_LEVELUP_BRD::charLevel) == 1
msg_brd.charLevel = actor_char_info.char_level;
};
ns_world::WorldUserManager* const user_manager = ns_world::WorldUserManager::Instance();
GROUP_MEMBER_MAP::const_iterator it = member_table->begin();
const GROUP_MEMBER_MAP::const_iterator end = member_table->end();
for (; it != end; ++it)
{
const STRUCT_GROUP_MEMBER_INFO& group_member_info = it->second;
ns_world::WorldUser* member_user = \
user_manager->FindUserByCharGuid(group_member_info.charGUID);
if (member_user == NULL) {
continue;
};
if (member_user->EnabledSendMessage() == false) {
continue;
};
member_user->SendPacket(&msg_brd, sizeof(msg_brd));
};
}
void ns_world::GroupManager::SendAroundMember(ns_world::WorldUser* user, eSTAT_ALAM member_stat)
{
int group_num = GetGroupMemberCount(user);
if (group_num <= 0) {
return;
};
assert(group_num <= MAX_GROUP_MEMBER);
if (group_num > MAX_GROUP_MEMBER)
{
//매우 특수한 상황이나 해당 로직을 멈출수는 없다. 확인후 후처리
SUNLOG(eCRITICAL_LOG, "["__FUNCTION__"]%s Member count is Over[count:%d]",
user->GetCharName(), group_num);
};
switch (member_stat)
{
case LOG_OUT:
{
NotifyLogoutState(user);
break;
}
case LOG_IN:
{
NotifyLoginState(user);
break;
}
case GET_MONEY:
{
NotifyMoneyState(user);
break;
}
case GET_EXP:
{
NotifyEXPState(user);
break;
}
case MEMBER_LEVELUP:
{
NotifyLevelUpState(user);
break;
}
}; //end 'switch'
}
//유저가 가지고 있는 GroupMemberMap에서 해당 유저의 온라인 오프라인 정보를 세팅한다.
bool ns_world::GroupManager::SetMemeberConnectStat(
ns_world::WorldUser* user, DWORD char_guid, bool connected)
{
STRUCT_GROUP_MEMBER_INFO* const editable_member_info =
const_cast<STRUCT_GROUP_MEMBER_INFO*>(FindGroupMember(user, char_guid));
if (editable_member_info == NULL) {
return false;
};
editable_member_info->GroupMemberInfo.bOnline = connected;
return true;
}
/*
//필드 입장시 케릭터의 그룹 정보를 세팅한다.
bool ns_world::GroupManager::SetMemberInfo(
ns_world::WorldUser* user, const STRUCT_GROUP_MEMBER_INFO& group_member_info)
{
ns_world::GroupLink* const editable_group_link = \
const_cast<ns_world::GroupLink*>(&user->GetGroupInfo());
GROUP_MEMBER_MAP* member_table = editable_group_link->link_;
if (member_table == NULL)
{
InitializeGroupMemberTable(user);
member_table = editable_group_link->link_;
};
if (GetGroupMemberCount(user) > MAX_GROUP_MEMBER)
{
SUNLOG(eCRITICAL_LOG, "["__FUNCTION__"]OverFlow, WorldUser = %s, MemberGuid = %u",
user->GetCharName(), group_member_info.charGUID);
}
if (FindGroupMember(user, group_member_info.charGUID) == NULL)
{
AddMemberInfo(user, group_member_info);
return true;
}
else
{
SUNLOG(eCRITICAL_LOG, "["__FUNCTION__"]Already Group, WorldUser[%s], MemberGuid = %u" ,
user->GetCharName().c_str(), group_member_info.charGUID);
return false;
}
}
*/
//유저의 그룹 맴버를 초기화 할때(유저 재접속 같은 경우)
void ns_world::GroupManager::ReleaseMember(ns_world::WorldUser* user)
{
ns_world::GroupLink* group_link = const_cast<ns_world::GroupLink*>(&user->GetGroupInfo());
if (group_link->link_ == NULL) {
return;
};
group_table_.erase(user->GetUserAccountInfo().user_guid);
TAllocDelete(GROUP_MEMBER_MAP, group_link->link_);
group_link->link_ = NULL;
if (group_link->transaction_data_.member_addition_data) {
TAllocDelete(STRUCT_GROUP_MEMBER_INFO, group_link->transaction_data_.member_addition_data);
group_link->transaction_data_.member_addition_data = NULL;
};
group_link->Clear();
group_link->user_ = user;
}
//유저의 현재 그룹 정보를 게임 서버와 동기화 시킨다
void ns_world::GroupManager::UpdateGroupInfo(ns_world::WorldUser* user)
{
ns_world::GroupLink* group_link = const_cast<ns_world::GroupLink*>(&user->GetGroupInfo());
;{
group_link->group_state_high_member_ = GROUP_HAVE_NONE;
group_link->group_state_low_member_ = GROUP_HAVE_NONE;
if (group_link->link_ == NULL) {
return;
};
};
const GROUP_MEMBER_MAP& member_table = *group_link->link_;
GROUP_MEMBER_MAP::const_iterator it = member_table.begin();
const GROUP_MEMBER_MAP::const_iterator end = member_table.end();
for (; it != end; ++it)
{
const STRUCT_GROUP_MEMBER_INFO& group_member_info = it->second;
const STRUCT_GROUP_MEMBER_PACKET_INFO& group_message_info = \
group_member_info.GroupMemberInfo;
;;;; if (group_member_info.groupSTAT == GROUP_LEVEL_HIGH)
{
group_link->group_state_high_member_ = (group_message_info.bOnline != false)
? GROUP_HAVE_ONLINE_HIGH : GROUP_HAVE_OFFLINE_HIGH;
}
else if (group_member_info.groupSTAT == GROUP_LEVEL_LOW &&
group_link->group_state_low_member_ != GROUP_HAVE_ONLINE_LOW)
{
group_link->group_state_high_member_ = (group_message_info.bOnline != false)
? GROUP_HAVE_ONLINE_LOW : GROUP_HAVE_OFFLINE_LOW;
};
//상하위 멤버가 전부 온라인으로세팅 되어있으면 더이상 살펴볼 필요 없다.
if (group_link->group_state_high_member_ == GROUP_HAVE_ONLINE_HIGH &&
group_link->group_state_low_member_ == GROUP_HAVE_ONLINE_LOW)
{
break;
};
}; //end 'for'
SendGroupInfoSet(user, group_link->group_state_high_member_,
group_link->group_state_low_member_);
}
//유저 상위 멤버 정보를 세팅한다(게임 서버 내용과 동기화)
void ns_world::GroupManager::SetGroupHighMember(ns_world::WorldUser* user, eGROUP_STAT eHighMember)
{
ns_world::GroupLink* group_link = const_cast<ns_world::GroupLink*>(&user->GetGroupInfo());
group_link->group_state_high_member_ = eHighMember;
}
//유저 하위 멤버 정보를 세팅한다(게임 서버 내용과 동기화)
void ns_world::GroupManager::SetGroupLowMember(ns_world::WorldUser* user, eGROUP_STAT eLowMember)
{
ns_world::GroupLink* group_link = const_cast<ns_world::GroupLink*>(&user->GetGroupInfo());
group_link->group_state_low_member_ = eLowMember;
}
//그룹 맴버 정보 동기화를 위한 정보를 게임 서버로 보낸다.
void ns_world::GroupManager::SendGroupInfoSet(
ns_world::WorldUser* user, eGROUP_STAT eHighMember, eGROUP_STAT eLowMember)
{
if (user->EnabledSendMessage() == NULL) {
SUNLOG(eCRITICAL_LOG, _T("|["__FUNCTION__"]|Msg=can't found linked server, UserGuid=%u"),
user->GetUserAccountInfo().user_guid);
return;
};
ns_world::WorldServer* const world_server = ns_world::WorldServer::Instance();
ns_world::ServerSessionInWorld* const dbproxy = world_server->GetGameDBProxy();
if (dbproxy == NULL) {
return; // NOTE: f110811.5L, this case means all services closed.
};
const ns_world::GroupLink& group_link = user->GetGroupInfo();
;{
MSG_AW_STATUS_GROUP_SET_MEMBERINFO_CMD msg_to_agent;
msg_to_agent.m_CharGuid = user->GetConnectedCharInfo().char_guid;
msg_to_agent.m_HighGroupinfo = group_link.group_state_high_member_;
msg_to_agent.m_LowGroupInfo = group_link.group_state_low_member_;
user->SendPacket(&msg_to_agent, sizeof(msg_to_agent));
};
//
;{
MSG_DW_GROUP_SET_MEMBERINFO_CMD msg_to_dbp;
msg_to_dbp.m_UserKey = user->GetUserAccountInfo().user_guid;
msg_to_dbp.m_HighGroupinfo = group_link.group_state_high_member_;
msg_to_dbp.m_LowGroupInfo = group_link.group_state_low_member_;
dbproxy->SendPacket(&msg_to_dbp, sizeof(msg_to_dbp));
};
}
bool ns_world::GroupManager::SendAccumEXP(
DWORD user_guid, CHARGUID high_member_guid, EXPTYPE accumulated_exp)
{
ns_world::WorldServer* const world_server = ns_world::WorldServer::Instance();
ns_world::ServerSessionInWorld* const dbproxy = world_server->GetGameDBProxy();
if (dbproxy == NULL) {
return false; // NOTE: f110811.5L, this case means all services closed.
};
MSG_DW_GROUP_ACCUMEXP_CMD msg_cmd;
;{
msg_cmd.m_UserKey = user_guid;
msg_cmd.m_HighCharGUID = high_member_guid;
msg_cmd.m_AccumEXP = DWORD(accumulated_exp); // changes 'EXPTYPE' to 'DWORD'
};
dbproxy->SendPacket(&msg_cmd, sizeof(msg_cmd));
return true;
}
//lastDate와 현재 시간차이가 chDate보다 크면 false 리턴
bool ns_world::GroupManager::IsOverBonusDate(DWORD64 last_date, INT check_date)
{
::tm stLastDate;
::SYSTEMTIME LASTTIME;
time_t t_LastTime, t_NowTime, t_AccumTime;
int date;
last_date = last_date * 100;//초시간을 맞추기 위해 100을 곱해준다.
util::YYYYMMDDHHMMSSToSYSTEMTIME(last_date, LASTTIME);//DWORD64를 LASTTIME으로 변경
stLastDate.tm_year = LASTTIME.wYear - 1900;
stLastDate.tm_mon = LASTTIME.wMonth - 1;
stLastDate.tm_mday = LASTTIME.wDay;
stLastDate.tm_hour = LASTTIME.wHour;
stLastDate.tm_min = LASTTIME.wMinute;
stLastDate.tm_sec = LASTTIME.wSecond;
//util::GetDateTime_YYMMDDHHMMSS() : time()으로 구한 시간을 DWORD64로 변경
//util::GetDateTime_YYMMDDHHMM()
t_LastTime = ::mktime(&stLastDate);
util::TimeSync::time(&t_NowTime);
t_AccumTime = t_NowTime - t_LastTime;
date = (INT)t_AccumTime / (3600*24);
if (date > check_date) {
return false;
};
return true;
}
void ns_world::GroupManager::SendAccumEXPMemo(ns_world::WorldUser* user)
{
ns_world::WorldServer* const world_server = ns_world::WorldServer::Instance();
ns_world::ServerSessionInWorld* const dbproxy = world_server->GetGameDBProxy();
if (dbproxy == NULL) {
return; // NOTE: f110811.5L, this case means all services closed.
};
MSG_DW_MEMO_SEND_SYN sendMsg;
sendMsg.m_dwKey = 0; // 시스템으로부터의 쪽지
sendMsg.m_SendGuid = 0; // 시스템으로부터의 쪽지
//_NA_20100812_INTEGRATE_SYSTEM_MEMO_DOMINATION = {
sendMsg.m_typeSystemMemo = NORMAL_MEMO;
sendMsg.m_recvItemCode = 0;
sendMsg.m_amountCost = 0;
sendMsg.m_recvGuid = 0;
//}
_tcsncpy(sendMsg.m_ptszRecvName, user->GetCharName(), _countof(sendMsg.m_ptszRecvName));
sendMsg.m_ptszRecvName[_countof(sendMsg.m_ptszRecvName) - 1] = '\0';
_sntprintf(sendMsg.m_ptszMemo, _countof(sendMsg.m_ptszMemo), "%d|%d",
SYSMEMO_GROUP_MGR, user->GetGroupInfo().accumulated_exp_);
sendMsg.m_ptszMemo[_countof(sendMsg.m_ptszMemo) - 1] = '\0';
dbproxy->SendPacket(&sendMsg, sizeof(sendMsg));
}
//==================================================================================================
//==================================================================================================
//==================================================================================================