Skip to content

Instantly share code, notes, and snippets.

@imniko
Forked from axxag/borderlands3.js
Last active November 22, 2023 11:13
Show Gist options
  • Save imniko/dcbd9a55b132c65885e9c60f285ff0fe to your computer and use it in GitHub Desktop.
Save imniko/dcbd9a55b132c65885e9c60f285ff0fe to your computer and use it in GitHub Desktop.
Borderlands3-Epic-NucleusCoop. This is a forked version of the original to make the script compatible with latest Epic version.
Game.DirSymlinkExclusions = [
"OakGame\\Binaries\\Win64",
];
Game.FileSymlinkExclusions = [
"steam_api64.dll",
"steam_appid.txt",
"xinput1_3.dll"
];
Game.GameName = "Borderlands 3";
Game.HandlerInterval = 100;
Game.SymlinkExe = false;
Game.SymlinkGame = true;
Game.SymlinkFolders = true;
Game.ExecutableName = "Borderlands3.exe";
Game.SteamID = "397540";
Game.GUID = "Borderlands 3";
Game.MaxPlayers = 4;
Game.MaxPlayersOneMonitor = 4;
Game.BinariesFolder = "OakGame\\Binaries\\Win64";
Game.UseNucleusEnvironment = false;
Game.UseGoldberg = true;
//Game.CreateSteamAppIdByExe = true;
Game.LauncherTitle = "splashscreen";
Game.Hook.ForceFocus = true;
Game.Hook.ForceFocusWindowName = "Borderlands® 3";
Game.SetWindowHook = true;
Game.ResetWindows = true;
Game.RefreshWindowAfterStart = true;
Game.Hook.DInputEnabled = false;
Game.Hook.XInputEnabled = true;
Game.Hook.XInputReroute = false;
Game.Hook.CustomDllEnabled = false;
Game.XInputPlusDll = ["xinput1_3.dll"];
Game.UserProfileConfigPath = "Documents\\My Games\\Borderlands 3\\Saved\\Config\\WindowsNoEditor";
Game.UserProfileSavePath = "Documents\\My Games\\Borderlands 3\\Saved\\SaveGames";
Game.Description = "Only works with the Epic Games version. Right click on borderlands3_preflight.ps1 inside Nucleus handlers folder and click Run with Powershell before running this handler. Go to network options, network mode and select LAN in each instance. If you use keyboards and mice after all the instances open and finish setting up press the END key just once to lock the input for all the instances to have their own cursor and kb. Press the END key again to unlock the input when you finish playing. You can also use CTRL+Q to close Nucleus and all its instances when the input is unlocked. Limit the fps for better performance, you can also alt-tab to the Nucleus app window to even out fps, the game windows will still remain on top.";
Game.PauseBetweenContextAndLaunch = 7;
Game.PauseBetweenStarts = 40;
//USS deprecated options:
Game.HookSetCursorPos = false;
Game.HookGetCursorPos = false;
Game.HookGetKeyState = false;
Game.HookGetAsyncKeyState = false;
Game.HookGetKeyboardState = false;
Game.HookFilterRawInput = false;
Game.HookFilterMouseMessages = false;
Game.HookUseLegacyInput = false;
Game.HookDontUpdateLegacyInMouseMsg = false;
Game.HookMouseVisibility = false;
Game.SendNormalMouseInput = false;
Game.SendNormalKeyboardInput = false;
Game.SendScrollWheel = false;
Game.ForwardRawKeyboardInput = false;
Game.ForwardRawMouseInput = false;
Game.HookReRegisterRawInput = false;
Game.HookReRegisterRawInputMouse = false;
Game.HookReRegisterRawInputKeyboard = false;
Game.DrawFakeMouseCursor = false;
//ProtoInput:
Game.SupportsMultipleKeyboardsAndMice = true;
Game.ProtoInput.InjectStartup = false;
Game.ProtoInput.InjectRuntime_RemoteLoadMethod = false;
Game.ProtoInput.InjectRuntime_EasyHookMethod = true;
Game.ProtoInput.InjectRuntime_EasyHookStealthMethod = false;
Game.LockInputAtStart = false;
Game.LockInputSuspendsExplorer = true;
Game.ProtoInput.FreezeExternalInputWhenInputNotLocked = false;
Game.LockInputToggleKey = 0x23;
Game.ProtoInput.RenameHandlesHook = false;
Game.ProtoInput.RenameHandles = [];
Game.ProtoInput.RenameNamedPipes = [];
Game.ProtoInput.RegisterRawInputHook = true;
Game.ProtoInput.GetRawInputDataHook = false;
Game.ProtoInput.MessageFilterHook = true;
Game.ProtoInput.GetCursorPosHook = false;
Game.ProtoInput.SetCursorPosHook = false;
Game.ProtoInput.GetKeyStateHook = false;
Game.ProtoInput.GetAsyncKeyStateHook = false;
Game.ProtoInput.GetKeyboardStateHook = false;
Game.ProtoInput.CursorVisibilityHook = false;
Game.ProtoInput.ClipCursorHook = true;
Game.ProtoInput.FocusHooks = false;
Game.ProtoInput.DrawFakeCursor = false;
Game.ProtoInput.FindWindowHook = false;
Game.ProtoInput.RawInputFilter = false;
Game.ProtoInput.MouseMoveFilter = false;
Game.ProtoInput.MouseActivateFilter = false;
Game.ProtoInput.WindowActivateFilter = false;
Game.ProtoInput.WindowActvateAppFilter = false;
Game.ProtoInput.MouseWheelFilter = false;
Game.ProtoInput.MouseButtonFilter = false;
Game.ProtoInput.KeyboardButtonFilter = false;
Game.ProtoInput.SendMouseWheelMessages = true;
Game.ProtoInput.SendMouseButtonMessages = true;
Game.ProtoInput.SendMouseMovementMessages = true;
Game.ProtoInput.SendKeyboardButtonMessages = true;
Game.ProtoInput.XinputHook = false;
Game.ProtoInput.UseOpenXinput = false;
Game.ProtoInput.UseDinputRedirection = false;
Game.ProtoInput.DinputDeviceHook = false;
Game.ProtoInput.DinputHookAlsoHooksGetDeviceState = false;
Game.ProtoInput.EnableFocusMessageLoop = false;
//Game.ProtoInput.FocusLoopIntervalMilliseconds = 40000;
//Game.ProtoInput.FocusLoop_WM_ACTIVATE = true;
//Game.ProtoInput.FocusLoop_WM_ACTIVATEAPP = true;
//Game.ProtoInput.FocusLoop_WM_NCACTIVATE = true;
//Game.ProtoInput.FocusLoop_WM_SETFOCUS = true;
//Game.ProtoInput.FocusLoop_WM_MOUSEACTIVATE = true;
Game.ProtoInput.BlockedMessages = [ 0x0008 ]; // Blocks WM_KILLFOCUS
Game.Play = function () {
var EpicArgs = 'PREFLIGHT_SCRIPT_WILL_REPLACE';
var GameArgs = " -windowed -AlwaysFocus -nosplash" + Context.IsKeyboardPlayer ? " -NoController" : "";
var Args = GameArgs + EpicArgs;
Context.StartArguments = Args;
var savePath = Context.GetFolder(Nucleus.Folder.Documents) + "\\My Games\\Borderlands 3\\Saved\\Config\\WindowsNoEditor\\GameUserSettings.ini";
Context.ModifySaveFile(savePath, savePath, Nucleus.SaveType.INI, [
new Nucleus.IniSaveInfo("/Script/OakGame.OakGameUserSettings", "FullscreenMode", "2"),
new Nucleus.IniSaveInfo("/Script/OakGame.OakGameUserSettings", "ResolutionSizeX", Context.Width),
new Nucleus.IniSaveInfo("/Script/OakGame.OakGameUserSettings", "ResolutionSizeY", Context.Height)
]);
Game.ProtoInput.OnInputLocked = function() {
for (var i = 0; i < PlayerList.Count; i++) {
var player = PlayerList[i];
ProtoInput.InstallHook(player.ProtoInputInstanceHandle, ProtoInput.Values.GetCursorPosHookID);
ProtoInput.InstallHook(player.ProtoInputInstanceHandle, ProtoInput.Values.SetCursorPosHookID);
ProtoInput.InstallHook(player.ProtoInputInstanceHandle, ProtoInput.Values.GetKeyStateHookID);
ProtoInput.InstallHook(player.ProtoInputInstanceHandle, ProtoInput.Values.GetAsyncKeyStateHookID);
ProtoInput.InstallHook(player.ProtoInputInstanceHandle, ProtoInput.Values.GetKeyboardStateHookID);
ProtoInput.InstallHook(player.ProtoInputInstanceHandle, ProtoInput.Values.CursorVisibilityStateHookID);
ProtoInput.InstallHook(player.ProtoInputInstanceHandle, ProtoInput.Values.GetRawInputDataHookID);
ProtoInput.EnableMessageFilter(player.ProtoInputInstanceHandle, ProtoInput.Values.RawInputFilterID);
// Avoid the mouse move filter unless absolutely necessary as it can massively affect performance if the game gets primary input from mouse move messages
//ProtoInput.EnableMessageFilter(player.ProtoInputInstanceHandle, ProtoInput.Values.MouseMoveFilterID);
ProtoInput.EnableMessageFilter(player.ProtoInputInstanceHandle, ProtoInput.Values.MouseActivateFilterID);
ProtoInput.EnableMessageFilter(player.ProtoInputInstanceHandle, ProtoInput.Values.WindowActivateFilterID);
ProtoInput.EnableMessageFilter(player.ProtoInputInstanceHandle, ProtoInput.Values.WindowActivateAppFilterID);
ProtoInput.EnableMessageFilter(player.ProtoInputInstanceHandle, ProtoInput.Values.MouseWheelFilterID);
ProtoInput.EnableMessageFilter(player.ProtoInputInstanceHandle, ProtoInput.Values.MouseButtonFilterID);
ProtoInput.EnableMessageFilter(player.ProtoInputInstanceHandle, ProtoInput.Values.KeyboardButtonFilterID);
ProtoInput.SetDrawFakeCursor(player.ProtoInputInstanceHandle, true);
ProtoInput.StopFocusMessageLoop(player.ProtoInputInstanceHandle);
}
}
Game.ProtoInput.OnInputUnlocked = function() {
for (var i = 0; i < PlayerList.Count; i++) {
var player = PlayerList[i];
ProtoInput.UninstallHook(player.ProtoInputInstanceHandle, ProtoInput.Values.GetCursorPosHookID);
ProtoInput.UninstallHook(player.ProtoInputInstanceHandle, ProtoInput.Values.SetCursorPosHookID);
ProtoInput.UninstallHook(player.ProtoInputInstanceHandle, ProtoInput.Values.GetKeyStateHookID);
ProtoInput.UninstallHook(player.ProtoInputInstanceHandle, ProtoInput.Values.GetAsyncKeyStateHookID);
ProtoInput.UninstallHook(player.ProtoInputInstanceHandle, ProtoInput.Values.GetKeyboardStateHookID);
ProtoInput.UninstallHook(player.ProtoInputInstanceHandle, ProtoInput.Values.CursorVisibilityStateHookID);
ProtoInput.UninstallHook(player.ProtoInputInstanceHandle, ProtoInput.Values.GetRawInputDataHookID);
ProtoInput.InstallHook(player.ProtoInputInstanceHandle, ProtoInput.Values.FocusHooksHookID);
ProtoInput.DisableMessageFilter(player.ProtoInputInstanceHandle, ProtoInput.Values.RawInputFilterID);
ProtoInput.DisableMessageFilter(player.ProtoInputInstanceHandle, ProtoInput.Values.MouseMoveFilterID);
ProtoInput.DisableMessageFilter(player.ProtoInputInstanceHandle, ProtoInput.Values.MouseActivateFilterID);
ProtoInput.DisableMessageFilter(player.ProtoInputInstanceHandle, ProtoInput.Values.WindowActivateFilterID);
ProtoInput.DisableMessageFilter(player.ProtoInputInstanceHandle, ProtoInput.Values.WindowActivateAppFilterID);
ProtoInput.DisableMessageFilter(player.ProtoInputInstanceHandle, ProtoInput.Values.MouseWheelFilterID);
ProtoInput.DisableMessageFilter(player.ProtoInputInstanceHandle, ProtoInput.Values.MouseButtonFilterID);
ProtoInput.DisableMessageFilter(player.ProtoInputInstanceHandle, ProtoInput.Values.KeyboardButtonFilterID);
ProtoInput.SetDrawFakeCursor(player.ProtoInputInstanceHandle, false);
ProtoInput.StartFocusMessageLoop(player.ProtoInputInstanceHandle, 3000, true, true, false, false, false);
}
}
};
$NC_SCRIPT_PATH = ".\Borderlands3.js"
$NC_SCRIPT_REPLACE_STRING = "PREFLIGHT_SCRIPT_WILL_REPLACE"
$EPIC_LOG_PATH = "$env:USERPROFILE\AppData\Local\EpicGamesLauncher\Saved\Logs\EpicGamesLauncher.log"
try {
Write-Host Checking if NC Script file needs launch args
$containsString = Select-String -Path $NC_SCRIPT_PATH -Pattern $NC_SCRIPT_REPLACE_STRING
If ($null -eq $containsString) {
Write-Host "Launch args already added! Skipping..."
exit
}
}
catch {
Write-Error "Unable to open nucleus script file. Quitting..." -ErrorAction Stop
}
try {
Write-Host Checking if epic logs already contain launch args
$Launch_Info = Get-Content $env:USERPROFILE\AppData\Local\EpicGamesLauncher\Saved\Logs\EpicGamesLauncher.log | `
Where-Object { $_ -like '*FCommunityPortalLaunchAppTask: Launching app*Borderlands3.exe*' } | `
Select-Object -First 1
}
catch {
# Cant find args yet
}
if ($null -eq $Launch_Info) {
# Start an instance of Borderlands 3 using the Epic Launcher
try {
Write-Host Args not found yet. Starting Borderlands 3 using Epic Launcher
Start-Process -FilePath "com.epicgames.launcher://apps/Catnip?action=launch&silent=true"
}
catch {
Write-Error "Unable to launch Borderlands 3 using Epic Launcher. Quitting..." -ErrorAction Stop
}
# Get args needed to launch BL3 offline
try {
# Search through Epic Launcher logs to find the launch parameters for Borderlands 3.
Write-Host Finding offline launch arguments
while (($null -eq $Launch_Info ) -or ($null -eq $bl3_process)) {
Start-Sleep -s 1
$bl3_process = Get-Process -name Borderlands3 -ErrorAction SilentlyContinue
$Launch_Info = Get-Content $EPIC_LOG_PATH | `
Where-Object { $_ -like '*FCommunityPortalLaunchAppTask: Launching app*Borderlands3.exe*' } | `
Select-Object -First 1
}
Start-Sleep -s 15
Write-Host Killing Borderlands3
Stop-Process $bl3_process.Id
}
catch {
Write-Error "Unable to get offline args for Borderlands 3. Quitting..." -ErrorAction Stop
}
}
try {
Write-Host Parsing launch args
# Reformat it, split out the binary/args
$null, $bl3_bin = $Launch_Info -split "Launching app "
$bl3_bin = $bl3_bin -replace "'", ""
$bl3_bin, $bl3_arg = $bl3_bin -split "with commandline "
$bl3_bin = $bl3_bin -replace "Borderlands3.exe", "OakGame/Binaries/Win64/Borderlands3.exe"
# Sanity check- make sure binary exists
$null = Test-Path -Path $bl3_bin -ErrorAction stop
Write-Host Found Borderlands 3 args
Write-Host Writing launch args to script
(Get-Content -Path $NC_SCRIPT_PATH) -replace $NC_SCRIPT_REPLACE_STRING, $bl3_arg | Set-Content -Path $NC_SCRIPT_PATH
}
catch {
Write-Error "Unable to write launch args to NC script. Quitting..." -ErrorAction Stop
}
Write-Host All done!
@Marc-MMA
Copy link

