Minecraft Modding: Organizing Your Proxy

Introduction


The concept of a "proxy" in Minecraft modding is very important but difficult to understand at first. For the general idea, you should look at TheGreyGhost's Explanation Of Proxies Tutorial.

Got that? Good now let's make the proxies...

Some Difference Of Opinion About Using A "Common" Proxy


In the example implementation for the @SidedProxy annotation, cpw gives an explanation that he intended people to make a CommonProxy class and then extend it for a ClientProxy (and ServerProxy if needed, although rarer).

However, since then some prominent Forge community folk such as diesieben07 have objected to the logic -- by definition "common" stuff doesn't need to be in a proxy.

So while I've historically used the common proxy approach, I have now modified it to an interface-based approach to satisfy the dissenters.

When You Need The Proxy


Key PointYou need to put code in the proxy whenever the same method (i.e. same method name and parameter list) is supposed to act differently on the client versus the server (or common).

If you have a method that is only on one side, it doesn't need to go into the proxy, rather it just can use the @SideOnly annotation.

But for almost all mods, there is a need for the FML life cycle process (which goes through stages of pre-initialization, initialization, post-initialization) to use a proxy because you need to call event handling methods that have the same name and parameters but need to act differently on common and client.

Organizing The Proxies


There are many ways to do organize the proxies, but this works for me:
  1. Create an interface called IProxy. This interface has prototype methods to handle each of the FML lifestyle events ("pre-init", "init", etc.) as well as any other methods my mod might have where behavior differs between sides (such as context processing for custom packets).
  2. Create a class called ClientProxy that extends IProxy.
  3. Create a class called ServerProxy that extends IProxy.
  4. In my main mod class, I create a field called proxy of type IProxy that uses the @SidedProxy annotation to point to these proxy classes.
  5. In my main mod class, I also handle the FML lifestyle events which includes all common (to both sides) code but also call the proxy instance's version of the same method to add any side-specific implementations. 
  6. In the ClientProxy and ServeryProxy I implement all the IProxy interface methods, with side-specific code as appropriate.

Here is an example of a typical IProxy, showing what sort of things should go into each FML life cycle event handling method:

public interface IProxy
{
    /**
     * Fml life cycle event for Pre-Initialization. Historically (before registry events) 
     * this was where blocks, items, etc. were registered. There are still things 
     * like entities and networking which should still be registered here.
     *
     * @param event the event
     */
    void preInit(FMLPreInitializationEvent event);

    /**
     * Fml life cycle event for Initialization. This phase is good for registering event listeners, for registering things that depend on things in pre-init from other mods (like
     * recipes, advancements and such.)
     *
     * @param event the event
     */
    void init(FMLInitializationEvent event);

    /**
     * Fml life cycle event Post Initialization. This phase is useful For doing inter-mod stuff like checking which mods are loaded or if you want a complete view of things across
     * mods like having a list of all registered items to aid random item generation.
     *
     * @param event the event
     */
    void postInit(FMLPostInitializationEvent event);

    /**
     * Fml life cycle event. Server commands should be registered here.
     *
     * @param event the event
     */
    void serverStarting(FMLServerStartingEvent event);

    /*
     * Thanks to CoolAlias for this tip!
     */
    /**
     * Returns a side-appropriate EntityPlayer for use during message handling.
     *
     * @param parContext the context
     * @return the player entity from context
     */
    EntityPlayer getPlayerEntityFromContext(MessageContext parContext);
}

In the above code, the various methods called like registerBlocks() would be methods you'd add to this same class to do what is appropriate for your mod.

Create A ClientProxy Class That Implements IProxy


And here is a typical ClientProxy:

public class ClientProxy implements IProxy
{
    // mouse helper
    public static MouseHelper mouseHelperAI = new MouseHelperAI(); // used to intercept user mouse movement for "bot" functionality

    @Override
    public void preInit(FMLPreInitializationEvent event)
    {
        // DEBUG
        System.out.println("on Client side");
        
        Minecraft.getMinecraft().mouseHelper = ClientProxy.mouseHelperAI;
        RenderFactories.registerEntityRenderers();
    }

    @Override
    public void init(FMLInitializationEvent event)
    {
        // DEBUG
        System.out.println("on Client side");

        // register key bindings
        ModKeyBindings.registerKeyBindings();
    }

    @Override
    public void postInit(FMLPostInitializationEvent event)
    {
        // DEBUG
        System.out.println("on Client side");
    }

    @Override
    public EntityPlayer getPlayerEntityFromContext(MessageContext ctx)
    {
        return (ctx.side.isClient() ? Minecraft.getMinecraft().player : MainMod.proxy.getPlayerEntityFromContext(ctx));
    }

