🆙 Patch GlTextureSystem & Fix effects like BBRed

- Monkey-patch the renderer's GlTextureSystem to prevent crashes from destroyed textures
This commit is contained in:
DuckieTM
2026-02-07 19:08:39 +01:00
parent 9c43cb240e
commit 8f5a9f7188
7 changed files with 84 additions and 22 deletions
+6 -3
View File
@@ -291,8 +291,6 @@ export class AvatarImage implements IAvatarImage, IAvatarEffectListener
clear: true
});
container.destroy();
//@ts-ignore
this._activeTexture.source.hitMap = null;
@@ -585,7 +583,7 @@ export class AvatarImage implements IAvatarImage, IAvatarEffectListener
this._effectIdInUse = -1;
}
if(effectChanged) this._cache.disposeInactiveActions();
if(effectChanged) this._cache.disposeInactiveActions(0);
if(this._lastActionsString != this._currentActionsString)
{
@@ -653,6 +651,11 @@ export class AvatarImage implements IAvatarImage, IAvatarEffectListener
if(animation.hasDirectionData()) this._directionOffset = animation.directionData.offset;
if(animation.hasAvatarData()) this._avatarSpriteData = animation.avatarData;
if(!this._isAnimating && (animation.spriteData?.length > 0 || animation.hasAvatarData()))
{
this._isAnimating = true;
}
}
}
}
@@ -7,6 +7,7 @@ interface IWindowReflectionAvatarState
id: number;
texture: Texture;
location: IVector3D;
verticalOffset: number;
}
export class RoomWindowReflectionState
@@ -14,7 +15,7 @@ export class RoomWindowReflectionState
private static _avatars: Map<number, IWindowReflectionAvatarState> = new Map();
private static _updateId: number = 0;
public static setAvatar(id: number, texture: Texture, location: IVector3D): void
public static setAvatar(id: number, texture: Texture, location: IVector3D, verticalOffset: number = 0): void
{
if(!texture || !location) return;
@@ -25,7 +26,8 @@ export class RoomWindowReflectionState
this._avatars.set(id, {
id,
texture,
location: storedLocation
location: storedLocation,
verticalOffset
});
// Always bump updateId so reflected walk cycles stay frame-synced
@@ -76,6 +76,7 @@ export class AvatarVisualization extends RoomObjectSpriteVisualization implement
private _isAvatarReady: boolean;
private _needsUpdate: boolean;
private _geometryUpdateCounter: number;
private _reflectionVerticalOffset: number;
private _additions: Map<number, IAvatarAddition>;
@@ -127,6 +128,7 @@ export class AvatarVisualization extends RoomObjectSpriteVisualization implement
this._isAvatarReady = false;
this._needsUpdate = false;
this._geometryUpdateCounter = -1;
this._reflectionVerticalOffset = 0;
this._additions = new Map();
}
@@ -307,7 +309,7 @@ export class AvatarVisualization extends RoomObjectSpriteVisualization implement
sprite.offsetX = ((((-1 * scale) / 2) + _local_20[0]) - ((sprite.texture.width - scale) / 2));
sprite.offsetY = (((-(sprite.texture.height) + (scale / 4)) + _local_20[1]) + this._postureOffset);
}
if(this._isLaying)
{
if(this._layInside) sprite.relativeDepth = -0.5;
@@ -329,8 +331,6 @@ export class AvatarVisualization extends RoomObjectSpriteVisualization implement
}
}
this.updateWindowReflectionSource();
const typingBubble = this.getAddition(AvatarVisualization.TYPING_BUBBLE_ID) as TypingBubbleAddition;
if(typingBubble)
@@ -451,6 +451,17 @@ export class AvatarVisualization extends RoomObjectSpriteVisualization implement
_local_21++;
}
}
const avatarSprite = this.getSprite(AvatarVisualization.SPRITE_INDEX_AVATAR);
if(avatarSprite?.texture)
{
const baseOffsetY = (-(avatarSprite.texture.height) + (scale / 4));
this._reflectionVerticalOffset = avatarSprite.offsetY - baseOffsetY;
}
this.updateWindowReflectionSource();
}
}
@@ -1007,7 +1018,7 @@ export class AvatarVisualization extends RoomObjectSpriteVisualization implement
if(sprite?.texture)
{
RoomWindowReflectionState.setAvatar(this.object.id, sprite.texture, this.object.getLocation());
RoomWindowReflectionState.setAvatar(this.object.id, sprite.texture, this.object.getLocation(), this._reflectionVerticalOffset);
return;
}
@@ -91,8 +91,8 @@ export class RoomPlane implements IRoomPlane
private _windowMasks: { leftSideLoc: number; rightSideLoc: number }[] = [];
private _lastWindowReflectionUpdateId: number = -1;
private _windowReflectionFirstSeenAt: Map<number, number> = new Map();
private _windowReflectionLastVisible: Map<number, { texture: Texture; location: IVector3D }> = new Map();
private _windowReflectionFadeOut: Map<number, { texture: Texture; location: IVector3D; startedAt: number }> = new Map();
private _windowReflectionLastVisible: Map<number, { texture: Texture; location: IVector3D; verticalOffset: number }> = new Map();
private _windowReflectionFadeOut: Map<number, { texture: Texture; location: IVector3D; verticalOffset: number; startedAt: number }> = new Map();
constructor(origin: IVector3D, location: IVector3D, leftSide: IVector3D, rightSide: IVector3D, type: number, usesMask: boolean, secondaryNormals: IVector3D[], randomSeed: number, textureOffsetX: number = 0, textureOffsetY: number = 0, textureMaxX: number = 0, textureMaxY: number = 0)
{
@@ -884,8 +884,8 @@ export class RoomPlane implements IRoomPlane
const container = new Container();
const visibleAvatarIds = new Set<number>();
const addReflectionSprite = (texture: Texture, location: IVector3D, alpha: number): boolean => {
if(!texture?.source || !location || alpha < 0) return false;
const addReflectionSprite = (texture: Texture, location: IVector3D, alpha: number, verticalOffset: number = 0): boolean => {
if(!texture?.source || texture.source.destroyed || !texture.source.style || !location || alpha < 0) return false;
const relative = Vector3d.dif(location, this._location);
const planeDistance = Math.abs(Vector3d.scalarProjection(relative, this._normal));
@@ -906,7 +906,7 @@ export class RoomPlane implements IRoomPlane
if(!closestMask || (closestMask.score > 3)) return false;
const x = (canvasWidth - ((canvasWidth * leftSideLoc) / this._leftSide.length));
const y = (canvasHeight - ((canvasHeight * rightSideLoc) / this._rightSide.length));
const y = (canvasHeight - ((canvasHeight * rightSideLoc) / this._rightSide.length)) + verticalOffset;
const sprite = new Sprite(texture);
sprite.anchor.set(0.5, 1);
@@ -922,7 +922,7 @@ export class RoomPlane implements IRoomPlane
for(const avatar of avatars)
{
if(!avatar?.texture?.source || !avatar.location) continue;
if(!avatar?.texture?.source || avatar.texture.source.destroyed || !avatar.texture.source.style || !avatar.location) continue;
let firstSeenAt = this._windowReflectionFirstSeenAt.get(avatar.id);
@@ -935,7 +935,7 @@ export class RoomPlane implements IRoomPlane
const progress = (elapsed / fadeDurationMs);
const alpha = (0.4 * progress);
if(!addReflectionSprite(avatar.texture, avatar.location, alpha)) continue;
if(!addReflectionSprite(avatar.texture, avatar.location, alpha, avatar.verticalOffset || 0)) continue;
if(!this._windowReflectionFirstSeenAt.has(avatar.id)) this._windowReflectionFirstSeenAt.set(avatar.id, firstSeenAt);
@@ -947,7 +947,8 @@ export class RoomPlane implements IRoomPlane
this._windowReflectionLastVisible.set(avatar.id, {
texture: avatar.texture,
location: storedLocation
location: storedLocation,
verticalOffset: avatar.verticalOffset || 0
});
}
@@ -955,7 +956,7 @@ export class RoomPlane implements IRoomPlane
{
if(visibleAvatarIds.has(id) || this._windowReflectionFadeOut.has(id)) continue;
if(!lastVisible.texture?.source)
if(!lastVisible.texture?.source || lastVisible.texture.source.destroyed || !lastVisible.texture.source.style)
{
this._windowReflectionLastVisible.delete(id);
this._windowReflectionFirstSeenAt.delete(id);
@@ -966,6 +967,7 @@ export class RoomPlane implements IRoomPlane
this._windowReflectionFadeOut.set(id, {
texture: lastVisible.texture,
location: lastVisible.location,
verticalOffset: lastVisible.verticalOffset,
startedAt: now
});
@@ -986,7 +988,7 @@ export class RoomPlane implements IRoomPlane
const alpha = (0.4 * (1 - (elapsed / fadeDurationMs)));
if(!addReflectionSprite(fadeOut.texture, fadeOut.location, alpha)) this._windowReflectionFadeOut.delete(id);
if(!addReflectionSprite(fadeOut.texture, fadeOut.location, alpha, fadeOut.verticalOffset)) this._windowReflectionFadeOut.delete(id);
}
if(!container.children.length)
@@ -409,7 +409,7 @@ export class RoomSpriteCanvas implements IRoomRenderingCanvas
const texture = sprite.texture;
const baseTexture = texture && texture.source;
if(!texture || !baseTexture || baseTexture.destroyed) continue;
if(!texture || !baseTexture) continue;
const spriteX = ((x + sprite.offsetX) + this._screenOffsetX);
const spriteY = ((y + sprite.offsetY) + this._screenOffsetY);
@@ -29,7 +29,7 @@ export class ExtendedSprite extends Sprite
public setTexture(texture: Texture): void
{
if(!texture || texture.source?.destroyed) texture = Texture.EMPTY;
if(!texture) texture = Texture.EMPTY;
if(texture === this.texture) return;
+45 -1
View File
@@ -1,13 +1,57 @@
import { AutoDetectOptions, Renderer, autoDetectRenderer } from 'pixi.js';
import { AutoDetectOptions, Renderer, Texture, autoDetectRenderer } from 'pixi.js';
let renderer: Renderer = null;
const patchGlTextureSystem = (r: Renderer): void =>
{
const textureSystem = (r as any).texture;
if(!textureSystem) return;
const proto = Object.getPrototypeOf(textureSystem);
if(!proto) return;
const origUpdateStyle = proto.updateStyle;
if(origUpdateStyle && !proto.__patchedUpdateStyle)
{
proto.updateStyle = function(source: any, firstCreation: boolean)
{
if(!source || source.destroyed || !source.style) return;
return origUpdateStyle.call(this, source, firstCreation);
};
proto.__patchedUpdateStyle = true;
}
const origBindSource = proto.bindSource;
if(origBindSource && !proto.__patchedBindSource)
{
proto.bindSource = function(source: any, location = 0)
{
if(!source || source.destroyed || !source.style)
{
source = Texture.EMPTY.source;
}
return origBindSource.call(this, source, location);
};
proto.__patchedBindSource = true;
}
};
export const PrepareRenderer = async (options: Partial<AutoDetectOptions>): Promise<Renderer> =>
{
renderer = await autoDetectRenderer(options);
renderer.events?.destroy();
patchGlTextureSystem(renderer);
return renderer;
};