Skip to content
Beta: this is a preview of the new SplitScreen.Me. Data may be reset before launch.

Titanfall 2 · handler “Titanfall 2 + Northstar Mod”

Version 1

Release notes

First handler release

Released
Size
6.6 MB
Downloads
944
Check
Verified
SHA-256
1b91463e087e98624105c36062bf597b93587d83ea7deea6e3bba90c865a39ff
Download v1 (.nc)All versions

Files 27

PathSize
handler.js27 KB
Titanfall 2 + Northstar/connectinstances.ahk1.6 KB
Titanfall 2 + Northstar/connectinstances.exe1.2 MB
Titanfall 2 + Northstar/mapimgs/angelcity.png273 KB
Titanfall 2 + Northstar/mapimgs/blackwatercanal.png273 KB
Titanfall 2 + Northstar/mapimgs/boomtown.png275 KB
Titanfall 2 + Northstar/mapimgs/coliseum.png221 KB
Titanfall 2 + Northstar/mapimgs/colony.png250 KB
Titanfall 2 + Northstar/mapimgs/complex.png268 KB
Titanfall 2 + Northstar/mapimgs/crashsite.png284 KB
Titanfall 2 + Northstar/mapimgs/deck.png246 KB
Titanfall 2 + Northstar/mapimgs/drydock.png244 KB
Titanfall 2 + Northstar/mapimgs/eden.png292 KB
Titanfall 2 + Northstar/mapimgs/exoplanet.png302 KB
Titanfall 2 + Northstar/mapimgs/forwardbasekodai.png279 KB
Titanfall 2 + Northstar/mapimgs/glitch.png249 KB
Titanfall 2 + Northstar/mapimgs/homestead.png243 KB
Titanfall 2 + Northstar/mapimgs/lobby.png170 KB
Titanfall 2 + Northstar/mapimgs/meadow.png283 KB
Titanfall 2 + Northstar/mapimgs/pillars.png203 KB
Titanfall 2 + Northstar/mapimgs/relic.png234 KB
Titanfall 2 + Northstar/mapimgs/rise.png250 KB
Titanfall 2 + Northstar/mapimgs/stacks.png310 KB
Titanfall 2 + Northstar/mapimgs/township.png295 KB
Titanfall 2 + Northstar/mapimgs/traffic.png259 KB
Titanfall 2 + Northstar/mapimgs/uma.png247 KB
Titanfall 2 + Northstar/mapimgs/wargames.png283 KB

handler.js

Plain text
Hub.Handler.Version = 1; // Released at https://hub.splitscreen.me/ on Tue Oct 04 2022 21:48:41 GMT+0000 (Coordinated Universal Time).
Hub.Handler.Id = "M2MbhbsjCj5Zj4vvg";
Hub.Maintainer.Name = "LordVGames";
Hub.Maintainer.Id = "r9YaWrFtkQWGHLaDX";

//import './lib/nucleus_coop';

//#################### Game Info ####################
Game.ExecutableContext = ["Titanfall2.exe", "Northstar.dll", "R2Northstar"];
Game.GameName = "Titanfall 2";
Game.GUID = "Titanfall2";
Game.SteamID = "1237970";
Game.ExecutableName = "NorthstarLauncher.exe";
Game.LauncherTitle = "";
Game.HandlerInterval = 100;
Game.NeedsSteamEmulation = false;
Game.CreateSteamAppIdByExe = false;
Game.MaxPlayersOneMonitor = 4;
Game.MaxPlayers = 16;
Game.Hook.ForceFocus = true;
Game.Hook.ForceFocusWindowName = "Titanfall 2";
Game.IgnoreWindowBordercheck = true;
Game.DPIHandling = Nucleus.DPIHandling.InvScaled;
Game.KeyboardPlayerFirst = true;
Game.PromptAfterFirstInstance = false;
Game.KillProcessesOnClose = ["connectinstances"]; //Get rid of the auto-connect ahk script if it's still running somehow

