Jump to content

Looking for some guidance regarding hierarchy and code organization


DavidTriphon

Recommended Posts

Hello,

 

I'm fairly new to the modding scene and have been experimenting with forge in 1.8 after watching and reading several tutorials (most of all MrCrayfish's tutorial videos). I think I have some grasp on some basics now: creating new blocks, items, adding models and textures, and adding recipes. But what I'm most worried about is accidentally learning bad habits from all the tutorials I've been watching. I don't think I've ever seen a programmer explain why their file hierarchy is the way it is. I've been mostly trying to avoid asking for help because I assume that all of you reading this probably have something better to do than trying to help another newbie programmer, but I felt that I might be able to benefit significantly from hearing about some of your experience regarding organization and programming habits.

 

I guess I can give some specifics to at least jumpstart discussion:

 

  • How do you organize your files, and what features are commonly used in forge that I should prepare my hierarchy for?
     
  • What is a proxy used for? I was told to make one for my mod class to reference, but all it does is call my block and item initialization files to register renders. What else would normally go in those files?
     
  • I've seen some people specify the qualities of blocks within the extended block class files, and others specify it in the block intilization class. Which is better practice, or are both fine?
     
  • Do you have any other tips that you wish you had received when you started programming/modding? I would love to hear them.

 

some of my files for reference:

 

EngineeringMod.java:

 

 

package davidt.tutorial;

import davidt.tutorial.init.EngineeringBlocks;
import davidt.tutorial.init.EngineeringItems;
import davidt.tutorial.init.EngineeringRecipes;
import davidt.tutorial.init.EngineeringTileEntities;
import davidt.tutorial.proxy.CommonProxy;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.Mod.EventHandler;
import net.minecraftforge.fml.common.SidedProxy;
import net.minecraftforge.fml.common.event.FMLInitializationEvent;
import net.minecraftforge.fml.common.event.FMLPostInitializationEvent;
import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;

@Mod(modid = Reference.MOD_ID, name = Reference.MOD_NAME, version = Reference.VERSION)
public class EngineeringMod
{

@SidedProxy(clientSide = Reference.CLIENT_PROXY_CLASS, serverSide = Reference.SERVER_PROXY_CLASS)
public static CommonProxy proxy;

public static final EngineeringTab tabEngineering = new EngineeringTab("tabEngineering");

@EventHandler
public void preInit(FMLPreInitializationEvent event)
{
	EngineeringBlocks.init();
	EngineeringBlocks.register();
	EngineeringItems.init();
	EngineeringItems.register();
	EngineeringRecipes.addRecipes();
	EngineeringTileEntities.register();
}

@EventHandler
public void init(FMLInitializationEvent event)
{
	proxy.registerRenders();
}

@EventHandler
public void postInit(FMLPostInitializationEvent event)
{

}
}

 

 

init.EngineeringBlocks.java:

 

 

package davidt.tutorial.init;

import davidt.tutorial.EngineeringMod;
import davidt.tutorial.Reference;
import davidt.tutorial.blocks.*;
import net.minecraft.block.Block;
import net.minecraft.block.material.Material;
import net.minecraft.client.Minecraft;
import net.minecraft.client.resources.model.ModelResourceLocation;
import net.minecraft.item.Item;
import net.minecraftforge.fml.common.registry.GameRegistry;

