Skip to content

Feature/epona branch instances#326

Closed
Caeldeth wants to merge 90 commits into
mainfrom
feature/epona-branch-instances
Closed

Feature/epona branch instances#326
Caeldeth wants to merge 90 commits into
mainfrom
feature/epona-branch-instances

Conversation

@Caeldeth

@Caeldeth Caeldeth commented Apr 24, 2026

Copy link
Copy Markdown
Collaborator

Description

Adds the minimal server-side support needed for Epona's branch-aware server instance management (multi-target expansion plan, Stage 3) and embedded-console mode (Stage 3.0 follow-up). Two small, independent changes: (1) a conditional Hybrasyl.Xml reference in Hybrasyl.csproj that lets Epona swap the NuGet package for a local ProjectReference via a gitignored Directory.Build.props, and (2) a Console.IsInputRedirected guard around the fatal-exit Console.ReadKey() in Game.cs so the server exits cleanly when launched with piped stdio instead of throwing InvalidOperationException. No behavior change for standalone terminal runs, CI, or developers not using Epona. Design docs added under docs/.

Related PRs

branch PR

Checklist

  • Tested and working locally
  • Reviewed
  • Tested and working on dev server
  • Deployed to staging

How to QA

  1. Default NuGet path (regression check): With no Directory.Build.props in the repo root, run dotnet build hybrasyl/Hybrasyl.csproj. Confirm it restores and builds against Hybrasyl.Xml 0.9.4.11 from NuGet exactly as before.
  2. Local XML project reference: Create Directory.Build.props in the repo root with:
    <Project>
      <PropertyGroup>
        <UseLocalXml>true</UseLocalXml>
        <LocalXmlProjectPath>E:\Dark Ages Dev\Repos\xml\src\Hybrasyl.Xml.csproj</LocalXmlProjectPath>
      </PropertyGroup>
    </Project>
    Run dotnet build hybrasyl/Hybrasyl.csproj and confirm it builds against the local XML source (check build output for the project reference). Delete the file and confirm the NuGet path is restored.
  3. Standalone ReadKey behavior (unchanged): Run dotnet hybrasyl/bin/.../Hybrasyl.dll --datadir <bad path> in a terminal to trigger World.Init() failure. Confirm the "Press any key to exit." message prints and the process waits for a keypress.
  4. Redirected-stdin ReadKey behavior (new): Run the same command through a wrapper that pipes stdin (e.g. echo | dotnet hybrasyl/bin/.../Hybrasyl.dll --datadir <bad path> or launch via Epona's embedded mode). Confirm the process exits immediately with code 1, the fatal log line is still emitted, and no InvalidOperationException is thrown.
  5. Run dotnet test — all existing tests stay green.

Impacted Areas in Hybrasyl

  • Build configuration (hybrasyl/Hybrasyl.csproj, .gitignore) — conditional Hybrasyl.Xml reference
  • Startup / world init failure path (hybrasyl/Game.cs) — fatal-exit console handling
  • Documentation (docs/epona-branch-instances.md, docs/embedded-console-readkey-guard.md)
  • No runtime, gameplay, networking, or data changes

baughj added 30 commits July 22, 2024 15:55
…legends; reorganization of chat commands into single files
* Clean up world init

* Fix longstanding bug in jobs

* Humanize display of job intervals

* Handle OnSpawn() for monsters now that script copies are no longer a thing

* Cleanups in various places
…se conditions

* Fix bug with Disoriented (cut / paste fail)

* Add DirectHeal / DirectDamage to be used by scripting, which will take new conditions into
  account
… gained on level, XP to next level, etc

* Support Reactor display names

* Use NPC display names correctly
…otations

* Add support for several new monster targeting conditions
* Misc fixes
baughj and others added 25 commits April 2, 2026 14:19
Only install mono-runtime (for NaturalDocs) and zip (for release
packaging). Everything else is already on the runner image.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The BreakStealth code path iterated over Statuses (the persistence
list, only populated during save) instead of CurrentStatuses (the
runtime dictionary). This meant the invisibility status was never
actually removed from CurrentStatuses when assail broke stealth -
only the Condition flag was cleared.

Additionally, the code re-looked up the XML status via GetByIndex
which could return null. Replaced with direct access to
CreatureStatus.ConditionChanges and .ToList() to safely iterate
while modifying the collection.

Also:
- Add TestHelpers.WaitFor() for polling async game state in tests
  instead of fragile Thread.Sleep
- Replace platform-specific path config in test fixture with
  env var (HYB_WORLD_DIR) + automatic resolution from solution root
- Tests now pass 10/10 on full suite runs

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Remove the generate/show test config steps that wrote a JSON file
with hardcoded paths. The test fixture now resolves world data via
HYB_WORLD_DIR env var with automatic fallback to sibling checkout.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Allows the BreakStealth code path to check condition flags directly
on the status object without re-looking up the XML definition.
Fixes CI build failure from previous commit (ConditionChanges was
only on the concrete CreatureStatus class, not the interface).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace runtime-only Dockerfile (required pre-built binaries) with
a proper multi-stage build from source. Anyone can now docker build
without a local dotnet SDK.

- Build stage: SDK image, NuGet restore with layer caching, publish
  self-contained for linux-x64
- Runtime stage: runtime-deps (smaller than runtime since
  self-contained bundles the runtime), non-root hybrasyl user

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
MSBuildGitHash can't run git rev-parse inside Docker (no .git dir).
Add GIT_HASH build arg (defaults to "unknown") and pass it through
as /p:MSBuildGitHashValue. CI passes SHORT_SHA, standalone docker
build gets "unknown" which the server already handles gracefully.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace outdated compose config with working setup:
- Init service clones ceridwen into shared volume (idempotent)
- Switch from bitnami/redis to valkey/valkey
- Use ghcr.io/hybrasyl/server:latest image
- Redis config via env vars, config.xml mounted from contrib
- Named volumes, drop deprecated version/links/networks
- docker compose up just works with no prior setup

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
alpine/git has git as ENTRYPOINT so sh -c wrapper doesn't work.
Switch to plain alpine with apk add git. Add platform: linux/amd64
for ARM Mac compatibility via Rosetta.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
LogInit crashed with NullReferenceException on Path.Combine when
dataDirectory was null. Now falls back to console-only logging
when no log directory is specified, matching the documented behavior
in the --logDir help text. Also handles null globalConfig (no
<Logging> element in config XML).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Shell syntax (${VAR}) doesn't work in action args, need GitHub
Actions expression syntax (${{ env.VAR }}). Also fixed missing
closing paren.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Login server: replace direct dictionary access on ExpectedConnections
with TryGetValue. Invalid redirect IDs now log a warning and
disconnect instead of crashing the handler with KeyNotFoundException.

Merchant handlers: add null checks on Inventory[slot] in
SellItemWithQuantity, SendParcelQuantity, and DepositItemQuantity.
A client sending an empty slot no longer causes NullReferenceException.

SellItemAccept: validate PendingSellableSlot bounds and null check
the item before accessing it. Prevents crash when pending state is
stale or manipulated.

Also: add MSBuildGitHashValidateSuccess=false to csproj (needed for
Docker builds where .git is not available).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Species system: 50 species enum, ushort sprite index, ServerConfig mapping
- Castable FormulaVariables: CastableObject wrapper following ItemObject pattern
- Chair sitting: walk-into-wall detection, client sprite fold, turn in place
- Clan names: account-level identifier, new DisplayUser field
- Account manager: scoping notes for account entity and auth refactor
- NCalc integer division: regex fix for int/int truncation in all formulas

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Allow building against a local Hybrasyl.Xml project by adding a conditional ProjectReference (controlled by UseLocalXml and LocalXmlProjectPath) while keeping the NuGet PackageReference as the default. Add documentation (docs/epona-branch-instances.md) describing Epona's branch-aware server instances, worktree strategy, and the generated Directory.Build.props file used to switch to local XML branches. Update .gitignore to ignore .worktrees/ and Directory.Build.props so Epona-managed worktrees and build overrides remain out of version control.
Guard Console.ReadKey() in Game.cs with !Console.IsInputRedirected so
the "press any key to exit" pause only runs under a real console. When
the server is launched by a wrapper that pipes stdio (Epona embedded
mode, CI), World.Init() failure now exits cleanly with code 1 instead
of throwing InvalidOperationException. Unchanged for standalone
terminal runs. Enables Epona's embedded-console LogPane path.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@codecov

codecov Bot commented Apr 24, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 30.90%. Comparing base (6d582aa) to head (a225216).

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #326      +/-   ##
==========================================
+ Coverage   30.50%   30.90%   +0.40%     
==========================================
  Files         238      336      +98     
  Lines       25578    26215     +637     
  Branches     3513     3639     +126     
==========================================
+ Hits         7803     8103     +300     
- Misses      17137    17406     +269     
- Partials      638      706      +68     

☔ View full report in Codecov by Sentry.
📢 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.

@Caeldeth Caeldeth closed this Apr 24, 2026
@Caeldeth Caeldeth deleted the feature/epona-branch-instances branch April 26, 2026 23:44
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.

2 participants