今日目標要把剩下的處理完,讓我們繼續下去吧,加油!
我們先處理[Day9]的部分。先將[Day6]的PigDoll移除,因為這個算是半成品;再來我們將PigDoll_v2這個類別移動到com.ithome.mymod.events下,並且重新命名為PigDoll:
進入到PigDoll這個檔案,內容可以參考[Day9],我們這邊需要把自行處理Prxoy的部分移除(就是所有的@SideOnly(Side.CLIENT)都拿掉):
移除後的PigDoll長這樣
package com.ithome.mymod.events;
import com.ithome.mymod.entities.EntityScalePig;
import com.ithome.mymod.entities.RenderScalePig;
import net.minecraft.client.Minecraft;
import net.minecraft.client.model.ModelPig;
import net.minecraft.entity.ai.EntityAIBase;
import net.minecraft.entity.passive.EntityPig;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraftforge.client.event.RenderLivingEvent;
import net.minecraftforge.event.entity.EntityJoinWorldEvent;
import net.minecraftforge.event.entity.living.LivingDropsEvent;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
public class PigDoll {
    private float scale = 1.0f;
    private AtomicInteger count = new AtomicInteger(0);
    private AtomicBoolean flag = new AtomicBoolean(false);
    private List<String> firstRenderPig = new ArrayList<String>();
    @SubscribeEvent
    public void preRender(RenderLivingEvent.Pre event) {
        if (event.entity.getCustomNameTag().startsWith("myPig")) {
            String tag = event.entity.getCustomNameTag();
            float parserScale = Float.parseFloat(tag.split("-")[1]);
            if (flag.compareAndSet(false, true)) {
                 Minecraft.getMinecraft().getRenderManager().entityRenderMap.put(EntityScalePig.class,
                         new RenderScalePig(Minecraft.getMinecraft().getRenderManager(), new ModelPig(), 0.5f * parserScale));
            }
            if(event.renderer instanceof RenderScalePig) {
                RenderScalePig render = (RenderScalePig) event.renderer;
                render.setScale(parserScale);
            }
            if(!firstRenderPig.contains(tag)) {
                EntityScalePig pig = (EntityScalePig) event.entity;
                pig.scale(parserScale);
                firstRenderPig.add(tag);
            }
        }
    }
    @SubscribeEvent
    public void pigDoll(EntityJoinWorldEvent event) {
        if (event.entity.worldObj.isRemote) return;
        if (event.world.playerEntities.isEmpty()) return;
        EntityPlayer player = (EntityPlayer) event.entity.worldObj.playerEntities.get(0);
        float distance = event.entity.getDistanceToEntity(player);
        if (distance < 7.0f) {
            if (EntityPig.class.isAssignableFrom(event.entity.getClass())) {
                if(!event.entity.getCustomNameTag().startsWith("myPig")) {
                    event.entity.setDead();
                }
                if(count.compareAndSet(5, 0)) {
                    scale = 1.0f;
                    firstRenderPig.clear();
                    return;
                }
                scale = scale * 0.8f;
                EntityScalePig pig = new EntityScalePig(event.entity.worldObj);
                pig.setPosition(event.entity.posX - 1.2f, event.entity.posY, event.entity.posZ);
                pig.setCustomNameTag(String.format("myPig-%f", scale));
                // 將豬的AI行為去除
                EntityAIBase ai = new EntityAIBase() {
                    @Override
                    public boolean shouldExecute() {
                        return true;
                    }
                };
                ai.setMutexBits(0xFFFFFF);
                pig.tasks.addTask(0, ai);
                count.getAndIncrement();
                event.entity.worldObj.spawnEntityInWorld(pig);
            }
        }
    }
}
然後把註冊動作放到ClientProxy的init()內:
MinecraftForge.EVENT_BUS.register(new PigDoll());
最後,註冊我們EntityScalePig的動作是Client與Server都需要的(因為他們都會用到這個實體),在Proxy的概念下,如果不需要去關心是屬於Client或Server,就放在主程式內。因此我們把註冊EntityScalePig實體的動作放到MyMod的preInit()內:
@EventHandler
public void preInit(FMLPreInitializationEvent event) {
    int availableId = 1;
    while (EntityList.getClassFromID(availableId) != null) {
        availableId++;
    }
    EntityRegistry.registerGlobalEntityID(EntityScalePig.class, EntityScalePig.name, availableId);
    proxy.preInit(event);
}
[Da11]是指令部分,所以我們再建立一個叫做com.ithome.mymod.commands的套件目錄,並且將BurningPigs移動到這個套件內。
由於指令需要註冊到FMLServerStartingEvent這個事件內,我們先回到IProxy介面定義方法,然後將指令註冊放到ServerProxy內(因為這個事件是Server端的)。注意:雖然ClientProxy不會用到這個事件,但還是需要定義!
IProxy
package com.ithome.mymod.proxy;
import net.minecraftforge.fml.common.event.FMLInitializationEvent;
import net.minecraftforge.fml.common.event.FMLPostInitializationEvent;
import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;
import net.minecraftforge.fml.common.event.FMLServerStartingEvent;
public interface IProxy {
    /**
     * 準備Minecraft的所有物體。舉凡讀取設定檔、建立方塊、物品,註冊物體到Minecraft等。
     *
     * @param event 事件
     */
    void preInit(FMLPreInitializationEvent event);
    /**
     * 模組設置。例如註冊事件處理、註冊資源、建立配方(合成、燒煉與釀造)。
     *
     * @param event 事件
     */
    void init(FMLInitializationEvent event);
    /**
     * 與其他模組互動。如果有任何其他需要處理的的功能,或是要建立與其他模組的相依關係。
     *
     * @param event 事件
     */
    void postInit(FMLPostInitializationEvent event);
    /**
     * 註冊伺服器指令的地方,這個事件只會被Server呼叫。
     *
     * @param event 事件
     */
    void serverStarting(FMLServerStartingEvent event);
}
ClientProxy
package com.ithome.mymod.proxy;
import com.ithome.mymod.commands.Colosseum;
import com.ithome.mymod.events.*;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.fml.common.FMLCommonHandler;
import net.minecraftforge.fml.common.event.FMLInitializationEvent;
import net.minecraftforge.fml.common.event.FMLPostInitializationEvent;
import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;
import net.minecraftforge.fml.common.event.FMLServerStartingEvent;
public class ClientProxy implements IProxy {
    @Override
    public void preInit(FMLPreInitializationEvent event) {
    }
    @Override
    public void init(FMLInitializationEvent event) {
        MinecraftForge.EVENT_BUS.register(new BlockBreakEvent());
        MinecraftForge.EVENT_BUS.register(new TNTExplosions());
        MinecraftForge.EVENT_BUS.register(new PigDroppingItems());
        MinecraftForge.EVENT_BUS.register(new ZombieKnights());
        MinecraftForge.EVENT_BUS.register(new SuperJump());
        MinecraftForge.EVENT_BUS.register(new PigDoll());
        FMLCommonHandler.instance().bus().register(new SuperJump());
    }
    @Override
    public void postInit(FMLPostInitializationEvent event) {
    }
    @Override
    public void serverStarting(FMLServerStartingEvent event) {
        // 這個事件不應該不呼叫
    }
}
ServerProxy
package com.ithome.mymod.proxy;
import com.ithome.mymod.commands.BurningPigs;
import net.minecraftforge.fml.common.event.FMLInitializationEvent;
import net.minecraftforge.fml.common.event.FMLPostInitializationEvent;
import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;
import net.minecraftforge.fml.common.event.FMLServerStartingEvent;
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 BurningPigs());
    }
}
最後,我們需要在MyMod檔案內把這個新的事件FMLServerStartingEvent加上,並標註@EventHandler:
MyMod
package com.ithome.mymod;
import com.ithome.mymod.configs.Constants;
import com.ithome.mymod.entities.EntityScalePig;
import com.ithome.mymod.proxy.IProxy;
import net.minecraft.entity.EntityList;
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;
import net.minecraftforge.fml.common.event.FMLServerStartingEvent;
import net.minecraftforge.fml.common.registry.EntityRegistry;
@Mod(modid = Constants.MOD_ID, name = Constants.MOD_NAME, version = Constants.VERSION)
public class MyMod {
    @SidedProxy(serverSide = Constants.SERVER_PROXY, clientSide = Constants.CLIENT_PROXY)
    public static IProxy proxy;
    @EventHandler
    public void preInit(FMLPreInitializationEvent event) {
        int availableId = 1;
        while (EntityList.getClassFromID(availableId) != null) {
            availableId++;
        }
        EntityRegistry.registerGlobalEntityID(EntityScalePig.class, EntityScalePig.name, availableId);
        proxy.preInit(event);
    }
    @EventHandler
    public void init(FMLInitializationEvent event) {
        proxy.init(event);
    }
    @EventHandler
    public void postInit(FMLPostInitializationEvent event) {
        proxy.postInit(event);
    }
    @EventHandler
    public void serverStarting(FMLServerStartingEvent event)
    {
        proxy.serverStarting(event);
    }
}
[Day12]到[Day13]一樣是使用指令,同前一個步驟,所以這裡只將有變更的地方放上來:
ClientProxy的init()方法:
MinecraftForge.EVENT_BUS.register(new Colosseum());
ServerProxy的serverStarting()方法:
event.registerServerCommand(new ColosseumFillCommand());
[Day14]到[Day17]是新的物品與方塊,這個是屬於註冊物體的工作,會放在preInit()方法內。因為註冊物品與Client或Server端無關,我們一樣直接放到MyMod內就好:
MyMod
// 自定義的方塊
public static Block enderBlock;
// 自定義的物品
public static Item pillItem;
@EventHandler
public void preInit(FMLPreInitializationEvent event) {
    int availableId = 1;
    while (EntityList.getClassFromID(availableId) != null) {
        availableId++;
    }
    EntityRegistry.registerGlobalEntityID(EntityScalePig.class, EntityScalePig.name, availableId);
    enderBlock = new EnderBlock();
    GameRegistry.registerBlock(enderBlock, "enderBlock");
    pillItem = new PillItem();
    GameRegistry.registerItem(pillItem, "pillItem");
    proxy.preInit(event);
}
@EventHandler
public void init(FMLInitializationEvent event) {
    // 從方塊轉換成物品的方法
    Item item = Item.getItemFromBlock(enderBlock);
    // 定義方塊資源目錄
    // 第一個參數是"{模組ID}:方塊註冊名稱"
    // 第二個參數請使用"inventory",表示從資源庫內尋找
    ModelResourceLocation location = new ModelResourceLocation(
            "myfancymods:enderBlock",
            "inventory"
    );
    // 註冊這個方塊物品的資源到Forge內
    Minecraft.getMinecraft().getRenderItem().getItemModelMesher()
            .register(item, 0, location);
    // 註冊藥丸物品的資源到Forge內
    Minecraft.getMinecraft().getRenderItem().getItemModelMesher()
            .register(pillItem, 0, new ModelResourceLocation(
                    "myfancymods:pillItem",
                    "inventory"
            ));
    proxy.init(event);
}
最後在[Day18]到[Day19],是屬於配方註冊。直接就想到將這部分的邏輯放到MyMod底下:
MyMod
@EventHandler
public void init(FMLInitializationEvent event) {
    // 從方塊轉換成物品的方法
    Item item = Item.getItemFromBlock(enderBlock);
    // 定義方塊資源目錄
    // 第一個參數是"{模組ID}:方塊註冊名稱"
    // 第二個參數請使用"inventory",表示從資源庫內尋找
    ModelResourceLocation location = new ModelResourceLocation(
            "myfancymods:enderBlock",
            "inventory"
    );
    // 註冊這個方塊物品的資源到Forge內
    Minecraft.getMinecraft().getRenderItem().getItemModelMesher()
            .register(item, 0, location);
    // 註冊藥丸物品的資源到Forge內
    Minecraft.getMinecraft().getRenderItem().getItemModelMesher()
            .register(pillItem, 0, new ModelResourceLocation(
                    "myfancymods:pillItem",
                    "inventory"
            ));
    GameRegistry.addShapedRecipe(
            new ItemStack(enderBlock),
            "X X",
            " X ",
            "X X",
            'X',
            Blocks.dirt);
    GameRegistry.addShapelessRecipe(
            new ItemStack(pillItem, 9),
            enderBlock
    );
    GameRegistry.addSmelting(
            Items.sugar,
            new ItemStack(pillItem, 1),
            1.0f
    );
    pillItem.setPotionEffect(PotionHelper.blazePowderEffect);
    proxy.init(event);
}
到這裡我們的Main檔案就可以移除了。接下來更新資源檔的命名,把resources/assets/myfancymods改名為resources/assets/mymod就可以了。另外,在資源檔的目錄resources下有一個mcmod.info的檔案,我們順便將這個檔案做更新,可以讓我們的模組資訊更完整:
更改名稱後的resource目錄
mcmod.info
[
{
  "modid": "mymod",
  "name": "My Mod",
  "description": "A mod for itHome",
  "version": "${version}",
  "mcversion": "${mcversion}",
  "url": "",
  "authorList": ["Sam"],
  "credits": "",
  "logoFile": "",
  "screenshots": [],
  "dependencies": []
}
]
檔案內容應該很好理解,這裡的"version"與"mcversion"是Forge內部保留字,會在執行時自動替換,我們不需要更改這部分內容。


我們的模組現在資訊更完整,並且可以"Disable"了。
如果到目前為止的程式碼你不是很了解的話,別擔心,到最後面會有完整的程式碼可以參考的。