public class EngineeringBlocks
{
//public static Block metal_block;
public static Block iron_frame;
public static Block iron_strut;
public static Block iron_plating;
public static Block iron_armor;
public static Block copper_frame;
public static Block copper_strut;
public static Block copper_plating;
public static Block copper_armor;

public static void init()
{
	//metal_block = new BlockMetalBlock(Material.rock).setUnlocalizedName("metal_block").setCreativeTab(EngineeringMod.tabEngineering);
	iron_frame = new BlockIronFrame(Material.rock).setUnlocalizedName("iron_frame").setCreativeTab(EngineeringMod.tabEngineering);
	iron_strut = new BlockIronStrut(Material.rock).setUnlocalizedName("iron_strut").setCreativeTab(EngineeringMod.tabEngineering);
	iron_plating = new BlockIronPlating(Material.rock).setUnlocalizedName("iron_plating").setCreativeTab(EngineeringMod.tabEngineering);
	iron_armor = new BlockIronArmor(Material.rock).setUnlocalizedName("iron_armor").setCreativeTab(EngineeringMod.tabEngineering);
	copper_frame = new BlockCopperFrame(Material.rock).setUnlocalizedName("copper_frame").setCreativeTab(EngineeringMod.tabEngineering);
	copper_strut = new BlockCopperStrut(Material.rock).setUnlocalizedName("copper_strut").setCreativeTab(EngineeringMod.tabEngineering);
	copper_plating = new BlockCopperPlating(Material.rock).setUnlocalizedName("copper_plating").setCreativeTab(EngineeringMod.tabEngineering);
	copper_armor = new BlockCopperArmor(Material.rock).setUnlocalizedName("copper_armor").setCreativeTab(EngineeringMod.tabEngineering);
}

public static void register()
{
	//registerStuff(metal_block);
	registerStuff(iron_frame);
	registerStuff(iron_strut);
	registerStuff(iron_plating);
	registerStuff(iron_armor);
	registerStuff(copper_frame);
	registerStuff(copper_strut);
	registerStuff(copper_plating);
	registerStuff(copper_armor);
}

public static void registerStuff(Block block)
{
	GameRegistry.registerBlock(block, block.getUnlocalizedName().substring(5));
}

public static void registerRenders()
{
	//registerRender(metal_block);
	registerRender(iron_frame);
	registerRender(iron_strut);
	registerRender(iron_plating);
	registerRender(iron_armor);
	registerRender(copper_frame);
	registerRender(copper_strut);
	registerRender(copper_plating);
	registerRender(copper_armor);
}

public static void registerRender(Block block)
{
	Item item = Item.getItemFromBlock(block);
	Minecraft.getMinecraft().getRenderItem().getItemModelMesher().register(item, 0, new ModelResourceLocation(Reference.MOD_ID + ":" 
			+ item.getUnlocalizedName().substring(5), "inventory"));
}
}

 

 

init.EngineeringItems.java:

 

 

package davidt.tutorial.init;

import davidt.tutorial.EngineeringMod;
import davidt.tutorial.Reference;
import davidt.tutorial.items.ItemMetalRod;
import net.minecraft.client.Minecraft;
import net.minecraft.client.resources.model.ModelResourceLocation;
import net.minecraft.item.Item;
import net.minecraftforge.fml.common.registry.GameRegistry;

public class EngineeringItems
{

//stone type rocks
public static Item stone_rock, andesite_rock, diorite_rock, granite_rock, lapis_rock;
//ore type rocks
public static Item iron_rock, gold_rock/*, copper_rock, aluminum_rock*/;
//metal items
public static Item metal_bar;

public static void init()
{
	//stone rocks
	stone_rock = new Item().setUnlocalizedName("stone_rock").setCreativeTab(EngineeringMod.tabEngineering);
	andesite_rock = new Item().setUnlocalizedName("andesite_rock").setCreativeTab(EngineeringMod.tabEngineering);
	diorite_rock = new Item().setUnlocalizedName("diorite_rock").setCreativeTab(EngineeringMod.tabEngineering);
	granite_rock = new Item().setUnlocalizedName("granite_rock").setCreativeTab(EngineeringMod.tabEngineering);
	lapis_rock = new Item().setUnlocalizedName("lapis_rock").setCreativeTab(EngineeringMod.tabEngineering);

	//ore rocks
	iron_rock = new Item().setUnlocalizedName("iron_rock").setCreativeTab(EngineeringMod.tabEngineering);
	gold_rock = new Item().setUnlocalizedName("gold_rock").setCreativeTab(EngineeringMod.tabEngineering);

	//metal items
	metal_bar = new ItemMetalRod().setUnlocalizedName("metal_bar").setCreativeTab(EngineeringMod.tabEngineering);
}

public static void register()
{
	//stone rocks
	registerItem(stone_rock);
	registerItem(andesite_rock);
	registerItem(diorite_rock);
	registerItem(granite_rock);
	registerItem(lapis_rock);

	//ore rocks
	registerItem(iron_rock);
	registerItem(gold_rock);

	//metal items
	registerItem(metal_bar);
}

public static void registerItem(Item item)
{
	GameRegistry.registerItem(item, item.getUnlocalizedName().substring(5));
}

public static void registerRenders()
{
	//stone rocks
	registerRender(stone_rock);
	registerRender(andesite_rock);
	registerRender(diorite_rock);
	registerRender(granite_rock);
	registerRender(lapis_rock);

	//ore rocks
	registerRender(iron_rock);
	registerRender(gold_rock);

	//metal items
	registerRender(metal_bar);
}

public static void registerRender(Item item)
{
	Minecraft.getMinecraft().getRenderItem().getItemModelMesher().register(item, 0, new ModelResourceLocation(Reference.MOD_ID + ":" 
			+ item.getUnlocalizedName().substring(5), "inventory"));
}
}

 

 

