mirror of
https://github.com/tgstation/tgstation-server.git
synced 2026-08-25 14:06:41 +01:00
Merge upstream
This commit is contained in:
@@ -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";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
Generated
+1668
-2065
File diff suppressed because it is too large
Load Diff
@@ -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)
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
@@ -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
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -18,6 +18,7 @@ namespace TGServerService
|
||||
{
|
||||
FindTheDroidsWereLookingFor();
|
||||
InitChat();
|
||||
InitRepo();
|
||||
InitByond();
|
||||
InitCompiler();
|
||||
InitDreamDaemon();
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
@@ -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,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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user