public class ApiInfo {
    public static String getName() {
        return getInstance().name;
    }
    public static String getVersion() {
        return getInstance().version;
    }
    public static boolean isVersionAtLeast(String minVersion) {
        if (minVersion == null) {
            throw new IllegalArgumentException("minVersion cannot be null");
        }
        return minVersion.compareTo(getInstance().version) <= 0;
    }
    @Deprecated public static String getUrl() {
        return getInstance().urlMain.toString();
    }
    public static URL getUrlMain() {
        return getInstance().urlMain;
    }
    public static URL getUrlShort() {
        return getInstance().urlShort;
    }
    public static URL getUrlSource() {
        return getInstance().urlSource;
    }
    public static URL getUrlUpdate() {
        return getInstance().urlUpdate;
    }
    public static String getAuthors() {
        return getInstance().authors;
    }
    public static File getModDirectory() {
        return LSDModDirectory.DIRECTORY;
    }

    private final String name;
    private final String version;
    private final URL urlMain;
    private final URL urlShort;
    private final URL urlSource;
    private final URL urlUpdate;
    private final String authors;

    private static ApiInfo instance;

    private ApiInfo() {
        Properties props = new Properties();
        InputStream in = getClass().getResourceAsStream("api.properties");
        try {
            props.load(in);
            in.close();
        } catch (IOException e) {
            throw new LSDInternalException("unable to load resource", e);
        }
        name = notNull(props.getProperty("name"));
        version = notNull(props.getProperty("version"));
        urlMain = validUrl(props.getProperty("url-main"));
        urlShort = validUrl(props.getProperty("url-short"));
        urlSource = validUrl(props.getProperty("url-source"));
        urlUpdate = validUrl(props.getProperty("url-update"));
        authors = notNull(props.getProperty("authors"));
    }

    private static ApiInfo getInstance() {
        if (instance == null) {
            instance = new ApiInfo();
        }
        return instance;
    }

    private String notNull(String value) {
        if (value == null) {
            throw new IllegalArgumentException("value cannot be null");
        }
        return value;
    }

