Files
hjha-server/GameModule/Manager/FriendRoomStatistics.cs
xiaoou e9616125ce feat: initial commit - HJHA game server full source
6 major server modules (PdkFriendServer/GlobalSever/ServerCore/GameModule/GameNetModule) +
game logic (GameFix/GameDAL/ServerData) +
network layer (NetWorkMessage) +
data layer (ObjectModel) +
utilities (MrWu/Core/Config/CloudAPI/dll) +
adapters (zyxAdapter/base)

.NET 8.0 C# solution, 16 projects, 958 source files
2026-07-07 12:02:15 +08:00

576 lines
20 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading;
using MrWu;
using System.Threading.Tasks;
using MrWu.Debug;
using Newtonsoft.Json;
using Server.Core;
namespace Server
{
/// <summary>
/// 位置胜率,胜率最高者,胜率最低者,金额最高者,金额最低者
/// </summary>
public class FriendRoomStatistics : SingleInstance<FriendRoomStatistics>
{
private readonly object lockObj = new object();
/// <summary>
/// 上一天的统计数据
/// </summary>
private Dictionary<string, WayStatics> LastDayWayStaticsList = new Dictionary<string, WayStatics>();
/// <summary>
/// 玩法数据统计
/// </summary>
private Dictionary<string, WayStatics> WayStaticsMap = new Dictionary<string, WayStatics>();
private Timer _timer;
/// <summary>
/// 下次发送通知的时间
/// </summary>
private long NextSendNotice;
private string gameName;
public FriendRoomStatistics()
{
LoadData();
GameDispatcher.Instance.AddListener(GameMsgId.DayChange, OnDayChange);
_timer = new Timer(OnTimer, null, 1000, 60 * 1000);
SetNextNotice();
}
public void Start()
{
gameName = ServerConfigManager.Instance.GameName;
}
private void OnTimer(object state)
{
if (TimeInfo.Instance.FrameTime > NextSendNotice)
{
SetNextNotice();
SendNotice();
}
}
private void SetNextNotice()
{
int hour = 9;
if (DateTime.Now >= DateTime.Today.AddHours(hour))
{
hour += 24;
}
NextSendNotice = TimeInfo.Instance.TransitionLocationDateTime(DateTime.Today.AddHours(hour));
}
private const float Median = 0.5f;
/// <summary>
/// 发送上一天的通知
/// </summary>
private void SendNotice()
{
StringBuilder stringBuilder = null;
List<string> lines = new List<string>();
lock (lockObj)
{
if (LastDayWayStaticsList.Count <= 0)
{
return;
}
stringBuilder = new StringBuilder();
//统计座位
foreach (var kv in LastDayWayStaticsList)
{
if (kv.Value.PlayerStaticsMap.Count <= 0)
{
continue;
}
stringBuilder.Clear();
stringBuilder.AppendLine($"{gameName}统计报告:");
stringBuilder.AppendLine();
stringBuilder.AppendLine($"{kv.Value.WayId} 倍率:{kv.Value.BeiLv}");
foreach (var seatKv in kv.Value.SeatStaticsMap)
{
if (seatKv.Value.TotalCnt <= 0)
{
continue;
}
float WinningRate = (float)seatKv.Value.WinCnt / seatKv.Value.TotalCnt;
stringBuilder.AppendLine($"座位{seatKv.Value.Seat} 胜率:%{(WinningRate * 100):f2}");
}
stringBuilder.AppendLine($"玩家人数:{kv.Value.PlayerStaticsMap.Count}");
//最高胜率
PlayerStatics maxWinningRatePlayerStatics = null;
float maxWinningRate = 0f;
PlayerStatics minWinningRatePlayerStatics = null;
//最低胜率
float minWinningRate = 1f;
//最高场次胜率
PlayerStatics maxSessionWinningRatePlayerStatics = null;
float maxSessionWinningRate = 0f;
//最低场次胜率
PlayerStatics minSessionWinningRatePlayerStatics = null;
float minSessionWinningRate = 1f;
//最高分
PlayerStatics maxScorePlayerStatics = null;
int maxScore = 0;
//最低分
PlayerStatics minScorePlayerStatics = null;
int minScore = int.MaxValue;
foreach (var playerKv in kv.Value.PlayerStaticsMap)
{
if (playerKv.Value.TotalCnt > 20) //大于20局才统计
{
float winningRate = (float)playerKv.Value.WinCnt / playerKv.Value.TotalCnt;
if (winningRate > maxWinningRate)
{
maxWinningRate = winningRate;
maxWinningRatePlayerStatics = playerKv.Value;
}
if (winningRate < Median && winningRate < minWinningRate)
{
minWinningRate = winningRate;
minWinningRatePlayerStatics = playerKv.Value;
}
}
if (playerKv.Value.TotalSessionCnt > 10) //大于10局才统计
{
float winningRate = (float)playerKv.Value.WinSessionCnt / playerKv.Value.TotalSessionCnt;
if (winningRate > Median && winningRate > maxSessionWinningRate)
{
maxSessionWinningRate = winningRate;
maxSessionWinningRatePlayerStatics = playerKv.Value;
}
if (winningRate < Median && winningRate < minSessionWinningRate)
{
minSessionWinningRate = winningRate;
minSessionWinningRatePlayerStatics = playerKv.Value;
}
}
if (playerKv.Value.TotalSessionScore > maxScore)
{
maxScore = (int)playerKv.Value.TotalSessionScore;
maxScorePlayerStatics = playerKv.Value;
}
if (playerKv.Value.TotalSessionScore < minScore)
{
minScore = (int)playerKv.Value.TotalSessionScore;
minScorePlayerStatics = playerKv.Value;
}
}
//最高胜率
if (maxWinningRatePlayerStatics != null)
{
stringBuilder.AppendLine(
$"最高胜率:%{(maxWinningRate * 100):f2}");
stringBuilder.AppendLine(
$" 玩家:{maxWinningRatePlayerStatics.UserId} 局数:{maxWinningRatePlayerStatics.TotalCnt} 幸运次数:{maxWinningRatePlayerStatics.LuckCnt} 霉运次数:{maxWinningRatePlayerStatics.BadLuckCnt}");
}
if (minWinningRatePlayerStatics != null)
{
stringBuilder.AppendLine(
$"最低胜率:%{(minWinningRate * 100):f2}");
stringBuilder.AppendLine(
$" 玩家:{minWinningRatePlayerStatics.UserId} 局数:{minWinningRatePlayerStatics.TotalCnt} 幸运次数:{minWinningRatePlayerStatics.LuckCnt} 霉运次数:{minWinningRatePlayerStatics.BadLuckCnt}");
}
if (maxSessionWinningRatePlayerStatics != null)
{
stringBuilder.AppendLine(
$"场次最高胜率:%{(maxSessionWinningRate * 100):f2}");
stringBuilder.AppendLine(
$" 玩家:{maxSessionWinningRatePlayerStatics.UserId} 场次:{maxSessionWinningRatePlayerStatics.TotalSessionCnt} 幸运次数:{maxSessionWinningRatePlayerStatics.LuckCnt} 霉运次数:{maxSessionWinningRatePlayerStatics.BadLuckCnt}");
}
if (minSessionWinningRatePlayerStatics != null)
{
stringBuilder.AppendLine(
$"场次最低胜率:%{(minSessionWinningRate * 100):f2}");
stringBuilder.AppendLine(
$" 玩家:{minSessionWinningRatePlayerStatics.UserId} 场次:{minSessionWinningRatePlayerStatics.TotalSessionCnt} 幸运次数:{minSessionWinningRatePlayerStatics.LuckCnt} 霉运次数:{minSessionWinningRatePlayerStatics.BadLuckCnt}");
}
if (maxScorePlayerStatics != null)
{
stringBuilder.AppendLine($"最高分:{maxScore}");
stringBuilder.AppendLine(
$" 玩家:{maxScorePlayerStatics.UserId} 幸运次数:{maxScorePlayerStatics.LuckCnt} 霉运次数:{maxScorePlayerStatics.BadLuckCnt}");
}
if (minScorePlayerStatics != null)
{
stringBuilder.AppendLine($"最低分:{minScore}");
stringBuilder.AppendLine(
$" 玩家:{minScorePlayerStatics.UserId} 幸运次数:{minScorePlayerStatics.LuckCnt} 霉运次数:{minScorePlayerStatics.BadLuckCnt}");
}
lines.Add(stringBuilder.ToString());
}
}
foreach (var line in lines)
{
if (!string.IsNullOrEmpty(line))
{
WeChatRobotManager.Instance.SendMsg(line, WeChatRobotManager.RobotType.StatisticsReport);
}
}
}
private void LoadData()
{
try
{
//加载昨天和今天的数据
LoadData(DateTime.Now.AddDays(-1), LastDayWayStaticsList);
LoadData(DateTime.Now, WayStaticsMap);
}
catch (Exception e)
{
Debug.Error($"加载失败! {e.Message}");
}
}
public void AddBalance(string wayId, int luckUserId, int badLuckUserId)
{
if (luckUserId <= 0 && badLuckUserId <= 0)
{
return;
}
WayStatics wayStatics = GetWayStatics(wayId);
if (luckUserId > 0 &&
wayStatics.PlayerStaticsMap.TryGetValue(luckUserId, out PlayerStatics luckPlayerStatics))
{
luckPlayerStatics.LuckCnt++;
}
if (badLuckUserId > 0 &&
wayStatics.PlayerStaticsMap.TryGetValue(badLuckUserId, out PlayerStatics badLuckplayerStatics))
{
badLuckplayerStatics.BadLuckCnt++;
}
}
private void LoadData(DateTime dateTime, Dictionary<string, WayStatics> staticsMap)
{
string filename = GetFileName(dateTime);
if (File.Exists(filename))
{
string jsonData = File.ReadAllText(filename);
List<WayStatics> staticsList = JsonConvert.DeserializeObject<List<WayStatics>>(jsonData);
Debug.Info($"加载的数据:{filename}");
for (int i = 0; i < staticsList.Count; i++)
{
WayStatics wayStatics = staticsList[i];
staticsMap[staticsList[i].WayId] = wayStatics;
Debug.Info($"玩家人数:{wayStatics.PlayerStaticsList.Count}");
for (int j = 0; j < wayStatics.PlayerStaticsList.Count; j++)
{
wayStatics.PlayerStaticsMap[wayStatics.PlayerStaticsList[j].UserId] =
wayStatics.PlayerStaticsList[j];
}
for (int j = 0; j < wayStatics.SeatStaticsList.Count; j++)
{
wayStatics.SeatStaticsMap[wayStatics.SeatStaticsList[j].Seat] = wayStatics.SeatStaticsList[j];
}
}
}
}
public void Dispose()
{
List<WayStatics> wayStaticsList = new List<WayStatics>(WayStaticsMap.Values);
SaveData(DateTime.Now, wayStaticsList);
_timer?.Dispose();
_timer = null;
}
private void OnDayChange(object data)
{
lock (lockObj)
{
LastDayWayStaticsList = WayStaticsMap;
}
WayStaticsMap = new Dictionary<string, WayStatics>();
//保存昨天的数据
SaveLastDayDataAsync();
}
/// <summary>
/// 保存昨天的统计数据
/// </summary>
private void SaveLastDayDataAsync()
{
Task.Run(() =>
{
lock (lockObj)
{
List<WayStatics> wayStaticsList = new List<WayStatics>(LastDayWayStaticsList.Values);
SaveData(DateTime.Now.AddDays(-1), wayStaticsList);
}
});
}
private void SaveData(DateTime dateTime, List<WayStatics> staticsList)
{
if (staticsList.Count <= 0)
{
return;
}
try
{
//Debug.Info($"保存的长度:{staticsList.Count}");
for (int i = 0; i < staticsList.Count; i++)
{
WayStatics wayStatics = staticsList[i];
wayStatics.PlayerStaticsList = new List<PlayerStatics>(wayStatics.PlayerStaticsMap.Values);
wayStatics.SeatStaticsList = new List<SeatStatics>(wayStatics.SeatStaticsMap.Values);
//Debug.Info($"转换数据:{wayStatics.PlayerStaticsMap.Count} {wayStatics.SeatStaticsMap.Count}");
}
string jsonData = JsonConvert.SerializeObject(staticsList);
string fileName = GetFileName(dateTime);
File.WriteAllText(fileName, jsonData);
}
catch (Exception e)
{
Debug.Error($"保存失败:{e.Message}");
}
}
private WayStatics GetWayStatics(string wayId)
{
if (!WayStaticsMap.TryGetValue(wayId, out WayStatics statics))
{
statics = new WayStatics()
{
WayId = wayId
};
WayStaticsMap[wayId] = statics;
}
return statics;
}
private string dirPath = "statistics";
private string GetFileName(DateTime dateTime)
{
if (!Directory.Exists(dirPath))
{
Directory.CreateDirectory(dirPath);
}
string fileName = $"{dirPath}/{dateTime.ToString("yyyyMMdd")}";
return fileName;
}
/// <summary>
/// 一次战斗
/// </summary>
/// <param name="wayId"></param>
/// <param name="seat"></param>
/// <param name="userid"></param>
/// <param name="score"></param>
public void AddOnceFight(string wayId, int seat, int userid, int score)
{
WayStatics wayStatics = GetWayStatics(wayId);
if (!wayStatics.SeatStaticsMap.TryGetValue(seat, out SeatStatics seatStatics))
{
seatStatics = new SeatStatics()
{
Seat = seat
};
wayStatics.SeatStaticsMap[seat] = seatStatics;
}
seatStatics.TotalCnt++;
seatStatics.Score += score;
if (score >= 0)
{
seatStatics.WinCnt++;
}
if (!wayStatics.PlayerStaticsMap.TryGetValue(userid, out PlayerStatics playerStatics))
{
playerStatics = new PlayerStatics()
{
UserId = userid,
};
wayStatics.PlayerStaticsMap[userid] = playerStatics;
}
playerStatics.TotalCnt++;
playerStatics.TotalScore += score;
if (score >= 0)
{
playerStatics.WinCnt++;
}
//Debug.Info($"保存一局数据!{wayStatics.PlayerStaticsMap.Count} {Thread.CurrentThread.ManagedThreadId}");
}
/// <summary>
/// 一场战斗
/// </summary>
public void AddSessionFight(string wayId, float beilv, int userid, int score)
{
WayStatics wayStatics = GetWayStatics(wayId);
if (wayStatics.BeiLv <= 0.0001f)
{
wayStatics.BeiLv = beilv;
}
if (!wayStatics.PlayerStaticsMap.TryGetValue(userid, out PlayerStatics playerStatics))
{
playerStatics = new PlayerStatics()
{
UserId = userid
};
}
playerStatics.TotalSessionCnt++;
playerStatics.TotalSessionScore += score;
if (score >= 0)
playerStatics.WinSessionCnt++;
//Debug.Info($"保存一大局数据: {wayStatics.PlayerStaticsMap.Count} {Thread.CurrentThread.ManagedThreadId}");
}
/// <summary>
/// 玩法统计
/// </summary>
public class WayStatics
{
/// <summary>
/// 玩法Id
/// </summary>
public string WayId;
/// <summary>
/// 倍率
/// </summary>
public float BeiLv;
/// <summary>
/// 位置统计
/// </summary>
public List<SeatStatics> SeatStaticsList = null;
/// <summary>
/// 玩家统计
/// </summary>
public List<PlayerStatics> PlayerStaticsList = null;
/// <summary>
/// 座位统计
/// </summary>
[JsonIgnore] public Dictionary<int, SeatStatics> SeatStaticsMap = new Dictionary<int, SeatStatics>();
/// <summary>
/// 玩家统计
/// </summary>
[JsonIgnore] public Dictionary<int, PlayerStatics> PlayerStaticsMap = new Dictionary<int, PlayerStatics>();
}
/// <summary>
/// 座位统计
/// </summary>
public class SeatStatics
{
/// <summary>
/// 座位
/// </summary>
public int Seat;
/// <summary>
/// 赢的次数
/// </summary>
public int WinCnt;
/// <summary>
/// 总的次数
/// </summary>
public int TotalCnt;
/// <summary>
/// 总得分
/// </summary>
public int Score;
}
/// <summary>
/// 玩家统计
/// </summary>
public class PlayerStatics
{
/// <summary>
/// 玩家Id
/// </summary>
public int UserId;
/// <summary>
/// 总得分
/// </summary>
public double TotalScore;
/// <summary>
/// 赢的数量
/// </summary>
public int WinCnt;
/// <summary>
/// 总数量
/// </summary>
public int TotalCnt;
/// <summary>
/// 赢的场次
/// </summary>
public int WinSessionCnt;
/// <summary>
/// 总的场次
/// </summary>
public int TotalSessionCnt;
public int TotalSessionScore;
public int LuckCnt;
public int BadLuckCnt;
}
}
}