diff --git a/src/main/java/net/tfminecraft/tfmcweb/entitlements/EntitlementResolver.java b/src/main/java/net/tfminecraft/tfmcweb/entitlements/EntitlementResolver.java index 2babc07..ccf5312 100644 --- a/src/main/java/net/tfminecraft/tfmcweb/entitlements/EntitlementResolver.java +++ b/src/main/java/net/tfminecraft/tfmcweb/entitlements/EntitlementResolver.java @@ -21,6 +21,8 @@ public final class EntitlementResolver { public static final String KEY_WARDROBE_SLOTS = "wardrobe-skin-slots"; public static final String KEY_MAX_3D_PAIR = "max-3d-pair-bytes"; public static final String KEY_SKIN_COOLDOWN = "skin-token-cooldown-days"; + /** Most swappable wardrobe skins per character; the website and RPCharacters use the same cap. */ + public static final int MAX_WARDROBE_SKIN_SLOTS = 5; private EntitlementResolver() {} @@ -71,7 +73,7 @@ public static ResolvedMeta resolve(Player player) { int maxAlive = maxInt(player, Cache.rpcMetaGroups, Cache.rpcMetaDefaults, KEY_MAX_ALIVE); int wardrobeSlots = Math.max( 1, - Math.min(3, maxInt(player, Cache.rpcMetaGroups, Cache.rpcMetaDefaults, KEY_WARDROBE_SLOTS)) + Math.min(MAX_WARDROBE_SKIN_SLOTS, maxInt(player, Cache.rpcMetaGroups, Cache.rpcMetaDefaults, KEY_WARDROBE_SLOTS)) ); int pairBytes = maxInt(player, Cache.skinsMetaGroups, Cache.skinsMetaDefaults, KEY_MAX_3D_PAIR); diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 934183e..84a4300 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -44,6 +44,7 @@ token-cooldowns: # armourshop/src/main/resources/permission-groups.yml # drinkbuilder/src/main/resources/permission-groups.yml # TFMCWeb only posts values; plugins still own in-game unlock semantics. +# wardrobe-skin-slots is 1 to 5 swappable skins per character (base + Skin 2 … Skin 5). # Optional per-realm overrides (falls back to top-level rpc/skins/drinks): # player-meta.by-realm.dev.rpc: { defaults: ..., groups: [...] } player-meta: @@ -72,22 +73,27 @@ player-meta: tier: 1 name-colour-stops: 1 max-alive-characters: 3 - wardrobe-skin-slots: 1 + wardrobe-skin-slots: 2 - permission: rpchar.group.gilded tier: 2 name-colour-stops: 2 max-alive-characters: 4 - wardrobe-skin-slots: 2 + wardrobe-skin-slots: 3 - permission: rpchar.group.ascended tier: 3 name-colour-stops: 20 max-alive-characters: 5 - wardrobe-skin-slots: 3 + wardrobe-skin-slots: 5 - permission: rpchar.group.legacy tier: 4 name-colour-stops: 20 max-alive-characters: 5 - wardrobe-skin-slots: 3 + wardrobe-skin-slots: 5 + # Staff and helpers (RPCharacters' hidden staff group), on and off duty. + - permission: rpchar.group.staff + tier: 100 + max-alive-characters: 8 + wardrobe-skin-slots: 5 skins: defaults: diff --git a/src/test/java/net/tfminecraft/tfmcweb/entitlements/EntitlementsTest.java b/src/test/java/net/tfminecraft/tfmcweb/entitlements/EntitlementsTest.java index 0c89ccf..acd84f8 100644 --- a/src/test/java/net/tfminecraft/tfmcweb/entitlements/EntitlementsTest.java +++ b/src/test/java/net/tfminecraft/tfmcweb/entitlements/EntitlementsTest.java @@ -52,12 +52,22 @@ MetaGroupDefinition group(String permission, int tier, Map perks Cache.drinksMetaGroups=Arrays.asList(null,blank,low,high,higher); Cache.playerMetaSyncPermissions=Arrays.asList(null," "," allowed ","denied"); var meta=EntitlementResolver.resolve(player); - assertEquals(4,meta.nameColourStops);assertEquals(8,meta.maxAliveCharacters);assertEquals(3,meta.wardrobeSkinSlots);assertEquals(60000,meta.max3dPairBytes); + assertEquals(4,meta.nameColourStops);assertEquals(8,meta.maxAliveCharacters);assertEquals(EntitlementResolver.MAX_WARDROBE_SKIN_SLOTS,meta.wardrobeSkinSlots);assertEquals(5,meta.wardrobeSkinSlots);assertEquals(60000,meta.max3dPairBytes); assertEquals(5,meta.skinTokenCooldownDays);assertEquals(List.of("item","book","armor"),meta.skinKinds);assertTrue(meta.allowArmor3dHelmet && meta.allowDrinkTexture && meta.allowDrinkMessage); assertEquals(Map.of("allowed",true,"denied",false),meta.permissionFlags); Cache.skinsMetaGroups=List.of(high); assertEquals(-1,EntitlementResolver.resolve(player).skinTokenCooldownDays); assertFalse(EntitlementResolver.resolve(player).allowArmor3dHelmet); Cache.drinksMetaGroups=List.of(low);when(player.hasPermission("rank.low")).thenReturn(true);assertFalse(EntitlementResolver.resolve(player).allowDrinkTexture);assertFalse(EntitlementResolver.resolve(player).allowDrinkMessage); } + @Test void wardrobeSlotsUpToTheCapResolveAsConfigured() { + Cache.rpcMetaDefaults=Map.of(); Cache.skinsMetaDefaults=Map.of(); Cache.drinksMetaDefaults=Map.of(); + Cache.skinsMetaGroups=List.of(); Cache.drinksMetaGroups=List.of(); Cache.playerMetaSyncPermissions=List.of(); + Cache.rpcMetaGroups=List.of(group("rank.four",2,Map.of("wardrobe-skin-slots",4),null,null,null,null), + group("rank.five",3,Map.of("wardrobe-skin-slots",5),null,null,null,null)); + Player four=mock(Player.class); when(four.hasPermission("rank.four")).thenReturn(true); + Player five=mock(Player.class); when(five.hasPermission("rank.five")).thenReturn(true); + assertEquals(4,EntitlementResolver.resolve(four).wardrobeSkinSlots); + assertEquals(5,EntitlementResolver.resolve(five).wardrobeSkinSlots); + } @Test void syncJsonCopiesValuesAndSkipsBlankKeys() { UUID id=UUID.randomUUID(); Cache.realmId="dev"; var meta=new EntitlementResolver.ResolvedMeta(3,true,false,8,2,40000,5,List.of(" BOOK "," ","item"),true,Map.of("p\"\\",true,"q",false," ",true));