Skip to content

Commit

Permalink
Generate IDs for local achievements when using CLI (#491)
Browse files Browse the repository at this point in the history
  • Loading branch information
Jamiras authored May 22, 2024
1 parent e5d0c92 commit 372218c
Show file tree
Hide file tree
Showing 4 changed files with 208 additions and 100 deletions.
161 changes: 161 additions & 0 deletions Source/Parser/PublishedAssets.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
using Jamiras.Components;
using Jamiras.IO.Serialization;
using Jamiras.Services;
using RATools.Data;
using System;
using System.Collections.Generic;
using System.Diagnostics;

namespace RATools.Parser
{
/// <summary>
/// Class for interacting with the published assets file for a game.
/// </summary>
[DebuggerDisplay("PublishedAssets: {Title}")]
public class PublishedAssets
{
/// <summary>
/// Initializes a new instance of the <see cref="PublishedAssets"/> class.
/// </summary>
/// <param name="filename">The path to the 'XXX.json' file.</param>
public PublishedAssets(string filename)
: this(filename, ServiceRepository.Instance.FindService<IFileSystemService>())
{
}

public PublishedAssets(string filename, IFileSystemService fileSystemService)
{
_fileSystemService = fileSystemService;

_achievements = new List<Achievement>();
_leaderboards = new List<Leaderboard>();
RichPresence = null;

_filename = filename;

Read();
}

private readonly IFileSystemService _fileSystemService;
private readonly string _filename;

/// <summary>
/// Gets the title of the associated game.
/// </summary>
public string Title { get; set; }

public string Filename { get { return _filename; } }

/// <summary>
/// Gets the achievements read from the file.
/// </summary>
public IEnumerable<Achievement> Achievements
{
get { return _achievements; }
}
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private List<Achievement> _achievements;

/// <summary>
/// Gets the leaderboards read from the file.
/// </summary>
public IEnumerable<Leaderboard> Leaderboards
{
get { return _leaderboards; }
}
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private List<Leaderboard> _leaderboards;

public RichPresence RichPresence { get; private set; }

private void Read()
{
var unixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
_achievements.Clear();
_leaderboards.Clear();
RichPresence = null;

using (var stream = _fileSystemService.OpenFile(_filename, OpenFileMode.Read))
{
if (stream == null)
return;

var publishedData = new JsonObject(stream);
Title = publishedData.GetField("Title").StringValue;

var publishedAchievements = publishedData.GetField("Achievements");
if (publishedAchievements.Type == JsonFieldType.ObjectArray)
{
foreach (var publishedAchievement in publishedAchievements.ObjectArrayValue)
{
var builder = new AchievementBuilder();
builder.Id = publishedAchievement.GetField("ID").IntegerValue.GetValueOrDefault();
builder.Title = publishedAchievement.GetField("Title").StringValue;
builder.Description = publishedAchievement.GetField("Description").StringValue;
builder.Points = publishedAchievement.GetField("Points").IntegerValue.GetValueOrDefault();
builder.BadgeName = publishedAchievement.GetField("BadgeName").StringValue;
builder.ParseRequirements(Tokenizer.CreateTokenizer(publishedAchievement.GetField("MemAddr").StringValue));
builder.Category = publishedAchievement.GetField("Flags").IntegerValue.GetValueOrDefault();

var typeField = publishedAchievement.GetField("Type");
if (!String.IsNullOrEmpty(typeField.StringValue))
builder.Type = Achievement.ParseType(typeField.StringValue);

var builtAchievement = builder.ToAchievement();
builtAchievement.Published = unixEpoch.AddSeconds(publishedAchievement.GetField("Created").IntegerValue.GetValueOrDefault());
builtAchievement.LastModified = unixEpoch.AddSeconds(publishedAchievement.GetField("Modified").IntegerValue.GetValueOrDefault());

if (builtAchievement.Category == 5 || builtAchievement.Category == 3)
_achievements.Add(builtAchievement);
}
}

var publishedLeaderboards = publishedData.GetField("Leaderboards");
if (publishedLeaderboards.Type == JsonFieldType.ObjectArray)
{
foreach (var publishedLeaderboard in publishedLeaderboards.ObjectArrayValue)
{
var leaderboard = new Leaderboard();
leaderboard.Id = publishedLeaderboard.GetField("ID").IntegerValue.GetValueOrDefault();
leaderboard.Title = publishedLeaderboard.GetField("Title").StringValue;
leaderboard.Description = publishedLeaderboard.GetField("Description").StringValue;
leaderboard.Format = Leaderboard.ParseFormat(publishedLeaderboard.GetField("Format").StringValue);
leaderboard.LowerIsBetter = publishedLeaderboard.GetField("LowerIsBetter").BooleanValue;

var mem = publishedLeaderboard.GetField("Mem").StringValue;
var tokenizer = Tokenizer.CreateTokenizer(mem);
while (tokenizer.NextChar != '\0')
{
var part = tokenizer.ReadTo("::");
if (part.StartsWith("STA:"))
leaderboard.Start = Trigger.Deserialize(part.Substring(4));
else if (part.StartsWith("CAN:"))
leaderboard.Cancel = Trigger.Deserialize(part.Substring(4));
else if (part.StartsWith("SUB:"))
leaderboard.Submit = Trigger.Deserialize(part.Substring(4));
else if (part.StartsWith("VAL:"))
leaderboard.Value = Value.Deserialize(part.Substring(4));

tokenizer.Advance(2);
}

if (leaderboard.Start == null)
leaderboard.Start = new Trigger();
if (leaderboard.Cancel == null)
leaderboard.Cancel = new Trigger();
if (leaderboard.Submit == null)
leaderboard.Submit = new Trigger();
if (leaderboard.Value == null)
leaderboard.Value = new Value();

_leaderboards.Add(leaderboard);
}

var publishedRichPresence = publishedData.GetField("RichPresencePatch");
if (publishedRichPresence.Type == JsonFieldType.String)
RichPresence = new RichPresence { Script = publishedRichPresence.StringValue };
}
}
}
}
}
123 changes: 25 additions & 98 deletions Source/ViewModels/GameViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -581,8 +581,6 @@ public int LocalAchievementPoints
private set { SetValue(LocalAchievementPointsProperty, value); }
}

