Skip to content

Commit 7e7c476

Browse files
committed
Init v5
1 parent feb624a commit 7e7c476

34 files changed

+858
-321
lines changed

CHANGELOG.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,14 @@
1+
# 5.0.0
2+
* Added possibility to add a simple/static local provider
3+
* Require no network connection
4+
* Can be utilized by mods (via metadata/resources) or by locally in the new configuration directory
5+
* New configuration directory: `config/cape-provider`
6+
* Migrated configuration file from `config/cape-provider.json5` to `config/cape-provider/config.json`
7+
* Some configuration options changed and might be reset to the defaults
8+
* Updated documentation
9+
* Removed OptiFine from default providers
10+
* Removed unused translations
11+
112
# 4.3.1
213
* Fix GSON Map serialization problem
314

README.md

Lines changed: 94 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,33 @@ Improved/Reworked version of the ["Capes" mod](https://github.com/CaelTheColher/
2525

2626
### Creating a custom cape provider
2727

28-
#### As a user
28+
The mod provides many different ways how a provider can be added.
29+
30+
The following possibilities are sorted by simplicity:
31+
32+
#### Simple Local Provider
33+
34+
> Recommended for:
35+
> * Users that just want a customizable cape
36+
> * Modpacks (using `config/cape-provider/simple-custom`)
37+
38+
The simples way to display a cape is by going into the `config/cape-provider` directory and creating a cape texture file named `cape.png`.
39+
40+
Additionally there are the following optional files:
41+
* `owners.txt` - Determines which player names or UUIDs will get the cape displayed. If this file is not present then all players will display with the cape.
42+
* `name.txt` - To override the display name of the provider
43+
44+
You can also add more providers by creating corresponding directories in `config/cape-provider/simple-custom`.<br/>Example: `config/cape-provider/simple-custom/my-super-cool-provider/cape.png`
45+
46+
#### Remote Provider in configuration
47+
48+
> Recommended for:
49+
> * Users that want to add a custom remote provider
50+
2951
This demo showcases how to apply the capes inside [``custom-cape-demo``](https://github.com/litetex-oss/mcm-cape-provider/tree/dev/custom-cape-demo).
3052

31-
1. Open the config file located in ``config/cape-provider.json5``
32-
2. In the ``customProviders`` section add the following entry:
53+
1. Open the config file located in ``config/cape-provider/config.json``
54+
2. In the ``remoteCustomProviders`` section add the following entry:
3355
```jsonc
3456
{
3557
"id": "cp1",
@@ -51,31 +73,64 @@ This demo showcases how to apply the capes inside [``custom-cape-demo``](https:/
5173
</details>
5274
3. Restart the game and activate the provider
5375

54-
For more details have a look at [CustomProvider](https://github.com/litetex-oss/mcm-cape-provider/tree/dev/src/main/java/net/litetex/capes/provider/CustomProvider.java) and [CustomProviderConfig](https://github.com/litetex-oss/mcm-cape-provider/tree/dev/src/main/java/net/litetex/capes/config/CustomProviderConfig.java)
76+
For more details have a look at [RemoteCustomProvider](https://github.com/litetex-oss/mcm-cape-provider/tree/dev/src/main/java/net/litetex/capes/provider/custom/remote/RemoteCustomProvider.java) and [RemoteCustomProviderConfig](https://github.com/litetex-oss/mcm-cape-provider/tree/dev/src/main/java/net/litetex/capes/provider/custom/remote/RemoteCustomProviderConfig.java)
5577

56-
##### Maximum size
78+
NOTE: Texture resolvers can be selected using the `textureResolverId` attribute (see below for details).
5779

58-
Images/Textures should not exceed 10MB otherwise they might be ignored.
80+
#### via Mods
5981

60-
##### Texture resolvers / Animated textures
82+
> Recommended for:
83+
> * Mods
6184

62-
Texture resolvers can be selected using the `textureResolverId` attribute.
63-
The following resolvers are currently built-in:
85+
If you are a mod developer and want to e.g. display a cape for supporters or contributors of your mod, you can provide it using the mod's resources and/or metadata in ``fabric.mod.json``.
86+
The overall behavior is similar to how [``modmenu``](https://github.com/TerraformersMC/ModMenu?tab=readme-ov-file#fabric-metadata-api) handles this.
6487
65-
| Resolver-ID | Animated | Format | Example | Notes |
66-
| --- | --- | --- | --- | --- |
67-
| `default` / null || [PNG](https://de.wikipedia.org/wiki/Portable_Network_Graphics) | [uuid.png](https://raw.githubusercontent.com/litetex-oss/mcm-cape-provider/refs/heads/dev/custom-cape-demo/uuid.png) | |
68-
| `sprite` || Stacked [PNG](https://de.wikipedia.org/wiki/Portable_Network_Graphics) | [animated.png](https://raw.githubusercontent.com/litetex-oss/mcm-cape-provider/refs/heads/dev/custom-cape-demo/animated.png) | |
69-
| `gif` || [GIF](https://de.wikipedia.org/wiki/Graphics_Interchange_Format) | [animated.gif](https://raw.githubusercontent.com/litetex-oss/mcm-cape-provider/refs/heads/dev/custom-cape-demo/animated.gif) | _Usage not recommended_<br/>GIFs require more resources when compared to more modern formats like PNG. |
88+
##### Local/Simple (Recommended)
7089
71-
Please note that animated textures can be frozen or completely disabled in the settings.
90+
This approach requires no network communication and is the recommended approach.
91+
It works by reading metadata and resources from the `cape` directory.
92+
93+
Here is an example:
94+
1. Add the following mod metadata:
95+
``fabric.mod.json``
96+
```json5
97+
{
98+
...
99+
"custom": {
100+
"cape": "Contributors"
101+
}
102+
}
103+
```
104+
2. Create a `cape` directory inside `resources`
105+
3. Add the cape texture in `cape/cape.png`
106+
4. Add the players that should be given the cape in `cape/owners.txt` with their UUIDs or names
72107
73-
#### As a developer / Proving capes through mods
108+
<details><summary>Note: There is also a more detailed variant</summary>
74109
75-
If you are a mod developer and want to e.g. display a cape for supporters of your mod, you can provide it using the mod's metadata / ``fabric.mod.json``.
76-
The overall behavior is similar to how [``modmenu``](https://github.com/TerraformersMC/ModMenu?tab=readme-ov-file#fabric-metadata-api) handles this.
110+
``fabric.mod.json``
111+
```json5
112+
{
113+
"custom": {
114+
"cape": {
115+
"name-extra": "Contributors",
116+
"owners": {
117+
// You can also used UUIDs
118+
"names": [
119+
"Notch"
120+
]
121+
}
122+
}
123+
}
124+
}
125+
```
77126
78-
Here's a simple implementation:
127+
</details>
128+
129+
The mod uses this strategy itself. See the [`fabric.mod.json`](https://github.com/litetex-oss/mcm-cape-provider/tree/dev/src/main/resources/fabric.mod.json) or [`cape` directory](https://github.com/litetex-oss/mcm-cape-provider/tree/dev/src/main/resources/cape) for details.
130+
131+
##### Remote
132+
133+
Here's an example implementation that shows how a remote cape provider can be added:
79134

80135
``fabric.mod.json``
81136
```json5
@@ -108,8 +163,28 @@ Here's a simple implementation:
108163
109164
</details>
110165
166+
#### Programmatic
167+
111168
You can also create a [programmatic cape provider](https://github.com/litetex-oss/mcm-cape-provider/tree/dev/PROGRAMMATIC_PROVIDER.md).
112169
170+
### Further notes
171+
172+
#### Maximum size
173+
174+
Images/Textures should not exceed 10MB. Otherwise they might be ignored.
175+
176+
#### Texture resolvers / Animated textures
177+
178+
The following resolvers are currently built-in:
179+
180+
| Resolver-ID | Animated | Format | Example | Notes |
181+
| --- | --- | --- | --- | --- |
182+
| `default` / null | ❌ | [PNG](https://de.wikipedia.org/wiki/Portable_Network_Graphics) | [uuid.png](https://raw.githubusercontent.com/litetex-oss/mcm-cape-provider/refs/heads/dev/custom-cape-demo/uuid.png) | |
183+
| `sprite` | ✔ | Stacked [PNG](https://de.wikipedia.org/wiki/Portable_Network_Graphics) | [animated.png](https://raw.githubusercontent.com/litetex-oss/mcm-cape-provider/refs/heads/dev/custom-cape-demo/animated.png) | |
184+
| `gif` | ✔ | [GIF](https://de.wikipedia.org/wiki/Graphics_Interchange_Format) | [animated.gif](https://raw.githubusercontent.com/litetex-oss/mcm-cape-provider/refs/heads/dev/custom-cape-demo/animated.gif) | _Usage not recommended_<br/>GIFs require more resources when compared to more modern formats like PNG. |
185+
186+
Please note that animated textures can be frozen or completely disabled in the settings.
187+
113188
<!-- modrinth_exclude.start -->
114189
115190
## Installation

gradle.properties

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,15 @@ minecraft_version=1.21.11
44
# Fabric Properties
55
loader_version=0.18.3
66
# Mod Properties
7-
mod_version=4.3.2-SNAPSHOT
7+
mod_version=5.0.0-SNAPSHOT
88
maven_group=net.litetex.mcm
99
archivesBaseName=cape-provider
1010
mod_name=Cape Provider
1111
mod_desc=Use capes from various providers
1212
mod_license_spdx_id=LGPL-2.1-or-later
1313
mod_license_url=https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
1414
# Additional
15-
fabric_api_version=0.139.5+1.21.11
15+
fabric_api_version=0.140.0+1.21.11
1616
modmenu_version=17.0.0-alpha.1
1717
skinshuffle_version=2.10.1+1.21.11-fabric
1818
minecraftcapes_version=fabric-1.21.11-1.0.0

src/main/java/net/litetex/capes/Capes.java

Lines changed: 60 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -12,26 +12,29 @@
1212
import java.util.Objects;
1313
import java.util.Optional;
1414
import java.util.Set;
15+
import java.util.concurrent.CompletableFuture;
1516
import java.util.function.Consumer;
1617
import java.util.function.Predicate;
1718
import java.util.function.Supplier;
1819
import java.util.stream.Collectors;
20+
import java.util.stream.Stream;
1921

2022
import org.slf4j.Logger;
2123
import org.slf4j.LoggerFactory;
2224

2325
import com.mojang.authlib.GameProfile;
2426

2527
import net.litetex.capes.config.Config;
26-
import net.litetex.capes.config.ModProviderHandling;
2728
import net.litetex.capes.handler.PlayerCapeHandler;
2829
import net.litetex.capes.handler.PlayerCapeHandlerManager;
2930
import net.litetex.capes.handler.ProfileTextureLoadThrottler;
3031
import net.litetex.capes.handler.textures.TextureResolver;
3132
import net.litetex.capes.provider.CapeProvider;
32-
import net.litetex.capes.provider.CustomProvider;
3333
import net.litetex.capes.provider.DefaultMinecraftCapeProvider;
34-
import net.litetex.capes.provider.ModMetadataProvider;
34+
import net.litetex.capes.provider.custom.local.LocalCustomProvider;
35+
import net.litetex.capes.provider.suppliers.CapeProviders;
36+
import net.litetex.capes.provider.suppliers.ModMetadataProviderSupplier;
37+
import net.litetex.capes.provider.suppliers.SimpleFilesystemProviders;
3538
import net.litetex.capes.texturecache.TextureCache;
3639
import net.litetex.capes.util.CapeProviderTextureAsset;
3740
import net.minecraft.client.Minecraft;
@@ -69,6 +72,7 @@ public static void setInstance(final Capes instance)
6972
private final Consumer<Config> saveConfigFunc;
7073
private final Map<String, CapeProvider> allProviders;
7174
private final Map<String, TextureResolver> allTextureResolvers;
75+
private final Set<CapeProvider> autoActivatingProviders;
7276

7377
private final boolean validateProfile;
7478
private final Duration loadThrottleSuppressDuration;
@@ -84,19 +88,37 @@ public static void setInstance(final Capes instance)
8488
@SuppressWarnings("checkstyle:MagicNumber")
8589
public Capes(
8690
final Path modStateDir,
91+
final Path configDir,
8792
final Config config,
8893
final Consumer<Config> saveConfigFunc,
89-
final Map<String, CapeProvider> allProviders,
94+
final Supplier<ModMetadataProviderSupplier> modMetadataProviderSupplier,
9095
final Map<String, TextureResolver> allTextureResolvers)
9196
{
9297
this.config = config;
9398
this.saveConfigFunc = saveConfigFunc;
94-
this.allProviders = allProviders;
9599
this.allTextureResolvers = allTextureResolvers;
96100

101+
final CompletableFuture<Set<CapeProvider>> cfModMetadataProviders =
102+
CompletableFuture.supplyAsync(() -> Optional.ofNullable(config.isLoadProvidersFromMods()
103+
? modMetadataProviderSupplier.get()
104+
: null)
105+
.stream()
106+
.flatMap(ModMetadataProviderSupplier::get)
107+
.collect(Collectors.toSet()));
108+
109+
final CompletableFuture<Set<LocalCustomProvider>> cfSimpleFileSystemProviders =
110+
CompletableFuture.supplyAsync(() -> config.isLoadSimpleLocalProvidersFromFilesystem()
111+
? SimpleFilesystemProviders.createFsProviders(configDir)
112+
: Set.of());
113+
114+
this.allProviders = CapeProviders.findAllProviders(
115+
config.getRemoteCustomProviders(),
116+
cfSimpleFileSystemProviders,
117+
cfModMetadataProviders);
118+
97119
if(LOG.isDebugEnabled())
98120
{
99-
LOG.debug("Providers: {}", allProviders.keySet());
121+
LOG.debug("Providers: {}", this.allProviders.keySet());
100122
LOG.debug("Texture-Resolvers: {}", allTextureResolvers.keySet());
101123
}
102124

@@ -113,8 +135,8 @@ public Capes(
113135
this.blockedProviderCapeHashes = Optional.ofNullable(this.config().getBlockedProviderCapeHashes())
114136
.map(map -> map.entrySet()
115137
.stream()
116-
.filter(e -> allProviders.containsKey(e.getKey()))
117-
.collect(Collectors.toMap(e -> allProviders.get(e.getKey()), Map.Entry::getValue)))
138+
.filter(e -> this.allProviders.containsKey(e.getKey()))
139+
.collect(Collectors.toMap(e -> this.allProviders.get(e.getKey()), Map.Entry::getValue)))
118140
.orElseGet(Map::of);
119141
LOG.debug("blockedProviderCapeHashes: {}x", this.blockedProviderCapeHashes.size());
120142

@@ -131,7 +153,7 @@ public Capes(
131153
this.profileTextureLoadThrottler = new ProfileTextureLoadThrottler(
132154
this.playerCapeHandlerManager,
133155
this.playerCacheSize());
134-
this.textureCache = Optional.of(allProviders.values()
156+
this.textureCache = Optional.of(this.allProviders.values()
135157
.stream()
136158
.filter(CapeProvider::canUseCache)
137159
.map(CapeProvider::id)
@@ -151,31 +173,33 @@ public Capes(
151173
.orElse(null);
152174

153175
final long startMs = System.currentTimeMillis();
154-
this.postProcessModProviders();
155-
LOG.debug("Post processing mod providers took {}ms", System.currentTimeMillis() - startMs);
176+
this.autoActivatingProviders = Stream.of(cfSimpleFileSystemProviders.join(), cfModMetadataProviders.join())
177+
.filter(Objects::nonNull)
178+
.flatMap(Collection::stream)
179+
.collect(Collectors.toSet());
180+
this.postProcessAutoActivatingProviders();
181+
LOG.debug("Post processing auto-activating providers took {}ms", System.currentTimeMillis() - startMs);
156182
}
157183

158-
protected void postProcessModProviders()
184+
protected boolean postProcessAutoActivatingProviders()
159185
{
160-
final ModProviderHandling modProviderHandling = this.config().getModProviderHandling();
161-
if(modProviderHandling.activateByDefault())
186+
if(this.config().isActivateExternalProvidersOnInitialLoad())
162187
{
163188
// Works like this:
164189
// Mod is present? -> FirstTimeMissing=Instant.MAX
165190
// Mod was present during last time? -> FirstTimeMissing=NOW
166191
// Remove all mods where FirstTimeMissing is too old
167-
final Set<String> providerIdsLoadedByMods = this.getAllProviders().values()
192+
final Set<String> providerIdsAutoactivating = this.getAllProviders().values()
168193
.stream()
169-
.filter(ModMetadataProvider.class::isInstance)
170-
.map(ModMetadataProvider.class::cast)
171-
.map(CustomProvider::id)
194+
.filter(this.autoActivatingProviders::contains)
195+
.map(CapeProvider::id)
172196
.collect(Collectors.toCollection(LinkedHashSet::new));
173197

174198
final Instant nullPlaceholder = Instant.MAX; // GSON doesn't serialize nulls by default
175199
final Instant now = Instant.now();
176200
final Instant removeOutdated = now.minus(Duration.ofDays(7));
177201
final Map<String, Instant> knownProviderIdsFirstTimeMissing =
178-
Optional.ofNullable(this.config().getKnownModProviderIdsFirstTimeMissing())
202+
Optional.ofNullable(this.config().getKnownAutoActivatingProviderIdsFirstTimeMissing())
179203
.map(Map::entrySet)
180204
.stream()
181205
.flatMap(Collection::stream)
@@ -188,25 +212,27 @@ protected void postProcessModProviders()
188212
final Set<String> activeProviderIds = Objects.requireNonNullElseGet(
189213
this.config().getActiveProviderIds(),
190214
LinkedHashSet::new);
191-
providerIdsLoadedByMods.stream()
215+
providerIdsAutoactivating.stream()
192216
.filter(id -> !knownProviderIdsFirstTimeMissing.containsKey(id))
193217
.forEach(activeProviderIds::add);
194218
this.config().setActiveProviderIds(activeProviderIds);
195219

196-
providerIdsLoadedByMods.forEach(id -> knownProviderIdsFirstTimeMissing.put(id, nullPlaceholder));
220+
providerIdsAutoactivating.forEach(id -> knownProviderIdsFirstTimeMissing.put(id, nullPlaceholder));
197221

198-
this.config().setKnownModProviderIdsFirstTimeMissing(knownProviderIdsFirstTimeMissing);
222+
this.config().setKnownAutoActivatingProviderIdsFirstTimeMissing(knownProviderIdsFirstTimeMissing);
199223
this.saveConfig();
200224

201-
return;
225+
return true;
202226
}
203227

204228
// Reset all known providers due to privacy reasons
205-
if(this.config().getKnownModProviderIdsFirstTimeMissing() != null)
229+
if(this.config().getKnownAutoActivatingProviderIdsFirstTimeMissing() != null)
206230
{
207-
this.config().setKnownModProviderIdsFirstTimeMissing(null);
231+
this.config().setKnownAutoActivatingProviderIdsFirstTimeMissing(null);
208232
this.saveConfig();
233+
return true;
209234
}
235+
return false;
210236
}
211237

212238
public void saveConfig()
@@ -355,4 +381,13 @@ public boolean overwriteSkinTextures(
355381
}
356382
return false;
357383
}
384+
385+
public void reset()
386+
{
387+
this.config.reset();
388+
if(!this.postProcessAutoActivatingProviders())
389+
{
390+
this.saveConfigAndMarkRefresh();
391+
}
392+
}
358393
}

0 commit comments

Comments
 (0)