feat : 파티 구조 뼈대 완성
This commit is contained in:
@@ -540,7 +540,7 @@ public class RequestPartyPacket
|
|||||||
}
|
}
|
||||||
|
|
||||||
[ProtoMember(2)]
|
[ProtoMember(2)]
|
||||||
public long PartyId
|
public int PartyId
|
||||||
{
|
{
|
||||||
get;
|
get;
|
||||||
set;
|
set;
|
||||||
@@ -559,7 +559,7 @@ public class RequestPartyPacket
|
|||||||
public class UpdatePartyPacket
|
public class UpdatePartyPacket
|
||||||
{
|
{
|
||||||
[ProtoMember(1)]
|
[ProtoMember(1)]
|
||||||
public long PartyId
|
public int PartyId
|
||||||
{
|
{
|
||||||
get;
|
get;
|
||||||
set;
|
set;
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ using LiteNetLib;
|
|||||||
using LiteNetLib.Utils;
|
using LiteNetLib.Utils;
|
||||||
using MMOserver.Api;
|
using MMOserver.Api;
|
||||||
using MMOserver.Game.Channel;
|
using MMOserver.Game.Channel;
|
||||||
|
using MMOserver.Game.Party;
|
||||||
using MMOserver.Packet;
|
using MMOserver.Packet;
|
||||||
using MMOserver.Utils;
|
using MMOserver.Utils;
|
||||||
using ProtoBuf;
|
using ProtoBuf;
|
||||||
@@ -25,6 +26,7 @@ public class GameServer : ServerBase
|
|||||||
[(ushort)PacketCode.TRANSFORM_PLAYER] = OnTransformPlayer,
|
[(ushort)PacketCode.TRANSFORM_PLAYER] = OnTransformPlayer,
|
||||||
[(ushort)PacketCode.ACTION_PLAYER] = OnActionPlayer,
|
[(ushort)PacketCode.ACTION_PLAYER] = OnActionPlayer,
|
||||||
[(ushort)PacketCode.STATE_PLAYER] = OnStatePlayer,
|
[(ushort)PacketCode.STATE_PLAYER] = OnStatePlayer,
|
||||||
|
[(ushort)PacketCode.REQUEST_PARTY] = OnRequestParty,
|
||||||
};
|
};
|
||||||
userUuidGenerator = new UuidGenerator();
|
userUuidGenerator = new UuidGenerator();
|
||||||
}
|
}
|
||||||
@@ -462,4 +464,99 @@ public class GameServer : ServerBase
|
|||||||
byte[] data = PacketSerializer.Serialize<StatePlayerPacket>((ushort)PacketCode.STATE_PLAYER, packet);
|
byte[] data = PacketSerializer.Serialize<StatePlayerPacket>((ushort)PacketCode.STATE_PLAYER, packet);
|
||||||
BroadcastToChannel(channelId, data, peer);
|
BroadcastToChannel(channelId, data, peer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnRequestParty(NetPeer peer, int hashKey, byte[] payload)
|
||||||
|
{
|
||||||
|
RequestPartyPacket req = Serializer.Deserialize<RequestPartyPacket>(new ReadOnlyMemory<byte>(payload));
|
||||||
|
PartyManager pm = PartyManager.Instance;
|
||||||
|
|
||||||
|
switch (req.Type)
|
||||||
|
{
|
||||||
|
case PartyUpdateType.CREATE:
|
||||||
|
{
|
||||||
|
if (!pm.CreateParty(hashKey, req.PartyName, out PartyInfo? party))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdatePartyPacket notify = new UpdatePartyPacket
|
||||||
|
{
|
||||||
|
PartyId = party!.PartyId,
|
||||||
|
Type = PartyUpdateType.CREATE,
|
||||||
|
LeaderId = party.LeaderId,
|
||||||
|
PlayerId = hashKey,
|
||||||
|
PartyName = party.PartyName,
|
||||||
|
};
|
||||||
|
byte[] data = PacketSerializer.Serialize<UpdatePartyPacket>((ushort)PacketCode.UPDATE_PARTY, notify);
|
||||||
|
SendTo(peer, data);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case PartyUpdateType.JOIN:
|
||||||
|
{
|
||||||
|
if (!pm.JoinParty(hashKey, req.PartyId, out PartyInfo? party))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdatePartyPacket notify = new UpdatePartyPacket
|
||||||
|
{
|
||||||
|
PartyId = party!.PartyId,
|
||||||
|
Type = PartyUpdateType.JOIN,
|
||||||
|
LeaderId = party.LeaderId,
|
||||||
|
PlayerId = hashKey,
|
||||||
|
};
|
||||||
|
byte[] data = PacketSerializer.Serialize<UpdatePartyPacket>((ushort)PacketCode.UPDATE_PARTY, notify);
|
||||||
|
BroadcastToUsers(party.PartyMemberIds, data); // 새 멤버 포함 전원
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case PartyUpdateType.LEAVE:
|
||||||
|
{
|
||||||
|
if (!pm.LeaveParty(hashKey, out PartyInfo? party))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdatePartyPacket notify = new UpdatePartyPacket
|
||||||
|
{
|
||||||
|
PartyId = req.PartyId,
|
||||||
|
Type = PartyUpdateType.LEAVE,
|
||||||
|
LeaderId = party?.LeaderId ?? 0,
|
||||||
|
PlayerId = hashKey,
|
||||||
|
};
|
||||||
|
byte[] data = PacketSerializer.Serialize<UpdatePartyPacket>((ushort)PacketCode.UPDATE_PARTY, notify);
|
||||||
|
if (party != null)
|
||||||
|
BroadcastToUsers(party.PartyMemberIds, data); // 남은 멤버들에게
|
||||||
|
SendTo(peer, data); // 탈퇴자 본인에게
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case PartyUpdateType.DELETE:
|
||||||
|
{
|
||||||
|
if (!pm.DeleteParty(hashKey, req.PartyId, out PartyInfo? party))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdatePartyPacket notify = new UpdatePartyPacket
|
||||||
|
{
|
||||||
|
PartyId = party!.PartyId,
|
||||||
|
Type = PartyUpdateType.DELETE,
|
||||||
|
LeaderId = party.LeaderId,
|
||||||
|
};
|
||||||
|
byte[] data = PacketSerializer.Serialize<UpdatePartyPacket>((ushort)PacketCode.UPDATE_PARTY, notify);
|
||||||
|
BroadcastToUsers(party.PartyMemberIds, data); // 전원 (리더 포함)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void BroadcastToUsers(IEnumerable<int> userIds, byte[] data, DeliveryMethod method = DeliveryMethod.ReliableOrdered)
|
||||||
|
{
|
||||||
|
foreach (int userId in userIds)
|
||||||
|
{
|
||||||
|
if (sessions.TryGetValue(userId, out NetPeer? targetPeer))
|
||||||
|
{
|
||||||
|
SendTo(targetPeer, data, method);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
35
MMOTestServer/MMOserver/Game/Party/Party.cs
Normal file
35
MMOTestServer/MMOserver/Game/Party/Party.cs
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
namespace MMOserver.Game.Party;
|
||||||
|
|
||||||
|
public class PartyInfo
|
||||||
|
{
|
||||||
|
public static readonly int partyMemberMax = 3;
|
||||||
|
|
||||||
|
public int PartyId
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int LeaderId
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<int> PartyMemberIds
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
set;
|
||||||
|
} = new List<int>();
|
||||||
|
|
||||||
|
public string PartyName
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
set;
|
||||||
|
} = "";
|
||||||
|
|
||||||
|
public int GetPartyMemberCount()
|
||||||
|
{
|
||||||
|
return PartyMemberIds.Count;
|
||||||
|
}
|
||||||
|
}
|
||||||
152
MMOTestServer/MMOserver/Game/Party/PartyManager.cs
Normal file
152
MMOTestServer/MMOserver/Game/Party/PartyManager.cs
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
using MMOserver.Utils;
|
||||||
|
|
||||||
|
namespace MMOserver.Game.Party;
|
||||||
|
|
||||||
|
public class PartyManager : Singleton<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)
|
||||||
|
{
|
||||||
|
party = null;
|
||||||
|
|
||||||
|
if (playerPartyMap.ContainsKey(leaderId))
|
||||||
|
{
|
||||||
|
return false; // 이미 파티에 속해있음
|
||||||
|
}
|
||||||
|
|
||||||
|
int partyId = partyUuidGenerator.Create();
|
||||||
|
party = new PartyInfo
|
||||||
|
{
|
||||||
|
PartyId = partyId,
|
||||||
|
LeaderId = leaderId,
|
||||||
|
PartyName = partyName,
|
||||||
|
};
|
||||||
|
party.PartyMemberIds.Add(leaderId);
|
||||||
|
|
||||||
|
parties[partyId] = party;
|
||||||
|
playerPartyMap[leaderId] = 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);
|
||||||
|
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 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -540,7 +540,7 @@ public class RequestPartyPacket
|
|||||||
}
|
}
|
||||||
|
|
||||||
[ProtoMember(2)]
|
[ProtoMember(2)]
|
||||||
public long PartyId
|
public int PartyId
|
||||||
{
|
{
|
||||||
get;
|
get;
|
||||||
set;
|
set;
|
||||||
@@ -559,7 +559,7 @@ public class RequestPartyPacket
|
|||||||
public class UpdatePartyPacket
|
public class UpdatePartyPacket
|
||||||
{
|
{
|
||||||
[ProtoMember(1)]
|
[ProtoMember(1)]
|
||||||
public long PartyId
|
public int PartyId
|
||||||
{
|
{
|
||||||
get;
|
get;
|
||||||
set;
|
set;
|
||||||
|
|||||||
Reference in New Issue
Block a user