Skip to content

Commit

Permalink
Merge pull request #80 from EusthEnoptEron/feature/controller-remapping
Browse files Browse the repository at this point in the history
Feature/controller remapping
  • Loading branch information
EusthEnoptEron authored Aug 16, 2022
2 parents 7a7f225 + d7fc7a1 commit 3765a5d
Show file tree
Hide file tree
Showing 26 changed files with 257 additions and 6 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@
## LATEST

- Fixed a bug where the multiplayer camera wouldn't kick in when the second character slot was empty. (#74)
- Improved handling of haptic feedback.
- Added option to show the HP bar of P1 alongside with the others on the right side. (`ShowP1Health`)
- Added use of LED colors on gamepads that support it (e.g. PS5). Blue = 1st, Red = 2nd, Green = 3rd, Purple = 4th
- Added a highly experimental menu that allows changing the controller assignment (opens with <kbd>F2</kbd>). Can be used to disable controllers or move them around or simply debug them.

## 1.0.5

Expand Down
35 changes: 35 additions & 0 deletions Mod/MultiplayerModeCore/InputManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ int InputManager::GetIndexForController(InputHandle_t handle) {
// Found empty spot
Controllers[emptySpot] = handle;
Log::Info("Registering new controller: %p (P%d)", handle, emptySpot+1);
SyncColors();
return emptySpot;
}

Expand All @@ -36,6 +37,8 @@ int InputManager::GetIndexForController(InputHandle_t handle) {

Controllers.push_back(handle);
Log::Info("Registering new controller: %p (P%d)", handle, Controllers.size());
SyncColors();

return Controllers.size() - 1;
}

Expand Down Expand Up @@ -132,6 +135,38 @@ void InputManager::Refresh(GamepadState gamepads[]) {
//SteamAPI_ReleaseCurrentThreadMemory();
}

void InputManager::SyncColors()
{
int i = 0;
for (const auto handle : Controllers) {
uint8 r = 50;
uint8 g = 50;
uint8 b = 50;

if (handle != 0) {
switch (i) {
case 0:
b = 255;
break;
case 1:
r = 255;
break;
case 2:
g = 255;
break;
case 3:
r = 255;
b = 255;
break;
}

Input->SetLEDColor(handle, r, g, b, ESteamInputLEDFlag::k_ESteamInputLEDFlag_SetColor);
}

i++;
}
}

bool InputManager::IsMenuPressed(int index) {
auto handle = GetControllerForIndex(index);
if (handle == 0) return false;
Expand Down
5 changes: 4 additions & 1 deletion Mod/MultiplayerModeCore/InputManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,13 @@ class InputManager
return GetInstance()->Input;
}

void SyncColors();

bool IsMenuPressed(int index);

bool SkipFirstPlayer = false;
std::vector<InputHandle_t> Controllers;

