-
-
Notifications
You must be signed in to change notification settings - Fork 348
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
11 changed files
with
237 additions
and
51 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
using System; | ||
using System.IO; | ||
using System.Linq; | ||
using System.Collections.Generic; | ||
|
||
using log4net; | ||
using ICSharpCode.SharpZipLib.Zip; | ||
using ParsecSharp; | ||
using KSPMMCfgParser; | ||
using static KSPMMCfgParser.KSPMMCfgParser; | ||
|
||
namespace CKAN.NetKAN.Services | ||
{ | ||
using NodeCache = Dictionary<CkanModule, ConfigNodesCacheEntry>; | ||
|
||
/// <summary> | ||
/// Since parsing cfg files can be expensive, cache results for 15 minutes | ||
/// </summary> | ||
internal sealed class CachingConfigParser : IConfigParser | ||
{ | ||
public CachingConfigParser(IModuleService modSvc) | ||
{ | ||
moduleService = modSvc; | ||
} | ||
|
||
public Dictionary<InstallableFile, KSPConfigNode[]> GetConfigNodes(CkanModule module, ZipFile zip, GameInstance inst) | ||
=> GetCachedNodes(module) ?? AddAndReturn( | ||
module, | ||
moduleService.GetConfigFiles(module, zip, inst).ToDictionary( | ||
cfg => cfg, | ||
cfg => ConfigFile.ToArray() | ||
.Parse(zip.GetInputStream(cfg.source)) | ||
.CaseOf(failure => | ||
{ | ||
log.InfoFormat("{0}:{1}:{2}: {3}", | ||
inst.ToRelativeGameDir(cfg.destination), | ||
failure.State.Position.Line, | ||
failure.State.Position.Column, | ||
failure.Message); | ||
return new KSPConfigNode[] { }; | ||
}, | ||
success => success.Value))); | ||
|
||
private Dictionary<InstallableFile, KSPConfigNode[]> AddAndReturn(CkanModule module, | ||
Dictionary<InstallableFile, KSPConfigNode[]> nodes) | ||
{ | ||
log.DebugFormat("Caching config nodes for {0}", module); | ||
cache.Add(module, | ||
new ConfigNodesCacheEntry() | ||
{ | ||
Value = nodes, | ||
Timestamp = DateTime.Now, | ||
}); | ||
return nodes; | ||
} | ||
|
||
private Dictionary<InstallableFile, KSPConfigNode[]> GetCachedNodes(CkanModule module) | ||
{ | ||
if (cache.TryGetValue(module, out ConfigNodesCacheEntry entry)) | ||
{ | ||
if (DateTime.Now - entry.Timestamp < stringCacheLifetime) | ||
{ | ||
log.DebugFormat("Using cached nodes for {0}", module); | ||
return entry.Value; | ||
} | ||
else | ||
{ | ||
log.DebugFormat("Purging stale nodes for {0}", module); | ||
cache.Remove(module); | ||
} | ||
} | ||
return null; | ||
} | ||
|
||
private readonly IModuleService moduleService; | ||
private readonly NodeCache cache = new NodeCache(); | ||
// Re-use parse results within 15 minutes | ||
private static readonly TimeSpan stringCacheLifetime = new TimeSpan(0, 15, 0); | ||
private static readonly ILog log = LogManager.GetLogger(typeof(CachingConfigParser)); | ||
} | ||
|
||
public class ConfigNodesCacheEntry | ||
{ | ||
public Dictionary<InstallableFile, KSPConfigNode[]> Value; | ||
public DateTime Timestamp; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
|
||
using ICSharpCode.SharpZipLib.Zip; | ||
using KSPMMCfgParser; | ||
|
||
namespace CKAN.NetKAN.Services | ||
{ | ||
internal interface IConfigParser | ||
{ | ||
Dictionary<InstallableFile, KSPConfigNode[]> GetConfigNodes(CkanModule module, ZipFile zip, GameInstance inst); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
using System.Linq; | ||
|
||
using Newtonsoft.Json.Linq; | ||
using ICSharpCode.SharpZipLib.Zip; | ||
using log4net; | ||
|
||
using CKAN.NetKAN.Services; | ||
using CKAN.NetKAN.Model; | ||
using CKAN.Games; | ||
|
||
namespace CKAN.NetKAN.Validators | ||
{ | ||
internal sealed class ForClauseValidator : IValidator | ||
{ | ||
public ForClauseValidator(IHttpService http, IModuleService moduleService, IConfigParser parser) | ||
{ | ||
_http = http; | ||
_moduleService = moduleService; | ||
_parser = parser; | ||
} | ||
|
||
public void Validate(Metadata metadata) | ||
{ | ||
Log.Info("Validating that :FOR[] clauses specify the right mod"); | ||
|
||
JObject json = metadata.Json(); | ||
CkanModule mod = CkanModule.FromJson(json.ToString()); | ||
if (!mod.IsDLC) | ||
{ | ||
var package = _http.DownloadModule(metadata); | ||
if (!string.IsNullOrEmpty(package)) | ||
{ | ||
ZipFile zip = new ZipFile(package); | ||
GameInstance inst = new GameInstance(new KerbalSpaceProgram(), "/", "dummy", new NullUser()); | ||
|
||
// Check for :FOR[identifier] in .cfg files | ||
var mismatchedIdentifiers = KerbalSpaceProgram | ||
.IdentifiersFromConfigNodes( | ||
_parser.GetConfigNodes(mod, zip, inst) | ||
.SelectMany(kvp => kvp.Value)) | ||
.Where(ident => ident != mod.identifier | ||
&& Identifier.ValidIdentifierPattern.IsMatch(ident)) | ||
.OrderBy(s => s) | ||
.ToArray(); | ||
if (mismatchedIdentifiers.Any()) | ||
{ | ||
Log.WarnFormat("Found :FOR[] clauses with the wrong identifiers: {0}", | ||
string.Join(", ", mismatchedIdentifiers)); | ||
} | ||
} | ||
} | ||
} | ||
|
||
private readonly IHttpService _http; | ||
private readonly IModuleService _moduleService; | ||
private readonly IConfigParser _parser; | ||
|
||
private static readonly ILog Log = LogManager.GetLogger(typeof(ForClauseValidator)); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.