/// <summary> /// Determines if the interest collection has pending interest changes. /// (Joining or leaving the collection via queues). /// </summary> /// <param name="collection"></param> /// <returns></returns> public static bool HasPendingChanges([NotNull] this InterestCollection collection) { if (collection == null) { throw new ArgumentNullException(nameof(collection)); } return(!collection.EnteringDequeueable.isEmpty || !collection.LeavingDequeueable.isEmpty); }
public DefaultGameObjectActorState([NotNull] IEntityDataFieldContainer entityData, [NotNull] NetworkEntityGuid entityGuid, [NotNull] GameObjectInstanceModel instanceModel, [NotNull] GameObjectTemplateModel templateModel, [NotNull] InterestCollection interest) : base(entityData, entityGuid, interest) { InstanceModel = instanceModel ?? throw new ArgumentNullException(nameof(instanceModel)); TemplateModel = templateModel ?? throw new ArgumentNullException(nameof(templateModel)); }
protected override void OnEventFired(object source, EntityCreationStartingEventArgs args) { InterestCollection playerInterestCollection = new InterestCollection(); //directly add ourselves so we don't become interest in ourselves after spawning playerInterestCollection.Add(args.EntityGuid); //We just create our own manaul interest collection here. GuidToInterestCollectionMappable.AddObject(args.EntityGuid, playerInterestCollection); }
protected override void OnEventFired(object source, EntityDeconstructionFinishedEventArgs args) { //At this point, the entity deconstruction is FINISHED so we can actually tell all known entites to forget it. //Threadsafe internally to iterate foreach (NetworkEntityGuid entity in KnownEntities) { InterestCollection ic = InterestCollections.RetrieveEntity(entity); if (ic.Contains(args.EntityGuid)) { //We just spoof an exit to every interested collection who knows of the entity being cleaned up. InterestEventSpoofer.SpoofExitInterest(new EntityInterestChangeEventArgs(entity, args.EntityGuid, EntityInterestChangeEventArgs.ChangeType.Exit)); } } }
protected override Task HandleMessage(IPeerSessionMessageContext <GameServerPacketPayload> context, PlayerNetworkTrackerChangeUpdateRequest payload, NetworkEntityGuid guid) { //TODO: Do some validation here. Players could be sending empty ones, or invalid ones. InterestCollection interestCollection = InterestCollections.RetrieveEntity(guid); PlayerNetworkTrackerChangeUpdateEvent changeUpdateEvent = new PlayerNetworkTrackerChangeUpdateEvent(new EntityAssociatedData <PlayerNetworkTrackerChangeUpdateRequest>(guid, payload)); //Only send to players and not yourself. foreach (NetworkEntityGuid entity in interestCollection.ContainedEntities) { if (entity.EntityType == EntityType.Player && entity.EntityId != guid.EntityId) { EntityMessageSender.SendMessageAsync(entity, changeUpdateEvent); } } return(Task.CompletedTask); }
/// <inheritdoc /> protected override void OnFinishedServicingEvents() { //After ALL the queued interest changes have been serviced //we can actually handle the changes and send them and such KnownEntities.LockObject.EnterReadLock(); try { //We need to iterate the entire interest dictionary //That means we need to check the new incoming and outgoing entities //We do this because we need to build update packets for the players //so that they can become aware of them AND we can start pushing //events to them foreach (var entity in KnownEntities) { InterestCollection interestCollection = ManagedInterestCollections.RetrieveEntity(entity); //We want to skip any collection that doesn't have any pending changes. //No reason to send a message about it nor dequeue anything if (!interestCollection.HasPendingChanges()) { continue; } //We should only build packets for players. if (entity.EntityType == EntityType.Player) { VisibilityMessageSender.Send(new EntityVisibilityChangeContext(entity, interestCollection)); } //No matter player or NPC we should dequeue the joining/leaving //entites so that the state of the known entites reflects the diff packets sent InterestDequeueSetCommand dequeueCommand = new InterestDequeueSetCommand(interestCollection); //TODO: Should we execute right away? Or after all packets are sent? dequeueCommand.Execute(); } } finally { KnownEntities.LockObject.ExitReadLock(); } }
/// <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(); } }
public NetworkedObjectActorState(IEntityDataFieldContainer entityData, NetworkEntityGuid entityGuid, [NotNull] InterestCollection interest) : base(entityData, entityGuid) { Interest = interest ?? throw new ArgumentNullException(nameof(interest)); }
public BehaviourGameObjectState(IEntityDataFieldContainer entityData, NetworkEntityGuid entityGuid, GameObjectInstanceModel instanceModel, GameObjectTemplateModel templateModel, [NotNull] TBehaviourType behaviourData, [NotNull] InterestCollection interest) : base(entityData, entityGuid, instanceModel, templateModel, interest) { BehaviourData = behaviourData ?? throw new ArgumentNullException(nameof(behaviourData)); }
public DefaultPlayerEntityActorState(IEntityDataFieldContainer entityData, NetworkEntityGuid entityGuid, InterestCollection interest, [NotNull] IPeerPayloadSendService <GameServerPacketPayload> sendService) : base(entityData, entityGuid, interest) { SendService = sendService ?? throw new ArgumentNullException(nameof(sendService)); }