/// <inheritdoc /> protected override void HandleEvent(PlayerSessionClaimedEventArgs args) { if (Logger.IsDebugEnabled) { Logger.Debug($"Dequeueing entity creation request for: {args.EntityGuid.EntityType}:{args.EntityGuid.EntityId}"); } //TODO: This is test data EntityFieldDataCollection <EntityDataFieldType> testData = new EntityFieldDataCollection <EntityDataFieldType>(); //TODO: Test values set testData.SetFieldValue(EntityDataFieldType.EntityCurrentHealth, 100); testData.SetFieldValue(EntityDataFieldType.EntityMaxHealth, 120); using (LockingPolicy.WriterLock(null, CancellationToken.None)) { //TODO: Time stamp //TODO: We should check if the result is valid? Maybe return a CreationResult? //We don't need to do anything with the returned object. GameObject playerGameObject = PlayerFactory.Create(new PlayerEntityCreationContext(args.EntityGuid, args.SessionContext, new PositionChangeMovementData(0, args.SpawnPosition, Vector2.zero), EntityPrefab.RemotePlayer, testData)); } if (Logger.IsDebugEnabled) { Logger.Debug($"Sending player spawn payload Id: {args.EntityGuid.EntityId}"); } OnPlayerWorldSessionCreated?.Invoke(this, new PlayerWorldSessionCreationEventArgs(args.EntityGuid)); //TODO: If we want to do anything post-creation with the provide gameobject we could. But we really don't want to at the moment. }
/// <inheritdoc /> public override async Task HandleMessage(IPeerMessageContext <GameClientPacketPayload> context, PlayerSelfSpawnEventPayload payload) { //TODO: Actually handle this. Right now it's just demo code, it actually could fail. if (Logger.IsInfoEnabled) { Logger.Info($"Recieved server commanded PlayerSpawn. Player GUID: {payload.CreationData.EntityGuid} Position: {payload.CreationData.InitialMovementData.InitialPosition}"); } EntityFieldDataCollection <EntityDataFieldType> entityData = CreateEntityDataCollectionFromPayload(payload.CreationData.InitialFieldValues); LogEntityDataFields(payload); await new UnityYieldAwaitable(); //Don't do any checks for now, we just spawn GameObject playerGameObject = PlayerFactory.Create(new DefaultEntityCreationContext(payload.CreationData.EntityGuid, payload.CreationData.InitialMovementData, EntityPrefab.LocalPlayer, entityData)); //Set local player entity guid, lots of dependencies need this set to work. LocalPlayerDetails.LocalPlayerGuid = payload.CreationData.EntityGuid; //Call all OnGameInitializables foreach (var init in Initializables) { await init.OnGameInitialized() .ConfigureAwait(false); } //TODO: We need to make this the first packet, or couple of packets. We don't want to do this inbetween potentially slow operatons. await context.PayloadSendService.SendMessageImmediately(new ServerTimeSyncronizationRequestPayload(DateTime.UtcNow.Ticks)) .ConfigureAwait(false); }
/// <inheritdoc /> protected override void OnEventFired(object source, SelfPlayerSpawnEventArgs args) { EntityFieldDataCollection <EntityDataFieldType> entityData = CreateEntityDataCollectionFromPayload(args.CreationData.InitialFieldValues); //Don't do any checks for now, we just spawn GameObject playerGameObject = PlayerFactory.Create(new DefaultEntityCreationContext(args.CreationData.EntityGuid, args.CreationData.InitialMovementData, EntityPrefab.LocalPlayer, entityData)); //Broadcast that the player has spawned. OnLocalPlayerSpawned?.Invoke(this, new LocalPlayerSpawnedEventArgs(args.CreationData.EntityGuid)); }
private static EntityFieldDataCollection <EntityDataFieldType> CreateEntityDataCollectionFromPayload(FieldValueUpdate fieldUpdateValues) { //TODO: We need better initial handling for entity data EntityFieldDataCollection <EntityDataFieldType> entityData = new EntityFieldDataCollection <EntityDataFieldType>(); int currentSentIndex = 0; foreach (var setIndex in fieldUpdateValues.FieldValueUpdateMask.EnumerateSetBitsByIndex()) { entityData.SetFieldValue(setIndex, fieldUpdateValues.FieldValueUpdates.ElementAt(currentSentIndex)); currentSentIndex++; } return(entityData); }
/// <inheritdoc /> public override async Task HandleMessage(IPeerMessageContext <GameClientPacketPayload> context, NetworkObjectVisibilityChangeEventPayload payload) { foreach (var entity in payload.EntitiesToCreate) { if (Logger.IsDebugEnabled) { Logger.Debug($"Encountered new entity: {entity.EntityGuid}"); } } foreach (var entity in payload.OutOfRangeEntities) { if (Logger.IsErrorEnabled) { Logger.Debug($"Leaving entity: {entity}"); } } //Assume it's a player for now foreach (var creationData in payload.EntitiesToCreate) { //TODO: Right now we're creating a temporary entity data collection. EntityFieldDataCollection <EntityDataFieldType> testData = new EntityFieldDataCollection <EntityDataFieldType>(); //We set the initial values off the main thread, it is much better that way. //However, that means initial values won't dispatch OnChange stuff. SetInitialFieldValues(creationData, testData); OnNetworkEntityNowVisible?.Invoke(this, new NetworkEntityNowVisibleEventArgs(creationData.EntityGuid, creationData, testData)); } foreach (var destroyData in payload.OutOfRangeEntities) { OnNetworkEntityVisibilityLost?.Invoke(this, new NetworkEntityVisibilityLostEventArgs(destroyData)); } //TODO: We should not waste 2, maybe even more, frames to prevent the race condition for spawn/despawn and other packets. //We SHOULD actually only call these awaits in other handlers where we realize we MAY not have spawned the entity yet. //This should yield better-case throughput because MANY packets could be handled unrelated inbetween these awaits. //Two tickable frames is long enough for all spawn/despawn logic to have run. await UnityExtended.AwaitNextTickableFrameAsync() .ConfigureAwait(false); await UnityExtended.AwaitNextTickableFrameAsync() .ConfigureAwait(false); }