move connection UI to commons
This commit is contained in:
145
OpenRA.Mods.Common/Widgets/Logic/ConnectionLogic.cs
Normal file
145
OpenRA.Mods.Common/Widgets/Logic/ConnectionLogic.cs
Normal file
@@ -0,0 +1,145 @@
|
||||
#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 OpenRA.Network;
|
||||
using OpenRA.Widgets;
|
||||
|
||||
namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
{
|
||||
public class ConnectionLogic
|
||||
{
|
||||
Action onConnect, onAbort;
|
||||
Action<string> onRetry;
|
||||
|
||||
void ConnectionStateChanged(OrderManager om)
|
||||
{
|
||||
if (om.Connection.ConnectionState == ConnectionState.Connected)
|
||||
{
|
||||
CloseWindow();
|
||||
onConnect();
|
||||
}
|
||||
else if (om.Connection.ConnectionState == ConnectionState.NotConnected)
|
||||
{
|
||||
CloseWindow();
|
||||
Ui.OpenWindow("CONNECTIONFAILED_PANEL", new WidgetArgs()
|
||||
{
|
||||
{ "orderManager", om },
|
||||
{ "onAbort", onAbort },
|
||||
{ "onRetry", onRetry }
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void CloseWindow()
|
||||
{
|
||||
Game.ConnectionStateChanged -= ConnectionStateChanged;
|
||||
Ui.CloseWindow();
|
||||
}
|
||||
|
||||
[ObjectCreator.UseCtor]
|
||||
public ConnectionLogic(Widget widget, string host, int port, Action onConnect, Action onAbort, Action<string> onRetry)
|
||||
{
|
||||
this.onConnect = onConnect;
|
||||
this.onAbort = onAbort;
|
||||
this.onRetry = onRetry;
|
||||
|
||||
Game.ConnectionStateChanged += ConnectionStateChanged;
|
||||
|
||||
var panel = widget;
|
||||
panel.Get<ButtonWidget>("ABORT_BUTTON").OnClick = () => { CloseWindow(); onAbort(); };
|
||||
|
||||
widget.Get<LabelWidget>("CONNECTING_DESC").GetText = () =>
|
||||
"Connecting to {0}:{1}...".F(host, port);
|
||||
}
|
||||
|
||||
public static void Connect(string host, int port, string password, Action onConnect, Action onAbort)
|
||||
{
|
||||
Game.JoinServer(host, port, password);
|
||||
Action<string> onRetry = newPassword => Connect(host, port, newPassword, onConnect, onAbort);
|
||||
|
||||
Ui.OpenWindow("CONNECTING_PANEL", new WidgetArgs()
|
||||
{
|
||||
{ "host", host },
|
||||
{ "port", port },
|
||||
{ "onConnect", onConnect },
|
||||
{ "onAbort", onAbort },
|
||||
{ "onRetry", onRetry }
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public class ConnectionFailedLogic
|
||||
{
|
||||
PasswordFieldWidget passwordField;
|
||||
bool passwordOffsetAdjusted;
|
||||
|
||||
[ObjectCreator.UseCtor]
|
||||
public ConnectionFailedLogic(Widget widget, OrderManager orderManager, Action onAbort, Action<string> onRetry)
|
||||
{
|
||||
var panel = widget;
|
||||
var abortButton = panel.Get<ButtonWidget>("ABORT_BUTTON");
|
||||
var retryButton = panel.Get<ButtonWidget>("RETRY_BUTTON");
|
||||
|
||||
abortButton.Visible = onAbort != null;
|
||||
abortButton.OnClick = () => { Ui.CloseWindow(); onAbort(); };
|
||||
|
||||
retryButton.Visible = onRetry != null;
|
||||
retryButton.OnClick = () =>
|
||||
{
|
||||
var password = passwordField != null && passwordField.IsVisible() ? passwordField.Text : orderManager.Password;
|
||||
|
||||
Ui.CloseWindow();
|
||||
onRetry(password);
|
||||
};
|
||||
|
||||
widget.Get<LabelWidget>("CONNECTING_DESC").GetText = () =>
|
||||
"Could not connect to {0}:{1}".F(orderManager.Host, orderManager.Port);
|
||||
|
||||
var connectionError = widget.Get<LabelWidget>("CONNECTION_ERROR");
|
||||
connectionError.GetText = () => orderManager.ServerError;
|
||||
|
||||
passwordField = panel.GetOrNull<PasswordFieldWidget>("PASSWORD");
|
||||
if (passwordField != null)
|
||||
{
|
||||
passwordField.Text = orderManager.Password;
|
||||
passwordField.IsVisible = () => orderManager.AuthenticationFailed;
|
||||
var passwordLabel = widget.Get<LabelWidget>("PASSWORD_LABEL");
|
||||
passwordLabel.IsVisible = passwordField.IsVisible;
|
||||
passwordField.OnEnterKey = () => { retryButton.OnClick(); return true; };
|
||||
passwordField.OnEscKey = () => { abortButton.OnClick(); return true; };
|
||||
}
|
||||
|
||||
passwordOffsetAdjusted = false;
|
||||
var connectionFailedTicker = panel.GetOrNull<LogicTickerWidget>("CONNECTION_FAILED_TICKER");
|
||||
if (connectionFailedTicker != null)
|
||||
{
|
||||
connectionFailedTicker.OnTick = () =>
|
||||
{
|
||||
// Adjust the dialog once the AuthenticationError is parsed.
|
||||
if (passwordField.IsVisible() && !passwordOffsetAdjusted)
|
||||
{
|
||||
var offset = passwordField.Bounds.Y - connectionError.Bounds.Y;
|
||||
abortButton.Bounds.Y += offset;
|
||||
retryButton.Bounds.Y += offset;
|
||||
panel.Bounds.Height += offset;
|
||||
panel.Bounds.Y -= offset / 2;
|
||||
|
||||
var background = panel.GetOrNull("CONNECTION_BACKGROUND");
|
||||
if (background != null)
|
||||
background.Bounds.Height += offset;
|
||||
|
||||
passwordOffsetAdjusted = true;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
43
OpenRA.Mods.Common/Widgets/Logic/DirectConnectLogic.cs
Normal file
43
OpenRA.Mods.Common/Widgets/Logic/DirectConnectLogic.cs
Normal file
@@ -0,0 +1,43 @@
|
||||
#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 OpenRA.Widgets;
|
||||
|
||||
namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
{
|
||||
public class DirectConnectLogic
|
||||
{
|
||||
[ObjectCreator.UseCtor]
|
||||
public DirectConnectLogic(Widget widget, Action onExit, Action openLobby)
|
||||
{
|
||||
var panel = widget;
|
||||
var ipField = panel.Get<TextFieldWidget>("IP");
|
||||
var portField = panel.Get<TextFieldWidget>("PORT");
|
||||
|
||||
var last = Game.Settings.Player.LastServer.Split(':');
|
||||
ipField.Text = last.Length > 1 ? last[0] : "localhost";
|
||||
portField.Text = last.Length == 2 ? last[1] : "1234";
|
||||
|
||||
panel.Get<ButtonWidget>("JOIN_BUTTON").OnClick = () =>
|
||||
{
|
||||
var port = Exts.WithDefault(1234, () => Exts.ParseIntegerInvariant(portField.Text));
|
||||
|
||||
Game.Settings.Player.LastServer = "{0}:{1}".F(ipField.Text, port);
|
||||
Game.Settings.Save();
|
||||
|
||||
Ui.CloseWindow();
|
||||
ConnectionLogic.Connect(ipField.Text, port, "", openLobby, onExit);
|
||||
};
|
||||
|
||||
panel.Get<ButtonWidget>("BACK_BUTTON").OnClick = () => { Ui.CloseWindow(); onExit(); };
|
||||
}
|
||||
}
|
||||
}
|
||||
219
OpenRA.Mods.Common/Widgets/Logic/Ingame/IngameChatLogic.cs
Normal file
219
OpenRA.Mods.Common/Widgets/Logic/Ingame/IngameChatLogic.cs
Normal file
@@ -0,0 +1,219 @@
|
||||
#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.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using OpenRA.Mods.Common.Commands;
|
||||
using OpenRA.Mods.Common.Traits;
|
||||
using OpenRA.Network;
|
||||
using OpenRA.Widgets;
|
||||
|
||||
namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
{
|
||||
public class IngameChatLogic
|
||||
{
|
||||
readonly OrderManager orderManager;
|
||||
readonly Ruleset modRules;
|
||||
|
||||
readonly ContainerWidget chatOverlay;
|
||||
readonly ChatDisplayWidget chatOverlayDisplay;
|
||||
|
||||
readonly ContainerWidget chatChrome;
|
||||
readonly ScrollPanelWidget chatScrollPanel;
|
||||
readonly ContainerWidget chatTemplate;
|
||||
readonly TextFieldWidget chatText;
|
||||
|
||||
readonly List<INotifyChat> chatTraits;
|
||||
|
||||
readonly TabCompletionLogic tabCompletion = new TabCompletionLogic();
|
||||
|
||||
bool disableTeamChat;
|
||||
bool teamChat;
|
||||
bool inDialog;
|
||||
|
||||
[ObjectCreator.UseCtor]
|
||||
public IngameChatLogic(Widget widget, OrderManager orderManager, World world, Ruleset modRules)
|
||||
{
|
||||
this.orderManager = orderManager;
|
||||
this.modRules = modRules;
|
||||
|
||||
chatTraits = world.WorldActor.TraitsImplementing<INotifyChat>().ToList();
|
||||
|
||||
var players = world.Players.Where(p => p != world.LocalPlayer && !p.NonCombatant && !p.IsBot);
|
||||
disableTeamChat = world.LocalPlayer == null || world.LobbyInfo.IsSinglePlayer || !players.Any(p => p.IsAlliedWith(world.LocalPlayer));
|
||||
teamChat = !disableTeamChat;
|
||||
|
||||
tabCompletion.Commands = chatTraits.OfType<ChatCommands>().SelectMany(x => x.Commands.Keys).ToList();
|
||||
tabCompletion.Names = orderManager.LobbyInfo.Clients.Select(c => c.Name).Distinct().ToList();
|
||||
|
||||
var chatPanel = (ContainerWidget)widget;
|
||||
chatOverlay = chatPanel.GetOrNull<ContainerWidget>("CHAT_OVERLAY");
|
||||
if (chatOverlay != null)
|
||||
{
|
||||
chatOverlayDisplay = chatOverlay.Get<ChatDisplayWidget>("CHAT_DISPLAY");
|
||||
chatOverlay.Visible = false;
|
||||
}
|
||||
else
|
||||
inDialog = true;
|
||||
|
||||
chatChrome = chatPanel.Get<ContainerWidget>("CHAT_CHROME");
|
||||
chatChrome.Visible = true;
|
||||
|
||||
var chatMode = chatChrome.Get<ButtonWidget>("CHAT_MODE");
|
||||
chatMode.GetText = () => teamChat ? "Team" : "All";
|
||||
chatMode.OnClick = () => teamChat ^= true;
|
||||
chatMode.IsDisabled = () => disableTeamChat;
|
||||
|
||||
chatText = chatChrome.Get<TextFieldWidget>("CHAT_TEXTFIELD");
|
||||
chatText.OnEnterKey = () =>
|
||||
{
|
||||
var team = teamChat && !disableTeamChat;
|
||||
if (chatText.Text != "")
|
||||
if (!chatText.Text.StartsWith("/"))
|
||||
orderManager.IssueOrder(Order.Chat(team, chatText.Text.Trim()));
|
||||
else
|
||||
if (chatTraits != null)
|
||||
{
|
||||
var text = chatText.Text.Trim();
|
||||
foreach (var trait in chatTraits)
|
||||
trait.OnChat(orderManager.LocalClient.Name, text);
|
||||
}
|
||||
|
||||
chatText.Text = "";
|
||||
CloseChat();
|
||||
return true;
|
||||
};
|
||||
chatText.OnTabKey = () =>
|
||||
{
|
||||
var previousText = chatText.Text;
|
||||
chatText.Text = tabCompletion.Complete(chatText.Text);
|
||||
chatText.CursorPosition = chatText.Text.Length;
|
||||
|
||||
if (chatText.Text == previousText)
|
||||
return SwitchTeamChat();
|
||||
else
|
||||
return true;
|
||||
};
|
||||
chatText.OnEscKey = () => { CloseChat(); return true; };
|
||||
|
||||
if (!inDialog)
|
||||
{
|
||||
var chatClose = chatChrome.Get<ButtonWidget>("CHAT_CLOSE");
|
||||
chatClose.OnClick += CloseChat;
|
||||
|
||||
chatPanel.OnKeyPress = e =>
|
||||
{
|
||||
if (e.Event == KeyInputEvent.Up)
|
||||
return false;
|
||||
|
||||
if (!chatChrome.IsVisible() && (e.Key == Keycode.RETURN || e.Key == Keycode.KP_ENTER))
|
||||
{
|
||||
OpenChat();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
}
|
||||
|
||||
chatScrollPanel = chatChrome.Get<ScrollPanelWidget>("CHAT_SCROLLPANEL");
|
||||
chatTemplate = chatScrollPanel.Get<ContainerWidget>("CHAT_TEMPLATE");
|
||||
chatScrollPanel.RemoveChildren();
|
||||
chatScrollPanel.ScrollToBottom();
|
||||
|
||||
foreach (var chatLine in orderManager.ChatCache)
|
||||
AddChatLine(chatLine.Color, chatLine.Name, chatLine.Text, true);
|
||||
|
||||
orderManager.AddChatLine += AddChatLineWrapper;
|
||||
Game.BeforeGameStart += UnregisterEvents;
|
||||
|
||||
CloseChat();
|
||||
}
|
||||
|
||||
bool SwitchTeamChat()
|
||||
{
|
||||
if (!disableTeamChat)
|
||||
teamChat ^= true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void UnregisterEvents()
|
||||
{
|
||||
orderManager.AddChatLine -= AddChatLineWrapper;
|
||||
Game.BeforeGameStart -= UnregisterEvents;
|
||||
}
|
||||
|
||||
public void OpenChat()
|
||||
{
|
||||
chatText.Text = "";
|
||||
chatChrome.Visible = true;
|
||||
chatScrollPanel.ScrollToBottom();
|
||||
chatText.TakeKeyboardFocus();
|
||||
if (!inDialog)
|
||||
chatOverlay.Visible = false;
|
||||
}
|
||||
|
||||
public void CloseChat()
|
||||
{
|
||||
if (inDialog)
|
||||
return;
|
||||
chatChrome.Visible = false;
|
||||
chatText.YieldKeyboardFocus();
|
||||
chatOverlay.Visible = true;
|
||||
}
|
||||
|
||||
public void AddChatLineWrapper(Color c, string from, string text)
|
||||
{
|
||||
AddChatLine(c, from, text, false);
|
||||
}
|
||||
|
||||
void AddChatLine(Color c, string from, string text, bool replayCache)
|
||||
{
|
||||
if (!(inDialog || replayCache))
|
||||
chatOverlayDisplay.AddLine(c, from, text);
|
||||
|
||||
var template = chatTemplate.Clone();
|
||||
var nameLabel = template.Get<LabelWidget>("NAME");
|
||||
var textLabel = template.Get<LabelWidget>("TEXT");
|
||||
|
||||
var name = "";
|
||||
if (!string.IsNullOrEmpty(from))
|
||||
name = from + ":";
|
||||
|
||||
var font = Game.Renderer.Fonts[nameLabel.Font];
|
||||
var nameSize = font.Measure(from);
|
||||
|
||||
nameLabel.GetColor = () => c;
|
||||
nameLabel.GetText = () => name;
|
||||
nameLabel.Bounds.Width = nameSize.X;
|
||||
textLabel.Bounds.X += nameSize.X;
|
||||
textLabel.Bounds.Width -= nameSize.X;
|
||||
|
||||
// Hack around our hacky wordwrap behavior: need to resize the widget to fit the text
|
||||
text = WidgetUtils.WrapText(text, textLabel.Bounds.Width, font);
|
||||
textLabel.GetText = () => text;
|
||||
var dh = font.Measure(text).Y - textLabel.Bounds.Height;
|
||||
if (dh > 0)
|
||||
{
|
||||
textLabel.Bounds.Height += dh;
|
||||
template.Bounds.Height += dh;
|
||||
}
|
||||
|
||||
var scrolledToBottom = chatScrollPanel.ScrolledToBottom;
|
||||
chatScrollPanel.AddChild(template);
|
||||
if (scrolledToBottom)
|
||||
chatScrollPanel.ScrollToBottom(smooth: true);
|
||||
|
||||
if (!replayCache)
|
||||
Sound.PlayNotification(modRules, null, "Sounds", "ChatLine", null);
|
||||
}
|
||||
}
|
||||
}
|
||||
88
OpenRA.Mods.Common/Widgets/Logic/Lobby/ClientTooltipLogic.cs
Normal file
88
OpenRA.Mods.Common/Widgets/Logic/Lobby/ClientTooltipLogic.cs
Normal file
@@ -0,0 +1,88 @@
|
||||
#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.Net;
|
||||
using OpenRA.Network;
|
||||
using OpenRA.Widgets;
|
||||
|
||||
namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
{
|
||||
public class ClientTooltipLogic
|
||||
{
|
||||
[ObjectCreator.UseCtor]
|
||||
public ClientTooltipLogic(Widget widget, TooltipContainerWidget tooltipContainer, OrderManager orderManager, int clientIndex)
|
||||
{
|
||||
var admin = widget.Get<LabelWidget>("ADMIN");
|
||||
var adminFont = Game.Renderer.Fonts[admin.Font];
|
||||
|
||||
var latency = widget.Get<LabelWidget>("LATENCY");
|
||||
var latencyFont = Game.Renderer.Fonts[latency.Font];
|
||||
|
||||
var latencyPrefix = widget.Get<LabelWidget>("LATENCY_PREFIX");
|
||||
var latencyPrefixFont = Game.Renderer.Fonts[latencyPrefix.Font];
|
||||
|
||||
var ip = widget.Get<LabelWidget>("IP");
|
||||
var addressFont = Game.Renderer.Fonts[ip.Font];
|
||||
|
||||
var location = widget.Get<LabelWidget>("LOCATION");
|
||||
var locationFont = Game.Renderer.Fonts[location.Font];
|
||||
|
||||
var locationOffset = location.Bounds.Y;
|
||||
var addressOffset = ip.Bounds.Y;
|
||||
var latencyOffset = latency.Bounds.Y;
|
||||
var tooltipHeight = widget.Bounds.Height;
|
||||
|
||||
var margin = widget.Bounds.Width;
|
||||
|
||||
tooltipContainer.IsVisible = () => (orderManager.LobbyInfo.ClientWithIndex(clientIndex) != null);
|
||||
tooltipContainer.BeforeRender = () =>
|
||||
{
|
||||
var latencyPrefixSize = latencyPrefix.Bounds.X + latencyPrefixFont.Measure(latencyPrefix.GetText() + " ").X;
|
||||
var width = Math.Max(locationFont.Measure(location.GetText()).X, Math.Max(adminFont.Measure(admin.GetText()).X, Math.Max(addressFont.Measure(ip.GetText()).X, latencyPrefixSize + latencyFont.Measure(latency.GetText()).X)));
|
||||
widget.Bounds.Width = width + 2 * margin;
|
||||
latency.Bounds.Width = widget.Bounds.Width;
|
||||
ip.Bounds.Width = widget.Bounds.Width;
|
||||
admin.Bounds.Width = widget.Bounds.Width;
|
||||
location.Bounds.Width = widget.Bounds.Width;
|
||||
|
||||
ip.Bounds.Y = addressOffset;
|
||||
latency.Bounds.Y = latencyOffset;
|
||||
location.Bounds.Y = locationOffset;
|
||||
widget.Bounds.Height = tooltipHeight;
|
||||
|
||||
if (admin.IsVisible())
|
||||
{
|
||||
ip.Bounds.Y += admin.Bounds.Height;
|
||||
latency.Bounds.Y += admin.Bounds.Height;
|
||||
location.Bounds.Y += admin.Bounds.Height;
|
||||
widget.Bounds.Height += admin.Bounds.Height;
|
||||
}
|
||||
|
||||
latencyPrefix.Bounds.Y = latency.Bounds.Y;
|
||||
latency.Bounds.X = latencyPrefixSize;
|
||||
};
|
||||
|
||||
admin.IsVisible = () => orderManager.LobbyInfo.ClientWithIndex(clientIndex).IsAdmin;
|
||||
var client = orderManager.LobbyInfo.ClientWithIndex(clientIndex);
|
||||
var ping = orderManager.LobbyInfo.PingFromClient(client);
|
||||
latency.GetText = () => LobbyUtils.LatencyDescription(ping);
|
||||
latency.GetColor = () => LobbyUtils.LatencyColor(ping);
|
||||
var address = orderManager.LobbyInfo.ClientWithIndex(clientIndex).IpAddress;
|
||||
if (clientIndex == orderManager.LocalClient.Index && UPnP.NatDevice != null
|
||||
&& address == IPAddress.Loopback.ToString())
|
||||
address = UPnP.NatDevice.GetExternalIP().ToString();
|
||||
var cachedDescriptiveIP = LobbyUtils.DescriptiveIpAddress(address);
|
||||
ip.GetText = () => cachedDescriptiveIP;
|
||||
var cachedCountryLookup = LobbyUtils.LookupCountry(address);
|
||||
location.GetText = () => cachedCountryLookup;
|
||||
}
|
||||
}
|
||||
}
|
||||
41
OpenRA.Mods.Common/Widgets/Logic/Lobby/KickClientLogic.cs
Normal file
41
OpenRA.Mods.Common/Widgets/Logic/Lobby/KickClientLogic.cs
Normal file
@@ -0,0 +1,41 @@
|
||||
#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 OpenRA.Widgets;
|
||||
|
||||
namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
{
|
||||
class KickClientLogic
|
||||
{
|
||||
[ObjectCreator.UseCtor]
|
||||
public KickClientLogic(Widget widget, string clientName, Action<bool> okPressed, Action cancelPressed)
|
||||
{
|
||||
widget.Get<LabelWidget>("TITLE").GetText = () => "Kick {0}?".F(clientName);
|
||||
|
||||
var tempBan = false;
|
||||
var preventRejoiningCheckbox = widget.Get<CheckboxWidget>("PREVENT_REJOINING_CHECKBOX");
|
||||
preventRejoiningCheckbox.IsChecked = () => tempBan;
|
||||
preventRejoiningCheckbox.OnClick = () => tempBan ^= true;
|
||||
|
||||
widget.Get<ButtonWidget>("OK_BUTTON").OnClick = () =>
|
||||
{
|
||||
widget.Parent.RemoveChild(widget);
|
||||
okPressed(tempBan);
|
||||
};
|
||||
|
||||
widget.Get<ButtonWidget>("CANCEL_BUTTON").OnClick = () =>
|
||||
{
|
||||
widget.Parent.RemoveChild(widget);
|
||||
cancelPressed();
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
#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 OpenRA.Widgets;
|
||||
|
||||
namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
{
|
||||
class KickSpectatorsLogic
|
||||
{
|
||||
[ObjectCreator.UseCtor]
|
||||
public KickSpectatorsLogic(Widget widget, string clientCount, Action okPressed, Action cancelPressed)
|
||||
{
|
||||
widget.Get<LabelWidget>("TEXT").GetText = () => "Are you sure you want to kick {0} spectators?".F(clientCount);
|
||||
|
||||
widget.Get<ButtonWidget>("OK_BUTTON").OnClick = () =>
|
||||
{
|
||||
widget.Parent.RemoveChild(widget);
|
||||
okPressed();
|
||||
};
|
||||
|
||||
widget.Get<ButtonWidget>("CANCEL_BUTTON").OnClick = () =>
|
||||
{
|
||||
widget.Parent.RemoveChild(widget);
|
||||
cancelPressed();
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
829
OpenRA.Mods.Common/Widgets/Logic/Lobby/LobbyLogic.cs
Normal file
829
OpenRA.Mods.Common/Widgets/Logic/Lobby/LobbyLogic.cs
Normal file
@@ -0,0 +1,829 @@
|
||||
#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.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Mods.Common.Traits;
|
||||
using OpenRA.Network;
|
||||
using OpenRA.Traits;
|
||||
using OpenRA.Widgets;
|
||||
|
||||
namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
{
|
||||
public class LobbyLogic
|
||||
{
|
||||
static readonly Action DoNothing = () => { };
|
||||
|
||||
public MapPreview Map = MapCache.UnknownMap;
|
||||
|
||||
readonly Action onStart;
|
||||
readonly Action onExit;
|
||||
readonly OrderManager orderManager;
|
||||
readonly bool skirmishMode;
|
||||
readonly Ruleset modRules;
|
||||
|
||||
enum PanelType { Players, Options, Kick, ForceStart }
|
||||
PanelType panel = PanelType.Players;
|
||||
|
||||
readonly Widget lobby;
|
||||
readonly Widget editablePlayerTemplate;
|
||||
readonly Widget nonEditablePlayerTemplate;
|
||||
readonly Widget emptySlotTemplate;
|
||||
readonly Widget editableSpectatorTemplate;
|
||||
readonly Widget nonEditableSpectatorTemplate;
|
||||
readonly Widget newSpectatorTemplate;
|
||||
|
||||
readonly ScrollPanelWidget chatPanel;
|
||||
readonly Widget chatTemplate;
|
||||
|
||||
readonly ScrollPanelWidget players;
|
||||
|
||||
readonly Dictionary<string, LobbyCountry> countries = new Dictionary<string, LobbyCountry>();
|
||||
|
||||
readonly ColorPreviewManagerWidget colorPreview;
|
||||
|
||||
readonly TabCompletionLogic tabCompletion = new TabCompletionLogic();
|
||||
|
||||
readonly LabelWidget chatLabel;
|
||||
bool teamChat;
|
||||
|
||||
// Listen for connection failures
|
||||
void ConnectionStateChanged(OrderManager om)
|
||||
{
|
||||
if (om.Connection.ConnectionState == ConnectionState.NotConnected)
|
||||
{
|
||||
// Show connection failed dialog
|
||||
CloseWindow();
|
||||
|
||||
Action onConnect = () =>
|
||||
{
|
||||
Game.OpenWindow("SERVER_LOBBY", new WidgetArgs()
|
||||
{
|
||||
{ "onExit", onExit },
|
||||
{ "onStart", onStart },
|
||||
{ "skirmishMode", false }
|
||||
});
|
||||
};
|
||||
|
||||
Action<string> onRetry = password => ConnectionLogic.Connect(om.Host, om.Port, password, onConnect, onExit);
|
||||
|
||||
Ui.OpenWindow("CONNECTIONFAILED_PANEL", new WidgetArgs()
|
||||
{
|
||||
{ "orderManager", om },
|
||||
{ "onAbort", onExit },
|
||||
{ "onRetry", onRetry }
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void CloseWindow()
|
||||
{
|
||||
orderManager.AddChatLine -= AddChatLine;
|
||||
Game.LobbyInfoChanged -= UpdateCurrentMap;
|
||||
Game.LobbyInfoChanged -= UpdatePlayerList;
|
||||
Game.BeforeGameStart -= OnGameStart;
|
||||
Game.ConnectionStateChanged -= ConnectionStateChanged;
|
||||
|
||||
Ui.CloseWindow();
|
||||
}
|
||||
|
||||
[ObjectCreator.UseCtor]
|
||||
internal LobbyLogic(Widget widget, WorldRenderer worldRenderer, OrderManager orderManager,
|
||||
Action onExit, Action onStart, bool skirmishMode, Ruleset modRules)
|
||||
{
|
||||
lobby = widget;
|
||||
this.orderManager = orderManager;
|
||||
this.onStart = onStart;
|
||||
this.onExit = onExit;
|
||||
this.skirmishMode = skirmishMode;
|
||||
this.modRules = modRules;
|
||||
|
||||
orderManager.AddChatLine += AddChatLine;
|
||||
Game.LobbyInfoChanged += UpdateCurrentMap;
|
||||
Game.LobbyInfoChanged += UpdatePlayerList;
|
||||
Game.BeforeGameStart += OnGameStart;
|
||||
Game.ConnectionStateChanged += ConnectionStateChanged;
|
||||
|
||||
var name = lobby.GetOrNull<LabelWidget>("SERVER_NAME");
|
||||
if (name != null)
|
||||
name.GetText = () => orderManager.LobbyInfo.GlobalSettings.ServerName;
|
||||
|
||||
Ui.LoadWidget("LOBBY_MAP_PREVIEW", lobby.Get("MAP_PREVIEW_ROOT"), new WidgetArgs
|
||||
{
|
||||
{ "orderManager", orderManager },
|
||||
{ "lobby", this }
|
||||
});
|
||||
|
||||
UpdateCurrentMap();
|
||||
players = Ui.LoadWidget<ScrollPanelWidget>("LOBBY_PLAYER_BIN", lobby.Get("PLAYER_BIN_ROOT"), new WidgetArgs());
|
||||
players.IsVisible = () => panel == PanelType.Players;
|
||||
|
||||
var playerBinHeaders = lobby.GetOrNull<ContainerWidget>("LABEL_CONTAINER");
|
||||
if (playerBinHeaders != null)
|
||||
playerBinHeaders.IsVisible = () => panel == PanelType.Players;
|
||||
|
||||
editablePlayerTemplate = players.Get("TEMPLATE_EDITABLE_PLAYER");
|
||||
nonEditablePlayerTemplate = players.Get("TEMPLATE_NONEDITABLE_PLAYER");
|
||||
emptySlotTemplate = players.Get("TEMPLATE_EMPTY");
|
||||
editableSpectatorTemplate = players.Get("TEMPLATE_EDITABLE_SPECTATOR");
|
||||
nonEditableSpectatorTemplate = players.Get("TEMPLATE_NONEDITABLE_SPECTATOR");
|
||||
newSpectatorTemplate = players.Get("TEMPLATE_NEW_SPECTATOR");
|
||||
colorPreview = lobby.Get<ColorPreviewManagerWidget>("COLOR_MANAGER");
|
||||
colorPreview.Color = Game.Settings.Player.Color;
|
||||
|
||||
countries.Add("random", new LobbyCountry { Name = "Any" });
|
||||
foreach (var c in modRules.Actors["world"].Traits.WithInterface<CountryInfo>().Where(c => c.Selectable))
|
||||
countries.Add(c.Race, new LobbyCountry { Name = c.Name, Side = c.Side, Description = c.Description });
|
||||
|
||||
var gameStarting = false;
|
||||
Func<bool> configurationDisabled = () => !Game.IsHost || gameStarting ||
|
||||
panel == PanelType.Kick || panel == PanelType.ForceStart ||
|
||||
orderManager.LocalClient == null || orderManager.LocalClient.IsReady;
|
||||
|
||||
var mapButton = lobby.GetOrNull<ButtonWidget>("CHANGEMAP_BUTTON");
|
||||
if (mapButton != null)
|
||||
{
|
||||
mapButton.IsDisabled = configurationDisabled;
|
||||
mapButton.OnClick = () =>
|
||||
{
|
||||
var onSelect = new Action<string>(uid =>
|
||||
{
|
||||
// Don't select the same map again
|
||||
if (uid == Map.Uid)
|
||||
return;
|
||||
|
||||
orderManager.IssueOrder(Order.Command("map " + uid));
|
||||
Game.Settings.Server.Map = uid;
|
||||
Game.Settings.Save();
|
||||
});
|
||||
|
||||
Ui.OpenWindow("MAPCHOOSER_PANEL", new WidgetArgs()
|
||||
{
|
||||
{ "initialMap", Map.Uid },
|
||||
{ "onExit", DoNothing },
|
||||
{ "onSelect", onSelect },
|
||||
{ "filter", MapVisibility.Lobby },
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
var slotsButton = lobby.GetOrNull<DropDownButtonWidget>("SLOTS_DROPDOWNBUTTON");
|
||||
if (slotsButton != null)
|
||||
{
|
||||
slotsButton.IsDisabled = () => configurationDisabled() || panel != PanelType.Players ||
|
||||
Map.RuleStatus != MapRuleStatus.Cached || !orderManager.LobbyInfo.Slots.Values.Any(s => s.AllowBots || !s.LockTeam);
|
||||
|
||||
var botNames = modRules.Actors["player"].Traits.WithInterface<IBotInfo>().Select(t => t.Name);
|
||||
slotsButton.OnMouseDown = _ =>
|
||||
{
|
||||
var options = new Dictionary<string, IEnumerable<DropDownOption>>();
|
||||
|
||||
var botController = orderManager.LobbyInfo.Clients.FirstOrDefault(c => c.IsAdmin);
|
||||
if (orderManager.LobbyInfo.Slots.Values.Any(s => s.AllowBots))
|
||||
{
|
||||
var botOptions = new List<DropDownOption>()
|
||||
{
|
||||
new DropDownOption()
|
||||
{
|
||||
Title = "Add",
|
||||
IsSelected = () => false,
|
||||
OnClick = () =>
|
||||
{
|
||||
foreach (var slot in orderManager.LobbyInfo.Slots)
|
||||
{
|
||||
var bot = botNames.Random(Game.CosmeticRandom);
|
||||
var c = orderManager.LobbyInfo.ClientInSlot(slot.Key);
|
||||
if (slot.Value.AllowBots == true && (c == null || c.Bot != null))
|
||||
orderManager.IssueOrder(Order.Command("slot_bot {0} {1} {2}".F(slot.Key, botController.Index, bot)));
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (orderManager.LobbyInfo.Clients.Any(c => c.Bot != null))
|
||||
{
|
||||
botOptions.Add(new DropDownOption()
|
||||
{
|
||||
Title = "Remove",
|
||||
IsSelected = () => false,
|
||||
OnClick = () =>
|
||||
{
|
||||
foreach (var slot in orderManager.LobbyInfo.Slots)
|
||||
{
|
||||
var c = orderManager.LobbyInfo.ClientInSlot(slot.Key);
|
||||
if (c != null && c.Bot != null)
|
||||
orderManager.IssueOrder(Order.Command("slot_open " + slot.Value.PlayerReference));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
options.Add("Configure Bots", botOptions);
|
||||
}
|
||||
|
||||
var teamCount = (orderManager.LobbyInfo.Slots.Count(s => !s.Value.LockTeam && orderManager.LobbyInfo.ClientInSlot(s.Key) != null) + 1) / 2;
|
||||
if (teamCount >= 1)
|
||||
{
|
||||
var teamOptions = Enumerable.Range(2, teamCount - 1).Reverse().Select(d => new DropDownOption
|
||||
{
|
||||
Title = "{0} Teams".F(d),
|
||||
IsSelected = () => false,
|
||||
OnClick = () => orderManager.IssueOrder(Order.Command("assignteams {0}".F(d.ToString())))
|
||||
}).ToList();
|
||||
|
||||
if (orderManager.LobbyInfo.Slots.Any(s => s.Value.AllowBots))
|
||||
{
|
||||
teamOptions.Add(new DropDownOption
|
||||
{
|
||||
Title = "Humans vs Bots",
|
||||
IsSelected = () => false,
|
||||
OnClick = () => orderManager.IssueOrder(Order.Command("assignteams 1"))
|
||||
});
|
||||
}
|
||||
|
||||
teamOptions.Add(new DropDownOption
|
||||
{
|
||||
Title = "Free for all",
|
||||
IsSelected = () => false,
|
||||
OnClick = () => orderManager.IssueOrder(Order.Command("assignteams 0"))
|
||||
});
|
||||
|
||||
options.Add("Configure Teams", teamOptions);
|
||||
}
|
||||
|
||||
Func<DropDownOption, ScrollItemWidget, ScrollItemWidget> setupItem = (option, template) =>
|
||||
{
|
||||
var item = ScrollItemWidget.Setup(template, option.IsSelected, option.OnClick);
|
||||
item.Get<LabelWidget>("LABEL").GetText = () => option.Title;
|
||||
return item;
|
||||
};
|
||||
slotsButton.ShowDropDown("LABEL_DROPDOWN_TEMPLATE", 175, options, setupItem);
|
||||
};
|
||||
}
|
||||
|
||||
var optionsBin = Ui.LoadWidget("LOBBY_OPTIONS_BIN", lobby, new WidgetArgs());
|
||||
optionsBin.IsVisible = () => panel == PanelType.Options;
|
||||
|
||||
var optionsButton = lobby.Get<ButtonWidget>("OPTIONS_BUTTON");
|
||||
optionsButton.IsDisabled = () => Map.RuleStatus != MapRuleStatus.Cached || panel == PanelType.Kick || panel == PanelType.ForceStart;
|
||||
optionsButton.GetText = () => panel == PanelType.Options ? "Players" : "Options";
|
||||
optionsButton.OnClick = () => panel = (panel == PanelType.Options) ? PanelType.Players : PanelType.Options;
|
||||
|
||||
// Force start panel
|
||||
Action startGame = () =>
|
||||
{
|
||||
gameStarting = true;
|
||||
orderManager.IssueOrder(Order.Command("startgame"));
|
||||
};
|
||||
|
||||
var startGameButton = lobby.GetOrNull<ButtonWidget>("START_GAME_BUTTON");
|
||||
if (startGameButton != null)
|
||||
{
|
||||
startGameButton.IsDisabled = () => configurationDisabled() || Map.RuleStatus != MapRuleStatus.Cached ||
|
||||
orderManager.LobbyInfo.Slots.Any(sl => sl.Value.Required && orderManager.LobbyInfo.ClientInSlot(sl.Key) == null);
|
||||
startGameButton.OnClick = () =>
|
||||
{
|
||||
Func<KeyValuePair<string, Session.Slot>, bool> notReady = sl =>
|
||||
{
|
||||
var cl = orderManager.LobbyInfo.ClientInSlot(sl.Key);
|
||||
|
||||
// Bots and admins don't count
|
||||
return cl != null && !cl.IsAdmin && cl.Bot == null && !cl.IsReady;
|
||||
};
|
||||
|
||||
if (orderManager.LobbyInfo.Slots.Any(notReady))
|
||||
panel = PanelType.ForceStart;
|
||||
else
|
||||
startGame();
|
||||
};
|
||||
}
|
||||
|
||||
var forceStartBin = Ui.LoadWidget("FORCE_START_DIALOG", lobby, new WidgetArgs());
|
||||
forceStartBin.IsVisible = () => panel == PanelType.ForceStart;
|
||||
forceStartBin.Get("KICK_WARNING").IsVisible = () => orderManager.LobbyInfo.Clients.Any(c => c.IsInvalid);
|
||||
forceStartBin.Get<ButtonWidget>("OK_BUTTON").OnClick = startGame;
|
||||
forceStartBin.Get<ButtonWidget>("CANCEL_BUTTON").OnClick = () => panel = PanelType.Players;
|
||||
|
||||
// Options panel
|
||||
var allowCheats = optionsBin.GetOrNull<CheckboxWidget>("ALLOWCHEATS_CHECKBOX");
|
||||
if (allowCheats != null)
|
||||
{
|
||||
allowCheats.IsChecked = () => orderManager.LobbyInfo.GlobalSettings.AllowCheats;
|
||||
allowCheats.IsDisabled = () => Map.Status != MapStatus.Available || Map.Map.Options.Cheats.HasValue || configurationDisabled();
|
||||
allowCheats.OnClick = () => orderManager.IssueOrder(Order.Command(
|
||||
"allowcheats {0}".F(!orderManager.LobbyInfo.GlobalSettings.AllowCheats)));
|
||||
}
|
||||
|
||||
var crates = optionsBin.GetOrNull<CheckboxWidget>("CRATES_CHECKBOX");
|
||||
if (crates != null)
|
||||
{
|
||||
crates.IsChecked = () => orderManager.LobbyInfo.GlobalSettings.Crates;
|
||||
crates.IsDisabled = () => Map.Status != MapStatus.Available || Map.Map.Options.Crates.HasValue || configurationDisabled();
|
||||
crates.OnClick = () => orderManager.IssueOrder(Order.Command(
|
||||
"crates {0}".F(!orderManager.LobbyInfo.GlobalSettings.Crates)));
|
||||
}
|
||||
|
||||
var creeps = optionsBin.GetOrNull<CheckboxWidget>("CREEPS_CHECKBOX");
|
||||
if (creeps != null)
|
||||
{
|
||||
creeps.IsChecked = () => orderManager.LobbyInfo.GlobalSettings.Creeps;
|
||||
creeps.IsDisabled = () => Map.Status != MapStatus.Available || Map.Map.Options.Creeps.HasValue || configurationDisabled();
|
||||
creeps.OnClick = () => orderManager.IssueOrder(Order.Command(
|
||||
"creeps {0}".F(!orderManager.LobbyInfo.GlobalSettings.Creeps)));
|
||||
}
|
||||
|
||||
var allybuildradius = optionsBin.GetOrNull<CheckboxWidget>("ALLYBUILDRADIUS_CHECKBOX");
|
||||
if (allybuildradius != null)
|
||||
{
|
||||
allybuildradius.IsChecked = () => orderManager.LobbyInfo.GlobalSettings.AllyBuildRadius;
|
||||
allybuildradius.IsDisabled = () => Map.Status != MapStatus.Available || Map.Map.Options.AllyBuildRadius.HasValue || configurationDisabled();
|
||||
allybuildradius.OnClick = () => orderManager.IssueOrder(Order.Command(
|
||||
"allybuildradius {0}".F(!orderManager.LobbyInfo.GlobalSettings.AllyBuildRadius)));
|
||||
}
|
||||
|
||||
var fragileAlliance = optionsBin.GetOrNull<CheckboxWidget>("FRAGILEALLIANCES_CHECKBOX");
|
||||
if (fragileAlliance != null)
|
||||
{
|
||||
fragileAlliance.IsChecked = () => orderManager.LobbyInfo.GlobalSettings.FragileAlliances;
|
||||
fragileAlliance.IsDisabled = () => Map.Status != MapStatus.Available || Map.Map.Options.FragileAlliances.HasValue || configurationDisabled();
|
||||
fragileAlliance.OnClick = () => orderManager.IssueOrder(Order.Command(
|
||||
"fragilealliance {0}".F(!orderManager.LobbyInfo.GlobalSettings.FragileAlliances)));
|
||||
}
|
||||
|
||||
var shortGame = optionsBin.GetOrNull<CheckboxWidget>("SHORTGAME_CHECKBOX");
|
||||
if (shortGame != null)
|
||||
{
|
||||
shortGame.IsChecked = () => orderManager.LobbyInfo.GlobalSettings.ShortGame;
|
||||
shortGame.IsDisabled = () => Map.Status != MapStatus.Available || Map.Map.Options.ShortGame.HasValue || configurationDisabled();
|
||||
shortGame.OnClick = () => orderManager.IssueOrder(Order.Command(
|
||||
"shortgame {0}".F(!orderManager.LobbyInfo.GlobalSettings.ShortGame)));
|
||||
}
|
||||
|
||||
var difficulty = optionsBin.GetOrNull<DropDownButtonWidget>("DIFFICULTY_DROPDOWNBUTTON");
|
||||
if (difficulty != null)
|
||||
{
|
||||
difficulty.IsVisible = () => Map.Status == MapStatus.Available && Map.Map.Options.Difficulties.Any();
|
||||
difficulty.IsDisabled = () => Map.Status != MapStatus.Available || configurationDisabled();
|
||||
difficulty.GetText = () => orderManager.LobbyInfo.GlobalSettings.Difficulty;
|
||||
difficulty.OnMouseDown = _ =>
|
||||
{
|
||||
var options = Map.Map.Options.Difficulties.Select(d => new DropDownOption
|
||||
{
|
||||
Title = d,
|
||||
IsSelected = () => orderManager.LobbyInfo.GlobalSettings.Difficulty == d,
|
||||
OnClick = () => orderManager.IssueOrder(Order.Command("difficulty {0}".F(d)))
|
||||
});
|
||||
Func<DropDownOption, ScrollItemWidget, ScrollItemWidget> setupItem = (option, template) =>
|
||||
{
|
||||
var item = ScrollItemWidget.Setup(template, option.IsSelected, option.OnClick);
|
||||
item.Get<LabelWidget>("LABEL").GetText = () => option.Title;
|
||||
return item;
|
||||
};
|
||||
difficulty.ShowDropDown("LABEL_DROPDOWN_TEMPLATE", options.Count() * 30, options, setupItem);
|
||||
};
|
||||
|
||||
optionsBin.Get<LabelWidget>("DIFFICULTY_DESC").IsVisible = difficulty.IsVisible;
|
||||
}
|
||||
|
||||
var startingUnits = optionsBin.GetOrNull<DropDownButtonWidget>("STARTINGUNITS_DROPDOWNBUTTON");
|
||||
if (startingUnits != null)
|
||||
{
|
||||
var startUnitsInfo = modRules.Actors["world"].Traits.WithInterface<MPStartUnitsInfo>();
|
||||
var classes = startUnitsInfo.Select(a => a.Class).Distinct();
|
||||
Func<string, string> className = c =>
|
||||
{
|
||||
var selectedClass = startUnitsInfo.Where(s => s.Class == c).Select(u => u.ClassName).FirstOrDefault();
|
||||
return selectedClass != null ? selectedClass : c;
|
||||
};
|
||||
|
||||
startingUnits.IsDisabled = () => Map.Status != MapStatus.Available || !Map.Map.Options.ConfigurableStartingUnits || configurationDisabled();
|
||||
startingUnits.GetText = () => Map.Status != MapStatus.Available || !Map.Map.Options.ConfigurableStartingUnits ? "Not Available" : className(orderManager.LobbyInfo.GlobalSettings.StartingUnitsClass);
|
||||
startingUnits.OnMouseDown = _ =>
|
||||
{
|
||||
var options = classes.Select(c => new DropDownOption
|
||||
{
|
||||
Title = className(c),
|
||||
IsSelected = () => orderManager.LobbyInfo.GlobalSettings.StartingUnitsClass == c,
|
||||
OnClick = () => orderManager.IssueOrder(Order.Command("startingunits {0}".F(c)))
|
||||
});
|
||||
|
||||
Func<DropDownOption, ScrollItemWidget, ScrollItemWidget> setupItem = (option, template) =>
|
||||
{
|
||||
var item = ScrollItemWidget.Setup(template, option.IsSelected, option.OnClick);
|
||||
item.Get<LabelWidget>("LABEL").GetText = () => option.Title;
|
||||
return item;
|
||||
};
|
||||
|
||||
startingUnits.ShowDropDown("LABEL_DROPDOWN_TEMPLATE", options.Count() * 30, options, setupItem);
|
||||
};
|
||||
|
||||
optionsBin.Get<LabelWidget>("STARTINGUNITS_DESC").IsVisible = startingUnits.IsVisible;
|
||||
}
|
||||
|
||||
var startingCash = optionsBin.GetOrNull<DropDownButtonWidget>("STARTINGCASH_DROPDOWNBUTTON");
|
||||
if (startingCash != null)
|
||||
{
|
||||
startingCash.IsDisabled = () => Map.Status != MapStatus.Available || Map.Map.Options.StartingCash.HasValue || configurationDisabled();
|
||||
startingCash.GetText = () => Map.Status != MapStatus.Available || Map.Map.Options.StartingCash.HasValue ? "Not Available" : "${0}".F(orderManager.LobbyInfo.GlobalSettings.StartingCash);
|
||||
startingCash.OnMouseDown = _ =>
|
||||
{
|
||||
var options = modRules.Actors["player"].Traits.Get<PlayerResourcesInfo>().SelectableCash.Select(c => new DropDownOption
|
||||
{
|
||||
Title = "${0}".F(c),
|
||||
IsSelected = () => orderManager.LobbyInfo.GlobalSettings.StartingCash == c,
|
||||
OnClick = () => orderManager.IssueOrder(Order.Command("startingcash {0}".F(c)))
|
||||
});
|
||||
|
||||
Func<DropDownOption, ScrollItemWidget, ScrollItemWidget> setupItem = (option, template) =>
|
||||
{
|
||||
var item = ScrollItemWidget.Setup(template, option.IsSelected, option.OnClick);
|
||||
item.Get<LabelWidget>("LABEL").GetText = () => option.Title;
|
||||
return item;
|
||||
};
|
||||
|
||||
startingCash.ShowDropDown("LABEL_DROPDOWN_TEMPLATE", options.Count() * 30, options, setupItem);
|
||||
};
|
||||
}
|
||||
|
||||
var techLevel = optionsBin.GetOrNull<DropDownButtonWidget>("TECHLEVEL_DROPDOWNBUTTON");
|
||||
if (techLevel != null)
|
||||
{
|
||||
var techTraits = modRules.Actors["player"].Traits.WithInterface<ProvidesTechPrerequisiteInfo>().ToArray();
|
||||
techLevel.IsVisible = () => techTraits.Length > 0;
|
||||
optionsBin.GetOrNull<LabelWidget>("TECHLEVEL_DESC").IsVisible = () => techTraits.Length > 0;
|
||||
techLevel.IsDisabled = () => Map.Status != MapStatus.Available || Map.Map.Options.TechLevel != null || configurationDisabled() || techTraits.Length <= 1;
|
||||
techLevel.GetText = () => Map.Status != MapStatus.Available || Map.Map.Options.TechLevel != null ? "Not Available" : "{0}".F(orderManager.LobbyInfo.GlobalSettings.TechLevel);
|
||||
techLevel.OnMouseDown = _ =>
|
||||
{
|
||||
var options = techTraits.Select(c => new DropDownOption
|
||||
{
|
||||
Title = "{0}".F(c.Name),
|
||||
IsSelected = () => orderManager.LobbyInfo.GlobalSettings.TechLevel == c.Name,
|
||||
OnClick = () => orderManager.IssueOrder(Order.Command("techlevel {0}".F(c.Name)))
|
||||
});
|
||||
|
||||
Func<DropDownOption, ScrollItemWidget, ScrollItemWidget> setupItem = (option, template) =>
|
||||
{
|
||||
var item = ScrollItemWidget.Setup(template, option.IsSelected, option.OnClick);
|
||||
item.Get<LabelWidget>("LABEL").GetText = () => option.Title;
|
||||
return item;
|
||||
};
|
||||
|
||||
techLevel.ShowDropDown("LABEL_DROPDOWN_TEMPLATE", options.Count() * 30, options, setupItem);
|
||||
};
|
||||
}
|
||||
|
||||
var enableShroud = optionsBin.GetOrNull<CheckboxWidget>("SHROUD_CHECKBOX");
|
||||
if (enableShroud != null)
|
||||
{
|
||||
enableShroud.IsChecked = () => orderManager.LobbyInfo.GlobalSettings.Shroud;
|
||||
enableShroud.IsDisabled = () => Map.Status != MapStatus.Available || Map.Map.Options.Shroud.HasValue || configurationDisabled();
|
||||
enableShroud.OnClick = () => orderManager.IssueOrder(Order.Command(
|
||||
"shroud {0}".F(!orderManager.LobbyInfo.GlobalSettings.Shroud)));
|
||||
}
|
||||
|
||||
var enableFog = optionsBin.GetOrNull<CheckboxWidget>("FOG_CHECKBOX");
|
||||
if (enableFog != null)
|
||||
{
|
||||
enableFog.IsChecked = () => orderManager.LobbyInfo.GlobalSettings.Fog;
|
||||
enableFog.IsDisabled = () => Map.Status != MapStatus.Available || Map.Map.Options.Fog.HasValue || configurationDisabled();
|
||||
enableFog.OnClick = () => orderManager.IssueOrder(Order.Command(
|
||||
"fog {0}".F(!orderManager.LobbyInfo.GlobalSettings.Fog)));
|
||||
}
|
||||
|
||||
var disconnectButton = lobby.Get<ButtonWidget>("DISCONNECT_BUTTON");
|
||||
disconnectButton.OnClick = () => { CloseWindow(); onExit(); };
|
||||
|
||||
if (skirmishMode)
|
||||
disconnectButton.Text = "Back";
|
||||
|
||||
chatLabel = lobby.Get<LabelWidget>("LABEL_CHATTYPE");
|
||||
var chatTextField = lobby.Get<TextFieldWidget>("CHAT_TEXTFIELD");
|
||||
|
||||
chatTextField.TakeKeyboardFocus();
|
||||
|
||||
chatTextField.OnEnterKey = () =>
|
||||
{
|
||||
if (chatTextField.Text.Length == 0)
|
||||
return true;
|
||||
|
||||
// Always scroll to bottom when we've typed something
|
||||
chatPanel.ScrollToBottom();
|
||||
|
||||
orderManager.IssueOrder(Order.Chat(teamChat, chatTextField.Text));
|
||||
chatTextField.Text = "";
|
||||
return true;
|
||||
};
|
||||
chatTextField.OnTabKey = () =>
|
||||
{
|
||||
var previousText = chatTextField.Text;
|
||||
chatTextField.Text = tabCompletion.Complete(chatTextField.Text);
|
||||
chatTextField.CursorPosition = chatTextField.Text.Length;
|
||||
|
||||
if (chatTextField.Text == previousText)
|
||||
return SwitchTeamChat();
|
||||
else
|
||||
return true;
|
||||
};
|
||||
|
||||
chatPanel = lobby.Get<ScrollPanelWidget>("CHAT_DISPLAY");
|
||||
chatTemplate = chatPanel.Get("CHAT_TEMPLATE");
|
||||
chatPanel.RemoveChildren();
|
||||
|
||||
var musicButton = lobby.GetOrNull<ButtonWidget>("MUSIC_BUTTON");
|
||||
if (musicButton != null)
|
||||
musicButton.OnClick = () => Ui.OpenWindow("MUSIC_PANEL", new WidgetArgs
|
||||
{
|
||||
{ "onExit", DoNothing },
|
||||
{ "world", orderManager.World }
|
||||
});
|
||||
|
||||
var settingsButton = lobby.GetOrNull<ButtonWidget>("SETTINGS_BUTTON");
|
||||
if (settingsButton != null)
|
||||
{
|
||||
settingsButton.OnClick = () => Ui.OpenWindow("SETTINGS_PANEL", new WidgetArgs
|
||||
{
|
||||
{ "onExit", DoNothing },
|
||||
{ "worldRenderer", worldRenderer }
|
||||
});
|
||||
}
|
||||
|
||||
// Add a bot on the first lobbyinfo update
|
||||
if (skirmishMode)
|
||||
{
|
||||
Game.LobbyInfoChanged += WidgetUtils.Once(() =>
|
||||
{
|
||||
var slot = orderManager.LobbyInfo.FirstEmptyBotSlot();
|
||||
var bot = modRules.Actors["player"].Traits.WithInterface<IBotInfo>().Select(t => t.Name).FirstOrDefault();
|
||||
var botController = orderManager.LobbyInfo.Clients.FirstOrDefault(c => c.IsAdmin);
|
||||
if (slot != null && bot != null)
|
||||
orderManager.IssueOrder(Order.Command("slot_bot {0} {1} {2}".F(slot, botController.Index, bot)));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void AddChatLine(Color c, string from, string text)
|
||||
{
|
||||
var template = chatTemplate.Clone();
|
||||
var nameLabel = template.Get<LabelWidget>("NAME");
|
||||
var timeLabel = template.Get<LabelWidget>("TIME");
|
||||
var textLabel = template.Get<LabelWidget>("TEXT");
|
||||
|
||||
var name = from + ":";
|
||||
var font = Game.Renderer.Fonts[nameLabel.Font];
|
||||
var nameSize = font.Measure(from);
|
||||
|
||||
var time = DateTime.Now;
|
||||
timeLabel.GetText = () => "{0:D2}:{1:D2}".F(time.Hour, time.Minute);
|
||||
|
||||
nameLabel.GetColor = () => c;
|
||||
nameLabel.GetText = () => name;
|
||||
nameLabel.Bounds.Width = nameSize.X;
|
||||
textLabel.Bounds.X += nameSize.X;
|
||||
textLabel.Bounds.Width -= nameSize.X;
|
||||
|
||||
// Hack around our hacky wordwrap behavior: need to resize the widget to fit the text
|
||||
text = WidgetUtils.WrapText(text, textLabel.Bounds.Width, font);
|
||||
textLabel.GetText = () => text;
|
||||
var dh = font.Measure(text).Y - textLabel.Bounds.Height;
|
||||
if (dh > 0)
|
||||
{
|
||||
textLabel.Bounds.Height += dh;
|
||||
template.Bounds.Height += dh;
|
||||
}
|
||||
|
||||
var scrolledToBottom = chatPanel.ScrolledToBottom;
|
||||
chatPanel.AddChild(template);
|
||||
if (scrolledToBottom)
|
||||
chatPanel.ScrollToBottom(smooth: true);
|
||||
|
||||
Sound.PlayNotification(modRules, null, "Sounds", "ChatLine", null);
|
||||
}
|
||||
|
||||
bool SwitchTeamChat()
|
||||
{
|
||||
teamChat ^= true;
|
||||
chatLabel.Text = teamChat ? "Team:" : "Chat:";
|
||||
return true;
|
||||
}
|
||||
|
||||
void UpdateCurrentMap()
|
||||
{
|
||||
var uid = orderManager.LobbyInfo.GlobalSettings.Map;
|
||||
if (Map.Uid == uid)
|
||||
return;
|
||||
|
||||
Map = Game.ModData.MapCache[uid];
|
||||
if (Map.Status == MapStatus.Available)
|
||||
{
|
||||
// Maps need to be validated and pre-loaded before they can be accessed
|
||||
new Thread(_ =>
|
||||
{
|
||||
var map = Map;
|
||||
map.CacheRules();
|
||||
Game.RunAfterTick(() =>
|
||||
{
|
||||
// Map may have changed in the meantime
|
||||
if (map != Map)
|
||||
return;
|
||||
|
||||
if (map.RuleStatus != MapRuleStatus.Invalid)
|
||||
{
|
||||
// Tell the server that we have the map
|
||||
orderManager.IssueOrder(Order.Command("state {0}".F(Session.ClientState.NotReady)));
|
||||
|
||||
// Restore default starting cash if the last map set it to something invalid
|
||||
var pri = modRules.Actors["player"].Traits.Get<PlayerResourcesInfo>();
|
||||
if (!Map.Map.Options.StartingCash.HasValue && !pri.SelectableCash.Contains(orderManager.LobbyInfo.GlobalSettings.StartingCash))
|
||||
orderManager.IssueOrder(Order.Command("startingcash {0}".F(pri.DefaultCash)));
|
||||
}
|
||||
});
|
||||
}).Start();
|
||||
}
|
||||
else if (Game.Settings.Game.AllowDownloading)
|
||||
Game.ModData.MapCache.QueryRemoteMapDetails(new[] { uid });
|
||||
}
|
||||
|
||||
void UpdatePlayerList()
|
||||
{
|
||||
var idx = 0;
|
||||
foreach (var kv in orderManager.LobbyInfo.Slots)
|
||||
{
|
||||
var key = kv.Key;
|
||||
var slot = kv.Value;
|
||||
var client = orderManager.LobbyInfo.ClientInSlot(key);
|
||||
Widget template = null;
|
||||
|
||||
// get template for possible reuse
|
||||
if (idx < players.Children.Count)
|
||||
template = players.Children[idx];
|
||||
|
||||
if (client == null)
|
||||
{
|
||||
// Empty slot
|
||||
if (template == null || template.Id != emptySlotTemplate.Id)
|
||||
template = emptySlotTemplate.Clone();
|
||||
|
||||
if (Game.IsHost)
|
||||
LobbyUtils.SetupEditableSlotWidget(template, slot, client, orderManager, modRules);
|
||||
else
|
||||
LobbyUtils.SetupSlotWidget(template, slot, client);
|
||||
|
||||
var join = template.Get<ButtonWidget>("JOIN");
|
||||
join.IsVisible = () => !slot.Closed;
|
||||
join.IsDisabled = () => orderManager.LocalClient.IsReady;
|
||||
join.OnClick = () => orderManager.IssueOrder(Order.Command("slot " + key));
|
||||
}
|
||||
else if ((client.Index == orderManager.LocalClient.Index) ||
|
||||
(client.Bot != null && Game.IsHost))
|
||||
{
|
||||
// Editable player in slot
|
||||
if (template == null || template.Id != editablePlayerTemplate.Id)
|
||||
template = editablePlayerTemplate.Clone();
|
||||
|
||||
LobbyUtils.SetupClientWidget(template, slot, client, orderManager, client.Bot == null);
|
||||
|
||||
if (client.Bot != null)
|
||||
LobbyUtils.SetupEditableSlotWidget(template, slot, client, orderManager, modRules);
|
||||
else
|
||||
LobbyUtils.SetupEditableNameWidget(template, slot, client, orderManager);
|
||||
|
||||
LobbyUtils.SetupEditableColorWidget(template, slot, client, orderManager, colorPreview);
|
||||
LobbyUtils.SetupEditableFactionWidget(template, slot, client, orderManager, countries);
|
||||
LobbyUtils.SetupEditableTeamWidget(template, slot, client, orderManager, Map);
|
||||
LobbyUtils.SetupEditableSpawnWidget(template, slot, client, orderManager, Map);
|
||||
LobbyUtils.SetupEditableReadyWidget(template, slot, client, orderManager, Map);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Non-editable player in slot
|
||||
if (template == null || template.Id != nonEditablePlayerTemplate.Id)
|
||||
template = nonEditablePlayerTemplate.Clone();
|
||||
|
||||
LobbyUtils.SetupClientWidget(template, slot, client, orderManager, client.Bot == null);
|
||||
LobbyUtils.SetupNameWidget(template, slot, client);
|
||||
LobbyUtils.SetupKickWidget(template, slot, client, orderManager, lobby,
|
||||
() => panel = PanelType.Kick, () => panel = PanelType.Players);
|
||||
LobbyUtils.SetupColorWidget(template, slot, client);
|
||||
LobbyUtils.SetupFactionWidget(template, slot, client, countries);
|
||||
LobbyUtils.SetupTeamWidget(template, slot, client);
|
||||
LobbyUtils.SetupSpawnWidget(template, slot, client);
|
||||
LobbyUtils.SetupReadyWidget(template, slot, client);
|
||||
}
|
||||
|
||||
template.IsVisible = () => true;
|
||||
|
||||
if (idx >= players.Children.Count)
|
||||
players.AddChild(template);
|
||||
else if (players.Children[idx].Id != template.Id)
|
||||
players.ReplaceChild(players.Children[idx], template);
|
||||
|
||||
idx++;
|
||||
}
|
||||
|
||||
// Add spectators
|
||||
foreach (var client in orderManager.LobbyInfo.Clients.Where(client => client.Slot == null))
|
||||
{
|
||||
Widget template = null;
|
||||
var c = client;
|
||||
|
||||
// get template for possible reuse
|
||||
if (idx < players.Children.Count)
|
||||
template = players.Children[idx];
|
||||
|
||||
// Editable spectator
|
||||
if (c.Index == orderManager.LocalClient.Index)
|
||||
{
|
||||
if (template == null || template.Id != editableSpectatorTemplate.Id)
|
||||
template = editableSpectatorTemplate.Clone();
|
||||
|
||||
LobbyUtils.SetupEditableNameWidget(template, null, c, orderManager);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Non-editable spectator
|
||||
if (template == null || template.Id != nonEditableSpectatorTemplate.Id)
|
||||
template = nonEditableSpectatorTemplate.Clone();
|
||||
|
||||
LobbyUtils.SetupNameWidget(template, null, client);
|
||||
LobbyUtils.SetupKickWidget(template, null, client, orderManager, lobby,
|
||||
() => panel = PanelType.Kick, () => panel = PanelType.Players);
|
||||
}
|
||||
|
||||
LobbyUtils.SetupClientWidget(template, null, c, orderManager, true);
|
||||
template.IsVisible = () => true;
|
||||
|
||||
if (idx >= players.Children.Count)
|
||||
players.AddChild(template);
|
||||
else if (players.Children[idx].Id != template.Id)
|
||||
players.ReplaceChild(players.Children[idx], template);
|
||||
|
||||
idx++;
|
||||
}
|
||||
|
||||
// Spectate button
|
||||
if (orderManager.LocalClient.Slot != null)
|
||||
{
|
||||
Widget spec = null;
|
||||
if (idx < players.Children.Count)
|
||||
spec = players.Children[idx];
|
||||
if (spec == null || spec.Id != newSpectatorTemplate.Id)
|
||||
spec = newSpectatorTemplate.Clone();
|
||||
|
||||
LobbyUtils.SetupKickSpectatorsWidget(spec, orderManager, lobby,
|
||||
() => panel = PanelType.Kick, () => panel = PanelType.Players, skirmishMode);
|
||||
|
||||
var btn = spec.Get<ButtonWidget>("SPECTATE");
|
||||
btn.OnClick = () => orderManager.IssueOrder(Order.Command("spectate"));
|
||||
btn.IsDisabled = () => orderManager.LocalClient.IsReady;
|
||||
btn.IsVisible = () => orderManager.LobbyInfo.GlobalSettings.AllowSpectators
|
||||
|| orderManager.LocalClient.IsAdmin;
|
||||
|
||||
spec.IsVisible = () => true;
|
||||
|
||||
if (idx >= players.Children.Count)
|
||||
players.AddChild(spec);
|
||||
else if (players.Children[idx].Id != spec.Id)
|
||||
players.ReplaceChild(players.Children[idx], spec);
|
||||
|
||||
idx++;
|
||||
}
|
||||
|
||||
while (players.Children.Count > idx)
|
||||
players.RemoveChild(players.Children[idx]);
|
||||
|
||||
tabCompletion.Names = orderManager.LobbyInfo.Clients.Select(c => c.Name).Distinct().ToList();
|
||||
}
|
||||
|
||||
void OnGameStart()
|
||||
{
|
||||
CloseWindow();
|
||||
onStart();
|
||||
}
|
||||
|
||||
class DropDownOption
|
||||
{
|
||||
public string Title;
|
||||
public Func<bool> IsSelected;
|
||||
public Action OnClick;
|
||||
}
|
||||
}
|
||||
|
||||
public class LobbyCountry
|
||||
{
|
||||
public string Name;
|
||||
public string Description;
|
||||
public string Side;
|
||||
}
|
||||
}
|
||||
162
OpenRA.Mods.Common/Widgets/Logic/Lobby/LobbyMapPreviewLogic.cs
Normal file
162
OpenRA.Mods.Common/Widgets/Logic/Lobby/LobbyMapPreviewLogic.cs
Normal file
@@ -0,0 +1,162 @@
|
||||
#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 OpenRA.Network;
|
||||
using OpenRA.Widgets;
|
||||
|
||||
namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
{
|
||||
public class LobbyMapPreviewLogic
|
||||
{
|
||||
[ObjectCreator.UseCtor]
|
||||
internal LobbyMapPreviewLogic(Widget widget, OrderManager orderManager, LobbyLogic lobby)
|
||||
{
|
||||
var available = widget.GetOrNull("MAP_AVAILABLE");
|
||||
if (available != null)
|
||||
{
|
||||
available.IsVisible = () => lobby.Map.Status == MapStatus.Available && lobby.Map.RuleStatus == MapRuleStatus.Cached;
|
||||
|
||||
var preview = available.Get<MapPreviewWidget>("MAP_PREVIEW");
|
||||
preview.Preview = () => lobby.Map;
|
||||
preview.OnMouseDown = mi => LobbyUtils.SelectSpawnPoint(orderManager, preview, lobby.Map, mi);
|
||||
preview.SpawnOccupants = () => LobbyUtils.GetSpawnOccupants(orderManager.LobbyInfo, lobby.Map);
|
||||
|
||||
var title = available.GetOrNull<LabelWidget>("MAP_TITLE");
|
||||
if (title != null)
|
||||
title.GetText = () => lobby.Map.Title;
|
||||
|
||||
var type = available.GetOrNull<LabelWidget>("MAP_TYPE");
|
||||
if (type != null)
|
||||
type.GetText = () => lobby.Map.Type;
|
||||
|
||||
var author = available.GetOrNull<LabelWidget>("MAP_AUTHOR");
|
||||
if (author != null)
|
||||
author.GetText = () => "Created by {0}".F(lobby.Map.Author);
|
||||
}
|
||||
|
||||
var invalid = widget.GetOrNull("MAP_INVALID");
|
||||
if (invalid != null)
|
||||
{
|
||||
invalid.IsVisible = () => lobby.Map.Status == MapStatus.Available && lobby.Map.RuleStatus == MapRuleStatus.Invalid;
|
||||
|
||||
var preview = invalid.Get<MapPreviewWidget>("MAP_PREVIEW");
|
||||
preview.Preview = () => lobby.Map;
|
||||
preview.OnMouseDown = mi => LobbyUtils.SelectSpawnPoint(orderManager, preview, lobby.Map, mi);
|
||||
preview.SpawnOccupants = () => LobbyUtils.GetSpawnOccupants(orderManager.LobbyInfo, lobby.Map);
|
||||
|
||||
var title = invalid.GetOrNull<LabelWidget>("MAP_TITLE");
|
||||
if (title != null)
|
||||
title.GetText = () => lobby.Map.Title;
|
||||
|
||||
var type = invalid.GetOrNull<LabelWidget>("MAP_TYPE");
|
||||
if (type != null)
|
||||
type.GetText = () => lobby.Map.Type;
|
||||
}
|
||||
|
||||
var download = widget.GetOrNull("MAP_DOWNLOADABLE");
|
||||
if (download != null)
|
||||
{
|
||||
download.IsVisible = () => lobby.Map.Status == MapStatus.DownloadAvailable;
|
||||
|
||||
var preview = download.Get<MapPreviewWidget>("MAP_PREVIEW");
|
||||
preview.Preview = () => lobby.Map;
|
||||
preview.OnMouseDown = mi => LobbyUtils.SelectSpawnPoint(orderManager, preview, lobby.Map, mi);
|
||||
preview.SpawnOccupants = () => LobbyUtils.GetSpawnOccupants(orderManager.LobbyInfo, lobby.Map);
|
||||
|
||||
var title = download.GetOrNull<LabelWidget>("MAP_TITLE");
|
||||
if (title != null)
|
||||
title.GetText = () => lobby.Map.Title;
|
||||
|
||||
var type = download.GetOrNull<LabelWidget>("MAP_TYPE");
|
||||
if (type != null)
|
||||
type.GetText = () => lobby.Map.Type;
|
||||
|
||||
var author = download.GetOrNull<LabelWidget>("MAP_AUTHOR");
|
||||
if (author != null)
|
||||
author.GetText = () => "Created by {0}".F(lobby.Map.Author);
|
||||
|
||||
var install = download.GetOrNull<ButtonWidget>("MAP_INSTALL");
|
||||
if (install != null)
|
||||
install.OnClick = () => lobby.Map.Install();
|
||||
}
|
||||
|
||||
var progress = widget.GetOrNull("MAP_PROGRESS");
|
||||
if (progress != null)
|
||||
{
|
||||
progress.IsVisible = () => (lobby.Map.Status != MapStatus.Available || lobby.Map.RuleStatus == MapRuleStatus.Unknown) && lobby.Map.Status != MapStatus.DownloadAvailable;
|
||||
|
||||
var preview = progress.Get<MapPreviewWidget>("MAP_PREVIEW");
|
||||
preview.Preview = () => lobby.Map;
|
||||
preview.OnMouseDown = mi => LobbyUtils.SelectSpawnPoint(orderManager, preview, lobby.Map, mi);
|
||||
preview.SpawnOccupants = () => LobbyUtils.GetSpawnOccupants(orderManager.LobbyInfo, lobby.Map);
|
||||
|
||||
var title = progress.GetOrNull<LabelWidget>("MAP_TITLE");
|
||||
if (title != null)
|
||||
title.GetText = () => lobby.Map.Title;
|
||||
|
||||
var type = progress.GetOrNull<LabelWidget>("MAP_TYPE");
|
||||
if (type != null)
|
||||
type.GetText = () => lobby.Map.Type;
|
||||
|
||||
var statusSearching = progress.GetOrNull("MAP_STATUS_SEARCHING");
|
||||
if (statusSearching != null)
|
||||
statusSearching.IsVisible = () => lobby.Map.Status == MapStatus.Searching;
|
||||
|
||||
var statusUnavailable = progress.GetOrNull("MAP_STATUS_UNAVAILABLE");
|
||||
if (statusUnavailable != null)
|
||||
statusUnavailable.IsVisible = () => lobby.Map.Status == MapStatus.Unavailable;
|
||||
|
||||
var statusError = progress.GetOrNull("MAP_STATUS_ERROR");
|
||||
if (statusError != null)
|
||||
statusError.IsVisible = () => lobby.Map.Status == MapStatus.DownloadError;
|
||||
|
||||
var statusDownloading = progress.GetOrNull<LabelWidget>("MAP_STATUS_DOWNLOADING");
|
||||
if (statusDownloading != null)
|
||||
{
|
||||
statusDownloading.IsVisible = () => lobby.Map.Status == MapStatus.Downloading;
|
||||
statusDownloading.GetText = () =>
|
||||
{
|
||||
if (lobby.Map.DownloadBytes == 0)
|
||||
return "Connecting...";
|
||||
|
||||
// Server does not provide the total file length
|
||||
if (lobby.Map.DownloadPercentage == 0)
|
||||
return "Downloading {0} kB".F(lobby.Map.DownloadBytes / 1024);
|
||||
|
||||
return "Downloading {0} kB ({1}%)".F(lobby.Map.DownloadBytes / 1024, lobby.Map.DownloadPercentage);
|
||||
};
|
||||
}
|
||||
|
||||
var retry = progress.GetOrNull<ButtonWidget>("MAP_RETRY");
|
||||
if (retry != null)
|
||||
{
|
||||
retry.IsVisible = () => (lobby.Map.Status == MapStatus.DownloadError || lobby.Map.Status == MapStatus.Unavailable) && lobby.Map != MapCache.UnknownMap;
|
||||
retry.OnClick = () =>
|
||||
{
|
||||
if (lobby.Map.Status == MapStatus.DownloadError)
|
||||
lobby.Map.Install();
|
||||
else if (lobby.Map.Status == MapStatus.Unavailable)
|
||||
Game.ModData.MapCache.QueryRemoteMapDetails(new[] { lobby.Map.Uid });
|
||||
};
|
||||
|
||||
retry.GetText = () => lobby.Map.Status == MapStatus.DownloadError ? "Retry Install" : "Retry Search";
|
||||
}
|
||||
|
||||
var progressbar = progress.GetOrNull<ProgressBarWidget>("MAP_PROGRESSBAR");
|
||||
if (progressbar != null)
|
||||
{
|
||||
progressbar.IsIndeterminate = () => lobby.Map.DownloadPercentage == 0;
|
||||
progressbar.GetPercentage = () => lobby.Map.DownloadPercentage;
|
||||
progressbar.IsVisible = () => !retry.IsVisible();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
475
OpenRA.Mods.Common/Widgets/Logic/Lobby/LobbyUtils.cs
Normal file
475
OpenRA.Mods.Common/Widgets/Logic/Lobby/LobbyUtils.cs
Normal file
@@ -0,0 +1,475 @@
|
||||
#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.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Network;
|
||||
using OpenRA.Primitives;
|
||||
using OpenRA.Traits;
|
||||
using OpenRA.Widgets;
|
||||
|
||||
namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
{
|
||||
public static class LobbyUtils
|
||||
{
|
||||
class SlotDropDownOption
|
||||
{
|
||||
public string Title;
|
||||
public string Order;
|
||||
public Func<bool> Selected;
|
||||
|
||||
public SlotDropDownOption(string title, string order, Func<bool> selected)
|
||||
{
|
||||
Title = title;
|
||||
Order = order;
|
||||
Selected = selected;
|
||||
}
|
||||
}
|
||||
|
||||
public static void ShowSlotDropDown(Ruleset rules, DropDownButtonWidget dropdown, Session.Slot slot,
|
||||
Session.Client client, OrderManager orderManager)
|
||||
{
|
||||
var options = new Dictionary<string, IEnumerable<SlotDropDownOption>>() { { "Slot", new List<SlotDropDownOption>()
|
||||
{
|
||||
new SlotDropDownOption("Open", "slot_open " + slot.PlayerReference, () => (!slot.Closed && client == null)),
|
||||
new SlotDropDownOption("Closed", "slot_close " + slot.PlayerReference, () => slot.Closed)
|
||||
} } };
|
||||
|
||||
var bots = new List<SlotDropDownOption>();
|
||||
if (slot.AllowBots)
|
||||
{
|
||||
foreach (var b in rules.Actors["player"].Traits.WithInterface<IBotInfo>().Select(t => t.Name))
|
||||
{
|
||||
var bot = b;
|
||||
var botController = orderManager.LobbyInfo.Clients.FirstOrDefault(c => c.IsAdmin);
|
||||
bots.Add(new SlotDropDownOption(bot,
|
||||
"slot_bot {0} {1} {2}".F(slot.PlayerReference, botController.Index, bot),
|
||||
() => client != null && client.Bot == bot));
|
||||
}
|
||||
}
|
||||
|
||||
options.Add(bots.Any() ? "Bots" : "Bots Disabled", bots);
|
||||
|
||||
Func<SlotDropDownOption, ScrollItemWidget, ScrollItemWidget> setupItem = (o, itemTemplate) =>
|
||||
{
|
||||
var item = ScrollItemWidget.Setup(itemTemplate,
|
||||
o.Selected,
|
||||
() => orderManager.IssueOrder(Order.Command(o.Order)));
|
||||
item.Get<LabelWidget>("LABEL").GetText = () => o.Title;
|
||||
return item;
|
||||
};
|
||||
|
||||
dropdown.ShowDropDown<SlotDropDownOption>("LABEL_DROPDOWN_TEMPLATE", 167, options, setupItem);
|
||||
}
|
||||
|
||||
public static void ShowTeamDropDown(DropDownButtonWidget dropdown, Session.Client client,
|
||||
OrderManager orderManager, int teamCount)
|
||||
{
|
||||
Func<int, ScrollItemWidget, ScrollItemWidget> setupItem = (ii, itemTemplate) =>
|
||||
{
|
||||
var item = ScrollItemWidget.Setup(itemTemplate,
|
||||
() => client.Team == ii,
|
||||
() => orderManager.IssueOrder(Order.Command("team {0} {1}".F(client.Index, ii))));
|
||||
item.Get<LabelWidget>("LABEL").GetText = () => ii == 0 ? "-" : ii.ToString();
|
||||
return item;
|
||||
};
|
||||
|
||||
var options = Enumerable.Range(0, teamCount + 1);
|
||||
dropdown.ShowDropDown("TEAM_DROPDOWN_TEMPLATE", 150, options, setupItem);
|
||||
}
|
||||
|
||||
public static void ShowSpawnDropDown(DropDownButtonWidget dropdown, Session.Client client,
|
||||
OrderManager orderManager, IEnumerable<int> spawnPoints)
|
||||
{
|
||||
Func<int, ScrollItemWidget, ScrollItemWidget> setupItem = (ii, itemTemplate) =>
|
||||
{
|
||||
var item = ScrollItemWidget.Setup(itemTemplate,
|
||||
() => client.SpawnPoint == ii,
|
||||
() => SetSpawnPoint(orderManager, client, ii));
|
||||
item.Get<LabelWidget>("LABEL").GetText = () => ii == 0 ? "-" : Convert.ToChar('A' - 1 + ii).ToString();
|
||||
return item;
|
||||
};
|
||||
|
||||
dropdown.ShowDropDown("SPAWN_DROPDOWN_TEMPLATE", 150, spawnPoints, setupItem);
|
||||
}
|
||||
|
||||
public static void ShowRaceDropDown(DropDownButtonWidget dropdown, Session.Client client,
|
||||
OrderManager orderManager, Dictionary<string, LobbyCountry> countries)
|
||||
{
|
||||
Func<string, ScrollItemWidget, ScrollItemWidget> setupItem = (race, itemTemplate) =>
|
||||
{
|
||||
var item = ScrollItemWidget.Setup(itemTemplate,
|
||||
() => client.Country == race,
|
||||
() => orderManager.IssueOrder(Order.Command("race {0} {1}".F(client.Index, race))));
|
||||
var country = countries[race];
|
||||
item.Get<LabelWidget>("LABEL").GetText = () => country.Name;
|
||||
var flag = item.Get<ImageWidget>("FLAG");
|
||||
flag.GetImageCollection = () => "flags";
|
||||
flag.GetImageName = () => race;
|
||||
item.GetTooltipText = () => country.Description;
|
||||
return item;
|
||||
};
|
||||
|
||||
var options = countries.GroupBy(c => c.Value.Side).ToDictionary(g => g.Key ?? "", g => g.Select(c => c.Key));
|
||||
|
||||
dropdown.ShowDropDown("RACE_DROPDOWN_TEMPLATE", 150, options, setupItem);
|
||||
}
|
||||
|
||||
public static void ShowColorDropDown(DropDownButtonWidget color, Session.Client client,
|
||||
OrderManager orderManager, ColorPreviewManagerWidget preview)
|
||||
{
|
||||
Action onExit = () =>
|
||||
{
|
||||
if (client.Bot == null)
|
||||
{
|
||||
Game.Settings.Player.Color = preview.Color;
|
||||
Game.Settings.Save();
|
||||
}
|
||||
|
||||
color.RemovePanel();
|
||||
orderManager.IssueOrder(Order.Command("color {0} {1}".F(client.Index, preview.Color)));
|
||||
};
|
||||
|
||||
Action<HSLColor> onChange = c => preview.Color = c;
|
||||
|
||||
var colorChooser = Game.LoadWidget(orderManager.World, "COLOR_CHOOSER", null, new WidgetArgs()
|
||||
{
|
||||
{ "onChange", onChange },
|
||||
{ "initialColor", client.Color }
|
||||
});
|
||||
|
||||
color.AttachPanel(colorChooser, onExit);
|
||||
}
|
||||
|
||||
public static Dictionary<CPos, SpawnOccupant> GetSpawnOccupants(Session lobbyInfo, MapPreview preview)
|
||||
{
|
||||
var spawns = preview.SpawnPoints;
|
||||
return lobbyInfo.Clients
|
||||
.Where(c => (c.SpawnPoint - 1 >= 0) && (c.SpawnPoint - 1 < spawns.Count))
|
||||
.ToDictionary(c => spawns[c.SpawnPoint - 1], c => new SpawnOccupant(c));
|
||||
}
|
||||
|
||||
public static Dictionary<CPos, SpawnOccupant> GetSpawnOccupants(IEnumerable<GameInformation.Player> players, MapPreview preview)
|
||||
{
|
||||
var spawns = preview.SpawnPoints;
|
||||
return players
|
||||
.Where(c => (c.SpawnPoint - 1 >= 0) && (c.SpawnPoint - 1 < spawns.Count))
|
||||
.ToDictionary(c => spawns[c.SpawnPoint - 1], c => new SpawnOccupant(c));
|
||||
}
|
||||
|
||||
public static void SelectSpawnPoint(OrderManager orderManager, MapPreviewWidget mapPreview, MapPreview preview, MouseInput mi)
|
||||
{
|
||||
if (mi.Button != MouseButton.Left)
|
||||
return;
|
||||
|
||||
if (!orderManager.LocalClient.IsObserver && orderManager.LocalClient.State == Session.ClientState.Ready)
|
||||
return;
|
||||
|
||||
var spawnSize = new float2(ChromeProvider.GetImage("lobby-bits", "spawn-unclaimed").Bounds.Size);
|
||||
var selectedSpawn = preview.SpawnPoints
|
||||
.Select((sp, i) => Pair.New(mapPreview.ConvertToPreview(sp), i))
|
||||
.Where(a => ((a.First - mi.Location).ToFloat2() / spawnSize * 2).LengthSquared <= 1)
|
||||
.Select(a => a.Second + 1)
|
||||
.FirstOrDefault();
|
||||
|
||||
var locals = orderManager.LobbyInfo.Clients.Where(c => c.Index == orderManager.LocalClient.Index || (Game.IsHost && c.Bot != null));
|
||||
var playerToMove = locals.FirstOrDefault(c => ((selectedSpawn == 0) ^ (c.SpawnPoint == 0) && !c.IsObserver));
|
||||
SetSpawnPoint(orderManager, playerToMove, selectedSpawn);
|
||||
}
|
||||
|
||||
private static void SetSpawnPoint(OrderManager orderManager, Session.Client playerToMove, int selectedSpawn)
|
||||
{
|
||||
var owned = orderManager.LobbyInfo.Clients.Any(c => c.SpawnPoint == selectedSpawn);
|
||||
if (selectedSpawn == 0 || !owned)
|
||||
orderManager.IssueOrder(Order.Command("spawn {0} {1}".F((playerToMove ?? orderManager.LocalClient).Index, selectedSpawn)));
|
||||
}
|
||||
|
||||
public static Color LatencyColor(Session.ClientPing ping)
|
||||
{
|
||||
if (ping == null)
|
||||
return Color.Gray;
|
||||
|
||||
// Levels set relative to the default order lag of 3 net ticks (360ms)
|
||||
// TODO: Adjust this once dynamic lag is implemented
|
||||
if (ping.Latency < 0)
|
||||
return Color.Gray;
|
||||
if (ping.Latency < 300)
|
||||
return Color.LimeGreen;
|
||||
if (ping.Latency < 600)
|
||||
return Color.Orange;
|
||||
return Color.Red;
|
||||
}
|
||||
|
||||
public static string LatencyDescription(Session.ClientPing ping)
|
||||
{
|
||||
if (ping == null)
|
||||
return "Unknown";
|
||||
|
||||
if (ping.Latency < 0)
|
||||
return "Unknown";
|
||||
if (ping.Latency < 300)
|
||||
return "Good";
|
||||
if (ping.Latency < 600)
|
||||
return "Moderate";
|
||||
return "Poor";
|
||||
}
|
||||
|
||||
public static string DescriptiveIpAddress(string ip)
|
||||
{
|
||||
if (ip == null)
|
||||
return "Unknown Host";
|
||||
if (ip == IPAddress.Loopback.ToString())
|
||||
return "Local Host";
|
||||
return ip;
|
||||
}
|
||||
|
||||
public static string LookupCountry(string ip)
|
||||
{
|
||||
try
|
||||
{
|
||||
return Game.GeoIpDatabase.Country(ip).Country.Name;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Write("geoip", "LookupCountry failed: {0}", e);
|
||||
return "Unknown Location";
|
||||
}
|
||||
}
|
||||
|
||||
public static void SetupClientWidget(Widget parent, Session.Slot s, Session.Client c, OrderManager orderManager, bool visible)
|
||||
{
|
||||
parent.Get("ADMIN_INDICATOR").IsVisible = () => c.IsAdmin;
|
||||
var block = parent.Get("LATENCY");
|
||||
block.IsVisible = () => visible;
|
||||
|
||||
if (visible)
|
||||
block.Get<ColorBlockWidget>("LATENCY_COLOR").GetColor = () => LatencyColor(
|
||||
orderManager.LobbyInfo.PingFromClient(c));
|
||||
|
||||
var tooltip = parent.Get<ClientTooltipRegionWidget>("CLIENT_REGION");
|
||||
tooltip.IsVisible = () => visible;
|
||||
tooltip.Bind(orderManager, c.Index);
|
||||
}
|
||||
|
||||
public static void SetupEditableNameWidget(Widget parent, Session.Slot s, Session.Client c, OrderManager orderManager)
|
||||
{
|
||||
var name = parent.Get<TextFieldWidget>("NAME");
|
||||
name.IsVisible = () => true;
|
||||
name.IsDisabled = () => orderManager.LocalClient.IsReady;
|
||||
|
||||
name.Text = c.Name;
|
||||
name.OnLoseFocus = () =>
|
||||
{
|
||||
name.Text = name.Text.Trim();
|
||||
if (name.Text.Length == 0)
|
||||
name.Text = c.Name;
|
||||
|
||||
if (name.Text == c.Name)
|
||||
return;
|
||||
|
||||
orderManager.IssueOrder(Order.Command("name " + name.Text));
|
||||
Game.Settings.Player.Name = name.Text;
|
||||
Game.Settings.Save();
|
||||
};
|
||||
|
||||
name.OnEnterKey = () => { name.YieldKeyboardFocus(); return true; };
|
||||
}
|
||||
|
||||
public static void SetupNameWidget(Widget parent, Session.Slot s, Session.Client c)
|
||||
{
|
||||
var name = parent.Get<LabelWidget>("NAME");
|
||||
name.GetText = () => c.Name;
|
||||
}
|
||||
|
||||
public static void SetupEditableSlotWidget(Widget parent, Session.Slot s, Session.Client c, OrderManager orderManager, Ruleset rules)
|
||||
{
|
||||
var slot = parent.Get<DropDownButtonWidget>("SLOT_OPTIONS");
|
||||
slot.IsVisible = () => true;
|
||||
slot.IsDisabled = () => orderManager.LocalClient.IsReady;
|
||||
slot.GetText = () => c != null ? c.Name : s.Closed ? "Closed" : "Open";
|
||||
slot.OnMouseDown = _ => ShowSlotDropDown(rules, slot, s, c, orderManager);
|
||||
|
||||
// Ensure Name selector (if present) is hidden
|
||||
var name = parent.GetOrNull("NAME");
|
||||
if (name != null)
|
||||
name.IsVisible = () => false;
|
||||
}
|
||||
|
||||
public static void SetupSlotWidget(Widget parent, Session.Slot s, Session.Client c)
|
||||
{
|
||||
var name = parent.Get<LabelWidget>("NAME");
|
||||
name.IsVisible = () => true;
|
||||
name.GetText = () => c != null ? c.Name : s.Closed ? "Closed" : "Open";
|
||||
|
||||
// Ensure Slot selector (if present) is hidden
|
||||
var slot = parent.GetOrNull("SLOT_OPTIONS");
|
||||
if (slot != null)
|
||||
slot.IsVisible = () => false;
|
||||
}
|
||||
|
||||
public static void SetupKickWidget(Widget parent, Session.Slot s, Session.Client c, OrderManager orderManager, Widget lobby, Action before, Action after)
|
||||
{
|
||||
var button = parent.Get<ButtonWidget>("KICK");
|
||||
button.IsVisible = () => Game.IsHost && c.Index != orderManager.LocalClient.Index;
|
||||
button.IsDisabled = () => orderManager.LocalClient.IsReady;
|
||||
Action<bool> okPressed = tempBan => { orderManager.IssueOrder(Order.Command("kick {0} {1}".F(c.Index, tempBan))); after(); };
|
||||
button.OnClick = () =>
|
||||
{
|
||||
before();
|
||||
|
||||
Game.LoadWidget(null, "KICK_CLIENT_DIALOG", lobby, new WidgetArgs
|
||||
{
|
||||
{ "clientName", c.Name },
|
||||
{ "okPressed", okPressed },
|
||||
{ "cancelPressed", after }
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
public static void SetupKickSpectatorsWidget(Widget parent, OrderManager orderManager, Widget lobby, Action before, Action after, bool skirmishMode)
|
||||
{
|
||||
var checkBox = parent.Get<CheckboxWidget>("TOGGLE_SPECTATORS");
|
||||
checkBox.IsChecked = () => orderManager.LobbyInfo.GlobalSettings.AllowSpectators;
|
||||
checkBox.IsVisible = () => orderManager.LocalClient.IsAdmin && !skirmishMode;
|
||||
checkBox.IsDisabled = () => false;
|
||||
|
||||
Action okPressed = () =>
|
||||
{
|
||||
orderManager.IssueOrder(Order.Command("allow_spectators {0}".F(!orderManager.LobbyInfo.GlobalSettings.AllowSpectators)));
|
||||
orderManager.IssueOrders(
|
||||
orderManager.LobbyInfo.Clients.Where(
|
||||
c => c.IsObserver && !c.IsAdmin).Select(
|
||||
client => Order.Command("kick {0} {1}".F(client.Index, client.Name))).ToArray());
|
||||
|
||||
after();
|
||||
};
|
||||
|
||||
checkBox.OnClick = () =>
|
||||
{
|
||||
before();
|
||||
|
||||
var spectatorCount = orderManager.LobbyInfo.Clients.Count(c => c.IsObserver);
|
||||
if (spectatorCount > 0)
|
||||
{
|
||||
Game.LoadWidget(null, "KICK_SPECTATORS_DIALOG", lobby, new WidgetArgs
|
||||
{
|
||||
{ "clientCount", "{0}".F(spectatorCount) },
|
||||
{ "okPressed", okPressed },
|
||||
{ "cancelPressed", after }
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
orderManager.IssueOrder(Order.Command("allow_spectators {0}".F(!orderManager.LobbyInfo.GlobalSettings.AllowSpectators)));
|
||||
after();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static void SetupEditableColorWidget(Widget parent, Session.Slot s, Session.Client c, OrderManager orderManager, ColorPreviewManagerWidget colorPreview)
|
||||
{
|
||||
var color = parent.Get<DropDownButtonWidget>("COLOR");
|
||||
color.IsDisabled = () => (s != null && s.LockColor) || orderManager.LocalClient.IsReady;
|
||||
color.OnMouseDown = _ => ShowColorDropDown(color, c, orderManager, colorPreview);
|
||||
|
||||
SetupColorWidget(color, s, c);
|
||||
}
|
||||
|
||||
public static void SetupColorWidget(Widget parent, Session.Slot s, Session.Client c)
|
||||
{
|
||||
var color = parent.Get<ColorBlockWidget>("COLORBLOCK");
|
||||
color.GetColor = () => c.Color.RGB;
|
||||
}
|
||||
|
||||
public static void SetupEditableFactionWidget(Widget parent, Session.Slot s, Session.Client c, OrderManager orderManager,
|
||||
Dictionary<string, LobbyCountry> countries)
|
||||
{
|
||||
var dropdown = parent.Get<DropDownButtonWidget>("FACTION");
|
||||
dropdown.IsDisabled = () => s.LockRace || orderManager.LocalClient.IsReady;
|
||||
dropdown.OnMouseDown = _ => ShowRaceDropDown(dropdown, c, orderManager, countries);
|
||||
var factionDescription = countries[c.Country].Description;
|
||||
dropdown.GetTooltipText = () => factionDescription;
|
||||
SetupFactionWidget(dropdown, s, c, countries);
|
||||
}
|
||||
|
||||
public static void SetupFactionWidget(Widget parent, Session.Slot s, Session.Client c,
|
||||
Dictionary<string, LobbyCountry> countries)
|
||||
{
|
||||
var factionName = parent.Get<LabelWidget>("FACTIONNAME");
|
||||
factionName.GetText = () => countries[c.Country].Name;
|
||||
var factionFlag = parent.Get<ImageWidget>("FACTIONFLAG");
|
||||
factionFlag.GetImageName = () => c.Country;
|
||||
factionFlag.GetImageCollection = () => "flags";
|
||||
}
|
||||
|
||||
public static void SetupEditableTeamWidget(Widget parent, Session.Slot s, Session.Client c, OrderManager orderManager, MapPreview map)
|
||||
{
|
||||
var dropdown = parent.Get<DropDownButtonWidget>("TEAM");
|
||||
dropdown.IsDisabled = () => s.LockTeam || orderManager.LocalClient.IsReady;
|
||||
dropdown.OnMouseDown = _ => ShowTeamDropDown(dropdown, c, orderManager, map.PlayerCount);
|
||||
dropdown.GetText = () => (c.Team == 0) ? "-" : c.Team.ToString();
|
||||
}
|
||||
|
||||
public static void SetupTeamWidget(Widget parent, Session.Slot s, Session.Client c)
|
||||
{
|
||||
parent.Get<LabelWidget>("TEAM").GetText = () => (c.Team == 0) ? "-" : c.Team.ToString();
|
||||
}
|
||||
|
||||
public static void SetupEditableSpawnWidget(Widget parent, Session.Slot s, Session.Client c, OrderManager orderManager, MapPreview map)
|
||||
{
|
||||
var dropdown = parent.Get<DropDownButtonWidget>("SPAWN");
|
||||
dropdown.IsDisabled = () => s.LockSpawn || orderManager.LocalClient.IsReady;
|
||||
dropdown.OnMouseDown = _ => ShowSpawnDropDown(dropdown, c, orderManager, Enumerable.Range(0, map.SpawnPoints.Count + 1).Except(orderManager.LobbyInfo.Clients.Where(client => client != c && client.SpawnPoint != 0).Select(client => client.SpawnPoint)));
|
||||
dropdown.GetText = () => (c.SpawnPoint == 0) ? "-" : Convert.ToChar('A' - 1 + c.SpawnPoint).ToString();
|
||||
}
|
||||
|
||||
public static void SetupSpawnWidget(Widget parent, Session.Slot s, Session.Client c)
|
||||
{
|
||||
parent.Get<LabelWidget>("SPAWN").GetText = () => (c.SpawnPoint == 0) ? "-" : Convert.ToChar('A' - 1 + c.SpawnPoint).ToString();
|
||||
}
|
||||
|
||||
public static void SetupEditableReadyWidget(Widget parent, Session.Slot s, Session.Client c, OrderManager orderManager, MapPreview map)
|
||||
{
|
||||
var status = parent.Get<CheckboxWidget>("STATUS_CHECKBOX");
|
||||
status.IsChecked = () => orderManager.LocalClient.IsReady || c.Bot != null;
|
||||
status.IsVisible = () => true;
|
||||
status.IsDisabled = () => c.Bot != null || map.Status != MapStatus.Available || map.RuleStatus != MapRuleStatus.Cached;
|
||||
|
||||
var state = orderManager.LocalClient.IsReady ? Session.ClientState.NotReady : Session.ClientState.Ready;
|
||||
status.OnClick = () => orderManager.IssueOrder(Order.Command("state {0}".F(state)));
|
||||
}
|
||||
|
||||
public static void SetupReadyWidget(Widget parent, Session.Slot s, Session.Client c)
|
||||
{
|
||||
parent.Get<ImageWidget>("STATUS_IMAGE").IsVisible = () => c.IsReady || c.Bot != null;
|
||||
}
|
||||
|
||||
public static void AddPlayerFlagAndName(ScrollItemWidget template, Player player)
|
||||
{
|
||||
var flag = template.Get<ImageWidget>("FLAG");
|
||||
flag.GetImageName = () => player.Country.Race;
|
||||
flag.GetImageCollection = () => "flags";
|
||||
|
||||
var playerName = template.Get<LabelWidget>("PLAYER");
|
||||
var client = player.World.LobbyInfo.ClientWithIndex(player.ClientIndex);
|
||||
playerName.GetText = () =>
|
||||
{
|
||||
if (client != null && client.State == Network.Session.ClientState.Disconnected)
|
||||
return player.PlayerName + " (Gone)";
|
||||
return player.PlayerName + (player.WinState == WinState.Undefined ? "" : " (" + player.WinState + ")");
|
||||
};
|
||||
playerName.GetColor = () => player.Color.RGB;
|
||||
}
|
||||
}
|
||||
}
|
||||
166
OpenRA.Mods.Common/Widgets/Logic/MapChooserLogic.cs
Normal file
166
OpenRA.Mods.Common/Widgets/Logic/MapChooserLogic.cs
Normal file
@@ -0,0 +1,166 @@
|
||||
#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.Generic;
|
||||
using System.Linq;
|
||||
using OpenRA.Primitives;
|
||||
using OpenRA.Widgets;
|
||||
|
||||
namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
{
|
||||
public class MapChooserLogic
|
||||
{
|
||||
string selectedUid;
|
||||
|
||||
// May be a subset of available maps if a mode filter is active
|
||||
List<string> visibleMaps;
|
||||
|
||||
ScrollPanelWidget scrollpanel;
|
||||
ScrollItemWidget itemTemplate;
|
||||
string mapFilter;
|
||||
string gameMode;
|
||||
|
||||
[ObjectCreator.UseCtor]
|
||||
internal MapChooserLogic(Widget widget, string initialMap, Action onExit, Action<string> onSelect, MapVisibility filter)
|
||||
{
|
||||
selectedUid = WidgetUtils.ChooseInitialMap(initialMap);
|
||||
|
||||
var approving = new Action(() => { Ui.CloseWindow(); onSelect(selectedUid); });
|
||||
var canceling = new Action(() => { Ui.CloseWindow(); onExit(); });
|
||||
|
||||
widget.Get<ButtonWidget>("BUTTON_OK").OnClick = approving;
|
||||
widget.Get<ButtonWidget>("BUTTON_CANCEL").OnClick = canceling;
|
||||
|
||||
scrollpanel = widget.Get<ScrollPanelWidget>("MAP_LIST");
|
||||
scrollpanel.Layout = new GridLayout(scrollpanel);
|
||||
|
||||
itemTemplate = scrollpanel.Get<ScrollItemWidget>("MAP_TEMPLATE");
|
||||
|
||||
var gameModeDropdown = widget.GetOrNull<DropDownButtonWidget>("GAMEMODE_FILTER");
|
||||
if (gameModeDropdown != null)
|
||||
{
|
||||
var selectableMaps = Game.ModData.MapCache.Where(m => m.Status == MapStatus.Available && (m.Map.Visibility & filter) != 0);
|
||||
var gameModes = selectableMaps
|
||||
.GroupBy(m => m.Type)
|
||||
.Select(g => Pair.New(g.Key, g.Count())).ToList();
|
||||
|
||||
// 'all game types' extra item
|
||||
gameModes.Insert(0, Pair.New(null as string, selectableMaps.Count()));
|
||||
|
||||
Func<Pair<string, int>, string> showItem =
|
||||
x => "{0} ({1})".F(x.First ?? "All Game Types", x.Second);
|
||||
|
||||
Func<Pair<string, int>, ScrollItemWidget, ScrollItemWidget> setupItem = (ii, template) =>
|
||||
{
|
||||
var item = ScrollItemWidget.Setup(template,
|
||||
() => gameMode == ii.First,
|
||||
() => { gameMode = ii.First; EnumerateMaps(onSelect, filter); });
|
||||
item.Get<LabelWidget>("LABEL").GetText = () => showItem(ii);
|
||||
return item;
|
||||
};
|
||||
|
||||
gameModeDropdown.OnClick = () =>
|
||||
gameModeDropdown.ShowDropDown("LABEL_DROPDOWN_TEMPLATE", 210, gameModes, setupItem);
|
||||
|
||||
gameModeDropdown.GetText = () => showItem(gameModes.First(m => m.First == gameMode));
|
||||
}
|
||||
|
||||
var mapfilterInput = widget.GetOrNull<TextFieldWidget>("MAPFILTER_INPUT");
|
||||
if (mapfilterInput != null)
|
||||
{
|
||||
mapfilterInput.TakeKeyboardFocus();
|
||||
mapfilterInput.OnEscKey = () =>
|
||||
{
|
||||
if (mapfilterInput.Text.Length == 0)
|
||||
canceling();
|
||||
else
|
||||
{
|
||||
mapFilter = mapfilterInput.Text = null;
|
||||
EnumerateMaps(onSelect, filter);
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
mapfilterInput.OnEnterKey = () => { approving(); return true; };
|
||||
mapfilterInput.OnTextEdited = () =>
|
||||
{ mapFilter = mapfilterInput.Text; EnumerateMaps(onSelect, filter); };
|
||||
}
|
||||
|
||||
var randomMapButton = widget.GetOrNull<ButtonWidget>("RANDOMMAP_BUTTON");
|
||||
if (randomMapButton != null)
|
||||
{
|
||||
randomMapButton.OnClick = () =>
|
||||
{
|
||||
var uid = visibleMaps.Random(Game.CosmeticRandom);
|
||||
selectedUid = uid;
|
||||
scrollpanel.ScrollToItem(uid, smooth: true);
|
||||
};
|
||||
randomMapButton.IsDisabled = () => visibleMaps == null || visibleMaps.Count == 0;
|
||||
}
|
||||
|
||||
EnumerateMaps(onSelect, filter);
|
||||
}
|
||||
|
||||
void EnumerateMaps(Action<string> onSelect, MapVisibility filter)
|
||||
{
|
||||
var maps = Game.ModData.MapCache
|
||||
.Where(m => m.Status == MapStatus.Available && (m.Map.Visibility & filter) != 0)
|
||||
.Where(m => gameMode == null || m.Type == gameMode)
|
||||
.Where(m => mapFilter == null || m.Title.IndexOf(mapFilter, StringComparison.OrdinalIgnoreCase) >= 0 || m.Author.IndexOf(mapFilter, StringComparison.OrdinalIgnoreCase) >= 0)
|
||||
.OrderBy(m => m.PlayerCount)
|
||||
.ThenBy(m => m.Title);
|
||||
|
||||
scrollpanel.RemoveChildren();
|
||||
foreach (var loop in maps)
|
||||
{
|
||||
var preview = loop;
|
||||
|
||||
// Access the minimap to trigger async generation of the minimap.
|
||||
preview.GetMinimap();
|
||||
|
||||
var item = ScrollItemWidget.Setup(preview.Uid, itemTemplate, () => selectedUid == preview.Uid, () => selectedUid = preview.Uid, () => { Ui.CloseWindow(); onSelect(preview.Uid); });
|
||||
item.IsVisible = () => item.RenderBounds.IntersectsWith(scrollpanel.RenderBounds);
|
||||
|
||||
var titleLabel = item.Get<LabelWidget>("TITLE");
|
||||
titleLabel.GetText = () => preview.Title;
|
||||
|
||||
var previewWidget = item.Get<MapPreviewWidget>("PREVIEW");
|
||||
previewWidget.Preview = () => preview;
|
||||
|
||||
var detailsWidget = item.GetOrNull<LabelWidget>("DETAILS");
|
||||
if (detailsWidget != null)
|
||||
detailsWidget.GetText = () => "{0} ({1} players)".F(preview.Type, preview.PlayerCount);
|
||||
|
||||
var authorWidget = item.GetOrNull<LabelWidget>("AUTHOR");
|
||||
if (authorWidget != null)
|
||||
authorWidget.GetText = () => "Created by {0}".F(preview.Author);
|
||||
|
||||
var sizeWidget = item.GetOrNull<LabelWidget>("SIZE");
|
||||
if (sizeWidget != null)
|
||||
{
|
||||
var size = preview.Bounds.Width + "x" + preview.Bounds.Height;
|
||||
var numberPlayableCells = preview.Bounds.Width * preview.Bounds.Height;
|
||||
if (numberPlayableCells >= 120 * 120) size += " (Huge)";
|
||||
else if (numberPlayableCells >= 90 * 90) size += " (Large)";
|
||||
else if (numberPlayableCells >= 60 * 60) size += " (Medium)";
|
||||
else size += " (Small)";
|
||||
sizeWidget.GetText = () => size;
|
||||
}
|
||||
|
||||
scrollpanel.AddChild(item);
|
||||
}
|
||||
|
||||
visibleMaps = maps.Select(m => m.Uid).ToList();
|
||||
if (visibleMaps.Contains(selectedUid))
|
||||
scrollpanel.ScrollToItem(selectedUid);
|
||||
}
|
||||
}
|
||||
}
|
||||
709
OpenRA.Mods.Common/Widgets/Logic/ReplayBrowserLogic.cs
Normal file
709
OpenRA.Mods.Common/Widgets/Logic/ReplayBrowserLogic.cs
Normal file
@@ -0,0 +1,709 @@
|
||||
#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.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using OpenRA.FileFormats;
|
||||
using OpenRA.Mods.Common.Widgets;
|
||||
using OpenRA.Primitives;
|
||||
using OpenRA.Widgets;
|
||||
|
||||
namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
{
|
||||
public class ReplayBrowserLogic
|
||||
{
|
||||
static Filter filter = new Filter();
|
||||
|
||||
Widget panel;
|
||||
ScrollPanelWidget replayList, playerList;
|
||||
ScrollItemWidget playerTemplate, playerHeader;
|
||||
List<ReplayMetadata> replays;
|
||||
Dictionary<ReplayMetadata, ReplayState> replayState = new Dictionary<ReplayMetadata, ReplayState>();
|
||||
|
||||
Dictionary<CPos, SpawnOccupant> selectedSpawns;
|
||||
ReplayMetadata selectedReplay;
|
||||
|
||||
[ObjectCreator.UseCtor]
|
||||
public ReplayBrowserLogic(Widget widget, Action onExit, Action onStart)
|
||||
{
|
||||
panel = widget;
|
||||
|
||||
playerList = panel.Get<ScrollPanelWidget>("PLAYER_LIST");
|
||||
playerHeader = playerList.Get<ScrollItemWidget>("HEADER");
|
||||
playerTemplate = playerList.Get<ScrollItemWidget>("TEMPLATE");
|
||||
playerList.RemoveChildren();
|
||||
|
||||
panel.Get<ButtonWidget>("CANCEL_BUTTON").OnClick = () => { Ui.CloseWindow(); onExit(); };
|
||||
|
||||
replayList = panel.Get<ScrollPanelWidget>("REPLAY_LIST");
|
||||
var template = panel.Get<ScrollItemWidget>("REPLAY_TEMPLATE");
|
||||
|
||||
var mod = Game.ModData.Manifest.Mod;
|
||||
var dir = Platform.ResolvePath("^", "Replays", mod.Id, mod.Version);
|
||||
|
||||
replayList.RemoveChildren();
|
||||
if (Directory.Exists(dir))
|
||||
{
|
||||
using (new Support.PerfTimer("Load replays"))
|
||||
{
|
||||
replays = Directory
|
||||
.GetFiles(dir, "*.rep")
|
||||
.Select(ReplayMetadata.Read)
|
||||
.Where(r => r != null)
|
||||
.OrderByDescending(r => r.GameInfo.StartTimeUtc)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
foreach (var replay in replays)
|
||||
AddReplay(replay, template);
|
||||
|
||||
ApplyFilter();
|
||||
}
|
||||
else
|
||||
replays = new List<ReplayMetadata>();
|
||||
|
||||
var watch = panel.Get<ButtonWidget>("WATCH_BUTTON");
|
||||
watch.IsDisabled = () => selectedReplay == null || selectedReplay.GameInfo.MapPreview.Status != MapStatus.Available;
|
||||
watch.OnClick = () => { WatchReplay(); onStart(); };
|
||||
|
||||
panel.Get("REPLAY_INFO").IsVisible = () => selectedReplay != null;
|
||||
|
||||
var preview = panel.Get<MapPreviewWidget>("MAP_PREVIEW");
|
||||
preview.SpawnOccupants = () => selectedSpawns;
|
||||
preview.Preview = () => selectedReplay != null ? selectedReplay.GameInfo.MapPreview : null;
|
||||
|
||||
var title = panel.GetOrNull<LabelWidget>("MAP_TITLE");
|
||||
if (title != null)
|
||||
title.GetText = () => selectedReplay != null ? selectedReplay.GameInfo.MapPreview.Title : null;
|
||||
|
||||
var type = panel.GetOrNull<LabelWidget>("MAP_TYPE");
|
||||
if (type != null)
|
||||
type.GetText = () => selectedReplay.GameInfo.MapPreview.Type;
|
||||
|
||||
panel.Get<LabelWidget>("DURATION").GetText = () => WidgetUtils.FormatTimeSeconds((int)selectedReplay.GameInfo.Duration.TotalSeconds);
|
||||
|
||||
SetupFilters();
|
||||
SetupManagement();
|
||||
}
|
||||
|
||||
void SetupFilters()
|
||||
{
|
||||
// Game type
|
||||
{
|
||||
var ddb = panel.GetOrNull<DropDownButtonWidget>("FLT_GAMETYPE_DROPDOWNBUTTON");
|
||||
if (ddb != null)
|
||||
{
|
||||
// Using list to maintain the order
|
||||
var options = new List<Pair<GameType, string>>
|
||||
{
|
||||
Pair.New(GameType.Any, ddb.GetText()),
|
||||
Pair.New(GameType.Singleplayer, "Singleplayer"),
|
||||
Pair.New(GameType.Multiplayer, "Multiplayer")
|
||||
};
|
||||
var lookup = options.ToDictionary(kvp => kvp.First, kvp => kvp.Second);
|
||||
|
||||
ddb.GetText = () => lookup[filter.Type];
|
||||
ddb.OnMouseDown = _ =>
|
||||
{
|
||||
Func<Pair<GameType, string>, ScrollItemWidget, ScrollItemWidget> setupItem = (option, tpl) =>
|
||||
{
|
||||
var item = ScrollItemWidget.Setup(
|
||||
tpl,
|
||||
() => filter.Type == option.First,
|
||||
() => { filter.Type = option.First; ApplyFilter(); });
|
||||
item.Get<LabelWidget>("LABEL").GetText = () => option.Second;
|
||||
return item;
|
||||
};
|
||||
|
||||
ddb.ShowDropDown("LABEL_DROPDOWN_TEMPLATE", 330, options, setupItem);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Date type
|
||||
{
|
||||
var ddb = panel.GetOrNull<DropDownButtonWidget>("FLT_DATE_DROPDOWNBUTTON");
|
||||
if (ddb != null)
|
||||
{
|
||||
// Using list to maintain the order
|
||||
var options = new List<Pair<DateType, string>>
|
||||
{
|
||||
Pair.New(DateType.Any, ddb.GetText()),
|
||||
Pair.New(DateType.Today, "Today"),
|
||||
Pair.New(DateType.LastWeek, "Last 7 days"),
|
||||
Pair.New(DateType.LastFortnight, "Last 14 days"),
|
||||
Pair.New(DateType.LastMonth, "Last 30 days")
|
||||
};
|
||||
var lookup = options.ToDictionary(kvp => kvp.First, kvp => kvp.Second);
|
||||
|
||||
ddb.GetText = () => lookup[filter.Date];
|
||||
ddb.OnMouseDown = _ =>
|
||||
{
|
||||
Func<Pair<DateType, string>, ScrollItemWidget, ScrollItemWidget> setupItem = (option, tpl) =>
|
||||
{
|
||||
var item = ScrollItemWidget.Setup(
|
||||
tpl,
|
||||
() => filter.Date == option.First,
|
||||
() => { filter.Date = option.First; ApplyFilter(); });
|
||||
|
||||
item.Get<LabelWidget>("LABEL").GetText = () => option.Second;
|
||||
return item;
|
||||
};
|
||||
|
||||
ddb.ShowDropDown("LABEL_DROPDOWN_TEMPLATE", 330, options, setupItem);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Duration
|
||||
{
|
||||
var ddb = panel.GetOrNull<DropDownButtonWidget>("FLT_DURATION_DROPDOWNBUTTON");
|
||||
if (ddb != null)
|
||||
{
|
||||
// Using list to maintain the order
|
||||
var options = new List<Pair<DurationType, string>>
|
||||
{
|
||||
Pair.New(DurationType.Any, ddb.GetText()),
|
||||
Pair.New(DurationType.VeryShort, "Under 5 min"),
|
||||
Pair.New(DurationType.Short, "Short (10 min)"),
|
||||
Pair.New(DurationType.Medium, "Medium (30 min)"),
|
||||
Pair.New(DurationType.Long, "Long (60+ min)")
|
||||
};
|
||||
var lookup = options.ToDictionary(kvp => kvp.First, kvp => kvp.Second);
|
||||
|
||||
ddb.GetText = () => lookup[filter.Duration];
|
||||
ddb.OnMouseDown = _ =>
|
||||
{
|
||||
Func<Pair<DurationType, string>, ScrollItemWidget, ScrollItemWidget> setupItem = (option, tpl) =>
|
||||
{
|
||||
var item = ScrollItemWidget.Setup(
|
||||
tpl,
|
||||
() => filter.Duration == option.First,
|
||||
() => { filter.Duration = option.First; ApplyFilter(); });
|
||||
item.Get<LabelWidget>("LABEL").GetText = () => option.Second;
|
||||
return item;
|
||||
};
|
||||
|
||||
ddb.ShowDropDown("LABEL_DROPDOWN_TEMPLATE", 330, options, setupItem);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Map
|
||||
{
|
||||
var ddb = panel.GetOrNull<DropDownButtonWidget>("FLT_MAPNAME_DROPDOWNBUTTON");
|
||||
if (ddb != null)
|
||||
{
|
||||
var options = new HashSet<string>(replays.Select(r => r.GameInfo.MapTitle), StringComparer.OrdinalIgnoreCase).ToList();
|
||||
options.Sort(StringComparer.OrdinalIgnoreCase);
|
||||
options.Insert(0, null); // no filter
|
||||
|
||||
var anyText = ddb.GetText();
|
||||
ddb.GetText = () => string.IsNullOrEmpty(filter.MapName) ? anyText : filter.MapName;
|
||||
ddb.OnMouseDown = _ =>
|
||||
{
|
||||
Func<string, ScrollItemWidget, ScrollItemWidget> setupItem = (option, tpl) =>
|
||||
{
|
||||
var item = ScrollItemWidget.Setup(
|
||||
tpl,
|
||||
() => string.Compare(filter.MapName, option, true) == 0,
|
||||
() => { filter.MapName = option; ApplyFilter(); });
|
||||
item.Get<LabelWidget>("LABEL").GetText = () => option ?? anyText;
|
||||
return item;
|
||||
};
|
||||
|
||||
ddb.ShowDropDown("LABEL_DROPDOWN_TEMPLATE", 330, options, setupItem);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Players
|
||||
{
|
||||
var ddb = panel.GetOrNull<DropDownButtonWidget>("FLT_PLAYER_DROPDOWNBUTTON");
|
||||
if (ddb != null)
|
||||
{
|
||||
var options = new HashSet<string>(replays.SelectMany(r => r.GameInfo.Players.Select(p => p.Name)), StringComparer.OrdinalIgnoreCase).ToList();
|
||||
options.Sort(StringComparer.OrdinalIgnoreCase);
|
||||
options.Insert(0, null); // no filter
|
||||
|
||||
var anyText = ddb.GetText();
|
||||
ddb.GetText = () => string.IsNullOrEmpty(filter.PlayerName) ? anyText : filter.PlayerName;
|
||||
ddb.OnMouseDown = _ =>
|
||||
{
|
||||
Func<string, ScrollItemWidget, ScrollItemWidget> setupItem = (option, tpl) =>
|
||||
{
|
||||
var item = ScrollItemWidget.Setup(
|
||||
tpl,
|
||||
() => string.Compare(filter.PlayerName, option, true) == 0,
|
||||
() => { filter.PlayerName = option; ApplyFilter(); });
|
||||
item.Get<LabelWidget>("LABEL").GetText = () => option ?? anyText;
|
||||
return item;
|
||||
};
|
||||
|
||||
ddb.ShowDropDown("LABEL_DROPDOWN_TEMPLATE", 330, options, setupItem);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Outcome (depends on Player)
|
||||
{
|
||||
var ddb = panel.GetOrNull<DropDownButtonWidget>("FLT_OUTCOME_DROPDOWNBUTTON");
|
||||
if (ddb != null)
|
||||
{
|
||||
ddb.IsDisabled = () => string.IsNullOrEmpty(filter.PlayerName);
|
||||
|
||||
// Using list to maintain the order
|
||||
var options = new List<Pair<WinState, string>>
|
||||
{
|
||||
Pair.New(WinState.Undefined, ddb.GetText()),
|
||||
Pair.New(WinState.Lost, "Defeat"),
|
||||
Pair.New(WinState.Won, "Victory")
|
||||
};
|
||||
var lookup = options.ToDictionary(kvp => kvp.First, kvp => kvp.Second);
|
||||
|
||||
ddb.GetText = () => lookup[filter.Outcome];
|
||||
ddb.OnMouseDown = _ =>
|
||||
{
|
||||
Func<Pair<WinState, string>, ScrollItemWidget, ScrollItemWidget> setupItem = (option, tpl) =>
|
||||
{
|
||||
var item = ScrollItemWidget.Setup(
|
||||
tpl,
|
||||
() => filter.Outcome == option.First,
|
||||
() => { filter.Outcome = option.First; ApplyFilter(); });
|
||||
item.Get<LabelWidget>("LABEL").GetText = () => option.Second;
|
||||
return item;
|
||||
};
|
||||
|
||||
ddb.ShowDropDown("LABEL_DROPDOWN_TEMPLATE", 330, options, setupItem);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Faction (depends on Player)
|
||||
{
|
||||
var ddb = panel.GetOrNull<DropDownButtonWidget>("FLT_FACTION_DROPDOWNBUTTON");
|
||||
if (ddb != null)
|
||||
{
|
||||
ddb.IsDisabled = () => string.IsNullOrEmpty(filter.PlayerName);
|
||||
|
||||
var options = new HashSet<string>(replays.SelectMany(r => r.GameInfo.Players.Select(p => p.FactionName).Where(n => !string.IsNullOrEmpty(n))), StringComparer.OrdinalIgnoreCase).ToList();
|
||||
options.Sort(StringComparer.OrdinalIgnoreCase);
|
||||
options.Insert(0, null); // no filter
|
||||
|
||||
var anyText = ddb.GetText();
|
||||
ddb.GetText = () => string.IsNullOrEmpty(filter.Faction) ? anyText : filter.Faction;
|
||||
ddb.OnMouseDown = _ =>
|
||||
{
|
||||
Func<string, ScrollItemWidget, ScrollItemWidget> setupItem = (option, tpl) =>
|
||||
{
|
||||
var item = ScrollItemWidget.Setup(
|
||||
tpl,
|
||||
() => string.Compare(filter.Faction, option, true) == 0,
|
||||
() => { filter.Faction = option; ApplyFilter(); });
|
||||
item.Get<LabelWidget>("LABEL").GetText = () => option ?? anyText;
|
||||
return item;
|
||||
};
|
||||
|
||||
ddb.ShowDropDown("LABEL_DROPDOWN_TEMPLATE", 330, options, setupItem);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Reset button
|
||||
{
|
||||
var button = panel.Get<ButtonWidget>("FLT_RESET_BUTTON");
|
||||
button.IsDisabled = () => filter.IsEmpty;
|
||||
button.OnClick = () => { filter = new Filter(); ApplyFilter(); };
|
||||
}
|
||||
}
|
||||
|
||||
void SetupManagement()
|
||||
{
|
||||
{
|
||||
var button = panel.Get<ButtonWidget>("MNG_RENSEL_BUTTON");
|
||||
button.IsDisabled = () => selectedReplay == null;
|
||||
button.OnClick = () =>
|
||||
{
|
||||
var r = selectedReplay;
|
||||
var initialName = Path.GetFileNameWithoutExtension(r.FilePath);
|
||||
var directoryName = Path.GetDirectoryName(r.FilePath);
|
||||
var invalidChars = Path.GetInvalidFileNameChars();
|
||||
|
||||
ConfirmationDialogs.TextInputPrompt(
|
||||
"Rename Replay",
|
||||
"Enter a new file name:",
|
||||
initialName,
|
||||
onAccept: newName => RenameReplay(r, newName),
|
||||
onCancel: null,
|
||||
acceptText: "Rename",
|
||||
cancelText: null,
|
||||
inputValidator: newName =>
|
||||
{
|
||||
if (newName == initialName)
|
||||
return false;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(newName))
|
||||
return false;
|
||||
|
||||
if (newName.IndexOfAny(invalidChars) >= 0)
|
||||
return false;
|
||||
|
||||
if (File.Exists(Path.Combine(directoryName, newName)))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
Action<ReplayMetadata, Action> onDeleteReplay = (r, after) =>
|
||||
{
|
||||
ConfirmationDialogs.PromptConfirmAction(
|
||||
"Delete selected replay?",
|
||||
"Delete replay '{0}'?".F(Path.GetFileNameWithoutExtension(r.FilePath)),
|
||||
() =>
|
||||
{
|
||||
DeleteReplay(r);
|
||||
if (after != null)
|
||||
after.Invoke();
|
||||
},
|
||||
null,
|
||||
"Delete");
|
||||
};
|
||||
|
||||
{
|
||||
var button = panel.Get<ButtonWidget>("MNG_DELSEL_BUTTON");
|
||||
button.IsDisabled = () => selectedReplay == null;
|
||||
button.OnClick = () =>
|
||||
{
|
||||
onDeleteReplay(selectedReplay, () =>
|
||||
{
|
||||
if (selectedReplay == null)
|
||||
SelectFirstVisibleReplay();
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
{
|
||||
var button = panel.Get<ButtonWidget>("MNG_DELALL_BUTTON");
|
||||
button.IsDisabled = () => replayState.Count(kvp => kvp.Value.Visible) == 0;
|
||||
button.OnClick = () =>
|
||||
{
|
||||
var list = replayState.Where(kvp => kvp.Value.Visible).Select(kvp => kvp.Key).ToList();
|
||||
if (list.Count == 0)
|
||||
return;
|
||||
|
||||
if (list.Count == 1)
|
||||
{
|
||||
onDeleteReplay(list[0], () => { if (selectedReplay == null) SelectFirstVisibleReplay(); });
|
||||
return;
|
||||
}
|
||||
|
||||
ConfirmationDialogs.PromptConfirmAction(
|
||||
"Delete all selected replays?",
|
||||
"Delete {0} replays?".F(list.Count),
|
||||
() =>
|
||||
{
|
||||
list.ForEach(DeleteReplay);
|
||||
if (selectedReplay == null)
|
||||
SelectFirstVisibleReplay();
|
||||
},
|
||||
null,
|
||||
"Delete All");
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
void RenameReplay(ReplayMetadata replay, string newFilenameWithoutExtension)
|
||||
{
|
||||
try
|
||||
{
|
||||
replay.RenameFile(newFilenameWithoutExtension);
|
||||
replayState[replay].Item.Text = newFilenameWithoutExtension;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Write("debug", ex.ToString());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void DeleteReplay(ReplayMetadata replay)
|
||||
{
|
||||
try
|
||||
{
|
||||
File.Delete(replay.FilePath);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Game.Debug("Failed to delete replay file '{0}'. See the logs for details.", replay.FilePath);
|
||||
Log.Write("debug", ex.ToString());
|
||||
return;
|
||||
}
|
||||
|
||||
if (replay == selectedReplay)
|
||||
SelectReplay(null);
|
||||
|
||||
replayList.RemoveChild(replayState[replay].Item);
|
||||
replays.Remove(replay);
|
||||
replayState.Remove(replay);
|
||||
}
|
||||
|
||||
bool EvaluateReplayVisibility(ReplayMetadata replay)
|
||||
{
|
||||
// Game type
|
||||
if ((filter.Type == GameType.Multiplayer && replay.GameInfo.IsSinglePlayer) || (filter.Type == GameType.Singleplayer && !replay.GameInfo.IsSinglePlayer))
|
||||
return false;
|
||||
|
||||
// Date type
|
||||
if (filter.Date != DateType.Any)
|
||||
{
|
||||
TimeSpan t;
|
||||
switch (filter.Date)
|
||||
{
|
||||
case DateType.Today:
|
||||
t = TimeSpan.FromDays(1d);
|
||||
break;
|
||||
|
||||
case DateType.LastWeek:
|
||||
t = TimeSpan.FromDays(7d);
|
||||
break;
|
||||
|
||||
case DateType.LastFortnight:
|
||||
t = TimeSpan.FromDays(14d);
|
||||
break;
|
||||
|
||||
case DateType.LastMonth:
|
||||
default:
|
||||
t = TimeSpan.FromDays(30d);
|
||||
break;
|
||||
}
|
||||
|
||||
if (replay.GameInfo.StartTimeUtc < DateTime.UtcNow - t)
|
||||
return false;
|
||||
}
|
||||
|
||||
// Duration
|
||||
if (filter.Duration != DurationType.Any)
|
||||
{
|
||||
var minutes = replay.GameInfo.Duration.TotalMinutes;
|
||||
switch (filter.Duration)
|
||||
{
|
||||
case DurationType.VeryShort:
|
||||
if (minutes >= 5)
|
||||
return false;
|
||||
break;
|
||||
|
||||
case DurationType.Short:
|
||||
if (minutes < 5 || minutes >= 20)
|
||||
return false;
|
||||
break;
|
||||
|
||||
case DurationType.Medium:
|
||||
if (minutes < 20 || minutes >= 60)
|
||||
return false;
|
||||
break;
|
||||
|
||||
case DurationType.Long:
|
||||
if (minutes < 60)
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Map
|
||||
if (!string.IsNullOrEmpty(filter.MapName) && string.Compare(filter.MapName, replay.GameInfo.MapTitle, true) != 0)
|
||||
return false;
|
||||
|
||||
// Player
|
||||
if (!string.IsNullOrEmpty(filter.PlayerName))
|
||||
{
|
||||
var player = replay.GameInfo.Players.FirstOrDefault(p => string.Compare(filter.PlayerName, p.Name, true) == 0);
|
||||
if (player == null)
|
||||
return false;
|
||||
|
||||
// Outcome
|
||||
if (filter.Outcome != WinState.Undefined && filter.Outcome != player.Outcome)
|
||||
return false;
|
||||
|
||||
// Faction
|
||||
if (!string.IsNullOrEmpty(filter.Faction) && string.Compare(filter.Faction, player.FactionName, true) != 0)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ApplyFilter()
|
||||
{
|
||||
foreach (var replay in replays)
|
||||
replayState[replay].Visible = EvaluateReplayVisibility(replay);
|
||||
|
||||
if (selectedReplay == null || replayState[selectedReplay].Visible == false)
|
||||
SelectFirstVisibleReplay();
|
||||
|
||||
replayList.Layout.AdjustChildren();
|
||||
}
|
||||
|
||||
void SelectFirstVisibleReplay()
|
||||
{
|
||||
SelectReplay(replays.FirstOrDefault(r => replayState[r].Visible));
|
||||
}
|
||||
|
||||
void SelectReplay(ReplayMetadata replay)
|
||||
{
|
||||
selectedReplay = replay;
|
||||
selectedSpawns = (selectedReplay != null)
|
||||
? LobbyUtils.GetSpawnOccupants(selectedReplay.GameInfo.Players, selectedReplay.GameInfo.MapPreview)
|
||||
: new Dictionary<CPos, SpawnOccupant>();
|
||||
|
||||
if (replay == null)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
var players = replay.GameInfo.Players
|
||||
.GroupBy(p => p.Team)
|
||||
.OrderBy(g => g.Key);
|
||||
|
||||
var teams = new Dictionary<string, IEnumerable<GameInformation.Player>>();
|
||||
var noTeams = players.Count() == 1;
|
||||
foreach (var p in players)
|
||||
{
|
||||
var label = noTeams ? "Players" : p.Key == 0 ? "No Team" : "Team {0}".F(p.Key);
|
||||
teams.Add(label, p);
|
||||
}
|
||||
|
||||
playerList.RemoveChildren();
|
||||
|
||||
foreach (var kv in teams)
|
||||
{
|
||||
var group = kv.Key;
|
||||
if (group.Length > 0)
|
||||
{
|
||||
var header = ScrollItemWidget.Setup(playerHeader, () => true, () => { });
|
||||
header.Get<LabelWidget>("LABEL").GetText = () => group;
|
||||
playerList.AddChild(header);
|
||||
}
|
||||
|
||||
foreach (var option in kv.Value)
|
||||
{
|
||||
var o = option;
|
||||
|
||||
var color = o.Color.RGB;
|
||||
|
||||
var item = ScrollItemWidget.Setup(playerTemplate, () => false, () => { });
|
||||
|
||||
var label = item.Get<LabelWidget>("LABEL");
|
||||
label.GetText = () => o.Name;
|
||||
label.GetColor = () => color;
|
||||
|
||||
var flag = item.Get<ImageWidget>("FLAG");
|
||||
flag.GetImageCollection = () => "flags";
|
||||
flag.GetImageName = () => o.FactionId;
|
||||
|
||||
playerList.AddChild(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Write("debug", "Exception while parsing replay: {0}", e);
|
||||
SelectReplay(null);
|
||||
}
|
||||
}
|
||||
|
||||
void WatchReplay()
|
||||
{
|
||||
if (selectedReplay != null && selectedReplay.GameInfo.MapPreview.Status == MapStatus.Available)
|
||||
{
|
||||
Game.JoinReplay(selectedReplay.FilePath);
|
||||
Ui.CloseWindow();
|
||||
}
|
||||
}
|
||||
|
||||
void AddReplay(ReplayMetadata replay, ScrollItemWidget template)
|
||||
{
|
||||
var item = ScrollItemWidget.Setup(template,
|
||||
() => selectedReplay == replay,
|
||||
() => SelectReplay(replay),
|
||||
() => WatchReplay());
|
||||
|
||||
replayState[replay] = new ReplayState
|
||||
{
|
||||
Item = item,
|
||||
Visible = true
|
||||
};
|
||||
|
||||
item.Text = Path.GetFileNameWithoutExtension(replay.FilePath);
|
||||
item.Get<LabelWidget>("TITLE").GetText = () => item.Text;
|
||||
item.IsVisible = () => replayState[replay].Visible;
|
||||
replayList.AddChild(item);
|
||||
}
|
||||
|
||||
class ReplayState
|
||||
{
|
||||
public bool Visible;
|
||||
public ScrollItemWidget Item;
|
||||
}
|
||||
|
||||
class Filter
|
||||
{
|
||||
public GameType Type;
|
||||
public DateType Date;
|
||||
public DurationType Duration;
|
||||
public WinState Outcome;
|
||||
public string PlayerName;
|
||||
public string MapName;
|
||||
public string Faction;
|
||||
|
||||
public bool IsEmpty
|
||||
{
|
||||
get
|
||||
{
|
||||
return Type == default(GameType)
|
||||
&& Date == default(DateType)
|
||||
&& Duration == default(DurationType)
|
||||
&& Outcome == default(WinState)
|
||||
&& string.IsNullOrEmpty(PlayerName)
|
||||
&& string.IsNullOrEmpty(MapName)
|
||||
&& string.IsNullOrEmpty(Faction);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum GameType
|
||||
{
|
||||
Any,
|
||||
Singleplayer,
|
||||
Multiplayer
|
||||
}
|
||||
|
||||
enum DateType
|
||||
{
|
||||
Any,
|
||||
Today,
|
||||
LastWeek,
|
||||
LastFortnight,
|
||||
LastMonth
|
||||
}
|
||||
|
||||
enum DurationType
|
||||
{
|
||||
Any,
|
||||
VeryShort,
|
||||
Short,
|
||||
Medium,
|
||||
Long
|
||||
}
|
||||
}
|
||||
}
|
||||
396
OpenRA.Mods.Common/Widgets/Logic/ServerBrowserLogic.cs
Normal file
396
OpenRA.Mods.Common/Widgets/Logic/ServerBrowserLogic.cs
Normal file
@@ -0,0 +1,396 @@
|
||||
#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.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using OpenRA.Network;
|
||||
using OpenRA.Server;
|
||||
using OpenRA.Widgets;
|
||||
|
||||
namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
{
|
||||
public class ServerBrowserLogic
|
||||
{
|
||||
static readonly Action DoNothing = () => { };
|
||||
|
||||
GameServer currentServer;
|
||||
ScrollItemWidget serverTemplate;
|
||||
ScrollItemWidget headerTemplate;
|
||||
|
||||
Action onStart;
|
||||
|
||||
enum SearchStatus { Fetching, Failed, NoGames, Hidden }
|
||||
SearchStatus searchStatus = SearchStatus.Fetching;
|
||||
Download currentQuery;
|
||||
Widget panel, serverList;
|
||||
|
||||
bool showWaiting = true;
|
||||
bool showEmpty = true;
|
||||
bool showStarted = false;
|
||||
bool showProtected = true;
|
||||
bool showIncompatible = false;
|
||||
|
||||
public string ProgressLabelText()
|
||||
{
|
||||
switch (searchStatus)
|
||||
{
|
||||
case SearchStatus.Failed: return "Failed to contact master server.";
|
||||
case SearchStatus.NoGames: return "No games found.";
|
||||
default: return "";
|
||||
}
|
||||
}
|
||||
|
||||
[ObjectCreator.UseCtor]
|
||||
public ServerBrowserLogic(Widget widget, Action onStart, Action onExit, string directConnectHost, int directConnectPort)
|
||||
{
|
||||
panel = widget;
|
||||
this.onStart = onStart;
|
||||
|
||||
serverList = panel.Get<ScrollPanelWidget>("SERVER_LIST");
|
||||
headerTemplate = serverList.Get<ScrollItemWidget>("HEADER_TEMPLATE");
|
||||
serverTemplate = serverList.Get<ScrollItemWidget>("SERVER_TEMPLATE");
|
||||
|
||||
// Menu buttons
|
||||
var refreshButton = panel.Get<ButtonWidget>("REFRESH_BUTTON");
|
||||
refreshButton.IsDisabled = () => searchStatus == SearchStatus.Fetching;
|
||||
refreshButton.GetText = () => searchStatus == SearchStatus.Fetching ? "Refreshing..." : "Refresh";
|
||||
refreshButton.OnClick = RefreshServerList;
|
||||
|
||||
panel.Get<ButtonWidget>("DIRECTCONNECT_BUTTON").OnClick = OpenDirectConnectPanel;
|
||||
panel.Get<ButtonWidget>("CREATE_BUTTON").OnClick = OpenCreateServerPanel;
|
||||
|
||||
var join = panel.Get<ButtonWidget>("JOIN_BUTTON");
|
||||
join.IsDisabled = () => currentServer == null || !currentServer.IsJoinable;
|
||||
join.OnClick = () => Join(currentServer);
|
||||
|
||||
panel.Get<ButtonWidget>("BACK_BUTTON").OnClick = () => { Ui.CloseWindow(); onExit(); };
|
||||
|
||||
// Display the progress label over the server list
|
||||
// The text is only visible when the list is empty
|
||||
var progressText = panel.Get<LabelWidget>("PROGRESS_LABEL");
|
||||
progressText.IsVisible = () => searchStatus != SearchStatus.Hidden;
|
||||
progressText.GetText = ProgressLabelText;
|
||||
|
||||
var showWaitingCheckbox = panel.GetOrNull<CheckboxWidget>("WAITING_FOR_PLAYERS");
|
||||
if (showWaitingCheckbox != null)
|
||||
{
|
||||
showWaitingCheckbox.IsChecked = () => showWaiting;
|
||||
showWaitingCheckbox.OnClick = () => { showWaiting ^= true; RefreshServerList(); };
|
||||
}
|
||||
|
||||
var showEmptyCheckbox = panel.GetOrNull<CheckboxWidget>("EMPTY");
|
||||
if (showEmptyCheckbox != null)
|
||||
{
|
||||
showEmptyCheckbox.IsChecked = () => showEmpty;
|
||||
showEmptyCheckbox.OnClick = () => { showEmpty ^= true; RefreshServerList(); };
|
||||
}
|
||||
|
||||
var showAlreadyStartedCheckbox = panel.GetOrNull<CheckboxWidget>("ALREADY_STARTED");
|
||||
if (showAlreadyStartedCheckbox != null)
|
||||
{
|
||||
showAlreadyStartedCheckbox.IsChecked = () => showStarted;
|
||||
showAlreadyStartedCheckbox.OnClick = () => { showStarted ^= true; RefreshServerList(); };
|
||||
}
|
||||
|
||||
var showProtectedCheckbox = panel.GetOrNull<CheckboxWidget>("PASSWORD_PROTECTED");
|
||||
if (showProtectedCheckbox != null)
|
||||
{
|
||||
showProtectedCheckbox.IsChecked = () => showProtected;
|
||||
showProtectedCheckbox.OnClick = () => { showProtected ^= true; RefreshServerList(); };
|
||||
}
|
||||
|
||||
var showIncompatibleCheckbox = panel.GetOrNull<CheckboxWidget>("INCOMPATIBLE_VERSION");
|
||||
if (showIncompatibleCheckbox != null)
|
||||
{
|
||||
showIncompatibleCheckbox.IsChecked = () => showIncompatible;
|
||||
showIncompatibleCheckbox.OnClick = () => { showIncompatible ^= true; RefreshServerList(); };
|
||||
}
|
||||
|
||||
RefreshServerList();
|
||||
|
||||
if (directConnectHost != null)
|
||||
{
|
||||
// The connection window must be opened at the end of the tick for the widget hierarchy to
|
||||
// work out, but we also want to prevent the server browser from flashing visible for one tick.
|
||||
widget.Visible = false;
|
||||
Game.RunAfterTick(() =>
|
||||
{
|
||||
ConnectionLogic.Connect(directConnectHost, directConnectPort, "", OpenLobby, DoNothing);
|
||||
widget.Visible = true;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void RefreshServerList()
|
||||
{
|
||||
// Query in progress
|
||||
if (currentQuery != null)
|
||||
return;
|
||||
|
||||
searchStatus = SearchStatus.Fetching;
|
||||
|
||||
Action<DownloadDataCompletedEventArgs, bool> onComplete = (i, cancelled) =>
|
||||
{
|
||||
currentQuery = null;
|
||||
|
||||
if (i.Error != null || cancelled)
|
||||
{
|
||||
RefreshServerListInner(null);
|
||||
return;
|
||||
}
|
||||
|
||||
var data = Encoding.UTF8.GetString(i.Result);
|
||||
var yaml = MiniYaml.FromString(data);
|
||||
|
||||
var games = yaml.Select(a => new GameServer(a.Value))
|
||||
.Where(gs => gs.Address != null);
|
||||
|
||||
Game.RunAfterTick(() => RefreshServerListInner(games));
|
||||
};
|
||||
|
||||
currentQuery = new Download(Game.Settings.Server.MasterServer + "games", _ => { }, onComplete);
|
||||
}
|
||||
|
||||
int GroupSortOrder(GameServer testEntry)
|
||||
{
|
||||
// Games that we can't join are sorted last
|
||||
if (!testEntry.IsCompatible)
|
||||
return 0;
|
||||
|
||||
// Games for the current mod+version are sorted first
|
||||
if (testEntry.ModId == Game.ModData.Manifest.Mod.Id)
|
||||
return 2;
|
||||
|
||||
// Followed by games for different mods that are joinable
|
||||
return 1;
|
||||
}
|
||||
|
||||
void RefreshServerListInner(IEnumerable<GameServer> games)
|
||||
{
|
||||
if (games == null)
|
||||
return;
|
||||
|
||||
var mods = games.GroupBy(g => g.Mods)
|
||||
.OrderByDescending(g => GroupSortOrder(g.First()))
|
||||
.ThenByDescending(g => g.Count());
|
||||
|
||||
var rows = new List<Widget>();
|
||||
foreach (var modGames in mods)
|
||||
{
|
||||
if (modGames.All(Filtered))
|
||||
continue;
|
||||
|
||||
var header = ScrollItemWidget.Setup(headerTemplate, () => true, () => { });
|
||||
|
||||
var headerTitle = modGames.First().ModLabel;
|
||||
header.Get<LabelWidget>("LABEL").GetText = () => headerTitle;
|
||||
rows.Add(header);
|
||||
|
||||
foreach (var loop in modGames.OrderByDescending(g => g.IsJoinable).ThenByDescending(g => g.Players))
|
||||
{
|
||||
var game = loop;
|
||||
if (game == null || Filtered(game))
|
||||
continue;
|
||||
|
||||
var canJoin = game.IsJoinable;
|
||||
var compatible = game.IsCompatible;
|
||||
|
||||
var item = ScrollItemWidget.Setup(serverTemplate, () => currentServer == game, () => currentServer = game, () => Join(game));
|
||||
|
||||
var map = Game.ModData.MapCache[game.Map];
|
||||
var preview = item.GetOrNull<MapPreviewWidget>("MAP_PREVIEW");
|
||||
if (preview != null)
|
||||
preview.Preview = () => map;
|
||||
|
||||
var title = item.GetOrNull<LabelWidget>("TITLE");
|
||||
if (title != null)
|
||||
{
|
||||
title.GetText = () => game.Name;
|
||||
title.GetColor = () => !compatible ? Color.DarkGray : !canJoin ? Color.LightGray : title.TextColor;
|
||||
}
|
||||
|
||||
var maptitle = item.GetOrNull<LabelWidget>("MAP");
|
||||
if (title != null)
|
||||
{
|
||||
maptitle.GetText = () => map.Title;
|
||||
maptitle.GetColor = () => !compatible ? Color.DarkGray : !canJoin ? Color.LightGray : maptitle.TextColor;
|
||||
}
|
||||
|
||||
var players = item.GetOrNull<LabelWidget>("PLAYERS");
|
||||
if (players != null)
|
||||
{
|
||||
players.GetText = () => "{0} / {1}".F(game.Players, game.MaxPlayers)
|
||||
+ (game.Spectators > 0 ? " ({0} Spectator{1})".F(game.Spectators, game.Spectators > 1 ? "s" : "") : "");
|
||||
players.GetColor = () => !compatible ? Color.DarkGray : !canJoin ? Color.LightGray : players.TextColor;
|
||||
}
|
||||
|
||||
var state = item.GetOrNull<LabelWidget>("STATE");
|
||||
if (state != null)
|
||||
{
|
||||
state.GetText = () => GetStateLabel(game);
|
||||
state.GetColor = () => GetStateColor(game, state, !compatible || !canJoin);
|
||||
}
|
||||
|
||||
var ip = item.GetOrNull<LabelWidget>("IP");
|
||||
if (ip != null)
|
||||
{
|
||||
ip.GetText = () => game.Address;
|
||||
ip.GetColor = () => !compatible ? Color.DarkGray : !canJoin ? Color.LightGray : ip.TextColor;
|
||||
}
|
||||
|
||||
var location = item.GetOrNull<LabelWidget>("LOCATION");
|
||||
if (location != null)
|
||||
{
|
||||
var cachedServerLocation = LobbyUtils.LookupCountry(game.Address.Split(':')[0]);
|
||||
location.GetText = () => cachedServerLocation;
|
||||
location.GetColor = () => !compatible ? Color.DarkGray : !canJoin ? Color.LightGray : location.TextColor;
|
||||
}
|
||||
|
||||
rows.Add(item);
|
||||
}
|
||||
}
|
||||
|
||||
Game.RunAfterTick(() =>
|
||||
{
|
||||
serverList.RemoveChildren();
|
||||
currentServer = null;
|
||||
|
||||
if (games == null)
|
||||
{
|
||||
searchStatus = SearchStatus.Failed;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!games.Any())
|
||||
{
|
||||
searchStatus = SearchStatus.NoGames;
|
||||
return;
|
||||
}
|
||||
|
||||
currentServer = games.FirstOrDefault();
|
||||
searchStatus = SearchStatus.Hidden;
|
||||
|
||||
// Search for any unknown maps
|
||||
if (Game.Settings.Game.AllowDownloading)
|
||||
Game.ModData.MapCache.QueryRemoteMapDetails(games.Where(g => !Filtered(g)).Select(g => g.Map));
|
||||
|
||||
foreach (var row in rows)
|
||||
serverList.AddChild(row);
|
||||
});
|
||||
}
|
||||
|
||||
void OpenLobby()
|
||||
{
|
||||
Game.OpenWindow("SERVER_LOBBY", new WidgetArgs
|
||||
{
|
||||
{ "onExit", Game.Disconnect },
|
||||
{ "onStart", onStart },
|
||||
{ "skirmishMode", false }
|
||||
});
|
||||
}
|
||||
|
||||
void OpenDirectConnectPanel()
|
||||
{
|
||||
Ui.OpenWindow("DIRECTCONNECT_PANEL", new WidgetArgs
|
||||
{
|
||||
{ "openLobby", OpenLobby },
|
||||
{ "onExit", DoNothing }
|
||||
});
|
||||
}
|
||||
|
||||
void OpenCreateServerPanel()
|
||||
{
|
||||
Ui.OpenWindow("CREATESERVER_PANEL", new WidgetArgs
|
||||
{
|
||||
{ "openLobby", OpenLobby },
|
||||
{ "onExit", DoNothing }
|
||||
});
|
||||
}
|
||||
|
||||
void Join(GameServer server)
|
||||
{
|
||||
if (server == null || !server.IsJoinable)
|
||||
return;
|
||||
|
||||
var host = server.Address.Split(':')[0];
|
||||
var port = Exts.ParseIntegerInvariant(server.Address.Split(':')[1]);
|
||||
|
||||
ConnectionLogic.Connect(host, port, "", OpenLobby, DoNothing);
|
||||
}
|
||||
|
||||
static string GetStateLabel(GameServer game)
|
||||
{
|
||||
if (game == null)
|
||||
return "";
|
||||
|
||||
if (game.State == (int)ServerState.GameStarted)
|
||||
{
|
||||
try
|
||||
{
|
||||
var runTime = DateTime.Now - System.DateTime.Parse(game.Started);
|
||||
return "In progress for {0} minute{1}".F(runTime.Minutes, runTime.Minutes > 1 ? "s" : "");
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return "In progress";
|
||||
}
|
||||
}
|
||||
|
||||
if (game.Protected)
|
||||
return "Password protected";
|
||||
|
||||
if (game.State == (int)ServerState.WaitingPlayers)
|
||||
return "Waiting for players";
|
||||
|
||||
if (game.State == (int)ServerState.ShuttingDown)
|
||||
return "Server shutting down";
|
||||
|
||||
return "Unknown server state";
|
||||
}
|
||||
|
||||
static Color GetStateColor(GameServer game, LabelWidget label, bool darkened)
|
||||
{
|
||||
if (game.Protected && game.State == (int)ServerState.WaitingPlayers)
|
||||
return darkened ? Color.DarkRed : Color.Red;
|
||||
|
||||
if (game.State == (int)ServerState.WaitingPlayers)
|
||||
return darkened ? Color.LimeGreen : Color.Lime;
|
||||
|
||||
if (game.State == (int)ServerState.GameStarted)
|
||||
return darkened ? Color.Chocolate : Color.Orange;
|
||||
|
||||
return label.TextColor;
|
||||
}
|
||||
|
||||
bool Filtered(GameServer game)
|
||||
{
|
||||
if ((game.State == (int)ServerState.GameStarted) && !showStarted)
|
||||
return true;
|
||||
|
||||
if ((game.State == (int)ServerState.WaitingPlayers) && !showWaiting)
|
||||
return true;
|
||||
|
||||
if ((game.Players == 0) && !showEmpty)
|
||||
return true;
|
||||
|
||||
if (!game.IsCompatible && !showIncompatible)
|
||||
return true;
|
||||
|
||||
if (game.Protected && !showProtected)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
111
OpenRA.Mods.Common/Widgets/Logic/ServerCreationLogic.cs
Normal file
111
OpenRA.Mods.Common/Widgets/Logic/ServerCreationLogic.cs
Normal file
@@ -0,0 +1,111 @@
|
||||
#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.Net;
|
||||
using OpenRA.Widgets;
|
||||
|
||||
namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
{
|
||||
public class ServerCreationLogic
|
||||
{
|
||||
Widget panel;
|
||||
Action onCreate;
|
||||
Action onExit;
|
||||
MapPreview preview = MapCache.UnknownMap;
|
||||
bool advertiseOnline;
|
||||
bool allowPortForward;
|
||||
|
||||
[ObjectCreator.UseCtor]
|
||||
public ServerCreationLogic(Widget widget, Action onExit, Action openLobby)
|
||||
{
|
||||
panel = widget;
|
||||
onCreate = openLobby;
|
||||
this.onExit = onExit;
|
||||
|
||||
var settings = Game.Settings;
|
||||
preview = Game.ModData.MapCache[WidgetUtils.ChooseInitialMap(Game.Settings.Server.Map)];
|
||||
|
||||
panel.Get<ButtonWidget>("BACK_BUTTON").OnClick = () => { Ui.CloseWindow(); onExit(); };
|
||||
panel.Get<ButtonWidget>("CREATE_BUTTON").OnClick = CreateAndJoin;
|
||||
|
||||
var mapButton = panel.GetOrNull<ButtonWidget>("MAP_BUTTON");
|
||||
if (mapButton != null)
|
||||
{
|
||||
panel.Get<ButtonWidget>("MAP_BUTTON").OnClick = () =>
|
||||
{
|
||||
Ui.OpenWindow("MAPCHOOSER_PANEL", new WidgetArgs()
|
||||
{
|
||||
{ "initialMap", preview.Uid },
|
||||
{ "onExit", () => { } },
|
||||
{ "onSelect", (Action<string>)(uid => preview = Game.ModData.MapCache[uid]) }
|
||||
});
|
||||
};
|
||||
|
||||
panel.Get<MapPreviewWidget>("MAP_PREVIEW").Preview = () => preview;
|
||||
panel.Get<LabelWidget>("MAP_NAME").GetText = () => preview.Title;
|
||||
}
|
||||
|
||||
panel.Get<TextFieldWidget>("SERVER_NAME").Text = settings.Server.Name ?? "";
|
||||
panel.Get<TextFieldWidget>("LISTEN_PORT").Text = settings.Server.ListenPort.ToString();
|
||||
advertiseOnline = Game.Settings.Server.AdvertiseOnline;
|
||||
|
||||
var externalPort = panel.Get<TextFieldWidget>("EXTERNAL_PORT");
|
||||
externalPort.Text = settings.Server.ExternalPort.ToString();
|
||||
externalPort.IsDisabled = () => !advertiseOnline;
|
||||
|
||||
var advertiseCheckbox = panel.Get<CheckboxWidget>("ADVERTISE_CHECKBOX");
|
||||
advertiseCheckbox.IsChecked = () => advertiseOnline;
|
||||
advertiseCheckbox.OnClick = () => advertiseOnline ^= true;
|
||||
|
||||
allowPortForward = Game.Settings.Server.AllowPortForward;
|
||||
var checkboxUPnP = panel.Get<CheckboxWidget>("UPNP_CHECKBOX");
|
||||
checkboxUPnP.IsChecked = () => allowPortForward;
|
||||
checkboxUPnP.OnClick = () => allowPortForward ^= true;
|
||||
checkboxUPnP.IsDisabled = () => !Game.Settings.Server.NatDeviceAvailable;
|
||||
|
||||
var passwordField = panel.GetOrNull<PasswordFieldWidget>("PASSWORD");
|
||||
if (passwordField != null)
|
||||
passwordField.Text = Game.Settings.Server.Password;
|
||||
}
|
||||
|
||||
void CreateAndJoin()
|
||||
{
|
||||
var name = panel.Get<TextFieldWidget>("SERVER_NAME").Text;
|
||||
int listenPort, externalPort;
|
||||
if (!Exts.TryParseIntegerInvariant(panel.Get<TextFieldWidget>("LISTEN_PORT").Text, out listenPort))
|
||||
listenPort = 1234;
|
||||
|
||||
if (!Exts.TryParseIntegerInvariant(panel.Get<TextFieldWidget>("EXTERNAL_PORT").Text, out externalPort))
|
||||
externalPort = 1234;
|
||||
|
||||
var passwordField = panel.GetOrNull<PasswordFieldWidget>("PASSWORD");
|
||||
var password = passwordField != null ? passwordField.Text : "";
|
||||
|
||||
// Save new settings
|
||||
Game.Settings.Server.Name = name;
|
||||
Game.Settings.Server.ListenPort = listenPort;
|
||||
Game.Settings.Server.ExternalPort = externalPort;
|
||||
Game.Settings.Server.AdvertiseOnline = advertiseOnline;
|
||||
Game.Settings.Server.AllowPortForward = allowPortForward;
|
||||
Game.Settings.Server.Map = preview.Uid;
|
||||
Game.Settings.Server.Password = password;
|
||||
Game.Settings.Save();
|
||||
|
||||
// Take a copy so that subsequent changes don't affect the server
|
||||
var settings = new ServerSettings(Game.Settings.Server);
|
||||
|
||||
// Create and join the server
|
||||
Game.CreateServer(settings);
|
||||
Ui.CloseWindow();
|
||||
ConnectionLogic.Connect(IPAddress.Loopback.ToString(), Game.Settings.Server.ListenPort, password, onCreate, onExit);
|
||||
}
|
||||
}
|
||||
}
|
||||
78
OpenRA.Mods.Common/Widgets/Logic/TabCompletionLogic.cs
Normal file
78
OpenRA.Mods.Common/Widgets/Logic/TabCompletionLogic.cs
Normal file
@@ -0,0 +1,78 @@
|
||||
#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.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
{
|
||||
public class TabCompletionLogic
|
||||
{
|
||||
IList<string> candidates = new List<string>();
|
||||
int currentCandidateIndex = 0;
|
||||
string lastCompleted;
|
||||
string prefix;
|
||||
string suffix;
|
||||
|
||||
public IList<string> Commands { get; set; }
|
||||
|
||||
public IList<string> Names { get; set; }
|
||||
|
||||
public string Complete(string text)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(text))
|
||||
return text;
|
||||
|
||||
if (lastCompleted == text)
|
||||
{
|
||||
lastCompleted = prefix + candidates[++currentCandidateIndex % candidates.Count] + suffix;
|
||||
return lastCompleted;
|
||||
}
|
||||
|
||||
var toComplete = "";
|
||||
if (text.StartsWith("/") && Commands != null)
|
||||
{
|
||||
prefix = "/";
|
||||
suffix = "";
|
||||
toComplete = text.Substring(1);
|
||||
candidates = Commands.Where(x => x.StartsWith(toComplete, StringComparison.InvariantCultureIgnoreCase)).ToList();
|
||||
}
|
||||
else if (Names != null)
|
||||
{
|
||||
var oneWord = text.Contains(' ');
|
||||
if (oneWord)
|
||||
{
|
||||
prefix = text.Substring(0, text.LastIndexOf(' ') + 1);
|
||||
suffix = "";
|
||||
toComplete = text.Substring(prefix.Length);
|
||||
}
|
||||
else
|
||||
{
|
||||
prefix = "";
|
||||
suffix = ": ";
|
||||
toComplete = text;
|
||||
}
|
||||
|
||||
candidates = Names.Where(x => x.StartsWith(toComplete, StringComparison.InvariantCultureIgnoreCase)).ToList();
|
||||
}
|
||||
else
|
||||
return text;
|
||||
|
||||
currentCandidateIndex = 0;
|
||||
|
||||
if (candidates.Count == 0)
|
||||
return text;
|
||||
|
||||
lastCompleted = prefix + candidates[currentCandidateIndex] + suffix;
|
||||
return lastCompleted;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user