diff --git a/AUTHORS b/AUTHORS index 51349db1c4..dd6f01de0d 100644 --- a/AUTHORS +++ b/AUTHORS @@ -151,6 +151,9 @@ Motmans and distributed under the MIT license. Using ICSharpCode.SharpZipLib initially by Mike Krueger and distributed under the GNU GPL terms. +Using SmartIrc4Net developed by Mirco Bauer +distributed under the LGPL version 2.1 or later. + Finally, special thanks goes to the original teams at Westwood Studios and EA for creating the classic diff --git a/Makefile b/Makefile index 5781604907..ecde9d2d0a 100644 --- a/Makefile +++ b/Makefile @@ -40,7 +40,7 @@ CSC = dmcs CSFLAGS = -nologo -warn:4 -codepage:utf8 -unsafe -warnaserror DEFINE = TRACE -COMMON_LIBS = System.dll System.Core.dll System.Data.dll System.Data.DataSetExtensions.dll System.Drawing.dll System.Xml.dll thirdparty/download/ICSharpCode.SharpZipLib.dll thirdparty/download/FuzzyLogicLibrary.dll thirdparty/download/Mono.Nat.dll thirdparty/download/MaxMind.Db.dll thirdparty/download/MaxMind.GeoIP2.dll thirdparty/download/Eluant.dll +COMMON_LIBS = System.dll System.Core.dll System.Data.dll System.Data.DataSetExtensions.dll System.Drawing.dll System.Xml.dll thirdparty/download/ICSharpCode.SharpZipLib.dll thirdparty/download/FuzzyLogicLibrary.dll thirdparty/download/Mono.Nat.dll thirdparty/download/MaxMind.Db.dll thirdparty/download/MaxMind.GeoIP2.dll thirdparty/download/Eluant.dll thirdparty/download/SmarIrc4net.dll DEBUG = true ifeq ($(DEBUG), $(filter $(DEBUG),false no n off 0)) diff --git a/OpenRA.Game/Settings.cs b/OpenRA.Game/Settings.cs index 1a0542c396..4c86efe5ca 100644 --- a/OpenRA.Game/Settings.cs +++ b/OpenRA.Game/Settings.cs @@ -288,6 +288,17 @@ namespace OpenRA } } + public class IrcSettings + { + public string[] Hostname = { "irc.openra.net" }; + public int Port = 6667; + public string Channel = "lobby"; + public string Nickname = "Newbie"; + public string QuitMessage = "Battle control terminated!"; + public string TimestampFormat = "HH:mm"; + public bool ConnectAutomatically = false; + } + public class Settings { string settingsFile; @@ -299,6 +310,7 @@ namespace OpenRA public ServerSettings Server = new ServerSettings(); public DebugSettings Debug = new DebugSettings(); public KeySettings Keys = new KeySettings(); + public IrcSettings Irc = new IrcSettings(); public Dictionary Sections; @@ -314,6 +326,7 @@ namespace OpenRA { "Server", Server }, { "Debug", Debug }, { "Keys", Keys }, + { "Irc", Irc } }; // Override fieldloader to ignore invalid entries diff --git a/OpenRA.Game/Support/Log.cs b/OpenRA.Game/Support/Log.cs index f839cf249d..a20143d668 100644 --- a/OpenRA.Game/Support/Log.cs +++ b/OpenRA.Game/Support/Log.cs @@ -61,6 +61,18 @@ namespace OpenRA catch (IOException) { } } + public static void Write(string channel, string value) + { + ChannelInfo info; + if (!Channels.TryGetValue(channel, out info)) + throw new Exception("Tried logging to non-existant channel " + channel); + + if (info.Writer == null) + return; + + info.Writer.WriteLine(value); + } + public static void Write(string channel, string format, params object[] args) { ChannelInfo info; diff --git a/OpenRA.Mods.Common/Irc.cs b/OpenRA.Mods.Common/Irc.cs new file mode 100644 index 0000000000..29279d1b28 --- /dev/null +++ b/OpenRA.Mods.Common/Irc.cs @@ -0,0 +1,19 @@ +#region Copyright & License Information +/* + * Copyright 2007-2015 The OpenRA Developers (see AUTHORS) + * This file is part of OpenRA, which is free software. It is made + * available to you under the terms of the GNU General Public License + * as published by the Free Software Foundation. For more information, + * see COPYING. + */ +#endregion + +using Meebey.SmartIrc4net; + +namespace OpenRA.Mods.Common +{ + public static class Irc + { + public static volatile IrcClient Client; + } +} \ No newline at end of file diff --git a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj index c5a35d08db..0af1d6857c 100644 --- a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj +++ b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj @@ -78,6 +78,9 @@ ..\thirdparty\download\ICSharpCode.SharpZipLib.dll False + + ..\thirdparty\download\SmarIrc4net.dll + @@ -598,6 +601,8 @@ + + diff --git a/OpenRA.Mods.Common/Widgets/Logic/IrcLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/IrcLogic.cs new file mode 100644 index 0000000000..930fb78fb2 --- /dev/null +++ b/OpenRA.Mods.Common/Widgets/Logic/IrcLogic.cs @@ -0,0 +1,374 @@ +#region Copyright & License Information +/* + * Copyright 2007-2015 The OpenRA Developers (see AUTHORS) + * This file is part of OpenRA, which is free software. It is made + * available to you under the terms of the GNU General Public License + * as published by the Free Software Foundation. For more information, + * see COPYING. + */ +#endregion + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using Meebey.SmartIrc4net; +using OpenRA.Widgets; + +namespace OpenRA.Mods.Common.Widgets.Logic +{ + class IrcLogic + { + readonly TextFieldWidget inputBox; + readonly TextFieldWidget nicknameBox; + readonly Widget connectBG; + readonly Widget ircContainer; + + readonly ScrollPanelWidget historyPanel; + readonly LabelWidget historyTemplate; + + readonly ScrollPanelWidget nicknamePanel; + readonly LabelWidget nicknameTemplate; + + bool pingSent; + Channel channel; + + [ObjectCreator.UseCtor] + public IrcLogic(Widget widget) + { + Log.AddChannel("irc", "irc.log"); + + historyPanel = widget.Get("HISTORY_PANEL"); + historyTemplate = widget.Get("HISTORY_TEMPLATE"); + nicknamePanel = widget.Get("NICKNAME_PANEL"); + nicknameTemplate = widget.Get("NICKNAME_TEMPLATE"); + + inputBox = widget.Get("INPUT_BOX"); + inputBox.OnEnterKey = EnterPressed; + inputBox.IsDisabled = () => Irc.Client == null; + + if (Game.Settings.Irc.Nickname == new IrcSettings().Nickname) + Game.Settings.Irc.Nickname += Game.CosmeticRandom.Next(100, 999); + + nicknameBox = widget.Get("NICKNAME_BOX"); + nicknameBox.Text = SanitizedName(Game.Settings.Irc.Nickname); + nicknameBox.OnTextEdited = () => + { + nicknameBox.Text = SanitizedName(nicknameBox.Text); + Game.Settings.Irc.Nickname = nicknameBox.Text; + Game.Settings.Save(); + }; + + connectBG = widget.Get("IRC_CONNECT_BG"); + ircContainer = widget.Get("IRC_CONTAINER"); + + var disconnectButton = widget.Get("DISCONNECT_BUTTON"); + disconnectButton.IsDisabled = () => Irc.Client == null; + disconnectButton.OnClick = Disconnect; + + MaybeShowConnectPanel(); + } + + static string SanitizedName(string dirty) + { + if (string.IsNullOrEmpty(dirty)) + return null; + + // TODO: some special chars are allowed as well, but not at every position + var clean = new string(dirty.Where(c => char.IsLetterOrDigit(c)).ToArray()); + + if (string.IsNullOrEmpty(clean)) + return null; + + if (char.IsDigit(clean[0])) + return SanitizedName(clean.Substring(1)); + + // Source: https://tools.ietf.org/html/rfc2812#section-1.2.1 + if (clean.Length > 9) + clean = clean.Substring(0, 9); + + return clean; + } + + void MaybeShowConnectPanel() + { + if (Irc.Client != null && Irc.Client.IsConnected) + { + ircContainer.Visible = true; + connectBG.Visible = false; + + Initialize(); + + if (Irc.Client.JoinedChannels.Count > 0) + channel = Irc.Client.GetChannel(Irc.Client.JoinedChannels[0]); + + SyncNicknamePanel(); + + return; + } + + if (Game.Settings.Irc.ConnectAutomatically) + { + ircContainer.Visible = true; + connectBG.Visible = false; + Connect(); + return; + } + + ircContainer.Visible = false; + connectBG.Visible = true; + + var connectAutomaticallyCheckBox = connectBG.Get("CONNECT_AUTOMATICALLY_CHECKBOX"); + connectAutomaticallyCheckBox.IsChecked = () => Game.Settings.Irc.ConnectAutomatically; + connectAutomaticallyCheckBox.OnClick = () => Game.Settings.Irc.ConnectAutomatically ^= true; + + var connectButton = connectBG.Get("CONNECT_BUTTON"); + connectButton.IsDisabled = () => string.IsNullOrEmpty(nicknameBox.Text); + connectButton.OnClick = () => + { + ircContainer.Visible = true; + connectBG.Visible = false; + + Game.Settings.Irc.ConnectAutomatically = connectAutomaticallyCheckBox.IsChecked(); + Game.Settings.Save(); + Connect(); + }; + } + + void Initialize() + { + Irc.Client.OnConnected += OnConnected; + Irc.Client.OnError += OnError; + Irc.Client.OnRawMessage += OnRawMessage; + Irc.Client.OnJoin += OnJoin; + Irc.Client.OnChannelActiveSynced += OnChannelActiveSynced; + Irc.Client.OnNickChange += OnNickChange; + Irc.Client.OnPart += OnPart; + Irc.Client.OnQuit += OnQuit; + Irc.Client.OnChannelMessage += OnChannelMessage; + Irc.Client.OnPong += OnPong; + } + + void Connect() + { + Irc.Client = new IrcClient(); + Irc.Client.Encoding = System.Text.Encoding.UTF8; + Irc.Client.SendDelay = 100; + Irc.Client.ActiveChannelSyncing = true; + + Initialize(); + + Game.OnQuit += Disconnect; + + try + { + AddChatLine("Connecting to {0}...".F(Game.Settings.Irc.Hostname)); + Irc.Client.Connect(Game.Settings.Irc.Hostname, Game.Settings.Irc.Port); + } + catch (Exception e) + { + AddChatLine("Connection error: {0}".F(e.Message)); + Game.RunAfterTick(() => + { + Log.Write("irc", e.ToString()); + }); + } + + new Thread(Irc.Client.Listen) { Name = "IrcListenThread" }.Start(); + } + + void OnPong(object sender, PongEventArgs e) + { + if (pingSent) + { + AddChatLine("PONG recieved after {0} ms.".F(e.Lag.Milliseconds)); + pingSent = false; + } + else + { + Game.RunAfterTick(() => + { + Log.Write("irc", "PONG sent after {0} ms.".F(e.Lag.Milliseconds)); + }); + } + } + + Widget MakeLabelWidget(LabelWidget template, string item) + { + var widget = (LabelWidget)template.Clone(); + var font = Game.Renderer.Fonts[widget.Font]; + item = WidgetUtils.WrapText(item, widget.Bounds.Width, font); + widget.Bounds.Height = font.Measure(item).Y; + widget.GetText = () => item; + widget.Id = item; + return widget; + } + + void AddChatLine(string text) + { + Game.RunAfterTick(() => + { + Log.Write("irc", text); + + var scrolledToBottom = historyPanel.ScrolledToBottom; + + var newChild = MakeLabelWidget(historyTemplate, text); + historyPanel.AddChild(newChild); + + if (scrolledToBottom) + historyPanel.ScrollToBottom(smooth: true); + }); + } + + bool EnterPressed() + { + if (inputBox.Text.Length == 0) + return true; + + var text = inputBox.Text; + inputBox.Text = ""; + + if (text.StartsWith("/nick ")) + { + var nick = text.Replace("/nick ", string.Empty); + if (Rfc2812.IsValidNickname(nick)) + Irc.Client.RfcNick(nick); + else + AddChatLine("Invalid nickname."); + } + else if (text.StartsWith("/ping ")) + { + Irc.Client.RfcPing(Irc.Client.GetIrcUser(text.Replace("/ping ", string.Empty)).Host); + pingSent = true; + } + else if (text.StartsWith("/")) + AddChatLine("Unknown command."); + else + { + AddChatLine("[{0}] <{1}> {2}".F(DateTime.Now.ToString(Game.Settings.Irc.TimestampFormat), Irc.Client.Nickname, text)); + Irc.Client.SendMessage(SendType.Message, "#" + Game.Settings.Irc.Channel, text); + } + + return true; + } + + void OnConnected(object sender, EventArgs e) + { + AddChatLine("Connected."); + + if (!Rfc2812.IsValidNickname(Game.Settings.Irc.Nickname)) + { + AddChatLine("Invalid nickname. Can't login."); + return; + } + + Irc.Client.Login(new[] { Game.Settings.Irc.Nickname }, "in-game IRC client", 0, "OpenRA"); + + Irc.Client.RfcJoin("#" + Game.Settings.Irc.Channel); + } + + void OnError(object sender, ErrorEventArgs e) + { + AddChatLine("Error: " + e.ErrorMessage); + Game.RunAfterTick(() => + { + Log.Write("irc", e.ToString()); + }); + } + + void OnRawMessage(object sender, IrcEventArgs e) + { + Game.RunAfterTick(() => + { + Log.Write("irc", e.Data.RawMessage); + }); + } + + void OnChannelMessage(object sender, IrcEventArgs e) + { + AddChatLine("[{0}] <{1}> {2}".F(DateTime.Now.ToString(Game.Settings.Irc.TimestampFormat), e.Data.Nick, e.Data.Message)); + } + + void OnJoin(object sender, JoinEventArgs e) + { + if (e.Who == Irc.Client.Nickname) + return; + + AddChatLine("{0} joined channel {1}.".F(e.Who, e.Channel)); + channel = Irc.Client.GetChannel(e.Channel); + SyncNicknamePanel(); + } + + void OnChannelActiveSynced(object sender, IrcEventArgs e) + { + channel = Irc.Client.GetChannel(e.Data.Channel); + + AddChatLine("{0} users online".F(channel.Users.Count)); + + if (!string.IsNullOrEmpty(channel.Topic)) + AddChatLine("*** Topic: {0}".F(channel.Topic)); + + SyncNicknamePanel(); + } + + void OnNickChange(object sender, NickChangeEventArgs e) + { + AddChatLine("{0} is now known as {1}.".F(e.OldNickname, e.NewNickname)); + SyncNicknamePanel(); + } + + void SyncNicknamePanel() + { + if (channel == null) + return; + + var users = channel.Users; + Game.RunAfterTick(() => + { + nicknamePanel.RemoveChildren(); + + foreach (DictionaryEntry user in users) + { + var channeluser = (ChannelUser)user.Value; + var prefix = channeluser.IsOp ? "@" : channeluser.IsVoice ? "+" : ""; + var newChild = MakeLabelWidget(nicknameTemplate, prefix + channeluser.Nick); + nicknamePanel.AddChild(newChild); + } + }); + } + + void OnQuit(object sender, QuitEventArgs e) + { + AddChatLine("{0} quit.".F(e.Who)); + } + + void OnPart(object sender, PartEventArgs e) + { + AddChatLine("{0} left {1}.".F(e.Who, e.Data.Channel)); + channel = Irc.Client.GetChannel(e.Data.Channel); + SyncNicknamePanel(); + } + + void Disconnect() + { + if (Irc.Client == null) + return; + + Irc.Client.RfcQuit(Game.Settings.Irc.QuitMessage); + + AddChatLine("Disconnecting from {0}...".F(Irc.Client.Address)); + + if (Irc.Client.IsConnected) + Irc.Client.Disconnect(); + + nicknamePanel.RemoveChildren(); + + Game.Settings.Irc.ConnectAutomatically = false; + + Irc.Client = null; + + MaybeShowConnectPanel(); + } + } +} diff --git a/OpenRA.Mods.Common/Widgets/Logic/ServerBrowserLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/ServerBrowserLogic.cs index 72ee81d390..235d82b306 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/ServerBrowserLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/ServerBrowserLogic.cs @@ -135,6 +135,15 @@ namespace OpenRA.Mods.Common.Widgets.Logic showIncompatibleCheckbox.OnClick = () => { showIncompatible ^= true; RefreshServerList(); }; } + try + { + Game.LoadWidget(null, "SERVERBROWSER_IRC", panel.Get("IRC_ROOT"), new WidgetArgs()); + } + catch + { + Log.Write("debug", "Failed to load server browser IRC chrome layout"); + } + RefreshServerList(); if (directConnectHost != null) diff --git a/mods/cnc/chrome/irc.yaml b/mods/cnc/chrome/irc.yaml new file mode 100644 index 0000000000..0165e25cf3 --- /dev/null +++ b/mods/cnc/chrome/irc.yaml @@ -0,0 +1,70 @@ +Container@SERVERBROWSER_IRC: + Logic: IrcLogic + Width: 700 + Height: 250 + Children: + Container@IRC_CONTAINER: + Width: PARENT_RIGHT + Height: PARENT_BOTTOM + Children: + ScrollPanel@HISTORY_PANEL: + Width: 565 + Height: PARENT_BOTTOM - 30 + ItemSpacing: 5 + Label@HISTORY_TEMPLATE: + X: 5 + Width: 530 + Height: 25 + WordWrap: True + TextField@INPUT_BOX: + Y: PARENT_BOTTOM - 25 + Width: 565 + Height: 25 + ScrollPanel@NICKNAME_PANEL: + X: 570 + Width: 130 + Height: PARENT_BOTTOM - 30 + Label@NICKNAME_TEMPLATE: + X: 5 + Button@DISCONNECT_BUTTON: + X: 570 + Y: PARENT_BOTTOM - 25 + Width: 130 + Height: 25 + Text: Disconnect + Font: Bold + Background@IRC_CONNECT_BG: + Width: PARENT_RIGHT + Height: PARENT_BOTTOM + Background: scrollpanel-bg + Children: + Label@GLOBAL_CHAT_LABEL: + Y: PARENT_BOTTOM / 4 + Width: PARENT_RIGHT + Align: Center + Text: Global Chat + Font: Bold + Label@NICKNAME_LABEL: + X: 200 + Y: PARENT_BOTTOM / 4 + 35 + Text: Nickname: + TextField@NICKNAME_BOX: + X: 270 + Y: PARENT_BOTTOM / 4 + 25 + Width: 150 + Height: 25 + Checkbox@CONNECT_AUTOMATICALLY_CHECKBOX: + X: 270 + Y: PARENT_BOTTOM / 4 + 75 + Height: 20 + Width: 180 + Font: Regular + Text: Connect Automatically + Button@CONNECT_BUTTON: + X: 430 + Y: PARENT_BOTTOM / 4 + 25 + Width: 100 + Height: 25 + Text: Connect + Font: Bold + diff --git a/mods/cnc/chrome/serverbrowser.yaml b/mods/cnc/chrome/serverbrowser.yaml index 2e93f9f0f2..d3bc3cdab1 100644 --- a/mods/cnc/chrome/serverbrowser.yaml +++ b/mods/cnc/chrome/serverbrowser.yaml @@ -1,74 +1,31 @@ Container@SERVERBROWSER_PANEL: Logic: ServerBrowserLogic X: (WINDOW_RIGHT - WIDTH)/2 - Y: (WINDOW_BOTTOM - 500)/2 + Y: (WINDOW_BOTTOM - HEIGHT)/2 Width: 730 - Height: 535 + Height: 595 Children: Label@TITLE: Text: Multiplayer Width: 740 - Y: 0-25 + Y: 0-10 Font: BigBold Contrast: true Align: Center Background@bg: Width: 730 - Height: 500 + Height: PARENT_BOTTOM - 30 Background: panel-black + Y: 15 Children: - Label@SHOW_LABEL_TITLE: - X: 20 - Y: 465 - Width: 20 - Height: 25 - Text: Show: - Font: Bold - Checkbox@WAITING_FOR_PLAYERS: - X: 80 - Y: 467 - Width: 100 - Height: 20 - Text: Waiting - TextColor: 50,205,50 - Checkbox@EMPTY: - X: 180 - Y: 467 - Width: 100 - Height: 20 - Text: Empty - Checkbox@PASSWORD_PROTECTED: - X: 270 - Y: 467 - Width: 100 - Height: 20 - Text: Protected - TextColor: 255,0,0 - Checkbox@ALREADY_STARTED: - X: 385 - Y: 467 - Width: 100 - Height: 20 - Text: Started - TextColor: 255,165,0 - Checkbox@INCOMPATIBLE_VERSION: - X: 480 - Y: 467 - Width: 100 - Height: 20 - Text: Incompatible - TextColor: 190,190,190 - Button@REFRESH_BUTTON: - X: PARENT_RIGHT - WIDTH - 15 - Y: 465 - Width: 100 - Height: 25 - Text: Refresh - ScrollPanel@SERVER_LIST: + Container@IRC_ROOT: X: 15 Y: 15 + ScrollPanel@SERVER_LIST: + X: 15 + Y: 280 Width: 700 - Height: 440 + Height: 240 Children: ScrollItem@HEADER_TEMPLATE: Width: PARENT_RIGHT-27 @@ -129,35 +86,82 @@ Container@SERVERBROWSER_PANEL: Height: 25 Label@PROGRESS_LABEL: X: (PARENT_RIGHT - WIDTH) / 2 - Y: PARENT_BOTTOM / 2 - HEIGHT + Y: PARENT_BOTTOM / 2 - HEIGHT + (280 / 2) Width: 710 Height: 25 Font: Bold Align: Center Visible: false + Label@SHOW_LABEL_TITLE: + X: 20 + Y: 525 + Width: 20 + Height: 25 + Text: Show: + Font: Bold + Checkbox@WAITING_FOR_PLAYERS: + X: 80 + Y: 527 + Width: 100 + Height: 20 + Text: Waiting + TextColor: 50,205,50 + Checkbox@EMPTY: + X: 180 + Y: 527 + Width: 100 + Height: 20 + Text: Empty + Checkbox@PASSWORD_PROTECTED: + X: 270 + Y: 527 + Width: 100 + Height: 20 + Text: Protected + TextColor: 255,0,0 + Checkbox@ALREADY_STARTED: + X: 385 + Y: 527 + Width: 100 + Height: 20 + Text: Started + TextColor: 255,165,0 + Checkbox@INCOMPATIBLE_VERSION: + X: 480 + Y: 527 + Width: 100 + Height: 20 + Text: Incompatible + TextColor: 190,190,190 + Button@REFRESH_BUTTON: + X: PARENT_RIGHT - WIDTH - 15 + Y: 525 + Width: 100 + Height: 25 + Text: Refresh Button@BACK_BUTTON: Key: escape X: 0 - Y: 499 + Y: PARENT_BOTTOM - 16 Width: 140 Height: 35 Text: Back Button@CREATE_BUTTON: X: PARENT_RIGHT - 140 - 10 - 140 - 10 - 140 - Y: 499 + Y: PARENT_BOTTOM - 16 Width: 140 Height: 35 Text: Create Button@DIRECTCONNECT_BUTTON: X: PARENT_RIGHT - 140 - 10 - 140 - Y: 499 + Y: PARENT_BOTTOM - 16 Width: 140 Height: 35 Text: Direct IP Button@JOIN_BUTTON: Key: return X: PARENT_RIGHT - 140 - Y: 499 + Y: PARENT_BOTTOM - 16 Width: 140 Height: 35 Text: Join diff --git a/mods/cnc/mod.yaml b/mods/cnc/mod.yaml index 4a7f7e5a0b..a19b2c892a 100644 --- a/mods/cnc/mod.yaml +++ b/mods/cnc/mod.yaml @@ -120,6 +120,7 @@ ChromeLayout: ./mods/cnc/chrome/assetbrowser.yaml ./mods/cnc/chrome/missionbrowser.yaml ./mods/cnc/chrome/editor.yaml + ./mods/cnc/chrome/irc.yaml Voices: ./mods/cnc/audio/voices.yaml diff --git a/mods/d2k/mod.yaml b/mods/d2k/mod.yaml index b638e3b2a5..729e9a911b 100644 --- a/mods/d2k/mod.yaml +++ b/mods/d2k/mod.yaml @@ -102,6 +102,7 @@ ChromeLayout: ./mods/d2k/chrome/missionbrowser.yaml ./mods/ra/chrome/confirmation-dialogs.yaml ./mods/ra/chrome/editor.yaml + ./mods/ra/chrome/irc.yaml Weapons: ./mods/d2k/weapons.yaml diff --git a/mods/ra/chrome/irc.yaml b/mods/ra/chrome/irc.yaml new file mode 100644 index 0000000000..7926b90704 --- /dev/null +++ b/mods/ra/chrome/irc.yaml @@ -0,0 +1,69 @@ +Container@SERVERBROWSER_IRC: + Logic: IrcLogic + Width: 700 + Height: 250 + Children: + Container@IRC_CONTAINER: + Width: PARENT_RIGHT + Height: PARENT_BOTTOM + Children: + ScrollPanel@HISTORY_PANEL: + Width: 565 + Height: PARENT_BOTTOM - 30 + ItemSpacing: 5 + Label@HISTORY_TEMPLATE: + X: 5 + Width: 530 + Height: 25 + WordWrap: True + TextField@INPUT_BOX: + Y: PARENT_BOTTOM - 25 + Width: 565 + Height: 25 + ScrollPanel@NICKNAME_PANEL: + X: 570 + Width: 130 + Height: PARENT_BOTTOM - 30 + Label@NICKNAME_TEMPLATE: + X: 5 + Button@DISCONNECT_BUTTON: + X: 570 + Y: PARENT_BOTTOM - 25 + Width: 130 + Height: 25 + Text: Disconnect + Font: Bold + Background@IRC_CONNECT_BG: + Width: PARENT_RIGHT + Height: PARENT_BOTTOM + Background: scrollpanel-bg + Children: + Label@GLOBAL_CHAT_LABEL: + Y: PARENT_BOTTOM / 4 + Width: PARENT_RIGHT + Align: Center + Text: Global Chat + Font: Bold + Label@NICKNAME_LABEL: + X: 200 + Y: PARENT_BOTTOM / 4 + 35 + Text: Nickname: + TextField@NICKNAME_BOX: + X: 270 + Y: PARENT_BOTTOM / 4 + 25 + Width: 150 + Height: 25 + Checkbox@CONNECT_AUTOMATICALLY_CHECKBOX: + X: 270 + Y: PARENT_BOTTOM / 4 + 75 + Height: 20 + Width: 180 + Text: Connect Automatically + Button@CONNECT_BUTTON: + X: 430 + Y: PARENT_BOTTOM / 4 + 25 + Width: 100 + Height: 25 + Text: Connect + Font: Bold + diff --git a/mods/ra/chrome/serverbrowser.yaml b/mods/ra/chrome/serverbrowser.yaml index cdcae58345..b748e65511 100644 --- a/mods/ra/chrome/serverbrowser.yaml +++ b/mods/ra/chrome/serverbrowser.yaml @@ -3,7 +3,7 @@ Background@SERVERBROWSER_PANEL: X: (WINDOW_RIGHT - WIDTH)/2 Y: (WINDOW_BOTTOM - HEIGHT)/2 Width: 740 - Height: 500 + Height: 645 Children: Label@MULTIPLAYER_LABEL_TITLE: X: 0 @@ -58,7 +58,7 @@ Background@SERVERBROWSER_PANEL: X: 20 Y: 80 Width: 700 - Height: 360 + Height: 240 Children: ScrollItem@HEADER_TEMPLATE: BaseName: scrollheader @@ -118,6 +118,10 @@ Background@SERVERBROWSER_PANEL: Y: 40 Align: Right Height: 25 + Container@IRC_ROOT: + X: 20 + Y: 370 + Width: 260 Label@PROGRESS_LABEL: X: (PARENT_RIGHT - WIDTH) / 2 Y: PARENT_BOTTOM / 2 - HEIGHT @@ -127,28 +131,28 @@ Background@SERVERBROWSER_PANEL: Align: Center Button@REFRESH_BUTTON: X: 20 - Y: PARENT_BOTTOM - 45 + Y: 325 Width: 100 Height: 25 Text: Refresh Font: Bold Button@CREATE_BUTTON: X: PARENT_RIGHT - 120 - 120 - 120 - 120 - Y: PARENT_BOTTOM - 45 + Y: 325 Width: 100 Height: 25 Text: Create Font: Bold Button@DIRECTCONNECT_BUTTON: X: PARENT_RIGHT - 120 - 120 - 120 - Y: PARENT_BOTTOM - 45 + Y: 325 Width: 100 Height: 25 Text: Direct IP Font: Bold Button@JOIN_BUTTON: X: PARENT_RIGHT - 120 - 120 - Y: PARENT_BOTTOM - 45 + Y: 325 Width: 100 Height: 25 Text: Join @@ -156,7 +160,7 @@ Background@SERVERBROWSER_PANEL: Key: return Button@BACK_BUTTON: X: PARENT_RIGHT - 120 - Y: PARENT_BOTTOM - 45 + Y: 325 Width: 100 Height: 25 Text: Cancel diff --git a/mods/ra/mod.yaml b/mods/ra/mod.yaml index 2cded39907..0c387cb8cc 100644 --- a/mods/ra/mod.yaml +++ b/mods/ra/mod.yaml @@ -114,6 +114,7 @@ ChromeLayout: ./mods/ra/chrome/missionbrowser.yaml ./mods/ra/chrome/confirmation-dialogs.yaml ./mods/ra/chrome/editor.yaml + ./mods/ra/chrome/irc.yaml Weapons: ./mods/ra/weapons/explosions.yaml diff --git a/mods/ts/mod.yaml b/mods/ts/mod.yaml index 842f0719c5..352ad2e585 100644 --- a/mods/ts/mod.yaml +++ b/mods/ts/mod.yaml @@ -168,6 +168,7 @@ ChromeLayout: ./mods/ra/chrome/missionbrowser.yaml ./mods/ra/chrome/confirmation-dialogs.yaml ./mods/ra/chrome/editor.yaml + ./mods/ra/chrome/irc.yaml Voices: ./mods/ts/audio/voices.yaml diff --git a/packaging/package-all.sh b/packaging/package-all.sh index a331836d76..50201944f3 100755 --- a/packaging/package-all.sh +++ b/packaging/package-all.sh @@ -67,6 +67,9 @@ cp thirdparty/download/MaxMind.GeoIP2.dll packaging/built cp thirdparty/download/Newtonsoft.Json.dll packaging/built cp thirdparty/download/RestSharp.dll packaging/built +# global chat +cp thirdparty/download/SmarIrc4net.dll packaging/built + # Copy game icon for windows package cp OpenRA.Game/OpenRA.ico packaging/built diff --git a/packaging/windows/OpenRA.nsi b/packaging/windows/OpenRA.nsi index 93c97928fa..6b2732f5a6 100644 --- a/packaging/windows/OpenRA.nsi +++ b/packaging/windows/OpenRA.nsi @@ -104,6 +104,7 @@ Section "Game" GAME File "${SRCDIR}\RestSharp.dll" File "${SRCDIR}\GeoLite2-Country.mmdb.gz" File "${SRCDIR}\eluant.dll" + File "${SRCDIR}\SmarIrc4net.dll" File "${DEPSDIR}\soft_oal.dll" File "${DEPSDIR}\SDL2.dll" File "${DEPSDIR}\freetype6.dll" @@ -214,6 +215,7 @@ Function ${UN}Clean Delete $INSTDIR\eluant.dll Delete $INSTDIR\freetype6.dll Delete $INSTDIR\SDL2-CS.dll + Delete $INSTDIR\SmarIrc4net.dll RMDir /r $INSTDIR\Support DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\OpenRA" diff --git a/thirdparty/fetch-thirdparty-deps.ps1 b/thirdparty/fetch-thirdparty-deps.ps1 index fe985a92ce..393cc9a1a6 100644 --- a/thirdparty/fetch-thirdparty-deps.ps1 +++ b/thirdparty/fetch-thirdparty-deps.ps1 @@ -137,4 +137,12 @@ if (!(Test-Path "GeoLite2-Country.mmdb.gz") -Or (((get-date) - (get-item "GeoLit (New-Object System.Net.WebClient).DownloadFile("http://geolite.maxmind.com/download/geoip/database/GeoLite2-Country.mmdb.gz", $target) } +if (!(Test-Path "SmarIrc4net.dll")) +{ + echo "Fetching SmartIrc4net from NuGet." + ./nuget.exe install SmartIrc4net -Version 0.4.5.1 -ExcludeVersion + cp SmartIrc4net/lib/net40/SmarIrc4net.* . + rmdir SmartIrc4net -Recurse +} + cd .. diff --git a/thirdparty/fetch-thirdparty-deps.sh b/thirdparty/fetch-thirdparty-deps.sh index 3544509a15..bc7f14bc09 100755 --- a/thirdparty/fetch-thirdparty-deps.sh +++ b/thirdparty/fetch-thirdparty-deps.sh @@ -101,3 +101,10 @@ if [ ! -f Eluant.dll ]; then echo "Fetching Eluant from GitHub." curl -s -L -O https://github.com/OpenRA/Eluant/releases/download/20140425/Eluant.dll fi + +if [ ! -f SmarIrc4net.dll ]; then + echo "Fetching SmartIrc4net from NuGet." + get SmartIrc4net 0.4.5.1 + cp ./SmartIrc4net/lib/net40/SmarIrc4net* . + rm -rf SmartIrc4net +fi \ No newline at end of file