Compare commits

...
Sign in to create a new pull request.

2 commits

Author SHA1 Message Date
Kamron Batman
a4104d0768
docs(website): drop zstd from the install requirements
ZstdNet bundles libzstd for every RID, so nothing needs a system zstd.
Verified: the 15 ManagedArchive round-trip tests pass in a container
with no zstd package installed, and the build deploys
runtimes/<rid>/native/libzstd.* for linux-x64, linux-arm64, osx-x64,
osx-arm64 and win. The apt/dnf "zstd" package is the CLI tool, which
nothing shells out to.

Also aligns the Fedora line to libicu rather than libicu-devel, matching
the README and NativeLibraryChecker.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-06 21:36:03 -07:00
Kamron Batman
4dacd498e3
feat: Adds website 2026-08-06 21:34:53 -07:00
101 changed files with 47002 additions and 0 deletions

36
.github/workflows/update-docs.yml vendored Normal file
View file

@ -0,0 +1,36 @@
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

21
website/.gitignore vendored Normal file
View file

@ -0,0 +1,21 @@
# 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*

View file

@ -0,0 +1,206 @@
---
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 <name>")]
[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 <name>");
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.

View file

@ -0,0 +1,150 @@
---
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.

View file

@ -0,0 +1,418 @@
---
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.

View file

@ -0,0 +1,569 @@
---
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) |
---
<Tabs>
<TabItem value="codegen" label="Code Generation" default>
### 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.
</TabItem>
<TabItem value="generic" label="Generic Persistence">
### 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<Mobile, Timer> 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<Mobile>();
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.
</TabItem>
<TabItem value="entity-persistence" label="Entity Persistence">
### Custom Entity Types
Use `GenericEntityPersistence<T>` 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<IBOBEntry>
{
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.
:::
</TabItem>
</Tabs>
---
## 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
}
}
```

View file

@ -0,0 +1,280 @@
---
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.

View file

@ -0,0 +1,95 @@
---
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.
<OsTabs>
{{
windows: (
<CodeBlock language="bash">{'./publish.cmd'}</CodeBlock>
),
macos: (
<CodeBlock language="bash">{'./publish.sh'}</CodeBlock>
),
linux: (
<CodeBlock language="bash">{'./publish.sh'}</CodeBlock>
),
}}
</OsTabs>
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.
<OsTabs>
{{
windows: (
<CodeBlock language="bash">{'./publish.cmd release win x64'}</CodeBlock>
),
macos: (
<CodeBlock language="bash">{'./publish.sh release osx x64'}</CodeBlock>
),
linux: (
<CodeBlock language="bash">{'./publish.sh release linux x64'}</CodeBlock>
),
}}
</OsTabs>
The general format is:
```
publish <release|debug> [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.
:::

View file

@ -0,0 +1,441 @@
---
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<MySettings>(
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.

View file

@ -0,0 +1,77 @@
---
sidebar_position: 1
title: Installation
---
import OsTabs from '@site/src/components/OsTabs';
import CodeBlock from '@theme/CodeBlock';
import Admonition from '@theme/Admonition';
# Installation
<OsTabs>
{{
windows: (
<>
<h3>Prerequisites</h3>
<ol>
<li>Download and install the latest <a href="https://dotnet.microsoft.com/download/dotnet/10.0">.NET 10 SDK</a></li>
<li>Download and install <a href="https://git-scm.com/download/win">Git for Windows</a></li>
<li>Install <a href="https://learn.microsoft.com/en-us/cpp/windows/latest-supported-vc-redist">Visual C++ Redistributable</a> (v14 or later)</li>
</ol>
<Admonition type="tip">
<p>Use <a href="https://learn.microsoft.com/en-us/windows/terminal/install">Windows Terminal</a> as your command prompt.</p>
</Admonition>
<p><strong>Recommended IDEs:</strong> <a href="https://visualstudio.microsoft.com/">Visual Studio 2026+</a>, <a href="https://www.jetbrains.com/rider/">JetBrains Rider 2025.3+</a>, or <a href="https://code.visualstudio.com/">VS Code</a></p>
<h3>Install ModernUO</h3>
<ol>
<li>Navigate to the folder where you want to install ModernUO.</li>
<li>Using <em>Windows Terminal</em>, run:</li>
</ol>
<CodeBlock language="bash">{`git clone https://github.com/modernuo/modernuo
cd modernuo`}</CodeBlock>
</>
),
macos: (
<>
<h3>Prerequisites</h3>
<ol>
<li>Download and install the latest <a href="https://dotnet.microsoft.com/download/dotnet/10.0">.NET 10 SDK</a></li>
<li>Using <em>terminal</em>, install <a href="https://brew.sh">Homebrew</a> and dependencies:</li>
</ol>
<CodeBlock language="bash">{`/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"
brew install git icu4c libdeflate argon2`}</CodeBlock>
<p><strong>Recommended IDEs:</strong> <a href="https://www.jetbrains.com/rider/">JetBrains Rider 2025.3+</a> or <a href="https://code.visualstudio.com/">VS Code</a></p>
<h3>Install ModernUO</h3>
<ol>
<li>Using <em>terminal</em>, navigate to the folder where you want to install ModernUO and run:</li>
</ol>
<CodeBlock language="bash">{`git clone https://github.com/modernuo/modernuo
cd modernuo`}</CodeBlock>
</>
),
linux: (
<>
<h3>Prerequisites</h3>
<ol>
<li>Download and install the latest <a href="https://docs.microsoft.com/en-us/dotnet/core/install/linux">.NET 10 SDK</a></li>
<li>Using <em>bash</em>, install git and dependencies:</li>
</ol>
<p><strong>Debian / Ubuntu:</strong></p>
<CodeBlock language="bash">{'sudo apt update && sudo apt install git libicu-dev libdeflate-dev libargon2-dev'}</CodeBlock>
<p><strong>Fedora:</strong></p>
<CodeBlock language="bash">{'sudo dnf install git libicu libdeflate-devel libargon2-devel'}</CodeBlock>
<Admonition type="note">
<p>The exact package names may vary by distribution. Consult your distribution's package manager documentation.</p>
</Admonition>
<p><strong>Recommended IDEs:</strong> <a href="https://www.jetbrains.com/rider/">JetBrains Rider 2025.3+</a> or <a href="https://code.visualstudio.com/">VS Code</a></p>
<h3>Install ModernUO</h3>
<ol>
<li>Using <em>bash</em>, navigate to the folder where you want to install ModernUO and run:</li>
</ol>
<CodeBlock language="bash">{`git clone https://github.com/modernuo/modernuo
cd modernuo`}</CodeBlock>
</>
),
}}
</OsTabs>

View file

@ -0,0 +1,266 @@
---
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.
<OsTabs>
{{
windows: (
<>
<p>Using <em>Windows Terminal</em>, <em>Git Bash</em>, or <em>PowerShell</em>, run:</p>
<CodeBlock language="bash">{`cd Distribution
ModernUO.exe`}</CodeBlock>
</>
),
macos: (
<>
<p>Using <em>terminal</em>, run:</p>
<CodeBlock language="bash">{`cd Distribution
dotnet ModernUO.dll`}</CodeBlock>
</>
),
linux: (
<>
<p>Using <em>terminal</em>, run:</p>
<CodeBlock language="bash">{`cd Distribution
dotnet ModernUO.dll`}</CodeBlock>
</>
),
}}
</OsTabs>
## 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"]
```
<OsTabs>
{{
windows: (
<>
<p>Find what is using the port:</p>
<CodeBlock language="bash">{'netstat -ano | findstr :2593'}</CodeBlock>
</>
),
macos: (
<>
<p>Find what is using the port:</p>
<CodeBlock language="bash">{'lsof -i :2593'}</CodeBlock>
</>
),
linux: (
<>
<p>Find what is using the port:</p>
<CodeBlock language="bash">{'ss -tlnp | grep 2593'}</CodeBlock>
</>
),
}}
</OsTabs>
### 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
<OsTabs>
{{
windows: (
<>
<p>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:</p>
<CodeBlock language="bash">{'./publish.cmd'}</CodeBlock>
</>
),
macos: (
<>
<p>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:</p>
<CodeBlock language="bash">{'./publish.sh'}</CodeBlock>
</>
),
linux: (
<>
<p>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:</p>
<CodeBlock language="bash">{'./publish.sh'}</CodeBlock>
</>
),
}}
</OsTabs>
:::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.

View file

@ -0,0 +1,166 @@
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: '<a href="https://muo.gg/discord" target="_blank" rel="noopener noreferrer" class="navbar-icon" aria-label="Discord" title="Discord"><svg viewBox="0 0 24 24" fill="currentColor" width="22" height="22"><path d="M20.317 4.37a19.791 19.791 0 0 0-4.885-1.515.074.074 0 0 0-.079.037c-.21.375-.444.864-.608 1.25a18.27 18.27 0 0 0-5.487 0 12.64 12.64 0 0 0-.617-1.25.077.077 0 0 0-.079-.037A19.736 19.736 0 0 0 3.677 4.37a.07.07 0 0 0-.032.027C.533 9.046-.32 13.58.099 18.057a.082.082 0 0 0 .031.057 19.9 19.9 0 0 0 5.993 3.03.078.078 0 0 0 .084-.028c.462-.63.874-1.295 1.226-1.994a.076.076 0 0 0-.041-.106 13.107 13.107 0 0 1-1.872-.892.077.077 0 0 1-.008-.128 10.2 10.2 0 0 0 .372-.292.074.074 0 0 1 .077-.01c3.928 1.793 8.18 1.793 12.062 0a.074.074 0 0 1 .078.01c.12.098.246.198.373.292a.077.077 0 0 1-.006.127 12.299 12.299 0 0 1-1.873.892.077.077 0 0 0-.041.107c.36.698.772 1.362 1.225 1.993a.076.076 0 0 0 .084.028 19.839 19.839 0 0 0 6.002-3.03.077.077 0 0 0 .032-.054c.5-5.177-.838-9.674-3.549-13.66a.061.061 0 0 0-.031-.03zM8.02 15.33c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.956-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.956 2.418-2.157 2.418zm7.975 0c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.956-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.947 2.418-2.157 2.418z"/></svg></a>',
},
{
type: 'html',
position: 'right',
value: '<a href="https://github.com/modernuo/ModernUO" target="_blank" rel="noopener noreferrer" class="navbar-icon" aria-label="GitHub" title="GitHub"><svg viewBox="0 0 24 24" fill="currentColor" width="22" height="22"><path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/></svg></a>',
},
{
type: 'html',
position: 'right',
value: '<a href="https://github.com/sponsors/modernuo" target="_blank" rel="noopener noreferrer" class="navbar-icon navbar-icon--sponsor" aria-label="Sponsor" title="Sponsor"><svg viewBox="0 0 24 24" fill="currentColor" width="20" height="20"><path d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z"/></svg></a>',
},
],
},
footer: {},
prism: {
theme: prismThemes.github,
darkTheme: prismThemes.dracula,
additionalLanguages: ['csharp', 'bash', 'json', 'powershell'],
},
} satisfies Preset.ThemeConfig,
};
export default config;

18448
website/package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

47
website/package.json Normal file
View file

@ -0,0 +1,47 @@
{
"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"
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,39 @@
{
"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
}
}
]
}

View file

@ -0,0 +1,216 @@
{
"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
}
}
]
}

View file

@ -0,0 +1,250 @@
{
"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
}
}
]
}

View file

@ -0,0 +1,93 @@
{
"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
}
}
]
}

View file

@ -0,0 +1,965 @@
{
"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
}
}
]
}

View file

@ -0,0 +1,142 @@
{
"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
}
}
]
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,134 @@
{
"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
}
}
]
}

View file

@ -0,0 +1,116 @@
{
"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
}
}
]
}

View file

@ -0,0 +1,188 @@
{
"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."
}
]
}

View file

@ -0,0 +1,37 @@
{
"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."
}
]
}

View file

@ -0,0 +1,298 @@
{
"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
}
}
]
}

View file

@ -0,0 +1,907 @@
{
"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
}
}
]
}

View file

@ -0,0 +1,84 @@
{
"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
}
}
]
}

View file

@ -0,0 +1,300 @@
{
"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
}
}
]
}

View file

@ -0,0 +1,168 @@
{
"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
}
}
]
}

View file

@ -0,0 +1,56 @@
{
"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
}
}
]
}

View file

@ -0,0 +1,989 @@
{
"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
}
}
]
}

View file

@ -0,0 +1,144 @@
{
"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
}
}
]
}

View file

@ -0,0 +1,32 @@
{
"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."
}
]
}

View file

@ -0,0 +1,117 @@
{
"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
}
}
]
}

View file

@ -0,0 +1,145 @@
{
"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
}
}
]
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,137 @@
{
"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
}
}
]
}

View file

@ -0,0 +1,75 @@
{
"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
}
}
]
}

View file

@ -0,0 +1,122 @@
{
"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."
}
]
}

View file

@ -0,0 +1,217 @@
{
"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."
}
]
}

View file

@ -0,0 +1,307 @@
{
"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
}
}
]
}

View file

@ -0,0 +1,276 @@
{
"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
}
}
]
}

View file

@ -0,0 +1,69 @@
{
"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
}
}
]
}

View file

@ -0,0 +1,120 @@
{
"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
}
}
]
}

View file

@ -0,0 +1,594 @@
{
"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
}
}
]
}

View file

@ -0,0 +1,212 @@
{
"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
}
}
]
}

View file

@ -0,0 +1,76 @@
{
"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
}
}
]
}

View file

@ -0,0 +1,107 @@
{
"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
}
}
]
}

View file

@ -0,0 +1,459 @@
{
"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
}
}
]
}

View file

@ -0,0 +1,403 @@
{
"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
}
}
]
}

View file

@ -0,0 +1,173 @@
{
"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
}
}
]
}

View file

@ -0,0 +1,70 @@
{
"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
}
}
]
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,309 @@
{
"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
}
}
]
}

View file

@ -0,0 +1,104 @@
{
"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
}
}
]
}

View file

@ -0,0 +1,65 @@
{
"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
}
}
]
}

View file

@ -0,0 +1,501 @@
{
"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
}
}
]
}

View file

@ -0,0 +1,427 @@
{
"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
}
}
]
}

View file

@ -0,0 +1,185 @@
{
"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
}
}
]
}

View file

@ -0,0 +1,534 @@
{
"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
}
}
]
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,256 @@
{
"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
}
}
]
}

View file

@ -0,0 +1,276 @@
{
"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
}
}
]
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,275 @@
{
"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
}
}
]
}

View file

@ -0,0 +1,223 @@
{
"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
}
}
]
}

View file

@ -0,0 +1,216 @@
{
"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
}
}
]
}

View file

@ -0,0 +1,89 @@
{
"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
}
}
]
}

File diff suppressed because it is too large Load diff

50
website/sidebars.ts Normal file
View file

@ -0,0 +1,50 @@
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;

View file

@ -0,0 +1,82 @@
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&mdash;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&rsquo;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 (
<div className={styles.featureCard}>
<div className={styles.featureHeader}>
<span className={styles.featureIcon}>{icon}</span>
<Heading as="h3">{title}</Heading>
</div>
<p>{description}</p>
</div>
);
}
export default function HomepageFeatures(): ReactNode {
return (
<section className={styles.features}>
<div className="container">
<div className={styles.grid}>
{FeatureList.map((props, idx) => (
<Feature key={idx} {...props} />
))}
</div>
</div>
</section>
);
}

View file

@ -0,0 +1,80 @@
.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;
}
}

View file

@ -0,0 +1,52 @@
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 (
<Tabs groupId="os" defaultValue={defaultOs}>
{children.windows && (
<TabItem value="windows" label="Windows">
{children.windows}
</TabItem>
)}
{children.macos && (
<TabItem value="macos" label="macOS">
{children.macos}
</TabItem>
)}
{children.linux && (
<TabItem value="linux" label="Linux">
{children.linux}
</TabItem>
)}
</Tabs>
);
}

View file

@ -0,0 +1,47 @@
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 (
<section className={styles.quickStart}>
<div className="container">
<div className={styles.content}>
<h2 className={styles.heading}>Up and running in minutes</h2>
<p className={styles.subheading}>
Clone, build, and launch your server with three commands.
</p>
<div className={styles.codeWrapper}>
<OsTabs>
{{
linux: (
<CodeBlock language="bash">
{`git clone https://github.com/modernuo/modernuo\ncd modernuo\n./publish.sh release linux x64`}
</CodeBlock>
),
macos: (
<CodeBlock language="bash">
{`git clone https://github.com/modernuo/modernuo\ncd modernuo\n./publish.sh release osx x64`}
</CodeBlock>
),
windows: (
<CodeBlock language="bash">
{`git clone https://github.com/modernuo/modernuo\ncd modernuo\n./publish.cmd release win x64`}
</CodeBlock>
),
}}
</OsTabs>
</div>
<Link
className="button button--lg button--outline-gold"
to="/docs/getting-started/installation"
>
View full setup guide
</Link>
</div>
</div>
</section>
);
}

