@@ -0,0 +1,112 @@
using System.Collections.Generic;
using System.Drawing;
using System.Media;
using System.Windows.Forms;

namespace Chernobyl_Relay_Chat
{
class CRCDisplay
{
private static ClientDisplay clientDisplay;
private static List<string> users;

public static void Start()
{
clientDisplay = new ClientDisplay();
Application.Run(clientDisplay);
}

public static void Stop()
{
clientDisplay.Close();
}

public static void OnConnected()
{
clientDisplay?.Enable();
clientDisplay?.AddInformation("You are now connected to the network");
}

public static void OnChannelActiveSynced(List<string> usersOnJoin)
{
users = usersOnJoin;
clientDisplay?.UpdateUsers(users);
}

public static void OnHighlightMessage(string nick, string message)
{
clientDisplay?.AddHighlightMessage(nick, message);
}

public static void OnChannelMessage(string nick, string message)
{
clientDisplay?.AddMessage(nick, message, Color.Black);
}

public static void OnOwnChannelMessage(string nick, string message)
{
clientDisplay?.AddMessage(nick, message, Color.Gray);
}

public static void OnQueryMessage(string from, string to, string message)
{
SystemSounds.Asterisk.Play();
clientDisplay?.AddMessage(from + " -> " + to, message, Color.DeepPink);
}

public static void OnJoin(string nick)
{
users.Add(nick);
clientDisplay?.UpdateUsers(users);
clientDisplay?.AddInformation(nick + " has logged on");
}

public static void OnPart(string nick)
{
users.Remove(nick);
clientDisplay?.UpdateUsers(users);
clientDisplay?.AddInformation(nick + " has logged off");
}

public static void OnKick(string victim, string reason)
{
users.Remove(victim);
clientDisplay?.UpdateUsers(users);
clientDisplay?.AddInformation(victim + " has been kicked for: " + reason);
}

public static void OnGotKicked(string reason)
{
users.Clear();
clientDisplay?.UpdateUsers(users);
clientDisplay?.AddError("You have been kicked for: " + reason);
clientDisplay?.Disable();
}

public static void OnNickChange(string oldNick, string newNick)
{
users.Remove(oldNick);
users.Add(newNick);
clientDisplay?.UpdateUsers(users);
clientDisplay?.AddInformation(oldNick + " is now known as " + newNick);
}

public static void OnOwnNickChange(string oldNick, string newNick)
{
users.Remove(oldNick);
users.Add(newNick);
clientDisplay?.UpdateUsers(users);
clientDisplay?.AddInformation("You are now known as " + newNick);
}

public static void OnBanned()
{
clientDisplay?.AddError("Woops, you're banned!");
}

public static void OnError(string message)
{
clientDisplay?.AddError("Error: " + message);
}
}
}
@@ -9,30 +9,31 @@