private static DateTime UnixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);

public void AssociateRACacheDirectory(string raCacheDirectory)
{
RACacheDirectory = raCacheDirectory;
Expand Down Expand Up @@ -631,109 +629,38 @@ private void ReadPublished()
_publishedRichPresence = null;

var fileName = Path.Combine(RACacheDirectory, GameId + ".json");
using (var stream = _fileSystemService.OpenFile(fileName, OpenFileMode.Read))
{
if (stream == null)
return;
var publishedAssets = new PublishedAssets(fileName, _fileSystemService);

var publishedData = new JsonObject(stream);
Title = publishedData.GetField("Title").StringValue;

var publishedAchievements = publishedData.GetField("Achievements");
var coreCount = 0;
var corePoints = 0;
var unofficialCount = 0;
var unofficialPoints = 0;
if (publishedAchievements.Type == JsonFieldType.ObjectArray)
var coreCount = 0;
var corePoints = 0;
var unofficialCount = 0;
var unofficialPoints = 0;
foreach (var achievement in publishedAssets.Achievements)
{
if (achievement.Category == 3)
{
foreach (var publishedAchievement in publishedAchievements.ObjectArrayValue)
{
var builder = new AchievementBuilder();
builder.Id = publishedAchievement.GetField("ID").IntegerValue.GetValueOrDefault();
builder.Title = publishedAchievement.GetField("Title").StringValue;
builder.Description = publishedAchievement.GetField("Description").StringValue;
builder.Points = publishedAchievement.GetField("Points").IntegerValue.GetValueOrDefault();
builder.BadgeName = publishedAchievement.GetField("BadgeName").StringValue;
builder.ParseRequirements(Tokenizer.CreateTokenizer(publishedAchievement.GetField("MemAddr").StringValue));
builder.Category = publishedAchievement.GetField("Flags").IntegerValue.GetValueOrDefault();

var typeField = publishedAchievement.GetField("Type");
if (!String.IsNullOrEmpty(typeField.StringValue))
builder.Type = Achievement.ParseType(typeField.StringValue);

var builtAchievement = builder.ToAchievement();
builtAchievement.Published = UnixEpoch.AddSeconds(publishedAchievement.GetField("Created").IntegerValue.GetValueOrDefault());
builtAchievement.LastModified = UnixEpoch.AddSeconds(publishedAchievement.GetField("Modified").IntegerValue.GetValueOrDefault());

if (builtAchievement.Category == 5)
{
_publishedAchievements.Add(builtAchievement);
unofficialCount++;
unofficialPoints += builtAchievement.Points;
}
else if (builtAchievement.Category == 3)
{
_publishedAchievements.Add(builtAchievement);
coreCount++;
corePoints += builtAchievement.Points;
}
}
coreCount++;
corePoints += achievement.Points;
}

var publishedLeaderboards = publishedData.GetField("Leaderboards");
if (publishedLeaderboards.Type == JsonFieldType.ObjectArray)
else
{
foreach (var publishedLeaderboard in publishedLeaderboards.ObjectArrayValue)
{
var leaderboard = new Leaderboard();
leaderboard.Id = publishedLeaderboard.GetField("ID").IntegerValue.GetValueOrDefault();
leaderboard.Title = publishedLeaderboard.GetField("Title").StringValue;
leaderboard.Description = publishedLeaderboard.GetField("Description").StringValue;
leaderboard.Format = Leaderboard.ParseFormat(publishedLeaderboard.GetField("Format").StringValue);
leaderboard.LowerIsBetter = publishedLeaderboard.GetField("LowerIsBetter").BooleanValue;

var mem = publishedLeaderboard.GetField("Mem").StringValue;
var tokenizer = Tokenizer.CreateTokenizer(mem);
while (tokenizer.NextChar != '\0')
{
var part = tokenizer.ReadTo("::");
if (part.StartsWith("STA:"))
leaderboard.Start = Trigger.Deserialize(part.Substring(4));
else if (part.StartsWith("CAN:"))
leaderboard.Cancel = Trigger.Deserialize(part.Substring(4));
else if (part.StartsWith("SUB:"))
leaderboard.Submit = Trigger.Deserialize(part.Substring(4));
else if (part.StartsWith("VAL:"))
leaderboard.Value = Value.Deserialize(part.Substring(4));

tokenizer.Advance(2);
}

if (leaderboard.Start == null)
leaderboard.Start = new Trigger();
if (leaderboard.Cancel == null)
leaderboard.Cancel = new Trigger();
if (leaderboard.Submit == null)
leaderboard.Submit = new Trigger();
if (leaderboard.Value == null)
leaderboard.Value = new Value();

_publishedLeaderboards.Add(leaderboard);
}

var publishedRichPresence = publishedData.GetField("RichPresencePatch");
if (publishedRichPresence.Type == JsonFieldType.String)
_publishedRichPresence = new RichPresence { Script = publishedRichPresence.StringValue };
unofficialCount++;
unofficialPoints += achievement.Points;
}
}

CoreAchievementCount = coreCount;
CoreAchievementPoints = corePoints;
UnofficialAchievementCount = unofficialCount;
UnofficialAchievementPoints = unofficialPoints;
CoreAchievementCount = coreCount;
CoreAchievementPoints = corePoints;
UnofficialAchievementCount = unofficialCount;
UnofficialAchievementPoints = unofficialPoints;

_logger.WriteVerbose(String.Format("Identified {0} core achievements ({1} points)", coreCount, corePoints));
_logger.WriteVerbose(String.Format("Identified {0} unofficial achievements ({1} points)", unofficialCount, unofficialPoints));
}
_publishedAchievements.AddRange(publishedAssets.Achievements);
_publishedLeaderboards.AddRange(publishedAssets.Leaderboards);
_publishedRichPresence = publishedAssets.RichPresence;
Title = publishedAssets.Title;

_logger.WriteVerbose(String.Format("Identified {0} core achievements ({1} points)", coreCount, corePoints));
_logger.WriteVerbose(String.Format("Identified {0} unofficial achievements ({1} points)", unofficialCount, unofficialPoints));
}

