Skip to content

fix seven small bugs, and make regeneration the same on all platforms - #14

Open
Caeldeth wants to merge 8 commits into
mainfrom
fix/small-bugs
Open

Caeldeth wants to merge 8 commits into
mainfrom
fix/small-bugs

Conversation

@Caeldeth

Copy link
Copy Markdown
Contributor

Seven small bug fixes, and a regeneration fix (HXML-26). Each fix is a separate commit with its card in a Refs: trailer, so you can review one commit at a time. No fix changes world or ceridwen data.

To review the HXML-26 commit, turn on "Hide whitespace" in the diff settings. That commit converts 163 files to LF line endings, and nothing else in them changes, so with whitespace hidden it shows only tools/regenerate.sh, the new tools/normalize-generated.py, .gitattributes and README.md.

Fixes

  • HXML-3: trailing spaces in two XSD type names. Common.xsd referred to "CreatureCookies " and "CreatureImmunities ". .NET accepts the space, but libxml2 (lxml, xmllint) does not compile the schema. Now libxml2 compiles the full schema set. The generated code does not change.
  • HXML-2: SpawnGroup is a root element. Spawns.xsd declared SpawnGroup as a type only, so a validator could not check a file in spawngroups/. Now the XSD declares the element, and the generator emits XmlRoot on SpawnGroup. The hand-written XmlRoot in the partial class is removed, because two declarations do not compile. A ceridwen spawn group file now validates with lxml. SpawnGroup.MapId is now XmlIgnore: the server sets it at load, and the schema has no MapId, so Serialize() wrote an element that does not validate. No spawn group file has a MapId.
  • HXML-32: Item.MaxAbility read Level.Max. Now it reads Ab.Max, as MinAbility reads Ab.Min. Before the fix, the server ignored <Ab Max>, and each <Level Max> also limited ability. No world item sets Ab Max.
  • HXML-5: LootList + lost the gold and xp of the second list. The ?? operator has a lower precedence than +, so l1?.Gold?.Min ?? 0 + l2?.Gold?.Min ?? 0 returned the first list's value. Now each value has parentheses. Gold stays null when neither list has gold, so the sum of two empty lists is empty. The server does not use this operator yet. The additive spawn loot (HS-1573) needs it.
  • HXML-4: the unused Creature & operator is removed. It made a merged creature and then returned the first operand without the merge. Nothing calls it: ProcessAll adds each <Type> as a separate creature. Inheritance from the parent is not wanted without a data check, because most world types have their own loot. An xs:documentation annotation on CreatureTypes now says that each type needs its own Loot, Hostility, SetCookies and Description. xscgen copies it into the generated class's doc comment.
  • HXML-30: StatModifiers + lost ElementalModifiers. The result kept the left operand's list and lost the right operand's list. Thus a variant with a resistance or augment made an item without it (ceridwen var_resists1/2 and var_augs1/2), and a behavior set import lost the imported set's elemental modifiers. Now the operator adds the right operand's entries to the list. Two entries of the same type and element stay as two entries, because the server adds them when it applies them (ElementalResistance.Apply). The entries are shared, not copied, and the server never changes them.
  • HXML-31: flaky store tests. RandomCastable took its name from 1,000 values. Two castables with the same name share a store key, so Contains failed in about 3 of 1,000 runs, and four other tests had the same risk. Now each name has a new GUID.
  • HXML-26: tools/regenerate.sh gives the same output on every platform. On Windows, a regeneration with no schema change rewrote every generated file, for two causes. The sed that removed the xscgen -o path from each header matched only a POSIX path, so a Windows temp path (C:/Users/...) stayed in the header. It also did not work on macOS, where sed -i needs a suffix argument. And the line endings differed: xscgen writes CRLF on every line on Windows and CRLF only inside property bodies on Linux, so the committed files had three styles (158 mixed, 109 LF, 5 CRLF). Now regenerate.sh passes the fixed relative folder obj/xscgen as the -o path, so the header is the same on every machine. A new last step, tools/normalize-generated.py, replaces the sed: it writes src/Objects in place of obj/xscgen (and stops, writing nothing, if a generated header has neither path), and it ends every line in src/Objects and src/Enums with LF. .gitattributes keeps them LF in a checkout. A Windows run now gives no change on main's schemas, and a second run gives no change. Please run it once on Linux to confirm the same.

