Data Driving Minecraft

Gathering the Resources

Minecraft uses the SimpleReloadableResourceManager and FallbackResourceManager classes to load resources from the assets and data folders. Ideally we could just reuse these classes. Unfortunately there are two major problems with this.

First, the resources aren’t loaded until we have already created our items. This makes sense, since many of the resources in data packs rely on the items already being available (think recipes and models).

Second, SimpleReloadableResourceManager contains more functionality than we need for our purposes. So I went ahead and created a stripped down version specifically for loading Mod data:

public class ModResourceManager implements IResourceManager {
    private final Map<String, FallbackResourceManager> mNamespaceResourceManagers = Maps.newHashMap();
    private final Set<String> mResourceNamespaces = Sets.newLinkedHashSet();
    private final ResourcePackType mType;

    public ModResourceManager(ResourcePackType side, String modId) {
        mType = side;

        ModFileInfo modFileInfo = ModList.get().getModFileById(modId);
        ModFileResourcePack resourcePack = new ModFileResourcePack(modFileInfo.getFile());
        addResourcePack(resourcePack);
    }

    @Override
    public Set<String> getResourceNamespaces() {
        return mResourceNamespaces;
    }

    @Override
    public IResource getResource(ResourceLocation resourceLocation) throws IOException {
        IResourceManager resourceManager = mNamespaceResourceManagers.get(resourceLocation.getNamespace());
        if (resourceManager != null) {
            return resourceManager.getResource(resourceLocation);
        } else {
            throw new FileNotFoundException((resourceLocation.toString()));
        }
    }

    @Override
    public boolean hasResource(ResourceLocation resourceLocation) {
        IResourceManager resourceManager = mNamespaceResourceManagers.get(resourceLocation.getNamespace());
        return resourceManager != null && resourceManager.hasResource(resourceLocation);
    }

    @Override
    public List<IResource> getAllResources(ResourceLocation resourceLocation) throws IOException {
        IResourceManager resourceManager = mNamespaceResourceManagers.get(resourceLocation.getNamespace());
        if (resourceManager != null) {
            return resourceManager.getAllResources(resourceLocation);
        } else {
            throw new FileNotFoundException((resourceLocation.toString()));
        }
    }

    @Override
    public Collection<ResourceLocation> getAllResourceLocations(String path, Predicate<String> filter) {
        Set<ResourceLocation> set = Sets.newHashSet();

        for(FallbackResourceManager fallbackresourcemanager : mNamespaceResourceManagers.values()) {
            set.addAll(fallbackresourcemanager.getAllResourceLocations(path, filter));
        }

        List<ResourceLocation> list = Lists.newArrayList(set);
        Collections.sort(list);
        return list;
    }

    @Override
    public void addResourcePack(IResourcePack resourcePack) {
        for(String s : resourcePack.getResourceNamespaces(mType)) {
            mResourceNamespaces.add(s);
            FallbackResourceManager fallbackResourceManager = mNamespaceResourceManagers.get(s);
            if (fallbackResourceManager == null) {
                fallbackResourceManager = new FallbackResourceManager(mType);
                mNamespaceResourceManagers.put(s, fallbackResourceManager);
            }

            fallbackResourceManager.addResourcePack(resourcePack);
        }
    }
}

This code is mostly copied from SimpleReloadableResourceManager, but there are a few major changes.

First we inherit from IResourceManager instead of IReloadableResourceManager. We also strip out anything to do with reloading from the class. Items are created when the game launches, unlike recipes and models which are created when a world is loaded. They cannot be reloaded without restarting the game. So we don’t need any of that functionality in this class.

Second we have changed the constructor so it takes a Mod ID:

public ModResourceManager(ResourcePackType side, String modId) {
    mType = side;

    ModFileInfo modFileInfo = ModList.get().getModFileById(modId);
    ModFileResourcePack resourcePack = new ModFileResourcePack(modFileInfo.getFile());
    addResourcePack(resourcePack);
}

The ModResourceManager will internally get the mod info it needs and add the resource pack for that mod only. Doing it this way means that the class can be reused in other mods – we just pass in a different ID.

We now have resource manager we can create earlier, and can use to load data from files that can be parsed to create items.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.