Merge upstream

This commit is contained in:
Cyberboss
2017-09-21 11:08:02 -04:00
16 changed files with 1991 additions and 4123 deletions
+6 -6
View File
@@ -47,7 +47,7 @@ namespace TGCommandLine
protected override ExitCode Run(IList<string> parameters)
{
var bytes = Server.GetComponent<ITGConfig>().ReadRaw(parameters[0], parameters.Count > 2 && parameters[2].ToLower() == "--repo", out string error);
var bytes = Server.GetComponent<ITGConfig>().ReadText(parameters[0], parameters.Count > 2 && parameters[2].ToLower() == "--repo", out string error);
if(bytes == null)
{
OutputProc("Error: " + error);
@@ -67,11 +67,11 @@ namespace TGCommandLine
}
public override string GetArgumentString()
{
return "<source config file> <out file> [--repo]";
return "<source static file> <out file> [--repo]";
}
public override string GetHelpText()
{
return "Downloads the specified file from the config tree and writes it to out file. --repo will fetch it from the repository instead of the game config";
return "Downloads the specified file from the static tree and writes it to out file. --repo will fetch it from the repository instead of the Static directory";
}
}
@@ -87,7 +87,7 @@ namespace TGCommandLine
{
try
{
var res = Server.GetComponent<ITGConfig>().WriteRaw(parameters[0], File.ReadAllText(parameters[1]));
var res = Server.GetComponent<ITGConfig>().WriteText(parameters[0], File.ReadAllText(parameters[1]));
if (res != null)
{
OutputProc("Error: " + res);
@@ -103,12 +103,12 @@ namespace TGCommandLine
public override string GetArgumentString()
{
return "<destination config file> <source file> [--repo]";
return "<destination statoc file> <source file> [--repo]";
}
public override string GetHelpText()
{
return "Uploads the specified file to the config tree from source file";
return "Uploads the specified file to the static tree from source file";
}
}
}
-232
View File
@@ -1,232 +0,0 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
using TGServiceInterface;
namespace TGControlPanel
{
class ConfigTextBox : TextBox
{
IList<ConfigSetting> ChangeList;
ConfigSetting Setting;
public ConfigTextBox(ConfigSetting c, IList<ConfigSetting> cl)
{
Setting = c;
ChangeList = cl;
Text = Setting.Value;
Multiline = true;
Width = 560;
Height *= 3;
ScrollBars = ScrollBars.Both;
TextChanged += ConfigTextbox_TextChanged;
}
private void ConfigTextbox_TextChanged(object sender, EventArgs e)
{
Setting.Value = Text;
if (!ChangeList.Contains(Setting))
ChangeList.Add(Setting);
}
}
class JobNumeric : NumericUpDown
{
IList<JobSetting> ChangeList;
JobSetting Setting;
bool spawn;
public JobNumeric(JobSetting c, IList<JobSetting> cl, bool s)
{
spawn = s;
Setting = c;
ChangeList = cl;
Minimum = -1;
Maximum = 10000;
Value = spawn ? c.SpawnPositions : c.TotalPositions;
ValueChanged += JobNumeric_ValueChanged;
}
private void JobNumeric_ValueChanged(object sender, EventArgs e)
{
if (spawn)
Setting.SpawnPositions = (int)Value;
else
Setting.TotalPositions = (int)Value;
if (!ChangeList.Contains(Setting))
ChangeList.Add(Setting);
}
}
enum MapNumType
{
MaxPlayers,
MinPlayers,
VoteWeight,
}
class MapNumeric : NumericUpDown
{
IList<MapSetting> ChangeList;
MapSetting Setting;
MapNumType type;
public MapNumeric(MapSetting c, IList<MapSetting> cl, MapNumType t)
{
type = t;
Setting = c;
ChangeList = cl;
Minimum = -1;
Maximum = 10000;
switch (type)
{
case MapNumType.MaxPlayers:
Value = Setting.MaxPlayers;
break;
case MapNumType.MinPlayers:
Value = Setting.MinPlayers;
break;
case MapNumType.VoteWeight:
DecimalPlaces = 5;
Value = Convert.ToDecimal(Setting.VoteWeight);
break;
}
ValueChanged += MapNumeric_ValueChanged;
}
private void MapNumeric_ValueChanged(object sender, EventArgs e)
{
switch (type)
{
case MapNumType.MaxPlayers:
Setting.MaxPlayers = (int)Value;
break;
case MapNumType.MinPlayers:
Setting.MinPlayers = (int)Value;
break;
case MapNumType.VoteWeight:
Setting.VoteWeight = (float)Value;
break;
}
if (!ChangeList.Contains(Setting))
ChangeList.Add(Setting);
}
}
class ConfigCheckBox : CheckBox
{
IList<ConfigSetting> ChangeList;
ConfigSetting Setting;
public ConfigCheckBox(ConfigSetting c, IList<ConfigSetting> cl)
{
Setting = c;
ChangeList = cl;
Text = "Enabled";
Font = new Font("Verdana", 8.0f);
ForeColor = Color.FromArgb(248, 248, 242);
Checked = Setting.Value != null;
CheckStateChanged += ConfigCheckBox_CheckStateChanged;
}
private void ConfigCheckBox_CheckStateChanged(object sender, EventArgs e)
{
Setting.Value = Checked ? "" : null;
if (!ChangeList.Contains(Setting))
ChangeList.Add(Setting);
}
}
class MapRadioButton : RadioButton
{
IList<MapSetting> ChangeList;
IList<MapRadioButton> AllButtons;
MapSetting Setting;
public MapRadioButton(MapSetting c, IList<MapSetting> cl, IList<MapRadioButton> others)
{
ChangeList = cl;
Setting = c;
AllButtons = others;
Font = new Font("Verdana", 8.0f);
ForeColor = Color.FromArgb(248, 248, 242);
Text = "Default";
AllButtons.Add(this);
if (Setting.Default)
Checked = true;
CheckedChanged += MapRadioButton_CheckedChanged;
}
private void MapRadioButton_CheckedChanged(object sender, EventArgs e)
{
if (!Checked)
return;
foreach(var I in AllButtons)
if(I != this && I.Checked)
{
I.Checked = false;
I.Setting.Default = false;
if(!ChangeList.Contains(I.Setting))
ChangeList.Add(I.Setting);
break;
}
Setting.Default = true;
if (!ChangeList.Contains(Setting))
ChangeList.Add(Setting);
}
}
class MapCheckBox : CheckBox
{
IList<MapSetting> ChangeList;
MapSetting Setting;
public MapCheckBox(MapSetting c, IList<MapSetting> cl)
{
Setting = c;
ChangeList = cl;
Text = "Enabled";
Font = new Font("Verdana", 8.0f);
ForeColor = Color.FromArgb(248, 248, 242);
Checked = Setting.Enabled;
CheckStateChanged += ConfigCheckBox_CheckStateChanged;
}
private void ConfigCheckBox_CheckStateChanged(object sender, EventArgs e)
{
Setting.Enabled = Checked;
if (!ChangeList.Contains(Setting))
ChangeList.Add(Setting);
}
}
class ConfigAddRemoveButton : Button
{
ConfigSetting Setting;
Main main;
TGConfigType type;
bool remove;
public ConfigAddRemoveButton(ConfigSetting c, Main m, TGConfigType t)
{
Setting = c;
main = m;
type = t;
UseVisualStyleBackColor = true;
remove = Setting.ExistsInStatic || !Setting.ExistsInRepo;
Text = remove ? "Remove" : "Add";
Click += ConfigAddRemoveButton_Click;
}
private void ConfigAddRemoveButton_Click(object sender, EventArgs e)
{
if (remove)
{
Setting.Values = Setting.DefaultValues;
Setting.Value = null;
}
else
{
Setting.Value = Setting.DefaultValue;
Setting.Values = Setting.DefaultValues;
}
var Result = Server.GetComponent<ITGConfig>().SetItem(type, Setting);
if (Result != null)
MessageBox.Show("Error: " + Result);
main.RefreshCurrentPage();
}
}
}
-654
View File
@@ -1,654 +0,0 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Windows.Forms;
using TGServiceInterface;
namespace TGControlPanel
{
partial class Main
{
enum ConfigIndex
{
Config = 0,
Database = 1,
Game = 2,
Jobs = 3,
Maps = 4,
Admins = 5,
}
IList<ConfigSetting> GeneralChangelist, DatabaseChangelist, GameChangelist;
IList<JobSetting> JobsChangelist;
IList<MapSetting> MapsChangelist;
FlowLayoutPanel ConfigConfigFlow, DatabaseConfigFlow, GameConfigFlow, JobsConfigFlow, MapsConfigFlow;
bool updatingAdminPerms = false;
FlowLayoutPanel CreateFLP(Control parent)
{
var res = new FlowLayoutPanel()
{
AutoSize = true,
FlowDirection = FlowDirection.TopDown,
};
AdjustFlow(res, parent);
parent.Controls.Add(res);
return res;
}
void AdjustFlow(FlowLayoutPanel flow, Control parent)
{
flow.MaximumSize = new Size(parent.Width - 90, 9999999);
}
void InitConfigPage()
{
ConfigPanels.SelectedIndex = Properties.Settings.Default.LastConfigPageIndex;
ConfigApply.Enabled = (ConfigIndex)ConfigPanels.SelectedIndex != ConfigIndex.Admins;
ConfigPanels.SelectedIndexChanged += ConfigPanels_SelectedIndexChanged;
Resize += ReadjustFlow;
ConfigConfigFlow = CreateFLP(ConfigConfigPanel);
GeneralChangelist = new List<ConfigSetting>();
DatabaseConfigFlow = CreateFLP(DatabaseConfigPanel);
DatabaseChangelist = new List<ConfigSetting>();
GameConfigFlow = CreateFLP(GameConfigPanel);
GameChangelist = new List<ConfigSetting>();
JobsConfigFlow = CreateFLP(JobsConfigPanel);
JobsChangelist = new List<JobSetting>();
MapsConfigFlow = CreateFLP(MapsConfigPanel);
MapsChangelist = new List<MapSetting>();
AdminRanksListBox.SelectedIndexChanged += AdminRanksListBox_SelectedIndexChanged;
PermissionsListBox.ItemCheck += AdjustCurrentRankPermissions;
NegativePermissions.ItemCheck += AdjustCurrentRankPermissions;
LoadConfig();
}
void LoadConfig()
{
var RepoReady = Server.GetComponent<ITGRepository>().Exists();
ConfigPanels.Visible = RepoReady;
ConfigApply.Visible = RepoReady;
ConfigDownload.Visible = RepoReady;
ConfigDownloadRepo.Visible = RepoReady;
ConfigUpload.Visible = RepoReady;
if (!RepoReady)
return;
LoadGenericConfig(TGConfigType.General);
LoadGenericConfig(TGConfigType.Database);
LoadGenericConfig(TGConfigType.Game);
LoadJobsConfig();
LoadMapsConfig();
LoadAdminsConfig();
}
private void RemoveRankButton_Click(object sender, EventArgs e)
{
var rank = (string)AdminRanksListBox.SelectedItem;
var result = Server.GetComponent<ITGConfig>().RemoveAdminRank(rank);
if (result != null)
MessageBox.Show("Error: " + result);
LoadRanksList();
}
private void AddRankButton_Click(object sender, EventArgs e)
{
var result = Server.GetComponent<ITGConfig>().SetAdminRank(AddRankTextBox.Text, new Dictionary<string, bool>());
if (result != null)
MessageBox.Show("Error: " + result);
else
AddRankTextBox.Text = "";
LoadRanksList();
}
private void ApplyAdminRankButton_Click(object sender, EventArgs e)
{
var rank = (string)AdminRanksListBox.SelectedItem;
var admin = (string)AdminsListBox.SelectedItem;
if(rank == null || admin == null)
{
MessageBox.Show("Please select a rank and admin!");
return;
}
admin = admin.Split(' ')[0];
var result = Server.GetComponent<ITGConfig>().Addmin(admin, rank);
if (result != null)
MessageBox.Show("Error: " + result);
LoadAdminsList();
}
private void DeadminButton_Click(object sender, EventArgs e)
{
var selectedAdmin = (string)AdminsListBox.SelectedItem;
if(selectedAdmin == null)
{
MessageBox.Show("You must select an admin first!");
return;
}
var result = Server.GetComponent<ITGConfig>().Deadmin(selectedAdmin.Split('(')[0].Trim());
if (result != null)
MessageBox.Show("Error: " + result);
LoadAdminsList();
}
private void AddminButton_Click(object sender, EventArgs e)
{
var rank = (string)AdminRanksListBox.SelectedItem;
if(rank == null)
{
MessageBox.Show("You must select a rank first!");
return;
}
var result = Server.GetComponent<ITGConfig>().Addmin(AddminTextBox.Text, rank);
if (result != null)
MessageBox.Show("Error: " + result);
else
AddminTextBox.Text = "";
LoadAdminsList();
}
private void AdjustCurrentRankPermissions(object sender, ItemCheckEventArgs e)
{
if (updatingAdminPerms)
return;
if (AdminRanksListBox.SelectedIndex == -1)
{
MessageBox.Show("No admin rank selected!");
return;
}
var perms = new Dictionary<string, bool>();
for (var I = 0; I < PermissionsListBox.Items.Count; ++I)
{
var negChecked = NegativePermissions.GetItemChecked(I) || (sender == NegativePermissions && e.Index == I && e.NewValue == CheckState.Checked);
var posChecked = PermissionsListBox.GetItemChecked(I) || (sender == PermissionsListBox && e.Index == I && e.NewValue == CheckState.Checked);
var perm = ((string)PermissionsListBox.Items[I]).Split(' ')[0];
if (posChecked ^ negChecked)
perms.Add(perm, posChecked);
}
var result = Server.GetComponent<ITGConfig>().SetAdminRank((string)AdminRanksListBox.SelectedItem, perms);
if (result != null)
MessageBox.Show("Error: " + result);
UpdatePermissionsDisplay();
}
private void AdminRanksListBox_SelectedIndexChanged(object sender, EventArgs e)
{
UpdatePermissionsDisplay();
}
void UpdatePermissionsDisplay() {
var rank = (string)AdminRanksListBox.SelectedItem;
var ranks = Server.GetComponent<ITGConfig>().AdminRanks(out string error);
if (ranks != null && !ranks.ContainsKey(rank))
error = "Could not find rank: " + rank + "!";
if (error != null)
{
MessageBox.Show("Error: " + error);
return;
}
var ourRank = ranks[rank];
updatingAdminPerms = true;
for (var I = 0; I < PermissionsListBox.Items.Count; ++I)
{
PermissionsListBox.SetItemChecked(I, false);
NegativePermissions.SetItemChecked(I, false);
}
foreach (var I in ourRank)
if (I.Value)
{
for (var J = 0; J < PermissionsListBox.Items.Count; ++J)
if (((string)PermissionsListBox.Items[J]).Split(' ')[0] == I.Key)
PermissionsListBox.SetItemChecked(J, true);
}
else
for (var J = 0; J < PermissionsListBox.Items.Count; ++J)
if (((string)NegativePermissions.Items[J]).Split(' ')[0] == I.Key)
NegativePermissions.SetItemChecked(J, false);
updatingAdminPerms = false;
}
private void ConfigPanels_SelectedIndexChanged(object sender, EventArgs e)
{
ConfigApply.Enabled = (ConfigIndex)ConfigPanels.SelectedIndex != ConfigIndex.Admins;
Properties.Settings.Default.LastConfigPageIndex = ConfigPanels.SelectedIndex;
}
void ReadjustFlow(object sender, EventArgs e)
{
AdjustFlow(ConfigConfigFlow, ConfigConfigPanel);
AdjustFlow(GameConfigFlow, GameConfigPanel);
AdjustFlow(DatabaseConfigFlow, DatabaseConfigPanel);
AdjustFlow(JobsConfigFlow, JobsConfigPanel);
AdjustFlow(MapsConfigFlow, MapsConfigPanel);
}
void LoadGenericConfig(TGConfigType type)
{
FlowLayoutPanel flow;
IList<ConfigSetting> changeList;
switch (type)
{
case TGConfigType.Database:
flow = DatabaseConfigFlow;
changeList = DatabaseChangelist;
break;
case TGConfigType.Game:
flow = GameConfigFlow;
changeList = GameChangelist;
break;
case TGConfigType.General:
flow = ConfigConfigFlow;
changeList = GeneralChangelist;
break;
default:
throw new Exception(String.Format("Invalid TGConfigType {0}", type));
}
changeList.Clear();
flow.Controls.Clear();
flow.SuspendLayout();
var Entries = Server.GetComponent<ITGConfig>().Retrieve(type, out string error);
if (Entries != null)
foreach (var I in Entries)
HandleConfigEntry(I, flow, changeList, type);
else
flow.Controls.Add(new Label() { Text = "Unable to load related config!" });
flow.ResumeLayout();
}
void ConfigRefresh_Click(object sender, System.EventArgs e)
{
RefreshCurrentPage();
}
public void RefreshCurrentPage()
{
if (!ConfigPanels.Visible)
{
LoadConfig();
return;
}
switch ((ConfigIndex)ConfigPanels.SelectedIndex)
{
case ConfigIndex.Config:
LoadGenericConfig(TGConfigType.General);
break;
case ConfigIndex.Database:
LoadGenericConfig(TGConfigType.Database);
break;
case ConfigIndex.Game:
LoadGenericConfig(TGConfigType.Game);
break;
case ConfigIndex.Jobs:
LoadJobsConfig();
break;
case ConfigIndex.Maps:
LoadMapsConfig();
break;
case ConfigIndex.Admins:
LoadAdminsConfig();
break;
}
}
void LoadRanksList()
{
var ranks = Server.GetComponent<ITGConfig>().AdminRanks(out string error);
AdminRanksListBox.Items.Clear();
if (ranks == null)
MessageBox.Show("Error: " + error);
else
foreach (var I in ranks)
AdminRanksListBox.Items.Add(I.Key);
if (AdminRanksListBox.Items.Count > 0 && AdminRanksListBox.SelectedIndex == -1)
{
AdminRanksListBox.SelectedIndex = 0;
RemoveRankButton.Enabled = true;
}
else
RemoveRankButton.Enabled = false;
}
void LoadAdminsList()
{
var admins = Server.GetComponent<ITGConfig>().Admins(out string error);
AdminsListBox.Items.Clear();
if (admins == null)
MessageBox.Show("Error: " + error);
else
foreach (var I in admins)
AdminsListBox.Items.Add(String.Format("{0} ({1})", I.Key, I.Value));
if(AdminsListBox.Items.Count > 0 && AdminsListBox.SelectedIndex == -1) {
AdminsListBox.SelectedIndex = 0;
DeadminButton.Enabled = true;
}
else
DeadminButton.Enabled = false;
}
void LoadAdminsConfig()
{
var perms = Server.GetComponent<ITGConfig>().ListPermissions(out string error);
PermissionsListBox.Items.Clear();
if (perms == null)
MessageBox.Show("Error: " + error);
else
foreach (var I in perms)
{
var formattedDisplay = String.Format("{0} ({1})", I.Key, I.Value);
PermissionsListBox.Items.Add(formattedDisplay);
NegativePermissions.Items.Add(formattedDisplay);
}
LoadRanksList();
LoadAdminsList();
}
void ApplyGenericConfig(IList<ConfigSetting> changelist, TGConfigType type)
{
var Config = Server.GetComponent<ITGConfig>();
foreach (var I in changelist)
{
var error = Config.SetItem(type, I);
if (error != null)
{
MessageBox.Show("An error occurred: {1}" + error);
break;
}
}
}
void ConfigApply_Click(object sender, EventArgs e)
{
switch ((ConfigIndex)ConfigPanels.SelectedIndex)
{
case ConfigIndex.Config:
ApplyGenericConfig(GeneralChangelist, TGConfigType.General);
break;
case ConfigIndex.Database:
ApplyGenericConfig(DatabaseChangelist, TGConfigType.Database);
break;
case ConfigIndex.Game:
ApplyGenericConfig(GameChangelist, TGConfigType.Game);
break;
case ConfigIndex.Jobs:
var Config = Server.GetComponent<ITGConfig>();
foreach (var I in JobsChangelist)
Config.SetJob(I);
break;
case ConfigIndex.Maps:
Config = Server.GetComponent<ITGConfig>();
foreach (var I in MapsChangelist)
Config.SetMapSettings(I);
break;
case ConfigIndex.Admins:
MessageBox.Show("How were you able to click that???");
break;
}
RefreshCurrentPage();
}
void LoadMapsConfig()
{
MapsConfigFlow.Controls.Clear();
var Maps = Server.GetComponent<ITGConfig>().MapSettings(out string error);
if (Maps == null)
{
MapsConfigFlow.Controls.Add(new Label()
{
Text = "Error: " + error,
AutoSize = true,
Font = new Font("Verdana", 10.0f),
ForeColor = Color.FromArgb(248, 248, 242)
});
return;
}
MapsConfigFlow.SuspendLayout();
MapsConfigFlow.Controls.Add(new Label()
{
Text = "Set a player limit to 0 for it to be ignored",
AutoSize = true,
Font = new Font("Verdana", 10.0f),
ForeColor = Color.FromArgb(248, 248, 242)
});
MapsConfigFlow.Controls.Add(new Label());
var mapRadios = new List<MapRadioButton>();
foreach(var M in Maps)
{
var p = new FlowLayoutPanel() { FlowDirection = FlowDirection.LeftToRight, AutoSize = true };
p.Controls.Add(new Label()
{
Text = M.Name + ":",
AutoSize = true,
Font = new Font("Verdana", 10.0f),
ForeColor = Color.FromArgb(248, 248, 242)
});
p.Controls.Add(new MapCheckBox(M, MapsChangelist));
p.Controls.Add(new MapRadioButton(M, MapsChangelist, mapRadios));
MapsConfigFlow.Controls.Add(p);
p = new FlowLayoutPanel() { FlowDirection = FlowDirection.LeftToRight, AutoSize = true };
p.Controls.Add(new Label()
{
Text = "Min Players:",
AutoSize = true,
Font = new Font("Verdana", 10.0f),
ForeColor = Color.FromArgb(248, 248, 242)
});
p.Controls.Add(new MapNumeric(M, MapsChangelist, MapNumType.MinPlayers));
p.Controls.Add(new Label()
{
Text = "Max Players:",
AutoSize = true,
Font = new Font("Verdana", 10.0f),
ForeColor = Color.FromArgb(248, 248, 242)
});
p.Controls.Add(new MapNumeric(M, MapsChangelist, MapNumType.MaxPlayers));
p.Controls.Add(new Label()
{
Text = "Vote Weight:",
AutoSize = true,
Font = new Font("Verdana", 10.0f),
ForeColor = Color.FromArgb(248, 248, 242)
});
p.Controls.Add(new MapNumeric(M, MapsChangelist, MapNumType.VoteWeight));
MapsConfigFlow.Controls.Add(p);
MapsConfigFlow.Controls.Add(new Label()); //line break
}
MapsConfigFlow.ResumeLayout();
}
void LoadJobsConfig()
{
JobsConfigFlow.Controls.Clear();
var Jobs = Server.GetComponent<ITGConfig>().Jobs(out string error);
if(Jobs == null)
{
JobsConfigFlow.Controls.Add(new Label()
{
Text = "Error: " + error,
AutoSize = true,
Font = new Font("Verdana", 10.0f),
ForeColor = Color.FromArgb(248, 248, 242)
});
return;
}
JobsConfigFlow.SuspendLayout();
JobsConfigFlow.Controls.Add(new Label()
{
Text = "Set a value to -1 for infinite positions",
AutoSize = true,
Font = new Font("Verdana", 10.0f),
ForeColor = Color.FromArgb(248, 248, 242)
});
JobsConfigFlow.Controls.Add(new Label());
foreach (var J in Jobs)
{
JobsConfigFlow.Controls.Add(new Label()
{
Text = J.Name + ":",
AutoSize = true,
Font = new Font("Verdana", 10.0f),
ForeColor = Color.FromArgb(248, 248, 242)
});
var p = new FlowLayoutPanel() { FlowDirection = FlowDirection.LeftToRight, AutoSize = true };
p.Controls.Add(new Label()
{
Text = "Total Positions:",
AutoSize = true,
Font = new Font("Verdana", 10.0f),
ForeColor = Color.FromArgb(248, 248, 242)
});
p.Controls.Add(new JobNumeric(J, JobsChangelist, false));
p.Controls.Add(new Label()
{
Text = "Spawn Positions:",
AutoSize = true,
Font = new Font("Verdana", 10.0f),
ForeColor = Color.FromArgb(248, 248, 242)
});
p.Controls.Add(new JobNumeric(J, JobsChangelist, true));
JobsConfigFlow.Controls.Add(p);
JobsConfigFlow.Controls.Add(new Label()); //line break
}
JobsConfigFlow.ResumeLayout();
}
void ConfigUpload_Click(object sender, EventArgs eva)
{
var ofd = new OpenFileDialog()
{
CheckFileExists = true,
CheckPathExists = true,
DefaultExt = ".txt",
Multiselect = false,
Title = "Config Upload",
ValidateNames = true,
Filter = "Text files (*.txt)|*.txt|PNG files (*.png)|*.png|All files (*.*)|*.*",
AddExtension = false,
SupportMultiDottedExtensions = true,
};
if (ofd.ShowDialog() != DialogResult.OK)
return;
var fileToUpload = ofd.FileName;
var originalFileName = Program.TextPrompt("Config Upload", "Enter the path of the destination file in the config folder:");
if (originalFileName == null)
return;
string fileContents = null;
string error = null;
try
{
fileContents = File.ReadAllText(fileToUpload);
} catch (Exception e)
{
error = e.ToString();
}
if (error == null)
error = Server.GetComponent<ITGConfig>().WriteRaw(originalFileName, fileContents);
if (error != null)
MessageBox.Show("An error occurred: " + error);
}
void DownloadConfig(string remotePath, bool repo)
{
if (remotePath == null)
return;
var text = Server.GetComponent<ITGConfig>().ReadRaw(remotePath, repo, out string error);
if (text != null)
{
var ofd = new SaveFileDialog()
{
CheckFileExists = false,
CheckPathExists = true,
DefaultExt = ".txt",
Title = "Config Download",
ValidateNames = true,
Filter = "Text files (*.txt)|*.txt|PNG files (*.png)|*.png|All files (*.*)|*.*",
AddExtension = false,
CreatePrompt = false,
OverwritePrompt = true,
SupportMultiDottedExtensions = true,
};
if (ofd.ShowDialog() != DialogResult.OK)
return;
try
{
File.WriteAllText(ofd.FileName, text);
return;
}
catch (Exception e)
{
error = e.ToString();
}
}
MessageBox.Show("An error occurred: " + error);
}
void ConfigDownload_Click(object sender, EventArgs eva)
{
DownloadConfig(Program.TextPrompt("Config Download", "Enter the path of the source file in the config folder:"), false);
}
void ConfigDownloadRepo_Click(object sender, EventArgs e)
{
DownloadConfig(Program.TextPrompt("Repo Config Download", "Enter the path of the source file in the repository's config folder:"), true);
}
void HandleConfigEntry(ConfigSetting setting, FlowLayoutPanel flow, IList<ConfigSetting> changelist, TGConfigType type)
{
flow.Controls.Add(new Label()
{
Text = setting.Name + (setting.ExistsInRepo ? "" : " (Does not exist in repository!)"),
AutoSize = true,
Font = new Font("Verdana", 10.0f),
ForeColor = Color.FromArgb(248, 248, 242)
});
if (setting.ExistsInRepo)
flow.Controls.Add(new Label()
{
AutoSize = true,
Font = new Font("Verdana", 8.0f),
ForeColor = Color.FromArgb(248, 248, 242),
Text = setting.Comment
});
if (setting.IsMultiKey)
{
flow.Controls.Add(new Label()
{
Text = "MANUAL EDIT REQUIRED!",
AutoSize = true,
Font = new Font("Verdana", 10.0f),
ForeColor = Color.FromArgb(248, 248, 242)
});
}
else
{
var IsSwitch = setting.DefaultValue == "" || setting.DefaultValue == null;
if (!IsSwitch || !setting.ExistsInRepo)
flow.Controls.Add(new ConfigAddRemoveButton(setting, this, type));
if (IsSwitch || setting.ExistsInStatic)
flow.Controls.Add(IsSwitch ? (Control)new ConfigCheckBox(setting, changelist) : new ConfigTextBox(setting, changelist));
}
flow.Controls.Add(new Label()); //line break
}
}
}
+1668 -2065
View File
File diff suppressed because it is too large Load Diff
+1 -2
View File
@@ -10,12 +10,11 @@ namespace TGControlPanel
{
InitializeComponent();
Panels.SelectedIndexChanged += Panels_SelectedIndexChanged;
Panels.SelectedIndex += Properties.Settings.Default.LastPageIndex;
Panels.SelectedIndex += Math.Min(Properties.Settings.Default.LastPageIndex, Panels.TabCount - 1);
InitRepoPage();
InitBYONDPage();
InitServerPage();
LoadChatPage();
InitConfigPage();
}
private void Main_Resize(object sender, EventArgs e)
-6
View File
@@ -53,12 +53,6 @@
<Compile Include="ChatPage.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="ConfigHelperControls.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="ConfigPage.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="Login.cs">
<SubType>Form</SubType>
</Compile>
+58
View File
@@ -0,0 +1,58 @@
{
"documentation": [
"Place this file in the root of your repo in order to configure TGS3 per repository options",
"This file is not required, it merely configures changelog generation, static directories, and .dlls",
"All keys except the ones below are ignored by the service"
],
"changelog": {
"script": "path/to/changelog/generator.py",
"arguments": "space delimited \"script arguments\"",
"pip_dependancies": [
"optional",
"list",
"of",
"pip",
"install",
"packages"
]
},
"synchronize_paths": [
"list",
"of",
"regex",
"paths",
"to",
"stage",
"for",
"git",
"commit"
],
"static_directories": [
"list",
"of",
"directories",
"that",
"should",
"not",
"be",
"changed",
"with",
"repo",
"updates",
"et al"
],
"dlls": [
"list",
"of",
".dlls",
"that",
"your",
"game",
"uses",
"these",
"will",
"be",
"handled",
"properly"
]
}
+54 -36
View File
@@ -22,8 +22,6 @@ namespace TGServerService
#endregion
const string StaticDirs = "Static";
const string StaticDataDir = StaticDirs + "/data";
const string StaticConfigDir = StaticDirs + "/config";
const string StaticBackupDir = "Static_BACKUP";
const string LibMySQLFile = "/libmysql.dll";
@@ -40,9 +38,6 @@ namespace TGServerService
const string InterfaceDLLName = "TGServiceInterface.dll";
List<string> copyExcludeList = new List<string> { ".git", "data", "config", "libmysql.dll" }; //shit we handle
List<string> deleteExcludeList = new List<string> { "data", "config", "libmysql.dll" }; //shit we handle
object CompilerLock = new object();
TGCompilerStatus compilerCurrentStatus;
string lastCompilerError;
@@ -130,32 +125,37 @@ namespace TGServerService
return TGCompilerStatus.Uninitialized;
}
void CleanGameFolderList(string GameDir, IList<string> theList)
{
foreach (var I in theList)
{
var the_path = Path.Combine(GameDir, I);
if (Directory.Exists(the_path))
Directory.Delete(the_path);
}
}
//we need to remove symlinks before we can recursively delete
void CleanGameFolder()
{
if (Directory.Exists(Path.Combine(GameDirA, InterfaceDLLName)))
Directory.Delete(Path.Combine(GameDirA, InterfaceDLLName));
if (Directory.Exists(GameDirA + LibMySQLFile))
Directory.Delete(GameDirA + LibMySQLFile);
if (Directory.Exists(GameDirA + "/data"))
Directory.Delete(GameDirA + "/data");
if (Directory.Exists(GameDirA + "/config"))
Directory.Delete(GameDirA + "/config");
var Config = LoadRepoConfig();
if (Config != null)
{
CleanGameFolderList(GameDirA, Config.StaticDirectoryPaths);
CleanGameFolderList(GameDirA, Config.DLLPaths);
}
if (Directory.Exists(Path.Combine(GameDirB, InterfaceDLLName)))
Directory.Delete(Path.Combine(GameDirB, InterfaceDLLName));
if (Directory.Exists(GameDirB + LibMySQLFile))
Directory.Delete(GameDirB + LibMySQLFile);
if (Directory.Exists(GameDirB + "/data"))
Directory.Delete(GameDirB + "/data");
if (Directory.Exists(GameDirB + "/config"))
Directory.Delete(GameDirB + "/config");
if (Config != null)
{
CleanGameFolderList(GameDirB, Config.StaticDirectoryPaths);
CleanGameFolderList(GameDirB, Config.DLLPaths);
}
if (Directory.Exists(GameDirLive))
Directory.Delete(GameDirLive);
@@ -194,14 +194,14 @@ namespace TGServerService
Directory.CreateDirectory(GameDirA);
Directory.CreateDirectory(GameDirB);
CreateSymlink(GameDirA + "/data", StaticDataDir);
CreateSymlink(GameDirB + "/data", StaticDataDir);
var Config = LoadRepoConfig();
CreateSymlink(GameDirA + "/config", StaticConfigDir);
CreateSymlink(GameDirB + "/config", StaticConfigDir);
CreateSymlink(GameDirA + LibMySQLFile, StaticDirs + LibMySQLFile);
CreateSymlink(GameDirB + LibMySQLFile, StaticDirs + LibMySQLFile);
if (Config != null) {
foreach (var I in Config.StaticDirectoryPaths)
CreateSymlink(Path.Combine(GameDirA, I), Path.Combine(StaticDirs, I));
foreach (var I in Config.DLLPaths)
CreateSymlink(Path.Combine(GameDirA, I), Path.Combine(StaticDirs, I));
}
CreateSymlink(Path.Combine(GameDirA, InterfaceDLLName), Path.Combine(StaticDirs, InterfaceDLLName));
CreateSymlink(Path.Combine(GameDirB, InterfaceDLLName), Path.Combine(StaticDirs, InterfaceDLLName));
@@ -316,16 +316,33 @@ namespace TGServerService
var resurrectee = GetStagingDir();
Program.DeleteDirectory(resurrectee, true, deleteExcludeList);
var Config = LoadRepoConfig();
var deleteExcludeList = new List<string>();
if (Config != null)
{
deleteExcludeList.AddRange(Config.StaticDirectoryPaths);
deleteExcludeList.AddRange(Config.DLLPaths);
Program.DeleteDirectory(resurrectee, true, deleteExcludeList);
}
Directory.CreateDirectory(resurrectee + "/.git/logs");
if (!Directory.Exists(resurrectee + "/config"))
CreateSymlink(resurrectee + "/config", StaticConfigDir);
if (!Directory.Exists(resurrectee + "/data"))
CreateSymlink(resurrectee + "/data", StaticDataDir);
if (!File.Exists(resurrectee + LibMySQLFile))
CreateSymlink(resurrectee + LibMySQLFile, StaticDirs + LibMySQLFile);
if (Config != null)
{
foreach (var I in Config.StaticDirectoryPaths)
{
var the_path = Path.Combine(resurrectee, I);
if (!Directory.Exists(the_path))
CreateSymlink(Path.Combine(resurrectee, I), Path.Combine(StaticDirs, I));
}
foreach (var I in Config.DLLPaths)
{
var the_path = Path.Combine(resurrectee, I);
if (!File.Exists(the_path))
CreateSymlink(the_path, Path.Combine(StaticDirs, I));
}
}
if (!File.Exists(Path.Combine(resurrectee, InterfaceDLLName)))
CreateSymlink(Path.Combine(resurrectee, InterfaceDLLName), Path.Combine(StaticDirs, InterfaceDLLName));
@@ -354,7 +371,8 @@ namespace TGServerService
}
try
{
Program.CopyDirectory(RepoPath, resurrectee, copyExcludeList);
deleteExcludeList.Add(".git");
Program.CopyDirectory(RepoPath, resurrectee, deleteExcludeList);
//just the tip
const string GitLogsDir = "/.git/logs";
Program.CopyDirectory(RepoPath + GitLogsDir, resurrectee + GitLogsDir);
+36 -807
View File
@@ -11,475 +11,7 @@ namespace TGServerService
//knobs and such
partial class TGStationServer : ITGConfig
{
const string ConfigPostfix = "/config.txt";
const string DBConfigPostfix = "/dbconfig.txt";
const string GameConfigPostfix = "/game_options.txt";
const string AdminRanksConfig = StaticConfigDir + "/admin_ranks.txt";
const string AdminRanksRepo = RepoConfig + "/admin_ranks.txt";
const string AdminConfig = StaticConfigDir + "/admins.txt";
const string InteropConfig = StaticConfigDir + "/server_to_tool_bridge_port.txt";
const string MapConfig = StaticConfigDir + "/maps.txt";
const string JobsConfig = StaticConfigDir + "/jobs.txt";
const string TitleImagesConfig = StaticConfigDir + "/title_screens/images";
object configLock = new object(); //for atomic reads/writes
//Write out the admin assoiciations to admins.txt
string WriteMins(IDictionary<string, string> current_mins)
{
string outText = "";
foreach (var I in current_mins)
outText += I.Key + " = " + I.Value + "\r\n";
try
{
lock (configLock)
{
File.WriteAllText(AdminConfig, outText);
}
return null;
}
catch (Exception e)
{
return e.ToString();
}
}
//public api
public string Addmin(string ckey, string rank)
{
var Aranks = AdminRanks(out string error);
if (Aranks != null)
{
if (Aranks.Keys.Contains(rank))
{
var current_mins = Admins(out error);
if (current_mins != null)
{
current_mins[ckey] = rank;
return WriteMins(current_mins);
}
return error;
}
return "Rank " + rank + " does not exist";
}
return error;
}
//public api
public IDictionary<string, IDictionary<string, bool>> AdminRanks(out string error)
{
try
{
List<string> fileLines;
lock (configLock)
{
fileLines = new List<string>(File.ReadAllLines(AdminRanksConfig));
}
var result = new Dictionary<string, IDictionary<string, bool>>();
IDictionary<string, bool> previousPermissions = new Dictionary<string, bool>();
foreach (var L in fileLines)
{
if (L.Length > 0 && L[0] == '#')
continue;
var splits = L.Split('=');
if (splits.Length < 2) //???
continue;
var rank = splits[0].Trim();
var tmp = new List<string>(splits);
tmp.RemoveAt(0);
splits = String.Join(" ", tmp).Split(' ');
var asList = new List<string>(splits);
asList.RemoveAt(0);
var perms = ProcessPermissions(asList, previousPermissions);
result.Add(rank, perms);
previousPermissions = perms;
}
error = null;
return result;
}
catch (Exception e)
{
error = e.ToString();
return null;
}
}
//same thing the proc in admin_ranks.dm does, properly calculates string permission sets and returns them as an enum
IDictionary<string, bool> ProcessPermissions(IList<string> text, IDictionary<string, bool> previousPermissions)
{
IDictionary<string, bool> permissions = new Dictionary<string, bool>();
foreach(var E in text)
{
var trimmed = E.Trim();
if (trimmed.Length == 0)
continue;
bool adding;
switch (trimmed[0])
{
case '-':
adding = false;
trimmed = trimmed.Substring(1);
break;
case '+':
adding = true;
trimmed = trimmed.Substring(1);
break;
default:
adding = true;
break;
}
if (trimmed.Length == 0)
continue;
var perms = StringToPermission(trimmed, previousPermissions, adding);
if(perms != null)
foreach(var perm in perms)
{
if(!permissions.ContainsKey(perm.Key))
permissions.Add(perm);
else
{
var wasAdded = permissions[perm.Key];
if (wasAdded == perm.Value)
continue;
//cancel each other out
permissions.Remove(perm.Key);
}
}
}
return permissions;
}
//basic conversion
IDictionary<string, bool> StringToPermission(string permstring, IDictionary<string, bool> oldpermissions, bool adding)
{
switch (permstring.Trim().ToUpper())
{
case "@":
case "PREV":
return oldpermissions;
default:
return new Dictionary<string, bool> { { permstring, adding } };
}
}
//public api
public IDictionary<string, string> Admins(out string error)
{
try
{
List<string> fileLines;
lock (configLock)
{
fileLines = new List<string>(File.ReadAllLines(AdminConfig));
}
var mins = new Dictionary<string, string>();
foreach (var L in fileLines)
{
var trimmed = L.Trim();
if (L.Length == 0 || L[0] == '#')
continue;
var splits = L.Split('=');
if (splits.Length != 2)
continue;
var key = splits[0].Trim();
if (!mins.ContainsKey(key))
mins.Add(key, splits[1].Trim());
//don't care about dupes
}
error = null;
return mins;
}
catch (Exception e)
{
error = e.ToString();
return null;
}
}
//public api
public string Deadmin(string admin)
{
var current_mins = Admins(out string error);
if (current_mins != null)
{
if (current_mins.ContainsKey(admin))
{
current_mins.Remove(admin);
return WriteMins(current_mins);
}
return null;
}
return error;
}
//public api
public IList<JobSetting> Jobs(out string error)
{
try
{
IList<string> lines;
lock (configLock)
{
lines = File.ReadAllLines(JobsConfig);
}
IList<JobSetting> results = new List<JobSetting>();
foreach (var L in lines)
{
var trimmed = L.Trim();
if (trimmed.Length == 0 || trimmed[0] == '#')
continue;
var splits = trimmed.Split('=');
if (splits.Length < 2)
continue;
var countSplits = splits[1].Split(',');
if (countSplits.Length < 2)
continue;
int total, spawn;
try
{
total = Convert.ToInt32(countSplits[0]);
spawn = Convert.ToInt32(countSplits[1]);
}
catch
{
continue;
}
results.Add(new JobSetting()
{
Name = splits[0],
TotalPositions = total,
SpawnPositions = spawn,
});
}
error = null;
return results;
}
catch (Exception e)
{
error = e.ToString();
return null;
}
}
//public api
public ushort InteropPort(out string error)
{
try
{
error = null;
lock (configLock)
{
return Convert.ToUInt16(File.ReadAllText(InteropConfig));
}
}
catch (Exception e)
{
error = e.ToString();
return 0;
}
}
//TGConfigType to the right path, Repo or static
string ConfigTypeToPath(TGConfigType type, bool repo)
{
var path = repo ? RepoConfig : StaticConfigDir;
switch (type)
{
case TGConfigType.Database:
return path + DBConfigPostfix;
case TGConfigType.Game:
return path + GameConfigPostfix;
case TGConfigType.General:
return path + ConfigPostfix;
default:
throw new Exception("Bad TGConfigType: " + type);
}
}
string TranslateConfigComment(string input)
{
//https://github.com/tgstation/tgstation/pull/27632#discussion_r118389053
return input
.Replace("uncomment", "activate")
.Replace("Uncomment", "Activate")
.Replace("commented out", "deactivated")
.Replace("comment this out", "deactivate")
.Replace("Comment this out", "Deactivate")
.Replace("uncommenting", "activating")
.Replace("Uncommenting", "Activating")
.Replace("uncommented", "activated");
}
//public api
public IList<ConfigSetting> Retrieve(TGConfigType type, out string error)
{
try
{
IList<string> RepoConfigData, StaticConfigData;
var repolocked = false;
if (!Monitor.TryEnter(RepoLock))
repolocked = true;
try
{
if (!repolocked && RepoBusy)
repolocked = true;
if (repolocked)
{
error = "Repo locked!";
return null;
}
lock (configLock)
{
RepoConfigData = new List<string>(File.ReadAllLines(ConfigTypeToPath(type, true)));
StaticConfigData = new List<string>(File.ReadAllLines(ConfigTypeToPath(type, false)));
}
}
finally
{
Monitor.Exit(RepoLock);
}
//## designates an option comment
//# designates a commented out option
IDictionary<string, ConfigSetting> repoConfig = new Dictionary<string, ConfigSetting>();
IList<ConfigSetting> results = new List<ConfigSetting>();
ConfigSetting currentSetting = new ConfigSetting();
foreach (var I in RepoConfigData)
{
var trimmed = I.Trim();
if (trimmed.Length == 0)
continue;
var commented = trimmed[0] == '#';
if (commented)
{
if (trimmed.Length == 1)
continue;
if (trimmed[1] == '#')
{
//comment line
if (currentSetting.Comment == null)
currentSetting.Comment = TranslateConfigComment(trimmed.Substring(2).Trim());
else
currentSetting.Comment += "\r\n" + TranslateConfigComment(trimmed.Substring(2).Trim());
continue;
}
if (trimmed.Length == 2)
continue;
trimmed = trimmed.Substring(1).Trim();
}
var splits = new List<string>(trimmed.Split(' '));
currentSetting.Name = splits.First().ToUpper();
splits.RemoveAt(0);
var value = String.Join(" ", splits);
if (commented && value == "")
value = null;
currentSetting.ExistsInRepo = true;
//multi-keying
if (repoConfig.Keys.Contains(currentSetting.Name))
{
currentSetting = repoConfig[currentSetting.Name];
if (!currentSetting.IsMultiKey)
{
currentSetting.IsMultiKey = true;
currentSetting.DefaultValues = new List<string> { currentSetting.DefaultValue, value };
currentSetting.DefaultValue = null;
}
else
currentSetting.DefaultValues.Add(value);
}
else
{
currentSetting.DefaultValue = value;
repoConfig.Add(currentSetting.Name, currentSetting);
results.Add(currentSetting);
}
currentSetting = new ConfigSetting();
}
//gather the stuff from our config
foreach (var I in StaticConfigData)
{
var trimmed = I.Trim();
if (trimmed.Length == 0)
continue;
var commented = trimmed[0] == '#';
if (trimmed.Length < 3 || trimmed[1] == '#' || commented)
continue;
if (commented)
trimmed = trimmed.Substring(1).Trim();
var splits = new List<string>(trimmed.Split(' '));
var name = splits[0].ToUpper();
if (!repoConfig.Keys.Contains(name))
{
currentSetting = new ConfigSetting()
{
Comment = "SETTING DOES NOT EXIST IN REPOSITORY",
Name = name
};
//don't support multikeying here
results.Add(currentSetting);
}
else if (commented)
continue;
else
currentSetting = repoConfig[name];
currentSetting.ExistsInStatic = true;
splits.RemoveAt(0);
var value = String.Join(" ", splits);
if (currentSetting.IsMultiKey)
{
if (currentSetting.Values == null)
currentSetting.Values = new List<string> { value };
else
currentSetting.Values.Add(value);
}
else
currentSetting.Value = value;
}
error = null;
return results;
}
catch (Exception e)
{
error = e.ToString();
return null;
}
}
//public api
public string ServerDirectory()
{
@@ -487,232 +19,42 @@ namespace TGServerService
}
//public api
public string SetItem(TGConfigType type, ConfigSetting newSetting)
{
try
{
var entries = Retrieve(type, out string error);
if (entries == null)
return error;
//serialize it
var asStrings = new List<string>();
bool somethingChanged = false;
foreach (var I in entries)
{
if (newSetting.Name == I.Name)
{
somethingChanged = true;
if (newSetting.Value == null && newSetting.Values == null) //unset
continue;
I.Value = newSetting.Value;
I.Values = newSetting.Values;
I.IsMultiKey = newSetting.IsMultiKey;
}
else if (!I.ExistsInStatic)
continue;
if (I.IsMultiKey)
foreach (var J in I.Values)
asStrings.Add((I.Name + " " + J).Trim());
else
asStrings.Add((I.Name + " " + I.Value).Trim());
}
if (!somethingChanged)
return null;
//write it out
lock (configLock)
{
File.WriteAllLines(ConfigTypeToPath(type, false), asStrings);
}
return null;
}
catch (Exception e)
{
return e.ToString();
}
}
//public api
public string SetJob(JobSetting job)
{
try
{
var entries = Jobs(out string error);
if (entries == null)
return error;
var lines = new List<string>();
foreach (var J in entries)
{
var thingToUse = J.Name == job.Name ? job : J;
lines.Add(String.Format("{0}={1},{2}", thingToUse.Name, thingToUse.TotalPositions, thingToUse.SpawnPositions));
}
lock (configLock)
{
File.WriteAllLines(JobsConfig, lines);
}
return null;
}
catch (Exception e)
{
return e.ToString();
}
}
public IList<MapSetting> MapSettings(out string error)
{
try
{
IList<string> lines;
lock (configLock)
{
lines = File.ReadAllLines(MapConfig);
}
MapSetting currentMap = null, lastDefaultMap = null;
IList<MapSetting> results = new List<MapSetting>();
foreach(var L in lines)
{
var trimmed = L.Trim();
if (trimmed.Length == 0 || trimmed[0] == '#')
continue;
var splits = trimmed.Split(' ');
switch (splits[0].ToLower())
{
case "map":
if (splits.Length < 2)
continue;
currentMap = new MapSetting()
{
Name = splits[1], //defaults in game
Enabled = true,
VoteWeight = 1,
};
break;
case "endmap":
if (currentMap != null)
results.Add(currentMap);
currentMap = null;
break;
case "minplayer":
case "minplayers":
if (splits.Length < 2)
continue;
if (currentMap != null)
try
{
currentMap.MinPlayers = Convert.ToInt32(splits[1]);
}
catch {
continue;
}
break;
case "maxplayer":
case "maxplayers":
if (splits.Length < 2)
continue;
if (currentMap != null)
try
{
currentMap.MaxPlayers = Convert.ToInt32(splits[1]);
}
catch
{
continue;
}
break;
case "weight":
case "voteweight":
if (splits.Length < 2)
continue;
if (currentMap != null)
try
{
currentMap.VoteWeight = float.Parse(splits[1]);
}
catch
{
continue;
}
break;
case "default":
case "defaultmap":
if (currentMap == null)
continue;
if (lastDefaultMap != null)
lastDefaultMap.Default = false;
currentMap.Default = true;
lastDefaultMap = currentMap;
break;
case "disabled":
if (currentMap == null)
continue;
currentMap.Enabled = false;
break;
}
}
error = null;
return results;
}
catch (Exception e)
{
error = e.ToString();
return null;
}
}
public string SetMapSettings(MapSetting newSetting)
{
try
{
var entries = MapSettings(out string error);
if (entries == null)
return error;
IList<string> asStrings = new List<string>();
foreach (var I in entries)
{
var entryToUse = I.Name == newSetting.Name ? newSetting : I;
asStrings.Add("map " + entryToUse.Name);
if (!entryToUse.Enabled)
asStrings.Add("\tdisabled");
if (entryToUse.MinPlayers > 0)
asStrings.Add(String.Format("\tminplayers {0}", entryToUse.MinPlayers));
if (entryToUse.MaxPlayers > 0)
asStrings.Add(String.Format("\tmaxplayers {0}", entryToUse.MaxPlayers));
if (entryToUse.VoteWeight != 1)
asStrings.Add(String.Format("\tvoteweight {0}", entryToUse.VoteWeight));
if (entryToUse.Default)
asStrings.Add("\tdefault");
asStrings.Add("endmap");
}
lock (configLock)
{
File.WriteAllLines(MapConfig, asStrings);
}
return null;
}
catch (Exception e)
{
return e.ToString();
}
}
[OperationBehavior(Impersonation = ImpersonationOption.Required)]
public string ReadRaw(string configRelativePath, bool repo, out string error)
public string ReadText(string staticRelativePath, bool repo, out string error)
{
try
{
var configDir = repo ? RepoConfig : StaticConfigDir;
var path = configDir + "/" + configRelativePath;
lock (configLock) {
var configDir = repo ? RepoPath : StaticDirs;
var path = Path.Combine(configDir, staticRelativePath);
lock (configLock)
{
var di1 = new DirectoryInfo(configDir);
if (repo)
{
//ensure we aren't trying to read anything outside the static dirs
var Config = LoadRepoConfig();
if (Config == null)
{
error = "Unable to load static directory configuration";
return null;
}
var Found = false;
foreach (var I in Config.StaticDirectoryPaths)
{
if (di1.FullName == new DirectoryInfo(Path.Combine(RepoPath, I)).FullName)
{
Found = true;
break;
}
}
if (!Found)
{
error = "File is not in a configured static directory!";
return null;
}
}
var di2 = new DirectoryInfo(new FileInfo(path).Directory.FullName);
var good = false;
@@ -728,7 +70,7 @@ namespace TGServerService
if (!good)
{
error = "Cannot read above config directory!";
error = "Cannot read above static directories!";
return null;
}
@@ -743,14 +85,14 @@ namespace TGServerService
}
}
[OperationBehavior(Impersonation = ImpersonationOption.Required)]
public string WriteRaw(string configRelativePath, string data)
public string WriteText(string staticRelativePath, string data)
{
try
{
var path = StaticConfigDir + "/" + configRelativePath;
var path = Path.Combine(StaticDirs, staticRelativePath);
lock (configLock)
{
var di1 = new DirectoryInfo(StaticConfigDir);
var di1 = new DirectoryInfo(StaticDirs);
var destdir = new FileInfo(path).Directory.FullName;
var di2 = new DirectoryInfo(destdir);
@@ -766,7 +108,7 @@ namespace TGServerService
}
if (!good)
return "Cannot write above config directory!";
return "Cannot write above static directories!";
Directory.CreateDirectory(destdir);
File.WriteAllText(path, data);
return null;
@@ -777,118 +119,5 @@ namespace TGServerService
return e.ToString();
}
}
public string SetTitleImage(string filename, byte[] data)
{
try
{
lock (configLock)
{
var path = StaticConfigDir + TitleImagesConfig + "/" + filename;
File.WriteAllBytes(path, data);
return null;
}
}
catch (Exception e)
{
return e.ToString();
}
}
public IDictionary<string, string> ListPermissions(out string error)
{
try
{
IList<string> lines;
lock (configLock)
{
lines = new List<string>(File.ReadAllLines(AdminRanksRepo));
}
IDictionary<string, string> res = new Dictionary<string, string>();
var inKeywordsBlock = false;
foreach(var I in lines)
{
var trimmed = I.Trim();
if (trimmed.Length == 0 || trimmed[0] != '#')
continue;
var splits = trimmed.Substring(1).Trim().Split(' ');
if (splits[0] == "BEGIN_KEYWORDS")
inKeywordsBlock = true;
else if (splits[0] == "END_KEYWORDS")
inKeywordsBlock = false;
else if (inKeywordsBlock)
{
var key = splits[0];
if (key.Length < 2 || key[0] != '+') //bad format
continue;
key = key.Substring(1);
string description = "";
bool found = false;
foreach (var J in splits)
if (found)
description += J + " ";
else if (J.Length == 1 && J[0] == '=')
found = true;
res.Add(key, description);
}
}
error = null;
return res;
}
catch (Exception e)
{
error = e.ToString();
return null;
}
}
string WriteAdminRanks(IDictionary<string, IDictionary<string, bool>> ranks)
{
try
{
var lines = new List<string>();
foreach (var I in ranks)
{
var line = I.Key + " =";
foreach (var J in I.Value)
line += " " + (J.Value ? "+" : "-") + J.Key;
lines.Add(line);
}
lock (configLock)
{
File.WriteAllLines(AdminRanksConfig, lines);
}
return null;
}
catch (Exception e)
{
return e.ToString();
}
}
public string RemoveAdminRank(string rank)
{
var ranks = AdminRanks(out string error);
if (ranks == null)
return error;
if (!ranks.ContainsKey(rank))
return "Given rank does not exist!";
ranks.Remove(rank);
return WriteAdminRanks(ranks);
}
public string SetAdminRank(string rankName, IDictionary<string, bool> permissions)
{
var ranks = AdminRanks(out string error);
if (ranks == null)
return error;
if (ranks.ContainsKey(rankName))
ranks[rankName] = permissions;
else
ranks.Add(rankName, permissions);
return WriteAdminRanks(ranks);
}
}
}
+1 -2
View File
@@ -38,7 +38,6 @@ namespace TGServerService
//Only need 1 proc instance
void InitDreamDaemon()
{
UpdateInterfaceDll(false);
var Reattach = Properties.Settings.Default.ReattachToDD;
if (Reattach)
try
@@ -374,7 +373,7 @@ namespace TGServerService
return;
//Copy the interface dll to the static dir
var InterfacePath = Assembly.GetAssembly(typeof(DDInteropCallHolder)).Location;
File.Copy(InterfacePath, targetPath, true);
Program.CopyFileForceDirectories(InterfacePath, targetPath, true);
}
//used by Start and Watchdog to start a DD instance
+1
View File
@@ -18,6 +18,7 @@ namespace TGServerService
{
FindTheDroidsWereLookingFor();
InitChat();
InitRepo();
InitByond();
InitCompiler();
InitDreamDaemon();
+11 -1
View File
@@ -2,7 +2,6 @@
using System.Collections.Generic;
using System.IO;
using System.Security.Cryptography;
using System.Text;
namespace TGServerService
{
@@ -12,6 +11,16 @@ namespace TGServerService
//Everything in this file is just generic helpers
public static void CopyFileForceDirectories(string source, string dest, bool overwrite)
{
try
{
Directory.CreateDirectory(Path.GetDirectoryName(dest));
}
catch { } //we don't care if the above errors
File.Copy(source, dest, overwrite); //if this throws errors thats all we care about
}
//http://stackoverflow.com/questions/1701457/directory-delete-doesnt-work-access-denied-error-but-under-windows-explorer-it
public static void DeleteDirectory(string path, bool ContentsOnly = false, IList<string> excludeRoot = null)
{
@@ -30,6 +39,7 @@ namespace TGServerService
di.Delete(true);
}
}
static void NormalizeAndDelete(DirectoryInfo dir, IList<string> excludeRoot)
{
foreach (var subDir in dir.GetDirectories())
+149 -37
View File
@@ -14,8 +14,7 @@ namespace TGServerService
partial class TGStationServer : ITGRepository, IDisposable
{
const string RepoPath = "Repository";
const string RepoConfig = RepoPath + "/config";
const string RepoData = RepoPath + "/data";
const string RepoTGS3SettingsPath = RepoPath + "/TGS3.json";
const string RepoErrorUpToDate = "Already up to date!";
const string SSHPushRemote = "ssh_push_target";
const string PrivateKeyPath = "RepoKey/private_key.txt";
@@ -30,6 +29,80 @@ namespace TGServerService
Repository Repo;
int currentProgress = -1;
/// <summary>
/// Repo specific information about the installation
/// Requires RepoLock and !RepoBusy to be instantiated
/// </summary>
class RepoConfig
{
public RepoConfig()
{
if (!File.Exists(RepoTGS3SettingsPath))
return;
var rawdata = File.ReadAllText(RepoTGS3SettingsPath);
var Deserializer = new JavaScriptSerializer();
var json = Deserializer.Deserialize<IDictionary<string, object>>(rawdata);
try
{
var details = (IDictionary<string, object>)json["changelog"];
PathToChangelogPy = (string)details["script"];
ChangelogPyArguments = (string)details["arguments"];
ChangelogSupport = true;
try
{
PipDependancies = (IList<string>)details["pip_dependancies"];
}
catch { }
try
{
ChangelogPathsToStage = (IList<string>)details["synchronize_paths"];
}
catch { }
}
catch {
ChangelogSupport = false;
}
try
{
StaticDirectoryPaths = (IList<string>)json["static_directories"];
}
catch { }
try
{
DLLPaths = (IList<string>)json["dlls"];
}
catch { }
}
public readonly bool ChangelogSupport;
public readonly string PathToChangelogPy;
public readonly string ChangelogPyArguments;
public readonly IList<string> PipDependancies = new List<string>();
public readonly IList<string> ChangelogPathsToStage = new List<string>();
public readonly IList<string> StaticDirectoryPaths = new List<string>();
public readonly IList<string> DLLPaths = new List<string>();
}
RepoConfig _CurrentRepoConfig;
void InitRepo()
{
if(Exists())
UpdateInterfaceDll(false);
}
RepoConfig LoadRepoConfig()
{
if (_CurrentRepoConfig == null)
lock (RepoLock)
{
if (RepoBusy)
return null;
if (LoadRepo() == null)
_CurrentRepoConfig = new RepoConfig();
}
return _CurrentRepoConfig;
}
//public api
public bool OperationInProgress()
{
@@ -155,12 +228,8 @@ namespace TGServerService
//create an ssh remote for pushing
Repo.Network.Remotes.Add(SSHPushRemote, RepoURL.Replace("git://", "ssh://").Replace("https://", "ssh://"));
lock (configLock)
{
Program.CopyDirectory(RepoConfig, StaticConfigDir);
}
Program.CopyDirectory(RepoData, StaticDataDir, null, true);
File.Copy(RepoPath + LibMySQLFile, StaticDirs + LibMySQLFile, true);
InitialConfigureRepository();
SendMessage("REPO: Clone complete!", ChatMessageType.DeveloperInfo);
TGServerService.WriteInfo("Repository {0}:{1} successfully cloned", TGServerService.EventID.RepoClone);
}
@@ -185,6 +254,48 @@ namespace TGServerService
}
}
void InitialConfigureRepository()
{
Directory.CreateDirectory(StaticDirs);
UpdateInterfaceDll(false);
var Config = new RepoConfig(); //RepoBusy is set if we're here
foreach(var I in Config.StaticDirectoryPaths)
{
try
{
var source = Path.Combine(RepoPath, I);
var dest = Path.Combine(StaticDirs, I);
if (Directory.Exists(source))
Program.CopyDirectory(source, dest);
else
Directory.CreateDirectory(dest);
}
catch
{
TGServerService.WriteWarning("Could not setup static directory: " + I, TGServerService.EventID.RepoConfigurationFail);
}
}
foreach(var I in Config.DLLPaths)
{
try
{
var source = Path.Combine(RepoPath, I);
if (!File.Exists(source))
{
TGServerService.WriteWarning("Could not find DLL: " + I, TGServerService.EventID.RepoConfigurationFail);
continue;
}
var dest = Path.Combine(StaticDirs, I);
Program.CopyFileForceDirectories(source, dest, false);
}
catch
{
TGServerService.WriteWarning("Could not setup static DLL: " + I, TGServerService.EventID.RepoConfigurationFail);
}
}
_CurrentRepoConfig = Config;
}
//kicks off the cloning thread
//public api
public string Setup(string RepoURL, string BranchName)
@@ -324,6 +435,7 @@ namespace TGServerService
Commands.Checkout(Repo, sha, Opts);
var res = ResetNoLock(null);
UpdateSubmodules();
_CurrentRepoConfig = new RepoConfig();
SendMessage("REPO: Checkout complete!", ChatMessageType.DeveloperInfo);
TGServerService.WriteInfo("Repo checked out " + sha, TGServerService.EventID.RepoCheckout);
return res;
@@ -391,6 +503,7 @@ namespace TGServerService
if (res != null)
throw new Exception(res);
UpdateSubmodules();
_CurrentRepoConfig = new RepoConfig();
TGServerService.WriteInfo("Repo merge updated to " + originBranch.Tip.Sha, TGServerService.EventID.RepoMergeUpdate);
return null;
}
@@ -497,6 +610,7 @@ namespace TGServerService
lock (RepoLock)
{
var res = LoadRepo() ?? ResetNoLock(trackedBranch ? (Repo.Head.TrackedBranch ?? Repo.Head) : Repo.Head);
_CurrentRepoConfig = new RepoConfig();
if (res == null)
{
SendMessage(String.Format("REPO: Hard reset to {0}branch", trackedBranch ? "tracked " : ""), ChatMessageType.DeveloperInfo);
@@ -604,6 +718,7 @@ namespace TGServerService
if (Result == null)
try
{
_CurrentRepoConfig = new RepoConfig();
UpdateSubmodules();
}
catch (Exception e)
@@ -727,9 +842,12 @@ namespace TGServerService
public string PushChangelog()
{
if (!SSHAuth())
var Config = LoadRepoConfig();
if (Config == null)
return "Error reading changelog configuration";
if(!Config.ChangelogSupport || !SSHAuth())
return null;
return LocalIsRemote() ? Commit() ?? Push() : "Can't push changelog: HEAD does not match tracked remote branch";
return LocalIsRemote() ? Commit(Config) ?? Push() : "Can't push changelog: HEAD does not match tracked remote branch";
}
FetchOptions GenerateFetchOptions()
@@ -781,7 +899,7 @@ namespace TGServerService
}
}
string Commit()
string Commit(RepoConfig Config)
{
lock (RepoLock)
{
@@ -791,8 +909,8 @@ namespace TGServerService
try
{
// Stage the file
Commands.Stage(Repo, "html/changelog.html");
Commands.Stage(Repo, "html/changelogs/*");
foreach(var I in Config.ChangelogPathsToStage)
Commands.Stage(Repo, I);
var status = Repo.RetrieveStatus();
var sum = status.Added.Count() + status.Removed.Count() + status.Modified.Count();
@@ -878,9 +996,19 @@ namespace TGServerService
//impl proc just for single level recursion
public string GenerateChangelogImpl(out string error, bool recurse = false)
{
const string ChangelogPy = RepoPath + "/tools/ss13_genchangelog.py";
const string ChangelogHtml = RepoPath + "/html/changelog.html";
const string ChangelogDir = RepoPath + "/html/changelogs";
var RConfig = LoadRepoConfig();
if (RConfig == null)
{
error = null;
return "Error loading changelog config!";
}
if (!RConfig.ChangelogSupport)
{
error = null;
return null;
}
string ChangelogPy = Path.Combine(RepoPath, RConfig.PathToChangelogPy);
if (!Exists())
{
error = "Repo does not exist!";
@@ -899,23 +1027,13 @@ namespace TGServerService
error = "Missing changelog generation script!";
return null;
}
if (!File.Exists(ChangelogHtml))
{
error = "Missing changelog html!";
return null;
}
if (!Directory.Exists(ChangelogDir))
{
error = "Missing auto changelog directory!";
return null;
}
var Config = Properties.Settings.Default;
var PythonFile = Config.PythonPath + "/python.exe";
if (!File.Exists(PythonFile))
{
error = "Cannot locate python 2.7!";
error = "Cannot locate python!";
return null;
}
try
@@ -925,7 +1043,7 @@ namespace TGServerService
using (var python = new Process())
{
python.StartInfo.FileName = PythonFile;
python.StartInfo.Arguments = String.Format("{0} {1} {2}", ChangelogPy, ChangelogHtml, ChangelogDir);
python.StartInfo.Arguments = String.Format("{0} {1}", ChangelogPy, RConfig.ChangelogPyArguments);
python.StartInfo.UseShellExecute = false;
python.StartInfo.RedirectStandardOutput = true;
python.Start();
@@ -939,7 +1057,7 @@ namespace TGServerService
}
if (exitCode != 0)
{
if (recurse)
if (recurse || RConfig.PipDependancies.Count == 0)
{
error = "Script failed!";
return result;
@@ -947,12 +1065,11 @@ namespace TGServerService
//update pip deps and try again
string PipFile = Config.PythonPath + "/scripts/pip.exe";
bool runningBSoup = false;
while (true)
foreach(var I in RConfig.PipDependancies)
using (var pip = new Process())
{
pip.StartInfo.FileName = PipFile;
pip.StartInfo.Arguments = !runningBSoup ? "install PyYaml" : "install beautifulsoup4";
pip.StartInfo.Arguments = "install " + I;
pip.StartInfo.UseShellExecute = false;
pip.StartInfo.RedirectStandardOutput = true;
pip.Start();
@@ -966,11 +1083,6 @@ namespace TGServerService
error = "Script and pip failed!";
return result;
}
if (runningBSoup)
break;
else
runningBSoup = true;
}
//and recurse
return GenerateChangelogImpl(out error, true);
+1
View File
@@ -80,6 +80,7 @@ namespace TGServerService
PreactionFail = 6900,
InteropCallException = 7000,
APIVersionMismatch = 7100,
RepoConfigurationFail = 7200,
}
static TGServerService ActiveService; //So everyone else can write to our eventlog
+4 -275
View File
@@ -4,160 +4,6 @@ using System.ServiceModel;
namespace TGServiceInterface
{
/// <summary>
/// The type of string -> string config being modified
/// </summary>
public enum TGConfigType
{
/// <summary>
/// dbconfig.txt
/// </summary>
Database,
/// <summary>
/// game_options.txt
/// </summary>
Game,
/// <summary>
/// config.txt
/// </summary>
General,
}
/// <summary>
/// Map configuration settings
/// </summary>
[DataContract]
public class MapSetting
{
/// <summary>
/// The name of the map
/// </summary>
[DataMember]
public string Name { get; set; }
/// <summary>
/// If this is the default voted map
/// </summary>
[DataMember]
public bool Default { get; set; }
/// <summary>
/// The voteweight of the map
/// </summary>
[DataMember]
public float VoteWeight { get; set; }
/// <summary>
/// The minimum number of players to run this map
/// </summary>
[DataMember]
public int MinPlayers { get; set; }
/// <summary>
/// The maximum number of players to run this map
/// </summary>
[DataMember]
public int MaxPlayers { get; set; }
/// <summary>
/// If the map is enabled
/// </summary>
[DataMember]
public bool Enabled { get; set; }
}
/// <summary>
/// Used for white/blacklisting certain map files
/// </summary>
[DataContract]
public class MapEnabled
{
/// <summary>
/// The file name of the map
/// </summary>
[DataMember]
public string Filename { get; set; }
/// <summary>
/// True if the map is enabled, false otherwise
/// </summary>
[DataMember]
public bool Enabled { get; set; }
}
/// <summary>
/// Game configuration setting
/// </summary>
[DataContract]
public class ConfigSetting
{
/// <summary>
/// The setting name
/// </summary>
[DataMember]
public string Name { get; set; }
/// <summary>
/// Comments above the setting
/// </summary>
[DataMember]
public string Comment { get; set; }
/// <summary>
/// True if this setting exists in the current configuration
/// </summary>
[DataMember]
public bool ExistsInStatic { get; set; }
/// <summary>
/// True if this setting exists in the repo's config
/// </summary>
[DataMember]
public bool ExistsInRepo { get; set; }
/// <summary>
/// True if this value appears more than once in either config
/// </summary>
[DataMember]
public bool IsMultiKey { get; set; }
/// <summary>
/// Value of the setting
/// null means unset, empty string means flag
/// </summary>
[DataMember]
public string Value { get; set; }
/// <summary>
/// For when the first word of the config setting can be repeated many times
/// Usually null
/// </summary>
[DataMember]
public List<string> Values { get; set; }
/// <summary>
/// Value of the setting in the repo
/// null means unset, empty string means flag
/// </summary>
[DataMember]
public string DefaultValue { get; set; }
/// <summary>
/// For when the first word of the config setting can be repeated many times
/// Usually null
/// </summary>
[DataMember]
public List<string> DefaultValues { get; set; }
}
/// <summary>
/// Setting for job populations
/// </summary>
[DataContract]
public class JobSetting
{
/// <summary>
/// The name of the job
/// </summary>
[DataMember]
public string Name { get; set; }
/// <summary>
/// Number of total positions for this job, -1 for infinite
/// </summary>
[DataMember]
public int TotalPositions { get; set; }
/// <summary>
/// Number of positions for this job when the game starts, -1 for infinite
/// </summary>
[DataMember]
public int SpawnPositions { get; set; }
}
/// <summary>
/// For modifying the in game config
/// Most if not all of these will not apply until the next server reboot
@@ -165,114 +11,6 @@ namespace TGServiceInterface
[ServiceContract]
public interface ITGConfig
{
/// <summary>
/// Gets the config settings of some config
/// </summary>
/// <param name="type">The type of config to retrieve</param>
/// <param name="error">null on success, error message on failure</param>
/// <returns>The list of configs on success or null on failure</returns>
[OperationContract]
IList<ConfigSetting> Retrieve(TGConfigType type, out string error);
/// <summary>
/// Sets a config setting of some config
/// </summary>
/// <param name="type">The type of config to retrieve</param>
/// <param name="newSetting">The updated config setting, only name and value fields are read</param>
/// <returns>null on success, error message on failure</returns>
[OperationContract]
string SetItem(TGConfigType type, ConfigSetting newSetting);
/// <summary>
/// List the permissions as defined in the repo's admin_ranks.txt
/// </summary>
/// <param name="error">null on success, error message on failure</param>
/// <returns>Dictionary of permission strings -> description on success, null on failure</returns>
[OperationContract]
IDictionary<string, string> ListPermissions(out string error);
/// <summary>
/// Get the configured admin ranks
/// </summary>
/// <param name="error">null on success, error message on failure</param>
/// <returns>A dictionary of rank -> permissions -> +/- on success, null on failure</returns>
[OperationContract]
IDictionary<string, IDictionary<string, bool>> AdminRanks(out string error);
/// <summary>
/// Deletes the given admin rank
/// </summary>
/// <param name="rank">The rank to delete</param>
/// <returns>null on success, error message on failure</returns>
[OperationContract]
string RemoveAdminRank(string rank);
/// <summary>
/// Adds or update an admin rank
/// </summary>
/// <param name="rankName">The name of the added rank</param>
/// <param name="permissions">The permissions of the added rank</param>
/// <returns>null on success, error message on failure</returns>
[OperationContract]
string SetAdminRank(string rankName, IDictionary<string, bool> permissions);
/// <summary>
/// List the admins
/// </summary>
/// <param name="error">null on success, error message on failure</param>
/// <returns>a dictionary of ckey -> admin rank on success, null on failure</returns>
[OperationContract]
IDictionary<string, string> Admins(out string error);
/// <summary>
/// Add or modify a ckey's admin status
/// </summary>
/// <param name="ckey">The byond ckey to modify</param>
/// <param name="rank">The rank of the admin</param>
/// <returns>null on success, error message on failure</returns>
[OperationContract]
string Addmin(string ckey, string rank);
/// <summary>
/// Remove the admin status of a ckey
/// </summary>
/// <param name="admin">The ckey to deadmin</param>
/// <returns></returns>
[OperationContract]
string Deadmin(string admin);
/// <summary>
/// List the job population limits
/// </summary>
/// <param name="error">null on success, error message on failure</param>
/// <returns>A list of JobSettings</returns>
[OperationContract]
IList<JobSetting> Jobs(out string error);
/// <summary>
/// Set the population limits for a job
/// </summary>
/// <param name="job">The population limits</param>
/// <returns>null on success, error on failure</returns>
[OperationContract]
string SetJob(JobSetting job);
/// <summary>
/// Lists game map settings
/// </summary>
/// <param name="error">null on success, error on failure</param>
/// <returns>The list of MapSettings</returns>
[OperationContract]
IList<MapSetting> MapSettings(out string error);
/// <summary>
/// Sets a game map's settings
/// </summary>
/// <param name="newSetting">The new setting for the map</param>
/// <returns>null on success, error on failure</returns>
[OperationContract]
string SetMapSettings(MapSetting newSetting);
/// <summary>
/// Return the directory of the server on the host machine
/// </summary>
@@ -280,24 +18,15 @@ namespace TGServiceInterface
[OperationContract]
string ServerDirectory();
/// <summary>
/// Upload a titlescreen image
/// </summary>
/// <param name="filename">The name of the file saved in config/title_screens/images</param>
/// <param name="data">The bytes of the file, null will delete the file</param>
/// <returns>null on success, error message on failure</returns>
[OperationContract]
string SetTitleImage(string filename, byte[] data);
/// <summary>
/// For when you really just need to see the raw data of the config
/// </summary>
/// <param name="configRelativePath">The path from the configDir. E.g. config.txt</param>
/// <param name="repo">if true, the file will be read </param>
/// <param name="staticRelativePath">The path from the Static dir. E.g. config/config.txt</param>
/// <param name="repo">if true, the file will be read from the repository instead of the static dir</param>
/// <param name="error">null on success, error message on failure</param>
/// <returns>The full text of the file on success, null on failure</returns>
[OperationContract]
string ReadRaw(string configRelativePath, bool repo, out string error);
string ReadText(string staticRelativePath, bool repo, out string error);
/// <summary>
/// For when you really just need to set the raw data of the config
@@ -306,6 +35,6 @@ namespace TGServiceInterface
/// <param name="data">The full text of the config file</param>
/// <returns>null on success, error message on failure</returns>
[OperationContract]
string WriteRaw(string configRelativePath, string data);
string WriteText(string staticRelativePath, string data);
}
}
+1
View File
@@ -73,6 +73,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "DMAPI", "DMAPI", "{9032B448
DMAPI\server_tools.dm = DMAPI\server_tools.dm
DMAPI\st_commands.dm = DMAPI\st_commands.dm
DMAPI\st_interface.dm = DMAPI\st_interface.dm
TGS3.json = TGS3.json
EndProjectSection
EndProject
Global