Kamron Batman
f7f1265216
fix: Fixes PotionKeg causing weight issues ( #2330 )
2026-02-08 20:55:15 -08:00
Kamron Batman
b191498569
feat: Adds Feature Flag System ( #2328 )
...
## Feature Flag System
A runtime feature flag system for dynamically enabling/disabling game systems and blocking specific game elements
without server restarts.
### Overview
Two types of controls:
1. Feature Flags - Named boolean flags that gate entire systems (trading, PvP, vendors, housing, etc.)
2. Dynamic Blocks - Block specific gumps, items, skills, or spells by type
All changes are persisted to JSON, broadcast to online staff, and bypass-able by administrators.
### Predefined Flags
```
┌─────────────────┬──────────┬─────────────────────────────────────┐
│ Flag │ Category │ Description │
├─────────────────┼──────────┼─────────────────────────────────────┤
│ player_trading │ Economy │ Allow secure trades between players │
├─────────────────┼──────────┼─────────────────────────────────────┤
│ vendor_purchase │ Economy │ Allow purchasing from NPC vendors │
├─────────────────┼──────────┼─────────────────────────────────────┤
│ vendor_sell │ Economy │ Allow selling to NPC vendors │
├─────────────────┼──────────┼─────────────────────────────────────┤
│ player_vendors │ Economy │ Allow player vendor interactions │
├─────────────────┼──────────┼─────────────────────────────────────┤
│ bank_access │ Economy │ Allow bank box access │
├─────────────────┼──────────┼─────────────────────────────────────┤
│ house_placement │ Housing │ Allow new house placements │
├─────────────────┼──────────┼─────────────────────────────────────┤
│ boat_placement │ Housing │ Allow new boat placements │
├─────────────────┼──────────┼─────────────────────────────────────┤
│ bulk_orders │ Crafting │ Allow bulk order deeds │
├─────────────────┼──────────┼─────────────────────────────────────┤
│ pvp_combat │ Combat │ Allow player vs player combat │
└─────────────────┴──────────┴─────────────────────────────────────┘
```
### Commands
#### Feature Flags
```
┌────────────────────────────────────────────────────┬───────────────────────┐
│ Command │ Description │
├────────────────────────────────────────────────────┼───────────────────────┤
│ [FeatureFlag <key> on|off|toggle|info │ Manage a feature flag │
├────────────────────────────────────────────────────┼───────────────────────┤
│ [FF <key> on|off │ Shorthand alias │
├────────────────────────────────────────────────────┼───────────────────────┤
│ [FeatureFlag <key> create <category> <desc> │ Create a custom flag │
├────────────────────────────────────────────────────┼───────────────────────┤
│ [FeatureFlag <key> delete │ Delete a custom flag │
├────────────────────────────────────────────────────┼───────────────────────┤
│ [FeatureList [flags|gumps|items|skills|spells|all] │ List all flags/blocks │
├────────────────────────────────────────────────────┼───────────────────────┤
│ [FeatureAdmin │ Open the admin gump │
└────────────────────────────────────────────────────┴───────────────────────┘
```
#### Gump Blocks
```
┌────────────────────────────────┬──────────────────────────────────────────────────────┐
│ Command │ Description │
├────────────────────────────────┼──────────────────────────────────────────────────────┤
│ [BlockGump <typeName> [reason] │ Block a gump type from displaying │
├────────────────────────────────┼──────────────────────────────────────────────────────┤
│ [UnblockGump <typeName> │ Remove a gump block │
├────────────────────────────────┼──────────────────────────────────────────────────────┤
│ [ListGumps [self] │ List open gumps on a player (for finding type names) │
└────────────────────────────────┴──────────────────────────────────────────────────────┘
```
#### Item Blocks
```
┌────────────────────────────────────────────────┬───────────────────────────────┐
│ Command │ Description │
├────────────────────────────────────────────────┼───────────────────────────────┤
│ [BlockItemUse <typeName|target> [reason] │ Block item use │
├────────────────────────────────────────────────┼───────────────────────────────┤
│ [BlockItemEquip <typeName|target> [reason] │ Block item equipping │
├────────────────────────────────────────────────┼───────────────────────────────┤
│ [BlockItemContainer <typeName|target> [reason] │ Block container access │
├────────────────────────────────────────────────┼───────────────────────────────┤
│ [UnblockItemUse <typeName> │ Remove use block │
├────────────────────────────────────────────────┼───────────────────────────────┤
│ [UnblockItemEquip <typeName> │ Remove equip block │
├────────────────────────────────────────────────┼───────────────────────────────┤
│ [UnblockItemContainer <typeName> │ Remove container access block │
└────────────────────────────────────────────────┴───────────────────────────────┘
```
Item block commands create an entry if one doesn't exist, or flip the relevant flag on an existing entry. Unblock
commands only clear their specific flag -- the entry is removed when all flags (use/equip/container) are off.
#### Skill & Spell Blocks
```
┌──────────────────────────────────────┬──────────────────────┐
│ Command │ Description │
├──────────────────────────────────────┼──────────────────────┤
│ [BlockSkill <SkillName> [reason] │ Block a skill │
├──────────────────────────────────────┼──────────────────────┤
│ [UnblockSkill <SkillName> │ Remove a skill block │
├──────────────────────────────────────┼──────────────────────┤
│ [BlockSpell <SpellTypeName> [reason] │ Block a spell │
├──────────────────────────────────────┼──────────────────────┤
│ [UnblockSpell <SpellTypeName> │ Remove a spell block │
└──────────────────────────────────────┴──────────────────────┘
```
### Architecture
- Static bool classes for hot-path checks (no dictionary lookups): ServerFeatureFlags (Server project) and
ContentFeatureFlags (UOContent)
- Synced automatically by FeatureFlagManager.SyncStaticFlag() when flags change
- Predefined flags loaded from Distribution/Configuration/FeatureFlags/default-flags.json
- Runtime state persisted to Configuration/FeatureFlags/*.json (flags, gump-blocks, item-blocks, skill-blocks,
spell-blocks)
- Admin gump with tabbed UI for managing all block types, per-entry PropertiesGump editing, and pagination
- All staff at AccessLevel.GameMaster+ bypass dynamic blocks; AccessLevel.Administrator+ bypass feature flags
### Hook Points
Hook: Player trading
Location: Mobile.OpenTrade
Mechanism: ServerFeatureFlags.PlayerTrading
────────────────────────────────────────
Hook: PvP combat
Location: Mobile.CanBeHarmful
Mechanism: ServerFeatureFlags.PvPCombat
────────────────────────────────────────
Hook: Bank access
Location: BankBox.Open()
Mechanism: ServerFeatureFlags.BankAccess
────────────────────────────────────────
Hook: Vendor buy/sell
Location: BaseVendor
Mechanism: ContentFeatureFlags.VendorPurchase/Sell
────────────────────────────────────────
Hook: Player vendors
Location: PlayerVendor
Mechanism: ContentFeatureFlags.PlayerVendors
────────────────────────────────────────
Hook: House placement
Location: HousePlacement.Check()
Mechanism: ContentFeatureFlags.HousePlacement (returns BadRegionTemp)
────────────────────────────────────────
Hook: Boat placement
Location: BaseBoatDeed.OnDoubleClick/OnPlacement
Mechanism: ContentFeatureFlags.BoatPlacement
────────────────────────────────────────
Hook: Bulk orders
Location: SmallBOD/LargeBOD
Mechanism: ContentFeatureFlags.BulkOrders
────────────────────────────────────────
Hook: Gump display
Location: GumpSystem.SendGump
Mechanism: FeatureFlagManager.IsGumpBlocked
────────────────────────────────────────
Hook: Item use
Location: PlayerMobile.AllowItemUse
Mechanism: FeatureFlagManager.IsItemUseBlocked
────────────────────────────────────────
Hook: Item equip
Location: PlayerMobile.CheckEquip
Mechanism: FeatureFlagManager.IsItemEquipBlocked
────────────────────────────────────────
Hook: Container access
Location: BaseContainer.DisplayTo
Mechanism: FeatureFlagManager.IsContainerAccessBlocked
────────────────────────────────────────
Hook: Skill use
Location: PlayerMobile.AllowSkillUse
Mechanism: FeatureFlagManager.IsSkillBlocked
────────────────────────────────────────
Hook: Spell casting
Location: Spell.Cast / Spellbook.CastSpellRequest
Mechanism: FeatureFlagManager.IsSpellBlocked
2026-02-07 12:02:57 -08:00
Kamron Batman
fd2f90cba9
feat: Adds SerializableFieldChanged option ( #2324 )
2026-02-01 21:28:26 -08:00
Kamron Batman
0404251638
feat: Adds Latin1 text support ( #2317 )
...
## Summary
- Adds proper Latin1 encoding support, replacing CP1252 usage throughout the codebase
- Adds specialized, optimized string decoding methods with safe string filtering for each encoding type
- Filters invalid Unicode characters (C0/C1 control codes, non-characters) by removal rather than replacement since
the UO client renders nothing for these characters
- Fixes UTF-16 null terminator position handling to correctly advance by 2 bytes
## Changes
TextEncoding.cs
- Added SearchValues-based invalid byte/char detection for efficient filtering
- Added encoding-specific GetString methods: GetStringAscii, GetStringLatin1, GetStringUtf8, GetStringBigUni,
GetStringLittleUni
- Each method supports a safeString parameter for filtering invalid characters
- Little-endian UTF-16 uses direct memory cast for zero-copy decoding on LE systems
- Invalid characters are removed (not replaced with U+FFFD) since the client renders nothing for them
SpanReader.cs
- Added ReadLatin1() and ReadLatin1Safe() methods
- Rewrote encoding-specific read methods to use optimized TextEncoding.GetString* methods
- Fixed UTF-16 null terminator handling: position now correctly advances by byteLength (2) instead of 1
SpanWriter.cs
- Added WriteLatin1 and WriteLatin1Null methods
## Packet Updates
- Updated all packet code to use Latin1 encoding instead of CP1252
- Affected: account packets, equipment packets, menu packets, message packets, mobile packets, player packets, secure
trade packets, vendor packets, gump packets, book packets, mahjong packets
## Filtering Behavior
Invalid characters filtered in safe mode:
```
┌───────────────┬────────────────────────┐
│ Range │ Description │
├───────────────┼────────────────────────┤
│ 0x00-0x1F │ C0 control codes │
├───────────────┼────────────────────────┤
│ 0x7F │ DEL │
├───────────────┼────────────────────────┤
│ 0x80-0x9F │ C1 control codes │
├───────────────┼────────────────────────┤
│ 0xFFFE-0xFFFF │ Unicode non-characters │
└───────────────┴────────────────────────┘
```
Note: Surrogate pairs (0xD800-0xDFFF) are not filtered because proper validation requires context checking for paired
vs unpaired surrogates. The UO client renders nothing for these anyway.
## Test Plan
- All 631 Server.Tests pass
- Verified client rendering behavior using TestUnicodeGump command (pages 1-5)
- Confirmed U+FFFD, unpaired surrogates, and non-characters all render as blank in client
- Verified Latin1 characters (0xA0-0xFF) display correctly
- Verified C1 control codes (0x80-0x9F) are filtered and don't display
2026-01-22 15:51:00 -08:00
Kamron Batman
ee2cd1d18d
feat: Adds new decay system and SkipSerialization ( #2311 )
...
## Summary
- Replaces scan-based decay checking during world saves with an event-driven timer wheel scheduler
- Removes virtual property checks from serialization hot path, achieving 6-8x faster world saves
## Changes
### DecayScheduler (new):
- Timer wheel with 12 HashSet buckets (5-min intervals) + PriorityQueue for active processing
- O(1) register/unregister vs O(n) scan of all items
- Auto start/stop when items exist/empty
- Configurable tick interval with jitter to prevent system synchronization
### Item.cs:
- Added ScheduledDecayTime computed property
- Added UpdateDecayRegistration() called from SetLastMoved(), property setters, AddItem()/RemoveItem()
- Hooks in Visible, Movable, Spawner setters and Delete()
### World.cs:
- Removed _decayQueue, EnqueueForDecay(), ProcessDecay()
- ItemPersistence.Serialize() now tight loop without virtual calls
## Performance
| Metric | Before | After | Improvement |
|---------------|------------|-----------|-------------|
| Items (230K) | 245K ticks | 34K ticks | 8x faster |
| Mobiles (43K) | 56K ticks | 6K ticks | 9x faster |
| Total | 302K ticks | 40K ticks | 7.5x faster |
## Configuration
decay.maxItemsPerTick = 250 # Items processed per tick
decay.tickInterval = 256ms # Base processing interval
decay.bucketInterval = 5min # Timer wheel bucket size
decay.jitterMaxMs = 25 # ±25ms tick jitter
2026-01-08 11:43:51 -08:00
Kamron Batman
c14f361de1
fix: Moves containers/bods/traps/etc to serialization generator ( #2310 )
2026-01-07 21:55:55 -08:00
Kamron Batman
d3b43f6f0e
feat: Fixes spawner bugs with position finding, Adds [ShowSpawnerBorders ( #2297 )
...
### Summary
- Fixes BaseSpawner and Spawner bugs.
- Adds [ShowSpawnerBorders to see the spawn bounds.
2025-12-28 18:26:03 -08:00
Kamron Batman
cc4a679fb0
fix: Fixes serializing EngravedText ( #2296 )
2025-12-28 02:44:49 -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
ebaf104935
chore: Use var everywhere ( #2294 )
2025-12-27 16:47:28 -08:00
Kamron Batman
8048711483
fix: Fixes ConditionTeleporter.DenyFollowers ( #2284 )
2025-11-29 16:28:14 -08:00
Kamron Batman
7bd5a853a3
feat: Adds size/style support to gump builders, optimizes EscapeHtml ( #2257 )
...
> [!IMPORTANT]
> **Developer Note**
> THIS IS A BREAKING CHANGE TO THE NEW API GUMP.
> Please give us feedback in [discord ](https://muo.gg/discord ) if you have issues, need help, or have ideas for a better API change!
### Summary
* Adds support for size/style to dynamic/static builder.
* Drastically simplifies the dynamic/static builder api for AddHtml.
* Cleans up some legacy gump files.
2025-11-23 11:35:31 -08:00
Kamron Batman
4836bff5eb
fix: Eliminates List allocations in various places. ( #2158 )
2025-11-16 18:33:53 -08:00
Kamron Batman
bbb7dc2294
fix: Fixes dropping a stack of scrolls on a spellbook ( #2248 )
2025-08-09 20:55:36 -07:00
Kamron Batman
fa0b67ec29
fix: Optimizes items to use default weights. (Part 4) ( #2243 )
2025-07-24 23:29:29 -07:00
Kamron Batman
40ba85d7b8
fix: Optimizes items to use default weights. (Part 3) ( #2242 )
2025-07-24 16:21:27 -07:00
Kamron Batman
2c5441731e
fix: Optimizes items to use default weights. (Part 2) ( #2241 )
2025-07-24 15:41:44 -07:00
Kamron Batman
2eba2868a6
fix: Optimizes items to use default weights. (Part 1) ( #2240 )
2025-07-24 14:44:39 -07:00
Kamron Batman
8a118170e7
fix: Fixes treasure map chest crash from null guardians ( #2236 )
2025-07-22 16:01:27 -07:00
Felipe Maya Muniz
4209f7ea16
fix: Add properties for crafting ranged weapons with colored logs ( #2222 )
2025-07-07 18:51:08 -07:00
Bohica
35faba5087
fix: fixes the id for farmable cabbage ( #2227 )
2025-07-02 15:04:48 -07:00
Kamron Batman
a286e282a9
fix: Fixes fist disarm (pre-aos) ( #2214 )
2025-06-07 18:22:54 -07:00
Kamron Batman
0d2ed60fed
fix: Fixes null components in BaseAddon ( #2208 )
2025-06-02 22:11:40 -10:00
Kamron Batman
f3bcf89015
fix: Fixes weapons not serializing lesser poison ( #2194 )
...
### Summary
Fixes weapons not serializing lesser poison.
> [!NOTE]
> Dev Note: After applying this fix, fix weapons already in-game using the following command
> `[global set poison lesser where baseweapon poison = null poisoncharges > 0`
2025-05-23 15:54:17 -07:00
Kamron Batman
e468f79e18
feat: Adds SpecialAttack monster ability trigger. Fixes fanning fire. ( #2186 )
2025-05-15 22:14:52 -07:00
Kamron Batman
da517f56c4
chore: Fixes variable types for possible performance issues ( #2172 )
2025-05-01 21:54:41 -07:00
Kamron Batman
47b03f5d62
fix: Fixes NPCs not able to equip armor/clothing/weapons ( #2161 )
2025-04-21 14:01:08 -07:00
Kamron Batman
4aa272d429
feat: Adds an Item/Mobile memory leak detector. Fixes minor leak in doors. ( #2159 )
...
### Summary
Adds the command [TrackLeaks to enable tracking item/mobiles that have been deleted but still have dangling references. Requires adding the _TRACK_LEAKS_ define constant during build.
2025-04-15 19:14:45 -07:00
Kamron Batman
a94814a7ef
fix: Fixes and optimizes NameVerification and ProfanityProtection ( #2153 )
...
### Summary
Significantly improves the performance of NameVerification & ProfanityProtection:
```cs
| Method | Mean | Error | StdDev |
|--------------- |----------:|----------:|----------:|
| ValidateName | 728.34 ns | 13.244 ns | 11.059 ns |
| ValidateNameSV | 26.62 ns | 0.233 ns | 0.207 ns |
```
2025-04-12 02:26:10 -07:00
Kamron Batman
56e3086ed1
fix: Makes hairdye gump static ( #2138 )
2025-03-04 15:55:05 -08:00
Kamron Batman
2e08eda8cb
feat: Standardizes South/East selection gumps ( #1839 )
2025-02-14 00:15:14 -08:00
Kamron Batman
279b10dd0f
feat: Replaces params array with params ReadOnlySpan ( #2125 )
2025-02-13 21:19:02 -08:00
Kamron Batman
40479c946a
feat: Adds item graphic size to Bounds.bin and makes item graphic offset available to gumps ( #2115 )
2025-02-10 19:31:39 -08:00
Bohica
772511d1e8
fix: Reverts GetProtOffset, Adds PreT2A Weapon Damage & PreUOR Armor Durability ( #2114 )
2025-02-06 01:27:40 -08:00
Bohica
51367df0d2
fix: Fixes AR/Durability/Tool Uses for Pre-UOR and LBR ( #2113 )
2025-02-05 19:54:56 -08:00
Kamron Batman
d407847fc4
fix: Fixes axes not working when equipped or in backpack ( #2112 )
2025-02-04 22:11:29 -08:00
Kamron Batman
ed87df1a61
fix: Converts bedroll/insurance gumps to static. ( #2096 )
2025-01-27 14:00:32 -08:00
Kamron Batman
ea364237f0
fix: Fix crash from not enough fish to kill in Aquarium ( #2092 )
2025-01-25 18:45:20 -08:00
Kamron Batman
a8b625b338
fix: Aquarium no longer uses list to kill fish ( #2090 )
2025-01-25 17:41:27 -08:00
mark1145
d5160bd6db
fix: Fixes buffs don't start/stop properly. Streamlines constructor and Add/Remove buffs. ( #2082 )
2025-01-24 18:20:23 -08:00
Kamron Batman
c8f13b2933
fix: Fixes runebook OPL not updating with charges ( #2088 )
2025-01-24 18:17:55 -08:00
Kamron Batman
84d9383294
feat: Fixes beneficial notoriety checks and adds better young restrictions/messaging ( #2000 )
2025-01-20 11:27:19 -08:00
Bohica
64e32a817d
fix: Fixes fillable containers start respawn on unlock. ( #2069 )
2025-01-20 09:20:26 -08:00
Kamron Batman
09d6420320
fix: Fixes Item ID so it doesn't gain from non-identifiable items ( #2075 )
2025-01-18 10:58:22 -08:00
Kamron Batman
b6950fc77c
fix: Removes unused usings ( #2071 )
2025-01-17 16:23:30 -08:00
Reetus
d15263c7d0
fix: Fix FillableContainer default so it is None ( #2065 )
2025-01-17 15:22:47 -08:00
Kamron Batman
66a257ece2
feat: Converts OnLogin to a coded generated event ( #2070 )
2025-01-17 15:21:45 -08:00
Erik Askov Mousing
3b698de556
feat: Adds Pre-UOTD single click support for weapons, armor, wands, and clothing ( #2053 )
2025-01-13 17:04:24 -08:00
Nathan Oines
863b4b5460
fix: Ensure ShouldSerializeCrafter serializes when _crafter is not null or empty ( #2062 )
2025-01-13 13:22:56 -08:00
Kamron Batman
f3c7a0e035
fix: Fixes explosion potion timer offset ( #2047 )
2025-01-01 20:28:12 -08:00