public static int operator -(Tick a, Tick b) { RailDebug.Assert(a.IsValid && b.IsValid); long difference = a.tickValue - (long)b.tickValue; return((int)difference); }
public static int operator +(Tick a, Tick b) { RailDebug.Assert(a.IsValid && b.IsValid); long sum = a.tickValue + (long)b.tickValue; return((int)sum); }
/// <summary> /// Remove an entity from being controlled by this peer. /// </summary> public void RevokeControlInternal(RailEntityBase entity) { RailDebug.Assert(entity.Controller == this); controlledEntities.Remove(entity); entity.AssignController(null); }
/// <summary> /// Returns true iff we stored the delta. /// </summary> public bool ProcessDelta(RailStateDelta delta) { if (knownEntities.TryGetValue(delta.EntityId, out RailEntityClient entity) == false) { RailDebug.Assert(delta.IsFrozen == false, "Frozen unknown entity"); if (delta.IsFrozen || delta.IsRemoving) { return(false); } entity = delta.ProduceEntity(Resource) as RailEntityClient; if (entity == null) { throw new TypeAccessException( "Got unexpected instance from RailResource. Internal error in type RailRegistry and/or RailResource."); } entity.AssignId(delta.EntityId); entity.PrimeState(delta); pendingEntities.Add(entity.Id, entity); knownEntities.Add(entity.Id, entity); } // If we're already removing the entity, we don't care about other deltas bool stored = false; if (entity.IsRemoving == false) { stored = entity.ReceiveDelta(delta); } return(stored); }
public static bool operator !=(SequenceId a, SequenceId b) { RailDebug.Assert(a.IsValid); RailDebug.Assert(b.IsValid); return(a.rawValue != b.rawValue); }
public void Deallocate(T instance) { RailDebug.Assert(instance.Pool == this); instance.Reset(); instance.Pool = null; // Prevent multiple frees freeList.Push(instance); }
private static int GetDifference(SequenceId a, SequenceId b) { RailDebug.Assert(a.IsValid); RailDebug.Assert(b.IsValid); int difference = (int)((a.rawValue << BIT_SHIFT) - (b.rawValue << BIT_SHIFT)); return(difference); }
public void SendPacket(RailResource resource, IRailNetPeer peer, RailPacketOutgoing packet) { bitBuffer.Clear(); packet.Encode(resource, bitBuffer); int length = bitBuffer.Store(bytes); RailDebug.Assert(length <= RailConfig.PACKCAP_MESSAGE_TOTAL); peer.SendPayload(new ArraySegment <byte>(bytes, 0, length)); }
/// <summary> /// Adds an entity to be controlled by this peer. /// </summary> public void GrantControlInternal(RailEntityBase entity) { if (entity.Controller == this) { return; } RailDebug.Assert(entity.Controller == null); controlledEntities.Add(entity); entity.AssignController(this); }
/// <summary> /// Queues an event to broadcast to the server with a number of retries. /// </summary> public void RaiseEvent <T>(Action <T> initializer, ushort attempts = 3) where T : RailEvent { RailDebug.Assert(client.ServerPeer != null); if (client.ServerPeer != null) { T evnt = eventCreator.CreateEvent <T>(); initializer(evnt); client.ServerPeer.SendEvent(evnt, attempts, false); } }
/// <summary> /// Packs all elements of an enumerable up to a given size. /// Max MAX_LIST_COUNT elements, by default 255. /// </summary> public byte PackToSize <T>( int maxTotalBytes, int maxIndividualBytes, IEnumerable <T> elements, Action <T, RailBitBuffer> encode, Action <T> packed = null) { const int SIZE_BITS = 8; maxTotalBytes -= 1; // Sentinel bit can blow this up byte count = 0; // Reserve: [Count] int countWritePos = writePos; Write(SIZE_BITS, 0); // Write: [Elements] foreach (T val in elements) { if (count == MAX_LIST_COUNT) { break; } int rollback = writePos; int startByteSize = ByteSize; encode.Invoke(val, this); int endByteSize = ByteSize; int writeByteSize = endByteSize - startByteSize; if (writeByteSize > maxIndividualBytes) { writePos = rollback; RailDebug.LogWarning("Skipping " + val + " (" + writeByteSize + "B)"); } else if (endByteSize > maxTotalBytes) { writePos = rollback; break; } else { packed?.Invoke(val); count++; } } // Deferred Write: [Count] Insert(countWritePos, SIZE_BITS, count); return(count); }
private static void EncodeHeader(RailPacketOutgoing packet, RailBitBuffer buffer) { RailDebug.Assert(packet.SenderTick.IsValid); // Write: [LocalTick] buffer.WriteTick(packet.SenderTick); // Write: [LastAckTick] buffer.WriteTick(packet.LastAckTick); // Write: [AckReliableEventId] buffer.WriteSequenceId(packet.LastAckEventId); }
/// <summary> /// Selects outgoing events to send. /// </summary> private IEnumerable <RailEvent> FilterOutgoingEvents() { // The receiving client can only store a limited size sequence history // of events in its received buffer, and will skip any events older than // its latest received minus that history length, including reliable // events. In order to make sure we don't force the client to skip an // event with attempts remaining, we will throttle the outgoing events // if we've been sending them too fast. For example, if we have a live // event with ID 3 pending, and a maximum history length of 64 (see // RailConfig.HISTORY_CHUNKS) then the highest ID we can send would be // ID 67. Were we to send an event with ID 68, then the client may ignore // ID 3 when it comes in for being too old, even though it's still live. // // In practice this shouldn't be a problem unless we're sending way // more events than is reasonable(/possible) in a single packet, or // something is wrong with reliable event acking. You can always increase // the number of history chunks if this becomes an issue. SequenceId firstId = SequenceId.Invalid; foreach (RailEvent evnt in outgoingEvents) { // Ignore dead events, they'll be cleaned up eventually if (evnt.Attempts <= 0) { continue; } // Don't send an event if it's out of scope for this peer if (Scope != null && Scope.Includes(evnt) == false) { // Skipping due to out of scope counts as an attempt evnt.RegisterSkip(); continue; } if (firstId.IsValid == false) { firstId = evnt.EventId; } RailDebug.Assert(firstId <= evnt.EventId); if (eventHistory.AreInRange(firstId, evnt.EventId) == false) { RailDebug.LogWarning("Throttling events due to lack of ack"); break; } yield return(evnt); } }
public void Overwrite(IRailStateConstruction stateCreator, Tick tick, RailState state) { RailDebug.Assert(tick.IsValid); this.tick = tick; if (this.state == null) { this.state = state.Clone(stateCreator); } else { this.state.OverwriteFrom(state); } }
public static Tick Subtract(Tick a, uint b, bool warnClamp = false) { long result = a.tickValue - (int)b; if (result < 1) { if (warnClamp) { RailDebug.LogWarning("Clamping tick subtraction"); } result = 1; } return(new Tick((uint)result)); }
public uint Pack(int value) { if ((value < this.minValue) || (value > this.maxValue)) { RailDebug.LogWarning( "Clamping value for send! " + value + " vs. [" + this.minValue + "," + this.maxValue + "]"); } return((uint)(value - this.minValue) & this.mask); }
public void Overwrite( Tick tick, RailState state) { RailDebug.Assert(tick.IsValid); this.tick = tick; if (this.state == null) { this.state = state.Clone(); } else { this.state.OverwriteFrom(state); } }
public ulong Pack(long value) { if (value < minValue || value > maxValue) { RailDebug.LogWarning( "Clamping value for send! " + value + " vs. [" + minValue + "," + maxValue + "]"); } return((ulong)(value - minValue) & mask); }
public uint Pack(float value) { float newValue = RailUtil.Clamp(value, this.minValue, this.maxValue); if (newValue != value) { RailDebug.LogWarning( "Clamping value for send! " + value + " vs. [" + this.minValue + "," + this.maxValue + "]"); } float adjusted = (value - this.minValue) * this.invPrecision; return((uint)(adjusted + 0.5f) & this.mask); }
public void WriteString(string value) { if (value == null) { throw new ArgumentNullException(nameof(value)); } uint length = (uint)value.Length; RailDebug.Assert(length <= RailConfig.STRING_LENGTH_MAX, value); if (value.Length > RailConfig.STRING_LENGTH_MAX) { length = RailConfig.STRING_LENGTH_MAX; } Write(STRING_LENGTH_BITS, length); for (int i = 0; i < length; i++) { Write(ASCII_BITS, ToASCII(value[i])); } }
/// <summary> /// Copies the buffer to a byte buffer. /// </summary> public int Store(byte[] data) { // Write a sentinel bit to find our position and flash out bad data Write(1, 1); int numChunks = (writePos >> 5) + 1; RailDebug.Assert(data.Length >= numChunks * 4, "Buffer too small"); for (int i = 0; i < numChunks; i++) { int dataIdx = i * 4; uint chunk = chunks[i]; data[dataIdx] = (byte)chunk; data[dataIdx + 1] = (byte)(chunk >> 8); data[dataIdx + 2] = (byte)(chunk >> 16); data[dataIdx + 3] = (byte)(chunk >> 24); } return(ByteSize); }
private static byte ToASCII(char character) { byte value; try { value = Convert.ToByte(character); } catch (OverflowException) { RailDebug.LogWarning("Cannot convert to simple ASCII: " + character); return(0); } if (value > 127) { RailDebug.LogWarning("Cannot convert to simple ASCII: " + character); return(0); } return(value); }
/// <summary> /// Sets the current server peer. /// </summary> public void SetPeer(IRailNetPeer netPeer) { if (netPeer == null) { if (ServerPeer != null) { ServerPeer.PacketReceived -= OnPacketReceived; ServerPeer.EventReceived -= OnEventReceived; Disconnected?.Invoke(ServerPeer); } ServerPeer = null; } else { RailDebug.Assert(ServerPeer == null, "Overwriting peer"); ServerPeer = new RailClientPeer(Resource, netPeer, Interpreter); ServerPeer.PacketReceived += OnPacketReceived; ServerPeer.EventReceived += OnEventReceived; Connected?.Invoke(ServerPeer); } }
private void OnPayloadReceived(IRailNetPeer peer, ArraySegment <byte> buffer) { try { RailBitBuffer bitBuffer = interpreter.LoadData(buffer); reusableIncoming.Reset(); reusableIncoming.Decode(Resource, bitBuffer); if (bitBuffer.IsFinished) { ProcessPacket(reusableIncoming, LocalTick); } else { RailDebug.LogError("Bad packet read, discarding..."); } } catch (Exception e) { RailDebug.LogError("Error during packet read: " + e); } }
// See http://www.gamedev.net/topic/652186-de-jitter-buffer-on-both-the-client-and-server/ public void Update() { if (this.shouldTick == false) { return; // 0; } this.estimatedRemote = this.estimatedRemote + 1; if (this.shouldUpdateEstimate == false) { return; // 1; } int delta = this.latestRemote - this.estimatedRemote; if (this.ShouldSnapTick(delta)) { // Reset RailDebug.LogMessage("Reset"); this.estimatedRemote = this.latestRemote - this.delayDesired; return; // 0; } else if (delta > this.delayMax) { // Jump 1 RailDebug.LogMessage("Jump 1"); this.estimatedRemote = this.estimatedRemote + 1; return; // 2; } else if (delta < this.delayMin) { // Stall 1 RailDebug.LogMessage("Stall 1"); this.estimatedRemote = this.estimatedRemote - 1; return; // 0; } this.shouldUpdateEstimate = false; return; // 1; }
public virtual void Removed() { RailDebug.Assert(HasStarted); OnRemoved(); }
public bool Equals(Tick x, Tick y) { RailDebug.Assert(x.IsValid); RailDebug.Assert(y.IsValid); return(x == y); }
public Tick GetNext() { RailDebug.Assert(IsValid); return(new Tick(tickValue + 1)); }
public float ToTime(float tickDeltaTime) { RailDebug.Assert(IsValid); return((tickValue - 1) * tickDeltaTime); }
public static Tick operator +(Tick a, uint b) { RailDebug.Assert(a.IsValid); return(new Tick(a.tickValue + b)); }