Exemple #1
0
        public void GenerateFieldUpdateDiff(FieldValueUpdate fieldsCollection, [NotNull] IChangeTrackableEntityDataCollection changeTrackable)
        {
            if (changeTrackable == null)
            {
                throw new ArgumentNullException(nameof(changeTrackable));
            }

            lock (changeTrackable.SyncObj)
            {
                int updateDiffIndex = 0;
                foreach (int setIndex in fieldsCollection.FieldValueUpdateMask.EnumerateSetBitsByIndex())
                {
                    changeTrackable.SetFieldValue(setIndex, fieldsCollection.FieldValueUpdates.ElementAt(updateDiffIndex));

                    //Hey, so there was a bug for 8byte values that caused this to break.
                    //I know it's bad design but we all have deadlines here. We NEED this to FORCEIBLY
                    //make it appear as if it's changed even if it hasn't. Otherwise the change may not get dispatched.
                    //Trust me, these hacks will be hidden deep in networking engine code like this that runs on another thread
                    //that nobody understands. If you're here, then you understand. Tell them only that the Lich King is dead
                    //and that Bolvar Fordragon died with him.
                    changeTrackable.ChangeTrackingArray.Set(setIndex, true);
                    updateDiffIndex++;
                }
            }
        }
Exemple #2
0
        public void Test_FieldValueFactory_With_Single_Value_Produces_Correct_FieldValueUpdate([EntityDataCollectionTestRange] int index, [Values(1, 2, 3, 4, 5, 6, 7, 8)] int value)
        {
            //arrange
            ChangeTrackingEntityFieldDataCollectionDecorator collection = new ChangeTrackingEntityFieldDataCollectionDecorator(new EntityFieldDataCollection(8));
            FieldValueUpdateFactory updateFactory = new FieldValueUpdateFactory();

            //act
            collection.SetFieldValue <int>(index, value);
            FieldValueUpdate fieldValueUpdate = updateFactory.Create(new EntityFieldUpdateCreationContext(collection, collection.ChangeTrackingArray));


            //assert
            Assert.AreEqual(1, fieldValueUpdate.FieldValueUpdateMask.EnumerateSetBitsByIndex().Count(), $"Found more than 1 set bit.");
            Assert.AreEqual(value, fieldValueUpdate.FieldValueUpdates.First(), $"Serialized value was not expected value.");
            Assert.AreEqual(index, fieldValueUpdate.FieldValueUpdateMask.EnumerateSetBitsByIndex().First(), $"Index: {index} was expected to be in the update but was not.");
        }
Exemple #3
0
        public void GenerateFieldUpdateDiff(FieldValueUpdate fieldsCollection, [NotNull] IChangeTrackableEntityDataCollection changeTrackable)
        {
            if (changeTrackable == null)
            {
                throw new ArgumentNullException(nameof(changeTrackable));
            }

            lock (changeTrackable.SyncObj)
            {
                int updateDiffIndex = 0;
                foreach (int setIndex in fieldsCollection.FieldValueUpdateMask.EnumerateSetBitsByIndex())
                {
                    changeTrackable.SetFieldValue(setIndex, fieldsCollection.FieldValueUpdates.ElementAt(updateDiffIndex));
                    updateDiffIndex++;
                }
            }
        }
Exemple #4
0
        public void Test_ChangeTracker_With_Multiple_Value_Indicates_No_Changes_After_Clearing_FieldValueUpdate()
        {
            //arrange
            ChangeTrackingEntityFieldDataCollectionDecorator collection = new ChangeTrackingEntityFieldDataCollectionDecorator(new EntityFieldDataCollection(8));
            FieldValueUpdateFactory updateFactory = new FieldValueUpdateFactory();

            //act
            collection.SetFieldValue <int>(1, 5);
            collection.SetFieldValue <int>(2, 4);
            collection.SetFieldValue <int>(3, 7);
            collection.ClearTrackedChanges();
            FieldValueUpdate fieldValueUpdate = updateFactory.Create(new EntityFieldUpdateCreationContext(collection, collection.ChangeTrackingArray));


            //assert
            Assert.AreEqual(0, fieldValueUpdate.FieldValueUpdateMask.EnumerateSetBitsByIndex().Count(), $"Found more than 1 set bit.");
            Assert.AreEqual(0, fieldValueUpdate.FieldValueUpdates.Count, $"Field updates should be empty due to no changes..");
        }
