291 lines
12 KiB
C#
291 lines
12 KiB
C#
using System.Text.Json;
|
|
using AutoMapper;
|
|
using Common.Utils;
|
|
using CommonService;
|
|
using FishGameService.GameSend;
|
|
using FishGameService.Hubs;
|
|
using FishGameService.Services;
|
|
using Microsoft.Extensions.Logging;
|
|
using RedisService.Param;
|
|
using StackExchange.Redis;
|
|
|
|
namespace Service.Fishing
|
|
{
|
|
public class FishingSendService : IFishingSendService
|
|
{
|
|
|
|
public readonly IHubSend<FishGameHub> _hubSend;
|
|
public readonly IMapper _mapper;
|
|
private readonly ISubscriber _subscriber;
|
|
private readonly ILogger<FishingSendService> _logger;
|
|
|
|
public FishingSendService(IHubSend<FishGameHub> hubSend, IMapper mapper, IConnectionMultiplexer redis, ILogger<FishingSendService> logger)
|
|
{
|
|
_hubSend = hubSend;
|
|
_mapper = mapper;
|
|
_subscriber = redis.GetSubscriber();
|
|
_logger = logger;
|
|
}
|
|
public void BindSuccess(string cid)
|
|
{
|
|
_hubSend.SendUserCId(cid, "BindSuccess", "绑定成功");
|
|
}
|
|
public void ErrorMessage(string cid, int code, string msg)
|
|
{
|
|
_hubSend.SendUserCId(cid, "Exception", new { code, message = msg });
|
|
}
|
|
|
|
|
|
|
|
public void KillNotice(string FishId, string userName, long gold = 0)
|
|
{
|
|
var msg = JsonSerializer.Serialize(new { Target = "All", Event = "KillNotice", Data = new { FishId, userName, gold } });
|
|
_subscriber.Publish("fishgame:global", msg);
|
|
}
|
|
|
|
public void KillNotice(string FishId, string userName, long gold, string fishName, long multiplier,
|
|
string gameTypeName, string roomName, long timestamp)
|
|
{
|
|
var text = $"恭喜玩家{userName}在{gameTypeName}{roomName}捕获 {fishName}*{multiplier}炮倍 {gold}大奖";
|
|
var data = new
|
|
{
|
|
Text = text,
|
|
UserName = userName,
|
|
GameTypeName = gameTypeName,
|
|
RoomName = roomName,
|
|
FishName = fishName,
|
|
Multiple = multiplier,
|
|
Gold = gold,
|
|
Time = timestamp,
|
|
FishId
|
|
};
|
|
var msg = JsonSerializer.Serialize(new { Target = "All", Event = "KillNotice", Data = data });
|
|
_subscriber.Publish("fishgame:global", msg);
|
|
}
|
|
|
|
public async Task SendRoomInfo(string cid, RedisGameRoom roomInfo, List<ActiveFish> activeFishes = null, bool sendInitialFish = true)
|
|
{
|
|
object payload;
|
|
List<object> initialFishPayload = null;
|
|
if (activeFishes != null && activeFishes.Count > 0)
|
|
{
|
|
initialFishPayload = BuildActiveFishPayload(activeFishes);
|
|
|
|
payload = new
|
|
{
|
|
roomInfo.RoomId,
|
|
roomInfo.RoomName,
|
|
roomInfo.GameType,
|
|
roomInfo.RoomLevel,
|
|
roomInfo.RoomPartition,
|
|
roomInfo.RoomNumber,
|
|
roomInfo.MaxPlayerCount,
|
|
roomInfo.CurrentOnlinePlayerCount,
|
|
roomInfo.RobotCount,
|
|
roomInfo.Players,
|
|
roomInfo.CreateUserId,
|
|
roomInfo.SystemFactor,
|
|
activeFishes = initialFishPayload
|
|
};
|
|
|
|
|
|
}
|
|
else
|
|
{
|
|
payload = roomInfo;
|
|
}
|
|
await _hubSend.SendUserCIdAsync(cid, "RoomInfo", payload);
|
|
if (sendInitialFish && initialFishPayload != null && initialFishPayload.Count > 0)
|
|
{
|
|
_ = SendInitialFishAfterSceneReady(cid, roomInfo.RoomId, initialFishPayload);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 补发场上活鱼(挂起恢复 / 重连专用独立协议):
|
|
/// 发到独立事件 OnFishResync,客户端按"全量替换"处理(先清本地旧鱼,再按本列表重建)。
|
|
/// fish 列表口径与 RoomInfo.activeFishes / 首屏 init 完全一致,都带服务端原始的 createdAt。
|
|
/// </summary>
|
|
public async Task FishResyncNoticeAsync(string cid, string roomId, List<ActiveFish> activeFishes)
|
|
{
|
|
var fish = activeFishes == null || activeFishes.Count == 0
|
|
? new List<object>()
|
|
: BuildActiveFishPayload(activeFishes);
|
|
|
|
var now = DateTimeOffset.UtcNow;
|
|
await _hubSend.SendUserCIdAsync(cid, "OnFishResync", new
|
|
{
|
|
roomId,
|
|
timestamp = now.ToUnixTimeMilliseconds(),
|
|
serverTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff"),
|
|
count = fish.Count,
|
|
fish
|
|
});
|
|
}
|
|
|
|
/// <summary>
|
|
/// 把场上活鱼转成下发 payload:单体鱼逐条、鱼阵按 FishArrayNumber 分组(鱼阵用首条 fishId 作主键)。
|
|
/// createdAt / expireAt 一律取服务端原始值,客户端据此按 (当前时间 - createdAt) / 路径总时长 推算位置。
|
|
/// </summary>
|
|
private static List<object> BuildActiveFishPayload(List<ActiveFish> activeFishes)
|
|
{
|
|
var now = DateTimeOffset.UtcNow;
|
|
return activeFishes
|
|
.Where(f => f != null && string.IsNullOrWhiteSpace(f.FishArrayNumber))
|
|
.Select(f => (object)new
|
|
{
|
|
fishId = f.FishId,
|
|
fishIds = new[] { f.FishId },
|
|
fishNumber = f.FishNumber,
|
|
routeId = f.ClientRouteId,
|
|
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)
|
|
})
|
|
.Concat(
|
|
activeFishes
|
|
.Where(f => f != null && !string.IsNullOrWhiteSpace(f.FishArrayNumber))
|
|
.GroupBy(f => f.FishArrayNumber)
|
|
.Select(group =>
|
|
{
|
|
var first = group.First();
|
|
return (object)new
|
|
{
|
|
fishId = first.FishId,
|
|
fishIds = group.Select(f => f.FishId).ToList(),
|
|
fishNumber = string.Empty,
|
|
routeId = first.ClientRouteId,
|
|
fishArrayNumber = first.FishArrayNumber,
|
|
createdAt = new DateTimeOffset(first.CreatedAt).ToUnixTimeMilliseconds(),
|
|
createdTime = first.CreatedAt.ToString("yyyy-MM-dd HH:mm:ss.fff"),
|
|
expireAt = new DateTimeOffset(group.Min(f => f.ExpireAt)).ToUnixTimeMilliseconds(),
|
|
expireTime = group.Min(f => f.ExpireAt).ToString("yyyy-MM-dd HH:mm:ss.fff"),
|
|
remainingSeconds = (int)Math.Max(0, (new DateTimeOffset(group.Min(f => f.ExpireAt)) - now).TotalSeconds)
|
|
};
|
|
}))
|
|
.ToList();
|
|
}
|
|
|
|
private async Task SendInitialFishAfterSceneReady(string cid, string roomId, List<object> initialFishPayload)
|
|
{
|
|
try
|
|
{
|
|
await Task.Delay(300);
|
|
var syncNow = DateTimeOffset.UtcNow;
|
|
await _hubSend.SendUserCIdAsync(cid, "OnFishSpawn", new
|
|
{
|
|
type = "init",
|
|
timestamp = syncNow.ToUnixTimeMilliseconds(),
|
|
serverTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff"),
|
|
fish = initialFishPayload
|
|
});
|
|
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogWarning(ex,
|
|
"[Socket][FishSpawnData] 首屏鱼延迟补发失败 roomId={RoomId} targetCid={Cid} fishCount={FishCount}",
|
|
roomId, cid, initialFishPayload.Count);
|
|
}
|
|
}
|
|
public void RoomKillNotice(string roomId, string fishNumber, string userId, long gold, string fishId, long timespan, double beishu = 1,long skillsGold=0,int fishScore=1, object param = null, object drop = null, long gainExp = 0, long exp = 0, int level = 0, long expMax = 0)
|
|
{
|
|
|
|
var killtime=DateTime.Now.ToTimeStamp();
|
|
|
|
_hubSend.SendGroup(roomId, "RoomKillNotice", new { fishNumber, userId, gold, fishId, timespan, killtime, msg = $"时间:{killtime - timespan}秒", beishu, skillsGold, fishScore, param, drop, gainExp, exp, level, expMax });
|
|
}
|
|
|
|
|
|
public void RoomDisconnectNotice(string roomId, string userId)
|
|
{
|
|
_hubSend.SendGroup(roomId, "RoomDisconnectNotice", userId);
|
|
}
|
|
|
|
public void RoomJoinNotice(string roomId, RedisGamePlayer player)
|
|
{
|
|
_hubSend.SendGroup(roomId, "RoomJoinNotice", player);
|
|
}
|
|
|
|
public void ChangeGunMultipleNotice(string roomId, string userId, int multiple)
|
|
{
|
|
_hubSend.SendGroup(roomId, "ChangeGunMultipleNotice", new { userId, multiple });
|
|
}
|
|
|
|
public void RoomFireNotice(string roomId, string userId, List<string> bulletIds, long gold, int count, object extra = null, bool isBot = false)
|
|
{
|
|
_hubSend.SendGroup(roomId, "RoomFireNotice", new { userId, bulletIds, gold, count, extra, isBot });
|
|
}
|
|
|
|
public Task RoomFireNoticeAsync(string roomId, string callerCid, string userId, List<string> bulletIds, long gold, int count, object extra = null, bool isBot = false)
|
|
{
|
|
var payload = new { userId, bulletIds, gold, count, extra, isBot };
|
|
return Task.WhenAll(
|
|
_hubSend.SendUserCIdAsync(callerCid, "RoomFireNotice", payload),
|
|
_hubSend.GroupExceptAsync(callerCid, roomId, "RoomFireNotice", payload));
|
|
}
|
|
|
|
|
|
public void SyncPlayerGold(string cid, long gold)
|
|
{
|
|
_hubSend.SendUserCId(cid, "SyncPlayerGold", gold);
|
|
}
|
|
public void SendPlayerInfo(string cid, RedisGamePlayer player)
|
|
{
|
|
_hubSend.SendUserCId(cid, "SendPlayerInfoResult", player?.Gold);
|
|
}
|
|
|
|
|
|
public void RoomLeaveNotice(string roomId, string userId)
|
|
{
|
|
_hubSend.SendGroup(roomId, "RoomLeaveNotice", userId);
|
|
}
|
|
|
|
|
|
public void BotLeaveRoomNotice(string roomId, string userId)
|
|
{
|
|
_hubSend.SendGroup(roomId, "BotLeaveRoomNotice", new { userId });
|
|
}
|
|
|
|
|
|
public void ChangeControllerUser(string roomId, string userId)
|
|
{
|
|
_hubSend.SendGroup(roomId, "ChangeControllerUser", userId);
|
|
}
|
|
|
|
public void HostDisconnectNotice(string roomId)
|
|
{
|
|
_hubSend.SendGroup(roomId, "HostDisconnect", null);
|
|
}
|
|
|
|
|
|
public void SyncGameData(string roomId, object data)
|
|
{
|
|
_hubSend.SendGroup(roomId, "SyncGameData", data);
|
|
}
|
|
|
|
public void UsePropNotice(string roomId, string userId, string propNumber)
|
|
{
|
|
_hubSend.SendGroup(roomId, "UsePropNotice", new { userId, propNumber });
|
|
}
|
|
|
|
public void TorpedoResultNotice(string roomId, string userId, string propNumber, long gold, string color)
|
|
{
|
|
_hubSend.SendGroup(roomId, "TorpedoResultNotice", new { userId, propNumber, gold, color });
|
|
}
|
|
|
|
public void PlayerAfkNotice(string roomId, string userId)
|
|
{
|
|
_hubSend.SendGroup(roomId, "PlayerAfkNotice", new { userId });
|
|
}
|
|
|
|
public void PlayerCancelAfkNotice(string roomId, string userId)
|
|
{
|
|
_hubSend.SendGroup(roomId, "PlayerCancelAfkNotice", new { userId });
|
|
}
|
|
}
|
|
}
|