2 Commits

Author SHA1 Message Date
qornwh1
6e8a9c0b5e feat : 파티용 패킷 수정,
파티 CRUD 버그 수정
2026-03-11 19:36:00 +09:00
qornwh1
056ec8d0c3 feat : 파티 정보 업데이트 기능 추가 2026-03-11 15:09:06 +09:00
4 changed files with 154 additions and 17 deletions

View File

@@ -210,6 +210,39 @@ public class LoadChannelPacket
} = new List<ChannelInfo>(); } = new List<ChannelInfo>();
} }
// 채널 내 파티 정보 (INTO_CHANNEL 응답에 포함)
[ProtoContract]
public class PartyInfoData
{
[ProtoMember(1)]
public int PartyId
{
get;
set;
}
[ProtoMember(2)]
public int LeaderId
{
get;
set;
}
[ProtoMember(3)]
public List<int> MemberPlayerIds
{
get;
set;
} = new List<int>();
[ProtoMember(4)]
public string PartyName
{
get;
set;
}
}
// INTO_CHANNEL 클라->서버: 입장할 채널 ID / 서버->클라: 채널 내 나 이외 플레이어 목록 // INTO_CHANNEL 클라->서버: 입장할 채널 ID / 서버->클라: 채널 내 나 이외 플레이어 목록
[ProtoContract] [ProtoContract]
public class IntoChannelPacket public class IntoChannelPacket
@@ -227,6 +260,13 @@ public class IntoChannelPacket
get; get;
set; set;
} = new List<PlayerInfo>(); // 서버->클라: 채널 내 플레이어 목록 } = new List<PlayerInfo>(); // 서버->클라: 채널 내 플레이어 목록
[ProtoMember(3)]
public List<PartyInfoData> Parties
{
get;
set;
} = new List<PartyInfoData>(); // 서버->클라: 채널 내 파티 목록
} }
// UPDATE_CHANNEL_USER 유저 접속/나감 // UPDATE_CHANNEL_USER 유저 접속/나감

View File