namespace Chernobyl_Relay_Chat
{
class CRCGame : ICRCSendable
class CRCGame
{
private static readonly CRCGameWrapper wrapper = new CRCGameWrapper();
private static readonly Encoding encoding = Encoding.GetEncoding(1251);
private static readonly Regex outputRx = new Regex("^(.+?)(?:/(.+))?$");
private static readonly Regex messageRx = new Regex("^(.+?)/(.+)$");
private static readonly Regex deathRx = new Regex("^(.+?)/(.+?)/(.+?)/(.+)$");

private ClientDisplay display;
private CRCClient client;
private static bool disable = false;
private static int processID = -1;
private static string gamePath;
private static bool firstClear = false;
private static StringBuilder sendQueue = new StringBuilder();
private static object queueLock = new object();

private bool disable = false;
private int processID = -1;
private string gamePath;
private bool firstClear = false;
private StringBuilder sendQueue = new StringBuilder();
private object queueLock = new object();
private Regex outputRx = new Regex("^(.+?)(?:/(.+))?$");
private Regex messageRx = new Regex("^(.+?)/(.+)$");
private Regex deathRx = new Regex("^(.+?)/(.+?)/(.+?)/(.+)$");
private static ClientDisplay display;
private static CRCClient client;

public CRCGame(ClientDisplay clientDisplay, CRCClient crcClient)
{
display = clientDisplay;
client = crcClient;
}

private void Disable()
private static void Disable()
{
disable = true;
MessageBox.Show("CRC was unable to read or write files needed to communicate with the game.\r\n"
@@ -41,7 +42,7 @@ private void Disable()
"Chernobyl Relay Chat", MessageBoxButtons.OK, MessageBoxIcon.Error);
}

public void GameCheck()
public static void GameCheck()
{
if (disable) return;

@@ -80,7 +81,7 @@ public void GameCheck()
}
}

public void GameUpdate()
public static void GameUpdate()
{
if (disable || processID == -1) return;

@@ -122,14 +123,14 @@ public void GameUpdate()
string faction = messageMatch.Groups[1].Value;
string message = messageMatch.Groups[2].Value;
if (message[0] == '/')
CRCCommands.ProcessCommand(message, this);
CRCCommands.ProcessCommand(message, wrapper);
else
{
CRCOptions.GameFaction = CRCStrings.ValidateFaction(faction);
if (CRCOptions.GameFaction == "actor_zombied")
client.Send(CRCZombie.Generate());
CRCClient.Send(CRCZombie.Generate());
else
client.Send(message);
CRCClient.Send(message);
}
}
else if (type == "Death" && CRCOptions.SendDeath)
@@ -143,7 +144,7 @@ public void GameUpdate()
if (CRCOptions.GameFaction != "actor_zombied")
{
string message = CRCStrings.DeathMessage(CRCOptions.Name, level, xrClass, section);
client.SendDeath(message);
CRCClient.SendDeath(message);
}
}
}
@@ -172,13 +173,13 @@ public void GameUpdate()
}
}

public void UpdateSettings()
public static void UpdateSettings()
{
SendToGame("Setting/NewsDuration/" + (CRCOptions.NewsDuration * 1000));
SendToGame("Setting/ChatKey/DIK_" + CRCOptions.ChatKey);
}

private void SendToGame(string line)
private static void SendToGame(string line)
{
if (disable || processID == -1) return;

@@ -188,80 +189,80 @@ private void SendToGame(string line)
}
}

public void AddInformation(string message)
public static void AddInformation(string message)
{
SendToGame("Information/" + message);
}

public void AddError(string message)
public static void AddError(string message)
{
SendToGame("Error/" + message);
}


public void OnBanned()
public static void OnBanned()
{
SendToGame("Error/Woops, you're banned!");
AddError("Woops, you're banned!");
}

public void OnError(string message)
public static void OnError(string message)
{
SendToGame("Error/" + message);
AddError(message);
}

public void OnUpdate(string message)
public static void OnUpdate(string message)
{
SendToGame("Information/" + message);
AddInformation(message);
}

public void OnConnected()
public static void OnConnected()
{
SendToGame("Information/You are now connected to the network");
AddInformation("You are now connected to the network");
}

public void OnHighlightMessage(string nick, string faction, string message)
public static void OnHighlightMessage(string nick, string faction, string message)
{
SendToGame("Highlight/" + faction + "/" + nick + "/" + message);
}

public void OnChannelMessage(string nick, string faction, string message)
public static void OnChannelMessage(string nick, string faction, string message)
{
SendToGame("Message/" + faction + "/" + nick + "/" + message);
}

public void OnQueryMessage(string from, string to, string faction, string message)
public static void OnQueryMessage(string from, string to, string faction, string message)
{
SendToGame("Query/" + faction + "/" + from + "/" + to + "/" + message);
}

public void OnJoin(string nick)
public static void OnJoin(string nick)
{
SendToGame("Information/" + nick + " has logged on");
AddInformation(nick + " has logged on");
}

public void OnPart(string nick)
public static void OnPart(string nick)
{
SendToGame("Information/" + nick + " has logged off");
AddInformation(nick + " has logged off");
}

public void OnKick(string victim, string reason)
public static void OnKick(string victim, string reason)
{
SendToGame("Information/" + victim + " has been kicked for: " + reason);
AddInformation(victim + " has been kicked for: " + reason);
}

