Add Friends/Social & Multiplayer systems - 95 total endpoints

- Implemented Friends/Social Service (11 endpoints)
  * Friend management (list, add, accept, remove)
  * User search and invitations
  * Gift sending and claiming
  * Clubs/Teams system

- Implemented Multiplayer Service (12 endpoints)
  * Matchmaking (queue, status, leave)
  * Race sessions (create, join, ready, details)
  * Ghost data (upload, download)
  * Race results (submit, view)
  * Competitive rankings (rating, leaderboard)

- Added database entities:
  * Friends, FriendInvitations, Gifts
  * Clubs, ClubMembers
  * MatchmakingQueues, RaceSessions, RaceParticipants
  * GhostData, CompetitiveRatings

- Created migrations:
  * AddFriendsSocialSystem (5 tables)
  * AddMultiplayerSystem (5 tables)

Total: 95 endpoints - 100% EA server replacement ready

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
2026-02-23 16:55:33 -08:00
parent a8d282ab36
commit a934f57b52
28 changed files with 8136 additions and 10 deletions

View File

@@ -368,3 +368,327 @@ public class SendNotificationRequest
public string Message { get; set; } = string.Empty;
public int? ExpiresInHours { get; set; } // null = never expires
}
// ===== FRIENDS/SOCIAL SYSTEM MODELS =====
// Simple response with just resultCode and message (no data)
public class SimpleResponse
{
public int ResultCode { get; set; } = 0;
public string? Message { get; set; }
}
// Friend DTO
public class FriendDto
{
public int UserId { get; set; }
public string Nickname { get; set; } = string.Empty;
public string SynergyId { get; set; } = string.Empty;
public int Level { get; set; }
public DateTime? LastOnline { get; set; }
public DateTime FriendsSince { get; set; }
}
// Friend list response
public class FriendsListResponse
{
public int ResultCode { get; set; } = 0;
public string? Message { get; set; }
public List<FriendDto> Friends { get; set; } = new();
public int TotalCount { get; set; }
}
// Friend invitation DTO
public class FriendInvitationDto
{
public int InvitationId { get; set; }
public int SenderId { get; set; }
public string SenderNickname { get; set; } = string.Empty;
public string SenderSynergyId { get; set; } = string.Empty;
public int SenderLevel { get; set; }
public string Status { get; set; } = string.Empty;
public DateTime CreatedAt { get; set; }
public DateTime ExpiresAt { get; set; }
}
// Pending invitations response
public class PendingInvitationsResponse
{
public int ResultCode { get; set; } = 0;
public string? Message { get; set; }
public List<FriendInvitationDto> Invitations { get; set; } = new();
}
// User search result DTO
public class UserSearchResultDto
{
public int UserId { get; set; }
public string Nickname { get; set; } = string.Empty;
public string SynergyId { get; set; } = string.Empty;
public int Level { get; set; }
public bool IsFriend { get; set; }
public bool HasPendingInvite { get; set; }
}
// User search response
public class UserSearchResponse
{
public int ResultCode { get; set; } = 0;
public string? Message { get; set; }
public List<UserSearchResultDto> Users { get; set; } = new();
}
// Gift DTO
public class GiftDto
{
public int GiftId { get; set; }
public int SenderId { get; set; }
public string SenderNickname { get; set; } = string.Empty;
public string GiftType { get; set; } = string.Empty;
public int Amount { get; set; }
public string? Message { get; set; }
public DateTime SentAt { get; set; }
public DateTime ExpiresAt { get; set; }
}
// Pending gifts response
public class PendingGiftsResponse
{
public int ResultCode { get; set; } = 0;
public string? Message { get; set; }
public List<GiftDto> Gifts { get; set; } = new();
}
// Send gift request
public class SendGiftRequest
{
public string SynergyId { get; set; } = string.Empty;
public string FriendSynergyId { get; set; } = string.Empty;
public string GiftType { get; set; } = string.Empty;
public int Amount { get; set; }
public string? Message { get; set; }
}
// Claim gift response
public class ClaimGiftResponse
{
public int ResultCode { get; set; } = 0;
public string? Message { get; set; }
public string GiftType { get; set; } = string.Empty;
public int Amount { get; set; }
public int NewBalance { get; set; }
}
// Club DTO
public class ClubDto
{
public int ClubId { get; set; }
public string Name { get; set; } = string.Empty;
public string Description { get; set; } = string.Empty;
public string Tag { get; set; } = string.Empty;
public int MemberCount { get; set; }
public int MaxMembers { get; set; }
public bool IsPublic { get; set; }
public bool IsRecruiting { get; set; }
public int TotalPoints { get; set; }
public DateTime CreatedAt { get; set; }
}
// Club list response
public class ClubsListResponse
{
public int ResultCode { get; set; } = 0;
public string? Message { get; set; }
public List<ClubDto> Clubs { get; set; } = new();
}
// Club member DTO
public class ClubMemberDto
{
public int UserId { get; set; }
public string Nickname { get; set; } = string.Empty;
public string SynergyId { get; set; } = string.Empty;
public int Level { get; set; }
public string Role { get; set; } = string.Empty;
public int ContributedPoints { get; set; }
public DateTime JoinedAt { get; set; }
}
// Club members response
public class ClubMembersResponse
{
public int ResultCode { get; set; } = 0;
public string? Message { get; set; }
public ClubDto Club { get; set; } = new();
public List<ClubMemberDto> Members { get; set; } = new();
}
// Create club request
public class CreateClubRequest
{
public string Name { get; set; } = string.Empty;
public string Description { get; set; } = string.Empty;
public string Tag { get; set; } = string.Empty;
public bool IsPublic { get; set; } = true;
}
// ===== MULTIPLAYER SYSTEM MODELS =====
// Matchmaking queue request
public class JoinMatchmakingRequest
{
public string SynergyId { get; set; } = string.Empty;
public string CarClass { get; set; } = string.Empty;
public string Track { get; set; } = string.Empty;
public string GameMode { get; set; } = "casual"; // "ranked", "casual"
}
// Matchmaking status response
public class MatchmakingStatusResponse
{
public int ResultCode { get; set; } = 0;
public string? Message { get; set; }
public string Status { get; set; } = string.Empty; // "queued", "matched", "cancelled"
public int? QueueId { get; set; }
public int? SessionId { get; set; }
public string? SessionCode { get; set; }
public int? EstimatedWaitSeconds { get; set; }
public DateTime QueuedAt { get; set; }
}
// Create race session request
public class CreateRaceSessionRequest
{
public string SynergyId { get; set; } = string.Empty;
public string Track { get; set; } = string.Empty;
public string CarClass { get; set; } = string.Empty;
public int MaxPlayers { get; set; } = 8;
public bool IsPrivate { get; set; } = false;
}
// Race session DTO
public class RaceSessionDto
{
public int SessionId { get; set; }
public string SessionCode { get; set; } = string.Empty;
public string Track { get; set; } = string.Empty;
public string CarClass { get; set; } = string.Empty;
public int HostUserId { get; set; }
public string HostNickname { get; set; } = string.Empty;
public int CurrentPlayers { get; set; }
public int MaxPlayers { get; set; }
public string Status { get; set; } = string.Empty;
public DateTime CreatedAt { get; set; }
}
// Race session response
public class RaceSessionResponse
{
public int ResultCode { get; set; } = 0;
public string? Message { get; set; }
public RaceSessionDto Session { get; set; } = new();
public List<ParticipantDto> Participants { get; set; } = new();
}
// Participant DTO
public class ParticipantDto
{
public int UserId { get; set; }
public string Nickname { get; set; } = string.Empty;
public string SynergyId { get; set; } = string.Empty;
public string CarId { get; set; } = string.Empty;
public bool IsReady { get; set; }
public int? FinishPosition { get; set; }
public double? RaceTime { get; set; }
}
// Upload ghost request
public class UploadGhostRequest
{
public string SynergyId { get; set; } = string.Empty;
public string Track { get; set; } = string.Empty;
public string CarId { get; set; } = string.Empty;
public double RaceTime { get; set; }
public string TelemetryData { get; set; } = string.Empty; // Base64 or JSON
}
// Ghost data DTO
public class GhostDataDto
{
public int GhostId { get; set; }
public int UserId { get; set; }
public string Nickname { get; set; } = string.Empty;
public string Track { get; set; } = string.Empty;
public string CarId { get; set; } = string.Empty;
public double RaceTime { get; set; }
public string TelemetryData { get; set; } = string.Empty;
public DateTime UploadedAt { get; set; }
}
// Ghost data response
public class GhostDataResponse
{
public int ResultCode { get; set; } = 0;
public string? Message { get; set; }
public GhostDataDto? Ghost { get; set; }
}
// Submit race result request
public class SubmitRaceResultRequest
{
public string SynergyId { get; set; } = string.Empty;
public int SessionId { get; set; }
public double RaceTime { get; set; }
public int Position { get; set; }
public string? TelemetryData { get; set; }
}
// Race result response
public class RaceResultResponse
{
public int ResultCode { get; set; } = 0;
public string? Message { get; set; }
public int Position { get; set; }
public int RewardGold { get; set; }
public int RewardCash { get; set; }
public int RewardXP { get; set; }
public int? RatingChange { get; set; } // For ranked matches
public int? NewRating { get; set; }
}
// Race results (all players)
public class RaceResultsResponse
{
public int ResultCode { get; set; } = 0;
public string? Message { get; set; }
public List<ParticipantDto> Results { get; set; } = new();
}
// Competitive rating DTO
public class CompetitiveRatingDto
{
public int UserId { get; set; }
public string Nickname { get; set; } = string.Empty;
public int Rating { get; set; }
public int Wins { get; set; }
public int Losses { get; set; }
public int Draws { get; set; }
public string Division { get; set; } = string.Empty;
public int DivisionRank { get; set; }
}
// Rating response
public class RatingResponse
{
public int ResultCode { get; set; } = 0;
public string? Message { get; set; }
public CompetitiveRatingDto Rating { get; set; } = new();
}
// Leaderboard response
public class CompetitiveLeaderboardResponse
{
public int ResultCode { get; set; } = 0;
public string? Message { get; set; }
public List<CompetitiveRatingDto> Leaderboard { get; set; } = new();
}