//#################### File System ####################
Game.SymlinkExe = true;
Game.SymlinkGame = true;
Game.SymlinkFolders = true;
//Certain parts of northstar's core mods need to be copied, or the game will say they're missing/empty for some reason
Game.DirSymlinkCopyInstead = [
  "r2\\cfg",
  "R2Northstar\\mods\\Northstar.Custom\\keyvalues",
  "R2Northstar\\mods\\Northstar.CustomServers\\mod\\scripts",
  "R2Northstar\\mods\\Northstar.CustomServers\\mod\\cfg"
];
//These are the old values, leaving this here since the list of copied folders list has changed since I started this handler, and may change again in the future.
//Game.DirSymlinkCopyInstead = ["r2\\cfg", "R2Northstar\\mods\\Northstar.Client", "R2Northstar\\mods\\Northstar.Custom\\mod", "R2Northstar\\mods\\Northstar.Custom\\keyvalues", "R2Northstar\\mods\\Northstar.CustomServers\\mod\\scripts", "R2Northstar\\mods\\Northstar.CustomServers\\mod\\cfg"];
Game.DirSymlinkCopyInsteadIncludeSubFolders = true;
Game.FileSymlinkExclusions = ["xinput1_3.dll", "xinput9_1_0.dll"];
Game.DirExclusions = ["__Installer", "Support"];
Game.XInputPlusDll = ["xinput1_3.dll", "xinput9_1_0.dll"];
Game.Description =
  "NOTE: You will need Origin started up beforehand, logged in with the origin account that owns Titanfall 2. " +
  'After the auto-connect, players can join via the server browser, with a server named "Nucleus Co-op server for *nickname*!". ' +
  "Keyboard/mouse players can do the same, or use the `con` command in their game console. " +
  "If a player wants their loadouts back, look on the thunderstore for a Northstar mod I made, and use per-player config files (i.e. config_player1.cfg) with it. " +
  'If you get an "INVALID_MASTERSERVER_TOKEN" error, it means the Northstar masterserver is down, so you\'ll just have to try again later. ';
'If you get an "Authentication Failed" error when joining through the server browser, go back to the main menu and try again. ' +
  "If it's someone else's server you're joining, they likely don't allow non-auth connections, so you'll have to find one that does.";
Game.PauseBetweenStarts = 25;

Game.Hook.CustomDllEnabled = false;
Game.Hook.EnableMKBInput = true;
Game.SupportsMultipleKeyboardsAndMice = true;

//#region 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.DrawFakeMouseCursor = false;
//#endregion

Game.LockInputAtStart = false;
Game.LockInputSuspendsExplorer = true;
Game.ProtoInput.FreezeExternalInputWhenInputNotLocked = true;
Game.LockInputToggleKey = 0x23;

Game.ProtoInput.InjectStartup = false;
Game.ProtoInput.InjectRuntime_RemoteLoadMethod = false;
Game.ProtoInput.InjectRuntime_EasyHookMethod = true;
Game.ProtoInput.InjectRuntime_EasyHookStealthMethod = false;

// This hook should always be on, regardless of input lock
Game.ProtoInput.RegisterRawInputHook = true;

// This can always be enabled, since we are going to allow/disallow raw input with the bypass function
Game.ProtoInput.GetRawInputDataHook = true;

// We will be installing/uninstalling the filters manually so there's no need to
Game.ProtoInput.MessageFilterHook = true;
Game.ProtoInput.ClipCursorHook = true;
Game.ProtoInput.FocusHooks = true;

// These aren't problematic to leave on, since the external "freeze fake input input" when real input ISN'T locked will prevent these from interfering
Game.ProtoInput.SendMouseWheelMessages = true;
Game.ProtoInput.SendMouseButtonMessages = true;
Game.ProtoInput.SendMouseMovementMessages = true;
Game.ProtoInput.SendKeyboardButtonMessages = true;

//#region Handled on input lock/unlock, don't enable this by default
//Game.ProtoInput.EnableFocusMessageLoop = false;
//Game.ProtoInput.FocusLoopIntervalMilliseconds = 5;
//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;
//#endregion

Game.ProtoInput.AutoHideTaskbar = true;

