添加项目文件。
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\Infrastructure\MongoRepository\MongoRepository.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,44 @@
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using MongoDB.Bson.Serialization.Attributes;
|
||||
using MongoRepository;
|
||||
using MongoRepository.Attributes;
|
||||
using MongoRepository.Config;
|
||||
|
||||
namespace ChessGameDomain
|
||||
{
|
||||
[BsonCollection("game_room", ServiceDatabases.Chess)]
|
||||
[BsonIgnoreExtraElements]
|
||||
public class ChessGameRoom : MongoBaseEntity
|
||||
{
|
||||
[Column(TypeName = "jsonb")]
|
||||
public List<RedisChessGameUser> users { get; set; } = new List<RedisChessGameUser>();
|
||||
public string RoomId { get; set; }
|
||||
|
||||
public DateTime CreateTime { get; set; }
|
||||
|
||||
public int gameType { get; set; }
|
||||
|
||||
public string password { get; set; }
|
||||
|
||||
public string createUserId { get; set; }
|
||||
|
||||
public int state { get; set; } = 1;
|
||||
public long gold { get; set; } = 1000;
|
||||
|
||||
public int robotCount { get; set; } = 0;
|
||||
|
||||
public Dictionary<string, long> EndGameRequests { get; set; } = new Dictionary<string, long>();
|
||||
|
||||
public long LastActiveTime { get; set; } = 0;
|
||||
|
||||
public int TimeoutSeconds { get; set; } = 60;
|
||||
|
||||
public int AfkTimeoutSeconds { get; set; } = 120;
|
||||
|
||||
public string TimeoutLoserUserId { get; set; } = "";
|
||||
|
||||
public string PendingWinUserId { get; set; } = "";
|
||||
|
||||
public bool IsGameEnded { get; set; } = false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ChessGameDomain
|
||||
{
|
||||
public class RedisChessGameUser
|
||||
{
|
||||
|
||||
public string Cid { get; set; }
|
||||
public string userId { get; set; }
|
||||
public string userName { get; set; }
|
||||
|
||||
public string userAvatar { get; set; }
|
||||
|
||||
public int type { get; set; }
|
||||
public bool IsLeave { get; set; } = false;
|
||||
|
||||
public bool IsReady { get; set; } = false;
|
||||
public long Gold { get; set; } = 0;
|
||||
|
||||
public long DisconnectTime { get; set; } = 0;
|
||||
|
||||
public bool IsAfk { get; set; } = false;
|
||||
|
||||
public long AfkTime { get; set; } = 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,600 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using ChessGameDomain;
|
||||
using ChessGameService.Param;
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using RedisService;
|
||||
using XAct;
|
||||
|
||||
namespace ChessGameService
|
||||
{
|
||||
public class ChessGameHub : Hub
|
||||
{
|
||||
public const string LobbyGroup = "chess_lobby";
|
||||
private readonly ILogger<ChessGameHub> _logger;
|
||||
private readonly IChessGameService _chessService;
|
||||
private readonly IRedisService _redisService;
|
||||
|
||||
public ChessGameHub(ILogger<ChessGameHub> logger, IChessGameService chessService, IRedisService redisService)
|
||||
{
|
||||
_logger = logger;
|
||||
_chessService = chessService;
|
||||
_redisService = redisService;
|
||||
}
|
||||
|
||||
private async Task JoinLobbyAsync(string connectionId)
|
||||
{
|
||||
var cid = string.IsNullOrWhiteSpace(connectionId) ? Context.ConnectionId : connectionId;
|
||||
await Groups.AddToGroupAsync(cid, LobbyGroup);
|
||||
}
|
||||
|
||||
private async Task LeaveLobbyAsync(string connectionId)
|
||||
{
|
||||
var cid = string.IsNullOrWhiteSpace(connectionId) ? Context.ConnectionId : connectionId;
|
||||
await Groups.RemoveFromGroupAsync(cid, LobbyGroup);
|
||||
}
|
||||
|
||||
private async Task JoinLobbyBatchAsync(IEnumerable<string> connectionIds)
|
||||
{
|
||||
foreach (var connectionId in connectionIds.Distinct())
|
||||
{
|
||||
try
|
||||
{
|
||||
await Groups.AddToGroupAsync(connectionId, LobbyGroup);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "[ChessLobby] 加入大厅组失败 cid={Cid}", connectionId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public override async Task OnConnectedAsync()
|
||||
{
|
||||
var cid = Context.ConnectionId;
|
||||
var reconnectUserId = Context.GetHttpContext()?.Request?.Query["userId"].ToString();
|
||||
|
||||
var room = !string.IsNullOrWhiteSpace(reconnectUserId)
|
||||
? _redisService.GetAllChessRooms()?.FirstOrDefault(r => r.users.Any(u => u.userId == reconnectUserId && (r.state == 1 || r.state == 2 || r.IsGameEnded)))
|
||||
: _chessService.GetUserRoom(cid);
|
||||
if (room != null)
|
||||
{
|
||||
var user = room.users.FirstOrDefault(c => c.Cid == cid || c.userId == reconnectUserId);
|
||||
if (user != null)
|
||||
{
|
||||
if (!user.IsLeave && user.Cid != cid)
|
||||
{
|
||||
await Clients.Caller.SendAsync("GameError", "用户已在其他连接中在线");
|
||||
|
||||
await base.OnConnectedAsync();
|
||||
return;
|
||||
}
|
||||
|
||||
user.Cid = cid;
|
||||
user.DisconnectTime = 0;
|
||||
user.IsLeave = false;
|
||||
room.robotCount = Math.Clamp(room.robotCount, 0, Math.Max(0, 2 - room.users.Count));
|
||||
_redisService.SetChessRoom(room);
|
||||
|
||||
var otherUser = room.users.FirstOrDefault(c => c.Cid != cid);
|
||||
if (otherUser != null)
|
||||
{
|
||||
await Clients.Group(room.RoomId).SendAsync("Reconnect", new
|
||||
{
|
||||
userId = user.userId,
|
||||
userName = user.userName
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
await Clients.Caller.SendAsync("BeginGame", room);
|
||||
|
||||
await Groups.AddToGroupAsync(cid, room.RoomId);
|
||||
}
|
||||
else
|
||||
{
|
||||
await JoinLobbyAsync(cid);
|
||||
}
|
||||
}
|
||||
|
||||
public override async Task OnDisconnectedAsync(Exception? exception)
|
||||
{
|
||||
var cid = Context.ConnectionId;
|
||||
|
||||
var room = _chessService.GetUserRoom(cid);
|
||||
if (room != null)
|
||||
{
|
||||
var user = room.users.FirstOrDefault(c => c.Cid == cid);
|
||||
|
||||
if (user != null)
|
||||
{
|
||||
if (room.state == 2 && room.users.Count == 2)
|
||||
{
|
||||
user.DisconnectTime = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
|
||||
user.IsLeave = true;
|
||||
var timeoutSeconds = room.TimeoutSeconds > 0 ? room.TimeoutSeconds : 60;
|
||||
user.Cid = string.Empty;
|
||||
|
||||
if (room.users.All(u => u.IsLeave))
|
||||
{
|
||||
var affectedUserIds = room.users.Select(u => u.userId).Distinct().ToList();
|
||||
_chessService.ExitRoom(room);
|
||||
await Task.WhenAll(affectedUserIds.Select(id => _chessService.UpdateUserGameType(id, 0)));
|
||||
await Clients.Group(LobbyGroup).SendAsync("ExitRoomNotice", room);
|
||||
}
|
||||
else
|
||||
{
|
||||
_redisService.SetChessRoom(room);
|
||||
|
||||
await Clients.Group(room.RoomId).SendAsync("Disconnect", new
|
||||
{
|
||||
userId = user.userId,
|
||||
disconnectTime = user.DisconnectTime,
|
||||
timeoutSeconds = timeoutSeconds
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
var affectedUserIds = room.createUserId == user.userId
|
||||
? room.users.Where(u => u != null).Select(u => u.userId).Distinct().ToList()
|
||||
: new List<string> { user.userId };
|
||||
var (_, _, updatedRoom) = _chessService.ExitRoom(room.RoomId, user.userId);
|
||||
await Task.WhenAll(affectedUserIds.Select(id => _chessService.UpdateUserGameType(id, 0)));
|
||||
await Groups.RemoveFromGroupAsync(Context.ConnectionId, room.RoomId);
|
||||
if (updatedRoom != null && updatedRoom.users != null && updatedRoom.users.Count > 0)
|
||||
{
|
||||
await Clients.Group(updatedRoom.RoomId).SendAsync("BeginGame", updatedRoom);
|
||||
}
|
||||
await Clients.Group(LobbyGroup).SendAsync("ExitRoomNotice", updatedRoom ?? room);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async Task JoinGame(string gameId, string pwd, string userId)
|
||||
{
|
||||
string cid = Context.ConnectionId;
|
||||
var (success, message, room) = await _chessService.JoinGameAsync(gameId, pwd, userId, cid);
|
||||
|
||||
if (!success)
|
||||
{
|
||||
await Clients.Caller.SendAsync("GameError", message);
|
||||
return;
|
||||
}
|
||||
|
||||
await _chessService.UpdateUserGameType(userId, 3, room.RoomId);
|
||||
|
||||
await Groups.AddToGroupAsync(cid, room.RoomId);
|
||||
await LeaveLobbyAsync(cid);
|
||||
await Clients.Caller.SendAsync("JoinRoom", room);
|
||||
await Clients.Group(room.RoomId).SendAsync("BeginGame", room);
|
||||
await Clients.Group(room.RoomId).SendAsync("RoomStateNotice", room);
|
||||
await Clients.Group(LobbyGroup).SendAsync("JoinRoomNotice", room);
|
||||
if (room.users.Count == 2 && room.state == 1)
|
||||
{
|
||||
var redUser = room.users.FirstOrDefault(u => u.type == 2);
|
||||
await Clients.Group(room.RoomId).SendAsync("GoldConfigRequired", new
|
||||
{
|
||||
targetUserId = redUser?.userId,
|
||||
room
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public async Task StartGame(string gameId, string userId)
|
||||
{
|
||||
if (!IsCaller(gameId, userId)) return;
|
||||
var (success, message, room) = _chessService.StartGame(gameId, userId);
|
||||
if (!success)
|
||||
{
|
||||
await Clients.Caller.SendAsync("GameError", message);
|
||||
return;
|
||||
}
|
||||
|
||||
var redUser = room.users.FirstOrDefault(u => u.type == 2);
|
||||
await Clients.Group(room.RoomId).SendAsync("GoldConfigRequired", new
|
||||
{
|
||||
targetUserId = redUser?.userId,
|
||||
room
|
||||
});
|
||||
}
|
||||
|
||||
public async Task GameReady(string gameId, string userId)
|
||||
{
|
||||
if (!IsCaller(gameId, userId)) return;
|
||||
var (success, message, allReady) = _chessService.GameReady(gameId, userId);
|
||||
|
||||
if (!success)
|
||||
{
|
||||
await Clients.Caller.SendAsync("GameError", message);
|
||||
return;
|
||||
}
|
||||
|
||||
var room = _chessService.GetRoom(gameId);
|
||||
await Clients.Groups(room.RoomId).SendAsync("ReadyNotice", userId);
|
||||
|
||||
if (allReady)
|
||||
{
|
||||
await Clients.Groups(room.RoomId).SendAsync("AllBeginTime", room);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task EndGame(string gameId, string winUserId)
|
||||
{
|
||||
if (!IsCallerInRoom(gameId)) return;
|
||||
var settledConnectionIds = GetRoomConnectionIds(gameId);
|
||||
var (success, message, room) = await _chessService.EndGame(gameId, winUserId);
|
||||
|
||||
if (!success)
|
||||
{
|
||||
await Clients.Caller.SendAsync("GameError", message);
|
||||
return;
|
||||
}
|
||||
|
||||
await Clients.Group(room.RoomId).SendAsync("GameEnd", new
|
||||
{
|
||||
winUserId = winUserId,
|
||||
room = room
|
||||
});
|
||||
await Clients.Group(LobbyGroup).SendAsync("ExitRoomNotice", room);
|
||||
await JoinLobbyBatchAsync(settledConnectionIds);
|
||||
await RemoveConnectionsFromRoomGroupAsync(room.RoomId, settledConnectionIds, "EndGame");
|
||||
}
|
||||
|
||||
public async Task RestartGame(string gameId, string userId)
|
||||
{
|
||||
if (!IsCaller(gameId, userId))
|
||||
{
|
||||
var room0 = _chessService.GetRoom(gameId);
|
||||
if (room0 == null || room0.createUserId != userId)
|
||||
{
|
||||
await Clients.Caller.SendAsync("GameError", "无权操作");
|
||||
return;
|
||||
}
|
||||
}
|
||||
var (success, message, room) = _chessService.RestartGame(gameId, userId);
|
||||
|
||||
if (!success)
|
||||
{
|
||||
await Clients.Caller.SendAsync("GameError", message);
|
||||
return;
|
||||
}
|
||||
|
||||
await Groups.AddToGroupAsync(Context.ConnectionId, room.RoomId);
|
||||
await LeaveLobbyAsync(Context.ConnectionId);
|
||||
await Clients.Caller.SendAsync("RestartGameSuccess", room);
|
||||
await Clients.Group(LobbyGroup).SendAsync("RestartRoomNotice", room);
|
||||
}
|
||||
|
||||
public async Task RequestEndGame(string gameId, string userId, string winUserId)
|
||||
{
|
||||
if (!IsCaller(gameId, userId)) return;
|
||||
if (string.IsNullOrEmpty(userId))
|
||||
{
|
||||
await Clients.Caller.SendAsync("GameError", "参数异常");
|
||||
return;
|
||||
}
|
||||
|
||||
var settledConnectionIds = GetRoomConnectionIds(gameId);
|
||||
var (success, message, room, allConfirmed) = await _chessService.RequestEndGame(gameId, userId, winUserId);
|
||||
|
||||
if (!success)
|
||||
{
|
||||
await Clients.Caller.SendAsync("GameError", message);
|
||||
return;
|
||||
}
|
||||
|
||||
if (allConfirmed)
|
||||
{
|
||||
await Clients.Group(room.RoomId).SendAsync("GameEnd", new
|
||||
{
|
||||
winUserId = winUserId,
|
||||
room = room
|
||||
});
|
||||
await Clients.Group(LobbyGroup).SendAsync("ExitRoomNotice", room);
|
||||
await JoinLobbyBatchAsync(settledConnectionIds);
|
||||
await RemoveConnectionsFromRoomGroupAsync(room.RoomId, settledConnectionIds, "RequestEndGame");
|
||||
}
|
||||
else
|
||||
{
|
||||
var otherUserId = room.users.FirstOrDefault(u => u.userId != userId)?.userId;
|
||||
if (!string.IsNullOrEmpty(otherUserId))
|
||||
{
|
||||
await Clients.Group(room.RoomId).SendAsync("OpponentRequestEnd", userId);
|
||||
}
|
||||
await Clients.Caller.SendAsync("EndGameRequestSent", message);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task Heartbeat(string gameId)
|
||||
{
|
||||
var userId = Context.GetHttpContext()?.Request?.Query["userId"].ToString();
|
||||
if (string.IsNullOrEmpty(userId))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!IsCaller(gameId, userId)) return;
|
||||
|
||||
await _chessService.UpdateUserActiveTime(gameId, userId);
|
||||
}
|
||||
|
||||
public async Task SetPlayerAfk(string gameId, string userId)
|
||||
{
|
||||
if (!IsCaller(gameId, userId)) return;
|
||||
await _chessService.SetPlayerAfk(gameId, userId);
|
||||
await Clients.Group(gameId).SendAsync("PlayerAfkNotice", new { userId });
|
||||
var room = _chessService.GetRoom(gameId);
|
||||
if (room != null)
|
||||
{
|
||||
await Clients.Group(gameId).SendAsync("RoomStateNotice", room);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task CancelPlayerAfk(string gameId, string userId)
|
||||
{
|
||||
if (!IsCaller(gameId, userId)) return;
|
||||
await _chessService.CancelPlayerAfk(gameId, userId);
|
||||
await Clients.Group(gameId).SendAsync("PlayerCancelAfkNotice", new { userId });
|
||||
var room = _chessService.GetRoom(gameId);
|
||||
if (room != null)
|
||||
{
|
||||
await Clients.Caller.SendAsync("BeginGame", room);
|
||||
await Clients.Group(gameId).SendAsync("RoomStateNotice", room);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task CheckTimeout(string gameId)
|
||||
{
|
||||
if (!IsCallerInRoom(gameId)) return;
|
||||
var beforeEndRoom = _redisService.GetChessRoom(gameId);
|
||||
var settledConnectionIds = GetRoomConnectionIds(beforeEndRoom);
|
||||
var (isTimeout, loserUserId) = await _chessService.CheckRoomTimeout(gameId);
|
||||
|
||||
if (isTimeout)
|
||||
{
|
||||
var room = _redisService.GetChessRoom(gameId);
|
||||
if (room != null && beforeEndRoom != null)
|
||||
{
|
||||
await Clients.Group(room.RoomId).SendAsync("GameEnd", new
|
||||
{
|
||||
winUserId = beforeEndRoom.users.FirstOrDefault(u => u.userId != loserUserId)?.userId ?? "",
|
||||
room = room
|
||||
});
|
||||
await Clients.Group(LobbyGroup).SendAsync("ExitRoomNotice", room);
|
||||
await JoinLobbyBatchAsync(settledConnectionIds);
|
||||
await RemoveConnectionsFromRoomGroupAsync(room.RoomId, settledConnectionIds, "CheckTimeout");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async Task ExitRoom(string gameId, string userId)
|
||||
{
|
||||
if (!IsCaller(gameId, userId)) return;
|
||||
var beforeExitRoom = _chessService.GetRoom(gameId);
|
||||
var beforeExitUserIds = beforeExitRoom?.users?
|
||||
.Where(u => u != null).Select(u => u.userId).Distinct().ToList()
|
||||
?? new List<string> { userId };
|
||||
var beforeExitConnectionIds = GetRoomConnectionIds(beforeExitRoom);
|
||||
var (success, message, room) = _chessService.ExitRoom(gameId, userId);
|
||||
|
||||
if (!success)
|
||||
{
|
||||
await Clients.Caller.SendAsync("GameError", message);
|
||||
return;
|
||||
}
|
||||
|
||||
var affectedUserIds = room.users == null || room.users.Count == 0
|
||||
? beforeExitUserIds
|
||||
: new List<string> { userId };
|
||||
await Task.WhenAll(affectedUserIds.Select(id => _chessService.UpdateUserGameType(id, 0)));
|
||||
await Groups.RemoveFromGroupAsync(Context.ConnectionId, gameId);
|
||||
await JoinLobbyAsync(Context.ConnectionId);
|
||||
|
||||
if (room.users == null || room.users.Count == 0)
|
||||
{
|
||||
await Clients.Group(LobbyGroup).SendAsync("ExitRoomNotice", room);
|
||||
}
|
||||
else
|
||||
{
|
||||
await Clients.Group(room.RoomId).SendAsync("ExitRoomSuccess", userId);
|
||||
await Clients.Group(room.RoomId).SendAsync("BeginGame", room);
|
||||
await Clients.Group(room.RoomId).SendAsync("RoomStateNotice", room);
|
||||
await Clients.Group(LobbyGroup).SendAsync("ExitRoomNotice", room);
|
||||
}
|
||||
|
||||
}
|
||||
public async Task CreateRoom(CreateRoomParam param)
|
||||
{
|
||||
string cid = Context.ConnectionId;
|
||||
var (success, message, room) = await _chessService.CreateRoomAsync(param, cid);
|
||||
|
||||
if (!success)
|
||||
{
|
||||
await Clients.Caller.SendAsync("GameError", message);
|
||||
return;
|
||||
}
|
||||
|
||||
await _chessService.UpdateUserGameType(param.userId, 3, room.RoomId);
|
||||
|
||||
await Clients.Caller.SendAsync("BeginGame", room);
|
||||
await Groups.AddToGroupAsync(cid, room.RoomId);
|
||||
await LeaveLobbyAsync(cid);
|
||||
await Clients.Group(LobbyGroup).SendAsync("CreateRoomNotice", room);
|
||||
|
||||
}
|
||||
|
||||
public async Task GoldConfig(string gameId, long gold, string userId)
|
||||
{
|
||||
if (!IsCaller(gameId, userId)) return;
|
||||
var (success, message, room) = _chessService.GoldConfig(gameId, gold, userId);
|
||||
|
||||
if (!success)
|
||||
{
|
||||
await Clients.Caller.SendAsync("GameError", message);
|
||||
return;
|
||||
}
|
||||
|
||||
await Clients.Groups(room.RoomId).SendAsync("GoldConfigSuccess", room);
|
||||
await Clients.Group(room.RoomId).SendAsync("GoldConfigRoomNotice", room);
|
||||
}
|
||||
|
||||
public async Task GetRoomList(int partition = 0)
|
||||
{
|
||||
await JoinLobbyAsync(Context.ConnectionId);
|
||||
var list = _chessService.GetRoomList(partition, 100);
|
||||
await Clients.Caller.SendAsync("GetRoomList", list);
|
||||
}
|
||||
|
||||
public async Task GetRoomState(string gameId)
|
||||
{
|
||||
var room = string.IsNullOrWhiteSpace(gameId) ? null : _chessService.GetRoom(gameId);
|
||||
if (room == null || !IsCallerInRoom(gameId))
|
||||
{
|
||||
await Clients.Caller.SendAsync("GameError", "房间不存在或当前连接不在房间中");
|
||||
return;
|
||||
}
|
||||
|
||||
await Clients.Caller.SendAsync("RoomStateNotice", room);
|
||||
}
|
||||
|
||||
public async Task KickPlayer(string gameId, string targetUserId)
|
||||
{
|
||||
var room = string.IsNullOrWhiteSpace(gameId) ? null : _chessService.GetRoom(gameId);
|
||||
var caller = room?.users?.FirstOrDefault(u => u.Cid == Context.ConnectionId && !u.IsLeave);
|
||||
if (room == null || caller == null)
|
||||
{
|
||||
await Clients.Caller.SendAsync("GameError", "房间不存在或当前连接不在房间中");
|
||||
return;
|
||||
}
|
||||
if (room.createUserId != caller.userId)
|
||||
{
|
||||
await Clients.Caller.SendAsync("GameError", "只有房主可以踢人");
|
||||
return;
|
||||
}
|
||||
if (room.state == 2)
|
||||
{
|
||||
await Clients.Caller.SendAsync("GameError", "游戏进行中不能踢人");
|
||||
return;
|
||||
}
|
||||
if (string.IsNullOrWhiteSpace(targetUserId) || targetUserId == caller.userId)
|
||||
{
|
||||
await Clients.Caller.SendAsync("GameError", "不能踢出自己");
|
||||
return;
|
||||
}
|
||||
|
||||
var target = room.users.FirstOrDefault(u => u.userId == targetUserId);
|
||||
if (target == null)
|
||||
{
|
||||
await Clients.Caller.SendAsync("GameError", "目标玩家不在房间中");
|
||||
return;
|
||||
}
|
||||
|
||||
var targetCid = target.Cid;
|
||||
var (success, message, updatedRoom) = _chessService.ExitRoom(gameId, targetUserId);
|
||||
if (!success)
|
||||
{
|
||||
await Clients.Caller.SendAsync("GameError", message);
|
||||
return;
|
||||
}
|
||||
|
||||
await _chessService.UpdateUserGameType(targetUserId, 0);
|
||||
if (!string.IsNullOrWhiteSpace(targetCid))
|
||||
{
|
||||
await Clients.Client(targetCid).SendAsync("KickedFromRoom", new { gameId, userId = targetUserId });
|
||||
await Groups.RemoveFromGroupAsync(targetCid, gameId);
|
||||
await JoinLobbyAsync(targetCid);
|
||||
}
|
||||
|
||||
await Clients.Group(gameId).SendAsync("RoomStateNotice", updatedRoom);
|
||||
await Clients.Group(gameId).SendAsync("BeginGame", updatedRoom);
|
||||
await Clients.Group(LobbyGroup).SendAsync("ExitRoomNotice", updatedRoom);
|
||||
}
|
||||
|
||||
public async Task SyncGameData(string gameId, string data)
|
||||
{
|
||||
var room = _redisService.GetChessRoom(gameId);
|
||||
var otherUser = room?.users?.FirstOrDefault(u => u.Cid != Context.ConnectionId);
|
||||
if (otherUser != null && !otherUser.IsLeave)
|
||||
await Clients.Client(otherUser.Cid).SendAsync("SyncGameData", data);
|
||||
}
|
||||
|
||||
public async Task GameOperation(string gameId, int type, string data)
|
||||
{
|
||||
var userId = Context.GetHttpContext()?.Request?.Query["userId"].ToString();
|
||||
if (!string.IsNullOrEmpty(userId))
|
||||
{
|
||||
if (!IsCaller(gameId, userId)) return;
|
||||
await _chessService.UpdateUserActiveTime(gameId, userId);
|
||||
}
|
||||
|
||||
if (type == 2 && !string.IsNullOrEmpty(userId))
|
||||
{
|
||||
var room = _redisService.GetChessRoom(gameId);
|
||||
if (room != null && room.state == 2)
|
||||
{
|
||||
var winUserId = room.users.FirstOrDefault(u => u.userId != userId)?.userId ?? "";
|
||||
await EndGame(gameId, winUserId);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
await Clients.Group(gameId).SendAsync("GameOperation", new { type, data });
|
||||
}
|
||||
|
||||
private bool IsCaller(string gameId, string userId)
|
||||
{
|
||||
var room = string.IsNullOrWhiteSpace(gameId) ? null : _chessService.GetRoom(gameId);
|
||||
return room?.users?.Any(u => u.userId == userId && u.Cid == Context.ConnectionId && !u.IsLeave) == true;
|
||||
}
|
||||
|
||||
private bool IsCallerInRoom(string gameId)
|
||||
{
|
||||
var room = string.IsNullOrWhiteSpace(gameId) ? null : _chessService.GetRoom(gameId);
|
||||
return room?.users?.Any(u => u.Cid == Context.ConnectionId && !u.IsLeave) == true;
|
||||
}
|
||||
|
||||
private List<string> GetRoomConnectionIds(string gameId)
|
||||
{
|
||||
return GetRoomConnectionIds(_redisService.GetChessRoom(gameId));
|
||||
}
|
||||
|
||||
private static List<string> GetRoomConnectionIds(ChessGameRoom? room)
|
||||
{
|
||||
return room?.users?
|
||||
.Where(user => !string.IsNullOrWhiteSpace(user.Cid))
|
||||
.Select(user => user.Cid)
|
||||
.Distinct()
|
||||
.ToList() ?? new List<string>();
|
||||
}
|
||||
|
||||
private async Task RemoveConnectionsFromRoomGroupAsync(
|
||||
string roomId, IEnumerable<string> connectionIds, string reason)
|
||||
{
|
||||
foreach (var connectionId in connectionIds.Distinct())
|
||||
{
|
||||
try
|
||||
{
|
||||
await Groups.RemoveFromGroupAsync(connectionId, roomId);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex,
|
||||
"[ChessGroup] 结算退组失败 roomId={RoomId} cid={Cid} reason={Reason}",
|
||||
roomId, connectionId, reason);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,918 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using ChessGameDomain;
|
||||
using ChessGameService.Param;
|
||||
using Common.Redis;
|
||||
using CommonDomain;
|
||||
using CommonDomain.BaseModel;
|
||||
using CommonDomain.Enum;
|
||||
using DotNetCore.CAP;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using RedisHelper;
|
||||
using RedisService;
|
||||
using UserDomain.Enum;
|
||||
using UserService;
|
||||
using UserService.Param;
|
||||
using UserService.User;
|
||||
|
||||
namespace ChessGameService
|
||||
{
|
||||
public class ChessGameService : IChessGameService
|
||||
{
|
||||
private readonly IRedisService _redisService;
|
||||
private readonly ILogger<ChessGameService> _logger;
|
||||
private readonly IDistributedLock _distributedLock;
|
||||
private readonly IUserService _userService;
|
||||
private readonly ICapPublisher _capPublisher;
|
||||
|
||||
public ChessGameService(IRedisService redisService, ILogger<ChessGameService> logger, IDistributedLock distributedLock, IUserService userService, ICapPublisher capPublisher)
|
||||
{
|
||||
_redisService = redisService;
|
||||
_logger = logger;
|
||||
_distributedLock = distributedLock;
|
||||
_userService = userService;
|
||||
_capPublisher = capPublisher;
|
||||
}
|
||||
|
||||
public async Task<(bool isok, string msg, ChessGameRoom room)> CreateRoomAsync(CreateRoomParam param, string cid)
|
||||
{
|
||||
var user = await _userService.GetUser(param.userId);
|
||||
if (user == null)
|
||||
return (false, "用户不存在", null);
|
||||
|
||||
var gameConfig = _redisService.GetGameConfig();
|
||||
if (gameConfig == null)
|
||||
return (false, "游戏配置不存在", null);
|
||||
|
||||
return await _distributedLock.TryExecuteAsync(
|
||||
$"{RedisKey.DistributedLockPrefix}chess:room:{param.roomId}",
|
||||
() =>
|
||||
{
|
||||
var room = _redisService.GetChessRoom(param.roomId);
|
||||
|
||||
if (room == null)
|
||||
{
|
||||
return Task.FromResult<(bool isok, string msg, ChessGameRoom room)>((false, "房间不存在", null));
|
||||
}
|
||||
|
||||
var allRooms = _redisService.GetAllChessRooms();
|
||||
if (allRooms != null)
|
||||
{
|
||||
foreach (var oldRoom in allRooms)
|
||||
{
|
||||
if (oldRoom.RoomId == param.roomId) continue;
|
||||
var oldUser = oldRoom.users?.FirstOrDefault(u => u.userId == param.userId);
|
||||
if (oldUser != null)
|
||||
{
|
||||
if (oldRoom.createUserId == param.userId)
|
||||
{
|
||||
oldRoom.users = new List<RedisChessGameUser>();
|
||||
oldRoom.state = 3;
|
||||
oldRoom.password = "";
|
||||
oldRoom.createUserId = "";
|
||||
oldRoom.gold = 0;
|
||||
oldRoom.gameType = 0;
|
||||
oldRoom.robotCount = 0;
|
||||
oldRoom.IsGameEnded = false;
|
||||
oldRoom.EndGameRequests?.Clear();
|
||||
oldRoom.PendingWinUserId = null;
|
||||
oldRoom.TimeoutLoserUserId = "";
|
||||
oldRoom.LastActiveTime = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
oldRoom.users.Remove(oldUser);
|
||||
if (oldRoom.users.Count == 0)
|
||||
{
|
||||
oldRoom.state = 3;
|
||||
oldRoom.password = "";
|
||||
oldRoom.createUserId = "";
|
||||
oldRoom.gold = 0;
|
||||
oldRoom.gameType = 0;
|
||||
oldRoom.robotCount = 0;
|
||||
oldRoom.IsGameEnded = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
oldRoom.gold = 0;
|
||||
}
|
||||
}
|
||||
_redisService.SetChessRoom(oldRoom);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((room.state == 3 && room.users.Count == 0) || room.state == 0)
|
||||
{
|
||||
bool isCreareRoom = false;
|
||||
if (gameConfig.ChessConfig.CreateRoomLimit.Any(c=>c==3))
|
||||
isCreareRoom = true;
|
||||
if (gameConfig.ChessConfig.CreateRoomLimit.Any(c => c == 1) && user.Assets.VipLevel > 0)
|
||||
{
|
||||
var vipLevels = gameConfig.ChessConfig.CreateRoomVipLevels ?? new List<int>();
|
||||
if (vipLevels.Count == 0 || vipLevels.Contains(user.Assets.VipLevel))
|
||||
isCreareRoom = true;
|
||||
}
|
||||
if (gameConfig.ChessConfig.CreateRoomLimit.Any(c => c == 2) && user.UserType==2)
|
||||
isCreareRoom = true;
|
||||
if (!isCreareRoom)
|
||||
{
|
||||
var roles = new List<string>();
|
||||
if (gameConfig.ChessConfig.CreateRoomLimit.Any(c => c == 1)) roles.Add("VIP");
|
||||
if (gameConfig.ChessConfig.CreateRoomLimit.Any(c => c == 2)) roles.Add("代理");
|
||||
var msg = roles.Count > 0 ? $"只有{string.Join("/", roles)}才能配置密码" : "没有权限配置密码";
|
||||
return Task.FromResult<(bool isok, string msg, ChessGameRoom room)>((false, msg, null));
|
||||
}
|
||||
room.users = new List<RedisChessGameUser>() { new RedisChessGameUser
|
||||
{
|
||||
Cid = cid,
|
||||
userId = param.userId,
|
||||
userName = user.BaseInfo.UserName,
|
||||
userAvatar = user.BaseInfo.AvatarUrl,
|
||||
Gold = user.Assets.Gold,
|
||||
type = param.gameType,
|
||||
IsLeave = false,
|
||||
IsReady = false
|
||||
}};
|
||||
room.gameType = param.gameType;
|
||||
room.password = param.password;
|
||||
room.createUserId = param.userId;
|
||||
room.gold = param.gold;
|
||||
room.state = 1;
|
||||
room.robotCount = 0;
|
||||
room.IsGameEnded = false;
|
||||
}
|
||||
else if (room.users.Any(c => c.userId == param.userId))
|
||||
{
|
||||
var existing = room.users.First(c => c.userId == param.userId);
|
||||
existing.Cid = cid;
|
||||
existing.IsLeave = false;
|
||||
existing.DisconnectTime = 0;
|
||||
room.robotCount = Math.Clamp(room.robotCount, 0, Math.Max(0, 2 - room.users.Count));
|
||||
_redisService.SetChessRoom(room);
|
||||
return Task.FromResult<(bool isok, string msg, ChessGameRoom room)>((true, "", room));
|
||||
}
|
||||
else if (room.state == 3)
|
||||
return Task.FromResult<(bool isok, string msg, ChessGameRoom room)>((false, "上一局玩家尚未退出", room));
|
||||
else if (room.users.Count + room.robotCount >= 2 || room.state == 2)
|
||||
return Task.FromResult<(bool isok, string msg, ChessGameRoom room)>((false, "当前房间已有玩家", room));
|
||||
else
|
||||
{
|
||||
var firstPlayerType = room.gameType;
|
||||
var secondPlayerType = firstPlayerType == 1 ? 2 : 1;
|
||||
room.users.Add(new RedisChessGameUser
|
||||
{
|
||||
Cid = cid,
|
||||
userId = param.userId,
|
||||
userName = user.BaseInfo.UserName,
|
||||
userAvatar = user.BaseInfo.AvatarUrl,
|
||||
Gold = user.Assets.Gold,
|
||||
type = secondPlayerType,
|
||||
IsLeave = false,
|
||||
IsReady = false
|
||||
});
|
||||
room.state = 1;
|
||||
}
|
||||
|
||||
_redisService.SetChessRoom(room);
|
||||
|
||||
|
||||
return Task.FromResult<(bool isok, string msg, ChessGameRoom room)>((true, "", room));
|
||||
},
|
||||
expiry: 10
|
||||
);
|
||||
}
|
||||
|
||||
public async Task<(bool success, string message, ChessGameRoom room)> JoinGameAsync(string gameId, string pwd, string userId, string cid)
|
||||
{
|
||||
var user = await _userService.GetUser(userId);
|
||||
if (user == null)
|
||||
return (false, "用户不存在", null);
|
||||
return await _distributedLock.TryExecuteAsync(
|
||||
$"{RedisKey.DistributedLockPrefix}chess:room:{gameId}",
|
||||
() =>
|
||||
{
|
||||
var room = _redisService.GetChessRoom(gameId);
|
||||
|
||||
if (room == null || room.state == 3)
|
||||
{
|
||||
return Task.FromResult((false, "房间不存在", (ChessGameRoom)null));
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(room.password))
|
||||
{
|
||||
return Task.FromResult((false, "房间未设置密码,无法进入", (ChessGameRoom)null));
|
||||
}
|
||||
|
||||
if (room.password != pwd)
|
||||
{
|
||||
return Task.FromResult((false, "密码错误", (ChessGameRoom)null));
|
||||
}
|
||||
|
||||
if (room.users.Any(c => c.userId == userId))
|
||||
{
|
||||
var existing = room.users.First(c => c.userId == userId);
|
||||
existing.Cid = cid;
|
||||
existing.IsLeave = false;
|
||||
existing.DisconnectTime = 0;
|
||||
room.robotCount = Math.Clamp(room.robotCount, 0, Math.Max(0, 2 - room.users.Count));
|
||||
_redisService.SetChessRoom(room);
|
||||
return Task.FromResult((true, "", room));
|
||||
}
|
||||
|
||||
if (room.state == 3 && room.IsGameEnded && room.users.Count < 2)
|
||||
{
|
||||
var secondPlayerType = room.gameType == 1 ? 2 : 1;
|
||||
room.users.Add(new RedisChessGameUser
|
||||
{
|
||||
Cid = cid,
|
||||
userId = userId,
|
||||
Gold = user.Assets.Gold,
|
||||
userName = user.BaseInfo.UserName,
|
||||
userAvatar = user.BaseInfo.AvatarUrl,
|
||||
type = secondPlayerType,
|
||||
IsLeave = false,
|
||||
IsReady = false
|
||||
});
|
||||
room.robotCount = 0;
|
||||
room.state = 1;
|
||||
room.IsGameEnded = false;
|
||||
room.gold = 0;
|
||||
_redisService.SetChessRoom(room);
|
||||
return Task.FromResult((true, "", room));
|
||||
}
|
||||
|
||||
if (room.users.Count + room.robotCount >= 2)
|
||||
{
|
||||
return Task.FromResult((false, "房间已满", (ChessGameRoom)null));
|
||||
}
|
||||
|
||||
room.users.Add(new RedisChessGameUser
|
||||
{
|
||||
Cid = cid,
|
||||
userId = userId,
|
||||
Gold = user.Assets.Gold,
|
||||
userName = user.BaseInfo.UserName,
|
||||
userAvatar = user.BaseInfo.AvatarUrl,
|
||||
type = room.gameType == 1 ? 2 : 1,
|
||||
IsLeave = false,
|
||||
IsReady = false
|
||||
});
|
||||
room.robotCount = 0;
|
||||
|
||||
_redisService.SetChessRoom(room);
|
||||
|
||||
|
||||
return Task.FromResult((true, "", room));
|
||||
},
|
||||
expiry: 10
|
||||
);
|
||||
}
|
||||
|
||||
public List<ChessGameRoom> GetRoomList(int partition, int maxRooms = 100)
|
||||
{
|
||||
var existingRooms = _redisService.GetAllChessRooms();
|
||||
if (existingRooms == null || existingRooms.Count == 0)
|
||||
return new List<ChessGameRoom>();
|
||||
|
||||
return existingRooms
|
||||
.Where(c => c != null
|
||||
&& (partition <= 0 || GetChessRoomPartition(c.RoomId) == partition)
|
||||
&& !(c.state == 1
|
||||
&& string.IsNullOrWhiteSpace(c.createUserId)
|
||||
&& (c.users == null || c.users.Count == 0)))
|
||||
.OrderBy(c => c.RoomId)
|
||||
.Take(maxRooms)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
private static int GetChessRoomPartition(string roomId)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(roomId) || roomId.Length < 4)
|
||||
return 0;
|
||||
|
||||
var partitionText = roomId.Substring(1, roomId.Length - 3);
|
||||
return int.TryParse(partitionText, out var partition) ? partition : 0;
|
||||
}
|
||||
|
||||
public (bool success, string message, ChessGameRoom room) StartGame(string gameId, string userId)
|
||||
{
|
||||
var room = _redisService.GetChessRoom(gameId);
|
||||
if (room == null || room.state == 3)
|
||||
return (false, "房间不存在", null);
|
||||
if (room.state != 1)
|
||||
return (false, "游戏已开始", null);
|
||||
if (room.createUserId != userId)
|
||||
return (false, "只有房主才能开始游戏", null);
|
||||
if (room.users.Count < 2)
|
||||
return (false, "人数不足,无法开始", null);
|
||||
return (true, "", room);
|
||||
}
|
||||
|
||||
public (bool success, string message, bool allReady) GameReady(string gameId, string userId)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(gameId) || string.IsNullOrWhiteSpace(userId))
|
||||
return (false, "房间或玩家参数不能为空", false);
|
||||
|
||||
return _distributedLock.TryExecute(
|
||||
$"{RedisKey.DistributedLockPrefix}chess:room:{gameId}",
|
||||
() =>
|
||||
{
|
||||
var room = _redisService.GetChessRoom(gameId);
|
||||
if (room == null || room.state == 3)
|
||||
return (false, "房间不存在", false);
|
||||
|
||||
var user = room.users.FirstOrDefault(c => c.userId == userId);
|
||||
if (user == null)
|
||||
return (false, "玩家不存在", false);
|
||||
if (room.gold == 0)
|
||||
return (false, "未设置金币", false);
|
||||
|
||||
user.IsReady = true;
|
||||
var allReady = room.users.Count >= 2 && room.users.All(c => c.IsReady);
|
||||
if (allReady)
|
||||
room.state = 2;
|
||||
_redisService.SetChessRoom(room);
|
||||
|
||||
return (true, "", allReady);
|
||||
},
|
||||
expiry: 10,
|
||||
retry: true,
|
||||
retryDelay: 20);
|
||||
}
|
||||
|
||||
|
||||
public async Task<(bool success, string message, ChessGameRoom room)> EndGame(string gameId, string winUserId)
|
||||
{
|
||||
return await _distributedLock.TryExecuteAsync(
|
||||
$"{RedisKey.DistributedLockPrefix}chess:room:{gameId}",
|
||||
async () =>
|
||||
{
|
||||
var room = _redisService.GetChessRoom(gameId);
|
||||
if (room == null)
|
||||
return (false, "房间不存在", null);
|
||||
if (room.state != 2 || room.IsGameEnded)
|
||||
return (true, "游戏已结束", room);
|
||||
if (!string.IsNullOrWhiteSpace(winUserId) && !room.users.Any(u => u.userId == winUserId))
|
||||
return (false, "胜者不在房间中", (ChessGameRoom)null);
|
||||
|
||||
room.state = 3;
|
||||
room.IsGameEnded = true;
|
||||
_redisService.SetChessRoom(room);
|
||||
if (!string.IsNullOrWhiteSpace(winUserId))
|
||||
{
|
||||
var loser = room.users.FirstOrDefault(u => u.userId != winUserId);
|
||||
var winner = room.users.FirstOrDefault(u => u.userId == winUserId);
|
||||
var winnerName = winner?.userName ?? "";
|
||||
var loserName = loser?.userName ?? "";
|
||||
var loserId = loser?.userId ?? "";
|
||||
|
||||
var winnerEntity = winner != null ? await _userService.GetUser(winner.userId) : null;
|
||||
var loserEntity = loser != null ? await _userService.GetUser(loser.userId) : null;
|
||||
var realLoserGold = loserEntity?.Assets.Gold ?? 0;
|
||||
var actualBet = Math.Min(room.gold, realLoserGold);
|
||||
|
||||
if (actualBet > 0)
|
||||
{
|
||||
if (loserEntity != null)
|
||||
{
|
||||
var deductRemark = $"房间号({room.RoomId}) {winnerName}({winner?.userId})赢得对局 {loserName}({loserId})输 金额{actualBet}";
|
||||
var deductResult = await _userService.UpdateAssets(new UserAssetParam()
|
||||
{
|
||||
ChangeType = UserAssetChangeTypeEnum.减少,
|
||||
Gold = actualBet,
|
||||
TotalRechargeGoldDelta = -actualBet,
|
||||
Remark = deductRemark,
|
||||
Source = RewardSource.象棋,
|
||||
UserId = loserEntity.Id,
|
||||
RoomInfo = room.RoomId,
|
||||
OtherUserId = winUserId
|
||||
});
|
||||
if (!deductResult.isok)
|
||||
{
|
||||
room.state = 2;
|
||||
room.IsGameEnded = false;
|
||||
_redisService.SetChessRoom(room);
|
||||
return (false, "结算失败:" + deductResult.msg, room);
|
||||
}
|
||||
loser.Gold = Math.Max(0, realLoserGold - actualBet);
|
||||
}
|
||||
|
||||
if (winnerEntity != null)
|
||||
{
|
||||
var winRemark = $"房间号({room.RoomId}) {winnerName}({winner?.userId})赢得对局 {loserName}({loserId})输 金额{actualBet}";
|
||||
var winResult = await _userService.UpdateAssets(new UserAssetParam()
|
||||
{
|
||||
ChangeType = UserAssetChangeTypeEnum.增加,
|
||||
Gold = actualBet,
|
||||
TotalRechargeGoldDelta = actualBet,
|
||||
Remark = winRemark,
|
||||
Source = RewardSource.象棋,
|
||||
UserId = winnerEntity.Id,
|
||||
RoomInfo = room.RoomId,
|
||||
OtherUserId = loserId
|
||||
});
|
||||
if (!winResult.isok)
|
||||
{
|
||||
if (loserEntity != null)
|
||||
{
|
||||
await _userService.UpdateAssets(new UserAssetParam()
|
||||
{
|
||||
ChangeType = UserAssetChangeTypeEnum.增加,
|
||||
Gold = actualBet,
|
||||
TotalRechargeGoldDelta = actualBet,
|
||||
Remark = $"房间号({room.RoomId}) 结算异常回退 {winnerName}({winner?.userId}) 金额{actualBet}",
|
||||
Source = RewardSource.象棋,
|
||||
UserId = loserEntity.Id,
|
||||
RoomInfo = room.RoomId,
|
||||
OtherUserId = winUserId
|
||||
});
|
||||
}
|
||||
}
|
||||
winner.Gold = winnerEntity.Assets.Gold + actualBet;
|
||||
}
|
||||
|
||||
var newWinnerEntity = winnerEntity != null ? await _userService.GetUser(winnerEntity.Id) : null;
|
||||
var newLoserEntity = loserEntity != null ? await _userService.GetUser(loserEntity.Id) : null;
|
||||
|
||||
if (winnerEntity != null)
|
||||
{
|
||||
var newWinnerGold = newWinnerEntity?.Assets.Gold ?? winnerEntity.Assets.Gold;
|
||||
_capPublisher.Publish("BackgroundOperation.NotifyUser", new BackgroundOperationNotifyMessage
|
||||
{
|
||||
UserId = winnerEntity.Id,
|
||||
CurrencyType = 1,
|
||||
ChangeType = 1,
|
||||
Amount = actualBet,
|
||||
NewGold = newWinnerGold,
|
||||
Source = "象棋对局胜利",
|
||||
Remark = $"房间号({room.RoomId})赢得{actualBet}金币"
|
||||
});
|
||||
}
|
||||
|
||||
if (loserEntity != null)
|
||||
{
|
||||
var newLoserGold = newLoserEntity?.Assets.Gold ?? loser.Gold;
|
||||
_capPublisher.Publish("BackgroundOperation.NotifyUser", new BackgroundOperationNotifyMessage
|
||||
{
|
||||
UserId = loserEntity.Id,
|
||||
CurrencyType = 1,
|
||||
ChangeType = 2,
|
||||
Amount = actualBet,
|
||||
NewGold = newLoserGold,
|
||||
Source = "象棋对局失败",
|
||||
Remark = $"房间号({room.RoomId})输掉{actualBet}金币"
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var userIds = room.users?.Select(u => u.userId).Where(id => !string.IsNullOrWhiteSpace(id)).ToList()
|
||||
?? new List<string>();
|
||||
room.gold = 0;
|
||||
room.gameType = 0;
|
||||
room.robotCount = 0;
|
||||
room.EndGameRequests?.Clear();
|
||||
room.PendingWinUserId = null;
|
||||
room.TimeoutLoserUserId = "";
|
||||
room.LastActiveTime = 0;
|
||||
room.users.Clear();
|
||||
_redisService.SetChessRoom(room);
|
||||
|
||||
foreach (var userId in userIds)
|
||||
{
|
||||
try
|
||||
{
|
||||
await UpdateUserGameType(userId, 0);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "[象棋] EndGame 清除游戏状态失败 userId={UserId}", userId);
|
||||
}
|
||||
}
|
||||
|
||||
return (true, "", room);
|
||||
},
|
||||
expiry: 30,
|
||||
retry: false);
|
||||
}
|
||||
|
||||
public (bool success, string message, ChessGameRoom room) RestartGame(string gameId, string userId)
|
||||
{
|
||||
var room = _redisService.GetChessRoom(gameId);
|
||||
if (room == null)
|
||||
{
|
||||
return (false, "房间不存在", null);
|
||||
}
|
||||
|
||||
if (room.state == 3 && room.IsGameEnded)
|
||||
{
|
||||
room.state = 1;
|
||||
room.IsGameEnded = false;
|
||||
room.gold = 0;
|
||||
room.robotCount = 0;
|
||||
room.EndGameRequests?.Clear();
|
||||
room.PendingWinUserId = null;
|
||||
room.TimeoutLoserUserId = "";
|
||||
room.LastActiveTime = 0;
|
||||
if (room.users == null) room.users = new List<RedisChessGameUser>();
|
||||
|
||||
var existingUser = room.users.FirstOrDefault(u => u.userId == userId);
|
||||
if (existingUser == null)
|
||||
{
|
||||
room.users.Add(new RedisChessGameUser
|
||||
{
|
||||
userId = userId,
|
||||
userName = "",
|
||||
type = room.gameType,
|
||||
IsLeave = false,
|
||||
IsReady = false
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
existingUser.IsReady = false;
|
||||
existingUser.IsLeave = false;
|
||||
existingUser.DisconnectTime = 0;
|
||||
}
|
||||
|
||||
_redisService.SetChessRoom(room);
|
||||
return (true, "", room);
|
||||
}
|
||||
|
||||
if (!room.IsGameEnded)
|
||||
{
|
||||
return (false, "对局尚未结束,无法重新开始", null);
|
||||
}
|
||||
|
||||
var user = room.users.FirstOrDefault(u => u.userId == userId);
|
||||
if (user == null)
|
||||
{
|
||||
return (false, "对局已结束,房间已释放,请重新创建房间", null);
|
||||
}
|
||||
|
||||
room.state = 1;
|
||||
room.IsGameEnded = false;
|
||||
room.gold = 0;
|
||||
room.EndGameRequests?.Clear();
|
||||
room.PendingWinUserId = null;
|
||||
room.TimeoutLoserUserId = "";
|
||||
room.LastActiveTime = 0;
|
||||
|
||||
foreach (var u in room.users)
|
||||
{
|
||||
u.IsReady = false;
|
||||
}
|
||||
|
||||
_redisService.SetChessRoom(room);
|
||||
|
||||
return (true, "", room);
|
||||
}
|
||||
|
||||
public async Task<(bool success, string message, ChessGameRoom room, bool allConfirmed)> RequestEndGame(string gameId, string userId, string winUserId)
|
||||
{
|
||||
var room = _redisService.GetChessRoom(gameId);
|
||||
if (room == null || room.state == 3)
|
||||
{
|
||||
return (false, "房间不存在", null, false);
|
||||
}
|
||||
|
||||
if (room.state != 2)
|
||||
{
|
||||
return (false, "游戏未开始", null, false);
|
||||
}
|
||||
|
||||
var user = room.users.FirstOrDefault(u => u.userId == userId);
|
||||
if (user == null)
|
||||
{
|
||||
return (false, "参数异常", null, false);
|
||||
}
|
||||
if (!string.IsNullOrWhiteSpace(winUserId) && !room.users.Any(u => u.userId == winUserId))
|
||||
return (false, "胜者不在房间中", null, false);
|
||||
|
||||
if (room.EndGameRequests == null)
|
||||
room.EndGameRequests = new Dictionary<string, long>();
|
||||
|
||||
room.EndGameRequests[userId] = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
|
||||
|
||||
if (!string.IsNullOrEmpty(winUserId) && string.IsNullOrEmpty(room.PendingWinUserId))
|
||||
{
|
||||
room.PendingWinUserId = winUserId;
|
||||
}
|
||||
|
||||
_redisService.SetChessRoom(room);
|
||||
|
||||
var roomUserIds = room.users.Select(u => u.userId).ToHashSet();
|
||||
var confirmedCount = room.EndGameRequests.Keys.Count(roomUserIds.Contains);
|
||||
|
||||
if (confirmedCount >= room.users.Count && room.users.Count >= 2)
|
||||
{
|
||||
string finalWinUserId = room.PendingWinUserId ?? "";
|
||||
|
||||
room.EndGameRequests.Clear();
|
||||
room.PendingWinUserId = null;
|
||||
_redisService.SetChessRoom(room);
|
||||
|
||||
var result = await EndGame(gameId, finalWinUserId);
|
||||
return (result.success, result.message, result.room, true);
|
||||
}
|
||||
|
||||
return (true, "等待对方确认结束", room, false);
|
||||
}
|
||||
|
||||
public async Task<(bool isTimeout, string loserUserId)> CheckRoomTimeout(string gameId)
|
||||
{
|
||||
var room = _redisService.GetChessRoom(gameId);
|
||||
if (room == null || room.state != 2)
|
||||
{
|
||||
return (false, "");
|
||||
}
|
||||
|
||||
var now = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
|
||||
var timeout = room.TimeoutSeconds > 0 ? room.TimeoutSeconds : 60;
|
||||
|
||||
foreach (var user in room.users)
|
||||
{
|
||||
if (user.IsLeave && user.DisconnectTime > 0 && (now - user.DisconnectTime) > timeout)
|
||||
{
|
||||
var winUserId = room.users.FirstOrDefault(u => u.userId != user.userId)?.userId ?? "";
|
||||
|
||||
|
||||
|
||||
room.TimeoutLoserUserId = user.userId;
|
||||
_redisService.SetChessRoom(room);
|
||||
|
||||
await EndGame(gameId, winUserId);
|
||||
|
||||
return (true, user.userId);
|
||||
}
|
||||
}
|
||||
|
||||
return (false, "");
|
||||
}
|
||||
|
||||
public async Task<(bool isTimeout, string loserUserId)> CheckRoomAfkTimeout(string gameId)
|
||||
{
|
||||
var room = _redisService.GetChessRoom(gameId);
|
||||
if (room == null || room.state != 2 || room.users.Count < 2)
|
||||
{
|
||||
return (false, "");
|
||||
}
|
||||
|
||||
var now = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
|
||||
var afkUsers = room.users.Where(u => u.IsAfk && u.AfkTime > 0).OrderBy(u => u.AfkTime).ToList();
|
||||
|
||||
if (afkUsers.Count == 2)
|
||||
{
|
||||
var firstAfkUser = afkUsers[0];
|
||||
var winUserId = room.users.FirstOrDefault(u => u.userId != firstAfkUser.userId)?.userId ?? "";
|
||||
await EndGame(gameId, winUserId);
|
||||
return (true, firstAfkUser.userId);
|
||||
}
|
||||
|
||||
if (afkUsers.Count == 1)
|
||||
{
|
||||
var afkUser = afkUsers[0];
|
||||
var timeout = room.AfkTimeoutSeconds > 0 ? room.AfkTimeoutSeconds : 120;
|
||||
if ((now - afkUser.AfkTime) > timeout)
|
||||
{
|
||||
var winUserId = room.users.FirstOrDefault(u => u.userId != afkUser.userId)?.userId ?? "";
|
||||
await EndGame(gameId, winUserId);
|
||||
return (true, afkUser.userId);
|
||||
}
|
||||
}
|
||||
|
||||
return (false, "");
|
||||
}
|
||||
|
||||
public async Task UpdateUserActiveTime(string gameId, string userId)
|
||||
{
|
||||
var room = _redisService.GetChessRoom(gameId);
|
||||
if (room == null || room.state != 2)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var user = room.users.FirstOrDefault(u => u.userId == userId);
|
||||
if (user == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
room.LastActiveTime = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
|
||||
_redisService.SetChessRoom(room);
|
||||
}
|
||||
|
||||
public async Task SetPlayerAfk(string gameId, string userId)
|
||||
{
|
||||
var room = string.IsNullOrWhiteSpace(gameId) ? null : _redisService.GetChessRoom(gameId);
|
||||
var user = room?.users?.FirstOrDefault(u => u.userId == userId);
|
||||
if (user == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
user.IsAfk = true;
|
||||
user.AfkTime = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
|
||||
_redisService.SetChessRoom(room);
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
|
||||
public async Task CancelPlayerAfk(string gameId, string userId)
|
||||
{
|
||||
var room = string.IsNullOrWhiteSpace(gameId) ? null : _redisService.GetChessRoom(gameId);
|
||||
var user = room?.users?.FirstOrDefault(u => u.userId == userId);
|
||||
if (user == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
user.IsAfk = false;
|
||||
user.AfkTime = 0;
|
||||
_redisService.SetChessRoom(room);
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
|
||||
public bool ExitRoom(ChessGameRoom room)
|
||||
{
|
||||
|
||||
room.users = new List<RedisChessGameUser>();
|
||||
room.state = 3;
|
||||
room.password = "";
|
||||
room.createUserId = "";
|
||||
room.gold = 0;
|
||||
room.gameType = 0;
|
||||
room.robotCount = 0;
|
||||
room.IsGameEnded = false;
|
||||
room.EndGameRequests?.Clear();
|
||||
room.PendingWinUserId = null;
|
||||
room.TimeoutLoserUserId = "";
|
||||
room.LastActiveTime = 0;
|
||||
_redisService.SetChessRoom(room);
|
||||
return true;
|
||||
}
|
||||
|
||||
public (bool isok, string msg, ChessGameRoom room) ExitRoom(string gameId, string userId)
|
||||
{
|
||||
var room = _redisService.GetChessRoom(gameId);
|
||||
if (room == null)
|
||||
{
|
||||
return (false, "房间不存在", null);
|
||||
}
|
||||
if (room.state == 3 && (room.users == null || room.users.Count == 0))
|
||||
{
|
||||
ExitRoom(room);
|
||||
return (true, "房间已清空", room);
|
||||
}
|
||||
|
||||
var user = room.users.FirstOrDefault(c => c.userId == userId);
|
||||
if (user == null)
|
||||
{
|
||||
return (false, "玩家不存在", null);
|
||||
}
|
||||
|
||||
if (room.state == 2)
|
||||
{
|
||||
user.IsLeave = true;
|
||||
user.DisconnectTime = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
|
||||
user.Cid = string.Empty;
|
||||
if (room.users.All(u => u.IsLeave))
|
||||
{
|
||||
ExitRoom(room);
|
||||
return (true, "所有玩家已退出,房间已重置", room);
|
||||
}
|
||||
|
||||
_redisService.SetChessRoom(room);
|
||||
return (true, "玩家已退出,等待其他玩家", room);
|
||||
}
|
||||
|
||||
if (room.createUserId == userId)
|
||||
{
|
||||
ExitRoom(room);
|
||||
return (true, "房主退出,房间已清空", room);
|
||||
}
|
||||
|
||||
room.users.Remove(user);
|
||||
|
||||
if (room.users.Count == 0 || (room.state == 3 && room.users.Count <= 1))
|
||||
{
|
||||
ExitRoom(room);
|
||||
return (true, "房间已清空", room);
|
||||
}
|
||||
|
||||
room.gold = 0;
|
||||
room.robotCount = Math.Clamp(room.robotCount, 0, Math.Max(0, 2 - room.users.Count));
|
||||
_redisService.SetChessRoom(room);
|
||||
|
||||
return (true, "", room);
|
||||
}
|
||||
|
||||
public (bool isok, string msg, ChessGameRoom room) AdminKickPlayer(string gameId, string userId)
|
||||
{
|
||||
var room = _redisService.GetChessRoom(gameId);
|
||||
if (room == null)
|
||||
return (false, "房间不存在", null);
|
||||
if (room.state == 2)
|
||||
return (false, "游戏进行中不能直接踢人,请先结束对局", room);
|
||||
|
||||
var user = room.users?.FirstOrDefault(c => c.userId == userId);
|
||||
if (user == null)
|
||||
return (false, "玩家不存在", room);
|
||||
|
||||
room.users.Remove(user);
|
||||
if (room.users.Count == 0)
|
||||
{
|
||||
ExitRoom(room);
|
||||
return (true, "玩家已踢出,房间已重置", room);
|
||||
}
|
||||
|
||||
if (room.createUserId == userId)
|
||||
room.createUserId = room.users[0].userId;
|
||||
room.state = 1;
|
||||
room.gold = 0;
|
||||
room.IsGameEnded = false;
|
||||
room.EndGameRequests?.Clear();
|
||||
room.PendingWinUserId = null;
|
||||
room.TimeoutLoserUserId = string.Empty;
|
||||
room.LastActiveTime = 0;
|
||||
room.robotCount = Math.Clamp(room.robotCount, 0, Math.Max(0, 2 - room.users.Count));
|
||||
foreach (var remainingUser in room.users)
|
||||
remainingUser.IsReady = false;
|
||||
_redisService.SetChessRoom(room);
|
||||
return (true, "玩家已踢出", room);
|
||||
}
|
||||
|
||||
public async Task UpdateUserGameType(string userId, int gameType, string roomNumber = "")
|
||||
{
|
||||
try
|
||||
{
|
||||
var user = await _userService.GetUser(userId);
|
||||
if (user != null)
|
||||
{
|
||||
user.Static.CurrentGameType = gameType;
|
||||
user.Static.RoomNumber = gameType > 0 ? (roomNumber ?? string.Empty) : string.Empty;
|
||||
await _userService.UpdateUser(user);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "更新用户当前游戏类型失败 userId={UserId} gameType={GameType} roomNumber={RoomNumber}", userId, gameType, roomNumber);
|
||||
}
|
||||
}
|
||||
|
||||
public (bool success, string message, ChessGameRoom room) GoldConfig(string gameId, long gold, string userId)
|
||||
{
|
||||
var room = _redisService.GetChessRoom(gameId);
|
||||
if (room == null || room.state == 3)
|
||||
{
|
||||
return (false, "房间不存在", null);
|
||||
}
|
||||
var gameconfig= _redisService.GetGameConfig();
|
||||
if (gameconfig==null)
|
||||
{
|
||||
return (false, "游戏配置不存在", null);
|
||||
}
|
||||
if(gold<gameconfig.ChessConfig.MinBetGold)
|
||||
return (false, $"金币不能小于{gameconfig.ChessConfig.MinBetGold}", null);
|
||||
if (!room.users.Any(c => c.type == 2 && c.userId == userId))
|
||||
return (false, "只有红方才可以配置金币", null);
|
||||
|
||||
|
||||
|
||||
var redPlayer = room.users.FirstOrDefault(c => c.type == 2);
|
||||
if (redPlayer != null && redPlayer.Gold < gold)
|
||||
return (false, "金币不足", null);
|
||||
room.gold = gold;
|
||||
_redisService.SetChessRoom(room);
|
||||
|
||||
|
||||
|
||||
return (true, "", room);
|
||||
}
|
||||
|
||||
public ChessGameRoom GetUserRoom(string cid)
|
||||
{
|
||||
var rooms = _redisService.GetAllChessRooms();
|
||||
if (rooms == null) return null;
|
||||
|
||||
return rooms.FirstOrDefault(c => c.users.Any(d => d.Cid == cid && (c.state == 1 || c.state == 2)));
|
||||
}
|
||||
|
||||
|
||||
public ChessGameRoom GetRoom(string gameId)
|
||||
{
|
||||
return _redisService.GetChessRoom(gameId);
|
||||
}
|
||||
|
||||
public List<ChessGameRoom> GetAllPlayingRooms()
|
||||
{
|
||||
var allRooms = _redisService.GetAllChessRooms();
|
||||
if (allRooms == null)
|
||||
return new List<ChessGameRoom>();
|
||||
|
||||
return allRooms.Where(r => r != null && r.state == 2).ToList();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.SignalR.Core" Version="1.2.9" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.3" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\RedisService\RedisService.csproj" />
|
||||
<ProjectReference Include="..\..\UserSrc\UserService\UserService.csproj" />
|
||||
<ProjectReference Include="..\ChessGameDomain\ChessGameDomain.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,55 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using ChessGameDomain;
|
||||
using ChessGameService.Param;
|
||||
|
||||
namespace ChessGameService
|
||||
{
|
||||
public interface IChessGameService
|
||||
{
|
||||
Task<(bool isok, string msg, ChessGameRoom room)> CreateRoomAsync(CreateRoomParam param, string cid);
|
||||
|
||||
Task<(bool success, string message, ChessGameRoom room)> JoinGameAsync(string gameId, string pwd, string userId, string cid);
|
||||
|
||||
List<ChessGameRoom> GetRoomList(int partition, int maxRooms = 100);
|
||||
|
||||
(bool success, string message, ChessGameRoom room) StartGame(string gameId, string userId);
|
||||
|
||||
(bool success, string message, bool allReady) GameReady(string gameId, string userId);
|
||||
|
||||
|
||||
Task<(bool success, string message, ChessGameRoom room)> EndGame(string gameId, string winUserId);
|
||||
|
||||
(bool success, string message, ChessGameRoom room) RestartGame(string gameId, string userId);
|
||||
|
||||
Task<(bool success, string message, ChessGameRoom room, bool allConfirmed)> RequestEndGame(string gameId, string userId, string endRequestId);
|
||||
|
||||
Task<(bool isTimeout, string loserUserId)> CheckRoomTimeout(string gameId);
|
||||
|
||||
Task<(bool isTimeout, string loserUserId)> CheckRoomAfkTimeout(string gameId);
|
||||
|
||||
Task UpdateUserActiveTime(string gameId, string userId);
|
||||
|
||||
Task SetPlayerAfk(string gameId, string userId);
|
||||
|
||||
Task CancelPlayerAfk(string gameId, string userId);
|
||||
bool ExitRoom(ChessGameRoom room);
|
||||
|
||||
(bool isok, string msg, ChessGameRoom room) ExitRoom(string gameId, string userId);
|
||||
|
||||
(bool isok, string msg, ChessGameRoom room) AdminKickPlayer(string gameId, string userId);
|
||||
|
||||
(bool success, string message, ChessGameRoom room) GoldConfig(string gameId, long gold, string userId);
|
||||
|
||||
ChessGameRoom GetUserRoom(string cid);
|
||||
|
||||
ChessGameRoom GetRoom(string gameId);
|
||||
|
||||
List<ChessGameRoom> GetAllPlayingRooms();
|
||||
|
||||
Task UpdateUserGameType(string userId, int gameType, string roomNumber = "");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ChessGameService.Param
|
||||
{
|
||||
public class CreateRoomParam
|
||||
{
|
||||
|
||||
public string roomId { get; set; }
|
||||
|
||||
public string userId { get; set; }
|
||||
/// <summary>
|
||||
|
||||
|
||||
public int gameType { get; set; }
|
||||
|
||||
public string password { get; set; }
|
||||
|
||||
public long gold { get; set; } = 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
using CommonDomain;
|
||||
using DotNetCore.CAP;
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace ChessGameService.Subscribers;
|
||||
|
||||
public sealed class AdminKickChessPlayerSubscriber : ICapSubscribe
|
||||
{
|
||||
private readonly IChessGameService _chessService;
|
||||
private readonly IHubContext<ChessGameHub> _hubContext;
|
||||
|
||||
public AdminKickChessPlayerSubscriber(
|
||||
IChessGameService chessService,
|
||||
IHubContext<ChessGameHub> hubContext)
|
||||
{
|
||||
_chessService = chessService;
|
||||
_hubContext = hubContext;
|
||||
}
|
||||
|
||||
[CapSubscribe(TopicKeys.后台踢出象棋玩家)]
|
||||
public async Task Handle(AdminKickChessPlayerMessage message)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(message.RoomId) || string.IsNullOrWhiteSpace(message.UserId))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var room = _chessService.GetRoom(message.RoomId);
|
||||
var target = room?.users?.FirstOrDefault(u => u.userId == message.UserId);
|
||||
if (room == null || target == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (room.state == 2)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var targetCid = target.Cid;
|
||||
var (success, resultMessage, updatedRoom) = _chessService.AdminKickPlayer(message.RoomId, message.UserId);
|
||||
if (!success || updatedRoom == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await _chessService.UpdateUserGameType(message.UserId, 0);
|
||||
if (!string.IsNullOrWhiteSpace(targetCid))
|
||||
{
|
||||
await _hubContext.Clients.Client(targetCid).SendAsync("KickedFromRoom", new
|
||||
{
|
||||
gameId = message.RoomId,
|
||||
userId = message.UserId,
|
||||
reason = string.IsNullOrWhiteSpace(message.Reason) ? "被管理员移出房间" : message.Reason,
|
||||
source = "admin"
|
||||
});
|
||||
await _hubContext.Groups.RemoveFromGroupAsync(targetCid, message.RoomId);
|
||||
await _hubContext.Groups.AddToGroupAsync(targetCid, ChessGameHub.LobbyGroup);
|
||||
}
|
||||
|
||||
await _hubContext.Clients.Group(message.RoomId).SendAsync("RoomStateNotice", updatedRoom);
|
||||
await _hubContext.Clients.Group(message.RoomId).SendAsync("BeginGame", updatedRoom);
|
||||
await _hubContext.Clients.Group(ChessGameHub.LobbyGroup).SendAsync("ExitRoomNotice", updatedRoom);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using ChessGameService;
|
||||
using CommonDomain;
|
||||
using DotNetCore.CAP;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using RedisService;
|
||||
|
||||
namespace ChessGameService.Subscribers
|
||||
{
|
||||
public class ChessLoginSubscriber : ICapSubscribe
|
||||
{
|
||||
private readonly IChessGameService _chessService;
|
||||
private readonly IRedisService _redisService;
|
||||
private readonly ICapPublisher _capPublisher;
|
||||
private readonly ILogger<ChessLoginSubscriber> _logger;
|
||||
|
||||
public ChessLoginSubscriber(
|
||||
IChessGameService chessService,
|
||||
IRedisService redisService,
|
||||
ICapPublisher capPublisher,
|
||||
ILogger<ChessLoginSubscriber> logger)
|
||||
{
|
||||
_chessService = chessService;
|
||||
_redisService = redisService;
|
||||
_capPublisher = capPublisher;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
[CapSubscribe(TopicKeys.登录绑定)]
|
||||
public async Task HandleLoginBind(LoginBindMessage msg)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(msg.UserId))
|
||||
return;
|
||||
|
||||
var rooms = _redisService.GetAllChessRooms();
|
||||
if (rooms == null)
|
||||
return;
|
||||
|
||||
foreach (var room in rooms)
|
||||
{
|
||||
if (room == null || room.state != 2 || room.IsGameEnded)
|
||||
continue;
|
||||
|
||||
var player = room.users?.FirstOrDefault(u => u.userId == msg.UserId);
|
||||
if (player == null)
|
||||
continue;
|
||||
|
||||
var winnerUserId = room.users.FirstOrDefault(u => u.userId != msg.UserId)?.userId ?? "";
|
||||
if (string.IsNullOrWhiteSpace(winnerUserId))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await _chessService.EndGame(room.RoomId, winnerUserId);
|
||||
await _capPublisher.PublishAsync(TopicKeys.登录结果通知, new LoginBindResultMessage
|
||||
{
|
||||
UserId = msg.UserId,
|
||||
RoomId = room.RoomId,
|
||||
OperationId = msg.OperationId,
|
||||
Success = true
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "[象棋] 登录结算失败 userId={UserId} roomId={RoomId}", msg.UserId, room.RoomId);
|
||||
await _capPublisher.PublishAsync(TopicKeys.登录结果通知, new LoginBindResultMessage
|
||||
{
|
||||
UserId = msg.UserId,
|
||||
RoomId = room.RoomId,
|
||||
OperationId = msg.OperationId,
|
||||
Success = false,
|
||||
Error = ex.Message
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user