Skip to content

Commit f8a045b

Browse files
authored
Merge pull request #369 from csfloat/fix/bugged-market-skins
Gracefully Handles "Bugged" Steam Market Skins
2 parents 1be97c2 + 6b9499f commit f8a045b

4 files changed

Lines changed: 104 additions & 1 deletion

File tree

src/lib/components/market/item_row_wrapper.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {
1717
isCharm,
1818
isBlueSkin,
1919
isHighlightCharm,
20+
isBuggedSkin,
2021
} from '../../utils/skin';
2122
import {gFilterService} from '../../services/filter';
2223
import {AppId, ContextId, Currency} from '../../types/steam_constants';
@@ -143,6 +144,10 @@ export class ItemRowWrapper extends FloatElement {
143144
return;
144145
}
145146

147+
if (isBuggedSkin(this.asset)) {
148+
return;
149+
}
150+
146151
try {
147152
this.itemInfo = await this.fetchFloat();
148153
} catch (e: any) {
@@ -236,6 +241,10 @@ export class ItemRowWrapper extends FloatElement {
236241
return nothing;
237242
}
238243

244+
if (isBuggedSkin(this.asset)) {
245+
return nothing;
246+
}
247+
239248
if (this.itemInfo && isSkin(this.asset)) {
240249
const fadePercentage = this.asset && getFadePercentage(this.asset, this.itemInfo)?.percentage;
241250

src/lib/components/market/utility_belt.ts

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,21 @@
11
import {FloatElement} from '../custom';
22
import {CustomElement, InjectBefore, InjectionMode} from '../injectors';
3-
import {css, html, HTMLTemplateResult} from 'lit';
3+
import {css, html, HTMLTemplateResult, nothing} from 'lit';
4+
import {state} from 'lit/decorators.js';
45
import '../common/ui/steam-button';
56
import './page_size';
67
import './sort_listings';
78
import '../filter/filter_container';
9+
import {Observe} from '../../utils/observers';
10+
import {isBuggedSkin} from '../../utils/skin';
11+
import {AppId, ContextId} from '../../types/steam_constants';
812

913
@CustomElement()
1014
@InjectBefore('#searchResultsRows', InjectionMode.ONCE)
1115
export class UtilityBelt extends FloatElement {
16+
@state()
17+
private buggedSkinCount = 0;
18+
1219
get marketHashName(): string {
1320
return (document.querySelector('.market_listing_nav a:nth-child(2)') as HTMLElement).innerText;
1421
}
@@ -31,6 +38,16 @@ export class UtilityBelt extends FloatElement {
3138
text-decoration: underline;
3239
font-family: 'Motiva Sans', sans-serif;
3340
}
41+
42+
.bugged-skin-warning {
43+
padding: 8px 12px;
44+
margin-top: 10px;
45+
background-color: rgba(255, 152, 0, 0.15);
46+
border: 1px solid rgba(255, 152, 0, 0.4);
47+
border-radius: 4px;
48+
color: #ffb74d;
49+
font-size: 13px;
50+
}
3451
`,
3552
];
3653

@@ -45,12 +62,53 @@ export class UtilityBelt extends FloatElement {
4562
?hidden="${!this.marketHashName}"
4663
.key="${this.marketHashName}"
4764
></csfloat-filter-container>
65+
${this.renderBuggedSkinWarning()}
4866
</div>
4967
<csfloat-ad-banner></csfloat-ad-banner>
5068
`;
5169
}
5270

71+
private countBuggedSkins(): number {
72+
try {
73+
const assets = g_rgAssets?.[AppId.CSGO]?.[ContextId.PRIMARY];
74+
if (!assets) return 0;
75+
76+
return Object.values(assets).filter((asset) => isBuggedSkin(asset)).length;
77+
} catch {
78+
return 0;
79+
}
80+
}
81+
82+
private renderBuggedSkinWarning(): HTMLTemplateResult | typeof nothing {
83+
if (this.buggedSkinCount === 0) {
84+
return nothing;
85+
}
86+
87+
return html`
88+
<div class="bugged-skin-warning">
89+
<b>${this.buggedSkinCount} skin${this.buggedSkinCount > 1 ? 's' : ''}</b> on this page cannot display
90+
float data since they're not inspectable in-game (March 4, 2026 update). Valve pls fix.
91+
</div>
92+
`;
93+
}
94+
5395
async connectedCallback() {
5496
super.connectedCallback();
97+
98+
this.buggedSkinCount = this.countBuggedSkins();
99+
100+
Observe(
101+
() => {
102+
try {
103+
return Object.keys(g_rgAssets?.[AppId.CSGO]?.[ContextId.PRIMARY] || {}).join(',');
104+
} catch {
105+
return '';
106+
}
107+
},
108+
() => {
109+
this.buggedSkinCount = this.countBuggedSkins();
110+
},
111+
100
112+
);
55113
}
56114
}

src/lib/types/steam.d.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,21 @@ export interface rgDescription {
7171
}[];
7272
}
7373

74+
type RequireOnlyOne<T, Keys extends keyof T = keyof T> = Pick<T, Exclude<keyof T, Keys>> &
75+
{
76+
[K in Keys]-?: Required<Pick<T, K>> & Partial<Record<Exclude<Keys, K>, never>>;
77+
}[Keys];
78+
79+
interface rgAssetPropertyBase {
80+
propertyid: number;
81+
int_value?: string;
82+
float_value?: string;
83+
string_value?: string;
84+
}
85+
86+
// Only one of int_value, float_value, or string_value can be present
87+
type rgAssetProperty = RequireOnlyOne<rgAssetPropertyBase, 'int_value' | 'float_value' | 'string_value'>;
88+
7489
// g_rgAssets
7590
export interface rgAsset extends rgDescription {
7691
amount: number;
@@ -86,6 +101,7 @@ export interface rgAsset extends rgDescription {
86101
unowned_contextid: string;
87102
unowned_id: string;
88103
element?: HTMLElement;
104+
asset_properties?: rgAssetProperty[];
89105
}
90106

91107
export interface rgInventoryAsset {

src/lib/utils/skin.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,3 +230,23 @@ export function floor(n: number, precision?: number) {
230230

231231
return Math.floor(n * p) / p;
232232
}
233+
234+
/**
235+
* As part of the March 4/2026 update, skins listed on SCM get moved into the trade protected inventory context
236+
* so the user can still use them in-game while listed. However, Valve introduced a bug where these skins
237+
* can't be inspected in game.
238+
*
239+
* Detect and skip these skins to prevent overloaded errors to the user and browser request throttling.
240+
*/
241+
export function isBuggedSkin(asset: rgAsset | undefined): boolean {
242+
if (!asset || !isSkin(asset)) {
243+
return false;
244+
}
245+
246+
if (asset.unowned_contextid !== '16') {
247+
return false;
248+
}
249+
250+
const fv = (asset.asset_properties || []).find((prop) => prop.propertyid === 2);
251+
return !fv;
252+
}

0 commit comments

Comments
 (0)