LocalUserSystemComponent.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include <LocalUserSystemComponent.h>
  9. #include <LocalUser/LocalUserNotificationBus.h>
  10. #include <AzFramework/Input/Devices/Gamepad/InputDeviceGamepad.h>
  11. #include <AzCore/RTTI/BehaviorContext.h>
  12. #include <AzCore/Serialization/SerializeContext.h>
  13. #include <AzCore/Serialization/EditContext.h>
  14. #include <AzCore/Serialization/EditContextConstants.inl>
  15. #include <AzCore/std/string/conversions.h>
  16. ////////////////////////////////////////////////////////////////////////////////////////////////////
  17. namespace LocalUser
  18. {
  19. ////////////////////////////////////////////////////////////////////////////////////////////////
  20. class LocalUserNotificationBusBehaviorHandler
  21. : public LocalUserNotificationBus::Handler
  22. , public AZ::BehaviorEBusHandler
  23. {
  24. public:
  25. ////////////////////////////////////////////////////////////////////////////////////////////
  26. AZ_EBUS_BEHAVIOR_BINDER(LocalUserNotificationBusBehaviorHandler, "{6A3B1CAB-92BE-4773-A3AE-470203D70662}", AZ::SystemAllocator
  27. , OnLocalUserSignedIn
  28. , OnLocalUserSignedOut
  29. , OnLocalUserIdAssignedToLocalPlayerSlot
  30. , OnLocalUserIdRemovedFromLocalPlayerSlot
  31. );
  32. ////////////////////////////////////////////////////////////////////////////////////////////
  33. void OnLocalUserSignedIn(AzFramework::LocalUserId localUserId) override
  34. {
  35. Call(FN_OnLocalUserSignedIn, localUserId);
  36. }
  37. ////////////////////////////////////////////////////////////////////////////////////////////
  38. void OnLocalUserSignedOut(AzFramework::LocalUserId localUserId) override
  39. {
  40. Call(FN_OnLocalUserSignedOut, localUserId);
  41. }
  42. ////////////////////////////////////////////////////////////////////////////////////////////
  43. void OnLocalUserIdAssignedToLocalPlayerSlot(AzFramework::LocalUserId localUserId,
  44. AZ::u32 newLocalPlayerSlot,
  45. AZ::u32 previousLocalPlayerSlot) override
  46. {
  47. Call(FN_OnLocalUserIdAssignedToLocalPlayerSlot, localUserId, newLocalPlayerSlot, previousLocalPlayerSlot);
  48. }
  49. ////////////////////////////////////////////////////////////////////////////////////////////
  50. void OnLocalUserIdRemovedFromLocalPlayerSlot(AzFramework::LocalUserId localUserId,
  51. AZ::u32 localPlayerSlot) override
  52. {
  53. Call(FN_OnLocalUserIdRemovedFromLocalPlayerSlot, localUserId, localPlayerSlot);
  54. }
  55. };
  56. ////////////////////////////////////////////////////////////////////////////////////////////////
  57. void LocalUserSystemComponent::Reflect(AZ::ReflectContext* context)
  58. {
  59. if (AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context))
  60. {
  61. serialize->Class<LocalUserSystemComponent, AZ::Component>()
  62. ->Version(0);
  63. if (AZ::EditContext* ec = serialize->GetEditContext())
  64. {
  65. ec->Class<LocalUserSystemComponent>("LocalUser", "Provides functionality for mapping local user ids to local player slots and managing local user profiles.")
  66. ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
  67. ->Attribute(AZ::Edit::Attributes::AutoExpand, true)
  68. ;
  69. }
  70. }
  71. if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
  72. {
  73. behaviorContext->EBus<LocalUserNotificationBus>("LocalUserNotificationBus")
  74. ->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All)
  75. ->Attribute(AZ::Script::Attributes::Category, "LocalUser")
  76. ->Handler<LocalUserNotificationBusBehaviorHandler>()
  77. ;
  78. behaviorContext->EBus<LocalUserRequestBus>("LocalUserRequestBus")
  79. ->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All)
  80. ->Attribute(AZ::Script::Attributes::Category, "LocalUser")
  81. ->Event("GetMaxLocalUsers", &LocalUserRequestBus::Events::GetMaxLocalUsers)
  82. ->Event("IsLocalUserSignedIn", &LocalUserRequestBus::Events::IsLocalUserSignedIn)
  83. ->Event("GetLocalUserName", &LocalUserRequestBus::Events::GetLocalUserName)
  84. ->Event("AssignLocalUserIdToLocalPlayerSlot", &LocalUserRequestBus::Events::AssignLocalUserIdToLocalPlayerSlot)
  85. ->Event("RemoveLocalUserIdFromLocalPlayerSlot", &LocalUserRequestBus::Events::RemoveLocalUserIdFromLocalPlayerSlot)
  86. ->Event("GetLocalUserIdAssignedToLocalPlayerSlot", &LocalUserRequestBus::Events::GetLocalUserIdAssignedToLocalPlayerSlot)
  87. ->Event("GetLocalPlayerSlotOccupiedByLocalUserId", &LocalUserRequestBus::Events::GetLocalPlayerSlotOccupiedByLocalUserId)
  88. ->Event("ClearAllLocalUserIdToLocalPlayerSlotAssignments", &LocalUserRequestBus::Events::ClearAllLocalUserIdToLocalPlayerSlotAssignments)
  89. ;
  90. }
  91. }
  92. ////////////////////////////////////////////////////////////////////////////////////////////////
  93. void LocalUserSystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
  94. {
  95. provided.push_back(AZ_CRC("LocalUserService"));
  96. }
  97. ////////////////////////////////////////////////////////////////////////////////////////////////
  98. void LocalUserSystemComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
  99. {
  100. incompatible.push_back(AZ_CRC("LocalUserService"));
  101. }
  102. ////////////////////////////////////////////////////////////////////////////////////////////////
  103. LocalUserSystemComponent::LocalUserSystemComponent()
  104. {
  105. for (AZ::u32 i = 0; i < LocalPlayerSlotMax; ++i)
  106. {
  107. m_localUserIdsByLocalPlayerSlot[i] = AzFramework::LocalUserIdNone;
  108. }
  109. }
  110. ////////////////////////////////////////////////////////////////////////////////////////////////
  111. void LocalUserSystemComponent::Activate()
  112. {
  113. m_pimpl.reset(Implementation::Create());
  114. LocalUserRequestBus::Handler::BusConnect();
  115. }
  116. ////////////////////////////////////////////////////////////////////////////////////////////////
  117. void LocalUserSystemComponent::Deactivate()
  118. {
  119. ClearAllLocalUserIdToLocalPlayerSlotAssignments();
  120. LocalUserRequestBus::Handler::BusDisconnect();
  121. m_pimpl.reset();
  122. }
  123. ////////////////////////////////////////////////////////////////////////////////////////////////
  124. AZStd::shared_ptr<LocalUserProfile> LocalUserSystemComponent::FindLocalUserProfile(AzFramework::LocalUserId localUserId)
  125. {
  126. return m_pimpl ? m_pimpl->FindLocalUserProfile(localUserId) : nullptr;
  127. }
  128. ////////////////////////////////////////////////////////////////////////////////////////////////
  129. AZ::u32 LocalUserSystemComponent::GetMaxLocalUsers() const
  130. {
  131. if (m_pimpl)
  132. {
  133. return m_pimpl->GetMaxLocalUsers();
  134. }
  135. // On platforms with no concept of a local user profile the local user id corresponds
  136. // to a unique input device index, so the maximum is the number of supported gamepads.
  137. auto deviceGamepadImplFactory = AZ::Interface<AzFramework::InputDeviceGamepad::ImplementationFactory>::Get();
  138. return (deviceGamepadImplFactory != nullptr) ? deviceGamepadImplFactory->GetMaxSupportedGamepads() : 0;
  139. }
  140. ////////////////////////////////////////////////////////////////////////////////////////////////
  141. bool LocalUserSystemComponent::IsLocalUserSignedIn(AzFramework::LocalUserId localUserId)
  142. {
  143. if (m_pimpl)
  144. {
  145. return m_pimpl->IsLocalUserSignedIn(localUserId);
  146. }
  147. // On platforms with no concept of a local user profile the local user id corresponds
  148. // to a unique input device index, and is therefore always considered to be signed in.
  149. return localUserId != AzFramework::LocalUserIdNone;
  150. }
  151. ////////////////////////////////////////////////////////////////////////////////////////////////
  152. AZStd::string LocalUserSystemComponent::GetLocalUserName(AzFramework::LocalUserId localUserId)
  153. {
  154. if (m_pimpl)
  155. {
  156. return m_pimpl->GetLocalUserName(localUserId);
  157. }
  158. // On platforms that have no concept of a local user profile, return "Player N" where "N" is
  159. // the local player slot currently occupied by localUserId, otherwise return an empty string.
  160. const AZ::u32 localPlayerSlot = GetLocalPlayerSlotOccupiedByLocalUserId(localUserId);
  161. return (localPlayerSlot < LocalPlayerSlotMax) ?
  162. AZStd::string("Player ") + AZStd::to_string(localPlayerSlot + 1) :
  163. "";
  164. }
  165. ////////////////////////////////////////////////////////////////////////////////////////////////
  166. AZ::u32 LocalUserSystemComponent::AssignLocalUserIdToLocalPlayerSlot(AzFramework::LocalUserId localUserId,
  167. AZ::u32 localPlayerSlot)
  168. {
  169. AZ_Warning("LocalUserSystemComponent",
  170. localUserId != AzFramework::LocalUserIdAny,
  171. "Assigning LocalUserIdAny to local player slot %d.\n"
  172. "You should likely prompt the user to sign-in first,\n"
  173. "probably by using InputDevice::PromptLocalUserSignIn\n");
  174. const AZ::u32 existingLocalPlayerSlot = GetLocalPlayerSlotOccupiedByLocalUserId(localUserId);
  175. if (localPlayerSlot < LocalPlayerSlotMax)
  176. {
  177. // A specific slot has been requested...
  178. if (m_localUserIdsByLocalPlayerSlot[localPlayerSlot] == AzFramework::LocalUserIdNone)
  179. {
  180. // ...and it is unoccupied, so assign the user into the slot
  181. // and remove the user from any existing slot it occupied.
  182. m_localUserIdsByLocalPlayerSlot[localPlayerSlot] = localUserId;
  183. if (existingLocalPlayerSlot < LocalPlayerSlotMax)
  184. {
  185. m_localUserIdsByLocalPlayerSlot[existingLocalPlayerSlot] = AzFramework::LocalUserIdNone;
  186. }
  187. LocalUserNotificationBus::Broadcast(&LocalUserNotifications::OnLocalUserIdAssignedToLocalPlayerSlot,
  188. localUserId,
  189. localPlayerSlot,
  190. existingLocalPlayerSlot);
  191. return localPlayerSlot;
  192. }
  193. else
  194. {
  195. // ...and it is occupied, so just return the existing slot
  196. // that the user occupies, which may be LocalPlayerSlotNone.
  197. return existingLocalPlayerSlot;
  198. }
  199. }
  200. if (existingLocalPlayerSlot < LocalPlayerSlotMax)
  201. {
  202. // The user is already assigned to a slot and the requested
  203. // slot is already occupied (or any slot was requested).
  204. return existingLocalPlayerSlot;
  205. }
  206. if (localPlayerSlot == LocalPlayerSlotAny)
  207. {
  208. // The user is not already assigned to a slot, and any slot
  209. // was requested, so assign the user to the first empty slot.
  210. for (AZ::u32 i = 0; i < LocalPlayerSlotMax; ++i)
  211. {
  212. if (m_localUserIdsByLocalPlayerSlot[i] == AzFramework::LocalUserIdNone)
  213. {
  214. m_localUserIdsByLocalPlayerSlot[i] = localUserId;
  215. LocalUserNotificationBus::Broadcast(&LocalUserNotifications::OnLocalUserIdAssignedToLocalPlayerSlot,
  216. localUserId,
  217. i,
  218. LocalPlayerSlotNone);
  219. return i;
  220. }
  221. }
  222. }
  223. // Unable to assign the local user id to the requested local player slot
  224. return LocalPlayerSlotNone;
  225. }
  226. ////////////////////////////////////////////////////////////////////////////////////////////////
  227. AZ::u32 LocalUserSystemComponent::RemoveLocalUserIdFromLocalPlayerSlot(AzFramework::LocalUserId localUserId)
  228. {
  229. const AZ::u32 existingLocalPlayerSlot = GetLocalPlayerSlotOccupiedByLocalUserId(localUserId);
  230. if (existingLocalPlayerSlot < LocalPlayerSlotMax)
  231. {
  232. m_localUserIdsByLocalPlayerSlot[existingLocalPlayerSlot] = AzFramework::LocalUserIdNone;
  233. LocalUserNotificationBus::Broadcast(&LocalUserNotifications::OnLocalUserIdRemovedFromLocalPlayerSlot,
  234. localUserId,
  235. existingLocalPlayerSlot);
  236. }
  237. return existingLocalPlayerSlot;
  238. }
  239. ////////////////////////////////////////////////////////////////////////////////////////////////
  240. AzFramework::LocalUserId LocalUserSystemComponent::GetLocalUserIdAssignedToLocalPlayerSlot(AZ::u32 localPlayerSlot)
  241. {
  242. return localPlayerSlot < LocalPlayerSlotMax ?
  243. m_localUserIdsByLocalPlayerSlot[localPlayerSlot] :
  244. AzFramework::LocalUserIdNone;
  245. }
  246. ////////////////////////////////////////////////////////////////////////////////////////////////
  247. AZ::u32 LocalUserSystemComponent::GetLocalPlayerSlotOccupiedByLocalUserId(AzFramework::LocalUserId localUserId)
  248. {
  249. for (AZ::u32 i = 0; i < LocalPlayerSlotMax; ++i)
  250. {
  251. if (m_localUserIdsByLocalPlayerSlot[i] == localUserId)
  252. {
  253. return i;
  254. }
  255. }
  256. return LocalPlayerSlotNone;
  257. }
  258. ////////////////////////////////////////////////////////////////////////////////////////////////
  259. void LocalUserSystemComponent::ClearAllLocalUserIdToLocalPlayerSlotAssignments()
  260. {
  261. for (AZ::u32 i = 0; i < LocalPlayerSlotMax; ++i)
  262. {
  263. const AzFramework::LocalUserId& localUserId = m_localUserIdsByLocalPlayerSlot[i];
  264. if (localUserId != AzFramework::LocalUserIdNone)
  265. {
  266. RemoveLocalUserIdFromLocalPlayerSlot(localUserId);
  267. }
  268. }
  269. }
  270. } // namespace LocalUser