Supported Games
Provides an API for checking player ownership of DLC items and skins by content or workshop ID
Workshop ID's are what you see on steam (ex. https://steamcommunity.com/sharedfiles/filedetails/?id=513329998), most skin plugins will use workshop IDs
Content ID's are Rusts own identifier for paid content
Why this plugin is useful
Its stupid that I have to do this, but Death from codefling has claimed this plugin is a overcomplication and has expensive API calls. Death does not know what he is talking about, and since I do not use codefling I will post my rebuttal to his claims here
I am going to touch on the 2 parts of his statement that are relevant to this plugin.

The native CheckSkinOwnership method, requires you provide the the ID of the DLC you want to check. That is useless if you only have workshop IDs like most plugins do. But lets say you do have the DLC ID.
The first thing that function does is call the ItemSkinDirectory.FindByInventoryDefinitionId method to find the DLC info. That method makes 2 Linq calls both generating garbage for every ID that is checked, Where and FirstOrDefault. (See: https://www.jacksondunstan.com/articles/4840 )
This plugin has 2 API methods that produce the same result as the native function;
CheckContentOwnership which takes in a DLC ID, the same as the native method, but without the garbage allocations.
IsOwnedOrFreeSkin which takes in a workshop ID instead of a DLC ID, pretty useful for plugins storing workshop IDs, and also without garbage allocations
Both of these API methods are faster to call through Oxides hook API then running the native CheckSkinOwnership function.
Here is some benchmark results comparing the 3 methods 3 times back to back, testing against every approved skin
-------------------------------------------------
[API] IsOwnedOrFreeSkin: 5469 iterations
Totals: Time: 7.5846ms | Allocations: 0 bytes
Average: Time: 0.0014ms | Allocations: 0 bytes
-------------------------------------------------
[API] CheckContentOwnership: 5469 iterations
Totals: Time: 6.0259ms | Allocations: 0 bytes
Average: Time: 0.0011ms | Allocations: 0 bytes
-------------------------------------------------
[Native] CheckSkinOwnership: 5469 iterations
Totals: Time: 14.4965ms | Allocations: 143360 bytes
Average: Time: 0.0027ms | Allocations: 26 bytes
-------------------------------------------------
[API] IsOwnedOrFreeSkin: 5469 iterations
Totals: Time: 5.9590ms | Allocations: 0 bytes
Average: Time: 0.0011ms | Allocations: 0 bytes
-------------------------------------------------
[API] CheckContentOwnership: 5469 iterations
Totals: Time: 5.6988ms | Allocations: 0 bytes
Average: Time: 0.0010ms | Allocations: 0 bytes
-------------------------------------------------
[Native] CheckSkinOwnership: 5469 iterations
Totals: Time: 14.4887ms | Allocations: 139264 bytes
Average: Time: 0.0026ms | Allocations: 25 bytes
-------------------------------------------------
[API] IsOwnedOrFreeSkin: 5469 iterations
Totals: Time: 6.3950ms | Allocations: 0 bytes
Average: Time: 0.0012ms | Allocations: 0 bytes
-------------------------------------------------
[API] CheckContentOwnership: 5469 iterations
Totals: Time: 5.5006ms | Allocations: 0 bytes
Average: Time: 0.0010ms | Allocations: 0 bytes
-------------------------------------------------
[Native] CheckSkinOwnership: 5469 iterations
Totals: Time: 14.1449ms | Allocations: 135168 bytes
Average: Time: 0.0026ms | Allocations: 24 bytes
Not only does this plugin provides faster, non-allocating methods for both DLC IDs AND Workshop IDs, it also allows you to filter a entire list of dlc/workshop IDs for a player in a single call leaving you with a list containing only what the player is allowed to use.

The HasLicense function requires that you already have the item created. It then makes a call into the steam_api64.dll managed binary. We don't have access to the C source code from there but it is likely that it makes a API request to Steam to do the comparison.
At the end of the day, I dont care if other developers want to or don't want make use of this plugin. I will be using it for all my plugins to quickly and easily filter out content players can't use.
I can post the plugin used to benchmark if anyone wants to try it themselves
API
Plugin
// Checks if the plugin is fully initialized.
bool Initialized()
Workshop/Skin IDs
// Checks if the specified workshop skin ID is a paid skin.
bool IsPaidSkin(ulong workshopId)
------------------------------------------------------------------------------
// Removes all paid skins from the provided list of workshop skin IDs.
bool FilterPaidSkins(List<ulong> workshopIds)
------------------------------------------------------------------------------
// Filters a list of workshop skin IDs to only include those that the player owns or are unapproved workshop skins.
// Returns false if skin definitions have not yet been initialized
bool FilterOwnedOrFreeSkins(BasePlayer player, List<ulong> workshopIds)
// Example usage:
List<ulong> workshopIds = new List<ulong>
{
491265826,
491250135,
491272870,
491151877
};
bool result = PluginDLCAPI.Call<bool>("FilterOwnedOrFreeSkins", player, workshopIds);
if (result)
{
// The list will now only contain skin IDs that the player is allowed to use
}
------------------------------------------------------------------------------
// Checks whether the specified skin is owned by the player or is an unapproved workshop skin.
// Returns false if skin definitions have not yet been initialized, or if the user does not own the skin
bool IsOwnedOrFreeSkin(BasePlayer player, ulong workshopId)
// Example Usage:
bool result = PluginDLCAPI.Call<bool>("IsOwnedOrFreeSkin", player, 491265826);
if (result)
{
// The player is allowed to use this workshop skin
}
Items
// Checks whether the specified item is owned by the player or free to use.
// Returns false if skin definitions have not yet been initialized, or if the user does not own the item
bool IsOwnedOrFreeItem(BasePlayer player, Item item)
bool IsOwnedOrFreeItem(BasePlayer player, int itemId, ulong skin = 0)
bool IsOwnedOrFreeItem(BasePlayer player, string shortname, ulong skin = 0)
------------------------------------------------------------------------------
// Filters a list items to only include those that the player owns or can use
// Returns false if skin definitions have not yet been initialized
bool FilterOwnedOrFreeItems(BasePlayer player, List<Item> items)
DLC Items
// Checks whether the specified item is a DLC item or skin.
// Returns false if skin definitions have not yet been initialized, or true if the item is a DLC item or skin
bool IsDLCItem(Item item)
bool IsDLCItem(int itemId, ulong skin = 0)
bool IsDLCItem(string shortname, ulong skin = 0)
Content IDs (ItemSkinDirectory.Skin.id / SteamDLCItem.dlcAppId)
//Filters a list of content IDs to only include those that the player owns.
//Returns false if skin definitions have not yet been initialized
bool FilterContentOwnership(BasePlayer player, List<int> contentIds)
// Example Usage:
List<int> contentIds = new List<int>();
foreach (ConstructionGrade grade in grade.grades)
{
contentIds.Add((int)grade.gradeBase.skin)
}
bool result = PluginDLCAPI.Call<bool>("FilterContentOwnership", player, contentIds);
if (result)
{
// The list will only contain the ID's of the building grades/skins the player is allowed to use
}
------------------------------------------------------------------------------
// A faster non-allocating implementation of Rusts CheckSkinOwnership method.
// This works for all DLC items.
bool CheckContentOwnership(BasePlayer player, int contentId)
// Example Usage:
int railroadPlanterContentId = 10298;
bool result = PluginDLCAPI.Call<bool>("CheckContentOwnership", player, railroadPlanterContentId);
if (result)
{
// The player is allowed to use the railroad planter
}
Redirected Skins
// Checks if a item shortname or ID is a redirected skin.
bool IsRedirectedSkin(string shortname)
bool IsRedirectedSkin(int itemId)
------------------------------------------------------------------------------
// Converts a item shortname back to its unskinned definition shortname. (ex. hazmatsuit.spacesuit -> hazmatsuit)
string GetRedirectedShortname(string shortname)
------------------------------------------------------------------------------
// Converts a item ID back to its unskinned definition ID
int GetRedirectedItemId(int itemId)
------------------------------------------------------------------------------
// Takes a redirected item shortname and checks if the player can use it.
// If the player can use the item redirect, the redirect shortname will be returned, else the base item shortname will be returned
string GetRedirectedShortnameIfNotOwned(BasePlayer player, string shortname)
------------------------------------------------------------------------------
// Takes a redirected item shortname and checks if the player can use it.
// If the player can use the item redirect, the redirect item ID will be returned, else the base item ID will be returned
int GetRedirectedItemIdIfNotOwned(BasePlayer player, int itemId)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

