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
576 lines
20 KiB
C#
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;
|
|
}
|
|
}
|
|
} |