diff --git a/src/routes/api/routes/players.ts b/src/routes/api/routes/players.ts index a7205ec..f4d3de3 100644 --- a/src/routes/api/routes/players.ts +++ b/src/routes/api/routes/players.ts @@ -1,8 +1,9 @@ import z from "zod"; import { createHonoRoute } from "../../../util/import.ts"; -import { authenticate } from "../../../util/api.ts"; +import { authenticate, statusResponse } from "../../../util/api.ts"; import { typedZValidator } from "../../../util/validators.ts"; import Server from "../../../server/server.ts"; +import { HTTPStatus } from "@oneday/http-status"; export const route = createHonoRoute("/players"); @@ -13,8 +14,10 @@ route.app.get('/v2/progression/bulk', c => { const getProgParamSchema = z.object({ id: z.coerce.number() }); -route.app.get('/v1/progression/:id', authenticate, typedZValidator('param', getProgParamSchema), async c => { - return c.json(await c.get('profile').Progression.get()); +route.app.get('/v1/progression/:id', typedZValidator('param', getProgParamSchema), async c => { + const prof = await Server.Profiles.get(c.req.valid('param').id); + if (!prof) return statusResponse(c, HTTPStatus.NotFound); + return c.json(await prof.Progression.get()); }); const getProgBulkBodySchema = z.object({ diff --git a/src/routes/api/routes/storefronts.ts b/src/routes/api/routes/storefronts.ts new file mode 100644 index 0000000..0dba5cc --- /dev/null +++ b/src/routes/api/routes/storefronts.ts @@ -0,0 +1,21 @@ +import z from "zod"; +import { authenticate } from "../../../util/api.ts"; +import { createHonoRoute } from "../../../util/import.ts"; +import { transformCheckEnum, typedZValidator } from "../../../util/validators.ts"; +import { CurrencyType, StorefrontBalanceType, StorefrontTypes } from "../../../server/storefronts/types.ts"; + +export const route = createHonoRoute("/storefronts"); + +const getGiftDropStoreParamSchema = z.object({ + storeId: z.coerce.number().transform(transformCheckEnum(StorefrontTypes)) +}); +route.app.get('/v3/giftdropstore/:storeId', authenticate, typedZValidator('param', getGiftDropStoreParamSchema), c => { + return c.json({ StoreItems: [] }); // stub +}); + +const getCurrencyByTypeParamSchema = z.object({ + currencyType: z.coerce.number().transform(transformCheckEnum(CurrencyType)) +}); +route.app.get('/v4/balance/:currencyType', authenticate, typedZValidator('param', getCurrencyByTypeParamSchema), c => { + return c.json([ { Balance: 0, CurrencyType: c.req.valid('param').currencyType, BalanceType: StorefrontBalanceType.NonPurchasedNonP2P } ]); // stub +}); \ No newline at end of file diff --git a/src/routes/auth/routes/connect.ts b/src/routes/auth/routes/connect.ts index 3e26f28..5a392b2 100644 --- a/src/routes/auth/routes/connect.ts +++ b/src/routes/auth/routes/connect.ts @@ -35,7 +35,8 @@ const authBodyBaseSchema = z.object({ } catch { ctx.addIssue("Steam Auth Ticket could not be parsed"); } - }) + }), + "x-patch-plugin-hash": z.string() }); const cachedLoginGrantSchema = authBodyBaseSchema.extend({ @@ -80,6 +81,7 @@ route.app.post('/token', typedZValidator('form', tokenGrantSchema), async c => { const form = c.req.valid('form'); if (typeof form.platform_auth == 'undefined' || typeof form.platform == 'undefined') return error(TokenRequestError.AccessDenied); + if (typeof form.device_class !== 'number') return error(TokenRequestError.AccessDenied); const { valid } = await Steam.AuthenticateUserTicket(form.platform_auth, form.platform_id); if (valid == SteamAuthResult.Failure) return error(TokenRequestError.AccessDenied, TokenRequestErrorDescriptions.PlatformVerificationFailed); @@ -99,6 +101,8 @@ route.app.post('/token', typedZValidator('form', tokenGrantSchema), async c => { const profile = await Server.Profiles.get(token.sub); if (!profile) return error(TokenRequestError.AccessDenied); + await profile.Matchmaking.setLastDeviceClass(form.device_class!); + return c.json({ access_token: await Server.Platforms.getToken(profile, TokenType.Access), refresh_token: await Server.Platforms.getToken(profile, TokenType.Refresh), @@ -113,7 +117,9 @@ route.app.post('/token', typedZValidator('form', tokenGrantSchema), async c => { const profile = await Server.Profiles.get(form.account_id); if (!profile) return error(TokenRequestError.InvalidRequest, "No such profile"); await Server.Platforms.updateLastLoginTime(form.platform, form.platform_id, form.account_id); + await profile.Matchmaking.setLastDeviceClass(form.device_class!); + log.d(`Patch hash: ${form["x-patch-plugin-hash"]}`); return c.json({ access_token: await Server.Platforms.getToken(profile, TokenType.Access), refresh_token: await Server.Platforms.getToken(profile, TokenType.Refresh), diff --git a/src/server/matchmaking/base.ts b/src/server/matchmaking/base.ts index 76cd0c1..70d1fe2 100644 --- a/src/server/matchmaking/base.ts +++ b/src/server/matchmaking/base.ts @@ -34,7 +34,7 @@ export class ServerMatchmakingBase extends ServerContentBase { if (options.roomName === 'DormRoom') return `@${options.profile.getUsername()}'s Dorm`; else { - if (options.subRoomName || options.subRoomName !== "Home") return `^${options.roomName}.${options.subRoomName}`; + if (typeof options.subRoomName !== 'undefined' && options.subRoomName !== "Home") return `^${options.roomName}.${options.subRoomName}`; else return `^${options.roomName}`; } } @@ -73,7 +73,10 @@ export class ServerMatchmakingBase extends ServerContentBase { let allInstances = this.server.Instances.getAllInstances(); const subroom = (await targetRoom.getAllSubrooms()).values().find(factory => factory.Name == options.subRoomName); - // if a subroom was specified, filter instances that + // filter by instances with the same room ID + allInstances = new Set(allInstances.values().filter(inst => inst.roomId == targetRoom.getRoomId())); + + // if a subroom was specified, filter by instances with the same scene ID if (subroom) allInstances = new Set(allInstances.values().filter(inst => inst.subRoomId == subroom.RoomSceneId)); // filter out instances that are in progress and do not support joininprogress @@ -86,6 +89,7 @@ export class ServerMatchmakingBase extends ServerContentBase { if (!foundInstance) { const matchmakeableSubrooms = (await targetRoom.getAllSubrooms()).values().filter(scene => scene.CanMatchmakeInto).toArray(); + if (matchmakeableSubrooms.length === 0) return { errorCode: MatchmakingErrorCode.RoomIsNotActive }; const index = Math.floor(Math.random() * matchmakeableSubrooms.length); log.d(`Scene ${matchmakeableSubrooms[index].RoomSceneId} was chosen for matchmaking into new instance of room ${targetRoom.getRoomId()}`); diff --git a/src/server/storefronts/test.ts b/src/server/storefronts/test.ts deleted file mode 100644 index e69de29..0000000 diff --git a/src/server/storefronts/types.ts b/src/server/storefronts/types.ts new file mode 100644 index 0000000..f9c74cb --- /dev/null +++ b/src/server/storefronts/types.ts @@ -0,0 +1,41 @@ +export enum StorefrontTypes { + None, + LaserTag, + RecCenter, + Watch, + Quest_LostSkulls = 100, + Quest_Dracula, + Quest_GoldenTrophy, + Quest_CrimsonCauldron, + RecRoyale = 200, + Cafe = 300, + Paintball = 400, + Paintball_River, + Paintball_Homestead, + Paintball_Quarry, + Paintball_ClearCut, + Paintball_Spillway, + Paintball_SunsetDriveIn, + Bowling = 500, + StuntRunner = 600, + DormMirror = 700 +} + +export enum CurrencyType { + Invalid, + LaserTagTickets, + RecCenterTokens, + LostSkullsGold = 100, + DraculaSilver, + RecRoyale_Season1 = 200 +} + +export enum StorefrontBalanceType { + NonPurchasedNonP2P = -2, + NonPurchasedP2P, + SteamPurchased, + OculusPurchased, + PlayStationPurchased, + MicrosoftPurchased, + IOSPurchased = 5 +} \ No newline at end of file