How to get a DeviceIndex from an input

#1
Hi,

I'm trying to create a simple flowgraph node that reacts on controllers input and outputs deviceIndex of the controller.
I know it can be reached by creating my own input listener and there is actually a member deviceIndex that contains info about the id of used device (as it is here: http://apprize.info/game/cryengine/3.html)
However, this can't be reached from flowgraph so I need to write my own node.
But I'm not that familiar with programming so I need your help or advice.

The idea of getting a device index is to support multiple players playing the same level.

Any suggestions would be great help.

Also, If someone knows how to do that, we can talk about a paid job for implementing this

Re: How to get a DeviceIndex from an input

#2
Anyone?

In short, all I need is to avoid a situation where 2 plugged in controllers control one player.
Then I need to have a possibility to know the source of input (was it controller 2 or controller 1?) - for this I want to have a flowgraph node.

Any help would be great. As I said before, I can pay for implementing this task on GameSDK project base.

Re: How to get a DeviceIndex from an input

#3
Hi
Try adding this to a flow node cpp file, before all those lines starting with "REGISTER_FLOW_NODE" . I added it to "MPNodes.cpp".

Code: Select all

class CFlowNode_MyInputActionListener : public CFlowBaseNode<eNCT_Instanced>, public IInputEventListener
{
   enum EInputs
   {
      EIP_ENABLE = 0,
      EIP_DISABLE,
      EIP_ACTION
   };

   enum EOutputs
   {
      EOP_ENABLED,
      EOP_DISABLED,
      EOP_PRESSED,
      EOP_RELEASED,
      EOP_VALUE,
      EOP_DEVICEINDEX,
      EOP_DEVICEUNIQUEID
   };

public:
   CFlowNode_MyInputActionListener(SActivationInfo* pActInfo)
      : m_bActive(false)
   {
   }

   ~CFlowNode_MyInputActionListener()
   {
      Register(false, 0);
   }

   IFlowNodePtr Clone(SActivationInfo* pActInfo)
   {
      return new CFlowNode_MyInputActionListener(pActInfo);
   }

   virtual void GetMemoryUsage(ICrySizer* s) const
   {
      s->Add(*this);
   }

   void GetConfiguration(SFlowNodeConfig& config)
   {
      static const SInputPortConfig in_ports[] =
      {
         InputPortConfig_Void("Enable", _HELP("Trigger to enable")),
         InputPortConfig_Void("Disable", _HELP("Trigger to disable")),
         InputPortConfig<string>("actionMapActions_Action", _HELP("Action input to trigger"), _HELP("Action")),
         { 0 }
      };
      static const SOutputPortConfig out_ports[] =
      {
         OutputPortConfig_Void("Enabled", _HELP("Action Listener set")),
         OutputPortConfig_Void("Disabled", _HELP("Action Listener removed")),
         OutputPortConfig<string>("Pressed", _HELP("Action trigger pressed.")),
         OutputPortConfig<string>("Released", _HELP("Action trigger released.")),
         OutputPortConfig<float>("Value", _HELP("Value associated with the event.")),
         OutputPortConfig<int>("DeviceIndex", _HELP("Local index of this particular controller type.")),
         OutputPortConfig<int>("DeviceUniqueID", _HELP("Process wide unique controller ID.")),
         { 0 }
      };
      config.pInputPorts = in_ports;
      config.pOutputPorts = out_ports;
      config.sDescription = _HELP("Checks various Multiplayer states and conditions");
      config.SetCategory(EFLN_APPROVED);
   }

   void Register(bool bRegister, SActivationInfo* pActInfo)
   {
      IInput* pInput = gEnv->pInput;
      if (pInput)
      {
         if (bRegister)
            pInput->AddEventListener(this);
         else
            pInput->RemoveEventListener(this);
      }
      m_bActive = bRegister;
   }

   void ProcessEvent(EFlowEvent ev, SActivationInfo *pActInfo)
   {
      switch (ev)
      {
      case eFE_Initialize:
      {
         m_pActInfo = *pActInfo;
         Register(false, pActInfo);
         break;
      }

      case eFE_Activate:
      {
         m_pActInfo = *pActInfo;

         if (IsPortActive(pActInfo, EIP_DISABLE))
         {
            Register(false, pActInfo);
         }

         if (IsPortActive(pActInfo, EIP_ENABLE))
         {
            Register(true, pActInfo);
         }

         break;
      }
      }

      
   }

   virtual bool OnInputEvent(const SInputEvent& event)
   {
      if (gEnv->pConsole->IsOpened())
         return false;

      bool bActionFound = false;
      const string& eventKeyName = event.keyName.c_str();
      const string& actionInput = GetPortString(&m_pActInfo, EIP_ACTION);
      const string& actionMapName = actionInput.substr(0, actionInput.find_first_of(":"));
      const string& actionName = actionInput.substr(actionInput.find_first_of(":") + 1, (actionInput.length() - actionInput.find_first_of(":")));

      const EntityId filterEntityId = m_pActInfo.pEntity ? m_pActInfo.pEntity->GetId() : INVALID_ENTITYID;

      if (!actionMapName.empty() && !actionName.empty())
      {
         IActionMapManager* pActionMapMgr = gEnv->pGame->GetIGameFramework()->GetIActionMapManager();
         if (pActionMapMgr)
         {
            const IActionMap* pActionMap = pActionMapMgr->GetActionMap(actionMapName);
            if (pActionMap && (filterEntityId == INVALID_ENTITYID || filterEntityId == pActionMap->GetActionListener()))
            {
               const IActionMapAction* pAction = pActionMap->GetAction(ActionId(actionName));
               if (pAction)
               {
                  const int iNumInputData = pAction->GetNumActionInputs();

                  for (int i = 0; i < iNumInputData; ++i)
                  {
                     const SActionInput* pActionInput = pAction->GetActionInput(i);

                     if (!stricmp(pActionInput->input, eventKeyName.c_str()))
                     {
                        bActionFound = true;
                        break;
                     }
                  }
               }
            }
         }
      }
      
      if (bActionFound)
      {
         const bool nullInputValue = (fabs_tpl(event.value) < 0.02f);

         if ((event.state == eIS_Pressed) || ((event.state == eIS_Changed) && !nullInputValue))
         {
            ActivateOutput(&m_pActInfo, EOP_PRESSED, eventKeyName);
         }
         else if ((event.state == eIS_Released) || ((event.state == eIS_Changed) && nullInputValue))
         {
            ActivateOutput(&m_pActInfo, EOP_RELEASED, eventKeyName);
         }
         // for some unknown reason without this two variables, I gem compile errors
         int deviceIndex = event.deviceIndex;
         int deviceUniqueId = event.deviceUniqueID;

         if (!nullInputValue)
         {
            ActivateOutput(&m_pActInfo, EOP_VALUE, event.value);
            ActivateOutput(&m_pActInfo, EOP_DEVICEINDEX, deviceIndex);
            ActivateOutput(&m_pActInfo, EOP_DEVICEUNIQUEID, deviceUniqueId);
         }
      }

      // return false, so other listeners get notification as well
      return false;
   }


   SActivationInfo m_pActInfo;
   bool            m_bActive;
};

REGISTER_FLOW_NODE("Input:ActionMaps:MyActionListener", CFlowNode_MyInputActionListener);

And add this line to beginning of the file you chose, right after all those lines starting with "#include":

Code: Select all

#include "CryInput\IInput.h"

After you compile gamesdk, copy the dll file to "bin/win_x64" directory of gamesdk project.
The new node is present in "Input -> ActionMaps".

I hope this helps.
Last edited by game_dev5 on Fri May 05, 2017 6:48 pm, edited 2 times in total.

Re: How to get a DeviceIndex from an input

#4
game_dev5 wrote:Hi
adding this to a flow node cpp file, before all those lines starting with "REGISTER_FLOW_NODE" . I added it to "MPNodes.cpp".

Code: Select all

class CFlowNode_MyInputActionListener : public CFlowBaseNode<eNCT_Instanced>, public IInputEventListener
{
   enum EInputs
   {
      EIP_ENABLE = 0,
      EIP_DISABLE,
      EIP_ACTION
   };

   enum EOutputs
   {
      EOP_ENABLED,
      EOP_DISABLED,
      EOP_PRESSED,
      EOP_RELEASED,
      EOP_VALUE,
      EOP_DEVICEINDEX,
      EOP_DEVICEUNIQUEID
   };

public:
   CFlowNode_MyInputActionListener(SActivationInfo* pActInfo)
      : m_bActive(false)
   {
   }

   ~CFlowNode_MyInputActionListener()
   {
      Register(false, 0);
   }

   IFlowNodePtr Clone(SActivationInfo* pActInfo)
   {
      return new CFlowNode_MyInputActionListener(pActInfo);
   }

   virtual void GetMemoryUsage(ICrySizer* s) const
   {
      s->Add(*this);
   }

   void GetConfiguration(SFlowNodeConfig& config)
   {
      static const SInputPortConfig in_ports[] =
      {
         InputPortConfig_Void("Enable", _HELP("Trigger to enable")),
         InputPortConfig_Void("Disable", _HELP("Trigger to disable")),
         InputPortConfig<string>("actionMapActions_Action", _HELP("Action input to trigger"), _HELP("Action")),
         { 0 }
      };
      static const SOutputPortConfig out_ports[] =
      {
         OutputPortConfig_Void("Enabled", _HELP("Action Listener set")),
         OutputPortConfig_Void("Disabled", _HELP("Action Listener removed")),
         OutputPortConfig<string>("Pressed", _HELP("Action trigger pressed.")),
         OutputPortConfig<string>("Released", _HELP("Action trigger released.")),
         OutputPortConfig<float>("Value", _HELP("Value associated with the event.")),
         OutputPortConfig<int>("DeviceIndex", _HELP("Local index of this particular controller type.")),
         OutputPortConfig<int>("DeviceUniqueID", _HELP("Process wide unique controller ID.")),
         { 0 }
      };
      config.pInputPorts = in_ports;
      config.pOutputPorts = out_ports;
      config.sDescription = _HELP("Checks various Multiplayer states and conditions");
      config.SetCategory(EFLN_APPROVED);
   }

   void Register(bool bRegister, SActivationInfo* pActInfo)
   {
      IInput* pInput = gEnv->pInput;
      if (pInput)
      {
         if (bRegister)
            pInput->AddEventListener(this);
         else
            pInput->RemoveEventListener(this);
      }
      m_bActive = bRegister;
   }

   void ProcessEvent(EFlowEvent ev, SActivationInfo *pActInfo)
   {
      switch (ev)
      {
      case eFE_Initialize:
      {
         m_pActInfo = *pActInfo;
         Register(false, pActInfo);
         break;
      }

      case eFE_Activate:
      {
         m_pActInfo = *pActInfo;

         if (IsPortActive(pActInfo, EIP_DISABLE))
         {
            Register(false, pActInfo);
         }

         if (IsPortActive(pActInfo, EIP_ENABLE))
         {
            Register(true, pActInfo);
         }

         break;
      }
      }

      
   }

   virtual bool OnInputEvent(const SInputEvent& event)
   {
      if (gEnv->pConsole->IsOpened())
         return false;

      bool bActionFound = false;
      const string& eventKeyName = event.keyName.c_str();
      const string& actionInput = GetPortString(&m_pActInfo, EIP_ACTION);
      const string& actionMapName = actionInput.substr(0, actionInput.find_first_of(":"));
      const string& actionName = actionInput.substr(actionInput.find_first_of(":") + 1, (actionInput.length() - actionInput.find_first_of(":")));

      const EntityId filterEntityId = m_pActInfo.pEntity ? m_pActInfo.pEntity->GetId() : INVALID_ENTITYID;

      if (!actionMapName.empty() && !actionName.empty())
      {
         IActionMapManager* pActionMapMgr = gEnv->pGame->GetIGameFramework()->GetIActionMapManager();
         if (pActionMapMgr)
         {
            const IActionMap* pActionMap = pActionMapMgr->GetActionMap(actionMapName);
            if (pActionMap && (filterEntityId == INVALID_ENTITYID || filterEntityId == pActionMap->GetActionListener()))
            {
               const IActionMapAction* pAction = pActionMap->GetAction(ActionId(actionName));
               if (pAction)
               {
                  const int iNumInputData = pAction->GetNumActionInputs();

                  for (int i = 0; i < iNumInputData; ++i)
                  {
                     const SActionInput* pActionInput = pAction->GetActionInput(i);

                     if (!stricmp(pActionInput->input, eventKeyName.c_str()))
                     {
                        bActionFound = true;
                        break;
                     }
                  }
               }
            }
         }
      }
      
      if (bActionFound)
      {
         const bool nullInputValue = (fabs_tpl(event.value) < 0.02f);

         if ((event.state == eIS_Pressed) || ((event.state == eIS_Changed) && !nullInputValue))
         {
            ActivateOutput(&m_pActInfo, EOP_PRESSED, eventKeyName);
         }
         else if ((event.state == eIS_Released) || ((event.state == eIS_Changed) && nullInputValue))
         {
            ActivateOutput(&m_pActInfo, EOP_RELEASED, eventKeyName);
         }
         // for some unknown reason without this two variables, I gem compile errors
         int deviceIndex = event.deviceIndex;
         int deviceUniqueId = event.deviceUniqueID;

         if (!nullInputValue)
         {
            ActivateOutput(&m_pActInfo, EOP_VALUE, event.value);
            ActivateOutput(&m_pActInfo, EOP_DEVICEINDEX, deviceIndex);
            ActivateOutput(&m_pActInfo, EOP_DEVICEUNIQUEID, deviceUniqueId);
         }
      }

      // return false, so other listeners get notification as well
      return false;
   }


   SActivationInfo m_pActInfo;
   bool            m_bActive;
};

REGISTER_FLOW_NODE("Input:ActionMaps:MyActionListener", CFlowNode_MyInputActionListener);

And add this line to beginning of the file you chose, right after all those lines starting with "#include":

Code: Select all

#include "CryInput\IInput.h"

After you compile gamesdk, copy the dll file to bin directory of gamesdk project.
The new node is present in "Input -> ActionMaps".

I hope this helps.


Just to be clear, this is a slightly modified version of "CryAction"s "ActionListener" node.

Re: How to get a DeviceIndex from an input

#5
Thanks so much for helping!

Now one problem is solved.

There's another one left.

How to make only one gamepad control the player?

I mean, by default cryengine accepts input from gamepad 1 and gamepad 2, and all of them are controlling the same player.

I need to somehow set only one controller to control player.

(Second controller input will be held by a flowgraph and should not control the player)

How can I achieve this?

Again, thanks so much for helping out with the flowgraph node

Re: How to get a DeviceIndex from an input

#7
Bs1_BslTeam wrote:Thanks so much for helping!

Now one problem is solved.

There's another one left.

How to make only one gamepad control the player?

I mean, by default cryengine accepts input from gamepad 1 and gamepad 2, and all of them are controlling the same player.

I need to somehow set only one controller to control player.

(Second controller input will be held by a flowgraph and should not control the player)

How can I achieve this?

Again, thanks so much for helping out with the flowgraph node


Sorry, I don't think I can help you with this one. :(
But if your player character is not that complicated, I suggest you prototype your idea the way the robot player is implemented in amazon lumberyard's samplesproject. It's using flowgraphs.
Or you can wait for a few more weeks and use Cryengine 5.4 new schematyc nodes. The promised schematyc components look interesting.
Check "animation", "ai" and "physics" tabs in this page:
https://www.cryengine.com/roadmap
Last edited by game_dev5 on Sat May 06, 2017 6:06 am, edited 1 time in total.

Re: How to get a DeviceIndex from an input

#8
search

bool CBaseInput::SendEventToListeners(const SInputEvent& event)

and OnInputEvent(event)

actionInputNode->getAttr(ACTIONINPUT_INPUTBLOCKDEVICEINDEX_STR, iDeviceIndex);


int deviceIndex = event.deviceIndex;
int deviceUniqueId = event.deviceUniqueID;
combine them to filter


flow node need add deviceindex parameter
Last edited by mknmknmknjk on Sun May 07, 2017 12:08 am, edited 1 time in total.

Who is online

Users browsing this forum: No registered users and 2 guests