Exemple #5
0
        public void Test_FieldValueFactory_With_Multiple_Value_Produces_Correct_FieldValueUpdate()
        {
            //arrange
            ChangeTrackingEntityFieldDataCollectionDecorator collection = new ChangeTrackingEntityFieldDataCollectionDecorator(new EntityFieldDataCollection(8));
            FieldValueUpdateFactory updateFactory = new FieldValueUpdateFactory();

            //act
            collection.SetFieldValue <int>(1, 5);
            collection.SetFieldValue <int>(2, 4);
            collection.SetFieldValue <int>(3, 7);
            FieldValueUpdate fieldValueUpdate = updateFactory.Create(new EntityFieldUpdateCreationContext(collection, collection.ChangeTrackingArray));


            //assert
            Assert.AreEqual(3, fieldValueUpdate.FieldValueUpdateMask.EnumerateSetBitsByIndex().Count(), $"Found more than 1 set bit.");
            Assert.AreEqual(5, fieldValueUpdate.FieldValueUpdates.First(), $"Serialized value was not expected value.");
            Assert.AreEqual(1, fieldValueUpdate.FieldValueUpdateMask.EnumerateSetBitsByIndex().First(), $"Index: {1} was expected to be first index.");
        }
Exemple #6
0
        /// <inheritdoc />
        public void Tick()
        {
            foreach (var entry in GuidToInterestCollectionMappable.EnumerateWithGuid(KnownEntities, EntityType.Player))
            {
                InterestCollection interest = entry.ComponentValue;

                //Even if we only know ourselves we should do this anyway
                //so that the client can receieve entity data changes about itself

                //TODO: We probably won't send an update about ALL entites, so this is some wasted allocations and time
                List <EntityAssociatedData <FieldValueUpdate> > updates = new List <EntityAssociatedData <FieldValueUpdate> >(interest.ContainedEntities.Count);

                foreach (var interestingEntityGuid in interest.ContainedEntities)
                {
                    //Don't build an update for entities that don't have any changes
                    if (!ChangeTrackerHasChangesForEntity(interestingEntityGuid))
                    {
                        continue;
                    }

                    //TODO: We should cache this update value so we don't need to recompute it for ALL players who are interested
                    //This is the update collection for the particular Entity with guid interestingEntityGuid
                    //We want to use the CHANGE TRACKING bitarray for updates. If this was initial discovery we'd use the SIT bitarray to send all set values.
                    FieldValueUpdate update = UpdateFactory.Create(new EntityFieldUpdateCreationContext(ChangeTrackingCollections.RetrieveEntity(interestingEntityGuid), ChangeTrackingCollections.RetrieveEntity(interestingEntityGuid).ChangeTrackingArray));

                    updates.Add(new EntityAssociatedData <FieldValueUpdate>(interestingEntityGuid, update));
                }

                //It's possible no entity had updates, so we should not send a packet update
                if (updates.Count != 0)
                {
                    SendUpdate(entry.EntityGuid, updates);
                }
            }

            foreach (var dataEntityCollection in ChangeTrackingCollections.Enumerate(KnownEntities))
            {
                dataEntityCollection.ClearTrackedChanges();
            }
        }
