Esempio n. 1
0
        /// <summary>
        /// Updates all entities and dispatches a snapshot if applicable. Should
        /// be called once per game simulation tick (e.g. during Unity's
        /// FixedUpdate pass).
        /// </summary>
        public void Update()
        {
            DoStart();

            foreach (var client in clients.Values)
            {
                client.RemoteClock.Update();
            }

            {
                // ServerWorld.ServerUpdate
                var tick = World_Tick.GetNext();

                World_Update(tick, entity =>
                {
                    //entity.Initialize();
                    if (!entity.HasStarted)
                    {
                        Entity_OnStart(entity);
                    }
                    entity.HasStarted = true;

                    //entity.NotifyControllerChanged();
                    if (entity.DeferNotifyControllerChanged)
                    {
                        Entity_OnControllerChanged(entity);
                    }
                    entity.DeferNotifyControllerChanged = false;

                    Entity_UpdateAuth(entity);

                    Command latest = default;
                    if (entity.Controller != null)
                    {
                        latest = entity.IncomingCommands.GetLatestAt(entity.Controller.EstimatedRemoteTick);
                    }

                    if (latest != null)
                    {
                        Entity_ApplyControlGeneric(entity, latest);
                        latest.IsNewCommand = false;

                        var latestCommandTick = entity.Controller.EstimatedRemoteTick;
                        // Use the remote tick rather than the last applied tick
                        // because we might be skipping some commands to keep up
                        var shouldAck = !entity.CommandAck.IsValid || (latestCommandTick > entity.CommandAck);
                        if (shouldAck)
                        {
                            entity.CommandAck = latestCommandTick;
                        }
                    }

                    Entity_PostUpdate(entity);
                },
                             entity => Entity_OnShutdown(entity));
            }

            if (World_Tick.IsSendTick())
            {
                {
                    // ServerWorld.StoreStates
                    foreach (var entity in World_Entities.Values)
                    {
                        {
                            // Entity.StoreRecord();
                            var record = CreateRecord(World_Tick, entity.StateBase, entity.OutgoingStates.Latest);

                            /// <summary>
                            /// Creates a record of the current state, taking the latest record (if
                            /// any) into account. If a latest state is given, this function will
                            /// return null if there is no change between the current and latest.
                            /// </summary>
                            StateRecord CreateRecord(Tick tick, State current, StateRecord latestRecord = null)
                            {
                                if (latestRecord != null)
                                {
                                    var latest       = latestRecord.State;
                                    var shouldReturn = current.CompareMutableData(latest) > 0 || !current.IsControllerDataEqual(latest);
                                    if (!shouldReturn)
                                    {
                                        return(null);
                                    }
                                }

                                var _record = _pools.RecordPool.Allocate();

                                {
                                    //record.Overwrite(tick, current);
                                    Debug.Assert(tick.IsValid);

                                    _record.Tick = tick;
                                    if (_record.State == null)
                                    {
                                        //this.state = state.Clone();
                                        var clone = _pools.CreateState(current.TypeCode);
                                        clone.OverwriteFrom(current);
                                        _record.State = clone;
                                    }
                                    else
                                    {
                                        _record.State.OverwriteFrom(current);
                                    }
                                }
                                return(_record);
                            }

                            if (record != null)
                            {
                                entity.OutgoingStates.Store(record);
                            }
                        }
                    }
                }
                {
                    /// <summary>
                    /// Packs and sends a server-to-client packet to each peer.
                    /// </summary>
                    // ServerManager.BroadcastPackets
                    foreach (var controller in clients.Values)
                    {
                        var localTick = World_Tick;
                        var active    = World_Entities.Values;
                        var destroyed = destroyedEntities.Values;
                        {
                            //ServerConnection.SendPacket
                            /// <summary>
                            /// Allocates a packet and writes common boilerplate information to it.
                            /// Make sure to call OnSent() afterwards.
                            /// </summary>
                            // Packet.PrepareSend
                            //var packet_ = controller.ReusableOutgoing;
                            //packet_.Reset();
                            var packet_ = new ServerOutgoingPacket();
                            packet_.Initialize(localTick, controller.RemoteClock.LatestRemote, controller.ProcessedEventHistory.Latest, FilterOutgoingEvents(controller));

                            controller.Scope.PopulateDeltas(controller, localTick, packet_, active, destroyed, () => _pools.DeltaPool.Allocate(), typeCode => _pools.CreateState(typeCode));

                            var t = _protocol.Encode(packet_);
                            controller.Connection.SendPayload(t.Item1, t.Item2);


                            foreach (var delta in packet_.SentDeltas)
                            {
                                controller.Scope.LastSent.RecordUpdate(delta.EntityId, new ViewEntry(localTick, delta.IsFrozen));
                            }
                        }
                    }
                }
            }
        }
