You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
o3de/Gems/LocalUser/Code/Source/LocalUserSystemComponent.cpp

305 lines
14 KiB
C++

/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <LocalUserSystemComponent.h>
#include <LocalUser/LocalUserNotificationBus.h>
#include <AzFramework/Input/Devices/Gamepad/InputDeviceGamepad.h>
#include <AzCore/RTTI/BehaviorContext.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/Serialization/EditContextConstants.inl>
#include <AzCore/std/string/conversions.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace LocalUser
{
////////////////////////////////////////////////////////////////////////////////////////////////
class LocalUserNotificationBusBehaviorHandler
: public LocalUserNotificationBus::Handler
, public AZ::BehaviorEBusHandler
{
public:
////////////////////////////////////////////////////////////////////////////////////////////
AZ_EBUS_BEHAVIOR_BINDER(LocalUserNotificationBusBehaviorHandler, "{6A3B1CAB-92BE-4773-A3AE-470203D70662}", AZ::SystemAllocator
, OnLocalUserSignedIn
, OnLocalUserSignedOut
, OnLocalUserIdAssignedToLocalPlayerSlot
, OnLocalUserIdRemovedFromLocalPlayerSlot
);
////////////////////////////////////////////////////////////////////////////////////////////
void OnLocalUserSignedIn(AzFramework::LocalUserId localUserId) override
{
Call(FN_OnLocalUserSignedIn, localUserId);
}
////////////////////////////////////////////////////////////////////////////////////////////
void OnLocalUserSignedOut(AzFramework::LocalUserId localUserId) override
{
Call(FN_OnLocalUserSignedOut, localUserId);
}
////////////////////////////////////////////////////////////////////////////////////////////
void OnLocalUserIdAssignedToLocalPlayerSlot(AzFramework::LocalUserId localUserId,
AZ::u32 newLocalPlayerSlot,
AZ::u32 previousLocalPlayerSlot) override
{
Call(FN_OnLocalUserIdAssignedToLocalPlayerSlot, localUserId, newLocalPlayerSlot, previousLocalPlayerSlot);
}
////////////////////////////////////////////////////////////////////////////////////////////
void OnLocalUserIdRemovedFromLocalPlayerSlot(AzFramework::LocalUserId localUserId,
AZ::u32 localPlayerSlot) override
{
Call(FN_OnLocalUserIdRemovedFromLocalPlayerSlot, localUserId, localPlayerSlot);
}
};
////////////////////////////////////////////////////////////////////////////////////////////////
void LocalUserSystemComponent::Reflect(AZ::ReflectContext* context)
{
if (AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context))
{
serialize->Class<LocalUserSystemComponent, AZ::Component>()
->Version(0);
if (AZ::EditContext* ec = serialize->GetEditContext())
{
ec->Class<LocalUserSystemComponent>("LocalUser", "Provides functionality for mapping local user ids to local player slots and managing local user profiles.")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("System"))
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
;
}
}
if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
{
behaviorContext->EBus<LocalUserNotificationBus>("LocalUserNotificationBus")
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All)
->Attribute(AZ::Script::Attributes::Category, "LocalUser")
->Handler<LocalUserNotificationBusBehaviorHandler>()
;
behaviorContext->EBus<LocalUserRequestBus>("LocalUserRequestBus")
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All)
->Attribute(AZ::Script::Attributes::Category, "LocalUser")
->Event("GetMaxLocalUsers", &LocalUserRequestBus::Events::GetMaxLocalUsers)
->Event("IsLocalUserSignedIn", &LocalUserRequestBus::Events::IsLocalUserSignedIn)
->Event("GetLocalUserName", &LocalUserRequestBus::Events::GetLocalUserName)
->Event("AssignLocalUserIdToLocalPlayerSlot", &LocalUserRequestBus::Events::AssignLocalUserIdToLocalPlayerSlot)
->Event("RemoveLocalUserIdFromLocalPlayerSlot", &LocalUserRequestBus::Events::RemoveLocalUserIdFromLocalPlayerSlot)
->Event("GetLocalUserIdAssignedToLocalPlayerSlot", &LocalUserRequestBus::Events::GetLocalUserIdAssignedToLocalPlayerSlot)
->Event("GetLocalPlayerSlotOccupiedByLocalUserId", &LocalUserRequestBus::Events::GetLocalPlayerSlotOccupiedByLocalUserId)
->Event("ClearAllLocalUserIdToLocalPlayerSlotAssignments", &LocalUserRequestBus::Events::ClearAllLocalUserIdToLocalPlayerSlotAssignments)
;
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
void LocalUserSystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
{
provided.push_back(AZ_CRC("LocalUserService"));
}
////////////////////////////////////////////////////////////////////////////////////////////////
void LocalUserSystemComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
{
incompatible.push_back(AZ_CRC("LocalUserService"));
}
////////////////////////////////////////////////////////////////////////////////////////////////
LocalUserSystemComponent::LocalUserSystemComponent()
{
for (AZ::u32 i = 0; i < LocalPlayerSlotMax; ++i)
{
m_localUserIdsByLocalPlayerSlot[i] = AzFramework::LocalUserIdNone;
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
void LocalUserSystemComponent::Activate()
{
m_pimpl.reset(Implementation::Create(*this));
LocalUserRequestBus::Handler::BusConnect();
}
////////////////////////////////////////////////////////////////////////////////////////////////
void LocalUserSystemComponent::Deactivate()
{
ClearAllLocalUserIdToLocalPlayerSlotAssignments();
LocalUserRequestBus::Handler::BusDisconnect();
m_pimpl.reset();
}
////////////////////////////////////////////////////////////////////////////////////////////////
AZStd::shared_ptr<LocalUserProfile> LocalUserSystemComponent::FindLocalUserProfile(AzFramework::LocalUserId localUserId)
{
return m_pimpl ? m_pimpl->FindLocalUserProfile(localUserId) : nullptr;
}
////////////////////////////////////////////////////////////////////////////////////////////////
AZ::u32 LocalUserSystemComponent::GetMaxLocalUsers() const
{
if (m_pimpl)
{
return m_pimpl->GetMaxLocalUsers();
}
// On platforms with no concept of a local user profile the local user id corresponds
// to a unique input device index, so the maximum is the number of supported gamepads.
return AzFramework::InputDeviceGamepad::GetMaxSupportedGamepads();
}
////////////////////////////////////////////////////////////////////////////////////////////////
bool LocalUserSystemComponent::IsLocalUserSignedIn(AzFramework::LocalUserId localUserId)
{
if (m_pimpl)
{
return m_pimpl->IsLocalUserSignedIn(localUserId);
}
// On platforms with no concept of a local user profile the local user id corresponds
// to a unique input device index, and is therefore always considered to be signed in.
return localUserId != AzFramework::LocalUserIdNone;
}
////////////////////////////////////////////////////////////////////////////////////////////////
AZStd::string LocalUserSystemComponent::GetLocalUserName(AzFramework::LocalUserId localUserId)
{
if (m_pimpl)
{
return m_pimpl->GetLocalUserName(localUserId);
}
// On platforms that have no concept of a local user profile, return "Player N" where "N" is
// the local player slot currently occupied by localUserId, otherwise return an empty string.
const AZ::u32 localPlayerSlot = GetLocalPlayerSlotOccupiedByLocalUserId(localUserId);
return (localPlayerSlot < LocalPlayerSlotMax) ?
AZStd::string("Player ") + AZStd::to_string(localPlayerSlot + 1) :
"";
}
////////////////////////////////////////////////////////////////////////////////////////////////
AZ::u32 LocalUserSystemComponent::AssignLocalUserIdToLocalPlayerSlot(AzFramework::LocalUserId localUserId,
AZ::u32 localPlayerSlot)
{
AZ_Warning("LocalUserSystemComponent",
localUserId != AzFramework::LocalUserIdAny,
"Assigning LocalUserIdAny to local player slot %d.\n"
"You should likely prompt the user to sign-in first,\n"
"probably by using InputDevice::PromptLocalUserSignIn\n");
const AZ::u32 existingLocalPlayerSlot = GetLocalPlayerSlotOccupiedByLocalUserId(localUserId);
if (localPlayerSlot < LocalPlayerSlotMax)
{
// A specific slot has been requested...
if (m_localUserIdsByLocalPlayerSlot[localPlayerSlot] == AzFramework::LocalUserIdNone)
{
// ...and it is unoccupied, so assign the user into the slot
// and remove the user from any existing slot it occupied.
m_localUserIdsByLocalPlayerSlot[localPlayerSlot] = localUserId;
if (existingLocalPlayerSlot < LocalPlayerSlotMax)
{
m_localUserIdsByLocalPlayerSlot[existingLocalPlayerSlot] = AzFramework::LocalUserIdNone;
}
LocalUserNotificationBus::Broadcast(&LocalUserNotifications::OnLocalUserIdAssignedToLocalPlayerSlot,
localUserId,
localPlayerSlot,
existingLocalPlayerSlot);
return localPlayerSlot;
}
else
{
// ...and it is occupied, so just return the existing slot
// that the user occupies, which may be LocalPlayerSlotNone.
return existingLocalPlayerSlot;
}
}
if (existingLocalPlayerSlot < LocalPlayerSlotMax)
{
// The user is already assigned to a slot and the requested
// slot is already occupied (or any slot was requested).
return existingLocalPlayerSlot;
}
if (localPlayerSlot == LocalPlayerSlotAny)
{
// The user is not already assigned to a slot, and any slot
// was requested, so assign the user to the first empty slot.
for (AZ::u32 i = 0; i < LocalPlayerSlotMax; ++i)
{
if (m_localUserIdsByLocalPlayerSlot[i] == AzFramework::LocalUserIdNone)
{
m_localUserIdsByLocalPlayerSlot[i] = localUserId;
LocalUserNotificationBus::Broadcast(&LocalUserNotifications::OnLocalUserIdAssignedToLocalPlayerSlot,
localUserId,
i,
LocalPlayerSlotNone);
return i;
}
}
}
// Unable to assign the local user id to the requested local player slot
return LocalPlayerSlotNone;
}
////////////////////////////////////////////////////////////////////////////////////////////////
AZ::u32 LocalUserSystemComponent::RemoveLocalUserIdFromLocalPlayerSlot(AzFramework::LocalUserId localUserId)
{
const AZ::u32 existingLocalPlayerSlot = GetLocalPlayerSlotOccupiedByLocalUserId(localUserId);
if (existingLocalPlayerSlot < LocalPlayerSlotMax)
{
m_localUserIdsByLocalPlayerSlot[existingLocalPlayerSlot] = AzFramework::LocalUserIdNone;
LocalUserNotificationBus::Broadcast(&LocalUserNotifications::OnLocalUserIdRemovedFromLocalPlayerSlot,
localUserId,
existingLocalPlayerSlot);
}
return existingLocalPlayerSlot;
}
////////////////////////////////////////////////////////////////////////////////////////////////
AzFramework::LocalUserId LocalUserSystemComponent::GetLocalUserIdAssignedToLocalPlayerSlot(AZ::u32 localPlayerSlot)
{
return localPlayerSlot < LocalPlayerSlotMax ?
m_localUserIdsByLocalPlayerSlot[localPlayerSlot] :
AzFramework::LocalUserIdNone;
}
////////////////////////////////////////////////////////////////////////////////////////////////
AZ::u32 LocalUserSystemComponent::GetLocalPlayerSlotOccupiedByLocalUserId(AzFramework::LocalUserId localUserId)
{
for (AZ::u32 i = 0; i < LocalPlayerSlotMax; ++i)
{
if (m_localUserIdsByLocalPlayerSlot[i] == localUserId)
{
return i;
}
}
return LocalPlayerSlotNone;
}
////////////////////////////////////////////////////////////////////////////////////////////////
void LocalUserSystemComponent::ClearAllLocalUserIdToLocalPlayerSlotAssignments()
{
for (AZ::u32 i = 0; i < LocalPlayerSlotMax; ++i)
{
const AzFramework::LocalUserId& localUserId = m_localUserIdsByLocalPlayerSlot[i];
if (localUserId != AzFramework::LocalUserIdNone)
{
RemoveLocalUserIdFromLocalPlayerSlot(localUserId);
}
}
}
} // namespace LocalUser