using System; using Robust.Client.GameObjects.Components; using Robust.Client.Interfaces.Input; using Robust.Client.Player; using Robust.Shared.GameObjects; using Robust.Shared.GameObjects.Systems; using Robust.Shared.Input; using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.IoC; using Robust.Shared.Log; using Robust.Shared.Players; using Robust.Shared.Utility; namespace Robust.Client.GameObjects.EntitySystems { /// /// Client-side processing of all input commands through the simulation. /// public class InputSystem : EntitySystem { #pragma warning disable 649 [Dependency] private readonly IInputManager _inputManager; [Dependency] private readonly IPlayerManager _playerManager; #pragma warning restore 649 private readonly IPlayerCommandStates _cmdStates = new PlayerCommandStates(); private readonly CommandBindMapping _bindMap = new CommandBindMapping(); /// /// Current states for all of the keyFunctions. /// public IPlayerCommandStates CmdStates => _cmdStates; /// /// Holds the keyFunction -> handler bindings for the simulation. /// public ICommandBindMapping BindMap => _bindMap; /// /// Inserts an Input Command into the simulation. /// /// Player session that raised the command. On client, this is always the LocalPlayer session. /// Function that is being changed. /// Arguments for this event. public void HandleInputCommand(ICommonSession session, BoundKeyFunction function, FullInputCmdMessage message) { #if DEBUG var funcId = _inputManager.NetworkBindMap.KeyFunctionID(function); DebugTools.Assert(funcId == message.InputFunctionId, "Function ID in message does not match function."); #endif // set state, state change is updated regardless if it is locally bound _cmdStates.SetState(function, message.State); // handle local binds before sending off if (_bindMap.TryGetHandler(function, out var handler)) { // local handlers can block sending over the network. if (handler.HandleCmdMessage(session, message)) return; } RaiseNetworkEvent(message); } /// public override void SubscribeEvents() { base.SubscribeEvents(); SubscribeEvent(OnAttachedEntityChanged); } private void OnAttachedEntityChanged(object sender, EntitySystemMessage message) { if(!(message is PlayerAttachSysMessage msg)) return; if (msg.AttachedEntity != null) // attach { SetEntityContextActive(_inputManager, msg.AttachedEntity); } else // detach { _inputManager.Contexts.SetActiveContext(InputContextContainer.DefaultContextName); } } private static void SetEntityContextActive(IInputManager inputMan, IEntity entity) { if(entity == null || !entity.IsValid()) throw new ArgumentNullException(nameof(entity)); if (!entity.TryGetComponent(out InputComponent inputComp)) { Logger.DebugS("input.context", $"AttachedEnt has no InputComponent: entId={entity.Uid}, entProto={entity.Prototype}"); return; } if (inputMan.Contexts.Exists(inputComp.ContextName)) { inputMan.Contexts.SetActiveContext(inputComp.ContextName); } else { Logger.ErrorS("input.context", $"Unknown context: entId={entity.Uid}, entProto={entity.Prototype}, context={inputComp.ContextName}"); } } /// /// Sets the active context to the defined context on the attached entity. /// public void SetEntityContextActive() { if (_playerManager.LocalPlayer.ControlledEntity == null) { return; } SetEntityContextActive(_inputManager, _playerManager.LocalPlayer.ControlledEntity); } } /// /// Entity system message that is raised when the player changes attached entities. /// public class PlayerAttachSysMessage : EntitySystemMessage { /// /// New entity the player is attached to. /// public IEntity AttachedEntity { get; } /// /// Creates a new instance of . /// /// New entity the player is attached to. public PlayerAttachSysMessage(IEntity attachedEntity) { AttachedEntity = attachedEntity; } } }