Game.ProtoInput.BlockedMessages = [0x0008]; // Blocks WM_KILLFOCUS

Game.ProtoInput.RenameHandlesHook = false;
Game.ProtoInput.RenameHandles = [];
Game.ProtoInput.RenameNamedPipes = [];

// We can leave these on and not worry about input lock.
// It doesn't matter if controller input still works since it won't get in the way
Game.ProtoInput.XinputHook = true;
Game.ProtoInput.UseOpenXinput = true;
Game.ProtoInput.UseDinputRedirection = false;

// The old Nucleus options. Don't use these with the Xinput hook
Game.Hook.DInputEnabled = false;
Game.Hook.DInputForceDisable = false;
Game.Hook.XInputEnabled = false;
Game.Hook.XInputReroute = false;
Game.Hook.CustomDllEnabled = false;

Game.SaveAndEditCustomUserGeneralValues = false;
Game.CustomUserGeneralPrompts = ["To start the game server with a password, enter it in the textbox. Leave it blank for no password."];

//#region ######## Options ########
var GamemodeAnswers = [
  "Skirmsh",
  "Attrition",
  "Amped Hardpoint",
  //Disabled in-game for some reason
  //"Bounty Hunt",
  "Capture the Flag",
  "Last Titan Standing",
  "Pilots vs. Pilots",
  "Live Fire",
  "Marked for Death",
  "Titan Brawl",
  "Free for All",
  "Free Agents",
  "Coliseum",
  "Aegis Titan Brawl",
  "Turbo Titan Brawl",
  "Aegis LTS",
  "Turbo LTS",
  "Rocket Arena",
  "The Great Bamboozle",
  "One in the Chamber",
  "The Hidden",
  "Sticks and Stones",
  "Gun Game",
  "Titan Tag",
  "Infection",
  "Amped Killrace",
  "Fastball",
  "Hide and Seek",
  "Competitive CTF",
  "Titan Free for All"
  //Not fully working yet
  /* "Frontier Defense: Easy",
	"Frontier Defense: Regular",
	"Frontier Defense: Hard",
	"Frontier Defense: Master" */
];
Game.AddOption(
  "Gamemode",
  "Select the gamemode. Note that certain modes wil NOT work properly on certain maps. For example, titan-based modes will most likely not work on live fire maps, nor the coliseum maps.",
  "GamemodeOption",
  GamemodeAnswers
);

var TimeLimitAnswers = ["Use default", "10", "5", "15", "20", "25", "30", "60", "1 day"];
Game.AddOption("Match TimeLimit", "Match TimeLimit in minutes.", "TimeLimitOption", TimeLimitAnswers);

var ScoreLimitAnswers = ["Use default", "25", "5", "10", "15", "20", "30", "40", "50", "100", "200", "250", "300", "400", "500", "600", "700", "750", "1000", "9999"];
Game.AddOption(
  "Score limit",
  "The score/frag limit, based on the mode. Note that some gamemodes have score limits higher than others, i.e. attrition works best with a scorelimit of 500-750, while skirmish works best with a scorelimit of around 30.",
  "ScoreLimitOption",
  ScoreLimitAnswers
);

var OutOfBoundsEnabledAnswers = ["Yes", "No"];
Game.AddOption("Out of bounds timer", "Should the out of bounds timer be enabled?", "OutOfBoundsEnabledOption", OutOfBoundsEnabledAnswers);

var ClassicMPEnabledAnswers = ["Yes", "No"];
Game.AddOption("Map intro", "Should the map intro be enabled? This only works when starting a map through the lobby.", "ClassicMPEnabledOption", ClassicMPEnabledAnswers);

var EpilogueEnabledAnswers = ["Yes", "No"];
Game.AddOption("Dropship escape", "Should the dropship escape sequence be enabled? May only work in certain gamemodes.", "EpilogueEnabledOption", EpilogueEnabledAnswers);

var ReturnToLobbyAnswers = ["Yes", "No"];
Game.AddOption(
  "Return to lobby",
  "Should the server return to the private lobby after a match is finished? If not, it will go to the next map/mode in the playlist.",
  "ReturnToLobbyOption",
  ReturnToLobbyAnswers
);

