diff --git a/res/Sectors/Faraway/Foreven-ImperiumStaple.txt b/res/Sectors/Faraway/Foreven-ImperiumStaple.txt index 1374c91ff..e955936d4 100644 --- a/res/Sectors/Faraway/Foreven-ImperiumStaple.txt +++ b/res/Sectors/Faraway/Foreven-ImperiumStaple.txt @@ -119,7 +119,7 @@ Hex Name UWP Remarks {Ix} (Ex) [Cx] N B Z PBG W A 2737 Trively E200677-7 Ni - - - 535 Na K1 II 2739 Wilthol B100200-A Lo Ni - - - 513 Na K5 V D 2831 Span B692995-C Hi In - S - 325 Na F1 V D -2837 Zymn C875657-8 Ni Ag DroyW - - R 301 Naxx D D +2837 Zymn C875657-8 Ni Ag DroyW - - R 301 NaXX D D 2838 Whinot D537899-5 - - - 604 Na F2 V D 2839 Olfaniston B206443-B Ni - M - 103 Na M6 V 2931 Iton E8C8463-6 Ni - - - 424 Na G2 V diff --git a/server/World.cs b/server/World.cs index 1fb314b0d..278bf4ca7 100644 --- a/server/World.cs +++ b/server/World.cs @@ -126,26 +126,20 @@ internal int PopulationMantissa internal double Population => Math.Pow(10, PopulationExponent) * PopulationMantissa; internal bool WaterPresent => (Hydrographics > 0) && (Atmosphere.InRange(2, 9) || Atmosphere.InRange(0xD, 0xF)); + // Only define what is needed. + // TODO: Unify with validation checks. + // Planetary internal bool IsAs => Size == 0; - internal bool IsDe => Atmosphere.InRange(2, 10) && Hydrographics == 0; - internal bool IsFl => Atmosphere >= 10 && Hydrographics > 0; - internal bool IsIc => Atmosphere.InList(0, 1) && Hydrographics > 0; internal bool IsVa => Atmosphere == 0; - internal bool IsWa => Hydrographics == 10; // Population - internal bool IsBa => PopulationExponent == 0; - internal bool IsLo => PopulationExponent < 4; - internal bool IsNi => PopulationExponent.InRange(1, 6); internal bool IsHi => PopulationExponent >= 9; // Economic internal bool IsAg => Atmosphere.InRange(4, 9) && Hydrographics.InRange(4, 8) && PopulationExponent.InRange(5, 7); - internal bool IsNa => Atmosphere.InRange(0, 3) && Hydrographics.InRange(0, 3) && PopulationExponent.InRange(6, 10); - internal bool IsIn => Atmosphere.InList(0, 1, 2, 4, 7, 9) && PopulationExponent.InList(9, 10); - internal bool IsPo => Atmosphere.InList(2, 3, 4, 5) && Hydrographics.InList(0, 1, 2, 3) && PopulationExponent > 0; - internal bool IsRi => Atmosphere.InList(6, 7, 8) && PopulationExponent.InList(6, 7, 8) && Government.InList(4, 5, 6, 7, 8, 9); + internal bool IsIn => Atmosphere.InList(0, 1, 2, 4, 7, 9, 10, 11, 12) && PopulationExponent >= 9; + internal bool IsRi => Atmosphere.InList(6, 8) && PopulationExponent.InList(6, 7, 8); internal bool IsCp => HasCode("Cp"); internal bool IsCs => HasCode("Cs"); @@ -288,6 +282,7 @@ internal void Validate(ErrorLogger errors, int lineNumber, string line) // TODO: Validate partial UWPs if (UWP.Contains('?') || UWP == "XXXXXXX-X") return; + #region Helpers Action Error = (string message) => { errors.Warning(message, lineNumber, line); }; Action ErrorIf = (bool test, string message) => { if (test) Error(message); }; Action ErrorUnless = (bool test, string message) => { if (!test) Error(message); }; @@ -309,7 +304,9 @@ internal void Validate(ErrorLogger errors, int lineNumber, string line) ErrorUnless(!HasCode(code), $"Extraneous code: {code}"); return calc; }; + #endregion + #region UWP // UWP ErrorIf(Atmosphere > 15, $"UWP: Atm={Atmosphere} out of range; should be: 0...F"); @@ -331,7 +328,9 @@ internal void Validate(ErrorLogger errors, int lineNumber, string line) ErrorUnless(TechLevel.InRange(tlmod + 1, tlmod + 6) || (PopulationExponent == 0 && TechLevel == 0), $"UWP: TL={TechLevel} out of range; should be: mods(={tlmod}) + 1D"); + #endregion + #region Codes // Planetary bool As = CC("As", Check(Size, "0") /*&& Check(Atmosphere, "0") && Check(Hydrographics, "0")*/); bool De = CC("De", Check(Atmosphere, "23456789") && Check(Hydrographics, "0")); @@ -362,94 +361,110 @@ internal void Validate(ErrorLogger errors, int lineNumber, string line) bool Pr = CC("Pr", Check(Atmosphere, "68") && Check(PopulationExponent, "59")); bool Ri = CC("Ri", Check(Atmosphere, "68") && Check(PopulationExponent, "678")); + ErrorUnless(As == IsAs, "Internal code failure: As/IsAs definitions"); + ErrorUnless(Va == IsVa, "Internal code failure: Va/IsVa definitions"); + ErrorUnless(Ag == IsAg, "Internal code failure: Ag/IsAg definitions"); + ErrorUnless(In == IsIn, "Internal code failure: In/IsIn definitions"); + ErrorUnless(Ri == IsRi, "Internal code failure: Ri/IsRi definitions"); + #endregion + // {Ix} int imp = 0; if (!string.IsNullOrWhiteSpace(Importance)) { string ix = Importance.Replace('{', ' ').Replace('}', ' ').Trim(); + if (ix != "") + { - if ("AB".Contains(Starport)) ++imp; - if ("DEX".Contains(Starport)) --imp; - if (TechLevel >= 10) ++imp; - if (TechLevel >= 16) ++imp; - if (TechLevel <= 8) --imp; - if (PopulationExponent <= 6) --imp; - if (PopulationExponent >= 9) ++imp; - if (Ag) ++imp; - if (Ri) ++imp; - if (In) ++imp; - if (Bases == "NS" || Bases == "NW" || Bases == "W" || Bases == "X" || Bases == "D" || Bases == "RT" || Bases == "CK" || Bases == "KM" || Bases == "KV") ++imp; - - ErrorUnless(Int32.Parse(ix) == imp, - $"{{Ix}} Importance={ix} incorrect; should be: {imp}"); + if ("AB".Contains(Starport)) ++imp; + if ("DEX".Contains(Starport)) --imp; + if (TechLevel >= 10) ++imp; + if (TechLevel >= 16) ++imp; + if (TechLevel <= 8) --imp; + if (PopulationExponent <= 6) --imp; + if (PopulationExponent >= 9) ++imp; + if (Ag) ++imp; + if (Ri) ++imp; + if (In) ++imp; + if (Bases == "NS" || Bases == "NW" || Bases == "W" || Bases == "X" || Bases == "D" || Bases == "RT" || Bases == "CK" || Bases == "KM" || Bases == "KV") ++imp; + + ErrorUnless(Int32.Parse(ix) == imp, + $"{{Ix}} Importance={ix} incorrect; should be: {imp}"); + } } // (Ex) if (!string.IsNullOrWhiteSpace(Economic)) { string ex = Economic.Replace('(', ' ').Replace(')', ' ').Trim(); - int resources = SecondSurvey.FromHex(ex[0]); - int labor = SecondSurvey.FromHex(ex[1]); - int infrastructure = SecondSurvey.FromHex(ex[2]); - int efficiency = Int32.Parse(ex.Substring(3)); - - if (TechLevel < 8) - ErrorUnless(resources.InRange(2, 12), - $"(Ex) Resources={resources} out of range; should be: 2D if TL(={TechLevel})<8"); - else - ErrorUnless(resources.InRange(2 + GasGiants + Belts, 12 + GasGiants + Belts), - $"(Ex) Resources={resources} out of range; should be: 2D + GG(={GasGiants}) + Belts(={Belts}) if TL(={TechLevel})=8+"); - - ErrorUnless(labor == Math.Max(0, PopulationExponent - 1), - $"(Ex) Labor={labor} incorrect; should be: Pop(={PopulationExponent}) - 1"); - - if (Ba) - ErrorUnless(infrastructure == 0, - $"(Ex) Infrastructure={infrastructure} incorrect; should be: 0 if Ba"); - else if (Lo) - ErrorUnless(infrastructure == 1, - $"(Ex) Infrastructure={infrastructure} incorrect; should be: 1 if Lo"); - else if (Ni) - ErrorUnless(infrastructure.InRange(Math.Max(0, imp + 1), Math.Max(0, imp + 6)), - $"(Ex) Infrastructure={infrastructure} out of range for Ni; should be: Imp(={imp}) + 1D"); - else - ErrorUnless(infrastructure.InRange(Math.Max(0, imp + 2), Math.Max(0, imp + 12)), - $"(Ex) Infrastructure={infrastructure} out of range; should be: Imp(={imp}) + 2D"); - - ErrorUnless(efficiency.InRange(-5, 5), - $"(Ex) Efficiency={efficiency} out of range; should be: Flux"); + if (ex != "") + { + int resources = SecondSurvey.FromHex(ex[0]); + int labor = SecondSurvey.FromHex(ex[1]); + int infrastructure = SecondSurvey.FromHex(ex[2]); + int efficiency = Int32.Parse(ex.Substring(3)); + + if (TechLevel < 8) + ErrorUnless(resources.InRange(2, 12), + $"(Ex) Resources={resources} out of range; should be: 2D if TL(={TechLevel})<8"); + else + ErrorUnless(resources.InRange(2 + GasGiants + Belts, 12 + GasGiants + Belts), + $"(Ex) Resources={resources} out of range; should be: 2D + GG(={GasGiants}) + Belts(={Belts}) if TL(={TechLevel})=8+"); + + ErrorUnless(labor == Math.Max(0, PopulationExponent - 1), + $"(Ex) Labor={labor} incorrect; should be: Pop(={PopulationExponent}) - 1"); + + if (Ba) + ErrorUnless(infrastructure == 0, + $"(Ex) Infrastructure={infrastructure} incorrect; should be: 0 if Ba"); + else if (Lo) + ErrorUnless(infrastructure == 1, + $"(Ex) Infrastructure={infrastructure} incorrect; should be: 1 if Lo"); + else if (Ni) + ErrorUnless(infrastructure.InRange(Math.Max(0, imp + 1), Math.Max(0, imp + 6)), + $"(Ex) Infrastructure={infrastructure} out of range for Ni; should be: Imp(={imp}) + 1D"); + else + ErrorUnless(infrastructure.InRange(Math.Max(0, imp + 2), Math.Max(0, imp + 12)), + $"(Ex) Infrastructure={infrastructure} out of range; should be: Imp(={imp}) + 2D"); + + ErrorUnless(efficiency.InRange(-5, 5), + $"(Ex) Efficiency={efficiency} out of range; should be: Flux"); + } } // [Cx] if (!string.IsNullOrWhiteSpace(Cultural)) { string cx = Cultural.Replace('[', ' ').Replace(']', ' ').Trim(); - int homogeneity = SecondSurvey.FromHex(cx[0]); - int acceptance = SecondSurvey.FromHex(cx[1]); - int strangeness = SecondSurvey.FromHex(cx[2]); - int symbols = SecondSurvey.FromHex(cx[3]); - - if (PopulationExponent == 0) + if (cx != "") { - ErrorUnless(homogeneity == 0, - $"[Cx] Homogeneity={homogeneity} incorrect; should be: 0 if Pop=0"); - ErrorUnless(acceptance == 0, - $"[Cx] Acceptance={acceptance} incorrect; should be: 0 if Pop=0"); - ErrorUnless(strangeness == 0, - $"[Cx] Strangeness={strangeness} incorrect; should be: 0 if Pop=0"); - ErrorUnless(symbols == 0, - $"[Cx] Symbols={symbols} incorrect; should be: 0 if Pop=0"); - } - else - { - ErrorUnless(homogeneity.InRange(Math.Max(1, PopulationExponent - 5), Math.Max(1, PopulationExponent + 5)), - $"[Cx] Homogeneity={homogeneity} out of range; should be: Pop(={PopulationExponent}) + Flux"); - ErrorUnless(acceptance == Math.Max(1, PopulationExponent + imp), - $"[Cx] Acceptance={acceptance} incorrect; should be: Pop(={PopulationExponent}) + Imp(={imp})"); - ErrorUnless(strangeness.InRange(Math.Max(1, 5 - 5), Math.Max(1, 5 + 5)), - $"[Cx] Strangeness={strangeness} out of range; should be: Flux + 5"); - ErrorUnless(symbols.InRange(Math.Max(1, TechLevel - 5), Math.Max(1, TechLevel + 5)), - $"[Cx] Symbols={symbols} out of range; should be: TL(={TechLevel}) + Flux"); + int homogeneity = SecondSurvey.FromHex(cx[0]); + int acceptance = SecondSurvey.FromHex(cx[1]); + int strangeness = SecondSurvey.FromHex(cx[2]); + int symbols = SecondSurvey.FromHex(cx[3]); + + if (PopulationExponent == 0) + { + ErrorUnless(homogeneity == 0, + $"[Cx] Homogeneity={homogeneity} incorrect; should be: 0 if Pop=0"); + ErrorUnless(acceptance == 0, + $"[Cx] Acceptance={acceptance} incorrect; should be: 0 if Pop=0"); + ErrorUnless(strangeness == 0, + $"[Cx] Strangeness={strangeness} incorrect; should be: 0 if Pop=0"); + ErrorUnless(symbols == 0, + $"[Cx] Symbols={symbols} incorrect; should be: 0 if Pop=0"); + } + else + { + ErrorUnless(homogeneity.InRange(Math.Max(1, PopulationExponent - 5), Math.Max(1, PopulationExponent + 5)), + $"[Cx] Homogeneity={homogeneity} out of range; should be: Pop(={PopulationExponent}) + Flux"); + ErrorUnless(acceptance == Math.Max(1, PopulationExponent + imp), + $"[Cx] Acceptance={acceptance} incorrect; should be: Pop(={PopulationExponent}) + Imp(={imp})"); + ErrorUnless(strangeness.InRange(Math.Max(1, 5 - 5), Math.Max(1, 5 + 5)), + $"[Cx] Strangeness={strangeness} out of range; should be: Flux + 5"); + ErrorUnless(symbols.InRange(Math.Max(1, TechLevel - 5), Math.Max(1, TechLevel + 5)), + $"[Cx] Symbols={symbols} out of range; should be: TL(={TechLevel}) + Flux"); + } } } diff --git a/server/admin/AdminHandler.cs b/server/admin/AdminHandler.cs index e85d8290e..2e75c4d76 100644 --- a/server/admin/AdminHandler.cs +++ b/server/admin/AdminHandler.cs @@ -57,6 +57,16 @@ protected static string GetStringOption(HttpContext context, string name) return queryDefaults[name].ToString(); return null; } + + protected static bool GetBoolOption(HttpContext context, string name, bool defaultValue = false) + { + if (context.Request[name] != null) + return context.Request[name] == "1"; + var queryDefaults = Defaults(context); + if (queryDefaults != null && queryDefaults.ContainsKey(name)) + return queryDefaults[name].ToString() == "1"; + return defaultValue; + } } internal class AdminHandler : AdminHandlerBase diff --git a/server/admin/ErrorsHandler.cs b/server/admin/ErrorsHandler.cs index 110f81b9a..97e94d210 100644 --- a/server/admin/ErrorsHandler.cs +++ b/server/admin/ErrorsHandler.cs @@ -18,6 +18,7 @@ protected override void Process(System.Web.HttpContext context, ResourceManager string type = GetStringOption(context, "type"); string milieu = GetStringOption(context, "milieu"); string tag = GetStringOption(context, "tag"); + ErrorLogger.Severity severity = GetBoolOption(context, "warnings", true) ? 0 : ErrorLogger.Severity.Error; // NOTE: This (re)initializes a static data structure used for // resolving names into sector locations, so needs to be run @@ -37,7 +38,7 @@ protected override void Process(System.Web.HttpContext context, ResourceManager foreach (var sector in sectorQuery) { - context.Response.Output.WriteLine(sector.Names[0].Text); + context.Response.Output.WriteLine($"{sector.Names[0].Text} - {sector.Milieu}"); #if DEBUG int error_count = 0; int warning_count = 0; @@ -51,7 +52,7 @@ protected override void Process(System.Web.HttpContext context, ResourceManager context.Response.Output.WriteLine($"{worlds.Count()} world(s) - population: {pop / 1e9:#,###.##} billion"); else context.Response.Output.WriteLine($"{worlds.Count()} world(s) - population: N/A"); - worlds.ErrorList.Report(context.Response.Output); + worlds.ErrorList.Report(context.Response.Output, severity); error_count += worlds.ErrorList.CountOf(ErrorLogger.Severity.Error); warning_count += worlds.ErrorList.CountOf(ErrorLogger.Severity.Warning); } @@ -86,7 +87,8 @@ protected override void Process(System.Web.HttpContext context, ResourceManager } else if (distance > 4) { - context.Response.Output.WriteLine($"Warning: Route length {distance}: {route.ToString()}"); + if (severity <= ErrorLogger.Severity.Warning) + context.Response.Output.WriteLine($"Warning: Route length {distance}: {route.ToString()}"); ++warning_count; } /* diff --git a/server/utilities/Util.cs b/server/utilities/Util.cs index fd368c141..09c7c612b 100644 --- a/server/utilities/Util.cs +++ b/server/utilities/Util.cs @@ -63,12 +63,7 @@ internal static class ExtensionMethods } public static bool InRange(this IComparable item, T a, T b) => item.CompareTo(a) >= 0 && item.CompareTo(b) <= 0; - - public static bool InList(this T item, T o1, T o2) => item.Equals(o1) || item.Equals(o2); - public static bool InList(this T item, T o1, T o2, T o3) => item.Equals(o1) || item.Equals(o2) || item.Equals(o3); - public static bool InList(this T item, T o1, T o2, T o3, T o4) => item.Equals(o1) || item.Equals(o2) || item.Equals(o3) || item.Equals(o4); - public static bool InList(this T item, T o1, T o2, T o3, T o4, T o5) => item.Equals(o1) || item.Equals(o2) || item.Equals(o3) || item.Equals(o4) || item.Equals(o5); - public static bool InList(this T item, T o1, T o2, T o3, T o4, T o5, T o6) => item.Equals(o1) || item.Equals(o2) || item.Equals(o3) || item.Equals(o4) || item.Equals(o5) || item.Equals(o6); + public static bool InList(this T item, params T[] options) => options.Contains(item); #endregion #region String Methods @@ -327,11 +322,12 @@ public void Clear() internal class ErrorLogger { + // In increasing order, for filtering public enum Severity { - Fatal, - Error, + Hint, Warning, - Hint + Error, + Fatal } public struct Record @@ -381,11 +377,12 @@ public void Log(Severity sev, string message, int lineNumber, string line) public int CountOf(Severity sev) => log.Where(r => r.severity == sev).Count(); - public void Report(TextWriter writer) + public void Report(TextWriter writer, Severity minSeverity) { foreach (var record in log) { - writer.WriteLine($"{record.severity.ToString()}: {record.message}"); + if (record.severity >= minSeverity) + writer.WriteLine($"{record.severity.ToString()}: {record.message}"); } } @@ -393,7 +390,7 @@ public override string ToString() { using (StringWriter writer = new StringWriter()) { - Report(writer); + Report(writer, Severity.Hint); writer.WriteLine($"{CountOf(Severity.Error)} errors, {CountOf(Severity.Warning)} warnings."); return writer.ToString(); }