Package net.minecraft.src

Source Code of net.minecraft.src.mod_LibShapeDraw

package net.minecraft.src;

import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;

import libshapedraw.ApiInfo;
import libshapedraw.MinecraftAccess;
import libshapedraw.internal.LSDController;
import libshapedraw.internal.LSDUtil;
import libshapedraw.internal.LSDUtil.NullList;
import libshapedraw.internal.LSDUtil.NullMap;
import libshapedraw.primitive.ReadonlyVector3;
import libshapedraw.primitive.Vector3;
import net.minecraft.client.Minecraft;

/**
* Internal class. Mods using the LibShapeDraw API can safely ignore this.
* Rather, instantiate {@link libshapedraw.LibShapeDraw}.
* <p>
* This is a ModLoader mod (also compatible with Forge/FML) that links itself
* to the internal API Controller, providing it data and events from Minecraft.
* This class does the bare minimum of processing before passing these off to
* the controller. I.e., this class is a thin wrapper for Minecraft used by
* LibShapeDraw.
* <p>
* As a wrapper, all direct interaction with Minecraft objects passes through
* this class, making the LibShapeDraw API itself clean and free of obfuscated
* code. (There is a single exception: LSDModDirectory.getMinecraftDir.)
*/
public class mod_LibShapeDraw extends BaseMod implements MinecraftAccess {
    /**
     * Install our render hook by inserting a proxy for Minecraft.mcProfiler.
     * <p>
     * The long-awaited official Minecraft API will hopefully provide standard
     * entry points for client mods (like this one!) that require rendering
     * hooks. Until then, we have to do some hackish stuff to add our hook.
     * <p>
     * Option 1 is the naive, quick-and-dirty method: patch or proxy the
     * EntityRender class. However this class is already being modified by many
     * mods, including OptiFine, ModLoader, and Forge. Introducing yet another
     * mutually incompatible mod is a poor choice. Compatibility is a key goal
     * of LibShapeDraw.
     * <p>
     * Option 2 is to use Forge's hooks. This is also not an acceptable option:
     * not everyone uses Forge. LibShapeDraw supports Forge but does not
     * require it.
     * <p>
     * Option 3 is to register a fake entity and add our render hook to it.
     * This is a valid, highly-compatible approach, used successfully by
     * several mods (including LibShapeDraw v1.0). However this has a key
     * drawback: entities are rendered before water, clouds, and other
     * elements. This can result in ugly graphical glitches when rendering
     * semi-transparent shapes near water.
     * <p>
     * Option 4, which is what this class implements, is an even more egregious
     * hack than option 1 or 3. The Profiler class is of course intended for
     * debugging, gathering metrics on how long it takes to render each
     * element.
     * <p>
     * As it happens, the point at which we want to insert our hook occurs just
     * before the player's hand is rendered. The profiler's context gets
     * switched at this point, giving us our hook! Furthermore, we're able to
     * proxy the Profiler class instead of modifying it directly, fulfilling
     * another one of LibShapeDraw's goals: no bytecode modification of vanilla
     * classes.
     * <p>
     * This doesn't guarantee compatibility with every mod: If another mod is
     * trying to proxy the Profiler class as well for some reason, Bad Things
     * might happen. If in EntityRender.renderWorld the call to
     * Profiler.endStartSection("hand") is removed by another mod patching that
     * class, Bad Things will definitely happen. We can and do check for these
     * cases, however.
     * <p>
     * Anyway, this method is a roundabout hack, but it works. It may very well
     * break at some point when the rendering engine is overhauled in Minecraft
     * 1.5, but this is also when the official Minecraft API is scheduled to
     * finally be released.
     * <p>
     * The sooner the better.
     */
    // obf: Profiler
    public class Proxy extends Profiler {
        /**
         * Keep a reference to the old Profiler if we detect that another mod
         * is also proxying it. Play nice with others!
         */
        // obf: Profiler
        protected Profiler orig;

        @Override
        // obf: Profiler.startSection
        public void startSection(String sectionName) {
            // obf: Profiler.startSection
            super.startSection(sectionName);
            if (orig != null) {
                // obf: Profiler.startSection
                orig.startSection(sectionName);
            }
        }

        @Override
        // obf: Profiler.endSection
        public void endSection() {
            // obf: Profiler.endSection
            super.endSection();
            if (orig != null) {
                // obf: Profiler.endSection
                orig.endSection();
            }
        }

        @Override
        // obf: Profiler.endStartSection
        public void endStartSection(String sectionName) {
            if (sectionName.equals("hand")) {
                // obf: Profiler.endStartSection
                super.endStartSection("LibShapeDraw"); // we'll take the blame :-)
                render();
            }
            // obf: Profiler.endStartSection
            super.endStartSection(sectionName);
            if (orig != null) {
                // obf: Profiler.endStartSection
                orig.endStartSection(sectionName);
            }
        }
    }

