Merge pull request #10916 from pchote/lobby-bots

Support map-defined bot types
This commit is contained in:
abcdefg30
2016-03-18 19:31:05 +01:00
6 changed files with 118 additions and 110 deletions

View File

@@ -81,6 +81,7 @@ namespace OpenRA
Lazy<Ruleset> rules; Lazy<Ruleset> rules;
public Ruleset Rules { get { return rules != null ? rules.Value : null; } } public Ruleset Rules { get { return rules != null ? rules.Value : null; } }
public bool InvalidCustomRules { get; private set; } public bool InvalidCustomRules { get; private set; }
public bool RulesLoaded { get; private set; }
Download download; Download download;
public long DownloadBytes { get; private set; } public long DownloadBytes { get; private set; }
@@ -209,6 +210,9 @@ namespace OpenRA
Status = MapStatus.Unavailable; Status = MapStatus.Unavailable;
} }
// Note: multiple threads may try to access the value at the same time
// We rely on the thread-safety guarantees given by Lazy<T> to prevent race conitions.
// If you're thinking about replacing this, then you must be careful to keep this safe.
rules = Exts.Lazy(() => rules = Exts.Lazy(() =>
{ {
try try
@@ -225,9 +229,12 @@ namespace OpenRA
catch catch
{ {
InvalidCustomRules = true; InvalidCustomRules = true;
return Ruleset.LoadDefaultsForTileSet(modData, TileSet);
}
finally
{
RulesLoaded = true;
} }
return Ruleset.LoadDefaultsForTileSet(modData, TileSet);
}); });
if (p.Contains("map.png")) if (p.Contains("map.png"))

View File

