919 lines
38 KiB
C#
919 lines
38 KiB
C#
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();
|
|
}
|
|
}
|
|
}
|