    private Minecraft minecraft;
    // obf: Timer
    private Timer timer;
    private Proxy proxy;
    private LSDController controller;
    private boolean renderHeartbeat;
    private boolean renderHeartbroken;
    private Object curWorld;
    // obf: EntityClientPlayerMP
    private EntityClientPlayerMP curPlayer;
    private Integer curDimension;

    public mod_LibShapeDraw() {
        controller = LSDController.getInstance();
        controller.initialize(this);
    }

    @Override
    public String getName() {
        return ApiInfo.getName();
    }

    @Override
    public String getVersion() {
        return ApiInfo.getVersion();
    }

    @Override
    public String getPriorities() {
        // Request that this mod gets loaded last. Ideally, we want to be the
        // last mod to potentially modify Minecraft.mcProfiler.
        return "after:*";
    }

    @Override
    public void load() {
        // Do nothing; wait until modsLoaded.
    }

    @Override
    public void modsLoaded() {
        // obf: Minecraft.getMinecraft
        minecraft = Minecraft.getMinecraft();
        // Get a reference to Minecraft's timer so we can get the partial
        // tick time for rendering (it's not passed to the profiler directly).
        //
        // There's only one Timer field declared by Minecraft so it's safe to
        // look it up by type.
        // obf: Timer
        timer = (Timer) LSDUtil.getFieldValue(LSDUtil.getFieldByType(Minecraft.class, Timer.class, 0), minecraft);

        installRenderHook();
        ModLoader.setInGameHook(this, true, true); // game ticks only, not every render frame.
        LSDController.getLog().info(getClass().getName() + " loaded");
    }

    /**
     * Use reflection to install the profiler proxy class, overwriting
     * Minecraft.mcProfiler.
     */
    private void installRenderHook() {
        final Class<? super Proxy> vanillaClass = Proxy.class.getSuperclass();
        proxy = new Proxy();
        // There's only one Profiler field declared by Minecraft so it's safe
        // to look it up by type.
        final Field fp = LSDUtil.getFieldByType(Minecraft.class, vanillaClass, 0);
        // obf: Profiler
        proxy.orig = (Profiler) LSDUtil.getFieldValue(fp, minecraft);
        final String origClass = proxy.orig.getClass().getName();
        LSDController.getLog().info(
                "installing render hook using profiler proxy, replacing " + origClass);
        LSDUtil.setFinalField(fp, minecraft, proxy);

        // Copy all vanilla-defined field values from the original profiler to
        // the new proxy.
        for (Field f : vanillaClass.getDeclaredFields()) {
            f.setAccessible(true);
            Object origValue = LSDUtil.getFieldValue(f, proxy.orig);
            LSDUtil.setFinalField(f, proxy, origValue);
            LSDController.getLog().fine("copied profiler field " + f + " = " + String.valueOf(origValue));
            // "Neuter" the original profiler by changing its vanilla-defined
            // reference types to new dummy instances.
            if (f.getType() == List.class) {
                LSDUtil.setFinalField(f, proxy.orig, new NullList());
            } else if (f.getType() == Map.class) {
                LSDUtil.setFinalField(f, proxy.orig, new NullMap());
            }
        }

        if (proxy.orig.getClass() == vanillaClass) {
            // No need to keep a reference to the original profiler.
            proxy.orig = null;
        } else {
            // We overwrote some other mod's hook, so keep the reference to the
            // other mod's proxy. This will ensure that the other mod still
            // receives its expected events.
            //
            // Log a (hopefully benign) warning message if we don't recognize
            // the other proxy class.
            if (!origClass.equals("com.mumfrey.liteloader.core.HookProfiler")) {
                LSDController.getLog().warning(
                        "possible mod incompatibility detected: replaced unknown profiler proxy class " + origClass);
            }
        }
    }

    @Override
    // obf: NetClientHandler
    public void clientConnect(NetClientHandler netClientHandler) {
        LSDController.getLog().info(getClass().getName() + " new server connection");
        curWorld = null;
        curPlayer = null;
        curDimension = null;
    }

