Skip to content

Commit

Permalink
Guard item.container against cycles (foundryvtt#15113)
Browse files Browse the repository at this point in the history
  • Loading branch information
CarlosFdez committed Jun 16, 2024
1 parent 01aa557 commit 6a76396
Showing 1 changed file with 25 additions and 8 deletions.
33 changes: 25 additions & 8 deletions src/module/item/physical/document.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ abstract class PhysicalItemPF2e<TParent extends ActorPF2e | null = ActorPF2e | n
* The cached container of this item, if in a container, or null
* @ignore
*/
private declare _container: ContainerPF2e<ActorPF2e> | null;
private declare _container?: ContainerPF2e<ActorPF2e> | null;

/** Doubly-embedded adjustments, attachments, talismans etc. */
declare subitems: Collection<PhysicalItemPF2e<TParent>>;
Expand Down Expand Up @@ -192,9 +192,8 @@ abstract class PhysicalItemPF2e<TParent extends ActorPF2e | null = ActorPF2e | n

/** Get this item's container, returning null if it is not in a container */
get container(): ContainerPF2e<ActorPF2e> | null {
if (this.system.containerId === null) return (this._container = null);
return (this._container ??=
this.actor?.itemTypes.backpack.find((c) => c.id === this.system.containerId) ?? null);
this.updateContainerCache();
return this._container ?? null;
}

/** Returns the bulk of this item and all sub-containers */
Expand Down Expand Up @@ -250,7 +249,7 @@ abstract class PhysicalItemPF2e<TParent extends ActorPF2e | null = ActorPF2e | n
}

protected override _initialize(options?: Record<string, unknown>): void {
this._container = null;
delete this._container;
this.subitems ??= new Collection();
super._initialize(options);
}
Expand Down Expand Up @@ -293,9 +292,9 @@ abstract class PhysicalItemPF2e<TParent extends ActorPF2e | null = ActorPF2e | n
equipped.inSlot = false;
}

// Set the _container cache property to null if it no longer matches this item's container ID
// Remove the _container cache property if it no longer matches this item's container ID
if (this._container?.id !== this.system.containerId) {
this._container = null;
delete this._container;
}

// Prepare doubly-embedded items if this is of an appropriate physical-item type
Expand Down Expand Up @@ -377,7 +376,8 @@ abstract class PhysicalItemPF2e<TParent extends ActorPF2e | null = ActorPF2e | n

// Clear the container reference if it turns out to be stale
if (this._container && !this.actor.items.has(this._container.id)) {
this._container = this.system.containerId = null;
this.system.containerId = null;
delete this._container;
}

// Ensure that there is only one selected apex item, and all others are set to false
Expand Down Expand Up @@ -592,6 +592,23 @@ abstract class PhysicalItemPF2e<TParent extends ActorPF2e | null = ActorPF2e | n
return game.i18n.format("PF2E.identification.UnidentifiedItem", { item: itemType });
}

/** Updates this container's cache while also resolving cyclical references. Skips if already cached */
protected updateContainerCache(seen: string[] = []): void {
// If already cached or there is no container, return
if ("_container" in this) {
return;
}

const container = this.actor?.items.get(this.system.containerId ?? "") ?? null;
if (!container?.isOfType("backpack") || this.id === container.id || seen.includes(container.id)) {
this._container = null;
} else {
seen.push(this.id);
this._container = container;
container.updateContainerCache(seen);
}
}

/** Include mystification-related rendering instructions for views that will display this data. */
protected override traitChatData(dictionary: Record<string, string>): TraitChatData[] {
const traitData = super.traitChatData(dictionary);
Expand Down

0 comments on commit 6a76396

Please sign in to comment.