Skip to content

Commit d062268

Browse files
committed
feat: add highlight_reel (Sticker field 11), make paintwear nullable, add csfloat test vectors
1 parent e2f46b8 commit d062268

File tree

4 files changed

+81
-4
lines changed

4 files changed

+81
-4
lines changed

cs2_inspect/inspect_link.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ def serialize(data: ItemPreviewData) -> str:
106106
Returns:
107107
Uppercase hex string, e.g. "00183C20B803..."
108108
"""
109-
if data.paintwear < 0.0 or data.paintwear > 1.0:
109+
if data.paintwear is not None and (data.paintwear < 0.0 or data.paintwear > 1.0):
110110
raise ValueError(
111111
f"paintwear must be in [0.0, 1.0], got {data.paintwear}"
112112
)

cs2_inspect/models.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ class Sticker:
2424
offset_y: Optional[float] = None
2525
offset_z: Optional[float] = None
2626
pattern: int = 0
27+
highlight_reel: Optional[int] = None
2728

2829

2930
@dataclass
@@ -43,7 +44,7 @@ class ItemPreviewData:
4344
paintindex: int = 0
4445
rarity: int = 0
4546
quality: int = 0
46-
paintwear: float = 0.0
47+
paintwear: Optional[float] = None
4748
paintseed: int = 0
4849
killeaterscoretype: int = 0
4950
killeatervalue: int = 0

cs2_inspect/proto.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,8 @@ def encode_sticker(s: "Sticker") -> bytes: # noqa: F821 (forward ref ok)
211211
if s.offset_z is not None:
212212
w.write_float32_fixed(9, s.offset_z)
213213
w.write_uint32(10, s.pattern)
214+
if s.highlight_reel is not None:
215+
w.write_uint32(11, s.highlight_reel)
214216
return w.to_bytes()
215217

216218

@@ -239,6 +241,8 @@ def decode_sticker(data: bytes) -> "Sticker": # noqa: F821
239241
s.offset_z = struct.unpack("<f", value)[0]
240242
elif field_num == 10:
241243
s.pattern = value
244+
elif field_num == 11:
245+
s.highlight_reel = value
242246
return s
243247

244248

@@ -252,8 +256,9 @@ def encode_item(item: "ItemPreviewData") -> bytes: # noqa: F821
252256
w.write_uint32(5, item.rarity)
253257
w.write_uint32(6, item.quality)
254258
# paintwear: float32 reinterpreted as uint32 varint
255-
pw_uint32 = float32_to_uint32(item.paintwear)
256-
w.write_uint32(7, pw_uint32)
259+
if item.paintwear is not None:
260+
pw_uint32 = float32_to_uint32(item.paintwear)
261+
w.write_uint32(7, pw_uint32)
257262
w.write_uint32(8, item.paintseed)
258263
w.write_uint32(9, item.killeaterscoretype)
259264
w.write_uint32(10, item.killeatervalue)

tests/test_inspect_link.py

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -337,3 +337,74 @@ def test_serialize_customname_100_chars_is_valid(self):
337337
data = ItemPreviewData(defindex=7, customname="x" * 100)
338338
result = serialize(data)
339339
assert result.startswith("00")
340+
341+
342+
# -------------------------------------------------------------------------
343+
# CSFloat / gen.test.ts test vectors
344+
# -------------------------------------------------------------------------
345+
346+
CSFLOAT_A = "00180720DA03280638FBEE88F90340B2026BC03C96"
347+
CSFLOAT_B = "00180720C80A280638A4E1F5FB03409A0562040800104C62040801104C62040802104C62040803104C6D4F5E30"
348+
CSFLOAT_C = "A2B2A2BA69A882A28AA192AECAA2D2B700A3A5AAA2B286FA7BA0D684BE72"
349+
350+
351+
class TestCsfloatVectors:
352+
def test_vector_a_defindex(self):
353+
assert deserialize(CSFLOAT_A).defindex == 7
354+
355+
def test_vector_a_paintindex(self):
356+
assert deserialize(CSFLOAT_A).paintindex == 474
357+
358+
def test_vector_a_paintseed(self):
359+
assert deserialize(CSFLOAT_A).paintseed == 306
360+
361+
def test_vector_a_rarity(self):
362+
assert deserialize(CSFLOAT_A).rarity == 6
363+
364+
def test_vector_a_paintwear_not_none(self):
365+
assert deserialize(CSFLOAT_A).paintwear is not None
366+
367+
def test_vector_a_paintwear(self):
368+
assert abs(deserialize(CSFLOAT_A).paintwear - 0.6337) < 0.001
369+
370+
def test_vector_b_sticker_count(self):
371+
assert len(deserialize(CSFLOAT_B).stickers) == 4
372+
373+
def test_vector_b_sticker_ids(self):
374+
for s in deserialize(CSFLOAT_B).stickers:
375+
assert s.sticker_id == 76
376+
377+
def test_vector_b_paintindex(self):
378+
assert deserialize(CSFLOAT_B).paintindex == 1352
379+
380+
def test_vector_b_paintwear(self):
381+
assert abs(deserialize(CSFLOAT_B).paintwear - 0.99) < 0.01
382+
383+
def test_vector_c_defindex(self):
384+
assert deserialize(CSFLOAT_C).defindex == 1355
385+
386+
def test_vector_c_quality(self):
387+
assert deserialize(CSFLOAT_C).quality == 12
388+
389+
def test_vector_c_keychain_count(self):
390+
assert len(deserialize(CSFLOAT_C).keychains) == 1
391+
392+
def test_vector_c_keychain_highlight_reel(self):
393+
assert deserialize(CSFLOAT_C).keychains[0].highlight_reel == 345
394+
395+
def test_vector_c_no_paintwear(self):
396+
assert deserialize(CSFLOAT_C).paintwear is None
397+
398+
399+
class TestRoundtripNewFeatures:
400+
def test_highlight_reel_roundtrip(self):
401+
from cs2_inspect.models import Sticker as StickerModel
402+
data = ItemPreviewData(defindex=7, keychains=[StickerModel(slot=0, sticker_id=36, highlight_reel=345)])
403+
result = deserialize(serialize(data))
404+
assert len(result.keychains) == 1
405+
assert result.keychains[0].highlight_reel == 345
406+
407+
def test_null_paintwear_roundtrip(self):
408+
data = ItemPreviewData(defindex=7, paintwear=None)
409+
result = deserialize(serialize(data))
410+
assert result.paintwear is None

0 commit comments

Comments
 (0)