private:
InputManager() {
Input = SteamInput();
Expand Down Expand Up @@ -121,7 +125,6 @@ class InputManager
ISteamInput* Input = nullptr;
InputHandle_t InputHandles[STEAM_INPUT_MAX_COUNT];
int InputHandleCount = 0;
std::vector<InputHandle_t> Controllers;
int FirstPlayerIndex = 0;
int ActiveIndex = 0;

Expand Down
114 changes: 112 additions & 2 deletions Mod/MultiplayerModeCore/Modules/BlueprintProxyModule.cpp
Original file line number Diff line number Diff line change
@@ -1,10 +1,18 @@
#include "BlueprintProxyModule.h"

MultiplayerMod * BlueprintProxyModule::ModRef = nullptr;
MultiplayerMod* BlueprintProxyModule::ModRef = nullptr;
BlueprintProxyModule* BlueprintProxyModule::Instance = nullptr;

static SDK::UClass* controllerDataClazz;
static SDK::UFunction* controllerDataSetId;
static SDK::UFunction* controllerDataGetId;

void BlueprintProxyModule::Initialize(MultiplayerMod* mod)
{
controllerDataClazz = SDK::UClass::FindClass("Class ControllerData.ControllerData_C");
controllerDataSetId = SDK::UFunction::FindObject<SDK::UFunction>("Function ControllerData.ControllerData_C.SetId");
controllerDataGetId = SDK::UFunction::FindObject<SDK::UFunction>("Function ControllerData.ControllerData_C.GetId");

mod->AddBlueprintHook(
"BP_ModHelper.BP_ModHelper_C.Native_GetHudVisibility",
Native_GetHudVisibilityImpl
Expand All @@ -20,6 +28,26 @@ void BlueprintProxyModule::Initialize(MultiplayerMod* mod)
Native_PrintWidgetHierarchyImpl
);

mod->AddBlueprintHook(
"BP_ModHelper.BP_ModHelper_C.Native_GetControllers",
Native_GetControllersImpl
);

mod->AddBlueprintHook(
"BP_ModHelper.BP_ModHelper_C.Native_GetControllerCount",
Native_GetControllerCountImpl
);

mod->AddBlueprintHook(
"BP_ModHelper.BP_ModHelper_C.Native_SetControllers",
Native_SetControllersImpl
);

mod->AddBlueprintHook(
"BP_ModHelper.BP_ModHelper_C.Native_SetVibration",
Native_SetVibrationImpl
);

mod->AddBlueprintHook(
"MultiPlayerController.MultiPlayerController_C.Native_GetPlayerController",
Native_GetPlayerControllerImpl
Expand All @@ -36,7 +64,7 @@ void BlueprintProxyModule::Initialize(MultiplayerMod* mod)
);

ModRef = mod;

Instance = this;
}

void BlueprintProxyModule::Native_GetHudVisibilityImpl(UE4::UObject* Context, UE4::FFrame& Stack, void* result, FNativeFuncPtr processFn)
Expand Down Expand Up @@ -140,6 +168,88 @@ void BlueprintProxyModule::Native_SetProcessImpl(UE4::UObject* Context, UE4::FFr
processFn(Context, Stack, result);
}

void BlueprintProxyModule::Native_GetControllerCountImpl(UE4::UObject* Context, UE4::FFrame& Stack, void* result, FNativeFuncPtr processFn)
{
*((int32_t*)((FOutParmRec*)Stack.OutParms)->PropAddr) = InputManager::GetInstance()->Controllers.size();
}

void BlueprintProxyModule::Native_GetControllersImpl(UE4::UObject* Context, UE4::FFrame& Stack, void* result, FNativeFuncPtr processFn)
{
const SDK::TArray<SDK::UObject*> controllerArray = *(SDK::TArray<SDK::UObject*>*)(((FOutParmRec*)Stack.OutParms)->PropAddr);

//Log::Info("%p [%d] -> %d", controllerArray, controllerArray->Num(), (*(SDK::TArray<SDK::UObject *>**)(&controllerArray))->Num());
if (InputManager::GetInstance()->Controllers.size() != controllerArray.Num())
{
Log::Error("Expected Size: %d, real size: %d", InputManager::GetInstance()->Controllers.size(), controllerArray.Num());
processFn(Context, Stack, result);
return;
}

int i = 0;
for (const auto controller : InputManager::GetInstance()->Controllers) {
if (controller != 0) {
auto controllerData = controllerArray[i];
SDK::FString idString(std::to_wstring(controller).c_str());

Log::Info("Setting id: %s", std::to_wstring(controller).c_str());
controllerData->ProcessEvent(controllerDataSetId, &idString);
}

i++;
}

processFn(Context, Stack, result);
//*((UE4::AActor**)((FOutParmRec*)Stack.OutParms)->PropAddr) = ModRef->InputProcesses[0];
}

void BlueprintProxyModule::Native_SetControllersImpl(UE4::UObject* Context, UE4::FFrame& Stack, void* result, FNativeFuncPtr processFn)
{

const auto inputManager = InputManager::GetInstance();
const auto controllerArray = *(SDK::TArray<SDK::UObject*>*)(((FOutParmRec*)Stack.OutParms)->PropAddr);


inputManager->Controllers.clear();

for (int i = 0; i < controllerArray.Num(); i++) {
const auto obj = controllerArray[i];

if (((SDK::UKismetSystemLibrary *)Context)->STATIC_IsValid(obj)) {
SDK::FString idString;
obj->ProcessEvent(controllerDataGetId, &idString);

const auto handle = std::stoull(idString.ToString());
inputManager->Controllers.push_back(handle);

Log::Info("Pushing %s", idString.ToString().c_str());
}
else {
inputManager->Controllers.push_back(0);
Log::Info("Pushing NULL");
}
}

inputManager->SyncColors();
}

void BlueprintProxyModule::Native_SetVibrationImpl(UE4::UObject* Context, UE4::FFrame& Stack, void* result, FNativeFuncPtr processFn)
{
const auto parms = Stack.GetParams<SetVibrationParms>();
const auto handleString = parms->Id.ToString();
try {
const auto handle = std::stoull(handleString);
const auto isOn = parms->IsOn;

InputManager::GetInstance()->GetInput()->TriggerVibration(handle, isOn ? USHRT_MAX / 4 : 0, isOn ? USHRT_MAX / 4 : 0);
}
catch (std::invalid_argument) {
Log::Info("invalid argument! (%s)", handleString.c_str());
}
catch (std::out_of_range) {
Log::Info("Out of range! (%s)", handleString.c_str());
}
}


void BlueprintProxyModule::PrintHierarchy(SDK::UWidget* widget, int depth) {
Log::Info("%s%s (%s)", std::string(depth * 2, ' ').c_str(), widget->GetName().c_str(), widget->Class->GetName().c_str());
Expand Down
17 changes: 17 additions & 0 deletions Mod/MultiplayerModeCore/Modules/BlueprintProxyModule.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,17 @@
#pragma once
#include "BaseModule.h"

struct SetGetControllersParms {
//SDK::UObject* Context;
int dummy0;
SDK::TArray<SDK::UObject*> Controllers;
int dummy2;
};

struct SetVibrationParms {
SDK::FString Id;
bool IsOn;
};

/// <summary>
/// Module that takes care of the communication between native code and blueprints.
Expand All @@ -20,8 +31,14 @@ class BlueprintProxyModule :
static void Native_GetInputProcessImpl(UE4::UObject* Context, UE4::FFrame& Stack, void* result, FNativeFuncPtr processFn);
static void Native_SetProcessImpl(UE4::UObject* Context, UE4::FFrame& Stack, void* result, FNativeFuncPtr processFn);

static void Native_GetControllerCountImpl(UE4::UObject* Context, UE4::FFrame& Stack, void* result, FNativeFuncPtr processFn);
static void Native_GetControllersImpl(UE4::UObject* Context, UE4::FFrame& Stack, void* result, FNativeFuncPtr processFn);
static void Native_SetControllersImpl(UE4::UObject* Context, UE4::FFrame& Stack, void* result, FNativeFuncPtr processFn);
static void Native_SetVibrationImpl(UE4::UObject* Context, UE4::FFrame& Stack, void* result, FNativeFuncPtr processFn);

static void PrintHierarchy(SDK::UWidget* widget, int depth = 0);

static BlueprintProxyModule *Instance;
static MultiplayerMod* ModRef;
};

1 change: 0 additions & 1 deletion Mod/MultiplayerModeCore/Modules/BoostAttackModule.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ class BoostAttackModule : public BaseModule
static void OnRunBoostAttack(UE4::UObject* Context, UE4::FFrame& Stack, void* result, FNativeFuncPtr processFn);
static void OnAttackBeginEvent(UE4::UObject* Context, UE4::FFrame& Stack, void* result, FNativeFuncPtr processFn);


static MultiplayerMod* ModRef;

static int LastStrikeInitiator;
Expand Down
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,16 @@ If you want them to control separate characters (what you'd expect from multi-pl
- A.) Plug in (or emulate) a second gamepad and ignore the first one.
- B.) Set "FirstPlayerIsKeyboard=1" in the settings. (See [Customization](#customization))

## Changing the Controller Order

By pressing <kbd>F2</kbd> you can open a *highly experimental* menu that lets you rebind the gamepad order (i.e. you can move / disable certain gamepads).

A few things to note:

- You need a mouse to operate the menu.
- You should be in a menu to begin with (e.g. the main menu), otherwise the mouse cursor won't work properly.
- You can drag & drop gamepads where you want them. They should vibrate (somewhat) when you drag them, for identification.

## How to build

see [BUILDING.md](BUILDING.md)
Expand Down
Binary file modified Unreal/Content/Mods/MultiplayerMod/BP_ModHelper.uasset
Binary file not shown.
Binary file modified Unreal/Content/Mods/MultiplayerMod/CharacterHUD.uasset
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file modified Unreal/Content/Mods/MultiplayerMod/ModActor.uasset
Binary file not shown.
Binary file modified Unreal/Content/Mods/MultiplayerMod/MultiPlayerController.uasset
Binary file not shown.
Binary file modified Unreal/Content/Mods/MultiplayerMod/Sprites/StrikeTip.uasset
Binary file not shown.
Binary file not shown.
Binary file modified Unreal/Content/ThirdPersonBP/Blueprints/ModTest.uasset
Binary file not shown.
Binary file not shown.
Binary file modified Unreal/Content/ThirdPersonBP/Maps/ThirdPersonExampleMap.umap
Binary file not shown.
40 changes: 38 additions & 2 deletions Unreal/Source/Arise/Private/AriseGameMode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,43 @@
#include "AriseGameMode.h"


EAriseGameScene AAriseGameMode::GetDisplayScene() { return (EAriseGameScene)0; }

void AAriseGameMode::DebugCamera(bool bEnable)
{
}

EAriseGameScene AAriseGameMode::GetDisplayScene() { return (EAriseGameScene)0; }
void AAriseGameMode::DebugCamera(bool bEnable) {}
void AAriseGameMode::SetSystemPauseRequestEnable(bool bEnable)
{
}

void AAriseGameMode::RequestSystemPause()
{
}

bool AAriseGameMode::IsPausePossibleState()
{
return 0;
}

bool AAriseGameMode::IsSystemPauseRequestEnabled()
{
return 0;
}

void AAriseGameMode::FlushRequestSystemPause()
{
}

int AAriseGameMode::CheckSystemPauseState()
{
return 0;
}

void AAriseGameMode::CancelSystemPause()
{
}

void AAriseGameMode::CancelSystemPauseAll()
{
}
10 changes: 10 additions & 0 deletions Unreal/Source/Arise/Private/BtlCharacterBase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,10 +77,20 @@ bool ABtlCharacterBase::SetPlayerOperation(bool Enable) { return false; }

bool ABtlCharacterBase::SetEnableRestartAI(bool on) { return false; }


void ABtlCharacterBase::SetBtlAIController(class ABtl_AIControllerBase* AIController)
{
}

ABtl_AIControllerBase* ABtlCharacterBase::GetBtlAIController()
{
return 0;
}

void ABtlCharacterBase::RestartAI()
{
}

void ABtlCharacterBase::SetAIFlag(bool Enable, EBtlBitFlagCategory Category)
{
}
Expand Down
22 changes: 22 additions & 0 deletions Unreal/Source/Arise/Public/AriseGameMode.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,5 +48,27 @@ class ARISE_API AAriseGameMode : public AGameMode
UFUNCTION(BlueprintCallable)
void DebugCamera(bool bEnable);

UFUNCTION(BlueprintCallable)
void SetSystemPauseRequestEnable(bool bEnable);

UFUNCTION(BlueprintCallable)
void RequestSystemPause();

UFUNCTION(BlueprintCallable)
bool IsPausePossibleState();

UFUNCTION(BlueprintCallable)
bool IsSystemPauseRequestEnabled();

UFUNCTION(BlueprintCallable)
void FlushRequestSystemPause();

UFUNCTION(BlueprintCallable)
int CheckSystemPauseState();

UFUNCTION(BlueprintCallable)
void CancelSystemPause();

UFUNCTION(BlueprintCallable)
void CancelSystemPauseAll();
};
Loading

0 comments on commit 3765a5d

Please sign in to comment.