View file

@ -0,0 +1,27 @@
.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;
}

View file

@ -0,0 +1,97 @@
/* 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;
}

View file

@ -0,0 +1,71 @@
.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;
}
}

View file

@ -0,0 +1,50 @@
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 (
<header className={styles.hero}>
<div className="container">
<div className={styles.logoWrapper}>
<div className={styles.glow} />
<img
src="/branding/logo.svg"
alt="ModernUO"
className={styles.logo}
/>
</div>
<p className={styles.tagline}>
The Ultima Online server emulator for the modern era
</p>
<div className={styles.buttons}>
<Link
className="button button--lg button--gold"
to="/docs/getting-started/installation"
>
Get Started
</Link>
</div>
</div>
</header>
);
}
export default function Home(): ReactNode {
return (
<Layout
title="Ultima Online Server Emulator"
description="ModernUO is a modern, open-source Ultima Online server emulator built on .NET 10. High performance, code-generated serialization, and an active community."
>
<HomepageHeader />
<main>
<HomepageFeatures />
<QuickStart />
</main>
</Layout>
);
}

View file

@ -0,0 +1,161 @@
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<Counts>({});
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: (
<svg viewBox="0 0 24 24" fill="currentColor" width="16" height="16">
<path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z" />
</svg>
),
},
{
label: 'Discord',
href: 'https://muo.gg/discord',
countKey: 'discord',
icon: (
<svg viewBox="0 0 24 24" fill="currentColor" width="16" height="16">
<path d="M20.317 4.37a19.791 19.791 0 0 0-4.885-1.515.074.074 0 0 0-.079.037c-.21.375-.444.864-.608 1.25a18.27 18.27 0 0 0-5.487 0 12.64 12.64 0 0 0-.617-1.25.077.077 0 0 0-.079-.037A19.736 19.736 0 0 0 3.677 4.37a.07.07 0 0 0-.032.027C.533 9.046-.32 13.58.099 18.057a.082.082 0 0 0 .031.057 19.9 19.9 0 0 0 5.993 3.03.078.078 0 0 0 .084-.028c.462-.63.874-1.295 1.226-1.994a.076.076 0 0 0-.041-.106 13.107 13.107 0 0 1-1.872-.892.077.077 0 0 1-.008-.128 10.2 10.2 0 0 0 .372-.292.074.074 0 0 1 .077-.01c3.928 1.793 8.18 1.793 12.062 0a.074.074 0 0 1 .078.01c.12.098.246.198.373.292a.077.077 0 0 1-.006.127 12.299 12.299 0 0 1-1.873.892.077.077 0 0 0-.041.107c.36.698.772 1.362 1.225 1.993a.076.076 0 0 0 .084.028 19.839 19.839 0 0 0 6.002-3.03.077.077 0 0 0 .032-.054c.5-5.177-.838-9.674-3.549-13.66a.061.061 0 0 0-.031-.03zM8.02 15.33c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.956-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.956 2.418-2.157 2.418zm7.975 0c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.956-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.947 2.418-2.157 2.418z" />
</svg>
),
},
{
label: 'Reddit',
href: 'https://muo.gg/reddit',
icon: (
<svg viewBox="0 0 24 24" fill="currentColor" width="16" height="16">
<path d="M12 0A12 12 0 0 0 0 12a12 12 0 0 0 12 12 12 12 0 0 0 12-12A12 12 0 0 0 12 0zm5.01 4.744c.688 0 1.25.561 1.25 1.249a1.25 1.25 0 0 1-2.498.056l-2.597-.547-.8 3.747c1.824.07 3.48.632 4.674 1.488.308-.309.73-.491 1.207-.491.968 0 1.754.786 1.754 1.754 0 .716-.435 1.333-1.01 1.614a3.111 3.111 0 0 1 .042.52c0 2.694-3.13 4.87-7.004 4.87-3.874 0-7.004-2.176-7.004-4.87 0-.183.015-.366.043-.534A1.748 1.748 0 0 1 4.028 12c0-.968.786-1.754 1.754-1.754.463 0 .898.196 1.207.49 1.207-.883 2.878-1.43 4.744-1.487l.885-4.182a.342.342 0 0 1 .14-.197.35.35 0 0 1 .238-.042l2.906.617a1.214 1.214 0 0 1 1.108-.701zM9.25 12C8.561 12 8 12.562 8 13.25c0 .687.561 1.248 1.25 1.248.687 0 1.248-.561 1.248-1.249 0-.688-.561-1.249-1.249-1.249zm5.5 0c-.687 0-1.248.561-1.248 1.25 0 .687.561 1.248 1.249 1.248.688 0 1.249-.561 1.249-1.249 0-.687-.562-1.249-1.25-1.249zm-5.466 3.99a.327.327 0 0 0-.231.094.33.33 0 0 0 0 .463c.842.842 2.484.913 2.961.913.477 0 2.105-.056 2.961-.913a.361.361 0 0 0 .029-.463.33.33 0 0 0-.464 0c-.547.533-1.684.73-2.512.73-.828 0-1.979-.196-2.512-.73a.326.326 0 0 0-.232-.095z" />
</svg>
),
},
{
label: 'Sponsor',
href: 'https://github.com/sponsors/modernuo',
icon: (
<svg viewBox="0 0 24 24" fill="currentColor" width="16" height="16">
<path d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z" />
</svg>
),
},
];
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 (
<footer className={styles.footer}>
<div className="container">
<div className={styles.content}>
<div className={styles.docs}>
<h4 className={styles.heading}>Documentation</h4>
<ul className={styles.linkList}>
{docLinks.map((link, idx) => (
<li key={idx}>
{'to' in link ? (
<Link to={link.to} className={styles.docLink}>{link.label}</Link>
) : (
<a href={link.href} className={styles.docLink}>{link.label}</a>
)}
</li>
))}
</ul>
</div>
<div className={styles.social}>
<h4 className={styles.heading}>Community</h4>
<div className={styles.socialButtons}>
{socialLinks.map((link, idx) => {
const count = link.countKey ? counts[link.countKey] : undefined;
return (
<a
key={idx}
href={link.href}
target="_blank"
rel="noopener noreferrer"
className={styles.socialLink}
>
<span className={styles.icon}>{link.icon}</span>
<span>{link.label}</span>
{count != null && (
<span className={styles.count}>{formatCount(count)}</span>
)}
</a>
);
})}
</div>
</div>
</div>
<div className={styles.bottom}>
<span>Copyright {new Date().getFullYear()} ModernUO Development Team</span>
</div>
</div>
</footer>
);
}

View file

@ -0,0 +1,105 @@
.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;
}

0
website/static/.nojekyll Normal file
View file

1
website/static/CNAME Normal file
View file

@ -0,0 +1 @@
modernuo.com

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

View file

@ -0,0 +1 @@
<svg height="529.15997" viewBox="0 0 529.15997 529.15997" width="529.15997" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><clipPath id="a"><path d="m2512.85 434.602c664.19 226.828 1108.92 846.878 1108.92 1549.778 0 506.38-229.59 976.52-629.89 1289.87l-48.49 37.96v-1419.24c0-285.63-16.44-484.51-48.85-591.12-31.25-102.71-92.53-204.08-182.15-301.28-59.65-64.691-126.61-118.961-199.54-161.781v2695.511l-20.68 6.77c-163.79 53.55-334.64 80.71-507.81 80.71-116.47 0-232.75-12.43-346.2-36.98v323.08c114.09 20.48 230.43 30.86 346.2 30.86 1077.66 0 1954.37-876.72 1954.37-1954.36 0-877.2-584.34-1645.45-1425.88-1881.771z"/></clipPath><linearGradient id="b" gradientTransform="matrix(0 -3981.4 -3981.4 0 2788.45 3927.9)" gradientUnits="userSpaceOnUse" spreadMethod="pad" x1="0" x2="1" y1="0" y2="0"><stop offset="0" stop-color="#bfbdbb"/><stop offset=".248485" stop-color="#fbfcfc"/><stop offset=".484848" stop-color="#959696"/><stop offset=".70303" stop-color="#f0f0f0"/><stop offset="1" stop-color="#8d8d8d"/></linearGradient><clipPath id="c"><path d="m1984.36 30.0117c-1077.637 0-1954.36 876.7073-1954.36 1954.3683 0 877.19 584.355 1645.42 1425.89 1881.75v-2120.73c0-174.88 16.68-297.63 50.98-375.3 36.14-81.88 96.23-147.98 178.61-196.45 80.54-47.37 177.6-71.39 288.48-71.39 116.83 0 219.8 26.54 306.04 78.89 12.05 7.32 23.7 15.02 34.9 23.06v-449.19c-98.23-27.68-208.6-41.7-328.46-41.7-206.52 0-387.97 45.481-539.3 135.2-150.47 89.179-259.14 201.46-323.02 333.71-65.54 135.72-98.77 374.85-98.77 710.74v1419.24l-48.491-37.96c-400.308-313.35-629.89-783.49-629.89-1289.87 0-902.87 734.531-1637.411 1637.391-1637.411 111.23 0 222.24 11.269 330.54 33.543v-322.4534c-108.89-18.6289-219.96-28.0469-330.54-28.0469z"/></clipPath><linearGradient id="d" gradientTransform="matrix(0 -3981.4 -3981.4 0 1172.45 3927.91)" gradientUnits="userSpaceOnUse" spreadMethod="pad" x1="0" x2="1" y1="0" y2="0"><stop offset="0" stop-color="#ae9964"/><stop offset=".248485" stop-color="#fbe383"/><stop offset=".484848" stop-color="#8d7036"/><stop offset=".714286" stop-color="#c9b14e"/><stop offset="1" stop-color="#7f6740"/></linearGradient><g transform="matrix(.13333333 0 0 -.13333333 0 529.16)"><path d="m1984.36 3968.74c-128.6 0-254.34-12.45-376.2-35.93v-385.64c120.72 29.1 246.68 44.61 376.2 44.61 173.94 0 341.47-27.89 498.49-79.23v-2724.37c92.71 47.199 177.2 111.371 251.6 192.058 92.7 100.542 156.23 205.822 188.8 312.892 33.27 109.43 50.14 311.26 50.14 599.84v1357.66c376.17-294.47 618.38-752.6 618.38-1266.25 0-712.39-465.88-1317.919-1108.92-1528.189v-392.7613c853.7 221.6793 1485.88 998.8603 1485.88 1920.9503 0 1094.17-890.17 1984.36-1984.37 1984.36" fill="#dfdfdd"/><g clip-path="url(#a)"><path d="m2512.85 434.602c664.19 226.828 1108.92 846.878 1108.92 1549.778 0 506.38-229.59 976.52-629.89 1289.87l-48.49 37.96v-1419.24c0-285.63-16.44-484.51-48.85-591.12-31.25-102.71-92.53-204.08-182.15-301.28-59.65-64.691-126.61-118.961-199.54-161.781v2695.511l-20.68 6.77c-163.79 53.55-334.64 80.71-507.81 80.71-116.47 0-232.75-12.43-346.2-36.98v323.08c114.09 20.48 230.43 30.86 346.2 30.86 1077.66 0 1954.37-876.72 1954.37-1954.36 0-877.2-584.34-1645.45-1425.88-1881.771v331.993" fill="url(#b)"/></g><path d="m1973.96 1132.26c-105.46 0-197.39 22.62-273.26 67.24-76.85 45.22-132.83 106.7-166.38 182.71-32.59 73.78-48.43 192.58-48.43 363.19v1226.51 540.64 392.76c-853.699-221.68-1485.89-998.85-1485.89-1920.93 0-1094.2 890.184-1984.3682812 1984.36-1984.3682812 123.1 0 243.58 11.3476812 360.54 32.9179812v384.8203c-115.94-26.668-236.61-40.781-360.54-40.781-886.32 0-1607.391 721.081-1607.391 1607.411 0 513.65 242.207 971.78 618.383 1266.25v-1357.66c0-340.44 34.228-583.96 101.758-723.78 66.48-137.66 179.09-254.221 334.73-346.471 156.02-92.489 342.61-139.387 554.6-139.387 132.62 0 252.67 16.719 358.46 49.25v535.068c-23.54-22.21-50.33-42.57-80.46-60.85-81.5-49.46-179.23-74.54-290.48-74.54" fill="#e9cf72"/><g clip-path="url(#c)"><path d="m1984.36 30.0117c-1077.637 0-1954.36 876.7073-1954.36 1954.3683 0 877.19 584.355 1645.42 1425.89 1881.75v-2120.73c0-174.88 16.68-297.63 50.98-375.3 36.14-81.88 96.23-147.98 178.61-196.45 80.54-47.37 177.6-71.39 288.48-71.39 116.83 0 219.8 26.54 306.04 78.89 12.05 7.32 23.7 15.02 34.9 23.06v-449.19c-98.23-27.68-208.6-41.7-328.46-41.7-206.52 0-387.97 45.481-539.3 135.2-150.47 89.179-259.14 201.46-323.02 333.71-65.54 135.72-98.77 374.85-98.77 710.74v1419.24l-48.491-37.96c-400.308-313.35-629.89-783.49-629.89-1289.87 0-902.87 734.531-1637.411 1637.391-1637.411 111.23 0 222.24 11.269 330.54 33.543v-322.4534c-108.89-18.6289-219.96-28.0469-330.54-28.0469" fill="url(#d)"/></g></g></svg>

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

View file

@ -0,0 +1 @@
<svg height="700pt" preserveAspectRatio="xMidYMid meet" viewBox="0 0 700 700" width="700pt" xmlns="http://www.w3.org/2000/svg"><g transform="matrix(.1 0 0 -.1 0 700)"><path d="m3278 6996c-1-2-41-6-88-10-98-8-120-11-252-33-87-14-98-18-99-37-7-128-2-656 6-656 6 0 28 5 50 10 55 12 89 18 170 31 39 5 84 12 100 15 51 8 245 18 350 18 249 0 499-35 752-105l113-31 2-2404 3-2403 85 52c133 81 196 131 315 251 170 173 298 369 360 557 28 84 27 77 48 209 46 276 50 435 51 1937 1 727 4 1324 6 1327 13 12 304-264 415-394 161-189 328-458 442-715 28-64 118-318 128-362 2-10 15-67 29-128 14-60 27-128 31-150 3-22 7-53 10-70 12-75 15-106 22-190 12-155 6-443-11-545-3-14-7-47-11-75-3-27-8-59-10-70-3-11-7-31-9-45-66-371-224-753-446-1076-250-365-578-664-977-888-123-69-360-174-444-196l-39-11v-348-349l113 33c454 136 815 328 1231 655 56 43 241 218 313 293 479 507 792 1130 912 1817 11 63 23 142 26 175 4 33 9 71 11 85 9 48 11 525 4 605-17 176-22 220-25 225-2 3-6 30-10 60-7 55-58 298-75 355-98 332-208 594-352 839-391 663-964 1169-1666 1469-297 127-647 220-957 253-27 3-63 8-80 10-34 6-541 14-547 10z"/><path d="m2585 6879c-96-22-409-132-429-150-6-5-16-9-23-9-20 0-297-139-404-203-260-155-464-313-675-522-184-183-222-226-344-386-288-382-491-802-603-1254-77-309-102-518-102-860 1-194 7-327 20-406 2-13 6-46 10-74 9-76 16-116 25-160 5-22 11-53 14-70 29-160 113-433 193-625 142-346 374-711 633-1000 92-102 273-277 359-346 13-10 28-24 35-29 51-48 406-295 422-295 2 0 23-12 45-26 59-37 278-145 389-192 269-114 649-215 920-246 292-34 694-28 948 13 37 6 78 13 92 15l25 3v338l1 337-36-5c-19-3-89-15-155-26-230-39-591-46-795-15-315 47-550 113-799 223-186 83-415 217-556 328-16 13-35 27-40 31-90 64-340 307-431 417-303 367-504 781-597 1230-22 106-29 144-43 255-22 166-22 556 0 666 2 12 7 45 11 75 7 58 44 236 69 334 33 132 126 375 197 517 61 121 221 385 248 408 3 3 26 32 50 65 25 33 47 62 50 65s40 43 81 89c62 70 127 136 234 237 16 14 126 104 129 104 1 0 2-593 3-1318 1-1162 5-1521 19-1612 2-14 6-56 10-95 3-38 8-81 10-95s7-45 10-70c48-362 168-617 395-839 261-255 616-426 989-475 281-37 603-21 860 43 46 12 84 21 85 21s2 211 1 469c0 311-3 467-10 463-5-3-28-20-50-37-49-39-161-105-179-105-6 0-16-4-22-9-13-14-147-50-229-62-121-18-307-12-400 13-22 5-47 12-55 13-36 8-103 36-166 70-220 119-344 305-379 565-3 25-8 61-12 80-3 19-8 901-10 1960s-4 1981-6 2050l-2 126z"/></g></svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

2619
website/static/commands.html Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,194 @@
# 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 '<', '&lt;' -replace '>', '&gt;'
# 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 '<!-- PACKETS -->', $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

Some files were not shown because too many files have changed in this diff Show more