feat(items): item rule flags (tradable, droppable, storable, sellable, repairable) - #983
Merged
Merged
Conversation
Adds IsTradable, IsDroppable, IsStorable, IsSellableToNpc, IsPersonalStoreSellable and IsRepairable to ItemDefinition. They are true unless set otherwise, and the migration adds them with the default true, so existing items keep allowing everything. The admin panel shows them as checkboxes on the item pages. They match the rule flags of the MuMain client item data (tradable, droppable, storable, sellable, personalShopSellable, repairable), so both sides can allow the same actions.
- MoveItemAction: an item can't be moved into a trade, the vault or the personal store when IsTradable, IsStorable or IsPersonalStoreSellable is false. - SellItemToNpcAction: IsSellableToNpc. - DropItemAction: IsDroppable. - ItemRepairAction: IsRepairable; repair all skips such items. A refused action shows a message to the player and logs a warning: the client should not have sent it, so its item data may differ from the server (or the client is modified). IsBoundToCharacter keeps working as before.
ItemRules lists the Season 6 items which don't allow every action, with the values of the MuMain client item data (src/bin/Data/Items). It is applied by the Season 6 initialization and, for existing databases, by the mandatory AddItemRuleFlagsPlugIn update. Two differences to the client data: - The Wizards Ring is bound to the character here, so it is not tradable, storable or sellable in a personal store (the client allows that at +0; its data will be adjusted). - The Dark Horse and Dark Raven stay repairable, because the server repairs trainable pets.
- Trade: the rules are also checked when the trade completes. Items which another window (e.g. the chaos machine) left in the temporary storage go back to the inventory when a trade opens; before, they could be traded, or were lost when the trade was cancelled. - Personal store: an item which isn't sellable there can't be bought, also when it was put into the store before the rules existed. - Repair: new Item.CanBeRepaired() (wearable and IsRepairable). Items which can't be worn are no longer repaired in any version: their durability is the stack size, and a repair reduced it to one. Offline auto repair and the bots' merchant repair skip items which can't be repaired instead of logging a warning on every attempt. - Bots keep items which can't be sold to NPCs instead of trying to sell and then destroying them. - ItemRules.Apply no longer fails on duplicate item definitions; every copy gets the rule. - The remarks of ItemRules say that the pets and transformation rings follow the client and can't be repaired. - Tests: the repair test uses a wearable item and checks the full durability; new tests for stacks, leftover items when a trade opens, the trade completion and personal store purchases. The data test builds the Season 6 data once for its read-only tests.
IsRepairable and Item.CanBeRepaired() are about the repair by an NPC or the inventory repair. Other ways to restore the durability don't use them: the Horn of Fenrir, which can't be repaired normally, is still repaired with a Jewel of Bless (the repair target items of the jewel of bless consume handler). A test covers that.
This was referenced Sep 26, 2026
Mosch0512
marked this pull request as draft
September 26, 2026 20:01
- ItemRules lists the blocked actions of each item instead of combining them with bitwise operations: Codacy's Sonar analysis didn't see the Flags attribute of the nested enum and flagged every combination. - TradeButtonAction checks both traders' items with two variables instead of a non-short-circuit operator, and gets the missing blank line after the block.
The Icarus map requires the CanFly attribute, which the wings, the Horn of Dinorant and the Horn of Fenrir give. The Dark Horse didn't, while the client counts it as a flying mount (its data tags it as flying), so client and server disagreed about entering and staying in Icarus. The Season 6 initialization now gives the Dark Horse the CanFly power-up, and the mandatory AddDarkHorseCanFlyPlugIn update adds it to existing databases (only once).
Mosch0512
marked this pull request as ready for review
September 27, 2026 13:23
Member
ReviewI read the full diff on head VerificationI ran the pipeline steps on Linux with the .NET 10.0.401 SDK, in Release with
The red What I checked
Minor notes (none of them block the merge)
Generated by Claude Code |
This was referenced Sep 27, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Server part of the data-driven items plan in MuMain (phase 3 / "PR A"): the client moved its item rules into data in sven-n/MuMain#635. This adds the same rules to OpenMU, so the server enforces what the client shows. Reviewed first in the fork (Mosch0512#1).
What changes
New fields on
ItemDefinition,trueunless set otherwise:IsTradableMoveItemAction(into a trade) andTradeButtonAction(when the trade completes)tradableIsStorableMoveItemAction(into the vault)storableIsPersonalStoreSellableMoveItemAction(into the personal store) andBuyRequestAction(when someone buys it)personalShopSellableIsSellableToNpcSellItemToNpcActionsellableIsDroppableDropItemActiondroppableIsRepairableItemRepairAction, via the newItem.CanBeRepaired()(repair all, offline auto repair and bot repairs skip such items)repairableIsRepairableonly covers the normal repair (NPC or inventory repair). Other ways to restore durability don't depend on it. For example, the Horn of Fenrir can't be repaired normally, but a Jewel of Bless still repairs it (configured as a repair target item of the Jewel of Bless consume handler). A test covers that.PlayerMessageentries) and logs a warning. The client should not have sent the request, so either its item data differs from the server or it is a modified client.IsBoundToCharacterworks as before (owner-only pickup, no moves between storages).AutoFields).Data
AddItemRuleFlagsadds the columns with the defaulttrue, so existing items keep allowing everything.VersionSeasonSix/Items/ItemRuleslists the 156 Season 6 items that don't allow every action. The values come from the client item data. The list is applied by the Season 6 initialization and, for existing databases, by the mandatoryAddItemRuleFlagsPlugInupdate.Two deliberate differences to the client data (and one deliberate match):
Dark Horse can fly
Fixes #982.
The Icarus map requires
CanFly. The wings, the Horn of Dinorant and the Horn of Fenrir give it; the Dark Horse didn't. The client counts the Dark Horse as a flying mount, so client and server disagreed about Icarus.CanFlypower-up.AddDarkHorseCanFlyPlugInupdate adds it to existing databases, only once.This is data only, like
CanFlyon the other items; there is no schema change.Why repair matters
For items that can't be worn, OpenMU counts durability as the stack size, and the maximum durability of one piece is 1. A repair request for a stack sets the durability to 1, and because the durability is above the maximum, the repair price is negative. Verified against master, in a unit test (a stack of 3 apples becomes 1 apple, and the player gains 1,158 Zen) and in the game: a MuMain client whose data allowed repairing apples sent the request, and the server turned the stack into one apple while adding Zen.
The normal client never sends this request, because it blocks repairing potions, so players don't run into it. A modified client could, and could farm Zen with it.
Item.CanBeRepaired()now refuses items that can't be worn, in every game version and for custom items, independent of the item data.Not included
Tests
ItemRuleFlagsTests:MoveItemActionTests: trade, vault and personal store.ItemRulesDataTest:ItemConsumptionTest: the Jewel of Bless repairs an item that can't be repaired normally.DarkHorseCanFlyTest: a new Season 6 database lets the Dark Horse fly, and the update adds the power-up once.MUnique.OpenMU.Tests(1033) andMUnique.OpenMU.Persistence.Initialization.Tests(30, 6 skipped), in Release as in the pipeline.Tested in game
Against a local all-in-one container built from this branch, with an existing Season 6 database:
The update plugins here only change default values; #984 proposes showing such changes on the Updates page before they are applied.