/// <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);
        }
Example #2
0
        /// <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 />
        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 void Tick()
        {
            //Don't need to do this if we aren't moving
            if (isMoving)
            {
                using (SyncObj.Lock())
                {
                    //Double check locking
                    if (!isMoving)
                    {
                        return;
                    }

                    GameObject worldObject = WorldObjectMap[EntityGuid.ComputeEntityGuid(EntityType.Player, SlotModel.SlotSelected)];

                    //From old movement
                    //Vector3.Magnitude(lastPosition - transform.position) > Vector3.kEpsilon
                    if (Vector3.Magnitude(LastPosition - worldObject.transform.position) > Vector3.kEpsilon)
                    {
                        LocalPlayerNetworkController.UpdatedMovementLocation(worldObject.transform.position, worldObject.transform.rotation);

                        //TODO: This design is such that the above statement will be true the first time around. This could be bad for the first time the player moves
                        LastPosition = worldObject.transform.position;
                    }
                }
            }
        }
 /// <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);
 }
Example #6
0
        /// <inheritdoc />
        protected override Task HandleSubMessage(IPeerMessageContext <PSOBBGamePacketPayloadClient> context, Sub60WarpToNewAreaCommand command)
        {
            //For games, we only set zone data in this handler if they are bursting right now.
            if (BurstingService.isBurstingInProgress && BurstingService.BurstingEntityGuid.Value == EntityGuid.ComputeEntityGuid(EntityType.Player, command.Identifier))
            {
                ZoneDataMappable[EntityGuid.ComputeEntityGuid(EntityType.Player, command.Identifier)] = new PlayerZoneData(command.Zone);
            }

            return(Task.CompletedTask);
        }
Example #7
0
        /// <inheritdoc />
        public override async Task HandleMessage(IPeerMessageContext <PSOBBGamePacketPayloadClient> context, BlockGamePlayerJoinedEventPayload payload)
        {
            //When this join is recieved, then we have to set the bursting state so it can be remembered, referenced or cleaned up.
            if (BurstingService.SetBurstingEntity(EntityGuid.ComputeEntityGuid(EntityType.Player, payload.Identifier)))
            {
                //TODO: We are creating a fake 0x6D 0x70 here. Do we ever need a real one??
                await context.PayloadSendService.SendMessage(new BlockNetworkCommand6DEventClientPayload(payload.Identifier, new Sub6DFakePlayerJoinDataNeededCommand(SlotModel.SlotSelected)));
            }

            //TODO: What do we do if this fails?
        }
Example #8
0
        /// <inheritdoc />
        public void Tick()
        {
            float ver = CurrentMovementArgs.NewVerticalInput;
            float hor = CurrentMovementArgs.NewHorizontalInput;

            GameObject player = GuidToWorldObjectMappable[EntityGuid.ComputeEntityGuid(EntityType.Player, SlotModel.SlotSelected)];

            player.transform.position += player.transform.forward * ver * Speed * Time.deltaTime + player.transform.right * hor * Speed * Time.deltaTime;

            //TODO: Handle rotation again someday

            /*if(Input.GetKey(KeyCode.E))
             *      transform.rotation = Quaternion.AngleAxis(transform.eulerAngles.y + Speed * Time.deltaTime * 10.0f, Vector3.up);
             * else if(Input.GetKey(KeyCode.Q))
             *      transform.rotation = Quaternion.AngleAxis(transform.eulerAngles.y + -Speed * Time.deltaTime * 10.0f, Vector3.up);*/
        }
        /// <inheritdoc />
        protected override Task HandleSubMessage(IPeerMessageContext <PSOBBGamePacketPayloadClient> context, Sub60StartNewWarpCommand command)
        {
            //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}");
            }

            //Even if we don't know about the entity, and have never seen it, let's assume that we will or need this data
            //and store/override the existing zone data in the ZoneMappable.

            int entityGuid = EntityGuid.ComputeEntityGuid(EntityType.Player, command.Identifier);

            ZoneDataMappable[entityGuid] = new PlayerZoneData(command.ZoneId);

            return(Task.CompletedTask);
        }
