mirror of
https://github.com/djdevin/recflare.git
synced 2026-09-09 15:11:29 -07:00
[api] fix default avatars in 2025 (#40)
This commit is contained in:
+19
-8
@@ -487,13 +487,8 @@ export const LegacyAvatarItemSaves = z.object({
|
|||||||
customAvatarItemSavesByAvatarItemDesc: z.record(z.string(), CustomAvatarItemSave),
|
customAvatarItemSavesByAvatarItemDesc: z.record(z.string(), CustomAvatarItemSave),
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/** `GET /outfits/me` — the outfit stored in slot 0, served back exactly as it was saved. */
|
||||||
* `GET /outfits/me` — the outfit envelope. Either the outfit stored in slot 0, served
|
export const StoredOutfit = z.object({
|
||||||
* back exactly as it was saved, or (for a player who has never saved) the brand-new-
|
|
||||||
* account form, where every field that would carry an outfit is null/empty and
|
|
||||||
* `DataVersion` is 9.
|
|
||||||
*/
|
|
||||||
export const OutfitsMeResponse = z.object({
|
|
||||||
LegacyData: z.object({
|
LegacyData: z.object({
|
||||||
SelectionsV1: z.string().nullable().describe('Semicolon-delimited legacy descriptors'),
|
SelectionsV1: z.string().nullable().describe('Semicolon-delimited legacy descriptors'),
|
||||||
SelectionsV2: z.string().nullable().describe('JSON-in-a-string: `{ selections: [...] }`'),
|
SelectionsV2: z.string().nullable().describe('JSON-in-a-string: `{ selections: [...] }`'),
|
||||||
@@ -502,7 +497,7 @@ export const OutfitsMeResponse = z.object({
|
|||||||
HairColor: z.string().nullable(),
|
HairColor: z.string().nullable(),
|
||||||
}),
|
}),
|
||||||
Selections: JsonArray,
|
Selections: JsonArray,
|
||||||
DataVersion: z.int().describe('9 in the new-account envelope; whatever was saved otherwise'),
|
DataVersion: z.int().describe('The client’s outfit format version, as saved'),
|
||||||
CustomizationSettings: z
|
CustomizationSettings: z
|
||||||
.string()
|
.string()
|
||||||
.nullable()
|
.nullable()
|
||||||
@@ -513,6 +508,22 @@ export const OutfitsMeResponse = z.object({
|
|||||||
Slot: z.int().describe('0 — the outfit being worn'),
|
Slot: z.int().describe('0 — the outfit being worn'),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* `GET /outfits/me` for a player who has never saved — the brand-new-account envelope.
|
||||||
|
* Flatter than a stored outfit rather than a nulled-out copy of it: four empty strings and
|
||||||
|
* nothing else, no `LegacyData`, no `Selections`, no `DataVersion`. `OutfitSelections` is
|
||||||
|
* the flat field name here, not `SelectionsV1`/`SelectionsV2`.
|
||||||
|
*/
|
||||||
|
export const EmptyOutfit = z.object({
|
||||||
|
FaceFeatures: z.string(),
|
||||||
|
HairColor: z.string(),
|
||||||
|
OutfitSelections: z.string(),
|
||||||
|
SkinColor: z.string(),
|
||||||
|
})
|
||||||
|
|
||||||
|
/** `GET /outfits/me` — the stored outfit, or the empty envelope for a new player. */
|
||||||
|
export const OutfitsMeResponse = z.union([StoredOutfit, EmptyOutfit])
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* `PUT /outfits/me` JSON body — the outfit the client is saving, in the newer envelope.
|
* `PUT /outfits/me` JSON body — the outfit the client is saving, in the newer envelope.
|
||||||
* The heavy fields are JSON-in-a-string, exactly as the client serialises them:
|
* The heavy fields are JSON-in-a-string, exactly as the client serialises them:
|
||||||
|
|||||||
@@ -334,8 +334,9 @@ export const avatarRoutes = new Hono<App>({ strict: false })
|
|||||||
'`outfit` table — the newer client treats slot 0 as the outfit currently worn — and ' +
|
'`outfit` table — the newer client treats slot 0 as the outfit currently worn — and ' +
|
||||||
'handed back exactly as it was saved, since the payload’s heavy fields are the ' +
|
'handed back exactly as it was saved, since the payload’s heavy fields are the ' +
|
||||||
'client’s own JSON-in-a-string documents.\n\n' +
|
'client’s own JSON-in-a-string documents.\n\n' +
|
||||||
'A player who has never saved gets the brand-new-account envelope: all-null ' +
|
'A player who has never saved gets the brand-new-account envelope, which is a ' +
|
||||||
'`LegacyData`, no `Selections`, `DataVersion` 9.',
|
'different, flatter shape than a stored outfit: the four empty-string fields ' +
|
||||||
|
'`FaceFeatures`, `HairColor`, `OutfitSelections` and `SkinColor`, and nothing else.',
|
||||||
security: AUTHED,
|
security: AUTHED,
|
||||||
responses: {
|
responses: {
|
||||||
200: json(OutfitsMeResponse, 'The stored outfit, or the empty envelope'),
|
200: json(OutfitsMeResponse, 'The stored outfit, or the empty envelope'),
|
||||||
@@ -350,20 +351,10 @@ export const avatarRoutes = new Hono<App>({ strict: false })
|
|||||||
if (outfit !== null) return c.json(outfit)
|
if (outfit !== null) return c.json(outfit)
|
||||||
|
|
||||||
return c.json({
|
return c.json({
|
||||||
LegacyData: {
|
FaceFeatures: '',
|
||||||
SelectionsV1: null,
|
HairColor: '',
|
||||||
SelectionsV2: null,
|
OutfitSelections: '',
|
||||||
FaceFeatures: null,
|
SkinColor: '',
|
||||||
SkinColor: null,
|
|
||||||
HairColor: null,
|
|
||||||
},
|
|
||||||
Selections: [],
|
|
||||||
DataVersion: 9,
|
|
||||||
CustomizationSettings: null,
|
|
||||||
ThumbnailFileName: null,
|
|
||||||
Name: null,
|
|
||||||
Accessibility: 0,
|
|
||||||
Slot: 0,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -670,20 +670,10 @@ describe('public endpoints', () => {
|
|||||||
const res = await exports.default.fetch(`${ORIGIN}/outfits/me`, { headers: await bearer('77') })
|
const res = await exports.default.fetch(`${ORIGIN}/outfits/me`, { headers: await bearer('77') })
|
||||||
expect(res.status).toBe(200)
|
expect(res.status).toBe(200)
|
||||||
expect(await res.json()).toEqual({
|
expect(await res.json()).toEqual({
|
||||||
LegacyData: {
|
FaceFeatures: '',
|
||||||
SelectionsV1: null,
|
HairColor: '',
|
||||||
SelectionsV2: null,
|
OutfitSelections: '',
|
||||||
FaceFeatures: null,
|
SkinColor: '',
|
||||||
SkinColor: null,
|
|
||||||
HairColor: null,
|
|
||||||
},
|
|
||||||
Selections: [],
|
|
||||||
DataVersion: 9,
|
|
||||||
CustomizationSettings: null,
|
|
||||||
ThumbnailFileName: null,
|
|
||||||
Name: null,
|
|
||||||
Accessibility: 0,
|
|
||||||
Slot: 0,
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user