    private URL validUrl(String value) {
        try {
            return new URL(notNull(value));
        } catch (MalformedURLException e) {
            throw new IllegalArgumentException("invalid url: " + value, e);
        }
    }
} public class LibShapeDraw {
    private final Set<Shape> shapes;
    private final Set<Shape> shapesReadonly;
    private final Set<LSDEventListener> eventListeners;
    private final Set<LSDEventListener> eventListenersReadonly;
    private final String instanceId;
    private final String ownerId;
    private boolean visible = true;
    private boolean visibleWhenHidingGui = false;

    /**
     * Create a new API instance, automatically registering it with the
     * internal controller.
     */
    public LibShapeDraw() {
        this(Thread.currentThread().getStackTrace()[2].toString());
    }

    /**
     * The ownerId is optional: an arbitrary identifier for the mod that owns
     * this API instance.
     */
    public LibShapeDraw(String ownerId) {
        if (ownerId == null) {
            ownerId = Thread.currentThread().getStackTrace()[2].toString();
        }
        shapes = new LinkedHashSet<Shape>();
        shapesReadonly = Collections.unmodifiableSet(shapes);
        eventListeners = new LinkedHashSet<LSDEventListener>();
        eventListenersReadonly = Collections.unmodifiableSet(eventListeners);
        instanceId = LSDController.getInstance().registerApiInstance(this, ownerId);
        this.ownerId = ownerId;
    }

    /**
     * Permanently unregister the entire API instance. It will no longer render
     * its Shapes or dispatch LSDEvents to registered listeners.
     * <p>
     * Normally there's no need to ever do this. It generally makes more sense
     * to call setVisible(false) or clearShapes() to temporarily shut things
     * down.
     */
    public boolean unregister() {
        return LSDController.getInstance().unregisterApiInstance(this);
    }

    /** @deprecated use {@link ApiInfo} */
    @Deprecated public static String getVersion() {
        return ApiInfo.getVersion();
    }

    /**
     * @return true if the backend controller is all set up.
     *     If false, the cause is likely one of:
     *     a) ModLoader or Forge is disabled/missing;
     *     b) mod_LibShapeDraw is disabled/missing; or
     *     c) ModLoader hasn't instantiated mod_LibShapeDraw yet
     *        because it's too early in the mod lifecycle.
     *        Wait until the load method or later.
     * 
     * @deprecated use {@link #verifyInitialized}
     */
    @Deprecated public static boolean isControllerInitialized() {
        return LSDController.isInitialized();
    }

    /**
     * Optional sanity check for mods to make sure that ModLoader or Forge has
     * successfully initialized LibShapeDraw. Throws an exception if not.
     * <p>
     * This is the only LibShapeDraw API method that has a restriction on when
     * you can call it. Always wait until at least the BaseMod.load method.
     * <p>
     * In other words, do not call verifyInitialized() from your mod's
     * constructor. If you do, the exception may be thrown depending on the
     * order that ModLoader or Forge chooses to instantiate the various mod
     * classes.
     */
    public LibShapeDraw verifyInitialized() {
        // This method can't be automatically called from the constructor
        // because it is valid for client code to create LibShapeDraw instances
        // at any point... even before ModLoader/Forge has had a chance to
        // instantiated mod_LibShapeDraw.
        if (!LSDController.isInitialized()) {
            throw new LSDInternalException(ApiInfo.getName() +
                    " is not initialized. Possible causes:" +
                    " a) ModLoader or Forge is disabled/missing;" +
                    " b) mod_LibShapeDraw is disabled/missing; or" +
                    " c) another mod is incorrectly calling verifyInitialized too early.");
        }
        return this;
    }

    /**
     * Get a {@link MinecraftAccess} instance for calling obfuscated
     * Minecraft methods.
     */
    public MinecraftAccess getMinecraftAccess() {
        return LSDController.getMinecraftAccess();
    }

    /**
     * Whether this API instance's shapes will be rendered. Defaults to true.
     * When set to false, the shapes still exist. They just won't be rendered
     * until this property is set to true again.
     * <p>
     * To show/hide individual shapes rather than the entire shape collection,
     * use {@link Shape#setVisible}.
     */
    public boolean isVisible() {
        return visible;
    }

    /**
     * Whether this API instance's shapes will be rendered. Defaults to true.
     * When set to false, the shapes still exist. They just won't be rendered
     * until this property is set to true again.
     * <p>
     * To show/hide individual shapes rather than the entire shape collection,
     * use {@link Shape#setVisible}.
     */
    public LibShapeDraw setVisible(boolean visible) {
        this.visible = visible;
        return this;
    }

    /**
     * Whether this API instance's shapes are considered part of the game world
     * (true) or part of the GUI (false, default).
     * 
     * @see #setVisibleWhenHidingGui
     */
    public boolean isVisibleWhenHidingGui() {
        return visibleWhenHidingGui;
    }

    /**
     * Whether this API instance's shapes are considered part of the game world
     * (true) or part of the GUI (false, default).
     * <p>
     * If set to true, the shapes owned by this API instance are rendered
     * regardless of whether the GUI is visible. In other words, the shapes are
     * considered part of the game world rather than part of the GUI.
     * <p>
     * If set to false (default), the shapes are considered part of the GUI.
     * They will be appropriately hidden when the user presses F1 to hide the
     * GUI.
     * <p>
     * See also {@link #setVisible}, which overrides this property when set.
     */
    public LibShapeDraw setVisibleWhenHidingGui(boolean visibleWhenHidingGui) {
        this.visibleWhenHidingGui = visibleWhenHidingGui;
        return this;
    }

    /**
     * Get a read-only view of the set of shapes registered to this API
     * instance. To modify this set use addShape, removeShape, and clearShapes.
     */
    public Set<Shape> getShapes() {
        return shapesReadonly;
    }

    /**
     * Register a Shape to be rendered by this API instance.
     * <p>
     * Thread safety is not guaranteed. To avoid non-deterministic behavior,
     * only call this method from the main Minecraft thread.
     * @return the instance (for method chaining)
     */
    public LibShapeDraw addShape(Shape shape) {
        if (shape == null) {
            throw new IllegalArgumentException("shape cannot be null");
        }
        if (shapes.add(shape)) {
            shape.onAdd(this);
        }
        return this;
    }

    /**
     * Unregister a Shape, no longer rendering it.
     * <p>
     * If the Shape has Colors or ShapeTransforms that are busy animating, they
     * will continue to do so even after the Shape is unregistered. The
     * animations can be manually stopped using animateStop, or simply allowed
     * to expire normally (if non-looping).
     * <p>
     * Animations are not automatically stopped here because it is perfectly
     * valid (and often desired, for synchronized animation) for multiple
     * Shapes to share Color/ShapeTransform instances.
     * <p>
     * Attempting to remove a shape that is not part of this API instance's
     * shape collection is allowed but won't do anything.
     * <p>
     * Thread safety is not guaranteed. To avoid non-deterministic behavior,
     * only call this method from the main Minecraft thread.
     * <p>
     * See also {@link Shape#setVisible} for an alternate way of preventing a
     * shape from rendering. Generally, removeShape should be called if the
     * shape will never be rendered again. If the shape just needs to be hidden
     * temporarily, use setVisible.
     * 
     * @return the instance (for method chaining)
     */
    public LibShapeDraw removeShape(Shape shape) {
        if (shapes.remove(shape)) {
            shape.onRemove(this);
        }
        return this;
    }

    /**
     * Unregister all Shapes owned by this API instance, no longer rendering
     * them.
     * <p>
     * Any animations affecting the Shapes are <b>not</b> automatically
     * stopped. See {@link #removeShape}'s documentation for more details.
     * <p>
     * Thread safety is not guaranteed. To avoid non-deterministic behavior,
     * only call this method from the main Minecraft thread.
     * <p>
     * See also {@link #setVisible} for an alternate way of preventing all
     * shapes from rendering. Generally, clearShapes should be called if the
     * current shapes will never be rendered again. If the shapes just need to
     * be hidden temporarily, use setVisible.
     * 
     * @return the instance (for method chaining)
     */
    public LibShapeDraw clearShapes() {
        LinkedHashSet<Shape> prev = new LinkedHashSet<Shape>(shapes);
        shapes.clear();
        for (Shape shape : prev) {
            shape.onRemove(this);
        }
        return this;
    }

    /**
     * Get a read-only view of the set of event listeners associated with this
     * API instance. To modify this set use addEventListener,
     * removeEventListener, and clearEventListeners.
     * <p>
     * Thread safety is not guaranteed. To avoid non-deterministic behavior,
     * only call this method from the main Minecraft thread.
     */
    public Set<LSDEventListener> getEventListeners() {
        return eventListenersReadonly;
    }

    /**
     * Register an event listener to receive LSDEvents.
     * <p>
     * Thread safety is not guaranteed. To avoid non-deterministic behavior,
     * only call this method from the main Minecraft thread.
     * @return the instance (for method chaining)
     */
    public LibShapeDraw addEventListener(LSDEventListener listener) {
        if (listener == null) {
            throw new IllegalArgumentException("listener cannot be null");
        }
        eventListeners.add(listener);
        return this;
    }

    /**
     * Unregister an event listener. It will no longer receive LSDEvents.
     * <p>
     * Normally there is no need to manually unregister event listeners, but
     * this method is here if needed.
     * <p>
     * Attempting to remove an event listener that is not registered to this
     * API instance is allowed but won't do anything.
     * <p>
     * Thread safety is not guaranteed. To avoid non-deterministic behavior,
     * only call this method from the main Minecraft thread.
     * @return the instance (for method chaining)
     */
    public LibShapeDraw removeEventListener(LSDEventListener listener) {
        eventListeners.remove(listener);
        return this;
    }

    /**
     * Unregister all event listeners that were registered to this API
     * instance.
     * <p>
     * Normally there is no need to manually unregister event listeners, but
     * this method is here if needed.
     * <p>
     * Thread safety is not guaranteed. To avoid non-deterministic behavior,
     * only call this method from the main Minecraft thread.
     * @return the instance (for method chaining)
     */
    public LibShapeDraw clearEventListeners() {
        eventListeners.clear();
        return this;
    }

    /**
     * Output the complete state of all LibShapeDraw API instances, including
     * this one, to <code>(minecraft-dir)/mods/LibShapeDraw/LibShapeDraw.log</code>.
     * @return false if logging was disabled by user preference. Logging is
     *         enabled by default, however.
     */
    public boolean debugDump() {
        return LSDController.getInstance().debugDump();
    }

    /**
     * Get the unique id assigned to this API instance by the internal
     * controller. E.g.: "LibShapeDraw#0".
     */
    public String getInstanceId() {
        return instanceId;
    }

    @Override
    public String toString() {
        return getInstanceId() + ":" + ownerId;
    }
} public interface MinecraftAccess {
    /** Tessellator.instance.startDrawing */
    public MinecraftAccess startDrawing(int mode);

    /** Tessellator.instance.addVertex */
    public MinecraftAccess addVertex(double x, double y, double z);

    /** Tessellator.instance.addVertex */
    public MinecraftAccess addVertex(ReadonlyVector3 coords);

    /** Tessellator.instance.draw */
    public MinecraftAccess finishDrawing();

    /** RenderHelper.enableStandardItemLighting */
    public MinecraftAccess enableStandardItemLighting();

    /** Minecraft.ingameGUI.getChatGUI().printChatMessage */
    public MinecraftAccess sendChatMessage(String message);

    /** Minecraft.ingameGUI.getChatGUI() */
    public boolean chatWindowExists();

    /** Minecraft.timer.renderPartialTicks */
    public float getPartialTick();

    /** Minecraft.mcProfiler.startSection */
    public MinecraftAccess profilerStartSection(String sectionName);

    /** Minecraft.mcProfiler.endSection */
    public MinecraftAccess profilerEndSection();

    /** Minecraft.mcProfiler.endStartSection */
    public MinecraftAccess profilerEndStartSection(String sectionName);
} public class TridentConfig {
    private static TridentConfig config;

    private Set<UIToolkitHandler> uiToolkitHandlers;

    private Set<PropertyInterpolator> propertyInterpolators;

    private TridentConfig.PulseSource pulseSource;

    public interface PulseSource {
        public void waitUntilNextPulse();
    }

    public static class FixedRatePulseSource implements
    TridentConfig.PulseSource {
        private int msDelay;

        public FixedRatePulseSource(int msDelay) {
            this.msDelay = msDelay;
        }

        @Override
        public void waitUntilNextPulse() {
            try {
                Thread.sleep(this.msDelay);
            } catch (InterruptedException ie) {
                ie.printStackTrace();
            }
        }
    }

    private class DefaultPulseSource extends FixedRatePulseSource {
        DefaultPulseSource() {
            super(40);
        }
    }

    private TridentConfig() {
        this.pulseSource = new DefaultPulseSource();

        this.uiToolkitHandlers = new HashSet<UIToolkitHandler>();
        this.propertyInterpolators = new HashSet<PropertyInterpolator>();

        // Simplify Trident config to not load its UIToolkitHandlers and
        // PropertyInterpolator from properties files. These can still be added
        // programmatically if needed.
        PropertyInterpolatorSource piSource = new CorePropertyInterpolators();
        for (PropertyInterpolator pi : piSource.getPropertyInterpolators()) {
            this.propertyInterpolators.add(pi);
        }
        /*
        ClassLoader classLoader = Thread.currentThread()
                .getContextClassLoader();
        try {
            Enumeration urls = classLoader
                    .getResources("META-INF/trident-plugin.properties");
            while (urls.hasMoreElements()) {
                URL pluginUrl = (URL) urls.nextElement();
                BufferedReader reader = null;
                try {
                    reader = new BufferedReader(new InputStreamReader(pluginUrl
                            .openStream()));
                    while (true) {
                        String line = reader.readLine();
                        if (line == null)
                            break;
                        String[] parts = line.split("=");
                        if (parts.length != 2)
                            continue;
                        String key = parts[0];
                        String value = parts[1];
                        if ("UIToolkitHandler".compareTo(key) == 0) {
                            try {
                                Class pluginClass = classLoader
                                        .loadClass(value);
                                if (pluginClass == null)
                                    continue;
                                if (UIToolkitHandler.class
                                        .isAssignableFrom(pluginClass)) {
                                    UIToolkitHandler uiToolkitHandler = (UIToolkitHandler) pluginClass
                                            .newInstance();
                                    uiToolkitHandler.isHandlerFor(new Object());
                                    this.uiToolkitHandlers
                                    .add(uiToolkitHandler);
                                }
                            } catch (NoClassDefFoundError ncdfe) {
                                // trying to initialize a plugin with a missing
                                // class
                            }
                        }
                        if ("PropertyInterpolatorSource".compareTo(key) == 0) {
                            try {
                                Class piSourceClass = classLoader
                                        .loadClass(value);
                                if (piSourceClass == null)
                                    continue;
                                if (PropertyInterpolatorSource.class
                                        .isAssignableFrom(piSourceClass)) {
                                    PropertyInterpolatorSource piSource = (PropertyInterpolatorSource) piSourceClass
                                            .newInstance();
                                    Set<PropertyInterpolator> interpolators = piSource
                                            .getPropertyInterpolators();
                                    for (PropertyInterpolator pi : interpolators) {
                                        try {
                                            Class basePropertyClass = pi
                                                    .getBasePropertyClass();
                                            // is in classpath?
                                            basePropertyClass.getClass();
                                            this.propertyInterpolators.add(pi);
                                        } catch (NoClassDefFoundError ncdfe) {
                                            // trying to initialize a plugin
                                            // with a missing
                                            // class - just skip
                                        }

                                    }
                                    // this.propertyInterpolators.addAll(piSource
                                    // .getPropertyInterpolators());
                                }
                            } catch (NoClassDefFoundError ncdfe) {
                                // trying to initialize a plugin with a missing
                                // class
                            }
                        }
                    }
                } finally {
                    if (reader != null) {
                        try {
                            reader.close();
                        } catch (IOException ioe) {
                        }
                    }
                }
            }
        } catch (Exception exc) {
            exc.printStackTrace();
        }
        */
    }

    public static synchronized TridentConfig getInstance() {
        if (config == null)
            config = new TridentConfig();
        return config;
    }

    public synchronized Collection<UIToolkitHandler> getUIToolkitHandlers() {
        return Collections.unmodifiableSet(this.uiToolkitHandlers);
    }

    public synchronized Collection<PropertyInterpolator> getPropertyInterpolators() {
        return Collections.unmodifiableSet(this.propertyInterpolators);
    }

    public synchronized PropertyInterpolator getPropertyInterpolator(
            Object... values) {
        for (PropertyInterpolator interpolator : this.propertyInterpolators) {
            try {
                Class basePropertyClass = interpolator.getBasePropertyClass();
                boolean hasMatch = true;
                for (Object value : values) {
                    if (!basePropertyClass.isAssignableFrom(value.getClass())) {
                        hasMatch = false;
                        continue;
                    }
                }
                if (hasMatch)
                    return interpolator;
            } catch (NoClassDefFoundError ncdfe) {
                continue;
            }
        }
        return null;
    }

    public synchronized void addPropertyInterpolator(
            PropertyInterpolator pInterpolator) {
        this.propertyInterpolators.add(pInterpolator);
    }

    public synchronized void addPropertyInterpolatorSource(
            PropertyInterpolatorSource pInterpolatorSource) {
        this.propertyInterpolators.addAll(pInterpolatorSource
                .getPropertyInterpolators());
    }

    public synchronized void removePropertyInterpolator(
            PropertyInterpolator pInterpolator) {
        this.propertyInterpolators.remove(pInterpolator);
    }

    public synchronized void addUIToolkitHandler(
            UIToolkitHandler uiToolkitHandler) {
        this.uiToolkitHandlers.add(uiToolkitHandler);
    }

    public synchronized void removeUIToolkitHandler(
            UIToolkitHandler uiToolkitHandler) {
        this.uiToolkitHandlers.remove(uiToolkitHandler);
    }

    public synchronized void setPulseSource(PulseSource pulseSource) {
        TridentAnimationThread current = TimelineEngine.getInstance().animatorThread;
        if ((current != null) && current.isAlive())
            throw new IllegalStateException(
                    "Cannot replace the pulse source thread once it's running");
        this.pulseSource = pulseSource;
    }

    public synchronized TridentConfig.PulseSource getPulseSource() {
        return pulseSource;
    }
}
 public interface LSDEventListener {
    /** @see LSDRespawnEvent */
    public void onRespawn(LSDRespawnEvent event);

    /** @see LSDGameTickEvent */
    public void onGameTick(LSDGameTickEvent event);

    /** @see LSDPreRenderEvent */
    public void onPreRender(LSDPreRenderEvent event);
} public class LSDGameTickEvent extends LSDEvent {
    public LSDGameTickEvent(LibShapeDraw apiInstance, ReadonlyVector3 playerCoords) {
        super(apiInstance, playerCoords);
    }
} public class LSDPreRenderEvent extends LSDEvent {
    private final float partialTick;
    private final boolean guiHidden;

    public LSDPreRenderEvent(LibShapeDraw apiInstance, ReadonlyVector3 playerCoords, float partialTick, boolean guiHidden) {
        super(apiInstance, playerCoords);
        this.partialTick = partialTick;
        this.guiHidden = guiHidden;
    }

    /**
     * The partial tick time: a number in [0.0, 1.0) indicating where the
     * rendering frame is in between game ticks.
     */
    public float getPartialTick() {
        return partialTick;
    }

    /**
     * True if the user has hidden the GUI (F1) and there is no active GUI screen.
     */
    public boolean isGuiHidden() {
        return guiHidden;
    }
} public class LSDRespawnEvent extends LSDEvent {
    private final boolean newServer;
    private final boolean newDimension;

    public LSDRespawnEvent(LibShapeDraw apiInstance, ReadonlyVector3 playerCoords, boolean newServer, boolean newDimension) {
        super(apiInstance, playerCoords);
        this.newServer = newServer;
        this.newDimension = newDimension;
    }

    /**
     * Whether the player is spawning for the first time during a server
     * connection session. This includes single player worlds, which use an
     * integrated server.
     * <p>
     * Reconnecting to the same dedicated server will result in another event
     * with isNewServer==true.
     * <p>
     * Implies isNewDimension.
     */
    public boolean isNewServer() {
        return newServer;
    }

    /**
     * Whether the player respawned in a different dimension (e.g., taking a Nether/End portal,
     * or dying in one of those dimensions then respawning back in the overworld.)
     * <p>
     * If true, the world definitely changed.
     * If false, it's not safe to assume either way. The server might be running a multi-world plugin.
     * <p>
     * Always true if isNewServer.
     */
    public boolean isNewDimension() {
        return newDimension;
    }
} public static class FileLogger extends Logger {
    /** Formats lines like: "2012-05-21 20:57:06.540 [INFO] example message" */
    public static class LineFormatter extends Formatter {
        @Override
        public String format(LogRecord record) {
            StringWriter w = new StringWriter();
            String ts = new Timestamp((new Date()).getTime()).toString();
            w.append(ts);
            for (int pad = ts.length(); pad < 23; pad++) {
                w.append('0');
            }
            w.append(" [").append(record.getLevel().getName());
            w.append("] ").append(formatMessage(record)).append('\n');
            if (record.getThrown() != null) {
                record.getThrown().printStackTrace(new PrintWriter(w));
            }
            return w.toString();
        }
    }

    public FileLogger(File baseDirectory, String name, boolean append) {
        super(name, null);
        baseDirectory.mkdirs();
        String logFilePattern = baseDirectory.getPath() + File.separator + name + ".log";
        try {
            FileHandler handler = new FileHandler(logFilePattern, append);
            handler.setFormatter(new LineFormatter());
            addHandler(handler);
        } catch (IOException e) {
            throw new LSDInternalException("unable to add file handler for " + logFilePattern, e);
        }
    }
} public static class NullLogger extends Logger {
    public NullLogger() {
        super("null", null);
        setLevel(Level.OFF);
    }
} public interface ReadonlyVector3 extends Serializable {
    /**
     * @return a new deep-copied mutable Vector3. Does not copy animations.
     *         <p>
     *         Same concept as Object.clone(), minus the tedious/clunky checked
     *         exception, CloneNotSupportedException.
     */
    public Vector3 copy();

    /** @return the vector's x component. */
    public double getX();

    /** @return the vector's y component. */
    public double getY();

    /** @return the vector's z component. */
    public double getZ();

    /** @return the vector's component for the given axis. */
    public double getComponent(Axis axis);

    /**
     * @return true if the specified vector component matches a value within a
     *         a margin of error (epsilon).
     * @see #EPSILON
     */
    public boolean componentEquals(Axis axis, double value, double epsilon);

    /** @return true if the vector components match the other's exactly. */
    public boolean equalsExact(ReadonlyVector3 other);

    /** @return true if the vector components match the other's exactly. */
    public boolean equalsExact(double otherX, double otherY, double otherZ);

    /**
     * @return true if the vector components match the other's within a margin
     *         of error (epsilon).
     * @see #EPSILON
     */
    public boolean equals(ReadonlyVector3 other, double epsilon);

    /**
     * @return true if the vector components match the other's within a margin
     *         of error (epsilon).
     * @see #EPSILON
     */
    public boolean equals(double otherX, double otherY, double otherZ, double epsilon);

    /**
     * A recommended margin of error to use with
     * {@link #equals(ReadonlyVector3, double)}.
     */
    public static final double EPSILON = 1E-6;

    /**
     * Equivalent to {@link #equalsExact(ReadonlyVector3)}.
     * <p>
     * This method is marked as deprecated because it's better practice to
     * explicitly state margins of error for floating-point comparisons. For a
     * non-zero margin of error, use {@link #equals(ReadonlyVector3, double)}.
     * @deprecated
     */
    @Override
    @Deprecated
    public boolean equals(Object other);

    @Override
    public int hashCode();

    @Override
    public String toString();

    /** @return true if all of this vector's components are exactly zero. */
    public boolean isZero();

    /**
     * Equivalent to <code>distanceSquared(Vector3.ZEROS)</code>.
     * @return the vector's length (aka magnitude) squared. To compare two
     *         two vectors' lengths, use this method instead of
     *         {@link #length()} as it avoids the relatively expensive sqrt
     *         call.
     */
    public double lengthSquared();

    /**
     * Equivalent to <code>distance(Vector3.ZEROS)</code>.
     * @return the vector's length (aka magnitude). This uses the relatively
     *         expensive sqrt function, so avoid calling it repeatedly.
     */
    public double length();

    /**
     * @return the distance between two points, squared. If you're trying to
     *         compare two distances, use this method instead of
     *         {@link #distance} as it avoids the relatively expensive sqrt
     *         call.
     */
    public double distanceSquared(ReadonlyVector3 other);

    /** @deprecated as of release 1.1 replaced by {@link #distanceSquared} */
    @Deprecated public double getDistanceSquared(ReadonlyVector3 other);

    /**
     * @return the distance between two points. This uses the relatively
     *         expensive sqrt function, so avoid calling it repeatedly.
     */
    public double distance(ReadonlyVector3 other);

    /** @deprecated as of release 1.1 replaced by {@link #distance} */
    @Deprecated public double getDistance(ReadonlyVector3 other);

    /** @return the dot product between two vectors: x0*x1 + y0*y1 + z0*z1. */
    public double dot(ReadonlyVector3 other);

    /**
     * @return the angle, in radians [0, pi], between two vectors. If either
     *         vector is all zeros, 0 is returned.
     */
    public double angle(ReadonlyVector3 other);

    /**
     * @return the angle, in degrees [0, 180], between two vectors. If either
     *         vector is all zeros, 0 is returned.
     */
    public double angleDegrees(ReadonlyVector3 other);

    /**
     * @return the angle, in radians (-pi/2, pi/2] of the vector direction's
     *         heading, relative to the z axis.
     */
    public double yaw();

    /**
     * @return the angle, in degrees (-180, 180] of the vector direction's
     *         heading, relative to the z axis.
     */
    public double yawDegrees();

    /**
     * @return the angle, in radians [-pi/2, pi/2] of the vector direction's
     *         elevation.
     */
    public double pitch();

    /**
     * @return the angle, in degrees [-90, 90] of the vector direction's
     *         elevation.
     */
    public double pitchDegrees();

    /**
     * @return true if the point is inside the axis-aligned bounding box
     *         specified by the two corner parameters.
     */
    public boolean isInAABB(ReadonlyVector3 lowerCorner, ReadonlyVector3 upperColor);

    /** @return true if the point is inside the sphere. */
    public boolean isInSphere(ReadonlyVector3 origin, double radius);

    /**
     * Convenience method to update the OpenGL state with this vector.
     * <p>
     * Equivalent to
     * <code>GL11.glRotated(angleDegrees, v.getX(), v.getY(), v.getZ())</code>.
     * <p>
     * Calling this method on a zero vector is a no-op.
     * <p>
     * "Un-applying" the rotation (e.g., glPushMatrix and glPopMatrix)
     * is the responsibility of the caller.
     * <p>
     * All of these operations are actually
     * <a href="http://www.opengl.org/wiki/Legacy_OpenGL">deprecated in OpenGL
     * 3.0</a>, but Minecraft is just old school like that.
     */
    public void glApplyRotateDegrees(double angleDegrees);

    /**
     * Convenience method to update the OpenGL state with this vector.
     * <p>
     * Equivalent to
     * <code>GL11.glRotated(angleRadians*180.0/Math.PI, v.getX(), v.getY(), v.getZ())</code>.
     * <p>
     * Calling this method on a zero vector is a no-op.
     * <p>
     * "Un-applying" the rotation (e.g., glPushMatrix and glPopMatrix)
     * is the responsibility of the caller.
     * <p>
     * All of these operations are actually
     * <a href="http://www.opengl.org/wiki/Legacy_OpenGL">deprecated in OpenGL
     * 3.0</a>, but Minecraft is just old school like that.
     */
    public void glApplyRotateRadians(double angleRadians);

    /**
     * Convenience method to update the OpenGL state with this vector.
     * <p>
     * Equivalent to
     * <code>GL11.glScaled(v.getX(), v.getY(), v.getZ())</code>.
     * <p>
     * "Un-applying" the scaling (e.g., glPushMatrix and glPopMatrix)
     * is the responsibility of the caller.
     * <p>
     * All of these operations are actually
     * <a href="http://www.opengl.org/wiki/Legacy_OpenGL">deprecated in OpenGL
     * 3.0</a>, but Minecraft is just old school like that.
     */
    public void glApplyScale();

    /**
     * Convenience method to update the OpenGL state with this vector.
     * <p>
     * Equivalent to
     * <code>GL11.glTranslated(v.getX(), v.getY(), v.getZ())</code>.
     * <p>
     * "Un-applying" the translation (e.g., glPushMatrix and glPopMatrix)
     * is the responsibility of the caller.
     * <p>
     * All of these operations are actually
     * <a href="http://www.opengl.org/wiki/Legacy_OpenGL">deprecated in OpenGL
     * 3.0</a>, but Minecraft is just old school like that.
     */
    public void glApplyTranslate();

    /** @return true if this vector is being updated by an active animation. */
    public boolean isAnimating();
} public abstract class Shape {
    private boolean visible = true;
    private Vector3 origin;
    private boolean relativeToOrigin = true;
    private List<ShapeTransform> transforms;
    private List<ShapeTransform> transformsReadonly;

    public Shape(Vector3 origin) {
        setOrigin(origin);
    }

    /** If false, the Shape will not be rendered. */
    public boolean isVisible() {
        return visible;
    }
    /** If false, the Shape will not be rendered. */
    public void setVisible(boolean visible) {
        this.visible = visible;
    }

    /**
     * The point around which ShapeTransforms should occur. This is generally
     * the center point of the Shape, if that makes sense for the Shape type.
     */
    public ReadonlyVector3 getOriginReadonly() {
        return origin;
    }
    /** @see #getOriginReadonly */
    protected Vector3 getOrigin() {
        return origin;
    }
    /** @see #getOriginReadonly */
    protected void setOrigin(Vector3 origin) {
        if (origin == null) {
            throw new IllegalArgumentException("origin cannot be null");
        }
        this.origin = origin;
    }

    /**
     * If true, render this shape relative to its own origin x/y/z.
     * If false, rendering will ignore the shape's origin, operating
     * on absolute world x/y/z coordinates.
     * <p>
     * Which setting is the default, and whether it's possible to change this
     * setting, depends on the Shape type.
     */
    public boolean isRelativeToOrigin() {
        return relativeToOrigin;
    }
    /** @see #isRelativeToOrigin */
    protected void setRelativeToOrigin(boolean relativeToOrigin) {
        this.relativeToOrigin = relativeToOrigin;
    }

    /** Lazily create the lists. Many shapes don't need transforms. */
    private void makeTransforms() {
        if (transforms == null) {
            transforms = new LinkedList<ShapeTransform>();
            transformsReadonly = Collections.unmodifiableList(transforms);
        }
    }

    /**
     * Get a read-only view of the list of ShapeTransforms to perform right
     * before rendering this Shape, if any. To modify this list use
     * addTransform, removeTransform, and clearTransforms.
     */
    public List<ShapeTransform> getTransforms() {
        makeTransforms();
        return transformsReadonly;
    }

    /**
     * Register a ShapeTransform to be applied to this Shape.
     * <p>
     * Thread safety is not guaranteed. To avoid non-deterministic behavior,
     * only call this method from the main Minecraft thread.
     * 
     * @return the instance (for method chaining)
     */
    public Shape addTransform(ShapeTransform transform) {
        makeTransforms();
        if (transform == null) {
            throw new IllegalArgumentException("transform cannot be null");
        }
        transforms.add(transform);
        return this;
    }

    /**
     * Unregister a ShapeTransform, no longer applying it to this Shape.
     * <p>
     * Attempting to remove a transform that is not registered to this shape
     * is allowed but won't do anything.
     * <p>
     * Thread safety is not guaranteed. To avoid non-deterministic behavior,
     * only call this method from the main Minecraft thread.
     * 
     * @return the instance (for method chaining)
     */
    public Shape removeTransform(ShapeTransform transform) {
        makeTransforms();
        transforms.remove(transform);
        return this;
    }

    /**
     * Unregister all ShapeTransforms registered to this Shape.
     * <p>
     * Thread safety is not guaranteed. To avoid non-deterministic behavior,
     * only call this method from the main Minecraft thread.
     * 
     * @return the instance (for method chaining)
     */
    public Shape clearTransforms() {
        makeTransforms();
        transforms.clear();
        return this;
    }

    /**
     * Called whenever this Shape is added to a LibShapeDraw API instance's set
     * of shapes to render.
     */
    public void onAdd(LibShapeDraw apiInstance) {
        // do nothing; derived classes can override as needed.
    }

    /**
     * Called whenever this Shape is removed from a LibShapeDraw API instance's
     * set of shapes to render.
     * <p>
     * This method should clean up any external resources (such as VBOs) that
     * were owned by this Shape.
     */
    public void onRemove(LibShapeDraw apiInstance) {
        // do nothing; derived classes can override as needed.
    }

    /**
     * Called immediately before this Shape is rendered.
     * <p>
     * Calls glContextStandardSetup.
     */
    public void onPreRender(MinecraftAccess mc) {
        glContextStandardSetup();
    }

    /**
     * Resets common OpenGL context settings, helping ensure that Shapes will
     * render the same regardless of what was rendered beforehand.
     */
    public static final void glContextStandardSetup() {
        GL11.glEnable(GL11.GL_BLEND);
        GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
        GL11.glDisable(GL11.GL_TEXTURE_2D);
        GL11.glDisable(GL11.GL_LIGHTING);
        GL11.glDisable(GL11.GL_LIGHT0);
        GL11.glDisable(GL11.GL_LIGHT1);
        GL11.glDisable(GL11.GL_COLOR_MATERIAL);
        GL11.glDepthFunc(GL11.GL_LEQUAL);
        GL11.glLineWidth(1.0F);
        // Every Shape calls glColord; no need to reset that here.
    }

    /**
     * Called immediately after this Shape is rendered.
     * <p>
     * If any unique OpenGL context settings were applied but not reset during
     * rendering, they should be reset here. Otherwise, later elements may be
     * rendered incorrectly.
     */
    public void onPostRender(MinecraftAccess mc) {
        // do nothing; derived classes can override as needed.
    }

    /**
     * Render the Shape, if visible. Also perform any ShapeTransforms
     * registered to this Shape.
     * <p>
     * This method is automatically called as appropriate when a Shape has been
     * registered to a LibShapeDraw API instance using addShape.
     * <p>
     * It can also be called manually for Shapes not associated with an API
     * instance.
     */
    public final void render(MinecraftAccess mc) {
        if (!isVisible()) {
            return;
        }
        onPreRender(mc);
        final boolean absolute = !isRelativeToOrigin();
        if (absolute && transforms == null) {
            renderShape(mc);
        } else {
            final ReadonlyVector3 origin = getOriginReadonly();
            if (origin == null) {
                return;
            }
            GL11.glPushMatrix();
            GL11.glTranslated(origin.getX(), origin.getY(), origin.getZ());
            if (transforms != null) {
                for (ShapeTransform t : transforms) {
                    if (t != null) {
                        t.preRender();
                    }
                }
            }
            if (absolute) {
                GL11.glTranslated(-origin.getX(), -origin.getY(), -origin.getZ());
            }
            renderShape(mc);
            GL11.glPopMatrix();
        }
        onPostRender(mc);
    }

    /**
     * Does the actual work of rendering the Shape, called by the Shape's main
     * render method after setting up the OpenGL context.
     */
    protected abstract void renderShape(MinecraftAccess mc);

    @Override
    public String toString() {
        StringBuilder b = new StringBuilder()
        .append(getClass().getSimpleName())
        .append('@')
        .append(Integer.toHexString(hashCode()))
        .append('{');
        if (isRelativeToOrigin()) {
            b.append('R');
        }
        if (isVisible()) {
            b.append('V');
        }
        if (transforms != null) {
            for (int i = 0; i < transforms.size(); i++) {
                b.append('T');
            }
        }
        return b.append('}').append(getOriginReadonly()).toString();
    }
}
import java.util.LinkedHashSet;
import java.util.logging.Level;
import java.util.logging.Logger;
import libshapedraw.ApiInfo;
import libshapedraw.LibShapeDraw;
import libshapedraw.MinecraftAccess;
import libshapedraw.animation.trident.TridentConfig;
import libshapedraw.event.LSDEventListener;
import libshapedraw.event.LSDGameTickEvent;
import libshapedraw.event.LSDPreRenderEvent;
import libshapedraw.event.LSDRespawnEvent;
import libshapedraw.internal.LSDUtil.FileLogger;
import libshapedraw.internal.LSDUtil.NullLogger;
import libshapedraw.primitive.ReadonlyVector3;
import libshapedraw.shape.Shape;
import org.lwjgl.opengl.GL11;
package libshapedraw.internal;