proxy.ClientProxy.java:

 

 

package davidt.tutorial.proxy;

import davidt.tutorial.init.EngineeringBlocks;
import davidt.tutorial.init.EngineeringItems;

public class ClientProxy extends CommonProxy
{
@Override
public void registerRenders()
{
	EngineeringBlocks.registerRenders();
	EngineeringItems.registerRenders();
}
}

 

 

proxy.CommonProxy.java: (literally does nothing but exist)

 

 

package davidt.tutorial.proxy;

public class CommonProxy
{
public void registerRenders()
{

}
}

 

Always, RESEARCH before asking a question. It's likely someone has already asked your question in the past. Filter your google searches with "site:www.minecraftforge.net/forum" to have a better chance finding your problem.

Link to comment
Share on other sites

Well first of all, most programmers end up with a distinctive style. Some styles can have negative consequences (like being prone to errors, or being hard to maintain) while others are simply choices that are equally good. If you're the only coder on your mods, then usually you can express your own style, but when doing a joint project it is important to establish an agreed style / organization for everyone to follow. In fact in multi-coder projects you usually have to make sure your IDE has same code style settings as well because all the automatic indenting and such can make the version control system think changes are being made when they aren't.

 

Anyway, beyond that I'd say the next question is whether it will be a complicated mod or a simple mod. Mostly complexity can be measured in the number of classes. So if you're just adding a single item or something you probably can use a flat hierarchy. However, in my experience even simple mods will get added to so it is best to organize like a big mod even if you think it will only be small.

 

So, assuming you're organizing for a big mod, most teams I've seen do the class hierarchy as follows:

1) top level has mod class and package folders for client and common

2) under each of client and common you have package folders for each type of thing. Like Entities, Blocks, Items, Packets, Particles, GUIs, etc.

3) if you have a lot of any particular thing you might have one more level of package folders. Like under entities you might have a birds, big cats, serpents, etc.

 

That's pretty much it.

 

Note that from a pure coding perspective, package organization is usually more important because there is usually "package private" scope information that allows classes in same package to access each other. But in Minecraft modding people seem to freely make public scope access so this isn't really something needing thought for modding.

 

There are lots of other style questions. Like some people like to register their things from within the constructors. Some people like to create registration methods within each thing, then call those. Mostly these are all valid choices, but sometimes they have impact. Like I like having explicit control of registering because sometimes the order matters (like crops need the block registered before the seed item). But frankly you sort of need to encounter these issues yourself to really figure out how you prefer to tackle the problems.

Check out my tutorials here: http://jabelarminecraft.blogspot.com/

Link to comment
Share on other sites

Thank you! I had not considered sorting my classes between client and common. Generally what sorts of mod aspects go into client side only, server side only or are required to be accessible to both?

Always, RESEARCH before asking a question. It's likely someone has already asked your question in the past. Filter your google searches with "site:www.minecraftforge.net/forum" to have a better chance finding your problem.

Link to comment
Share on other sites

Thank you! I had not considered sorting my classes between client and common. Generally what sorts of mod aspects go into client side only, server side only or are required to be accessible to both?

 

Classes that have @SideOnly annotation for Side.CLIENT should go in client. All others in common.

 

Examples of client-only classes are models, renderers, GUI, key handlers. Basically things that either affect the display or process input are usually client side.

 

So for example, the entities folder under client would have the models and renderer classes under it, whereas the entities folder under common would have the entity class itself.

 

Regarding your question about how the proxy works, I have a information on the subject here: http://jabelarminecraft.blogspot.com/p/minecraft-forge-17217x-quick-tips-for.html

Check out my tutorials here: http://jabelarminecraft.blogspot.com/

Link to comment
Share on other sites

Personally, I don't have a package named 'common', as everything not in the client package is, by default, common. Not that it makes any difference, really, but compare the 2 structures:

main
  |_client
       |_other client packages and classes
  |_common
       |_ other packages and classes that both sides use
  Main.java, possibly some other stuff

main
  |_client
       |_other client packages and classes
  |_ other packages and classes that both sides use
  Main.java, possibly some other stuff
[code]
In the end it's up to you, and as I said, it doesn't matter, but I don't see any benefit to having that particular package; many do, you may, too. Even the client package isn't really necessary, but it can be helpful, especially if you have lots of client-side classes or need that extra reminder that your code is only going to be on one side.

Just do whatever makes sense for your project now, and change it later if it no longer fits - it's not the end of the world to restructure your packages. 

