1. PlayerId 스푸핑 방지: OnTransformPlayer, OnActionPlayer, OnStatePlayer에서 브로드캐스트 전 packet.PlayerId = hashKey로 강제 교체 2. HP/MP 클라이언트 조작 방지: OnStatePlayer에서 범위 클램핑 (0 ≤ Hp ≤ MaxHp, 0 ≤ Mp ≤ MaxMp) 3. CreateParty 파티원 등록 누락 수정: - memberIds 파라미터 사용 시 모든 멤버를 playerPartyMap에 등록 - 리더 중복 추가 방지 (Contains 체크) 4. OnIntoChannel 채널 만석 유령 상태 방지: 이전 채널 제거 후 새 채널 입장 실패 시 이전 채널로 복귀 5. HandleAuth async 경합 방지: authenticatingTokens HashSet으로 동일 토큰 동시 인증 차단 6. 레이드 맵 미반환 수정: TryReleaseRaidMap 헬퍼 추가, OnChangeMap/OnSessionDisconnected에서 레이드 맵(1001+) 유저 0명 시 인스턴스 맵 해제 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
197 lines
4.8 KiB
C#
197 lines
4.8 KiB
C#
using MMOserver.Utils;
|
|
|
|
namespace MMOserver.Game.Party;
|
|
|
|
public class PartyManager
|
|
{
|
|
private readonly UuidGenerator partyUuidGenerator = new UuidGenerator();
|
|
|
|
// partyId → PartyInfo
|
|
private readonly Dictionary<int, PartyInfo> parties = new();
|
|
|
|
// playerId → partyId (한 플레이어는 하나의 파티만)
|
|
private readonly Dictionary<int, int> playerPartyMap = new();
|
|
|
|
// 파티 생성
|
|
public bool CreateParty(int leaderId, string partyName, out PartyInfo? party, List<int>? memeberIds = null)
|
|
{
|
|
if (playerPartyMap.ContainsKey(leaderId))
|
|
{
|
|
party = null;
|
|
return false; // 이미 파티에 속해있음
|
|
}
|
|
|
|
int partyId = partyUuidGenerator.Create();
|
|
|
|
if (memeberIds == null)
|
|
{
|
|
memeberIds = new List<int>();
|
|
}
|
|
|
|
// 리더 중복 방지: 기존 멤버 목록에 리더가 없을 때만 추가
|
|
if (!memeberIds.Contains(leaderId))
|
|
{
|
|
memeberIds.Add(leaderId);
|
|
}
|
|
|
|
party = new PartyInfo
|
|
{
|
|
PartyId = partyId,
|
|
LeaderId = leaderId,
|
|
PartyName = partyName,
|
|
PartyMemberIds = memeberIds
|
|
};
|
|
|
|
parties[partyId] = party;
|
|
|
|
// 모든 멤버를 playerPartyMap에 등록
|
|
foreach (int memberId in memeberIds)
|
|
{
|
|
playerPartyMap[memberId] = partyId;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// 파티 참가
|
|
public bool JoinParty(int playerId, int partyId, out PartyInfo? party)
|
|
{
|
|
party = null;
|
|
|
|
if (playerPartyMap.ContainsKey(playerId))
|
|
{
|
|
return false; // 이미 파티에 속해있음
|
|
}
|
|
|
|
if (!parties.TryGetValue(partyId, out party))
|
|
{
|
|
return false; // 파티 없음
|
|
}
|
|
|
|
if (party.GetPartyMemberCount() >= PartyInfo.partyMemberMax)
|
|
{
|
|
party = null;
|
|
return false; // 파티 인원 초과
|
|
}
|
|
|
|
party.PartyMemberIds.Add(playerId);
|
|
playerPartyMap[playerId] = partyId;
|
|
|
|
return true;
|
|
}
|
|
|
|
// 파티 탈퇴
|
|
public bool LeaveParty(int playerId, out PartyInfo? party)
|
|
{
|
|
party = null;
|
|
|
|
if (!playerPartyMap.TryGetValue(playerId, out int partyId))
|
|
{
|
|
return false; // 파티에 속해있지 않음
|
|
}
|
|
|
|
if (!parties.TryGetValue(partyId, out party))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
party.PartyMemberIds.Remove(playerId);
|
|
playerPartyMap.Remove(playerId);
|
|
|
|
// 마지막 멤버가 나가면 파티 해산
|
|
if (party.PartyMemberIds.Count == 0)
|
|
{
|
|
DeletePartyInternal(partyId, party);
|
|
// id가 필요하다 그래서 참조해 둔다.
|
|
// party = null;
|
|
return true;
|
|
}
|
|
|
|
// 리더가 나갔으면 남은 멤버 중 첫 번째를 리더로 승계
|
|
if (party.LeaderId == playerId)
|
|
{
|
|
party.LeaderId = party.PartyMemberIds[0];
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// 파티 해산 (리더만)
|
|
public bool DeleteParty(int leaderId, int partyId, out PartyInfo? party)
|
|
{
|
|
party = null;
|
|
|
|
if (!parties.TryGetValue(partyId, out party))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (party.LeaderId != leaderId)
|
|
{
|
|
party = null;
|
|
return false; // 리더만 해산 가능
|
|
}
|
|
|
|
DeletePartyInternal(partyId, party);
|
|
return true;
|
|
}
|
|
|
|
// 파티 업데이트
|
|
public bool UpdateParty(int leaderId, int partyId, string newPartyName, out PartyInfo? party)
|
|
{
|
|
party = null;
|
|
|
|
if (!parties.TryGetValue(partyId, out party))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (party.LeaderId != leaderId)
|
|
{
|
|
party = null;
|
|
return false; // 리더만 업데이트 가능
|
|
}
|
|
|
|
// 파티 이름 변경
|
|
party.PartyName = newPartyName;
|
|
return true;
|
|
}
|
|
|
|
// 전체 파티 목록 조회
|
|
public IEnumerable<PartyInfo> GetAllParties()
|
|
{
|
|
return parties.Values;
|
|
}
|
|
|
|
// 조회
|
|
public PartyInfo? GetParty(int partyId)
|
|
{
|
|
parties.TryGetValue(partyId, out PartyInfo? party);
|
|
return party;
|
|
}
|
|
|
|
public PartyInfo? GetPartyByPlayer(int playerId)
|
|
{
|
|
if (!playerPartyMap.TryGetValue(playerId, out int partyId))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
parties.TryGetValue(partyId, out PartyInfo? party);
|
|
return party;
|
|
}
|
|
|
|
// 삭제
|
|
private void DeletePartyInternal(int partyId, PartyInfo party)
|
|
{
|
|
// 남아 있는인원도 제거
|
|
foreach (int memberId in party.PartyMemberIds)
|
|
{
|
|
playerPartyMap.Remove(memberId);
|
|
}
|
|
|
|
parties.Remove(partyId);
|
|
partyUuidGenerator.Release(partyId);
|
|
}
|
|
}
|