Skip to content

Commit 5617d95

Browse files
committed
Update thunderstore readme
1 parent 55114fe commit 5617d95

File tree

1 file changed

+76
-18
lines changed

1 file changed

+76
-18
lines changed

publish/thunderstore/README.md

Lines changed: 76 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,39 @@
11
Content Warning Shop API
22
===
3-
Exposes an easy to use API to add custom items to the in-game shop. Loosely based on the now defunct [ShopUtils mod by hyydsz](https://github.com/hyydsz/ContentWarningShopUtils).
3+
Exposes an easy-to-use API to add custom items to the in-game shop. Loosely based on the now defunct [ShopUtils mod by hyydsz](https://github.com/hyydsz/ContentWarningShopUtils).
44

5-
**If you are a developer, see the mod's [GitHub repository](https://github.com/Xerren09/ContentWarningShopAPI/#compatibility) for detailed setup instructions and more information.**
5+
**If you are a developer, see the mod's [GitHub repository](https://github.com/Xerren09/ContentWarningShopAPI/) for detailed setup instructions and more information.**
66

77
## Usage
88

99
Once added as a reference to your project, all classes are available under the `ContentWarningShop` namespace.
1010

11+
## Usage
12+
13+
Once added as a reference to your project, all classes are available under the `ContentWarningShop` namespace.
14+
15+
#### Creating items
16+
17+
Ideally `Item`s should be preconfigured and packed into an [`AssetBundle`](https://docs.unity3d.com/Manual/AssetBundlesIntro.html), but you can also construct them at runtime if its easier.
18+
19+
Custom `Item`s should have their `persistentID`, `price`, `purchasable`, `Category`, and `icon` properties set to work.
20+
21+
If you would like your item to have a chance to be randomly spawned in the Old World like other items, set `spawnable` to `true`, and `itemType` to `Item.ItemType.Tool`.
22+
1123
### Registering items
1224

13-
Use the `RegisterItem` method to register an `Item` to the shop. Ideally the item instance is preconfigured and loaded from an AssetBundle, but you can also construct it during runtime.
14-
Make sure to set the `persistentID`, `price`, `purchasable`, `Category`, and `icon` properties at the very least to ensure the item will show up correctly in the store.
25+
Items can be registered via the `RegisterItem` method:
26+
27+
```csharp
28+
//using ContentWarningShop;
29+
30+
var yourCustomItem = yourAssetBundle.LoadAsset<Item>("yourItem");
1531

16-
> NOTE:
32+
Shop.RegisterItem(yourCustomItem);
33+
Shop.RegisterCustomDataEntries();
34+
```
35+
36+
> **NOTE:**
1737
> Item prices are automatically synchronised between players on lobby join. The price set by the lobby's host will be used for the entire lobby. Once a lobby has been created, the price of a registered item can be updated via `UpdateItemPrice`.
1838
1939
You can check if a custom item has been already registered via the `IsItemRegistered` method. The list of **all** registered custom items is also available via the `CustomItems` property.
@@ -24,32 +44,40 @@ If your item uses custom `ItemDataEntry` types, call the `RegisterCustomDataEntr
2444

2545
The `SynchronisedMetadata<T>` class allows you to synchronise arbitrary settings between players through the use of [Steam Lobby Metadata](https://partner.steamgames.com/doc/features/multiplayer/matchmaking#6) keys. Simply create a new instance with a specific type and key and it will be automatically updated whenever the key's value is changed.
2646

27-
> TIP:
47+
> **TIP:**
2848
> Consider prepending your mod's GUID to the key to ensure it won't accidentally collide with a different mod.
2949
3050
For example to synchronise a simple boolean setting with `false` as the initial value:
3151
```csharp
3252
public static readonly SynchronisedMetadata<bool> ExampleSetting = new("ExampleSetting", false);
3353
```
34-
`SynchronisedMetadata` instances remain valid between different lobbies, so once bound to a key they can be safely kept in a static property and used for the entire duration of the game.
54+
Instances will remain valid between different lobbies, so once bound to a key they can be safely kept in a static property and used for the entire duration of the game.
3555

36-
To update the value, call `SetValue(T value)`. Only the lobby's host may update the value of a key, so the method returns a boolean indicating if the set was allowed. If it was rejected, the instance's value isn't updated. Use the `CanSet` method to check if the current player has permission to update the setting.
56+
To update the value, call `SetValue(T value)`. Only the lobby's host may update the value of a key, so the method returns a boolean indicating if the set was allowed. If it was rejected, the instance's value isn't updated. The `CanSet` method can be used to check if the current player has permission to update the setting.
3757

38-
> IMPORTANT:
58+
> **IMPORTANT:**
3959
> Values are converted to strings when passed on to the steam lobby, so make sure your type can be cast to string and back.
4060
4161
When a key is successfully updated either locally or remotely, the `ValueChanged` event will be raised with the new value. Since instances are valid for the lifetime of the game, this event can also be safely used anywhere in your plugin.
4262

43-
> NOTE:
63+
> **NOTE:**
4464
> When not currently in a lobby, setting the value is permitted as if the current player was the host, and the `ValueChanged` event will still be raised.
4565
66+
In the scenario that you have separate player and lobby settings, and you want to apply the local player's settings when they host a new lobby, make sure to subscribe to the `LobbyHosted` event and overwrite the current value. This ensures that any values set by a previous lobby will be replaced with your player's settings:
67+
68+
```csharp
69+
ExampleSetting.LobbyHosted += () => {
70+
ExampleSetting.SetValue(SomeContentWarningSetting.Value);
71+
};
72+
```
73+
4674
### Localisation
4775

48-
The game's built-in localisation implementation is not extendable, so a custom solution is included with the mod under the `ContentWarningShop.Localisation` namespace. This patches `Item.GetLocalizedDisplayName` and `Item.GetTootipData`.
76+
The game's built-in localisation implementation is not extendable, so a custom solution is included with the mod under the `ContentWarningShop.Localisation` namespace. This patches `Item.GetLocalizedDisplayName`, `Item.GetTootipData`, and the `ShopItem`'s constructor.
4977

50-
Use the `ShopLocalisation` class to add localised strings to your items. Each string is represented as a key-value pair assigned to a specific locale. For built in strings such as display name and tooltips, the item's unity object name (filename) is used or prefixed. For example to localise the Item "Spookbox" the key would be simply also "Spookbox".
78+
Use the `ShopLocalisation` class to add localised strings to your items. Each string is represented as a key-value pair assigned to a specific locale. For built in strings such as display name and tooltips, the item's unity object name (filename) is used or prefixed. For example, to localise an `Item` with the object name "Spookbox", the key would also be simply "Spookbox".
5179

52-
When adding locale strings, use the constants defined in the `LocaleKeys` static class to retreive a locale used by the game via the `ShopLocalisation.TryGetLocale` method:
80+
When adding locale strings, use the constants defined in the `LocaleKeys` static class to retrieve a locale used by the game via the `ShopLocalisation.TryGetLocale` method. These locales are guaranteed to be available:
5381

5482
```csharp
5583
ShopLocalisation.TryGetLocale(LocaleKeys.English, out UnityEngine.Localization.Locale locale);
@@ -61,7 +89,7 @@ The returned standard Unity Locale object can then be used via the `AddLocaleStr
6189
locale?.AddLocaleString("Spookbox_ToolTips", $"{ShopLocalisation.UseGlyphString} Play;{ShopLocalisation.Use2GlyphString} Next Track");
6290
```
6391

64-
Note that when localising item tooltips the key must be the item's name, suffixed with `_ToolTips` (`ShopLocalisation.TooltipsSuffix`), and the value must be a `;` separated list. To display action glyphs (such as right mouse button, etc) use the included constants in your strings, and the appropriate icon will be inserted into the tooltip by the game:
92+
Note that when localising item tooltips, the key must be the item's name suffixed with `_ToolTips` (`ShopLocalisation.TooltipsSuffix`), and the value must be a `;` separated list. To display action glyphs (for example "[Left Click] Toggle", etc) use the glyph strings defined on the `ShopLocalisation` class in your strings, and the appropriate icon will be inserted into the tooltip by the game:
6593

6694
| Const | Glyph |
6795
| -------- | ------- |
@@ -70,11 +98,41 @@ Note that when localising item tooltips the key must be the item's name, suffixe
7098
| SelfieGlyph | R (Default) |
7199
| ZoomGlyph | Scroll wheel |
72100

73-
> IMPORTANT:
74-
> If you don't want to add localisation ( :( ), use the `SetDefaultTooltips` extension method on your `Item` to set default tooltips.
101+
> **IMPORTANT:**
102+
> If you don't want to add full localisation ( :( ), use the `SetDefaultTooltips` extension method on your `Item` to set default tooltips.
75103
> If you set tooltips in the editor, they won't work: this is a bug on Unity's end, not this mod. (those tooltips are serialised to null when you save them, even if they look right in the inspector)
76-
> Setting a default is recommended in any case, but especially if you don't or only partially provide localisation.
104+
> Setting a default is recommended in any case, but especially if you don't- or only partially provide localisation.
77105
78106
## Compatibility
79107

80-
This mod patches `ItemInstanceData`'s `GetEntryIdentifier` and `GetEntryType` methods which may cause issues with other mods that do the same. The library exposes a property to help avoid these issues, which can be read without requiring this mod as a dependency. See the repository's [compatibility](https://github.com/Xerren09/ContentWarningShopAPI/#compatibility) section for more information.
108+
This mod patches `ItemInstanceData`'s `GetEntryIdentifier` and `GetEntryType` methods which are used by the game to serialise and deserialise items when synchronising state between players. Unfortunately, the IDs are hardcoded and there is no way to "reserve" one for a specific type, which means two mods patching these same methods can interpret the same values as their own entries incorrectly. To avoid most (hopefully all) collisions like this, entry IDs used by this mod are counted backwards, from `byte.MaxValue`. However, just to be safe the `Shop` class exposes a `MaxUsedEntryID` property that returns the lowest entry ID it uses, above which all other IDs are reserved.
109+
110+
> **WARNING:**
111+
> `MaxUsedEntryID` will not be accurate until all other mods have initialised and registered their items. Try to check for this value as late as possible, after all other mods are loaded.
112+
113+
Obviously if you aren't already using this mod, you don't want to require it just for this; use this snippet to check if this mod is in use and attempt to fetch this value as a "soft" dependency:
114+
115+
```csharp
116+
using System.Reflection;
117+
118+
private static byte GetMaxShopReservedID()
119+
{
120+
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
121+
var target = Array.Find(assemblies, a => a.GetName().Name == "ShopAPI");
122+
if (target != null)
123+
{
124+
var t = target.GetType("ContentWarningShop.Shop");
125+
var prop = t.GetProperty("MaxUsedEntryID", BindingFlags.Public | BindingFlags.Static);
126+
var val = prop.GetValue(null);
127+
if (val != null)
128+
{
129+
return (byte)val;
130+
}
131+
}
132+
else
133+
{
134+
Debug.Log($"Neither the steam or modman version of ShopAPI is loaded; assuming unaltered ItemInstanceData entry registry.");
135+
}
136+
return 0;
137+
}
138+
```

0 commit comments

Comments
 (0)