private void MergeAchievements(List<ViewerViewModelBase> editors, IEnumerable<AssetBase> assets,
Expand Down
22 changes: 21 additions & 1 deletion Source/rascript-cli/RAScriptCLI.cs
Original file line number Diff line number Diff line change
Expand Up @@ -236,16 +236,26 @@ public ReturnCode Run()
return ReturnCode.EvaluationError;
}

var publishedAssetsFilename = Path.Combine(OutputDirectory, String.Format("{0}.json", interpreter.GameId));
var publishedAssets = new PublishedAssets(publishedAssetsFilename, _fileSystemService);

if (_verbose && File.Exists(publishedAssetsFilename))
{
OutputStream.WriteLine("Read {0} achievements and {1} leaderboards from {2}.json",
publishedAssets.Achievements.Count(), publishedAssets.Leaderboards.Count(), interpreter.GameId);
}

var outputFileName = Path.Combine(OutputDirectory, String.Format("{0}-User.txt", interpreter.GameId));
var localAchievements = new LocalAssets(outputFileName, _fileSystemService);
localAchievements.Title = interpreter.GameTitle ?? Path.GetFileNameWithoutExtension(_inputFileName);
localAchievements.Title = interpreter.GameTitle ?? publishedAssets.Title ?? Path.GetFileNameWithoutExtension(_inputFileName);

if (_verbose)
{
OutputStream.WriteLine("Read {0} achievements and {1} leaderboards from {2}-User.txt",
localAchievements.Achievements.Count(), localAchievements.Leaderboards.Count(), interpreter.GameId);
}

var nextLocalId = AssetBase.FirstLocalId;
var existingAchievements = new List<Achievement>(localAchievements.Achievements);
foreach (var achievement in interpreter.Achievements)
{
Expand All @@ -255,6 +265,16 @@ public ReturnCode Run()
existingAchievements.Remove(existingAchievement);
achievement.Id = existingAchievement.Id;
}
else if (achievement.Id == 0)
{
existingAchievement = Achievement.FindMergeAchievement(publishedAssets.Achievements, achievement);
if (existingAchievement != null)
achievement.Id = existingAchievement.Id;
}

if (achievement.Id == 0)
achievement.Id = nextLocalId++;

localAchievements.Replace(existingAchievement, achievement);
}

Expand Down
2 changes: 1 addition & 1 deletion Tests/Regression/ScriptTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,7 @@ Stream IFileSystemService.CreateFile(string path)

Stream IFileSystemService.OpenFile(string path, OpenFileMode mode)
{
throw new NotImplementedException();
return null;
}

bool IFileSystemService.FileExists(string path)
Expand Down

0 comments on commit 372218c

Please sign in to comment.