@@ -249,8 +249,6 @@ namespace OpenRA.Widgets
return trimmed; return trimmed;
} }
public static Action Once(Action a) { return () => { if (a != null) { a(); a = null; } }; }
public static string ChooseInitialMap(string initialUid) public static string ChooseInitialMap(string initialUid)
{ {
if (string.IsNullOrEmpty(initialUid) || Game.ModData.MapCache[initialUid].Status != MapStatus.Available) if (string.IsNullOrEmpty(initialUid) || Game.ModData.MapCache[initialUid].Status != MapStatus.Available)

View File

@@ -357,6 +357,8 @@ namespace OpenRA.Mods.Common.Server
// - Observers remain as observers // - Observers remain as observers
// - Players who now lack a slot are made observers // - Players who now lack a slot are made observers
// - Bots who now lack a slot are dropped // - Bots who now lack a slot are dropped
// - Bots who are not defined in the map rules are dropped
var botNames = server.Map.Rules.Actors["player"].TraitInfos<IBotInfo>().Select(t => t.Name);
var slots = server.LobbyInfo.Slots.Keys.ToArray(); var slots = server.LobbyInfo.Slots.Keys.ToArray();
var i = 0; var i = 0;
foreach (var os in oldSlots) foreach (var os in oldSlots)
@@ -370,7 +372,7 @@ namespace OpenRA.Mods.Common.Server
if (c.Slot != null) if (c.Slot != null)
{ {
// Remove Bot from slot if slot forbids bots // Remove Bot from slot if slot forbids bots
if (c.Bot != null && !server.Map.Players.Players[c.Slot].AllowBots) if (c.Bot != null && (!server.Map.Players.Players[c.Slot].AllowBots || !botNames.Contains(c.Bot)))
server.LobbyInfo.Clients.Remove(c); server.LobbyInfo.Clients.Remove(c);
S.SyncClientToPlayerReference(c, server.Map.Players.Players[c.Slot]); S.SyncClientToPlayerReference(c, server.Map.Players.Players[c.Slot]);
} }

View File

@@ -13,7 +13,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Drawing; using System.Drawing;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading.Tasks;
using OpenRA.Chat; using OpenRA.Chat;
using OpenRA.Graphics; using OpenRA.Graphics;
using OpenRA.Mods.Common.Traits; using OpenRA.Mods.Common.Traits;
@@ -27,8 +27,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
{ {
static readonly Action DoNothing = () => { }; static readonly Action DoNothing = () => { };
public MapPreview MapPreview { get; private set; } public MapPreview Map { get; private set; }
public Map Map { get; private set; }
readonly ModData modData; readonly ModData modData;
readonly Action onStart; readonly Action onStart;
@@ -66,6 +65,8 @@ namespace OpenRA.Mods.Common.Widgets.Logic
readonly LabelWidget chatLabel; readonly LabelWidget chatLabel;
bool teamChat; bool teamChat;
bool addBotOnMapLoad;
int lobbyChatUnreadMessages; int lobbyChatUnreadMessages;
int globalChatLastReadMessages; int globalChatLastReadMessages;
int globalChatUnreadMessages; int globalChatUnreadMessages;
@@ -114,7 +115,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
internal LobbyLogic(Widget widget, ModData modData, WorldRenderer worldRenderer, OrderManager orderManager, internal LobbyLogic(Widget widget, ModData modData, WorldRenderer worldRenderer, OrderManager orderManager,
Action onExit, Action onStart, bool skirmishMode) Action onExit, Action onStart, bool skirmishMode)
{ {
MapPreview = MapCache.UnknownMap; Map = MapCache.UnknownMap;
lobby = widget; lobby = widget;
this.modData = modData; this.modData = modData;
this.orderManager = orderManager; this.orderManager = orderManager;
@@ -163,7 +164,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
var gameStarting = false; var gameStarting = false;
Func<bool> configurationDisabled = () => !Game.IsHost || gameStarting || Func<bool> configurationDisabled = () => !Game.IsHost || gameStarting ||
panel == PanelType.Kick || panel == PanelType.ForceStart || panel == PanelType.Kick || panel == PanelType.ForceStart ||
Map == null || Map.InvalidCustomRules || !Map.RulesLoaded || Map.InvalidCustomRules ||
orderManager.LocalClient == null || orderManager.LocalClient.IsReady; orderManager.LocalClient == null || orderManager.LocalClient.IsReady;
var mapButton = lobby.GetOrNull<ButtonWidget>("CHANGEMAP_BUTTON"); var mapButton = lobby.GetOrNull<ButtonWidget>("CHANGEMAP_BUTTON");
@@ -176,7 +177,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
var onSelect = new Action<string>(uid => var onSelect = new Action<string>(uid =>
{ {
// Don't select the same map again // Don't select the same map again
if (uid == MapPreview.Uid) if (uid == Map.Uid)
return; return;
orderManager.IssueOrder(Order.Command("map " + uid)); orderManager.IssueOrder(Order.Command("map " + uid));
@@ -186,7 +187,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
Ui.OpenWindow("MAPCHOOSER_PANEL", new WidgetArgs() Ui.OpenWindow("MAPCHOOSER_PANEL", new WidgetArgs()
{ {
{ "initialMap", MapPreview.Uid }, { "initialMap", Map.Uid },
{ "initialTab", MapClassification.System }, { "initialTab", MapClassification.System },
{ "onExit", DoNothing }, { "onExit", DoNothing },
{ "onSelect", Game.IsHost ? onSelect : null }, { "onSelect", Game.IsHost ? onSelect : null },
@@ -202,9 +203,9 @@ namespace OpenRA.Mods.Common.Widgets.Logic
(orderManager.LobbyInfo.Slots.Values.All(s => !s.AllowBots) && (orderManager.LobbyInfo.Slots.Values.All(s => !s.AllowBots) &&
orderManager.LobbyInfo.Slots.Count(s => !s.Value.LockTeam && orderManager.LobbyInfo.ClientInSlot(s.Key) != null) == 0); orderManager.LobbyInfo.Slots.Count(s => !s.Value.LockTeam && orderManager.LobbyInfo.ClientInSlot(s.Key) != null) == 0);
var botNames = modRules.Actors["player"].TraitInfos<IBotInfo>().Select(t => t.Name);
slotsButton.OnMouseDown = _ => slotsButton.OnMouseDown = _ =>
{ {
var botNames = Map.Rules.Actors["player"].TraitInfos<IBotInfo>().Select(t => t.Name);
var options = new Dictionary<string, IEnumerable<DropDownOption>>(); var options = new Dictionary<string, IEnumerable<DropDownOption>>();
var botController = orderManager.LobbyInfo.Clients.FirstOrDefault(c => c.IsAdmin); var botController = orderManager.LobbyInfo.Clients.FirstOrDefault(c => c.IsAdmin);
@@ -302,7 +303,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
var optionsTab = lobby.Get<ButtonWidget>("OPTIONS_TAB"); var optionsTab = lobby.Get<ButtonWidget>("OPTIONS_TAB");
optionsTab.IsHighlighted = () => panel == PanelType.Options; optionsTab.IsHighlighted = () => panel == PanelType.Options;
optionsTab.IsDisabled = () => Map == null || Map.InvalidCustomRules || panel == PanelType.Kick || panel == PanelType.ForceStart; optionsTab.IsDisabled = () => !Map.RulesLoaded || Map.InvalidCustomRules || panel == PanelType.Kick || panel == PanelType.ForceStart;
optionsTab.OnClick = () => panel = PanelType.Options; optionsTab.OnClick = () => panel = PanelType.Options;
var playersTab = lobby.Get<ButtonWidget>("PLAYERS_TAB"); var playersTab = lobby.Get<ButtonWidget>("PLAYERS_TAB");
@@ -349,7 +350,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
var allowCheats = optionsBin.GetOrNull<CheckboxWidget>("ALLOWCHEATS_CHECKBOX"); var allowCheats = optionsBin.GetOrNull<CheckboxWidget>("ALLOWCHEATS_CHECKBOX");
if (allowCheats != null) if (allowCheats != null)
{ {
var cheatsLocked = new CachedTransform<Map, bool>( var cheatsLocked = new CachedTransform<MapPreview, bool>(
map => map.Rules.Actors["player"].TraitInfo<DeveloperModeInfo>().Locked); map => map.Rules.Actors["player"].TraitInfo<DeveloperModeInfo>().Locked);
allowCheats.IsChecked = () => orderManager.LobbyInfo.GlobalSettings.AllowCheats; allowCheats.IsChecked = () => orderManager.LobbyInfo.GlobalSettings.AllowCheats;
@@ -361,7 +362,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
var crates = optionsBin.GetOrNull<CheckboxWidget>("CRATES_CHECKBOX"); var crates = optionsBin.GetOrNull<CheckboxWidget>("CRATES_CHECKBOX");
if (crates != null) if (crates != null)
{ {
var cratesLocked = new CachedTransform<Map, bool>(map => var cratesLocked = new CachedTransform<MapPreview, bool>(map =>
{ {
var crateSpawner = map.Rules.Actors["world"].TraitInfoOrDefault<CrateSpawnerInfo>(); var crateSpawner = map.Rules.Actors["world"].TraitInfoOrDefault<CrateSpawnerInfo>();
return crateSpawner == null || crateSpawner.Locked; return crateSpawner == null || crateSpawner.Locked;
@@ -376,7 +377,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
var creeps = optionsBin.GetOrNull<CheckboxWidget>("CREEPS_CHECKBOX"); var creeps = optionsBin.GetOrNull<CheckboxWidget>("CREEPS_CHECKBOX");
if (creeps != null) if (creeps != null)
{ {
var creepsLocked = new CachedTransform<Map, bool>(map => var creepsLocked = new CachedTransform<MapPreview, bool>(map =>
{ {
var mapCreeps = map.Rules.Actors["world"].TraitInfoOrDefault<MapCreepsInfo>(); var mapCreeps = map.Rules.Actors["world"].TraitInfoOrDefault<MapCreepsInfo>();
return mapCreeps == null || mapCreeps.Locked; return mapCreeps == null || mapCreeps.Locked;
@@ -391,7 +392,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
var allybuildradius = optionsBin.GetOrNull<CheckboxWidget>("ALLYBUILDRADIUS_CHECKBOX"); var allybuildradius = optionsBin.GetOrNull<CheckboxWidget>("ALLYBUILDRADIUS_CHECKBOX");
if (allybuildradius != null) if (allybuildradius != null)
{ {
var allyBuildRadiusLocked = new CachedTransform<Map, bool>(map => var allyBuildRadiusLocked = new CachedTransform<MapPreview, bool>(map =>
{ {
var mapBuildRadius = map.Rules.Actors["world"].TraitInfoOrDefault<MapBuildRadiusInfo>(); var mapBuildRadius = map.Rules.Actors["world"].TraitInfoOrDefault<MapBuildRadiusInfo>();
return mapBuildRadius == null || mapBuildRadius.AllyBuildRadiusLocked; return mapBuildRadius == null || mapBuildRadius.AllyBuildRadiusLocked;
@@ -406,7 +407,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
var shortGame = optionsBin.GetOrNull<CheckboxWidget>("SHORTGAME_CHECKBOX"); var shortGame = optionsBin.GetOrNull<CheckboxWidget>("SHORTGAME_CHECKBOX");
if (shortGame != null) if (shortGame != null)
{ {
var shortGameLocked = new CachedTransform<Map, bool>( var shortGameLocked = new CachedTransform<MapPreview, bool>(
map => map.Rules.Actors["world"].TraitInfo<MapOptionsInfo>().ShortGameLocked); map => map.Rules.Actors["world"].TraitInfo<MapOptionsInfo>().ShortGameLocked);
shortGame.IsChecked = () => orderManager.LobbyInfo.GlobalSettings.ShortGame; shortGame.IsChecked = () => orderManager.LobbyInfo.GlobalSettings.ShortGame;
@@ -418,10 +419,10 @@ namespace OpenRA.Mods.Common.Widgets.Logic
var difficulty = optionsBin.GetOrNull<DropDownButtonWidget>("DIFFICULTY_DROPDOWNBUTTON"); var difficulty = optionsBin.GetOrNull<DropDownButtonWidget>("DIFFICULTY_DROPDOWNBUTTON");
if (difficulty != null) if (difficulty != null)
{ {
var mapOptions = new CachedTransform<Map, MapOptionsInfo>( var mapOptions = new CachedTransform<MapPreview, MapOptionsInfo>(
map => map.Rules.Actors["world"].TraitInfo<MapOptionsInfo>()); map => map.Rules.Actors["world"].TraitInfo<MapOptionsInfo>());
difficulty.IsVisible = () => Map != null && mapOptions.Update(Map).Difficulties.Any(); difficulty.IsVisible = () => Map.RulesLoaded && mapOptions.Update(Map).Difficulties.Any();
difficulty.IsDisabled = () => configurationDisabled() || mapOptions.Update(Map).DifficultyLocked; difficulty.IsDisabled = () => configurationDisabled() || mapOptions.Update(Map).DifficultyLocked;
difficulty.GetText = () => orderManager.LobbyInfo.GlobalSettings.Difficulty; difficulty.GetText = () => orderManager.LobbyInfo.GlobalSettings.Difficulty;
difficulty.OnMouseDown = _ => difficulty.OnMouseDown = _ =>
@@ -447,10 +448,10 @@ namespace OpenRA.Mods.Common.Widgets.Logic
var startingUnits = optionsBin.GetOrNull<DropDownButtonWidget>("STARTINGUNITS_DROPDOWNBUTTON"); var startingUnits = optionsBin.GetOrNull<DropDownButtonWidget>("STARTINGUNITS_DROPDOWNBUTTON");
if (startingUnits != null) if (startingUnits != null)
{ {
var startUnitsInfos = new CachedTransform<Map, IEnumerable<MPStartUnitsInfo>>( var startUnitsInfos = new CachedTransform<MapPreview, IEnumerable<MPStartUnitsInfo>>(
map => map.Rules.Actors["world"].TraitInfos<MPStartUnitsInfo>()); map => map.Rules.Actors["world"].TraitInfos<MPStartUnitsInfo>());
var startUnitsLocked = new CachedTransform<Map, bool>(map => var startUnitsLocked = new CachedTransform<MapPreview, bool>(map =>
{ {
var spawnUnitsInfo = map.Rules.Actors["world"].TraitInfoOrDefault<SpawnMPUnitsInfo>(); var spawnUnitsInfo = map.Rules.Actors["world"].TraitInfoOrDefault<SpawnMPUnitsInfo>();
return spawnUnitsInfo == null || spawnUnitsInfo.Locked; return spawnUnitsInfo == null || spawnUnitsInfo.Locked;
@@ -463,8 +464,8 @@ namespace OpenRA.Mods.Common.Widgets.Logic
}; };
startingUnits.IsDisabled = () => configurationDisabled() || startUnitsLocked.Update(Map); startingUnits.IsDisabled = () => configurationDisabled() || startUnitsLocked.Update(Map);
startingUnits.GetText = () => MapPreview.Status != MapStatus.Available || startingUnits.GetText = () => Map.Status != MapStatus.Available ||
Map == null || startUnitsLocked.Update(Map) ? "Not Available" : className(orderManager.LobbyInfo.GlobalSettings.StartingUnitsClass); !Map.RulesLoaded || startUnitsLocked.Update(Map) ? "Not Available" : className(orderManager.LobbyInfo.GlobalSettings.StartingUnitsClass);
startingUnits.OnMouseDown = _ => startingUnits.OnMouseDown = _ =>
{ {
var classes = startUnitsInfos.Update(Map).Select(a => a.Class).Distinct(); var classes = startUnitsInfos.Update(Map).Select(a => a.Class).Distinct();
@@ -491,12 +492,12 @@ namespace OpenRA.Mods.Common.Widgets.Logic
var startingCash = optionsBin.GetOrNull<DropDownButtonWidget>("STARTINGCASH_DROPDOWNBUTTON"); var startingCash = optionsBin.GetOrNull<DropDownButtonWidget>("STARTINGCASH_DROPDOWNBUTTON");
if (startingCash != null) if (startingCash != null)
{ {
var playerResources = new CachedTransform<Map, PlayerResourcesInfo>( var playerResources = new CachedTransform<MapPreview, PlayerResourcesInfo>(
map => map.Rules.Actors["player"].TraitInfo<PlayerResourcesInfo>()); map => map.Rules.Actors["player"].TraitInfo<PlayerResourcesInfo>());
startingCash.IsDisabled = () => configurationDisabled() || playerResources.Update(Map).DefaultCashLocked; startingCash.IsDisabled = () => configurationDisabled() || playerResources.Update(Map).DefaultCashLocked;
startingCash.GetText = () => MapPreview.Status != MapStatus.Available || startingCash.GetText = () => Map.Status != MapStatus.Available ||
Map == null || playerResources.Update(Map).DefaultCashLocked ? "Not Available" : "${0}".F(orderManager.LobbyInfo.GlobalSettings.StartingCash); !Map.RulesLoaded || playerResources.Update(Map).DefaultCashLocked ? "Not Available" : "${0}".F(orderManager.LobbyInfo.GlobalSettings.StartingCash);
startingCash.OnMouseDown = _ => startingCash.OnMouseDown = _ =>
{ {
var options = playerResources.Update(Map).SelectableCash.Select(c => new DropDownOption var options = playerResources.Update(Map).SelectableCash.Select(c => new DropDownOption
@@ -520,20 +521,20 @@ namespace OpenRA.Mods.Common.Widgets.Logic
var techLevel = optionsBin.GetOrNull<DropDownButtonWidget>("TECHLEVEL_DROPDOWNBUTTON"); var techLevel = optionsBin.GetOrNull<DropDownButtonWidget>("TECHLEVEL_DROPDOWNBUTTON");
if (techLevel != null) if (techLevel != null)
{ {
var mapOptions = new CachedTransform<Map, MapOptionsInfo>( var mapOptions = new CachedTransform<MapPreview, MapOptionsInfo>(
map => map.Rules.Actors["world"].TraitInfo<MapOptionsInfo>()); map => map.Rules.Actors["world"].TraitInfo<MapOptionsInfo>());
var techLevels = new CachedTransform<Map, List<ProvidesTechPrerequisiteInfo>>( var techLevels = new CachedTransform<MapPreview, List<ProvidesTechPrerequisiteInfo>>(
map => map.Rules.Actors["player"].TraitInfos<ProvidesTechPrerequisiteInfo>().ToList()); map => map.Rules.Actors["player"].TraitInfos<ProvidesTechPrerequisiteInfo>().ToList());
techLevel.IsVisible = () => Map != null && techLevels.Update(Map).Any(); techLevel.IsVisible = () => Map.RulesLoaded && techLevels.Update(Map).Any();
var techLevelDescription = optionsBin.GetOrNull<LabelWidget>("TECHLEVEL_DESC"); var techLevelDescription = optionsBin.GetOrNull<LabelWidget>("TECHLEVEL_DESC");
if (techLevelDescription != null) if (techLevelDescription != null)
techLevelDescription.IsVisible = techLevel.IsVisible; techLevelDescription.IsVisible = techLevel.IsVisible;
techLevel.IsDisabled = () => configurationDisabled() || mapOptions.Update(Map).TechLevelLocked; techLevel.IsDisabled = () => configurationDisabled() || mapOptions.Update(Map).TechLevelLocked;
techLevel.GetText = () => MapPreview.Status != MapStatus.Available || techLevel.GetText = () => Map.Status != MapStatus.Available ||
Map == null || mapOptions.Update(Map).TechLevelLocked ? "Not Available" : "{0}".F(orderManager.LobbyInfo.GlobalSettings.TechLevel); !Map.RulesLoaded || mapOptions.Update(Map).TechLevelLocked ? "Not Available" : "{0}".F(orderManager.LobbyInfo.GlobalSettings.TechLevel);
techLevel.OnMouseDown = _ => techLevel.OnMouseDown = _ =>
{ {
var options = techLevels.Update(Map).Select(c => new DropDownOption var options = techLevels.Update(Map).Select(c => new DropDownOption
@@ -562,7 +563,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
gameSpeed.IsDisabled = configurationDisabled; gameSpeed.IsDisabled = configurationDisabled;
gameSpeed.GetText = () => gameSpeed.GetText = () =>
{ {
if (MapPreview.Status != MapStatus.Available) if (Map.Status != MapStatus.Available)
return "Not Available"; return "Not Available";
GameSpeed speed; GameSpeed speed;
@@ -595,7 +596,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
var exploredMap = optionsBin.GetOrNull<CheckboxWidget>("EXPLORED_MAP_CHECKBOX"); var exploredMap = optionsBin.GetOrNull<CheckboxWidget>("EXPLORED_MAP_CHECKBOX");
if (exploredMap != null) if (exploredMap != null)
{ {
var exploredMapLocked = new CachedTransform<Map, bool>( var exploredMapLocked = new CachedTransform<MapPreview, bool>(
map => map.Rules.Actors["player"].TraitInfo<ShroudInfo>().ExploredMapLocked); map => map.Rules.Actors["player"].TraitInfo<ShroudInfo>().ExploredMapLocked);
exploredMap.IsChecked = () => !orderManager.LobbyInfo.GlobalSettings.Shroud; exploredMap.IsChecked = () => !orderManager.LobbyInfo.GlobalSettings.Shroud;
@@ -607,7 +608,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
var enableFog = optionsBin.GetOrNull<CheckboxWidget>("FOG_CHECKBOX"); var enableFog = optionsBin.GetOrNull<CheckboxWidget>("FOG_CHECKBOX");
if (enableFog != null) if (enableFog != null)
{ {
var fogLocked = new CachedTransform<Map, bool>( var fogLocked = new CachedTransform<MapPreview, bool>(
map => map.Rules.Actors["player"].TraitInfo<ShroudInfo>().FogLocked); map => map.Rules.Actors["player"].TraitInfo<ShroudInfo>().FogLocked);
enableFog.IsChecked = () => orderManager.LobbyInfo.GlobalSettings.Fog; enableFog.IsChecked = () => orderManager.LobbyInfo.GlobalSettings.Fog;
@@ -712,16 +713,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
// Add a bot on the first lobbyinfo update // Add a bot on the first lobbyinfo update
if (skirmishMode) if (skirmishMode)
{ addBotOnMapLoad = true;
Game.LobbyInfoChanged += WidgetUtils.Once(() =>
{
var slot = orderManager.LobbyInfo.FirstEmptyBotSlot();
var bot = modRules.Actors["player"].TraitInfos<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)));
});
}
} }
public override void Tick() public override void Tick()
@@ -787,28 +779,37 @@ namespace OpenRA.Mods.Common.Widgets.Logic
void UpdateCurrentMap() void UpdateCurrentMap()
{ {
var uid = orderManager.LobbyInfo.GlobalSettings.Map; var uid = orderManager.LobbyInfo.GlobalSettings.Map;
if (MapPreview.Uid == uid) if (Map.Uid == uid)
return; return;
MapPreview = modData.MapCache[uid]; Map = modData.MapCache[uid];
Map = null; if (Map.Status == MapStatus.Available)
if (MapPreview.Status == MapStatus.Available)
{ {
// Maps need to be validated and pre-loaded before they can be accessed // Maps need to be validated and pre-loaded before they can be accessed
new Thread(_ => var currentMap = Map;
new Task(() =>
{ {
var currentMap = Map = new Map(modData, MapPreview.Package); // Force map rules to be loaded on this background thread
currentMap.PreloadRules(); var unused = currentMap.Rules;
Game.RunAfterTick(() => Game.RunAfterTick(() =>
{ {
// Map may have changed in the meantime // Map may have changed in the meantime
if (currentMap != Map) if (currentMap != Map)
return; return;
// Tell the server that we have the map
if (!currentMap.InvalidCustomRules) if (!currentMap.InvalidCustomRules)
{
// Tell the server that we have the map
orderManager.IssueOrder(Order.Command("state {0}".F(Session.ClientState.NotReady))); orderManager.IssueOrder(Order.Command("state {0}".F(Session.ClientState.NotReady)));
if (addBotOnMapLoad)
{
var slot = orderManager.LobbyInfo.FirstEmptyBotSlot();
var bot = currentMap.Rules.Actors["player"].TraitInfos<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)));
addBotOnMapLoad = false;
} }
}); });
}).Start(); }).Start();
@@ -841,7 +842,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
template = emptySlotTemplate.Clone(); template = emptySlotTemplate.Clone();
if (Game.IsHost) if (Game.IsHost)
LobbyUtils.SetupEditableSlotWidget(template, slot, client, orderManager, modRules); LobbyUtils.SetupEditableSlotWidget(this, template, slot, client, orderManager);
else else
LobbyUtils.SetupSlotWidget(template, slot, client); LobbyUtils.SetupSlotWidget(template, slot, client);
@@ -860,15 +861,15 @@ namespace OpenRA.Mods.Common.Widgets.Logic
LobbyUtils.SetupClientWidget(template, client, orderManager, client.Bot == null); LobbyUtils.SetupClientWidget(template, client, orderManager, client.Bot == null);
if (client.Bot != null) if (client.Bot != null)
LobbyUtils.SetupEditableSlotWidget(template, slot, client, orderManager, modRules); LobbyUtils.SetupEditableSlotWidget(this, template, slot, client, orderManager);
else else
LobbyUtils.SetupEditableNameWidget(template, slot, client, orderManager); LobbyUtils.SetupEditableNameWidget(template, slot, client, orderManager);
LobbyUtils.SetupEditableColorWidget(template, slot, client, orderManager, shellmapWorld, colorPreview); LobbyUtils.SetupEditableColorWidget(template, slot, client, orderManager, shellmapWorld, colorPreview);
LobbyUtils.SetupEditableFactionWidget(template, slot, client, orderManager, factions); LobbyUtils.SetupEditableFactionWidget(template, slot, client, orderManager, factions);
LobbyUtils.SetupEditableTeamWidget(template, slot, client, orderManager, MapPreview); LobbyUtils.SetupEditableTeamWidget(template, slot, client, orderManager, Map);
LobbyUtils.SetupEditableSpawnWidget(template, slot, client, orderManager, MapPreview); LobbyUtils.SetupEditableSpawnWidget(template, slot, client, orderManager, Map);
LobbyUtils.SetupEditableReadyWidget(template, slot, client, orderManager, MapPreview, Map == null || Map.InvalidCustomRules); LobbyUtils.SetupEditableReadyWidget(template, slot, client, orderManager, Map, !Map.RulesLoaded || Map.InvalidCustomRules);
} }
else else
{ {

View File

@@ -26,80 +26,80 @@ namespace OpenRA.Mods.Common.Widgets.Logic
var available = widget.GetOrNull("MAP_AVAILABLE"); var available = widget.GetOrNull("MAP_AVAILABLE");
if (available != null) if (available != null)
{ {
available.IsVisible = () => lobby.MapPreview.Status == MapStatus.Available && (lobby.Map == null || !lobby.Map.InvalidCustomRules); available.IsVisible = () => lobby.Map.Status == MapStatus.Available && (!lobby.Map.RulesLoaded || !lobby.Map.InvalidCustomRules);
var preview = available.Get<MapPreviewWidget>("MAP_PREVIEW"); var preview = available.Get<MapPreviewWidget>("MAP_PREVIEW");
preview.Preview = () => lobby.MapPreview; preview.Preview = () => lobby.Map;
preview.OnMouseDown = mi => LobbyUtils.SelectSpawnPoint(orderManager, preview, lobby.MapPreview, mi); preview.OnMouseDown = mi => LobbyUtils.SelectSpawnPoint(orderManager, preview, lobby.Map, mi);
preview.SpawnOccupants = () => LobbyUtils.GetSpawnOccupants(orderManager.LobbyInfo, lobby.MapPreview); preview.SpawnOccupants = () => LobbyUtils.GetSpawnOccupants(orderManager.LobbyInfo, lobby.Map);
var titleLabel = available.GetOrNull<LabelWidget>("MAP_TITLE"); var titleLabel = available.GetOrNull<LabelWidget>("MAP_TITLE");
if (titleLabel != null) if (titleLabel != null)
{ {
var font = Game.Renderer.Fonts[titleLabel.Font]; var font = Game.Renderer.Fonts[titleLabel.Font];
var title = new CachedTransform<MapPreview, string>(m => WidgetUtils.TruncateText(m.Title, titleLabel.Bounds.Width, font)); var title = new CachedTransform<MapPreview, string>(m => WidgetUtils.TruncateText(m.Title, titleLabel.Bounds.Width, font));
titleLabel.GetText = () => title.Update(lobby.MapPreview); titleLabel.GetText = () => title.Update(lobby.Map);
} }
var typeLabel = available.GetOrNull<LabelWidget>("MAP_TYPE"); var typeLabel = available.GetOrNull<LabelWidget>("MAP_TYPE");
if (typeLabel != null) if (typeLabel != null)
typeLabel.GetText = () => lobby.MapPreview.Type; typeLabel.GetText = () => lobby.Map.Type;
var authorLabel = available.GetOrNull<LabelWidget>("MAP_AUTHOR"); var authorLabel = available.GetOrNull<LabelWidget>("MAP_AUTHOR");
if (authorLabel != null) if (authorLabel != null)
{ {
var font = Game.Renderer.Fonts[authorLabel.Font]; var font = Game.Renderer.Fonts[authorLabel.Font];
var author = new CachedTransform<MapPreview, string>( var author = new CachedTransform<MapPreview, string>(
m => WidgetUtils.TruncateText("Created by {0}".F(lobby.MapPreview.Author), authorLabel.Bounds.Width, font)); m => WidgetUtils.TruncateText("Created by {0}".F(lobby.Map.Author), authorLabel.Bounds.Width, font));
authorLabel.GetText = () => author.Update(lobby.MapPreview); authorLabel.GetText = () => author.Update(lobby.Map);
} }
} }
var invalid = widget.GetOrNull("MAP_INVALID"); var invalid = widget.GetOrNull("MAP_INVALID");
if (invalid != null) if (invalid != null)
{ {
invalid.IsVisible = () => lobby.MapPreview.Status == MapStatus.Available && lobby.Map != null && lobby.Map.InvalidCustomRules; invalid.IsVisible = () => lobby.Map.Status == MapStatus.Available && lobby.Map.InvalidCustomRules;
var preview = invalid.Get<MapPreviewWidget>("MAP_PREVIEW"); var preview = invalid.Get<MapPreviewWidget>("MAP_PREVIEW");
preview.Preview = () => lobby.MapPreview; preview.Preview = () => lobby.Map;
preview.OnMouseDown = mi => LobbyUtils.SelectSpawnPoint(orderManager, preview, lobby.MapPreview, mi); preview.OnMouseDown = mi => LobbyUtils.SelectSpawnPoint(orderManager, preview, lobby.Map, mi);
preview.SpawnOccupants = () => LobbyUtils.GetSpawnOccupants(orderManager.LobbyInfo, lobby.MapPreview); preview.SpawnOccupants = () => LobbyUtils.GetSpawnOccupants(orderManager.LobbyInfo, lobby.Map);
var title = invalid.GetOrNull<LabelWidget>("MAP_TITLE"); var title = invalid.GetOrNull<LabelWidget>("MAP_TITLE");
if (title != null) if (title != null)
title.GetText = () => lobby.MapPreview.Title; title.GetText = () => lobby.Map.Title;
var type = invalid.GetOrNull<LabelWidget>("MAP_TYPE"); var type = invalid.GetOrNull<LabelWidget>("MAP_TYPE");
if (type != null) if (type != null)
type.GetText = () => lobby.MapPreview.Type; type.GetText = () => lobby.Map.Type;
} }
var download = widget.GetOrNull("MAP_DOWNLOADABLE"); var download = widget.GetOrNull("MAP_DOWNLOADABLE");
if (download != null) if (download != null)
{ {
download.IsVisible = () => lobby.MapPreview.Status == MapStatus.DownloadAvailable; download.IsVisible = () => lobby.Map.Status == MapStatus.DownloadAvailable;
var preview = download.Get<MapPreviewWidget>("MAP_PREVIEW"); var preview = download.Get<MapPreviewWidget>("MAP_PREVIEW");
preview.Preview = () => lobby.MapPreview; preview.Preview = () => lobby.Map;
preview.OnMouseDown = mi => LobbyUtils.SelectSpawnPoint(orderManager, preview, lobby.MapPreview, mi); preview.OnMouseDown = mi => LobbyUtils.SelectSpawnPoint(orderManager, preview, lobby.Map, mi);
preview.SpawnOccupants = () => LobbyUtils.GetSpawnOccupants(orderManager.LobbyInfo, lobby.MapPreview); preview.SpawnOccupants = () => LobbyUtils.GetSpawnOccupants(orderManager.LobbyInfo, lobby.Map);
var title = download.GetOrNull<LabelWidget>("MAP_TITLE"); var title = download.GetOrNull<LabelWidget>("MAP_TITLE");
if (title != null) if (title != null)
title.GetText = () => lobby.MapPreview.Title; title.GetText = () => lobby.Map.Title;
var type = download.GetOrNull<LabelWidget>("MAP_TYPE"); var type = download.GetOrNull<LabelWidget>("MAP_TYPE");
if (type != null) if (type != null)
type.GetText = () => lobby.MapPreview.Type; type.GetText = () => lobby.Map.Type;
var author = download.GetOrNull<LabelWidget>("MAP_AUTHOR"); var author = download.GetOrNull<LabelWidget>("MAP_AUTHOR");
if (author != null) if (author != null)
author.GetText = () => "Created by {0}".F(lobby.MapPreview.Author); author.GetText = () => "Created by {0}".F(lobby.Map.Author);
var install = download.GetOrNull<ButtonWidget>("MAP_INSTALL"); var install = download.GetOrNull<ButtonWidget>("MAP_INSTALL");
if (install != null) if (install != null)
{ {
install.OnClick = () => lobby.MapPreview.Install(); install.OnClick = () => lobby.Map.Install();
install.IsHighlighted = () => installHighlighted; install.IsHighlighted = () => installHighlighted;
} }
} }
@@ -107,72 +107,72 @@ namespace OpenRA.Mods.Common.Widgets.Logic
var progress = widget.GetOrNull("MAP_PROGRESS"); var progress = widget.GetOrNull("MAP_PROGRESS");
if (progress != null) if (progress != null)
{ {
progress.IsVisible = () => lobby.MapPreview.Status != MapStatus.Available && progress.IsVisible = () => lobby.Map.Status != MapStatus.Available &&
lobby.MapPreview.Status != MapStatus.DownloadAvailable; lobby.Map.Status != MapStatus.DownloadAvailable;
var preview = progress.Get<MapPreviewWidget>("MAP_PREVIEW"); var preview = progress.Get<MapPreviewWidget>("MAP_PREVIEW");
preview.Preview = () => lobby.MapPreview; preview.Preview = () => lobby.Map;
preview.OnMouseDown = mi => LobbyUtils.SelectSpawnPoint(orderManager, preview, lobby.MapPreview, mi); preview.OnMouseDown = mi => LobbyUtils.SelectSpawnPoint(orderManager, preview, lobby.Map, mi);
preview.SpawnOccupants = () => LobbyUtils.GetSpawnOccupants(orderManager.LobbyInfo, lobby.MapPreview); preview.SpawnOccupants = () => LobbyUtils.GetSpawnOccupants(orderManager.LobbyInfo, lobby.Map);
var title = progress.GetOrNull<LabelWidget>("MAP_TITLE"); var title = progress.GetOrNull<LabelWidget>("MAP_TITLE");
if (title != null) if (title != null)
title.GetText = () => lobby.MapPreview.Title; title.GetText = () => lobby.Map.Title;
var type = progress.GetOrNull<LabelWidget>("MAP_TYPE"); var type = progress.GetOrNull<LabelWidget>("MAP_TYPE");
if (type != null) if (type != null)
type.GetText = () => lobby.MapPreview.Type; type.GetText = () => lobby.Map.Type;
var statusSearching = progress.GetOrNull("MAP_STATUS_SEARCHING"); var statusSearching = progress.GetOrNull("MAP_STATUS_SEARCHING");
if (statusSearching != null) if (statusSearching != null)
statusSearching.IsVisible = () => lobby.MapPreview.Status == MapStatus.Searching; statusSearching.IsVisible = () => lobby.Map.Status == MapStatus.Searching;
var statusUnavailable = progress.GetOrNull("MAP_STATUS_UNAVAILABLE"); var statusUnavailable = progress.GetOrNull("MAP_STATUS_UNAVAILABLE");
if (statusUnavailable != null) if (statusUnavailable != null)
statusUnavailable.IsVisible = () => lobby.MapPreview.Status == MapStatus.Unavailable; statusUnavailable.IsVisible = () => lobby.Map.Status == MapStatus.Unavailable;
var statusError = progress.GetOrNull("MAP_STATUS_ERROR"); var statusError = progress.GetOrNull("MAP_STATUS_ERROR");
if (statusError != null) if (statusError != null)
statusError.IsVisible = () => lobby.MapPreview.Status == MapStatus.DownloadError; statusError.IsVisible = () => lobby.Map.Status == MapStatus.DownloadError;
var statusDownloading = progress.GetOrNull<LabelWidget>("MAP_STATUS_DOWNLOADING"); var statusDownloading = progress.GetOrNull<LabelWidget>("MAP_STATUS_DOWNLOADING");
if (statusDownloading != null) if (statusDownloading != null)
{ {
statusDownloading.IsVisible = () => lobby.MapPreview.Status == MapStatus.Downloading; statusDownloading.IsVisible = () => lobby.Map.Status == MapStatus.Downloading;
statusDownloading.GetText = () => statusDownloading.GetText = () =>
{ {
if (lobby.MapPreview.DownloadBytes == 0) if (lobby.Map.DownloadBytes == 0)
return "Connecting..."; return "Connecting...";
// Server does not provide the total file length // Server does not provide the total file length
if (lobby.MapPreview.DownloadPercentage == 0) if (lobby.Map.DownloadPercentage == 0)
return "Downloading {0} kB".F(lobby.MapPreview.DownloadBytes / 1024); return "Downloading {0} kB".F(lobby.Map.DownloadBytes / 1024);
return "Downloading {0} kB ({1}%)".F(lobby.MapPreview.DownloadBytes / 1024, lobby.MapPreview.DownloadPercentage); return "Downloading {0} kB ({1}%)".F(lobby.Map.DownloadBytes / 1024, lobby.Map.DownloadPercentage);
}; };
} }
var retry = progress.GetOrNull<ButtonWidget>("MAP_RETRY"); var retry = progress.GetOrNull<ButtonWidget>("MAP_RETRY");
if (retry != null) if (retry != null)
{ {
retry.IsVisible = () => (lobby.MapPreview.Status == MapStatus.DownloadError || lobby.MapPreview.Status == MapStatus.Unavailable) && retry.IsVisible = () => (lobby.Map.Status == MapStatus.DownloadError || lobby.Map.Status == MapStatus.Unavailable) &&
lobby.MapPreview != MapCache.UnknownMap; lobby.Map != MapCache.UnknownMap;
retry.OnClick = () => retry.OnClick = () =>
{ {
if (lobby.MapPreview.Status == MapStatus.DownloadError) if (lobby.Map.Status == MapStatus.DownloadError)
lobby.MapPreview.Install(); lobby.Map.Install();
else if (lobby.MapPreview.Status == MapStatus.Unavailable) else if (lobby.Map.Status == MapStatus.Unavailable)
modData.MapCache.QueryRemoteMapDetails(new[] { lobby.MapPreview.Uid }); modData.MapCache.QueryRemoteMapDetails(new[] { lobby.Map.Uid });
}; };
retry.GetText = () => lobby.MapPreview.Status == MapStatus.DownloadError ? "Retry Install" : "Retry Search"; retry.GetText = () => lobby.Map.Status == MapStatus.DownloadError ? "Retry Install" : "Retry Search";
} }
var progressbar = progress.GetOrNull<ProgressBarWidget>("MAP_PROGRESSBAR"); var progressbar = progress.GetOrNull<ProgressBarWidget>("MAP_PROGRESSBAR");
if (progressbar != null) if (progressbar != null)
{ {
progressbar.IsIndeterminate = () => lobby.MapPreview.DownloadPercentage == 0; progressbar.IsIndeterminate = () => lobby.Map.DownloadPercentage == 0;
progressbar.GetPercentage = () => lobby.MapPreview.DownloadPercentage; progressbar.GetPercentage = () => lobby.Map.DownloadPercentage;
progressbar.IsVisible = () => !retry.IsVisible(); progressbar.IsVisible = () => !retry.IsVisible();
} }
} }

View File

@@ -38,7 +38,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
} }
} }
public static void ShowSlotDropDown(Ruleset rules, DropDownButtonWidget dropdown, Session.Slot slot, public static void ShowSlotDropDown(LobbyLogic logic, DropDownButtonWidget dropdown, Session.Slot slot,
Session.Client client, OrderManager orderManager) Session.Client client, OrderManager orderManager)
{ {
var options = new Dictionary<string, IEnumerable<SlotDropDownOption>>() { { "Slot", new List<SlotDropDownOption>() var options = new Dictionary<string, IEnumerable<SlotDropDownOption>>() { { "Slot", new List<SlotDropDownOption>()
@@ -50,7 +50,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
var bots = new List<SlotDropDownOption>(); var bots = new List<SlotDropDownOption>();
if (slot.AllowBots) if (slot.AllowBots)
{ {
foreach (var b in rules.Actors["player"].TraitInfos<IBotInfo>().Select(t => t.Name)) foreach (var b in logic.Map.Rules.Actors["player"].TraitInfos<IBotInfo>().Select(t => t.Name))
{ {
var bot = b; var bot = b;
var botController = orderManager.LobbyInfo.Clients.FirstOrDefault(c => c.IsAdmin); var botController = orderManager.LobbyInfo.Clients.FirstOrDefault(c => c.IsAdmin);
@@ -303,13 +303,13 @@ namespace OpenRA.Mods.Common.Widgets.Logic
name.GetText = () => label; name.GetText = () => label;
} }
public static void SetupEditableSlotWidget(Widget parent, Session.Slot s, Session.Client c, OrderManager orderManager, Ruleset rules) public static void SetupEditableSlotWidget(LobbyLogic logic, Widget parent, Session.Slot s, Session.Client c, OrderManager orderManager)
{ {
var slot = parent.Get<DropDownButtonWidget>("SLOT_OPTIONS"); var slot = parent.Get<DropDownButtonWidget>("SLOT_OPTIONS");
slot.IsVisible = () => true; slot.IsVisible = () => true;
slot.IsDisabled = () => orderManager.LocalClient.IsReady; slot.IsDisabled = () => orderManager.LocalClient.IsReady;
slot.GetText = () => c != null ? c.Name : s.Closed ? "Closed" : "Open"; slot.GetText = () => c != null ? c.Name : s.Closed ? "Closed" : "Open";
slot.OnMouseDown = _ => ShowSlotDropDown(rules, slot, s, c, orderManager); slot.OnMouseDown = _ => ShowSlotDropDown(logic, slot, s, c, orderManager);
// Ensure Name selector (if present) is hidden // Ensure Name selector (if present) is hidden
var name = parent.GetOrNull("NAME"); var name = parent.GetOrNull("NAME");