/// <summary> /// walks through the message queue and packs the messages together and sends them off. /// later we can add priority filtering to the messages we add to the message queue, as it's just sorting the queued messages in some way. /// any messages that don't fit in this packet we leave until next time around (and increase their priority) /// maybe we should keep two message queues, one for entity updates or something /// </summary> public void NetworkSend() { //Debug.Log("NetworkSend"); UnityEngine.Profiling.Profiler.BeginSample("Network Send"); foreach (SteamConnection sc in connections.Values) { if (sc.messageQueue.Count > 0) { //pack writeStream.Reset(packetSize); //grab the first message //check if it can fit in the stream //if it can, remove it from the queue and write it //continue until writeStream can't fit any other messages //or until the message queue is empty //we should loop through all the messages and calculate their priority. //store this message list in a new list, and sort it by priority (or maybe just sort the queue, since we don't really need the original order? for (int i = 0; i < sc.messageQueue.Count; i++) { NetworkMessage m = sc.messageQueue[i]; if (MessagePriorityCalculators[m.msgCode] != null) { if (m.isEntityMsg) { if (m.entity == null) { m.priority = 0; //can't send this because our entity is GONE } else { m.priority = m.entity.Priority(sc.steamID); } } else { //normal message m.priority = MessagePriorityCalculators[m.msgCode](sc.steamID, m.args); } } else { m.priority = 1f; //default priority for normal messages. These will send *eventually* whenever there is space } } //sort it by priority, with higher priority values first, lower last. sc.messageQueue = sc.messageQueue.OrderByDescending(p => p.priority + p.skipped).ToList(); for (int i = 0; i < sc.messageQueue.Count; i++) { //Debug.Log("queue count: " + sc.messageQueue.Count); //Debug.Log("message: " + i); NetworkMessage m = sc.messageQueue[i]; if (!writeStream.CanWrite()) { m.skipped++; //Debug.Log("!writeStream.CanWrite()"); continue; //continue because we need to mark the rest of the messages as skipped. //break; //ZERO room left m } else { if (m.priority == 0f) //if the message has a priority of 0 we dont want to send the message { sc.messageQueue.RemoveAt(i); //this will happen when we queue something to everyone, but i--; //some players are too far away to care about the message (in a different map, etc) continue; //so it just gets discarded. //what happens if we discard a spawn request, and then move into the area //the object should be in, and start receiving state updates from that spawn??? //maybe NEVER remove a spawn request (even with priority 0?) //add it to a list and just buffer the message until we get into the area? //that could work... } //get the total message size to check if it will fit int msgSize = 8; //8 bits for the msgCode included before the data if (MessagePeekers[m.msgCode] != null) //if it's null we don't have any data to send, just the msgCode (like for a keep alive) { msgSize += Core.net.MessagePeekers[m.msgCode](m.args); if (IsEntityMsg(m.msgCode)) { msgSize += m.entity.Peek(); //entity state msgSize //Debug.Log("msgSize: ["+m.msgCode+"]: " + msgSize); } } //Debug.Log("CanWrite: " + msgSize + " : " + writeStream.CanWrite(msgSize)); if (writeStream.CanWrite(msgSize)) //will it fit? { SerializerUtils.WriteInt(writeStream, m.msgCode, 0, 255); //write the msgCode if (MessageSerializers[m.msgCode] != null) { Core.net.MessageSerializers[m.msgCode](sc.steamID, writeStream, m.args); //write the rest of the data if (IsEntityMsg(m.msgCode)) { m.entity.Serialize(writeStream); } } //Debug.Log("Wrote: " + msgSize + " : new bit position: " + writeStream.Position); //remove this message from the list. sc.messageQueue.RemoveAt(i); i--; } else { m.skipped++; //Debug.Log("trying next message, can't fit.."); continue; //try the next message I guess? until the packet is full or we run out } } } //try and add a "StreamEmpty" message at the end if it fits //"there are no more messages" message. Stop trying to read any data after. //this isn't actually necessary, because since our Empty code is 00000000, and junk data after is 00000000 //it picks up the empty automagically. Since when we grab the message code, if it's junk data it's the empty msgCode! //if(writeStream.CanWrite() && writeStream.CanWrite(8)) { // SerializerUtils.WriteInt(writeStream, GetMessageCode("Empty"), 0, 255); //} SendP2PData(sc.steamID, writeStream.Data, writeStream.Position, Networking.SendType.Reliable, 0); //send it! } } UnityEngine.Profiling.Profiler.EndSample(); }
/// <summary> /// walks through the message queue and packs the messages together and sends them off. /// later we can add priority filtering to the messages we add to the message queue, as it's just sorting the queued messages in some way. /// any messages that don't fit in this packet we leave until next time around (and increase their priority) /// maybe we should keep two message queues, one for entity updates or something /// </summary> public void NetworkSend() { //Debug.Log("NetworkSend"); UnityEngine.Profiling.Profiler.BeginSample("Network Send"); if (NetworkSendEvent != null) { NetworkSendEvent(); } foreach (SteamConnection sc in connections.Values) { //walk the entities and say "hey, we're sending this frame" so we can update the state? //or, let entities subscribe to an event? Wonder how much faster it would be vs walking the entire entity list. //if we had 4096 entites (max) things would break down anyways. Probably don't want to just //add state update messages willy-nilly. Should check priority or somethings? if (sc.messageQueue.Count > 0) { //pack writeStream.Reset(packetSize); //grab the first message //check if it can fit in the stream //if it can, remove it from the queue and write it //continue until writeStream can't fit any other messages //or until the message queue is empty //we should loop through all the messages and calculate their priority. //store this message list in a new list, and sort it by priority (or maybe just sort the queue, since we don't really need the original order? for (int i = 0; i < sc.messageQueue.Count; i++) { NetworkMessage m = sc.messageQueue[i]; if (MessagePriorityCalculators[m.msgCode] != null) { if (m.isEntityMsg) { if (m.entity == null) { m.priority = 0; //can't send this because our entity is GONE } else { //this checks the priority to whoever we are sending to //via distance check around their player //what if the entity we want to send is too far away from US for us to care about the entity //anymore and we want to destroy it locally? //do we do a separate check somewhere for that too? m.priority = m.entity.PriorityCaller(sc.steamID, true); } } else { //normal message m.priority = MessagePriorityCalculators[m.msgCode](sc.steamID, m.args); } } else { m.priority = 1f; //default priority for normal messages. These will send *eventually* whenever there is space } } //sort it by priority, with higher priority values first, lower last. sc.messageQueue = sc.messageQueue.OrderByDescending(p => p.priority + p.skipped).ToList(); for (int i = 0; i < sc.messageQueue.Count; i++) { //Debug.Log("queue count: " + sc.messageQueue.Count); //Debug.Log("message: " + i); NetworkMessage m = sc.messageQueue[i]; if (!writeStream.CanWrite()) { m.skipped++; //Debug.Log("!writeStream.CanWrite()"); continue; //continue because we need to mark the rest of the messages as skipped. //break; //ZERO room left m } else { if (m.priority == 0f) { if (m.isEntityMsg) { m.entity.queuedMessage[sc.steamID] = null; } //if the message has a priority of 0 we dont want to send the message sc.messageQueue.RemoveAt(i); //this will happen when we queue something to everyone, but i--; //some players are too far away to care about the message (in a different map, etc) continue; //so it just gets discarded. //what happens if we discard a spawn request, and then move into the area //the object should be in, and start receiving state updates from that spawn??? //maybe NEVER remove a spawn request (even with priority 0?) //add it to a list and just buffer the message until we get into the area? //that could work... } //get the total message size to check if it will fit int msgSize = SerializerUtils.RequiredBitsInt(0, maxMessageTypes); //8 bits for the msgCode included before the data if (MessagePeekers[m.msgCode] != null) //if it's null we don't have any data to send, just the msgCode (like for a keep alive) { msgSize += Core.net.MessagePeekers[m.msgCode](m.args); if (m.msgCode == GetMessageCode("EntityUpdate")) { int entityDataSize = m.entity.Peek(); msgSize += entityDataSize; //entity state msgSize //Debug.Log("msgSize: ["+m.msgCode+"]: " + msgSize); //if size is zero, we should just NOT send the message if (entityDataSize == 0) { //just don't send it, no point wasting 8 bits with an empty message code. //Debug.Log("removed"); m.entity.queuedMessage[sc.steamID] = null; sc.messageQueue.RemoveAt(i); i--; continue; } } } //Debug.Log("CanWrite: " + msgSize + " : " + writeStream.CanWrite(msgSize)); if (writeStream.CanWrite(msgSize)) //will it fit? //Debug.Log("write message"); { SerializerUtils.WriteInt(writeStream, m.msgCode, 0, maxMessageTypes); //write the msgCode if (MessageSerializers[m.msgCode] != null) { Core.net.MessageSerializers[m.msgCode](sc.steamID, writeStream, m.args); //write the rest of the data if (m.msgCode == GetMessageCode("EntityUpdate")) { m.entity.queuedMessage[sc.steamID] = null; //clear this so they can queue the next message m.entity.Serialize(writeStream); } else if (m.msgCode == GetMessageCode("EntityDestroy")) { m.entity.queuedMessage[sc.steamID] = null; m.entity.TryDestroyInternal(); //we can only destroy the entity after the message has been sent. //If we try to destroy it earlier, the entity is already null when trying to send and things fail } } //Debug.Log("Wrote: " + msgSize + " : new bit position: " + writeStream.Position); //remove this message from the list. sc.messageQueue.RemoveAt(i); i--; continue; } else { m.skipped++; //Debug.Log("trying next message, can't fit.."); continue; //try the next message I guess? until the packet is full or we run out } } } //try and add a "StreamEmpty" message at the end if it fits //"there are no more messages" message. Stop trying to read any data after. //this isn't actually necessary, because since our Empty code is 00000000, and junk data after is 00000000 //it picks up the empty automagically. Since when we grab the message code, if it's junk data it's the empty msgCode! //if(writeStream.CanWrite() && writeStream.CanWrite(8)) { // SerializerUtils.WriteInt(writeStream, GetMessageCode("Empty"), 0, 255); //} SendP2PData(sc.steamID, writeStream.Data, writeStream.Position, Networking.SendType.Reliable, 0); //send it! } } UnityEngine.Profiling.Profiler.EndSample(); }