var LANOnlyAnswers = ["No", "Yes"];
Game.AddOption("LAN Only", "Should the server not be public, and only visible on the LAN?", "LANOnlyOption", LANOnlyAnswers);

var MapList = [
  {
    Name: "None",
    Details: "Do not start the first instance in a map, and leave it in the main menu. You will then have to go into a private lobby for the other instances to join.",
    Console: "none",
    ImageUrl: ""
  },
  {
    Name: "Lobby",
    Details: "Start the first instance in the lobby. It isn't started in the private match lobby, so you must click on the 'Private Match' button to get into a joinable lobby.",
    Console: "mp_lobby",
    ImageUrl: "mapimgs\\lobby.png"
  },
  { Name: "Angel City", Details: "Start the first instance in the 'Angel City' map.", Console: "mp_angel_city", ImageUrl: "mapimgs\\angelcity.png" },
  { Name: "Black Water Canal", Details: "Start the first instance in the 'Black Water Canal' map.", Console: "mp_black_water_canal", ImageUrl: "mapimgs\\blackwatercanal.png" },
  { Name: "Boomtown", Details: "Start the first instance in the 'Boomtown' map.", Console: "mp_grave", ImageUrl: "mapimgs\\boomtown.png" },
  { Name: "Colony", Details: "Start the first instance in the 'Colony' map.", Console: "mp_colony02", ImageUrl: "mapimgs\\colony.png" },
  { Name: "Complex", Details: "Start the first instance in the 'Complex' map.", Console: "mp_complex3", ImageUrl: "mapimgs\\complex.png" },
  { Name: "Crashsite", Details: "Start the first instance in the 'Crashsite' map.", Console: "mp_crashsite3", ImageUrl: "mapimgs\\crashsite.png" },
  { Name: "DryDock", Details: "Start the first instance in the 'DryDock' map.", Console: "mp_drydock", ImageUrl: "mapimgs\\drydock.png" },
  { Name: "Eden", Details: "Start the first instance in the 'Eden' map.", Console: "mp_eden", ImageUrl: "mapimgs\\eden.png" },
  { Name: "Exoplanet", Details: "Start the first instance in the 'Exoplanet' map.", Console: "mp_thaw", ImageUrl: "mapimgs\\exoplanet.png" },
  { Name: "Forward Base Kodai", Details: "Start the first instance in the 'Forward Base Kodai' map.", Console: "mp_forwardbase_kodai", ImageUrl: "mapimgs\\forwardbasekodai.png" },
  { Name: "Glitch", Details: "Start the first instance in the 'Glitch' map.", Console: "mp_glitch", ImageUrl: "mapimgs\\glitch.png" },
  { Name: "Homestead", Details: "Start the first instance in the 'Homestead' map.", Console: "mp_homestead", ImageUrl: "mapimgs\\homestead.png" },
  { Name: "Relic", Details: "Start the first instance in the 'Relic' map.", Console: "mp_relic02", ImageUrl: "mapimgs\\relic.png" },
  { Name: "Rise", Details: "Start the first instance in the 'Rise' map.", Console: "mp_rise", ImageUrl: "mapimgs\\rise.png" },
  { Name: "Wargames", Details: "Start the first instance in the 'Wargames' map.", Console: "mp_wargames", ImageUrl: "mapimgs\\wargames.png" },
  { Name: "Deck", Details: "Start the first instance in the 'Deck' live fire map.", Console: "mp_lf_deck", ImageUrl: "mapimgs\\deck.png" },
  { Name: "Meadow", Details: "Start the first instance in the 'Meadow' live fire map.", Console: "mp_lf_meadow", ImageUrl: "mapimgs\\meadow.png" },
  { Name: "Stacks", Details: "Start the first instance in the 'Stacks' live fire map.", Console: "mp_lf_stacks", ImageUrl: "mapimgs\\stacks.png" },
  { Name: "Township", Details: "Start the first instance in the 'Township' live fire map.", Console: "mp_lf_township", ImageUrl: "mapimgs\\township.png" },
  { Name: "Traffic", Details: "Start the first instance in the 'Traffic' live fire map.", Console: "mp_lf_traffic", ImageUrl: "mapimgs\\traffic.png" },
  { Name: "UMA", Details: "Start the first instance in the 'UMA' live fire map.", Console: "mp_lf_uma", ImageUrl: "mapimgs\\uma.png" },
  { Name: "The Coliseum", Details: "Start the first instance in the 'The Coliseum' coliseum map.", Console: "mp_coliseum", ImageUrl: "mapimgs\\coliseum.png" },
  { Name: "Pillars", Details: "Start the first instance in the 'Pillars' coliseum map.", Console: "mp_coliseum_column", ImageUrl: "mapimgs\\pillars.png" }
];