    @Override
    public boolean onTickInGame(float partialTick, Minecraft minecraft) {
        ReadonlyVector3 playerCoords = getPlayerCoords();

        // obf: Minecraft.theWorld, Minecraft.thePlayer
        if (curWorld != minecraft.theWorld || curPlayer != minecraft.thePlayer) {
            // obf: Minecraft.theWorld
            curWorld = minecraft.theWorld;
            // obf: Minecraft.thePlayer
            curPlayer = minecraft.thePlayer;

            // Dispatch respawn event to Controller.
            // obf: Entity.dimension
            int newDimension = curPlayer.dimension;
            controller.respawn(playerCoords,
                    curDimension == null,
                    curDimension == null || curDimension != newDimension);
            curDimension = newDimension;
        }

        // Dispatch game tick event to Controller.
        controller.gameTick(playerCoords);

        // Make sure our render hook is still working.
        // obf: Minecraft.skipRenderWorld
        if (!renderHeartbeat && !renderHeartbroken && !minecraft.skipRenderWorld) {
            // Despite our best efforts when installing the profiler proxy,
            // some other mod probably overwrote our hook without providing a
            // compatibility layer like we do. :-(
            //
            // Attempting to reinstall our hook would be futile: chances are it
            // would simply be overwritten again by the next tick. Rather than
            // participating in an inter-mod slap fight, back down and log an
            // error message. No need to crash Minecraft.
            Object newProxy = LSDUtil.getFieldValue(
                    LSDUtil.getFieldByType(Minecraft.class, Proxy.class.getSuperclass(), 0), minecraft);
            String message = "mod incompatibility detected: render hook not working! Minecraft.mcProfiler is " +
                    (newProxy == null ? "null" : newProxy.getClass().getName());
            LSDController.getLog().warning(message);
            sendChatMessage("\u00a7c[" + getName() + "] " + message);
            renderHeartbroken = true; // don't spam log
        }
        renderHeartbeat = false;

        return true;
    }

    /** Dispatch render event to Controller. */
    protected void render() {
        // obf: Minecraft.gameSettings, GameSettings.hideGUI, Minecraft.currentScreen
        controller.render(getPlayerCoords(), minecraft.gameSettings.hideGUI && minecraft.currentScreen == null);
        renderHeartbeat = true;
    }

    /**
     * Get the player's current coordinates, adjusted for movement that occurs
     * between game ticks.
     */
    private ReadonlyVector3 getPlayerCoords() {
        if (curPlayer == null) {
            return Vector3.ZEROS;
        }
        float partialTick = getPartialTick();
        return new Vector3(
                // obf: Entity.prevPosX, Entity.posX
                curPlayer.prevPosX + partialTick*(curPlayer.posX - curPlayer.prevPosX),
                // obf: Entity.prevPosY, Entity.posY
                curPlayer.prevPosY + partialTick*(curPlayer.posY - curPlayer.prevPosY),
                // obf: Entity.prevPosZ, Entity.posZ
                curPlayer.prevPosZ + partialTick*(curPlayer.posZ - curPlayer.prevPosZ));
    }

    // ====
    // MinecraftAccess implementation
    // ====

    @Override
    public MinecraftAccess startDrawing(int mode) {
        // obf: Tessellator, Tessellator.instance, Tessellator.startDrawing
        Tessellator.instance.startDrawing(mode);
        return this;
    }

    @Override
    public MinecraftAccess addVertex(double x, double y, double z) {
        // obf: Tessellator, Tessellator.instance, Tessellator.addVertex
        Tessellator.instance.addVertex(x, y, z);
        return this;
    }

    @Override
    public MinecraftAccess addVertex(ReadonlyVector3 coords) {
        // obf: Tessellator, Tessellator.instance, Tessellator.addVertex
        Tessellator.instance.addVertex(coords.getX(), coords.getY(), coords.getZ());
        return this;
    }

    @Override
    public MinecraftAccess finishDrawing() {
        // obf: Tessellator, Tessellator.instance, Tessellator.draw
        Tessellator.instance.draw();
        return this;
    }

    @Override
    public MinecraftAccess enableStandardItemLighting() {
        // obf: RenderHelper, RenderHelper.enableStandardItemLighting
        RenderHelper.enableStandardItemLighting();
        return this;
    }

    @Override
    public MinecraftAccess sendChatMessage(String message) {
        boolean visible = chatWindowExists();
        LSDController.getLog().info("sendChatMessage visible=" + visible + " message=" + message);
        if (visible) {
            // obf: Minecraft.ingameGUI, GuiIngame.getChatGUI, GuiNewChat.printChatMessage
            minecraft.ingameGUI.getChatGUI().printChatMessage(message);
        }
        return this;
    }

    @Override
    public boolean chatWindowExists() {
        // obf: Minecraft.ingameGUI, GuiIngame.getChatGUI
        return minecraft != null && minecraft.ingameGUI != null && minecraft.ingameGUI.getChatGUI() != null;
    }

    @Override
    public float getPartialTick() {
        // obf: Timer.renderPartialTicks
        return timer == null ? 0.0F : timer.renderPartialTicks;
    }


    @Override
    public MinecraftAccess profilerStartSection(String sectionName) {
        if (proxy != null) {
            // obf: Profiler.startSection
            proxy.startSection(sectionName);
        }
        return this;
    }

    @Override
    public MinecraftAccess profilerEndSection() {
        if (proxy != null) {
            // obf: Profiler.endSection
            proxy.endSection();
        }
        return this;
    }

    @Override
    public MinecraftAccess profilerEndStartSection(String sectionName) {
        if (proxy != null) {
            // obf: Profiler.endStartSection
            proxy.endStartSection(sectionName);
        }
        return this;
    }
}
TOP

Related Classes of net.minecraft.src.mod_LibShapeDraw

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.
>java.lang.reflect.Field
TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.