Skip to content

Instantly share code, notes, and snippets.

@JakubNei
Last active June 29, 2016 07:34
Show Gist options
  • Save JakubNei/7a8c305289c03cb6223f to your computer and use it in GitHub Desktop.
Save JakubNei/7a8c305289c03cb6223f to your computer and use it in GitHub Desktop.
Arma 3 game dedicated server utility is used to start server, to automatically set -mod folders and -port, you can configure it in config file, all [varName] is replaced with contents of matching <varName></varName>, replacing is done in few iterations to propagate dependencies, has two inbuild variables: [port] is taken from the containing fold…
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<!--
this utility is used to start server, to automatically set -mod folders and -port
this utility was designed to be right next to the <executable>
you can configure it in this config file, all [varName] is replaced with contents of matching <varName></varName>
replacing is done in few iterations to propagate dependencies
has two inbuild variables
[port] is taken from the containing folder name port_theRestOfTheName
[modFolders] is a ; seperated list of all top level folders starting with @
Sometimes you want to prioritize some modsFolders, if so put them into <modFoldersPriority> seperated by new line.
If modFolder is present it will be loaded before others. Top is highest priority.
-->
<profilesFolder>server_configs</profilesFolder>
<profileName>server_profile</profileName>
<serverConfigMain>[profilesFolder]\TAWServer.cfg</serverConfigMain>
<serverConfigBasic>[profilesFolder]\TAWbasic.cfg</serverConfigBasic>
<executable>arma3server.exe</executable>
<arguments>
-port=[port]
-skipIntro
-noSplash
-world=empty
"-config=[serverConfigMain]"
"-cfg=[serverConfigBasic]"
"-profiles=[profilesFolder]"
"-name=[profileName]"
-noSound
-autoInit
-loadMissionToMemory
"-mod=[modFolders]"
</arguments>
<modFoldersPriority>
@TAW_SERVER_CORE
@TAW_DIV_CORE
@TAW_SERVER_CONTENT
@TAW_AM2_CONTENT
@AllInArmaTerrainPack
@TAW_AM2_MAPS
</modFoldersPriority>
</configuration>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Threading.Tasks;
using System.Diagnostics;
using System.Threading;
using System.ComponentModel;
using System.Xml;
using System.Xml.Linq;
using System.Runtime.InteropServices;
namespace start_server
{
class Program
{
static void Main(string[] args)
{
var p = new Program();
p.Run();
}
public void Run() {
/*
example param line:
arma3server.exe
-port=2382
-skipIntro
-noSplash
-world=empty
-config=insurgency_mandatory_modpack_Configs\TAWServer.cfg
-cfg=insurgency_mandatory_modpack_Configs\TAWbasic.cfg
-profiles=insurgency_mandatory_modpack_Configs
-name=server_profile
-noSound
-autoInit
-loadMissionToMemory
-mod=@TAW_SERVER_CORE;@TAW_SERVER_CONTENT;
*/
var myExePath = Path.GetFullPath(Environment.GetCommandLineArgs()[0]);
var myExeFileName = Path.GetFileName(myExePath);
var myExeDir = Path.GetDirectoryName(myExePath);
var myExeTopDirName = GetDeepestFolderInPath(myExeDir);
var modFolders = Directory.GetDirectories(myExeDir, "@*", SearchOption.TopDirectoryOnly).Select(f => GetDeepestFolderInPath(f)).ToArray();
int port = 0;
if (myExeTopDirName.Contains('_')) port = int.Parse(myExeTopDirName.Substring(0, myExeTopDirName.IndexOf("_")));
else throw new System.InvalidOperationException(myExeTopDirName + " folder name does not contain _ the dir name should be port_therest, where port is the server port");
Dictionary<string, string> values = new Dictionary<string,string>();
var xml = XDocument.Load(myExeFileName + ".config");
var configuration = xml.Element("configuration");
// load all values from xml config
foreach (var e in configuration.Elements())
{
values[e.Name.LocalName] = e.Value;
}
var modFoldersPriority = values["modFoldersPriority"].Split('\n').Select(x=>x.Trim()).Reverse().ToList();
values["modFolders"] = string.Join(";", modFolders.OrderByDescending(x=>modFoldersPriority.IndexOf(x)));
values["port"] = port.ToString();
// in all values, replace [varName] with its actual value
for (int i = 0; i < 5; i++) // few iterations to propagate [varName]
{
var valuesCopy = new Dictionary<string, string>(values);
foreach (var kvp_replaceInThis in valuesCopy.Keys)
{
foreach (var kvp_replaceBy in valuesCopy)
{
values[kvp_replaceInThis] = values[kvp_replaceInThis].Replace("[" + kvp_replaceBy.Key + "]", kvp_replaceBy.Value);
}
}
}
// check if values that mean file location exists
foreach (var f in new[] { "serverConfigMain", "serverConfigBasic", "executable" })
{
var path = values[f];
if (!File.Exists(path)) throw new Exception("the path from variable " + f + "=" + path + " doesn't exists");
}
// split the arguments line by new line and trim from both sides
var arguments = values["arguments"].Split('\n').Select(x => x.Trim());
var executable = values["executable"];
Console.WriteLine("Folder:");
Console.WriteLine("\t" + myExeTopDirName);
Console.WriteLine();
Console.WriteLine("Executable:");
Console.WriteLine("\t" + executable);
Console.WriteLine();
Console.WriteLine("Parameters:");
Console.WriteLine("\t" + string.Join("\n\t", arguments));
var startInfo = new ProcessStartInfo()
{
FileName = executable,
Arguments = string.Join(" ", arguments),
UseShellExecute = false,
};
var process = Process.Start(startInfo);
if (process == null) throw new System.NullReferenceException("the started process is null");
AppDomain.CurrentDomain.ProcessExit += (sender, a) =>
{
process.CloseMainWindow();
process.Kill();
};
var handler = new ConsoleEventDelegate((int eventType) =>
{
if (eventType == 2)
{
process.CloseMainWindow();
process.Kill();
}
return true;
});
SetConsoleCtrlHandler(handler, true);
Console.WriteLine();
Console.WriteLine("Waiting for server to die...");
process.WaitForExit();
}
static string GetDeepestFolderInPath(string path)
{
return path.Split(new[] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar }).Last();
}
// its pain in the ass to detect close in C#
//http://stackoverflow.com/questions/4646827/on-exit-for-a-console-application
private delegate bool ConsoleEventDelegate(int eventType);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool SetConsoleCtrlHandler(ConsoleEventDelegate callback, bool add);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment