Skip to content

Commit

Permalink
Merge pull request #28 from penev92/spriteSequenceSupportFull
Browse files Browse the repository at this point in the history
Add sprite sequence support
  • Loading branch information
penev92 authored Oct 22, 2022
2 parents 36a785a + 8055c2d commit e1aed4b
Show file tree
Hide file tree
Showing 22 changed files with 868 additions and 21 deletions.
4 changes: 3 additions & 1 deletion server/Oraide.Core/Entities/MiniYaml/CursorTarget.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,11 @@ public enum FileType
Rules = 1,
Weapons = 2,
Cursors = 3,
SpriteSequences = 4,
MapFile = 10,
MapRules = 11,
MapWeapons = 12
MapWeapons = 12,
MapSpriteSequences = 14,
}

public readonly struct CursorTarget
Expand Down
3 changes: 3 additions & 0 deletions server/Oraide.Core/Entities/MiniYaml/MapManifest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ public readonly struct MapManifest

public readonly IReadOnlyList<string> WeaponsFiles;

public readonly IReadOnlyList<string> SpriteSequenceFiles;

public string MapFile => MapFolder == null ? null : $"{MapFolder}/map.yaml";

public string MapReference => MapFolder == null ? null : $"{mapsFolder}/{Path.GetFileName(MapFolder)}";
Expand All @@ -26,6 +28,7 @@ public MapManifest(string mapFolder, IEnumerable<YamlNode> yamlNodes, string map
MapFolder = mapFolder;
RulesFiles = new ReadOnlyCollection<string>(GetValues(yamlNodes, "Rules", Path.GetFileName(mapFolder), mapsFolder).ToList());
WeaponsFiles = new ReadOnlyCollection<string>(GetValues(yamlNodes, "Weapons", Path.GetFileName(mapFolder), mapsFolder).ToList());
SpriteSequenceFiles = new ReadOnlyCollection<string>(GetValues(yamlNodes, "Sequences", Path.GetFileName(mapFolder), mapsFolder).ToList());

this.mapsFolder = mapsFolder;
}
Expand Down
8 changes: 8 additions & 0 deletions server/Oraide.Core/Entities/MiniYaml/ModManifest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,20 @@ public readonly struct ModManifest

public readonly IReadOnlyList<string> WeaponsFiles;

public readonly IReadOnlyList<string> SpriteSequences;

public readonly (string Type, IReadOnlyDictionary<string, YamlNode> Metadata) SpriteSequenceFormat;

public ModManifest(IEnumerable<YamlNode> yamlNodes)
{
MapsFolder = yamlNodes.FirstOrDefault(x => x.Key == "MapFolders")?.ChildNodes?.FirstOrDefault(x => x.Value == "System")?.Key;
RulesFiles = new ReadOnlyCollection<string>(GetValues(yamlNodes, "Rules").ToList());
CursorsFiles = new ReadOnlyCollection<string>(GetValues(yamlNodes, "Cursors").ToList());
WeaponsFiles = new ReadOnlyCollection<string>(GetValues(yamlNodes, "Weapons").ToList());
SpriteSequences = new ReadOnlyCollection<string>(GetValues(yamlNodes, "Sequences").ToList());

var spriteSequenceFormatNode = yamlNodes.FirstOrDefault(x => x.Key == "SpriteSequenceFormat");
SpriteSequenceFormat = (spriteSequenceFormatNode?.Value, spriteSequenceFormatNode?.ChildNodes?.ToDictionary(x => x.Key, y => y));
}