/**
 * Internal singleton controller class, lazily instantiated.
 * Relies on mod_LibShapeDraw to feed it Minecraft game events.
 */
public class LSDController {
    private static LSDController instance;
    private final Logger log;
    private final LinkedHashSet<LibShapeDraw> apiInstances;
    private int topApiInstanceId;
    private MinecraftAccess minecraftAccess;
    private LSDUpdateCheck updateCheck;
    private boolean initialized;
    private long lastDump;

    private LSDController() {
        if (LSDGlobalSettings.isLoggingEnabled()) {
            log = new FileLogger(LSDModDirectory.DIRECTORY, ApiInfo.getName(), LSDGlobalSettings.isLoggingAppend());
        } else {
            log = new NullLogger();
        }
        apiInstances = new LinkedHashSet<LibShapeDraw>();
        topApiInstanceId = 0;

        TridentConfig trident = TridentConfig.getInstance();
        trident.addPropertyInterpolator(new ReadonlyColorPropertyInterpolator());
        trident.addPropertyInterpolator(new ReadonlyVector3PropertyInterpolator());
        trident.addPropertyInterpolator(new ReadonlyLineStylePropertyInterpolator());

        log.info(ApiInfo.getName() + " v" + ApiInfo.getVersion() + " by " + ApiInfo.getAuthors());
        log.info(ApiInfo.getUrlMain().toString());
        log.info(ApiInfo.getUrlSource().toString());
        log.info(getClass().getName() + " instantiated");
    }

