Terrain Control

Terrain Control

235k Downloads

MCPC+ Forge MC 1.6.4 - PopulateChunkEvent Pre/Post is not called for Terrain Control chunks.

wnewbery opened this issue ยท 13 comments

commented

These events seem to not be invoked for a world generated by Terrain Control. If Terrain Control is just being used for one dimension, the event still works on the other dimensions (e.g. nether and end).

I can't seem to attach the java source files or compiled files, so the file code is pasted below...

Tested with http://mctcp.com:8900/job/TerrainControl/59/com.khorn.terraincontrol$terraincontrol-release/

I did look in the Terrain Control source and it does look like it should call the event using com.khorn.terraincontrol.forge.EventManager, but I haven't managed to successfully locally build TC or had time to try and attach Eclipse to the existing build to work out what is going on there.

package wn.terraintesting;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.event.ForgeSubscribe;
import net.minecraftforge.event.terraingen.PopulateChunkEvent;
import cpw.mods.fml.common.Mod;
import cpw.mods.fml.common.Mod.EventHandler;
import cpw.mods.fml.common.Mod.Instance;
import cpw.mods.fml.common.SidedProxy;
import cpw.mods.fml.common.event.FMLInitializationEvent;
import cpw.mods.fml.common.event.FMLPostInitializationEvent;
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
import cpw.mods.fml.common.network.NetworkMod;

@Mod(modid="WillTestMod", name="WillTestMod", version="0.0.0")
@NetworkMod(clientSideRequired=false)
public class WillTestMod {
    @Instance(value = "WillTestMod")
    public static WillTestMod instance;

    public static class PopulateTest {

        public static PopulateTest instance = new PopulateTest();

        @ForgeSubscribe
        public void populate(PopulateChunkEvent.Pre event) {
            message(event, "PopulateChunkEvent.Pre");
        }

        @ForgeSubscribe
        public void populate(PopulateChunkEvent.Post event) {
            message(event, "PopulateChunkEvent.Post");
        }

        @ForgeSubscribe
        public void populate(PopulateChunkEvent.Populate event) {
            message(event, "PopulateChunkEvent.Populate");
        }

        public void message(PopulateChunkEvent event, String eventStr) {
            String str = eventStr + " called for ";
            str += event.world.getWorldInfo().getWorldName() + " ";
            str += event.chunkX + "," + event.chunkZ;

            System.out.println(str);
        }
    }

    //@SidedProxy(clientSide="tutorial.generic.client.ClientProxy", serverSide="tutorial.generic.CommonProxy")
    //public static CommonProxy proxy;

    @EventHandler
    public void preInit(FMLPreInitializationEvent event) {
        MinecraftForge.EVENT_BUS.register(PopulateTest.instance);
    }

    @EventHandler
    public void load(FMLInitializationEvent event) {

    }

    @EventHandler
    public void postInit(FMLPostInitializationEvent event) {

    }
}
commented

Closing this, as it's an old issue that isn't going to be fixed, unfortunately.

commented

Interesting issue. The event should be called in the onPopulateStart method. This means that the onPopulateStart method isn't being called.

commented

Debugged it a bit further. The forge.EventHandler appears to only be added to TerrainControl static event handler list by forge.TCPlugin. However when TerrainControl is installed to plugins as per https://github.com/Wickth/TerrainControl/wiki/Installation-instructions#wiki-multiplayer forge is not loading forge.TCPlugin (and bukkit.TCPlugin even when it detects MCPC+ appears not to attempt to do anything about this).

Of course if TerrainControl is installed only to /mods/ while Forge loads it, the plugin part is not so for example when Bukkit comes across "generator: TerrainControl" it doesn't know what that generator is. Attempting to put TerrainControl in both mods/ and plugins/ results in errors, for example when both the Forge mod and Bukkit plugin call TerrainControl.setEngine...

So really not sure on how this was intended to work?

commented

It was intended that people can use either the Forge or the Bukkit version on MCPC+. The Forge version doesn't support multiworld, the Bukkit version doesn't support the Forge events. I hoped that MCPC+ would fire the population start/end events for Bukkit world generators: it is already firing CraftBukkit's onChunPopulate event.

commented

So is MCPC+ with forge events as a mod supported? In my testing it ignored the level-type from server.properties.

