Commit graph

16 commits

Author SHA1 Message Date
Kamron Batman
d8a64f3316
refactor(spawners): replace DynamicJson with typed SpawnerDto records (#2505)
## Summary

Removes the dated `DynamicJson` JSON helper and migrates spawner JSON (de)serialization to a polymorphic `record SpawnerDto` hierarchy. `DynamicJson` was the last remaining consumer (regions moved off it in #1400).

The key correctness improvement: **System.Text.Json deserializes plain DTO records, never a live `Item`.** Previously, deserializing directly into an `Item` meant STJ constructed world-registered objects *before* the data was validated — a malformed/hand-edited spawn file could leave orphaned spawner Items in the world save. Now a parse failure is GC-only; `dto.ToSpawner()` constructs the spawner only from a fully-validated DTO and self-cleans on failure.

## What changed

- **New:** `SpawnerDto` (abstract) + `SpawnerDataDto` / `RegionSpawnerDto` / `ProximitySpawnerDto`, each marked with a reusable `[JsonDiscoverableType]` opt-in attribute. Auto-discovered at the `Configure` phase — no manual registration list (avoids the regions `Register<T>()` footgun), open to custom spawner subtypes.
- **Symmetric mapping:** `BaseSpawner.ToDto()` (export) ⇄ `SpawnerDto.ToSpawner()` (import). `ToSpawner()` deletes-and-rethrows on any failure, so the importer can never orphan an Item.
- **`SpawnerJsonSerializer`** wires `$type` polymorphism on the `SpawnerDto` root with loud collision/constructibility validation.
- **Import/export commands** rewired to the typed DTO path (reflection `FindTypeByName`/`CreateInstance` removed).
- **Data migration:** the 109 `Distribution/Data/Spawns/**` files moved from `"type"`→`"$type"` and legacy `homeRange`→`spawnBounds`. The runtime still *reads* legacy `homeRange` for external files. The `homeRange→spawnBounds` formula is proven equivalent to the runtime conversion (`BoundsEquivalenceTests`, hr=0/1/3/7).
- **Deleted:** `Projects/Server/Json/DynamicJson.cs`.

Sparse export output matches the legacy `ToJson` (nullable DTO properties + `WhenWritingNull`). Binary world-save serialization is untouched.

## Tests

- DTO round-trip per spawner type; sparse-default omission; legacy `homeRange` read; export/import file round-trip.
- `Import_MalformedFile_LeaksNoWorldItems` — proves a mid-array parse failure constructs zero world Items.
- `AllSpawnFilesLoadTests` — every migrated spawn file deserializes and builds.
- Duplicate-discriminator validation.
- UOContent.Tests 485/485, Server.Tests 710/710, build clean.

## Follow-up (not in this PR)

`Rectangle3DConverter.Write` (in `Projects/Server/`) omits `z1/z2` when `Start.Z == -128`, so a future server-side export of a `homeRange`-style spawner round-trips depth 256→255. Pre-existing and out of scope here (Server change); the migrated data reads correctly. Worth a separate small converter PR.
2026-06-25 23:24:45 -07:00
Kamron Batman
e1e1a7c640
fix: Bumps deps. Updates copyrights (#2353) 2026-03-05 19:36:54 -08:00
Kamron Batman
3e8d548f38
feat: Add spawn position caching and spiral scan optimization (#2295)
### Summary

Adds spawn position caching and optimization for constrained spawners (e.g., those near houses, water, or blocked terrain).

### Key features:
- Sector-based bitmap cache (32 bytes per 16x16 sector) stores valid spawn positions
- Spiral scan progressively discovers positions from spawner center outward
- Automatic mode detects constrained spawners after 5+ non-transient failures
- Prevents mob spawning inside private houses (allows public AoS buildings)
- Deduplicates sector lookups for multi-bounds spawners (RegionSpawner)
- Cache invalidation on house placement/demolition
- Moves SpawnBounds to Spawner

### New spawner properties:
- SpawnPositionMode: Automatic (default), Enabled, Disabled, Abandoned
- MaxSpawnAttempts: Configurable attempts before optimization engages (default: 5)
2025-12-28 02:40:21 -08:00
Kamron Batman
6d51b33cf8
feat: Add CanSpawnMobile overload with props Z-range support. (#2293)
### Summary

- Adds CanSpawnMobile(x, y, minZ, maxZ, canSwim, cantWalk, out spawnZ) overload for finding spawn surfaces within a Z range
- Adds CanSpawnItem(x, y, minZ, maxZ, out spawnZ) for item spawning with Surface+Impassable support (tables, furniture)
- Uses bitmask optimization inspired by Item.DropToWorld's m_OpenSlots pattern for O(1) surface/blocker checks
- HomeRange spawners now use surface detection to set proper Z bounds

### Key Changes

Map.cs:
- CanSpawnMobile with Z-range finds lowest valid surface for mobiles
- CanSpawnItem with Z-range finds lowest valid surface for items (including tables)
- CanFitItem for point-check item placement on Surface+Impassable tiles
- Bitmask approach eliminates nested loops and stackalloc arrays

Spawners:
- Simplified GetSpawnPosition using new Z-range methods
- HomeRange setter detects surface below spawner for proper Z bounds
- Consistent handling for mobiles and items

### Bug Fixes

- Water tiles (Impassable | Wet) no longer block swimming mobs
- Items can now spawn on tables/furniture (Surface+Impassable)

### Test Plan

- Run dotnet test - 631 tests pass
- Manual testing: multi-story spawning, water mobs, item spawning on tables
- Verify HomeRange spawner movement shifts bounds correctly
2025-12-27 17:01:15 -08:00
Quick
0b34cc4417
feat: Changes Spawner HomeRange to SpawnBounds (#2290)
### Summary

This PR transitions the spawner system from a simple radius-based model to a flexible 3D boundary system.

### Core Changes

* **Replaced `HomeRange` with `SpawnBounds`**: Spawners now use a `Rectangle3D` to define spawn areas instead of a circular integer range.
* **Backward Compatibility**:
* The `HomeRange` property remains as a helper that generates square `SpawnBounds` centered on the spawner.
* Included a migration path (v10 to v11) that automatically converts old range data into new bounds during deserialization.


* **Dynamic Bounds Shifting**: If a spawner is moved, its `SpawnBounds` will automatically shift with it, provided the bounds are currently configured as a centered square.
* **New Spawn Logic**: Added `SpawnLocationIsHome` toggle. If enabled, spawned mobiles treat their exact spawn coordinates as their "Home" rather than the spawner's location.

### Implementation Details

* **Interface Updates**: Updated `ISpawner` to include `WalkingRange`, `SpawnBounds`, and `IsInSpawnBounds()`.
* **UI Enhancements**: The Spawner Controller Gump now displays "Custom" for complex bounds and allows copying of the new boundary properties between spawners.
* **Refactored Constructors**: Streamlined `BaseSpawner`, `ProximitySpawner`, and `RegionSpawner` constructors to support the new data types.
2025-12-26 18:07:26 -08:00
Kamron Batman
279b10dd0f
feat: Replaces params array with params ReadOnlySpan (#2125) 2025-02-13 21:19:02 -08:00
Marcelo Paez Sequeira
832bb24f11
fix: Fixes ProximitySpawner and RegionSpawner ToJson (#1827) 2024-06-06 16:31:48 -07:00
Kamron Batman
5ada7a4e50
fix: Codegens spawners (#1678) 2024-02-12 21:14:42 -08:00
Kamron Batman
8389bfacfe
chore: Updates copyright (#1448) 2023-08-09 09:09:26 -07:00
Kamron Batman
f268d5d4e2
fix: Cleans up core code (#1187)
**Only one functional change**
* Fixes a bug in LogFactory where `Warning` is being logged as `Information`

Non-functional changes:
* Updates/Fixes copyright headers
* Removes namespace scopes for core files.

View with [whitespace off](https://github.com/modernuo/ModernUO/pull/1187/files?w=1).
2022-10-10 21:47:08 -07:00
Kamron Batman
b74b47159f
fix: Fixes localization corner cases with OPL (#1050)
## Changes
- [X] Adds OPL convenience methods
    - `opl.Add(cliloc, value)` and `opl.Add(value)` - value as an integer or string works just like `opl.Add(cliloc, $"{value}")`
    - `opl.AddLocalized(cliloc, clilocValue)` - works the same as `opl.Add(cliloc, $"#{clilocValue}");`
- [X] Simplifies basic `list.Add()` situations
- [X] Changes cliloc as an argument so it works with custom IPropertyList implementations (HTML)
- [X] Fixes plants so they support the old localization and new (changed in 7.0.12.0+)
- [X] Exposes more methods to override for Item to make creating custom OPL possible.

## Important Notes
* Using a ternary as an argument, like this `opl.Add(number, showType ? $"{type}\t{value}" : $"{value}");` _will not use the correct string interpolation_. This means if you use a custom PropertyList (for HTML or some other purpose), the property list won't be localized properly.
* All localization values must be interpolated, even if they are literal strings, or integers. Example: `opl.Add(number, $"{"Charges"}\t{m_Charges}");` is correct. Using the following: `$"Charges\t{m_Charges}"` will not work for custom PropertyList implementations!
2022-06-12 21:17:42 -07:00
Kamron Batman
ecbee17690
fix: Optimizes OPL using string interpolation (#1041)
## Breaking Changes (New API)
ObjectPropertyList supports the following API:
```cs
list.Add(500000);
list.Add(500001, stringArgument);
list.Add("Some text");
list.Add($"Some text with {argument}");
list.Add(500002, $"{arg1}\t{arg2}");
```

## Notes
1. All API uses that require a formatter like this:
    ```cs
    list.Add(500002, "{0}\t{1}", arg1, arg2);
    ```
    Should be changed to use string interpolation, for example:
    ```cs
    list.Add(500002, $"{arg1}\t{arg2}");
    ```
2. The following paradigm should no longer be used:
    ```cs
    list.Add(1061170, prop.ToString()); // strength requirement ~1_val~
    ```
    The new string interpolation API will avoid having to convert the argument to a string before writing it to the packet. Instead use the following:
    ```cs
    list.Add(1061170, $"{prop}"); // strength requirement ~1_val~
    ```

### Benchmarks
```cs
|                         Method |     Mean |   Error |  StdDev |  Gen 0 | Allocated |
|------------------------------- |---------:|--------:|--------:|-------:|----------:|
|                BenchmarkOldOPL | 241.0 ns | 0.56 ns | 0.47 ns | 0.0105 |      88 B |
| BenchmarkStringInterpolatedOPL | 199.9 ns | 2.44 ns | 2.39 ns |      - |         - |
```

### Changes
- [X] Removes crash in STArray.Return when array is null.
- [X] Fixes NPE in OPL when entity is null. Serial in packet will be 0 when entity is null.
- [X] Fixes NPE in AosAttributes when Parent is null.
- [X] Changes OPL to use string interpolation.
- [X] Introduces `IPropertyList` to allow extending PropertyList for other uses.
2022-06-02 10:09:53 -07:00
Kamron Batman
0d1fffd9fc
fix: Fixes spawners, adds convertpremiumspawners, exportspawners, and replaces with neruns distro (#751)
### Additions
* Adds `[exportspawners <relative json file path to distro>`
  * If no path is provided, it will be saved to the `Data\Spawns` folder with the current timestamp as the file name.
  * Supports global, facet, region, multi, and area
* Fixes client disconnect for bad spawner generation command.
* Moves spawner commands to their own folder.
* Adds GUIDs for spawners. This allows for easy replacement during import to reduce duplication.
* Allows exporting the name of the spawner.
* Fixes globbing when using [generatespawners
* Adds [convertpremiumspawners to convert Premium Spawners (from Neruns distro)

Possibly in .NET 6 serializing JSON will be more performant using JSON nodes.


### Screenshots
![Screen_Shot_2021-08-31_at_10 20 06_PM](https://user-images.githubusercontent.com/3953314/131618495-cf7e3ae5-58e5-4f86-9d3c-3cc49906d9fc.png)
![Screen_Shot_2021-08-31_at_10 21 08_PM](https://user-images.githubusercontent.com/3953314/131618502-c39cc368-2266-47a5-9b87-e5ffa108b875.png)
![Screen_Shot_2021-08-31_at_10 13 29_PM](https://user-images.githubusercontent.com/3953314/131618512-f9807f1b-76e4-4503-989d-42324798fbf5.png)
2021-09-03 21:52:44 -07:00
Kamron Batman
8149620b0c
Fixes brace style (#248) 2020-09-13 21:49:46 -07:00
Kamron Batman
ad3775c4d7
Formats UO Content (#201) 2020-08-27 18:30:38 -07:00
Kamron Batman
390f30e706
Convert Regions/Spawns to JSON (#138) 2020-05-26 00:34:29 -07:00