LobbyLogic, ReplayBrowserLogic Map property changed to map field

This commit is contained in:
rob-v
2017-05-13 22:56:07 +02:00
committed by Paul Chote
parent a26210f914
commit 127ef8bb27
3 changed files with 50 additions and 38 deletions

View File

@@ -62,7 +62,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
readonly LabelWidget chatLabel; readonly LabelWidget chatLabel;
MapPreview Map { get; set; } MapPreview map;
bool addBotOnMapLoad; bool addBotOnMapLoad;
bool teamChat; bool teamChat;
int lobbyChatUnreadMessages; int lobbyChatUnreadMessages;
@@ -103,7 +103,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)
{ {
Map = MapCache.UnknownMap; map = MapCache.UnknownMap;
lobby = widget; lobby = widget;
this.modData = modData; this.modData = modData;
this.orderManager = orderManager; this.orderManager = orderManager;
@@ -130,7 +130,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
Ui.LoadWidget("MAP_PREVIEW", lobby.Get("MAP_PREVIEW_ROOT"), new WidgetArgs Ui.LoadWidget("MAP_PREVIEW", lobby.Get("MAP_PREVIEW_ROOT"), new WidgetArgs
{ {
{ "orderManager", orderManager }, { "orderManager", orderManager },
{ "getMap", (Func<MapPreview>)(() => Map) }, { "getMap", (Func<MapPreview>)(() => map) },
{ "onMouseDown", (Action<MapPreviewWidget, MapPreview, MouseInput>)((preview, map, mi) => LobbyUtils.SelectSpawnPoint(orderManager, preview, map, mi)) }, { "onMouseDown", (Action<MapPreviewWidget, MapPreview, MouseInput>)((preview, map, mi) => LobbyUtils.SelectSpawnPoint(orderManager, preview, map, mi)) },
{ "getSpawnOccupants", (Func<MapPreview, Dictionary<CPos, SpawnOccupant>>)(map => LobbyUtils.GetSpawnOccupants(orderManager.LobbyInfo, map)) }, { "getSpawnOccupants", (Func<MapPreview, Dictionary<CPos, SpawnOccupant>>)(map => LobbyUtils.GetSpawnOccupants(orderManager.LobbyInfo, map)) },
}); });
@@ -156,7 +156,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.RulesLoaded || 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");
@@ -169,7 +169,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 == Map.Uid) if (uid == map.Uid)
return; return;
orderManager.IssueOrder(Order.Command("map " + uid)); orderManager.IssueOrder(Order.Command("map " + uid));
@@ -179,7 +179,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
Ui.OpenWindow("MAPCHOOSER_PANEL", new WidgetArgs() Ui.OpenWindow("MAPCHOOSER_PANEL", new WidgetArgs()
{ {
{ "initialMap", Map.Uid }, { "initialMap", map.Uid },
{ "initialTab", MapClassification.System }, { "initialTab", MapClassification.System },
{ "onExit", DoNothing }, { "onExit", DoNothing },
{ "onSelect", Game.IsHost ? onSelect : null }, { "onSelect", Game.IsHost ? onSelect : null },
@@ -197,7 +197,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
slotsButton.OnMouseDown = _ => slotsButton.OnMouseDown = _ =>
{ {
var botNames = Map.Rules.Actors["player"].TraitInfos<IBotInfo>().Select(t => t.Name); 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);
@@ -295,7 +295,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.RulesLoaded || 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");
@@ -318,7 +318,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
var startGameButton = lobby.GetOrNull<ButtonWidget>("START_GAME_BUTTON"); var startGameButton = lobby.GetOrNull<ButtonWidget>("START_GAME_BUTTON");
if (startGameButton != null) if (startGameButton != null)
{ {
startGameButton.IsDisabled = () => configurationDisabled() || Map.Status != MapStatus.Available || startGameButton.IsDisabled = () => configurationDisabled() || map.Status != MapStatus.Available ||
orderManager.LobbyInfo.Slots.Any(sl => sl.Value.Required && orderManager.LobbyInfo.ClientInSlot(sl.Key) == null) || orderManager.LobbyInfo.Slots.Any(sl => sl.Value.Required && orderManager.LobbyInfo.ClientInSlot(sl.Key) == null) ||
(!orderManager.LobbyInfo.GlobalSettings.EnableSingleplayer && orderManager.LobbyInfo.IsSinglePlayer); (!orderManager.LobbyInfo.GlobalSettings.EnableSingleplayer && orderManager.LobbyInfo.IsSinglePlayer);
@@ -399,14 +399,14 @@ namespace OpenRA.Mods.Common.Widgets.Logic
var getOptionLabel = new CachedTransform<string, string>(id => var getOptionLabel = new CachedTransform<string, string>(id =>
{ {
string value; string value;
if (id == null || !option.Update(Map).Values.TryGetValue(id, out value)) if (id == null || !option.Update(map).Values.TryGetValue(id, out value))
return "Not Available"; return "Not Available";
return value; return value;
}); });
dropdown.GetText = () => getOptionLabel.Update(optionValue.Update(orderManager.LobbyInfo.GlobalSettings).Value); dropdown.GetText = () => getOptionLabel.Update(optionValue.Update(orderManager.LobbyInfo.GlobalSettings).Value);
dropdown.IsVisible = () => option.Update(Map) != null; dropdown.IsVisible = () => option.Update(map) != null;
dropdown.IsDisabled = () => configurationDisabled() || dropdown.IsDisabled = () => configurationDisabled() ||
optionValue.Update(orderManager.LobbyInfo.GlobalSettings).Locked; optionValue.Update(orderManager.LobbyInfo.GlobalSettings).Locked;
@@ -422,13 +422,13 @@ namespace OpenRA.Mods.Common.Widgets.Logic
return item; return item;
}; };
var options = option.Update(Map).Values; var options = option.Update(map).Values;
dropdown.ShowDropDown("LABEL_DROPDOWN_TEMPLATE", options.Count() * 30, options, setupItem); dropdown.ShowDropDown("LABEL_DROPDOWN_TEMPLATE", options.Count() * 30, options, setupItem);
}; };
var label = optionsBin.GetOrNull(kv.Key + "_DESC"); var label = optionsBin.GetOrNull(kv.Key + "_DESC");
if (label != null) if (label != null)
label.IsVisible = () => option.Update(Map) != null; label.IsVisible = () => option.Update(map) != null;
} }
} }
@@ -594,14 +594,14 @@ namespace OpenRA.Mods.Common.Widgets.Logic
void UpdateCurrentMap() void UpdateCurrentMap()
{ {
var uid = orderManager.LobbyInfo.GlobalSettings.Map; var uid = orderManager.LobbyInfo.GlobalSettings.Map;
if (Map.Uid == uid) if (map.Uid == uid)
return; return;
Map = modData.MapCache[uid]; map = modData.MapCache[uid];
if (Map.Status == MapStatus.Available) if (map.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
var currentMap = Map; var currentMap = map;
new Task(() => new Task(() =>
{ {
// Force map rules to be loaded on this background thread // Force map rules to be loaded on this background thread
@@ -609,7 +609,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
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 // Tell the server that we have the map
@@ -629,8 +629,8 @@ namespace OpenRA.Mods.Common.Widgets.Logic
}); });
}).Start(); }).Start();
} }
else if (Map.Status == MapStatus.DownloadAvailable) else if (map.Status == MapStatus.DownloadAvailable)
LoadMapPreviewRules(Map); LoadMapPreviewRules(map);
else if (Game.Settings.Game.AllowDownloading) else if (Game.Settings.Game.AllowDownloading)
modData.MapCache.QueryRemoteMapDetails(services.MapRepository, new[] { uid }, LoadMapPreviewRules); modData.MapCache.QueryRemoteMapDetails(services.MapRepository, new[] { uid }, LoadMapPreviewRules);
} }
@@ -660,7 +660,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
template = emptySlotTemplate.Clone(); template = emptySlotTemplate.Clone();
if (isHost) if (isHost)
LobbyUtils.SetupEditableSlotWidget(template, slot, client, orderManager, Map); LobbyUtils.SetupEditableSlotWidget(template, slot, client, orderManager, map);
else else
LobbyUtils.SetupSlotWidget(template, slot, client); LobbyUtils.SetupSlotWidget(template, slot, client);
@@ -679,15 +679,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, Map); LobbyUtils.SetupEditableSlotWidget(template, slot, client, orderManager, map);
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, Map); LobbyUtils.SetupEditableTeamWidget(template, slot, client, orderManager, map);
LobbyUtils.SetupEditableSpawnWidget(template, slot, client, orderManager, Map); LobbyUtils.SetupEditableSpawnWidget(template, slot, client, orderManager, map);
LobbyUtils.SetupEditableReadyWidget(template, slot, client, orderManager, Map); LobbyUtils.SetupEditableReadyWidget(template, slot, client, orderManager, map);
} }
else else
{ {
@@ -703,8 +703,8 @@ namespace OpenRA.Mods.Common.Widgets.Logic
LobbyUtils.SetupFactionWidget(template, slot, client, factions); LobbyUtils.SetupFactionWidget(template, slot, client, factions);
if (isHost) if (isHost)
{ {
LobbyUtils.SetupEditableTeamWidget(template, slot, client, orderManager, Map); LobbyUtils.SetupEditableTeamWidget(template, slot, client, orderManager, map);
LobbyUtils.SetupEditableSpawnWidget(template, slot, client, orderManager, Map); LobbyUtils.SetupEditableSpawnWidget(template, slot, client, orderManager, map);
} }
else else
{ {
@@ -744,7 +744,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
LobbyUtils.SetupEditableNameWidget(template, null, c, orderManager); LobbyUtils.SetupEditableNameWidget(template, null, c, orderManager);
if (client.IsAdmin) if (client.IsAdmin)
LobbyUtils.SetupEditableReadyWidget(template, null, client, orderManager, Map); LobbyUtils.SetupEditableReadyWidget(template, null, client, orderManager, map);
} }
else else
{ {

View File

@@ -37,6 +37,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
var map = getMap(); var map = getMap();
return map.Status == MapStatus.Available && (!map.RulesLoaded || !map.InvalidCustomRules); return map.Status == MapStatus.Available && (!map.RulesLoaded || !map.InvalidCustomRules);
}; };
SetupWidgets(available, getMap, onMouseDown, getSpawnOccupants); SetupWidgets(available, getMap, onMouseDown, getSpawnOccupants);
} }
@@ -48,6 +49,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
var map = getMap(); var map = getMap();
return map.Status == MapStatus.Available && map.InvalidCustomRules; return map.Status == MapStatus.Available && map.InvalidCustomRules;
}; };
SetupWidgets(invalid, getMap, onMouseDown, getSpawnOccupants); SetupWidgets(invalid, getMap, onMouseDown, getSpawnOccupants);
} }
@@ -83,6 +85,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
var map = getMap(); var map = getMap();
return map.Status != MapStatus.Available && map.Status != MapStatus.DownloadAvailable; return map.Status != MapStatus.Available && map.Status != MapStatus.DownloadAvailable;
}; };
SetupWidgets(progress, getMap, onMouseDown, getSpawnOccupants); SetupWidgets(progress, getMap, onMouseDown, getSpawnOccupants);
var statusSearching = progress.GetOrNull("MAP_STATUS_SEARCHING"); var statusSearching = progress.GetOrNull("MAP_STATUS_SEARCHING");
@@ -91,11 +94,13 @@ namespace OpenRA.Mods.Common.Widgets.Logic
var statusUnavailable = progress.GetOrNull("MAP_STATUS_UNAVAILABLE"); var statusUnavailable = progress.GetOrNull("MAP_STATUS_UNAVAILABLE");
if (statusUnavailable != null) if (statusUnavailable != null)
{
statusUnavailable.IsVisible = () => statusUnavailable.IsVisible = () =>
{ {
var map = getMap(); var map = getMap();
return map.Status == MapStatus.Unavailable && map != MapCache.UnknownMap; return map.Status == MapStatus.Unavailable && map != MapCache.UnknownMap;
}; };
}
var statusError = progress.GetOrNull("MAP_STATUS_ERROR"); var statusError = progress.GetOrNull("MAP_STATUS_ERROR");
if (statusError != null) if (statusError != null)
@@ -127,6 +132,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
var map = getMap(); var map = getMap();
return (map.Status == MapStatus.DownloadError || map.Status == MapStatus.Unavailable) && map != MapCache.UnknownMap; return (map.Status == MapStatus.DownloadError || map.Status == MapStatus.Unavailable) && map != MapCache.UnknownMap;
}; };
retry.OnClick = () => retry.OnClick = () =>
{ {
var map = getMap(); var map = getMap();

View File

@@ -36,7 +36,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
readonly ModData modData; readonly ModData modData;
readonly WebServices services; readonly WebServices services;
MapPreview Map { get; set; } MapPreview map;
ReplayMetadata selectedReplay; ReplayMetadata selectedReplay;
volatile bool cancelLoadingReplays; volatile bool cancelLoadingReplays;
@@ -44,7 +44,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
[ObjectCreator.UseCtor] [ObjectCreator.UseCtor]
public ReplayBrowserLogic(Widget widget, ModData modData, Action onExit, Action onStart) public ReplayBrowserLogic(Widget widget, ModData modData, Action onExit, Action onStart)
{ {
Map = MapCache.UnknownMap; map = MapCache.UnknownMap;
panel = widget; panel = widget;
services = modData.Manifest.Get<WebServices>(); services = modData.Manifest.Get<WebServices>();
@@ -69,7 +69,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
ThreadPool.QueueUserWorkItem(_ => LoadReplays(dir, template)); ThreadPool.QueueUserWorkItem(_ => LoadReplays(dir, template));
var watch = panel.Get<ButtonWidget>("WATCH_BUTTON"); var watch = panel.Get<ButtonWidget>("WATCH_BUTTON");
watch.IsDisabled = () => selectedReplay == null || Map.Status != MapStatus.Available; watch.IsDisabled = () => selectedReplay == null || map.Status != MapStatus.Available;
watch.OnClick = () => { WatchReplay(); }; watch.OnClick = () => { WatchReplay(); };
panel.Get("REPLAY_INFO").IsVisible = () => selectedReplay != null; panel.Get("REPLAY_INFO").IsVisible = () => selectedReplay != null;
@@ -77,12 +77,14 @@ namespace OpenRA.Mods.Common.Widgets.Logic
Ui.LoadWidget("MAP_PREVIEW", panel.Get("MAP_PREVIEW_ROOT"), new WidgetArgs Ui.LoadWidget("MAP_PREVIEW", panel.Get("MAP_PREVIEW_ROOT"), new WidgetArgs
{ {
{ "orderManager", null }, { "orderManager", null },
{ "getMap", (Func<MapPreview>)(() => Map) }, { "getMap", (Func<MapPreview>)(() => map) },
{ "onMouseDown", (Action<MapPreviewWidget, MapPreview, MouseInput>)((preview, map, mi) => { }) }, { "onMouseDown", (Action<MapPreviewWidget, MapPreview, MouseInput>)((preview, map, mi) => { }) },
{ "getSpawnOccupants", (Func<MapPreview, Dictionary<CPos, SpawnOccupant>>)(map => LobbyUtils.GetSpawnOccupants(selectedReplay.GameInfo.Players, map)) }, { "getSpawnOccupants", (Func<MapPreview, Dictionary<CPos, SpawnOccupant>>)(map => LobbyUtils.GetSpawnOccupants(selectedReplay.GameInfo.Players, map)) },
}); });
panel.Get<LabelWidget>("DURATION").GetText = () => WidgetUtils.FormatTimeSeconds((int)selectedReplay.GameInfo.Duration.TotalSeconds); var replayDuration = new CachedTransform<ReplayMetadata, string>(r =>
"Duration: {0}".F(WidgetUtils.FormatTimeSeconds((int)selectedReplay.GameInfo.Duration.TotalSeconds)));
panel.Get<LabelWidget>("DURATION").GetText = () => replayDuration.Update(selectedReplay);
SetupFilters(); SetupFilters();
SetupManagement(); SetupManagement();
@@ -136,6 +138,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
Pair.New(GameType.Singleplayer, "Singleplayer"), Pair.New(GameType.Singleplayer, "Singleplayer"),
Pair.New(GameType.Multiplayer, "Multiplayer") Pair.New(GameType.Multiplayer, "Multiplayer")
}; };
var lookup = options.ToDictionary(kvp => kvp.First, kvp => kvp.Second); var lookup = options.ToDictionary(kvp => kvp.First, kvp => kvp.Second);
ddb.GetText = () => lookup[filter.Type]; ddb.GetText = () => lookup[filter.Type];
@@ -170,6 +173,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
Pair.New(DateType.LastFortnight, "Last 14 days"), Pair.New(DateType.LastFortnight, "Last 14 days"),
Pair.New(DateType.LastMonth, "Last 30 days") Pair.New(DateType.LastMonth, "Last 30 days")
}; };
var lookup = options.ToDictionary(kvp => kvp.First, kvp => kvp.Second); var lookup = options.ToDictionary(kvp => kvp.First, kvp => kvp.Second);
ddb.GetText = () => lookup[filter.Date]; ddb.GetText = () => lookup[filter.Date];
@@ -205,6 +209,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
Pair.New(DurationType.Medium, "Medium (30 min)"), Pair.New(DurationType.Medium, "Medium (30 min)"),
Pair.New(DurationType.Long, "Long (60+ min)") Pair.New(DurationType.Long, "Long (60+ min)")
}; };
var lookup = options.ToDictionary(kvp => kvp.First, kvp => kvp.Second); var lookup = options.ToDictionary(kvp => kvp.First, kvp => kvp.Second);
ddb.GetText = () => lookup[filter.Duration]; ddb.GetText = () => lookup[filter.Duration];
@@ -239,6 +244,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
Pair.New(WinState.Lost, "Defeat"), Pair.New(WinState.Lost, "Defeat"),
Pair.New(WinState.Won, "Victory") Pair.New(WinState.Won, "Victory")
}; };
var lookup = options.ToDictionary(kvp => kvp.First, kvp => kvp.Second); var lookup = options.ToDictionary(kvp => kvp.First, kvp => kvp.Second);
ddb.GetText = () => lookup[filter.Outcome]; ddb.GetText = () => lookup[filter.Outcome];
@@ -595,19 +601,19 @@ namespace OpenRA.Mods.Common.Widgets.Logic
void SelectReplay(ReplayMetadata replay) void SelectReplay(ReplayMetadata replay)
{ {
selectedReplay = replay; selectedReplay = replay;
Map = (selectedReplay != null) ? selectedReplay.GameInfo.MapPreview : MapCache.UnknownMap; map = selectedReplay != null ? selectedReplay.GameInfo.MapPreview : MapCache.UnknownMap;
if (replay == null) if (replay == null)
return; return;
try try
{ {
if (Map.Status != MapStatus.Available) if (map.Status != MapStatus.Available)
{ {
if (Map.Status == MapStatus.DownloadAvailable) if (map.Status == MapStatus.DownloadAvailable)
LoadMapPreviewRules(Map); LoadMapPreviewRules(map);
else if (Game.Settings.Game.AllowDownloading) else if (Game.Settings.Game.AllowDownloading)
modData.MapCache.QueryRemoteMapDetails(services.MapRepository, new[] { Map.Uid }, LoadMapPreviewRules); modData.MapCache.QueryRemoteMapDetails(services.MapRepository, new[] { map.Uid }, LoadMapPreviewRules);
} }
var players = replay.GameInfo.Players var players = replay.GameInfo.Players