Fix: 플레이어 프로필 DB 연동, 파티 초대/추방 프로토콜 구현

- 채널 입장 시 API 서버에서 플레이어 프로필 로드 (레벨/스탯/위치)
- 채널 퇴장 시 위치/플레이타임 DB 저장 (SaveGameDataAsync)
- Player.cs에 AttackPower/AttackRange/SprintMultiplier/Experience 필드 추가
- ToPlayerInfo에서 전투 스탯 매핑 추가
- Session에 ChannelJoinedAt 추가 (플레이타임 계산용)
- PartyUpdateType에 INVITE/KICK 추가
- RequestPartyPacket에 TargetPlayerId 필드 추가
- GameServer에 INVITE/KICK 핸들러 구현
- Channel에 GetPeer() 메서드 추가
- RestApi에 GetPlayerProfileAsync/SaveGameDataAsync 추가

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-19 21:01:56 +09:00
parent f77219e9d2
commit 64855c5a69
7 changed files with 170 additions and 2 deletions

View File

@@ -129,6 +129,18 @@ public class RestApi : Singleton<RestApi>
[JsonPropertyName("sprintMultiplier")]
public double SprintMultiplier { get; set; }
[JsonPropertyName("lastPosX")]
public double LastPosX { get; set; }
[JsonPropertyName("lastPosY")]
public double LastPosY { get; set; }
[JsonPropertyName("lastPosZ")]
public double LastPosZ { get; set; }
[JsonPropertyName("lastRotY")]
public double LastRotY { get; set; }
}
// 레이드 채널 접속 여부 체크
@@ -192,6 +204,39 @@ public class RestApi : Singleton<RestApi>
return null;
}
// 게임 데이터 저장 (채널 퇴장 시 위치/플레이타임 저장)
public async Task<bool> SaveGameDataAsync(string username, float? posX, float? posY, float? posZ, float? rotY, long? playTimeDelta)
{
string url = AppConfig.RestApi.BaseUrl + "/api/internal/player/save?username=" + Uri.EscapeDataString(username);
var body = new Dictionary<string, object?>();
if (posX.HasValue) body["lastPosX"] = posX.Value;
if (posY.HasValue) body["lastPosY"] = posY.Value;
if (posZ.HasValue) body["lastPosZ"] = posZ.Value;
if (rotY.HasValue) body["lastRotY"] = rotY.Value;
if (playTimeDelta.HasValue) body["playTimeDelta"] = playTimeDelta.Value;
for (int attempt = 1; attempt <= MAX_RETRY; attempt++)
{
try
{
HttpResponseMessage response = await httpClient.PostAsJsonAsync(url, body);
response.EnsureSuccessStatusCode();
return true;
}
catch (Exception ex) when (attempt < MAX_RETRY)
{
Log.Warning("[RestApi] 게임 데이터 저장 실패 (시도 {Attempt}/{Max}): {Message}", attempt, MAX_RETRY, ex.Message);
await Task.Delay(RETRY_DELAY);
}
catch (Exception ex)
{
Log.Error("[RestApi] 게임 데이터 저장 최종 실패 ({Max}회 시도): {Message}", MAX_RETRY, ex.Message);
}
}
return false;
}
private sealed class BossRaidAccessResponse
{
[JsonPropertyName("roomId")]