private async ValueTask <GameJoinResult> AddClientSafeAsync(ClientBase client) { // Check if the IP of the player is banned. if (client.Connection != null && _bannedIps.Contains(client.Connection.EndPoint.Address)) { return(GameJoinResult.FromError(GameJoinError.Banned)); } var player = client.Player; // Check if; // - The player is already in this game. // - The game is full. if (player?.Game != this && _players.Count >= Options.MaxPlayers) { return(GameJoinResult.FromError(GameJoinError.GameFull)); } if (GameState == GameStates.Starting || GameState == GameStates.Started) { return(GameJoinResult.FromError(GameJoinError.GameStarted)); } if (GameState == GameStates.Destroyed) { return(GameJoinResult.FromError(GameJoinError.GameDestroyed)); } var isNew = false; if (player == null || player.Game != this) { var clientPlayer = new ClientPlayer(_serviceProvider.GetRequiredService <ILogger <ClientPlayer> >(), client, this); if (!_clientManager.Validate(client)) { return(GameJoinResult.FromError(GameJoinError.InvalidClient)); } isNew = true; player = clientPlayer; client.Player = clientPlayer; } // Check current player state. if (player.Limbo == LimboStates.NotLimbo) { return(GameJoinResult.FromError(GameJoinError.InvalidLimbo)); } if (GameState == GameStates.Ended) { await HandleJoinGameNext(player, isNew); return(GameJoinResult.CreateSuccess(player)); } await HandleJoinGameNew(player, isNew); return(GameJoinResult.CreateSuccess(player)); }
private void WriteWaitForHostMessage(MessageWriter message, bool clear, ClientPlayer player) { Message12WaitForHost.Serialize(message, clear, Code, player.Client.Id); }
public async ValueTask <bool> HandleGameDataAsync(IMessageReader parent, ClientPlayer sender, bool toPlayer) { // Find target player. ClientPlayer target = null; if (toPlayer) { var targetId = parent.ReadPackedInt32(); if (targetId == FakeClientId && !_gamedataFakeReceived && sender.IsHost) { _gamedataFakeReceived = true; // Remove the fake client, we received the data. using (var message = MessageWriter.Get(MessageType.Reliable)) { WriteRemovePlayerMessage(message, false, FakeClientId, (byte)DisconnectReason.ExitGame); await sender.Client.Connection.SendAsync(message); } } else if (!TryGetPlayer(targetId, out target)) { _logger.LogWarning("Player {0} tried to send GameData to unknown player {1}.", sender.Client.Id, targetId); return(false); } _logger.LogTrace("Received GameData for target {0}.", targetId); } // Parse GameData messages. while (parent.Position < parent.Length) { using var reader = parent.ReadMessage(); switch (reader.Tag) { case GameDataTag.DataFlag: { var netId = reader.ReadPackedUInt32(); if (_allObjectsFast.TryGetValue(netId, out var obj)) { await obj.DeserializeAsync(sender, target, reader, false); } else { _logger.LogWarning("Received DataFlag for unregistered NetId {0}.", netId); } break; } case GameDataTag.RpcFlag: { var netId = reader.ReadPackedUInt32(); if (_allObjectsFast.TryGetValue(netId, out var obj)) { if (!await obj.HandleRpc(sender, target, (RpcCalls)reader.ReadByte(), reader)) { return(false); } } else { _logger.LogWarning("Received RpcFlag for unregistered NetId {0}.", netId); } break; } case GameDataTag.SpawnFlag: { // Only the host is allowed to despawn objects. if (!sender.IsHost) { if (await sender.Client.ReportCheatAsync(new CheatContext(nameof(GameDataTag.SpawnFlag)), "Tried to send SpawnFlag as non-host.")) { return(false); } } var objectId = reader.ReadPackedUInt32(); if (objectId < SpawnableObjects.Length) { var innerNetObject = (InnerNetObject)ActivatorUtilities.CreateInstance(_serviceProvider, SpawnableObjects[objectId], this); var ownerClientId = reader.ReadPackedInt32(); // Prevent fake client from being broadcasted. // TODO: Remove message from stream properly. if (ownerClientId == FakeClientId) { return(false); } innerNetObject.SpawnFlags = (SpawnFlags)reader.ReadByte(); var components = innerNetObject.GetComponentsInChildren <InnerNetObject>(); var componentsCount = reader.ReadPackedInt32(); if (componentsCount != components.Count) { _logger.LogError( "Children didn't match for spawnable {0}, name {1} ({2} != {3})", objectId, innerNetObject.GetType().Name, componentsCount, components.Count); continue; } _logger.LogDebug( "Spawning {0} components, SpawnFlags {1}", innerNetObject.GetType().Name, innerNetObject.SpawnFlags); for (var i = 0; i < componentsCount; i++) { var obj = components[i]; obj.NetId = reader.ReadPackedUInt32(); obj.OwnerId = ownerClientId; _logger.LogDebug( "- {0}, NetId {1}, OwnerId {2}", obj.GetType().Name, obj.NetId, obj.OwnerId); if (!AddNetObject(obj)) { _logger.LogTrace("Failed to AddNetObject, it already exists."); obj.NetId = uint.MaxValue; break; } using var readerSub = reader.ReadMessage(); if (readerSub.Length > 0) { await obj.DeserializeAsync(sender, target, readerSub, true); } await OnSpawnAsync(obj); } continue; } _logger.LogError("Couldn't find spawnable object {0}.", objectId); break; } // Only the host is allowed to despawn objects. case GameDataTag.DespawnFlag: { var netId = reader.ReadPackedUInt32(); if (_allObjectsFast.TryGetValue(netId, out var obj)) { if (sender.Client.Id != obj.OwnerId && !sender.IsHost) { _logger.LogWarning( "Player {0} ({1}) tried to send DespawnFlag for {2} but was denied.", sender.Client.Name, sender.Client.Id, netId); return(false); } RemoveNetObject(obj); await OnDestroyAsync(obj); _logger.LogDebug("Destroyed InnerNetObject {0} ({1}), OwnerId {2}", obj.GetType().Name, netId, obj.OwnerId); } else { _logger.LogDebug( "Player {0} ({1}) sent DespawnFlag for unregistered NetId {2}.", sender.Client.Name, sender.Client.Id, netId); } break; } case GameDataTag.SceneChangeFlag: { // Sender is only allowed to change his own scene. var clientId = reader.ReadPackedInt32(); if (clientId != sender.Client.Id) { _logger.LogWarning( "Player {0} ({1}) tried to send SceneChangeFlag for another player.", sender.Client.Name, sender.Client.Id); return(false); } sender.Scene = reader.ReadString(); _logger.LogTrace("> Scene {0} to {1}", clientId, sender.Scene); break; } case GameDataTag.ReadyFlag: { var clientId = reader.ReadPackedInt32(); _logger.LogTrace("> IsReady {0}", clientId); break; } default: { _logger.LogTrace("Bad GameData tag {0}", reader.Tag); break; } } if (sender.Client.Player == null) { // Disconnect handler was probably invoked, cancel the rest. return(false); } } return(true); }
private ValueTask BroadcastJoinMessage(IMessageWriter message, bool clear, ClientPlayer player) { Message01JoinGameS2C.SerializeJoin(message, clear, Code, player.Client.Id, HostId); return(SendToAllExceptAsync(message, player.Client.Id)); }
private async ValueTask <GameJoinResult> AddClientSafeAsync(ClientBase client) { // Check if the IP of the player is banned. if (_bannedIps.Contains(client.Connection.EndPoint.Address)) { return(GameJoinResult.FromError(GameJoinError.Banned)); } var player = client.Player; // Check if; // - The player is already in this game. // - The game is full. if (player?.Game != this && _players.Count >= Options.MaxPlayers) { return(GameJoinResult.FromError(GameJoinError.GameFull)); } if (GameState == GameStates.Starting || GameState == GameStates.Started) { return(GameJoinResult.FromError(GameJoinError.GameStarted)); } if (GameState == GameStates.Destroyed) { return(GameJoinResult.FromError(GameJoinError.GameDestroyed)); } if (Host != null) { foreach (var hostMod in Host.Client.Mods) { if (hostMod.Side == PluginSide.Both && client.Mods.All(clientMod => hostMod.Id != clientMod.Id)) { return(GameJoinResult.CreateCustomError($"You are missing {hostMod.Id} - {hostMod.Version}")); } } foreach (var clientMod in client.Mods) { if (clientMod.Side == PluginSide.Both && Host.Client.Mods.All(hostMod => clientMod.Id != hostMod.Id)) { return(GameJoinResult.CreateCustomError($"Host of this game is missing {clientMod.Id} - {clientMod.Version}")); } } } var isNew = false; if (player == null || player.Game != this) { var clientPlayer = new ClientPlayer(_serviceProvider.GetRequiredService <ILogger <ClientPlayer> >(), client, this); if (!_clientManager.Validate(client)) { return(GameJoinResult.FromError(GameJoinError.InvalidClient)); } isNew = true; player = clientPlayer; client.Player = clientPlayer; } // Check current player state. if (player.Limbo == LimboStates.NotLimbo) { return(GameJoinResult.FromError(GameJoinError.InvalidLimbo)); } if (GameState == GameStates.Ended) { await HandleJoinGameNext(player, isNew); return(GameJoinResult.CreateSuccess(player)); } await HandleJoinGameNew(player, isNew); return(GameJoinResult.CreateSuccess(player)); }