1 #region Copyright & License Information
2 /*
3  * Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
4  * This file is part of OpenRA, which is free software. It is made
5  * available to you under the terms of the GNU General Public License
6  * as published by the Free Software Foundation, either version 3 of
7  * the License, or (at your option) any later version. For more
8  * information, see COPYING.
9  */
10 #endregion
11 
12 using System;
13 using System.Linq;
14 using System.Net;
15 using OpenRA.Network;
16 using OpenRA.Primitives;
17 using OpenRA.Widgets;
18 
19 namespace OpenRA.Mods.Common.Widgets.Logic
20 {
21 	public class ServerCreationLogic : ChromeLogic
22 	{
23 		readonly Widget panel;
24 		readonly LabelWidget noticesLabelA, noticesLabelB, noticesLabelC;
25 		readonly Action onCreate;
26 		readonly Action onExit;
27 		MapPreview preview = MapCache.UnknownMap;
28 		bool advertiseOnline;
29 
30 		[ObjectCreator.UseCtor]
ServerCreationLogic(Widget widget, ModData modData, Action onExit, Action openLobby)31 		public ServerCreationLogic(Widget widget, ModData modData, Action onExit, Action openLobby)
32 		{
33 			panel = widget;
34 			onCreate = openLobby;
35 			this.onExit = onExit;
36 
37 			var settings = Game.Settings;
38 			preview = modData.MapCache[modData.MapCache.ChooseInitialMap(Game.Settings.Server.Map, Game.CosmeticRandom)];
39 
40 			panel.Get<ButtonWidget>("BACK_BUTTON").OnClick = () => { Ui.CloseWindow(); onExit(); };
41 			panel.Get<ButtonWidget>("CREATE_BUTTON").OnClick = CreateAndJoin;
42 
43 			var mapButton = panel.GetOrNull<ButtonWidget>("MAP_BUTTON");
44 			if (mapButton != null)
45 			{
46 				panel.Get<ButtonWidget>("MAP_BUTTON").OnClick = () =>
47 				{
48 					Ui.OpenWindow("MAPCHOOSER_PANEL", new WidgetArgs()
49 					{
50 						{ "initialMap", preview.Uid },
51 						{ "initialTab", MapClassification.System },
52 						{ "onExit", () => { } },
53 						{ "onSelect", (Action<string>)(uid => preview = modData.MapCache[uid]) },
54 						{ "filter", MapVisibility.Lobby },
55 						{ "onStart", () => { } }
56 					});
57 				};
58 
59 				panel.Get<MapPreviewWidget>("MAP_PREVIEW").Preview = () => preview;
60 
61 				var titleLabel = panel.GetOrNull<LabelWithTooltipWidget>("MAP_TITLE");
62 				if (titleLabel != null)
63 				{
64 					var font = Game.Renderer.Fonts[titleLabel.Font];
65 					var title = new CachedTransform<MapPreview, string>(m => WidgetUtils.TruncateText(m.Title, titleLabel.Bounds.Width, font));
66 					titleLabel.GetText = () => title.Update(preview);
67 					titleLabel.GetTooltipText = () => preview.Title;
68 				}
69 
70 				var typeLabel = panel.GetOrNull<LabelWidget>("MAP_TYPE");
71 				if (typeLabel != null)
72 				{
73 					var type = new CachedTransform<MapPreview, string>(m => m.Categories.FirstOrDefault() ?? "");
74 					typeLabel.GetText = () => type.Update(preview);
75 				}
76 
77 				var authorLabel = panel.GetOrNull<LabelWidget>("MAP_AUTHOR");
78 				if (authorLabel != null)
79 				{
80 					var font = Game.Renderer.Fonts[authorLabel.Font];
81 					var author = new CachedTransform<MapPreview, string>(
82 						m => WidgetUtils.TruncateText("Created by {0}".F(m.Author), authorLabel.Bounds.Width, font));
83 					authorLabel.GetText = () => author.Update(preview);
84 				}
85 			}
86 
87 			var serverName = panel.Get<TextFieldWidget>("SERVER_NAME");
88 			serverName.Text = Settings.SanitizedServerName(settings.Server.Name);
89 			serverName.OnEnterKey = () => { serverName.YieldKeyboardFocus(); return true; };
90 			serverName.OnLoseFocus = () =>
91 			{
92 				serverName.Text = Settings.SanitizedServerName(serverName.Text);
93 				settings.Server.Name = serverName.Text;
94 			};
95 
96 			panel.Get<TextFieldWidget>("LISTEN_PORT").Text = settings.Server.ListenPort.ToString();
97 
98 			advertiseOnline = Game.Settings.Server.AdvertiseOnline;
99 
100 			var advertiseCheckbox = panel.Get<CheckboxWidget>("ADVERTISE_CHECKBOX");
101 			advertiseCheckbox.IsChecked = () => advertiseOnline;
102 			advertiseCheckbox.OnClick = () =>
103 			{
104 				advertiseOnline ^= true;
105 				BuildNotices();
106 			};
107 
108 			var passwordField = panel.GetOrNull<PasswordFieldWidget>("PASSWORD");
109 			if (passwordField != null)
110 				passwordField.Text = Game.Settings.Server.Password;
111 
112 			noticesLabelA = panel.GetOrNull<LabelWidget>("NOTICES_HEADER_A");
113 			noticesLabelB = panel.GetOrNull<LabelWidget>("NOTICES_HEADER_B");
114 			noticesLabelC = panel.GetOrNull<LabelWidget>("NOTICES_HEADER_C");
115 
116 			var noticesNoUPnP = panel.GetOrNull("NOTICES_NO_UPNP");
117 			if (noticesNoUPnP != null)
118 			{
119 				noticesNoUPnP.IsVisible = () => advertiseOnline &&
120 					(UPnP.Status == UPnPStatus.NotSupported || UPnP.Status == UPnPStatus.Disabled);
121 
122 				var settingsA = noticesNoUPnP.GetOrNull("SETTINGS_A");
123 				if (settingsA != null)
124 					settingsA.IsVisible = () => UPnP.Status == UPnPStatus.Disabled;
125 
126 				var settingsB = noticesNoUPnP.GetOrNull("SETTINGS_B");
127 				if (settingsB != null)
128 					settingsB.IsVisible = () => UPnP.Status == UPnPStatus.Disabled;
129 			}
130 
131 			var noticesUPnP = panel.GetOrNull("NOTICES_UPNP");
132 			if (noticesUPnP != null)
133 				noticesUPnP.IsVisible = () => advertiseOnline && UPnP.Status == UPnPStatus.Enabled;
134 
135 			var noticesLAN = panel.GetOrNull("NOTICES_LAN");
136 			if (noticesLAN != null)
137 				noticesLAN.IsVisible = () => !advertiseOnline;
138 
139 			BuildNotices();
140 		}
141 
BuildNotices()142 		void BuildNotices()
143 		{
144 			if (noticesLabelA == null || noticesLabelB == null || noticesLabelC == null)
145 				return;
146 
147 			if (advertiseOnline)
148 			{
149 				noticesLabelA.Text = "Internet Server (UPnP ";
150 				var aWidth = Game.Renderer.Fonts[noticesLabelA.Font].Measure(noticesLabelA.Text).X;
151 				noticesLabelA.Bounds.Width = aWidth;
152 
153 				var status = UPnP.Status;
154 				noticesLabelB.Text = status == UPnPStatus.Enabled ? "Enabled" :
155 					status == UPnPStatus.NotSupported ? "Not Supported" : "Disabled";
156 
157 				noticesLabelB.TextColor = status == UPnPStatus.Enabled ? ChromeMetrics.Get<Color>("NoticeSuccessColor") :
158 					status == UPnPStatus.NotSupported ? ChromeMetrics.Get<Color>("NoticeErrorColor") :
159 					ChromeMetrics.Get<Color>("NoticeInfoColor");
160 
161 				var bWidth = Game.Renderer.Fonts[noticesLabelB.Font].Measure(noticesLabelB.Text).X;
162 				noticesLabelB.Bounds.X = noticesLabelA.Bounds.Right;
163 				noticesLabelB.Bounds.Width = bWidth;
164 				noticesLabelB.Visible = true;
165 
166 				noticesLabelC.Text = "):";
167 				noticesLabelC.Bounds.X = noticesLabelB.Bounds.Right;
168 				noticesLabelC.Visible = true;
169 			}
170 			else
171 			{
172 				noticesLabelA.Text = "Local Server:";
173 				noticesLabelB.Visible = false;
174 				noticesLabelC.Visible = false;
175 			}
176 		}
177 
CreateAndJoin()178 		void CreateAndJoin()
179 		{
180 			var name = Settings.SanitizedServerName(panel.Get<TextFieldWidget>("SERVER_NAME").Text);
181 			int listenPort;
182 			if (!Exts.TryParseIntegerInvariant(panel.Get<TextFieldWidget>("LISTEN_PORT").Text, out listenPort))
183 				listenPort = 1234;
184 
185 			var passwordField = panel.GetOrNull<PasswordFieldWidget>("PASSWORD");
186 			var password = passwordField != null ? passwordField.Text : "";
187 
188 			// Save new settings
189 			Game.Settings.Server.Name = name;
190 			Game.Settings.Server.ListenPort = listenPort;
191 			Game.Settings.Server.AdvertiseOnline = advertiseOnline;
192 			Game.Settings.Server.Map = preview.Uid;
193 			Game.Settings.Server.Password = password;
194 			Game.Settings.Save();
195 
196 			// Take a copy so that subsequent changes don't affect the server
197 			var settings = Game.Settings.Server.Clone();
198 
199 			// Create and join the server
200 			try
201 			{
202 				Game.CreateServer(settings);
203 			}
204 			catch (System.Net.Sockets.SocketException e)
205 			{
206 				var message = "Could not listen on port {0}.".F(Game.Settings.Server.ListenPort);
207 
208 				// AddressAlreadyInUse (WSAEADDRINUSE)
209 				if (e.ErrorCode == 10048)
210 					message += "\nCheck if the port is already being used.";
211 				else
212 					message += "\nError is: \"{0}\" ({1})".F(e.Message, e.ErrorCode);
213 
214 				ConfirmationDialogs.ButtonPrompt("Server Creation Failed", message, onCancel: () => { }, cancelText: "Back");
215 				return;
216 			}
217 
218 			Ui.CloseWindow();
219 			ConnectionLogic.Connect(IPAddress.Loopback.ToString(), Game.Settings.Server.ListenPort, password, onCreate, onExit);
220 		}
221 	}
222 }
223