public void Tick() { if (!IsReadyForNextFrame) { throw new InvalidOperationException(); } Connection.Send(NetFrameNumber + FramesAhead, localOrders.Select(o => o.Serialize()).ToList()); localOrders.Clear(); var sync = new List <int>(); sync.Add(world.SyncHash()); foreach (var order in frameData.OrdersForFrame(world, NetFrameNumber)) { UnitOrders.ProcessOrder(this, world, order.Client, order.Order); sync.Add(world.SyncHash()); } var ss = sync.SerializeSync(); Connection.SendSync(NetFrameNumber, ss); syncReport.UpdateSyncReport(); ++NetFrameNumber; }
public void TickImmediate() { if (localImmediateOrders.Count != 0 && GameSaveLastFrame < NetFrameNumber + FramesAhead) { Connection.SendImmediate(localImmediateOrders.Select(o => o.Serialize())); } localImmediateOrders.Clear(); Connection.Receive( (clientId, packet) => { // HACK: The shellmap relies on ticking a disposed OM if (disposed && World.Type != WorldType.Shellmap) { return; } var frame = BitConverter.ToInt32(packet, 0); if (packet.Length == 5 && packet[4] == (byte)OrderType.Disconnect) { pendingPackets.Remove(clientId); } else if (packet.Length > 4 && packet[4] == (byte)OrderType.SyncHash) { if (packet.Length != 4 + Order.SyncHashOrderLength) { Log.Write("debug", $"Dropped sync order with length {packet.Length}. Expected length {4 + Order.SyncHashOrderLength}."); return; } CheckSync(packet); } else if (frame == 0) { foreach (var o in packet.ToOrderList(World)) { UnitOrders.ProcessOrder(this, World, clientId, o); // A mod switch or other event has pulled the ground from beneath us if (disposed) { return; } } } else { if (pendingPackets.TryGetValue(clientId, out var queue)) { queue.Enqueue(packet); } else { Log.Write("debug", $"Received packet from disconnected client '{clientId}'"); } } }); }
void ProcessOrders() { var clientOrders = new List <ClientOrder>(); foreach (var(clientId, clientPackets) in pendingPackets) { // The IsReadyForNextFrame check above guarantees that all clients have sent a packet var frameData = clientPackets.Dequeue(); // Orders are synchronised by sending an initial FramesAhead set of empty packets // and then making sure that we enqueue and process exactly one packet for each player each tick. // This may change in the future, so sanity check that the orders are for the frame we expect // and crash early instead of risking desyncs. var frameNumber = BitConverter.ToInt32(frameData, 0); if (frameNumber != NetFrameNumber) { throw new InvalidDataException($"Attempted to process orders from client {clientId} for frame {frameNumber} on frame {NetFrameNumber}"); } foreach (var order in frameData.ToOrderList(World)) { UnitOrders.ProcessOrder(this, World, clientId, order); clientOrders.Add(new ClientOrder { Client = clientId, Order = order }); } } if (NetFrameNumber + FramesAhead >= GameSaveLastSyncFrame) { var defeatState = 0UL; for (var i = 0; i < World.Players.Length; i++) { if (World.Players[i].WinState == WinState.Lost) { defeatState |= 1UL << i; } } Connection.SendSync(NetFrameNumber, OrderIO.SerializeSync(World.SyncHash(), defeatState)); } else { Connection.SendSync(NetFrameNumber, OrderIO.SerializeSync(0, 0)); } if (generateSyncReport) { using (new PerfSample("sync_report")) syncReport.UpdateSyncReport(clientOrders); } ++NetFrameNumber; }
public void TickImmediate() { if (localImmediateOrders.Count != 0 && GameSaveLastFrame < NetFrameNumber + FramesAhead) { Connection.SendImmediate(localImmediateOrders.Select(o => o.Serialize())); } localImmediateOrders.Clear(); var immediatePackets = new List <(int ClientId, byte[] Packet)>(); Connection.Receive( (clientId, packet) => { var frame = BitConverter.ToInt32(packet, 0); if (packet.Length == 5 && packet[4] == (byte)OrderType.Disconnect) { frameData.ClientQuit(clientId, frame); } else if (packet.Length > 4 && packet[4] == (byte)OrderType.SyncHash) { if (packet.Length != 4 + Order.SyncHashOrderLength) { Log.Write("debug", "Dropped sync order with length {0}. Expected length {1}.".F(packet.Length, 4 + Order.SyncHashOrderLength)); return; } CheckSync(packet); } else if (frame == 0) { immediatePackets.Add((clientId, packet)); } else { frameData.AddFrameOrders(clientId, frame, packet); } }); foreach (var p in immediatePackets) { foreach (var o in p.Packet.ToOrderList(World)) { UnitOrders.ProcessOrder(this, World, p.ClientId, o); // A mod switch or other event has pulled the ground from beneath us if (disposed) { return; } } } }
void ReceiveAllOrdersAndCheckSync() { Connection.Receive( (clientId, packet) => { // HACK: The shellmap relies on ticking a disposed OM if (disposed && World.Type != WorldType.Shellmap) { return; } var frame = BitConverter.ToInt32(packet, 0); if (packet.Length == Order.DisconnectOrderLength + 4 && packet[4] == (byte)OrderType.Disconnect) { pendingPackets.Remove(BitConverter.ToInt32(packet, 5)); } else if (packet.Length > 4 && packet[4] == (byte)OrderType.SyncHash) { if (packet.Length != 4 + Order.SyncHashOrderLength) { Log.Write("debug", $"Dropped sync order with length {packet.Length}. Expected length {4 + Order.SyncHashOrderLength}."); return; } CheckSync(packet); } else if (frame == 0) { foreach (var o in packet.ToOrderList(World)) { UnitOrders.ProcessOrder(this, World, clientId, o); // A mod switch or other event has pulled the ground from beneath us if (disposed) { return; } } } else { if (pendingPackets.TryGetValue(clientId, out var queue)) { queue.Enqueue(packet); } else { Log.Write("debug", $"Received packet from disconnected client '{clientId}'"); } } }); }
public void TickImmediate() { var immediateOrders = localOrders.Where(o => o.IsImmediate).ToList(); if (immediateOrders.Count != 0) { Connection.SendImmediate(immediateOrders.Select(o => o.Serialize()).ToList()); } localOrders.RemoveAll(o => o.IsImmediate); var immediatePackets = new List <Pair <int, byte[]> >(); Connection.Receive( (clientId, packet) => { var frame = BitConverter.ToInt32(packet, 0); if (packet.Length == 5 && packet[4] == 0xBF) { frameData.ClientQuit(clientId, frame); } else if (packet.Length >= 5 && packet[4] == 0x65) { CheckSync(packet); } else if (frame == 0) { immediatePackets.Add(Pair.New(clientId, packet)); } else { frameData.AddFrameOrders(clientId, frame, packet); } }); foreach (var p in immediatePackets) { foreach (var o in p.Second.ToOrderList(World)) { UnitOrders.ProcessOrder(this, World, p.First, o); // A mod switch or other event has pulled the ground from beneath us if (disposed) { return; } } } }
public void TickImmediate() { if (localImmediateOrders.Count != 0 && GameSaveLastFrame < NetFrameNumber + FramesAhead) { Connection.SendImmediate(localImmediateOrders.Select(o => o.Serialize())); } localImmediateOrders.Clear(); var immediatePackets = new List <Pair <int, byte[]> >(); Connection.Receive( (clientId, packet) => { var frame = BitConverter.ToInt32(packet, 0); if (packet.Length == 5 && packet[4] == (byte)OrderType.Disconnect) { frameData.ClientQuit(clientId, frame); } else if (packet.Length >= 5 && packet[4] == (byte)OrderType.SyncHash) { CheckSync(packet); } else if (frame == 0) { immediatePackets.Add(Pair.New(clientId, packet)); } else { frameData.AddFrameOrders(clientId, frame, packet); } }); foreach (var p in immediatePackets) { foreach (var o in p.Second.ToOrderList(World)) { UnitOrders.ProcessOrder(this, World, p.First, o); // A mod switch or other event has pulled the ground from beneath us if (disposed) { return; } } } }
public void Tick() { if (!IsReadyForNextFrame) { throw new InvalidOperationException(); } if (GameSaveLastFrame < NetFrameNumber + FramesAhead) { Connection.Send(NetFrameNumber + FramesAhead, localOrders.Select(o => o.Serialize()).ToList()); } localOrders.Clear(); foreach (var order in frameData.OrdersForFrame(World, NetFrameNumber)) { UnitOrders.ProcessOrder(this, World, order.Client, order.Order); } if (NetFrameNumber + FramesAhead >= GameSaveLastSyncFrame) { var defeatState = 0UL; for (var i = 0; i < World.Players.Length; i++) { if (World.Players[i].WinState == WinState.Lost) { defeatState |= 1UL << i; } } Connection.SendSync(NetFrameNumber, OrderIO.SerializeSync(World.SyncHash(), defeatState)); } else { Connection.SendSync(NetFrameNumber, OrderIO.SerializeSync(0, 0)); } if (generateSyncReport) { using (new PerfSample("sync_report")) syncReport.UpdateSyncReport(); } ++NetFrameNumber; }
void ProcessImmediateOrders() { foreach (var p in receivedImmediateOrders) { foreach (var o in p.Second.ToOrderList(World)) { UnitOrders.ProcessOrder(this, World, p.First, o); // A mod switch or other event has pulled the ground from beneath us if (disposed) { return; } } } receivedImmediateOrders.Clear(); }
/* * Only available if TickImmediate() is called first and we are ready to dispatch received orders locally. * Process all incoming orders for this frame, handle sync hashes and step our net frame. */ void ProcessOrders() { foreach (var order in frameData.OrdersForFrame(World, NetFrameNumber)) { UnitOrders.ProcessOrder(this, World, order.Client, order.Order); } if (NetFrameNumber + FramesAhead >= GameSaveLastSyncFrame) { Connection.SendSync(NetFrameNumber, OrderIO.SerializeSync(World.SyncHash())); } else { Connection.SendSync(NetFrameNumber, OrderIO.SerializeSync(0)); } if (generateSyncReport) { using (new PerfSample("sync_report")) syncReport.UpdateSyncReport(); } ++NetFrameNumber; }
public void Tick() { if (!IsReadyForNextFrame) { throw new InvalidOperationException(); } Connection.Send(NetFrameNumber + FramesAhead, localOrders.Select(o => o.Serialize()).ToList()); localOrders.Clear(); foreach (var order in frameData.OrdersForFrame(World, NetFrameNumber)) { UnitOrders.ProcessOrder(this, World, order.Client, order.Order); } Connection.SendSync(NetFrameNumber, OrderIO.SerializeSync(World.SyncHash())); if (generateSyncReport) { syncReport.UpdateSyncReport(); } ++NetFrameNumber; }