/// <inheritdoc /> protected override async Task HandleSubMessage(IPeerMessageContext <PSOBBGamePacketPayloadClient> context, Sub60GameBurstingCompleteEventCommand command) { //It's possible we get this on OUR join. We may not be spawned yet. if (!PlayerData.isWorldObjectSpawned) { return; } //This could be for a couple of reasons. Bursting wasn't set, and it's a BIG failure //or this is our JOIN bursting finish and we don't do anything here really. if (!BurstingService.isBurstingInProgress) { return; } //TODO: At some point, this may not run on the main thread so this won't be safe. GameObject playerWorldObject = PlayerData.WorldObject; Vector3 <float> scaledPosition = ScalerService.UnScale(playerWorldObject.transform.position).ToNetworkVector3(); float scaledRotation = ScalerService.UnScaleYRotation(playerWorldObject.transform.rotation.y); //If have to send this message otherwise other client's won't know we're also in the same zone //It's odd, but it's something we have to do. await context.PayloadSendService.SendMessage(new Sub60FinishedWarpAckCommand(SlotModel.SlotSelected, ZoneSettings.ZoneId, scaledPosition, scaledRotation).ToPayload()); int entityGuid = BurstingService.BurstingEntityGuid.Value; //Successful burst, let everyone know. OnClientBurstingFinished?.Invoke(this, new ClientBurstingEndingEventArgs(entityGuid, true)); //Bursting is done, we should release bursting state //Then we should broadcast to everyone that bursting is done. BurstingService.ClearBursting(); }
/// <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) { NetworkEntityNowVisibleEventArgs visibilityEvent = VisibileEventFactory.Create(creationData); VisibilityEventPublisher.Publish(visibilityEvent); } foreach (var destroyData in payload.OutOfRangeEntities) { OnNetworkEntityVisibilityLost?.Invoke(this, new NetworkEntityVisibilityLostEventArgs(destroyData)); } }
/// <inheritdoc /> public async Task HandleMessage(IPeerMessageContext <GameClientPacketPayload> context, GameServerPacketPayload payload) { if (Logger.IsWarnEnabled) { Logger.Warn($"Recieved unhandled Packet: {payload.GetType().Name}"); } }
//Just dispatches to the decorated handler. /// <inheritdoc /> public virtual async Task <bool> TryHandleMessage(IPeerMessageContext <TOutgoingPayloadType> context, NetworkIncomingMessage <TIncomingPayloadBaseType> message) { if (context == null) { throw new ArgumentNullException(nameof(context)); } if (message == null) { throw new ArgumentNullException(nameof(message)); } try { if (CanHandle(message)) { Logger.Info($"Recieved: {message.Payload}"); await HandleMessage(context, message.Payload as TPayloadType); return(true); } return(false); } catch (Exception e) { Logger.Error($"Encounter error in Handle: {GetType().Name} Exception: {e.Message} \n\n StackTrace: {e.StackTrace}"); throw; } }
/// <inheritdoc /> public override Task HandleMessage(IPeerMessageContext <PSOBBGamePacketPayloadClient> context, BlockClientPingEventPayload payload) { //Just send the ping response; otherwise we'll be disconnected. context.PayloadSendService.SendMessage(new BlockClientPingResponsePayload()); return(Task.CompletedTask); }
/// <inheritdoc /> public override Task HandleMessage(IPeerMessageContext <PSOBBGamePacketPayloadClient> context, BlockLobbyJoinEventPayload payload) { if (Logger.IsDebugEnabled) { Logger.Debug($"**Handling**: {nameof(BlockLobbyJoinEventPayload)}"); } //We need to make sure the lobby scene is registered if (!LobbyNumberToSceneNameMap.ContainsKey(payload.LobbyNumber)) { string lobbyError = $"Tried to enter Lobby: {payload.LobbyNumber} but no lobby for that id was registered."; if (Logger.IsErrorEnabled) { Logger.Error(lobbyError); } throw new InvalidOperationException(lobbyError); } //Just set the old char slot to the clientid //It's basically like a slot, like a lobby or party slot. SlotModel.SlotSelected = payload.ClientId; OnLobbyJoinEvent?.Invoke(); //TODO: Handle multiple different lobby scenes //Now we need to load the actual lobby //Doing so will require us to load a new lobby scene SceneManager.LoadSceneAsync(LobbyNumberToSceneNameMap[payload.LobbyNumber]).allowSceneActivation = true; //Don't send anything here, the server will send a 0x60 0x6F after this return(Task.CompletedTask); }
/// <inheritdoc /> public Task HandleMessage(IPeerMessageContext <TOutgoingPayloadType> context, TPayloadType payload) { if (context == null) { throw new ArgumentNullException(nameof(context)); } if (payload == null) { throw new ArgumentNullException(nameof(payload)); } //TODO: We can disconnect if we encounter unknowns or do more indepth logging/decisions if (Logger.IsInfoEnabled) { if (payload is IUnknownPayloadType unk) { Logger.Info(unk.ToString()); } else { Logger.Info($"Recieved unhandled payload of Type: {payload.GetType().Name} Info: {payload}"); } } return(Task.CompletedTask); }
/// <inheritdoc /> public override Task HandleMessage(IPeerMessageContext <GameClientPacketPayload> context, FieldValueUpdateEvent payload) { //Assume the update fields aren't null and there is at least 1 foreach (EntityAssociatedData <FieldValueUpdate> update in payload.FieldValueUpdates) { //TODO: We shouldn't assume we know the entity, but technically we should based on order of server-side events. IEntityDataFieldContainer entityDataContainer = null; try { entityDataContainer = EntityDataContainerMap[update.EntityGuid]; } catch (Exception e) { if (Logger.IsWarnEnabled) { Logger.Warn($"Encountered Entity FieldValueUpdate for Unknown Entity: {update.EntityGuid.EntityType}:{update.EntityGuid.EntityId}. Error: {e.Message}"); } throw; } //We have to lock here otherwise we could encounter race conditions with the //change tracking system. lock (entityDataContainer.SyncObj) foreach (var entry in update.Data.FieldValueUpdateMask .EnumerateSetBitsByIndex() .Zip(update.Data.FieldValueUpdates, (setIndex, value) => new { setIndex, value })) { entityDataContainer.SetFieldValue(entry.setIndex, entry.value); } } return(Task.CompletedTask); }
/// <inheritdoc /> public override Task HandleMessage(IPeerMessageContext <PSOBBGamePacketPayloadClient> context, CharacterTimestampEventPayload payload) { if (Logger.IsInfoEnabled) { Logger.Info($"TimeStamp: {payload.Timestamp}"); } //TODO: How should we account for latency between the server //See: https://en.wikipedia.org/wiki/Swatch_Internet_Time#Calculation_from_UTC.2B1 //Teht: "%u:%02u:%02u: %02u:%02u:%02u.%03u" //rawtime.wYear, rawtime.wMonth, rawtime.wDay, rawtime.wHour, rawtime.wMinute, rawtime.wSecond, rawtime.wMilliseconds //Set the start beat time for use during this session //beats = (UTC+1seconds + (UTC+1minutes * 60) + (UTC+1hours * 3600)) / 86.4 //based on: https://stackoverflow.com/questions/10479991/convert-datetime-to-swatch-internet-time-beat-time TimeService.StartBeatsTime = DateTime .ParseExact(payload.Timestamp, "yyyy:MM:dd: HH:mm:ss.fff", CultureInfo.InvariantCulture) .ToUniversalTime() .AddHours(-4) //we must remove 4 hours to be in sync with the beats the client reads .TimeOfDay .TotalSeconds / 86.4d; if (Logger.IsInfoEnabled) { Logger.Info($"CurrentBeat: {TimeService.StartBeatsTime % 1000}"); } return(Task.CompletedTask); }
/// <inheritdoc /> protected override Task HandleSubMessage(IPeerMessageContext <PSOBBGamePacketPayloadClient> context, Sub60FinishedWarpAckCommand command) { int entityGuid = EntityGuid.ComputeEntityGuid(EntityType.Player, command.Identifier); if (Logger.IsInfoEnabled) { Logger.Info($"Client broadcasted existence Id: {command.Identifier} ZoneId: {command.ZoneId}"); } //The reason we have to do this is because remote players, that we already known about, //could be broadcasting out a warp ack to alert other players that they exist //but not intend for it to reach us really. In this case, we already have the player existing //so if we don't do it this way then we will end up with duplicate spawns if (WorldTransformMappable.ContainsKey(entityGuid) && ZoneDataMappable.ContainsKey(entityGuid)) { //TODO: Should we ever assume they will ack a new zone??? Probably never legit in the lobby but might happen in games? Unsure. InitializeAckDataToEntityMappables(command, entityGuid); } else { HandleUnknownEntityWarpAck(command, entityGuid); } return(Task.CompletedTask); }
/// <inheritdoc /> public override async Task HandleMessage(IPeerMessageContext <PSOBBGamePacketPayloadClient> context, TPayloadType payload) { //We want to grab the subtype, but we don't know how. Let the implementers do it. //then we can dispatch it. await HandleSubMessage(context, RetrieveSubMessage(payload)) .ConfigureAwait(true); }
protected override async Task HandleSubMessage(IPeerMessageContext <PSOBBGamePacketPayloadClient> context, Sub62ClientWarpBeginEventCommand command) { if (Logger.IsInfoEnabled) { Logger.Info($"Recieved: {this.MessageName()} about to create local player."); } INetworkPlayer player = null; try { //TODO: Is this where we should do this? //We need to create the player represenation here player = PlayerFactory.CreateLocalPlayer(); } catch (Exception e) { if (Logger.IsErrorEnabled || Logger.IsFatalEnabled) { Logger.Fatal($"Failed to create network player. Exception: {e.Message} \n\n Stacktrace: {e.StackTrace}"); } throw; } //TODO: Handle the zoneid better await context.PayloadSendService.SendMessage(new Sub60TeleportToPositionCommand(player.Identity.EntityId, ScalerService.UnScale(player.Transform.Position).ToNetworkVector3()).ToPayload()); await context.PayloadSendService.SendMessage(new Sub60WarpToNewAreaCommand(player.Identity.EntityId, 0).ToPayload()); //pioneer 2 await context.PayloadSendService.SendMessage(new Sub60FinishedMapLoadCommand(player.Identity.EntityId).ToPayload()); //This tells everyone else we're doing bursting await context.PayloadSendService.SendMessage(new BlockFinishedGameBurstingRequestPayload()); }
/// <inheritdoc /> protected override async Task HandleSubMessage(IPeerMessageContext <PSOBBGamePacketPayloadClient> context, Sub60FinishedWarpingBurstingCommand command) { if (Logger.IsInfoEnabled) { Logger.Info($"Recieved finished warp from Client: {command.Identifier} SameZone: {command}"); } //TODO: Can we always assume we have a world object when we recieved this?? if (!LocalPlayerData.isWorldObjectSpawned) { throw new InvalidOperationException($"Recieved {nameof(Sub60FinishedWarpingBurstingCommand)} before local player exists."); } Vector3 <float> scaledPosition = ScalingService.UnScale(LocalPlayerData.WorldObject.transform.position).ToNetworkVector3(); float scaledRotation = ScalingService.UnScaleYRotation(LocalPlayerData.WorldObject.transform.rotation.y); //If have to send this message otherwise other client's won't know we're also in the same zone //It's odd, but it's something we have to do. await context.PayloadSendService.SendMessage(new Sub60FinishedWarpAckCommand(LocalPlayerData.SlotIndex, ZoneId, scaledPosition, scaledRotation).ToPayload()); //Other clients send photon char information but I don't know what is in it yet or if it's required await context.PayloadSendService.SendMessage(new Sub62PhotonChairCommand().ToPayload()); int entityGuid = EntityGuid.ComputeEntityGuid(EntityType.Player, command.Identifier); //TODO: Is it really safe to assume that they have zone data?? If they never sent it then this will throw here. Or it'll be stale. //TODO: Should we broadcast this event before or after the warp ack is sent? OnRemotePlayedFinishedWarpToZone?.Invoke(this, new PlayerWarpedToZoneEventArgs(entityGuid, PlayerZoneDataMappable[entityGuid].ZoneId)); }
/// <inheritdoc /> public override Task HandleMessage(IPeerMessageContext <GameClientPacketPayload> context, SpellCastResponsePayload payload) { if (!payload.isSuccessful) { if (Logger.IsDebugEnabled) { Logger.Debug($"Failed to cast Spell: {payload.SpellId} Reason: {payload.Result}"); } return(Task.CompletedTask); } //If successful we should do some prediction long predictedStartTime = TimeService.CurrentRemoteTime - TimeService.CurrentLatency; int spellId = payload.SpellId; //These must be set atomically. lock (PlayerDetails.EntityData.SyncObj) { PlayerDetails.EntityData.SetFieldValue(EntityObjectField.UNIT_FIELD_CASTING_SPELLID, spellId); PlayerDetails.EntityData.SetFieldValue(EntityObjectField.UNIT_FIELD_CASTING_STARTTIME, predictedStartTime); } return(Task.CompletedTask); }
/// <inheritdoc /> public Task HandleMessage(IPeerMessageContext <AuthenticationClientPayload> context, AuthLogonChallengeResponse payload) { AuthLogonProofRequest proof = null; //TODO: Change this console logging if (payload.Result != AuthenticationResult.Success) { Console.WriteLine($"Failed Auth: {payload.Result}"); } using (WoWSRP6ClientCryptoServiceProvider srpProvider = new WoWSRP6ClientCryptoServiceProvider(payload.Challenge.B.ToBigInteger(), payload.Challenge.N.ToBigInteger(), payload.Challenge.g.ToBigInteger())) { using (WoWSRP6PublicComponentHashServiceProvider hashingService = new WoWSRP6PublicComponentHashServiceProvider()) { //TODO: Remove hardcoded name/pass //Set the session key in the store for usage BigInteger unhashedKey = srpProvider.ComputeSessionKey("Glader".ToUpper(), "test", payload.Challenge.salt); Console.WriteLine($"SessionKey: {unhashedKey} KeySize: {unhashedKey.ToCleanByteArray().Length}"); proof = new AuthLogonProofRequest(srpProvider.A.ToCleanByteArray(), hashingService.ComputeSRP6M1(srpProvider.g, srpProvider.N, "Glader".ToUpper(), payload.Challenge.salt, srpProvider.A, srpProvider.B, unhashedKey)); //Set the session key as a hashed session key //SessionKeyStorage.SessionKey = hashingService.HashSessionKey(unhashedKey); } } Console.WriteLine("Sending Proof"); return(context.PayloadSendService.SendMessage(proof)); }
/// <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 /> public override async Task HandleMessage(IPeerMessageContext <GameClientPacketPayload> context, PlayerSelfSpawnEventPayload payload) { //This is kinda a hack, to make this handler continue on the main thread so that dispatching the event //will be on the main thread too. //Otherwise, we'd need a system for queueing such an event on the main thread and NOT continuting the networking until //all events complete. //The reason being as SOON as this happens, something COULD happen the player entity to trigger a value change update. await new UnityYieldAwaitable(); //We should ONLY allow this to be the case for THIS HANDLER ONLY. Doing this in other handlers is NOT VIABLE. //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}"); } LogEntityDataFields(payload); LocalPlayerDetails.LocalPlayerGuid = payload.CreationData.EntityGuid; //We should broadcast to interested parties that spawn event has occured. OnSelfPlayerSpawnEvent?.Invoke(this, new SelfPlayerSpawnEventArgs(payload.CreationData)); //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 /> public override Task HandleMessage(IPeerMessageContext <PSOBBGamePacketPayloadClient> context, SharedLoginResponsePayload payload) { //We don't yet handle the UI for this so we just log it if (Logger.IsInfoEnabled) { Logger.Info($"Recieved LoginResponse: {payload.ResponseCode}"); } if (Logger.IsDebugEnabled) { Logger.Debug($"Tag: {payload.Tag} GuildCard: {payload.GuildCard} TeamId: {payload.TeamId}"); } if (!payload.isSuccessful) { OnLoginFailed?.Invoke(); return(Task.CompletedTask); } //Set the data required to flow through the login process SessionDetails.SessionId = payload.TeamId; SessionDetails.SessionVerificationData = payload.SecurityData; SessionDetails.GuildCardNumber = payload.GuildCard; //Invoke login success if it's succesful at this point. OnLoginSuccess?.Invoke(); return(Task.CompletedTask); }
/// <inheritdoc /> public async Task HandleMessage(IPeerMessageContext <AuthenticationClientPayload> context, AuthLogonChallengeResponse payload) { AuthLogonProofRequest proof = null; if (payload.Result != AuthenticationResult.Success) { throw new InvalidOperationException($"The auth challenge failed. Returned: {payload.Result}."); } using (WoWSRP6CryptoServiceProvider srpProvider = new WoWSRP6CryptoServiceProvider(payload.Challenge.B.ToBigInteger(), payload.Challenge.N.ToBigInteger(), payload.Challenge.g.ToBigInteger())) { using (WoWSRP6PublicComponentHashServiceProvider hashingService = new WoWSRP6PublicComponentHashServiceProvider()) { //TODO: Remove hardcoded name/pass //Set the session key in the store for usage BigInteger unhashedKey = srpProvider.ComputeSessionKey("Glader".ToUpper(), "test", payload.Challenge.salt); proof = new AuthLogonProofRequest(srpProvider.A.ToCleanByteArray(), hashingService.ComputeSRP6M1(srpProvider.g, srpProvider.N, "Glader".ToUpper(), payload.Challenge.salt, srpProvider.A, srpProvider.B, unhashedKey)); //Set the session key as a hashed session key //SessionKeyStorage.SessionKey = hashingService.HashSessionKey(unhashedKey); } } await context.PayloadSendService.SendMessage(proof); }
/// <inheritdoc /> protected override Task HandleSubMessage(IPeerMessageContext <PSOBBGamePacketPayloadClient> context, TPositionChangeCommandType command) { int entityGuid = EntityGuid.ComputeEntityGuid(EntityType.Player, command.Identifier); //TODO: is this the best approach, just ignoring/ditching the position of players //if they aren't in our zone? if (!MovementManagerMappable.ContainsKey(entityGuid)) { return(Task.CompletedTask); } //We can safely assume they have a known world transform or they can't have been spawned. Vector2 position = Scaler.ScaleYasZ(command.Position); MovementManagerMappable[entityGuid].RegisterState(CreateMovementGenerator(position, command)); //New position commands should be direcly updating the entity's position. Even though "MovementGenerators" handle true movement by learping them. //They aren't the source of Truth since they aren't deterministic/authorative like is REAL MMOs. So, the true source of truth is the WorldTransform. Vector3 positionIn3dSpace = new Vector3(position.x, WorldTransformMappable[entityGuid].Position.y, position.y); WorldTransformMappable[entityGuid] = new WorldTransform(positionIn3dSpace, WorldTransformMappable[entityGuid].Rotation); return(Task.CompletedTask); }
/// <inheritdoc /> protected override async Task HandleSubMessage(IPeerMessageContext <PSOBBGamePacketPayloadClient> context, Sub60StartNewWarpCommand command, INetworkPlayerNetworkMessageContext commandContext) { //TODO: We should remove the player's physical representation from the current map if they're in it. if (Logger.IsDebugEnabled) { Logger.Debug($"Player ID: {command.Identifier} starting warp to ZoneId: {command.ZoneId} - Unused: {command.Unused1} {command.Unused2}"); } }
public async Task HandleMessage(IPeerMessageContext <PSOBBPatchPacketPayloadClient> context, PatchingWelcomePayload payload) { //We need to init the crypto before we can even send the following payload Initializers.DecryptionInitializable.Initialize(payload.ServerVector); Initializers.EncryptionInitializable.Initialize(payload.ClientVector); //Sends an ack to let the patch server know we recieved the welcome and that we've established encryption. await context.PayloadSendService.SendMessage(new PatchingWelcomeAckPayload()); }
/// <inheritdoc /> protected override Task HandleSubMessage(IPeerMessageContext <PSOBBGamePacketPayloadClient> context, Sub60WarpToNewAreaCommand command) { //All we need to do is set the new zone for lobby. //We should not assume that they are ever going to leave in the lobby //so don't remove them even if it appears they're going to a different map/area //that the local player is not in. ZoneDataMappable[EntityGuid.ComputeEntityGuid(EntityType.Player, command.Identifier)] = new PlayerZoneData(command.Zone); return(Task.CompletedTask); }
/// <inheritdoc /> public Task HandleMessage(IPeerMessageContext <GameClientPacketPayload> context, GameServerPacketPayload payload) { if (Logger.IsWarnEnabled) { Logger.Warn($"Recieved unhandable Payload: {payload.GetType().Name}"); } return(Task.CompletedTask); }
/// <inheritdoc /> protected override Task HandleSubMessage(IPeerMessageContext <PSOBBGamePacketPayloadClient> context, Sub60PlayerFreezeCommand command) { if (this.Logger.IsDebugEnabled) { Logger.Debug($"Freeze ClientId: {command.Identifier} Unknown: {command.Unknown1} Type: {command.Reason} Location1: {command.Position} Unknown2: {command.Unknown2}"); } return(Task.CompletedTask); }
/// <inheritdoc /> protected override Task HandleMessage(IPeerMessageContext <PSOBBGamePacketPayloadClient> context, BlockTextChatMessageEventPayload payload, INetworkPlayerNetworkMessageContext payloadContext) { if (Logger.IsInfoEnabled) { Logger.Info($"Recieved Chat ClientId: {payloadContext.RemotePlayer.Identity.EntityId} GCN: {payload.GuildCardNumber} Message: {payload.ChatMessage.Aggregate("", (s, b) => $"{s} {b}")}"); } return(Task.CompletedTask); }
public override async Task HandleMessage(IPeerMessageContext <GameClientPacketPayload> context, PlayerSelfSpawnEventPayload payload) { NetworkEntityNowVisibleEventArgs visibilityEvent = VisibileEventFactory.Create(payload.CreationData); VisibilityEventPublisher.Publish(visibilityEvent); //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)); }
public override Task HandleMessage(IPeerMessageContext <GameClientPacketPayload> context, FieldValueUpdateEvent payload) { foreach (var entry in payload.FieldValueUpdates) { GenerateFieldUpdateDiff(entry.Data, ChangeTrackableCollection.RetrieveEntity(entry.EntityGuid)); } return(Task.CompletedTask); }
/// <inheritdoc /> protected override Task HandleSubMessage(IPeerMessageContext <PSOBBGamePacketPayloadClient> context, Sub60MovingSlowPositionSetCommand command, INetworkPlayerNetworkMessageContext commandContext) { Vector2 position = Scaler.ScaleYasZ(command.Position); //Set the position of the network transform commandContext.RemotePlayer.Transform.Position = new Vector3(position.x, commandContext.RemotePlayer.Transform.Position.y, position.y); return(Task.CompletedTask); }
//TODO: This is a work in progress, we need a time service. /// <inheritdoc /> public override Task HandleMessage(IPeerMessageContext <GameClientPacketPayload> context, ServerPingPacketPayload payload) { //The issue with time sync every ping is that the remote packet queue could contain a bunch of inputs from rotation/movement //which will skew the time sync result. Best to just go with the initial/original time syncronization. //context.PayloadSendService.SendMessageImmediately(new ServerTimeSyncronizationRequestPayload(DateTime.UtcNow.Ticks)) // .ConfigureAwaitFalse(); return(Task.CompletedTask); }