private static bool CheckInteractInternal <T>(IBaseInteractable <T> interactable, T interaction, NetworkSide side) where T : Interaction { if (Cooldowns.IsOn(interaction, CooldownID.Asset(CommonCooldowns.Instance.Interaction, side))) { return(false); } var result = false; //check if client side interaction should be triggered if (side == NetworkSide.Client && interactable is IClientInteractable <T> clientInteractable) { result = clientInteractable.Interact(interaction); if (result) { Logger.LogTraceFormat("ClientInteractable triggered from {0} on {1} for object {2}", Category.Interaction, typeof(T).Name, clientInteractable.GetType().Name, (clientInteractable as Component).gameObject.name); Cooldowns.TryStartClient(interaction, CommonCooldowns.Instance.Interaction); return(true); } } //check other kinds of interactions if (interactable is ICheckable <T> checkable) { result = checkable.WillInteract(interaction, side); if (result) { Logger.LogTraceFormat("WillInteract triggered from {0} on {1} for object {2}", Category.Interaction, typeof(T).Name, checkable.GetType().Name, (checkable as Component).gameObject.name); return(true); } } else if (interactable is IInteractable <T> ) { //use default logic result = DefaultWillInteract.Default(interaction, side); if (result) { Logger.LogTraceFormat("WillInteract triggered from {0} on {1} for object {2}", Category.Interaction, typeof(T).Name, interactable.GetType().Name, (interactable as Component).gameObject.name); return(true); } } Logger.LogTraceFormat("No interaction triggered from {0} on {1} for object {2}", Category.Interaction, typeof(T).Name, interactable.GetType().Name, (interactable as Component).gameObject.name); return(false); }
//only intended to be used by core if2 classes, please use InteractionUtils.RequestInteract instead. public static void Send <T>(T interaction, IBaseInteractable <T> interactableComponent) where T : Interaction { if (typeof(T) == typeof(TileApply)) { Logger.LogError("Cannot use Send with TileApply, please use SendTileApply instead.", Category.Interaction); return; } //never send anything for client-side-only interactions if (interactableComponent is IClientInteractable <T> && !(interactableComponent is IInteractable <T>)) { Logger.LogWarningFormat("Interaction request {0} will not be sent because interactable component {1} is" + " IClientInteractable only (client-side only).", Category.Interaction, interaction, interactableComponent); return; } //if we are client and the interaction has client prediction, trigger it. //Note that client prediction is not triggered for server player. if (!CustomNetworkManager.IsServer && interactableComponent is IPredictedInteractable <T> predictedInteractable) { Logger.LogTraceFormat("Predicting {0} interaction for {1} on {2}", Category.Interaction, typeof(T).Name, interactableComponent.GetType().Name, ((Component)interactableComponent).gameObject.name); predictedInteractable.ClientPredictInteraction(interaction); } if (!interaction.Performer.Equals(PlayerManager.LocalPlayer)) { Logger.LogError("Client attempting to perform an interaction on behalf of another player." + " This is not allowed. Client can only perform an interaction as themselves. Message" + " will not be sent.", Category.NetMessage); return; } if (!(interactableComponent is Component)) { Logger.LogError("interactableComponent must be a component, but isn't. The message will not be sent.", Category.NetMessage); return; } var comp = interactableComponent as Component; var msg = new RequestInteractMessage() { ComponentType = interactableComponent.GetType(), InteractionType = typeof(T), ProcessorObject = comp.GetComponent <NetworkIdentity>().netId, Intent = interaction.Intent }; if (typeof(T) == typeof(PositionalHandApply)) { var casted = interaction as PositionalHandApply; msg.TargetObject = casted.TargetObject.NetId(); msg.TargetVector = casted.TargetVector; msg.TargetBodyPart = casted.TargetBodyPart; } else if (typeof(T) == typeof(HandApply)) { var casted = interaction as HandApply; msg.TargetObject = casted.TargetObject.NetId(); msg.TargetBodyPart = casted.TargetBodyPart; msg.IsAltUsed = casted.IsAltClick; } else if (typeof(T) == typeof(AimApply)) { var casted = interaction as AimApply; msg.TargetVector = casted.TargetVector; msg.MouseButtonState = casted.MouseButtonState; } else if (typeof(T) == typeof(MouseDrop)) { var casted = interaction as MouseDrop; msg.TargetObject = casted.TargetObject.NetId(); msg.UsedObject = casted.UsedObject.NetId(); } else if (typeof(T) == typeof(InventoryApply)) { var casted = interaction as InventoryApply; msg.Storage = casted.TargetSlot.ItemStorageNetID; msg.SlotIndex = casted.TargetSlot.SlotIdentifier.SlotIndex; msg.NamedSlot = casted.TargetSlot.SlotIdentifier.NamedSlot.GetValueOrDefault(NamedSlot.none); msg.UsedObject = casted.UsedObject.NetId(); msg.IsAltUsed = casted.IsAltClick; } msg.Send(); }
private static bool CheckInteractInternal <T>(this IBaseInteractable <T> interactable, T interaction, NetworkSide side, out bool wasClientInteractable) where T : Interaction { wasClientInteractable = false; //interactions targeting an object at hiddenpos are NEVER allowed (except for inventory actions, //since they can target an object in inventory which means its at hiddenpos) if (!(interaction is InventoryApply) && interaction is TargetedInteraction targetedInteraction) { if (targetedInteraction.TargetObject != null && targetedInteraction.TargetObject.IsAtHiddenPos()) { Logger.LogTraceFormat("Aborting {0} interaction on object {1} because the object is hidden.", Category.Interaction, typeof(T).Name, targetedInteraction.TargetObject.name); return(false); } } if (Cooldowns.IsOn(interaction, CooldownID.Asset(CommonCooldowns.Instance.Interaction, side))) { return(false); } var result = false; //check if client side interaction should be triggered if (side == NetworkSide.Client && interactable is IClientInteractable <T> clientInteractable) { result = clientInteractable.Interact(interaction); if (result) { Logger.LogTraceFormat("ClientInteractable triggered from {0} on {1} for object {2}", Category.Interaction, typeof(T).Name, clientInteractable.GetType().Name, (clientInteractable as Component).gameObject.name); Cooldowns.TryStartClient(interaction, CommonCooldowns.Instance.Interaction); wasClientInteractable = true; return(true); } } //check other kinds of interactions if (interactable is ICheckable <T> checkable) { result = checkable.WillInteract(interaction, side); if (result) { Logger.LogTraceFormat("WillInteract triggered from {0} on {1} for object {2}", Category.Interaction, typeof(T).Name, checkable.GetType().Name, (checkable as Component).gameObject.name); wasClientInteractable = false; return(true); } } else if (interactable is IInteractable <T> ) { //use default logic result = DefaultWillInteract.Default(interaction, side); if (result) { Logger.LogTraceFormat("WillInteract triggered from {0} on {1} for object {2}", Category.Interaction, typeof(T).Name, interactable.GetType().Name, (interactable as Component).gameObject.name); wasClientInteractable = false; return(true); } } Logger.LogTraceFormat("No interaction triggered from {0} on {1} for object {2}", Category.Interaction, typeof(T).Name, interactable.GetType().Name, (interactable as Component).gameObject.name); wasClientInteractable = false; return(false); }
/// <summary> /// Checks if this component would trigger any interaction, also invokes client side logic if it implements IClientInteractable. /// </summary> /// <param name="interactable"></param> /// <param name="interaction"></param> /// <param name="side"></param> /// <typeparam name="T"></typeparam> /// <returns></returns> public static bool CheckInteract <T>(this IBaseInteractable <T> interactable, T interaction, NetworkSide side) where T : Interaction { var result = false; //check if client side interaction should be triggered if (side == NetworkSide.Client && interactable is IClientInteractable <T> clientInteractable) { result = clientInteractable.Interact(interaction); if (result) { Logger.LogTraceFormat("ClientInteractable triggered from {0} on {1} for object {2}", Category.Interaction, typeof(T).Name, clientInteractable.GetType().Name, (clientInteractable as Component).gameObject.name); return(true); } } else if (interactable is ICheckable <T> checkable) { result = checkable.WillInteract(interaction, side); if (result) { Logger.LogTraceFormat("WillInteract triggered from {0} on {1} for object {2}", Category.Interaction, typeof(T).Name, checkable.GetType().Name, (checkable as Component).gameObject.name); return(true); } } else if (interactable is IInteractable <T> ) { //use default logic result = DefaultWillInteract.Default(interaction, side); if (result) { Logger.LogTraceFormat("WillInteract triggered from {0} on {1} for object {2}", Category.Interaction, typeof(T).Name, interactable.GetType().Name, (interactable as Component).gameObject.name); return(true); } } Logger.LogTraceFormat("No interaction triggered from {0} on {1} for object {2}", Category.Interaction, typeof(T).Name, interactable.GetType().Name, (interactable as Component).gameObject.name); return(false); }
//only intended to be used by core if2 classes, please use InteractionUtils.RequestInteract instead. //pass null for interactableComponent if you want the server to determine which component of the involved objects should be triggered. //(which can be useful when client doesn't have enough info to know which one to trigger) public static void Send <T>(T interaction, IBaseInteractable <T> interactableComponent) where T : global::Interaction { if (typeof(T) == typeof(TileApply)) { Logger.LogError("Cannot use Send with TileApply, please use SendTileApply instead.", Category.Interaction); return; } //never send anything for client-side-only interactions if (interactableComponent is IClientInteractable <T> && !(interactableComponent is IInteractable <T>)) { Logger.LogWarningFormat("Interaction request {0} will not be sent because interactable component {1} is" + " IClientInteractable only (client-side only).", Category.Interaction, interaction, interactableComponent); return; } //if we are client and the interaction has client prediction, trigger it. //Note that client prediction is not triggered for server player. if (!CustomNetworkManager.IsServer && interactableComponent is IPredictedInteractable <T> predictedInteractable) { Logger.LogTraceFormat("Predicting {0} interaction for {1} on {2}", Category.Interaction, typeof(T).Name, interactableComponent.GetType().Name, ((Component)interactableComponent).gameObject.name); predictedInteractable.ClientPredictInteraction(interaction); } if (!interaction.Performer.Equals(PlayerManager.LocalPlayer)) { Logger.LogError("Client attempting to perform an interaction on behalf of another player." + " This is not allowed. Client can only perform an interaction as themselves. Message" + " will not be sent.", Category.Exploits); return; } if (interactableComponent != null && !(interactableComponent is Component)) { Logger.LogError("interactableComponent must be a component, but isn't. The message will not be sent.", Category.Exploits); return; } var comp = interactableComponent as Component; var msg = new NetMessage() { ComponentType = interactableComponent == null ? null : interactableComponent.GetType(), InteractionType = typeof(T), ProcessorObject = comp == null ? NetId.Invalid : GetNetId(comp.gameObject), Intent = interaction.Intent }; if (typeof(T) == typeof(PositionalHandApply)) { var casted = interaction as PositionalHandApply; msg.TargetObject = GetNetId(casted.TargetObject); msg.TargetPosition = casted.TargetPosition; msg.TargetBodyPart = casted.TargetBodyPart; msg.IsAltUsed = casted.IsAltClick; } else if (typeof(T) == typeof(HandApply)) { var casted = interaction as HandApply; msg.TargetObject = GetNetId(casted.TargetObject); msg.TargetBodyPart = casted.TargetBodyPart; msg.IsAltUsed = casted.IsAltClick; } else if (typeof(T) == typeof(AimApply)) { var casted = interaction as AimApply; msg.TargetPosition = casted.TargetPosition; //TODO add client Origin msg.MouseButtonState = casted.MouseButtonState; msg.TargetBodyPart = casted.TargetBodyPart; } else if (typeof(T) == typeof(MouseDrop)) { var casted = interaction as MouseDrop; msg.TargetObject = GetNetId(casted.TargetObject); msg.UsedObject = GetNetId(casted.UsedObject); } else if (typeof(T) == typeof(InventoryApply)) { var casted = interaction as InventoryApply; //StorageIndexOnGameObject msg.StorageIndexOnGameObject = 0; foreach (var itemStorage in NetworkIdentity.spawned[casted.TargetSlot.ItemStorageNetID].GetComponents <ItemStorage>()) { if (itemStorage == casted.TargetSlot.ItemStorage) { break; } msg.StorageIndexOnGameObject++; } msg.Storage = casted.TargetSlot.ItemStorageNetID; msg.SlotIndex = casted.TargetSlot.SlotIdentifier.SlotIndex; msg.NamedSlot = casted.TargetSlot.SlotIdentifier.NamedSlot.GetValueOrDefault(NamedSlot.none); msg.UsedObject = GetNetId(casted.UsedObject); msg.IsAltUsed = casted.IsAltClick; } else if (typeof(T) == typeof(ConnectionApply)) { var casted = interaction as ConnectionApply; msg.TargetObject = GetNetId(casted.TargetObject); msg.TargetPosition = casted.TargetPosition; msg.connectionPointA = casted.WireEndA; msg.connectionPointB = casted.WireEndB; } else if (typeof(T) == typeof(ContextMenuApply)) { var casted = interaction as ContextMenuApply; msg.TargetObject = GetNetId(casted.TargetObject); msg.RequestedOption = casted.RequestedOption; } else if (typeof(T) == typeof(AiActivate)) { var casted = interaction as AiActivate; msg.TargetObject = GetNetId(casted.TargetObject); msg.ClickTypes = casted.ClickType; } Send(msg); }
//only intended to be used by core if2 classes, please use InteractionUtils.RequestInteract instead. public static void Send <T>(T interaction, IBaseInteractable <T> interactableComponent) where T : Interaction { //never send anything for client-side-only interactions if (interactableComponent is IClientInteractable <T> ) { return; } //if we are client and the interaction has client prediction, trigger it. //Note that client prediction is not triggered for server player. if (!CustomNetworkManager.IsServer && interactableComponent is IPredictedInteractable <T> predictedInteractable) { Logger.LogTraceFormat("Predicting {0} interaction for {1} on {2}", Category.Interaction, typeof(T).Name, interactableComponent.GetType().Name, ((Component)interactableComponent).gameObject.name); predictedInteractable.ClientPredictInteraction(interaction); } if (!interaction.Performer.Equals(PlayerManager.LocalPlayer)) { Logger.LogError("Client attempting to perform an interaction on behalf of another player." + " This is not allowed. Client can only perform an interaction as themselves. Message" + " will not be sent.", Category.NetMessage); return; } if (!(interactableComponent is Component)) { Logger.LogError("interactableComponent must be a component, but isn't. The message will not be sent.", Category.NetMessage); return; } var comp = interactableComponent as Component; var msg = new RequestInteractMessage() { ComponentType = interactableComponent.GetType(), InteractionType = typeof(T), ProcessorObject = comp.GetComponent <NetworkIdentity>().netId }; if (typeof(T) == typeof(PositionalHandApply)) { var casted = interaction as PositionalHandApply; msg.TargetObject = casted.TargetObject.GetComponent <NetworkIdentity>().netId; msg.TargetVector = casted.TargetVector; } else if (typeof(T) == typeof(HandApply)) { var casted = interaction as HandApply; msg.TargetObject = casted.TargetObject.GetComponent <NetworkIdentity>().netId; msg.TargetBodyPart = casted.TargetBodyPart; } else if (typeof(T) == typeof(AimApply)) { var casted = interaction as AimApply; msg.TargetVector = casted.TargetVector; msg.MouseButtonState = casted.MouseButtonState; } else if (typeof(T) == typeof(MouseDrop)) { var casted = interaction as MouseDrop; msg.TargetObject = casted.TargetObject.GetComponent <NetworkIdentity>().netId; msg.UsedObject = casted.UsedObject.GetComponent <NetworkIdentity>().netId; } msg.Send(); }