chuni: setting for equip unearned

This commit is contained in:
sk1982 2024-04-01 06:50:19 -04:00
parent 13d8eaa51a
commit ab40bb06bf
2 changed files with 20 additions and 18 deletions

View File

@ -12,6 +12,7 @@ import { UserboxItems } from '@/actions/chuni/userbox';
import { getUser, requireUser } from '@/actions/auth';
import { Entries } from 'type-fest';
import { CHUNI_NAMEPLATE_PROFILE_KEYS } from '@/components/chuni/nameplate';
import { getGlobalConfig } from '@/config';
type RecentRating = {
scoreMax: string,
@ -23,8 +24,6 @@ type RecentRating = {
const avatarNames = ['avatarBack', 'avatarFace', 'avatarItem', 'avatarWear', 'avatarFront', 'avatarSkin',
'avatarHead'] as const;
const ALLOW_EQUIP_UNEARNED = ['true', '1', 'yes'].includes(process.env.CHUNI_ALLOW_EQUIP_UNEARNED?.toLowerCase() ?? '');
export async function getUserData(user: { id: number }) {
const res = await db.selectFrom('chuni_profile_data as p')
.leftJoin('actaeon_chuni_static_name_plate as nameplate', 'p.nameplateId', 'nameplate.id')
@ -126,7 +125,7 @@ export async function getUserRating(user: UserPayload) {
return { recent, top };
}
const validators = new Map<keyof GeneratedDB['chuni_profile_data'], (user: number, profile: NonNullable<ChuniUserData>, value: any) => Promise<any>>();
const validators = new Map<keyof GeneratedDB['chuni_profile_data'], (user: UserPayload, profile: NonNullable<ChuniUserData>, value: any) => Promise<any>>();
const itemValidators = [
['mapIconId', 'actaeon_chuni_static_map_icon as map_icon', 'map_icon.id', ItemKind.MAP_ICON],
@ -135,6 +134,8 @@ const itemValidators = [
['trophyId', 'actaeon_chuni_static_trophies as trophy', 'trophy.id', ItemKind.TROPHY]
] as const;
const canEquipUnearned = (user: UserPayload) => (getGlobalConfig('chuni_allow_equip_unearned') & user.permissions!);
itemValidators.forEach(([key, table, joinKey, itemKind]) => {
validators.set(key, async (user, profile, value) => {
value = parseInt(value);
@ -144,7 +145,7 @@ itemValidators.forEach(([key, table, joinKey, itemKind]) => {
const res = await db.selectFrom(table)
.leftJoin('chuni_item_item as item', join => join
.onRef('item.itemId', '=', joinKey as any)
.on('item.user', '=', user)
.on('item.user', '=', user.id)
.on('item.itemKind', '=', itemKind))
.where(joinKey as any, '=', value)
.select('item.itemId')
@ -153,7 +154,7 @@ itemValidators.forEach(([key, table, joinKey, itemKind]) => {
if (!res)
throw new Error(`Item with id ${value} does not exist.`);
if (res.itemId === null && value !== profile[key] && !ALLOW_EQUIP_UNEARNED)
if (res.itemId === null && value !== profile[key] && !canEquipUnearned(user))
throw new Error(`You do not own that item.`);
return value;
@ -170,7 +171,7 @@ Object.entries(AvatarCategory).forEach(([category, number]) => {
const res = await db.selectFrom('chuni_static_avatar as avatar')
.leftJoin('chuni_item_item as item', join => join
.onRef('item.itemId', '=', 'avatar.avatarAccessoryId')
.on('item.user', '=', user)
.on('item.user', '=', user.id)
.on('item.itemKind', '=', ItemKind.AVATAR_ACCESSORY))
.where(({ eb, and, selectFrom }) => and([
eb('avatar.version', '=', selectFrom('chuni_static_avatar')
@ -184,7 +185,7 @@ Object.entries(AvatarCategory).forEach(([category, number]) => {
if (!res)
throw new Error(`Item with id ${value} does not exist.`);
if (res.itemId === null && value !== profile[key as keyof ChuniUserData] && !ALLOW_EQUIP_UNEARNED)
if (res.itemId === null && value !== profile[key as keyof ChuniUserData] && !canEquipUnearned(user))
throw new Error(`You do not own that item.`);
return value;
@ -207,7 +208,7 @@ export const updateProfile = async (data: ProfileUpdate) => {
return { error: true, message: `Unknown key "${key}"` };
try {
update[key as keyof ProfileUpdate] = ((await (validators.get(key as any)!(user.id, profile, value))) ?? value) as any;
update[key as keyof ProfileUpdate] = ((await (validators.get(key as any)!(user, profile, value))) ?? value) as any;
} catch (e: any) {
return { error: true, message: e?.message ?? 'Unknown error occurred.' };
}

View File

@ -9,16 +9,17 @@ import { AvatarCategory } from '@/helpers/chuni/avatar';
import { DB } from '@/types/db';
import { ChuniUserData } from '@/actions/chuni/profile';
import { parseJsonResult } from '@/helpers/parse-json-result';
import { getGlobalConfig } from '@/config';
const ALLOW_EQUIP_UNEARNED = ['true', '1', 'yes'].includes(process.env.CHUNI_ALLOW_EQUIP_UNEARNED?.toLowerCase() ?? '');
const joinItem = <DB extends GeneratedDB, TB extends keyof DB, O>(builder: SelectQueryBuilder<DB, TB, O>, joinKey: any, user: UserPayload, itemKind: ItemKind, ...equipped: (number | null | undefined)[]) => {
const canEquipUnearned = (getGlobalConfig('chuni_allow_equip_unearned') & user.permissions!);
const joinItem = <DB extends GeneratedDB, TB extends keyof DB, O>(builder: SelectQueryBuilder<DB, TB, O>, joinKey: any, user: number, itemKind: ItemKind, ...equipped: (number | null | undefined)[]) => {
return (builder.leftJoin('chuni_item_item', join =>
join.onRef('chuni_item_item.itemId' as any, '=', joinKey)
.on('chuni_item_item.user' as any, '=', user)
.on('chuni_item_item.user' as any, '=', user.id)
.on('chuni_item_item.itemKind' as any, '=', itemKind)
) as any)
.where((eb: ExpressionBuilder<any, any>) => eb.or(ALLOW_EQUIP_UNEARNED ? [eb.lit(true)] : [
.where((eb: ExpressionBuilder<any, any>) => eb.or(canEquipUnearned ? [eb.lit(true)] : [
eb('chuni_item_item.itemId', 'is not', null), // owned item
...equipped.map(id => eb(joinKey, '=', id ?? -1)) // equipped but not owned
])) as SelectQueryBuilder<DB, TB, O>;
@ -39,7 +40,7 @@ export type UserboxItems = {
export const getUserboxItems = async (user: UserPayload, profile: ChuniUserData): Promise<UserboxItems> => {
const res = await db
.with('map_icons', eb => joinItem(eb.selectFrom('actaeon_chuni_static_map_icon as map_icon'),
'map_icon.id', user.id, ItemKind.MAP_ICON, profile?.mapIconId)
'map_icon.id', user, ItemKind.MAP_ICON, profile?.mapIconId)
.select(eb => jsonObjectArray(eb, [
'map_icon.id',
'map_icon.name',
@ -48,7 +49,7 @@ export const getUserboxItems = async (user: UserPayload, profile: ChuniUserData)
]).as('mapIcon'))
)
.with('name_plates', eb => joinItem(eb.selectFrom('actaeon_chuni_static_name_plate as name_plate'),
'name_plate.id', user.id, ItemKind.NAME_PLATE, profile?.nameplateId)
'name_plate.id', user, ItemKind.NAME_PLATE, profile?.nameplateId)
.select(eb => jsonObjectArray(eb, [
'name_plate.id',
'name_plate.name',
@ -56,7 +57,7 @@ export const getUserboxItems = async (user: UserPayload, profile: ChuniUserData)
'name_plate.imagePath'
]).as('namePlate')))
.with('system_voices', eb => joinItem(eb.selectFrom('actaeon_chuni_static_system_voice as system_voice'),
'system_voice.id', user.id, ItemKind.SYSTEM_VOICE, profile?.voiceId)
'system_voice.id', user, ItemKind.SYSTEM_VOICE, profile?.voiceId)
.select(eb => jsonObjectArray(eb, [
'system_voice.id',
'system_voice.name',
@ -65,7 +66,7 @@ export const getUserboxItems = async (user: UserPayload, profile: ChuniUserData)
'system_voice.cuePath',
]).as('systemVoice')))
.with('trophies', eb => joinItem(eb.selectFrom('actaeon_chuni_static_trophies as trophy'),
'trophy.id', user.id, ItemKind.TROPHY, profile?.nameplateId)
'trophy.id', user, ItemKind.TROPHY, profile?.nameplateId)
.select(eb => jsonObjectArray(eb, [
'trophy.id',
'trophy.name',
@ -73,7 +74,7 @@ export const getUserboxItems = async (user: UserPayload, profile: ChuniUserData)
'trophy.explainText'
]).as('trophy')))
.with('avatars', eb => joinItem(eb.selectFrom('chuni_static_avatar as avatar'),
'avatar.avatarAccessoryId', user.id, ItemKind.AVATAR_ACCESSORY, profile?.avatarBack, profile?.avatarFace, profile?.avatarItem,
'avatar.avatarAccessoryId', user, ItemKind.AVATAR_ACCESSORY, profile?.avatarBack, profile?.avatarFace, profile?.avatarItem,
profile?.avatarWear, profile?.avatarFront, profile?.avatarSkin, profile?.avatarHead)
.where(({ selectFrom, eb }) => eb('avatar.version', '=', selectFrom('chuni_static_avatar')
.select(({ fn }) => fn.max('version').as('latest'))))