static IEnumerable<string> GetValues(IEnumerable<YamlNode> yamlNodes, string key)
Expand Down
29 changes: 29 additions & 0 deletions server/Oraide.Core/Entities/MiniYaml/SpriteSequenceDefinition.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
namespace Oraide.Core.Entities.MiniYaml
{
public readonly struct SpriteSequenceDefinition
{
public readonly string Name;

public readonly string ImageName;

public readonly string FileName;

public readonly MemberLocation Location;

public SpriteSequenceDefinition(string name, string imageName, string fileName, MemberLocation location)
{
Name = name;
ImageName = imageName;
FileName = fileName;
Location = location;
}

public override string ToString() => $"Sequence {Name}";

public string ToMarkdownInfoString() => "```csharp\n" +
$"Image \"{ImageName}\"\n" +
$"Sequence \"{Name}\"\n" +
$"File name \"{FileName}\"\n" +
"```";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
namespace Oraide.Core.Entities.MiniYaml
{
public readonly struct SpriteSequenceImageDefinition
{
public readonly string Name;

public readonly MemberLocation Location;

public readonly SpriteSequenceDefinition[] Sequences;

public SpriteSequenceImageDefinition(string name, MemberLocation location, SpriteSequenceDefinition[] sequences)
{
Name = name;
Location = location;
Sequences = sequences;
}

public override string ToString() => $"Image {Name}";

public string ToMarkdownInfoString() => "```csharp\n" +
$"Image \"{Name}\"" +
"\n```";
}
}
5 changes: 5 additions & 0 deletions server/Oraide.Csharp/CodeInformationProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,11 @@ public ILookup<string, TraitInfo> GetPaletteTraitInfos()
return symbolGenerator.GetPaletteTraitInfos();
}

public ILookup<string, SimpleClassInfo> GetSpriteSequenceInfos()
{
return symbolGenerator.GetSpriteSequenceInfos();
}

string GetOpenRaFolder(string workspaceFolderPath, string defaultOpenRaFolderPath)
{
var oraFolderPath = "";
Expand Down
67 changes: 63 additions & 4 deletions server/Oraide.Csharp/CodeParsers/RoslynCodeParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,13 @@ namespace Oraide.Csharp.CodeParsers
{
public static class RoslynCodeParser
{
public static (ILookup<string, TraitInfo>, WeaponInfo, ILookup<string, TraitInfo>) Parse(in string oraFolderPath)
public static (ILookup<string, TraitInfo>, WeaponInfo, ILookup<string, TraitInfo>, ILookup<string, SimpleClassInfo>) Parse(in string oraFolderPath)
{
var traitInfos = new List<TraitInfo>();
var weaponInfoFields = Array.Empty<ClassFieldInfo>();
var warheadInfos = new List<SimpleClassInfo>();
var projectileInfos = new List<SimpleClassInfo>();
var spriteSequences = new List<SimpleClassInfo>();

var filePaths = Directory.EnumerateFiles(oraFolderPath, "*.cs", SearchOption.AllDirectories)
.Where(x => !x.Contains("OpenRA.Test"));
Expand Down Expand Up @@ -64,6 +65,36 @@ public static (ILookup<string, TraitInfo>, WeaponInfo, ILookup<string, TraitInfo
var warheadInfo = ParseWarheadInfo(filePath, fileText, classDeclaration);
warheadInfos.Add(warheadInfo);
}
else if (classDeclaration.Identifier.ValueText.EndsWith("SpriteSequence"))
{
var classInfo = ParseSimpleClass(filePath, fileText, classDeclaration);

// Sequences need special member field parsing...
var fields = new List<ClassFieldInfo>();
foreach (var member in classDeclaration.Members)
{
if (member is FieldDeclarationSyntax fieldMember
&& fieldMember.Modifiers.Any(x => x.ValueText == "static")
&& fieldMember.Modifiers.Any(x => x.ValueText == "readonly")
&& (fieldMember.Declaration.Type as GenericNameSyntax).Identifier.ValueText == "SpriteSequenceField")
{
foreach (var fieldInfo in ParseClassField(filePath, fileText, fieldMember))
{
var type = string.Join(", ", (fieldMember.Declaration.Type as GenericNameSyntax).TypeArgumentList.Arguments);
var defaultValue = fieldInfo.DefaultValue.Split(',').Last().Trim();
var newFieldInfo = new ClassFieldInfo(fieldInfo.Name, type, UserFriendlyTypeName(type),
defaultValue, fieldInfo.ClassName, fieldInfo.Location, fieldInfo.Description, fieldInfo.OtherAttributes);

fields.Add(newFieldInfo);
}
}
}

var newClassInfo = new SimpleClassInfo(classInfo.Name, classInfo.InfoName, classInfo.Description, classInfo.Location,
classInfo.InheritedTypes, fields.ToArray(), classInfo.IsAbstract);

spriteSequences.Add(newClassInfo);
}
}
}
}
Expand Down Expand Up @@ -124,6 +155,7 @@ public static (ILookup<string, TraitInfo>, WeaponInfo, ILookup<string, TraitInfo
var fieldInfos = new List<ClassFieldInfo>();
foreach (var (className, classFieldNames) in baseTypes)
{
// TODO: This seems like it could be simplified a lot like SpriteSequences below.
foreach (var typeFieldName in classFieldNames)
{
var fi = wi.PropertyInfos.FirstOrDefault(z => z.Name == typeFieldName);
Expand Down Expand Up @@ -160,7 +192,34 @@ public static (ILookup<string, TraitInfo>, WeaponInfo, ILookup<string, TraitInfo
.Any(z => z.Name == "PaletteDefinition")))
.ToLookup(x => x.TraitInfoName, y => y);

return (finalTraitInfos.ToLookup(x => x.TraitInfoName, y => y), weaponInfo, paletteTraitInfos);
// Resolve warhead inheritance - load base types and a full list in fields - inherited or not.
var finalSpriteSequenceInfos = new List<SimpleClassInfo>(spriteSequences.Count);
foreach (var ssi in spriteSequences)
{
var baseTypes = GetWarheadBaseTypes(spriteSequences, ssi.Name);
var fieldInfos = new List<ClassFieldInfo>();
foreach (var (baseClassName, _) in baseTypes)
{
var baseClassInfo = spriteSequences.FirstOrDefault(x => x.Name == baseClassName);
fieldInfos.AddRange(baseClassInfo.PropertyInfos);
}

var spriteSequenceInfo = new SimpleClassInfo(
ssi.Name,
ssi.InfoName,
ssi.Description,
ssi.Location,
baseTypes.Where(x => x.TypeName != ssi.InfoName).Select(x => x.TypeName).ToArray(),
fieldInfos.ToArray(),
ssi.IsAbstract);

finalSpriteSequenceInfos.Add(spriteSequenceInfo);
}

return (finalTraitInfos.ToLookup(x => x.TraitInfoName, y => y),
weaponInfo,
paletteTraitInfos,
finalSpriteSequenceInfos.ToLookup(x => x.Name, y => y));
}

// Files can potentially contain multiple TraitInfos.
Expand Down Expand Up @@ -231,8 +290,8 @@ static IEnumerable<ClassFieldInfo> ParseClassFields(string filePath, string file
foreach (var member in classDeclaration.Members)
if (member is FieldDeclarationSyntax fieldMember
&& fieldMember.Modifiers.Any(x => x.ValueText == "public")
&& fieldMember.Modifiers.Any(x => x.ValueText == "readonly")
&& fieldMember.Modifiers.All(x => x.ValueText != "static"))
&& fieldMember.Modifiers.All(x => x.ValueText != "static")
&& fieldMember.Modifiers.Any(x => x.ValueText == "readonly"))
foreach (var fieldInfo in ParseClassField(filePath, fileText, fieldMember))
yield return fieldInfo;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ class CodeParsingSymbolGenerationStrategy : ICodeSymbolGenerationStrategy
ILookup<string, TraitInfo> traitInfos;
WeaponInfo weaponInfo;
ILookup<string, TraitInfo> paletteTraitInfos;
ILookup<string, SimpleClassInfo> spriteSequenceInfos;

public CodeParsingSymbolGenerationStrategy(string openRaFolder)
{
Expand Down Expand Up @@ -41,12 +42,21 @@ public ILookup<string, TraitInfo> GetPaletteTraitInfos()
return paletteTraitInfos;
}

public ILookup<string, SimpleClassInfo> GetSpriteSequenceInfos()
{
if (spriteSequenceInfos == null)
Parse();

return spriteSequenceInfos;
}

void Parse()
{
var (traits, weapons, palettes) = RoslynCodeParser.Parse(openRaFolder);
var (traits, weapons, palettes, spriteSequences) = RoslynCodeParser.Parse(openRaFolder);
traitInfos = traits;
weaponInfo = weapons;
paletteTraitInfos = palettes;
spriteSequenceInfos = spriteSequences;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,14 @@ class FromStaticFileSymbolGenerationStrategy : ICodeSymbolGenerationStrategy

readonly JObject traitsData;
readonly JObject weaponsData;
readonly JObject spriteSequencesData;

static readonly MemberLocation NoLocation = new MemberLocation(string.Empty, 0, 0);

ILookup<string, TraitInfo> traitInfos;
WeaponInfo weaponInfo;
ILookup<string, TraitInfo> paletteTraitInfos;
ILookup<string, SimpleClassInfo> spriteSequenceInfos;

public FromStaticFileSymbolGenerationStrategy(string openRaFolder)
{
Expand All @@ -34,6 +36,10 @@ public FromStaticFileSymbolGenerationStrategy(string openRaFolder)
var weaponsFile = Path.Combine(assemblyFolder, "docs", $"{LoadedVersion}-weapons.json");
var weaponsText = File.ReadAllText(weaponsFile);
weaponsData = JsonConvert.DeserializeObject<JObject>(weaponsText);

var spriteSequencesFile = Path.Combine(assemblyFolder, "docs", $"{LoadedVersion}-sprite-sequences.json");
var spriteSequencesText = File.ReadAllText(spriteSequencesFile);
spriteSequencesData = JsonConvert.DeserializeObject<JObject>(spriteSequencesText);
}

public ILookup<string, TraitInfo> GetTraitInfos()
Expand Down Expand Up @@ -97,6 +103,28 @@ public ILookup<string, TraitInfo> GetPaletteTraitInfos()
return paletteTraitInfos;
}

public ILookup<string, SimpleClassInfo> GetSpriteSequenceInfos()
{
if (spriteSequenceInfos != null)
return spriteSequenceInfos;


var typeInfos = spriteSequencesData["SpriteSequenceTypes"]!.Select(x =>
{
var baseTypes = GetBaseTypes(x);
var properties = ReadProperties(x);

var name = x["Name"].ToString();
return new SimpleClassInfo(name, name, x["Description"].ToString(),
NoLocation, baseTypes, properties, false);
});

return typeInfos.ToLookup(x => x.Name, y => y);
}


#region Private methods

string GetVersion(string openRaFolder)
{
var versionFile = Path.Combine(openRaFolder, "VERSION");
Expand All @@ -109,8 +137,7 @@ static ClassFieldInfo[] ReadProperties(JToken jToken)
{
var attributes = prop["OtherAttributes"]
.Select(attribute =>
(attribute["Name"].ToString(),
attribute["Value"].ToString().Replace("\r\n", "").Replace(" ", "").Replace(",", ", ")))
(attribute["Name"].ToString(), ""))
.ToArray();

var p = new ClassFieldInfo(prop["PropertyName"].ToString(), prop["InternalType"].ToString(), prop["UserFriendlyType"].ToString(),
Expand All @@ -126,5 +153,7 @@ static string[] GetBaseTypes(JToken jToken)
.Select(y => y.ToString())
.ToArray();
}

#endregion
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,7 @@ public interface ICodeSymbolGenerationStrategy
WeaponInfo GetWeaponInfo();

ILookup<string, TraitInfo> GetPaletteTraitInfos();

ILookup<string, SimpleClassInfo> GetSpriteSequenceInfos();
}
}
Loading

0 comments on commit e1aed4b

Please sign in to comment.