Marc-MMA commented Jun 5, 2022

Nobody has a solution to split screen Steam and Epic without using Virtual Split Screen?

With the tutorial on Reddit, I can't choose the controllers, it's totally buggy. Maybe because I'm on Windows 11 and Virtual Split screen hasn't received an update for a long time. I've been looking for solutions for months...

Maybe I could try with your method by staying on Epic game, but it seems to me that it poses some inconveniences of saves and objects no?

Thanks in advance!

Translated with DeepL

@soanvig
Copy link

soanvig commented Jun 12, 2022

Works quite nice. Strong computer advised.

After putting handlers in the directory, just use "Search game" button and add Borderlands3.exe to library. It wil automatically pickup Epic handler.

@MasterGrunt727
Copy link

MasterGrunt727 commented Jun 27, 2022

Currently unable to focus on both instances equally. so only a single controller will work. Tried alt+tab to Nucleus UI but then neither instance is focused and no controller responds. when selecting a single instance the correct controller responds. Any suggestions what could be wrong?
I am unable to reach the discord from the link posted previously.

Using two XBO controllers

@cholojuanito
Copy link

This is fantastic. I got it to work with a mouse and keyboard in one split and an xbox controller in the other.
I did run into one issue where the first instance would try to connect to Discord and that would cause a stack overflow exception as the second instance was booting up. Force quitting Discord solved that issue and both instances run buttery smooth.