Esempio n. 2
0
        public void PopulateDeltas(Controller target, Tick serverTick, ServerOutgoingPacket packet, IEnumerable <ServerEntity> activeEntities, IEnumerable <ServerEntity> destroyedEntities,
                                   Func <StateDelta> deltaFactory, Func <int, State> stateFactory)
        {
            {
                /// <summary>
                /// Divides the active entities into those that are in scope and those
                /// out of scope. If an entity is out of scope and hasn't been acked as
                /// such by the client, we will add it to the outgoing frozen delta list.
                /// Otherwise, if an entity is in scope we will add it to the sorted
                /// active delta list.
                /// </summary>
                // void ProduceScoped(IController target, Tick serverTick, IEnumerable<Entity> activeEntities)
                entryList.Clear();

                foreach (var entity in activeEntities)
                {
                    // Controlled entities are always in scope with highest priority
                    if (entity.Controller == target)
                    {
                        entryList.Add(new KeyValuePair <float, ServerEntity>(float.MinValue, entity));
                    }
                    else
                    {
                        var b = GetPriority(entity, serverTick, out var priority);
                        if (b)
                        {
                            entryList.Add(new KeyValuePair <float, ServerEntity>(priority, entity));
                        }
                        else
                        {
                            // We only want to send a freeze state if we aren't already frozen
                            var latest = AckedByClient.GetLatest(entity.Id);
                            if (!latest.IsFrozen)
                            {
                                // StateDelta.CreateFrozen
                                var delta = deltaFactory();
                                delta.Initialize(serverTick, entity.Id, null, true);

                                frozenList.Add(delta);
                            }
                        }
                    }
                }

                entryList.Sort(_comparer);
                foreach (var entry in entryList)
                {
                    var latest = AckedByClient.GetLatest(entry.Value.Id);
                    var delta  = ProduceDelta(deltaFactory, stateFactory, entry.Value, latest.Tick, target);
                    if (delta != null)
                    {
                        activeList.Add(delta);
                    }
                }
            }
            {
                /// <summary>
                /// Produces deltas for all non-acked destroyed entities.
                /// </summary>
                // private void ProduceDestroyed(IController target, IEnumerable<Entity> destroyedEntities)
                foreach (var entity in destroyedEntities)
                {
                    var latest = AckedByClient.GetLatest(entity.Id);
                    if (latest.Tick.IsValid && (latest.Tick < entity.RemovedTick))
                    {
                        // Note: Because the removed tick is valid, this should force-create
                        var delta = ProduceDelta(deltaFactory, stateFactory, entity, latest.Tick, target);
                        destroyedList.Add(delta);
                    }
                }
            }

            {
                // packet.Populate(activeList, frozenList, destroyedList);
                packet.PendingDeltas.AddRange(destroyedList);
                packet.PendingDeltas.AddRange(frozenList);
                packet.PendingDeltas.AddRange(activeList);
            }

            destroyedList.Clear();
            frozenList.Clear();
            activeList.Clear();
        }
Esempio n. 3
0
        public (byte[], int) Encode(ServerOutgoingPacket packet)
        {
            var writeSize = MessagePackSerializer.Serialize(ref bytes, 0, packet, _resolver);

            return(bytes, writeSize);
        }