添加项目文件。
This commit is contained in:
@@ -0,0 +1,433 @@
|
||||
using CommonDomain;
|
||||
using FishGameService.GameSend;
|
||||
using FishGameService.GameService;
|
||||
using System.Text.Json;
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using RedisService;
|
||||
using RedisService.Param;
|
||||
using UserService.Hubs;
|
||||
using UserService.User;
|
||||
|
||||
namespace FishGameService.Hubs
|
||||
{
|
||||
public class FishGameHub : Hub
|
||||
{
|
||||
private readonly IFishingGameService _fishingService;
|
||||
private readonly IFishingSendService _send;
|
||||
private readonly IRedisService _redisService;
|
||||
private readonly IUserService _userService;
|
||||
private readonly ILogger<FishGameHub> _logger;
|
||||
private readonly IHubContext<LoginHub> _hubLoginContext;
|
||||
|
||||
|
||||
public FishGameHub(IFishingGameService fishingService, IFishingSendService send, IRedisService redisService, IUserService userService, ILogger<FishGameHub> logger, IHubContext<LoginHub> hubLoginContext)
|
||||
{
|
||||
_fishingService = fishingService;
|
||||
_send = send;
|
||||
_redisService = redisService;
|
||||
_userService = userService;
|
||||
_logger = logger;
|
||||
_hubLoginContext = hubLoginContext;
|
||||
}
|
||||
|
||||
public override async Task OnConnectedAsync()
|
||||
{
|
||||
|
||||
try
|
||||
{
|
||||
await base.OnConnectedAsync();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "FishHub建立连接异常 cid={Cid}", Context.ConnectionId);
|
||||
}
|
||||
}
|
||||
|
||||
public override async Task OnDisconnectedAsync(Exception? exception)
|
||||
{
|
||||
var cid = Context.ConnectionId;
|
||||
var userId = _redisService.GetUserId(cid);
|
||||
if (exception != null)
|
||||
{
|
||||
_logger.LogWarning(exception, "FishHub异常断开 cid={Cid} userId={UserId} reason={Reason}",
|
||||
cid, userId, exception.Message);
|
||||
}
|
||||
try
|
||||
{
|
||||
await _fishingService.HandleDisconnect(Context.ConnectionId);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "FishHub断线处理失败 cid={Cid} userId={UserId}", cid, userId);
|
||||
}
|
||||
|
||||
await base.OnDisconnectedAsync(exception);
|
||||
}
|
||||
|
||||
public async Task Reconnect(string userId)
|
||||
{
|
||||
try
|
||||
{
|
||||
await ReconnectInternal(userId);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "FishHub重连异常 cid={Cid} userId={UserId}", Context.ConnectionId, userId);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ReconnectInternal(string userId)
|
||||
{
|
||||
var cid = Context.ConnectionId;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(userId))
|
||||
{
|
||||
_send.ErrorMessage(cid, ErrorCodes.用户不存在, "缺少 userId,无法重连");
|
||||
return;
|
||||
}
|
||||
|
||||
var userRedis = _redisService.GetRedisUserCId(userId);
|
||||
if (userRedis == null)
|
||||
{
|
||||
userRedis = new RedisUserCid { UserId = userId, CId = cid };
|
||||
_redisService.SetRedisUserCId(userRedis);
|
||||
_redisService.SetUserId(cid, userId);
|
||||
_send.BindSuccess(cid);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var oldCid = userRedis.CId;
|
||||
if (!string.IsNullOrWhiteSpace(oldCid) && oldCid != cid)
|
||||
{
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(userRedis.RoomId))
|
||||
{
|
||||
|
||||
try { _send.RoomLeaveNotice(userRedis.RoomId, userId); } catch (Exception ex) { _logger.LogError(ex, "发送RoomLeaveNotice失败 userId={UserId} roomId={RoomId}", userId, userRedis.RoomId); }
|
||||
}
|
||||
|
||||
_redisService.RemoveUserId(oldCid);
|
||||
try
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(userRedis.RoomId))
|
||||
await Groups.RemoveFromGroupAsync(oldCid, userRedis.RoomId);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogWarning(ex, "FishHub重连移除旧连接房间组失败 oldCid={OldCid} roomId={RoomId}", oldCid, userRedis.RoomId);
|
||||
}
|
||||
}
|
||||
|
||||
userRedis.CId = cid;
|
||||
_redisService.SetRedisUserCId(userRedis);
|
||||
_redisService.SetUserId(cid, userId);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(userRedis.RoomId))
|
||||
{
|
||||
var room = _redisService.GetGameRoom(userRedis.RoomId);
|
||||
if (room != null)
|
||||
{
|
||||
var player = room.Players.FirstOrDefault(p => p.UserId == userId);
|
||||
if (player != null)
|
||||
{
|
||||
await _fishingService.CancelPendingDisconnect(userId);
|
||||
player.CId = cid;
|
||||
player.State = 0;
|
||||
|
||||
bool ownerRestored = false;
|
||||
if (!string.IsNullOrWhiteSpace(userId) && room.CreateUserId != userId)
|
||||
{
|
||||
var currentOwner = room.Players.FirstOrDefault(p => p.UserId == room.CreateUserId && !p.IsBot);
|
||||
if (string.IsNullOrWhiteSpace(room.CreateUserId)
|
||||
|| currentOwner == null
|
||||
|| currentOwner.State != 0)
|
||||
{
|
||||
room.CreateUserId = userId;
|
||||
ownerRestored = true;
|
||||
}
|
||||
}
|
||||
|
||||
_redisService.SetGameRoom(room);
|
||||
|
||||
await Groups.AddToGroupAsync(cid, room.RoomId);
|
||||
|
||||
var activeFishes = _fishingService.GetActiveFishes(room.RoomId);
|
||||
await _send.SendRoomInfo(cid, room, activeFishes);
|
||||
_send.RoomJoinNotice(room.RoomId, player);
|
||||
await Clients.Caller.SendAsync("OnPlayerInfo", new
|
||||
{
|
||||
userId = player.UserId,
|
||||
userName = player.UserName,
|
||||
gold = player.Gold,
|
||||
diamond = player.Diamond,
|
||||
viplvl = player.VipLevel,
|
||||
cannonMultiple = player.CannonMultiple,
|
||||
cannonId = player.CannonId,
|
||||
wingsId = player.WingsId,
|
||||
position = player.Position
|
||||
});
|
||||
await Clients.Group(room.RoomId).SendAsync("OnSeatChange", new
|
||||
{
|
||||
type = "join",
|
||||
playerId = player.Position + 1,
|
||||
position = player.Position,
|
||||
playerName = player.UserName,
|
||||
nickname = player.UserName,
|
||||
gold = player.Gold,
|
||||
diamond = player.Diamond,
|
||||
vipLevel = player.VipLevel,
|
||||
turretId = 1,
|
||||
turretRate = 1,
|
||||
turretLv = 1,
|
||||
headId = 1,
|
||||
roomRate = 1,
|
||||
bulletPower = 1
|
||||
});
|
||||
if (ownerRestored)
|
||||
_send.ChangeControllerUser(room.RoomId, room.CreateUserId);
|
||||
|
||||
await _fishingService.SendRoomChange(room);
|
||||
|
||||
await SendActiveFishSync(room.RoomId);
|
||||
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
var user = await _userService.GetUser(userId);
|
||||
if (user != null)
|
||||
{
|
||||
user.Static.RoomNumber = room.RoomId;
|
||||
user.Static.CurrentGameType = (int)room.GameType;
|
||||
await _userService.UpdateUser(user);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogWarning(ex, "FishHub重连后更新用户状态失败 userId={UserId} roomId={RoomId}", userId, room.RoomId);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await SendActiveFishSync(room.RoomId);
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogWarning(ex, "FishHub重连同服务端步活跃鱼失败 userId={UserId} roomId={RoomId}", userId, room.RoomId);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_send.BindSuccess(cid);
|
||||
|
||||
}
|
||||
|
||||
public async Task JoinRoom(string roomId, string userId, int posituin)
|
||||
{
|
||||
string cid = Context.ConnectionId;
|
||||
try
|
||||
{
|
||||
var boundUserId = _redisService.GetUserId(cid);
|
||||
if (!string.IsNullOrWhiteSpace(boundUserId) && boundUserId != userId)
|
||||
{
|
||||
|
||||
await Clients.Caller.SendAsync("KickToMainPage", new { userId });
|
||||
return;
|
||||
}
|
||||
|
||||
await _fishingService.JoinRoom(cid, roomId, userId, posituin);
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "FishHub加入房间异常 cid={Cid} roomId={RoomId} userId={UserId} position={Position}", cid, roomId, userId, posituin);
|
||||
_send.ErrorMessage(cid, ErrorCodes.未知错误, $"加入房间失败: {ex.Message}");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
public async Task GetRoomInfo(string roomId)
|
||||
{
|
||||
string cid = Context.ConnectionId;
|
||||
if (!IsConnectionInRoom(cid, roomId))
|
||||
return;
|
||||
var room = _redisService.GetGameRoom(roomId);
|
||||
|
||||
await Clients.Caller.SendAsync("GetRoomInfoResult", room);
|
||||
|
||||
}
|
||||
|
||||
public async Task SyncGameData(string roomId, object data)
|
||||
{
|
||||
_send.SyncGameData(roomId, data);
|
||||
}
|
||||
|
||||
private bool IsConnectionInRoom(string cid, string roomId)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(cid) || string.IsNullOrWhiteSpace(roomId))
|
||||
return false;
|
||||
|
||||
var userId = _redisService.GetUserId(cid);
|
||||
if (string.IsNullOrWhiteSpace(userId))
|
||||
return false;
|
||||
|
||||
var userRedis = _redisService.GetRedisUserCId(userId);
|
||||
return userRedis != null && userRedis.CId == cid && userRedis.RoomId == roomId;
|
||||
}
|
||||
public async Task<int> UseProp(string roomId, string userId, string propNumber, int diamond)
|
||||
{
|
||||
string cid = Context.ConnectionId;
|
||||
|
||||
return await _fishingService.UseProp(cid, roomId, userId, propNumber, diamond);
|
||||
}
|
||||
|
||||
public async Task<long> UseTorpedo(string roomId, string userId, string propNumber)
|
||||
{
|
||||
string cid = Context.ConnectionId;
|
||||
return await _fishingService.UseTorpedo(cid, roomId, userId, propNumber);
|
||||
}
|
||||
|
||||
public async Task<object> FireCannon(string roomId, string userId, long gold, int count, long timestamp, string sign, object extra = null)
|
||||
{
|
||||
string cid = Context.ConnectionId;
|
||||
var result = extra is string extraSign && (sign == null || sign.Length != 8)
|
||||
? await _fishingService.FireCannon(cid, roomId, userId, gold, count, timestamp, extraSign, null)
|
||||
: await _fishingService.FireCannon(cid, roomId, userId, gold, count, timestamp, sign, extra);
|
||||
return new
|
||||
{
|
||||
bullets = result.bullets.Select(bulletId => new { bulletId, gold }).ToList(),
|
||||
gold = result.gold,
|
||||
timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<object> KillFish(string roomId, string userId, string fishNumber, string fihsId, long gold, long timespan, string bulletId, long timestamp, string sign)
|
||||
{
|
||||
string cid = Context.ConnectionId;
|
||||
|
||||
var currentGold = await _fishingService.KillFish(cid, roomId, userId, fishNumber, fihsId, gold, timespan, bulletId, timestamp, sign);
|
||||
return new { gold = currentGold, timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() };
|
||||
}
|
||||
|
||||
public async Task LeaveRoom(string roomId, string userId)
|
||||
{
|
||||
string cid = Context.ConnectionId;
|
||||
try
|
||||
{
|
||||
|
||||
|
||||
await _fishingService.LeaveRoom(cid, roomId, userId);
|
||||
|
||||
if (IsConnectionInRoom(cid, roomId))
|
||||
{
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "FishHub离开房间异常 cid={Cid} roomId={RoomId} userId={UserId}", cid, roomId, userId);
|
||||
throw;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public async Task ChangeGunMultiple(string roomId, string userId, int multiple)
|
||||
{
|
||||
string cid = Context.ConnectionId;
|
||||
|
||||
await _fishingService.ChangeGunMultiple(cid, roomId, userId, multiple);
|
||||
}
|
||||
|
||||
|
||||
public async Task GetPlayerInfo(string roomId, string userId)
|
||||
{
|
||||
string cid = Context.ConnectionId;
|
||||
await _fishingService.GetPlayerInfo(cid, roomId, userId);
|
||||
}
|
||||
|
||||
public async Task SetPlayerAfk(string roomId, string userId)
|
||||
{
|
||||
string cid = Context.ConnectionId;
|
||||
await _fishingService.SetPlayerAfk(cid, roomId, userId);
|
||||
}
|
||||
|
||||
public async Task CancelPlayerAfk(string roomId, string userId)
|
||||
{
|
||||
string cid = Context.ConnectionId;
|
||||
await _fishingService.CancelPlayerAfk(cid, roomId, userId);
|
||||
|
||||
try
|
||||
{
|
||||
var room = _redisService.GetGameRoom(roomId);
|
||||
if (room != null)
|
||||
{
|
||||
var activeFishes = _fishingService.GetActiveFishes(roomId);
|
||||
await _send.SendRoomInfo(cid, room, activeFishes);
|
||||
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogWarning(ex, "CancelPlayerAfk 同步房间信息失败 userId={UserId} roomId={RoomId}", userId, roomId);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task SendActiveFishSync(string roomId)
|
||||
{
|
||||
var activeFishes = _fishingService.GetActiveFishes(roomId);
|
||||
if (activeFishes == null || activeFishes.Count == 0)
|
||||
return;
|
||||
|
||||
var now = DateTimeOffset.UtcNow;
|
||||
var syncFish = activeFishes.Select(f => new
|
||||
{
|
||||
fishId = f.FishId,
|
||||
fishIds = new[] { f.FishId },
|
||||
fishNumber = string.IsNullOrWhiteSpace(f.FishArrayNumber) ? f.FishNumber : string.Empty,
|
||||
routeId = f.ClientRouteId,
|
||||
fishArrayNumber = f.FishArrayNumber ?? string.Empty,
|
||||
createdAt = new DateTimeOffset(f.CreatedAt).ToUnixTimeMilliseconds(),
|
||||
createdTime = f.CreatedAt.ToString("yyyy-MM-dd HH:mm:ss.fff"),
|
||||
expireAt = new DateTimeOffset(f.ExpireAt).ToUnixTimeMilliseconds(),
|
||||
expireTime = f.ExpireAt.ToString("yyyy-MM-dd HH:mm:ss.fff"),
|
||||
remainingSeconds = (int)Math.Max(0, (new DateTimeOffset(f.ExpireAt) - now).TotalSeconds)
|
||||
}).ToList();
|
||||
|
||||
var payload = new
|
||||
{
|
||||
type = "sync",
|
||||
timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(),
|
||||
serverTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff"),
|
||||
fish = syncFish
|
||||
};
|
||||
|
||||
|
||||
await Clients.Caller.SendAsync("OnFishSpawn", payload);
|
||||
}
|
||||
|
||||
|
||||
public async Task<List<string>> BotFireCannon(string roomId, string botUserId, long gold, int count)
|
||||
{
|
||||
string cid = Context.ConnectionId;
|
||||
return await _fishingService.BotFireCannon(cid, roomId, botUserId, gold, count);
|
||||
}
|
||||
|
||||
public async Task BotKillFish(string roomId, string botUserId, string fishNumber, string fishId, long gold, string bulletId)
|
||||
{
|
||||
string cid = Context.ConnectionId;
|
||||
await _fishingService.BotKillFish(cid, roomId, botUserId, fishNumber, fishId, gold, bulletId);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user