@Offworlder
Copy link

Offworlder commented Jan 16, 2023

Im having issues getting this going, I have the game launched, the handlers added to the correct nucleus folder, but when trying to run borderlands3_preflight in powershell, it just opens and then instantly closes itself without doing anything. Running the borderlands 3 js file gives me
Script: (path)/handlers/borderlands3.js
Line: 1
Char: 1
Error: 'Game' is undefined
Code: 800A1391
Source: Microsoft JScript runtime error
Any help for a person that knows next to nothing about coding would be much appreciated!

@cholojuanito
Copy link

cholojuanito commented Jan 17, 2023

@Offworlder always hard to say with stuff like that. It sounds like there is a typo or you've modified the borderlands3.js file. Also you should be starting the game from the Nucleus launcher, not running the JS file separately.

Because without running it through the Nucleus launcher this script won't have access to the Game object which the first line references it through

1  Game.DirSymlinkExclusions = [ "OakGame\\Binaries\\Win64"];
    ...

@Querzion
Copy link

Screenshot_1
Since there's something wrong with something. I keep getting this error, when I try to start BL3. Only BL3. Question is now... Should I try to re-install the game a fourth time or start dissecting the messages and reinstall the drivers that seems to have gotten broken?

@SMURFING4GAMES
Copy link

when i drag it into handlers is the thing supposed to be there
Capture

@SMURFING4GAMES
Copy link

cuz its not and thats where i get lost

@imniko
Copy link
Author

imniko commented Nov 22, 2023

I dont have the game installed, sorry that I cant help you guys anymore.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment