diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index da66d8d0c..f9f5fbe15 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -3,7 +3,7 @@ "isRoot": true, "tools": { "modernuoschemagenerator": { - "version": "2.14.3", + "version": "4.0.0", "commands": [ "ModernUOSchemaGenerator" ] diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index 92684562b..eb74c39a4 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -46,7 +46,7 @@ jobs: - name: Install Prerequisites run: | brew update - brew install icu4c libdeflate zstd argon2 + brew install icu4c libdeflate argon2 - name: Set Library Path run: echo "DYLD_LIBRARY_PATH=/opt/homebrew/lib:$DYLD_LIBRARY_PATH" >> $GITHUB_ENV - name: Build @@ -124,12 +124,36 @@ jobs: dnf config-manager --set-enabled crb dnf install -y epel-release if: ${{ matrix.epel }} + # Runtime packages only, deliberately. Installing the -dev packages here would add the + # unversioned .so symlink and mask the very thing the binding packages now probe for, so a + # regression in versioned-SONAME resolution would sail through CI. - name: Install Prerequisites using dnf - run: dnf makecache --refresh && dnf install -y findutils libicu libdeflate-devel zstd libargon2-devel + run: dnf makecache --refresh && dnf install -y findutils libicu libdeflate libargon2 tzdata if: ${{ matrix.packageManager == 'dnf' }} + # ICU's runtime package carries the ABI version in its name (libicu70 on jammy, libicu76 on + # trixie) and has no stable alias, so match it by pattern. libicu-dev was the old way to stay + # version-independent, but it drags in the unversioned symlink and defeats the check below. - name: Install Prerequisites using apt - run: apt-get update -y && apt-get install -y curl libicu-dev libdeflate-dev zstd libargon2-dev tzdata + run: apt-get update -y && apt-get install -y curl '^libicu[0-9]+$' libdeflate0 libargon2-1 tzdata if: ${{ matrix.packageManager == 'apt' }} + # Versioned-SONAME resolution is only under test while the unversioned symlink is absent. If a + # base image or a package ever starts shipping it, every probe would succeed on the first try + # and a regression in the fallback would sail through CI, so fail loudly instead of silently + # testing nothing. + - name: Assert the unversioned .so symlinks are absent + run: | + found="" + for lib in libicuuc libicui18n libdeflate libargon2; do + hit=$(ls /usr/lib/*/"$lib".so /usr/lib64/"$lib".so 2>/dev/null || true) + if [ -n "$hit" ]; then + found="$found $hit" + fi + done + if [ -n "$found" ]; then + echo "::error::Unversioned symlinks present, so CI is no longer exercising versioned SONAME resolution:$found" + exit 1 + fi + echo "No unversioned symlinks present; versioned SONAME resolution is under test." - uses: actions/checkout@v7 with: fetch-depth: 0 # avoid shallow clone so nbgv can do its work. diff --git a/.github/workflows/update-docs.yml b/.github/workflows/update-docs.yml deleted file mode 100644 index 005d262ca..000000000 --- a/.github/workflows/update-docs.yml +++ /dev/null @@ -1,36 +0,0 @@ -name: Deploy Docs - -on: - push: - branches: [website] - paths: - - 'website/**' - - '.github/workflows/update-docs.yml' - workflow_dispatch: - -jobs: - deploy-docs: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v6 - - - name: Build packets documentation - shell: pwsh - run: ./website/tools/build-packets.ps1 -OutputPath ./website/static/packets.html - - - name: Install dependencies - working-directory: website - run: npm ci - - - name: Build site - working-directory: website - run: npm run build - - - name: Deploy to GitHub Pages - uses: JamesIves/github-pages-deploy-action@v4 - with: - folder: ./website/build - branch: gh-pages - clean: true - clean-exclude: | - .nojekyll diff --git a/.gitignore b/.gitignore index 2cc93a052..d5b26e268 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,7 @@ /Distribution/Configuration/blocklist.json /Distribution/Configuration/crowdsec.json /Distribution/Configuration/expansion.json +/Distribution/Configuration/firewall.json /Distribution/Configuration/ip-allowlist*.txt /Distribution/Configuration/ip-allowlist*.txt.tmp /Distribution/Configuration/ip-blocklist.txt @@ -25,6 +26,7 @@ /Distribution/Configuration/email-settings.json /Distribution/Configuration/throttles.json /Distribution/Configuration/tot.json +/Distribution/Data/Pathfinding /Distribution/Logs /Distribution/Archives /Distribution/Backups diff --git a/CLAUDE.md b/CLAUDE.md index 7f24661a4..c77d39e19 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -18,8 +18,8 @@ Apply these when writing or reviewing `.cs` files under `Projects/`. 6. **Cancel timers in `OnDelete()`/`OnAfterDelete()`** — call `_token.Cancel()` or `_timer?.Stop()` 7. **`STArrayPool.Shared`** not `ArrayPool.Shared` — single-threaded optimized, no locks 8. **`PooledRefList`** not `new List()` on hot paths — zero GC pressure, stack-allocated ref struct -9. **Serialization** — class must be `partial`, constructor needs `[Constructible]`, `TimerExecutionToken` must NOT have `[SerializableField]`. New classes: use `[SerializationGenerator(version)]` (omit `encoded`). When bumping versions, add `MigrateFrom(VXContent)` (X = previous version). Never modify `Deserialize(reader, version)` for version bumps — that method is only for pre-codegen legacy saves. When migrating from pre-codegen Serialize/Deserialize: pass `false` if old code used `reader.ReadInt()`, bump version +1, and keep old logic as `private void Deserialize(IGenericReader reader, int version)` → `dev-docs/runuo-migration-docs/02-serialization.md` -10. **No `Task.Run`/`new Thread()` for game logic** (tandem with rule #3) — game logic is the single-threaded event loop. Backgrounding is allowed only for work that does not itself touch game state (external service calls, large-file parse). When such work must *feed* game logic: run the heavy/I/O part off-loop and `ConfigureAwait(false)` its awaits so a continuation never resumes on the loop and silently foregrounds heavy work; then hand the result back **explicitly** — publish an immutable snapshot swapped via a `volatile` reference (the loop reads it lock-free), or marshal the apply step with `Core.LoopContext.Post(() => …)`. Never touch game state off-thread; never let the scheduler decide where the heavy work runs → `dev-docs/threading-model.md` +9. **Serialization** — class must be `partial`, constructor needs `[Constructible]`, `TimerExecutionToken` must NOT have `[SerializableField]`. New classes: use `[SerializationGenerator(version)]` (omit `encoded`). Setters that coerce/veto/run side effects: use `[SerializableField]` args `allowFieldChange: nameof(BoolRefMethod)` / `fieldChanged: nameof(OldNewMethod)` — reserve `[SerializableProperty]` for custom getters. Serializable `Timer` members declare `[DeserializeTimer(nameof(Method))]` on the field (anchored by default — downtime preserves remaining delay; `wallClock: true` = absolute; method runs only when a timer was running at save). Conditional writes: `[SaveFlag(nameof(Should), nameof(Default))]` on the field. When bumping versions, add `MigrateFrom(VXContent)` (X = previous version). Never modify `Deserialize(reader, version)` for version bumps — that method is only for pre-codegen legacy saves. When migrating from pre-codegen Serialize/Deserialize: pass `false` if old code used `reader.ReadInt()`, bump version +1, and keep old logic as `private void Deserialize(IGenericReader reader, int version)` → `dev-docs/serialization.md`, `dev-docs/runuo-migration-docs/02-serialization.md` +10. **No `Task.Run`/`new Thread()` for game logic** (tandem with rule #3) — game logic is the single-threaded event loop. Backgrounding is allowed only for work that does not itself touch game state (external service calls, large-file parse). **Prove the need before adding a thread**: measure **on-loop** time, not wall-clock (frozen world is the cost, player latency is not), and gate on `Environment.ProcessorCount` — off-loading creates no CPU and buys nothing on 1–2 cores. New workers go in the vetted table in `dev-docs/threading-model.md` with their measurement. When such work must *feed* game logic: run the heavy/I/O part off-loop and `ConfigureAwait(false)` its awaits so a continuation never resumes on the loop and silently foregrounds heavy work; then hand the result back **explicitly** — publish an immutable snapshot swapped via a `volatile` reference (the loop reads it lock-free), or marshal the apply step with `Core.LoopContext.Post(() => …)`, re-validating in the continuation whatever may have changed while it ran. Never touch game state off-thread; never let the scheduler decide where the heavy work runs → `dev-docs/threading-model.md` 11. **Never assume era** — if code uses `Core.AOS`/`Core.SE`/etc., ask which expansion to target 12. **Naming** — `_camelCase` private fields, `PascalCase` properties/methods/classes; don't flag legacy `m_` but use `_` for new code 13. **No empty gumps** — every gump must produce visual elements. An empty gump leaks on client+server (no way to close it). Use static `DisplayTo()` to validate before constructing → `dev-docs/gump-system.md` @@ -29,6 +29,7 @@ Apply these when writing or reviewing `.cs` files under `Projects/`. 17. **No `System.Text.StringBuilder`** — use `ValueStringBuilder` with `stackalloc` (bounded output) or `ValueStringBuilder.Create()` (unbounded). Supports `$"..."` interpolation directly. Always use `using var` for disposal. Use `Reset()` instead of reassigning → `dev-docs/string-handling.md` 18. **Interpolation anti-patterns on handler-aware APIs** — `Send*`/`Say`/`Emote`/`PublicOverhead*`/`IPropertyList.Add`/gump `AddLabel`/`AddHtml`/`Html.Center`/`SpanWriter.Write*` all have `ref RawInterpolatedStringHandler` overloads that allocate zero strings, but only when the call-site argument is a `$"..."` literal directly. Avoid: ternaries with interpolated branches (`Send(c ? $"a" : $"b")`), switch expressions with interpolated arms, pre-built `var s = $"..."` locals (single-use), `.ToString()` / `.String()` / `string.Format` inside holes, string concat (`{a + b}`), LINQ string ops in holes. Use `:L` format spec for lowercase (`{rank:L}` not `rank.ToString().ToLowerInvariant()`) → `dev-docs/string-handling.md` § Interpolation Anti-Patterns 19. **No `InvalidateProperties()` from inside `GetProperties`** — every property a `GetProperties` override reads must be a pure read. `InvalidateProperties()` rebuilds the list in place (`Reset()` + rebuild), and `Reset()` returns the pooled interpolation buffer — which the compiler rents for the whole `$"..."` expression, so every hole is evaluated while it is live — and rewinds the packet cursor. A getter that invalidates therefore throws `ArgumentNullException` (parameter `"array"`) out of `GetProperties` from an unrelated-looking line, or silently corrupts the tooltip. The engine refuses and logs an error; `DEBUG` throws. Lazy recomputation in a getter is fine — the *notification* is not. Invalidate in the setter that changes the value, or defer with `Timer.DelayCall(InvalidateProperties)` → `dev-docs/property-lists.md` § Never Invalidate From Inside `GetProperties` +20. **Tick-count math must be wraparound-safe** — compare `Core.TickCount`/`GetTimestamp()` values only by subtraction (`a - b < 0`, never `a < b`), no zero/sign sentinels on tick fields, seed deadline fields from a real tick (never rely on the 0 default). Cloud hypervisors (GCP) pass through the host's never-resetting counter: ticks start enormous and can wrap negative. Linux affected in production; Windows not so far → `dev-docs/tick-counts.md` ## Dev-Docs Reference @@ -45,7 +46,11 @@ Apply these when writing or reviewing `.cs` files under `Projects/`. | Commands & targeting | `dev-docs/commands-targeting.md` | | Event system | `dev-docs/events.md` | | Threading model | `dev-docs/threading-model.md` | +| Server hardware requirements | `dev-docs/server-requirements.md` | +| Debugging event-loop performance (profiling build, decomposition, GC/RAM) | `dev-docs/debugging-event-loop.md` | +| Tick-count overflow rules (subtraction comparisons; GCP pass-through counters) | `dev-docs/tick-counts.md` | | Server lifecycle & bootstrap phases (Configure/ConfigurePrompts/Initialize) | `dev-docs/server-lifecycle.md` | +| Platform prerequisites (ICU, tzdata, native libs per distro) | `dev-docs/platform-prerequisites.md` | | Configuration system | `dev-docs/configuration.md` | | Networking & packets | `dev-docs/networking-packets.md` | | IP bans, blocklists & allowlists (incl. unblocking a player) | `dev-docs/ip-bans-and-allowlists.md` | @@ -94,7 +99,18 @@ Then copy only the relevant skill files based on the task: | Migrate persistence (WorldSave) | `migrate-from-runuo/migrate-persistence` | | Migrate multi-file system | `migrate-from-runuo/migrate-systems` | -To enable a skill: `cp dev-docs/claude-skills/.md .claude/skills/` +To enable a skill — Claude Code loads `.claude/skills//SKILL.md`; a bare `.md` dropped +directly into `.claude/skills/` is **not** picked up, and newly installed skills appear in the +*next* session: + +```sh +# Standard skills (modernuo-*) +mkdir -p .claude/skills/ && cp dev-docs/claude-skills/.md .claude/skills//SKILL.md + +# Migration skills — sources live in the migrate-from-runuo/ subfolder, but install under the +# bare skill name (the table's "migrate-from-runuo/" is the source path, not the name): +mkdir -p .claude/skills/ && cp dev-docs/claude-skills/migrate-from-runuo/.md .claude/skills//SKILL.md +``` Migration skills reference the deep docs in `dev-docs/runuo-migration-docs/` and point to existing ModernUO skills for best practices. diff --git a/Directory.Build.props b/Directory.Build.props index 59311d439..c832f1006 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -63,8 +63,15 @@ ..\..\Rules.ruleset latest + + + $(DefineConstants);EVENT_LOOP_PROFILING + - + diff --git a/Distribution/Data/npc-speeds.json b/Distribution/Data/npc-speeds.json index 6e07859d5..707f61e4a 100644 --- a/Distribution/Data/npc-speeds.json +++ b/Distribution/Data/npc-speeds.json @@ -3,12 +3,16 @@ "level": "VerySlow", "active": 0.4, "passive": 0.8, + "activeMove": 0.9, + "passiveMove": 1.5, "types": [] }, { "level": "Slow", "active": 0.3, "passive": 0.6, + "activeMove": 0.6, + "passiveMove": 1.2, "types": [ "AntLion", "ArcticOgreLord", "BogThing", "Bogle", "BoneKnight", "EarthElemental", @@ -28,6 +32,8 @@ "level": "Medium", "active": 0.25, "passive": 0.5, + "activeMove": 0.45, + "passiveMove": 1.05, "types": [ "AcidElemental", "AgapiteElemental", "Alligator", "AncientLich", "Betrayer", "Bird", @@ -108,6 +114,8 @@ "level": "Fast", "active": 0.2, "passive": 0.4, + "activeMove": 0.3, + "passiveMove": 0.9, "types": [ "LordOaks", "Silvani", "AirElemental", "AncientWyrm", "Balron", "BladeSpirits", @@ -139,6 +147,8 @@ "level": "VeryFast", "active": 0.125, "passive": 0.30, + "activeMove": 0.125, + "passiveMove": 0.6, "types": [ "Barracoon", "Mephitis", "Neira", "Rikktor", "Semidar", "EnergyVortex", diff --git a/Projects/BuildTool/BuildOptions.cs b/Projects/BuildTool/BuildOptions.cs index 9aea5010d..9e1d56388 100644 --- a/Projects/BuildTool/BuildOptions.cs +++ b/Projects/BuildTool/BuildOptions.cs @@ -14,4 +14,11 @@ public sealed class BuildOptions public string? Arch { get; set; } public bool SkipPrereqs { get; set; } public bool Interactive { get; set; } + + /// + /// Report the native library prerequisites and exit. The interactive flow is the only other + /// path that runs these checks, so without this there is no way to verify a deployment target + /// from a script or a container. + /// + public bool CheckPrereqsOnly { get; set; } } diff --git a/Projects/BuildTool/BuildTool.csproj b/Projects/BuildTool/BuildTool.csproj index b3f37cf7a..983000240 100644 --- a/Projects/BuildTool/BuildTool.csproj +++ b/Projects/BuildTool/BuildTool.csproj @@ -17,6 +17,5 @@ - diff --git a/Projects/BuildTool/Prerequisites/NativeLibraryChecker.cs b/Projects/BuildTool/Prerequisites/NativeLibraryChecker.cs index 4f84aa373..95c521c24 100644 --- a/Projects/BuildTool/Prerequisites/NativeLibraryChecker.cs +++ b/Projects/BuildTool/Prerequisites/NativeLibraryChecker.cs @@ -1,3 +1,4 @@ +using System.Runtime.InteropServices; using BuildTool.Platform; using BuildTool.Publishing; @@ -31,8 +32,10 @@ public static class NativeLibraryChecker "Linux", [ ".NET 10 Runtime — https://dotnet.microsoft.com/download/dotnet/10.0", - "Debian/Ubuntu: sudo apt-get install -y libicu-dev libdeflate-dev zstd libargon2-dev", - "Fedora/RHEL: sudo dnf install -y libicu libdeflate-devel zstd libargon2-devel", + "Debian/Ubuntu: sudo apt-get install -y libdeflate0 libargon2-1 libicuNN tzdata", + " (libicuNN varies by release, e.g. libicu76 — run build-tool --check-prereqs there for the exact name)", + " (add tzdata-legacy if the shard is configured with an alias such as US/Eastern)", + "Fedora/RHEL: sudo dnf install -y libdeflate libargon2 libicu tzdata", "CentOS: Also requires epel-release and CRB enabled" ] ), @@ -176,82 +179,59 @@ public static class NativeLibraryChecker return results; } + /// + /// Native libraries the server needs from the system on Linux, and the SONAME range to accept + /// for each. Rationale and per-distro package names: dev-docs/platform-prerequisites.md. + /// + private static readonly (string Name, int MinSoVersion, int MaxSoVersion)[] _linuxLibraries = + [ + ("libicuuc", 60, 120), + ("libicui18n", 60, 120), + ("libdeflate", 0, 9), + ("libargon2", 0, 9) + ]; + private static List CheckLinux(PlatformInfo platform) - { - return platform.PackageManager switch - { - PackageManager.Apt => CheckLinuxApt(), - PackageManager.Dnf => CheckLinuxDnf(platform), - _ => CheckLinuxGeneric(platform) - }; - } - - private static List CheckLinuxApt() { var results = new List(); - var packages = new[] { "libicu-dev", "libdeflate-dev", "zstd", "libargon2-dev" }; var missing = new List(); - foreach (var package in packages) + foreach (var (name, minSoVersion, maxSoVersion) in _linuxLibraries) { - var result = ProcessRunner.RunCaptured("dpkg", $"-l {package}"); - var installed = result.Success && result.StandardOutput.Contains("ii"); + var found = CanLoad(name, minSoVersion, maxSoVersion); - if (!installed) + if (!found) { - missing.Add(package); + missing.Add(name); } results.Add(new PrerequisiteResult { - Name = package, - Passed = installed, - Details = installed ? "Installed" : "Not installed" + Name = name, + Passed = found, + Details = found ? "Found" : "Not found" }); } - if (missing.Count > 0) + var hasTimeZoneData = HasTimeZoneData(); + if (!hasTimeZoneData) { - results.Add(new PrerequisiteResult - { - Name = "Install all missing", - Passed = false, - IsWarning = true, - Details = "Run the following command to install all missing dependencies:", - InstallCommand = $"sudo apt-get install -y {string.Join(' ', missing)}" - }); + missing.Add("tzdata"); } - return results; - } - - private static List CheckLinuxDnf(PlatformInfo platform) - { - var results = new List(); - var packages = new[] { "libicu", "libdeflate-devel", "zstd", "libargon2-devel" }; - var missing = new List(); - - foreach (var package in packages) + results.Add(new PrerequisiteResult { - var result = ProcessRunner.RunCaptured("rpm", $"-q {package}"); - var installed = result.Success; + Name = "tzdata", + Passed = hasTimeZoneData, + Details = hasTimeZoneData ? "Found" : "Not found — every zone except UTC will throw" + }); - if (!installed) - { - missing.Add(package); - } - - results.Add(new PrerequisiteResult - { - Name = package, - Passed = installed, - Details = installed ? "Installed" : "Not installed" - }); + if (missing.Count == 0) + { + return results; } - // Check if this is CentOS (needs EPEL) - var isCentOs = platform.DistroId?.Equals("centos", StringComparison.OrdinalIgnoreCase) == true; - if (isCentOs && missing.Count > 0) + if (platform.DistroId?.Equals("centos", StringComparison.OrdinalIgnoreCase) == true) { results.Add(new PrerequisiteResult { @@ -263,48 +243,131 @@ public static class NativeLibraryChecker }); } - if (missing.Count > 0) + results.Add(new PrerequisiteResult { - results.Add(new PrerequisiteResult - { - Name = "Install all missing", - Passed = false, - IsWarning = true, - Details = "Run the following command to install all missing dependencies:", - InstallCommand = $"sudo dnf install -y {string.Join(' ', missing)}" - }); - } + Name = "Install all missing", + Passed = false, + IsWarning = true, + Details = "Install the missing dependencies. The -dev/-devel packages are not required:", + InstallCommand = BuildInstallCommand(platform, missing) + }); return results; } - private static List CheckLinuxGeneric(PlatformInfo platform) + /// + /// tzdata is data, not a library, so no loader probe finds it. Asking the runtime rather than + /// stat'ing a path keeps TZDIR honoured, and the count is still accurate under + /// InvariantGlobalization, which this tool runs with — only display names degrade there. + /// + private static bool HasTimeZoneData() { - var results = new List(); - - // Use ldconfig to check for shared libraries - var ldResult = ProcessRunner.RunCaptured("ldconfig", "-p"); - var ldOutput = ldResult.Success ? ldResult.StandardOutput : ""; - - var libraries = new Dictionary + try { - ["libicu"] = "libicuuc", - ["libdeflate"] = "libdeflate", - ["zstd"] = "libzstd", - ["libargon2"] = "libargon2" - }; - - foreach (var (name, soName) in libraries) + return TimeZoneInfo.GetSystemTimeZones().Count > 1; + } + catch { - var found = ldOutput.Contains(soName, StringComparison.OrdinalIgnoreCase); - results.Add(new PrerequisiteResult - { - Name = name, - Passed = found, - Details = found ? "Found" : "Not found — install using your package manager" - }); + return false; + } + } + + /// + /// Asks the loader directly rather than querying a package database or scanning ldconfig's + /// cache, both of which answer a different question and can disagree with what dlopen will do. + /// Mirrors the binding packages' own probing: the unversioned name first, then libfoo.so.N + /// descending. Bare names go through the full loader search path, so LD_LIBRARY_PATH and + /// /etc/ld.so.conf.d still apply. + /// + private static bool CanLoad(string library, int minSoVersion, int maxSoVersion) + { + if (TryLoadAndFree($"{library}.so")) + { + return true; } - return results; + for (var soVersion = maxSoVersion; soVersion >= minSoVersion; soVersion--) + { + if (TryLoadAndFree($"{library}.so.{soVersion}")) + { + return true; + } + } + + return false; + } + + private static bool TryLoadAndFree(string candidate) + { + if (!NativeLibrary.TryLoad(candidate, out var handle)) + { + return false; + } + + NativeLibrary.Free(handle); + return true; + } + + private static string BuildInstallCommand(PlatformInfo platform, List missing) + { + switch (platform.PackageManager) + { + case PackageManager.Apt: + { + // Distinct because the two ICU libraries resolve to the same package, and + // ResolveAptIcuPackage shells out, so it is memoized rather than called per name. + var packages = missing.Select( + library => library switch + { + "libdeflate" => "libdeflate0", + "libargon2" => "libargon2-1", + "tzdata" => "tzdata", + _ => _aptIcuPackage ??= ResolveAptIcuPackage() + } + ).Distinct(); + + return $"sudo apt-get install -y {string.Join(' ', packages)}"; + } + case PackageManager.Dnf: + { + var packages = missing.Select( + library => library switch + { + "libdeflate" => "libdeflate", + "libargon2" => "libargon2", + "tzdata" => "tzdata", + _ => "libicu" + } + ).Distinct(); + + return $"sudo dnf install -y {string.Join(' ', packages)}"; + } + default: + return $"Install your distribution's runtime packages for: {string.Join(", ", missing)}"; + } + } + + private static string _aptIcuPackage; + + /// + /// ICU's apt package carries the ABI version in its name and there is no stable alias, so ask + /// apt which one this release actually ships instead of printing a name that rots. + /// + private static string ResolveAptIcuPackage() + { + var result = ProcessRunner.RunCaptured("apt-cache", "search --names-only ^libicu[0-9]+$"); + if (!result.Success) + { + return "libicu"; + } + + var best = result.StandardOutput + .Split('\n', StringSplitOptions.RemoveEmptyEntries) + .Select(line => line.Split(' ', 2)[0].Trim()) + .Where(name => name.StartsWith("libicu", StringComparison.Ordinal)) + .OrderBy(name => int.TryParse(name.AsSpan(6), out var version) ? version : 0) + .LastOrDefault(); + + return best ?? "libicu"; } } diff --git a/Projects/BuildTool/Program.cs b/Projects/BuildTool/Program.cs index 3b36066da..915839bbf 100644 --- a/Projects/BuildTool/Program.cs +++ b/Projects/BuildTool/Program.cs @@ -4,6 +4,7 @@ using BuildTool.Interactive; using BuildTool.Platform; using BuildTool.Prerequisites; using BuildTool.Publishing; +using Spectre.Console; Console.OutputEncoding = Encoding.UTF8; @@ -36,6 +37,22 @@ options.Os ??= detectedPlatform.OsRid; options.Arch ??= detectedPlatform.ArchRid; var rid = $"{options.Os}-{options.Arch}"; +if (options.CheckPrereqsOnly) +{ + // Same renderer the guided menu uses, so the two cannot drift. Spectre drops ANSI styling by + // itself when stdout is not a terminal, which is the case this flag exists for, but it also + // falls back to an 80 column width and folds anything longer. The install hints we print are + // shell commands — the CentOS one is 95 characters — and a fold puts a newline in the middle of + // a command that someone is meant to copy. Widen the profile so they stay on one line. + if (Console.IsOutputRedirected) + { + AnsiConsole.Profile.Width = 200; + } + + // Exit code is the machine-readable half: 0 when everything resolves, 1 when anything is missing. + return PrerequisiteChecker.CheckNativeLibraries(detectedPlatform, interactive: false) ? 0 : 1; +} + // Run prerequisite checks unless skipped if (!options.SkipPrereqs) { @@ -108,6 +125,12 @@ static BuildOptions ParseArguments(string[] args) hasNamedArgs = true; break; } + case "--check-prereqs": + { + options.CheckPrereqsOnly = true; + hasNamedArgs = true; + break; + } case "--interactive": { options.Interactive = true; diff --git a/Projects/Server.Tests/Server.Tests.csproj b/Projects/Server.Tests/Server.Tests.csproj index 1be789258..4bae245cb 100644 --- a/Projects/Server.Tests/Server.Tests.csproj +++ b/Projects/Server.Tests/Server.Tests.csproj @@ -5,17 +5,16 @@ Server.Tests - + - + all runtime; build; native; contentfiles; analyzers; buildtransitive - diff --git a/Projects/Server.Tests/Tests/Items/DecayRegistrationTests.cs b/Projects/Server.Tests/Tests/Items/DecayRegistrationTests.cs index 0e48977ef..59d736c6f 100644 --- a/Projects/Server.Tests/Tests/Items/DecayRegistrationTests.cs +++ b/Projects/Server.Tests/Tests/Items/DecayRegistrationTests.cs @@ -208,6 +208,272 @@ public class DecayRegistrationTests item.Delete(); } + // Unfreezing an item with a stale LastMoved must grant a fresh decay window, + // not delete it on the next tick. + [Fact] + public void StaleImmovableItemMadeMovable_GetsAFreshDecayWindow() + { + var start = Core._now; + + try + { + var item = new Item(0x1234); + item.MoveToWorld(new Point3D(107, 100, 0), Map.Felucca); + + item.Movable = false; + Assert.False(DecayScheduler.IsRegistered(item), "A frozen item must not be tracked for decay."); + + Core._now = start + TimeSpan.FromDays(30); + var flipped = Core._now; + + item.Movable = true; + + Assert.True(DecayScheduler.IsRegistered(item), "An unfrozen item must be tracked for decay."); + + AdvanceDecay(flipped, item.DecayTime - TimeSpan.FromMinutes(2), item); + Assert.False(item.Deleted, "An unfrozen item must get a full decay window, not vanish immediately."); + + AdvanceDecay(Core._now, TimeSpan.FromMinutes(4), item); + Assert.True(item.Deleted, "An unfrozen item must still decay once the fresh window elapses."); + } + finally + { + Core._now = start; + } + } + + // Same transition through the Visible setter: unhiding a long-hidden item. + [Fact] + public void StaleHiddenItemMadeVisible_GetsAFreshDecayWindow() + { + var start = Core._now; + + try + { + var item = new Item(0x1234); + item.MoveToWorld(new Point3D(109, 100, 0), Map.Felucca); + + item.Visible = false; + Assert.False(DecayScheduler.IsRegistered(item), "A hidden item must not be tracked for decay."); + + Core._now = start + TimeSpan.FromDays(30); + var flipped = Core._now; + + item.Visible = true; + + Assert.True(DecayScheduler.IsRegistered(item), "An unhidden item must be tracked for decay."); + + AdvanceDecay(flipped, item.DecayTime - TimeSpan.FromMinutes(2), item); + Assert.False(item.Deleted, "An unhidden item must get a full decay window, not vanish immediately."); + + AdvanceDecay(Core._now, TimeSpan.FromMinutes(4), item); + Assert.True(item.Deleted, "An unhidden item must still decay once the fresh window elapses."); + } + finally + { + Core._now = start; + } + } + + // A refusal restarts the countdown without rewriting LastMoved. + [Fact] + public void RefusedDecay_DoesNotRewriteLastMoved() + { + var start = Core._now; + + try + { + var item = new RefusesDecayItem(); + item.MoveToWorld(new Point3D(110, 100, 0), Map.Felucca); + var lastMoved = item.LastMoved; + + AdvanceDecay(start, item.DecayTime + TimeSpan.FromMinutes(2), item); + + Assert.False(item.Deleted, "A refused decay must not delete the item."); + Assert.True(DecayScheduler.IsRegistered(item), "A refused decay must leave the item tracked."); + Assert.Equal(lastMoved, item.LastMoved); + + item.Delete(); + } + finally + { + Core._now = start; + } + } + + // The fresh window must survive a save/load cycle, or a restart mid-window deletes the item. + [Fact] + public void FreshDecayWindow_SurvivesSerialization() + { + var start = Core._now; + + try + { + var item = new Item(0x1234); + item.MoveToWorld(new Point3D(111, 100, 0), Map.Felucca); + + item.Movable = false; + Core._now = start + TimeSpan.FromDays(30); + item.Movable = true; + + var expected = item.ScheduledDecayTime; + + var writer = new BufferWriter(new byte[512], true); + item.Serialize(writer); + + var copy = new Item(item.Serial); + copy.Deserialize(new BufferReader(writer.Buffer)); + + // The stamp is stored as a delta, so it ages only by the real time between + // write and read - milliseconds here, the downtime in production. + Assert.True( + (copy.ScheduledDecayTime - expected).Duration() <= TimeSpan.FromSeconds(5), + "The restarted decay window must survive a save/load cycle." + ); + + item.Delete(); + copy.Delete(); + } + finally + { + Core._now = start; + } + } + + // A real move supersedes the reset stamp; it must be dropped so the CompactInfo can collapse. + [Fact] + public void MovingAnItem_ClearsASupersededDecayResetStamp() + { + var start = Core._now; + + try + { + var item = new Item(0x1234); + item.MoveToWorld(new Point3D(112, 100, 0), Map.Felucca); + + item.Movable = false; + Core._now = start + TimeSpan.FromDays(30); + item.Movable = true; + + Assert.NotEqual(default, item.DecayResetTime); + + Core._now += TimeSpan.FromMinutes(1); + item.MoveToWorld(new Point3D(113, 100, 0), Map.Felucca); + + Assert.Equal(default, item.DecayResetTime); + Assert.Equal(item.LastMoved + item.DecayTime, item.ScheduledDecayTime); + Assert.True(DecayScheduler.IsRegistered(item)); + + item.Delete(); + } + finally + { + Core._now = start; + } + } + + // Losing decay eligibility makes the stamp meaningless; it must be dropped so the + // CompactInfo is not held for as long as the item stays ineligible. + [Fact] + public void ItemBecomingIneligible_DropsTheDecayResetStamp() + { + var start = Core._now; + + try + { + var item = new Item(0x1234); + item.MoveToWorld(new Point3D(115, 100, 0), Map.Felucca); + + item.Movable = false; + Core._now = start + TimeSpan.FromDays(30); + item.Movable = true; + + Assert.NotEqual(default, item.DecayResetTime); + + item.Movable = false; + + Assert.Equal(default, item.DecayResetTime); + + item.Delete(); + } + finally + { + Core._now = start; + } + } + + // Moving a stamped item into a container programmatically (no drop, no SetLastMoved) + // must also drop the stamp. + [Fact] + public void StampedItemAddedToContainer_DropsTheDecayResetStamp() + { + var start = Core._now; + + try + { + var pack = new Container(0xE75); + pack.MoveToWorld(new Point3D(116, 100, 0), Map.Felucca); + + var item = new Item(0x1234); + item.MoveToWorld(new Point3D(117, 100, 0), Map.Felucca); + + item.Movable = false; + Core._now = start + TimeSpan.FromDays(30); + item.Movable = true; + + Assert.NotEqual(default, item.DecayResetTime); + + pack.AddItem(item); + + Assert.Equal(default, item.DecayResetTime); + + pack.Delete(); + } + finally + { + Core._now = start; + } + } + + // A raw Map assignment (e.g. a GM changing Map through props) is a move: it must + // enroll an untracked item for decay. + [Fact] + public void ItemMovedToRealMapViaMapSetter_IsRegisteredForDecay() + { + var item = new Item(0x1234); + Assert.False(DecayScheduler.IsRegistered(item)); + + item.Map = Map.Felucca; + + Assert.True(item.CanDecay()); + Assert.True(DecayScheduler.IsRegistered(item), "Item placed on a map via the Map setter must be tracked."); + + item.Delete(); + } + + // LiftItemDupe places the remainder of a partially lifted ground stack via raw + // Location/Map assignments, with no MoveToWorld fallback: it must still be tracked. + [Fact] + public void PartialLiftOfGroundStack_LeavesRemainderRegisteredForDecay() + { + var stack = new Item(0x1234) { Stackable = true, Amount = 10 }; + stack.MoveToWorld(new Point3D(114, 100, 0), Map.Felucca); + + var remainder = Mobile.LiftItemDupe(stack, 3); + + Assert.NotNull(remainder); + Assert.Equal(7, remainder.Amount); + Assert.Null(remainder.Parent); + Assert.Equal(Map.Felucca, remainder.Map); + Assert.True( + DecayScheduler.IsRegistered(remainder), + "The remainder of a partially lifted ground stack must be tracked for decay." + ); + + stack.Delete(); + remainder.Delete(); + } + // Dropping into a container must untrack; taking it back out to the ground must re-track. [Fact] public void ItemMovedIntoContainerThenBackToGround_IsRegisteredForDecay() diff --git a/Projects/Server.Tests/Tests/Items/PlayerConstructedStackingTests.cs b/Projects/Server.Tests/Tests/Items/PlayerConstructedStackingTests.cs new file mode 100644 index 000000000..955a87293 --- /dev/null +++ b/Projects/Server.Tests/Tests/Items/PlayerConstructedStackingTests.cs @@ -0,0 +1,95 @@ +using Xunit; + +namespace Server.Tests; + +[Collection("Sequential Server Tests")] +public class PlayerConstructedStackingTests +{ + // PlayerConstructed is per-instance provenance, and stack operations were written when no + // item carried any. Merging keeps the receiver's copy of a field and splitting rebuilds one + // half from a fixed list of fields, so a flag that is not accounted for in both places is + // one that ordinary stacking can launder or erase. + + // Stands in for a real stackable type. LiftItemDupe builds the remainder through the + // parameterless constructor and copies only a fixed list of fields onto it -- Stackable is + // not on that list -- so the remainder is only stackable if the type restores it the way + // every genuine stackable does. + private class StackableItem : Item + { + public StackableItem() => Stackable = true; + + public StackableItem(Serial serial) : base(serial) => Stackable = true; + } + + private static StackableItem MakeStack(Serial serial, int amount, bool playerConstructed) => + new(serial) { Amount = amount, PlayerConstructed = playerConstructed }; + + [Theory] + [InlineData(false)] + [InlineData(true)] + public void CanStackWith_IsTrueWhenProvenanceMatches(bool playerConstructed) + { + var first = MakeStack((Serial)0x1, 5, playerConstructed); + var second = MakeStack((Serial)0x2, 7, playerConstructed); + + try + { + Assert.True(first.CanStackWith(second)); + } + finally + { + first.Delete(); + second.Delete(); + } + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public void LiftItemDupe_CopiesPlayerConstructedToRemainder(bool playerConstructed) + { + var stack = MakeStack((Serial)0x1, 10, playerConstructed); + Item remainder = null; + + try + { + remainder = Mobile.LiftItemDupe(stack, 4); + + Assert.NotNull(remainder); + Assert.NotSame(stack, remainder); + Assert.Equal(4, stack.Amount); + Assert.Equal(6, remainder.Amount); + Assert.Equal(playerConstructed, remainder.PlayerConstructed); + } + finally + { + stack.Delete(); + remainder?.Delete(); + } + } + + [Fact] + public void SplitHalvesRemainStackableWithEachOther() + { + // The two halves of a split must still be one pile's worth: if the split dropped the + // flag, the remainder would no longer stack back onto what it came from. + var stack = MakeStack((Serial)0x1, 10, true); + Item remainder = null; + + try + { + remainder = Mobile.LiftItemDupe(stack, 4); + Assert.NotNull(remainder); + + Assert.True(stack.CanStackWith(remainder)); + Assert.True(stack.StackWith(null, remainder, false)); + Assert.Equal(10, stack.Amount); + Assert.True(stack.PlayerConstructed); + } + finally + { + stack.Delete(); + remainder?.Delete(); + } + } +} diff --git a/Projects/Server.Tests/Tests/Network/EventLoopIdleTests.cs b/Projects/Server.Tests/Tests/Network/EventLoopIdleTests.cs new file mode 100644 index 000000000..dfb8c7be7 --- /dev/null +++ b/Projects/Server.Tests/Tests/Network/EventLoopIdleTests.cs @@ -0,0 +1,69 @@ +using Xunit; + +namespace Server.Tests; + +/// +/// The event loop only sleeps when every queue it drains is empty. These drains are deliberately +/// bounded -- ExecuteTasks stops at its per-frame cap -- so leftover work is normal and must keep +/// the loop awake. Getting this wrong strands queued work for the length of a sleep. +/// +[Collection("Sequential Server Tests")] +public class EventLoopIdleTests +{ + [Fact] + public void FreshContextIsEmpty() + { + var context = new EventLoopContext(); + + Assert.True(context.IsEmpty); + } + + [Fact] + public void PostedWorkMakesContextNonEmpty() + { + var context = new EventLoopContext(); + + context.Post(() => { }); + + Assert.False(context.IsEmpty); + } + + [Fact] + public void PriorityWorkMakesContextNonEmpty() + { + var context = new EventLoopContext(); + + context.Post(() => { }, EventLoopContext.Priority.High); + + Assert.False(context.IsEmpty); + } + + [Fact] + public void ContextIsEmptyAgainOnceDrained() + { + var context = new EventLoopContext(); + context.Post(() => { }); + + context.ExecuteTasks(); + + Assert.True(context.IsEmpty); + } + + [Fact] + public void WorkBeyondThePerFrameCapKeepsContextNonEmpty() + { + // The cap is what makes IsEmpty necessary: a single ExecuteTasks pass cannot be assumed + // to have drained everything, so the loop must not treat "I just ran tasks" as "idle". + const int perFrameCap = 128; + var context = new EventLoopContext(perFrameCap); + + for (var i = 0; i < perFrameCap + 10; i++) + { + context.Post(() => { }); + } + + context.ExecuteTasks(); + + Assert.False(context.IsEmpty); + } +} diff --git a/Projects/Server.Tests/Tests/Serialization/AnchoredItemSerializationTests.cs b/Projects/Server.Tests/Tests/Serialization/AnchoredItemSerializationTests.cs new file mode 100644 index 000000000..7889c57c8 --- /dev/null +++ b/Projects/Server.Tests/Tests/Serialization/AnchoredItemSerializationTests.cs @@ -0,0 +1,76 @@ +using System; +using Xunit; + +namespace Server.Tests; + +[Collection("Sequential Server Tests")] +public class AnchoredItemSerializationTests +{ + private static byte[] SerializeItem(Item item) + { + var writer = new BufferWriter(new byte[256], true); + item.Serialize(writer); + return writer.Buffer[..(int)writer.Position]; + } + + /// + /// Item v11 stores LastMoved and DecayResetTime as anchored time: the serialized bytes + /// are a function of item state only, not of when the save runs. Pre-v11 stored + /// minutes-since-moved and delta time, which rewrote the bytes on every save. + /// + [Fact] + public void ItemBytes_AreStable_AcrossSavesAtDifferentTimes() + { + var start = Core._now; + + try + { + var item = new Item(0x1F13); + item.MoveToWorld(new Point3D(120, 100, 0), Map.Felucca); + item.RestartDecay(); + + var first = SerializeItem(item); + + // A save hours later, with no state change, must produce identical bytes. + Core._now = start + TimeSpan.FromHours(5); + var second = SerializeItem(item); + + Assert.Equal(first, second); + + item.Delete(); + } + finally + { + Core._now = start; + } + } + + /// + /// Pre-v11 LastMoved was stored at whole-minute precision relative to the save time and + /// could never round-trip exactly. Anchored storage is absolute and exact. + /// + [Fact] + public void LastMovedAndDecayReset_RoundTripExactly() + { + var item = new Item(0x1F13); + item.MoveToWorld(new Point3D(121, 100, 0), Map.Felucca); + + // Sub-minute precision that the old minutes encoding would have destroyed. + var moved = Core.Now - TimeSpan.FromSeconds(90.5) - TimeSpan.FromMilliseconds(123); + item.LastMoved = moved; + + item.RestartDecay(); + var decayReset = item.DecayResetTime; + Assert.NotEqual(default(DateTime), decayReset); + + var bytes = SerializeItem(item); + + var restored = new Item((Serial)0x7ffff123u); + restored.Deserialize(new BufferReader(bytes)); + + Assert.Equal(moved, restored.LastMoved); + Assert.Equal(decayReset, restored.DecayResetTime); + + item.Delete(); + } +} diff --git a/Projects/Server.Tests/Tests/Serialization/AnchoredTimeTests.cs b/Projects/Server.Tests/Tests/Serialization/AnchoredTimeTests.cs new file mode 100644 index 000000000..a99b9850c --- /dev/null +++ b/Projects/Server.Tests/Tests/Serialization/AnchoredTimeTests.cs @@ -0,0 +1,189 @@ +using System; +using System.Collections.Generic; +using System.IO; +using Xunit; + +namespace Server.Tests; + +public class AnchoredTimeTests +{ + private static (BufferWriter Writer, Func Read) CreateRoundTrip() + { + var writer = new BufferWriter(new byte[64], true); + return (writer, shift => new BufferReader(writer.Buffer) { AnchoredTimeShift = shift }); + } + + [Fact] + public void AnchoredTime_RoundTripsExactly_WithZeroShift() + { + var (writer, read) = CreateRoundTrip(); + var value = new DateTime(2026, 8, 22, 12, 30, 0, DateTimeKind.Utc); + + writer.WriteAnchoredTime(value); + + Assert.Equal(value, read(TimeSpan.Zero).ReadAnchoredTime()); + } + + [Fact] + public void AnchoredTime_AppliesShiftOnRead() + { + var (writer, read) = CreateRoundTrip(); + var value = new DateTime(2026, 8, 22, 12, 30, 0, DateTimeKind.Utc); + var shift = TimeSpan.FromHours(3); + + writer.WriteAnchoredTime(value); + + Assert.Equal(value + shift, read(shift).ReadAnchoredTime()); + } + + [Fact] + public void AnchoredTime_SentinelsPassThroughUnshifted() + { + var (writer, read) = CreateRoundTrip(); + + writer.WriteAnchoredTime(DateTime.MinValue); + writer.WriteAnchoredTime(DateTime.MaxValue); + + var reader = read(TimeSpan.FromDays(2)); + Assert.Equal(DateTime.MinValue, reader.ReadAnchoredTime()); + Assert.Equal(DateTime.MaxValue, reader.ReadAnchoredTime()); + } + + [Fact] + public void AnchoredTime_SaturatesInsteadOfOverflowing() + { + var (writer, read) = CreateRoundTrip(); + + writer.WriteAnchoredTime(DateTime.MaxValue - TimeSpan.FromMinutes(1)); + + Assert.Equal(DateTime.MaxValue, read(TimeSpan.FromDays(1)).ReadAnchoredTime()); + } + + [Fact] + public void AnchoredTime_NormalizesLocalKindOnWrite() + { + var (writer, read) = CreateRoundTrip(); + var local = new DateTime(2026, 8, 22, 12, 30, 0, DateTimeKind.Local); + + writer.WriteAnchoredTime(local); + + Assert.Equal(local.ToUniversalTime(), read(TimeSpan.Zero).ReadAnchoredTime()); + } +} + +internal class AnchoredEntity : ISerializable +{ + public AnchoredEntity(Serial serial) => Serial = serial; + + public Serial Serial { get; } + public DateTime Created { get; set; } = DateTime.UtcNow; + public bool Deleted => false; + + public DateTime LastRested { get; set; } + + public void Delete() + { + } + + public void Serialize(IGenericWriter writer) => writer.WriteAnchoredTime(LastRested); + + public void Deserialize(IGenericReader reader) => LastRested = reader.ReadAnchoredTime(); +} + +[Collection("Sequential Server Tests")] +public class AnchoredTimePersistenceTests +{ + private class AnchoredPersistence : GenericEntityPersistence + { + public AnchoredPersistence(int priority) : base("AnchoredTrip", priority, 1, 0x7FFFFFFF) + { + } + } + + /// + /// The idx v5 header carries the save-start anchor; loading re-bases anchored timestamps + /// by the elapsed time since the save started, so downtime does not age them. + /// + [Fact] + public void SaveStartAnchor_RebasesAnchoredTimestampsAtLoad() + { + var previousAssemblies = AssemblyHandler.Assemblies; + AssemblyHandler.Assemblies = [.. previousAssemblies ?? [], typeof(AnchoredEntity).Assembly]; + + var source = new SerializationChunkSource(); + var workers = new SerializationThreadWorker[2]; + for (var i = 0; i < workers.Length; i++) + { + workers[i] = new SerializationThreadWorker(i, source); + workers[i].AllocateHeap(); + } + + var previousWorkers = World._threadWorkers; + World._threadWorkers = workers; + + var previousSaveStart = World.SaveStartTime; + + var persistence = new AnchoredPersistence(2100); + AnchoredPersistence loaded = null; + + var dir = Path.Combine(Path.GetTempPath(), $"muo-anchored-{Guid.NewGuid():N}"); + Directory.CreateDirectory(dir); + + try + { + var lastRested = Core.Now - TimeSpan.FromMinutes(10); + var serial = (Serial)1u; + persistence.EntitiesBySerial[serial] = new AnchoredEntity(serial) { LastRested = lastRested }; + persistence.RegisterType(typeof(AnchoredEntity)); + + // Pretend the save started two hours ago, as if the server had been down since. + var downtime = TimeSpan.FromHours(2); + World.SaveStartTime = Core.Now - downtime; + + foreach (var worker in workers) + { + worker.Wake(); + } + + source.SetOwner(persistence); + Assert.True(persistence.TrySnapshotEntries(out var slotCount)); + source.PushSlotRanges(persistence, slotCount); + + source.Flush(); + foreach (var worker in workers) + { + worker.Sleep(); + } + + persistence.WriteSnapshot(dir); + persistence.PostWorldSave(); + + loaded = new AnchoredPersistence(2101); + loaded.DeserializeIndexes(dir, null); + loaded.Deserialize(dir, null); + + var entity = loaded.EntitiesBySerial[serial]; + var expected = lastRested + downtime; + + Assert.True( + (entity.LastRested - expected).Duration() <= TimeSpan.FromSeconds(30), + $"Anchored timestamp must re-base by the downtime; expected ~{expected}, got {entity.LastRested}." + ); + } + finally + { + World.SaveStartTime = previousSaveStart; + persistence.Unregister(); + loaded?.Unregister(); + + foreach (var worker in workers) + { + worker.Exit(); + } + + World._threadWorkers = previousWorkers; + AssemblyHandler.Assemblies = previousAssemblies; + Directory.Delete(dir, true); + } + } +} diff --git a/Projects/Server/Diagnostics/EventLoopProfiler.cs b/Projects/Server/Diagnostics/EventLoopProfiler.cs new file mode 100644 index 000000000..615a84afd --- /dev/null +++ b/Projects/Server/Diagnostics/EventLoopProfiler.cs @@ -0,0 +1,234 @@ +/************************************************************************* + * ModernUO * + * Copyright 2019-2026 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: EventLoopProfiler.cs * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 3 of the License, or * + * (at your option) any later version. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + *************************************************************************/ + +using System; +using System.Diagnostics; +using System.Runtime.CompilerServices; + +namespace Server; + +public enum LoopPhase +{ + MobileDeltas, + ItemDeltas, + TimerSlice, + NetworkSlice, + LoopTasks, + WorldSnapshot, +} + +/// +/// Event-loop time accounting, compiled out of normal builds. Build with +/// -p:EventLoopProfiling=true to enable; every hook is +/// [Conditional("EVENT_LOOP_PROFILING")], so without the flag the call sites do not exist +/// in the IL and this class is dormant. See dev-docs/debugging-event-loop.md for how to read it. +/// +/// +/// Each one-second sample decomposes wall time into work (per ), sleep, +/// GC pause, and a stolen residual (wall - work - sleep): time the host ran something else. +/// Samples land in a ring buffer (~15 minutes) so a lag episode can be compared against the good +/// minutes on the same box, build, and world — the baseline RunUO's profiler never had. +/// +public static class EventLoopProfiler +{ + public const int PhaseCount = 6; + private const int RingSize = 900; + private const long SampleIntervalMs = 1000; + + public struct Sample + { + public long WallStart; // Core.TickCount at sample start + public long WallMs; // sample length + public long Iterations; + public long Sleeps; + public double SleepMs; // total time blocked in WaitForCompletion + public double SleepOvershootMaxMs; // worst (elapsed - requested) this sample + public long LateWakes; // overshoot >= Timer.TickRate + public long WheelLagMaxMs; // worst wheel lateness observed at Slice entry + public long WakesIssued; + public long WakesElided; + public double GcPauseMs; // GC.GetTotalPauseDuration delta + public int Gen0; + public int Gen1; + public int Gen2; + public PhaseTimes Phases; + + // Work the phases did not account for and the loop did not spend sleeping: host + // scheduling steals, and anything between the bracketed phases. GC pauses inside a + // phase or sleep inflate those measurements instead, so GcPauseMs overlaps rather + // than subtracts. + public double StolenMs + { + get + { + var known = SleepMs + Phases.Total; + return WallMs > known ? WallMs - known : 0; + } + } + } + + [InlineArray(PhaseCount)] + public struct PhaseTimes + { + private double _element0; + + public double Total + { + get + { + double total = 0; + for (var i = 0; i < PhaseCount; i++) + { + total += this[i]; + } + + return total; + } + } + } + + private static readonly double _msPerTick = 1000.0 / Stopwatch.Frequency; + + private static Sample[] _ring; + private static int _ringCount; + private static int _ringHead; + + private static Sample _current; + private static long _phaseStartTimestamp; + private static long _sampleStartedAt; + private static TimeSpan _lastGcPause; + private static int _lastGen0; + private static int _lastGen1; + private static int _lastGen2; + + /// Number of samples recorded so far (capped at the ring size). + public static int SampleCount => _ringCount; + + /// The sample currently being accumulated (not yet in the ring). + public static Sample Current => _current; + + /// + /// Copies the newest completed samples, oldest first. + /// + public static Sample[] History(int count = RingSize) + { + count = Math.Min(count, _ringCount); + var result = new Sample[count]; + for (var i = 0; i < count; i++) + { + result[i] = _ring[(_ringHead - count + i + RingSize) % RingSize]; + } + + return result; + } + + [Conditional("EVENT_LOOP_PROFILING")] + public static void IterationStart(long tickCount) + { + if (_ring == null) + { + _ring = new Sample[RingSize]; + _sampleStartedAt = tickCount; + _current.WallStart = tickCount; + _lastGcPause = GC.GetTotalPauseDuration(); + _lastGen0 = GC.CollectionCount(0); + _lastGen1 = GC.CollectionCount(1); + _lastGen2 = GC.CollectionCount(2); + } + + _current.Iterations++; + + if (tickCount - _sampleStartedAt < SampleIntervalMs) + { + return; + } + + _current.WallMs = tickCount - _sampleStartedAt; + + var pause = GC.GetTotalPauseDuration(); + _current.GcPauseMs = (pause - _lastGcPause).TotalMilliseconds; + _lastGcPause = pause; + + var gen0 = GC.CollectionCount(0); + var gen1 = GC.CollectionCount(1); + var gen2 = GC.CollectionCount(2); + _current.Gen0 = gen0 - _lastGen0; + _current.Gen1 = gen1 - _lastGen1; + _current.Gen2 = gen2 - _lastGen2; + _lastGen0 = gen0; + _lastGen1 = gen1; + _lastGen2 = gen2; + + _ring[_ringHead] = _current; + _ringHead = (_ringHead + 1) % RingSize; + if (_ringCount < RingSize) + { + _ringCount++; + } + + _sampleStartedAt = tickCount; + _current = default; + _current.WallStart = tickCount; + } + + [Conditional("EVENT_LOOP_PROFILING")] + public static void PhaseStart(LoopPhase phase) => _phaseStartTimestamp = Stopwatch.GetTimestamp(); + + [Conditional("EVENT_LOOP_PROFILING")] + public static void PhaseEnd(LoopPhase phase) => + _current.Phases[(int)phase] += (Stopwatch.GetTimestamp() - _phaseStartTimestamp) * _msPerTick; + + [Conditional("EVENT_LOOP_PROFILING")] + public static void SleepEnd(int requestedMs, long elapsedMs) + { + _current.Sleeps++; + _current.SleepMs += elapsedMs; + + var overshoot = elapsedMs - requestedMs; + if (overshoot > _current.SleepOvershootMaxMs) + { + _current.SleepOvershootMaxMs = overshoot; + } + + if (overshoot >= Timer.TickRate) + { + _current.LateWakes++; + } + } + + [Conditional("EVENT_LOOP_PROFILING")] + public static void WheelSlice(long deltaSinceTurn) + { + var lag = deltaSinceTurn - Timer.TickRate; + if (lag > _current.WheelLagMaxMs) + { + _current.WheelLagMaxMs = lag; + } + } + + // Cross-thread; approximate counts are fine for diagnosis, so no interlocked. + [Conditional("EVENT_LOOP_PROFILING")] + public static void WakeSignal(bool elided) + { + if (elided) + { + _current.WakesElided++; + } + else + { + _current.WakesIssued++; + } + } +} diff --git a/Projects/Server/EventLoopTasks.cs b/Projects/Server/EventLoopTasks.cs index 88ae35b49..c3ff2c10c 100644 --- a/Projects/Server/EventLoopTasks.cs +++ b/Projects/Server/EventLoopTasks.cs @@ -42,10 +42,47 @@ public sealed class EventLoopContext : SynchronizationContext public override SynchronizationContext CreateCopy() => new EventLoopContext(); - public void Post(Action d, Priority priority = Priority.Normal) => - (priority == Priority.High ? _priorityQueue : _queue).Enqueue(d); + /// + /// True when no callbacks are waiting to run. + /// + /// + /// drains at most _maxPerFrame callbacks, so work can + /// legitimately be left over. The event loop checks this before sleeping so a backlog keeps + /// it running instead. + /// + public bool IsEmpty => _queue.IsEmpty && _priorityQueue.IsEmpty; - public override void Post(SendOrPostCallback d, object state) => _queue.Enqueue(() => d(state)); + public void Post(Action d, Priority priority = Priority.Normal) + { + (priority == Priority.High ? _priorityQueue : _queue).Enqueue(d); + WakeEventLoop(); + } + + public override void Post(SendOrPostCallback d, object state) + { + _queue.Enqueue(() => d(state)); + WakeEventLoop(); + } + + /// + /// Nudges the game loop in case it is asleep: the loop blocks on network I/O, which a queue + /// push alone does not signal. + /// + private void WakeEventLoop() + { + // A post from the loop thread cannot need a wake -- the loop is executing this very call + // -- and the signal is a syscall on every backend. + if (Thread.CurrentThread == _mainThread) + { + EventLoopProfiler.WakeSignal(elided: true); + return; + } + + EventLoopProfiler.WakeSignal(elided: false); + + // Safe before networking is configured and after teardown; NetState.Wake does nothing. + Network.NetState.Wake(); + } public override void Send(SendOrPostCallback d, object state) { @@ -63,6 +100,8 @@ public sealed class EventLoopContext : SynchronizationContext evt.Set(); }); + WakeEventLoop(); + evt.WaitOne(); } diff --git a/Projects/Server/Events/AccountLoginEvent.cs b/Projects/Server/Events/AccountLoginEvent.cs index 5c81af000..cf1c0bd1a 100644 --- a/Projects/Server/Events/AccountLoginEvent.cs +++ b/Projects/Server/Events/AccountLoginEvent.cs @@ -37,6 +37,13 @@ public class AccountLoginEventArgs public bool Accepted { get; set; } public ALRReason RejectReason { get; set; } + + /// + /// No verdict yet: a subscriber moved the password check off the game loop and replies itself + /// once it lands. The packet handler must send neither accept nor reject while this is set, or + /// the client gets two answers to one login. + /// + public bool Deferred { get; set; } } public static partial class EventSink diff --git a/Projects/Server/Items/Container.cs b/Projects/Server/Items/Container.cs index f139da24b..06c676b25 100644 --- a/Projects/Server/Items/Container.cs +++ b/Projects/Server/Items/Container.cs @@ -44,10 +44,10 @@ public partial class Container : Item internal int _version; [SerializableField(3)] + [SaveFlag(nameof(ShouldSerializeLiftOverride))] [SerializedCommandProperty(AccessLevel.GameMaster)] private bool _liftOverride; - [SerializableFieldSaveFlag(3)] private bool ShouldSerializeLiftOverride() => _liftOverride; public Container(int itemID) : base(itemID) @@ -84,6 +84,7 @@ public partial class Container : Item [EncodedInt] [SerializableProperty(0)] + [SaveFlag(nameof(ShouldSerializeMaxItems), nameof(MaxItemsDefaultValue))] [CommandProperty(AccessLevel.GameMaster)] public int MaxItems { @@ -96,14 +97,13 @@ public partial class Container : Item } } - [SerializableFieldSaveFlag(0)] private bool ShouldSerializeMaxItems() => _maxItems != -1; - [SerializableFieldDefault(0)] private int MaxItemsDefaultValue() => -1; [EncodedInt] [SerializableProperty(1)] + [SaveFlag(nameof(ShouldSerializeGumpId), nameof(GumpIDDefaultValue))] [CommandProperty(AccessLevel.GameMaster)] public int GumpID { @@ -115,14 +115,13 @@ public partial class Container : Item } } - [SerializableFieldSaveFlag(1)] private bool ShouldSerializeGumpId() => _gumpID != -1; - [SerializableFieldDefault(1)] private int GumpIDDefaultValue() => -1; [EncodedInt] [SerializableProperty(2)] + [SaveFlag(nameof(ShouldSerializeDropSound), nameof(DropSoundDefaultValue))] [CommandProperty(AccessLevel.GameMaster)] public int DropSound { @@ -134,10 +133,8 @@ public partial class Container : Item } } - [SerializableFieldSaveFlag(2)] private bool ShouldSerializeDropSound() => _dropSound != -1; - [SerializableFieldDefault(2)] private int DropSoundDefaultValue() => -1; [CommandProperty(AccessLevel.GameMaster)] diff --git a/Projects/Server/Items/DecayScheduler.cs b/Projects/Server/Items/DecayScheduler.cs index 694d9b6b2..70dc1ef27 100644 --- a/Projects/Server/Items/DecayScheduler.cs +++ b/Projects/Server/Items/DecayScheduler.cs @@ -311,7 +311,7 @@ public class DecayScheduler : Timer if (timeUntilDecay > _bucketInterval) { - // Item was moved (SetLastMoved called) - re-bucket or move to overflow + // Deadline was pushed out (SetLastMoved/RestartDecay) - re-bucket or move to overflow if (timeUntilDecay > _totalBucketSpan) { // Extended beyond total span - move to overflow @@ -429,7 +429,7 @@ public class DecayScheduler : Timer { // Refused by the region. Restart the clock rather than dropping the item, which has // already left the queue; re-registering as-is would spin on a due time in the past. - item.SetLastMoved(); + item.RestartDecay(); } } } diff --git a/Projects/Server/Items/Item.cs b/Projects/Server/Items/Item.cs index 186437400..13f249f2f 100644 --- a/Projects/Server/Items/Item.cs +++ b/Projects/Server/Items/Item.cs @@ -335,7 +335,25 @@ public partial class Item : IHued, IComparable, ISpawnable, IObjectPropert [CommandProperty(AccessLevel.GameMaster)] public virtual bool Decays => Movable && Visible && Spawner == null; - public DateTime LastMoved { get; set; } + private DateTime _lastMoved; + + public DateTime LastMoved + { + get => _lastMoved; + set + { + _lastMoved = value; + + // A move at or past the reset stamp supersedes it; drop it so the CompactInfo can collapse. + var info = LookupCompactInfo(); + + if (info != null && info.m_DecayReset != default && info.m_DecayReset <= value) + { + info.m_DecayReset = default; + VerifyCompactInfo(); + } + } + } [CommandProperty(AccessLevel.GameMaster)] public bool Stackable @@ -373,7 +391,7 @@ public partial class Item : IHued, IComparable, ISpawnable, IObjectPropert } Delta(ItemDelta.Update); - UpdateDecayRegistration(); + RestartDecay(); } } } @@ -389,7 +407,7 @@ public partial class Item : IHued, IComparable, ISpawnable, IObjectPropert SetFlag(ImplFlag.Movable, value); Delta(ItemDelta.Update); - UpdateDecayRegistration(); + RestartDecay(); } } } @@ -749,6 +767,12 @@ public partial class Item : IHued, IComparable, ISpawnable, IObjectPropert public static bool ScissorCopyLootType { get; set; } + /// + /// True when the item was produced by the crafting system rather than bought or looted. + /// + [CommandProperty(AccessLevel.GameMaster)] + public bool PlayerConstructed { get; set; } + [CommandProperty(AccessLevel.GameMaster)] public bool QuestItem { @@ -839,7 +863,7 @@ public partial class Item : IHued, IComparable, ISpawnable, IObjectPropert public virtual void Serialize(IGenericWriter writer) { - writer.Write(9); // version + writer.Write(11); // version var flags = SaveFlag.None; @@ -949,6 +973,11 @@ public partial class Item : IHued, IComparable, ISpawnable, IObjectPropert { flags |= SaveFlag.SavedFlags; } + + if (info.m_DecayReset > LastMoved) + { + flags |= SaveFlag.DecayReset; + } } if (info == null || info.m_Weight < 0) @@ -979,16 +1008,21 @@ public partial class Item : IHued, IComparable, ISpawnable, IObjectPropert flags |= SaveFlag.ImplFlags; } + if (PlayerConstructed) + { + flags |= SaveFlag.PlayerConstructed; + } + writer.Write((int)flags); - /* begin last moved time optimization */ - var ticks = LastMoved.Ticks; - var now = Core.Now.Ticks; + // Anchored: shifted by downtime at load, so time-since-moved is preserved and the + // bytes are stable across saves while the item does not move. + writer.WriteAnchoredTime(LastMoved); - var minutes = new TimeSpan(now - ticks).TotalMinutes; - - writer.WriteEncodedInt((int)Math.Clamp(minutes, int.MinValue, int.MaxValue)); - /* end */ + if (GetSaveFlag(flags, SaveFlag.DecayReset)) + { + writer.WriteAnchoredTime(info.m_DecayReset); + } if (GetSaveFlag(flags, SaveFlag.Direction)) { @@ -1307,6 +1341,12 @@ public partial class Item : IHued, IComparable, ISpawnable, IObjectPropert OnMapChange(); + if (m_Parent == null) + { + // A map change is a move; nothing else updates decay registration for a raw Map change. + SetLastMoved(); + } + if (old == null || old == Map.Internal) { InvalidateProperties(); @@ -1537,7 +1577,7 @@ public partial class Item : IHued, IComparable, ISpawnable, IObjectPropert if (oldValue != value) { - UpdateDecayRegistration(); + RestartDecay(); } } } @@ -1731,6 +1771,7 @@ public partial class Item : IHued, IComparable, ISpawnable, IObjectPropert || info.m_HeldBy != null || info.m_BlessedFor != null || info.m_Spawner != null + || info.m_DecayReset != default || info.m_TempFlags != 0 || info.m_SavedFlags != 0 || info.m_Weight >= 0; @@ -2315,7 +2356,64 @@ public partial class Item : IHued, IComparable, ISpawnable, IObjectPropert public virtual bool OnDecay() => CanDecay() && Region.Find(Location, Map).OnDecay(this); - public DateTime ScheduledDecayTime => LastMoved + DecayTime; + public DateTime ScheduledDecayTime + { + get + { + var reset = DecayResetTime; + var lastMoved = LastMoved; + + return (reset > lastMoved ? reset : lastMoved) + DecayTime; + } + } + + /// + /// When decay eligibility was last restored without the item moving, e.g. a GM unfreezing it. + /// The decay countdown runs from the later of this and . + /// + public DateTime DecayResetTime + { + get => LookupCompactInfo()?.m_DecayReset ?? default; + private set + { + if (value == default) + { + var info = LookupCompactInfo(); + + if (info != null && info.m_DecayReset != default) + { + info.m_DecayReset = default; + VerifyCompactInfo(); + } + } + else + { + AcquireCompactInfo().m_DecayReset = value; + } + } + } + + /// + /// Restarts the decay countdown without touching : call when decay + /// eligibility changes state (Movable/Visible/Spawner) or a region refuses a decay, where a + /// stale would otherwise decay the item on the next tick. + /// Stamps only when that extends the current deadline, then + /// updates the scheduler registration. + /// + public void RestartDecay() + { + if (CanDecay()) + { + var now = Core.Now; + + if (ScheduledDecayTime < now + DecayTime) + { + DecayResetTime = now; + } + } + + UpdateDecayRegistration(); + } public void UpdateDecayRegistration() { @@ -2325,6 +2423,12 @@ public partial class Item : IHued, IComparable, ISpawnable, IObjectPropert { DecayScheduler.Register(this); } + else + { + // No countdown to anchor while ineligible; drop the stamp so the CompactInfo + // can collapse. Re-eligibility always re-anchors. + DecayResetTime = default; + } } public void SetLastMoved() @@ -2357,6 +2461,11 @@ public partial class Item : IHued, IComparable, ISpawnable, IObjectPropert } Amount += dropped.Amount; + if (PlayerConstructed != dropped.PlayerConstructed) + { + PlayerConstructed = false; + } + dropped.Delete(); if (playSound && from != null) @@ -2657,6 +2766,8 @@ public partial class Item : IHued, IComparable, ISpawnable, IObjectPropert switch (version) { + case 11: + case 10: case 9: case 8: case 7: @@ -2664,7 +2775,11 @@ public partial class Item : IHued, IComparable, ISpawnable, IObjectPropert { var flags = (SaveFlag)reader.ReadInt(); - if (version < 7) + if (version >= 11) + { + LastMoved = reader.ReadAnchoredTime(); + } + else if (version < 7) { LastMoved = reader.ReadDeltaTime(); } @@ -2682,6 +2797,18 @@ public partial class Item : IHued, IComparable, ISpawnable, IObjectPropert } } + if (version >= 10 && GetSaveFlag(flags, SaveFlag.DecayReset)) + { + var reset = version >= 11 ? reader.ReadAnchoredTime() : reader.ReadDeltaTime(); + + // Pre-v11 LastMoved was stored at whole-minute precision; keep the + // stamp only while it still extends the deadline. + if (reset > LastMoved) + { + DecayResetTime = reset; + } + } + if (GetSaveFlag(flags, SaveFlag.Direction)) { m_Direction = (Direction)reader.ReadByte(); @@ -2854,6 +2981,8 @@ public partial class Item : IHued, IComparable, ISpawnable, IObjectPropert AcquireCompactInfo().m_SavedFlags = reader.ReadEncodedInt(); } + PlayerConstructed = GetSaveFlag(flags, SaveFlag.PlayerConstructed); + if (m_Map != null && m_Parent == null) { m_Map.OnEnter(this); @@ -3325,6 +3454,12 @@ public partial class Item : IHued, IComparable, ISpawnable, IObjectPropert m_DeltaFlags &= ~flags; } + /// + /// True when deltas remain queued after a pass, which is + /// bounded by the count it saw on entry. The event loop consults this before sleeping. + /// + public static bool HasQueuedDeltas => m_DeltaQueue.Count > 0; + public static void ProcessDeltaQueue() { var limit = m_DeltaQueue.Count; @@ -3431,7 +3566,7 @@ public partial class Item : IHued, IComparable, ISpawnable, IObjectPropert for (var i = 0; i < props.Length; i++) { var p = props[i]; - if (p.GetCustomAttribute(typeof(IgnoreDupeAttribute), true) != null || !p.CanRead || !p.CanWrite) + if (p.GetCustomAttribute(true) != null || !p.CanRead || !p.CanWrite) { continue; } @@ -4337,6 +4472,8 @@ public partial class Item : IHued, IComparable, ISpawnable, IObjectPropert public ISpawner m_Spawner; + public DateTime m_DecayReset; + public int m_TempFlags; public double m_Weight = -1; @@ -4374,6 +4511,8 @@ public partial class Item : IHued, IComparable, ISpawnable, IObjectPropert HeldBy = 0x00800000, IntWeight = 0x01000000, SavedFlags = 0x02000000, - NullWeight = 0x04000000 + NullWeight = 0x04000000, + PlayerConstructed = 0x08000000, + DecayReset = 0x10000000 } } diff --git a/Projects/Server/Main.cs b/Projects/Server/Main.cs index 2be9d7d04..608256651 100644 --- a/Projects/Server/Main.cs +++ b/Projects/Server/Main.cs @@ -39,10 +39,181 @@ public static class Core { private static readonly ILogger logger = LogFactory.GetLogger(typeof(Core)); - private static bool _performProcessKill; + // Written off-loop (Kill, RequestSnapshot); volatile because the loop blocks between reads. + private static volatile bool _performProcessKill; private static bool _restartOnKill; - private static bool _performSnapshot; + private static volatile bool _performSnapshot; private static string _snapshotPath; + + // A backstop, not a latency control: the wheel's tick rate already bounds the sleep. + // Measured across 1/2/4/8ms; 2 is optimal. + private static int _eventLoopIdleWaitMs = 2; + + /// + /// Longest the loop will block while idle, in milliseconds. 0 spins instead; the backoff + /// does the same temporarily when the host keeps returning waits late. + /// + public static int EventLoopIdleWaitMs => _eventLoopIdleWaitMs; + + /// + /// True when idle sleeping was disabled at startup because the host cannot honor short + /// waits, overriding whatever server.eventLoopIdleWaitMs was configured to. + /// + public static bool IdleSleepUnsupported { get; private set; } + + /// + /// Whether idle sleeping is currently suspended because the host returned waits late. + /// + /// + /// Compared by subtraction, never directly: tick counts can start enormous and wrap. + /// See dev-docs/tick-counts.md. + /// + public static bool IdleSleepSuspended => _tickCount - _idleSleepSuspendedUntil < 0; + + private const long HealthSampleIntervalMs = 1000; + + // Doubling: a fixed suspension oscillates forever on a persistently bad host, while doubling + // converges on "stop sleeping" yet still recovers from a transient. + private const long BackoffBaseMs = 5000; + private const long BackoffMaxMs = 120_000; + private const int BackoffMaxShift = 5; + + // Clean streak that clears the escalation. + private const long BackoffResetAfterCleanMs = 60_000; + + // Below this a backoff is still recoverable and not actionable, so it only logs at Debug. + private const int WarnAfterConsecutiveBackoffs = 3; + + // A sleep is bounded by the next wheel turn, so only a wait returning late can cost a deadline. + // Measured per sleep, which is why server work (saves, heavy commands) cannot trip the backoff. + private static int _lateWakes; + + // Denominator for the late-wake rate. + private static int _sleepAttempts; + + private static long _nextHealthSample; + private static long _idleSleepSuspendedUntil; + private static int _lateWakeThreshold = 1; + private static int _lateWakePercent = 10; + private static long _idleSleepBackoffs; + private static int _consecutiveBadSamples; + private static int _consecutiveBackoffs; + private static long _currentBackoffMs = BackoffBaseMs; + private static long _lastBackoffAt; + private static bool _loggedBackoffCeiling; + + /// + /// Once a second, suspends idle sleeping (with escalating duration) if the host keeps + /// returning idle waits a full tick or more late. + /// + private static void CheckSchedulerHealth() + { + if (_tickCount - _nextHealthSample < 0) + { + return; + } + + _nextHealthSample = _tickCount + HealthSampleIntervalMs; + + var late = _lateWakes; + var sleeps = _sleepAttempts; + _lateWakes = 0; + _sleepAttempts = 0; + + // A clean streak resets the escalation and re-arms the ceiling Error. Gated on the count + // rather than a "_lastBackoffAt > 0" sentinel because tick counts are not guaranteed positive. + if (_consecutiveBackoffs > 0 && _tickCount - _lastBackoffAt > BackoffResetAfterCleanMs) + { + if (_consecutiveBackoffs >= WarnAfterConsecutiveBackoffs) + { + logger.Information( + "This host has returned idle waits on time for {Duration}ms; idle sleeping is back to normal", + BackoffResetAfterCleanMs + ); + } + + _consecutiveBackoffs = 0; + _loggedBackoffCeiling = false; + } + + if (late <= _lateWakeThreshold) + { + _consecutiveBadSamples = 0; + return; + } + + // Lateness is a rate: an idle loop sleeps hundreds of times a second, so a few outliers are + // normal, while a host that cannot schedule the process returns most of its waits late. The + // threshold above is the floor for windows with too few sleeps for a proportion to mean anything. + if (late * 100 < sleeps * _lateWakePercent) + { + _consecutiveBadSamples = 0; + return; + } + + // Require persistence: any host can drop one sample to unrelated load, but an oversubscribed + // one stays bad. + if (++_consecutiveBadSamples < 2) + { + return; + } + + if (_eventLoopIdleWaitMs <= 0) + { + return; + } + + _currentBackoffMs = Math.Min(BackoffBaseMs << Math.Min(_consecutiveBackoffs, BackoffMaxShift), BackoffMaxMs); + _consecutiveBackoffs++; + _lastBackoffAt = _tickCount; + _idleSleepSuspendedUntil = _tickCount + _currentBackoffMs; + _idleSleepBackoffs++; + + if (_currentBackoffMs >= BackoffMaxMs) + { + // Escalation has run out of room; say so once. + if (!_loggedBackoffCeiling) + { + _loggedBackoffCeiling = true; + logger.Error( + "This host keeps returning idle waits late and sleeping has backed off {Count} times. " + + "The process is not being scheduled promptly, which is typical of shared or burstable vCPUs. " + + "Set server.eventLoopIdleWaitMs to 0 to disable sleeping permanently and trade a full core for latency.", + _idleSleepBackoffs + ); + } + + return; + } + + // Each backoff doubles the suspension, so every line is a distinct escalation step and + // needs no further rate limiting. + if (_consecutiveBackoffs < WarnAfterConsecutiveBackoffs) + { + logger.Debug( + "This host returned a {Requested}ms idle wait at least {TickRate}ms late {Count} of {Sleeps} time(s) " + + "in the last second; idle sleeping suspended for {Duration}ms", + _eventLoopIdleWaitMs, + Timer.TickRate, + late, + sleeps, + _currentBackoffMs + ); + + return; + } + + logger.Warning( + "This host returned a {Requested}ms idle wait at least {TickRate}ms late {Count} of {Sleeps} time(s) in " + + "the last second, for the {Backoffs}th time running; idle sleeping suspended for {Duration}ms", + _eventLoopIdleWaitMs, + Timer.TickRate, + late, + sleeps, + _consecutiveBackoffs, + _currentBackoffMs + ); + } private static bool _crashed; private static string _baseDirectory; @@ -111,14 +282,6 @@ public static class Core public static long Uptime => TickCount - _firstTick; - private static double _currentCPS; - private static double _averageCPS; - private static bool _cpsInitialized; - - public static double CyclesPerSecond => _currentCPS; - - public static double AverageCPS => _averageCPS; - public static string BaseDirectory { get @@ -235,6 +398,10 @@ public static class Core { _restartOnKill = restart; _performProcessKill = true; + + // Callers are usually off-loop (console input, signal handlers); wake so the request + // is noticed now rather than whenever the loop next surfaces. + NetState.Wake(); } public static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) @@ -424,6 +591,45 @@ public static class Core ServerConfiguration.Load(); + // 0 disables idle sleeping entirely (full-core spin, zero scheduling overhead). + var idleWaitMs = ServerConfiguration.GetSetting("server.eventLoopIdleWaitMs", 2); + if (idleWaitMs < 0) + { + logger.Warning( + "server.eventLoopIdleWaitMs {Value} is negative; using 0 (idle sleeping disabled)", + idleWaitMs + ); + } + + _eventLoopIdleWaitMs = Math.Max(0, idleWaitMs); + + // Floor for the backoff: idle waits per second the host may return a full tick late before + // the rate test below applies at all. Set very high to disable the backoff. + var lateWakeThreshold = ServerConfiguration.GetSetting("server.lateWakeThreshold", 1); + if (lateWakeThreshold < 0) + { + logger.Warning( + "server.lateWakeThreshold {Value} is negative; using 0", + lateWakeThreshold + ); + } + + _lateWakeThreshold = Math.Max(0, lateWakeThreshold); + + // Share of a second's idle waits that must return late before the backoff trips. 0 leaves + // the threshold above in sole charge. + var lateWakePercent = ServerConfiguration.GetSetting("server.lateWakePercent", 10); + if (lateWakePercent is < 0 or > 100) + { + logger.Warning( + "server.lateWakePercent {Value} is outside 0-100; using {Clamped}", + lateWakePercent, + Math.Clamp(lateWakePercent, 0, 100) + ); + } + + _lateWakePercent = Math.Clamp(lateWakePercent, 0, 100); + var assemblyPath = Path.Join(BaseDirectory, AssembliesConfiguration); // Load UOContent.dll @@ -440,10 +646,8 @@ public static class Core AssemblyHandler.LoadAssemblies(assemblyFiles); - // First-boot interactive setup. Runs after assemblies are loaded (so content can - // register prompts) but before any Serilog output, so console prompts are not - // interleaved with the async console sink. Handlers self-gate on first-boot state - // (e.g. "is my setting already present?"). + // First-boot interactive setup. After assemblies load so content can register prompts, + // before any Serilog output so prompts are not interleaved with the async console sink. AssemblyHandler.Invoke("ConfigurePrompts"); logger.Information("Running on {Framework}", RuntimeInformation.FrameworkDescription); @@ -453,6 +657,11 @@ public static class Core _now = DateTime.UtcNow; _firstTick = _tickCount = GetTimestamp(); + // Seed from a real tick: tick counts need not start near zero, so a zero-initialized + // deadline compares wrong. See dev-docs/tick-counts.md. + _nextHealthSample = _tickCount + HealthSampleIntervalMs; + _idleSleepSuspendedUntil = _tickCount; + Timer.Init(_tickCount); AssemblyHandler.Invoke("Configure"); @@ -469,41 +678,71 @@ public static class Core NetState.Start(); PingServer.Start(); EventSink.InvokeServerStarted(); + + // Without a high-resolution wait a 2ms request quantises to 15.625ms and the loop runs a + // tick behind. Only fires when the high-res timer and the timeBeginPeriod fallback both failed. + if (_eventLoopIdleWaitMs > 0 && NetState.Ring?.SupportsHighResolutionWait == false) + { + logger.Error( + "This host cannot honor short waits (no high-resolution timer, and raising the system timer " + + "resolution failed). Idle sleeping is disabled. The loop will spin instead, using a full core." + ); + + IdleSleepUnsupported = true; + _eventLoopIdleWaitMs = 0; + } + RunEventLoop(); } + /// + /// True when every queue the loop drains is empty, so sleeping cannot strand pending work. + /// The drains are bounded, so leftovers are normal and must keep the loop awake. + /// + private static bool IsIdle() => + !Mobile.HasQueuedDeltas && !Item.HasQueuedDeltas && LoopContext.IsEmpty && NetState.IsIdle; + public static void RunEventLoop() { try { - var lastRaw = Stopwatch.GetTimestamp(); - const int interval = 100; - double frequency = Stopwatch.Frequency * interval; - const double alpha = 2.0 / 129; // EMA smoothing (≈128-sample window) - - var sample = 0; - while (!Closing) { _tickCount = GetTimestamp(); _now = DateTime.UtcNow; + EventLoopProfiler.IterationStart(_tickCount); + + EventLoopProfiler.PhaseStart(LoopPhase.MobileDeltas); Mobile.ProcessDeltaQueue(); + EventLoopProfiler.PhaseEnd(LoopPhase.MobileDeltas); + + EventLoopProfiler.PhaseStart(LoopPhase.ItemDeltas); Item.ProcessDeltaQueue(); + EventLoopProfiler.PhaseEnd(LoopPhase.ItemDeltas); + + EventLoopProfiler.PhaseStart(LoopPhase.TimerSlice); Timer.Slice(_tickCount); + EventLoopProfiler.PhaseEnd(LoopPhase.TimerSlice); // Handle networking + EventLoopProfiler.PhaseStart(LoopPhase.NetworkSlice); NetState.Slice(); + EventLoopProfiler.PhaseEnd(LoopPhase.NetworkSlice); // Execute captured post-await methods (like Timer.Pause) + EventLoopProfiler.PhaseStart(LoopPhase.LoopTasks); LoopContext.ExecuteTasks(); + EventLoopProfiler.PhaseEnd(LoopPhase.LoopTasks); Timer.CheckTimerPool(); // Check for pool depletion so we can async refill it. if (_performSnapshot) { + EventLoopProfiler.PhaseStart(LoopPhase.WorldSnapshot); // Return value is the offset that can be used to fix timers that should drift World.Snapshot(_snapshotPath); + EventLoopProfiler.PhaseEnd(LoopPhase.WorldSnapshot); _performSnapshot = false; } @@ -513,29 +752,35 @@ public static class Core break; } - if (sample++ == interval) + CheckSchedulerHealth(); + + if (_eventLoopIdleWaitMs > 0 && _tickCount - _idleSleepSuspendedUntil >= 0 && IsIdle()) { - sample = 0; - var nowRaw = Stopwatch.GetTimestamp(); - - _currentCPS = frequency / (nowRaw - lastRaw); - - if (!_cpsInitialized) + // Re-read the clock: a stale timestamp overstates the time to the next tick + // and sleeps straight past it. + var start = GetTimestamp(); + var due = Timer.MillisecondsUntilNextTick(start); + if (due > 0) { - _averageCPS = _currentCPS; - _cpsInitialized = true; - } - else - { - _averageCPS += alpha * (_currentCPS - _averageCPS); - } + var requested = (int)Math.Min(due, _eventLoopIdleWaitMs); - lastRaw = nowRaw; + // The GC prefers to collect during idle sleeps, so its pauses land here by + // design and are not the host's fault. Gen1 and above (what + // CollectionCount(1) counts) are the only pauses long enough to reach a tick. + var collections = GC.CollectionCount(1); - var sleepMs = (int)Timer.MillisecondsUntilNextTick(_tickCount); - if (sleepMs >= 2) - { - NetState.WaitForCompletion(sleepMs - 1); + NetState.WaitForCompletion(requested); + + var elapsed = GetTimestamp() - start; + EventLoopProfiler.SleepEnd(requested, elapsed); + _sleepAttempts++; + + // The second collection read sits behind the overshoot test, so the common + // path reads the counter once, not twice. + if (elapsed - requested >= Timer.TickRate && GC.CollectionCount(1) == collections) + { + _lateWakes++; + } } } } @@ -553,6 +798,9 @@ public static class Core { _snapshotPath = snapshotPath; _performSnapshot = true; + + // Save requests arrive off-loop; wake so the snapshot starts now. + NetState.Wake(); } public static void VerifySerialization() diff --git a/Projects/Server/Mobiles/Mobile.cs b/Projects/Server/Mobiles/Mobile.cs index f22c9a939..fb028b9d7 100644 --- a/Projects/Server/Mobiles/Mobile.cs +++ b/Projects/Server/Mobiles/Mobile.cs @@ -2324,11 +2324,11 @@ public partial class Mobile : IHued, IComparable, ISpawnable, IObjectPro public virtual void Serialize(IGenericWriter writer) { - writer.Write(37); // version + writer.Write(38); // version - writer.WriteDeltaTime(LastStrGain); - writer.WriteDeltaTime(LastIntGain); - writer.WriteDeltaTime(LastDexGain); + writer.WriteAnchoredTime(LastStrGain); + writer.WriteAnchoredTime(LastIntGain); + writer.WriteAnchoredTime(LastDexGain); byte hairflag = 0x00; @@ -5248,8 +5248,15 @@ public partial class Mobile : IHued, IComparable, ISpawnable, IObjectPro item.Name = oldItem.Name; item.Weight = oldItem.Weight; + item.PlayerConstructed = oldItem.PlayerConstructed; item.Amount = oldAmount - amount; - item.Map = oldItem.Map; + + // A parented remainder gets its map from AddItem (parent first, then map), keeping the + // split off the decay scheduler; a ground remainder is placed and enrolled here. + if (oldItem.Parent == null) + { + item.Map = oldItem.Map; + } oldItem.OnAfterDuped(item); @@ -6143,6 +6150,7 @@ public partial class Mobile : IHued, IComparable, ISpawnable, IObjectPro switch (version) { + case 38: // Stat-gain stamps moved from delta time to anchored time case 37: // Decomposed hair into inline item id/hue (dropped the VirtualHairInfo object) case 36: // Moved virtues to VirtueSystem case 35: // Moved short term murders to PlayerMurderSystem @@ -6151,9 +6159,18 @@ public partial class Mobile : IHued, IComparable, ISpawnable, IObjectPro case 32: // Removed StuckMenu case 31: { - LastStrGain = reader.ReadDeltaTime(); - LastIntGain = reader.ReadDeltaTime(); - LastDexGain = reader.ReadDeltaTime(); + if (version >= 38) + { + LastStrGain = reader.ReadAnchoredTime(); + LastIntGain = reader.ReadAnchoredTime(); + LastDexGain = reader.ReadAnchoredTime(); + } + else + { + LastStrGain = reader.ReadDeltaTime(); + LastIntGain = reader.ReadDeltaTime(); + LastDexGain = reader.ReadDeltaTime(); + } goto case 30; } @@ -7834,6 +7851,12 @@ public partial class Mobile : IHued, IComparable, ISpawnable, IObjectPro } } + /// + /// True when deltas remain queued after a pass, which is + /// bounded by the count it saw on entry. The event loop consults this before sleeping. + /// + public static bool HasQueuedDeltas => m_DeltaQueue.Count > 0; + public static void ProcessDeltaQueue() { var limit = m_DeltaQueue.Count; diff --git a/Projects/Server/Mobiles/Mods/ResistanceMod.cs b/Projects/Server/Mobiles/Mods/ResistanceMod.cs index bd569f20e..a5423e039 100644 --- a/Projects/Server/Mobiles/Mods/ResistanceMod.cs +++ b/Projects/Server/Mobiles/Mods/ResistanceMod.cs @@ -21,17 +21,15 @@ namespace Server; [SerializationGenerator(0)] public partial class ResistanceMod : MobileMod { - [SerializableField(0)] + [SerializableField(0, fieldChanged: nameof(OnTypeChanged))] private ResistanceType _type; - [SerializableFieldChanged(0)] [MethodImpl(MethodImplOptions.AggressiveInlining)] private void OnTypeChanged(ResistanceType oldValue, ResistanceType newValue) => Owner?.UpdateResistances(); - [SerializableField(1)] + [SerializableField(1, fieldChanged: nameof(OnOffsetChanged))] private int _offset; - [SerializableFieldChanged(1)] [MethodImpl(MethodImplOptions.AggressiveInlining)] private void OnOffsetChanged(int oldValue, int newValue) => Owner?.UpdateResistances(); diff --git a/Projects/Server/Mobiles/Mods/SkillMod.cs b/Projects/Server/Mobiles/Mods/SkillMod.cs index a78ec5ce6..cfe96c0c4 100644 --- a/Projects/Server/Mobiles/Mods/SkillMod.cs +++ b/Projects/Server/Mobiles/Mods/SkillMod.cs @@ -21,33 +21,29 @@ namespace Server; [SerializationGenerator(0)] public abstract partial class SkillMod : MobileMod { - [SerializableField(0)] + [SerializableField(0, fieldChanged: nameof(OnObeyCapChanged))] private bool _obeyCap; - [SerializableFieldChanged(0)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void OnObeCapChanged(bool oldValue, bool newValue) => Owner?.Skills[_skill]?.Update(); + private void OnObeyCapChanged(bool oldValue, bool newValue) => Owner?.Skills[_skill]?.Update(); - [SerializableField(1)] + [SerializableField(1, fieldChanged: nameof(OnSkillChanged))] private SkillName _skill; - [SerializableFieldChanged(1)] private void OnSkillChanged(SkillName oldValue, SkillName newValue) { Owner?.Skills[newValue]?.Update(); Owner?.Skills[oldValue]?.Update(); } - [SerializableField(2)] + [SerializableField(2, fieldChanged: nameof(OnRelativeChanged))] private bool _relative; - [SerializableFieldChanged(2)] private void OnRelativeChanged(bool oldValue, bool newValue) => Owner?.Skills[_skill]?.Update(); - [SerializableField(3)] + [SerializableField(3, fieldChanged: nameof(OnValueChanged))] private double _value; - [SerializableFieldChanged(3)] private void OnValueChanged(double oldValue, double newValue) => Owner?.Skills[_skill]?.Update(); public SkillMod(Mobile owner) : base(owner) diff --git a/Projects/Server/Network/NetState/NetState.Network.cs b/Projects/Server/Network/NetState/NetState.Network.cs index 7a17d8cd0..f69f92076 100644 --- a/Projects/Server/Network/NetState/NetState.Network.cs +++ b/Projects/Server/Network/NetState/NetState.Network.cs @@ -71,6 +71,24 @@ public partial class NetState _socketManager?.WaitForCompletion(timeoutMs); } + /// + /// Wakes the game loop if it is blocked in . Safe from any + /// thread; a no-op before networking is configured or after teardown. The signal is sticky, + /// so a wake racing the loop's decision to sleep is not lost. + /// + public static void Wake() + { + _socketManager?.Ring?.Wake(); + } + + /// + /// True when no queued network work remains for the loop to drain. defers + /// work in several places, so an empty completion queue alone is not enough. + /// + internal static bool IsIdle => + _throttled.Count == 0 && _throttledPending.Count == 0 && + _flushPending.Count == 0 && _pendingDisconnects.Count == 0 && _disposed.Count == 0; + /// /// Gets the listening addresses that the server is bound to. /// diff --git a/Projects/Server/Serialization/BinaryFileReader.cs b/Projects/Server/Serialization/BinaryFileReader.cs index ec76516aa..f8b51c182 100644 --- a/Projects/Server/Serialization/BinaryFileReader.cs +++ b/Projects/Server/Serialization/BinaryFileReader.cs @@ -74,6 +74,12 @@ public sealed unsafe class BinaryFileReader : IDisposable, IGenericReader /// public long Position => _reader.Position; + public TimeSpan AnchoredTimeShift + { + get => _reader.AnchoredTimeShift; + set => _reader.AnchoredTimeShift = value; + } + public void Dispose() { _accessor?.SafeMemoryMappedViewHandle.ReleasePointer(); diff --git a/Projects/Server/Serialization/BufferReader.cs b/Projects/Server/Serialization/BufferReader.cs index 846e9e5dc..bd37f4ff6 100644 --- a/Projects/Server/Serialization/BufferReader.cs +++ b/Projects/Server/Serialization/BufferReader.cs @@ -37,6 +37,8 @@ public class BufferReader : IGenericReader public long Position => _position; public long BufferSize => _buffer.Length; + public TimeSpan AnchoredTimeShift { get; set; } + public BufferReader(byte[] buffer, Dictionary typesDb = null, Encoding encoding = null) { _buffer = buffer; diff --git a/Projects/Server/Serialization/BufferWriter.cs b/Projects/Server/Serialization/BufferWriter.cs index b811d68cb..ab53e2f2f 100644 --- a/Projects/Server/Serialization/BufferWriter.cs +++ b/Projects/Server/Serialization/BufferWriter.cs @@ -384,6 +384,7 @@ public class BufferWriter : IGenericWriter } [MethodImpl(MethodImplOptions.AggressiveInlining)] + [Obsolete("Delta time rewrites its bytes on every save. Write anchored time instead (WriteAnchoredTime, or [AnchoredDateTime] on generated fields); bump the containing type's version, as the wire format changes. Existing delta payloads remain readable through ReadDeltaTime in old-version fallbacks.")] public void WriteDeltaTime(DateTime value) { if (value == DateTime.MinValue) @@ -407,6 +408,21 @@ public class BufferWriter : IGenericWriter Write(value.Ticks - DateTime.UtcNow.Ticks); } + /// + /// Writes the absolute value; re-bases it + /// by the elapsed time since the save started, so downtime does not age it and an + /// unchanged value serializes to identical bytes. + /// + public void WriteAnchoredTime(DateTime value) + { + if (value.Kind == DateTimeKind.Local) + { + value = value.ToUniversalTime(); + } + + Write(value.Ticks); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Write(IPAddress value) { diff --git a/Projects/Server/Serialization/GenericEntityPersistence.cs b/Projects/Server/Serialization/GenericEntityPersistence.cs index a49f213eb..c7323329f 100644 --- a/Projects/Server/Serialization/GenericEntityPersistence.cs +++ b/Projects/Server/Serialization/GenericEntityPersistence.cs @@ -114,9 +114,10 @@ public class GenericEntityPersistence : GenericPersistence, IGenericEntityPer using var binFs = new FileStream( Path.Combine(dir, $"{Name}.bin"), FileMode.Create, FileAccess.Write, FileShare.None, 1024 * 1024 ); - // v4 records are fixed-width 26 bytes; the header carries the type table - // (name lengths vary — 64 bytes per entry is a staging hint, not a contract). - var expectedIdxSize = 12 + 26L * EntitiesBySerial.Count + 64L * _typeTable.Count; + // v4 records are fixed-width 26 bytes; the v5 header carries the save-start anchor + // and the type table (name lengths vary — 64 bytes per entry is a staging hint, not + // a contract). + var expectedIdxSize = 20 + 26L * EntitiesBySerial.Count + 64L * _typeTable.Count; using var idx = new FileBufferWriter(Path.Combine(dir, $"{Name}.idx"), expectedIdxSize); var binPosition = 0L; @@ -142,7 +143,10 @@ public class GenericEntityPersistence : GenericPersistence, IGenericEntityPer binPosition += _selfLength; } - idx.Write(4); // Version + idx.Write(5); // Version + + // One anchor for the whole save: the world is frozen from the moment it is stamped. + idx.Write(World.SaveStartTime.Ticks); // The type table is fully known at freeze (AddEntity diverts to the pending // queues while saving) and is written before the records so the loader can @@ -494,6 +498,18 @@ public class GenericEntityPersistence : GenericPersistence, IGenericEntityPer var version = dataReader.ReadInt(); + if (version >= 5) + { + // Re-base anchored timestamps by the elapsed time since the save started. + var anchor = new DateTime(dataReader.ReadLong(), DateTimeKind.Utc); + var shift = Core.Now - anchor; + _anchoredTimeShift = anchor.Ticks > 0 && shift > TimeSpan.Zero ? shift : TimeSpan.Zero; + + // The whole save shares one anchor. Publish it so payloads without their own + // (GenericPersistence bins) can shift too; indexes load before any of them. + World.LoadTimeShift = _anchoredTimeShift; + } + if (version >= 4) { DeserializeIndexesV4(dataReader, entities); @@ -660,6 +676,9 @@ public class GenericEntityPersistence : GenericPersistence, IGenericEntityPer private static List _toDelete; + // From the loaded idx (v5+); zero when the save predates the anchor. + private TimeSpan _anchoredTimeShift; + private unsafe void InternalDeserialize(string filePath, int index, Dictionary typesDb) { using var mmf = MemoryMappedFile.CreateFromFile(filePath, FileMode.Open); @@ -667,7 +686,10 @@ public class GenericEntityPersistence : GenericPersistence, IGenericEntityPer byte* ptr = null; accessor.SafeMemoryMappedViewHandle.AcquirePointer(ref ptr); - var dataReader = new UnmanagedDataReader(ptr, accessor.Length, typesDb); + var dataReader = new UnmanagedDataReader(ptr, accessor.Length, typesDb) + { + AnchoredTimeShift = _anchoredTimeShift + }; Deserialize(dataReader); diff --git a/Projects/Server/Serialization/GenericPersistence.cs b/Projects/Server/Serialization/GenericPersistence.cs index 5b8e79c29..2268a86c6 100644 --- a/Projects/Server/Serialization/GenericPersistence.cs +++ b/Projects/Server/Serialization/GenericPersistence.cs @@ -98,7 +98,13 @@ public abstract class GenericPersistence : Persistence, IGenericSerializable byte* ptr = null; accessor.SafeMemoryMappedViewHandle.AcquirePointer(ref ptr); - var dataReader = new UnmanagedDataReader(ptr, accessor.Length, typesDb); + var dataReader = new UnmanagedDataReader(ptr, accessor.Length, typesDb) + { + // These payloads carry no anchor of their own; they inherit the save-wide + // shift stamped while the entity indexes were read (indexes always load + // before persistence payloads — see Persistence.Load). + AnchoredTimeShift = World.LoadTimeShift + }; Deserialize(dataReader); error = dataReader.Position != fileLength diff --git a/Projects/Server/Serialization/IGenericReader.cs b/Projects/Server/Serialization/IGenericReader.cs index 0244a5166..bfa302b39 100644 --- a/Projects/Server/Serialization/IGenericReader.cs +++ b/Projects/Server/Serialization/IGenericReader.cs @@ -43,6 +43,12 @@ public interface IGenericReader DateTime ReadDateTime() => new(ReadLong(), DateTimeKind.Utc); TimeSpan ReadTimeSpan() => new(ReadLong()); + /// + /// Decodes a legacy delta-time value. Only for reading old-version payloads (version + /// fallbacks and migration replays) — current formats store anchored time and read it + /// with . is + /// obsolete: no current-version format may write delta time. + /// DateTime ReadDeltaTime() { return ReadLong() switch @@ -52,6 +58,37 @@ public interface IGenericReader var delta => new DateTime(delta + DateTime.UtcNow.Ticks, DateTimeKind.Utc) }; } + + /// + /// Elapsed time between the loaded save starting and this load, applied by + /// . Zero when the source carries no anchor. + /// + TimeSpan AnchoredTimeShift => TimeSpan.Zero; + + DateTime ReadAnchoredTime() + { + var value = ReadDateTime(); + + if (value == DateTime.MinValue || value == DateTime.MaxValue) + { + return value; + } + + var shift = AnchoredTimeShift; + if (shift == TimeSpan.Zero) + { + return value; + } + + var ticks = value.Ticks + shift.Ticks; + + if (ticks >= DateTime.MaxValue.Ticks) + { + return DateTime.MaxValue; + } + + return ticks <= 0 ? DateTime.MinValue : new DateTime(ticks, DateTimeKind.Utc); + } decimal ReadDecimal() => new([ReadInt(), ReadInt(), ReadInt(), ReadInt()]); int ReadEncodedInt() { diff --git a/Projects/Server/Serialization/IGenericWriter.cs b/Projects/Server/Serialization/IGenericWriter.cs index 9c9563139..4162655de 100644 --- a/Projects/Server/Serialization/IGenericWriter.cs +++ b/Projects/Server/Serialization/IGenericWriter.cs @@ -40,7 +40,11 @@ public interface IGenericWriter void Write(decimal value); void WriteEncodedInt(int value); void Write(DateTime value); + + [Obsolete("Delta time rewrites its bytes on every save. Write anchored time instead (WriteAnchoredTime, or [AnchoredDateTime] on generated fields); bump the containing type's version, as the wire format changes. Existing delta payloads remain readable through ReadDeltaTime in old-version fallbacks.")] void WriteDeltaTime(DateTime value); + + void WriteAnchoredTime(DateTime value); void Write(IPAddress value); void Write(TimeSpan value); void Write(Point3D value); diff --git a/Projects/Server/Serialization/UnmanagedDataReader.cs b/Projects/Server/Serialization/UnmanagedDataReader.cs index 7c8b5da1e..aa3916ee9 100644 --- a/Projects/Server/Serialization/UnmanagedDataReader.cs +++ b/Projects/Server/Serialization/UnmanagedDataReader.cs @@ -43,6 +43,8 @@ public unsafe class UnmanagedDataReader : IGenericReader /// public long Position { get; private set; } + public TimeSpan AnchoredTimeShift { get; set; } + /// /// Read bits of data raw from a serialized file using Little-endian. /// diff --git a/Projects/Server/Server.csproj b/Projects/Server/Server.csproj index b022c584c..fd69557b7 100644 --- a/Projects/Server/Server.csproj +++ b/Projects/Server/Server.csproj @@ -34,14 +34,13 @@ - + - - + + - - - + + diff --git a/Projects/Server/Timer/Timer.TimerWheel.cs b/Projects/Server/Timer/Timer.TimerWheel.cs index 8c9dc47fd..2ab3ce547 100644 --- a/Projects/Server/Timer/Timer.TimerWheel.cs +++ b/Projects/Server/Timer/Timer.TimerWheel.cs @@ -51,8 +51,15 @@ public partial class Timer } } + /// + /// Milliseconds of simulated time one wheel turn advances. + /// + public static int TickRate => _tickRate; + public static void Slice(long tickCount) { + EventLoopProfiler.WheelSlice(tickCount - _lastTickTurned); + var deltaSinceTurn = tickCount - _lastTickTurned; while (deltaSinceTurn >= _tickRate) { diff --git a/Projects/Server/Utilities/Utility.cs b/Projects/Server/Utilities/Utility.cs index f3786d834..9d7cd9f3b 100644 --- a/Projects/Server/Utilities/Utility.cs +++ b/Projects/Server/Utilities/Utility.cs @@ -1050,28 +1050,16 @@ public static partial class Utility return; } - using var queue = PooledRefQueue.Create(); foreach (var (key, value) in dictionary) { - if (serializableKey) - { - if (key == null || ((ISerializable)key).Deleted) - { - queue.Enqueue(key); - } - } - else - { - if (value == null || ((ISerializable)value).Deleted) - { - queue.Enqueue(key); - } - } - } + var deleted = serializableKey + ? ((ISerializable)key).Deleted + : value == null || ((ISerializable)value).Deleted; - while (queue.Count > 0) - { - dictionary.Remove(queue.Dequeue()); + if (deleted) + { + dictionary.Remove(key); + } } dictionary.TrimExcess(); diff --git a/Projects/Server/World/World.cs b/Projects/Server/World/World.cs index 2c9e6d216..c00fc85f6 100644 --- a/Projects/Server/World/World.cs +++ b/Projects/Server/World/World.cs @@ -93,6 +93,21 @@ public static class World public static string SavePath { get; private set; } public static WorldState WorldState { get; private set; } public static bool Saving => WorldState == WorldState.Saving; + + /// + /// UTC time the current or most recent world save started. Written into save indexes so + /// anchored timestamps can be re-based by the downtime at load. + /// + public static DateTime SaveStartTime { get; internal set; } + + /// + /// The anchored-time shift for the save currently being loaded: the downtime between the + /// save's start and this load. Stamped while entity indexes are read (they all carry the + /// same anchor, since the whole save shares one ) and applied + /// to every reader of that save's files — including + /// payloads, which carry no anchor of their own. Zero for saves that predate the anchor. + /// + public static TimeSpan LoadTimeShift { get; internal set; } public static bool Running => WorldState is not WorldState.Loading and not WorldState.Initial; public static bool Loading => WorldState == WorldState.Loading; @@ -287,6 +302,10 @@ public static class World WorldState = WorldState.Saving; + // The world is frozen from here: one anchor for the whole save. Written into save + // indexes so anchored timestamps can be re-based by the downtime at load. + SaveStartTime = Core.Now; + Broadcast(0x35, true, "The world is saving, please wait."); logger.Information("Saving world"); diff --git a/Projects/UOContent.Tests/Fixtures/TestServerInitializer.cs b/Projects/UOContent.Tests/Fixtures/TestServerInitializer.cs index 640c05837..a38af9c3d 100644 --- a/Projects/UOContent.Tests/Fixtures/TestServerInitializer.cs +++ b/Projects/UOContent.Tests/Fixtures/TestServerInitializer.cs @@ -100,6 +100,8 @@ internal static class TestServerInitializer } World.Configure(); + // Registers the Accounts entity persistence; without it no test can construct an Account. + Server.Accounting.Accounts.Configure(); RaceDefinitions.Configure(); MovementImpl.Configure(); PathFollower.Configure(); diff --git a/Projects/UOContent.Tests/Tests/Accounting/AccountPasswordTests.cs b/Projects/UOContent.Tests/Tests/Accounting/AccountPasswordTests.cs new file mode 100644 index 000000000..29a1b818f --- /dev/null +++ b/Projects/UOContent.Tests/Tests/Accounting/AccountPasswordTests.cs @@ -0,0 +1,74 @@ +using System; +using Server.Accounting; +using Server.Accounting.Security; +using Xunit; + +namespace Server.Tests.Accounting; + +[Collection("Sequential UOContent Tests")] +public class AccountPasswordTests : IDisposable +{ + private const string Password = "hunter2"; + + // CurrentAlgorithm is process-wide state shared with the rest of the collection. + private readonly PasswordProtectionAlgorithm _originalAlgorithm = AccountSecurity.CurrentAlgorithm; + + public void Dispose() => AccountSecurity.CurrentAlgorithm = _originalAlgorithm; + + [Theory] + [InlineData(PasswordProtectionAlgorithm.SHA1)] + [InlineData(PasswordProtectionAlgorithm.SHA2)] + [InlineData(PasswordProtectionAlgorithm.PBKDF2)] + [InlineData(PasswordProtectionAlgorithm.Argon2)] + public void NewAccount_CanLogIn(PasswordProtectionAlgorithm algorithm) + { + AccountSecurity.CurrentAlgorithm = algorithm; + var account = new Account($"new-{algorithm}-user", Password); + + Assert.Equal(algorithm, account.PasswordAlgorithm); + Assert.True(account.CheckPassword(Password)); + Assert.False(account.CheckPassword("wrong-password")); + } + + // SetPassword assigns PasswordAlgorithm before deriving the phrase from it. Reversed, the hash + // is salted by the outgoing algorithm's rule but stored under the incoming one, which verifies + // once and then never again. + [Theory] + [InlineData(PasswordProtectionAlgorithm.SHA1)] + [InlineData(PasswordProtectionAlgorithm.SHA2)] + [InlineData(PasswordProtectionAlgorithm.PBKDF2)] + public void UpgradingAlgorithm_DoesNotLockTheAccountOut(PasswordProtectionAlgorithm from) + { + AccountSecurity.CurrentAlgorithm = from; + var account = new Account($"upgrade-{from}-user", Password); + Assert.True(account.CheckPassword(Password)); + + AccountSecurity.CurrentAlgorithm = PasswordProtectionAlgorithm.Argon2; + + Assert.True(account.CheckPassword(Password)); + Assert.Equal(PasswordProtectionAlgorithm.Argon2, account.PasswordAlgorithm); + + // Must verify against what the rehash wrote. + Assert.True(account.CheckPassword(Password)); + Assert.False(account.CheckPassword("wrong-password")); + } + + [Fact] + public void StaleArgon2Parameters_AreRehashedOnLogin() + { + AccountSecurity.CurrentAlgorithm = PasswordProtectionAlgorithm.Argon2; + var account = new Account("stale-params-user", Password); + + // The shipping default before this change: Argon2i, m=8192, t=3, p=1. + account.Password = + "$argon2i$v=19$m=8192,t=3,p=1$LD1XJz7P3wQmIJ+Tu6ScgA$NO5hBABsHQ172C5nDO2X4gWnB4jDef3x6WhLdVE2LFw"; + + Assert.True(account.CheckPassword(Password)); + Assert.StartsWith("$argon2id$v=19$m=16384,t=1,p=1$", account.Password); + + // Already current: verifying again must not rewrite the hash. + var afterFirst = account.Password; + Assert.True(account.CheckPassword(Password)); + Assert.Equal(afterFirst, account.Password); + } +} diff --git a/Projects/UOContent.Tests/Tests/Accounting/PasswordWorkerTests.cs b/Projects/UOContent.Tests/Tests/Accounting/PasswordWorkerTests.cs new file mode 100644 index 000000000..5972e5eb9 --- /dev/null +++ b/Projects/UOContent.Tests/Tests/Accounting/PasswordWorkerTests.cs @@ -0,0 +1,229 @@ +using System; +using System.Threading; +using Server.Accounting; +using Server.Accounting.Security; +using Xunit; + +namespace Server.Tests.Accounting; + +[Collection("Sequential UOContent Tests")] +public class PasswordWorkerTests : IDisposable +{ + private const string Password = "hunter2"; + + private readonly PasswordProtectionAlgorithm _originalAlgorithm = AccountSecurity.CurrentAlgorithm; + + public PasswordWorkerTests() => AccountSecurity.CurrentAlgorithm = PasswordProtectionAlgorithm.Argon2; + + public void Dispose() => AccountSecurity.CurrentAlgorithm = _originalAlgorithm; + + private static Account CreateAccount(string username) => + Accounts.GetAccount(username) as Account ?? new Account(username, Password); + + /// + /// Enqueues work, then pumps the loop context until or the deadline. + /// + /// The context pins itself to the thread that constructed it and refuses ExecuteTasks + /// from any other. The fixture's belongs to whichever thread built the fixture, and xUnit gives + /// no guarantee that a test method runs on that thread even inside a sequential collection -- + /// so this owns one for the duration and puts the original back. Pumping the fixture's context + /// passed locally and failed on CI. + /// + private static void PumpUntil(Action enqueue, Func complete, int timeoutSeconds = 20) + { + var original = Core.LoopContext; + var owned = new EventLoopContext(); + Core.LoopContext = owned; + + try + { + enqueue(); + + var deadline = DateTime.UtcNow.AddSeconds(timeoutSeconds); + + while (!complete() && DateTime.UtcNow < deadline) + { + owned.ExecuteTasks(); + Thread.Sleep(5); + } + + // Anything that landed between the last pump and the final check. + owned.ExecuteTasks(); + } + finally + { + Core.LoopContext = original; + } + } + + private static PasswordJob JobFor(Account account, string submitted) => + new() + { + Account = account, + StoredHash = account.Password, + VerifyPhrase = account.GetVerifyPhrase(submitted), + HashPhrase = account.NeedsPasswordUpgrade() ? account.GetRehashPhrase(submitted) : null, + StoredAlgorithm = account.PasswordAlgorithm, + TargetAlgorithm = AccountSecurity.CurrentAlgorithm + }; + + /// + /// Drives the real queue rather than ComputeInline. A job with no NetState attached -- an + /// admin password change -- was being dropped by the liveness check, which read a null State as + /// a dead connection, so the change silently never happened and its callback never fired. + /// + [Fact] + public void RunsAJobThatHasNoConnectionAttached() + { + var account = CreateAccount("offloop-no-netstate-user"); + var applied = false; + + var job = new PasswordJob + { + Account = account, + HashPhrase = account.GetRehashPhrase("a-queued-password"), + TargetAlgorithm = AccountSecurity.CurrentAlgorithm, + OnComplete = (_, outcome) => applied = outcome.Hash != null + }; + + PumpUntil(() => Assert.True(PasswordWorker.TryEnqueue(job)), () => applied); + + Assert.True(applied); + Assert.True(account.CheckPassword("a-queued-password")); + } + + [Fact] + public void VerifiesTheCorrectPassword() + { + var account = CreateAccount("offloop-correct-user"); + + var outcome = PasswordWorker.ComputeInline(JobFor(account, Password)); + + Assert.True(outcome.Verified); + } + + [Fact] + public void RejectsTheWrongPassword() + { + var account = CreateAccount("offloop-wrong-user"); + + var outcome = PasswordWorker.ComputeInline(JobFor(account, "not-the-password")); + + Assert.False(outcome.Verified); + Assert.Null(outcome.Hash); + } + + [Fact] + public void ProducesNoUpgradeWhenParametersAreCurrent() + { + var account = CreateAccount("offloop-current-user"); + + var outcome = PasswordWorker.ComputeInline(JobFor(account, Password)); + + Assert.True(outcome.Verified); + Assert.Null(outcome.Hash); + } + + [Fact] + public void ProducesAnUpgradeWhenParametersAreStale() + { + var account = CreateAccount("offloop-stale-user"); + + // The shipping default before #2562: Argon2i, m=8192, t=3, p=1. + account.Password = + "$argon2i$v=19$m=8192,t=3,p=1$LD1XJz7P3wQmIJ+Tu6ScgA$NO5hBABsHQ172C5nDO2X4gWnB4jDef3x6WhLdVE2LFw"; + + var outcome = PasswordWorker.ComputeInline(JobFor(account, Password)); + + Assert.True(outcome.Verified); + Assert.StartsWith("$argon2id$v=19$m=16384,t=1,p=1$", outcome.Hash); + } + + [Fact] + public void ProducesNoUpgradeWhenThePasswordIsWrong() + { + var account = CreateAccount("offloop-wrong-stale-user"); + account.Password = + "$argon2i$v=19$m=8192,t=3,p=1$LD1XJz7P3wQmIJ+Tu6ScgA$NO5hBABsHQ172C5nDO2X4gWnB4jDef3x6WhLdVE2LFw"; + + var outcome = PasswordWorker.ComputeInline(JobFor(account, "not-the-password")); + + Assert.False(outcome.Verified); + Assert.Null(outcome.Hash); + } + + [Fact] + public void AppliesAWrite() + { + var account = CreateAccount("offloop-apply-user"); + var upgraded = Argon2PasswordProtection.Instance.EncryptPassword(Password); + + account.ApplyPasswordWrite(upgraded, PasswordProtectionAlgorithm.Argon2); + + Assert.Equal(upgraded, account.Password); + Assert.True(account.CheckPassword(Password)); + } + + /// + /// Writes apply in dispatch order, which is what makes a guard unnecessary: dispatch is on the + /// loop, one worker drains FIFO, and results return through the loop context in that same order. + /// A second worker thread would break this and would need ordering reintroduced. + /// + [Fact] + public void WritesApplyInDispatchOrder() + { + var account = CreateAccount("offloop-two-writes-user"); + var done = 0; + + PumpUntil( + () => + { + for (var i = 1; i <= 2; i++) + { + Assert.True( + PasswordWorker.TryEnqueue( + new PasswordJob + { + Account = account, + HashPhrase = account.GetRehashPhrase($"password-{i}"), + StoredAlgorithm = account.PasswordAlgorithm, + TargetAlgorithm = AccountSecurity.CurrentAlgorithm, + OnComplete = (_, _) => done++ + } + ) + ); + } + }, + () => done >= 2 + ); + + Assert.Equal(2, done); + Assert.True(account.CheckPassword("password-2")); + Assert.False(account.CheckPassword("password-1")); + } + + [Theory] + [InlineData(PasswordProtectionAlgorithm.SHA1)] + [InlineData(PasswordProtectionAlgorithm.SHA2)] + public void UsesTheUsernameSaltedPhraseForShaAccounts(PasswordProtectionAlgorithm algorithm) + { + AccountSecurity.CurrentAlgorithm = algorithm; + var account = CreateAccount($"offloop-phrase-{algorithm}-user"); + + // Verification must use the algorithm the hash was stored under... + Assert.Equal($"{account.Username}{Password}", account.GetVerifyPhrase(Password)); + + // ...and a rehash the one it is moving to. Swapping these is the #2562 lockout. + AccountSecurity.CurrentAlgorithm = PasswordProtectionAlgorithm.Argon2; + Assert.Equal(Password, account.GetRehashPhrase(Password)); + } + + [Fact] + public void UsesTheBarePasswordForArgon2Accounts() + { + var account = CreateAccount("offloop-phrase-argon2-user"); + + Assert.Equal(Password, account.GetVerifyPhrase(Password)); + Assert.Equal(Password, account.GetRehashPhrase(Password)); + } +} diff --git a/Projects/UOContent.Tests/Tests/Accounting/Security/PasswordProtectionTest.cs b/Projects/UOContent.Tests/Tests/Accounting/Security/PasswordProtectionTest.cs index effd2da88..a0ef97a56 100644 --- a/Projects/UOContent.Tests/Tests/Accounting/Security/PasswordProtectionTest.cs +++ b/Projects/UOContent.Tests/Tests/Accounting/Security/PasswordProtectionTest.cs @@ -74,4 +74,89 @@ public class PasswordProtectionTest Assert.False(passwordProtection.ValidatePassword(encryptedPassword, "Not the same password")); } + + /// + /// Literal digests of , so the stored format cannot drift. These are + /// compared as strings against what is already in every account database -- a casing or encoding + /// change would lock out every SHA and MD5 account on the shard at once. + /// + [Theory] + [InlineData("MD5", "52284053181040AC90DBDE74A0E7FF5E")] + [InlineData("SHA1", "9AC635509803AAE2D8312BA1879289259A50C5F0")] + [InlineData( + "SHA2", + "5A727BFF8F8E08A24BDF6B0CD5065F30A1F8E0060B857BB8AFD6955BE0ACBC489DA63F19B8F4CF08D73DE4069CF4B" + + "29D94B353F31513B2FB2D9382EFE15AE975" + )] + public void HashAlgorithm_StoredFormatIsStable(string algorithmType, string expected) + { + var protection = algorithmType switch + { + "SHA1" => HashAlgorithmPasswordProtection.SHA1Instance, + "SHA2" => HashAlgorithmPasswordProtection.SHA2Instance, + _ => HashAlgorithmPasswordProtection.MD5Instance, + }; + + Assert.Equal(expected, protection.EncryptPassword(plainPassword)); + Assert.True(protection.ValidatePassword(expected, plainPassword)); + } + + // The shipping default before this change, as a literal so it cannot drift with the configured + // defaults. Password: "hunter2". + private const string LegacyArgon2iHash = + "$argon2i$v=19$m=8192,t=3,p=1$LD1XJz7P3wQmIJ+Tu6ScgA$NO5hBABsHQ172C5nDO2X4gWnB4jDef3x6WhLdVE2LFw"; + + [Fact] + public void Argon2_ValidatesLegacyArgon2iHash() + { + Assert.True(Argon2PasswordProtection.Instance.ValidatePassword(LegacyArgon2iHash, "hunter2")); + Assert.False(Argon2PasswordProtection.Instance.ValidatePassword(LegacyArgon2iHash, "wrong")); + } + + [Theory] + // type, memory, time, parallelism -> expected NeedsRehash + [InlineData("argon2id", 16384, 1, 1, false)] // current defaults + [InlineData("argon2i", 8192, 3, 1, true)] // the old shipping default + [InlineData("argon2id", 8192, 1, 1, true)] // right type, stale memory + [InlineData("argon2id", 16384, 3, 1, true)] // right type, stale iterations + [InlineData("argon2id", 16384, 1, 2, true)] // right type, stale parallelism + [InlineData("argon2i", 16384, 1, 1, true)] // right cost, stale type + public void Argon2_NeedsRehash_ComparesTypeAndCost( + string type, int memory, int time, int parallelism, bool expected + ) + { + var hash = $"${type}$v=19$m={memory},t={time},p={parallelism}$" + + "LD1XJz7P3wQmIJ+Tu6ScgA$NO5hBABsHQ172C5nDO2X4gWnB4jDef3x6WhLdVE2LFw"; + + Assert.Equal(expected, Argon2PasswordProtection.Instance.NeedsRehash(hash)); + } + + // Digest and salt lengths are decoded base64 sizes rather than parameter-list entries, so they + // need their own literals. Current type and cost throughout; only a length differs. + [Theory] + // 16-byte digest: 22 base64 chars instead of the 43 a 32-byte digest encodes to. + [InlineData("$argon2id$v=19$m=16384,t=1,p=1$LD1XJz7P3wQmIJ+Tu6ScgA$NO5hBABsHQ172C5nDO2X4g")] + // 8-byte salt: 11 base64 chars instead of the 22 a 16-byte salt encodes to. + [InlineData("$argon2id$v=19$m=16384,t=1,p=1$LD1XJz7P3wQ$NO5hBABsHQ172C5nDO2X4gWnB4jDef3x6WhLdVE2LFw")] + public void Argon2_NeedsRehash_ComparesSaltAndDigestLengths(string hash) + { + Assert.True(Argon2PasswordProtection.Instance.NeedsRehash(hash)); + } + + [Theory] + [InlineData("")] + [InlineData("not-a-hash")] + public void Argon2_NeedsRehash_IsTrueForUnparseableHashes(string hash) + { + Assert.True(Argon2PasswordProtection.Instance.NeedsRehash(hash)); + } + + [Fact] + public void NonArgon2Protections_NeverNeedRehash() + { + Assert.False(PBKDF2PasswordProtection.Instance.NeedsRehash("anything")); + Assert.False(HashAlgorithmPasswordProtection.SHA2Instance.NeedsRehash("anything")); + Assert.False(HashAlgorithmPasswordProtection.SHA1Instance.NeedsRehash("anything")); + Assert.False(HashAlgorithmPasswordProtection.MD5Instance.NeedsRehash("anything")); + } } diff --git a/Projects/UOContent.Tests/Tests/Gumps/TestGumps/DynamicTestGump.cs b/Projects/UOContent.Tests/Tests/Gumps/TestGumps/DynamicTestGump.cs index 998e4c537..fd3dafc56 100644 --- a/Projects/UOContent.Tests/Tests/Gumps/TestGumps/DynamicTestGump.cs +++ b/Projects/UOContent.Tests/Tests/Gumps/TestGumps/DynamicTestGump.cs @@ -6,6 +6,8 @@ public class DynamicTestGump : DynamicGump { private readonly string _petName; + public bool HasVisualElementsForTest => HasVisualElements; + public DynamicTestGump(string petName) : base(50, 50) { _petName = petName; diff --git a/Projects/UOContent.Tests/Tests/Gumps/TestGumps/EmptyTestGumps.cs b/Projects/UOContent.Tests/Tests/Gumps/TestGumps/EmptyTestGumps.cs new file mode 100644 index 000000000..b99de1700 --- /dev/null +++ b/Projects/UOContent.Tests/Tests/Gumps/TestGumps/EmptyTestGumps.cs @@ -0,0 +1,40 @@ +using Server.Gumps; + +namespace Server.Tests.Gumps; + +public sealed class EmptyLegacyTestGump : Gump +{ + public bool HasVisualElementsForTest => HasVisualElements; + + public EmptyLegacyTestGump() : base(0, 0) + { + } +} + +public sealed class EmptyDynamicTestGump : DynamicGump +{ + public bool HasVisualElementsForTest => HasVisualElements; + + public EmptyDynamicTestGump() : base(0, 0) + { + } + + protected override void BuildLayout(ref DynamicGumpBuilder builder) + { + builder.AddPage(); + } +} + +public sealed class EmptyStaticTestGump : StaticGump +{ + public bool HasVisualElementsForTest => HasVisualElements; + + public EmptyStaticTestGump() : base(0, 0) + { + } + + protected override void BuildLayout(ref StaticGumpBuilder builder) + { + builder.SetNoClose(); + } +} diff --git a/Projects/UOContent.Tests/Tests/Gumps/TestGumps/LegacyTestGump.cs b/Projects/UOContent.Tests/Tests/Gumps/TestGumps/LegacyTestGump.cs index 668d7e8ae..e45514c38 100644 --- a/Projects/UOContent.Tests/Tests/Gumps/TestGumps/LegacyTestGump.cs +++ b/Projects/UOContent.Tests/Tests/Gumps/TestGumps/LegacyTestGump.cs @@ -4,6 +4,8 @@ namespace Server.Tests.Gumps; public sealed class LegacyTestGump : Gump { + public bool HasVisualElementsForTest => HasVisualElements; + public LegacyTestGump(string petName) : base(50, 50) { Serial = (Serial)0x123; diff --git a/Projects/UOContent.Tests/Tests/Gumps/TestGumps/StaticTestGump.cs b/Projects/UOContent.Tests/Tests/Gumps/TestGumps/StaticTestGump.cs index ea67720f5..66d80c963 100644 --- a/Projects/UOContent.Tests/Tests/Gumps/TestGumps/StaticTestGump.cs +++ b/Projects/UOContent.Tests/Tests/Gumps/TestGumps/StaticTestGump.cs @@ -4,6 +4,8 @@ namespace Server.Tests.Gumps; public class StaticTestGump : StaticGump { + public bool HasVisualElementsForTest => HasVisualElements; + public StaticTestGump() : base(50, 50) { Serial = (Serial)0x123; diff --git a/Projects/UOContent.Tests/Tests/Gumps/TestLayoutGumps.cs b/Projects/UOContent.Tests/Tests/Gumps/TestLayoutGumps.cs index 7cd8e67d1..804408fc7 100644 --- a/Projects/UOContent.Tests/Tests/Gumps/TestLayoutGumps.cs +++ b/Projects/UOContent.Tests/Tests/Gumps/TestLayoutGumps.cs @@ -73,6 +73,32 @@ public class TestLayoutGumps AssertThat.Equal(writer.Span, packet); } + [Fact] + public void TestEmptyGumpsHaveNoVisualElements() + { + Assert.False(Compile(new EmptyLegacyTestGump()).HasVisualElementsForTest); + Assert.False(Compile(new EmptyDynamicTestGump()).HasVisualElementsForTest); + Assert.False(Compile(new EmptyStaticTestGump()).HasVisualElementsForTest); + Assert.False(Compile(new EmptyStaticTestGump()).HasVisualElementsForTest); + } + + [Fact] + public void TestVisibleGumpsHaveVisualElements() + { + Assert.True(Compile(new LegacyTestGump("Test")).HasVisualElementsForTest); + Assert.True(Compile(new DynamicTestGump("Test")).HasVisualElementsForTest); + Assert.True(Compile(new StaticTestGump()).HasVisualElementsForTest); + Assert.True(Compile(new StaticTestGump()).HasVisualElementsForTest); + } + + private static T Compile(T gump) where T : BaseGump + { + var buffer = GC.AllocateUninitializedArray(512); + var writer = new SpanWriter(buffer); + gump.Compile(ref writer); + return gump; + } + private static void InternalTestStaticGump(ReadOnlySpan expectedLayout, StaticGump staticGump, string[] strings) where T : StaticGump { diff --git a/Projects/UOContent.Tests/Tests/Items/TreasureChests/TreasureMapChestLiftTests.cs b/Projects/UOContent.Tests/Tests/Items/TreasureChests/TreasureMapChestLiftTests.cs new file mode 100644 index 000000000..f9b8763e7 --- /dev/null +++ b/Projects/UOContent.Tests/Tests/Items/TreasureChests/TreasureMapChestLiftTests.cs @@ -0,0 +1,111 @@ +using Server; +using Server.Items; +using Server.Mobiles; +using Server.Tests; +using Xunit; + +namespace UOContent.Tests; + +[Collection("Sequential UOContent Tests")] +public class TreasureMapChestLiftTests +{ + // Coordinates chosen to avoid overlap with Tracking (1000-4000, 1000-4000) and + // DetectHidden (1000-2400, 500) test areas. + + [Fact] + public void PartialLift_MarksSplitRemainderAsLifted() + { + using var rng = new PredictableRandom(10); // RandomDouble() = 0.5, no spawn roll fires + var map = Map.Felucca; + var location = new Point3D(5000, 600, 0); + var player = CreatePlayerMobile(map, location); + var chest = new TreasureMapChest(1); + + try + { + chest.MoveToWorld(location, map); + chest.Locked = false; + + var gold = FindGold(chest, null); + Assert.NotNull(gold); + + player.Lift(gold, 1, out var rejected, out _); + Assert.False(rejected); + + // The stack split re-adds the remainder as a brand-new item. It must count as + // already lifted, otherwise every 1-coin pull grants a fresh guardian spawn roll. + var remainder = FindGold(chest, gold); + Assert.NotNull(remainder); + Assert.Contains(remainder, chest.Lifted); + Assert.Contains(gold, chest.Lifted); + } + finally + { + player.Holding?.Delete(); + player.Delete(); + chest.Delete(); + } + } + + [Fact] + public void ItemAddedAfterFill_IsMarkedLifted() + { + using var rng = new PredictableRandom(10); + var chest = new TreasureMapChest(1); + var packed = new Gold(500); + + try + { + // Anything entering the chest after the initial fill (packed-back gold, split + // remainders, GM drops) was never part of the original loot and must not + // grant spawn rolls when lifted back out. + chest.DropItem(packed); + + Assert.Contains(packed, chest.Lifted); + } + finally + { + chest.Delete(); + } + } + + [Fact] + public void OriginalFillLoot_IsNotMarkedLifted() + { + using var rng = new PredictableRandom(10); + var chest = new TreasureMapChest(1); + + try + { + // The original loot must stay roll-eligible for its first lift. + Assert.True(chest.Lifted == null || chest.Lifted.Count == 0); + } + finally + { + chest.Delete(); + } + } + + private static Gold FindGold(TreasureMapChest chest, Gold except) + { + var items = chest.Items; + + for (var i = 0; i < items.Count; i++) + { + if (items[i] is Gold gold && gold != except) + { + return gold; + } + } + + return null; + } + + private static PlayerMobile CreatePlayerMobile(Map map, Point3D location) + { + var mobile = new PlayerMobile(World.NewMobile); + mobile.DefaultMobileInit(); + mobile.MoveToWorld(location, map); + return mobile; + } +} diff --git a/Projects/UOContent.Tests/Tests/Mobiles/AI/MoveSpeedTests.cs b/Projects/UOContent.Tests/Tests/Mobiles/AI/MoveSpeedTests.cs new file mode 100644 index 000000000..7d9243a29 --- /dev/null +++ b/Projects/UOContent.Tests/Tests/Mobiles/AI/MoveSpeedTests.cs @@ -0,0 +1,219 @@ +using System; +using System.Collections.Generic; +using Server; +using Server.Mobiles; +using Xunit; + +namespace UOContent.Tests.Mobiles.AI; + +// Pins the CurrentMoveSpeed classification (verbatim active/passive maps to the matching +// move value; bespoke stays fused), SetSpeed's one-clock guarantee, and the v22 tail. +[Collection("Sequential UOContent Tests")] +public class MoveSpeedTests : IDisposable +{ + // Delete spawned stubs so they don't linger in the shared static World. + private readonly List _created = new(); + + public void Dispose() + { + for (var i = 0; i < _created.Count; i++) + { + _created[i].Delete(); + } + } + + private sealed class SpeedStub : BaseCreature + { + // Stands in for the npc-speeds table (unconfigured in the test fixture). + public double TableActiveMove; + public double TablePassiveMove; + + public SpeedStub() : base(AIType.AI_Animal) => Body = 0xC9; + + public SpeedStub(Serial serial) : base(serial) => Body = 0xC9; + + public override void GetSpeeds(out double activeSpeed, out double passiveSpeed) + { + activeSpeed = 0.3; + passiveSpeed = 0.6; + } + + public override void GetMoveSpeeds(out double activeMoveSpeed, out double passiveMoveSpeed) + { + activeMoveSpeed = TableActiveMove; + passiveMoveSpeed = TablePassiveMove; + } + } + + private SpeedStub NewCreature() + { + var bc = new SpeedStub(); + _created.Add(bc); + return bc; + } + + [Fact] + public void MoveSpeeds_InheritThinkValues_ByDefault() + { + var bc = NewCreature(); + + Assert.Equal(0.3, bc.ActiveMoveSpeed); + Assert.Equal(0.6, bc.PassiveMoveSpeed); + Assert.Equal(bc.CurrentSpeed, bc.CurrentMoveSpeed); + } + + [Fact] + public void CurrentMoveSpeed_ResolvesPerMode_WhenOverridden() + { + var bc = NewCreature(); + bc.SetMoveSpeed(0.45, 0.9); + + // SetSpeed left the creature passive; the think clock is untouched. + Assert.Equal(0.6, bc.CurrentSpeed); + Assert.Equal(0.9, bc.CurrentMoveSpeed); + + bc.SetCurrentSpeedToActive(); + Assert.Equal(0.3, bc.CurrentSpeed); + Assert.Equal(0.45, bc.CurrentMoveSpeed); + } + + [Fact] + public void CurrentMoveSpeed_BespokePace_StaysFused() + { + var bc = NewCreature(); + bc.SetMoveSpeed(0.45, 0.9); + + // Neither think value verbatim, so both clocks run it. + bc.CurrentSpeed = 0.11; + Assert.Equal(0.11, bc.CurrentMoveSpeed); + } + + [Fact] + public void SetSpeed_ClearsMoveOverrides() + { + var bc = NewCreature(); + bc.SetMoveSpeed(0.45, 0.9); + + bc.SetSpeed(0.2, 0.4); + + Assert.Equal(0.2, bc.ActiveMoveSpeed); + Assert.Equal(0.4, bc.PassiveMoveSpeed); + } + + [Fact] + public void NonPositiveMoveSpeed_ClearsThatOverride() + { + var bc = NewCreature(); + bc.SetMoveSpeed(0.45, 0.9); + + bc.ActiveMoveSpeed = 0; + + Assert.Equal(0.3, bc.ActiveMoveSpeed); // inheriting again + Assert.Equal(0.9, bc.PassiveMoveSpeed); // other override untouched + } + + [Fact] + public void ScaleMoveSpeed_ScalesOverrides_LeavesInheritAlone() + { + var bc = NewCreature(); + bc.ActiveMoveSpeed = 0.6; // passive left inheriting + + bc.ScaleMoveSpeed(1.0 / 1.2); + + Assert.Equal(0.5, bc.ActiveMoveSpeed); + Assert.Equal(bc.PassiveSpeed, bc.PassiveMoveSpeed); // still inheriting, not 0 * scalar + } + + [Fact] + public void Herding_DrivesMoveClock_ThinkUntouched() + { + var bc = NewCreature(); // think 0.3/0.6, passive + bc.SetMoveSpeed(0.45, 1.05); + + bc.TargetLocation = new Point2D(10, 10); + + Assert.Equal(0.6, bc.CurrentSpeed); // think clock unaffected by herding + Assert.Equal(0.3, bc.CurrentMoveSpeed); // fixed herding pace, not 1.05 + + bc.TargetLocation = null; + Assert.Equal(1.05, bc.CurrentMoveSpeed); + } + + [Fact] + public void SnapSpeedsToTable_UndoesScalingDrift_KeepsTunedValues() + { + var bc = NewCreature(); + bc.TableActiveMove = 0.45; + bc.TablePassiveMove = 0.9; + bc.SetMoveSpeed(0.45, 0.9); + + // 0.45 and 0.9 do not survive /1.2 then *1.2 bit-exactly. + bc.ScaleMoveSpeed(1.0 / 1.2); + bc.ScaleMoveSpeed(1.2); + Assert.NotEqual(0.45, bc.ActiveMoveSpeed); + + bc.SnapSpeedsToTable(); + Assert.Equal(0.45, bc.ActiveMoveSpeed); + Assert.Equal(0.9, bc.PassiveMoveSpeed); + + // A hand-tuned value is nowhere near the epsilon and must keep. + bc.SetMoveSpeed(0.7, 0.9); + bc.SnapSpeedsToTable(); + Assert.Equal(0.7, bc.ActiveMoveSpeed); + } + + [Fact] + public void Migration_MatchingThinkSpeeds_AdoptTableMoveValues() + { + var bc = NewCreature(); // think 0.3/0.6, matching its table entry + bc.TableActiveMove = 0.45; + bc.TablePassiveMove = 0.9; + + bc.MigrateMoveSpeeds(); + + Assert.Equal(0.45, bc.ActiveMoveSpeed); + Assert.Equal(0.9, bc.PassiveMoveSpeed); + } + + [Fact] + public void Migration_TunedThinkSpeeds_KeepInheriting() + { + var bc = NewCreature(); + bc.SetSpeed(0.35, 0.6); // hand-tuned: no longer matches the table entry + bc.TableActiveMove = 0.45; + bc.TablePassiveMove = 0.9; + + bc.MigrateMoveSpeeds(); + + Assert.Equal(0.35, bc.ActiveMoveSpeed); + Assert.Equal(0.6, bc.PassiveMoveSpeed); + } + + [Theory] + [InlineData(true)] + [InlineData(false)] + public void MoveSpeedOverrides_SurviveSerialization(bool overridden) + { + var bc = NewCreature(); + if (overridden) + { + bc.SetMoveSpeed(0.45, 0.9); + } + + var writer = new BufferWriter(true); + bc.Serialize(writer); + + var buffer = new byte[writer.Position]; + writer.Buffer.AsSpan(0, (int)writer.Position).CopyTo(buffer); + + var copy = new SpeedStub(World.NewMobile); + _created.Add(copy); + var reader = new BufferReader(buffer); + copy.Deserialize(reader); + + // The v22 tail is the last block; exact consumption catches any offset mistake. + Assert.Equal(buffer.Length, reader.Position); + Assert.Equal(overridden ? 0.45 : 0.3, copy.ActiveMoveSpeed); + Assert.Equal(overridden ? 0.9 : 0.6, copy.PassiveMoveSpeed); + } +} diff --git a/Projects/UOContent.Tests/Tests/Network/AutoDenylist/AutoDenylistTests.cs b/Projects/UOContent.Tests/Tests/Network/AutoDenylist/AutoDenylistTests.cs index 3931eb3e4..ae926794d 100644 --- a/Projects/UOContent.Tests/Tests/Network/AutoDenylist/AutoDenylistTests.cs +++ b/Projects/UOContent.Tests/Tests/Network/AutoDenylist/AutoDenylistTests.cs @@ -20,8 +20,8 @@ using Xunit; namespace Server.Tests.Network.AutoDenylists; -// Static store, so every test resets it first. Addresses come from TEST-NET-2 (198.51.100.0/24). -// Sequential: the cap tests reach Sweep, which rents from STArrayPool, which is not thread-safe. +// Static store, so every test resets it first and none may run alongside another. +// Addresses come from TEST-NET-2 (198.51.100.0/24). [Collection("Sequential UOContent Tests")] public class AutoDenylistTests { @@ -62,8 +62,11 @@ public class AutoDenylistTests Assert.Equal(0, AutoDenylist.Count); } + // Not refreshed on purpose: it is what keeps insertion order equal to expiry order, so retiring lapsed + // entries costs the number expiring instead of the number held. A flooder whose hold lapses trips the + // rate limiter on its next attempt -- which runs ahead of the connection filters -- and is held again. [Fact] - public void Repeat_detection_extends_the_hold() + public void Repeat_detection_does_not_extend_the_hold() { Reset(); var ip = IPAddress.Parse("198.51.100.13"); @@ -71,8 +74,72 @@ public class AutoDenylistTests AutoDenylist.Hold(ip, BanReasons.SilentConnect, Now); AutoDenylist.Hold(ip, BanReasons.SilentConnect, Now + DurationMs - 1); - Assert.True(AutoDenylist.IsDenied(ip, Now + DurationMs + 1)); // would have lapsed without the second - Assert.Equal(1, AutoDenylist.Count); // and did not add a duplicate + Assert.Equal(1, AutoDenylist.Count); // no duplicate + Assert.True(AutoDenylist.IsDenied(ip, Now + DurationMs - 1)); + Assert.False(AutoDenylist.IsDenied(ip, Now + DurationMs + 1)); // lapses from the FIRST detection + } + + // The ring carries the expiry and the set carries membership; if they ever disagree, an address is + // either denied forever or retired early. + [Fact] + public void Ring_and_set_stay_in_step() + { + Reset(maxEntries: 4); + + for (var i = 0; i < 8; i++) + { + AutoDenylist.Hold(IPAddress.Parse($"198.51.100.{70 + i}"), BanReasons.InvalidSeed, Now); + } + + Assert.Equal(4, AutoDenylist.Count); + Assert.Equal(AutoDenylist.Count, AutoDenylist.RingCount); + + AutoDenylist.Release(IPAddress.Parse("198.51.100.71")); + Assert.Equal(3, AutoDenylist.Count); + Assert.Equal(AutoDenylist.Count, AutoDenylist.RingCount); + + AutoDenylist.Drain(Now + DurationMs + 1); + Assert.Equal(0, AutoDenylist.Count); + Assert.Equal(0, AutoDenylist.RingCount); + } + + // The ring grows in doublings but is capped at maxEntries, which is not a power of two. Filling exactly + // to it must land on the last slot rather than off the end. + [Fact] + public void Ring_fills_exactly_to_a_non_power_of_two_cap() + { + Reset(maxEntries: 100); + + for (var i = 0; i < 120; i++) + { + AutoDenylist.Hold(IPAddress.Parse($"198.51.100.{i}"), BanReasons.InvalidSeed, Now); + } + + Assert.Equal(100, AutoDenylist.Count); + Assert.Equal(100, AutoDenylist.RingCount); + + // And the whole ring still drains, so no slot was stranded by a wrapped write. + AutoDenylist.Drain(Now + DurationMs + 1); + Assert.Equal(0, AutoDenylist.Count); + Assert.Equal(0, AutoDenylist.RingCount); + } + + // Releasing leaves no ring record behind, so a re-detection is not retired by the old one. + [Fact] + public void Release_then_re_hold_is_not_retired_by_the_stale_record() + { + Reset(); + var ip = IPAddress.Parse("198.51.100.15"); + + AutoDenylist.Hold(ip, BanReasons.RateLimit, Now); + AutoDenylist.Release(ip); + + var later = Now + DurationMs - 1; + AutoDenylist.Hold(ip, BanReasons.RateLimit, later); + + // The first hold's expiry has passed; the second must survive it. + Assert.True(AutoDenylist.IsDenied(ip, Now + DurationMs + 1)); + Assert.Equal(1, AutoDenylist.RingCount); } [Fact] diff --git a/Projects/UOContent.Tests/Tests/Network/BanExemptionsTests.cs b/Projects/UOContent.Tests/Tests/Network/BanExemptionsTests.cs index 7e930fbdf..b38364373 100644 --- a/Projects/UOContent.Tests/Tests/Network/BanExemptionsTests.cs +++ b/Projects/UOContent.Tests/Tests/Network/BanExemptionsTests.cs @@ -28,15 +28,15 @@ public class BanExemptionsTests private static readonly IPAddress _listed = IPAddress.Parse("192.0.2.10"); private static readonly IPAddress _unlisted = IPAddress.Parse("192.0.2.11"); - private static void WithFileAllowlist(string contents) => - FileAllowlist.LoadForTesting(BlocklistSnapshot.Build(Encoding.ASCII.GetBytes(contents), out _, out _)); + private static void WithManualAllowlist(string contents) => + ManualAllowlist.LoadForTesting(BlocklistSnapshot.Build(Encoding.ASCII.GetBytes(contents), out _, out _)); - private static void WithEmptyFileAllowlist() => FileAllowlist.LoadForTesting(BlocklistSnapshot.Empty); + private static void WithEmptyManualAllowlist() => ManualAllowlist.LoadForTesting(BlocklistSnapshot.Empty); [Fact] - public void File_allowlist_exempts_behavioral_contributions() + public void Manual_allowlist_exempts_behavioral_contributions() { - WithFileAllowlist("192.0.2.10"); + WithManualAllowlist("192.0.2.10"); // Subtracting from the blocklist does nothing for behavioural detections, which never consult it. Assert.True(BanExemptions.IsExempt(_listed, BanReasons.ForeignProtocol, NeverCalled)); @@ -46,10 +46,10 @@ public class BanExemptionsTests } [Fact] - public void File_allowlist_covers_cidr_entries() + public void Manual_allowlist_covers_cidr_entries() { // Carve-outs are CIDRs, so a shared-CGNAT player is only covered if ranges work here. - WithFileAllowlist("192.0.2.0/24"); + WithManualAllowlist("192.0.2.0/24"); Assert.True(BanExemptions.IsExempt(_listed, BanReasons.RateLimit, NeverCalled)); Assert.True(BanExemptions.IsExempt(IPAddress.Parse("192.0.2.254"), BanReasons.RateLimit, NeverCalled)); @@ -57,9 +57,9 @@ public class BanExemptionsTests } [Fact] - public void Manual_bans_are_never_exempt_even_when_file_allowlisted() + public void Manual_bans_are_never_exempt_even_when_allowlisted() { - WithFileAllowlist("192.0.2.10"); + WithManualAllowlist("192.0.2.10"); // An explicit decision outranks the operator's own carve-out, and must not cost a strike. Assert.False(BanExemptions.IsExempt(_listed, BanReasons.Manual, NeverCalled)); @@ -68,16 +68,16 @@ public class BanExemptionsTests [Fact] public void Unopted_reasons_are_never_exempt() { - WithFileAllowlist("192.0.2.10"); + WithManualAllowlist("192.0.2.10"); Assert.False(BanExemptions.IsExempt(_listed, BanReasons.Blocklist, NeverCalled)); Assert.False(BanExemptions.IsExempt(_listed, "some-future-reason", NeverCalled)); } [Fact] - public void File_allowlist_does_not_spend_the_earned_lists_strikes() + public void Manual_allowlist_does_not_spend_the_earned_lists_strikes() { - WithFileAllowlist("192.0.2.10"); + WithManualAllowlist("192.0.2.10"); // Unconditional, so the revocable list must not be consulted -- that would burn a strike. Assert.True(BanExemptions.IsExempt(_listed, BanReasons.RateLimit, NeverCalled)); @@ -86,7 +86,7 @@ public class BanExemptionsTests [Fact] public void Falls_through_to_the_login_allowlist_when_not_file_listed() { - WithEmptyFileAllowlist(); + WithEmptyManualAllowlist(); var consulted = 0; @@ -107,7 +107,7 @@ public class BanExemptionsTests [Fact] public void Null_address_is_never_exempt() { - WithEmptyFileAllowlist(); + WithEmptyManualAllowlist(); Assert.False(BanExemptions.IsExempt(null, BanReasons.RateLimit, NeverCalled)); } diff --git a/Projects/UOContent.Tests/Tests/Network/Bans/Blocklist/BlocklistConfigurationTests.cs b/Projects/UOContent.Tests/Tests/Network/Bans/Blocklist/BlocklistConfigurationTests.cs index 6464a1bed..3d81c8d87 100644 --- a/Projects/UOContent.Tests/Tests/Network/Bans/Blocklist/BlocklistConfigurationTests.cs +++ b/Projects/UOContent.Tests/Tests/Network/Bans/Blocklist/BlocklistConfigurationTests.cs @@ -30,6 +30,7 @@ public class BlocklistConfigurationTests { var original = new BlocklistSettings { + Enabled = true, File = "D:/shared/ip-blocklist.txt", ReloadInterval = TimeSpan.FromMinutes(5), ReportHits = false, @@ -39,6 +40,7 @@ public class BlocklistConfigurationTests var json = JsonConfig.Serialize(original); + Assert.Contains("\"enabled\"", json); Assert.Contains("\"file\"", json); Assert.Contains("\"reloadInterval\"", json); Assert.Contains("\"reportHits\"", json); @@ -48,6 +50,7 @@ public class BlocklistConfigurationTests var restored = JsonSerializer.Deserialize(json, JsonConfig.DefaultOptions); Assert.NotNull(restored); + Assert.Equal(original.Enabled, restored.Enabled); Assert.Equal(original.File, restored.File); Assert.Equal(original.ReloadInterval, restored.ReloadInterval); Assert.Equal(original.ReportHits, restored.ReportHits); @@ -55,6 +58,13 @@ public class BlocklistConfigurationTests Assert.Equal(original.PromoteSuppression, restored.PromoteSuppression); } + // The point of the flag: a shard that never opts in must not start the reload poll. + [Fact] + public void Blocklist_is_off_by_default() + { + Assert.False(new BlocklistSettings().Enabled); + } + // The generator (tools/Export-IpBlocklist.ps1) writes to this path by default; if one side moves // without the other, a shard silently enforces nothing. [Fact] diff --git a/Projects/UOContent.Tests/Tests/Network/ManualAllowlist/ManualAllowlistConfigurationTests.cs b/Projects/UOContent.Tests/Tests/Network/ManualAllowlist/ManualAllowlistConfigurationTests.cs new file mode 100644 index 000000000..1146414b5 --- /dev/null +++ b/Projects/UOContent.Tests/Tests/Network/ManualAllowlist/ManualAllowlistConfigurationTests.cs @@ -0,0 +1,66 @@ +/************************************************************************* + * ModernUO * + * Copyright 2019-2026 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: ManualAllowlistConfigurationTests.cs * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 3 of the License, or * + * (at your option) any later version. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + *************************************************************************/ + +using System; +using System.Text.Json; +using Server.Json; +using Server.Network.Bans; +using Xunit; + +namespace Server.Tests.Network.ManualAllowlists; + +public class ManualAllowlistConfigurationTests +{ + // Locks the JsonConfig casing contract: JsonConfig's options are case-SENSITIVE, so every settings + // member must carry an explicit [JsonPropertyName("camelCase")] or it silently binds nothing. + [Fact] + public void ManualAllowlistSettings_RoundTripsThroughJsonConfig() + { + var original = new ManualAllowlistSettings + { + Enabled = true, + Files = ["D:/shared/ip-allowlist*.txt"], + ReloadInterval = TimeSpan.FromMinutes(5) + }; + + var json = JsonConfig.Serialize(original); + + Assert.Contains("\"enabled\"", json); + Assert.Contains("\"files\"", json); + Assert.Contains("\"reloadInterval\"", json); + + var restored = JsonSerializer.Deserialize(json, JsonConfig.DefaultOptions); + + Assert.NotNull(restored); + Assert.Equal(original.Enabled, restored.Enabled); + Assert.Equal(original.Files, restored.Files); + Assert.Equal(original.ReloadInterval, restored.ReloadInterval); + } + + // The point of the flag: a shard that never opts in must not start the reload poll. + [Fact] + public void Manual_allowlist_is_off_by_default() + { + Assert.False(new ManualAllowlistSettings().Enabled); + } + + // The generator creates ip-allowlist.txt beside the blocklist; the wildcard is what picks up a + // carve-out file (-RefreshCarveouts writes ip-allowlist-starlink.txt) with no config edit. + [Fact] + public void Default_pattern_matches_the_generator_output_path() + { + Assert.Equal(["Configuration/ip-allowlist*.txt"], new ManualAllowlistSettings().Files); + } +} diff --git a/Projects/UOContent.Tests/Tests/Network/Packets/AuthIdTests.cs b/Projects/UOContent.Tests/Tests/Network/Packets/AuthIdTests.cs new file mode 100644 index 000000000..456eb174a --- /dev/null +++ b/Projects/UOContent.Tests/Tests/Network/Packets/AuthIdTests.cs @@ -0,0 +1,382 @@ +using System; +using System.Net; +using Server.Accounting; +using Server.Accounting.Security; +using Server.Network; +using Server.Tests.Network; +using Xunit; + +namespace Server.Tests.Network.Packets; + +[Collection("Sequential UOContent Tests")] +public class AuthIdTests : IDisposable +{ + private static readonly IPAddress AddressX = IPAddress.Parse("203.0.113.10"); + private static readonly IPAddress AddressY = IPAddress.Parse("203.0.113.11"); + + private readonly PasswordProtectionAlgorithm _originalAlgorithm = AccountSecurity.CurrentAlgorithm; + + public AuthIdTests() + { + AccountSecurity.CurrentAlgorithm = PasswordProtectionAlgorithm.Argon2; + IncomingAccountPackets.ClearAuthIdWindow(); + } + + public void Dispose() + { + IncomingAccountPackets.ClearAuthIdWindow(); + AccountSecurity.CurrentAlgorithm = _originalAlgorithm; + } + + private static IAccount CreateAccount(string username) => + Accounts.GetAccount(username) ?? new Account(username, "hunter2"); + + private static int Register(IAccount account, IPAddress address) => + IncomingAccountPackets.RegisterAuthId(account, address, new ClientVersion(7, 0, 0, 0)); + + [Fact] + public void VouchesForTheAccountAndAddressItWasIssuedTo() + { + var account = CreateAccount("authid-match-user"); + var authId = Register(account, AddressX); + + var result = IncomingAccountPackets.ConsumeAuthId(authId, account.Username, AddressX, out var entry); + + Assert.Equal(IncomingAccountPackets.AuthIdResult.Vouched, result); + Assert.Same(account, entry.Account); + } + + [Fact] + public void RejectsADifferentAccount() + { + var issued = CreateAccount("authid-owner-user"); + var other = CreateAccount("authid-other-user"); + var authId = Register(issued, AddressX); + + Assert.Equal( + IncomingAccountPackets.AuthIdResult.Rejected, + IncomingAccountPackets.ConsumeAuthId(authId, other.Username, AddressX, out _) + ); + } + + [Fact] + public void RejectsADifferentAddress() + { + var account = CreateAccount("authid-switch-user"); + var authId = Register(account, AddressX); + + Assert.Equal( + IncomingAccountPackets.AuthIdResult.Rejected, + IncomingAccountPackets.ConsumeAuthId(authId, account.Username, AddressY, out _) + ); + } + + [Fact] + public void MatchesTheUsernameCaseInsensitively() + { + var account = CreateAccount("AuthId-Case-User"); + var authId = Register(account, AddressX); + + Assert.Equal( + IncomingAccountPackets.AuthIdResult.Vouched, + IncomingAccountPackets.ConsumeAuthId(authId, "authid-case-user", AddressX, out _) + ); + } + + [Fact] + public void MatchesAnIPv4MappedIPv6Address() + { + var account = CreateAccount("authid-mapped-user"); + var authId = Register(account, AddressX); + + Assert.Equal( + IncomingAccountPackets.AuthIdResult.Vouched, + IncomingAccountPackets.ConsumeAuthId(authId, account.Username, AddressX.MapToIPv6(), out _) + ); + } + + [Fact] + public void RejectsAnUnknownAuthId() + { + var account = CreateAccount("authid-unknown-user"); + var authId = Register(account, AddressX); + + Assert.Equal( + IncomingAccountPackets.AuthIdResult.Rejected, + IncomingAccountPackets.ConsumeAuthId(authId + 1, account.Username, AddressX, out _) + ); + } + + [Fact] + public void IsSingleUseAfterASuccess() + { + var account = CreateAccount("authid-once-user"); + var authId = Register(account, AddressX); + + Assert.Equal( + IncomingAccountPackets.AuthIdResult.Vouched, + IncomingAccountPackets.ConsumeAuthId(authId, account.Username, AddressX, out _) + ); + Assert.Equal( + IncomingAccountPackets.AuthIdResult.Rejected, + IncomingAccountPackets.ConsumeAuthId(authId, account.Username, AddressX, out _) + ); + } + + // A rejected attempt must not consume the id, or anyone landing on a live one could burn it and + // force its owner to log in again. + [Fact] + public void SurvivesAnAttemptFromTheWrongAddress() + { + var account = CreateAccount("authid-not-burned-address-user"); + var authId = Register(account, AddressX); + + Assert.Equal( + IncomingAccountPackets.AuthIdResult.Rejected, + IncomingAccountPackets.ConsumeAuthId(authId, account.Username, AddressY, out _) + ); + + Assert.Equal(1, IncomingAccountPackets.AuthIdWindowCount); + Assert.Equal( + IncomingAccountPackets.AuthIdResult.Vouched, + IncomingAccountPackets.ConsumeAuthId(authId, account.Username, AddressX, out _) + ); + } + + [Fact] + public void SurvivesAnAttemptForTheWrongAccount() + { + var account = CreateAccount("authid-not-burned-account-user"); + var authId = Register(account, AddressX); + + Assert.Equal( + IncomingAccountPackets.AuthIdResult.Rejected, + IncomingAccountPackets.ConsumeAuthId(authId, "not-the-owner", AddressX, out _) + ); + + Assert.Equal(1, IncomingAccountPackets.AuthIdWindowCount); + Assert.Equal( + IncomingAccountPackets.AuthIdResult.Vouched, + IncomingAccountPackets.ConsumeAuthId(authId, account.Username, AddressX, out _) + ); + } + + [Fact] + public void ARejectedAttemptYieldsNoEntry() + { + var account = CreateAccount("authid-no-leak-user"); + var authId = Register(account, AddressX); + + IncomingAccountPackets.ConsumeAuthId(authId, "not-the-owner", AddressX, out var entry); + + Assert.Null(entry.Account); + } + + [Fact] + public void AnExpiredIdIsSpentByItsOwner() + { + var account = CreateAccount("authid-expired-spent-user"); + var authId = Register(account, AddressX); + + var now = Core._now; + + try + { + Core._now = now + TimeSpan.FromMinutes(30.0); + + Assert.Equal( + IncomingAccountPackets.AuthIdResult.Expired, + IncomingAccountPackets.ConsumeAuthId(authId, account.Username, AddressX, out _) + ); + Assert.Equal(0, IncomingAccountPackets.AuthIdWindowCount); + } + finally + { + Core._now = now; + } + } + + // Expiry is not a lockout. The game login always verified the password before any of this + // existed, so falling back to that verify is the behaviour we started from. + [Fact] + public void ExpiresIntoAPasswordVerifyRatherThanARejection() + { + var account = CreateAccount("authid-expired-user"); + var authId = Register(account, AddressX); + + var now = Core._now; + + try + { + Core._now = now + TimeSpan.FromMinutes(30.0); + + Assert.Equal( + IncomingAccountPackets.AuthIdResult.Expired, + IncomingAccountPackets.ConsumeAuthId(authId, account.Username, AddressX, out var entry) + ); + + // Still carries the client version the game login needs. + Assert.Equal(new ClientVersion(7, 0, 0, 0), entry.Version); + } + finally + { + Core._now = now; + } + } + + [Fact] + public void AnExpiredIdFromAnotherAddressIsStillRejected() + { + var account = CreateAccount("authid-expired-elsewhere-user"); + var authId = Register(account, AddressX); + + var now = Core._now; + + try + { + Core._now = now + TimeSpan.FromMinutes(30.0); + + Assert.Equal( + IncomingAccountPackets.AuthIdResult.Rejected, + IncomingAccountPackets.ConsumeAuthId(authId, account.Username, AddressY, out _) + ); + } + finally + { + Core._now = now; + } + } + + private static int Ensure(int existingAuthId, IAccount account, IPAddress address) => + IncomingAccountPackets.EnsureAuthId( + existingAuthId, + account, + address, + new ClientVersion(7, 0, 0, 0) + ); + + [Fact] + public void IssuesAnIdWhenTheConnectionHasNone() + { + var account = CreateAccount("authid-first-select-user"); + + var authId = Ensure(0, account, AddressX); + + Assert.NotEqual(0, authId); + Assert.Equal(1, IncomingAccountPackets.AuthIdWindowCount); + } + + // Handing the same id back rather than minting another is what makes an orphan impossible, + // instead of something to clean up afterwards. + [Fact] + public void ReSelectingReturnsTheSameIdAndAddsNothingToTheWindow() + { + var account = CreateAccount("authid-reselect-user"); + var first = Ensure(0, account, AddressX); + + for (var i = 0; i < 10; i++) + { + Assert.Equal(first, Ensure(first, account, AddressX)); + } + + Assert.Equal(1, IncomingAccountPackets.AuthIdWindowCount); + Assert.Equal( + IncomingAccountPackets.AuthIdResult.Vouched, + IncomingAccountPackets.ConsumeAuthId(first, account.Username, AddressX, out _) + ); + } + + [Fact] + public void AbandonedIdsAreSweptWhenNewOnesAreIssued() + { + var abandoned = CreateAccount("authid-abandoned-user"); + var live = CreateAccount("authid-live-user"); + + var now = Core._now; + + try + { + for (var i = 0; i < 128; i++) + { + Register(abandoned, AddressX); + } + + Assert.Equal(128, IncomingAccountPackets.AuthIdWindowCount); + + Core._now = now + TimeSpan.FromMinutes(30.0); + + var liveId = Register(live, AddressX); + + Assert.Equal(1, IncomingAccountPackets.AuthIdWindowCount); + Assert.Equal( + IncomingAccountPackets.AuthIdResult.Vouched, + IncomingAccountPackets.ConsumeAuthId(liveId, live.Username, AddressX, out _) + ); + } + finally + { + Core._now = now; + } + } + + // A login rush is not a backlog. Every id belongs to a client on its way to redeem it, so none + // may be discarded to hold the window at some arbitrary size. + [Fact] + public void ALoginRushDoesNotEvictAnyonesAuthId() + { + var account = CreateAccount("authid-rush-user"); + var ids = new int[800]; + + for (var i = 0; i < ids.Length; i++) + { + ids[i] = Register(account, AddressX); + } + + Assert.Equal(ids.Length, IncomingAccountPackets.AuthIdWindowCount); + + // Every id issued during the rush is still redeemable, including the first one. + for (var i = 0; i < ids.Length; i++) + { + Assert.Equal( + IncomingAccountPackets.AuthIdResult.Vouched, + IncomingAccountPackets.ConsumeAuthId(ids[i], account.Username, AddressX, out _) + ); + } + } + + [Fact] + public void PreAuthenticatedGameLogin_SkipsThePasswordCheck() + { + var account = CreateAccount("authid-preauth-user"); + using var ns = PacketTestUtilities.CreateTestNetState(); + + // A wrong password is accepted only because the auth id already vouched for the account. + var e = new GameServer.GameLoginEventArgs(ns, account.Username, "wrong-password", true); + GameServer.GameServerLoginEvent(e); + + Assert.True(e.Accepted); + } + + [Fact] + public void GameLoginWithoutPreAuthentication_StillChecksThePassword() + { + var account = CreateAccount("authid-nopreauth-user"); + using var ns = PacketTestUtilities.CreateTestNetState(); + + var wrong = new GameServer.GameLoginEventArgs(ns, account.Username, "wrong-password", false); + GameServer.GameServerLoginEvent(wrong); + Assert.False(wrong.Accepted); + + var right = new GameServer.GameLoginEventArgs(ns, account.Username, "hunter2", false); + GameServer.GameServerLoginEvent(right); + Assert.True(right.Accepted); + } + + [Fact] + public void GeneratesDistinctAuthIds() + { + var account = CreateAccount("authid-distinct-user"); + + Assert.NotEqual(Register(account, AddressX), Register(account, AddressX)); + } +} diff --git a/Projects/UOContent.Tests/UOContent.Tests.csproj b/Projects/UOContent.Tests/UOContent.Tests.csproj index 712246f72..1c73f6eb4 100644 --- a/Projects/UOContent.Tests/UOContent.Tests.csproj +++ b/Projects/UOContent.Tests/UOContent.Tests.csproj @@ -4,9 +4,9 @@ Debug;Release;Analyze - + - + all runtime; build; native; contentfiles; analyzers; buildtransitive @@ -15,7 +15,6 @@ - diff --git a/Projects/UOContent/Accounting/Account.cs b/Projects/UOContent/Accounting/Account.cs index d79d70b1a..b583585ee 100644 --- a/Projects/UOContent/Accounting/Account.cs +++ b/Projects/UOContent/Accounting/Account.cs @@ -378,28 +378,54 @@ public partial class Account : IAccount, IComparable public void SetPassword(string plainPassword) { - var phrase = _passwordAlgorithm is PasswordProtectionAlgorithm.SHA1 or PasswordProtectionAlgorithm.SHA2 - ? $"{_username}{plainPassword}" - : plainPassword; - - Password = AccountSecurity.CurrentPasswordProtection.EncryptPassword(phrase); PasswordAlgorithm = AccountSecurity.CurrentAlgorithm; + Password = AccountSecurity.CurrentPasswordProtection.EncryptPassword( + AccountSecurity.DerivePhrase(PasswordAlgorithm, _username, plainPassword) + ); + } + + /// The phrase that verifies against the currently stored hash. + internal string GetVerifyPhrase(string plainPassword) => + AccountSecurity.DerivePhrase(_passwordAlgorithm, _username, plainPassword); + + /// The phrase a rehash to the configured algorithm would be derived from. + internal string GetRehashPhrase(string plainPassword) => + AccountSecurity.DerivePhrase(AccountSecurity.CurrentAlgorithm, _username, plainPassword); + + /// + /// Whether a successful login should rewrite the stored hash, because the algorithm changed or + /// its cost parameters moved. + /// + internal bool NeedsPasswordUpgrade() => + _passwordAlgorithm != AccountSecurity.CurrentAlgorithm || + AccountSecurity.CurrentPasswordProtection.NeedsRehash(Password); + + /// + /// Applies a hash derived off the game loop. Distinct from the private UpgradePassword + /// below, which adopts a legacy hash when loading pre-binary XML accounts. + /// + /// Unguarded: dispatch is on the loop, one worker drains FIFO, and results return through the + /// loop context in that order, so last dispatched is last applied. A second worker would need + /// ordering reintroduced here. + /// + internal void ApplyPasswordWrite(string newEncrypted, PasswordProtectionAlgorithm algorithm) + { + PasswordAlgorithm = algorithm; + Password = newEncrypted; } public bool CheckPassword(string plainPassword) { - var phrase = _passwordAlgorithm is PasswordProtectionAlgorithm.SHA1 or PasswordProtectionAlgorithm.SHA2 - ? $"{_username}{plainPassword}" - : plainPassword; + var ok = AccountSecurity.GetPasswordProtection(_passwordAlgorithm) + .ValidatePassword(Password, GetVerifyPhrase(plainPassword)); - var ok = AccountSecurity.GetPasswordProtection(_passwordAlgorithm).ValidatePassword(Password, phrase); if (!ok) { return false; } // Upgrade the password protection in case we change the algorithm - if (_passwordAlgorithm != AccountSecurity.CurrentAlgorithm) + if (NeedsPasswordUpgrade()) { SetPassword(plainPassword); } diff --git a/Projects/UOContent/Accounting/AccountHandler.cs b/Projects/UOContent/Accounting/AccountHandler.cs index ea0c4b9e0..eea0dc069 100644 --- a/Projects/UOContent/Accounting/AccountHandler.cs +++ b/Projects/UOContent/Accounting/AccountHandler.cs @@ -5,6 +5,7 @@ using System.Net; using System.Runtime.CompilerServices; using ModernUO.CodeGeneratedEvents; using Server.Accounting; +using Server.Accounting.Security; using Server.Engines.CharacterCreation; using Server.Engines.Help; using Server.Logging; @@ -69,6 +70,9 @@ public static class AccountHandler public static void Initialize() { EventSink.AccountLogin += EventSink_AccountLogin; + + EventSink.Shutdown += PasswordWorker.Stop; + EventSink.ServerCrashed += PasswordWorker.OnCrashed; } [Usage("Password ")] @@ -139,8 +143,12 @@ public static class AccountHandler if (accessList[0].MatchClassC(ipAddress)) { - acct.SetPassword(pass); - from.SendMessage("The password to your account has changed."); + // Confirmed from the callback: off-loop the write has not landed yet here. + PasswordWorker.SetPassword( + acct, + pass, + _ => from.SendMessage("The password to your account has changed.") + ); } else { @@ -307,25 +315,129 @@ public static class AccountHandler logger.Information("Login: {NetState} Access denied for '{Username}'", e.State, un); e.RejectReason = LockdownLevel > AccessLevel.Player ? ALRReason.BadComm : ALRReason.BadPass; } - else if (!acct.CheckPassword(pw)) + else { - logger.Information("Login: {NetState} Invalid password for '{Username}'", e.State, un); - e.RejectReason = ALRReason.BadPass; + HandlePasswordCheck(e, acct, pw); } - else if (acct.Banned) + } + + /// + /// Separate from the caller's else-if chain because two outcomes are not verdicts: the off-loop + /// path has none yet, and a full queue must reject rather than fall through and verify. + /// + private static void HandlePasswordCheck(AccountLoginEventArgs e, Account acct, string pw) + { + switch (DispatchPasswordCheck(e, acct, pw)) { - logger.Information("Login: {NetState} Banned account '{Username}'", e.State, un); + case PasswordCheckDispatch.Deferred: + { + e.Deferred = true; + return; + } + case PasswordCheckDispatch.Saturated: + { + // Reject rather than verify inline: steering work back onto the loop is what a + // flood wants. + logger.Warning( + "Login: {NetState} Password verification queue full, rejecting '{Username}'", + e.State, + acct.Username + ); + + e.RejectReason = ALRReason.BadComm; + return; + } + } + + if (!acct.CheckPassword(pw)) + { + logger.Information("Login: {NetState} Invalid password for '{Username}'", e.State, acct.Username); + e.RejectReason = ALRReason.BadPass; + return; + } + + ApplyVerifiedLogin(e, acct); + } + + /// Everything after the password is known good, shared so an off-loop verdict lands + /// in the same state as an inline one. + private static void ApplyVerifiedLogin(AccountLoginEventArgs e, Account acct) + { + if (acct.Banned) + { + logger.Information("Login: {NetState} Banned account '{Username}'", e.State, acct.Username); e.RejectReason = ALRReason.Blocked; + return; + } + + logger.Information("Login: {NetState} Valid credentials for '{Username}'", e.State, acct.Username); + e.State.Account = acct; + e.Accepted = true; + + acct.LogAccess(e.State); + LoginAllowlist.RecordLogin(e.State?.Address); + } + + private enum PasswordCheckDispatch + { + /// Verify on the loop. + Inline, + + /// Handed to the worker; no verdict yet. + Deferred, + + /// The queue is full. + Saturated + } + + /// + /// Hands the password check to the worker, whatever algorithm it uses. Every protection is safe + /// off the loop, so there is no carve-out, and a cheap digest does not need one either: + /// AccountSecurity.Configure refuses anything below SHA2 as the configured algorithm, so + /// MD5 and SHA1 only appear as a stored hash awaiting migration. That makes + /// NeedsPasswordUpgrade true, and the upgrade hash dominates the job. + /// + private static PasswordCheckDispatch DispatchPasswordCheck(AccountLoginEventArgs e, Account acct, string pw) + { + if (!PasswordWorker.Enabled) + { + return PasswordCheckDispatch.Inline; + } + + var job = new PasswordJob + { + Account = acct, + State = e.State, + StoredHash = acct.Password, + StoredAlgorithm = acct.PasswordAlgorithm, + VerifyPhrase = acct.GetVerifyPhrase(pw), + HashPhrase = acct.NeedsPasswordUpgrade() ? acct.GetRehashPhrase(pw) : null, + TargetAlgorithm = AccountSecurity.CurrentAlgorithm, + OnComplete = static (j, outcome) => + CompleteDeferredAccountLogin(j.State, j.Account, outcome.Verified) + }; + + return PasswordWorker.TryEnqueue(job) + ? PasswordCheckDispatch.Deferred + : PasswordCheckDispatch.Saturated; + } + + /// Resumes a login whose password check ran on the verification thread. + internal static void CompleteDeferredAccountLogin(NetState state, Account acct, bool verified) + { + var e = new AccountLoginEventArgs(state, acct.Username, null); + + if (verified) + { + ApplyVerifiedLogin(e, acct); } else { - logger.Information("Login: {NetState} Valid credentials for '{Username}'", e.State, un); - e.State.Account = acct; - e.Accepted = true; - - acct.LogAccess(e.State); - LoginAllowlist.RecordLogin(e.State?.Address); + logger.Information("Login: {NetState} Invalid password for '{Username}'", state, acct.Username); + e.RejectReason = ALRReason.BadPass; } + + IncomingAccountPackets.CompleteAccountLogin(state, e.Accepted, e.RejectReason); } [OnEvent(nameof(GameServer.GameServerLoginEvent))] @@ -343,7 +455,9 @@ public static class AccountHandler logger.Information("Login: {NetState} Access denied for '{Username}'", e.State, un); e.Accepted = false; } - else if (!acct.CheckPassword(pw)) + // The auth id was only issued after the account login packet verified this password, so + // re-deriving the hash costs a second Argon2 verify to answer the same question. + else if (!e.PreAuthenticated && !acct.CheckPassword(pw)) { logger.Information("Login: {NetState} Invalid password for '{Username}'", e.State, un); e.Accepted = false; diff --git a/Projects/UOContent/Accounting/IPasswordProtection.cs b/Projects/UOContent/Accounting/IPasswordProtection.cs index 0fdd6ced9..f5590a148 100644 --- a/Projects/UOContent/Accounting/IPasswordProtection.cs +++ b/Projects/UOContent/Accounting/IPasswordProtection.cs @@ -4,5 +4,12 @@ namespace Server.Accounting { string EncryptPassword(string plainPassword); bool ValidatePassword(string encryptedPassword, string plainPassword); + + /// + /// True when was produced with parameters that differ + /// from the ones this protection currently uses, so a successful login should rewrite it. + /// Algorithms whose cost is not embedded in the stored value never need this. + /// + bool NeedsRehash(string encryptedPassword) => false; } } diff --git a/Projects/UOContent/Accounting/Security/AccountSecurity.cs b/Projects/UOContent/Accounting/Security/AccountSecurity.cs index 9f7faeafc..deae4c8e0 100644 --- a/Projects/UOContent/Accounting/Security/AccountSecurity.cs +++ b/Projects/UOContent/Accounting/Security/AccountSecurity.cs @@ -51,6 +51,17 @@ public static class AccountSecurity } } + /// + /// The string actually fed to the KDF. SHA1 and SHA2 salt by username; everything else hashes + /// the password alone. Verification must derive with the algorithm the stored hash was made + /// with, and a rehash with the one it is moving to -- deriving with the wrong one produces a + /// hash that verifies once and never again. + /// + public static string DerivePhrase(PasswordProtectionAlgorithm algorithm, string username, string plainPassword) + => algorithm is PasswordProtectionAlgorithm.SHA1 or PasswordProtectionAlgorithm.SHA2 + ? $"{username}{plainPassword}" + : plainPassword; + public static IPasswordProtection GetPasswordProtection(PasswordProtectionAlgorithm algorithm) { var passwordProtection = algorithm switch diff --git a/Projects/UOContent/Accounting/Security/Argon2PasswordProtection.cs b/Projects/UOContent/Accounting/Security/Argon2PasswordProtection.cs index 99f320a78..0a952117d 100644 --- a/Projects/UOContent/Accounting/Security/Argon2PasswordProtection.cs +++ b/Projects/UOContent/Accounting/Security/Argon2PasswordProtection.cs @@ -21,11 +21,37 @@ public class Argon2PasswordProtection : IPasswordProtection { public static IPasswordProtection Instance = new Argon2PasswordProtection(); - private readonly Argon2PasswordHasher m_PasswordHasher = new(rng: RandomNumberGenerator.Create()); + // 16 MiB at t=1 is cheaper than 8 MiB at t=3 (8.5 ms vs 10.1 ms) and twice as memory-hard, which + // is what resists GPU and ASIC cracking. p=1: native argon2 spawns a thread per lane. + private readonly Argon2PasswordHasher _passwordHasher = new( + time: 1, + memory: 16384, + parallel: 1, + type: Argon2Type.Argon2id, + rng: RandomNumberGenerator.Create() + ); public string EncryptPassword(string plainPassword) => - m_PasswordHasher.Hash(plainPassword); + _passwordHasher.Hash(plainPassword); public bool ValidatePassword(string encryptedPassword, string plainPassword) => - m_PasswordHasher.Verify(encryptedPassword, plainPassword); + _passwordHasher.Verify(encryptedPassword, plainPassword); + + // Verification uses the parameters embedded in the PHC string, not the configured ones, so + // comparing them is what lets a parameter change reach existing accounts. + public bool NeedsRehash(string encryptedPassword) + { + // Unparseable but verified: a format this build does not understand, so rewrite it. + if (!Argon2PasswordHasher.TryExtractMetadataValues(encryptedPassword, out var values)) + { + return true; + } + + return values.ArgonType != _passwordHasher.ArgonType + || values.MemoryCost != _passwordHasher.MemoryCost + || values.TimeCost != _passwordHasher.TimeCost + || values.Parallelism != _passwordHasher.Parallelism + || values.HashLength != (int)_passwordHasher.HashLength + || values.SaltLength != (int)_passwordHasher.SaltLength; + } } diff --git a/Projects/UOContent/Accounting/Security/HashAlgorithmPasswordProtection.cs b/Projects/UOContent/Accounting/Security/HashAlgorithmPasswordProtection.cs index 6d6e579cd..63d0ac7a8 100644 --- a/Projects/UOContent/Accounting/Security/HashAlgorithmPasswordProtection.cs +++ b/Projects/UOContent/Accounting/Security/HashAlgorithmPasswordProtection.cs @@ -19,19 +19,47 @@ using Server.Text; namespace Server.Accounting.Security; +/// +/// The obsolete unsalted digests, kept only so imported accounts can log in once and be upgraded. +/// +/// Hashing goes through the one-shot static APIs rather than a retained . +/// A instance carries the running digest across HashCore/HashFinal, so +/// two threads sharing one corrupt each other's result -- and these are process-wide singletons. +/// The static form has no such state, allocates nothing, and produces identical bytes. +/// public class HashAlgorithmPasswordProtection : IPasswordProtection { - public static IPasswordProtection MD5Instance = new HashAlgorithmPasswordProtection(MD5.Create()); - public static IPasswordProtection SHA1Instance = new HashAlgorithmPasswordProtection(SHA1.Create()); - public static IPasswordProtection SHA2Instance = new HashAlgorithmPasswordProtection(SHA512.Create()); - private readonly HashAlgorithm _hashAlgorithm; + private enum Kind + { + MD5, + SHA1, + SHA512 + } - public HashAlgorithmPasswordProtection(HashAlgorithm hashAlgorithm) => _hashAlgorithm = hashAlgorithm; + public static readonly IPasswordProtection MD5Instance = new HashAlgorithmPasswordProtection(Kind.MD5); + public static readonly IPasswordProtection SHA1Instance = new HashAlgorithmPasswordProtection(Kind.SHA1); + public static readonly IPasswordProtection SHA2Instance = new HashAlgorithmPasswordProtection(Kind.SHA512); + + private const int MaxDigestLength = 64; // SHA512, the largest of the three. + + private readonly Kind _kind; + + private HashAlgorithmPasswordProtection(Kind kind) => _kind = kind; public string EncryptPassword(string plainPassword) { var bytes = plainPassword.AsSpan(0, Math.Min(256, plainPassword.Length)).GetBytesAscii(); - return _hashAlgorithm.ComputeHash(bytes).ToHexString(); + + Span digest = stackalloc byte[MaxDigestLength]; + + var written = _kind switch + { + Kind.MD5 => MD5.HashData(bytes, digest), + Kind.SHA1 => SHA1.HashData(bytes, digest), + _ => SHA512.HashData(bytes, digest) + }; + + return digest[..written].ToHexString(); } public bool ValidatePassword(string encryptedPassword, string plainPassword) => diff --git a/Projects/UOContent/Accounting/Security/PBKDF2PasswordProtection.cs b/Projects/UOContent/Accounting/Security/PBKDF2PasswordProtection.cs index 7ae464021..96706c030 100644 --- a/Projects/UOContent/Accounting/Security/PBKDF2PasswordProtection.cs +++ b/Projects/UOContent/Accounting/Security/PBKDF2PasswordProtection.cs @@ -22,23 +22,24 @@ namespace Server.Accounting.Security; public class PBKDF2PasswordProtection : IPasswordProtection { - private const ushort m_MinIterations = 1024; - private const ushort m_MaxIterations = 1536; - private const int m_SaltSize = 8; - private const int m_HashSize = 32; - private const int m_OutputSize = 2 + m_SaltSize + m_HashSize; + private const ushort MinIterations = 1024; + private const ushort MaxIterations = 1536; + private const int SaltSize = 8; + private const int HashSize = 32; + private const int OutputSize = 2 + SaltSize + HashSize; public static readonly IPasswordProtection Instance = new PBKDF2PasswordProtection(); public string EncryptPassword(string plainPassword) { - Span output = stackalloc byte[m_OutputSize]; - var iterations = Utility.RandomMinMax(m_MinIterations, m_MaxIterations); + Span output = stackalloc byte[OutputSize]; + + var iterations = RandomNumberGenerator.GetInt32(MinIterations, MaxIterations + 1); BinaryPrimitives.WriteUInt16LittleEndian(output[..2], (ushort)iterations); - var salt = output.Slice(2, m_SaltSize); + var salt = output.Slice(2, SaltSize); RandomNumberGenerator.Fill(salt); - var hash = output.Slice(2 + m_SaltSize, m_HashSize); + var hash = output.Slice(2 + SaltSize, HashSize); Rfc2898DeriveBytes.Pbkdf2(plainPassword, salt, hash, iterations, HashAlgorithmName.SHA256); return output.ToHexString(); @@ -46,15 +47,15 @@ public class PBKDF2PasswordProtection : IPasswordProtection public bool ValidatePassword(string encryptedPassword, string plainPassword) { - Span encryptedBytes = stackalloc byte[m_OutputSize]; + Span encryptedBytes = stackalloc byte[OutputSize]; encryptedPassword.GetBytes(encryptedBytes); var iterations = BinaryPrimitives.ReadUInt16LittleEndian(encryptedBytes[..2]); - var salt = encryptedBytes.Slice(2, m_SaltSize); + var salt = encryptedBytes.Slice(2, SaltSize); - Span hash = stackalloc byte[m_HashSize]; + Span hash = stackalloc byte[HashSize]; Rfc2898DeriveBytes.Pbkdf2(plainPassword, salt, hash, iterations, HashAlgorithmName.SHA256); - return hash.SequenceEqual(encryptedBytes[(m_SaltSize + 2)..]); + return hash.SequenceEqual(encryptedBytes[(SaltSize + 2)..]); } } diff --git a/Projects/UOContent/Accounting/Security/PasswordWorker.cs b/Projects/UOContent/Accounting/Security/PasswordWorker.cs new file mode 100644 index 000000000..83e9207f1 --- /dev/null +++ b/Projects/UOContent/Accounting/Security/PasswordWorker.cs @@ -0,0 +1,293 @@ +/************************************************************************* + * ModernUO * + * Copyright 2019-2026 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: PasswordWorker.cs * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 3 of the License, or * + * (at your option) any later version. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + *************************************************************************/ + +using System; +using System.Collections.Concurrent; +using System.Threading; +using Server.Logging; +using Server.Network; + +namespace Server.Accounting.Security; + +/// +/// Work handed to the password thread, which reads no game state and writes none. +/// +/// Verify and hash are independently optional: a login verifies and may rehash, an explicit change +/// only hashes. +/// +internal sealed class PasswordJob +{ + public Account Account; + + /// Ties the job to a connection. Null when the work is not gated on one, such as a + /// password change by an admin. + public NetState State; + + /// Hash to verify against, with . + public string StoredHash; + + /// Algorithm was written with. Both algorithms are resolved on + /// the loop; AccountSecurity.CurrentAlgorithm is mutable state the worker must not read. + public PasswordProtectionAlgorithm StoredAlgorithm; + + /// Phrase to verify, or null to skip verification. + public string VerifyPhrase; + + /// Phrase to hash, or null when nothing needs writing. + public string HashPhrase; + + public PasswordProtectionAlgorithm TargetAlgorithm; + + /// Runs on the game loop with the result. Free to touch game state. + public Action OnComplete; +} + +internal readonly struct PasswordOutcome +{ + /// True when no verification was asked for, or it succeeded. + public readonly bool Verified; + + /// The derived hash, or null when nothing was hashed or verification failed. + public readonly string Hash; + + public PasswordOutcome(bool verified, string hash) + { + Verified = verified; + Hash = hash; + } +} + +/// +/// Runs password hashing off the game loop. An Argon2 verify costs ~8.9 ms of frozen world per +/// login attempt, successful or not. +/// +/// Exactly one worker, and that is load-bearing three times over. It cannot cost the loop more than +/// an inline verify under any scheduling regime, because at worst it takes an equal share of one +/// core -- which is what lets the measurement hold on hardware we cannot inspect. It caps live +/// hashing arenas at one. And writes apply in dispatch order only because a single thread drains +/// FIFO, so a second would need ordering reintroduced. +/// +/// ~110 verifies/sec, which is ample: only loop time matters, not login latency. +/// +internal sealed class PasswordWorker +{ + private static readonly ILogger logger = LogFactory.GetLogger(typeof(PasswordWorker)); + + /// + /// Backstop, not a flood defense. SentFirstPacket holds a connection to one pending + /// verify and the engine caps connections at 4096 (NetState.Network.cs), so this matches + /// that bound and can only trip if that invariant breaks. A cap low enough to blunt an attack + /// would reject real players first; flood defense belongs at the connection layer. + /// + private const int MaxPending = 4096; + + // Nothing signals the worker when a save freeze ends, so it re-checks on this interval -- but + // only while a save is in progress, never in steady state. + private const int SaveGatePollMs = 50; + + private static PasswordWorker _instance; + + // Needs a spare core to move work to, which a 1-2 core host does not have. Off in DEBUG, where + // logins are rare and the inline path is easier to follow. + internal static readonly bool Enabled = +#if DEBUG + false; +#else + Environment.ProcessorCount >= 4; +#endif + + private readonly Thread _thread; + private readonly AutoResetEvent _work = new(false); + private readonly ConcurrentQueue _queue = []; + + private int _pending; + private volatile bool _exit; + + private PasswordWorker() + { + _thread = new Thread(Execute) + { + IsBackground = true, + Name = "Password Worker" + }; + + _thread.Start(); + } + + private static PasswordWorker Instance => _instance ??= new PasswordWorker(); + + /// Queues a job. False when full, and the caller must then reject without verifying. + internal static bool TryEnqueue(PasswordJob job) => Instance.TryEnqueueCore(job); + + private bool TryEnqueueCore(PasswordJob job) + { + if (Volatile.Read(ref _pending) >= MaxPending) + { + return false; + } + + Interlocked.Increment(ref _pending); + _queue.Enqueue(job); + _work.Set(); + + return true; + } + + /// + /// Checked before each job, which bounds a save overlap to whichever hash was already running: + /// the freeze holds the loop, so nothing new can be queued during it. PendingSave counts too -- + /// the serialization threads are already awake and spinning on an empty queue by then. + /// + private static bool CanRunNow() => World.WorldState is WorldState.Running or WorldState.WritingSave; + + private void Execute() + { + while (!_exit) + { + if (_queue.IsEmpty) + { + // A kernel block at zero CPU. Set() during a hash leaves the event signalled, so a + // wake arriving mid-job is not lost. + _work.WaitOne(); + continue; + } + + if (!CanRunNow()) + { + _work.WaitOne(SaveGatePollMs); + continue; + } + + if (!_queue.TryDequeue(out var job)) + { + continue; + } + + Interlocked.Decrement(ref _pending); + + // Gone while it waited: skip it rather than hash for a verdict nobody receives. Running + // only goes true -> false, so a stale read wastes a hash but never skips a live one. A + // null State is a job with no connection to lose, and still runs. + if (job.State?.Running == false) + { + continue; + } + + PasswordOutcome outcome; + + try + { + outcome = Compute(job); + } + catch (Exception ex) + { + // A verdict must still come back, or the connection never gets a reply. + logger.Error(ex, "Password work failed for {Username}", job.Account?.Username); + outcome = new PasswordOutcome(false, null); + } + + Core.LoopContext.Post(() => Apply(job, outcome)); + } + } + + private static PasswordOutcome Compute(PasswordJob job) + { + if (job.VerifyPhrase != null && + !AccountSecurity.GetPasswordProtection(job.StoredAlgorithm) + .ValidatePassword(job.StoredHash, job.VerifyPhrase)) + { + return new PasswordOutcome(false, null); + } + + return new PasswordOutcome( + true, + job.HashPhrase == null + ? null : AccountSecurity.GetPasswordProtection(job.TargetAlgorithm).EncryptPassword(job.HashPhrase) + ); + } + + private static void Apply(PasswordJob job, PasswordOutcome outcome) + { + // Re-checked: a connection can drop while the result sits in the loop queue. + if (job.State?.Running == false) + { + return; + } + + if (outcome.Verified && outcome.Hash != null) + { + job.Account.ApplyPasswordWrite(outcome.Hash, job.TargetAlgorithm); + } + + job.OnComplete?.Invoke(job, outcome); + } + + /// + /// Sets a password, off the loop where available and inline otherwise, invoking + /// on the loop either way. + /// + /// Confirm from , not the call site: off-loop the write has not + /// happened when this returns. + /// + internal static void SetPassword(Account account, string plainPassword, Action onDone) + { + if (!Enabled) + { + account.SetPassword(plainPassword); + onDone?.Invoke(true); + return; + } + + var job = new PasswordJob + { + Account = account, + HashPhrase = account.GetRehashPhrase(plainPassword), + TargetAlgorithm = AccountSecurity.CurrentAlgorithm, + OnComplete = (_, outcome) => onDone?.Invoke(outcome.Hash != null) + }; + + if (!TryEnqueue(job)) + { + // Saturated. Unlike a login, a password change must not be dropped, so it pays the + // hash on the loop instead. + account.SetPassword(plainPassword); + onDone?.Invoke(true); + } + } + + /// Runs a job on the calling thread. The seam the tests drive. + internal static PasswordOutcome ComputeInline(PasswordJob job) => Compute(job); + + /// + /// Stops the worker on shutdown or crash. Pending jobs are dropped rather than finished: + /// nothing saves the world after this point, so a write applied here would reach no disk. + /// + /// Draining the loop context is not this type's business either. That belongs in the core + /// shutdown path, before subscriber events run -- a subscriber pumping the shared context would + /// execute other subscribers' work at an arbitrary point in the event order. + /// + internal static void Stop() => _instance?.StopThread(); + + // HandleClosed skips InvokeShutdown when the server crashed, so the crash path needs its own + // subscription. + internal static void OnCrashed(ServerCrashedEventArgs e) => Stop(); + + private void StopThread() + { + _exit = true; + _work.Set(); + _thread.Join(TimeSpan.FromSeconds(5)); + } +} diff --git a/Projects/UOContent/Commands/LoopStats.cs b/Projects/UOContent/Commands/LoopStats.cs new file mode 100644 index 000000000..012de68d8 --- /dev/null +++ b/Projects/UOContent/Commands/LoopStats.cs @@ -0,0 +1,117 @@ +#if EVENT_LOOP_PROFILING +using System; +using System.Globalization; +using System.IO; +using Server.Logging; + +namespace Server.Commands; + +/// +/// Reports the event-loop time decomposition recorded by . +/// Only compiled when the server is built with -p:EventLoopProfiling=true. +/// See dev-docs/debugging-event-loop.md for how to read the output. +/// +public static class LoopStats +{ + private static readonly ILogger logger = LogFactory.GetLogger(typeof(LoopStats)); + + public static void Configure() + { + CommandSystem.Register("LoopStats", AccessLevel.Administrator, LoopStats_OnCommand); + } + + [Usage("LoopStats")] + [Description("Summarizes the last minute of event-loop time accounting and writes the full history to a CSV.")] + private static void LoopStats_OnCommand(CommandEventArgs e) + { + var history = EventLoopProfiler.History(); + if (history.Length == 0) + { + e.Mobile.SendMessage("No samples recorded yet."); + return; + } + + var window = Math.Min(60, history.Length); + + double wall = 0, sleep = 0, gc = 0, stolen = 0, stolenMax = 0; + long iterations = 0, sleeps = 0, lateWakes = 0, wheelLagMax = 0; + Span phases = stackalloc double[EventLoopProfiler.PhaseCount]; + Span phaseMax = stackalloc double[EventLoopProfiler.PhaseCount]; + + for (var i = history.Length - window; i < history.Length; i++) + { + ref var s = ref history[i]; + wall += s.WallMs; + sleep += s.SleepMs; + gc += s.GcPauseMs; + stolen += s.StolenMs; + iterations += s.Iterations; + sleeps += s.Sleeps; + lateWakes += s.LateWakes; + + if (s.StolenMs > stolenMax) + { + stolenMax = s.StolenMs; + } + + if (s.WheelLagMaxMs > wheelLagMax) + { + wheelLagMax = s.WheelLagMaxMs; + } + + for (var p = 0; p < EventLoopProfiler.PhaseCount; p++) + { + phases[p] += s.Phases[p]; + if (s.Phases[p] > phaseMax[p]) + { + phaseMax[p] = s.Phases[p]; + } + } + } + + e.Mobile.SendMessage($"Loop, last {window}s of wall time {wall:F0}ms:"); + e.Mobile.SendMessage($" sleep {100 * sleep / wall:F1}%, gc {100 * gc / wall:F1}%, stolen {100 * stolen / wall:F1}% (worst {stolenMax:F0}ms/s)"); + + for (var p = 0; p < EventLoopProfiler.PhaseCount; p++) + { + e.Mobile.SendMessage($" {(LoopPhase)p}: {100 * phases[p] / wall:F1}% (worst {phaseMax[p]:F0}ms/s)"); + } + + e.Mobile.SendMessage($" {iterations} iterations, {sleeps} sleeps, {lateWakes} late wakes, worst wheel lag {wheelLagMax}ms"); + + var path = Path.Combine(Core.BaseDirectory, $"loopstats-{Core.Now:yyyyMMdd-HHmmss}.csv"); + WriteCsv(path, history); + e.Mobile.SendMessage($"Full history ({history.Length} samples) written to {path}"); + logger.Information("Loop stats dumped to {Path}", path); + } + + private static void WriteCsv(string path, EventLoopProfiler.Sample[] history) + { + using var writer = new StreamWriter(path); + writer.Write("wallStart,wallMs,iterations,sleeps,sleepMs,sleepOvershootMaxMs,lateWakes,wheelLagMaxMs,wakesIssued,wakesElided,gcPauseMs,gen0,gen1,gen2,stolenMs"); + for (var p = 0; p < EventLoopProfiler.PhaseCount; p++) + { + writer.Write(','); + writer.Write((LoopPhase)p); + } + + writer.WriteLine(); + + for (var i = 0; i < history.Length; i++) + { + ref var s = ref history[i]; + writer.Write(string.Create( + CultureInfo.InvariantCulture, + $"{s.WallStart},{s.WallMs},{s.Iterations},{s.Sleeps},{s.SleepMs:F2},{s.SleepOvershootMaxMs:F2},{s.LateWakes},{s.WheelLagMaxMs},{s.WakesIssued},{s.WakesElided},{s.GcPauseMs:F2},{s.Gen0},{s.Gen1},{s.Gen2},{s.StolenMs:F2}" + )); + for (var p = 0; p < EventLoopProfiler.PhaseCount; p++) + { + writer.Write(','); + writer.Write(string.Create(CultureInfo.InvariantCulture, $"{s.Phases[p]:F2}")); + } + + writer.WriteLine(); + } + } +} +#endif diff --git a/Projects/UOContent/Commands/VisibilityList.cs b/Projects/UOContent/Commands/VisibilityList.cs index 3e4a0dc2a..ddad258c2 100644 --- a/Projects/UOContent/Commands/VisibilityList.cs +++ b/Projects/UOContent/Commands/VisibilityList.cs @@ -103,9 +103,8 @@ namespace Server.Commands { var list = pm.VisibilityList; - if (list.Contains(targ)) + if (list.Remove(targ)) { - list.Remove(targ); pm.SendMessage($"{targ.Name} has been removed from your visibility list."); } else diff --git a/Projects/UOContent/Engines/Advanced Search/AdvancedSearchUtilities.cs b/Projects/UOContent/Engines/Advanced Search/AdvancedSearchUtilities.cs index 2c349847d..6775dcaef 100644 --- a/Projects/UOContent/Engines/Advanced Search/AdvancedSearchUtilities.cs +++ b/Projects/UOContent/Engines/Advanced Search/AdvancedSearchUtilities.cs @@ -1,6 +1,5 @@ using System; using System.Buffers; -using System.Collections.Generic; using System.Globalization; using System.Numerics; using System.Runtime.CompilerServices; diff --git a/Projects/UOContent/Engines/Bulk Orders/Books/BOBFilter.cs b/Projects/UOContent/Engines/Bulk Orders/Books/BOBFilter.cs index 962c3d8e3..f63e4f8d4 100644 --- a/Projects/UOContent/Engines/Bulk Orders/Books/BOBFilter.cs +++ b/Projects/UOContent/Engines/Bulk Orders/Books/BOBFilter.cs @@ -6,27 +6,27 @@ namespace Server.Engines.BulkOrders; public partial class BOBFilter { [SerializableField(0)] + [SaveFlag(nameof(ShouldSerializeType))] private int _type; - [SerializableFieldSaveFlag(0)] private bool ShouldSerializeType() => _type != 0; [SerializableField(1)] + [SaveFlag(nameof(ShouldSerializeQuality))] private int _quality; - [SerializableFieldSaveFlag(1)] private bool ShouldSerializeQuality() => _quality != 0; [SerializableField(2)] + [SaveFlag(nameof(ShouldSerializeMaterial))] private int _material; - [SerializableFieldSaveFlag(2)] private bool ShouldSerializeMaterial() => _material != 0; [SerializableField(3)] + [SaveFlag(nameof(ShouldSerializeQuantity))] private int _quantity; - [SerializableFieldSaveFlag(3)] private bool ShouldSerializeQuantity() => _quantity != 0; private void Deserialize(IGenericReader reader, int version) diff --git a/Projects/UOContent/Engines/CannedEvil/ChampionSkullBrazier.cs b/Projects/UOContent/Engines/CannedEvil/ChampionSkullBrazier.cs index 9ff8dc633..2632fd954 100755 --- a/Projects/UOContent/Engines/CannedEvil/ChampionSkullBrazier.cs +++ b/Projects/UOContent/Engines/CannedEvil/ChampionSkullBrazier.cs @@ -33,17 +33,13 @@ public partial class ChampionSkullBrazier : AddonComponent [SerializedCommandProperty(AccessLevel.GameMaster)] private ChampionSkullPlatform _platform; - [SerializableProperty(2)] - [CommandProperty(AccessLevel.GameMaster)] - public Item Skull + [SerializableField(2, fieldChanged: nameof(OnSkullChanged))] + [SerializedCommandProperty(AccessLevel.GameMaster)] + private Item _skull; + + private void OnSkullChanged(Item oldValue, Item newValue) { - get => _skull; - set - { - _skull = value; - this.MarkDirty(); - _platform?.Validate(); - } + _platform?.Validate(); } public override int LabelNumber => 1049489 + (int)_type; diff --git a/Projects/UOContent/Engines/CannedEvil/ChampionSpawn.cs b/Projects/UOContent/Engines/CannedEvil/ChampionSpawn.cs index a26bcc0f9..9daea0ba3 100755 --- a/Projects/UOContent/Engines/CannedEvil/ChampionSpawn.cs +++ b/Projects/UOContent/Engines/CannedEvil/ChampionSpawn.cs @@ -27,16 +27,44 @@ using Server.Logging; namespace Server.Engines.CannedEvil; -[SerializationGenerator(10, false)] +[SerializationGenerator(11, false)] public partial class ChampionSpawn : Item { + private void MigrateFrom(V10Content content) + { + _level = content.Level; + _activatedByProximity = content.ActivatedByProximity; + _nextProximityTime = content.NextProximityTime; + _maxLevel = content.MaxLevel; + _activatedByValor = content.ActivatedByValor; + _damageEntries = content.DamageEntries; + _confinedRoaming = content.ConfinedRoaming; + _idol = content.Idol; + _hasBeenAdvanced = content.HasBeenAdvanced; + _spawnArea = content.SpawnArea; + _randomizeType = content.RandomizeType; + _kills = content.Kills; + _active = content.Active; + _type = content.Type; + _creatures = content.Creatures; + _redSkulls = content.RedSkulls; + _whiteSkulls = content.WhiteSkulls; + _platform = content.Platform; + _altar = content.Altar; + _expireDelay = content.ExpireDelay; + _expireTime = content.ExpireTime; + _champion = content.Champion; + _restartDelay = content.RestartDelay; + _restartTime = content.RestartTime; + } + private static readonly ILogger logger = LogFactory.GetLogger(typeof(ChampionSpawn)); [SerializableField(1)] [SerializedCommandProperty(AccessLevel.GameMaster)] private bool _activatedByProximity; - [DeltaDateTime] + [AnchoredDateTime] [SerializableField(2)] [SerializedCommandProperty(AccessLevel.GameMaster)] private DateTime _nextProximityTime; @@ -96,7 +124,7 @@ public partial class ChampionSpawn : Item [SerializedCommandProperty(AccessLevel.GameMaster)] private TimeSpan _expireDelay; - [DeltaDateTime] + [AnchoredDateTime] [SerializableField(20)] [SerializedCommandProperty(AccessLevel.GameMaster)] private DateTime _expireTime; @@ -109,7 +137,7 @@ public partial class ChampionSpawn : Item [SerializedCommandProperty(AccessLevel.GameMaster)] private TimeSpan _restartDelay; - [DeltaDateTime] + [AnchoredDateTime] [SerializableField(23, setter: "private")] [SerializedCommandProperty(AccessLevel.GameMaster)] private DateTime _restartTime; @@ -203,47 +231,38 @@ public partial class ChampionSpawn : Item } } - [SerializableProperty(3)] - [CommandProperty(AccessLevel.GameMaster, AccessLevel.Administrator)] - public int MaxLevel + [SerializableField(3, allowFieldChange: nameof(AllowMaxLevelChange))] + [SerializedCommandProperty(AccessLevel.GameMaster, AccessLevel.Administrator)] + private int _maxLevel; + + private bool AllowMaxLevelChange(ref int value) { - get => _maxLevel; - set => _maxLevel = Math.Clamp(value, 0, 18); + value = Math.Clamp(value, 0, 18); + return true; } - [SerializableProperty(9)] - [CommandProperty(AccessLevel.GameMaster)] - public Rectangle2D SpawnArea + [SerializableField(9, fieldChanged: nameof(OnSpawnAreaChanged))] + [SerializedCommandProperty(AccessLevel.GameMaster)] + [InvalidateProperties] + private Rectangle2D _spawnArea; + + private void OnSpawnAreaChanged(Rectangle2D oldValue, Rectangle2D newValue) { - get => _spawnArea; - set - { - _spawnArea = value; - this.MarkDirty(); - InvalidateProperties(); - UpdateRegion(); - } + UpdateRegion(); } - [SerializableProperty(11)] - [CommandProperty(AccessLevel.GameMaster)] - public int Kills + [SerializableField(11, fieldChanged: nameof(OnKillsChanged))] + [SerializedCommandProperty(AccessLevel.GameMaster)] + [InvalidateProperties] + private int _kills; + + private void OnKillsChanged(int oldValue, int newValue) { - get => _kills; - set + var n = _kills / (double)MaxKills; + var p = (int)(n * 100); + if (p < 90) { - _kills = value; - this.MarkDirty(); - - var n = _kills / (double)MaxKills; - var p = (int)(n * 100); - - if (p < 90) - { - SetWhiteSkullCount(p / 20); - } - - InvalidateProperties(); + SetWhiteSkullCount(p / 20); } } diff --git a/Projects/UOContent/Engines/CannedEvil/ChampionTitleContext.cs b/Projects/UOContent/Engines/CannedEvil/ChampionTitleContext.cs index bfd00c0bf..644e2b965 100644 --- a/Projects/UOContent/Engines/CannedEvil/ChampionTitleContext.cs +++ b/Projects/UOContent/Engines/CannedEvil/ChampionTitleContext.cs @@ -51,9 +51,9 @@ public partial class ChampionTitleContext } [SerializableField(1)] + [SaveFlag(nameof(ShouldSerializeAbyss))] private ChampionTitle _abyss; - [SerializableFieldSaveFlag(1)] private bool ShouldSerializeAbyss() => _abyss != null; [CommandProperty(AccessLevel.GameMaster)] @@ -71,9 +71,9 @@ public partial class ChampionTitleContext } [SerializableField(2)] + [SaveFlag(nameof(ShouldSerializeArachnid))] private ChampionTitle _arachnid; - [SerializableFieldSaveFlag(2)] private bool ShouldSerializeArachnid() => _arachnid != null; [CommandProperty(AccessLevel.GameMaster)] @@ -91,9 +91,9 @@ public partial class ChampionTitleContext } [SerializableField(3)] + [SaveFlag(nameof(ShouldSerializeColdBlood))] private ChampionTitle _coldBlood; - [SerializableFieldSaveFlag(3)] private bool ShouldSerializeColdBlood() => _coldBlood != null; [CommandProperty(AccessLevel.GameMaster)] @@ -111,9 +111,9 @@ public partial class ChampionTitleContext } [SerializableField(4)] + [SaveFlag(nameof(ShouldSerializeForestLord))] private ChampionTitle _forestLord; - [SerializableFieldSaveFlag(4)] private bool ShouldSerializeForestLord() => _forestLord != null; [CommandProperty(AccessLevel.GameMaster)] @@ -131,9 +131,9 @@ public partial class ChampionTitleContext } [SerializableField(5)] + [SaveFlag(nameof(ShouldSerializeVerminHorde))] private ChampionTitle _verminHorde; - [SerializableFieldSaveFlag(5)] private bool ShouldSerializeVerminHorde() => _verminHorde != null; [CommandProperty(AccessLevel.GameMaster)] @@ -151,9 +151,9 @@ public partial class ChampionTitleContext } [SerializableField(6)] + [SaveFlag(nameof(ShouldSerializeUnholyTerror))] private ChampionTitle _unholyTerror; - [SerializableFieldSaveFlag(6)] private bool ShouldSerializeUnholyTerror() => _unholyTerror != null; [CommandProperty(AccessLevel.GameMaster)] @@ -171,9 +171,9 @@ public partial class ChampionTitleContext } [SerializableField(7)] + [SaveFlag(nameof(ShouldSerializeSleepingDragon))] private ChampionTitle _sleepingDragon; - [SerializableFieldSaveFlag(7)] private bool ShouldSerializeSleepingDragon() => _sleepingDragon != null; [CommandProperty(AccessLevel.GameMaster)] @@ -191,9 +191,9 @@ public partial class ChampionTitleContext } [SerializableField(8)] + [SaveFlag(nameof(ShouldSerializeCorrupt))] private ChampionTitle _corrupt; - [SerializableFieldSaveFlag(8)] private bool ShouldSerializeCorrupt() => _corrupt != null; [CommandProperty(AccessLevel.GameMaster)] @@ -211,9 +211,9 @@ public partial class ChampionTitleContext } [SerializableField(9)] + [SaveFlag(nameof(ShouldSerializeGlade))] private ChampionTitle _glade; - [SerializableFieldSaveFlag(9)] private bool ShouldSerializeGlade() => _glade != null; [CommandProperty(AccessLevel.GameMaster)] diff --git a/Projects/UOContent/Engines/CannedEvil/ChampionTitleSystem.cs b/Projects/UOContent/Engines/CannedEvil/ChampionTitleSystem.cs index d82bfaf15..6160c4595 100644 --- a/Projects/UOContent/Engines/CannedEvil/ChampionTitleSystem.cs +++ b/Projects/UOContent/Engines/CannedEvil/ChampionTitleSystem.cs @@ -167,20 +167,13 @@ public class ChampionTitleSystem : GenericPersistence return; } - using var queue = PooledRefQueue.Create(); - foreach (var context in _championTitleContexts.Values) { if (!context.CheckAtrophy()) { - queue.Enqueue(context.Player); + _championTitleContexts.Remove(context.Player); } } - - while (queue.Count > 0) - { - _championTitleContexts.Remove((PlayerMobile)queue.Dequeue()); - } } } } diff --git a/Projects/UOContent/Engines/CannedEvil/StarRoomGate.cs b/Projects/UOContent/Engines/CannedEvil/StarRoomGate.cs index a5426e7a5..88acd587e 100644 --- a/Projects/UOContent/Engines/CannedEvil/StarRoomGate.cs +++ b/Projects/UOContent/Engines/CannedEvil/StarRoomGate.cs @@ -3,15 +3,21 @@ using ModernUO.Serialization; namespace Server.Items; -[SerializationGenerator(1, false)] +[SerializationGenerator(2, false)] public partial class StarRoomGate : Moongate { + private void MigrateFrom(V1Content content) + { + _decays = content.Decays; + _decayTime = content.DecayTime; + } + private static TimeSpan GateDuration = TimeSpan.FromMinutes(2.0); [SerializableField(0)] private bool _decays; - [DeltaDateTime] + [AnchoredDateTime] [SerializableField(1)] private DateTime _decayTime; diff --git a/Projects/UOContent/Engines/Chat/Channel.cs b/Projects/UOContent/Engines/Chat/Channel.cs index 09b8f6cb3..69f33ddcf 100644 --- a/Projects/UOContent/Engines/Chat/Channel.cs +++ b/Projects/UOContent/Engines/Chat/Channel.cs @@ -146,15 +146,8 @@ namespace Server.Engines.Chat m_Users.Remove(user); user.CurrentChannel = null; - if (m_Moderators.Contains(user)) - { - m_Moderators.Remove(user); - } - - if (m_Voices.Contains(user)) - { - m_Voices.Remove(user); - } + m_Moderators.Remove(user); + m_Voices.Remove(user); SendCommand(ChatCommand.RemoveUserFromChannel, user, user.Username); ChatSystem.SendCommandTo(user.Mobile, ChatCommand.LeaveChannel); @@ -183,10 +176,7 @@ namespace Server.Engines.Chat public void RemoveBan(ChatUser user) { - if (m_Banned.Contains(user)) - { - m_Banned.Remove(user); - } + m_Banned.Remove(user); } public void Kick(ChatUser user, ChatUser moderator = null) diff --git a/Projects/UOContent/Engines/ConPVP/Games/BombingRun.cs b/Projects/UOContent/Engines/ConPVP/Games/BombingRun.cs index 29e0b9949..6ab4fffea 100644 --- a/Projects/UOContent/Engines/ConPVP/Games/BombingRun.cs +++ b/Projects/UOContent/Engines/ConPVP/Games/BombingRun.cs @@ -716,10 +716,7 @@ public partial class BRBomb : Item m.Target = new BombTarget(this, m); - if (m_Helpers.Contains(m)) - { - m_Helpers.Remove(m); - } + m_Helpers.Remove(m); if (m_Helpers.Count > 0) { @@ -833,10 +830,9 @@ public partial class BRBomb : Item [SerializationGenerator(0, false)] public partial class BRGoal : BaseAddon { - [SerializableField(0)] + [SerializableField(0, fieldChanged: nameof(OnNorthChanged))] private bool _north; - [SerializableFieldChanged(0)] [MethodImpl(MethodImplOptions.AggressiveInlining)] private void OnNorthChanged(bool oldValue, bool newValue) => Remake(); diff --git a/Projects/UOContent/Engines/ConPVP/Games/KingOfTheHill.cs b/Projects/UOContent/Engines/ConPVP/Games/KingOfTheHill.cs index 6f73a0949..6aec6f328 100644 --- a/Projects/UOContent/Engines/ConPVP/Games/KingOfTheHill.cs +++ b/Projects/UOContent/Engines/ConPVP/Games/KingOfTheHill.cs @@ -252,10 +252,9 @@ public partial class HillOfTheKing : Item public partial class KHBoard : Item { [SerializedCommandProperty(AccessLevel.GameMaster)] - [SerializableField(0)] + [SerializableField(0, fieldChanged: nameof(OnControllerChanged))] private KHController _controller; - [SerializableFieldChanged(0)] private void OnControllerChanged(KHController oldValue, KHController newValue) { oldValue?.RemoveBoard(this); diff --git a/Projects/UOContent/Engines/ConPVP/Ruleset.cs b/Projects/UOContent/Engines/ConPVP/Ruleset.cs index fb4a1ec8b..895496c43 100644 --- a/Projects/UOContent/Engines/ConPVP/Ruleset.cs +++ b/Projects/UOContent/Engines/ConPVP/Ruleset.cs @@ -56,12 +56,11 @@ namespace Server.Engines.ConPVP public void RemoveFlavor(Ruleset flavor) { - if (!Flavors.Contains(flavor)) + if (!Flavors.Remove(flavor)) { return; } - Flavors.Remove(flavor); Options.And(flavor.Options.Not()); flavor.Options.Not(); } diff --git a/Projects/UOContent/Engines/ConPVP/Trophy.cs b/Projects/UOContent/Engines/ConPVP/Trophy.cs index fa712de33..603b52ef3 100644 --- a/Projects/UOContent/Engines/ConPVP/Trophy.cs +++ b/Projects/UOContent/Engines/ConPVP/Trophy.cs @@ -64,17 +64,13 @@ public partial class Trophy : Item UpdateStyle(); } - [SerializableProperty(1)] - [CommandProperty(AccessLevel.GameMaster)] - public TrophyRank Rank + [SerializableField(1, fieldChanged: nameof(OnRankChanged))] + [SerializedCommandProperty(AccessLevel.GameMaster)] + private TrophyRank _rank; + + private void OnRankChanged(TrophyRank oldValue, TrophyRank newValue) { - get => _rank; - set - { - _rank = value; - UpdateStyle(); - this.MarkDirty(); - } + UpdateStyle(); } private void Deserialize(IGenericReader reader, int version) diff --git a/Projects/UOContent/Engines/Craft/Core/CraftItem.cs b/Projects/UOContent/Engines/Craft/Core/CraftItem.cs index 5a31aec3c..e99760bbc 100644 --- a/Projects/UOContent/Engines/Craft/Core/CraftItem.cs +++ b/Projects/UOContent/Engines/Craft/Core/CraftItem.cs @@ -1451,6 +1451,9 @@ namespace Server.Engines.Craft if (item != null) { + // Stamped here, not in OnCraft: most craftables do not implement ICraftable. + item.PlayerConstructed = true; + if (item is ICraftable craftable) { endquality = craftable.OnCraft(quality, makersMark, from, craftSystem, typeRes, tool, this, resHue); @@ -1742,6 +1745,9 @@ namespace Server.Engines.Craft if (item != null) { + // Stamped here, not in OnCraft: most craftables do not implement ICraftable. + item.PlayerConstructed = true; + if (item is ICraftable craftable) { endquality = craftable.OnCraft(quality, makersMark, from, craftSystem, typeRes, tool, this, resHue); diff --git a/Projects/UOContent/Engines/Ethics/Core/Player.cs b/Projects/UOContent/Engines/Ethics/Core/Player.cs index f21fe9eb9..d8aad9eb3 100644 --- a/Projects/UOContent/Engines/Ethics/Core/Player.cs +++ b/Projects/UOContent/Engines/Ethics/Core/Player.cs @@ -5,9 +5,20 @@ using Server.Mobiles; namespace Server.Ethics; [PropertyObject] -[SerializationGenerator(1)] +[SerializationGenerator(2)] public partial class Player : EthicsEntity { + private void MigrateFrom(V1Content content) + { + _mobile = content.Mobile; + _power = content.Power; + _history = content.History; + _steed = content.Steed; + _familiar = content.Familiar; + _shield = content.Shield; + _ethic = content.Ethic; + } + [SerializableField(0, setter: "private")] private Mobile _mobile; @@ -27,7 +38,7 @@ public partial class Player : EthicsEntity [SerializedCommandProperty(AccessLevel.GameMaster, AccessLevel.Administrator)] private Mobile _familiar; - [DeltaDateTime] + [AnchoredDateTime] [SerializableField(5, setter: "private")] private DateTime _shield; diff --git a/Projects/UOContent/Engines/Khaldun/PuzzleChest.cs b/Projects/UOContent/Engines/Khaldun/PuzzleChest.cs index a5872e437..53932c8f0 100644 --- a/Projects/UOContent/Engines/Khaldun/PuzzleChest.cs +++ b/Projects/UOContent/Engines/Khaldun/PuzzleChest.cs @@ -162,10 +162,15 @@ namespace Server.Items } } - [SerializationGenerator(0)] + [SerializationGenerator(1)] public partial class PuzzleChestSolutionAndTime : PuzzleChestSolution { - [DeltaDateTime] + private void MigrateFrom(V0Content content) + { + _when = content.When; + } + + [AnchoredDateTime] [SerializableField(0)] private DateTime _when; @@ -237,16 +242,12 @@ namespace Server.Items } } - [SerializableProperty(0)] - public PuzzleChestSolution Solution + [SerializableField(0, fieldChanged: nameof(OnSolutionChanged))] + private PuzzleChestSolution _solution; + + private void OnSolutionChanged(PuzzleChestSolution oldValue, PuzzleChestSolution newValue) { - get => _solution; - set - { - _solution = value; - InitHints(); - this.MarkDirty(); - } + InitHints(); } public PuzzleChestCylinder FirstHint @@ -550,21 +551,14 @@ namespace Server.Items return; } - using var toDelete = PooledRefQueue.Create(); - foreach (var (key, value) in _guesses) { if (Core.Now - value.When > CleanupTime) { - toDelete.Enqueue(key); + _guesses.Remove(key); } } - while (toDelete.Count > 0) - { - _guesses.Remove(toDelete.Dequeue()); - } - if (_guesses.Count == 0) { _guesses = null; diff --git a/Projects/UOContent/Engines/ML Quests/Gumps/RaceChangeGump.cs b/Projects/UOContent/Engines/ML Quests/Gumps/RaceChangeGump.cs index 6e37f88f5..a0de2461d 100644 --- a/Projects/UOContent/Engines/ML Quests/Gumps/RaceChangeGump.cs +++ b/Projects/UOContent/Engines/ML Quests/Gumps/RaceChangeGump.cs @@ -116,10 +116,9 @@ namespace Server.Engines.MLQuests.Gumps private static void CloseCurrent(NetState ns) { - if (m_Pending.TryGetValue(ns, out var state)) + if (m_Pending.Remove(ns, out var state)) { state._timeoutToken.Cancel(); - m_Pending.Remove(ns); } ns.SendCloseRaceChanger(); diff --git a/Projects/UOContent/Engines/ML Quests/MLQuestPersistence.cs b/Projects/UOContent/Engines/ML Quests/MLQuestPersistence.cs index 5d846d595..d3e39c09f 100644 --- a/Projects/UOContent/Engines/ML Quests/MLQuestPersistence.cs +++ b/Projects/UOContent/Engines/ML Quests/MLQuestPersistence.cs @@ -19,7 +19,7 @@ namespace Server.Engines.MLQuests { base.Serialize(writer); - writer.Write(2); // version + writer.Write(3); // version writer.Write(MLQuestSystem.Contexts.Count); foreach (var context in MLQuestSystem.Contexts.Values) diff --git a/Projects/UOContent/Engines/ML Quests/Objectives/BaseObjective.cs b/Projects/UOContent/Engines/ML Quests/Objectives/BaseObjective.cs index 2838c7736..77c3350f5 100644 --- a/Projects/UOContent/Engines/ML Quests/Objectives/BaseObjective.cs +++ b/Projects/UOContent/Engines/ML Quests/Objectives/BaseObjective.cs @@ -119,7 +119,7 @@ namespace Server.Engines.MLQuests.Objectives if (IsTimed) { writer.Write(true); - writer.WriteDeltaTime(EndTime); + writer.WriteAnchoredTime(EndTime); } else { @@ -135,7 +135,7 @@ namespace Server.Engines.MLQuests.Objectives { if (reader.ReadBool()) { - var endTime = reader.ReadDeltaTime(); + var endTime = version >= 3 ? reader.ReadAnchoredTime() : reader.ReadDeltaTime(); if (objInstance != null) { diff --git a/Projects/UOContent/Engines/Pathing/Cache/StepCache.cs b/Projects/UOContent/Engines/Pathing/Cache/StepCache.cs index adf178927..b4c5eba7c 100644 --- a/Projects/UOContent/Engines/Pathing/Cache/StepCache.cs +++ b/Projects/UOContent/Engines/Pathing/Cache/StepCache.cs @@ -727,20 +727,14 @@ public sealed class StepCache var window = MissPromotionWindowMs; var beforeCount = _chunkMissTracker.Count; - using var toRemove = PooledRefQueue.Create(); foreach (var kvp in _chunkMissTracker) { if (now - kvp.Value.LastMissTickStamp > window) { - toRemove.Enqueue(kvp.Key); + _chunkMissTracker.Remove(kvp.Key); } } - while (toRemove.Count > 0) - { - _chunkMissTracker.Remove(toRemove.Dequeue()); - } - if (_chunkMissTracker.Count == beforeCount) { _chunkMissTracker.Clear(); diff --git a/Projects/UOContent/Engines/Pathing/MovementPath.cs b/Projects/UOContent/Engines/Pathing/MovementPath.cs index 32aa8ba11..d1ef874fc 100644 --- a/Projects/UOContent/Engines/Pathing/MovementPath.cs +++ b/Projects/UOContent/Engines/Pathing/MovementPath.cs @@ -1,7 +1,5 @@ using System; using System.Diagnostics; -using Server.Engines.Pathing; -using Server.Engines.Pathing.Cache; using Server.Items; using Server.PathAlgorithms; using Server.Spells; diff --git a/Projects/UOContent/Engines/Plants/PlantItem.cs b/Projects/UOContent/Engines/Plants/PlantItem.cs index 60451f53b..5f66b1a7a 100644 --- a/Projects/UOContent/Engines/Plants/PlantItem.cs +++ b/Projects/UOContent/Engines/Plants/PlantItem.cs @@ -37,17 +37,17 @@ public partial class PlantItem : Item, ISecurable [SerializedIgnoreDupe] [SerializableField(0)] + [SaveFlag(nameof(ShouldSerializeSecureLevel))] [SerializedCommandProperty(AccessLevel.GameMaster)] private SecureLevel _level; - [SerializableFieldSaveFlag(0)] private bool ShouldSerializeSecureLevel() => (int)_level != 0; [SerializedIgnoreDupe] [SerializableField(5, setter: "private")] + [SaveFlag(nameof(ShouldSerializePlantSystem))] private PlantSystem _plantSystem; - [SerializableFieldSaveFlag(5)] private bool ShouldSerializePlantSystem() => _plantStatus < PlantStatus.DecorativePlant; // For clients older than 7.0.12.0 @@ -82,6 +82,7 @@ public partial class PlantItem : Item, ISecurable [CommandProperty(AccessLevel.GameMaster)] [SerializableProperty(1)] + [SaveFlag(nameof(ShouldSerializePlantStatus))] public PlantStatus PlantStatus { get => _plantStatus; @@ -120,53 +121,38 @@ public partial class PlantItem : Item, ISecurable } } - [SerializableFieldSaveFlag(1)] private bool ShouldSerializePlantStatus() => _plantStatus != PlantStatus.BowlOfDirt; - [SerializableProperty(2)] - [CommandProperty(AccessLevel.GameMaster)] - public PlantType PlantType + [SerializableField(2, fieldChanged: nameof(OnPlantTypeChanged))] + [SaveFlag(nameof(ShouldSerializePlantType))] + [SerializedCommandProperty(AccessLevel.GameMaster)] + private PlantType _plantType; + + private void OnPlantTypeChanged(PlantType oldValue, PlantType newValue) { - get => _plantType; - set - { - _plantType = value; - Update(); - } + Update(); } - [SerializableFieldSaveFlag(2)] private bool ShouldSerializePlantType() => (int)_plantType != 0; - [SerializableProperty(3)] - [CommandProperty(AccessLevel.GameMaster)] - public PlantHue PlantHue + [SerializableField(3, fieldChanged: nameof(OnPlantHueChanged))] + [SaveFlag(nameof(ShouldSerializePlantHue))] + [SerializedCommandProperty(AccessLevel.GameMaster)] + private PlantHue _plantHue; + + private void OnPlantHueChanged(PlantHue oldValue, PlantHue newValue) { - get => _plantHue; - set - { - _plantHue = value; - Update(); - } + Update(); } - [SerializableFieldSaveFlag(3)] private bool ShouldSerializePlantHue() => _plantHue != PlantHue.None; - [SerializableProperty(4)] - [CommandProperty(AccessLevel.GameMaster)] - public bool ShowType - { - get => _showType; - set - { - _showType = value; - InvalidateProperties(); - this.MarkDirty(); - } - } + [SerializableField(4)] + [SaveFlag(nameof(ShouldSerializeShowType))] + [SerializedCommandProperty(AccessLevel.GameMaster)] + [InvalidateProperties] + private bool _showType; - [SerializableFieldSaveFlag(4)] private bool ShouldSerializeShowType() => _showType; [CommandProperty(AccessLevel.GameMaster)] diff --git a/Projects/UOContent/Engines/Plants/PlantSystem.cs b/Projects/UOContent/Engines/Plants/PlantSystem.cs index 29b9f48be..4b202ef63 100644 --- a/Projects/UOContent/Engines/Plants/PlantSystem.cs +++ b/Projects/UOContent/Engines/Plants/PlantSystem.cs @@ -33,24 +33,24 @@ namespace Server.Engines.Plants private PlantItem _plant; [SerializableField(0)] + [SaveFlag(nameof(ShouldSerializeFertileDirt))] private bool _fertileDirt; - [SerializableFieldSaveFlag(0)] private bool ShouldSerializeFertileDirt() => _fertileDirt; [SerializableField(1)] private DateTime _nextGrowth; [SerializableField(2, setter: "private")] + [SaveFlag(nameof(ShouldSerializeGrowthIndicator))] private PlantGrowthIndicator _growthIndicator; - [SerializableFieldSaveFlag(2)] private bool ShouldSerializeGrowthIndicator() => _growthIndicator != PlantGrowthIndicator.None; [SerializableField(13)] + [SaveFlag(nameof(ShouldSerializePollinated))] private bool _pollinated; - [SerializableFieldSaveFlag(13)] private bool ShouldSerializePollinated() => _pollinated; public PlantSystem(PlantItem plant) @@ -97,45 +97,42 @@ namespace Server.Engines.Plants public bool IsFullWater => _water >= 4; - [SerializableProperty(3)] - public int Water + [SerializableField(3, fieldChanged: nameof(OnWaterChanged), allowFieldChange: nameof(AllowWaterChange))] + [SaveFlag(nameof(ShouldSerializeWater))] + private int _water; + + private bool AllowWaterChange(ref int value) { - get => _water; - set - { - _water = Math.Clamp(value, 0, 4); - Plant.InvalidateProperties(); - MarkDirty(); - } + value = Math.Clamp(value, 0, 4); + return true; + } + + private void OnWaterChanged(int oldValue, int newValue) + { + Plant.InvalidateProperties(); } - [SerializableFieldSaveFlag(3)] private bool ShouldSerializeWater() => _water != 0; - [SerializableProperty(4)] - public int Hits + [SerializableField(4, fieldChanged: nameof(OnHitsChanged), allowFieldChange: nameof(AllowHitsChange))] + [SaveFlag(nameof(ShouldSerializeHits))] + private int _hits; + + private bool AllowHitsChange(ref int value) { - get => _hits; - set - { - if (_hits == value) - { - return; - } - - _hits = Math.Clamp(value, 0, MaxHits); - - if (_hits == 0) - { - Plant.Die(); - } - - Plant.InvalidateProperties(); - MarkDirty(); - } + value = Math.Clamp(value, 0, MaxHits); + return true; + } + + private void OnHitsChanged(int oldValue, int newValue) + { + if (_hits == 0) + { + Plant.Die(); + } + Plant.InvalidateProperties(); } - [SerializableFieldSaveFlag(4)] private bool ShouldSerializeHits() => _hits != 0; public int MaxHits => 10 + (int)Plant.PlantStatus * 2; @@ -149,124 +146,108 @@ namespace Server.Engines.Plants _ => PlantHealth.Vibrant }; - [SerializableProperty(5)] - public int Infestation + [SerializableField(5, allowFieldChange: nameof(AllowInfestationChange))] + [SaveFlag(nameof(ShouldSerializeInfestation))] + private int _infestation; + + private bool AllowInfestationChange(ref int value) { - get => _infestation; - set - { - _infestation = Math.Clamp(value, 0, 2); - MarkDirty(); - } + value = Math.Clamp(value, 0, 2); + return true; } - [SerializableFieldSaveFlag(5)] private bool ShouldSerializeInfestation() => _infestation != 0; - [SerializableProperty(6)] - public int Fungus + [SerializableField(6, allowFieldChange: nameof(AllowFungusChange))] + [SaveFlag(nameof(ShouldSerializeFungus))] + private int _fungus; + + private bool AllowFungusChange(ref int value) { - get => _fungus; - set - { - _fungus = Math.Clamp(value, 0, 2); - MarkDirty(); - } + value = Math.Clamp(value, 0, 2); + return true; } - [SerializableFieldSaveFlag(6)] private bool ShouldSerializeFungus() => _fungus != 0; - [SerializableProperty(7)] - public int Poison + [SerializableField(7, allowFieldChange: nameof(AllowPoisonChange))] + [SaveFlag(nameof(ShouldSerializePoison))] + private int _poison; + + private bool AllowPoisonChange(ref int value) { - get => _poison; - set - { - _poison = Math.Clamp(value, 0, 2); - MarkDirty(); - } + value = Math.Clamp(value, 0, 2); + return true; } - [SerializableFieldSaveFlag(7)] private bool ShouldSerializePoison() => _poison != 0; - [SerializableProperty(8)] - public int Disease + [SerializableField(8, allowFieldChange: nameof(AllowDiseaseChange))] + [SaveFlag(nameof(ShouldSerializeDisease))] + private int _disease; + + private bool AllowDiseaseChange(ref int value) { - get => _disease; - set - { - _disease = Math.Clamp(value, 0, 2); - MarkDirty(); - } + value = Math.Clamp(value, 0, 2); + return true; } - [SerializableFieldSaveFlag(8)] private bool ShouldSerializeDisease() => _disease != 0; public bool IsFullPoisonPotion => _poisonPotion >= 2; - [SerializableProperty(9)] - public int PoisonPotion + [SerializableField(9, allowFieldChange: nameof(AllowPoisonPotionChange))] + [SaveFlag(nameof(ShouldSerializePoisonPotion))] + private int _poisonPotion; + + private bool AllowPoisonPotionChange(ref int value) { - get => _poisonPotion; - set - { - _poisonPotion = Math.Clamp(value, 0, 2); - MarkDirty(); - } + value = Math.Clamp(value, 0, 2); + return true; } - [SerializableFieldSaveFlag(9)] private bool ShouldSerializePoisonPotion() => _poisonPotion != 0; public bool IsFullCurePotion => _curePotion >= 2; - [SerializableProperty(10)] - public int CurePotion + [SerializableField(10, allowFieldChange: nameof(AllowCurePotionChange))] + [SaveFlag(nameof(ShouldSerializeCurePotion))] + private int _curePotion; + + private bool AllowCurePotionChange(ref int value) { - get => _curePotion; - set - { - _curePotion = Math.Clamp(value, 0, 2); - MarkDirty(); - } + value = Math.Clamp(value, 0, 2); + return true; } - [SerializableFieldSaveFlag(10)] private bool ShouldSerializeCurePotion() => _curePotion != 0; public bool IsFullHealPotion => _healPotion >= 2; - [SerializableProperty(11)] - public int HealPotion + [SerializableField(11, allowFieldChange: nameof(AllowHealPotionChange))] + [SaveFlag(nameof(ShouldSerializeHealPotion))] + private int _healPotion; + + private bool AllowHealPotionChange(ref int value) { - get => _healPotion; - set - { - _healPotion = Math.Clamp(value, 0, 2); - MarkDirty(); - } + value = Math.Clamp(value, 0, 2); + return true; } - [SerializableFieldSaveFlag(11)] private bool ShouldSerializeHealPotion() => _healPotion != 0; public bool IsFullStrengthPotion => _strengthPotion >= 2; - [SerializableProperty(12)] - public int StrengthPotion + [SerializableField(12, allowFieldChange: nameof(AllowStrengthPotionChange))] + [SaveFlag(nameof(ShouldSerializeStrengthPotion))] + private int _strengthPotion; + + private bool AllowStrengthPotionChange(ref int value) { - get => _strengthPotion; - set - { - _strengthPotion = Math.Clamp(value, 0, 2); - MarkDirty(); - } + value = Math.Clamp(value, 0, 2); + return true; } - [SerializableFieldSaveFlag(12)] private bool ShouldSerializeStrengthPotion() => _strengthPotion != 0; public bool HasMaladies => Infestation > 0 || Fungus > 0 || Poison > 0 || Disease > 0 || Water != 2; @@ -274,6 +255,7 @@ namespace Server.Engines.Plants public bool PollenProducing => Plant.IsCrossable && Plant.PlantStatus >= PlantStatus.FullGrownPlant; [SerializableProperty(14)] + [SaveFlag(nameof(ShouldSerializeSeedType))] public PlantType SeedType { get => Pollinated ? _seedType : Plant.PlantType; @@ -284,10 +266,10 @@ namespace Server.Engines.Plants } } - [SerializableFieldSaveFlag(14)] private bool ShouldSerializeSeedType() => _pollinated; [SerializableProperty(15)] + [SaveFlag(nameof(ShouldSerializeSeedHue))] public PlantHue SeedHue { get => Pollinated ? _seedHue : Plant.PlantHue; @@ -298,53 +280,58 @@ namespace Server.Engines.Plants } } - [SerializableFieldSaveFlag(15)] private bool ShouldSerializeSeedHue() => _pollinated; - [SerializableProperty(16)] - public int AvailableSeeds + [SerializableField(16, allowFieldChange: nameof(AllowAvailableSeedsChange))] + [SaveFlag(nameof(ShouldSerializeAvailableSeeds))] + private int _availableSeeds; + + private bool AllowAvailableSeedsChange(ref int value) { - get => _availableSeeds; - set => _availableSeeds = Math.Max(value, 0); + value = Math.Max(value, 0); + return true; } - [SerializableFieldSaveFlag(16)] private bool ShouldSerializeAvailableSeeds() => _availableSeeds != 0; - [SerializableProperty(17)] - public int LeftSeeds + [SerializableField(17, allowFieldChange: nameof(AllowLeftSeedsChange))] + [SaveFlag(nameof(ShouldSerializeLeftSeeds), nameof(LeftSeedsDefaultValue))] + private int _leftSeeds; + + private bool AllowLeftSeedsChange(ref int value) { - get => _leftSeeds; - set => _leftSeeds = Math.Max(value, 0); + value = Math.Max(value, 0); + return true; } - [SerializableFieldSaveFlag(17)] private bool ShouldSerializeLeftSeeds() => _leftSeeds != 8; - [SerializableFieldDefault(17)] private int LeftSeedsDefaultValue() => 8; - [SerializableProperty(18)] - public int AvailableResources + [SerializableField(18, allowFieldChange: nameof(AllowAvailableResourcesChange))] + [SaveFlag(nameof(ShouldSerializeAvailableResources))] + private int _availableResources; + + private bool AllowAvailableResourcesChange(ref int value) { - get => _availableResources; - set => _availableResources = Math.Max(value, 0); + value = Math.Max(value, 0); + return true; } - [SerializableFieldSaveFlag(18)] private bool ShouldSerializeAvailableResources() => _availableResources != 0; - [SerializableProperty(19)] - public int LeftResources + [SerializableField(19, allowFieldChange: nameof(AllowLeftResourcesChange))] + [SaveFlag(nameof(ShouldSerializeLeftResources), nameof(LeftResourcesDefaultValue))] + private int _leftResources; + + private bool AllowLeftResourcesChange(ref int value) { - get => _leftResources; - set => _leftResources = Math.Max(value, 0); + value = Math.Max(value, 0); + return true; } - [SerializableFieldSaveFlag(19)] private bool ShouldSerializeLeftResources() => _leftResources != 8; - [SerializableFieldDefault(19)] private int LeftResourcesDefaultValue() => 8; public void Reset(bool potions) diff --git a/Projects/UOContent/Engines/Plants/Seed.cs b/Projects/UOContent/Engines/Plants/Seed.cs index 24a290f0b..ed6f13b7b 100644 --- a/Projects/UOContent/Engines/Plants/Seed.cs +++ b/Projects/UOContent/Engines/Plants/Seed.cs @@ -35,18 +35,14 @@ public partial class Seed : Item public override double DefaultWeight => 1.0; - [CommandProperty(AccessLevel.GameMaster)] - [SerializableProperty(1)] - public PlantHue PlantHue + [SerializableField(1, fieldChanged: nameof(OnPlantHueChanged))] + [SerializedCommandProperty(AccessLevel.GameMaster)] + [InvalidateProperties] + private PlantHue _plantHue; + + private void OnPlantHueChanged(PlantHue oldValue, PlantHue newValue) { - get => _plantHue; - set - { - _plantHue = value; - Hue = PlantHueInfo.GetInfo(value).Hue; - InvalidateProperties(); - this.MarkDirty(); - } + Hue = PlantHueInfo.GetInfo(newValue).Hue; } public override int LabelNumber => 1060810; // seed diff --git a/Projects/UOContent/Engines/Player Murder System/MurderContext.cs b/Projects/UOContent/Engines/Player Murder System/MurderContext.cs index cecd7bf7e..384c91c98 100644 --- a/Projects/UOContent/Engines/Player Murder System/MurderContext.cs +++ b/Projects/UOContent/Engines/Player Murder System/MurderContext.cs @@ -16,12 +16,14 @@ public partial class MurderContext [SerializedCommandProperty(AccessLevel.GameMaster)] private TimeSpan _longTermElapse; - [SerializableProperty(2)] - [CommandProperty(AccessLevel.GameMaster)] - public int ShortTermMurders + [SerializableField(2, allowFieldChange: nameof(AllowShortTermMurdersChange))] + [SerializedCommandProperty(AccessLevel.GameMaster)] + private int _shortTermMurders; + + private bool AllowShortTermMurdersChange(ref int value) { - get => _shortTermMurders; - set => _shortTermMurders = Math.Max(value, 0); + value = Math.Max(value, 0); + return true; } [SerializableField(3)] diff --git a/Projects/UOContent/Engines/Player Murder System/PlayerMurderSystem.cs b/Projects/UOContent/Engines/Player Murder System/PlayerMurderSystem.cs index b5a1b77d5..d1be82aa9 100644 --- a/Projects/UOContent/Engines/Player Murder System/PlayerMurderSystem.cs +++ b/Projects/UOContent/Engines/Player Murder System/PlayerMurderSystem.cs @@ -403,27 +403,20 @@ public class PlayerMurderSystem : GenericPersistence return; } - using var queue = PooledRefQueue.Create(); - foreach (var context in _contextTerms) { context.DecayKills(); if (!context.CheckStart()) { - queue.Enqueue(context.Player); - } - } - - while (queue.Count > 0) - { - var pm = (PlayerMobile)queue.Dequeue(); - if (_murderContexts.TryGetValue(pm, out var ctx)) - { - if (ctx.CanRemove()) + var pm = context.Player; + if (_murderContexts.TryGetValue(pm, out var ctx)) { - _murderContexts.Remove(pm); + if (ctx.CanRemove()) + { + _murderContexts.Remove(pm); + } + _contextTerms.Remove(ctx); } - _contextTerms.Remove(ctx); } } } diff --git a/Projects/UOContent/Engines/Quests/The Summoning/Items/SummoningAltar.cs b/Projects/UOContent/Engines/Quests/The Summoning/Items/SummoningAltar.cs index d5ec6f24a..4e3e5e237 100644 --- a/Projects/UOContent/Engines/Quests/The Summoning/Items/SummoningAltar.cs +++ b/Projects/UOContent/Engines/Quests/The Summoning/Items/SummoningAltar.cs @@ -12,16 +12,12 @@ public partial class SummoningAltar : AbbatoirAddon { } - [SerializableProperty(0)] - public BoneDemon Daemon + [SerializableField(0, fieldChanged: nameof(OnDaemonChanged))] + private BoneDemon _daemon; + + private void OnDaemonChanged(BoneDemon oldValue, BoneDemon newValue) { - get => _daemon; - set - { - _daemon = value; - CheckDaemon(); - this.MarkDirty(); - } + CheckDaemon(); } public void CheckDaemon() diff --git a/Projects/UOContent/Engines/Spawners/BaseSpawner.cs b/Projects/UOContent/Engines/Spawners/BaseSpawner.cs index 7ceeb00f6..8efc54b8c 100644 --- a/Projects/UOContent/Engines/Spawners/BaseSpawner.cs +++ b/Projects/UOContent/Engines/Spawners/BaseSpawner.cs @@ -54,10 +54,10 @@ public abstract partial class BaseSpawner : Item, ISpawner [SerializedCommandProperty(AccessLevel.Developer)] private Guid _guid; - [SerializableFieldSaveFlag(1)] private bool ShouldSerializeReturnOnDeactivate() => _returnOnDeactivate; [SerializableField(1)] + [SaveFlag(nameof(ShouldSerializeReturnOnDeactivate))] [SerializedCommandProperty(AccessLevel.Developer)] private bool _returnOnDeactivate; @@ -67,48 +67,46 @@ public abstract partial class BaseSpawner : Item, ISpawner private int _walkingRange = -1; - [SerializableFieldSaveFlag(4)] private bool ShouldSerializeWayPoint() => _wayPoint != null; [SerializableField(4)] + [SaveFlag(nameof(ShouldSerializeWayPoint))] [SerializedCommandProperty(AccessLevel.Developer)] private WayPoint _wayPoint; - [SerializableFieldSaveFlag(5)] private bool ShouldSerializeGroup() => _group; [InvalidateProperties] [SerializableField(5)] + [SaveFlag(nameof(ShouldSerializeGroup))] [SerializedCommandProperty(AccessLevel.Developer)] private bool _group; - [SerializableFieldSaveFlag(6)] private bool ShouldSerializeMinDelay() => _minDelay != DefaultMinDelay; - [SerializableFieldDefault(6)] private TimeSpan MinDelayDefault() => DefaultMinDelay; [InvalidateProperties] [SerializableField(6)] + [SaveFlag(nameof(ShouldSerializeMinDelay), nameof(MinDelayDefault))] [SerializedCommandProperty(AccessLevel.Developer)] private TimeSpan _minDelay; - [SerializableFieldSaveFlag(7)] private bool ShouldSerializeMaxDelay() => _maxDelay != DefaultMaxDelay; - [SerializableFieldDefault(7)] private TimeSpan MaxDelayDefault() => DefaultMaxDelay; [InvalidateProperties] [SerializableField(7)] + [SaveFlag(nameof(ShouldSerializeMaxDelay), nameof(MaxDelayDefault))] [SerializedCommandProperty(AccessLevel.Developer)] private TimeSpan _maxDelay; - [SerializableFieldSaveFlag(9)] private bool ShouldSerializeTeam() => _team != 0; [InvalidateProperties] [SerializableField(9)] + [SaveFlag(nameof(ShouldSerializeTeam))] [SerializedCommandProperty(AccessLevel.Developer)] private int _team; @@ -125,29 +123,29 @@ public abstract partial class BaseSpawner : Item, ISpawner /// If true, the home location of the spawn is the location where it spawned /// If false, the home location of the spawn is the location of the spawner /// - [SerializableFieldSaveFlag(11)] private bool ShouldSerializeSpawnLocationIsHome() => _spawnLocationIsHome; [InvalidateProperties] [SerializableField(11)] + [SaveFlag(nameof(ShouldSerializeSpawnLocationIsHome))] [SerializedCommandProperty(AccessLevel.Developer)] private bool _spawnLocationIsHome; - [SerializableFieldSaveFlag(12)] private bool ShouldSerializeEnd() => _end != default; [SerializableField(12)] + [SaveFlag(nameof(ShouldSerializeEnd))] [SerializedCommandProperty(AccessLevel.Developer)] private DateTime _end; /// /// Controls how spawn position optimization is handled. /// - [SerializableFieldSaveFlag(13)] private bool ShouldSerializeSpawnPositionMode() => _spawnPositionMode is not SpawnPositionMode.Automatic and not SpawnPositionMode.Abandoned; [SerializableField(13)] + [SaveFlag(nameof(ShouldSerializeSpawnPositionMode))] [SerializedCommandProperty(AccessLevel.Developer)] private SpawnPositionMode _spawnPositionMode; @@ -156,13 +154,12 @@ public abstract partial class BaseSpawner : Item, ISpawner /// /// Maximum number of random position attempts before engaging optimization. /// - [SerializableFieldSaveFlag(14)] private bool ShouldSerializeMaxSpawnAttempts() => _maxSpawnAttempts != DefaultMaxSpawnAttempts; - [SerializableFieldDefault(14)] private int MaxSpawnAttemptsDefault() => DefaultMaxSpawnAttempts; [SerializableField(14)] + [SaveFlag(nameof(ShouldSerializeMaxSpawnAttempts), nameof(MaxSpawnAttemptsDefault))] [SerializedCommandProperty(AccessLevel.Developer)] private int _maxSpawnAttempts; @@ -314,26 +311,20 @@ public abstract partial class BaseSpawner : Item, ISpawner } } - [SerializableProperty(8)] - [CommandProperty(AccessLevel.Developer)] - public int Count + [SerializableField(8, fieldChanged: nameof(OnCountChanged))] + [SerializedCommandProperty(AccessLevel.Developer)] + [InvalidateProperties] + private int _count; + + private void OnCountChanged(int oldValue, int newValue) { - get => _count; - set + if (IsFull) { - _count = value; - - if (IsFull) - { - _timer?.Stop(); - } - else if (_timer?.Running != true) - { - DoTimer(); - } - - InvalidateProperties(); - this.MarkDirty(); + _timer?.Stop(); + } + else if (_timer?.Running != true) + { + DoTimer(); } } diff --git a/Projects/UOContent/Engines/Spawners/Spawner.cs b/Projects/UOContent/Engines/Spawners/Spawner.cs index 7c07d2681..db25cab55 100644 --- a/Projects/UOContent/Engines/Spawners/Spawner.cs +++ b/Projects/UOContent/Engines/Spawners/Spawner.cs @@ -10,17 +10,17 @@ public partial class Spawner : BaseSpawner /// When true, enables proactive spiral scanning to find valid spawn positions. /// Only relevant when SpawnPositionMode is Automatic or Enabled. /// - [SerializableFieldSaveFlag(0)] private bool ShouldSerializeUseSpiralScan() => _useSpiralScan; [SerializableField(0)] + [SaveFlag(nameof(ShouldSerializeUseSpiralScan))] [SerializedCommandProperty(AccessLevel.Developer)] private bool _useSpiralScan; - [SerializableFieldSaveFlag(1)] private bool ShouldSerializeSpawnBounds() => _spawnBounds != default; [SerializableProperty(1)] + [SaveFlag(nameof(ShouldSerializeSpawnBounds))] [CommandProperty(AccessLevel.Developer)] public override Rectangle3D SpawnBounds { diff --git a/Projects/UOContent/Engines/Stealables/StealableArtifacts.cs b/Projects/UOContent/Engines/Stealables/StealableArtifacts.cs index 396b87dcd..4cd8d8c63 100644 --- a/Projects/UOContent/Engines/Stealables/StealableArtifacts.cs +++ b/Projects/UOContent/Engines/Stealables/StealableArtifacts.cs @@ -248,7 +248,7 @@ public class StealableArtifacts : GenericPersistence public override void Serialize(IGenericWriter writer) { - writer.WriteEncodedInt(1); // version + writer.WriteEncodedInt(2); // version writer.Write(_enabled); @@ -261,7 +261,7 @@ public class StealableArtifacts : GenericPersistence var si = _artifacts[i]; writer.Write(si.Item); - writer.WriteDeltaTime(si.NextRespawn); + writer.WriteAnchoredTime(si.NextRespawn); } } } @@ -282,7 +282,7 @@ public class StealableArtifacts : GenericPersistence for (var i = 0; i < length; i++) { var item = reader.ReadEntity(); - var nextRespawn = reader.ReadDeltaTime(); + var nextRespawn = version >= 2 ? reader.ReadAnchoredTime() : reader.ReadDeltaTime(); if (i < _artifacts.Length) { diff --git a/Projects/UOContent/Engines/Treasures of Tokuno/GreaterArtifacts.cs b/Projects/UOContent/Engines/Treasures of Tokuno/GreaterArtifacts.cs index 005688e1e..c42cfae4e 100644 --- a/Projects/UOContent/Engines/Treasures of Tokuno/GreaterArtifacts.cs +++ b/Projects/UOContent/Engines/Treasures of Tokuno/GreaterArtifacts.cs @@ -332,27 +332,22 @@ public partial class PigmentsOfTokuno : BasePigmentsOfTokuno [Constructible] public PigmentsOfTokuno(PigmentType type, int uses) : base(uses) => Type = type; - [SerializableProperty(0)] - [CommandProperty(AccessLevel.GameMaster)] - public PigmentType Type + [SerializableField(0, fieldChanged: nameof(OnTypeChanged))] + [SerializedCommandProperty(AccessLevel.GameMaster)] + private PigmentType _type; + + private void OnTypeChanged(PigmentType oldValue, PigmentType newValue) { - get => _type; - set + var v = (int)_type; + if (v >= 0 && v < _table.Length) { - _type = value; - - var v = (int)_type; - - if (v >= 0 && v < _table.Length) - { - Hue = _table[v][0]; - Label = _table[v][1]; - } - else - { - Hue = 0; - Label = -1; - } + Hue = _table[v][0]; + Label = _table[v][1]; + } + else + { + Hue = 0; + Label = -1; } } diff --git a/Projects/UOContent/Engines/Treasures of Tokuno/LesserArtifacts.cs b/Projects/UOContent/Engines/Treasures of Tokuno/LesserArtifacts.cs index 54d026df4..22c6af344 100644 --- a/Projects/UOContent/Engines/Treasures of Tokuno/LesserArtifacts.cs +++ b/Projects/UOContent/Engines/Treasures of Tokuno/LesserArtifacts.cs @@ -617,27 +617,22 @@ public partial class LesserPigmentsOfTokuno : BasePigmentsOfTokuno [Constructible] public LesserPigmentsOfTokuno(LesserPigmentType type) : base(1) => Type = type; - [SerializableProperty(0)] - [CommandProperty(AccessLevel.GameMaster)] - public LesserPigmentType Type + [SerializableField(0, fieldChanged: nameof(OnTypeChanged))] + [SerializedCommandProperty(AccessLevel.GameMaster)] + private LesserPigmentType _type; + + private void OnTypeChanged(LesserPigmentType oldValue, LesserPigmentType newValue) { - get => _type; - set + var v = (int)_type; + if (v >= 0 && v < _table.Length) { - _type = value; - - var v = (int)_type; - - if (v >= 0 && v < _table.Length) - { - Hue = _table[v][0]; - Label = _table[v][1]; - } - else - { - Hue = 0; - Label = -1; - } + Hue = _table[v][0]; + Label = _table[v][1]; + } + else + { + Hue = 0; + Label = -1; } } diff --git a/Projects/UOContent/Engines/Veteran Rewards/Character Statue Maker/CharacterStatue.cs b/Projects/UOContent/Engines/Veteran Rewards/Character Statue Maker/CharacterStatue.cs index ff665ec53..7a62cc735 100644 --- a/Projects/UOContent/Engines/Veteran Rewards/Character Statue Maker/CharacterStatue.cs +++ b/Projects/UOContent/Engines/Veteran Rewards/Character Statue Maker/CharacterStatue.cs @@ -79,45 +79,33 @@ public partial class CharacterStatue : Mobile, IRewardItem InvalidateHues(); } - [SerializableProperty(0)] - [CommandProperty(AccessLevel.GameMaster)] - public StatueType StatueType + [SerializableField(0, fieldChanged: nameof(OnStatueTypeChanged))] + [SerializedCommandProperty(AccessLevel.GameMaster)] + private StatueType _statueType; + + private void OnStatueTypeChanged(StatueType oldValue, StatueType newValue) { - get => _statueType; - set - { - _statueType = value; - InvalidateHues(); - InvalidatePose(); - this.MarkDirty(); - } + InvalidateHues(); + InvalidatePose(); } - [SerializableProperty(1)] - [CommandProperty(AccessLevel.GameMaster)] - public StatuePose Pose + [SerializableField(1, fieldChanged: nameof(OnPoseChanged))] + [SerializedCommandProperty(AccessLevel.GameMaster)] + private StatuePose _pose; + + private void OnPoseChanged(StatuePose oldValue, StatuePose newValue) { - get => _pose; - set - { - _pose = value; - InvalidatePose(); - this.MarkDirty(); - } + InvalidatePose(); } - [SerializableProperty(2)] - [CommandProperty(AccessLevel.GameMaster)] - public StatueMaterial Material + [SerializableField(2, fieldChanged: nameof(OnMaterialChanged))] + [SerializedCommandProperty(AccessLevel.GameMaster)] + private StatueMaterial _material; + + private void OnMaterialChanged(StatueMaterial oldValue, StatueMaterial newValue) { - get => _material; - set - { - _material = value; - InvalidateHues(); - InvalidatePose(); - this.MarkDirty(); - } + InvalidateHues(); + InvalidatePose(); } public override void OnDoubleClick(Mobile from) diff --git a/Projects/UOContent/Engines/Veteran Rewards/Character Statue Maker/CharacterStatueMaker.cs b/Projects/UOContent/Engines/Veteran Rewards/Character Statue Maker/CharacterStatueMaker.cs index a028b1c85..48ae30ae9 100644 --- a/Projects/UOContent/Engines/Veteran Rewards/Character Statue Maker/CharacterStatueMaker.cs +++ b/Projects/UOContent/Engines/Veteran Rewards/Character Statue Maker/CharacterStatueMaker.cs @@ -25,17 +25,13 @@ public partial class CharacterStatueMaker : Item, IRewardItem public override int LabelNumber => 1076173; // Character Statue Maker - [SerializableProperty(1)] - [CommandProperty(AccessLevel.GameMaster)] - public StatueType StatueType + [SerializableField(1, fieldChanged: nameof(OnStatueTypeChanged))] + [SerializedCommandProperty(AccessLevel.GameMaster)] + private StatueType _statueType; + + private void OnStatueTypeChanged(StatueType oldValue, StatueType newValue) { - get => _statueType; - set - { - _statueType = value; - InvalidateHue(); - this.MarkDirty(); - } + InvalidateHue(); } public override void OnDoubleClick(Mobile from) diff --git a/Projects/UOContent/Engines/Virtues/VirtueContext.cs b/Projects/UOContent/Engines/Virtues/VirtueContext.cs index 3b26b5294..7524f77ed 100644 --- a/Projects/UOContent/Engines/Virtues/VirtueContext.cs +++ b/Projects/UOContent/Engines/Virtues/VirtueContext.cs @@ -5,102 +5,121 @@ using Server.Mobiles; namespace Server.Engines.Virtues; [PropertyObject] -[SerializationGenerator(0)] +[SerializationGenerator(1)] public partial class VirtueContext { - [DeltaDateTime] + private void MigrateFrom(V0Content content) + { + // Save-flagged values arrive as nullables; unset flags fall back to the same + // defaults the old deserialize left in place. + _lastSacrificeGain = content.LastSacrificeGain ?? default; + _lastSacrificeLoss = content.LastSacrificeLoss ?? default; + _availableResurrects = content.AvailableResurrects ?? 0; + _lastJusticeLoss = content.LastJusticeLoss ?? default; + _lastCompassionLoss = content.LastCompassionLoss ?? default; + _nextCompassionDay = content.NextCompassionDay ?? default; + _compassionGains = content.CompassionGains ?? 0; + _lastValorLoss = content.LastValorLoss ?? default; + _lastHonorUse = content.LastHonorUse ?? default; + _honorActive = content.HonorActive; + _justiceProtection = content.JusticeProtection; + _justiceStatus = content.JusticeStatus ?? JusticeProtectorStatus.None; + _values = content.Values; + } + + [AnchoredDateTime] [SerializableField(0)] + [SaveFlag(nameof(ShouldSerializeLastSacrificeGain))] [SerializedCommandProperty(AccessLevel.GameMaster, AccessLevel.Administrator)] private DateTime _lastSacrificeGain; - [SerializableFieldSaveFlag(0)] private bool ShouldSerializeLastSacrificeGain() => !SacrificeVirtue.CanGain(this); - [DeltaDateTime] + [AnchoredDateTime] [SerializableField(1)] + [SaveFlag(nameof(ShouldSerializeLastSacrificeLoss))] [SerializedCommandProperty(AccessLevel.GameMaster, AccessLevel.Administrator)] private DateTime _lastSacrificeLoss; - [SerializableFieldSaveFlag(1)] private bool ShouldSerializeLastSacrificeLoss() => !SacrificeVirtue.CanAtrophy(this); [SerializableField(2)] + [SaveFlag(nameof(ShouldSerializeAvailableResurrects))] [SerializedCommandProperty(AccessLevel.GameMaster)] private int _availableResurrects; - [SerializableFieldSaveFlag(2)] private bool ShouldSerializeAvailableResurrects() => _availableResurrects > 0; - [DeltaDateTime] + [AnchoredDateTime] [SerializableField(3)] + [SaveFlag(nameof(ShouldSerializeLastJusticeLoss))] [SerializedCommandProperty(AccessLevel.GameMaster, AccessLevel.Administrator)] private DateTime _lastJusticeLoss; - [SerializableFieldSaveFlag(3)] private bool ShouldSerializeLastJusticeLoss() => !JusticeVirtue.CanAtrophy(this); - [DeltaDateTime] + [AnchoredDateTime] [SerializableField(4)] + [SaveFlag(nameof(ShouldSerializeLastCompassionLoss))] [SerializedCommandProperty(AccessLevel.GameMaster, AccessLevel.Administrator)] private DateTime _lastCompassionLoss; - [SerializableFieldSaveFlag(4)] private bool ShouldSerializeLastCompassionLoss() => !CompassionVirtue.CanAtrophy(this); - [DeltaDateTime] + [AnchoredDateTime] [SerializableField(5)] + [SaveFlag(nameof(ShouldSerializeNextCompassionDay))] [SerializedCommandProperty(AccessLevel.GameMaster)] private DateTime _nextCompassionDay; - [SerializableFieldSaveFlag(5)] private bool ShouldSerializeNextCompassionDay() => _nextCompassionDay > Core.Now; [SerializableField(6)] + [SaveFlag(nameof(ShouldSerializeCompassionGains))] [SerializedCommandProperty(AccessLevel.GameMaster)] private int _compassionGains; - [SerializableFieldSaveFlag(6)] private bool ShouldSerializeCompassionGains() => _compassionGains > 0; - [DeltaDateTime] + [AnchoredDateTime] [SerializableField(7)] + [SaveFlag(nameof(ShouldSerializeValorLoss))] [SerializedCommandProperty(AccessLevel.GameMaster, AccessLevel.Administrator)] private DateTime _lastValorLoss; - [SerializableFieldSaveFlag(7)] private bool ShouldSerializeValorLoss() => !ValorVirtue.CanAtrophy(this); - [DeltaDateTime] + [AnchoredDateTime] [SerializableField(8)] + [SaveFlag(nameof(ShouldSerializeLastHonorUse))] [SerializedCommandProperty(AccessLevel.GameMaster)] private DateTime _lastHonorUse; - [SerializableFieldSaveFlag(8)] private bool ShouldSerializeLastHonorUse() => !HonorVirtue.CanUse(this); [SerializableField(9)] + [SaveFlag(nameof(ShouldSerializeHonorActive))] [SerializedCommandProperty(AccessLevel.GameMaster, AccessLevel.Administrator)] private bool _honorActive; - [SerializableFieldSaveFlag(9)] private bool ShouldSerializeHonorActive() => _honorActive; [SerializableField(10)] + [SaveFlag(nameof(ShouldSerializeJusticeProtection))] private PlayerMobile _justiceProtection; - [SerializableFieldSaveFlag(10)] private bool ShouldSerializeJusticeProtection() => _justiceProtection != null && _justiceStatus != JusticeProtectorStatus.None; [SerializableField(11)] + [SaveFlag(nameof(ShouldSerializeJusticeStatus))] private JusticeProtectorStatus _justiceStatus; - [SerializableFieldSaveFlag(11)] private bool ShouldSerializeJusticeStatus() => _justiceProtection != null && _justiceStatus != JusticeProtectorStatus.None; [SerializableField(12, setter: "private")] + [SaveFlag(nameof(ShouldSerializeValues))] private int[] _values; - [SerializableFieldSaveFlag(12)] private bool ShouldSerializeValues() { if (_values == null) diff --git a/Projects/UOContent/Engines/Virtues/VirtueSystem.cs b/Projects/UOContent/Engines/Virtues/VirtueSystem.cs index 89c5fa3b5..e57a2d386 100644 --- a/Projects/UOContent/Engines/Virtues/VirtueSystem.cs +++ b/Projects/UOContent/Engines/Virtues/VirtueSystem.cs @@ -372,8 +372,6 @@ public class VirtueSystem : GenericPersistence return; } - using var queue = PooledRefQueue.Create(); - // This is not particularly efficient. If it gets too slow, then use a different architecture. foreach (var (player, virtues) in _playerVirtues) { @@ -381,14 +379,9 @@ public class VirtueSystem : GenericPersistence if (!virtues.IsUsed()) { - queue.Enqueue(player); + _playerVirtues.Remove(player); } } - - while (queue.Count > 0) - { - _playerVirtues.Remove((PlayerMobile)queue.Dequeue()); - } } ~VirtueTimer() diff --git a/Projects/UOContent/Gumps/AdminGump.cs b/Projects/UOContent/Gumps/AdminGump.cs index 1fe917b55..a4fbe0f18 100644 --- a/Projects/UOContent/Gumps/AdminGump.cs +++ b/Projects/UOContent/Gumps/AdminGump.cs @@ -5,6 +5,7 @@ using System.Net; using System.Runtime.InteropServices; using System.Threading; using Server.Accounting; +using Server.Accounting.Security; using Server.Collections; using Server.Commands; using Server.Maps; @@ -226,9 +227,12 @@ namespace Server.Gumps } case AdminGumpPage.Information_Perf: { - AddLabel(20, 130, LabelHue, "Cycles Per Second:"); - AddLabel(40, 150, LabelHue, $"Current: {Core.CyclesPerSecond:N2}"); - AddLabel(40, 170, LabelHue, $"Average: {Core.AverageCPS:N2}"); + var loopStatus = Core.IdleSleepUnsupported ? "Spinning - host cannot honor short waits" : + Core.EventLoopIdleWaitMs == 0 ? "Spinning (configured)" : + Core.IdleSleepSuspended ? "Sleep suspended - host returning waits late" : "Healthy"; + + AddLabel(20, 130, LabelHue, "Event Loop:"); + AddLabel(40, 150, LabelHue, loopStatus); using var sb = ValueStringBuilder.Create(); @@ -2903,7 +2907,7 @@ namespace Server.Gumps else { notice = "The password has been changed."; - a.SetPassword(password); + PasswordWorker.SetPassword(a, password, null); page = AdminGumpPage.AccountDetails_Information; CommandLogging.WriteLine( from, diff --git a/Projects/UOContent/Gumps/Base/BaseGump.cs b/Projects/UOContent/Gumps/Base/BaseGump.cs index 6d844735d..05770608c 100644 --- a/Projects/UOContent/Gumps/Base/BaseGump.cs +++ b/Projects/UOContent/Gumps/Base/BaseGump.cs @@ -13,6 +13,7 @@ * along with this program. If not, see . * *************************************************************************/ +using Server.Logging; using Server.Network; using System; using System.Buffers; @@ -23,10 +24,12 @@ namespace Server.Gumps; public abstract class BaseGump { private static readonly byte[] _packetBuffer = GC.AllocateUninitializedArray(0x10000); + private static readonly ILogger _logger = LogFactory.GetLogger(typeof(BaseGump)); private static Serial nextSerial = (Serial)1; public int TypeID { get; protected set; } public Serial Serial { get; protected set; } + protected bool HasVisualElements { get; set; } public abstract int Switches { get; } public abstract int TextEntries { get; } @@ -56,6 +59,11 @@ public abstract class BaseGump var writer = new SpanWriter(_packetBuffer); Compile(ref writer); + if (!HasVisualElements) + { + _logger.Warning("Sending empty gump {GumpType}", GetType().FullName); + } + ns.Send(writer.Span); writer.Dispose(); diff --git a/Projects/UOContent/Gumps/Base/DynamicGump.cs b/Projects/UOContent/Gumps/Base/DynamicGump.cs index f1b3c064a..de894da68 100644 --- a/Projects/UOContent/Gumps/Base/DynamicGump.cs +++ b/Projects/UOContent/Gumps/Base/DynamicGump.cs @@ -47,6 +47,7 @@ public abstract class DynamicGump : BaseGump BuildLayout(ref gumpBuilder); gumpBuilder.FinalizeLayout(); + HasVisualElements = gumpBuilder.HasVisualElements; _switches = gumpBuilder.Switches; _textEntries = gumpBuilder.TextEntries; diff --git a/Projects/UOContent/Gumps/Base/DynamicGumpBuilder.cs b/Projects/UOContent/Gumps/Base/DynamicGumpBuilder.cs index e98c8c6df..9238e6e04 100644 --- a/Projects/UOContent/Gumps/Base/DynamicGumpBuilder.cs +++ b/Projects/UOContent/Gumps/Base/DynamicGumpBuilder.cs @@ -36,6 +36,7 @@ public ref struct DynamicGumpBuilder public int Switches => _gumpBuilder._switches; public int TextEntries => _gumpBuilder._textEntries; + internal bool HasVisualElements => _gumpBuilder._hasVisualElements; [MethodImpl(MethodImplOptions.AggressiveInlining)] public DynamicGumpBuilder() diff --git a/Projects/UOContent/Gumps/Base/GumpLayoutBuilder.cs b/Projects/UOContent/Gumps/Base/GumpLayoutBuilder.cs index 7f0ca5dff..4bd5c7d8f 100644 --- a/Projects/UOContent/Gumps/Base/GumpLayoutBuilder.cs +++ b/Projects/UOContent/Gumps/Base/GumpLayoutBuilder.cs @@ -30,6 +30,7 @@ public ref struct GumpLayoutBuilder internal GumpFlags _flags; internal int _switches; internal int _textEntries; + internal bool _hasVisualElements; internal Span LayoutData => _layoutBuffer.AsSpan(0, _bytesWritten); @@ -95,6 +96,7 @@ public ref struct GumpLayoutBuilder public void AddBackground(int x, int y, int width, int height, int gumpId) { + _hasVisualElements = true; GrowIfNeeded(9 + 9 + 45); WriteStart("resizepic"u8); WriteValue(x); @@ -109,6 +111,7 @@ public ref struct GumpLayoutBuilder int x, int y, int normalId, int pressedId, int buttonId, GumpButtonType type = GumpButtonType.Reply, int param = 0 ) { + _hasVisualElements = true; GrowIfNeeded(11 + 6 + 54 + 2); WriteStart("button"u8); WriteValue(x); @@ -123,6 +126,7 @@ public ref struct GumpLayoutBuilder public void AddCheckbox(int x, int y, int inactiveId, int activeId, bool selected, int switchId) { + _hasVisualElements = true; GrowIfNeeded(10 + 8 + 45 + 2); WriteStart("checkbox"u8); WriteValue(x); @@ -154,6 +158,7 @@ public ref struct GumpLayoutBuilder public int AddHtmlPlaceholder(int x, int y, int width, int height, bool background = false, bool scrollbar = false) { + _hasVisualElements = true; GrowIfNeeded(11 + 8 + 36 + 10); WriteStart("htmlgump"u8); WriteValue(x); @@ -172,6 +177,7 @@ public ref struct GumpLayoutBuilder public void AddHtml(int x, int y, int width, int height, int text, bool background = false, bool scrollbar = false) { + _hasVisualElements = true; GrowIfNeeded(11 + 8 + 45 + 4); WriteStart("htmlgump"u8); WriteValue(x); @@ -188,6 +194,7 @@ public ref struct GumpLayoutBuilder int x, int y, int width, int height, int number, bool background = false, bool scrollbar = false ) { + _hasVisualElements = true; GrowIfNeeded(11 + 11 + 45 + 4); WriteStart("xmfhtmlgump"u8); WriteValue(x); @@ -204,6 +211,7 @@ public ref struct GumpLayoutBuilder int x, int y, int width, int height, int number, int color, bool background = false, bool scrollbar = false ) { + _hasVisualElements = true; GrowIfNeeded(12 + 16 + 45 + 5 + 4); WriteStart("xmfhtmlgumpcolor"u8); WriteValue(x); @@ -220,6 +228,7 @@ public ref struct GumpLayoutBuilder public void AddHtmlLocalized(int x, int y, int width, int height, int number, ReadOnlySpan args, int color, bool background = false, bool scrollbar = false) { + _hasVisualElements = true; GrowIfNeeded(12 + 10 + 45 + 5 + 4 + (args.Length > 0 ? 3 + args.Length : 0)); WriteStart("xmfhtmltok"u8); WriteValue(x); @@ -254,6 +263,7 @@ public ref struct GumpLayoutBuilder public void AddImage(int x, int y, int gumpId, int hue = 0, ReadOnlySpan cls = default) { + _hasVisualElements = true; GrowIfNeeded(7 + 7 + 36 + (hue != 0 ? 14 : 0) + (cls.Length > 0 ? 7 + cls.Length : 0)); WriteStart("gumppic"u8); WriteValue(x); @@ -294,6 +304,7 @@ public ref struct GumpLayoutBuilder public void AddImageTiledButton(int x, int y, int normalId, int pressedId, int buttonId, GumpButtonType type, int param, int itemId, int hue, int width, int height, int localizedTooltip = -1) { + _hasVisualElements = true; GrowIfNeeded(15 + 13 + 90 + 2); WriteStart("buttontileart"u8); WriteValue(x); @@ -319,6 +330,7 @@ public ref struct GumpLayoutBuilder public void AddImageTiled(int x, int y, int width, int height, int gumpId) { + _hasVisualElements = true; GrowIfNeeded(9 + 12 + 45); WriteStart("gumppictiled"u8); WriteValue(x); @@ -331,6 +343,7 @@ public ref struct GumpLayoutBuilder public void AddItem(int x, int y, int itemId, int hue = 0) { + _hasVisualElements = true; GrowIfNeeded(7 + 36 + (hue != 0 ? 20 : 7)); WriteStart(hue == 0 ? "tilepic"u8 : "tilepichue"u8); WriteValue(x); @@ -355,6 +368,7 @@ public ref struct GumpLayoutBuilder public int AddLabelPlaceholder(int x, int y, int hue) { + _hasVisualElements = true; GrowIfNeeded(8 + 4 + 27 + 6); WriteStart("text"u8); WriteValue(x); @@ -368,6 +382,7 @@ public ref struct GumpLayoutBuilder public void AddLabel(int x, int y, int hue, int text) { + _hasVisualElements = true; GrowIfNeeded(8 + 4 + 36 + 6); WriteStart("text"u8); WriteValue(x); @@ -379,6 +394,7 @@ public ref struct GumpLayoutBuilder public int AddLabelCroppedPlaceholder(int x, int y, int width, int height, int hue) { + _hasVisualElements = true; GrowIfNeeded(10 + 11 + 45 + 6); WriteStart("croppedtext"u8); WriteValue(x); @@ -394,6 +410,7 @@ public ref struct GumpLayoutBuilder public void AddLabelCropped(int x, int y, int width, int height, int hue, int text) { + _hasVisualElements = true; GrowIfNeeded(10 + 11 + 54 + 6); WriteStart("croppedtext"u8); WriteValue(x); @@ -430,6 +447,7 @@ public ref struct GumpLayoutBuilder public void AddRadio(int x, int y, int inactiveId, int activeId, bool selected, int switchId) { + _hasVisualElements = true; GrowIfNeeded(10 + 5 + 45 + 2); WriteStart("radio"u8); WriteValue(x); @@ -445,6 +463,7 @@ public ref struct GumpLayoutBuilder public void AddSpriteImage(int x, int y, int gumpId, int width, int height, int sx, int sy) { + _hasVisualElements = true; GrowIfNeeded(11 + 8 + 63); WriteStart("picinpic"u8); WriteValue(x); @@ -461,6 +480,7 @@ public ref struct GumpLayoutBuilder int x, int y, int width, int height, int hue, int entryId ) { + _hasVisualElements = true; GrowIfNeeded(11 + 9 + 54 + 6); WriteStart("textentry"u8); WriteValue(x); @@ -481,6 +501,7 @@ public ref struct GumpLayoutBuilder int x, int y, int width, int height, int hue, int entryId, int initialText ) { + _hasVisualElements = true; GrowIfNeeded(11 + 9 + 63 + 6); WriteStart("textentry"u8); WriteValue(x); @@ -499,6 +520,7 @@ public ref struct GumpLayoutBuilder int x, int y, int width, int height, int hue, int entryId, int size = 0 ) { + _hasVisualElements = true; GrowIfNeeded(12 + 16 + 63 + 6); WriteStart("textentrylimited"u8); WriteValue(x); @@ -520,6 +542,7 @@ public ref struct GumpLayoutBuilder int x, int y, int width, int height, int hue, int entryId, int initialText, int size = 0 ) { + _hasVisualElements = true; GrowIfNeeded(12 + 16 + 72); WriteStart("textentrylimited"u8); WriteValue(x); diff --git a/Projects/UOContent/Gumps/Base/Legacy/Gump.cs b/Projects/UOContent/Gumps/Base/Legacy/Gump.cs index 5cfb1f14a..3be1ddebf 100644 --- a/Projects/UOContent/Gumps/Base/Legacy/Gump.cs +++ b/Projects/UOContent/Gumps/Base/Legacy/Gump.cs @@ -249,6 +249,7 @@ public class Gump : BaseGump { _textEntries = 0; _switches = 0; + HasVisualElements = false; var layoutWriter = new SpanWriter(_layoutBuffer); @@ -274,6 +275,7 @@ public class Gump : BaseGump foreach (var entry in Entries) { + HasVisualElements |= IsVisualEntry(entry); entry.AppendTo(ref layoutWriter, _stringsList, ref _textEntries, ref _switches); } @@ -311,6 +313,9 @@ public class Gump : BaseGump } } + private static bool IsVisualEntry(GumpEntry entry) => + entry is not (GumpAlphaRegion or GumpECHandleInput or GumpGroup or GumpItemProperty or GumpMasterGump or GumpPage or GumpTooltip); + protected void Reset() { _switches = 0; diff --git a/Projects/UOContent/Gumps/Base/StaticGump.cs b/Projects/UOContent/Gumps/Base/StaticGump.cs index a10491618..0ab2df5e0 100644 --- a/Projects/UOContent/Gumps/Base/StaticGump.cs +++ b/Projects/UOContent/Gumps/Base/StaticGump.cs @@ -30,6 +30,7 @@ public abstract class StaticGump : BaseGump where TSelf : StaticGump : BaseGump where TSelf : StaticGump : BaseGump where TSelf : StaticGump _gumpBuilder._switches; public int TextEntries => _gumpBuilder._textEntries; + internal bool HasVisualElements => _gumpBuilder._hasVisualElements; [MethodImpl(MethodImplOptions.AggressiveInlining)] public StaticGumpBuilder() diff --git a/Projects/UOContent/Items/Addons/BaseAddon.cs b/Projects/UOContent/Items/Addons/BaseAddon.cs index 6b2abbc66..f2a662f6a 100644 --- a/Projects/UOContent/Items/Addons/BaseAddon.cs +++ b/Projects/UOContent/Items/Addons/BaseAddon.cs @@ -63,22 +63,14 @@ namespace Server.Items } } - [SerializableProperty(1)] - [CommandProperty(AccessLevel.GameMaster)] - public CraftResource Resource - { - get => _resource; - set - { - if (_resource != value) - { - _resource = value; - Hue = CraftResources.GetHue(_resource); + [SerializableField(1, fieldChanged: nameof(OnResourceChanged))] + [SerializedCommandProperty(AccessLevel.GameMaster)] + [InvalidateProperties] + private CraftResource _resource; - InvalidateProperties(); - this.MarkDirty(); - } - } + private void OnResourceChanged(CraftResource oldValue, CraftResource newValue) + { + Hue = CraftResources.GetHue(_resource); } Item IAddon.Deed => Deed; diff --git a/Projects/UOContent/Items/Addons/BaseAddonContainer.cs b/Projects/UOContent/Items/Addons/BaseAddonContainer.cs index ad655c24e..8554ad4e5 100644 --- a/Projects/UOContent/Items/Addons/BaseAddonContainer.cs +++ b/Projects/UOContent/Items/Addons/BaseAddonContainer.cs @@ -41,22 +41,14 @@ namespace Server.Items } } - [SerializableProperty(1)] - [CommandProperty(AccessLevel.GameMaster)] - public CraftResource Resource - { - get => _resource; - set - { - if (_resource != value) - { - _resource = value; - Hue = CraftResources.GetHue(_resource); + [SerializableField(1, fieldChanged: nameof(OnResourceChanged))] + [SerializedCommandProperty(AccessLevel.GameMaster)] + [InvalidateProperties] + private CraftResource _resource; - InvalidateProperties(); - this.MarkDirty(); - } - } + private void OnResourceChanged(CraftResource oldValue, CraftResource newValue) + { + Hue = CraftResources.GetHue(_resource); } public virtual bool RetainDeedHue => false; diff --git a/Projects/UOContent/Items/Addons/BaseAddonContainerDeed.cs b/Projects/UOContent/Items/Addons/BaseAddonContainerDeed.cs index c7e08e955..7d5014dee 100644 --- a/Projects/UOContent/Items/Addons/BaseAddonContainerDeed.cs +++ b/Projects/UOContent/Items/Addons/BaseAddonContainerDeed.cs @@ -23,22 +23,14 @@ public abstract partial class BaseAddonContainerDeed : Item, ICraftable public abstract BaseAddonContainer Addon { get; } - [SerializableProperty(0)] - [CommandProperty(AccessLevel.GameMaster)] - public CraftResource Resource - { - get => _resource; - set - { - if (_resource != value) - { - _resource = value; - Hue = CraftResources.GetHue(_resource); + [SerializableField(0, fieldChanged: nameof(OnResourceChanged))] + [SerializedCommandProperty(AccessLevel.GameMaster)] + [InvalidateProperties] + private CraftResource _resource; - InvalidateProperties(); - this.MarkDirty(); - } - } + private void OnResourceChanged(CraftResource oldValue, CraftResource newValue) + { + Hue = CraftResources.GetHue(_resource); } public virtual int OnCraft( diff --git a/Projects/UOContent/Items/Addons/FlourMillEastAddon.cs b/Projects/UOContent/Items/Addons/FlourMillEastAddon.cs index f84c959cf..decf3ca65 100644 --- a/Projects/UOContent/Items/Addons/FlourMillEastAddon.cs +++ b/Projects/UOContent/Items/Addons/FlourMillEastAddon.cs @@ -48,17 +48,19 @@ namespace Server.Items [CommandProperty(AccessLevel.GameMaster)] public int MaxFlour => 2; - [SerializableProperty(0)] - [CommandProperty(AccessLevel.GameMaster)] - public int CurFlour + [SerializableField(0, fieldChanged: nameof(OnCurFlourChanged), allowFieldChange: nameof(AllowCurFlourChange))] + [SerializedCommandProperty(AccessLevel.GameMaster)] + private int _curFlour; + + private bool AllowCurFlourChange(ref int value) { - get => _curFlour; - set - { - _curFlour = Math.Clamp(value, 0, MaxFlour); - UpdateStage(); - this.MarkDirty(); - } + value = Math.Clamp(value, 0, MaxFlour); + return true; + } + + private void OnCurFlourChanged(int oldValue, int newValue) + { + UpdateStage(); } public void StartWorking(Mobile from) diff --git a/Projects/UOContent/Items/Addons/FlourMillSouthAddon.cs b/Projects/UOContent/Items/Addons/FlourMillSouthAddon.cs index cbd0e2cc3..fa48aa83c 100644 --- a/Projects/UOContent/Items/Addons/FlourMillSouthAddon.cs +++ b/Projects/UOContent/Items/Addons/FlourMillSouthAddon.cs @@ -35,16 +35,19 @@ namespace Server.Items [CommandProperty(AccessLevel.GameMaster)] public int MaxFlour => 2; - [SerializableProperty(0)] - [CommandProperty(AccessLevel.GameMaster)] - public int CurFlour + [SerializableField(0, fieldChanged: nameof(OnCurFlourChanged), allowFieldChange: nameof(AllowCurFlourChange))] + [SerializedCommandProperty(AccessLevel.GameMaster)] + private int _curFlour; + + private bool AllowCurFlourChange(ref int value) { - get => _curFlour; - set - { - _curFlour = Math.Max(0, Math.Min(value, MaxFlour)); - UpdateStage(); - } + value = Math.Max(0, Math.Min(value, MaxFlour)); + return true; + } + + private void OnCurFlourChanged(int oldValue, int newValue) + { + UpdateStage(); } public void StartWorking(Mobile from) diff --git a/Projects/UOContent/Items/Addons/SHTeleporter.cs b/Projects/UOContent/Items/Addons/SHTeleporter.cs index f34f9cb85..e6ce612b4 100644 --- a/Projects/UOContent/Items/Addons/SHTeleporter.cs +++ b/Projects/UOContent/Items/Addons/SHTeleporter.cs @@ -25,35 +25,27 @@ namespace Server.Items _teleOffset = offset; } - [SerializableProperty(0)] - [CommandProperty(AccessLevel.GameMaster)] - public bool Active - { - get => _active; - set - { - _active = value; + [SerializableField(0, fieldChanged: nameof(OnActiveChanged))] + [SerializedCommandProperty(AccessLevel.GameMaster)] + private bool _active; - if (Addon is SHTeleporter sourceAddon) - { - sourceAddon.ChangeActive(value); - } + private void OnActiveChanged(bool oldValue, bool newValue) + { + if (Addon is SHTeleporter sourceAddon) + { + sourceAddon.ChangeActive(newValue); } } - [SerializableProperty(1)] - [CommandProperty(AccessLevel.GameMaster)] - public SHTeleComponent TeleDest - { - get => _teleDest; - set - { - _teleDest = value; + [SerializableField(1, fieldChanged: nameof(OnTeleDestChanged))] + [SerializedCommandProperty(AccessLevel.GameMaster)] + private SHTeleComponent _teleDest; - if (Addon is SHTeleporter sourceAddon) - { - sourceAddon.ChangeDest(value); - } + private void OnTeleDestChanged(SHTeleComponent oldValue, SHTeleComponent newValue) + { + if (Addon is SHTeleporter sourceAddon) + { + sourceAddon.ChangeDest(newValue); } } diff --git a/Projects/UOContent/Items/Aquarium/Aquarium.cs b/Projects/UOContent/Items/Aquarium/Aquarium.cs index 749c677c2..054fea588 100644 --- a/Projects/UOContent/Items/Aquarium/Aquarium.cs +++ b/Projects/UOContent/Items/Aquarium/Aquarium.cs @@ -31,9 +31,9 @@ namespace Server.Items private bool m_EvaluateDay; [SerializableField(0, setter: "private")] + [DeserializeTimer(nameof(DeserializeEvaluateTimer), wallClock: true)] private Timer _evaluateTimer; - [DeserializeTimerField(0)] private void DeserializeEvaluateTimer(TimeSpan delay) { _evaluateTimer = Timer.DelayCall(delay, EvaluationInterval, Evaluate); diff --git a/Projects/UOContent/Items/Aquarium/AquariumState.cs b/Projects/UOContent/Items/Aquarium/AquariumState.cs index cdd59d9d9..75fe6fc6f 100644 --- a/Projects/UOContent/Items/Aquarium/AquariumState.cs +++ b/Projects/UOContent/Items/Aquarium/AquariumState.cs @@ -30,19 +30,14 @@ namespace Server.Items public AquariumState(Aquarium parent) => _aquarium = parent; - [SerializableProperty(0)] - [CommandProperty(AccessLevel.GameMaster)] - public int State + [SerializableField(0, allowFieldChange: nameof(AllowStateChange))] + [SerializedCommandProperty(AccessLevel.GameMaster)] + private int _state; + + private bool AllowStateChange(ref int value) { - get => _state; - set - { - if (_state != value) - { - _state = Math.Clamp(value, 0, 4); - MarkDirty(); - } - } + value = Math.Clamp(value, 0, 4); + return true; } [SerializableField(1)] diff --git a/Projects/UOContent/Items/Armor/BaseArmor.Migrations.cs b/Projects/UOContent/Items/Armor/BaseArmor.Migrations.cs index 3135850a9..f15a7fd6a 100644 --- a/Projects/UOContent/Items/Armor/BaseArmor.Migrations.cs +++ b/Projects/UOContent/Items/Armor/BaseArmor.Migrations.cs @@ -1,9 +1,40 @@ +using System; using AMA = Server.Items.ArmorMeditationAllowance; namespace Server.Items; public partial class BaseArmor { + // PlayerConstructed moved onto Item + private void MigrateFrom(V9Content content) + { + _attributes = content.Attributes ?? AttributesDefaultValue(); + _armorAttributes = content.ArmorAttributes ?? ArmorAttributesDefaultValue(); + _physicalBonus = content.PhysicalBonus ?? 0; + _fireBonus = content.FireBonus ?? 0; + _coldBonus = content.ColdBonus ?? 0; + _poisonBonus = content.PoisonBonus ?? 0; + _energyBonus = content.EnergyBonus ?? 0; + _identified = content.Identified; + _maxHitPoints = content.MaxHitPoints ?? 0; + _hitPoints = content.HitPoints ?? 0; + _crafter = content.Crafter; + _quality = content.Quality ?? ArmorQuality.Regular; + _durability = content.Durability ?? ArmorDurabilityLevel.Regular; + _protectionLevel = content.ProtectionLevel ?? ArmorProtectionLevel.Regular; + _resource = content.Resource ?? DefaultResource; + _armorBase = content.BaseArmorRating ?? -1; + _strBonus = content.StrBonus ?? -1; + _dexBonus = content.DexBonus ?? -1; + _intBonus = content.IntBonus ?? -1; + _strReq = content.StrRequirement ?? -1; + _dexReq = content.DexRequirement ?? -1; + _intReq = content.IntRequirement ?? -1; + _meditate = content.MeditationAllowance ?? (AMA)(-1); + _skillBonuses = content.SkillBonuses ?? SkillBonusesDefaultValue(); + PlayerConstructed = content.PlayerConstructed; + } + private void MigrateFrom(V8Content content) { _attributes = content.Attributes ?? AttributesDefaultValue(); @@ -29,7 +60,7 @@ public partial class BaseArmor _intReq = content.IntRequirement ?? -1; _meditate = content.MeditationAllowance ?? (AMA)(-1); _skillBonuses = content.SkillBonuses ?? SkillBonusesDefaultValue(); - _playerConstructed = content.PlayerConstructed; + PlayerConstructed = content.PlayerConstructed; } // Version 7 (pre-codegen) @@ -162,4 +193,37 @@ public partial class BaseArmor PlayerConstructed = GetSaveFlag(flags, OldSaveFlag.PlayerConstructed); } + + private static bool GetSaveFlag(OldSaveFlag flags, OldSaveFlag toGet) => (flags & toGet) != 0; + + [Flags] + private enum OldSaveFlag + { + None = 0x00000000, + Attributes = 0x00000001, + ArmorAttributes = 0x00000002, + PhysicalBonus = 0x00000004, + FireBonus = 0x00000008, + ColdBonus = 0x00000010, + PoisonBonus = 0x00000020, + EnergyBonus = 0x00000040, + Identified = 0x00000080, + MaxHitPoints = 0x00000100, + HitPoints = 0x00000200, + Crafter = 0x00000400, + Quality = 0x00000800, + Durability = 0x00001000, + Protection = 0x00002000, + Resource = 0x00004000, + BaseArmor = 0x00008000, + StrBonus = 0x00010000, + DexBonus = 0x00020000, + IntBonus = 0x00040000, + StrReq = 0x00080000, + DexReq = 0x00100000, + IntReq = 0x00200000, + MedAllowance = 0x00400000, + SkillBonuses = 0x00800000, + PlayerConstructed = 0x01000000 + } } diff --git a/Projects/UOContent/Items/Armor/BaseArmor.cs b/Projects/UOContent/Items/Armor/BaseArmor.cs index 7f01648a0..f4062326d 100644 --- a/Projects/UOContent/Items/Armor/BaseArmor.cs +++ b/Projects/UOContent/Items/Armor/BaseArmor.cs @@ -12,101 +12,98 @@ using AMT = Server.Items.ArmorMaterialType; namespace Server.Items { - [SerializationGenerator(9, false)] + [SerializationGenerator(10, false)] public abstract partial class BaseArmor : Item, IScissorable, IFactionItem, ICraftable, IWearableDurability, IAosItem, IIdentifiable { [SerializedIgnoreDupe] [SerializableField(0, setter: "private")] + [SaveFlag(nameof(ShouldSerializeAosAttributes), nameof(AttributesDefaultValue))] [SerializedCommandProperty(AccessLevel.GameMaster, canModify: true)] private AosAttributes _attributes; - [SerializableFieldSaveFlag(0)] private bool ShouldSerializeAosAttributes() => !_attributes.IsEmpty; - [SerializableFieldDefault(0)] private AosAttributes AttributesDefaultValue() => new(this); [SerializedIgnoreDupe] [SerializableField(1, setter: "private")] + [SaveFlag(nameof(ShouldSerializeArmorAttributes), nameof(ArmorAttributesDefaultValue))] [SerializedCommandProperty(AccessLevel.GameMaster, canModify: true)] private AosArmorAttributes _armorAttributes; - [SerializableFieldSaveFlag(1)] private bool ShouldSerializeArmorAttributes() => !_armorAttributes.IsEmpty; - [SerializableFieldDefault(1)] private AosArmorAttributes ArmorAttributesDefaultValue() => new(this); [EncodedInt] [InvalidateProperties] [SerializableField(2)] + [SaveFlag(nameof(ShouldSerializePhysicalBonus))] [SerializedCommandProperty(AccessLevel.GameMaster)] private int _physicalBonus; - [SerializableFieldSaveFlag(2)] private bool ShouldSerializePhysicalBonus() => _physicalBonus != 0; [EncodedInt] [InvalidateProperties] [SerializableField(3)] + [SaveFlag(nameof(ShouldSerializeFireBonus))] [SerializedCommandProperty(AccessLevel.GameMaster)] private int _fireBonus; - [SerializableFieldSaveFlag(3)] private bool ShouldSerializeFireBonus() => _fireBonus != 0; [EncodedInt] [InvalidateProperties] [SerializableField(4)] + [SaveFlag(nameof(ShouldSerializeColdBonus))] [SerializedCommandProperty(AccessLevel.GameMaster)] private int _coldBonus; - [SerializableFieldSaveFlag(4)] private bool ShouldSerializeColdBonus() => _coldBonus != 0; [EncodedInt] [InvalidateProperties] [SerializableField(5)] + [SaveFlag(nameof(ShouldSerializePoisonBonus))] [SerializedCommandProperty(AccessLevel.GameMaster)] private int _poisonBonus; - [SerializableFieldSaveFlag(5)] private bool ShouldSerializePoisonBonus() => _poisonBonus != 0; [EncodedInt] [InvalidateProperties] [SerializableField(6)] + [SaveFlag(nameof(ShouldSerializeEnergyBonus))] [SerializedCommandProperty(AccessLevel.GameMaster)] private int _energyBonus; - [SerializableFieldSaveFlag(6)] private bool ShouldSerializeEnergyBonus() => _energyBonus != 0; [SerializableField(7)] + [SaveFlag(nameof(ShouldSerializeIdentified))] [SerializedCommandProperty(AccessLevel.GameMaster)] private bool _identified; - [SerializableFieldSaveFlag(7)] private bool ShouldSerializeIdentified() => _identified; [EncodedInt] [SerializableField(8)] + [SaveFlag(nameof(ShouldSerializeMaxHitPoints))] [SerializedCommandProperty(AccessLevel.GameMaster)] private int _maxHitPoints; - [SerializableFieldSaveFlag(8)] private bool ShouldSerializeMaxHitPoints() => _maxHitPoints != 0; [InvalidateProperties] [SerializableField(10)] + [SaveFlag(nameof(ShouldSerializeCrafter))] [SerializedCommandProperty(AccessLevel.GameMaster)] private string _crafter; - [SerializableFieldSaveFlag(10)] private bool ShouldSerializeCrafter() => !string.IsNullOrEmpty(_crafter); - [SerializableFieldSaveFlag(14)] private bool ShouldSerializeResource() => _resource != DefaultResource; // Field 15 @@ -135,22 +132,14 @@ namespace Server.Items [SerializedIgnoreDupe] [SerializableField(23, setter: "private")] + [SaveFlag(nameof(ShouldSerializeSkillBonuses), nameof(SkillBonusesDefaultValue))] [SerializedCommandProperty(AccessLevel.GameMaster, canModify: true)] public AosSkillBonuses _skillBonuses; - [SerializableFieldSaveFlag(23)] private bool ShouldSerializeSkillBonuses() => !_skillBonuses.IsEmpty; - [SerializableFieldDefault(23)] private AosSkillBonuses SkillBonusesDefaultValue() => new(this); - [SerializableField(24)] - [SerializedCommandProperty(AccessLevel.GameMaster)] - public bool _playerConstructed; - - [SerializableFieldSaveFlag(24)] - private bool ShouldSerializePlayerConstructed() => _playerConstructed; - private FactionItem m_FactionState; public BaseArmor(int itemID) : base(itemID) @@ -197,6 +186,7 @@ namespace Server.Items public virtual int OldIntReq => 0; [SerializableProperty(11)] + [SaveFlag(nameof(ShouldSerializeArmorQuality), nameof(QualityDefaultValue))] [CommandProperty(AccessLevel.GameMaster)] public ArmorQuality Quality { @@ -209,13 +199,12 @@ namespace Server.Items } } - [SerializableFieldSaveFlag(11)] private bool ShouldSerializeArmorQuality() => _quality != ArmorQuality.Regular; - [SerializableFieldDefault(11)] private ArmorQuality QualityDefaultValue() => ArmorQuality.Regular; [SerializableProperty(12)] + [SaveFlag(nameof(ShouldSerializeDurability))] [CommandProperty(AccessLevel.GameMaster)] public ArmorDurabilityLevel Durability { @@ -228,33 +217,24 @@ namespace Server.Items } } - [SerializableFieldSaveFlag(12)] private bool ShouldSerializeDurability() => _durability != ArmorDurabilityLevel.Regular; - [SerializableProperty(13)] - [CommandProperty(AccessLevel.GameMaster)] - public ArmorProtectionLevel ProtectionLevel + [SerializableField(13, fieldChanged: nameof(OnProtectionLevelChanged))] + [SaveFlag(nameof(ShouldSerializeProtectionLevel))] + [SerializedCommandProperty(AccessLevel.GameMaster)] + [InvalidateProperties] + private ArmorProtectionLevel _protectionLevel; + + private void OnProtectionLevelChanged(ArmorProtectionLevel oldValue, ArmorProtectionLevel newValue) { - get => _protectionLevel; - set - { - if (_protectionLevel != value) - { - _protectionLevel = value; - - Invalidate(); - InvalidateProperties(); - - (Parent as Mobile)?.UpdateResistances(); - this.MarkDirty(); - } - } + Invalidate(); + (Parent as Mobile)?.UpdateResistances(); } - [SerializableFieldSaveFlag(13)] private bool ShouldSerializeProtectionLevel() => _protectionLevel != ArmorProtectionLevel.Regular; [SerializableProperty(14)] + [SaveFlag(nameof(ShouldSerializeResource), nameof(ResourceDefaultValue))] [CommandProperty(AccessLevel.GameMaster)] public CraftResource Resource { @@ -280,11 +260,11 @@ namespace Server.Items } } - [SerializableFieldDefault(14)] private CraftResource ResourceDefaultValue() => DefaultResource; [EncodedInt] [SerializableProperty(15, useField: nameof(_armorBase))] + [SaveFlag(nameof(ShouldSerializeArmorBase), nameof(ArmorBaseDefaultValue))] [CommandProperty(AccessLevel.GameMaster)] public int BaseArmorRating { @@ -297,10 +277,8 @@ namespace Server.Items } } - [SerializableFieldSaveFlag(15)] private bool ShouldSerializeArmorBase() => _armorBase != -1; - [SerializableFieldDefault(15)] private int ArmorBaseDefaultValue() => -1; public double BaseArmorRatingScaled => BaseArmorRating * ArmorScalar; @@ -350,6 +328,7 @@ namespace Server.Items [EncodedInt] [SerializableProperty(16, useField: nameof(_strBonus))] + [SaveFlag(nameof(ShouldSerializeStrBonus), nameof(StrBonusDefaultValue))] [CommandProperty(AccessLevel.GameMaster)] public int StrBonus { @@ -362,14 +341,13 @@ namespace Server.Items } } - [SerializableFieldSaveFlag(16)] private bool ShouldSerializeStrBonus() => _strBonus != -1; - [SerializableFieldDefault(16)] private int StrBonusDefaultValue() => -1; [EncodedInt] [SerializableProperty(17, useField: nameof(_dexBonus))] + [SaveFlag(nameof(ShouldSerializeDexBonus), nameof(DexBonusDefaultValue))] [CommandProperty(AccessLevel.GameMaster)] public int DexBonus { @@ -382,14 +360,13 @@ namespace Server.Items } } - [SerializableFieldSaveFlag(17)] private bool ShouldSerializeDexBonus() => _dexBonus != -1; - [SerializableFieldDefault(17)] private int DexBonusDefaultValue() => -1; [EncodedInt] [SerializableProperty(18, useField: nameof(_intBonus))] + [SaveFlag(nameof(ShouldSerializeIntBonus), nameof(IntBonusDefaultValue))] [CommandProperty(AccessLevel.GameMaster)] public int IntBonus { @@ -402,14 +379,13 @@ namespace Server.Items } } - [SerializableFieldSaveFlag(18)] private bool ShouldSerializeIntBonus() => _intBonus != -1; - [SerializableFieldDefault(18)] private int IntBonusDefaultValue() => -1; [EncodedInt] [SerializableProperty(19, useField: nameof(_strReq))] + [SaveFlag(nameof(ShouldSerializeStrReq), nameof(StrReqDefaultValue))] [CommandProperty(AccessLevel.GameMaster)] public int StrRequirement { @@ -422,14 +398,13 @@ namespace Server.Items } } - [SerializableFieldSaveFlag(19)] private bool ShouldSerializeStrReq() => _strReq != -1; - [SerializableFieldDefault(19)] private int StrReqDefaultValue() => -1; [EncodedInt] [SerializableProperty(20, useField: nameof(_dexReq))] + [SaveFlag(nameof(ShouldSerializeDexReq), nameof(DexReqDefaultValue))] [CommandProperty(AccessLevel.GameMaster)] public int DexRequirement { @@ -442,14 +417,13 @@ namespace Server.Items } } - [SerializableFieldSaveFlag(20)] private bool ShouldSerializeDexReq() => _dexReq != -1; - [SerializableFieldDefault(20)] private int DexReqDefaultValue() => -1; [EncodedInt] [SerializableProperty(21, useField: nameof(_intReq))] + [SaveFlag(nameof(ShouldSerializeIntReq), nameof(IntReqDefaultValue))] [CommandProperty(AccessLevel.GameMaster)] public int IntRequirement { @@ -462,13 +436,12 @@ namespace Server.Items } } - [SerializableFieldSaveFlag(21)] private bool ShouldSerializeIntReq() => _intReq != -1; - [SerializableFieldDefault(21)] private int IntReqDefaultValue() => -1; [SerializableProperty(22, useField: nameof(_meditate))] + [SaveFlag(nameof(ShouldSerializeMeditationAllowance))] [CommandProperty(AccessLevel.GameMaster)] public AMA MeditationAllowance { @@ -480,7 +453,6 @@ namespace Server.Items } } - [SerializableFieldSaveFlag(22)] private bool ShouldSerializeMeditationAllowance() => _meditate >= AMA.All; public virtual double ArmorScalar @@ -570,7 +542,6 @@ namespace Server.Items var resourceType = typeRes ?? craftItem.Resources[0].ItemType; Resource = CraftResources.GetFromType(resourceType); - PlayerConstructed = true; Identified = true; var context = craftSystem.GetContext(from); @@ -697,6 +668,7 @@ namespace Server.Items [EncodedInt] [SerializableProperty(9)] + [SaveFlag(nameof(ShouldSerializeHitPoints))] [CommandProperty(AccessLevel.GameMaster)] public int HitPoints { @@ -724,7 +696,6 @@ namespace Server.Items } } - [SerializableFieldSaveFlag(9)] private bool ShouldSerializeHitPoints() => _hitPoints != 0; public virtual int InitMinHits => 0; @@ -1026,8 +997,6 @@ namespace Server.Items (Parent as Mobile)?.Delta(MobileDelta.Armor); // Tell them armor rating has changed } - private static bool GetSaveFlag(OldSaveFlag flags, OldSaveFlag toGet) => (flags & toGet) != 0; - [AfterDeserialization] private void AfterDeserialization() { @@ -1514,35 +1483,5 @@ namespace Server.Items }; } - [Flags] - private enum OldSaveFlag - { - None = 0x00000000, - Attributes = 0x00000001, - ArmorAttributes = 0x00000002, - PhysicalBonus = 0x00000004, - FireBonus = 0x00000008, - ColdBonus = 0x00000010, - PoisonBonus = 0x00000020, - EnergyBonus = 0x00000040, - Identified = 0x00000080, - MaxHitPoints = 0x00000100, - HitPoints = 0x00000200, - Crafter = 0x00000400, - Quality = 0x00000800, - Durability = 0x00001000, - Protection = 0x00002000, - Resource = 0x00004000, - BaseArmor = 0x00008000, - StrBonus = 0x00010000, - DexBonus = 0x00020000, - IntBonus = 0x00040000, - StrReq = 0x00080000, - DexReq = 0x00100000, - IntReq = 0x00200000, - MedAllowance = 0x00400000, - SkillBonuses = 0x00800000, - PlayerConstructed = 0x01000000 - } } } diff --git a/Projects/UOContent/Items/Armor/Glasses/ElvenGlasses.cs b/Projects/UOContent/Items/Armor/Glasses/ElvenGlasses.cs index 4b0fb791f..e44967f0b 100644 --- a/Projects/UOContent/Items/Armor/Glasses/ElvenGlasses.cs +++ b/Projects/UOContent/Items/Armor/Glasses/ElvenGlasses.cs @@ -31,13 +31,12 @@ namespace Server.Items public override ArmorMeditationAllowance DefMedAllowance => ArmorMeditationAllowance.All; [SerializableField(0, setter: "private")] + [SaveFlag(nameof(ShouldSerializeWeaponAttributes), nameof(WeaponAttributesDefaultValue))] [SerializedCommandProperty(AccessLevel.GameMaster, canModify: true)] public AosWeaponAttributes _weaponAttributes; - [SerializableFieldSaveFlag(0)] private bool ShouldSerializeWeaponAttributes() => !_weaponAttributes.IsEmpty; - [SerializableFieldDefault(0)] private AosWeaponAttributes WeaponAttributesDefaultValue() => new(this); public override void AppendChildNameProperties(IPropertyList list) diff --git a/Projects/UOContent/Items/Armor/Leather/LeafGloves.cs b/Projects/UOContent/Items/Armor/Leather/LeafGloves.cs index f9abd485b..aa023cdc3 100644 --- a/Projects/UOContent/Items/Armor/Leather/LeafGloves.cs +++ b/Projects/UOContent/Items/Armor/Leather/LeafGloves.cs @@ -33,32 +33,26 @@ namespace Server.Items public override ArmorMeditationAllowance DefMedAllowance => ArmorMeditationAllowance.All; + [SerializableField(0, fieldChanged: nameof(OnCurArcaneChargesChanged))] [EncodedInt] - [SerializableProperty(0)] - [CommandProperty(AccessLevel.GameMaster)] - public int CurArcaneCharges + [SerializedCommandProperty(AccessLevel.GameMaster)] + [InvalidateProperties] + private int _curArcaneCharges; + + private void OnCurArcaneChargesChanged(int oldValue, int newValue) { - get => _curArcaneCharges; - set - { - _curArcaneCharges = value; - InvalidateProperties(); - Update(); - } + Update(); } + [SerializableField(1, fieldChanged: nameof(OnMaxArcaneChargesChanged))] [EncodedInt] - [SerializableProperty(1)] - [CommandProperty(AccessLevel.GameMaster)] - public int MaxArcaneCharges + [SerializedCommandProperty(AccessLevel.GameMaster)] + [InvalidateProperties] + private int _maxArcaneCharges; + + private void OnMaxArcaneChargesChanged(int oldValue, int newValue) { - get => _maxArcaneCharges; - set - { - _maxArcaneCharges = value; - InvalidateProperties(); - Update(); - } + Update(); } [CommandProperty(AccessLevel.GameMaster)] diff --git a/Projects/UOContent/Items/Armor/Leather/LeatherGloves.cs b/Projects/UOContent/Items/Armor/Leather/LeatherGloves.cs index a1ded151b..297bd8ed1 100644 --- a/Projects/UOContent/Items/Armor/Leather/LeatherGloves.cs +++ b/Projects/UOContent/Items/Armor/Leather/LeatherGloves.cs @@ -32,32 +32,26 @@ namespace Server.Items public override ArmorMeditationAllowance DefMedAllowance => ArmorMeditationAllowance.All; + [SerializableField(0, fieldChanged: nameof(OnCurArcaneChargesChanged))] [EncodedInt] - [SerializableProperty(0)] - [CommandProperty(AccessLevel.GameMaster)] - public int CurArcaneCharges + [SerializedCommandProperty(AccessLevel.GameMaster)] + [InvalidateProperties] + private int _curArcaneCharges; + + private void OnCurArcaneChargesChanged(int oldValue, int newValue) { - get => _curArcaneCharges; - set - { - _curArcaneCharges = value; - InvalidateProperties(); - Update(); - } + Update(); } + [SerializableField(1, fieldChanged: nameof(OnMaxArcaneChargesChanged))] [EncodedInt] - [SerializableProperty(1)] - [CommandProperty(AccessLevel.GameMaster)] - public int MaxArcaneCharges + [SerializedCommandProperty(AccessLevel.GameMaster)] + [InvalidateProperties] + private int _maxArcaneCharges; + + private void OnMaxArcaneChargesChanged(int oldValue, int newValue) { - get => _maxArcaneCharges; - set - { - _maxArcaneCharges = value; - InvalidateProperties(); - Update(); - } + Update(); } [CommandProperty(AccessLevel.GameMaster)] diff --git a/Projects/UOContent/Items/Books/BaseBook.cs b/Projects/UOContent/Items/Books/BaseBook.cs index 47f368754..3d9b9f7b8 100644 --- a/Projects/UOContent/Items/Books/BaseBook.cs +++ b/Projects/UOContent/Items/Books/BaseBook.cs @@ -19,41 +19,38 @@ namespace Server.Items [InternString] [InvalidateProperties] [SerializableField(1)] + [SaveFlag(nameof(ShouldSerializeTitle), nameof(TitleDefaultValue))] [SerializedCommandProperty(AccessLevel.GameMaster)] private string _title; - [SerializableFieldSaveFlag(1)] private bool ShouldSerializeTitle() => _title != DefaultContent?.Title; - [SerializableFieldDefault(1)] private string TitleDefaultValue() => DefaultContent?.Title; [InvalidateProperties] [SerializableField(2)] + [SaveFlag(nameof(ShouldSerializeAuthor), nameof(AuthorDefaultValue))] [SerializedCommandProperty(AccessLevel.GameMaster)] private string _author; - [SerializableFieldSaveFlag(2)] private bool ShouldSerializeAuthor() => _author != DefaultContent?.Author; - [SerializableFieldDefault(2)] private string AuthorDefaultValue() => DefaultContent?.Author; [SerializableField(3)] + [SaveFlag(nameof(ShouldSerializeWritable))] [SerializedCommandProperty(AccessLevel.GameMaster)] private bool _writable; - [SerializableFieldSaveFlag(3)] private bool ShouldSerializeWritable() => _writable; [SerializedIgnoreDupe] [SerializableField(4, setter: "protected")] + [SaveFlag(nameof(ShouldSerializePages), nameof(PagesDefaultValue))] private BookPageInfo[] _pages; - [SerializableFieldSaveFlag(4)] private bool ShouldSerializePages() => DefaultContent?.IsMatch(_pages) != true; - [SerializableFieldDefault(4)] private BookPageInfo[] PagesDefaultValue() => DefaultContent?.Copy() ?? Array.Empty(); [Constructible] diff --git a/Projects/UOContent/Items/Clothing/BaseClothing.Migrations.cs b/Projects/UOContent/Items/Clothing/BaseClothing.Migrations.cs index e267cd1aa..7c431f076 100644 --- a/Projects/UOContent/Items/Clothing/BaseClothing.Migrations.cs +++ b/Projects/UOContent/Items/Clothing/BaseClothing.Migrations.cs @@ -1,7 +1,25 @@ +using System; + namespace Server.Items; public partial class BaseClothing { + // PlayerConstructed moved onto Item + private void MigrateFrom(V7Content content) + { + _resource = content.Resource ?? DefaultResource; + _attributes = content.Attributes ?? AttributesDefaultValue(); + _clothingAttributes = content.ClothingAttributes ?? ClothingAttributesDefaultValue(); + _skillBonuses = content.SkillBonuses ?? SkillBonusesDefaultValue(); + _resistances = content.Resistances ?? ResistancesDefaultValue(); + _maxHitPoints = content.MaxHitPoints ?? 0; + _hitPoints = content.HitPoints ?? 0; + PlayerConstructed = content.PlayerConstructed; + _crafter = content.Crafter; + _quality = content.Quality ?? ClothingQuality.Regular; + _strReq = content.StrRequirement ?? -1; + } + private void MigrateFrom(V6Content content) { _resource = content.RawResource ?? DefaultResource; @@ -10,7 +28,7 @@ public partial class BaseClothing _skillBonuses = content.SkillBonuses ?? SkillBonusesDefaultValue(); _resistances = content.Resistances ?? ResistancesDefaultValue(); _maxHitPoints = content.MaxHitPoints ?? 0; - _playerConstructed = content.PlayerConstructed; + PlayerConstructed = content.PlayerConstructed; Timer.DelayCall((item, crafter) => item._crafter = crafter?.RawName, this, content.Crafter); _quality = content.Quality ?? ClothingQuality.Regular; _strReq = content.StrRequirement ?? -1; @@ -85,4 +103,23 @@ public partial class BaseClothing PlayerConstructed = GetSaveFlag(flags, OldSaveFlag.PlayerConstructed); } + + private static bool GetSaveFlag(OldSaveFlag flags, OldSaveFlag toGet) => (flags & toGet) != 0; + + [Flags] + private enum OldSaveFlag + { + None = 0x00000000, + Resource = 0x00000001, + Attributes = 0x00000002, + ClothingAttributes = 0x00000004, + SkillBonuses = 0x00000008, + Resistances = 0x00000010, + MaxHitPoints = 0x00000020, + HitPoints = 0x00000040, + PlayerConstructed = 0x00000080, + Crafter = 0x00000100, + Quality = 0x00000200, + StrReq = 0x00000400 + } } diff --git a/Projects/UOContent/Items/Clothing/BaseClothing.cs b/Projects/UOContent/Items/Clothing/BaseClothing.cs index 725eed100..983568d2d 100644 --- a/Projects/UOContent/Items/Clothing/BaseClothing.cs +++ b/Projects/UOContent/Items/Clothing/BaseClothing.cs @@ -22,90 +22,78 @@ namespace Server.Items int MaxArcaneCharges { get; set; } } - [SerializationGenerator(7, false)] + [SerializationGenerator(8, false)] public abstract partial class BaseClothing : Item, IDyable, IScissorable, IFactionItem, ICraftable, IWearableDurability, IAosItem { - [SerializableFieldSaveFlag(0)] private bool ShouldSerializeResource() => _resource != DefaultResource; [SerializedIgnoreDupe] [SerializableField(1, setter: "private")] + [SaveFlag(nameof(ShouldSerializeAttributes), nameof(AttributesDefaultValue))] [SerializedCommandProperty(AccessLevel.GameMaster, canModify: true)] private AosAttributes _attributes; - [SerializableFieldSaveFlag(1)] private bool ShouldSerializeAttributes() => !_attributes.IsEmpty; - [SerializableFieldDefault(1)] private AosAttributes AttributesDefaultValue() => new(this); [SerializedIgnoreDupe] [SerializableField(2, setter: "private")] + [SaveFlag(nameof(ShouldSerializeClothingAttributes), nameof(ClothingAttributesDefaultValue))] [SerializedCommandProperty(AccessLevel.GameMaster, canModify: true)] private AosArmorAttributes _clothingAttributes; - [SerializableFieldSaveFlag(2)] private bool ShouldSerializeClothingAttributes() => !_clothingAttributes.IsEmpty; - [SerializableFieldDefault(2)] private AosArmorAttributes ClothingAttributesDefaultValue() => new(this); [SerializedIgnoreDupe] [SerializableField(3, setter: "private")] + [SaveFlag(nameof(ShouldSerializeSkillBonuses), nameof(SkillBonusesDefaultValue))] [SerializedCommandProperty(AccessLevel.GameMaster, canModify: true)] private AosSkillBonuses _skillBonuses; - [SerializableFieldSaveFlag(3)] private bool ShouldSerializeSkillBonuses() => !_skillBonuses.IsEmpty; - [SerializableFieldDefault(3)] private AosSkillBonuses SkillBonusesDefaultValue() => new(this); [SerializedIgnoreDupe] [SerializableField(4, setter: "private")] + [SaveFlag(nameof(ShouldSerializeResistances), nameof(ResistancesDefaultValue))] [SerializedCommandProperty(AccessLevel.GameMaster, canModify: true)] private AosElementAttributes _resistances; - [SerializableFieldSaveFlag(4)] private bool ShouldSerializeResistances() => !_resistances.IsEmpty; - [SerializableFieldDefault(4)] private AosElementAttributes ResistancesDefaultValue() => new(this); [EncodedInt] [InvalidateProperties] [SerializableField(5)] + [SaveFlag(nameof(ShouldSerializeMaxHitPoints))] [SerializedCommandProperty(AccessLevel.GameMaster)] private int _maxHitPoints; - [SerializableFieldSaveFlag(5)] private bool ShouldSerializeMaxHitPoints() => _maxHitPoints != 0; - [SerializableField(7)] - [SerializedCommandProperty(AccessLevel.GameMaster)] - private bool _playerConstructed; - - [SerializableFieldSaveFlag(7)] - private bool ShouldSerializePlayerConstructed() => _playerConstructed; - [InvalidateProperties] - [SerializableField(8)] + [SerializableField(7)] + [SaveFlag(nameof(ShouldSerializeCrafter))] [SerializedCommandProperty(AccessLevel.GameMaster)] private string _crafter; - [SerializableFieldSaveFlag(8)] private bool ShouldSerializeCrafter() => !string.IsNullOrEmpty(_crafter); [InvalidateProperties] - [SerializableField(9)] + [SerializableField(8)] + [SaveFlag(nameof(ShouldSerializeQuality))] [SerializedCommandProperty(AccessLevel.GameMaster)] private ClothingQuality _quality = ClothingQuality.Regular; - [SerializableFieldSaveFlag(9)] private bool ShouldSerializeQuality() => _quality != ClothingQuality.Regular; - // Field 10 + // Field 9 private int _strReq = -1; private FactionItem _factionState; @@ -125,21 +113,19 @@ namespace Server.Items Resistances = new AosElementAttributes(this); } - [SerializableProperty(0)] - [CommandProperty(AccessLevel.GameMaster)] - public CraftResource Resource + [SerializableField(0, fieldChanged: nameof(OnResourceChanged))] + [SaveFlag(nameof(ShouldSerializeResource))] + [SerializedCommandProperty(AccessLevel.GameMaster)] + [InvalidateProperties] + private CraftResource _resource; + + private void OnResourceChanged(CraftResource oldValue, CraftResource newValue) { - get => _resource; - set - { - _resource = value; - Hue = CraftResources.GetHue(_resource); - InvalidateProperties(); - this.MarkDirty(); - } + Hue = CraftResources.GetHue(_resource); } - [SerializableProperty(10, useField: nameof(_strReq))] + [SerializableProperty(9, useField: nameof(_strReq))] + [SaveFlag(nameof(ShouldSerializeStrReq))] [CommandProperty(AccessLevel.GameMaster)] public int StrRequirement { @@ -152,7 +138,6 @@ namespace Server.Items } } - [SerializableFieldSaveFlag(10)] private bool ShouldSerializeStrReq() => _strReq != -1; public virtual CraftResource DefaultResource => CraftResource.None; @@ -207,8 +192,6 @@ namespace Server.Items Hue = resHue; } - PlayerConstructed = true; - var context = craftSystem.GetContext(from); if (context?.DoNotColor == true) @@ -308,6 +291,7 @@ namespace Server.Items [EncodedInt] [SerializableProperty(6)] + [SaveFlag(nameof(ShouldSerializeHitPoints))] [CommandProperty(AccessLevel.GameMaster)] public int HitPoints { @@ -333,7 +317,6 @@ namespace Server.Items } } - [SerializableFieldSaveFlag(6)] private bool ShouldSerializeHitPoints() => _hitPoints != 0; public virtual int InitMinHits => 0; @@ -889,8 +872,6 @@ namespace Server.Items InvalidateProperties(); } - private static bool GetSaveFlag(OldSaveFlag flags, OldSaveFlag toGet) => (flags & toGet) != 0; - [AfterDeserialization] private void AfterDeserialization() { @@ -911,21 +892,5 @@ namespace Server.Items } } - [Flags] - private enum OldSaveFlag - { - None = 0x00000000, - Resource = 0x00000001, - Attributes = 0x00000002, - ClothingAttributes = 0x00000004, - SkillBonuses = 0x00000008, - Resistances = 0x00000010, - MaxHitPoints = 0x00000020, - HitPoints = 0x00000040, - PlayerConstructed = 0x00000080, - Crafter = 0x00000100, - Quality = 0x00000200, - StrReq = 0x00000400 - } } } diff --git a/Projects/UOContent/Items/Clothing/Cloaks.cs b/Projects/UOContent/Items/Clothing/Cloaks.cs index 67f572d9d..a92e8fc8c 100644 --- a/Projects/UOContent/Items/Clothing/Cloaks.cs +++ b/Projects/UOContent/Items/Clothing/Cloaks.cs @@ -22,34 +22,26 @@ namespace Server.Items public override double DefaultWeight => 5.0; + [SerializableField(0, fieldChanged: nameof(OnCurArcaneChargesChanged))] [EncodedInt] - [SerializableProperty(0)] - [CommandProperty(AccessLevel.GameMaster)] - public int CurArcaneCharges + [SerializedCommandProperty(AccessLevel.GameMaster)] + [InvalidateProperties] + private int _curArcaneCharges; + + private void OnCurArcaneChargesChanged(int oldValue, int newValue) { - get => _curArcaneCharges; - set - { - _curArcaneCharges = value; - this.MarkDirty(); - InvalidateProperties(); - Update(); - } + Update(); } + [SerializableField(1, fieldChanged: nameof(OnMaxArcaneChargesChanged))] [EncodedInt] - [SerializableProperty(1)] - [CommandProperty(AccessLevel.GameMaster)] - public int MaxArcaneCharges + [SerializedCommandProperty(AccessLevel.GameMaster)] + [InvalidateProperties] + private int _maxArcaneCharges; + + private void OnMaxArcaneChargesChanged(int oldValue, int newValue) { - get => _maxArcaneCharges; - set - { - _maxArcaneCharges = value; - this.MarkDirty(); - InvalidateProperties(); - Update(); - } + Update(); } [CommandProperty(AccessLevel.GameMaster)] diff --git a/Projects/UOContent/Items/Clothing/OuterTorso.cs b/Projects/UOContent/Items/Clothing/OuterTorso.cs index 4fda45bb5..f3b0b077f 100644 --- a/Projects/UOContent/Items/Clothing/OuterTorso.cs +++ b/Projects/UOContent/Items/Clothing/OuterTorso.cs @@ -37,21 +37,22 @@ namespace Server.Items public override double DefaultWeight => 3.0; } - [SerializationGenerator(3, false)] + [SerializationGenerator(4, false)] public partial class DeathRobe : Robe { private static readonly TimeSpan m_DefaultDecayTime = TimeSpan.FromMinutes(1.0); - [TimerDrift] [SerializableField(0)] + [DeserializeTimer(nameof(DeserializeDecayTimer))] private Timer _decayTimer; - [DeserializeTimerField(0)] - private void DeserializeDecayTimer(TimeSpan delay) + private void DeserializeDecayTimer(TimeSpan delay) => BeginDecay(delay); + + private void MigrateFrom(V3Content content) { - if (delay != TimeSpan.MinValue) + if (content.DecayTimerDelay != TimeSpan.MinValue) { - BeginDecay(delay); + DeserializeDecayTimer(content.DecayTimerDelay); } } @@ -324,34 +325,26 @@ namespace Server.Items public override double DefaultWeight => 3.0; + [SerializableField(0, fieldChanged: nameof(OnCurArcaneChargesChanged))] [EncodedInt] - [SerializableProperty(0)] - [CommandProperty(AccessLevel.GameMaster)] - public int CurArcaneCharges + [SerializedCommandProperty(AccessLevel.GameMaster)] + [InvalidateProperties] + private int _curArcaneCharges; + + private void OnCurArcaneChargesChanged(int oldValue, int newValue) { - get => _curArcaneCharges; - set - { - _curArcaneCharges = value; - InvalidateProperties(); - Update(); - this.MarkDirty(); - } + Update(); } + [SerializableField(1, fieldChanged: nameof(OnMaxArcaneChargesChanged))] [EncodedInt] - [SerializableProperty(1)] - [CommandProperty(AccessLevel.GameMaster)] - public int MaxArcaneCharges + [SerializedCommandProperty(AccessLevel.GameMaster)] + [InvalidateProperties] + private int _maxArcaneCharges; + + private void OnMaxArcaneChargesChanged(int oldValue, int newValue) { - get => _maxArcaneCharges; - set - { - _maxArcaneCharges = value; - InvalidateProperties(); - Update(); - this.MarkDirty(); - } + Update(); } [CommandProperty(AccessLevel.GameMaster)] diff --git a/Projects/UOContent/Items/Clothing/Shoes.cs b/Projects/UOContent/Items/Clothing/Shoes.cs index 4d80b4699..f791b3a62 100644 --- a/Projects/UOContent/Items/Clothing/Shoes.cs +++ b/Projects/UOContent/Items/Clothing/Shoes.cs @@ -54,11 +54,10 @@ namespace Server.Items { [EncodedInt] [InvalidateProperties] - [SerializableField(0)] + [SerializableField(0, fieldChanged: nameof(OnCurArcaneChargesChanged))] [SerializedCommandProperty(AccessLevel.GameMaster)] private int _curArcaneCharges; - [SerializableFieldChanged(0)] [MethodImpl(MethodImplOptions.AggressiveInlining)] private void OnCurArcaneChargesChanged(int oldValue, int newValue) => Update(); @@ -71,19 +70,15 @@ namespace Server.Items public override CraftResource DefaultResource => CraftResource.RegularLeather; + [SerializableField(1, fieldChanged: nameof(OnMaxArcaneChargesChanged))] [EncodedInt] - [SerializableProperty(1)] - [CommandProperty(AccessLevel.GameMaster)] - public int MaxArcaneCharges + [SerializedCommandProperty(AccessLevel.GameMaster)] + [InvalidateProperties] + private int _maxArcaneCharges; + + private void OnMaxArcaneChargesChanged(int oldValue, int newValue) { - get => _maxArcaneCharges; - set - { - _maxArcaneCharges = value; - InvalidateProperties(); - Update(); - this.MarkDirty(); - } + Update(); } [CommandProperty(AccessLevel.GameMaster)] diff --git a/Projects/UOContent/Items/Construction/Doors/BaseDoor.cs b/Projects/UOContent/Items/Construction/Doors/BaseDoor.cs index dc3449c24..ef779760b 100644 --- a/Projects/UOContent/Items/Construction/Doors/BaseDoor.cs +++ b/Projects/UOContent/Items/Construction/Doors/BaseDoor.cs @@ -74,43 +74,31 @@ public abstract partial class BaseDoor : Item, ILockable, ITelekinesisable Movable = false; } - [SerializableProperty(1)] - [CommandProperty(AccessLevel.GameMaster)] - public bool Open + [SerializableField(1, fieldChanged: nameof(OnOpenChanged))] + [SerializedCommandProperty(AccessLevel.GameMaster)] + private bool _open; + + private void OnOpenChanged(bool oldValue, bool newValue) { - get => _open; - set + ItemID = _open ? _openedId : _closedId; + if (_open) { - if (_open != value) - { - _open = value; - - ItemID = _open ? _openedId : _closedId; - - if (_open) - { - Location = new Point3D(X + _offset.X, Y + _offset.Y, Z + _offset.Z); - } - else - { - Location = new Point3D(X - _offset.X, Y - _offset.Y, Z - _offset.Z); - } - - Effects.PlaySound(this, _open ? OpenedSound : ClosedSound); - - if (_open) - { - _timer ??= new InternalTimer(this); - _timer.Start(); - } - else - { - _timer.Stop(); - _timer = null; - } - - this.MarkDirty(); - } + Location = new Point3D(X + _offset.X, Y + _offset.Y, Z + _offset.Z); + } + else + { + Location = new Point3D(X - _offset.X, Y - _offset.Y, Z - _offset.Z); + } + Effects.PlaySound(this, _open ? OpenedSound : ClosedSound); + if (_open) + { + _timer ??= new InternalTimer(this); + _timer.Start(); + } + else + { + _timer.Stop(); + _timer = null; } } diff --git a/Projects/UOContent/Items/Containers/Fillable Containers/FillableContainer.cs b/Projects/UOContent/Items/Containers/Fillable Containers/FillableContainer.cs index a9da67dbb..da4d8b77f 100644 --- a/Projects/UOContent/Items/Containers/Fillable Containers/FillableContainer.cs +++ b/Projects/UOContent/Items/Containers/Fillable Containers/FillableContainer.cs @@ -3,19 +3,22 @@ using ModernUO.Serialization; namespace Server.Items; -[SerializationGenerator(2, false)] +[SerializationGenerator(3, false)] public abstract partial class FillableContainer : LockableContainer { - [TimerDrift] [SerializableField(1)] + [DeserializeTimer(nameof(DeserializeRespawnTimer))] private Timer _respawnTimer; - [DeserializeTimerField(1)] - private void DeserializeRespawnTimer(TimeSpan delay) + private void DeserializeRespawnTimer(TimeSpan delay) => _respawnTimer = Timer.DelayCall(delay, Respawn); + + private void MigrateFrom(V2Content content) { - if (delay > TimeSpan.MinValue) + _contentType = content.ContentType; + + if (content.RespawnTimerDelay != TimeSpan.MinValue) { - _respawnTimer = Timer.DelayCall(delay, Respawn); + DeserializeRespawnTimer(content.RespawnTimerDelay); } } diff --git a/Projects/UOContent/Items/Containers/MarkContainer.cs b/Projects/UOContent/Items/Containers/MarkContainer.cs index fa3f7e440..04e7c89ea 100644 --- a/Projects/UOContent/Items/Containers/MarkContainer.cs +++ b/Projects/UOContent/Items/Containers/MarkContainer.cs @@ -3,14 +3,13 @@ using ModernUO.Serialization; namespace Server.Items; -[SerializationGenerator(0, false)] +[SerializationGenerator(1, false)] public partial class MarkContainer : LockableContainer { - [TimerDrift] [SerializableField(1, getter: "private", setter: "private")] + [DeserializeTimer(nameof(DeserializeRelockTimer))] private InternalTimer _relockTimer; - [DeserializeTimerField(1)] private void DeserializeRelockTimer(TimeSpan delay) { if (!Locked && _autoLock) @@ -19,6 +18,19 @@ public partial class MarkContainer : LockableContainer } } + private void MigrateFrom(V0Content content) + { + _autoLock = content.AutoLock; + _targetMap = content.TargetMap; + _target = content.Target; + _description = content.Description; + + if (content.RelockTimerDelay != TimeSpan.MinValue) + { + DeserializeRelockTimer(content.RelockTimerDelay); + } + } + [SerializableField(2)] [SerializedCommandProperty(AccessLevel.GameMaster)] private Map _targetMap; @@ -50,23 +62,19 @@ public partial class MarkContainer : LockableContainer } } - [SerializableProperty(0)] - [CommandProperty(AccessLevel.GameMaster)] - public bool AutoLock - { - get => _autoLock; - set - { - _autoLock = value; + [SerializableField(0, fieldChanged: nameof(OnAutoLockChanged))] + [SerializedCommandProperty(AccessLevel.GameMaster)] + private bool _autoLock; - if (!_autoLock) - { - StopTimer(); - } - else if (!Locked) - { - _relockTimer ??= new InternalTimer(this); - } + private void OnAutoLockChanged(bool oldValue, bool newValue) + { + if (!_autoLock) + { + StopTimer(); + } + else if (!Locked) + { + _relockTimer ??= new InternalTimer(this); } } diff --git a/Projects/UOContent/Items/Containers/TreasureMapChest.cs b/Projects/UOContent/Items/Containers/TreasureMapChest.cs index b59911a8a..a1aafa0b3 100644 --- a/Projects/UOContent/Items/Containers/TreasureMapChest.cs +++ b/Projects/UOContent/Items/Containers/TreasureMapChest.cs @@ -9,10 +9,11 @@ using Server.Network; namespace Server.Items; -[SerializationGenerator(2, false)] +[SerializationGenerator(4, false)] public partial class TreasureMapChest : LockableContainer { [Tidy] + [CanBeNull] [SerializableField(0, setter: "private")] private List _guardians; @@ -28,12 +29,11 @@ public partial class TreasureMapChest : LockableContainer [SerializedCommandProperty(AccessLevel.GameMaster)] private int _level; - [TimerDrift] [SerializableField(4)] [SerializedCommandProperty(AccessLevel.GameMaster)] + [DeserializeTimer(nameof(DeserializeExpireTimer))] private Timer _expireTimer; - [DeserializeTimerField(4)] private void DeserializeExpireTimer(TimeSpan delay) { if (!_temporary) @@ -42,11 +42,29 @@ public partial class TreasureMapChest : LockableContainer } } + private void MigrateFrom(V3Content content) + { + _guardians = content.Guardians; + _temporary = content.Temporary; + _owner = content.Owner; + _level = content.Level; + _lifted = content.Lifted; + + if (content.ExpireTimerDelay != TimeSpan.MinValue) + { + DeserializeExpireTimer(content.ExpireTimerDelay); + } + } + [Tidy] + [CanBeNull] [SerializableField(5, setter: "private")] [SerializedCommandProperty(AccessLevel.GameMaster)] private HashSet _lifted; + // False only while the constructor fills the chest; deserialized chests are always filled. + private bool _filled; + [Constructible] public TreasureMapChest(int level) : this(null, level) { @@ -58,10 +76,10 @@ public partial class TreasureMapChest : LockableContainer _level = level; _temporary = temporary; - _guardians = []; _expireTimer = Timer.DelayCall(TimeSpan.FromHours(3.0), Delete); Fill(this, level); + _filled = true; } public override int LabelNumber => 3000541; @@ -182,7 +200,6 @@ public partial class TreasureMapChest : LockableContainer 2 => 76, 3 => 84, 4 => 92, - 5 => 100, _ => 100 }; @@ -300,14 +317,17 @@ public partial class TreasureMapChest : LockableContainer if (_level == 0 && from.AccessLevel < AccessLevel.GameMaster) { - for (var i = 0; i < _guardians.Count; i++) + if (_guardians != null) { - var m = _guardians[i]; - if (m.Alive) + for (var i = 0; i < _guardians.Count; i++) { - // You must first kill the guardians before you may open this chest. - from.SendLocalizedMessage(1046448); - return true; + var m = _guardians[i]; + if (m.Alive) + { + // You must first kill the guardians before you may open this chest. + from.SendLocalizedMessage(1046448); + return true; + } } } @@ -361,12 +381,21 @@ public partial class TreasureMapChest : LockableContainer public override bool CheckLift(Mobile from, Item item, ref LRReason reject) => CheckLoot(from, true) && base.CheckLift(from, item, ref reject); + public override void OnItemAdded(Item item) + { + base.OnItemAdded(item); + + if (_filled) + { + _lifted ??= []; + _lifted.Add(item); + } + } + public override void OnItemLifted(Mobile from, Item item) { - var notYetLifted = _lifted?.Contains(item) != true; from.RevealingAction(); - - if (notYetLifted) + if (_lifted?.Contains(item) != true) { _lifted ??= []; _lifted.Add(item); @@ -393,8 +422,6 @@ public partial class TreasureMapChest : LockableContainer private void Deserialize(IGenericReader reader, int version) { - _guardians = []; - _owner = reader.ReadEntity(); _level = reader.ReadInt(); var expireTimerNext = reader.ReadDeltaTime(); @@ -402,12 +429,34 @@ public partial class TreasureMapChest : LockableContainer _lifted = reader.ReadEntitySet(); } - [AfterDeserialization(false)] + private void MigrateFrom(V2Content content) + { + _guardians = content.Guardians; + if (_guardians.Count == 0) + { + _guardians = null; + } + _temporary = content.Temporary; + _owner = content.Owner; + _level = content.Level; + _lifted = content.Lifted; + if (_lifted.Count == 0) + { + _lifted = null; + } + + var expireTimerDelay = content.ExpireTimerDelay; + DeserializeExpireTimer(expireTimerDelay); + } + + [AfterDeserialization] private void AfterDeserialization() { + _filled = true; + if (_expireTimer == null) { - Delete(); + Timer.DelayCall(Delete); } } diff --git a/Projects/UOContent/Items/Deeds/DragonBardingDeed.cs b/Projects/UOContent/Items/Deeds/DragonBardingDeed.cs index 6599e6c56..5ed370153 100644 --- a/Projects/UOContent/Items/Deeds/DragonBardingDeed.cs +++ b/Projects/UOContent/Items/Deeds/DragonBardingDeed.cs @@ -28,17 +28,14 @@ public partial class DragonBardingDeed : Item, ICraftable public override int LabelNumber => _exceptional ? 1053181 : 1053012; // dragon barding deed - [SerializableProperty(2)] - [CommandProperty(AccessLevel.GameMaster)] - public CraftResource Resource + [SerializableField(2, fieldChanged: nameof(OnResourceChanged))] + [SerializedCommandProperty(AccessLevel.GameMaster)] + [InvalidateProperties] + private CraftResource _resource; + + private void OnResourceChanged(CraftResource oldValue, CraftResource newValue) { - get => _resource; - set - { - _resource = value; - Hue = CraftResources.GetHue(value); - InvalidateProperties(); - } + Hue = CraftResources.GetHue(newValue); } public int OnCraft( diff --git a/Projects/UOContent/Items/Food/Beverage.cs b/Projects/UOContent/Items/Food/Beverage.cs index 3a9f50297..85eecf17f 100644 --- a/Projects/UOContent/Items/Food/Beverage.cs +++ b/Projects/UOContent/Items/Food/Beverage.cs @@ -324,27 +324,21 @@ public abstract partial class BaseBeverage : Item, IHasQuantity [CommandProperty(AccessLevel.GameMaster)] public bool IsFull => _quantity >= MaxQuantity; - [SerializableProperty(2)] - [CommandProperty(AccessLevel.GameMaster)] - public BeverageType Content + [SerializableField(2, fieldChanged: nameof(OnContentChanged))] + [SerializedCommandProperty(AccessLevel.GameMaster)] + [InvalidateProperties] + private BeverageType _content; + + private void OnContentChanged(BeverageType oldValue, BeverageType newValue) { - get => _content; - set + var itemID = ComputeItemID(); + if (itemID > 0) { - _content = value; - - InvalidateProperties(); - - var itemID = ComputeItemID(); - - if (itemID > 0) - { - ItemID = itemID; - } - else - { - Delete(); - } + ItemID = itemID; + } + else + { + Delete(); } } diff --git a/Projects/UOContent/Items/Food/Cooking.cs b/Projects/UOContent/Items/Food/Cooking.cs index 6cff16d72..cd010432d 100644 --- a/Projects/UOContent/Items/Food/Cooking.cs +++ b/Projects/UOContent/Items/Food/Cooking.cs @@ -76,25 +76,25 @@ public partial class SackFlour : Item, IHasQuantity public override double DefaultWeight => 5.0; - [SerializableProperty(0)] - [CommandProperty(AccessLevel.GameMaster)] - public int Quantity + [SerializableField(0, fieldChanged: nameof(OnQuantityChanged), allowFieldChange: nameof(AllowQuantityChange))] + [SerializedCommandProperty(AccessLevel.GameMaster)] + private int _quantity; + + private bool AllowQuantityChange(ref int value) { - get => _quantity; - set + value = Math.Min(20, Math.Max(0, value)); + return true; + } + + private void OnQuantityChanged(int oldValue, int newValue) + { + if (_quantity == 0) { - _quantity = Math.Min(20, Math.Max(0, value)); - - if (_quantity == 0) - { - Delete(); - } - else if (_quantity < 20 && ItemID is 0x1039 or 0x1045) - { - ++ItemID; - } - - this.MarkDirty(); + Delete(); + } + else if (_quantity < 20 && ItemID is 0x1039 or 0x1045) + { + ++ItemID; } } diff --git a/Projects/UOContent/Items/Games/Mahjong/MahjongGame.cs b/Projects/UOContent/Items/Games/Mahjong/MahjongGame.cs index de7e663b7..89e4d6f8f 100644 --- a/Projects/UOContent/Items/Games/Mahjong/MahjongGame.cs +++ b/Projects/UOContent/Items/Games/Mahjong/MahjongGame.cs @@ -55,55 +55,33 @@ public partial class MahjongGame : Item, ISecurable public override double DefaultWeight => 5.0; - [CommandProperty(AccessLevel.GameMaster)] - [SerializableProperty(6)] - public bool ShowScores + [SerializableField(6, fieldChanged: nameof(OnShowScoresChanged))] + [SerializedCommandProperty(AccessLevel.GameMaster)] + private bool _showScores; + + private void OnShowScoresChanged(bool oldValue, bool newValue) { - get => _showScores; - set + if (newValue) { - if (_showScores == value) - { - return; - } - - _showScores = value; - - if (value) - { - _players.SendPlayersPacket(true, true); - } - - _players.SendGeneralPacket(true, true); - _players.SendLocalizedMessage(value ? 1062777 : 1062778); // The dealer has enabled/disabled score display. - this.MarkDirty(); + _players.SendPlayersPacket(true, true); } + _players.SendGeneralPacket(true, true); + _players.SendLocalizedMessage(newValue ? 1062777 : 1062778); // The dealer has enabled/disabled score display. } - [CommandProperty(AccessLevel.GameMaster)] - [SerializableProperty(7)] - public bool SpectatorVision + [SerializableField(7, fieldChanged: nameof(OnSpectatorVisionChanged))] + [SerializedCommandProperty(AccessLevel.GameMaster)] + [InvalidateProperties] + private bool _spectatorVision; + + private void OnSpectatorVisionChanged(bool oldValue, bool newValue) { - get => _spectatorVision; - set + if (_players.IsInGamePlayer(_players.DealerPosition)) { - if (_spectatorVision == value) - { - return; - } - - _spectatorVision = value; - - if (_players.IsInGamePlayer(_players.DealerPosition)) - { - _players.Dealer.NetState.SendMahjongGeneralInfo(this); - } - - _players.SendTilesPacket(false, true); - _players.SendLocalizedMessage(value ? 1062715 : 1062716); // The dealer has enabled/disabled Spectator Vision. - InvalidateProperties(); - this.MarkDirty(); + _players.Dealer.NetState.SendMahjongGeneralInfo(this); } + _players.SendTilesPacket(false, true); + _players.SendLocalizedMessage(newValue ? 1062715 : 1062716); // The dealer has enabled/disabled Spectator Vision. } private void BuildHorizontalWall( diff --git a/Projects/UOContent/Items/Jewels/BaseJewel.cs b/Projects/UOContent/Items/Jewels/BaseJewel.cs index 4eb01cbe0..e693c0aaa 100644 --- a/Projects/UOContent/Items/Jewels/BaseJewel.cs +++ b/Projects/UOContent/Items/Jewels/BaseJewel.cs @@ -93,16 +93,13 @@ public abstract partial class BaseJewel : Item, ICraftable, IAosItem } } - [SerializableProperty(2)] - [CommandProperty(AccessLevel.GameMaster)] - public CraftResource Resource + [SerializableField(2, fieldChanged: nameof(OnResourceChanged))] + [SerializedCommandProperty(AccessLevel.GameMaster)] + private CraftResource _resource; + + private void OnResourceChanged(CraftResource oldValue, CraftResource newValue) { - get => _resource; - set - { - _resource = value; - Hue = CraftResources.GetHue(_resource); - } + Hue = CraftResources.GetHue(_resource); } public override int PhysicalResistance => Resistances.Physical; diff --git a/Projects/UOContent/Items/Lights/BaseLight.cs b/Projects/UOContent/Items/Lights/BaseLight.cs index d3bff4af5..4bd63ba63 100644 --- a/Projects/UOContent/Items/Lights/BaseLight.cs +++ b/Projects/UOContent/Items/Lights/BaseLight.cs @@ -3,7 +3,7 @@ using ModernUO.Serialization; namespace Server.Items; -[SerializationGenerator(1, false)] +[SerializationGenerator(2, false)] public abstract partial class BaseLight : Item { public static readonly bool Burnout = false; @@ -16,11 +16,10 @@ public abstract partial class BaseLight : Item [SerializedCommandProperty(AccessLevel.GameMaster)] private bool _protected; - [TimerDrift] [SerializableField(4, getter: "private", setter: "private")] + [DeserializeTimer(nameof(DeserializeTimer))] private Timer _burnTimer; - [DeserializeTimerField(4)] private void DeserializeTimer(TimeSpan delay) { if (_burning && _duration != TimeSpan.Zero) @@ -29,6 +28,19 @@ public abstract partial class BaseLight : Item } } + private void MigrateFrom(V1Content content) + { + _burntOut = content.BurntOut; + _burning = content.Burning; + _duration = content.Duration; + _protected = content.Protected; + + if (content.BurnTimerDelay != TimeSpan.MinValue) + { + DeserializeTimer(content.BurnTimerDelay); + } + } + [Constructible] public BaseLight(int itemID) : base(itemID) { diff --git a/Projects/UOContent/Items/Misc/CommunicationCrystals.cs b/Projects/UOContent/Items/Misc/CommunicationCrystals.cs index a52b1a12d..07df233e9 100644 --- a/Projects/UOContent/Items/Misc/CommunicationCrystals.cs +++ b/Projects/UOContent/Items/Misc/CommunicationCrystals.cs @@ -275,8 +275,16 @@ public partial class BroadcastCrystal : Item [SerializationGenerator(0)] public partial class ReceiverCrystal : Item { + [SerializableField(0, fieldChanged: nameof(OnSenderChanged))] + [SerializedCommandProperty(AccessLevel.GameMaster)] private BroadcastCrystal _sender; + private void OnSenderChanged(BroadcastCrystal oldValue, BroadcastCrystal newValue) + { + oldValue?.RemoveReceiver(this); + newValue?.AddReceiver(this); + } + [Constructible] public ReceiverCrystal() : base(0x1ED0) => Light = LightType.Circle150; @@ -297,20 +305,6 @@ public partial class ReceiverCrystal : Item } } - [SerializableProperty(0, useField: nameof(_sender))] - [CommandProperty(AccessLevel.GameMaster)] - public BroadcastCrystal Sender - { - get => _sender; - set - { - _sender?.RemoveReceiver(this); - _sender = value; - value?.AddReceiver(this); - this.MarkDirty(); - } - } - public override void GetProperties(IPropertyList list) { base.GetProperties(list); diff --git a/Projects/UOContent/Items/Misc/Corpses/Corpse.Migrations.cs b/Projects/UOContent/Items/Misc/Corpses/Corpse.Migrations.cs new file mode 100644 index 000000000..bc9ce047e --- /dev/null +++ b/Projects/UOContent/Items/Misc/Corpses/Corpse.Migrations.cs @@ -0,0 +1,179 @@ +using System; + +namespace Server.Items; + +public partial class Corpse +{ + // Decay timer and TimeOfDeath moved from delta time to anchored time + private void MigrateFrom(V18Content content) + { + _restoreEquip = content.RestoreEquip; + _flags = content.Flags; + _timeOfDeath = content.TimeOfDeath; + _restoreTable = content.RestoreTable; + _looters = content.Looters; + _killer = content.Killer; + _aggressors = content.Aggressors; + _owner = content.Owner; + _corpseName = content.CorpseName; + _accessLevel = content.AccessLevel; + _guild = content.Guild; + _equipItems = content.EquipItems; + _hairItemId = content.HairItemId; + _hairHue = content.HairHue; + _facialHairItemId = content.FacialHairItemId; + _facialHairHue = content.FacialHairHue; + + if (content.DecayTimerDelay != TimeSpan.MinValue) + { + DeserializeDecayTimer(content.DecayTimerDelay); + } + } + + // Decay timer moved from [TimerDrift]/[DeserializeTimerField] to [DeserializeTimer] + private void MigrateFrom(V17Content content) + { + _restoreEquip = content.RestoreEquip; + _flags = content.Flags; + _timeOfDeath = content.TimeOfDeath; + _restoreTable = content.RestoreTable; + _looters = content.Looters; + _killer = content.Killer; + _aggressors = content.Aggressors; + _owner = content.Owner; + _corpseName = content.CorpseName; + _accessLevel = content.AccessLevel; + _guild = content.Guild; + _equipItems = content.EquipItems; + _hairItemId = content.HairItemId; + _hairHue = content.HairHue; + _facialHairItemId = content.FacialHairItemId; + _facialHairHue = content.FacialHairHue; + + if (content.DecayTimerDelay != TimeSpan.MinValue) + { + DeserializeDecayTimer(content.DecayTimerDelay); + } + } + + // Decomposed VirtualHairInfo into discrete int fields (hair/facial hair item id + hue) + private void MigrateFrom(V16Content content) + { + _restoreEquip = content.RestoreEquip; + _flags = content.Flags; + _timeOfDeath = content.TimeOfDeath; + _restoreTable = content.RestoreTable; + _decayTimer = new InternalTimer(this, content.DecayTimerDelay); + _decayTimer.Start(); + _looters = content.Looters; + _killer = content.Killer; + _aggressors = content.Aggressors; + _owner = content.Owner; + _corpseName = content.CorpseName; + _accessLevel = content.AccessLevel; + _guild = content.Guild; + _equipItems = content.EquipItems; + if (content.Hair != null) + { + _hairItemId = content.Hair.ItemId; + _hairHue = content.Hair.Hue; + } + + if (content.FacialHair != null) + { + _facialHairItemId = content.FacialHair.ItemId; + _facialHairHue = content.FacialHair.Hue; + } + } + + // Folded Murderer bool field into CorpseFlag.Murderer + private void MigrateFrom(V15Content content) + { + _restoreEquip = content.RestoreEquip; + _flags = content.Flags; + if (content.Murderer) + { + _flags |= CorpseFlag.Murderer; + } + _timeOfDeath = content.TimeOfDeath; + _restoreTable = content.RestoreTable; + _decayTimer = new InternalTimer(this, content.DecayTimerDelay); + _decayTimer.Start(); + _looters = content.Looters; + _killer = content.Killer; + _aggressors = content.Aggressors; + _owner = content.Owner; + _corpseName = content.CorpseName; + _accessLevel = content.AccessLevel; + _guild = content.Guild; + _equipItems = content.EquipItems; + if (content.Hair != null) + { + _hairItemId = content.Hair.ItemId; + _hairHue = content.Hair.Hue; + } + + if (content.FacialHair != null) + { + _facialHairItemId = content.FacialHair.ItemId; + _facialHairHue = content.FacialHair.Hue; + } + } + + // Replaced int Kills snapshot with bool Murderer snapshot + private void MigrateFrom(V14Content content) + { + _restoreEquip = content.RestoreEquip; + _flags = content.Flags; + if (content.Kills >= 5) + { + _flags |= CorpseFlag.Murderer; + } + _timeOfDeath = content.TimeOfDeath; + _restoreTable = content.RestoreTable; + _decayTimer = new InternalTimer(this, content.DecayTimerDelay); + _decayTimer.Start(); + _looters = content.Looters; + _killer = content.Killer; + _aggressors = content.Aggressors; + _owner = content.Owner; + _corpseName = content.CorpseName; + _accessLevel = content.AccessLevel; + _guild = content.Guild; + _equipItems = content.EquipItems; + if (content.Hair != null) + { + _hairItemId = content.Hair.ItemId; + _hairHue = content.Hair.Hue; + } + + if (content.FacialHair != null) + { + _facialHairItemId = content.FacialHair.ItemId; + _facialHairHue = content.FacialHair.Hue; + } + } + + // Added corpse hair and corpse facial hair + private void MigrateFrom(V13Content content) + { + _restoreEquip = content.RestoreEquip; + _flags = content.Flags; + if (content.Kills >= 5) + { + _flags |= CorpseFlag.Murderer; + } + _timeOfDeath = content.TimeOfDeath; + _restoreTable = content.RestoreTable; + _decayTimer = new InternalTimer(this, content.DecayTimerDelay); + _decayTimer.Start(); + _looters = content.Looters; + _killer = content.Killer; + _aggressors = content.Aggressors; + _owner = content.Owner; + _corpseName = content.CorpseName; + _accessLevel = content.AccessLevel; + _guild = content.Guild; + _equipItems = content.EquipItems; + } +} diff --git a/Projects/UOContent/Items/Misc/Corpses/Corpse.cs b/Projects/UOContent/Items/Misc/Corpses/Corpse.cs index 1ee04170c..04e9806c9 100644 --- a/Projects/UOContent/Items/Misc/Corpses/Corpse.cs +++ b/Projects/UOContent/Items/Misc/Corpses/Corpse.cs @@ -86,7 +86,7 @@ public enum CorpseFlag OwnerWasAnimatedDead = 0x00000800 } -[SerializationGenerator(17, false)] +[SerializationGenerator(19, false)] public partial class Corpse : Container, ICarvable { public static readonly TimeSpan MonsterLootRightSacrifice = TimeSpan.FromMinutes(2.0); @@ -106,7 +106,7 @@ public partial class Corpse : Container, ICarvable [SerializableField(1)] private CorpseFlag _flags; - [DeltaDateTime] + [AnchoredDateTime] [SerializableField(2)] [SerializedCommandProperty(AccessLevel.GameMaster)] private DateTime _timeOfDeath; @@ -114,11 +114,10 @@ public partial class Corpse : Container, ICarvable [SerializableField(3, getter: "private", setter: "private")] private Dictionary _restoreTable; - [TimerDrift] [SerializableField(4, getter: "private", setter: "private")] + [DeserializeTimer(nameof(DeserializeDecayTimer))] private Timer _decayTimer; - [DeserializeTimerField(4)] private void DeserializeDecayTimer(TimeSpan delay) => BeginDecay(delay); [SerializableField(5, setter: "private")] @@ -318,127 +317,6 @@ public partial class Corpse : Container, ICarvable DevourCorpse(); } - // Decomposed VirtualHairInfo into discrete int fields (hair/facial hair item id + hue) - private void MigrateFrom(V16Content content) - { - _restoreEquip = content.RestoreEquip; - _flags = content.Flags; - _timeOfDeath = content.TimeOfDeath; - _restoreTable = content.RestoreTable; - _decayTimer = new InternalTimer(this, content.DecayTimerDelay); - _decayTimer.Start(); - _looters = content.Looters; - _killer = content.Killer; - _aggressors = content.Aggressors; - _owner = content.Owner; - _corpseName = content.CorpseName; - _accessLevel = content.AccessLevel; - _guild = content.Guild; - _equipItems = content.EquipItems; - if (content.Hair != null) - { - _hairItemId = content.Hair.ItemId; - _hairHue = content.Hair.Hue; - } - - if (content.FacialHair != null) - { - _facialHairItemId = content.FacialHair.ItemId; - _facialHairHue = content.FacialHair.Hue; - } - } - - // Folded Murderer bool field into CorpseFlag.Murderer - private void MigrateFrom(V15Content content) - { - _restoreEquip = content.RestoreEquip; - _flags = content.Flags; - if (content.Murderer) - { - _flags |= CorpseFlag.Murderer; - } - _timeOfDeath = content.TimeOfDeath; - _restoreTable = content.RestoreTable; - _decayTimer = new InternalTimer(this, content.DecayTimerDelay); - _decayTimer.Start(); - _looters = content.Looters; - _killer = content.Killer; - _aggressors = content.Aggressors; - _owner = content.Owner; - _corpseName = content.CorpseName; - _accessLevel = content.AccessLevel; - _guild = content.Guild; - _equipItems = content.EquipItems; - if (content.Hair != null) - { - _hairItemId = content.Hair.ItemId; - _hairHue = content.Hair.Hue; - } - - if (content.FacialHair != null) - { - _facialHairItemId = content.FacialHair.ItemId; - _facialHairHue = content.FacialHair.Hue; - } - } - - // Replaced int Kills snapshot with bool Murderer snapshot - private void MigrateFrom(V14Content content) - { - _restoreEquip = content.RestoreEquip; - _flags = content.Flags; - if (content.Kills >= 5) - { - _flags |= CorpseFlag.Murderer; - } - _timeOfDeath = content.TimeOfDeath; - _restoreTable = content.RestoreTable; - _decayTimer = new InternalTimer(this, content.DecayTimerDelay); - _decayTimer.Start(); - _looters = content.Looters; - _killer = content.Killer; - _aggressors = content.Aggressors; - _owner = content.Owner; - _corpseName = content.CorpseName; - _accessLevel = content.AccessLevel; - _guild = content.Guild; - _equipItems = content.EquipItems; - if (content.Hair != null) - { - _hairItemId = content.Hair.ItemId; - _hairHue = content.Hair.Hue; - } - - if (content.FacialHair != null) - { - _facialHairItemId = content.FacialHair.ItemId; - _facialHairHue = content.FacialHair.Hue; - } - } - - // Added corpse hair and corpse facial hair - private void MigrateFrom(V13Content content) - { - _restoreEquip = content.RestoreEquip; - _flags = content.Flags; - if (content.Kills >= 5) - { - _flags |= CorpseFlag.Murderer; - } - _timeOfDeath = content.TimeOfDeath; - _restoreTable = content.RestoreTable; - _decayTimer = new InternalTimer(this, content.DecayTimerDelay); - _decayTimer.Start(); - _looters = content.Looters; - _killer = content.Killer; - _aggressors = content.Aggressors; - _owner = content.Owner; - _corpseName = content.CorpseName; - _accessLevel = content.AccessLevel; - _guild = content.Guild; - _equipItems = content.EquipItems; - } - [CommandProperty(AccessLevel.GameMaster)] public virtual bool InstancedCorpse => Core.SE && Core.Now < TimeOfDeath + InstancedCorpseTime; diff --git a/Projects/UOContent/Items/Misc/Corpses/DecayedCorpse.cs b/Projects/UOContent/Items/Misc/Corpses/DecayedCorpse.cs index ead812e2f..17267a6cc 100644 --- a/Projects/UOContent/Items/Misc/Corpses/DecayedCorpse.cs +++ b/Projects/UOContent/Items/Misc/Corpses/DecayedCorpse.cs @@ -3,18 +3,25 @@ using ModernUO.Serialization; namespace Server.Items; -[SerializationGenerator(2, false)] +[SerializationGenerator(3, false)] public partial class DecayedCorpse : Container { private static TimeSpan _defaultDecayTime = TimeSpan.FromMinutes(7.0); - [TimerDrift] [SerializableField(0, getter: "private", setter: "private")] + [DeserializeTimer(nameof(DeserializeDecayTimer))] private Timer _decayTimer; - [DeserializeTimerField(0)] private void DeserializeDecayTimer(TimeSpan delay) => BeginDecay(delay); + private void MigrateFrom(V2Content content) + { + if (content.DecayTimerDelay != TimeSpan.MinValue) + { + DeserializeDecayTimer(content.DecayTimerDelay); + } + } + public DecayedCorpse(string name) : base(Utility.Random(0xECA, 9)) { Movable = false; diff --git a/Projects/UOContent/Items/Misc/MorphItem.cs b/Projects/UOContent/Items/Misc/MorphItem.cs index c5dd9c0de..7e5a74d8b 100644 --- a/Projects/UOContent/Items/Misc/MorphItem.cs +++ b/Projects/UOContent/Items/Misc/MorphItem.cs @@ -30,20 +30,24 @@ public partial class MorphItem : Item _outsideRange = outRange; } - [SerializableProperty(0)] - [CommandProperty(AccessLevel.GameMaster)] - public int OutsideRange + [SerializableField(0, allowFieldChange: nameof(AllowOutsideRangeChange))] + [SerializedCommandProperty(AccessLevel.GameMaster)] + private int _outsideRange; + + private bool AllowOutsideRangeChange(ref int value) { - get => _outsideRange; - set => _outsideRange = Math.Clamp(value, 0, 18); + value = Math.Clamp(value, 0, 18); + return true; } - [SerializableProperty(3)] - [CommandProperty(AccessLevel.GameMaster)] - public int InsideRange + [SerializableField(3, allowFieldChange: nameof(AllowInsideRangeChange))] + [SerializedCommandProperty(AccessLevel.GameMaster)] + private int _insideRange; + + private bool AllowInsideRangeChange(ref int value) { - get => _insideRange; - set => _insideRange = Math.Clamp(value, 0, 18); + value = Math.Clamp(value, 0, 18); + return true; } [CommandProperty(AccessLevel.GameMaster)] diff --git a/Projects/UOContent/Items/Misc/ProjectedItem.cs b/Projects/UOContent/Items/Misc/ProjectedItem.cs index e86025613..03e93d0ea 100644 --- a/Projects/UOContent/Items/Misc/ProjectedItem.cs +++ b/Projects/UOContent/Items/Misc/ProjectedItem.cs @@ -126,20 +126,14 @@ public partial class ProjectedItem : Item private static void OnTick() { - using var queue = PooledRefQueue.Create(); foreach (var item in _active) { if (!item.SendEffect()) { - queue.Enqueue(item); + _active.Remove(item); } } - while (queue.Count > 0) - { - _active.Remove(queue.Dequeue() as ProjectedItem); - } - if (_active.Count == 0) { _timer?.Stop(); diff --git a/Projects/UOContent/Items/Misc/WarningItem.cs b/Projects/UOContent/Items/Misc/WarningItem.cs index 5d0b491a4..3779e4900 100644 --- a/Projects/UOContent/Items/Misc/WarningItem.cs +++ b/Projects/UOContent/Items/Misc/WarningItem.cs @@ -14,8 +14,16 @@ public partial class WarningItem : Item private TextDefinition _warningMessage; // Field 1 + [SerializableField(1, allowFieldChange: nameof(AllowRangeChange))] + [SerializedCommandProperty(AccessLevel.GameMaster)] private int _range; + private bool AllowRangeChange(ref int value) + { + value = Math.Min(value, 18); + return true; + } + [SerializableField(2)] private TimeSpan _resetDelay; @@ -39,18 +47,6 @@ public partial class WarningItem : Item _range = Math.Min(range, 18); } - [CommandProperty(AccessLevel.GameMaster)] - [SerializableProperty(1, useField: nameof(_range))] - public int Range - { - get => _range; - set - { - _range = Math.Min(value, 18); - this.MarkDirty(); - } - } - public virtual bool OnlyToTriggerer => false; public virtual int NeighborRange => 5; diff --git a/Projects/UOContent/Items/Quivers/BaseQuiver.cs b/Projects/UOContent/Items/Quivers/BaseQuiver.cs index 427871f16..70824c9f6 100644 --- a/Projects/UOContent/Items/Quivers/BaseQuiver.cs +++ b/Projects/UOContent/Items/Quivers/BaseQuiver.cs @@ -11,64 +11,62 @@ public partial class BaseQuiver : Container, ICraftable, IAosItem [SerializedIgnoreDupe] [SerializableField(0, setter: "private")] + [SaveFlag(nameof(ShouldSerializeAosAttributes), nameof(AttributesDefaultValue))] [SerializedCommandProperty(AccessLevel.GameMaster, canModify: true)] private AosAttributes _attributes; - [SerializableFieldSaveFlag(0)] private bool ShouldSerializeAosAttributes() => !_attributes.IsEmpty; - [SerializableFieldDefault(0)] private AosAttributes AttributesDefaultValue() => new(this); [InvalidateProperties] [SerializableField(1)] + [SaveFlag(nameof(ShouldSerializeLowerAmmoCost))] [SerializedCommandProperty(AccessLevel.GameMaster)] private int _lowerAmmoCost; - [SerializableFieldSaveFlag(1)] private bool ShouldSerializeLowerAmmoCost() => _lowerAmmoCost != 0; [InvalidateProperties] [SerializableField(2)] + [SaveFlag(nameof(ShouldSerializeWeightReduction))] [SerializedCommandProperty(AccessLevel.GameMaster)] private int _weightReduction; - [SerializableFieldSaveFlag(2)] private bool ShouldSerializeWeightReduction() => _weightReduction != 0; [InvalidateProperties] [SerializableField(3)] + [SaveFlag(nameof(ShouldSerializeDamageIncrease))] [SerializedCommandProperty(AccessLevel.GameMaster)] private int _damageIncrease; - [SerializableFieldSaveFlag(3)] private bool ShouldSerializeDamageIncrease() => _damageIncrease != 0; [InvalidateProperties] [SerializableField(4)] + [SaveFlag(nameof(ShouldSerializeCrafter))] [SerializedCommandProperty(AccessLevel.GameMaster)] private string _crafter; - [SerializableFieldSaveFlag(4)] private bool ShouldSerializeCrafter() => !string.IsNullOrEmpty(_crafter); [InvalidateProperties] [SerializableField(5)] + [SaveFlag(nameof(ShouldSerializeQuality), nameof(QualityDefaultValue))] [SerializedCommandProperty(AccessLevel.GameMaster)] private ClothingQuality _quality; - [SerializableFieldSaveFlag(5)] private bool ShouldSerializeQuality() => _quality != ClothingQuality.Regular; - [SerializableFieldDefault(5)] private ClothingQuality QualityDefaultValue() => ClothingQuality.Regular; [InvalidateProperties] [SerializableField(6)] + [SaveFlag(nameof(ShouldSerializeCapacity))] [SerializedCommandProperty(AccessLevel.GameMaster)] private int _capacity; - [SerializableFieldSaveFlag(6)] private bool ShouldSerializeCapacity() => _capacity != 0; public BaseQuiver(int itemID = 0x2FB7) : base(itemID) diff --git a/Projects/UOContent/Items/Resources/Blacksmithing/Ingots.cs b/Projects/UOContent/Items/Resources/Blacksmithing/Ingots.cs index ce772da40..8170b286d 100644 --- a/Projects/UOContent/Items/Resources/Blacksmithing/Ingots.cs +++ b/Projects/UOContent/Items/Resources/Blacksmithing/Ingots.cs @@ -16,22 +16,14 @@ public abstract partial class BaseIngot : Item, ICommodity public override double DefaultWeight => 0.1; - [SerializableProperty(0)] - [CommandProperty(AccessLevel.GameMaster)] - public CraftResource Resource - { - get => _resource; - set - { - if (_resource != value) - { - _resource = value; - Hue = CraftResources.GetHue(value); + [SerializableField(0, fieldChanged: nameof(OnResourceChanged))] + [SerializedCommandProperty(AccessLevel.GameMaster)] + [InvalidateProperties] + private CraftResource _resource; - InvalidateProperties(); - this.MarkDirty(); - } - } + private void OnResourceChanged(CraftResource oldValue, CraftResource newValue) + { + Hue = CraftResources.GetHue(newValue); } public override int LabelNumber diff --git a/Projects/UOContent/Items/Resources/Blacksmithing/Ore.cs b/Projects/UOContent/Items/Resources/Blacksmithing/Ore.cs index 3116d7136..6ae0c71c3 100644 --- a/Projects/UOContent/Items/Resources/Blacksmithing/Ore.cs +++ b/Projects/UOContent/Items/Resources/Blacksmithing/Ore.cs @@ -17,22 +17,14 @@ public abstract partial class BaseOre : Item _resource = resource; } - [SerializableProperty(0)] - [CommandProperty(AccessLevel.GameMaster)] - public CraftResource Resource - { - get => _resource; - set - { - if (_resource != value) - { - _resource = value; - Hue = CraftResources.GetHue(value); + [SerializableField(0, fieldChanged: nameof(OnResourceChanged))] + [SerializedCommandProperty(AccessLevel.GameMaster)] + [InvalidateProperties] + private CraftResource _resource; - InvalidateProperties(); - this.MarkDirty(); - } - } + private void OnResourceChanged(CraftResource oldValue, CraftResource newValue) + { + Hue = CraftResources.GetHue(newValue); } public override int LabelNumber diff --git a/Projects/UOContent/Items/Resources/Blacksmithing/Scales.cs b/Projects/UOContent/Items/Resources/Blacksmithing/Scales.cs index fa63d902c..277ed569d 100644 --- a/Projects/UOContent/Items/Resources/Blacksmithing/Scales.cs +++ b/Projects/UOContent/Items/Resources/Blacksmithing/Scales.cs @@ -14,22 +14,14 @@ public abstract partial class BaseScales : Item, ICommodity _resource = resource; } - [SerializableProperty(0)] - [CommandProperty(AccessLevel.GameMaster)] - public CraftResource Resource - { - get => _resource; - set - { - if (_resource != value) - { - _resource = value; - Hue = CraftResources.GetHue(value); + [SerializableField(0, fieldChanged: nameof(OnResourceChanged))] + [SerializedCommandProperty(AccessLevel.GameMaster)] + [InvalidateProperties] + private CraftResource _resource; - InvalidateProperties(); - this.MarkDirty(); - } - } + private void OnResourceChanged(CraftResource oldValue, CraftResource newValue) + { + Hue = CraftResources.GetHue(newValue); } public override int LabelNumber => 1053139; // dragon scales diff --git a/Projects/UOContent/Items/Resources/Masonry/Granite.cs b/Projects/UOContent/Items/Resources/Masonry/Granite.cs index ae4f215d7..089181ebe 100644 --- a/Projects/UOContent/Items/Resources/Masonry/Granite.cs +++ b/Projects/UOContent/Items/Resources/Masonry/Granite.cs @@ -15,22 +15,14 @@ public abstract partial class BaseGranite : Item public override double DefaultWeight => Core.ML ? 1.0 : 10.0; - [SerializableProperty(0)] - [CommandProperty(AccessLevel.GameMaster)] - public CraftResource Resource - { - get => _resource; - set - { - if (_resource != value) - { - _resource = value; - Hue = CraftResources.GetHue(value); + [SerializableField(0, fieldChanged: nameof(OnResourceChanged))] + [SerializedCommandProperty(AccessLevel.GameMaster)] + [InvalidateProperties] + private CraftResource _resource; - InvalidateProperties(); - this.MarkDirty(); - } - } + private void OnResourceChanged(CraftResource oldValue, CraftResource newValue) + { + Hue = CraftResources.GetHue(newValue); } public override int LabelNumber => 1044607; // high quality granite diff --git a/Projects/UOContent/Items/Resources/Tailor/Hides.cs b/Projects/UOContent/Items/Resources/Tailor/Hides.cs index df6423fb2..d64eb2f0b 100644 --- a/Projects/UOContent/Items/Resources/Tailor/Hides.cs +++ b/Projects/UOContent/Items/Resources/Tailor/Hides.cs @@ -15,22 +15,14 @@ public abstract partial class BaseHides : Item, ICommodity public override double DefaultWeight => 5.0; - [SerializableProperty(0)] - [CommandProperty(AccessLevel.GameMaster)] - public CraftResource Resource - { - get => _resource; - set - { - if (_resource != value) - { - _resource = value; - Hue = CraftResources.GetHue(value); + [SerializableField(0, fieldChanged: nameof(OnResourceChanged))] + [SerializedCommandProperty(AccessLevel.GameMaster)] + [InvalidateProperties] + private CraftResource _resource; - InvalidateProperties(); - this.MarkDirty(); - } - } + private void OnResourceChanged(CraftResource oldValue, CraftResource newValue) + { + Hue = CraftResources.GetHue(newValue); } public override int LabelNumber diff --git a/Projects/UOContent/Items/Resources/Tailor/Leathers.cs b/Projects/UOContent/Items/Resources/Tailor/Leathers.cs index 173e2dae0..afb757e1f 100644 --- a/Projects/UOContent/Items/Resources/Tailor/Leathers.cs +++ b/Projects/UOContent/Items/Resources/Tailor/Leathers.cs @@ -15,22 +15,14 @@ public abstract partial class BaseLeather : Item, ICommodity public override double DefaultWeight => 1.0; - [SerializableProperty(0)] - [CommandProperty(AccessLevel.GameMaster)] - public CraftResource Resource - { - get => _resource; - set - { - if (_resource != value) - { - _resource = value; - Hue = CraftResources.GetHue(value); + [SerializableField(0, fieldChanged: nameof(OnResourceChanged))] + [SerializedCommandProperty(AccessLevel.GameMaster)] + [InvalidateProperties] + private CraftResource _resource; - InvalidateProperties(); - this.MarkDirty(); - } - } + private void OnResourceChanged(CraftResource oldValue, CraftResource newValue) + { + Hue = CraftResources.GetHue(newValue); } public override int LabelNumber diff --git a/Projects/UOContent/Items/Skill Items/Carpenter Items/Board.cs b/Projects/UOContent/Items/Skill Items/Carpenter Items/Board.cs index 9ff0d8386..53c8077f1 100644 --- a/Projects/UOContent/Items/Skill Items/Carpenter Items/Board.cs +++ b/Projects/UOContent/Items/Skill Items/Carpenter Items/Board.cs @@ -21,22 +21,14 @@ public partial class Board : Item, ICommodity Hue = CraftResources.GetHue(resource); } - [SerializableProperty(0)] - [CommandProperty(AccessLevel.GameMaster)] - public CraftResource Resource - { - get => _resource; - set - { - if (_resource != value) - { - _resource = value; - Hue = CraftResources.GetHue(value); + [SerializableField(0, fieldChanged: nameof(OnResourceChanged))] + [SerializedCommandProperty(AccessLevel.GameMaster)] + [InvalidateProperties] + private CraftResource _resource; - InvalidateProperties(); - this.MarkDirty(); - } - } + private void OnResourceChanged(CraftResource oldValue, CraftResource newValue) + { + Hue = CraftResources.GetHue(newValue); } int ICommodity.DescriptionNumber diff --git a/Projects/UOContent/Items/Skill Items/Fishing/Misc/MessageInABottle.cs b/Projects/UOContent/Items/Skill Items/Fishing/Misc/MessageInABottle.cs index 5d6062099..bf626c2fd 100644 --- a/Projects/UOContent/Items/Skill Items/Fishing/Misc/MessageInABottle.cs +++ b/Projects/UOContent/Items/Skill Items/Fishing/Misc/MessageInABottle.cs @@ -25,16 +25,14 @@ public partial class MessageInABottle : Item public override int LabelNumber => 1041080; // a message in a bottle - [SerializableProperty(0)] - [CommandProperty(AccessLevel.GameMaster)] - public int Level + [SerializableField(0, allowFieldChange: nameof(AllowLevelChange))] + [SerializedCommandProperty(AccessLevel.GameMaster)] + private int _level; + + private bool AllowLevelChange(ref int value) { - get => _level; - set - { - _level = Math.Max(1, Math.Min(value, 4)); - this.MarkDirty(); - } + value = Math.Max(1, Math.Min(value, 4)); + return true; } public static int GetRandomLevel() diff --git a/Projects/UOContent/Items/Skill Items/Fishing/Misc/SOS.cs b/Projects/UOContent/Items/Skill Items/Fishing/Misc/SOS.cs index f8450e63f..5374f8716 100644 --- a/Projects/UOContent/Items/Skill Items/Fishing/Misc/SOS.cs +++ b/Projects/UOContent/Items/Skill Items/Fishing/Misc/SOS.cs @@ -103,18 +103,20 @@ public partial class SOS : Item [CommandProperty(AccessLevel.GameMaster)] public bool IsAncient => _level >= 4; - [SerializableProperty(0)] - [CommandProperty(AccessLevel.GameMaster)] - public int Level + [SerializableField(0, fieldChanged: nameof(OnLevelChanged), allowFieldChange: nameof(AllowLevelChange))] + [SerializedCommandProperty(AccessLevel.GameMaster)] + [InvalidateProperties] + private int _level; + + private bool AllowLevelChange(ref int value) { - get => _level; - set - { - _level = Math.Max(1, Math.Min(value, 4)); - UpdateHue(); - InvalidateProperties(); - this.MarkDirty(); - } + value = Math.Max(1, Math.Min(value, 4)); + return true; + } + + private void OnLevelChanged(int oldValue, int newValue) + { + UpdateHue(); } public void UpdateHue() => Hue = IsAncient ? 0x481 : 0; diff --git a/Projects/UOContent/Items/Skill Items/Lumberjack/Log.cs b/Projects/UOContent/Items/Skill Items/Lumberjack/Log.cs index 2873957d5..2a06f535a 100644 --- a/Projects/UOContent/Items/Skill Items/Lumberjack/Log.cs +++ b/Projects/UOContent/Items/Skill Items/Lumberjack/Log.cs @@ -28,22 +28,14 @@ public partial class Log : Item, ICommodity, IAxe public override double DefaultWeight => 2.0; - [SerializableProperty(0)] - [CommandProperty(AccessLevel.GameMaster)] - public CraftResource Resource - { - get => _resource; - set - { - if (_resource != value) - { - _resource = value; - Hue = CraftResources.GetHue(value); + [SerializableField(0, fieldChanged: nameof(OnResourceChanged))] + [SerializedCommandProperty(AccessLevel.GameMaster)] + [InvalidateProperties] + private CraftResource _resource; - InvalidateProperties(); - this.MarkDirty(); - } - } + private void OnResourceChanged(CraftResource oldValue, CraftResource newValue) + { + Hue = CraftResources.GetHue(newValue); } public virtual bool Axe(Mobile from, BaseAxe axe) => TryCreateBoards(from, 0, new Board()); diff --git a/Projects/UOContent/Items/Skill Items/Magical/Misc/RecallRune.cs b/Projects/UOContent/Items/Skill Items/Magical/Misc/RecallRune.cs index 68b6f324a..14db7ac1e 100644 --- a/Projects/UOContent/Items/Skill Items/Magical/Misc/RecallRune.cs +++ b/Projects/UOContent/Items/Skill Items/Magical/Misc/RecallRune.cs @@ -47,36 +47,24 @@ public partial class RecallRune : Item } } - [SerializableProperty(2)] - [CommandProperty(AccessLevel.Counselor, AccessLevel.GameMaster)] - public bool Marked + [SerializableField(2, fieldChanged: nameof(OnMarkedChanged))] + [SerializedCommandProperty(AccessLevel.Counselor, AccessLevel.GameMaster)] + [InvalidateProperties] + private bool _marked; + + private void OnMarkedChanged(bool oldValue, bool newValue) { - get => _marked; - set - { - if (_marked != value) - { - _marked = value; - CalculateHue(); - InvalidateProperties(); - } - } + CalculateHue(); } - [SerializableProperty(4)] - [CommandProperty(AccessLevel.Counselor, AccessLevel.GameMaster)] - public Map TargetMap + [SerializableField(4, fieldChanged: nameof(OnTargetMapChanged))] + [SerializedCommandProperty(AccessLevel.Counselor, AccessLevel.GameMaster)] + [InvalidateProperties] + private Map _targetMap; + + private void OnTargetMapChanged(Map oldValue, Map newValue) { - get => _targetMap; - set - { - if (_targetMap != value) - { - _targetMap = value; - CalculateHue(); - InvalidateProperties(); - } - } + CalculateHue(); } private void Deserialize(IGenericReader reader, int version) diff --git a/Projects/UOContent/Items/Skill Items/Magical/Runebook.cs b/Projects/UOContent/Items/Skill Items/Magical/Runebook.cs index 0a5a0f308..f9ecdfa22 100644 --- a/Projects/UOContent/Items/Skill Items/Magical/Runebook.cs +++ b/Projects/UOContent/Items/Skill Items/Magical/Runebook.cs @@ -371,27 +371,27 @@ public partial class RunebookEntry private Runebook _runebook; [SerializableField(0)] + [SaveFlag(nameof(ShouldSerializeHouse))] private BaseHouse _house; - [SerializableFieldSaveFlag(0)] public bool ShouldSerializeHouse() => _house?.Deleted == false; [SerializableField(1)] + [SaveFlag(nameof(ShouldSerializeLocation))] private Point3D _location; - [SerializableFieldSaveFlag(1)] public bool ShouldSerializeLocation() => _house?.Deleted != false; [SerializableField(2)] + [SaveFlag(nameof(ShouldSerializeMap))] private Map _map; - [SerializableFieldSaveFlag(2)] public bool ShouldSerializeMap() => _house?.Deleted != false; [SerializableField(3)] + [SaveFlag(nameof(ShouldSerializeDesc))] private string _description; - [SerializableFieldSaveFlag(3)] public bool ShouldSerializeDesc() => _house?.Deleted != false; public RunebookEntry( diff --git a/Projects/UOContent/Items/Skill Items/Magical/Spellbook.cs b/Projects/UOContent/Items/Skill Items/Magical/Spellbook.cs index f1ced7308..9447fddfa 100644 --- a/Projects/UOContent/Items/Skill Items/Magical/Spellbook.cs +++ b/Projects/UOContent/Items/Skill Items/Magical/Spellbook.cs @@ -133,28 +133,19 @@ public partial class Spellbook : Item, ICraftable, ISlayer, IAosItem public virtual int BookOffset => 0; public virtual int BookCount => 64; - [CommandProperty(AccessLevel.GameMaster)] - [SerializableProperty(7)] - public ulong Content + [SerializableField(7, fieldChanged: nameof(OnContentChanged))] + [SerializedCommandProperty(AccessLevel.GameMaster)] + [InvalidateProperties] + private ulong _content; + + private void OnContentChanged(ulong oldValue, ulong newValue) { - get => _content; - set + // This assignment will mark it as dirty + SpellCount = 0; + while (newValue > 0) { - if (_content != value) - { - _content = value; - - // This assignment will mark it as dirty - SpellCount = 0; - - while (value > 0) - { - _spellCount += (int)(value & 0x1); - value >>= 1; - } - - InvalidateProperties(); - } + _spellCount += (int)(newValue & 0x1); + newValue >>= 1; } } diff --git a/Projects/UOContent/Items/Skill Items/Misc/RepairDeed.cs b/Projects/UOContent/Items/Skill Items/Misc/RepairDeed.cs index dbb8df68c..e4d486d10 100644 --- a/Projects/UOContent/Items/Skill Items/Misc/RepairDeed.cs +++ b/Projects/UOContent/Items/Skill Items/Misc/RepairDeed.cs @@ -62,17 +62,15 @@ public partial class RepairDeed : Item public override bool DisplayLootType => false; - [CommandProperty(AccessLevel.GameMaster)] - [SerializableProperty(1)] - public double SkillLevel + [SerializableField(1, allowFieldChange: nameof(AllowSkillLevelChange))] + [SerializedCommandProperty(AccessLevel.GameMaster)] + [InvalidateProperties] + private double _skillLevel; + + private bool AllowSkillLevelChange(ref double value) { - get => _skillLevel; - set - { - _skillLevel = Math.Clamp(value, 0, 120.0); - InvalidateProperties(); - this.MarkDirty(); - } + value = Math.Clamp(value, 0, 120.0); + return true; } public override void AddNameProperty(IPropertyList list) diff --git a/Projects/UOContent/Items/Skill Items/Musical Instruments/BaseInstrument.cs b/Projects/UOContent/Items/Skill Items/Musical Instruments/BaseInstrument.cs index b01416211..f7716a4a1 100644 --- a/Projects/UOContent/Items/Skill Items/Musical Instruments/BaseInstrument.cs +++ b/Projects/UOContent/Items/Skill Items/Musical Instruments/BaseInstrument.cs @@ -74,16 +74,13 @@ public abstract partial class BaseInstrument : Item, ICraftable, ISlayer } } - [SerializableProperty(1)] - [CommandProperty(AccessLevel.GameMaster)] - public DateTime LastReplenished + [SerializableField(1, fieldChanged: nameof(OnLastReplenishedChanged))] + [SerializedCommandProperty(AccessLevel.GameMaster)] + private DateTime _lastReplenished; + + private void OnLastReplenishedChanged(DateTime oldValue, DateTime newValue) { - get => _lastReplenished; - set - { - _lastReplenished = value; - CheckReplenishUses(); - } + CheckReplenishUses(); } [SerializableProperty(3)] diff --git a/Projects/UOContent/Items/Skill Items/Tailor Items/Dyetubs/DyeTub.cs b/Projects/UOContent/Items/Skill Items/Tailor Items/Dyetubs/DyeTub.cs index 6b557b7be..e35cca471 100644 --- a/Projects/UOContent/Items/Skill Items/Tailor Items/Dyetubs/DyeTub.cs +++ b/Projects/UOContent/Items/Skill Items/Tailor Items/Dyetubs/DyeTub.cs @@ -41,20 +41,13 @@ namespace Server.Items public virtual bool AllowDyables => true; - [SerializableProperty(2)] - [CommandProperty(AccessLevel.GameMaster)] - public int DyedHue - { - get => _dyedHue; - set - { - if (_redyable) - { - _dyedHue = value; - Hue = value; - } - } - } + [SerializableField(2, allowFieldChange: nameof(AllowDyedHueChange), fieldChanged: nameof(OnDyedHueChanged))] + [SerializedCommandProperty(AccessLevel.GameMaster)] + private int _dyedHue; + + private bool AllowDyedHueChange(ref int value) => _redyable; + + private void OnDyedHueChanged(int oldValue, int newValue) => Hue = newValue; // Three metallic tubs now. public virtual bool MetallicHues => false; diff --git a/Projects/UOContent/Items/Skill Items/Tools/BaseRunicTool.cs b/Projects/UOContent/Items/Skill Items/Tools/BaseRunicTool.cs index 6010ec376..595b6b11d 100644 --- a/Projects/UOContent/Items/Skill Items/Tools/BaseRunicTool.cs +++ b/Projects/UOContent/Items/Skill Items/Tools/BaseRunicTool.cs @@ -60,17 +60,14 @@ public abstract partial class BaseRunicTool : BaseTool public BaseRunicTool(CraftResource resource, int uses, int itemID) : base(uses, itemID) => _resource = resource; - [SerializableProperty(0)] - [CommandProperty(AccessLevel.GameMaster)] - public CraftResource Resource + [SerializableField(0, fieldChanged: nameof(OnResourceChanged))] + [SerializedCommandProperty(AccessLevel.GameMaster)] + [InvalidateProperties] + private CraftResource _resource; + + private void OnResourceChanged(CraftResource oldValue, CraftResource newValue) { - get => _resource; - set - { - _resource = value; - Hue = CraftResources.GetHue(_resource); - InvalidateProperties(); - } + Hue = CraftResources.GetHue(_resource); } private void Deserialize(IGenericReader reader, int version) diff --git a/Projects/UOContent/Items/Special/8th Anniversary Items/FountainOfLife.cs b/Projects/UOContent/Items/Special/8th Anniversary Items/FountainOfLife.cs index 3bdfd00fa..404fc9012 100644 --- a/Projects/UOContent/Items/Special/8th Anniversary Items/FountainOfLife.cs +++ b/Projects/UOContent/Items/Special/8th Anniversary Items/FountainOfLife.cs @@ -31,6 +31,7 @@ public partial class FountainOfLife : BaseAddonContainer public const int MaxCharges = 10; [SerializableField(1)] + [DeserializeTimer(nameof(DeserializeTimer), wallClock: true)] private Timer _timer; [Constructible] @@ -39,7 +40,6 @@ public partial class FountainOfLife : BaseAddonContainer _charges = charges; } - [DeserializeTimerField(1)] private void DeserializeTimer(TimeSpan delay) { _timer = Timer.DelayCall(Utility.Max(delay, TimeSpan.Zero), RechargeTime, Recharge); @@ -54,17 +54,15 @@ public partial class FountainOfLife : BaseAddonContainer public override int DefaultDropSound => 66; public override int DefaultMaxItems => 125; - [SerializableProperty(0)] - [CommandProperty(AccessLevel.GameMaster)] - public int Charges + [SerializableField(0, allowFieldChange: nameof(AllowChargesChange))] + [SerializedCommandProperty(AccessLevel.GameMaster)] + [InvalidateProperties] + private int _charges; + + private bool AllowChargesChange(ref int value) { - get => _charges; - set - { - _charges = Math.Min(value, MaxCharges); - InvalidateProperties(); - this.MarkDirty(); - } + value = Math.Min(value, MaxCharges); + return true; } public override bool OnDragLift(Mobile from) => false; @@ -183,16 +181,14 @@ public partial class FountainOfLifeDeed : BaseAddonContainerDeed public override int LabelNumber => 1075197; // Fountain of Life public override BaseAddonContainer Addon => new FountainOfLife(_charges); - [SerializableProperty(0)] - [CommandProperty(AccessLevel.GameMaster)] - public int Charges + [SerializableField(0, allowFieldChange: nameof(AllowChargesChange))] + [SerializedCommandProperty(AccessLevel.GameMaster)] + [InvalidateProperties] + private int _charges; + + private bool AllowChargesChange(ref int value) { - get => _charges; - set - { - _charges = Math.Min(value, FountainOfLife.MaxCharges); - InvalidateProperties(); - this.MarkDirty(); - } + value = Math.Min(value, FountainOfLife.MaxCharges); + return true; } } diff --git a/Projects/UOContent/Items/Special/Heritage Items/FruitTrees.cs b/Projects/UOContent/Items/Special/Heritage Items/FruitTrees.cs index 6a7e5af6f..fabb756d8 100644 --- a/Projects/UOContent/Items/Special/Heritage Items/FruitTrees.cs +++ b/Projects/UOContent/Items/Special/Heritage Items/FruitTrees.cs @@ -14,12 +14,14 @@ public abstract partial class BaseFruitTreeAddon : BaseAddon public abstract override BaseAddonDeed Deed { get; } public abstract Item Fruit { get; } - [SerializableProperty(0)] - [CommandProperty(AccessLevel.GameMaster)] - public int Fruits + [SerializableField(0, allowFieldChange: nameof(AllowFruitsChange))] + [SerializedCommandProperty(AccessLevel.GameMaster)] + private int _fruits; + + private bool AllowFruitsChange(ref int value) { - get => _fruits; - set => _fruits = Math.Max(value, 0); + value = Math.Max(value, 0); + return true; } public override void OnComponentUsed(AddonComponent c, Mobile from) diff --git a/Projects/UOContent/Items/Special/House Raffle/HouseRaffleStone.cs b/Projects/UOContent/Items/Special/House Raffle/HouseRaffleStone.cs index c24985f9f..59e2b8277 100644 --- a/Projects/UOContent/Items/Special/House Raffle/HouseRaffleStone.cs +++ b/Projects/UOContent/Items/Special/House Raffle/HouseRaffleStone.cs @@ -146,34 +146,24 @@ public partial class HouseRaffleStone : Item } } - [SerializableProperty(3)] - [CommandProperty(AccessLevel.GameMaster, AccessLevel.Seer)] - public Rectangle2D PlotBounds - { - get => _plotBounds; - set - { - _plotBounds = value; + [SerializableField(3, fieldChanged: nameof(OnPlotBoundsChanged))] + [SerializedCommandProperty(AccessLevel.GameMaster, AccessLevel.Seer)] + [InvalidateProperties] + private Rectangle2D _plotBounds; - InvalidateRegion(); - InvalidateProperties(); - this.MarkDirty(); - } + private void OnPlotBoundsChanged(Rectangle2D oldValue, Rectangle2D newValue) + { + InvalidateRegion(); } - [SerializableProperty(4)] - [CommandProperty(AccessLevel.GameMaster, AccessLevel.Seer)] - public Map PlotFacet - { - get => _plotFacet; - set - { - _plotFacet = value; + [SerializableField(4, fieldChanged: nameof(OnPlotFacetChanged))] + [SerializedCommandProperty(AccessLevel.GameMaster, AccessLevel.Seer)] + [InvalidateProperties] + private Map _plotFacet; - InvalidateRegion(); - InvalidateProperties(); - this.MarkDirty(); - } + private void OnPlotFacetChanged(Map oldValue, Map newValue) + { + InvalidateRegion(); } [CommandProperty(AccessLevel.GameMaster)] @@ -190,17 +180,15 @@ public partial class HouseRaffleStone : Item } } - [SerializableProperty(6)] - [CommandProperty(AccessLevel.GameMaster, AccessLevel.Seer)] - public int TicketPrice + [SerializableField(6, allowFieldChange: nameof(AllowTicketPriceChange))] + [SerializedCommandProperty(AccessLevel.GameMaster, AccessLevel.Seer)] + [InvalidateProperties] + private int _ticketPrice; + + private bool AllowTicketPriceChange(ref int value) { - get => _ticketPrice; - set - { - _ticketPrice = Math.Max(0, value); - InvalidateProperties(); - this.MarkDirty(); - } + value = Math.Max(0, value); + return true; } public override string DefaultName => "a house raffle stone"; diff --git a/Projects/UOContent/Items/Special/MonsterStatuette.cs b/Projects/UOContent/Items/Special/MonsterStatuette.cs index 8d49dd0e4..fd26a0060 100644 --- a/Projects/UOContent/Items/Special/MonsterStatuette.cs +++ b/Projects/UOContent/Items/Special/MonsterStatuette.cs @@ -162,21 +162,15 @@ public partial class MonsterStatuette : Item, IRewardItem, IGumpToggleItem _ => fallback }; - [SerializableProperty(0)] - [CommandProperty(AccessLevel.GameMaster)] - public MonsterStatuetteType Type + [SerializableField(0, fieldChanged: nameof(OnTypeChanged))] + [SerializedCommandProperty(AccessLevel.GameMaster)] + [InvalidateProperties] + private MonsterStatuetteType _type; + + private void OnTypeChanged(MonsterStatuetteType oldValue, MonsterStatuetteType newValue) { - get => _type; - set - { - _type = value; - ItemID = MonsterStatuetteInfo.GetInfo(_type).ItemID; - - Hue = GetStatuetteHue(_type, Hue); - - InvalidateProperties(); - this.MarkDirty(); - } + ItemID = MonsterStatuetteInfo.GetInfo(_type).ItemID; + Hue = GetStatuetteHue(_type, Hue); } public override int LabelNumber => MonsterStatuetteInfo.GetInfo(_type).LabelNumber; diff --git a/Projects/UOContent/Items/Special/Solen Items/BagOfSending.cs b/Projects/UOContent/Items/Special/Solen Items/BagOfSending.cs index f2772b342..9441fd5c7 100644 --- a/Projects/UOContent/Items/Special/Solen Items/BagOfSending.cs +++ b/Projects/UOContent/Items/Special/Solen Items/BagOfSending.cs @@ -36,50 +36,41 @@ public partial class BagOfSending : Item, TranslocationItem public override int LabelNumber => 1054104; // a bag of sending - [SerializableProperty(0)] - [CommandProperty(AccessLevel.GameMaster)] - public BagOfSendingHue BagOfSendingHue - { - get => _bagOfSendingHue; - set - { - _bagOfSendingHue = value; + [SerializableField(0, fieldChanged: nameof(OnBagOfSendingHueChanged))] + [SerializedCommandProperty(AccessLevel.GameMaster)] + private BagOfSendingHue _bagOfSendingHue; - Hue = value switch - { - BagOfSendingHue.Yellow => 0x8A5, - BagOfSendingHue.Blue => 0x8AD, - BagOfSendingHue.Red => 0x89B, - _ => Hue - }; - this.MarkDirty(); - } + private void OnBagOfSendingHueChanged(BagOfSendingHue oldValue, BagOfSendingHue newValue) + { + Hue = newValue switch + { + BagOfSendingHue.Yellow => 0x8A5, + BagOfSendingHue.Blue => 0x8AD, + BagOfSendingHue.Red => 0x89B, + _ => Hue + }; } - [SerializableProperty(1)] - [CommandProperty(AccessLevel.GameMaster)] - public int Charges + [SerializableField(1, allowFieldChange: nameof(AllowChargesChange))] + [SerializedCommandProperty(AccessLevel.GameMaster)] + [InvalidateProperties] + private int _charges; + + private bool AllowChargesChange(ref int value) { - get => _charges; - set - { - _charges = Math.Clamp(value, 0, MaxCharges); - InvalidateProperties(); - this.MarkDirty(); - } + value = Math.Clamp(value, 0, MaxCharges); + return true; } - [SerializableProperty(2)] - [CommandProperty(AccessLevel.GameMaster)] - public int Recharges + [SerializableField(2, allowFieldChange: nameof(AllowRechargesChange))] + [SerializedCommandProperty(AccessLevel.GameMaster)] + [InvalidateProperties] + private int _recharges; + + private bool AllowRechargesChange(ref int value) { - get => _recharges; - set - { - _recharges = Math.Clamp(value, 0, MaxRecharges); - InvalidateProperties(); - this.MarkDirty(); - } + value = Math.Clamp(value, 0, MaxRecharges); + return true; } [CommandProperty(AccessLevel.GameMaster)] diff --git a/Projects/UOContent/Items/Special/Solen Items/BallOfSummoning.cs b/Projects/UOContent/Items/Special/Solen Items/BallOfSummoning.cs index a76833e30..070a802b1 100644 --- a/Projects/UOContent/Items/Special/Solen Items/BallOfSummoning.cs +++ b/Projects/UOContent/Items/Special/Solen Items/BallOfSummoning.cs @@ -29,30 +29,26 @@ public partial class BallOfSummoning : Item, TranslocationItem public override double DefaultWeight => 10.0; - [SerializableProperty(0)] - [CommandProperty(AccessLevel.GameMaster)] - public int Recharges + [SerializableField(0, allowFieldChange: nameof(AllowRechargesChange))] + [SerializedCommandProperty(AccessLevel.GameMaster)] + [InvalidateProperties] + private int _recharges; + + private bool AllowRechargesChange(ref int value) { - get => _recharges; - set - { - _recharges = Math.Clamp(value, 0, MaxRecharges); - InvalidateProperties(); - this.MarkDirty(); - } + value = Math.Clamp(value, 0, MaxRecharges); + return true; } - [SerializableProperty(1)] - [CommandProperty(AccessLevel.GameMaster)] - public int Charges + [SerializableField(1, allowFieldChange: nameof(AllowChargesChange))] + [SerializedCommandProperty(AccessLevel.GameMaster)] + [InvalidateProperties] + private int _charges; + + private bool AllowChargesChange(ref int value) { - get => _charges; - set - { - _charges = Math.Clamp(value, 0, MaxCharges); - InvalidateProperties(); - this.MarkDirty(); - } + value = Math.Clamp(value, 0, MaxCharges); + return true; } [SerializableProperty(2)] diff --git a/Projects/UOContent/Items/Special/Solen Items/BraceletOfBinding.cs b/Projects/UOContent/Items/Special/Solen Items/BraceletOfBinding.cs index 2f82791c2..1d508c4ac 100644 --- a/Projects/UOContent/Items/Special/Solen Items/BraceletOfBinding.cs +++ b/Projects/UOContent/Items/Special/Solen Items/BraceletOfBinding.cs @@ -27,30 +27,26 @@ public partial class BraceletOfBinding : BaseBracelet, TranslocationItem public override double DefaultWeight => 1.0; - [SerializableProperty(0)] - [CommandProperty(AccessLevel.GameMaster)] - public int Recharges + [SerializableField(0, allowFieldChange: nameof(AllowRechargesChange))] + [SerializedCommandProperty(AccessLevel.GameMaster)] + [InvalidateProperties] + private int _recharges; + + private bool AllowRechargesChange(ref int value) { - get => _recharges; - set - { - _recharges = Math.Clamp(value, 0, MaxRecharges); - InvalidateProperties(); - this.MarkDirty(); - } + value = Math.Clamp(value, 0, MaxRecharges); + return true; } - [SerializableProperty(1)] - [CommandProperty(AccessLevel.GameMaster)] - public int Charges + [SerializableField(1, allowFieldChange: nameof(AllowChargesChange))] + [SerializedCommandProperty(AccessLevel.GameMaster)] + [InvalidateProperties] + private int _charges; + + private bool AllowChargesChange(ref int value) { - get => _charges; - set - { - _charges = Math.Clamp(value, 0, MaxCharges); - InvalidateProperties(); - this.MarkDirty(); - } + value = Math.Clamp(value, 0, MaxCharges); + return true; } [SerializableProperty(3)] diff --git a/Projects/UOContent/Items/Special/SoulStone.cs b/Projects/UOContent/Items/Special/SoulStone.cs index bcad4a749..d26f6792c 100644 --- a/Projects/UOContent/Items/Special/SoulStone.cs +++ b/Projects/UOContent/Items/Special/SoulStone.cs @@ -83,20 +83,14 @@ public partial class SoulStone : Item, ISecurable } } - [SerializableProperty(6)] - [CommandProperty(AccessLevel.GameMaster)] - public double SkillValue + [SerializableField(6, fieldChanged: nameof(OnSkillValueChanged))] + [SerializedCommandProperty(AccessLevel.GameMaster)] + [InvalidateProperties] + private double _skillValue; + + private void OnSkillValueChanged(double oldValue, double newValue) { - get => _skillValue; - set - { - _skillValue = value; - - ItemID = IsEmpty ? _inactiveItemID : _activeItemID; - - InvalidateProperties(); - this.MarkDirty(); - } + ItemID = IsEmpty ? _inactiveItemID : _activeItemID; } [CommandProperty(AccessLevel.GameMaster)] diff --git a/Projects/UOContent/Items/Suits/BaseSuit.cs b/Projects/UOContent/Items/Suits/BaseSuit.cs index 018c010a2..dd1eb00f6 100644 --- a/Projects/UOContent/Items/Suits/BaseSuit.cs +++ b/Projects/UOContent/Items/Suits/BaseSuit.cs @@ -17,20 +17,9 @@ public abstract partial class BaseSuit : Item public override double DefaultWeight => 1.0; - [SerializableProperty(0)] - public AccessLevel AccessLevel - { - get => _accessLevel; - set - { - var oldAccessLevel = _accessLevel; - _accessLevel = value; - InvalidateProperties(); - this.MarkDirty(); - - OnAccessLevelChanged(oldAccessLevel, _accessLevel); - } - } + [SerializableField(0, fieldChanged: nameof(OnAccessLevelChanged))] + [InvalidateProperties] + private AccessLevel _accessLevel; public virtual void OnAccessLevelChanged(AccessLevel oldAccessLevel, AccessLevel accessLevel) { diff --git a/Projects/UOContent/Items/Talismans/BaseTalisman.cs b/Projects/UOContent/Items/Talismans/BaseTalisman.cs index 632587523..b3f0c2ed4 100644 --- a/Projects/UOContent/Items/Talismans/BaseTalisman.cs +++ b/Projects/UOContent/Items/Talismans/BaseTalisman.cs @@ -128,136 +128,131 @@ public partial class BaseTalisman : Item, IAosItem [SerializedIgnoreDupe] [SerializableField(0, setter: "private")] + [SaveFlag(nameof(ShouldSerializeAttributes), nameof(AttributesDefaultValue))] [SerializedCommandProperty(AccessLevel.GameMaster, canModify: true)] private AosAttributes _attributes; - [SerializableFieldSaveFlag(0)] public bool ShouldSerializeAttributes() => !_attributes.IsEmpty; - [SerializableFieldDefault(0)] private AosAttributes AttributesDefaultValue() => new(this); [SerializedIgnoreDupe] [SerializableField(1, setter: "private")] + [SaveFlag(nameof(ShouldSerializeSkillBonuses), nameof(SkillBonusesDefaultValue))] [SerializedCommandProperty(AccessLevel.GameMaster, canModify: true)] private AosSkillBonuses _skillBonuses; - [SerializableFieldSaveFlag(1)] public bool ShouldSerializeSkillBonuses() => !_skillBonuses.IsEmpty; - [SerializableFieldDefault(1)] private AosSkillBonuses SkillBonusesDefaultValue() => new(this); [SerializedIgnoreDupe] [InvalidateProperties] [SerializableField(2)] + [SaveFlag(nameof(ShouldSerializeProtection), nameof(ProtectionDefaultValue))] [SerializedCommandProperty(AccessLevel.GameMaster, canModify: true)] private TalismanAttribute _protection; - [SerializableFieldSaveFlag(2)] public bool ShouldSerializeProtection() => !_protection.IsEmpty; - [SerializableFieldDefault(2)] private TalismanAttribute ProtectionDefaultValue() => new(); [SerializedIgnoreDupe] [InvalidateProperties] [SerializableField(3)] + [SaveFlag(nameof(ShouldSerializeKiller), nameof(KillerDefaultValue))] [SerializedCommandProperty(AccessLevel.GameMaster, canModify: true)] private TalismanAttribute _killer; - [SerializableFieldSaveFlag(3)] public bool ShouldSerializeKiller() => !_killer.IsEmpty; - [SerializableFieldDefault(3)] private TalismanAttribute KillerDefaultValue() => new(); [SerializedIgnoreDupe] [InvalidateProperties] [SerializableField(4)] + [SaveFlag(nameof(ShouldSerializeSummoner), nameof(SummonerDefaultValue))] [SerializedCommandProperty(AccessLevel.GameMaster, canModify: true)] private TalismanAttribute _summoner; - [SerializableFieldSaveFlag(4)] public bool ShouldSerializeSummoner() => !_summoner.IsEmpty; - [SerializableFieldDefault(4)] private TalismanAttribute SummonerDefaultValue() => new(); [InvalidateProperties] [SerializableField(5)] + [SaveFlag(nameof(ShouldSerializeRemoval))] [SerializedCommandProperty(AccessLevel.GameMaster)] private TalismanRemoval _removal; - [SerializableFieldSaveFlag(5)] public bool ShouldSerializeRemoval() => _removal != TalismanRemoval.None; [InvalidateProperties] [SerializableField(6)] + [SaveFlag(nameof(ShouldSerializeSkill))] [SerializedCommandProperty(AccessLevel.GameMaster)] private SkillName _skill; - [SerializableFieldSaveFlag(6)] public bool ShouldSerializeSkill() => (int)_skill != 0; [EncodedInt] [InvalidateProperties] [SerializableField(7)] + [SaveFlag(nameof(ShouldSerializeSuccessBonus))] [SerializedCommandProperty(AccessLevel.GameMaster)] private int _successBonus; - [SerializableFieldSaveFlag(7)] public bool ShouldSerializeSuccessBonus() => _successBonus != 0; [EncodedInt] [InvalidateProperties] [SerializableField(8)] + [SaveFlag(nameof(ShouldSerializeExceptionalBonus))] [SerializedCommandProperty(AccessLevel.GameMaster)] private int _exceptionalBonus; - [SerializableFieldSaveFlag(8)] public bool ShouldSerializeExceptionalBonus() => _exceptionalBonus != 0; [EncodedInt] [InvalidateProperties] [SerializableField(9)] + [SaveFlag(nameof(ShouldSerializeMaxCharges))] [SerializedCommandProperty(AccessLevel.GameMaster)] private int _maxCharges; - [SerializableFieldSaveFlag(9)] public bool ShouldSerializeMaxCharges() => _maxCharges != 0; [EncodedInt] [InvalidateProperties] [SerializableField(11)] + [SaveFlag(nameof(ShouldSerializeMaxChargeTime))] [SerializedCommandProperty(AccessLevel.GameMaster)] private int _maxChargeTime; - [SerializableFieldSaveFlag(11)] public bool ShouldSerializeMaxChargeTime() => _maxChargeTime != 0; [EncodedInt] [InvalidateProperties] [SerializableField(12)] + [SaveFlag(nameof(ShouldSerializeChargeTime))] private int _chargeTime; - [SerializableFieldSaveFlag(12)] public bool ShouldSerializeChargeTime() => _chargeTime != 0; [InvalidateProperties] [SerializableField(13)] + [SaveFlag(nameof(ShouldSerializeBlessed))] [SerializedCommandProperty(AccessLevel.GameMaster)] private bool _blessed; - [SerializableFieldSaveFlag(13)] public bool ShouldSerializeBlessed() => _blessed; [InvalidateProperties] [SerializableField(14)] + [SaveFlag(nameof(ShouldSerializeSlayer))] [SerializedCommandProperty(AccessLevel.GameMaster)] private TalismanSlayerName _slayer; - [SerializableFieldSaveFlag(14)] public bool ShouldSerializeSlayer() => _slayer != TalismanSlayerName.None; private BaseCreature _creature; @@ -284,26 +279,20 @@ public partial class BaseTalisman : Item, IAosItem public override int LabelNumber => 1071023; // Talisman public virtual bool ForceShowName => false; // used to override default summoner/removal name - [SerializableProperty(10)] - [CommandProperty(AccessLevel.GameMaster)] - public int Charges + [SerializableField(10, fieldChanged: nameof(OnChargesChanged))] + [SaveFlag(nameof(ShouldSerializeCharges))] + [SerializedCommandProperty(AccessLevel.GameMaster)] + [InvalidateProperties] + private int _charges; + + private void OnChargesChanged(int oldValue, int newValue) { - get => _charges; - set + if (_chargeTime > 0) { - _charges = value; - - if (_chargeTime > 0) - { - StartTimer(); - } - - InvalidateProperties(); - this.MarkDirty(); + StartTimer(); } } - [SerializableFieldSaveFlag(10)] public bool ShouldSerializeCharges() => _charges != 0; public static void Configure() diff --git a/Projects/UOContent/Items/Weapons/BaseWeapon.Migrations.cs b/Projects/UOContent/Items/Weapons/BaseWeapon.Migrations.cs new file mode 100644 index 000000000..3385598d9 --- /dev/null +++ b/Projects/UOContent/Items/Weapons/BaseWeapon.Migrations.cs @@ -0,0 +1,311 @@ +using System; +using Server.Engines.Craft; + +namespace Server.Items; + +public partial class BaseWeapon +{ + // PlayerConstructed moved onto Item + private void MigrateFrom(V10Content content) + { + _damageLevel = content.DamageLevel ?? WeaponDamageLevel.Regular; + _accuracyLevel = content.AccuracyLevel ?? WeaponAccuracyLevel.Regular; + _durabilityLevel = content.DurabilityLevel ?? WeaponDurabilityLevel.Regular; + _quality = content.Quality ?? WeaponQuality.Regular; + _hitPoints = content.HitPoints ?? 0; + _maxHitPoints = content.MaxHitPoints ?? 0; + _slayer = content.Slayer ?? SlayerName.None; + _poison = content.Poison; + _poisonCharges = content.PoisonCharges ?? 0; + _crafter = content.Crafter; + _identified = content.Identified; + _strRequirement = content.StrRequirement ?? -1; + _dexRequirement = content.DexRequirement ?? -1; + _intRequirement = content.IntRequirement ?? -1; + _minDamage = content.MinDamage ?? -1; + _maxDamage = content.MaxDamage ?? -1; + _hitSound = content.HitSound ?? -1; + _missSound = content.MissSound ?? -1; + _speed = content.Speed ?? -1; + _maxRange = content.MaxRange ?? -1; + _skill = content.Skill ?? (SkillName)(-1); + _type = content.Type ?? (WeaponType)(-1); + _animation = content.Animation ?? (WeaponAnimation)(-1); + _resource = content.Resource ?? CraftResource.Iron; + _attributes = content.Attributes ?? AttributesDefaultValue(); + _weaponAttributes = content.WeaponAttributes ?? WeaponAttributesDefaultValue(); + PlayerConstructed = content.PlayerConstructed; + _skillBonuses = content.SkillBonuses ?? SkillBonusesDefaultValue(); + _slayer2 = content.Slayer2 ?? SlayerName.None; + _aosElementDamages = content.AosElementDamages ?? AosElementAttributesDefaultValue(); + _engravedText = content.EngravedText; + } + + // Version 9 (pre-codegen) + private void Deserialize(IGenericReader reader, int version) + { + var flags = (OldSaveFlag)reader.ReadInt(); + + if (GetSaveFlag(flags, OldSaveFlag.DamageLevel)) + { + _damageLevel = (WeaponDamageLevel)reader.ReadInt(); + } + + if (GetSaveFlag(flags, OldSaveFlag.AccuracyLevel)) + { + _accuracyLevel = (WeaponAccuracyLevel)reader.ReadInt(); + } + + if (GetSaveFlag(flags, OldSaveFlag.DurabilityLevel)) + { + _durabilityLevel = (WeaponDurabilityLevel)reader.ReadInt(); + } + + if (GetSaveFlag(flags, OldSaveFlag.Quality)) + { + _quality = (WeaponQuality)reader.ReadInt(); + } + else + { + _quality = WeaponQuality.Regular; + } + + if (GetSaveFlag(flags, OldSaveFlag.Hits)) + { + _hitPoints = reader.ReadInt(); + } + + if (GetSaveFlag(flags, OldSaveFlag.MaxHits)) + { + _maxHitPoints = reader.ReadInt(); + } + + if (GetSaveFlag(flags, OldSaveFlag.Slayer)) + { + _slayer = (SlayerName)reader.ReadInt(); + } + + if (GetSaveFlag(flags, OldSaveFlag.Poison)) + { + _poison = reader.ReadPoison(); + } + + if (GetSaveFlag(flags, OldSaveFlag.PoisonCharges)) + { + _poisonCharges = reader.ReadInt(); + } + + if (GetSaveFlag(flags, OldSaveFlag.Crafter)) + { + Timer.DelayCall(crafter => _crafter = crafter?.RawName, reader.ReadEntity()); + } + + if (GetSaveFlag(flags, OldSaveFlag.Identified)) + { + _identified = version >= 6 || reader.ReadBool(); + } + + if (GetSaveFlag(flags, OldSaveFlag.StrReq)) + { + _strRequirement = reader.ReadInt(); + } + else + { + _strRequirement = -1; + } + + if (GetSaveFlag(flags, OldSaveFlag.DexReq)) + { + _dexRequirement = reader.ReadInt(); + } + else + { + _dexRequirement = -1; + } + + if (GetSaveFlag(flags, OldSaveFlag.IntReq)) + { + _intRequirement = reader.ReadInt(); + } + else + { + _intRequirement = -1; + } + + if (GetSaveFlag(flags, OldSaveFlag.MinDamage)) + { + _minDamage = reader.ReadInt(); + } + else + { + _minDamage = -1; + } + + if (GetSaveFlag(flags, OldSaveFlag.MaxDamage)) + { + _maxDamage = reader.ReadInt(); + } + else + { + _maxDamage = -1; + } + + if (GetSaveFlag(flags, OldSaveFlag.HitSound)) + { + _hitSound = reader.ReadInt(); + } + else + { + _hitSound = -1; + } + + if (GetSaveFlag(flags, OldSaveFlag.MissSound)) + { + _missSound = reader.ReadInt(); + } + else + { + _missSound = -1; + } + + if (GetSaveFlag(flags, OldSaveFlag.Speed)) + { + if (version < 9) + { + _speed = reader.ReadInt(); + } + else + { + _speed = reader.ReadFloat(); + } + } + else + { + _speed = -1; + } + + if (GetSaveFlag(flags, OldSaveFlag.MaxRange)) + { + _maxRange = reader.ReadInt(); + } + else + { + _maxRange = -1; + } + + if (GetSaveFlag(flags, OldSaveFlag.Skill)) + { + _skill = (SkillName)reader.ReadInt(); + } + else + { + _skill = (SkillName)(-1); + } + + if (GetSaveFlag(flags, OldSaveFlag.Type)) + { + _type = (WeaponType)reader.ReadInt(); + } + else + { + _type = (WeaponType)(-1); + } + + if (GetSaveFlag(flags, OldSaveFlag.Animation)) + { + _animation = (WeaponAnimation)reader.ReadInt(); + } + else + { + _animation = (WeaponAnimation)(-1); + } + + if (GetSaveFlag(flags, OldSaveFlag.Resource)) + { + _resource = (CraftResource)reader.ReadInt(); + } + else + { + _resource = CraftResource.Iron; + } + + Attributes = new AosAttributes(this); + + if (GetSaveFlag(flags, OldSaveFlag.Attributes)) + { + Attributes.Deserialize(reader); + } + + WeaponAttributes = new AosWeaponAttributes(this); + + if (GetSaveFlag(flags, OldSaveFlag.WeaponAttributes)) + { + WeaponAttributes.Deserialize(reader); + } + + PlayerConstructed = GetSaveFlag(flags, OldSaveFlag.PlayerConstructed); + + SkillBonuses = new AosSkillBonuses(this); + + if (GetSaveFlag(flags, OldSaveFlag.SkillBonuses)) + { + SkillBonuses.Deserialize(reader); + } + + if (GetSaveFlag(flags, OldSaveFlag.Slayer2)) + { + _slayer2 = (SlayerName)reader.ReadInt(); + } + + AosElementDamages = new AosElementAttributes(this); + + if (GetSaveFlag(flags, OldSaveFlag.ElementalDamages)) + { + AosElementDamages.Deserialize(reader); + } + + if (GetSaveFlag(flags, OldSaveFlag.EngravedText)) + { + _engravedText = reader.ReadString(); + } + } + + private static bool GetSaveFlag(OldSaveFlag flags, OldSaveFlag toGet) => (flags & toGet) != 0; + + [Flags] + private enum OldSaveFlag + { + None = 0x00000000, + DamageLevel = 0x00000001, + AccuracyLevel = 0x00000002, + DurabilityLevel = 0x00000004, + Quality = 0x00000008, + Hits = 0x00000010, + MaxHits = 0x00000020, + Slayer = 0x00000040, + Poison = 0x00000080, + PoisonCharges = 0x00000100, + Crafter = 0x00000200, + Identified = 0x00000400, + StrReq = 0x00000800, + DexReq = 0x00001000, + IntReq = 0x00002000, + MinDamage = 0x00004000, + MaxDamage = 0x00008000, + HitSound = 0x00010000, + MissSound = 0x00020000, + Speed = 0x00040000, + MaxRange = 0x00080000, + Skill = 0x00100000, + Type = 0x00200000, + Animation = 0x00400000, + Resource = 0x00800000, + Attributes = 0x01000000, + WeaponAttributes = 0x02000000, + PlayerConstructed = 0x04000000, + SkillBonuses = 0x08000000, + Slayer2 = 0x10000000, + ElementalDamages = 0x20000000, + EngravedText = 0x40000000 + } +} diff --git a/Projects/UOContent/Items/Weapons/BaseWeapon.cs b/Projects/UOContent/Items/Weapons/BaseWeapon.cs index b28f0d371..d48824670 100644 --- a/Projects/UOContent/Items/Weapons/BaseWeapon.cs +++ b/Projects/UOContent/Items/Weapons/BaseWeapon.cs @@ -27,7 +27,7 @@ public interface ISlayer SlayerName Slayer2 { get; set; } } -[SerializationGenerator(10, false)] +[SerializationGenerator(11, false)] public abstract partial class BaseWeapon : Item, IWeapon, IFactionItem, ICraftable, ISlayer, IDurability, IAosItem, IIdentifiable { @@ -56,138 +56,126 @@ public abstract partial class BaseWeapon [InvalidateProperties] [SerializableField(0)] + [SaveFlag(nameof(ShouldSerializeDamageLevel))] [SerializedCommandProperty(AccessLevel.GameMaster)] private WeaponDamageLevel _damageLevel; - [SerializableFieldSaveFlag(0)] [MethodImpl(MethodImplOptions.AggressiveInlining)] private bool ShouldSerializeDamageLevel() => _damageLevel != WeaponDamageLevel.Regular; [InvalidateProperties] [SerializableField(5)] + [SaveFlag(nameof(ShouldSerializeMaxHitPoints))] [SerializedCommandProperty(AccessLevel.GameMaster)] private int _maxHitPoints; - [SerializableFieldSaveFlag(5)] [MethodImpl(MethodImplOptions.AggressiveInlining)] private bool ShouldSerializeMaxHitPoints() => _maxHitPoints != 0; [InvalidateProperties] [SerializableField(6)] + [SaveFlag(nameof(ShouldSerializeSlayer))] [SerializedCommandProperty(AccessLevel.GameMaster)] private SlayerName _slayer; - [SerializableFieldSaveFlag(6)] [MethodImpl(MethodImplOptions.AggressiveInlining)] private bool ShouldSerializeSlayer() => _slayer != SlayerName.None; [InvalidateProperties] [SerializableField(7)] + [SaveFlag(nameof(ShouldSerializePoison))] [SerializedCommandProperty(AccessLevel.GameMaster)] private Poison _poison; - [SerializableFieldSaveFlag(7)] [MethodImpl(MethodImplOptions.AggressiveInlining)] private bool ShouldSerializePoison() => _poison != null; [InvalidateProperties] [SerializableField(8)] + [SaveFlag(nameof(ShouldSerializePoisonCharges))] [SerializedCommandProperty(AccessLevel.GameMaster)] private int _poisonCharges; - [SerializableFieldSaveFlag(8)] [MethodImpl(MethodImplOptions.AggressiveInlining)] private bool ShouldSerializePoisonCharges() => _poisonCharges > 0; [InvalidateProperties] [SerializableField(9)] + [SaveFlag(nameof(ShouldSerializeCrafter))] [SerializedCommandProperty(AccessLevel.GameMaster)] private string _crafter; - [SerializableFieldSaveFlag(9)] [MethodImpl(MethodImplOptions.AggressiveInlining)] private bool ShouldSerializeCrafter() => !string.IsNullOrEmpty(_crafter); [InvalidateProperties] [SerializableField(10)] + [SaveFlag(nameof(ShouldSerializeIdentified))] [SerializedCommandProperty(AccessLevel.GameMaster)] private bool _identified; - [SerializableFieldSaveFlag(10)] [MethodImpl(MethodImplOptions.AggressiveInlining)] private bool ShouldSerializeIdentified() => _identified; [SerializedIgnoreDupe] [SerializableField(24, setter: "private")] + [SaveFlag(nameof(ShouldSerializeAttributes), nameof(AttributesDefaultValue))] [SerializedCommandProperty(AccessLevel.GameMaster, canModify: true)] private AosAttributes _attributes; - [SerializableFieldSaveFlag(24)] [MethodImpl(MethodImplOptions.AggressiveInlining)] private bool ShouldSerializeAttributes() => !_attributes.IsEmpty; - [SerializableFieldDefault(24)] private AosAttributes AttributesDefaultValue() => new(this); [SerializedIgnoreDupe] [SerializableField(25, setter: "private")] + [SaveFlag(nameof(ShouldSerializeWeaponAttributes), nameof(WeaponAttributesDefaultValue))] [SerializedCommandProperty(AccessLevel.GameMaster, canModify: true)] private AosWeaponAttributes _weaponAttributes; - [SerializableFieldSaveFlag(25)] [MethodImpl(MethodImplOptions.AggressiveInlining)] private bool ShouldSerializeWeaponAttributes() => !_weaponAttributes.IsEmpty; - [SerializableFieldDefault(25)] private AosWeaponAttributes WeaponAttributesDefaultValue() => new(this); - [SerializableField(26)] - [SerializedCommandProperty(AccessLevel.GameMaster)] - private bool _playerConstructed; - - [SerializableFieldSaveFlag(26)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private bool ShouldSerializePlayerConstructed() => _playerConstructed; - [SerializedIgnoreDupe] - [SerializableField(27, setter: "private")] + [SerializableField(26, setter: "private")] + [SaveFlag(nameof(ShouldSerializeSkillBonuses), nameof(SkillBonusesDefaultValue))] [SerializedCommandProperty(AccessLevel.GameMaster, canModify: true)] private AosSkillBonuses _skillBonuses; - [SerializableFieldSaveFlag(27)] [MethodImpl(MethodImplOptions.AggressiveInlining)] private bool ShouldSerializeSkillBonuses() => !_skillBonuses.IsEmpty; - [SerializableFieldDefault(27)] private AosSkillBonuses SkillBonusesDefaultValue() => new(this); [InvalidateProperties] - [SerializableField(28)] + [SerializableField(27)] + [SaveFlag(nameof(ShouldSerializeSlayer2))] [SerializedCommandProperty(AccessLevel.GameMaster)] private SlayerName _slayer2; - [SerializableFieldSaveFlag(28)] [MethodImpl(MethodImplOptions.AggressiveInlining)] private bool ShouldSerializeSlayer2() => _slayer2 != SlayerName.None; [SerializedIgnoreDupe] - [SerializableField(29, setter: "private")] + [SerializableField(28, setter: "private")] + [SaveFlag(nameof(ShouldSerializeElementAttributes), nameof(AosElementAttributesDefaultValue))] [SerializedCommandProperty(AccessLevel.GameMaster, canModify: true)] private AosElementAttributes _aosElementDamages; - [SerializableFieldSaveFlag(29)] [MethodImpl(MethodImplOptions.AggressiveInlining)] private bool ShouldSerializeElementAttributes() => !_aosElementDamages.IsEmpty; - [SerializableFieldDefault(29)] private AosElementAttributes AosElementAttributesDefaultValue() => new(this); [InvalidateProperties] - [SerializableField(30)] + [SerializableField(29)] + [SaveFlag(nameof(ShouldSerializeEngravedText))] [SerializedCommandProperty(AccessLevel.GameMaster)] private string _engravedText; - [SerializableFieldSaveFlag(30)] [MethodImpl(MethodImplOptions.AggressiveInlining)] private bool ShouldSerializeEngravedText() => !string.IsNullOrEmpty(_engravedText); @@ -294,6 +282,7 @@ public abstract partial class BaseWeapon public bool Consecrated { get; set; } [SerializableProperty(1)] + [SaveFlag(nameof(ShouldSerializeWeaponAccuracy))] [CommandProperty(AccessLevel.GameMaster)] public WeaponAccuracyLevel AccuracyLevel { @@ -329,10 +318,10 @@ public abstract partial class BaseWeapon } } - [SerializableFieldSaveFlag(1)] private bool ShouldSerializeWeaponAccuracy() => _accuracyLevel != WeaponAccuracyLevel.Regular; [SerializableProperty(2)] + [SaveFlag(nameof(ShouldSerializeDurabilityLevel))] [CommandProperty(AccessLevel.GameMaster)] public WeaponDurabilityLevel DurabilityLevel { @@ -347,10 +336,10 @@ public abstract partial class BaseWeapon } } - [SerializableFieldSaveFlag(2)] private bool ShouldSerializeDurabilityLevel() => _durabilityLevel != WeaponDurabilityLevel.Regular; [SerializableProperty(3)] + [SaveFlag(nameof(ShouldSerializeQuality), nameof(QualityDefaultValue))] [CommandProperty(AccessLevel.GameMaster)] public WeaponQuality Quality { @@ -365,13 +354,12 @@ public abstract partial class BaseWeapon } } - [SerializableFieldSaveFlag(3)] private bool ShouldSerializeQuality() => _quality != WeaponQuality.Regular; - [SerializableFieldDefault(3)] private WeaponQuality QualityDefaultValue() => WeaponQuality.Regular; [SerializableProperty(4)] + [SaveFlag(nameof(ShouldSerializeHitPoints))] [CommandProperty(AccessLevel.GameMaster)] public int HitPoints { @@ -395,10 +383,10 @@ public abstract partial class BaseWeapon } } - [SerializableFieldSaveFlag(4)] private bool ShouldSerializeHitPoints() => _hitPoints > 0; [SerializableProperty(11)] + [SaveFlag(nameof(ShouldSerializeStrReq), nameof(StrReqDefaultValue))] [CommandProperty(AccessLevel.GameMaster)] public int StrRequirement { @@ -411,13 +399,12 @@ public abstract partial class BaseWeapon } } - [SerializableFieldSaveFlag(11)] private bool ShouldSerializeStrReq() => _strRequirement != -1; - [SerializableFieldDefault(11)] private int StrReqDefaultValue() => -1; [SerializableProperty(12)] + [SaveFlag(nameof(ShouldSerializeDexReq), nameof(DexReqDefaultValue))] [CommandProperty(AccessLevel.GameMaster)] public int DexRequirement { @@ -430,13 +417,12 @@ public abstract partial class BaseWeapon } } - [SerializableFieldSaveFlag(12)] private bool ShouldSerializeDexReq() => _dexRequirement != -1; - [SerializableFieldDefault(12)] private int DexReqDefaultValue() => -1; [SerializableProperty(13)] + [SaveFlag(nameof(ShouldSerializeIntReq), nameof(IntReqDefaultValue))] [CommandProperty(AccessLevel.GameMaster)] public int IntRequirement { @@ -449,13 +435,12 @@ public abstract partial class BaseWeapon } } - [SerializableFieldSaveFlag(13)] private bool ShouldSerializeIntReq() => _intRequirement != -1; - [SerializableFieldDefault(13)] private int IntReqDefaultValue() => -1; [SerializableProperty(14)] + [SaveFlag(nameof(ShouldSerializeMinDamage), nameof(MinDamageDefaultValue))] [CommandProperty(AccessLevel.GameMaster)] public int MinDamage { @@ -468,13 +453,12 @@ public abstract partial class BaseWeapon } } - [SerializableFieldSaveFlag(14)] private bool ShouldSerializeMinDamage() => _minDamage != -1; - [SerializableFieldDefault(14)] private int MinDamageDefaultValue() => -1; [SerializableProperty(15)] + [SaveFlag(nameof(ShouldSerializeMaxDamage), nameof(MaxDamageDefaultValue))] [CommandProperty(AccessLevel.GameMaster)] public int MaxDamage { @@ -487,13 +471,12 @@ public abstract partial class BaseWeapon } } - [SerializableFieldSaveFlag(15)] private bool ShouldSerializeMaxDamage() => _maxDamage != -1; - [SerializableFieldDefault(15)] private int MaxDamageDefaultValue() => -1; [SerializableProperty(16)] + [SaveFlag(nameof(ShouldSerializeHitSound), nameof(HitSoundDefaultValue))] [CommandProperty(AccessLevel.GameMaster)] public int HitSound { @@ -505,13 +488,12 @@ public abstract partial class BaseWeapon } } - [SerializableFieldSaveFlag(16)] private bool ShouldSerializeHitSound() => _hitSound != -1; - [SerializableFieldDefault(16)] private int HitSoundDefaultValue() => -1; [SerializableProperty(17)] + [SaveFlag(nameof(ShouldSerializeMissSound), nameof(MissSoundDefaultValue))] [CommandProperty(AccessLevel.GameMaster)] public int MissSound { @@ -523,13 +505,12 @@ public abstract partial class BaseWeapon } } - [SerializableFieldSaveFlag(17)] private bool ShouldSerializeMissSound() => _missSound != -1; - [SerializableFieldDefault(17)] private int MissSoundDefaultValue() => -1; [SerializableProperty(18)] + [SaveFlag(nameof(ShouldSerializeSpeed), nameof(SpeedDefaultValue))] [CommandProperty(AccessLevel.GameMaster)] public float Speed { @@ -560,13 +541,12 @@ public abstract partial class BaseWeapon } } - [SerializableFieldSaveFlag(18)] private bool ShouldSerializeSpeed() => _speed != -1; - [SerializableFieldDefault(18)] private float SpeedDefaultValue() => -1; [SerializableProperty(19)] + [SaveFlag(nameof(ShouldSerializeMaxRange), nameof(MaxRangeDefaultValue))] [CommandProperty(AccessLevel.GameMaster)] public int MaxRange { @@ -579,13 +559,12 @@ public abstract partial class BaseWeapon } } - [SerializableFieldSaveFlag(19)] private bool ShouldSerializeMaxRange() => _maxRange != -1; - [SerializableFieldDefault(19)] private int MaxRangeDefaultValue() => -1; [SerializableProperty(20)] + [SaveFlag(nameof(ShouldSerializeSkill), nameof(SkillNameDefaultValue))] [CommandProperty(AccessLevel.GameMaster)] public SkillName Skill { @@ -598,13 +577,12 @@ public abstract partial class BaseWeapon } } - [SerializableFieldSaveFlag(20)] private bool ShouldSerializeSkill() => _skill != (SkillName)(-1); - [SerializableFieldDefault(20)] private SkillName SkillNameDefaultValue() => (SkillName)(-1); [SerializableProperty(21)] + [SaveFlag(nameof(ShouldSerializeType), nameof(TypeDefaultValue))] [CommandProperty(AccessLevel.GameMaster)] public WeaponType Type { @@ -616,13 +594,12 @@ public abstract partial class BaseWeapon } } - [SerializableFieldSaveFlag(21)] private bool ShouldSerializeType() => _type != (WeaponType)(-1); - [SerializableFieldDefault(21)] private WeaponType TypeDefaultValue() => (WeaponType)(-1); [SerializableProperty(22)] + [SaveFlag(nameof(ShouldSerializeAnimation), nameof(AnimationDefaultValue))] [CommandProperty(AccessLevel.GameMaster)] public WeaponAnimation Animation { @@ -634,13 +611,12 @@ public abstract partial class BaseWeapon } } - [SerializableFieldSaveFlag(22)] private bool ShouldSerializeAnimation() => _animation != (WeaponAnimation)(-1); - [SerializableFieldDefault(22)] private WeaponAnimation AnimationDefaultValue() => (WeaponAnimation)(-1); [SerializableProperty(23)] + [SaveFlag(nameof(ShouldSerializeResource), nameof(ResourceDefaultValue))] [CommandProperty(AccessLevel.GameMaster)] public CraftResource Resource { @@ -656,10 +632,8 @@ public abstract partial class BaseWeapon } } - [SerializableFieldSaveFlag(23)] private bool ShouldSerializeResource() => _resource != CraftResource.Iron; - [SerializableFieldDefault(23)] private CraftResource ResourceDefaultValue() => CraftResource.Iron; public virtual int OnCraft( @@ -674,7 +648,6 @@ public abstract partial class BaseWeapon Crafter = from.RawName; } - PlayerConstructed = true; Identified = true; var resourceType = typeRes ?? craftItem.Resources[0].ItemType; @@ -3580,236 +3553,6 @@ public abstract partial class BaseWeapon } } - private static bool GetSaveFlag(OldSaveFlag flags, OldSaveFlag toGet) => (flags & toGet) != 0; - - private void Deserialize(IGenericReader reader, int version) - { - var flags = (OldSaveFlag)reader.ReadInt(); - - if (GetSaveFlag(flags, OldSaveFlag.DamageLevel)) - { - _damageLevel = (WeaponDamageLevel)reader.ReadInt(); - } - - if (GetSaveFlag(flags, OldSaveFlag.AccuracyLevel)) - { - _accuracyLevel = (WeaponAccuracyLevel)reader.ReadInt(); - } - - if (GetSaveFlag(flags, OldSaveFlag.DurabilityLevel)) - { - _durabilityLevel = (WeaponDurabilityLevel)reader.ReadInt(); - } - - if (GetSaveFlag(flags, OldSaveFlag.Quality)) - { - _quality = (WeaponQuality)reader.ReadInt(); - } - else - { - _quality = WeaponQuality.Regular; - } - - if (GetSaveFlag(flags, OldSaveFlag.Hits)) - { - _hitPoints = reader.ReadInt(); - } - - if (GetSaveFlag(flags, OldSaveFlag.MaxHits)) - { - _maxHitPoints = reader.ReadInt(); - } - - if (GetSaveFlag(flags, OldSaveFlag.Slayer)) - { - _slayer = (SlayerName)reader.ReadInt(); - } - - if (GetSaveFlag(flags, OldSaveFlag.Poison)) - { - _poison = reader.ReadPoison(); - } - - if (GetSaveFlag(flags, OldSaveFlag.PoisonCharges)) - { - _poisonCharges = reader.ReadInt(); - } - - if (GetSaveFlag(flags, OldSaveFlag.Crafter)) - { - Timer.DelayCall(crafter => _crafter = crafter?.RawName, reader.ReadEntity()); - } - - if (GetSaveFlag(flags, OldSaveFlag.Identified)) - { - _identified = version >= 6 || reader.ReadBool(); - } - - if (GetSaveFlag(flags, OldSaveFlag.StrReq)) - { - _strRequirement = reader.ReadInt(); - } - else - { - _strRequirement = -1; - } - - if (GetSaveFlag(flags, OldSaveFlag.DexReq)) - { - _dexRequirement = reader.ReadInt(); - } - else - { - _dexRequirement = -1; - } - - if (GetSaveFlag(flags, OldSaveFlag.IntReq)) - { - _intRequirement = reader.ReadInt(); - } - else - { - _intRequirement = -1; - } - - if (GetSaveFlag(flags, OldSaveFlag.MinDamage)) - { - _minDamage = reader.ReadInt(); - } - else - { - _minDamage = -1; - } - - if (GetSaveFlag(flags, OldSaveFlag.MaxDamage)) - { - _maxDamage = reader.ReadInt(); - } - else - { - _maxDamage = -1; - } - - if (GetSaveFlag(flags, OldSaveFlag.HitSound)) - { - _hitSound = reader.ReadInt(); - } - else - { - _hitSound = -1; - } - - if (GetSaveFlag(flags, OldSaveFlag.MissSound)) - { - _missSound = reader.ReadInt(); - } - else - { - _missSound = -1; - } - - if (GetSaveFlag(flags, OldSaveFlag.Speed)) - { - if (version < 9) - { - _speed = reader.ReadInt(); - } - else - { - _speed = reader.ReadFloat(); - } - } - else - { - _speed = -1; - } - - if (GetSaveFlag(flags, OldSaveFlag.MaxRange)) - { - _maxRange = reader.ReadInt(); - } - else - { - _maxRange = -1; - } - - if (GetSaveFlag(flags, OldSaveFlag.Skill)) - { - _skill = (SkillName)reader.ReadInt(); - } - else - { - _skill = (SkillName)(-1); - } - - if (GetSaveFlag(flags, OldSaveFlag.Type)) - { - _type = (WeaponType)reader.ReadInt(); - } - else - { - _type = (WeaponType)(-1); - } - - if (GetSaveFlag(flags, OldSaveFlag.Animation)) - { - _animation = (WeaponAnimation)reader.ReadInt(); - } - else - { - _animation = (WeaponAnimation)(-1); - } - - if (GetSaveFlag(flags, OldSaveFlag.Resource)) - { - _resource = (CraftResource)reader.ReadInt(); - } - else - { - _resource = CraftResource.Iron; - } - - Attributes = new AosAttributes(this); - - if (GetSaveFlag(flags, OldSaveFlag.Attributes)) - { - Attributes.Deserialize(reader); - } - - WeaponAttributes = new AosWeaponAttributes(this); - - if (GetSaveFlag(flags, OldSaveFlag.WeaponAttributes)) - { - WeaponAttributes.Deserialize(reader); - } - - PlayerConstructed = GetSaveFlag(flags, OldSaveFlag.PlayerConstructed); - - SkillBonuses = new AosSkillBonuses(this); - - if (GetSaveFlag(flags, OldSaveFlag.SkillBonuses)) - { - SkillBonuses.Deserialize(reader); - } - - if (GetSaveFlag(flags, OldSaveFlag.Slayer2)) - { - _slayer2 = (SlayerName)reader.ReadInt(); - } - - AosElementDamages = new AosElementAttributes(this); - - if (GetSaveFlag(flags, OldSaveFlag.ElementalDamages)) - { - AosElementDamages.Deserialize(reader); - } - - if (GetSaveFlag(flags, OldSaveFlag.EngravedText)) - { - _engravedText = reader.ReadString(); - } - } - [AfterDeserialization] private void AfterDeserialization() { @@ -3875,42 +3618,6 @@ public abstract partial class BaseWeapon } } - [Flags] - private enum OldSaveFlag - { - None = 0x00000000, - DamageLevel = 0x00000001, - AccuracyLevel = 0x00000002, - DurabilityLevel = 0x00000004, - Quality = 0x00000008, - Hits = 0x00000010, - MaxHits = 0x00000020, - Slayer = 0x00000040, - Poison = 0x00000080, - PoisonCharges = 0x00000100, - Crafter = 0x00000200, - Identified = 0x00000400, - StrReq = 0x00000800, - DexReq = 0x00001000, - IntReq = 0x00002000, - MinDamage = 0x00004000, - MaxDamage = 0x00008000, - HitSound = 0x00010000, - MissSound = 0x00020000, - Speed = 0x00040000, - MaxRange = 0x00080000, - Skill = 0x00100000, - Type = 0x00200000, - Animation = 0x00400000, - Resource = 0x00800000, - Attributes = 0x01000000, - WeaponAttributes = 0x02000000, - PlayerConstructed = 0x04000000, - SkillBonuses = 0x08000000, - Slayer2 = 0x10000000, - ElementalDamages = 0x20000000, - EngravedText = 0x40000000 - } } public enum CheckSlayerResult diff --git a/Projects/UOContent/Migrations/Server.Engines.CannedEvil.ChampionSpawn.v11.json b/Projects/UOContent/Migrations/Server.Engines.CannedEvil.ChampionSpawn.v11.json new file mode 100644 index 000000000..defcbe100 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Engines.CannedEvil.ChampionSpawn.v11.json @@ -0,0 +1,189 @@ +{ + "version": 11, + "type": "Server.Engines.CannedEvil.ChampionSpawn", + "properties": [ + { + "name": "Level", + "type": "int", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "ActivatedByProximity", + "type": "bool", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "NextProximityTime", + "type": "System.DateTime", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "AnchoredTime" + ] + }, + { + "name": "MaxLevel", + "type": "int", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "ActivatedByValor", + "type": "bool", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "DamageEntries", + "type": "System.Collections.Generic.Dictionary\u003CServer.Mobile, int\u003E", + "rule": "DictionaryMigrationRule", + "ruleArguments": [ + "Server.Mobile", + "SerializableInterfaceMigrationRule", + "0", + "int", + "PrimitiveTypeMigrationRule", + "1", + "" + ] + }, + { + "name": "ConfinedRoaming", + "type": "bool", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "Idol", + "type": "Server.Engines.CannedEvil.IdolOfTheChampion", + "rule": "SerializableInterfaceMigrationRule" + }, + { + "name": "HasBeenAdvanced", + "type": "bool", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "SpawnArea", + "type": "Server.Rectangle2D", + "rule": "PrimitiveUOTypeMigrationRule", + "ruleArguments": [ + "Rect2D" + ] + }, + { + "name": "RandomizeType", + "type": "bool", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "Kills", + "type": "int", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "Active", + "type": "bool", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "Type", + "type": "Server.Engines.CannedEvil.ChampionSpawnType", + "rule": "EnumMigrationRule" + }, + { + "name": "Creatures", + "type": "System.Collections.Generic.List\u003CServer.Mobile\u003E", + "rule": "ListMigrationRule", + "ruleArguments": [ + "@Tidy", + "Server.Mobile", + "SerializableInterfaceMigrationRule" + ] + }, + { + "name": "RedSkulls", + "type": "System.Collections.Generic.List\u003CServer.Item\u003E", + "rule": "ListMigrationRule", + "ruleArguments": [ + "@Tidy", + "Server.Item", + "SerializableInterfaceMigrationRule" + ] + }, + { + "name": "WhiteSkulls", + "type": "System.Collections.Generic.List\u003CServer.Item\u003E", + "rule": "ListMigrationRule", + "ruleArguments": [ + "@Tidy", + "Server.Item", + "SerializableInterfaceMigrationRule" + ] + }, + { + "name": "Platform", + "type": "Server.Engines.CannedEvil.ChampionPlatform", + "rule": "SerializableInterfaceMigrationRule" + }, + { + "name": "Altar", + "type": "Server.Engines.CannedEvil.ChampionAltar", + "rule": "SerializableInterfaceMigrationRule" + }, + { + "name": "ExpireDelay", + "type": "System.TimeSpan", + "rule": "PrimitiveTypeMigrationRule" + }, + { + "name": "ExpireTime", + "type": "System.DateTime", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "AnchoredTime" + ] + }, + { + "name": "Champion", + "type": "Server.Mobile", + "rule": "SerializableInterfaceMigrationRule" + }, + { + "name": "RestartDelay", + "type": "System.TimeSpan", + "rule": "PrimitiveTypeMigrationRule" + }, + { + "name": "RestartTime", + "type": "System.DateTime", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "AnchoredTime" + ] + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Engines.Virtues.VirtueContext.v1.json b/Projects/UOContent/Migrations/Server.Engines.Virtues.VirtueContext.v1.json new file mode 100644 index 000000000..956de91db --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Engines.Virtues.VirtueContext.v1.json @@ -0,0 +1,119 @@ +{ + "version": 1, + "type": "Server.Engines.Virtues.VirtueContext", + "properties": [ + { + "name": "LastSacrificeGain", + "type": "System.DateTime", + "usesSaveFlag": true, + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "AnchoredTime" + ] + }, + { + "name": "LastSacrificeLoss", + "type": "System.DateTime", + "usesSaveFlag": true, + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "AnchoredTime" + ] + }, + { + "name": "AvailableResurrects", + "type": "int", + "usesSaveFlag": true, + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "LastJusticeLoss", + "type": "System.DateTime", + "usesSaveFlag": true, + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "AnchoredTime" + ] + }, + { + "name": "LastCompassionLoss", + "type": "System.DateTime", + "usesSaveFlag": true, + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "AnchoredTime" + ] + }, + { + "name": "NextCompassionDay", + "type": "System.DateTime", + "usesSaveFlag": true, + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "AnchoredTime" + ] + }, + { + "name": "CompassionGains", + "type": "int", + "usesSaveFlag": true, + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "LastValorLoss", + "type": "System.DateTime", + "usesSaveFlag": true, + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "AnchoredTime" + ] + }, + { + "name": "LastHonorUse", + "type": "System.DateTime", + "usesSaveFlag": true, + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "AnchoredTime" + ] + }, + { + "name": "HonorActive", + "type": "bool", + "usesSaveFlag": true, + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "JusticeProtection", + "type": "Server.Mobiles.PlayerMobile", + "usesSaveFlag": true, + "rule": "SerializableInterfaceMigrationRule" + }, + { + "name": "JusticeStatus", + "type": "Server.Engines.Virtues.JusticeProtectorStatus", + "usesSaveFlag": true, + "rule": "EnumMigrationRule" + }, + { + "name": "Values", + "type": "int[]", + "usesSaveFlag": true, + "rule": "ArrayMigrationRule", + "ruleArguments": [ + "int", + "PrimitiveTypeMigrationRule", + "" + ] + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Ethics.Player.v2.json b/Projects/UOContent/Migrations/Server.Ethics.Player.v2.json new file mode 100644 index 000000000..04f4a0199 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Ethics.Player.v2.json @@ -0,0 +1,50 @@ +{ + "version": 2, + "type": "Server.Ethics.Player", + "properties": [ + { + "name": "Mobile", + "type": "Server.Mobile", + "rule": "SerializableInterfaceMigrationRule" + }, + { + "name": "Power", + "type": "int", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "History", + "type": "int", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "Steed", + "type": "Server.Mobile", + "rule": "SerializableInterfaceMigrationRule" + }, + { + "name": "Familiar", + "type": "Server.Mobile", + "rule": "SerializableInterfaceMigrationRule" + }, + { + "name": "Shield", + "type": "System.DateTime", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "AnchoredTime" + ] + }, + { + "name": "Ethic", + "type": "Server.Ethics.Ethic", + "rule": "SerializableInterfaceMigrationRule" + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.BaseArmor.v10.json b/Projects/UOContent/Migrations/Server.Items.BaseArmor.v10.json new file mode 100644 index 000000000..d7844e116 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.BaseArmor.v10.json @@ -0,0 +1,207 @@ +{ + "version": 10, + "type": "Server.Items.BaseArmor", + "properties": [ + { + "name": "Attributes", + "type": "Server.AosAttributes", + "usesSaveFlag": true, + "rule": "RawSerializableMigrationRule", + "ruleArguments": [ + "DeserializationRequiresParent" + ] + }, + { + "name": "ArmorAttributes", + "type": "Server.AosArmorAttributes", + "usesSaveFlag": true, + "rule": "RawSerializableMigrationRule", + "ruleArguments": [ + "DeserializationRequiresParent" + ] + }, + { + "name": "PhysicalBonus", + "type": "int", + "usesSaveFlag": true, + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "EncodedInt" + ] + }, + { + "name": "FireBonus", + "type": "int", + "usesSaveFlag": true, + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "EncodedInt" + ] + }, + { + "name": "ColdBonus", + "type": "int", + "usesSaveFlag": true, + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "EncodedInt" + ] + }, + { + "name": "PoisonBonus", + "type": "int", + "usesSaveFlag": true, + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "EncodedInt" + ] + }, + { + "name": "EnergyBonus", + "type": "int", + "usesSaveFlag": true, + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "EncodedInt" + ] + }, + { + "name": "Identified", + "type": "bool", + "usesSaveFlag": true, + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "MaxHitPoints", + "type": "int", + "usesSaveFlag": true, + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "EncodedInt" + ] + }, + { + "name": "HitPoints", + "type": "int", + "usesSaveFlag": true, + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "EncodedInt" + ] + }, + { + "name": "Crafter", + "type": "string", + "usesSaveFlag": true, + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "Quality", + "type": "Server.Items.ArmorQuality", + "usesSaveFlag": true, + "rule": "EnumMigrationRule" + }, + { + "name": "Durability", + "type": "Server.Items.ArmorDurabilityLevel", + "usesSaveFlag": true, + "rule": "EnumMigrationRule" + }, + { + "name": "ProtectionLevel", + "type": "Server.Items.ArmorProtectionLevel", + "usesSaveFlag": true, + "rule": "EnumMigrationRule" + }, + { + "name": "Resource", + "type": "Server.Items.CraftResource", + "usesSaveFlag": true, + "rule": "EnumMigrationRule" + }, + { + "name": "BaseArmorRating", + "type": "int", + "usesSaveFlag": true, + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "EncodedInt" + ] + }, + { + "name": "StrBonus", + "type": "int", + "usesSaveFlag": true, + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "EncodedInt" + ] + }, + { + "name": "DexBonus", + "type": "int", + "usesSaveFlag": true, + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "EncodedInt" + ] + }, + { + "name": "IntBonus", + "type": "int", + "usesSaveFlag": true, + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "EncodedInt" + ] + }, + { + "name": "StrRequirement", + "type": "int", + "usesSaveFlag": true, + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "EncodedInt" + ] + }, + { + "name": "DexRequirement", + "type": "int", + "usesSaveFlag": true, + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "EncodedInt" + ] + }, + { + "name": "IntRequirement", + "type": "int", + "usesSaveFlag": true, + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "EncodedInt" + ] + }, + { + "name": "MeditationAllowance", + "type": "Server.Items.ArmorMeditationAllowance", + "usesSaveFlag": true, + "rule": "EnumMigrationRule" + }, + { + "name": "SkillBonuses", + "type": "Server.AosSkillBonuses", + "usesSaveFlag": true, + "rule": "RawSerializableMigrationRule", + "ruleArguments": [ + "DeserializationRequiresParent" + ] + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.BaseClothing.v8.json b/Projects/UOContent/Migrations/Server.Items.BaseClothing.v8.json new file mode 100644 index 000000000..09d5e07be --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.BaseClothing.v8.json @@ -0,0 +1,90 @@ +{ + "version": 8, + "type": "Server.Items.BaseClothing", + "properties": [ + { + "name": "Resource", + "type": "Server.Items.CraftResource", + "usesSaveFlag": true, + "rule": "EnumMigrationRule" + }, + { + "name": "Attributes", + "type": "Server.AosAttributes", + "usesSaveFlag": true, + "rule": "RawSerializableMigrationRule", + "ruleArguments": [ + "DeserializationRequiresParent" + ] + }, + { + "name": "ClothingAttributes", + "type": "Server.AosArmorAttributes", + "usesSaveFlag": true, + "rule": "RawSerializableMigrationRule", + "ruleArguments": [ + "DeserializationRequiresParent" + ] + }, + { + "name": "SkillBonuses", + "type": "Server.AosSkillBonuses", + "usesSaveFlag": true, + "rule": "RawSerializableMigrationRule", + "ruleArguments": [ + "DeserializationRequiresParent" + ] + }, + { + "name": "Resistances", + "type": "Server.AosElementAttributes", + "usesSaveFlag": true, + "rule": "RawSerializableMigrationRule", + "ruleArguments": [ + "DeserializationRequiresParent" + ] + }, + { + "name": "MaxHitPoints", + "type": "int", + "usesSaveFlag": true, + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "EncodedInt" + ] + }, + { + "name": "HitPoints", + "type": "int", + "usesSaveFlag": true, + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "EncodedInt" + ] + }, + { + "name": "Crafter", + "type": "string", + "usesSaveFlag": true, + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "Quality", + "type": "Server.Items.ClothingQuality", + "usesSaveFlag": true, + "rule": "EnumMigrationRule" + }, + { + "name": "StrRequirement", + "type": "int", + "usesSaveFlag": true, + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.BaseLight.v2.json b/Projects/UOContent/Migrations/Server.Items.BaseLight.v2.json new file mode 100644 index 000000000..c04a03f3b --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.BaseLight.v2.json @@ -0,0 +1,43 @@ +{ + "version": 2, + "type": "Server.Items.BaseLight", + "properties": [ + { + "name": "BurntOut", + "type": "bool", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "Burning", + "type": "bool", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "Duration", + "type": "System.TimeSpan", + "rule": "PrimitiveTypeMigrationRule" + }, + { + "name": "Protected", + "type": "bool", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "BurnTimer", + "type": "Server.Timer", + "rule": "TimerMigrationRule", + "ruleArguments": [ + "@AnchoredTimer" + ] + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.BaseWeapon.v11.json b/Projects/UOContent/Migrations/Server.Items.BaseWeapon.v11.json new file mode 100644 index 000000000..009666433 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.BaseWeapon.v11.json @@ -0,0 +1,246 @@ +{ + "version": 11, + "type": "Server.Items.BaseWeapon", + "properties": [ + { + "name": "DamageLevel", + "type": "Server.Items.WeaponDamageLevel", + "usesSaveFlag": true, + "rule": "EnumMigrationRule" + }, + { + "name": "AccuracyLevel", + "type": "Server.Items.WeaponAccuracyLevel", + "usesSaveFlag": true, + "rule": "EnumMigrationRule" + }, + { + "name": "DurabilityLevel", + "type": "Server.Items.WeaponDurabilityLevel", + "usesSaveFlag": true, + "rule": "EnumMigrationRule" + }, + { + "name": "Quality", + "type": "Server.Items.WeaponQuality", + "usesSaveFlag": true, + "rule": "EnumMigrationRule" + }, + { + "name": "HitPoints", + "type": "int", + "usesSaveFlag": true, + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "MaxHitPoints", + "type": "int", + "usesSaveFlag": true, + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "Slayer", + "type": "Server.Items.SlayerName", + "usesSaveFlag": true, + "rule": "EnumMigrationRule" + }, + { + "name": "Poison", + "type": "Server.Poison", + "usesSaveFlag": true, + "rule": "PrimitiveUOTypeMigrationRule", + "ruleArguments": [ + "Poison" + ] + }, + { + "name": "PoisonCharges", + "type": "int", + "usesSaveFlag": true, + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "Crafter", + "type": "string", + "usesSaveFlag": true, + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "Identified", + "type": "bool", + "usesSaveFlag": true, + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "StrRequirement", + "type": "int", + "usesSaveFlag": true, + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "DexRequirement", + "type": "int", + "usesSaveFlag": true, + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "IntRequirement", + "type": "int", + "usesSaveFlag": true, + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "MinDamage", + "type": "int", + "usesSaveFlag": true, + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "MaxDamage", + "type": "int", + "usesSaveFlag": true, + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "HitSound", + "type": "int", + "usesSaveFlag": true, + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "MissSound", + "type": "int", + "usesSaveFlag": true, + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "Speed", + "type": "float", + "usesSaveFlag": true, + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "MaxRange", + "type": "int", + "usesSaveFlag": true, + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "Skill", + "type": "Server.SkillName", + "usesSaveFlag": true, + "rule": "EnumMigrationRule" + }, + { + "name": "Type", + "type": "Server.Items.WeaponType", + "usesSaveFlag": true, + "rule": "EnumMigrationRule" + }, + { + "name": "Animation", + "type": "Server.Items.WeaponAnimation", + "usesSaveFlag": true, + "rule": "EnumMigrationRule" + }, + { + "name": "Resource", + "type": "Server.Items.CraftResource", + "usesSaveFlag": true, + "rule": "EnumMigrationRule" + }, + { + "name": "Attributes", + "type": "Server.AosAttributes", + "usesSaveFlag": true, + "rule": "RawSerializableMigrationRule", + "ruleArguments": [ + "DeserializationRequiresParent" + ] + }, + { + "name": "WeaponAttributes", + "type": "Server.AosWeaponAttributes", + "usesSaveFlag": true, + "rule": "RawSerializableMigrationRule", + "ruleArguments": [ + "DeserializationRequiresParent" + ] + }, + { + "name": "SkillBonuses", + "type": "Server.AosSkillBonuses", + "usesSaveFlag": true, + "rule": "RawSerializableMigrationRule", + "ruleArguments": [ + "DeserializationRequiresParent" + ] + }, + { + "name": "Slayer2", + "type": "Server.Items.SlayerName", + "usesSaveFlag": true, + "rule": "EnumMigrationRule" + }, + { + "name": "AosElementDamages", + "type": "Server.AosElementAttributes", + "usesSaveFlag": true, + "rule": "RawSerializableMigrationRule", + "ruleArguments": [ + "DeserializationRequiresParent" + ] + }, + { + "name": "EngravedText", + "type": "string", + "usesSaveFlag": true, + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Corpse.v18.json b/Projects/UOContent/Migrations/Server.Items.Corpse.v18.json new file mode 100644 index 000000000..452e3ba7c --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Corpse.v18.json @@ -0,0 +1,137 @@ +{ + "version": 18, + "type": "Server.Items.Corpse", + "properties": [ + { + "name": "RestoreEquip", + "type": "System.Collections.Generic.List\u003CServer.Item\u003E", + "rule": "ListMigrationRule", + "ruleArguments": [ + "Server.Item", + "SerializableInterfaceMigrationRule" + ] + }, + { + "name": "Flags", + "type": "Server.Items.CorpseFlag", + "rule": "EnumMigrationRule" + }, + { + "name": "TimeOfDeath", + "type": "System.DateTime", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "DeltaTime" + ] + }, + { + "name": "RestoreTable", + "type": "System.Collections.Generic.Dictionary\u003CServer.Item, Server.Point3D\u003E", + "rule": "DictionaryMigrationRule", + "ruleArguments": [ + "Server.Item", + "SerializableInterfaceMigrationRule", + "0", + "Server.Point3D", + "PrimitiveUOTypeMigrationRule", + "1", + "Point3D" + ] + }, + { + "name": "DecayTimer", + "type": "Server.Timer", + "rule": "TimerMigrationRule", + "ruleArguments": [ + "@AnchoredTimer" + ] + }, + { + "name": "Looters", + "type": "System.Collections.Generic.HashSet\u003CServer.Mobile\u003E", + "rule": "HashSetMigrationRule", + "ruleArguments": [ + "Server.Mobile", + "SerializableInterfaceMigrationRule" + ] + }, + { + "name": "Killer", + "type": "Server.Mobile", + "rule": "SerializableInterfaceMigrationRule" + }, + { + "name": "Aggressors", + "type": "System.Collections.Generic.List\u003CServer.Mobile\u003E", + "rule": "ListMigrationRule", + "ruleArguments": [ + "Server.Mobile", + "SerializableInterfaceMigrationRule" + ] + }, + { + "name": "Owner", + "type": "Server.Mobile", + "rule": "SerializableInterfaceMigrationRule" + }, + { + "name": "CorpseName", + "type": "string", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "AccessLevel", + "type": "Server.AccessLevel", + "rule": "EnumMigrationRule" + }, + { + "name": "Guild", + "type": "Server.Guilds.Guild", + "rule": "SerializableInterfaceMigrationRule" + }, + { + "name": "EquipItems", + "type": "System.Collections.Generic.List\u003CServer.Item\u003E", + "rule": "ListMigrationRule", + "ruleArguments": [ + "Server.Item", + "SerializableInterfaceMigrationRule" + ] + }, + { + "name": "HairItemId", + "type": "int", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "HairHue", + "type": "int", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "FacialHairItemId", + "type": "int", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "FacialHairHue", + "type": "int", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Corpse.v19.json b/Projects/UOContent/Migrations/Server.Items.Corpse.v19.json new file mode 100644 index 000000000..2cccb267a --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Corpse.v19.json @@ -0,0 +1,137 @@ +{ + "version": 19, + "type": "Server.Items.Corpse", + "properties": [ + { + "name": "RestoreEquip", + "type": "System.Collections.Generic.List\u003CServer.Item\u003E", + "rule": "ListMigrationRule", + "ruleArguments": [ + "Server.Item", + "SerializableInterfaceMigrationRule" + ] + }, + { + "name": "Flags", + "type": "Server.Items.CorpseFlag", + "rule": "EnumMigrationRule" + }, + { + "name": "TimeOfDeath", + "type": "System.DateTime", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "AnchoredTime" + ] + }, + { + "name": "RestoreTable", + "type": "System.Collections.Generic.Dictionary\u003CServer.Item, Server.Point3D\u003E", + "rule": "DictionaryMigrationRule", + "ruleArguments": [ + "Server.Item", + "SerializableInterfaceMigrationRule", + "0", + "Server.Point3D", + "PrimitiveUOTypeMigrationRule", + "1", + "Point3D" + ] + }, + { + "name": "DecayTimer", + "type": "Server.Timer", + "rule": "TimerMigrationRule", + "ruleArguments": [ + "@AnchoredTimer" + ] + }, + { + "name": "Looters", + "type": "System.Collections.Generic.HashSet\u003CServer.Mobile\u003E", + "rule": "HashSetMigrationRule", + "ruleArguments": [ + "Server.Mobile", + "SerializableInterfaceMigrationRule" + ] + }, + { + "name": "Killer", + "type": "Server.Mobile", + "rule": "SerializableInterfaceMigrationRule" + }, + { + "name": "Aggressors", + "type": "System.Collections.Generic.List\u003CServer.Mobile\u003E", + "rule": "ListMigrationRule", + "ruleArguments": [ + "Server.Mobile", + "SerializableInterfaceMigrationRule" + ] + }, + { + "name": "Owner", + "type": "Server.Mobile", + "rule": "SerializableInterfaceMigrationRule" + }, + { + "name": "CorpseName", + "type": "string", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "AccessLevel", + "type": "Server.AccessLevel", + "rule": "EnumMigrationRule" + }, + { + "name": "Guild", + "type": "Server.Guilds.Guild", + "rule": "SerializableInterfaceMigrationRule" + }, + { + "name": "EquipItems", + "type": "System.Collections.Generic.List\u003CServer.Item\u003E", + "rule": "ListMigrationRule", + "ruleArguments": [ + "Server.Item", + "SerializableInterfaceMigrationRule" + ] + }, + { + "name": "HairItemId", + "type": "int", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "HairHue", + "type": "int", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "FacialHairItemId", + "type": "int", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "FacialHairHue", + "type": "int", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.DeathRobe.v4.json b/Projects/UOContent/Migrations/Server.Items.DeathRobe.v4.json new file mode 100644 index 000000000..ef81d79cd --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.DeathRobe.v4.json @@ -0,0 +1,14 @@ +{ + "version": 4, + "type": "Server.Items.DeathRobe", + "properties": [ + { + "name": "DecayTimer", + "type": "Server.Timer", + "rule": "TimerMigrationRule", + "ruleArguments": [ + "@AnchoredTimer" + ] + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.DecayedCorpse.v3.json b/Projects/UOContent/Migrations/Server.Items.DecayedCorpse.v3.json new file mode 100644 index 000000000..adcf02ff6 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.DecayedCorpse.v3.json @@ -0,0 +1,14 @@ +{ + "version": 3, + "type": "Server.Items.DecayedCorpse", + "properties": [ + { + "name": "DecayTimer", + "type": "Server.Timer", + "rule": "TimerMigrationRule", + "ruleArguments": [ + "@AnchoredTimer" + ] + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.FillableContainer.v3.json b/Projects/UOContent/Migrations/Server.Items.FillableContainer.v3.json new file mode 100644 index 000000000..2384676a3 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.FillableContainer.v3.json @@ -0,0 +1,19 @@ +{ + "version": 3, + "type": "Server.Items.FillableContainer", + "properties": [ + { + "name": "ContentType", + "type": "Server.Items.FillableContentType", + "rule": "EnumMigrationRule" + }, + { + "name": "RespawnTimer", + "type": "Server.Timer", + "rule": "TimerMigrationRule", + "ruleArguments": [ + "@AnchoredTimer" + ] + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.MarkContainer.v1.json b/Projects/UOContent/Migrations/Server.Items.MarkContainer.v1.json new file mode 100644 index 000000000..267ca4709 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.MarkContainer.v1.json @@ -0,0 +1,46 @@ +{ + "version": 1, + "type": "Server.Items.MarkContainer", + "properties": [ + { + "name": "AutoLock", + "type": "bool", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "RelockTimer", + "type": "Server.Items.MarkContainer.InternalTimer", + "rule": "TimerMigrationRule", + "ruleArguments": [ + "@AnchoredTimer" + ] + }, + { + "name": "TargetMap", + "type": "Server.Map", + "rule": "PrimitiveUOTypeMigrationRule", + "ruleArguments": [ + "Map" + ] + }, + { + "name": "Target", + "type": "Server.Point3D", + "rule": "PrimitiveUOTypeMigrationRule", + "ruleArguments": [ + "Point3D" + ] + }, + { + "name": "Description", + "type": "string", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.PuzzleChestSolutionAndTime.v1.json b/Projects/UOContent/Migrations/Server.Items.PuzzleChestSolutionAndTime.v1.json new file mode 100644 index 000000000..8424276f6 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.PuzzleChestSolutionAndTime.v1.json @@ -0,0 +1,14 @@ +{ + "version": 1, + "type": "Server.Items.PuzzleChestSolutionAndTime", + "properties": [ + { + "name": "When", + "type": "System.DateTime", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "AnchoredTime" + ] + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.StarRoomGate.v2.json b/Projects/UOContent/Migrations/Server.Items.StarRoomGate.v2.json new file mode 100644 index 000000000..0a61f7bfe --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.StarRoomGate.v2.json @@ -0,0 +1,22 @@ +{ + "version": 2, + "type": "Server.Items.StarRoomGate", + "properties": [ + { + "name": "Decays", + "type": "bool", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "DecayTime", + "type": "System.DateTime", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "AnchoredTime" + ] + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.TransientItem.v2.json b/Projects/UOContent/Migrations/Server.Items.TransientItem.v2.json new file mode 100644 index 000000000..3745c4428 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.TransientItem.v2.json @@ -0,0 +1,14 @@ +{ + "version": 2, + "type": "Server.Items.TransientItem", + "properties": [ + { + "name": "Expiration", + "type": "System.DateTime", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "AnchoredTime" + ] + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.TreasureMapChest.v3.json b/Projects/UOContent/Migrations/Server.Items.TreasureMapChest.v3.json new file mode 100644 index 000000000..805bbb6d1 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.TreasureMapChest.v3.json @@ -0,0 +1,57 @@ +{ + "version": 3, + "type": "Server.Items.TreasureMapChest", + "properties": [ + { + "name": "Guardians", + "type": "System.Collections.Generic.List\u003CServer.Mobile\u003E", + "rule": "ListMigrationRule", + "ruleArguments": [ + "@Tidy", + "@CanBeNull", + "Server.Mobile", + "SerializableInterfaceMigrationRule" + ] + }, + { + "name": "Temporary", + "type": "bool", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "Owner", + "type": "Server.Mobile", + "rule": "SerializableInterfaceMigrationRule" + }, + { + "name": "Level", + "type": "int", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "ExpireTimer", + "type": "Server.Timer", + "rule": "TimerMigrationRule", + "ruleArguments": [ + "@TimerDrift" + ] + }, + { + "name": "Lifted", + "type": "System.Collections.Generic.HashSet\u003CServer.Item\u003E", + "rule": "HashSetMigrationRule", + "ruleArguments": [ + "@Tidy", + "@CanBeNull", + "Server.Item", + "SerializableInterfaceMigrationRule" + ] + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.TreasureMapChest.v4.json b/Projects/UOContent/Migrations/Server.Items.TreasureMapChest.v4.json new file mode 100644 index 000000000..b4d1f85d4 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.TreasureMapChest.v4.json @@ -0,0 +1,57 @@ +{ + "version": 4, + "type": "Server.Items.TreasureMapChest", + "properties": [ + { + "name": "Guardians", + "type": "System.Collections.Generic.List\u003CServer.Mobile\u003E", + "rule": "ListMigrationRule", + "ruleArguments": [ + "@Tidy", + "@CanBeNull", + "Server.Mobile", + "SerializableInterfaceMigrationRule" + ] + }, + { + "name": "Temporary", + "type": "bool", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "Owner", + "type": "Server.Mobile", + "rule": "SerializableInterfaceMigrationRule" + }, + { + "name": "Level", + "type": "int", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "ExpireTimer", + "type": "Server.Timer", + "rule": "TimerMigrationRule", + "ruleArguments": [ + "@AnchoredTimer" + ] + }, + { + "name": "Lifted", + "type": "System.Collections.Generic.HashSet\u003CServer.Item\u003E", + "rule": "HashSetMigrationRule", + "ruleArguments": [ + "@Tidy", + "@CanBeNull", + "Server.Item", + "SerializableInterfaceMigrationRule" + ] + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Mobiles.BaseEscortable.v3.json b/Projects/UOContent/Migrations/Server.Mobiles.BaseEscortable.v3.json new file mode 100644 index 000000000..5149f4347 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Mobiles.BaseEscortable.v3.json @@ -0,0 +1,43 @@ +{ + "version": 3, + "type": "Server.Mobiles.BaseEscortable", + "properties": [ + { + "name": "DestinationString", + "type": "string", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "DeleteTimer", + "type": "Server.Timer", + "rule": "TimerMigrationRule", + "ruleArguments": [ + "@AnchoredTimer" + ] + }, + { + "name": "MlQuestType", + "type": "System.Type", + "rule": "PrimitiveTypeMigrationRule" + }, + { + "name": "MlQuestDestinationMessage", + "type": "Server.TextDefinition", + "rule": "PrimitiveUOTypeMigrationRule", + "ruleArguments": [ + "TextDefinition" + ] + }, + { + "name": "MlQuestPaymentMessage", + "type": "Server.TextDefinition", + "rule": "PrimitiveUOTypeMigrationRule", + "ruleArguments": [ + "TextDefinition" + ] + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Mobiles.PlayerVendor.v4.json b/Projects/UOContent/Migrations/Server.Mobiles.PlayerVendor.v4.json new file mode 100644 index 000000000..6b9c9eb1c --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Mobiles.PlayerVendor.v4.json @@ -0,0 +1,62 @@ +{ + "version": 4, + "type": "Server.Mobiles.PlayerVendor", + "properties": [ + { + "name": "ShopName", + "type": "string", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "NextPayTime", + "type": "System.DateTime", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "AnchoredTime" + ] + }, + { + "name": "House", + "type": "Server.Multis.BaseHouse", + "rule": "SerializableInterfaceMigrationRule" + }, + { + "name": "Owner", + "type": "Server.Mobile", + "rule": "SerializableInterfaceMigrationRule" + }, + { + "name": "BankAccount", + "type": "int", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "HoldGold", + "type": "int", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "SellItems", + "type": "System.Collections.Generic.Dictionary\u003CServer.Item, Server.Mobiles.VendorItem\u003E", + "rule": "DictionaryMigrationRule", + "ruleArguments": [ + "Server.Item", + "SerializableInterfaceMigrationRule", + "0", + "Server.Mobiles.VendorItem", + "RawSerializableMigrationRule", + "1", + "" + ] + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Mobiles.RentedVendor.v1.json b/Projects/UOContent/Migrations/Server.Mobiles.RentedVendor.v1.json new file mode 100644 index 000000000..b42fa33eb --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Mobiles.RentedVendor.v1.json @@ -0,0 +1,62 @@ +{ + "version": 1, + "type": "Server.Mobiles.RentedVendor", + "properties": [ + { + "name": "RentalDurationId", + "type": "int", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "RentalPrice", + "type": "int", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "LandlordRenew", + "type": "bool", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "RenterRenew", + "type": "bool", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "RenewalPrice", + "type": "int", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "RentalGold", + "type": "int", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "RentalExpireTime", + "type": "System.DateTime", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "AnchoredTime" + ] + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Mobiles.Sheep.v1.json b/Projects/UOContent/Migrations/Server.Mobiles.Sheep.v1.json new file mode 100644 index 000000000..bd224f77f --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Mobiles.Sheep.v1.json @@ -0,0 +1,14 @@ +{ + "version": 1, + "type": "Server.Mobiles.Sheep", + "properties": [ + { + "name": "NextWoolTime", + "type": "System.DateTime", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "AnchoredTime" + ] + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Multis.BaseBoat.v5.json b/Projects/UOContent/Migrations/Server.Multis.BaseBoat.v5.json new file mode 100644 index 000000000..140d3dd64 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Multis.BaseBoat.v5.json @@ -0,0 +1,73 @@ +{ + "version": 5, + "type": "Server.Multis.BaseBoat", + "properties": [ + { + "name": "MapItem", + "type": "Server.Items.MapItem", + "rule": "SerializableInterfaceMigrationRule" + }, + { + "name": "NextNavPoint", + "type": "int", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "Facing", + "type": "Server.Direction", + "rule": "EnumMigrationRule" + }, + { + "name": "TimeOfDecay", + "type": "System.DateTime", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "AnchoredTime" + ] + }, + { + "name": "Owner", + "type": "Server.Mobile", + "rule": "SerializableInterfaceMigrationRule" + }, + { + "name": "PPlank", + "type": "Server.Items.Plank", + "rule": "SerializableInterfaceMigrationRule" + }, + { + "name": "SPlank", + "type": "Server.Items.Plank", + "rule": "SerializableInterfaceMigrationRule" + }, + { + "name": "TillerMan", + "type": "Server.Items.TillerMan", + "rule": "SerializableInterfaceMigrationRule" + }, + { + "name": "Hold", + "type": "Server.Items.Hold", + "rule": "SerializableInterfaceMigrationRule" + }, + { + "name": "Anchored", + "type": "bool", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "ShipName", + "type": "string", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Multis.BaseCamp.v2.json b/Projects/UOContent/Migrations/Server.Multis.BaseCamp.v2.json new file mode 100644 index 000000000..471c645f1 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Multis.BaseCamp.v2.json @@ -0,0 +1,34 @@ +{ + "version": 2, + "type": "Server.Multis.BaseCamp", + "properties": [ + { + "name": "Items", + "type": "System.Collections.Generic.List\u003CServer.Item\u003E", + "rule": "ListMigrationRule", + "ruleArguments": [ + "@Tidy", + "Server.Item", + "SerializableInterfaceMigrationRule" + ] + }, + { + "name": "Mobiles", + "type": "System.Collections.Generic.List\u003CServer.Mobile\u003E", + "rule": "ListMigrationRule", + "ruleArguments": [ + "@Tidy", + "Server.Mobile", + "SerializableInterfaceMigrationRule" + ] + }, + { + "name": "DecayTime", + "type": "System.DateTime", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "AnchoredTime" + ] + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Spells.Fifth.PoisonField.v1.json b/Projects/UOContent/Migrations/Server.Spells.Fifth.PoisonField.v1.json new file mode 100644 index 000000000..1f16a4d7f --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Spells.Fifth.PoisonField.v1.json @@ -0,0 +1,19 @@ +{ + "version": 1, + "type": "Server.Spells.Fifth.PoisonField", + "properties": [ + { + "name": "Caster", + "type": "Server.Mobile", + "rule": "SerializableInterfaceMigrationRule" + }, + { + "name": "End", + "type": "System.DateTime", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "AnchoredTime" + ] + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Spells.Fourth.FireFieldItem.v1.json b/Projects/UOContent/Migrations/Server.Spells.Fourth.FireFieldItem.v1.json new file mode 100644 index 000000000..3e91c7a4d --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Spells.Fourth.FireFieldItem.v1.json @@ -0,0 +1,27 @@ +{ + "version": 1, + "type": "Server.Spells.Fourth.FireFieldItem", + "properties": [ + { + "name": "Damage", + "type": "int", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "Caster", + "type": "Server.Mobile", + "rule": "SerializableInterfaceMigrationRule" + }, + { + "name": "End", + "type": "System.DateTime", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "AnchoredTime" + ] + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Spells.Seventh.EnergyField.v2.json b/Projects/UOContent/Migrations/Server.Spells.Seventh.EnergyField.v2.json new file mode 100644 index 000000000..dd202da03 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Spells.Seventh.EnergyField.v2.json @@ -0,0 +1,19 @@ +{ + "version": 2, + "type": "Server.Spells.Seventh.EnergyField", + "properties": [ + { + "name": "Caster", + "type": "Server.Mobile", + "rule": "SerializableInterfaceMigrationRule" + }, + { + "name": "End", + "type": "System.DateTime", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "AnchoredTime" + ] + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Spells.Sixth.ParalyzeField.v1.json b/Projects/UOContent/Migrations/Server.Spells.Sixth.ParalyzeField.v1.json new file mode 100644 index 000000000..3e834a6b1 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Spells.Sixth.ParalyzeField.v1.json @@ -0,0 +1,19 @@ +{ + "version": 1, + "type": "Server.Spells.Sixth.ParalyzeField", + "properties": [ + { + "name": "Caster", + "type": "Server.Mobile", + "rule": "SerializableInterfaceMigrationRule" + }, + { + "name": "End", + "type": "System.DateTime", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "AnchoredTime" + ] + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Spells.Third.WallOfStone.v1.json b/Projects/UOContent/Migrations/Server.Spells.Third.WallOfStone.v1.json new file mode 100644 index 000000000..eafcc5945 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Spells.Third.WallOfStone.v1.json @@ -0,0 +1,19 @@ +{ + "version": 1, + "type": "Server.Spells.Third.WallOfStone", + "properties": [ + { + "name": "Caster", + "type": "Server.Mobile", + "rule": "SerializableInterfaceMigrationRule" + }, + { + "name": "End", + "type": "System.DateTime", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "AnchoredTime" + ] + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Misc/ShardPoller.cs b/Projects/UOContent/Misc/ShardPoller.cs index 762a0be9c..8516e6678 100644 --- a/Projects/UOContent/Misc/ShardPoller.cs +++ b/Projects/UOContent/Misc/ShardPoller.cs @@ -36,16 +36,14 @@ public partial class ShardPoller : Item Movable = false; } - [SerializableProperty(0)] - [CommandProperty(AccessLevel.GameMaster, AccessLevel.Administrator)] - public string Title + [SerializableField(0, allowFieldChange: nameof(AllowTitleChange))] + [SerializedCommandProperty(AccessLevel.GameMaster, AccessLevel.Administrator)] + private string _title; + + private bool AllowTitleChange(ref string value) { - get => _title; - set - { - _title = ShardPollPrompt.UrlToHref(value); - this.MarkDirty(); - } + value = ShardPollPrompt.UrlToHref(value); + return true; } [CommandProperty(AccessLevel.GameMaster, AccessLevel.Administrator)] @@ -54,31 +52,20 @@ public partial class ShardPoller : Item ? TimeSpan.Zero : Utility.Max(StartTime + Duration - Core.Now, TimeSpan.Zero); - [SerializableProperty(3)] - [CommandProperty(AccessLevel.GameMaster, AccessLevel.Administrator)] - public bool Active + [SerializableField(3, fieldChanged: nameof(OnActiveChanged))] + [SerializedCommandProperty(AccessLevel.GameMaster, AccessLevel.Administrator)] + private bool _active; + + private void OnActiveChanged(bool oldValue, bool newValue) { - get => _active; - set + if (_active) { - if (_active == value) - { - return; - } - - _active = value; - - if (_active) - { - StartTime = Core.Now; - _activePollers.Add(this); - } - else - { - _activePollers.Remove(this); - } - - this.MarkDirty(); + StartTime = Core.Now; + _activePollers.Add(this); + } + else + { + _activePollers.Remove(this); } } @@ -250,15 +237,12 @@ public partial class ShardPollOption } } - [SerializableProperty(0)] - public string Title + [SerializableField(0, fieldChanged: nameof(OnTitleChanged))] + private string _title; + + private void OnTitleChanged(string oldValue, string newValue) { - get => _title; - set - { - _title = value; - _lineBreaks = -1; - } + _lineBreaks = -1; } public int Votes => Voters.Length; diff --git a/Projects/UOContent/Misc/StaminaSystem.cs b/Projects/UOContent/Misc/StaminaSystem.cs index ed8074cba..fcd5d46b0 100644 --- a/Projects/UOContent/Misc/StaminaSystem.cs +++ b/Projects/UOContent/Misc/StaminaSystem.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using ModernUO.CodeGeneratedEvents; -using Server.Collections; using Server.Logging; using Server.Mobiles; using Server.Spells.Ninjitsu; @@ -60,22 +59,16 @@ public static class StaminaSystem EventSink.Logout += Logout; // Credit idle time - using var queue = PooledRefQueue.Create(); foreach (var m in _stepsTaken.Keys) { - // We cannot remove since we are iterating. + // Keeps the ref valid for the check below. ref var stepsTaken = ref RegenSteps(m, out var exists, removeOnInvalidation: false); if (exists && stepsTaken.Steps <= 0) { - queue.Enqueue(m); + _stepsTaken.Remove(m); } } - - while (queue.Count > 0) - { - _stepsTaken.Remove(queue.Dequeue()); - } } [OnEvent(nameof(PlayerMobile.PlayerDeletedEvent))] @@ -325,7 +318,7 @@ public static class StaminaSystem { var from = e.Mobile; var running = (e.Direction & Direction.Running) != 0; - + if (CannotWalkWhenFatigued && from.Stam <= 0) { from.SendLocalizedMessage(500110); // You are too fatigued to move. @@ -481,27 +474,13 @@ public static class StaminaSystem if (_resetHash.Count > 0) { - using var queue = PooledRefQueue.Create(); - ref var stepsTaken = ref Unsafe.NullRef(); foreach (var m in _resetHash) { stepsTaken = ref GetStepsTaken(m, out var exists); if (!exists || Core.Now >= stepsTaken.IdleStartTime + ResetDuration) { - queue.Enqueue(m); - } - } - - if (_resetHash.Count == queue.Count) - { - _resetHash.Clear(); - } - else - { - while (queue.Count > 0) - { - _resetHash.Remove(queue.Dequeue()); + _resetHash.Remove(m); } } } diff --git a/Projects/UOContent/Mobiles/AI/ArcherAI.cs b/Projects/UOContent/Mobiles/AI/ArcherAI.cs index dd461a03f..dedd914c9 100644 --- a/Projects/UOContent/Mobiles/AI/ArcherAI.cs +++ b/Projects/UOContent/Mobiles/AI/ArcherAI.cs @@ -47,7 +47,7 @@ public class ArcherAI : BaseAI { this.DebugSayFormatted($"I am still not in range of {combatant.Name}"); - if ((int)Mobile.GetDistanceToSqrt(combatant) > Mobile.RangePerception + 1) + if (!Mobile.InRange(combatant, Mobile.ChaseLeashRange)) { this.DebugSayFormatted($"I have lost {combatant.Name}"); diff --git a/Projects/UOContent/Mobiles/AI/BaseAI/AIGroupMovement.cs b/Projects/UOContent/Mobiles/AI/BaseAI/AIGroupMovement.cs index af6c00038..548fe0d54 100644 --- a/Projects/UOContent/Mobiles/AI/BaseAI/AIGroupMovement.cs +++ b/Projects/UOContent/Mobiles/AI/BaseAI/AIGroupMovement.cs @@ -27,26 +27,41 @@ public abstract partial class BaseAI private static void CleanupReservedPositions() { - using var toRemove = PooledRefQueue.Create(); - foreach (var (m, p) in _reservedPositions) { if (m?.Deleted != false || m.GetDistanceToSqrt(p) < 1) { - toRemove.Enqueue(m); + _reservedPositions.Remove(m); } } - - while (toRemove.Count > 0) - { - _reservedPositions.Remove(toRemove.Dequeue()); - } } - private bool UseGroupMovement(Mobile target) => + /// + /// Crowding refinement for the final approach: engages only near the target when allies + /// contest the ring, so creatures spread instead of stacking. Chasing any real distance + /// always uses the pathfinding approach primitive. + /// + private bool UseGroupMovement(Mobile target, int range) => Mobile.Combatant == target && !Mobile.Controlled - && CountNearbyAllies(target) > 0; + && Mobile.InRange(target, range + 2) + && CountCrowdingAllies(target, range) > 0; + + private int CountCrowdingAllies(Mobile target, int range) + { + var crowding = 0; + + foreach (var m in target.GetMobilesInRange(range + 1)) + { + if (m != Mobile && m.Combatant == target && m is BaseCreature { Controlled: false } bc + && bc.Team == Mobile.Team) + { + crowding++; + } + } + + return crowding; + } public static bool MoveToWithGroup(BaseAI ai, Mobile target, bool run, int range) { @@ -63,6 +78,7 @@ public abstract partial class BaseAI { if (optimalPosition == Point3D.Zero) { + return ai.MoveToWithCollisionAvoidance(target, run, range); } @@ -75,7 +91,15 @@ public abstract partial class BaseAI direction = GetAdjustedDirection(direction); } - return ai.DoMove(direction, true); + var res = ai.DoMoveImpl(direction, true); + + if (res is MoveResult.Success or MoveResult.BadState) + { + return true; + } + + // A blocked or wall-slid step is not progress — route around the obstacle. + return ai.ApproachTarget(target, run, range); } finally { @@ -83,21 +107,6 @@ public abstract partial class BaseAI } } - private int CountNearbyAllies(Mobile target) - { - var allies = 0; - foreach (var m in Mobile.GetMobilesInRange(8)) - { - if (m != Mobile && m.Combatant == target && m is BaseCreature { Controlled: false } bc - && bc.Team == Mobile.Team) - { - allies++; - } - } - - return allies; - } - private PooledRefList GetNearbyAllies(Mobile target) { var allies = PooledRefList.Create(); diff --git a/Projects/UOContent/Mobiles/AI/BaseAI/AIMovement.cs b/Projects/UOContent/Mobiles/AI/BaseAI/AIMovement.cs index 1063dd70a..ccd1c72b6 100644 --- a/Projects/UOContent/Mobiles/AI/BaseAI/AIMovement.cs +++ b/Projects/UOContent/Mobiles/AI/BaseAI/AIMovement.cs @@ -13,6 +13,7 @@ * along with this program. If not, see . * ************************************************************************/ +using System; using System.Runtime.CompilerServices; using Server.Collections; using Server.Items; @@ -37,7 +38,18 @@ public abstract partial class BaseAI private bool _approachGaveUp; private Point3D _approachGaveUpGoalLoc; - public static double BadlyHurtMoveDelay(BaseCreature bc) + // --- Move intent (see ContinueMove) ------------------------------------------------ + // Durable movement goal renewed by en-route ApproachTarget/MoveToPoint calls; while + // live, the AITimer wakes at NextMove between think ticks to advance the step. + private Mobile _moveIntentTarget; + private IPoint3D _moveIntentPoint; + private bool _moveIntentRun; + private int _moveIntentRange; + private long _moveIntentExpire; + + // Inflates a step delay while badly hurt; computed from the passed base so it cannot + // compound across steps. Damage slows steps, never decisions. + public static double BadlyHurtMoveDelay(BaseCreature bc, double delay) { var statMin = Core.HS ? bc.Stam : bc.Hits; var statMax = Core.HS ? bc.StamMax : bc.HitsMax; @@ -45,20 +57,40 @@ public abstract partial class BaseAI if (!bc.IsDeadPet && (bc.ReduceSpeedWithDamage || bc.IsSubdued) && statMax > 0 && statMin < statMax * 0.3) { - var hits = (double)statMin / statMax; + var stat = (double)statMin / statMax; - if (hits < 0.1) { return bc.CurrentSpeed + 0.15; } - if (hits < 0.2) { return bc.CurrentSpeed + 0.1; } - if (hits < 0.3) { return bc.CurrentSpeed + 0.05; } + if (stat < 0.1) { return delay + 0.15; } + if (stat < 0.2) { return delay + 0.1; } + + return delay + 0.05; } - return bc.CurrentSpeed; + return delay; } public bool CanMoveNow(out double delay) { delay = 0.0; - return Core.TickCount >= NextMove; + return Core.TickCount - NextMove >= 0; + } + + // Accumulative full-step budget: long-run pacing averages CurrentMoveSpeed exactly + // regardless of timer-grid jitter; snap-to-now caps stall catch-up at one step. + private void ConsumeMoveBudget() + { + var stepDelay = Mobile.CurrentMoveSpeed; + + if (!(Core.AOS && IsFollowingMaster())) + { + stepDelay = BadlyHurtMoveDelay(Mobile, stepDelay); + } + + NextMove += Math.Max(50, (long)(stepDelay * 1000)); + + if (Core.TickCount - NextMove > 0) + { + NextMove = Core.TickCount; + } } public virtual bool CheckMove() => !(Mobile.Deleted || Mobile.DisallowAllMoves); @@ -86,23 +118,22 @@ public abstract partial class BaseAI if (TryMove(d)) { + // Writes the think clock only; hurt slowdown applies in ConsumeMoveBudget. if (Core.AOS && IsFollowingMaster()) { Mobile.CurrentSpeed = 0.1; } - else if (Mobile.Hits < Mobile.HitsMax * 0.3) - { - Mobile.CurrentSpeed = BadlyHurtMoveDelay(Mobile); - } else if (Mobile.Warmode || Mobile.Combatant != null) { - Mobile.CurrentSpeed = Mobile.ActiveSpeed; + Mobile.SetCurrentSpeedToActive(); } else { - Mobile.CurrentSpeed = Mobile.PassiveSpeed; + Mobile.SetCurrentSpeedToPassive(); } + ConsumeMoveBudget(); + return MoveResult.Success; } @@ -151,6 +182,7 @@ public abstract partial class BaseAI if (Mobile.Move(Mobile.Direction)) { + ConsumeMoveBudget(); return MoveResult.SuccessAutoTurn; } } @@ -307,12 +339,14 @@ public abstract partial class BaseAI { if (Mobile.Deleted || Mobile.DisallowAllMoves || target?.Deleted != false) { + ClearMoveIntent(); return false; } if (Mobile.InRange(target, range)) { ResetApproach(); + ClearMoveIntent(); return true; } @@ -321,12 +355,15 @@ public abstract partial class BaseAI { if (target.Location == _approachGaveUpGoalLoc) { + ClearMoveIntent(); return false; } ResetApproach(); // target moved — try again fresh } + RenewMoveIntent(target, null, run, range); + // FAST PATH: greedy step toward the target, counted as success ONLY when the move // fully succeeded (not an auto-turn sidestep) and actually got us closer. An // auto-turn sidestep can reduce Euclidean distance while moving in the wrong @@ -341,31 +378,83 @@ public abstract partial class BaseAI if (res == MoveResult.BadState) { - return false; // not allowed to move this tick; not a stall + return true; // not allowed to move this tick (frozen/casting/throttled); not a failure } if (res == MoveResult.Success && Mobile.GetDistanceToSqrt(target) < distBefore) { + ResetApproach(); - return Mobile.InRange(target, range); + return true; // healthy en-route progress } + // else: fall through; let the PathFollower route around the obstacle. } // PLANNING PATH: a persistent PathFollower, never discarded by a greedy step. if (Path == null || Path.Goal != target) { + Path = new PathFollower(Mobile, target) { Mover = DoMoveImpl }; } + // Sample move-eligibility BEFORE the attempt: a successful step consumes the move + // budget, which would mask stall accounting and the progress signal. + var couldMove = CanMoveNow(out _) && !IsInBadState(); + var locBefore = Mobile.Location; + if (Path.Follow(run, range)) { ResetApproach(); return true; } - TrackApproachProgress(target); - return false; + TrackApproachProgress(target, couldMove); + + // En-route progress is success; failure only when a move-eligible tick took no step + // (no working path), or the approach has given up. + var progressed = !_approachGaveUp && (Mobile.Location != locBefore || !couldMove); + + return progressed; + } + + /// + /// Walks toward a fixed point (e.g. a target's last-known position), pathfinding around + /// obstacles. Returns false on arrival or when genuinely unable to make progress. + /// + public bool MoveToPoint(IPoint3D goal, bool run) + { + if (Mobile.Deleted || Mobile.DisallowAllMoves || goal == null) + { + ClearMoveIntent(); + return false; + } + + if (Path?.Goal != goal) + { + Path = new PathFollower(Mobile, goal) { Mover = DoMoveImpl }; + } + + RenewMoveIntent(null, goal, run, 1); + + var couldMove = CanMoveNow(out _) && !IsInBadState(); + var locBefore = Mobile.Location; + + if (Path.Follow(run, 1)) + { + Path = null; + ClearMoveIntent(); + return false; // arrived + } + + var progressed = Mobile.Location != locBefore || !couldMove; + + if (!progressed) + { + ClearMoveIntent(); + } + + return progressed; } /// @@ -376,11 +465,11 @@ public abstract partial class BaseAI /// gives up and idles. A MOVING goal (an active chase) resets the baseline every tick, /// so chases never give up even when the gap holds constant. /// - private void TrackApproachProgress(Mobile target) + private void TrackApproachProgress(Mobile target, bool couldMove) { - if (!CanMoveNow(out _)) + if (!couldMove) { - return; // a not-yet-due move (stun) is not a stall + return; // a tick that was never allowed to move (stun, stall) is not a stall } var dist = Mobile.GetDistanceToSqrt(target); @@ -411,6 +500,7 @@ public abstract partial class BaseAI _approachGaveUp = true; _approachGaveUpGoalLoc = goalLoc; Path = null; + ClearMoveIntent(); } } @@ -426,6 +516,56 @@ public abstract partial class BaseAI _approachGaveUp = false; } + private void RenewMoveIntent(Mobile target, IPoint3D point, bool run, int range) + { + _moveIntentTarget = target; + _moveIntentPoint = point; + _moveIntentRun = run; + _moveIntentRange = range; + + // A live pursuit renews every think tick; unrenewed intent dies on its own. + _moveIntentExpire = Core.TickCount + (long)(Mobile.CurrentSpeed * 2000) + 250; + } + + public void ClearMoveIntent() + { + _moveIntentTarget = null; + _moveIntentPoint = null; + } + + /// + /// True while a durable movement goal is live; is the tick + /// the movement budget elapses. + /// + public bool TryGetMoveWake(out long nextMove) + { + nextMove = NextMove; + + return (_moveIntentTarget != null || _moveIntentPoint != null) && + Core.TickCount - _moveIntentExpire < 0; + } + + /// + /// Advances the current pursuit/investigation by one step on a movement-clock wake; + /// no decisions run. + /// + public void ContinueMove() + { + if (!TryGetMoveWake(out var nextMove) || Core.TickCount - nextMove < 0) + { + return; + } + + if (_moveIntentTarget != null) + { + ApproachTarget(_moveIntentTarget, _moveIntentRun, _moveIntentRange); + } + else + { + MoveToPoint(_moveIntentPoint, _moveIntentRun); + } + } + public virtual bool MoveTo(Mobile m, bool run, int range) { if (Mobile.Deleted || Mobile.DisallowAllMoves || m?.Deleted != false) @@ -444,7 +584,7 @@ public abstract partial class BaseAI return true; } - if (UseGroupMovement(m)) + if (UseGroupMovement(m, range)) { return MoveToWithGroup(this, m, shouldRun, range); } @@ -467,7 +607,11 @@ public abstract partial class BaseAI var direction = Mobile.GetDirectionTo(target); - if (DoMove(direction, true)) + // Wall-slide auto-turns must not count as progress, or a creature pinned on + // geometry reports success forever. + var res = DoMoveImpl(direction, true); + + if (res is MoveResult.Success or MoveResult.BadState) { return true; } @@ -476,14 +620,14 @@ public abstract partial class BaseAI { var clockwise = (Direction)(((int)direction + i) % 8); - if (DoMove(clockwise, true)) + if (DoMoveImpl(clockwise, true) == MoveResult.Success) { return true; } var counterclockwise = (Direction)(((int)direction - i + 8) % 8); - if (DoMove(counterclockwise, true)) + if (DoMoveImpl(counterclockwise, true) == MoveResult.Success) { return true; } diff --git a/Projects/UOContent/Mobiles/AI/BaseAI/AITimer.cs b/Projects/UOContent/Mobiles/AI/BaseAI/AITimer.cs index 3c08d944b..fe24e5f6d 100644 --- a/Projects/UOContent/Mobiles/AI/BaseAI/AITimer.cs +++ b/Projects/UOContent/Mobiles/AI/BaseAI/AITimer.cs @@ -17,9 +17,15 @@ using System; namespace Server.Mobiles; +/// +/// Drives an AI on two clocks: decisions at , plus +/// move-only wakes at while a pursuit is live. Each tick +/// schedules the earlier of the two deadlines. +/// public sealed class AITimer : Timer { private readonly BaseAI _owner; + private long _nextThink; private int _detectHiddenMinDelay; private int _detectHiddenMaxDelay; @@ -28,14 +34,29 @@ public sealed class AITimer : Timer { _owner = owner; _owner._nextDetectHidden = Core.TickCount; + _nextThink = Core.TickCount; } public void Activate() { + _nextThink = Core.TickCount; Interval = TimeSpan.FromSeconds(_owner.Mobile.CurrentSpeed); Start(); } + // A speed-up must not wait out a stale, longer think deadline. + public void OnSpeedChanged() + { + var candidate = Core.TickCount + (long)(_owner.Mobile.CurrentSpeed * 1000); + + if (candidate - _nextThink < 0) + { + _nextThink = candidate; + } + + Interval = TimeSpan.FromSeconds(_owner.Mobile.CurrentSpeed); + } + protected override void OnTick() { if (ShouldStop()) @@ -44,23 +65,52 @@ public sealed class AITimer : Timer return; } - _owner.Mobile.OnThink(); - - if (ShouldStop()) + if (Core.TickCount - _nextThink >= 0) { - Stop(); - return; + _owner.Mobile.OnThink(); + + if (ShouldStop()) + { + Stop(); + return; + } + + HandleBardEffects(); + + if (_owner.Mobile.Controlled ? _owner.Obey() : _owner.Think()) + { + HandleDetectHidden(); + } + + // Cadence from the post-decision speed (decisions may flip active/passive). + _nextThink = Core.TickCount + (long)(_owner.Mobile.CurrentSpeed * 1000); + } + else + { + _owner.ContinueMove(); } - Interval = TimeSpan.FromSeconds(_owner.Mobile.CurrentSpeed); - HandleBardEffects(); + ScheduleNext(); + } - if (_owner.Mobile.Controlled ? !_owner.Obey() : !_owner.Think()) + private void ScheduleNext() + { + var now = Core.TickCount; + var delay = _nextThink - now; + + if (_owner.TryGetMoveWake(out var nextMove)) { - return; + var moveDelay = nextMove - now; + + // Only a future budget is a wake — a blocked creature must not spin the timer. + if (moveDelay > 0 && moveDelay < delay) + { + delay = moveDelay; + } } - HandleDetectHidden(); + // The wheel rounds up to its 8ms resolution; a non-positive delay becomes one turn. + Interval = TimeSpan.FromMilliseconds(delay); } private bool ShouldStop() diff --git a/Projects/UOContent/Mobiles/AI/BaseAI/BaseAI.cs b/Projects/UOContent/Mobiles/AI/BaseAI/BaseAI.cs index 57b6f6a25..cf64cae17 100644 --- a/Projects/UOContent/Mobiles/AI/BaseAI/BaseAI.cs +++ b/Projects/UOContent/Mobiles/AI/BaseAI/BaseAI.cs @@ -26,11 +26,26 @@ namespace Server.Mobiles; public abstract partial class BaseAI { + // Last-known-position tracking: recorded while the combatant is in LOS; drives the + // guard-time investigation and the instant re-engage. + private const int GuardGraceDuration = 10_000; + private const int LkpFreshDuration = 30_000; + private const int InvestigateDuration = 15_000; + private ActionType _action; public long _nextDetectHidden; public DateTime _lastOrder = DateTime.MinValue; public Mobile _commandIssuer; + private Mobile _lkpTarget; + private Point3D _lkpLocation; + private IPoint3D _lkpGoal; // boxed _lkpLocation handed to the PathFollower + private IPoint3D _herdGoal; // boxed herding goal handed to the PathFollower + private long _lkpExpireTick; + private long _guardStopTick; + private long _investigateStopTick; + private bool _investigating; + public PathFollower Path { get; protected set; } public AITimer AITimer { get; } public long NextMove { get; set; } @@ -44,6 +59,7 @@ public abstract partial class BaseAI public BaseAI(BaseCreature m) { Mobile = m; + NextMove = Core.TickCount; AITimer = new AITimer(this); if (!m.PlayerRangeSensitive || !World.Loading && m.Map != null && m.Map != Map.Internal && m.Map.GetSector(m.Location).Active) @@ -233,6 +249,15 @@ public abstract partial class BaseAI return true; } + if (_action == ActionType.Combat) + { + UpdateLastKnownLocation(); + } + else if (_action is ActionType.Wander or ActionType.Guard) + { + TryReengageLastKnown(); + } + switch (Action) { case ActionType.Wander: @@ -272,6 +297,9 @@ public abstract partial class BaseAI public virtual void OnActionChanged() { + // A change of course invalidates between-think movement continuation. + ClearMoveIntent(); + switch (Action) { case ActionType.Wander: @@ -323,6 +351,15 @@ public abstract partial class BaseAI { Mobile.Warmode = true; Mobile.Combatant = null; + + // Investigate a fresh last-seen position that is not already in view; the guard + // grace period begins once the investigation ends. + _investigating = _lkpTarget != null && Core.TickCount - _lkpExpireTick < 0 && + !(Mobile.InRange(_lkpLocation, 1) || + Mobile.InLOS(_lkpLocation) && Mobile.InRange(_lkpLocation, Mobile.RangePerception)); + _investigateStopTick = Core.TickCount + InvestigateDuration; + _guardStopTick = Core.TickCount + GuardGraceDuration; + _lkpGoal = null; } private void HandleFleeAction() @@ -453,18 +490,118 @@ public abstract partial class BaseAI public virtual bool DoActionGuard() { - if (Mobile.Combatant == null) + if (_investigating) { - DebugSay("No threats found. Going home..."); - Action = ActionType.Wander; + if (InvestigateLastKnown()) + { + return true; + } + + _investigating = false; + _guardStopTick = Core.TickCount + GuardGraceDuration; } - DebugSay("I stopped being on guard."); + if (Core.TickCount - _guardStopTick < 0) + { + DebugSay("I am on guard."); + + if (Utility.Random(8) == 0) + { + Mobile.Direction = (Direction)Utility.Random(8); + } + + return true; + } + + DebugSay("I stopped being on guard. Going home..."); Action = ActionType.Wander; return true; } + /// + /// Records the combatant's position while it is visible and in line of sight. + /// + private void UpdateLastKnownLocation() + { + var combatant = Mobile.Combatant; + + if (combatant?.Deleted == false && combatant.Map == Mobile.Map && + Mobile.CanSee(combatant) && Mobile.InLOS(combatant)) + { + _lkpTarget = combatant; + _lkpLocation = combatant.Location; + _lkpExpireTick = Core.TickCount + LkpFreshDuration; + } + } + + /// + /// Re-engages the last-seen target when it returns to view within perception range, + /// bypassing the reacquire throttle. + /// + private bool TryReengageLastKnown() + { + var target = _lkpTarget; + + if (target == null) + { + return false; + } + + if (target.Deleted || !target.Alive || target.Map != Mobile.Map || + target is BaseCreature { IsDeadPet: true } || Core.TickCount - _lkpExpireTick >= 0) + { + ClearLastKnown(); + return false; + } + + if (Mobile.Controlled || Mobile.BardPacified || Mobile.BardProvoked || Mobile.FightMode == FightMode.None) + { + return false; + } + + if (!Mobile.InRange(target, Mobile.RangePerception) || !Mobile.CanSee(target) || + !Mobile.InLOS(target) || !Mobile.CanBeHarmful(target, false)) + { + return false; + } + + DebugSay("There you are!"); + Mobile.Combatant = target; + Mobile.FocusMob = null; + Action = ActionType.Combat; + return true; + } + + /// + /// Walks toward the last-seen position until it is in view, reached, timed out, or + /// unreachable. Returns false when the investigation is finished. + /// + private bool InvestigateLastKnown() + { + if (_lkpTarget == null || Core.TickCount - _investigateStopTick >= 0) + { + return false; + } + + if (Mobile.InRange(_lkpLocation, 1) || + Mobile.InLOS(_lkpLocation) && Mobile.InRange(_lkpLocation, Mobile.RangePerception)) + { + DebugSay("They truly disappeared..."); + return false; + } + + _lkpGoal ??= _lkpLocation; + return MoveToPoint(_lkpGoal, false); + } + + private void ClearLastKnown() + { + _lkpTarget = null; + _lkpGoal = null; + _investigating = false; + } + public virtual bool DoActionFlee() { var from = Mobile.Combatant; @@ -491,6 +628,7 @@ public abstract partial class BaseAI if (target == null) { + _herdGoal = null; return false; } @@ -498,7 +636,15 @@ public abstract partial class BaseAI if (distance >= 1 && distance <= 15) { - DoMove(Mobile.GetDirectionTo(target)); + // A cached boxed goal keeps the PathFollower persistent across ticks; walking + // through MoveToPoint paces herding on the movement clock and paths around + // obstacles. + if (_herdGoal == null || _herdGoal.X != target.X || _herdGoal.Y != target.Y) + { + _herdGoal = new Point3D(target.X, target.Y, Mobile.Map?.GetAverageZ(target.X, target.Y) ?? Mobile.Z); + } + + MoveToPoint(_herdGoal, false); return true; } @@ -508,6 +654,7 @@ public abstract partial class BaseAI } Mobile.TargetLocation = null; + _herdGoal = null; return false; } @@ -999,6 +1146,6 @@ public abstract partial class BaseAI public virtual void OnCurrentSpeedChanged() { - AITimer.Interval = TimeSpan.FromSeconds(Mobile.CurrentSpeed); + AITimer.OnSpeedChanged(); } } diff --git a/Projects/UOContent/Mobiles/AI/MageAI.cs b/Projects/UOContent/Mobiles/AI/MageAI.cs index e9450db98..61be59a1f 100644 --- a/Projects/UOContent/Mobiles/AI/MageAI.cs +++ b/Projects/UOContent/Mobiles/AI/MageAI.cs @@ -679,7 +679,7 @@ public class MageAI : BaseAI Mobile.Combatant = Mobile.FocusMob; Mobile.FocusMob = null; } - else if (!Mobile.InRange(c, Mobile.RangePerception * 3)) + else if (!Mobile.InRange(c, Mobile.ChaseLeashRange)) { Mobile.Combatant = null; } @@ -695,6 +695,23 @@ public class MageAI : BaseAI } } + // Geometry (not hiding — CanSee passed above) is blocking the shot: close in until + // line of sight returns. Poisoned mages still fall through to cure. + if (!Mobile.Poisoned && Mobile.Spell?.IsCasting != true && !Mobile.InLOS(c)) + { + DebugSay("I cannot see my target, moving to regain line of sight"); + + if (!MoveTo(c, false, 1)) + { + OnFailedMove(); + } + + _lastTarget = c; + _lastTargetLoc = c.Location; + + return true; + } + if (Mobile.TriggerAbility(MonsterAbilityTrigger.CombatAction, c)) { DebugSay("I used my abilities!"); @@ -1018,7 +1035,16 @@ public class MageAI : BaseAI if (toTarget != null) { - RunTo(toTarget); + // Without line of sight the stand-off is pointless — close in so the held + // target can be invoked. + if (!Mobile.InLOS(toTarget)) + { + MoveTo(toTarget, true, 1); + } + else + { + RunTo(toTarget); + } } } diff --git a/Projects/UOContent/Mobiles/AI/MeleeAI.cs b/Projects/UOContent/Mobiles/AI/MeleeAI.cs index aa262caee..544770069 100644 --- a/Projects/UOContent/Mobiles/AI/MeleeAI.cs +++ b/Projects/UOContent/Mobiles/AI/MeleeAI.cs @@ -82,7 +82,7 @@ public class MeleeAI : BaseAI return true; } - if (!Mobile.InRange(combatant, Mobile.RangePerception * 3)) + if (!Mobile.InRange(combatant, Mobile.ChaseLeashRange)) { Mobile.Combatant = null; } diff --git a/Projects/UOContent/Mobiles/Animals/Misc/Sheep.cs b/Projects/UOContent/Mobiles/Animals/Misc/Sheep.cs index b43c34272..741093110 100644 --- a/Projects/UOContent/Mobiles/Animals/Misc/Sheep.cs +++ b/Projects/UOContent/Mobiles/Animals/Misc/Sheep.cs @@ -5,9 +5,14 @@ using System.Runtime.CompilerServices; namespace Server.Mobiles { - [SerializationGenerator(0, false)] + [SerializationGenerator(1, false)] public partial class Sheep : BaseCreature, ICarvable { + private void MigrateFrom(V0Content content) + { + _nextWoolTime = content.NextWoolTime; + } + [Constructible] public Sheep() : base(AIType.AI_Animal, FightMode.Aggressor) { @@ -43,18 +48,14 @@ namespace Server.Mobiles public override string CorpseName => "a sheep corpse"; - [DeltaDateTime] - [SerializableProperty(0)] - [CommandProperty(AccessLevel.GameMaster)] - public DateTime NextWoolTime + [SerializableField(0, fieldChanged: nameof(OnNextWoolTimeChanged))] + [AnchoredDateTime] + [SerializedCommandProperty(AccessLevel.GameMaster)] + private DateTime _nextWoolTime; + + private void OnNextWoolTimeChanged(DateTime oldValue, DateTime newValue) { - get => _nextWoolTime; - set - { - _nextWoolTime = value; - SheepBody(); - this.MarkDirty(); - } + SheepBody(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/Projects/UOContent/Mobiles/Animals/Mounts/Ethereals.cs b/Projects/UOContent/Mobiles/Animals/Mounts/Ethereals.cs index f13cebcbe..f6dc13b40 100644 --- a/Projects/UOContent/Mobiles/Animals/Mounts/Ethereals.cs +++ b/Projects/UOContent/Mobiles/Animals/Mounts/Ethereals.cs @@ -11,19 +11,19 @@ namespace Server.Mobiles public partial class EtherealMount : Item, IMount, IMountItem, IRewardItem { [SerializableField(0)] + [SaveFlag(nameof(ShouldSerializeIsDonationItem))] [SerializedCommandProperty(AccessLevel.GameMaster, AccessLevel.Administrator)] public bool _isDonationItem; [MethodImpl(MethodImplOptions.AggressiveInlining)] - [SerializableFieldSaveFlag(0)] public bool ShouldSerializeIsDonationItem() => _isDonationItem; [SerializableField(1)] + [SaveFlag(nameof(ShouldSerializeIsRewardItem))] [SerializedCommandProperty(AccessLevel.GameMaster)] public bool _isRewardItem; [MethodImpl(MethodImplOptions.AggressiveInlining)] - [SerializableFieldSaveFlag(1)] public bool ShouldSerializeIsRewardItem() => _isRewardItem; [Constructible] @@ -40,43 +40,27 @@ namespace Server.Mobiles public override double DefaultWeight => 1.0; - [SerializableProperty(2)] - [CommandProperty(AccessLevel.GameMaster)] - public int MountedID - { - get => _mountedID; - set - { - if (_mountedID != value) - { - _mountedID = value; + [SerializableField(2, fieldChanged: nameof(OnMountedIDChanged))] + [SerializedCommandProperty(AccessLevel.GameMaster)] + private int _mountedID; - if (_rider != null) - { - ItemID = value; - } - this.MarkDirty(); - } + private void OnMountedIDChanged(int oldValue, int newValue) + { + if (_rider != null) + { + ItemID = newValue; } } - [SerializableProperty(3)] - [CommandProperty(AccessLevel.GameMaster)] - public int RegularID - { - get => _regularID; - set - { - if (_regularID != value) - { - _regularID = value; + [SerializableField(3, fieldChanged: nameof(OnRegularIDChanged))] + [SerializedCommandProperty(AccessLevel.GameMaster)] + private int _regularID; - if (_rider == null) - { - ItemID = value; - } - this.MarkDirty(); - } + private void OnRegularIDChanged(int oldValue, int newValue) + { + if (_rider == null) + { + ItemID = newValue; } } @@ -87,6 +71,7 @@ namespace Server.Mobiles public virtual int EtherealHue => 0x4001; [SerializableProperty(4)] + [SaveFlag(nameof(ShouldSerializeRider))] [CommandProperty(AccessLevel.GameMaster)] public Mobile Rider { @@ -124,22 +109,19 @@ namespace Server.Mobiles } } - [SerializableFieldSaveFlag(4)] private bool ShouldSerializeRider() => _rider != null; - [CommandProperty(AccessLevel.GameMaster)] - [SerializableProperty(5)] - public int Steps + [SerializableField(5, allowFieldChange: nameof(AllowStepsChange))] + [SerializedCommandProperty(AccessLevel.GameMaster)] + [SaveFlag(nameof(ShouldSerializeSteps))] + private int _steps; + + private bool AllowStepsChange(ref int value) { - get => _steps; - set - { - _steps = Math.Clamp(value, 0, StepsMax); - this.MarkDirty(); - } + value = Math.Clamp(value, 0, StepsMax); + return true; } - [SerializableFieldSaveFlag(5)] private bool ShouldSerializeSteps() => _steps != StepsMax; public virtual int StepsMax => 3840; // Should be same as horse diff --git a/Projects/UOContent/Mobiles/Animals/Mounts/SwampDragon.cs b/Projects/UOContent/Mobiles/Animals/Mounts/SwampDragon.cs index 69bd9ce56..19a061af2 100644 --- a/Projects/UOContent/Mobiles/Animals/Mounts/SwampDragon.cs +++ b/Projects/UOContent/Mobiles/Animals/Mounts/SwampDragon.cs @@ -62,48 +62,37 @@ namespace Server.Mobiles [SerializableField(3)] private int _bardingHP; - [CommandProperty(AccessLevel.GameMaster)] - [SerializableProperty(2)] - public bool HasBarding - { - get => _hasBarding; - set - { - _hasBarding = value; + [SerializableField(2, fieldChanged: nameof(OnHasBardingChanged))] + [SerializedCommandProperty(AccessLevel.GameMaster)] + [InvalidateProperties] + private bool _hasBarding; - if (_hasBarding) - { - Hue = CraftResources.GetHue(_bardingResource); - Body = 0x31F; - ItemID = 0x3EBE; - } - else - { - Hue = 0x851; - Body = 0x31A; - ItemID = 0x3EBD; - } - InvalidateProperties(); - this.MarkDirty(); + private void OnHasBardingChanged(bool oldValue, bool newValue) + { + if (_hasBarding) + { + Hue = CraftResources.GetHue(_bardingResource); + Body = 0x31F; + ItemID = 0x3EBE; + } + else + { + Hue = 0x851; + Body = 0x31A; + ItemID = 0x3EBD; } } - [CommandProperty(AccessLevel.GameMaster)] - [SerializableProperty(4)] - public CraftResource BardingResource + [SerializableField(4, fieldChanged: nameof(OnBardingResourceChanged))] + [SerializedCommandProperty(AccessLevel.GameMaster)] + [InvalidateProperties] + private CraftResource _bardingResource; + + private void OnBardingResourceChanged(CraftResource oldValue, CraftResource newValue) { - get => _bardingResource; - set + if (_hasBarding) { - _bardingResource = value; - - if (_hasBarding) - { - Hue = CraftResources.GetHue(value); - } - - InvalidateProperties(); - this.MarkDirty(); + Hue = CraftResources.GetHue(newValue); } } diff --git a/Projects/UOContent/Mobiles/BaseCreature.cs b/Projects/UOContent/Mobiles/BaseCreature.cs index af85a195a..f64a289f4 100644 --- a/Projects/UOContent/Mobiles/BaseCreature.cs +++ b/Projects/UOContent/Mobiles/BaseCreature.cs @@ -265,8 +265,12 @@ namespace Server.Mobiles private double _passiveSpeed; private double _currentSpeed; - // Herding - Overrides the AI to force the mob to move to a specific location - // Thinking: 0.3s, Movement: 0.6s. + // Movement clock (seconds per step); 0 = inherit the matching think value. + private double _activeMoveSpeed; + private double _passiveMoveSpeed; + + // Herding - forces the mob to walk to a specific location, paced by the movement + // clock at HerdingMoveSpeed. Thinking is unaffected. private IPoint2D _targetLocation; private int m_DamageMax = -1; @@ -342,6 +346,7 @@ namespace Server.Mobiles FightMode = mode; GetSpeeds(out var activeSpeed, out var passiveSpeed); + GetMoveSpeeds(out _activeMoveSpeed, out _passiveMoveSpeed); ActiveSpeed = activeSpeed; PassiveSpeed = passiveSpeed; @@ -660,12 +665,20 @@ namespace Server.Mobiles [CommandProperty(AccessLevel.GameMaster)] public int RangePerception { get; set; } + /// + /// How far a chase may stretch before the creature gives up its combatant. Between + /// RangePerception and this leash it keeps chasing but may switch to closer targets. + /// + [CommandProperty(AccessLevel.GameMaster)] + public virtual int ChaseLeashRange => RangePerception * 2; + [CommandProperty(AccessLevel.GameMaster)] public int RangeFight { get; set; } [CommandProperty(AccessLevel.GameMaster)] public int RangeHome { get; set; } = 10; + /// Seconds per AI decision while engaged; see for movement pace. [CommandProperty(AccessLevel.GameMaster)] public virtual double ActiveSpeed { @@ -679,6 +692,7 @@ namespace Server.Mobiles } } + /// Seconds per AI decision while idle; see for movement pace. [CommandProperty(AccessLevel.GameMaster)] public virtual double PassiveSpeed { @@ -693,21 +707,37 @@ namespace Server.Mobiles } } + /// Seconds per step while engaged. Inherits ; set 0 to re-inherit. + [CommandProperty(AccessLevel.GameMaster)] + public virtual double ActiveMoveSpeed + { + get => _activeMoveSpeed > 0 ? _activeMoveSpeed : _activeSpeed; + set => _activeMoveSpeed = value > 0 ? value : 0; + } + + /// Seconds per step while idle. Inherits ; set 0 to re-inherit. + [CommandProperty(AccessLevel.GameMaster)] + public virtual double PassiveMoveSpeed + { + get => _passiveMoveSpeed > 0 ? _passiveMoveSpeed : _passiveSpeed; + set => _passiveMoveSpeed = value > 0 ? value : 0; + } + + // Herded creatures walk at a fixed standard pace regardless of their own speed + // (RunUO's forced 0.3, without its TransformMoveDelay inflation to 0.6). + private const double HerdingMoveSpeed = 0.3; + [CommandProperty(AccessLevel.GameMaster)] public IPoint2D TargetLocation { get => _targetLocation; - set - { - _targetLocation = value; - AIObject?.OnCurrentSpeedChanged(); - } + set => _targetLocation = value; } [CommandProperty(AccessLevel.GameMaster)] public double CurrentSpeed { - get => _targetLocation != null ? 0.3 : _currentSpeed; + get => _currentSpeed; set { if (Math.Abs(_currentSpeed - value) > 0.0001) @@ -718,8 +748,26 @@ namespace Server.Mobiles } } + /// + /// Resolved seconds per step: a verbatim active/passive + /// maps to the matching movement value; a bespoke pace stays fused to both clocks. + /// A herded creature is always driven at . + /// [CommandProperty(AccessLevel.GameMaster)] - public double MoveSpeedMod { get; set; } + public double CurrentMoveSpeed + { + get + { + if (_targetLocation != null) + { + return HerdingMoveSpeed; + } + + return _currentSpeed == _activeSpeed ? ActiveMoveSpeed + : _currentSpeed == _passiveSpeed ? PassiveMoveSpeed + : _currentSpeed; + } + } [CommandProperty(AccessLevel.GameMaster)] public Point3D Home @@ -1843,7 +1891,7 @@ namespace Server.Mobiles { base.Serialize(writer); - writer.Write(20); // version + writer.Write(22); // version writer.Write((int)m_CurrentAI); writer.Write((int)m_DefaultAI); @@ -1880,7 +1928,7 @@ namespace Server.Mobiles if (_summoned) { - writer.WriteDeltaTime(SummonEnd); + writer.WriteAnchoredTime(SummonEnd); } writer.Write(ControlSlots); @@ -1963,6 +2011,10 @@ namespace Server.Mobiles // Version 19 writer.Write(HomeMap); + + // Version 22 (0 = inherit the matching think value) + writer.Write(_activeMoveSpeed); + writer.Write(_passiveMoveSpeed); } public override void Deserialize(IGenericReader reader) @@ -2035,7 +2087,7 @@ namespace Server.Mobiles if (_summoned) { - SummonEnd = reader.ReadDeltaTime(); + SummonEnd = version >= 21 ? reader.ReadAnchoredTime() : reader.ReadDeltaTime(); new UnsummonTimer(this, SummonEnd - Core.Now).Start(); } @@ -2166,6 +2218,16 @@ namespace Server.Mobiles HomeMap = reader.ReadMap(); } + if (version >= 22) + { + _activeMoveSpeed = reader.ReadDouble(); + _passiveMoveSpeed = reader.ReadDouble(); + } + else + { + MigrateMoveSpeeds(); + } + if (version <= 14 && m_Paragon && Hue == 0x31) { Hue = Paragon.Hue; // Paragon hue fixed, should now be 0x501. @@ -4579,13 +4641,78 @@ namespace Server.Mobiles return false; } + /// + /// Sets the think clock and clears movement overrides (legacy one-clock semantics); + /// use for an independent movement pace. + /// public void SetSpeed(double active, double passive, bool isPassive = true) { ActiveSpeed = active; PassiveSpeed = passive; + ClearMoveSpeed(); CurrentSpeed = isPassive ? PassiveSpeed : ActiveSpeed; } + /// Sets only the movement clock (seconds per step). + public void SetMoveSpeed(double active, double passive) + { + ActiveMoveSpeed = active; + PassiveMoveSpeed = passive; + } + + /// Clears movement overrides; steps pace off the think clock again. + public void ClearMoveSpeed() + { + _activeMoveSpeed = 0; + _passiveMoveSpeed = 0; + } + + /// + /// Scales movement overrides (paragon and similar buffs). Inheriting values stay + /// inheriting — they already follow the scaled think clock. + /// + public void ScaleMoveSpeed(double scalar) + { + if (_activeMoveSpeed > 0) + { + _activeMoveSpeed *= scalar; + } + + if (_passiveMoveSpeed > 0) + { + _passiveMoveSpeed *= scalar; + } + } + + /// + /// Snaps speeds within rounding distance of the creature's table values back to + /// exact. A scaling buff that divides then multiplies can drift by an ulp (e.g. + /// 0.9 and 0.45 through 1.2), which would read as hand-tuned; call after undoing + /// such a buff. Genuinely tuned speeds are nowhere near the epsilon and keep. + /// + public void SnapSpeedsToTable() + { + GetSpeeds(out var activeSpeed, out var passiveSpeed); + + if (Math.Abs(_activeSpeed - activeSpeed) < 0.0001 && Math.Abs(_passiveSpeed - passiveSpeed) < 0.0001) + { + _activeSpeed = activeSpeed; + _passiveSpeed = passiveSpeed; + } + + GetMoveSpeeds(out var activeMoveSpeed, out var passiveMoveSpeed); + + if (activeMoveSpeed > 0 && Math.Abs(_activeMoveSpeed - activeMoveSpeed) < 0.0001) + { + _activeMoveSpeed = activeMoveSpeed; + } + + if (passiveMoveSpeed > 0 && Math.Abs(_passiveMoveSpeed - passiveMoveSpeed) < 0.0001) + { + _passiveMoveSpeed = passiveMoveSpeed; + } + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void SetCurrentSpeedToActive() => CurrentSpeed = ActiveSpeed; @@ -4899,6 +5026,25 @@ namespace Server.Mobiles NPCSpeeds.GetSpeeds(this, out activeSpeed, out passiveSpeed); } + public virtual void GetMoveSpeeds(out double activeMoveSpeed, out double passiveMoveSpeed) + { + NPCSpeeds.GetMoveSpeeds(this, out activeMoveSpeed, out passiveMoveSpeed); + } + + // Pre-v22 saves carry no movement clock. A creature whose serialized think speeds + // still match what it would spawn with today was never hand-tuned: adopt today's + // move values so existing worlds (and pets) pick up npc-speeds pacing without a + // respawn. Tuned creatures keep movement inheriting their think clock. + internal void MigrateMoveSpeeds() + { + GetSpeeds(out var activeSpeed, out var passiveSpeed); + + if (_activeSpeed == activeSpeed && _passiveSpeed == passiveSpeed) + { + GetMoveSpeeds(out _activeMoveSpeed, out _passiveMoveSpeed); + } + } + public virtual void DropBackpack() { var backpack = Backpack; diff --git a/Projects/UOContent/Mobiles/Hireables/BaseHire.cs b/Projects/UOContent/Mobiles/Hireables/BaseHire.cs index e3fbadd97..3f96e5d36 100644 --- a/Projects/UOContent/Mobiles/Hireables/BaseHire.cs +++ b/Projects/UOContent/Mobiles/Hireables/BaseHire.cs @@ -23,19 +23,14 @@ public partial class BaseHire : BaseCreature public int GoldOnDeath { get; set; } - [SerializableProperty(1)] - [CommandProperty(AccessLevel.GameMaster)] - public bool IsHired - { - get => _isHired; - set - { - _isHired = value; + [SerializableField(1, fieldChanged: nameof(OnIsHiredChanged))] + [SerializedCommandProperty(AccessLevel.GameMaster)] + [InvalidateProperties] + private bool _isHired; - Delta(MobileDelta.Noto); - InvalidateProperties(); - this.MarkDirty(); - } + private void OnIsHiredChanged(bool oldValue, bool newValue) + { + Delta(MobileDelta.Noto); } public BaseHire(AIType AI) : base(AI, FightMode.Aggressor) diff --git a/Projects/UOContent/Mobiles/NPCSpeeds.cs b/Projects/UOContent/Mobiles/NPCSpeeds.cs index a61ce70c6..8f5e908bb 100644 --- a/Projects/UOContent/Mobiles/NPCSpeeds.cs +++ b/Projects/UOContent/Mobiles/NPCSpeeds.cs @@ -38,6 +38,22 @@ public static class NPCSpeeds passiveSpeed = sp.PassiveSpeed; } + // Move speeds are optional (0 = inherit), so this tolerates a missing entry or table. + public static void GetMoveSpeeds(BaseCreature bc, out double activeMoveSpeed, out double passiveMoveSpeed) + { + if ((bc.SpeedClass == SpeedLevel.None || !_speedsByLevel.TryGetValue(bc.SpeedClass, out var sp)) && + !_speedsByType.TryGetValue(bc.GetType(), out sp) && + !_speedsByLevel.TryGetValue(SpeedLevel.Medium, out sp)) + { + activeMoveSpeed = 0; + passiveMoveSpeed = 0; + return; + } + + activeMoveSpeed = sp.ActiveMoveSpeed; + passiveMoveSpeed = sp.PassiveMoveSpeed; + } + public static void RegisterSpeed(SpeedClassEntry entry) { _speedsByLevel[entry.Level] = entry; @@ -78,6 +94,13 @@ public static class NPCSpeeds [JsonPropertyName("passive")] public double PassiveSpeed { get; init; } + // Movement clock (seconds per step); absent/0 = inherit the matching think value. + [JsonPropertyName("activeMove")] + public double ActiveMoveSpeed { get; init; } + + [JsonPropertyName("passiveMove")] + public double PassiveMoveSpeed { get; init; } + [JsonPropertyName("types")] public HashSet Types { get; init; } } diff --git a/Projects/UOContent/Mobiles/Special/Paragon.cs b/Projects/UOContent/Mobiles/Special/Paragon.cs index 825609e22..cbd0a30c7 100644 --- a/Projects/UOContent/Mobiles/Special/Paragon.cs +++ b/Projects/UOContent/Mobiles/Special/Paragon.cs @@ -79,6 +79,7 @@ public static class Paragon bc.PassiveSpeed /= SpeedBuff; bc.ActiveSpeed /= SpeedBuff; + bc.ScaleMoveSpeed(1.0 / SpeedBuff); bc.CurrentSpeed = bc.PassiveSpeed; bc.DamageMin += DamageBuff; @@ -143,6 +144,8 @@ public static class Paragon bc.PassiveSpeed *= SpeedBuff; bc.ActiveSpeed *= SpeedBuff; + bc.ScaleMoveSpeed(SpeedBuff); + bc.SnapSpeedsToTable(); // an ulp of scaling drift must not read as hand-tuned bc.CurrentSpeed = bc.PassiveSpeed; bc.DamageMin -= DamageBuff; diff --git a/Projects/UOContent/Mobiles/Townfolk/BaseEscortable.cs b/Projects/UOContent/Mobiles/Townfolk/BaseEscortable.cs index 462307be3..cf439a76c 100644 --- a/Projects/UOContent/Mobiles/Townfolk/BaseEscortable.cs +++ b/Projects/UOContent/Mobiles/Townfolk/BaseEscortable.cs @@ -17,7 +17,7 @@ using EDI = Server.Mobiles.EscortDestinationInfo; namespace Server.Mobiles; -[SerializationGenerator(2, false)] +[SerializationGenerator(3, false)] public partial class BaseEscortable : BaseCreature { private static readonly ILogger logger = LogFactory.GetLogger(typeof(BaseEscortable)); @@ -158,16 +158,22 @@ public partial class BaseEscortable : BaseCreature [SerializableField(0, setter: "private")] private string _destinationString; - [TimerDrift] [SerializableField(1)] + [DeserializeTimer(nameof(DeserializeDeleteTimer))] private Timer _deleteTimer; - [DeserializeTimerField(1)] - private void DeserializeDeleteTimer(TimeSpan delay) + private void DeserializeDeleteTimer(TimeSpan delay) => Timer.DelayCall(delay, Delete); + + private void MigrateFrom(V2Content content) { - if (delay >= TimeSpan.Zero) + _destinationString = content.DestinationString; + _mlQuestType = content.MlQuestType; + _mlQuestDestinationMessage = content.MlQuestDestinationMessage; + _mlQuestPaymentMessage = content.MlQuestPaymentMessage; + + if (content.DeleteTimerDelay != TimeSpan.MinValue) { - Timer.DelayCall(delay, Delete); + DeserializeDeleteTimer(content.DeleteTimerDelay); } } diff --git a/Projects/UOContent/Mobiles/Vendors/Barkeeper/PlayerBarkeeper.cs b/Projects/UOContent/Mobiles/Vendors/Barkeeper/PlayerBarkeeper.cs index d80c60dcc..bbbc3ff6f 100644 --- a/Projects/UOContent/Mobiles/Vendors/Barkeeper/PlayerBarkeeper.cs +++ b/Projects/UOContent/Mobiles/Vendors/Barkeeper/PlayerBarkeeper.cs @@ -148,18 +148,13 @@ public partial class PlayerBarkeeper : BaseVendor LoadSBInfo(); } - [SerializableProperty(0)] - public BaseHouse House - { - get => _house; - set - { - _house?.PlayerBarkeepers.Remove(this); - value?.PlayerBarkeepers.Add(this); + [SerializableField(0, fieldChanged: nameof(OnHouseChanged))] + private BaseHouse _house; - _house = value; - this.MarkDirty(); - } + private void OnHouseChanged(BaseHouse oldValue, BaseHouse newValue) + { + oldValue?.PlayerBarkeepers.Remove(this); + newValue?.PlayerBarkeepers.Add(this); } public override bool IsActiveBuyer => false; diff --git a/Projects/UOContent/Mobiles/Vendors/PlayerVendor.cs b/Projects/UOContent/Mobiles/Vendors/PlayerVendor.cs index 2ff6eb812..9b8f5a2f3 100644 --- a/Projects/UOContent/Mobiles/Vendors/PlayerVendor.cs +++ b/Projects/UOContent/Mobiles/Vendors/PlayerVendor.cs @@ -22,9 +22,20 @@ public class PlayerVendorTargetAttribute : Attribute; * Next, uncomment the MigrateFrom function and change the `V3Content` type to match the serialization version * before it was bumped. Then run publish.cmd to generate the migration file. */ -[SerializationGenerator(3, false)] +[SerializationGenerator(4, false)] public partial class PlayerVendor : Mobile { + private void MigrateFrom(V3Content content) + { + _shopName = content.ShopName; + _nextPayTime = content.NextPayTime; + _house = content.House; + _owner = content.Owner; + _bankAccount = content.BankAccount; + _holdGold = content.HoldGold; + _sellItems = content.SellItems; + } + private Timer _payTimer; [InvalidateProperties] @@ -32,7 +43,7 @@ public partial class PlayerVendor : Mobile [SerializedCommandProperty(AccessLevel.GameMaster)] private string _shopName; - [DeltaDateTime] + [AnchoredDateTime] [SerializableField(1, setter: "private")] [SerializedCommandProperty(AccessLevel.GameMaster)] private DateTime _nextPayTime; @@ -94,18 +105,13 @@ public partial class PlayerVendor : Mobile public PlayerVendorPlaceholder Placeholder { get; set; } - [SerializableProperty(2)] - public BaseHouse House - { - get => _house; - set - { - _house?.PlayerVendors.Remove(this); - value?.PlayerVendors.Add(this); + [SerializableField(2, fieldChanged: nameof(OnHouseChanged))] + private BaseHouse _house; - _house = value; - this.MarkDirty(); - } + private void OnHouseChanged(BaseHouse oldValue, BaseHouse newValue) + { + oldValue?.PlayerVendors.Remove(this); + newValue?.PlayerVendors.Add(this); } public int ChargePerDay diff --git a/Projects/UOContent/Mobiles/Vendors/RentedVendor.cs b/Projects/UOContent/Mobiles/Vendors/RentedVendor.cs index 99d818826..d7d0a3ab6 100644 --- a/Projects/UOContent/Mobiles/Vendors/RentedVendor.cs +++ b/Projects/UOContent/Mobiles/Vendors/RentedVendor.cs @@ -46,9 +46,20 @@ public class VendorRentalDuration } } -[SerializationGenerator(0)] +[SerializationGenerator(1)] public partial class RentedVendor : PlayerVendor { + private void MigrateFrom(V0Content content) + { + _rentalDurationId = content.RentalDurationId; + _rentalPrice = content.RentalPrice; + _landlordRenew = content.LandlordRenew; + _renterRenew = content.RenterRenew; + _renewalPrice = content.RenewalPrice; + _rentalGold = content.RentalGold; + _rentalExpireTime = content.RentalExpireTime; + } + private Timer _rentalExpireTimer; public RentedVendor( @@ -93,7 +104,7 @@ public partial class RentedVendor : PlayerVendor [SerializedCommandProperty(AccessLevel.GameMaster)] private int _rentalGold; - [DeltaDateTime] + [AnchoredDateTime] [SerializableField(6)] [SerializedCommandProperty(AccessLevel.GameMaster)] private DateTime _rentalExpireTime; diff --git a/Projects/UOContent/Mobiles/Vendors/VendorInventory.cs b/Projects/UOContent/Mobiles/Vendors/VendorInventory.cs index d4d5ce4bd..8f0a1fba8 100644 --- a/Projects/UOContent/Mobiles/Vendors/VendorInventory.cs +++ b/Projects/UOContent/Mobiles/Vendors/VendorInventory.cs @@ -37,7 +37,7 @@ namespace Server.Mobiles Items = reader.ReadEntityList(); Gold = reader.ReadInt(); - ExpireTime = reader.ReadDeltaTime(); + ExpireTime = version >= 1 ? reader.ReadAnchoredTime() : reader.ReadDeltaTime(); if (Items.Count == 0 && Gold == 0) { @@ -88,7 +88,7 @@ namespace Server.Mobiles public void Serialize(IGenericWriter writer) { - writer.WriteEncodedInt(0); // version + writer.WriteEncodedInt(1); // version writer.Write(Owner); writer.Write(VendorName); @@ -98,7 +98,7 @@ namespace Server.Mobiles writer.Write(Items); writer.Write(Gold); - writer.WriteDeltaTime(ExpireTime); + writer.WriteAnchoredTime(ExpireTime); } private class ExpireTimer : Timer diff --git a/Projects/UOContent/Mobiles/Vendors/VendorItem.cs b/Projects/UOContent/Mobiles/Vendors/VendorItem.cs index 14af3656a..99d1446e6 100644 --- a/Projects/UOContent/Mobiles/Vendors/VendorItem.cs +++ b/Projects/UOContent/Mobiles/Vendors/VendorItem.cs @@ -32,18 +32,20 @@ public partial class VendorItem public string FormattedPrice => Core.ML ? Price.ToString("N0", CultureInfo.GetCultureInfo("en-US")) : Price.ToString(); - [SerializableProperty(2)] - public string Description - { - get => _description; - set - { - _description = value ?? ""; + [SerializableField(2, fieldChanged: nameof(OnDescriptionChanged), allowFieldChange: nameof(AllowDescriptionChange))] + private string _description; - if (Valid) - { - Item.InvalidateProperties(); - } + private bool AllowDescriptionChange(ref string value) + { + value = value ?? ""; + return true; + } + + private void OnDescriptionChanged(string oldValue, string newValue) + { + if (Valid) + { + Item.InvalidateProperties(); } } diff --git a/Projects/UOContent/Multis/Boats/BaseBoat.cs b/Projects/UOContent/Multis/Boats/BaseBoat.cs index 2082056da..ed618b91a 100644 --- a/Projects/UOContent/Multis/Boats/BaseBoat.cs +++ b/Projects/UOContent/Multis/Boats/BaseBoat.cs @@ -19,9 +19,24 @@ namespace Server.Multis Single } - [SerializationGenerator(4, false)] + [SerializationGenerator(5, false)] public abstract partial class BaseBoat : BaseMulti { + private void MigrateFrom(V4Content content) + { + _mapItem = content.MapItem; + _nextNavPoint = content.NextNavPoint; + _facing = content.Facing; + _timeOfDecay = content.TimeOfDecay; + _owner = content.Owner; + _pPlank = content.PPlank; + _sPlank = content.SPlank; + _tillerMan = content.TillerMan; + _hold = content.Hold; + _anchored = content.Anchored; + _shipName = content.ShipName; + } + public enum DryDockResult { Valid, @@ -136,31 +151,23 @@ namespace Server.Multis } } - [DeltaDateTime] - [SerializableProperty(3)] - [CommandProperty(AccessLevel.GameMaster)] - public DateTime TimeOfDecay + [SerializableField(3, fieldChanged: nameof(OnTimeOfDecayChanged))] + [AnchoredDateTime] + [SerializedCommandProperty(AccessLevel.GameMaster)] + private DateTime _timeOfDecay; + + private void OnTimeOfDecayChanged(DateTime oldValue, DateTime newValue) { - get => _timeOfDecay; - set - { - _timeOfDecay = value; - TillerMan?.InvalidateProperties(); - this.MarkDirty(); - } + TillerMan?.InvalidateProperties(); } - [SerializableProperty(10)] - [CommandProperty(AccessLevel.GameMaster)] - public string ShipName + [SerializableField(10, fieldChanged: nameof(OnShipNameChanged))] + [SerializedCommandProperty(AccessLevel.GameMaster)] + private string _shipName; + + private void OnShipNameChanged(string oldValue, string newValue) { - get => _shipName; - set - { - _shipName = value; - TillerMan?.InvalidateProperties(); - this.MarkDirty(); - } + TillerMan?.InvalidateProperties(); } [CommandProperty(AccessLevel.GameMaster)] diff --git a/Projects/UOContent/Multis/Camps/BaseCamp.cs b/Projects/UOContent/Multis/Camps/BaseCamp.cs index e3d10dd0b..e60e3e46e 100644 --- a/Projects/UOContent/Multis/Camps/BaseCamp.cs +++ b/Projects/UOContent/Multis/Camps/BaseCamp.cs @@ -6,9 +6,16 @@ using Server.Mobiles; namespace Server.Multis; -[SerializationGenerator(1, false)] +[SerializationGenerator(2, false)] public abstract partial class BaseCamp : BaseMulti { + private void MigrateFrom(V1Content content) + { + _items = content.Items; + _mobiles = content.Mobiles; + _decayTime = content.DecayTime; + } + [Tidy] [SerializableField(0, setter: "private")] private List _items; @@ -17,7 +24,7 @@ public abstract partial class BaseCamp : BaseMulti [SerializableField(1, setter: "private")] private List _mobiles; - [DeltaDateTime] + [AnchoredDateTime] [SerializableField(2, setter: "private")] private DateTime _decayTime; diff --git a/Projects/UOContent/Network/AutoDenylist/AutoDenylist.cs b/Projects/UOContent/Network/AutoDenylist/AutoDenylist.cs index a55280e1a..7b3c44ae4 100644 --- a/Projects/UOContent/Network/AutoDenylist/AutoDenylist.cs +++ b/Projects/UOContent/Network/AutoDenylist/AutoDenylist.cs @@ -17,7 +17,6 @@ using System; using System.Collections.Generic; using System.Net; using System.Threading; -using Server.Collections; using Server.Logging; using Server.Network.Bans; @@ -29,16 +28,30 @@ namespace Server.Network; /// /// The local half of promotion. Contributing to CrowdSec only helps once an OS bouncer reacts; until then /// every reconnect costs a socket, a buffer and a NetState slot — and the verdicts that matter most -/// are reachable only after reading bytes, like a zero seed. It is also the whole defence on a shard running +/// are reachable only after reading bytes, like a zero seed. It is also the whole defense on a shard running /// no bouncer, which is the default. Not persisted, by design: a holding pen that survives restarts is a ban /// without a ban's review. Only verdicts are held. /// +/// +/// A hold is never refreshed, so every expiry is insertion + duration and the ring is sorted by +/// construction. Retiring lapsed entries is therefore the number expiring rather than the number held, which +/// is what lets the cap be sized for the flood instead of for a scan. +/// public static class AutoDenylist { private static readonly ILogger logger = LogFactory.GetLogger(typeof(AutoDenylist)); - // Address (normalized v6 bits) -> Core.TickCount at which the hold lapses. Loop-only. - private static readonly Dictionary _held = []; + // Membership only (normalized v6 bits). Loop-only. The expiry lives beside the key in the ring, so + // there is exactly one copy of it and the two cannot disagree. + private static readonly HashSet _held = []; + + // The same keys in expiry order. Parallel arrays rather than an array of structs: UInt128 forces + // 16-byte alignment, so a packed (key, expiry) struct costs 32 bytes where these cost 24 -- and the + // drain reads only the long[], 8 sequential bytes per entry. + private static UInt128[] _ringKeys = []; + private static long[] _ringExpiry = []; + private static int _ringHead; + private static int _ringCount; private static bool _enabled; private static long _durationMs; @@ -47,6 +60,9 @@ public static class AutoDenylist public static int Count => _held.Count; + // Test seam: the ring and the set hold the same entries, and nothing else may assume it. + internal static int RingCount => _ringCount; + public static void Configure() { AutoDenylistConfiguration.Load(); @@ -78,35 +94,47 @@ public static class AutoDenylist return false; } + Drain(nowTicks); + var key = address.ToUInt128(); - // An address already held is just extended, so no cap check is needed. - if (!_held.ContainsKey(key) && _held.Count >= _maxEntries) + // Deliberately not refreshed: the first detection sets the expiry and later ones leave it alone. + // That keeps insertion order equal to expiry order, which is why the drain can stop at the first + // live record. A flooder whose hold lapses trips the rate limiter on its next attempt -- which + // runs ahead of the connection filters -- and is held again. + if (!_held.Add(key)) { - Sweep(nowTicks); - - if (_held.Count >= _maxEntries) - { - if (!_warnedFull) - { - _warnedFull = true; - logger.Warning( - "Auto-denylist is full at {Max} addresses; further detections are disconnected but not held", - _maxEntries - ); - } - - return false; - } + return true; } - _held[key] = nowTicks + _durationMs; + // Drain already reclaimed everything reclaimable, so being over now means genuinely full. + if (_held.Count > _maxEntries) + { + _held.Remove(key); + + if (!_warnedFull) + { + _warnedFull = true; + logger.Warning( + "Auto-denylist is full at {Max} addresses; further detections are disconnected but not held", + _maxEntries + ); + } + + return false; + } + + Push(key, nowTicks + _durationMs); return true; } public static bool IsDenied(IPAddress address) => IsDenied(address, Core.TickCount); - /// The pure decision, split out so the accept-path policy can be tested without a clock. + /// + /// The accept-path decision, split out so the policy can be tested without a clock. Drains first: the + /// expiry lives in the ring, not beside the membership, so a lapsed hold has to be retired here rather + /// than expired on read. One array read when nothing has lapsed. + /// internal static bool IsDenied(IPAddress address, long nowTicks) { if (!_enabled || address == null) @@ -114,50 +142,136 @@ public static class AutoDenylist return false; } - // Decided on read, so a lapsed hold cannot deny even before the sweep. Subtraction: TickCount wraps. - return _held.TryGetValue(address.ToUInt128(), out var expires) && expires - nowTicks > 0; + Drain(nowTicks); + return _held.Contains(address.ToUInt128()); } /// Releases an address early, e.g. when an operator retracts a ban. public static void Release(IPAddress address) { - if (_enabled && address != null) - { - _held.Remove(address.ToUInt128()); - } - } - - internal static void Sweep(long nowTicks) - { - if (_held.Count == 0) + if (!_enabled || address == null) { return; } - using var lapsed = new PooledRefList(16); - - foreach (var (address, expires) in _held) + var key = address.ToUInt128(); + if (_held.Remove(key)) { - if (expires - nowTicks <= 0) - { - lapsed.Add(address); - } + // The ring record has to go too. Nothing records that this key was released, so if it were + // detected again before the old record lapsed, that record would retire the new hold early. + // O(n), but this is an operator retraction, not the accept path. + PurgeRing(key); + } + } + + /// + /// Retires everything that has lapsed. Expiries only ever increase along the ring, so the first live + /// record ends the scan and the cost is the number actually expiring, not the number held. + /// + internal static void Drain(long nowTicks) + { + var before = _ringCount; + + // Subtraction, never a direct compare: tick counts wrap. See dev-docs/tick-counts.md. + while (_ringCount > 0 && _ringExpiry[_ringHead] - nowTicks <= 0) + { + _held.Remove(_ringKeys[_ringHead]); + _ringHead = _ringHead + 1 == _ringKeys.Length ? 0 : _ringHead + 1; + _ringCount--; } - for (var i = 0; i < lapsed.Count; i++) - { - _held.Remove(lapsed[i]); - } - - if (lapsed.Count > 0) + if (_ringCount != before) { _warnedFull = false; } } + private static void Push(UInt128 key, long expiry) + { + if (_ringCount == _ringKeys.Length) + { + Grow(); + } + + var tail = _ringHead + _ringCount; + if (tail >= _ringKeys.Length) + { + tail -= _ringKeys.Length; + } + + _ringKeys[tail] = key; + _ringExpiry[tail] = expiry; + _ringCount++; + } + + private static void Grow() + { + // Capped at the entry cap: Push only runs below it, so the ring never needs more, and doubling + // past it would reserve roughly twice the slots it can ever use. + var size = Math.Min(Math.Max(64, _ringKeys.Length * 2), _maxEntries); + var keys = new UInt128[size]; + var expiry = new long[size]; + + for (var i = 0; i < _ringCount; i++) + { + var from = _ringHead + i; + if (from >= _ringKeys.Length) + { + from -= _ringKeys.Length; + } + + keys[i] = _ringKeys[from]; + expiry[i] = _ringExpiry[from]; + } + + _ringKeys = keys; + _ringExpiry = expiry; + _ringHead = 0; + } + + private static void PurgeRing(UInt128 key) + { + var capacity = _ringKeys.Length; + + for (var i = 0; i < _ringCount; i++) + { + var at = _ringHead + i; + if (at >= capacity) + { + at -= capacity; + } + + if (_ringKeys[at] != key) + { + continue; + } + + // Close the gap so the ring stays contiguous and expiry-ordered. + for (var j = i; j < _ringCount - 1; j++) + { + var to = _ringHead + j; + if (to >= capacity) + { + to -= capacity; + } + + var from = to + 1 == capacity ? 0 : to + 1; + _ringKeys[to] = _ringKeys[from]; + _ringExpiry[to] = _ringExpiry[from]; + } + + _ringCount--; + return; + } + } + internal static void LoadForTesting(bool enabled, long durationMs, int maxEntries) { _held.Clear(); + _ringKeys = []; + _ringExpiry = []; + _ringHead = 0; + _ringCount = 0; _enabled = enabled; _durationMs = durationMs; _maxEntries = maxEntries; @@ -168,6 +282,8 @@ public static class AutoDenylist /// Accept-path gate for . public sealed class AutoDenylistFilter : IConnectionFilter { + private Timer _sweepTimer; + public string Name => "auto-denylist"; public void Register() @@ -176,12 +292,20 @@ public sealed class AutoDenylistFilter : IConnectionFilter public void Start(CancellationToken token) { - // Only an optimisation: IsDenied expires on read. - Timer.DelayCall(TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(1), () => AutoDenylist.Sweep(Core.TickCount)); + // Only reclaims memory: Hold and IsDenied both drain, so this matters on a shard that has gone + // quiet after a flood and would otherwise hold the ring until someone next connects. + _sweepTimer = Timer.DelayCall( + TimeSpan.FromMinutes(1), + TimeSpan.FromMinutes(1), + () => AutoDenylist.Drain(Core.TickCount) + ); } public void Stop() { + // Recurring, so an uncancelled sweep survives Stop and the next Start adds a second one. + _sweepTimer?.Stop(); + _sweepTimer = null; } public bool ShouldDeny(IPAddress address) => AutoDenylist.IsDenied(address); diff --git a/Projects/UOContent/Network/AutoDenylist/AutoDenylistConfiguration.cs b/Projects/UOContent/Network/AutoDenylist/AutoDenylistConfiguration.cs index 4d45da350..fd13ef003 100644 --- a/Projects/UOContent/Network/AutoDenylist/AutoDenylistConfiguration.cs +++ b/Projects/UOContent/Network/AutoDenylist/AutoDenylistConfiguration.cs @@ -21,9 +21,8 @@ using Server.Json; namespace Server.Network; /// -/// Loads the from Configuration/auto-denylist.json (matching the -/// per-feature JSON config pattern used by BlocklistConfiguration). Loaded once; a missing file writes -/// a template so operators have something to edit. +/// Loads the from Configuration/auto-denylist.json. Loaded once; +/// a missing file writes a template so operators have something to edit. /// public static class AutoDenylistConfiguration { @@ -76,6 +75,14 @@ public record AutoDenylistSettings /// stops it becoming the exhaustion it prevents. At the cap new addresses are not tracked, but are still /// disconnected by whichever gate detected them. /// + /// + /// A HashSet capacity, not a round number. Grown from empty it steps 36,353 → 75,431 → 156,437 + /// → 324,449, so this fills one exactly instead of stranding slots: 65,536 sat just past a resize and + /// left 9,895 of them unusable. Sized to cover the 50k–250k distinct-source floods seen in practice, + /// for ~19 MB — 36 bytes a set slot plus 24 for the ring record. Raising it is bounded by memory + /// rather than by a scan, since holds are retired from the ring in expiry order; a flood past it wants + /// upstream scrubbing rather than a larger cap. + /// [JsonPropertyName("maxEntries")] - public int MaxEntries { get; set; } = 65536; + public int MaxEntries { get; set; } = 324_449; } diff --git a/Projects/UOContent/Network/BanExemptions.cs b/Projects/UOContent/Network/BanExemptions.cs index 5566bd5e7..c5f6b726a 100644 --- a/Projects/UOContent/Network/BanExemptions.cs +++ b/Projects/UOContent/Network/BanExemptions.cs @@ -20,7 +20,7 @@ using Server.Network.Bans; namespace Server.Network; /// -/// Combines and into the one answer +/// Combines and into the one answer /// asks for, so neither source has to know about the other. /// public static class BanExemptions @@ -51,7 +51,7 @@ public static class BanExemptions } // Deliberate and unconditional, so it wins and must not spend the earned list's strikes. - if (FileAllowlist.Contains(address)) + if (ManualAllowlist.Contains(address)) { return true; } diff --git a/Projects/UOContent/Network/Blocklist/BlocklistConfiguration.cs b/Projects/UOContent/Network/Blocklist/BlocklistConfiguration.cs index 8a2df5ca6..19541636f 100644 --- a/Projects/UOContent/Network/Blocklist/BlocklistConfiguration.cs +++ b/Projects/UOContent/Network/Blocklist/BlocklistConfiguration.cs @@ -21,9 +21,8 @@ using Server.Json; namespace Server.Network.Bans; /// -/// Loads the from Configuration/blocklist.json (matching the -/// per-feature JSON config pattern used by AssistantConfiguration). Loaded once; a missing file -/// writes a template so operators have something to edit. +/// Loads the from Configuration/blocklist.json. Loaded once; a +/// missing file writes a template so operators have something to edit. /// public static class BlocklistConfiguration { @@ -53,12 +52,19 @@ public static class BlocklistConfiguration } /// -/// Bound configuration for . The filter is inert unless -/// points at a list that actually exists, so the shipped defaults are safe on a shard that never runs -/// the generator. +/// Bound configuration for . The filter is inert unless +/// is set and points at a list that exists, so a shard that never runs the generator +/// pays nothing for the defaults. /// public record BlocklistSettings { + /// + /// Whether the accept-path gate runs at all. Off by default: the reload poll runs for the whole + /// uptime, which no shard should pay before an operator has chosen to run a blocklist. + /// + [JsonPropertyName("enabled")] + public bool Enabled { get; set; } + /// /// Path to the blocklist. A relative path resolves against ; an /// absolute path is used as-is (handy when several shards share one generated list). Set to @@ -67,19 +73,6 @@ public record BlocklistSettings [JsonPropertyName("file")] public string File { get; set; } = "Configuration/ip-blocklist.txt"; - /// - /// Addresses that must never be blocked and never escalated, in the blocklist's own format. The same - /// files tools/Export-IpBlocklist.ps1 subtracts at generation time; the shard reads them so an - /// entry also suppresses ban contributions, which the generator alone cannot do. See - /// . - /// - /// - /// The filename may contain wildcards, which is how the default picks up a carve-out an admin adds - /// without anyone editing this file. - /// - [JsonPropertyName("allowlistFiles")] - public string[] AllowlistFiles { get; set; } = ["Configuration/ip-allowlist*.txt"]; - /// How often the file is checked for changes. Reloads only happen when it actually changed. [JsonPropertyName("reloadInterval")] public TimeSpan ReloadInterval { get; set; } = TimeSpan.FromSeconds(60); diff --git a/Projects/UOContent/Network/Blocklist/BlocklistFilter.cs b/Projects/UOContent/Network/Blocklist/BlocklistFilter.cs index 36354160b..030997803 100644 --- a/Projects/UOContent/Network/Blocklist/BlocklistFilter.cs +++ b/Projects/UOContent/Network/Blocklist/BlocklistFilter.cs @@ -25,8 +25,8 @@ namespace Server.Network.Bans; /// /// Accept-path gate for a large, file-sourced IP blocklist, hydrated from the file a generator /// (tools/Export-IpBlocklist.ps1) writes on a schedule. Holds an immutable snapshot swapped -/// atomically by an off-loop reload poll, so accept-path reads are lock-free. Inert when no file is -/// configured or present. +/// atomically by an off-loop reload poll, so accept-path reads are lock-free. Opt-in via +/// blocklist.json's enabled; inert when off, or when no file is configured or present. /// /// /// This is the demand-paging half of the design: an OS firewall cannot hold millions of entries on @@ -38,12 +38,13 @@ public sealed class BlocklistFilter : IConnectionFilter { private static readonly ILogger logger = LogFactory.GetLogger(typeof(BlocklistFilter)); - // Written by the reload poll (off-loop), read by the accept path (game loop): a single volatile - // reference swap is the whole synchronization story — readers see the old or the new snapshot, whole. + // Written by the reload poll (off-loop), read by the accept path (game loop). One volatile reference + // swap is the whole synchronization story: readers see the old or the new snapshot, whole. private volatile BlocklistSnapshot _snapshot = BlocklistSnapshot.Empty; private readonly PromotedGuard _guard = new(); + private bool _enabled; private string _path; private TimeSpan _interval; private bool _reportHits; @@ -52,6 +53,7 @@ public sealed class BlocklistFilter : IConnectionFilter private string _lastGenerated; private DateTime _lastWriteUtc; private CancellationTokenSource _cts; + private Timer _sweepTimer; public string Name => "blocklist"; @@ -68,6 +70,7 @@ public sealed class BlocklistFilter : IConnectionFilter var s = BlocklistConfiguration.Settings; _path = ResolvePath(s.File); + _enabled = s.Enabled && _path != null; _interval = s.ReloadInterval <= TimeSpan.Zero ? TimeSpan.FromSeconds(60) : s.ReloadInterval; _reportHits = s.ReportHits; _banDuration = s.BanDuration; @@ -91,16 +94,26 @@ public sealed class BlocklistFilter : IConnectionFilter public void Start(CancellationToken token) { - if (_path == null) + if (!_enabled) { - logger.Information("Blocklist disabled (\"file\" empty in blocklist.json)"); + LogWhyDisabled(); return; } + // The operator's override on this gate, opted into separately. Without it only the generator's + // subtraction covers carve-outs, and that does not cover ban contributions. + if (!ManualAllowlist.Enabled) + { + logger.Warning( + "Blocklist is on but the manual allowlist is not; set \"enabled\" in ip-allowlist.json so a " + + "carve-out also suppresses ban contributions" + ); + } + _cts = CancellationTokenSource.CreateLinkedTokenSource(token); - // A missing file is the shipped default, not an error: the gate stays inert until the poll picks - // up whatever the generator first writes. No restart needed. + // A missing file is not an error: the gate stays inert until the poll picks up whatever the + // generator first writes. No restart needed. if (File.Exists(_path)) { Reload(); // synchronous prime; empty on failure (fail-open) @@ -110,17 +123,47 @@ public sealed class BlocklistFilter : IConnectionFilter logger.Information("Blocklist inert: no list at \"{Path}\"; polling every {Interval}", _path, _interval); } - // Sweep the promote-guard so a distinct-IP flood cannot grow it unbounded. - Timer.DelayCall(TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(1), SweepGuard); + // Sweep the promote-guard so a distinct-IP flood cannot grow it unbounded. Only marked when hits + // are reported, so there is nothing to sweep otherwise. + if (_reportHits) + { + _sweepTimer = Timer.DelayCall(TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(1), SweepGuard); + } _ = Task.Run(() => PollLoop(_cts.Token), _cts.Token); } + private void LogWhyDisabled() + { + if (_path == null) + { + logger.Information("Blocklist disabled (\"file\" empty in blocklist.json)"); + } + else if (File.Exists(_path)) + { + // An upgraded shard has a list on disk but no "enabled" key, so say so rather than silently + // dropping a gate it was relying on. + logger.Warning( + "Blocklist is off (\"enabled\" false in blocklist.json) but a list is present at \"{Path}\"; " + + "no addresses will be denied", + _path + ); + } + else + { + logger.Information("Blocklist disabled (\"enabled\" false in blocklist.json)"); + } + } + public void Stop() { _cts?.Cancel(); _cts?.Dispose(); _cts = null; + + // Recurring, so an uncancelled sweep survives Stop and the next Start adds a second one. + _sweepTimer?.Stop(); + _sweepTimer = null; } public bool ShouldDeny(IPAddress address) @@ -153,9 +196,9 @@ public sealed class BlocklistFilter : IConnectionFilter } // Both are asked only once the list has matched, so they cost the common accept nothing. The file - // list is usually redundant because the generator subtracts it — except right after an operator adds - // an entry without regenerating, which is exactly when someone is waiting to get back in. - if (FileAllowlist.Contains(address)) + // list is usually redundant because the generator subtracts it — except right after an operator + // adds an entry without regenerating, which is when someone is waiting to get back in. + if (ManualAllowlist.Contains(address)) { return false; } @@ -248,9 +291,8 @@ public sealed class BlocklistFilter : IConnectionFilter private void Reload() { - // Capture the mtime/header BEFORE Load() so the markers describe the version being parsed, not - // one the producer swapped in mid-parse. Stale markers only cost an extra reload next poll; - // capturing after could skip a version entirely. + // Capture the mtime/header BEFORE Load() so they describe the version being parsed. Capturing + // after could skip a version the producer swapped in mid-parse; stale markers only cost a reload. var writeUtc = default(DateTime); try { diff --git a/Projects/UOContent/Network/Blocklist/BlocklistSnapshot.cs b/Projects/UOContent/Network/Blocklist/BlocklistSnapshot.cs index 29b2e5176..d62df0752 100644 --- a/Projects/UOContent/Network/Blocklist/BlocklistSnapshot.cs +++ b/Projects/UOContent/Network/Blocklist/BlocklistSnapshot.cs @@ -46,8 +46,7 @@ public sealed class BlocklistSnapshot /// Parses a blocklist directly from its UTF-8/ASCII file bytes — one line at a time, splitting on /// '\n' with no per-line string allocation. IPv4 singles and CIDRs are parsed straight from the /// byte span; IPv6 (the rare path) decodes the single address token and defers to the framework parser. - /// Malformed lines increment and never throw. Build-time intermediates use - /// the multithreaded pool because this runs off the game loop on the reload/bootstrap thread. + /// Malformed lines increment and never throw. /// public static BlocklistSnapshot Build(ReadOnlySpan data, out int parsed, out int skipped) { @@ -183,7 +182,7 @@ public sealed class BlocklistSnapshot } /// - /// Plain set membership, for callers whose set is an ALLOWlist (see ) and for + /// Plain set membership, for callers whose set is an ALLOWlist (see ) and for /// whom would read backwards. The interval machinery is direction-agnostic. /// public bool Contains(IPAddress ip) => IsBanned(ip); diff --git a/Projects/UOContent/Network/Blocklist/PromotedGuard.cs b/Projects/UOContent/Network/Blocklist/PromotedGuard.cs index 4f820b16e..bba8e3ee5 100644 --- a/Projects/UOContent/Network/Blocklist/PromotedGuard.cs +++ b/Projects/UOContent/Network/Blocklist/PromotedGuard.cs @@ -39,17 +39,13 @@ public sealed class PromotedGuard { return; } - using var dead = Collections.PooledRefQueue.Create(); + foreach (var (ip, exp) in _expiry) { if (exp - nowTicks <= 0) { - dead.Enqueue(ip); + _expiry.Remove(ip); } } - while (dead.Count > 0) - { - _expiry.Remove(dead.Dequeue()); - } } } diff --git a/Projects/UOContent/Network/CrowdSec/CrowdSecConfiguration.cs b/Projects/UOContent/Network/CrowdSec/CrowdSecConfiguration.cs index b1376b6a0..5855633f9 100644 --- a/Projects/UOContent/Network/CrowdSec/CrowdSecConfiguration.cs +++ b/Projects/UOContent/Network/CrowdSec/CrowdSecConfiguration.cs @@ -21,9 +21,8 @@ using Server.Json; namespace Server.Network.Bans.CrowdSec; /// -/// Loads the from Configuration/crowdsec.json (matching the -/// per-feature JSON config pattern used by AssistantConfiguration). Loaded once; a missing file -/// writes a disabled-by-default template so operators have something to edit. +/// Loads the from Configuration/crowdsec.json. Loaded once; a +/// missing file writes a disabled-by-default template so operators have something to edit. /// public static class CrowdSecConfiguration { diff --git a/Projects/UOContent/Network/CrowdSec/CrowdSecReporter.cs b/Projects/UOContent/Network/CrowdSec/CrowdSecReporter.cs index 385534d2a..c900eb90f 100644 --- a/Projects/UOContent/Network/CrowdSec/CrowdSecReporter.cs +++ b/Projects/UOContent/Network/CrowdSec/CrowdSecReporter.cs @@ -304,12 +304,10 @@ public sealed class CrowdSecReporter : IBanReporter } /// - /// Sends with up to 3 attempts total (1 initial + 2 retries), backing off 1s then 2s between - /// attempts, for transient LAPI failures (network blips, 5xx). Backoff uses - /// so it never blocks the thread; a cancellation during backoff propagates as - /// so the drain loop exits cleanly. Returns false (never - /// throws for a send failure) once attempts are exhausted, so the caller can count the drop and keep - /// draining instead of losing the rest of the batch/queue. + /// Up to 3 attempts (1 initial + 2 retries) backing off 1s then 2s, for transient LAPI failures + /// (network blips, 5xx). Backoff uses so it never blocks the thread, and a + /// cancellation during it propagates so the drain loop exits cleanly. Returns false rather than + /// throwing once attempts are exhausted, so the caller counts the drop and keeps draining. /// private static async ValueTask SendWithBoundedRetryAsync(Func send, CancellationToken token) { diff --git a/Projects/UOContent/Network/Firewall/Firewall.cs b/Projects/UOContent/Network/Firewall/Firewall.cs index 075b47ec3..36c11bbf9 100644 --- a/Projects/UOContent/Network/Firewall/Firewall.cs +++ b/Projects/UOContent/Network/Firewall/Firewall.cs @@ -28,10 +28,10 @@ namespace Server.Network; public static class Firewall { // Single-threaded: the accept path, admin gump/command, TTL expiry timer, and boot load all run on - // the main game loop. No locks, caches, or version counters are needed. See the ban-channel design doc. - // _entries is the authoritative store (gump/persistence/TTL/command all work against it); _index is a - // derived, rebuild-on-demand SortedRangeIndex used only for the accept-path IsBlocked lookup, shared - // with the same sorted-range binary-search primitive the blocklist uses (see BlocklistSnapshot). + // the main game loop, so no locks, caches, or version counters are needed. _entries is the + // authoritative store; _index is a derived, rebuild-on-demand SortedRangeIndex used only for the + // accept-path IsBlocked lookup, over the same primitive the blocklist uses (see BlocklistSnapshot). + // See dev-docs/ip-bans-and-allowlists.md. private static readonly List _entries = []; // Entries with a TTL: entry -> absolute expiry tick (Core.TickCount). Permanent entries are absent. @@ -152,7 +152,7 @@ public static class Firewall } /// - /// Removes every entry whose TTL has elapsed. Called from the main-thread maintenance timer (Task 2). + /// Removes every entry whose TTL has elapsed. Called from the main-thread maintenance timer. /// internal static void ExpireEntries(long nowTicks) { diff --git a/Projects/UOContent/Network/GameServer.cs b/Projects/UOContent/Network/GameServer.cs index 9fc170d69..b9de09515 100644 --- a/Projects/UOContent/Network/GameServer.cs +++ b/Projects/UOContent/Network/GameServer.cs @@ -6,13 +6,21 @@ public static partial class GameServer { public class GameLoginEventArgs { - public GameLoginEventArgs(NetState state, string un, string pw) + public GameLoginEventArgs(NetState state, string un, string pw, bool preAuthenticated) { State = state; Username = un; Password = pw; + PreAuthenticated = preAuthenticated; } + /// + /// The auth id presented on this game login was issued to this account, from this address, + /// after the account login packet verified the password. Read-only so a subscriber cannot + /// grant itself the skip. + /// + public bool PreAuthenticated { get; } + public NetState State { get; } public string Username { get; } diff --git a/Projects/UOContent/Network/LoginAllowlist/LoginAllowlist.cs b/Projects/UOContent/Network/LoginAllowlist/LoginAllowlist.cs index ed65b5ec6..23075f7ff 100644 --- a/Projects/UOContent/Network/LoginAllowlist/LoginAllowlist.cs +++ b/Projects/UOContent/Network/LoginAllowlist/LoginAllowlist.cs @@ -19,8 +19,8 @@ using System.Globalization; using System.IO; using System.Net; using System.Text; +using System.Threading; using System.Threading.Tasks; -using Server.Collections; using Server.Logging; using Server.Network.Bans; @@ -31,16 +31,11 @@ namespace Server.Network; /// blocked and a flaky connection cannot get one globally banned. /// /// -/// -/// Consulted only after the blocklist has already matched, and again before a ban is contributed, so a -/// normal accept pays nothing for it. An entry is evidence rather than a licence: enough strikes inside the -/// window revokes it. It cannot bootstrap, so it hedges stable addresses and does not replace -/// . See dev-docs/ip-bans-and-allowlists.md. -/// -/// -/// Both dictionaries are game-loop state. Only the file write runs off-loop, over a snapshot taken on the -/// loop. -/// +/// Consulted only after the blocklist has already matched, so a normal accept pays nothing for it. An entry +/// is evidence rather than a licence: enough strikes inside the window revokes it. It cannot bootstrap, so +/// it does not replace . Both dictionaries are game-loop state; only the file +/// write runs off-loop, over a snapshot taken on the loop. +/// See dev-docs/ip-bans-and-allowlists.md. /// public static class LoginAllowlist { @@ -53,6 +48,11 @@ public static class LoginAllowlist // _allowed and cannot be grown by an attacker. private static readonly Dictionary _strikes = []; + // Reused: past ~5,300 entries a fresh UInt128[] is an LOH allocation, once per flush. Grown + // geometrically, never shrunk. + private static UInt128[] _addressBuffer = []; + private static long[] _stampBuffer = []; + private static bool _enabled; private static string _path; private static long _ttlSeconds; @@ -60,6 +60,10 @@ public static class LoginAllowlist private static long _strikeWindowSeconds; private static bool _dirty; + // Loop-only. The writer owns the buffers until it posts completion back, so a flush landing mid-write + // waits rather than overwriting them. + private static bool _writing; + public static int Count => _allowed.Count; private struct Strike @@ -98,10 +102,14 @@ public static class LoginAllowlist var interval = LoginAllowlistConfiguration.Settings.FlushInterval; if (interval <= TimeSpan.Zero) { - interval = TimeSpan.FromMinutes(1); + interval = TimeSpan.FromHours(1); } Timer.DelayCall(interval, interval, Flush); + + // HandleClosed skips InvokeShutdown when the server crashed, so the crash path needs its own. + EventSink.Shutdown += OnShutdown; + EventSink.ServerCrashed += OnCrashed; } /// @@ -198,6 +206,8 @@ public static class LoginAllowlist { _allowed.Clear(); _strikes.Clear(); + _writing = false; + _dirty = false; _enabled = enabled; _ttlSeconds = ttlSeconds; _escalateAfterStrikes = escalateAfterStrikes; @@ -209,57 +219,105 @@ public static class LoginAllowlist private static void Flush() { - if (!_enabled || !_dirty) - { - return; - } - // A save owns the disk and nothing here is urgent. _dirty stays set, so skipping loses nothing. // See the threading policy in CLAUDE.md (rules #3 and #10). - if (World.Saving || World.WorldState == WorldState.PendingSave) + if (!_enabled || !_dirty || _writing || World.Saving || World.WorldState == WorldState.PendingSave) { return; } + var count = Snapshot(out var dropped); + var addresses = _addressBuffer; + var stamps = _stampBuffer; + var path = _path; + + _dirty = false; + _writing = true; + + _ = Task.Run( + () => + { + var written = Write(path, addresses, stamps, count, dropped); + + // _writing and _dirty are loop state, so the writer hands the release back. Rule #10. + Core.LoopContext.Post( + () => + { + _writing = false; + if (!written) + { + _dirty = true; // nothing reached disk; the next flush retries + } + } + ); + } + ); + } + + /// + /// A crash is the case the flush interval cannot cover, so write on the way down. Runs on whichever + /// thread faulted, and the dictionaries are loop state, so it only writes when that is the loop. + /// + private static void OnCrashed(ServerCrashedEventArgs e) + { + if (Thread.CurrentThread == Core.Thread) + { + OnShutdown(); + } + } + + /// Synchronous: nothing schedules after this, so a handed-off write would reach no disk. + private static void OnShutdown() + { + // A write already in flight holds the buffers and has all but the last moments of the list. + if (!_enabled || !_dirty || _writing) + { + return; + } + + var count = Snapshot(out var dropped); + _dirty = false; + + Write(_path, _addressBuffer, _stampBuffer, count, dropped); + } + + /// + /// Prunes expired entries and copies what survives into the shared buffers. Returns the live count; the + /// buffers run longer and everything past it is stale. + /// + private static int Snapshot(out int dropped) + { var nowUnix = ToUnixSeconds(Core.Now); var cutoff = nowUnix - _ttlSeconds; - // Prune and snapshot in one loop-side pass; the writer only sees private copies. Not pooled: - // STArrayPool is single-threaded and these escape to another thread. - var addresses = new UInt128[_allowed.Count]; - var stamps = new long[_allowed.Count]; - var count = 0; + if (_addressBuffer.Length < _allowed.Count) + { + // Geometric so a shard adding addresses one at a time does not reallocate every flush. + var size = Math.Max(_allowed.Count, Math.Max(64, _addressBuffer.Length * 2)); + _addressBuffer = new UInt128[size]; + _stampBuffer = new long[size]; + } - using var expired = new PooledRefList(16); + var count = 0; + dropped = 0; foreach (var (address, stamp) in _allowed) { if (stamp < cutoff) { - expired.Add(address); + _allowed.Remove(address); + _strikes.Remove(address); + dropped++; continue; } - addresses[count] = address; - stamps[count] = stamp; + _addressBuffer[count] = address; + _stampBuffer[count] = stamp; count++; } - for (var i = 0; i < expired.Count; i++) - { - _allowed.Remove(expired[i]); - _strikes.Remove(expired[i]); - } - PruneStaleStrikes(nowUnix); - - _dirty = false; - - var path = _path; - var total = count; - var dropped = expired.Count; - - _ = Task.Run(() => Write(path, addresses, stamps, total, dropped)); + return count; } /// Drops tallies whose window has closed. @@ -270,23 +328,17 @@ public static class LoginAllowlist return; } - using var stale = new PooledRefList(16); - foreach (var (address, strike) in _strikes) { if (nowUnix - strike.WindowStart > _strikeWindowSeconds) { - stale.Add(address); + _strikes.Remove(address); } } - - for (var i = 0; i < stale.Count; i++) - { - _strikes.Remove(stale[i]); - } } - private static void Write(string path, UInt128[] addresses, long[] stamps, int count, int dropped) + /// Writes the list out. Returns false when nothing reached disk, so the caller can retry. + private static bool Write(string path, UInt128[] addresses, long[] stamps, int count, int dropped) { try { @@ -322,11 +374,14 @@ public static class LoginAllowlist { logger.Information("Login allowlist wrote {Count} entr(ies), dropped {Dropped} past TTL", count, dropped); } + + return true; } catch (Exception e) { // Recoverable: entries are still in memory and the next flush retries. logger.Warning(e, "Could not write the login allowlist to \"{Path}\"", path); + return false; } } diff --git a/Projects/UOContent/Network/LoginAllowlist/LoginAllowlistConfiguration.cs b/Projects/UOContent/Network/LoginAllowlist/LoginAllowlistConfiguration.cs index b3242a85f..5c673eb10 100644 --- a/Projects/UOContent/Network/LoginAllowlist/LoginAllowlistConfiguration.cs +++ b/Projects/UOContent/Network/LoginAllowlist/LoginAllowlistConfiguration.cs @@ -21,9 +21,8 @@ using Server.Json; namespace Server.Network; /// -/// Loads the from Configuration/login-allowlist.json (matching -/// the per-feature JSON config pattern used by BlocklistConfiguration). Loaded once; a missing file -/// writes a template so operators have something to edit. +/// Loads the from Configuration/login-allowlist.json. Loaded +/// once; a missing file writes a template so operators have something to edit. /// public static class LoginAllowlistConfiguration { @@ -82,18 +81,19 @@ public record LoginAllowlistSettings public TimeSpan Ttl { get; set; } = TimeSpan.FromDays(90); /// - /// How often a changed list is written out. A crash loses at most this much, and an entry is re-earned by - /// the next login. + /// How often a changed list is written out. A clean shutdown always writes, so this only bounds what a + /// crash loses — and an entry is re-earned by the next login. Hourly against a 90-day TTL, because each + /// flush walks the whole list on the game loop. /// [JsonPropertyName("flushInterval")] - public TimeSpan FlushInterval { get; set; } = TimeSpan.FromMinutes(1); + public TimeSpan FlushInterval { get; set; } = TimeSpan.FromHours(1); /// /// How many suppressed contributions inside revoke an address's entry. Past /// this it escalates like anything else until it earns a new entry by logging in again. /// /// - /// Generous on purpose: local defences never stop applying, so a high threshold only delays the external + /// Generous on purpose: local defenses never stop applying, so a high threshold only delays the external /// ban. A bad line might trip a gate a few times an hour; a host being used to flood burns through this /// in seconds. Set to 0 to never revoke. /// diff --git a/Projects/UOContent/Network/Blocklist/FileAllowlist.cs b/Projects/UOContent/Network/ManualAllowlist/ManualAllowlist.cs similarity index 83% rename from Projects/UOContent/Network/Blocklist/FileAllowlist.cs rename to Projects/UOContent/Network/ManualAllowlist/ManualAllowlist.cs index 0d5582713..20e351457 100644 --- a/Projects/UOContent/Network/Blocklist/FileAllowlist.cs +++ b/Projects/UOContent/Network/ManualAllowlist/ManualAllowlist.cs @@ -2,7 +2,7 @@ * ModernUO * * Copyright 2019-2026 - ModernUO Development Team * * Email: hi@modernuo.com * - * File: FileAllowlist.cs * + * File: ManualAllowlist.cs * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -32,15 +32,15 @@ namespace Server.Network.Bans; /// Behavioural detections never consult the blocklist, so without reading the files here a carve-out is /// quietly routed around: one scanner behind a shared CGNAT address is enough to get the whole address /// contributed and firewalled. Reading them also means an entry applies on the next reload rather than the -/// next regeneration. Unconditional, unlike , but still no shield against a -/// manual ban — see . +/// next regeneration. Opt-in via ip-allowlist.json's enabled, since the poll runs for the +/// whole uptime; no shield against a manual ban either — see . /// -public static class FileAllowlist +public static class ManualAllowlist { - private static readonly ILogger logger = LogFactory.GetLogger(typeof(FileAllowlist)); + private static readonly ILogger logger = LogFactory.GetLogger(typeof(ManualAllowlist)); - // Written by the reload poll (off-loop), read by the accept path (game loop): a single volatile - // reference swap is the whole synchronization story — readers see the old or the new snapshot, whole. + // Written by the reload poll (off-loop), read by the accept path (game loop). One volatile reference + // swap is the whole synchronization story: readers see the old or the new snapshot, whole. private static volatile BlocklistSnapshot _snapshot = BlocklistSnapshot.Empty; private static string[] _patterns = []; @@ -53,24 +53,51 @@ public static class FileAllowlist /// True when an operator listed this address. Safe before . public static bool Contains(IPAddress address) => address != null && _snapshot.Contains(address); + /// True when the shard is reading allowlist files. Safe before . + public static bool Enabled { get; private set; } + public static void Initialize() { - // BlocklistFilter.Register ran during the Configure sweep, so the settings are populated. - var settings = BlocklistConfiguration.Settings; + ManualAllowlistConfiguration.Load(); + var settings = ManualAllowlistConfiguration.Settings; if (settings == null) { return; } - _patterns = ResolvePaths(settings.AllowlistFiles); + _patterns = ResolvePaths(settings.Files); _interval = settings.ReloadInterval <= TimeSpan.Zero ? TimeSpan.FromSeconds(60) : settings.ReloadInterval; - if (_patterns.Length == 0) + if (!settings.Enabled) { - logger.Information("File allowlist disabled (\"allowlistFiles\" empty in blocklist.json)"); + // The generator still subtracts these files, so a carve-out an operator already wrote looks + // like it works right up until a behavioural detection contributes the address anyway. + var present = ExpandPaths().Length; + _patterns = []; + + if (present > 0) + { + logger.Warning( + "Manual allowlist is off (\"enabled\" false in ip-allowlist.json) but {Count} allowlist file(s) " + + "are present; those carve-outs will not suppress ban contributions", + present + ); + } + else + { + logger.Information("Manual allowlist disabled (\"enabled\" false in ip-allowlist.json)"); + } + return; } + if (_patterns.Length == 0) + { + logger.Information("Manual allowlist disabled (\"files\" empty in ip-allowlist.json)"); + return; + } + + Enabled = true; Reload(); _cts = CancellationTokenSource.CreateLinkedTokenSource(Core.ClosingTokenSource.Token); @@ -197,7 +224,7 @@ public static class FileAllowlist } catch (Exception e) { - logger.Warning(e, "File allowlist reload check failed; keeping last snapshot ({Count})", Count); + logger.Warning(e, "Manual allowlist reload check failed; keeping last snapshot ({Count})", Count); } } } @@ -246,7 +273,7 @@ public static class FileAllowlist _lastStamp = stamp; logger.Information( - "File allowlist loaded {Count} range(s) from {Files} file(s)", + "Manual allowlist loaded {Count} range(s) from {Files} file(s)", next.Count, files ); diff --git a/Projects/UOContent/Network/ManualAllowlist/ManualAllowlistConfiguration.cs b/Projects/UOContent/Network/ManualAllowlist/ManualAllowlistConfiguration.cs new file mode 100644 index 000000000..dd21fe97c --- /dev/null +++ b/Projects/UOContent/Network/ManualAllowlist/ManualAllowlistConfiguration.cs @@ -0,0 +1,83 @@ +/************************************************************************* + * ModernUO * + * Copyright 2019-2026 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: ManualAllowlistConfiguration.cs * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 3 of the License, or * + * (at your option) any later version. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + *************************************************************************/ + +using System; +using System.IO; +using System.Text.Json.Serialization; +using Server.Json; + +namespace Server.Network.Bans; + +/// +/// Loads the from Configuration/ip-allowlist.json. Loaded once; +/// a missing file writes a template so operators have something to edit. +/// +public static class ManualAllowlistConfiguration +{ + private const string _path = "Configuration/ip-allowlist.json"; + + public static ManualAllowlistSettings Settings { get; private set; } + + public static void Load() + { + var path = Path.Join(Core.BaseDirectory, _path); + + if (File.Exists(path)) + { + Settings = JsonConfig.Deserialize(path); + } + else + { + Settings = new ManualAllowlistSettings(); + Save(); + } + } + + private static void Save() + { + JsonConfig.Serialize(Path.Join(Core.BaseDirectory, _path), Settings); + } +} + +/// +/// Bound configuration for . Its own file rather than a corner of +/// blocklist.json: the blocklist is only one of two consumers, and the other +/// () works on a shard that runs no blocklist at all. +/// +public record ManualAllowlistSettings +{ + /// + /// Whether the shard reads at all. Off by default: reading them costs a poll for + /// the whole uptime, which no shard should pay before an operator has written a carve-out. + /// + [JsonPropertyName("enabled")] + public bool Enabled { get; set; } + + /// + /// Addresses that must never be blocked and never escalated, in the blocklist's own format. The same + /// files tools/Export-IpBlocklist.ps1 subtracts at generation time; the shard reads them so an + /// entry also suppresses ban contributions, which the generator alone cannot do. + /// + /// + /// The filename may contain wildcards, which is how the default picks up a carve-out an admin adds + /// without anyone editing this file. + /// + [JsonPropertyName("files")] + public string[] Files { get; set; } = ["Configuration/ip-allowlist*.txt"]; + + /// How often the files are checked for changes. Reloads only happen when one actually changed. + [JsonPropertyName("reloadInterval")] + public TimeSpan ReloadInterval { get; set; } = TimeSpan.FromSeconds(60); +} diff --git a/Projects/UOContent/Network/Packets/IncomingAccountPackets.cs b/Projects/UOContent/Network/Packets/IncomingAccountPackets.cs index 07a444d03..792ed880b 100644 --- a/Projects/UOContent/Network/Packets/IncomingAccountPackets.cs +++ b/Projects/UOContent/Network/Packets/IncomingAccountPackets.cs @@ -17,6 +17,9 @@ using System; using System.Buffers; using System.Collections.Generic; using System.IO; +using System.Net; +using System.Security.Cryptography; +using Server.Accounting; using Server.Engines.CharacterCreation; using Server.Misc; using Server.Mobiles; @@ -25,7 +28,16 @@ namespace Server.Network; public static class IncomingAccountPackets { + // Initial capacity and the point at which issuing sweeps expired ids. Not a cap; the window + // grows rather than evicting a live id. private const int _authIDWindowSize = 128; + + private static int _authIdPurgeThreshold = _authIDWindowSize; + + // The gap between PlayServerAck and the game login is seconds. Bounds how long a stolen id + // stays usable. + private static readonly TimeSpan _authIDLifetime = TimeSpan.FromMinutes(2.0); + private static readonly Dictionary _authIDWindow = new(_authIDWindowSize); @@ -34,13 +46,33 @@ public static class IncomingAccountPackets public DateTime Age; public readonly ClientVersion Version; - public AuthIDPersistence(ClientVersion v) + // GameLogin skips its password verify when both match, so the id is a bearer token and has + // to be bound to whatever earned it. + public readonly IAccount Account; + public readonly IPAddress Address; + + public AuthIDPersistence(ClientVersion v, IAccount account, IPAddress address) { Age = Core.Now; Version = v; + Account = account; + Address = Utility.Intern(address); } } + internal enum AuthIdResult + { + // No such id, or it was issued for a different account or address. + Rejected, + + // Right account and address, too old to stand in for the verify. Idling on the server list + // is normal, so this falls back to the password check rather than becoming a lockout. + Expired, + + // Issued to this account, from this address, recently. Stands in for the password verify. + Vouched + } + public static unsafe void Configure() { IncomingPackets.Register(0x00, &CreateCharacter, 104, outgameOnly: true); @@ -312,42 +344,92 @@ public static class IncomingAccountPackets } } - private static int GenerateAuthID(this NetState state) + private static int GenerateAuthID(this NetState state) => + EnsureAuthId(state.AuthId, state.Account, state.Address, state.Version); + + /// + /// One id per connection, by construction. Choosing a server queues a disconnect that is not + /// drained until the next slice, so a client pipelining another select into the same buffer + /// arrives here again; handing back the id it already holds cannot orphan one. + /// + internal static int EnsureAuthId(int existingAuthId, IAccount account, IPAddress address, ClientVersion version) + => existingAuthId != 0 ? existingAuthId : RegisterAuthId(account, address, version); + + internal static int RegisterAuthId(IAccount account, IPAddress address, ClientVersion version) { - if (_authIDWindow.Count == _authIDWindowSize) + // Sweep the ids left behind by clients that picked a server and never arrived, but never + // evict a live one to make room -- the client holding it is on its way to redeem it. If all + // are live the window grows, which is a login rush, not a backlog. Each entry costs a + // successful password verify, so the size is self-limiting. + if (_authIDWindow.Count >= _authIdPurgeThreshold) { - var oldestID = 0; - var oldest = DateTime.MaxValue; - - foreach (var (key, authId) in _authIDWindow) - { - if (authId.Age < oldest) - { - oldestID = key; - oldest = authId.Age; - } - } - - _authIDWindow.Remove(oldestID); + PurgeExpiredAuthIds(); + _authIdPurgeThreshold = Math.Max(_authIDWindowSize, _authIDWindow.Count * 2); } int authID; + // The id stands in for a password verify, so it has to be unguessable. Zero is reserved: + // GameLogin reads state.AuthId == 0 as "no auth id was issued". do { - authID = Utility.Random(1, int.MaxValue - 1); + authID = RandomNumberGenerator.GetInt32(int.MinValue, int.MaxValue); + } while (authID == 0 || _authIDWindow.ContainsKey(authID)); - if (Utility.RandomBool()) - { - authID |= 1 << 31; - } - } while (_authIDWindow.ContainsKey(authID)); - - _authIDWindow[authID] = new AuthIDPersistence(state.Version); + _authIDWindow[authID] = new AuthIDPersistence(version, account, address); return authID; } + /// + /// Spends an auth id, but only for the account and address it was issued to. An address + /// mismatch is rather than a fallback: network switching + /// mid-login is not supported. + /// + internal static AuthIdResult ConsumeAuthId(int authId, string username, IPAddress address, out AuthIDPersistence entry) + { + if (!_authIDWindow.TryGetValue(authId, out entry)) + { + return AuthIdResult.Rejected; + } + + // Look, then take: removing before ownership is proven would let anyone landing on a live id + // burn it, leaving its owner to log in again. Address before username, so a remote guesser + // never learns whether a username matched. + if (!Utility.Intern(address).Equals(entry.Address) + || entry.Account == null || !username.InsensitiveEquals(entry.Account.Username)) + { + entry = default; + return AuthIdResult.Rejected; + } + + // Theirs, so spend it. Expired counts as spent; it has done all it is ever going to do. + _authIDWindow.Remove(authId); + + return Core.Now - entry.Age > _authIDLifetime ? AuthIdResult.Expired : AuthIdResult.Vouched; + } + + private static void PurgeExpiredAuthIds() + { + var now = Core.Now; + + foreach (var (key, entry) in _authIDWindow) + { + if (now - entry.Age > _authIDLifetime) + { + _authIDWindow.Remove(key); + } + } + } + + internal static void ClearAuthIdWindow() + { + _authIDWindow.Clear(); + _authIdPurgeThreshold = _authIDWindowSize; + } + + internal static int AuthIdWindowCount => _authIDWindow.Count; + public static void GameLogin(NetState state, SpanReader reader) { if (state.SentFirstPacket) @@ -360,12 +442,6 @@ public static class IncomingAccountPackets var authId = reader.ReadInt32(); - if (!_authIDWindow.TryGetValue(authId, out var ap)) - { - state.LogInfo("Invalid client detected, disconnecting..."); - state.Disconnect("Unable to find auth id."); - } - if (state.AuthId != 0 && authId != state.AuthId || state.AuthId == 0 && authId != state.Seed) { state.LogInfo("Invalid client detected, disconnecting..."); @@ -373,14 +449,28 @@ public static class IncomingAccountPackets return; } - _authIDWindow.Remove(authId); - state.Version = ap.Version; - state.Seeded = true; - var username = reader.ReadLatin1Safe(30); var password = reader.ReadLatin1Safe(30); - var e = new GameServer.GameLoginEventArgs(state, username, password); + var authResult = ConsumeAuthId(authId, username, state.Address, out var ap); + + if (authResult == AuthIdResult.Rejected) + { + state.LogInfo("Invalid client detected, disconnecting..."); + state.Disconnect("Unable to find auth id."); + return; + } + + state.Version = ap.Version; + state.Seeded = true; + + // Expired carries a usable entry; only the password verify skip is withheld. + var e = new GameServer.GameLoginEventArgs( + state, + username, + password, + authResult == AuthIdResult.Vouched + ); GameServer.GameServerLoginEvent(e); @@ -402,6 +492,14 @@ public static class IncomingAccountPackets public static void PlayServer(NetState state, SpanReader reader) { + // A server is picked once per connection. Picking again hands back an id this connection may + // already have spent on a game login, which the client could never redeem. + if (state.AuthId != 0) + { + state.Disconnect("Duplicate play server packet sent."); + return; + } + int index = reader.ReadInt16(); var info = state.ServerInfo; var a = state.Account; @@ -414,7 +512,7 @@ public static class IncomingAccountPackets { var si = info[index]; - state.AuthId = GenerateAuthID(state); + state.AuthId = state.GenerateAuthID(); state.SentFirstPacket = false; state.SendPlayServerAck(si, state.AuthId); @@ -423,6 +521,14 @@ public static class IncomingAccountPackets public static void LoginServerSeed(NetState state, SpanReader reader) { + // Seeding happens once per connection. A second one restarts a handshake this connection + // already completed, which no real client does. + if (state.Seeded) + { + state.Disconnect("Duplicate login server seed packet sent."); + return; + } + state.Seed = reader.ReadInt32(); state.Seeded = true; @@ -458,7 +564,22 @@ public static class IncomingAccountPackets EventSink.InvokeAccountLogin(accountLoginEventArgs); - if (accountLoginEventArgs.Accepted) + // The password check moved off the loop; whoever took it replies when the verdict lands. + if (accountLoginEventArgs.Deferred) + { + return; + } + + CompleteAccountLogin(state, accountLoginEventArgs.Accepted, accountLoginEventArgs.RejectReason); + } + + /// + /// Replies to an account login. Split out so a verdict produced off the loop reaches the client + /// through exactly the same path as one produced inline. + /// + internal static void CompleteAccountLogin(NetState state, bool accepted, ALRReason rejectReason) + { + if (accepted) { var serverListEventArgs = new GatewayServer.ServerListEventArgs(state, state.Account); @@ -478,7 +599,7 @@ public static class IncomingAccountPackets else { state.Account = null; - AccountLogin_ReplyRej(state, accountLoginEventArgs.RejectReason); + AccountLogin_ReplyRej(state, rejectReason); } } diff --git a/Projects/UOContent/Regions/BaseRegion.cs b/Projects/UOContent/Regions/BaseRegion.cs index b18c7fa18..776ec269d 100644 --- a/Projects/UOContent/Regions/BaseRegion.cs +++ b/Projects/UOContent/Regions/BaseRegion.cs @@ -113,7 +113,7 @@ public class BaseRegion : Region m_RectBuffer2.RemoveAt(k); var sz = rect.Start.Z; - var ez = rect.End.X; + var ez = rect.End.Z; if (l1 < l2) { diff --git a/Projects/UOContent/Skills/AntiMacroSystem.cs b/Projects/UOContent/Skills/AntiMacroSystem.cs index 60fcc899f..e37431ddd 100644 --- a/Projects/UOContent/Skills/AntiMacroSystem.cs +++ b/Projects/UOContent/Skills/AntiMacroSystem.cs @@ -120,23 +120,17 @@ public static class AntiMacroSystem var now = Core.Now; - using var toRemove = PooledRefQueue.Create(); foreach (var (m, antiMacro) in _antiMacroTable) { if (antiMacro._lastExpiration <= now) { - toRemove.Enqueue(m); + _antiMacroTable.Remove(m); } else { antiMacro.CleanExpired(); } } - - while (toRemove.Count > 0) - { - _antiMacroTable.Remove(toRemove.Dequeue()); - } } [OnEvent(nameof(PlayerMobile.PlayerLoginEvent))] @@ -259,20 +253,13 @@ public static class AntiMacroSystem { var now = Core.Now; - using var toRemove = PooledRefQueue<(Skill, object)>.Create(); - foreach (var (key, countAndTimeStamp) in _antiMacroTracking) { if (countAndTimeStamp._count <= 0 || countAndTimeStamp._expiration <= now) { - toRemove.Enqueue(key); + _antiMacroTracking.Remove(key); } } - - while (toRemove.Count > 0) - { - _antiMacroTracking.Remove(toRemove.Dequeue()); - } } } diff --git a/Projects/UOContent/Skills/DetectHidden.cs b/Projects/UOContent/Skills/DetectHidden.cs index b1b0a02fb..487eda693 100644 --- a/Projects/UOContent/Skills/DetectHidden.cs +++ b/Projects/UOContent/Skills/DetectHidden.cs @@ -39,20 +39,13 @@ public static class DetectHidden // Clean up old debounce entries to prevent memory bloat private static void CleanupDebounceCache(long now) { - using var entriesToRemove = PooledRefQueue<(Mobile, Mobile)>.Create(); - foreach (var entry in PassiveDetectDebounce) { if (now - entry.Value > DebounceExpiryMs) { - entriesToRemove.Enqueue(entry.Key); + PassiveDetectDebounce.Remove(entry.Key); } } - - while (entriesToRemove.Count > 0) - { - PassiveDetectDebounce.Remove(entriesToRemove.Dequeue()); - } } // For testing: clear the debounce cache to prevent cross-test contamination diff --git a/Projects/UOContent/Spells/Fifth/PoisonField.cs b/Projects/UOContent/Spells/Fifth/PoisonField.cs index 3c5d2f609..92a797f19 100644 --- a/Projects/UOContent/Spells/Fifth/PoisonField.cs +++ b/Projects/UOContent/Spells/Fifth/PoisonField.cs @@ -64,13 +64,19 @@ public class PoisonFieldSpell : MagerySpell, ITargetingSpell } [DispellableField] -[SerializationGenerator(0, false)] +[SerializationGenerator(1, false)] public partial class PoisonField : Item { + private void MigrateFrom(V0Content content) + { + _caster = content.Caster; + _end = content.End; + } + [SerializableField(0)] private Mobile _caster; - [DeltaDateTime] + [AnchoredDateTime] [SerializableField(1)] private DateTime _end; diff --git a/Projects/UOContent/Spells/Fourth/FireField.cs b/Projects/UOContent/Spells/Fourth/FireField.cs index 0b0a22129..8f20f995b 100644 --- a/Projects/UOContent/Spells/Fourth/FireField.cs +++ b/Projects/UOContent/Spells/Fourth/FireField.cs @@ -67,16 +67,23 @@ public class FireFieldSpell : MagerySpell, ITargetingSpell } [DispellableField] -[SerializationGenerator(0, false)] +[SerializationGenerator(1, false)] public partial class FireFieldItem : Item { + private void MigrateFrom(V0Content content) + { + _damage = content.Damage; + _caster = content.Caster; + _end = content.End; + } + [SerializableField(0)] private int _damage; [SerializableField(1)] private Mobile _caster; - [DeltaDateTime] + [AnchoredDateTime] [SerializableField(2)] private DateTime _end; private Timer _timer; diff --git a/Projects/UOContent/Spells/Seventh/EnergyField.cs b/Projects/UOContent/Spells/Seventh/EnergyField.cs index 5e3ea1149..dbfece36d 100644 --- a/Projects/UOContent/Spells/Seventh/EnergyField.cs +++ b/Projects/UOContent/Spells/Seventh/EnergyField.cs @@ -77,13 +77,19 @@ public class EnergyFieldSpell : MagerySpell, ITargetingSpell } [DispellableField] -[SerializationGenerator(1, false)] +[SerializationGenerator(2, false)] public partial class EnergyField : Item { + private void MigrateFrom(V1Content content) + { + _caster = content.Caster; + _end = content.End; + } + [SerializableField(0)] private Mobile _caster; - [DeltaDateTime] + [AnchoredDateTime] [SerializableField(1)] private DateTime _end; diff --git a/Projects/UOContent/Spells/Sixth/ParalyzeField.cs b/Projects/UOContent/Spells/Sixth/ParalyzeField.cs index 830f16dfe..301cee507 100644 --- a/Projects/UOContent/Spells/Sixth/ParalyzeField.cs +++ b/Projects/UOContent/Spells/Sixth/ParalyzeField.cs @@ -77,13 +77,19 @@ public class ParalyzeFieldSpell : MagerySpell, ITargetingSpell } [DispellableField] -[SerializationGenerator(0, false)] +[SerializationGenerator(1, false)] public partial class ParalyzeField : Item { + private void MigrateFrom(V0Content content) + { + _caster = content.Caster; + _end = content.End; + } + [SerializableField(0)] private Mobile _caster; - [DeltaDateTime] + [AnchoredDateTime] [SerializableField(1)] private DateTime _end; diff --git a/Projects/UOContent/Spells/Spellweaving/Items/TransientItem.cs b/Projects/UOContent/Spells/Spellweaving/Items/TransientItem.cs index 54ac52030..49894cc95 100644 --- a/Projects/UOContent/Spells/Spellweaving/Items/TransientItem.cs +++ b/Projects/UOContent/Spells/Spellweaving/Items/TransientItem.cs @@ -3,12 +3,17 @@ using ModernUO.Serialization; namespace Server.Items; -[SerializationGenerator(1, false)] +[SerializationGenerator(2, false)] public partial class TransientItem : Item { + private void MigrateFrom(V1Content content) + { + _expiration = content.Expiration; + } + private TimerExecutionToken _timerToken; - [DeltaDateTime] + [AnchoredDateTime] [SerializableField(0)] [SerializedCommandProperty(AccessLevel.GameMaster)] private DateTime _expiration; diff --git a/Projects/UOContent/Spells/Third/WallOfStone.cs b/Projects/UOContent/Spells/Third/WallOfStone.cs index 7cae3b848..1b1dd04d3 100644 --- a/Projects/UOContent/Spells/Third/WallOfStone.cs +++ b/Projects/UOContent/Spells/Third/WallOfStone.cs @@ -63,13 +63,19 @@ public class WallOfStoneSpell : MagerySpell, ITargetingSpell } [DispellableField] -[SerializationGenerator(0, false)] +[SerializationGenerator(1, false)] public partial class WallOfStone : Item { + private void MigrateFrom(V0Content content) + { + _caster = content.Caster; + _end = content.End; + } + [SerializableField(0)] private Mobile _caster; - [DeltaDateTime] + [AnchoredDateTime] [SerializableField(1)] private DateTime _end; diff --git a/Projects/UOContent/UOContent.csproj b/Projects/UOContent/UOContent.csproj index 4f230902d..60f28ac6f 100644 --- a/Projects/UOContent/UOContent.csproj +++ b/Projects/UOContent/UOContent.csproj @@ -40,19 +40,18 @@ false - - + + - + - + - - - + + diff --git a/Projects/UOContent/Utilities/Types.cs b/Projects/UOContent/Utilities/Types.cs index fa669d96a..ca5b9422d 100644 --- a/Projects/UOContent/Utilities/Types.cs +++ b/Projects/UOContent/Utilities/Types.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Concurrent; -using System.Collections.Generic; using System.Globalization; using System.Reflection; using System.Runtime.CompilerServices; diff --git a/README.md b/README.md index b61204743..01a368804 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ ModernUO [![Discord](https://img.shields.io/discord/751317910504603701?logo=disc ## Requirements #### Supported Operating Systems -[![Windows 10/11/2012/2016/2019/2022/2025](https://img.shields.io/badge/-server%202025-3c78d5?labelColor=222222&logo=data:image/svg%2bxml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHJvbGU9ImltZyIgdmlld0JveD0iMCAwIDI0IDI0Ij48dGl0bGU+V2luZG93czwvdGl0bGU+PHBhdGggZD0iTTAsMEgxMS4zNzdWMTEuMzcySDBaTTEyLjYyMywwSDI0VjExLjM3MkgxMi42MjNaTTAsMTIuNjIzSDExLjM3N1YyNEgwWm0xMi42MjMsMEgyNFYyNEgxMi42MjMiIGZpbGw9IiMzYzc4ZDUiLz48L3N2Zz4=)](https://www.microsoft.com/en-US/evalcenter/evaluate-windows-server-2022) +[![Windows 10/11/2012 R2/2016/2019/2022/2025](https://img.shields.io/badge/-server%202025-3c78d5?labelColor=222222&logo=data:image/svg%2bxml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHJvbGU9ImltZyIgdmlld0JveD0iMCAwIDI0IDI0Ij48dGl0bGU+V2luZG93czwvdGl0bGU+PHBhdGggZD0iTTAsMEgxMS4zNzdWMTEuMzcySDBaTTEyLjYyMywwSDI0VjExLjM3MkgxMi42MjNaTTAsMTIuNjIzSDExLjM3N1YyNEgwWm0xMi42MjMsMEgyNFYyNEgxMi42MjMiIGZpbGw9IiMzYzc4ZDUiLz48L3N2Zz4=)](https://www.microsoft.com/en-US/evalcenter/evaluate-windows-server-2022) ![MacOS 14+](https://img.shields.io/badge/-sonoma-222222?logo=apple&logoColor=white&labelColor=222222) [![Debian 12+](https://img.shields.io/badge/-trixie-A81D33?logo=debian&logoColor=A81D33&labelColor=222222)](https://www.debian.org/distrib/) [![Ubuntu 22+ LTS](https://img.shields.io/badge/-26LTS-E95420?logo=ubuntu&logoColor=E95420&labelColor=222222)](https://ubuntu.com/download/server) @@ -37,6 +37,17 @@ ModernUO [![Discord](https://img.shields.io/discord/751317910504603701?logo=disc ##### Windows [![VC++ Redistributable v14](https://img.shields.io/badge/-Redist%20v14-00599C?logo=cplusplus&logoColor=white&labelColor=222222)](https://aka.ms/vc14/vc_redist.x64.exe) +#### Hardware + +| Use | vCPU | RAM | Storage | +|---|---|---|---| +| Development / test | 2 **dedicated** | 2 GB | SSD | +| Small live shard (< 50 concurrent) | 4 dedicated | 4 GB | NVMe | +| Medium (50–200) | 4–8 | 8 GB | NVMe | +| Large (200+) | 8+, high clock | 16 GB+ | NVMe | + +See [dev-docs/server-requirements.md](dev-docs/server-requirements.md) for more information. + #### Development [![git](https://img.shields.io/badge/-git-F05032?logo=git&logoColor=F05032&labelColor=222222)](https://git-scm.com/downloads) [![.NET](https://img.shields.io/badge/-%2010.0.100%20SDK-5C2D91?logo=.NET&logoColor=white&labelColor=222222)](https://dotnet.microsoft.com/download/dotnet/10.0) @@ -87,18 +98,31 @@ dnf install -y dnf-plugins-core dnf config-manager --set-enabled crb dnf install -y epel-release # Prerequisites -dnf install -y findutils libicu libdeflate-devel zstd libargon2-devel +dnf install -y findutils libicu libdeflate libargon2 tzdata ``` ### Ubuntu, Debian, etc ```shell apt-get update -y -apt-get install -y libicu-dev libdeflate-dev zstd libargon2-dev +# The ICU runtime package carries the ABI version in its name (libicu74, libicu76, …) and has no +# stable alias, so match it by pattern rather than pinning a release-specific name. +apt-get install -y '^libicu[0-9]+$' libdeflate0 libargon2-1 tzdata ``` +Only the runtime libraries are needed — the `-dev`/`-devel` packages are not. Run +`./build-tool --check-prereqs` to check the current machine and print the exact packages your +release needs. + +`zstd` is not listed because ZstdNet bundles `libzstd` for every platform, and `liburing` is not +listed because IORingGroup issues `io_uring` syscalls directly. + +If the shard's configured time zone is a legacy alias such as `US/Eastern`, Debian 12 and Ubuntu +24.04 also need `tzdata-legacy`. See [Platform Prerequisites](dev-docs/platform-prerequisites.md) +for what each dependency is for and what breaks without it. + ## OSX Requirements ```shell -brew install icu4c libdeflate zstd argon2 +brew install icu4c libdeflate argon2 ``` ## Running the Server diff --git a/dev-docs/claude-skills/modernuo-code-audit.md b/dev-docs/claude-skills/modernuo-code-audit.md index 5d5af15bb..d805b0578 100644 --- a/dev-docs/claude-skills/modernuo-code-audit.md +++ b/dev-docs/claude-skills/modernuo-code-audit.md @@ -200,8 +200,27 @@ mob.SendMessage($"You earned a {rank:L} trophy!"); // "gold" not "Gold" **See**: `dev-docs/property-lists.md` § "Never Invalidate From Inside `GetProperties`". +### 20. Tick-Count Math Must Be Wraparound-Safe +**Check**: Every comparison between `Core.TickCount` / `Core.GetTimestamp()` values (or fields +derived from them — names like `*Until`, `*At`, `*Next*`, `deadline`) must be in subtraction form. +Flag direct comparisons, zero/sign sentinels, and deadline fields left at their zero default. +**Bad**: `if (Core.TickCount < _deadline)`; `if (_lastEventAt > 0)` as "has happened"; +`private static long _deadline;` compared before being seeded from a real tick. +**Good**: `if (Core.TickCount - _deadline < 0)`; a separate `bool` for "has happened"; seeding +deadline fields from the first observed timestamp. +**Why**: On some hypervisors — Google Cloud specifically — the VM receives a pass-through of the +host's never-resetting counter. Tick counts are NOT zero at process start, NOT zero at OS boot, +can be enormous from the first read, and can wrap negative. Direct comparisons and sign sentinels +then fail only on those hosts, after long host uptimes — the least reproducible bug class there +is. Windows has not shown this in testing; Linux has, in production. Subtraction of two ticks +wraps correctly in two's complement. +**Note**: `DateTime`/`DateTimeOffset` comparisons are unaffected; this applies only to the +monotonic tick domain. + +**See**: `dev-docs/tick-counts.md` for the full rules and review checklist. + ## Severity Levels -- **ERROR**: Rules 3, 9, 10, 13, 19 (will cause bugs, build failures, or client-side leaks) +- **ERROR**: Rules 3, 9, 10, 13, 19, 20 (will cause bugs, build failures, or client-side leaks) - **WARNING**: Rules 1 (Tier 3 LINQ), 2, 4, 5, 6, 7, 8, 12, 14, 15, 17 (performance/convention issues) - **INFO**: Rules 1 (Tier 2 LINQ on warm paths — note it but don't flag as violation), 16 (switch patterns — suggest but don't flag) - **ASK**: Rule 11 (need user input) diff --git a/dev-docs/claude-skills/modernuo-content-patterns.md b/dev-docs/claude-skills/modernuo-content-patterns.md index 12c9515ef..ce7f18acf 100644 --- a/dev-docs/claude-skills/modernuo-content-patterns.md +++ b/dev-docs/claude-skills/modernuo-content-patterns.md @@ -23,6 +23,12 @@ description: > 4. **Clean up timers and references in `OnDelete()`/`OnAfterDelete()`** 5. **No LINQ** in game logic -- use loops and `PooledRefList` 6. **File placement** matters -- follow the directory conventions below +7. **Creature speeds are delays in seconds, on two clocks** -- think + (`ActiveSpeed`/`PassiveSpeed`, seconds per AI decision) and move + (`ActiveMoveSpeed`/`PassiveMoveSpeed`, seconds per step; inherits think until + overridden). Prefer `npc-speeds.json` buckets (`SpeedClass`); `SetSpeed()` sets think + AND clears move overrides, `SetMoveSpeed()` sets move only -- see + `dev-docs/content-patterns.md` § Creature Speeds ## New Item Template diff --git a/dev-docs/claude-skills/modernuo-serialization.md b/dev-docs/claude-skills/modernuo-serialization.md index c96244656..91e3d5e95 100644 --- a/dev-docs/claude-skills/modernuo-serialization.md +++ b/dev-docs/claude-skills/modernuo-serialization.md @@ -40,21 +40,31 @@ public partial class MyItem : Item { } public partial class MigratedItem : Item { } ``` -### [SerializableField(index, setter, saveIf)] +### [SerializableField(index, getter, setter, isVirtual, fieldChanged, allowFieldChange)] Applied to `_camelCase` private fields. Generates `PascalCase` property. - `index`: Serialization order (0+) -- `setter`: Access level -- `"private"`, `"internal"`, or omit for public -- `saveIf`: Condition method name for conditional serialization +- `getter`/`setter`: Access level -- `"private"`, `"internal"`, or omit for public +- `isVirtual`: Generate a virtual property +- `fieldChanged`: `nameof` of `void Method(T oldValue, T newValue)`, invoked by the generated setter after assignment +- `allowFieldChange`: `nameof` of `bool Method(ref T value)`, invoked before assignment -- coerce through the `ref` parameter or return `false` to reject + +Generated setter pipeline: equality check → `allowFieldChange` → assignment → `MarkDirty` → `InvalidateProperties` (if declared) → `fieldChanged`. The field still holds the old value while the gate runs. Hooks require a generated setter (SG3018 on readonly/setterless fields); a missing or wrong-shaped named method is SG3015. ```csharp -[SerializableField(0)] +[SerializableField(0, allowFieldChange: nameof(AllowChargesChange))] [SerializedCommandProperty(AccessLevel.GameMaster)] +[InvalidateProperties] private int _charges; -// Generates: public int Charges { get; set; } + +private bool AllowChargesChange(ref int value) +{ + value = Math.Clamp(value, 0, MaxCharges); + return true; +} ``` ### [SerializableProperty(index, useField)] -Applied to properties with custom get/set logic. +Applied to properties with **custom getters** (fallback defaults, lazy/self-healing reads) or setter semantics the field hooks cannot express. For setters that only coerce, veto, or run post-change side effects, use `[SerializableField]` with `allowFieldChange`/`fieldChanged` instead. - `index`: Serialization order - `useField`: Backing field name if auto-detection fails @@ -63,12 +73,12 @@ Applied to properties with custom get/set logic. [CommandProperty(AccessLevel.GameMaster)] public int MaxItems { - get => _maxItems == -1 ? DefaultMaxItems : _maxItems; + get => _maxItems == -1 ? DefaultMaxItems : _maxItems; // custom getter: the reason this is a property set { _maxItems = value; InvalidateProperties(); - this.MarkDirty(); + this.MarkDirty(); // REQUIRED in custom setters } } ``` @@ -89,8 +99,11 @@ Exposes field to `[Props` gump for in-game editing. ### [EncodedInt] Variable-length int encoding (saves space for small values). +### [AnchoredDateTime] +Stores the absolute UTC instant; shifted by downtime at load so remaining time is preserved. Byte-stable across idle saves. Prefer for deadlines/elapsed-while-running values. + ### [DeltaDateTime] -Stores DateTime as offset from current time (handles server restarts). +Stores DateTime as offset from current time (handles server restarts). Legacy: rewrites bytes every save; prefer `[AnchoredDateTime]` for new fields. Converting between the two changes the wire format (version bump). ### [InternString] Interns strings to reduce memory for repeated values. @@ -128,28 +141,32 @@ private void AfterDeserialization() } ``` -### [DeserializeTimerField(fieldIndex)] -Custom timer deserialization. Timer is saved as remaining TimeSpan. +### [DeserializeTimer(nameof(Method), wallClock)] +Required on every serializable `Timer` member (SG3008 otherwise). By default the next tick is stored as **anchored time** (downtime does not consume the remaining delay; idle saves byte-stable); `wallClock: true` stores an absolute deadline instead (delay negative if it passed during downtime). The method -- `void Method(TimeSpan delay)` -- is invoked **only when a timer was running at save**; there is no sentinel to check. ```csharp [SerializableField(0, setter: "private")] +[DeserializeTimer(nameof(DeserializeEvaluateTimer), wallClock: true)] private Timer _evaluateTimer; -[DeserializeTimerField(0)] private void DeserializeEvaluateTimer(TimeSpan delay) { _evaluateTimer = Timer.DelayCall(delay, EvaluationInterval, Evaluate); } ``` -### [SerializableFieldSaveFlag(fieldIndex)] / [SerializableFieldDefault(fieldIndex)] -Conditional serialization -- skip fields with default values. +Switching a timer between drifting and `wallClock` changes the wire format: bump the class version and add `MigrateFrom` -- the old content struct exposes `XxxNext` (`DateTime`) and `XxxDelay` (`TimeSpan`, `TimeSpan.MinValue` when no timer ran). + +### [SaveFlag(nameof(ShouldSerializeMethod), nameof(DefaultValueMethod))] +On the serializable field/property itself. Conditional serialization -- skip fields with default values. Second method optional; when omitted, the field keeps its default at load. ```csharp -[SerializableFieldSaveFlag(0)] +[SerializableField(0)] +[SaveFlag(nameof(ShouldSerializeMaxItems), nameof(MaxItemsDefaultValue))] +private int _maxItems; + private bool ShouldSerializeMaxItems() => _maxItems != -1; -[SerializableFieldDefault(0)] private int MaxItemsDefaultValue() => -1; ``` @@ -216,28 +233,35 @@ public partial class ChargedItem : Item } ``` -### Item with Custom Properties +### Item with Setter Hooks (coerce + side effects) ```csharp [SerializationGenerator(2)] public partial class BagOfSending : Item { - [SerializableProperty(0)] - [CommandProperty(AccessLevel.GameMaster)] - public BagOfSendingHue BagOfSendingHue + [SerializableField(0, fieldChanged: nameof(OnBagOfSendingHueChanged))] + [SerializedCommandProperty(AccessLevel.GameMaster)] + private BagOfSendingHue _bagOfSendingHue; + + private void OnBagOfSendingHueChanged(BagOfSendingHue oldValue, BagOfSendingHue newValue) { - get => _bagOfSendingHue; - set + Hue = newValue switch { - _bagOfSendingHue = value; - Hue = value switch - { - BagOfSendingHue.Yellow => 0x8A5, - BagOfSendingHue.Blue => 0x8AD, - BagOfSendingHue.Red => 0x89B, - _ => Hue - }; - this.MarkDirty(); - } + BagOfSendingHue.Yellow => 0x8A5, + BagOfSendingHue.Blue => 0x8AD, + BagOfSendingHue.Red => 0x89B, + _ => Hue + }; + } + + [SerializableField(1, allowFieldChange: nameof(AllowChargesChange))] + [SerializedCommandProperty(AccessLevel.GameMaster)] + [InvalidateProperties] + private int _charges; + + private bool AllowChargesChange(ref int value) + { + value = Math.Clamp(value, 0, MaxCharges); + return true; } } ``` @@ -328,11 +352,13 @@ public partial class MagicGem ## Real Examples - Simple creature: `Projects/UOContent/Mobiles/Animals/Bears/BlackBear.cs` - Serialized fields + timer: `Projects/UOContent/Items/Weapons/Ranged/BaseRanged.cs` -- Custom properties: `Projects/UOContent/Items/Special/Solen Items/BagOfSending.cs` +- Setter hooks (allowFieldChange + fieldChanged): `Projects/UOContent/Items/Special/Solen Items/BagOfSending.cs` +- Custom getters (era fallbacks, the [SerializableProperty] use case): `Projects/UOContent/Items/Weapons/BaseWeapon.cs` - Complex with AfterDeserialization: `Projects/UOContent/Accounting/Account.cs` -- Timer deserialization: `Projects/UOContent/Items/Aquarium/Aquarium.cs` +- Timer deserialization (wall-clock): `Projects/UOContent/Items/Aquarium/Aquarium.cs` +- Timer deserialization (drifting/anchored + timer MigrateFrom): `Projects/UOContent/Items/Lights/BaseLight.cs` - Tidy + DeltaDateTime: `Projects/UOContent/Engines/CannedEvil/ChampionSpawn.cs` -- Conditional serialization: `Projects/Server/Items/Container.cs` +- Conditional serialization ([SaveFlag]): `Projects/Server/Items/Container.cs` ## Version Migration Migration schemas are JSON files in `Projects/Server/Migrations/` and `Projects/UOContent/Migrations/`: diff --git a/dev-docs/claude-skills/modernuo-threading.md b/dev-docs/claude-skills/modernuo-threading.md index 800bf77d2..3b6507af2 100644 --- a/dev-docs/claude-skills/modernuo-threading.md +++ b/dev-docs/claude-skills/modernuo-threading.md @@ -150,6 +150,78 @@ These files MAY use threading (they're server infrastructure, not game logic): - `Projects/Server/Network/` - Network I/O - `Projects/Server/Timer/Timer.Pool.cs` - Pool refill +## Exceptions: Vetted Workers in UOContent + +**A background thread is a last resort.** The forbidden list is about game logic, which is never +threaded. A dedicated worker touching no game state is the sanctioned way off the loop, and +necessarily uses `new Thread`, `ConcurrentQueue`, `Interlocked`, `AutoResetEvent` and +`volatile` **at the thread boundary only**. + +### Prove the need first + +- Measure **on-loop time**, not wall-clock. Frozen world is the cost; player latency is not. +- Off-loading creates no CPU. On 1-2 cores there is no spare core — gate on `ProcessorCount`. +- Count what stays: dispatch, continuation, and the loop slowing while the worker evicts shared L3. +- Record the measurement, or nobody can re-justify the worker later. + +### Game logic stays on the loop — chunk it + +Work needing game state cannot be threaded at any core count. Too slow for one tick? Split across +ticks, bounded by count or elapsed time — never "until done". + +```csharp +Timer.DelayCall(TimeSpan.Zero, TimeSpan.FromMilliseconds(50), () => +{ + var budget = 0; + while (_cursor < _items.Count && budget++ < 100) { Process(_items[_cursor++]); } +}); +``` + +### Vetted workers + +| Worker | Justification | +|---|---| +| `Accounting/Security/PasswordWorker.cs` | 8.9 ms/login on-loop at Argon2; 3.5-8.9 ms measured saving | +| `Engines/Advanced Search/AdvancedSearchGump.cs` | Admin-triggered full-world scan, saves disabled | + +### The six rules + +1. No game state read or written off-thread; dispatch immutable values captured on the loop. +2. Resolve policy (algorithm, salt, era branch) at dispatch — the worker holds none. +3. Park on a kernel wait, never spin. Spinning burns a core on shared hosts. +4. Run only while `WorldState is Running or WritingSave`. **Not** `World.Saving` — that misses + `PendingSave`, where serialization threads are already spinning. +5. Bounded queue, or a bound upstream named in a comment. +6. Everything the worker calls must itself be thread-safe. A singleton is not automatically safe — + `HashAlgorithm.ComputeHash` carries state, `Utility`'s RNG is a shared `System.Random` and game + state. Prefer static one-shot APIs (`SHA256.HashData`, `RandomNumberGenerator.Fill`). + +### Crossing the boundary + +Dispatch captures what the continuation will need to re-validate: + +```csharp +var job = new Job { Target = state, Expected = account.Password, Input = DerivePhrase(...) }; +if (!Worker.TryEnqueue(job)) { /* reject — never fall back to running it inline */ } +``` + +Hand back one of two ways, and no other: + +```csharp +Core.LoopContext.Post(() => Apply(job, result)); // a result for a specific caller +Volatile.Write(ref _snapshot, newTable); // a shared table rebuilt periodically +``` + +The continuation re-validates, because time passed: + +```csharp +if (job.Target?.Running != true) { return; } // gone +if (account.Password != job.Expected) { return; } // changed underneath +``` + +Always post a result, including on failure — a worker that throws silently leaves its caller +waiting forever. Use `ConfigureAwait(false)` on every await inside off-loop work. + ## Anti-Patterns | Pattern | Problem | Solution | diff --git a/dev-docs/claude-skills/modernuo-timers.md b/dev-docs/claude-skills/modernuo-timers.md index 2c5c8168e..f366b3569 100644 --- a/dev-docs/claude-skills/modernuo-timers.md +++ b/dev-docs/claude-skills/modernuo-timers.md @@ -147,18 +147,27 @@ public partial class DecayingItem : Item } ``` -### [DeserializeTimerField] Pattern (for Timer fields) +### [DeserializeTimer] Pattern (for Timer fields) +Required on every serializable `Timer` member. Drifting by default: the next tick is stored +as anchored time, so server downtime does not consume the remaining delay. Use +`wallClock: true` for absolute deadlines (delay is negative if it passed during downtime). +The method is invoked **only when a timer was running at save** — no sentinel to check. + ```csharp [SerializableField(0, setter: "private")] +[DeserializeTimer(nameof(DeserializeEvaluateTimer), wallClock: true)] private Timer _evaluateTimer; -[DeserializeTimerField(0)] private void DeserializeEvaluateTimer(TimeSpan delay) { _evaluateTimer = Timer.DelayCall(delay, EvaluationInterval, Evaluate); } ``` +Switching an existing timer between drifting and `wallClock` changes the wire format — bump +the class's `[SerializationGenerator]` version and add a `MigrateFrom` (the old content +struct exposes `XxxDelay`, `TimeSpan.MinValue` when no timer ran). + ### Custom Timer Class (When You Need Complex Logic) ```csharp private class DecayTimer : Timer diff --git a/dev-docs/configuration.md b/dev-docs/configuration.md index 9ddc348a7..b69349451 100644 --- a/dev-docs/configuration.md +++ b/dev-docs/configuration.md @@ -98,6 +98,7 @@ Examples from the codebase: accountHandler.enableAutoAccountCreation accountHandler.enablePlayerPasswordCommand accountHandler.maxAccountsPerIP +accountSecurity.encryptionAlgorithm autosave.enabled autosave.saveDelay world.savePath diff --git a/dev-docs/content-patterns.md b/dev-docs/content-patterns.md index 30bb942f3..ec56e1bb3 100644 --- a/dev-docs/content-patterns.md +++ b/dev-docs/content-patterns.md @@ -254,6 +254,35 @@ public override int TreasureMapLevel => 3; // Drops treasure map public override double WeaponAbilityChance => 0.4; // Weapon ability chance ``` +### Creature Speeds (think vs move clocks) + +All "speed" values are **delays in seconds** (smaller = faster). A creature runs two clocks: + +- **Think clock** — `ActiveSpeed`/`PassiveSpeed`/`CurrentSpeed`: seconds per AI decision + (combat decisions, target acquisition, spell timing). +- **Move clock** — `ActiveMoveSpeed`/`PassiveMoveSpeed`/`CurrentMoveSpeed`: seconds per + step. Inherits the matching think value until overridden, so a creature configured with + only think speeds behaves as one clock. Any value is legal — steps are scheduled + independently of think ticks, so the two need not divide evenly. + +Speeds normally come from `Distribution/Data/npc-speeds.json` (via `SpeedClass` or type +lists); `activeMove`/`passiveMove` are optional per bucket. Prefer data over code: + +```csharp +public override SpeedLevel SpeedClass => SpeedLevel.Slow; // bucket in npc-speeds.json +``` + +Code-level overrides for special cases: + +```csharp +SetSpeed(0.5, 2.0); // think clock; ALSO clears move overrides (one-clock legacy semantics) +SetMoveSpeed(0.45, 0.9); // move clock only — call after SetSpeed if both are wanted +ClearMoveSpeed(); // back to inheriting the think clock +``` + +All four are `[props`-tunable per instance (move values: set `0` to re-inherit); per-instance +move overrides serialize. Being badly hurt slows steps, never decisions (RunUO parity). + --- ## New Spell diff --git a/dev-docs/debugging-event-loop.md b/dev-docs/debugging-event-loop.md new file mode 100644 index 000000000..c38a0e6f7 --- /dev/null +++ b/dev-docs/debugging-event-loop.md @@ -0,0 +1,123 @@ +# Debugging Event Loop Performance + +How to diagnose "the server feels slow" — written for both humans and AI assistants. Follow the +funnel in order; most incidents resolve before the last step. Do not start with dotnet-trace. + +## The model + +Every second of the main thread's wall time goes to exactly one of four places: + +1. **Work** — the loop's phases: mobile deltas, item deltas, timer callbacks (`Timer.Slice`), + network processing (`NetState.Slice`), posted tasks (`LoopContext`), world snapshots + (`WorldSnapshot` — the on-loop portion of a save). +2. **Sleep** — idle blocking in `NetState.WaitForCompletion`, bounded by the next timer tick and + `server.eventLoopIdleWaitMs`. +3. **GC pauses** — land inside whichever phase (or sleep) was running. +4. **Stolen** — the host ran something else: hypervisor scheduling, noisy neighbors, CPU credit + throttling. + +A sleep is bounded by the time to the next wheel turn, so **a correctly honoured sleep can never +cost a deadline**. The only way sleeping harms the game is the wait *returning late* — that is +stolen time, and the server measures it directly on every sleep. + +## Step 0 — Read what production already tells you + +No build changes needed. Three signals exist, all actionable: + +| Signal | Meaning | Action | +|---|---|---| +| Startup error: *host cannot honour short waits* | No high-resolution timer and `timeBeginPeriod` failed. Very old or unusual Windows. | Nothing is wrong with the server; it spins and uses a full core. Upgrade the OS or accept the core. | +| Warning: *host returned a Nms idle wait late … for the Nth time running* | The OS did not reschedule the process promptly after a 1–2ms wait, through several escalating backoffs. Shared/burstable vCPU signature. | Move to dedicated CPU, or set `server.eventLoopIdleWaitMs=0` to spin permanently. This is a **host** problem — no amount of server-side change fixes it. | +| Error: *keeps returning idle waits late and sleeping has backed off N times* | The escalation hit its 120s ceiling. The host is not going to recover. | As above, but stop waiting for it to settle. Logged once per degradation, re-armed after a clean minute. | +| Admin gump → Performance → *Event Loop* | `Healthy` / `Sleep suspended (host)` / `Spinning (configured)` / `Spinning - host cannot honor short waits` | Same as above; the last verdict is the startup error's state, not a config choice. | + +The first two backoffs of any episode log at **Debug**, not Warning: a single suspension is +recoverable and not something an operator can act on. Raise the log level if you are chasing a +marginal host and want to see them. Late wakes that coincide with a gen1-or-higher GC are not +counted at all — the GC deliberately collects during idle sleeps, so its pauses land there by +design and are not the host's fault. + +If none of these fired and the shard still feels laggy, the cause is work, GC, or something a +boot-time signal cannot see. Continue. + +## Step 1 — Flip the profiling build + +``` +dotnet build -p:EventLoopProfiling=true +``` + +This compiles in `EventLoopProfiler` (Server) and the `[LoopStats` command (UOContent). Without +the flag every hook call site is removed by the compiler (`[Conditional]`), so there is nothing to +"turn off" in normal builds and no cost to leave the hooks in the code. The profiling build's own +overhead is a handful of timestamp reads per iteration — small enough to run for days while +hunting an intermittent problem. + +**Capture a baseline first.** Run `[LoopStats` while the shard feels *fine* and keep the CSV. The +profiler also keeps ~15 minutes of history in memory, so if the problem is episodic you can wait +for an episode and the good minutes on either side are already recorded. Numbers without a +baseline are how RunUO's profiler became useless — always compare bad minutes to good minutes on +the same box, build, and world. + +## Step 2 — Read the decomposition + +`[LoopStats` prints the last minute and writes the full history CSV (one row per second). Match +the shape against these signatures: + +| Signature | Diagnosis | Next step | +|---|---|---| +| One phase consistently hot (e.g. `TimerSlice` 40%/s) | Deep processing in that subsystem | Step 3 — find the culprit in that phase | +| All phases near zero, `stolen` high, `lateWakes` > 0 | Host is stealing CPU | Host problem; see step 0 actions | +| `gcPauseMs` high, gen2 counts rising | GC pressure — something is allocating heavily | Step 3 on the allocating phase, or dotnet-counters for alloc rate | +| Iterations ≫ sleeps while shard is idle | The loop is not sleeping: a queue never drains or a wake storm | Check `IsIdle` inputs; a stuck signal in the ring is the historical example | +| Sleeps ≈ iterations, each sleep ~0ms | Spurious wake storm | Ring backend issue; count `wakesIssued` vs actual cross-thread posts | +| Everything normal, complaint persists | Not the event loop | Look at the network path, client, or DB/save timing | + +**Wheel lag vs player lag:** `wheelLagMaxMs` is how late timer callbacks fired. Receives are +handled the moment they arrive (they wake the loop), so player-felt lag with a clean wheel points +away from the loop entirely. + +## Step 3 — Find the culprit inside a hot phase + +Add a temporary culprit hook rather than reaching for a tracer. The pattern: same +`[Conditional("EVENT_LOOP_PROFILING")]` attribute, own file or the profiler file, record only the +worst offender per second (identity + duration), never a per-event log. Examples: + +- `TimerSlice` hot → time each timer callback, keep the max and its `timer.ToString()`. +- `NetworkSlice` hot → time packet handlers by packet id, keep the max. +- GC pressure → `dotnet-counters monitor --counters System.Runtime` for alloc rate first; it is + cheap and often names the culprit generation without a trace. + +Keep the hook after the hunt if it earns its cost in the profiling build; delete it otherwise. + +## Step 4 — dotnet-trace, last and targeted + +Only when a hot phase resists the culprit hook. Know the costs: EventPipe visibly slows the +process (worst exactly when things are already bad) and adds artifacts to the trace — on small +vCPU hosts the tracer's own threads appear as hotspots and Rider/PerfView hotspot views can +mislead. Mitigate by being narrow: + +- Trace the specific minutes the decomposition flagged, not "a while". +- `dotnet-trace collect --profile cpu-sampling --duration 00:00:30` is usually enough. +- Compare against a trace of a good minute (same rule as step 1: no baseline, no conclusions). + +## The RAM / GC misconception (read before declaring a leak) + +ModernUO allocates very little, and the GC collects opportunistically — mostly during idle sleeps +and world saves. Under a spinning loop (`eventLoopIdleWaitMs=0`, or the pre-2026 default) the GC +may find **no** natural pause point: memory climbs to a large fraction of physical RAM, a forced +collection eventually drops part of it, and fragmentation keeps the baseline permanently above +where it started. Task manager shows alarming numbers; the in-game numbers do not. **Performance +is unaffected — this is lazy collection working as designed, not a leak.** Idle sleeping largely +removes the effect because every sleep is a natural GC opportunity. Before investigating "a leak": +check `gen0/1/2` and `gcPauseMs` in the decomposition, and compare working set *after a world +save*, which forces the collection the spin loop never allowed. + +## Rules of thumb + +- Never trade always-on profiling for the numbers. Production carries one timestamp per sleep and + nothing else; everything heavier lives behind the build flag or on the `measure/event-loop` + branch (full harness, A/B scripts, vendored ring experiments). +- One decomposition chart beats a thousand log lines. Resist adding warnings the reader cannot + act on; the three production signals are deliberate. +- When filing or reporting: attach the baseline CSV and the episode CSV. Relative statements + ("TimerSlice went from 4% to 61% during the episode") are the useful form. diff --git a/dev-docs/ip-bans-and-allowlists.md b/dev-docs/ip-bans-and-allowlists.md index 5588bb19e..40320efe5 100644 --- a/dev-docs/ip-bans-and-allowlists.md +++ b/dev-docs/ip-bans-and-allowlists.md @@ -24,7 +24,7 @@ Filters are consulted in registration order, first denial wins: | Filter | Source | Scope | |---|---|---| | `firewall` | `Configuration/firewall.json`, mutable in-game | Admin-curated, permanent | -| `blocklist` | `Configuration/ip-blocklist.txt` (millions of entries) | Reputation feeds | +| `blocklist` | `Configuration/ip-blocklist.txt` (millions of entries, opt-in) | Reputation feeds | | `auto-denylist` | In-memory, 15 min | What this shard just caught misbehaving | ### Contributing a ban @@ -38,7 +38,7 @@ Two, with different authority: | List | Source | Revocable? | Covers | |---|---|---|---| -| `FileAllowlist` | every `ip-allowlist*.txt` | No — an operator said so | Blocking **and** escalation | +| `ManualAllowlist` | every `ip-allowlist*.txt` (opt-in) | No — an operator said so | Blocking **and** escalation | | `LoginAllowlist` | Earned by authenticating, 90-day TTL | Yes — 10 strikes/hour | Blocking **and** escalation | Both are consulted **only after the blocklist has already matched**, so a normal accept — the one an @@ -66,16 +66,22 @@ If none of those match, they may be inside a **CIDR** in the blocklist, or held ### 2. Add them to the allowlist -One entry per line in `Distribution/Configuration/ip-allowlist.txt` — a bare address or a CIDR. This file -is yours; the generator creates it once and never rewrites it. +Set `"enabled": true` in `Configuration/ip-allowlist.json` first — it is off by default, so a shard that +has never written a carve-out does not poll for one. The shard logs a warning at startup if allowlist files +are present while the flag is off. + +Then one entry per line in `Distribution/Configuration/ip-allowlist.txt` — a bare address or a CIDR. This +file is yours; the generator creates it once and never rewrites it. ``` 203.0.113.42 # shard owner, listed via a shared upstream address 198.51.100.0/24 # a whole range if the ISP rotates within it ``` -The shard reloads within `reloadInterval` (60s default). **No restart, and no need to re-run the -generator.** From that point the address is neither blocked nor contributed. +With the flag on, the shard reloads within `reloadInterval` (60s default). **No restart, and no need to +re-run the generator.** From that point the address is neither blocked nor contributed. With the flag off +the generator still subtracts the file at generation time, so the address stops being *blocked* — but a +behavioural detection can still contribute it, which is the case the flag exists to cover. ### 3. Clear any ban that already exists @@ -123,8 +129,10 @@ where your players actually are, and a carve-out names a real network, so you bu ``` That writes `ip-allowlist-starlink.txt` beside the blocklist, and every `ip-allowlist*.txt` there is -subtracted — both by the generator and by the shard, with no config edit. Starlink costs about 0.1% of the -list. Blank a file (keep the file) to reputation-block that network again; delete it to drop the carve-out. +subtracted by the generator with no config edit. For the shard to read them too — which is what also stops +a carve-out address being *contributed* by a behavioural detection — set `enabled` in `ip-allowlist.json`; +it is off by default so no shard polls for files it never wrote. Starlink costs about 0.1% of the list. +Blank a file (keep the file) to reputation-block that network again; delete it to drop the carve-out. Carve-out files carry an `asn=` marker in their header, which is how `-RefreshCarveouts` finds them. A hand-written allowlist has no marker and is never rewritten. @@ -155,6 +163,12 @@ Escalation is **immediate**, on the first detection: a 15-minute local hold plus (4h) contribution. There is no N-connection threshold; the strike counter governs only revoking a `LoginAllowlist` entry. +The local hold runs 15 minutes from the **first** detection and is never extended by later ones, so an +address that keeps trying is released on schedule rather than held indefinitely. It does not get a free +run: the rate limiter sits *ahead* of the connection filters, so a flooder is re-reported and re-held on +its next attempt. Not refreshing is what keeps the holds in expiry order, which is what makes retiring +lapsed ones cost the number expiring rather than the number held. + ### What is deliberately NOT detected **Do not add rules based on arrival framing.** TCP has no message boundaries, so the network, the OS or a @@ -177,21 +191,27 @@ firewalled off. Shortening the 5s handshake window has been tried and broke real - **An allowlist cannot bootstrap.** A `LoginAllowlist` entry is only earned by getting in, so it can never repair an existing false positive, and it is weakest on rotating CGNAT — a player whose lease moved is a - stranger again. `FileAllowlist` is the fix for that, which is why it is manual. + stranger again. `ManualAllowlist` is the fix for that, which is why it is manual — and opt-in, via + `ip-allowlist.json`. - **A never-logged-in player on a shared address can still be caught**, for up to `badConnectDuration`, if a co-tenant misbehaves. Accepted: it is 4h and self-healing. The cheapest lever is `badConnectDuration`. - **`MaxConnections` (4096) is a hard ceiling.** The accept gate runs *after* the kernel completed the TCP handshake, so a blocklist match saves the socket setup and the `NetState` slot but never the connection itself. Only an upstream L4 proxy or edge scrubbing moves that cost off the shard. +- **The `auto-denylist` stops tracking at `maxEntries`.** Past it a detection still disconnects the + connection, but the address is not held, so it pays full detection cost on every reconnect instead of a + cheap accept-gate deny. The default is sized for the 50k–250k distinct-source floods seen in practice; a + flood past it wants upstream scrubbing rather than a larger cap, which only buys a longer on-loop scan. ## Configuration | File | Controls | |---|---| | `bans.json` | `reportRateLimitTrips`, `autoBanDuration`, `reportBadConnects`, `badConnectDuration` | -| `blocklist.json` | `file`, `allowlistFiles` (wildcards allowed), `reloadInterval`, `reportHits`, `banDuration`, `promoteSuppression` | +| `blocklist.json` | `enabled` (default `false`), `file`, `reloadInterval`, `reportHits`, `banDuration`, `promoteSuppression` | +| `ip-allowlist.json` | `enabled` (default `false`), `files` (wildcards allowed), `reloadInterval` | | `login-allowlist.json` | `enabled`, `file`, `ttl`, `flushInterval`, `escalateAfterStrikes`, `strikeWindow` | -| `auto-denylist.json` | `enabled`, `duration`, `maxEntries` | +| `auto-denylist.json` | `enabled`, `duration`, `maxEntries` (default `324,449` — sized for the floods seen in practice; see the remark on the setting before raising it) | | `crowdsec.json` | `lapiUrl`, `machineId`, `password`, `origin`, `manualBanDuration`, `flushInterval`, `maxQueue` | | `firewall.json` | Admin-curated entries | @@ -208,7 +228,7 @@ A shard fronted by an upstream proxy can disable all of it and register nothing. | `Projects/Server/Network/Bans/BanReasons.cs` | Reason slugs + the behavioural opt-in set | | `Projects/UOContent/Network/BanExemptions.cs` | Combines both allowlists into one answer | | `Projects/UOContent/Network/Blocklist/BlocklistFilter.cs` | File-sourced blocklist filter | -| `Projects/UOContent/Network/Blocklist/FileAllowlist.cs` | Operator carve-outs, read from the allowlist files | +| `Projects/UOContent/Network/Blocklist/ManualAllowlist.cs` | Operator carve-outs, read from the allowlist files | | `Projects/UOContent/Network/LoginAllowlist/LoginAllowlist.cs` | Allowlist earned by authenticating | | `Projects/UOContent/Network/AutoDenylist/AutoDenylist.cs` | Short-lived local hold | | `Projects/UOContent/Network/CrowdSec/CrowdSecReporter.cs` | LAPI contribution sink | diff --git a/dev-docs/networking-packets.md b/dev-docs/networking-packets.md index af47a1e06..639963cfd 100644 --- a/dev-docs/networking-packets.md +++ b/dev-docs/networking-packets.md @@ -511,9 +511,9 @@ Rules: Core owns the question; **every implementation lives in UOContent**. The three that ship are `firewall` (admin-curated, mutable at runtime, persisted to `Configuration/firewall.json`), `blocklist` (file-sourced, -millions of entries, demand-pages hits to CrowdSec) and `auto-denylist` (in-memory, short-lived, fed by the -shard's own behavioural detections). A shard that fronts its server with an upstream proxy or edge scrubbing -can drop all of them and register nothing. +millions of entries, demand-pages hits to CrowdSec, **opt-in**) and `auto-denylist` (in-memory, +short-lived, fed by the shard's own behavioural detections). A shard that fronts its server with an +upstream proxy or edge scrubbing can drop all of them and register nothing. The allowlists, ban contribution, behavioural detection and the operator process for exempting a false-positive address are covered separately in diff --git a/dev-docs/platform-prerequisites.md b/dev-docs/platform-prerequisites.md new file mode 100644 index 000000000..3e76d3b47 --- /dev/null +++ b/dev-docs/platform-prerequisites.md @@ -0,0 +1,159 @@ +# Platform Prerequisites + +OS-level dependencies ModernUO needs at runtime, why each one is required, and what breaks without +it. This page is about software packages, not hardware sizing. + +Run `./build-tool --check-prereqs` from the repository root to check the current machine. It prints +the exact install command for the detected distribution. + +## What is required + +| Dependency | Platform | Why | +|---|---|---| +| .NET 10 Runtime | all | — | +| ICU (`libicuuc`, `libicui18n`) | Linux, macOS | The runtime refuses to start without it; see below | +| tzdata | Linux | Time zone lookups; see below | +| `libdeflate` | all | `LibDeflate.Bindings` | +| `libargon2` | all | `Argon2.Bindings` (password hashing) | +| VC++ Redistributable v14 | Windows | Native bindings | + +Not required, despite appearances: + +- **zstd** — `ZstdNet` bundles `libzstd` for every RID. +- **liburing** — `IORingGroup` issues `io_uring` syscalls directly. It imports only `libc`, + `libSystem.dylib`, `kernel32.dll`, `kernelbase.dll` and `ws2_32.dll`. +- **`-dev` / `-devel` packages** — see "Runtime packages only" below. + +## Install + +```sh +# Debian / Ubuntu (ICU has no stable package alias, so match it by pattern) +sudo apt-get install -y '^libicu[0-9]+$' libdeflate0 libargon2-1 tzdata + +# Fedora / RHEL +sudo dnf install -y libdeflate libargon2 libicu tzdata + +# Alpine +apk add --no-cache libdeflate argon2-libs icu-libs tzdata + +# macOS +brew install icu4c libdeflate argon2 +``` + +CentOS additionally needs EPEL and CRB: + +```sh +sudo dnf install -y epel-release epel-next-release && sudo dnf config-manager --set-enabled crb +``` + +## Runtime packages only + +Only the runtime packages are needed. The `-dev`/`-devel` packages are **not** required. + +They used to be, because .NET's `DllImport` probing looks for the unversioned `libfoo.so`, and on +Linux that bare symlink ships only in the development package. The runtime package ships the +versioned SONAME (`libdeflate.so.0`, `libargon2.so.1`). The binding packages now probe the versioned +names as well, so the runtime package is sufficient. + +Anything still documenting `libicu-dev` or `libdeflate-dev` as a requirement is out of date. + +## ICU + +`Directory.Build.props` sets `InvariantGlobalization=false`, so ICU is mandatory. Without it the +runtime does **not** throw — it `FailFast`s: + +``` +Couldn't find a valid ICU package installed on the system. Please install libicu (or icu-libs) +using your package manager and try again. +``` + +That is `SIGABRT` (exit 134) and it cannot be caught. Note the process **starts cleanly and aborts +later**, at whatever line first touches a culture, so the crash rarely points at the cause. + +### Why invariant mode is not an option + +`InvariantGlobalization=true` would remove the ICU dependency, but it changes behaviour in ways that +corrupt data silently. Measured on .NET 10 with the repository's settings: + +| Behaviour | With ICU | Invariant mode | +|---|---|---| +| `new CultureInfo("de-DE")` | real culture | succeeds, returns invariant data | +| de-DE decimal separator | `,` | `.` | +| `1234.5` as de-DE | `1.234,5` | `1,234.5` | +| `string.Compare("a", "B", InvariantCulture)` | `-1` (linguistic) | `31` (ordinal) | +| sort `[b, A, a, B]` | `a, A, b, B` | `A, B, a, b` | +| `FindSystemTimeZoneById("Eastern Standard Time")` on Linux | resolves | `TimeZoneNotFoundException` | +| UTF-8 round-trip of non-ASCII | unaffected | unaffected | + +The dangerous row is the first. Because `Directory.Build.props` also sets +`PredefinedCulturesOnly=false`, constructing a culture in invariant mode **succeeds** instead of +throwing `CultureNotFoundException`, and hands back an object populated with invariant data. Number +parsing and formatting then produce wrong values with no error, and culture-sensitive sort order +silently becomes ordinal. + +Encoding is not affected — UTF-8 round-trips correctly in both modes. + +### Version floor + +The runtime accepts `libicuuc.so.60` and above (`MinICUVersion` in `pal_icushim.c`). The prerequisite +checker enforces the same floor, so a host carrying only an older ICU is reported missing rather than +passing and then aborting at startup. RHEL/CentOS 7 ships ICU 50 and is affected. + +ICU tracks its own release train, so the SONAME digit varies widely by distribution — `.so.74` on +Ubuntu 24.04, `.so.76` on Alpine, `.so.77` on Fedora, `.so.78` on openSUSE. There is no stable +package alias on Debian and Ubuntu, which is why the checker resolves the name via `apt-cache` +instead of hardcoding one. + +Only `libicuuc` and `libicui18n` are used; those are the two names +`libSystem.Globalization.Native.so` loads. `libicudata` arrives as a dependency of `libicuuc`, and +`libicuio`/`libicutu`/`libicutest` are never referenced. Every distribution ships all of them in a +single package, so installing ICU at all satisfies both. + +## tzdata + +The event scheduler resolves configured zone IDs through `TimeZoneInfo`, which reads +`/usr/share/zoneinfo` on Linux. This is separate from ICU: it is data, not a library, so no loader +probe finds it, and slim container images routinely omit it. + +Without tzdata every lookup except `UTC` throws: + +``` +TimeZoneNotFoundException: The time zone ID 'America/New_York' was not found on the local computer. +``` + +`TimeZoneInfo.GetSystemTimeZones()` returns 1 entry instead of ~419, and `TimeZoneInfo.Local` falls +back to UTC. + +### There is no per-zone subset + +Distributions do not package individual zones — it is one `tzdata` package, about 2 MB installed for +the full set. Subsetting is not worth pursuing. + +The one split that does exist is **`tzdata-legacy`** on Debian 12 and Ubuntu 24.04, which carries the +deprecated aliases. With plain `tzdata` alone: + +| Zone ID | `tzdata` | `tzdata-legacy` | +|---|---|---| +| `America/New_York` | present | — | +| `Europe/Kyiv` | present | — | +| `EST5EDT` | present | — | +| `US/Eastern` | **missing** | present | +| `Asia/Calcutta` | **missing** | present | + +So a shard configured with a legacy alias such as `US/Eastern` throws on a current Debian or Ubuntu +even though tzdata is installed. Either install `tzdata-legacy` or switch the configured value to the +canonical ID (`America/New_York`, `Asia/Kolkata`). + +`TZDIR` is honoured if the data lives somewhere non-standard. + +## How the check works + +`--check-prereqs` asks the loader directly — `NativeLibrary.TryLoad` on the unversioned name, then +`libfoo.so.N` descending through the accepted range. + +It deliberately does not consult a package database or `ldconfig -p`. Both answer a different +question than "will `dlopen` succeed": + +- Package queries need a hardcoded name, which does not exist for ICU. +- `ldconfig`'s cache can be stale, omits `LD_LIBRARY_PATH`, and carries no version information to + enforce the ICU floor against. On musl it exits successfully while producing nothing usable. diff --git a/dev-docs/runuo-migration-docs/02-serialization.md b/dev-docs/runuo-migration-docs/02-serialization.md index 1470792af..666ebfdb4 100644 --- a/dev-docs/runuo-migration-docs/02-serialization.md +++ b/dev-docs/runuo-migration-docs/02-serialization.md @@ -457,16 +457,24 @@ set ``` Without this, changes won't be saved. +Most RunUO custom setters only clamp the value or run side effects after assignment. Those +convert to a plain `[SerializableField]` with the `allowFieldChange`/`fieldChanged` hooks, +which handle the equality check and `MarkDirty()` for you -- reserve `[SerializableProperty]` +for custom getters (see `dev-docs/serialization.md`). + ### 3. Field Ordering The `[SerializableField(N)]` index determines serialization order. Choose a logical order and don't change it after the first save — or increment the version. ### 4. Conditional Serialization -Use `[SerializableFieldSaveFlag]` and `[SerializableFieldDefault]` to skip default values: +Use `[SaveFlag]` on the serializable field to skip default values (the second method is +optional -- omit it and the field keeps its default at load): ```csharp -[SerializableFieldSaveFlag(0)] +[SerializableField(0)] +[SaveFlag(nameof(ShouldSerializeMaxItems), nameof(MaxItemsDefaultValue))] +private int _maxItems; + private bool ShouldSerializeMaxItems() => _maxItems != -1; -[SerializableFieldDefault(0)] private int MaxItemsDefaultValue() => -1; ``` @@ -479,12 +487,15 @@ private List _followers; ``` ### 6. DateTime Fields -Use `[DeltaDateTime]` to survive server restarts: +Use `[AnchoredDateTime]` to survive server restarts -- the value is shifted by downtime at +load, so the remaining time is preserved and idle saves stay byte-stable: ```csharp -[DeltaDateTime] +[AnchoredDateTime] [SerializableField(0)] private DateTime _expireTime; ``` +(`[DeltaDateTime]` is the legacy equivalent; it rewrites bytes on every save. Converting an +existing field between the two changes the wire format and requires a version bump.) ### 7. Keeping Manual Serialization (Rare) Some edge cases still need manual serialization. If a type has complex conditional logic that can't be expressed with attributes, you can implement `ISerializable` manually. But this is rare — try attributes first. diff --git a/dev-docs/runuo-migration-docs/03-timers.md b/dev-docs/runuo-migration-docs/03-timers.md index 29f3d1282..13596a079 100644 --- a/dev-docs/runuo-migration-docs/03-timers.md +++ b/dev-docs/runuo-migration-docs/03-timers.md @@ -305,7 +305,7 @@ In RunUO, timers are commonly started in `Deserialize()`. In ModernUO, use `[Aft `_token.Cancel()` can be called on a default token, a stopped token, or an already-cancelled token. No null checks needed. ### 4. Timer.DelayCall Still Exists -`Timer.DelayCall()` is still available and returns a `Timer` object. Use it when you need the `Timer` reference (e.g., for `[DeserializeTimerField]`) or state-carrying overloads. +`Timer.DelayCall()` is still available and returns a `Timer` object. Use it when you need the `Timer` reference (e.g., for a serialized timer field with `[DeserializeTimer]`) or state-carrying overloads. ### 5. Custom Timer Classes Are Still Possible For complex timer logic (e.g., `Corpse.DecayTimer`), you can still subclass `Timer` with `OnTick()`. But prefer the fire-and-forget pattern for simple cases. diff --git a/dev-docs/serialization.md b/dev-docs/serialization.md index 151d9d717..059fbff69 100644 --- a/dev-docs/serialization.md +++ b/dev-docs/serialization.md @@ -117,7 +117,7 @@ public partial class MyItem : Item { } See `dev-docs/runuo-migration-docs/02-serialization.md` for complete migration guidance. -### [SerializableField(index, setter, saveIf)] +### [SerializableField(index, getter, setter, isVirtual, fieldChanged, allowFieldChange)] **Target**: Private field (`_camelCase`) **Generates**: Public `PascalCase` property with get/set @@ -125,8 +125,11 @@ See `dev-docs/runuo-migration-docs/02-serialization.md` for complete migration g | Parameter | Type | Default | Description | |---|---|---|---| | `index` | `int` | Required | Serialization order (0-based) | -| `setter` | `string` | `null` (public) | `"private"` or `"internal"` to restrict setter | -| `saveIf` | `string` | `null` | Method name returning bool for conditional save | +| `getter` | `string` | `"public"` | Getter accessibility | +| `setter` | `string` | `"public"` | `"private"` or `"internal"` to restrict setter | +| `isVirtual` | `bool` | `false` | Generate a `virtual` property | +| `fieldChanged` | `string` | `null` | `nameof` of a `void Method(T oldValue, T newValue)` invoked by the generated setter after assignment | +| `allowFieldChange` | `string` | `null` | `nameof` of a `bool Method(ref T value)` invoked before assignment; coerce the value through the `ref` parameter, or return `false` to reject the change | ```csharp [SerializableField(0)] // Public property @@ -144,14 +147,57 @@ The generated property for `_charges` would be: public int Charges { get => _charges; - set { _charges = value; this.MarkDirty(); } + set + { + if (value != _charges) + { + _charges = value; + this.MarkDirty(); + } + } } ``` +**Setter hooks** replace most hand-written `[SerializableProperty]` setters. The generated +pipeline is: equality check → `allowFieldChange` (coerce/veto) → assignment → `MarkDirty` → +`InvalidateProperties` (if declared) → `fieldChanged`. The gate runs before assignment, so +the field itself still holds the old value inside it. + +```csharp +[SerializableField(0, allowFieldChange: nameof(AllowChargesChange))] +[SerializedCommandProperty(AccessLevel.GameMaster)] +[InvalidateProperties] +private int _charges; + +private bool AllowChargesChange(ref int value) +{ + value = Math.Clamp(value, 0, MaxCharges); // coerce, or return false to veto + return true; +} + +[SerializableField(1, fieldChanged: nameof(OnOwnerChanged))] +private Mobile _owner; + +// oldValue makes unsubscribe/resubscribe patterns trivial +private void OnOwnerChanged(Mobile oldValue, Mobile newValue) +{ + oldValue?.Followers.Remove(this); + newValue?.Followers.Add(this); +} +``` + +Both hooks require a generated setter — declaring one on a `readonly` field or with +`setter: null` is a compile-time error (SG3018), and a named method that is missing or has +the wrong signature is too (SG3015). + ### [SerializableProperty(index, useField)] **Target**: Property with custom get/set logic -**Use when**: You need non-trivial getter/setter logic +**Use when**: You need a **custom getter** (fallback defaults, lazy or self-healing reads) +or setter semantics the field hooks cannot express (work that must run on *equal* +assignment, pre-assignment state capture). For setters that only coerce, veto, or run +post-change side effects, prefer `[SerializableField]` with `allowFieldChange`/`fieldChanged` +instead — the generated setter handles equality, `MarkDirty`, and ordering for you. | Parameter | Type | Default | Description | |---|---|---|---| @@ -163,7 +209,7 @@ public int Charges [CommandProperty(AccessLevel.GameMaster)] public int MaxItems { - get => _maxItems == -1 ? DefaultMaxItems : _maxItems; + get => _maxItems == -1 ? DefaultMaxItems : _maxItems; // custom getter: the reason this is a property set { _maxItems = value; @@ -173,6 +219,10 @@ public int MaxItems } ``` +Note: the `fieldChanged`/`allowFieldChange` hooks are `[SerializableField]` arguments and +cannot be declared on a `[SerializableProperty]` — its setter is your own code, so call your +methods from the setter directly. + ### [InvalidateProperties] **Target**: `[SerializableField]`-decorated field @@ -208,12 +258,32 @@ Overloads: Best for fields that are usually small values (counts, IDs, indexes). +### [AnchoredDateTime] + +**Target**: `DateTime` field +**Effect**: Stores the absolute UTC instant; at load it is shifted forward by the downtime +between the save and the load (using the save-start anchor in the save's index file), so +server downtime does not consume the remaining time. `DateTime.MinValue`/`MaxValue` +sentinels pass through unshifted. + +Prefer this for deadlines and "elapsed while running" values. Unlike `[DeltaDateTime]`, the +stored bytes do not change on every save when the value is unchanged, keeping idle saves +byte-stable. + +```csharp +[AnchoredDateTime] +[SerializableField(0)] +private DateTime _expireTime; +``` + ### [DeltaDateTime] **Target**: `DateTime` field **Effect**: Stores as offset from current time rather than absolute timestamp. -This ensures timers and expiration dates survive server restarts correctly. +Legacy encoding for surviving restarts: it rewrites the bytes on every save even when the +value has not changed. Prefer `[AnchoredDateTime]` for new fields; converting an existing +field between the two changes the wire format and requires a version bump. ```csharp [DeltaDateTime] @@ -289,40 +359,76 @@ private void AfterDeserialization() } ``` -### [DeserializeTimerField(fieldIndex)] +### [DeserializeTimer(nameof(Method), wallClock)] -**Target**: Method taking `TimeSpan` parameter -**Effect**: Custom deserialization for Timer fields. The timer is saved as remaining delay. +**Target**: `Timer`-typed `[SerializableField]` or `[SerializableProperty]` member +**Effect**: Declares how the timer is stored and restored. Required on every serializable +timer (SG3008 otherwise). + +By default the timer's next tick is stored as **anchored time**: server downtime does not +consume the remaining delay, and idle saves are byte-stable. Pass `wallClock: true` to store +an absolute deadline instead (the delay is then negative when the deadline passed during +downtime). + +The named method — `void Method(TimeSpan delay)` — is invoked **only when a timer was +actually running at save**, with the remaining delay. There is no sentinel value to check. ```csharp [SerializableField(0, setter: "private")] +[DeserializeTimer(nameof(DeserializeDecayTimer))] private Timer _decayTimer; -[DeserializeTimerField(0)] -private void DeserializeDecayTimer(TimeSpan delay) +private void DeserializeDecayTimer(TimeSpan delay) => _decayTimer = Timer.DelayCall(delay, Delete); +``` + +Switching an existing timer between drifting and `wallClock` changes the wire format — bump +the class version and add a `MigrateFrom`. The old-version content struct exposes the +timer's `XxxNext` (`DateTime`) and `XxxDelay` (`TimeSpan`, `TimeSpan.MinValue` when no timer +was running): + +```csharp +private void MigrateFrom(V3Content content) { - _decayTimer = Timer.DelayCall(delay, Delete); - _decayTimer.Start(); + if (content.DecayTimerDelay != TimeSpan.MinValue) + { + DeserializeDecayTimer(content.DecayTimerDelay); + } } ``` -### [SerializableFieldSaveFlag(fieldIndex)] / [SerializableFieldDefault(fieldIndex)] +### [SaveFlag(nameof(ShouldSerializeMethod), nameof(DefaultValueMethod))] +**Target**: the serializable field or property itself **Conditional serialization** -- skip fields that have their default value. +The first method (`bool Method()`) decides whether the value is written. The optional second +method (returning the field's type, no parameters) supplies the value at load when it was +not written; when omitted, the field keeps its default value. + +```csharp +[SerializableField(0)] +[SaveFlag(nameof(ShouldSerializeCharges), nameof(ChargesDefaultValue))] +private int _charges; + +private bool ShouldSerializeCharges() => _charges != -1; + +private int ChargesDefaultValue() => -1; +``` + +Works on `[SerializableProperty]` members the same way: + ```csharp [EncodedInt] [SerializableProperty(0)] +[SaveFlag(nameof(ShouldSerializeMaxItems), nameof(MaxItemsDefaultValue))] public int MaxItems { get => _maxItems == -1 ? DefaultMaxItems : _maxItems; set { _maxItems = value; this.MarkDirty(); } } -[SerializableFieldSaveFlag(0)] private bool ShouldSerializeMaxItems() => _maxItems != -1; -[SerializableFieldDefault(0)] private int MaxItemsDefaultValue() => -1; ``` diff --git a/dev-docs/server-requirements.md b/dev-docs/server-requirements.md new file mode 100644 index 000000000..0b2c50569 --- /dev/null +++ b/dev-docs/server-requirements.md @@ -0,0 +1,121 @@ +# Server Requirements + +Hardware guidance for running a ModernUO shard. + +## Tiers + +| Use | vCPU | RAM | Storage | +|---|---|---|---| +| Development / test | 2 **dedicated** | 2 GB | SSD | +| Small live shard (< 50 concurrent) | 4 dedicated | 4 GB | NVMe | +| Medium (50–200) | 4–8 | 8 GB | NVMe | +| Large (200+) | 8+, high clock | 16 GB+ | NVMe | + +These are starting points. Save size drives RAM more than player count does, and single-thread +clock speed drives tick latency more than core count does. Both are explained below. + +## Dedicated vCPU, not burstable + +This matters more than any other line on this page. + +Budget VPS plans sold as "2 vCPU" are frequently shared or burstable: you get a CPU credit balance +or a cgroup quota, and once it is exhausted the hypervisor throttles you. Throttling shows up in +game as periodic freezes that correlate with nothing in your logs, and it is the single most common +cause of "ModernUO is laggy on my $3/month VPS". + +Symptoms worth checking before blaming the server: + +- Steal time above ~1% (`top`, the `%st` column on Linux) +- Lag that disappears when you move to a larger plan with the same core count +- Tick lag spikes with no matching CPU spike in the process itself + +## Cores + +Game logic is **single-threaded**. Every mobile, item, timer, and packet handler runs on one +thread, so a shard's headroom is bounded by how fast one core is. Two fast cores beat four slow +ones. + +Cores beyond the first are used by: + +- **World saves.** `world.useMultithreadedSaves` (default on) spins up `ProcessorCount - 1` + serialization workers plus one inline on the main thread. On a 2-core box that is one worker; on + a 2-core box with a large world, consider setting it to `false` so saves do not contend with the + loop. +- **The .NET runtime.** Tiered JIT compilation (heaviest in the first minutes after boot) and + background GC. +- **Everything else on the machine**, including your OS and, on Windows, antivirus. + +Since ModernUO 2026 the loop sleeps when idle, so an empty shard costs roughly 1% of a core rather +than spinning. That change disproportionately helps small hosts. + +## Memory + +Three things dominate, and only one of them scales with players. + +**World size.** A world of ~190,000 items and ~33,000 mobiles loads in about a second and is not +itself large. Items and mobiles are the cheap part. + +**Saves.** Each serialization worker pre-allocates a heap sized to its share of the last save, at +roughly 1.25× total save size, and those buffers are retained afterwards. A 400 MB save therefore +implies about 500 MB of resident serialization heap on top of the live world. **This is the reason +1 GB hosts are not viable for a real shard**, even though an empty one boots fine. + +**Map residency.** `TileMatrix` reads map blocks from disk on demand and caches them permanently — +there is no eviction. Memory climbs toward full-facet residency as players explore. Felucca's land +tiles alone are around 117 MB, and statics are larger. + +Optional systems can add substantially more. The pathfinding prebake +(`pathfinding.prebakeMaps`) peaks above 1 GB of heap while baking. Budget for it or leave it off on +small hosts. + +Network buffers are minor by comparison: 64 KB receive plus a configurable 256 KB send +(`network.sendBufferSize`) per connection, so 100 players is roughly 32 MB. + +ModernUO runs **Workstation GC**, which is the right default for small hosts. Do not switch to +Server GC on a 2-core box. + +## Storage + +Saves are write-heavy bursts. Cheap network-attached storage with throttled IOPS will stall the +save path, and `World.WaitForWriteCompletion` blocks the loop at shutdown. Use local NVMe or SSD. + +Budget disk for: the world save, plus archives and backups if `autoArchive` is enabled (retention +defaults keep 24 hourly, 30 daily, and 12 monthly copies), plus the pathfinding cache if enabled. + +## Operating systems + +See the README for the full supported list. Two things are worth calling out: + +- **Windows Server 2012 R2 and 2016 sleep via a raised timer resolution.** Sleeping for a couple + of milliseconds prefers a high-resolution waitable timer, which requires Windows 10 1803 / + Server 2019. On older versions the ring falls back to `timeBeginPeriod(1)`, which raises the + system timer resolution to 1 ms so the plain wait timeout is accurate enough. The trade-off is a + higher interrupt rate (system-wide on those versions) — an acceptable price on a dedicated game + server, and the reason the high-resolution timer is preferred where it exists. + + Only if *both* mechanisms fail does the server detect it at startup, log it, and spin instead — + the same behaviour as setting `server.eventLoopIdleWaitMs` to 0: a full core at idle, and zero + missed deadlines. A host that claims short waits but cannot deliver them is caught at runtime by + the adaptive backoff. +- **Linux kernel 6.1** or newer (Debian 12 and equivalents). io_uring is used where available, with + automatic epoll fallback. + +## Tuning for a small host + +| Setting | Default | Why change it | +|---|---|---| +| `server.eventLoopIdleWaitMs` | `2` | `0` never sleeps: ~98% of one core, but zero skipped timer slots and zero lag. The choice for a large shard on dedicated CPU that would rather spend a core than risk a late wake. Above `2` the wheel starts losing slots. | +| `server.lateWakeThreshold` | `1` | Floor for the backoff: idle waits the host may return a full tick late, per second, before the rate test below applies at all. Raise on a jittery host; set very high to disable the backoff. | +| `server.lateWakePercent` | `10` | Share of a second's idle waits that must come back late before idle sleeping backs off. An idle loop sleeps hundreds of times a second, so a bare count cannot tell a few tail outliers from a host that never schedules the process — a genuinely bad host misses *most* of its waits. `0` leaves `lateWakeThreshold` in sole charge. | +| `world.useMultithreadedSaves` | `true` | Set `false` on 2-core hosts so saves do not contend with the game loop. | +| `pathfinding.prebakeMaps` | varies | Leave off on memory-constrained hosts; it peaks above 1 GB while baking. | +| `network.sendBufferSize` | 256 KB | Lower it if you are memory-bound with many connections. | +| `autoArchive.*` retention | 24h/30d/12m | Reduce if disk is tight. | + +## Am I undersized? + +Watch the log. The server warns when the host returns idle waits late and suspends idle sleeping, +and says so at startup if the host cannot honour short waits at all. Those warnings mean the host +is not scheduling the process promptly — typical of burstable or shared vCPU plans — and no +server-side change fixes that. For anything deeper, see +[debugging-event-loop.md](debugging-event-loop.md). diff --git a/dev-docs/threading-model.md b/dev-docs/threading-model.md index 09b0ec8f4..d89bc1c61 100644 --- a/dev-docs/threading-model.md +++ b/dev-docs/threading-model.md @@ -130,6 +130,138 @@ These files in `Projects/Server/` MAY use threading because they handle I/O outs - `Timer/Timer.Pool.cs` -- Async pool refill - `EventLoopTasks.cs` -- The synchronization context itself +### Exceptions: Vetted Workers in `Projects/UOContent/` + +**Take great care here. A background thread is a last resort, not a tool of first choice.** + +The table above is about **game logic**, which is never threaded. A dedicated worker that touches +no game state is the sanctioned way to move CPU-heavy or I/O work off the loop, and it necessarily +uses primitives the table forbids -- `new Thread`, `ConcurrentQueue`, `Interlocked`, +`AutoResetEvent`, `volatile`. Those are legitimate **at the thread boundary**, and nowhere else. + +#### First: prove the need + +Do not add a worker because something "looks slow". Measure, and measure the right thing: + +- **Measure on-loop time, not wall-clock.** How long a player waits does not matter; how long the + world is frozen does. A change that improves latency but not loop time buys nothing. +- **Off-loading does not create CPU.** It converts "the loop is blocked for N ms" into "the loop + competes for cores for N ms". On a 1--2 core host there is no spare core and it buys nothing at + all -- gate on `Environment.ProcessorCount`. +- **Account for what stays behind.** Dispatch, the continuation, and the loop's own work slowing + down while the worker evicts shared L3. That last one is real and is usually the largest. +- **Write the benchmark down.** A worker with no recorded measurement cannot be re-justified later, + and will be removed by someone who cannot tell whether it earns its complexity. + +#### Game logic stays on the loop -- chunk it instead + +Work that **needs** game state cannot be threaded at any core count. If it is too slow for one +tick, split it across ticks rather than across threads: + +```csharp +// Bound the work per tick, resume where it left off. +Timer.DelayCall(TimeSpan.Zero, TimeSpan.FromMilliseconds(50), () => +{ + var budget = 0; + while (_cursor < _items.Count && budget++ < 100) + { + Process(_items[_cursor++]); + } +}); +``` + +Bound by count or elapsed time, never by "until done". Threading game state is not a faster +version of this -- it is a correctness bug. + +#### Vetted workers + +| Worker | Off-loop work | Justification | +|---|---|---| +| `Accounting/Security/PasswordWorker.cs` | Password verification and hashing | `docs/handoffs/2026-08-07-off-loop-argon2-hashing.md` -- 8.9 ms/login on-loop at Argon2, measured 3.5--8.9 ms saved | +| `Engines/Advanced Search/AdvancedSearchGump.cs` | Parallel entity search | Admin-triggered full-world scan; saves disabled for its duration | + +Adding to this table needs the same bar: a measurement, and all five rules below. + +#### The six rules + +1. **No game state off-thread, read or written.** Hand the worker immutable values (strings, + structs) captured on the loop. Carrying a reference is fine only if the worker just passes it + back untouched. +2. **Decide policy on the loop, compute on the worker.** Anything rule-dependent -- which algorithm, + which salt, which era branch -- is resolved at dispatch, so the worker holds no policy it could + apply inconsistently. +3. **Park on a kernel wait; never spin.** `AutoResetEvent.WaitOne()` costs nothing while idle. + `SerializationThreadWorker` does spin, but only to await a producer mid-drain; absent that race, + spinning is a bug that burns a core on shared hosts. +4. **Yield to world saves.** Run only while `WorldState is Running or WritingSave`. `World.Saving` + is *not* the right check -- it covers only the freeze and misses `PendingSave`, where the + serialization threads are already awake and spinning on an empty queue. +5. **Bound the queue**, or rely on a bound upstream and say which one in a comment. +6. **Everything the worker calls must itself be safe off-thread.** A process-wide singleton is not + automatically safe -- look for instance state. `HashAlgorithm.ComputeHash` carries the running + digest across `HashCore`/`HashFinal`, so two threads sharing one corrupt each other. `Utility`'s + RNG is a shared `System.Random`, which is both thread-unsafe and game state. Prefer the static + one-shot forms (`SHA256.HashData`, `RandomNumberGenerator.Fill`), and if a dependency cannot be + made safe, fix it at the source rather than narrowing the worker around it. + +#### Handing work across the boundary + +**Loop → worker (dispatch).** Snapshot everything needed into immutable values. Capture any value +you intend to overwrite later, so the continuation can tell whether it changed: + +```csharp +var job = new Job +{ + Target = state, // carried, never dereferenced off-thread + Expected = account.Password, // captured so the continuation can detect a change + Input = DerivePhrase(...) // policy resolved here, on the loop +}; + +if (!Worker.TryEnqueue(job)) +{ + // Full. Reject -- do not fall back to running it inline, or a flood steers the work + // straight back onto the loop. +} +``` + +**Worker → loop (hand back).** Two sanctioned routes, and no others: + +```csharp +// 1. Marshal the apply step. Preferred when a specific result belongs to a specific caller. +Core.LoopContext.Post(() => Apply(job, result)); + +// 2. Publish an immutable snapshot behind a single volatile reference, read lock-free by the loop. +// Preferred for a shared lookup table rebuilt periodically. +Volatile.Write(ref _snapshot, newTable); +``` + +**The continuation must re-validate.** Time passed, and the loop kept running: + +```csharp +private static void Apply(Job job, Result result) +{ + // Gone? Never revive a dead NetState or a deleted entity. + if (job.Target?.Running != true) + { + return; + } + + // Changed? Do not overwrite a newer value with one derived from an older one. + if (!string.Equals(account.Password, job.Expected, StringComparison.Ordinal)) + { + return; + } + + account.Apply(result); +} +``` + +**Always post a result, including on failure.** A worker that throws and posts nothing leaves +whatever awaited it waiting forever. Catch, log, and post a failure verdict. + +**Never** call into game state from the worker, and never `await` on the loop in a way that lets a +continuation resume heavy work there -- `ConfigureAwait(false)` on every await inside off-loop work. + ## Memory Pooling ### STArrayPool diff --git a/dev-docs/tick-counts.md b/dev-docs/tick-counts.md new file mode 100644 index 000000000..f53e223d4 --- /dev/null +++ b/dev-docs/tick-counts.md @@ -0,0 +1,55 @@ +# Tick Counts: Overflow and Huge Starting Values + +Rules for any code that compares `Core.TickCount` / `Core.GetTimestamp()` values. Getting this +wrong produces bugs that only appear on specific cloud hosts after long host uptimes — the worst +kind to reproduce. + +## Why this matters (the Linux/cloud problem) + +`Core.GetTimestamp()` is built on `Stopwatch.GetTimestamp()`, which on Linux reads the kernel's +monotonic clock — and on some hypervisors, notably **Google Cloud**, the VM receives a +**pass-through of the host's never-resetting counter**. The tick count is *not* zero when the +process starts and *not* zero when the operating system booted; it is however long the physical +host has been up, which can be months or years. We have been burned by this in production. + +Consequences: + +- Raw values are enormous from the first read. Arithmetic that would "never overflow in 292 + years" of process uptime can overflow immediately (`Core.GetTimestamp()`'s `UInt128` + conversion path exists precisely because `raw * 1000` does not fit in 64 bits for large raws). +- Wrapped values can be **negative**. Nothing may assume a tick count is positive. +- **Windows is not affected** in our testing so far, which is exactly why this class of bug + ships: it works on every dev machine and fails on a customer's GCP instance. + +## The rules + +1. **Compare by subtraction, never directly.** Subtraction of two ticks wraps correctly in two's + complement; direct comparison does not. + + ```csharp + // WRONG: fails when ticks wrap or start huge + if (Core.TickCount < deadline) + + // RIGHT: wraparound-safe + if (Core.TickCount - deadline < 0) + ``` + +2. **Durations are always subtractions of two readings** (`elapsed = end - start`). Never derive + a duration from a single absolute value. + +3. **No zero or sign sentinels.** `if (_lastEventAt > 0)` as "has this happened yet" breaks when + ticks are negative. Track "has happened" with a separate `bool` or an existing counter. + +4. **Seed deadline fields from a real tick, not from field initialization.** A `long _deadline;` + left at 0 compares wrong against a huge or negative tick. Initialize relative to the first + observed timestamp (see the schedule-state seeding in `Core.Setup`). + +5. **Store deadlines as `start + interval` only if every comparison follows rule 1.** The + addition may wrap; the subtraction comparison handles it. + +## Reviewing for it + +Grep the diff for `TickCount <`, `TickCount >`, `GetTimestamp() <`, and comparisons against any +field whose name suggests a deadline (`*Until`, `*At`, `*Next*`). Each hit must be in subtraction +form. `DateTime`/`DateTimeOffset` comparisons are unaffected; this applies only to the monotonic +tick domain. diff --git a/dev-docs/timers.md b/dev-docs/timers.md index c10f31bd6..2c051cca6 100644 --- a/dev-docs/timers.md +++ b/dev-docs/timers.md @@ -188,15 +188,19 @@ public partial class TimedItem : Item ``` ### Pattern 4: Serializable Timer Field +Every serializable `Timer` member declares `[DeserializeTimer(nameof(Method))]` on the +field. By default the next tick is stored as anchored time (downtime does not consume the +remaining delay); pass `wallClock: true` for absolute deadlines. The method runs **only when +a timer was running at save**, with the remaining delay. + ```csharp [SerializableField(0, setter: "private")] +[DeserializeTimer(nameof(DeserializeDecayTimer))] private Timer _decayTimer; -[DeserializeTimerField(0)] private void DeserializeDecayTimer(TimeSpan delay) { _decayTimer = Timer.DelayCall(delay, Delete); - _decayTimer.Start(); } public void BeginDecay(TimeSpan delay) diff --git a/website/.gitignore b/website/.gitignore deleted file mode 100644 index f6992f32a..000000000 --- a/website/.gitignore +++ /dev/null @@ -1,21 +0,0 @@ -# Dependencies -/node_modules - -# Production -/build - -# Generated files -.docusaurus -.cache-loader -/static/packets.html - -# Misc -.DS_Store -.env.local -.env.development.local -.env.test.local -.env.production.local - -npm-debug.log* -yarn-debug.log* -yarn-error.log* diff --git a/website/content/development/commands-and-targeting.mdx b/website/content/development/commands-and-targeting.mdx deleted file mode 100644 index 1edc30040..000000000 --- a/website/content/development/commands-and-targeting.mdx +++ /dev/null @@ -1,206 +0,0 @@ ---- -sidebar_position: 4 -title: Commands & Targeting ---- - -# Commands & Targeting - -## Overview - -ModernUO uses a command system where all player/staff commands are prefixed with `[` by default (this is configurable). Commands are registered in static `Configure()` methods that the server discovers automatically at startup. Each command is bound to a minimum access level, so only authorized players can execute it. - ---- - -## Registering a Command - -Commands are registered by calling `CommandSystem.Register` inside a static `Configure()` method. The server calls all `Configure()` methods during initialization -- no manual wiring is needed. - -```csharp -public static class MyCommands -{ - public static void Configure() - { - CommandSystem.Register("MyCommand", AccessLevel.GameMaster, MyCommand_OnCommand); - } - - [Usage("MyCommand ")] - [Description("Does something with a name")] - public static void MyCommand_OnCommand(CommandEventArgs e) - { - var from = e.Mobile; - if (e.Length < 1) - { - from.SendMessage("Usage: [MyCommand "); - return; - } - - var name = e.GetString(0); - from.SendMessage($"Processing {name}"); - } -} -``` - -The `[Usage]` and `[Description]` attributes provide help text that appears in the in-game help system. - ---- - -## CommandEventArgs API - -When a command handler fires, it receives a `CommandEventArgs` object with the following members: - -| Member | Type | Description | -|:-------|:-----|:------------| -| `Mobile` | `Mobile` | The mobile that issued the command | -| `Command` | `string` | The command name that was typed | -| `ArgString` | `string` | The full argument string after the command name | -| `Arguments` | `string[]` | Arguments split by spaces | -| `Length` | `int` | Number of arguments (`Arguments.Length`) | -| `GetString(i)` | `string` | Get argument at index `i` as a string | -| `GetInt32(i)` | `int` | Get argument at index `i` as an integer | -| `GetBoolean(i)` | `bool` | Get argument at index `i` as a boolean | -| `GetDouble(i)` | `double` | Get argument at index `i` as a double | - ---- - -## Access Levels - -Each command requires a minimum access level. Players below that level cannot execute the command. - -| Level | Value | Description | -|:------|:------|:------------| -| `Player` | 0 | Normal player (default) | -| `Counselor` | 1 | Support staff with limited powers | -| `GameMaster` | 2 | GM with full world interaction | -| `Seer` | 3 | Event coordinator with extra tools | -| `Administrator` | 4 | Server administrator | -| `Developer` | 5 | Developer with access to debug commands | -| `Owner` | 6 | Server owner with unrestricted access | - ---- - -## Targeting System - -The targeting system lets a command (or any code) ask a player to click on something in the game world. The flow is: - -1. Code sets `mobile.Target = new MyTarget()`. -2. The client displays a targeting cursor. -3. The player clicks on a mobile, item, land tile, or static object. -4. The `OnTarget` method fires with what was clicked. - ---- - -## Target Implementation - -A target class inherits from `Target` and overrides `OnTarget`. The `targeted` parameter can be a `Mobile`, `Item`, `LandTarget`, or `StaticTarget` -- use a `switch` to handle each case. - -```csharp -public class IdentifyTarget : Target -{ - public IdentifyTarget() : base(12, false, TargetFlags.None) - { - } - - protected override void OnTarget(Mobile from, object targeted) - { - switch (targeted) - { - case Mobile m: - { - from.SendMessage($"That is a mobile named {m.Name}."); - break; - } - case Item item: - { - from.SendMessage($"That is an item: {item.GetType().Name} (0x{item.ItemID:X4})."); - break; - } - case LandTarget land: - { - from.SendMessage($"That is land tile at {land.Location}."); - break; - } - case StaticTarget st: - { - from.SendMessage($"That is a static: 0x{st.ItemID:X4}."); - break; - } - } - } -} -``` - -The `Target` constructor takes three parameters: -- **range** -- Maximum distance the target can be from the player. -- **allowGround** -- Whether clicking the ground is valid. -- **flags** -- `TargetFlags` value controlling criminal/beneficial checks. - ---- - -## Command + Targeting Pattern - -A common pattern is for a command to initiate targeting, then the target handler performs the actual work. This cleanly separates input from logic. - -```csharp -public static class HealCommands -{ - public static void Configure() - { - CommandSystem.Register("Heal", AccessLevel.GameMaster, Heal_OnCommand); - } - - [Usage("Heal")] - [Description("Fully heals the targeted mobile")] - public static void Heal_OnCommand(CommandEventArgs e) - { - e.Mobile.SendMessage("Who do you want to heal?"); - e.Mobile.Target = new HealTarget(); - } - - private class HealTarget : Target - { - public HealTarget() : base(12, false, TargetFlags.Beneficial) - { - } - - protected override void OnTarget(Mobile from, object targeted) - { - if (targeted is Mobile m) - { - m.Hits = m.HitsMax; - m.SendMessage("You have been fully healed."); - from.SendMessage($"You healed {m.Name}."); - } - else - { - from.SendMessage("That is not a mobile."); - } - } - } -} -``` - ---- - -## TargetFlags - -Target flags tell the server what kind of action the player is performing, which affects criminal checks and other systems. - -| Flag | Description | -|:-----|:------------| -| `None` | Neutral action -- no criminal or beneficial checks | -| `Harmful` | Hostile action -- triggers criminal flagging if targeting innocents | -| `Beneficial` | Helpful action -- triggers beneficial checks (healing, buffing) | - -:::tip -Always set the correct flag. Using `Harmful` on a healing target (or `None` on an attack) bypasses important game mechanics like the criminal system. -::: - ---- - -## Best Practices - -- **Register in `Configure()`** -- The server discovers these methods automatically. Do not register commands in constructors or other lifecycle methods. -- **Validate argument count** -- Always check `e.Length` before accessing arguments to avoid index-out-of-range errors. -- **Use appropriate access levels** -- Do not default to `Owner`. Choose the lowest level that makes sense for the command. -- **Use `Harmful`/`Beneficial` flags correctly** -- This ensures the criminal and notoriety systems work as intended. -- **Keep target handlers focused** -- Let the command set up the target, and let the target handler do the work. diff --git a/website/content/development/era-and-expansions.mdx b/website/content/development/era-and-expansions.mdx deleted file mode 100644 index ec57a32b8..000000000 --- a/website/content/development/era-and-expansions.mdx +++ /dev/null @@ -1,150 +0,0 @@ ---- -sidebar_position: 5 -title: Era & Expansions ---- - -# Era & Expansions - -## Overview - -ModernUO supports all Ultima Online expansions from the original release through the most recent era. The target expansion controls game mechanics, damage formulas, loot tables, available features, and which maps are accessible. A single configuration setting determines which era your server emulates, and all era-aware code branches automatically based on that setting. - ---- - -## Expansions - -ModernUO defines the following expansions in chronological order: - -| Expansion | Enum Value | Core Check | Year | Key Changes | -|:----------|:-----------|:-----------|:-----|:------------| -| None | `Expansion.None` | -- | 1997 | Original release, no expansion features | -| The Second Age | `Expansion.T2A` | `Core.T2A` | 1998 | Lost Lands, new dungeons, stat cap 225 | -| Renaissance | `Expansion.UOR` | `Core.UOR` | 2000 | Trammel/Felucca split, power hour | -| Third Dawn | `Expansion.UOTD` | `Core.UOTD` | 2001 | 3D client, Ilshenar, new monsters | -| Lord Blackthorn's Revenge | `Expansion.LBR` | `Core.LBR` | 2002 | Pet bonding, new quests | -| Age of Shadows | `Expansion.AOS` | `Core.AOS` | 2003 | Item properties, Malas, Paladin/Necromancer | -| Samurai Empire | `Expansion.SE` | `Core.SE` | 2004 | Tokuno, Bushido/Ninjitsu, new housing | -| Mondain's Legacy | `Expansion.ML` | `Core.ML` | 2005 | Elves, new dungeons, ML artifacts | -| Stygian Abyss | `Expansion.SA` | `Core.SA` | 2009 | Gargoyles, Ter Mur, Enhanced Client | -| High Seas | `Expansion.HS` | `Core.HS` | 2010 | Ship combat, fishing overhaul | -| Time of Legends | `Expansion.TOL` | `Core.TOL` | 2015 | Valley of Eodon, account gold | -| Endless Journey | `Expansion.EJ` | `Core.EJ` | 2018 | Free-to-play tier, restrictions on F2P | - ---- - -## Era Checks - -The `Core` class provides boolean properties for each expansion. Each property returns `true` when the server's configured expansion is **equal to or later than** that era. - -```csharp -Core.AOS // true if Age of Shadows or later -Core.ML // true if Mondain's Legacy or later -Core.SA // true if Stygian Abyss or later -``` - -This means that on a Mondain's Legacy server, `Core.AOS`, `Core.SE`, and `Core.ML` all return `true`, while `Core.SA` and later return `false`. - ---- - -## The AOS Divide - -Age of Shadows (AOS) is the most significant dividing line in Ultima Online's history. It fundamentally restructured combat and itemization: - -- **Five damage types** -- Physical, Fire, Cold, Poison, Energy replaced the single damage model. -- **Item properties** -- Weapons and armor gained randomized magical properties (hit chance, damage increase, resistances). -- **Luck system** -- Luck stat influences loot quality. -- **Insurance** -- Players can insure items against loss on death. -- **New spell schools** -- Chivalry (Paladin) and Necromancy were added. -- **Property lists (tooltips)** -- Items display their properties on hover. - -Because of this, the majority of era-conditional code in the codebase checks `Core.AOS`. If you are writing mechanics that differ between classic and modern UO, this is almost always the branch point. - ---- - -## Writing Era-Conditional Code - -### Ternary chains - -For simple value selection, chain ternaries from **newest to oldest** expansion: - -```csharp -var delay = Core.SE ? 250 : Core.AOS ? 500 : 1000; -``` - -This reads as: "If SE or later, use 250ms. Otherwise if AOS or later, use 500ms. Otherwise use 1000ms." - -### If/else branching - -For more complex logic like damage formulas, use if/else blocks: - -```csharp -if (Core.AOS) -{ - // AOS+ damage: uses item properties, resistances, and 5 damage types - var baseDamage = weapon.MaxDamage; - var bonus = attacker.GetDamageBonus(); - damage = ScaleDamage(baseDamage, bonus); -} -else -{ - // Pre-AOS damage: simpler formula based on weapon damage and tactics - damage = weapon.MaxDamage; - damage += (int)(attacker.Skills.Tactics.Value * 0.5); -} -``` - -### Property display branching - -Tooltips often show different information depending on the era: - -```csharp -if (Core.ML) -{ - list.Add(1060847, $"{"crafted by"}\t{_crafter?.Name}"); -} -``` - -### Era-aware loot - -`LootPack` automatically selects era-appropriate loot tables. Use the built-in properties: - -```csharp -LootPack.Rich // Selects the correct rich loot table for the current era -LootPack.Average // Era-appropriate average loot -``` - ---- - -## Configuration - -The server's target expansion is set in `expansion.json`. This file is generated during first-run setup, but you can edit it manually. - -```json -{ - "Id": 8, - "Name": "Stygian Abyss", - "ClientFlags": "Felucca,Trammel,Ilshenar,Malas,Tokuno,TerMur", - "MapSelectionFlags": { - "Felucca": true, - "Trammel": true, - "Ilshenar": true, - "Malas": true, - "Tokuno": true, - "TerMur": true - } -} -``` - -The `Id` field corresponds to the expansion's numeric value (0 = None, 1 = T2A, ..., 8 = SA, etc.). The `MapSelectionFlags` control which maps are available to players. - -A companion file, `expansions.json`, contains the full metadata for all expansions, including supported features, character creation flags, and housing flags. The server uses this as a reference when applying `expansion.json`. - ---- - -## Best Practices - -- **Use `Core.XYZ` properties** -- Write `Core.AOS`, not `Core.Expansion >= Expansion.AOS`. The properties are clearer and less error-prone. -- **Chain ternaries from newest to oldest** -- `Core.SE ? x : Core.AOS ? y : z` reads naturally and avoids logic bugs. -- **Test both branches** -- When adding era-conditional code, verify behavior on both sides of the branch. A feature that works on AOS but crashes on pre-AOS (or vice versa) is a bug. -- **Use era-aware `LootPack` properties** -- Do not hardcode loot tables. The built-in properties handle era selection automatically. -- **Do not assume an era** -- If you are unsure which expansion a piece of code should target, ask. The correct branch points depend on the specific mechanic. diff --git a/website/content/development/items-and-mobiles.mdx b/website/content/development/items-and-mobiles.mdx deleted file mode 100644 index c5d747028..000000000 --- a/website/content/development/items-and-mobiles.mdx +++ /dev/null @@ -1,418 +0,0 @@ ---- -sidebar_position: 1 -title: Items & Mobiles ---- - -# Items & Mobiles - -This guide covers the most common content creation tasks: building items and creatures for your shard. - ---- - -## Creating an Item - -### Minimal Item - -Every item needs `[SerializationGenerator]`, a `partial` class, and a `[Constructible]` constructor: - -```csharp -using ModernUO.Serialization; - -namespace Server.Items; - -[SerializationGenerator(0)] -public partial class SimpleItem : Item -{ - [Constructible] - public SimpleItem() : base(0x1234) - { - Weight = 1.0; - } - - public override string DefaultName => "a simple item"; -} -``` - -- `0x1234` is the item graphic ID from UO art files. -- `DefaultName` sets the tooltip name. Use `LabelNumber` for cliloc-based names instead. - -### Full Item Example - -A complete item with serialized fields, a timer, property list, and double-click behavior: - -```csharp -using ModernUO.Serialization; - -namespace Server.Items; - -[SerializationGenerator(0)] -public partial class MagicLantern : Item -{ - [SerializableField(0)] - [InvalidateProperties] - [SerializedCommandProperty(AccessLevel.GameMaster)] - private int _charges; - - [SerializableField(1)] - [SerializedCommandProperty(AccessLevel.GameMaster)] - private Mobile _owner; - - private TimerExecutionToken _glowTimer; - - [Constructible] - public MagicLantern() : base(0xA25) - { - _charges = Utility.RandomMinMax(5, 15); - Weight = 2.0; - Light = LightType.Circle300; - StartGlow(); - } - - public override string DefaultName => "a magic lantern"; - - private void StartGlow() - { - Timer.StartTimer(TimeSpan.FromSeconds(3), TimeSpan.FromSeconds(3), Glow, out _glowTimer); - } - - [AfterDeserialization] - private void AfterDeserialization() => StartGlow(); - - public override void OnAfterDelete() - { - _glowTimer.Cancel(); - base.OnAfterDelete(); - } - - private void Glow() - { - if (_charges > 0) - { - Effects.SendLocationParticles(this, 0x376A, 9, 10, 5042); - } - } - - public override void OnDoubleClick(Mobile from) - { - if (!IsChildOf(from.Backpack)) - { - from.SendLocalizedMessage(1042001); // Must be in your backpack - return; - } - - if (_charges <= 0) - { - from.SendMessage("The lantern is depleted."); - return; - } - - Charges--; - from.SendMessage("The lantern flares brightly!"); - from.FixedParticles(0x376A, 9, 32, 5042, EffectLayer.Waist); - } - - public override void GetProperties(IPropertyList list) - { - base.GetProperties(list); - list.Add(1060741, $"{_charges}"); // "charges: ~1_val~" - } -} -``` - -Key patterns: -- **`TimerExecutionToken`** is never serialized -- restart it in `[AfterDeserialization]`. -- **`[InvalidateProperties]`** auto-refreshes the tooltip when `Charges` changes. -- **`[SerializedCommandProperty]`** exposes the field to the `[Props` gump for GMs. -- **`OnAfterDelete`** cancels the timer to prevent it firing on a deleted entity. - ---- - -## Common Base Classes - -| Base Class | Use For | -|:-----------|:--------| -| `Item` | Generic items | -| `BaseWeapon` | Melee weapons | -| `BaseRanged` | Ranged weapons (bows, crossbows) | -| `BaseArmor` | Armor pieces | -| `BaseShield` | Shields | -| `BaseClothing` | Wearable clothing | -| `BaseJewel` | Rings, bracelets, necklaces | -| `BaseContainer` | Containers (bags, boxes, chests) | -| `BasePotion` | Potions | -| `Food` | Edible items | -| `SpellScroll` | Spell scrolls | - ---- - -## Key Item Properties - -Set these in the constructor: - -```csharp -Weight = 1.0; // Weight in stones -Stackable = true; // Can stack with same type -Amount = 1; // Stack amount -Movable = true; // Can be picked up -Hue = 0; // Color (0 = default) -LootType = LootType.Regular; // Regular, Newbied, Blessed, Cursed -Layer = Layer.OneHanded; // Equipment layer -Light = LightType.Circle300; // Light emission -``` - ---- - -## Creating a Creature - -### Basic Creature - -Creatures extend `BaseCreature` and define stats, resistances, skills, and loot: - -```csharp -using ModernUO.Serialization; -using Server.Items; - -namespace Server.Mobiles; - -[SerializationGenerator(0)] -public partial class ForestWolf : BaseCreature -{ - [Constructible] - public ForestWolf() : base(AIType.AI_Melee, FightMode.Closest) - { - Body = 225; - BaseSoundID = 0xE5; - - SetStr(80, 120); - SetDex(90, 110); - SetInt(20, 40); - - SetHits(60, 80); - SetMana(0); - - SetDamage(8, 14); - - SetDamageType(ResistanceType.Physical, 100); - - SetResistance(ResistanceType.Physical, 25, 35); - SetResistance(ResistanceType.Fire, 5, 10); - SetResistance(ResistanceType.Cold, 15, 25); - SetResistance(ResistanceType.Poison, 10, 15); - SetResistance(ResistanceType.Energy, 5, 10); - - SetSkill(SkillName.MagicResist, 30.0, 50.0); - SetSkill(SkillName.Tactics, 50.0, 70.0); - SetSkill(SkillName.Wrestling, 50.0, 70.0); - - Fame = 600; - Karma = 0; - - VirtualArmor = 28; - - Tamable = true; - ControlSlots = 1; - MinTameSkill = 50.1; - } - - public override string CorpseName => "a wolf corpse"; - public override string DefaultName => "a forest wolf"; - public override int Meat => 1; - public override int Hides => 6; - public override HideType HideType => HideType.Regular; - public override FoodType FavoriteFood => FoodType.Meat; - public override PackInstinct PackInstinct => PackInstinct.Canine; - - public override void GenerateLoot() - { - AddLoot(LootPack.Meager); - } -} -``` - -### Optional Creature Overrides - -```csharp -public override Poison PoisonImmune => Poison.Regular; -public override Poison HitPoison => Poison.Lesser; -public override double HitPoisonChance => 0.2; -public override bool CanRummageCorpses => true; -public override bool BardImmune => true; -public override bool Unprovokable => true; -public override bool CanFly => true; -public override int TreasureMapLevel => 3; -public override double WeaponAbilityChance => 0.4; -``` - ---- - -## AI Types - -| AIType | Use For | -|:-------|:--------| -| `AI_Melee` | Warriors, melee fighters | -| `AI_Mage` | Spellcasters | -| `AI_Archer` | Ranged attackers | -| `AI_Animal` | Passive animals (flee when hurt) | -| `AI_Predator` | Hunting animals | -| `AI_Healer` | Healing NPCs | -| `AI_Vendor` | Shop NPCs | - ---- - -## Fight Modes - -| FightMode | Behavior | -|:----------|:---------| -| `None` | Never attacks | -| `Aggressor` | Only retaliates when attacked | -| `Strongest` | Targets highest-stat enemy | -| `Weakest` | Targets lowest-stat enemy | -| `Closest` | Targets nearest enemy | -| `Evil` | Attacks aggressors or evil-karma targets | - ---- - -## Creature Stats Guide - -Use these ranges as a baseline when creating creatures: - -| Level | Str | Dex | Int | Hits | Damage | Fame | -|:------|:----|:----|:----|:-----|:-------|:-----| -| Weak | 30--60 | 30--50 | 10--20 | 20--40 | 2--6 | 100--300 | -| Average | 80--120 | 60--90 | 20--40 | 60--100 | 6--14 | 500--1,500 | -| Strong | 150--250 | 80--120 | 50--100 | 120--200 | 12--22 | 2,000--5,000 | -| Elite | 300--500 | 100--150 | 100--200 | 250--500 | 18--30 | 5,000--15,000 | -| Boss | 500--1,000 | 150--250 | 200--400 | 500--2,000 | 25--40 | 15,000+ | - ---- - -## Loot System - -### Predefined Loot Packs - -Use `AddLoot` in `GenerateLoot()` to assign standard loot tiers: - -```csharp -public override void GenerateLoot() -{ - AddLoot(LootPack.Poor); // ~50 gold equivalent - AddLoot(LootPack.Meager); // ~100 gold equivalent - AddLoot(LootPack.Average); // ~250 gold equivalent - AddLoot(LootPack.Rich); // ~500 gold equivalent - AddLoot(LootPack.FilthyRich); // ~1,000 gold equivalent - AddLoot(LootPack.UltraRich); // ~2,000 gold equivalent - AddLoot(LootPack.SuperBoss); // Boss-level loot - - // Auxiliary packs - AddLoot(LootPack.Gems, 2); // 2 random gems - AddLoot(LootPack.Potions); // Random potion - AddLoot(LootPack.LowScrolls); // Circle 1--4 scroll - AddLoot(LootPack.MedScrolls); // Circle 5--6 scroll - AddLoot(LootPack.HighScrolls); // Circle 7--8 scroll -} -``` - -Packs automatically select era-appropriate loot based on the server expansion. - -### Specific Items - -For items that always drop, add them directly: - -```csharp -PackItem(new Arrow(Utility.RandomMinMax(20, 40))); -PackGold(100, 200); -PackItem(new Bandage(Utility.RandomMinMax(5, 10))); -``` - ---- - -## Property Lists (Tooltips) - -Override `GetProperties` to customize what players see when hovering over your item: - -```csharp -public override void GetProperties(IPropertyList list) -{ - base.GetProperties(list); // Always call base first - - // Cliloc with a value argument - list.Add(1060741, $"{_charges}"); // "charges: ~1_val~" - - // Key-value pair (string constants must be holes) - list.Add(1060658, $"{"Quality"}\t{_quality}"); // "~1_val~: ~2_val~" - - // Raw string line - list.Add($"{"Crafted with care"}"); -} -``` - -:::warning -String literals in interpolated property list arguments **must** be wrapped as holes: `$"{"Map"}\t{value}"` not `$"Map\t{value}"`. The handler treats bare text as delimiters and `{}` holes as arguments. Only `\t` should be a bare literal. -::: - -Use `[InvalidateProperties]` on serialized fields to auto-refresh tooltips when values change. - ---- - -## Entity Lifecycle - -Entities go through a two-phase deletion process: - -```csharp -// Phase 1: Pre-removal -- cancel timers, unregister from systems -public override void OnDelete() -{ - _timerToken.Cancel(); - base.OnDelete(); -} - -// Phase 2: Post-removal -- null out references -public override void OnAfterDelete() -{ - _timer?.Stop(); - _timer = null; - _owner = null; - base.OnAfterDelete(); -} -``` - -| Phase | Method | What to Do | -|:------|:-------|:-----------| -| Pre-removal | `OnDelete()` | Cancel `TimerExecutionToken`, unregister from tracking systems | -| Post-removal | `OnAfterDelete()` | Stop and null `Timer` references, null `Item`/`Mobile` references | - ---- - -## File Organization - -Place new content files under `Projects/UOContent/` following this structure: - -``` -Projects/UOContent/ - Items/ - Weapons/Swords/ # Swords - Weapons/Maces/ # Maces - Weapons/Ranged/ # Bows, crossbows - Armor/Plate/ # Plate armor - Armor/Leather/ # Leather armor - Clothing/ # Wearable clothing - Containers/ # Bags, boxes, chests - Misc/ # General items - Special/ # Unique or quest items - Resources/ # Crafting materials - Mobiles/ - Animals/Bears/ # Bears - Animals/Birds/ # Birds - Monsters/AOS/ # AOS-era monsters - Monsters/SE/ # SE-era monsters - Monsters/ML/ # ML-era monsters - Special/ # Champions, bosses - Vendors/ # NPC vendors - Townfolk/ # NPCs -``` - -**Naming rules:** -- File name matches the primary class name. -- One primary class per file. -- Group related items in subdirectories. -- Era-specific content goes in era-named subdirectories. diff --git a/website/content/development/serialization.mdx b/website/content/development/serialization.mdx deleted file mode 100644 index e10e1fd15..000000000 --- a/website/content/development/serialization.mdx +++ /dev/null @@ -1,569 +0,0 @@ ---- -sidebar_position: 2 -title: Serialization ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -# Serialization - -ModernUO uses a source generator-based serialization system. Decorate fields with attributes, and the generator produces `Serialize()` / `Deserialize()` methods automatically. - ---- - -## When to Use Which - -| Approach | Use For | -|:---------|:--------| -| **Code Generation** | Items, Mobiles, and any class inheriting `ISerializable` | -| **Generic Persistence** | Global systems, lookup tables, non-entity data | -| **Entity Persistence** | Custom types that need their own `Serial` and parallel serialization (like Items/Mobiles) | - ---- - - - - -### Before and After - -**Old way** -- manual serialization: -```csharp -public class ExampleItem : Item -{ - private string _exampleText; - - [CommandProperty(AccessLevel.GameMaster)] - public string ExampleText - { - get => _exampleText; - set - { - if (value != _exampleText) - { - _exampleText = value; - this.MarkDirty(); - } - } - } - - [Constructible] - public ExampleItem(string text) : base(0) - { - _exampleText = text; - } - - public ExampleItem(Serial serial) : base(serial) { } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - writer.WriteEncodedInt(0); - writer.Write(_exampleText); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - var version = reader.ReadEncodedInt(); - _exampleText = reader.ReadString(); - } -} -``` - -**New way** -- source-generated: -```csharp -[SerializationGenerator(0)] -public partial class ExampleItem : Item -{ - [SerializableField(0)] - [SerializedCommandProperty(AccessLevel.GameMaster)] - private string _exampleText; - - [Constructible] - public ExampleItem(string text) : base(0) - { - _exampleText = text; - } -} -``` - -### Step by Step - -1. Add `[SerializationGenerator(version)]` and make the class `partial`: - ```csharp - [SerializationGenerator(0)] - public partial class ExampleItem : Item - ``` -2. Delete the `Serial` constructor. -3. Delete the `Serialize` and `Deserialize` methods. -4. Add `[SerializableField(index)]` to each field you want saved: - ```csharp - [SerializableField(0)] - private string _exampleText; - ``` -5. Run `publish.cmd` (or `publish.sh`) to generate migration files. - -The generator creates `Namespace.TypeName.v0.json` and `Namespace.TypeName.Serialization.cs` for you. - - - - -### Global System Data - -Use `GenericPersistence` for global data that doesn't belong to a specific entity. Subclass it and implement `Serialize` / `Deserialize`. - -Here's a real example based on the disguise system, which tracks active disguise timers per player: - -```csharp -using System; -using System.Collections.Generic; - -namespace Server.Items; - -public class DisguisePersistence : GenericPersistence -{ - private static DisguisePersistence _instance; - public static Dictionary Timers { get; } = new(); - - public static void Configure() - { - _instance = new DisguisePersistence(); - } - - public DisguisePersistence() : base("Disguises", 10) - { - } - - public override void Serialize(IGenericWriter writer) - { - writer.WriteEncodedInt(Timers.Count); - foreach (var (m, timer) in Timers) - { - writer.Write(m); - writer.Write(timer.Next - Core.Now); - writer.Write(m.NameMod); - } - } - - public override void Deserialize(IGenericReader reader) - { - var count = reader.ReadEncodedInt(); - for (var i = 0; i < count; ++i) - { - var m = reader.ReadEntity(); - var delay = reader.ReadTimeSpan(); - var nameMod = reader.ReadString(); - // Restore timer and state - CreateTimer(m, delay); - m.NameMod = nameMod; - } - } - - public static void CreateTimer(Mobile m, TimeSpan delay) { /* ... */ } -} -``` - -Key points: - -- The constructor takes a **name** (used for the save file path) and a **priority** (load order). -- `Configure()` is automatically discovered and called at startup. -- Data is saved to `Saves/Disguises/Disguises.bin`. -- You are responsible for reading/writing in the exact same order. - - - - -### Custom Entity Types - -Use `GenericEntityPersistence` when you need custom types with their own `Serial` that are serialized in parallel -- the same way Items and Mobiles work internally. Each entity gets its own serialization thread for parallel world saves. - -#### 1. Define the persistence manager - -```csharp -namespace Server.Engines.BulkOrders; - -public class BOBEntries : GenericEntityPersistence -{ - private static BOBEntries _instance; - - public static void Configure() - { - _instance = new BOBEntries(); - } - - // name, priority, minSerial, maxSerial - public BOBEntries() : base("BOBEntries", 3, 0x1, 0x7FFFFFFF) - { - } - - public static Serial NewBOBEntry => _instance.NewEntity; - public static void Add(IBOBEntry entity) => _instance.AddEntity(entity); - public static void Remove(IBOBEntry entity) => _instance.RemoveEntity(entity); -} -``` - -#### 2. Define the entity base class - -The base class uses standard `[SerializationGenerator]` attributes and manages its own `Serial`: - -```csharp -[SerializationGenerator(1)] -public abstract partial class BaseBOBEntry : IBOBEntry -{ - [SerializableField(0, setter: "protected")] - private bool _requireExceptional; - - [SerializableField(1, setter: "protected")] - private BODType _deedType; - - [SerializableField(2, setter: "protected")] - private BulkMaterialType _material; - - [SerializableField(3, setter: "protected")] - private int _amountMax; - - [SerializableField(4)] - private int _price; - - public Serial Serial { get; } - public bool Deleted { get; private set; } - - public BaseBOBEntry() - { - Serial = BOBEntries.NewBOBEntry; - BOBEntries.Add(this); - } - - public virtual void Delete() - { - Deleted = true; - BOBEntries.Remove(this); - } -} -``` - -Concrete subclasses inherit from this base and add their own serialized fields, just like how specific items inherit from `Item`. - -:::caution -Each entity has approximately **32 bytes of indexing overhead** regardless of its data size. Don't use entity persistence for lightweight or high-volume data where `GenericPersistence` would suffice. Benchmark your world save sizes and times before committing to this pattern. -::: - - - - ---- - -## Attribute Reference - -### Class-Level Attributes - -| Attribute | Target | Description | -|:----------|:-------|:------------| -| `[SerializationGenerator(version)]` | Class | Enables code generation. `version` is the current serialization version number. | -| `[Constructible]` | Constructor | Marks the constructor as available for the `[add` command. | -| `[TypeAlias("OldName")]` | Class | Maps old type names for deserialization of legacy saves. | - -### Field-Level Attributes - -| Attribute | Target | Description | -|:----------|:-------|:------------| -| `[SerializableField(index)]` | Private field | Marks field for serialization at the given index. Generates a public PascalCase property. | -| `[InvalidateProperties]` | Serializable field | Auto-calls `InvalidateProperties()` in the generated setter to refresh tooltips. | -| `[SerializedCommandProperty(level)]` | Serializable field | Exposes the generated property to the `[Props` gump for in-game editing. | -| `[DeltaDateTime]` | `DateTime` field | Stores as offset from current time. Ensures expiration dates survive restarts. | -| `[EncodedInt]` | `int` field | Uses variable-length encoding (1 byte for 0--127, 2 bytes for 128--16383, etc.). | -| `[InternString]` | `string` field | Deduplicates identical strings in memory via `string.Intern()`. | -| `[Tidy]` | Collection field | Removes null and deleted entries after deserialization. | - -### Method-Level Attributes - -| Attribute | Target | Description | -|:----------|:-------|:------------| -| `[AfterDeserialization]` | Private method | Called after fields are loaded. Use this to restart timers and set up derived state. | -| `[DeserializeTimerField(index)]` | Method taking `TimeSpan` | Custom deserialization for `Timer` fields. The timer is saved as remaining delay. | - ---- - -## Serializable Fields in Detail - -### Basic Field - -```csharp -[SerializableField(0)] -private int _charges; -``` - -The generator creates: -```csharp -public int Charges -{ - get => _charges; - set { _charges = value; this.MarkDirty(); } -} -``` - -### Field with Tooltip Refresh and GM Access - -```csharp -[SerializableField(0)] -[InvalidateProperties] -[SerializedCommandProperty(AccessLevel.GameMaster)] -private int _charges; -``` - -### Private or Internal Setter - -```csharp -[SerializableField(0, setter: "private")] -private string _name; -``` - -### Custom Property Logic - -Use `[SerializableProperty]` when you need non-trivial getter/setter logic: - -```csharp -[SerializableProperty(0)] -[CommandProperty(AccessLevel.GameMaster)] -public int MaxItems -{ - get => _maxItems == -1 ? DefaultMaxItems : _maxItems; - set - { - _maxItems = value; - InvalidateProperties(); - this.MarkDirty(); // REQUIRED in custom setters - } -} -``` - ---- - -## Version Migration - -When you add, remove, or reorder serialized fields, bump the version number. - -### Adding a Field - -```csharp -// Version 0 had only _charges. Version 1 adds _quality. -[SerializationGenerator(1)] -public partial class MagicGem : Item -{ - [SerializableField(0)] - private int _charges; - - [SerializableField(1)] // New in v1 - private GemQuality _quality; - - [Constructible] - public MagicGem() : base(0x1EA7) - { - _charges = Utility.RandomMinMax(5, 15); - _quality = GemQuality.Rough; - } -} -``` - -After running `publish`, the generator creates a `V0Content` struct. You must provide a migration: - -```csharp -// In MagicGem.Migrations.cs (separate partial file) -public partial class MagicGem -{ - private void MigrateFrom(V0Content content) - { - _charges = content.Charges; - // _quality gets its default value (GemQuality.Rough) - } -} -``` - -### The MigrateFrom Pattern - -- Method signature: `private void MigrateFrom(VXContent content)` where `X` is the **previous** version. -- `VXContent` is auto-generated with PascalCase properties matching the old fields. -- New fields not present in the old version get their default values. -- Add one `MigrateFrom` for each older version that needs a migration path. - -:::tip -Since the class is `partial`, create a standalone `MyClass.Migrations.cs` file to keep migrations organized. -::: - -### Migrating from Pre-Codegen - -To migrate a class that previously used manual `Serialize`/`Deserialize`: - -1. Set `encoded` to `false` if the old code used `reader.ReadInt()` for the version: - ```csharp - [SerializationGenerator(3, false)] // Old version was 2, bumped to 3 - ``` -2. Keep the old deserialization logic as a private method: - ```csharp - private void Deserialize(IGenericReader reader, int version) - { - // Old deserialization logic here - } - ``` - -This method is called automatically for saves that predate the serialization generator. - -:::warning -**Never** modify `Deserialize(IGenericReader reader, int version)` for post-codegen version bumps. That method only handles legacy (pre-codegen) saves. All new version transitions must use `MigrateFrom`. -::: - ---- - -## After Deserialization - -Use `[AfterDeserialization]` to run code after an entity's fields are loaded: - -```csharp -[AfterDeserialization] -private void AfterDeserialization() -{ - // Restart timers, compute derived values - Timer.StartTimer(TimeSpan.FromSeconds(5), CheckExpiry, out _timerToken); -} -``` - -The attribute accepts an optional `synchronous` parameter: - -| Value | Timing | Use When | -|:------|:-------|:---------| -| `true` (default) | Immediately after this entity loads | Restarting timers, setting up derived state from own fields | -| `false` | After **all** entities in the world are loaded | Logic that depends on other entities, calls `Delete()`, or affects game state | - -```csharp -[AfterDeserialization(false)] -private void AfterDeserialization() -{ - if (_expireTime < Core.Now) - { - Delete(); // Safe -- all entities are loaded - } -} -``` - ---- - -## Important Rules - -1. **Class must be `partial`** -- the generator adds code to your class via a separate file. -2. **`TimerExecutionToken` must NOT have `[SerializableField]`** -- it is not serializable. Restart timers in `[AfterDeserialization]`. -3. **Call `this.MarkDirty()`** in any custom property setter to flag the entity for saving. -4. **Use `[AfterDeserialization]`** to restart timers after world load -- never create timers inside the deserialization path directly. -5. **For new classes**, omit the `encoded` parameter: `[SerializationGenerator(0)]`. -6. **Field index order matters** -- fields are serialized/deserialized in index order. Never reorder without bumping the version. - ---- - -## Complete Example - -```csharp -using ModernUO.Serialization; - -namespace Server.Items; - -public enum GemQuality { Rough, Cut, Flawless } - -[SerializationGenerator(1)] -public partial class MagicGem : Item -{ - [SerializableField(0)] - [InvalidateProperties] - [SerializedCommandProperty(AccessLevel.GameMaster)] - private int _charges; - - [SerializableField(1)] - [InvalidateProperties] - [SerializedCommandProperty(AccessLevel.GameMaster)] - private GemQuality _quality; - - private TimerExecutionToken _pulseTimer; - - [Constructible] - public MagicGem() : base(0x1EA7) - { - _charges = Utility.RandomMinMax(5, 15); - _quality = GemQuality.Rough; - Weight = 1.0; - Light = LightType.Circle150; - StartPulse(); - } - - public override string DefaultName => "a magic gem"; - - private void StartPulse() - { - Timer.StartTimer( - TimeSpan.FromSeconds(3), - TimeSpan.FromSeconds(3), - Pulse, - out _pulseTimer - ); - } - - [AfterDeserialization] - private void AfterDeserialization() => StartPulse(); - - public override void OnAfterDelete() - { - _pulseTimer.Cancel(); - base.OnAfterDelete(); - } - - private void Pulse() - { - if (_charges <= 0) - { - _pulseTimer.Cancel(); - return; - } - - Effects.SendLocationParticles(this, 0x376A, 9, 10, 5042); - } - - public override void GetProperties(IPropertyList list) - { - base.GetProperties(list); - list.Add(1060741, $"{_charges}"); - list.Add($"{"Quality: "}{_quality}"); - } - - public override void OnDoubleClick(Mobile from) - { - if (!IsChildOf(from.Backpack)) - { - from.SendLocalizedMessage(1042001); - return; - } - - if (_charges <= 0) - { - from.SendMessage("The gem is depleted."); - return; - } - - _charges--; - InvalidateProperties(); - this.MarkDirty(); - from.SendMessage("The gem pulses with energy!"); - } -} -``` - -Migration file (`MagicGem.Migrations.cs`): -```csharp -namespace Server.Items; - -public partial class MagicGem -{ - private void MigrateFrom(V0Content content) - { - _charges = content.Charges; - // _quality defaults to GemQuality.Rough - } -} -``` diff --git a/website/content/development/timers.mdx b/website/content/development/timers.mdx deleted file mode 100644 index 0ef79876b..000000000 --- a/website/content/development/timers.mdx +++ /dev/null @@ -1,280 +0,0 @@ ---- -sidebar_position: 3 -title: Timers ---- - -# Timers - -ModernUO uses a hierarchical timer wheel for scheduling delayed and recurring actions. The system is single-threaded, lock-free, and processes timers during each game loop tick. - ---- - -## Overview - -The timer wheel has 3 layers with 4,096 slots each: - -| Layer | Resolution | Range | -|:------|:-----------|:------| -| 0 | 8ms | ~32.8 seconds | -| 1 | ~32.8s | ~22 minutes | -| 2 | ~22m | ~16 days | - -Key characteristics: -- **O(1) insert and remove** -- adding thousands of timers does not slow the server. -- **No locks** -- the entire system runs on the main game thread. -- **No `TimerPriority`** -- this concept from RunUO is removed entirely. -- **8ms minimum precision** -- all delays round up to the nearest 8ms boundary. - ---- - -## Timer.StartTimer (Preferred) - -The primary API for creating timers. Timers are automatically pooled for reuse. - -### Immediate Execution - -```csharp -Timer.StartTimer(callback); -``` - -### Delayed Execution - -```csharp -Timer.StartTimer(TimeSpan.FromSeconds(5), callback); -``` - -### Repeating - -```csharp -// Repeat every second, starting after 1 second -Timer.StartTimer(TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(1), callback); -``` - -### Repeating with Count Limit - -```csharp -// Execute 10 times, once per second, starting immediately -Timer.StartTimer(TimeSpan.FromSeconds(0), TimeSpan.FromSeconds(1), 10, callback); -``` - -### Delayed Start, Then Repeating - -```csharp -// Wait 5 seconds, then repeat every second -Timer.StartTimer(TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(1), callback); -``` - ---- - -## Cancellation with TimerExecutionToken - -When you need to cancel a timer later, pass an `out` token: - -```csharp -private TimerExecutionToken _token; - -// Start a cancellable repeating timer -Timer.StartTimer( - TimeSpan.FromSeconds(5), - TimeSpan.FromSeconds(5), - DoWork, - out _token -); - -// Cancel the timer (safe to call multiple times) -_token.Cancel(); -``` - -### Token Properties - -| Property | Type | Description | -|:---------|:-----|:------------| -| `Running` | `bool` | Whether the timer is still active | -| `RemainingCount` | `int` | Ticks remaining (`int.MaxValue` if infinite) | -| `Next` | `DateTime` | When the next tick fires | -| `Index` | `int` | How many times `OnTick` has fired so far | - -### Lifecycle Pattern - -Always cancel tokens when the owning entity is deleted: - -```csharp -private TimerExecutionToken _checkTimer; - -[Constructible] -public MyItem() : base(0x1234) -{ - Timer.StartTimer(TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(5), Check, out _checkTimer); -} - -public override void OnAfterDelete() -{ - _checkTimer.Cancel(); - base.OnAfterDelete(); -} -``` - -:::warning -`TimerExecutionToken` is **not serializable**. Never add `[SerializableField]` to a token. Restore timers in `[AfterDeserialization]` instead. -::: - ---- - -## Timer.DelayCall (Legacy) - -Returns a `Timer` object directly. Useful when you need state parameters to avoid lambda allocation: - -```csharp -// Basic delay call -var timer = Timer.DelayCall(TimeSpan.FromSeconds(5), DoWork); -timer.Stop(); // Cancel - -// With state parameters (no closure allocation) -Timer.DelayCall(TimeSpan.FromSeconds(2), ProcessTarget, mobile, item); - -// Supports up to 5 state parameters -Timer.DelayCall(TimeSpan.FromSeconds(1), DoWork, arg1, arg2, arg3); -``` - -### When to Use DelayCall - -Prefer `Timer.StartTimer` for most cases. Use `Timer.DelayCall` when: -- You need to pass state parameters to avoid lambda/closure allocation on hot paths. -- You need the `Timer` object reference for advanced control. - ---- - -## Timer Restoration After Deserialization - -Timers do not survive server restarts. Save the relevant timing data as a serialized field, then restart the timer after the world loads. - -### Pattern: Save Expiration Time - -```csharp -[SerializationGenerator(0)] -public partial class TimedItem : Item -{ - private TimerExecutionToken _timer; // NOT serialized - - [SerializableField(0)] - [DeltaDateTime] - private DateTime _expireTime; - - [Constructible] - public TimedItem() : base(0x1234) - { - _expireTime = Core.Now + TimeSpan.FromHours(1); - StartTimer(); - } - - private void StartTimer() - { - Timer.StartTimer(TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(1), Check, out _timer); - } - - [AfterDeserialization] - private void AfterDeserialization() => StartTimer(); - - public override void OnAfterDelete() - { - _timer.Cancel(); - base.OnAfterDelete(); - } - - private void Check() - { - if (Core.Now >= _expireTime) - { - Delete(); - } - } -} -``` - -Key points: -- **`[DeltaDateTime]`** stores the time as an offset from `Core.Now`, so it adjusts correctly if the server is down for a while. -- **`[AfterDeserialization]`** runs after the entity's fields are loaded -- this is where you restart timers. -- The `TimerExecutionToken` field has no serialization attribute. - -### Pattern: Timer Field with DeserializeTimerField - -For `Timer` objects (not tokens), use `[DeserializeTimerField]`: - -```csharp -[SerializableField(0, setter: "private")] -private Timer _decayTimer; - -[DeserializeTimerField(0)] -private void DeserializeDecayTimer(TimeSpan delay) -{ - _decayTimer = Timer.DelayCall(delay, Delete); - _decayTimer.Start(); -} - -public override void OnAfterDelete() -{ - _decayTimer?.Stop(); - _decayTimer = null; - base.OnAfterDelete(); -} -``` - -The serialization system saves the remaining delay and passes it to your deserialize method. - ---- - -## Common Mistakes - -| Mistake | Problem | Fix | -|:--------|:--------|:----| -| Adding `[SerializableField]` to `TimerExecutionToken` | Build error or data corruption | Leave unserialized; use `[AfterDeserialization]` to restart | -| Not cancelling timer on delete | Timer fires on a deleted entity, causing errors | Cancel in `OnAfterDelete()` | -| Using `Thread.Sleep` | Blocks the entire game loop | Use `await Timer.Pause()` | -| Creating timer inside deserialization | Timer starts before the world is fully loaded | Use `[AfterDeserialization]` | -| Lambda capturing state in hot-path timer | Allocates a closure object every invocation | Use `Timer.DelayCall` with state parameters | - -### Avoiding Lambda Allocation - -```csharp -// BAD on hot paths -- allocates a closure each time -Timer.StartTimer(TimeSpan.FromSeconds(2), () => ProcessTarget(from, target)); - -// GOOD -- state parameters, no allocation -Timer.DelayCall(TimeSpan.FromSeconds(2), ProcessTarget, from, target); - -private static void ProcessTarget(Mobile from, Mobile target) -{ - // Process... -} -``` - ---- - -## Quick Reference - -### Fire-and-Forget - -```csharp -// One-shot after 10 seconds -Timer.StartTimer(TimeSpan.FromSeconds(10), Delete); -``` - -### Cancellable Repeating Timer - -```csharp -private TimerExecutionToken _token; - -Timer.StartTimer(TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(5), Tick, out _token); - -// Later: -_token.Cancel(); -``` - -### Awaitable Pause - -```csharp -await Timer.Pause(TimeSpan.FromMilliseconds(100)); -await Timer.Pause(500); // Milliseconds overload -``` - -This is safe because `EventLoopContext` routes continuations back to the main thread. diff --git a/website/content/getting-started/building.mdx b/website/content/getting-started/building.mdx deleted file mode 100644 index 711c89046..000000000 --- a/website/content/getting-started/building.mdx +++ /dev/null @@ -1,95 +0,0 @@ ---- -sidebar_position: 2 -title: Creating a Build ---- - -import OsTabs from '@site/src/components/OsTabs'; -import CodeBlock from '@theme/CodeBlock'; -import Admonition from '@theme/Admonition'; - -# Creating a Build - -ModernUO includes a build tool that handles prerequisite checks, platform detection, and compilation. Run it through the publish script for your platform. - -## Interactive Mode *(recommended)* - -Run the publish script with no arguments to launch the guided setup wizard. It will check your environment, detect your platform, and walk you through the build process. - - -{{ - windows: ( - {'./publish.cmd'} - ), - macos: ( - {'./publish.sh'} - ), - linux: ( - {'./publish.sh'} - ), -}} - - -The interactive mode will: -- Verify your .NET SDK version and offer to install it if missing -- Check for required native libraries on your platform -- Let you choose between Debug and Release builds -- Select your target OS and architecture -- Run the full publish pipeline - -:::tip -Interactive mode is the best way to get started. It catches missing dependencies before they become build errors. -::: - -## Command Line Mode - -For scripting or CI environments, pass arguments directly to skip the wizard. - - -{{ - windows: ( - {'./publish.cmd release win x64'} - ), - macos: ( - {'./publish.sh release osx x64'} - ), - linux: ( - {'./publish.sh release linux x64'} - ), -}} - - -The general format is: - -``` -publish [os] [arch] -``` - -### Build Mode - -| Value | Description | -|-------|-------------| -| `release` | Optimized for production | -| `debug` | Includes debug symbols for development | - -### Target OS *(optional)* - -Defaults to the current operating system if omitted. - -| Value | Platform | -|-------|----------| -| `win` | Windows | -| `osx` | macOS | -| `linux` | Linux | - -### Target Architecture *(optional)* - -Defaults to `x64` if omitted. - -| Value | Architecture | -|-------|-------------| -| `x64` | 64-bit x86 | -| `arm64` | ARM 64-bit | - -:::tip -Cross-compilation is supported — you can build for any OS/architecture combination from any platform. -::: diff --git a/website/content/getting-started/configuration.mdx b/website/content/getting-started/configuration.mdx deleted file mode 100644 index b53649d14..000000000 --- a/website/content/getting-started/configuration.mdx +++ /dev/null @@ -1,441 +0,0 @@ ---- -sidebar_position: 4 -title: Configuration ---- - -import CodeBlock from '@theme/CodeBlock'; - -# Configuration - -ModernUO is self-configuring. On first launch, the server walks you through an interactive setup and generates all required configuration files. After that, settings take effect on the next server restart (unless otherwise noted). - -All configuration files live in the `Configuration/` folder inside your `Distribution` directory. - -## Configuration Files - -| File | Purpose | -|------|---------| -| `modernuo.json` | Main server settings -- listeners, data paths, and all `settings` key-value pairs | -| `expansion.json` | Target expansion, enabled maps, and feature/client flags | -| `antimacro.json` | Anti-macro skill gain rules (per-skill toggle, area size, cooldowns) | -| `email-settings.json` | SMTP and crash report email configuration | -| `server-access.json` | Protected accounts that auto-reset to Owner access on login | -| `throttles.json` | Per-packet throttle delays in milliseconds | - -:::tip -If you ever want to re-run the first-launch setup wizard, delete `modernuo.json` and `expansion.json`, then restart the server. -::: - -## modernuo.json Structure - -The main configuration file has four top-level keys: - -```json -{ - "assemblyDirectories": ["./Assemblies"], - "dataDirectories": ["/path/to/uo/game/files"], - "listeners": ["0.0.0.0:2593"], - "settings": { - "accountHandler.maxAccountsPerIP": "1", - "autosave.enabled": "true", - "autosave.saveDelay": "00:05:00" - } -} -``` - -| Key | Type | Description | -|-----|------|-------------| -| `assemblyDirectories` | string array | Directories to search for additional plugin assemblies | -| `dataDirectories` | string array | Paths to Ultima Online game files (`.mul` / `.uop`) | -| `listeners` | string array | IP:port endpoints the server binds to | -| `settings` | key-value map | All configurable settings (string keys and string values) | - -:::note -All values in the `settings` map are stored as strings. The server parses them to the appropriate type (bool, int, TimeSpan, etc.) at startup. Unrecognized keys are silently ignored. -::: - -## Feature Flags - -ModernUO includes a runtime feature flag system for toggling game mechanics without restarting the server. Feature flags are managed by the `FeatureFlagManager` and stored in the `Configuration/FeatureFlags/` directory. - -### Built-in Flags - -These boolean flags are optimized for hot-path checks and are synchronized from UOContent: - -**Server-level flags** (checked in the core engine): - -| Flag Key | Default | Description | -|----------|---------|-------------| -| `player_trading` | `true` | Allow players to trade items | -| `pvp_combat` | `true` | Allow player-vs-player combat | -| `bank_access` | `true` | Allow bank box access | -| `speedhack_detection` | `false` | Enable speed-hack detection via movement analysis | - -**Content-level flags** (checked in UOContent game logic): - -| Flag Key | Default | Description | -|----------|---------|-------------| -| `vendor_purchase` | `true` | Allow buying from NPC vendors | -| `vendor_sell` | `true` | Allow selling to NPC vendors | -| `player_vendors` | `true` | Allow player vendor usage | -| `house_placement` | `true` | Allow placing new houses | -| `boat_placement` | `true` | Allow placing new boats | -| `bulk_orders` | `true` | Allow bulk order deed system | -| `passive_detect_hidden` | `true` | Allow passive detect hidden skill | - -### Additional Blocking - -Beyond boolean flags, the feature flag system also supports blocking specific: - -- **Gumps** -- prevent specific UI dialogs from opening -- **Items** -- block use, equip, or container access for specific item types -- **Skills** -- disable individual skills with a custom message -- **Spells** -- disable individual spells with a custom message - -These are managed through in-game admin commands and the feature flag admin gump, and are persisted as JSON files in `Configuration/FeatureFlags/`. - -## Settings Reference - -The tables below document every setting key available in the `settings` section of `modernuo.json`. Default values marked with an expansion name (e.g., `Core.AOS`) mean the default depends on your configured expansion. - -### Movement - -| Key | Default | Description | -|-----|---------|-------------| -| `movement.delay.runFoot` | `200` | Run speed on foot (ms) | -| `movement.delay.runMount` | `100` | Run speed while mounted (ms) | -| `movement.delay.walkFoot` | `400` | Walk speed on foot (ms) | -| `movement.delay.walkMount` | `200` | Walk speed while mounted (ms) | -| `movement.delay.turn` | `0` | Delay for turning in place (ms) | -| `movement.delay.npcMinIdle` | `15` | Minimum idle time for NPCs (seconds) | -| `movement.delay.npcMaxIdle` | `25` | Maximum idle time for NPCs (seconds) | - -### Movement Throttling - -The movement throttle system uses RTT-based credit buffering to absorb network jitter while detecting speed hacks through movement rate analysis. - -| Key | Default | Description | -|-----|---------|-------------| -| `movementThrottle.debugLogging` | `false` | Enable debug logging for movement throttle decisions | -| `movementThrottle.maxCredit` | `200` | Maximum credit buffer for timing jitter (ms) | -| `movementThrottle.hardQueueLimit` | `10` | Reject and clear movement queue at this depth | -| `movementThrottle.movementHistorySize` | `20` | Circular buffer size for rate analysis | -| `movementThrottle.minSamplesForRate` | `8` | Minimum movements before calculating speed | -| `movementThrottle.suspiciousRateThreshold` | `1.05` | Flag as suspicious at 5% over expected speed | -| `movementThrottle.definiteRateThreshold` | `1.10` | Flag as definite hack at 10% over expected speed | - -:::note -Most movement throttle settings are auto-tuned and rarely need manual adjustment. The `debugLogging` flag is useful for diagnosing false positives. -::: - -### Client Verification - -| Key | Default | Description | -|-----|---------|-------------| -| `clientVerification.enable` | `true` | Enable client version verification | -| `clientVerification.ageLeniency` | `10.00:00:00` (10 days) | Grace period for new accounts before enforcing version checks | -| `clientVerification.gameTimeLeniency` | `1.01:00:00` | Grace period based on game time played | -| `clientVerification.invalidClientResponse` | `Kick` | Action on invalid client: `Kick`, `LenientKick`, `Annoy`, or `None` | -| `clientVerification.kickDelay` | `00:00:20` (20s) | Delay before kicking an invalid client | -| `clientVerification.minRequired` | `null` | Minimum allowed client version (e.g., `7.0.0.0`) | -| `clientVerification.maxRequired` | `null` | Maximum allowed client version | -| `clientVerification.allowedClientTypes` | `Classic \| SA` | Allowed client types bitmask | -| `clientData.clientVersion` | `null` | Override the expected client version | - -### Accounts and Security - -| Key | Default | Description | -|-----|---------|-------------| -| `accountHandler.enableAutoAccountCreation` | `true` | Create accounts automatically on first login | -| `accountHandler.enablePlayerPasswordCommand` | `false` | Allow players to change password via in-game command | -| `accountHandler.maxAccountsPerIP` | `1` | Maximum accounts allowed per IP address | -| `accountSecurity.encryptionAlgorithm` | `Argon2` | Password hashing algorithm (`SHA2`, `PBKDF2`, or `Argon2`) | - -:::note -Algorithms below `SHA2` (such as `MD5`, `SHA1`, `None`) are rejected at startup. They exist only for automatic password migration from legacy RunUO/ServUO databases. -::: - -### World Saves - -| Key | Default | Description | -|-----|---------|-------------| -| `world.savePath` | `Saves` | Directory for world save files (relative to Distribution) | -| `world.tempSavePath` | `temp` | Temporary directory during save operations | -| `world.useMultithreadedSaves` | `true` | Use background threads for serialization during saves | -| `world.enableAutoRestart` | `false` | Automatically restart the server after a crash or shutdown | -| `autosave.enabled` | `true` | Enable automatic world saves | -| `autosave.saveDelay` | `00:05:00` (5 min) | Interval between automatic saves | -| `autosave.warningDelay` | `00:00:00` (0) | Broadcast a warning this long before each save (0 = no warning) | - -### Archives and Backups - -| Key | Default | Description | -|-----|---------|-------------| -| `autoArchive.archiveLocally` | `true` | Archive saves to a local directory | -| `autoArchive.archivePath` | `Archives` | Directory for compressed save archives | -| `autoArchive.backupPath` | `Backups` | Directory for backup copies | -| `autoArchive.compressionLevel` | `3` | Zstd compression level (1-19) | -| `autoArchive.enableArchivePruning` | `true` | Automatically prune old archives based on retention policy | -| `autoArchive.verifyArchives` | `true` | Verify archive integrity after creation | -| `autoArchive.retryCount` | `3` | Number of retries on archive failure | -| `autoArchive.retryDelayMs` | `500` | Delay between retries (ms) | -| `autoArchive.backupMaxAge` | `30` | Maximum age of backup files in days | -| `autoArchive.hourlyRetention` | `24` | Number of hourly archives to keep | -| `autoArchive.dailyRetention` | `30` | Number of daily archives to keep | -| `autoArchive.monthlyRetention` | `12` | Number of monthly archives to keep | - -### Crash Guard - -| Key | Default | Description | -|-----|---------|-------------| -| `crashGuard.enabled` | `true` | Enable the crash guard system | -| `crashGuard.saveBackup` | `true` | Save a backup on crash | -| `crashGuard.restartServer` | `true` | Attempt to restart after a crash | -| `crashGuard.generateReport` | `true` | Generate a crash report file | - -### Stats and Stamina - -**Stat Gain:** - -| Key | Default | Description | -|-----|---------|-------------| -| `stats.statMax` | `Core.LBR ? 125 : 100` | Maximum value for a single stat | -| `stats.gainChanceMultiplier` | `1.0` | Multiplier for stat gain chance | -| `stats.primaryStatGainChance` | `0.75` | Chance to gain in the primary stat | -| `stats.gainDelay` | `Core.ML ? 0.05m : 10m` | Cooldown between stat gain checks | -| `stats.petGainDelay` | `5m` | Cooldown between pet stat gain checks | -| `stats.usePub45StatGain` | `Core.ML` | Use Publish 45 stat gain system | - -**Stamina System:** - -| Key | Default | Description | -|-----|---------|-------------| -| `stamina.cannotRunWhenFatigued` | `!Core.AOS` | Prevent running at zero stamina | -| `stamina.cannotWalkWhenFatigued` | `false` | Prevent walking at zero stamina | -| `stamina.stonesPerOverweightLoss` | `25` | Stones of weight per stamina loss tick | -| `stamina.stonesOverweightAllowance` | `4` | Extra stones allowed before overweight penalty | -| `stamina.baseOverweightLoss` | `5` | Base stamina loss per overweight tick | -| `stamina.additionalLossWhenBelow` | `0.10` | Extra loss rate when stamina is below this fraction | -| `stamina.enableMountStamina` | `true` | Enable stamina drain while mounted | -| `stamina.useMountStaminaOnlyWhenOverloaded` | `Core.SA` | Only drain mount stamina when overloaded | -| `stamina.globalEtherealMountStamina` | `Core.ML` | Apply stamina drain to ethereal mounts | - -### Combat and Systems - -| Key | Default | Description | -|-----|---------|-------------| -| `melee.enableInstaHit` | `!Core.UOR` | Enable instant first melee hit on target switch | -| `spellCasting.disableCastParalyze` | `true` | Prevent casting while paralyzed | -| `actionDelay` | `Core.AOS ? 1000 : 500` | Global action delay in milliseconds | -| `visibleDamage` | `Core.AOS` | Show damage numbers above targets | -| `insurance.enable` | `Core.AOS` | Enable the item insurance system | - -### Player Systems - -**Murder System:** - -| Key | Default | Description | -|-----|---------|-------------| -| `murderSystem.shortTermMurderDuration` | `8h` | Duration of a short-term murder count | -| `murderSystem.longTermMurderDuration` | `40h` | Duration of a long-term murder count | -| `murderSystem.bountiesEnabled` | `!Core.LBR` | Enable the bounty system | -| `murderSystem.recentlyReportedDelay` | `10m` | Cooldown before a victim can report the same murderer | -| `murderSystem.bountyExpiry` | `14d` | Time before an uncollected bounty expires | - -**Stealing:** - -| Key | Default | Description | -|-----|---------|-------------| -| `stealing.classicMode` | `!Core.AOS` | Use classic (pre-AOS) stealing mechanics | -| `stealing.suspendOnMurder` | `!Core.AOS` | Suspend stealing perma-flag on murder | -| `stealing.canStealContainers` | `!Core.AOS` | Allow stealing entire containers | -| `stealing.maxWeightToSteal` | `10` | Maximum weight of an item that can be stolen (stones) | - -**Taming:** - -| Key | Default | Description | -|-----|---------|-------------| -| `taming.enableBonding` | `Core.LBR` | Enable pet bonding | - -### Game Systems - -| Key | Default | Description | -|-----|---------|-------------| -| `opl.enable` | `Core.AOS` | Enable Object Property Lists (item tooltips) | -| `opl.enableForVendorBuy` | `true` | Show property lists in vendor buy menus | -| `vendor.isInvulnerable` | `Core.LBR` | Make NPC vendors invulnerable | -| `guards.instantKill` | `true` | Guards instantly kill criminals (vs. fighting them) | -| `factions.enabled` | `false` | Enable the factions system | -| `ethics.enable` | `false` | Enable the ethics (Hero/Evil) system | -| `questSystem.enableMLQuests` | `Core.ML` | Enable Mondain's Legacy quest system | -| `vetRewards.enable` | `true` | Enable veteran rewards | -| `vetRewards.skillCapRewards` | `true` | Enable skill cap increase rewards | -| `vetRewards.rewardInterval` | `30d` | Time between reward tiers | -| `testCenter.enable` | `false` | Enable test center mode (free skills, items, etc.) | -| `chat.enabled` | `false` | Enable the built-in chat system | -| `buffIcons.enable` | `Core.ML` | Enable buff/debuff icons on the client UI | -| `houseDecay.enable` | `true` | Enable house decay over time | -| `pathfinding.enable` | `true` | Enable NPC pathfinding | - -### Network - -| Key | Default | Description | -|-----|---------|-------------| -| `pingServer.enabled` | `true` | Enable the UDP ping server (used by server browsers) | -| `pingServer.port` | `12000` | UDP port for the ping server | -| `pingServer.maxConnections` | `2048` | Maximum queued ping connections | -| `network.encryptionMode` | `Both` | Encryption mode: `None`, `Login`, `Game`, or `Both` | -| `network.encryptionDebug` | `false` | Enable debug logging for encryption negotiation | -| `netstate.packetLoggingPath` | `Packets` | Directory for per-client packet logs | -| `uogateway.enabled` | `true` | Enable the UO Gateway protocol | -| `assistants.enableNegotiation` | `false` | Enable Razor-style assistant protocol negotiation | - -### Server Listing - -| Key | Default | Description | -|-----|---------|-------------| -| `serverListing.serverName` | `ModernUO` | Server name shown in the server list | -| `serverListing.address` | `null` | Public IP address override for the server list | -| `serverListing.autoDetect` | `true` | Auto-detect public IP via external service | - -### Maps and Client Data - -| Key | Default | Description | -|-----|---------|-------------| -| `maps.enablePre6000Trammel` | `false` | Use pre-client-6000 Trammel map format | -| `maps.enableMapDiffPatches` | auto | Enable map diff patches | -| `maps.enableStaticsDiffPatches` | auto | Enable statics diff patches | -| `maps.enablePostHSMultiComponentFormat` | auto | Use post-High Seas multi component format | -| `expansion.forceOldAnimations` | `false` | Force pre-expansion animation set | - -### Miscellaneous - -| Key | Default | Description | -|-----|---------|-------------| -| `commandsystem.prefix` | `[` | Command prefix character (e.g., `[` for `[command`) | -| `profanityProtection.enabled` | `false` | Enable profanity filter | -| `profanityProtection.action` | `Disallow` | Profanity action: `Disallow`, `Criminal`, `None` | -| `system.localTimeZone` | system default | Override the server's time zone (IANA or Windows ID) | -| `pages.discordWebhookUrl` | `null` | Discord webhook URL for GM page notifications | -| `guildClickMessage` | `!Core.AOS` | Show guild abbreviation on single-click | -| `asciiClickMessage` | `!Core.AOS` | Use ASCII (not Unicode) for single-click messages | -| `bulletinboards.creationTimeDelay` | `2m` | Cooldown between creating bulletin board threads | -| `bulletinboards.expireDuration` | `6h` | Time before bulletin board threads expire | -| `bulletinboards.replyDelay` | `30s` | Cooldown between bulletin board replies | - -## Other Configuration Files - -### expansion.json - -Controls which expansion the server emulates and which maps are active. - -```json -{ - "id": 7, - "name": "Mondain\u0027s Legacy", - "mapSelectionFlags": "Felucca, Trammel, Ilshenar, Malas, Tokuno" -} -``` - -The `id` corresponds to the `Expansion` enum (0 = None, 1 = T2A, 2 = UOR, 3 = UOTD, 4 = LBR, 5 = AOS, 6 = SE, 7 = ML, 8 = SA, 9 = HS, 10 = TOL, 11 = EJ). The `mapSelectionFlags` field controls which maps are loaded. - -### antimacro.json - -Configures per-skill anti-macro rules to prevent automated skill gain. - -```json -{ - "allowance": 3, - "locationSize": 5, - "enabled": true, - "skillTriggers": { - "Anatomy": true, - "AnimalLore": true, - "Blacksmith": false, - "Magery": true - }, - "expire": "00:05:00" -} -``` - -| Field | Description | -|-------|-------------| -| `allowance` | Number of allowed skill uses per location before throttling | -| `locationSize` | Tile radius that defines a "location" for anti-macro purposes | -| `enabled` | Master toggle for the anti-macro system | -| `skillTriggers` | Per-skill toggle (true = anti-macro enforced for this skill) | -| `expire` | How long before location-based counters reset | - -### email-settings.json - -SMTP configuration for crash reports and support emails. Created with defaults on first launch if not present. - -```json -{ - "enabled": false, - "fromAddress": "support@example.com", - "fromName": "ModernUO Team", - "crashAddress": "crashes@example.com", - "crashName": "Crash Log", - "speechLogPageAddress": "support@example.com", - "speechLogPageName": "GM Support Conversation", - "emailServer": "smtp.gmail.com", - "emailPort": 465, - "emailUsername": "support@example.com", - "emailPassword": "your-app-password", - "emailSendRetryCount": 5, - "emailSendRetryDelay": 3 -} -``` - -:::tip -Set `"enabled": true` and configure your SMTP credentials to receive crash reports by email. For Gmail, use an [App Password](https://support.google.com/accounts/answer/185833). -::: - -### server-access.json - -Defines protected accounts that cannot be permanently locked out. If a protected account is banned or has its access level lowered, it automatically resets to `Owner` on the next successful login. - -```json -{ - "protectedAccounts": ["admin", "owner"] -} -``` - -:::note -Account names are case-insensitive. This is a safety net for server owners -- it ensures you can always regain access to your server even if another admin modifies your account. -::: - -### throttles.json - -Maps packet IDs to throttle delays in milliseconds. Packets sent faster than the configured delay are dropped for players (staff is exempt). - -```json -{ - "0x03": 25, - "0x12": 25, - "0x75": 500, - "0xAD": 25 -} -``` - -| Packet | Delay | Purpose | -|--------|-------|---------| -| `0x03` | 25ms | Speech | -| `0xAD` | 25ms | Unicode speech | -| `0x12` | 25ms | Text commands | -| `0x75` | 500ms | Rename request | - -You can modify throttles at runtime using the `[SetThrottle` and `[GetThrottle` admin commands. - -## Custom Configuration - -Developers can create custom JSON configuration files using the `JsonConfig` utility: - -```csharp -var mySettings = JsonConfig.Deserialize( - Path.Combine(Core.BaseDirectory, "Configuration/my-settings.json") -); -``` - -Files are read from the `Configuration/` directory and support comments, trailing commas, and all standard JSON converters (enums, TimeSpan, IPEndPoint, etc.) automatically. diff --git a/website/content/getting-started/installation.mdx b/website/content/getting-started/installation.mdx deleted file mode 100644 index 0b4a7c3fd..000000000 --- a/website/content/getting-started/installation.mdx +++ /dev/null @@ -1,77 +0,0 @@ ---- -sidebar_position: 1 -title: Installation ---- - -import OsTabs from '@site/src/components/OsTabs'; -import CodeBlock from '@theme/CodeBlock'; -import Admonition from '@theme/Admonition'; - -# Installation - - -{{ - windows: ( - <> -

Prerequisites

-
    -
  1. Download and install the latest .NET 10 SDK
  2. -
  3. Download and install Git for Windows
  4. -
  5. Install Visual C++ Redistributable (v14 or later)
  6. -
- -

Use Windows Terminal as your command prompt.

-
-

Recommended IDEs: Visual Studio 2026+, JetBrains Rider 2025.3+, or VS Code

-

Install ModernUO

-
    -
  1. Navigate to the folder where you want to install ModernUO.
  2. -
  3. Using Windows Terminal, run:
  4. -
- {`git clone https://github.com/modernuo/modernuo -cd modernuo`} - - ), - macos: ( - <> -

Prerequisites

-
    -
  1. Download and install the latest .NET 10 SDK
  2. -
  3. Using terminal, install Homebrew and dependencies:
  4. -
- {`/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)" -brew install git icu4c libdeflate argon2`} -

Recommended IDEs: JetBrains Rider 2025.3+ or VS Code

-

Install ModernUO

-
    -
  1. Using terminal, navigate to the folder where you want to install ModernUO and run:
  2. -
- {`git clone https://github.com/modernuo/modernuo -cd modernuo`} - - ), - linux: ( - <> -

Prerequisites

-
    -
  1. Download and install the latest .NET 10 SDK
  2. -
  3. Using bash, install git and dependencies:
  4. -
-

Debian / Ubuntu:

- {'sudo apt update && sudo apt install git libicu-dev libdeflate-dev libargon2-dev'} -

Fedora:

- {'sudo dnf install git libicu libdeflate-devel libargon2-devel'} - -

The exact package names may vary by distribution. Consult your distribution's package manager documentation.

-
-

Recommended IDEs: JetBrains Rider 2025.3+ or VS Code

-

Install ModernUO

-
    -
  1. Using bash, navigate to the folder where you want to install ModernUO and run:
  2. -
- {`git clone https://github.com/modernuo/modernuo -cd modernuo`} - - ), -}} -
diff --git a/website/content/getting-started/starting.mdx b/website/content/getting-started/starting.mdx deleted file mode 100644 index 59f0a0a65..000000000 --- a/website/content/getting-started/starting.mdx +++ /dev/null @@ -1,266 +0,0 @@ ---- -sidebar_position: 3 -title: Starting the Server ---- - -import OsTabs from '@site/src/components/OsTabs'; -import CodeBlock from '@theme/CodeBlock'; -import Admonition from '@theme/Admonition'; - -# Starting the Server - -Now that the server has been built, everything is run from the **Distribution** folder. - - -{{ - windows: ( - <> -

Using Windows Terminal, Git Bash, or PowerShell, run:

- {`cd Distribution -ModernUO.exe`} - - ), - macos: ( - <> -

Using terminal, run:

- {`cd Distribution -dotnet ModernUO.dll`} - - ), - linux: ( - <> -

Using terminal, run:

- {`cd Distribution -dotnet ModernUO.dll`} - - ), -}} -
- -## First Launch - -When you start ModernUO for the very first time, there is no `Configuration/modernuo.json` file yet. The server will walk you through an interactive setup to create one. - -### Step 1: Game Data Directory - -The server prompts you for the absolute path to your Ultima Online game files (or ClassicUO installation directory): - -``` -Please enter the absolute path to your ClassicUO or Ultima Online data: - > C:\Program Files\Ultima Online -Added C:\Program Files\Ultima Online. -[enter to finish]> -``` - -You can enter multiple directories if your files are split across locations. Press **Enter** on a blank line to finish. - -### Step 2: Listener Address - -Next, the server asks which IP address and port to listen on: - -``` -Please enter the IP and ports to listen: - - Only enter IP addresses directly bound to this machine - - To listen to all IP addresses enter 0.0.0.0 -[0.0.0.0:2593]> -Added 0.0.0.0:2593. -``` - -Press **Enter** to accept the default (`0.0.0.0:2593`), which listens on all network interfaces on port 2593. This is the correct choice for most setups. - -### Step 3: Server Name - -The server asks for your shard name, which is displayed in the server list: - -``` -Please enter the name of your shard: -[ModernUO]> My Shard -Server name set to My Shard. -``` - -Press **Enter** to accept the default name "ModernUO". - -### Step 4: Expansion and Maps - -Finally, the server prompts you to select the target expansion (e.g., T2A, UOR, AOS, ML, SA, etc.) and which maps to enable. Your selection is saved to `Configuration/expansion.json`. - -### Step 5: Owner Account - -If the server has no accounts (first launch), it offers to create the owner account: - -``` -[20:04:28 WRN] This server has no accounts. -[20:04:28 INF] Do you want to create the owner account now? (y/n): -y -[20:04:30 INF] Input Username: -admin -[20:04:32 INF] Input Password: -mypassword -[20:04:34 INF] Owner account created: admin -``` - -This account has full administrative access (`AccessLevel.Owner`) and is automatically added to the protected accounts list, meaning it cannot be banned or deleted through in-game commands. If you skip this step, you can create accounts later by connecting with auto-account creation enabled (the default). - -### Configuration Saved - -After answering these prompts, the server writes its configuration files: - -- `Configuration/modernuo.json` -- main server settings -- `Configuration/expansion.json` -- expansion and map selection -- `Configuration/server-access.json` -- protected accounts - -On every subsequent launch, the server reads these files and **skips the prompts entirely**. To re-run setup, delete these files and restart. - -## Expected Console Output - -A successful startup looks like this (abbreviated): - -``` -ModernUO - [https://github.com/modernuo/modernuo] Version 0.15.6.6 -Copyright 2019-2026 ModernUO Development Team - -[20:04:29 INF] Reading server configuration from Configuration/modernuo.json... -[20:04:29 INF] Running on .NET 10.0.5 -[20:04:32 INF] Loading Map Definitions -[20:04:32 INF] Loading map definitions done (7 maps, 0 failures) -[20:04:33 INF] Protected accounts registered: admin -[20:04:33 INF] Automatically detected client version 7.0.103.0 -[20:04:33 INF] Encryption support enabled: Both -[20:04:35 INF] Loading regions done (385 regions) -[20:04:38 INF] Loading world done (230276 items, 43358 mobiles) (2.63 seconds) -[20:04:38 INF] Auto-detected public IP address (47.154.82.160) -[20:04:39 INF] Feature Flag system initialized with 10 flags -[20:04:39 INF] Listening: 192.168.1.100:2593 -[20:04:39 INF] Listening: 127.0.0.1:2593 -[20:04:39 INF] Listening: 192.168.1.100:12000 (Pings) -[20:04:39 INF] Listening: 127.0.0.1:12000 (Pings) -``` - -Once you see the **`Listening:`** lines, the server is running and ready to accept connections. Port `2593` is the game port; port `12000` is for ping/status queries. - -## Game Files - -:::note -Ultima Online game files are required to run the server. These include map data, art, and other client assets that the server uses for world simulation. -::: - -:::tip -Game files are not distributed with ModernUO. Download the latest client from the [UO client download page](https://uo.com/Client-Download/). After installing, point ModernUO to the directory containing the `.mul` and `.uop` files. -::: - -If you already have a ClassicUO installation, ModernUO can automatically detect the game files directory from ClassicUO's `settings.json`. You can also point the server directly at the ClassicUO data folder. - -## Connecting to the Server - -### Using ClassicUO - -1. Open ClassicUO and go to the server configuration screen. -2. Set the **Server IP** to the address of the machine running ModernUO. -3. Set the **Server Port** to `2593` (or whichever port you configured). -4. Enter any username and password. If auto-account creation is enabled (the default), a new account is created on first login. - -### Local Testing - -For testing on the same machine that runs the server, use `127.0.0.1` as the server address with port `2593`. - -### Connecting from Another Machine - -If connecting from a different machine on your local network, use the server machine's LAN IP address (e.g., `192.168.1.100`). For connections over the internet, you need to forward port `2593` (TCP) on your router to the server machine. - -## Basic Troubleshooting - -### Port Already in Use - -``` -Error: Address already in use -``` - -Another process is using port 2593. Either stop the conflicting process or change the listener port in `Configuration/modernuo.json`: - -```json -"listeners": ["0.0.0.0:2594"] -``` - - -{{ - windows: ( - <> -

Find what is using the port:

- {'netstat -ano | findstr :2593'} - - ), - macos: ( - <> -

Find what is using the port:

- {'lsof -i :2593'} - - ), - linux: ( - <> -

Find what is using the port:

- {'ss -tlnp | grep 2593'} - - ), -}} -
- -### Missing Game Files or Wrong Data Directory - -If the server cannot find required `.mul` or `.uop` files, it will fail during world loading. Verify that: - -1. The path in `Configuration/modernuo.json` under `"dataDirectories"` points to the correct location. -2. The directory contains files like `map0.mul`, `statics0.mul`, `tiledata.mul`, or their `.uop` equivalents. -3. The UO client has been fully installed (not just the launcher). - -To fix the path, either edit `Configuration/modernuo.json` directly or delete it and re-run the server to go through the setup prompts again. - -### .NET SDK Not Found - - -{{ - windows: ( - <> -

If you see errors about the .NET runtime or SDK not being found, run the build tool in interactive mode. It will detect and offer to install the correct .NET SDK:

- {'./publish.cmd'} - - ), - macos: ( - <> -

If you see errors about the .NET runtime or SDK not being found, run the build tool in interactive mode. It will detect and offer to install the correct .NET SDK:

- {'./publish.sh'} - - ), - linux: ( - <> -

If you see errors about the .NET runtime or SDK not being found, run the build tool in interactive mode. It will detect and offer to install the correct .NET SDK:

- {'./publish.sh'} - - ), -}} -
- -:::tip -The interactive build tool is the easiest way to resolve SDK issues. It checks your environment and offers to install missing dependencies automatically. -::: - -### Permission Denied on Linux - -Do not run ModernUO as the `root` user. Create a dedicated user account for the server: - -```bash -sudo useradd -m modernuo -sudo su - modernuo -``` - -If you need to bind to a port below 1024 (not typical for UO), grant the binary the capability instead of running as root: - -```bash -sudo setcap 'cap_net_bind_service=+ep' /path/to/Distribution/dotnet -``` - -### Server Starts but Clients Cannot Connect - -- Make sure you are connecting to the correct IP address and port. -- Check that no firewall is blocking port 2593 (TCP). -- If connecting over the internet, verify that port forwarding is configured on your router. -- On Linux, check `iptables` or `ufw` rules allow inbound traffic on port 2593. diff --git a/website/docusaurus.config.ts b/website/docusaurus.config.ts deleted file mode 100644 index 57fabecf3..000000000 --- a/website/docusaurus.config.ts +++ /dev/null @@ -1,166 +0,0 @@ -import { themes as prismThemes } from 'prism-react-renderer'; -import type { Config } from '@docusaurus/types'; -import type * as Preset from '@docusaurus/preset-classic'; - -const config: Config = { - title: 'ModernUO', - tagline: 'The Ultima Online server emulator for the modern era', - favicon: 'branding/favicon.png', - - future: { - v4: true, - }, - - url: 'https://modernuo.com', - baseUrl: '/', - - organizationName: 'modernuo', - projectName: 'ModernUO', - - onBrokenLinks: 'throw', - - i18n: { - defaultLocale: 'en', - locales: ['en'], - }, - - headTags: [ - { - tagName: 'link', - attributes: { - rel: 'preconnect', - href: 'https://fonts.googleapis.com', - }, - }, - { - tagName: 'link', - attributes: { - rel: 'preconnect', - href: 'https://fonts.gstatic.com', - crossorigin: 'anonymous', - }, - }, - { - tagName: 'link', - attributes: { - rel: 'stylesheet', - href: 'https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap', - }, - }, - { - tagName: 'link', - attributes: { - rel: 'apple-touch-icon', - sizes: '180x180', - href: '/branding/apple-touch-icon.png', - }, - }, - { - tagName: 'link', - attributes: { - rel: 'icon', - type: 'image/png', - sizes: '32x32', - href: '/branding/favicon-32x32.png', - }, - }, - { - tagName: 'link', - attributes: { - rel: 'icon', - type: 'image/png', - sizes: '16x16', - href: '/branding/favicon-16x16.png', - }, - }, - { - tagName: 'meta', - attributes: { - name: 'msapplication-TileImage', - content: '/branding/mstile-144x144.png', - }, - }, - ], - - presets: [ - [ - 'classic', - { - docs: { - path: 'content', - routeBasePath: 'docs', - sidebarPath: './sidebars.ts', - editUrl: 'https://github.com/modernuo/ModernUO/tree/main/website/', - }, - blog: false, - theme: { - customCss: './src/css/custom.css', - }, - } satisfies Preset.Options, - ], - ], - - themeConfig: { - image: 'branding/android-chrome-512x512.png', - metadata: [ - { name: 'twitter:card', content: 'summary' }, - { name: 'twitter:site', content: '@modernuo' }, - { name: 'og:type', content: 'website' }, - ], - colorMode: { - defaultMode: 'dark', - disableSwitch: true, - respectPrefersColorScheme: false, - }, - navbar: { - title: 'ModernUO', - logo: { - alt: 'ModernUO Logo', - src: 'branding/logo.svg', - }, - items: [ - { - type: 'docSidebar', - sidebarId: 'docsSidebar', - position: 'left', - label: 'Docs', - }, - { - href: 'pathname:///commands.html', - label: 'Commands', - position: 'left', - className: 'navbar__link--internal', - }, - { - href: 'pathname:///packets.html', - label: 'Packets', - position: 'left', - className: 'navbar__link--internal', - }, - { - type: 'html', - position: 'right', - value: '', - }, - { - type: 'html', - position: 'right', - value: '', - }, - { - type: 'html', - position: 'right', - value: '', - }, - ], - }, - footer: {}, - prism: { - theme: prismThemes.github, - darkTheme: prismThemes.dracula, - additionalLanguages: ['csharp', 'bash', 'json', 'powershell'], - }, - } satisfies Preset.ThemeConfig, -}; - -export default config; diff --git a/website/package-lock.json b/website/package-lock.json deleted file mode 100644 index a8f153d22..000000000 --- a/website/package-lock.json +++ /dev/null @@ -1,18448 +0,0 @@ -{ - "name": "website", - "version": "0.0.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "website", - "version": "0.0.0", - "dependencies": { - "@docusaurus/core": "3.9.2", - "@docusaurus/preset-classic": "3.9.2", - "@mdx-js/react": "^3.0.0", - "clsx": "^2.0.0", - "prism-react-renderer": "^2.3.0", - "react": "^19.0.0", - "react-dom": "^19.0.0" - }, - "devDependencies": { - "@docusaurus/module-type-aliases": "3.9.2", - "@docusaurus/tsconfig": "3.9.2", - "@docusaurus/types": "3.9.2", - "typescript": "~5.6.2" - }, - "engines": { - "node": ">=20.0" - } - }, - "node_modules/@algolia/abtesting": { - "version": "1.16.0", - "resolved": "https://registry.npmjs.org/@algolia/abtesting/-/abtesting-1.16.0.tgz", - "integrity": "sha512-alHFZ68/i9qLC/muEB07VQ9r7cB8AvCcGX6dVQi2PNHhc/ZQRmmFAv8KK1ay4UiseGSFr7f0nXBKsZ/jRg7e4g==", - "license": "MIT", - "dependencies": { - "@algolia/client-common": "5.50.0", - "@algolia/requester-browser-xhr": "5.50.0", - "@algolia/requester-fetch": "5.50.0", - "@algolia/requester-node-http": "5.50.0" - }, - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/@algolia/autocomplete-core": { - "version": "1.19.2", - "resolved": "https://registry.npmjs.org/@algolia/autocomplete-core/-/autocomplete-core-1.19.2.tgz", - "integrity": "sha512-mKv7RyuAzXvwmq+0XRK8HqZXt9iZ5Kkm2huLjgn5JoCPtDy+oh9yxUMfDDaVCw0oyzZ1isdJBc7l9nuCyyR7Nw==", - "license": "MIT", - "dependencies": { - "@algolia/autocomplete-plugin-algolia-insights": "1.19.2", - "@algolia/autocomplete-shared": "1.19.2" - } - }, - "node_modules/@algolia/autocomplete-plugin-algolia-insights": { - "version": "1.19.2", - "resolved": "https://registry.npmjs.org/@algolia/autocomplete-plugin-algolia-insights/-/autocomplete-plugin-algolia-insights-1.19.2.tgz", - "integrity": "sha512-TjxbcC/r4vwmnZaPwrHtkXNeqvlpdyR+oR9Wi2XyfORkiGkLTVhX2j+O9SaCCINbKoDfc+c2PB8NjfOnz7+oKg==", - "license": "MIT", - "dependencies": { - "@algolia/autocomplete-shared": "1.19.2" - }, - "peerDependencies": { - "search-insights": ">= 1 < 3" - } - }, - "node_modules/@algolia/autocomplete-shared": { - "version": "1.19.2", - "resolved": "https://registry.npmjs.org/@algolia/autocomplete-shared/-/autocomplete-shared-1.19.2.tgz", - "integrity": "sha512-jEazxZTVD2nLrC+wYlVHQgpBoBB5KPStrJxLzsIFl6Kqd1AlG9sIAGl39V5tECLpIQzB3Qa2T6ZPJ1ChkwMK/w==", - "license": "MIT", - "peerDependencies": { - "@algolia/client-search": ">= 4.9.1 < 6", - "algoliasearch": ">= 4.9.1 < 6" - } - }, - "node_modules/@algolia/client-abtesting": { - "version": "5.50.0", - "resolved": "https://registry.npmjs.org/@algolia/client-abtesting/-/client-abtesting-5.50.0.tgz", - "integrity": "sha512-mfgUdLQNxOAvCZUGzPQxjahEWEPuQkKlV0ZtGmePOa9ZxIQZlk31vRBNbM6ScU8jTH41SCYE77G/lCifDr1SVw==", - "license": "MIT", - "dependencies": { - "@algolia/client-common": "5.50.0", - "@algolia/requester-browser-xhr": "5.50.0", - "@algolia/requester-fetch": "5.50.0", - "@algolia/requester-node-http": "5.50.0" - }, - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/@algolia/client-analytics": { - "version": "5.50.0", - "resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-5.50.0.tgz", - "integrity": "sha512-5mjokeKYyPaP3Q8IYJEnutI+O4dW/Ixxx5IgsSxT04pCfGqPXxTOH311hTQxyNpcGGEOGrMv8n8Z+UMTPamioQ==", - "license": "MIT", - "dependencies": { - "@algolia/client-common": "5.50.0", - "@algolia/requester-browser-xhr": "5.50.0", - "@algolia/requester-fetch": "5.50.0", - "@algolia/requester-node-http": "5.50.0" - }, - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/@algolia/client-common": { - "version": "5.50.0", - "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-5.50.0.tgz", - "integrity": "sha512-emtOvR6dl3rX3sBJXXbofMNHU1qMQqQSWu319RMrNL5BWoBqyiq7y0Zn6cjJm7aGHV/Qbf+KCCYeWNKEMPI3BQ==", - "license": "MIT", - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/@algolia/client-insights": { - "version": "5.50.0", - "resolved": "https://registry.npmjs.org/@algolia/client-insights/-/client-insights-5.50.0.tgz", - "integrity": "sha512-IerGH2/hcj/6bwkpQg/HHRqmlGN1XwygQWythAk0gZFBrghs9danJaYuSS3ShzLSVoIVth4jY5GDPX9Lbw5cgg==", - "license": "MIT", - "dependencies": { - "@algolia/client-common": "5.50.0", - "@algolia/requester-browser-xhr": "5.50.0", - "@algolia/requester-fetch": "5.50.0", - "@algolia/requester-node-http": "5.50.0" - }, - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/@algolia/client-personalization": { - "version": "5.50.0", - "resolved": "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-5.50.0.tgz", - "integrity": "sha512-3idPJeXn5L0MmgP9jk9JJqblrQ/SguN93dNK9z9gfgyupBhHnJMOEjrRYcVgTIfvG13Y04wO+Q0FxE2Ut8PVbA==", - "license": "MIT", - "dependencies": { - "@algolia/client-common": "5.50.0", - "@algolia/requester-browser-xhr": "5.50.0", - "@algolia/requester-fetch": "5.50.0", - "@algolia/requester-node-http": "5.50.0" - }, - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/@algolia/client-query-suggestions": { - "version": "5.50.0", - "resolved": "https://registry.npmjs.org/@algolia/client-query-suggestions/-/client-query-suggestions-5.50.0.tgz", - "integrity": "sha512-q7qRoWrQK1a8m5EFQEmPlo7+pg9mVQ8X5jsChtChERre0uS2pdYEDixBBl0ydBSGkdGbLUDufcACIhH/077E4g==", - "license": "MIT", - "dependencies": { - "@algolia/client-common": "5.50.0", - "@algolia/requester-browser-xhr": "5.50.0", - "@algolia/requester-fetch": "5.50.0", - "@algolia/requester-node-http": "5.50.0" - }, - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/@algolia/client-search": { - "version": "5.50.0", - "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-5.50.0.tgz", - "integrity": "sha512-Jc360x4yqb3eEg4OY4KEIdGePBxZogivKI+OGIU8aLXgAYPTECvzeOBc90312yHA1hr3AeRlAFl0rIc8lQaIrQ==", - "license": "MIT", - "dependencies": { - "@algolia/client-common": "5.50.0", - "@algolia/requester-browser-xhr": "5.50.0", - "@algolia/requester-fetch": "5.50.0", - "@algolia/requester-node-http": "5.50.0" - }, - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/@algolia/events": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@algolia/events/-/events-4.0.1.tgz", - "integrity": "sha512-FQzvOCgoFXAbf5Y6mYozw2aj5KCJoA3m4heImceldzPSMbdyS4atVjJzXKMsfX3wnZTFYwkkt8/z8UesLHlSBQ==", - "license": "MIT" - }, - "node_modules/@algolia/ingestion": { - "version": "1.50.0", - "resolved": "https://registry.npmjs.org/@algolia/ingestion/-/ingestion-1.50.0.tgz", - "integrity": "sha512-OS3/Viao+NPpyBbEY3tf6hLewppG+UclD+9i0ju56mq2DrdMJFCkEky6Sk9S5VPcbLzxzg3BqBX6u9Q35w19aQ==", - "license": "MIT", - "dependencies": { - "@algolia/client-common": "5.50.0", - "@algolia/requester-browser-xhr": "5.50.0", - "@algolia/requester-fetch": "5.50.0", - "@algolia/requester-node-http": "5.50.0" - }, - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/@algolia/monitoring": { - "version": "1.50.0", - "resolved": "https://registry.npmjs.org/@algolia/monitoring/-/monitoring-1.50.0.tgz", - "integrity": "sha512-/znwgSiGufpbJVIoDmeQaHtTq+OMdDawFRbMSJVv+12n79hW+qdQXS8/Uu3BD3yn0BzgVFJEvrsHrCsInZKdhw==", - "license": "MIT", - "dependencies": { - "@algolia/client-common": "5.50.0", - "@algolia/requester-browser-xhr": "5.50.0", - "@algolia/requester-fetch": "5.50.0", - "@algolia/requester-node-http": "5.50.0" - }, - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/@algolia/recommend": { - "version": "5.50.0", - "resolved": "https://registry.npmjs.org/@algolia/recommend/-/recommend-5.50.0.tgz", - "integrity": "sha512-dHjUfu4jfjdQiKDpCpAnM7LP5yfG0oNShtfpF5rMCel6/4HIoqJ4DC4h5GKDzgrvJYtgAhblo0AYBmOM00T+lQ==", - "license": "MIT", - "dependencies": { - "@algolia/client-common": "5.50.0", - "@algolia/requester-browser-xhr": "5.50.0", - "@algolia/requester-fetch": "5.50.0", - "@algolia/requester-node-http": "5.50.0" - }, - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/@algolia/requester-browser-xhr": { - "version": "5.50.0", - "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-5.50.0.tgz", - "integrity": "sha512-bffIbUljAWnh/Ctu5uScORajuUavqmZ0ACYd1fQQeSSYA9NNN83ynO26pSc2dZRXpSK0fkc1//qSSFXMKGu+aw==", - "license": "MIT", - "dependencies": { - "@algolia/client-common": "5.50.0" - }, - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/@algolia/requester-fetch": { - "version": "5.50.0", - "resolved": "https://registry.npmjs.org/@algolia/requester-fetch/-/requester-fetch-5.50.0.tgz", - "integrity": "sha512-y0EwNvPGvkM+yTAqqO6Gpt9wVGm3CLDtpLvNEiB3VGvN3WzfkjZGtLUsG/ru2kVJIIU7QcV0puuYgEpBeFxcJg==", - "license": "MIT", - "dependencies": { - "@algolia/client-common": "5.50.0" - }, - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/@algolia/requester-node-http": { - "version": "5.50.0", - "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-5.50.0.tgz", - "integrity": "sha512-xpwefe4fCOWnZgXCbkGpqQY6jgBSCf2hmgnySbyzZIccrv3SoashHKGPE4x6vVG+gdHrGciMTAcDo9HOZwH22Q==", - "license": "MIT", - "dependencies": { - "@algolia/client-common": "5.50.0" - }, - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", - "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.28.5", - "js-tokens": "^4.0.0", - "picocolors": "^1.1.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/compat-data": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.0.tgz", - "integrity": "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", - "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.29.0", - "@babel/generator": "^7.29.0", - "@babel/helper-compilation-targets": "^7.28.6", - "@babel/helper-module-transforms": "^7.28.6", - "@babel/helpers": "^7.28.6", - "@babel/parser": "^7.29.0", - "@babel/template": "^7.28.6", - "@babel/traverse": "^7.29.0", - "@babel/types": "^7.29.0", - "@jridgewell/remapping": "^2.3.5", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@babel/core/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/generator": { - "version": "7.29.1", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", - "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.29.0", - "@babel/types": "^7.29.0", - "@jridgewell/gen-mapping": "^0.3.12", - "@jridgewell/trace-mapping": "^0.3.28", - "jsesc": "^3.0.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-annotate-as-pure": { - "version": "7.27.3", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz", - "integrity": "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==", - "license": "MIT", - "dependencies": { - "@babel/types": "^7.27.3" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", - "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.28.6", - "@babel/helper-validator-option": "^7.27.1", - "browserslist": "^4.24.0", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.28.6.tgz", - "integrity": "sha512-dTOdvsjnG3xNT9Y0AUg1wAl38y+4Rl4sf9caSQZOXdNqVn+H+HbbJ4IyyHaIqNR6SW9oJpA/RuRjsjCw2IdIow==", - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.3", - "@babel/helper-member-expression-to-functions": "^7.28.5", - "@babel/helper-optimise-call-expression": "^7.27.1", - "@babel/helper-replace-supers": "^7.28.6", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", - "@babel/traverse": "^7.28.6", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-create-class-features-plugin/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/helper-create-regexp-features-plugin": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.28.5.tgz", - "integrity": "sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw==", - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.3", - "regexpu-core": "^6.3.1", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-create-regexp-features-plugin/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/helper-define-polyfill-provider": { - "version": "0.6.8", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.8.tgz", - "integrity": "sha512-47UwBLPpQi1NoWzLuHNjRoHlYXMwIJoBf7MFou6viC/sIHWYygpvr0B6IAyh5sBdA2nr2LPIRww8lfaUVQINBA==", - "license": "MIT", - "dependencies": { - "@babel/helper-compilation-targets": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6", - "debug": "^4.4.3", - "lodash.debounce": "^4.0.8", - "resolve": "^1.22.11" - }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" - } - }, - "node_modules/@babel/helper-globals": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", - "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.28.5.tgz", - "integrity": "sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg==", - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.28.5", - "@babel/types": "^7.28.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-imports": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", - "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.28.6", - "@babel/types": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", - "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", - "license": "MIT", - "dependencies": { - "@babel/helper-module-imports": "^7.28.6", - "@babel/helper-validator-identifier": "^7.28.5", - "@babel/traverse": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-optimise-call-expression": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.27.1.tgz", - "integrity": "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==", - "license": "MIT", - "dependencies": { - "@babel/types": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", - "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-remap-async-to-generator": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.27.1.tgz", - "integrity": "sha512-7fiA521aVw8lSPeI4ZOD3vRFkoqkJcS+z4hFo82bFSH/2tNd6eJ5qCVMS5OzDmZh/kaHQeBaeyxK6wljcPtveA==", - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.1", - "@babel/helper-wrap-function": "^7.27.1", - "@babel/traverse": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-replace-supers": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.28.6.tgz", - "integrity": "sha512-mq8e+laIk94/yFec3DxSjCRD2Z0TAjhVbEJY3UQrlwVo15Lmt7C2wAUbK4bjnTs4APkwsYLTahXRraQXhb1WCg==", - "license": "MIT", - "dependencies": { - "@babel/helper-member-expression-to-functions": "^7.28.5", - "@babel/helper-optimise-call-expression": "^7.27.1", - "@babel/traverse": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.27.1.tgz", - "integrity": "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==", - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-string-parser": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", - "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", - "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-option": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", - "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-wrap-function": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.28.6.tgz", - "integrity": "sha512-z+PwLziMNBeSQJonizz2AGnndLsP2DeGHIxDAn+wdHOGuo4Fo1x1HBPPXeE9TAOPHNNWQKCSlA2VZyYyyibDnQ==", - "license": "MIT", - "dependencies": { - "@babel/template": "^7.28.6", - "@babel/traverse": "^7.28.6", - "@babel/types": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helpers": { - "version": "7.29.2", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.2.tgz", - "integrity": "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==", - "license": "MIT", - "dependencies": { - "@babel/template": "^7.28.6", - "@babel/types": "^7.29.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/parser": { - "version": "7.29.2", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.2.tgz", - "integrity": "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==", - "license": "MIT", - "dependencies": { - "@babel/types": "^7.29.0" - }, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.28.5.tgz", - "integrity": "sha512-87GDMS3tsmMSi/3bWOte1UblL+YUTFMV8SZPZ2eSEL17s74Cw/l63rR6NmGVKMYW2GYi85nE+/d6Hw5N0bEk2Q==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/traverse": "^7.28.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/plugin-bugfix-safari-class-field-initializer-scope": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.27.1.tgz", - "integrity": "sha512-qNeq3bCKnGgLkEXUuFry6dPlGfCdQNZbn7yUAPCInwAJHMU7THJfrBSozkcWq5sNM6RcF3S8XyQL2A52KNR9IA==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.27.1.tgz", - "integrity": "sha512-g4L7OYun04N1WyqMNjldFwlfPCLVkgB54A/YCXICZYBsvJJE3kByKv9c9+R/nAfmIfjl2rKYLNyMHboYbZaWaA==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.27.1.tgz", - "integrity": "sha512-oO02gcONcD5O1iTLi/6frMJBIwWEHceWGSGqrpCmEL8nogiS6J9PBlE48CaK20/Jx1LuRml9aDftLgdjXT8+Cw==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", - "@babel/plugin-transform-optional-chaining": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.13.0" - } - }, - "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.28.6.tgz", - "integrity": "sha512-a0aBScVTlNaiUe35UtfxAN7A/tehvvG4/ByO6+46VPKTRSlfnAFsgKy0FUh+qAkQrDTmhDkT+IBOKlOoMUxQ0g==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/traverse": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/plugin-proposal-private-property-in-object": { - "version": "7.21.0-placeholder-for-preset-env.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", - "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-dynamic-import": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", - "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-import-assertions": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.28.6.tgz", - "integrity": "sha512-pSJUpFHdx9z5nqTSirOCMtYVP2wFgoWhP0p3g8ONK/4IHhLIBd0B9NYqAvIUAhq+OkhO4VM1tENCt0cjlsNShw==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-import-attributes": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.28.6.tgz", - "integrity": "sha512-jiLC0ma9XkQT3TKJ9uYvlakm66Pamywo+qwL+oL8HJOvc6TWdZXVfhqJr8CCzbSGUAbDOzlGHJC1U+vRfLQDvw==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.28.6.tgz", - "integrity": "sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.28.6.tgz", - "integrity": "sha512-+nDNmQye7nlnuuHDboPbGm00Vqg3oO8niRRL27/4LYHUsHYh0zJ1xWOz0uRwNFmM1Avzk8wZbc6rdiYhomzv/A==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-unicode-sets-regex": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz", - "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==", - "license": "MIT", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/plugin-transform-arrow-functions": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.27.1.tgz", - "integrity": "sha512-8Z4TGic6xW70FKThA5HYEKKyBpOOsucTOD1DjU3fZxDg+K3zBJcXMFnt/4yQiZnf5+MiOMSXQ9PaEK/Ilh1DeA==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-async-generator-functions": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.29.0.tgz", - "integrity": "sha512-va0VdWro4zlBr2JsXC+ofCPB2iG12wPtVGTWFx2WLDOM3nYQZZIGP82qku2eW/JR83sD+k2k+CsNtyEbUqhU6w==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/helper-remap-async-to-generator": "^7.27.1", - "@babel/traverse": "^7.29.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-async-to-generator": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.28.6.tgz", - "integrity": "sha512-ilTRcmbuXjsMmcZ3HASTe4caH5Tpo93PkTxF9oG2VZsSWsahydmcEHhix9Ik122RcTnZnUzPbmux4wh1swfv7g==", - "license": "MIT", - "dependencies": { - "@babel/helper-module-imports": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/helper-remap-async-to-generator": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-block-scoped-functions": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.27.1.tgz", - "integrity": "sha512-cnqkuOtZLapWYZUYM5rVIdv1nXYuFVIltZ6ZJ7nIj585QsjKM5dhL2Fu/lICXZ1OyIAFc7Qy+bvDAtTXqGrlhg==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.28.6.tgz", - "integrity": "sha512-tt/7wOtBmwHPNMPu7ax4pdPz6shjFrmHDghvNC+FG9Qvj7D6mJcoRQIF5dy4njmxR941l6rgtvfSB2zX3VlUIw==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-class-properties": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.28.6.tgz", - "integrity": "sha512-dY2wS3I2G7D697VHndN91TJr8/AAfXQNt5ynCTI/MpxMsSzHp+52uNivYT5wCPax3whc47DR8Ba7cmlQMg24bw==", - "license": "MIT", - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-class-static-block": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.28.6.tgz", - "integrity": "sha512-rfQ++ghVwTWTqQ7w8qyDxL1XGihjBss4CmTgGRCTAC9RIbhVpyp4fOeZtta0Lbf+dTNIVJer6ych2ibHwkZqsQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.12.0" - } - }, - "node_modules/@babel/plugin-transform-classes": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.28.6.tgz", - "integrity": "sha512-EF5KONAqC5zAqT783iMGuM2ZtmEBy+mJMOKl2BCvPZ2lVrwvXnB6o+OBWCS+CoeCCpVRF2sA2RBKUxvT8tQT5Q==", - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.3", - "@babel/helper-compilation-targets": "^7.28.6", - "@babel/helper-globals": "^7.28.0", - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/helper-replace-supers": "^7.28.6", - "@babel/traverse": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-computed-properties": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.28.6.tgz", - "integrity": "sha512-bcc3k0ijhHbc2lEfpFHgx7eYw9KNXqOerKWfzbxEHUGKnS3sz9C4CNL9OiFN1297bDNfUiSO7DaLzbvHQQQ1BQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/template": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-destructuring": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.28.5.tgz", - "integrity": "sha512-Kl9Bc6D0zTUcFUvkNuQh4eGXPKKNDOJQXVyyM4ZAQPMveniJdxi8XMJwLo+xSoW3MIq81bD33lcUe9kZpl0MCw==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/traverse": "^7.28.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-dotall-regex": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.28.6.tgz", - "integrity": "sha512-SljjowuNKB7q5Oayv4FoPzeB74g3QgLt8IVJw9ADvWy3QnUb/01aw8I4AVv8wYnPvQz2GDDZ/g3GhcNyDBI4Bg==", - "license": "MIT", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.28.5", - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-duplicate-keys": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.27.1.tgz", - "integrity": "sha512-MTyJk98sHvSs+cvZ4nOauwTTG1JeonDjSGvGGUNHreGQns+Mpt6WX/dVzWBHgg+dYZhkC4X+zTDfkTU+Vy9y7Q==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-duplicate-named-capturing-groups-regex": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.29.0.tgz", - "integrity": "sha512-zBPcW2lFGxdiD8PUnPwJjag2J9otbcLQzvbiOzDxpYXyCuYX9agOwMPGn1prVH0a4qzhCKu24rlH4c1f7yA8rw==", - "license": "MIT", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.28.5", - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/plugin-transform-dynamic-import": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.27.1.tgz", - "integrity": "sha512-MHzkWQcEmjzzVW9j2q8LGjwGWpG2mjwaaB0BNQwst3FIjqsg8Ct/mIZlvSPJvfi9y2AC8mi/ktxbFVL9pZ1I4A==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-explicit-resource-management": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-explicit-resource-management/-/plugin-transform-explicit-resource-management-7.28.6.tgz", - "integrity": "sha512-Iao5Konzx2b6g7EPqTy40UZbcdXE126tTxVFr/nAIj+WItNxjKSYTEw3RC+A2/ZetmdJsgueL1KhaMCQHkLPIg==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/plugin-transform-destructuring": "^7.28.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-exponentiation-operator": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.28.6.tgz", - "integrity": "sha512-WitabqiGjV/vJ0aPOLSFfNY1u9U3R7W36B03r5I2KoNix+a3sOhJ3pKFB3R5It9/UiK78NiO0KE9P21cMhlPkw==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-export-namespace-from": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.27.1.tgz", - "integrity": "sha512-tQvHWSZ3/jH2xuq/vZDy0jNn+ZdXJeM8gHvX4lnJmsc3+50yPlWdZXIc5ay+umX+2/tJIqHqiEqcJvxlmIvRvQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-for-of": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.27.1.tgz", - "integrity": "sha512-BfbWFFEJFQzLCQ5N8VocnCtA8J1CLkNTe2Ms2wocj75dd6VpiqS5Z5quTYcUoo4Yq+DN0rtikODccuv7RU81sw==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-function-name": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.27.1.tgz", - "integrity": "sha512-1bQeydJF9Nr1eBCMMbC+hdwmRlsv5XYOMu03YSWFwNs0HsAmtSxxF1fyuYPqemVldVyFmlCU7w8UE14LupUSZQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-compilation-targets": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/traverse": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-json-strings": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.28.6.tgz", - "integrity": "sha512-Nr+hEN+0geQkzhbdgQVPoqr47lZbm+5fCUmO70722xJZd0Mvb59+33QLImGj6F+DkK3xgDi1YVysP8whD6FQAw==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-literals": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.27.1.tgz", - "integrity": "sha512-0HCFSepIpLTkLcsi86GG3mTUzxV5jpmbv97hTETW3yzrAij8aqlD36toB1D0daVFJM8NK6GvKO0gslVQmm+zZA==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-logical-assignment-operators": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.28.6.tgz", - "integrity": "sha512-+anKKair6gpi8VsM/95kmomGNMD0eLz1NQ8+Pfw5sAwWH9fGYXT50E55ZpV0pHUHWf6IUTWPM+f/7AAff+wr9A==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-member-expression-literals": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.27.1.tgz", - "integrity": "sha512-hqoBX4dcZ1I33jCSWcXrP+1Ku7kdqXf1oeah7ooKOIiAdKQ+uqftgCFNOSzA5AMS2XIHEYeGFg4cKRCdpxzVOQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-modules-amd": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.27.1.tgz", - "integrity": "sha512-iCsytMg/N9/oFq6n+gFTvUYDZQOMK5kEdeYxmxt91fcJGycfxVP9CnrxoliM0oumFERba2i8ZtwRUCMhvP1LnA==", - "license": "MIT", - "dependencies": { - "@babel/helper-module-transforms": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.28.6.tgz", - "integrity": "sha512-jppVbf8IV9iWWwWTQIxJMAJCWBuuKx71475wHwYytrRGQ2CWiDvYlADQno3tcYpS/T2UUWFQp3nVtYfK/YBQrA==", - "license": "MIT", - "dependencies": { - "@babel/helper-module-transforms": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-modules-systemjs": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.29.0.tgz", - "integrity": "sha512-PrujnVFbOdUpw4UHiVwKvKRLMMic8+eC0CuNlxjsyZUiBjhFdPsewdXCkveh2KqBA9/waD0W1b4hXSOBQJezpQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-module-transforms": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/helper-validator-identifier": "^7.28.5", - "@babel/traverse": "^7.29.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-modules-umd": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.27.1.tgz", - "integrity": "sha512-iQBE/xC5BV1OxJbp6WG7jq9IWiD+xxlZhLrdwpPkTX3ydmXdvoCpyfJN7acaIBZaOqTfr76pgzqBJflNbeRK+w==", - "license": "MIT", - "dependencies": { - "@babel/helper-module-transforms": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.29.0.tgz", - "integrity": "sha512-1CZQA5KNAD6ZYQLPw7oi5ewtDNxH/2vuCh+6SmvgDfhumForvs8a1o9n0UrEoBD8HU4djO2yWngTQlXl1NDVEQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.28.5", - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/plugin-transform-new-target": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.27.1.tgz", - "integrity": "sha512-f6PiYeqXQ05lYq3TIfIDu/MtliKUbNwkGApPUvyo6+tc7uaR4cPjPe7DFPr15Uyycg2lZU6btZ575CuQoYh7MQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.28.6.tgz", - "integrity": "sha512-3wKbRgmzYbw24mDJXT7N+ADXw8BC/imU9yo9c9X9NKaLF1fW+e5H1U5QjMUBe4Qo4Ox/o++IyUkl1sVCLgevKg==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-numeric-separator": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.28.6.tgz", - "integrity": "sha512-SJR8hPynj8outz+SlStQSwvziMN4+Bq99it4tMIf5/Caq+3iOc0JtKyse8puvyXkk3eFRIA5ID/XfunGgO5i6w==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-object-rest-spread": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.28.6.tgz", - "integrity": "sha512-5rh+JR4JBC4pGkXLAcYdLHZjXudVxWMXbB6u6+E9lRL5TrGVbHt1TjxGbZ8CkmYw9zjkB7jutzOROArsqtncEA==", - "license": "MIT", - "dependencies": { - "@babel/helper-compilation-targets": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/plugin-transform-destructuring": "^7.28.5", - "@babel/plugin-transform-parameters": "^7.27.7", - "@babel/traverse": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-object-super": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.27.1.tgz", - "integrity": "sha512-SFy8S9plRPbIcxlJ8A6mT/CxFdJx/c04JEctz4jf8YZaVS2px34j7NXRrlGlHkN/M2gnpL37ZpGRGVFLd3l8Ng==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-replace-supers": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-optional-catch-binding": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.28.6.tgz", - "integrity": "sha512-R8ja/Pyrv0OGAvAXQhSTmWyPJPml+0TMqXlO5w+AsMEiwb2fg3WkOvob7UxFSL3OIttFSGSRFKQsOhJ/X6HQdQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-optional-chaining": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.28.6.tgz", - "integrity": "sha512-A4zobikRGJTsX9uqVFdafzGkqD30t26ck2LmOzAuLL8b2x6k3TIqRiT2xVvA9fNmFeTX484VpsdgmKNA0bS23w==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-parameters": { - "version": "7.27.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.27.7.tgz", - "integrity": "sha512-qBkYTYCb76RRxUM6CcZA5KRu8K4SM8ajzVeUgVdMVO9NN9uI/GaVmBg/WKJJGnNokV9SY8FxNOVWGXzqzUidBg==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-private-methods": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.28.6.tgz", - "integrity": "sha512-piiuapX9CRv7+0st8lmuUlRSmX6mBcVeNQ1b4AYzJxfCMuBfB0vBXDiGSmm03pKJw1v6cZ8KSeM+oUnM6yAExg==", - "license": "MIT", - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-private-property-in-object": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.28.6.tgz", - "integrity": "sha512-b97jvNSOb5+ehyQmBpmhOCiUC5oVK4PMnpRvO7+ymFBoqYjeDHIU9jnrNUuwHOiL9RpGDoKBpSViarV+BU+eVA==", - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.3", - "@babel/helper-create-class-features-plugin": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-property-literals": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.27.1.tgz", - "integrity": "sha512-oThy3BCuCha8kDZ8ZkgOg2exvPYUlprMukKQXI1r1pJ47NCvxfkEy8vK+r/hT9nF0Aa4H1WUPZZjHTFtAhGfmQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-constant-elements": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.27.1.tgz", - "integrity": "sha512-edoidOjl/ZxvYo4lSBOQGDSyToYVkTAwyVoa2tkuYTSmjrB1+uAedoL5iROVLXkxH+vRgA7uP4tMg2pUJpZ3Ug==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-display-name": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.28.0.tgz", - "integrity": "sha512-D6Eujc2zMxKjfa4Zxl4GHMsmhKKZ9VpcqIchJLvwTxad9zWIYulwYItBovpDOoNLISpcZSXoDJ5gaGbQUDqViA==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-jsx": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.28.6.tgz", - "integrity": "sha512-61bxqhiRfAACulXSLd/GxqmAedUSrRZIu/cbaT18T1CetkTmtDN15it7i80ru4DVqRK1WMxQhXs+Lf9kajm5Ow==", - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.3", - "@babel/helper-module-imports": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/plugin-syntax-jsx": "^7.28.6", - "@babel/types": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-jsx-development": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.27.1.tgz", - "integrity": "sha512-ykDdF5yI4f1WrAolLqeF3hmYU12j9ntLQl/AOG1HAS21jxyg1Q0/J/tpREuYLfatGdGmXp/3yS0ZA76kOlVq9Q==", - "license": "MIT", - "dependencies": { - "@babel/plugin-transform-react-jsx": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-pure-annotations": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.27.1.tgz", - "integrity": "sha512-JfuinvDOsD9FVMTHpzA/pBLisxpv1aSf+OIV8lgH3MuWrks19R27e6a6DipIg4aX1Zm9Wpb04p8wljfKrVSnPA==", - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-regenerator": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.29.0.tgz", - "integrity": "sha512-FijqlqMA7DmRdg/aINBSs04y8XNTYw/lr1gJ2WsmBnnaNw1iS43EPkJW+zK7z65auG3AWRFXWj+NcTQwYptUog==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-regexp-modifiers": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.28.6.tgz", - "integrity": "sha512-QGWAepm9qxpaIs7UM9FvUSnCGlb8Ua1RhyM4/veAxLwt3gMat/LSGrZixyuj4I6+Kn9iwvqCyPTtbdxanYoWYg==", - "license": "MIT", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.28.5", - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/plugin-transform-reserved-words": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.27.1.tgz", - "integrity": "sha512-V2ABPHIJX4kC7HegLkYoDpfg9PVmuWy/i6vUM5eGK22bx4YVFD3M5F0QQnWQoDs6AGsUWTVOopBiMFQgHaSkVw==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-runtime": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.29.0.tgz", - "integrity": "sha512-jlaRT5dJtMaMCV6fAuLbsQMSwz/QkvaHOHOSXRitGGwSpR1blCY4KUKoyP2tYO8vJcqYe8cEj96cqSztv3uF9w==", - "license": "MIT", - "dependencies": { - "@babel/helper-module-imports": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6", - "babel-plugin-polyfill-corejs2": "^0.4.14", - "babel-plugin-polyfill-corejs3": "^0.13.0", - "babel-plugin-polyfill-regenerator": "^0.6.5", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-runtime/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/plugin-transform-shorthand-properties": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.27.1.tgz", - "integrity": "sha512-N/wH1vcn4oYawbJ13Y/FxcQrWk63jhfNa7jef0ih7PHSIHX2LB7GWE1rkPrOnka9kwMxb6hMl19p7lidA+EHmQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-spread": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.28.6.tgz", - "integrity": "sha512-9U4QObUC0FtJl05AsUcodau/RWDytrU6uKgkxu09mLR9HLDAtUMoPuuskm5huQsoktmsYpI+bGmq+iapDcriKA==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-sticky-regex": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.27.1.tgz", - "integrity": "sha512-lhInBO5bi/Kowe2/aLdBAawijx+q1pQzicSgnkB6dUPc1+RC8QmJHKf2OjvU+NZWitguJHEaEmbV6VWEouT58g==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-template-literals": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.27.1.tgz", - "integrity": "sha512-fBJKiV7F2DxZUkg5EtHKXQdbsbURW3DZKQUWphDum0uRP6eHGGa/He9mc0mypL680pb+e/lDIthRohlv8NCHkg==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-typeof-symbol": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.27.1.tgz", - "integrity": "sha512-RiSILC+nRJM7FY5srIyc4/fGIwUhyDuuBSdWn4y6yT6gm652DpCHZjIipgn6B7MQ1ITOUnAKWixEUjQRIBIcLw==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-typescript": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.28.6.tgz", - "integrity": "sha512-0YWL2RFxOqEm9Efk5PvreamxPME8OyY0wM5wh5lHjF+VtVhdneCWGzZeSqzOfiobVqQaNCd2z0tQvnI9DaPWPw==", - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.3", - "@babel/helper-create-class-features-plugin": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", - "@babel/plugin-syntax-typescript": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-unicode-escapes": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.27.1.tgz", - "integrity": "sha512-Ysg4v6AmF26k9vpfFuTZg8HRfVWzsh1kVfowA23y9j/Gu6dOuahdUVhkLqpObp3JIv27MLSii6noRnuKN8H0Mg==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-unicode-property-regex": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.28.6.tgz", - "integrity": "sha512-4Wlbdl/sIZjzi/8St0evF0gEZrgOswVO6aOzqxh1kDZOl9WmLrHq2HtGhnOJZmHZYKP8WZ1MDLCt5DAWwRo57A==", - "license": "MIT", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.28.5", - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-unicode-regex": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.27.1.tgz", - "integrity": "sha512-xvINq24TRojDuyt6JGtHmkVkrfVV3FPT16uytxImLeBZqW3/H52yN+kM1MGuyPkIQxrzKwPHs5U/MP3qKyzkGw==", - "license": "MIT", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-unicode-sets-regex": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.28.6.tgz", - "integrity": "sha512-/wHc/paTUmsDYN7SZkpWxogTOBNnlx7nBQYfy6JJlCT7G3mVhltk3e++N7zV0XfgGsrqBxd4rJQt9H16I21Y1Q==", - "license": "MIT", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.28.5", - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/preset-env": { - "version": "7.29.2", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.29.2.tgz", - "integrity": "sha512-DYD23veRYGvBFhcTY1iUvJnDNpuqNd/BzBwCvzOTKUnJjKg5kpUBh3/u9585Agdkgj+QuygG7jLfOPWMa2KVNw==", - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.29.0", - "@babel/helper-compilation-targets": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/helper-validator-option": "^7.27.1", - "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.28.5", - "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.27.1", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.27.1", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.27.1", - "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.28.6", - "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", - "@babel/plugin-syntax-import-assertions": "^7.28.6", - "@babel/plugin-syntax-import-attributes": "^7.28.6", - "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", - "@babel/plugin-transform-arrow-functions": "^7.27.1", - "@babel/plugin-transform-async-generator-functions": "^7.29.0", - "@babel/plugin-transform-async-to-generator": "^7.28.6", - "@babel/plugin-transform-block-scoped-functions": "^7.27.1", - "@babel/plugin-transform-block-scoping": "^7.28.6", - "@babel/plugin-transform-class-properties": "^7.28.6", - "@babel/plugin-transform-class-static-block": "^7.28.6", - "@babel/plugin-transform-classes": "^7.28.6", - "@babel/plugin-transform-computed-properties": "^7.28.6", - "@babel/plugin-transform-destructuring": "^7.28.5", - "@babel/plugin-transform-dotall-regex": "^7.28.6", - "@babel/plugin-transform-duplicate-keys": "^7.27.1", - "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.29.0", - "@babel/plugin-transform-dynamic-import": "^7.27.1", - "@babel/plugin-transform-explicit-resource-management": "^7.28.6", - "@babel/plugin-transform-exponentiation-operator": "^7.28.6", - "@babel/plugin-transform-export-namespace-from": "^7.27.1", - "@babel/plugin-transform-for-of": "^7.27.1", - "@babel/plugin-transform-function-name": "^7.27.1", - "@babel/plugin-transform-json-strings": "^7.28.6", - "@babel/plugin-transform-literals": "^7.27.1", - "@babel/plugin-transform-logical-assignment-operators": "^7.28.6", - "@babel/plugin-transform-member-expression-literals": "^7.27.1", - "@babel/plugin-transform-modules-amd": "^7.27.1", - "@babel/plugin-transform-modules-commonjs": "^7.28.6", - "@babel/plugin-transform-modules-systemjs": "^7.29.0", - "@babel/plugin-transform-modules-umd": "^7.27.1", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.29.0", - "@babel/plugin-transform-new-target": "^7.27.1", - "@babel/plugin-transform-nullish-coalescing-operator": "^7.28.6", - "@babel/plugin-transform-numeric-separator": "^7.28.6", - "@babel/plugin-transform-object-rest-spread": "^7.28.6", - "@babel/plugin-transform-object-super": "^7.27.1", - "@babel/plugin-transform-optional-catch-binding": "^7.28.6", - "@babel/plugin-transform-optional-chaining": "^7.28.6", - "@babel/plugin-transform-parameters": "^7.27.7", - "@babel/plugin-transform-private-methods": "^7.28.6", - "@babel/plugin-transform-private-property-in-object": "^7.28.6", - "@babel/plugin-transform-property-literals": "^7.27.1", - "@babel/plugin-transform-regenerator": "^7.29.0", - "@babel/plugin-transform-regexp-modifiers": "^7.28.6", - "@babel/plugin-transform-reserved-words": "^7.27.1", - "@babel/plugin-transform-shorthand-properties": "^7.27.1", - "@babel/plugin-transform-spread": "^7.28.6", - "@babel/plugin-transform-sticky-regex": "^7.27.1", - "@babel/plugin-transform-template-literals": "^7.27.1", - "@babel/plugin-transform-typeof-symbol": "^7.27.1", - "@babel/plugin-transform-unicode-escapes": "^7.27.1", - "@babel/plugin-transform-unicode-property-regex": "^7.28.6", - "@babel/plugin-transform-unicode-regex": "^7.27.1", - "@babel/plugin-transform-unicode-sets-regex": "^7.28.6", - "@babel/preset-modules": "0.1.6-no-external-plugins", - "babel-plugin-polyfill-corejs2": "^0.4.15", - "babel-plugin-polyfill-corejs3": "^0.14.0", - "babel-plugin-polyfill-regenerator": "^0.6.6", - "core-js-compat": "^3.48.0", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/preset-env/node_modules/babel-plugin-polyfill-corejs3": { - "version": "0.14.2", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.14.2.tgz", - "integrity": "sha512-coWpDLJ410R781Npmn/SIBZEsAetR4xVi0SxLMXPaMO4lSf1MwnkGYMtkFxew0Dn8B3/CpbpYxN0JCgg8mn67g==", - "license": "MIT", - "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.6.8", - "core-js-compat": "^3.48.0" - }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" - } - }, - "node_modules/@babel/preset-env/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/preset-modules": { - "version": "0.1.6-no-external-plugins", - "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz", - "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/types": "^7.4.4", - "esutils": "^2.0.2" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0" - } - }, - "node_modules/@babel/preset-react": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.28.5.tgz", - "integrity": "sha512-Z3J8vhRq7CeLjdC58jLv4lnZ5RKFUJWqH5emvxmv9Hv3BD1T9R/Im713R4MTKwvFaV74ejZ3sM01LyEKk4ugNQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-validator-option": "^7.27.1", - "@babel/plugin-transform-react-display-name": "^7.28.0", - "@babel/plugin-transform-react-jsx": "^7.27.1", - "@babel/plugin-transform-react-jsx-development": "^7.27.1", - "@babel/plugin-transform-react-pure-annotations": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/preset-typescript": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.28.5.tgz", - "integrity": "sha512-+bQy5WOI2V6LJZpPVxY+yp66XdZ2yifu0Mc1aP5CQKgjn4QM5IN2i5fAZ4xKop47pr8rpVhiAeu+nDQa12C8+g==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-validator-option": "^7.27.1", - "@babel/plugin-syntax-jsx": "^7.27.1", - "@babel/plugin-transform-modules-commonjs": "^7.27.1", - "@babel/plugin-transform-typescript": "^7.28.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/runtime": { - "version": "7.29.2", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz", - "integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/runtime-corejs3": { - "version": "7.29.2", - "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.29.2.tgz", - "integrity": "sha512-Lc94FOD5+0aXhdb0Tdg3RUtqT6yWbI/BbFWvlaSJ3gAb9Ks+99nHRDKADVqC37er4eCB0fHyWT+y+K3QOvJKbw==", - "license": "MIT", - "dependencies": { - "core-js-pure": "^3.48.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/template": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", - "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.28.6", - "@babel/parser": "^7.28.6", - "@babel/types": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", - "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.29.0", - "@babel/generator": "^7.29.0", - "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.29.0", - "@babel/template": "^7.28.6", - "@babel/types": "^7.29.0", - "debug": "^4.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/types": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", - "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.28.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@colors/colors": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", - "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">=0.1.90" - } - }, - "node_modules/@csstools/cascade-layer-name-parser": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@csstools/cascade-layer-name-parser/-/cascade-layer-name-parser-2.0.5.tgz", - "integrity": "sha512-p1ko5eHgV+MgXFVa4STPKpvPxr6ReS8oS2jzTukjR74i5zJNyWO1ZM1m8YKBXnzDKWfBN1ztLYlHxbVemDD88A==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT", - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4" - } - }, - "node_modules/@csstools/color-helpers": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.1.0.tgz", - "integrity": "sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "engines": { - "node": ">=18" - } - }, - "node_modules/@csstools/css-calc": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-2.1.4.tgz", - "integrity": "sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT", - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4" - } - }, - "node_modules/@csstools/css-color-parser": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-3.1.0.tgz", - "integrity": "sha512-nbtKwh3a6xNVIp/VRuXV64yTKnb1IjTAEEh3irzS+HkKjAOYLTGNb9pmVNntZ8iVBHcWDA2Dof0QtPgFI1BaTA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT", - "dependencies": { - "@csstools/color-helpers": "^5.1.0", - "@csstools/css-calc": "^2.1.4" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4" - } - }, - "node_modules/@csstools/css-parser-algorithms": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.5.tgz", - "integrity": "sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT", - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@csstools/css-tokenizer": "^3.0.4" - } - }, - "node_modules/@csstools/css-tokenizer": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.4.tgz", - "integrity": "sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/@csstools/media-query-list-parser": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-4.0.3.tgz", - "integrity": "sha512-HAYH7d3TLRHDOUQK4mZKf9k9Ph/m8Akstg66ywKR4SFAigjs3yBiUeZtFxywiTm5moZMAp/5W/ZuFnNXXYLuuQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT", - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4" - } - }, - "node_modules/@csstools/postcss-alpha-function": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@csstools/postcss-alpha-function/-/postcss-alpha-function-1.0.1.tgz", - "integrity": "sha512-isfLLwksH3yHkFXfCI2Gcaqg7wGGHZZwunoJzEZk0yKYIokgre6hYVFibKL3SYAoR1kBXova8LB+JoO5vZzi9w==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "dependencies": { - "@csstools/css-color-parser": "^3.1.0", - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4", - "@csstools/postcss-progressive-custom-properties": "^4.2.1", - "@csstools/utilities": "^2.0.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-cascade-layers": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/@csstools/postcss-cascade-layers/-/postcss-cascade-layers-5.0.2.tgz", - "integrity": "sha512-nWBE08nhO8uWl6kSAeCx4im7QfVko3zLrtgWZY4/bP87zrSPpSyN/3W3TDqz1jJuH+kbKOHXg5rJnK+ZVYcFFg==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "dependencies": { - "@csstools/selector-specificity": "^5.0.0", - "postcss-selector-parser": "^7.0.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-cascade-layers/node_modules/@csstools/selector-specificity": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-5.0.0.tgz", - "integrity": "sha512-PCqQV3c4CoVm3kdPhyeZ07VmBRdH2EpMFA/pd9OASpOEC3aXNGoqPDAZ80D0cLpMBxnmk0+yNhGsEx31hq7Gtw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss-selector-parser": "^7.0.0" - } - }, - "node_modules/@csstools/postcss-cascade-layers/node_modules/postcss-selector-parser": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz", - "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", - "license": "MIT", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@csstools/postcss-color-function": { - "version": "4.0.12", - "resolved": "https://registry.npmjs.org/@csstools/postcss-color-function/-/postcss-color-function-4.0.12.tgz", - "integrity": "sha512-yx3cljQKRaSBc2hfh8rMZFZzChaFgwmO2JfFgFr1vMcF3C/uyy5I4RFIBOIWGq1D+XbKCG789CGkG6zzkLpagA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "dependencies": { - "@csstools/css-color-parser": "^3.1.0", - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4", - "@csstools/postcss-progressive-custom-properties": "^4.2.1", - "@csstools/utilities": "^2.0.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-color-function-display-p3-linear": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@csstools/postcss-color-function-display-p3-linear/-/postcss-color-function-display-p3-linear-1.0.1.tgz", - "integrity": "sha512-E5qusdzhlmO1TztYzDIi8XPdPoYOjoTY6HBYBCYSj+Gn4gQRBlvjgPQXzfzuPQqt8EhkC/SzPKObg4Mbn8/xMg==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "dependencies": { - "@csstools/css-color-parser": "^3.1.0", - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4", - "@csstools/postcss-progressive-custom-properties": "^4.2.1", - "@csstools/utilities": "^2.0.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-color-mix-function": { - "version": "3.0.12", - "resolved": "https://registry.npmjs.org/@csstools/postcss-color-mix-function/-/postcss-color-mix-function-3.0.12.tgz", - "integrity": "sha512-4STERZfCP5Jcs13P1U5pTvI9SkgLgfMUMhdXW8IlJWkzOOOqhZIjcNhWtNJZes2nkBDsIKJ0CJtFtuaZ00moag==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "dependencies": { - "@csstools/css-color-parser": "^3.1.0", - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4", - "@csstools/postcss-progressive-custom-properties": "^4.2.1", - "@csstools/utilities": "^2.0.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-color-mix-variadic-function-arguments": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@csstools/postcss-color-mix-variadic-function-arguments/-/postcss-color-mix-variadic-function-arguments-1.0.2.tgz", - "integrity": "sha512-rM67Gp9lRAkTo+X31DUqMEq+iK+EFqsidfecmhrteErxJZb6tUoJBVQca1Vn1GpDql1s1rD1pKcuYzMsg7Z1KQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "dependencies": { - "@csstools/css-color-parser": "^3.1.0", - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4", - "@csstools/postcss-progressive-custom-properties": "^4.2.1", - "@csstools/utilities": "^2.0.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-content-alt-text": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/@csstools/postcss-content-alt-text/-/postcss-content-alt-text-2.0.8.tgz", - "integrity": "sha512-9SfEW9QCxEpTlNMnpSqFaHyzsiRpZ5J5+KqCu1u5/eEJAWsMhzT40qf0FIbeeglEvrGRMdDzAxMIz3wqoGSb+Q==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "dependencies": { - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4", - "@csstools/postcss-progressive-custom-properties": "^4.2.1", - "@csstools/utilities": "^2.0.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-contrast-color-function": { - "version": "2.0.12", - "resolved": "https://registry.npmjs.org/@csstools/postcss-contrast-color-function/-/postcss-contrast-color-function-2.0.12.tgz", - "integrity": "sha512-YbwWckjK3qwKjeYz/CijgcS7WDUCtKTd8ShLztm3/i5dhh4NaqzsbYnhm4bjrpFpnLZ31jVcbK8YL77z3GBPzA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "dependencies": { - "@csstools/css-color-parser": "^3.1.0", - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4", - "@csstools/postcss-progressive-custom-properties": "^4.2.1", - "@csstools/utilities": "^2.0.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-exponential-functions": { - "version": "2.0.9", - "resolved": "https://registry.npmjs.org/@csstools/postcss-exponential-functions/-/postcss-exponential-functions-2.0.9.tgz", - "integrity": "sha512-abg2W/PI3HXwS/CZshSa79kNWNZHdJPMBXeZNyPQFbbj8sKO3jXxOt/wF7juJVjyDTc6JrvaUZYFcSBZBhaxjw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "dependencies": { - "@csstools/css-calc": "^2.1.4", - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-font-format-keywords": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@csstools/postcss-font-format-keywords/-/postcss-font-format-keywords-4.0.0.tgz", - "integrity": "sha512-usBzw9aCRDvchpok6C+4TXC57btc4bJtmKQWOHQxOVKen1ZfVqBUuCZ/wuqdX5GHsD0NRSr9XTP+5ID1ZZQBXw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "dependencies": { - "@csstools/utilities": "^2.0.0", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-gamut-mapping": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/@csstools/postcss-gamut-mapping/-/postcss-gamut-mapping-2.0.11.tgz", - "integrity": "sha512-fCpCUgZNE2piVJKC76zFsgVW1apF6dpYsqGyH8SIeCcM4pTEsRTWTLCaJIMKFEundsCKwY1rwfhtrio04RJ4Dw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "dependencies": { - "@csstools/css-color-parser": "^3.1.0", - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-gradients-interpolation-method": { - "version": "5.0.12", - "resolved": "https://registry.npmjs.org/@csstools/postcss-gradients-interpolation-method/-/postcss-gradients-interpolation-method-5.0.12.tgz", - "integrity": "sha512-jugzjwkUY0wtNrZlFeyXzimUL3hN4xMvoPnIXxoZqxDvjZRiSh+itgHcVUWzJ2VwD/VAMEgCLvtaJHX+4Vj3Ow==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "dependencies": { - "@csstools/css-color-parser": "^3.1.0", - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4", - "@csstools/postcss-progressive-custom-properties": "^4.2.1", - "@csstools/utilities": "^2.0.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-hwb-function": { - "version": "4.0.12", - "resolved": "https://registry.npmjs.org/@csstools/postcss-hwb-function/-/postcss-hwb-function-4.0.12.tgz", - "integrity": "sha512-mL/+88Z53KrE4JdePYFJAQWFrcADEqsLprExCM04GDNgHIztwFzj0Mbhd/yxMBngq0NIlz58VVxjt5abNs1VhA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "dependencies": { - "@csstools/css-color-parser": "^3.1.0", - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4", - "@csstools/postcss-progressive-custom-properties": "^4.2.1", - "@csstools/utilities": "^2.0.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-ic-unit": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@csstools/postcss-ic-unit/-/postcss-ic-unit-4.0.4.tgz", - "integrity": "sha512-yQ4VmossuOAql65sCPppVO1yfb7hDscf4GseF0VCA/DTDaBc0Wtf8MTqVPfjGYlT5+2buokG0Gp7y0atYZpwjg==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "dependencies": { - "@csstools/postcss-progressive-custom-properties": "^4.2.1", - "@csstools/utilities": "^2.0.0", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-initial": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@csstools/postcss-initial/-/postcss-initial-2.0.1.tgz", - "integrity": "sha512-L1wLVMSAZ4wovznquK0xmC7QSctzO4D0Is590bxpGqhqjboLXYA16dWZpfwImkdOgACdQ9PqXsuRroW6qPlEsg==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-is-pseudo-class": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/@csstools/postcss-is-pseudo-class/-/postcss-is-pseudo-class-5.0.3.tgz", - "integrity": "sha512-jS/TY4SpG4gszAtIg7Qnf3AS2pjcUM5SzxpApOrlndMeGhIbaTzWBzzP/IApXoNWEW7OhcjkRT48jnAUIFXhAQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "dependencies": { - "@csstools/selector-specificity": "^5.0.0", - "postcss-selector-parser": "^7.0.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-is-pseudo-class/node_modules/@csstools/selector-specificity": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-5.0.0.tgz", - "integrity": "sha512-PCqQV3c4CoVm3kdPhyeZ07VmBRdH2EpMFA/pd9OASpOEC3aXNGoqPDAZ80D0cLpMBxnmk0+yNhGsEx31hq7Gtw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss-selector-parser": "^7.0.0" - } - }, - "node_modules/@csstools/postcss-is-pseudo-class/node_modules/postcss-selector-parser": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz", - "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", - "license": "MIT", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@csstools/postcss-light-dark-function": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/@csstools/postcss-light-dark-function/-/postcss-light-dark-function-2.0.11.tgz", - "integrity": "sha512-fNJcKXJdPM3Lyrbmgw2OBbaioU7yuKZtiXClf4sGdQttitijYlZMD5K7HrC/eF83VRWRrYq6OZ0Lx92leV2LFA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "dependencies": { - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4", - "@csstools/postcss-progressive-custom-properties": "^4.2.1", - "@csstools/utilities": "^2.0.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-logical-float-and-clear": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@csstools/postcss-logical-float-and-clear/-/postcss-logical-float-and-clear-3.0.0.tgz", - "integrity": "sha512-SEmaHMszwakI2rqKRJgE+8rpotFfne1ZS6bZqBoQIicFyV+xT1UF42eORPxJkVJVrH9C0ctUgwMSn3BLOIZldQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-logical-overflow": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@csstools/postcss-logical-overflow/-/postcss-logical-overflow-2.0.0.tgz", - "integrity": "sha512-spzR1MInxPuXKEX2csMamshR4LRaSZ3UXVaRGjeQxl70ySxOhMpP2252RAFsg8QyyBXBzuVOOdx1+bVO5bPIzA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-logical-overscroll-behavior": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@csstools/postcss-logical-overscroll-behavior/-/postcss-logical-overscroll-behavior-2.0.0.tgz", - "integrity": "sha512-e/webMjoGOSYfqLunyzByZj5KKe5oyVg/YSbie99VEaSDE2kimFm0q1f6t/6Jo+VVCQ/jbe2Xy+uX+C4xzWs4w==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-logical-resize": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@csstools/postcss-logical-resize/-/postcss-logical-resize-3.0.0.tgz", - "integrity": "sha512-DFbHQOFW/+I+MY4Ycd/QN6Dg4Hcbb50elIJCfnwkRTCX05G11SwViI5BbBlg9iHRl4ytB7pmY5ieAFk3ws7yyg==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-logical-viewport-units": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@csstools/postcss-logical-viewport-units/-/postcss-logical-viewport-units-3.0.4.tgz", - "integrity": "sha512-q+eHV1haXA4w9xBwZLKjVKAWn3W2CMqmpNpZUk5kRprvSiBEGMgrNH3/sJZ8UA3JgyHaOt3jwT9uFa4wLX4EqQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "dependencies": { - "@csstools/css-tokenizer": "^3.0.4", - "@csstools/utilities": "^2.0.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-media-minmax": { - "version": "2.0.9", - "resolved": "https://registry.npmjs.org/@csstools/postcss-media-minmax/-/postcss-media-minmax-2.0.9.tgz", - "integrity": "sha512-af9Qw3uS3JhYLnCbqtZ9crTvvkR+0Se+bBqSr7ykAnl9yKhk6895z9rf+2F4dClIDJWxgn0iZZ1PSdkhrbs2ig==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT", - "dependencies": { - "@csstools/css-calc": "^2.1.4", - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4", - "@csstools/media-query-list-parser": "^4.0.3" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-media-queries-aspect-ratio-number-values": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@csstools/postcss-media-queries-aspect-ratio-number-values/-/postcss-media-queries-aspect-ratio-number-values-3.0.5.tgz", - "integrity": "sha512-zhAe31xaaXOY2Px8IYfoVTB3wglbJUVigGphFLj6exb7cjZRH9A6adyE22XfFK3P2PzwRk0VDeTJmaxpluyrDg==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "dependencies": { - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4", - "@csstools/media-query-list-parser": "^4.0.3" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-nested-calc": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@csstools/postcss-nested-calc/-/postcss-nested-calc-4.0.0.tgz", - "integrity": "sha512-jMYDdqrQQxE7k9+KjstC3NbsmC063n1FTPLCgCRS2/qHUbHM0mNy9pIn4QIiQGs9I/Bg98vMqw7mJXBxa0N88A==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "dependencies": { - "@csstools/utilities": "^2.0.0", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-normalize-display-values": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@csstools/postcss-normalize-display-values/-/postcss-normalize-display-values-4.0.1.tgz", - "integrity": "sha512-TQUGBuRvxdc7TgNSTevYqrL8oItxiwPDixk20qCB5me/W8uF7BPbhRrAvFuhEoywQp/woRsUZ6SJ+sU5idZAIA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-oklab-function": { - "version": "4.0.12", - "resolved": "https://registry.npmjs.org/@csstools/postcss-oklab-function/-/postcss-oklab-function-4.0.12.tgz", - "integrity": "sha512-HhlSmnE1NKBhXsTnNGjxvhryKtO7tJd1w42DKOGFD6jSHtYOrsJTQDKPMwvOfrzUAk8t7GcpIfRyM7ssqHpFjg==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "dependencies": { - "@csstools/css-color-parser": "^3.1.0", - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4", - "@csstools/postcss-progressive-custom-properties": "^4.2.1", - "@csstools/utilities": "^2.0.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-position-area-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@csstools/postcss-position-area-property/-/postcss-position-area-property-1.0.0.tgz", - "integrity": "sha512-fUP6KR8qV2NuUZV3Cw8itx0Ep90aRjAZxAEzC3vrl6yjFv+pFsQbR18UuQctEKmA72K9O27CoYiKEgXxkqjg8Q==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-progressive-custom-properties": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@csstools/postcss-progressive-custom-properties/-/postcss-progressive-custom-properties-4.2.1.tgz", - "integrity": "sha512-uPiiXf7IEKtUQXsxu6uWtOlRMXd2QWWy5fhxHDnPdXKCQckPP3E34ZgDoZ62r2iT+UOgWsSbM4NvHE5m3mAEdw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-property-rule-prelude-list": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@csstools/postcss-property-rule-prelude-list/-/postcss-property-rule-prelude-list-1.0.0.tgz", - "integrity": "sha512-IxuQjUXq19fobgmSSvUDO7fVwijDJaZMvWQugxfEUxmjBeDCVaDuMpsZ31MsTm5xbnhA+ElDi0+rQ7sQQGisFA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "dependencies": { - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-random-function": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@csstools/postcss-random-function/-/postcss-random-function-2.0.1.tgz", - "integrity": "sha512-q+FQaNiRBhnoSNo+GzqGOIBKoHQ43lYz0ICrV+UudfWnEF6ksS6DsBIJSISKQT2Bvu3g4k6r7t0zYrk5pDlo8w==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "dependencies": { - "@csstools/css-calc": "^2.1.4", - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-relative-color-syntax": { - "version": "3.0.12", - "resolved": "https://registry.npmjs.org/@csstools/postcss-relative-color-syntax/-/postcss-relative-color-syntax-3.0.12.tgz", - "integrity": "sha512-0RLIeONxu/mtxRtf3o41Lq2ghLimw0w9ByLWnnEVuy89exmEEq8bynveBxNW3nyHqLAFEeNtVEmC1QK9MZ8Huw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "dependencies": { - "@csstools/css-color-parser": "^3.1.0", - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4", - "@csstools/postcss-progressive-custom-properties": "^4.2.1", - "@csstools/utilities": "^2.0.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-scope-pseudo-class": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@csstools/postcss-scope-pseudo-class/-/postcss-scope-pseudo-class-4.0.1.tgz", - "integrity": "sha512-IMi9FwtH6LMNuLea1bjVMQAsUhFxJnyLSgOp/cpv5hrzWmrUYU5fm0EguNDIIOHUqzXode8F/1qkC/tEo/qN8Q==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "dependencies": { - "postcss-selector-parser": "^7.0.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-scope-pseudo-class/node_modules/postcss-selector-parser": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz", - "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", - "license": "MIT", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@csstools/postcss-sign-functions": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@csstools/postcss-sign-functions/-/postcss-sign-functions-1.1.4.tgz", - "integrity": "sha512-P97h1XqRPcfcJndFdG95Gv/6ZzxUBBISem0IDqPZ7WMvc/wlO+yU0c5D/OCpZ5TJoTt63Ok3knGk64N+o6L2Pg==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "dependencies": { - "@csstools/css-calc": "^2.1.4", - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-stepped-value-functions": { - "version": "4.0.9", - "resolved": "https://registry.npmjs.org/@csstools/postcss-stepped-value-functions/-/postcss-stepped-value-functions-4.0.9.tgz", - "integrity": "sha512-h9btycWrsex4dNLeQfyU3y3w40LMQooJWFMm/SK9lrKguHDcFl4VMkncKKoXi2z5rM9YGWbUQABI8BT2UydIcA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "dependencies": { - "@csstools/css-calc": "^2.1.4", - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-syntax-descriptor-syntax-production": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@csstools/postcss-syntax-descriptor-syntax-production/-/postcss-syntax-descriptor-syntax-production-1.0.1.tgz", - "integrity": "sha512-GneqQWefjM//f4hJ/Kbox0C6f2T7+pi4/fqTqOFGTL3EjnvOReTqO1qUQ30CaUjkwjYq9qZ41hzarrAxCc4gow==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "dependencies": { - "@csstools/css-tokenizer": "^3.0.4" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-system-ui-font-family": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@csstools/postcss-system-ui-font-family/-/postcss-system-ui-font-family-1.0.0.tgz", - "integrity": "sha512-s3xdBvfWYfoPSBsikDXbuorcMG1nN1M6GdU0qBsGfcmNR0A/qhloQZpTxjA3Xsyrk1VJvwb2pOfiOT3at/DuIQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "dependencies": { - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-text-decoration-shorthand": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@csstools/postcss-text-decoration-shorthand/-/postcss-text-decoration-shorthand-4.0.3.tgz", - "integrity": "sha512-KSkGgZfx0kQjRIYnpsD7X2Om9BUXX/Kii77VBifQW9Ih929hK0KNjVngHDH0bFB9GmfWcR9vJYJJRvw/NQjkrA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "dependencies": { - "@csstools/color-helpers": "^5.1.0", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-trigonometric-functions": { - "version": "4.0.9", - "resolved": "https://registry.npmjs.org/@csstools/postcss-trigonometric-functions/-/postcss-trigonometric-functions-4.0.9.tgz", - "integrity": "sha512-Hnh5zJUdpNrJqK9v1/E3BbrQhaDTj5YiX7P61TOvUhoDHnUmsNNxcDAgkQ32RrcWx9GVUvfUNPcUkn8R3vIX6A==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "dependencies": { - "@csstools/css-calc": "^2.1.4", - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-unset-value": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@csstools/postcss-unset-value/-/postcss-unset-value-4.0.0.tgz", - "integrity": "sha512-cBz3tOCI5Fw6NIFEwU3RiwK6mn3nKegjpJuzCndoGq3BZPkUjnsq7uQmIeMNeMbMk7YD2MfKcgCpZwX5jyXqCA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/utilities": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@csstools/utilities/-/utilities-2.0.0.tgz", - "integrity": "sha512-5VdOr0Z71u+Yp3ozOx8T11N703wIFGVRgOWbOZMKgglPJsWA54MRIoMNVMa7shUToIhx5J8vX4sOZgD2XiihiQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@discoveryjs/json-ext": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", - "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", - "license": "MIT", - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/@docsearch/core": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/@docsearch/core/-/core-4.6.2.tgz", - "integrity": "sha512-/S0e6Dj7Zcm8m9Rru49YEX49dhU11be68c+S/BCyN8zQsTTgkKzXlhRbVL5mV6lOLC2+ZRRryaTdcm070Ug2oA==", - "license": "MIT", - "peerDependencies": { - "@types/react": ">= 16.8.0 < 20.0.0", - "react": ">= 16.8.0 < 20.0.0", - "react-dom": ">= 16.8.0 < 20.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "react": { - "optional": true - }, - "react-dom": { - "optional": true - } - } - }, - "node_modules/@docsearch/css": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/@docsearch/css/-/css-4.6.2.tgz", - "integrity": "sha512-fH/cn8BjEEdM2nJdjNMHIvOVYupG6AIDtFVDgIZrNzdCSj4KXr9kd+hsehqsNGYjpUjObeKYKvgy/IwCb1jZYQ==", - "license": "MIT" - }, - "node_modules/@docsearch/react": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/@docsearch/react/-/react-4.6.2.tgz", - "integrity": "sha512-/BbtGFtqVOGwZx0dw/UfhN/0/DmMQYnulY4iv0tPRhC2JCXv0ka/+izwt3Jzo1ZxXS/2eMvv9zHsBJOK1I9f/w==", - "license": "MIT", - "dependencies": { - "@algolia/autocomplete-core": "1.19.2", - "@docsearch/core": "4.6.2", - "@docsearch/css": "4.6.2" - }, - "peerDependencies": { - "@types/react": ">= 16.8.0 < 20.0.0", - "react": ">= 16.8.0 < 20.0.0", - "react-dom": ">= 16.8.0 < 20.0.0", - "search-insights": ">= 1 < 3" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "react": { - "optional": true - }, - "react-dom": { - "optional": true - }, - "search-insights": { - "optional": true - } - } - }, - "node_modules/@docusaurus/babel": { - "version": "3.9.2", - "resolved": "https://registry.npmjs.org/@docusaurus/babel/-/babel-3.9.2.tgz", - "integrity": "sha512-GEANdi/SgER+L7Japs25YiGil/AUDnFFHaCGPBbundxoWtCkA2lmy7/tFmgED4y1htAy6Oi4wkJEQdGssnw9MA==", - "license": "MIT", - "dependencies": { - "@babel/core": "^7.25.9", - "@babel/generator": "^7.25.9", - "@babel/plugin-syntax-dynamic-import": "^7.8.3", - "@babel/plugin-transform-runtime": "^7.25.9", - "@babel/preset-env": "^7.25.9", - "@babel/preset-react": "^7.25.9", - "@babel/preset-typescript": "^7.25.9", - "@babel/runtime": "^7.25.9", - "@babel/runtime-corejs3": "^7.25.9", - "@babel/traverse": "^7.25.9", - "@docusaurus/logger": "3.9.2", - "@docusaurus/utils": "3.9.2", - "babel-plugin-dynamic-import-node": "^2.3.3", - "fs-extra": "^11.1.1", - "tslib": "^2.6.0" - }, - "engines": { - "node": ">=20.0" - } - }, - "node_modules/@docusaurus/bundler": { - "version": "3.9.2", - "resolved": "https://registry.npmjs.org/@docusaurus/bundler/-/bundler-3.9.2.tgz", - "integrity": "sha512-ZOVi6GYgTcsZcUzjblpzk3wH1Fya2VNpd5jtHoCCFcJlMQ1EYXZetfAnRHLcyiFeBABaI1ltTYbOBtH/gahGVA==", - "license": "MIT", - "dependencies": { - "@babel/core": "^7.25.9", - "@docusaurus/babel": "3.9.2", - "@docusaurus/cssnano-preset": "3.9.2", - "@docusaurus/logger": "3.9.2", - "@docusaurus/types": "3.9.2", - "@docusaurus/utils": "3.9.2", - "babel-loader": "^9.2.1", - "clean-css": "^5.3.3", - "copy-webpack-plugin": "^11.0.0", - "css-loader": "^6.11.0", - "css-minimizer-webpack-plugin": "^5.0.1", - "cssnano": "^6.1.2", - "file-loader": "^6.2.0", - "html-minifier-terser": "^7.2.0", - "mini-css-extract-plugin": "^2.9.2", - "null-loader": "^4.0.1", - "postcss": "^8.5.4", - "postcss-loader": "^7.3.4", - "postcss-preset-env": "^10.2.1", - "terser-webpack-plugin": "^5.3.9", - "tslib": "^2.6.0", - "url-loader": "^4.1.1", - "webpack": "^5.95.0", - "webpackbar": "^6.0.1" - }, - "engines": { - "node": ">=20.0" - }, - "peerDependencies": { - "@docusaurus/faster": "*" - }, - "peerDependenciesMeta": { - "@docusaurus/faster": { - "optional": true - } - } - }, - "node_modules/@docusaurus/core": { - "version": "3.9.2", - "resolved": "https://registry.npmjs.org/@docusaurus/core/-/core-3.9.2.tgz", - "integrity": "sha512-HbjwKeC+pHUFBfLMNzuSjqFE/58+rLVKmOU3lxQrpsxLBOGosYco/Q0GduBb0/jEMRiyEqjNT/01rRdOMWq5pw==", - "license": "MIT", - "dependencies": { - "@docusaurus/babel": "3.9.2", - "@docusaurus/bundler": "3.9.2", - "@docusaurus/logger": "3.9.2", - "@docusaurus/mdx-loader": "3.9.2", - "@docusaurus/utils": "3.9.2", - "@docusaurus/utils-common": "3.9.2", - "@docusaurus/utils-validation": "3.9.2", - "boxen": "^6.2.1", - "chalk": "^4.1.2", - "chokidar": "^3.5.3", - "cli-table3": "^0.6.3", - "combine-promises": "^1.1.0", - "commander": "^5.1.0", - "core-js": "^3.31.1", - "detect-port": "^1.5.1", - "escape-html": "^1.0.3", - "eta": "^2.2.0", - "eval": "^0.1.8", - "execa": "5.1.1", - "fs-extra": "^11.1.1", - "html-tags": "^3.3.1", - "html-webpack-plugin": "^5.6.0", - "leven": "^3.1.0", - "lodash": "^4.17.21", - "open": "^8.4.0", - "p-map": "^4.0.0", - "prompts": "^2.4.2", - "react-helmet-async": "npm:@slorber/react-helmet-async@1.3.0", - "react-loadable": "npm:@docusaurus/react-loadable@6.0.0", - "react-loadable-ssr-addon-v5-slorber": "^1.0.1", - "react-router": "^5.3.4", - "react-router-config": "^5.1.1", - "react-router-dom": "^5.3.4", - "semver": "^7.5.4", - "serve-handler": "^6.1.6", - "tinypool": "^1.0.2", - "tslib": "^2.6.0", - "update-notifier": "^6.0.2", - "webpack": "^5.95.0", - "webpack-bundle-analyzer": "^4.10.2", - "webpack-dev-server": "^5.2.2", - "webpack-merge": "^6.0.1" - }, - "bin": { - "docusaurus": "bin/docusaurus.mjs" - }, - "engines": { - "node": ">=20.0" - }, - "peerDependencies": { - "@mdx-js/react": "^3.0.0", - "react": "^18.0.0 || ^19.0.0", - "react-dom": "^18.0.0 || ^19.0.0" - } - }, - "node_modules/@docusaurus/cssnano-preset": { - "version": "3.9.2", - "resolved": "https://registry.npmjs.org/@docusaurus/cssnano-preset/-/cssnano-preset-3.9.2.tgz", - "integrity": "sha512-8gBKup94aGttRduABsj7bpPFTX7kbwu+xh3K9NMCF5K4bWBqTFYW+REKHF6iBVDHRJ4grZdIPbvkiHd/XNKRMQ==", - "license": "MIT", - "dependencies": { - "cssnano-preset-advanced": "^6.1.2", - "postcss": "^8.5.4", - "postcss-sort-media-queries": "^5.2.0", - "tslib": "^2.6.0" - }, - "engines": { - "node": ">=20.0" - } - }, - "node_modules/@docusaurus/logger": { - "version": "3.9.2", - "resolved": "https://registry.npmjs.org/@docusaurus/logger/-/logger-3.9.2.tgz", - "integrity": "sha512-/SVCc57ByARzGSU60c50rMyQlBuMIJCjcsJlkphxY6B0GV4UH3tcA1994N8fFfbJ9kX3jIBe/xg3XP5qBtGDbA==", - "license": "MIT", - "dependencies": { - "chalk": "^4.1.2", - "tslib": "^2.6.0" - }, - "engines": { - "node": ">=20.0" - } - }, - "node_modules/@docusaurus/mdx-loader": { - "version": "3.9.2", - "resolved": "https://registry.npmjs.org/@docusaurus/mdx-loader/-/mdx-loader-3.9.2.tgz", - "integrity": "sha512-wiYoGwF9gdd6rev62xDU8AAM8JuLI/hlwOtCzMmYcspEkzecKrP8J8X+KpYnTlACBUUtXNJpSoCwFWJhLRevzQ==", - "license": "MIT", - "dependencies": { - "@docusaurus/logger": "3.9.2", - "@docusaurus/utils": "3.9.2", - "@docusaurus/utils-validation": "3.9.2", - "@mdx-js/mdx": "^3.0.0", - "@slorber/remark-comment": "^1.0.0", - "escape-html": "^1.0.3", - "estree-util-value-to-estree": "^3.0.1", - "file-loader": "^6.2.0", - "fs-extra": "^11.1.1", - "image-size": "^2.0.2", - "mdast-util-mdx": "^3.0.0", - "mdast-util-to-string": "^4.0.0", - "rehype-raw": "^7.0.0", - "remark-directive": "^3.0.0", - "remark-emoji": "^4.0.0", - "remark-frontmatter": "^5.0.0", - "remark-gfm": "^4.0.0", - "stringify-object": "^3.3.0", - "tslib": "^2.6.0", - "unified": "^11.0.3", - "unist-util-visit": "^5.0.0", - "url-loader": "^4.1.1", - "vfile": "^6.0.1", - "webpack": "^5.88.1" - }, - "engines": { - "node": ">=20.0" - }, - "peerDependencies": { - "react": "^18.0.0 || ^19.0.0", - "react-dom": "^18.0.0 || ^19.0.0" - } - }, - "node_modules/@docusaurus/module-type-aliases": { - "version": "3.9.2", - "resolved": "https://registry.npmjs.org/@docusaurus/module-type-aliases/-/module-type-aliases-3.9.2.tgz", - "integrity": "sha512-8qVe2QA9hVLzvnxP46ysuofJUIc/yYQ82tvA/rBTrnpXtCjNSFLxEZfd5U8cYZuJIVlkPxamsIgwd5tGZXfvew==", - "license": "MIT", - "dependencies": { - "@docusaurus/types": "3.9.2", - "@types/history": "^4.7.11", - "@types/react": "*", - "@types/react-router-config": "*", - "@types/react-router-dom": "*", - "react-helmet-async": "npm:@slorber/react-helmet-async@1.3.0", - "react-loadable": "npm:@docusaurus/react-loadable@6.0.0" - }, - "peerDependencies": { - "react": "*", - "react-dom": "*" - } - }, - "node_modules/@docusaurus/plugin-content-blog": { - "version": "3.9.2", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-blog/-/plugin-content-blog-3.9.2.tgz", - "integrity": "sha512-3I2HXy3L1QcjLJLGAoTvoBnpOwa6DPUa3Q0dMK19UTY9mhPkKQg/DYhAGTiBUKcTR0f08iw7kLPqOhIgdV3eVQ==", - "license": "MIT", - "dependencies": { - "@docusaurus/core": "3.9.2", - "@docusaurus/logger": "3.9.2", - "@docusaurus/mdx-loader": "3.9.2", - "@docusaurus/theme-common": "3.9.2", - "@docusaurus/types": "3.9.2", - "@docusaurus/utils": "3.9.2", - "@docusaurus/utils-common": "3.9.2", - "@docusaurus/utils-validation": "3.9.2", - "cheerio": "1.0.0-rc.12", - "feed": "^4.2.2", - "fs-extra": "^11.1.1", - "lodash": "^4.17.21", - "schema-dts": "^1.1.2", - "srcset": "^4.0.0", - "tslib": "^2.6.0", - "unist-util-visit": "^5.0.0", - "utility-types": "^3.10.0", - "webpack": "^5.88.1" - }, - "engines": { - "node": ">=20.0" - }, - "peerDependencies": { - "@docusaurus/plugin-content-docs": "*", - "react": "^18.0.0 || ^19.0.0", - "react-dom": "^18.0.0 || ^19.0.0" - } - }, - "node_modules/@docusaurus/plugin-content-docs": { - "version": "3.9.2", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-docs/-/plugin-content-docs-3.9.2.tgz", - "integrity": "sha512-C5wZsGuKTY8jEYsqdxhhFOe1ZDjH0uIYJ9T/jebHwkyxqnr4wW0jTkB72OMqNjsoQRcb0JN3PcSeTwFlVgzCZg==", - "license": "MIT", - "dependencies": { - "@docusaurus/core": "3.9.2", - "@docusaurus/logger": "3.9.2", - "@docusaurus/mdx-loader": "3.9.2", - "@docusaurus/module-type-aliases": "3.9.2", - "@docusaurus/theme-common": "3.9.2", - "@docusaurus/types": "3.9.2", - "@docusaurus/utils": "3.9.2", - "@docusaurus/utils-common": "3.9.2", - "@docusaurus/utils-validation": "3.9.2", - "@types/react-router-config": "^5.0.7", - "combine-promises": "^1.1.0", - "fs-extra": "^11.1.1", - "js-yaml": "^4.1.0", - "lodash": "^4.17.21", - "schema-dts": "^1.1.2", - "tslib": "^2.6.0", - "utility-types": "^3.10.0", - "webpack": "^5.88.1" - }, - "engines": { - "node": ">=20.0" - }, - "peerDependencies": { - "react": "^18.0.0 || ^19.0.0", - "react-dom": "^18.0.0 || ^19.0.0" - } - }, - "node_modules/@docusaurus/plugin-content-pages": { - "version": "3.9.2", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-pages/-/plugin-content-pages-3.9.2.tgz", - "integrity": "sha512-s4849w/p4noXUrGpPUF0BPqIAfdAe76BLaRGAGKZ1gTDNiGxGcpsLcwJ9OTi1/V8A+AzvsmI9pkjie2zjIQZKA==", - "license": "MIT", - "dependencies": { - "@docusaurus/core": "3.9.2", - "@docusaurus/mdx-loader": "3.9.2", - "@docusaurus/types": "3.9.2", - "@docusaurus/utils": "3.9.2", - "@docusaurus/utils-validation": "3.9.2", - "fs-extra": "^11.1.1", - "tslib": "^2.6.0", - "webpack": "^5.88.1" - }, - "engines": { - "node": ">=20.0" - }, - "peerDependencies": { - "react": "^18.0.0 || ^19.0.0", - "react-dom": "^18.0.0 || ^19.0.0" - } - }, - "node_modules/@docusaurus/plugin-css-cascade-layers": { - "version": "3.9.2", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-css-cascade-layers/-/plugin-css-cascade-layers-3.9.2.tgz", - "integrity": "sha512-w1s3+Ss+eOQbscGM4cfIFBlVg/QKxyYgj26k5AnakuHkKxH6004ZtuLe5awMBotIYF2bbGDoDhpgQ4r/kcj4rQ==", - "license": "MIT", - "dependencies": { - "@docusaurus/core": "3.9.2", - "@docusaurus/types": "3.9.2", - "@docusaurus/utils": "3.9.2", - "@docusaurus/utils-validation": "3.9.2", - "tslib": "^2.6.0" - }, - "engines": { - "node": ">=20.0" - } - }, - "node_modules/@docusaurus/plugin-debug": { - "version": "3.9.2", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-debug/-/plugin-debug-3.9.2.tgz", - "integrity": "sha512-j7a5hWuAFxyQAkilZwhsQ/b3T7FfHZ+0dub6j/GxKNFJp2h9qk/P1Bp7vrGASnvA9KNQBBL1ZXTe7jlh4VdPdA==", - "license": "MIT", - "dependencies": { - "@docusaurus/core": "3.9.2", - "@docusaurus/types": "3.9.2", - "@docusaurus/utils": "3.9.2", - "fs-extra": "^11.1.1", - "react-json-view-lite": "^2.3.0", - "tslib": "^2.6.0" - }, - "engines": { - "node": ">=20.0" - }, - "peerDependencies": { - "react": "^18.0.0 || ^19.0.0", - "react-dom": "^18.0.0 || ^19.0.0" - } - }, - "node_modules/@docusaurus/plugin-google-analytics": { - "version": "3.9.2", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-analytics/-/plugin-google-analytics-3.9.2.tgz", - "integrity": "sha512-mAwwQJ1Us9jL/lVjXtErXto4p4/iaLlweC54yDUK1a97WfkC6Z2k5/769JsFgwOwOP+n5mUQGACXOEQ0XDuVUw==", - "license": "MIT", - "dependencies": { - "@docusaurus/core": "3.9.2", - "@docusaurus/types": "3.9.2", - "@docusaurus/utils-validation": "3.9.2", - "tslib": "^2.6.0" - }, - "engines": { - "node": ">=20.0" - }, - "peerDependencies": { - "react": "^18.0.0 || ^19.0.0", - "react-dom": "^18.0.0 || ^19.0.0" - } - }, - "node_modules/@docusaurus/plugin-google-gtag": { - "version": "3.9.2", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-gtag/-/plugin-google-gtag-3.9.2.tgz", - "integrity": "sha512-YJ4lDCphabBtw19ooSlc1MnxtYGpjFV9rEdzjLsUnBCeis2djUyCozZaFhCg6NGEwOn7HDDyMh0yzcdRpnuIvA==", - "license": "MIT", - "dependencies": { - "@docusaurus/core": "3.9.2", - "@docusaurus/types": "3.9.2", - "@docusaurus/utils-validation": "3.9.2", - "@types/gtag.js": "^0.0.12", - "tslib": "^2.6.0" - }, - "engines": { - "node": ">=20.0" - }, - "peerDependencies": { - "react": "^18.0.0 || ^19.0.0", - "react-dom": "^18.0.0 || ^19.0.0" - } - }, - "node_modules/@docusaurus/plugin-google-tag-manager": { - "version": "3.9.2", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-tag-manager/-/plugin-google-tag-manager-3.9.2.tgz", - "integrity": "sha512-LJtIrkZN/tuHD8NqDAW1Tnw0ekOwRTfobWPsdO15YxcicBo2ykKF0/D6n0vVBfd3srwr9Z6rzrIWYrMzBGrvNw==", - "license": "MIT", - "dependencies": { - "@docusaurus/core": "3.9.2", - "@docusaurus/types": "3.9.2", - "@docusaurus/utils-validation": "3.9.2", - "tslib": "^2.6.0" - }, - "engines": { - "node": ">=20.0" - }, - "peerDependencies": { - "react": "^18.0.0 || ^19.0.0", - "react-dom": "^18.0.0 || ^19.0.0" - } - }, - "node_modules/@docusaurus/plugin-sitemap": { - "version": "3.9.2", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-sitemap/-/plugin-sitemap-3.9.2.tgz", - "integrity": "sha512-WLh7ymgDXjG8oPoM/T4/zUP7KcSuFYRZAUTl8vR6VzYkfc18GBM4xLhcT+AKOwun6kBivYKUJf+vlqYJkm+RHw==", - "license": "MIT", - "dependencies": { - "@docusaurus/core": "3.9.2", - "@docusaurus/logger": "3.9.2", - "@docusaurus/types": "3.9.2", - "@docusaurus/utils": "3.9.2", - "@docusaurus/utils-common": "3.9.2", - "@docusaurus/utils-validation": "3.9.2", - "fs-extra": "^11.1.1", - "sitemap": "^7.1.1", - "tslib": "^2.6.0" - }, - "engines": { - "node": ">=20.0" - }, - "peerDependencies": { - "react": "^18.0.0 || ^19.0.0", - "react-dom": "^18.0.0 || ^19.0.0" - } - }, - "node_modules/@docusaurus/plugin-svgr": { - "version": "3.9.2", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-svgr/-/plugin-svgr-3.9.2.tgz", - "integrity": "sha512-n+1DE+5b3Lnf27TgVU5jM1d4x5tUh2oW5LTsBxJX4PsAPV0JGcmI6p3yLYtEY0LRVEIJh+8RsdQmRE66wSV8mw==", - "license": "MIT", - "dependencies": { - "@docusaurus/core": "3.9.2", - "@docusaurus/types": "3.9.2", - "@docusaurus/utils": "3.9.2", - "@docusaurus/utils-validation": "3.9.2", - "@svgr/core": "8.1.0", - "@svgr/webpack": "^8.1.0", - "tslib": "^2.6.0", - "webpack": "^5.88.1" - }, - "engines": { - "node": ">=20.0" - }, - "peerDependencies": { - "react": "^18.0.0 || ^19.0.0", - "react-dom": "^18.0.0 || ^19.0.0" - } - }, - "node_modules/@docusaurus/preset-classic": { - "version": "3.9.2", - "resolved": "https://registry.npmjs.org/@docusaurus/preset-classic/-/preset-classic-3.9.2.tgz", - "integrity": "sha512-IgyYO2Gvaigi21LuDIe+nvmN/dfGXAiMcV/murFqcpjnZc7jxFAxW+9LEjdPt61uZLxG4ByW/oUmX/DDK9t/8w==", - "license": "MIT", - "dependencies": { - "@docusaurus/core": "3.9.2", - "@docusaurus/plugin-content-blog": "3.9.2", - "@docusaurus/plugin-content-docs": "3.9.2", - "@docusaurus/plugin-content-pages": "3.9.2", - "@docusaurus/plugin-css-cascade-layers": "3.9.2", - "@docusaurus/plugin-debug": "3.9.2", - "@docusaurus/plugin-google-analytics": "3.9.2", - "@docusaurus/plugin-google-gtag": "3.9.2", - "@docusaurus/plugin-google-tag-manager": "3.9.2", - "@docusaurus/plugin-sitemap": "3.9.2", - "@docusaurus/plugin-svgr": "3.9.2", - "@docusaurus/theme-classic": "3.9.2", - "@docusaurus/theme-common": "3.9.2", - "@docusaurus/theme-search-algolia": "3.9.2", - "@docusaurus/types": "3.9.2" - }, - "engines": { - "node": ">=20.0" - }, - "peerDependencies": { - "react": "^18.0.0 || ^19.0.0", - "react-dom": "^18.0.0 || ^19.0.0" - } - }, - "node_modules/@docusaurus/theme-classic": { - "version": "3.9.2", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-classic/-/theme-classic-3.9.2.tgz", - "integrity": "sha512-IGUsArG5hhekXd7RDb11v94ycpJpFdJPkLnt10fFQWOVxAtq5/D7hT6lzc2fhyQKaaCE62qVajOMKL7OiAFAIA==", - "license": "MIT", - "dependencies": { - "@docusaurus/core": "3.9.2", - "@docusaurus/logger": "3.9.2", - "@docusaurus/mdx-loader": "3.9.2", - "@docusaurus/module-type-aliases": "3.9.2", - "@docusaurus/plugin-content-blog": "3.9.2", - "@docusaurus/plugin-content-docs": "3.9.2", - "@docusaurus/plugin-content-pages": "3.9.2", - "@docusaurus/theme-common": "3.9.2", - "@docusaurus/theme-translations": "3.9.2", - "@docusaurus/types": "3.9.2", - "@docusaurus/utils": "3.9.2", - "@docusaurus/utils-common": "3.9.2", - "@docusaurus/utils-validation": "3.9.2", - "@mdx-js/react": "^3.0.0", - "clsx": "^2.0.0", - "infima": "0.2.0-alpha.45", - "lodash": "^4.17.21", - "nprogress": "^0.2.0", - "postcss": "^8.5.4", - "prism-react-renderer": "^2.3.0", - "prismjs": "^1.29.0", - "react-router-dom": "^5.3.4", - "rtlcss": "^4.1.0", - "tslib": "^2.6.0", - "utility-types": "^3.10.0" - }, - "engines": { - "node": ">=20.0" - }, - "peerDependencies": { - "react": "^18.0.0 || ^19.0.0", - "react-dom": "^18.0.0 || ^19.0.0" - } - }, - "node_modules/@docusaurus/theme-common": { - "version": "3.9.2", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-common/-/theme-common-3.9.2.tgz", - "integrity": "sha512-6c4DAbR6n6nPbnZhY2V3tzpnKnGL+6aOsLvFL26VRqhlczli9eWG0VDUNoCQEPnGwDMhPS42UhSAnz5pThm5Ag==", - "license": "MIT", - "dependencies": { - "@docusaurus/mdx-loader": "3.9.2", - "@docusaurus/module-type-aliases": "3.9.2", - "@docusaurus/utils": "3.9.2", - "@docusaurus/utils-common": "3.9.2", - "@types/history": "^4.7.11", - "@types/react": "*", - "@types/react-router-config": "*", - "clsx": "^2.0.0", - "parse-numeric-range": "^1.3.0", - "prism-react-renderer": "^2.3.0", - "tslib": "^2.6.0", - "utility-types": "^3.10.0" - }, - "engines": { - "node": ">=20.0" - }, - "peerDependencies": { - "@docusaurus/plugin-content-docs": "*", - "react": "^18.0.0 || ^19.0.0", - "react-dom": "^18.0.0 || ^19.0.0" - } - }, - "node_modules/@docusaurus/theme-search-algolia": { - "version": "3.9.2", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-search-algolia/-/theme-search-algolia-3.9.2.tgz", - "integrity": "sha512-GBDSFNwjnh5/LdkxCKQHkgO2pIMX1447BxYUBG2wBiajS21uj64a+gH/qlbQjDLxmGrbrllBrtJkUHxIsiwRnw==", - "license": "MIT", - "dependencies": { - "@docsearch/react": "^3.9.0 || ^4.1.0", - "@docusaurus/core": "3.9.2", - "@docusaurus/logger": "3.9.2", - "@docusaurus/plugin-content-docs": "3.9.2", - "@docusaurus/theme-common": "3.9.2", - "@docusaurus/theme-translations": "3.9.2", - "@docusaurus/utils": "3.9.2", - "@docusaurus/utils-validation": "3.9.2", - "algoliasearch": "^5.37.0", - "algoliasearch-helper": "^3.26.0", - "clsx": "^2.0.0", - "eta": "^2.2.0", - "fs-extra": "^11.1.1", - "lodash": "^4.17.21", - "tslib": "^2.6.0", - "utility-types": "^3.10.0" - }, - "engines": { - "node": ">=20.0" - }, - "peerDependencies": { - "react": "^18.0.0 || ^19.0.0", - "react-dom": "^18.0.0 || ^19.0.0" - } - }, - "node_modules/@docusaurus/theme-translations": { - "version": "3.9.2", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-translations/-/theme-translations-3.9.2.tgz", - "integrity": "sha512-vIryvpP18ON9T9rjgMRFLr2xJVDpw1rtagEGf8Ccce4CkTrvM/fRB8N2nyWYOW5u3DdjkwKw5fBa+3tbn9P4PA==", - "license": "MIT", - "dependencies": { - "fs-extra": "^11.1.1", - "tslib": "^2.6.0" - }, - "engines": { - "node": ">=20.0" - } - }, - "node_modules/@docusaurus/tsconfig": { - "version": "3.9.2", - "resolved": "https://registry.npmjs.org/@docusaurus/tsconfig/-/tsconfig-3.9.2.tgz", - "integrity": "sha512-j6/Fp4Rlpxsc632cnRnl5HpOWeb6ZKssDj6/XzzAzVGXXfm9Eptx3rxCC+fDzySn9fHTS+CWJjPineCR1bB5WQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@docusaurus/types": { - "version": "3.9.2", - "resolved": "https://registry.npmjs.org/@docusaurus/types/-/types-3.9.2.tgz", - "integrity": "sha512-Ux1JUNswg+EfUEmajJjyhIohKceitY/yzjRUpu04WXgvVz+fbhVC0p+R0JhvEu4ytw8zIAys2hrdpQPBHRIa8Q==", - "license": "MIT", - "dependencies": { - "@mdx-js/mdx": "^3.0.0", - "@types/history": "^4.7.11", - "@types/mdast": "^4.0.2", - "@types/react": "*", - "commander": "^5.1.0", - "joi": "^17.9.2", - "react-helmet-async": "npm:@slorber/react-helmet-async@1.3.0", - "utility-types": "^3.10.0", - "webpack": "^5.95.0", - "webpack-merge": "^5.9.0" - }, - "peerDependencies": { - "react": "^18.0.0 || ^19.0.0", - "react-dom": "^18.0.0 || ^19.0.0" - } - }, - "node_modules/@docusaurus/types/node_modules/webpack-merge": { - "version": "5.10.0", - "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.10.0.tgz", - "integrity": "sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA==", - "license": "MIT", - "dependencies": { - "clone-deep": "^4.0.1", - "flat": "^5.0.2", - "wildcard": "^2.0.0" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/@docusaurus/utils": { - "version": "3.9.2", - "resolved": "https://registry.npmjs.org/@docusaurus/utils/-/utils-3.9.2.tgz", - "integrity": "sha512-lBSBiRruFurFKXr5Hbsl2thmGweAPmddhF3jb99U4EMDA5L+e5Y1rAkOS07Nvrup7HUMBDrCV45meaxZnt28nQ==", - "license": "MIT", - "dependencies": { - "@docusaurus/logger": "3.9.2", - "@docusaurus/types": "3.9.2", - "@docusaurus/utils-common": "3.9.2", - "escape-string-regexp": "^4.0.0", - "execa": "5.1.1", - "file-loader": "^6.2.0", - "fs-extra": "^11.1.1", - "github-slugger": "^1.5.0", - "globby": "^11.1.0", - "gray-matter": "^4.0.3", - "jiti": "^1.20.0", - "js-yaml": "^4.1.0", - "lodash": "^4.17.21", - "micromatch": "^4.0.5", - "p-queue": "^6.6.2", - "prompts": "^2.4.2", - "resolve-pathname": "^3.0.0", - "tslib": "^2.6.0", - "url-loader": "^4.1.1", - "utility-types": "^3.10.0", - "webpack": "^5.88.1" - }, - "engines": { - "node": ">=20.0" - } - }, - "node_modules/@docusaurus/utils-common": { - "version": "3.9.2", - "resolved": "https://registry.npmjs.org/@docusaurus/utils-common/-/utils-common-3.9.2.tgz", - "integrity": "sha512-I53UC1QctruA6SWLvbjbhCpAw7+X7PePoe5pYcwTOEXD/PxeP8LnECAhTHHwWCblyUX5bMi4QLRkxvyZ+IT8Aw==", - "license": "MIT", - "dependencies": { - "@docusaurus/types": "3.9.2", - "tslib": "^2.6.0" - }, - "engines": { - "node": ">=20.0" - } - }, - "node_modules/@docusaurus/utils-validation": { - "version": "3.9.2", - "resolved": "https://registry.npmjs.org/@docusaurus/utils-validation/-/utils-validation-3.9.2.tgz", - "integrity": "sha512-l7yk3X5VnNmATbwijJkexdhulNsQaNDwoagiwujXoxFbWLcxHQqNQ+c/IAlzrfMMOfa/8xSBZ7KEKDesE/2J7A==", - "license": "MIT", - "dependencies": { - "@docusaurus/logger": "3.9.2", - "@docusaurus/utils": "3.9.2", - "@docusaurus/utils-common": "3.9.2", - "fs-extra": "^11.2.0", - "joi": "^17.9.2", - "js-yaml": "^4.1.0", - "lodash": "^4.17.21", - "tslib": "^2.6.0" - }, - "engines": { - "node": ">=20.0" - } - }, - "node_modules/@hapi/hoek": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", - "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==", - "license": "BSD-3-Clause" - }, - "node_modules/@hapi/topo": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz", - "integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==", - "license": "BSD-3-Clause", - "dependencies": { - "@hapi/hoek": "^9.0.0" - } - }, - "node_modules/@jest/schemas": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", - "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", - "license": "MIT", - "dependencies": { - "@sinclair/typebox": "^0.27.8" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/types": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", - "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", - "license": "MIT", - "dependencies": { - "@jest/schemas": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.13", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", - "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "node_modules/@jridgewell/remapping": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", - "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", - "license": "MIT", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/source-map": { - "version": "0.3.11", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz", - "integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==", - "license": "MIT", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", - "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.31", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", - "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@jsonjoy.com/base64": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jsonjoy.com/base64/-/base64-1.1.2.tgz", - "integrity": "sha512-q6XAnWQDIMA3+FTiOYajoYqySkO+JSat0ytXGSuRdq9uXE7o92gzuQwQM14xaCRlBLGq3v5miDGC4vkVTn54xA==", - "license": "Apache-2.0", - "engines": { - "node": ">=10.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/streamich" - }, - "peerDependencies": { - "tslib": "2" - } - }, - "node_modules/@jsonjoy.com/buffers": { - "version": "17.67.0", - "resolved": "https://registry.npmjs.org/@jsonjoy.com/buffers/-/buffers-17.67.0.tgz", - "integrity": "sha512-tfExRpYxBvi32vPs9ZHaTjSP4fHAfzSmcahOfNxtvGHcyJel+aibkPlGeBB+7AoC6hL7lXIE++8okecBxx7lcw==", - "license": "Apache-2.0", - "engines": { - "node": ">=10.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/streamich" - }, - "peerDependencies": { - "tslib": "2" - } - }, - "node_modules/@jsonjoy.com/codegen": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@jsonjoy.com/codegen/-/codegen-1.0.0.tgz", - "integrity": "sha512-E8Oy+08cmCf0EK/NMxpaJZmOxPqM+6iSe2S4nlSBrPZOORoDJILxtbSUEDKQyTamm/BVAhIGllOBNU79/dwf0g==", - "license": "Apache-2.0", - "engines": { - "node": ">=10.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/streamich" - }, - "peerDependencies": { - "tslib": "2" - } - }, - "node_modules/@jsonjoy.com/fs-core": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-core/-/fs-core-4.57.1.tgz", - "integrity": "sha512-YrEi/ZPmgc+GfdO0esBF04qv8boK9Dg9WpRQw/+vM8Qt3nnVIJWIa8HwZ/LXVZ0DB11XUROM8El/7yYTJX+WtA==", - "license": "Apache-2.0", - "dependencies": { - "@jsonjoy.com/fs-node-builtins": "4.57.1", - "@jsonjoy.com/fs-node-utils": "4.57.1", - "thingies": "^2.5.0" - }, - "engines": { - "node": ">=10.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/streamich" - }, - "peerDependencies": { - "tslib": "2" - } - }, - "node_modules/@jsonjoy.com/fs-fsa": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-fsa/-/fs-fsa-4.57.1.tgz", - "integrity": "sha512-ooEPvSW/HQDivPDPZMibHGKZf/QS4WRir1czGZmXmp3MsQqLECZEpN0JobrD8iV9BzsuwdIv+PxtWX9WpPLsIA==", - "license": "Apache-2.0", - "dependencies": { - "@jsonjoy.com/fs-core": "4.57.1", - "@jsonjoy.com/fs-node-builtins": "4.57.1", - "@jsonjoy.com/fs-node-utils": "4.57.1", - "thingies": "^2.5.0" - }, - "engines": { - "node": ">=10.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/streamich" - }, - "peerDependencies": { - "tslib": "2" - } - }, - "node_modules/@jsonjoy.com/fs-node": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-node/-/fs-node-4.57.1.tgz", - "integrity": "sha512-3YaKhP8gXEKN+2O49GLNfNb5l2gbnCFHyAaybbA2JkkbQP3dpdef7WcUaHAulg/c5Dg4VncHsA3NWAUSZMR5KQ==", - "license": "Apache-2.0", - "dependencies": { - "@jsonjoy.com/fs-core": "4.57.1", - "@jsonjoy.com/fs-node-builtins": "4.57.1", - "@jsonjoy.com/fs-node-utils": "4.57.1", - "@jsonjoy.com/fs-print": "4.57.1", - "@jsonjoy.com/fs-snapshot": "4.57.1", - "glob-to-regex.js": "^1.0.0", - "thingies": "^2.5.0" - }, - "engines": { - "node": ">=10.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/streamich" - }, - "peerDependencies": { - "tslib": "2" - } - }, - "node_modules/@jsonjoy.com/fs-node-builtins": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-node-builtins/-/fs-node-builtins-4.57.1.tgz", - "integrity": "sha512-XHkFKQ5GSH3uxm8c3ZYXVrexGdscpWKIcMWKFQpMpMJc8gA3AwOMBJXJlgpdJqmrhPyQXxaY9nbkNeYpacC0Og==", - "license": "Apache-2.0", - "engines": { - "node": ">=10.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/streamich" - }, - "peerDependencies": { - "tslib": "2" - } - }, - "node_modules/@jsonjoy.com/fs-node-to-fsa": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-node-to-fsa/-/fs-node-to-fsa-4.57.1.tgz", - "integrity": "sha512-pqGHyWWzNck4jRfaGV39hkqpY5QjRUQ/nRbNT7FYbBa0xf4bDG+TE1Gt2KWZrSkrkZZDE3qZUjYMbjwSliX6pg==", - "license": "Apache-2.0", - "dependencies": { - "@jsonjoy.com/fs-fsa": "4.57.1", - "@jsonjoy.com/fs-node-builtins": "4.57.1", - "@jsonjoy.com/fs-node-utils": "4.57.1" - }, - "engines": { - "node": ">=10.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/streamich" - }, - "peerDependencies": { - "tslib": "2" - } - }, - "node_modules/@jsonjoy.com/fs-node-utils": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-node-utils/-/fs-node-utils-4.57.1.tgz", - "integrity": "sha512-vp+7ZzIB8v43G+GLXTS4oDUSQmhAsRz532QmmWBbdYA20s465JvwhkSFvX9cVTqRRAQg+vZ7zWDaIEh0lFe2gw==", - "license": "Apache-2.0", - "dependencies": { - "@jsonjoy.com/fs-node-builtins": "4.57.1" - }, - "engines": { - "node": ">=10.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/streamich" - }, - "peerDependencies": { - "tslib": "2" - } - }, - "node_modules/@jsonjoy.com/fs-print": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-print/-/fs-print-4.57.1.tgz", - "integrity": "sha512-Ynct7ZJmfk6qoXDOKfpovNA36ITUx8rChLmRQtW08J73VOiuNsU8PB6d/Xs7fxJC2ohWR3a5AqyjmLojfrw5yw==", - "license": "Apache-2.0", - "dependencies": { - "@jsonjoy.com/fs-node-utils": "4.57.1", - "tree-dump": "^1.1.0" - }, - "engines": { - "node": ">=10.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/streamich" - }, - "peerDependencies": { - "tslib": "2" - } - }, - "node_modules/@jsonjoy.com/fs-snapshot": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-snapshot/-/fs-snapshot-4.57.1.tgz", - "integrity": "sha512-/oG8xBNFMbDXTq9J7vepSA1kerS5vpgd3p5QZSPd+nX59uwodGJftI51gDYyHRpP57P3WCQf7LHtBYPqwUg2Bg==", - "license": "Apache-2.0", - "dependencies": { - "@jsonjoy.com/buffers": "^17.65.0", - "@jsonjoy.com/fs-node-utils": "4.57.1", - "@jsonjoy.com/json-pack": "^17.65.0", - "@jsonjoy.com/util": "^17.65.0" - }, - "engines": { - "node": ">=10.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/streamich" - }, - "peerDependencies": { - "tslib": "2" - } - }, - "node_modules/@jsonjoy.com/fs-snapshot/node_modules/@jsonjoy.com/base64": { - "version": "17.67.0", - "resolved": "https://registry.npmjs.org/@jsonjoy.com/base64/-/base64-17.67.0.tgz", - "integrity": "sha512-5SEsJGsm15aP8TQGkDfJvz9axgPwAEm98S5DxOuYe8e1EbfajcDmgeXXzccEjh+mLnjqEKrkBdjHWS5vFNwDdw==", - "license": "Apache-2.0", - "engines": { - "node": ">=10.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/streamich" - }, - "peerDependencies": { - "tslib": "2" - } - }, - "node_modules/@jsonjoy.com/fs-snapshot/node_modules/@jsonjoy.com/codegen": { - "version": "17.67.0", - "resolved": "https://registry.npmjs.org/@jsonjoy.com/codegen/-/codegen-17.67.0.tgz", - "integrity": "sha512-idnkUplROpdBOV0HMcwhsCUS5TRUi9poagdGs70A6S4ux9+/aPuKbh8+UYRTLYQHtXvAdNfQWXDqZEx5k4Dj2Q==", - "license": "Apache-2.0", - "engines": { - "node": ">=10.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/streamich" - }, - "peerDependencies": { - "tslib": "2" - } - }, - "node_modules/@jsonjoy.com/fs-snapshot/node_modules/@jsonjoy.com/json-pack": { - "version": "17.67.0", - "resolved": "https://registry.npmjs.org/@jsonjoy.com/json-pack/-/json-pack-17.67.0.tgz", - "integrity": "sha512-t0ejURcGaZsn1ClbJ/3kFqSOjlryd92eQY465IYrezsXmPcfHPE/av4twRSxf6WE+TkZgLY+71vCZbiIiFKA/w==", - "license": "Apache-2.0", - "dependencies": { - "@jsonjoy.com/base64": "17.67.0", - "@jsonjoy.com/buffers": "17.67.0", - "@jsonjoy.com/codegen": "17.67.0", - "@jsonjoy.com/json-pointer": "17.67.0", - "@jsonjoy.com/util": "17.67.0", - "hyperdyperid": "^1.2.0", - "thingies": "^2.5.0", - "tree-dump": "^1.1.0" - }, - "engines": { - "node": ">=10.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/streamich" - }, - "peerDependencies": { - "tslib": "2" - } - }, - "node_modules/@jsonjoy.com/fs-snapshot/node_modules/@jsonjoy.com/json-pointer": { - "version": "17.67.0", - "resolved": "https://registry.npmjs.org/@jsonjoy.com/json-pointer/-/json-pointer-17.67.0.tgz", - "integrity": "sha512-+iqOFInH+QZGmSuaybBUNdh7yvNrXvqR+h3wjXm0N/3JK1EyyFAeGJvqnmQL61d1ARLlk/wJdFKSL+LHJ1eaUA==", - "license": "Apache-2.0", - "dependencies": { - "@jsonjoy.com/util": "17.67.0" - }, - "engines": { - "node": ">=10.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/streamich" - }, - "peerDependencies": { - "tslib": "2" - } - }, - "node_modules/@jsonjoy.com/fs-snapshot/node_modules/@jsonjoy.com/util": { - "version": "17.67.0", - "resolved": "https://registry.npmjs.org/@jsonjoy.com/util/-/util-17.67.0.tgz", - "integrity": "sha512-6+8xBaz1rLSohlGh68D1pdw3AwDi9xydm8QNlAFkvnavCJYSze+pxoW2VKP8p308jtlMRLs5NTHfPlZLd4w7ew==", - "license": "Apache-2.0", - "dependencies": { - "@jsonjoy.com/buffers": "17.67.0", - "@jsonjoy.com/codegen": "17.67.0" - }, - "engines": { - "node": ">=10.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/streamich" - }, - "peerDependencies": { - "tslib": "2" - } - }, - "node_modules/@jsonjoy.com/json-pack": { - "version": "1.21.0", - "resolved": "https://registry.npmjs.org/@jsonjoy.com/json-pack/-/json-pack-1.21.0.tgz", - "integrity": "sha512-+AKG+R2cfZMShzrF2uQw34v3zbeDYUqnQ+jg7ORic3BGtfw9p/+N6RJbq/kkV8JmYZaINknaEQ2m0/f693ZPpg==", - "license": "Apache-2.0", - "dependencies": { - "@jsonjoy.com/base64": "^1.1.2", - "@jsonjoy.com/buffers": "^1.2.0", - "@jsonjoy.com/codegen": "^1.0.0", - "@jsonjoy.com/json-pointer": "^1.0.2", - "@jsonjoy.com/util": "^1.9.0", - "hyperdyperid": "^1.2.0", - "thingies": "^2.5.0", - "tree-dump": "^1.1.0" - }, - "engines": { - "node": ">=10.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/streamich" - }, - "peerDependencies": { - "tslib": "2" - } - }, - "node_modules/@jsonjoy.com/json-pack/node_modules/@jsonjoy.com/buffers": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@jsonjoy.com/buffers/-/buffers-1.2.1.tgz", - "integrity": "sha512-12cdlDwX4RUM3QxmUbVJWqZ/mrK6dFQH4Zxq6+r1YXKXYBNgZXndx2qbCJwh3+WWkCSn67IjnlG3XYTvmvYtgA==", - "license": "Apache-2.0", - "engines": { - "node": ">=10.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/streamich" - }, - "peerDependencies": { - "tslib": "2" - } - }, - "node_modules/@jsonjoy.com/json-pointer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@jsonjoy.com/json-pointer/-/json-pointer-1.0.2.tgz", - "integrity": "sha512-Fsn6wM2zlDzY1U+v4Nc8bo3bVqgfNTGcn6dMgs6FjrEnt4ZCe60o6ByKRjOGlI2gow0aE/Q41QOigdTqkyK5fg==", - "license": "Apache-2.0", - "dependencies": { - "@jsonjoy.com/codegen": "^1.0.0", - "@jsonjoy.com/util": "^1.9.0" - }, - "engines": { - "node": ">=10.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/streamich" - }, - "peerDependencies": { - "tslib": "2" - } - }, - "node_modules/@jsonjoy.com/util": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@jsonjoy.com/util/-/util-1.9.0.tgz", - "integrity": "sha512-pLuQo+VPRnN8hfPqUTLTHk126wuYdXVxE6aDmjSeV4NCAgyxWbiOIeNJVtID3h1Vzpoi9m4jXezf73I6LgabgQ==", - "license": "Apache-2.0", - "dependencies": { - "@jsonjoy.com/buffers": "^1.0.0", - "@jsonjoy.com/codegen": "^1.0.0" - }, - "engines": { - "node": ">=10.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/streamich" - }, - "peerDependencies": { - "tslib": "2" - } - }, - "node_modules/@jsonjoy.com/util/node_modules/@jsonjoy.com/buffers": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@jsonjoy.com/buffers/-/buffers-1.2.1.tgz", - "integrity": "sha512-12cdlDwX4RUM3QxmUbVJWqZ/mrK6dFQH4Zxq6+r1YXKXYBNgZXndx2qbCJwh3+WWkCSn67IjnlG3XYTvmvYtgA==", - "license": "Apache-2.0", - "engines": { - "node": ">=10.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/streamich" - }, - "peerDependencies": { - "tslib": "2" - } - }, - "node_modules/@leichtgewicht/ip-codec": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz", - "integrity": "sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==", - "license": "MIT" - }, - "node_modules/@mdx-js/mdx": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@mdx-js/mdx/-/mdx-3.1.1.tgz", - "integrity": "sha512-f6ZO2ifpwAQIpzGWaBQT2TXxPv6z3RBzQKpVftEWN78Vl/YweF1uwussDx8ECAXVtr3Rs89fKyG9YlzUs9DyGQ==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "@types/estree-jsx": "^1.0.0", - "@types/hast": "^3.0.0", - "@types/mdx": "^2.0.0", - "acorn": "^8.0.0", - "collapse-white-space": "^2.0.0", - "devlop": "^1.0.0", - "estree-util-is-identifier-name": "^3.0.0", - "estree-util-scope": "^1.0.0", - "estree-walker": "^3.0.0", - "hast-util-to-jsx-runtime": "^2.0.0", - "markdown-extensions": "^2.0.0", - "recma-build-jsx": "^1.0.0", - "recma-jsx": "^1.0.0", - "recma-stringify": "^1.0.0", - "rehype-recma": "^1.0.0", - "remark-mdx": "^3.0.0", - "remark-parse": "^11.0.0", - "remark-rehype": "^11.0.0", - "source-map": "^0.7.0", - "unified": "^11.0.0", - "unist-util-position-from-estree": "^2.0.0", - "unist-util-stringify-position": "^4.0.0", - "unist-util-visit": "^5.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/@mdx-js/react": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@mdx-js/react/-/react-3.1.1.tgz", - "integrity": "sha512-f++rKLQgUVYDAtECQ6fn/is15GkEH9+nZPM3MS0RcxVqoTfawHvDlSCH7JbMhAM6uJ32v3eXLvLmLvjGu7PTQw==", - "license": "MIT", - "dependencies": { - "@types/mdx": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - }, - "peerDependencies": { - "@types/react": ">=16", - "react": ">=16" - } - }, - "node_modules/@noble/hashes": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", - "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==", - "license": "MIT", - "engines": { - "node": ">= 16" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "license": "MIT", - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@peculiar/asn1-cms": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/@peculiar/asn1-cms/-/asn1-cms-2.6.1.tgz", - "integrity": "sha512-vdG4fBF6Lkirkcl53q6eOdn3XYKt+kJTG59edgRZORlg/3atWWEReRCx5rYE1ZzTTX6vLK5zDMjHh7vbrcXGtw==", - "license": "MIT", - "dependencies": { - "@peculiar/asn1-schema": "^2.6.0", - "@peculiar/asn1-x509": "^2.6.1", - "@peculiar/asn1-x509-attr": "^2.6.1", - "asn1js": "^3.0.6", - "tslib": "^2.8.1" - } - }, - "node_modules/@peculiar/asn1-csr": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/@peculiar/asn1-csr/-/asn1-csr-2.6.1.tgz", - "integrity": "sha512-WRWnKfIocHyzFYQTka8O/tXCiBquAPSrRjXbOkHbO4qdmS6loffCEGs+rby6WxxGdJCuunnhS2duHURhjyio6w==", - "license": "MIT", - "dependencies": { - "@peculiar/asn1-schema": "^2.6.0", - "@peculiar/asn1-x509": "^2.6.1", - "asn1js": "^3.0.6", - "tslib": "^2.8.1" - } - }, - "node_modules/@peculiar/asn1-ecc": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/@peculiar/asn1-ecc/-/asn1-ecc-2.6.1.tgz", - "integrity": "sha512-+Vqw8WFxrtDIN5ehUdvlN2m73exS2JVG0UAyfVB31gIfor3zWEAQPD+K9ydCxaj3MLen9k0JhKpu9LqviuCE1g==", - "license": "MIT", - "dependencies": { - "@peculiar/asn1-schema": "^2.6.0", - "@peculiar/asn1-x509": "^2.6.1", - "asn1js": "^3.0.6", - "tslib": "^2.8.1" - } - }, - "node_modules/@peculiar/asn1-pfx": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/@peculiar/asn1-pfx/-/asn1-pfx-2.6.1.tgz", - "integrity": "sha512-nB5jVQy3MAAWvq0KY0R2JUZG8bO/bTLpnwyOzXyEh/e54ynGTatAR+csOnXkkVD9AFZ2uL8Z7EV918+qB1qDvw==", - "license": "MIT", - "dependencies": { - "@peculiar/asn1-cms": "^2.6.1", - "@peculiar/asn1-pkcs8": "^2.6.1", - "@peculiar/asn1-rsa": "^2.6.1", - "@peculiar/asn1-schema": "^2.6.0", - "asn1js": "^3.0.6", - "tslib": "^2.8.1" - } - }, - "node_modules/@peculiar/asn1-pkcs8": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/@peculiar/asn1-pkcs8/-/asn1-pkcs8-2.6.1.tgz", - "integrity": "sha512-JB5iQ9Izn5yGMw3ZG4Nw3Xn/hb/G38GYF3lf7WmJb8JZUydhVGEjK/ZlFSWhnlB7K/4oqEs8HnfFIKklhR58Tw==", - "license": "MIT", - "dependencies": { - "@peculiar/asn1-schema": "^2.6.0", - "@peculiar/asn1-x509": "^2.6.1", - "asn1js": "^3.0.6", - "tslib": "^2.8.1" - } - }, - "node_modules/@peculiar/asn1-pkcs9": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/@peculiar/asn1-pkcs9/-/asn1-pkcs9-2.6.1.tgz", - "integrity": "sha512-5EV8nZoMSxeWmcxWmmcolg22ojZRgJg+Y9MX2fnE2bGRo5KQLqV5IL9kdSQDZxlHz95tHvIq9F//bvL1OeNILw==", - "license": "MIT", - "dependencies": { - "@peculiar/asn1-cms": "^2.6.1", - "@peculiar/asn1-pfx": "^2.6.1", - "@peculiar/asn1-pkcs8": "^2.6.1", - "@peculiar/asn1-schema": "^2.6.0", - "@peculiar/asn1-x509": "^2.6.1", - "@peculiar/asn1-x509-attr": "^2.6.1", - "asn1js": "^3.0.6", - "tslib": "^2.8.1" - } - }, - "node_modules/@peculiar/asn1-rsa": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/@peculiar/asn1-rsa/-/asn1-rsa-2.6.1.tgz", - "integrity": "sha512-1nVMEh46SElUt5CB3RUTV4EG/z7iYc7EoaDY5ECwganibQPkZ/Y2eMsTKB/LeyrUJ+W/tKoD9WUqIy8vB+CEdA==", - "license": "MIT", - "dependencies": { - "@peculiar/asn1-schema": "^2.6.0", - "@peculiar/asn1-x509": "^2.6.1", - "asn1js": "^3.0.6", - "tslib": "^2.8.1" - } - }, - "node_modules/@peculiar/asn1-schema": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@peculiar/asn1-schema/-/asn1-schema-2.6.0.tgz", - "integrity": "sha512-xNLYLBFTBKkCzEZIw842BxytQQATQv+lDTCEMZ8C196iJcJJMBUZxrhSTxLaohMyKK8QlzRNTRkUmanucnDSqg==", - "license": "MIT", - "dependencies": { - "asn1js": "^3.0.6", - "pvtsutils": "^1.3.6", - "tslib": "^2.8.1" - } - }, - "node_modules/@peculiar/asn1-x509": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/@peculiar/asn1-x509/-/asn1-x509-2.6.1.tgz", - "integrity": "sha512-O9jT5F1A2+t3r7C4VT7LYGXqkGLK7Kj1xFpz7U0isPrubwU5PbDoyYtx6MiGst29yq7pXN5vZbQFKRCP+lLZlA==", - "license": "MIT", - "dependencies": { - "@peculiar/asn1-schema": "^2.6.0", - "asn1js": "^3.0.6", - "pvtsutils": "^1.3.6", - "tslib": "^2.8.1" - } - }, - "node_modules/@peculiar/asn1-x509-attr": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/@peculiar/asn1-x509-attr/-/asn1-x509-attr-2.6.1.tgz", - "integrity": "sha512-tlW6cxoHwgcQghnJwv3YS+9OO1737zgPogZ+CgWRUK4roEwIPzRH4JEiG770xe5HX2ATfCpmX60gurfWIF9dcQ==", - "license": "MIT", - "dependencies": { - "@peculiar/asn1-schema": "^2.6.0", - "@peculiar/asn1-x509": "^2.6.1", - "asn1js": "^3.0.6", - "tslib": "^2.8.1" - } - }, - "node_modules/@peculiar/x509": { - "version": "1.14.3", - "resolved": "https://registry.npmjs.org/@peculiar/x509/-/x509-1.14.3.tgz", - "integrity": "sha512-C2Xj8FZ0uHWeCXXqX5B4/gVFQmtSkiuOolzAgutjTfseNOHT3pUjljDZsTSxXFGgio54bCzVFqmEOUrIVk8RDA==", - "license": "MIT", - "dependencies": { - "@peculiar/asn1-cms": "^2.6.0", - "@peculiar/asn1-csr": "^2.6.0", - "@peculiar/asn1-ecc": "^2.6.0", - "@peculiar/asn1-pkcs9": "^2.6.0", - "@peculiar/asn1-rsa": "^2.6.0", - "@peculiar/asn1-schema": "^2.6.0", - "@peculiar/asn1-x509": "^2.6.0", - "pvtsutils": "^1.3.6", - "reflect-metadata": "^0.2.2", - "tslib": "^2.8.1", - "tsyringe": "^4.10.0" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@pnpm/config.env-replace": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@pnpm/config.env-replace/-/config.env-replace-1.1.0.tgz", - "integrity": "sha512-htyl8TWnKL7K/ESFa1oW2UB5lVDxuF5DpM7tBi6Hu2LNL3mWkIzNLG6N4zoCUP1lCKNxWy/3iu8mS8MvToGd6w==", - "license": "MIT", - "engines": { - "node": ">=12.22.0" - } - }, - "node_modules/@pnpm/network.ca-file": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@pnpm/network.ca-file/-/network.ca-file-1.0.2.tgz", - "integrity": "sha512-YcPQ8a0jwYU9bTdJDpXjMi7Brhkr1mXsXrUJvjqM2mQDgkRiz8jFaQGOdaLxgjtUfQgZhKy/O3cG/YwmgKaxLA==", - "license": "MIT", - "dependencies": { - "graceful-fs": "4.2.10" - }, - "engines": { - "node": ">=12.22.0" - } - }, - "node_modules/@pnpm/network.ca-file/node_modules/graceful-fs": { - "version": "4.2.10", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", - "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", - "license": "ISC" - }, - "node_modules/@pnpm/npm-conf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@pnpm/npm-conf/-/npm-conf-3.0.2.tgz", - "integrity": "sha512-h104Kh26rR8tm+a3Qkc5S4VLYint3FE48as7+/5oCEcKR2idC/pF1G6AhIXKI+eHPJa/3J9i5z0Al47IeGHPkA==", - "license": "MIT", - "dependencies": { - "@pnpm/config.env-replace": "^1.1.0", - "@pnpm/network.ca-file": "^1.0.1", - "config-chain": "^1.1.11" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@polka/url": { - "version": "1.0.0-next.29", - "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.29.tgz", - "integrity": "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==", - "license": "MIT" - }, - "node_modules/@sideway/address": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.5.tgz", - "integrity": "sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==", - "license": "BSD-3-Clause", - "dependencies": { - "@hapi/hoek": "^9.0.0" - } - }, - "node_modules/@sideway/formula": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.1.tgz", - "integrity": "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==", - "license": "BSD-3-Clause" - }, - "node_modules/@sideway/pinpoint": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz", - "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==", - "license": "BSD-3-Clause" - }, - "node_modules/@sinclair/typebox": { - "version": "0.27.10", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.10.tgz", - "integrity": "sha512-MTBk/3jGLNB2tVxv6uLlFh1iu64iYOQ2PbdOSK3NW8JZsmlaOh2q6sdtKowBhfw8QFLmYNzTW4/oK4uATIi6ZA==", - "license": "MIT" - }, - "node_modules/@sindresorhus/is": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", - "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/is?sponsor=1" - } - }, - "node_modules/@slorber/remark-comment": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@slorber/remark-comment/-/remark-comment-1.0.0.tgz", - "integrity": "sha512-RCE24n7jsOj1M0UPvIQCHTe7fI0sFL4S2nwKVWwHyVr/wI/H8GosgsJGyhnsZoGFnD/P2hLf1mSbrrgSLN93NA==", - "license": "MIT", - "dependencies": { - "micromark-factory-space": "^1.0.0", - "micromark-util-character": "^1.1.0", - "micromark-util-symbol": "^1.0.1" - } - }, - "node_modules/@svgr/babel-plugin-add-jsx-attribute": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-8.0.0.tgz", - "integrity": "sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g==", - "license": "MIT", - "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@svgr/babel-plugin-remove-jsx-attribute": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-8.0.0.tgz", - "integrity": "sha512-BcCkm/STipKvbCl6b7QFrMh/vx00vIP63k2eM66MfHJzPr6O2U0jYEViXkHJWqXqQYjdeA9cuCl5KWmlwjDvbA==", - "license": "MIT", - "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@svgr/babel-plugin-remove-jsx-empty-expression": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-8.0.0.tgz", - "integrity": "sha512-5BcGCBfBxB5+XSDSWnhTThfI9jcO5f0Ai2V24gZpG+wXF14BzwxxdDb4g6trdOux0rhibGs385BeFMSmxtS3uA==", - "license": "MIT", - "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@svgr/babel-plugin-replace-jsx-attribute-value": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-8.0.0.tgz", - "integrity": "sha512-KVQ+PtIjb1BuYT3ht8M5KbzWBhdAjjUPdlMtpuw/VjT8coTrItWX6Qafl9+ji831JaJcu6PJNKCV0bp01lBNzQ==", - "license": "MIT", - "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@svgr/babel-plugin-svg-dynamic-title": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-8.0.0.tgz", - "integrity": "sha512-omNiKqwjNmOQJ2v6ge4SErBbkooV2aAWwaPFs2vUY7p7GhVkzRkJ00kILXQvRhA6miHnNpXv7MRnnSjdRjK8og==", - "license": "MIT", - "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@svgr/babel-plugin-svg-em-dimensions": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-8.0.0.tgz", - "integrity": "sha512-mURHYnu6Iw3UBTbhGwE/vsngtCIbHE43xCRK7kCw4t01xyGqb2Pd+WXekRRoFOBIY29ZoOhUCTEweDMdrjfi9g==", - "license": "MIT", - "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@svgr/babel-plugin-transform-react-native-svg": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-8.1.0.tgz", - "integrity": "sha512-Tx8T58CHo+7nwJ+EhUwx3LfdNSG9R2OKfaIXXs5soiy5HtgoAEkDay9LIimLOcG8dJQH1wPZp/cnAv6S9CrR1Q==", - "license": "MIT", - "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@svgr/babel-plugin-transform-svg-component": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-8.0.0.tgz", - "integrity": "sha512-DFx8xa3cZXTdb/k3kfPeaixecQLgKh5NVBMwD0AQxOzcZawK4oo1Jh9LbrcACUivsCA7TLG8eeWgrDXjTMhRmw==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@svgr/babel-preset": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-8.1.0.tgz", - "integrity": "sha512-7EYDbHE7MxHpv4sxvnVPngw5fuR6pw79SkcrILHJ/iMpuKySNCl5W1qcwPEpU+LgyRXOaAFgH0KhwD18wwg6ug==", - "license": "MIT", - "dependencies": { - "@svgr/babel-plugin-add-jsx-attribute": "8.0.0", - "@svgr/babel-plugin-remove-jsx-attribute": "8.0.0", - "@svgr/babel-plugin-remove-jsx-empty-expression": "8.0.0", - "@svgr/babel-plugin-replace-jsx-attribute-value": "8.0.0", - "@svgr/babel-plugin-svg-dynamic-title": "8.0.0", - "@svgr/babel-plugin-svg-em-dimensions": "8.0.0", - "@svgr/babel-plugin-transform-react-native-svg": "8.1.0", - "@svgr/babel-plugin-transform-svg-component": "8.0.0" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@svgr/core": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@svgr/core/-/core-8.1.0.tgz", - "integrity": "sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==", - "license": "MIT", - "dependencies": { - "@babel/core": "^7.21.3", - "@svgr/babel-preset": "8.1.0", - "camelcase": "^6.2.0", - "cosmiconfig": "^8.1.3", - "snake-case": "^3.0.4" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - } - }, - "node_modules/@svgr/hast-util-to-babel-ast": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-8.0.0.tgz", - "integrity": "sha512-EbDKwO9GpfWP4jN9sGdYwPBU0kdomaPIL2Eu4YwmgP+sJeXT+L7bMwJUBnhzfH8Q2qMBqZ4fJwpCyYsAN3mt2Q==", - "license": "MIT", - "dependencies": { - "@babel/types": "^7.21.3", - "entities": "^4.4.0" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - } - }, - "node_modules/@svgr/plugin-jsx": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-8.1.0.tgz", - "integrity": "sha512-0xiIyBsLlr8quN+WyuxooNW9RJ0Dpr8uOnH/xrCVO8GLUcwHISwj1AG0k+LFzteTkAA0GbX0kj9q6Dk70PTiPA==", - "license": "MIT", - "dependencies": { - "@babel/core": "^7.21.3", - "@svgr/babel-preset": "8.1.0", - "@svgr/hast-util-to-babel-ast": "8.0.0", - "svg-parser": "^2.0.4" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - }, - "peerDependencies": { - "@svgr/core": "*" - } - }, - "node_modules/@svgr/plugin-svgo": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@svgr/plugin-svgo/-/plugin-svgo-8.1.0.tgz", - "integrity": "sha512-Ywtl837OGO9pTLIN/onoWLmDQ4zFUycI1g76vuKGEz6evR/ZTJlJuz3G/fIkb6OVBJ2g0o6CGJzaEjfmEo3AHA==", - "license": "MIT", - "dependencies": { - "cosmiconfig": "^8.1.3", - "deepmerge": "^4.3.1", - "svgo": "^3.0.2" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - }, - "peerDependencies": { - "@svgr/core": "*" - } - }, - "node_modules/@svgr/webpack": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@svgr/webpack/-/webpack-8.1.0.tgz", - "integrity": "sha512-LnhVjMWyMQV9ZmeEy26maJk+8HTIbd59cH4F2MJ439k9DqejRisfFNGAPvRYlKETuh9LrImlS8aKsBgKjMA8WA==", - "license": "MIT", - "dependencies": { - "@babel/core": "^7.21.3", - "@babel/plugin-transform-react-constant-elements": "^7.21.3", - "@babel/preset-env": "^7.20.2", - "@babel/preset-react": "^7.18.6", - "@babel/preset-typescript": "^7.21.0", - "@svgr/core": "8.1.0", - "@svgr/plugin-jsx": "8.1.0", - "@svgr/plugin-svgo": "8.1.0" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - } - }, - "node_modules/@szmarczak/http-timer": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-5.0.1.tgz", - "integrity": "sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==", - "license": "MIT", - "dependencies": { - "defer-to-connect": "^2.0.1" - }, - "engines": { - "node": ">=14.16" - } - }, - "node_modules/@types/body-parser": { - "version": "1.19.6", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz", - "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==", - "license": "MIT", - "dependencies": { - "@types/connect": "*", - "@types/node": "*" - } - }, - "node_modules/@types/bonjour": { - "version": "3.5.13", - "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.13.tgz", - "integrity": "sha512-z9fJ5Im06zvUL548KvYNecEVlA7cVDkGUi6kZusb04mpyEFKCIZJvloCcmpmLaIahDpOQGHaHmG6imtPMmPXGQ==", - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/connect": { - "version": "3.4.38", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", - "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/connect-history-api-fallback": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.4.tgz", - "integrity": "sha512-n6Cr2xS1h4uAulPRdlw6Jl6s1oG8KrVilPN2yUITEs+K48EzMJJ3W1xy8K5eWuFvjp3R74AOIGSmp2UfBJ8HFw==", - "license": "MIT", - "dependencies": { - "@types/express-serve-static-core": "*", - "@types/node": "*" - } - }, - "node_modules/@types/debug": { - "version": "4.1.13", - "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.13.tgz", - "integrity": "sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==", - "license": "MIT", - "dependencies": { - "@types/ms": "*" - } - }, - "node_modules/@types/eslint": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", - "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", - "license": "MIT", - "dependencies": { - "@types/estree": "*", - "@types/json-schema": "*" - } - }, - "node_modules/@types/eslint-scope": { - "version": "3.7.7", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", - "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", - "license": "MIT", - "dependencies": { - "@types/eslint": "*", - "@types/estree": "*" - } - }, - "node_modules/@types/estree": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", - "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", - "license": "MIT" - }, - "node_modules/@types/estree-jsx": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/estree-jsx/-/estree-jsx-1.0.5.tgz", - "integrity": "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==", - "license": "MIT", - "dependencies": { - "@types/estree": "*" - } - }, - "node_modules/@types/express": { - "version": "4.17.25", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.25.tgz", - "integrity": "sha512-dVd04UKsfpINUnK0yBoYHDF3xu7xVH4BuDotC/xGuycx4CgbP48X/KF/586bcObxT0HENHXEU8Nqtu6NR+eKhw==", - "license": "MIT", - "dependencies": { - "@types/body-parser": "*", - "@types/express-serve-static-core": "^4.17.33", - "@types/qs": "*", - "@types/serve-static": "^1" - } - }, - "node_modules/@types/express-serve-static-core": { - "version": "4.19.8", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.8.tgz", - "integrity": "sha512-02S5fmqeoKzVZCHPZid4b8JH2eM5HzQLZWN2FohQEy/0eXTq8VXZfSN6Pcr3F6N9R/vNrj7cpgbhjie6m/1tCA==", - "license": "MIT", - "dependencies": { - "@types/node": "*", - "@types/qs": "*", - "@types/range-parser": "*", - "@types/send": "*" - } - }, - "node_modules/@types/gtag.js": { - "version": "0.0.12", - "resolved": "https://registry.npmjs.org/@types/gtag.js/-/gtag.js-0.0.12.tgz", - "integrity": "sha512-YQV9bUsemkzG81Ea295/nF/5GijnD2Af7QhEofh7xu+kvCN6RdodgNwwGWXB5GMI3NoyvQo0odNctoH/qLMIpg==", - "license": "MIT" - }, - "node_modules/@types/hast": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", - "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", - "license": "MIT", - "dependencies": { - "@types/unist": "*" - } - }, - "node_modules/@types/history": { - "version": "4.7.11", - "resolved": "https://registry.npmjs.org/@types/history/-/history-4.7.11.tgz", - "integrity": "sha512-qjDJRrmvBMiTx+jyLxvLfJU7UznFuokDv4f3WRuriHKERccVpFU+8XMQUAbDzoiJCsmexxRExQeMwwCdamSKDA==", - "license": "MIT" - }, - "node_modules/@types/html-minifier-terser": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", - "integrity": "sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==", - "license": "MIT" - }, - "node_modules/@types/http-cache-semantics": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", - "integrity": "sha512-L3LgimLHXtGkWikKnsPg0/VFx9OGZaC+eN1u4r+OB1XRqH3meBIAVC2zr1WdMH+RHmnRkqliQAOHNJ/E0j/e0Q==", - "license": "MIT" - }, - "node_modules/@types/http-errors": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", - "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==", - "license": "MIT" - }, - "node_modules/@types/http-proxy": { - "version": "1.17.17", - "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.17.tgz", - "integrity": "sha512-ED6LB+Z1AVylNTu7hdzuBqOgMnvG/ld6wGCG8wFnAzKX5uyW2K3WD52v0gnLCTK/VLpXtKckgWuyScYK6cSPaw==", - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/istanbul-lib-coverage": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", - "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", - "license": "MIT" - }, - "node_modules/@types/istanbul-lib-report": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", - "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", - "license": "MIT", - "dependencies": { - "@types/istanbul-lib-coverage": "*" - } - }, - "node_modules/@types/istanbul-reports": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", - "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", - "license": "MIT", - "dependencies": { - "@types/istanbul-lib-report": "*" - } - }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "license": "MIT" - }, - "node_modules/@types/mdast": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", - "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", - "license": "MIT", - "dependencies": { - "@types/unist": "*" - } - }, - "node_modules/@types/mdx": { - "version": "2.0.13", - "resolved": "https://registry.npmjs.org/@types/mdx/-/mdx-2.0.13.tgz", - "integrity": "sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw==", - "license": "MIT" - }, - "node_modules/@types/mime": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", - "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", - "license": "MIT" - }, - "node_modules/@types/ms": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", - "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", - "license": "MIT" - }, - "node_modules/@types/node": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-25.5.0.tgz", - "integrity": "sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw==", - "license": "MIT", - "dependencies": { - "undici-types": "~7.18.0" - } - }, - "node_modules/@types/prismjs": { - "version": "1.26.6", - "resolved": "https://registry.npmjs.org/@types/prismjs/-/prismjs-1.26.6.tgz", - "integrity": "sha512-vqlvI7qlMvcCBbVe0AKAb4f97//Hy0EBTaiW8AalRnG/xAN5zOiWWyrNqNXeq8+KAuvRewjCVY1+IPxk4RdNYw==", - "license": "MIT" - }, - "node_modules/@types/qs": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.15.0.tgz", - "integrity": "sha512-JawvT8iBVWpzTrz3EGw9BTQFg3BQNmwERdKE22vlTxawwtbyUSlMppvZYKLZzB5zgACXdXxbD3m1bXaMqP/9ow==", - "license": "MIT" - }, - "node_modules/@types/range-parser": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", - "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", - "license": "MIT" - }, - "node_modules/@types/react": { - "version": "19.2.14", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz", - "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", - "license": "MIT", - "dependencies": { - "csstype": "^3.2.2" - } - }, - "node_modules/@types/react-router": { - "version": "5.1.20", - "resolved": "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.20.tgz", - "integrity": "sha512-jGjmu/ZqS7FjSH6owMcD5qpq19+1RS9DeVRqfl1FeBMxTDQAGwlMWOcs52NDoXaNKyG3d1cYQFMs9rCrb88o9Q==", - "license": "MIT", - "dependencies": { - "@types/history": "^4.7.11", - "@types/react": "*" - } - }, - "node_modules/@types/react-router-config": { - "version": "5.0.11", - "resolved": "https://registry.npmjs.org/@types/react-router-config/-/react-router-config-5.0.11.tgz", - "integrity": "sha512-WmSAg7WgqW7m4x8Mt4N6ZyKz0BubSj/2tVUMsAHp+Yd2AMwcSbeFq9WympT19p5heCFmF97R9eD5uUR/t4HEqw==", - "license": "MIT", - "dependencies": { - "@types/history": "^4.7.11", - "@types/react": "*", - "@types/react-router": "^5.1.0" - } - }, - "node_modules/@types/react-router-dom": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/@types/react-router-dom/-/react-router-dom-5.3.3.tgz", - "integrity": "sha512-kpqnYK4wcdm5UaWI3fLcELopqLrHgLqNsdpHauzlQktfkHL3npOSwtj1Uz9oKBAzs7lFtVkV8j83voAz2D8fhw==", - "license": "MIT", - "dependencies": { - "@types/history": "^4.7.11", - "@types/react": "*", - "@types/react-router": "*" - } - }, - "node_modules/@types/retry": { - "version": "0.12.2", - "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.2.tgz", - "integrity": "sha512-XISRgDJ2Tc5q4TRqvgJtzsRkFYNJzZrhTdtMoGVBttwzzQJkPnS3WWTFc7kuDRoPtPakl+T+OfdEUjYJj7Jbow==", - "license": "MIT" - }, - "node_modules/@types/sax": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@types/sax/-/sax-1.2.7.tgz", - "integrity": "sha512-rO73L89PJxeYM3s3pPPjiPgVVcymqU490g0YO5n5By0k2Erzj6tay/4lr1CHAAU4JyOWd1rpQ8bCf6cZfHU96A==", - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/send": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.1.tgz", - "integrity": "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==", - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/serve-index": { - "version": "1.9.4", - "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.4.tgz", - "integrity": "sha512-qLpGZ/c2fhSs5gnYsQxtDEq3Oy8SXPClIXkW5ghvAvsNuVSA8k+gCONcUCS/UjLEYvYps+e8uBtfgXgvhwfNug==", - "license": "MIT", - "dependencies": { - "@types/express": "*" - } - }, - "node_modules/@types/serve-static": { - "version": "1.15.10", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.10.tgz", - "integrity": "sha512-tRs1dB+g8Itk72rlSI2ZrW6vZg0YrLI81iQSTkMmOqnqCaNr/8Ek4VwWcN5vZgCYWbg/JJSGBlUaYGAOP73qBw==", - "license": "MIT", - "dependencies": { - "@types/http-errors": "*", - "@types/node": "*", - "@types/send": "<1" - } - }, - "node_modules/@types/serve-static/node_modules/@types/send": { - "version": "0.17.6", - "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.6.tgz", - "integrity": "sha512-Uqt8rPBE8SY0RK8JB1EzVOIZ32uqy8HwdxCnoCOsYrvnswqmFZ/k+9Ikidlk/ImhsdvBsloHbAlewb2IEBV/Og==", - "license": "MIT", - "dependencies": { - "@types/mime": "^1", - "@types/node": "*" - } - }, - "node_modules/@types/sockjs": { - "version": "0.3.36", - "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.36.tgz", - "integrity": "sha512-MK9V6NzAS1+Ud7JV9lJLFqW85VbC9dq3LmwZCuBe4wBDgKC0Kj/jd8Xl+nSviU+Qc3+m7umHHyHg//2KSa0a0Q==", - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/unist": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", - "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", - "license": "MIT" - }, - "node_modules/@types/ws": { - "version": "8.18.1", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", - "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/yargs": { - "version": "17.0.35", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.35.tgz", - "integrity": "sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==", - "license": "MIT", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@types/yargs-parser": { - "version": "21.0.3", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", - "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", - "license": "MIT" - }, - "node_modules/@ungap/structured-clone": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", - "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", - "license": "ISC" - }, - "node_modules/@webassemblyjs/ast": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", - "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==", - "license": "MIT", - "dependencies": { - "@webassemblyjs/helper-numbers": "1.13.2", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2" - } - }, - "node_modules/@webassemblyjs/floating-point-hex-parser": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz", - "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==", - "license": "MIT" - }, - "node_modules/@webassemblyjs/helper-api-error": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz", - "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==", - "license": "MIT" - }, - "node_modules/@webassemblyjs/helper-buffer": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz", - "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==", - "license": "MIT" - }, - "node_modules/@webassemblyjs/helper-numbers": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz", - "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==", - "license": "MIT", - "dependencies": { - "@webassemblyjs/floating-point-hex-parser": "1.13.2", - "@webassemblyjs/helper-api-error": "1.13.2", - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@webassemblyjs/helper-wasm-bytecode": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz", - "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==", - "license": "MIT" - }, - "node_modules/@webassemblyjs/helper-wasm-section": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz", - "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==", - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-buffer": "1.14.1", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2", - "@webassemblyjs/wasm-gen": "1.14.1" - } - }, - "node_modules/@webassemblyjs/ieee754": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz", - "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==", - "license": "MIT", - "dependencies": { - "@xtuc/ieee754": "^1.2.0" - } - }, - "node_modules/@webassemblyjs/leb128": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz", - "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==", - "license": "Apache-2.0", - "dependencies": { - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@webassemblyjs/utf8": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz", - "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==", - "license": "MIT" - }, - "node_modules/@webassemblyjs/wasm-edit": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz", - "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==", - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-buffer": "1.14.1", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2", - "@webassemblyjs/helper-wasm-section": "1.14.1", - "@webassemblyjs/wasm-gen": "1.14.1", - "@webassemblyjs/wasm-opt": "1.14.1", - "@webassemblyjs/wasm-parser": "1.14.1", - "@webassemblyjs/wast-printer": "1.14.1" - } - }, - "node_modules/@webassemblyjs/wasm-gen": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz", - "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==", - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2", - "@webassemblyjs/ieee754": "1.13.2", - "@webassemblyjs/leb128": "1.13.2", - "@webassemblyjs/utf8": "1.13.2" - } - }, - "node_modules/@webassemblyjs/wasm-opt": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz", - "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==", - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-buffer": "1.14.1", - "@webassemblyjs/wasm-gen": "1.14.1", - "@webassemblyjs/wasm-parser": "1.14.1" - } - }, - "node_modules/@webassemblyjs/wasm-parser": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz", - "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==", - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-api-error": "1.13.2", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2", - "@webassemblyjs/ieee754": "1.13.2", - "@webassemblyjs/leb128": "1.13.2", - "@webassemblyjs/utf8": "1.13.2" - } - }, - "node_modules/@webassemblyjs/wast-printer": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz", - "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==", - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@xtuc/ieee754": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", - "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", - "license": "BSD-3-Clause" - }, - "node_modules/@xtuc/long": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", - "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", - "license": "Apache-2.0" - }, - "node_modules/accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "license": "MIT", - "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/accepts/node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/accepts/node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/accepts/node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/acorn": { - "version": "8.16.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", - "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-import-phases": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/acorn-import-phases/-/acorn-import-phases-1.0.4.tgz", - "integrity": "sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==", - "license": "MIT", - "engines": { - "node": ">=10.13.0" - }, - "peerDependencies": { - "acorn": "^8.14.0" - } - }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "license": "MIT", - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/acorn-walk": { - "version": "8.3.5", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.5.tgz", - "integrity": "sha512-HEHNfbars9v4pgpW6SO1KSPkfoS0xVOM/9UzkJltjlsHZmJasxg8aXkuZa7SMf8vKGIBhpUsPluQSqhJFCqebw==", - "license": "MIT", - "dependencies": { - "acorn": "^8.11.0" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/address": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/address/-/address-1.2.2.tgz", - "integrity": "sha512-4B/qKCfeE/ODUaAUpSwfzazo5x29WD4r3vXiWsB7I2mSDAihwEqKO+g8GELZUQSSAo5e1XTYh3ZVfLyxBc12nA==", - "license": "MIT", - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/aggregate-error": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", - "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", - "license": "MIT", - "dependencies": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/ajv": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", - "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ajv-formats": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", - "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", - "license": "MIT", - "dependencies": { - "ajv": "^8.0.0" - }, - "peerDependencies": { - "ajv": "^8.0.0" - }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } - } - }, - "node_modules/ajv-keywords": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", - "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3" - }, - "peerDependencies": { - "ajv": "^8.8.2" - } - }, - "node_modules/algoliasearch": { - "version": "5.50.0", - "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-5.50.0.tgz", - "integrity": "sha512-yE5I83Q2s8euVou8Y3feXK08wyZInJWLYXgWO6Xti9jBUEZAGUahyeQ7wSZWkifLWVnQVKEz5RAmBlXG5nqxog==", - "license": "MIT", - "dependencies": { - "@algolia/abtesting": "1.16.0", - "@algolia/client-abtesting": "5.50.0", - "@algolia/client-analytics": "5.50.0", - "@algolia/client-common": "5.50.0", - "@algolia/client-insights": "5.50.0", - "@algolia/client-personalization": "5.50.0", - "@algolia/client-query-suggestions": "5.50.0", - "@algolia/client-search": "5.50.0", - "@algolia/ingestion": "1.50.0", - "@algolia/monitoring": "1.50.0", - "@algolia/recommend": "5.50.0", - "@algolia/requester-browser-xhr": "5.50.0", - "@algolia/requester-fetch": "5.50.0", - "@algolia/requester-node-http": "5.50.0" - }, - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/algoliasearch-helper": { - "version": "3.28.1", - "resolved": "https://registry.npmjs.org/algoliasearch-helper/-/algoliasearch-helper-3.28.1.tgz", - "integrity": "sha512-6iXpbkkrAI5HFpCWXlNmIDSBuoN/U1XnEvb2yJAoWfqrZ+DrybI7MQ5P5mthFaprmocq+zbi6HxnR28xnZAYBw==", - "license": "MIT", - "dependencies": { - "@algolia/events": "^4.0.1" - }, - "peerDependencies": { - "algoliasearch": ">= 3.1 < 6" - } - }, - "node_modules/ansi-align": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", - "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==", - "license": "ISC", - "dependencies": { - "string-width": "^4.1.0" - } - }, - "node_modules/ansi-align/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "license": "MIT" - }, - "node_modules/ansi-align/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "license": "MIT", - "dependencies": { - "type-fest": "^0.21.3" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-escapes/node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-html-community": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz", - "integrity": "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==", - "engines": [ - "node >= 0.8.0" - ], - "license": "Apache-2.0", - "bin": { - "ansi-html": "bin/ansi-html" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "license": "ISC", - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/arg": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", - "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", - "license": "MIT" - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "license": "Python-2.0" - }, - "node_modules/array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", - "license": "MIT" - }, - "node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/asn1js": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/asn1js/-/asn1js-3.0.7.tgz", - "integrity": "sha512-uLvq6KJu04qoQM6gvBfKFjlh6Gl0vOKQuR5cJMDHQkmwfMOQeN3F3SHCv9SNYSL+CRoHvOGFfllDlVz03GQjvQ==", - "license": "BSD-3-Clause", - "dependencies": { - "pvtsutils": "^1.3.6", - "pvutils": "^1.1.3", - "tslib": "^2.8.1" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/astring": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/astring/-/astring-1.9.0.tgz", - "integrity": "sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==", - "license": "MIT", - "bin": { - "astring": "bin/astring" - } - }, - "node_modules/autoprefixer": { - "version": "10.4.27", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.27.tgz", - "integrity": "sha512-NP9APE+tO+LuJGn7/9+cohklunJsXWiaWEfV3si4Gi/XHDwVNgkwr1J3RQYFIvPy76GmJ9/bW8vyoU1LcxwKHA==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/autoprefixer" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "browserslist": "^4.28.1", - "caniuse-lite": "^1.0.30001774", - "fraction.js": "^5.3.4", - "picocolors": "^1.1.1", - "postcss-value-parser": "^4.2.0" - }, - "bin": { - "autoprefixer": "bin/autoprefixer" - }, - "engines": { - "node": "^10 || ^12 || >=14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/babel-loader": { - "version": "9.2.1", - "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-9.2.1.tgz", - "integrity": "sha512-fqe8naHt46e0yIdkjUZYqddSXfej3AHajX+CSO5X7oy0EmPc6o5Xh+RClNoHjnieWz9AW4kZxW9yyFMhVB1QLA==", - "license": "MIT", - "dependencies": { - "find-cache-dir": "^4.0.0", - "schema-utils": "^4.0.0" - }, - "engines": { - "node": ">= 14.15.0" - }, - "peerDependencies": { - "@babel/core": "^7.12.0", - "webpack": ">=5" - } - }, - "node_modules/babel-plugin-dynamic-import-node": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", - "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==", - "license": "MIT", - "dependencies": { - "object.assign": "^4.1.0" - } - }, - "node_modules/babel-plugin-polyfill-corejs2": { - "version": "0.4.17", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.17.tgz", - "integrity": "sha512-aTyf30K/rqAsNwN76zYrdtx8obu0E4KoUME29B1xj+B3WxgvWkp943vYQ+z8Mv3lw9xHXMHpvSPOBxzAkIa94w==", - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.28.6", - "@babel/helper-define-polyfill-provider": "^0.6.8", - "semver": "^6.3.1" - }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" - } - }, - "node_modules/babel-plugin-polyfill-corejs2/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/babel-plugin-polyfill-corejs3": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.13.0.tgz", - "integrity": "sha512-U+GNwMdSFgzVmfhNm8GJUX88AadB3uo9KpJqS3FaqNIPKgySuvMb+bHPsOmmuWyIcuqZj/pzt1RUIUZns4y2+A==", - "license": "MIT", - "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.6.5", - "core-js-compat": "^3.43.0" - }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" - } - }, - "node_modules/babel-plugin-polyfill-regenerator": { - "version": "0.6.8", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.8.tgz", - "integrity": "sha512-M762rNHfSF1EV3SLtnCJXFoQbbIIz0OyRwnCmV0KPC7qosSfCO0QLTSuJX3ayAebubhE6oYBAYPrBA5ljowaZg==", - "license": "MIT", - "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.6.8" - }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" - } - }, - "node_modules/bail": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", - "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "license": "MIT" - }, - "node_modules/baseline-browser-mapping": { - "version": "2.10.12", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.12.tgz", - "integrity": "sha512-qyq26DxfY4awP2gIRXhhLWfwzwI+N5Nxk6iQi8EFizIaWIjqicQTE4sLnZZVdeKPRcVNoJOkkpfzoIYuvCKaIQ==", - "license": "Apache-2.0", - "bin": { - "baseline-browser-mapping": "dist/cli.cjs" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/batch": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", - "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==", - "license": "MIT" - }, - "node_modules/big.js": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", - "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", - "license": "MIT", - "engines": { - "node": "*" - } - }, - "node_modules/binary-extensions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", - "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/body-parser": { - "version": "1.20.4", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.4.tgz", - "integrity": "sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==", - "license": "MIT", - "dependencies": { - "bytes": "~3.1.2", - "content-type": "~1.0.5", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "~1.2.0", - "http-errors": "~2.0.1", - "iconv-lite": "~0.4.24", - "on-finished": "~2.4.1", - "qs": "~6.14.0", - "raw-body": "~2.5.3", - "type-is": "~1.6.18", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/body-parser/node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/body-parser/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/body-parser/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, - "node_modules/bonjour-service": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.3.0.tgz", - "integrity": "sha512-3YuAUiSkWykd+2Azjgyxei8OWf8thdn8AITIog2M4UICzoqfjlqr64WIjEXZllf/W6vK1goqleSR6brGomxQqA==", - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "multicast-dns": "^7.2.5" - } - }, - "node_modules/boolbase": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", - "license": "ISC" - }, - "node_modules/boxen": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/boxen/-/boxen-6.2.1.tgz", - "integrity": "sha512-H4PEsJXfFI/Pt8sjDWbHlQPx4zL/bvSQjcilJmaulGt5mLDorHOHpmdXAJcBcmru7PhYSp/cDMWRko4ZUMFkSw==", - "license": "MIT", - "dependencies": { - "ansi-align": "^3.0.1", - "camelcase": "^6.2.0", - "chalk": "^4.1.2", - "cli-boxes": "^3.0.0", - "string-width": "^5.0.1", - "type-fest": "^2.5.0", - "widest-line": "^4.0.1", - "wrap-ansi": "^8.0.1" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/brace-expansion": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", - "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "license": "MIT", - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browserslist": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", - "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "baseline-browser-mapping": "^2.9.0", - "caniuse-lite": "^1.0.30001759", - "electron-to-chromium": "^1.5.263", - "node-releases": "^2.0.27", - "update-browserslist-db": "^1.2.0" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "license": "MIT" - }, - "node_modules/bundle-name": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz", - "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==", - "license": "MIT", - "dependencies": { - "run-applescript": "^7.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/bytes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", - "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/bytestreamjs": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/bytestreamjs/-/bytestreamjs-2.0.1.tgz", - "integrity": "sha512-U1Z/ob71V/bXfVABvNr/Kumf5VyeQRBEm6Txb0PQ6S7V5GpBM3w4Cbqz/xPDicR5tN0uvDifng8C+5qECeGwyQ==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/cacheable-lookup": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-7.0.0.tgz", - "integrity": "sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==", - "license": "MIT", - "engines": { - "node": ">=14.16" - } - }, - "node_modules/cacheable-request": { - "version": "10.2.14", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-10.2.14.tgz", - "integrity": "sha512-zkDT5WAF4hSSoUgyfg5tFIxz8XQK+25W/TLVojJTMKBaxevLBBtLxgqguAuVQB8PVW79FVjHcU+GJ9tVbDZ9mQ==", - "license": "MIT", - "dependencies": { - "@types/http-cache-semantics": "^4.0.2", - "get-stream": "^6.0.1", - "http-cache-semantics": "^4.1.1", - "keyv": "^4.5.3", - "mimic-response": "^4.0.0", - "normalize-url": "^8.0.0", - "responselike": "^3.0.0" - }, - "engines": { - "node": ">=14.16" - } - }, - "node_modules/call-bind": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", - "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.0", - "es-define-property": "^1.0.0", - "get-intrinsic": "^1.2.4", - "set-function-length": "^1.2.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/call-bind-apply-helpers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", - "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/call-bound": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", - "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "get-intrinsic": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/camel-case": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", - "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", - "license": "MIT", - "dependencies": { - "pascal-case": "^3.1.2", - "tslib": "^2.0.3" - } - }, - "node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/caniuse-api": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", - "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==", - "license": "MIT", - "dependencies": { - "browserslist": "^4.0.0", - "caniuse-lite": "^1.0.0", - "lodash.memoize": "^4.1.2", - "lodash.uniq": "^4.5.0" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001781", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001781.tgz", - "integrity": "sha512-RdwNCyMsNBftLjW6w01z8bKEvT6e/5tpPVEgtn22TiLGlstHOVecsX2KHFkD5e/vRnIE4EGzpuIODb3mtswtkw==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "CC-BY-4.0" - }, - "node_modules/ccount": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", - "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/char-regex": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", - "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "node_modules/character-entities": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", - "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/character-entities-html4": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", - "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/character-entities-legacy": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", - "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/character-reference-invalid": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz", - "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/cheerio": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.12.tgz", - "integrity": "sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==", - "license": "MIT", - "dependencies": { - "cheerio-select": "^2.1.0", - "dom-serializer": "^2.0.0", - "domhandler": "^5.0.3", - "domutils": "^3.0.1", - "htmlparser2": "^8.0.1", - "parse5": "^7.0.0", - "parse5-htmlparser2-tree-adapter": "^7.0.0" - }, - "engines": { - "node": ">= 6" - }, - "funding": { - "url": "https://github.com/cheeriojs/cheerio?sponsor=1" - } - }, - "node_modules/cheerio-select": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz", - "integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==", - "license": "BSD-2-Clause", - "dependencies": { - "boolbase": "^1.0.0", - "css-select": "^5.1.0", - "css-what": "^6.1.0", - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3", - "domutils": "^3.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", - "license": "MIT", - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/chrome-trace-event": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", - "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==", - "license": "MIT", - "engines": { - "node": ">=6.0" - } - }, - "node_modules/ci-info": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", - "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/clean-css": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.3.tgz", - "integrity": "sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg==", - "license": "MIT", - "dependencies": { - "source-map": "~0.6.0" - }, - "engines": { - "node": ">= 10.0" - } - }, - "node_modules/clean-css/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/clean-stack": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/cli-boxes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-3.0.0.tgz", - "integrity": "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cli-table3": { - "version": "0.6.5", - "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.5.tgz", - "integrity": "sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==", - "license": "MIT", - "dependencies": { - "string-width": "^4.2.0" - }, - "engines": { - "node": "10.* || >= 12.*" - }, - "optionalDependencies": { - "@colors/colors": "1.5.0" - } - }, - "node_modules/cli-table3/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "license": "MIT" - }, - "node_modules/cli-table3/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/clone-deep": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", - "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", - "license": "MIT", - "dependencies": { - "is-plain-object": "^2.0.4", - "kind-of": "^6.0.2", - "shallow-clone": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/clsx": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", - "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/collapse-white-space": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/collapse-white-space/-/collapse-white-space-2.1.0.tgz", - "integrity": "sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "license": "MIT" - }, - "node_modules/colord": { - "version": "2.9.3", - "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz", - "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==", - "license": "MIT" - }, - "node_modules/colorette": { - "version": "2.0.20", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", - "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", - "license": "MIT" - }, - "node_modules/combine-promises": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/combine-promises/-/combine-promises-1.2.0.tgz", - "integrity": "sha512-VcQB1ziGD0NXrhKxiwyNbCDmRzs/OShMs2GqW2DlU2A/Sd0nQxE1oWDAE5O0ygSx5mgQOn9eIFh7yKPgFRVkPQ==", - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "node_modules/comma-separated-tokens": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", - "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/commander": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", - "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/common-path-prefix": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/common-path-prefix/-/common-path-prefix-3.0.0.tgz", - "integrity": "sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w==", - "license": "ISC" - }, - "node_modules/compressible": { - "version": "2.0.18", - "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", - "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", - "license": "MIT", - "dependencies": { - "mime-db": ">= 1.43.0 < 2" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/compressible/node_modules/mime-db": { - "version": "1.54.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", - "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/compression": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/compression/-/compression-1.8.1.tgz", - "integrity": "sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w==", - "license": "MIT", - "dependencies": { - "bytes": "3.1.2", - "compressible": "~2.0.18", - "debug": "2.6.9", - "negotiator": "~0.6.4", - "on-headers": "~1.1.0", - "safe-buffer": "5.2.1", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/compression/node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/compression/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/compression/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "license": "MIT" - }, - "node_modules/config-chain": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz", - "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==", - "license": "MIT", - "dependencies": { - "ini": "^1.3.4", - "proto-list": "~1.2.1" - } - }, - "node_modules/config-chain/node_modules/ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "license": "ISC" - }, - "node_modules/configstore": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/configstore/-/configstore-6.0.0.tgz", - "integrity": "sha512-cD31W1v3GqUlQvbBCGcXmd2Nj9SvLDOP1oQ0YFuLETufzSPaKp11rYBsSOm7rCsW3OnIRAFM3OxRhceaXNYHkA==", - "license": "BSD-2-Clause", - "dependencies": { - "dot-prop": "^6.0.1", - "graceful-fs": "^4.2.6", - "unique-string": "^3.0.0", - "write-file-atomic": "^3.0.3", - "xdg-basedir": "^5.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/yeoman/configstore?sponsor=1" - } - }, - "node_modules/connect-history-api-fallback": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz", - "integrity": "sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==", - "license": "MIT", - "engines": { - "node": ">=0.8" - } - }, - "node_modules/consola": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", - "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", - "license": "MIT", - "engines": { - "node": "^14.18.0 || >=16.10.0" - } - }, - "node_modules/content-disposition": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", - "integrity": "sha512-kRGRZw3bLlFISDBgwTSA1TMBFN6J6GWDeubmDE3AF+3+yXL8hTWv8r5rkLbqYXY4RjPk/EzHnClI3zQf1cFmHA==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "license": "MIT" - }, - "node_modules/cookie": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", - "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie-signature": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz", - "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==", - "license": "MIT" - }, - "node_modules/copy-webpack-plugin": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-11.0.0.tgz", - "integrity": "sha512-fX2MWpamkW0hZxMEg0+mYnA40LTosOSa5TqZ9GYIBzyJa9C3QUaMPSE2xAi/buNr8u89SfD9wHSQVBzrRa/SOQ==", - "license": "MIT", - "dependencies": { - "fast-glob": "^3.2.11", - "glob-parent": "^6.0.1", - "globby": "^13.1.1", - "normalize-path": "^3.0.0", - "schema-utils": "^4.0.0", - "serialize-javascript": "^6.0.0" - }, - "engines": { - "node": ">= 14.15.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.1.0" - } - }, - "node_modules/copy-webpack-plugin/node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/copy-webpack-plugin/node_modules/globby": { - "version": "13.2.2", - "resolved": "https://registry.npmjs.org/globby/-/globby-13.2.2.tgz", - "integrity": "sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w==", - "license": "MIT", - "dependencies": { - "dir-glob": "^3.0.1", - "fast-glob": "^3.3.0", - "ignore": "^5.2.4", - "merge2": "^1.4.1", - "slash": "^4.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/copy-webpack-plugin/node_modules/slash": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", - "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/core-js": { - "version": "3.49.0", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.49.0.tgz", - "integrity": "sha512-es1U2+YTtzpwkxVLwAFdSpaIMyQaq0PBgm3YD1W3Qpsn1NAmO3KSgZfu+oGSWVu6NvLHoHCV/aYcsE5wiB7ALg==", - "hasInstallScript": true, - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" - } - }, - "node_modules/core-js-compat": { - "version": "3.49.0", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.49.0.tgz", - "integrity": "sha512-VQXt1jr9cBz03b331DFDCCP90b3fanciLkgiOoy8SBHy06gNf+vQ1A3WFLqG7I8TipYIKeYK9wxd0tUrvHcOZA==", - "license": "MIT", - "dependencies": { - "browserslist": "^4.28.1" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" - } - }, - "node_modules/core-js-pure": { - "version": "3.49.0", - "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.49.0.tgz", - "integrity": "sha512-XM4RFka59xATyJv/cS3O3Kml72hQXUeGRuuTmMYFxwzc9/7C8OYTaIR/Ji+Yt8DXzsFLNhat15cE/JP15HrCgw==", - "hasInstallScript": true, - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" - } - }, - "node_modules/core-util-is": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", - "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", - "license": "MIT" - }, - "node_modules/cosmiconfig": { - "version": "8.3.6", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", - "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==", - "license": "MIT", - "dependencies": { - "import-fresh": "^3.3.0", - "js-yaml": "^4.1.0", - "parse-json": "^5.2.0", - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/d-fischer" - }, - "peerDependencies": { - "typescript": ">=4.9.5" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/crypto-random-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-4.0.0.tgz", - "integrity": "sha512-x8dy3RnvYdlUcPOjkEHqozhiwzKNSq7GcPuXFbnyMOCHxX8V3OgIg/pYuabl2sbUPfIJaeAQB7PMOK8DFIdoRA==", - "license": "MIT", - "dependencies": { - "type-fest": "^1.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/crypto-random-string/node_modules/type-fest": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", - "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/css-blank-pseudo": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/css-blank-pseudo/-/css-blank-pseudo-7.0.1.tgz", - "integrity": "sha512-jf+twWGDf6LDoXDUode+nc7ZlrqfaNphrBIBrcmeP3D8yw1uPaix1gCC8LUQUGQ6CycuK2opkbFFWFuq/a94ag==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "dependencies": { - "postcss-selector-parser": "^7.0.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/css-blank-pseudo/node_modules/postcss-selector-parser": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz", - "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", - "license": "MIT", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/css-declaration-sorter": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-7.3.1.tgz", - "integrity": "sha512-gz6x+KkgNCjxq3Var03pRYLhyNfwhkKF1g/yoLgDNtFvVu0/fOLV9C8fFEZRjACp/XQLumjAYo7JVjzH3wLbxA==", - "license": "ISC", - "engines": { - "node": "^14 || ^16 || >=18" - }, - "peerDependencies": { - "postcss": "^8.0.9" - } - }, - "node_modules/css-has-pseudo": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/css-has-pseudo/-/css-has-pseudo-7.0.3.tgz", - "integrity": "sha512-oG+vKuGyqe/xvEMoxAQrhi7uY16deJR3i7wwhBerVrGQKSqUC5GiOVxTpM9F9B9hw0J+eKeOWLH7E9gZ1Dr5rA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "dependencies": { - "@csstools/selector-specificity": "^5.0.0", - "postcss-selector-parser": "^7.0.0", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/css-has-pseudo/node_modules/@csstools/selector-specificity": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-5.0.0.tgz", - "integrity": "sha512-PCqQV3c4CoVm3kdPhyeZ07VmBRdH2EpMFA/pd9OASpOEC3aXNGoqPDAZ80D0cLpMBxnmk0+yNhGsEx31hq7Gtw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss-selector-parser": "^7.0.0" - } - }, - "node_modules/css-has-pseudo/node_modules/postcss-selector-parser": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz", - "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", - "license": "MIT", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/css-loader": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.11.0.tgz", - "integrity": "sha512-CTJ+AEQJjq5NzLga5pE39qdiSV56F8ywCIsqNIRF0r7BDgWsN25aazToqAFg7ZrtA/U016xudB3ffgweORxX7g==", - "license": "MIT", - "dependencies": { - "icss-utils": "^5.1.0", - "postcss": "^8.4.33", - "postcss-modules-extract-imports": "^3.1.0", - "postcss-modules-local-by-default": "^4.0.5", - "postcss-modules-scope": "^3.2.0", - "postcss-modules-values": "^4.0.0", - "postcss-value-parser": "^4.2.0", - "semver": "^7.5.4" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "@rspack/core": "0.x || 1.x", - "webpack": "^5.0.0" - }, - "peerDependenciesMeta": { - "@rspack/core": { - "optional": true - }, - "webpack": { - "optional": true - } - } - }, - "node_modules/css-minimizer-webpack-plugin": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-5.0.1.tgz", - "integrity": "sha512-3caImjKFQkS+ws1TGcFn0V1HyDJFq1Euy589JlD6/3rV2kj+w7r5G9WDMgSHvpvXHNZ2calVypZWuEDQd9wfLg==", - "license": "MIT", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.18", - "cssnano": "^6.0.1", - "jest-worker": "^29.4.3", - "postcss": "^8.4.24", - "schema-utils": "^4.0.1", - "serialize-javascript": "^6.0.1" - }, - "engines": { - "node": ">= 14.15.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.0.0" - }, - "peerDependenciesMeta": { - "@parcel/css": { - "optional": true - }, - "@swc/css": { - "optional": true - }, - "clean-css": { - "optional": true - }, - "csso": { - "optional": true - }, - "esbuild": { - "optional": true - }, - "lightningcss": { - "optional": true - } - } - }, - "node_modules/css-prefers-color-scheme": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/css-prefers-color-scheme/-/css-prefers-color-scheme-10.0.0.tgz", - "integrity": "sha512-VCtXZAWivRglTZditUfB4StnsWr6YVZ2PRtuxQLKTNRdtAf8tpzaVPE9zXIF3VaSc7O70iK/j1+NXxyQCqdPjQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/css-select": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.2.2.tgz", - "integrity": "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==", - "license": "BSD-2-Clause", - "dependencies": { - "boolbase": "^1.0.0", - "css-what": "^6.1.0", - "domhandler": "^5.0.2", - "domutils": "^3.0.1", - "nth-check": "^2.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/css-tree": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", - "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==", - "license": "MIT", - "dependencies": { - "mdn-data": "2.0.30", - "source-map-js": "^1.0.1" - }, - "engines": { - "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" - } - }, - "node_modules/css-what": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.2.2.tgz", - "integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==", - "license": "BSD-2-Clause", - "engines": { - "node": ">= 6" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/cssdb": { - "version": "8.8.0", - "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-8.8.0.tgz", - "integrity": "sha512-QbLeyz2Bgso1iRlh7IpWk6OKa3lLNGXsujVjDMPl9rOZpxKeiG69icLpbLCFxeURwmcdIfZqQyhlooKJYM4f8Q==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - } - ], - "license": "MIT-0" - }, - "node_modules/cssesc": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", - "license": "MIT", - "bin": { - "cssesc": "bin/cssesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/cssnano": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-6.1.2.tgz", - "integrity": "sha512-rYk5UeX7VAM/u0lNqewCdasdtPK81CgX8wJFLEIXHbV2oldWRgJAsZrdhRXkV1NJzA2g850KiFm9mMU2HxNxMA==", - "license": "MIT", - "dependencies": { - "cssnano-preset-default": "^6.1.2", - "lilconfig": "^3.1.1" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/cssnano" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/cssnano-preset-advanced": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/cssnano-preset-advanced/-/cssnano-preset-advanced-6.1.2.tgz", - "integrity": "sha512-Nhao7eD8ph2DoHolEzQs5CfRpiEP0xa1HBdnFZ82kvqdmbwVBUr2r1QuQ4t1pi+D1ZpqpcO4T+wy/7RxzJ/WPQ==", - "license": "MIT", - "dependencies": { - "autoprefixer": "^10.4.19", - "browserslist": "^4.23.0", - "cssnano-preset-default": "^6.1.2", - "postcss-discard-unused": "^6.0.5", - "postcss-merge-idents": "^6.0.3", - "postcss-reduce-idents": "^6.0.3", - "postcss-zindex": "^6.0.2" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/cssnano-preset-default": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-6.1.2.tgz", - "integrity": "sha512-1C0C+eNaeN8OcHQa193aRgYexyJtU8XwbdieEjClw+J9d94E41LwT6ivKH0WT+fYwYWB0Zp3I3IZ7tI/BbUbrg==", - "license": "MIT", - "dependencies": { - "browserslist": "^4.23.0", - "css-declaration-sorter": "^7.2.0", - "cssnano-utils": "^4.0.2", - "postcss-calc": "^9.0.1", - "postcss-colormin": "^6.1.0", - "postcss-convert-values": "^6.1.0", - "postcss-discard-comments": "^6.0.2", - "postcss-discard-duplicates": "^6.0.3", - "postcss-discard-empty": "^6.0.3", - "postcss-discard-overridden": "^6.0.2", - "postcss-merge-longhand": "^6.0.5", - "postcss-merge-rules": "^6.1.1", - "postcss-minify-font-values": "^6.1.0", - "postcss-minify-gradients": "^6.0.3", - "postcss-minify-params": "^6.1.0", - "postcss-minify-selectors": "^6.0.4", - "postcss-normalize-charset": "^6.0.2", - "postcss-normalize-display-values": "^6.0.2", - "postcss-normalize-positions": "^6.0.2", - "postcss-normalize-repeat-style": "^6.0.2", - "postcss-normalize-string": "^6.0.2", - "postcss-normalize-timing-functions": "^6.0.2", - "postcss-normalize-unicode": "^6.1.0", - "postcss-normalize-url": "^6.0.2", - "postcss-normalize-whitespace": "^6.0.2", - "postcss-ordered-values": "^6.0.2", - "postcss-reduce-initial": "^6.1.0", - "postcss-reduce-transforms": "^6.0.2", - "postcss-svgo": "^6.0.3", - "postcss-unique-selectors": "^6.0.4" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/cssnano-utils": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-4.0.2.tgz", - "integrity": "sha512-ZR1jHg+wZ8o4c3zqf1SIUSTIvm/9mU343FMR6Obe/unskbvpGhZOo1J6d/r8D1pzkRQYuwbcH3hToOuoA2G7oQ==", - "license": "MIT", - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/csso": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz", - "integrity": "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==", - "license": "MIT", - "dependencies": { - "css-tree": "~2.2.0" - }, - "engines": { - "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", - "npm": ">=7.0.0" - } - }, - "node_modules/csso/node_modules/css-tree": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.2.1.tgz", - "integrity": "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==", - "license": "MIT", - "dependencies": { - "mdn-data": "2.0.28", - "source-map-js": "^1.0.1" - }, - "engines": { - "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", - "npm": ">=7.0.0" - } - }, - "node_modules/csso/node_modules/mdn-data": { - "version": "2.0.28", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.28.tgz", - "integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==", - "license": "CC0-1.0" - }, - "node_modules/csstype": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", - "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", - "license": "MIT" - }, - "node_modules/debounce": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz", - "integrity": "sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==", - "license": "MIT" - }, - "node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/decode-named-character-reference": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.3.0.tgz", - "integrity": "sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==", - "license": "MIT", - "dependencies": { - "character-entities": "^2.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/decompress-response": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", - "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", - "license": "MIT", - "dependencies": { - "mimic-response": "^3.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/decompress-response/node_modules/mimic-response": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", - "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "license": "MIT", - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/deepmerge": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", - "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/default-browser": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.5.0.tgz", - "integrity": "sha512-H9LMLr5zwIbSxrmvikGuI/5KGhZ8E2zH3stkMgM5LpOWDutGM2JZaj460Udnf1a+946zc7YBgrqEWwbk7zHvGw==", - "license": "MIT", - "dependencies": { - "bundle-name": "^4.1.0", - "default-browser-id": "^5.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/default-browser-id": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.1.tgz", - "integrity": "sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/defer-to-connect": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", - "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "node_modules/define-data-property": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", - "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", - "license": "MIT", - "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "gopd": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/define-lazy-prop": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", - "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/define-properties": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", - "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", - "license": "MIT", - "dependencies": { - "define-data-property": "^1.0.1", - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/dequal": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", - "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", - "license": "MIT", - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/detect-node": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", - "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", - "license": "MIT" - }, - "node_modules/detect-port": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/detect-port/-/detect-port-1.6.1.tgz", - "integrity": "sha512-CmnVc+Hek2egPx1PeTFVta2W78xy2K/9Rkf6cC4T59S50tVnzKj+tnx5mmx5lwvCkujZ4uRrpRSuV+IVs3f90Q==", - "license": "MIT", - "dependencies": { - "address": "^1.0.1", - "debug": "4" - }, - "bin": { - "detect": "bin/detect-port.js", - "detect-port": "bin/detect-port.js" - }, - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/devlop": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", - "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", - "license": "MIT", - "dependencies": { - "dequal": "^2.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "license": "MIT", - "dependencies": { - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/dns-packet": { - "version": "5.6.1", - "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.1.tgz", - "integrity": "sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw==", - "license": "MIT", - "dependencies": { - "@leichtgewicht/ip-codec": "^2.0.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/dom-converter": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz", - "integrity": "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==", - "license": "MIT", - "dependencies": { - "utila": "~0.4" - } - }, - "node_modules/dom-serializer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", - "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", - "license": "MIT", - "dependencies": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.2", - "entities": "^4.2.0" - }, - "funding": { - "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" - } - }, - "node_modules/domelementtype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", - "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], - "license": "BSD-2-Clause" - }, - "node_modules/domhandler": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", - "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", - "license": "BSD-2-Clause", - "dependencies": { - "domelementtype": "^2.3.0" - }, - "engines": { - "node": ">= 4" - }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" - } - }, - "node_modules/domutils": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", - "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", - "license": "BSD-2-Clause", - "dependencies": { - "dom-serializer": "^2.0.0", - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3" - }, - "funding": { - "url": "https://github.com/fb55/domutils?sponsor=1" - } - }, - "node_modules/dot-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", - "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", - "license": "MIT", - "dependencies": { - "no-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "node_modules/dot-prop": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-6.0.1.tgz", - "integrity": "sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==", - "license": "MIT", - "dependencies": { - "is-obj": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/dot-prop/node_modules/is-obj": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", - "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/dunder-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", - "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "es-errors": "^1.3.0", - "gopd": "^1.2.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/duplexer": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", - "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", - "license": "MIT" - }, - "node_modules/eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "license": "MIT" - }, - "node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", - "license": "MIT" - }, - "node_modules/electron-to-chromium": { - "version": "1.5.328", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.328.tgz", - "integrity": "sha512-QNQ5l45DzYytThO21403XN3FvK0hOkWDG8viNf6jqS42msJ8I4tGDSpBCgvDRRPnkffafiwAym2X2eHeGD2V0w==", - "license": "ISC" - }, - "node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "license": "MIT" - }, - "node_modules/emojilib": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/emojilib/-/emojilib-2.4.0.tgz", - "integrity": "sha512-5U0rVMU5Y2n2+ykNLQqMoqklN9ICBT/KsvC1Gz6vqHbz2AXXGkG+Pm5rMWk/8Vjrr/mY9985Hi8DYzn1F09Nyw==", - "license": "MIT" - }, - "node_modules/emojis-list": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", - "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/emoticon": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/emoticon/-/emoticon-4.1.0.tgz", - "integrity": "sha512-VWZfnxqwNcc51hIy/sbOdEem6D+cVtpPzEEtVAFdaas30+1dgkyaOQ4sQ6Bp0tOMqWO1v+HQfYaoodOkdhK6SQ==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/encodeurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", - "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/enhanced-resolve": { - "version": "5.20.1", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.20.1.tgz", - "integrity": "sha512-Qohcme7V1inbAfvjItgw0EaxVX5q2rdVEZHRBrEQdRZTssLDGsL8Lwrznl8oQ/6kuTJONLaDcGjkNP247XEhcA==", - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.4", - "tapable": "^2.3.0" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/entities": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/error-ex": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", - "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", - "license": "MIT", - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, - "node_modules/es-define-property": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-module-lexer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.0.0.tgz", - "integrity": "sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==", - "license": "MIT" - }, - "node_modules/es-object-atoms": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/esast-util-from-estree": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/esast-util-from-estree/-/esast-util-from-estree-2.0.0.tgz", - "integrity": "sha512-4CyanoAudUSBAn5K13H4JhsMH6L9ZP7XbLVe/dKybkxMO7eDyLsT8UHl9TRNrU2Gr9nz+FovfSIjuXWJ81uVwQ==", - "license": "MIT", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "devlop": "^1.0.0", - "estree-util-visit": "^2.0.0", - "unist-util-position-from-estree": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/esast-util-from-js": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/esast-util-from-js/-/esast-util-from-js-2.0.1.tgz", - "integrity": "sha512-8Ja+rNJ0Lt56Pcf3TAmpBZjmx8ZcK5Ts4cAzIOjsjevg9oSXJnl6SUQ2EevU8tv3h6ZLWmoKL5H4fgWvdvfETw==", - "license": "MIT", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "acorn": "^8.0.0", - "esast-util-from-estree": "^2.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-goat": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-4.0.0.tgz", - "integrity": "sha512-2Sd4ShcWxbx6OY1IHyla/CVNwvg7XwZVoXZHcSu9w9SReNP1EzzD5T8NWKIR38fIqEns9kDWKUQTXXAmlDrdPg==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", - "license": "MIT" - }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "license": "BSD-2-Clause", - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "license": "BSD-2-Clause", - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "license": "BSD-2-Clause", - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esrecurse/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estree-util-attach-comments": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/estree-util-attach-comments/-/estree-util-attach-comments-3.0.0.tgz", - "integrity": "sha512-cKUwm/HUcTDsYh/9FgnuFqpfquUbwIqwKM26BVCGDPVgvaCl/nDCCjUfiLlx6lsEZ3Z4RFxNbOQ60pkaEwFxGw==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/estree-util-build-jsx": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/estree-util-build-jsx/-/estree-util-build-jsx-3.0.1.tgz", - "integrity": "sha512-8U5eiL6BTrPxp/CHbs2yMgP8ftMhR5ww1eIKoWRMlqvltHF8fZn5LRDvTKuxD3DUn+shRbLGqXemcP51oFCsGQ==", - "license": "MIT", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "devlop": "^1.0.0", - "estree-util-is-identifier-name": "^3.0.0", - "estree-walker": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/estree-util-is-identifier-name": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz", - "integrity": "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==", - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/estree-util-scope": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/estree-util-scope/-/estree-util-scope-1.0.0.tgz", - "integrity": "sha512-2CAASclonf+JFWBNJPndcOpA8EMJwa0Q8LUFJEKqXLW6+qBvbFZuF5gItbQOs/umBUkjviCSDCbBwU2cXbmrhQ==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "devlop": "^1.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/estree-util-to-js": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/estree-util-to-js/-/estree-util-to-js-2.0.0.tgz", - "integrity": "sha512-WDF+xj5rRWmD5tj6bIqRi6CkLIXbbNQUcxQHzGysQzvHmdYG2G7p/Tf0J0gpxGgkeMZNTIjT/AoSvC9Xehcgdg==", - "license": "MIT", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "astring": "^1.8.0", - "source-map": "^0.7.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/estree-util-value-to-estree": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/estree-util-value-to-estree/-/estree-util-value-to-estree-3.5.0.tgz", - "integrity": "sha512-aMV56R27Gv3QmfmF1MY12GWkGzzeAezAX+UplqHVASfjc9wNzI/X6hC0S9oxq61WT4aQesLGslWP9tKk6ghRZQ==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/remcohaszing" - } - }, - "node_modules/estree-util-visit": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/estree-util-visit/-/estree-util-visit-2.0.0.tgz", - "integrity": "sha512-m5KgiH85xAhhW8Wta0vShLcUvOsh3LLPI2YVwcbio1l7E09NTLL1EyMZFM1OyWowoH0skScNbhOPl4kcBgzTww==", - "license": "MIT", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/estree-walker": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", - "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/eta": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/eta/-/eta-2.2.0.tgz", - "integrity": "sha512-UVQ72Rqjy/ZKQalzV5dCCJP80GrmPrMxh6NlNf+erV6ObL0ZFkhCstWRawS85z3smdr3d2wXPsZEY7rDPfGd2g==", - "license": "MIT", - "engines": { - "node": ">=6.0.0" - }, - "funding": { - "url": "https://github.com/eta-dev/eta?sponsor=1" - } - }, - "node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/eval": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/eval/-/eval-0.1.8.tgz", - "integrity": "sha512-EzV94NYKoO09GLXGjXj9JIlXijVck4ONSr5wiCWDvhsvj5jxSrzTmRU/9C1DyB6uToszLs8aifA6NQ7lEQdvFw==", - "dependencies": { - "@types/node": "*", - "require-like": ">= 0.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/eventemitter3": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", - "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", - "license": "MIT" - }, - "node_modules/events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "license": "MIT", - "engines": { - "node": ">=0.8.x" - } - }, - "node_modules/execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "license": "MIT", - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/express": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.22.1.tgz", - "integrity": "sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==", - "license": "MIT", - "dependencies": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "~1.20.3", - "content-disposition": "~0.5.4", - "content-type": "~1.0.4", - "cookie": "~0.7.1", - "cookie-signature": "~1.0.6", - "debug": "2.6.9", - "depd": "2.0.0", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "~1.3.1", - "fresh": "~0.5.2", - "http-errors": "~2.0.0", - "merge-descriptors": "1.0.3", - "methods": "~1.1.2", - "on-finished": "~2.4.1", - "parseurl": "~1.3.3", - "path-to-regexp": "~0.1.12", - "proxy-addr": "~2.0.7", - "qs": "~6.14.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "~0.19.0", - "serve-static": "~1.16.2", - "setprototypeof": "1.2.0", - "statuses": "~2.0.1", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.10.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/express/node_modules/content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", - "license": "MIT", - "dependencies": { - "safe-buffer": "5.2.1" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/express/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/express/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, - "node_modules/express/node_modules/path-to-regexp": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.13.tgz", - "integrity": "sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA==", - "license": "MIT" - }, - "node_modules/express/node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "license": "MIT" - }, - "node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", - "license": "MIT", - "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "license": "MIT" - }, - "node_modules/fast-glob": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", - "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.8" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "license": "MIT" - }, - "node_modules/fast-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", - "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "BSD-3-Clause" - }, - "node_modules/fastq": { - "version": "1.20.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", - "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", - "license": "ISC", - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/fault": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fault/-/fault-2.0.1.tgz", - "integrity": "sha512-WtySTkS4OKev5JtpHXnib4Gxiurzh5NCGvWrFaZ34m6JehfTUhKZvn9njTfw48t6JumVQOmrKqpmGcdwxnhqBQ==", - "license": "MIT", - "dependencies": { - "format": "^0.2.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/faye-websocket": { - "version": "0.11.4", - "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", - "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", - "license": "Apache-2.0", - "dependencies": { - "websocket-driver": ">=0.5.1" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/feed": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/feed/-/feed-4.2.2.tgz", - "integrity": "sha512-u5/sxGfiMfZNtJ3OvQpXcvotFpYkL0n9u9mM2vkui2nGo8b4wvDkJ8gAkYqbA8QpGyFCv3RK0Z+Iv+9veCS9bQ==", - "license": "MIT", - "dependencies": { - "xml-js": "^1.6.11" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/figures": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", - "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", - "license": "MIT", - "dependencies": { - "escape-string-regexp": "^1.0.5" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/figures/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "license": "MIT", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/file-loader": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.2.0.tgz", - "integrity": "sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==", - "license": "MIT", - "dependencies": { - "loader-utils": "^2.0.0", - "schema-utils": "^3.0.0" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^4.0.0 || ^5.0.0" - } - }, - "node_modules/file-loader/node_modules/ajv": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", - "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/file-loader/node_modules/ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "license": "MIT", - "peerDependencies": { - "ajv": "^6.9.1" - } - }, - "node_modules/file-loader/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "license": "MIT" - }, - "node_modules/file-loader/node_modules/schema-utils": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", - "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", - "license": "MIT", - "dependencies": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "license": "MIT", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/finalhandler": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz", - "integrity": "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==", - "license": "MIT", - "dependencies": { - "debug": "2.6.9", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "on-finished": "~2.4.1", - "parseurl": "~1.3.3", - "statuses": "~2.0.2", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/finalhandler/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/finalhandler/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, - "node_modules/find-cache-dir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-4.0.0.tgz", - "integrity": "sha512-9ZonPT4ZAK4a+1pUPVPZJapbi7O5qbbJPdYw/NOQWZZbVLdDTYM3A4R9z/DpAM08IDaFGsvPgiGZ82WEwUDWjg==", - "license": "MIT", - "dependencies": { - "common-path-prefix": "^3.0.0", - "pkg-dir": "^7.0.0" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/find-up": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-6.3.0.tgz", - "integrity": "sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==", - "license": "MIT", - "dependencies": { - "locate-path": "^7.1.0", - "path-exists": "^5.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/flat": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", - "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", - "license": "BSD-3-Clause", - "bin": { - "flat": "cli.js" - } - }, - "node_modules/follow-redirects": { - "version": "1.15.11", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", - "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "license": "MIT", - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, - "node_modules/form-data-encoder": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-2.1.4.tgz", - "integrity": "sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw==", - "license": "MIT", - "engines": { - "node": ">= 14.17" - } - }, - "node_modules/format": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/format/-/format-0.2.2.tgz", - "integrity": "sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==", - "engines": { - "node": ">=0.4.x" - } - }, - "node_modules/forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fraction.js": { - "version": "5.3.4", - "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz", - "integrity": "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==", - "license": "MIT", - "engines": { - "node": "*" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/rawify" - } - }, - "node_modules/fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fs-extra": { - "version": "11.3.4", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.4.tgz", - "integrity": "sha512-CTXd6rk/M3/ULNQj8FBqBWHYBVYybQ3VPBw0xGKFe3tuH7ytT6ACnvzpIQ3UZtB8yvUKC2cXn1a+x+5EVQLovA==", - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=14.14" - } - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/get-intrinsic": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", - "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "function-bind": "^1.1.2", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "math-intrinsics": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-own-enumerable-property-symbols": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz", - "integrity": "sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==", - "license": "ISC" - }, - "node_modules/get-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", - "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "license": "MIT", - "dependencies": { - "dunder-proto": "^1.0.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/github-slugger": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-1.5.0.tgz", - "integrity": "sha512-wIh+gKBI9Nshz2o46B0B3f5k/W+WI9ZAv6y5Dn5WJ5SK1t0TnDimB4WE5rmTD05ZAIn8HALCZVmCsvj0w0v0lw==", - "license": "ISC" - }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/glob-to-regex.js": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/glob-to-regex.js/-/glob-to-regex.js-1.2.0.tgz", - "integrity": "sha512-QMwlOQKU/IzqMUOAZWubUOT8Qft+Y0KQWnX9nK3ch0CJg0tTp4TvGZsTfudYKv2NzoQSyPcnA6TYeIQ3jGichQ==", - "license": "Apache-2.0", - "engines": { - "node": ">=10.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/streamich" - }, - "peerDependencies": { - "tslib": "2" - } - }, - "node_modules/glob-to-regexp": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", - "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", - "license": "BSD-2-Clause" - }, - "node_modules/global-dirs": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.1.tgz", - "integrity": "sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA==", - "license": "MIT", - "dependencies": { - "ini": "2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "license": "MIT", - "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/gopd": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/got": { - "version": "12.6.1", - "resolved": "https://registry.npmjs.org/got/-/got-12.6.1.tgz", - "integrity": "sha512-mThBblvlAF1d4O5oqyvN+ZxLAYwIJK7bpMxgYqPD9okW0C3qm5FFn7k811QrcuEBwaogR3ngOFoCfs6mRv7teQ==", - "license": "MIT", - "dependencies": { - "@sindresorhus/is": "^5.2.0", - "@szmarczak/http-timer": "^5.0.1", - "cacheable-lookup": "^7.0.0", - "cacheable-request": "^10.2.8", - "decompress-response": "^6.0.0", - "form-data-encoder": "^2.1.2", - "get-stream": "^6.0.1", - "http2-wrapper": "^2.1.10", - "lowercase-keys": "^3.0.0", - "p-cancelable": "^3.0.0", - "responselike": "^3.0.0" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sindresorhus/got?sponsor=1" - } - }, - "node_modules/got/node_modules/@sindresorhus/is": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-5.6.0.tgz", - "integrity": "sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g==", - "license": "MIT", - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sindresorhus/is?sponsor=1" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "license": "ISC" - }, - "node_modules/gray-matter": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/gray-matter/-/gray-matter-4.0.3.tgz", - "integrity": "sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q==", - "license": "MIT", - "dependencies": { - "js-yaml": "^3.13.1", - "kind-of": "^6.0.2", - "section-matter": "^1.0.0", - "strip-bom-string": "^1.0.0" - }, - "engines": { - "node": ">=6.0" - } - }, - "node_modules/gray-matter/node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "license": "MIT", - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/gray-matter/node_modules/js-yaml": { - "version": "3.14.2", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", - "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", - "license": "MIT", - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/gzip-size": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-6.0.0.tgz", - "integrity": "sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==", - "license": "MIT", - "dependencies": { - "duplexer": "^0.1.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/handle-thing": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", - "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==", - "license": "MIT" - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/has-property-descriptors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", - "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", - "license": "MIT", - "dependencies": { - "es-define-property": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-yarn": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-3.0.0.tgz", - "integrity": "sha512-IrsVwUHhEULx3R8f/aA8AHuEzAorplsab/v8HBzEiIukwq5i/EC+xmOW+HfP1OaDP+2JkgT1yILHN2O3UFIbcA==", - "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/hast-util-from-parse5": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-8.0.3.tgz", - "integrity": "sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/unist": "^3.0.0", - "devlop": "^1.0.0", - "hastscript": "^9.0.0", - "property-information": "^7.0.0", - "vfile": "^6.0.0", - "vfile-location": "^5.0.0", - "web-namespaces": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-parse-selector": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-4.0.0.tgz", - "integrity": "sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-raw": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/hast-util-raw/-/hast-util-raw-9.1.0.tgz", - "integrity": "sha512-Y8/SBAHkZGoNkpzqqfCldijcuUKh7/su31kEBp67cFY09Wy0mTRgtsLYsiIxMJxlu0f6AA5SUTbDR8K0rxnbUw==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/unist": "^3.0.0", - "@ungap/structured-clone": "^1.0.0", - "hast-util-from-parse5": "^8.0.0", - "hast-util-to-parse5": "^8.0.0", - "html-void-elements": "^3.0.0", - "mdast-util-to-hast": "^13.0.0", - "parse5": "^7.0.0", - "unist-util-position": "^5.0.0", - "unist-util-visit": "^5.0.0", - "vfile": "^6.0.0", - "web-namespaces": "^2.0.0", - "zwitch": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-to-estree": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/hast-util-to-estree/-/hast-util-to-estree-3.1.3.tgz", - "integrity": "sha512-48+B/rJWAp0jamNbAAf9M7Uf//UVqAoMmgXhBdxTDJLGKY+LRnZ99qcG+Qjl5HfMpYNzS5v4EAwVEF34LeAj7w==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "@types/estree-jsx": "^1.0.0", - "@types/hast": "^3.0.0", - "comma-separated-tokens": "^2.0.0", - "devlop": "^1.0.0", - "estree-util-attach-comments": "^3.0.0", - "estree-util-is-identifier-name": "^3.0.0", - "hast-util-whitespace": "^3.0.0", - "mdast-util-mdx-expression": "^2.0.0", - "mdast-util-mdx-jsx": "^3.0.0", - "mdast-util-mdxjs-esm": "^2.0.0", - "property-information": "^7.0.0", - "space-separated-tokens": "^2.0.0", - "style-to-js": "^1.0.0", - "unist-util-position": "^5.0.0", - "zwitch": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-to-jsx-runtime": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.6.tgz", - "integrity": "sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "@types/hast": "^3.0.0", - "@types/unist": "^3.0.0", - "comma-separated-tokens": "^2.0.0", - "devlop": "^1.0.0", - "estree-util-is-identifier-name": "^3.0.0", - "hast-util-whitespace": "^3.0.0", - "mdast-util-mdx-expression": "^2.0.0", - "mdast-util-mdx-jsx": "^3.0.0", - "mdast-util-mdxjs-esm": "^2.0.0", - "property-information": "^7.0.0", - "space-separated-tokens": "^2.0.0", - "style-to-js": "^1.0.0", - "unist-util-position": "^5.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-to-parse5": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/hast-util-to-parse5/-/hast-util-to-parse5-8.0.1.tgz", - "integrity": "sha512-MlWT6Pjt4CG9lFCjiz4BH7l9wmrMkfkJYCxFwKQic8+RTZgWPuWxwAfjJElsXkex7DJjfSJsQIt931ilUgmwdA==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "comma-separated-tokens": "^2.0.0", - "devlop": "^1.0.0", - "property-information": "^7.0.0", - "space-separated-tokens": "^2.0.0", - "web-namespaces": "^2.0.0", - "zwitch": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-whitespace": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", - "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hastscript": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-9.0.1.tgz", - "integrity": "sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "comma-separated-tokens": "^2.0.0", - "hast-util-parse-selector": "^4.0.0", - "property-information": "^7.0.0", - "space-separated-tokens": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "license": "MIT", - "bin": { - "he": "bin/he" - } - }, - "node_modules/history": { - "version": "4.10.1", - "resolved": "https://registry.npmjs.org/history/-/history-4.10.1.tgz", - "integrity": "sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.1.2", - "loose-envify": "^1.2.0", - "resolve-pathname": "^3.0.0", - "tiny-invariant": "^1.0.2", - "tiny-warning": "^1.0.0", - "value-equal": "^1.0.1" - } - }, - "node_modules/hoist-non-react-statics": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", - "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", - "license": "BSD-3-Clause", - "dependencies": { - "react-is": "^16.7.0" - } - }, - "node_modules/hpack.js": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", - "integrity": "sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==", - "license": "MIT", - "dependencies": { - "inherits": "^2.0.1", - "obuf": "^1.0.0", - "readable-stream": "^2.0.1", - "wbuf": "^1.1.0" - } - }, - "node_modules/hpack.js/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "license": "MIT" - }, - "node_modules/hpack.js/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "license": "MIT", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/hpack.js/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "license": "MIT" - }, - "node_modules/hpack.js/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "license": "MIT" - }, - "node_modules/html-minifier-terser": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-7.2.0.tgz", - "integrity": "sha512-tXgn3QfqPIpGl9o+K5tpcj3/MN4SfLtsx2GWwBC3SSd0tXQGyF3gsSqad8loJgKZGM3ZxbYDd5yhiBIdWpmvLA==", - "license": "MIT", - "dependencies": { - "camel-case": "^4.1.2", - "clean-css": "~5.3.2", - "commander": "^10.0.0", - "entities": "^4.4.0", - "param-case": "^3.0.4", - "relateurl": "^0.2.7", - "terser": "^5.15.1" - }, - "bin": { - "html-minifier-terser": "cli.js" - }, - "engines": { - "node": "^14.13.1 || >=16.0.0" - } - }, - "node_modules/html-minifier-terser/node_modules/commander": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", - "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", - "license": "MIT", - "engines": { - "node": ">=14" - } - }, - "node_modules/html-tags": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.3.1.tgz", - "integrity": "sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==", - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/html-void-elements": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz", - "integrity": "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/html-webpack-plugin": { - "version": "5.6.6", - "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.6.6.tgz", - "integrity": "sha512-bLjW01UTrvoWTJQL5LsMRo1SypHW80FTm12OJRSnr3v6YHNhfe+1r0MYUZJMACxnCHURVnBWRwAsWs2yPU9Ezw==", - "license": "MIT", - "dependencies": { - "@types/html-minifier-terser": "^6.0.0", - "html-minifier-terser": "^6.0.2", - "lodash": "^4.17.21", - "pretty-error": "^4.0.0", - "tapable": "^2.0.0" - }, - "engines": { - "node": ">=10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/html-webpack-plugin" - }, - "peerDependencies": { - "@rspack/core": "0.x || 1.x", - "webpack": "^5.20.0" - }, - "peerDependenciesMeta": { - "@rspack/core": { - "optional": true - }, - "webpack": { - "optional": true - } - } - }, - "node_modules/html-webpack-plugin/node_modules/commander": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", - "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", - "license": "MIT", - "engines": { - "node": ">= 12" - } - }, - "node_modules/html-webpack-plugin/node_modules/html-minifier-terser": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", - "integrity": "sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==", - "license": "MIT", - "dependencies": { - "camel-case": "^4.1.2", - "clean-css": "^5.2.2", - "commander": "^8.3.0", - "he": "^1.2.0", - "param-case": "^3.0.4", - "relateurl": "^0.2.7", - "terser": "^5.10.0" - }, - "bin": { - "html-minifier-terser": "cli.js" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/htmlparser2": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", - "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", - "funding": [ - "https://github.com/fb55/htmlparser2?sponsor=1", - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], - "license": "MIT", - "dependencies": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3", - "domutils": "^3.0.1", - "entities": "^4.4.0" - } - }, - "node_modules/http-cache-semantics": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", - "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==", - "license": "BSD-2-Clause" - }, - "node_modules/http-deceiver": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", - "integrity": "sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==", - "license": "MIT" - }, - "node_modules/http-errors": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", - "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", - "license": "MIT", - "dependencies": { - "depd": "~2.0.0", - "inherits": "~2.0.4", - "setprototypeof": "~1.2.0", - "statuses": "~2.0.2", - "toidentifier": "~1.0.1" - }, - "engines": { - "node": ">= 0.8" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/http-parser-js": { - "version": "0.5.10", - "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.10.tgz", - "integrity": "sha512-Pysuw9XpUq5dVc/2SMHpuTY01RFl8fttgcyunjL7eEMhGM3cI4eOmiCycJDVCo/7O7ClfQD3SaI6ftDzqOXYMA==", - "license": "MIT" - }, - "node_modules/http-proxy": { - "version": "1.18.1", - "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", - "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", - "license": "MIT", - "dependencies": { - "eventemitter3": "^4.0.0", - "follow-redirects": "^1.0.0", - "requires-port": "^1.0.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/http-proxy-middleware": { - "version": "2.0.9", - "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.9.tgz", - "integrity": "sha512-c1IyJYLYppU574+YI7R4QyX2ystMtVXZwIdzazUIPIJsHuWNd+mho2j+bKoHftndicGj9yh+xjd+l0yj7VeT1Q==", - "license": "MIT", - "dependencies": { - "@types/http-proxy": "^1.17.8", - "http-proxy": "^1.18.1", - "is-glob": "^4.0.1", - "is-plain-obj": "^3.0.0", - "micromatch": "^4.0.2" - }, - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "@types/express": "^4.17.13" - }, - "peerDependenciesMeta": { - "@types/express": { - "optional": true - } - } - }, - "node_modules/http-proxy-middleware/node_modules/is-plain-obj": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", - "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/http2-wrapper": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-2.2.1.tgz", - "integrity": "sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ==", - "license": "MIT", - "dependencies": { - "quick-lru": "^5.1.1", - "resolve-alpn": "^1.2.0" - }, - "engines": { - "node": ">=10.19.0" - } - }, - "node_modules/human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "license": "Apache-2.0", - "engines": { - "node": ">=10.17.0" - } - }, - "node_modules/hyperdyperid": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/hyperdyperid/-/hyperdyperid-1.2.0.tgz", - "integrity": "sha512-Y93lCzHYgGWdrJ66yIktxiaGULYc6oGiABxhcO5AufBeOyoIdZF7bIfLaOrbM0iGIOXQQgxxRrFEnb+Y6w1n4A==", - "license": "MIT", - "engines": { - "node": ">=10.18" - } - }, - "node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/icss-utils": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", - "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", - "license": "ISC", - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/ignore": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/image-size": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/image-size/-/image-size-2.0.2.tgz", - "integrity": "sha512-IRqXKlaXwgSMAMtpNzZa1ZAe8m+Sa1770Dhk8VkSsP9LS+iHD62Zd8FQKs8fbPiagBE7BzoFX23cxFnwshpV6w==", - "license": "MIT", - "bin": { - "image-size": "bin/image-size.js" - }, - "engines": { - "node": ">=16.x" - } - }, - "node_modules/import-fresh": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", - "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", - "license": "MIT", - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/import-lazy": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-4.0.0.tgz", - "integrity": "sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "license": "MIT", - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/infima": { - "version": "0.2.0-alpha.45", - "resolved": "https://registry.npmjs.org/infima/-/infima-0.2.0-alpha.45.tgz", - "integrity": "sha512-uyH0zfr1erU1OohLk0fT4Rrb94AOhguWNOcD9uGrSpRvNB+6gZXUoJX5J0NtvzBO10YZ9PgvA4NFgt+fYg8ojw==", - "license": "MIT", - "engines": { - "node": ">=12" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "license": "ISC" - }, - "node_modules/ini": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz", - "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==", - "license": "ISC", - "engines": { - "node": ">=10" - } - }, - "node_modules/inline-style-parser": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.7.tgz", - "integrity": "sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==", - "license": "MIT" - }, - "node_modules/invariant": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", - "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", - "license": "MIT", - "dependencies": { - "loose-envify": "^1.0.0" - } - }, - "node_modules/ipaddr.js": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.3.0.tgz", - "integrity": "sha512-Zv/pA+ciVFbCSBBjGfaKUya/CcGmUHzTydLMaTwrUUEM2DIEO3iZvueGxmacvmN50fGpGVKeTXpb2LcYQxeVdg==", - "license": "MIT", - "engines": { - "node": ">= 10" - } - }, - "node_modules/is-alphabetical": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz", - "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/is-alphanumerical": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz", - "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==", - "license": "MIT", - "dependencies": { - "is-alphabetical": "^2.0.0", - "is-decimal": "^2.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "license": "MIT" - }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "license": "MIT", - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-ci": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-3.0.1.tgz", - "integrity": "sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==", - "license": "MIT", - "dependencies": { - "ci-info": "^3.2.0" - }, - "bin": { - "is-ci": "bin.js" - } - }, - "node_modules/is-core-module": { - "version": "2.16.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", - "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", - "license": "MIT", - "dependencies": { - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-decimal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz", - "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/is-docker": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", - "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", - "license": "MIT", - "bin": { - "is-docker": "cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "license": "MIT", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-hexadecimal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz", - "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/is-inside-container": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", - "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", - "license": "MIT", - "dependencies": { - "is-docker": "^3.0.0" - }, - "bin": { - "is-inside-container": "cli.js" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-inside-container/node_modules/is-docker": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", - "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", - "license": "MIT", - "bin": { - "is-docker": "cli.js" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-installed-globally": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.4.0.tgz", - "integrity": "sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==", - "license": "MIT", - "dependencies": { - "global-dirs": "^3.0.0", - "is-path-inside": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-network-error": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/is-network-error/-/is-network-error-1.3.1.tgz", - "integrity": "sha512-6QCxa49rQbmUWLfk0nuGqzql9U8uaV2H6279bRErPBHe/109hCzsLUBUHfbEtvLIHBd6hyXbgedBSHevm43Edw==", - "license": "MIT", - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-npm": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-6.1.0.tgz", - "integrity": "sha512-O2z4/kNgyjhQwVR1Wpkbfc19JIhggF97NZNCpWTnjH7kVcZMUrnut9XSN7txI7VdyIYk5ZatOq3zvSuWpU8hoA==", - "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", - "integrity": "sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-plain-obj": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", - "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "license": "MIT", - "dependencies": { - "isobject": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-regexp": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz", - "integrity": "sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", - "license": "MIT" - }, - "node_modules/is-wsl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", - "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", - "license": "MIT", - "dependencies": { - "is-docker": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-yarn-global": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.4.1.tgz", - "integrity": "sha512-/kppl+R+LO5VmhYSEWARUFjodS25D68gvj8W7z0I7OWhUla5xWu8KL6CtB2V0R6yqhnRgbcaREMr4EEM6htLPQ==", - "license": "MIT", - "engines": { - "node": ">=12" - } - }, - "node_modules/isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==", - "license": "MIT" - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "license": "ISC" - }, - "node_modules/isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/jest-util": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", - "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-worker": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", - "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", - "license": "MIT", - "dependencies": { - "@types/node": "*", - "jest-util": "^29.7.0", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-worker/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/jiti": { - "version": "1.21.7", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", - "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", - "license": "MIT", - "bin": { - "jiti": "bin/jiti.js" - } - }, - "node_modules/joi": { - "version": "17.13.3", - "resolved": "https://registry.npmjs.org/joi/-/joi-17.13.3.tgz", - "integrity": "sha512-otDA4ldcIx+ZXsKHWmp0YizCweVRZG96J10b0FevjfuncLO1oX59THoAmHkNubYJ+9gWsYsp5k8v4ib6oDv1fA==", - "license": "BSD-3-Clause", - "dependencies": { - "@hapi/hoek": "^9.3.0", - "@hapi/topo": "^5.1.0", - "@sideway/address": "^4.1.5", - "@sideway/formula": "^3.0.1", - "@sideway/pinpoint": "^2.0.0" - } - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "license": "MIT" - }, - "node_modules/js-yaml": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", - "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/jsesc": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", - "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", - "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "license": "MIT" - }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "license": "MIT" - }, - "node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "license": "MIT" - }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "license": "MIT", - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/jsonfile": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", - "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", - "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", - "license": "MIT", - "dependencies": { - "json-buffer": "3.0.1" - } - }, - "node_modules/kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/kleur": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/latest-version": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-7.0.0.tgz", - "integrity": "sha512-KvNT4XqAMzdcL6ka6Tl3i2lYeFDgXNCuIX+xNx6ZMVR1dFq+idXd9FLKNMOIx0t9mJ9/HudyX4oZWXZQ0UJHeg==", - "license": "MIT", - "dependencies": { - "package-json": "^8.1.0" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/launch-editor": { - "version": "2.13.2", - "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.13.2.tgz", - "integrity": "sha512-4VVDnbOpLXy/s8rdRCSXb+zfMeFR0WlJWpET1iA9CQdlZDfwyLjUuGQzXU4VeOoey6AicSAluWan7Etga6Kcmg==", - "license": "MIT", - "dependencies": { - "picocolors": "^1.1.1", - "shell-quote": "^1.8.3" - } - }, - "node_modules/leven": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", - "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/lilconfig": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", - "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", - "license": "MIT", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/antonk52" - } - }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "license": "MIT" - }, - "node_modules/loader-runner": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.1.tgz", - "integrity": "sha512-IWqP2SCPhyVFTBtRcgMHdzlf9ul25NwaFx4wCEH/KjAXuuHY4yNjvPXsBokp8jCB936PyWRaPKUNh8NvylLp2Q==", - "license": "MIT", - "engines": { - "node": ">=6.11.5" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/loader-utils": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", - "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", - "license": "MIT", - "dependencies": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^2.1.2" - }, - "engines": { - "node": ">=8.9.0" - } - }, - "node_modules/locate-path": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz", - "integrity": "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==", - "license": "MIT", - "dependencies": { - "p-locate": "^6.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lodash": { - "version": "4.17.23", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", - "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==", - "license": "MIT" - }, - "node_modules/lodash.debounce": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", - "license": "MIT" - }, - "node_modules/lodash.memoize": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", - "license": "MIT" - }, - "node_modules/lodash.uniq": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", - "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==", - "license": "MIT" - }, - "node_modules/longest-streak": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", - "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "license": "MIT", - "dependencies": { - "js-tokens": "^3.0.0 || ^4.0.0" - }, - "bin": { - "loose-envify": "cli.js" - } - }, - "node_modules/lower-case": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", - "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", - "license": "MIT", - "dependencies": { - "tslib": "^2.0.3" - } - }, - "node_modules/lowercase-keys": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz", - "integrity": "sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==", - "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "license": "ISC", - "dependencies": { - "yallist": "^3.0.2" - } - }, - "node_modules/markdown-extensions": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/markdown-extensions/-/markdown-extensions-2.0.0.tgz", - "integrity": "sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q==", - "license": "MIT", - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/markdown-table": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.4.tgz", - "integrity": "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/math-intrinsics": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/mdast-util-directive": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mdast-util-directive/-/mdast-util-directive-3.1.0.tgz", - "integrity": "sha512-I3fNFt+DHmpWCYAT7quoM6lHf9wuqtI+oCOfvILnoicNIqjh5E3dEJWiXuYME2gNe8vl1iMQwyUHa7bgFmak6Q==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "@types/unist": "^3.0.0", - "ccount": "^2.0.0", - "devlop": "^1.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0", - "parse-entities": "^4.0.0", - "stringify-entities": "^4.0.0", - "unist-util-visit-parents": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-find-and-replace": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.2.tgz", - "integrity": "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "escape-string-regexp": "^5.0.0", - "unist-util-is": "^6.0.0", - "unist-util-visit-parents": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-find-and-replace/node_modules/escape-string-regexp": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", - "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/mdast-util-from-markdown": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.3.tgz", - "integrity": "sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "@types/unist": "^3.0.0", - "decode-named-character-reference": "^1.0.0", - "devlop": "^1.0.0", - "mdast-util-to-string": "^4.0.0", - "micromark": "^4.0.0", - "micromark-util-decode-numeric-character-reference": "^2.0.0", - "micromark-util-decode-string": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0", - "unist-util-stringify-position": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-from-markdown/node_modules/micromark-util-symbol": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", - "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/mdast-util-frontmatter": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/mdast-util-frontmatter/-/mdast-util-frontmatter-2.0.1.tgz", - "integrity": "sha512-LRqI9+wdgC25P0URIJY9vwocIzCcksduHQ9OF2joxQoyTNVduwLAFUzjoopuRJbJAReaKrNQKAZKL3uCMugWJA==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "devlop": "^1.0.0", - "escape-string-regexp": "^5.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0", - "micromark-extension-frontmatter": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-frontmatter/node_modules/escape-string-regexp": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", - "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/mdast-util-gfm": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-3.1.0.tgz", - "integrity": "sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==", - "license": "MIT", - "dependencies": { - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-gfm-autolink-literal": "^2.0.0", - "mdast-util-gfm-footnote": "^2.0.0", - "mdast-util-gfm-strikethrough": "^2.0.0", - "mdast-util-gfm-table": "^2.0.0", - "mdast-util-gfm-task-list-item": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-autolink-literal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.1.tgz", - "integrity": "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "ccount": "^2.0.0", - "devlop": "^1.0.0", - "mdast-util-find-and-replace": "^3.0.0", - "micromark-util-character": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-autolink-literal/node_modules/micromark-util-character": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", - "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/mdast-util-gfm-autolink-literal/node_modules/micromark-util-symbol": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", - "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/mdast-util-gfm-footnote": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.1.0.tgz", - "integrity": "sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "devlop": "^1.1.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-strikethrough": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.0.tgz", - "integrity": "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-table": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-2.0.0.tgz", - "integrity": "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "devlop": "^1.0.0", - "markdown-table": "^3.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-task-list-item": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.0.tgz", - "integrity": "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "devlop": "^1.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-mdx": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-mdx/-/mdast-util-mdx-3.0.0.tgz", - "integrity": "sha512-JfbYLAW7XnYTTbUsmpu0kdBUVe+yKVJZBItEjwyYJiDJuZ9w4eeaqks4HQO+R7objWgS2ymV60GYpI14Ug554w==", - "license": "MIT", - "dependencies": { - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-mdx-expression": "^2.0.0", - "mdast-util-mdx-jsx": "^3.0.0", - "mdast-util-mdxjs-esm": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-mdx-expression": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.1.tgz", - "integrity": "sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==", - "license": "MIT", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "devlop": "^1.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-mdx-jsx": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.2.0.tgz", - "integrity": "sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==", - "license": "MIT", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "@types/unist": "^3.0.0", - "ccount": "^2.0.0", - "devlop": "^1.1.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0", - "parse-entities": "^4.0.0", - "stringify-entities": "^4.0.0", - "unist-util-stringify-position": "^4.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-mdxjs-esm": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-2.0.1.tgz", - "integrity": "sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==", - "license": "MIT", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "devlop": "^1.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-phrasing": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz", - "integrity": "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "unist-util-is": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-to-hast": { - "version": "13.2.1", - "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.1.tgz", - "integrity": "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "@ungap/structured-clone": "^1.0.0", - "devlop": "^1.0.0", - "micromark-util-sanitize-uri": "^2.0.0", - "trim-lines": "^3.0.0", - "unist-util-position": "^5.0.0", - "unist-util-visit": "^5.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-to-markdown": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.2.tgz", - "integrity": "sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "@types/unist": "^3.0.0", - "longest-streak": "^3.0.0", - "mdast-util-phrasing": "^4.0.0", - "mdast-util-to-string": "^4.0.0", - "micromark-util-classify-character": "^2.0.0", - "micromark-util-decode-string": "^2.0.0", - "unist-util-visit": "^5.0.0", - "zwitch": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-to-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", - "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdn-data": { - "version": "2.0.30", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", - "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==", - "license": "CC0-1.0" - }, - "node_modules/media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/memfs": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/memfs/-/memfs-4.57.1.tgz", - "integrity": "sha512-WvzrWPwMQT+PtbX2Et64R4qXKK0fj/8pO85MrUCzymX3twwCiJCdvntW3HdhG1teLJcHDDLIKx5+c3HckWYZtQ==", - "license": "Apache-2.0", - "dependencies": { - "@jsonjoy.com/fs-core": "4.57.1", - "@jsonjoy.com/fs-fsa": "4.57.1", - "@jsonjoy.com/fs-node": "4.57.1", - "@jsonjoy.com/fs-node-builtins": "4.57.1", - "@jsonjoy.com/fs-node-to-fsa": "4.57.1", - "@jsonjoy.com/fs-node-utils": "4.57.1", - "@jsonjoy.com/fs-print": "4.57.1", - "@jsonjoy.com/fs-snapshot": "4.57.1", - "@jsonjoy.com/json-pack": "^1.11.0", - "@jsonjoy.com/util": "^1.9.0", - "glob-to-regex.js": "^1.0.1", - "thingies": "^2.5.0", - "tree-dump": "^1.0.3", - "tslib": "^2.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/streamich" - }, - "peerDependencies": { - "tslib": "2" - } - }, - "node_modules/merge-descriptors": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", - "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "license": "MIT" - }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/micromark": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz", - "integrity": "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "@types/debug": "^4.0.0", - "debug": "^4.0.0", - "decode-named-character-reference": "^1.0.0", - "devlop": "^1.0.0", - "micromark-core-commonmark": "^2.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-chunked": "^2.0.0", - "micromark-util-combine-extensions": "^2.0.0", - "micromark-util-decode-numeric-character-reference": "^2.0.0", - "micromark-util-encode": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0", - "micromark-util-resolve-all": "^2.0.0", - "micromark-util-sanitize-uri": "^2.0.0", - "micromark-util-subtokenize": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-core-commonmark": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz", - "integrity": "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "decode-named-character-reference": "^1.0.0", - "devlop": "^1.0.0", - "micromark-factory-destination": "^2.0.0", - "micromark-factory-label": "^2.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-factory-title": "^2.0.0", - "micromark-factory-whitespace": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-chunked": "^2.0.0", - "micromark-util-classify-character": "^2.0.0", - "micromark-util-html-tag-name": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0", - "micromark-util-resolve-all": "^2.0.0", - "micromark-util-subtokenize": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-core-commonmark/node_modules/micromark-factory-space": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", - "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-core-commonmark/node_modules/micromark-util-character": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", - "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-core-commonmark/node_modules/micromark-util-symbol": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", - "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-extension-directive": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/micromark-extension-directive/-/micromark-extension-directive-3.0.2.tgz", - "integrity": "sha512-wjcXHgk+PPdmvR58Le9d7zQYWy+vKEU9Se44p2CrCDPiLr2FMyiT4Fyb5UFKFC66wGB3kPlgD7q3TnoqPS7SZA==", - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-factory-whitespace": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0", - "parse-entities": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-directive/node_modules/micromark-factory-space": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", - "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-extension-directive/node_modules/micromark-util-character": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", - "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-extension-directive/node_modules/micromark-util-symbol": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", - "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-extension-frontmatter": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-extension-frontmatter/-/micromark-extension-frontmatter-2.0.0.tgz", - "integrity": "sha512-C4AkuM3dA58cgZha7zVnuVxBhDsbttIMiytjgsM2XbHAB2faRVaHRle40558FBN+DJcrLNCoqG5mlrpdU4cRtg==", - "license": "MIT", - "dependencies": { - "fault": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-frontmatter/node_modules/micromark-util-character": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", - "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-extension-frontmatter/node_modules/micromark-util-symbol": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", - "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-extension-gfm": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-3.0.0.tgz", - "integrity": "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==", - "license": "MIT", - "dependencies": { - "micromark-extension-gfm-autolink-literal": "^2.0.0", - "micromark-extension-gfm-footnote": "^2.0.0", - "micromark-extension-gfm-strikethrough": "^2.0.0", - "micromark-extension-gfm-table": "^2.0.0", - "micromark-extension-gfm-tagfilter": "^2.0.0", - "micromark-extension-gfm-task-list-item": "^2.0.0", - "micromark-util-combine-extensions": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-autolink-literal": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.1.0.tgz", - "integrity": "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==", - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-sanitize-uri": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-autolink-literal/node_modules/micromark-util-character": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", - "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-extension-gfm-autolink-literal/node_modules/micromark-util-symbol": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", - "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-extension-gfm-footnote": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.1.0.tgz", - "integrity": "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==", - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-core-commonmark": "^2.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0", - "micromark-util-sanitize-uri": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-footnote/node_modules/micromark-factory-space": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", - "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-extension-gfm-footnote/node_modules/micromark-util-character": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", - "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-extension-gfm-footnote/node_modules/micromark-util-symbol": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", - "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-extension-gfm-strikethrough": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-2.1.0.tgz", - "integrity": "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==", - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-util-chunked": "^2.0.0", - "micromark-util-classify-character": "^2.0.0", - "micromark-util-resolve-all": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-strikethrough/node_modules/micromark-util-symbol": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", - "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-extension-gfm-table": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.1.tgz", - "integrity": "sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==", - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-table/node_modules/micromark-factory-space": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", - "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-extension-gfm-table/node_modules/micromark-util-character": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", - "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-extension-gfm-table/node_modules/micromark-util-symbol": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", - "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-extension-gfm-tagfilter": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-2.0.0.tgz", - "integrity": "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==", - "license": "MIT", - "dependencies": { - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-task-list-item": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-2.1.0.tgz", - "integrity": "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==", - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-task-list-item/node_modules/micromark-factory-space": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", - "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-extension-gfm-task-list-item/node_modules/micromark-util-character": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", - "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-extension-gfm-task-list-item/node_modules/micromark-util-symbol": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", - "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-extension-mdx-expression": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/micromark-extension-mdx-expression/-/micromark-extension-mdx-expression-3.0.1.tgz", - "integrity": "sha512-dD/ADLJ1AeMvSAKBwO22zG22N4ybhe7kFIZ3LsDI0GlsNr2A3KYxb0LdC1u5rj4Nw+CHKY0RVdnHX8vj8ejm4Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "devlop": "^1.0.0", - "micromark-factory-mdx-expression": "^2.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-events-to-acorn": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-extension-mdx-expression/node_modules/micromark-factory-space": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", - "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-extension-mdx-expression/node_modules/micromark-util-character": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", - "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-extension-mdx-expression/node_modules/micromark-util-symbol": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", - "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-extension-mdx-jsx": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/micromark-extension-mdx-jsx/-/micromark-extension-mdx-jsx-3.0.2.tgz", - "integrity": "sha512-e5+q1DjMh62LZAJOnDraSSbDMvGJ8x3cbjygy2qFEi7HCeUT4BDKCvMozPozcD6WmOt6sVvYDNBKhFSz3kjOVQ==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "devlop": "^1.0.0", - "estree-util-is-identifier-name": "^3.0.0", - "micromark-factory-mdx-expression": "^2.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-events-to-acorn": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-mdx-jsx/node_modules/micromark-factory-space": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", - "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-extension-mdx-jsx/node_modules/micromark-util-character": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", - "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-extension-mdx-jsx/node_modules/micromark-util-symbol": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", - "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-extension-mdx-md": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-extension-mdx-md/-/micromark-extension-mdx-md-2.0.0.tgz", - "integrity": "sha512-EpAiszsB3blw4Rpba7xTOUptcFeBFi+6PY8VnJ2hhimH+vCQDirWgsMpz7w1XcZE7LVrSAUGb9VJpG9ghlYvYQ==", - "license": "MIT", - "dependencies": { - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-mdxjs": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/micromark-extension-mdxjs/-/micromark-extension-mdxjs-3.0.0.tgz", - "integrity": "sha512-A873fJfhnJ2siZyUrJ31l34Uqwy4xIFmvPY1oj+Ean5PHcPBYzEsvqvWGaWcfEIr11O5Dlw3p2y0tZWpKHDejQ==", - "license": "MIT", - "dependencies": { - "acorn": "^8.0.0", - "acorn-jsx": "^5.0.0", - "micromark-extension-mdx-expression": "^3.0.0", - "micromark-extension-mdx-jsx": "^3.0.0", - "micromark-extension-mdx-md": "^2.0.0", - "micromark-extension-mdxjs-esm": "^3.0.0", - "micromark-util-combine-extensions": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-mdxjs-esm": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/micromark-extension-mdxjs-esm/-/micromark-extension-mdxjs-esm-3.0.0.tgz", - "integrity": "sha512-DJFl4ZqkErRpq/dAPyeWp15tGrcrrJho1hKK5uBS70BCtfrIFg81sqcTVu3Ta+KD1Tk5vAtBNElWxtAa+m8K9A==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "devlop": "^1.0.0", - "micromark-core-commonmark": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-events-to-acorn": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0", - "unist-util-position-from-estree": "^2.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-mdxjs-esm/node_modules/micromark-util-character": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", - "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-extension-mdxjs-esm/node_modules/micromark-util-symbol": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", - "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-factory-destination": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz", - "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-destination/node_modules/micromark-util-character": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", - "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-destination/node_modules/micromark-util-symbol": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", - "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-factory-label": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz", - "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-label/node_modules/micromark-util-character": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", - "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-label/node_modules/micromark-util-symbol": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", - "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-factory-mdx-expression": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/micromark-factory-mdx-expression/-/micromark-factory-mdx-expression-2.0.3.tgz", - "integrity": "sha512-kQnEtA3vzucU2BkrIa8/VaSAsP+EJ3CKOvhMuJgOEGg9KDC6OAY6nSnNDVRiVNRqj7Y4SlSzcStaH/5jge8JdQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "devlop": "^1.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-events-to-acorn": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0", - "unist-util-position-from-estree": "^2.0.0", - "vfile-message": "^4.0.0" - } - }, - "node_modules/micromark-factory-mdx-expression/node_modules/micromark-factory-space": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", - "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-mdx-expression/node_modules/micromark-util-character": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", - "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-mdx-expression/node_modules/micromark-util-symbol": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", - "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-factory-space": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-1.1.0.tgz", - "integrity": "sha512-cRzEj7c0OL4Mw2v6nwzttyOZe8XY/Z8G0rzmWQZTBi/jjwyw/U4uqKtUORXQrR5bAZZnbTI/feRV/R7hc4jQYQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-character": "^1.0.0", - "micromark-util-types": "^1.0.0" - } - }, - "node_modules/micromark-factory-space/node_modules/micromark-util-types": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-1.1.0.tgz", - "integrity": "sha512-ukRBgie8TIAcacscVHSiddHjO4k/q3pnedmzMQ4iwDcK0FtFCohKOlFbaOL/mPgfnPsL3C1ZyxJa4sbWrBl3jg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-factory-title": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz", - "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-title/node_modules/micromark-factory-space": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", - "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-title/node_modules/micromark-util-character": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", - "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-title/node_modules/micromark-util-symbol": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", - "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-factory-whitespace": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz", - "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-whitespace/node_modules/micromark-factory-space": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", - "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-whitespace/node_modules/micromark-util-character": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", - "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-whitespace/node_modules/micromark-util-symbol": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", - "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-util-character": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-1.2.0.tgz", - "integrity": "sha512-lXraTwcX3yH/vMDaFWCQJP1uIszLVebzUa3ZHdrgxr7KEU/9mL4mVgCpGbyhvNLNlauROiNUq7WN5u7ndbY6xg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^1.0.0", - "micromark-util-types": "^1.0.0" - } - }, - "node_modules/micromark-util-character/node_modules/micromark-util-types": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-1.1.0.tgz", - "integrity": "sha512-ukRBgie8TIAcacscVHSiddHjO4k/q3pnedmzMQ4iwDcK0FtFCohKOlFbaOL/mPgfnPsL3C1ZyxJa4sbWrBl3jg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-util-chunked": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz", - "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-chunked/node_modules/micromark-util-symbol": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", - "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-util-classify-character": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz", - "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-classify-character/node_modules/micromark-util-character": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", - "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-classify-character/node_modules/micromark-util-symbol": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", - "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-util-combine-extensions": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz", - "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-chunked": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-decode-numeric-character-reference": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz", - "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-decode-numeric-character-reference/node_modules/micromark-util-symbol": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", - "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-util-decode-string": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz", - "integrity": "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "decode-named-character-reference": "^1.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-decode-numeric-character-reference": "^2.0.0", - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-decode-string/node_modules/micromark-util-character": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", - "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-decode-string/node_modules/micromark-util-symbol": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", - "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-util-encode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", - "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-util-events-to-acorn": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/micromark-util-events-to-acorn/-/micromark-util-events-to-acorn-2.0.3.tgz", - "integrity": "sha512-jmsiEIiZ1n7X1Rr5k8wVExBQCg5jy4UXVADItHmNk1zkwEVhBuIUKRu3fqv+hs4nxLISi2DQGlqIOGiFxgbfHg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "@types/unist": "^3.0.0", - "devlop": "^1.0.0", - "estree-util-visit": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0", - "vfile-message": "^4.0.0" - } - }, - "node_modules/micromark-util-events-to-acorn/node_modules/micromark-util-symbol": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", - "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-util-html-tag-name": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz", - "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-util-normalize-identifier": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz", - "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-normalize-identifier/node_modules/micromark-util-symbol": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", - "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-util-resolve-all": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz", - "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-sanitize-uri": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", - "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-encode": "^2.0.0", - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-sanitize-uri/node_modules/micromark-util-character": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", - "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-sanitize-uri/node_modules/micromark-util-symbol": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", - "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-util-subtokenize": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz", - "integrity": "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-util-chunked": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-subtokenize/node_modules/micromark-util-symbol": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", - "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-util-symbol": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-1.1.0.tgz", - "integrity": "sha512-uEjpEYY6KMs1g7QfJ2eX1SQEV+ZT4rUD3UcF6l57acZvLNK7PBZL+ty82Z1qhK1/yXIY4bdx04FKMgR0g4IAag==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-util-types": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz", - "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark/node_modules/micromark-factory-space": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", - "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark/node_modules/micromark-util-character": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", - "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark/node_modules/micromark-util-symbol": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", - "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "license": "MIT", - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "license": "MIT", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/mime-db": { - "version": "1.33.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz", - "integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.18", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz", - "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==", - "license": "MIT", - "dependencies": { - "mime-db": "~1.33.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/mimic-response": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-4.0.0.tgz", - "integrity": "sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==", - "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/mini-css-extract-plugin": { - "version": "2.10.2", - "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.10.2.tgz", - "integrity": "sha512-AOSS0IdEB95ayVkxn5oGzNQwqAi2J0Jb/kKm43t7H73s8+f5873g0yuj0PNvK4dO75mu5DHg4nlgp4k6Kga8eg==", - "license": "MIT", - "dependencies": { - "schema-utils": "^4.0.0", - "tapable": "^2.2.1" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.0.0" - } - }, - "node_modules/minimalistic-assert": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", - "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", - "license": "ISC" - }, - "node_modules/minimatch": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", - "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/mrmime": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz", - "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==", - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/multicast-dns": { - "version": "7.2.5", - "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz", - "integrity": "sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==", - "license": "MIT", - "dependencies": { - "dns-packet": "^5.2.2", - "thunky": "^1.0.2" - }, - "bin": { - "multicast-dns": "cli.js" - } - }, - "node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/negotiator": { - "version": "0.6.4", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz", - "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "license": "MIT" - }, - "node_modules/no-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", - "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", - "license": "MIT", - "dependencies": { - "lower-case": "^2.0.2", - "tslib": "^2.0.3" - } - }, - "node_modules/node-emoji": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-2.2.0.tgz", - "integrity": "sha512-Z3lTE9pLaJF47NyMhd4ww1yFTAP8YhYI8SleJiHzM46Fgpm5cnNzSl9XfzFNqbaz+VlJrIj3fXQ4DeN1Rjm6cw==", - "license": "MIT", - "dependencies": { - "@sindresorhus/is": "^4.6.0", - "char-regex": "^1.0.2", - "emojilib": "^2.4.0", - "skin-tone": "^2.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/node-releases": { - "version": "2.0.36", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.36.tgz", - "integrity": "sha512-TdC8FSgHz8Mwtw9g5L4gR/Sh9XhSP/0DEkQxfEFXOpiul5IiHgHan2VhYYb6agDSfp4KuvltmGApc8HMgUrIkA==", - "license": "MIT" - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/normalize-url": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-8.1.1.tgz", - "integrity": "sha512-JYc0DPlpGWB40kH5g07gGTrYuMqV653k3uBKY6uITPWds3M0ov3GaWGp9lbE3Bzngx8+XkfzgvASb9vk9JDFXQ==", - "license": "MIT", - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "license": "MIT", - "dependencies": { - "path-key": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/nprogress": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/nprogress/-/nprogress-0.2.0.tgz", - "integrity": "sha512-I19aIingLgR1fmhftnbWWO3dXc0hSxqHQHQb3H8m+K3TnEn/iSeTZZOyvKXWqQESMwuUVnatlCnZdLBZZt2VSA==", - "license": "MIT" - }, - "node_modules/nth-check": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", - "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", - "license": "BSD-2-Clause", - "dependencies": { - "boolbase": "^1.0.0" - }, - "funding": { - "url": "https://github.com/fb55/nth-check?sponsor=1" - } - }, - "node_modules/null-loader": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/null-loader/-/null-loader-4.0.1.tgz", - "integrity": "sha512-pxqVbi4U6N26lq+LmgIbB5XATP0VdZKOG25DhHi8btMmJJefGArFyDg1yc4U3hWCJbMqSrw0qyrz1UQX+qYXqg==", - "license": "MIT", - "dependencies": { - "loader-utils": "^2.0.0", - "schema-utils": "^3.0.0" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^4.0.0 || ^5.0.0" - } - }, - "node_modules/null-loader/node_modules/ajv": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", - "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/null-loader/node_modules/ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "license": "MIT", - "peerDependencies": { - "ajv": "^6.9.1" - } - }, - "node_modules/null-loader/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "license": "MIT" - }, - "node_modules/null-loader/node_modules/schema-utils": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", - "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", - "license": "MIT", - "dependencies": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-inspect": { - "version": "1.13.4", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", - "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.assign": { - "version": "4.1.7", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", - "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0", - "has-symbols": "^1.1.0", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/obuf": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", - "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", - "license": "MIT" - }, - "node_modules/on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "license": "MIT", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/on-headers": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz", - "integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "license": "MIT", - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/open": { - "version": "8.4.2", - "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", - "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", - "license": "MIT", - "dependencies": { - "define-lazy-prop": "^2.0.0", - "is-docker": "^2.1.1", - "is-wsl": "^2.2.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/opener": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz", - "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==", - "license": "(WTFPL OR MIT)", - "bin": { - "opener": "bin/opener-bin.js" - } - }, - "node_modules/p-cancelable": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-3.0.0.tgz", - "integrity": "sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==", - "license": "MIT", - "engines": { - "node": ">=12.20" - } - }, - "node_modules/p-finally": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/p-limit": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", - "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==", - "license": "MIT", - "dependencies": { - "yocto-queue": "^1.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz", - "integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==", - "license": "MIT", - "dependencies": { - "p-limit": "^4.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-map": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", - "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", - "license": "MIT", - "dependencies": { - "aggregate-error": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-queue": { - "version": "6.6.2", - "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-6.6.2.tgz", - "integrity": "sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==", - "license": "MIT", - "dependencies": { - "eventemitter3": "^4.0.4", - "p-timeout": "^3.2.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-retry": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-6.2.1.tgz", - "integrity": "sha512-hEt02O4hUct5wtwg4H4KcWgDdm+l1bOaEy/hWzd8xtXB9BqxTWBBhb+2ImAtH4Cv4rPjV76xN3Zumqk3k3AhhQ==", - "license": "MIT", - "dependencies": { - "@types/retry": "0.12.2", - "is-network-error": "^1.0.0", - "retry": "^0.13.1" - }, - "engines": { - "node": ">=16.17" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-timeout": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz", - "integrity": "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==", - "license": "MIT", - "dependencies": { - "p-finally": "^1.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/package-json": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/package-json/-/package-json-8.1.1.tgz", - "integrity": "sha512-cbH9IAIJHNj9uXi196JVsRlt7cHKak6u/e6AkL/bkRelZ7rlL3X1YKxsZwa36xipOEKAsdtmaG6aAJoM1fx2zA==", - "license": "MIT", - "dependencies": { - "got": "^12.1.0", - "registry-auth-token": "^5.0.1", - "registry-url": "^6.0.0", - "semver": "^7.3.7" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/param-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", - "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", - "license": "MIT", - "dependencies": { - "dot-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "license": "MIT", - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/parse-entities": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.2.tgz", - "integrity": "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==", - "license": "MIT", - "dependencies": { - "@types/unist": "^2.0.0", - "character-entities-legacy": "^3.0.0", - "character-reference-invalid": "^2.0.0", - "decode-named-character-reference": "^1.0.0", - "is-alphanumerical": "^2.0.0", - "is-decimal": "^2.0.0", - "is-hexadecimal": "^2.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/parse-entities/node_modules/@types/unist": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", - "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", - "license": "MIT" - }, - "node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/parse-numeric-range": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/parse-numeric-range/-/parse-numeric-range-1.3.0.tgz", - "integrity": "sha512-twN+njEipszzlMJd4ONUYgSfZPDxgHhT9Ahed5uTigpQn90FggW4SA/AIPq/6a149fTbE9qBEcSwE3FAEp6wQQ==", - "license": "ISC" - }, - "node_modules/parse5": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", - "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", - "license": "MIT", - "dependencies": { - "entities": "^6.0.0" - }, - "funding": { - "url": "https://github.com/inikulin/parse5?sponsor=1" - } - }, - "node_modules/parse5-htmlparser2-tree-adapter": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.1.0.tgz", - "integrity": "sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==", - "license": "MIT", - "dependencies": { - "domhandler": "^5.0.3", - "parse5": "^7.0.0" - }, - "funding": { - "url": "https://github.com/inikulin/parse5?sponsor=1" - } - }, - "node_modules/parse5/node_modules/entities": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", - "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/pascal-case": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", - "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", - "license": "MIT", - "dependencies": { - "no-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "node_modules/path-exists": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz", - "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==", - "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - } - }, - "node_modules/path-is-inside": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", - "integrity": "sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==", - "license": "(WTFPL OR MIT)" - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "license": "MIT" - }, - "node_modules/path-to-regexp": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.9.0.tgz", - "integrity": "sha512-xIp7/apCFJuUHdDLWe8O1HIkb0kQrOMb/0u6FXQjemHn/ii5LrIzU6bdECnsiTF/GjZkMEKg1xdiZwNqDYlZ6g==", - "license": "MIT", - "dependencies": { - "isarray": "0.0.1" - } - }, - "node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "license": "ISC" - }, - "node_modules/picomatch": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", - "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pkg-dir": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-7.0.0.tgz", - "integrity": "sha512-Ie9z/WINcxxLp27BKOCHGde4ITq9UklYKDzVo1nhk5sqGEXU3FpkwP5GM2voTGJkGd9B3Otl+Q4uwSOeSUtOBA==", - "license": "MIT", - "dependencies": { - "find-up": "^6.3.0" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/pkijs": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/pkijs/-/pkijs-3.4.0.tgz", - "integrity": "sha512-emEcLuomt2j03vxD54giVB4SxTjnsqkU692xZOZXHDVoYyypEm+b3jpiTcc+Cf+myooc+/Ly0z01jqeNHVgJGw==", - "license": "BSD-3-Clause", - "dependencies": { - "@noble/hashes": "1.4.0", - "asn1js": "^3.0.6", - "bytestreamjs": "^2.0.1", - "pvtsutils": "^1.3.6", - "pvutils": "^1.1.3", - "tslib": "^2.8.1" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/postcss": { - "version": "8.5.8", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.8.tgz", - "integrity": "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "nanoid": "^3.3.11", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/postcss-attribute-case-insensitive": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-7.0.1.tgz", - "integrity": "sha512-Uai+SupNSqzlschRyNx3kbCTWgY/2hcwtHEI/ej2LJWc9JJ77qKgGptd8DHwY1mXtZ7Aoh4z4yxfwMBue9eNgw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT", - "dependencies": { - "postcss-selector-parser": "^7.0.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-attribute-case-insensitive/node_modules/postcss-selector-parser": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz", - "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", - "license": "MIT", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/postcss-calc": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-9.0.1.tgz", - "integrity": "sha512-TipgjGyzP5QzEhsOZUaIkeO5mKeMFpebWzRogWG/ysonUlnHcq5aJe0jOjpfzUU8PeSaBQnrE8ehR0QA5vs8PQ==", - "license": "MIT", - "dependencies": { - "postcss-selector-parser": "^6.0.11", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.2.2" - } - }, - "node_modules/postcss-clamp": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/postcss-clamp/-/postcss-clamp-4.1.0.tgz", - "integrity": "sha512-ry4b1Llo/9zz+PKC+030KUnPITTJAHeOwjfAyyB60eT0AorGLdzp52s31OsPRHRf8NchkgFoG2y6fCfn1IV1Ow==", - "license": "MIT", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": ">=7.6.0" - }, - "peerDependencies": { - "postcss": "^8.4.6" - } - }, - "node_modules/postcss-color-functional-notation": { - "version": "7.0.12", - "resolved": "https://registry.npmjs.org/postcss-color-functional-notation/-/postcss-color-functional-notation-7.0.12.tgz", - "integrity": "sha512-TLCW9fN5kvO/u38/uesdpbx3e8AkTYhMvDZYa9JpmImWuTE99bDQ7GU7hdOADIZsiI9/zuxfAJxny/khknp1Zw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "dependencies": { - "@csstools/css-color-parser": "^3.1.0", - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4", - "@csstools/postcss-progressive-custom-properties": "^4.2.1", - "@csstools/utilities": "^2.0.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-color-hex-alpha": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/postcss-color-hex-alpha/-/postcss-color-hex-alpha-10.0.0.tgz", - "integrity": "sha512-1kervM2cnlgPs2a8Vt/Qbe5cQ++N7rkYo/2rz2BkqJZIHQwaVuJgQH38REHrAi4uM0b1fqxMkWYmese94iMp3w==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT", - "dependencies": { - "@csstools/utilities": "^2.0.0", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-color-rebeccapurple": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/postcss-color-rebeccapurple/-/postcss-color-rebeccapurple-10.0.0.tgz", - "integrity": "sha512-JFta737jSP+hdAIEhk1Vs0q0YF5P8fFcj+09pweS8ktuGuZ8pPlykHsk6mPxZ8awDl4TrcxUqJo9l1IhVr/OjQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "dependencies": { - "@csstools/utilities": "^2.0.0", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-colormin": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-6.1.0.tgz", - "integrity": "sha512-x9yX7DOxeMAR+BgGVnNSAxmAj98NX/YxEMNFP+SDCEeNLb2r3i6Hh1ksMsnW8Ub5SLCpbescQqn9YEbE9554Sw==", - "license": "MIT", - "dependencies": { - "browserslist": "^4.23.0", - "caniuse-api": "^3.0.0", - "colord": "^2.9.3", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-convert-values": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-6.1.0.tgz", - "integrity": "sha512-zx8IwP/ts9WvUM6NkVSkiU902QZL1bwPhaVaLynPtCsOTqp+ZKbNi+s6XJg3rfqpKGA/oc7Oxk5t8pOQJcwl/w==", - "license": "MIT", - "dependencies": { - "browserslist": "^4.23.0", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-custom-media": { - "version": "11.0.6", - "resolved": "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-11.0.6.tgz", - "integrity": "sha512-C4lD4b7mUIw+RZhtY7qUbf4eADmb7Ey8BFA2px9jUbwg7pjTZDl4KY4bvlUV+/vXQvzQRfiGEVJyAbtOsCMInw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT", - "dependencies": { - "@csstools/cascade-layer-name-parser": "^2.0.5", - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4", - "@csstools/media-query-list-parser": "^4.0.3" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-custom-properties": { - "version": "14.0.6", - "resolved": "https://registry.npmjs.org/postcss-custom-properties/-/postcss-custom-properties-14.0.6.tgz", - "integrity": "sha512-fTYSp3xuk4BUeVhxCSJdIPhDLpJfNakZKoiTDx7yRGCdlZrSJR7mWKVOBS4sBF+5poPQFMj2YdXx1VHItBGihQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT", - "dependencies": { - "@csstools/cascade-layer-name-parser": "^2.0.5", - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4", - "@csstools/utilities": "^2.0.0", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-custom-selectors": { - "version": "8.0.5", - "resolved": "https://registry.npmjs.org/postcss-custom-selectors/-/postcss-custom-selectors-8.0.5.tgz", - "integrity": "sha512-9PGmckHQswiB2usSO6XMSswO2yFWVoCAuih1yl9FVcwkscLjRKjwsjM3t+NIWpSU2Jx3eOiK2+t4vVTQaoCHHg==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT", - "dependencies": { - "@csstools/cascade-layer-name-parser": "^2.0.5", - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4", - "postcss-selector-parser": "^7.0.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-custom-selectors/node_modules/postcss-selector-parser": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz", - "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", - "license": "MIT", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/postcss-dir-pseudo-class": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/postcss-dir-pseudo-class/-/postcss-dir-pseudo-class-9.0.1.tgz", - "integrity": "sha512-tRBEK0MHYvcMUrAuYMEOa0zg9APqirBcgzi6P21OhxtJyJADo/SWBwY1CAwEohQ/6HDaa9jCjLRG7K3PVQYHEA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "dependencies": { - "postcss-selector-parser": "^7.0.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-dir-pseudo-class/node_modules/postcss-selector-parser": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz", - "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", - "license": "MIT", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/postcss-discard-comments": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-6.0.2.tgz", - "integrity": "sha512-65w/uIqhSBBfQmYnG92FO1mWZjJ4GL5b8atm5Yw2UgrwD7HiNiSSNwJor1eCFGzUgYnN/iIknhNRVqjrrpuglw==", - "license": "MIT", - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-discard-duplicates": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-6.0.3.tgz", - "integrity": "sha512-+JA0DCvc5XvFAxwx6f/e68gQu/7Z9ud584VLmcgto28eB8FqSFZwtrLwB5Kcp70eIoWP/HXqz4wpo8rD8gpsTw==", - "license": "MIT", - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-discard-empty": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-6.0.3.tgz", - "integrity": "sha512-znyno9cHKQsK6PtxL5D19Fj9uwSzC2mB74cpT66fhgOadEUPyXFkbgwm5tvc3bt3NAy8ltE5MrghxovZRVnOjQ==", - "license": "MIT", - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-discard-overridden": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-6.0.2.tgz", - "integrity": "sha512-j87xzI4LUggC5zND7KdjsI25APtyMuynXZSujByMaav2roV6OZX+8AaCUcZSWqckZpjAjRyFDdpqybgjFO0HJQ==", - "license": "MIT", - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-discard-unused": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/postcss-discard-unused/-/postcss-discard-unused-6.0.5.tgz", - "integrity": "sha512-wHalBlRHkaNnNwfC8z+ppX57VhvS+HWgjW508esjdaEYr3Mx7Gnn2xA4R/CKf5+Z9S5qsqC+Uzh4ueENWwCVUA==", - "license": "MIT", - "dependencies": { - "postcss-selector-parser": "^6.0.16" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-double-position-gradients": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/postcss-double-position-gradients/-/postcss-double-position-gradients-6.0.4.tgz", - "integrity": "sha512-m6IKmxo7FxSP5nF2l63QbCC3r+bWpFUWmZXZf096WxG0m7Vl1Q1+ruFOhpdDRmKrRS+S3Jtk+TVk/7z0+BVK6g==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "dependencies": { - "@csstools/postcss-progressive-custom-properties": "^4.2.1", - "@csstools/utilities": "^2.0.0", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-focus-visible": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/postcss-focus-visible/-/postcss-focus-visible-10.0.1.tgz", - "integrity": "sha512-U58wyjS/I1GZgjRok33aE8juW9qQgQUNwTSdxQGuShHzwuYdcklnvK/+qOWX1Q9kr7ysbraQ6ht6r+udansalA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "dependencies": { - "postcss-selector-parser": "^7.0.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-focus-visible/node_modules/postcss-selector-parser": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz", - "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", - "license": "MIT", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/postcss-focus-within": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/postcss-focus-within/-/postcss-focus-within-9.0.1.tgz", - "integrity": "sha512-fzNUyS1yOYa7mOjpci/bR+u+ESvdar6hk8XNK/TRR0fiGTp2QT5N+ducP0n3rfH/m9I7H/EQU6lsa2BrgxkEjw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "dependencies": { - "postcss-selector-parser": "^7.0.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-focus-within/node_modules/postcss-selector-parser": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz", - "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", - "license": "MIT", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/postcss-font-variant": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-font-variant/-/postcss-font-variant-5.0.0.tgz", - "integrity": "sha512-1fmkBaCALD72CK2a9i468mA/+tr9/1cBxRRMXOUaZqO43oWPR5imcyPjXwuv7PXbCid4ndlP5zWhidQVVa3hmA==", - "license": "MIT", - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-gap-properties": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/postcss-gap-properties/-/postcss-gap-properties-6.0.0.tgz", - "integrity": "sha512-Om0WPjEwiM9Ru+VhfEDPZJAKWUd0mV1HmNXqp2C29z80aQ2uP9UVhLc7e3aYMIor/S5cVhoPgYQ7RtfeZpYTRw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-image-set-function": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/postcss-image-set-function/-/postcss-image-set-function-7.0.0.tgz", - "integrity": "sha512-QL7W7QNlZuzOwBTeXEmbVckNt1FSmhQtbMRvGGqqU4Nf4xk6KUEQhAoWuMzwbSv5jxiRiSZ5Tv7eiDB9U87znA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "dependencies": { - "@csstools/utilities": "^2.0.0", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-lab-function": { - "version": "7.0.12", - "resolved": "https://registry.npmjs.org/postcss-lab-function/-/postcss-lab-function-7.0.12.tgz", - "integrity": "sha512-tUcyRk1ZTPec3OuKFsqtRzW2Go5lehW29XA21lZ65XmzQkz43VY2tyWEC202F7W3mILOjw0voOiuxRGTsN+J9w==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "dependencies": { - "@csstools/css-color-parser": "^3.1.0", - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4", - "@csstools/postcss-progressive-custom-properties": "^4.2.1", - "@csstools/utilities": "^2.0.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-loader": { - "version": "7.3.4", - "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-7.3.4.tgz", - "integrity": "sha512-iW5WTTBSC5BfsBJ9daFMPVrLT36MrNiC6fqOZTTaHjBNX6Pfd5p+hSBqe/fEeNd7pc13QiAyGt7VdGMw4eRC4A==", - "license": "MIT", - "dependencies": { - "cosmiconfig": "^8.3.5", - "jiti": "^1.20.0", - "semver": "^7.5.4" - }, - "engines": { - "node": ">= 14.15.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "postcss": "^7.0.0 || ^8.0.1", - "webpack": "^5.0.0" - } - }, - "node_modules/postcss-logical": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/postcss-logical/-/postcss-logical-8.1.0.tgz", - "integrity": "sha512-pL1hXFQ2fEXNKiNiAgtfA005T9FBxky5zkX6s4GZM2D8RkVgRqz3f4g1JUoq925zXv495qk8UNldDwh8uGEDoA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-merge-idents": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/postcss-merge-idents/-/postcss-merge-idents-6.0.3.tgz", - "integrity": "sha512-1oIoAsODUs6IHQZkLQGO15uGEbK3EAl5wi9SS8hs45VgsxQfMnxvt+L+zIr7ifZFIH14cfAeVe2uCTa+SPRa3g==", - "license": "MIT", - "dependencies": { - "cssnano-utils": "^4.0.2", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-merge-longhand": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-6.0.5.tgz", - "integrity": "sha512-5LOiordeTfi64QhICp07nzzuTDjNSO8g5Ksdibt44d+uvIIAE1oZdRn8y/W5ZtYgRH/lnLDlvi9F8btZcVzu3w==", - "license": "MIT", - "dependencies": { - "postcss-value-parser": "^4.2.0", - "stylehacks": "^6.1.1" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-merge-rules": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-6.1.1.tgz", - "integrity": "sha512-KOdWF0gju31AQPZiD+2Ar9Qjowz1LTChSjFFbS+e2sFgc4uHOp3ZvVX4sNeTlk0w2O31ecFGgrFzhO0RSWbWwQ==", - "license": "MIT", - "dependencies": { - "browserslist": "^4.23.0", - "caniuse-api": "^3.0.0", - "cssnano-utils": "^4.0.2", - "postcss-selector-parser": "^6.0.16" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-minify-font-values": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-6.1.0.tgz", - "integrity": "sha512-gklfI/n+9rTh8nYaSJXlCo3nOKqMNkxuGpTn/Qm0gstL3ywTr9/WRKznE+oy6fvfolH6dF+QM4nCo8yPLdvGJg==", - "license": "MIT", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-minify-gradients": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-6.0.3.tgz", - "integrity": "sha512-4KXAHrYlzF0Rr7uc4VrfwDJ2ajrtNEpNEuLxFgwkhFZ56/7gaE4Nr49nLsQDZyUe+ds+kEhf+YAUolJiYXF8+Q==", - "license": "MIT", - "dependencies": { - "colord": "^2.9.3", - "cssnano-utils": "^4.0.2", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-minify-params": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-6.1.0.tgz", - "integrity": "sha512-bmSKnDtyyE8ujHQK0RQJDIKhQ20Jq1LYiez54WiaOoBtcSuflfK3Nm596LvbtlFcpipMjgClQGyGr7GAs+H1uA==", - "license": "MIT", - "dependencies": { - "browserslist": "^4.23.0", - "cssnano-utils": "^4.0.2", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-minify-selectors": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-6.0.4.tgz", - "integrity": "sha512-L8dZSwNLgK7pjTto9PzWRoMbnLq5vsZSTu8+j1P/2GB8qdtGQfn+K1uSvFgYvgh83cbyxT5m43ZZhUMTJDSClQ==", - "license": "MIT", - "dependencies": { - "postcss-selector-parser": "^6.0.16" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-modules-extract-imports": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.1.0.tgz", - "integrity": "sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q==", - "license": "ISC", - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-modules-local-by-default": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.2.0.tgz", - "integrity": "sha512-5kcJm/zk+GJDSfw+V/42fJ5fhjL5YbFDl8nVdXkJPLLW+Vf9mTD5Xe0wqIaDnLuL2U6cDNpTr+UQ+v2HWIBhzw==", - "license": "MIT", - "dependencies": { - "icss-utils": "^5.0.0", - "postcss-selector-parser": "^7.0.0", - "postcss-value-parser": "^4.1.0" - }, - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-modules-local-by-default/node_modules/postcss-selector-parser": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz", - "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", - "license": "MIT", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/postcss-modules-scope": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.2.1.tgz", - "integrity": "sha512-m9jZstCVaqGjTAuny8MdgE88scJnCiQSlSrOWcTQgM2t32UBe+MUmFSO5t7VMSfAf/FJKImAxBav8ooCHJXCJA==", - "license": "ISC", - "dependencies": { - "postcss-selector-parser": "^7.0.0" - }, - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-modules-scope/node_modules/postcss-selector-parser": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz", - "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", - "license": "MIT", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/postcss-modules-values": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", - "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", - "license": "ISC", - "dependencies": { - "icss-utils": "^5.0.0" - }, - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-nesting": { - "version": "13.0.2", - "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-13.0.2.tgz", - "integrity": "sha512-1YCI290TX+VP0U/K/aFxzHzQWHWURL+CtHMSbex1lCdpXD1SoR2sYuxDu5aNI9lPoXpKTCggFZiDJbwylU0LEQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "dependencies": { - "@csstools/selector-resolve-nested": "^3.1.0", - "@csstools/selector-specificity": "^5.0.0", - "postcss-selector-parser": "^7.0.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-nesting/node_modules/@csstools/selector-resolve-nested": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@csstools/selector-resolve-nested/-/selector-resolve-nested-3.1.0.tgz", - "integrity": "sha512-mf1LEW0tJLKfWyvn5KdDrhpxHyuxpbNwTIwOYLIvsTffeyOf85j5oIzfG0yosxDgx/sswlqBnESYUcQH0vgZ0g==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss-selector-parser": "^7.0.0" - } - }, - "node_modules/postcss-nesting/node_modules/@csstools/selector-specificity": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-5.0.0.tgz", - "integrity": "sha512-PCqQV3c4CoVm3kdPhyeZ07VmBRdH2EpMFA/pd9OASpOEC3aXNGoqPDAZ80D0cLpMBxnmk0+yNhGsEx31hq7Gtw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss-selector-parser": "^7.0.0" - } - }, - "node_modules/postcss-nesting/node_modules/postcss-selector-parser": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz", - "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", - "license": "MIT", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/postcss-normalize-charset": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-6.0.2.tgz", - "integrity": "sha512-a8N9czmdnrjPHa3DeFlwqst5eaL5W8jYu3EBbTTkI5FHkfMhFZh1EGbku6jhHhIzTA6tquI2P42NtZ59M/H/kQ==", - "license": "MIT", - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-normalize-display-values": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-6.0.2.tgz", - "integrity": "sha512-8H04Mxsb82ON/aAkPeq8kcBbAtI5Q2a64X/mnRRfPXBq7XeogoQvReqxEfc0B4WPq1KimjezNC8flUtC3Qz6jg==", - "license": "MIT", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-normalize-positions": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-6.0.2.tgz", - "integrity": "sha512-/JFzI441OAB9O7VnLA+RtSNZvQ0NCFZDOtp6QPFo1iIyawyXg0YI3CYM9HBy1WvwCRHnPep/BvI1+dGPKoXx/Q==", - "license": "MIT", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-normalize-repeat-style": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-6.0.2.tgz", - "integrity": "sha512-YdCgsfHkJ2jEXwR4RR3Tm/iOxSfdRt7jplS6XRh9Js9PyCR/aka/FCb6TuHT2U8gQubbm/mPmF6L7FY9d79VwQ==", - "license": "MIT", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-normalize-string": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-6.0.2.tgz", - "integrity": "sha512-vQZIivlxlfqqMp4L9PZsFE4YUkWniziKjQWUtsxUiVsSSPelQydwS8Wwcuw0+83ZjPWNTl02oxlIvXsmmG+CiQ==", - "license": "MIT", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-normalize-timing-functions": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-6.0.2.tgz", - "integrity": "sha512-a+YrtMox4TBtId/AEwbA03VcJgtyW4dGBizPl7e88cTFULYsprgHWTbfyjSLyHeBcK/Q9JhXkt2ZXiwaVHoMzA==", - "license": "MIT", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-normalize-unicode": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-6.1.0.tgz", - "integrity": "sha512-QVC5TQHsVj33otj8/JD869Ndr5Xcc/+fwRh4HAsFsAeygQQXm+0PySrKbr/8tkDKzW+EVT3QkqZMfFrGiossDg==", - "license": "MIT", - "dependencies": { - "browserslist": "^4.23.0", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-normalize-url": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-6.0.2.tgz", - "integrity": "sha512-kVNcWhCeKAzZ8B4pv/DnrU1wNh458zBNp8dh4y5hhxih5RZQ12QWMuQrDgPRw3LRl8mN9vOVfHl7uhvHYMoXsQ==", - "license": "MIT", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-normalize-whitespace": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-6.0.2.tgz", - "integrity": "sha512-sXZ2Nj1icbJOKmdjXVT9pnyHQKiSAyuNQHSgRCUgThn2388Y9cGVDR+E9J9iAYbSbLHI+UUwLVl1Wzco/zgv0Q==", - "license": "MIT", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-opacity-percentage": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/postcss-opacity-percentage/-/postcss-opacity-percentage-3.0.0.tgz", - "integrity": "sha512-K6HGVzyxUxd/VgZdX04DCtdwWJ4NGLG212US4/LA1TLAbHgmAsTWVR86o+gGIbFtnTkfOpb9sCRBx8K7HO66qQ==", - "funding": [ - { - "type": "kofi", - "url": "https://ko-fi.com/mrcgrtz" - }, - { - "type": "liberapay", - "url": "https://liberapay.com/mrcgrtz" - } - ], - "license": "MIT", - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-ordered-values": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-6.0.2.tgz", - "integrity": "sha512-VRZSOB+JU32RsEAQrO94QPkClGPKJEL/Z9PCBImXMhIeK5KAYo6slP/hBYlLgrCjFxyqvn5VC81tycFEDBLG1Q==", - "license": "MIT", - "dependencies": { - "cssnano-utils": "^4.0.2", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-overflow-shorthand": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/postcss-overflow-shorthand/-/postcss-overflow-shorthand-6.0.0.tgz", - "integrity": "sha512-BdDl/AbVkDjoTofzDQnwDdm/Ym6oS9KgmO7Gr+LHYjNWJ6ExORe4+3pcLQsLA9gIROMkiGVjjwZNoL/mpXHd5Q==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-page-break": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/postcss-page-break/-/postcss-page-break-3.0.4.tgz", - "integrity": "sha512-1JGu8oCjVXLa9q9rFTo4MbeeA5FMe00/9C7lN4va606Rdb+HkxXtXsmEDrIraQ11fGz/WvKWa8gMuCKkrXpTsQ==", - "license": "MIT", - "peerDependencies": { - "postcss": "^8" - } - }, - "node_modules/postcss-place": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/postcss-place/-/postcss-place-10.0.0.tgz", - "integrity": "sha512-5EBrMzat2pPAxQNWYavwAfoKfYcTADJ8AXGVPcUZ2UkNloUTWzJQExgrzrDkh3EKzmAx1evfTAzF9I8NGcc+qw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-preset-env": { - "version": "10.6.1", - "resolved": "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-10.6.1.tgz", - "integrity": "sha512-yrk74d9EvY+W7+lO9Aj1QmjWY9q5NsKjK2V9drkOPZB/X6KZ0B3igKsHUYakb7oYVhnioWypQX3xGuePf89f3g==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "dependencies": { - "@csstools/postcss-alpha-function": "^1.0.1", - "@csstools/postcss-cascade-layers": "^5.0.2", - "@csstools/postcss-color-function": "^4.0.12", - "@csstools/postcss-color-function-display-p3-linear": "^1.0.1", - "@csstools/postcss-color-mix-function": "^3.0.12", - "@csstools/postcss-color-mix-variadic-function-arguments": "^1.0.2", - "@csstools/postcss-content-alt-text": "^2.0.8", - "@csstools/postcss-contrast-color-function": "^2.0.12", - "@csstools/postcss-exponential-functions": "^2.0.9", - "@csstools/postcss-font-format-keywords": "^4.0.0", - "@csstools/postcss-gamut-mapping": "^2.0.11", - "@csstools/postcss-gradients-interpolation-method": "^5.0.12", - "@csstools/postcss-hwb-function": "^4.0.12", - "@csstools/postcss-ic-unit": "^4.0.4", - "@csstools/postcss-initial": "^2.0.1", - "@csstools/postcss-is-pseudo-class": "^5.0.3", - "@csstools/postcss-light-dark-function": "^2.0.11", - "@csstools/postcss-logical-float-and-clear": "^3.0.0", - "@csstools/postcss-logical-overflow": "^2.0.0", - "@csstools/postcss-logical-overscroll-behavior": "^2.0.0", - "@csstools/postcss-logical-resize": "^3.0.0", - "@csstools/postcss-logical-viewport-units": "^3.0.4", - "@csstools/postcss-media-minmax": "^2.0.9", - "@csstools/postcss-media-queries-aspect-ratio-number-values": "^3.0.5", - "@csstools/postcss-nested-calc": "^4.0.0", - "@csstools/postcss-normalize-display-values": "^4.0.1", - "@csstools/postcss-oklab-function": "^4.0.12", - "@csstools/postcss-position-area-property": "^1.0.0", - "@csstools/postcss-progressive-custom-properties": "^4.2.1", - "@csstools/postcss-property-rule-prelude-list": "^1.0.0", - "@csstools/postcss-random-function": "^2.0.1", - "@csstools/postcss-relative-color-syntax": "^3.0.12", - "@csstools/postcss-scope-pseudo-class": "^4.0.1", - "@csstools/postcss-sign-functions": "^1.1.4", - "@csstools/postcss-stepped-value-functions": "^4.0.9", - "@csstools/postcss-syntax-descriptor-syntax-production": "^1.0.1", - "@csstools/postcss-system-ui-font-family": "^1.0.0", - "@csstools/postcss-text-decoration-shorthand": "^4.0.3", - "@csstools/postcss-trigonometric-functions": "^4.0.9", - "@csstools/postcss-unset-value": "^4.0.0", - "autoprefixer": "^10.4.23", - "browserslist": "^4.28.1", - "css-blank-pseudo": "^7.0.1", - "css-has-pseudo": "^7.0.3", - "css-prefers-color-scheme": "^10.0.0", - "cssdb": "^8.6.0", - "postcss-attribute-case-insensitive": "^7.0.1", - "postcss-clamp": "^4.1.0", - "postcss-color-functional-notation": "^7.0.12", - "postcss-color-hex-alpha": "^10.0.0", - "postcss-color-rebeccapurple": "^10.0.0", - "postcss-custom-media": "^11.0.6", - "postcss-custom-properties": "^14.0.6", - "postcss-custom-selectors": "^8.0.5", - "postcss-dir-pseudo-class": "^9.0.1", - "postcss-double-position-gradients": "^6.0.4", - "postcss-focus-visible": "^10.0.1", - "postcss-focus-within": "^9.0.1", - "postcss-font-variant": "^5.0.0", - "postcss-gap-properties": "^6.0.0", - "postcss-image-set-function": "^7.0.0", - "postcss-lab-function": "^7.0.12", - "postcss-logical": "^8.1.0", - "postcss-nesting": "^13.0.2", - "postcss-opacity-percentage": "^3.0.0", - "postcss-overflow-shorthand": "^6.0.0", - "postcss-page-break": "^3.0.4", - "postcss-place": "^10.0.0", - "postcss-pseudo-class-any-link": "^10.0.1", - "postcss-replace-overflow-wrap": "^4.0.0", - "postcss-selector-not": "^8.0.1" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-pseudo-class-any-link": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/postcss-pseudo-class-any-link/-/postcss-pseudo-class-any-link-10.0.1.tgz", - "integrity": "sha512-3el9rXlBOqTFaMFkWDOkHUTQekFIYnaQY55Rsp8As8QQkpiSgIYEcF/6Ond93oHiDsGb4kad8zjt+NPlOC1H0Q==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "dependencies": { - "postcss-selector-parser": "^7.0.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-pseudo-class-any-link/node_modules/postcss-selector-parser": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz", - "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", - "license": "MIT", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/postcss-reduce-idents": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/postcss-reduce-idents/-/postcss-reduce-idents-6.0.3.tgz", - "integrity": "sha512-G3yCqZDpsNPoQgbDUy3T0E6hqOQ5xigUtBQyrmq3tn2GxlyiL0yyl7H+T8ulQR6kOcHJ9t7/9H4/R2tv8tJbMA==", - "license": "MIT", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-reduce-initial": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-6.1.0.tgz", - "integrity": "sha512-RarLgBK/CrL1qZags04oKbVbrrVK2wcxhvta3GCxrZO4zveibqbRPmm2VI8sSgCXwoUHEliRSbOfpR0b/VIoiw==", - "license": "MIT", - "dependencies": { - "browserslist": "^4.23.0", - "caniuse-api": "^3.0.0" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-reduce-transforms": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-6.0.2.tgz", - "integrity": "sha512-sB+Ya++3Xj1WaT9+5LOOdirAxP7dJZms3GRcYheSPi1PiTMigsxHAdkrbItHxwYHr4kt1zL7mmcHstgMYT+aiA==", - "license": "MIT", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-replace-overflow-wrap": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/postcss-replace-overflow-wrap/-/postcss-replace-overflow-wrap-4.0.0.tgz", - "integrity": "sha512-KmF7SBPphT4gPPcKZc7aDkweHiKEEO8cla/GjcBK+ckKxiZslIu3C4GCRW3DNfL0o7yW7kMQu9xlZ1kXRXLXtw==", - "license": "MIT", - "peerDependencies": { - "postcss": "^8.0.3" - } - }, - "node_modules/postcss-selector-not": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/postcss-selector-not/-/postcss-selector-not-8.0.1.tgz", - "integrity": "sha512-kmVy/5PYVb2UOhy0+LqUYAhKj7DUGDpSWa5LZqlkWJaaAV+dxxsOG3+St0yNLu6vsKD7Dmqx+nWQt0iil89+WA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT", - "dependencies": { - "postcss-selector-parser": "^7.0.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-selector-not/node_modules/postcss-selector-parser": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz", - "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", - "license": "MIT", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/postcss-selector-parser": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", - "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", - "license": "MIT", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/postcss-sort-media-queries": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/postcss-sort-media-queries/-/postcss-sort-media-queries-5.2.0.tgz", - "integrity": "sha512-AZ5fDMLD8SldlAYlvi8NIqo0+Z8xnXU2ia0jxmuhxAU+Lqt9K+AlmLNJ/zWEnE9x+Zx3qL3+1K20ATgNOr3fAA==", - "license": "MIT", - "dependencies": { - "sort-css-media-queries": "2.2.0" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "postcss": "^8.4.23" - } - }, - "node_modules/postcss-svgo": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-6.0.3.tgz", - "integrity": "sha512-dlrahRmxP22bX6iKEjOM+c8/1p+81asjKT+V5lrgOH944ryx/OHpclnIbGsKVd3uWOXFLYJwCVf0eEkJGvO96g==", - "license": "MIT", - "dependencies": { - "postcss-value-parser": "^4.2.0", - "svgo": "^3.2.0" - }, - "engines": { - "node": "^14 || ^16 || >= 18" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-unique-selectors": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-6.0.4.tgz", - "integrity": "sha512-K38OCaIrO8+PzpArzkLKB42dSARtC2tmG6PvD4b1o1Q2E9Os8jzfWFfSy/rixsHwohtsDdFtAWGjFVFUdwYaMg==", - "license": "MIT", - "dependencies": { - "postcss-selector-parser": "^6.0.16" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-value-parser": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", - "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", - "license": "MIT" - }, - "node_modules/postcss-zindex": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/postcss-zindex/-/postcss-zindex-6.0.2.tgz", - "integrity": "sha512-5BxW9l1evPB/4ZIc+2GobEBoKC+h8gPGCMi+jxsYvd2x0mjq7wazk6DrP71pStqxE9Foxh5TVnonbWpFZzXaYg==", - "license": "MIT", - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/pretty-error": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-4.0.0.tgz", - "integrity": "sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw==", - "license": "MIT", - "dependencies": { - "lodash": "^4.17.20", - "renderkid": "^3.0.0" - } - }, - "node_modules/pretty-time": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/pretty-time/-/pretty-time-1.1.0.tgz", - "integrity": "sha512-28iF6xPQrP8Oa6uxE6a1biz+lWeTOAPKggvjB8HAs6nVMKZwf5bG++632Dx614hIWgUPkgivRfG+a8uAXGTIbA==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/prism-react-renderer": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/prism-react-renderer/-/prism-react-renderer-2.4.1.tgz", - "integrity": "sha512-ey8Ls/+Di31eqzUxC46h8MksNuGx/n0AAC8uKpwFau4RPDYLuE3EXTp8N8G2vX2N7UC/+IXeNUnlWBGGcAG+Ig==", - "license": "MIT", - "dependencies": { - "@types/prismjs": "^1.26.0", - "clsx": "^2.0.0" - }, - "peerDependencies": { - "react": ">=16.0.0" - } - }, - "node_modules/prismjs": { - "version": "1.30.0", - "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz", - "integrity": "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "license": "MIT" - }, - "node_modules/prompts": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", - "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", - "license": "MIT", - "dependencies": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/prop-types": { - "version": "15.8.1", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", - "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "license": "MIT", - "dependencies": { - "loose-envify": "^1.4.0", - "object-assign": "^4.1.1", - "react-is": "^16.13.1" - } - }, - "node_modules/property-information": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.1.0.tgz", - "integrity": "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/proto-list": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", - "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==", - "license": "ISC" - }, - "node_modules/proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "license": "MIT", - "dependencies": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/proxy-addr/node_modules/ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "license": "MIT", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/pupa": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/pupa/-/pupa-3.3.0.tgz", - "integrity": "sha512-LjgDO2zPtoXP2wJpDjZrGdojii1uqO0cnwKoIoUzkfS98HDmbeiGmYiXo3lXeFlq2xvne1QFQhwYXSUCLKtEuA==", - "license": "MIT", - "dependencies": { - "escape-goat": "^4.0.0" - }, - "engines": { - "node": ">=12.20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/pvtsutils": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/pvtsutils/-/pvtsutils-1.3.6.tgz", - "integrity": "sha512-PLgQXQ6H2FWCaeRak8vvk1GW462lMxB5s3Jm673N82zI4vqtVUPuZdffdZbPDFRoU8kAhItWFtPCWiPpp4/EDg==", - "license": "MIT", - "dependencies": { - "tslib": "^2.8.1" - } - }, - "node_modules/pvutils": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/pvutils/-/pvutils-1.1.5.tgz", - "integrity": "sha512-KTqnxsgGiQ6ZAzZCVlJH5eOjSnvlyEgx1m8bkRJfOhmGRqfo5KLvmAlACQkrjEtOQ4B7wF9TdSLIs9O90MX9xA==", - "license": "MIT", - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/qs": { - "version": "6.14.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz", - "integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==", - "license": "BSD-3-Clause", - "dependencies": { - "side-channel": "^1.1.0" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/quick-lru": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", - "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "license": "MIT", - "dependencies": { - "safe-buffer": "^5.1.0" - } - }, - "node_modules/range-parser": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", - "integrity": "sha512-kA5WQoNVo4t9lNx2kQNFCxKeBl5IbbSNBl1M/tLkw9WCn+hxNBAW5Qh8gdhs63CJnhjJ2zQWFoqPJP2sK1AV5A==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/raw-body": { - "version": "2.5.3", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz", - "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==", - "license": "MIT", - "dependencies": { - "bytes": "~3.1.2", - "http-errors": "~2.0.1", - "iconv-lite": "~0.4.24", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/raw-body/node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", - "dependencies": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "bin": { - "rc": "cli.js" - } - }, - "node_modules/rc/node_modules/ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "license": "ISC" - }, - "node_modules/rc/node_modules/strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react": { - "version": "19.2.4", - "resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz", - "integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-dom": { - "version": "19.2.4", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz", - "integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==", - "license": "MIT", - "dependencies": { - "scheduler": "^0.27.0" - }, - "peerDependencies": { - "react": "^19.2.4" - } - }, - "node_modules/react-fast-compare": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.2.tgz", - "integrity": "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==", - "license": "MIT" - }, - "node_modules/react-helmet-async": { - "name": "@slorber/react-helmet-async", - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@slorber/react-helmet-async/-/react-helmet-async-1.3.0.tgz", - "integrity": "sha512-e9/OK8VhwUSc67diWI8Rb3I0YgI9/SBQtnhe9aEuK6MhZm7ntZZimXgwXnd8W96YTmSOb9M4d8LwhRZyhWr/1A==", - "license": "Apache-2.0", - "dependencies": { - "@babel/runtime": "^7.12.5", - "invariant": "^2.2.4", - "prop-types": "^15.7.2", - "react-fast-compare": "^3.2.0", - "shallowequal": "^1.1.0" - }, - "peerDependencies": { - "react": "^16.6.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", - "react-dom": "^16.6.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" - } - }, - "node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "license": "MIT" - }, - "node_modules/react-json-view-lite": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/react-json-view-lite/-/react-json-view-lite-2.5.0.tgz", - "integrity": "sha512-tk7o7QG9oYyELWHL8xiMQ8x4WzjCzbWNyig3uexmkLb54r8jO0yH3WCWx8UZS0c49eSA4QUmG5caiRJ8fAn58g==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "react": "^18.0.0 || ^19.0.0" - } - }, - "node_modules/react-loadable": { - "name": "@docusaurus/react-loadable", - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/@docusaurus/react-loadable/-/react-loadable-6.0.0.tgz", - "integrity": "sha512-YMMxTUQV/QFSnbgrP3tjDzLHRg7vsbMn8e9HAa8o/1iXoiomo48b7sk/kkmWEuWNDPJVlKSJRB6Y2fHqdJk+SQ==", - "license": "MIT", - "dependencies": { - "@types/react": "*" - }, - "peerDependencies": { - "react": "*" - } - }, - "node_modules/react-loadable-ssr-addon-v5-slorber": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/react-loadable-ssr-addon-v5-slorber/-/react-loadable-ssr-addon-v5-slorber-1.0.3.tgz", - "integrity": "sha512-GXfh9VLwB5ERaCsU6RULh7tkemeX15aNh6wuMEBtfdyMa7fFG8TXrhXlx1SoEK2Ty/l6XIkzzYIQmyaWW3JgdQ==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.10.3" - }, - "engines": { - "node": ">=10.13.0" - }, - "peerDependencies": { - "react-loadable": "*", - "webpack": ">=4.41.1 || 5.x" - } - }, - "node_modules/react-router": { - "version": "5.3.4", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-5.3.4.tgz", - "integrity": "sha512-Ys9K+ppnJah3QuaRiLxk+jDWOR1MekYQrlytiXxC1RyfbdsZkS5pvKAzCCr031xHixZwpnsYNT5xysdFHQaYsA==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.12.13", - "history": "^4.9.0", - "hoist-non-react-statics": "^3.1.0", - "loose-envify": "^1.3.1", - "path-to-regexp": "^1.7.0", - "prop-types": "^15.6.2", - "react-is": "^16.6.0", - "tiny-invariant": "^1.0.2", - "tiny-warning": "^1.0.0" - }, - "peerDependencies": { - "react": ">=15" - } - }, - "node_modules/react-router-config": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/react-router-config/-/react-router-config-5.1.1.tgz", - "integrity": "sha512-DuanZjaD8mQp1ppHjgnnUnyOlqYXZVjnov/JzFhjLEwd3Z4dYjMSnqrEzzGThH47vpCOqPPwJM2FtthLeJ8Pbg==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.1.2" - }, - "peerDependencies": { - "react": ">=15", - "react-router": ">=5" - } - }, - "node_modules/react-router-dom": { - "version": "5.3.4", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-5.3.4.tgz", - "integrity": "sha512-m4EqFMHv/Ih4kpcBCONHbkT68KoAeHN4p3lAGoNryfHi0dMy0kCzEZakiKRsvg5wHZ/JLrLW8o8KomWiz/qbYQ==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.12.13", - "history": "^4.9.0", - "loose-envify": "^1.3.1", - "prop-types": "^15.6.2", - "react-router": "5.3.4", - "tiny-invariant": "^1.0.2", - "tiny-warning": "^1.0.0" - }, - "peerDependencies": { - "react": ">=15" - } - }, - "node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "license": "MIT", - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/recma-build-jsx": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/recma-build-jsx/-/recma-build-jsx-1.0.0.tgz", - "integrity": "sha512-8GtdyqaBcDfva+GUKDr3nev3VpKAhup1+RvkMvUxURHpW7QyIvk9F5wz7Vzo06CEMSilw6uArgRqhpiUcWp8ew==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "estree-util-build-jsx": "^3.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/recma-jsx": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/recma-jsx/-/recma-jsx-1.0.1.tgz", - "integrity": "sha512-huSIy7VU2Z5OLv6oFLosQGGDqPqdO1iq6bWNAdhzMxSJP7RAso4fCZ1cKu8j9YHCZf3TPrq4dw3okhrylgcd7w==", - "license": "MIT", - "dependencies": { - "acorn-jsx": "^5.0.0", - "estree-util-to-js": "^2.0.0", - "recma-parse": "^1.0.0", - "recma-stringify": "^1.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - }, - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/recma-parse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/recma-parse/-/recma-parse-1.0.0.tgz", - "integrity": "sha512-OYLsIGBB5Y5wjnSnQW6t3Xg7q3fQ7FWbw/vcXtORTnyaSFscOtABg+7Pnz6YZ6c27fG1/aN8CjfwoUEUIdwqWQ==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "esast-util-from-js": "^2.0.0", - "unified": "^11.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/recma-stringify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/recma-stringify/-/recma-stringify-1.0.0.tgz", - "integrity": "sha512-cjwII1MdIIVloKvC9ErQ+OgAtwHBmcZ0Bg4ciz78FtbT8In39aAYbaA7zvxQ61xVMSPE8WxhLwLbhif4Js2C+g==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "estree-util-to-js": "^2.0.0", - "unified": "^11.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/reflect-metadata": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz", - "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==", - "license": "Apache-2.0" - }, - "node_modules/regenerate": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", - "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", - "license": "MIT" - }, - "node_modules/regenerate-unicode-properties": { - "version": "10.2.2", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.2.tgz", - "integrity": "sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g==", - "license": "MIT", - "dependencies": { - "regenerate": "^1.4.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/regexpu-core": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.4.0.tgz", - "integrity": "sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA==", - "license": "MIT", - "dependencies": { - "regenerate": "^1.4.2", - "regenerate-unicode-properties": "^10.2.2", - "regjsgen": "^0.8.0", - "regjsparser": "^0.13.0", - "unicode-match-property-ecmascript": "^2.0.0", - "unicode-match-property-value-ecmascript": "^2.2.1" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/registry-auth-token": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-5.1.1.tgz", - "integrity": "sha512-P7B4+jq8DeD2nMsAcdfaqHbssgHtZ7Z5+++a5ask90fvmJ8p5je4mOa+wzu+DB4vQ5tdJV/xywY+UnVFeQLV5Q==", - "license": "MIT", - "dependencies": { - "@pnpm/npm-conf": "^3.0.2" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/registry-url": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-6.0.1.tgz", - "integrity": "sha512-+crtS5QjFRqFCoQmvGduwYWEBng99ZvmFvF+cUJkGYF1L1BfU8C6Zp9T7f5vPAwyLkUExpvK+ANVZmGU49qi4Q==", - "license": "MIT", - "dependencies": { - "rc": "1.2.8" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/regjsgen": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.8.0.tgz", - "integrity": "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==", - "license": "MIT" - }, - "node_modules/regjsparser": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.13.0.tgz", - "integrity": "sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q==", - "license": "BSD-2-Clause", - "dependencies": { - "jsesc": "~3.1.0" - }, - "bin": { - "regjsparser": "bin/parser" - } - }, - "node_modules/rehype-raw": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/rehype-raw/-/rehype-raw-7.0.0.tgz", - "integrity": "sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "hast-util-raw": "^9.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/rehype-recma": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/rehype-recma/-/rehype-recma-1.0.0.tgz", - "integrity": "sha512-lqA4rGUf1JmacCNWWZx0Wv1dHqMwxzsDWYMTowuplHF3xH0N/MmrZ/G3BDZnzAkRmxDadujCjaKM2hqYdCBOGw==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "@types/hast": "^3.0.0", - "hast-util-to-estree": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/relateurl": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", - "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==", - "license": "MIT", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/remark-directive": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/remark-directive/-/remark-directive-3.0.1.tgz", - "integrity": "sha512-gwglrEQEZcZYgVyG1tQuA+h58EZfq5CSULw7J90AFuCTyib1thgHPoqQ+h9iFvU6R+vnZ5oNFQR5QKgGpk741A==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-directive": "^3.0.0", - "micromark-extension-directive": "^3.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-emoji": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/remark-emoji/-/remark-emoji-4.0.1.tgz", - "integrity": "sha512-fHdvsTR1dHkWKev9eNyhTo4EFwbUvJ8ka9SgeWkMPYFX4WoI7ViVBms3PjlQYgw5TLvNQso3GUB/b/8t3yo+dg==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.2", - "emoticon": "^4.0.1", - "mdast-util-find-and-replace": "^3.0.1", - "node-emoji": "^2.1.0", - "unified": "^11.0.4" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - } - }, - "node_modules/remark-frontmatter": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/remark-frontmatter/-/remark-frontmatter-5.0.0.tgz", - "integrity": "sha512-XTFYvNASMe5iPN0719nPrdItC9aU0ssC4v14mH1BCi1u0n1gAocqcujWUrByftZTbLhRtiKRyjYTSIOcr69UVQ==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-frontmatter": "^2.0.0", - "micromark-extension-frontmatter": "^2.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-gfm": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.1.tgz", - "integrity": "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-gfm": "^3.0.0", - "micromark-extension-gfm": "^3.0.0", - "remark-parse": "^11.0.0", - "remark-stringify": "^11.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-mdx": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/remark-mdx/-/remark-mdx-3.1.1.tgz", - "integrity": "sha512-Pjj2IYlUY3+D8x00UJsIOg5BEvfMyeI+2uLPn9VO9Wg4MEtN/VTIq2NEJQfde9PnX15KgtHyl9S0BcTnWrIuWg==", - "license": "MIT", - "dependencies": { - "mdast-util-mdx": "^3.0.0", - "micromark-extension-mdxjs": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-parse": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz", - "integrity": "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-from-markdown": "^2.0.0", - "micromark-util-types": "^2.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-rehype": { - "version": "11.1.2", - "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-11.1.2.tgz", - "integrity": "sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "mdast-util-to-hast": "^13.0.0", - "unified": "^11.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-stringify": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-11.0.0.tgz", - "integrity": "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-to-markdown": "^2.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/renderkid": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-3.0.0.tgz", - "integrity": "sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg==", - "license": "MIT", - "dependencies": { - "css-select": "^4.1.3", - "dom-converter": "^0.2.0", - "htmlparser2": "^6.1.0", - "lodash": "^4.17.21", - "strip-ansi": "^6.0.1" - } - }, - "node_modules/renderkid/node_modules/css-select": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", - "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", - "license": "BSD-2-Clause", - "dependencies": { - "boolbase": "^1.0.0", - "css-what": "^6.0.1", - "domhandler": "^4.3.1", - "domutils": "^2.8.0", - "nth-check": "^2.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/renderkid/node_modules/dom-serializer": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", - "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", - "license": "MIT", - "dependencies": { - "domelementtype": "^2.0.1", - "domhandler": "^4.2.0", - "entities": "^2.0.0" - }, - "funding": { - "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" - } - }, - "node_modules/renderkid/node_modules/domhandler": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", - "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", - "license": "BSD-2-Clause", - "dependencies": { - "domelementtype": "^2.2.0" - }, - "engines": { - "node": ">= 4" - }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" - } - }, - "node_modules/renderkid/node_modules/domutils": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", - "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", - "license": "BSD-2-Clause", - "dependencies": { - "dom-serializer": "^1.0.1", - "domelementtype": "^2.2.0", - "domhandler": "^4.2.0" - }, - "funding": { - "url": "https://github.com/fb55/domutils?sponsor=1" - } - }, - "node_modules/renderkid/node_modules/entities": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", - "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", - "license": "BSD-2-Clause", - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/renderkid/node_modules/htmlparser2": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", - "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==", - "funding": [ - "https://github.com/fb55/htmlparser2?sponsor=1", - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], - "license": "MIT", - "dependencies": { - "domelementtype": "^2.0.1", - "domhandler": "^4.0.0", - "domutils": "^2.5.2", - "entities": "^2.0.0" - } - }, - "node_modules/repeat-string": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==", - "license": "MIT", - "engines": { - "node": ">=0.10" - } - }, - "node_modules/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/require-like": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/require-like/-/require-like-0.1.2.tgz", - "integrity": "sha512-oyrU88skkMtDdauHDuKVrgR+zuItqr6/c//FXzvmxRGMexSDc6hNvJInGW3LL46n+8b50RykrvwSUIIQH2LQ5A==", - "engines": { - "node": "*" - } - }, - "node_modules/requires-port": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", - "license": "MIT" - }, - "node_modules/resolve": { - "version": "1.22.11", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", - "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", - "license": "MIT", - "dependencies": { - "is-core-module": "^2.16.1", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-alpn": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", - "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==", - "license": "MIT" - }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/resolve-pathname": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-3.0.0.tgz", - "integrity": "sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng==", - "license": "MIT" - }, - "node_modules/responselike": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/responselike/-/responselike-3.0.0.tgz", - "integrity": "sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg==", - "license": "MIT", - "dependencies": { - "lowercase-keys": "^3.0.0" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/retry": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", - "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/reusify": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", - "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", - "license": "MIT", - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/rtlcss": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/rtlcss/-/rtlcss-4.3.0.tgz", - "integrity": "sha512-FI+pHEn7Wc4NqKXMXFM+VAYKEj/mRIcW4h24YVwVtyjI+EqGrLc2Hx/Ny0lrZ21cBWU2goLy36eqMcNj3AQJig==", - "license": "MIT", - "dependencies": { - "escalade": "^3.1.1", - "picocolors": "^1.0.0", - "postcss": "^8.4.21", - "strip-json-comments": "^3.1.1" - }, - "bin": { - "rtlcss": "bin/rtlcss.js" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/run-applescript": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.1.0.tgz", - "integrity": "sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "license": "MIT" - }, - "node_modules/sax": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.6.0.tgz", - "integrity": "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==", - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=11.0.0" - } - }, - "node_modules/scheduler": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", - "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", - "license": "MIT" - }, - "node_modules/schema-dts": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/schema-dts/-/schema-dts-1.1.5.tgz", - "integrity": "sha512-RJr9EaCmsLzBX2NDiO5Z3ux2BVosNZN5jo0gWgsyKvxKIUL5R3swNvoorulAeL9kLB0iTSX7V6aokhla2m7xbg==", - "license": "Apache-2.0" - }, - "node_modules/schema-utils": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz", - "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==", - "license": "MIT", - "dependencies": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.9.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.1.0" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/search-insights": { - "version": "2.17.3", - "resolved": "https://registry.npmjs.org/search-insights/-/search-insights-2.17.3.tgz", - "integrity": "sha512-RQPdCYTa8A68uM2jwxoY842xDhvx3E5LFL1LxvxCNMev4o5mLuokczhzjAgGwUZBAmOKZknArSxLKmXtIi2AxQ==", - "license": "MIT", - "peer": true - }, - "node_modules/section-matter": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/section-matter/-/section-matter-1.0.0.tgz", - "integrity": "sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==", - "license": "MIT", - "dependencies": { - "extend-shallow": "^2.0.1", - "kind-of": "^6.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/select-hose": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", - "integrity": "sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==", - "license": "MIT" - }, - "node_modules/selfsigned": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-5.5.0.tgz", - "integrity": "sha512-ftnu3TW4+3eBfLRFnDEkzGxSF/10BJBkaLJuBHZX0kiPS7bRdlpZGu6YGt4KngMkdTwJE6MbjavFpqHvqVt+Ew==", - "license": "MIT", - "dependencies": { - "@peculiar/x509": "^1.14.2", - "pkijs": "^3.3.3" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/semver": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", - "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/semver-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-4.0.0.tgz", - "integrity": "sha512-0Ju4+6A8iOnpL/Thra7dZsSlOHYAHIeMxfhWQRI1/VLcT3WDBZKKtQt/QkBOsiIN9ZpuvHE6cGZ0x4glCMmfiA==", - "license": "MIT", - "dependencies": { - "semver": "^7.3.5" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/send": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz", - "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==", - "license": "MIT", - "dependencies": { - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "~0.5.2", - "http-errors": "~2.0.1", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "~2.4.1", - "range-parser": "~1.2.1", - "statuses": "~2.0.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/send/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/send/node_modules/debug/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, - "node_modules/send/node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/serialize-javascript": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", - "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", - "license": "BSD-3-Clause", - "dependencies": { - "randombytes": "^2.1.0" - } - }, - "node_modules/serve-handler": { - "version": "6.1.7", - "resolved": "https://registry.npmjs.org/serve-handler/-/serve-handler-6.1.7.tgz", - "integrity": "sha512-CinAq1xWb0vR3twAv9evEU8cNWkXCb9kd5ePAHUKJBkOsUpR1wt/CvGdeca7vqumL1U5cSaeVQ6zZMxiJ3yWsg==", - "license": "MIT", - "dependencies": { - "bytes": "3.0.0", - "content-disposition": "0.5.2", - "mime-types": "2.1.18", - "minimatch": "3.1.5", - "path-is-inside": "1.0.2", - "path-to-regexp": "3.3.0", - "range-parser": "1.2.0" - } - }, - "node_modules/serve-handler/node_modules/path-to-regexp": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-3.3.0.tgz", - "integrity": "sha512-qyCH421YQPS2WFDxDjftfc1ZR5WKQzVzqsp4n9M2kQhVOo/ByahFoUNJfl58kOcEGfQ//7weFTDhm+ss8Ecxgw==", - "license": "MIT" - }, - "node_modules/serve-index": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.2.tgz", - "integrity": "sha512-KDj11HScOaLmrPxl70KYNW1PksP4Nb/CLL2yvC+Qd2kHMPEEpfc4Re2e4FOay+bC/+XQl/7zAcWON3JVo5v3KQ==", - "license": "MIT", - "dependencies": { - "accepts": "~1.3.8", - "batch": "0.6.1", - "debug": "2.6.9", - "escape-html": "~1.0.3", - "http-errors": "~1.8.0", - "mime-types": "~2.1.35", - "parseurl": "~1.3.3" - }, - "engines": { - "node": ">= 0.8.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/serve-index/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/serve-index/node_modules/depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/serve-index/node_modules/http-errors": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", - "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==", - "license": "MIT", - "dependencies": { - "depd": "~1.1.2", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.1" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/serve-index/node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/serve-index/node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/serve-index/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, - "node_modules/serve-index/node_modules/statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/serve-static": { - "version": "1.16.3", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz", - "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==", - "license": "MIT", - "dependencies": { - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "~0.19.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/set-function-length": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", - "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", - "license": "MIT", - "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", - "license": "ISC" - }, - "node_modules/shallow-clone": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", - "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", - "license": "MIT", - "dependencies": { - "kind-of": "^6.0.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shallowequal": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", - "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==", - "license": "MIT" - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/shell-quote": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.3.tgz", - "integrity": "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", - "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3", - "side-channel-list": "^1.0.0", - "side-channel-map": "^1.0.1", - "side-channel-weakmap": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-list": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", - "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-map": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", - "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-weakmap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", - "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3", - "side-channel-map": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "license": "ISC" - }, - "node_modules/sirv": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/sirv/-/sirv-2.0.4.tgz", - "integrity": "sha512-94Bdh3cC2PKrbgSOUqTiGPWVZeSiXfKOVZNJniWoqrWrRkB1CJzBU3NEbiTsPcYy1lDsANA/THzS+9WBiy5nfQ==", - "license": "MIT", - "dependencies": { - "@polka/url": "^1.0.0-next.24", - "mrmime": "^2.0.0", - "totalist": "^3.0.0" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/sisteransi": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", - "license": "MIT" - }, - "node_modules/sitemap": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/sitemap/-/sitemap-7.1.3.tgz", - "integrity": "sha512-tAjEd+wt/YwnEbfNB2ht51ybBJxbEWwe5ki/Z//Wh0rpBFTCUSj46GnxUKEWzhfuJTsee8x3lybHxFgUMig2hw==", - "license": "MIT", - "dependencies": { - "@types/node": "^17.0.5", - "@types/sax": "^1.2.1", - "arg": "^5.0.0", - "sax": "^1.2.4" - }, - "bin": { - "sitemap": "dist/cli.js" - }, - "engines": { - "node": ">=12.0.0", - "npm": ">=5.6.0" - } - }, - "node_modules/sitemap/node_modules/@types/node": { - "version": "17.0.45", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.45.tgz", - "integrity": "sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==", - "license": "MIT" - }, - "node_modules/skin-tone": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/skin-tone/-/skin-tone-2.0.0.tgz", - "integrity": "sha512-kUMbT1oBJCpgrnKoSr0o6wPtvRWT9W9UKvGLwfJYO2WuahZRHOpEyL1ckyMGgMWh0UdpmaoFqKKD29WTomNEGA==", - "license": "MIT", - "dependencies": { - "unicode-emoji-modifier-base": "^1.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/snake-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz", - "integrity": "sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==", - "license": "MIT", - "dependencies": { - "dot-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "node_modules/sockjs": { - "version": "0.3.24", - "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", - "integrity": "sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==", - "license": "MIT", - "dependencies": { - "faye-websocket": "^0.11.3", - "uuid": "^8.3.2", - "websocket-driver": "^0.7.4" - } - }, - "node_modules/sort-css-media-queries": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/sort-css-media-queries/-/sort-css-media-queries-2.2.0.tgz", - "integrity": "sha512-0xtkGhWCC9MGt/EzgnvbbbKhqWjl1+/rncmhTh5qCpbYguXh6S/qwePfv/JQ8jePXXmqingylxoC49pCkSPIbA==", - "license": "MIT", - "engines": { - "node": ">= 6.3.0" - } - }, - "node_modules/source-map": { - "version": "0.7.6", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz", - "integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==", - "license": "BSD-3-Clause", - "engines": { - "node": ">= 12" - } - }, - "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "license": "MIT", - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/source-map-support/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/space-separated-tokens": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", - "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/spdy": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", - "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", - "license": "MIT", - "dependencies": { - "debug": "^4.1.0", - "handle-thing": "^2.0.0", - "http-deceiver": "^1.2.7", - "select-hose": "^2.0.0", - "spdy-transport": "^3.0.0" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/spdy-transport": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", - "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", - "license": "MIT", - "dependencies": { - "debug": "^4.1.0", - "detect-node": "^2.0.4", - "hpack.js": "^2.1.6", - "obuf": "^1.1.2", - "readable-stream": "^3.0.6", - "wbuf": "^1.7.3" - } - }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "license": "BSD-3-Clause" - }, - "node_modules/srcset": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/srcset/-/srcset-4.0.0.tgz", - "integrity": "sha512-wvLeHgcVHKO8Sc/H/5lkGreJQVeYMm9rlmt8PuR1xE31rIuXhuzznUUqAt8MqLhB3MqJdFzlNAfpcWnxiFUcPw==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/statuses": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", - "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/std-env": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz", - "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==", - "license": "MIT" - }, - "node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, - "node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "license": "MIT", - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/string-width/node_modules/ansi-regex": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", - "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/string-width/node_modules/strip-ansi": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", - "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.2.2" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/stringify-entities": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", - "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==", - "license": "MIT", - "dependencies": { - "character-entities-html4": "^2.0.0", - "character-entities-legacy": "^3.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/stringify-object": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz", - "integrity": "sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==", - "license": "BSD-2-Clause", - "dependencies": { - "get-own-enumerable-property-symbols": "^3.0.0", - "is-obj": "^1.0.1", - "is-regexp": "^1.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-bom-string": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-bom-string/-/strip-bom-string-1.0.0.tgz", - "integrity": "sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/style-to-js": { - "version": "1.1.21", - "resolved": "https://registry.npmjs.org/style-to-js/-/style-to-js-1.1.21.tgz", - "integrity": "sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==", - "license": "MIT", - "dependencies": { - "style-to-object": "1.0.14" - } - }, - "node_modules/style-to-object": { - "version": "1.0.14", - "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.14.tgz", - "integrity": "sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==", - "license": "MIT", - "dependencies": { - "inline-style-parser": "0.2.7" - } - }, - "node_modules/stylehacks": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-6.1.1.tgz", - "integrity": "sha512-gSTTEQ670cJNoaeIp9KX6lZmm8LJ3jPB5yJmX8Zq/wQxOsAFXV3qjWzHas3YYk1qesuVIyYWWUpZ0vSE/dTSGg==", - "license": "MIT", - "dependencies": { - "browserslist": "^4.23.0", - "postcss-selector-parser": "^6.0.16" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/svg-parser": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/svg-parser/-/svg-parser-2.0.4.tgz", - "integrity": "sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==", - "license": "MIT" - }, - "node_modules/svgo": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/svgo/-/svgo-3.3.3.tgz", - "integrity": "sha512-+wn7I4p7YgJhHs38k2TNjy1vCfPIfLIJWR5MnCStsN8WuuTcBnRKcMHQLMM2ijxGZmDoZwNv8ipl5aTTen62ng==", - "license": "MIT", - "dependencies": { - "commander": "^7.2.0", - "css-select": "^5.1.0", - "css-tree": "^2.3.1", - "css-what": "^6.1.0", - "csso": "^5.0.5", - "picocolors": "^1.0.0", - "sax": "^1.5.0" - }, - "bin": { - "svgo": "bin/svgo" - }, - "engines": { - "node": ">=14.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/svgo" - } - }, - "node_modules/svgo/node_modules/commander": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", - "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", - "license": "MIT", - "engines": { - "node": ">= 10" - } - }, - "node_modules/tapable": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.2.tgz", - "integrity": "sha512-1MOpMXuhGzGL5TTCZFItxCc0AARf1EZFQkGqMm7ERKj8+Hgr5oLvJOVFcC+lRmR8hCe2S3jC4T5D7Vg/d7/fhA==", - "license": "MIT", - "engines": { - "node": ">=6" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/terser": { - "version": "5.46.1", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.46.1.tgz", - "integrity": "sha512-vzCjQO/rgUuK9sf8VJZvjqiqiHFaZLnOiimmUuOKODxWL8mm/xua7viT7aqX7dgPY60otQjUotzFMmCB4VdmqQ==", - "license": "BSD-2-Clause", - "dependencies": { - "@jridgewell/source-map": "^0.3.3", - "acorn": "^8.15.0", - "commander": "^2.20.0", - "source-map-support": "~0.5.20" - }, - "bin": { - "terser": "bin/terser" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/terser-webpack-plugin": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.4.0.tgz", - "integrity": "sha512-Bn5vxm48flOIfkdl5CaD2+1CiUVbonWQ3KQPyP7/EuIl9Gbzq/gQFOzaMFUEgVjB1396tcK0SG8XcNJ/2kDH8g==", - "license": "MIT", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.25", - "jest-worker": "^27.4.5", - "schema-utils": "^4.3.0", - "terser": "^5.31.1" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.1.0" - }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "esbuild": { - "optional": true - }, - "uglify-js": { - "optional": true - } - } - }, - "node_modules/terser-webpack-plugin/node_modules/jest-worker": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", - "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", - "license": "MIT", - "dependencies": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "engines": { - "node": ">= 10.13.0" - } - }, - "node_modules/terser-webpack-plugin/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/terser/node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "license": "MIT" - }, - "node_modules/thingies": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/thingies/-/thingies-2.6.0.tgz", - "integrity": "sha512-rMHRjmlFLM1R96UYPvpmnc3LYtdFrT33JIB7L9hetGue1qAPfn1N2LJeEjxUSidu1Iku+haLZXDuEXUHNGO/lg==", - "license": "MIT", - "engines": { - "node": ">=10.18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/streamich" - }, - "peerDependencies": { - "tslib": "^2" - } - }, - "node_modules/thunky": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", - "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", - "license": "MIT" - }, - "node_modules/tiny-invariant": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz", - "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==", - "license": "MIT" - }, - "node_modules/tiny-warning": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz", - "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==", - "license": "MIT" - }, - "node_modules/tinypool": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.1.1.tgz", - "integrity": "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==", - "license": "MIT", - "engines": { - "node": "^18.0.0 || >=20.0.0" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "license": "MIT", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "license": "MIT", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/totalist": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", - "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/tree-dump": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/tree-dump/-/tree-dump-1.1.0.tgz", - "integrity": "sha512-rMuvhU4MCDbcbnleZTFezWsaZXRFemSqAM+7jPnzUl1fo9w3YEKOxAeui0fz3OI4EU4hf23iyA7uQRVko+UaBA==", - "license": "Apache-2.0", - "engines": { - "node": ">=10.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/streamich" - }, - "peerDependencies": { - "tslib": "2" - } - }, - "node_modules/trim-lines": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", - "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/trough": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz", - "integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" - }, - "node_modules/tsyringe": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/tsyringe/-/tsyringe-4.10.0.tgz", - "integrity": "sha512-axr3IdNuVIxnaK5XGEUFTu3YmAQ6lllgrvqfEoR16g/HGnYY/6We4oWENtAnzK6/LpJ2ur9PAb80RBt7/U4ugw==", - "license": "MIT", - "dependencies": { - "tslib": "^1.9.3" - }, - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/tsyringe/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "license": "0BSD" - }, - "node_modules/type-fest": { - "version": "2.19.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", - "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=12.20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "license": "MIT", - "dependencies": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/type-is/node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/type-is/node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/typedarray-to-buffer": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", - "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", - "license": "MIT", - "dependencies": { - "is-typedarray": "^1.0.0" - } - }, - "node_modules/typescript": { - "version": "5.6.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", - "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", - "devOptional": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/undici-types": { - "version": "7.18.2", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", - "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==", - "license": "MIT" - }, - "node_modules/unicode-canonical-property-names-ecmascript": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz", - "integrity": "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/unicode-emoji-modifier-base": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unicode-emoji-modifier-base/-/unicode-emoji-modifier-base-1.0.0.tgz", - "integrity": "sha512-yLSH4py7oFH3oG/9K+XWrz1pSi3dfUrWEnInbxMfArOfc1+33BlGPQtLsOYwvdMy11AwUBetYuaRxSPqgkq+8g==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/unicode-match-property-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", - "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", - "license": "MIT", - "dependencies": { - "unicode-canonical-property-names-ecmascript": "^2.0.0", - "unicode-property-aliases-ecmascript": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/unicode-match-property-value-ecmascript": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.1.tgz", - "integrity": "sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/unicode-property-aliases-ecmascript": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.2.0.tgz", - "integrity": "sha512-hpbDzxUY9BFwX+UeBnxv3Sh1q7HFxj48DTmXchNgRa46lO8uj3/1iEn3MiNUYTg1g9ctIqXCCERn8gYZhHC5lQ==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/unified": { - "version": "11.0.5", - "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz", - "integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "bail": "^2.0.0", - "devlop": "^1.0.0", - "extend": "^3.0.0", - "is-plain-obj": "^4.0.0", - "trough": "^2.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unique-string": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-3.0.0.tgz", - "integrity": "sha512-VGXBUVwxKMBUznyffQweQABPRRW1vHZAbadFZud4pLFAqRGvv/96vafgjWFqzourzr8YonlQiPgH0YCJfawoGQ==", - "license": "MIT", - "dependencies": { - "crypto-random-string": "^4.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/unist-util-is": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.1.tgz", - "integrity": "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-position": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", - "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-position-from-estree": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unist-util-position-from-estree/-/unist-util-position-from-estree-2.0.0.tgz", - "integrity": "sha512-KaFVRjoqLyF6YXCbVLNad/eS4+OfPQQn2yOd7zF/h5T/CSL2v8NpN6a5TPvtbXthAGw5nG+PuTtq+DdIZr+cRQ==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-stringify-position": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", - "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-visit": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.1.0.tgz", - "integrity": "sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0", - "unist-util-visit-parents": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-visit-parents": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.2.tgz", - "integrity": "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", - "license": "MIT", - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/update-browserslist-db": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", - "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "escalade": "^3.2.0", - "picocolors": "^1.1.1" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/update-notifier": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-6.0.2.tgz", - "integrity": "sha512-EDxhTEVPZZRLWYcJ4ZXjGFN0oP7qYvbXWzEgRm/Yql4dHX5wDbvh89YHP6PK1lzZJYrMtXUuZZz8XGK+U6U1og==", - "license": "BSD-2-Clause", - "dependencies": { - "boxen": "^7.0.0", - "chalk": "^5.0.1", - "configstore": "^6.0.0", - "has-yarn": "^3.0.0", - "import-lazy": "^4.0.0", - "is-ci": "^3.0.1", - "is-installed-globally": "^0.4.0", - "is-npm": "^6.0.0", - "is-yarn-global": "^0.4.0", - "latest-version": "^7.0.0", - "pupa": "^3.1.0", - "semver": "^7.3.7", - "semver-diff": "^4.0.0", - "xdg-basedir": "^5.1.0" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/yeoman/update-notifier?sponsor=1" - } - }, - "node_modules/update-notifier/node_modules/boxen": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/boxen/-/boxen-7.1.1.tgz", - "integrity": "sha512-2hCgjEmP8YLWQ130n2FerGv7rYpfBmnmp9Uy2Le1vge6X3gZIfSmEzP5QTDElFxcvVcXlEn8Aq6MU/PZygIOog==", - "license": "MIT", - "dependencies": { - "ansi-align": "^3.0.1", - "camelcase": "^7.0.1", - "chalk": "^5.2.0", - "cli-boxes": "^3.0.0", - "string-width": "^5.1.2", - "type-fest": "^2.13.0", - "widest-line": "^4.0.1", - "wrap-ansi": "^8.1.0" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/update-notifier/node_modules/camelcase": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-7.0.1.tgz", - "integrity": "sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw==", - "license": "MIT", - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/update-notifier/node_modules/chalk": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", - "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", - "license": "MIT", - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "license": "BSD-2-Clause", - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/url-loader": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-4.1.1.tgz", - "integrity": "sha512-3BTV812+AVHHOJQO8O5MkWgZ5aosP7GnROJwvzLS9hWDj00lZ6Z0wNak423Lp9PBZN05N+Jk/N5Si8jRAlGyWA==", - "license": "MIT", - "dependencies": { - "loader-utils": "^2.0.0", - "mime-types": "^2.1.27", - "schema-utils": "^3.0.0" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "file-loader": "*", - "webpack": "^4.0.0 || ^5.0.0" - }, - "peerDependenciesMeta": { - "file-loader": { - "optional": true - } - } - }, - "node_modules/url-loader/node_modules/ajv": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", - "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/url-loader/node_modules/ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "license": "MIT", - "peerDependencies": { - "ajv": "^6.9.1" - } - }, - "node_modules/url-loader/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "license": "MIT" - }, - "node_modules/url-loader/node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/url-loader/node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/url-loader/node_modules/schema-utils": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", - "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", - "license": "MIT", - "dependencies": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "license": "MIT" - }, - "node_modules/utila": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz", - "integrity": "sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA==", - "license": "MIT" - }, - "node_modules/utility-types": { - "version": "3.11.0", - "resolved": "https://registry.npmjs.org/utility-types/-/utility-types-3.11.0.tgz", - "integrity": "sha512-6Z7Ma2aVEWisaL6TvBCy7P8rm2LQoPv6dJ7ecIaIixHcwfbJ0x7mWdbcwlIM5IGQxPZSFYeqRCqlOOeKoJYMkw==", - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", - "license": "MIT", - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "license": "MIT", - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/value-equal": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/value-equal/-/value-equal-1.0.1.tgz", - "integrity": "sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw==", - "license": "MIT" - }, - "node_modules/vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/vfile": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", - "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/vfile-location": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-5.0.3.tgz", - "integrity": "sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/vfile-message": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.3.tgz", - "integrity": "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-stringify-position": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/watchpack": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.5.1.tgz", - "integrity": "sha512-Zn5uXdcFNIA1+1Ei5McRd+iRzfhENPCe7LeABkJtNulSxjma+l7ltNx55BWZkRlwRnpOgHqxnjyaDgJnNXnqzg==", - "license": "MIT", - "dependencies": { - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.1.2" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/wbuf": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", - "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", - "license": "MIT", - "dependencies": { - "minimalistic-assert": "^1.0.0" - } - }, - "node_modules/web-namespaces": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-2.0.1.tgz", - "integrity": "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/webpack": { - "version": "5.105.4", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.105.4.tgz", - "integrity": "sha512-jTywjboN9aHxFlToqb0K0Zs9SbBoW4zRUlGzI2tYNxVYcEi/IPpn+Xi4ye5jTLvX2YeLuic/IvxNot+Q1jMoOw==", - "license": "MIT", - "dependencies": { - "@types/eslint-scope": "^3.7.7", - "@types/estree": "^1.0.8", - "@types/json-schema": "^7.0.15", - "@webassemblyjs/ast": "^1.14.1", - "@webassemblyjs/wasm-edit": "^1.14.1", - "@webassemblyjs/wasm-parser": "^1.14.1", - "acorn": "^8.16.0", - "acorn-import-phases": "^1.0.3", - "browserslist": "^4.28.1", - "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.20.0", - "es-module-lexer": "^2.0.0", - "eslint-scope": "5.1.1", - "events": "^3.2.0", - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.2.11", - "json-parse-even-better-errors": "^2.3.1", - "loader-runner": "^4.3.1", - "mime-types": "^2.1.27", - "neo-async": "^2.6.2", - "schema-utils": "^4.3.3", - "tapable": "^2.3.0", - "terser-webpack-plugin": "^5.3.17", - "watchpack": "^2.5.1", - "webpack-sources": "^3.3.4" - }, - "bin": { - "webpack": "bin/webpack.js" - }, - "engines": { - "node": ">=10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependenciesMeta": { - "webpack-cli": { - "optional": true - } - } - }, - "node_modules/webpack-bundle-analyzer": { - "version": "4.10.2", - "resolved": "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.10.2.tgz", - "integrity": "sha512-vJptkMm9pk5si4Bv922ZbKLV8UTT4zib4FPgXMhgzUny0bfDDkLXAVQs3ly3fS4/TN9ROFtb0NFrm04UXFE/Vw==", - "license": "MIT", - "dependencies": { - "@discoveryjs/json-ext": "0.5.7", - "acorn": "^8.0.4", - "acorn-walk": "^8.0.0", - "commander": "^7.2.0", - "debounce": "^1.2.1", - "escape-string-regexp": "^4.0.0", - "gzip-size": "^6.0.0", - "html-escaper": "^2.0.2", - "opener": "^1.5.2", - "picocolors": "^1.0.0", - "sirv": "^2.0.3", - "ws": "^7.3.1" - }, - "bin": { - "webpack-bundle-analyzer": "lib/bin/analyzer.js" - }, - "engines": { - "node": ">= 10.13.0" - } - }, - "node_modules/webpack-bundle-analyzer/node_modules/commander": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", - "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", - "license": "MIT", - "engines": { - "node": ">= 10" - } - }, - "node_modules/webpack-dev-middleware": { - "version": "7.4.5", - "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-7.4.5.tgz", - "integrity": "sha512-uxQ6YqGdE4hgDKNf7hUiPXOdtkXvBJXrfEGYSx7P7LC8hnUYGK70X6xQXUvXeNyBDDcsiQXpG2m3G9vxowaEuA==", - "license": "MIT", - "dependencies": { - "colorette": "^2.0.10", - "memfs": "^4.43.1", - "mime-types": "^3.0.1", - "on-finished": "^2.4.1", - "range-parser": "^1.2.1", - "schema-utils": "^4.0.0" - }, - "engines": { - "node": ">= 18.12.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.0.0" - }, - "peerDependenciesMeta": { - "webpack": { - "optional": true - } - } - }, - "node_modules/webpack-dev-middleware/node_modules/mime-db": { - "version": "1.54.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", - "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/webpack-dev-middleware/node_modules/mime-types": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", - "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", - "license": "MIT", - "dependencies": { - "mime-db": "^1.54.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/webpack-dev-middleware/node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/webpack-dev-server": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-5.2.3.tgz", - "integrity": "sha512-9Gyu2F7+bg4Vv+pjbovuYDhHX+mqdqITykfzdM9UyKqKHlsE5aAjRhR+oOEfXW5vBeu8tarzlJFIZva4ZjAdrQ==", - "license": "MIT", - "dependencies": { - "@types/bonjour": "^3.5.13", - "@types/connect-history-api-fallback": "^1.5.4", - "@types/express": "^4.17.25", - "@types/express-serve-static-core": "^4.17.21", - "@types/serve-index": "^1.9.4", - "@types/serve-static": "^1.15.5", - "@types/sockjs": "^0.3.36", - "@types/ws": "^8.5.10", - "ansi-html-community": "^0.0.8", - "bonjour-service": "^1.2.1", - "chokidar": "^3.6.0", - "colorette": "^2.0.10", - "compression": "^1.8.1", - "connect-history-api-fallback": "^2.0.0", - "express": "^4.22.1", - "graceful-fs": "^4.2.6", - "http-proxy-middleware": "^2.0.9", - "ipaddr.js": "^2.1.0", - "launch-editor": "^2.6.1", - "open": "^10.0.3", - "p-retry": "^6.2.0", - "schema-utils": "^4.2.0", - "selfsigned": "^5.5.0", - "serve-index": "^1.9.1", - "sockjs": "^0.3.24", - "spdy": "^4.0.2", - "webpack-dev-middleware": "^7.4.2", - "ws": "^8.18.0" - }, - "bin": { - "webpack-dev-server": "bin/webpack-dev-server.js" - }, - "engines": { - "node": ">= 18.12.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.0.0" - }, - "peerDependenciesMeta": { - "webpack": { - "optional": true - }, - "webpack-cli": { - "optional": true - } - } - }, - "node_modules/webpack-dev-server/node_modules/define-lazy-prop": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", - "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/webpack-dev-server/node_modules/open": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/open/-/open-10.2.0.tgz", - "integrity": "sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==", - "license": "MIT", - "dependencies": { - "default-browser": "^5.2.1", - "define-lazy-prop": "^3.0.0", - "is-inside-container": "^1.0.0", - "wsl-utils": "^0.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/webpack-dev-server/node_modules/ws": { - "version": "8.20.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.20.0.tgz", - "integrity": "sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA==", - "license": "MIT", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/webpack-merge": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-6.0.1.tgz", - "integrity": "sha512-hXXvrjtx2PLYx4qruKl+kyRSLc52V+cCvMxRjmKwoA+CBbbF5GfIBtR6kCvl0fYGqTUPKB+1ktVmTHqMOzgCBg==", - "license": "MIT", - "dependencies": { - "clone-deep": "^4.0.1", - "flat": "^5.0.2", - "wildcard": "^2.0.1" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/webpack-sources": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.3.4.tgz", - "integrity": "sha512-7tP1PdV4vF+lYPnkMR0jMY5/la2ub5Fc/8VQrrU+lXkiM6C4TjVfGw7iKfyhnTQOsD+6Q/iKw0eFciziRgD58Q==", - "license": "MIT", - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/webpack/node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/webpack/node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/webpackbar": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/webpackbar/-/webpackbar-6.0.1.tgz", - "integrity": "sha512-TnErZpmuKdwWBdMoexjio3KKX6ZtoKHRVvLIU0A47R0VVBDtx3ZyOJDktgYixhoJokZTYTt1Z37OkO9pnGJa9Q==", - "license": "MIT", - "dependencies": { - "ansi-escapes": "^4.3.2", - "chalk": "^4.1.2", - "consola": "^3.2.3", - "figures": "^3.2.0", - "markdown-table": "^2.0.0", - "pretty-time": "^1.1.0", - "std-env": "^3.7.0", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=14.21.3" - }, - "peerDependencies": { - "webpack": "3 || 4 || 5" - } - }, - "node_modules/webpackbar/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "license": "MIT" - }, - "node_modules/webpackbar/node_modules/markdown-table": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-2.0.0.tgz", - "integrity": "sha512-Ezda85ToJUBhM6WGaG6veasyym+Tbs3cMAw/ZhOPqXiYsr0jgocBV3j3nx+4lk47plLlIqjwuTm/ywVI+zjJ/A==", - "license": "MIT", - "dependencies": { - "repeat-string": "^1.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/webpackbar/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/webpackbar/node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/websocket-driver": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", - "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", - "license": "Apache-2.0", - "dependencies": { - "http-parser-js": ">=0.5.1", - "safe-buffer": ">=5.1.0", - "websocket-extensions": ">=0.1.1" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/websocket-extensions": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", - "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", - "license": "Apache-2.0", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/widest-line": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-4.0.1.tgz", - "integrity": "sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig==", - "license": "MIT", - "dependencies": { - "string-width": "^5.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/wildcard": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz", - "integrity": "sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==", - "license": "MIT" - }, - "node_modules/wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi/node_modules/ansi-regex": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", - "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/wrap-ansi/node_modules/ansi-styles": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", - "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/wrap-ansi/node_modules/strip-ansi": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", - "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.2.2" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/write-file-atomic": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", - "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", - "license": "ISC", - "dependencies": { - "imurmurhash": "^0.1.4", - "is-typedarray": "^1.0.0", - "signal-exit": "^3.0.2", - "typedarray-to-buffer": "^3.1.5" - } - }, - "node_modules/ws": { - "version": "7.5.10", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", - "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", - "license": "MIT", - "engines": { - "node": ">=8.3.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/wsl-utils": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/wsl-utils/-/wsl-utils-0.1.0.tgz", - "integrity": "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==", - "license": "MIT", - "dependencies": { - "is-wsl": "^3.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/wsl-utils/node_modules/is-wsl": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.1.tgz", - "integrity": "sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==", - "license": "MIT", - "dependencies": { - "is-inside-container": "^1.0.0" - }, - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/xdg-basedir": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-5.1.0.tgz", - "integrity": "sha512-GCPAHLvrIH13+c0SuacwvRYj2SxJXQ4kaVTT5xgL3kPrz56XxkF21IGhjSE1+W0aw7gpBWRGXLCPnPby6lSpmQ==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/xml-js": { - "version": "1.6.11", - "resolved": "https://registry.npmjs.org/xml-js/-/xml-js-1.6.11.tgz", - "integrity": "sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g==", - "license": "MIT", - "dependencies": { - "sax": "^1.2.4" - }, - "bin": { - "xml-js": "bin/cli.js" - } - }, - "node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "license": "ISC" - }, - "node_modules/yocto-queue": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.2.2.tgz", - "integrity": "sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==", - "license": "MIT", - "engines": { - "node": ">=12.20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/zwitch": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", - "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - } - } -} diff --git a/website/package.json b/website/package.json deleted file mode 100644 index 452e8815d..000000000 --- a/website/package.json +++ /dev/null @@ -1,47 +0,0 @@ -{ - "name": "website", - "version": "0.0.0", - "private": true, - "scripts": { - "docusaurus": "docusaurus", - "start": "docusaurus start", - "build": "docusaurus build", - "swizzle": "docusaurus swizzle", - "deploy": "docusaurus deploy", - "clear": "docusaurus clear", - "serve": "docusaurus serve", - "write-translations": "docusaurus write-translations", - "write-heading-ids": "docusaurus write-heading-ids", - "typecheck": "tsc" - }, - "dependencies": { - "@docusaurus/core": "3.9.2", - "@docusaurus/preset-classic": "3.9.2", - "@mdx-js/react": "^3.0.0", - "clsx": "^2.0.0", - "prism-react-renderer": "^2.3.0", - "react": "^19.0.0", - "react-dom": "^19.0.0" - }, - "devDependencies": { - "@docusaurus/module-type-aliases": "3.9.2", - "@docusaurus/tsconfig": "3.9.2", - "@docusaurus/types": "3.9.2", - "typescript": "~5.6.2" - }, - "browserslist": { - "production": [ - ">0.5%", - "not dead", - "not op_mini all" - ], - "development": [ - "last 3 chrome version", - "last 3 firefox version", - "last 5 safari version" - ] - }, - "engines": { - "node": ">=20.0" - } -} diff --git a/website/packets/incoming/account.json b/website/packets/incoming/account.json deleted file mode 100644 index 410864d6d..000000000 --- a/website/packets/incoming/account.json +++ /dev/null @@ -1,1081 +0,0 @@ -{ - "category": "Account", - "packets": [ - { - "id": "0x80", - "name": "Account Login", - "description": "Initial login request sent to the login server. Contains account credentials for authentication.", - "direction": "incoming", - "isDynamic": false, - "size": 62, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x80", - "description": "Packet identifier" - }, - { - "name": "Username", - "type": "ascii", - "size": 30, - "description": "Account username (null-terminated)" - }, - { - "name": "Password", - "type": "ascii", - "size": 30, - "description": "Account password (null-terminated)" - }, - { - "name": "Next Login Key", - "type": "byte", - "size": 1, - "description": "Next login key" - } - ], - "related": [ - { - "id": "0x82", - "relationship": "rej", - "note": "Sent if login fails" - }, - { - "id": "0xA8", - "relationship": "ack", - "note": "Sent if login succeeds (server list)" - } - ], - "source": { - "file": "Projects/UOContent/Network/Packets/IncomingAccountPackets.cs", - "line": 439 - } - }, - { - "id": "0x91", - "name": "Game Login", - "description": "Login request sent to the game server after selecting a shard from the server list.", - "direction": "incoming", - "isDynamic": false, - "size": 65, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x91", - "description": "Packet identifier" - }, - { - "name": "Auth ID", - "type": "int", - "size": 4, - "description": "Authentication ID from PlayServerAck (0x8C)" - }, - { - "name": "Username", - "type": "ascii", - "size": 30, - "description": "Account username" - }, - { - "name": "Password", - "type": "ascii", - "size": 30, - "description": "Account password" - } - ], - "related": [ - { - "id": "0xA9", - "relationship": "response", - "note": "Character list sent on success" - } - ], - "source": { - "file": "Projects/UOContent/Network/Packets/IncomingAccountPackets.cs", - "line": 345 - } - }, - { - "id": "0x5D", - "name": "Play Character", - "description": "Request to enter the world with a selected character from the character list.", - "direction": "incoming", - "isDynamic": false, - "size": 73, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x5D", - "description": "Packet identifier" - }, - { - "name": "Pattern", - "type": "uint", - "size": 4, - "value": "0xEDEDEDED", - "description": "Fixed pattern" - }, - { - "name": "Name", - "type": "ascii", - "size": 30, - "description": "Character name (unused)" - }, - { - "name": "Unknown", - "type": "byte[2]", - "size": 2, - "description": "Unknown" - }, - { - "name": "Flags", - "type": "int", - "size": 4, - "description": "Client flags" - }, - { - "name": "Unknown2", - "type": "byte[24]", - "size": 24, - "description": "Unknown" - }, - { - "name": "Char Slot", - "type": "int", - "size": 4, - "description": "Character slot index (0-based)" - }, - { - "name": "Client IP", - "type": "int", - "size": 4, - "description": "Client IP address" - } - ], - "related": [ - { - "id": "0x1B", - "relationship": "response", - "note": "Login confirmation sent on success" - } - ], - "source": { - "file": "Projects/UOContent/Network/Packets/IncomingAccountPackets.cs", - "line": 210 - } - }, - { - "id": "0xA0", - "name": "Play Server", - "description": "Request to connect to a specific game server from the server list.", - "direction": "incoming", - "isDynamic": false, - "size": 3, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xA0", - "description": "Packet identifier" - }, - { - "name": "Server Index", - "type": "short", - "size": 2, - "description": "Index of selected server in the list" - } - ], - "related": [ - { - "id": "0x8C", - "relationship": "response", - "note": "Server connection details" - } - ], - "source": { - "file": "Projects/UOContent/Network/Packets/IncomingAccountPackets.cs", - "line": 398 - } - }, - { - "id": "0x83", - "name": "Delete Character", - "description": "Request to delete a character from the account.", - "direction": "incoming", - "isDynamic": false, - "size": 39, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x83", - "description": "Packet identifier" - }, - { - "name": "Password", - "type": "ascii", - "size": 30, - "description": "Account password for verification" - }, - { - "name": "Char Index", - "type": "int", - "size": 4, - "description": "Character slot index to delete" - }, - { - "name": "Client IP", - "type": "int", - "size": 4, - "description": "Client IP address" - } - ], - "related": [ - { - "id": "0x85", - "relationship": "response", - "note": "Delete result" - } - ], - "source": { - "file": "Projects/UOContent/Network/Packets/IncomingAccountPackets.cs", - "line": 185 - } - }, - { - "id": "0xEF", - "name": "Login Server Seed", - "description": "Initial connection packet sent before account login. Contains seed and client version information.", - "direction": "incoming", - "isDynamic": false, - "size": 21, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xEF", - "description": "Packet identifier" - }, - { - "name": "Seed", - "type": "int", - "size": 4, - "description": "Random seed for encryption" - }, - { - "name": "Client Major", - "type": "int", - "size": 4, - "description": "Client major version" - }, - { - "name": "Client Minor", - "type": "int", - "size": 4, - "description": "Client minor version" - }, - { - "name": "Client Revision", - "type": "int", - "size": 4, - "description": "Client revision" - }, - { - "name": "Client Patch", - "type": "int", - "size": 4, - "description": "Client patch level" - } - ], - "clientVersion": { - "classic": { - "min": "6.0.5.0" - }, - "enhanced": {}, - "notes": "Replaces the legacy 4-byte seed for clients 6.0.5.0+. Pre-6.0.5.0 clients send only a 4-byte seed without version info." - }, - "source": { - "file": "Projects/UOContent/Network/Packets/IncomingAccountPackets.cs", - "line": 419 - } - }, - { - "id": "0xBD", - "name": "Client Version", - "description": "Response to server\u0027s version request (0xBD). Contains the client version string.", - "direction": "incoming", - "isDynamic": true, - "size": "3 + version string length", - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xBD", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Version", - "type": "ascii", - "description": "Client version string (e.g., \u00277.0.95.0\u0027)" - } - ], - "source": { - "file": "Projects/UOContent/Network/Packets/IncomingAccountPackets.cs", - "line": 193 - } - }, - { - "id": "0xE1", - "name": "Client Type", - "description": "Sent by the client to identify the client type and version. Added during Kingdom Reborn/Stygian Abyss expansion.", - "direction": "incoming", - "isDynamic": true, - "size": "var", - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xE1", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Unknown", - "type": "ushort", - "size": 2, - "value": "0x0001", - "description": "Unknown (always 0x0001)" - }, - { - "name": "Client Type", - "type": "enum", - "enumType": "sequential", - "size": 2, - "description": "Client type identifier", - "values": [ - { "value": "0x00", "name": "Classic", "description": "Classic 2D Client" }, - { "value": "0x02", "name": "KR", "description": "Kingdom Reborn Client" }, - { "value": "0x03", "name": "EC", "description": "Enhanced Client (Stygian Abyss)" } - ] - }, - { - "name": "Version", - "type": "ascii", - "description": "Client version string" - } - ], - "related": [ - { - "id": "0xBF/0x0F", - "relationship": "related", - "note": "Client Info - similar client type information sent at login" - } - ], - "clientVersion": { - "classic": { - "min": "6.0.14.3" - }, - "enhanced": {}, - "notes": "Added during Kingdom Reborn/Stygian Abyss expansion" - }, - "source": { - "file": "Projects/UOContent/Network/Packets/IncomingAccountPackets.cs", - "line": 200 - } - }, - { - "id": "0x00", - "name": "Create Character (Old)", - "description": "Request to create a new character. This is the older 104-byte version with 3 starting skills. Replaced by 0xF8 in client 7.0.16.0+.", - "direction": "incoming", - "isDynamic": false, - "size": 104, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x00", - "description": "Packet identifier" - }, - { - "name": "Unknown1", - "type": "int", - "size": 4, - "description": "Unknown" - }, - { - "name": "Unknown2", - "type": "int", - "size": 4, - "description": "Unknown" - }, - { - "name": "Unknown3", - "type": "byte", - "size": 1, - "description": "Unknown" - }, - { - "name": "Name", - "type": "ascii", - "size": 30, - "description": "Character name" - }, - { - "name": "Unknown4", - "type": "byte[2]", - "size": 2, - "description": "Unknown" - }, - { - "name": "Flags", - "type": "int", - "size": 4, - "description": "Client flags" - }, - { - "name": "Unknown5", - "type": "byte[8]", - "size": 8, - "description": "Unknown" - }, - { - "name": "Profession", - "type": "byte", - "size": 1, - "description": "Starting profession" - }, - { - "name": "Unknown6", - "type": "byte[15]", - "size": 15, - "description": "Unknown" - }, - { - "name": "Gender Race", - "type": "byte", - "size": 1, - "description": "Gender and race combined value" - }, - { - "name": "Strength", - "type": "byte", - "size": 1, - "description": "Starting strength" - }, - { - "name": "Dexterity", - "type": "byte", - "size": 1, - "description": "Starting dexterity" - }, - { - "name": "Intelligence", - "type": "byte", - "size": 1, - "description": "Starting intelligence" - }, - { - "name": "Skill1Id", - "type": "byte", - "size": 1, - "description": "First skill ID" - }, - { - "name": "Skill1Value", - "type": "byte", - "size": 1, - "description": "First skill value" - }, - { - "name": "Skill2Id", - "type": "byte", - "size": 1, - "description": "Second skill ID" - }, - { - "name": "Skill2Value", - "type": "byte", - "size": 1, - "description": "Second skill value" - }, - { - "name": "Skill3Id", - "type": "byte", - "size": 1, - "description": "Third skill ID" - }, - { - "name": "Skill3Value", - "type": "byte", - "size": 1, - "description": "Third skill value" - }, - { - "name": "Skin Hue", - "type": "ushort", - "size": 2, - "description": "Skin color hue" - }, - { - "name": "Hair Style", - "type": "short", - "size": 2, - "description": "Hair style ID" - }, - { - "name": "Hair Hue", - "type": "short", - "size": 2, - "description": "Hair color hue" - }, - { - "name": "Facial Hair Style", - "type": "short", - "size": 2, - "description": "Facial hair style ID" - }, - { - "name": "Facial Hair Hue", - "type": "short", - "size": 2, - "description": "Facial hair color hue" - }, - { - "name": "Unknown7", - "type": "byte", - "size": 1, - "description": "Unknown" - }, - { - "name": "City Index", - "type": "byte", - "size": 1, - "description": "Starting city index" - }, - { - "name": "Char Slot", - "type": "int", - "size": 4, - "description": "Character slot" - }, - { - "name": "Client IP", - "type": "int", - "size": 4, - "description": "Client IP" - }, - { - "name": "Shirt Hue", - "type": "short", - "size": 2, - "description": "Starting shirt hue" - }, - { - "name": "Pants Hue", - "type": "short", - "size": 2, - "description": "Starting pants hue" - } - ], - "related": [ - { - "id": "0xF8", - "relationship": "variant", - "note": "Create Character (New) for Classic 7.0.16.0+ with 4th skill" - }, - { - "id": "0x8D", - "relationship": "variant", - "note": "Create Character (EC) for Enhanced Client" - } - ], - "clientVersion": { - "classic": { - "max": "7.0.15.x" - }, - "notes": "Replaced by 0xF8 in Classic Client 7.0.16.0+. EC uses 0x8D instead." - }, - "source": { - "file": "Projects/UOContent/Network/Packets/IncomingAccountPackets.cs", - "line": 58 - } - }, - { - "id": "0xF8", - "name": "Create Character (New)", - "description": "Request to create a new character. This is the newer 106-byte version with support for 4 skills.", - "direction": "incoming", - "isDynamic": false, - "size": 106, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xF8", - "description": "Packet identifier" - }, - { - "name": "Unknown1", - "type": "int", - "size": 4, - "description": "Unknown" - }, - { - "name": "Unknown2", - "type": "int", - "size": 4, - "description": "Unknown" - }, - { - "name": "Unknown3", - "type": "byte", - "size": 1, - "description": "Unknown" - }, - { - "name": "Name", - "type": "ascii", - "size": 30, - "description": "Character name" - }, - { - "name": "Unknown4", - "type": "byte[2]", - "size": 2, - "description": "Unknown" - }, - { - "name": "Flags", - "type": "int", - "size": 4, - "description": "Client flags" - }, - { - "name": "Unknown5", - "type": "byte[8]", - "size": 8, - "description": "Unknown" - }, - { - "name": "Profession", - "type": "byte", - "size": 1, - "description": "Starting profession" - }, - { - "name": "Unknown6", - "type": "byte[15]", - "size": 15, - "description": "Unknown" - }, - { - "name": "Gender Race", - "type": "byte", - "size": 1, - "description": "Gender and race combined value" - }, - { - "name": "Strength", - "type": "byte", - "size": 1, - "description": "Starting strength" - }, - { - "name": "Dexterity", - "type": "byte", - "size": 1, - "description": "Starting dexterity" - }, - { - "name": "Intelligence", - "type": "byte", - "size": 1, - "description": "Starting intelligence" - }, - { - "name": "Skill1Id", - "type": "byte", - "size": 1, - "description": "First skill ID" - }, - { - "name": "Skill1Value", - "type": "byte", - "size": 1, - "description": "First skill value" - }, - { - "name": "Skill2Id", - "type": "byte", - "size": 1, - "description": "Second skill ID" - }, - { - "name": "Skill2Value", - "type": "byte", - "size": 1, - "description": "Second skill value" - }, - { - "name": "Skill3Id", - "type": "byte", - "size": 1, - "description": "Third skill ID" - }, - { - "name": "Skill3Value", - "type": "byte", - "size": 1, - "description": "Third skill value" - }, - { - "name": "Skill4Id", - "type": "byte", - "size": 1, - "description": "Fourth skill ID (newer clients)" - }, - { - "name": "Skill4Value", - "type": "byte", - "size": 1, - "description": "Fourth skill value (newer clients)" - }, - { - "name": "Skin Hue", - "type": "ushort", - "size": 2, - "description": "Skin color hue" - }, - { - "name": "Hair Style", - "type": "short", - "size": 2, - "description": "Hair style ID" - }, - { - "name": "Hair Hue", - "type": "short", - "size": 2, - "description": "Hair color hue" - }, - { - "name": "Facial Hair Style", - "type": "short", - "size": 2, - "description": "Facial hair style ID" - }, - { - "name": "Facial Hair Hue", - "type": "short", - "size": 2, - "description": "Facial hair color hue" - }, - { - "name": "Unknown7", - "type": "byte", - "size": 1, - "description": "Unknown" - }, - { - "name": "City Index", - "type": "byte", - "size": 1, - "description": "Starting city index" - }, - { - "name": "Char Slot", - "type": "int", - "size": 4, - "description": "Character slot" - }, - { - "name": "Client IP", - "type": "int", - "size": 4, - "description": "Client IP" - }, - { - "name": "Shirt Hue", - "type": "short", - "size": 2, - "description": "Starting shirt hue" - }, - { - "name": "Pants Hue", - "type": "short", - "size": 2, - "description": "Starting pants hue" - } - ], - "related": [ - { - "id": "0x00", - "relationship": "variant", - "note": "Create Character (Old) for Classic pre-7.0.16.0 with 3 skills" - }, - { - "id": "0x8D", - "relationship": "variant", - "note": "Create Character (EC) for Enhanced Client" - } - ], - "clientVersion": { - "classic": { - "min": "7.0.16.0" - }, - "notes": "Classic Client only. Replaces 0x00, adds 4th starting skill. EC uses 0x8D instead." - }, - "source": { - "file": "Projects/UOContent/Network/Packets/IncomingAccountPackets.cs", - "line": 58 - }, - "notes": "Uses the same handler as 0x00 but with 2 additional bytes for the 4th skill." - }, - { - "id": "0x8D", - "name": "Create Character", - "description": "Request to create a new character from Enhanced Client (KR/SA 3D clients). Variable length packet with different field layout than Classic client versions.", - "direction": "incoming", - "isDynamic": true, - "size": "var", - "implemented": false, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x8D", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Pattern1", - "type": "uint", - "size": 4, - "value": "0xEDEDEDED", - "description": "Fixed pattern" - }, - { - "name": "Character Index", - "type": "uint", - "size": 4, - "description": "Character slot index" - }, - { - "name": "Name", - "type": "ascii", - "size": 30, - "description": "Character name" - }, - { - "name": "Unknown", - "type": "byte[30]", - "size": 30, - "description": "Unknown (possibly password field)" - }, - { - "name": "Profession", - "type": "byte", - "size": 1, - "description": "Starting profession" - }, - { - "name": "Client Flags", - "type": "byte", - "size": 1, - "description": "Client flags (0x41 or 0x3F)" - }, - { - "name": "Gender", - "type": "byte", - "size": 1, - "description": "0=male, 1=female" - }, - { - "name": "Race", - "type": "byte", - "size": 1, - "description": "0=human, 1=elf, 2=gargoyle" - }, - { - "name": "Strength", - "type": "byte", - "size": 1, - "description": "Starting strength" - }, - { - "name": "Dexterity", - "type": "byte", - "size": 1, - "description": "Starting dexterity" - }, - { - "name": "Intelligence", - "type": "byte", - "size": 1, - "description": "Starting intelligence" - }, - { - "name": "Skin Color", - "type": "ushort", - "size": 2, - "description": "Character skin hue" - }, - { - "name": "Unknown2", - "type": "byte[8]", - "size": 8, - "description": "Unknown padding" - }, - { - "name": "Skill 1 ID", - "type": "byte", - "size": 1, - "description": "First skill ID" - }, - { - "name": "Skill 1 Value", - "type": "byte", - "size": 1, - "description": "First skill starting value" - }, - { - "name": "Skill 2 ID", - "type": "byte", - "size": 1, - "description": "Second skill ID" - }, - { - "name": "Skill 2 Value", - "type": "byte", - "size": 1, - "description": "Second skill starting value" - }, - { - "name": "Skill 3 ID", - "type": "byte", - "size": 1, - "description": "Third skill ID" - }, - { - "name": "Skill 3 Value", - "type": "byte", - "size": 1, - "description": "Third skill starting value" - }, - { - "name": "Skill 4 ID", - "type": "byte", - "size": 1, - "description": "Fourth skill ID" - }, - { - "name": "Skill 4 Value", - "type": "byte", - "size": 1, - "description": "Fourth skill starting value" - }, - { - "name": "Unknown3", - "type": "byte[26]", - "size": 26, - "description": "Unknown padding and appearance fields" - }, - { - "name": "Hair Color", - "type": "ushort", - "size": 2, - "description": "Hair hue" - }, - { - "name": "Hair Style", - "type": "ushort", - "size": 2, - "description": "Hair graphic ID" - }, - { - "name": "Shirt Color", - "type": "ushort", - "size": 2, - "description": "Shirt hue" - }, - { - "name": "Shirt Style", - "type": "ushort", - "size": 2, - "description": "Shirt graphic ID" - }, - { - "name": "Face Color", - "type": "ushort", - "size": 2, - "description": "Face hue" - }, - { - "name": "Face Style", - "type": "ushort", - "size": 2, - "description": "Face graphic ID" - }, - { - "name": "Beard Color", - "type": "ushort", - "size": 2, - "description": "Facial hair hue" - }, - { - "name": "Beard Style", - "type": "ushort", - "size": 2, - "description": "Facial hair graphic ID" - } - ], - "related": [ - { - "id": "0x00", - "relationship": "variant", - "note": "Create Character (Old) for Classic Client pre-7.0.16.0" - }, - { - "id": "0xF8", - "relationship": "variant", - "note": "Create Character (New) for Classic Client 7.0.16.0+" - } - ], - "clientVersion": { - "enhanced": {}, - "notes": "Enhanced Client only. Uses different field layout with separate gender/race bytes." - }, - "notes": "EC clients use this packet instead of 0x00/0xF8. The field layout differs significantly from Classic client versions, with separate gender and race bytes instead of combined genderRace field." - } - ] -} diff --git a/website/packets/incoming/assistant.json b/website/packets/incoming/assistant.json deleted file mode 100644 index f7d83d658..000000000 --- a/website/packets/incoming/assistant.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "category": "Assistant", - "packets": [ - { - "id": "0xBE", - "name": "Assistant Version", - "description": "Client sends assistant version information. Razor CE sends version as ASCII string.", - "direction": "incoming", - "isDynamic": true, - "size": "3+", - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xBE", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Version String", - "type": "ascii", - "size": "var", - "description": "Assistant version string (Razor CE sends \u0027version\u0027 or \u0027ProductName version\u0027)" - } - ], - "notes": "Razor Community Edition sends version as ASCII. Legacy UOAssist would send int32 + ASCII client version.", - "source": { - "file": "Projects/UOContent/Assistants/AssistantHandler.cs", - "line": 69 - } - } - ] -} \ No newline at end of file diff --git a/website/packets/incoming/book.json b/website/packets/incoming/book.json deleted file mode 100644 index 7a8078c18..000000000 --- a/website/packets/incoming/book.json +++ /dev/null @@ -1,216 +0,0 @@ -{ - "category": "Book", - "packets": [ - { - "id": "0x66", - "name": "Book Content Change", - "description": "Client sends updated book page content when player edits a writable book.", - "direction": "incoming", - "isDynamic": true, - "size": "9+", - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x66", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Book Serial", - "type": "uint", - "size": 4, - "description": "Serial of the book being edited" - }, - { - "name": "Page Count", - "type": "ushort", - "size": 2, - "description": "Number of pages being updated" - }, - { - "name": "Pages", - "type": "loop", - "description": "Page contents being updated", - "loop": { - "countField": "pageCount", - "fields": [ - { - "name": "Page Index", - "type": "ushort", - "size": 2, - "description": "Page number (1-indexed)" - }, - { - "name": "Line Count", - "type": "ushort", - "size": 2, - "description": "Number of lines on page (max 8)" - }, - { - "name": "Lines", - "type": "loop", - "description": "Lines on the page", - "loop": { - "countField": "lineCount", - "fields": [ - { - "name": "Text", - "type": "utf8-t", - "description": "Line text (max 80 characters)" - } - ] - } - } - ] - } - } - ], - "source": { - "file": "Projects/UOContent/Items/Books/BookPackets.cs", - "line": 87 - } - }, - { - "id": "0xD4", - "name": "Book Header Change", - "description": "Client sends updated book title and author when player edits a writable book\u0027s cover.", - "direction": "incoming", - "isDynamic": true, - "size": "13+", - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xD4", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Book Serial", - "type": "uint", - "size": 4, - "description": "Serial of the book being edited" - }, - { - "name": "Flags", - "type": "ushort", - "size": 2, - "description": "Book flags (ignored by server)" - }, - { - "name": "Page Count", - "type": "ushort", - "size": 2, - "description": "Number of pages (ignored by server)" - }, - { - "name": "Title Length", - "type": "ushort", - "size": 2, - "description": "Length of title string including null terminator (max 61)" - }, - { - "name": "Title", - "type": "utf8-t", - "description": "Book title (max 60 characters)" - }, - { - "name": "Author Length", - "type": "ushort", - "size": 2, - "description": "Length of author string including null terminator (max 31)" - }, - { - "name": "Author", - "type": "utf8-t", - "description": "Book author (max 30 characters)" - } - ], - "related": [ - { - "id": "0x93", - "direction": "incoming", - "relationship": "variant", - "note": "Old Header Change format for older clients" - } - ], - "source": { - "file": "Projects/UOContent/Items/Books/BookPackets.cs", - "line": 51 - } - }, - { - "id": "0x93", - "name": "Old Book Header Change", - "description": "Legacy format for book header changes. Uses fixed-size ASCII strings.", - "direction": "incoming", - "isDynamic": false, - "size": 99, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x93", - "description": "Packet identifier" - }, - { - "name": "Book Serial", - "type": "uint", - "size": 4, - "description": "Serial of the book being edited" - }, - { - "name": "Flags", - "type": "ushort", - "size": 2, - "description": "Book flags (ignored by server)" - }, - { - "name": "Page Count", - "type": "ushort", - "size": 2, - "description": "Number of pages (ignored by server)" - }, - { - "name": "Title", - "type": "ascii", - "size": 60, - "description": "Book title (fixed 60 bytes, null-padded)" - }, - { - "name": "Author", - "type": "ascii", - "size": 30, - "description": "Book author (fixed 30 bytes, null-padded)" - } - ], - "related": [ - { - "id": "0xD4", - "direction": "incoming", - "relationship": "variant", - "note": "New Header Change format for newer clients" - } - ], - "notes": "Old format used by older clients. Title and author are fixed-size ASCII.", - "source": { - "file": "Projects/UOContent/Items/Books/BookPackets.cs", - "line": 32 - } - } - ] -} \ No newline at end of file diff --git a/website/packets/incoming/bulletinboard.json b/website/packets/incoming/bulletinboard.json deleted file mode 100644 index 9238e5e9c..000000000 --- a/website/packets/incoming/bulletinboard.json +++ /dev/null @@ -1,250 +0,0 @@ -{ - "category": "Bulletin Board", - "packets": [ - { - "id": "0x71", - "subId": "0x03", - "name": "Bulletin Board Request Content", - "description": "Client requests the full content of a bulletin board message.", - "direction": "incoming", - "isDynamic": true, - "size": "12", - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x71", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Command", - "type": "byte", - "size": 1, - "value": "0x03", - "description": "Request Content command" - }, - { - "name": "Board Serial", - "type": "uint", - "size": 4, - "description": "Serial of the bulletin board" - }, - { - "name": "Message Serial", - "type": "uint", - "size": 4, - "description": "Serial of the message to read" - } - ], - "related": [ - { - "id": "0x71", - "subId": "0x02", - "direction": "outgoing", - "relationship": "response", - "note": "Message Content response" - } - ], - "source": { - "file": "Projects/UOContent/Items/Bulletin Boards/BulletinBoardPackets.cs", - "line": 78 - } - }, - { - "id": "0x71", - "subId": "0x04", - "name": "Bulletin Board Request Header", - "description": "Client requests the header (poster, subject, time) of a bulletin board message.", - "direction": "incoming", - "isDynamic": true, - "size": "12", - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x71", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Command", - "type": "byte", - "size": 1, - "value": "0x04", - "description": "Request Header command" - }, - { - "name": "Board Serial", - "type": "uint", - "size": 4, - "description": "Serial of the bulletin board" - }, - { - "name": "Message Serial", - "type": "uint", - "size": 4, - "description": "Serial of the message" - } - ], - "related": [ - { - "id": "0x71", - "subId": "0x01", - "direction": "outgoing", - "relationship": "response", - "note": "Message Header response" - } - ], - "source": { - "file": "Projects/UOContent/Items/Bulletin Boards/BulletinBoardPackets.cs", - "line": 88 - } - }, - { - "id": "0x71", - "subId": "0x05", - "name": "Bulletin Board Post Message", - "description": "Client posts a new message or reply to the bulletin board.", - "direction": "incoming", - "isDynamic": true, - "size": "12+", - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x71", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Command", - "type": "byte", - "size": 1, - "value": "0x05", - "description": "Post Message command" - }, - { - "name": "Board Serial", - "type": "uint", - "size": 4, - "description": "Serial of the bulletin board" - }, - { - "name": "Thread Serial", - "type": "uint", - "size": 4, - "description": "Serial of parent message (0 for new thread)" - }, - { - "name": "Subject Length", - "type": "byte", - "size": 1, - "description": "Length of subject string" - }, - { - "name": "Subject", - "type": "utf8", - "description": "Message subject" - }, - { - "name": "Line Count", - "type": "byte", - "size": 1, - "description": "Number of message lines" - }, - { - "name": "Lines", - "type": "loop", - "description": "Message body lines", - "loop": { - "countField": "Line Count", - "fields": [ - { - "name": "Line Length", - "type": "byte", - "size": 1, - "description": "Length of this line" - }, - { - "name": "Line", - "type": "utf8", - "description": "Line text" - } - ] - } - } - ], - "source": { - "file": "Projects/UOContent/Items/Bulletin Boards/BulletinBoardPackets.cs", - "line": 98 - } - }, - { - "id": "0x71", - "subId": "0x06", - "name": "Bulletin Board Remove Message", - "description": "Client requests to delete a bulletin board message.", - "direction": "incoming", - "isDynamic": true, - "size": "12", - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x71", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Command", - "type": "byte", - "size": 1, - "value": "0x06", - "description": "Remove Message command" - }, - { - "name": "Board Serial", - "type": "uint", - "size": 4, - "description": "Serial of the bulletin board" - }, - { - "name": "Message Serial", - "type": "uint", - "size": 4, - "description": "Serial of the message to delete" - } - ], - "notes": "Only the message poster or GameMaster+ can delete messages.", - "source": { - "file": "Projects/UOContent/Items/Bulletin Boards/BulletinBoardPackets.cs", - "line": 153 - } - } - ] -} diff --git a/website/packets/incoming/chat.json b/website/packets/incoming/chat.json deleted file mode 100644 index a62d2e9ed..000000000 --- a/website/packets/incoming/chat.json +++ /dev/null @@ -1,93 +0,0 @@ -{ - "category": "Chat", - "packets": [ - { - "id": "0xB5", - "name": "Open Chat Window Request", - "description": "Client requests to open the chat window. Newer clients don\u0027t send chat username.", - "direction": "incoming", - "isDynamic": false, - "size": 64, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xB5", - "description": "Packet identifier" - }, - { - "name": "Reserved", - "type": "byte[]", - "size": 63, - "description": "Reserved/unused (newer clients don\u0027t send chat username)" - } - ], - "related": [ - { - "id": "0xB2", - "direction": "outgoing", - "relationship": "response", - "note": "Server responds with Chat Message (OpenChatWindow command)" - } - ], - "source": { - "file": "Projects/UOContent/Engines/Chat/ChatPackets.cs", - "line": 30 - } - }, - { - "id": "0xB3", - "name": "Chat Action", - "description": "Client sends chat action commands like sending messages, joining channels, etc.", - "direction": "incoming", - "isDynamic": true, - "size": "8+", - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xB3", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Language", - "type": "ascii", - "size": 4, - "description": "Language code (e.g., \u0027enu\u0027 for English)" - }, - { - "name": "Action ID", - "type": "short", - "size": 2, - "description": "Chat action ID" - }, - { - "name": "Parameter", - "type": "utf16be", - "size": "var", - "description": "Action parameter (Big Endian Unicode)" - } - ], - "related": [ - { - "id": "0xB2", - "direction": "outgoing", - "relationship": "response", - "note": "Server responds with Chat Message" - } - ], - "source": { - "file": "Projects/UOContent/Engines/Chat/ChatPackets.cs", - "line": 51 - } - } - ] -} \ No newline at end of file diff --git a/website/packets/incoming/encoded.json b/website/packets/incoming/encoded.json deleted file mode 100644 index 3b5cad9db..000000000 --- a/website/packets/incoming/encoded.json +++ /dev/null @@ -1,965 +0,0 @@ -{ - "category": "Encoded Commands (0xD7)", - "packets": [ - { - "id": "0xD7", - "name": "Encoded Command", - "description": "Wrapper packet for encoded commands (0xD7 subpackets). Used for house design, abilities, guild/quest requests.", - "direction": "incoming", - "isDynamic": true, - "size": "var", - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xD7", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Entity Serial", - "type": "uint", - "size": 4, - "description": "Serial of target entity" - }, - { - "name": "Sub-Command", - "type": "ushort", - "size": 2, - "description": "Encoded command ID" - }, - { - "name": "Data", - "type": "byte[]", - "description": "Subcommand-specific data" - } - ], - "subpackets": [ - { - "subId": "0x02", - "name": "Backup", - "description": "Backup current house design state", - "direction": "incoming" - }, - { - "subId": "0x03", - "name": "Restore", - "description": "Restore house design from backup", - "direction": "incoming" - }, - { - "subId": "0x04", - "name": "Commit", - "description": "Commit house design changes", - "direction": "incoming" - }, - { - "subId": "0x05", - "name": "Delete Component", - "description": "Delete component from house", - "direction": "incoming" - }, - { - "subId": "0x06", - "name": "Build Component", - "description": "Place component in house", - "direction": "incoming" - }, - { - "subId": "0x0C", - "name": "Close Tool", - "description": "Close house design tool", - "direction": "incoming" - }, - { - "subId": "0x0D", - "name": "Add Stairs", - "description": "Add stairs to house design", - "direction": "incoming" - }, - { - "subId": "0x0E", - "name": "Sync Request", - "description": "Synchronize house design state", - "direction": "incoming" - }, - { - "subId": "0x10", - "name": "Clear Floor", - "description": "Clear a floor in house design", - "direction": "incoming" - }, - { - "subId": "0x12", - "name": "Change Floor Level", - "description": "Change visible floor level", - "direction": "incoming" - }, - { - "subId": "0x13", - "name": "Add Roof", - "description": "Add roof tile (SE+)", - "direction": "incoming" - }, - { - "subId": "0x14", - "name": "Delete Roof", - "description": "Delete roof tile (SE+)", - "direction": "incoming" - }, - { - "subId": "0x19", - "name": "Set Weapon Ability", - "description": "Select weapon special ability", - "direction": "incoming" - }, - { - "subId": "0x1A", - "name": "Revert", - "description": "Revert house to original state", - "direction": "incoming" - }, - { - "subId": "0x28", - "name": "Guild Gump Request", - "description": "Open guild gump", - "direction": "incoming" - }, - { - "subId": "0x32", - "name": "Quest Gump Request", - "description": "Open quest/MLB gump", - "direction": "incoming" - } - ], - "source": { - "file": "Projects/UOContent/Network/Packets/IncomingPlayerPackets.cs", - "line": 457 - } - }, - { - "id": "0xD7/0x02", - "subId": "0x02", - "name": "House Design: Backup", - "description": "Client requests to backup current house design state.", - "direction": "incoming", - "isDynamic": true, - "size": "var", - "tags": ["House"], - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xD7", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Serial", - "type": "uint", - "size": 4, - "description": "House foundation serial" - }, - { - "name": "Sub-Command", - "type": "ushort", - "size": 2, - "value": "0x02", - "description": "Backup subcommand" - } - ], - "source": { - "file": "Projects/UOContent/Multis/Houses/HouseFoundation.cs", - "line": 1024 - } - }, - { - "id": "0xD7/0x03", - "subId": "0x03", - "name": "House Design: Restore", - "description": "Client requests to restore house design from backup.", - "direction": "incoming", - "isDynamic": true, - "size": "var", - "tags": ["House"], - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xD7", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Serial", - "type": "uint", - "size": 4, - "description": "House foundation serial" - }, - { - "name": "Sub-Command", - "type": "ushort", - "size": 2, - "value": "0x03", - "description": "Restore subcommand" - } - ], - "source": { - "file": "Projects/UOContent/Multis/Houses/HouseFoundation.cs", - "line": 1025 - } - }, - { - "id": "0xD7/0x04", - "subId": "0x04", - "name": "House Design: Commit", - "description": "Client commits the current house design changes.", - "direction": "incoming", - "isDynamic": true, - "size": "var", - "tags": ["House"], - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xD7", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Serial", - "type": "uint", - "size": 4, - "description": "House foundation serial" - }, - { - "name": "Sub-Command", - "type": "ushort", - "size": 2, - "value": "0x04", - "description": "Commit subcommand" - } - ], - "source": { - "file": "Projects/UOContent/Multis/Houses/HouseFoundation.cs", - "line": 1026 - } - }, - { - "id": "0xD7/0x05", - "subId": "0x05", - "name": "House Design: Delete Component", - "description": "Client requests to delete a component from house design.", - "direction": "incoming", - "isDynamic": true, - "size": "var", - "tags": ["House"], - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xD7", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Serial", - "type": "uint", - "size": 4, - "description": "House foundation serial" - }, - { - "name": "Sub-Command", - "type": "ushort", - "size": 2, - "value": "0x05", - "description": "Delete subcommand" - }, - { - "name": "Tile ID", - "type": "int", - "size": 4, - "description": "Tile graphic ID to delete" - }, - { - "name": "X", - "type": "int", - "size": 4, - "description": "X offset from foundation" - }, - { - "name": "Y", - "type": "int", - "size": 4, - "description": "Y offset from foundation" - }, - { - "name": "Z", - "type": "int", - "size": 4, - "description": "Z offset from foundation" - } - ], - "source": { - "file": "Projects/UOContent/Multis/Houses/HouseFoundation.cs", - "line": 1027 - } - }, - { - "id": "0xD7/0x06", - "subId": "0x06", - "name": "House Design: Build Component", - "description": "Client requests to place a component in house design.", - "direction": "incoming", - "isDynamic": true, - "size": "var", - "tags": ["House"], - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xD7", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Serial", - "type": "uint", - "size": 4, - "description": "House foundation serial" - }, - { - "name": "Sub-Command", - "type": "ushort", - "size": 2, - "value": "0x06", - "description": "Build subcommand" - }, - { - "name": "Tile ID", - "type": "int", - "size": 4, - "description": "Tile graphic ID to place" - }, - { - "name": "X", - "type": "int", - "size": 4, - "description": "X offset from foundation" - }, - { - "name": "Y", - "type": "int", - "size": 4, - "description": "Y offset from foundation" - } - ], - "source": { - "file": "Projects/UOContent/Multis/Houses/HouseFoundation.cs", - "line": 1028 - } - }, - { - "id": "0xD7/0x0C", - "subId": "0x0C", - "name": "House Design: Close Tool", - "description": "Client closes the house design tool.", - "direction": "incoming", - "isDynamic": true, - "size": "var", - "tags": ["House"], - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xD7", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Serial", - "type": "uint", - "size": 4, - "description": "House foundation serial" - }, - { - "name": "Sub-Command", - "type": "ushort", - "size": 2, - "value": "0x0C", - "description": "Close subcommand" - } - ], - "source": { - "file": "Projects/UOContent/Multis/Houses/HouseFoundation.cs", - "line": 1029 - } - }, - { - "id": "0xD7/0x0D", - "subId": "0x0D", - "name": "House Design: Add Stairs", - "description": "Client requests to add stairs to house design.", - "direction": "incoming", - "isDynamic": true, - "size": "var", - "tags": ["House"], - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xD7", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Serial", - "type": "uint", - "size": 4, - "description": "House foundation serial" - }, - { - "name": "Sub-Command", - "type": "ushort", - "size": 2, - "value": "0x0D", - "description": "Stairs subcommand" - }, - { - "name": "Tile ID", - "type": "int", - "size": 4, - "description": "Stair tile graphic ID" - }, - { - "name": "X", - "type": "int", - "size": 4, - "description": "X offset from foundation" - }, - { - "name": "Y", - "type": "int", - "size": 4, - "description": "Y offset from foundation" - } - ], - "source": { - "file": "Projects/UOContent/Multis/Houses/HouseFoundation.cs", - "line": 1030 - } - }, - { - "id": "0xD7/0x0E", - "subId": "0x0E", - "name": "House Design: Sync Request", - "description": "Client requests to synchronize house design state.", - "direction": "incoming", - "isDynamic": true, - "size": "var", - "tags": ["House"], - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xD7", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Serial", - "type": "uint", - "size": 4, - "description": "House foundation serial" - }, - { - "name": "Sub-Command", - "type": "ushort", - "size": 2, - "value": "0x0E", - "description": "Sync subcommand" - } - ], - "source": { - "file": "Projects/UOContent/Multis/Houses/HouseFoundation.cs", - "line": 1031 - } - }, - { - "id": "0xD7/0x10", - "subId": "0x10", - "name": "House Design: Clear Floor", - "description": "Client requests to clear a floor in house design.", - "direction": "incoming", - "isDynamic": true, - "size": "var", - "tags": ["House"], - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xD7", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Serial", - "type": "uint", - "size": 4, - "description": "House foundation serial" - }, - { - "name": "Sub-Command", - "type": "ushort", - "size": 2, - "value": "0x10", - "description": "Clear subcommand" - } - ], - "source": { - "file": "Projects/UOContent/Multis/Houses/HouseFoundation.cs", - "line": 1032 - } - }, - { - "id": "0xD7/0x12", - "subId": "0x12", - "name": "House Design: Change Floor Level", - "description": "Client changes the visible floor level in house design.", - "direction": "incoming", - "isDynamic": true, - "size": "var", - "tags": ["House"], - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xD7", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Serial", - "type": "uint", - "size": 4, - "description": "House foundation serial" - }, - { - "name": "Sub-Command", - "type": "ushort", - "size": 2, - "value": "0x12", - "description": "Level subcommand" - }, - { - "name": "Floor", - "type": "int", - "size": 4, - "description": "Floor level (1-4)" - } - ], - "source": { - "file": "Projects/UOContent/Multis/Houses/HouseFoundation.cs", - "line": 1033 - } - }, - { - "id": "0xD7/0x13", - "subId": "0x13", - "name": "House Design: Add Roof", - "description": "Client requests to add a roof tile (Samurai Empire+).", - "direction": "incoming", - "isDynamic": true, - "size": "var", - "tags": ["House"], - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xD7", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Serial", - "type": "uint", - "size": 4, - "description": "House foundation serial" - }, - { - "name": "Sub-Command", - "type": "ushort", - "size": 2, - "value": "0x13", - "description": "Roof subcommand" - }, - { - "name": "Tile ID", - "type": "int", - "size": 4, - "description": "Roof tile graphic ID" - }, - { - "name": "X", - "type": "int", - "size": 4, - "description": "X offset from foundation" - }, - { - "name": "Y", - "type": "int", - "size": 4, - "description": "Y offset from foundation" - }, - { - "name": "Z", - "type": "int", - "size": 4, - "description": "Z offset for roof placement" - } - ], - "clientVersion": { - "classic": { - "min": "4.0.3a" - }, - "enhanced": {}, - "notes": "Added with Samurai Empire expansion" - }, - "source": { - "file": "Projects/UOContent/Multis/Houses/HouseFoundation.cs", - "line": 1035 - } - }, - { - "id": "0xD7/0x14", - "subId": "0x14", - "name": "House Design: Delete Roof", - "description": "Client requests to delete a roof tile (Samurai Empire+).", - "direction": "incoming", - "isDynamic": true, - "size": "var", - "tags": ["House"], - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xD7", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Serial", - "type": "uint", - "size": 4, - "description": "House foundation serial" - }, - { - "name": "Sub-Command", - "type": "ushort", - "size": 2, - "value": "0x14", - "description": "Roof Delete subcommand" - }, - { - "name": "Tile ID", - "type": "int", - "size": 4, - "description": "Roof tile graphic ID" - }, - { - "name": "X", - "type": "int", - "size": 4, - "description": "X offset from foundation" - }, - { - "name": "Y", - "type": "int", - "size": 4, - "description": "Y offset from foundation" - }, - { - "name": "Z", - "type": "int", - "size": 4, - "description": "Z offset of roof to delete" - } - ], - "clientVersion": { - "classic": { - "min": "4.0.3a" - }, - "enhanced": {}, - "notes": "Added with Samurai Empire expansion" - }, - "source": { - "file": "Projects/UOContent/Multis/Houses/HouseFoundation.cs", - "line": 1036 - } - }, - { - "id": "0xD7/0x19", - "subId": "0x19", - "name": "Set Weapon Ability", - "description": "Client selects a weapon special ability.", - "direction": "incoming", - "isDynamic": true, - "size": "var", - "tags": ["Combat"], - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xD7", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Serial", - "type": "uint", - "size": 4, - "description": "Player serial" - }, - { - "name": "Sub-Command", - "type": "ushort", - "size": 2, - "value": "0x19", - "description": "Set Ability subcommand" - }, - { - "name": "Ability Index", - "type": "int", - "size": 4, - "description": "Ability index (0 = clear, 1+ = ability index)" - } - ], - "related": [ - { - "id": "0xBF/0x21", - "relationship": "response", - "note": "Clear Weapon Ability" - }, - { - "id": "0xBF/0x25", - "relationship": "response", - "note": "Toggle Special Ability" - } - ], - "source": { - "file": "Projects/UOContent/Items/Weapons/Abilities/WeaponAbilityPackets.cs", - "line": 11 - } - }, - { - "id": "0xD7/0x1A", - "subId": "0x1A", - "name": "House Design: Revert", - "description": "Client requests to revert house design to original state.", - "direction": "incoming", - "isDynamic": true, - "size": "var", - "tags": ["House"], - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xD7", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Serial", - "type": "uint", - "size": 4, - "description": "House foundation serial" - }, - { - "name": "Sub-Command", - "type": "ushort", - "size": 2, - "value": "0x1A", - "description": "Revert subcommand" - } - ], - "source": { - "file": "Projects/UOContent/Multis/Houses/HouseFoundation.cs", - "line": 1038 - } - }, - { - "id": "0xD7/0x28", - "subId": "0x28", - "name": "Guild Gump Request", - "description": "Client requests to open the guild gump.", - "direction": "incoming", - "isDynamic": true, - "size": "var", - "tags": ["Guild"], - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xD7", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Serial", - "type": "uint", - "size": 4, - "description": "Player serial" - }, - { - "name": "Sub-Command", - "type": "ushort", - "size": 2, - "value": "0x28", - "description": "Guild Gump subcommand" - } - ], - "source": { - "file": "Projects/UOContent/Network/Packets/IncomingPlayerPackets.cs", - "line": 54 - } - }, - { - "id": "0xD7/0x32", - "subId": "0x32", - "name": "Quest Gump Request", - "description": "Client requests to open the quest/MLB gump.", - "direction": "incoming", - "isDynamic": true, - "size": "var", - "tags": ["Quest"], - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xD7", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Serial", - "type": "uint", - "size": 4, - "description": "Player serial" - }, - { - "name": "Sub-Command", - "type": "ushort", - "size": 2, - "value": "0x32", - "description": "Quest Gump subcommand" - } - ], - "source": { - "file": "Projects/UOContent/Network/Packets/IncomingPlayerPackets.cs", - "line": 55 - } - } - ] -} diff --git a/website/packets/incoming/entity.json b/website/packets/incoming/entity.json deleted file mode 100644 index 52f2d32b4..000000000 --- a/website/packets/incoming/entity.json +++ /dev/null @@ -1,142 +0,0 @@ -{ - "category": "Item", - "tags": ["Mobile"], - "packets": [ - { - "id": "0x06", - "name": "Use Request (Double-Click)", - "description": "Sent when the player double-clicks an item or mobile. Can also trigger paperdoll if high bit is set.", - "direction": "incoming", - "isDynamic": false, - "size": 5, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x06", - "description": "Packet identifier" - }, - { - "name": "Serial", - "type": "uint", - "size": 4, - "description": "Serial of the target. If high bit (0x80000000) is set, opens player\u0027s paperdoll instead." - } - ], - "source": { - "file": "Projects/UOContent/Network/Packets/IncomingEntityPackets.cs", - "line": 61 - } - }, - { - "id": "0x09", - "name": "Look Request (Single-Click)", - "description": "Sent when the player single-clicks an item or mobile to see its name/properties.", - "direction": "incoming", - "isDynamic": false, - "size": 5, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x09", - "description": "Packet identifier" - }, - { - "name": "Serial", - "type": "uint", - "size": 4, - "description": "Serial of the target entity" - } - ], - "source": { - "file": "Projects/UOContent/Network/Packets/IncomingEntityPackets.cs", - "line": 105 - } - }, - { - "id": "0xB6", - "name": "Object Help Request", - "description": "Sent when the player requests help/info about an object (Shift+Click or context menu).", - "direction": "incoming", - "isDynamic": false, - "size": 9, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xB6", - "description": "Packet identifier" - }, - { - "name": "Serial", - "type": "uint", - "size": 4, - "description": "Serial of the target entity" - }, - { - "name": "Unknown", - "type": "byte", - "size": 1, - "description": "Unknown" - }, - { - "name": "Language", - "type": "ascii", - "size": 3, - "description": "Language code (e.g., \u0027ENU\u0027)" - } - ], - "source": { - "file": "Projects/UOContent/Network/Packets/IncomingEntityPackets.cs", - "line": 32 - } - }, - { - "id": "0xD6", - "name": "Batch Query Properties", - "description": "Sent by the client to request Object Property Lists (tooltips) for multiple entities at once.", - "direction": "incoming", - "isDynamic": true, - "size": "3 + (4 x entityCount)", - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xD6", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Serials", - "type": "array", - "description": "List of entity serials to query", - "loop": { - "countField": "(length-3)/4", - "fields": [ - { - "name": "Serial", - "type": "uint", - "size": 4, - "description": "Entity serial" - } - ] - } - } - ], - "source": { - "file": "Projects/UOContent/Network/Packets/IncomingEntityPackets.cs", - "line": 154 - } - } - ] -} \ No newline at end of file diff --git a/website/packets/incoming/extended.json b/website/packets/incoming/extended.json deleted file mode 100644 index a4b531bc5..000000000 --- a/website/packets/incoming/extended.json +++ /dev/null @@ -1,1292 +0,0 @@ -{ - "category": "Extended Commands (0xBF)", - "packets": [ - { - "id": "0xBF", - "name": "Extended Command", - "description": "Wrapper packet for extended command subpackets. Used for both client-to-server and server-to-client communication.", - "direction": "both", - "isDynamic": true, - "size": "var", - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xBF", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Sub-Command", - "type": "ushort", - "size": 2, - "description": "Extended command ID" - }, - { - "name": "Data", - "type": "byte[]", - "description": "Subcommand-specific data" - } - ], - "subpackets": [ - { - "subId": "0x04", - "name": "Close Gump", - "description": "Close a generic gump", - "direction": "outgoing" - }, - { - "subId": "0x05", - "name": "Screen Size", - "description": "Client reports screen dimensions", - "direction": "incoming" - }, - { - "subId": "0x06", - "name": "Party Message", - "description": "Party system commands and messages", - "direction": "both" - }, - { - "subId": "0x07", - "name": "Quest Arrow Click", - "description": "Client clicked the quest tracking arrow", - "direction": "incoming" - }, - { - "subId": "0x08", - "name": "Map Change", - "description": "Notify client of map/facet change", - "direction": "outgoing" - }, - { - "subId": "0x09", - "name": "Disarm Request", - "description": "Request to disarm opponent", - "direction": "incoming" - }, - { - "subId": "0x0A", - "name": "Stun Request", - "description": "Request to stun opponent", - "direction": "incoming" - }, - { - "subId": "0x0B", - "name": "Language", - "description": "Client language setting", - "direction": "incoming" - }, - { - "subId": "0x0C", - "name": "Status Bar Close", - "description": "Client closed a status bar", - "direction": "incoming" - }, - { - "subId": "0x0E", - "name": "Animate", - "description": "Request to play animation", - "direction": "incoming" - }, - { - "subId": "0x0F", - "name": "Client Info", - "description": "Client type and flags (Spy on Client)", - "direction": "incoming" - }, - { - "subId": "0x10", - "name": "Query Object Properties", - "description": "Request object property list", - "direction": "incoming" - }, - { - "subId": "0x13", - "name": "Context Menu Request", - "description": "Request context menu for entity", - "direction": "incoming" - }, - { - "subId": "0x14", - "name": "Context Menu Display", - "description": "Display context menu to client", - "direction": "outgoing" - }, - { - "subId": "0x15", - "name": "Context Menu Response", - "description": "Selected context menu option", - "direction": "incoming" - }, - { - "subId": "0x18", - "name": "Map Patches", - "description": "Send map diff/patch information", - "direction": "outgoing" - }, - { - "subId": "0x19", - "name": "Stat Lock Info", - "description": "Stat lock states and bonded status", - "direction": "outgoing" - }, - { - "subId": "0x1A", - "name": "Stat Lock Change", - "description": "Change STR/DEX/INT lock state", - "direction": "incoming" - }, - { - "subId": "0x1B", - "name": "New Spellbook Content", - "description": "Spellbook spell list (AOS+)", - "direction": "outgoing" - }, - { - "subId": "0x1C", - "name": "Cast Spell", - "description": "Cast spell by ID", - "direction": "incoming" - }, - { - "subId": "0x1E", - "name": "Query Design Details", - "description": "Request house design details", - "direction": "incoming" - }, - { - "subId": "0x21", - "name": "Set Weapon Ability", - "description": "Clear weapon ability selection", - "direction": "outgoing" - }, - { - "subId": "0x22", - "name": "Damage", - "description": "Display damage number (old clients)", - "direction": "outgoing" - }, - { - "subId": "0x25", - "name": "Toggle Special Ability", - "description": "Toggle weapon special move icon", - "direction": "outgoing" - }, - { - "subId": "0x26", - "name": "Speed Mode", - "description": "Set movement speed mode", - "direction": "outgoing" - }, - { - "subId": "0x2A", - "name": "Race Change Reply", - "description": "Response to race change confirmation", - "direction": "incoming" - }, - { - "subId": "0x2C", - "name": "Bandage Target", - "description": "Apply bandage to target", - "direction": "incoming" - }, - { - "subId": "0x2D", - "name": "Targeted Spell", - "description": "Cast spell with pre-selected target", - "direction": "incoming" - }, - { - "subId": "0x2E", - "name": "Targeted Skill Use", - "description": "Use skill with pre-selected target", - "direction": "incoming" - }, - { - "subId": "0x30", - "name": "Target By Resource Macro", - "description": "Resource-based targeting macro", - "direction": "incoming" - }, - { - "subId": "0x32", - "name": "Toggle Flying", - "description": "Toggle gargoyle flying mode", - "direction": "incoming" - } - ], - "source": { - "file": "Projects/UOContent/Network/Packets/IncomingExtendedCommandPackets.cs", - "line": 43 - } - }, - { - "id": "0xBF/0x05", - "subId": "0x05", - "name": "Screen Size", - "description": "Client reports screen size.", - "direction": "incoming", - "isDynamic": true, - "size": "var", - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xBF", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Sub-Command", - "type": "ushort", - "size": 2, - "value": "0x05", - "description": "Screen Size subcommand" - }, - { - "name": "Width", - "type": "int", - "size": 4, - "description": "Screen width" - }, - { - "name": "Unknown", - "type": "int", - "size": 4, - "description": "Unknown value" - } - ], - "source": { - "file": "Projects/UOContent/Network/Packets/IncomingExtendedCommandPackets.cs", - "line": 136 - } - }, - { - "id": "0xBF/0x06", - "subId": "0x06", - "name": "Party Message", - "description": "Client sends party-related commands.", - "direction": "incoming", - "isDynamic": true, - "size": "var", - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xBF", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Sub-Command", - "type": "ushort", - "size": 2, - "value": "0x06", - "description": "Party Message subcommand" - }, - { - "name": "Party Command", - "type": "enum", - "enumType": "sequential", - "size": 1, - "description": "Party command type", - "values": [ - { - "value": 1, - "name": "Add Member", - "description": "Add member to party" - }, - { - "value": 2, - "name": "Remove Member", - "description": "Remove member from party" - }, - { - "value": 3, - "name": "Private Message", - "description": "Send private message to party member" - }, - { - "value": 4, - "name": "Public Message", - "description": "Send public message to party" - }, - { - "value": 6, - "name": "Set Can Loot", - "description": "Set loot permission" - }, - { - "value": 8, - "name": "Accept", - "description": "Accept party invitation" - }, - { - "value": 9, - "name": "Decline", - "description": "Decline party invitation" - } - ] - }, - { - "name": "Data", - "type": "byte[]", - "description": "Command-specific data (target serial, message text)" - } - ], - "source": { - "file": "Projects/UOContent/Network/Packets/IncomingExtendedCommandPackets.cs", - "line": 142 - } - }, - { - "id": "0xBF/0x07", - "subId": "0x07", - "name": "Quest Arrow Click", - "description": "Client clicked the quest tracking arrow.", - "direction": "incoming", - "isDynamic": true, - "size": "var", - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xBF", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Sub-Command", - "type": "ushort", - "size": 2, - "value": "0x07", - "description": "Quest Arrow Click subcommand" - }, - { - "name": "Right Click", - "type": "bool", - "size": 1, - "description": "True if right-clicked, false if left-clicked" - } - ], - "related": [ - { - "id": "0xBA", - "relationship": "response", - "note": "Quest Arrow packet" - } - ], - "source": { - "file": "Projects/UOContent/Skills/Tracking/Tracking.cs", - "line": 26 - } - }, - { - "id": "0xBF/0x09", - "subId": "0x09", - "name": "Disarm Request", - "description": "Client requests to disarm opponent.", - "direction": "incoming", - "isDynamic": true, - "size": "var", - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xBF", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Sub-Command", - "type": "ushort", - "size": 2, - "value": "0x09", - "description": "Disarm Request subcommand" - } - ], - "source": { - "file": "Projects/UOContent/Network/Packets/IncomingExtendedCommandPackets.cs", - "line": 289 - } - }, - { - "id": "0xBF/0x0A", - "subId": "0x0A", - "name": "Stun Request", - "description": "Client requests to stun opponent.", - "direction": "incoming", - "isDynamic": true, - "size": "var", - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xBF", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Sub-Command", - "type": "ushort", - "size": 2, - "value": "0x0A", - "description": "Stun Request subcommand" - } - ], - "source": { - "file": "Projects/UOContent/Network/Packets/IncomingExtendedCommandPackets.cs", - "line": 277 - } - }, - { - "id": "0xBF/0x0B", - "subId": "0x0B", - "name": "Language", - "description": "Client sets language preference.", - "direction": "incoming", - "isDynamic": true, - "size": "var", - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xBF", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Sub-Command", - "type": "ushort", - "size": 2, - "value": "0x0B", - "description": "Language subcommand" - }, - { - "name": "Language", - "type": "ascii", - "size": 4, - "description": "Language code (e.g., \u0027ENU\u0027)" - } - ], - "source": { - "file": "Projects/UOContent/Network/Packets/IncomingExtendedCommandPackets.cs", - "line": 343 - } - }, - { - "id": "0xBF/0x0C", - "subId": "0x0C", - "name": "Close Status", - "description": "Client closes a status gump.", - "direction": "incoming", - "isDynamic": true, - "size": "var", - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xBF", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Sub-Command", - "type": "ushort", - "size": 2, - "value": "0x0C", - "description": "Close Status subcommand" - }, - { - "name": "Serial", - "type": "uint", - "size": 4, - "description": "Serial of mobile whose status to close" - } - ], - "source": { - "file": "Projects/UOContent/Network/Packets/IncomingExtendedCommandPackets.cs", - "line": 338 - } - }, - { - "id": "0xBF/0x0E", - "subId": "0x0E", - "name": "Animate", - "description": "Client requests to play an animation.", - "direction": "incoming", - "isDynamic": true, - "size": "var", - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xBF", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Sub-Command", - "type": "ushort", - "size": 2, - "value": "0x0E", - "description": "Animate subcommand" - }, - { - "name": "Action", - "type": "int", - "size": 4, - "description": "Animation action ID (must be in valid list)" - } - ], - "notes": "Only specific animation IDs are allowed for security.", - "source": { - "file": "Projects/UOContent/Network/Packets/IncomingExtendedCommandPackets.cs", - "line": 233 - } - }, - { - "id": "0xBF/0x0F", - "subId": "0x0F", - "name": "Client Info", - "description": "Client type information sent once at login. ModernUO currently ignores this packet data.", - "direction": "incoming", - "isDynamic": true, - "implemented": false, - "size": "var", - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xBF", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Sub-Command", - "type": "ushort", - "size": 2, - "value": "0x000F", - "description": "Client Info subcommand" - }, - { - "name": "Unknown", - "type": "byte", - "size": 1, - "value": "0x0A", - "description": "Unknown (always 0x0A)" - }, - { - "name": "Client Type", - "type": "enum", - "enumType": "sequential", - "size": 4, - "description": "Client type flag (same as character create/login)", - "values": [ - { "value": "0x00", "name": "Classic", "description": "Classic 2D Client" }, - { "value": "0x02", "name": "KR", "description": "Kingdom Reborn Client" }, - { "value": "0x03", "name": "EC", "description": "Enhanced Client (Stygian Abyss)" } - ] - } - ], - "related": [ - { - "id": "0xE1", - "relationship": "related", - "note": "Client Type packet - similar client type information" - } - ], - "source": { - "file": "Projects/UOContent/Network/Packets/IncomingExtendedCommandPackets.cs", - "line": 52 - }, - "notes": "ModernUO registers this packet but ignores its data (calls Empty handler). The client type should match the value in 0xE1 Client Type packet." - }, - { - "id": "0xBF/0x10", - "subId": "0x10", - "name": "Query Properties", - "description": "Client queries Object Property List (tooltip) for an entity.", - "direction": "incoming", - "isDynamic": true, - "size": "var", - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xBF", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Sub-Command", - "type": "ushort", - "size": 2, - "value": "0x10", - "description": "Query Properties subcommand" - }, - { - "name": "Serial", - "type": "uint", - "size": 4, - "description": "Serial of entity to query" - } - ], - "related": [ - { - "id": "0xDC", - "direction": "outgoing", - "relationship": "response", - "note": "OPL Info packet" - } - ], - "source": { - "file": "Projects/UOContent/Network/Packets/IncomingExtendedCommandPackets.cs", - "line": 355 - } - }, - { - "id": "0xBF/0x13", - "subId": "0x13", - "name": "Context Menu Request", - "description": "Client requests context menu for an entity.", - "direction": "incoming", - "isDynamic": true, - "size": "var", - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xBF", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Sub-Command", - "type": "ushort", - "size": 2, - "value": "0x13", - "description": "Context Menu Request subcommand" - }, - { - "name": "Target Serial", - "type": "uint", - "size": 4, - "description": "Serial of entity to show context menu for" - } - ], - "related": [ - { - "id": "0xBF/0x14", - "relationship": "response", - "note": "Context Menu Display packet" - } - ], - "source": { - "file": "Projects/UOContent/Context Menus/ContextMenuSystem.cs", - "line": 101 - } - }, - { - "id": "0xBF/0x15", - "subId": "0x15", - "name": "Context Menu Response", - "description": "Client selects an option from context menu.", - "direction": "incoming", - "isDynamic": true, - "size": "var", - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xBF", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Sub-Command", - "type": "ushort", - "size": 2, - "value": "0x15", - "description": "Context Menu Response subcommand" - }, - { - "name": "Target Serial", - "type": "uint", - "size": 4, - "description": "Serial of entity context menu was shown for" - }, - { - "name": "Index", - "type": "ushort", - "size": 2, - "description": "Index of selected menu option" - } - ], - "related": [ - { - "id": "0xBF/0x13", - "relationship": "request", - "note": "Context Menu Request packet" - } - ], - "source": { - "file": "Projects/UOContent/Context Menus/ContextMenuSystem.cs", - "line": 48 - } - }, - { - "id": "0xBF/0x1A", - "subId": "0x1A", - "name": "Stat Lock Change", - "description": "Client changes stat lock (STR/DEX/INT).", - "direction": "incoming", - "isDynamic": true, - "size": "var", - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xBF", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Sub-Command", - "type": "ushort", - "size": 2, - "value": "0x1A", - "description": "Stat Lock Change subcommand" - }, - { - "name": "Stat", - "type": "enum", - "enumType": "sequential", - "size": 1, - "description": "Which stat to change", - "values": [ - { - "value": 0, - "name": "Strength", - "description": "Strength lock" - }, - { - "value": 1, - "name": "Dexterity", - "description": "Dexterity lock" - }, - { - "value": 2, - "name": "Intelligence", - "description": "Intelligence lock" - } - ] - }, - { - "name": "Lock Value", - "type": "enum", - "enumType": "sequential", - "size": 1, - "description": "New lock state", - "values": [ - { - "value": 0, - "name": "Up", - "description": "Stat gains enabled" - }, - { - "value": 1, - "name": "Down", - "description": "Stat decreases enabled" - }, - { - "value": 2, - "name": "Locked", - "description": "Stat locked" - } - ] - } - ], - "source": { - "file": "Projects/UOContent/Network/Packets/IncomingExtendedCommandPackets.cs", - "line": 301 - } - }, - { - "id": "0xBF/0x1C", - "subId": "0x1C", - "name": "Cast Spell", - "description": "Client casts a spell (optionally from spellbook).", - "direction": "incoming", - "isDynamic": true, - "size": "var", - "tags": ["Spell"], - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xBF", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Sub-Command", - "type": "ushort", - "size": 2, - "value": "0x1C", - "description": "Cast Spell subcommand" - }, - { - "name": "Has Book", - "type": "short", - "size": 2, - "description": "1 if casting from book, 0 otherwise" - }, - { - "name": "Book Serial", - "type": "uint", - "size": 4, - "description": "Serial of spellbook (if hasBook)", - "condition": "hasBook == 1" - }, - { - "name": "Spell ID", - "type": "short", - "size": 2, - "description": "Spell ID + 1 (1-based)" - } - ], - "source": { - "file": "Projects/UOContent/Network/Packets/IncomingExtendedCommandPackets.cs", - "line": 257 - } - }, - { - "id": "0xBF/0x1E", - "subId": "0x1E", - "name": "Query Design Details", - "description": "Client requests house design details.", - "direction": "incoming", - "isDynamic": true, - "size": "var", - "tags": ["House"], - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xBF", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Sub-Command", - "type": "ushort", - "size": 2, - "value": "0x1E", - "description": "Query Design Details subcommand" - }, - { - "name": "House Serial", - "type": "uint", - "size": 4, - "description": "Serial of house foundation" - } - ], - "source": { - "file": "Projects/UOContent/Multis/Houses/HouseFoundation.cs", - "line": 1826 - } - }, - { - "id": "0xBF/0x2A", - "subId": "0x2A", - "name": "Race Change Reply", - "description": "Client response to race change confirmation gump.", - "direction": "incoming", - "isDynamic": true, - "size": "var", - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xBF", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Sub-Command", - "type": "ushort", - "size": 2, - "value": "0x2A", - "description": "Race Change Reply subcommand" - }, - { - "name": "Skin Hue", - "type": "ushort", - "size": 2, - "description": "Selected skin hue", - "condition": "length \u003e 5" - }, - { - "name": "Hair Item ID", - "type": "ushort", - "size": 2, - "description": "Selected hair style", - "condition": "length \u003e 5" - }, - { - "name": "Hair Hue", - "type": "ushort", - "size": 2, - "description": "Selected hair hue", - "condition": "length \u003e 5" - }, - { - "name": "Facial Hair Item ID", - "type": "ushort", - "size": 2, - "description": "Selected facial hair style", - "condition": "length \u003e 5" - }, - { - "name": "Facial Hair Hue", - "type": "ushort", - "size": 2, - "description": "Selected facial hair hue", - "condition": "length \u003e 5" - } - ], - "source": { - "file": "Projects/UOContent/Engines/ML Quests/Gumps/RaceChangeGump.cs", - "line": 199 - } - }, - { - "id": "0xBF/0x2C", - "subId": "0x2C", - "name": "Bandage Target", - "description": "Client uses bandage on a target.", - "direction": "incoming", - "isDynamic": true, - "size": "var", - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xBF", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Sub-Command", - "type": "ushort", - "size": 2, - "value": "0x2C", - "description": "Bandage Target subcommand" - }, - { - "name": "Bandage Serial", - "type": "uint", - "size": 4, - "description": "Serial of the bandage item" - }, - { - "name": "Target Serial", - "type": "uint", - "size": 4, - "description": "Serial of target mobile" - } - ], - "source": { - "file": "Projects/UOContent/Network/Packets/IncomingExtendedCommandPackets.cs", - "line": 387 - } - }, - { - "id": "0xBF/0x2D", - "subId": "0x2D", - "name": "Targeted Spell", - "description": "Client casts spell with pre-selected target.", - "direction": "incoming", - "isDynamic": true, - "size": "var", - "tags": ["Spell"], - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xBF", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Sub-Command", - "type": "ushort", - "size": 2, - "value": "0x2D", - "description": "Targeted Spell subcommand" - }, - { - "name": "Spell ID", - "type": "short", - "size": 2, - "description": "Spell ID + 1 (1-based)" - }, - { - "name": "Target Serial", - "type": "uint", - "size": 4, - "description": "Serial of target entity" - } - ], - "source": { - "file": "Projects/UOContent/Network/Packets/IncomingExtendedCommandPackets.cs", - "line": 422 - } - }, - { - "id": "0xBF/0x2E", - "subId": "0x2E", - "name": "Targeted Skill Use", - "description": "Client uses skill with pre-selected target.", - "direction": "incoming", - "isDynamic": true, - "size": "var", - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xBF", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Sub-Command", - "type": "ushort", - "size": 2, - "value": "0x2E", - "description": "Targeted Skill Use subcommand" - }, - { - "name": "Skill ID", - "type": "short", - "size": 2, - "description": "Skill ID" - }, - { - "name": "Target Serial", - "type": "uint", - "size": 4, - "description": "Serial of target entity" - } - ], - "source": { - "file": "Projects/UOContent/Network/Packets/IncomingExtendedCommandPackets.cs", - "line": 429 - } - }, - { - "id": "0xBF/0x30", - "subId": "0x30", - "name": "Target By Resource Macro", - "description": "Client uses resource-based targeting macro.", - "direction": "incoming", - "isDynamic": true, - "size": "var", - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xBF", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Sub-Command", - "type": "ushort", - "size": 2, - "value": "0x30", - "description": "Target By Resource Macro subcommand" - }, - { - "name": "Item Serial", - "type": "uint", - "size": 4, - "description": "Serial of item" - }, - { - "name": "Resource Type", - "type": "short", - "size": 2, - "description": "Resource type ID" - } - ], - "source": { - "file": "Projects/UOContent/Network/Packets/IncomingExtendedCommandPackets.cs", - "line": 439 - } - }, - { - "id": "0xBF/0x32", - "subId": "0x32", - "name": "Toggle Flying", - "description": "Client toggles gargoyle flying mode.", - "direction": "incoming", - "isDynamic": true, - "size": "var", - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xBF", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Sub-Command", - "type": "ushort", - "size": 2, - "value": "0x32", - "description": "Toggle Flying subcommand" - } - ], - "source": { - "file": "Projects/UOContent/Network/Packets/IncomingExtendedCommandPackets.cs", - "line": 272 - } - } - ] -} diff --git a/website/packets/incoming/freeshard.json b/website/packets/incoming/freeshard.json deleted file mode 100644 index 83e50ccd4..000000000 --- a/website/packets/incoming/freeshard.json +++ /dev/null @@ -1,134 +0,0 @@ -{ - "category": "FreeShard Protocol (0xF1)", - "packets": [ - { - "id": "0xF1", - "name": "FreeShard Protocol", - "description": "Bundle packet for freeshard-specific commands (UOGateway). Sub-command is a single byte.", - "direction": "incoming", - "isDynamic": true, - "size": "var", - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xF1", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Sub-Command", - "type": "byte", - "size": 1, - "description": "Subcommand ID" - }, - { - "name": "Data", - "type": "byte[]", - "description": "Subcommand-specific data" - } - ], - "subpackets": [ - { - "subId": "0xFE", - "name": "Query Compact Shard Stats", - "direction": "incoming" - }, - { - "subId": "0xFF", - "name": "Query Extended Shard Stats", - "direction": "incoming" - } - ], - "source": { - "file": "Projects/UOContent/Network/FreeshardProtocol.cs", - "line": 21 - } - }, - { - "id": "0xF1/0xFE", - "subId": "0xFE", - "name": "Query Compact Shard Stats", - "description": "Query server for compact statistics. Server responds with 0x51.", - "direction": "incoming", - "isDynamic": true, - "size": "var", - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xF1", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Sub-Command", - "type": "byte", - "size": 1, - "value": "0xFE", - "description": "Query Compact Shard Stats subcommand" - } - ], - "notes": "Outgame only. Requires uogateway.enabled configuration.", - "related": [ - { - "id": "0x51", - "relationship": "response", - "note": "Compact Shard Stats response" - } - ], - "source": { - "file": "Projects/UOContent/Network/UOGateway.cs", - "line": 31 - } - }, - { - "id": "0xF1/0xFF", - "subId": "0xFF", - "name": "Query Extended Shard Stats", - "description": "Query server for extended statistics. Server responds with raw UTF-8 string.", - "direction": "incoming", - "isDynamic": true, - "size": "var", - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xF1", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Sub-Command", - "type": "byte", - "size": 1, - "value": "0xFF", - "description": "Query Extended Shard Stats subcommand" - } - ], - "notes": "Outgame only. Requires uogateway.enabled configuration. Response is a raw UTF-8 string (no packet wrapper): 'ModernUO, Name={name}, Age={hours}, Clients={count}, Items={count}, Chars={count}, Mem={kb}K, Ver=2\\0'", - "source": { - "file": "Projects/UOContent/Network/UOGateway.cs", - "line": 32 - } - } - ] -} diff --git a/website/packets/incoming/gump.json b/website/packets/incoming/gump.json deleted file mode 100644 index 932baa998..000000000 --- a/website/packets/incoming/gump.json +++ /dev/null @@ -1,116 +0,0 @@ -{ - "category": "Gump", - "packets": [ - { - "id": "0xB1", - "name": "Gump Response", - "description": "Client response to a generic gump dialog (button press, checkbox/radio selections, text entries).", - "direction": "incoming", - "isDynamic": true, - "size": "var", - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xB1", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Serial", - "type": "uint", - "size": 4, - "description": "Serial from the 0xB0/0xDD gump packet" - }, - { - "name": "Gump ID", - "type": "uint", - "size": 4, - "description": "Gump type ID from the 0xB0/0xDD gump packet" - }, - { - "name": "Button ID", - "type": "uint", - "size": 4, - "description": "ID of button pressed (0 if gump closed without pressing a button)" - }, - { - "name": "Switch Count", - "type": "uint", - "size": 4, - "description": "Number of active switches (checkboxes/radio buttons)" - }, - { - "name": "Switches", - "type": "array", - "description": "Array of switch IDs that are active/checked", - "entries": [ - { - "name": "Switch ID", - "type": "uint", - "size": 4, - "description": "ID of an active switch" - } - ] - }, - { - "name": "Text Entry Count", - "type": "uint", - "size": 4, - "description": "Number of text entries" - }, - { - "name": "Text Entries", - "type": "array", - "description": "Array of text entry responses", - "entries": [ - { - "name": "Entry ID", - "type": "ushort", - "size": 2, - "description": "Text entry field ID" - }, - { - "name": "Text Length", - "type": "ushort", - "size": 2, - "description": "Length of text in characters" - }, - { - "name": "Text", - "type": "unicode-be", - "size": "var", - "description": "UTF-16 BE encoded text (length * 2 bytes, not null-terminated)" - } - ] - } - ], - "related": [ - { - "id": "0xB0", - "relationship": "response", - "note": "Generic Gump (uncompressed) - server sends this to display the gump" - }, - { - "id": "0xDD", - "relationship": "response", - "note": "Compressed Gump - server sends this to display a compressed gump" - } - ], - "clientVersion": { - "classic": {}, - "enhanced": {} - }, - "source": { - "file": "Projects/UOContent/Gumps/Base/GumpSystem.cs", - "line": 31 - } - } - ] -} diff --git a/website/packets/incoming/hardware.json b/website/packets/incoming/hardware.json deleted file mode 100644 index 81bb40be7..000000000 --- a/website/packets/incoming/hardware.json +++ /dev/null @@ -1,188 +0,0 @@ -{ - "category": "Hardware", - "packets": [ - { - "id": "0xD9", - "name": "Hardware Info", - "description": "Client sends hardware and system information. Also known as 'Spy on Client' packet.", - "direction": "incoming", - "isDynamic": false, - "size": 268, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xD9", - "description": "Packet identifier" - }, - { - "name": "Unknown", - "type": "byte", - "size": 1, - "description": "Unknown (1 for pre-4.0.1a, 2 for 4.0.1a+)" - }, - { - "name": "Instance ID", - "type": "int", - "size": 4, - "description": "UO client instance identifier" - }, - { - "name": "OS Major", - "type": "int", - "size": 4, - "description": "Operating system major version" - }, - { - "name": "OS Minor", - "type": "int", - "size": 4, - "description": "Operating system minor version" - }, - { - "name": "OS Revision", - "type": "int", - "size": 4, - "description": "Operating system revision" - }, - { - "name": "CPU Manufacturer", - "type": "byte", - "size": 1, - "description": "CPU manufacturer code" - }, - { - "name": "CPU Family", - "type": "int", - "size": 4, - "description": "CPU family identifier" - }, - { - "name": "CPU Model", - "type": "int", - "size": 4, - "description": "CPU model number" - }, - { - "name": "CPU Clock Speed", - "type": "int", - "size": 4, - "description": "CPU clock speed in MHz" - }, - { - "name": "CPU Quantity", - "type": "byte", - "size": 1, - "description": "Number of CPUs/cores" - }, - { - "name": "Physical Memory", - "type": "int", - "size": 4, - "description": "Physical memory in MB" - }, - { - "name": "Screen Width", - "type": "int", - "size": 4, - "description": "Screen width in pixels" - }, - { - "name": "Screen Height", - "type": "int", - "size": 4, - "description": "Screen height in pixels" - }, - { - "name": "Screen Depth", - "type": "int", - "size": 4, - "description": "Screen color depth in bits" - }, - { - "name": "DirectX Major", - "type": "short", - "size": 2, - "description": "DirectX major version" - }, - { - "name": "DirectX Minor", - "type": "short", - "size": 2, - "description": "DirectX minor version" - }, - { - "name": "Video Card Description", - "type": "unicode-le", - "size": 128, - "description": "Video card description string (64 chars, little-endian UTF-16)" - }, - { - "name": "Video Card Vendor ID", - "type": "int", - "size": 4, - "description": "Video card vendor identifier" - }, - { - "name": "Video Card Device ID", - "type": "int", - "size": 4, - "description": "Video card device identifier" - }, - { - "name": "Video Card Memory", - "type": "int", - "size": 4, - "description": "Video card memory in MB" - }, - { - "name": "Distribution", - "type": "byte", - "size": 1, - "description": "Distribution type" - }, - { - "name": "Clients Running", - "type": "byte", - "size": 1, - "description": "Number of UO clients currently running" - }, - { - "name": "Clients Installed", - "type": "byte", - "size": 1, - "description": "Number of UO clients installed" - }, - { - "name": "Partial Installed", - "type": "byte", - "size": 1, - "description": "Partial installation flag" - }, - { - "name": "Language", - "type": "unicode-le", - "size": 8, - "description": "Language code (4 chars, little-endian UTF-16)" - }, - { - "name": "Unknown2", - "type": "ascii", - "size": 64, - "description": "Unknown data" - } - ], - "clientVersion": { - "classic": {}, - "enhanced": {}, - "notes": "Packet size is 268 bytes (0x10C) for clients 4.0.1a+" - }, - "source": { - "file": "Projects/UOContent/Misc/HardwareInfo.cs", - "line": 93 - }, - "notes": "This packet is sent automatically by the client after login. The server uses this to track hardware information for debugging and statistics purposes." - } - ] -} diff --git a/website/packets/incoming/house.json b/website/packets/incoming/house.json deleted file mode 100644 index bd7aa8e5a..000000000 --- a/website/packets/incoming/house.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "category": "House", - "packets": [ - { - "id": "0xFB", - "name": "Public House Content", - "description": "Client toggles whether to receive content updates for public houses. When enabled, the server sends container contents for publicly accessible houses.", - "direction": "incoming", - "isDynamic": false, - "size": 2, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xFB", - "description": "Packet identifier" - }, - { - "name": "Show Contents", - "type": "bool", - "size": 1, - "description": "0 = hide public house contents, 1 = show public house contents" - } - ], - "clientVersion": { - "classic": {}, - "enhanced": {} - }, - "source": { - "file": "Projects/UOContent/Network/Packets/IncomingHousePackets.cs", - "line": 24 - }, - "notes": "This packet allows clients to opt-in or opt-out of receiving container content data for publicly accessible houses, which can reduce bandwidth for players who don't need to see inside public houses." - } - ] -} diff --git a/website/packets/incoming/items.json b/website/packets/incoming/items.json deleted file mode 100644 index 09bef1230..000000000 --- a/website/packets/incoming/items.json +++ /dev/null @@ -1,298 +0,0 @@ -{ - "category": "Items", - "packets": [ - { - "id": "0x07", - "name": "Lift Request", - "description": "Client requests to pick up an item from the world or a container.", - "direction": "incoming", - "isDynamic": false, - "size": 7, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x07", - "description": "Packet identifier" - }, - { - "name": "Serial", - "type": "uint", - "size": 4, - "description": "Serial of the item to lift" - }, - { - "name": "Amount", - "type": "ushort", - "size": 2, - "description": "Amount to pick up" - } - ], - "related": [ - { - "id": "0x27", - "relationship": "rej", - "note": "Sent if lift is rejected" - } - ], - "source": { - "file": "Projects/UOContent/Network/Packets/IncomingItemPackets.cs", - "line": 35 - } - }, - { - "id": "0x08", - "name": "Drop Request", - "description": "Client requests to drop an item at a location or into a container. Size varies by client version.", - "direction": "incoming", - "isDynamic": false, - "size": "Varies", - "variants": [ - { - "name": "Classic", - "size": 14, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x08", - "description": "Packet identifier" - }, - { - "name": "Serial", - "type": "uint", - "size": 4, - "description": "Serial of item being dropped (ignored)" - }, - { - "name": "X", - "type": "short", - "size": 2, - "description": "Target X coordinate" - }, - { - "name": "Y", - "type": "short", - "size": 2, - "description": "Target Y coordinate" - }, - { - "name": "Z", - "type": "sbyte", - "size": 1, - "description": "Target Z coordinate" - }, - { - "name": "Dest", - "type": "uint", - "size": 4, - "description": "Target container/mobile serial, or 0xFFFFFFFF for world" - } - ] - }, - { - "name": "Container Grid Lines", - "condition": "Client version \u003e= 6.0.1.7 (ContainerGridLines)", - "size": 15, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x08", - "description": "Packet identifier" - }, - { - "name": "Serial", - "type": "uint", - "size": 4, - "description": "Serial of item being dropped (ignored)" - }, - { - "name": "X", - "type": "short", - "size": 2, - "description": "Target X coordinate" - }, - { - "name": "Y", - "type": "short", - "size": 2, - "description": "Target Y coordinate" - }, - { - "name": "Z", - "type": "sbyte", - "size": 1, - "description": "Target Z coordinate" - }, - { - "name": "Grid Location", - "type": "byte", - "size": 1, - "description": "Grid slot position in container" - }, - { - "name": "Dest", - "type": "uint", - "size": 4, - "description": "Target container/mobile serial, or 0xFFFFFFFF for world" - } - ] - } - ], - "clientVersion": { - "classic": {}, - "enhanced": {}, - "notes": "ContainerGridLines (6.0.1.7+) adds gridLocation byte" - }, - "source": { - "file": "Projects/UOContent/Network/Packets/IncomingItemPackets.cs", - "line": 69 - } - }, - { - "id": "0x13", - "name": "Equip Request", - "description": "Client requests to equip a held item on a mobile.", - "direction": "incoming", - "isDynamic": false, - "size": 10, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x13", - "description": "Packet identifier" - }, - { - "name": "Item Serial", - "type": "uint", - "size": 4, - "description": "Serial of item to equip" - }, - { - "name": "Layer", - "type": "byte", - "size": 1, - "description": "Equipment layer" - }, - { - "name": "Mobile Serial", - "type": "uint", - "size": 4, - "description": "Serial of target mobile" - } - ], - "source": { - "file": "Projects/UOContent/Network/Packets/IncomingItemPackets.cs", - "line": 44 - } - }, - { - "id": "0xEC", - "name": "Equip Macro", - "description": "Client requests to equip multiple items from a saved macro.", - "direction": "incoming", - "isDynamic": true, - "size": "var", - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xEC", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Count", - "type": "byte", - "size": 1, - "description": "Number of items to equip" - }, - { - "name": "Items", - "type": "loop", - "loop": { - "countField": "count", - "fields": [ - { - "name": "Serial", - "type": "uint", - "size": 4, - "description": "Serial of item to equip" - } - ] - } - } - ], - "clientVersion": { - "enhanced": {} - }, - "source": { - "file": "Projects/UOContent/Network/Packets/IncomingItemPackets.cs", - "line": 112 - } - }, - { - "id": "0xED", - "name": "Unequip Macro", - "description": "Client requests to unequip items from specific layers via macro.", - "direction": "incoming", - "isDynamic": true, - "size": "var", - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xED", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Count", - "type": "byte", - "size": 1, - "description": "Number of layers to unequip" - }, - { - "name": "Layers", - "type": "loop", - "loop": { - "countField": "count", - "fields": [ - { - "name": "Layer", - "type": "ushort", - "size": 2, - "description": "Layer to unequip from" - } - ] - } - } - ], - "clientVersion": { - "enhanced": {} - }, - "source": { - "file": "Projects/UOContent/Network/Packets/IncomingItemPackets.cs", - "line": 125 - } - } - ] -} \ No newline at end of file diff --git a/website/packets/incoming/mahjong.json b/website/packets/incoming/mahjong.json deleted file mode 100644 index 7703ed530..000000000 --- a/website/packets/incoming/mahjong.json +++ /dev/null @@ -1,907 +0,0 @@ -{ - "category": "Mahjong", - "packets": [ - { - "id": "0xDA", - "name": "Mahjong Game", - "description": "Mahjong game packets for controlling the in-game Mahjong table. Used for both client commands and server updates.", - "direction": "both", - "isDynamic": true, - "size": "var", - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xDA", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Game Serial", - "type": "uint", - "size": 4, - "description": "Serial of the Mahjong game" - }, - { - "name": "Command", - "type": "ushort", - "size": 2, - "description": "Command ID (incoming uses byte prefix + byte command)" - }, - { - "name": "Data", - "type": "byte[]", - "description": "Command-specific data" - } - ], - "subpackets": [ - { - "subId": "0x02", - "name": "Players Info", - "description": "Sends player information for all seats", - "direction": "outgoing" - }, - { - "subId": "0x03", - "name": "Tile Info", - "description": "Sends information about a single tile", - "direction": "outgoing" - }, - { - "subId": "0x04", - "name": "Tiles Info", - "description": "Sends information about all tiles", - "direction": "outgoing" - }, - { - "subId": "0x05", - "name": "General Info", - "description": "Sends game state (dice, dealer, wall break)", - "direction": "outgoing" - }, - { - "subId": "0x06", - "name": "Exit Game", - "description": "Client exits the game", - "direction": "incoming" - }, - { - "subId": "0x0A", - "name": "Give Points", - "description": "Transfer points to another player", - "direction": "incoming" - }, - { - "subId": "0x0B", - "name": "Roll Dice", - "description": "Client requests to roll dice", - "direction": "incoming" - }, - { - "subId": "0x0C", - "name": "Build Walls", - "description": "Dealer builds/resets tile walls", - "direction": "incoming" - }, - { - "subId": "0x0D", - "name": "Reset Scores", - "description": "Dealer resets all scores", - "direction": "incoming" - }, - { - "subId": "0x0F", - "name": "Assign Dealer", - "description": "Assign a new dealer", - "direction": "incoming" - }, - { - "subId": "0x10", - "name": "Open Seat", - "description": "Open a seat for new players", - "direction": "incoming" - }, - { - "subId": "0x11", - "name": "Change Option", - "description": "Change game options", - "direction": "incoming" - }, - { - "subId": "0x15", - "name": "Move Wall Break", - "description": "Move wall break indicator", - "direction": "incoming" - }, - { - "subId": "0x16", - "name": "Toggle Public Hand", - "description": "Toggle hand visibility", - "direction": "incoming" - }, - { - "subId": "0x17", - "name": "Move Tile", - "description": "Move a tile on the board", - "direction": "incoming" - }, - { - "subId": "0x18", - "name": "Move Dealer Indicator", - "description": "Move dealer indicator", - "direction": "incoming" - }, - { - "subId": "0x19", - "name": "Join Game", - "description": "Server opens game interface", - "direction": "outgoing" - }, - { - "subId": "0x1A", - "name": "Relieve", - "description": "Server closes game interface", - "direction": "outgoing" - } - ], - "source": { - "file": "Projects/UOContent/Items/Games/Mahjong/MahjongPackets.cs", - "line": 46 - } - }, - { - "id": "0xDA", - "subId": "0x06", - "name": "Mahjong Exit Game", - "description": "Client requests to exit the Mahjong game.", - "direction": "incoming", - "isDynamic": false, - "size": 9, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xDA", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Game Serial", - "type": "uint", - "size": 4, - "description": "Serial of the Mahjong game" - }, - { - "name": "Unknown", - "type": "byte", - "size": 1, - "description": "Unknown byte" - }, - { - "name": "Command", - "type": "byte", - "size": 1, - "value": "0x06", - "description": "Exit Game command" - } - ], - "source": { - "file": "Projects/UOContent/Items/Games/Mahjong/MahjongPackets.cs", - "line": 108 - } - }, - { - "id": "0xDA", - "subId": "0x0A", - "name": "Mahjong Give Points", - "description": "Client transfers points to another player.", - "direction": "incoming", - "isDynamic": false, - "size": 14, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xDA", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Game Serial", - "type": "uint", - "size": 4, - "description": "Serial of the Mahjong game" - }, - { - "name": "Unknown", - "type": "byte", - "size": 1, - "description": "Unknown byte" - }, - { - "name": "Command", - "type": "byte", - "size": 1, - "value": "0x0A", - "description": "Give Points command" - }, - { - "name": "To Position", - "type": "byte", - "size": 1, - "description": "Target player seat position" - }, - { - "name": "Amount", - "type": "int", - "size": 4, - "description": "Points to transfer" - } - ], - "source": { - "file": "Projects/UOContent/Items/Games/Mahjong/MahjongPackets.cs", - "line": 120 - } - }, - { - "id": "0xDA", - "subId": "0x0B", - "name": "Mahjong Roll Dice", - "description": "Client requests to roll the dice.", - "direction": "incoming", - "isDynamic": false, - "size": 9, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xDA", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Game Serial", - "type": "uint", - "size": 4, - "description": "Serial of the Mahjong game" - }, - { - "name": "Unknown", - "type": "byte", - "size": 1, - "description": "Unknown byte" - }, - { - "name": "Command", - "type": "byte", - "size": 1, - "value": "0x0B", - "description": "Roll Dice command" - } - ], - "source": { - "file": "Projects/UOContent/Items/Games/Mahjong/MahjongPackets.cs", - "line": 133 - } - }, - { - "id": "0xDA", - "subId": "0x0C", - "name": "Mahjong Build Walls", - "description": "Dealer requests to reset and build the tile walls.", - "direction": "incoming", - "isDynamic": false, - "size": 9, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xDA", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Game Serial", - "type": "uint", - "size": 4, - "description": "Serial of the Mahjong game" - }, - { - "name": "Unknown", - "type": "byte", - "size": 1, - "description": "Unknown byte" - }, - { - "name": "Command", - "type": "byte", - "size": 1, - "value": "0x0C", - "description": "Build Walls command" - } - ], - "source": { - "file": "Projects/UOContent/Items/Games/Mahjong/MahjongPackets.cs", - "line": 143 - } - }, - { - "id": "0xDA", - "subId": "0x0D", - "name": "Mahjong Reset Scores", - "description": "Dealer requests to reset all player scores to base value.", - "direction": "incoming", - "isDynamic": false, - "size": 9, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xDA", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Game Serial", - "type": "uint", - "size": 4, - "description": "Serial of the Mahjong game" - }, - { - "name": "Unknown", - "type": "byte", - "size": 1, - "description": "Unknown byte" - }, - { - "name": "Command", - "type": "byte", - "size": 1, - "value": "0x0D", - "description": "Reset Scores command" - } - ], - "source": { - "file": "Projects/UOContent/Items/Games/Mahjong/MahjongPackets.cs", - "line": 153 - } - }, - { - "id": "0xDA", - "subId": "0x0F", - "name": "Mahjong Assign Dealer", - "description": "Dealer assigns another player as the new dealer.", - "direction": "incoming", - "isDynamic": false, - "size": 10, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xDA", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Game Serial", - "type": "uint", - "size": 4, - "description": "Serial of the Mahjong game" - }, - { - "name": "Unknown", - "type": "byte", - "size": 1, - "description": "Unknown byte" - }, - { - "name": "Command", - "type": "byte", - "size": 1, - "value": "0x0F", - "description": "Assign Dealer command" - }, - { - "name": "Position", - "type": "byte", - "size": 1, - "description": "Seat position of new dealer" - } - ], - "source": { - "file": "Projects/UOContent/Items/Games/Mahjong/MahjongPackets.cs", - "line": 163 - } - }, - { - "id": "0xDA", - "subId": "0x10", - "name": "Mahjong Open Seat", - "description": "Dealer opens a seat, removing the current player.", - "direction": "incoming", - "isDynamic": false, - "size": 10, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xDA", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Game Serial", - "type": "uint", - "size": 4, - "description": "Serial of the Mahjong game" - }, - { - "name": "Unknown", - "type": "byte", - "size": 1, - "description": "Unknown byte" - }, - { - "name": "Command", - "type": "byte", - "size": 1, - "value": "0x10", - "description": "Open Seat command" - }, - { - "name": "Position", - "type": "byte", - "size": 1, - "description": "Seat position to open" - } - ], - "source": { - "file": "Projects/UOContent/Items/Games/Mahjong/MahjongPackets.cs", - "line": 175 - } - }, - { - "id": "0xDA", - "subId": "0x11", - "name": "Mahjong Change Option", - "description": "Dealer changes game options (show scores, spectator vision).", - "direction": "incoming", - "isDynamic": false, - "size": 13, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xDA", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Game Serial", - "type": "uint", - "size": 4, - "description": "Serial of the Mahjong game" - }, - { - "name": "Unknown", - "type": "byte", - "size": 1, - "description": "Unknown byte" - }, - { - "name": "Command", - "type": "byte", - "size": 1, - "value": "0x11", - "description": "Change Option command" - }, - { - "name": "Reserved", - "type": "short", - "size": 2, - "description": "Reserved (not used)" - }, - { - "name": "Reserved 2", - "type": "byte", - "size": 1, - "description": "Reserved (not used)" - }, - { - "name": "Options", - "type": "bitflags", - "size": 1, - "description": "Game options", - "values": [ - { "value": "0x01", "name": "Show Scores", "description": "Display player scores" }, - { "value": "0x02", "name": "Spectator Vision", "description": "Spectators can see all tiles" } - ] - } - ], - "source": { - "file": "Projects/UOContent/Items/Games/Mahjong/MahjongPackets.cs", - "line": 192 - } - }, - { - "id": "0xDA", - "subId": "0x15", - "name": "Mahjong Move Wall Break Indicator", - "description": "Dealer moves the wall break indicator position.", - "direction": "incoming", - "isDynamic": false, - "size": 13, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xDA", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Game Serial", - "type": "uint", - "size": 4, - "description": "Serial of the Mahjong game" - }, - { - "name": "Unknown", - "type": "byte", - "size": 1, - "description": "Unknown byte" - }, - { - "name": "Command", - "type": "byte", - "size": 1, - "value": "0x15", - "description": "Move Wall Break Indicator command" - }, - { - "name": "Y Position", - "type": "short", - "size": 2, - "description": "New Y coordinate" - }, - { - "name": "X Position", - "type": "short", - "size": 2, - "description": "New X coordinate" - } - ], - "source": { - "file": "Projects/UOContent/Items/Games/Mahjong/MahjongPackets.cs", - "line": 208 - } - }, - { - "id": "0xDA", - "subId": "0x16", - "name": "Mahjong Toggle Public Hand", - "description": "Player toggles whether their hand is visible to others.", - "direction": "incoming", - "isDynamic": false, - "size": 13, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xDA", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Game Serial", - "type": "uint", - "size": 4, - "description": "Serial of the Mahjong game" - }, - { - "name": "Unknown", - "type": "byte", - "size": 1, - "description": "Unknown byte" - }, - { - "name": "Command", - "type": "byte", - "size": 1, - "value": "0x16", - "description": "Toggle Public Hand command" - }, - { - "name": "Reserved", - "type": "short", - "size": 2, - "description": "Reserved (not used)" - }, - { - "name": "Reserved 2", - "type": "byte", - "size": 1, - "description": "Reserved (not used)" - }, - { - "name": "Public Hand", - "type": "bool", - "size": 1, - "description": "True to show hand publicly" - } - ], - "source": { - "file": "Projects/UOContent/Items/Games/Mahjong/MahjongPackets.cs", - "line": 221 - } - }, - { - "id": "0xDA", - "subId": "0x17", - "name": "Mahjong Move Tile", - "description": "Player moves a tile to a new position with direction and flip state.", - "direction": "incoming", - "isDynamic": false, - "size": 22, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xDA", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Game Serial", - "type": "uint", - "size": 4, - "description": "Serial of the Mahjong game" - }, - { - "name": "Unknown", - "type": "byte", - "size": 1, - "description": "Unknown byte" - }, - { - "name": "Command", - "type": "byte", - "size": 1, - "value": "0x17", - "description": "Move Tile command" - }, - { - "name": "Tile Number", - "type": "byte", - "size": 1, - "description": "Tile index number" - }, - { - "name": "Current Direction", - "type": "byte", - "size": 1, - "description": "Current tile direction (unused)" - }, - { - "name": "New Direction", - "type": "enum", - "size": 1, - "description": "New tile direction", - "values": [ - { "value": "0", "name": "Up", "description": "Facing up" }, - { "value": "1", "name": "Left", "description": "Facing left" }, - { "value": "2", "name": "Down", "description": "Facing down" }, - { "value": "3", "name": "Right", "description": "Facing right" } - ] - }, - { - "name": "Reserved", - "type": "byte", - "size": 1, - "description": "Reserved byte" - }, - { - "name": "Flip", - "type": "bool", - "size": 1, - "description": "True to flip tile face-up" - }, - { - "name": "Current Y", - "type": "short", - "size": 2, - "description": "Current Y position (unused)" - }, - { - "name": "Current X", - "type": "short", - "size": 2, - "description": "Current X position (unused)" - }, - { - "name": "Reserved 2", - "type": "byte", - "size": 1, - "description": "Reserved byte" - }, - { - "name": "New Y", - "type": "short", - "size": 2, - "description": "New Y position" - }, - { - "name": "New X", - "type": "short", - "size": 2, - "description": "New X position" - }, - { - "name": "Reserved 3", - "type": "byte", - "size": 1, - "description": "Reserved byte" - } - ], - "source": { - "file": "Projects/UOContent/Items/Games/Mahjong/MahjongPackets.cs", - "line": 236 - } - }, - { - "id": "0xDA", - "subId": "0x18", - "name": "Mahjong Move Dealer Indicator", - "description": "Dealer moves the dealer indicator with direction and wind.", - "direction": "incoming", - "isDynamic": false, - "size": 15, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xDA", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Game Serial", - "type": "uint", - "size": 4, - "description": "Serial of the Mahjong game" - }, - { - "name": "Unknown", - "type": "byte", - "size": 1, - "description": "Unknown byte" - }, - { - "name": "Command", - "type": "byte", - "size": 1, - "value": "0x18", - "description": "Move Dealer Indicator command" - }, - { - "name": "Direction", - "type": "enum", - "size": 1, - "description": "Indicator direction", - "values": [ - { "value": "0", "name": "Up", "description": "Facing up" }, - { "value": "1", "name": "Left", "description": "Facing left" }, - { "value": "2", "name": "Down", "description": "Facing down" }, - { "value": "3", "name": "Right", "description": "Facing right" } - ] - }, - { - "name": "Wind", - "type": "enum", - "size": 1, - "description": "Wind direction displayed", - "values": [ - { "value": "0", "name": "North", "description": "North wind" }, - { "value": "1", "name": "East", "description": "East wind" }, - { "value": "2", "name": "South", "description": "South wind" }, - { "value": "3", "name": "West", "description": "West wind" } - ] - }, - { - "name": "Y Position", - "type": "short", - "size": 2, - "description": "New Y position" - }, - { - "name": "X Position", - "type": "short", - "size": 2, - "description": "New X position" - } - ], - "source": { - "file": "Projects/UOContent/Items/Games/Mahjong/MahjongPackets.cs", - "line": 271 - } - } - ] -} diff --git a/website/packets/incoming/map.json b/website/packets/incoming/map.json deleted file mode 100644 index 2e8565423..000000000 --- a/website/packets/incoming/map.json +++ /dev/null @@ -1,84 +0,0 @@ -{ - "category": "Map", - "packets": [ - { - "id": "0x56", - "name": "Map Command", - "description": "Client sends map pin commands for editable map items. Commands include adding, inserting, changing, and removing pins.", - "direction": "incoming", - "isDynamic": false, - "size": 11, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x56", - "description": "Packet identifier" - }, - { - "name": "Map Serial", - "type": "uint", - "size": 4, - "description": "Serial of the map item" - }, - { - "name": "Command", - "type": "enum", - "size": 1, - "description": "Map pin command type", - "values": [ - { "value": "1", "name": "AddPin", "description": "Add a new pin" }, - { "value": "2", "name": "InsertPin", "description": "Insert pin at position" }, - { "value": "3", "name": "ChangePin", "description": "Change existing pin" }, - { "value": "4", "name": "RemovePin", "description": "Remove a pin" }, - { "value": "5", "name": "ClearPins", "description": "Clear all pins" }, - { "value": "6", "name": "ToggleEditable", "description": "Toggle edit mode" } - ] - }, - { - "name": "Number", - "type": "byte", - "size": 1, - "description": "Pin number (for insert, change, remove commands)" - }, - { - "name": "X", - "type": "short", - "size": 2, - "description": "X coordinate of pin" - }, - { - "name": "Y", - "type": "short", - "size": 2, - "description": "Y coordinate of pin" - } - ], - "related": [ - { - "id": "0x56", - "direction": "outgoing", - "relationship": "related", - "note": "Server map pin commands" - }, - { - "id": "0x90", - "direction": "outgoing", - "relationship": "related", - "note": "Map Details packet (old)" - }, - { - "id": "0xF5", - "direction": "outgoing", - "relationship": "related", - "note": "Map Details packet (new)" - } - ], - "source": { - "file": "Projects/UOContent/Items/Maps/MapItemPackets.cs", - "line": 28 - } - } - ] -} \ No newline at end of file diff --git a/website/packets/incoming/message.json b/website/packets/incoming/message.json deleted file mode 100644 index 4dc5fc429..000000000 --- a/website/packets/incoming/message.json +++ /dev/null @@ -1,300 +0,0 @@ -{ - "category": "Message", - "packets": [ - { - "id": "0x03", - "name": "ASCII Speech", - "description": "Client sends ASCII-encoded speech message.", - "direction": "incoming", - "isDynamic": true, - "size": "var", - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x03", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Type", - "type": "enum", - "enumType": "sequential", - "size": 1, - "description": "Message type", - "values": [ - { - "value": 0, - "name": "Regular", - "description": "Normal speech" - }, - { - "value": 1, - "name": "System", - "description": "System message" - }, - { - "value": 2, - "name": "Emote", - "description": "Emote (*action*)" - }, - { - "value": 6, - "name": "Label", - "description": "Object label" - }, - { - "value": 7, - "name": "Focus", - "description": "Focused message" - }, - { - "value": 8, - "name": "Whisper", - "description": "Whisper" - }, - { - "value": 9, - "name": "Yell", - "description": "Yell" - }, - { - "value": 10, - "name": "Spell", - "description": "Spell words" - }, - { - "value": 13, - "name": "Guild", - "description": "Guild chat" - }, - { - "value": 14, - "name": "Alliance", - "description": "Alliance chat" - }, - { - "value": 15, - "name": "Command", - "description": "Command" - } - ] - }, - { - "name": "Hue", - "type": "short", - "size": 2, - "description": "Text color hue" - }, - { - "name": "Font", - "type": "short", - "size": 2, - "description": "Font ID" - }, - { - "name": "Text", - "type": "ascii-t", - "description": "Speech text (max 128 chars)" - } - ], - "related": [ - { - "id": "0x1C", - "relationship": "response", - "note": "ASCII Message sent to other clients" - } - ], - "source": { - "file": "Projects/UOContent/Network/Packets/IncomingMessagePackets.cs", - "line": 31 - } - }, - { - "id": "0xAD", - "name": "Unicode Speech", - "description": "Client sends Unicode-encoded speech message. Type byte upper bits (0xC0) indicate if keywords are encoded.", - "direction": "incoming", - "isDynamic": true, - "size": "Varies", - "variants": [ - { - "name": "Standard Unicode", - "condition": "type & 0xC0 == 0", - "size": "12+", - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xAD", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Type", - "type": "enum", - "size": 1, - "description": "Message type (0xC0 bits clear)", - "values": [ - { - "value": 0, - "name": "Regular", - "description": "Normal speech" - }, - { - "value": 1, - "name": "System", - "description": "System message" - }, - { - "value": 2, - "name": "Emote", - "description": "Emote (*action*)" - }, - { - "value": 8, - "name": "Whisper", - "description": "Whisper" - }, - { - "value": 9, - "name": "Yell", - "description": "Yell" - }, - { - "value": 13, - "name": "Guild", - "description": "Guild chat" - }, - { - "value": 14, - "name": "Alliance", - "description": "Alliance chat" - }, - { - "value": 15, - "name": "Command", - "description": "Command" - } - ] - }, - { - "name": "Hue", - "type": "short", - "size": 2, - "description": "Text color hue" - }, - { - "name": "Font", - "type": "short", - "size": 2, - "description": "Font ID" - }, - { - "name": "Language", - "type": "ascii", - "size": 4, - "description": "Language code (e.g., 'ENU')" - }, - { - "name": "Text", - "type": "utf16be-t", - "description": "Speech text (Big Endian Unicode, null-terminated)" - } - ] - }, - { - "name": "Encoded Keywords", - "condition": "type & 0xC0 != 0", - "size": "14+", - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xAD", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Type", - "type": "byte", - "size": 1, - "description": "Message type with 0xC0 flag set. Lower 4 bits = message type (see Standard variant)" - }, - { - "name": "Hue", - "type": "short", - "size": 2, - "description": "Text color hue" - }, - { - "name": "Font", - "type": "short", - "size": 2, - "description": "Font ID" - }, - { - "name": "Language", - "type": "ascii", - "size": 4, - "description": "Language code (e.g., 'ENU')" - }, - { - "name": "Keyword Header", - "type": "ushort", - "size": 2, - "description": "Bits 15-4: keyword count (0-50), Bits 3-0: first hold value for packed IDs" - }, - { - "name": "Packed Keywords", - "type": "byte[]", - "description": "12-bit keyword IDs packed alternating: even indices use hold<<8|byte, odd indices use (short&0xFFF0)>>4 with new hold" - }, - { - "name": "Text", - "type": "utf8-t", - "description": "Speech text (UTF-8, null-terminated)" - } - ] - } - ], - "notes": "Encoded keywords use 12-bit speech IDs packed efficiently. Keyword count must be 0-50. The packing alternates: even-indexed keywords combine the previous 4-bit hold with a new byte (12 bits total), odd-indexed keywords read a short and extract bits 15-4 as the ID, bits 3-0 as the next hold.", - "related": [ - { - "id": "0x03", - "direction": "incoming", - "relationship": "variant", - "note": "ASCII Speech (simpler format)" - }, - { - "id": "0xAE", - "direction": "outgoing", - "relationship": "response", - "note": "Unicode Message sent to other clients" - } - ], - "source": { - "file": "Projects/UOContent/Network/Packets/IncomingMessagePackets.cs", - "line": 58 - } - } - ] -} diff --git a/website/packets/incoming/mobile.json b/website/packets/incoming/mobile.json deleted file mode 100644 index 9cea2c944..000000000 --- a/website/packets/incoming/mobile.json +++ /dev/null @@ -1,168 +0,0 @@ -{ - "category": "Mobile", - "packets": [ - { - "id": "0x75", - "name": "Rename Request", - "description": "Client requests to rename a mobile (pet).", - "direction": "incoming", - "isDynamic": false, - "size": 35, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x75", - "description": "Packet identifier" - }, - { - "name": "Target Serial", - "type": "uint", - "size": 4, - "description": "Serial of mobile to rename" - }, - { - "name": "New Name", - "type": "ascii", - "size": 30, - "description": "New name for the mobile" - } - ], - "source": { - "file": "Projects/UOContent/Network/Packets/IncomingMobilePackets.cs", - "line": 32 - } - }, - { - "id": "0x98", - "name": "Mobile Name Request", - "description": "Client requests the name of a mobile.", - "direction": "incoming", - "isDynamic": true, - "size": "var", - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x98", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Mobile Serial", - "type": "uint", - "size": 4, - "description": "Serial of mobile to query" - } - ], - "source": { - "file": "Projects/UOContent/Network/Packets/IncomingMobilePackets.cs", - "line": 43 - } - }, - { - "id": "0xB8", - "name": "Profile Request", - "description": "Client requests to view or edit a mobile's profile.", - "direction": "incoming", - "isDynamic": true, - "size": "Varies", - "variants": [ - { - "name": "Display Profile", - "condition": "mode == Display (0)", - "size": 8, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xB8", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Mode", - "type": "byte", - "size": 1, - "value": "0", - "description": "Display mode" - }, - { - "name": "Mobile Serial", - "type": "uint", - "size": 4, - "description": "Serial of target mobile" - } - ] - }, - { - "name": "Edit Profile", - "condition": "mode == Edit (1)", - "size": "12+", - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xB8", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Mode", - "type": "byte", - "size": 1, - "value": "1", - "description": "Edit mode" - }, - { - "name": "Mobile Serial", - "type": "uint", - "size": 4, - "description": "Serial of target mobile" - }, - { - "name": "Padding", - "type": "short", - "size": 2, - "description": "Unused padding" - }, - { - "name": "Text Length", - "type": "ushort", - "size": 2, - "description": "Length of text in characters" - }, - { - "name": "Text", - "type": "utf16be", - "description": "New profile text (Big Endian Unicode)" - } - ] - } - ], - "source": { - "file": "Projects/UOContent/Network/Packets/IncomingMobilePackets.cs", - "line": 53 - } - } - ] -} \ No newline at end of file diff --git a/website/packets/incoming/movement.json b/website/packets/incoming/movement.json deleted file mode 100644 index d776d3382..000000000 --- a/website/packets/incoming/movement.json +++ /dev/null @@ -1,56 +0,0 @@ -{ - "category": "Movement", - "packets": [ - { - "id": "0x02", - "name": "Movement Request", - "description": "Sent by the client when the player attempts to move in a direction. The server responds with either MovementAck (0x22) if successful or MovementRej (0x21) if blocked.", - "direction": "incoming", - "isDynamic": false, - "size": 7, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x02", - "description": "Packet identifier" - }, - { - "name": "Direction", - "type": "byte", - "size": 1, - "description": "Movement direction (0-7). Bit 0x80 indicates running." - }, - { - "name": "Sequence", - "type": "byte", - "size": 1, - "description": "Movement sequence number (1-255, wraps). Used for ack/rej matching." - }, - { - "name": "Fastwalk Key", - "type": "uint", - "size": 4, - "description": "Anti-speedhack prevention key. Set to 0 if fastwalk prevention is disabled." - } - ], - "related": [ - { - "id": "0x21", - "relationship": "rej", - "note": "Sent if movement is blocked" - }, - { - "id": "0x22", - "relationship": "ack", - "note": "Sent if movement is successful" - } - ], - "source": { - "file": "Projects/UOContent/Network/Packets/IncomingMovementPackets.cs", - "line": 83 - } - } - ] -} diff --git a/website/packets/incoming/player.json b/website/packets/incoming/player.json deleted file mode 100644 index 4596bb776..000000000 --- a/website/packets/incoming/player.json +++ /dev/null @@ -1,989 +0,0 @@ -{ - "category": "Player", - "packets": [ - { - "id": "0x01", - "name": "Disconnect", - "description": "Client sends disconnect notification.", - "direction": "incoming", - "isDynamic": false, - "size": 5, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x01", - "description": "Packet identifier" - }, - { - "name": "Minus One", - "type": "int", - "size": 4, - "value": "0xFFFFFFFF", - "description": "Always -1" - } - ], - "source": { - "file": "Projects/UOContent/Network/Packets/IncomingPlayerPackets.cs", - "line": 317 - } - }, - { - "id": "0x05", - "name": "Attack Request", - "description": "Client requests to attack a mobile.", - "direction": "incoming", - "isDynamic": false, - "size": 5, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x05", - "description": "Packet identifier" - }, - { - "name": "Target Serial", - "type": "uint", - "size": 4, - "description": "Serial of mobile to attack" - } - ], - "source": { - "file": "Projects/UOContent/Network/Packets/IncomingPlayerPackets.cs", - "line": 69 - } - }, - { - "id": "0x12", - "name": "Text Command", - "description": "Client sends text-based commands for skills, spells, virtues, etc.", - "direction": "incoming", - "isDynamic": true, - "size": "var", - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x12", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Command Type", - "type": "enum", - "enumType": "sequential", - "size": 1, - "description": "Type of text command", - "values": [ - { - "value": "0x24", - "name": "Use Skill", - "description": "Use a skill by ID" - }, - { - "value": "0x27", - "name": "Cast Spell From Book", - "description": "Cast spell from spellbook" - }, - { - "value": "0x2F", - "name": "Old Scroll Click", - "description": "Old scroll double-click" - }, - { - "value": "0x43", - "name": "Open Spellbook", - "description": "Open spellbook of type" - }, - { - "value": "0x56", - "name": "Cast Spell Macro", - "description": "Cast spell from macro" - }, - { - "value": "0x58", - "name": "Open Door", - "description": "Open door macro" - }, - { - "value": "0xC7", - "name": "Animate", - "description": "Play animation" - }, - { - "value": "0xF4", - "name": "Invoke Virtue", - "description": "Invoke virtue from macro" - } - ] - }, - { - "name": "Command", - "type": "ascii-t", - "description": "Command-specific text (skill ID, spell ID, etc.)" - } - ], - "source": { - "file": "Projects/UOContent/Network/Packets/IncomingPlayerPackets.cs", - "line": 119 - } - }, - { - "id": "0x22", - "name": "Resynchronize", - "description": "Client requests full world state resync.", - "direction": "incoming", - "noMerge": true, - "isDynamic": false, - "size": 3, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x22", - "description": "Packet identifier" - }, - { - "name": "Padding", - "type": "ushort", - "size": 2, - "description": "Unused padding" - } - ], - "notes": "Not related to outgoing 0x22 (Movement Acknowledgment) despite sharing the same packet ID.", - "source": { - "file": "Projects/UOContent/Network/Packets/IncomingPlayerPackets.cs", - "line": 349 - } - }, - { - "id": "0x2C", - "name": "Death Status Response", - "description": "Client acknowledges death status packet. Currently ignored by server.", - "direction": "incoming", - "isDynamic": false, - "size": 2, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x2C", - "description": "Packet identifier" - }, - { - "name": "Unknown", - "type": "byte", - "size": 1, - "description": "Unknown" - } - ], - "source": { - "file": "Projects/UOContent/Network/Packets/IncomingPlayerPackets.cs", - "line": 58 - } - }, - { - "id": "0x34", - "name": "Mobile Query", - "description": "Client queries information about a mobile (stats or skills).", - "direction": "incoming", - "isDynamic": false, - "size": 10, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x34", - "description": "Packet identifier" - }, - { - "name": "Pattern", - "type": "uint", - "size": 4, - "value": "0xEDEDEDED", - "description": "Fixed pattern" - }, - { - "name": "Query Type", - "type": "enum", - "enumType": "sequential", - "size": 1, - "description": "Type of query", - "values": [ - { - "value": "0x04", - "name": "Stats", - "description": "Request mobile stats" - }, - { - "value": "0x05", - "name": "Skills", - "description": "Request mobile skills" - } - ] - }, - { - "name": "Mobile Serial", - "type": "uint", - "size": 4, - "description": "Serial of mobile to query" - } - ], - "related": [ - { - "id": "0x11", - "relationship": "response", - "note": "Mobile Status packet sent in response to stats query" - }, - { - "id": "0x3A", - "relationship": "response", - "note": "Skills Update sent in response to skills query" - } - ], - "source": { - "file": "Projects/UOContent/Network/Packets/IncomingPlayerPackets.cs", - "line": 376 - } - }, - { - "id": "0x3A", - "name": "Change Skill Lock", - "description": "Client changes the lock state of a skill.", - "direction": "incoming", - "isDynamic": true, - "size": "var", - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x3A", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Skill Index", - "type": "short", - "size": 2, - "description": "Skill index to change" - }, - { - "name": "Lock State", - "type": "enum", - "enumType": "sequential", - "size": 1, - "description": "New lock state", - "values": [ - { - "value": 0, - "name": "Up", - "description": "Skill gains enabled" - }, - { - "value": 1, - "name": "Down", - "description": "Skill decreases enabled" - }, - { - "value": 2, - "name": "Locked", - "description": "Skill locked at current value" - } - ] - } - ], - "source": { - "file": "Projects/UOContent/Network/Packets/IncomingPlayerPackets.cs", - "line": 331 - } - }, - { - "id": "0x72", - "name": "Set War Mode", - "description": "Client toggles war/peace mode.", - "direction": "incoming", - "isDynamic": false, - "size": 5, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x72", - "description": "Packet identifier" - }, - { - "name": "Warmode", - "type": "bool", - "size": 1, - "description": "True = war mode, False = peace mode" - }, - { - "name": "Padding", - "type": "byte", - "size": 3, - "description": "Unused padding (0x00, 0x32, 0x00)" - } - ], - "source": { - "file": "Projects/UOContent/Network/Packets/IncomingPlayerPackets.cs", - "line": 343 - } - }, - { - "id": "0x73", - "name": "Ping Request", - "description": "Client sends ping for latency measurement.", - "direction": "incoming", - "isDynamic": false, - "size": 2, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x73", - "description": "Packet identifier" - }, - { - "name": "Sequence", - "type": "byte", - "size": 1, - "description": "Ping sequence number" - } - ], - "source": { - "file": "Projects/UOContent/Network/Packets/IncomingPlayerPackets.cs", - "line": 366 - } - }, - { - "id": "0x7D", - "name": "Menu Response", - "description": "Client responds to a displayed item menu.", - "direction": "incoming", - "isDynamic": false, - "size": 13, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x7D", - "description": "Packet identifier" - }, - { - "name": "Serial", - "type": "uint", - "size": 4, - "description": "Menu dialog serial" - }, - { - "name": "Menu ID", - "type": "short", - "size": 2, - "description": "Menu ID (unused in implementation)" - }, - { - "name": "Index", - "type": "short", - "size": 2, - "description": "Selected item index (1-based, 0 = cancel)" - }, - { - "name": "Item ID", - "type": "short", - "size": 2, - "description": "Item graphic ID of selection" - }, - { - "name": "Hue", - "type": "short", - "size": 2, - "description": "Hue of selection" - } - ], - "related": [ - { - "id": "0x7C", - "relationship": "request", - "note": "Display Item List Menu packet" - } - ], - "source": { - "file": "Projects/UOContent/Network/Packets/IncomingPlayerPackets.cs", - "line": 287 - } - }, - { - "id": "0x95", - "name": "Hue Picker Response", - "description": "Client responds to a hue picker dialog.", - "direction": "incoming", - "isDynamic": false, - "size": 9, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x95", - "description": "Packet identifier" - }, - { - "name": "Serial", - "type": "uint", - "size": 4, - "description": "Hue picker serial" - }, - { - "name": "Item ID", - "type": "short", - "size": 2, - "description": "Item ID (unused)" - }, - { - "name": "Hue", - "type": "short", - "size": 2, - "description": "Selected hue (masked with 0x3FFF)" - } - ], - "source": { - "file": "Projects/UOContent/Network/Packets/IncomingPlayerPackets.cs", - "line": 86 - } - }, - { - "id": "0x9A", - "name": "ASCII Prompt Response", - "description": "Client responds to an ASCII text prompt.", - "direction": "incoming", - "isDynamic": true, - "size": "var", - "tags": ["Menu"], - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x9A", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Serial", - "type": "uint", - "size": 4, - "description": "Prompt serial" - }, - { - "name": "Prompt ID", - "type": "int", - "size": 4, - "description": "Prompt ID" - }, - { - "name": "Type", - "type": "int", - "size": 4, - "description": "Response type: 0 = cancel, other = submit" - }, - { - "name": "Text", - "type": "ascii-t", - "description": "Response text (max 128 chars)" - } - ], - "related": [ - { - "id": "0xC2", - "relationship": "request", - "note": "Unicode Prompt packet that triggered this response" - } - ], - "source": { - "file": "Projects/UOContent/Network/Packets/IncomingPlayerPackets.cs", - "line": 214 - } - }, - { - "id": "0x9B", - "name": "Help Request", - "description": "Client requests help/GM assistance.", - "direction": "incoming", - "isDynamic": false, - "size": 258, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x9B", - "description": "Packet identifier" - }, - { - "name": "Data", - "type": "byte[]", - "size": 257, - "description": "Help request data (format TBD)" - } - ], - "source": { - "file": "Projects/UOContent/Network/Packets/IncomingPlayerPackets.cs", - "line": 338 - } - }, - { - "id": "0xA4", - "name": "System Info", - "description": "Client sends system/hardware information.", - "direction": "incoming", - "isDynamic": false, - "size": 149, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xA4", - "description": "Packet identifier" - }, - { - "name": "Unknown1", - "type": "byte", - "size": 1, - "description": "Unknown" - }, - { - "name": "Unknown2", - "type": "ushort", - "size": 2, - "description": "Unknown" - }, - { - "name": "Unknown3", - "type": "byte", - "size": 1, - "description": "Unknown" - }, - { - "name": "String1", - "type": "ascii", - "size": 32, - "description": "System string 1" - }, - { - "name": "String2", - "type": "ascii", - "size": 32, - "description": "System string 2" - }, - { - "name": "String3", - "type": "ascii", - "size": 32, - "description": "System string 3" - }, - { - "name": "String4", - "type": "ascii", - "size": 32, - "description": "System string 4" - }, - { - "name": "Unknown4", - "type": "ushort", - "size": 2, - "description": "Unknown" - }, - { - "name": "Unknown5", - "type": "ushort", - "size": 2, - "description": "Unknown" - }, - { - "name": "Unknown6", - "type": "int", - "size": 4, - "description": "Unknown" - }, - { - "name": "Unknown7", - "type": "int", - "size": 4, - "description": "Unknown" - }, - { - "name": "Unknown8", - "type": "int", - "size": 4, - "description": "Unknown" - } - ], - "source": { - "file": "Projects/UOContent/Network/Packets/IncomingPlayerPackets.cs", - "line": 103 - } - }, - { - "id": "0xA7", - "name": "Request Scroll Window", - "description": "Client requests a tips/scroll window.", - "direction": "incoming", - "isDynamic": false, - "size": 4, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xA7", - "description": "Packet identifier" - }, - { - "name": "Last Tip", - "type": "short", - "size": 2, - "description": "Last tip ID viewed" - }, - { - "name": "Type", - "type": "byte", - "size": 1, - "description": "Scroll type" - } - ], - "source": { - "file": "Projects/UOContent/Network/Packets/IncomingPlayerPackets.cs", - "line": 63 - } - }, - { - "id": "0xC2", - "name": "Unicode Prompt Response", - "description": "Client responds to a Unicode text prompt.", - "direction": "incoming", - "isDynamic": true, - "size": "var", - "tags": ["Menu"], - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xC2", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Serial", - "type": "uint", - "size": 4, - "description": "Prompt serial" - }, - { - "name": "Prompt ID", - "type": "int", - "size": 4, - "description": "Prompt ID" - }, - { - "name": "Type", - "type": "int", - "size": 4, - "description": "Response type: 0 = cancel, other = submit" - }, - { - "name": "Language", - "type": "ascii", - "size": 4, - "description": "Language code (e.g., \u0027ENU\u0027)" - }, - { - "name": "Text", - "type": "utf16le-t", - "description": "Response text (max 128 chars)" - } - ], - "related": [ - { - "id": "0xC2", - "direction": "outgoing", - "relationship": "request", - "note": "Unicode Prompt packet that triggered this response" - } - ], - "source": { - "file": "Projects/UOContent/Network/Packets/IncomingPlayerPackets.cs", - "line": 250 - } - }, - { - "id": "0xC8", - "name": "Set Update Range", - "description": "Client requests to change update range. Server ignores and sends back fixed range.", - "direction": "incoming", - "isDynamic": false, - "size": 2, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xC8", - "description": "Packet identifier" - }, - { - "name": "Range", - "type": "byte", - "size": 1, - "description": "Requested update range (ignored)" - } - ], - "source": { - "file": "Projects/UOContent/Network/Packets/IncomingPlayerPackets.cs", - "line": 371 - } - }, - { - "id": "0xD0", - "name": "Configuration File", - "description": "Client sends configuration data. Currently ignored by server.", - "direction": "incoming", - "isDynamic": true, - "size": "var", - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xD0", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Data", - "type": "byte[]", - "description": "Configuration data (ignored)" - } - ], - "source": { - "file": "Projects/UOContent/Network/Packets/IncomingPlayerPackets.cs", - "line": 322 - } - }, - { - "id": "0xD1", - "name": "Logout Request", - "description": "Client requests to logout.", - "direction": "incoming", - "isDynamic": false, - "size": 2, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xD1", - "description": "Packet identifier" - }, - { - "name": "Unknown", - "type": "byte", - "size": 1, - "description": "Unknown (typically 0x01)" - } - ], - "source": { - "file": "Projects/UOContent/Network/Packets/IncomingPlayerPackets.cs", - "line": 326 - } - }, - { - "id": "0xF4", - "name": "Crash Report", - "description": "Client sends crash/error report.", - "direction": "incoming", - "isDynamic": true, - "size": "var", - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xF4", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Client Major", - "type": "byte", - "size": 1, - "description": "Client major version" - }, - { - "name": "Client Minor", - "type": "byte", - "size": 1, - "description": "Client minor version" - }, - { - "name": "Client Rev", - "type": "byte", - "size": 1, - "description": "Client revision" - }, - { - "name": "Client Pat", - "type": "byte", - "size": 1, - "description": "Client patch" - }, - { - "name": "X", - "type": "ushort", - "size": 2, - "description": "Player X coordinate at crash" - }, - { - "name": "Y", - "type": "ushort", - "size": 2, - "description": "Player Y coordinate at crash" - }, - { - "name": "Z", - "type": "sbyte", - "size": 1, - "description": "Player Z coordinate at crash" - }, - { - "name": "Map", - "type": "byte", - "size": 1, - "description": "Map ID at crash" - }, - { - "name": "Account Name", - "type": "ascii", - "size": 32, - "description": "Account name" - }, - { - "name": "Character Name", - "type": "ascii", - "size": 32, - "description": "Character name" - }, - { - "name": "IP Address", - "type": "ascii", - "size": 15, - "description": "Client IP address" - }, - { - "name": "Unknown1", - "type": "int", - "size": 4, - "description": "Unknown" - }, - { - "name": "Exception Code", - "type": "int", - "size": 4, - "description": "Exception code" - }, - { - "name": "Process Name", - "type": "ascii", - "size": 100, - "description": "Process name" - }, - { - "name": "Report Text", - "type": "ascii", - "size": 100, - "description": "Report text" - }, - { - "name": "Terminator", - "type": "byte", - "size": 1, - "value": "0x00", - "description": "Null terminator" - }, - { - "name": "Offset", - "type": "int", - "size": 4, - "description": "Crash offset" - }, - { - "name": "Stack Count", - "type": "byte", - "size": 1, - "description": "Number of stack trace entries" - }, - { - "name": "Stack Addresses", - "type": "loop", - "loop": { - "countField": "stackCount", - "fields": [ - { - "name": "Address", - "type": "int", - "size": 4, - "description": "Stack frame address" - } - ] - } - } - ], - "source": { - "file": "Projects/UOContent/Network/Packets/IncomingPlayerPackets.cs", - "line": 413 - } - } - ] -} \ No newline at end of file diff --git a/website/packets/incoming/securetrade.json b/website/packets/incoming/securetrade.json deleted file mode 100644 index e0b26a90b..000000000 --- a/website/packets/incoming/securetrade.json +++ /dev/null @@ -1,144 +0,0 @@ -{ - "category": "Secure Trade", - "packets": [ - { - "id": "0x6F", - "name": "Secure Trade", - "description": "Client interacts with a secure trade window.", - "direction": "incoming", - "isDynamic": true, - "size": "Varies", - "variants": [ - { - "name": "Cancel Trade", - "condition": "action == Cancel (1)", - "size": 8, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x6F", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Action", - "type": "byte", - "size": 1, - "value": "1", - "description": "Cancel action" - }, - { - "name": "Container Serial", - "type": "uint", - "size": 4, - "description": "Serial of the trade container" - } - ] - }, - { - "name": "Toggle Accept", - "condition": "action == Check (2)", - "size": 12, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x6F", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Action", - "type": "byte", - "size": 1, - "value": "2", - "description": "Check/toggle action" - }, - { - "name": "Container Serial", - "type": "uint", - "size": 4, - "description": "Serial of the trade container" - }, - { - "name": "Accepted", - "type": "bool", - "size": 4, - "description": "True if accepting the trade" - } - ] - }, - { - "name": "Update Gold/Platinum", - "condition": "action == UpdateGold (3)", - "size": 16, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x6F", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Action", - "type": "byte", - "size": 1, - "value": "3", - "description": "Update gold action" - }, - { - "name": "Container Serial", - "type": "uint", - "size": 4, - "description": "Serial of the trade container" - }, - { - "name": "Gold", - "type": "int", - "size": 4, - "description": "Gold amount to trade" - }, - { - "name": "Platinum", - "type": "int", - "size": 4, - "description": "Platinum amount to trade" - } - ] - } - ], - "related": [ - { - "id": "0x6F", - "direction": "outgoing", - "relationship": "response", - "note": "Server sends trade window updates" - } - ], - "source": { - "file": "Projects/UOContent/Network/Packets/IncomingMobilePackets.cs", - "line": 93 - } - } - ] -} diff --git a/website/packets/incoming/store.json b/website/packets/incoming/store.json deleted file mode 100644 index 04ec54c22..000000000 --- a/website/packets/incoming/store.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "category": "Store", - "packets": [ - { - "id": "0xFA", - "name": "Open UO Store", - "description": "Client requests to open the Ultima Store interface. Sent when player clicks the UO Store button in the client toolbar (Classic) or selects Ultima Store from the menu (Enhanced).", - "direction": "incoming", - "isDynamic": false, - "size": 1, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xFA", - "description": "Packet identifier" - } - ], - "clientVersion": { - "classic": {}, - "enhanced": {}, - "notes": "Ultima Store feature" - }, - "source": { - "file": "Projects/UOContent/Engines/UltimaStore/UltimaStorePackets.cs", - "line": 10 - }, - "notes": "The Ultima Store is an in-game microtransaction store on official servers. ModernUO includes a placeholder that displays a 'not available' message. Free shards may implement their own store functionality using this packet trigger." - } - ] -} diff --git a/website/packets/incoming/targeting.json b/website/packets/incoming/targeting.json deleted file mode 100644 index ca9470029..000000000 --- a/website/packets/incoming/targeting.json +++ /dev/null @@ -1,117 +0,0 @@ -{ - "category": "Targeting", - "packets": [ - { - "id": "0x6C", - "name": "Target Response", - "description": "Client responds to a target cursor request.", - "direction": "incoming", - "isDynamic": false, - "size": 19, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x6C", - "description": "Packet identifier" - }, - { - "name": "Type", - "type": "enum", - "enumType": "sequential", - "size": 1, - "description": "Target type", - "values": [ - { - "value": 0, - "name": "Object", - "description": "Targeting a specific object (mobile/item)" - }, - { - "value": 1, - "name": "Location", - "description": "Targeting a location/tile" - } - ] - }, - { - "name": "Target ID", - "type": "int", - "size": 4, - "description": "Target cursor ID to match request" - }, - { - "name": "Flags", - "type": "enum", - "enumType": "sequential", - "size": 1, - "description": "Target flags", - "values": [ - { - "value": 0, - "name": "Neutral", - "description": "Neutral targeting" - }, - { - "value": 1, - "name": "Harmful", - "description": "Harmful action" - }, - { - "value": 2, - "name": "Beneficial", - "description": "Beneficial action" - }, - { - "value": 3, - "name": "Cancel", - "description": "Targeting canceled" - } - ] - }, - { - "name": "Target Serial", - "type": "uint", - "size": 4, - "description": "Serial of targeted object (0 for ground)" - }, - { - "name": "X", - "type": "short", - "size": 2, - "description": "Target X coordinate (-1 if canceled)" - }, - { - "name": "Y", - "type": "short", - "size": 2, - "description": "Target Y coordinate (-1 if canceled)" - }, - { - "name": "Unknown", - "type": "byte", - "size": 1, - "description": "Unknown (typically 0)" - }, - { - "name": "Z", - "type": "sbyte", - "size": 1, - "description": "Target Z coordinate" - }, - { - "name": "Graphic", - "type": "ushort", - "size": 2, - "description": "Graphic ID of targeted tile (0 for land)" - } - ], - "notes": "If x == -1 \u0026\u0026 y == -1 \u0026\u0026 serial is invalid, user pressed Escape to cancel targeting.", - "source": { - "file": "Projects/UOContent/Network/Packets/IncomingTargetingPackets.cs", - "line": 28 - } - } - ] -} \ No newline at end of file diff --git a/website/packets/incoming/vendor.json b/website/packets/incoming/vendor.json deleted file mode 100644 index ee54fa693..000000000 --- a/website/packets/incoming/vendor.json +++ /dev/null @@ -1,145 +0,0 @@ -{ - "category": "Vendor", - "packets": [ - { - "id": "0x3B", - "name": "Vendor Buy Reply", - "description": "Client confirms items to purchase from a vendor.", - "direction": "incoming", - "isDynamic": true, - "size": "var", - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x3B", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Vendor Serial", - "type": "uint", - "size": 4, - "description": "Serial of the vendor" - }, - { - "name": "Flag", - "type": "byte", - "size": 1, - "description": "0x00 = buy, 0x02 = cancel" - }, - { - "name": "Items", - "type": "loop", - "loop": { - "untilEnd": true, - "fields": [ - { - "name": "Layer", - "type": "byte", - "size": 1, - "description": "Layer/container index" - }, - { - "name": "Item Serial", - "type": "uint", - "size": 4, - "description": "Serial of item to buy" - }, - { - "name": "Amount", - "type": "short", - "size": 2, - "description": "Amount to purchase" - } - ] - } - } - ], - "related": [ - { - "id": "0x74", - "relationship": "request", - "note": "Vendor Buy List packet that initiated this" - } - ], - "source": { - "file": "Projects/UOContent/Network/Packets/IncomingVendorPackets.cs", - "line": 25 - } - }, - { - "id": "0x9F", - "name": "Vendor Sell Reply", - "description": "Client confirms items to sell to a vendor.", - "direction": "incoming", - "isDynamic": true, - "size": "var", - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x9F", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Vendor Serial", - "type": "uint", - "size": 4, - "description": "Serial of the vendor" - }, - { - "name": "Item Count", - "type": "ushort", - "size": 2, - "description": "Number of items to sell" - }, - { - "name": "Items", - "type": "loop", - "loop": { - "countField": "itemCount", - "fields": [ - { - "name": "Item Serial", - "type": "uint", - "size": 4, - "description": "Serial of item to sell" - }, - { - "name": "Amount", - "type": "short", - "size": 2, - "description": "Amount to sell" - } - ] - } - } - ], - "related": [ - { - "id": "0x9E", - "relationship": "request", - "note": "Vendor Sell List packet that initiated this" - } - ], - "source": { - "file": "Projects/UOContent/Network/Packets/IncomingVendorPackets.cs", - "line": 26 - } - } - ] -} diff --git a/website/packets/outgoing/account.json b/website/packets/outgoing/account.json deleted file mode 100644 index 7b0fda674..000000000 --- a/website/packets/outgoing/account.json +++ /dev/null @@ -1,1183 +0,0 @@ -{ - "category": "Account", - "packets": [ - { - "id": "0x82", - "name": "Account Login Rejected", - "description": "Sent when account login fails. Contains the reason for rejection.", - "direction": "outgoing", - "isDynamic": false, - "size": 2, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x82", - "description": "Packet identifier" - }, - { - "name": "Reason", - "type": "enum", - "size": 1, - "description": "Rejection reason code", - "values": [ - { "value": "0", "name": "Invalid", "description": "Invalid credentials" }, - { "value": "1", "name": "InUse", "description": "Account already in use" }, - { "value": "2", "name": "Blocked", "description": "Account blocked" }, - { "value": "3", "name": "BadPass", "description": "Incorrect password" }, - { "value": "254", "name": "Idle", "description": "Idle timeout" }, - { "value": "255", "name": "BadComm", "description": "Communication error" } - ] - } - ], - "related": [ - { - "id": "0x80", - "relationship": "request", - "note": "The login attempt that failed" - } - ], - "source": { - "file": "Projects/Server/Network/Packets/OutgoingAccountPackets.cs", - "line": 402 - } - }, - { - "id": "0xA8", - "name": "Account Login Ack (Server List)", - "description": "Sent after successful account login. Contains the list of available game servers.", - "direction": "outgoing", - "isDynamic": true, - "size": "6 + (40 x serverCount)", - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xA8", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "System Info Flag", - "type": "byte", - "size": 1, - "value": "0x5D", - "description": "System info flag" - }, - { - "name": "Server Count", - "type": "ushort", - "size": 2, - "description": "Number of servers in list" - }, - { - "name": "Servers", - "type": "array", - "description": "Server list entries", - "loop": { - "countField": "serverCount", - "fields": [ - { - "name": "Index", - "type": "ushort", - "size": 2, - "description": "Server index" - }, - { - "name": "Name", - "type": "ascii", - "size": 32, - "description": "Server name" - }, - { - "name": "Full Percent", - "type": "byte", - "size": 1, - "description": "Server load percentage" - }, - { - "name": "Timezone", - "type": "sbyte", - "size": 1, - "description": "Server timezone offset" - }, - { - "name": "IP Address", - "type": "uint", - "size": 4, - "description": "Server IP address (IPv4)" - } - ] - } - } - ], - "related": [ - { - "id": "0x80", - "relationship": "request", - "note": "The successful login request" - } - ], - "source": { - "file": "Projects/Server/Network/Packets/OutgoingAccountPackets.cs", - "line": 411 - } - }, - { - "id": "0x8C", - "name": "Play Server Ack", - "description": "Sent after selecting a server. Contains connection details for the game server.", - "direction": "outgoing", - "isDynamic": false, - "size": 11, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x8C", - "description": "Packet identifier" - }, - { - "name": "IP Address", - "type": "uint", - "size": 4, - "description": "Game server IP address (little-endian)" - }, - { - "name": "Port", - "type": "short", - "size": 2, - "description": "Game server port" - }, - { - "name": "Auth ID", - "type": "int", - "size": 4, - "description": "Authentication ID for game login" - } - ], - "related": [ - { - "id": "0xA0", - "relationship": "request", - "note": "The server selection request" - }, - { - "id": "0x91", - "relationship": "response", - "note": "Client sends game login with this authId" - } - ], - "source": { - "file": "Projects/Server/Network/Packets/OutgoingAccountPackets.cs", - "line": 447 - } - }, - { - "id": "0xA9", - "name": "Character List", - "description": "Sent after game login. Contains the list of characters on the account and starting city information. Extended format in 7.0.13.0+ includes city coordinates.", - "direction": "outgoing", - "isDynamic": true, - "size": "Varies", - "variants": [ - { - "version": "Classic", - "name": "Pre-7.0.13.0 Format", - "condition": "Client version \u003c 7.0.13.0", - "size": "9+", - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xA9", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Char Count", - "type": "byte", - "size": 1, - "description": "Number of character slots (1, 5, 6, or 7)" - }, - { - "name": "Characters", - "type": "loop", - "description": "Character list entries (60 bytes each)", - "loop": { - "countField": "charCount", - "itemSize": 60, - "fields": [ - { - "name": "Name", - "type": "ascii", - "size": 30, - "description": "Character name (empty if slot unused)" - }, - { - "name": "Password", - "type": "ascii", - "size": 30, - "description": "Always empty (legacy field)" - } - ] - } - }, - { - "name": "City Count", - "type": "byte", - "size": 1, - "description": "Number of starting cities" - }, - { - "name": "Cities", - "type": "loop", - "description": "Starting city entries (63 bytes each)", - "loop": { - "countField": "cityCount", - "itemSize": 63, - "fields": [ - { - "name": "Index", - "type": "byte", - "size": 1, - "description": "City index" - }, - { - "name": "City Name", - "type": "ascii", - "size": 31, - "description": "City name" - }, - { - "name": "Building Name", - "type": "ascii", - "size": 31, - "description": "Building/tavern name" - } - ] - } - }, - { - "name": "Flags", - "type": "bitfield", - "size": 4, - "description": "Character list flags", - "flags": [ - { - "bit": "0", - "name": "Unknown1", - "description": "Unknown" - }, - { - "bit": "1", - "name": "Overwrite Config", - "description": "Overwrite configuration file" - }, - { - "bit": "2", - "name": "One Character Slot", - "description": "Limit to 1 character slot" - }, - { - "bit": "3", - "name": "Context Menus", - "description": "Enable context menus" - }, - { - "bit": "4", - "name": "Slot Limit", - "description": "Limit character slots" - }, - { - "bit": "5", - "name": "AOS", - "description": "Age of Shadows features" - }, - { - "bit": "6", - "name": "Sixth Character Slot", - "description": "Enable 6th character slot" - }, - { - "bit": "7", - "name": "SE", - "description": "Samurai Empire features" - }, - { - "bit": "8", - "name": "ML", - "description": "Mondain\u0027s Legacy features" - }, - { - "bit": "9", - "name": "Unknown2", - "description": "Unknown" - }, - { - "bit": "10", - "name": "KR", - "description": "Kingdom Reborn features" - }, - { - "bit": "11", - "name": "SA", - "description": "Stygian Abyss features" - }, - { - "bit": "12", - "name": "HS", - "description": "High Seas features" - }, - { - "bit": "13", - "name": "Seventh Character Slot", - "description": "Enable 7th character slot" - }, - { - "bit": "14", - "name": "Unknown3", - "description": "Unknown" - }, - { - "bit": "15", - "name": "New Movement", - "description": "New movement system" - }, - { - "bit": "16", - "name": "New Felucca Areas", - "description": "New Felucca areas" - } - ] - } - ] - }, - { - "version": "NewCharacterList (7.0.13.0+)", - "name": "Extended City Format", - "condition": "Client version \u003e= 7.0.13.0 (NewCharacterList)", - "size": "11+", - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xA9", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Char Count", - "type": "byte", - "size": 1, - "description": "Number of character slots (1, 5, 6, or 7)" - }, - { - "name": "Characters", - "type": "loop", - "description": "Character list entries (60 bytes each)", - "loop": { - "countField": "charCount", - "itemSize": 60, - "fields": [ - { - "name": "Name", - "type": "ascii", - "size": 30, - "description": "Character name (empty if slot unused)" - }, - { - "name": "Password", - "type": "ascii", - "size": 30, - "description": "Always empty (legacy field)" - } - ] - } - }, - { - "name": "City Count", - "type": "byte", - "size": 1, - "description": "Number of starting cities" - }, - { - "name": "Cities", - "type": "loop", - "description": "Starting city entries (89 bytes each, extended format)", - "loop": { - "countField": "cityCount", - "itemSize": 89, - "fields": [ - { - "name": "Index", - "type": "byte", - "size": 1, - "description": "City index" - }, - { - "name": "City Name", - "type": "ascii", - "size": 32, - "description": "City name (1 byte longer than classic)" - }, - { - "name": "Building Name", - "type": "ascii", - "size": 32, - "description": "Building/tavern name (1 byte longer than classic)" - }, - { - "name": "X", - "type": "int", - "size": 4, - "description": "X coordinate of starting location" - }, - { - "name": "Y", - "type": "int", - "size": 4, - "description": "Y coordinate of starting location" - }, - { - "name": "Z", - "type": "int", - "size": 4, - "description": "Z coordinate of starting location" - }, - { - "name": "Map ID", - "type": "int", - "size": 4, - "description": "Map ID for starting location" - }, - { - "name": "Cliloc", - "type": "int", - "size": 4, - "description": "Cliloc ID for city description" - }, - { - "name": "Unknown", - "type": "int", - "size": 4, - "value": "0", - "description": "Unknown (always 0)" - } - ] - } - }, - { - "name": "Flags", - "type": "bitfield", - "size": 4, - "description": "Character list flags (same as classic format)", - "flags": [ - { - "bit": "2", - "name": "One Character Slot", - "description": "Limit to 1 character slot" - }, - { - "bit": "3", - "name": "Context Menus", - "description": "Enable context menus" - }, - { - "bit": "4", - "name": "Slot Limit", - "description": "Limit character slots" - }, - { - "bit": "5", - "name": "AOS", - "description": "Age of Shadows features" - }, - { - "bit": "6", - "name": "Sixth Character Slot", - "description": "Enable 6th character slot" - }, - { - "bit": "7", - "name": "SE", - "description": "Samurai Empire features" - }, - { - "bit": "8", - "name": "ML", - "description": "Mondain\u0027s Legacy features" - }, - { - "bit": "11", - "name": "SA", - "description": "Stygian Abyss features" - }, - { - "bit": "12", - "name": "HS", - "description": "High Seas features" - }, - { - "bit": "13", - "name": "Seventh Character Slot", - "description": "Enable 7th character slot" - } - ] - }, - { - "name": "Last Char Slot", - "type": "short", - "size": 2, - "value": "-1", - "description": "Last played character slot index (-1 if none)" - } - ] - } - ], - "related": [ - { - "id": "0x91", - "relationship": "request", - "note": "Game login request" - } - ], - "source": { - "file": "Projects/Server/Network/Packets/OutgoingAccountPackets.cs", - "line": 293 - } - }, - { - "id": "0x1B", - "name": "Login Confirmation", - "description": "Sent when entering the world. Confirms the player\u0027s mobile serial and initial position.", - "direction": "outgoing", - "isDynamic": false, - "size": 37, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x1B", - "description": "Packet identifier" - }, - { - "name": "Serial", - "type": "uint", - "size": 4, - "description": "Player mobile serial" - }, - { - "name": "Unknown", - "type": "int", - "size": 4, - "value": "0", - "description": "Unknown (always 0)" - }, - { - "name": "Body", - "type": "short", - "size": 2, - "description": "Player body ID" - }, - { - "name": "X", - "type": "short", - "size": 2, - "description": "X coordinate" - }, - { - "name": "Y", - "type": "short", - "size": 2, - "description": "Y coordinate" - }, - { - "name": "Z", - "type": "short", - "size": 2, - "description": "Z coordinate" - }, - { - "name": "Direction", - "type": "byte", - "size": 1, - "description": "Facing direction" - }, - { - "name": "Unknown2", - "type": "byte", - "size": 1, - "value": "0", - "description": "Unknown" - }, - { - "name": "Unknown3", - "type": "int", - "size": 4, - "value": "-1", - "description": "Unknown (always -1)" - }, - { - "name": "Unknown4", - "type": "int", - "size": 4, - "value": "0", - "description": "Unknown" - }, - { - "name": "Map Width", - "type": "short", - "size": 2, - "description": "Map width in tiles" - }, - { - "name": "Map Height", - "type": "short", - "size": 2, - "description": "Map height in tiles" - }, - { - "name": "Unknown5", - "type": "byte[6]", - "size": 6, - "description": "Unknown (zeros)" - } - ], - "related": [ - { - "id": "0x5D", - "relationship": "request", - "note": "Play character request" - } - ], - "source": { - "file": "Projects/Server/Network/Packets/OutgoingAccountPackets.cs", - "line": 190 - } - }, - { - "id": "0x55", - "name": "Login Complete", - "description": "Final packet in the login sequence. Signals that the client can begin normal gameplay.", - "direction": "outgoing", - "isDynamic": false, - "size": 1, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x55", - "description": "Packet identifier" - } - ], - "source": { - "file": "Projects/Server/Network/Packets/OutgoingAccountPackets.cs", - "line": 231 - } - }, - { - "id": "0xBD", - "name": "Client Version Request", - "description": "Request for the client to send its version string.", - "direction": "outgoing", - "isDynamic": false, - "size": 3, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xBD", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "value": "0x0003", - "description": "Packet length" - } - ], - "source": { - "file": "Projects/Server/Network/Packets/OutgoingAccountPackets.cs", - "line": 112 - }, - "notes": "Same packet ID (0xBD) is used for both request (server) and response (client)." - }, - { - "id": "0xB9", - "name": "Supported Features", - "description": "Informs the client which features are enabled on the server. Feature flags control T2A, LBR, AOS, SE, ML, SA, HS features and character slot limits.", - "direction": "outgoing", - "isDynamic": false, - "size": "Varies", - "variants": [ - { - "version": "Classic", - "name": "16-bit Feature Flags", - "condition": "Client without ExtendedSupportedFeatures", - "size": 3, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xB9", - "description": "Packet identifier" - }, - { - "name": "Features", - "type": "bitfield", - "size": 2, - "description": "16-bit feature flags bitmask", - "flags": [ - { - "bit": "0", - "name": "T2A", - "description": "The Second Age features" - }, - { - "bit": "1", - "name": "Renaissance", - "description": "Renaissance features" - }, - { - "bit": "2", - "name": "Third Dawn", - "description": "Third Dawn (3D client) features" - }, - { - "bit": "3", - "name": "LBR", - "description": "Lord Blackthorn\u0027s Revenge features" - }, - { - "bit": "4", - "name": "AOS", - "description": "Age of Shadows features" - }, - { - "bit": "5", - "name": "Sixth Character Slot", - "description": "Enable 6th character slot" - }, - { - "bit": "6", - "name": "SE", - "description": "Samurai Empire features" - }, - { - "bit": "7", - "name": "ML", - "description": "Mondain\u0027s Legacy features" - }, - { - "bit": "8", - "name": "Eighth Age", - "description": "Eighth Age splash screen" - }, - { - "bit": "9", - "name": "Ninth Age", - "description": "Ninth Age splash screen" - }, - { - "bit": "10", - "name": "Tenth Age", - "description": "Tenth Age splash screen" - }, - { - "bit": "11", - "name": "Increased Storage", - "description": "Increased storage" - }, - { - "bit": "12", - "name": "Seventh Character Slot", - "description": "Enable 7th character slot" - }, - { - "bit": "13", - "name": "Roleplay Faces", - "description": "Roleplay face selection" - }, - { - "bit": "14", - "name": "Trial Account", - "description": "Trial account flag" - }, - { - "bit": "15", - "name": "Live Account", - "description": "Live (paid) account flag" - } - ] - } - ] - }, - { - "version": "Extended", - "name": "32-bit Feature Flags", - "condition": "Client with ExtendedSupportedFeatures", - "size": 5, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xB9", - "description": "Packet identifier" - }, - { - "name": "Features", - "type": "bitfield", - "size": 4, - "description": "32-bit feature flags bitmask (includes all 16-bit flags plus expansion flags)", - "flags": [ - { - "bit": "0", - "name": "T2A", - "description": "The Second Age features" - }, - { - "bit": "1", - "name": "Renaissance", - "description": "Renaissance features" - }, - { - "bit": "2", - "name": "Third Dawn", - "description": "Third Dawn (3D client) features" - }, - { - "bit": "3", - "name": "LBR", - "description": "Lord Blackthorn\u0027s Revenge features" - }, - { - "bit": "4", - "name": "AOS", - "description": "Age of Shadows features" - }, - { - "bit": "5", - "name": "Sixth Character Slot", - "description": "Enable 6th character slot" - }, - { - "bit": "6", - "name": "SE", - "description": "Samurai Empire features" - }, - { - "bit": "7", - "name": "ML", - "description": "Mondain\u0027s Legacy features" - }, - { - "bit": "8", - "name": "Eighth Age", - "description": "Eighth Age splash screen" - }, - { - "bit": "9", - "name": "Ninth Age", - "description": "Ninth Age splash screen" - }, - { - "bit": "10", - "name": "Tenth Age", - "description": "Tenth Age splash screen" - }, - { - "bit": "11", - "name": "Increased Storage", - "description": "Increased storage" - }, - { - "bit": "12", - "name": "Seventh Character Slot", - "description": "Enable 7th character slot" - }, - { - "bit": "13", - "name": "Roleplay Faces", - "description": "Roleplay face selection" - }, - { - "bit": "14", - "name": "Trial Account", - "description": "Trial account flag" - }, - { - "bit": "15", - "name": "Live Account", - "description": "Live (paid) account flag" - }, - { - "bit": "16", - "name": "SA", - "description": "Stygian Abyss features" - }, - { - "bit": "17", - "name": "HS", - "description": "High Seas features" - }, - { - "bit": "18", - "name": "Gothic", - "description": "Gothic housing tiles" - }, - { - "bit": "19", - "name": "Rustic", - "description": "Rustic housing tiles" - }, - { - "bit": "20", - "name": "Jungle", - "description": "Jungle housing tiles" - }, - { - "bit": "21", - "name": "Shadowguard", - "description": "Shadowguard content" - }, - { - "bit": "22", - "name": "TOL", - "description": "Time of Legends features" - }, - { - "bit": "23", - "name": "EJ", - "description": "Endless Journey (F2P) features" - } - ] - } - ] - } - ], - "clientVersion": { - "classic": {}, - "enhanced": {}, - "notes": "ExtendedSupportedFeatures determines which variant is sent" - }, - "source": { - "file": "Projects/Server/Network/Packets/OutgoingAccountPackets.cs", - "line": 140 - } - }, - { - "id": "0x85", - "name": "Character Delete Result", - "description": "Result of a character deletion request.", - "direction": "outgoing", - "isDynamic": false, - "size": 2, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x85", - "description": "Packet identifier" - }, - { - "name": "Result", - "type": "enum", - "size": 1, - "description": "Deletion result code", - "values": [ - { "value": "0", "name": "PasswordInvalid", "description": "Password is invalid" }, - { "value": "1", "name": "CharNotExist", "description": "Character does not exist" }, - { "value": "2", "name": "CharBeingPlayed", "description": "Character is being played" }, - { "value": "3", "name": "CharTooYoung", "description": "Character too young to delete" }, - { "value": "4", "name": "CharQueued", "description": "Character queued for deletion" }, - { "value": "5", "name": "BadRequest", "description": "Invalid request" } - ] - } - ], - "related": [ - { - "id": "0x83", - "relationship": "request", - "note": "The delete request" - } - ], - "source": { - "file": "Projects/Server/Network/Packets/OutgoingAccountPackets.cs", - "line": 121 - } - }, - { - "id": "0x53", - "name": "Popup Message", - "description": "Displays a predefined popup message to the client.", - "direction": "outgoing", - "isDynamic": false, - "size": 2, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x53", - "description": "Packet identifier" - }, - { - "name": "Message ID", - "type": "enum", - "size": 1, - "description": "Predefined message identifier", - "values": [ - { "value": "1", "name": "CharNoExist", "description": "Character does not exist" }, - { "value": "2", "name": "CharExists", "description": "Character already exists" }, - { "value": "5", "name": "CharInWorld", "description": "Character is in world" }, - { "value": "6", "name": "LoginSyncError", "description": "Login synchronization error" }, - { "value": "7", "name": "IdleWarning", "description": "Idle timeout warning" } - ] - } - ], - "source": { - "file": "Projects/Server/Network/Packets/OutgoingAccountPackets.cs", - "line": 131 - } - }, - { - "id": "0x81", - "name": "Change Character", - "description": "Updates the character list during reconnection. Currently unused.", - "direction": "outgoing", - "isDynamic": true, - "size": "5 + (60 x charCount)", - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x81", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Char Count", - "type": "ushort", - "size": 2, - "description": "Number of characters" - }, - { - "name": "Characters", - "type": "array", - "description": "Character list entries", - "loop": { - "countField": "charCount", - "fields": [ - { - "name": "Name", - "type": "ascii", - "size": 30, - "description": "Character name" - }, - { - "name": "Password", - "type": "ascii", - "size": 30, - "description": "Always empty" - } - ] - } - } - ], - "source": { - "file": "Projects/Server/Network/Packets/OutgoingAccountPackets.cs", - "line": 63 - }, - "notes": "Currently unused in ModernUO." - }, - { - "id": "0x86", - "name": "Character List Update", - "description": "Updates the character list after character creation or deletion.", - "direction": "outgoing", - "isDynamic": true, - "size": "4 + (60 x charCount)", - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x86", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Char Count", - "type": "byte", - "size": 1, - "description": "Number of character slots" - }, - { - "name": "Characters", - "type": "array", - "description": "Character list entries", - "loop": { - "countField": "charCount", - "fields": [ - { - "name": "Name", - "type": "ascii", - "size": 30, - "description": "Character name (empty if slot unused)" - }, - { - "name": "Password", - "type": "ascii", - "size": 30, - "description": "Always empty" - } - ] - } - } - ], - "source": { - "file": "Projects/Server/Network/Packets/OutgoingAccountPackets.cs", - "line": 242 - } - } - ] -} \ No newline at end of file diff --git a/website/packets/outgoing/arrow.json b/website/packets/outgoing/arrow.json deleted file mode 100644 index ba16c5b76..000000000 --- a/website/packets/outgoing/arrow.json +++ /dev/null @@ -1,137 +0,0 @@ -{ - "category": "Quest", - "packets": [ - { - "id": "0xBA", - "name": "Quest Arrow", - "description": "Sets or cancels the quest tracking arrow on the client.", - "direction": "outgoing", - "isDynamic": false, - "size": "Varies", - "variants": [ - { - "name": "Pre-High Seas Set Arrow", - "condition": "command == 1 (Set), pre-HighSeas client", - "size": 6, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xBA", - "description": "Packet identifier" - }, - { - "name": "Command", - "type": "byte", - "size": 1, - "value": "0x01", - "description": "Set arrow command" - }, - { - "name": "X", - "type": "short", - "size": 2, - "description": "Target X coordinate" - }, - { - "name": "Y", - "type": "short", - "size": 2, - "description": "Target Y coordinate" - } - ] - }, - { - "name": "Pre-High Seas Cancel Arrow", - "condition": "command == 0 (Cancel), pre-HighSeas client", - "size": 6, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xBA", - "description": "Packet identifier" - }, - { - "name": "Command", - "type": "byte", - "size": 1, - "value": "0x00", - "description": "Cancel arrow command" - }, - { - "name": "X", - "type": "short", - "size": 2, - "value": "-1", - "description": "X coordinate (-1)" - }, - { - "name": "Y", - "type": "short", - "size": 2, - "value": "-1", - "description": "Y coordinate (-1)" - } - ] - }, - { - "name": "High Seas", - "condition": "HighSeas client (7.0.9.0+)", - "size": 10, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xBA", - "description": "Packet identifier" - }, - { - "name": "Command", - "type": "byte", - "size": 1, - "description": "Command (0 = cancel, 1 = set)" - }, - { - "name": "X", - "type": "short", - "size": 2, - "description": "Target X coordinate" - }, - { - "name": "Y", - "type": "short", - "size": 2, - "description": "Target Y coordinate" - }, - { - "name": "Target Serial", - "type": "uint", - "size": 4, - "description": "Serial of tracked target" - } - ] - } - ], - "related": [ - { - "id": "0xBF/0x07", - "relationship": "request", - "note": "Quest Arrow Click from client" - } - ], - "clientVersion": { - "classic": {}, - "enhanced": {}, - "notes": "HighSeas (7.0.9.0+) adds target serial. Pre-HS clients use 6-byte variant, HS+ clients use 10-byte variant." - }, - "source": { - "file": "Projects/UOContent/Skills/Tracking/OutgoingArrowPackets.cs", - "line": 29 - } - } - ] -} \ No newline at end of file diff --git a/website/packets/outgoing/assistant.json b/website/packets/outgoing/assistant.json deleted file mode 100644 index 1fb05e6ea..000000000 --- a/website/packets/outgoing/assistant.json +++ /dev/null @@ -1,75 +0,0 @@ -{ - "category": "Assistant", - "packets": [ - { - "id": "0xF0", - "name": "Assistant Handshake", - "description": "Server sends handshake to negotiate assistant features with Razor-style clients. Contains disallowed feature flags.", - "direction": "outgoing", - "isDynamic": false, - "size": 12, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xF0", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "value": "0x000C", - "description": "Packet length (12)" - }, - { - "name": "Command", - "type": "byte", - "size": 1, - "value": "0xFE", - "description": "Handshake command" - }, - { - "name": "Disallowed Features", - "type": "ulong", - "size": 8, - "description": "Bitmask of disallowed assistant features" - } - ], - "notes": "Used for assistant negotiation with Razor Community Edition and similar clients. If negotiation fails within 30 seconds, the server may kick the player.", - "source": { - "file": "Projects/UOContent/Assistants/AssistantHandler.cs", - "line": 132 - } - }, - { - "id": "0xBE", - "name": "Assistant Version Request", - "description": "Server requests assistant version information from the client.", - "direction": "outgoing", - "isDynamic": false, - "size": 3, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xBE", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "value": "0x0003", - "description": "Packet length (3)" - } - ], - "source": { - "file": "Projects/UOContent/Assistants/AssistantHandler.cs", - "line": 122 - } - } - ] -} \ No newline at end of file diff --git a/website/packets/outgoing/boat.json b/website/packets/outgoing/boat.json deleted file mode 100644 index d295510d1..000000000 --- a/website/packets/outgoing/boat.json +++ /dev/null @@ -1,122 +0,0 @@ -{ - "category": "Boat", - "packets": [ - { - "id": "0xF6", - "name": "Boat Move (High Seas)", - "description": "Sent when a boat moves, containing the boat\u0027s new position and all entities on board with their updated positions.", - "direction": "outgoing", - "isDynamic": true, - "size": "18 + (entityCount x 10)", - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xF6", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Boat Serial", - "type": "uint", - "size": 4, - "description": "Serial of the boat" - }, - { - "name": "Speed", - "type": "byte", - "size": 1, - "description": "Boat movement speed" - }, - { - "name": "Direction", - "type": "byte", - "size": 1, - "description": "Movement direction (0-7)" - }, - { - "name": "Facing", - "type": "byte", - "size": 1, - "description": "Boat facing direction" - }, - { - "name": "X", - "type": "short", - "size": 2, - "description": "Boat X coordinate" - }, - { - "name": "Y", - "type": "short", - "size": 2, - "description": "Boat Y coordinate" - }, - { - "name": "Z", - "type": "short", - "size": 2, - "description": "Boat Z coordinate" - }, - { - "name": "Entity Count", - "type": "short", - "size": 2, - "description": "Number of entities on boat (max 65535)" - }, - { - "name": "Entities", - "type": "array", - "description": "Entities on the boat with their positions", - "loop": { - "countField": "entityCount", - "fields": [ - { - "name": "Serial", - "type": "uint", - "size": 4, - "description": "Entity serial" - }, - { - "name": "X", - "type": "short", - "size": 2, - "description": "Entity X coordinate (boat offset applied)" - }, - { - "name": "Y", - "type": "short", - "size": 2, - "description": "Entity Y coordinate (boat offset applied)" - }, - { - "name": "Z", - "type": "short", - "size": 2, - "description": "Entity Z coordinate" - } - ] - } - } - ], - "clientVersion": { - "classic": { - "min": "7.0.9.0" - }, - "enhanced": {}, - "notes": "High Seas expansion feature" - }, - "source": { - "file": "Projects/UOContent/Multis/Boats/BoatPackets.cs", - "line": 27 - }, - "notes": "High Seas expansion only. Entities include mobiles and items on the boat. Coordinates include the boat\u0027s movement offset." - } - ] -} \ No newline at end of file diff --git a/website/packets/outgoing/book.json b/website/packets/outgoing/book.json deleted file mode 100644 index 6f89e299b..000000000 --- a/website/packets/outgoing/book.json +++ /dev/null @@ -1,217 +0,0 @@ -{ - "category": "Book", - "packets": [ - { - "id": "0x66", - "name": "Book Content", - "description": "Sends full book content including all pages and lines.", - "direction": "outgoing", - "isDynamic": true, - "size": "9+", - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x66", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Book Serial", - "type": "uint", - "size": 4, - "description": "Serial of the book" - }, - { - "name": "Page Count", - "type": "ushort", - "size": 2, - "description": "Number of pages" - }, - { - "name": "Pages", - "type": "loop", - "description": "Page contents", - "loop": { - "countField": "pageCount", - "fields": [ - { - "name": "Page Number", - "type": "ushort", - "size": 2, - "description": "Page number (1-indexed)" - }, - { - "name": "Line Count", - "type": "ushort", - "size": 2, - "description": "Number of lines on page" - }, - { - "name": "Lines", - "type": "loop", - "description": "Lines on the page", - "loop": { - "countField": "lineCount", - "fields": [ - { - "name": "Text", - "type": "utf8-t", - "description": "Line text" - } - ] - } - } - ] - } - } - ], - "source": { - "file": "Projects/UOContent/Items/Books/BookPackets.cs", - "line": 136 - } - }, - { - "id": "0xD4", - "name": "Book Header", - "description": "Sends book cover information (title, author, writable status).", - "direction": "outgoing", - "isDynamic": true, - "size": "17+", - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xD4", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Book Serial", - "type": "uint", - "size": 4, - "description": "Serial of the book" - }, - { - "name": "Flag On", - "type": "byte", - "size": 1, - "value": "0x01", - "description": "Flag on (always 1)" - }, - { - "name": "Writable", - "type": "bool", - "size": 1, - "description": "True if book is writable and in range" - }, - { - "name": "Page Count", - "type": "ushort", - "size": 2, - "description": "Number of pages" - }, - { - "name": "Title Length", - "type": "ushort", - "size": 2, - "description": "Length of title + 1 (for null)" - }, - { - "name": "Title", - "type": "utf8-t", - "description": "Book title" - }, - { - "name": "Author Length", - "type": "ushort", - "size": 2, - "description": "Length of author + 1 (for null)" - }, - { - "name": "Author", - "type": "utf8-t", - "description": "Book author" - } - ], - "related": [ - { - "id": "0x93", - "relationship": "variant", - "note": "Old Book Header format (outgoing)" - } - ], - "source": { - "file": "Projects/UOContent/Items/Books/BookPackets.cs", - "line": 184 - } - }, - { - "id": "0x93", - "name": "Old Book Header", - "description": "Old format for book cover (fixed-size ASCII strings).", - "direction": "outgoing", - "isDynamic": false, - "size": 99, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x93", - "description": "Packet identifier" - }, - { - "name": "Book Serial", - "type": "uint", - "size": 4, - "description": "Serial of the book" - }, - { - "name": "Flags", - "type": "byte", - "size": 1, - "description": "Book flags" - }, - { - "name": "Page Count", - "type": "ushort", - "size": 2, - "description": "Number of pages" - }, - { - "name": "Title", - "type": "ascii", - "size": 60, - "description": "Book title (fixed 60 bytes)" - }, - { - "name": "Author", - "type": "ascii", - "size": 30, - "description": "Book author (fixed 30 bytes)" - } - ], - "related": [ - { - "id": "0xD4", - "relationship": "variant", - "note": "New Book Header format (outgoing)" - } - ], - "notes": "Old format used by older clients. Title and author are fixed-size ASCII." - } - ] -} \ No newline at end of file diff --git a/website/packets/outgoing/bufficon.json b/website/packets/outgoing/bufficon.json deleted file mode 100644 index a4f2dbf7d..000000000 --- a/website/packets/outgoing/bufficon.json +++ /dev/null @@ -1,307 +0,0 @@ -{ - "category": "Buff Icon", - "packets": [ - { - "id": "0xDF", - "name": "Buff/Debuff System", - "description": "Manages buff and debuff icons displayed on the client.", - "direction": "outgoing", - "isDynamic": true, - "size": "Varies", - "variants": [ - { - "name": "Remove Buff", - "condition": "command == 0 (Remove)", - "size": 15, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xDF", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "value": "15", - "description": "Packet length" - }, - { - "name": "Mobile Serial", - "type": "uint", - "size": 4, - "description": "Serial of the mobile" - }, - { - "name": "Icon ID", - "type": "short", - "size": 2, - "description": "Buff icon ID" - }, - { - "name": "Command", - "type": "short", - "size": 2, - "value": "0x0000", - "description": "Remove command" - }, - { - "name": "Unused", - "type": "int", - "size": 4, - "value": "0", - "description": "Unused" - } - ] - }, - { - "name": "Add Buff (No Args)", - "condition": "command == 1 (Add), no arguments", - "size": 46, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xDF", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "value": "46", - "description": "Packet length" - }, - { - "name": "Mobile Serial", - "type": "uint", - "size": 4, - "description": "Serial of the mobile" - }, - { - "name": "Icon ID", - "type": "short", - "size": 2, - "description": "Buff icon ID" - }, - { - "name": "Command1", - "type": "short", - "size": 2, - "value": "0x0001", - "description": "Add command" - }, - { - "name": "Unused1", - "type": "int", - "size": 4, - "value": "0", - "description": "Unused" - }, - { - "name": "Icon Id2", - "type": "short", - "size": 2, - "description": "Buff icon ID (repeated)" - }, - { - "name": "Command2", - "type": "short", - "size": 2, - "value": "0x0001", - "description": "Add command (repeated)" - }, - { - "name": "Unused2", - "type": "int", - "size": 4, - "value": "0", - "description": "Unused" - }, - { - "name": "Duration", - "type": "short", - "size": 2, - "description": "Duration in seconds" - }, - { - "name": "Padding", - "type": "byte[]", - "size": 3, - "description": "Padding (zeros)" - }, - { - "name": "Title Cliloc", - "type": "int", - "size": 4, - "description": "Title cliloc number" - }, - { - "name": "Secondary Cliloc", - "type": "int", - "size": 4, - "description": "Secondary cliloc number" - }, - { - "name": "Empty Args", - "type": "byte[]", - "size": 10, - "description": "Empty arguments (zeros)" - } - ] - }, - { - "name": "Add Buff (With Args)", - "condition": "command == 1 (Add), with arguments", - "size": "52 + (args.Length x 2)", - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xDF", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Mobile Serial", - "type": "uint", - "size": 4, - "description": "Serial of the mobile" - }, - { - "name": "Icon ID", - "type": "short", - "size": 2, - "description": "Buff icon ID" - }, - { - "name": "Command1", - "type": "short", - "size": 2, - "value": "0x0001", - "description": "Add command" - }, - { - "name": "Unused1", - "type": "int", - "size": 4, - "value": "0", - "description": "Unused" - }, - { - "name": "Icon Id2", - "type": "short", - "size": 2, - "description": "Buff icon ID (repeated)" - }, - { - "name": "Command2", - "type": "short", - "size": 2, - "value": "0x0001", - "description": "Add command (repeated)" - }, - { - "name": "Unused2", - "type": "int", - "size": 4, - "value": "0", - "description": "Unused" - }, - { - "name": "Duration", - "type": "short", - "size": 2, - "description": "Duration in seconds" - }, - { - "name": "Padding", - "type": "byte[]", - "size": 3, - "description": "Padding (zeros)" - }, - { - "name": "Title Cliloc", - "type": "int", - "size": 4, - "description": "Title cliloc number" - }, - { - "name": "Secondary Cliloc", - "type": "int", - "size": 4, - "description": "Secondary cliloc number" - }, - { - "name": "Unused3", - "type": "int", - "size": 4, - "value": "0", - "description": "Unused" - }, - { - "name": "Has Args", - "type": "short", - "size": 2, - "value": "0x0001", - "description": "Has arguments flag" - }, - { - "name": "Unused4", - "type": "ushort", - "size": 2, - "value": "0", - "description": "Unused" - }, - { - "name": "Tab Prefix", - "type": "utf16le", - "size": 2, - "value": "\\t", - "description": "Tab character prefix" - }, - { - "name": "Arguments", - "type": "utf16le-t", - "description": "Cliloc arguments" - }, - { - "name": "Has Args2", - "type": "short", - "size": 2, - "value": "0x0001", - "description": "Has arguments flag (repeated)" - }, - { - "name": "Unused5", - "type": "ushort", - "size": 2, - "value": "0", - "description": "Unused" - } - ] - } - ], - "clientVersion": { - "classic": { - "min": "5.0.2b" - }, - "enhanced": {}, - "notes": "BuffIcon feature supported in Classic Client 5.0.2b+" - }, - "source": { - "file": "Projects/UOContent/Engines/BuffIcons/BuffIconPackets.cs", - "line": 8 - } - } - ] -} \ No newline at end of file diff --git a/website/packets/outgoing/bulletinboard.json b/website/packets/outgoing/bulletinboard.json deleted file mode 100644 index 0059fec55..000000000 --- a/website/packets/outgoing/bulletinboard.json +++ /dev/null @@ -1,276 +0,0 @@ -{ - "category": "Bulletin Board", - "packets": [ - { - "id": "0x71", - "name": "Bulletin Board", - "description": "Bulletin board display and message packets.", - "direction": "outgoing", - "isDynamic": true, - "size": "Varies", - "variants": [ - { - "name": "Display Board", - "condition": "command == 0x00", - "size": 38, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x71", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "value": "38", - "description": "Packet length" - }, - { - "name": "Command", - "type": "byte", - "size": 1, - "value": "0x00", - "description": "Display board command" - }, - { - "name": "Board Serial", - "type": "uint", - "size": 4, - "description": "Serial of the bulletin board" - }, - { - "name": "Board Name", - "type": "utf8", - "size": 30, - "description": "Board name (null-padded to 30 bytes)" - } - ] - }, - { - "name": "Message Header", - "condition": "command == 0x01", - "size": "22+", - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x71", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Command", - "type": "byte", - "size": 1, - "value": "0x01", - "description": "Message header command" - }, - { - "name": "Board Serial", - "type": "uint", - "size": 4, - "description": "Serial of the bulletin board" - }, - { - "name": "Message Serial", - "type": "uint", - "size": 4, - "description": "Serial of the message" - }, - { - "name": "Thread Serial", - "type": "uint", - "size": 4, - "description": "Serial of parent thread (0 if root)" - }, - { - "name": "Poster Length", - "type": "byte", - "size": 1, - "description": "Length of poster name" - }, - { - "name": "Poster", - "type": "utf8-t", - "description": "Poster name" - }, - { - "name": "Subject Length", - "type": "byte", - "size": 1, - "description": "Length of subject" - }, - { - "name": "Subject", - "type": "utf8-t", - "description": "Message subject" - }, - { - "name": "Time Length", - "type": "byte", - "size": 1, - "description": "Length of time string" - }, - { - "name": "Time", - "type": "utf8-t", - "description": "Posted time string" - } - ] - }, - { - "name": "Message Content", - "condition": "command == 0x02", - "size": "22+", - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x71", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Command", - "type": "byte", - "size": 1, - "value": "0x02", - "description": "Message content command" - }, - { - "name": "Board Serial", - "type": "uint", - "size": 4, - "description": "Serial of the bulletin board" - }, - { - "name": "Message Serial", - "type": "uint", - "size": 4, - "description": "Serial of the message" - }, - { - "name": "Poster Length", - "type": "byte", - "size": 1, - "description": "Length of poster name" - }, - { - "name": "Poster", - "type": "utf8-t", - "description": "Poster name" - }, - { - "name": "Subject Length", - "type": "byte", - "size": 1, - "description": "Length of subject" - }, - { - "name": "Subject", - "type": "utf8-t", - "description": "Message subject" - }, - { - "name": "Time Length", - "type": "byte", - "size": 1, - "description": "Length of time string" - }, - { - "name": "Time", - "type": "utf8-t", - "description": "Posted time string" - }, - { - "name": "Poster Body", - "type": "short", - "size": 2, - "description": "Poster body graphic" - }, - { - "name": "Poster Hue", - "type": "short", - "size": 2, - "description": "Poster body hue" - }, - { - "name": "Equip Count", - "type": "byte", - "size": 1, - "description": "Number of equipment items" - }, - { - "name": "Equipment", - "type": "loop", - "description": "Poster\u0027s equipment", - "loop": { - "countField": "equipCount", - "fields": [ - { - "name": "Item ID", - "type": "short", - "size": 2, - "description": "Equipment item graphic" - }, - { - "name": "Hue", - "type": "short", - "size": 2, - "description": "Equipment hue" - } - ] - } - }, - { - "name": "Line Count", - "type": "byte", - "size": 1, - "description": "Number of text lines" - }, - { - "name": "Lines", - "type": "loop", - "description": "Message lines", - "loop": { - "countField": "lineCount", - "fields": [ - { - "name": "Line Length", - "type": "byte", - "size": 1, - "description": "Line length" - }, - { - "name": "Line", - "type": "utf8", - "description": "Line text with 2-byte terminator (old client bug)" - } - ] - } - } - ] - } - ], - "source": { - "file": "Projects/UOContent/Items/Bulletin Boards/BulletinBoardPackets.cs", - "line": 168 - } - } - ] -} \ No newline at end of file diff --git a/website/packets/outgoing/chat.json b/website/packets/outgoing/chat.json deleted file mode 100644 index 87819fb1b..000000000 --- a/website/packets/outgoing/chat.json +++ /dev/null @@ -1,69 +0,0 @@ -{ - "category": "Chat", - "packets": [ - { - "id": "0xB2", - "name": "Chat Message", - "description": "Server sends chat system messages and commands to the client.", - "direction": "outgoing", - "isDynamic": true, - "size": "13+", - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xB2", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Command", - "type": "ushort", - "size": 2, - "description": "Chat command number (original number minus 20)" - }, - { - "name": "Language", - "type": "ascii", - "size": 4, - "description": "Language code (e.g., \u0027enu\u0027 for English)" - }, - { - "name": "Param 1", - "type": "utf16be-t", - "description": "First parameter (Big Endian Unicode, null-terminated)" - }, - { - "name": "Param 2", - "type": "utf16be-t", - "description": "Second parameter (Big Endian Unicode, null-terminated)" - } - ], - "related": [ - { - "id": "0xB3", - "direction": "incoming", - "relationship": "request", - "note": "Client sends Chat Action" - }, - { - "id": "0xB5", - "direction": "incoming", - "relationship": "request", - "note": "Client sends Open Chat Window Request" - } - ], - "notes": "Part of the in-game chat system. Command numbers are offset by -20 from original values.", - "source": { - "file": "Projects/UOContent/Engines/Chat/ChatPackets.cs", - "line": 104 - } - } - ] -} \ No newline at end of file diff --git a/website/packets/outgoing/combat.json b/website/packets/outgoing/combat.json deleted file mode 100644 index c5d09cef6..000000000 --- a/website/packets/outgoing/combat.json +++ /dev/null @@ -1,120 +0,0 @@ -{ - "category": "Combat", - "packets": [ - { - "id": "0x2F", - "name": "Swing", - "description": "Notifies client of a melee swing between two mobiles.", - "direction": "outgoing", - "isDynamic": false, - "size": 10, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x2F", - "description": "Packet identifier" - }, - { - "name": "Unknown", - "type": "byte", - "size": 1, - "value": "0x00", - "description": "Unknown" - }, - { - "name": "Attacker Serial", - "type": "uint", - "size": 4, - "description": "Serial of attacker" - }, - { - "name": "Defender Serial", - "type": "uint", - "size": 4, - "description": "Serial of defender" - } - ], - "source": { - "file": "Projects/Server/Network/Packets/OutgoingCombatPackets.cs", - "line": 23 - } - }, - { - "id": "0x72", - "name": "Set War Mode", - "description": "Sets the client\u0027s war/peace mode state.", - "direction": "outgoing", - "isDynamic": false, - "size": 5, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x72", - "description": "Packet identifier" - }, - { - "name": "Warmode", - "type": "bool", - "size": 1, - "description": "True = war mode, False = peace mode" - }, - { - "name": "Unknown1", - "type": "byte", - "size": 1, - "value": "0x00", - "description": "Unknown" - }, - { - "name": "Unknown2", - "type": "byte", - "size": 1, - "value": "0x32", - "description": "Unknown (50)" - }, - { - "name": "Unknown3", - "type": "byte", - "size": 1, - "value": "0x00", - "description": "Unknown" - } - ], - "source": { - "file": "Projects/Server/Network/Packets/OutgoingCombatPackets.cs", - "line": 40 - } - }, - { - "id": "0xAA", - "name": "Change Combatant", - "description": "Notifies client of current combat target.", - "direction": "outgoing", - "isDynamic": false, - "size": 5, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xAA", - "description": "Packet identifier" - }, - { - "name": "Combatant Serial", - "type": "uint", - "size": 4, - "description": "Serial of current combatant (0 = none)" - } - ], - "source": { - "file": "Projects/Server/Network/Packets/OutgoingCombatPackets.cs", - "line": 43 - } - } - ] -} \ No newline at end of file diff --git a/website/packets/outgoing/container.json b/website/packets/outgoing/container.json deleted file mode 100644 index f7a375753..000000000 --- a/website/packets/outgoing/container.json +++ /dev/null @@ -1,594 +0,0 @@ -{ - "category": "Container", - "packets": [ - { - "id": "0x24", - "name": "Display Container", - "description": "Opens a container gump on the client.", - "direction": "outgoing", - "isDynamic": false, - "size": "Varies", - "variants": [ - { - "name": "Classic", - "size": 7, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x24", - "description": "Packet identifier" - }, - { - "name": "Container Serial", - "type": "uint", - "size": 4, - "description": "Serial of the container" - }, - { - "name": "Gump ID", - "type": "ushort", - "size": 2, - "description": "Gump graphic ID" - } - ] - }, - { - "name": "High Seas Extended", - "condition": "Client version \u003e= 7.0.9.0 (HighSeas)", - "size": 9, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x24", - "description": "Packet identifier" - }, - { - "name": "Container Serial", - "type": "uint", - "size": 4, - "description": "Serial of the container" - }, - { - "name": "Gump ID", - "type": "ushort", - "size": 2, - "description": "Gump graphic ID" - }, - { - "name": "Container Type", - "type": "short", - "size": 2, - "value": "0x007D", - "description": "Container type (125)" - } - ] - } - ], - "clientVersion": { - "classic": {}, - "enhanced": {}, - "notes": "HighSeas (7.0.9.0+) adds containerType field" - }, - "source": { - "file": "Projects/Server/Network/Packets/OutgoingContainerPackets.cs", - "line": 108 - } - }, - { - "id": "0x25", - "name": "Container Content Update", - "description": "Updates a single item in a container.", - "direction": "outgoing", - "isDynamic": false, - "size": "Varies", - "variants": [ - { - "name": "Classic", - "size": 20, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x25", - "description": "Packet identifier" - }, - { - "name": "Item Serial", - "type": "uint", - "size": 4, - "description": "Serial of the item" - }, - { - "name": "Item ID", - "type": "ushort", - "size": 2, - "description": "Item graphic ID" - }, - { - "name": "Item ID Offset", - "type": "byte", - "size": 1, - "value": "0x00", - "description": "Item ID offset (signed)" - }, - { - "name": "Amount", - "type": "ushort", - "size": 2, - "description": "Stack amount" - }, - { - "name": "X", - "type": "short", - "size": 2, - "description": "X position in container" - }, - { - "name": "Y", - "type": "short", - "size": 2, - "description": "Y position in container" - }, - { - "name": "Container Serial", - "type": "uint", - "size": 4, - "description": "Serial of parent container" - }, - { - "name": "Hue", - "type": "ushort", - "size": 2, - "description": "Item hue" - } - ] - }, - { - "name": "Container Grid Lines", - "condition": "Client version \u003e= 6.0.1.7 (ContainerGridLines)", - "size": 21, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x25", - "description": "Packet identifier" - }, - { - "name": "Item Serial", - "type": "uint", - "size": 4, - "description": "Serial of the item" - }, - { - "name": "Item ID", - "type": "ushort", - "size": 2, - "description": "Item graphic ID" - }, - { - "name": "Item ID Offset", - "type": "byte", - "size": 1, - "value": "0x00", - "description": "Item ID offset (signed)" - }, - { - "name": "Amount", - "type": "ushort", - "size": 2, - "description": "Stack amount" - }, - { - "name": "X", - "type": "short", - "size": 2, - "description": "X position in container" - }, - { - "name": "Y", - "type": "short", - "size": 2, - "description": "Y position in container" - }, - { - "name": "Grid Location", - "type": "byte", - "size": 1, - "description": "Grid slot position" - }, - { - "name": "Container Serial", - "type": "uint", - "size": 4, - "description": "Serial of parent container" - }, - { - "name": "Hue", - "type": "ushort", - "size": 2, - "description": "Item hue" - } - ] - } - ], - "clientVersion": { - "classic": {}, - "enhanced": {}, - "notes": "ContainerGridLines (6.0.1.7+) adds gridLocation byte" - }, - "source": { - "file": "Projects/Server/Network/Packets/OutgoingContainerPackets.cs", - "line": 127 - } - }, - { - "id": "0x3C", - "name": "Container \u0026 Old Spellbook Content", - "description": "Sends full contents of a container. Also used for corpse contents and old-style spellbook content (pre-AOS clients). For AOS+ spellbooks, use 0xBF/0x1B instead.", - "direction": "outgoing", - "isDynamic": true, - "size": "Varies", - "tags": ["Spellbook", "Corpse"], - "variants": [ - { - "name": "Classic Format", - "condition": "Client version \u003c 6.0.1.7", - "size": "5 + (itemCount x 19)", - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x3C", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Item Count", - "type": "ushort", - "size": 2, - "description": "Number of items" - }, - { - "name": "Items", - "type": "loop", - "description": "Item entries (19 bytes each)", - "loop": { - "countField": "itemCount", - "itemSize": 19, - "fields": [ - { - "name": "Item Serial", - "type": "uint", - "size": 4, - "description": "Serial of the item" - }, - { - "name": "Item ID", - "type": "ushort", - "size": 2, - "description": "Item graphic ID" - }, - { - "name": "Item ID Offset", - "type": "byte", - "size": 1, - "description": "Item ID offset (signed)" - }, - { - "name": "Amount", - "type": "ushort", - "size": 2, - "description": "Stack amount" - }, - { - "name": "X", - "type": "short", - "size": 2, - "description": "X position in container" - }, - { - "name": "Y", - "type": "short", - "size": 2, - "description": "Y position in container" - }, - { - "name": "Container Serial", - "type": "uint", - "size": 4, - "description": "Serial of parent container" - }, - { - "name": "Hue", - "type": "ushort", - "size": 2, - "description": "Item hue" - } - ] - } - } - ] - }, - { - "name": "Grid Location Format", - "condition": "Client version \u003e= 6.0.1.7 (ContainerGridLines)", - "size": "5 + (itemCount x 20)", - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x3C", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Item Count", - "type": "ushort", - "size": 2, - "description": "Number of items" - }, - { - "name": "Items", - "type": "loop", - "description": "Item entries (20 bytes each)", - "loop": { - "countField": "itemCount", - "itemSize": 20, - "fields": [ - { - "name": "Item Serial", - "type": "uint", - "size": 4, - "description": "Serial of the item" - }, - { - "name": "Item ID", - "type": "ushort", - "size": 2, - "description": "Item graphic ID" - }, - { - "name": "Item ID Offset", - "type": "byte", - "size": 1, - "description": "Item ID offset (signed)" - }, - { - "name": "Amount", - "type": "ushort", - "size": 2, - "description": "Stack amount" - }, - { - "name": "X", - "type": "short", - "size": 2, - "description": "X position in container" - }, - { - "name": "Y", - "type": "short", - "size": 2, - "description": "Y position in container" - }, - { - "name": "Grid Location", - "type": "byte", - "size": 1, - "description": "Grid slot position in container" - }, - { - "name": "Container Serial", - "type": "uint", - "size": 4, - "description": "Serial of parent container" - }, - { - "name": "Hue", - "type": "ushort", - "size": 2, - "description": "Item hue" - } - ] - } - } - ] - } - ], - "related": [ - { - "id": "0xBF/0x1B", - "relationship": "variant", - "note": "New Spellbook Content for AOS+ clients" - }, - { - "id": "0x89", - "relationship": "related", - "note": "Corpse Equipment (layer mappings for corpse items)" - } - ], - "clientVersion": { - "classic": {}, - "enhanced": {}, - "notes": "ContainerGridLines (6.0.1.7+) adds gridLocation byte per item" - }, - "notes": "Also used for corpse contents. Corpses are special containers that display equipped items on a body graphic. Use with 0x89 Corpse Equipment for full corpse display.", - "source": { - "file": "Projects/Server/Network/Packets/OutgoingContainerPackets.cs", - "line": 169 - } - }, - { - "id": "0xBF", - "subId": "0x1B", - "name": "New Spellbook Content", - "description": "Sends spellbook contents using AOS format with a 64-bit bitmask of known spells.", - "direction": "outgoing", - "isDynamic": false, - "size": 23, - "tags": ["Spell", "Spellbook", "Extended Commands (0xBF)"], - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xBF", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "value": "0x0017", - "description": "Packet length (23)" - }, - { - "name": "Sub-Command", - "type": "ushort", - "size": 2, - "value": "0x001B", - "description": "Subcommand (New Spellbook Content)" - }, - { - "name": "Command", - "type": "short", - "size": 2, - "value": "0x0001", - "description": "Command (always 1)" - }, - { - "name": "Book Serial", - "type": "uint", - "size": 4, - "description": "Serial of the spellbook" - }, - { - "name": "Graphic", - "type": "short", - "size": 2, - "description": "Spellbook graphic ID" - }, - { - "name": "Offset", - "type": "short", - "size": 2, - "description": "Spell offset (circle/school start)" - }, - { - "name": "Spell Bits", - "type": "ulong", - "size": 8, - "description": "64-bit mask of known spells (written byte-by-byte, Little Endian)" - } - ], - "related": [ - { - "id": "0x3C", - "relationship": "variant", - "note": "Old Spellbook Content for pre-AOS clients" - } - ], - "clientVersion": { - "classic": { - "min": "5.0.0a" - }, - "enhanced": {}, - "notes": "NewSpellbook feature (Core.AOS)" - }, - "notes": "Used when Core.AOS \u0026\u0026 ns.NewSpellbook. Otherwise, old 0x3C format is used with spells as pseudo-items.", - "source": { - "file": "Projects/Server/Network/Packets/OutgoingContainerPackets.cs", - "line": 46 - } - }, - { - "id": "0xF7", - "name": "Packet Container", - "description": "Container packet for batching multiple entity packets. Used for sending groups of related entities efficiently, such as all items and mobiles visible on a boat.", - "direction": "outgoing", - "tags": ["Boat"], - "isDynamic": true, - "size": "5+", - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xF7", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Total packet length" - }, - { - "name": "Packet Count", - "type": "ushort", - "size": 2, - "description": "Number of embedded packets" - }, - { - "name": "Packets", - "type": "array", - "description": "Embedded packets (typically 0xF3 World Entity packets)", - "loop": { - "countField": "packetCount", - "fields": [ - { - "name": "Embedded Packet", - "type": "bytes", - "size": "var", - "description": "Complete embedded packet (typically 24-26 byte 0xF3 World Entity)" - } - ] - } - } - ], - "related": [ - { - "id": "0xF3", - "relationship": "contains", - "note": "Typically contains World Entity packets" - }, - { - "id": "0xF6", - "relationship": "variant", - "note": "Used alongside Move Boat HS for boat display" - } - ], - "clientVersion": { - "classic": { - "min": "7.0.9.0" - }, - "enhanced": {}, - "notes": "HighSeas expansion feature" - }, - "notes": "Uses PacketContainerBuilder for efficient dynamic growth. Commonly used by boat system to batch all visible entities on deck. Minimum packet length is 5 bytes (header only).", - "source": { - "file": "Projects/Server/Network/Packets/PacketContainerBuilder.cs", - "line": 23 - } - } - ] -} \ No newline at end of file diff --git a/website/packets/outgoing/contextmenu.json b/website/packets/outgoing/contextmenu.json deleted file mode 100644 index 6249e70da..000000000 --- a/website/packets/outgoing/contextmenu.json +++ /dev/null @@ -1,212 +0,0 @@ -{ - "category": "Context Menu", - "packets": [ - { - "id": "0xBF", - "subId": "0x14", - "name": "Display Context Menu", - "description": "Displays a context menu for an entity.", - "direction": "outgoing", - "isDynamic": true, - "size": "Varies", - "variants": [ - { - "name": "Old Format", - "condition": "command == 0x01 (pre-NewHaven)", - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xBF", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Sub-Command", - "type": "ushort", - "size": 2, - "value": "0x0014", - "description": "Context Menu subcommand" - }, - { - "name": "Command", - "type": "short", - "size": 2, - "value": "0x0001", - "description": "Old format command" - }, - { - "name": "Target Serial", - "type": "uint", - "size": 4, - "description": "Serial of the target entity" - }, - { - "name": "Entry Count", - "type": "byte", - "size": 1, - "description": "Number of menu entries" - }, - { - "name": "Entries", - "type": "loop", - "description": "Menu entries", - "loop": { - "countField": "entryCount", - "fields": [ - { - "name": "Index", - "type": "short", - "size": 2, - "description": "Entry index" - }, - { - "name": "Cliloc Offset", - "type": "ushort", - "size": 2, - "description": "Cliloc number - 3000000" - }, - { - "name": "Flags", - "type": "bitfield", - "size": 2, - "description": "Entry flags", - "flags": [ - { - "bit": "0", - "name": "Disabled", - "description": "Entry is disabled" - }, - { - "bit": "1", - "name": "Arrow", - "description": "Show arrow" - }, - { - "bit": "2", - "name": "Highlighted", - "description": "Entry is highlighted" - }, - { - "bit": "5", - "name": "Colored", - "description": "Entry uses custom color" - } - ] - }, - { - "name": "Hue", - "type": "short", - "size": 2, - "description": "Text hue (if Colored flag set)", - "condition": "flags \u0026 0x20" - } - ] - } - } - ] - }, - { - "name": "New Format", - "condition": "command == 0x02 (NewHaven+)", - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xBF", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Sub-Command", - "type": "ushort", - "size": 2, - "value": "0x0014", - "description": "Context Menu subcommand" - }, - { - "name": "Command", - "type": "short", - "size": 2, - "value": "0x0002", - "description": "New format command" - }, - { - "name": "Target Serial", - "type": "uint", - "size": 4, - "description": "Serial of the target entity" - }, - { - "name": "Entry Count", - "type": "byte", - "size": 1, - "description": "Number of menu entries" - }, - { - "name": "Entries", - "type": "loop", - "description": "Menu entries", - "loop": { - "countField": "entryCount", - "fields": [ - { - "name": "Cliloc Number", - "type": "int", - "size": 4, - "description": "Full cliloc number" - }, - { - "name": "Index", - "type": "short", - "size": 2, - "description": "Entry index" - }, - { - "name": "Flags", - "type": "short", - "size": 2, - "description": "Entry flags (see Old Format)" - } - ] - } - } - ] - } - ], - "related": [ - { - "id": "0xBF/0x13", - "relationship": "request", - "note": "Context Menu Request from client" - }, - { - "id": "0xBF/0x15", - "relationship": "response", - "note": "Context Menu Response from client" - } - ], - "clientVersion": { - "classic": {}, - "enhanced": {}, - "notes": "NewHaven clients (6.0.0.0+) use command 0x02" - }, - "source": { - "file": "Projects/UOContent/Context Menus/ContextMenuSystem.cs", - "line": 142 - } - } - ] -} \ No newline at end of file diff --git a/website/packets/outgoing/corpse.json b/website/packets/outgoing/corpse.json deleted file mode 100644 index 951fe8f09..000000000 --- a/website/packets/outgoing/corpse.json +++ /dev/null @@ -1,76 +0,0 @@ -{ - "category": "Corpse", - "packets": [ - { - "id": "0x89", - "name": "Corpse Equipment", - "description": "Sends the equipment layer mapping for items on a corpse, including virtual hair and facial hair items.", - "direction": "outgoing", - "isDynamic": true, - "size": "8+", - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x89", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Corpse Serial", - "type": "uint", - "size": 4, - "description": "Serial of the corpse" - }, - { - "name": "Equipment", - "type": "loop", - "description": "Equipment layer mappings (5 bytes each, until terminator)", - "loop": { - "terminator": "Layer == 0", - "fields": [ - { - "name": "Layer", - "type": "byte", - "size": 1, - "description": "Equipment layer + 1 (0 = terminator)" - }, - { - "name": "Item Serial", - "type": "uint", - "size": 4, - "description": "Serial of item in this layer" - } - ] - } - }, - { - "name": "Terminator", - "type": "byte", - "size": 1, - "value": "0x00", - "description": "Layer.Invalid (0) marks end of list" - } - ], - "notes": "Layer values are offset by +1 (e.g., Layer.Hair (0x0B) is sent as 0x0C). Hair and facial hair use virtual serials.", - "related": [ - { - "id": "0x3C", - "direction": "outgoing", - "relationship": "related", - "note": "Container Content (also used for corpse items)" - } - ], - "source": { - "file": "Projects/UOContent/Items/Misc/Corpses/CorpsePackets.cs", - "line": 24 - } - } - ] -} diff --git a/website/packets/outgoing/damage.json b/website/packets/outgoing/damage.json deleted file mode 100644 index 24f927fbe..000000000 --- a/website/packets/outgoing/damage.json +++ /dev/null @@ -1,107 +0,0 @@ -{ - "category": "Combat", - "packets": [ - { - "id": "0x0B", - "name": "Damage", - "description": "Shows damage dealt to a mobile (client 5.0.0a+).", - "direction": "outgoing", - "isDynamic": false, - "size": 7, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x0B", - "description": "Packet identifier" - }, - { - "name": "Serial", - "type": "uint", - "size": 4, - "description": "Serial of damaged mobile" - }, - { - "name": "Damage", - "type": "ushort", - "size": 2, - "description": "Damage amount (clamped 0-65535)" - } - ], - "clientVersion": { - "classic": { - "min": "5.0.0a" - }, - "enhanced": {}, - "notes": "Used by Enhanced Client (all versions) and Classic Client 5.0.0a+" - }, - "notes": "For pre-5.0.0a clients, use 0xBF/0x22 instead. Enhanced Client always uses this packet format (ushort damage allows values up to 65535).", - "source": { - "file": "Projects/Server/Network/Packets/OutgoingDamagePackets.cs", - "line": 34 - } - }, - { - "id": "0xBF/0x22", - "name": "Damage (Old)", - "description": "Shows damage dealt to a mobile (pre-5.0.0a clients).", - "direction": "outgoing", - "isDynamic": false, - "size": 11, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xBF", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "value": "11", - "description": "Packet length" - }, - { - "name": "Sub-Command", - "type": "ushort", - "size": 2, - "value": "0x22", - "description": "Subcommand (Damage)" - }, - { - "name": "Unknown", - "type": "byte", - "size": 1, - "value": "0x01", - "description": "Unknown (always 1)" - }, - { - "name": "Serial", - "type": "uint", - "size": 4, - "description": "Serial of damaged mobile" - }, - { - "name": "Damage", - "type": "byte", - "size": 1, - "description": "Damage amount (clamped 0-255)" - } - ], - "clientVersion": { - "classic": { - "max": "5.0.0a" - }, - "notes": "For Classic Client before DamagePacket feature. Not used by Enhanced Client." - }, - "notes": "Damage is capped at 255 for old clients. Classic Client only; Enhanced Client uses 0x0B packet.", - "source": { - "file": "Projects/Server/Network/Packets/OutgoingDamagePackets.cs", - "line": 40 - } - } - ] -} diff --git a/website/packets/outgoing/effects.json b/website/packets/outgoing/effects.json deleted file mode 100644 index 47af65187..000000000 --- a/website/packets/outgoing/effects.json +++ /dev/null @@ -1,459 +0,0 @@ -{ - "category": "Effects", - "packets": [ - { - "id": "0x54", - "name": "Sound Effect", - "description": "Plays a sound effect at a location.", - "direction": "outgoing", - "isDynamic": false, - "size": 12, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x54", - "description": "Packet identifier" - }, - { - "name": "Flags", - "type": "byte", - "size": 1, - "value": "0x01", - "description": "Sound flags" - }, - { - "name": "Sound ID", - "type": "short", - "size": 2, - "description": "Sound effect ID" - }, - { - "name": "Volume", - "type": "short", - "size": 2, - "value": "0x0000", - "description": "Volume (unused)" - }, - { - "name": "X", - "type": "short", - "size": 2, - "description": "X coordinate" - }, - { - "name": "Y", - "type": "short", - "size": 2, - "description": "Y coordinate" - }, - { - "name": "Z", - "type": "short", - "size": 2, - "description": "Z coordinate" - } - ], - "source": { - "file": "Projects/Server/Network/Packets/OutgoingEffectPackets.cs", - "line": 41 - } - }, - { - "id": "0x70", - "name": "Screen Effect", - "description": "Triggers a screen-wide visual effect (fade in/out).", - "direction": "outgoing", - "isDynamic": false, - "size": 28, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x70", - "description": "Packet identifier" - }, - { - "name": "Effect Type", - "type": "byte", - "size": 1, - "value": "0x04", - "description": "Effect type (4)" - }, - { - "name": "Padding1", - "type": "byte[]", - "size": 8, - "description": "Padding (zeros)" - }, - { - "name": "Type", - "type": "enum", - "enumType": "sequential", - "size": 2, - "description": "Screen effect type", - "values": [ - { - "value": 0, - "name": "Fade Out", - "description": "Fade screen to black" - }, - { - "value": 1, - "name": "Fade In", - "description": "Fade screen from black" - }, - { - "value": 2, - "name": "Light Flash", - "description": "Light flash effect" - }, - { - "value": 3, - "name": "Fade In Out", - "description": "Fade out then in" - }, - { - "value": 4, - "name": "Darken Screen", - "description": "Darken screen" - } - ] - }, - { - "name": "Padding2", - "type": "byte[]", - "size": 16, - "description": "Padding (zeros)" - } - ], - "source": { - "file": "Projects/Server/Network/Packets/OutgoingEffectPackets.cs", - "line": 322 - } - }, - { - "id": "0xC0", - "name": "Hued Effect", - "description": "Displays a graphical effect with hue and render mode.", - "direction": "outgoing", - "isDynamic": false, - "size": 36, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xC0", - "description": "Packet identifier" - }, - { - "name": "Effect Type", - "type": "enum", - "enumType": "sequential", - "size": 1, - "description": "Effect type", - "values": [ - { - "value": 0, - "name": "Moving", - "description": "Effect moves between two points" - }, - { - "value": 1, - "name": "Lightning", - "description": "Lightning bolt effect" - }, - { - "value": 2, - "name": "Fixed XYZ", - "description": "Effect at fixed location" - }, - { - "value": 3, - "name": "Fixed From", - "description": "Effect attached to source" - } - ] - }, - { - "name": "Source Serial", - "type": "uint", - "size": 4, - "description": "Serial of source entity" - }, - { - "name": "Target Serial", - "type": "uint", - "size": 4, - "description": "Serial of target entity" - }, - { - "name": "Item ID", - "type": "short", - "size": 2, - "description": "Effect graphic ID" - }, - { - "name": "Src X", - "type": "short", - "size": 2, - "description": "Source X coordinate" - }, - { - "name": "Src Y", - "type": "short", - "size": 2, - "description": "Source Y coordinate" - }, - { - "name": "Src Z", - "type": "sbyte", - "size": 1, - "description": "Source Z coordinate" - }, - { - "name": "Dst X", - "type": "short", - "size": 2, - "description": "Destination X coordinate" - }, - { - "name": "Dst Y", - "type": "short", - "size": 2, - "description": "Destination Y coordinate" - }, - { - "name": "Dst Z", - "type": "sbyte", - "size": 1, - "description": "Destination Z coordinate" - }, - { - "name": "Speed", - "type": "byte", - "size": 1, - "description": "Effect speed" - }, - { - "name": "Duration", - "type": "byte", - "size": 1, - "description": "Effect duration" - }, - { - "name": "Unknown1", - "type": "byte", - "size": 1, - "value": "0x00", - "description": "Unknown" - }, - { - "name": "Unknown2", - "type": "byte", - "size": 1, - "value": "0x00", - "description": "Unknown" - }, - { - "name": "Fixed Direction", - "type": "bool", - "size": 1, - "description": "Fixed direction" - }, - { - "name": "Explode", - "type": "bool", - "size": 1, - "description": "Explode on impact" - }, - { - "name": "Hue", - "type": "int", - "size": 4, - "description": "Effect hue" - }, - { - "name": "Render Mode", - "type": "int", - "size": 4, - "description": "Render mode" - } - ], - "source": { - "file": "Projects/Server/Network/Packets/OutgoingEffectPackets.cs", - "line": 175 - } - }, - { - "id": "0xC7", - "name": "Particle Effect", - "description": "Displays an advanced particle effect with extended parameters.", - "direction": "outgoing", - "isDynamic": false, - "size": 49, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xC7", - "description": "Packet identifier" - }, - { - "name": "Effect Type", - "type": "byte", - "size": 1, - "description": "Effect type (see 0xC0)" - }, - { - "name": "Source Serial", - "type": "uint", - "size": 4, - "description": "Serial of source entity" - }, - { - "name": "Target Serial", - "type": "uint", - "size": 4, - "description": "Serial of target entity" - }, - { - "name": "Item ID", - "type": "short", - "size": 2, - "description": "Effect graphic ID" - }, - { - "name": "Src X", - "type": "short", - "size": 2, - "description": "Source X coordinate" - }, - { - "name": "Src Y", - "type": "short", - "size": 2, - "description": "Source Y coordinate" - }, - { - "name": "Src Z", - "type": "sbyte", - "size": 1, - "description": "Source Z coordinate" - }, - { - "name": "Dst X", - "type": "short", - "size": 2, - "description": "Destination X coordinate" - }, - { - "name": "Dst Y", - "type": "short", - "size": 2, - "description": "Destination Y coordinate" - }, - { - "name": "Dst Z", - "type": "sbyte", - "size": 1, - "description": "Destination Z coordinate" - }, - { - "name": "Speed", - "type": "byte", - "size": 1, - "description": "Effect speed" - }, - { - "name": "Duration", - "type": "byte", - "size": 1, - "description": "Effect duration" - }, - { - "name": "Unknown1", - "type": "byte", - "size": 1, - "value": "0x00", - "description": "Unknown" - }, - { - "name": "Unknown2", - "type": "byte", - "size": 1, - "value": "0x00", - "description": "Unknown" - }, - { - "name": "Fixed Direction", - "type": "bool", - "size": 1, - "description": "Fixed direction" - }, - { - "name": "Explode", - "type": "bool", - "size": 1, - "description": "Explode on impact" - }, - { - "name": "Hue", - "type": "int", - "size": 4, - "description": "Effect hue" - }, - { - "name": "Render Mode", - "type": "int", - "size": 4, - "description": "Render mode" - }, - { - "name": "Effect", - "type": "short", - "size": 2, - "description": "Particle effect ID" - }, - { - "name": "Explode Effect", - "type": "short", - "size": 2, - "description": "Explosion effect ID" - }, - { - "name": "Explode Sound", - "type": "short", - "size": 2, - "description": "Explosion sound ID" - }, - { - "name": "Serial", - "type": "uint", - "size": 4, - "description": "Associated entity serial" - }, - { - "name": "Layer", - "type": "byte", - "size": 1, - "description": "Effect layer" - }, - { - "name": "Unknown3", - "type": "short", - "size": 2, - "description": "Unknown" - } - ], - "source": { - "file": "Projects/Server/Network/Packets/OutgoingEffectPackets.cs", - "line": 58 - } - } - ] -} diff --git a/website/packets/outgoing/entity.json b/website/packets/outgoing/entity.json deleted file mode 100644 index b0627f7e6..000000000 --- a/website/packets/outgoing/entity.json +++ /dev/null @@ -1,403 +0,0 @@ -{ - "category": "Item", - "tags": ["Mobile"], - "packets": [ - { - "id": "0xD6", - "name": "Object Property List", - "description": "Sends the full Object Property List (tooltip) for an entity. Contains a hash for client caching and a list of localized property entries.", - "direction": "outgoing", - "isDynamic": true, - "size": "15+", - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xD6", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Unknown1", - "type": "ushort", - "size": 2, - "value": "0x0001", - "description": "Unknown (always 1)" - }, - { - "name": "Serial", - "type": "uint", - "size": 4, - "description": "Entity serial" - }, - { - "name": "Unknown2", - "type": "ushort", - "size": 2, - "value": "0x0000", - "description": "Unknown (always 0)" - }, - { - "name": "Hash", - "type": "int", - "size": 4, - "description": "XOR hash of all properties (computed with 26-bit mask)" - }, - { - "name": "Properties", - "type": "array", - "description": "Property entries, terminated by number=0", - "loop": { - "terminator": "number == 0", - "fields": [ - { - "name": "Number", - "type": "int", - "size": 4, - "description": "Cliloc number (0 = end of list)" - }, - { - "name": "String Length", - "type": "ushort", - "size": 2, - "description": "Length of arguments in bytes (0 if no arguments)" - }, - { - "name": "Arguments", - "type": "utf16le", - "size": "var", - "description": "Unicode LE string with tab-separated cliloc arguments. Length = String Length field" - } - ] - } - } - ], - "related": [ - { - "id": "0xDC", - "relationship": "notification", - "note": "OPL Info notifies client of hash change, triggering 0xD6 request" - } - ], - "clientVersion": { - "classic": { - "min": "4.0.0a" - }, - "enhanced": {}, - "notes": "AOS tooltips feature" - }, - "notes": "Hash is calculated by XORing each property\u0027s cliloc number with ((hash \u003e\u003e 31) \u0026 1) ^ (hash \u003c\u003c 1). Properties are cliloc entries with optional Unicode LE arguments separated by tabs.", - "source": { - "file": "Projects/Server/PropertyList/ObjectPropertyList.cs", - "line": 19 - } - }, - { - "id": "0x1D", - "name": "Remove Entity", - "description": "Removes an entity (item, mobile, or multi) from the client\u0027s view.", - "direction": "outgoing", - "isDynamic": false, - "size": 5, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x1D", - "description": "Packet identifier" - }, - { - "name": "Serial", - "type": "uint", - "size": 4, - "description": "Serial of entity to remove" - } - ], - "source": { - "file": "Projects/Server/Network/Packets/OutgoingEntityPackets.cs", - "line": 75 - } - }, - { - "id": "0xDC", - "name": "OPL Info (Object Property List)", - "description": "Notifies the client of an entity\u0027s Object Property List hash. Client compares hash to cached version and requests full OPL if different.", - "direction": "outgoing", - "isDynamic": false, - "size": 9, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xDC", - "description": "Packet identifier" - }, - { - "name": "Serial", - "type": "uint", - "size": 4, - "description": "Entity serial" - }, - { - "name": "Hash", - "type": "int", - "size": 4, - "description": "Hash of the Object Property List" - } - ], - "related": [ - { - "id": "0xD6", - "direction": "outgoing", - "relationship": "data", - "note": "Full Object Property List packet" - } - ], - "source": { - "file": "Projects/Server/Network/Packets/OutgoingEntityPackets.cs", - "line": 33 - } - }, - { - "id": "0xF3", - "name": "World Entity (SA+)", - "description": "Unified entity packet for items, mobiles, and multis. Replaces older 0x1A World Item packet for Stygian Abyss+ clients. High Seas clients add 2 extra bytes.", - "direction": "outgoing", - "isDynamic": false, - "size": "Varies", - "variants": [ - { - "version": "Stygian Abyss", - "name": "SA Format", - "condition": "Client version 7.0.0.0 - 7.0.8.x (StygianAbyss, pre-HighSeas)", - "size": 24, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xF3", - "description": "Packet identifier" - }, - { - "name": "Command", - "type": "short", - "size": 2, - "value": "0x0001", - "description": "Command (always 1)" - }, - { - "name": "Entity Type", - "type": "enum", - "size": 1, - "description": "Entity type", - "values": [ - { "value": "0", "name": "Item", "description": "World item" }, - { "value": "1", "name": "Mobile", "description": "Mobile/creature" }, - { "value": "2", "name": "Multi", "description": "Multi/structure" } - ] - }, - { - "name": "Serial", - "type": "uint", - "size": 4, - "description": "Entity serial" - }, - { - "name": "Graphic ID", - "type": "ushort", - "size": 2, - "description": "Graphic/Body ID (masked with 0x7FFF)" - }, - { - "name": "Direction", - "type": "byte", - "size": 1, - "description": "Direction (mobiles) or 0" - }, - { - "name": "Amount Min", - "type": "short", - "size": 2, - "description": "Minimum amount (items) or 1" - }, - { - "name": "Amount Max", - "type": "short", - "size": 2, - "description": "Maximum amount (items) or 1" - }, - { - "name": "X", - "type": "short", - "size": 2, - "description": "X coordinate (masked with 0x7FFF)" - }, - { - "name": "Y", - "type": "short", - "size": 2, - "description": "Y coordinate (masked with 0x3FFF)" - }, - { - "name": "Z", - "type": "sbyte", - "size": 1, - "description": "Z coordinate" - }, - { - "name": "Light", - "type": "byte", - "size": 1, - "description": "Light level (items)" - }, - { - "name": "Hue", - "type": "short", - "size": 2, - "description": "Hue/color" - }, - { - "name": "Flags", - "type": "byte", - "size": 1, - "description": "Packet flags (hidden, blessed, etc.)" - } - ] - }, - { - "version": "High Seas+", - "name": "HS Format", - "condition": "Client version \u003e= 7.0.9.0 (HighSeas)", - "size": 26, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xF3", - "description": "Packet identifier" - }, - { - "name": "Command", - "type": "short", - "size": 2, - "value": "0x0001", - "description": "Command (always 1)" - }, - { - "name": "Entity Type", - "type": "enum", - "size": 1, - "description": "Entity type", - "values": [ - { "value": "0", "name": "Item", "description": "World item" }, - { "value": "1", "name": "Mobile", "description": "Mobile/creature" }, - { "value": "2", "name": "Multi", "description": "Multi/structure" } - ] - }, - { - "name": "Serial", - "type": "uint", - "size": 4, - "description": "Entity serial" - }, - { - "name": "Graphic ID", - "type": "ushort", - "size": 2, - "description": "Graphic/Body ID (masked with 0xFFFF, supports higher IDs)" - }, - { - "name": "Direction", - "type": "byte", - "size": 1, - "description": "Direction (mobiles) or 0" - }, - { - "name": "Amount Min", - "type": "short", - "size": 2, - "description": "Minimum amount (items) or 1" - }, - { - "name": "Amount Max", - "type": "short", - "size": 2, - "description": "Maximum amount (items) or 1" - }, - { - "name": "X", - "type": "short", - "size": 2, - "description": "X coordinate (masked with 0x7FFF)" - }, - { - "name": "Y", - "type": "short", - "size": 2, - "description": "Y coordinate (masked with 0x3FFF)" - }, - { - "name": "Z", - "type": "sbyte", - "size": 1, - "description": "Z coordinate" - }, - { - "name": "Light", - "type": "byte", - "size": 1, - "description": "Light level (items)" - }, - { - "name": "Hue", - "type": "short", - "size": 2, - "description": "Hue/color" - }, - { - "name": "Flags", - "type": "byte", - "size": 1, - "description": "Packet flags (hidden, blessed, etc.)" - }, - { - "name": "Unknown", - "type": "short", - "size": 2, - "value": "0x0000", - "description": "Unknown (always 0)" - } - ] - } - ], - "related": [ - { - "id": "0x1A", - "relationship": "variant", - "note": "Legacy World Item packet for pre-SA clients" - } - ], - "clientVersion": { - "classic": { - "min": "7.0.0.0" - }, - "enhanced": {}, - "notes": "StygianAbyss expansion feature" - }, - "source": { - "file": "Projects/Server/Network/Packets/OutgoingEntityPackets.cs", - "line": 88 - } - } - ] -} \ No newline at end of file diff --git a/website/packets/outgoing/equipment.json b/website/packets/outgoing/equipment.json deleted file mode 100644 index a86595f27..000000000 --- a/website/packets/outgoing/equipment.json +++ /dev/null @@ -1,173 +0,0 @@ -{ - "category": "Equipment", - "packets": [ - { - "id": "0x2E", - "name": "Equip Update", - "description": "Notifies client that an item has been equipped on a mobile.", - "direction": "outgoing", - "isDynamic": false, - "size": 15, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x2E", - "description": "Packet identifier" - }, - { - "name": "Item Serial", - "type": "uint", - "size": 4, - "description": "Serial of the equipped item" - }, - { - "name": "Item ID", - "type": "short", - "size": 2, - "description": "Item graphic ID" - }, - { - "name": "Layer", - "type": "ushort", - "size": 2, - "description": "Equipment layer" - }, - { - "name": "Mobile Serial", - "type": "uint", - "size": 4, - "description": "Serial of the mobile wearing the item" - }, - { - "name": "Hue", - "type": "short", - "size": 2, - "description": "Item hue (may be overridden by SolidHueOverride)" - } - ], - "source": { - "file": "Projects/Server/Network/Packets/OutgoingEquipmentPackets.cs", - "line": 86 - } - }, - { - "id": "0xBF", - "subId": "0x10", - "name": "Display Equipment Info", - "description": "Displays detailed equipment information including crafter name and magical properties.", - "direction": "outgoing", - "isDynamic": true, - "size": "17+", - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xBF", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Sub-Command", - "type": "ushort", - "size": 2, - "value": "0x0010", - "description": "Display Equipment Info subcommand" - }, - { - "name": "Item Serial", - "type": "uint", - "size": 4, - "description": "Serial of the item" - }, - { - "name": "Cliloc Number", - "type": "int", - "size": 4, - "description": "Base cliloc number for item name" - }, - { - "name": "Crafter Info", - "type": "conditional", - "description": "Crafter information (if present)", - "condition": "crafterName.Length \u003e 0", - "fields": [ - { - "name": "Crafted By", - "type": "int", - "size": 4, - "value": "-3", - "description": "Crafter marker" - }, - { - "name": "Crafter Name Length", - "type": "ushort", - "size": 2, - "description": "Length of crafter name" - }, - { - "name": "Crafter Name", - "type": "ascii", - "description": "Crafter name" - } - ] - }, - { - "name": "Unidentified", - "type": "conditional", - "description": "Unidentified marker", - "condition": "unidentified", - "fields": [ - { - "name": "Unidentified Marker", - "type": "int", - "size": 4, - "value": "-4", - "description": "Unidentified marker" - } - ] - }, - { - "name": "Attributes", - "type": "loop", - "description": "Equipment attributes", - "loop": { - "countField": "variable", - "fields": [ - { - "name": "Attribute Number", - "type": "int", - "size": 4, - "description": "Attribute cliloc number" - }, - { - "name": "Charges", - "type": "short", - "size": 2, - "description": "Charges (-1 if not applicable)" - } - ] - } - }, - { - "name": "Terminator", - "type": "int", - "size": 4, - "value": "-1", - "description": "End of attributes marker" - } - ], - "source": { - "file": "Projects/Server/Network/Packets/OutgoingEquipmentPackets.cs", - "line": 37 - } - } - ] -} \ No newline at end of file diff --git a/website/packets/outgoing/freeshard.json b/website/packets/outgoing/freeshard.json deleted file mode 100644 index 38bb5a6af..000000000 --- a/website/packets/outgoing/freeshard.json +++ /dev/null @@ -1,70 +0,0 @@ -{ - "category": "FreeShard Protocol", - "packets": [ - { - "id": "0x51", - "name": "Compact Shard Stats", - "description": "Server response to compact shard stats query (0xF1/0xFE).", - "direction": "outgoing", - "isDynamic": false, - "size": 27, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x51", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "value": "0x001B", - "description": "Packet length (27)" - }, - { - "name": "Clients", - "type": "int", - "size": 4, - "description": "Current client count" - }, - { - "name": "Items", - "type": "int", - "size": 4, - "description": "Total item count" - }, - { - "name": "Mobiles", - "type": "int", - "size": 4, - "description": "Total mobile count" - }, - { - "name": "Age", - "type": "uint", - "size": 4, - "description": "Server uptime in seconds" - }, - { - "name": "Memory", - "type": "long", - "size": 8, - "description": "Memory usage in bytes" - } - ], - "related": [ - { - "id": "0xF1/0xFE", - "relationship": "request", - "note": "Query Compact Shard Stats request" - } - ], - "source": { - "file": "Projects/UOContent/Network/UOGateway.cs", - "line": 60 - } - } - ] -} diff --git a/website/packets/outgoing/gump.json b/website/packets/outgoing/gump.json deleted file mode 100644 index 21b338ae9..000000000 --- a/website/packets/outgoing/gump.json +++ /dev/null @@ -1,1060 +0,0 @@ -{ - "category": "Gump", - "packets": [ - { - "id": "0x8B", - "name": "Display Sign Gump", - "description": "Displays a sign gump with text to the client.", - "direction": "outgoing", - "isDynamic": true, - "size": "15+", - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x8B", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Serial", - "type": "uint", - "size": 4, - "description": "Sign serial" - }, - { - "name": "Gump ID", - "type": "short", - "size": 2, - "description": "Gump graphic ID" - }, - { - "name": "Unknown Length", - "type": "short", - "size": 2, - "description": "Length of unknown text + 1" - }, - { - "name": "Unknown", - "type": "ascii-t", - "description": "Unknown text" - }, - { - "name": "Caption Length", - "type": "short", - "size": 2, - "description": "Length of caption + 1" - }, - { - "name": "Caption", - "type": "ascii-t", - "description": "Caption text" - } - ], - "source": { - "file": "Projects/UOContent/Gumps/Base/OutgoingGumpPackets.cs", - "line": 58 - } - }, - { - "id": "0xBF", - "subId": "0x04", - "name": "Close Gump", - "description": "Instructs client to close a specific gump.", - "direction": "outgoing", - "isDynamic": false, - "size": 13, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xBF", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "value": "13", - "description": "Packet length" - }, - { - "name": "Sub-Command", - "type": "ushort", - "size": 2, - "value": "0x0004", - "description": "Close Gump subcommand" - }, - { - "name": "Gump Type ID", - "type": "int", - "size": 4, - "description": "Gump type ID to close" - }, - { - "name": "Button ID", - "type": "int", - "size": 4, - "description": "Button ID (0 to close without response)" - } - ], - "source": { - "file": "Projects/UOContent/Gumps/Base/OutgoingGumpPackets.cs", - "line": 83 - } - }, - { - "id": "0xDD", - "name": "Dynamic Gump (Compressed)", - "description": "Sends a custom gump dialog with compressed layout and text strings. Uses raw DEFLATE compression (no zlib headers).", - "direction": "outgoing", - "isDynamic": true, - "size": "23+", - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xDD", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Serial", - "type": "uint", - "size": 4, - "description": "Gump serial (unique per gump instance)" - }, - { - "name": "Type ID", - "type": "uint", - "size": 4, - "description": "Gump type ID (identifies the gump class)" - }, - { - "name": "X", - "type": "int", - "size": 4, - "description": "Screen X position" - }, - { - "name": "Y", - "type": "int", - "size": 4, - "description": "Screen Y position" - }, - { - "name": "Layout Compressed Length", - "type": "int", - "size": 4, - "description": "Compressed layout data length (includes +4)" - }, - { - "name": "Layout Uncompressed Length", - "type": "int", - "size": 4, - "description": "Uncompressed layout data length" - }, - { - "name": "Layout Data", - "type": "bytes", - "size": "var", - "description": "DEFLATE compressed layout commands (ASCII, no zlib header/footer). Length = Layout Compressed Length - 4" - }, - { - "name": "String Count", - "type": "int", - "size": 4, - "description": "Number of text strings" - }, - { - "name": "Strings Compressed Length", - "type": "int", - "size": 4, - "description": "Compressed strings data length (includes +4)" - }, - { - "name": "Strings Uncompressed Length", - "type": "int", - "size": 4, - "description": "Uncompressed strings data length" - }, - { - "name": "Strings Data", - "type": "bytes", - "size": "var", - "description": "DEFLATE compressed strings (no zlib header/footer). Length = Strings Compressed Length - 4" - } - ], - "compression": { - "algorithm": "DEFLATE (raw)", - "notes": "Uses libdeflate. NO zlib header (0x78 0x9C) or footer (adler32 checksum). Layout and strings are compressed separately." - }, - "layoutFormat": { - "description": "Layout is a custom ASCII text format. Each command is brace-wrapped: { command param1 param2 ... }", - "encoding": "ASCII (arguments must be ASCII characters only)", - "commands": [ - { - "name": "Noclose", - "description": "Prevent closing via right-click or ESC" - }, - { - "name": "Nomove", - "description": "Prevent dragging the gump" - }, - { - "name": "Noresize", - "description": "Prevent resizing (Enhanced Client)", - "tags": ["EC"], - "notes": "Enhanced Client only" - }, - { - "name": "Nodispose", - "description": "Prevent server-side disposal" - }, - { - "name": "Page", - "description": "Define page (0=base visible on all pages)", - "args": [ - { - "name": "N", - "type": "int", - "description": "Page number (0 = always visible)" - } - ] - }, - { - "name": "Group", - "description": "Radio button group ID", - "args": [ - { - "name": "Group ID", - "type": "int", - "description": "Group identifier for radio buttons" - } - ] - }, - { - "name": "Resizepic", - "description": "Resizable background panel (9-slice)", - "args": [ - { - "name": "X", - "type": "int", - "description": "X coordinate" - }, - { - "name": "Y", - "type": "int", - "description": "Y coordinate" - }, - { - "name": "Gump ID", - "type": "int", - "description": "Gump graphic ID for the background" - }, - { - "name": "W", - "type": "int", - "description": "Width in pixels" - }, - { - "name": "H", - "type": "int", - "description": "Height in pixels" - } - ] - }, - { - "name": "Gumppic", - "description": "Static gump image", - "args": [ - { - "name": "X", - "type": "int", - "description": "X coordinate" - }, - { - "name": "Y", - "type": "int", - "description": "Y coordinate" - }, - { - "name": "Gump ID", - "type": "int", - "description": "Gump graphic ID" - }, - { - "name": "Hue=N", - "type": "int", - "description": "Optional hue override", - "optional": true - }, - { - "name": "Class=S", - "type": "string", - "description": "Optional CSS class (Enhanced Client)", - "optional": true - } - ] - }, - { - "name": "Gumppictiled", - "description": "Tiled gump image", - "args": [ - { - "name": "X", - "type": "int", - "description": "X coordinate" - }, - { - "name": "Y", - "type": "int", - "description": "Y coordinate" - }, - { - "name": "W", - "type": "int", - "description": "Width in pixels" - }, - { - "name": "H", - "type": "int", - "description": "Height in pixels" - }, - { - "name": "Gump ID", - "type": "int", - "description": "Gump graphic ID to tile" - } - ] - }, - { - "name": "Tilepic", - "description": "Item/tile graphic from art.mul", - "args": [ - { - "name": "X", - "type": "int", - "description": "X coordinate" - }, - { - "name": "Y", - "type": "int", - "description": "Y coordinate" - }, - { - "name": "Item ID", - "type": "int", - "description": "Item graphic ID from art.mul" - } - ] - }, - { - "name": "Tilepichue", - "description": "Item graphic with hue", - "args": [ - { - "name": "X", - "type": "int", - "description": "X coordinate" - }, - { - "name": "Y", - "type": "int", - "description": "Y coordinate" - }, - { - "name": "Item ID", - "type": "int", - "description": "Item graphic ID" - }, - { - "name": "Hue", - "type": "int", - "description": "Color hue" - } - ] - }, - { - "name": "Picinpic", - "description": "Cropped sprite from gump (sprite image rendering)", - "tags": ["EC"], - "notes": "Also known as GumpSpriteImage. EC-compatible.", - "args": [ - { - "name": "X", - "type": "int", - "description": "X coordinate" - }, - { - "name": "Y", - "type": "int", - "description": "Y coordinate" - }, - { - "name": "Gump ID", - "type": "int", - "description": "Gump graphic ID" - }, - { - "name": "W", - "type": "int", - "description": "Display width" - }, - { - "name": "H", - "type": "int", - "description": "Display height" - }, - { - "name": "Sprite X", - "type": "int", - "description": "X offset within sprite" - }, - { - "name": "Sprite Y", - "type": "int", - "description": "Y offset within sprite" - } - ] - }, - { - "name": "Checkertrans", - "description": "Checkerboard transparency region", - "args": [ - { - "name": "X", - "type": "int", - "description": "X coordinate" - }, - { - "name": "Y", - "type": "int", - "description": "Y coordinate" - }, - { - "name": "W", - "type": "int", - "description": "Width in pixels" - }, - { - "name": "H", - "type": "int", - "description": "Height in pixels" - } - ] - }, - { - "name": "Text", - "description": "Static text label", - "args": [ - { - "name": "X", - "type": "int", - "description": "X coordinate" - }, - { - "name": "Y", - "type": "int", - "description": "Y coordinate" - }, - { - "name": "Hue", - "type": "int", - "description": "Text color hue" - }, - { - "name": "String Index", - "type": "int", - "description": "Index into strings array" - } - ] - }, - { - "name": "Croppedtext", - "description": "Cropped/clipped text", - "args": [ - { - "name": "X", - "type": "int", - "description": "X coordinate" - }, - { - "name": "Y", - "type": "int", - "description": "Y coordinate" - }, - { - "name": "W", - "type": "int", - "description": "Crop width" - }, - { - "name": "H", - "type": "int", - "description": "Crop height" - }, - { - "name": "Hue", - "type": "int", - "description": "Text color hue" - }, - { - "name": "String Index", - "type": "int", - "description": "Index into strings array" - } - ] - }, - { - "name": "Htmlgump", - "description": "HTML text area", - "args": [ - { - "name": "X", - "type": "int", - "description": "X coordinate" - }, - { - "name": "Y", - "type": "int", - "description": "Y coordinate" - }, - { - "name": "W", - "type": "int", - "description": "Width in pixels" - }, - { - "name": "H", - "type": "int", - "description": "Height in pixels" - }, - { - "name": "String Index", - "type": "int", - "description": "Index into strings array (HTML content)" - }, - { - "name": "BG", - "type": "bool", - "description": "Display background panel" - }, - { - "name": "Scroll", - "type": "bool", - "description": "Display scrollbar" - } - ] - }, - { - "name": "Xmfhtmlgump", - "description": "Localized HTML from cliloc", - "args": [ - { - "name": "X", - "type": "int", - "description": "X coordinate" - }, - { - "name": "Y", - "type": "int", - "description": "Y coordinate" - }, - { - "name": "W", - "type": "int", - "description": "Width in pixels" - }, - { - "name": "H", - "type": "int", - "description": "Height in pixels" - }, - { - "name": "Cliloc", - "type": "int", - "description": "Cliloc string number" - }, - { - "name": "BG", - "type": "bool", - "description": "Display background panel" - }, - { - "name": "Scroll", - "type": "bool", - "description": "Display scrollbar" - } - ] - }, - { - "name": "Xmfhtmlgumpcolor", - "description": "Localized HTML with color", - "args": [ - { - "name": "X", - "type": "int", - "description": "X coordinate" - }, - { - "name": "Y", - "type": "int", - "description": "Y coordinate" - }, - { - "name": "W", - "type": "int", - "description": "Width in pixels" - }, - { - "name": "H", - "type": "int", - "description": "Height in pixels" - }, - { - "name": "Cliloc", - "type": "int", - "description": "Cliloc string number" - }, - { - "name": "BG", - "type": "bool", - "description": "Display background panel" - }, - { - "name": "Scroll", - "type": "bool", - "description": "Display scrollbar" - }, - { - "name": "Color", - "type": "int", - "description": "Text color (RGB format)" - } - ] - }, - { - "name": "Xmfhtmltok", - "description": "Localized HTML with arguments", - "args": [ - { - "name": "X", - "type": "int", - "description": "X coordinate" - }, - { - "name": "Y", - "type": "int", - "description": "Y coordinate" - }, - { - "name": "W", - "type": "int", - "description": "Width in pixels" - }, - { - "name": "H", - "type": "int", - "description": "Height in pixels" - }, - { - "name": "BG", - "type": "bool", - "description": "Display background panel" - }, - { - "name": "Scroll", - "type": "bool", - "description": "Display scrollbar" - }, - { - "name": "Color", - "type": "int", - "description": "Text color (RGB format)" - }, - { - "name": "Cliloc", - "type": "int", - "description": "Cliloc string number" - }, - { - "name": "@Args@", - "type": "string", - "description": "Tab-separated cliloc arguments" - } - ] - }, - { - "name": "Button", - "description": "Clickable button", - "args": [ - { - "name": "X", - "type": "int", - "description": "X coordinate" - }, - { - "name": "Y", - "type": "int", - "description": "Y coordinate" - }, - { - "name": "Normal ID", - "type": "int", - "description": "Gump ID for normal state" - }, - { - "name": "Pressed ID", - "type": "int", - "description": "Gump ID for pressed state" - }, - { - "name": "Type", - "type": "enum", - "description": "Button type", - "values": [ - { "value": "0", "name": "PageChange", "description": "Navigate to different page" }, - { "value": "1", "name": "ServerReply", "description": "Send response to server" } - ] - }, - { - "name": "Param", - "type": "int", - "description": "Page number (Type=0) or unused (Type=1)" - }, - { - "name": "Button ID", - "type": "int", - "description": "Button ID sent to server on click" - } - ] - }, - { - "name": "Buttontileart", - "description": "Button with item overlay", - "args": [ - { - "name": "X", - "type": "int", - "description": "X coordinate" - }, - { - "name": "Y", - "type": "int", - "description": "Y coordinate" - }, - { - "name": "Normal ID", - "type": "int", - "description": "Gump ID for normal state" - }, - { - "name": "Pressed ID", - "type": "int", - "description": "Gump ID for pressed state" - }, - { - "name": "Type", - "type": "enum", - "description": "Button type", - "values": [ - { "value": "0", "name": "PageChange", "description": "Navigate to different page" }, - { "value": "1", "name": "ServerReply", "description": "Send response to server" } - ] - }, - { - "name": "Param", - "type": "int", - "description": "Page number (Type=0) or unused (Type=1)" - }, - { - "name": "Button ID", - "type": "int", - "description": "Button ID sent to server on click" - }, - { - "name": "Item ID", - "type": "int", - "description": "Item graphic ID to overlay" - }, - { - "name": "Hue", - "type": "int", - "description": "Item hue" - }, - { - "name": "W", - "type": "int", - "description": "Item display width" - }, - { - "name": "H", - "type": "int", - "description": "Item display height" - } - ] - }, - { - "name": "Checkbox", - "description": "Checkbox toggle", - "args": [ - { - "name": "X", - "type": "int", - "description": "X coordinate" - }, - { - "name": "Y", - "type": "int", - "description": "Y coordinate" - }, - { - "name": "Inactive ID", - "type": "int", - "description": "Gump ID when unchecked" - }, - { - "name": "Active ID", - "type": "int", - "description": "Gump ID when checked" - }, - { - "name": "State", - "type": "bool", - "description": "Initial checked state" - }, - { - "name": "Switch ID", - "type": "int", - "description": "Switch ID sent to server" - } - ] - }, - { - "name": "Radio", - "description": "Radio button (use with group)", - "args": [ - { - "name": "X", - "type": "int", - "description": "X coordinate" - }, - { - "name": "Y", - "type": "int", - "description": "Y coordinate" - }, - { - "name": "Inactive ID", - "type": "int", - "description": "Gump ID when unselected" - }, - { - "name": "Active ID", - "type": "int", - "description": "Gump ID when selected" - }, - { - "name": "State", - "type": "bool", - "description": "Initial selected state" - }, - { - "name": "Switch ID", - "type": "int", - "description": "Switch ID sent to server" - } - ] - }, - { - "name": "Textentry", - "description": "Text input field", - "args": [ - { - "name": "X", - "type": "int", - "description": "X coordinate" - }, - { - "name": "Y", - "type": "int", - "description": "Y coordinate" - }, - { - "name": "W", - "type": "int", - "description": "Width in pixels" - }, - { - "name": "H", - "type": "int", - "description": "Height in pixels" - }, - { - "name": "Hue", - "type": "int", - "description": "Text color hue" - }, - { - "name": "Entry ID", - "type": "int", - "description": "Entry ID for server response" - }, - { - "name": "String Index", - "type": "int", - "description": "Index into strings array (default text)" - } - ] - }, - { - "name": "Textentrylimited", - "description": "Text input with max length", - "args": [ - { - "name": "X", - "type": "int", - "description": "X coordinate" - }, - { - "name": "Y", - "type": "int", - "description": "Y coordinate" - }, - { - "name": "W", - "type": "int", - "description": "Width in pixels" - }, - { - "name": "H", - "type": "int", - "description": "Height in pixels" - }, - { - "name": "Hue", - "type": "int", - "description": "Text color hue" - }, - { - "name": "Entry ID", - "type": "int", - "description": "Entry ID for server response" - }, - { - "name": "String Index", - "type": "int", - "description": "Index into strings array (default text)" - }, - { - "name": "Max Len", - "type": "int", - "description": "Maximum character length" - } - ] - }, - { - "name": "Tooltip", - "description": "Tooltip on previous element (GumpTooltip)", - "tags": ["EC"], - "notes": "EC-compatible tooltip display.", - "args": [ - { - "name": "Cliloc", - "type": "int", - "description": "Cliloc string number for tooltip" - }, - { - "name": "@Args@", - "type": "string", - "description": "Optional cliloc arguments", - "optional": true - } - ] - }, - { - "name": "Itemproperty", - "description": "Display item's OPL tooltip (GumpItemProperty)", - "tags": ["EC"], - "notes": "EC-compatible property display for items.", - "args": [ - { - "name": "Serial", - "type": "int", - "description": "Item serial number" - } - ] - }, - { - "name": "Mastergump", - "description": "Master gump ID override", - "args": [ - { - "name": "Gump ID", - "type": "int", - "description": "Master gump identifier" - } - ] - }, - { - "name": "Echandleinput", - "description": "EC-specific input handler", - "tags": ["EC"], - "notes": "Enhanced Client only" - } - ] - }, - "stringsFormat": { - "description": "Array of UTF-16 BE encoded strings for text/textentry elements", - "encoding": "UTF-16 Big Endian", - "entryFormat": [ - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "String length in characters (Big Endian)" - }, - { - "name": "Text", - "type": "utf16be", - "size": "var", - "description": "UTF-16 BE encoded text. Length = Length field * 2 bytes" - } - ] - }, - "related": [ - { - "id": "0xB1", - "relationship": "response", - "note": "Gump Response - client reply to button/checkbox/text" - } - ], - "clientVersion": { - "classic": { - "min": "3.0.0" - }, - "enhanced": {}, - "notes": "Compressed gumps supported since Classic Client 3.0.0" - }, - "notes": "Layout uses ASCII encoding only. The compressed lengths include a +4 offset. Decompression produces: layout as ASCII text, strings as length-prefixed UTF-16 BE entries.", - "source": { - "file": "Projects/UOContent/Gumps/Base/DynamicGump.cs", - "line": 105 - } - } - ] -} \ No newline at end of file diff --git a/website/packets/outgoing/house.json b/website/packets/outgoing/house.json deleted file mode 100644 index 3fff90eed..000000000 --- a/website/packets/outgoing/house.json +++ /dev/null @@ -1,309 +0,0 @@ -{ - "category": "House", - "packets": [ - { - "id": "0xD8", - "name": "House Design State Detailed", - "description": "Sends detailed house customization design data. Contains compressed plane data organized by floor level and component type. Tiles are encoded using grid-based or coordinate-based formats depending on their Z-height.", - "direction": "outgoing", - "isDynamic": true, - "size": "18+", - "tags": ["House"], - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xD8", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Compression Type", - "type": "byte", - "size": 1, - "value": "0x03", - "description": "Compression type (always 3 = compressed)" - }, - { - "name": "Unknown", - "type": "byte", - "size": 1, - "value": "0x00", - "description": "Unknown (always 0)" - }, - { - "name": "House Serial", - "type": "uint", - "size": 4, - "description": "Serial of the house foundation" - }, - { - "name": "Revision", - "type": "int", - "size": 4, - "description": "Design revision number" - }, - { - "name": "Tile Count", - "type": "ushort", - "size": 2, - "description": "Total number of tiles in the design" - }, - { - "name": "Buffer Length", - "type": "ushort", - "size": 2, - "description": "Length of remaining data (plane count + all plane sections)" - }, - { - "name": "Plane Count", - "type": "byte", - "size": 1, - "description": "Number of plane/buffer sections that follow" - }, - { - "name": "Plane Sections", - "type": "array", - "description": "Each section has a 4-byte header followed by compressed tile data", - "loop": { - "countField": "Plane Count", - "fields": [ - { - "name": "Section Header", - "type": "uint", - "size": 4, - "description": "Packed header: bits 28-31=mode, bits 24-27=planeZ, bits 16-23+(bits 4-7<<8)=decompLen, bits 8-15+(bits 0-3<<8)=compLen" - }, - { - "name": "Compressed Data", - "type": "bytes", - "size": "var", - "description": "DEFLATE compressed tile data (length from header)" - } - ] - } - } - ], - "planeFormat": { - "description": "Tiles are organized into planes based on Z-height. Planes 0-8 use grid encoding; planes 9+ use explicit coordinates for overflow items.", - "headerDecoding": { - "mode": "(header >> 28) & 0x0F", - "planeZ": "(header >> 24) & 0x0F", - "decompLen": "((header & 0xFF0000) >> 16) | ((header & 0xF0) << 4)", - "compLen": "((header & 0xFF00) >> 8) | ((header & 0x0F) << 8)" - }, - "planeZMapping": [ - { "plane": 0, "z": 0, "description": "Ground floor" }, - { "plane": 1, "z": 7, "description": "1st storey (floor items)" }, - { "plane": 2, "z": 27, "description": "2nd storey (floor items)" }, - { "plane": 3, "z": 47, "description": "3rd storey (floor items)" }, - { "plane": 4, "z": 67, "description": "4th storey (floor items)" }, - { "plane": "5-8", "z": "7/27/47/67", "description": "Non-floor items per storey" }, - { "plane": "9+", "z": "varies", "description": "Overflow/stair buffers (explicit coords)" } - ], - "encodingModes": [ - { - "mode": 0, - "name": "Full Coordinates", - "bytesPerTile": 5, - "format": "ushort itemId, sbyte x, sbyte y, sbyte z", - "description": "Used for overflow buffers with non-standard Z heights" - }, - { - "mode": 1, - "name": "XY Coordinates", - "bytesPerTile": 4, - "format": "ushort itemId, sbyte x, sbyte y", - "description": "Z calculated: ((planeZ - 1) % 4) * 20 + 7" - }, - { - "mode": 2, - "name": "Grid-Based", - "bytesPerTile": 2, - "format": "ushort itemId (0x0000 = empty)", - "description": "X,Y from grid index: x = i / gridHeight, y = i % gridHeight" - } - ], - "gridSizes": [ - { "plane": 0, "width": "width", "height": "height", "notes": "Full foundation footprint" }, - { "plane": "1-4", "width": "width - 1", "height": "height - 2", "notes": "Interior (floor items), offset by (1,1)" }, - { "plane": "5-8", "width": "width", "height": "height - 1", "notes": "Interior (non-floor items)" } - ] - }, - "compression": { - "algorithm": "DEFLATE", - "notes": "Raw DEFLATE compression (no zlib header/footer). Each plane buffer compressed independently. Max 750 items per overflow buffer (splits into multiple buffers if exceeded)." - }, - "related": [ - { - "id": "0xBF/0x1D", - "relationship": "related", - "note": "Design State General provides revision info only" - }, - { - "id": "0xBF/0x20", - "relationship": "related", - "note": "Begin/End House Customization commands" - } - ], - "clientVersion": { - "classic": { - "min": "4.0.0a" - }, - "enhanced": {}, - "notes": "AOS House Customization feature" - }, - "source": { - "file": "Projects/UOContent/Multis/Houses/HousePackets.cs", - "line": 93 - } - }, - { - "id": "0xBF", - "subId": "0x1D", - "name": "Design State General", - "description": "Sends house design revision information. Client requests full details if revision differs.", - "direction": "outgoing", - "isDynamic": false, - "size": 13, - "tags": ["Extended Commands (0xBF)", "House"], - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xBF", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "value": "0x000D", - "description": "Packet length (13)" - }, - { - "name": "Sub-Command", - "type": "ushort", - "size": 2, - "value": "0x001D", - "description": "Design State General subcommand" - }, - { - "name": "House Serial", - "type": "uint", - "size": 4, - "description": "Serial of the house" - }, - { - "name": "Revision", - "type": "int", - "size": 4, - "description": "Design revision number" - } - ], - "related": [ - { - "id": "0xD8", - "relationship": "related", - "note": "Full design details requested if revision differs" - } - ], - "source": { - "file": "Projects/UOContent/Multis/Houses/HousePackets.cs", - "line": 72 - } - }, - { - "id": "0xBF", - "subId": "0x20", - "name": "Begin House Customization", - "description": "Notifies client to enter house customization mode.", - "direction": "outgoing", - "isDynamic": false, - "size": 17, - "tags": ["Extended Commands (0xBF)", "House"], - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xBF", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "value": "0x0011", - "description": "Packet length (17)" - }, - { - "name": "Sub-Command", - "type": "ushort", - "size": 2, - "value": "0x0020", - "description": "House Customization subcommand" - }, - { - "name": "House Serial", - "type": "uint", - "size": 4, - "description": "Serial of the house" - }, - { - "name": "Command", - "type": "byte", - "size": 1, - "value": "0x04", - "description": "Begin customization command" - }, - { - "name": "Unknown 1", - "type": "ushort", - "size": 2, - "value": "0x0000", - "description": "Unknown (always 0)" - }, - { - "name": "Unknown 2", - "type": "ushort", - "size": 2, - "value": "0xFFFF", - "description": "Unknown (always 0xFFFF)" - }, - { - "name": "Unknown 3", - "type": "ushort", - "size": 2, - "value": "0xFFFF", - "description": "Unknown (always 0xFFFF)" - }, - { - "name": "Unknown 4", - "type": "byte", - "size": 1, - "value": "0xFF", - "description": "Unknown (always 0xFF)" - } - ], - "related": [ - { - "id": "0xBF/0x20", - "relationship": "variant", - "note": "End House Customization uses command 0x05" - } - ], - "source": { - "file": "Projects/UOContent/Multis/Houses/HousePackets.cs", - "line": 30 - } - } - ] -} \ No newline at end of file diff --git a/website/packets/outgoing/items.json b/website/packets/outgoing/items.json deleted file mode 100644 index 2c9f6e0f7..000000000 --- a/website/packets/outgoing/items.json +++ /dev/null @@ -1,104 +0,0 @@ -{ - "category": "Items", - "packets": [ - { - "id": "0x1A", - "name": "World Item", - "description": "Displays an item in the world (pre-Stygian Abyss clients).", - "direction": "outgoing", - "isDynamic": true, - "size": "14+", - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x1A", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length (14-20)" - }, - { - "name": "Serial", - "type": "uint", - "size": 4, - "description": "Item serial (bit 31 set if amount present)" - }, - { - "name": "Item ID", - "type": "ushort", - "size": 2, - "description": "Item graphic ID (bit 14 set for multi)" - }, - { - "name": "Amount", - "type": "ushort", - "size": 2, - "description": "Stack amount (if serial bit 31 set)", - "condition": "serial \u0026 0x80000000" - }, - { - "name": "X", - "type": "ushort", - "size": 2, - "description": "X coordinate (bit 15 set if direction present)" - }, - { - "name": "Y", - "type": "ushort", - "size": 2, - "description": "Y coordinate (bit 15=hue, bit 14=flags)" - }, - { - "name": "Direction", - "type": "byte", - "size": 1, - "description": "Direction (if x bit 15 set)", - "condition": "x \u0026 0x8000" - }, - { - "name": "Z", - "type": "sbyte", - "size": 1, - "description": "Z coordinate" - }, - { - "name": "Hue", - "type": "ushort", - "size": 2, - "description": "Item hue (if y bit 15 set)", - "condition": "y \u0026 0x8000" - }, - { - "name": "Flags", - "type": "byte", - "size": 1, - "description": "Packet flags (if y bit 14 set)", - "condition": "y \u0026 0x4000" - } - ], - "related": [ - { - "id": "0xF3", - "relationship": "variant", - "note": "World Entity packet for SA+ clients" - } - ], - "notes": "For Stygian Abyss+ clients, use 0xF3 World Entity instead.", - "clientVersion": { - "classic": { - "max": "6.0.14.2" - }, - "notes": "Replaced by 0xF3 for SA+ clients" - }, - "source": { - "file": "Projects/Server/Network/Packets/OutgoingItemPackets.cs", - "line": 24 - } - } - ] -} \ No newline at end of file diff --git a/website/packets/outgoing/light.json b/website/packets/outgoing/light.json deleted file mode 100644 index 0958046b4..000000000 --- a/website/packets/outgoing/light.json +++ /dev/null @@ -1,65 +0,0 @@ -{ - "category": "Light", - "packets": [ - { - "id": "0x4E", - "name": "Personal Light Level", - "description": "Sets the light level for a specific entity.", - "direction": "outgoing", - "isDynamic": false, - "size": 6, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x4E", - "description": "Packet identifier" - }, - { - "name": "Serial", - "type": "uint", - "size": 4, - "description": "Serial of entity" - }, - { - "name": "Level", - "type": "byte", - "size": 1, - "description": "Light level (0=brightest, 25-30=typical night)" - } - ], - "source": { - "file": "Projects/Server/Network/Packets/OutgoingLightPackets.cs", - "line": 23 - } - }, - { - "id": "0x4F", - "name": "Global Light Level", - "description": "Sets the global ambient light level for the client.", - "direction": "outgoing", - "isDynamic": false, - "size": 2, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x4F", - "description": "Packet identifier" - }, - { - "name": "Level", - "type": "byte", - "size": 1, - "description": "Light level (0=brightest, 25-30=typical night)" - } - ], - "source": { - "file": "Projects/Server/Network/Packets/OutgoingLightPackets.cs", - "line": 39 - } - } - ] -} diff --git a/website/packets/outgoing/mahjong.json b/website/packets/outgoing/mahjong.json deleted file mode 100644 index 5b9f3b9a9..000000000 --- a/website/packets/outgoing/mahjong.json +++ /dev/null @@ -1,501 +0,0 @@ -{ - "category": "Mahjong", - "packets": [ - { - "id": "0xDA", - "subId": "0x19", - "name": "Mahjong Join Game", - "description": "Notifies client to open the Mahjong game interface.", - "direction": "outgoing", - "isDynamic": false, - "size": 9, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xDA", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "value": "0x0009", - "description": "Packet length (9)" - }, - { - "name": "Game Serial", - "type": "uint", - "size": 4, - "description": "Serial of the Mahjong game" - }, - { - "name": "Command", - "type": "ushort", - "size": 2, - "value": "0x0019", - "description": "Join Game command" - } - ], - "source": { - "file": "Projects/UOContent/Items/Games/Mahjong/MahjongPackets.cs", - "line": 288 - } - }, - { - "id": "0xDA", - "subId": "0x03", - "name": "Mahjong Tile Info", - "description": "Sends information about a single Mahjong tile.", - "direction": "outgoing", - "isDynamic": false, - "size": 18, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xDA", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "value": "0x0012", - "description": "Packet length (18)" - }, - { - "name": "Game Serial", - "type": "uint", - "size": 4, - "description": "Serial of the Mahjong game" - }, - { - "name": "Command", - "type": "ushort", - "size": 2, - "value": "0x0003", - "description": "Tile Info command" - }, - { - "name": "Tile Number", - "type": "byte", - "size": 1, - "description": "Tile index number" - }, - { - "name": "Tile Value", - "type": "byte", - "size": 1, - "description": "Tile face value (0 if hidden)" - }, - { - "name": "Y Position", - "type": "short", - "size": 2, - "description": "Tile Y coordinate" - }, - { - "name": "X Position", - "type": "short", - "size": 2, - "description": "Tile X coordinate" - }, - { - "name": "Stack Level", - "type": "byte", - "size": 1, - "description": "Stack level for overlapping tiles" - }, - { - "name": "Direction", - "type": "byte", - "size": 1, - "description": "Tile direction (0=Up, 1=Left, 2=Down, 3=Right)" - }, - { - "name": "Flipped", - "type": "byte", - "size": 1, - "description": "0x10 if flipped/face-up, 0x00 if face-down" - } - ], - "source": { - "file": "Projects/UOContent/Items/Games/Mahjong/MahjongPackets.cs", - "line": 362 - } - }, - { - "id": "0xDA", - "subId": "0x04", - "name": "Mahjong Tiles Info", - "description": "Sends information about all tiles in the Mahjong game.", - "direction": "outgoing", - "isDynamic": true, - "size": "11+", - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xDA", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Game Serial", - "type": "uint", - "size": 4, - "description": "Serial of the Mahjong game" - }, - { - "name": "Command", - "type": "ushort", - "size": 2, - "value": "0x0004", - "description": "Tiles Info command" - }, - { - "name": "Tile Count", - "type": "short", - "size": 2, - "description": "Number of tiles" - }, - { - "name": "Tiles", - "type": "loop", - "description": "Tile data for each tile", - "loop": { - "countField": "Tile Count", - "fields": [ - { - "name": "Tile Number", - "type": "byte", - "size": 1, - "description": "Tile index number" - }, - { - "name": "Tile Value", - "type": "byte", - "size": 1, - "description": "Tile face value (0 if hidden)" - }, - { - "name": "Y Position", - "type": "short", - "size": 2, - "description": "Tile Y coordinate" - }, - { - "name": "X Position", - "type": "short", - "size": 2, - "description": "Tile X coordinate" - }, - { - "name": "Stack Level", - "type": "byte", - "size": 1, - "description": "Stack level" - }, - { - "name": "Direction", - "type": "byte", - "size": 1, - "description": "Tile direction" - }, - { - "name": "Flipped", - "type": "byte", - "size": 1, - "description": "0x10 if flipped, 0x00 otherwise" - } - ] - } - } - ], - "source": { - "file": "Projects/UOContent/Items/Games/Mahjong/MahjongPackets.cs", - "line": 408 - } - }, - { - "id": "0xDA", - "subId": "0x02", - "name": "Mahjong Players Info", - "description": "Sends information about all players in the Mahjong game.", - "direction": "outgoing", - "isDynamic": true, - "size": "11+", - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xDA", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Game Serial", - "type": "uint", - "size": 4, - "description": "Serial of the Mahjong game" - }, - { - "name": "Command", - "type": "ushort", - "size": 2, - "value": "0x0002", - "description": "Players Info command" - }, - { - "name": "Seat Count", - "type": "ushort", - "size": 2, - "description": "Number of seats with data" - }, - { - "name": "Players", - "type": "loop", - "description": "Player data for each seat", - "loop": { - "countField": "Seat Count", - "fields": [ - { - "name": "Player Serial", - "type": "uint", - "size": 4, - "description": "Serial of player (0 if empty)" - }, - { - "name": "Dealer Flag", - "type": "byte", - "size": 1, - "description": "1=Dealer, 2=Not dealer" - }, - { - "name": "Position", - "type": "byte", - "size": 1, - "description": "Seat position index" - }, - { - "name": "Score", - "type": "int", - "size": 4, - "description": "Player\u0027s current score" - }, - { - "name": "Reserved", - "type": "short", - "size": 2, - "value": "0", - "description": "Reserved (always 0)" - }, - { - "name": "Reserved 2", - "type": "byte", - "size": 1, - "value": "0", - "description": "Reserved (always 0)" - }, - { - "name": "Public Hand", - "type": "bool", - "size": 1, - "description": "True if hand is visible to others" - }, - { - "name": "Name", - "type": "ascii", - "size": 30, - "description": "Player name" - }, - { - "name": "Empty Seat", - "type": "bool", - "size": 1, - "description": "True if seat is empty or not in game" - } - ] - } - } - ], - "source": { - "file": "Projects/UOContent/Items/Games/Mahjong/MahjongPackets.cs", - "line": 304 - } - }, - { - "id": "0xDA", - "subId": "0x05", - "name": "Mahjong General Info", - "description": "Sends general game state including dice values, dealer indicator, and wall break position.", - "direction": "outgoing", - "isDynamic": false, - "size": 25, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xDA", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "value": "0x0019", - "description": "Packet length (25)" - }, - { - "name": "Game Serial", - "type": "uint", - "size": 4, - "description": "Serial of the Mahjong game" - }, - { - "name": "Command", - "type": "ushort", - "size": 2, - "value": "0x0005", - "description": "General Info command" - }, - { - "name": "Reserved", - "type": "short", - "size": 2, - "value": "0", - "description": "Reserved" - }, - { - "name": "Reserved 2", - "type": "byte", - "size": 1, - "value": "0", - "description": "Reserved" - }, - { - "name": "Options", - "type": "byte", - "size": 1, - "description": "Bit 0: Show Scores, Bit 1: Spectator Vision" - }, - { - "name": "Dice 1", - "type": "byte", - "size": 1, - "description": "First dice value" - }, - { - "name": "Dice 2", - "type": "byte", - "size": 1, - "description": "Second dice value" - }, - { - "name": "Dealer Wind", - "type": "enum", - "size": 1, - "description": "Dealer indicator wind direction", - "values": [ - { "value": "0", "name": "North", "description": "North wind" }, - { "value": "1", "name": "East", "description": "East wind" }, - { "value": "2", "name": "South", "description": "South wind" }, - { "value": "3", "name": "West", "description": "West wind" } - ] - }, - { - "name": "Dealer Y", - "type": "short", - "size": 2, - "description": "Dealer indicator Y position" - }, - { - "name": "Dealer X", - "type": "short", - "size": 2, - "description": "Dealer indicator X position" - }, - { - "name": "Dealer Direction", - "type": "byte", - "size": 1, - "description": "Dealer indicator direction" - }, - { - "name": "Wall Break Y", - "type": "short", - "size": 2, - "description": "Wall break indicator Y position" - }, - { - "name": "Wall Break X", - "type": "short", - "size": 2, - "description": "Wall break indicator X position" - } - ], - "source": { - "file": "Projects/UOContent/Items/Games/Mahjong/MahjongPackets.cs", - "line": 461 - } - }, - { - "id": "0xDA", - "subId": "0x1A", - "name": "Mahjong Relieve", - "description": "Notifies client to close the Mahjong game interface.", - "direction": "outgoing", - "isDynamic": false, - "size": 9, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xDA", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "value": "0x0009", - "description": "Packet length (9)" - }, - { - "name": "Game Serial", - "type": "uint", - "size": 4, - "description": "Serial of the Mahjong game" - }, - { - "name": "Command", - "type": "ushort", - "size": 2, - "value": "0x001A", - "description": "Relieve command" - } - ], - "source": { - "file": "Projects/UOContent/Items/Games/Mahjong/MahjongPackets.cs", - "line": 504 - } - } - ] -} \ No newline at end of file diff --git a/website/packets/outgoing/map.json b/website/packets/outgoing/map.json deleted file mode 100644 index e9e77376b..000000000 --- a/website/packets/outgoing/map.json +++ /dev/null @@ -1,427 +0,0 @@ -{ - "category": "Map", - "packets": [ - { - "id": "0xC6", - "name": "Invalid Map", - "description": "Notifies client that the current map is invalid.", - "direction": "outgoing", - "isDynamic": false, - "size": 1, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xC6", - "description": "Packet identifier" - } - ], - "source": { - "file": "Projects/Server/Network/Packets/OutgoingMapPackets.cs", - "line": 53 - } - }, - { - "id": "0xBF", - "subId": "0x08", - "name": "Map Change", - "description": "Notifies client of a facet/map change.", - "direction": "outgoing", - "isDynamic": false, - "size": 6, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xBF", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "value": "0x0006", - "description": "Packet length (6)" - }, - { - "name": "Sub-Command", - "type": "ushort", - "size": 2, - "value": "0x0008", - "description": "Map Change subcommand" - }, - { - "name": "Map ID", - "type": "enum", - "enumType": "sequential", - "size": 1, - "description": "Map/Facet ID", - "values": [ - { - "value": 0, - "name": "Felucca", - "description": "Felucca facet" - }, - { - "value": 1, - "name": "Trammel", - "description": "Trammel facet" - }, - { - "value": 2, - "name": "Ilshenar", - "description": "Ilshenar facet" - }, - { - "value": 3, - "name": "Malas", - "description": "Malas facet" - }, - { - "value": 4, - "name": "Tokuno", - "description": "Tokuno Islands facet" - }, - { - "value": 5, - "name": "Ter Mur", - "description": "Ter Mur facet" - } - ] - } - ], - "source": { - "file": "Projects/Server/Network/Packets/OutgoingMapPackets.cs", - "line": 55 - } - }, - { - "id": "0xBF", - "subId": "0x18", - "name": "Map Patches", - "description": "Sends map patch information for static and land blocks.", - "direction": "outgoing", - "isDynamic": false, - "size": 41, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xBF", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "value": "0x0029", - "description": "Packet length (41)" - }, - { - "name": "Sub-Command", - "type": "ushort", - "size": 2, - "value": "0x0018", - "description": "Map Patches subcommand" - }, - { - "name": "Map Count", - "type": "int", - "size": 4, - "value": "4", - "description": "Number of maps (always 4)" - }, - { - "name": "Patches", - "type": "loop", - "description": "Patch counts for each map (Felucca, Trammel, Ilshenar, Malas)", - "loop": { - "countField": "4", - "fields": [ - { - "name": "Static Blocks", - "type": "int", - "size": 4, - "description": "Number of patched static blocks" - }, - { - "name": "Land Blocks", - "type": "int", - "size": 4, - "description": "Number of patched land blocks" - } - ] - } - } - ], - "source": { - "file": "Projects/Server/Network/Packets/OutgoingMapPackets.cs", - "line": 25 - } - }, - { - "id": "0x56", - "name": "Map Command", - "description": "Server sends map pin commands to update the client\u0027s map display.", - "direction": "outgoing", - "isDynamic": false, - "size": 11, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x56", - "description": "Packet identifier" - }, - { - "name": "Map Serial", - "type": "uint", - "size": 4, - "description": "Serial of the map item" - }, - { - "name": "Command", - "type": "enum", - "size": 1, - "description": "Map command type", - "values": [ - { "value": "1", "name": "AddPin", "description": "Add a pin to the map" }, - { "value": "5", "name": "DisplayMap", "description": "Display the map to client" }, - { "value": "7", "name": "SetEditable", "description": "Set map editable state" } - ] - }, - { - "name": "Editable", - "type": "bool", - "size": 1, - "description": "True if map is editable (for command 7)" - }, - { - "name": "X", - "type": "short", - "size": 2, - "description": "X coordinate of pin" - }, - { - "name": "Y", - "type": "short", - "size": 2, - "description": "Y coordinate of pin" - } - ], - "related": [ - { - "id": "0x56", - "direction": "incoming", - "relationship": "related", - "note": "Client map pin commands" - }, - { - "id": "0x90", - "relationship": "related", - "note": "Map Details packet (old)" - }, - { - "id": "0xF5", - "relationship": "related", - "note": "Map Details packet (new)" - } - ], - "source": { - "file": "Projects/UOContent/Items/Maps/MapItemPackets.cs", - "line": 106 - } - }, - { - "id": "0x90", - "name": "Map Details", - "description": "Sends map details including bounds, dimensions, and graphic ID. Pre-NewCharacterList clients.", - "direction": "outgoing", - "isDynamic": false, - "size": 19, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x90", - "description": "Packet identifier" - }, - { - "name": "Map Serial", - "type": "uint", - "size": 4, - "description": "Serial of the map item" - }, - { - "name": "Graphic ID", - "type": "short", - "size": 2, - "value": "0x139D", - "description": "Map gump graphic ID" - }, - { - "name": "Start X", - "type": "short", - "size": 2, - "description": "Map bounds start X coordinate" - }, - { - "name": "Start Y", - "type": "short", - "size": 2, - "description": "Map bounds start Y coordinate" - }, - { - "name": "End X", - "type": "short", - "size": 2, - "description": "Map bounds end X coordinate" - }, - { - "name": "End Y", - "type": "short", - "size": 2, - "description": "Map bounds end Y coordinate" - }, - { - "name": "Width", - "type": "short", - "size": 2, - "description": "Map width in pixels" - }, - { - "name": "Height", - "type": "short", - "size": 2, - "description": "Map height in pixels" - } - ], - "related": [ - { - "id": "0x56", - "direction": "both", - "relationship": "related", - "note": "Map pin commands (client and server)" - }, - { - "id": "0xF5", - "relationship": "variant", - "note": "New Map Details for NewCharacterList clients" - } - ], - "source": { - "file": "Projects/UOContent/Items/Maps/MapItemPackets.cs", - "line": 78 - } - }, - { - "id": "0xF5", - "name": "Map Details (New)", - "description": "Sends map details for NewCharacterList clients. Includes facet ID.", - "direction": "outgoing", - "isDynamic": false, - "size": 21, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xF5", - "description": "Packet identifier" - }, - { - "name": "Map Serial", - "type": "uint", - "size": 4, - "description": "Serial of the map item" - }, - { - "name": "Graphic ID", - "type": "short", - "size": 2, - "value": "0x139D", - "description": "Map gump graphic ID" - }, - { - "name": "Start X", - "type": "short", - "size": 2, - "description": "Map bounds start X coordinate" - }, - { - "name": "Start Y", - "type": "short", - "size": 2, - "description": "Map bounds start Y coordinate" - }, - { - "name": "End X", - "type": "short", - "size": 2, - "description": "Map bounds end X coordinate" - }, - { - "name": "End Y", - "type": "short", - "size": 2, - "description": "Map bounds end Y coordinate" - }, - { - "name": "Width", - "type": "short", - "size": 2, - "description": "Map width in pixels" - }, - { - "name": "Height", - "type": "short", - "size": 2, - "description": "Map height in pixels" - }, - { - "name": "Facet ID", - "type": "enum", - "size": 2, - "description": "Map facet/world identifier", - "values": [ - { "value": "0", "name": "Felucca", "description": "Felucca facet" }, - { "value": "1", "name": "Trammel", "description": "Trammel facet" }, - { "value": "2", "name": "Ilshenar", "description": "Ilshenar facet" }, - { "value": "3", "name": "Malas", "description": "Malas facet" }, - { "value": "4", "name": "Tokuno", "description": "Tokuno Islands" }, - { "value": "5", "name": "TerMur", "description": "Ter Mur facet" } - ] - } - ], - "related": [ - { - "id": "0x56", - "direction": "both", - "relationship": "related", - "note": "Map pin commands (client and server)" - }, - { - "id": "0x90", - "relationship": "variant", - "note": "Old Map Details for pre-NewCharacterList clients" - } - ], - "clientVersion": { - "classic": { - "min": "7.0.13.0" - }, - "enhanced": {}, - "notes": "NewCharacterList feature" - }, - "source": { - "file": "Projects/UOContent/Items/Maps/MapItemPackets.cs", - "line": 78 - } - } - ] -} \ No newline at end of file diff --git a/website/packets/outgoing/menu.json b/website/packets/outgoing/menu.json deleted file mode 100644 index e69969a11..000000000 --- a/website/packets/outgoing/menu.json +++ /dev/null @@ -1,185 +0,0 @@ -{ - "category": "Menu", - "packets": [ - { - "id": "0x7C", - "name": "Display Menu", - "description": "Displays an item list menu or question menu to the client.", - "direction": "outgoing", - "isDynamic": true, - "size": "Varies", - "variants": [ - { - "name": "Item List Menu", - "condition": "Menu is ItemListMenu", - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x7C", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Menu Serial", - "type": "uint", - "size": 4, - "description": "Menu serial" - }, - { - "name": "Menu ID", - "type": "ushort", - "size": 2, - "value": "0x0000", - "description": "Menu ID (always 0)" - }, - { - "name": "Question Length", - "type": "byte", - "size": 1, - "description": "Length of question text" - }, - { - "name": "Question", - "type": "ascii", - "description": "Question text" - }, - { - "name": "Entry Count", - "type": "byte", - "size": 1, - "description": "Number of menu entries" - }, - { - "name": "Entries", - "type": "loop", - "description": "Menu entries", - "loop": { - "countField": "entryCount", - "fields": [ - { - "name": "Item ID", - "type": "ushort", - "size": 2, - "description": "Item graphic ID" - }, - { - "name": "Hue", - "type": "short", - "size": 2, - "description": "Item hue" - }, - { - "name": "Name Length", - "type": "byte", - "size": 1, - "description": "Length of entry name" - }, - { - "name": "Name", - "type": "ascii", - "description": "Entry name" - } - ] - } - } - ] - }, - { - "name": "Question Menu", - "condition": "Menu is QuestionMenu", - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x7C", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Menu Serial", - "type": "uint", - "size": 4, - "description": "Menu serial" - }, - { - "name": "Menu ID", - "type": "ushort", - "size": 2, - "value": "0x0000", - "description": "Menu ID (always 0)" - }, - { - "name": "Question Length", - "type": "byte", - "size": 1, - "description": "Length of question text" - }, - { - "name": "Question", - "type": "ascii", - "description": "Question text" - }, - { - "name": "Answer Count", - "type": "byte", - "size": 1, - "description": "Number of answers" - }, - { - "name": "Answers", - "type": "loop", - "description": "Answer options", - "loop": { - "countField": "answerCount", - "fields": [ - { - "name": "Padding", - "type": "int", - "size": 4, - "value": "0", - "description": "Unused padding" - }, - { - "name": "Answer Length", - "type": "byte", - "size": 1, - "description": "Length of answer text" - }, - { - "name": "Answer", - "type": "ascii", - "description": "Answer text" - } - ] - } - } - ] - } - ], - "related": [ - { - "id": "0x7D", - "relationship": "response", - "note": "Menu Response from client" - } - ], - "source": { - "file": "Projects/Server/Network/Packets/OutgoingMenuPackets.cs", - "line": 36 - } - } - ] -} \ No newline at end of file diff --git a/website/packets/outgoing/message.json b/website/packets/outgoing/message.json deleted file mode 100644 index df69402d9..000000000 --- a/website/packets/outgoing/message.json +++ /dev/null @@ -1,534 +0,0 @@ -{ - "category": "Message", - "packets": [ - { - "id": "0x15", - "name": "Follow Message", - "description": "Instructs client to follow one entity with another.", - "direction": "outgoing", - "isDynamic": false, - "size": 9, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x15", - "description": "Packet identifier" - }, - { - "name": "Follower Serial", - "type": "uint", - "size": 4, - "description": "Serial of the follower" - }, - { - "name": "Target Serial", - "type": "uint", - "size": 4, - "description": "Serial of the target to follow" - } - ], - "source": { - "file": "Projects/Server/Network/Packets/OutgoingMessagePackets.cs", - "line": 231 - } - }, - { - "id": "0x1C", - "name": "ASCII Message", - "description": "Server sends ASCII-encoded message to client.", - "direction": "outgoing", - "isDynamic": true, - "size": "var", - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x1C", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Serial", - "type": "uint", - "size": 4, - "description": "Serial of speaker (0xFFFFFFFF for system)" - }, - { - "name": "Graphic", - "type": "short", - "size": 2, - "description": "Body graphic of speaker" - }, - { - "name": "Type", - "type": "enum", - "enumType": "sequential", - "size": 1, - "description": "Message type", - "values": [ - { - "value": 0, - "name": "Regular", - "description": "Normal speech" - }, - { - "value": 1, - "name": "System", - "description": "System message" - }, - { - "value": 2, - "name": "Emote", - "description": "Emote (*action*)" - }, - { - "value": 6, - "name": "Label", - "description": "Object label" - }, - { - "value": 7, - "name": "Focus", - "description": "Focused message" - }, - { - "value": 8, - "name": "Whisper", - "description": "Whisper" - }, - { - "value": 9, - "name": "Yell", - "description": "Yell" - }, - { - "value": 10, - "name": "Spell", - "description": "Spell words" - }, - { - "value": 13, - "name": "Guild", - "description": "Guild chat" - }, - { - "value": 14, - "name": "Alliance", - "description": "Alliance chat" - }, - { - "value": 15, - "name": "Command", - "description": "Command" - } - ] - }, - { - "name": "Hue", - "type": "short", - "size": 2, - "description": "Text color hue" - }, - { - "name": "Font", - "type": "short", - "size": 2, - "description": "Font ID" - }, - { - "name": "Name", - "type": "ascii", - "size": 30, - "description": "Speaker name" - }, - { - "name": "Text", - "type": "ascii-t", - "description": "Message text" - } - ], - "related": [ - { - "id": "0x03", - "relationship": "request", - "note": "ASCII Speech from client" - } - ], - "source": { - "file": "Projects/Server/Network/Packets/OutgoingMessagePackets.cs", - "line": 180 - } - }, - { - "id": "0xAE", - "name": "Unicode Message", - "description": "Server sends Unicode-encoded message to client.", - "direction": "outgoing", - "isDynamic": true, - "size": "var", - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xAE", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Serial", - "type": "uint", - "size": 4, - "description": "Serial of speaker (0xFFFFFFFF for system)" - }, - { - "name": "Graphic", - "type": "short", - "size": 2, - "description": "Body graphic of speaker" - }, - { - "name": "Type", - "type": "byte", - "size": 1, - "description": "Message type (see 0x1C)" - }, - { - "name": "Hue", - "type": "short", - "size": 2, - "description": "Text color hue" - }, - { - "name": "Font", - "type": "short", - "size": 2, - "description": "Font ID" - }, - { - "name": "Language", - "type": "ascii", - "size": 4, - "description": "Language code (e.g., \u0027ENU\u0027)" - }, - { - "name": "Name", - "type": "ascii", - "size": 30, - "description": "Speaker name" - }, - { - "name": "Text", - "type": "utf16be-t", - "description": "Message text (Big Endian Unicode)" - } - ], - "related": [ - { - "id": "0xAD", - "relationship": "request", - "note": "Unicode Speech from client" - } - ], - "source": { - "file": "Projects/Server/Network/Packets/OutgoingMessagePackets.cs", - "line": 180 - } - }, - { - "id": "0xB7", - "name": "Help Response", - "description": "Server sends help text response to client.", - "direction": "outgoing", - "isDynamic": true, - "size": "var", - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xB7", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Serial", - "type": "uint", - "size": 4, - "description": "Serial of entity" - }, - { - "name": "Text", - "type": "utf16be-t", - "description": "Help text" - } - ], - "related": [ - { - "id": "0xB6", - "relationship": "request", - "note": "Object Help Request from client" - } - ], - "source": { - "file": "Projects/Server/Network/Packets/OutgoingMessagePackets.cs", - "line": 263 - } - }, - { - "id": "0xC1", - "name": "Localized Message", - "description": "Server sends a localized (cliloc) message to client.", - "direction": "outgoing", - "isDynamic": true, - "size": "var", - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xC1", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Serial", - "type": "uint", - "size": 4, - "description": "Serial of speaker" - }, - { - "name": "Graphic", - "type": "short", - "size": 2, - "description": "Body graphic of speaker" - }, - { - "name": "Type", - "type": "byte", - "size": 1, - "description": "Message type (see 0x1C)" - }, - { - "name": "Hue", - "type": "short", - "size": 2, - "description": "Text color hue" - }, - { - "name": "Font", - "type": "short", - "size": 2, - "description": "Font ID" - }, - { - "name": "Cliloc Number", - "type": "int", - "size": 4, - "description": "Cliloc entry number" - }, - { - "name": "Name", - "type": "ascii", - "size": 30, - "description": "Speaker name" - }, - { - "name": "Arguments", - "type": "utf16le-t", - "description": "Tab-separated arguments for cliloc" - } - ], - "source": { - "file": "Projects/Server/Network/Packets/OutgoingMessagePackets.cs", - "line": 55 - } - }, - { - "id": "0xC2", - "name": "Unicode Prompt", - "description": "Server requests text input from client.", - "direction": "outgoing", - "isDynamic": false, - "size": 21, - "tags": ["Menu"], - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xC2", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "value": "21", - "description": "Packet length" - }, - { - "name": "Serial", - "type": "uint", - "size": 4, - "description": "Prompt serial" - }, - { - "name": "Prompt ID", - "type": "uint", - "size": 4, - "description": "Prompt ID (same as serial)" - }, - { - "name": "Padding", - "type": "byte[]", - "size": 10, - "description": "Unused padding (zeros)" - } - ], - "related": [ - { - "id": "0x9A", - "direction": "incoming", - "relationship": "response", - "note": "ASCII Prompt Response" - }, - { - "id": "0xC2", - "direction": "incoming", - "relationship": "response", - "note": "Unicode Prompt Response" - } - ], - "source": { - "file": "Projects/Server/Network/Packets/OutgoingMessagePackets.cs", - "line": 246 - } - }, - { - "id": "0xCC", - "name": "Localized Message Affix", - "description": "Server sends a localized message with text prefix/suffix.", - "direction": "outgoing", - "isDynamic": true, - "size": "var", - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xCC", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Serial", - "type": "uint", - "size": 4, - "description": "Serial of speaker" - }, - { - "name": "Graphic", - "type": "short", - "size": 2, - "description": "Body graphic of speaker" - }, - { - "name": "Type", - "type": "byte", - "size": 1, - "description": "Message type (see 0x1C)" - }, - { - "name": "Hue", - "type": "short", - "size": 2, - "description": "Text color hue" - }, - { - "name": "Font", - "type": "short", - "size": 2, - "description": "Font ID" - }, - { - "name": "Cliloc Number", - "type": "int", - "size": 4, - "description": "Cliloc entry number" - }, - { - "name": "Affix Type", - "type": "enum", - "size": 1, - "description": "How to apply the affix", - "values": [ - { - "value": 0, - "name": "Append", - "description": "Append affix to end" - }, - { - "value": 1, - "name": "Prepend", - "description": "Prepend affix to start" - }, - { - "value": 2, - "name": "System", - "description": "System message style" - } - ] - }, - { - "name": "Name", - "type": "ascii", - "size": 30, - "description": "Speaker name" - }, - { - "name": "Affix", - "type": "ascii-t", - "description": "Text to prepend/append" - }, - { - "name": "Arguments", - "type": "utf16be-t", - "description": "Tab-separated arguments for cliloc" - } - ], - "source": { - "file": "Projects/Server/Network/Packets/OutgoingMessagePackets.cs", - "line": 112 - } - } - ] -} \ No newline at end of file diff --git a/website/packets/outgoing/mobile.json b/website/packets/outgoing/mobile.json deleted file mode 100644 index ea8bc6910..000000000 --- a/website/packets/outgoing/mobile.json +++ /dev/null @@ -1,2102 +0,0 @@ -{ - "category": "Mobile", - "packets": [ - { - "id": "0x11", - "name": "Mobile Status", - "description": "Detailed status information for a mobile. Version 0 (compact) is sent when viewing other mobiles. Versions 3-6 are sent for self based on expansion.", - "direction": "outgoing", - "isDynamic": true, - "size": "Varies", - "variants": [ - { - "version": "Version 0 (Compact)", - "name": "Other Mobile Status", - "condition": "Viewing another mobile (not self)", - "size": 43, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x11", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "value": "43", - "description": "Packet length" - }, - { - "name": "Serial", - "type": "uint", - "size": 4, - "description": "Mobile serial" - }, - { - "name": "Name", - "type": "ascii", - "size": 30, - "description": "Mobile name" - }, - { - "name": "Hits", - "type": "short", - "size": 2, - "description": "Current hits (normalized 0-100)" - }, - { - "name": "Hits Max", - "type": "short", - "size": 2, - "value": "100", - "description": "Maximum hits (always 100 for normalized)" - }, - { - "name": "Can Be Renamed", - "type": "bool", - "size": 1, - "description": "Whether the mobile can be renamed by viewer" - }, - { - "name": "Version", - "type": "byte", - "size": 1, - "value": "0", - "description": "Status version (0 = compact)" - } - ] - }, - { - "version": "Version 3 (Basic)", - "name": "Self Status (Pre-AOS)", - "condition": "Viewing self, pre-AOS expansion", - "size": 70, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x11", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "value": "70", - "description": "Packet length" - }, - { - "name": "Serial", - "type": "uint", - "size": 4, - "description": "Mobile serial" - }, - { - "name": "Name", - "type": "ascii", - "size": 30, - "description": "Mobile name" - }, - { - "name": "Hits", - "type": "short", - "size": 2, - "description": "Current hits" - }, - { - "name": "Hits Max", - "type": "short", - "size": 2, - "description": "Maximum hits" - }, - { - "name": "Can Be Renamed", - "type": "bool", - "size": 1, - "description": "Whether the mobile can be renamed" - }, - { - "name": "Version", - "type": "byte", - "size": 1, - "value": "3", - "description": "Status version" - }, - { - "name": "Female", - "type": "bool", - "size": 1, - "description": "Gender flag (true = female)" - }, - { - "name": "Str", - "type": "short", - "size": 2, - "description": "Strength" - }, - { - "name": "Dex", - "type": "short", - "size": 2, - "description": "Dexterity" - }, - { - "name": "Int", - "type": "short", - "size": 2, - "description": "Intelligence" - }, - { - "name": "Stam", - "type": "short", - "size": 2, - "description": "Current stamina" - }, - { - "name": "Stam Max", - "type": "short", - "size": 2, - "description": "Maximum stamina" - }, - { - "name": "Mana", - "type": "short", - "size": 2, - "description": "Current mana" - }, - { - "name": "Mana Max", - "type": "short", - "size": 2, - "description": "Maximum mana" - }, - { - "name": "Gold", - "type": "int", - "size": 4, - "description": "Total gold in backpack" - }, - { - "name": "Armor Rating", - "type": "short", - "size": 2, - "description": "Armor rating" - }, - { - "name": "Weight", - "type": "short", - "size": 2, - "description": "Current weight (body weight + carried)" - }, - { - "name": "Stat Cap", - "type": "short", - "size": 2, - "description": "Total stat cap" - }, - { - "name": "Followers", - "type": "byte", - "size": 1, - "description": "Current follower count" - }, - { - "name": "Followers Max", - "type": "byte", - "size": 1, - "description": "Maximum follower slots" - } - ] - }, - { - "version": "Version 4 (AOS)", - "name": "Self Status (Age of Shadows)", - "condition": "Viewing self, AOS expansion", - "size": 88, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x11", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "value": "88", - "description": "Packet length" - }, - { - "name": "Serial", - "type": "uint", - "size": 4, - "description": "Mobile serial" - }, - { - "name": "Name", - "type": "ascii", - "size": 30, - "description": "Mobile name" - }, - { - "name": "Hits", - "type": "short", - "size": 2, - "description": "Current hits" - }, - { - "name": "Hits Max", - "type": "short", - "size": 2, - "description": "Maximum hits" - }, - { - "name": "Can Be Renamed", - "type": "bool", - "size": 1, - "description": "Whether the mobile can be renamed" - }, - { - "name": "Version", - "type": "byte", - "size": 1, - "value": "4", - "description": "Status version" - }, - { - "name": "Female", - "type": "bool", - "size": 1, - "description": "Gender flag (true = female)" - }, - { - "name": "Str", - "type": "short", - "size": 2, - "description": "Strength" - }, - { - "name": "Dex", - "type": "short", - "size": 2, - "description": "Dexterity" - }, - { - "name": "Int", - "type": "short", - "size": 2, - "description": "Intelligence" - }, - { - "name": "Stam", - "type": "short", - "size": 2, - "description": "Current stamina" - }, - { - "name": "Stam Max", - "type": "short", - "size": 2, - "description": "Maximum stamina" - }, - { - "name": "Mana", - "type": "short", - "size": 2, - "description": "Current mana" - }, - { - "name": "Mana Max", - "type": "short", - "size": 2, - "description": "Maximum mana" - }, - { - "name": "Gold", - "type": "int", - "size": 4, - "description": "Total gold in backpack" - }, - { - "name": "Physical Resist", - "type": "short", - "size": 2, - "description": "Physical resistance" - }, - { - "name": "Weight", - "type": "short", - "size": 2, - "description": "Current weight" - }, - { - "name": "Stat Cap", - "type": "short", - "size": 2, - "description": "Total stat cap" - }, - { - "name": "Followers", - "type": "byte", - "size": 1, - "description": "Current follower count" - }, - { - "name": "Followers Max", - "type": "byte", - "size": 1, - "description": "Maximum follower slots" - }, - { - "name": "Fire Resist", - "type": "short", - "size": 2, - "description": "Fire resistance" - }, - { - "name": "Cold Resist", - "type": "short", - "size": 2, - "description": "Cold resistance" - }, - { - "name": "Poison Resist", - "type": "short", - "size": 2, - "description": "Poison resistance" - }, - { - "name": "Energy Resist", - "type": "short", - "size": 2, - "description": "Energy resistance" - }, - { - "name": "Luck", - "type": "short", - "size": 2, - "description": "Luck" - }, - { - "name": "Damage Min", - "type": "short", - "size": 2, - "description": "Minimum weapon damage" - }, - { - "name": "Damage Max", - "type": "short", - "size": 2, - "description": "Maximum weapon damage" - }, - { - "name": "Tithing Points", - "type": "int", - "size": 4, - "description": "Tithing points (for Chivalry)" - } - ] - }, - { - "version": "Version 5 (ML)", - "name": "Self Status (Mondain\u0027s Legacy)", - "condition": "Viewing self, ML expansion", - "size": 91, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x11", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "value": "91", - "description": "Packet length" - }, - { - "name": "Serial", - "type": "uint", - "size": 4, - "description": "Mobile serial" - }, - { - "name": "Name", - "type": "ascii", - "size": 30, - "description": "Mobile name" - }, - { - "name": "Hits", - "type": "short", - "size": 2, - "description": "Current hits" - }, - { - "name": "Hits Max", - "type": "short", - "size": 2, - "description": "Maximum hits" - }, - { - "name": "Can Be Renamed", - "type": "bool", - "size": 1, - "description": "Whether the mobile can be renamed" - }, - { - "name": "Version", - "type": "byte", - "size": 1, - "value": "5", - "description": "Status version" - }, - { - "name": "Female", - "type": "bool", - "size": 1, - "description": "Gender flag (true = female)" - }, - { - "name": "Str", - "type": "short", - "size": 2, - "description": "Strength" - }, - { - "name": "Dex", - "type": "short", - "size": 2, - "description": "Dexterity" - }, - { - "name": "Int", - "type": "short", - "size": 2, - "description": "Intelligence" - }, - { - "name": "Stam", - "type": "short", - "size": 2, - "description": "Current stamina" - }, - { - "name": "Stam Max", - "type": "short", - "size": 2, - "description": "Maximum stamina" - }, - { - "name": "Mana", - "type": "short", - "size": 2, - "description": "Current mana" - }, - { - "name": "Mana Max", - "type": "short", - "size": 2, - "description": "Maximum mana" - }, - { - "name": "Gold", - "type": "int", - "size": 4, - "description": "Total gold in backpack" - }, - { - "name": "Physical Resist", - "type": "short", - "size": 2, - "description": "Physical resistance" - }, - { - "name": "Weight", - "type": "short", - "size": 2, - "description": "Current weight" - }, - { - "name": "Max Weight", - "type": "short", - "size": 2, - "description": "Maximum carry weight" - }, - { - "name": "Race", - "type": "byte", - "size": 1, - "description": "Race ID + 1 (1=Human, 2=Elf, 0=none)" - }, - { - "name": "Stat Cap", - "type": "short", - "size": 2, - "description": "Total stat cap" - }, - { - "name": "Followers", - "type": "byte", - "size": 1, - "description": "Current follower count" - }, - { - "name": "Followers Max", - "type": "byte", - "size": 1, - "description": "Maximum follower slots" - }, - { - "name": "Fire Resist", - "type": "short", - "size": 2, - "description": "Fire resistance" - }, - { - "name": "Cold Resist", - "type": "short", - "size": 2, - "description": "Cold resistance" - }, - { - "name": "Poison Resist", - "type": "short", - "size": 2, - "description": "Poison resistance" - }, - { - "name": "Energy Resist", - "type": "short", - "size": 2, - "description": "Energy resistance" - }, - { - "name": "Luck", - "type": "short", - "size": 2, - "description": "Luck" - }, - { - "name": "Damage Min", - "type": "short", - "size": 2, - "description": "Minimum weapon damage" - }, - { - "name": "Damage Max", - "type": "short", - "size": 2, - "description": "Maximum weapon damage" - }, - { - "name": "Tithing Points", - "type": "int", - "size": 4, - "description": "Tithing points (for Chivalry)" - } - ] - }, - { - "version": "Version 6 (HS)", - "name": "Self Status (High Seas)", - "condition": "Viewing self, High Seas expansion with ExtendedStatus", - "size": 121, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x11", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "value": "121", - "description": "Packet length" - }, - { - "name": "Serial", - "type": "uint", - "size": 4, - "description": "Mobile serial" - }, - { - "name": "Name", - "type": "ascii", - "size": 30, - "description": "Mobile name" - }, - { - "name": "Hits", - "type": "short", - "size": 2, - "description": "Current hits" - }, - { - "name": "Hits Max", - "type": "short", - "size": 2, - "description": "Maximum hits" - }, - { - "name": "Can Be Renamed", - "type": "bool", - "size": 1, - "description": "Whether the mobile can be renamed" - }, - { - "name": "Version", - "type": "byte", - "size": 1, - "value": "6", - "description": "Status version" - }, - { - "name": "Female", - "type": "bool", - "size": 1, - "description": "Gender flag (true = female)" - }, - { - "name": "Str", - "type": "short", - "size": 2, - "description": "Strength" - }, - { - "name": "Dex", - "type": "short", - "size": 2, - "description": "Dexterity" - }, - { - "name": "Int", - "type": "short", - "size": 2, - "description": "Intelligence" - }, - { - "name": "Stam", - "type": "short", - "size": 2, - "description": "Current stamina" - }, - { - "name": "Stam Max", - "type": "short", - "size": 2, - "description": "Maximum stamina" - }, - { - "name": "Mana", - "type": "short", - "size": 2, - "description": "Current mana" - }, - { - "name": "Mana Max", - "type": "short", - "size": 2, - "description": "Maximum mana" - }, - { - "name": "Gold", - "type": "int", - "size": 4, - "description": "Total gold in backpack" - }, - { - "name": "Physical Resist", - "type": "short", - "size": 2, - "description": "Physical resistance" - }, - { - "name": "Weight", - "type": "short", - "size": 2, - "description": "Current weight" - }, - { - "name": "Max Weight", - "type": "short", - "size": 2, - "description": "Maximum carry weight" - }, - { - "name": "Race", - "type": "byte", - "size": 1, - "description": "Race ID + 1 (1=Human, 2=Elf, 3=Gargoyle)" - }, - { - "name": "Stat Cap", - "type": "short", - "size": 2, - "description": "Total stat cap" - }, - { - "name": "Followers", - "type": "byte", - "size": 1, - "description": "Current follower count" - }, - { - "name": "Followers Max", - "type": "byte", - "size": 1, - "description": "Maximum follower slots" - }, - { - "name": "Fire Resist", - "type": "short", - "size": 2, - "description": "Fire resistance" - }, - { - "name": "Cold Resist", - "type": "short", - "size": 2, - "description": "Cold resistance" - }, - { - "name": "Poison Resist", - "type": "short", - "size": 2, - "description": "Poison resistance" - }, - { - "name": "Energy Resist", - "type": "short", - "size": 2, - "description": "Energy resistance" - }, - { - "name": "Luck", - "type": "short", - "size": 2, - "description": "Luck" - }, - { - "name": "Damage Min", - "type": "short", - "size": 2, - "description": "Minimum weapon damage" - }, - { - "name": "Damage Max", - "type": "short", - "size": 2, - "description": "Maximum weapon damage" - }, - { - "name": "Tithing Points", - "type": "int", - "size": 4, - "description": "Tithing points (for Chivalry)" - }, - { - "name": "Aos Statuses", - "type": "short[15]", - "size": 30, - "description": "Extended AOS status values. Array of 15 shorts indexed by status type.", - "values": [ - { - "value": 0, - "name": "Max Physical Resist", - "description": "Maximum physical resistance cap" - }, - { - "value": 1, - "name": "Max Fire Resist", - "description": "Maximum fire resistance cap" - }, - { - "value": 2, - "name": "Max Cold Resist", - "description": "Maximum cold resistance cap" - }, - { - "value": 3, - "name": "Max Poison Resist", - "description": "Maximum poison resistance cap" - }, - { - "value": 4, - "name": "Max Energy Resist", - "description": "Maximum energy resistance cap" - }, - { - "value": 5, - "name": "Defense Chance Increase", - "description": "Current defense chance increase %" - }, - { - "value": 6, - "name": "Defense Chance Cap", - "description": "Maximum defense chance cap (always 45)" - }, - { - "value": 7, - "name": "Hit Chance Increase", - "description": "Hit chance increase %" - }, - { - "value": 8, - "name": "Swing Speed Increase", - "description": "Swing speed increase %" - }, - { - "value": 9, - "name": "Damage Increase", - "description": "Damage increase %" - }, - { - "value": 10, - "name": "Lower Reagent Cost", - "description": "Lower reagent cost %" - }, - { - "value": 11, - "name": "Spell Damage Increase", - "description": "Spell damage increase %" - }, - { - "value": 12, - "name": "Faster Cast Recovery", - "description": "Faster cast recovery" - }, - { - "value": 13, - "name": "Faster Casting", - "description": "Faster casting" - }, - { - "value": 14, - "name": "Lower Mana Cost", - "description": "Lower mana cost %" - } - ] - } - ], - "notes": "Enhanced Client supports additional status indices 15-28 (HP/Stam/Mana regen, Reflect Physical, Enhance Potions, Stat Increases). ModernUO does not support Enhanced Client." - } - ], - "notes": "ModernUO supports 15 AOS status values (indices 0-14). Enhanced Client extends this to 29 values (indices 0-28), but ModernUO does not support Enhanced Client.", - "related": [ - { - "id": "0x34", - "direction": "incoming", - "relationship": "request", - "note": "Mobile Query (stats) triggers this response" - } - ], - "source": { - "file": "Projects/Server/Network/Packets/OutgoingMobilePackets.cs", - "line": 497 - } - }, - { - "id": "0x20", - "name": "Mobile Update", - "description": "Updates a mobile\u0027s basic display information (body, hue, position, flags).", - "direction": "outgoing", - "isDynamic": false, - "size": 19, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x20", - "description": "Packet identifier" - }, - { - "name": "Serial", - "type": "uint", - "size": 4, - "description": "Mobile serial" - }, - { - "name": "Body", - "type": "short", - "size": 2, - "description": "Body/graphic ID" - }, - { - "name": "Unknown", - "type": "byte", - "size": 1, - "description": "Unknown (always 0)" - }, - { - "name": "Hue", - "type": "short", - "size": 2, - "description": "Hue/color (or solid hue override)" - }, - { - "name": "Flags", - "type": "byte", - "size": 1, - "description": "Packet flags (hidden, poisoned, etc.)" - }, - { - "name": "X", - "type": "short", - "size": 2, - "description": "X coordinate" - }, - { - "name": "Y", - "type": "short", - "size": 2, - "description": "Y coordinate" - }, - { - "name": "Unknown2", - "type": "short", - "size": 2, - "description": "Unknown (always 0)" - }, - { - "name": "Direction", - "type": "byte", - "size": 1, - "description": "Facing direction" - }, - { - "name": "Z", - "type": "sbyte", - "size": 1, - "description": "Z coordinate" - } - ], - "source": { - "file": "Projects/Server/Network/Packets/OutgoingMobilePackets.cs", - "line": 578 - } - }, - { - "id": "0x77", - "name": "Mobile Moving", - "description": "Sent when a mobile moves. Contains position, direction, hue, and notoriety.", - "direction": "outgoing", - "isDynamic": false, - "size": 17, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x77", - "description": "Packet identifier" - }, - { - "name": "Serial", - "type": "uint", - "size": 4, - "description": "Mobile serial" - }, - { - "name": "Body", - "type": "short", - "size": 2, - "description": "Body/graphic ID" - }, - { - "name": "X", - "type": "short", - "size": 2, - "description": "X coordinate" - }, - { - "name": "Y", - "type": "short", - "size": 2, - "description": "Y coordinate" - }, - { - "name": "Z", - "type": "sbyte", - "size": 1, - "description": "Z coordinate" - }, - { - "name": "Direction", - "type": "byte", - "size": 1, - "description": "Direction (0-7) with running flag (0x80)" - }, - { - "name": "Hue", - "type": "short", - "size": 2, - "description": "Hue/color" - }, - { - "name": "Flags", - "type": "byte", - "size": 1, - "description": "Packet flags" - }, - { - "name": "Notoriety", - "type": "enum", - "enumType": "sequential", - "size": 1, - "description": "Notoriety flag", - "values": [ - { - "value": 0, - "name": "Innocent", - "description": "Blue - innocent player" - }, - { - "value": 1, - "name": "Friend", - "description": "Green - ally/friend" - }, - { - "value": 2, - "name": "Attackable", - "description": "Gray - can be attacked" - }, - { - "value": 3, - "name": "Criminal", - "description": "Gray - criminal flag" - }, - { - "value": 4, - "name": "Enemy", - "description": "Orange - enemy" - }, - { - "value": 5, - "name": "Murderer", - "description": "Red - murderer" - }, - { - "value": 6, - "name": "Invulnerable", - "description": "Yellow - invulnerable" - } - ] - } - ], - "source": { - "file": "Projects/Server/Network/Packets/OutgoingMobilePackets.cs", - "line": 104 - } - }, - { - "id": "0x78", - "name": "Mobile Incoming", - "description": "Full mobile information including equipped items. Sent when a mobile enters view range. Structure varies by client version.", - "direction": "outgoing", - "isDynamic": true, - "size": "23+", - "variants": [ - { - "name": "Pre-Stygian Abyss", - "condition": "Classic client < 7.0.0.0", - "size": "23+", - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x78", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Serial", - "type": "uint", - "size": 4, - "description": "Mobile serial" - }, - { - "name": "Body", - "type": "short", - "size": 2, - "description": "Body/graphic ID" - }, - { - "name": "X", - "type": "short", - "size": 2, - "description": "X coordinate" - }, - { - "name": "Y", - "type": "short", - "size": 2, - "description": "Y coordinate" - }, - { - "name": "Z", - "type": "sbyte", - "size": 1, - "description": "Z coordinate" - }, - { - "name": "Direction", - "type": "byte", - "size": 1, - "description": "Facing direction" - }, - { - "name": "Hue", - "type": "short", - "size": 2, - "description": "Hue/color" - }, - { - "name": "Flags", - "type": "byte", - "size": 1, - "description": "Packet flags" - }, - { - "name": "Notoriety", - "type": "enum", - "enumType": "sequential", - "size": 1, - "description": "Notoriety flag", - "values": [ - { - "value": 0, - "name": "Innocent", - "description": "Blue - innocent player" - }, - { - "value": 1, - "name": "Friend", - "description": "Green - ally/friend" - }, - { - "value": 2, - "name": "Attackable", - "description": "Gray - can be attacked" - }, - { - "value": 3, - "name": "Criminal", - "description": "Gray - criminal flag" - }, - { - "value": 4, - "name": "Enemy", - "description": "Orange - enemy" - }, - { - "value": 5, - "name": "Murderer", - "description": "Red - murderer" - }, - { - "value": 6, - "name": "Invulnerable", - "description": "Yellow - invulnerable" - } - ] - }, - { - "name": "Equipment", - "type": "array", - "description": "Equipped items (terminated by 0x00000000)", - "loop": { - "countField": "variable (until terminator)", - "fields": [ - { - "name": "Item Serial", - "type": "uint", - "size": 4, - "description": "Item serial (0 = end of list)" - }, - { - "name": "Item ID", - "type": "ushort", - "size": 2, - "description": "Item graphic ID (15-bit, 0x7FFF mask). Bit 0x8000 indicates hue follows." - }, - { - "name": "Layer", - "type": "byte", - "size": 1, - "description": "Equipment layer" - }, - { - "name": "Hue", - "type": "short", - "size": 2, - "description": "Item hue (only present if bit 0x8000 set on Item ID)", - "condition": "Item ID & 0x8000" - } - ] - } - }, - { - "name": "Terminator", - "type": "int", - "size": 4, - "value": "0x00000000", - "description": "Equipment list terminator" - } - ] - }, - { - "name": "Stygian Abyss", - "condition": "Classic client 7.0.0.0 - 7.0.33.0, or Enhanced Client", - "size": "23+", - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x78", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Serial", - "type": "uint", - "size": 4, - "description": "Mobile serial" - }, - { - "name": "Body", - "type": "short", - "size": 2, - "description": "Body/graphic ID" - }, - { - "name": "X", - "type": "short", - "size": 2, - "description": "X coordinate" - }, - { - "name": "Y", - "type": "short", - "size": 2, - "description": "Y coordinate" - }, - { - "name": "Z", - "type": "sbyte", - "size": 1, - "description": "Z coordinate" - }, - { - "name": "Direction", - "type": "byte", - "size": 1, - "description": "Facing direction" - }, - { - "name": "Hue", - "type": "short", - "size": 2, - "description": "Hue/color" - }, - { - "name": "Flags", - "type": "byte", - "size": 1, - "description": "Packet flags (SA format)" - }, - { - "name": "Notoriety", - "type": "enum", - "enumType": "sequential", - "size": 1, - "description": "Notoriety flag", - "values": [ - { - "value": 0, - "name": "Innocent", - "description": "Blue - innocent player" - }, - { - "value": 1, - "name": "Friend", - "description": "Green - ally/friend" - }, - { - "value": 2, - "name": "Attackable", - "description": "Gray - can be attacked" - }, - { - "value": 3, - "name": "Criminal", - "description": "Gray - criminal flag" - }, - { - "value": 4, - "name": "Enemy", - "description": "Orange - enemy" - }, - { - "value": 5, - "name": "Murderer", - "description": "Red - murderer" - }, - { - "value": 6, - "name": "Invulnerable", - "description": "Yellow - invulnerable" - } - ] - }, - { - "name": "Equipment", - "type": "array", - "description": "Equipped items (terminated by 0x00000000)", - "loop": { - "countField": "variable (until terminator)", - "fields": [ - { - "name": "Item Serial", - "type": "uint", - "size": 4, - "description": "Item serial (0 = end of list)" - }, - { - "name": "Item ID", - "type": "ushort", - "size": 2, - "description": "Item graphic ID (15-bit, 0x7FFF mask). Bit 0x8000 indicates hue follows." - }, - { - "name": "Layer", - "type": "byte", - "size": 1, - "description": "Equipment layer" - }, - { - "name": "Hue", - "type": "short", - "size": 2, - "description": "Item hue (only present if bit 0x8000 set on Item ID)", - "condition": "Item ID & 0x8000" - } - ] - } - }, - { - "name": "Terminator", - "type": "int", - "size": 4, - "value": "0x00000000", - "description": "Equipment list terminator" - } - ] - }, - { - "name": "NewMobileIncoming", - "condition": "Classic client >= 7.0.33.1", - "size": "23+", - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x78", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Serial", - "type": "uint", - "size": 4, - "description": "Mobile serial" - }, - { - "name": "Body", - "type": "short", - "size": 2, - "description": "Body/graphic ID" - }, - { - "name": "X", - "type": "short", - "size": 2, - "description": "X coordinate" - }, - { - "name": "Y", - "type": "short", - "size": 2, - "description": "Y coordinate" - }, - { - "name": "Z", - "type": "sbyte", - "size": 1, - "description": "Z coordinate" - }, - { - "name": "Direction", - "type": "byte", - "size": 1, - "description": "Facing direction" - }, - { - "name": "Hue", - "type": "short", - "size": 2, - "description": "Hue/color" - }, - { - "name": "Flags", - "type": "byte", - "size": 1, - "description": "Packet flags (SA format)" - }, - { - "name": "Notoriety", - "type": "enum", - "enumType": "sequential", - "size": 1, - "description": "Notoriety flag", - "values": [ - { - "value": 0, - "name": "Innocent", - "description": "Blue - innocent player" - }, - { - "value": 1, - "name": "Friend", - "description": "Green - ally/friend" - }, - { - "value": 2, - "name": "Attackable", - "description": "Gray - can be attacked" - }, - { - "value": 3, - "name": "Criminal", - "description": "Gray - criminal flag" - }, - { - "value": 4, - "name": "Enemy", - "description": "Orange - enemy" - }, - { - "value": 5, - "name": "Murderer", - "description": "Red - murderer" - }, - { - "value": 6, - "name": "Invulnerable", - "description": "Yellow - invulnerable" - } - ] - }, - { - "name": "Equipment", - "type": "array", - "description": "Equipped items (terminated by 0x00000000). Always includes hue field.", - "loop": { - "countField": "variable (until terminator)", - "fields": [ - { - "name": "Item Serial", - "type": "uint", - "size": 4, - "description": "Item serial (0 = end of list)" - }, - { - "name": "Item ID", - "type": "ushort", - "size": 2, - "description": "Item graphic ID (full 16-bit, 0xFFFF mask)" - }, - { - "name": "Layer", - "type": "byte", - "size": 1, - "description": "Equipment layer" - }, - { - "name": "Hue", - "type": "short", - "size": 2, - "description": "Item hue (always present)" - } - ] - } - }, - { - "name": "Terminator", - "type": "int", - "size": 4, - "value": "0x00000000", - "description": "Equipment list terminator" - } - ] - } - ], - "source": { - "file": "Projects/Server/Network/Packets/OutgoingMobilePackets.cs", - "line": 601 - } - }, - { - "id": "0xA1", - "name": "Mobile Hits", - "description": "Updates a mobile\u0027s hit points. Can be normalized to 0-100 scale for non-self mobiles.", - "direction": "outgoing", - "isDynamic": false, - "size": 9, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xA1", - "description": "Packet identifier" - }, - { - "name": "Serial", - "type": "uint", - "size": 4, - "description": "Mobile serial" - }, - { - "name": "Hits Max", - "type": "short", - "size": 2, - "description": "Maximum hits" - }, - { - "name": "Hits", - "type": "short", - "size": 2, - "description": "Current hits" - } - ], - "source": { - "file": "Projects/Server/Network/Packets/OutgoingMobilePackets.cs", - "line": 210 - } - }, - { - "id": "0xA2", - "name": "Mobile Mana", - "description": "Updates a mobile\u0027s mana points.", - "direction": "outgoing", - "isDynamic": false, - "size": 9, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xA2", - "description": "Packet identifier" - }, - { - "name": "Serial", - "type": "uint", - "size": 4, - "description": "Mobile serial" - }, - { - "name": "Mana Max", - "type": "short", - "size": 2, - "description": "Maximum mana" - }, - { - "name": "Mana", - "type": "short", - "size": 2, - "description": "Current mana" - } - ], - "source": { - "file": "Projects/Server/Network/Packets/OutgoingMobilePackets.cs", - "line": 235 - } - }, - { - "id": "0xA3", - "name": "Mobile Stamina", - "description": "Updates a mobile\u0027s stamina points.", - "direction": "outgoing", - "isDynamic": false, - "size": 9, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xA3", - "description": "Packet identifier" - }, - { - "name": "Serial", - "type": "uint", - "size": 4, - "description": "Mobile serial" - }, - { - "name": "Stam Max", - "type": "short", - "size": 2, - "description": "Maximum stamina" - }, - { - "name": "Stam", - "type": "short", - "size": 2, - "description": "Current stamina" - } - ], - "source": { - "file": "Projects/Server/Network/Packets/OutgoingMobilePackets.cs", - "line": 260 - } - }, - { - "id": "0x2D", - "name": "Mobile Attributes", - "description": "Updates all three attribute bars (hits, mana, stamina) in a single packet.", - "direction": "outgoing", - "isDynamic": false, - "size": 17, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x2D", - "description": "Packet identifier" - }, - { - "name": "Serial", - "type": "uint", - "size": 4, - "description": "Mobile serial" - }, - { - "name": "Hits Max", - "type": "short", - "size": 2, - "description": "Maximum hits" - }, - { - "name": "Hits", - "type": "short", - "size": 2, - "description": "Current hits" - }, - { - "name": "Mana Max", - "type": "short", - "size": 2, - "description": "Maximum mana" - }, - { - "name": "Mana", - "type": "short", - "size": 2, - "description": "Current mana" - }, - { - "name": "Stam Max", - "type": "short", - "size": 2, - "description": "Maximum stamina" - }, - { - "name": "Stam", - "type": "short", - "size": 2, - "description": "Current stamina" - } - ], - "source": { - "file": "Projects/Server/Network/Packets/OutgoingMobilePackets.cs", - "line": 285 - } - }, - { - "id": "0x98", - "name": "Mobile Name", - "description": "Returns a mobile\u0027s name in response to a name request.", - "direction": "outgoing", - "isDynamic": false, - "size": 37, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x98", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "value": "0x0025", - "description": "Packet length (37)" - }, - { - "name": "Serial", - "type": "uint", - "size": 4, - "description": "Mobile serial" - }, - { - "name": "Name", - "type": "ascii", - "size": 30, - "description": "Mobile name (null-terminated)" - } - ], - "source": { - "file": "Projects/Server/Network/Packets/OutgoingMobilePackets.cs", - "line": 301 - } - }, - { - "id": "0x6E", - "name": "Mobile Animation", - "description": "Triggers an animation on a mobile.", - "direction": "outgoing", - "isDynamic": false, - "size": 14, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x6E", - "description": "Packet identifier" - }, - { - "name": "Serial", - "type": "uint", - "size": 4, - "description": "Mobile serial" - }, - { - "name": "Action", - "type": "short", - "size": 2, - "description": "Animation action ID" - }, - { - "name": "Frame Count", - "type": "short", - "size": 2, - "description": "Number of frames" - }, - { - "name": "Repeat Count", - "type": "short", - "size": 2, - "description": "Number of times to repeat" - }, - { - "name": "Reverse", - "type": "bool", - "size": 1, - "description": "Play animation in reverse" - }, - { - "name": "Repeat", - "type": "bool", - "size": 1, - "description": "Loop the animation" - }, - { - "name": "Delay", - "type": "byte", - "size": 1, - "description": "Frame delay" - } - ], - "source": { - "file": "Projects/Server/Network/Packets/OutgoingMobilePackets.cs", - "line": 318 - } - }, - { - "id": "0xE2", - "name": "New Mobile Animation", - "description": "Simplified animation packet for newer clients.", - "direction": "outgoing", - "isDynamic": false, - "size": 10, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xE2", - "description": "Packet identifier" - }, - { - "name": "Serial", - "type": "uint", - "size": 4, - "description": "Mobile serial" - }, - { - "name": "Action", - "type": "short", - "size": 2, - "description": "Animation action ID" - }, - { - "name": "Frame Count", - "type": "short", - "size": 2, - "description": "Number of frames" - }, - { - "name": "Delay", - "type": "byte", - "size": 1, - "description": "Frame delay" - } - ], - "source": { - "file": "Projects/Server/Network/Packets/OutgoingMobilePackets.cs", - "line": 354 - } - }, - { - "id": "0x17", - "name": "Mobile Healthbar", - "description": "Updates a mobile\u0027s healthbar status (poison level, yellow bar for invulnerability).", - "direction": "outgoing", - "isDynamic": false, - "size": 12, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x17", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "value": "0x000C", - "description": "Packet length (12)" - }, - { - "name": "Serial", - "type": "uint", - "size": 4, - "description": "Mobile serial" - }, - { - "name": "Show Bar", - "type": "enum", - "size": 2, - "description": "Show bar flag. If 0, packet ends here (no subsequent fields sent).", - "values": [ - { "value": "0", "name": "Hide", "description": "Hide healthbar, packet ends here" }, - { "value": "1", "name": "Show", "description": "Show healthbar, following fields included" } - ] - }, - { - "name": "Healthbar Type", - "type": "enum", - "size": 2, - "description": "Type of healthbar overlay (only sent if Show Bar = 1)", - "values": [ - { "value": "1", "name": "Poison", "description": "Poison status bar (green)" }, - { "value": "2", "name": "Yellow", "description": "Yellow/invulnerable bar" } - ] - }, - { - "name": "Level", - "type": "enum", - "size": 1, - "description": "Status level (only sent if Show Bar = 1)", - "values": [ - { "value": "0", "name": "Off", "description": "Status disabled" }, - { "value": "1", "name": "Level1/On", "description": "Poison level 1 or yellow on" }, - { "value": "2", "name": "Level2", "description": "Poison level 2 (Greater)" }, - { "value": "3", "name": "Level3", "description": "Poison level 3 (Deadly)" }, - { "value": "4", "name": "Level4", "description": "Poison level 4 (Lethal)" } - ] - } - ], - "related": [ - { - "id": "0x16", - "relationship": "variant", - "note": "Mobile Healthbar (EC) - Enhanced Client version" - } - ], - "clientVersion": { - "classic": {}, - "notes": "Classic Client only. EC uses 0x16 instead." - }, - "source": { - "file": "Projects/Server/Network/Packets/OutgoingMobilePackets.cs", - "line": 419 - }, - "notes": "When Show Bar is 0, the packet is only 9 bytes (no Healthbar Type or Level fields). Poison (green) overrides yellow coloring." - }, - { - "id": "0x16", - "name": "Mobile Healthbar", - "description": "Updates a mobile's healthbar status for Enhanced Client (poison level, yellow bar for invulnerability).", - "direction": "outgoing", - "isDynamic": true, - "implemented": false, - "size": "9 or 12", - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x16", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length (9 or 12)" - }, - { - "name": "Serial", - "type": "uint", - "size": 4, - "description": "Mobile serial" - }, - { - "name": "Show Bar", - "type": "enum", - "size": 2, - "description": "Show bar flag. If 0, packet ends here (no subsequent fields sent).", - "values": [ - { "value": "0", "name": "Hide", "description": "Hide healthbar, packet ends here" }, - { "value": "1", "name": "Show", "description": "Show healthbar, following fields included" } - ] - }, - { - "name": "Healthbar Type", - "type": "enum", - "size": 2, - "description": "Type of healthbar overlay (only sent if Show Bar = 1)", - "values": [ - { "value": "1", "name": "Poison", "description": "Poison status bar (green)" }, - { "value": "2", "name": "Yellow", "description": "Yellow/invulnerable bar" } - ] - }, - { - "name": "Level", - "type": "enum", - "size": 1, - "description": "Status level (only sent if Show Bar = 1)", - "values": [ - { "value": "0", "name": "Off", "description": "Status disabled" }, - { "value": "1", "name": "Level1/On", "description": "Poison level 1 or yellow on" }, - { "value": "2", "name": "Level2", "description": "Poison level 2 (Greater)" }, - { "value": "3", "name": "Level3", "description": "Poison level 3 (Deadly)" }, - { "value": "4", "name": "Level4", "description": "Poison level 4 (Lethal)" } - ] - } - ], - "related": [ - { - "id": "0x17", - "relationship": "variant", - "note": "Mobile Healthbar (Classic) - Classic Client version" - } - ], - "clientVersion": { - "enhanced": {}, - "notes": "Enhanced Client only. Classic uses 0x17 instead." - }, - "notes": "When Show Bar is 0, the packet is only 9 bytes (no Healthbar Type or Level fields). Poison (green) overrides yellow coloring. ModernUO currently sends 0x17 to all clients; this EC variant needs implementation." - }, - { - "id": "0xAF", - "name": "Death Animation", - "description": "Triggers the death animation and corpse creation for a mobile.", - "direction": "outgoing", - "isDynamic": false, - "size": 13, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xAF", - "description": "Packet identifier" - }, - { - "name": "Killed Serial", - "type": "uint", - "size": 4, - "description": "Serial of the killed mobile" - }, - { - "name": "Corpse Serial", - "type": "uint", - "size": 4, - "description": "Serial of the corpse created" - }, - { - "name": "Unknown", - "type": "int", - "size": 4, - "description": "Unknown (always 0)" - } - ], - "source": { - "file": "Projects/Server/Network/Packets/OutgoingMobilePackets.cs", - "line": 78 - } - } - ] -} \ No newline at end of file diff --git a/website/packets/outgoing/movement.json b/website/packets/outgoing/movement.json deleted file mode 100644 index 8d7632686..000000000 --- a/website/packets/outgoing/movement.json +++ /dev/null @@ -1,256 +0,0 @@ -{ - "category": "Movement", - "packets": [ - { - "id": "0x21", - "name": "Movement Rejection", - "description": "Sent by the server when player movement is blocked (collision, teleport area, etc). Contains the corrected position the client should snap back to.", - "direction": "outgoing", - "isDynamic": false, - "size": 8, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x21", - "description": "Packet identifier" - }, - { - "name": "Sequence", - "type": "byte", - "size": 1, - "description": "Sequence number of the rejected movement request" - }, - { - "name": "X", - "type": "short", - "size": 2, - "description": "Correct X coordinate" - }, - { - "name": "Y", - "type": "short", - "size": 2, - "description": "Correct Y coordinate" - }, - { - "name": "Direction", - "type": "byte", - "size": 1, - "description": "Correct facing direction (0-7)" - }, - { - "name": "Z", - "type": "sbyte", - "size": 1, - "description": "Correct Z coordinate" - } - ], - "related": [ - { - "id": "0x02", - "relationship": "request", - "note": "The movement request that was rejected" - } - ], - "source": { - "file": "Projects/Server/Network/Packets/OutgoingMovementPackets.cs", - "line": 45 - } - }, - { - "id": "0x22", - "name": "Movement Acknowledgment", - "description": "Sent by the server to confirm successful player movement. Includes the player\u0027s current notoriety for display purposes.", - "direction": "outgoing", - "noMerge": true, - "isDynamic": false, - "size": 3, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x22", - "description": "Packet identifier" - }, - { - "name": "Sequence", - "type": "byte", - "size": 1, - "description": "Sequence number of the accepted movement request" - }, - { - "name": "Notoriety", - "type": "enum", - "enumType": "sequential", - "size": 1, - "description": "Player\u0027s current notoriety flag", - "values": [ - { - "value": 0, - "name": "Innocent", - "description": "Blue - innocent player" - }, - { - "value": 1, - "name": "Friend", - "description": "Green - ally/friend" - }, - { - "value": 2, - "name": "Attackable", - "description": "Gray - can be attacked" - }, - { - "value": 3, - "name": "Criminal", - "description": "Gray - criminal flag" - }, - { - "value": 4, - "name": "Enemy", - "description": "Orange - enemy" - }, - { - "value": 5, - "name": "Murderer", - "description": "Red - murderer" - }, - { - "value": 6, - "name": "Invulnerable", - "description": "Yellow - invulnerable" - } - ] - } - ], - "related": [ - { - "id": "0x02", - "relationship": "request", - "note": "The movement request that was accepted" - } - ], - "notes": "Not related to incoming 0x22 (Resynchronize) despite sharing the same packet ID.", - "source": { - "file": "Projects/Server/Network/Packets/OutgoingMovementPackets.cs", - "line": 42 - } - }, - { - "id": "0x97", - "name": "Move Player", - "description": "Server-initiated player movement. Forces the client to move in a direction (used for pushback effects, conveyors, etc).", - "direction": "outgoing", - "isDynamic": false, - "size": 2, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x97", - "description": "Packet identifier" - }, - { - "name": "Direction", - "type": "byte", - "size": 1, - "description": "Direction to move (0-7). Bit 0x80 indicates running." - } - ], - "source": { - "file": "Projects/Server/Network/Packets/OutgoingMovementPackets.cs", - "line": 35 - } - }, - { - "id": "0xBF", - "subId": "0x26", - "name": "Speed Control", - "description": "Controls the player\u0027s movement speed. Used for mount speed changes, walk/run restrictions, etc.", - "direction": "outgoing", - "isDynamic": false, - "size": 6, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xBF", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "value": "0x0006", - "description": "Packet length" - }, - { - "name": "Sub-Command", - "type": "ushort", - "size": 2, - "value": "0x0026", - "description": "Sub-command for speed control" - }, - { - "name": "Speed Setting", - "type": "enum", - "size": 1, - "description": "Movement speed mode", - "values": [ - { "value": "0", "name": "Disable", "description": "Normal speed (remove override)" }, - { "value": "1", "name": "MountSpeed", "description": "Force mounted movement speed" }, - { "value": "2", "name": "WalkOnly", "description": "Force walk speed only" } - ] - } - ], - "source": { - "file": "Projects/Server/Network/Packets/OutgoingMovementPackets.cs", - "line": 31 - } - }, - { - "id": "0xF2", - "name": "Time Sync Response", - "description": "Server response to client time synchronization request. Contains tick counts for latency calculation.", - "direction": "outgoing", - "isDynamic": false, - "size": 25, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xF2", - "description": "Packet identifier" - }, - { - "name": "Tick Count1", - "type": "long", - "size": 8, - "description": "Server tick count" - }, - { - "name": "Tick Count2", - "type": "long", - "size": 8, - "description": "Server tick count" - }, - { - "name": "Tick Count3", - "type": "long", - "size": 8, - "description": "Server tick count" - } - ], - "source": { - "file": "Projects/Server/Network/Packets/OutgoingMovementPackets.cs", - "line": 63 - } - } - ] -} diff --git a/website/packets/outgoing/party.json b/website/packets/outgoing/party.json deleted file mode 100644 index 1fce9525a..000000000 --- a/website/packets/outgoing/party.json +++ /dev/null @@ -1,276 +0,0 @@ -{ - "category": "Party", - "packets": [ - { - "id": "0xBF", - "subId": "0x06", - "name": "Party Message", - "description": "Party system messages with various subcommands.", - "direction": "outgoing", - "isDynamic": true, - "size": "Varies", - "variants": [ - { - "name": "Member List", - "condition": "command == 0x01", - "size": "7 + (memberCount x 4)", - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xBF", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Sub-Command", - "type": "ushort", - "size": 2, - "value": "0x0006", - "description": "Party subcommand" - }, - { - "name": "Command", - "type": "byte", - "size": 1, - "value": "0x01", - "description": "Member list command" - }, - { - "name": "Member Count", - "type": "byte", - "size": 1, - "description": "Number of party members" - }, - { - "name": "Members", - "type": "loop", - "description": "Party members", - "loop": { - "countField": "memberCount", - "fields": [ - { - "name": "Serial", - "type": "uint", - "size": 4, - "description": "Member\u0027s serial" - } - ] - } - } - ] - }, - { - "name": "Remove Member", - "condition": "command == 0x02", - "size": "11 + (memberCount x 4)", - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xBF", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Sub-Command", - "type": "ushort", - "size": 2, - "value": "0x0006", - "description": "Party subcommand" - }, - { - "name": "Command", - "type": "byte", - "size": 1, - "value": "0x02", - "description": "Remove member command" - }, - { - "name": "Member Count", - "type": "byte", - "size": 1, - "description": "Number of remaining members" - }, - { - "name": "Removed Serial", - "type": "uint", - "size": 4, - "description": "Serial of removed member" - }, - { - "name": "Members", - "type": "loop", - "description": "Remaining party members", - "loop": { - "countField": "memberCount", - "fields": [ - { - "name": "Serial", - "type": "uint", - "size": 4, - "description": "Member\u0027s serial" - } - ] - } - } - ] - }, - { - "name": "Private Message", - "condition": "command == 0x03", - "size": "12 + (text.Length x 2)", - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xBF", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Sub-Command", - "type": "ushort", - "size": 2, - "value": "0x0006", - "description": "Party subcommand" - }, - { - "name": "Command", - "type": "byte", - "size": 1, - "value": "0x03", - "description": "Private message command" - }, - { - "name": "Sender Serial", - "type": "uint", - "size": 4, - "description": "Serial of message sender" - }, - { - "name": "Text", - "type": "utf16be-t", - "description": "Message text" - } - ] - }, - { - "name": "Public Message", - "condition": "command == 0x04", - "size": "12 + (text.Length x 2)", - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xBF", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Sub-Command", - "type": "ushort", - "size": 2, - "value": "0x0006", - "description": "Party subcommand" - }, - { - "name": "Command", - "type": "byte", - "size": 1, - "value": "0x04", - "description": "Public message command" - }, - { - "name": "Sender Serial", - "type": "uint", - "size": 4, - "description": "Serial of message sender" - }, - { - "name": "Text", - "type": "utf16be-t", - "description": "Message text" - } - ] - }, - { - "name": "Party Invitation", - "condition": "command == 0x07", - "size": 10, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xBF", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "value": "10", - "description": "Packet length" - }, - { - "name": "Sub-Command", - "type": "ushort", - "size": 2, - "value": "0x0006", - "description": "Party subcommand" - }, - { - "name": "Command", - "type": "byte", - "size": 1, - "value": "0x07", - "description": "Invitation command" - }, - { - "name": "Leader Serial", - "type": "uint", - "size": 4, - "description": "Serial of party leader" - } - ] - } - ], - "related": [ - { - "id": "0xBF/0x06", - "relationship": "request", - "note": "Party Message from client" - } - ], - "source": { - "file": "Projects/UOContent/Engines/Party/PartyPackets.cs", - "line": 28 - } - } - ] -} \ No newline at end of file diff --git a/website/packets/outgoing/player.json b/website/packets/outgoing/player.json deleted file mode 100644 index cf42eed14..000000000 --- a/website/packets/outgoing/player.json +++ /dev/null @@ -1,1211 +0,0 @@ -{ - "category": "Player", - "packets": [ - { - "id": "0x23", - "name": "Drag Effect", - "description": "Displays a drag/drop visual effect between two points.", - "direction": "outgoing", - "isDynamic": false, - "size": 26, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x23", - "description": "Packet identifier" - }, - { - "name": "Item ID", - "type": "short", - "size": 2, - "description": "Item graphic ID" - }, - { - "name": "Unknown", - "type": "byte", - "size": 1, - "value": "0x00", - "description": "Unknown" - }, - { - "name": "Hue", - "type": "short", - "size": 2, - "description": "Item hue" - }, - { - "name": "Amount", - "type": "short", - "size": 2, - "description": "Item amount" - }, - { - "name": "Src Serial", - "type": "uint", - "size": 4, - "description": "Source entity serial" - }, - { - "name": "Src X", - "type": "short", - "size": 2, - "description": "Source X coordinate" - }, - { - "name": "Src Y", - "type": "short", - "size": 2, - "description": "Source Y coordinate" - }, - { - "name": "Src Z", - "type": "sbyte", - "size": 1, - "description": "Source Z coordinate" - }, - { - "name": "Dst Serial", - "type": "uint", - "size": 4, - "description": "Destination entity serial" - }, - { - "name": "Dst X", - "type": "short", - "size": 2, - "description": "Destination X coordinate" - }, - { - "name": "Dst Y", - "type": "short", - "size": 2, - "description": "Destination Y coordinate" - }, - { - "name": "Dst Z", - "type": "sbyte", - "size": 1, - "description": "Destination Z coordinate" - } - ], - "source": { - "file": "Projects/Server/Network/Packets/OutgoingPlayerPackets.cs", - "line": 198 - } - }, - { - "id": "0x27", - "name": "Lift Reject", - "description": "Server rejects a lift/pick up request.", - "direction": "outgoing", - "isDynamic": false, - "size": 2, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x27", - "description": "Packet identifier" - }, - { - "name": "Reason", - "type": "enum", - "enumType": "sequential", - "size": 1, - "description": "Rejection reason", - "values": [ - { - "value": 0, - "name": "Cannot Lift", - "description": "You cannot pick that up" - }, - { - "value": 1, - "name": "Out Of Range", - "description": "That is out of range" - }, - { - "value": 2, - "name": "Out Of Sight", - "description": "That is out of sight" - }, - { - "value": 3, - "name": "Try To Steal", - "description": "That does not belong to you" - }, - { - "value": 4, - "name": "Are Holding", - "description": "You are already holding an item" - }, - { - "value": 5, - "name": "Inspecific", - "description": "You cannot pick that up" - } - ] - } - ], - "related": [ - { - "id": "0x07", - "relationship": "request", - "note": "Lift Request that was rejected" - } - ], - "source": { - "file": "Projects/Server/Network/Packets/OutgoingPlayerPackets.cs", - "line": 90 - } - }, - { - "id": "0x2C", - "name": "Death Status", - "description": "Notifies client of death/resurrection state.", - "direction": "outgoing", - "isDynamic": false, - "size": 2, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x2C", - "description": "Packet identifier" - }, - { - "name": "Dead", - "type": "byte", - "size": 1, - "value": "0x02", - "description": "Always 2 (dead)" - } - ], - "source": { - "file": "Projects/Server/Network/Packets/OutgoingPlayerPackets.cs", - "line": 63 - } - }, - { - "id": "0x38", - "name": "Pathfind Message", - "description": "Instructs client to pathfind to a location.", - "direction": "outgoing", - "isDynamic": false, - "size": 7, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x38", - "description": "Packet identifier" - }, - { - "name": "X", - "type": "short", - "size": 2, - "description": "Target X coordinate" - }, - { - "name": "Y", - "type": "short", - "size": 2, - "description": "Target Y coordinate" - }, - { - "name": "Z", - "type": "short", - "size": 2, - "description": "Target Z coordinate" - } - ], - "source": { - "file": "Projects/Server/Network/Packets/OutgoingPlayerPackets.cs", - "line": 319 - } - }, - { - "id": "0x3A", - "name": "Skills Update", - "description": "Sends full skills list or single skill update to client.", - "direction": "outgoing", - "isDynamic": true, - "size": "var", - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x3A", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Type", - "type": "enum", - "enumType": "sequential", - "size": 1, - "description": "Update type", - "values": [ - { - "value": "0x00", - "name": "Full No Caps", - "description": "Full list without caps" - }, - { - "value": "0x02", - "name": "Full With Caps", - "description": "Full list with skill caps" - }, - { - "value": "0xDF", - "name": "Single With Caps", - "description": "Single skill update with cap" - }, - { - "value": "0xFF", - "name": "Single No Caps", - "description": "Single skill update" - } - ] - }, - { - "name": "Skills", - "type": "loop", - "loop": { - "until": "skillId == 0", - "fields": [ - { - "name": "Skill ID", - "type": "ushort", - "size": 2, - "description": "Skill ID + 1 (0 terminates)" - }, - { - "name": "Value", - "type": "ushort", - "size": 2, - "description": "Current skill value * 10" - }, - { - "name": "Base", - "type": "ushort", - "size": 2, - "description": "Base skill value * 10" - }, - { - "name": "Lock", - "type": "enum", - "enumType": "sequential", - "size": 1, - "description": "Skill lock state", - "values": [ - { - "value": 0, - "name": "Up", - "description": "Skill gains enabled" - }, - { - "value": 1, - "name": "Down", - "description": "Skill decreases enabled" - }, - { - "value": 2, - "name": "Locked", - "description": "Skill locked" - } - ] - }, - { - "name": "Cap", - "type": "ushort", - "size": 2, - "description": "Skill cap * 10 (if type includes caps)" - } - ] - } - } - ], - "related": [ - { - "id": "0x34", - "direction": "incoming", - "relationship": "request", - "note": "Mobile Query (skills) triggers this response" - }, - { - "id": "0x3A", - "direction": "incoming", - "relationship": "related", - "note": "Change Skill Lock may trigger update" - } - ], - "source": { - "file": "Projects/Server/Network/Packets/OutgoingPlayerPackets.cs", - "line": 121 - } - }, - { - "id": "0x5B", - "name": "Current Time", - "description": "Sends current server time to client.", - "direction": "outgoing", - "isDynamic": false, - "size": 4, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x5B", - "description": "Packet identifier" - }, - { - "name": "Hour", - "type": "byte", - "size": 1, - "description": "Hour (0-23)" - }, - { - "name": "Minute", - "type": "byte", - "size": 1, - "description": "Minute (0-59)" - }, - { - "name": "Second", - "type": "byte", - "size": 1, - "description": "Second (0-59)" - } - ], - "source": { - "file": "Projects/Server/Network/Packets/OutgoingPlayerPackets.cs", - "line": 316 - } - }, - { - "id": "0x65", - "name": "Weather", - "description": "Sets weather conditions for client.", - "direction": "outgoing", - "isDynamic": false, - "size": 4, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x65", - "description": "Packet identifier" - }, - { - "name": "Type", - "type": "enum", - "enumType": "sequential", - "size": 1, - "description": "Weather type", - "values": [ - { - "value": 0, - "name": "Rain", - "description": "Raining" - }, - { - "value": 1, - "name": "Storm Brewing", - "description": "Storm brewing" - }, - { - "value": 2, - "name": "Snow", - "description": "Snowing" - }, - { - "value": 3, - "name": "Storm", - "description": "Storm in progress" - }, - { - "value": 254, - "name": "None", - "description": "No weather" - }, - { - "value": 255, - "name": "Clear", - "description": "Clear weather (stops current)" - } - ] - }, - { - "name": "Intensity", - "type": "byte", - "size": 1, - "description": "Weather intensity (0-255)" - }, - { - "name": "Temperature", - "type": "byte", - "size": 1, - "description": "Temperature value" - } - ], - "source": { - "file": "Projects/Server/Network/Packets/OutgoingPlayerPackets.cs", - "line": 97 - } - }, - { - "id": "0x6D", - "name": "Play Music", - "description": "Plays background music on client.", - "direction": "outgoing", - "isDynamic": false, - "size": 3, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x6D", - "description": "Packet identifier" - }, - { - "name": "Music ID", - "type": "short", - "size": 2, - "description": "Music track ID (0x1FFF = stop)" - } - ], - "source": { - "file": "Projects/Server/Network/Packets/OutgoingPlayerPackets.cs", - "line": 275 - } - }, - { - "id": "0x73", - "name": "Ping Ack", - "description": "Server acknowledges ping request.", - "direction": "outgoing", - "isDynamic": false, - "size": 2, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x73", - "description": "Packet identifier" - }, - { - "name": "Sequence", - "type": "byte", - "size": 1, - "description": "Ping sequence (echoed from request)" - } - ], - "source": { - "file": "Projects/Server/Network/Packets/OutgoingPlayerPackets.cs", - "line": 336 - } - }, - { - "id": "0x76", - "name": "Server Change", - "description": "Notifies client of server/map transition.", - "direction": "outgoing", - "isDynamic": false, - "size": 16, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x76", - "description": "Packet identifier" - }, - { - "name": "X", - "type": "short", - "size": 2, - "description": "New X coordinate" - }, - { - "name": "Y", - "type": "short", - "size": 2, - "description": "New Y coordinate" - }, - { - "name": "Z", - "type": "short", - "size": 2, - "description": "New Z coordinate" - }, - { - "name": "Unknown", - "type": "byte", - "size": 1, - "value": "0x00", - "description": "Unknown" - }, - { - "name": "Unknown2", - "type": "short", - "size": 2, - "value": "0x0000", - "description": "Unknown" - }, - { - "name": "Unknown3", - "type": "short", - "size": 2, - "value": "0x0000", - "description": "Unknown" - }, - { - "name": "Map Width", - "type": "short", - "size": 2, - "description": "Map width" - }, - { - "name": "Map Height", - "type": "short", - "size": 2, - "description": "Map height" - } - ], - "source": { - "file": "Projects/Server/Network/Packets/OutgoingPlayerPackets.cs", - "line": 100 - } - }, - { - "id": "0x7B", - "name": "Sequence", - "description": "Sends sequence number to client for synchronization.", - "direction": "outgoing", - "isDynamic": false, - "size": 2, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x7B", - "description": "Packet identifier" - }, - { - "name": "Sequence", - "type": "byte", - "size": 1, - "description": "Sequence number" - } - ], - "source": { - "file": "Projects/Server/Network/Packets/OutgoingPlayerPackets.cs", - "line": 154 - } - }, - { - "id": "0x88", - "name": "Display Paperdoll", - "description": "Opens paperdoll window for a mobile.", - "direction": "outgoing", - "isDynamic": false, - "size": 66, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x88", - "description": "Packet identifier" - }, - { - "name": "Serial", - "type": "uint", - "size": 4, - "description": "Serial of mobile" - }, - { - "name": "Title", - "type": "ascii", - "size": 60, - "description": "Title/name displayed" - }, - { - "name": "Flags", - "type": "bitfield", - "size": 1, - "description": "Paperdoll flags", - "flags": [ - { - "bit": "0", - "name": "Warmode", - "description": "Mobile is in war mode" - }, - { - "bit": "1", - "name": "Can Lift", - "description": "Viewer can lift equipment" - } - ] - } - ], - "source": { - "file": "Projects/Server/Network/Packets/OutgoingPlayerPackets.cs", - "line": 247 - } - }, - { - "id": "0x95", - "name": "Display Hue Picker", - "description": "Opens hue picker dialog on client.", - "direction": "outgoing", - "isDynamic": false, - "size": 9, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x95", - "description": "Packet identifier" - }, - { - "name": "Serial", - "type": "uint", - "size": 4, - "description": "Hue picker serial" - }, - { - "name": "Unknown", - "type": "short", - "size": 2, - "value": "0x0000", - "description": "Unknown" - }, - { - "name": "Item ID", - "type": "short", - "size": 2, - "description": "Item graphic to preview hue on" - } - ], - "source": { - "file": "Projects/Server/Network/Packets/OutgoingPlayerPackets.cs", - "line": 338 - } - }, - { - "id": "0xA5", - "name": "Launch Browser", - "description": "Opens a URL in client\u0027s web browser.", - "direction": "outgoing", - "isDynamic": true, - "size": "var", - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xA5", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "URL", - "type": "ascii-t", - "description": "URL to open" - } - ], - "source": { - "file": "Projects/Server/Network/Packets/OutgoingPlayerPackets.cs", - "line": 180 - } - }, - { - "id": "0xA6", - "name": "Scroll Message", - "description": "Displays a scrollable tips/message window.", - "direction": "outgoing", - "isDynamic": true, - "size": "var", - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xA6", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Type", - "type": "byte", - "size": 1, - "description": "Scroll type" - }, - { - "name": "Tip Number", - "type": "int", - "size": 4, - "description": "Tip/message number" - }, - { - "name": "Text Length", - "type": "ushort", - "size": 2, - "description": "Length of text" - }, - { - "name": "Text", - "type": "ascii", - "description": "Message text" - } - ], - "source": { - "file": "Projects/Server/Network/Packets/OutgoingPlayerPackets.cs", - "line": 291 - } - }, - { - "id": "0xB8", - "name": "Display Profile", - "description": "Displays a mobile\u0027s profile.", - "direction": "outgoing", - "isDynamic": true, - "size": "var", - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xB8", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Serial", - "type": "uint", - "size": 4, - "description": "Serial of mobile" - }, - { - "name": "Header", - "type": "ascii-t", - "description": "Profile header" - }, - { - "name": "Footer", - "type": "utf16be-t", - "description": "Profile footer" - }, - { - "name": "Body", - "type": "utf16be-t", - "description": "Profile body text" - } - ], - "source": { - "file": "Projects/Server/Network/Packets/OutgoingPlayerPackets.cs", - "line": 66 - } - }, - { - "id": "0xBC", - "name": "Season Change", - "description": "Changes the visual season on client.", - "direction": "outgoing", - "isDynamic": false, - "size": 3, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xBC", - "description": "Packet identifier" - }, - { - "name": "Season", - "type": "enum", - "enumType": "sequential", - "size": 1, - "description": "Season value", - "values": [ - { - "value": 0, - "name": "Spring", - "description": "Spring season" - }, - { - "value": 1, - "name": "Summer", - "description": "Summer season" - }, - { - "value": 2, - "name": "Fall", - "description": "Fall/Autumn season" - }, - { - "value": 3, - "name": "Winter", - "description": "Winter season" - }, - { - "value": 4, - "name": "Desolation", - "description": "Desolation (dead trees)" - } - ] - }, - { - "name": "Play Sound", - "type": "bool", - "size": 1, - "description": "Play season change sound" - } - ], - "source": { - "file": "Projects/Server/Network/Packets/OutgoingPlayerPackets.cs", - "line": 244 - } - }, - { - "id": "0xBF/0x19", - "name": "Extended Status", - "description": "Multi-purpose status packet. The Type byte determines the payload: Type 0 for pet bonded status, Type 2/5 for player stat locks.", - "direction": "outgoing", - "isDynamic": false, - "size": "Varies", - "fields": [], - "variants": [ - { - "name": "Bonded Status (Type 0)", - "condition": "Type = 0: Pet bonding status", - "size": 11, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xBF", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "value": "11", - "description": "Packet length" - }, - { - "name": "Sub-Command", - "type": "ushort", - "size": 2, - "value": "0x0019", - "description": "Extended Status subcommand" - }, - { - "name": "Type", - "type": "byte", - "size": 1, - "value": "0x00", - "description": "Type 0 = Bonded Status" - }, - { - "name": "Serial", - "type": "uint", - "size": 4, - "description": "Pet mobile serial" - }, - { - "name": "Bonded", - "type": "bool", - "size": 1, - "description": "True if pet is bonded to owner" - } - ], - "notes": "Sent when a pet's bonded status changes or when pet info is requested." - }, - { - "name": "Stat Lock Info - Classic (Type 2)", - "condition": "Type = 2: Classic Client stat locks", - "size": 12, - "clientVersion": { - "classic": {} - }, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xBF", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "value": "12", - "description": "Packet length" - }, - { - "name": "Sub-Command", - "type": "ushort", - "size": 2, - "value": "0x0019", - "description": "Extended Status subcommand" - }, - { - "name": "Type", - "type": "byte", - "size": 1, - "value": "0x02", - "description": "Type 2 = Stat Lock Info (Classic Client)" - }, - { - "name": "Serial", - "type": "uint", - "size": 4, - "description": "Player mobile serial" - }, - { - "name": "Unknown", - "type": "byte", - "size": 1, - "value": "0x00", - "description": "Unknown (always 0)" - }, - { - "name": "Lock Bits", - "type": "bitfield", - "size": 1, - "description": "Packed stat lock states (00SSDDII)", - "flags": [ - { - "bit": "5-4", - "name": "Str Lock", - "description": "Strength lock (value << 4)" - }, - { - "bit": "3-2", - "name": "Dex Lock", - "description": "Dexterity lock (value << 2)" - }, - { - "bit": "1-0", - "name": "Int Lock", - "description": "Intelligence lock (value << 0)" - } - ], - "values": [ - { - "value": 0, - "name": "Up", - "description": "Stat gains enabled" - }, - { - "value": 1, - "name": "Down", - "description": "Stat decreases enabled" - }, - { - "value": 2, - "name": "Locked", - "description": "Stat locked" - } - ], - "notes": "Formula: (StrLock << 4) | (DexLock << 2) | IntLock" - } - ] - }, - { - "name": "Stat Lock Info - Enhanced (Type 5)", - "condition": "Type = 5: Enhanced Client stat locks", - "size": 12, - "clientVersion": { - "enhanced": {} - }, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xBF", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "value": "12", - "description": "Packet length" - }, - { - "name": "Sub-Command", - "type": "ushort", - "size": 2, - "value": "0x0019", - "description": "Extended Status subcommand" - }, - { - "name": "Type", - "type": "byte", - "size": 1, - "value": "0x05", - "description": "Type 5 = Stat Lock Info (Enhanced Client)" - }, - { - "name": "Serial", - "type": "uint", - "size": 4, - "description": "Player mobile serial" - }, - { - "name": "Unknown", - "type": "byte", - "size": 1, - "value": "0x00", - "description": "Unknown (always 0)" - }, - { - "name": "Lock Bits", - "type": "bitfield", - "size": 1, - "description": "Packed stat lock states (00SSDDII)", - "flags": [ - { - "bit": "5-4", - "name": "Str Lock", - "description": "Strength lock (value << 4)" - }, - { - "bit": "3-2", - "name": "Dex Lock", - "description": "Dexterity lock (value << 2)" - }, - { - "bit": "1-0", - "name": "Int Lock", - "description": "Intelligence lock (value << 0)" - } - ], - "values": [ - { - "value": 0, - "name": "Up", - "description": "Stat gains enabled" - }, - { - "value": 1, - "name": "Down", - "description": "Stat decreases enabled" - }, - { - "value": 2, - "name": "Locked", - "description": "Stat locked" - } - ], - "notes": "Formula: (StrLock << 4) | (DexLock << 2) | IntLock" - } - ], - "notes": "Enhanced Client uses Type 5 instead of Type 2" - } - ], - "tags": ["Player", "Mobile"], - "notes": "The Type byte after the sub-command determines the packet structure. Type 0 is for pet bonding, Types 2/5 are for player stat locks (Classic/Enhanced Client respectively).", - "source": { - "file": "Projects/Server/Network/Packets/OutgoingPlayerPackets.cs", - "line": 36 - } - }, - { - "id": "0xC8", - "name": "Change Update Range", - "description": "Sets the client\u0027s update range.", - "direction": "outgoing", - "isDynamic": false, - "size": 2, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xC8", - "description": "Packet identifier" - }, - { - "name": "Range", - "type": "byte", - "size": 1, - "description": "Update range (typically 18)" - } - ], - "source": { - "file": "Projects/Server/Network/Packets/OutgoingPlayerPackets.cs", - "line": 59 - } - }, - { - "id": "0xD1", - "name": "Logout Ack", - "description": "Server acknowledges logout request.", - "direction": "outgoing", - "isDynamic": false, - "size": 2, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xD1", - "description": "Packet identifier" - }, - { - "name": "Ack", - "type": "byte", - "size": 1, - "value": "0x01", - "description": "Acknowledgment (1 = OK)" - } - ], - "source": { - "file": "Projects/Server/Network/Packets/OutgoingPlayerPackets.cs", - "line": 94 - } - } - ] -} \ No newline at end of file diff --git a/website/packets/outgoing/securetrade.json b/website/packets/outgoing/securetrade.json deleted file mode 100644 index 46d046f2a..000000000 --- a/website/packets/outgoing/securetrade.json +++ /dev/null @@ -1,275 +0,0 @@ -{ - "category": "Secure Trade", - "packets": [ - { - "id": "0x6F", - "name": "Secure Trade", - "description": "Manages secure trade windows between players.", - "direction": "outgoing", - "isDynamic": true, - "size": "Varies", - "variants": [ - { - "name": "Display Trade Window", - "condition": "flag == Display (0)", - "size": 47, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x6F", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "value": "47", - "description": "Packet length" - }, - { - "name": "Flag", - "type": "enum", - "enumType": "sequential", - "size": 1, - "value": "0", - "description": "Trade action flag", - "values": [ - { - "value": 0, - "name": "Display", - "description": "Open trade window" - }, - { - "value": 1, - "name": "Close", - "description": "Close trade window" - }, - { - "value": 2, - "name": "Update", - "description": "Update accept status" - }, - { - "value": 3, - "name": "Update Gold", - "description": "Update gold amounts" - }, - { - "value": 4, - "name": "Update Ledger", - "description": "Update ledger" - } - ] - }, - { - "name": "Partner Serial", - "type": "uint", - "size": 4, - "description": "Serial of trading partner" - }, - { - "name": "First Container Serial", - "type": "uint", - "size": 4, - "description": "Serial of your trade container" - }, - { - "name": "Second Container Serial", - "type": "uint", - "size": 4, - "description": "Serial of partner\u0027s trade container" - }, - { - "name": "Has Name", - "type": "bool", - "size": 1, - "value": "true", - "description": "Name field follows" - }, - { - "name": "Partner Name", - "type": "ascii", - "size": 30, - "description": "Trading partner\u0027s name" - } - ] - }, - { - "name": "Close Trade Window", - "condition": "flag == Close (1)", - "size": 17, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x6F", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "value": "17", - "description": "Packet length" - }, - { - "name": "Flag", - "type": "byte", - "size": 1, - "value": "1", - "description": "Close flag" - }, - { - "name": "Container Serial", - "type": "uint", - "size": 4, - "description": "Trade container serial" - }, - { - "name": "Unused1", - "type": "int", - "size": 4, - "value": "0", - "description": "Unused" - }, - { - "name": "Unused2", - "type": "int", - "size": 4, - "value": "0", - "description": "Unused" - }, - { - "name": "Has Name", - "type": "bool", - "size": 1, - "value": "false", - "description": "No name follows" - } - ] - }, - { - "name": "Update Accept Status", - "condition": "flag == Update (2)", - "size": 17, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x6F", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "value": "17", - "description": "Packet length" - }, - { - "name": "Flag", - "type": "byte", - "size": 1, - "value": "2", - "description": "Update flag" - }, - { - "name": "Container Serial", - "type": "uint", - "size": 4, - "description": "Trade container serial" - }, - { - "name": "Your Accept", - "type": "int", - "size": 4, - "description": "Your accept status (0 or 1)" - }, - { - "name": "Partner Accept", - "type": "int", - "size": 4, - "description": "Partner\u0027s accept status (0 or 1)" - }, - { - "name": "Has Name", - "type": "bool", - "size": 1, - "value": "false", - "description": "No name follows" - } - ] - }, - { - "name": "Update Gold/Platinum", - "condition": "flag == UpdateGold (3)", - "size": 17, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x6F", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "value": "17", - "description": "Packet length" - }, - { - "name": "Flag", - "type": "byte", - "size": 1, - "value": "3", - "description": "UpdateGold flag" - }, - { - "name": "Container Serial", - "type": "uint", - "size": 4, - "description": "Trade container serial" - }, - { - "name": "Gold", - "type": "int", - "size": 4, - "description": "Gold amount" - }, - { - "name": "Platinum", - "type": "int", - "size": 4, - "description": "Platinum amount" - }, - { - "name": "Has Name", - "type": "bool", - "size": 1, - "value": "false", - "description": "No name follows" - } - ] - } - ], - "related": [ - { - "id": "0x6F", - "direction": "incoming", - "relationship": "request", - "note": "Client trade actions" - } - ], - "source": { - "file": "Projects/Server/Network/Packets/OutgoingSecureTradePackets.cs", - "line": 32 - } - } - ] -} \ No newline at end of file diff --git a/website/packets/outgoing/targeting.json b/website/packets/outgoing/targeting.json deleted file mode 100644 index c6bbabb5a..000000000 --- a/website/packets/outgoing/targeting.json +++ /dev/null @@ -1,223 +0,0 @@ -{ - "category": "Targeting", - "packets": [ - { - "id": "0x6C", - "name": "Target Request", - "description": "Requests the client to select a target.", - "direction": "outgoing", - "isDynamic": false, - "size": 19, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x6C", - "description": "Packet identifier" - }, - { - "name": "Allow Ground", - "type": "bool", - "size": 1, - "description": "True = allow ground targeting" - }, - { - "name": "Target ID", - "type": "int", - "size": 4, - "description": "Target cursor ID for matching response" - }, - { - "name": "Flags", - "type": "enum", - "enumType": "sequential", - "size": 1, - "description": "Target cursor flags", - "values": [ - { - "value": 0, - "name": "Neutral", - "description": "Neutral targeting (gray cursor)" - }, - { - "value": 1, - "name": "Harmful", - "description": "Harmful action (red cursor)" - }, - { - "value": 2, - "name": "Beneficial", - "description": "Beneficial action (green cursor)" - }, - { - "value": 3, - "name": "Cancel", - "description": "Cancel targeting" - } - ] - }, - { - "name": "Padding", - "type": "byte[]", - "size": 12, - "description": "Padding (zeros)" - } - ], - "source": { - "file": "Projects/Server/Network/Packets/OutgoingTargetPackets.cs", - "line": 57 - } - }, - { - "id": "0x99", - "name": "Multi Target Request", - "description": "Requests the client to place a multi (house/boat) structure.", - "direction": "outgoing", - "isDynamic": false, - "size": "Varies", - "variants": [ - { - "version": "pre-HighSeas", - "name": "Pre-HighSeas (26 bytes)", - "size": 26, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x99", - "description": "Packet identifier" - }, - { - "name": "Allow Ground", - "type": "bool", - "size": 1, - "description": "Allow ground targeting" - }, - { - "name": "Target ID", - "type": "int", - "size": 4, - "description": "Target cursor ID" - }, - { - "name": "Flags", - "type": "byte", - "size": 1, - "description": "Target cursor flags" - }, - { - "name": "Padding", - "type": "byte[]", - "size": 11, - "description": "Padding (zeros)" - }, - { - "name": "Multi ID", - "type": "short", - "size": 2, - "description": "Multi graphic ID" - }, - { - "name": "Offset X", - "type": "short", - "size": 2, - "description": "X offset for placement" - }, - { - "name": "Offset Y", - "type": "short", - "size": 2, - "description": "Y offset for placement" - }, - { - "name": "Offset Z", - "type": "short", - "size": 2, - "description": "Z offset for placement" - } - ] - }, - { - "version": "HighSeas+", - "name": "HighSeas / Enhanced (30 bytes)", - "condition": "Classic >= 7.0.9.0, or Enhanced Client (any version)", - "size": 30, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x99", - "description": "Packet identifier" - }, - { - "name": "Allow Ground", - "type": "bool", - "size": 1, - "description": "Allow ground targeting" - }, - { - "name": "Target ID", - "type": "int", - "size": 4, - "description": "Target cursor ID" - }, - { - "name": "Flags", - "type": "byte", - "size": 1, - "description": "Target cursor flags" - }, - { - "name": "Padding", - "type": "byte[]", - "size": 11, - "description": "Padding (zeros)" - }, - { - "name": "Multi ID", - "type": "short", - "size": 2, - "description": "Multi graphic ID" - }, - { - "name": "Offset X", - "type": "short", - "size": 2, - "description": "X offset for placement" - }, - { - "name": "Offset Y", - "type": "short", - "size": 2, - "description": "Y offset for placement" - }, - { - "name": "Offset Z", - "type": "short", - "size": 2, - "description": "Z offset for placement" - }, - { - "name": "Unknown", - "type": "int", - "size": 4, - "description": "Unknown (HighSeas extension)" - } - ] - } - ], - "clientVersion": { - "classic": {}, - "enhanced": {}, - "notes": "HighSeas (7.0.9.0+) adds 4 bytes. EC always uses 30-byte variant." - }, - "source": { - "file": "Projects/Server/Network/Packets/OutgoingTargetPackets.cs", - "line": 23 - } - } - ] -} \ No newline at end of file diff --git a/website/packets/outgoing/vendor.json b/website/packets/outgoing/vendor.json deleted file mode 100644 index 5c55b7f0e..000000000 --- a/website/packets/outgoing/vendor.json +++ /dev/null @@ -1,216 +0,0 @@ -{ - "category": "Vendor", - "packets": [ - { - "id": "0x3B", - "name": "End Vendor Transaction", - "description": "Ends a vendor buy or sell transaction.", - "direction": "outgoing", - "isDynamic": false, - "size": 8, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x3B", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "value": "8", - "description": "Packet length" - }, - { - "name": "Vendor Serial", - "type": "uint", - "size": 4, - "description": "Serial of the vendor" - }, - { - "name": "Item Count", - "type": "byte", - "size": 1, - "value": "0", - "description": "Item count (0 = end transaction)" - } - ], - "source": { - "file": "Projects/Server/Network/Packets/OutgoingVendorBuyPackets.cs", - "line": 111 - } - }, - { - "id": "0x74", - "name": "Vendor Buy List", - "description": "Sends the list of items available for purchase from a vendor with prices.", - "direction": "outgoing", - "isDynamic": true, - "size": "8+", - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x74", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Container Serial", - "type": "uint", - "size": 4, - "description": "Serial of vendor\u0027s buy container" - }, - { - "name": "Item Count", - "type": "byte", - "size": 1, - "description": "Number of items" - }, - { - "name": "Items", - "type": "loop", - "description": "Buy items with prices", - "loop": { - "countField": "itemCount", - "fields": [ - { - "name": "Price", - "type": "int", - "size": 4, - "description": "Item price" - }, - { - "name": "Desc Length", - "type": "byte", - "size": 1, - "description": "Description length + 1" - }, - { - "name": "Description", - "type": "ascii-t", - "description": "Item description" - } - ] - } - } - ], - "related": [ - { - "id": "0x3C", - "relationship": "request", - "note": "Container Content with items" - } - ], - "source": { - "file": "Projects/Server/Network/Packets/OutgoingVendorBuyPackets.cs", - "line": 77 - } - }, - { - "id": "0x9E", - "name": "Vendor Sell List", - "description": "Sends the list of items the vendor will buy from the player.", - "direction": "outgoing", - "isDynamic": true, - "size": "9+", - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0x9E", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "description": "Packet length" - }, - { - "name": "Vendor Serial", - "type": "uint", - "size": 4, - "description": "Serial of the vendor" - }, - { - "name": "Item Count", - "type": "ushort", - "size": 2, - "description": "Number of items" - }, - { - "name": "Items", - "type": "loop", - "description": "Sell items with prices", - "loop": { - "countField": "itemCount", - "fields": [ - { - "name": "Item Serial", - "type": "uint", - "size": 4, - "description": "Item serial" - }, - { - "name": "Item ID", - "type": "ushort", - "size": 2, - "description": "Item graphic ID" - }, - { - "name": "Hue", - "type": "ushort", - "size": 2, - "description": "Item hue" - }, - { - "name": "Amount", - "type": "ushort", - "size": 2, - "description": "Item amount" - }, - { - "name": "Price", - "type": "ushort", - "size": 2, - "description": "Price per unit" - }, - { - "name": "Name Length", - "type": "ushort", - "size": 2, - "description": "Name length" - }, - { - "name": "Name", - "type": "ascii", - "description": "Item name" - } - ] - } - } - ], - "related": [ - { - "id": "0x9F", - "relationship": "response", - "note": "Vendor Sell Reply from client" - } - ], - "source": { - "file": "Projects/Server/Network/Packets/OutgoingVendorSellPackets.cs", - "line": 25 - } - } - ] -} \ No newline at end of file diff --git a/website/packets/outgoing/weaponability.json b/website/packets/outgoing/weaponability.json deleted file mode 100644 index 79f3ce907..000000000 --- a/website/packets/outgoing/weaponability.json +++ /dev/null @@ -1,89 +0,0 @@ -{ - "category": "Weapon Ability", - "packets": [ - { - "id": "0xBF", - "subId": "0x21", - "name": "Clear Weapon Ability", - "description": "Clears the currently selected weapon ability.", - "direction": "outgoing", - "isDynamic": false, - "size": 5, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xBF", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "value": "5", - "description": "Packet length" - }, - { - "name": "Sub-Command", - "type": "ushort", - "size": 2, - "value": "0x0021", - "description": "Clear Weapon Ability subcommand" - } - ], - "source": { - "file": "Projects/UOContent/Items/Weapons/Abilities/WeaponAbilityPackets.cs", - "line": 37 - } - }, - { - "id": "0xBF", - "subId": "0x25", - "name": "Toggle Special Ability", - "description": "Toggles a special weapon ability on or off.", - "direction": "outgoing", - "isDynamic": false, - "size": 8, - "fields": [ - { - "name": "Packet ID", - "type": "byte", - "size": 1, - "value": "0xBF", - "description": "Packet identifier" - }, - { - "name": "Length", - "type": "ushort", - "size": 2, - "value": "8", - "description": "Packet length" - }, - { - "name": "Sub-Command", - "type": "ushort", - "size": 2, - "value": "0x0025", - "description": "Toggle Special Ability subcommand" - }, - { - "name": "Ability ID", - "type": "short", - "size": 2, - "description": "Ability ID" - }, - { - "name": "Active", - "type": "bool", - "size": 1, - "description": "True if ability is active" - } - ], - "source": { - "file": "Projects/UOContent/Items/Weapons/Abilities/WeaponAbilityPackets.cs", - "line": 40 - } - } - ] -} diff --git a/website/packets/template.html b/website/packets/template.html deleted file mode 100644 index a97b51649..000000000 --- a/website/packets/template.html +++ /dev/null @@ -1,1910 +0,0 @@ - - - - - - - - - ModernUO Packet Documentation - - - - - - - -
- -
-
-
- -
-

- Packet Documentation - -

- -
-
-
- - -
-
-
- - -
- - - - - - - -
-
- - - - -
-
📦
-

Select a Packet

-

Choose a packet from the list to view its documentation.

-

Use the search box to find packets by ID (0x02), name, or field names.

-
- - - -
-
-
-
- - - - - - - - - - diff --git a/website/sidebars.ts b/website/sidebars.ts deleted file mode 100644 index b549440a1..000000000 --- a/website/sidebars.ts +++ /dev/null @@ -1,50 +0,0 @@ -import type { SidebarsConfig } from '@docusaurus/plugin-content-docs'; - -const sidebars: SidebarsConfig = { - docsSidebar: [ - { - type: 'category', - label: 'Get Started', - collapsed: false, - items: [ - 'getting-started/installation', - 'getting-started/building', - 'getting-started/starting', - 'getting-started/configuration', - ], - }, - { - type: 'category', - label: 'Content Development', - collapsed: false, - items: [ - 'development/items-and-mobiles', - 'development/serialization', - 'development/timers', - 'development/commands-and-targeting', - 'development/era-and-expansions', - ], - }, - { - type: 'category', - label: 'Reference', - collapsed: false, - items: [ - { - type: 'link', - label: 'Commands', - href: 'pathname:///commands.html', - className: 'menu__link--internal', - }, - { - type: 'link', - label: 'Packets', - href: 'pathname:///packets.html', - className: 'menu__link--internal', - }, - ], - }, - ], -}; - -export default sidebars; diff --git a/website/src/components/HomepageFeatures/index.tsx b/website/src/components/HomepageFeatures/index.tsx deleted file mode 100644 index 8240b295d..000000000 --- a/website/src/components/HomepageFeatures/index.tsx +++ /dev/null @@ -1,82 +0,0 @@ -import type { ReactNode } from 'react'; -import Heading from '@theme/Heading'; -import styles from './styles.module.css'; - -type FeatureItem = { - title: string; - icon: string; - description: ReactNode; -}; - -const FeatureList: FeatureItem[] = [ - { - title: 'Modern .NET Platform', - icon: '\u26A1', - description: ( - <> - Built on the latest .NET with native Linux support and cross-platform - compatibility out of the box. OS-level networking and minimal memory - overhead keep your shard lean and responsive. - - ), - }, - { - title: 'Code-Generated Serialization', - icon: '\uD83D\uDD27', - description: ( - <> - Automatic persistence powered by C# source generators. Annotate your - fields and get version migrations, dirty tracking, and zero-boilerplate - world saves—no manual serialization code required. - - ), - }, - { - title: 'Active Development & Community', - icon: '\uD83D\uDC65', - description: ( - <> - Actively maintained with regular updates and a growing contributor base. - Get help on Discord, collaborate on GitHub, and be part of a community - that’s pushing UO emulation forward. - - ), - }, - { - title: 'Built for Performance at Scale', - icon: '\uD83D\uDE80', - description: ( - <> - Engineered for large shards. Optimized data structures, a lock-free - architecture, and parallel world saves ensure your server stays smooth - under heavy player load. - - ), - }, -]; - -function Feature({ title, icon, description }: FeatureItem) { - return ( -
-
- {icon} - {title} -
-

{description}

-
- ); -} - -export default function HomepageFeatures(): ReactNode { - return ( -
-
-
- {FeatureList.map((props, idx) => ( - - ))} -
-
-
- ); -} diff --git a/website/src/components/HomepageFeatures/styles.module.css b/website/src/components/HomepageFeatures/styles.module.css deleted file mode 100644 index 3aca163b3..000000000 --- a/website/src/components/HomepageFeatures/styles.module.css +++ /dev/null @@ -1,80 +0,0 @@ -.features { - padding: 3rem 0; -} - -.grid { - display: grid; - grid-template-columns: repeat(4, 1fr); - gap: 1rem; -} - -@media screen and (max-width: 996px) { - .grid { - grid-template-columns: repeat(2, 1fr); - } -} - -@media screen and (max-width: 576px) { - .grid { - grid-template-columns: 1fr; - } -} - -.featureCard { - background: var(--ifm-background-surface-color); - border: 1px solid rgba(213, 191, 116, 0.15); - border-radius: 8px; - padding: 1.5rem; - transition: border-color 0.2s ease, transform 0.2s ease; -} - -.featureCard:hover { - border-color: rgba(213, 191, 116, 0.4); - transform: translateY(-2px); -} - -/* Desktop: icon centered on its own line */ -.featureHeader { - display: flex; - flex-direction: column; - align-items: center; - text-align: center; - gap: 0.4rem; - margin-bottom: 0.5rem; -} - -.featureIcon { - font-size: 1.6rem; - flex-shrink: 0; -} - -.featureCard h3 { - color: var(--ifm-color-primary); - margin: 0; - font-size: 1.05rem; -} - -.featureCard p { - font-size: 0.95rem; - line-height: 1.5; - margin: 0; - text-align: center; -} - -/* Tablet/mobile: icon inline next to title, left-aligned */ -@media screen and (max-width: 996px) { - .featureHeader { - flex-direction: row; - align-items: center; - text-align: left; - gap: 0.5rem; - } - - .featureIcon { - font-size: 1.4rem; - } - - .featureCard p { - text-align: left; - } -} diff --git a/website/src/components/OsTabs/index.tsx b/website/src/components/OsTabs/index.tsx deleted file mode 100644 index 98dca6193..000000000 --- a/website/src/components/OsTabs/index.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import type { ReactNode } from 'react'; -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -function detectOs(): string { - if (typeof navigator === 'undefined') { - return 'linux'; - } - - const platform = (navigator.platform ?? '').toLowerCase(); - const ua = navigator.userAgent.toLowerCase(); - - if (platform.startsWith('win') || ua.includes('windows')) { - return 'windows'; - } - if (platform.startsWith('mac') || ua.includes('macintosh')) { - return 'macos'; - } - return 'linux'; -} - -type OsTabsProps = { - children: { - windows?: ReactNode; - macos?: ReactNode; - linux?: ReactNode; - }; -}; - -export default function OsTabs({ children }: OsTabsProps): ReactNode { - const defaultOs = detectOs(); - - return ( - - {children.windows && ( - - {children.windows} - - )} - {children.macos && ( - - {children.macos} - - )} - {children.linux && ( - - {children.linux} - - )} - - ); -} diff --git a/website/src/components/QuickStart/index.tsx b/website/src/components/QuickStart/index.tsx deleted file mode 100644 index 6da3fbe7d..000000000 --- a/website/src/components/QuickStart/index.tsx +++ /dev/null @@ -1,47 +0,0 @@ -import type { ReactNode } from 'react'; -import CodeBlock from '@theme/CodeBlock'; -import Link from '@docusaurus/Link'; -import OsTabs from '@site/src/components/OsTabs'; -import styles from './styles.module.css'; - -export default function QuickStart(): ReactNode { - return ( -
-
-
-

Up and running in minutes

-

- Clone, build, and launch your server with three commands. -

-
- - {{ - linux: ( - - {`git clone https://github.com/modernuo/modernuo\ncd modernuo\n./publish.sh release linux x64`} - - ), - macos: ( - - {`git clone https://github.com/modernuo/modernuo\ncd modernuo\n./publish.sh release osx x64`} - - ), - windows: ( - - {`git clone https://github.com/modernuo/modernuo\ncd modernuo\n./publish.cmd release win x64`} - - ), - }} - -
- - View full setup guide - -
-
-
- ); -} diff --git a/website/src/components/QuickStart/styles.module.css b/website/src/components/QuickStart/styles.module.css deleted file mode 100644 index 43f6c86a9..000000000 --- a/website/src/components/QuickStart/styles.module.css +++ /dev/null @@ -1,27 +0,0 @@ -.quickStart { - padding: 4rem 0; - border-top: 1px solid rgba(213, 191, 116, 0.1); -} - -.content { - text-align: center; - max-width: 640px; - margin: 0 auto; -} - -.heading { - color: var(--ifm-color-primary); - font-size: 1.75rem; - margin-bottom: 0.5rem; -} - -.subheading { - font-size: 1.1rem; - opacity: 0.8; - margin-bottom: 1.5rem; -} - -.codeWrapper { - text-align: left; - margin-bottom: 1.5rem; -} diff --git a/website/src/css/custom.css b/website/src/css/custom.css deleted file mode 100644 index bf8b6c16e..000000000 --- a/website/src/css/custom.css +++ /dev/null @@ -1,97 +0,0 @@ -/* ModernUO Gold Theme — dark mode only */ - -:root { - --muo-gold: #d5bf74; - --muo-gold-dark: #b09f4f; - --muo-gold-light: #e8d89e; - --muo-green: #77d574; - --muo-blue: #748ad5; - --muo-purple: #bf74d5; - - --ifm-font-family-base: 'Inter', system-ui, -apple-system, sans-serif; - --ifm-heading-font-family: 'Inter', system-ui, -apple-system, sans-serif; - - --ifm-color-primary: #d5bf74; - --ifm-color-primary-dark: #cdb45e; - --ifm-color-primary-darker: #c9ae53; - --ifm-color-primary-darkest: #b09635; - --ifm-color-primary-light: #ddca8a; - --ifm-color-primary-lighter: #e1d095; - --ifm-color-primary-lightest: #ede0b6; - --ifm-background-color: #1a1a1a; - --ifm-background-surface-color: #212121; - --ifm-code-font-size: 95%; - --docusaurus-highlighted-code-line-bg: rgba(213, 191, 116, 0.15); -} - -/* Navbar */ -.navbar { - background-color: #212121; - border-bottom: 1px solid rgba(213, 191, 116, 0.2); -} - -.navbar__title { - color: #d5bf74; -} - -/* Hide external link icon on internal static pages */ -.navbar__link--internal svg[class*="iconExternalLink"], -.navbar__link--internal svg[class*="external"], -.navbar__link--internal::after, -.menu__link--internal svg[class*="iconExternalLink"], -.menu__link--internal svg[class*="external"], -.menu__link--internal::after { - display: none !important; -} - -/* Navbar icon links */ -.navbar-icon { - display: inline-flex; - align-items: center; - padding: 0.25rem; - color: rgba(255, 255, 255, 0.35); - transition: color 0.15s ease; -} - -.navbar-icon:hover { - color: #d5bf74; -} - -.navbar-icon--sponsor { - color: rgba(219, 68, 85, 0.45); -} - -.navbar-icon--sponsor:hover { - color: #db4455; -} - -/* Sidebar */ -.menu__link--active:not(.menu__link--sublist) { - color: #d5bf74; -} - -/* Button overrides for landing page */ -.button--gold { - background-color: #d5bf74; - color: #212121; - border: none; - font-weight: 600; -} - -.button--gold:hover { - background-color: #e8d89e; - color: #212121; -} - -.button--outline-gold { - background: transparent; - color: #d5bf74; - border: 1px solid rgba(213, 191, 116, 0.4); - font-weight: 500; -} - -.button--outline-gold:hover { - background-color: rgba(213, 191, 116, 0.08); - border-color: rgba(213, 191, 116, 0.6); - color: #d5bf74; -} diff --git a/website/src/pages/index.module.css b/website/src/pages/index.module.css deleted file mode 100644 index 797206b1c..000000000 --- a/website/src/pages/index.module.css +++ /dev/null @@ -1,71 +0,0 @@ -.hero { - background-color: #1a1a1a; - padding: 6rem 0 5rem; - text-align: center; -} - -.logoWrapper { - position: relative; - display: inline-block; - margin-bottom: 2rem; -} - -.glow { - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - width: 280px; - height: 280px; - border-radius: 50%; - background: radial-gradient( - circle, - rgba(213, 191, 116, 0.12) 0%, - rgba(213, 191, 116, 0.04) 40%, - transparent 70% - ); - pointer-events: none; -} - -.logo { - position: relative; - width: 180px; - height: 180px; -} - -.tagline { - color: #e8cea1; - font-size: 1.6rem; - font-weight: 300; - max-width: 560px; - margin: 0 auto 2.5rem; - line-height: 1.4; - letter-spacing: -0.01em; -} - -.buttons { - display: flex; - align-items: center; - justify-content: center; - gap: 1rem; -} - -@media screen and (max-width: 996px) { - .hero { - padding: 4rem 1rem 3rem; - } - - .logo { - width: 140px; - height: 140px; - } - - .glow { - width: 220px; - height: 220px; - } - - .tagline { - font-size: 1.3rem; - } -} diff --git a/website/src/pages/index.tsx b/website/src/pages/index.tsx deleted file mode 100644 index b34572af9..000000000 --- a/website/src/pages/index.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import type { ReactNode } from 'react'; -import Link from '@docusaurus/Link'; -import Layout from '@theme/Layout'; -import HomepageFeatures from '@site/src/components/HomepageFeatures'; -import QuickStart from '@site/src/components/QuickStart'; - -import styles from './index.module.css'; - -function HomepageHeader() { - return ( -
-
-
-
- ModernUO -
-

- The Ultima Online server emulator for the modern era -

-
- - Get Started - -
-
-
- ); -} - -export default function Home(): ReactNode { - return ( - - -
- - -
-
- ); -} diff --git a/website/src/theme/Footer/index.tsx b/website/src/theme/Footer/index.tsx deleted file mode 100644 index 10b1c6eef..000000000 --- a/website/src/theme/Footer/index.tsx +++ /dev/null @@ -1,161 +0,0 @@ -import { type ReactNode, useEffect, useState } from 'react'; -import Link from '@docusaurus/Link'; -import styles from './styles.module.css'; - -function formatCount(n: number): string { - if (n >= 1000) { - return `${(n / 1000).toFixed(1).replace(/\.0$/, '')}k`; - } - return n.toString(); -} - -type Counts = { - stars?: number; - discord?: number; -}; - -function useSocialCounts(): Counts { - const [counts, setCounts] = useState({}); - - useEffect(() => { - let cancelled = false; - - async function fetchCounts() { - const results: Counts = {}; - - try { - const res = await fetch('https://api.github.com/repos/modernuo/ModernUO'); - if (res.ok) { - const data = await res.json(); - results.stars = data.stargazers_count; - } - } catch { /* silent */ } - - try { - const res = await fetch('https://discord.com/api/v9/invites/DHkNUsq?with_counts=true'); - if (res.ok) { - const data = await res.json(); - results.discord = data.approximate_member_count; - } - } catch { /* silent */ } - - if (!cancelled) { - setCounts(results); - } - } - - fetchCounts(); - return () => { cancelled = true; }; - }, []); - - return counts; -} - -type SocialItem = { - label: string; - href: string; - icon: ReactNode; - countKey?: keyof Counts; -}; - -const socialLinks: SocialItem[] = [ - { - label: 'GitHub', - href: 'https://github.com/modernuo/ModernUO', - countKey: 'stars', - icon: ( - - - - ), - }, - { - label: 'Discord', - href: 'https://muo.gg/discord', - countKey: 'discord', - icon: ( - - - - ), - }, - { - label: 'Reddit', - href: 'https://muo.gg/reddit', - icon: ( - - - - ), - }, - { - label: 'Sponsor', - href: 'https://github.com/sponsors/modernuo', - icon: ( - - - - ), - }, -]; - -const docLinks = [ - { label: 'Get Started', to: '/docs/getting-started/installation' }, - { label: 'Configuration', to: '/docs/getting-started/configuration' }, - { label: 'Content Development', to: '/docs/development/items-and-mobiles' }, - { label: 'Commands', href: '/commands.html' }, - { label: 'Packets', href: '/packets.html' }, -]; - -export default function Footer(): ReactNode { - const counts = useSocialCounts(); - - return ( - - ); -} diff --git a/website/src/theme/Footer/styles.module.css b/website/src/theme/Footer/styles.module.css deleted file mode 100644 index bbd1666a1..000000000 --- a/website/src/theme/Footer/styles.module.css +++ /dev/null @@ -1,105 +0,0 @@ -.footer { - background-color: #1a1a1a; - border-top: 1px solid rgba(213, 191, 116, 0.15); - padding: 2.5rem 0 1.5rem; -} - -.content { - display: flex; - justify-content: space-between; - gap: 3rem; -} - -@media screen and (max-width: 768px) { - .content { - flex-direction: column; - gap: 2rem; - } -} - -.heading { - color: rgba(213, 191, 116, 0.5); - font-size: 0.75rem; - font-weight: 600; - text-transform: uppercase; - letter-spacing: 0.05em; - margin: 0 0 0.75rem; -} - -/* Documentation links */ -.docs { - flex-shrink: 0; -} - -.linkList { - list-style: none; - margin: 0; - padding: 0; - display: flex; - flex-wrap: wrap; - gap: 0.25rem 1.25rem; -} - -.docLink { - color: rgba(255, 255, 255, 0.6); - font-size: 0.875rem; - text-decoration: none; - transition: color 0.15s ease; -} - -.docLink:hover { - color: #d5bf74; - text-decoration: none; -} - -/* Social links */ -.social { - flex-shrink: 0; -} - -.socialButtons { - display: flex; - flex-wrap: wrap; - gap: 0.5rem; -} - -.socialLink { - display: inline-flex; - align-items: center; - gap: 0.4rem; - padding: 0.4rem 0.75rem; - border-radius: 6px; - border: 1px solid rgba(213, 191, 116, 0.15); - color: rgba(213, 191, 116, 0.7); - font-size: 0.8rem; - font-weight: 500; - text-decoration: none; - transition: color 0.15s ease, border-color 0.15s ease; -} - -.socialLink:hover { - color: #d5bf74; - border-color: rgba(213, 191, 116, 0.4); - text-decoration: none; -} - -.icon { - display: inline-flex; - align-items: center; -} - -.count { - opacity: 0.5; - font-size: 0.75rem; - font-variant-numeric: tabular-nums; -} - -/* Copyright */ -.bottom { - margin-top: 2rem; - padding-top: 1rem; - border-top: 1px solid rgba(213, 191, 116, 0.08); - text-align: center; - color: rgba(255, 255, 255, 0.3); - font-size: 0.8rem; -} diff --git a/website/static/.nojekyll b/website/static/.nojekyll deleted file mode 100644 index e69de29bb..000000000 diff --git a/website/static/CNAME b/website/static/CNAME deleted file mode 100644 index 9716c910a..000000000 --- a/website/static/CNAME +++ /dev/null @@ -1 +0,0 @@ -modernuo.com diff --git a/website/static/branding/android-chrome-192x192.png b/website/static/branding/android-chrome-192x192.png deleted file mode 100644 index 177604a9e..000000000 Binary files a/website/static/branding/android-chrome-192x192.png and /dev/null differ diff --git a/website/static/branding/android-chrome-512x512.png b/website/static/branding/android-chrome-512x512.png deleted file mode 100644 index 0fda32a43..000000000 Binary files a/website/static/branding/android-chrome-512x512.png and /dev/null differ diff --git a/website/static/branding/apple-touch-icon.png b/website/static/branding/apple-touch-icon.png deleted file mode 100644 index c6ce40fcd..000000000 Binary files a/website/static/branding/apple-touch-icon.png and /dev/null differ diff --git a/website/static/branding/favicon-16x16.png b/website/static/branding/favicon-16x16.png deleted file mode 100644 index 45b6741aa..000000000 Binary files a/website/static/branding/favicon-16x16.png and /dev/null differ diff --git a/website/static/branding/favicon-32x32.png b/website/static/branding/favicon-32x32.png deleted file mode 100644 index 023b48d9a..000000000 Binary files a/website/static/branding/favicon-32x32.png and /dev/null differ diff --git a/website/static/branding/favicon.ico b/website/static/branding/favicon.ico deleted file mode 100644 index 10c47ee1c..000000000 Binary files a/website/static/branding/favicon.ico and /dev/null differ diff --git a/website/static/branding/favicon.png b/website/static/branding/favicon.png deleted file mode 100644 index 023b48d9a..000000000 Binary files a/website/static/branding/favicon.png and /dev/null differ diff --git a/website/static/branding/logo.svg b/website/static/branding/logo.svg deleted file mode 100644 index 5583999ad..000000000 --- a/website/static/branding/logo.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/website/static/branding/mstile-144x144.png b/website/static/branding/mstile-144x144.png deleted file mode 100644 index a03597d2e..000000000 Binary files a/website/static/branding/mstile-144x144.png and /dev/null differ diff --git a/website/static/branding/mstile-150x150.png b/website/static/branding/mstile-150x150.png deleted file mode 100644 index f5ccc900e..000000000 Binary files a/website/static/branding/mstile-150x150.png and /dev/null differ diff --git a/website/static/branding/mstile-310x150.png b/website/static/branding/mstile-310x150.png deleted file mode 100644 index 6196557f9..000000000 Binary files a/website/static/branding/mstile-310x150.png and /dev/null differ diff --git a/website/static/branding/mstile-310x310.png b/website/static/branding/mstile-310x310.png deleted file mode 100644 index fcab2046c..000000000 Binary files a/website/static/branding/mstile-310x310.png and /dev/null differ diff --git a/website/static/branding/mstile-70x70.png b/website/static/branding/mstile-70x70.png deleted file mode 100644 index 2416cf18d..000000000 Binary files a/website/static/branding/mstile-70x70.png and /dev/null differ diff --git a/website/static/branding/safari-pinned-tab.svg b/website/static/branding/safari-pinned-tab.svg deleted file mode 100644 index 55d1c1fc7..000000000 --- a/website/static/branding/safari-pinned-tab.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/website/static/commands.html b/website/static/commands.html deleted file mode 100644 index cce8ae7ce..000000000 --- a/website/static/commands.html +++ /dev/null @@ -1,2619 +0,0 @@ - - - - - - - - - ModernUO Commands - - - - - - -
-
-
- -
- -
- -

ModernUO Commands

- -
    - -
- -
- -
-
-
-
- - - - - - - diff --git a/website/tools/build-packets.ps1 b/website/tools/build-packets.ps1 deleted file mode 100644 index 1e93dbc89..000000000 --- a/website/tools/build-packets.ps1 +++ /dev/null @@ -1,194 +0,0 @@ -# build-packets.ps1 -# Combines all packet JSON files with the HTML template to generate the final documentation page. -# -# Usage: -# .\build-packets.ps1 -# .\build-packets.ps1 -OutputPath "C:\custom\output\packets.html" - -param( - [string]$OutputPath = "" -) - -$ErrorActionPreference = "Stop" - -# Determine paths -$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path -$docsDir = (Resolve-Path (Join-Path $scriptDir "..")).Path -$packetsDir = Join-Path $docsDir "packets" -$templateFile = Join-Path $packetsDir "template.html" - -if (-not $OutputPath) { - $OutputPath = Join-Path $packetsDir "packets.html" -} - -Write-Host "ModernUO Packet Documentation Builder" -ForegroundColor Cyan -Write-Host "======================================" -ForegroundColor Cyan -Write-Host "" - -# Verify template exists -if (-not (Test-Path $templateFile)) { - Write-Error "Template file not found: $templateFile" - exit 1 -} - -# Collect all packet JSON files -$allPackets = @{ - incoming = @() - outgoing = @() -} - -$incomingDir = Join-Path $packetsDir "incoming" -$outgoingDir = Join-Path $packetsDir "outgoing" - -# Read incoming packets -if (Test-Path $incomingDir) { - $incomingFiles = Get-ChildItem "$incomingDir\*.json" -ErrorAction SilentlyContinue | Sort-Object Name - foreach ($file in $incomingFiles) { - Write-Host " Reading: $($file.Name)" -ForegroundColor Gray - try { - $content = Get-Content $file.FullName -Raw -Encoding UTF8 - $data = $content | ConvertFrom-Json - if ($data.packets) { - # Add category and merge tags for each packet - foreach ($packet in $data.packets) { - # Add category if not present (backward compatibility) - if (-not $packet.category -and $data.category) { - $packet | Add-Member -NotePropertyName "category" -NotePropertyValue $data.category -Force - } - - # Merge tags: file-level + packet-level - $fileTags = @() - if ($data.tags) { $fileTags = @($data.tags) } - - $packetTags = @() - if ($packet.tags) { $packetTags = @($packet.tags) } - - $allTags = @($fileTags + $packetTags) | Select-Object -Unique - - # Always include the primary category as a tag (first position) - if ($data.category -and $data.category -notin $allTags) { - $allTags = @($data.category) + $allTags - } - - # Ensure tags is always an array (PowerShell can collapse single-element arrays) - $allTags = @($allTags) - - $packet | Add-Member -NotePropertyName "tags" -NotePropertyValue $allTags -Force - } - $allPackets.incoming += $data.packets - } - } catch { - Write-Warning "Failed to parse $($file.Name): $_" - } - } - Write-Host " Found $($allPackets.incoming.Count) incoming packets" -ForegroundColor Green -} else { - Write-Host " No incoming packets directory found" -ForegroundColor Yellow -} - -# Read outgoing packets -if (Test-Path $outgoingDir) { - $outgoingFiles = Get-ChildItem "$outgoingDir\*.json" -ErrorAction SilentlyContinue | Sort-Object Name - foreach ($file in $outgoingFiles) { - Write-Host " Reading: $($file.Name)" -ForegroundColor Gray - try { - $content = Get-Content $file.FullName -Raw -Encoding UTF8 - $data = $content | ConvertFrom-Json - if ($data.packets) { - # Add category and merge tags for each packet - foreach ($packet in $data.packets) { - # Add category if not present (backward compatibility) - if (-not $packet.category -and $data.category) { - $packet | Add-Member -NotePropertyName "category" -NotePropertyValue $data.category -Force - } - - # Merge tags: file-level + packet-level - $fileTags = @() - if ($data.tags) { $fileTags = @($data.tags) } - - $packetTags = @() - if ($packet.tags) { $packetTags = @($packet.tags) } - - $allTags = @($fileTags + $packetTags) | Select-Object -Unique - - # Always include the primary category as a tag (first position) - if ($data.category -and $data.category -notin $allTags) { - $allTags = @($data.category) + $allTags - } - - # Ensure tags is always an array (PowerShell can collapse single-element arrays) - $allTags = @($allTags) - - $packet | Add-Member -NotePropertyName "tags" -NotePropertyValue $allTags -Force - } - $allPackets.outgoing += $data.packets - } - } catch { - Write-Warning "Failed to parse $($file.Name): $_" - } - } - Write-Host " Found $($allPackets.outgoing.Count) outgoing packets" -ForegroundColor Green -} else { - Write-Host " No outgoing packets directory found" -ForegroundColor Yellow -} - -$totalPackets = $allPackets.incoming.Count + $allPackets.outgoing.Count -Write-Host "" -Write-Host "Total packets: $totalPackets" -ForegroundColor Cyan - -if ($totalPackets -eq 0) { - Write-Warning "No packets found! The output will have empty documentation." -} - -# Sort packets by ID for deterministic output -# Handle IDs like "0x02" and "0xBF/0x05" (split on / and parse first part) -$allPackets.incoming = @($allPackets.incoming | Sort-Object { - $baseId = $_.id -split '/' | Select-Object -First 1 - [Convert]::ToInt32($baseId, 16) -}, { - if ($_.subId) { [Convert]::ToInt32($_.subId, 16) } - elseif ($_.id -match '/') { [Convert]::ToInt32(($_.id -split '/' | Select-Object -Last 1), 16) } - else { 0 } -}) -$allPackets.outgoing = @($allPackets.outgoing | Sort-Object { - $baseId = $_.id -split '/' | Select-Object -First 1 - [Convert]::ToInt32($baseId, 16) -}, { - if ($_.subId) { [Convert]::ToInt32($_.subId, 16) } - elseif ($_.id -match '/') { [Convert]::ToInt32(($_.id -split '/' | Select-Object -Last 1), 16) } - else { 0 } -}) - -# Convert to JSON -$packetsJson = $allPackets | ConvertTo-Json -Depth 20 -Compress - -# HTML entity escape the JSON to safely embed in HTML -$packetsJson = $packetsJson -replace '<', '<' -replace '>', '>' - -# Read template and inject JSON -Write-Host "" -Write-Host "Reading template..." -ForegroundColor Gray -$template = Get-Content $templateFile -Raw -Encoding UTF8 - -Write-Host "Injecting packet data..." -ForegroundColor Gray -$output = $template -replace '', $packetsJson - -# Ensure output directory exists -$outputDir = Split-Path $OutputPath -Parent -if (-not (Test-Path $outputDir)) { - Write-Host "Creating output directory: $outputDir" -ForegroundColor Gray - New-Item -ItemType Directory -Path $outputDir -Force | Out-Null -} - -# Write output -Write-Host "Writing output..." -ForegroundColor Gray -$output | Set-Content $OutputPath -Encoding UTF8 - -$outputSize = (Get-Item $OutputPath).Length -$outputSizeKB = [math]::Round($outputSize / 1024, 2) - -Write-Host "" -Write-Host "Success!" -ForegroundColor Green -Write-Host " Output: $OutputPath" -ForegroundColor White -Write-Host " Size: $outputSizeKB KB" -ForegroundColor White -Write-Host " Packets: $totalPackets" -ForegroundColor White diff --git a/website/tsconfig.json b/website/tsconfig.json deleted file mode 100644 index 920d7a652..000000000 --- a/website/tsconfig.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - // This file is not used in compilation. It is here just for a nice editor experience. - "extends": "@docusaurus/tsconfig", - "compilerOptions": { - "baseUrl": "." - }, - "exclude": [".docusaurus", "build"] -}