Skip to content

Commit

Permalink
Custom name (#5)
Browse files Browse the repository at this point in the history
- Added the `custom_name` module
- Fixed the `parameters` block being mandatory on modules with only
optional parameters.
- Fixed items with no variant using the fallback model instead of the
vanilla one.
  • Loading branch information
Estecka authored Aug 28, 2024
1 parent bb94cbd commit d8a6914
Show file tree
Hide file tree
Showing 10 changed files with 111 additions and 37 deletions.
5 changes: 5 additions & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,13 @@
- Added the `level` model predicate for enchanted books.

# v2
## 2.0
- Renammed from Enchants-CIT to Variants-CIT
- CIT logic is now modular, and can be extended via an api
- CIT modules are now enabled and configured via resource packs
- Added CIT modules: `axolotl_variant`, `custom_data`, `instrument`, `jukebox_playable`, `potion_type`, `stored_enchantments`
- Removed embedded resource pack.
## 2.1
- Added the `custom_name` module
- Fixed the `parameters` block being mandatory on modules with only optional parameters.
- Fixed items with no variant using the fallback model instead of the vanilla one.
15 changes: 8 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,21 +1,20 @@
# Variants-CIT
A CIT logic for MC 1.21, specialized in handling items with standardized variants.
A CIT logic for MC 1.21, optimized around items with standardized variants.

**This mod is not a plug-and-play replacement for Optifine/CIT-resewn;** it uses its own resource format. Changes to older packs are required for them to work.
On low-end PCs, using this mod instead of the optifine format may lead to improved performances in scenarios where a single item has very many different variants.

The mod is not as all-purpose as optifine, it still requires specialized code for most item type, but this code is modular and easy to expand upon. Other mods can also add custom modules for their own items.

Built-in modules support **Axolotl Buckets**, **Enchanted Books**, **Music Discs**, **Goat Horns**, and **Potions**.
There are also more generic modules that can identify a variant from the `custom_data` or `custom_name` component of an item.
If Mojang ever makes these items more componentized, you can expect Banner Patterns, Trim Templates, and Pottery Sherds to become supported in the future.

For datapack makers, there is also a more generic module that can pull a variant id from the `custom_data` component of an item.

## Resource Pack Format
A resource pack who wants to modify an item must start by providing a configuration file to declare what item to change, where its resources are located, and which logic to use.
The targetd item type is automatically derived from the file name: `/assets/<namespace>/variants-cit/item/<name>.json`
The format revolves around item variants (reduced to namespaced identifiers) being automatically associated to [item models](https://minecraft.wiki/w/Model#Item_models) with matching names, stored in a directory of your choosing.

The mod will look for [item models](https://minecraft.wiki/w/Model#Item_models) stored in a chosen directory, and automatically associate them to variants with the corresponding namespace and name.
Resource packs must start by providing a configuration file, that defines what item type is affected, how to figure out its variant, and where the models are located.

For example, here's a module that would reproduce the behaviour of the previous version of the mod, Enchants-CIT :
`/assets/minecraft/variant-cits/item/enchanted_book.json`
Expand All @@ -28,6 +27,8 @@ For example, here's a module that would reproduce the behaviour of the previous
}
}
```
Here, the enchantment `minecraft:unbreaking` will be associated with the model `minecraft:item/enchanted_book/unbreaking`, which is stored at `/assets/minecraft/models/item/enchanted_book/unbreaking.json`
The targeted item type is automatically derived from the file name of the config.
Here, the enchantment `minecraft:unbreaking` will be associated with the model stored at `/assets/minecraft/models/item/enchanted_book/unbreaking.json`

Some module types may define additional models to use in special cases, or take addional parameters. See the [wiki](/~https://github.com/Estecka/mc-Variants-CIT/wiki) for a more complete guide.
Some module types may define additional models to use in special cases, or take addional parameters.
See the [wiki](/~https://github.com/Estecka/mc-Variants-CIT/wiki) for a more complete guide.
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,6 @@ loader_version=0.16.2
fabric_version=0.102.1+1.21.1

# Mod Properties
mod_version=2.0.0
mod_version=2.1.0
maven_group=fr.estecka.variantscit
archives_base_name=variants-cit
64 changes: 38 additions & 26 deletions src/main/java/fr/estecka/variantscit/ModuleLoader.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import java.util.concurrent.Executor;
import org.jetbrains.annotations.Nullable;
import com.google.gson.JsonObject;
import com.mojang.serialization.DataResult;
import com.mojang.serialization.JsonOps;
import net.fabricmc.fabric.api.client.model.loading.v1.PreparableModelLoadingPlugin.DataLoader;
import net.minecraft.item.Item;
Expand All @@ -27,44 +28,28 @@ public CompletableFuture<Map<Item,VariantManager>> load(ResourceManager resource
private Map<Item,VariantManager> ReloadModules(ResourceManager manager){
Map<Item,VariantManager> result = new HashMap<>();

for (Map.Entry<Identifier, Resource> entry : manager.findResources("variant-cits/item", id->id.getPath().endsWith(".json")).entrySet()){
for (Map.Entry<Identifier, Resource> entry : manager.findResources("variant-cits/item", id->id.getPath().endsWith(".json")).entrySet())
{
Identifier resourceId = entry.getKey();
Item item = ItemFromModule(resourceId);
Item item = ItemFromResourcePath(resourceId);
if (item == null)
continue;

JsonObject json;
VariantManager module;
try {
json = JsonHelper.deserialize(entry.getValue().getReader());
DataResult<VariantManager> dataResult = ManagerFromResource(entry.getValue());
if (dataResult.isSuccess()){
VariantManager module = dataResult.getOrThrow();
module.ReloadVariants(manager);
result.put(item, module);
}
catch (IOException e){
VariantsCitMod.LOGGER.error("{}", e);
continue;
}

var dataResult = ModuleDefinition.CODEC.decoder().decode(JsonOps.INSTANCE, json);
if (dataResult.isError()){
else {
VariantsCitMod.LOGGER.error("Error in cit module {}: {}", resourceId, dataResult.error().get().message());
continue;
}

try {
module = ModuleRegistry.CreateManager(dataResult.getOrThrow().getFirst(), json);
}
catch (IllegalArgumentException|IllegalStateException e){
VariantsCitMod.LOGGER.error("Error building cit module of type {}: {}", dataResult.getPartialOrThrow().getFirst().type(), e);
continue;
}

module.ReloadVariants(manager);
result.put(item, module);
}

return result;
}

static private @Nullable Item ItemFromModule(Identifier resourceId){
static private @Nullable Item ItemFromResourcePath(Identifier resourceId){
String path = resourceId.getPath();
path = path.substring("variant-cits/item".length() + 1, path.length() - ".json".length());

Expand All @@ -75,4 +60,31 @@ private Map<Item,VariantManager> ReloadModules(ResourceManager manager){
else
return null;
}

static private DataResult<VariantManager> ManagerFromResource(Resource resource){
JsonObject json;
try {
json = JsonHelper.deserialize(resource.getReader());
}
catch (IOException e){
return DataResult.error(e::toString);
}

var dataResult = ModuleDefinition.CODEC.decoder().decode(JsonOps.INSTANCE, json);
if (dataResult.isError()){
return DataResult.error(dataResult.error().get()::message);
}

try {
ModuleDefinition definition = dataResult.getOrThrow().getFirst();
JsonObject parameters = json.getAsJsonObject("parameters");
if (parameters == null)
parameters = new JsonObject();

return DataResult.success(ModuleRegistry.CreateManager(definition, parameters));
}
catch (IllegalArgumentException|IllegalStateException|ClassCastException e){
return DataResult.error(e::toString);
}
}
}
4 changes: 2 additions & 2 deletions src/main/java/fr/estecka/variantscit/ModuleRegistry.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ static private interface ManagerFactory {
static public <T> void Register(Identifier type, ComplexCitModuleFactory<T> moduleFactory, MapCodec<T> codec){
assert moduleFactory != null;
Register(type, (ModuleDefinition config, JsonObject json) -> {
var data = codec.fieldOf("parameters").decoder().decode(JsonOps.INSTANCE, json);
var data = codec.decoder().decode(JsonOps.INSTANCE, json);
ICitModule module = moduleFactory.Build(config.GetSpecialModelIds(), data.getOrThrow().getFirst());
return new VariantManager(config, module);
});
Expand All @@ -33,7 +33,7 @@ static public <T> void Register(Identifier type, ComplexCitModuleFactory<T> modu
static public <T> void Register(Identifier type, ParameterizedCitModuleFactory<T> moduleFactory, MapCodec<T> codec){
assert moduleFactory != null;
Register(type, (ModuleDefinition config, JsonObject json) -> {
var data = codec.fieldOf("parameters").decoder().decode(JsonOps.INSTANCE, json);
var data = codec.decoder().decode(JsonOps.INSTANCE, json);
ICitModule module = moduleFactory.Build(data.getOrThrow().getFirst());
return new VariantManager(config, module);
});
Expand Down
6 changes: 5 additions & 1 deletion src/main/java/fr/estecka/variantscit/VariantManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,11 @@ public VariantManager(ModuleDefinition definition, ICitModule module){
}

public @Nullable ModelIdentifier GetModelVariantForItem(ItemStack stack){
ModelIdentifier modelId = this.variantModels.get(module.GetItemVariant(stack));
Identifier variant = module.GetItemVariant(stack);
if (variant == null)
return null;

ModelIdentifier modelId = this.variantModels.get(variant);
return (modelId != null) ? modelId : this.fallbackModel;
}

Expand Down
1 change: 1 addition & 0 deletions src/main/java/fr/estecka/variantscit/VariantsCitMod.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ public void onInitializeClient(){

ModuleRegistry.Register(Identifier.ofVanilla("axolotl_variant"), new AxolotlBucketModule());
ModuleRegistry.Register(Identifier.ofVanilla("custom_data"), CustomVariantModule::new, CustomVariantModule.CODEC);
ModuleRegistry.Register(Identifier.ofVanilla("custom_name"), CustomNameModule::new, CustomNameModule.CODEC);
ModuleRegistry.Register(Identifier.ofVanilla("instrument"), new GoatHornModule());
ModuleRegistry.Register(Identifier.ofVanilla("jukebox_playable"), new MusicDiscModule());
ModuleRegistry.Register(Identifier.ofVanilla("potion_effect"), new PotionEffectModule());
Expand Down
51 changes: 51 additions & 0 deletions src/main/java/fr/estecka/variantscit/modules/CustomNameModule.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package fr.estecka.variantscit.modules;

import java.util.HashMap;
import java.util.Map;
import com.mojang.serialization.Codec;
import com.mojang.serialization.MapCodec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import fr.estecka.variantscit.api.ISimpleCitModule;
import net.minecraft.component.DataComponentTypes;
import net.minecraft.item.ItemStack;
import net.minecraft.text.Text;
import net.minecraft.util.Identifier;

public class CustomNameModule
implements ISimpleCitModule
{
static public record CustomNameConfig(boolean caseSensitive, Map<String, Identifier> specialNames) {}

static public final MapCodec<CustomNameConfig> CODEC = RecordCodecBuilder.mapCodec(builder->builder
.group(
Codec.BOOL.fieldOf("caseSensitive").orElse(false).forGetter(p->p.caseSensitive),
Codec.unboundedMap(Codec.STRING, Identifier.CODEC).fieldOf("specialNames").orElse(Map.of()).forGetter(p->p.specialNames)
)
.apply(builder, CustomNameConfig::new)
);

private final boolean caseSensitive;
private final Map<String,Identifier> specialNames = new HashMap<>();

public CustomNameModule(CustomNameConfig params){
this.caseSensitive = params.caseSensitive;
if (caseSensitive)
this.specialNames.putAll(params.specialNames);
else for (var e : params.specialNames.entrySet())
this.specialNames.put(e.getKey().toLowerCase(), e.getValue());
}

@Override
public Identifier GetItemVariant(ItemStack stack){
Text component = stack.get(DataComponentTypes.CUSTOM_NAME);
if (component == null)
return null;

String name = component.getString();
if (!caseSensitive)
name = name.toLowerCase();

Identifier variant = specialNames.get(name);
return (variant != null) ? variant : Identifier.tryParse(name);
}
}
Binary file modified src/main/resources/assets/variants-cit/icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified src/main/resources/assets/variants-cit/icon.xcf
Binary file not shown.

0 comments on commit d8a6914

Please sign in to comment.