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 Clients { get; private set; } = new ObservableCollection(); private static ConcurrentBag receiveMes = new ConcurrentBag(); private static CustomList RoomList = new CustomList(); 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 Customers { get; private set; } = new ObservableCollection(); 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 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 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 clients, List 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 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 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(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))}#"); } } }