public void OnGotKicked(string reason)
public static void OnGotKicked(string reason)
{
SendToGame("Information/You have been kicked for: " + reason);
AddInformation("You have been kicked for: " + reason);
}

public void OnNickChange(string oldNick, string newNick)
public static void OnNickChange(string oldNick, string newNick)
{
SendToGame("Information/" + oldNick + " is now known as " + newNick);
AddInformation(oldNick + " is now known as " + newNick);
}

public void OnOwnNickChange(string newNick)
public static void OnOwnNickChange(string newNick)
{
SendToGame("Information/You are now known as " + newNick);
AddInformation("You are now known as " + newNick);
}
}
}
@@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Chernobyl_Relay_Chat
{
class CRCGameWrapper : ICRCSendable
{
public void AddError(string message)
{
CRCGame.AddError(message);
}

public void AddInformation(string message)
{
CRCGame.AddInformation(message);
}
}
}
@@ -13,13 +13,13 @@ public class CRCStrings
private const int REMARK_CHANCE = 25;

private static readonly Random rand = new Random();
private static readonly List<string> deathFormats, deathTimes, deathObservances, deathRemarks, deathGeneric;
private static readonly Dictionary<string, List<string>> deathLevels, deathSections, deathClasses, fNames, sNames;
private static List<string> deathFormats, deathTimes, deathObservances, deathRemarks, deathGeneric;
private static Dictionary<string, List<string>> deathLevels, deathSections, deathClasses, fNames, sNames;

private static readonly Regex invalidNickRx = new Regex(@"[^a-zA-Z0-9_\-\\^{}|]");
private static readonly Regex invalidNickFirstCharRx = new Regex(@"[^a-zA-Z_\\^{}|]");

static CRCStrings()
public static void Load()
{
deathFormats = loadXmlList(@"res\death_formats.xml");
deathTimes = loadXmlList(@"res\death_times.xml");
@@ -37,7 +37,7 @@ public static bool CheckFirstUpdate()
return false;
}

public static async Task CheckUpdate(ClientDisplay display, CRCClient client)
public static async Task<bool> CheckUpdate()
{
UpdateChecker updateChecker = new UpdateChecker("TKGP", "Chernobyl-Relay-Chat");
UpdateType updateType;
@@ -47,21 +47,22 @@ public static async Task CheckUpdate(ClientDisplay display, CRCClient client)
}
catch (RateLimitExceededException)
{
return;
return false;
}
if (updateType != UpdateType.None)
{
string releaseNotes = await updateChecker.RenderReleaseNotes();
SystemSounds.Asterisk.Play();
client.OnUpdate("A mandatory update for CRC is available. Please check the external client to download it.");
CRCGame.OnUpdate("A mandatory update for CRC is available. Please check the external client to download it.");
using (UpdateForm updateForm = new UpdateForm((updateType == UpdateType.Major || updateType == UpdateType.Minor), releaseNotes))
{
DialogResult dialogResult = updateForm.ShowDialog();
if (dialogResult == DialogResult.OK)
updateChecker.DownloadAsset("Chernobyl-Relay-Chat.zip");
}
display.Close();
return true;
}
return false;
}
}
}
@@ -85,6 +85,8 @@
<Compile Include="CRCClient.cs" />
<Compile Include="CRCCommand.cs" />
<Compile Include="CRCCommands.cs" />
<Compile Include="CRCDisplay.cs" />
<Compile Include="CRCGameWrapper.cs" />
<Compile Include="ICRCSendable.cs" />
<Compile Include="KeyPromptForm.cs">
<SubType>Form</SubType>
@@ -10,13 +10,23 @@ namespace Chernobyl_Relay_Chat
{
public partial class ClientDisplay : Form, ICRCSendable
{
private CRCClient client;
private static ClientDisplay clientDisplay;

private List<string> users = new List<string>();
private Font mainFont, boldFont, timeFont;

public ClientDisplay()
{
InitializeComponent();
if (clientDisplay == null)
clientDisplay = this;
else
clientDisplay.AddError("FUCK");
}

~ClientDisplay()
{
clientDisplay = null;
}

private void ClientDisplay_Load(object sender, EventArgs e)
@@ -32,7 +42,6 @@ private void ClientDisplay_Load(object sender, EventArgs e)
}

AddInformation("Connecting...");
client = new CRCClient(this);
}