@@ -169,9 +169,12 @@ public class GameServer : ServerBase
{ {
Log.Information("[GameServer] 세션 해제 HashKey={Key} Reason={Reason}", hashKey, info.Reason); Log.Information("[GameServer] 세션 해제 HashKey={Key} Reason={Reason}", hashKey, info.Reason);
// 같은 채널 유저들에게 나갔다고 알림
if (channelId >= 0 && player != null) if (channelId >= 0 && player != null)
{ {
// 파티 자동 탈퇴
HandlePartyLeaveOnExit(channelId, hashKey);
// 같은 채널 유저들에게 나갔다고 알림
SendExitChannelPacket(peer, hashKey, channelId, player); SendExitChannelPacket(peer, hashKey, channelId, player);
} }
} }
@@ -274,6 +277,7 @@ public class GameServer : ServerBase
PartyId = party.PartyId, PartyId = party.PartyId,
LeaderId = party.LeaderId, LeaderId = party.LeaderId,
MemberPlayerIds = new List<int>(party.PartyMemberIds), MemberPlayerIds = new List<int>(party.PartyMemberIds),
PartyName = party.PartyName
}); });
} }
@@ -304,7 +308,8 @@ public class GameServer : ServerBase
if (player == null) if (player == null)
{ {
Log.Warning("[GameServer] LOAD_GAME 플레이어 없음 HashKey={Key}", hashKey); Log.Warning("[GameServer] LOAD_GAME 플레이어 없음 HashKey={Key}", hashKey);
byte[] denied = PacketSerializer.Serialize<LoadGamePacket>((ushort)PacketCode.LOAD_GAME, new LoadGamePacket { IsAccepted = false }); byte[] denied =
PacketSerializer.Serialize<LoadGamePacket>((ushort)PacketCode.LOAD_GAME, new LoadGamePacket { IsAccepted = false });
SendTo(peer, denied); SendTo(peer, denied);
return; return;
} }
@@ -327,7 +332,8 @@ public class GameServer : ServerBase
// 특정 채널의 모든 유저에게 전송 (exclude 지정 시 해당 피어 제외) // 특정 채널의 모든 유저에게 전송 (exclude 지정 시 해당 피어 제외)
// Channel이 NetPeer를 직접 보유하므로 sessions 교차 조회 없음 // Channel이 NetPeer를 직접 보유하므로 sessions 교차 조회 없음
private void BroadcastToChannel(int channelId, byte[] data, NetPeer? exclude = null, DeliveryMethod method = DeliveryMethod.ReliableOrdered) private void BroadcastToChannel(int channelId, byte[] data, NetPeer? exclude = null,
DeliveryMethod method = DeliveryMethod.ReliableOrdered)
{ {
Channel.Channel channel = ChannelManager.Instance.GetChannel(channelId); Channel.Channel channel = ChannelManager.Instance.GetChannel(channelId);
foreach (NetPeer targetPeer in channel.GetConnectPeers()) foreach (NetPeer targetPeer in channel.GetConnectPeers())
@@ -377,7 +383,8 @@ public class GameServer : ServerBase
{ {
Log.Warning("[GameServer] INTO_CHANNEL 채널 인원 초과 HashKey={Key} ChannelId={ChannelId} UserCount={Count}/{Max}", Log.Warning("[GameServer] INTO_CHANNEL 채널 인원 초과 HashKey={Key} ChannelId={ChannelId} UserCount={Count}/{Max}",
hashKey, packet.ChannelId, channel.UserCount, channel.UserCountMax); hashKey, packet.ChannelId, channel.UserCount, channel.UserCountMax);
byte[] full = PacketSerializer.Serialize<IntoChannelPacket>((ushort)PacketCode.INTO_CHANNEL, new IntoChannelPacket { ChannelId = -1 }); byte[] full = PacketSerializer.Serialize<IntoChannelPacket>((ushort)PacketCode.INTO_CHANNEL,
new IntoChannelPacket { ChannelId = -1 });
SendTo(peer, full); SendTo(peer, full);
return; return;
} }
@@ -410,6 +417,12 @@ public class GameServer : ServerBase
int channelId = cm.HasUser(hashKey); int channelId = cm.HasUser(hashKey);
Player? player = channelId >= 0 ? cm.GetChannel(channelId).GetPlayer(hashKey) : null; Player? player = channelId >= 0 ? cm.GetChannel(channelId).GetPlayer(hashKey) : null;
// 파티 자동 탈퇴
if (channelId >= 0)
{
HandlePartyLeaveOnExit(channelId, hashKey);
}
cm.RemoveUser(hashKey); cm.RemoveUser(hashKey);
Log.Debug("[GameServer] EXIT_CHANNEL HashKey={Key} PlayerId={PlayerId}", hashKey, packet.PlayerId); Log.Debug("[GameServer] EXIT_CHANNEL HashKey={Key} PlayerId={PlayerId}", hashKey, packet.PlayerId);
@@ -520,7 +533,7 @@ public class GameServer : ServerBase
PartyName = party.PartyName, PartyName = party.PartyName,
}; };
byte[] data = PacketSerializer.Serialize<UpdatePartyPacket>((ushort)PacketCode.UPDATE_PARTY, notify); byte[] data = PacketSerializer.Serialize<UpdatePartyPacket>((ushort)PacketCode.UPDATE_PARTY, notify);
SendTo(peer, data); BroadcastToChannel(channelId, data); // 채널 전체 파티 목록 갱신
break; break;
} }
case PartyUpdateType.JOIN: case PartyUpdateType.JOIN:
@@ -539,7 +552,7 @@ public class GameServer : ServerBase
PlayerId = hashKey, PlayerId = hashKey,
}; };
byte[] data = PacketSerializer.Serialize<UpdatePartyPacket>((ushort)PacketCode.UPDATE_PARTY, notify); byte[] data = PacketSerializer.Serialize<UpdatePartyPacket>((ushort)PacketCode.UPDATE_PARTY, notify);
BroadcastToUsers(party.PartyMemberIds, data); // 새 멤버 포함 전원 BroadcastToChannel(channelId, data); // 채널 전체 파티 목록 갱신
break; break;
} }
case PartyUpdateType.LEAVE: case PartyUpdateType.LEAVE:
@@ -552,18 +565,17 @@ public class GameServer : ServerBase
UpdatePartyPacket notify = new UpdatePartyPacket UpdatePartyPacket notify = new UpdatePartyPacket
{ {
PartyId = req.PartyId, PartyId = party.PartyId, Type = PartyUpdateType.DELETE, LeaderId = party?.LeaderId ?? 0, PlayerId = hashKey,
Type = PartyUpdateType.LEAVE,
LeaderId = party?.LeaderId ?? 0,
PlayerId = hashKey,
}; };
byte[] data = PacketSerializer.Serialize<UpdatePartyPacket>((ushort)PacketCode.UPDATE_PARTY, notify);
if (party != null) // 파티가 남아있으면 살린다.
if (party.PartyMemberIds.Count > 0)
{ {
BroadcastToUsers(party.PartyMemberIds, data); // 남은 멤버들에게 notify.Type = PartyUpdateType.LEAVE;
} }
SendTo(peer, data); // 탈퇴자 본인에게 byte[] data = PacketSerializer.Serialize<UpdatePartyPacket>((ushort)PacketCode.UPDATE_PARTY, notify);
BroadcastToChannel(channelId, data); // 채널 전체 파티 목록 갱신 (탈퇴자 포함)
break; break;
} }
case PartyUpdateType.DELETE: case PartyUpdateType.DELETE:
@@ -581,7 +593,27 @@ public class GameServer : ServerBase
LeaderId = party.LeaderId, LeaderId = party.LeaderId,
}; };
byte[] data = PacketSerializer.Serialize<UpdatePartyPacket>((ushort)PacketCode.UPDATE_PARTY, notify); byte[] data = PacketSerializer.Serialize<UpdatePartyPacket>((ushort)PacketCode.UPDATE_PARTY, notify);
BroadcastToUsers(party.PartyMemberIds, data); // 전원 (리더 포함) BroadcastToChannel(channelId, data); // 채널 전체 파티 목록 갱신
break;
}
case PartyUpdateType.UPDATE:
{
if (!pm.UpdateParty(hashKey, req.PartyId, req.PartyName, out PartyInfo? party))
{
SendError(peer, ErrorCode.PARTY_UPDATE_FAILED);
return;
}
UpdatePartyPacket notify = new UpdatePartyPacket
{
PartyId = req.PartyId,
Type = PartyUpdateType.UPDATE,
LeaderId = party?.LeaderId ?? 0,
PlayerId = hashKey,
PartyName = req.PartyName,
};
byte[] data = PacketSerializer.Serialize<UpdatePartyPacket>((ushort)PacketCode.UPDATE_PARTY, notify);
BroadcastToChannel(channelId, data); // 채널 전체 파티 목록 갱신
break; break;
} }
} }
@@ -656,6 +688,40 @@ public class GameServer : ServerBase
} }
} }
// 채널 퇴장/연결 해제 시 파티 자동 탈퇴 처리
private void HandlePartyLeaveOnExit(int channelId, int hashKey)
{
PartyManager pm = ChannelManager.Instance.GetChannel(channelId).GetPartyManager();
int? partyId = pm.GetPartyByPlayer(hashKey)?.PartyId;
if (partyId == null)
{
return; // 파티에 없으면 무시
}
pm.LeaveParty(hashKey, out PartyInfo? remaining);
// 0명 → DELETE, 남은 멤버 있음 → LEAVE
UpdatePartyPacket notify = remaining != null
? new UpdatePartyPacket
{
PartyId = partyId.Value,
Type = PartyUpdateType.LEAVE,
LeaderId = remaining.LeaderId,
PlayerId = hashKey,
}
: new UpdatePartyPacket
{
PartyId = partyId.Value,
Type = PartyUpdateType.DELETE,
LeaderId = -1,
PlayerId = hashKey,
};
byte[] data = PacketSerializer.Serialize<UpdatePartyPacket>((ushort)PacketCode.UPDATE_PARTY, notify);
BroadcastToChannel(channelId, data);
}
private void SendError(NetPeer peer, ErrorCode code) private void SendError(NetPeer peer, ErrorCode code)
{ {
ErrorPacket err = new ErrorPacket { Code = code }; ErrorPacket err = new ErrorPacket { Code = code };

View File

@@ -86,7 +86,8 @@ public class PartyManager
if (party.PartyMemberIds.Count == 0) if (party.PartyMemberIds.Count == 0)
{ {
DeletePartyInternal(partyId, party); DeletePartyInternal(partyId, party);
party = null; // id가 필요하다 그래서 참조해 둔다.
// party = null;
return true; return true;
} }
@@ -119,6 +120,27 @@ public class PartyManager
return true; 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() public IEnumerable<PartyInfo> GetAllParties()
{ {

View File

@@ -234,6 +234,13 @@ public class PartyInfoData
get; get;
set; set;
} = new List<int>(); } = new List<int>();
[ProtoMember(4)]
public string PartyName
{
get;
set;
}
} }
// INTO_CHANNEL 클라->서버: 입장할 채널 ID / 서버->클라: 채널 내 나 이외 플레이어 목록 // INTO_CHANNEL 클라->서버: 입장할 채널 ID / 서버->클라: 채널 내 나 이외 플레이어 목록
@@ -560,6 +567,7 @@ public enum ErrorCode : int
PARTY_JOIN_FAILED = 10022, PARTY_JOIN_FAILED = 10022,
PARTY_NOT_IN_PARTY = 10023, PARTY_NOT_IN_PARTY = 10023,
PARTY_DELETE_FAILED = 10024, PARTY_DELETE_FAILED = 10024,
PARTY_UPDATE_FAILED = 10025,
} }
// ERROR (서버 -> 클라) // ERROR (서버 -> 클라)
@@ -583,7 +591,8 @@ public enum PartyUpdateType
CREATE, CREATE,
DELETE, DELETE,
JOIN, JOIN,
LEAVE LEAVE,
UPDATE
} }
// REQUEST_PARTY (클라 -> 서버) - CREATE: PartyName 사용 / JOIN·LEAVE·DELETE: PartyId 사용 // REQUEST_PARTY (클라 -> 서버) - CREATE: PartyName 사용 / JOIN·LEAVE·DELETE: PartyId 사용