Will look when get home, but if you have detected mcpc+ can the event handler not be registered there?

Also i did have stuff like buildcraft oil springs working with tc in 1.4.6. Anything change in the last year or so?

commented

Added this to my TerrainControl build, appears to work. Have not checked everything, but my little mod above worked, and found some Forestry bee hives. Only real concern I have was providing a null IChunkProvider (would appear to be fine, but I guess if a mod actually needs it, it wont work) and if there is a tidier way to get the net.minecraft.world.World (which would appear to be the same as net.minecraft.server.v1_6_R3.World in practice?).

TCPlugin.java

        if (Bukkit.getVersion().contains("MCPC-Plus"))
        {
            // We're on MCPC+, so enable the extra block ids.
            TerrainControl.supportedBlockIds = 4095;
            mcpc = true;
            this.log(Level.INFO, "MCPC+ detected, enabling extended block id support.");

            this.log(Level.INFO, "Enabling forge functionality.");
            try
            {
                this.log(Level.INFO, "Registering com.khorn.terraincontrol.forge.BukkitEventManager");
                TerrainControl.registerEventHandler(
                    (EventHandler)Class.forName("com.khorn.terraincontrol.forge.BukkitEventManager").newInstance(),
                    EventPriority.CANCELABLE);

                //Any of these important? From forge.TCPlugin
                // // Register player tracker, for sending configs.
                // GameRegistry.registerPlayerTracker(new PlayerTracker(this));
                // 
                // // Register sapling tracker, for custom tree growth.
                // SaplingListener saplingListener = new SaplingListener();
                // MinecraftForge.TERRAIN_GEN_BUS.register(saplingListener);
                // MinecraftForge.EVENT_BUS.register(saplingListener);
            } catch (Exception e)
            {
                TerrainControl.log(Level.SEVERE, "Failed to enable forge functionality: {0}", e);
            }
        }

platforms/forge/src/main/java/com/khorn/terraincontrol/forge/BukkitEventManager.java (new file)

package com.khorn.terraincontrol.forge;

import com.khorn.terraincontrol.DefaultMaterial;
import com.khorn.terraincontrol.LocalWorld;
import com.khorn.terraincontrol.events.EventHandler;
import com.khorn.terraincontrol.generator.resourcegens.*;
import cpw.mods.fml.common.registry.GameRegistry;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.event.terraingen.*;
import net.minecraftforge.event.terraingen.DecorateBiomeEvent.Decorate;
import net.minecraftforge.event.terraingen.OreGenEvent.GenerateMinable;
import net.minecraftforge.event.terraingen.PopulateChunkEvent.Populate;
import net.minecraft.world.World;

import java.lang.RuntimeException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;

/**
 * Translates TerrainControl events into MinecraftForge terrain events
 * for when TerrainControl is running on MCPC+ as a Bukkit plugin.
 */
public class BukkitEventManager extends EventHandler
{
    private boolean startedOreGen;
    private boolean startedDecoration;
    private Method bukkitWorldGetWorldMethod;

    public BukkitEventManager()
    {
        //Need to be able to call BukkitWorld.getWorld, but
        //com.khorn.terraincontrol.bukkit is not available at compile time
        try
        {
            Class bukkitWorld = Class.forName("com.khorn.terraincontrol.bukkit.BukkitWorld");
            bukkitWorldGetWorldMethod = bukkitWorld.getMethod("getWorld");
        } catch (Exception e)
        {
            throw new RuntimeException(e);
        }
    }

    public World getMcWorld(LocalWorld localWorld)
    {
        try
        {
            return (World)bukkitWorldGetWorldMethod.invoke(localWorld);
        } catch (Exception e)
        {
            throw new RuntimeException(e);
        }
    }

    @Override
    public boolean onResourceProcess(Resource resource, LocalWorld localWorld, Random random, boolean villageInChunk, int chunkX, int chunkZ, boolean isCancelled)
    {
        final World world = getMcWorld(localWorld);

        //Assuming the TerrainGen. calls are not important in the Bukkit context
        // Convert to Forge event and fire
        if (resource instanceof DungeonGen ||
                resource instanceof SmallLakeGen ||
                resource instanceof UndergroundLakeGen ||
                resource instanceof LiquidGen ||
                resource instanceof CustomObjectGen)
        {

        } else if (resource instanceof OreGen)
        {
            if (!startedOreGen)
            {
                startedOreGen = true;
                MinecraftForge.ORE_GEN_BUS.post(new OreGenEvent.Pre(world, random, chunkX, chunkZ));
            }
        } else
        {
            if (!startedDecoration)
            {
                startedDecoration = true;
                MinecraftForge.EVENT_BUS.post(new DecorateBiomeEvent.Pre(world, random, chunkX, chunkZ));
            }
        }
        return true;
    }

