Commit graph

5 commits

Author SHA1 Message Date
Erik
293584d6e8 feat(net): AcDream.Core.Net scaffold + ISAAC keystream (Phase 4.1)
First step of Phase 4 (networking). Adds a new AcDream.Core.Net project
for the AC UDP protocol implementation and a matching AcDream.Core.Net.Tests
project. Keeps networking isolated from rendering and the dat layer,
which also keeps the AGPL-reference-material hygiene cleaner.

AcDream.Core.Net/NOTICE.md documents the attribution policy: we read
ACE's AGPL network code (and holtburger's Rust ac-protocol crate) to
understand AC's wire format, but we reimplement everything in acdream's
own style. Wire-format facts aren't copyrightable; specific code is.

This commit adds one component: IsaacRandom — AC's variant of Bob
Jenkins' ISAAC PRNG, used to XOR a keystream into the CRC field of
every outbound packet for authentication. Clean-room reimplementation
based on reading:
  - references/ACE/Source/ACE.Common/Cryptography/ISAAC.cs (AGPL oracle)
  - Bob Jenkins' public ISAAC algorithm description

Implementation notes:
  - 256 uint32 mm[] state, 256 uint32 rsl[] output buffer, a/b/c regs
  - Initialize() runs 4 golden-ratio Mix() warmup rounds then two fold-in
    passes over rsl[] and mm[] (fresh instance → both start as zeroes)
  - AC variant: seed is exactly 4 bytes, interpreted as little-endian
    uint32 assigned to a = b = c before the first Scramble()
  - Scramble() produces 256 output words in one pass; Next() consumes
    them backwards from offset 255 → 0, re-scrambling at offset -1
  - Test seed 0x12345678 matches ACE's reference output byte-for-byte
    across the first 16 values (golden vectors transcribed from a
    throwaway oracle harness that compiled ACE's ISAAC.cs and printed
    its output; the harness was deleted after extracting the values)

Tests (5, all passing):
  - Next_Seed12345678_MatchesAceGoldenVectors: 16 golden uint32 values
  - Next_TwoInstancesSameSeed_ProduceIdenticalSequence: 1000 outputs
  - Next_DifferentSeeds_ProduceDifferentFirstOutput
  - Next_512Calls_SpansTwoScrambleBatches: >400 distinct values in 512
    outputs (catches all-zero / stuck-at-one bugs at scramble boundary)
  - Ctor_ShortSeed_Throws

Both test projects still green: 77 core + 5 net = 82/82.

Phase 4.2 (packet framing + checksum) next.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 14:14:28 +02:00
Erik
fb83e0bb6f feat(app): wire plugin host, ship smoke plugin, log lifecycle
Phase 1 MVP end-to-end. Program.cs initializes Serilog, builds an
AppPluginHost that hands plugins a SerilogAdapter (IPluginLogger),
discovers plugins from the App's output plugins/ dir, loads each via
PluginLoader, calls Enable on all of them before opening the GameWindow,
and calls Disable in a finally block on shutdown.

AcDream.Plugins.Smoke is a new first-party plugin that logs through
the host during Initialize / Enable / Disable. Its csproj references
the abstractions with Private=false + ExcludeAssets=runtime to avoid
shipping a second copy of AcDream.Plugin.Abstractions.dll (which would
break ALC type identity). An MSBuild Target on the App project copies
the plugin DLL into plugins/AcDream.Plugins.Smoke/ and writes the
plugin.json manifest next to it.

Smoke verified against real dats. Console output observed:
  [INF] scanning plugins in ...\plugins
  [INF] smoke plugin initialized
  [INF] loaded plugin acdream.smoke (Smoke Plugin)
  [INF] smoke plugin enabled
  loaded landblock 0xA9B4FFFF
  <window renders terrain>
  [INF] smoke plugin disabled  (on shutdown)

Phase 1 done.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 16:46:25 +02:00
Erik
a7f0732026 feat(core): add PluginLoader with collectible ALC 2026-04-10 09:51:16 +02:00
Erik
caf57cca3e chore: phase 1 — add Core, Abstractions, App, Tests projects 2026-04-10 09:22:33 +02:00
Erik
020ec2a35d chore: phase 0 — skeleton + dat asset inventory
Brand-new solution targeting .NET 10, using Chorizite.DatReaderWriter 2.1.4
to walk a retail AC dat directory and print how many of each asset type live
in client_portal / client_cell_1 / client_highres / client_local_English.

Opens the four dats in ~16 ms and counts 887,381 indexed assets across 40+
tracked DBObj types. Cell-database terrain (LandBlock, LandBlockInfo, EnvCell)
uses mask-based IDs that DatReaderWriter 2.1.4's GetAllIdsOfType<T> does not
support; worked around with a manual b-tree walk in CountCellByLow16.

Sanity check: LandBlock count is 65,025 = 255 x 255, exactly the AC world grid.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 09:02:56 +02:00