To add my 2 cents on the issue:
IHasModel makes you write redundant code. It's not bad in a sense that it will break compatibility or something, it's bad because you literally write way too much code. For an interface that's suposed to simplify the code it's doing the exact opposite. Consider the following:
A typical IHasModel usage:
class ItemX extends Item implements IHasModel
{
...
@Override
void registerModels()
{
Mod.proxy.registerModel(this, 0, "inventory");
}
}
class ClientProxy implements IProxy
{
...
@Override
void registerModel(Item i, int meta, string variant)
{
ModelLoader.setCustomModelResourceLocation(i, new ModelResourceLocation(i.getRegistryName(), variant));
}
@Override
void registerModel(Block b, int meta, string variant)
{
ModelLoader.setCustomModelResourceLocation(Item.getItemFromBlock(b), new ModelResourceLocation(b.getRegistryName(), variant));
}
}
@EventBusSubscriber
class ModelHandler
{
@SubscribeEvent
public static void onModelRegistry(ModelRegistryEvent event)
{
for (Item i : ModItems.ITEMS)
{
if (i instanceof IHasModel)
{
((IHasModel)i).registerModel();
}
}
for (Block i : ModBlocks.BLOCKS)
{
if (i instanceof IHasModel)
{
((IHasModel)i).registerModel();
}
}
}
}
And then you have to duplicate the code in ItemX for every item class you have. While you can have something like ItemBase do that for you you will STILL have to replicate this code for every item that doesn't extend ItemBase, like custom tools or armor or god knows what.
Now compare this to the "correct" approach to the situation:
@EventBusSubscriber
class ModelHandler
{
@SubscribeEvent
public static void onModelRegistry(ModelRegistryEvent event)
{
StreamSupport.stream(Item.REGISTRY.spliterator(), false).filter(i -> i.getRegistryName().getResourceDomain().equalsIgnoreCase("modid")).forEach(i -> ModelLoader.setCustomModelResourceLocation(i, 0, new ModelResourceLocation(i.getRegistryName(), "inventory")));
}
}
..and you are done. No, really, that's it.
How about items that have damage?
@EventBusSubscriber
class ModelHandler
{
@SubscribeEvent
public static void onModelRegistry(ModelRegistryEvent event)
{
StreamSupport.stream(Item.REGISTRY.spliterator(), false).filter(i -> i.getRegistryName().getResourceDomain().equalsIgnoreCase("modid")).forEach(i ->
{
ModelResourceLocation staticLocation = new ModelResourceLocation(i.getRegistryName(), "inventory");
if (i.getMaxDamage() == 0)
{
ModelLoader.setCustomModelResourceLocation(i, 0, staticLocation);
}
else
{
ModelLoader.setCustomMeshDefinition(i, it -> staticLocation);
ModelBakery.registerItemVariants(i, staticLocation);
}
});
}
}
What about special cases? This is where an interface is acceptable. There, that's all the code you need and even this can be simplified further using streams better than I did. No need to repeat your code for each new item.
CommonProxy:
Of course a common proxy isn't going to cause incompatibilities. There are 2 issues with this concept - the first one is the name, the second one - the whole concept. The tutorials on the internet don't explain you what a proxy is or why it is needed, they just blindly tell you - create these classes, do that and this and it will work. The thing is though - a common proxy makes no sense at all if you think about it. Proxies existed to separate sided only code. If your code is common it goes anywhere else but the proxy. Think about it. A network handler wouldn't have a "Common" side - it has a client and a server. Why would a proxy have it?
As for the "clutter" argument - please provide me an example of things you put in your common proxy that would otherwise clutter your mod class. What code do you even have there? 95% of all things were done with event handlers anyway, you rarely needed to do something in your FML lifecycle events.
Additionally I just want to point out that I indeed had helped people on this very forum that had an issue with their code where the whole case of the issue was indeed the CommonProxy. So it's not even harmless.
Also we are talking about 1.13.2. Proxies are gone anyway.
As for the whole "it's fine for a beginner, they will learn to do stuff properly later" argument - no, they won't. Look at the most popular mods out there. They still use IHasModel and CommonProxy. And many other things. Clearly those people haven't learned.