Game.AddOption("Starting Map", "The map the first instance will start in.", "MapName", MapList);
Game.ShowOptionAsStep("MapName", true, "Select a Map");
//#endregion

Game.Play = function() {
  Context.RunAdditionalFiles(["2|" + Game.Folder + "\\connectinstances.exe"], false, 0); //Start our auto-connect ahk script once the first instance is already up
  var PlayerNum = Context.PlayerID + 1;

  //#region ################### File, folder and options setup ####################
  var InstanceFolder = Context.GetFolder(Nucleus.Folder.InstancedGameFolder);
  var CfgFolder = InstanceFolder + "\\r2\\cfg";
  var AutoexecFile = CfgFolder + "\\autoexec.cfg";

  //Create a blank autoexec file incase we don't find one
  if (!System.IO.File.Exists(AutoexecFile)) {
    Context.WriteTextFile(AutoexecFile, [""]);
  }
  //Keep the original autoexec file intact by copying it into another file, since we're modifying the original autoexec later
  System.IO.File.Copy(AutoexecFile, CfgFolder + "\\autoexec_original.cfg", true);
  //Get rid of config files unneeded for the current player's instance (i.e. removing player2's config from player1's instance files)
  for (var i = 0; i < Context.NumberOfPlayers; i++) {
    if (i != Context.PlayerID && System.IO.File.Exists(CfgFolder + "\\config_player" + (i + 1) + ".cfg")) {
      System.IO.File.Delete(CfgFolder + "\\config_player" + (i + 1) + ".cfg");
    }
  }
  var ServerAutoexecFile = CfgFolder + "\\nucleusconfig_server.cfg";

  var TimeLimitChoice = Context.Options["TimeLimitOption"];
  var ScoreLimitChoice = Context.Options["ScoreLimitOption"];
  var OutOfBoundsEnabledChoice = Context.Options["OutOfBoundsEnabledOption"];
  var ClassicMPEnabledChoice = Context.Options["ClassicMPEnabledOption"];
  var EpilogueEnabledChoice = Context.Options["EpilogueEnabledOption"];
  var ReturnToLobbyChoice = Context.Options["ReturnToLobbyOption"];
  var LANOnlyChoice = Context.Options["LANOnlyOption"];
  var GamemodeChoice = "";
  switch (Context.Options["GamemodeOption"]) {
    case "Skirmish":
      GamemodeChoice = "tdm";
      break;
    case "Attrition":
      GamemodeChoice = "aitdm";
      break;
    case "Amped Hardpoint":
      GamemodeChoice = "cp";
      break;
    case "Bounty Hunt":
      GamemodeChoice = "at";
      break;
    case "Capture the Flag":
      GamemodeChoice = "ctf";
      break;
    case "Last Titan Standing":
      GamemodeChoice = "lts";
      break;
    case "Pilots vs. Pilots":
      GamemodeChoice = "ps";
      break;
    case "Live Fire":
      GamemodeChoice = "speedball";
      break;
    case "Marked for Death":
      GamemodeChoice = "mfd";
      break;
    case "Titan Brawl":
      GamemodeChoice = "ttdm";
      break;
    case "Free for All":
      GamemodeChoice = "ffa";
      break;
    case "Free Agents":
      GamemodeChoice = "fra";
      break;
    case "Coliseum":
      GamemodeChoice = "coliseum";
      break;
    case "Aegis Titan Brawl":
      GamemodeChoice = "attdm";
      break;
    case "Turbo Titan Brawl":
      GamemodeChoice = "turbo_ttdm";
      break;
    case "Aegis LTS":
      GamemodeChoice = "alts";
      break;
    case "Turbo LTS":
      GamemodeChoice = "turbo_lts";
      break;
    case "Rocket Arena":
      GamemodeChoice = "rocket_lf";
      break;
    case "The Great Bamboozle":
      GamemodeChoice = "holopilot_lf";
      break;
    case "One in the Chamber":
      GamemodeChoice = "chamber";
      break;
    case "The Hidden":
      GamemodeChoice = "hidden";
      break;
    case "Sticks and Stones":
      GamemodeChoice = "sns";
      break;
    case "Gun Game":
      GamemodeChoice = "gg";
      break;
    case "Titan Tag":
      GamemodeChoice = "tt";
      break;
    case "Infection":
      GamemodeChoice = "inf";
      break;
    case "Amped Killrace":
      GamemodeChoice = "kr";
      break;
    case "Fastball":
      GamemodeChoice = "fastball";
      break;
    case "Hide and Seek":
      GamemodeChoice = "hs";
      break;
    case "Competitive CTF":
      GamemodeChoice = "ctf_comp";
      break;
    case "Titan Free for All":
      GamemodeChoice = "tffa";
      break;
    case "Frontier Defense: Easy":
      GamemodeChoice = "fd_easy";
      break;
    case "Frontier Defense: Regular":
      GamemodeChoice = "fd_normal";
      break;
    case "Frontier Defense: Hard":
      GamemodeChoice = "fd_hard";
      break;
    case "Frontier Defense: Master":
      GamemodeChoice = "fd_master";
      break;
    case "Frontier Defense: Insane":
      GamemodeChoice = "fd_insane";
      break;
  }
  //#endregion

  //#region ################### Config settings setup ####################
  //We insert a line into the autoexec_ns_server.cfg file to exec our own cfg to override the original cfg's cvars
  //Searching for the end of a comment on the last line in the file
  var EndOfServerConfig = Context.FindLineNumberInTextFile(
    InstanceFolder + "\\R2Northstar\\mods\\Northstar.CustomServers\\mod\\cfg\\autoexec_ns_server.cfg",
    "client.dll edit",
    Nucleus.SearchType.Contains
  );
  //Replace it with itself + a line break and our line to exec our override cfg file
  Context.ReplacePartialLinesInTextFile(InstanceFolder + "\\R2Northstar\\mods\\Northstar.CustomServers\\mod\\cfg\\autoexec_ns_server.cfg", [
    EndOfServerConfig + "|client.dll edit|client.dll edit\nexec nucleusconfig_server"
  ]);

  var LANOnlyResult;
  if (LANOnlyChoice == "Yes") {
    LANOnlyResult = "0";
  } else {
    LANOnlyResult = "1";
  }
  var ServerOverrideCfgLines = [
    "ns_auth_allow_insecure 1",
    "ns_report_server_to_masterserver " + LANOnlyResult,
    'ns_server_name "Nucleus Co-op server for ' + Context.Nickname + '!"',
    'ns_server_desc "Server for a nucleus co-op session for ' + Context.Nickname + '."'
  ];

  //Base autoexec lines
  if (Context.CustomUserGeneralValues[0] != null) {
    var ConnectAlias = 'alias con "connect ' + Context.LocalIP + ":37015 " + Context.CustomUserGeneralValues[0] + '"'; //alias-ed connect command w/ server password
  } else {
    var ConnectAlias = 'alias con "connect ' + Context.LocalIP + ':37015"'; //alias-ed connect command w/o server password
  }
  var AutoexecLines = [
    "exec autoexec_original",
    ConnectAlias,
    "not_focus_sleep 0", //This disables fps drop when not in focus
    "sound_without_focus 1",
    "ns_private_match_only_host_can_change_settings 1",
    "ns_private_match_only_host_can_start 1",
    "ns_private_match_countdown_length 5",
    "ns_allow_kill_commands 1",
    "exec config_player" + PlayerNum
  ];

  //Inserting cvars based on the nucleus options
  var MapInternalName = Context.Options["MapName"].Console;
  if (ReturnToLobbyChoice == "No") {
    ServerOverrideCfgLines.push("ns_should_return_to_lobby 0");
  } else {
    ServerOverrideCfgLines.push("ns_should_return_to_lobby 1");
  }
  if (Context.CustomUserGeneralValues[0] != null) {
    ServerOverrideCfgLines.push('ns_server_password "' + Context.CustomUserGeneralValues[0] + '"');
  }
  if (MapInternalName != "none") {
    AutoexecLines.push("ns_private_match_last_map " + MapInternalName);
  } else {
    Game.PromptAfterFirstInstance = true; //Give them time to get to the lobby by pausing before starting up the rest of the instances.
  }
  if (GamemodeChoice != "tdm") {
    AutoexecLines.push("ns_private_match_last_mode " + GamemodeChoice);
  }

  Context.WriteTextFile(AutoexecFile, AutoexecLines);
  Context.WriteTextFile(ServerAutoexecFile, ServerOverrideCfgLines);
  //#endregion

  //#region ################### Launch arguement setup ####################
  var BaseStartArgs = " -multiple -noborder -window -novid -nohltv -tvdisable -nomenuvid -allowdupeaccounts -w " + Context.Width + " -h " + Context.Height + " +exec autoexec";
  if (Context.PlayerID == 0) {
    //First instance is the one that auto-hosts
    var HostStartArgs = BaseStartArgs;
    HostStartArgs += " -clientport 37005"; //Different client port to have server visible + joinable for other instances through server browser

    //#region setplaylistvaroverrides
    HostStartArgs += ' +setplaylistvaroverrides "'; //This always gets applied
    if (TimeLimitChoice != "Use default") {
      if (TimeLimitChoice == "1 day") {
        TimeLimitChoice = "1440"; //1440 minutes is 24 hours
      }
      HostStartArgs += "timelimit " + TimeLimitChoice + " ";
    }
    if (OutOfBoundsEnabledChoice == "Yes") {
      HostStartArgs += "oob_timer_enabled 1 ";
    } else {
      HostStartArgs += "oob_timer_enabled 0 ";
    }
    if (ClassicMPEnabledChoice == "Yes") {
      HostStartArgs += "classic_mp 0 ";
    } else {
      HostStartArgs += "classic_mp 1 ";
    }
    if (EpilogueEnabledChoice == "Yes") {
      HostStartArgs += "run_epilogue 1";
    } else {
      HostStartArgs += "run_epilogue 0";
    }
    if (ScoreLimitChoice != "Use default") {
      HostStartArgs += "scorelimit " + ScoreLimitChoice + " ";
    }
    HostStartArgs += '"';
    //#endregion

    if (MapInternalName != "none") {
      HostStartArgs += " +map " + MapInternalName;
    }
    if (GamemodeChoice != "tdm") {
      Context.StartArguments = HostStartArgs + " +mp_gamemode " + GamemodeChoice;
    }
  } else {
    Context.StartArguments = BaseStartArgs; //I would use the +connect launch arg, but it doesn't work with northstar
  }
  //#endregion
};

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.FocusHooksHookID);

    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 moessages
    //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.StartFocusMessageLoop(player.ProtoInputInstanceHandle, 5, true, true, true, true, true);

    // Disable the bypass: let the input be processed by Proto Input
    ProtoInput.SetRawInputBypass(player.ProtoInputInstanceHandle, false);
  }
};

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);

    // Intentionally disable focus so all the instances don't respond to input at the same time
    ProtoInput.UninstallHook(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);

    // Intentionally disable focus so all the instances don't respond to input at the same time
    ProtoInput.StopFocusMessageLoop(player.ProtoInputInstanceHandle);

    // Enable the bypass: allow any raw input to pass
    ProtoInput.SetRawInputBypass(player.ProtoInputInstanceHandle, true);
  }
};