    @Override
    public void serverStarting(FMLServerStartingEvent event)
    {
        // This will never get called on client side
    }
}

The code should be self-explanatory. In this case we're overriding the methods with the the client-specific stuff.

In this example I refer to classes I'm not showing here, such as RenderFactories where I have put my registration code for my entity render class mapping -- you would replace that with code that is appropriate to your mod.

Create A ServerProxy Class That Implements IProxy


And here is a typical ServerProxy:

public class ServerProxy implements IProxy
{
    @Override
    public void preInit(FMLPreInitializationEvent event)
    {
    }

    @Override
    public void init(FMLInitializationEvent event)
    {
    }
    
    @Override
    public void postInit(FMLPostInitializationEvent event)
    {
    }
    
    @Override
    public void serverStarting(FMLServerStartingEvent event)
    {
        event.registerServerCommand(new CommandStructureCapture());
    }
    
    @Override
    public EntityPlayer getPlayerEntityFromContext(MessageContext ctx)
    {
        return ctx.getServerHandler().player;
    }
}

The code should be self-explanatory. In this case we're overriding the methods with the the server-specific stuff which is typically not much.

Annotate And Instantiate The Proxy In Your Mod's Main Class


In your mod's main class (i.e. the one annotated with @Mod) you need to put in an annotation to indicate where the two proxies are. For example, put something like this in the fields list area of your main mod class:

@SidedProxy(
      clientSide="com.blogspot.jabelarminecraft.blocksmith.proxy.ClientProxy", 
      serverSide="com.blogspot.jabelarminecraft.blocksmith.proxy.ServerProxy"
    )
public static IProxy proxy;

Warning: You must change the strings to be the exact package location and name of each of your classes.

Call Your Proxy Methods From Mod's Main Class


So the proxy instance will get loaded based on the side, and so if you call proxy from your mod's main class it will call the appropriate one.  So in my main class I have the following:

    /**
     * Pre-Initialization FML Life Cycle event handling method which is automatically
     * called by Forge. It must be annotated as an event handler.
     *
     * @param event the event
     */
    @EventHandler
    // preInit "Run before anything else. Read your config, create blocks, items, etc, and register them with the GameRegistry."
    public void preInit(FMLPreInitializationEvent event)
    {
        // DEBUG
        System.out.println("preInit() " + event.getModMetadata().name);

        Utilities.setModInfo(event);
        ModConfig.initConfig(event); // load configuration before doing anything else that may be controlled by it.
        // register stuff
        ModTileEntities.registerTileEntities();
        ModFluids.registerFluids();
        ModNetworking.registerSimpleNetworking();

        proxy.preInit(event);
    }

    /**
     * Initialization FML Life Cycle event handling method which is automatically
     * called by Forge. It must be annotated as an event handler.
     *
     * @param event the event
     */
    @EventHandler
    // Do your mod setup. Build whatever data structures you care about.
    // Register network handlers
    public void init(FMLInitializationEvent event)
    {
        // DEBUG
        System.out.println("init()");
        
        // DEBUG
        System.out.println("Registering gui handler");
        NetworkRegistry.INSTANCE.registerGuiHandler(instance, new GuiHandler());

        ModAdvancements.registerAdvancements();
        ModWorldGenerators.registerWorldGenerators();

        proxy.init(event);
    }

    /**
     * Post-Initialization FML Life Cycle event handling method which is automatically
     * called by Forge. It must be annotated as an event handler.
     *
     * @param event the event
     */
    @EventHandler
    // postInit "Handle interaction with other mods, complete your setup based on this."
    public void postInit(FMLPostInitializationEvent event)
    {
        // DEBUG
        System.out.println("postInit()");

        proxy.postInit(event);
    }


    /**
     * Fml life cycle.
     *
     * @param event the event
     */
    @EventHandler
    public void serverStarting(FMLServerStartingEvent event)
    {
        // DEBUG
        System.out.println("Server starting");

        proxy.serverStarting(event);
    }


Conclusion


I know the concept can be a bit confusing but hopefully this gives you starting point for exploring how to use a proxy in your mod.

As always, please feel free to comment to ask for clarification or to give corrections. Happy modding!

3 comments:

  1. Thank you, very helpful getting me started.

    ReplyDelete
  2. Even i do not have any knowledge about proxy and how we can use this in coding section. But as you are explain in a way from starts to end will have little bit idea about it. I am new to project and i want to work on project which is based on Microleaves proxy dedicated ip project and hope will get some success to setup and works fine with it. Great job and waiting for articles which is related to education and all. Keep sharing.

    ReplyDelete
  3. Great explanation, really answered a lot of my questions. Thank you very much!

    ReplyDelete