Add RGA/RSI to Credits (#36704)

* Add RGA and RSI to Credits

* Move to thread + add directory field
This commit is contained in:
Thomas
2025-04-21 06:24:44 -04:00
committed by GitHub
parent b83cb209fe
commit df5e6a2b5c
3 changed files with 351 additions and 140 deletions

View File

@@ -1,7 +1,7 @@
<DefaultWindow xmlns="https://spacestation14.io" <DefaultWindow xmlns="https://spacestation14.io"
Title="{Loc 'credits-window-title'}" Title="{Loc 'credits-window-title'}"
SetSize="650 650" > SetSize="650 650">
<TabContainer> <TabContainer Name="MasterTabContainer">
<ScrollContainer Name="Ss14ContributorsTab" <ScrollContainer Name="Ss14ContributorsTab"
HScrollEnabled="False"> HScrollEnabled="False">
<BoxContainer Name="Ss14ContributorsContainer" <BoxContainer Name="Ss14ContributorsContainer"
@@ -26,5 +26,11 @@
<!-- Licenses get added here by code --> <!-- Licenses get added here by code -->
</BoxContainer> </BoxContainer>
</ScrollContainer> </ScrollContainer>
<ScrollContainer Name="AttributionsTab"
HScrollEnabled="False">
<BoxContainer Name="AttributionsContainer"
Orientation="Vertical"
Margin="2 2 0 0" />
</ScrollContainer>
</TabContainer> </TabContainer>
</DefaultWindow> </DefaultWindow>

View File

@@ -1,39 +1,48 @@
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Numerics; using System.Numerics;
using System.Threading.Tasks;
using Content.Client.Stylesheets; using Content.Client.Stylesheets;
using Content.Shared.CCVar; using Content.Shared.CCVar;
using Robust.Client.AutoGenerated; using Robust.Client.AutoGenerated;
using Robust.Client.Credits; using Robust.Client.Credits;
using Robust.Client.ResourceManagement;
using Robust.Client.UserInterface; using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls; using Robust.Client.UserInterface.CustomControls;
using Robust.Client.UserInterface.XAML; using Robust.Client.UserInterface.XAML;
using Robust.Shared.Configuration; using Robust.Shared.Configuration;
using Robust.Shared.ContentPack; using Robust.Shared.ContentPack;
using Robust.Shared.IoC; using Robust.Shared.Prototypes;
using Robust.Shared.Localization; using Robust.Shared.Serialization.Manager;
using Robust.Shared.Maths; using Robust.Shared.Serialization.Markdown;
using Robust.Shared.Serialization.Markdown.Mapping;
using Robust.Shared.Serialization.Markdown.Sequence;
using Robust.Shared.Utility; using Robust.Shared.Utility;
using YamlDotNet.RepresentationModel; using YamlDotNet.RepresentationModel;
using static Robust.Client.UserInterface.Controls.BoxContainer; using static Robust.Client.UserInterface.Controls.BoxContainer;
namespace Content.Client.Credits namespace Content.Client.Credits;
[GenerateTypedNameReferences]
public sealed partial class CreditsWindow : DefaultWindow
{ {
[GenerateTypedNameReferences]
public sealed partial class CreditsWindow : DefaultWindow
{
[Dependency] private readonly IResourceManager _resourceManager = default!; [Dependency] private readonly IResourceManager _resourceManager = default!;
[Dependency] private readonly IConfigurationManager _cfg = default!; [Dependency] private readonly IConfigurationManager _cfg = default!;
[Dependency] private readonly ISerializationManager _serialization = default!;
[Dependency] private readonly IPrototypeManager _protoManager = default!;
[Dependency] private readonly ILocalizationManager _loc = default!;
private static readonly Dictionary<string, int> PatronTierPriority = new() private static readonly Dictionary<string, int> PatronTierPriority = new()
{ {
["Nuclear Operative"] = 1, ["Nuclear Operative"] = 1,
["Syndicate Agent"] = 2, ["Syndicate Agent"] = 2,
["Revolutionary"] = 3 ["Revolutionary"] = 3,
}; };
private readonly List<FormattedMessage> _attributions = [];
private readonly ISawmill _sawmill = Logger.GetSawmill("Credits");
private const int AttributionsSourcesPerPage = 50;
public CreditsWindow() public CreditsWindow()
{ {
IoCManager.InjectDependencies(this); IoCManager.InjectDependencies(this);
@@ -42,23 +51,218 @@ namespace Content.Client.Credits
TabContainer.SetTabTitle(Ss14ContributorsTab, Loc.GetString("credits-window-ss14contributorslist-tab")); TabContainer.SetTabTitle(Ss14ContributorsTab, Loc.GetString("credits-window-ss14contributorslist-tab"));
TabContainer.SetTabTitle(PatronsTab, Loc.GetString("credits-window-patrons-tab")); TabContainer.SetTabTitle(PatronsTab, Loc.GetString("credits-window-patrons-tab"));
TabContainer.SetTabTitle(LicensesTab, Loc.GetString("credits-window-licenses-tab")); TabContainer.SetTabTitle(LicensesTab, Loc.GetString("credits-window-licenses-tab"));
TabContainer.SetTabTitle(AttributionsTab, Loc.GetString("credits-window-attributions-tab"));
_protoManager.PrototypesReloaded += _ =>
{
_attributions.Clear();
};
MasterTabContainer.OnTabChanged += OnTabChanged;
PopulateContributors(Ss14ContributorsContainer); PopulateContributors(Ss14ContributorsContainer);
}
/// <summary>
/// Only populates the tab when they are selected, which reduces lagspike when not looking at attributions.
/// </summary>
private void OnTabChanged(int tab)
{
if (tab == Ss14ContributorsTab.GetPositionInParent())
PopulateContributors(Ss14ContributorsContainer);
else if (tab == PatronsTab.GetPositionInParent())
PopulatePatrons(PatronsContainer); PopulatePatrons(PatronsContainer);
else if (tab == LicensesTab.GetPositionInParent())
PopulateLicenses(LicensesContainer); PopulateLicenses(LicensesContainer);
else if (tab == AttributionsTab.GetPositionInParent())
PopulateAttributions(AttributionsContainer, 0);
}
private async void PopulateAttributions(BoxContainer attributionsContainer, int count)
{
attributionsContainer.DisposeAllChildren();
if (_attributions.Count == 0)
{
var rsi = await CollectRSiAttributions();
var rga = await CollectRgaAttributions();
_attributions.AddRange(rsi);
_attributions.AddRange(rga);
}
foreach (var message in _attributions.Skip(count).Take(AttributionsSourcesPerPage))
{
var rich = new RichTextLabel();
rich.SetMessage(message);
attributionsContainer.AddChild(rich);
}
var container = new BoxContainer { Orientation = LayoutOrientation.Horizontal };
var previousPageButton = new Button { Text = "Previous Page" };
previousPageButton.OnPressed +=
_ => PopulateAttributions(attributionsContainer, count - AttributionsSourcesPerPage);
var nextPageButton = new Button { Text = "Next Page" };
nextPageButton.OnPressed +=
_ => PopulateAttributions(attributionsContainer, count + AttributionsSourcesPerPage);
if (count - AttributionsSourcesPerPage >= 0)
container.AddChild(previousPageButton);
if (count + AttributionsSourcesPerPage < _attributions.Count)
container.AddChild(nextPageButton);
attributionsContainer.AddChild(container);
}
private Task<List<FormattedMessage>> CollectRSiAttributions()
{
return Task.Run(() =>
{
var rsiStreams = _resourceManager.ContentFindFiles("/Textures/")
.Where(p => p.ToString().EndsWith(".rsi/meta.json"));
var attrs = new List<FormattedMessage>();
foreach (var stream in rsiStreams)
{
try
{
var m = new FormattedMessage();
var yamlStream = _resourceManager.ContentFileReadYaml(stream);
if (yamlStream.Documents[0].RootNode.ToDataNode() is not MappingDataNode map)
throw new Exception("meta.json is not a mapping.");
if (!map.TryGet("copyright", out var copyrightNode))
throw new Exception("Missing the copyright field.");
if (!map.TryGet("states", out var statesNode))
throw new Exception("Missing the states field.");
if (statesNode is not SequenceDataNode states)
throw new Exception("Missing a list of states.");
var copyright = copyrightNode.ToString();
var files = states.Select(n => (MappingDataNode)n)
.Select(n => n.Get("name") + ".png");
m.AddMarkupPermissive(_loc.GetString("credits-window-attributions-directory",
("directory", stream.Directory.ToString())));
m.AddText("\n");
m.AddMarkupPermissive(_loc.GetString("credits-window-attributions-files",
("files", string.Join(", ", files))));
m.AddText("\n");
m.AddMarkupPermissive(_loc.GetString("credits-window-attributions-copyright",
("copyright", copyright)));
m.AddText("\n");
attrs.Add(m);
}
catch (Exception e)
{
var m = new FormattedMessage();
m.AddMarkupPermissive(_loc.GetString("credits-window-attributions-failed",
("file", stream.ToString())));
m.AddText("\n");
_sawmill.Error($"{stream.ToString()}\n{e}");
attrs.Add(m);
}
}
return attrs;
});
}
private Task<List<FormattedMessage>> CollectRgaAttributions()
{
return Task.Run(() =>
{
var rgaStreams = _resourceManager.ContentFindFiles("/")
.Where(p => p.Filename == "attributions.yml");
var attrs = new List<FormattedMessage>();
foreach (var stream in rgaStreams)
{
try
{
var yamlStream = _resourceManager.ContentFileReadYaml(stream);
if (yamlStream.Documents[0].RootNode.ToDataNode() is not SequenceDataNode sequence)
throw new Exception("Attributions file is not a list of attributions.");
foreach (var attribution in sequence.Sequence)
{
var m = new FormattedMessage();
if (attribution is not MappingDataNode map)
throw new Exception("Attribution is not a mapping.");
if (!map.TryGet("files", out var filesNode))
throw new Exception("Attribution does not list files.");
if (!map.TryGet("copyright", out var copyrightNode))
throw new Exception("Attribution does not copyright.");
if (!map.TryGet("license", out var licenseNode))
throw new Exception("Attribution does not identify a license.");
if (!map.TryGet("source", out var sourceNode))
throw new Exception("Attribution does not identify a source.");
var files = _serialization.Read<string[]>(filesNode, notNullableOverride: true);
var copyright = copyrightNode.ToString();
var license = licenseNode.ToString();
var source = sourceNode.ToString();
m.AddMarkupPermissive(_loc.GetString("credits-window-attributions-directory",
("directory", stream.Directory.ToString())));
m.AddText("\n");
m.AddMarkupPermissive(_loc.GetString("credits-window-attributions-files",
("files", string.Join(", ", files))));
m.AddText("\n");
m.AddMarkupPermissive(_loc.GetString("credits-window-attributions-copyright",
("copyright", copyright)));
m.AddText("\n");
m.AddMarkupPermissive(
_loc.GetString("credits-window-attributions-license", ("license", license)));
m.AddText("\n");
m.AddMarkupPermissive(_loc.GetString("credits-window-attributions-source", ("source", source)));
m.AddText("\n");
attrs.Add(m);
}
}
catch (Exception e)
{
var m = new FormattedMessage();
m.AddMarkupPermissive(_loc.GetString("credits-window-attributions-failed",
("file", stream.ToString())));
m.AddText("\n");
_sawmill.Error($"{stream.ToString()}\n{e}");
attrs.Add(m);
}
}
return attrs;
});
} }
private void PopulateLicenses(BoxContainer licensesContainer) private void PopulateLicenses(BoxContainer licensesContainer)
{ {
foreach (var entry in CreditsManager.GetLicenses(_resourceManager).OrderBy(p => p.Name)) foreach (var entry in CreditsManager.GetLicenses(_resourceManager).OrderBy(p => p.Name))
{ {
licensesContainer.AddChild(new Label {StyleClasses = {StyleBase.StyleClassLabelHeading}, Text = entry.Name}); licensesContainer.AddChild(new Label
{ StyleClasses = { StyleBase.StyleClassLabelHeading }, Text = entry.Name });
// We split these line by line because otherwise // We split these line by line because otherwise
// the LGPL causes Clyde to go out of bounds in the rendering code. // the LGPL causes Clyde to go out of bounds in the rendering code.
foreach (var line in entry.License.Split("\n")) foreach (var line in entry.License.Split("\n"))
{ {
licensesContainer.AddChild(new Label {Text = line, FontColorOverride = new Color(200, 200, 200)}); licensesContainer.AddChild(new Label { Text = line, FontColorOverride = new Color(200, 200, 200) });
} }
} }
} }
@@ -76,7 +280,7 @@ namespace Content.Client.Credits
patronsContainer.AddChild(patronButton = new Button patronsContainer.AddChild(patronButton = new Button
{ {
Text = Loc.GetString("credits-window-become-patron-button"), Text = Loc.GetString("credits-window-become-patron-button"),
HorizontalAlignment = HAlignment.Center HorizontalAlignment = HAlignment.Center,
}); });
patronButton.OnPressed += patronButton.OnPressed +=
@@ -87,12 +291,11 @@ namespace Content.Client.Credits
foreach (var tier in patrons.GroupBy(p => p.Tier).OrderBy(p => PatronTierPriority[p.Key])) foreach (var tier in patrons.GroupBy(p => p.Tier).OrderBy(p => PatronTierPriority[p.Key]))
{ {
if (!first) if (!first)
{ patronsContainer.AddChild(new Control { MinSize = new Vector2(0, 10) });
patronsContainer.AddChild(new Control {MinSize = new Vector2(0, 10)});
}
first = false; first = false;
patronsContainer.AddChild(new Label {StyleClasses = {StyleBase.StyleClassLabelHeading}, Text = $"{tier.Key}"}); patronsContainer.AddChild(new Label
{ StyleClasses = { StyleBase.StyleClassLabelHeading }, Text = $"{tier.Key}" });
var msg = string.Join(", ", tier.OrderBy(p => p.Name).Select(p => p.Name)); var msg = string.Join(", ", tier.OrderBy(p => p.Name).Select(p => p.Name));
@@ -105,8 +308,8 @@ namespace Content.Client.Credits
private IEnumerable<PatronEntry> LoadPatrons() private IEnumerable<PatronEntry> LoadPatrons()
{ {
var yamlStream = _resourceManager.ContentFileReadYaml(new ("/Credits/Patrons.yml")); var yamlStream = _resourceManager.ContentFileReadYaml(new ResPath("/Credits/Patrons.yml"));
var sequence = (YamlSequenceNode) yamlStream.Documents[0].RootNode; var sequence = (YamlSequenceNode)yamlStream.Documents[0].RootNode;
return sequence return sequence
.Cast<YamlMappingNode>() .Cast<YamlMappingNode>()
@@ -124,9 +327,9 @@ namespace Content.Client.Credits
SeparationOverride = 20, SeparationOverride = 20,
Children = Children =
{ {
new Label {Text = Loc.GetString("credits-window-contributor-encouragement-label") }, new Label { Text = Loc.GetString("credits-window-contributor-encouragement-label") },
(contributeButton = new Button {Text = Loc.GetString("credits-window-contribute-button")}) (contributeButton = new Button { Text = Loc.GetString("credits-window-contribute-button") }),
} },
}); });
var first = true; var first = true;
@@ -134,23 +337,18 @@ namespace Content.Client.Credits
void AddSection(string title, string path, bool markup = false) void AddSection(string title, string path, bool markup = false)
{ {
if (!first) if (!first)
{ ss14ContributorsContainer.AddChild(new Control { MinSize = new Vector2(0, 10) });
ss14ContributorsContainer.AddChild(new Control {MinSize = new Vector2(0, 10)});
}
first = false; first = false;
ss14ContributorsContainer.AddChild(new Label {StyleClasses = {StyleBase.StyleClassLabelHeading}, Text = title}); ss14ContributorsContainer.AddChild(new Label
{ StyleClasses = { StyleBase.StyleClassLabelHeading }, Text = title });
var label = new RichTextLabel(); var label = new RichTextLabel();
var text = _resourceManager.ContentFileReadAllText($"/Credits/{path}"); var text = _resourceManager.ContentFileReadAllText($"/Credits/{path}");
if (markup) if (markup)
{
label.SetMessage(FormattedMessage.FromMarkupOrThrow(text.Trim())); label.SetMessage(FormattedMessage.FromMarkupOrThrow(text.Trim()));
}
else else
{
label.SetMessage(text); label.SetMessage(text);
}
ss14ContributorsContainer.AddChild(label); ss14ContributorsContainer.AddChild(label);
} }
@@ -180,5 +378,4 @@ namespace Content.Client.Credits
Tier = tier; Tier = tier;
} }
} }
}
} }

