private void Handler(RAGE.Elements.Entity entity, object data, object oldData)
        {
            if (!entity.HasData("Attachments"))
            {
                entity.SetData("Attachments", new List <uint>());
            }

            if (!entity.HasData("Objects"))
            {
                entity.SetData("Objects", new Dictionary <uint, MapObject>());
            }

            var newAttachments = DeSerialize(data).ToList();

            // process outdated first
            foreach (var attachment in entity.GetData <List <uint> >("Attachments"))
            {
                if (!newAttachments.Contains(attachment))
                {
                    DetachObject(entity, attachment);
                }
            }

            // then new attachments
            foreach (var key in newAttachments)
            {
                AttachObject(entity, key);
            }

            entity.SetData("Attachments", newAttachments);
        }
        private void AttachObject(RAGE.Elements.Entity entity, uint key)
        {
            var entityObjects = entity.GetData <Dictionary <uint, MapObject> >("Objects");

            if (AttachmentDictionary.ContainsKey(key) && !entityObjects.ContainsKey(key))
            {
                var entityAttachment = AttachmentDictionary[key];
                var mapObject        = new MapObject(entityAttachment.Model, new Vector3(0, 0, 0), new Vector3(0, 0, 0))
                {
                    Dimension = uint.MaxValue
                };

                var gameEntity = entity.GetGameEntity();

                Entity.AttachEntityToEntity(mapObject.Handle, gameEntity.Handle, RAGE.Game.Ped.GetPedBoneIndex(gameEntity.Handle, entityAttachment.BoneId),
                                            entityAttachment.Offset.X, entityAttachment.Offset.Y, entityAttachment.Offset.Z,
                                            entityAttachment.Rotation.X, entityAttachment.Rotation.Y, entityAttachment.Rotation.Z,
                                            false, false, false, false, 2, true);

                if (Entity.IsEntityAttachedToEntity(mapObject.Handle, gameEntity.Handle))
                {
                    OnEntityAttachedEvent?.Invoke(gameEntity, entityAttachment.AttachmentEvent);
                }

                entityObjects.Add(key, mapObject);
                entity.SetData("Objects", entityObjects);
            }
        }
        private void EntityStreamOut(RAGE.Elements.Entity entity)
        {
            var mapObjects = entity.GetData <List <uint> >("Objects");

            if (mapObjects != null)
            {
                foreach (var key in mapObjects)
                {
                    DetachObject(entity, key);
                }
            }
        }
        private void EntityStreamIn(RAGE.Elements.Entity entity)
        {
            var attachments = entity.GetData <List <uint> >("Attachments");

            if (attachments != null)
            {
                foreach (var key in attachments)
                {
                    AttachObject(entity, key);
                }
            }
        }
        private void DetachObject(RAGE.Elements.Entity entity, uint key)
        {
            var entityObjects = entity.GetData <Dictionary <uint, MapObject> >("Objects");

            if (entityObjects.ContainsKey(key))
            {
                var entityObject = entityObjects[key];

                if (Entity.DoesEntityExist(entityObject.Handle))
                {
                    entityObject.Destroy();
                    OnEntityDetachedEvent?.Invoke(entity.GetGameEntity(), AttachmentDictionary[key].AttachmentEvent);
                }

                entityObjects.Remove(key);
                entity.SetData("Objects", entityObjects);
            }
        }