private void ClientDisplay_FormClosing(object sender, FormClosingEventArgs e)
@@ -48,12 +57,12 @@ private void ClientDisplay_FormClosing(object sender, FormClosingEventArgs e)
CRCOptions.DisplaySize = RestoreBounds.Size;
}

client.Close();
CRCClient.Stop();
}

private void buttonOptions_Click(object sender, EventArgs e)
{
new OptionsForm(client).Show();
new OptionsForm().Show();
}

private void buttonSend_Click(object sender, EventArgs e)
@@ -65,24 +74,24 @@ private void buttonSend_Click(object sender, EventArgs e)
}
else if (trimmed.Length > 0)
{
client.Send(trimmed);
CRCClient.Send(trimmed);
}
textBoxInput.Clear();
}

private void timerGameCheck_Tick(object sender, EventArgs e)
{
client.GameCheck();
CRCGame.GameCheck();
}

private void timerGameUpdate_Tick(object sender, EventArgs e)
{
client.GameUpdate();
CRCGame.GameUpdate();
}

private async void timerCheckUpdate_Tick(object sender, EventArgs e)
{
await CRCUpdate.CheckUpdate(this, client);
bool result = await CRCUpdate.CheckUpdate();
}

private void richTextBoxMessages_LinkClicked(object sender, LinkClickedEventArgs e)
@@ -92,6 +101,23 @@ private void richTextBoxMessages_LinkClicked(object sender, LinkClickedEventArgs
}


public void Enable()
{
Invoke(() =>
{
buttonSend.Enabled = true;
buttonOptions.Enabled = true;
});
}

public void Disable()
{
Invoke(() =>
{
buttonSend.Enabled = false;
buttonOptions.Enabled = false;
});
}

private void AddLinePrefix()
{
@@ -105,15 +131,15 @@ private void AddLinePrefix()
}
}

private void AddLine(string line, Color color)
public void AddLine(string line, Color color)
{
Invoke(new Action(() =>
Invoke(() =>
{
AddLinePrefix();
richTextBoxMessages.SelectionFont = mainFont;
richTextBoxMessages.SelectionColor = color;
richTextBoxMessages.AppendText(line);
}));
});
}

public void AddInformation(string line)
@@ -126,9 +152,9 @@ public void AddError(string line)
AddLine(line, Color.Red);
}

private void AddMessage(string nick, string message, Color nickColor)
public void AddMessage(string nick, string message, Color nickColor)
{
Invoke(new Action(() =>
Invoke(() =>
{
AddLinePrefix();
richTextBoxMessages.SelectionFont = boldFont;
@@ -137,39 +163,12 @@ private void AddMessage(string nick, string message, Color nickColor)
richTextBoxMessages.SelectionFont = mainFont;
richTextBoxMessages.SelectionColor = Color.Black;
richTextBoxMessages.AppendText(message);
}));
}

private void UpdateUsers()
{
users.Sort();
Invoke(new Action(() =>
{
textBoxUsers.Text = string.Join("\r\n", users);
}));
}



public void OnConnected()
{
Invoke(new Action(() =>
{
buttonSend.Enabled = true;
buttonOptions.Enabled = true;
}));
AddInformation("You are now connected to the network");
}

public void OnChannelActiveSynced(List<string> usersOnJoin)
{
users = usersOnJoin;
UpdateUsers();
});
}

public void OnHighlightMessage(string nick, string message)
public void AddHighlightMessage(string nick, string message)
{
Invoke(new Action(() =>
Invoke(() =>
{
AddMessage(nick, message, Color.Black);
int start = richTextBoxMessages.GetFirstCharIndexOfCurrentLine();
@@ -178,82 +177,20 @@ public void OnHighlightMessage(string nick, string message)
richTextBoxMessages.SelectionBackColor = Color.Yellow;
richTextBoxMessages.Select(0, 0);
richTextBoxMessages.SelectionBackColor = Color.White;
}));
});
}

