Last active
June 29, 2016 07:34
-
-
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…
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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