public override void Start(ICoreAPI api) { this.api = api; worldConfig = api.World.Config; if (worldConfig == null) { worldConfig = new TreeAttribute(); } List <IAsset> entries = api.Assets.GetMany("patches/"); int appliedCount = 0; int notfoundCount = 0; int errorCount = 0; int totalCount = 0; int unmetConditionCount = 0; foreach (IAsset asset in entries) { JsonPatch[] patches = null; try { patches = asset.ToObject <JsonPatch[]>(); } catch (Exception e) { api.World.Logger.Error("Failed loading patches file {0}: {1}", asset.Location, e); } for (int j = 0; patches != null && j < patches.Length; j++) { JsonPatch patch = patches[j]; if (patch.Condition != null) { IAttribute attr = worldConfig[patch.Condition.When]; if (attr == null) { continue; } if (patch.Condition.useValue) { patch.Value = new JsonObject(JToken.Parse(attr.ToJsonToken())); } else { if (!patch.Condition.IsValue.Equals(attr.GetValue() + "", StringComparison.InvariantCultureIgnoreCase)) { unmetConditionCount++; continue; } } } totalCount++; ApplyPatch(j, asset.Location, patch, ref appliedCount, ref notfoundCount, ref errorCount); } } StringBuilder sb = new StringBuilder(); sb.Append("JsonPatch Loader: "); if (totalCount == 0) { sb.Append(Lang.Get("Nothing to patch", totalCount)); } else { sb.Append(Lang.Get("{0} patches total", totalCount)); if (appliedCount > 0) { sb.Append(Lang.Get(", successfully applied {0} patches", appliedCount)); } if (notfoundCount > 0) { sb.Append(Lang.Get(", missing files on {0} patches", notfoundCount)); } if (unmetConditionCount > 0) { sb.Append(Lang.Get(", unmet conditions on {0} patches", unmetConditionCount)); } if (errorCount > 0) { sb.Append(Lang.Get(", had errors on {0} patches", errorCount)); } else { sb.Append(Lang.Get(", no errors", errorCount)); } } api.World.Logger.Notification(sb.ToString()); base.Start(api); }
private void ApplyPatch(int patchIndex, AssetLocation patchSourcefile, JsonPatch jsonPatch, ref int applied, ref int notFound, ref int errorCount) { if (jsonPatch.SideType != EnumAppSide.Universal && jsonPatch.SideType != api.Side) { return; } var path = jsonPatch.File.Path; if (!path.EndsWith(".json")) { path += ".json"; } var asset = api.Assets.TryGet(path); if (asset == null) { api.World.Logger.VerboseDebug("Patch {0} in {1}: File {2} not found", patchIndex, patchSourcefile, path); notFound++; return; } Operation op = null; switch (jsonPatch.Op) { case EnumJsonPatchOp.Add: if (jsonPatch.Value == null) { api.World.Logger.Error("Patch {0} in {1} failed probably because it is an add operation and the value property is not set or misspelled", patchIndex, patchSourcefile); errorCount++; return; } op = new AddOperation() { Path = new Tavis.JsonPointer(jsonPatch.Path), Value = jsonPatch.Value.Token }; break; case EnumJsonPatchOp.Remove: op = new RemoveOperation() { Path = new Tavis.JsonPointer(jsonPatch.Path) }; break; case EnumJsonPatchOp.Replace: if (jsonPatch.Value == null) { api.World.Logger.Error("Patch {0} in {1} failed probably because it is a replace operation and the value property is not set or misspelled", patchIndex, patchSourcefile); errorCount++; return; } op = new ReplaceOperation() { Path = new Tavis.JsonPointer(jsonPatch.Path), Value = jsonPatch.Value.Token }; break; case EnumJsonPatchOp.Copy: op = new CopyOperation() { Path = new Tavis.JsonPointer(jsonPatch.Path), FromPath = new JsonPointer(jsonPatch.FromPath) }; break; case EnumJsonPatchOp.Move: op = new MoveOperation() { Path = new Tavis.JsonPointer(jsonPatch.Path), FromPath = new JsonPointer(jsonPatch.FromPath) }; break; } PatchDocument patchdoc = new PatchDocument(op); JToken token = null; try { token = JToken.Parse(asset.ToText()); } catch (Exception e) { api.World.Logger.Error("Patch {0} in {1} failed probably because the syntax of the value is broken: {2}", patchIndex, patchSourcefile, e); errorCount++; return; } try { patchdoc.ApplyTo(token); } catch (Tavis.PathNotFoundException p) { api.World.Logger.Error("Patch {0} in {1} failed because supplied path {2} is invalid: {3}", patchIndex, patchSourcefile, jsonPatch.Path, p.Message); errorCount++; return; } catch (Exception e) { api.World.Logger.Error("Patch {0} in {1} failed, following Exception was thrown: {2}", patchIndex, patchSourcefile, e.Message); errorCount++; return; } string text = token.ToString(); asset.Data = System.Text.Encoding.UTF8.GetBytes(text); applied++; }
private void ApplyPatch(int patchIndex, AssetLocation patchSourcefile, JsonPatch jsonPatch, ref int applied, ref int notFound, ref int errorCount) { EnumAppSide targetSide = jsonPatch.Side == null ? jsonPatch.File.Category.SideType : (EnumAppSide)jsonPatch.Side; if (targetSide != EnumAppSide.Universal && jsonPatch.Side != api.Side) { return; } var loc = jsonPatch.File.Clone(); if (jsonPatch.File.Path.EndsWith("*")) { List <IAsset> assets = api.Assets.GetMany(jsonPatch.File.Path.TrimEnd('*'), jsonPatch.File.Domain, false); foreach (var val in assets) { jsonPatch.File = val.Location; ApplyPatch(patchIndex, patchSourcefile, jsonPatch, ref applied, ref notFound, ref errorCount); } jsonPatch.File = loc; return; } if (!loc.Path.EndsWith(".json")) { loc.Path += ".json"; } var asset = api.Assets.TryGet(loc); if (asset == null) { if (jsonPatch.File.Category == null) { api.World.Logger.VerboseDebug("Patch {0} in {1}: File {2} not found. Wrong asset category", patchIndex, patchSourcefile, loc); } else { EnumAppSide catSide = jsonPatch.File.Category.SideType; if (catSide != EnumAppSide.Universal && api.Side != catSide) { api.World.Logger.VerboseDebug("Patch {0} in {1}: File {2} not found. Hint: This asset is usually only loaded {3} side", patchIndex, patchSourcefile, loc, catSide); } else { api.World.Logger.VerboseDebug("Patch {0} in {1}: File {2} not found", patchIndex, patchSourcefile, loc); } } notFound++; return; } Operation op = null; switch (jsonPatch.Op) { case EnumJsonPatchOp.Add: if (jsonPatch.Value == null) { api.World.Logger.Error("Patch {0} in {1} failed probably because it is an add operation and the value property is not set or misspelled", patchIndex, patchSourcefile); errorCount++; return; } op = new AddOperation() { Path = new Tavis.JsonPointer(jsonPatch.Path), Value = jsonPatch.Value.Token }; break; case EnumJsonPatchOp.Remove: op = new RemoveOperation() { Path = new Tavis.JsonPointer(jsonPatch.Path) }; break; case EnumJsonPatchOp.Replace: if (jsonPatch.Value == null) { api.World.Logger.Error("Patch {0} in {1} failed probably because it is a replace operation and the value property is not set or misspelled", patchIndex, patchSourcefile); errorCount++; return; } op = new ReplaceOperation() { Path = new Tavis.JsonPointer(jsonPatch.Path), Value = jsonPatch.Value.Token }; break; case EnumJsonPatchOp.Copy: op = new CopyOperation() { Path = new Tavis.JsonPointer(jsonPatch.Path), FromPath = new JsonPointer(jsonPatch.FromPath) }; break; case EnumJsonPatchOp.Move: op = new MoveOperation() { Path = new Tavis.JsonPointer(jsonPatch.Path), FromPath = new JsonPointer(jsonPatch.FromPath) }; break; } PatchDocument patchdoc = new PatchDocument(op); JToken token = null; try { token = JToken.Parse(asset.ToText()); } catch (Exception e) { api.World.Logger.Error("Patch {0} (target: {3}) in {1} failed probably because the syntax of the value is broken: {2}", patchIndex, patchSourcefile, e, loc); errorCount++; return; } try { patchdoc.ApplyTo(token); } catch (Tavis.PathNotFoundException p) { api.World.Logger.Error("Patch {0} (target: {4}) in {1} failed because supplied path {2} is invalid: {3}", patchIndex, patchSourcefile, jsonPatch.Path, p.Message, loc); errorCount++; return; } catch (Exception e) { api.World.Logger.Error("Patch {0} (target: {3}) in {1} failed, following Exception was thrown: {2}", patchIndex, patchSourcefile, e.Message, loc); errorCount++; return; } string text = token.ToString(); asset.Data = System.Text.Encoding.UTF8.GetBytes(text); applied++; }
public override void AssetsLoaded(ICoreAPI api) // This is done before assets and items are registerd etc, and before remapping, because of the ExecuteOrder, and (server-side) because of the position of ModHandler early in the ServerSystems list { this.api = api; worldConfig = api.World.Config; if (worldConfig == null) { worldConfig = new TreeAttribute(); } List <IAsset> entries = api.Assets.GetMany("patches/"); int appliedCount = 0; int notfoundCount = 0; int errorCount = 0; int totalCount = 0; int unmetConditionCount = 0; HashSet <string> loadedModIds = new HashSet <string>(api.ModLoader.Mods.Select((m) => m.Info.ModID).ToList()); foreach (IAsset asset in entries) { JsonPatch[] patches = null; try { patches = asset.ToObject <JsonPatch[]>(); } catch (Exception e) { api.Logger.Error("Failed loading patches file {0}: {1}", asset.Location, e); } for (int j = 0; patches != null && j < patches.Length; j++) { JsonPatch patch = patches[j]; if (patch.Condition != null) { IAttribute attr = worldConfig[patch.Condition.When]; if (attr == null) { continue; } if (patch.Condition.useValue) { patch.Value = new JsonObject(JToken.Parse(attr.ToJsonToken())); } else { if (!patch.Condition.IsValue.Equals(attr.GetValue() + "", StringComparison.InvariantCultureIgnoreCase)) { api.Logger.VerboseDebug("Patch file {0}, patch {1}: Unmet IsValue condition ({2}!={3})", asset.Location, j, patch.Condition.IsValue, attr.GetValue() + ""); unmetConditionCount++; continue; } } } if (patch.DependsOn != null) { bool enabled = true; foreach (var dependence in patch.DependsOn) { bool loaded = loadedModIds.Contains(dependence.modid); enabled = enabled && (loaded ^ dependence.invert); } if (!enabled) { unmetConditionCount++; api.Logger.VerboseDebug("Patch file {0}, patch {1}: Unmet DependsOn condition ({2})", asset.Location, j, string.Join(",", patch.DependsOn.Select(pd => (pd.invert ? "!" : "") + pd.modid))); continue; } } totalCount++; ApplyPatch(j, asset.Location, patch, ref appliedCount, ref notfoundCount, ref errorCount); } } StringBuilder sb = new StringBuilder(); sb.Append("JsonPatch Loader: "); if (totalCount == 0) { sb.Append(Lang.Get("Nothing to patch", totalCount)); } else { sb.Append(Lang.Get("{0} patches total", totalCount)); if (appliedCount > 0) { sb.Append(Lang.Get(", successfully applied {0} patches", appliedCount)); } if (notfoundCount > 0) { sb.Append(Lang.Get(", missing files on {0} patches", notfoundCount)); } if (unmetConditionCount > 0) { sb.Append(Lang.Get(", unmet conditions on {0} patches", unmetConditionCount)); } if (errorCount > 0) { sb.Append(Lang.Get(", had errors on {0} patches", errorCount)); } else { sb.Append(Lang.Get(", no errors", errorCount)); } } api.Logger.Notification(sb.ToString()); api.Logger.VerboseDebug("Patchloader finished"); }