Exemple #7
0
        public void Test_ChangeTracker_Doesnt_Set_Change_Bits_On_Same_Value_After_Clear_FieldValueUpdate()
        {
            //arrange
            WireReadyBitArray bitArray = new WireReadyBitArray(1328);

            bitArray.Set(1, true);
            bitArray.Set(2, true);
            bitArray.Set(4, true);

            //Reference the actual client's visibile field update computation.
            IEntityDataFieldContainer dataCollection = NetworkVisibilityCreationBlockToVisibilityEventFactory.CreateInitialEntityFieldContainer(new FieldValueUpdate(bitArray, new int[] { 5, 4, 7 }));

            ChangeTrackingEntityFieldDataCollectionDecorator collection = new ChangeTrackingEntityFieldDataCollectionDecorator(dataCollection, bitArray);
            FieldValueUpdateFactory updateFactory = new FieldValueUpdateFactory();

            //act
            FieldValueUpdate fieldValueUpdate = updateFactory.Create(new EntityFieldUpdateCreationContext(collection, collection.ChangeTrackingArray));

            Assert.AreEqual(3, fieldValueUpdate.FieldValueUpdateMask.EnumerateSetBitsByIndex().Count(), $"Found more than 1 set bit.");
            Assert.AreEqual(5, fieldValueUpdate.FieldValueUpdates.First(), $"Serialized value was not expected value.");
            Assert.AreEqual(1, fieldValueUpdate.FieldValueUpdateMask.EnumerateSetBitsByIndex().First(), $"Index: {1} was expected to be first index.");

            collection.ClearTrackedChanges();


            //Check they're event before setting them again
            Assert.AreEqual(collection.GetFieldValue <int>(1), 5, $"Values not the same.");
            Assert.AreEqual(collection.GetFieldValue <int>(2), 4, $"Values not the same.");
            collection.SetFieldValue(1, 5);
            collection.SetFieldValue(2, 4);

            fieldValueUpdate = updateFactory.Create(new EntityFieldUpdateCreationContext(collection, collection.ChangeTrackingArray));

            //assert
            Assert.AreEqual(0, fieldValueUpdate.FieldValueUpdateMask.EnumerateSetBitsByIndex().Count(), $"Found more than 1 set bit.");
            Assert.AreEqual(0, fieldValueUpdate.FieldValueUpdates.Count, $"Field updates should be empty due to no changes..");
        }
Exemple #8
0
        //Public and static because it's referenced in a unit test. That design oddity is
        //worth the absolute critical design fault that was uncovered and now protected by a test.
        public static unsafe IEntityDataFieldContainer CreateInitialEntityFieldContainer(FieldValueUpdate fieldValueData)
        {
            //TODO: We could pool this.
            //we actually CAN'T use the field enum length or count. Since TrinityCore may send additional bytes at the end so that
            //it's evently divisible by 32.
            byte[] internalEntityDataBytes = new byte[fieldValueData.FieldValueUpdateMask.Length * sizeof(int)];

            //It's absolutely CRITICAL that we don't use the sent fieldvalue internal bits for the set indication data
            //BECAUSE it also will be used as the initial changed values/change array. So we do the one-time copy here to avoid this critical
            //fault which is now covered by tests.
            byte[] copiedFieldUpdateMask = new byte[fieldValueData.FieldValueUpdateMask.InternalIntegerArray.Count];
            Buffer.BlockCopy(fieldValueData.FieldValueUpdateMask.InternalIntegerArray.ToArrayTryAvoidCopy(), 0, copiedFieldUpdateMask, 0, fieldValueData.FieldValueUpdateMask.InternalIntegerArray.Count);

            IEntityDataFieldContainer t = new EntityFieldDataCollection(new WireReadyBitArray(copiedFieldUpdateMask), internalEntityDataBytes);

            int updateDiffIndex = 0;

            foreach (int setIndex in t.DataSetIndicationArray.EnumerateSetBitsByIndex())
            {
                int   value = fieldValueData.FieldValueUpdates.ElementAt(updateDiffIndex);
                byte *bytes = (byte *)&value;

                //TODO: Would it be faster to buffer copy?
                //The way wow works is these are 4 byte chunks
                for (int i = 0; i < 4; i++)
                {
                    internalEntityDataBytes[setIndex * sizeof(int) + i] = *(bytes + i);
                }

                updateDiffIndex++;
            }

            return(t);
        }
Exemple #9
0
 /// <inheritdoc />
 public EntityCreationData([NotNull] NetworkEntityGuid entityGuid, [NotNull] IMovementData initialMovementData, [NotNull] FieldValueUpdate initialFieldValues)
 {
     EntityGuid          = entityGuid ?? throw new ArgumentNullException(nameof(entityGuid));
     InitialMovementData = initialMovementData ?? throw new ArgumentNullException(nameof(initialMovementData));
     InitialFieldValues  = initialFieldValues ?? throw new ArgumentNullException(nameof(initialFieldValues));
 }