/// <inheritdoc /> public override async Task HandleMessage(IPeerSessionMessageContext <GameServerPacketPayload> context, ServerTimeSyncronizationRequestPayload payload) { //TODO: Is ticks best? Or Unity3D deltatime since startup? Or Enviroment ticks? //TODO: Do we need to store the time diff? To track latency serverside for some reason? await context.PayloadSendService.SendMessage(new ServerTimeSyncronizationResponsePayload(payload.CurrentLocalTime, DateTime.UtcNow.Ticks)) .ConfigureAwait(false); }
/// <inheritdoc /> protected override Task HandleMessage(IPeerSessionMessageContext <GameServerPacketPayload> context, ClientRotationDataUpdateRequest payload, NetworkEntityGuid guid) { try { IMovementGenerator <GameObject> generator = MovementGenerator.RetrieveEntity(guid); IMovementData movementData = MovementDataMap.RetrieveEntity(guid); //TODO: This is a temporary hack, we nee d abetter solluition if (movementData is PositionChangeMovementData posChangeMoveDat) { Vector2 direction = posChangeMoveDat.Direction; //TODO: Sanity check position sent. //TODO: Sanity check timestamp MovementDataMap.ReplaceObject(guid, new PositionChangeMovementData(payload.TimeStamp, payload.ClientCurrentPosition, direction, payload.Rotation)); } else { throw new NotImplementedException($"TODO: Implement rotation when dealing with: {movementData.GetType().Name} type movement."); } OnPlayerRotationChanged?.Invoke(this, new PlayerRotiationChangeEventArgs(guid, payload.Rotation)); } catch (Exception e) { if (Logger.IsErrorEnabled) { Logger.Error($"Failed to update MovementData for GUID: {guid} Reason: {e.Message}"); } throw; } return(Task.CompletedTask); }
/// <inheritdoc /> protected override Task HandleMessage(IPeerSessionMessageContext <GameServerPacketPayload> context, ClientMovementDataUpdateRequest payload, NetworkEntityGuid guid) { try { IMovementGenerator <GameObject> generator = MovementGenerator.RetrieveEntity(guid); IMovementData movementData = MovementDataMap.RetrieveEntity(guid); PositionChangeMovementData changeMovementData = BuildPositionChangeMovementData(payload, generator, movementData); MovementDataMap.ReplaceObject(guid, changeMovementData); IActorRef playerActorRef = ActorReferenceMappable.RetrieveEntity(guid); playerActorRef.TellSelf(new PlayerMovementStateChangedMessage(changeMovementData.Direction)); //If the generator is running, we should use its initial position instead of the last movement data's position. MovementGenerator.ReplaceObject(guid, BuildCharacterControllerMovementGenerator(guid, changeMovementData, generator, movementData)); } catch (Exception e) { if (Logger.IsErrorEnabled) { Logger.Error($"Failed to update MovementData for GUID: {guid} Reason: {e.Message}"); } throw; } return(Task.CompletedTask); }
protected override Task HandleMessage(IPeerSessionMessageContext <GameServerPacketPayload> context, SpellCastRequestPayload payload, NetworkEntityGuid guid) { IActorRef entityActor = ActorReferenceMappable.RetrieveEntity(guid); entityActor.TellSelf(new TryCastSpellMessage(payload)); return(Task.CompletedTask); }
/// <inheritdoc /> public Task HandleMessage(IPeerSessionMessageContext <AuthenticationServerPayload> context, AuthenticationClientPayload payload) { if (Logger.IsWarnEnabled) { Logger.Warn($"Recieved unhandable Payload: {payload.GetType().Name} ConnectionId: {context.Details.ConnectionId}"); } return(Task.CompletedTask); }
/// <inheritdoc /> public async Task <bool> TryHandleMessage(IPeerSessionMessageContext <GameServerPacketPayload> context, NetworkIncomingMessage <GameClientPacketPayload> message) { if (CanHandle(message)) { await HandleMessage(context, message.Payload as TSpecificPayloadType); return(true); } return(false); }
public override async Task HandleMessage(IPeerSessionMessageContext <BaseGameServerPayload> context, ClientWalkMovementRequestPayload payload) { Console.WriteLine($"Recieved Path with Length: {payload.PathPoints.Length}"); foreach (var v in payload.PathPoints) { Console.WriteLine($"X: {v.X} Y: {v.Y}"); } //await context.PayloadSendService.SendMessage(new ServerUpdateLocalPlayerPayload(payload.PathPoints.Last())); }
/// <inheritdoc /> public override async Task HandleMessage(IPeerSessionMessageContext <GameServerPacketPayload> context, ClientSessionClaimRequestPayload payload) { //TODO: We need better validation/authorization for clients trying to claim a session. Right now it's open to malicious attack ZoneServerTryClaimSessionResponse zoneServerTryClaimSessionResponse = null; try { ProjectVersionStage.AssertAlpha(); zoneServerTryClaimSessionResponse = await GameServerClient.TryClaimSession(new ZoneServerTryClaimSessionRequest(await GameServerClient.GetAccountIdFromToken(payload.JWT), payload.CharacterId)) .ConfigureAwait(false); } catch (Exception e) //we could get an unauthorized response { Logger.Error($"Failed to Query for AccountId: {e.Message}. AuthToken provided was: {payload.JWT}"); throw; } if (!zoneServerTryClaimSessionResponse.isSuccessful) { //TODO: Better error code await context.PayloadSendService.SendMessage(new ClientSessionClaimResponsePayload(ClientSessionClaimResponseCode.SessionUnavailable)) .ConfigureAwait(false); return; } NetworkEntityGuidBuilder builder = new NetworkEntityGuidBuilder(); builder .WithId(payload.CharacterId) .WithType(EntityType.Player); //TODO: We assume they are authenticated, we don't check at the moment but we WILL and SHOULD. Just load their location. ZoneServerCharacterLocationResponse locationResponse = await GameServerClient.GetCharacterLocation(payload.CharacterId) .ConfigureAwait(false); Vector3 position = locationResponse.isSuccessful ? locationResponse.Position : Vector3.zero; if (Logger.IsDebugEnabled) { Logger.Debug($"Recieved player location: {position}"); } //Just broadcast successful claim, let listeners figure out what to do with this information. OnSuccessfulSessionClaimed?.Invoke(this, new PlayerSessionClaimedEventArgs(builder.Build(), position, new PlayerEntitySessionContext(context.PayloadSendService, context.Details.ConnectionId))); await context.PayloadSendService.SendMessage(new ClientSessionClaimResponsePayload(ClientSessionClaimResponseCode.Success)) .ConfigureAwait(false); //TODO: We shouldn't hardcode this, we should send the correct scene specified by the gameserver this zone/instance connects to to service. await context.PayloadSendService.SendMessage(new LoadNewSceneEventPayload(PlayableGameScene.LobbyType1)) .ConfigureAwait(false); }
public override async Task HandleMessage(IPeerSessionMessageContext <BaseGameServerPayload> context, ClientSessionClaimRequestPayload payload) { //params never null //TODO: Implement authentication with the payload's JWT. //If successful, we should then queue them up for creation on the simulation thread. if (Logger.IsDebugEnabled) { Logger.Debug($"Recieved SessionClaim: {payload.JWT}"); } if (Logger.IsDebugEnabled) { Logger.Debug($"Sending Welcome Packet."); } //TODO: This is just demo code, not how it should be handled. //208 await context.PayloadSendService.SendMessage(new ServerSetClientWalkableInterfacePayload(-1)); //Right-click player options for (int i = 0; i < 3; i++) { await context.PayloadSendService.SendMessage(new ServerSetPlayerRightClickOptionsPayload((byte)(i + 1), false, "Test")); } //249 await context.PayloadSendService.SendMessage(new ServerSetLocalPlayerNetworkStatusPayload(true, 1)); //206 await context.PayloadSendService.SendMessage(new ServerSetChatModeStatusPayload(ChatModeType.On, ChatModeType.On, ChatModeType.On)); //107 await context.PayloadSendService.SendMessage(new ServerResetLocalCameraPayload()); //68 await context.PayloadSendService.SendMessage(new ServerResetInterfaceButtonStatePayload()); await SendTabInterfaces(context.PayloadSendService); //176 await context.PayloadSendService.SendMessage(new ServerWelcomeMessagePacketPayload(5, 10, 5, 69)); //73 await context.PayloadSendService.SendMessage(new ServerSetClientRegionPayload(402, 402)); //Stub await context.PayloadSendService.SendMessage(new ServerUpdateLocalPlayerPayload(new Vector2 <short>(50, 50))); }
/// <inheritdoc /> protected override Task HandleMessage(IPeerSessionMessageContext <GameServerPacketPayload> context, PlayerModelChangeRequestPayload payload, NetworkEntityGuid guid) { //At this point, the player wants to change his model. //However we really can't be sure it's a valid model //so we validate it before setting the model id. //TODO: Validate the model id. ProjectVersionStage.AssertAlpha(); IEntityDataFieldContainer entityDataFieldContainer = EntityFieldMap.RetrieveEntity(guid); //This change will be broadcast to anyone interested. entityDataFieldContainer.SetFieldValue(BaseObjectField.UNIT_FIELD_DISPLAYID, payload.ModelId); return(Task.CompletedTask); }
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 Task HandleMessage(IPeerSessionMessageContext <GameServerPacketPayload> context, ClientMovementDataUpdateRequest payload, NetworkEntityGuid guid) { try { MovementGenerator[guid] = new ServerPlayerInputChangeMovementGenerator(payload.MovementInput, data => MovementDataMap[guid] = data, CharacterControllerMappable[guid]); } catch (Exception e) { if (Logger.IsErrorEnabled) { Logger.Error($"Failed to update MovementData for GUID: {guid} Reason: {e.Message}"); } throw; } return(Task.CompletedTask); }
/// <inheritdoc /> protected override Task HandleMessage(IPeerSessionMessageContext <GameServerPacketPayload> context, ClientInteractNetworkedObjectRequestPayload payload, NetworkEntityGuid guid) { //Special case here that indicates the client wants to clear their target. if (payload.TargetObjectGuid == NetworkEntityGuid.Empty) { IActorRef playerRef = ActorReferenceMappable.RetrieveEntity(guid); playerRef.Tell(new SetEntityActorTargetMessage(payload.TargetObjectGuid)); //Just send the empty set target to the player entity return(Task.CompletedTask); } if (!ActorReferenceMappable.ContainsKey(payload.TargetObjectGuid)) { if (Logger.IsWarnEnabled) { Logger.Warn($"Client: {guid} attempted to interact with unknown actor {payload.TargetObjectGuid}."); } } else { IActorRef interactable = ActorReferenceMappable.RetrieveEntity(payload.TargetObjectGuid); IActorRef playerRef = ActorReferenceMappable.RetrieveEntity(guid); switch (payload.InteractionType) { case ClientInteractNetworkedObjectRequestPayload.InteractType.Interaction: //Important to indicate that the player itself is sending it. interactable.Tell(new InteractWithEntityActorMessage(guid), playerRef); break; case ClientInteractNetworkedObjectRequestPayload.InteractType.Selection: //TODO: This can technically cause a race condition for target selection if client selects too fast. interactable.Tell(new EntityActorSelectedMessage(guid), playerRef); break; default: throw new ArgumentOutOfRangeException($"Client used unknown interaction Type: {(int)payload.InteractionType}"); } } return(Task.CompletedTask); }
/// <inheritdoc /> public async Task HandleMessage(IPeerSessionMessageContext <AuthenticationServerPayload> context, AuthLogonChallengeRequest payload) { using (var repo = AccountRepository.Create()) { bool accountExists = await repo.DoesAccountExists(payload.Identity) .ConfigureAwait(false); if (Logger.IsDebugEnabled) { Logger.Debug($"Account: {payload.Identity} Exists: {accountExists} ConnectionId: {context.Details.ConnectionId}"); } //Depending on if it exists we build an appropriate response AuthLogonChallengeResponse response = accountExists ? await BuildSuccessResponse(await repo.GetAccount(payload.Identity).ConfigureAwait(false), context.Details.ConnectionId) : BuildFailureResponse(); await context.PayloadSendService.SendMessage(response) .ConfigureAwait(false); } }
/// <inheritdoc /> public async Task HandleMessage(IPeerSessionMessageContext <AuthenticationServerPayload> context, AuthRealmListRequest payload) { if (Logger.IsDebugEnabled) { Logger.Debug($"Recieved {payload.GetType().Name} From ConnectionId: {context.Details.ConnectionId}"); } //All we need to do when we recieve this request //is send back a list of requests. It doesn't even technically matter if they're authenticated //or if they even provided the challenge/proof and failed. using (IAuthenticationRealmRepository realmService = RealmListService.Create()) { IEnumerable <Realmlist> realms = await realmService.Retrieve(); if (Logger.IsDebugEnabled) { Logger.Debug($"Recieved RealmList repository response."); } //TODO: Implement complete realm info handling; right now only uses default //TODO: Better handle handle other realm types. Resitrcted, dev, test or locked types. RealmInfo[] realmInfos = realms .Select(BuildRealmInfoFromModel) .ToArray(); RealmListContainer realmListContainer = new RealmListContainer(realmInfos); byte[] realmData = Serializer.Serialize(realmListContainer); AuthRealmListResponse response = new AuthRealmListResponse((ushort)(realmData.Length + 6), realmInfos); if (Logger.IsDebugEnabled) { Logger.Debug($"Sending {typeof(AuthRealmListResponse).Name} PayloadSize: {response.PayloadSize}."); } await context.PayloadSendService.SendMessage(response); } }
/// <inheritdoc /> protected override Task HandleMessage(IPeerSessionMessageContext <GameServerPacketPayload> context, ClientSetClickToMovePathRequestPayload payload, NetworkEntityGuid guid) { try { IMovementGenerator <GameObject> generator = MovementGenerator.RetrieveEntity(guid); IMovementData movementData = MovementDataMap.RetrieveEntity(guid); PathBasedMovementData changeMovementData = BuildPathData(payload, generator, movementData, guid); //If it doesn't have more one point reject it if (changeMovementData.MovementPath.Count < 2) { return(Task.CompletedTask); } MovementDataMap.ReplaceObject(guid, changeMovementData); IActorRef playerActorRef = ActorReferenceMappable.RetrieveEntity(guid); Vector3 direction3D = (changeMovementData.MovementPath[1] - changeMovementData.MovementPath[0]); Vector2 direction2D = new Vector2(direction3D.x, direction3D.z).normalized; playerActorRef.TellSelf(new PlayerMovementStateChangedMessage(direction2D)); //If the generator is running, we should use its initial position instead of the last movement data's position. MovementGenerator.ReplaceObject(guid, new PathMovementGenerator(changeMovementData)); } catch (Exception e) { if (Logger.IsErrorEnabled) { Logger.Error($"Failed to update MovementData for GUID: {guid} Reason: {e.Message}"); } throw; } return(Task.CompletedTask); }
/// <inheritdoc /> protected sealed override NetworkEntityGuid GenerateLockContext(IPeerSessionMessageContext <GameServerPacketPayload> context, TSpecificPayloadType payload) { //We just grab the entity, and assume that they want this. return(ExtractEntityGuidFromContext(context)); }
/// <inheritdoc /> public abstract Task HandleMessage(IPeerSessionMessageContext <BaseGameServerPayload> context, TSpecificPayloadType payload);
/// <inheritdoc /> public override async Task HandleMessage(IPeerSessionMessageContext <GameServerPacketPayload> context, ClientSessionClaimRequestPayload payload) { //TODO: We need better validation/authorization for clients trying to claim a session. Right now it's open to malicious attack ZoneServerTryClaimSessionResponse zoneServerTryClaimSessionResponse = null; try { ProjectVersionStage.AssertAlpha(); zoneServerTryClaimSessionResponse = await GameServerClient.TryClaimSession(new ZoneServerTryClaimSessionRequest(await GameServerClient.GetAccountIdFromToken(payload.JWT), payload.CharacterId)) .ConfigureAwaitFalse(); } catch (Exception e) //we could get an unauthorized response { Logger.Error($"Failed to Query for AccountId: {e.Message}. AuthToken provided was: {payload.JWT}"); throw; } if (!zoneServerTryClaimSessionResponse.isSuccessful) { if (Logger.IsWarnEnabled) { Logger.Warn($"Client attempted to claim session for Character: {payload.CharacterId} but was denied."); } //TODO: Better error code await context.PayloadSendService.SendMessage(new ClientSessionClaimResponsePayload(ClientSessionClaimResponseCode.SessionUnavailable)) .ConfigureAwaitFalse(); return; } NetworkEntityGuid entityGuid = new NetworkEntityGuidBuilder() .WithId(payload.CharacterId) .WithType(EntityType.Player) .Build(); //TODO: We assume they are authenticated, we don't check at the moment but we WILL and SHOULD. Just load their location. ZoneServerCharacterLocationResponse locationResponse = await GameServerClient.GetCharacterLocation(payload.CharacterId) .ConfigureAwaitFalse(); Vector3 position = locationResponse.isSuccessful ? locationResponse.Position : SpawnPointProvider.GetSpawnPoint().WorldPosition; SpawnPointData pointData = new SpawnPointData(position, Quaternion.identity); if (Logger.IsDebugEnabled) { Logger.Debug($"Recieved player location: {pointData.WorldPosition} from {(locationResponse.isSuccessful ? "Database" : "Spawnpoint")}"); } //TODO: We need a cleaner/better way to load initial player data. ResponseModel <CharacterDataInstance, CharacterDataQueryReponseCode> characterData = await CharacterService.GetCharacterData(payload.CharacterId); //TODO: Check success. InitialCharacterDataMappable.AddObject(entityGuid, characterData.Result); //Just broadcast successful claim, let listeners figure out what to do with this information. OnSuccessfulSessionClaimed?.Invoke(this, new PlayerSessionClaimedEventArgs(entityGuid, pointData.WorldPosition, new PlayerEntitySessionContext(context.PayloadSendService, context.Details.ConnectionId, context.ConnectionService))); await context.PayloadSendService.SendMessage(new ClientSessionClaimResponsePayload(ClientSessionClaimResponseCode.Success)) .ConfigureAwaitFalse(); }
/// <inheritdoc /> public async Task HandleMessage(IPeerSessionMessageContext <AuthenticationServerPayload> context, AuthLogonProofRequest payload) { if (Logger.IsDebugEnabled) { Logger.Debug($"Recieved {payload.GetType().Name} Id: {context.Details.ConnectionId}"); } BigInteger A = payload.A.ToBigInteger(); //SRP6 Safeguard from specification: A % N cannot be 0. if (A % WoWSRP6ServerCryptoServiceProvider.N == 0) { //TODO: This can't be 0 or it breaks } //Check if we have a challenge entry for this connection //TODO: Add proper response code if (!await ChallengeRepository.HasEntry(context.Details.ConnectionId)) { await context.PayloadSendService.SendMessage(new AuthLogonProofResponse(new LogonProofFailure())); return; } //TODO: Refactor using (WoWSRP6PublicComponentHashServiceProvider hasher = new WoWSRP6PublicComponentHashServiceProvider()) { AuthenticationChallengeModel challenge = await ChallengeRepository.Retrieve(context.Details.ConnectionId); byte[] hash = hasher.Hash(A, challenge.PublicB); // Both: u = H(A, B) BigInteger u = hash .Take(20) .ToArray() .ToBigInteger(); BigInteger N = WoWSRP6ServerCryptoServiceProvider.N; //Host: S = (Av^u) ^ b (computes session key) BigInteger S = ((A * (challenge.V.ModPow(u, N)))) //TODO: Do we need % N here? .ModPow(challenge.PrivateB, N); //Host: K = H(S) BigInteger K = hasher.HashSessionKey(S); byte[] preMHash = T3.ToCleanByteArray() .Concat(hasher.Hash(challenge.Identity.Reinterpret(Encoding.ASCII)).Take(20).ToArray()) .Concat(challenge.Salt.ToCleanByteArray()) .Concat(payload.A) .Concat(challenge.PublicB.ToCleanByteArray()) .Concat(K.ToCleanByteArray()) .ToArray(); byte[] M = hasher.Hash(preMHash); Logger.Debug($"M: {M.Aggregate("", (s, b) => $"{s} {b}")}"); Logger.Debug($"M1: {payload.M1.Aggregate("", (s, b) => $"{s} {b}")}"); //TODO: Remove this test code if (TestClass.CompareBuffers(M, payload.M1, 20) == 0) { Logger.Debug($"Auth Proof Success."); byte[] M2 = hasher.Hash(payload.A.Concat(M).Concat(K.ToCleanByteArray()).ToArray()); //TODO: We also want to update last/login IP and other stuff await AccountRepository.UpdateSessionKey(challenge.AccountId, K); await context.PayloadSendService.SendMessage(new AuthLogonProofResponse(new LogonProofSuccess(M2))); } else { Logger.Debug($"Auth Proof Failure."); await context.PayloadSendService.SendMessage(new AuthLogonProofResponse(new LogonProofFailure())); } } //TODO: Implement }