Example #10
0
        /// <inheritdoc />
        protected override void OnEventFired(object source, EventArgs args)
        {
            //This just spawns the player by grabbing a spawn point
            //and passing it to the local player factory.
            Transform spawnPoint = SpawnStrategy.GetSpawnpoint();

            int entityGuid = EntityGuid.ComputeEntityGuid(EntityType.Player, SlotIndex.SlotSelected);

            GameObject gameObject = LocalPlayerWorldRepresentationFactory.Create(new LocalPlayerWorldRepresentationCreationContext(entityGuid, spawnPoint));

            if (Logger.IsInfoEnabled)
            {
                Logger.Info($"Created LocalPlayer's World Object. Dispatching {nameof(OnLocalPlayerWorldObjectCreated)}");
            }


            OnLocalPlayerWorldObjectCreated?.Invoke(this, new LocalPlayerWorldObjectSpawnedEventArgs(entityGuid, gameObject));
        }
        /// <inheritdoc />
        protected override Task HandleSubMessage(IPeerMessageContext <PSOBBGamePacketPayloadClient> context, Sub60TeleportToPositionCommand command)
        {
            //TODO: Don't do anything with this
            if (Logger.IsInfoEnabled)
            {
                Logger.Info($"Recieved {nameof(Sub60TeleportToPositionCommand)} ClientId: {command.Identifier} X: {command.Position.X} Y: {command.Position.Y} Z: {command.Position.Z}");
            }

            int entityGuid = EntityGuid.ComputeEntityGuid(EntityType.Player, command.Identifier);

            //For this packet, we just directly set the world transform
            //Clients seem to set this when they're loading into a new area
            //or maybe to teleport too from teleports
            //TODO: Is this good enough, what about teleporters??
            EntityLocationMappable[entityGuid] = new WorldTransform(ScalerService.Scale(command.Position), Quaternion.identity);

            return(Task.CompletedTask);
        }
        /// <inheritdoc />
        protected override Task HandleSubMessage(IPeerMessageContext <PSOBBGamePacketPayloadClient> context, Sub60ClientZoneTeleportingEventCommand command)
        {
            //This is one of the cases where we need to remove ONLY
            //the world representation of the remote player.
            //They're still in the game, so leave other data untouched,
            //but their world representation needs to be cleared.

            int entityGuid = EntityGuid.ComputeEntityGuid(EntityType.Player, command.Identifier);

            MovementMappable.Remove(entityGuid);

            if (WorldObjectMappable.ContainsKey(entityGuid))
            {
                GameObject.Destroy(WorldObjectMappable[entityGuid]);
            }

            WorldObjectMappable.Remove(entityGuid);

            return(Task.CompletedTask);
        }
        /// <inheritdoc />
        protected override async void OnEventFired(object source, MovementInputChangedEventArgs args)
        {
            //State doesn't need to change if this is true
            //but it SHOULDN'T be true, as the event should not be
            //broadcasted
            if (CurrentMovementArgs == args)
            {
                return;
            }

            //It's ok to capture the sync context here, we'll need it to touch the GameObject
            //and the Transform.
            using (var l = await SyncObj.LockAsync())
            {
                //If the input says NOT MOVING but we are MOVING
                //then we need to end the movement and then send a movement finished command
                if (!args.isMoving && isMoving)
                {
                    GameObject worldObject = WorldObjectMap[EntityGuid.ComputeEntityGuid(EntityType.Player, SlotModel.SlotSelected)];

                    //It's important that this doesn't capture the sync context otheriwse it could block Update from finishing (causing deadlock)
                    //since it needs to wait on SyncContext to be serviced by Unity3D in fixed update. It's CRITICAL that we do NOT block on the main thread
                    //or such a deadlock will occur.
                    await LocalPlayerNetworkController.StopMovementAsync(worldObject.transform.position, worldObject.transform.rotation)
                    .ConfigureAwait(false);

                    //We tell the controller to stop moving and we set our movement here to false.
                    isMoving = false;
                }
                else
                {
                    isMoving = true;                     //it probably already is true but it doesn't hurt to set it again.
                }
                CurrentMovementArgs = args;
            }
        }
        /// <inheritdoc />
        public void Tick()
        {
            if (currentTimeSince < timeSendInterval)
            {
                currentTimeSince += Time.deltaTime;
                return;
            }

            //Don't need to do this if we aren't moving
            if (isMoving)
            {
                using (SyncObj.Lock())
                {
                    //Only reset if we're actually moving. Want to broadcast right away.
                    currentTimeSince = 0.0f;

                    //Double check locking
                    if (!isMoving)
                    {
                        return;
                    }

                    GameObject worldObject = WorldObjectMap[EntityGuid.ComputeEntityGuid(EntityType.Player, SlotModel.SlotSelected)];

                    //From old movement
                    //Vector3.Magnitude(lastPosition - transform.position) > Vector3.kEpsilon
                    if (Vector3.Magnitude(LastPosition - worldObject.transform.position) > Vector3.kEpsilon)
                    {
                        LocalPlayerNetworkController.UpdatedMovementLocation(worldObject.transform.position, worldObject.transform.rotation);

                        //TODO: This design is such that the above statement will be true the first time around. This could be bad for the first time the player moves
                        LastPosition = worldObject.transform.position;
                    }
                }
            }
        }
        /// <inheritdoc />
        public override Task HandleMessage(IPeerMessageContext <PSOBBGamePacketPayloadClient> context, BlockLobbyJoinEventPayload payload)
        {
            if (Logger.IsDebugEnabled)
            {
                Logger.Debug($"**Handling**: {nameof(BlockLobbyJoinEventPayload)}");
            }

            //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;

            OnLocalPlayerLobbyJoined?.Invoke(this, new LobbyJoinedEventArgs(payload.LobbyNumber, EntityGuid.ComputeEntityGuid(EntityType.Player, payload.ClientId)));

            //Don't send anything here, the server will send a 0x60 0x6F after this
            return(Task.CompletedTask);
        }
 /// <inheritdoc />
 public Task OnGameInitialized()
 {
     ZoneDataMappable[EntityGuid.ComputeEntityGuid(EntityType.Player, SlotModel.SlotSelected)] = new PlayerZoneData(ZoneSettings.ZoneId);
     return(Task.CompletedTask);
 }
        /// <inheritdoc />
        public override Task HandleMessage(IPeerMessageContext <PSOBBGamePacketPayloadClient> context, BlockOtherPlayerLeaveGameEventPayload payload)
        {
            if (Logger.IsInfoEnabled)
            {
                Logger.Warn($"Recieved Player GameLeave From EntityId: {payload.Identifier}.");
            }

            //We should just broadcast that a player left the lobby.
            OnRemotePlayerLeftLobby?.Invoke(this, new RemotePlayerLeaveLobbyEventArgs(EntityGuid.ComputeEntityGuid(EntityType.Player, payload.Identifier)));

            return(Task.CompletedTask);
        }
        /// <inheritdoc />
        public override Task HandleMessage(IPeerMessageContext <PSOBBGamePacketPayloadClient> context, BlockOtherPlayerLeaveLobbyEventPayload payload)
        {
            if (Logger.IsInfoEnabled)
            {
                Logger.Info($"Player with Id: {payload.ClientId} left Lobby. CurrentLeader: {payload.LeaderId}");
            }

            //TODO: So, we don't really want to do this INPLACE. But we're kinda forced to remove players INPLACE. Because of Sega's index/id design
            //we can't queue it up to be handled at another time becuase a new player WITH THAT ID, might join. So dumb but can't be avoided.
            OnRemotePlayerLeftLobby?.Invoke(this, new RemotePlayerLeaveLobbyEventArgs(EntityGuid.ComputeEntityGuid(EntityType.Player, payload.ClientId)));

            return(Task.CompletedTask);
        }