    public static LSDController getInstance() {
        if (instance == null) {
            instance = new LSDController();
        }
        return instance;
    }

    public static Logger getLog() {
        return getInstance().log;
    }

    public static MinecraftAccess getMinecraftAccess() {
        return getInstance().minecraftAccess;
    }

    /**
     * @return true if mod_LibShapeDraw has been instantiated and is linked up
     *         to the controller
     */
    public static boolean isInitialized() {
        return getInstance().initialized;
    }

    /**
     * Called by mod_LibShapeDraw to establish a link between the LSDController
     * and mod_LibShapeDraw singletons.
     */
    public void initialize(MinecraftAccess minecraftAccess) {
        if (isInitialized()) {
            // LibShapeDraw is probably installed incorrectly, causing
            // ModLoader/Forge to bogusly instantiate mod_LibShapeDraw multiple
            // times.
            throw new IllegalStateException("multiple initializations of controller");
        }
        this.minecraftAccess = minecraftAccess;
        initialized = true;
        log.info(getClass().getName() + " initialized by " + minecraftAccess.getClass().getName());
    }

    /**
     * Called by LibShapeDraw's constructor.
     */
    public String registerApiInstance(LibShapeDraw apiInstance, String ownerId) {
        if (apiInstances.contains(apiInstance)) {
            throw new IllegalStateException("already registered");
        }
        String apiInstanceId = apiInstance.getClass().getSimpleName() + "#" + topApiInstanceId;
        topApiInstanceId++;
        apiInstances.add(apiInstance);
        log.info("registered API instance " + apiInstanceId + ":" + ownerId);
        return apiInstanceId;
    }

    /**
     * Called by LibShapeDraw.unregister.
     */
    public boolean unregisterApiInstance(LibShapeDraw apiInstance) {
        boolean result = apiInstances.remove(apiInstance);
        if (result) {
            log.info("unregistered API instance " + apiInstance.getInstanceId());
        }
        return result;
    }

    /**
     * Called by mod_LibShapeDraw.
     * Dispatch the respawn event.
     */
    public void respawn(ReadonlyVector3 playerCoords, boolean isNewServer, boolean isNewDimension) {