Tests

281 pass (15 new). Each new test for HXML-32, HXML-5 and HXML-30 fails without its fix.

  • ItemRestrictions: the four level and ability accessors.
  • LootLists: the sum of gold and xp, one side with no gold or a null list, and the sum of empty lists.
  • SpawnGroups: deserialize and serialize a SpawnGroup root, without MapId.
  • StatModifiersElementTests, VariantResolution and ImportTests: elemental modifiers through +, through a variant, and through a behavior set import.

Every changed line has full line and branch coverage.

- SetCookies and Immunities in Common.xsd referred to "CreatureCookies "
  and "CreatureImmunities ", with a space at the end.
- .NET accepts the space, but libxml2 (lxml, xmllint) does not compile
  the schema. The generated code does not change.

Refs: HXML-3
- Spawns.xsd declared SpawnGroup as a type only, but each file in
  spawngroups/ has it as its root element, so a validator could not
  check a spawn group file.
- The generator now emits XmlRoot on SpawnGroup, so the partial class no
  longer declares it.
- Make SpawnGroup.MapId XmlIgnore. The server sets it at load, and the
  schema has no MapId, so Serialize wrote an element that does not
  validate.

Refs: HXML-2
- Item.MaxAbility read Restrictions.Level.Max, so the server ignored <Ab
  Max>, and each <Level Max> also limited ability.
- Read Restrictions.Ab.Max, as MinAbility reads Ab.Min.
- Add tests for the four level and ability accessors.

Refs: HXML-32
- ?? binds more loosely than +, so "l1?.Gold?.Min ?? 0 + l2?.Gold?.Min
  ?? 0" returned the gold of the first list and lost the second. Xp had
  the same error.
- The operator always made a Gold object, so the sum of two empty lists
  was not empty.
- Add explicit parentheses, and keep Gold null when neither list has
  gold.

Refs: HXML-5, HS-1573
- It made a merged creature and then returned the first operand. Nothing
  calls it: ProcessAll adds each <Type> as a separate creature.
- Document on CreatureTypes that a type takes nothing from its parent,
  so each type needs its own Loot, Hostility, SetCookies and
  Description.

Refs: HXML-4
- The operator combined each stat but not ElementalModifiers, so the
  list of the right operand was lost: the resistance or augment of a
  variant, and the elemental modifiers of an imported behavior set.
- Add the entries of the right operand to the list. Entries with the
  same type and element stay separate, because the server adds them.

Refs: HXML-30
- RandomCastable took its name from 1,000 values. The store keys a
  castable by its name, so two castables could share a key, and the
  second Add replaced the first.
- Put a new GUID in each name.

Refs: HXML-31
- xscgen writes its -o path into each file header. The temp path
  differed on each machine, and the sed that removed it matched only a
  POSIX path and did not run on macOS. Pass the fixed relative folder
  obj/xscgen instead.
- xscgen line endings differ on each platform, and the committed files
  had three styles. A new last step, tools/normalize-generated.py,
  writes src/Objects as the header path and ends every line in
  src/Objects and src/Enums with LF.
- Add .gitattributes, so a checkout keeps those files LF.
- Convert the 163 files that had a CR. Only their line endings change.

Refs: HXML-26
@codecov

codecov Bot commented Oct 31, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 72.91%. Comparing base (77432ae) to head (da3659b).

Additional details and impacted files
@@            Coverage Diff             @@
##             main      #14      +/-   ##
==========================================
+ Coverage   70.97%   72.91%   +1.94%     
==========================================
  Files          45       45              
  Lines        1757     1754       -3     
  Branches      432      432              
==========================================
+ Hits         1247     1279      +32     
+ Misses        402      369      -33     
+ Partials      108      106       -2     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

This branch was successfully deployed

1 active deployment
deploy — da3659bf Deployed Oct 31, 2026 by Caeldeth via build (10.0.x) #136
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant