powerfun-new-net/ViewModels/MainWindowViewModel.cs
2026-02-03 11:38:29 +08:00

560 lines
19 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using Prism.Mvvm;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Text;
using OnlineUserPool.Hander;
using OnlineUserPool.Model;
using OnlineUserPool.Unility;
using Serilog;
using System.Linq;
using System.Net;
using System.Windows.Threading;
using System.Diagnostics;
using OnlineUserPool.Services;
using Newtonsoft.Json;
using System.Collections.Concurrent;
using System.Windows;
using OnlineUserPool.Api;
namespace OnlineUserPool.ViewModels
{
public class MainWindowViewModel : BindableBase
{
public ObservableCollection<HostModel> Clients { get; private set; } = new ObservableCollection<HostModel>();
private static ConcurrentBag<MsgModel> receiveMes = new ConcurrentBag<MsgModel>();
private static CustomList<RoomModel> RoomList = new CustomList<RoomModel>();
private static int RoomMaxId = 0;
private static int Ticks = 0;
private static object locker = new object();
public static System.Timers.Timer timer;
private static IHandle mapRecordRankingHander;
private Dispatcher dispatcher;
public ObservableCollection<MsgModel> Customers { get; private set; } = new ObservableCollection<MsgModel>();
private string _Title = "";
public string Title
{
get { return _Title; }
set
{
SetProperty(ref _Title, value);
}
}
private string _SendDataSize = "";
public string SendDataSize
{
get { return _SendDataSize; }
set
{
SetProperty(ref _SendDataSize, value);
}
}
private string _VirtualData = "";
public string VirtualData
{
get { return _VirtualData; }
set
{
SetProperty(ref _VirtualData, value);
}
}
private bool _closeVirtualUser = false;
public bool CloseVirtualUser
{
get { return _closeVirtualUser; }
set
{
SetProperty(ref _closeVirtualUser, value);
}
}
private const int ROOM_TIME_OUT = 60;
private const int TOTAL_PROCESS = 100;
public MainWindowViewModel()
{
//初测程序关闭事件
Application.Current.MainWindow.Closing += MainWindow_Closing;
Title = $"{ IPAddress.Any }:{ ConfigHelp.UdpPort }";
dispatcher = Dispatcher.CurrentDispatcher;
WriteLine(DateTime.Now.ToShortDateString());
LogHelper.Init();
mapRecordRankingHander = new MapRecordRankingHander();//
//mapRecordRankingHander = new MultiUserHandle();
Log.Information($"初始化连接,当前地址:{ IPAddress.Any }:{ConfigHelp.UdpPort}");
new TcpService().RunServer(ReceivedData, ClientDisconnected);
var _udpService = new UdpService();
_udpService.RunServer(ReceivedData);
Log.Information("服务启动成功");
timer = new System.Timers.Timer(500);
timer.Elapsed += Timer_Elapsed;
timer.AutoReset = true;
timer.Start();
Log.Information("等待连接");
ReleaseMemory();
}
private void ReleaseMemory()
{
var timers = new System.Timers.Timer(3333);
timers.Elapsed += (s, e) =>
{
GC.Collect();
};
timers.AutoReset = true;
timers.Start();
}
private void MainWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
}
private void ReceivedData(IPEndPoint remoteIpEndPoint, ReceiveModel msg, IService service)
{
dispatcher.Invoke(() =>
{
if (!Clients.Any(c => c.Equals(remoteIpEndPoint)))
{
if (msg is SetClientCommand client)
{
Clients.Add(new HostModel
{
IPEndPoint = remoteIpEndPoint,
LastActiveTime = DateTime.Now,
Service = service,
MemberId = client.MemberId,
Encoding = client.Encoding,
V = client.V
});
}
}
switch (msg.CommandType)
{
//ping命令
case 0:
{
var client = Clients.FirstOrDefault(n => n.Equals(remoteIpEndPoint));
if(client == null) return;
client.LastActiveTime = DateTime.Now;
if (msg.V > 0)
{
client.V = msg.V;
}
}
break;
//消息
case 1:
{
var client = Clients.FirstOrDefault(n => n.Equals(remoteIpEndPoint));
if(client == null) return;
var msg1 = (msg as MsgModel);
client.Competitionid = msg1.CompetitionId;
client.Model = msg1.Model;
if (client.Model == "")
{
client.RoomId = msg1.RoomId;
}
if (msg1.V > 0)
{
client.V = msg1.V;
}
receiveMes.Add(msg1);
HandleGameRoomSaved(msg1);
}
break;
//设置客户端
case 2:
{
var client = Clients.FirstOrDefault(n => n.Equals(remoteIpEndPoint));
var msg1 = (msg as SetClientCommand);
client.Encoding = msg1.Encoding;
client.Client = msg1.Client;
client.V = msg1.V;
client.MemberId = msg1.MemberId;
client.Competitionid = msg1.Competitionid;
client.IsWatch = msg1.IsWatch;
}
break;
}
});
}
private void Timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
Ticks++;
NotifyClient();
}
private void HandleGameRoomSaved(MsgModel msg1)
{
//对战房间内的人保存处理
if (msg1.RoomId > 0 && msg1.Saved)
{
var current = RoomList.ToList().FirstOrDefault(c => c.RoomId == msg1.RoomId);
if (current != null)
{
//更新房间内的人的保存状态
var currentPlayer = current.List.FirstOrDefault(c => c.UserId == msg1.MemberId);
if (currentPlayer != null)
{
currentPlayer.Saved = true;
}
var notAllSaved = current.List.Any(c => !c.Saved);
if (!notAllSaved && current.Status != 2)
{
current.Status = 2;
WebService.UpdateGameRoom(current.RoomId, current.Status);
}
if (!current.Saved)
{
notAllSaved = current.List.Any(c => !c.Saved);
if (!notAllSaved && current.Status != 2)
{
current.Status = 2;
}
current.Saved = true;
WebService.AddGameRoom(current.RoomId, current.Status, current.UserId, current.Name, current.MapRouteId, current.Password, current.StartTime.Value, current.CloseTime, current.MaxMembers);
}
}
}
}
private void NotifyClient()
{
try
{
if (!receiveMes.Any())
{
dispatcher.Invoke(() =>
{
//移除5钟内连接不上的客户端
Clients.ToList().ForEach(item =>
{
if (item.Expire)
{
RemoveClient(item);
}
});
});
return;
}
lock (locker)
{
dispatcher.Invoke(() =>
{
var msgs = receiveMes.ToList();
//加入虚拟人物消息
if (!CloseVirtualUser)
{
var virtualData = mapRecordRankingHander.GetVirtualUserData();
VirtualData = $"{virtualData.Count}";
msgs.AddRange(virtualData);
}
//屏蔽房间模式的用户
var c = Clients.Where(c => string.IsNullOrEmpty(c.Model)).ToList();
SendMessage(c, msgs);
RemoveClientAdvanced(msgs);
AddCustomers(msgs);
receiveMes.Clear();
msgs.Clear();
msgs = null;
});
//更新虚拟人物信息
mapRecordRankingHander.RemoveEndAndAddNewVirtualUser(Clients.Count);
}
}
catch (Exception e)
{
Log.Error($"NotifyClient:{ e.Message }\r\n{ e.StackTrace }");
}
}
private void RemoveClientAdvanced(List<MsgModel> msgs)
{
//移除下线的客户端
for (int i = 0; i < msgs.Count; i++)
{
if (msgs[i].Exit && msgs[i].MemberId > 0)//客户端退出,并且不是虚拟的人物
{
//这个地方有严重的逻辑错误(虚拟的人物不能和真实的人用同一个名字)
var info = Clients.FirstOrDefault(n => n.MemberId == msgs[i].MemberId);
if (info != null)
{
RemoveClient(info);
}
}
}
//移除5钟内连接不上的客户端
Clients.ToList().ForEach(item =>
{
if (item.Expire)
{
RemoveClient(item);
}
});
}
private void AddCustomers(List<MsgModel> msgs)
{
foreach (var item in msgs)
{
var client = Clients.FirstOrDefault(c => c.MemberId == item.MemberId);
if (client == null)
{
Customers.Remove(item);
continue;
}
var customer = Customers.FirstOrDefault(c => c.MemberId == item.MemberId);
if (customer != null)
{
customer.Update(item);
}
else
{
Customers.Add(item);
}
}
}
private StringBuilder sb = new StringBuilder();
private void SendMessage(IList<HostModel> clients, List<MsgModel> list)
{
if (!clients.Any() || !list.Any())
{
return;
}
var clients1 = clients.ToList();
foreach (var item in list)
{
item.PreDistance = Math.Round(item.PreDistance, 5);
item.EndDistance = Math.Round(item.EndDistance, 5);
item.WeightKg = Math.Round(item.WeightKg, 2);
}
#region
foreach (var item in clients1)
{
try
{
if (item == null) continue;
if (item.V != 1 && item.V != 2)
{
var jsonString = Newtonsoft.Json.JsonConvert.SerializeObject(list.Select(m => new {
m.RouteId,
m.MemberId,
m.Point,
m.IsCompleted,
exit = m.Exit,
m.Speed,
m.PreDistance,
m.EndDistance,
//m.IsVirtual,//后面要把这个字段过滤掉
m.WeightKg,
Competitionid = m.CompetitionId,
m.Saved,
m.Frame,
}));
var data = Encoding.ASCII.GetBytes(jsonString);
item.Service.Send(data, data.Length, item.IPEndPoint);
data = null;
continue;
}
if(item.Service is TcpService)
{
NetworkData networkData = null;
if (item.V == 2)
{
networkData = HandleGzipNetworkData(list,item);
}
bool isZip = item.Encoding == "gzip";
item.Service.Send(networkData.GetBytes(isZip), networkData.GetBytes(isZip).Length, item.IPEndPoint);
networkData = null;
}
}
catch (Exception e)
{
Debug.WriteLine(e.Message);
Log.Error($"{ item.IPEndPoint.ToString() }:{ e.Message }\r\n{ e.StackTrace }");
}
}
clients1 = null;
#endregion
}
private NetworkData HandleGzipNetworkData(List<MsgModel> list, HostModel item)
{
sb.Clear();
sb.Append("l{");
var runtimeList = list.Where(m => m.CompetitionId == item.Competitionid).Select(m => m.ToString()).ToList();
foreach (var obj in runtimeList)
{
sb.Append(obj);
sb.Append("|");
}
if (runtimeList.Any())
{
sb.Remove(sb.Length - 1, 1);
}
sb.Append("};w{");
var watchList = Clients.Where(c => c.IsWatch && c.Competitionid == item.Competitionid).Select(c => c.MemberId).ToList();
foreach (var obj in watchList)
{
sb.Append(obj);
sb.Append("|");
}
if (watchList.Any())
{
sb.Remove(sb.Length - 1, 1);
}
sb.Append("};e{");
var errorList = list.Where(c => c.RoomId > 0 || c.Frame > 0).ToList();
foreach (var c in errorList)
{
sb.Append($"{c.MemberId},{c.RoomId},{c.Frame},{c.TotalTicks}");
sb.Append("|");
}
if (errorList.Any())
{
sb.Remove(sb.Length - 1, 1);
}
sb.Append("};");
var strV2 = sb.ToString();
var data2 = new NetworkData(strV2);
strV2 = null;
return data2;
}
void WriteLine(string str)
{
//Debug.WriteLine(str);
}
T CloneJson<T>(T source)
{
// Don't serialize a null object, simply return the default for that object
if (Object.ReferenceEquals(source, null))
{
return default(T);
}
// initialize inner objects individually
// for example in default constructor some list property initialized with some values,
// but in 'source' these items are cleaned -
// without ObjectCreationHandling.Replace default constructor values will be added to result
var deserializeSettings = new JsonSerializerSettings { ObjectCreationHandling = ObjectCreationHandling.Replace };
return JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(source), deserializeSettings);
}
private void ClientDisconnected(EndPoint point)
{
dispatcher.Invoke(() =>
{
try
{
var client = Clients.FirstOrDefault(f => f.IPEndPoint.ToString() == point.ToString());
if (client != null)
{
RemoveClient(client);
}
}
catch(Exception ex)
{
Log.Information("在ClientDisconnected触发以后删除client时报错了,"+ ex.Message +", " + ex.StackTrace);
}
});
}
private void RemoveClient(HostModel client)
{
BeforeRemoved(client);
Clients.Remove(client);
Customers.Clear();
}
private void BeforeRemoved(HostModel client)
{
if(client.RoomId == 0)return;
var room = RoomList.ToList().FirstOrDefault(c => c.RoomId == client.RoomId);
if(room == null)return;
var needRemove = room.List.FirstOrDefault(c => c.UserId == client.MemberId && c.Saved == false);
if(needRemove == null)return;
//从房间中移除,并考虑是否需要删除房间和移交房主
room.List.Remove(needRemove);
if (room.List.Count == 0)
{
RoomList.Remove(room);
}
else
{
var host = room.List.FirstOrDefault();
if (host != null)
{
host.IsOwner = true;
}
}
//更新房间状态
var isComplete = room.List.All(c => c.Saved != false);
if (room.Status > 0)
{
room.Status = isComplete ? 2 : 1;
}
}
}
public class NetworkData
{
private string _txt = "";
public NetworkData(string txt)
{
this._txt = txt;
}
public byte[] GetBytes(bool compress = false)
{
if (compress)
{
return GetCompressBytes();
}
return Encoding.UTF8.GetBytes(this._txt);
}
public void Dispose()
{
//this._txt = null;
}
private byte[] GetCompressBytes()
{
return Encoding.UTF8.GetBytes($"*{Convert.ToBase64String(CommonHelper.Compress(this._txt))}#");
}
}
}