Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,4 @@ Icon
Network Trash Folder
Temporary Items
.apdisk
/classes/
Original file line number Diff line number Diff line change
Expand Up @@ -60,4 +60,13 @@ public Item getItem() {
public int getMetadata() {
return metadata;
}

@Override
public String toString() {
return "ItemInfo{" +
"metadata=" + metadata +
", item=" + item +
", tag=" + tag +
'}';
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package codersafterdark.reskillable.base;

import codersafterdark.reskillable.Reskillable;
import codersafterdark.reskillable.api.ReskillableAPI;
import codersafterdark.reskillable.api.data.*;
import codersafterdark.reskillable.base.configs.ConfigHandler;
Expand Down Expand Up @@ -29,57 +30,34 @@
import net.minecraftforge.event.world.BlockEvent.BreakEvent;
import net.minecraftforge.fml.common.eventhandler.Event;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.oredict.OreDictionary;
import org.apache.logging.log4j.Level;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.*;

public class LevelLockHandler {
public static final String[] DEFAULT_SKILL_LOCKS = new String[]{"minecraft:iron_shovel:*=reskillable:gathering|5", "minecraft:iron_axe:*=reskillable:gathering|5", "minecraft:iron_sword:*=reskillable:attack|5", "minecraft:iron_pickaxe:*=reskillable:mining|5", "minecraft:iron_hoe:*=reskillable:farming|5", "minecraft:iron_helmet:*=reskillable:defense|5", "minecraft:iron_chestplate:*=reskillable:defense|5", "minecraft:iron_leggings:*=reskillable:defense|5", "minecraft:iron_boots:*=reskillable:defense|5", "minecraft:golden_shovel:*=reskillable:gathering|5,reskillable:magic|5", "minecraft:golden_axe:*=reskillable:gathering|5,reskillable:magic|5", "minecraft:golden_sword:*=reskillable:attack|5,reskillable:magic|5", "minecraft:golden_pickaxe:*=reskillable:mining|5,reskillable:magic|5", "minecraft:golden_hoe:*=reskillable:farming|5,reskillable:magic|5", "minecraft:golden_helmet:*=reskillable:defense|5,reskillable:magic|5", "minecraft:golden_chestplate:*=reskillable:defense|5,reskillable:magic|5", "minecraft:golden_leggings:*=reskillable:defense|5,reskillable:magic|5", "minecraft:golden_boots:*=reskillable:defense|5,reskillable:magic|5", "minecraft:diamond_shovel:*=reskillable:gathering|16", "minecraft:diamond_axe:*=reskillable:gathering|16", "minecraft:diamond_sword:*=reskillable:attack|16", "minecraft:diamond_pickaxe:*=reskillable:mining|16", "minecraft:diamond_hoe:*=reskillable:farming|16", "minecraft:diamond_helmet:*=reskillable:defense|16", "minecraft:diamond_chestplate:*=reskillable:defense|16", "minecraft:diamond_leggings:*=reskillable:defense|16", "minecraft:diamond_boots:*=reskillable:defense|16", "minecraft:shears:*=reskillable:farming|5,reskillable:gathering|5", "minecraft:fishing_rod:*=reskillable:gathering|8", "minecraft:shield:*=reskillable:defense|8", "minecraft:bow:*=reskillable:attack|8", "minecraft:ender_pearl=reskillable:magic|8", "minecraft:ender_eye=reskillable:magic|16,reskillable:building|8", "minecraft:elytra:*=reskillable:defense|16,reskillable:agility|24,reskillable:magic|16", "minecraft:lead=reskillable:farming|5", "minecraft:end_crystal=reskillable:building|24,reskillable:magic|32", "minecraft:iron_horse_armor:*=reskillable:defense|5,reskillable:agility|5", "minecraft:golden_horse_armor:*=reskillable:defense|5,reskillable:magic|5,reskillable:agility|5", "minecraft:diamond_horse_armor:*=reskillable:defense|16,reskillable:agility|16", "minecraft:fireworks=reskillable:agility|24", "minecraft:dye:15=reskillable:farming|12", "minecraft:saddle=reskillable:agility|12", "minecraft:redstone=reskillable:building|5", "minecraft:redstone_torch=reskillable:building|5", "minecraft:skull:1=reskillable:building|20,reskillable:attack|20,reskillable:defense|20"};
private static final Map<LockKey, RequirementHolder> locks = new HashMap<>(); //This should stay private to ensure that it is added to correctly
public static RequirementHolder EMPTY_LOCK = new RequirementHolder();
private static Map<Class<?>, List<Class<? extends LockKey>>> lockTypesMap = new HashMap<>();
private static Map<LockKey, Set<FuzzyLockKey>> fuzzyLockInfo = new HashMap<>();
private static String[] configLocks;
private static Map<String, Class<? extends LockKey>> lockNameMap = new HashMap<>();

public static void loadFromConfig(String[] configValues) {
configLocks = configValues;
static {
registerLockKeyName("itemstack", ItemInfo.class);
registerLockKeyName("mod", ModLockKey.class);
registerLockKeyName("nbt", GenericNBTLockKey.class);
}

public static void setupLocks() {
registerDefaultLockKeys();
if (configLocks != null) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wasn't this code for pulling from the config file?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I looked into how we are doing it currently and the setupLocks method is called in FMLPostInitializationEvent so we use it to register the lock keys, and then if the config has locks we register them. We previously loaded the strings here https://github.com/Coders-After-Dark/Reskillable/blob/develop/1.12.2/src/main/java/codersafterdark/reskillable/base/ConfigHandler.java#L53 Looking at the new code the locks are loaded from json here (which makes sense as all custom locks should have been registered by now from addons). I think this is fine however I think the linked line and LevelLockHandler#loadFromConfig and private static String[] configLocks; should probably be removed as they are no longer used. That or an automated converter should be made to port existing config files to the JSON format. I will mark through the review the lines that should be able to be deleted.

for (String s : configLocks) {
String[] tokens = s.split("=");
if (tokens.length == 2) {
String itemName = tokens[0].toLowerCase();
String[] itemParts = itemName.split(":");
if (itemParts.length == 1) {
addModLock(itemName, RequirementHolder.fromString(tokens[1])); //itemName is really the mod name
continue;
}
int metadata = 0;
if (itemParts.length > 2) {
String meta = itemParts[2];
try {
if (meta.equals("*")) {
metadata = OreDictionary.WILDCARD_VALUE;
} else {
metadata = Integer.parseInt(meta);
}
itemName = itemParts[0] + ':' + itemParts[1];
} catch (NumberFormatException ignored) {
//Do nothing if the meta is not a valid number or wildcard (Maybe it somehow is part of the item name)
}
}
Item item = Item.getByNameOrId(itemName);
if (item != null) {
addLock(new ItemStack(item, 1, metadata), RequirementHolder.fromString(tokens[1]));
}
}
}

try {
ConfigHandler.loadJSONLocks();
} catch (IOException e) {
Reskillable.logger.error("Failed to load jsons", e);
}

}

private static void registerDefaultLockKeys() {
Expand Down Expand Up @@ -109,6 +87,23 @@ public static void registerLockKey(Class<?> lockTypeClass, Class<? extends LockK
}
}

public static void registerLockKeyName(String name, Class<? extends LockKey> lockClass) {
lockNameMap.put(name, lockClass);
}

public static Class<? extends LockKey> getLockKeyClass(String name) {
Class<? extends LockKey> key = lockNameMap.get(name);
if (key == null) {
try {
key = (Class<? extends LockKey>) Class.forName(name);
} catch (ClassNotFoundException | ClassCastException e) {
Reskillable.logger.error("Failed to load LockKeyClass: ", e);
}
}

return key;
}

/**
* Adds locks to the given key.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -1,52 +1,55 @@
package codersafterdark.reskillable.base.configs;

import codersafterdark.reskillable.Reskillable;
import codersafterdark.reskillable.base.LevelLockHandler;
import codersafterdark.reskillable.base.configs.json.LockJson;
import codersafterdark.reskillable.base.configs.json.LockTypeJsonFactory;
import codersafterdark.reskillable.lib.LibMisc;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonParseException;
import com.google.gson.reflect.TypeToken;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.common.config.Configuration;

import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static codersafterdark.reskillable.base.configs.ConfigUtilities.loadPropBool;

public class ConfigHandler {

/////////////////
// Directories //
/////////////////
private static File configDir;
private static File jsonDir;


/////////////
// Configs //
/////////////
public static Configuration mainConfig;
public static Gson locks;

////////////////////
// Default Values //
////////////////////

/// Main Config ///
public static boolean disableSheepWool = true;
public static boolean enforceFakePlayers = true;

////////////////////
// Default Values //
////////////////////
public static boolean enableTabs = true;
public static boolean enableLevelUp = true;
public static boolean hideRequirements = true;

public static Map<String, Configuration> cachedConfigs = new HashMap<>();
/////////////////
// Directories //
/////////////////
private static File configDir;
private static File jsonDir;

public static void init(File file) {
generateFolder(file);
mainConfig = new Configuration(new File(configDir.getPath(), "reskillable.cfg"));
mainConfig.load();
loadData();
loadJSONLocks();
cachedConfigs.put(LibMisc.MOD_ID, mainConfig);
MinecraftForge.EVENT_BUS.register(ConfigListener.class);
}
Expand All @@ -58,33 +61,42 @@ public static void loadData() {
enableLevelUp = loadPropBool(mainConfig, "Enable Level-Up Button", "Set this to false to remove the level-up button if you don't want to use another means to leveling-up skills!", true);
hideRequirements = loadPropBool(mainConfig, "Hide Requirements", "Set this to false to not require holding down the shift key to view requirements!", true);

String desc = "Set requirements for items in this list. Each entry is composed of the item key and the requirements\n"
+ "The item key is in the simple mod:item_id format. Optionally, it can be in mod:item_id:metadata, if you want to match metadata.\n"
+ "The requirements are in a comma separated list, each in a key|value format. For example, to make an iron pickaxe require 5 mining\n"
+ "and 5 building, you'd use the following string:\n"
+ "\"minecraft:iron_pickaxe=mining|5,building|5\"\n\n"
+ "Item usage can also be locked behind an advancement, by using adv|id. For example, to make the elytra require the \"Acquire Hardware.\" advancement\n"
+ "you'd use the following string:\n"
+ "\"minecraft:elytra=adv|minecraft:story/smelt_iron\"\n\n"
+ "Skill requirements and advancements can be mixed and matched, so you can make an item require both, if you want.\n"
+ "You can also lock placed blocks from being used or broken, in the same manner.\n\n"
+ "Locks defined here apply to all the following cases: Right clicking an item, placing a block, breaking a block, using a block that's placed,\n"
+ "left clicking an item, using an item to break any block, and equipping an armor item.\n\n"
+ "You can lock entire mods by just using their name as the left argument. You can then specify specific items to not be locked,\n"
+ "by defining their lock in the normal way. If you want an item to not be locked in this way, use \"none\" after the =";
String[] locks = mainConfig.getStringList("Skill Locks", Configuration.CATEGORY_GENERAL, LevelLockHandler.DEFAULT_SKILL_LOCKS, desc);

LevelLockHandler.loadFromConfig(locks);

if (mainConfig.hasChanged()) {
mainConfig.save();
}
}

public static void loadJSONLocks(){
File mainLocks = new File(jsonDir, "defaultLocks.json");
String json = locks.toJson(LevelLockHandler.DEFAULT_SKILL_LOCKS);
ConfigUtilities.writeStringToFile(json, mainLocks);
public static void loadJSONLocks() throws IOException {
Reskillable.logger.info("Starting to load json");
if (!jsonDir.exists() && jsonDir.mkdir()) {
Reskillable.logger.warn("Couldn't create json lock file directory");
}

File[] list = jsonDir.listFiles();
if (list != null && list.length == 0) {
Files.copy(ConfigHandler.class.getResourceAsStream("/defaultLocks.json"), Paths.get(jsonDir.getAbsolutePath(), "defaultLocks.json"));
}

Files.walk(jsonDir.toPath())
.filter(Files::isRegularFile)
.filter(path -> path.toString().endsWith(".json"))
.sorted()
.forEachOrdered(path -> {
Reskillable.logger.info("Starting to load from file " + path);

try (FileReader reader = new FileReader(path.toFile())) {
List<LockJson> obj = LockTypeJsonFactory.constructGSON().fromJson(reader, new TypeToken<List<LockJson>>() {
}.getType());
Reskillable.logger.info("Locks loaded " + obj.size() + ": " + obj);

for (LockJson lockJson : obj) {
LevelLockHandler.addLockByKey(lockJson.getLockKey(), lockJson.getRequirements());
}

} catch (IOException | JsonParseException e) {
Reskillable.logger.error("Couldn't load json from " + path, e);
}
});
}


Expand All @@ -99,6 +111,5 @@ public static void generateFolder(File file) {
}
configDir = dir;
jsonDir = dir2;
locks = new GsonBuilder().setPrettyPrinting().create();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package codersafterdark.reskillable.base.configs.json;

import codersafterdark.reskillable.api.data.LockKey;
import codersafterdark.reskillable.api.data.RequirementHolder;

public class LockJson {
private RequirementHolder requirements;
private LockKey lockKey;

public LockJson(RequirementHolder requirements, LockKey lockKey) {
this.requirements = requirements;
this.lockKey = lockKey;
}

public RequirementHolder getRequirements() {
return requirements;
}

public LockKey getLockKey() {
return lockKey;
}

@Override
public String toString() {
return "LockJson{" +
"requirements=" + requirements +
", lockKey=" + lockKey +
'}';
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package codersafterdark.reskillable.base.configs.json;

import codersafterdark.reskillable.api.data.ItemInfo;
import codersafterdark.reskillable.base.configs.json.parsers.CustomItemInfoJson;
import codersafterdark.reskillable.base.configs.json.parsers.CustomItemJson;
import codersafterdark.reskillable.base.configs.json.parsers.CustomLockJson;
import codersafterdark.reskillable.base.configs.json.parsers.CustomNBTJson;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonDeserializer;
import net.minecraft.item.Item;
import net.minecraft.nbt.NBTTagCompound;

import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;

public class LockTypeJsonFactory {
private static Map<Type, JsonDeserializer> deserializerMap = new HashMap<>();

static {
registerDeserializer(LockJson.class, new CustomLockJson());
registerDeserializer(Item.class, new CustomItemJson());
registerDeserializer(ItemInfo.class, new CustomItemInfoJson());
registerDeserializer(NBTTagCompound.class, new CustomNBTJson());
}

public static void registerDeserializer(Type type, JsonDeserializer deserializer) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be accessible through the api somehow?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am leaving this up to pup, as I am not sure how he wants the API to look like.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is probably fine given the JSON stuff will probably be for a larger version such as 2.0 which will allow for refactoring a bunch of stuff without worrying about breaking API things. Given a bunch of the stuff in LevelLockHandler should probably be moved from the base package to an API package. The only reason it is in LevelLockHandler to begin with is that it basically grew as I was fixing and then expanding on things making them more generic and supporting custom registrations.

deserializerMap.put(type, deserializer);
}

public static Gson constructGSON() {
GsonBuilder builder = new GsonBuilder()
.setPrettyPrinting();

deserializerMap.forEach(builder::registerTypeAdapter);

return builder.create();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package codersafterdark.reskillable.base.configs.json.parsers;

import codersafterdark.reskillable.api.data.ItemInfo;
import com.google.gson.*;
import net.minecraft.item.Item;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraftforge.oredict.OreDictionary;

import java.lang.reflect.Type;

public class CustomItemInfoJson implements JsonDeserializer<ItemInfo> {
@Override
public ItemInfo deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
JsonObject obj = json.getAsJsonObject();
JsonElement itemElem = obj.get("item");

if (itemElem == null)
throw new JsonParseException("No key 'item' given in ItemInfo json.");

Item item = context.deserialize(itemElem, Item.class);
int meta = !obj.has("metadata") ? 0 : obj.get("metadata").getAsString().equals("*") ? OreDictionary.WILDCARD_VALUE : obj.get("metadata").getAsInt();
JsonElement nbtJson = obj.get("nbt");
NBTTagCompound tagCompound = null;
if (nbtJson != null) {
tagCompound = context.deserialize(nbtJson, NBTTagCompound.class);
}

return new ItemInfo(item, meta, tagCompound);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package codersafterdark.reskillable.base.configs.json.parsers;

import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
import net.minecraft.item.Item;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.fml.common.registry.ForgeRegistries;

import java.lang.reflect.Type;

public class CustomItemJson implements JsonDeserializer<Item> {
@Override
public Item deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
return ForgeRegistries.ITEMS.getValue(new ResourceLocation(json.getAsString()));
}
}
Loading