    @Override
    public void onPopulateStart(LocalWorld localWorld, Random random, boolean villageInChunk, int chunkX, int chunkZ)
    {
        final World world = getMcWorld(localWorld);

        // Reset states
        startedOreGen = false;
        startedDecoration = false;

        // Fire event
        final PopulateChunkEvent forgeEvent = new PopulateChunkEvent.Pre(null, world, random, chunkX, chunkZ, villageInChunk);
        MinecraftForge.EVENT_BUS.post(forgeEvent);
    }

    @Override
    public void onPopulateEnd(LocalWorld localWorld, Random random, boolean villageInChunk, int chunkX, int chunkZ)
    {
        final World world = getMcWorld(localWorld);

        // Fire all events

        // Decoration close
        if (startedDecoration)
        {
            MinecraftForge.EVENT_BUS.post(new DecorateBiomeEvent.Post(world, random, chunkX, chunkZ));
        }

        // Ore generation close
        if (startedOreGen)
        {
            MinecraftForge.EVENT_BUS.post(new OreGenEvent.Post(world, random, chunkX, chunkZ));
        }

        // Population close
        final PopulateChunkEvent forgeEvent = new PopulateChunkEvent.Post(null, world, random, chunkX, chunkZ, villageInChunk);
        MinecraftForge.EVENT_BUS.post(forgeEvent);
    }
}
commented

Thanks. Once the Forge version compiles again I will implement something like that, based on your code.

commented

Look forward to it :) Been stable since I posted on a server with Buildcraft, Forestry, Railcraft (set to spawn some of their hard to replicate things like oil).

Do you have any idea how I might have got it working back on MC 1.4? I really cant recall now but sure it was in plugins and definitely no code change on my part.

Also do you know what the relation is between net.minecraft.server.v1_6_R3.World and net.minecraft.world.World and similar?

commented

In Minecraft 1.4, you probably used installed TC as a Forge plugin. Another possiblity is that MCPC+ fired the event when a Bukkit populator got called.

net.minecraft.server.v1_6_R3.World is the same as net.minecraft.world.World, just different deobfuscation. In the Minecraft.jar, the class is called something like aeb.class.

commented

Actually thinking about it on that server was before mcpc+, when mods were ported, so might have had somthing todo with that.

Is it possible to put a getWorld in LocalWorld then? I tried but couldnt get it to work because of the different package names, but means i wouldnt need so much reflection stuff.

commented

@rutgerkok MinecraftPortCentral@eee0a63

I remapped the plugin to support Cauldron's dev environment and it now works fine as a bukkit plugin. If you want, you can remove all Cauldron(MCPC+) code from your master repo and just point users to the Cauldron(MCPC+) version. Soon we will provide a userenv for plugin devs to work in to make Cauldron(MCPC+) compatible plugins. Since I currently built this manually in my own environment, is it OK if I provide a link on our website for MCPC+ version of TerrainControl? or would you like me to provide you with the patched jar. Let me know, thanks.

You can reach me on webchat.esper.net in channel #cauldron

commented

@rutgerkok nevermind, I see you backported it to 1.7.2 here https://github.com/Wickth/TerrainControl/releases/tag/v2.7.0-SNAPSHOT-for-mc-172
and it works fine. Seems to be an issue on my end with inheritance not remapping correctly for anything > 1.7.2. Thanks for the support =)

commented

(I'm fine with people redistributing TC.)

I saw that you got TC 2.6.1 for Bukkit 1.7.9 working on MC 1.7.2 by adding some special remappings. I was wondering why those remappings in Cauldron were suddenly needed, when TC worked fine before. I made a quick backport to 1.7.2 and it worked fine again. (Except a little issue with Cauldron ignoring the load: startup setting in the plugin.yml, which causes Terrain Control to fail in default worlds. I can make a bug report for this on the Cauldron repo, if you want.)