public void OnChannelMessage(string nick, string message)
public void UpdateUsers(List<string> users)
{
AddMessage(nick, message, Color.Black);
}

public void OnOwnChannelMessage(string nick, string message)
{
AddMessage(nick, message, Color.Gray);
}

public void OnQueryMessage(string from, string to, string message)
{
SystemSounds.Asterisk.Play();
AddMessage(from + " -> " + to, message, Color.DeepPink);
}

public void OnJoin(string nick)
{
users.Add(nick);
UpdateUsers();
AddInformation(nick + " has logged on");
}

public void OnPart(string nick)
{
users.Remove(nick);
UpdateUsers();
AddInformation(nick + " has logged off");
}

public void OnKick(string victim, string reason)
{
users.Remove(victim);
UpdateUsers();
AddInformation(victim + " has been kicked for: " + reason);
}

public void OnGotKicked(string reason)
{
users.Clear();
UpdateUsers();
Invoke(new Action(() =>
{
buttonSend.Enabled = false;
buttonOptions.Enabled = false;
}));
AddError("You have been kicked for: " + reason);
}

public void OnNickChange(string oldNick, string newNick)
{
users.Remove(oldNick);
users.Add(newNick);
UpdateUsers();
AddInformation(oldNick + " is now known as " + newNick);
}

public void OnOwnNickChange(string oldNick, string newNick)
{
users.Remove(oldNick);
users.Add(newNick);
UpdateUsers();
AddInformation("You are now known as " + newNick);
}

public void OnBanned()
{
AddError("Woops, you're banned!");
users.Sort();
Invoke(() =>
textBoxUsers.Text = string.Join("\r\n", users
));
}

public void OnError(string message)
private void Invoke(Action action)
{
AddError("Error: " + message);
base.Invoke(action);
}
}
}
@@ -7,8 +7,6 @@ namespace Chernobyl_Relay_Chat
{
public partial class OptionsForm : Form
{
private CRCClient client;

private readonly Dictionary<string, int> factionToIndex = new Dictionary<string, int>()
{
["actor_bandit"] = 0,
@@ -35,10 +33,9 @@ public partial class OptionsForm : Form
[8] = "actor_monolith",
};

public OptionsForm(CRCClient crcClient)
public OptionsForm()
{
InitializeComponent();
client = crcClient;

radioButtonFactionAuto.Checked = CRCOptions.AutoFaction;
radioButtonFactionManual.Checked = !CRCOptions.AutoFaction;
@@ -73,7 +70,7 @@ private void buttonOK_Click(object sender, EventArgs e)
CRCOptions.NewsDuration = (int)numericUpDownNewsDuration.Value;
CRCOptions.ChatKey = textBoxChatKey.Text;
CRCOptions.Save();
client.UpdateSettings();
CRCClient.UpdateSettings();
this.Close();
}

@@ -9,29 +9,38 @@ static class Program
// GUID from AssemblyInfo.cs
private static readonly Mutex mutex = new Mutex(false, "64eb5dda-2131-47fc-a32c-fbc64f440d8a");

private static Thread displayThread, clientThread;

[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);

// Prevent multiple instances
if (!mutex.WaitOne(TimeSpan.FromSeconds(0), false))
return;

if (CRCUpdate.CheckFirstUpdate())
return;

if (CRCOptions.Load())
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new ClientDisplay());
CRCOptions.Save();
}
else

CRCStrings.Load();
bool optionsLoaded = CRCOptions.Load();
if (!optionsLoaded)
{
MessageBox.Show("CRC was unable to access the registry, which is needed to preserve settings.\r\n"
+ "Please try running the application As Administrator.",
"Chernobyl Relay Chat", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}

displayThread = new Thread(CRCDisplay.Start);
displayThread.Start();
clientThread = new Thread(CRCClient.Start);
clientThread.Start();

clientThread.Join();
displayThread.Join();
}
}
}