The only real advice I can give you is this: look at LOTS of other peoples' projects, both Minecraft- and non-Minecraft-related. Non only will that help give you ideas about package structures, but if you read the code you will also learn a lot about various design strategies - and those, I would argue, are much more important and will greatly influence the way your packages turn out.

Plus, as you read others' code, you will see styles you like and styles you don't. Don't get bogged down in it, though - the most important thing you can do is simply start writing code. The more you write, the more you will learn. Sure, you'll make lots of mistakes. Everyone does. Whether / how you learn from them will determine your level of success.

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Announcements



  • Recently Browsing

    • No registered users viewing this page.
  • Posts

    • Hi, I want to make a client-only mod, everything is ok, but when I use shaders, none of the textures rendered in RenderLevelStageEvent nor the crow entity model are rendered, I want them to be visible, because it's a horror themed mod Here is how i render the crow model in the CrowEntityRenderer<CrowEntity>, by the time i use this method, i know is not the right method but i don't think this is the cause of the problem, the renderType i'm using is entityCutout @Override public void render(CrowEntity p_entity, float entityYaw, float partialTick, PoseStack poseStack, MultiBufferSource bufferSource, int packedLight) { super.render(p_entity, entityYaw, partialTick, poseStack, bufferSource, packedLight); ClientEventHandler.getClient().crow.renderToBuffer(poseStack, bufferSource.getBuffer(ClientEventHandler.getClient().crow .renderType(TEXTURE)), packedLight, OverlayTexture.NO_OVERLAY, Utils.rgb(255, 255, 255)); } Here renderLevelStage @Override public void renderWorld(RenderLevelStageEvent e) { horrorEvents.draw(e); } Here is how i render every event public void draw(RenderLevelStageEvent e) { for (HorrorEvent event : currentHorrorEvents) { event.tick(e.getPartialTick()); event.draw(e); } } Here is how i render the crow model on the event @Override public void draw(RenderLevelStageEvent e) { if(e.getStage() == RenderLevelStageEvent.Stage.AFTER_ENTITIES) { float arcProgress = getArcProgress(0.25f); int alpha = (int) Mth.lerp(arcProgress, 0, 255); int packedLight = LevelRenderer.getLightColor(Minecraft.getInstance().level, blockPos); VertexConsumer builder = ClientEventHandler.bufferSource.getBuffer(crow); Crow<CreepyBirdHorrorEvent> model = ClientEventHandler .getClient().crow; model.setupAnim(this); RenderHelper.renderModelInWorld(model, position, offset, e.getCamera(), e.getPoseStack(), builder, packedLight, OverlayTexture.NO_OVERLAY, alpha); builder = ClientEventHandler.bufferSource.getBuffer(eyes); RenderHelper.renderModelInWorld(model, position, offset, e.getCamera(), e.getPoseStack(), builder, 15728880, OverlayTexture.NO_OVERLAY, alpha); } } How i render the model public static void renderModelInWorld(Model model, Vector3f pos, Vector3f offset, Camera camera, PoseStack matrix, VertexConsumer builder, int light, int overlay, int alpha) { matrix.pushPose(); Vec3 cameraPos = camera.getPosition(); double finalX = pos.x - cameraPos.x + offset.x; double finalY = pos.y - cameraPos.y + offset.y; double finalZ = pos.z - cameraPos.z + offset.z; matrix.pushPose(); matrix.translate(finalX, finalY, finalZ); matrix.mulPose(Axis.XP.rotationDegrees(180f)); model.renderToBuffer(matrix, builder, light, overlay, Utils .rgba(255, 255, 255, alpha)); matrix.popPose(); matrix.popPose(); } Thanks in advance
    • Here's the link: https://mclo.gs/7L5FibL Here's the link: https://mclo.gs/7L5FibL
    • Also the mod "Connector Extras" modifies Reach-entity-attributes and can cause fatal errors when combined with ValkrienSkies mod. Disable this mod and continue to use Syntra without it.
    • Hi everyone. I was trying modify the vanilla loot of the "short_grass" block, I would like it drops seeds and vegetal fiber (new item of my mod), but I don't found any guide or tutorial on internet. Somebody can help me?
    • On 1.20.1 use ValkrienSkies mod version 2.3.0 Beta 1. I had the same issues as you and it turns out the newer beta versions have tons of unresolved incompatibilities. If you change the version you will not be required to change the versions of eureka or any other additions unless prompted at startup. This will resolve Reach-entity-attributes error sound related error and cowardly errors.
  • Topics

×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.