View File

@@ -2,6 +2,7 @@ credits-window-title = Credits
credits-window-patrons-tab = Patrons credits-window-patrons-tab = Patrons
credits-window-ss14contributorslist-tab = Credits credits-window-ss14contributorslist-tab = Credits
credits-window-licenses-tab = Open Source Licenses credits-window-licenses-tab = Open Source Licenses
credits-window-attributions-tab = Attributions
credits-window-become-patron-button = Become a Patron credits-window-become-patron-button = Become a Patron
credits-window-contributor-encouragement-label = Want to get on this list? credits-window-contributor-encouragement-label = Want to get on this list?
credits-window-contribute-button = Contribute! credits-window-contribute-button = Contribute!
@@ -9,3 +10,10 @@ credits-window-contributors-section-title = Space Station 14 Contributors
credits-window-codebases-section-title = Space Station 13 Codebases credits-window-codebases-section-title = Space Station 13 Codebases
credits-window-original-remake-team-section-title = Original Space Station 13 Remake Team credits-window-original-remake-team-section-title = Original Space Station 13 Remake Team
credits-window-special-thanks-section-title = Special Thanks credits-window-special-thanks-section-title = Special Thanks
credits-window-attributions-directory = [color=white]Directory:[/color] {$directory}
credits-window-attributions-files = [color=white]Files:[/color] {$files}
credits-window-attributions-copyright = [color=white]Copyright:[/color] {$copyright}
credits-window-attributions-license = [color=white]License:[/color] {$license}
credits-window-attributions-source = [color=white]Source:[/color] {$source}
credits-window-attributions-failed = [color=red]Failed to read file:[/color] {$file}